From nobody Fri Dec 19 19:17:27 2025 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 25F441A23A9 for ; Mon, 30 Dec 2024 09:31:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=114.242.206.163 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735551096; cv=none; b=cMB3bcjqJllzR/7JCcpq2dEZqA0XEN49+LfjrTp1ROXuKbYISdHF0fj82LP2b8qLfInp06TfrGbA6psG9oMsspI84LN/JIaxPCCfk6xurYw0UHa0r5+d6HmWg/340FSmXisZzEkMnWm7fFLWMbXvbzTafiuqEibCgKQAnqjI3JI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1735551096; c=relaxed/simple; bh=wdeTuKnWdVlxsgYdcXaCluV0Rql2njun8x8xNjQo49g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=twgR17KFDA6wkpGB8BvAOOJQkwe/IFjM1vl2/OHcNFRr0xwopjIWWNTI1Bq2CiDlPiSqOQGbSgHdAGJMINLoa925HmyKq2UoRPdJa85nPwzcNwQYKU+s4GpWzON4GhJrDt/zmSqG5JdvStiA3oOmREcQqUB3oNk20exZn6aDWK0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=loongson.cn; spf=pass smtp.mailfrom=loongson.cn; arc=none smtp.client-ip=114.242.206.163 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=loongson.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=loongson.cn Received: from loongson.cn (unknown [223.64.68.63]) by gateway (Coremail) with SMTP id _____8AxLOJ0aHJnQM9bAA--.48422S3; Mon, 30 Dec 2024 17:31:32 +0800 (CST) Received: from localhost.localdomain (unknown [223.64.68.63]) by front1 (Coremail) with SMTP id qMiowMCxncVkaHJnp74NAA--.4295S5; Mon, 30 Dec 2024 17:31:21 +0800 (CST) From: Binbin Zhou To: Binbin Zhou , Huacai Chen , Lee Jones , Corey Minyard , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter Cc: Huacai Chen , linux-kernel@vger.kernel.org, openipmi-developer@lists.sourceforge.net, dri-devel@lists.freedesktop.org, Xuerui Wang , loongarch@lists.linux.dev, Binbin Zhou , Chong Qiao Subject: [PATCH v1 3/4] drm/ls2kbmc: Add support for Loongson-2K BMC display Date: Mon, 30 Dec 2024 17:31:10 +0800 Message-ID: <4d62076ee560b35c653a9fbced8d03ef3bdb3005.1735550269.git.zhoubinbin@loongson.cn> X-Mailer: git-send-email 2.43.5 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: qMiowMCxncVkaHJnp74NAA--.4295S5 X-CM-SenderInfo: p2kr3uplqex0o6or00hjvr0hdfq/ X-Coremail-Antispam: 1Uk129KBj9fXoWfWr4kurykGw4xAr48Xry7Jwc_yoW8uF48Xo Z7XF13Xw48Gw1Fq39xGr1ktanavayvga93G345ur4vva1DWw1YyFW7Ww1YvFWxZr1rtr4D Zr1qvF1xJF4xJan5l-sFpf9Il3svdjkaLaAFLSUrUUUU5b8apTn2vfkv8UJUUUU8wcxFpf 9Il3svdxBIdaVrn0xqx4xG64xvF2IEw4CE5I8CrVC2j2Jv73VFW2AGmfu7bjvjm3AaLaJ3 UjIYCTnIWjp_UUUYZ7kC6x804xWl14x267AKxVWUJVW8JwAFc2x0x2IEx4CE42xK8VAvwI 8IcIk0rVWrJVCq3wAFIxvE14AKwVWUAVWUZwA2ocxC64kIII0Yj41l84x0c7CEw4AK67xG Y2AK021l84ACjcxK6xIIjxv20xvE14v26F1j6w1UM28EF7xvwVC0I7IYx2IY6xkF7I0E14 v26F4j6r4UJwA2z4x0Y4vEx4A2jsIE14v26r4UJVWxJr1l84ACjcxK6I8E87Iv6xkF7I0E 14v26r4UJVWxJr1ln4kS14v26r126r1DM2AIxVAIcxkEcVAq07x20xvEncxIr21l57IF6x kI12xvs2x26I8E6xACxx1l5I8CrVACY4xI64kE6c02F40Ex7xfMcIj6xIIjxv20xvE14v2 6rWY6Fy7McIj6I8E87Iv67AKxVW8JVWxJwAm72CE4IkC6x0Yz7v_Jr0_Gr1lF7xvr2IYc2 Ij64vIr41lc7CjxVAaw2AFwI0_Jw0_GFyl42xK82IYc2Ij64vIr41l4I8I3I0E4IkC6x0Y z7v_Jr0_Gr1l4IxYO2xFxVAFwI0_JF0_Jw1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s026x 8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r4a6rW5MIIYrxkI7VAKI48JMIIF0xvE 2Ix0cI8IcVAFwI0_Xr0_Ar1lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4j6F4UMIIF0xvE42 xK8VAvwI8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVW8JVWxJwCI42IY6I8E87Iv6xkF 7I0E14v26r4j6r4UJbIYCTnIWIevJa73UjIFyTuYvjxUsDDJDUUUU Content-Type: text/plain; charset="utf-8" Adds a driver for the Loongson-2K BMC display as a sub-function of the BMC device. Display-related scan output buffers, sizes, and display formats are provided through the Loongson-2K BMC MFD driver. Co-developed-by: Chong Qiao Signed-off-by: Chong Qiao Signed-off-by: Binbin Zhou --- drivers/gpu/drm/tiny/Kconfig | 18 + drivers/gpu/drm/tiny/Makefile | 1 + drivers/gpu/drm/tiny/ls2kbmc.c | 636 +++++++++++++++++++++++++++++++++ 3 files changed, 655 insertions(+) create mode 100644 drivers/gpu/drm/tiny/ls2kbmc.c diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index 94cbdb1337c0..5412f639a964 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -171,6 +171,24 @@ config TINYDRM_ILI9486 =20 If M is selected the module will be called ili9486. =20 +config TINYDRM_LS2KBMC + tristate "DRM support for Loongson-2K BMC display" + depends on DRM && MMU + depends on MFD_LS2K_BMC + select APERTURE_HELPERS + select DRM_CLIENT_SELECTION + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + help + DRM driver for the Loongson-2K BMC display. + + This driver assumes that the display hardware has been initialized + by the Loongson-2K BMC. Since the Loongson-2K BMC does not support + resolution detection now, the scan buffer, size and display format + are fixed and provided by the BMC. + + If M is selected the module will be called ls2kbmc. + config TINYDRM_MI0283QT tristate "DRM support for MI0283QT" depends on DRM && SPI diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index 4aaf56f8707d..fa4e1646db77 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_TINYDRM_ILI9163) +=3D ili9163.o obj-$(CONFIG_TINYDRM_ILI9225) +=3D ili9225.o obj-$(CONFIG_TINYDRM_ILI9341) +=3D ili9341.o obj-$(CONFIG_TINYDRM_ILI9486) +=3D ili9486.o +obj-$(CONFIG_TINYDRM_LS2KBMC) +=3D ls2kbmc.o obj-$(CONFIG_TINYDRM_MI0283QT) +=3D mi0283qt.o obj-$(CONFIG_TINYDRM_REPAPER) +=3D repaper.o obj-$(CONFIG_TINYDRM_SHARP_MEMORY) +=3D sharp-memory.o diff --git a/drivers/gpu/drm/tiny/ls2kbmc.c b/drivers/gpu/drm/tiny/ls2kbmc.c new file mode 100644 index 000000000000..909d6c687193 --- /dev/null +++ b/drivers/gpu/drm/tiny/ls2kbmc.c @@ -0,0 +1,636 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DRM driver for Loongson-2K BMC display + * + * Copyright (C) 2024 Loongson Technology Corporation Limited. + * + * Based on simpledrm + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ls2kbmc_pdata { + struct pci_dev *pdev; + struct simplefb_platform_data pd; +}; + +/* + * Helpers for simplefb_platform_data + */ + +static int +simplefb_get_validated_int(struct drm_device *dev, const char *name, + u32 value) +{ + if (value > INT_MAX) { + drm_err(dev, "simplefb: invalid framebuffer %s of %u\n", + name, value); + return -EINVAL; + } + return (int)value; +} + +static int +simplefb_get_validated_int0(struct drm_device *dev, const char *name, + u32 value) +{ + if (!value) { + drm_err(dev, "simplefb: invalid framebuffer %s of %u\n", + name, value); + return -EINVAL; + } + return simplefb_get_validated_int(dev, name, value); +} + +static const struct drm_format_info * +simplefb_get_validated_format(struct drm_device *dev, const char *format_n= ame) +{ + static const struct simplefb_format formats[] =3D SIMPLEFB_FORMATS; + const struct simplefb_format *fmt =3D formats; + const struct simplefb_format *end =3D fmt + ARRAY_SIZE(formats); + const struct drm_format_info *info; + + if (!format_name) { + drm_err(dev, "simplefb: missing framebuffer format\n"); + return ERR_PTR(-EINVAL); + } + + while (fmt < end) { + if (!strcmp(format_name, fmt->name)) { + info =3D drm_format_info(fmt->fourcc); + if (!info) + return ERR_PTR(-EINVAL); + return info; + } + ++fmt; + } + + drm_err(dev, "simplefb: unknown framebuffer format %s\n", + format_name); + + return ERR_PTR(-EINVAL); +} + +static int +simplefb_get_width_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_int0(dev, "width", pd->width); +} + +static int +simplefb_get_height_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_int0(dev, "height", pd->height); +} + +static int +simplefb_get_stride_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_int(dev, "stride", pd->stride); +} + +static const struct drm_format_info * +simplefb_get_format_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_format(dev, pd->format); +} + +/* + * ls2kbmc Framebuffer device + */ + +struct ls2kbmc_device { + struct drm_device dev; + + /* simplefb settings */ + struct drm_display_mode mode; + const struct drm_format_info *format; + unsigned int pitch; + + /* memory management */ + struct iosys_map screen_base; + + /* modesetting */ + u32 formats[8]; + struct drm_plane primary_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; +}; + +static struct ls2kbmc_device *ls2kbmc_device_of_dev(struct drm_device *dev) +{ + return container_of(dev, struct ls2kbmc_device, dev); +} + +/* + * Modesetting + */ + +static const u64 ls2kbmc_primary_plane_format_modifiers[] =3D { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static int ls2kbmc_primary_plane_helper_atomic_check(struct drm_plane *pla= ne, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state =3D drm_atomic_get_new_plane_stat= e(state, plane); + struct drm_shadow_plane_state *new_shadow_plane_state =3D + to_drm_shadow_plane_state(new_plane_state); + struct drm_framebuffer *new_fb =3D new_plane_state->fb; + struct drm_crtc *new_crtc =3D new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state =3D NULL; + struct drm_device *dev =3D plane->dev; + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(dev); + int ret; + + if (new_crtc) + new_crtc_state =3D drm_atomic_get_new_crtc_state(state, new_crtc); + + ret =3D drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_sta= te, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + else if (!new_plane_state->visible) + return 0; + + if (new_fb->format !=3D sdev->format) { + void *buf; + + /* format conversion necessary; reserve buffer */ + buf =3D drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_st= ate, + sdev->pitch, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + return 0; +} + +static void ls2kbmc_primary_plane_helper_atomic_update(struct drm_plane *p= lane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state =3D drm_atomic_get_new_plane_state(st= ate, plane); + struct drm_plane_state *old_plane_state =3D drm_atomic_get_old_plane_stat= e(state, plane); + struct drm_shadow_plane_state *shadow_plane_state =3D to_drm_shadow_plane= _state(plane_state); + struct drm_framebuffer *fb =3D plane_state->fb; + struct drm_device *dev =3D plane->dev; + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(dev); + struct drm_atomic_helper_damage_iter iter; + struct drm_rect damage; + int ret, idx; + + ret =3D drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); + if (ret) + return; + + if (!drm_dev_enter(dev, &idx)) + goto out_drm_gem_fb_end_cpu_access; + + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + struct drm_rect dst_clip =3D plane_state->dst; + struct iosys_map dst =3D sdev->screen_base; + + if (!drm_rect_intersect(&dst_clip, &damage)) + continue; + + iosys_map_incr(&dst, drm_fb_clip_offset(sdev->pitch, sdev->format, &dst_= clip)); + drm_fb_blit(&dst, &sdev->pitch, sdev->format->format, shadow_plane_state= ->data, + fb, &damage, &shadow_plane_state->fmtcnv_state); + } + + drm_dev_exit(idx); +out_drm_gem_fb_end_cpu_access: + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); +} + +static void ls2kbmc_primary_plane_helper_atomic_disable(struct drm_plane *= plane, + struct drm_atomic_state *state) +{ + struct drm_device *dev =3D plane->dev; + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(dev); + int idx; + + if (!drm_dev_enter(dev, &idx)) + return; + + /* Clear screen to black if disabled */ + iosys_map_memset(&sdev->screen_base, 0, 0, sdev->pitch * sdev->mode.vdisp= lay); + + drm_dev_exit(idx); +} + +static int ls2kbmc_primary_plane_helper_get_scanout_buffer(struct drm_plan= e *plane, + struct drm_scanout_buffer *sb) +{ + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(plane->dev); + + sb->width =3D sdev->mode.hdisplay; + sb->height =3D sdev->mode.vdisplay; + sb->format =3D sdev->format; + sb->pitch[0] =3D sdev->pitch; + sb->map[0] =3D sdev->screen_base; + + return 0; +} + +static const struct drm_plane_helper_funcs ls2kbmc_primary_plane_helper_fu= ncs =3D { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check =3D ls2kbmc_primary_plane_helper_atomic_check, + .atomic_update =3D ls2kbmc_primary_plane_helper_atomic_update, + .atomic_disable =3D ls2kbmc_primary_plane_helper_atomic_disable, + .get_scanout_buffer =3D ls2kbmc_primary_plane_helper_get_scanout_buffer, +}; + +static const struct drm_plane_funcs ls2kbmc_primary_plane_funcs =3D { + .update_plane =3D drm_atomic_helper_update_plane, + .disable_plane =3D drm_atomic_helper_disable_plane, + .destroy =3D drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, +}; + +static enum drm_mode_status ls2kbmc_crtc_helper_mode_valid(struct drm_crtc= *crtc, + const struct drm_display_mode *mode) +{ + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(crtc->dev); + + return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sdev->mode); +} + +/* + * The CRTC is always enabled. Screen updates are performed by + * the primary plane's atomic_update function. Disabling clears + * the screen in the primary plane's atomic_disable function. + */ +static const struct drm_crtc_helper_funcs ls2kbmc_crtc_helper_funcs =3D { + .mode_valid =3D ls2kbmc_crtc_helper_mode_valid, + .atomic_check =3D drm_crtc_helper_atomic_check, +}; + +static const struct drm_crtc_funcs ls2kbmc_crtc_funcs =3D { + .reset =3D drm_atomic_helper_crtc_reset, + .destroy =3D drm_crtc_cleanup, + .set_config =3D drm_atomic_helper_set_config, + .page_flip =3D drm_atomic_helper_page_flip, + .atomic_duplicate_state =3D drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state =3D drm_atomic_helper_crtc_destroy_state, +}; + +static const struct drm_encoder_funcs ls2kbmc_encoder_funcs =3D { + .destroy =3D drm_encoder_cleanup, +}; + +static int ls2kbmc_connector_helper_get_modes(struct drm_connector *connec= tor) +{ + struct ls2kbmc_device *sdev =3D ls2kbmc_device_of_dev(connector->dev); + + return drm_connector_helper_get_modes_fixed(connector, &sdev->mode); +} + +static const struct drm_connector_helper_funcs ls2kbmc_connector_helper_fu= ncs =3D { + .get_modes =3D ls2kbmc_connector_helper_get_modes, +}; + +static const struct drm_connector_funcs ls2kbmc_connector_funcs =3D { + .reset =3D drm_atomic_helper_connector_reset, + .fill_modes =3D drm_helper_probe_single_connector_modes, + .destroy =3D drm_connector_cleanup, + .atomic_duplicate_state =3D drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state =3D drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_mode_config_funcs ls2kbmc_mode_config_funcs =3D { + .fb_create =3D drm_gem_fb_create_with_dirty, + .atomic_check =3D drm_atomic_helper_check, + .atomic_commit =3D drm_atomic_helper_commit, +}; + +/* + * Init / Cleanup + */ + +static struct drm_display_mode ls2kbmc_mode(unsigned int width, unsigned i= nt height, + unsigned int width_mm, unsigned int height_mm) +{ + const struct drm_display_mode mode =3D { + DRM_MODE_INIT(60, width, height, width_mm, height_mm) + }; + + return mode; +} + +/* + * DRM driver + */ + +DEFINE_DRM_GEM_FOPS(ls2kbmc_fops); + +static struct drm_driver ls2kbmc_driver =3D { + DRM_GEM_SHMEM_DRIVER_OPS, + DRM_FBDEV_SHMEM_DRIVER_OPS, + .name =3D "simpledrm", + .desc =3D "DRM driver for Loongson-2K BMC", + .date =3D "20241211", + .major =3D 1, + .minor =3D 0, + .driver_features =3D DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, + .fops =3D &ls2kbmc_fops, +}; + +/* + * Currently the Loongson-2K0500 BMC hardware does not have an i2c interfa= ce to + * adapt to the resolution. + * We set the resolution by presetting "video=3D1280x1024-16@2M" to the bm= c memory. + */ +static int ls2kbmc_get_video_mode(struct pci_dev *pdev, struct simplefb_pl= atform_data *pd) +{ + char *mode; + int depth, ret; + + /* The pci mem bar last 16M is used to store the string. */ + mode =3D devm_ioremap(&pdev->dev, pci_resource_start(pdev, 0) + SZ_16M, S= Z_16M); + if (!mode) + return -ENOMEM; + + /*env at last 16M's beginning, first env is video */ + if (!strncmp(mode, "video=3D", 6)) + mode =3D mode + 6; + + ret =3D kstrtoint(strsep(&mode, "x"), 10, &pd->width); + if (ret) + return ret; + + ret =3D kstrtoint(strsep(&mode, "-"), 10, &pd->height); + if (ret) + return ret; + + ret =3D kstrtoint(strsep(&mode, "@"), 10, &depth); + if (ret) + return ret; + + pd->stride =3D pd->width * depth / 8; + pd->format =3D depth =3D=3D 32 ? "a8r8g8b8" : "r5g6b5"; + + return 0; +} + +static struct ls2kbmc_device *ls2kbmc_device_create(struct drm_driver *drv, + struct platform_device *pdev, + struct ls2kbmc_pdata *priv) +{ + struct pci_dev *ppdev =3D priv->pdev; + struct simplefb_platform_data *pd =3D &priv->pd; + struct ls2kbmc_device *sdev; + struct drm_device *dev; + int width, height, stride; + int width_mm =3D 0, height_mm =3D 0; + const struct drm_format_info *format; + struct resource *res, *mem =3D NULL; + struct drm_plane *primary_plane; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + unsigned long max_width, max_height; + void __iomem *screen_base; + size_t nformats; + int ret; + + sdev =3D devm_drm_dev_alloc(&pdev->dev, drv, struct ls2kbmc_device, dev); + if (IS_ERR(sdev)) + return ERR_CAST(sdev); + dev =3D &sdev->dev; + platform_set_drvdata(pdev, sdev); + + ret =3D ls2kbmc_get_video_mode(ppdev, pd); + if (ret) { + drm_err(dev, "no simplefb configuration found\n"); + return ERR_PTR(ret); + } + + width =3D simplefb_get_width_pd(dev, pd); + if (width < 0) + return ERR_PTR(width); + + height =3D simplefb_get_height_pd(dev, pd); + if (height < 0) + return ERR_PTR(height); + + stride =3D simplefb_get_stride_pd(dev, pd); + if (stride < 0) + return ERR_PTR(stride); + + if (!stride) { + stride =3D drm_format_info_min_pitch(format, 0, width); + if (drm_WARN_ON(dev, !stride)) + return ERR_PTR(-EINVAL); + } + + format =3D simplefb_get_format_pd(dev, pd); + if (IS_ERR(format)) + return ERR_CAST(format); + + /* + * Assume a monitor resolution of 96 dpi if physical dimensions + * are not specified to get a somewhat reasonable screen size. + */ + if (!width_mm) + width_mm =3D DRM_MODE_RES_MM(width, 96ul); + if (!height_mm) + height_mm =3D DRM_MODE_RES_MM(height, 96ul); + + sdev->mode =3D ls2kbmc_mode(width, height, width_mm, height_mm); + sdev->format =3D format; + sdev->pitch =3D stride; + + drm_dbg(dev, "display mode=3D{" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sdev->m= ode)); + drm_dbg(dev, "framebuffer format=3D%p4cc, size=3D%dx%d, stride=3D%d byte\= n", + &format->format, width, height, stride); + + /* + * Memory management + */ + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return ERR_PTR(-EINVAL); + + ret =3D aperture_remove_conflicting_pci_devices(ppdev, ls2kbmc_driver.nam= e); + if (ret) { + drm_err(dev, "could not acquire memory range %pr: %d\n", res, ret); + return ERR_PTR(ret); + } + + drm_dbg(dev, "using I/O memory framebuffer at %pr\n", res); + + mem =3D devm_request_mem_region(&ppdev->dev, res->start, resource_size(re= s), + drv->name); + if (!mem) { + /* + * We cannot make this fatal. Sometimes this comes from magic + * spaces our resource handlers simply don't know about. Use + * the I/O-memory resource as-is and try to map that instead. + */ + drm_warn(dev, "could not acquire memory region %pr\n", res); + mem =3D res; + } + + screen_base =3D devm_ioremap_wc(&ppdev->dev, mem->start, resource_size(me= m)); + if (!screen_base) + return ERR_PTR(-ENOMEM); + + iosys_map_set_vaddr_iomem(&sdev->screen_base, screen_base); + + /* + * Modesetting + */ + + ret =3D drmm_mode_config_init(dev); + if (ret) + return ERR_PTR(ret); + + max_width =3D max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH); + max_height =3D max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT); + + dev->mode_config.min_width =3D width; + dev->mode_config.max_width =3D max_width; + dev->mode_config.min_height =3D height; + dev->mode_config.max_height =3D max_height; + dev->mode_config.preferred_depth =3D format->depth; + dev->mode_config.funcs =3D &ls2kbmc_mode_config_funcs; + + /* Primary plane */ + + nformats =3D drm_fb_build_fourcc_list(dev, &format->format, 1, + sdev->formats, ARRAY_SIZE(sdev->formats)); + + primary_plane =3D &sdev->primary_plane; + ret =3D drm_universal_plane_init(dev, primary_plane, 0, &ls2kbmc_primary_= plane_funcs, + sdev->formats, nformats, + ls2kbmc_primary_plane_format_modifiers, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ERR_PTR(ret); + drm_plane_helper_add(primary_plane, &ls2kbmc_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary_plane); + + /* CRTC */ + + crtc =3D &sdev->crtc; + ret =3D drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, + &ls2kbmc_crtc_funcs, NULL); + if (ret) + return ERR_PTR(ret); + drm_crtc_helper_add(crtc, &ls2kbmc_crtc_helper_funcs); + + /* Encoder */ + + encoder =3D &sdev->encoder; + ret =3D drm_encoder_init(dev, encoder, &ls2kbmc_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); + if (ret) + return ERR_PTR(ret); + encoder->possible_crtcs =3D drm_crtc_mask(crtc); + + /* Connector */ + + connector =3D &sdev->connector; + ret =3D drm_connector_init(dev, connector, &ls2kbmc_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) + return ERR_PTR(ret); + drm_connector_helper_add(connector, &ls2kbmc_connector_helper_funcs); + drm_connector_set_panel_orientation_with_quirk(connector, + DRM_MODE_PANEL_ORIENTATION_UNKNOWN, + width, height); + + ret =3D drm_connector_attach_encoder(connector, encoder); + if (ret) + return ERR_PTR(ret); + + drm_mode_config_reset(dev); + + return sdev; +} + +/* + * Platform driver + */ + +static int ls2kbmc_probe(struct platform_device *pdev) +{ + struct ls2kbmc_device *sdev; + struct ls2kbmc_pdata *priv; + struct drm_device *dev; + int ret; + + priv =3D devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (IS_ERR(priv)) + return -ENOMEM; + + priv->pdev =3D *(struct pci_dev **)dev_get_platdata(&pdev->dev); + + sdev =3D ls2kbmc_device_create(&ls2kbmc_driver, pdev, priv); + if (IS_ERR(sdev)) + return PTR_ERR(sdev); + dev =3D &sdev->dev; + + ret =3D drm_dev_register(dev, 0); + if (ret) + return ret; + + drm_client_setup(dev, sdev->format); + + return 0; +} + +static void ls2kbmc_remove(struct platform_device *pdev) +{ + struct ls2kbmc_device *sdev =3D platform_get_drvdata(pdev); + struct drm_device *dev =3D &sdev->dev; + + drm_dev_unplug(dev); +} + +static struct platform_driver ls2kbmc_platform_driver =3D { + .driver =3D { + .name =3D "ls2kbmc-framebuffer", + }, + .probe =3D ls2kbmc_probe, + .remove =3D ls2kbmc_remove, +}; + +module_platform_driver(ls2kbmc_platform_driver); + +MODULE_DESCRIPTION("DRM driver for Loongson-2K BMC"); +MODULE_LICENSE("GPL"); --=20 2.43.5