diff options
author | Dave Airlie <airlied@redhat.com> | 2023-03-21 11:03:16 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2023-03-22 04:42:36 +1000 |
commit | c6265f5c2f502e442c4f339f121bedbc990c12e7 (patch) | |
tree | af3f9eddf92e49fb7321c0e01d790afbe8732c66 /drivers | |
parent | 90031bc33f7525f0cc7a9ef0b1df62a1a4463382 (diff) | |
parent | b24343eaceedb902c1625854f85a193b0549d85f (diff) | |
download | linux-stable-c6265f5c2f502e442c4f339f121bedbc990c12e7.tar.gz linux-stable-c6265f5c2f502e442c4f339f121bedbc990c12e7.tar.bz2 linux-stable-c6265f5c2f502e442c4f339f121bedbc990c12e7.zip |
Merge tag 'drm-misc-next-2023-03-16' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v6.4-rc1:
Cross-subsystem Changes:
- Add drm_bridge.h to drm_bridge maintainers.
Core Changes:
- Assorted fixes to TTM, tests, format-helper, accel.
- Assorted Makefile fixes to drivers and accel.
- Implement fbdev emulation for GEM DMA drivers, and convert a lot of
drivers to use it.
- Use tgid instead of pid for tracking clients.
Driver Changes:
- Assorted fixes in rockchip, vmwgfx, nouveau, cirrus.
- Add imx25 driver.
- Add Elida KD50T048A, Sony TD4353, Novatek NT36523, STARRY 2081101QFH032011-53G panels.
- Add 4K mode support to rockchip.
- Convert cirrus to use regular atomic helpers, and more cirrus
improvements.
- Add damage clipping to cirrus, virtio.
[airlied: add drm_bridge.h include to imx]
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/f7b765c7-d49d-edb5-2a6a-4f7a7be16a59@linux.intel.com
Diffstat (limited to 'drivers')
65 files changed, 2696 insertions, 287 deletions
diff --git a/drivers/accel/Makefile b/drivers/accel/Makefile index 07aa77aed1c8..f22fd44d586b 100644 --- a/drivers/accel/Makefile +++ b/drivers/accel/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y += habanalabs/ -obj-y += ivpu/ +obj-$(CONFIG_DRM_ACCEL_HABANALABS) += habanalabs/ +obj-$(CONFIG_DRM_ACCEL_IVPU) += ivpu/ diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 1e04d135e866..a33257d2bc7f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o drm_dma_helper-y := drm_gem_dma_helper.o +drm_dma_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_dma.o drm_dma_helper-$(CONFIG_DRM_KMS_HELPER) += drm_fb_dma_helper.o obj-$(CONFIG_DRM_GEM_DMA_HELPER) += drm_dma_helper.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index d8e683688daa..863cb668e000 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -969,7 +969,7 @@ static int amdgpu_debugfs_gem_info_show(struct seq_file *m, void *unused) * Therefore, we need to protect this ->comm access using RCU. */ rcu_read_lock(); - task = pid_task(file->pid, PIDTYPE_PID); + task = pid_task(file->pid, PIDTYPE_TGID); seq_printf(m, "pid %8d command %s:\n", pid_nr(file->pid), task ? task->comm : "<unknown>"); rcu_read_unlock(); diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index e3507dd6f82a..a554c79dcd39 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -26,7 +26,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_debugfs.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_modeset_helper.h> @@ -301,7 +301,7 @@ static int hdlcd_drm_bind(struct device *dev) if (ret) goto err_register; - drm_fbdev_generic_setup(drm, 32); + drm_fbdev_dma_setup(drm, 32); return 0; diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index cf040e2e9efe..0597e6ad56e7 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -19,7 +19,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -852,7 +852,7 @@ static int malidp_bind(struct device *dev) if (ret) goto register_fail; - drm_fbdev_generic_setup(drm, 32); + drm_fbdev_dma_setup(drm, 32); return 0; diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c index ecfb060d2557..c8c7f8215155 100644 --- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c @@ -15,7 +15,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_device.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_module.h> @@ -341,7 +341,7 @@ static int aspeed_gfx_probe(struct platform_device *pdev) if (ret) goto err_unload; - drm_fbdev_generic_setup(&priv->drm, 32); + drm_fbdev_dma_setup(&priv->drm, 32); return 0; err_unload: diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 4e806b06d35d..29603561d501 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -19,7 +19,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_module.h> @@ -760,7 +760,7 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) if (ret) goto err_unload; - drm_fbdev_generic_setup(ddev, 24); + drm_fbdev_dma_setup(ddev, 24); return 0; diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 4f643a490dc3..4855230ba2c6 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -80,7 +80,7 @@ static int drm_clients_info(struct seq_file *m, void *data) seq_printf(m, "%20s %5s %3s master a %5s %10s\n", "command", - "pid", + "tgid", "dev", "uid", "magic"); @@ -94,7 +94,7 @@ static int drm_clients_info(struct seq_file *m, void *data) bool is_current_master = drm_is_current_master(priv); rcu_read_lock(); /* locks pid_task()->comm */ - task = pid_task(priv->pid, PIDTYPE_PID); + task = pid_task(priv->pid, PIDTYPE_TGID); uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID; seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n", task ? task->comm : "<unknown>", diff --git a/drivers/gpu/drm/drm_fbdev_dma.c b/drivers/gpu/drm/drm_fbdev_dma.c new file mode 100644 index 000000000000..cf553ac12a0f --- /dev/null +++ b/drivers/gpu/drm/drm_fbdev_dma.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: MIT + +#include <drm/drm_crtc_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_dma_helper.h> + +#include <drm/drm_fbdev_dma.h> + +/* + * struct fb_ops + */ + +static int drm_fbdev_dma_fb_open(struct fb_info *info, int user) +{ + struct drm_fb_helper *fb_helper = info->par; + + /* No need to take a ref for fbcon because it unbinds on unregister */ + if (user && !try_module_get(fb_helper->dev->driver->fops->owner)) + return -ENODEV; + + return 0; +} + +static int drm_fbdev_dma_fb_release(struct fb_info *info, int user) +{ + struct drm_fb_helper *fb_helper = info->par; + + if (user) + module_put(fb_helper->dev->driver->fops->owner); + + return 0; +} + +static void drm_fbdev_dma_fb_destroy(struct fb_info *info) +{ + struct drm_fb_helper *fb_helper = info->par; + + if (!fb_helper->dev) + return; + + drm_fb_helper_fini(fb_helper); + + drm_client_buffer_vunmap(fb_helper->buffer); + drm_client_framebuffer_delete(fb_helper->buffer); + drm_client_release(&fb_helper->client); + drm_fb_helper_unprepare(fb_helper); + kfree(fb_helper); +} + +static int drm_fbdev_dma_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct drm_fb_helper *fb_helper = info->par; + struct drm_device *dev = fb_helper->dev; + + if (drm_WARN_ON_ONCE(dev, !fb_helper->dev->driver->gem_prime_mmap)) + return -ENODEV; + + return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma); +} + +static const struct fb_ops drm_fbdev_dma_fb_ops = { + .owner = THIS_MODULE, + .fb_open = drm_fbdev_dma_fb_open, + .fb_release = drm_fbdev_dma_fb_release, + .fb_read = drm_fb_helper_sys_read, + .fb_write = drm_fb_helper_sys_write, + DRM_FB_HELPER_DEFAULT_OPS, + .fb_fillrect = drm_fb_helper_sys_fillrect, + .fb_copyarea = drm_fb_helper_sys_copyarea, + .fb_imageblit = drm_fb_helper_sys_imageblit, + .fb_destroy = drm_fbdev_dma_fb_destroy, + .fb_mmap = drm_fbdev_dma_fb_mmap, +}; + +/* + * struct drm_fb_helper + */ + +static int drm_fbdev_dma_helper_fb_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_client_dev *client = &fb_helper->client; + struct drm_device *dev = fb_helper->dev; + struct drm_client_buffer *buffer; + struct drm_gem_dma_object *dma_obj; + struct drm_framebuffer *fb; + struct fb_info *info; + u32 format; + struct iosys_map map; + int ret; + + drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n", + sizes->surface_width, sizes->surface_height, + sizes->surface_bpp); + + format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); + buffer = drm_client_framebuffer_create(client, sizes->surface_width, + sizes->surface_height, format); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + dma_obj = to_drm_gem_dma_obj(buffer->gem); + + fb = buffer->fb; + if (drm_WARN_ON(dev, fb->funcs->dirty)) { + ret = -ENODEV; /* damage handling not supported; use generic emulation */ + goto err_drm_client_buffer_delete; + } + + ret = drm_client_buffer_vmap(buffer, &map); + if (ret) { + goto err_drm_client_buffer_delete; + } else if (drm_WARN_ON(dev, map.is_iomem)) { + ret = -ENODEV; /* I/O memory not supported; use generic emulation */ + goto err_drm_client_buffer_delete; + } + + fb_helper->buffer = buffer; + fb_helper->fb = buffer->fb; + + info = drm_fb_helper_alloc_info(fb_helper); + if (IS_ERR(info)) { + ret = PTR_ERR(info); + goto err_drm_client_buffer_vunmap; + } + + drm_fb_helper_fill_info(info, fb_helper, sizes); + + info->fbops = &drm_fbdev_dma_fb_ops; + info->flags = FBINFO_DEFAULT; + + /* screen */ + info->flags |= FBINFO_VIRTFB; /* system memory */ + if (dma_obj->map_noncoherent) + info->flags |= FBINFO_READS_FAST; /* signal caching */ + info->screen_size = sizes->surface_height * fb->pitches[0]; + info->screen_buffer = map.vaddr; + info->fix.smem_len = info->screen_size; + +#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) + /* + * Shamelessly leak the physical address to user-space. + */ + if (fb_helper->hint_leak_smem_start && !info->fix.smem_start) + info->fix.smem_start = page_to_phys(virt_to_page(info->screen_buffer)); +#endif + + return 0; + +err_drm_client_buffer_vunmap: + fb_helper->fb = NULL; + fb_helper->buffer = NULL; + drm_client_buffer_vunmap(buffer); +err_drm_client_buffer_delete: + drm_client_framebuffer_delete(buffer); + return ret; +} + +static const struct drm_fb_helper_funcs drm_fbdev_dma_helper_funcs = { + .fb_probe = drm_fbdev_dma_helper_fb_probe, +}; + +/* + * struct drm_client_funcs + */ + +static void drm_fbdev_dma_client_unregister(struct drm_client_dev *client) +{ + struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); + + if (fb_helper->info) { + drm_fb_helper_unregister_info(fb_helper); + } else { + drm_client_release(&fb_helper->client); + drm_fb_helper_unprepare(fb_helper); + kfree(fb_helper); + } +} + +static int drm_fbdev_dma_client_restore(struct drm_client_dev *client) +{ + drm_fb_helper_lastclose(client->dev); + + return 0; +} + +static int drm_fbdev_dma_client_hotplug(struct drm_client_dev *client) +{ + struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); + struct drm_device *dev = client->dev; + int ret; + + if (dev->fb_helper) + return drm_fb_helper_hotplug_event(dev->fb_helper); + + ret = drm_fb_helper_init(dev, fb_helper); + if (ret) + goto err_drm_err; + + if (!drm_drv_uses_atomic_modeset(dev)) + drm_helper_disable_unused_functions(dev); + + ret = drm_fb_helper_initial_config(fb_helper); + if (ret) + goto err_drm_fb_helper_fini; + + return 0; + +err_drm_fb_helper_fini: + drm_fb_helper_fini(fb_helper); +err_drm_err: + drm_err(dev, "fbdev-dma: Failed to setup generic emulation (ret=%d)\n", ret); + return ret; +} + +static const struct drm_client_funcs drm_fbdev_dma_client_funcs = { + .owner = THIS_MODULE, + .unregister = drm_fbdev_dma_client_unregister, + .restore = drm_fbdev_dma_client_restore, + .hotplug = drm_fbdev_dma_client_hotplug, +}; + +/** + * drm_fbdev_dma_setup() - Setup fbdev emulation for GEM DMA helpers + * @dev: DRM device + * @preferred_bpp: Preferred bits per pixel for the device. + * @dev->mode_config.preferred_depth is used if this is zero. + * + * This function sets up fbdev emulation for GEM DMA drivers that support + * dumb buffers with a virtual address and that can be mmap'ed. + * drm_fbdev_dma_setup() shall be called after the DRM driver registered + * the new DRM device with drm_dev_register(). + * + * Restore, hotplug events and teardown are all taken care of. Drivers that do + * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves. + * Simple drivers might use drm_mode_config_helper_suspend(). + * + * This function is safe to call even when there are no connectors present. + * Setup will be retried on the next hotplug event. + * + * The fbdev is destroyed by drm_dev_unregister(). + */ +void drm_fbdev_dma_setup(struct drm_device *dev, unsigned int preferred_bpp) +{ + struct drm_fb_helper *fb_helper; + int ret; + + drm_WARN(dev, !dev->registered, "Device has not been registered.\n"); + drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n"); + + fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL); + if (!fb_helper) + return; + drm_fb_helper_prepare(dev, fb_helper, preferred_bpp, &drm_fbdev_dma_helper_funcs); + + ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_dma_client_funcs); + if (ret) { + drm_err(dev, "Failed to register client: %d\n", ret); + goto err_drm_client_init; + } + + ret = drm_fbdev_dma_client_hotplug(&fb_helper->client); + if (ret) + drm_dbg_kms(dev, "client hotplug ret=%d\n", ret); + + drm_client_register(&fb_helper->client); + + return; + +err_drm_client_init: + drm_fb_helper_unprepare(fb_helper); + kfree(fb_helper); +} +EXPORT_SYMBOL(drm_fbdev_dma_setup); diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index a51ff8cee049..c1018c470047 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -156,7 +156,7 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor) if (!file) return ERR_PTR(-ENOMEM); - file->pid = get_pid(task_pid(current)); + file->pid = get_pid(task_tgid(current)); file->minor = minor; /* for compatibility root is always authenticated */ diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 3d2f025d4fd4..4049fa4273ab 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -2,7 +2,7 @@ config DRM_EXYNOS tristate "DRM Support for Samsung SoC Exynos Series" depends on OF && DRM && COMMON_CLK - depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || ARCH_MULTIPLATFORM || COMPILE_TEST + depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST depends on MMU select DRM_DISPLAY_HELPER if DRM_EXYNOS_DP select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 8579c7629f5e..c09ba019ba5e 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -20,7 +20,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_modeset_helper.h> #include <drm/drm_module.h> @@ -333,7 +333,7 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) if (ret < 0) goto put; - drm_fbdev_generic_setup(drm, legacyfb_depth); + drm_fbdev_dma_setup(drm, legacyfb_depth); return 0; diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index e5749927fd6c..03535a15dd8f 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -2,3 +2,4 @@ source "drivers/gpu/drm/imx/dcss/Kconfig" source "drivers/gpu/drm/imx/ipuv3/Kconfig" +source "drivers/gpu/drm/imx/lcdc/Kconfig" diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index 909622864716..86f38e7c7422 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_DRM_IMX_DCSS) += dcss/ obj-$(CONFIG_DRM_IMX) += ipuv3/ +obj-$(CONFIG_DRM_IMX_LCDC) += lcdc/ diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c index dab5e664920d..896de946f8df 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-kms.c +++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c @@ -7,7 +7,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge_connector.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_of.h> @@ -145,7 +145,7 @@ struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss) if (ret) goto cleanup_crtc; - drm_fbdev_generic_setup(drm, 32); + drm_fbdev_dma_setup(drm, 32); return kms; diff --git a/drivers/gpu/drm/imx/ipuv3/Kconfig b/drivers/gpu/drm/imx/ipuv3/Kconfig index bb278a369575..bacf0655ebaf 100644 --- a/drivers/gpu/drm/imx/ipuv3/Kconfig +++ b/drivers/gpu/drm/imx/ipuv3/Kconfig @@ -4,7 +4,7 @@ config DRM_IMX select DRM_KMS_HELPER select VIDEOMODE_HELPERS select DRM_GEM_DMA_HELPER - depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM || COMPILE_TEST) + depends on DRM && (ARCH_MXC || COMPILE_TEST) depends on IMX_IPUV3_CORE help enable i.MX graphics support diff --git a/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c index e060fa6cbcb9..4a866ac60fff 100644 --- a/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c @@ -16,7 +16,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_managed.h> @@ -249,7 +249,7 @@ static int imx_drm_bind(struct device *dev) if (ret) goto err_poll_fini; - drm_fbdev_generic_setup(drm, legacyfb_depth); + drm_fbdev_dma_setup(drm, legacyfb_depth); return 0; diff --git a/drivers/gpu/drm/imx/lcdc/Kconfig b/drivers/gpu/drm/imx/lcdc/Kconfig new file mode 100644 index 000000000000..7e57922bbd9d --- /dev/null +++ b/drivers/gpu/drm/imx/lcdc/Kconfig @@ -0,0 +1,7 @@ +config DRM_IMX_LCDC + tristate "Freescale i.MX LCDC displays" + depends on DRM && (ARCH_MXC || COMPILE_TEST) + select DRM_GEM_DMA_HELPER + select DRM_KMS_HELPER + help + Found on i.MX1, i.MX21, i.MX25 and i.MX27. diff --git a/drivers/gpu/drm/imx/lcdc/Makefile b/drivers/gpu/drm/imx/lcdc/Makefile new file mode 100644 index 000000000000..e84daa432c2e --- /dev/null +++ b/drivers/gpu/drm/imx/lcdc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DRM_IMX_LCDC) += imx-lcdc.o diff --git a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c new file mode 100644 index 000000000000..8e6d457917da --- /dev/null +++ b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: 2020 Marian Cichy <M.Cichy@pengutronix.de> + +#include <drm/drm_bridge.h> +#include <drm/drm_bridge_connector.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fb_dma_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <drm/drm_vblank.h> +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#define IMX21LCDC_LSSAR 0x0000 /* LCDC Screen Start Address Register */ +#define IMX21LCDC_LSR 0x0004 /* LCDC Size Register */ +#define IMX21LCDC_LVPWR 0x0008 /* LCDC Virtual Page Width Register */ +#define IMX21LCDC_LCPR 0x000C /* LCDC Cursor Position Register */ +#define IMX21LCDC_LCWHB 0x0010 /* LCDC Cursor Width Height and Blink Register*/ +#define IMX21LCDC_LCCMR 0x0014 /* LCDC Color Cursor Mapping Register */ +#define IMX21LCDC_LPCR 0x0018 /* LCDC Panel Configuration Register */ +#define IMX21LCDC_LHCR 0x001C /* LCDC Horizontal Configuration Register */ +#define IMX21LCDC_LVCR 0x0020 /* LCDC Vertical Configuration Register */ +#define IMX21LCDC_LPOR 0x0024 /* LCDC Panning Offset Register */ +#define IMX21LCDC_LSCR 0x0028 /* LCDC Sharp Configuration Register */ +#define IMX21LCDC_LPCCR 0x002C /* LCDC PWM Contrast Control Register */ +#define IMX21LCDC_LDCR 0x0030 /* LCDC DMA Control Register */ +#define IMX21LCDC_LRMCR 0x0034 /* LCDC Refresh Mode Control Register */ +#define IMX21LCDC_LICR 0x0038 /* LCDC Interrupt Configuration Register */ +#define IMX21LCDC_LIER 0x003C /* LCDC Interrupt Enable Register */ +#define IMX21LCDC_LISR 0x0040 /* LCDC Interrupt Status Register */ +#define IMX21LCDC_LGWSAR 0x0050 /* LCDC Graphic Window Start Address Register */ +#define IMX21LCDC_LGWSR 0x0054 /* LCDC Graph Window Size Register */ +#define IMX21LCDC_LGWVPWR 0x0058 /* LCDC Graphic Window Virtual Page Width Register */ +#define IMX21LCDC_LGWPOR 0x005C /* LCDC Graphic Window Panning Offset Register */ +#define IMX21LCDC_LGWPR 0x0060 /* LCDC Graphic Window Position Register */ +#define IMX21LCDC_LGWCR 0x0064 /* LCDC Graphic Window Control Register */ +#define IMX21LCDC_LGWDCR 0x0068 /* LCDC Graphic Window DMA Control Register */ +#define IMX21LCDC_LAUSCR 0x0080 /* LCDC AUS Mode Control Register */ +#define IMX21LCDC_LAUSCCR 0x0084 /* LCDC AUS Mode Cursor Control Register */ +#define IMX21LCDC_BGLUT 0x0800 /* Background Lookup Table */ +#define IMX21LCDC_GWLUT 0x0C00 /* Graphic Window Lookup Table */ + +#define IMX21LCDC_LCPR_CC0 BIT(30) /* Cursor Control Bit 0 */ +#define IMX21LCDC_LCPR_CC1 BIT(31) /* Cursor Control Bit 1 */ + +/* Values HSYNC, VSYNC and Framesize Register */ +#define IMX21LCDC_LHCR_HWIDTH GENMASK(31, 26) +#define IMX21LCDC_LHCR_HFPORCH GENMASK(15, 8) /* H_WAIT_1 in the i.MX25 Reference manual */ +#define IMX21LCDC_LHCR_HBPORCH GENMASK(7, 0) /* H_WAIT_2 in the i.MX25 Reference manual */ + +#define IMX21LCDC_LVCR_VWIDTH GENMASK(31, 26) +#define IMX21LCDC_LVCR_VFPORCH GENMASK(15, 8) /* V_WAIT_1 in the i.MX25 Reference manual */ +#define IMX21LCDC_LVCR_VBPORCH GENMASK(7, 0) /* V_WAIT_2 in the i.MX25 Reference manual */ + +#define IMX21LCDC_LSR_XMAX GENMASK(25, 20) +#define IMX21LCDC_LSR_YMAX GENMASK(9, 0) + +/* Values for LPCR Register */ +#define IMX21LCDC_LPCR_PCD GENMASK(5, 0) +#define IMX21LCDC_LPCR_SHARP BIT(6) +#define IMX21LCDC_LPCR_SCLKSEL BIT(7) +#define IMX21LCDC_LPCR_ACD GENMASK(14, 8) +#define IMX21LCDC_LPCR_ACDSEL BIT(15) +#define IMX21LCDC_LPCR_REV_VS BIT(16) +#define IMX21LCDC_LPCR_SWAP_SEL BIT(17) +#define IMX21LCDC_LPCR_END_SEL BIT(18) +#define IMX21LCDC_LPCR_SCLKIDLE BIT(19) +#define IMX21LCDC_LPCR_OEPOL BIT(20) +#define IMX21LCDC_LPCR_CLKPOL BIT(21) +#define IMX21LCDC_LPCR_LPPOL BIT(22) +#define IMX21LCDC_LPCR_FLMPOL BIT(23) +#define IMX21LCDC_LPCR_PIXPOL BIT(24) +#define IMX21LCDC_LPCR_BPIX GENMASK(27, 25) +#define IMX21LCDC_LPCR_PBSIZ GENMASK(29, 28) +#define IMX21LCDC_LPCR_COLOR BIT(30) +#define IMX21LCDC_LPCR_TFT BIT(31) + +#define INTR_EOF BIT(1) /* VBLANK Interrupt Bit */ + +#define BPP_RGB565 0x05 +#define BPP_XRGB8888 0x07 + +#define LCDC_MIN_XRES 64 +#define LCDC_MIN_YRES 64 + +#define LCDC_MAX_XRES 1024 +#define LCDC_MAX_YRES 1024 + +struct imx_lcdc { + struct drm_device drm; + struct drm_simple_display_pipe pipe; + struct drm_connector *connector; + void __iomem *base; + + struct clk *clk_ipg; + struct clk *clk_ahb; + struct clk *clk_per; +}; + +static const u32 imx_lcdc_formats[] = { + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, +}; + +static inline struct imx_lcdc *imx_lcdc_from_drmdev(struct drm_device *drm) +{ + return container_of(drm, struct imx_lcdc, drm); +} + +static unsigned int imx_lcdc_get_format(unsigned int drm_format) +{ + switch (drm_format) { + default: + DRM_WARN("Format not supported - fallback to XRGB8888\n"); + fallthrough; + + case DRM_FORMAT_XRGB8888: + return BPP_XRGB8888; + + case DRM_FORMAT_RGB565: + return BPP_RGB565; + } +} + +static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *old_state, + bool mode_set) +{ + struct drm_crtc *crtc = &pipe->crtc; + struct drm_plane_state *new_state = pipe->plane.state; + struct drm_framebuffer *fb = new_state->fb; + struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev); + u32 lpcr, lvcr, lhcr; + u32 framesize; + dma_addr_t addr; + + addr = drm_fb_dma_get_gem_addr(fb, new_state, 0); + /* The LSSAR register specifies the LCD screen start address (SSA). */ + writel(addr, lcdc->base + IMX21LCDC_LSSAR); + + if (!mode_set) + return; + + /* Disable PER clock to make register write possible */ + if (old_state && old_state->crtc && old_state->crtc->enabled) + clk_disable_unprepare(lcdc->clk_per); + + /* Framesize */ + framesize = FIELD_PREP(IMX21LCDC_LSR_XMAX, crtc->mode.hdisplay >> 4) | + FIELD_PREP(IMX21LCDC_LSR_YMAX, crtc->mode.vdisplay); + writel(framesize, lcdc->base + IMX21LCDC_LSR); + + /* HSYNC */ + lhcr = FIELD_PREP(IMX21LCDC_LHCR_HFPORCH, crtc->mode.hsync_start - crtc->mode.hdisplay - 1) | + FIELD_PREP(IMX21LCDC_LHCR_HWIDTH, crtc->mode.hsync_end - crtc->mode.hsync_start - 1) | + FIELD_PREP(IMX21LCDC_LHCR_HBPORCH, crtc->mode.htotal - crtc->mode.hsync_end - 3); + writel(lhcr, lcdc->base + IMX21LCDC_LHCR); + + /* VSYNC */ + lvcr = FIELD_PREP(IMX21LCDC_LVCR_VFPORCH, crtc->mode.vsync_start - crtc->mode.vdisplay) | + FIELD_PREP(IMX21LCDC_LVCR_VWIDTH, crtc->mode.vsync_end - crtc->mode.vsync_start) | + FIELD_PREP(IMX21LCDC_LVCR_VBPORCH, crtc->mode.vtotal - crtc->mode.vsync_end); + writel(lvcr, lcdc->base + IMX21LCDC_LVCR); + + lpcr = readl(lcdc->base + IMX21LCDC_LPCR); + lpcr &= ~IMX21LCDC_LPCR_BPIX; + lpcr |= FIELD_PREP(IMX21LCDC_LPCR_BPIX, imx_lcdc_get_format(fb->format->format)); + writel(lpcr, lcdc->base + IMX21LCDC_LPCR); + + /* Virtual Page Width */ + writel(new_state->fb->pitches[0] / 4, lcdc->base + IMX21LCDC_LVPWR); + + /* Enable PER clock */ + if (new_state->crtc->enabled) + clk_prepare_enable(lcdc->clk_per); +} + +static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ + int ret; + int clk_div; + int bpp; + struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev); + struct drm_display_mode *mode = &pipe->crtc.mode; + struct drm_display_info *disp_info = &lcdc->connector->display_info; + const int hsync_pol = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : 1; + const int vsync_pol = (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : 1; + const int data_enable_pol = + (disp_info->bus_flags & DRM_BUS_FLAG_DE_HIGH) ? 0 : 1; + const int clk_pol = + (disp_info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE) ? 0 : 1; + + clk_div = DIV_ROUND_CLOSEST_ULL(clk_get_rate(lcdc->clk_per), + mode->clock * 1000); + bpp = imx_lcdc_get_format(plane_state->fb->format->format); + + writel(FIELD_PREP(IMX21LCDC_LPCR_PCD, clk_div - 1) | + FIELD_PREP(IMX21LCDC_LPCR_LPPOL, hsync_pol) | + FIELD_PREP(IMX21LCDC_LPCR_FLMPOL, vsync_pol) | + FIELD_PREP(IMX21LCDC_LPCR_OEPOL, data_enable_pol) | + FIELD_PREP(IMX21LCDC_LPCR_TFT, 1) | + FIELD_PREP(IMX21LCDC_LPCR_COLOR, 1) | + FIELD_PREP(IMX21LCDC_LPCR_PBSIZ, 3) | + FIELD_PREP(IMX21LCDC_LPCR_BPIX, bpp) | + FIELD_PREP(IMX21LCDC_LPCR_SCLKSEL, 1) | + FIELD_PREP(IMX21LCDC_LPCR_PIXPOL, 0) | + FIELD_PREP(IMX21LCDC_LPCR_CLKPOL, clk_pol), + lcdc->base + IMX21LCDC_LPCR); + + /* 0px panning offset */ + writel(0x00000000, lcdc->base + IMX21LCDC_LPOR); + + /* disable hardware cursor */ + writel(readl(lcdc->base + IMX21LCDC_LCPR) & ~(IMX21LCDC_LCPR_CC0 | IMX21LCDC_LCPR_CC1), + lcdc->base + IMX21LCDC_LCPR); + + ret = clk_prepare_enable(lcdc->clk_ipg); + if (ret) { + dev_err(pipe->crtc.dev->dev, "Cannot enable ipg clock: %pe\n", ERR_PTR(ret)); + return; + } + ret = clk_prepare_enable(lcdc->clk_ahb); + if (ret) { + dev_err(pipe->crtc.dev->dev, "Cannot enable ahb clock: %pe\n", ERR_PTR(ret)); + + clk_disable_unprepare(lcdc->clk_ipg); + + return; + } + + imx_lcdc_update_hw_registers(pipe, NULL, true); + + /* Enable VBLANK Interrupt */ + writel(INTR_EOF, lcdc->base + IMX21LCDC_LIER); +} + +static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe) +{ + struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev); + struct drm_crtc *crtc = &lcdc->pipe.crtc; + struct drm_pending_vblank_event *event; + + clk_disable_unprepare(lcdc->clk_ahb); + clk_disable_unprepare(lcdc->clk_ipg); + + if (pipe->crtc.enabled) + clk_disable_unprepare(lcdc->clk_per); + + spin_lock_irq(&lcdc->drm.event_lock); + event = crtc->state->event; + if (event) { + crtc->state->event = NULL; + drm_crtc_send_vblank_event(crtc, event); + } + spin_unlock_irq(&lcdc->drm.event_lock); + + /* Disable VBLANK Interrupt */ + writel(0, lcdc->base + IMX21LCDC_LIER); +} + +static int imx_lcdc_pipe_check(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state, + struct drm_crtc_state *crtc_state) +{ + const struct drm_display_mode *mode = &crtc_state->mode; + const struct drm_display_mode *old_mode = &pipe->crtc.state->mode; + + if (mode->hdisplay < LCDC_MIN_XRES || mode->hdisplay > LCDC_MAX_XRES || + mode->vdisplay < LCDC_MIN_YRES || mode->vdisplay > LCDC_MAX_YRES || + mode->hdisplay % 0x10) { /* must be multiple of 16 */ + drm_err(pipe->crtc.dev, "unsupported display mode (%u x %u)\n", + mode->hdisplay, mode->vdisplay); + return -EINVAL; + } + + crtc_state->mode_changed = + old_mode->hdisplay != mode->hdisplay || + old_mode->vdisplay != mode->vdisplay; + + return 0; +} + +static void imx_lcdc_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *old_state) +{ + struct drm_crtc *crtc = &pipe->crtc; + struct drm_pending_vblank_event *event = crtc->state->event; + struct drm_plane_state *new_state = pipe->plane.state; + struct drm_framebuffer *fb = new_state->fb; + struct drm_framebuffer *old_fb = old_state->fb; + struct drm_crtc *old_crtc = old_state->crtc; + bool mode_changed = false; + + if (old_fb && old_fb->format != fb->format) + mode_changed = true; + else if (old_crtc != crtc) + mode_changed = true; + + imx_lcdc_update_hw_registers(pipe, old_state, mode_changed); + + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + + if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + + spin_unlock_irq(&crtc->dev->event_lock); + } +} + +static const struct drm_simple_display_pipe_funcs imx_lcdc_pipe_funcs = { + .enable = imx_lcdc_pipe_enable, + .disable = imx_lcdc_pipe_disable, + .check = imx_lcdc_pipe_check, + .update = imx_lcdc_pipe_update, +}; + +static const struct drm_mode_config_funcs imx_lcdc_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static const struct drm_mode_config_helper_funcs imx_lcdc_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, +}; + +static void imx_lcdc_release(struct drm_device *drm) +{ + struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(drm); + + drm_kms_helper_poll_fini(drm); + kfree(lcdc); +} + +DEFINE_DRM_GEM_DMA_FOPS(imx_lcdc_drm_fops); + +static struct drm_driver imx_lcdc_drm_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &imx_lcdc_drm_fops, + DRM_GEM_DMA_DRIVER_OPS_VMAP, + .release = imx_lcdc_release, + .name = "imx-lcdc", + .desc = "i.MX LCDC driver", + .date = "20200716", +}; + +static const struct of_device_id imx_lcdc_of_dev_id[] = { + { + .compatible = "fsl,imx21-lcdc", + }, + { + .compatible = "fsl,imx25-lcdc", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_lcdc_of_dev_id); + +static irqreturn_t imx_lcdc_irq_handler(int irq, void *arg) +{ + struct imx_lcdc *lcdc = arg; + struct drm_crtc *crtc = &lcdc->pipe.crtc; + unsigned int status; + + status = readl(lcdc->base + IMX21LCDC_LISR); + + if (status & INTR_EOF) { + drm_crtc_handle_vblank(crtc); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int imx_lcdc_probe(struct platform_device *pdev) +{ + struct imx_lcdc *lcdc; + struct drm_device *drm; + struct drm_bridge *bridge; + int irq; + int ret; + struct device *dev = &pdev->dev; + + lcdc = devm_drm_dev_alloc(dev, &imx_lcdc_drm_driver, + struct imx_lcdc, drm); + if (!lcdc) + return -ENOMEM; + + drm = &lcdc->drm; + + lcdc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(lcdc->base)) + return dev_err_probe(dev, PTR_ERR(lcdc->base), "Cannot get IO memory\n"); + + bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); + if (IS_ERR(bridge)) + return dev_err_probe(dev, PTR_ERR(bridge), "Failed to find bridge\n"); + + /* Get Clocks */ + lcdc->clk_ipg = devm_clk_get(dev, "ipg"); + if (IS_ERR(lcdc->clk_ipg)) + return dev_err_probe(dev, PTR_ERR(lcdc->clk_ipg), "Failed to get %s clk\n", "ipg"); + + lcdc->clk_ahb = devm_clk_get(dev, "ahb"); + if (IS_ERR(lcdc->clk_ahb)) + return dev_err_probe(dev, PTR_ERR(lcdc->clk_ahb), "Failed to get %s clk\n", "ahb"); + + lcdc->clk_per = devm_clk_get(dev, "per"); + if (IS_ERR(lcdc->clk_per)) + return dev_err_probe(dev, PTR_ERR(lcdc->clk_per), "Failed to get %s clk\n", "per"); + + ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(dev, ret, "Cannot set DMA Mask\n"); + + /* Modeset init */ + ret = drmm_mode_config_init(drm); + if (ret) + return dev_err_probe(dev, ret, "Cannot initialize mode configuration structure\n"); + + /* CRTC, Plane, Encoder */ + ret = drm_simple_display_pipe_init(drm, &lcdc->pipe, + &imx_lcdc_pipe_funcs, + imx_lcdc_formats, + ARRAY_SIZE(imx_lcdc_formats), NULL, NULL); + if (ret < 0) + return dev_err_probe(drm->dev, ret, "Cannot setup simple display pipe\n"); + + ret = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (ret < 0) + return dev_err_probe(drm->dev, ret, "Failed to initialize vblank\n"); + + ret = drm_bridge_attach(&lcdc->pipe.encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) + return dev_err_probe(drm->dev, ret, "Cannot attach bridge\n"); + + lcdc->connector = drm_bridge_connector_init(drm, &lcdc->pipe.encoder); + if (IS_ERR(lcdc->connector)) + return dev_err_probe(drm->dev, PTR_ERR(lcdc->connector), "Cannot init bridge connector\n"); + + drm_connector_attach_encoder(lcdc->connector, &lcdc->pipe.encoder); + + /* + * The LCDC controller does not have an enable bit. The + * controller starts directly when the clocks are enabled. + * If the clocks are enabled when the controller is not yet + * programmed with proper register values (enabled at the + * bootloader, for example) then it just goes into some undefined + * state. + * To avoid this issue, let's enable and disable LCDC IPG, + * PER and AHB clock so that we force some kind of 'reset' + * to the LCDC block. + */ + + ret = clk_prepare_enable(lcdc->clk_ipg); + if (ret) + return dev_err_probe(dev, ret, "Cannot enable ipg clock\n"); + clk_disable_unprepare(lcdc->clk_ipg); + + ret = clk_prepare_enable(lcdc->clk_per); + if (ret) + return dev_err_probe(dev, ret, "Cannot enable per clock\n"); + clk_disable_unprepare(lcdc->clk_per); + + ret = clk_prepare_enable(lcdc->clk_ahb); + if (ret) + return dev_err_probe(dev, ret, "Cannot enable ahb clock\n"); + clk_disable_unprepare(lcdc->clk_ahb); + + drm->mode_config.min_width = LCDC_MIN_XRES; + drm->mode_config.max_width = LCDC_MAX_XRES; + drm->mode_config.min_height = LCDC_MIN_YRES; + drm->mode_config.max_height = LCDC_MAX_YRES; + drm->mode_config.preferred_depth = 16; + drm->mode_config.funcs = &imx_lcdc_mode_config_funcs; + drm->mode_config.helper_private = &imx_lcdc_mode_config_helpers; + + drm_mode_config_reset(drm); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + return ret; + } + + ret = devm_request_irq(dev, irq, imx_lcdc_irq_handler, 0, "imx-lcdc", lcdc); + if (ret < 0) + return dev_err_probe(drm->dev, ret, "Failed to install IRQ handler\n"); + + platform_set_drvdata(pdev, drm); + + ret = drm_dev_register(&lcdc->drm, 0); + if (ret) + return dev_err_probe(dev, ret, "Cannot register device\n"); + + drm_fbdev_generic_setup(drm, 0); + + return 0; +} + +static int imx_lcdc_remove(struct platform_device *pdev) +{ + struct drm_device *drm = platform_get_drvdata(pdev); + + drm_dev_unregister(drm); + drm_atomic_helper_shutdown(drm); + + return 0; +} + +static void imx_lcdc_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + +static struct platform_driver imx_lcdc_driver = { + .driver = { + .name = "imx-lcdc", + .of_match_table = imx_lcdc_of_dev_id, + }, + .probe = imx_lcdc_probe, + .remove = imx_lcdc_remove, + .shutdown = imx_lcdc_shutdown, +}; +module_platform_driver(imx_lcdc_driver); + +MODULE_AUTHOR("Marian Cichy <M.Cichy@pengutronix.de>"); +MODULE_DESCRIPTION("Freescale i.MX LCDC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/kmb/kmb_drv.c b/drivers/gpu/drm/kmb/kmb_drv.c index d29c678f6c91..24035b53441c 100644 --- a/drivers/gpu/drm/kmb/kmb_drv.c +++ b/drivers/gpu/drm/kmb/kmb_drv.c @@ -15,7 +15,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_module.h> @@ -562,7 +562,7 @@ static int kmb_probe(struct platform_device *pdev) if (ret) goto err_register; - drm_fbdev_generic_setup(&kmb->drm, 0); + drm_fbdev_dma_setup(&kmb->drm, 0); return 0; diff --git a/drivers/gpu/drm/logicvc/logicvc_drm.c b/drivers/gpu/drm/logicvc/logicvc_drm.c index 2fb23697740a..c35c453fd025 100644 --- a/drivers/gpu/drm/logicvc/logicvc_drm.c +++ b/drivers/gpu/drm/logicvc/logicvc_drm.c @@ -17,7 +17,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_print.h> @@ -449,7 +449,7 @@ static int logicvc_drm_probe(struct platform_device *pdev) preferred_bpp = 32; break; } - drm_fbdev_generic_setup(drm_dev, preferred_bpp); + drm_fbdev_dma_setup(drm_dev, preferred_bpp); return 0; diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index 4aedb050d2a5..a8cd86c06c14 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -69,7 +69,7 @@ #include <drm/drm_bridge.h> #include <drm/drm_drv.h> #include <drm/drm_fb_dma_helper.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -94,7 +94,7 @@ #define MCDE_PID_MAJOR_VERSION_MASK 0xFF000000 static const struct drm_mode_config_funcs mcde_mode_config_funcs = { - .fb_create = drm_gem_fb_create_with_dirty, + .fb_create = drm_gem_fb_create, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -237,7 +237,7 @@ static int mcde_drm_bind(struct device *dev) if (ret < 0) goto unbind; - drm_fbdev_generic_setup(drm, 32); + drm_fbdev_dma_setup(drm, 32); return 0; diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 79bfe3938d3c..6608a251106b 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -18,7 +18,7 @@ #include <drm/drm_aperture.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_modeset_helper_vtables.h> @@ -353,7 +353,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) if (ret) goto uninstall_irq; - drm_fbdev_generic_setup(drm, 32); + drm_fbdev_dma_setup(drm, 32); return 0; diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c index cc2ceb301b96..6fb5b469ee5a 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_drv.c +++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c @@ -16,7 +16,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_mode_config.h> @@ -220,7 +220,7 @@ static int lcdif_probe(struct platform_device *pdev) if (ret) goto err_unload; - drm_fbdev_generic_setup(drm, 32); + drm_fbdev_dma_setup(drm, 32); return 0; diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index b3ab86ad1b36..368b1fbd8305 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -20,7 +20,7 @@ #include <drm/drm_bridge.h> #include <drm/drm_connector.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -365,7 +365,7 @@ static int mxsfb_probe(struct platform_device *pdev) if (ret) goto err_unload; - drm_fbdev_generic_setup(drm, 32); + drm_fbdev_dma_setup(drm, 32); return 0; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c index 5bb65258c36d..6c94451d0faa 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c @@ -447,7 +447,7 @@ gf100_fifo_nonstall_allow(struct nvkm_event *event, int type, int index) spin_unlock_irqrestore(&fifo->lock, flags); } -void +static void gf100_fifo_nonstall_block(struct nvkm_event *event, int type, int index) { struct nvkm_fifo *fifo = container_of(event, typeof(*fifo), nonstall.event); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.c index b5836cbc29aa..93d628d7d508 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/runl.c @@ -30,7 +30,7 @@ #include <subdev/timer.h> #include <subdev/top.h> -struct nvkm_cgrp * +static struct nvkm_cgrp * nvkm_engn_cgrp_get(struct nvkm_engn *engn, unsigned long *pirqflags) { struct nvkm_cgrp *cgrp = NULL; diff --git a/drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c b/drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c index 83a9c48bc58c..7ac90c495737 100644 --- a/drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c +++ b/drivers/gpu/drm/nouveau/nvkm/nvfw/acr.c @@ -45,7 +45,7 @@ wpr_header_v1_dump(struct nvkm_subdev *subdev, const struct wpr_header_v1 *hdr) nvkm_debug(subdev, "\tstatus : %d\n", hdr->status); } -void +static void wpr_generic_header_dump(struct nvkm_subdev *subdev, const struct wpr_generic_header *hdr) { nvkm_debug(subdev, "wprGenericHeader\n"); diff --git a/drivers/gpu/drm/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig index 455e1a91f0e5..76ded1568bd0 100644 --- a/drivers/gpu/drm/omapdrm/Kconfig +++ b/drivers/gpu/drm/omapdrm/Kconfig @@ -2,7 +2,7 @@ config DRM_OMAP tristate "OMAP DRM" depends on DRM && OF - depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM + depends on ARCH_OMAP2PLUS select DRM_KMS_HELPER select VIDEOMODE_HELPERS select HDMI diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 29cf5fa39ff2..2b9d6db7860b 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -388,6 +388,16 @@ config DRM_PANEL_NOVATEK_NT35950 Sharp panels used in Sony Xperia Z5 Premium and XZ Premium mobile phones. +config DRM_PANEL_NOVATEK_NT36523 + tristate "Novatek NT36523 panel driver" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for the panels built + around the Novatek NT36523 display controller, such as some + Boe panels used in Xiaomi Mi Pad 5 and 5 Pro tablets. + config DRM_PANEL_NOVATEK_NT36672A tristate "Novatek NT36672A DSI panel" depends on OF @@ -696,6 +706,16 @@ config DRM_PANEL_SONY_ACX565AKM Say Y here if you want to enable support for the Sony ACX565AKM 800x600 3.5" panel (found on the Nokia N900). +config DRM_PANEL_SONY_TD4353_JDI + tristate "Sony TD4353 JDI panel" + depends on GPIOLIB && OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for the Sony Tama + TD4353 JDI command mode panel as found on some Sony Xperia + XZ2 and XZ2 Compact smartphones. + config DRM_PANEL_SONY_TULIP_TRULY_NT35521 tristate "Sony Tulip Truly NT35521 panel" depends on GPIOLIB && OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index b3e8ba29edd3..ff169781e82d 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3052C) += panel-newvision-nv3052c.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35510) += panel-novatek-nt35510.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35560) += panel-novatek-nt35560.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT35950) += panel-novatek-nt35950.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36523) += panel-novatek-nt36523.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o @@ -70,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o +obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index c924f1124ebc..783234ae0f57 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -1131,6 +1131,103 @@ static const struct panel_init_cmd auo_b101uan08_3_init_cmd[] = { {}, }; +static const struct panel_init_cmd starry_qfh032011_53g_init_cmd[] = { + _INIT_DCS_CMD(0xB0, 0x01), + _INIT_DCS_CMD(0xC3, 0x4F), + _INIT_DCS_CMD(0xC4, 0x40), + _INIT_DCS_CMD(0xC5, 0x40), + _INIT_DCS_CMD(0xC6, 0x40), + _INIT_DCS_CMD(0xC7, 0x40), + _INIT_DCS_CMD(0xC8, 0x4D), + _INIT_DCS_CMD(0xC9, 0x52), + _INIT_DCS_CMD(0xCA, 0x51), + _INIT_DCS_CMD(0xCD, 0x5D), + _INIT_DCS_CMD(0xCE, 0x5B), + _INIT_DCS_CMD(0xCF, 0x4B), + _INIT_DCS_CMD(0xD0, 0x49), + _INIT_DCS_CMD(0xD1, 0x47), + _INIT_DCS_CMD(0xD2, 0x45), + _INIT_DCS_CMD(0xD3, 0x41), + _INIT_DCS_CMD(0xD7, 0x50), + _INIT_DCS_CMD(0xD8, 0x40), + _INIT_DCS_CMD(0xD9, 0x40), + _INIT_DCS_CMD(0xDA, 0x40), + _INIT_DCS_CMD(0xDB, 0x40), + _INIT_DCS_CMD(0xDC, 0x4E), + _INIT_DCS_CMD(0xDD, 0x52), + _INIT_DCS_CMD(0xDE, 0x51), + _INIT_DCS_CMD(0xE1, 0x5E), + _INIT_DCS_CMD(0xE2, 0x5C), + _INIT_DCS_CMD(0xE3, 0x4C), + _INIT_DCS_CMD(0xE4, 0x4A), + _INIT_DCS_CMD(0xE5, 0x48), + _INIT_DCS_CMD(0xE6, 0x46), + _INIT_DCS_CMD(0xE7, 0x42), + _INIT_DCS_CMD(0xB0, 0x03), + _INIT_DCS_CMD(0xBE, 0x03), + _INIT_DCS_CMD(0xCC, 0x44), + _INIT_DCS_CMD(0xC8, 0x07), + _INIT_DCS_CMD(0xC9, 0x05), + _INIT_DCS_CMD(0xCA, 0x42), + _INIT_DCS_CMD(0xCD, 0x3E), + _INIT_DCS_CMD(0xCF, 0x60), + _INIT_DCS_CMD(0xD2, 0x04), + _INIT_DCS_CMD(0xD3, 0x04), + _INIT_DCS_CMD(0xD4, 0x01), + _INIT_DCS_CMD(0xD5, 0x00), + _INIT_DCS_CMD(0xD6, 0x03), + _INIT_DCS_CMD(0xD7, 0x04), + _INIT_DCS_CMD(0xD9, 0x01), + _INIT_DCS_CMD(0xDB, 0x01), + _INIT_DCS_CMD(0xE4, 0xF0), + _INIT_DCS_CMD(0xE5, 0x0A), + _INIT_DCS_CMD(0xB0, 0x00), + _INIT_DCS_CMD(0xCC, 0x08), + _INIT_DCS_CMD(0xC2, 0x08), + _INIT_DCS_CMD(0xC4, 0x10), + _INIT_DCS_CMD(0xB0, 0x02), + _INIT_DCS_CMD(0xC0, 0x00), + _INIT_DCS_CMD(0xC1, 0x0A), + _INIT_DCS_CMD(0xC2, 0x20), + _INIT_DCS_CMD(0xC3, 0x24), + _INIT_DCS_CMD(0xC4, 0x23), + _INIT_DCS_CMD(0xC5, 0x29), + _INIT_DCS_CMD(0xC6, 0x23), + _INIT_DCS_CMD(0xC7, 0x1C), + _INIT_DCS_CMD(0xC8, 0x19), + _INIT_DCS_CMD(0xC9, 0x17), + _INIT_DCS_CMD(0xCA, 0x17), + _INIT_DCS_CMD(0xCB, 0x18), + _INIT_DCS_CMD(0xCC, 0x1A), + _INIT_DCS_CMD(0xCD, 0x1E), + _INIT_DCS_CMD(0xCE, 0x20), + _INIT_DCS_CMD(0xCF, 0x23), + _INIT_DCS_CMD(0xD0, 0x07), + _INIT_DCS_CMD(0xD1, 0x00), + _INIT_DCS_CMD(0xD2, 0x00), + _INIT_DCS_CMD(0xD3, 0x0A), + _INIT_DCS_CMD(0xD4, 0x13), + _INIT_DCS_CMD(0xD5, 0x1C), + _INIT_DCS_CMD(0xD6, 0x1A), + _INIT_DCS_CMD(0xD7, 0x13), + _INIT_DCS_CMD(0xD8, 0x17), + _INIT_DCS_CMD(0xD9, 0x1C), + _INIT_DCS_CMD(0xDA, 0x19), + _INIT_DCS_CMD(0xDB, 0x17), + _INIT_DCS_CMD(0xDC, 0x17), + _INIT_DCS_CMD(0xDD, 0x18), + _INIT_DCS_CMD(0xDE, 0x1A), + _INIT_DCS_CMD(0xDF, 0x1E), + _INIT_DCS_CMD(0xE0, 0x20), + _INIT_DCS_CMD(0xE1, 0x23), + _INIT_DCS_CMD(0xE2, 0x07), + _INIT_DCS_CMD(0X11), + _INIT_DELAY_CMD(120), + _INIT_DCS_CMD(0X29), + _INIT_DELAY_CMD(80), + {}, +}; + static inline struct boe_panel *to_boe_panel(struct drm_panel *panel) { return container_of(panel, struct boe_panel, base); @@ -1497,6 +1594,32 @@ static const struct panel_desc boe_tv105wum_nw0_desc = { .init_cmds = boe_init_cmd, }; +static const struct drm_display_mode starry_qfh032011_53g_default_mode = { + .clock = 165731, + .hdisplay = 1200, + .hsync_start = 1200 + 100, + .hsync_end = 1200 + 100 + 10, + .htotal = 1200 + 100 + 10 + 100, + .vdisplay = 1920, + .vsync_start = 1920 + 14, + .vsync_end = 1920 + 14 + 10, + .vtotal = 1920 + 14 + 10 + 15, +}; + +static const struct panel_desc starry_qfh032011_53g_desc = { + .modes = &starry_qfh032011_53g_default_mode, + .bpc = 8, + .size = { + .width_mm = 135, + .height_mm = 216, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, + .init_cmds = starry_qfh032011_53g_init_cmd, +}; + static int boe_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector) { @@ -1667,6 +1790,9 @@ static const struct of_device_id boe_of_match[] = { { .compatible = "innolux,hj110iz-01a", .data = &inx_hj110iz_desc }, + { .compatible = "starry,2081101qfh032011-53g", + .data = &starry_qfh032011_53g_desc + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, boe_of_match); diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36523.c b/drivers/gpu/drm/panel/panel-novatek-nt36523.c new file mode 100644 index 000000000000..d30dbbfb67b1 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-novatek-nt36523.c @@ -0,0 +1,777 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Novatek NT36523 DriverIC panels driver + * + * Copyright (c) 2022, 2023 Jianhua Lu <lujianhua000@gmail.com> + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +#define DSI_NUM_MIN 1 + +#define mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, cmd, seq...) \ + do { \ + mipi_dsi_dcs_write_seq(dsi0, cmd, seq); \ + mipi_dsi_dcs_write_seq(dsi1, cmd, seq); \ + } while (0) + +struct panel_info { + struct drm_panel panel; + struct mipi_dsi_device *dsi[2]; + const struct panel_desc *desc; + + struct gpio_desc *reset_gpio; + struct backlight_device *backlight; + struct regulator *vddio; + + bool prepared; +}; + +struct panel_desc { + unsigned int width_mm; + unsigned int height_mm; + + unsigned int bpc; + unsigned int lanes; + unsigned long mode_flags; + enum mipi_dsi_pixel_format format; + + const struct drm_display_mode *modes; + unsigned int num_modes; + const struct mipi_dsi_device_info dsi_info; + int (*init_sequence)(struct panel_info *pinfo); + + bool is_dual_dsi; +}; + +static inline struct panel_info *to_panel_info(struct drm_panel *panel) +{ + return container_of(panel, struct panel_info, panel); +} + +static int elish_boe_init_sequence(struct panel_info *pinfo) +{ + struct mipi_dsi_device *dsi0 = pinfo->dsi[0]; + struct mipi_dsi_device *dsi1 = pinfo->dsi[1]; + /* No datasheet, so write magic init sequence directly */ + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x05); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x18, 0x40); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x02); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x23); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x80); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0x84); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x05, 0x2d); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x06, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x07, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x08, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x09, 0x45); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x11, 0x02); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x12, 0x80); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x15, 0x83); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x16, 0x0c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x29, 0x0a); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x30, 0xff); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x31, 0xfe); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x32, 0xfd); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x33, 0xfb); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x34, 0xf8); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x35, 0xf5); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x36, 0xf3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x37, 0xf2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x38, 0xf2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x39, 0xf2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3a, 0xef); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3b, 0xec); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3d, 0xe9); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3f, 0xe5); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x40, 0xe5); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x41, 0xe5); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2a, 0x13); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x45, 0xff); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x46, 0xf4); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x47, 0xe7); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x48, 0xda); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x49, 0xcd); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4a, 0xc0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4b, 0xb3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4c, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4d, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4e, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4f, 0x99); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x50, 0x80); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x68); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x52, 0x66); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x53, 0x66); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x54, 0x66); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2b, 0x0e); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x58, 0xff); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x59, 0xfb); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5a, 0xf7); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5b, 0xf3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5c, 0xef); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5d, 0xe3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5e, 0xda); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5f, 0xd8); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x60, 0xd8); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x61, 0xd8); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x62, 0xcb); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x63, 0xbf); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x64, 0xb3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x65, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x66, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x67, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x2a); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x25, 0x47); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x30, 0x47); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x39, 0x47); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x26); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x19, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1a, 0xe0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1b, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1c, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2a, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2b, 0xe0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xf0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x84, 0x08); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x85, 0x0c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x91, 0x1f); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x92, 0x0f); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x93, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x94, 0x18); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x95, 0x03); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x96, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb0, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x19, 0x1f); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1b, 0x1b); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x24); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb8, 0x28); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x27); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd0, 0x31); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd1, 0x20); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd2, 0x30); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd4, 0x08); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xde, 0x80); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xdf, 0x02); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x26); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x81); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0xb0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x22); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9f, 0x50); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x6f, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x70, 0x11); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x73, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x74, 0x49); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x76, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x77, 0x49); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa0, 0x3f); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa9, 0x50); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xaa, 0x28); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xab, 0x28); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xad, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb8, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x49); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xba, 0x49); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbb, 0x49); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbe, 0x04); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbf, 0x49); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc0, 0x04); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc1, 0x59); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc2, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc5, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc6, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc7, 0x48); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xca, 0x43); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xcb, 0x3c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xce, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xcf, 0x43); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd0, 0x3c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd3, 0x43); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd4, 0x3c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd7, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xdc, 0x43); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xdd, 0x3c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xe1, 0x43); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xe2, 0x3c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xf2, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xf3, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xf4, 0x48); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x13, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x14, 0x23); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbc, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbd, 0x23); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x2a); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x97, 0x3c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x98, 0x02); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x99, 0x95); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9a, 0x03); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9b, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9c, 0x0b); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9d, 0x0a); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9e, 0x90); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x22); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9f, 0x50); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x23); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa3, 0x50); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xe0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x14, 0x60); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x16, 0xc0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4f, 0x02); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xf0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3a, 0x08); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xd0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x02, 0xaf); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x09, 0xee); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1c, 0x99); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1d, 0x09); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x0f, 0xff); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x53, 0x2c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x35, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbb, 0x13); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3b, 0x03, 0xac, 0x1a, 0x04, 0x04); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x11); + msleep(70); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x29); + + return 0; +} + +static int elish_csot_init_sequence(struct panel_info *pinfo) +{ + struct mipi_dsi_device *dsi0 = pinfo->dsi[0]; + struct mipi_dsi_device *dsi1 = pinfo->dsi[1]; + /* No datasheet, so write magic init sequence directly */ + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x05); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x18, 0x40); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x02); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xd0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x02, 0xaf); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x30); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x09, 0xee); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1c, 0x99); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1d, 0x09); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xf0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3a, 0x08); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xe0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4f, 0x02); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x58, 0x40); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x35, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x23); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x80); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0x84); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x05, 0x2d); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x06, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x07, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x08, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x09, 0x45); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x11, 0x02); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x12, 0x80); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x15, 0x83); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x16, 0x0c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x29, 0x0a); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x30, 0xff); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x31, 0xfe); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x32, 0xfd); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x33, 0xfb); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x34, 0xf8); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x35, 0xf5); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x36, 0xf3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x37, 0xf2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x38, 0xf2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x39, 0xf2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3a, 0xef); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3b, 0xec); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3d, 0xe9); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3f, 0xe5); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x40, 0xe5); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x41, 0xe5); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2a, 0x13); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x45, 0xff); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x46, 0xf4); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x47, 0xe7); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x48, 0xda); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x49, 0xcd); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4a, 0xc0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4b, 0xb3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4c, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4d, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4e, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x4f, 0x99); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x50, 0x80); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x68); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x52, 0x66); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x53, 0x66); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x54, 0x66); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2b, 0x0e); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x58, 0xff); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x59, 0xfb); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5a, 0xf7); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5b, 0xf3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5c, 0xef); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5d, 0xe3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5e, 0xda); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x5f, 0xd8); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x60, 0xd8); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x61, 0xd8); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x62, 0xcb); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x63, 0xbf); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x64, 0xb3); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x65, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x66, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x67, 0xb2); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x0f, 0xff); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x53, 0x2c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x55, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbb, 0x13); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x3b, 0x03, 0xac, 0x1a, 0x04, 0x04); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x2a); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x25, 0x46); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x30, 0x46); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x39, 0x46); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x26); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0xb0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x19, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1a, 0xe0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1b, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1c, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2a, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x2b, 0xe0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0xf0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x84, 0x08); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x85, 0x0c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x20); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x51, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x91, 0x1f); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x92, 0x0f); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x93, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x94, 0x18); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x95, 0x03); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x96, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb0, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x19, 0x1f); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x1b, 0x1b); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x24); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb8, 0x28); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x27); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd0, 0x31); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd1, 0x20); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd4, 0x08); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xde, 0x80); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xdf, 0x02); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x26); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x00, 0x81); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x01, 0xb0); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x22); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x6f, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x70, 0x11); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x73, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x74, 0x4d); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa0, 0x3f); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xa9, 0x50); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xaa, 0x28); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xab, 0x28); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xad, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb8, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xb9, 0x4b); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xba, 0x96); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbb, 0x4b); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbe, 0x07); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbf, 0x4b); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc0, 0x07); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc1, 0x5c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc2, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc5, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc6, 0x3f); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xc7, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xca, 0x08); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xcb, 0x40); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xce, 0x00); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xcf, 0x08); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd0, 0x40); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd3, 0x08); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xd4, 0x40); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x25); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbc, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xbd, 0x1c); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x2a); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xfb, 0x01); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x9a, 0x03); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0xff, 0x10); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x11); + msleep(70); + mipi_dsi_dual_dcs_write_seq(dsi0, dsi1, 0x29); + + return 0; +} + +static const struct drm_display_mode elish_boe_modes[] = { + { + /* There is only one 120 Hz timing, but it doesn't work perfectly, 104 Hz preferred */ + .clock = (1600 + 60 + 8 + 60) * (2560 + 26 + 4 + 168) * 104 / 1000, + .hdisplay = 1600, + .hsync_start = 1600 + 60, + .hsync_end = 1600 + 60 + 8, + .htotal = 1600 + 60 + 8 + 60, + .vdisplay = 2560, + .vsync_start = 2560 + 26, + .vsync_end = 2560 + 26 + 4, + .vtotal = 2560 + 26 + 4 + 168, + }, +}; + +static const struct drm_display_mode elish_csot_modes[] = { + { + /* There is only one 120 Hz timing, but it doesn't work perfectly, 104 Hz preferred */ + .clock = (1600 + 200 + 40 + 52) * (2560 + 26 + 4 + 168) * 104 / 1000, + .hdisplay = 1600, + .hsync_start = 1600 + 200, + .hsync_end = 1600 + 200 + 40, + .htotal = 1600 + 200 + 40 + 52, + .vdisplay = 2560, + .vsync_start = 2560 + 26, + .vsync_end = 2560 + 26 + 4, + .vtotal = 2560 + 26 + 4 + 168, + }, +}; + +static const struct panel_desc elish_boe_desc = { + .modes = elish_boe_modes, + .num_modes = ARRAY_SIZE(elish_boe_modes), + .dsi_info = { + .type = "BOE-elish", + .channel = 0, + .node = NULL, + }, + .width_mm = 127, + .height_mm = 203, + .bpc = 8, + .lanes = 3, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM, + .init_sequence = elish_boe_init_sequence, + .is_dual_dsi = true, +}; + +static const struct panel_desc elish_csot_desc = { + .modes = elish_csot_modes, + .num_modes = ARRAY_SIZE(elish_csot_modes), + .dsi_info = { + .type = "CSOT-elish", + .channel = 0, + .node = NULL, + }, + .width_mm = 127, + .height_mm = 203, + .bpc = 8, + .lanes = 3, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM, + .init_sequence = elish_csot_init_sequence, + .is_dual_dsi = true, +}; + +static void nt36523_reset(struct panel_info *pinfo) +{ + gpiod_set_value_cansleep(pinfo->reset_gpio, 1); + usleep_range(12000, 13000); + gpiod_set_value_cansleep(pinfo->reset_gpio, 0); + usleep_range(12000, 13000); + gpiod_set_value_cansleep(pinfo->reset_gpio, 1); + usleep_range(12000, 13000); + gpiod_set_value_cansleep(pinfo->reset_gpio, 0); + usleep_range(12000, 13000); +} + +static int nt36523_prepare(struct drm_panel *panel) +{ + struct panel_info *pinfo = to_panel_info(panel); + int ret; + + if (pinfo->prepared) + return 0; + + ret = regulator_enable(pinfo->vddio); + if (ret) { + dev_err(panel->dev, "failed to enable vddio regulator: %d\n", ret); + return ret; + } + + nt36523_reset(pinfo); + + ret = pinfo->desc->init_sequence(pinfo); + if (ret < 0) { + regulator_disable(pinfo->vddio); + dev_err(panel->dev, "failed to initialize panel: %d\n", ret); + return ret; + } + + pinfo->prepared = true; + + return 0; +} + +static int nt36523_disable(struct drm_panel *panel) +{ + struct panel_info *pinfo = to_panel_info(panel); + int i, ret; + + for (i = 0; i < DSI_NUM_MIN + pinfo->desc->is_dual_dsi; i++) { + ret = mipi_dsi_dcs_set_display_off(pinfo->dsi[i]); + if (ret < 0) + dev_err(&pinfo->dsi[i]->dev, "failed to set display off: %d\n", ret); + } + + for (i = 0; i < DSI_NUM_MIN + pinfo->desc->is_dual_dsi; i++) { + ret = mipi_dsi_dcs_enter_sleep_mode(pinfo->dsi[i]); + if (ret < 0) + dev_err(&pinfo->dsi[i]->dev, "failed to enter sleep mode: %d\n", ret); + } + + msleep(70); + + return 0; +} + +static int nt36523_unprepare(struct drm_panel *panel) +{ + struct panel_info *pinfo = to_panel_info(panel); + + if (!pinfo->prepared) + return 0; + + gpiod_set_value_cansleep(pinfo->reset_gpio, 1); + regulator_disable(pinfo->vddio); + + pinfo->prepared = false; + + return 0; +} + +static void nt36523_remove(struct mipi_dsi_device *dsi) +{ + struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(pinfo->dsi[0]); + if (ret < 0) + dev_err(&dsi->dev, "failed to detach from DSI0 host: %d\n", ret); + + if (pinfo->desc->is_dual_dsi) { + ret = mipi_dsi_detach(pinfo->dsi[1]); + if (ret < 0) + dev_err(&pinfo->dsi[1]->dev, "failed to detach from DSI1 host: %d\n", ret); + mipi_dsi_device_unregister(pinfo->dsi[1]); + } + + drm_panel_remove(&pinfo->panel); +} + +static int nt36523_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct panel_info *pinfo = to_panel_info(panel); + int i; + + for (i = 0; i < pinfo->desc->num_modes; i++) { + const struct drm_display_mode *m = &pinfo->desc->modes[i]; + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, m); + if (!mode) { + dev_err(panel->dev, "failed to add mode %ux%u@%u\n", + m->hdisplay, m->vdisplay, drm_mode_vrefresh(m)); + return -ENOMEM; + } + + mode->type = DRM_MODE_TYPE_DRIVER; + if (i == 0) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + } + + connector->display_info.width_mm = pinfo->desc->width_mm; + connector->display_info.height_mm = pinfo->desc->height_mm; + connector->display_info.bpc = pinfo->desc->bpc; + + return pinfo->desc->num_modes; +} + +static const struct drm_panel_funcs nt36523_panel_funcs = { + .disable = nt36523_disable, + .prepare = nt36523_prepare, + .unprepare = nt36523_unprepare, + .get_modes = nt36523_get_modes, +}; + +static int nt36523_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct device_node *dsi1; + struct mipi_dsi_host *dsi1_host; + struct panel_info *pinfo; + const struct mipi_dsi_device_info *info; + int i, ret; + + pinfo = devm_kzalloc(dev, sizeof(*pinfo), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + pinfo->vddio = devm_regulator_get(dev, "vddio"); + if (IS_ERR(pinfo->vddio)) + return dev_err_probe(dev, PTR_ERR(pinfo->vddio), "failed to get vddio regulator\n"); + + pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(pinfo->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(pinfo->reset_gpio), "failed to get reset gpio\n"); + + pinfo->desc = of_device_get_match_data(dev); + if (!pinfo->desc) + return -ENODEV; + + /* If the panel is dual dsi, register DSI1 */ + if (pinfo->desc->is_dual_dsi) { + info = &pinfo->desc->dsi_info; + + dsi1 = of_graph_get_remote_node(dsi->dev.of_node, 1, -1); + if (!dsi1) { + dev_err(dev, "cannot get secondary DSI node.\n"); + return -ENODEV; + } + + dsi1_host = of_find_mipi_dsi_host_by_node(dsi1); + of_node_put(dsi1); + if (!dsi1_host) + return dev_err_probe(dev, -EPROBE_DEFER, "cannot get secondary DSI host\n"); + + pinfo->dsi[1] = mipi_dsi_device_register_full(dsi1_host, info); + if (!pinfo->dsi[1]) { + dev_err(dev, "cannot get secondary DSI device\n"); + return -ENODEV; + } + } + + pinfo->dsi[0] = dsi; + mipi_dsi_set_drvdata(dsi, pinfo); + drm_panel_init(&pinfo->panel, dev, &nt36523_panel_funcs, DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&pinfo->panel); + if (ret) + return dev_err_probe(dev, ret, "failed to get backlight\n"); + + drm_panel_add(&pinfo->panel); + + for (i = 0; i < DSI_NUM_MIN + pinfo->desc->is_dual_dsi; i++) { + pinfo->dsi[i]->lanes = pinfo->desc->lanes; + pinfo->dsi[i]->format = pinfo->desc->format; + pinfo->dsi[i]->mode_flags = pinfo->desc->mode_flags; + + ret = mipi_dsi_attach(pinfo->dsi[i]); + if (ret < 0) + return dev_err_probe(dev, ret, "cannot attach to DSI%d host.\n", i); + } + + return 0; +} + +static const struct of_device_id nt36523_of_match[] = { + { + .compatible = "xiaomi,elish-boe-nt36523", + .data = &elish_boe_desc, + }, + { + .compatible = "xiaomi,elish-csot-nt36523", + .data = &elish_csot_desc, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, nt36523_of_match); + +static struct mipi_dsi_driver nt36523_driver = { + .probe = nt36523_probe, + .remove = nt36523_remove, + .driver = { + .name = "panel-novatek-nt36523", + .of_match_table = nt36523_of_match, + }, +}; +module_mipi_dsi_driver(nt36523_driver); + +MODULE_AUTHOR("Jianhua Lu <lujianhua000@gmail.com>"); +MODULE_DESCRIPTION("DRM driver for Novatek NT36523 based MIPI DSI panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c index 76160e5d43bd..c250ca36a5b3 100644 --- a/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c +++ b/drivers/gpu/drm/panel/panel-seiko-43wvf1g.c @@ -7,6 +7,7 @@ */ #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/media-bus-format.h> #include <linux/module.h> #include <linux/of.h> @@ -48,6 +49,7 @@ struct seiko_panel { const struct seiko_panel_desc *desc; struct regulator *dvdd; struct regulator *avdd; + struct gpio_desc *enable_gpio; }; static inline struct seiko_panel *to_seiko_panel(struct drm_panel *panel) @@ -139,6 +141,8 @@ static int seiko_panel_unprepare(struct drm_panel *panel) if (!p->prepared) return 0; + gpiod_set_value_cansleep(p->enable_gpio, 0); + regulator_disable(p->avdd); /* Add a 100ms delay as per the panel datasheet */ @@ -174,6 +178,8 @@ static int seiko_panel_prepare(struct drm_panel *panel) goto disable_dvdd; } + gpiod_set_value_cansleep(p->enable_gpio, 1); + p->prepared = true; return 0; @@ -252,6 +258,12 @@ static int seiko_panel_probe(struct device *dev, if (IS_ERR(panel->avdd)) return PTR_ERR(panel->avdd); + panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(panel->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(panel->enable_gpio), + "failed to request GPIO\n"); + drm_panel_init(&panel->base, dev, &seiko_panel_funcs, DRM_MODE_CONNECTOR_DPI); diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 0b8cf65172ff..7eae83aa0ea1 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -135,6 +135,7 @@ struct st7701 { struct regulator_bulk_data supplies[2]; struct gpio_desc *reset; unsigned int sleep_delay; + enum drm_panel_orientation orientation; }; static inline struct st7701 *panel_to_st7701(struct drm_panel *panel) @@ -397,6 +398,31 @@ static void dmt028vghmcmi_1a_gip_sequence(struct st7701 *st7701) ST7701_DSI(st7701, 0x3A, 0x70); } +static void kd50t048a_gip_sequence(struct st7701 *st7701) +{ + /** + * ST7701_SPEC_V1.2 is unable to provide enough information above this + * specific command sequence, so grab the same from vendor BSP driver. + */ + ST7701_DSI(st7701, 0xE0, 0x00, 0x00, 0x02); + ST7701_DSI(st7701, 0xE1, 0x08, 0x00, 0x0A, 0x00, 0x07, 0x00, 0x09, + 0x00, 0x00, 0x33, 0x33); + ST7701_DSI(st7701, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + ST7701_DSI(st7701, 0xE3, 0x00, 0x00, 0x33, 0x33); + ST7701_DSI(st7701, 0xE4, 0x44, 0x44); + ST7701_DSI(st7701, 0xE5, 0x0E, 0x60, 0xA0, 0xA0, 0x10, 0x60, 0xA0, + 0xA0, 0x0A, 0x60, 0xA0, 0xA0, 0x0C, 0x60, 0xA0, 0xA0); + ST7701_DSI(st7701, 0xE6, 0x00, 0x00, 0x33, 0x33); + ST7701_DSI(st7701, 0xE7, 0x44, 0x44); + ST7701_DSI(st7701, 0xE8, 0x0D, 0x60, 0xA0, 0xA0, 0x0F, 0x60, 0xA0, + 0xA0, 0x09, 0x60, 0xA0, 0xA0, 0x0B, 0x60, 0xA0, 0xA0); + ST7701_DSI(st7701, 0xEB, 0x02, 0x01, 0xE4, 0xE4, 0x44, 0x00, 0x40); + ST7701_DSI(st7701, 0xEC, 0x02, 0x01); + ST7701_DSI(st7701, 0xED, 0xAB, 0x89, 0x76, 0x54, 0x01, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x10, 0x45, 0x67, 0x98, 0xBA); +} + static int st7701_prepare(struct drm_panel *panel) { struct st7701 *st7701 = panel_to_st7701(panel); @@ -489,15 +515,29 @@ static int st7701_get_modes(struct drm_panel *panel, connector->display_info.width_mm = desc_mode->width_mm; connector->display_info.height_mm = desc_mode->height_mm; + /* + * TODO: Remove once all drm drivers call + * drm_connector_set_orientation_from_panel() + */ + drm_connector_set_panel_orientation(connector, st7701->orientation); + return 1; } +static enum drm_panel_orientation st7701_get_orientation(struct drm_panel *panel) +{ + struct st7701 *st7701 = panel_to_st7701(panel); + + return st7701->orientation; +} + static const struct drm_panel_funcs st7701_funcs = { .disable = st7701_disable, .unprepare = st7701_unprepare, .prepare = st7701_prepare, .enable = st7701_enable, .get_modes = st7701_get_modes, + .get_orientation = st7701_get_orientation, }; static const struct drm_display_mode ts8550b_mode = { @@ -700,6 +740,105 @@ static const struct st7701_panel_desc dmt028vghmcmi_1a_desc = { .gip_sequence = dmt028vghmcmi_1a_gip_sequence, }; +static const struct drm_display_mode kd50t048a_mode = { + .clock = 27500, + + .hdisplay = 480, + .hsync_start = 480 + 2, + .hsync_end = 480 + 2 + 10, + .htotal = 480 + 2 + 10 + 2, + + .vdisplay = 854, + .vsync_start = 854 + 2, + .vsync_end = 854 + 2 + 2, + .vtotal = 854 + 2 + 2 + 17, + + .width_mm = 69, + .height_mm = 139, + + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +static const struct st7701_panel_desc kd50t048a_desc = { + .mode = &kd50t048a_mode, + .lanes = 2, + .format = MIPI_DSI_FMT_RGB888, + .panel_sleep_delay = 0, + + .pv_gamma = { + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC0_MASK, 0), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC4_MASK, 0xd), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC8_MASK, 0x14), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC16_MASK, 0xd), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC24_MASK, 0x10), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC52_MASK, 0x5), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC80_MASK, 0x2), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC108_MASK, 0x8), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC147_MASK, 0x8), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC175_MASK, 0x1e), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC203_MASK, 0x5), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC231_MASK, 0x13), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC239_MASK, 0x11), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 2) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC247_MASK, 0x23), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC251_MASK, 0x29), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC255_MASK, 0x18) + }, + .nv_gamma = { + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC0_MASK, 0), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC4_MASK, 0xc), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC8_MASK, 0x14), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC16_MASK, 0xc), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC24_MASK, 0x10), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC52_MASK, 0x5), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC80_MASK, 0x3), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC108_MASK, 0x8), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC147_MASK, 0x7), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC175_MASK, 0x20), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC203_MASK, 0x5), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC231_MASK, 0x13), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC239_MASK, 0x11), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 2) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC247_MASK, 0x24), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC251_MASK, 0x29), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC255_MASK, 0x18) + }, + .nlinv = 1, + .vop_uv = 4887500, + .vcom_uv = 937500, + .vgh_mv = 15000, + .vgl_mv = -9510, + .avdd_mv = 6600, + .avcl_mv = -4400, + .gamma_op_bias = OP_BIAS_MIDDLE, + .input_op_bias = OP_BIAS_MIN, + .output_op_bias = OP_BIAS_MIN, + .t2d_ns = 1600, + .t3d_ns = 10400, + .eot_en = true, + .gip_sequence = kd50t048a_gip_sequence, +}; + static int st7701_dsi_probe(struct mipi_dsi_device *dsi) { const struct st7701_panel_desc *desc; @@ -730,6 +869,10 @@ static int st7701_dsi_probe(struct mipi_dsi_device *dsi) return PTR_ERR(st7701->reset); } + ret = of_drm_get_panel_orientation(dsi->dev.of_node, &st7701->orientation); + if (ret < 0) + return dev_err_probe(&dsi->dev, ret, "Failed to get orientation\n"); + drm_panel_init(&st7701->panel, &dsi->dev, &st7701_funcs, DRM_MODE_CONNECTOR_DSI); @@ -775,6 +918,7 @@ static void st7701_dsi_remove(struct mipi_dsi_device *dsi) static const struct of_device_id st7701_of_match[] = { { .compatible = "densitron,dmt028vghmcmi-1a", .data = &dmt028vghmcmi_1a_desc }, + { .compatible = "elida,kd50t048a", .data = &kd50t048a_desc }, { .compatible = "techstar,ts8550b", .data = &ts8550b_desc }, { } }; diff --git a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c new file mode 100644 index 000000000000..8d8813dbaa45 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Konrad Dybcio <konrad.dybcio@somainline.org> + * + * Generated with linux-mdss-dsi-panel-driver-generator with a + * substantial amount of manual adjustments. + * + * SONY Downstream kernel calls this one: + * - "JDI ID3" for Akari (XZ2) + * - "JDI ID4" for Apollo (XZ2 Compact) + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +enum { + TYPE_TAMA_60HZ, + /* + * Leaving room for expansion - SONY very often uses + * *truly reliably* overclockable panels on their flagships! + */ +}; + +struct sony_td4353_jdi { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data supplies[3]; + struct gpio_desc *panel_reset_gpio; + struct gpio_desc *touch_reset_gpio; + bool prepared; + int type; +}; + +static inline struct sony_td4353_jdi *to_sony_td4353_jdi(struct drm_panel *panel) +{ + return container_of(panel, struct sony_td4353_jdi, panel); +} + +static int sony_td4353_jdi_on(struct sony_td4353_jdi *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_column_address(dsi, 0x0000, 1080 - 1); + if (ret < 0) { + dev_err(dev, "Failed to set column address: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 2160 - 1); + if (ret < 0) { + dev_err(dev, "Failed to set page address: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_tear_scanline(dsi, 0); + if (ret < 0) { + dev_err(dev, "Failed to set tear scanline: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (ret < 0) { + dev_err(dev, "Failed to set tear on: %d\n", ret); + return ret; + } + + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_ADDRESS_MODE, 0x00); + + ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77); + if (ret < 0) { + dev_err(dev, "Failed to set pixel format: %d\n", ret); + return ret; + } + + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_SET_PARTIAL_ROWS, + 0x00, 0x00, 0x08, 0x6f); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to exit sleep mode: %d\n", ret); + return ret; + } + msleep(70); + + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_MEMORY_START); + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + dev_err(dev, "Failed to turn display on: %d\n", ret); + return ret; + } + + return 0; +} + +static int sony_td4353_jdi_off(struct sony_td4353_jdi *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set display off: %d\n", ret); + return ret; + } + msleep(22); + + ret = mipi_dsi_dcs_set_tear_off(dsi); + if (ret < 0) { + dev_err(dev, "Failed to set tear off: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "Failed to enter sleep mode: %d\n", ret); + return ret; + } + msleep(80); + + return 0; +} + +static void sony_td4353_assert_reset_gpios(struct sony_td4353_jdi *ctx, int mode) +{ + gpiod_set_value_cansleep(ctx->touch_reset_gpio, mode); + gpiod_set_value_cansleep(ctx->panel_reset_gpio, mode); + usleep_range(5000, 5100); +} + +static int sony_td4353_jdi_prepare(struct drm_panel *panel) +{ + struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (ctx->prepared) + return 0; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + msleep(100); + + sony_td4353_assert_reset_gpios(ctx, 1); + + ret = sony_td4353_jdi_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to power on panel: %d\n", ret); + sony_td4353_assert_reset_gpios(ctx, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + return ret; + } + + ctx->prepared = true; + return 0; +} + +static int sony_td4353_jdi_unprepare(struct drm_panel *panel) +{ + struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + if (!ctx->prepared) + return 0; + + ret = sony_td4353_jdi_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to power off panel: %d\n", ret); + + sony_td4353_assert_reset_gpios(ctx, 0); + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + + ctx->prepared = false; + return 0; +} + +static const struct drm_display_mode sony_td4353_jdi_mode_tama_60hz = { + .clock = (1080 + 4 + 8 + 8) * (2160 + 259 + 8 + 8) * 60 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 4, + .hsync_end = 1080 + 4 + 8, + .htotal = 1080 + 4 + 8 + 8, + .vdisplay = 2160, + .vsync_start = 2160 + 259, + .vsync_end = 2160 + 259 + 8, + .vtotal = 2160 + 259 + 8 + 8, + .width_mm = 64, + .height_mm = 128, +}; + +static int sony_td4353_jdi_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct sony_td4353_jdi *ctx = to_sony_td4353_jdi(panel); + struct drm_display_mode *mode = NULL; + + if (ctx->type == TYPE_TAMA_60HZ) + mode = drm_mode_duplicate(connector->dev, &sony_td4353_jdi_mode_tama_60hz); + else + return -EINVAL; + + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs sony_td4353_jdi_panel_funcs = { + .prepare = sony_td4353_jdi_prepare, + .unprepare = sony_td4353_jdi_unprepare, + .get_modes = sony_td4353_jdi_get_modes, +}; + +static int sony_td4353_jdi_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct sony_td4353_jdi *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->type = (uintptr_t)of_device_get_match_data(dev); + + ctx->supplies[0].supply = "vddio"; + ctx->supplies[1].supply = "vsp"; + ctx->supplies[2].supply = "vsn"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ctx->panel_reset_gpio = devm_gpiod_get(dev, "panel-reset", GPIOD_ASIS); + if (IS_ERR(ctx->panel_reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->panel_reset_gpio), + "Failed to get panel-reset-gpios\n"); + + ctx->touch_reset_gpio = devm_gpiod_get(dev, "touch-reset", GPIOD_ASIS); + if (IS_ERR(ctx->touch_reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->touch_reset_gpio), + "Failed to get touch-reset-gpios\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS; + + drm_panel_init(&ctx->panel, dev, &sony_td4353_jdi_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&ctx->panel); + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(dev, "Failed to attach to DSI host: %d\n", ret); + drm_panel_remove(&ctx->panel); + return ret; + } + + return 0; +} + +static void sony_td4353_jdi_remove(struct mipi_dsi_device *dsi) +{ + struct sony_td4353_jdi *ctx = mipi_dsi_get_drvdata(dsi); + int ret; + + ret = mipi_dsi_detach(dsi); + if (ret < 0) + dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); + + drm_panel_remove(&ctx->panel); +} + +static const struct of_device_id sony_td4353_jdi_of_match[] = { + { .compatible = "sony,td4353-jdi-tama", .data = (void *)TYPE_TAMA_60HZ }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sony_td4353_jdi_of_match); + +static struct mipi_dsi_driver sony_td4353_jdi_driver = { + .probe = sony_td4353_jdi_probe, + .remove = sony_td4353_jdi_remove, + .driver = { + .name = "panel-sony-td4353-jdi", + .of_match_table = sony_td4353_jdi_of_match, + }, +}; +module_mipi_dsi_driver(sony_td4353_jdi_driver); + +MODULE_AUTHOR("Konrad Dybcio <konrad.dybcio@somainline.org>"); +MODULE_DESCRIPTION("DRM panel driver for SONY Xperia XZ2/XZ2c JDI panel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index 00deba0b7271..4b2a9e9753f6 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -48,7 +48,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -308,7 +308,7 @@ static int pl111_amba_probe(struct amba_device *amba_dev, if (ret < 0) goto dev_put; - drm_fbdev_generic_setup(drm, priv->variant->fb_bpp); + drm_fbdev_dma_setup(drm, priv->variant->fb_bpp); return 0; diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 7901c3babc8c..917e79951aac 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -359,11 +359,6 @@ static inline void dsi_write(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 val) writel(val, dsi->base + reg); } -static inline u32 dsi_read(struct dw_mipi_dsi_rockchip *dsi, u32 reg) -{ - return readl(dsi->base + reg); -} - static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi, u8 test_code, u8 test_data) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 2f4b8f64cbad..293dcf0f0593 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -74,6 +74,7 @@ struct rockchip_hdmi { struct regmap *regmap; struct rockchip_encoder encoder; const struct rockchip_hdmi_chip_data *chip_data; + const struct dw_hdmi_plat_data *plat_data; struct clk *ref_clk; struct clk *grf_clk; struct dw_hdmi *hdmi; @@ -161,6 +162,12 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { { 0x4064, 0x0003} }, }, { + 340000000, { + { 0x0040, 0x0003 }, + { 0x3b4c, 0x0003 }, + { 0x5a64, 0x0003 }, + }, + }, { ~0UL, { { 0x00a0, 0x000a }, { 0x2001, 0x000f }, @@ -186,6 +193,8 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = { }, { 148500000, { 0x0000, 0x0038, 0x0038 }, }, { + 600000000, { 0x0000, 0x0000, 0x0000 }, + }, { ~0UL, { 0x0000, 0x0000, 0x0000}, } }; @@ -241,23 +250,39 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) } static enum drm_mode_status -dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data, +dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, const struct drm_display_info *info, const struct drm_display_mode *mode) { + struct rockchip_hdmi *hdmi = data; const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; int pclk = mode->clock * 1000; - bool valid = false; + bool exact_match = hdmi->plat_data->phy_force_vendor; int i; + if (hdmi->ref_clk) { + int rpclk = clk_round_rate(hdmi->ref_clk, pclk); + + if (abs(rpclk - pclk) > pclk / 1000) + return MODE_NOCLOCK; + } + for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) { - if (pclk == mpll_cfg[i].mpixelclock) { - valid = true; - break; - } + /* + * For vendor specific phys force an exact match of the pixelclock + * to preserve the original behaviour of the driver. + */ + if (exact_match && pclk == mpll_cfg[i].mpixelclock) + return MODE_OK; + /* + * The Synopsys phy can work with pixelclocks up to the value given + * in the corresponding mpll_cfg entry. + */ + if (!exact_match && pclk <= mpll_cfg[i].mpixelclock) + return MODE_OK; } - return (valid) ? MODE_OK : MODE_BAD; + return MODE_BAD; } static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) @@ -546,8 +571,10 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, return -ENOMEM; hdmi->dev = &pdev->dev; + hdmi->plat_data = plat_data; hdmi->chip_data = plat_data->phy_data; plat_data->phy_data = hdmi; + plat_data->priv_data = hdmi; encoder = &hdmi->encoder.encoder; encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index abbc189affa7..d8f5e064a1ba 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -1171,6 +1171,17 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc) spin_unlock_irqrestore(&vop->irq_lock, flags); } +static enum drm_mode_status vop_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct vop *vop = to_vop(crtc); + + if (vop->data->max_output.width && mode->hdisplay > vop->data->max_output.width) + return MODE_BAD_HVALUE; + + return MODE_OK; +} + static bool vop_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -1582,6 +1593,7 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, } static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { + .mode_valid = vop_crtc_mode_valid, .mode_fixup = vop_crtc_mode_fixup, .atomic_check = vop_crtc_atomic_check, .atomic_begin = vop_crtc_atomic_begin, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 8502849833d9..5f56e0597df8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -42,6 +42,11 @@ enum vop_data_format { VOP_FMT_YUV444SP, }; +struct vop_rect { + int width; + int height; +}; + struct vop_reg { uint32_t mask; uint16_t offset; @@ -225,6 +230,7 @@ struct vop_data { const struct vop_win_data *win; unsigned int win_size; unsigned int lut_size; + struct vop_rect max_output; #define VOP_FEATURE_OUTPUT_RGB10 BIT(0) #define VOP_FEATURE_INTERNAL_RGB BIT(1) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 0e0012368976..03ca32cd2050 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -1438,6 +1438,8 @@ static void rk3568_set_intf_mux(struct vop2_video_port *vp, int id, die &= ~RK3568_SYS_DSP_INFACE_EN_RGB_MUX; die |= RK3568_SYS_DSP_INFACE_EN_RGB | FIELD_PREP(RK3568_SYS_DSP_INFACE_EN_RGB_MUX, vp->id); + dip &= ~RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL; + dip |= FIELD_PREP(RK3568_DSP_IF_POL__RGB_LVDS_PIN_POL, polflags); if (polflags & POLFLAG_DCLK_INV) regmap_write(vop2->grf, RK3568_GRF_VO_CON1, BIT(3 + 16) | BIT(3)); else @@ -2655,7 +2657,7 @@ static int vop2_bind(struct device *dev, struct device *master, void *data) return -ENODEV; /* Allocate vop2 struct and its vop2_win array */ - alloc_size = sizeof(*vop2) + sizeof(*vop2->win) * vop2_data->win_size; + alloc_size = struct_size(vop2, win, vop2_data->win_size); vop2 = devm_kzalloc(dev, alloc_size, GFP_KERNEL); if (!vop2) return -ENOMEM; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h index c727093a06d6..f1234a151130 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h @@ -27,11 +27,6 @@ enum win_dly_mode { VOP2_DLY_MODE_MAX, }; -struct vop_rect { - int width; - int height; -}; - enum vop2_scale_up_mode { VOP2_SCALE_UP_NRST_NBOR, VOP2_SCALE_UP_BIL, diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 014f99e8928e..20ac7811c5eb 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -181,6 +181,7 @@ static const struct vop_data rk3036_vop = { .output = &rk3036_output, .win = rk3036_vop_win_data, .win_size = ARRAY_SIZE(rk3036_vop_win_data), + .max_output = { 1920, 1080 }, }; static const struct vop_win_phy rk3126_win1_data = { @@ -213,6 +214,7 @@ static const struct vop_data rk3126_vop = { .output = &rk3036_output, .win = rk3126_vop_win_data, .win_size = ARRAY_SIZE(rk3126_vop_win_data), + .max_output = { 1920, 1080 }, }; static const int px30_vop_intrs[] = { @@ -340,6 +342,7 @@ static const struct vop_data px30_vop_big = { .output = &px30_output, .win = px30_vop_big_win_data, .win_size = ARRAY_SIZE(px30_vop_big_win_data), + .max_output = { 1920, 1080 }, }; static const struct vop_win_data px30_vop_lit_win_data[] = { @@ -356,6 +359,7 @@ static const struct vop_data px30_vop_lit = { .output = &px30_output, .win = px30_vop_lit_win_data, .win_size = ARRAY_SIZE(px30_vop_lit_win_data), + .max_output = { 1920, 1080 }, }; static const struct vop_scl_regs rk3066_win_scl = { @@ -479,6 +483,7 @@ static const struct vop_data rk3066_vop = { .output = &rk3066_output, .win = rk3066_vop_win_data, .win_size = ARRAY_SIZE(rk3066_vop_win_data), + .max_output = { 1920, 1080 }, }; static const struct vop_scl_regs rk3188_win_scl = { @@ -585,6 +590,7 @@ static const struct vop_data rk3188_vop = { .win = rk3188_vop_win_data, .win_size = ARRAY_SIZE(rk3188_vop_win_data), .feature = VOP_FEATURE_INTERNAL_RGB, + .max_output = { 2048, 1536 }, }; static const struct vop_scl_extension rk3288_win_full_scl_ext = { @@ -732,6 +738,12 @@ static const struct vop_data rk3288_vop = { .win = rk3288_vop_win_data, .win_size = ARRAY_SIZE(rk3288_vop_win_data), .lut_size = 1024, + /* + * This is the maximum resolution for the VOPB, the VOPL can only do + * 2560x1600, but we can't distinguish them as they have the same + * compatible. + */ + .max_output = { 3840, 2160 }, }; static const int rk3368_vop_intrs[] = { @@ -833,6 +845,7 @@ static const struct vop_data rk3368_vop = { .misc = &rk3368_misc, .win = rk3368_vop_win_data, .win_size = ARRAY_SIZE(rk3368_vop_win_data), + .max_output = { 4096, 2160 }, }; static const struct vop_intr rk3366_vop_intr = { @@ -854,6 +867,7 @@ static const struct vop_data rk3366_vop = { .misc = &rk3368_misc, .win = rk3368_vop_win_data, .win_size = ARRAY_SIZE(rk3368_vop_win_data), + .max_output = { 4096, 2160 }, }; static const struct vop_output rk3399_output = { @@ -984,6 +998,7 @@ static const struct vop_data rk3399_vop_big = { .win_size = ARRAY_SIZE(rk3399_vop_win_data), .win_yuv2yuv = rk3399_vop_big_win_yuv2yuv_data, .lut_size = 1024, + .max_output = { 4096, 2160 }, }; static const struct vop_win_data rk3399_vop_lit_win_data[] = { @@ -1010,6 +1025,7 @@ static const struct vop_data rk3399_vop_lit = { .win_size = ARRAY_SIZE(rk3399_vop_lit_win_data), .win_yuv2yuv = rk3399_vop_lit_win_yuv2yuv_data, .lut_size = 256, + .max_output = { 2560, 1600 }, }; static const struct vop_win_data rk3228_vop_win_data[] = { @@ -1029,6 +1045,7 @@ static const struct vop_data rk3228_vop = { .misc = &rk3368_misc, .win = rk3228_vop_win_data, .win_size = ARRAY_SIZE(rk3228_vop_win_data), + .max_output = { 4096, 2160 }, }; static const struct vop_modeset rk3328_modeset = { @@ -1100,6 +1117,7 @@ static const struct vop_data rk3328_vop = { .misc = &rk3328_misc, .win = rk3328_vop_win_data, .win_size = ARRAY_SIZE(rk3328_vop_win_data), + .max_output = { 4096, 2160 }, }; static const struct of_device_id vop_driver_dt_match[] = { diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig index f2a880c48485..3c7a5feff8de 100644 --- a/drivers/gpu/drm/sti/Kconfig +++ b/drivers/gpu/drm/sti/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config DRM_STI tristate "DRM Support for STMicroelectronics SoC stiH4xx Series" - depends on OF && DRM && (ARCH_STI || ARCH_MULTIPLATFORM) + depends on OF && DRM && ARCH_STI select RESET_CONTROLLER select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index ef6a4e63198f..1b87b5899f9e 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -14,7 +14,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_debugfs.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_of.h> @@ -199,7 +199,7 @@ static int sti_bind(struct device *dev) drm_mode_config_reset(ddev); - drm_fbdev_generic_setup(ddev, 32); + drm_fbdev_dma_setup(ddev, 32); return 0; diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig index ded72f879482..fa49cde43bb2 100644 --- a/drivers/gpu/drm/stm/Kconfig +++ b/drivers/gpu/drm/stm/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config DRM_STM tristate "DRM Support for STMicroelectronics SoC Series" - depends on DRM && (ARCH_STM32 || ARCH_MULTIPLATFORM) + depends on DRM && ARCH_STM32 select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER select DRM_PANEL_BRIDGE diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index 50410bd99dfe..422220df7d8c 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -18,7 +18,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_module.h> @@ -203,7 +203,7 @@ static int stm_drm_platform_probe(struct platform_device *pdev) if (ret) goto err_put; - drm_fbdev_generic_setup(ddev, 16); + drm_fbdev_dma_setup(ddev, 16); return 0; diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index cc94efbbf2d4..dd283a3a4e36 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -17,7 +17,7 @@ #include <drm/drm_aperture.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_module.h> #include <drm/drm_of.h> @@ -111,7 +111,7 @@ static int sun4i_drv_bind(struct device *dev) if (ret) goto finish_poll; - drm_fbdev_generic_setup(drm, 32); + drm_fbdev_dma_setup(drm, 32); dev_set_drvdata(dev, drm); diff --git a/drivers/gpu/drm/tests/drm_format_helper_test.c b/drivers/gpu/drm/tests/drm_format_helper_test.c index 9536829c6e3a..84b5cc29c8fc 100644 --- a/drivers/gpu/drm/tests/drm_format_helper_test.c +++ b/drivers/gpu/drm/tests/drm_format_helper_test.c @@ -409,12 +409,15 @@ static size_t conversion_buf_size(u32 dst_format, unsigned int dst_pitch, const struct drm_rect *clip) { const struct drm_format_info *dst_fi = drm_format_info(dst_format); + unsigned int bpp; if (!dst_fi) return -EINVAL; - if (!dst_pitch) - dst_pitch = drm_rect_width(clip) * dst_fi->cpp[0]; + if (!dst_pitch) { + bpp = drm_format_info_bpp(dst_fi, 0); + dst_pitch = DIV_ROUND_UP(drm_rect_width(clip) * bpp, 8); + } return dst_pitch * drm_rect_height(clip); } diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index e98b4150f556..4df47071dc88 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -19,14 +19,8 @@ static int fake_probe(struct platform_device *pdev) return 0; } -static int fake_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver fake_platform_driver = { .probe = fake_probe, - .remove = fake_remove, .driver = { .name = KUNIT_DEVICE_NAME, }, diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index 2dac8727d2f4..3f5f27fb6ebc 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -13,7 +13,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_managed.h> #include <drm/drm_module.h> @@ -179,7 +179,7 @@ static int tidss_probe(struct platform_device *pdev) goto err_irq_uninstall; } - drm_fbdev_generic_setup(ddev, 32); + drm_fbdev_dma_setup(ddev, 32); dev_dbg(dev, "%s done\n", __func__); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 4ca426007dc8..fe56beea3e93 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -16,7 +16,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_debugfs.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -384,7 +384,7 @@ static int tilcdc_init(const struct drm_driver *ddrv, struct device *dev) goto init_failed; priv->is_registered = true; - drm_fbdev_generic_setup(ddev, bpp); + drm_fbdev_dma_setup(ddev, bpp); return 0; init_failed: diff --git a/drivers/gpu/drm/tiny/arcpgu.c b/drivers/gpu/drm/tiny/arcpgu.c index 611bbee15071..e5b10e41554a 100644 --- a/drivers/gpu/drm/tiny/arcpgu.c +++ b/drivers/gpu/drm/tiny/arcpgu.c @@ -12,7 +12,7 @@ #include <drm/drm_drv.h> #include <drm/drm_edid.h> #include <drm/drm_fb_dma_helper.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_dma_helper.h> @@ -394,7 +394,7 @@ static int arcpgu_probe(struct platform_device *pdev) if (ret) goto err_unload; - drm_fbdev_generic_setup(&arcpgu->drm, 16); + drm_fbdev_dma_setup(&arcpgu->drm, 16); return 0; diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index accfa52e78c5..594bc472862f 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -24,6 +24,7 @@ #include <video/vga.h> #include <drm/drm_aperture.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_state_helper.h> #include <drm/drm_connector.h> @@ -43,7 +44,6 @@ #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_module.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #define DRIVER_NAME "cirrus" #define DRIVER_DESC "qemu cirrus vga" @@ -56,16 +56,34 @@ struct cirrus_device { struct drm_device dev; - struct drm_simple_display_pipe pipe; - struct drm_connector conn; - unsigned int cpp; - unsigned int pitch; + + /* modesetting pipeline */ + struct drm_plane primary_plane; + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; + + /* HW resources */ void __iomem *vram; void __iomem *mmio; }; #define to_cirrus(_dev) container_of(_dev, struct cirrus_device, dev) +struct cirrus_primary_plane_state { + struct drm_shadow_plane_state base; + + /* HW scanout buffer */ + const struct drm_format_info *format; + unsigned int pitch; +}; + +static inline struct cirrus_primary_plane_state * +to_cirrus_primary_plane_state(struct drm_plane_state *plane_state) +{ + return container_of(plane_state, struct cirrus_primary_plane_state, base.base); +}; + /* ------------------------------------------------------------------ */ /* * The meat of this driver. The core passes us a mode and we have to program @@ -126,46 +144,42 @@ static void wreg_hdr(struct cirrus_device *cirrus, u8 val) iowrite8(val, cirrus->mmio + VGA_DAC_MASK); } -static int cirrus_convert_to(struct drm_framebuffer *fb) +static const struct drm_format_info *cirrus_convert_to(struct drm_framebuffer *fb) { - if (fb->format->cpp[0] == 4 && fb->pitches[0] > CIRRUS_MAX_PITCH) { + if (fb->format->format == DRM_FORMAT_XRGB8888 && fb->pitches[0] > CIRRUS_MAX_PITCH) { if (fb->width * 3 <= CIRRUS_MAX_PITCH) /* convert from XR24 to RG24 */ - return 3; + return drm_format_info(DRM_FORMAT_RGB888); else /* convert from XR24 to RG16 */ - return 2; + return drm_format_info(DRM_FORMAT_RGB565); } - return 0; + return NULL; } -static int cirrus_cpp(struct drm_framebuffer *fb) +static const struct drm_format_info *cirrus_format(struct drm_framebuffer *fb) { - int convert_cpp = cirrus_convert_to(fb); + const struct drm_format_info *format = cirrus_convert_to(fb); - if (convert_cpp) - return convert_cpp; - return fb->format->cpp[0]; + if (format) + return format; + return fb->format; } static int cirrus_pitch(struct drm_framebuffer *fb) { - int convert_cpp = cirrus_convert_to(fb); + const struct drm_format_info *format = cirrus_convert_to(fb); - if (convert_cpp) - return convert_cpp * fb->width; + if (format) + return drm_format_info_min_pitch(format, 0, fb->width); return fb->pitches[0]; } static void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset) { - int idx; u32 addr; u8 tmp; - if (!drm_dev_enter(&cirrus->dev, &idx)) - return; - addr = offset >> 2; wreg_crt(cirrus, 0x0c, (u8)((addr >> 8) & 0xff)); wreg_crt(cirrus, 0x0d, (u8)(addr & 0xff)); @@ -180,21 +194,14 @@ static void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset) tmp &= 0x7f; tmp |= (addr >> 12) & 0x80; wreg_crt(cirrus, 0x1d, tmp); - - drm_dev_exit(idx); } -static int cirrus_mode_set(struct cirrus_device *cirrus, - struct drm_display_mode *mode, - struct drm_framebuffer *fb) +static void cirrus_mode_set(struct cirrus_device *cirrus, + struct drm_display_mode *mode) { int hsyncstart, hsyncend, htotal, hdispend; int vtotal, vdispend; - int tmp, idx; - int sr07 = 0, hdr = 0; - - if (!drm_dev_enter(&cirrus->dev, &idx)) - return -1; + int tmp; htotal = mode->htotal / 8; hsyncend = mode->hsync_end / 8; @@ -258,46 +265,39 @@ static int cirrus_mode_set(struct cirrus_device *cirrus, /* Disable Hercules/CGA compatibility */ wreg_crt(cirrus, VGA_CRTC_MODE, 0x03); +} + +static void cirrus_format_set(struct cirrus_device *cirrus, + const struct drm_format_info *format) +{ + u8 sr07, hdr; sr07 = rreg_seq(cirrus, 0x07); sr07 &= 0xe0; - hdr = 0; - cirrus->cpp = cirrus_cpp(fb); - switch (cirrus->cpp * 8) { - case 8: + switch (format->format) { + case DRM_FORMAT_C8: sr07 |= 0x11; + hdr = 0x00; break; - case 16: + case DRM_FORMAT_RGB565: sr07 |= 0x17; hdr = 0xc1; break; - case 24: + case DRM_FORMAT_RGB888: sr07 |= 0x15; hdr = 0xc5; break; - case 32: + case DRM_FORMAT_XRGB8888: sr07 |= 0x19; hdr = 0xc5; break; default: - drm_dev_exit(idx); - return -1; + return; } wreg_seq(cirrus, 0x7, sr07); - /* Program the pitch */ - cirrus->pitch = cirrus_pitch(fb); - tmp = cirrus->pitch / 8; - wreg_crt(cirrus, VGA_CRTC_OFFSET, tmp); - - /* Enable extended blanking and pitch bits, and enable full memory */ - tmp = 0x22; - tmp |= (cirrus->pitch >> 7) & 0x10; - tmp |= (cirrus->pitch >> 6) & 0x40; - wreg_crt(cirrus, 0x1b, tmp); - /* Enable high-colour modes */ wreg_gfx(cirrus, VGA_GFX_MODE, 0x40); @@ -305,208 +305,323 @@ static int cirrus_mode_set(struct cirrus_device *cirrus, wreg_gfx(cirrus, VGA_GFX_MISC, 0x01); wreg_hdr(cirrus, hdr); +} - cirrus_set_start_address(cirrus, 0); +static void cirrus_pitch_set(struct cirrus_device *cirrus, unsigned int pitch) +{ + u8 cr13, cr1b; - /* Unblank (needed on S3 resume, vgabios doesn't do it then) */ - outb(0x20, 0x3c0); + /* Program the pitch */ + cr13 = pitch / 8; + wreg_crt(cirrus, VGA_CRTC_OFFSET, cr13); - drm_dev_exit(idx); - return 0; + /* Enable extended blanking and pitch bits, and enable full memory */ + cr1b = 0x22; + cr1b |= (pitch >> 7) & 0x10; + cr1b |= (pitch >> 6) & 0x40; + wreg_crt(cirrus, 0x1b, cr1b); + + cirrus_set_start_address(cirrus, 0); } -static int cirrus_fb_blit_rect(struct drm_framebuffer *fb, - const struct iosys_map *vmap, - struct drm_rect *rect) -{ - struct cirrus_device *cirrus = to_cirrus(fb->dev); - struct iosys_map dst; - int idx; +/* ------------------------------------------------------------------ */ +/* cirrus display pipe */ - if (!drm_dev_enter(&cirrus->dev, &idx)) - return -ENODEV; +static const uint32_t cirrus_primary_plane_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, +}; - iosys_map_set_vaddr_iomem(&dst, cirrus->vram); +static const uint64_t cirrus_primary_plane_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static int cirrus_primary_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); + struct cirrus_primary_plane_state *new_primary_plane_state = + to_cirrus_primary_plane_state(new_plane_state); + struct drm_framebuffer *fb = new_plane_state->fb; + struct drm_crtc *new_crtc = new_plane_state->crtc; + struct drm_crtc_state *new_crtc_state = NULL; + int ret; + unsigned int pitch; - if (cirrus->cpp == fb->format->cpp[0]) { - iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, rect)); - drm_fb_memcpy(&dst, fb->pitches, vmap, fb, rect); + if (new_crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); - } else if (fb->format->cpp[0] == 4 && cirrus->cpp == 2) { - iosys_map_incr(&dst, drm_fb_clip_offset(cirrus->pitch, fb->format, rect)); - drm_fb_xrgb8888_to_rgb565(&dst, &cirrus->pitch, vmap, fb, rect, false); + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + else if (!new_plane_state->visible) + return 0; - } else if (fb->format->cpp[0] == 4 && cirrus->cpp == 3) { - iosys_map_incr(&dst, drm_fb_clip_offset(cirrus->pitch, fb->format, rect)); - drm_fb_xrgb8888_to_rgb888(&dst, &cirrus->pitch, vmap, fb, rect); + pitch = cirrus_pitch(fb); - } else { - WARN_ON_ONCE("cpp mismatch"); - } + /* validate size constraints */ + if (pitch > CIRRUS_MAX_PITCH) + return -EINVAL; + else if (pitch * fb->height > CIRRUS_VRAM_SIZE) + return -EINVAL; - drm_dev_exit(idx); + new_primary_plane_state->format = cirrus_format(fb); + new_primary_plane_state->pitch = pitch; return 0; } -static int cirrus_fb_blit_fullscreen(struct drm_framebuffer *fb, - const struct iosys_map *map) +static void cirrus_primary_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct drm_rect fullscreen = { - .x1 = 0, - .x2 = fb->width, - .y1 = 0, - .y2 = fb->height, - }; - return cirrus_fb_blit_rect(fb, map, &fullscreen); -} + struct cirrus_device *cirrus = to_cirrus(plane->dev); + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct cirrus_primary_plane_state *primary_plane_state = + to_cirrus_primary_plane_state(plane_state); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + const struct drm_format_info *format = primary_plane_state->format; + unsigned int pitch = primary_plane_state->pitch; + struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct cirrus_primary_plane_state *old_primary_plane_state = + to_cirrus_primary_plane_state(old_plane_state); + struct iosys_map vaddr = IOSYS_MAP_INIT_VADDR_IOMEM(cirrus->vram); + struct drm_atomic_helper_damage_iter iter; + struct drm_rect damage; + int idx; -static int cirrus_check_size(int width, int height, - struct drm_framebuffer *fb) -{ - int pitch = width * 2; + if (!fb) + return; - if (fb) - pitch = cirrus_pitch(fb); + if (!drm_dev_enter(&cirrus->dev, &idx)) + return; - if (pitch > CIRRUS_MAX_PITCH) - return -EINVAL; - if (pitch * height > CIRRUS_VRAM_SIZE) - return -EINVAL; - return 0; -} + if (old_primary_plane_state->format != format) + cirrus_format_set(cirrus, format); + if (old_primary_plane_state->pitch != pitch) + cirrus_pitch_set(cirrus, pitch); -/* ------------------------------------------------------------------ */ -/* cirrus connector */ + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + unsigned int offset = drm_fb_clip_offset(pitch, format, &damage); + struct iosys_map dst = IOSYS_MAP_INIT_OFFSET(&vaddr, offset); -static int cirrus_conn_get_modes(struct drm_connector *conn) -{ - int count; + drm_fb_blit(&dst, &pitch, format->format, shadow_plane_state->data, fb, &damage); + } - count = drm_add_modes_noedid(conn, - conn->dev->mode_config.max_width, - conn->dev->mode_config.max_height); - drm_set_preferred_mode(conn, 1024, 768); - return count; + drm_dev_exit(idx); } -static const struct drm_connector_helper_funcs cirrus_conn_helper_funcs = { - .get_modes = cirrus_conn_get_modes, -}; - -static const struct drm_connector_funcs cirrus_conn_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +static const struct drm_plane_helper_funcs cirrus_primary_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = cirrus_primary_plane_helper_atomic_check, + .atomic_update = cirrus_primary_plane_helper_atomic_update, }; -static int cirrus_conn_init(struct cirrus_device *cirrus) +static struct drm_plane_state * +cirrus_primary_plane_atomic_duplicate_state(struct drm_plane *plane) { - drm_connector_helper_add(&cirrus->conn, &cirrus_conn_helper_funcs); - return drm_connector_init(&cirrus->dev, &cirrus->conn, - &cirrus_conn_funcs, DRM_MODE_CONNECTOR_VGA); + struct drm_plane_state *plane_state = plane->state; + struct cirrus_primary_plane_state *primary_plane_state = + to_cirrus_primary_plane_state(plane_state); + struct cirrus_primary_plane_state *new_primary_plane_state; + struct drm_shadow_plane_state *new_shadow_plane_state; -} + if (!plane_state) + return NULL; -/* ------------------------------------------------------------------ */ -/* cirrus (simple) display pipe */ + new_primary_plane_state = kzalloc(sizeof(*new_primary_plane_state), GFP_KERNEL); + if (!new_primary_plane_state) + return NULL; + new_shadow_plane_state = &new_primary_plane_state->base; + + __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state); + new_primary_plane_state->format = primary_plane_state->format; + new_primary_plane_state->pitch = primary_plane_state->pitch; + + return &new_shadow_plane_state->base; +} -static enum drm_mode_status cirrus_pipe_mode_valid(struct drm_simple_display_pipe *pipe, - const struct drm_display_mode *mode) +static void cirrus_primary_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *plane_state) { - if (cirrus_check_size(mode->hdisplay, mode->vdisplay, NULL) < 0) - return MODE_BAD; - return MODE_OK; + struct cirrus_primary_plane_state *primary_plane_state = + to_cirrus_primary_plane_state(plane_state); + + __drm_gem_destroy_shadow_plane_state(&primary_plane_state->base); + kfree(primary_plane_state); } -static int cirrus_pipe_check(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state, - struct drm_crtc_state *crtc_state) +static void cirrus_reset_primary_plane(struct drm_plane *plane) { - struct drm_framebuffer *fb = plane_state->fb; + struct cirrus_primary_plane_state *primary_plane_state; - if (!fb) - return 0; - return cirrus_check_size(fb->width, fb->height, fb); + if (plane->state) { + cirrus_primary_plane_atomic_destroy_state(plane, plane->state); + plane->state = NULL; /* must be set to NULL here */ + } + + primary_plane_state = kzalloc(sizeof(*primary_plane_state), GFP_KERNEL); + if (!primary_plane_state) + return; + __drm_gem_reset_shadow_plane(plane, &primary_plane_state->base); } -static void cirrus_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state, - struct drm_plane_state *plane_state) +static const struct drm_plane_funcs cirrus_primary_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = cirrus_reset_primary_plane, + .atomic_duplicate_state = cirrus_primary_plane_atomic_duplicate_state, + .atomic_destroy_state = cirrus_primary_plane_atomic_destroy_state, +}; + +static int cirrus_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { - struct cirrus_device *cirrus = to_cirrus(pipe->crtc.dev); - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + int ret; + + if (!crtc_state->enable) + return 0; + + ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state); + if (ret) + return ret; - cirrus_mode_set(cirrus, &crtc_state->mode, plane_state->fb); - cirrus_fb_blit_fullscreen(plane_state->fb, &shadow_plane_state->data[0]); + return 0; } -static void cirrus_pipe_update(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *old_state) +static void cirrus_crtc_helper_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) { - struct cirrus_device *cirrus = to_cirrus(pipe->crtc.dev); - struct drm_plane_state *state = pipe->plane.state; - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); - struct drm_crtc *crtc = &pipe->crtc; - struct drm_rect rect; + struct cirrus_device *cirrus = to_cirrus(crtc->dev); + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + int idx; + + if (!drm_dev_enter(&cirrus->dev, &idx)) + return; - if (state->fb && cirrus->cpp != cirrus_cpp(state->fb)) - cirrus_mode_set(cirrus, &crtc->mode, state->fb); + cirrus_mode_set(cirrus, &crtc_state->mode); - if (state->fb && drm_atomic_helper_damage_merged(old_state, state, &rect)) - cirrus_fb_blit_rect(state->fb, &shadow_plane_state->data[0], &rect); + /* Unblank (needed on S3 resume, vgabios doesn't do it then) */ + outb(VGA_AR_ENABLE_DISPLAY, VGA_ATT_W); + + drm_dev_exit(idx); } -static const struct drm_simple_display_pipe_funcs cirrus_pipe_funcs = { - .mode_valid = cirrus_pipe_mode_valid, - .check = cirrus_pipe_check, - .enable = cirrus_pipe_enable, - .update = cirrus_pipe_update, - DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, +static const struct drm_crtc_helper_funcs cirrus_crtc_helper_funcs = { + .atomic_check = cirrus_crtc_helper_atomic_check, + .atomic_enable = cirrus_crtc_helper_atomic_enable, }; -static const uint32_t cirrus_formats[] = { - DRM_FORMAT_RGB565, - DRM_FORMAT_RGB888, - DRM_FORMAT_XRGB8888, +static const struct drm_crtc_funcs cirrus_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, }; -static const uint64_t cirrus_modifiers[] = { - DRM_FORMAT_MOD_LINEAR, - DRM_FORMAT_MOD_INVALID +static const struct drm_encoder_funcs cirrus_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int cirrus_connector_helper_get_modes(struct drm_connector *connector) +{ + int count; + + count = drm_add_modes_noedid(connector, + connector->dev->mode_config.max_width, + connector->dev->mode_config.max_height); + drm_set_preferred_mode(connector, 1024, 768); + return count; +} + +static const struct drm_connector_helper_funcs cirrus_connector_helper_funcs = { + .get_modes = cirrus_connector_helper_get_modes, +}; + +static const struct drm_connector_funcs cirrus_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; static int cirrus_pipe_init(struct cirrus_device *cirrus) { - return drm_simple_display_pipe_init(&cirrus->dev, - &cirrus->pipe, - &cirrus_pipe_funcs, - cirrus_formats, - ARRAY_SIZE(cirrus_formats), - cirrus_modifiers, - &cirrus->conn); + struct drm_device *dev = &cirrus->dev; + struct drm_plane *primary_plane; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + int ret; + + primary_plane = &cirrus->primary_plane; + ret = drm_universal_plane_init(dev, primary_plane, 0, + &cirrus_primary_plane_funcs, + cirrus_primary_plane_formats, + ARRAY_SIZE(cirrus_primary_plane_formats), + cirrus_primary_plane_format_modifiers, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + drm_plane_helper_add(primary_plane, &cirrus_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary_plane); + + crtc = &cirrus->crtc; + ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, + &cirrus_crtc_funcs, NULL); + if (ret) + return ret; + drm_crtc_helper_add(crtc, &cirrus_crtc_helper_funcs); + + encoder = &cirrus->encoder; + ret = drm_encoder_init(dev, encoder, &cirrus_encoder_funcs, + DRM_MODE_ENCODER_DAC, NULL); + if (ret) + return ret; + encoder->possible_crtcs = drm_crtc_mask(crtc); + + connector = &cirrus->connector; + ret = drm_connector_init(dev, connector, &cirrus_connector_funcs, + DRM_MODE_CONNECTOR_VGA); + if (ret) + return ret; + drm_connector_helper_add(connector, &cirrus_connector_helper_funcs); + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ret; + + return 0; } /* ------------------------------------------------------------------ */ /* cirrus framebuffers & mode config */ -static struct drm_framebuffer* -cirrus_fb_create(struct drm_device *dev, struct drm_file *file_priv, - const struct drm_mode_fb_cmd2 *mode_cmd) +static enum drm_mode_status cirrus_mode_config_mode_valid(struct drm_device *dev, + const struct drm_display_mode *mode) { - if (mode_cmd->pixel_format != DRM_FORMAT_RGB565 && - mode_cmd->pixel_format != DRM_FORMAT_RGB888 && - mode_cmd->pixel_format != DRM_FORMAT_XRGB8888) - return ERR_PTR(-EINVAL); - if (cirrus_check_size(mode_cmd->width, mode_cmd->height, NULL) < 0) - return ERR_PTR(-EINVAL); - return drm_gem_fb_create_with_dirty(dev, file_priv, mode_cmd); + const struct drm_format_info *format = drm_format_info(DRM_FORMAT_XRGB8888); + uint64_t pitch = drm_format_info_min_pitch(format, 0, mode->hdisplay); + + if (pitch * mode->vdisplay > CIRRUS_VRAM_SIZE) + return MODE_MEM; + + return MODE_OK; } static const struct drm_mode_config_funcs cirrus_mode_config_funcs = { - .fb_create = cirrus_fb_create, + .fb_create = drm_gem_fb_create_with_dirty, + .mode_valid = cirrus_mode_config_mode_valid, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -589,10 +704,6 @@ static int cirrus_pci_probe(struct pci_dev *pdev, if (ret) return ret; - ret = cirrus_conn_init(cirrus); - if (ret < 0) - return ret; - ret = cirrus_pipe_init(cirrus); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 882c2fa346f3..459f1b4440da 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -464,7 +464,8 @@ bounce: if (ret == -EMULTIHOP) { ret = ttm_bo_bounce_temp_buffer(bo, &evict_mem, ctx, &hop); if (ret) { - pr_err("Buffer eviction failed\n"); + if (ret != -ERESTARTSYS && ret != -EINTR) + pr_err("Buffer eviction failed\n"); ttm_resource_free(bo, &evict_mem); goto out; } diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c index c7a1862f322a..cd73631b6106 100644 --- a/drivers/gpu/drm/ttm/ttm_device.c +++ b/drivers/gpu/drm/ttm/ttm_device.c @@ -137,7 +137,6 @@ int ttm_global_swapout(struct ttm_operation_ctx *ctx, gfp_t gfp_flags) mutex_unlock(&ttm_global_mutex); return ret; } -EXPORT_SYMBOL(ttm_global_swapout); int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, gfp_t gfp_flags) diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c index 0d05c386d303..abd557332b28 100644 --- a/drivers/gpu/drm/tve200/tve200_drv.c +++ b/drivers/gpu/drm/tve200/tve200_drv.c @@ -40,7 +40,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_module.h> @@ -227,7 +227,7 @@ static int tve200_probe(struct platform_device *pdev) * Passing in 16 here will make the RGB565 mode the default * Passing in 32 will use XRGB8888 mode */ - drm_fbdev_generic_setup(drm, 16); + drm_fbdev_dma_setup(drm, 16); return 0; diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 0ccaee57fe9a..c8bf954042e0 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -33,7 +33,7 @@ #include <drm/drm_aperture.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_vblank.h> #include <soc/bcm2835/raspberrypi-firmware.h> @@ -387,7 +387,7 @@ static int vc4_drm_bind(struct device *dev) if (ret < 0) goto unbind_all; - drm_fbdev_generic_setup(drm, 16); + drm_fbdev_dma_setup(drm, 16); return 0; diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index 4c09e313bebc..a2e045f3a000 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -390,5 +390,9 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, return plane; drm_plane_helper_add(plane, funcs); + + if (type == DRM_PLANE_TYPE_PRIMARY) + drm_plane_enable_fb_damage_clips(plane); + return plane; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c index d6baf73a6458..c0da89e16e6f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c @@ -241,7 +241,7 @@ static int vmw_debugfs_gem_info_show(struct seq_file *m, void *unused) * Therefore, we need to protect this ->comm access using RCU. */ rcu_read_lock(); - task = pid_task(file->pid, PIDTYPE_PID); + task = pid_task(file->pid, PIDTYPE_TGID); seq_printf(m, "pid %8d command %s:\n", pid_nr(file->pid), task ? task->comm : "<unknown>"); rcu_read_unlock(); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index d79a6eccfaa4..ba0c0e12cfe9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -506,11 +506,11 @@ static void vmw_stdu_bo_cpu_commit(struct vmw_kms_dirty *dirty) /* Assume we are blitting from Guest (bo) to Host (display_srf) */ src_pitch = stdu->display_srf->metadata.base_size.width * stdu->cpp; src_bo = &stdu->display_srf->res.guest_memory_bo->tbo; - src_offset = ddirty->top * dst_pitch + ddirty->left * stdu->cpp; + src_offset = ddirty->top * src_pitch + ddirty->left * stdu->cpp; dst_pitch = ddirty->pitch; dst_bo = &ddirty->buf->tbo; - dst_offset = ddirty->fb_top * src_pitch + ddirty->fb_left * stdu->cpp; + dst_offset = ddirty->fb_top * dst_pitch + ddirty->fb_left * stdu->cpp; (void) vmw_bo_cpu_blit(dst_bo, dst_offset, dst_pitch, src_bo, src_offset, src_pitch, diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c index 776ef5480206..a7f8611be6f4 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_kms.c +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c @@ -19,7 +19,7 @@ #include <drm/drm_device.h> #include <drm/drm_drv.h> #include <drm/drm_encoder.h> -#include <drm/drm_fbdev_generic.h> +#include <drm/drm_fbdev_dma.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_dma_helper.h> @@ -515,7 +515,7 @@ int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub) goto err_poll_fini; /* Initialize fbdev generic emulation. */ - drm_fbdev_generic_setup(drm, 24); + drm_fbdev_dma_setup(drm, 24); return 0; diff --git a/drivers/gpu/ipu-v3/Kconfig b/drivers/gpu/ipu-v3/Kconfig index 061fb990c120..7dece2a53c5c 100644 --- a/drivers/gpu/ipu-v3/Kconfig +++ b/drivers/gpu/ipu-v3/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config IMX_IPUV3_CORE tristate "IPUv3 core support" - depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM || COMPILE_TEST + depends on SOC_IMX5 || SOC_IMX6Q || COMPILE_TEST depends on DRM || !DRM # if DRM=m, this can't be 'y' select BITREVERSE select GENERIC_ALLOCATOR if DRM |