From c51756d56e20a12476e4e192b332fac39c1550ca Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 29 Jun 2018 11:47:40 -0700 Subject: drm/sun4i: Remove VLA usage In the quest to remove all stack VLA usage from the kernel[1], this switches to using a kmalloc allocation and moves all the size calculations to the start to do an allocation. If an upper bounds on the mode timing calculations could be determined, a fixed stack size could be used instead. [1] https://lkml.kernel.org/r/CA+55aFzCG-zNmZwX4A2FQpadafLfEzK6CC=qPXydAacU1RqZWA@mail.gmail.com Signed-off-by: Kees Cook Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20180629184740.GA37415@beast --- drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 107 ++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 43 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index d4e7d16a2514..da9814f94d00 100644 --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -247,10 +248,8 @@ static u16 sun6i_dsi_crc_compute(u8 const *buffer, size_t len) return crc_ccitt(0xffff, buffer, len); } -static u16 sun6i_dsi_crc_repeat_compute(u8 pd, size_t len) +static u16 sun6i_dsi_crc_repeat(u8 pd, u8 *buffer, size_t len) { - u8 buffer[len]; - memset(buffer, pd, len); return sun6i_dsi_crc_compute(buffer, len); @@ -274,11 +273,11 @@ static u32 sun6i_dsi_build_blk0_pkt(u8 vc, u16 wc) wc & 0xff, wc >> 8); } -static u32 sun6i_dsi_build_blk1_pkt(u16 pd, size_t len) +static u32 sun6i_dsi_build_blk1_pkt(u16 pd, u8 *buffer, size_t len) { u32 val = SUN6I_DSI_BLK_PD(pd); - return val | SUN6I_DSI_BLK_PF(sun6i_dsi_crc_repeat_compute(pd, len)); + return val | SUN6I_DSI_BLK_PF(sun6i_dsi_crc_repeat(pd, buffer, len)); } static void sun6i_dsi_inst_abort(struct sun6i_dsi *dsi) @@ -452,6 +451,54 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, struct mipi_dsi_device *device = dsi->device; unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8; u16 hbp, hfp, hsa, hblk, vblk; + size_t bytes; + u8 *buffer; + + /* Do all timing calculations up front to allocate buffer space */ + + /* + * A sync period is composed of a blanking packet (4 bytes + + * payload + 2 bytes) and a sync event packet (4 bytes). Its + * minimal size is therefore 10 bytes + */ +#define HSA_PACKET_OVERHEAD 10 + hsa = max((unsigned int)HSA_PACKET_OVERHEAD, + (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD); + + /* + * The backporch is set using a blanking packet (4 bytes + + * payload + 2 bytes). Its minimal size is therefore 6 bytes + */ +#define HBP_PACKET_OVERHEAD 6 + hbp = max((unsigned int)HBP_PACKET_OVERHEAD, + (mode->hsync_start - mode->hdisplay) * Bpp - HBP_PACKET_OVERHEAD); + + /* + * The frontporch is set using a blanking packet (4 bytes + + * payload + 2 bytes). Its minimal size is therefore 6 bytes + */ +#define HFP_PACKET_OVERHEAD 6 + hfp = max((unsigned int)HFP_PACKET_OVERHEAD, + (mode->htotal - mode->hsync_end) * Bpp - HFP_PACKET_OVERHEAD); + + /* + * hblk seems to be the line + porches length. + */ + hblk = mode->htotal * Bpp - hsa; + + /* + * And I'm not entirely sure what vblk is about. The driver in + * Allwinner BSP is using a rather convoluted calculation + * there only for 4 lanes. However, using 0 (the !4 lanes + * case) even with a 4 lanes screen seems to work... + */ + vblk = 0; + + /* How many bytes do we need to send all payloads? */ + bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk); + buffer = kmalloc(bytes, GFP_KERNEL); + if (WARN_ON(!buffer)) + return; regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, 0); @@ -485,63 +532,37 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, SUN6I_DSI_BASIC_SIZE1_VACT(mode->vdisplay) | SUN6I_DSI_BASIC_SIZE1_VT(mode->vtotal)); - /* - * A sync period is composed of a blanking packet (4 bytes + - * payload + 2 bytes) and a sync event packet (4 bytes). Its - * minimal size is therefore 10 bytes - */ -#define HSA_PACKET_OVERHEAD 10 - hsa = max((unsigned int)HSA_PACKET_OVERHEAD, - (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD); + /* sync */ regmap_write(dsi->regs, SUN6I_DSI_BLK_HSA0_REG, sun6i_dsi_build_blk0_pkt(device->channel, hsa)); regmap_write(dsi->regs, SUN6I_DSI_BLK_HSA1_REG, - sun6i_dsi_build_blk1_pkt(0, hsa)); + sun6i_dsi_build_blk1_pkt(0, buffer, hsa)); - /* - * The backporch is set using a blanking packet (4 bytes + - * payload + 2 bytes). Its minimal size is therefore 6 bytes - */ -#define HBP_PACKET_OVERHEAD 6 - hbp = max((unsigned int)HBP_PACKET_OVERHEAD, - (mode->hsync_start - mode->hdisplay) * Bpp - HBP_PACKET_OVERHEAD); + /* backporch */ regmap_write(dsi->regs, SUN6I_DSI_BLK_HBP0_REG, sun6i_dsi_build_blk0_pkt(device->channel, hbp)); regmap_write(dsi->regs, SUN6I_DSI_BLK_HBP1_REG, - sun6i_dsi_build_blk1_pkt(0, hbp)); + sun6i_dsi_build_blk1_pkt(0, buffer, hbp)); - /* - * The frontporch is set using a blanking packet (4 bytes + - * payload + 2 bytes). Its minimal size is therefore 6 bytes - */ -#define HFP_PACKET_OVERHEAD 6 - hfp = max((unsigned int)HFP_PACKET_OVERHEAD, - (mode->htotal - mode->hsync_end) * Bpp - HFP_PACKET_OVERHEAD); + /* frontporch */ regmap_write(dsi->regs, SUN6I_DSI_BLK_HFP0_REG, sun6i_dsi_build_blk0_pkt(device->channel, hfp)); regmap_write(dsi->regs, SUN6I_DSI_BLK_HFP1_REG, - sun6i_dsi_build_blk1_pkt(0, hfp)); + sun6i_dsi_build_blk1_pkt(0, buffer, hfp)); - /* - * hblk seems to be the line + porches length. - */ - hblk = mode->htotal * Bpp - hsa; + /* hblk */ regmap_write(dsi->regs, SUN6I_DSI_BLK_HBLK0_REG, sun6i_dsi_build_blk0_pkt(device->channel, hblk)); regmap_write(dsi->regs, SUN6I_DSI_BLK_HBLK1_REG, - sun6i_dsi_build_blk1_pkt(0, hblk)); + sun6i_dsi_build_blk1_pkt(0, buffer, hblk)); - /* - * And I'm not entirely sure what vblk is about. The driver in - * Allwinner BSP is using a rather convoluted calculation - * there only for 4 lanes. However, using 0 (the !4 lanes - * case) even with a 4 lanes screen seems to work... - */ - vblk = 0; + /* vblk */ regmap_write(dsi->regs, SUN6I_DSI_BLK_VBLK0_REG, sun6i_dsi_build_blk0_pkt(device->channel, vblk)); regmap_write(dsi->regs, SUN6I_DSI_BLK_VBLK1_REG, - sun6i_dsi_build_blk1_pkt(0, vblk)); + sun6i_dsi_build_blk1_pkt(0, buffer, vblk)); + + kfree(buffer); } static int sun6i_dsi_start(struct sun6i_dsi *dsi, -- cgit v1.2.3-55-g7522 From 1c7c5fd916a0ff66501467f1e8e79d3ff8eca112 Mon Sep 17 00:00:00 2001 From: Haneen Mohammed Date: Mon, 14 May 2018 17:33:46 +0300 Subject: drm/vkms: Introduce basic VKMS driver This patch introduces Virtual Kernel Mode-Setting (VKMS) driver. It creates a very basic kms driver with 1 crtc/encoder/connector/plane. VKMS driver would be useful for testing, or for running X (or similar) on headless machines and be able to still use the GPU. Thus it enables a virtual display without the need for hardware display capability. Signed-off-by: Haneen Mohammed Signed-off-by: Daniel Vetter Signed-off-by: Gustavo Padovan Link: https://patchwork.freedesktop.org/patch/msgid/20180514143346.GA21695@haneen-vb --- drivers/gpu/drm/Kconfig | 6 ++ drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/vkms/Makefile | 3 + drivers/gpu/drm/vkms/vkms_drv.c | 146 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/vkms/vkms_drv.h | 13 ++++ 5 files changed, 169 insertions(+) create mode 100644 drivers/gpu/drm/vkms/Makefile create mode 100644 drivers/gpu/drm/vkms/vkms_drv.c create mode 100644 drivers/gpu/drm/vkms/vkms_drv.h (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 2a72d2feb76d..7db3d82cbb27 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -213,6 +213,12 @@ config DRM_VGEM as used by Mesa's software renderer for enhanced performance. If M is selected the module will be called vgem. +config DRM_VKMS + tristate "Virtual KMS" + depends on DRM + help + Choose this option to get a virtual kernal mode-setting driver. + If M is selected the module will be called vkms. source "drivers/gpu/drm/exynos/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 69c13517ea3a..6ae535ca0914 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_DRM_SAVAGE)+= savage/ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ obj-$(CONFIG_DRM_VIA) +=via/ obj-$(CONFIG_DRM_VGEM) += vgem/ +obj-$(CONFIG_DRM_VKMS) += vkms/ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile new file mode 100644 index 000000000000..2aef948d3a34 --- /dev/null +++ b/drivers/gpu/drm/vkms/Makefile @@ -0,0 +1,3 @@ +vkms-y := vkms_drv.o + +obj-$(CONFIG_DRM_VKMS) += vkms.o diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c new file mode 100644 index 000000000000..b1df08ed23a0 --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -0,0 +1,146 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include "vkms_drv.h" + +#define DRIVER_NAME "vkms" +#define DRIVER_DESC "Virtual Kernel Mode Setting" +#define DRIVER_DATE "20180514" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +static struct vkms_device *vkms_device; + +static const struct file_operations vkms_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .mmap = drm_gem_mmap, + .unlocked_ioctl = drm_ioctl, + .compat_ioctl = drm_compat_ioctl, + .poll = drm_poll, + .read = drm_read, + .llseek = no_llseek, + .release = drm_release, +}; + +static void vkms_release(struct drm_device *dev) +{ + struct vkms_device *vkms = container_of(dev, struct vkms_device, drm); + + platform_device_unregister(vkms->platform); + drm_mode_config_cleanup(&vkms->drm); + drm_dev_fini(&vkms->drm); +} + +struct drm_driver vkms_driver = { + .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM, + .release = vkms_release, + .fops = &vkms_driver_fops, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static const u32 vkms_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static void vkms_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs vkms_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vkms_connector_destroy, +}; + +static int __init vkms_init(void) +{ + int ret; + + vkms_device = kzalloc(sizeof(*vkms_device), GFP_KERNEL); + if (!vkms_device) + return -ENOMEM; + + ret = drm_dev_init(&vkms_device->drm, &vkms_driver, NULL); + if (ret) + goto out_free; + + vkms_device->platform = + platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + if (IS_ERR(vkms_device->platform)) { + ret = PTR_ERR(vkms_device->platform); + goto out_fini; + } + + drm_mode_config_init(&vkms_device->drm); + + ret = drm_connector_init(&vkms_device->drm, &vkms_device->connector, + &vkms_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret < 0) { + DRM_ERROR("Failed to init connector\n"); + goto out_unregister; + } + + ret = drm_simple_display_pipe_init(&vkms_device->drm, + &vkms_device->pipe, + NULL, + vkms_formats, + ARRAY_SIZE(vkms_formats), + NULL, + &vkms_device->connector); + if (ret < 0) { + DRM_ERROR("Cannot setup simple display pipe\n"); + goto out_unregister; + } + + ret = drm_dev_register(&vkms_device->drm, 0); + if (ret) + goto out_unregister; + + drm_connector_register(&vkms_device->connector); + + return 0; + +out_unregister: + platform_device_unregister(vkms_device->platform); +out_fini: + drm_dev_fini(&vkms_device->drm); +out_free: + kfree(vkms_device); + + return ret; +} + +static void __exit vkms_exit(void) +{ + if (!vkms_device) { + DRM_INFO("vkms_device is NULL.\n"); + return; + } + + drm_dev_unregister(&vkms_device->drm); + drm_dev_put(&vkms_device->drm); + + kfree(vkms_device); +} + +module_init(vkms_init); +module_exit(vkms_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h new file mode 100644 index 000000000000..c77c5bf5032a --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -0,0 +1,13 @@ +#ifndef _VKMS_DRV_H_ +#define _VKMS_DRV_H_ + +#include + +struct vkms_device { + struct drm_device drm; + struct platform_device *platform; + struct drm_simple_display_pipe pipe; + struct drm_connector connector; +}; + +#endif /* _VKMS_DRV_H_ */ -- cgit v1.2.3-55-g7522 From 41111ce17ee7277cb7451e8a21e319220afc130d Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 15 May 2018 19:30:52 +0800 Subject: drm/vkms: vkms_driver can be static Fixes: 58d8108f080c ("drm/vkms: Introduce basic VKMS driver") Signed-off-by: Fengguang Wu Acked-by: Haneen Mohammed Signed-off-by: Daniel Vetter Signed-off-by: Gustavo Padovan Link: https://patchwork.freedesktop.org/patch/msgid/20180515113052.GA111532@lkp-ib04 --- drivers/gpu/drm/vkms/vkms_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index b1df08ed23a0..35517b09538e 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -40,7 +40,7 @@ static void vkms_release(struct drm_device *dev) drm_dev_fini(&vkms->drm); } -struct drm_driver vkms_driver = { +static struct drm_driver vkms_driver = { .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM, .release = vkms_release, .fops = &vkms_driver_fops, -- cgit v1.2.3-55-g7522 From c04372ea4abd83ec6c86083f2afe9322515ee293 Mon Sep 17 00:00:00 2001 From: Rodrigo Siqueira Date: Wed, 16 May 2018 20:55:36 -0300 Subject: drm/vkms: Add mode_config initialization Initialize minimum and maximum width and height of the frame buffers with default values. Signed-off-by: Rodrigo Siqueira Reviewed-by: Haneen Mohammed Signed-off-by: Daniel Vetter Signed-off-by: Gustavo Padovan Link: https://patchwork.freedesktop.org/patch/msgid/75c55df671f24b037f9172700b479f4bb2fa7c92.1526514457.git.rodrigosiqueiramelo@gmail.com --- drivers/gpu/drm/vkms/vkms_drv.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 35517b09538e..aec3f180f96d 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "vkms_drv.h" #define DRIVER_NAME "vkms" @@ -17,6 +18,12 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 +#define XRES_MIN 32 +#define YRES_MIN 32 + +#define XRES_MAX 8192 +#define YRES_MAX 8192 + static struct vkms_device *vkms_device; static const struct file_operations vkms_driver_fops = { @@ -67,6 +74,11 @@ static const struct drm_connector_funcs vkms_connector_funcs = { .destroy = vkms_connector_destroy, }; +static const struct drm_mode_config_funcs vkms_mode_funcs = { + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + static int __init vkms_init(void) { int ret; @@ -87,6 +99,11 @@ static int __init vkms_init(void) } drm_mode_config_init(&vkms_device->drm); + vkms_device->drm.mode_config.funcs = &vkms_mode_funcs; + vkms_device->drm.mode_config.min_width = XRES_MIN; + vkms_device->drm.mode_config.min_height = YRES_MIN; + vkms_device->drm.mode_config.max_width = XRES_MAX; + vkms_device->drm.mode_config.max_height = YRES_MAX; ret = drm_connector_init(&vkms_device->drm, &vkms_device->connector, &vkms_connector_funcs, -- cgit v1.2.3-55-g7522 From 854502fa0a38dc77c9e855c95d239a8fd50a9b13 Mon Sep 17 00:00:00 2001 From: Rodrigo Siqueira Date: Wed, 16 May 2018 20:56:21 -0300 Subject: drm/vkms: Add basic CRTC initialization This commit adds the essential infrastructure for around CRTCs which is composed of: a new data struct for output data information, a function for creating planes, and a simple encoder attached to the connector. Finally, due to the introduction of a new initialization function, connectors were moved from vkms_drv.c to vkms_display.c. Signed-off-by: Rodrigo Siqueira Reviewed-by: Haneen Mohammed Signed-off-by: Daniel Vetter Signed-off-by: Gustavo Padovan Link: https://patchwork.freedesktop.org/patch/msgid/b6e27bc6a54f5cb340658fa5969f7b48fbfbf1b7.1526514457.git.rodrigosiqueiramelo@gmail.com --- drivers/gpu/drm/vkms/Makefile | 2 +- drivers/gpu/drm/vkms/vkms_crtc.c | 35 +++++++++++++++ drivers/gpu/drm/vkms/vkms_drv.c | 60 +++++++------------------ drivers/gpu/drm/vkms/vkms_drv.h | 24 ++++++++-- drivers/gpu/drm/vkms/vkms_output.c | 91 ++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/vkms/vkms_plane.c | 46 +++++++++++++++++++ 6 files changed, 211 insertions(+), 47 deletions(-) create mode 100644 drivers/gpu/drm/vkms/vkms_crtc.c create mode 100644 drivers/gpu/drm/vkms/vkms_output.c create mode 100644 drivers/gpu/drm/vkms/vkms_plane.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile index 2aef948d3a34..3f774a6a9c58 100644 --- a/drivers/gpu/drm/vkms/Makefile +++ b/drivers/gpu/drm/vkms/Makefile @@ -1,3 +1,3 @@ -vkms-y := vkms_drv.o +vkms-y := vkms_drv.o vkms_plane.o vkms_output.o vkms_crtc.o obj-$(CONFIG_DRM_VKMS) += vkms.o diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c new file mode 100644 index 000000000000..bf76cd39ece7 --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "vkms_drv.h" +#include +#include + +static const struct drm_crtc_funcs vkms_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = drm_crtc_cleanup, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *primary, struct drm_plane *cursor) +{ + int ret; + + ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor, + &vkms_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to init CRTC\n"); + return ret; + } + + return ret; +} diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index aec3f180f96d..070613e32934 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -6,7 +6,6 @@ */ #include -#include #include #include #include @@ -59,25 +58,24 @@ static struct drm_driver vkms_driver = { .minor = DRIVER_MINOR, }; -static const u32 vkms_formats[] = { - DRM_FORMAT_XRGB8888, +static const struct drm_mode_config_funcs vkms_mode_funcs = { + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, }; -static void vkms_connector_destroy(struct drm_connector *connector) +static int vkms_modeset_init(struct vkms_device *vkmsdev) { - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} + struct drm_device *dev = &vkmsdev->drm; -static const struct drm_connector_funcs vkms_connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = vkms_connector_destroy, -}; + drm_mode_config_init(dev); + dev->mode_config.funcs = &vkms_mode_funcs; + dev->mode_config.min_width = XRES_MIN; + dev->mode_config.min_height = YRES_MIN; + dev->mode_config.max_width = XRES_MAX; + dev->mode_config.max_height = YRES_MAX; -static const struct drm_mode_config_funcs vkms_mode_funcs = { - .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, -}; + return vkms_output_init(vkmsdev); +} static int __init vkms_init(void) { @@ -98,48 +96,24 @@ static int __init vkms_init(void) goto out_fini; } - drm_mode_config_init(&vkms_device->drm); - vkms_device->drm.mode_config.funcs = &vkms_mode_funcs; - vkms_device->drm.mode_config.min_width = XRES_MIN; - vkms_device->drm.mode_config.min_height = YRES_MIN; - vkms_device->drm.mode_config.max_width = XRES_MAX; - vkms_device->drm.mode_config.max_height = YRES_MAX; - - ret = drm_connector_init(&vkms_device->drm, &vkms_device->connector, - &vkms_connector_funcs, - DRM_MODE_CONNECTOR_VIRTUAL); - if (ret < 0) { - DRM_ERROR("Failed to init connector\n"); - goto out_unregister; - } - - ret = drm_simple_display_pipe_init(&vkms_device->drm, - &vkms_device->pipe, - NULL, - vkms_formats, - ARRAY_SIZE(vkms_formats), - NULL, - &vkms_device->connector); - if (ret < 0) { - DRM_ERROR("Cannot setup simple display pipe\n"); + ret = vkms_modeset_init(vkms_device); + if (ret) goto out_unregister; - } ret = drm_dev_register(&vkms_device->drm, 0); if (ret) goto out_unregister; - drm_connector_register(&vkms_device->connector); - return 0; out_unregister: platform_device_unregister(vkms_device->platform); + out_fini: drm_dev_fini(&vkms_device->drm); + out_free: kfree(vkms_device); - return ret; } diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index c77c5bf5032a..b0f9d2e61a42 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -1,13 +1,31 @@ #ifndef _VKMS_DRV_H_ #define _VKMS_DRV_H_ -#include +#include +#include +#include + +static const u32 vkms_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +struct vkms_output { + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; +}; struct vkms_device { struct drm_device drm; struct platform_device *platform; - struct drm_simple_display_pipe pipe; - struct drm_connector connector; + struct vkms_output output; }; +int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *primary, struct drm_plane *cursor); + +int vkms_output_init(struct vkms_device *vkmsdev); + +struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev); + #endif /* _VKMS_DRV_H_ */ diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c new file mode 100644 index 000000000000..48143eac3c12 --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_output.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "vkms_drv.h" +#include + +static void vkms_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs vkms_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vkms_connector_destroy, +}; + +static const struct drm_encoder_funcs vkms_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +int vkms_output_init(struct vkms_device *vkmsdev) +{ + struct vkms_output *output = &vkmsdev->output; + struct drm_device *dev = &vkmsdev->drm; + struct drm_connector *connector = &output->connector; + struct drm_encoder *encoder = &output->encoder; + struct drm_crtc *crtc = &output->crtc; + struct drm_plane *primary; + int ret; + + primary = vkms_plane_init(vkmsdev); + if (IS_ERR(primary)) + return PTR_ERR(primary); + + ret = vkms_crtc_init(dev, crtc, primary, NULL); + if (ret) + goto err_crtc; + + ret = drm_connector_init(dev, connector, &vkms_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to init connector\n"); + goto err_connector; + } + + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_connector_register; + } + + ret = drm_encoder_init(dev, encoder, &vkms_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to init encoder\n"); + goto err_encoder; + } + encoder->possible_crtcs = 1; + + ret = drm_mode_connector_attach_encoder(connector, encoder); + if (ret) { + DRM_ERROR("Failed to attach connector to encoder\n"); + goto err_attach; + } + + drm_mode_config_reset(dev); + + return 0; + +err_attach: + drm_encoder_cleanup(encoder); + +err_encoder: + drm_connector_unregister(connector); + +err_connector_register: + drm_connector_cleanup(connector); + +err_connector: + drm_crtc_cleanup(crtc); + +err_crtc: + drm_plane_cleanup(primary); + return ret; +} diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c new file mode 100644 index 000000000000..2c25b1d6ab5b --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_plane.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "vkms_drv.h" +#include +#include + +static const struct drm_plane_funcs vkms_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +struct drm_plane *vkms_plane_init(struct vkms_device *vkmsdev) +{ + struct drm_device *dev = &vkmsdev->drm; + struct drm_plane *plane; + const u32 *formats; + int ret, nformats; + + plane = kzalloc(sizeof(*plane), GFP_KERNEL); + if (!plane) + return ERR_PTR(-ENOMEM); + + formats = vkms_formats; + nformats = ARRAY_SIZE(vkms_formats); + + ret = drm_universal_plane_init(dev, plane, 0, + &vkms_plane_funcs, + formats, nformats, + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + kfree(plane); + return ERR_PTR(ret); + } + + return plane; +} -- cgit v1.2.3-55-g7522 From c91b007ed137c8497fa89993cc6757a8e81ff99b Mon Sep 17 00:00:00 2001 From: Rodrigo Siqueira Date: Wed, 16 May 2018 20:56:40 -0300 Subject: drm/vkms: Add extra information about vkms Add the following additional information: authors and description in Kconfig. Signed-off-by: Rodrigo Siqueira Reviewed-by: Haneen Mohammed Signed-off-by: Daniel Vetter Signed-off-by: Gustavo Padovan Link: https://patchwork.freedesktop.org/patch/msgid/180770375b0537f1ba1857bdb7fdc71dd201882e.1526514457.git.rodrigosiqueiramelo@gmail.com --- drivers/gpu/drm/Kconfig | 8 ++++++-- drivers/gpu/drm/vkms/vkms_drv.c | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 7db3d82cbb27..10f9f01123ea 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -214,10 +214,14 @@ config DRM_VGEM If M is selected the module will be called vgem. config DRM_VKMS - tristate "Virtual KMS" + tristate "Virtual KMS (EXPERIMENTAL)" depends on DRM + default n help - Choose this option to get a virtual kernal mode-setting driver. + Virtual Kernel Mode-Setting (VKMS) is used for testing or for + running GPU in a headless machines. Choose this option to get + a VKMS. + If M is selected the module will be called vkms. source "drivers/gpu/drm/exynos/Kconfig" diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 070613e32934..740a4cbfed91 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -133,5 +133,7 @@ static void __exit vkms_exit(void) module_init(vkms_init); module_exit(vkms_exit); +MODULE_AUTHOR("Haneen Mohammed "); +MODULE_AUTHOR("Rodrigo Siqueira "); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -- cgit v1.2.3-55-g7522 From 0d99889109892396a8164bf6dd178e36d3fe3166 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 28 Jun 2018 16:13:07 +0300 Subject: drm/fb-helper: Eliminate the .best_encoder() usage Instead of using the .best_encoder() hook to figure out whether a given connector+crtc combo will work, let's instead do what userspace does and just iterate over all the encoders for the connector, and then check each crtc against each encoder's possible_crtcs bitmask. v2: Avoid oopsing on NULL encoders (Daniel) s/connector_crtc_ok/connector_has_possible_crtc/ Cc: Dhinakaran Pandiyan Cc: Harry Wentland Cc: Daniel Vetter Reviewed-by: Daniel Vetter Reviewed-by: Alex Deucher Suggested-by: Daniel Vetter Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180628131315.14156-2-ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_fb_helper.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index cab14f253384..b37f06317d51 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -2323,6 +2323,27 @@ retry: return true; } +static bool connector_has_possible_crtc(struct drm_connector *connector, + struct drm_crtc *crtc) +{ + int i; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + struct drm_encoder *encoder; + + if (connector->encoder_ids[i] == 0) + break; + + encoder = drm_encoder_find(connector->dev, NULL, + connector->encoder_ids[i]); + + if (encoder->possible_crtcs & drm_crtc_mask(crtc)) + return true; + } + + return false; +} + static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, struct drm_fb_helper_crtc **best_crtcs, struct drm_display_mode **modes, @@ -2331,7 +2352,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, int c, o; struct drm_connector *connector; const struct drm_connector_helper_funcs *connector_funcs; - struct drm_encoder *encoder; int my_score, best_score, score; struct drm_fb_helper_crtc **crtcs, *crtc; struct drm_fb_helper_connector *fb_helper_conn; @@ -2362,20 +2382,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, connector_funcs = connector->helper_private; - /* - * If the DRM device implements atomic hooks and ->best_encoder() is - * NULL we fallback to the default drm_atomic_helper_best_encoder() - * helper. - */ - if (drm_drv_uses_atomic_modeset(fb_helper->dev) && - !connector_funcs->best_encoder) - encoder = drm_atomic_helper_best_encoder(connector); - else - encoder = connector_funcs->best_encoder(connector); - - if (!encoder) - goto out; - /* * select a crtc for this connector and then attempt to configure * remaining connectors @@ -2383,7 +2389,8 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, for (c = 0; c < fb_helper->crtc_count; c++) { crtc = &fb_helper->crtc_info[c]; - if ((encoder->possible_crtcs & (1 << c)) == 0) + if (!connector_has_possible_crtc(connector, + crtc->mode_set.crtc)) continue; for (o = 0; o < n; o++) @@ -2410,7 +2417,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, sizeof(struct drm_fb_helper_crtc *)); } } -out: + kfree(crtcs); return best_score; } -- cgit v1.2.3-55-g7522 From 20431c05ae682ff2ad387945738fc129d9751d6e Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 28 Jun 2018 16:13:08 +0300 Subject: drm/i915: Nuke intel_mst_best_encoder() With the fb-helper no longer relying on the non-atomic .best_encoder() we can eliminate the hook from the MST encoder. Cc: Daniel Vetter Cc: Dhinakaran Pandiyan Reviewed-by: Daniel Vetter Reviewed-by: Alex Deucher Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180628131315.14156-3-ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_dp_mst.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index 5890500a3a8b..0f012fbe34eb 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -403,20 +403,10 @@ static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *c return &intel_dp->mst_encoders[crtc->pipe]->base.base; } -static struct drm_encoder *intel_mst_best_encoder(struct drm_connector *connector) -{ - struct intel_connector *intel_connector = to_intel_connector(connector); - struct intel_dp *intel_dp = intel_connector->mst_port; - if (!intel_dp) - return NULL; - return &intel_dp->mst_encoders[0]->base.base; -} - static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = { .get_modes = intel_dp_mst_get_modes, .mode_valid = intel_dp_mst_mode_valid, .atomic_best_encoder = intel_mst_atomic_best_encoder, - .best_encoder = intel_mst_best_encoder, .atomic_check = intel_dp_mst_atomic_check, }; -- cgit v1.2.3-55-g7522 From 83aefbb887b59df0b3520965c3701e01deacfc52 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 28 Jun 2018 16:13:09 +0300 Subject: drm: Add drm_connector_for_each_possible_encoder() Add a convenience macro for iterating connector->encoder_ids[]. Isolates the users from the implementation details. Note that we don't seem to pass the file_priv down to drm_encoder_find() because encoders apparently don't get leased. No idea why drm_encoder_finc() even takes the file_priv actually. Also use ARRAY_SIZE() when populating the array to avoid spreading knowledge about the array size all over. v2: Hide the drm_encoder_find() in the macro, and rename the macro appropriately (Daniel) v3: Fix kernel docs (Daniel) Cc: Daniel Vetter Reviewed-by: Alex Deucher Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180628131315.14156-4-ville.syrjala@linux.intel.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_connector.c | 21 +++++++++------------ drivers/gpu/drm/drm_fb_helper.c | 11 ++--------- drivers/gpu/drm/drm_probe_helper.c | 10 +++------- include/drm/drm_connector.h | 13 +++++++++++++ 4 files changed, 27 insertions(+), 28 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index b09b3a3e4024..7387c0377e51 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -321,7 +321,7 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector, if (WARN_ON(connector->encoder)) return -EINVAL; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + for (i = 0; i < ARRAY_SIZE(connector->encoder_ids); i++) { if (connector->encoder_ids[i] == 0) { connector->encoder_ids[i] = encoder->base.id; return 0; @@ -1706,22 +1706,19 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, if (!connector) return -ENOENT; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) - if (connector->encoder_ids[i] != 0) - encoders_count++; + drm_connector_for_each_possible_encoder(connector, encoder, i) + encoders_count++; if ((out_resp->count_encoders >= encoders_count) && encoders_count) { copied = 0; encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] != 0) { - if (put_user(connector->encoder_ids[i], - encoder_ptr + copied)) { - ret = -EFAULT; - goto out; - } - copied++; + + drm_connector_for_each_possible_encoder(connector, encoder, i) { + if (put_user(encoder->base.id, encoder_ptr + copied)) { + ret = -EFAULT; + goto out; } + copied++; } } out_resp->count_encoders = encoders_count; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index b37f06317d51..d697c1c4a067 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -2326,17 +2326,10 @@ retry: static bool connector_has_possible_crtc(struct drm_connector *connector, struct drm_crtc *crtc) { + struct drm_encoder *encoder; int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - struct drm_encoder *encoder; - - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, - connector->encoder_ids[i]); - + drm_connector_for_each_possible_encoder(connector, encoder, i) { if (encoder->possible_crtcs & drm_crtc_mask(crtc)) return true; } diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 527743394150..1a901fe9e23e 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -88,9 +88,9 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode, struct drm_connector *connector) { struct drm_device *dev = connector->dev; - uint32_t *ids = connector->encoder_ids; enum drm_mode_status ret = MODE_OK; - unsigned int i; + struct drm_encoder *encoder; + int i; /* Step 1: Validate against connector */ ret = drm_connector_mode_valid(connector, mode); @@ -98,13 +98,9 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode, return ret; /* Step 2: Validate against encoders and crtcs */ - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - struct drm_encoder *encoder = drm_encoder_find(dev, NULL, ids[i]); + drm_connector_for_each_possible_encoder(connector, encoder, i) { struct drm_crtc *crtc; - if (!encoder) - continue; - ret = drm_encoder_mode_valid(encoder, mode); if (ret != MODE_OK) { /* No point in continuing for crtc check as this encoder diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index bf0f0f0786d3..bd6f30cf0755 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1199,4 +1199,17 @@ void drm_connector_list_iter_end(struct drm_connector_list_iter *iter); #define drm_for_each_connector_iter(connector, iter) \ while ((connector = drm_connector_list_iter_next(iter))) +/** + * drm_connector_for_each_possible_encoder - iterate connector's possible encoders + * @connector: &struct drm_connector pointer + * @encoder: &struct drm_encoder pointer used as cursor + * @__i: int iteration cursor, for macro-internal use + */ +#define drm_connector_for_each_possible_encoder(connector, encoder, __i) \ + for ((__i) = 0; (__i) < ARRAY_SIZE((connector)->encoder_ids) && \ + (connector)->encoder_ids[(__i)] != 0; (__i)++) \ + for_each_if((encoder) = \ + drm_encoder_find((connector)->dev, NULL, \ + (connector)->encoder_ids[(__i)])) \ + #endif -- cgit v1.2.3-55-g7522 From 98c0e348c09575edccdd63e9d2938f090b0d8739 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 28 Jun 2018 16:13:10 +0300 Subject: drm/amdgpu: Use drm_connector_for_each_possible_encoder() Use drm_connector_for_each_possible_encoder() for iterating connector->encoder_ids[]. A bit more convenient not having to deal with the implementation details. v2: Replace drm_for_each_connector_encoder_ids() with drm_connector_for_each_possible_encoder() (Daniel) Cc: Daniel Vetter Cc: Alex Deucher Cc: "Christian König" Cc: "David (ChunMing) Zhou" Cc: Harry Wentland Cc: amd-gfx@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/20180628131315.14156-5-ville.syrjala@linux.intel.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | 81 +++++++------------------- drivers/gpu/drm/amd/amdgpu/dce_virtual.c | 15 ++--- 2 files changed, 25 insertions(+), 71 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c index 8e66851eb427..881f7cb7ae6e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c @@ -212,30 +212,21 @@ static void amdgpu_connector_update_scratch_regs(struct drm_connector *connector, enum drm_connector_status status) { - struct drm_encoder *best_encoder = NULL; - struct drm_encoder *encoder = NULL; + struct drm_encoder *best_encoder; + struct drm_encoder *encoder; const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; bool connected; int i; best_encoder = connector_funcs->best_encoder(connector); - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, - connector->encoder_ids[i]); - if (!encoder) - continue; - + drm_connector_for_each_possible_encoder(connector, encoder, i) { if ((encoder == best_encoder) && (status == connector_status_connected)) connected = true; else connected = false; amdgpu_atombios_encoder_set_bios_scratch_regs(connector, encoder, connected); - } } @@ -246,17 +237,11 @@ amdgpu_connector_find_encoder(struct drm_connector *connector, struct drm_encoder *encoder; int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - encoder = drm_encoder_find(connector->dev, NULL, - connector->encoder_ids[i]); - if (!encoder) - continue; - + drm_connector_for_each_possible_encoder(connector, encoder, i) { if (encoder->encoder_type == encoder_type) return encoder; } + return NULL; } @@ -360,11 +345,13 @@ static int amdgpu_connector_ddc_get_modes(struct drm_connector *connector) static struct drm_encoder * amdgpu_connector_best_single_encoder(struct drm_connector *connector) { - int enc_id = connector->encoder_ids[0]; + struct drm_encoder *encoder; + int i; + + /* pick the first one */ + drm_connector_for_each_possible_encoder(connector, encoder, i) + return encoder; - /* pick the encoder ids */ - if (enc_id) - return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } @@ -985,9 +972,8 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force) struct drm_device *dev = connector->dev; struct amdgpu_device *adev = dev->dev_private; struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); - struct drm_encoder *encoder = NULL; const struct drm_encoder_helper_funcs *encoder_funcs; - int i, r; + int r; enum drm_connector_status ret = connector_status_disconnected; bool dret = false, broken_edid = false; @@ -1077,14 +1063,10 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force) /* find analog encoder */ if (amdgpu_connector->dac_load_detect) { - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); - if (!encoder) - continue; + struct drm_encoder *encoder; + int i; + drm_connector_for_each_possible_encoder(connector, encoder, i) { if (encoder->encoder_type != DRM_MODE_ENCODER_DAC && encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) continue; @@ -1132,18 +1114,11 @@ exit: static struct drm_encoder * amdgpu_connector_dvi_encoder(struct drm_connector *connector) { - int enc_id = connector->encoder_ids[0]; struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); struct drm_encoder *encoder; int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); - if (!encoder) - continue; + drm_connector_for_each_possible_encoder(connector, encoder, i) { if (amdgpu_connector->use_digital == true) { if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) return encoder; @@ -1158,8 +1133,9 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector) /* then check use digitial */ /* pick the first one */ - if (enc_id) - return drm_encoder_find(connector->dev, NULL, enc_id); + drm_connector_for_each_possible_encoder(connector, encoder, i) + return encoder; + return NULL; } @@ -1296,15 +1272,7 @@ u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn struct amdgpu_encoder *amdgpu_encoder; int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, - connector->encoder_ids[i]); - if (!encoder) - continue; - + drm_connector_for_each_possible_encoder(connector, encoder, i) { amdgpu_encoder = to_amdgpu_encoder(encoder); switch (amdgpu_encoder->encoder_id) { @@ -1326,14 +1294,7 @@ static bool amdgpu_connector_encoder_is_hbr2(struct drm_connector *connector) int i; bool found = false; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - encoder = drm_encoder_find(connector->dev, NULL, - connector->encoder_ids[i]); - if (!encoder) - continue; - + drm_connector_for_each_possible_encoder(connector, encoder, i) { amdgpu_encoder = to_amdgpu_encoder(encoder); if (amdgpu_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2) found = true; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c index dbf2ccd0c744..016f15093173 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c @@ -269,25 +269,18 @@ static int dce_virtual_early_init(void *handle) static struct drm_encoder * dce_virtual_encoder(struct drm_connector *connector) { - int enc_id = connector->encoder_ids[0]; struct drm_encoder *encoder; int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); - if (!encoder) - continue; - + drm_connector_for_each_possible_encoder(connector, encoder, i) { if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) return encoder; } /* pick the first one */ - if (enc_id) - return drm_encoder_find(connector->dev, NULL, enc_id); + drm_connector_for_each_possible_encoder(connector, encoder, i) + return encoder; + return NULL; } -- cgit v1.2.3-55-g7522 From ddba766dd07e67d0ca70b632d3bbac72234b547e Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Mon, 2 Jul 2018 18:29:27 +0300 Subject: drm/nouveau: Use drm_connector_for_each_possible_encoder() Use drm_connector_for_each_possible_encoder() for iterating connector->encoder_ids[]. A bit more convenient not having to deal with the implementation details. v2: Replace drm_for_each_connector_encoder_ids() with drm_connector_for_each_possible_encoder() (Daniel) v3: Initialize nv_encoder to NULL to shut up gcc/smatch Cc: Dan Carpenter Cc: Daniel Vetter Cc: Ben Skeggs Cc: nouveau@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/20180702152927.13351-1-ville.syrjala@linux.intel.com --- drivers/gpu/drm/nouveau/nouveau_connector.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 7b557c354307..bb46c1d489cf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -363,19 +363,11 @@ module_param_named(hdmimhz, nouveau_hdmimhz, int, 0400); struct nouveau_encoder * find_encoder(struct drm_connector *connector, int type) { - struct drm_device *dev = connector->dev; struct nouveau_encoder *nv_encoder; struct drm_encoder *enc; - int i, id; - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - id = connector->encoder_ids[i]; - if (!id) - break; + int i; - enc = drm_encoder_find(dev, NULL, id); - if (!enc) - continue; + drm_connector_for_each_possible_encoder(connector, enc, i) { nv_encoder = nouveau_encoder(enc); if (type == DCB_OUTPUT_ANY || @@ -420,7 +412,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_drm *drm = nouveau_drm(dev); struct nvkm_gpio *gpio = nvxx_gpio(&drm->client.device); - struct nouveau_encoder *nv_encoder; + struct nouveau_encoder *nv_encoder = NULL; struct drm_encoder *encoder; int i, panel = -ENODEV; @@ -436,14 +428,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) } } - for (i = 0; nv_encoder = NULL, i < DRM_CONNECTOR_MAX_ENCODER; i++) { - int id = connector->encoder_ids[i]; - if (id == 0) - break; - - encoder = drm_encoder_find(dev, NULL, id); - if (!encoder) - continue; + drm_connector_for_each_possible_encoder(connector, encoder, i) { nv_encoder = nouveau_encoder(encoder); if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { -- cgit v1.2.3-55-g7522 From 7b71ca249b265fe69ab3152a15bc6c305554a7da Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 28 Jun 2018 16:13:12 +0300 Subject: drm/radeon: Use drm_connector_for_each_possible_encoder() Use drm_connector_for_each_possible_encoder() for iterating connector->encoder_ids[]. A bit more convenient not having to deal with the implementation details. v2: Replace drm_for_each_connector_encoder_ids() with drm_connector_for_each_possible_encoder() (Daniel) Cc: Daniel Vetter Cc: Alex Deucher Cc: "Christian König" Cc: "David (ChunMing) Zhou" Cc: Harry Wentland Cc: amd-gfx@lists.freedesktop.org Signed-off-by: Ville Syrjälä Reviewed-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/20180628131315.14156-7-ville.syrjala@linux.intel.com --- drivers/gpu/drm/radeon/radeon_connectors.c | 90 +++++++++--------------------- 1 file changed, 26 insertions(+), 64 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 2aea2bdff99b..0655698f2956 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -244,23 +244,15 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c { struct drm_device *dev = connector->dev; struct radeon_device *rdev = dev->dev_private; - struct drm_encoder *best_encoder = NULL; - struct drm_encoder *encoder = NULL; + struct drm_encoder *best_encoder; + struct drm_encoder *encoder; const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; bool connected; int i; best_encoder = connector_funcs->best_encoder(connector); - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, - connector->encoder_ids[i]); - if (!encoder) - continue; - + drm_connector_for_each_possible_encoder(connector, encoder, i) { if ((encoder == best_encoder) && (status == connector_status_connected)) connected = true; else @@ -270,7 +262,6 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c radeon_atombios_connected_scratch_regs(connector, encoder, connected); else radeon_combios_connected_scratch_regs(connector, encoder, connected); - } } @@ -279,17 +270,11 @@ static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, struct drm_encoder *encoder; int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); - if (!encoder) - continue; - + drm_connector_for_each_possible_encoder(connector, encoder, i) { if (encoder->encoder_type == encoder_type) return encoder; } + return NULL; } @@ -393,10 +378,13 @@ static int radeon_ddc_get_modes(struct drm_connector *connector) static struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector) { - int enc_id = connector->encoder_ids[0]; - /* pick the encoder ids */ - if (enc_id) - return drm_encoder_find(connector->dev, NULL, enc_id); + struct drm_encoder *encoder; + int i; + + /* pick the first one */ + drm_connector_for_each_possible_encoder(connector, encoder, i) + return encoder; + return NULL; } @@ -436,19 +424,19 @@ radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector, struct drm_device *dev = connector->dev; struct drm_connector *conflict; struct radeon_connector *radeon_conflict; - int i; list_for_each_entry(conflict, &dev->mode_config.connector_list, head) { + struct drm_encoder *enc; + int i; + if (conflict == connector) continue; radeon_conflict = to_radeon_connector(conflict); - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (conflict->encoder_ids[i] == 0) - break; + drm_connector_for_each_possible_encoder(conflict, enc, i) { /* if the IDs match */ - if (conflict->encoder_ids[i] == encoder->base.id) { + if (enc == encoder) { if (conflict->status != connector_status_connected) continue; @@ -1256,7 +1244,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder = NULL; const struct drm_encoder_helper_funcs *encoder_funcs; - int i, r; + int r; enum drm_connector_status ret = connector_status_disconnected; bool dret = false, broken_edid = false; @@ -1374,15 +1362,9 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) /* find analog encoder */ if (radeon_connector->dac_load_detect) { - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, - connector->encoder_ids[i]); - if (!encoder) - continue; + int i; + drm_connector_for_each_possible_encoder(connector, encoder, i) { if (encoder->encoder_type != DRM_MODE_ENCODER_DAC && encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) continue; @@ -1458,18 +1440,11 @@ exit: /* okay need to be smart in here about which encoder to pick */ static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) { - int enc_id = connector->encoder_ids[0]; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder; int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); - if (!encoder) - continue; + drm_connector_for_each_possible_encoder(connector, encoder, i) { if (radeon_connector->use_digital == true) { if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) return encoder; @@ -1484,8 +1459,9 @@ static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) /* then check use digitial */ /* pick the first one */ - if (enc_id) - return drm_encoder_find(connector->dev, NULL, enc_id); + drm_connector_for_each_possible_encoder(connector, encoder, i) + return encoder; + return NULL; } @@ -1628,14 +1604,7 @@ u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn struct radeon_encoder *radeon_encoder; int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); - if (!encoder) - continue; - + drm_connector_for_each_possible_encoder(connector, encoder, i) { radeon_encoder = to_radeon_encoder(encoder); switch (radeon_encoder->encoder_id) { @@ -1657,14 +1626,7 @@ static bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector) int i; bool found = false; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; - - encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); - if (!encoder) - continue; - + drm_connector_for_each_possible_encoder(connector, encoder, i) { radeon_encoder = to_radeon_encoder(encoder); if (radeon_encoder->caps & ATOM_ENCODER_CAP_RECORD_HBR2) found = true; -- cgit v1.2.3-55-g7522 From 38cb8d96933eb07231073efdcd24a6ffa43b23e6 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 28 Jun 2018 16:13:13 +0300 Subject: drm: Add drm_connector_has_possible_encoder() Add a small helper for checking whether a connector and encoder are associated with each other. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180628131315.14156-8-ville.syrjala@linux.intel.com Reviewed-by: Alex Deucher --- drivers/gpu/drm/drm_connector.c | 23 +++++++++++++++++++++++ include/drm/drm_connector.h | 3 +++ 2 files changed, 26 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 7387c0377e51..5ada0640de5a 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -331,6 +331,29 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_connector_attach_encoder); +/** + * drm_connector_has_possible_encoder - check if the connector and encoder are assosicated with each other + * @connector: the connector + * @encoder: the encoder + * + * Returns: + * True if @encoder is one of the possible encoders for @connector. + */ +bool drm_connector_has_possible_encoder(struct drm_connector *connector, + struct drm_encoder *encoder) +{ + struct drm_encoder *enc; + int i; + + drm_connector_for_each_possible_encoder(connector, enc, i) { + if (enc == encoder) + return true; + } + + return false; +} +EXPORT_SYMBOL(drm_connector_has_possible_encoder); + static void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode) { diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index bd6f30cf0755..ad397dfc042b 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1187,6 +1187,9 @@ struct drm_connector * drm_connector_list_iter_next(struct drm_connector_list_iter *iter); void drm_connector_list_iter_end(struct drm_connector_list_iter *iter); +bool drm_connector_has_possible_encoder(struct drm_connector *connector, + struct drm_encoder *encoder); + /** * drm_for_each_connector_iter - connector_list iterator macro * @connector: &struct drm_connector pointer used as cursor -- cgit v1.2.3-55-g7522 From f8222409d1ac7a53de4c89db5a6dc268d523f6c7 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 28 Jun 2018 16:13:14 +0300 Subject: drm/msm: Use drm_connector_has_possible_encoder() Use drm_connector_has_possible_encoder() for checking whether the encoder has an associated connector. v2: Replace the drm_for_each_connector_encoder_ids() loop with a simple drm_connector_has_possible_encoder() call Cc: Rob Clark Cc: linux-arm-msm@vger.kernel.org Cc: freedreno@lists.freedesktop.org Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180628131315.14156-9-ville.syrjala@linux.intel.com Reviewed-by: Alex Deucher --- drivers/gpu/drm/msm/dsi/dsi_manager.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 4cb1cb68878b..4beba3f7d067 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -751,12 +751,8 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id) connector_list = &dev->mode_config.connector_list; list_for_each_entry(connector, connector_list, head) { - int i; - - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == encoder->base.id) - return connector; - } + if (drm_connector_has_possible_encoder(connector, encoder)) + return connector; } return ERR_PTR(-ENODEV); -- cgit v1.2.3-55-g7522 From 0b7510d15e14ba628ab9bacd6516e3f6a35ba21f Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 28 Jun 2018 16:13:15 +0300 Subject: drm/tilcdc: Use drm_connector_has_possible_encoder() Use drm_connector_has_possible_encoder() for checking whether the encoder has an associated connector. v2: Replace the drm_for_each_connector_encoder_ids() loop with a simple drm_connector_has_possible_encoder() call Cc: Jyri Sarha Cc: Tomi Valkeinen Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180628131315.14156-10-ville.syrjala@linux.intel.com Reviewed-by: Alex Deucher Acked-by: Jyri Sarha --- drivers/gpu/drm/tilcdc/tilcdc_external.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c index d651bdd6597e..b4eaf9bc87f8 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c @@ -103,12 +103,11 @@ struct drm_connector *tilcdc_encoder_find_connector(struct drm_device *ddev, struct drm_encoder *encoder) { struct drm_connector *connector; - int i; - list_for_each_entry(connector, &ddev->mode_config.connector_list, head) - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) - if (connector->encoder_ids[i] == encoder->base.id) - return connector; + list_for_each_entry(connector, &ddev->mode_config.connector_list, head) { + if (drm_connector_has_possible_encoder(connector, encoder)) + return connector; + } dev_err(ddev->dev, "No connector found for %s encoder (id %d)\n", encoder->name, encoder->base.id); -- cgit v1.2.3-55-g7522 From 408633d2e740221204100efb9c3eed71f39aacd0 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Wed, 4 Jul 2018 20:25:57 +0530 Subject: drm/v3d: use new return type vm_fault_t in v3d_gem_fault Instead of converting an errno into a vm_fault_t ourselves, use vmf_insert_mixed() which returns a vm_fault_t directly. Signed-off-by: Souptick Joarder Signed-off-by: Eric Anholt Link: https://patchwork.freedesktop.org/patch/msgid/20180704145556.GA11036@jordon-HP-15-Notebook-PC Reviewed-by: Matthew Wilcox --- drivers/gpu/drm/v3d/v3d_bo.c | 28 +++++----------------------- drivers/gpu/drm/v3d/v3d_drv.h | 3 ++- 2 files changed, 7 insertions(+), 24 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c index 7b1e2a549a71..54d96518a131 100644 --- a/drivers/gpu/drm/v3d/v3d_bo.c +++ b/drivers/gpu/drm/v3d/v3d_bo.c @@ -227,37 +227,19 @@ v3d_set_mmap_vma_flags(struct vm_area_struct *vma) vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); } -int v3d_gem_fault(struct vm_fault *vmf) +vm_fault_t v3d_gem_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *obj = vma->vm_private_data; struct v3d_bo *bo = to_v3d_bo(obj); - unsigned long pfn; + pfn_t pfn; pgoff_t pgoff; - int ret; /* We don't use vmf->pgoff since that has the fake offset: */ pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT; - pfn = page_to_pfn(bo->pages[pgoff]); - - ret = vm_insert_mixed(vma, vmf->address, __pfn_to_pfn_t(pfn, PFN_DEV)); - - switch (ret) { - case -EAGAIN: - case 0: - case -ERESTARTSYS: - case -EINTR: - case -EBUSY: - /* - * EBUSY is ok: this just means that another thread - * already did the job. - */ - return VM_FAULT_NOPAGE; - case -ENOMEM: - return VM_FAULT_OOM; - default: - return VM_FAULT_SIGBUS; - } + pfn = __pfn_to_pfn_t(page_to_pfn(bo->pages[pgoff]), PFN_DEV); + + return vmf_insert_mixed(vma, vmf->address, pfn); } int v3d_mmap(struct file *filp, struct vm_area_struct *vma) diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index f32ac8c98f37..282763c9c7f6 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -2,6 +2,7 @@ /* Copyright (C) 2015-2018 Broadcom */ #include +#include #include #include #include @@ -252,7 +253,7 @@ int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -int v3d_gem_fault(struct vm_fault *vmf); +vm_fault_t v3d_gem_fault(struct vm_fault *vmf); int v3d_mmap(struct file *filp, struct vm_area_struct *vma); struct reservation_object *v3d_prime_res_obj(struct drm_gem_object *obj); int v3d_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); -- cgit v1.2.3-55-g7522 From 624bb0c08b8298cbc6a16f9c68edc93f767716ec Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 3 Jul 2018 10:05:12 -0700 Subject: drm/v3d: Delay the scheduler timeout if we're still making progress. GTF-GLES2.gtf.GL.acos.acos_float_vert_xvary submits jobs that take 4 seconds at maximum resolution, but we still want to reset quickly if a job is really hung. Sample the CL's current address and the return address (since we call into tile lists repeatedly) and if either has changed then assume we've made progress. Signed-off-by: Eric Anholt Cc: Lucas Stach Link: https://patchwork.freedesktop.org/patch/msgid/20180703170515.6298-1-eric@anholt.net Reviewed-by: Alex Deucher Acked-by: Daniel Vetter --- drivers/gpu/drm/v3d/v3d_drv.h | 2 ++ drivers/gpu/drm/v3d/v3d_regs.h | 1 + drivers/gpu/drm/v3d/v3d_sched.c | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 282763c9c7f6..e6fed696ad86 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -184,6 +184,8 @@ struct v3d_job { /* GPU virtual addresses of the start/end of the CL job. */ u32 start, end; + + u32 timedout_ctca, timedout_ctra; }; struct v3d_exec_info { diff --git a/drivers/gpu/drm/v3d/v3d_regs.h b/drivers/gpu/drm/v3d/v3d_regs.h index fc13282dfc2f..854046565989 100644 --- a/drivers/gpu/drm/v3d/v3d_regs.h +++ b/drivers/gpu/drm/v3d/v3d_regs.h @@ -222,6 +222,7 @@ #define V3D_CLE_CTNCA(n) (V3D_CLE_CT0CA + 4 * n) #define V3D_CLE_CT0RA 0x00118 #define V3D_CLE_CT1RA 0x0011c +#define V3D_CLE_CTNRA(n) (V3D_CLE_CT0RA + 4 * n) #define V3D_CLE_CT0LC 0x00120 #define V3D_CLE_CT1LC 0x00124 #define V3D_CLE_CT0PC 0x00128 diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 808bc901f567..00667c733dca 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -153,7 +153,25 @@ v3d_job_timedout(struct drm_sched_job *sched_job) struct v3d_job *job = to_v3d_job(sched_job); struct v3d_exec_info *exec = job->exec; struct v3d_dev *v3d = exec->v3d; + enum v3d_queue job_q = job == &exec->bin ? V3D_BIN : V3D_RENDER; enum v3d_queue q; + u32 ctca = V3D_CORE_READ(0, V3D_CLE_CTNCA(job_q)); + u32 ctra = V3D_CORE_READ(0, V3D_CLE_CTNRA(job_q)); + + /* If the current address or return address have changed, then + * the GPU has probably made progress and we should delay the + * reset. This could fail if the GPU got in an infinite loop + * in the CL, but that is pretty unlikely outside of an i-g-t + * testcase. + */ + if (job->timedout_ctca != ctca || job->timedout_ctra != ctra) { + job->timedout_ctca = ctca; + job->timedout_ctra = ctra; + + schedule_delayed_work(&job->base.work_tdr, + job->base.sched->timeout); + return; + } mutex_lock(&v3d->reset_lock); -- cgit v1.2.3-55-g7522 From e0d018119ae82cbde32c1d4f8e9b8d8f43a3c88a Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 3 Jul 2018 10:05:13 -0700 Subject: drm/v3d: Remove unnecessary dma_fence_ops. The dma-fence core as of commit 418cc6ca0607 ("dma-fence: Make ->wait callback optional") provides appropriate defaults for these methods. Signed-off-by: Eric Anholt Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703170515.6298-2-eric@anholt.net Reviewed-by: Alex Deucher Acked-by: Daniel Vetter --- drivers/gpu/drm/v3d/v3d_fence.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/v3d/v3d_fence.c b/drivers/gpu/drm/v3d/v3d_fence.c index bfe31a89668b..50bfcf9a8a1a 100644 --- a/drivers/gpu/drm/v3d/v3d_fence.c +++ b/drivers/gpu/drm/v3d/v3d_fence.c @@ -35,19 +35,7 @@ static const char *v3d_fence_get_timeline_name(struct dma_fence *fence) return "v3d-render"; } -static bool v3d_fence_enable_signaling(struct dma_fence *fence) -{ - return true; -} - const struct dma_fence_ops v3d_fence_ops = { .get_driver_name = v3d_fence_get_driver_name, .get_timeline_name = v3d_fence_get_timeline_name, - .enable_signaling = v3d_fence_enable_signaling, - /* Each of our fences gets signaled as complete by the IRQ - * handler, so we rely on the core's tracking of signaling. - */ - .signaled = NULL, - .wait = dma_fence_default_wait, - .release = dma_fence_free, }; -- cgit v1.2.3-55-g7522 From a65020d0a65da69f504b5ee4d43d255d0875999e Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 3 Jul 2018 10:05:15 -0700 Subject: drm/v3d: Fix a grammar nit in the scheduler docs. Signed-off-by: Eric Anholt Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703170515.6298-4-eric@anholt.net Reviewed-by: Alex Deucher Acked-by: Daniel Vetter --- drivers/gpu/drm/v3d/v3d_sched.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 00667c733dca..a5501581d96b 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -14,8 +14,8 @@ * to the HW only when it has completed the last one, instead of * filling up the CT[01]Q FIFOs with jobs. Similarly, we use * v3d_job_dependency() to manage the dependency between bin and - * render, instead of having the clients submit jobs with using the - * HW's semaphores to interlock between them. + * render, instead of having the clients submit jobs using the HW's + * semaphores to interlock between them. */ #include -- cgit v1.2.3-55-g7522 From ca52bea9fa8007f837ab4685b98979bbffc33416 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 15 Jun 2018 20:07:34 +0300 Subject: drm/atomic-helper: Use bitwise or for filling a bitmask Using += to set the bits in a mask looks funny. It works in this case because we never set the same bit twice. But let's switch to |= to make this look more regular. Cc: Maarten Lankhorst Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180615170734.2774-1-ville.syrjala@linux.intel.com Reviewed-by: Eric Anholt --- drivers/gpu/drm/drm_atomic_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 8008a7de2e10..7787e0e6bbc6 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -645,7 +645,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, if (ret) return ret; - connectors_mask += BIT(i); + connectors_mask |= BIT(i); } /* -- cgit v1.2.3-55-g7522 From f8878bb2f8675e3c7716906b9b41bddc9b3bc039 Mon Sep 17 00:00:00 2001 From: Benjamin Gaignard Date: Tue, 5 Jun 2018 15:54:01 +0200 Subject: drm: print plane state normalized zpos value When dumping plane state print normalized zpos value as done for the other plane state fields. Signed-off-by: Benjamin Gaignard Reviewed-by: Philippe Cornu Link: https://patchwork.freedesktop.org/patch/msgid/20180605135407.20214-2-benjamin.gaignard@linaro.org --- drivers/gpu/drm/drm_atomic.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index d5cefb1cb2a2..7c55991170cc 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1111,6 +1111,7 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, drm_printf(p, "\tcrtc-pos=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&dest)); drm_printf(p, "\tsrc-pos=" DRM_RECT_FP_FMT "\n", DRM_RECT_FP_ARG(&src)); drm_printf(p, "\trotation=%x\n", state->rotation); + drm_printf(p, "\tnormalized-zpos=%x\n", state->normalized_zpos); drm_printf(p, "\tcolor-encoding=%s\n", drm_get_color_encoding_name(state->color_encoding)); drm_printf(p, "\tcolor-range=%s\n", -- cgit v1.2.3-55-g7522 From 7868e5079228b4c07cf61b5ad5d35af5c29f7375 Mon Sep 17 00:00:00 2001 From: Yannick Fertre Date: Fri, 29 Jun 2018 13:22:22 +0200 Subject: drm/stm: ltdc: filter mode pixel clock vs pad constraint Filter the requested mode pixel clock frequency according to the pad maximum supported frequency. Signed-off-by: Yannick Fertre Reviewed-by: Philippe Cornu Tested-by: Philippe Cornu Signed-off-by: Benjamin Gaignard Link: https://patchwork.freedesktop.org/patch/msgid/1530271342-5532-1-git-send-email-yannick.fertre@st.com --- drivers/gpu/drm/stm/ltdc.c | 16 ++++++++++++---- drivers/gpu/drm/stm/ltdc.h | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index d997a6014d6c..f1f37336a57a 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -457,6 +457,14 @@ ltdc_crtc_mode_valid(struct drm_crtc *crtc, int target_max = target + CLK_TOLERANCE_HZ; int result; + result = clk_round_rate(ldev->pixel_clk, target); + + DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result); + + /* Filter modes according to the max frequency supported by the pads */ + if (result > ldev->caps.pad_max_freq_hz) + return MODE_CLOCK_HIGH; + /* * Accept all "preferred" modes: * - this is important for panels because panel clock tolerances are @@ -468,10 +476,6 @@ ltdc_crtc_mode_valid(struct drm_crtc *crtc, if (mode->type & DRM_MODE_TYPE_PREFERRED) return MODE_OK; - result = clk_round_rate(ldev->pixel_clk, target); - - DRM_DEBUG_DRIVER("clk rate target %d, available %d\n", target, result); - /* * Filter modes according to the clock value, particularly useful for * hdmi modes that require precise pixel clocks. @@ -991,11 +995,15 @@ static int ltdc_get_caps(struct drm_device *ddev) * does not work on 2nd layer. */ ldev->caps.non_alpha_only_l1 = true; + ldev->caps.pad_max_freq_hz = 90000000; + if (ldev->caps.hw_version == HWVER_10200) + ldev->caps.pad_max_freq_hz = 65000000; break; case HWVER_20101: ldev->caps.reg_ofs = REG_OFS_4; ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1; ldev->caps.non_alpha_only_l1 = false; + ldev->caps.pad_max_freq_hz = 150000000; break; default: return -ENODEV; diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h index 1e16d6afb0d2..d5afb8960867 100644 --- a/drivers/gpu/drm/stm/ltdc.h +++ b/drivers/gpu/drm/stm/ltdc.h @@ -18,6 +18,7 @@ struct ltdc_caps { u32 bus_width; /* bus width (32 or 64 bits) */ const u32 *pix_fmt_hw; /* supported pixel formats */ bool non_alpha_only_l1; /* non-native no-alpha formats on layer 1 */ + int pad_max_freq_hz; /* max frequency supported by pad */ }; #define LTDC_MAX_LAYER 4 -- cgit v1.2.3-55-g7522 From 8adbbb2e7871e76d53ea91e6159864ee534fdac8 Mon Sep 17 00:00:00 2001 From: Yannick Fertre Date: Fri, 29 Jun 2018 13:22:35 +0200 Subject: drm/stm: ltdc: rework reset sequence Reset must be properly assert before deassert. This is important if there is an early boot splash screen before the kernel start up. Signed-off-by: Yannick Fertre Reviewed-by: Philippe Cornu Tested-by: Philippe Cornu Signed-off-by: Benjamin Gaignard Link: https://patchwork.freedesktop.org/patch/msgid/1530271355-5608-1-git-send-email-yannick.fertre@st.com --- drivers/gpu/drm/stm/ltdc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index f1f37336a57a..808d9fb627e9 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -1082,8 +1082,11 @@ int ltdc_load(struct drm_device *ddev) } } - if (!IS_ERR(rstc)) + if (!IS_ERR(rstc)) { + reset_control_assert(rstc); + usleep_range(10, 20); reset_control_deassert(rstc); + } /* Disable interrupts */ reg_clear(ldev->regs, LTDC_IER, -- cgit v1.2.3-55-g7522 From a012024571d98e2e4bf29a9168fb7ddc44b7ab86 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 18 Apr 2018 14:51:21 +0200 Subject: drm/crc: Only report a single overflow when a CRC fd is opened This reduces the amount of spam when you debug a CRC reading program. Signed-off-by: Maarten Lankhorst [mlankhorst: Change bool overflow to was_overflow (Ville)] Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20180418125121.72081-1-maarten.lankhorst@linux.intel.com --- drivers/gpu/drm/drm_debugfs_crc.c | 9 ++++++++- include/drm/drm_debugfs_crc.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c index 9f8312137cad..99961192bf03 100644 --- a/drivers/gpu/drm/drm_debugfs_crc.c +++ b/drivers/gpu/drm/drm_debugfs_crc.c @@ -139,6 +139,7 @@ static int crtc_crc_data_count(struct drm_crtc_crc *crc) static void crtc_crc_cleanup(struct drm_crtc_crc *crc) { kfree(crc->entries); + crc->overflow = false; crc->entries = NULL; crc->head = 0; crc->tail = 0; @@ -391,8 +392,14 @@ int drm_crtc_add_crc_entry(struct drm_crtc *crtc, bool has_frame, tail = crc->tail; if (CIRC_SPACE(head, tail, DRM_CRC_ENTRIES_NR) < 1) { + bool was_overflow = crc->overflow; + + crc->overflow = true; spin_unlock(&crc->lock); - DRM_ERROR("Overflow of CRC buffer, userspace reads too slow.\n"); + + if (!was_overflow) + DRM_ERROR("Overflow of CRC buffer, userspace reads too slow.\n"); + return -ENOBUFS; } diff --git a/include/drm/drm_debugfs_crc.h b/include/drm/drm_debugfs_crc.h index 7d63b1d4adb9..b225eeb30d05 100644 --- a/include/drm/drm_debugfs_crc.h +++ b/include/drm/drm_debugfs_crc.h @@ -43,6 +43,7 @@ struct drm_crtc_crc_entry { * @lock: protects the fields in this struct * @source: name of the currently configured source of CRCs * @opened: whether userspace has opened the data file for reading + * @overflow: whether an overflow occured. * @entries: array of entries, with size of %DRM_CRC_ENTRIES_NR * @head: head of circular queue * @tail: tail of circular queue @@ -52,7 +53,7 @@ struct drm_crtc_crc_entry { struct drm_crtc_crc { spinlock_t lock; const char *source; - bool opened; + bool opened, overflow; struct drm_crtc_crc_entry *entries; int head, tail; size_t values_cnt; -- cgit v1.2.3-55-g7522 From 73915b2b1f2581cbce5a511550c2cac2e2b42595 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 3 Jul 2018 19:40:46 +0200 Subject: drm/writeback: Fix the "overview" section of the doc Fix the bullet list declaration in the overview section. Signed-off-by: Boris Brezillon Reported-by: Daniel Vetter Fixes: 935774cd71fe ("drm: Add writeback connector type") Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703174046.22396-1-boris.brezillon@bootlin.com --- drivers/gpu/drm/drm_writeback.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c index 827395071f0b..69e7a63cfcc3 100644 --- a/drivers/gpu/drm/drm_writeback.c +++ b/drivers/gpu/drm/drm_writeback.c @@ -22,10 +22,13 @@ * Writeback connectors are used to expose hardware which can write the output * from a CRTC to a memory buffer. They are used and act similarly to other * types of connectors, with some important differences: - * - Writeback connectors don't provide a way to output visually to the user. - * - Writeback connectors should always report as "disconnected" (so that - * clients which don't understand them will ignore them). - * - Writeback connectors don't have EDID. + * + * * Writeback connectors don't provide a way to output visually to the user. + * + * * Writeback connectors should always report as "disconnected" (so that + * clients which don't understand them will ignore them). + * + * * Writeback connectors don't have EDID. * * A framebuffer may only be attached to a writeback connector when the * connector is attached to a CRTC. The WRITEBACK_FB_ID property which sets the -- cgit v1.2.3-55-g7522 From b82c1f8f78b4d273d1bcefe3e805eff61e879a68 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 3 Jul 2018 09:50:15 +0200 Subject: drm/atomic: Avoid connector to writeback_connector casts Use container_of() instead of type casting so that it keeps working even if base is moved inside the drm_writeback_connector struct. Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703075022.15138-2-boris.brezillon@bootlin.com --- drivers/gpu/drm/drm_atomic.c | 4 +++- include/drm/drm_writeback.h | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index e6062c779aaf..3eb061e11e2e 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -2428,6 +2428,7 @@ static int prepare_signaling(struct drm_device *dev, } for_each_new_connector_in_state(state, conn, conn_state, i) { + struct drm_writeback_connector *wb_conn; struct drm_writeback_job *job; struct drm_out_fence_state *f; struct dma_fence *fence; @@ -2451,7 +2452,8 @@ static int prepare_signaling(struct drm_device *dev, f[*num_fences].out_fence_ptr = fence_ptr; *fence_state = f; - fence = drm_writeback_get_out_fence((struct drm_writeback_connector *)conn); + wb_conn = drm_connector_to_writeback(conn); + fence = drm_writeback_get_out_fence(wb_conn); if (!fence) return -ENOMEM; diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h index a10fe556dfd4..23df9d463003 100644 --- a/include/drm/drm_writeback.h +++ b/include/drm/drm_writeback.h @@ -110,6 +110,12 @@ struct drm_writeback_job { struct dma_fence *out_fence; }; +static inline struct drm_writeback_connector * +drm_connector_to_writeback(struct drm_connector *connector) +{ + return container_of(connector, struct drm_writeback_connector, base); +} + int drm_writeback_connector_init(struct drm_device *dev, struct drm_writeback_connector *wb_connector, const struct drm_connector_funcs *con_funcs, -- cgit v1.2.3-55-g7522 From 425132fdb169c2a2772190ff56e0e8eea37ff716 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 3 Jul 2018 09:50:16 +0200 Subject: drm/connector: Pass a drm_connector_state to ->atomic_commit() Other atomic hooks are passed state objects, let's change this one to be consistent. Signed-off-by: Boris Brezillon Acked-by: Liviu Dudau Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703075022.15138-3-boris.brezillon@bootlin.com --- drivers/gpu/drm/drm_atomic_helper.c | 2 +- include/drm/drm_modeset_helper_vtables.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 7787e0e6bbc6..47e84aa7b6e2 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1187,7 +1187,7 @@ static void drm_atomic_helper_commit_writebacks(struct drm_device *dev, if (new_conn_state->writeback_job && new_conn_state->writeback_job->fb) { WARN_ON(connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK); - funcs->atomic_commit(connector, new_conn_state->writeback_job); + funcs->atomic_commit(connector, new_conn_state); } } } diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 3b289773297c..fb841f44949c 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -980,11 +980,13 @@ struct drm_connector_helper_funcs { * * This hook is to be used by drivers implementing writeback connectors * that need a point when to commit the writeback job to the hardware. + * The writeback_job to commit is available in + * &drm_connector_state.writeback_job. * * This callback is used by the atomic modeset helpers. */ void (*atomic_commit)(struct drm_connector *connector, - struct drm_writeback_job *writeback_job); + struct drm_connector_state *state); }; /** -- cgit v1.2.3-55-g7522 From 814bde99ee809c172a409829889d1bafc3e4bcf7 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 3 Jul 2018 09:50:17 +0200 Subject: drm/connector: Make ->atomic_commit() optional Not all writeback connector implementations might want to commit things from the connector driver. Some, like the malidp driver, commit things from their main commit_tail() function, and would rather not have to implement a dummy hook for drm_connector_helper_funcs.atomic_commit(). Make this function optional and reflect this fact in the doc. Signed-off-by: Boris Brezillon Acked-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/20180703075022.15138-4-boris.brezillon@bootlin.com --- drivers/gpu/drm/drm_atomic_helper.c | 2 ++ include/drm/drm_modeset_helper_vtables.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 47e84aa7b6e2..bab8051690bb 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1184,6 +1184,8 @@ static void drm_atomic_helper_commit_writebacks(struct drm_device *dev, const struct drm_connector_helper_funcs *funcs; funcs = connector->helper_private; + if (!funcs->atomic_commit) + continue; if (new_conn_state->writeback_job && new_conn_state->writeback_job->fb) { WARN_ON(connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK); diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index fb841f44949c..d0eb76c4b309 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -983,6 +983,8 @@ struct drm_connector_helper_funcs { * The writeback_job to commit is available in * &drm_connector_state.writeback_job. * + * This hook is optional. + * * This callback is used by the atomic modeset helpers. */ void (*atomic_commit)(struct drm_connector *connector, -- cgit v1.2.3-55-g7522 From 184d3cf4f73896267340cf06acfa751fad4f8dd2 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 3 Jul 2018 09:50:18 +0200 Subject: drm/vc4: Use wait_for_flip_done() instead of wait_for_vblanks() drm_atomic_helper_wait_for_vblanks() assumes the CRTC will continuously generate VBLANK events and the vblank counter will keep increasing. While this work for a regular pipeline, it doesn't when you have the CRTC is feeding the transposer block, because this block works in oneshot mode, and, by the time we reach drm_atomic_helper_wait_for_vblanks() the only VBLANK event might have already been sent and the VBLANK counter will stay unchanged, thus triggering a timeout. Luckily, we can replace the drm_atomic_helper_wait_for_vblanks() call by drm_atomic_helper_wait_for_flip_done() because the only thing we want to check when calling drm_atomic_helper_wait_for_vblanks() from vc4_atomic_complete_commit() is that new FBs are in use and the old ones can be safely released. Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Reviewed-by: Eric Anholt Link: https://patchwork.freedesktop.org/patch/msgid/20180703075022.15138-5-boris.brezillon@bootlin.com --- drivers/gpu/drm/vc4/vc4_kms.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 8a411e5f8776..91239b0a4fa0 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -153,18 +153,9 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_enables(dev, state); - /* Make sure that drm_atomic_helper_wait_for_vblanks() - * actually waits for vblank. If we're doing a full atomic - * modeset (as opposed to a vc4_update_plane() short circuit), - * then we need to wait for scanout to be done with our - * display lists before we free it and potentially reallocate - * and overwrite the dlist memory with a new modeset. - */ - state->legacy_cursor_update = false; - drm_atomic_helper_commit_hw_done(state); - drm_atomic_helper_wait_for_vblanks(dev, state); + drm_atomic_helper_wait_for_flip_done(dev, state); drm_atomic_helper_cleanup_planes(dev, state); -- cgit v1.2.3-55-g7522 From b25c60af7a8773694a505cdb0d2e67807243217d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 3 Jul 2018 09:50:19 +0200 Subject: drm/crtc: Add a generic infrastructure to fake VBLANK events In some cases CRTCs are active but are not able to generating events, at least not at every frame at it's expected to. This is typically the case when the CRTC is feeding a writeback connector that has no job queued. In this situation the CRTC is usually stopped until a new job is queued, and this can lead to timeouts when part of the pipeline is updated but no new jobs are queued to the active writeback connector. In order to solve that, we add a ->no_vblank flag to drm_crtc_state and ask the CRTC drivers to set it to true when they know they're not able to generate VBLANK events. The core drm_atomic_helper_fake_vblank() helper can then be used to fake VBLANKs at commit time. Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703075022.15138-6-boris.brezillon@bootlin.com --- drivers/gpu/drm/drm_atomic_helper.c | 39 +++++++++++++++++++++++++++++++++++++ include/drm/drm_atomic_helper.h | 1 + include/drm/drm_crtc.h | 23 ++++++++++++++++++++++ 3 files changed, 63 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index bab8051690bb..c31f670b1283 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2053,6 +2053,45 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) } EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); +/** + * drm_atomic_helper_fake_vblank - fake VBLANK events if needed + * @old_state: atomic state object with old state structures + * + * This function walks all CRTCs and fake VBLANK events on those with + * &drm_crtc_state.no_vblank set to true and &drm_crtc_state.event != NULL. + * The primary use of this function is writeback connectors working in oneshot + * mode and faking VBLANK events. In this case they only fake the VBLANK event + * when a job is queued, and any change to the pipeline that does not touch the + * connector is leading to timeouts when calling + * drm_atomic_helper_wait_for_vblanks() or + * drm_atomic_helper_wait_for_flip_done(). + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +void drm_atomic_helper_fake_vblank(struct drm_atomic_state *old_state) +{ + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { + unsigned long flags; + + if (!new_crtc_state->no_vblank) + continue; + + spin_lock_irqsave(&old_state->dev->event_lock, flags); + if (new_crtc_state->event) { + drm_crtc_send_vblank_event(crtc, + new_crtc_state->event); + new_crtc_state->event = NULL; + } + spin_unlock_irqrestore(&old_state->dev->event_lock, flags); + } +} +EXPORT_SYMBOL(drm_atomic_helper_fake_vblank); + /** * drm_atomic_helper_commit_hw_done - setup possible nonblocking commit * @old_state: atomic state object with old state structures diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 26aaba58d6ce..99e2a5297c69 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -100,6 +100,7 @@ int __must_check drm_atomic_helper_swap_state(struct drm_atomic_state *state, int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, bool nonblock); void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state); +void drm_atomic_helper_fake_vblank(struct drm_atomic_state *state); void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state); void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 23eddbccab10..17f4f93340b8 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -119,6 +119,29 @@ struct drm_crtc_state { bool zpos_changed : 1; bool color_mgmt_changed : 1; + /** + * @no_vblank: + * + * Reflects the ability of a CRTC to send VBLANK events. This state + * usually depends on the pipeline configuration, and the main usuage + * is CRTCs feeding a writeback connector operating in oneshot mode. + * In this case the VBLANK event is only generated when a job is queued + * to the writeback connector, and we want the core to fake VBLANK + * events when this part of the pipeline hasn't changed but others had + * or when the CRTC and connectors are being disabled. + * + * __drm_atomic_helper_crtc_duplicate_state() will not reset the value + * from the current state, the CRTC driver is then responsible for + * updating this field when needed. + * + * Note that the combination of &drm_crtc_state.event == NULL and + * &drm_crtc_state.no_blank == true is valid and usually used when the + * writeback connector attached to the CRTC has a new job queued. In + * this case the driver will send the VBLANK event on its own when the + * writeback job is complete. + */ + bool no_vblank : 1; + /* attached planes bitmask: * WARNING: transitional helpers do not maintain plane_mask so * drivers not converted over to atomic helpers should not rely -- cgit v1.2.3-55-g7522 From 6fb42b6682f0d3c6ca9dc0e338c294fb92448e1d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 3 Jul 2018 09:50:20 +0200 Subject: drm/atomic: Call fake_vblank() from the generic commit_tail() helpers Now that we have a way to fake VBLANK events when requested by the CRTC hook it up to the generic commit_tail() helpers. Signed-off-by: Boris Brezillon Reviewed-by: Liviu Dudau Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703075022.15138-7-boris.brezillon@bootlin.com --- drivers/gpu/drm/drm_atomic_helper.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index c31f670b1283..91fda6b8926e 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1450,6 +1450,8 @@ void drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state) drm_atomic_helper_commit_modeset_enables(dev, old_state); + drm_atomic_helper_fake_vblank(old_state); + drm_atomic_helper_commit_hw_done(old_state); drm_atomic_helper_wait_for_vblanks(dev, old_state); @@ -1479,6 +1481,8 @@ void drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state) drm_atomic_helper_commit_planes(dev, old_state, DRM_PLANE_COMMIT_ACTIVE_ONLY); + drm_atomic_helper_fake_vblank(old_state); + drm_atomic_helper_commit_hw_done(old_state); drm_atomic_helper_wait_for_vblanks(dev, old_state); -- cgit v1.2.3-55-g7522 From 1ebe99a75eeda1067c39c1c039ca9bb7e3c9c342 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 3 Jul 2018 09:50:21 +0200 Subject: drm/vc4: Call drm_atomic_helper_fake_vblank() in the commit path Mimic what is done in drm_atomic_commit_tail() and call drm_atomic_helper_fake_vblank() so that VBLANK events are faked when the drm_crtc_state.no_vblank is true. Will be needed when we'll add support for the transposer block. Signed-off-by: Boris Brezillon Reviewed-by: Eric Anholt Link: https://patchwork.freedesktop.org/patch/msgid/20180703075022.15138-8-boris.brezillon@bootlin.com --- drivers/gpu/drm/vc4/vc4_kms.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 91239b0a4fa0..ca5aa7fba769 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -153,6 +153,8 @@ vc4_atomic_complete_commit(struct drm_atomic_state *state) drm_atomic_helper_commit_modeset_enables(dev, state); + drm_atomic_helper_fake_vblank(state); + drm_atomic_helper_commit_hw_done(state); drm_atomic_helper_wait_for_flip_done(dev, state); -- cgit v1.2.3-55-g7522 From 008095e065a85a13ffb41b9c98149456267c30b8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 3 Jul 2018 09:50:22 +0200 Subject: drm/vc4: Add support for the transposer block The transposer block is providing support for mem-to-mem composition, which is exposed as a drm_writeback connector in DRM. Add a driver to support this feature. Signed-off-by: Boris Brezillon Reviewed-by: Eric Anholt Link: https://patchwork.freedesktop.org/patch/msgid/20180703075022.15138-9-boris.brezillon@bootlin.com --- .../devicetree/bindings/display/brcm,bcm-vc4.txt | 6 + drivers/gpu/drm/vc4/Makefile | 1 + drivers/gpu/drm/vc4/vc4_crtc.c | 138 ++++-- drivers/gpu/drm/vc4/vc4_debugfs.c | 1 + drivers/gpu/drm/vc4/vc4_drv.c | 1 + drivers/gpu/drm/vc4/vc4_drv.h | 7 + drivers/gpu/drm/vc4/vc4_txp.c | 477 +++++++++++++++++++++ 7 files changed, 607 insertions(+), 24 deletions(-) create mode 100644 drivers/gpu/drm/vc4/vc4_txp.c (limited to 'drivers/gpu/drm') diff --git a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt index 284e2b14cfbe..26649b4c4dd8 100644 --- a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt +++ b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt @@ -74,6 +74,12 @@ Required properties for DSI: The 3 clocks output from the DSI analog PHY: dsi[01]_byte, dsi[01]_ddr2, and dsi[01]_ddr +Required properties for the TXP (writeback) block: +- compatible: Should be "brcm,bcm2835-txp" +- reg: Physical base address and length of the TXP block's registers +- interrupts: The interrupt number + See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt + [1] Documentation/devicetree/bindings/media/video-interfaces.txt Example: diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index 4a3a868235f8..b303703bc7f3 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -19,6 +19,7 @@ vc4-y := \ vc4_plane.o \ vc4_render_cl.o \ vc4_trace_points.o \ + vc4_txp.o \ vc4_v3d.o \ vc4_validate.o \ vc4_validate_shaders.o diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index d222358fa8a7..0e6a121858d1 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -46,6 +46,8 @@ struct vc4_crtc_state { struct drm_crtc_state base; /* Dlist area for this CRTC configuration. */ struct drm_mm_node mm; + bool feed_txp; + bool txp_armed; }; static inline struct vc4_crtc_state * @@ -324,10 +326,8 @@ static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc) return NULL; } -static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) +static void vc4_crtc_config_pv(struct drm_crtc *crtc) { - struct drm_device *dev = crtc->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); @@ -338,12 +338,6 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 || vc4_encoder->type == VC4_ENCODER_TYPE_DSI1); u32 format = is_dsi ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24; - bool debug_dump_regs = false; - - if (debug_dump_regs) { - DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc)); - vc4_crtc_dump_regs(vc4_crtc); - } /* Reset the PV fifo. */ CRTC_WRITE(PV_CONTROL, 0); @@ -419,6 +413,49 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) PV_CONTROL_CLK_SELECT) | PV_CONTROL_FIFO_CLR | PV_CONTROL_EN); +} + +static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; + bool debug_dump_regs = false; + + if (debug_dump_regs) { + DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc)); + vc4_crtc_dump_regs(vc4_crtc); + } + + if (vc4_crtc->channel == 2) { + u32 dispctrl; + u32 dsp3_mux; + + /* + * SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to + * FIFO X'. + * SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'. + * + * DSP3 is connected to FIFO2 unless the transposer is + * enabled. In this case, FIFO 2 is directly accessed by the + * TXP IP, and we need to disable the FIFO2 -> pixelvalve1 + * route. + */ + if (vc4_state->feed_txp) + dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX); + else + dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); + + dispctrl = HVS_READ(SCALER_DISPCTRL) & + ~SCALER_DISPCTRL_DSP3_MUX_MASK; + HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux); + } + + if (!vc4_state->feed_txp) + vc4_crtc_config_pv(crtc); HVS_WRITE(SCALER_DISPBKGNDX(vc4_crtc->channel), SCALER_DISPBKGND_AUTOHS | @@ -499,6 +536,13 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, } } +void vc4_crtc_txp_armed(struct drm_crtc_state *state) +{ + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + + vc4_state->txp_armed = true; +} + static void vc4_crtc_update_dlist(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -514,8 +558,11 @@ static void vc4_crtc_update_dlist(struct drm_crtc *crtc) WARN_ON(drm_crtc_vblank_get(crtc) != 0); spin_lock_irqsave(&dev->event_lock, flags); - vc4_crtc->event = crtc->state->event; - crtc->state->event = NULL; + + if (!vc4_state->feed_txp || vc4_state->txp_armed) { + vc4_crtc->event = crtc->state->event; + crtc->state->event = NULL; + } HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), vc4_state->mm.start); @@ -533,8 +580,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - struct drm_crtc_state *state = crtc->state; - struct drm_display_mode *mode = &state->adjusted_mode; + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; require_hvs_enabled(dev); @@ -546,15 +593,21 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, /* Turn on the scaler, which will wait for vstart to start * compositing. + * When feeding the transposer, we should operate in oneshot + * mode. */ HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) | VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) | - SCALER_DISPCTRLX_ENABLE); + SCALER_DISPCTRLX_ENABLE | + (vc4_state->feed_txp ? SCALER_DISPCTRLX_ONESHOT : 0)); - /* Turn on the pixel valve, which will emit the vstart signal. */ - CRTC_WRITE(PV_V_CONTROL, - CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN); + /* When feeding the transposer block the pixelvalve is unneeded and + * should not be enabled. + */ + if (!vc4_state->feed_txp) + CRTC_WRITE(PV_V_CONTROL, + CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN); } static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc, @@ -579,8 +632,10 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, struct drm_plane *plane; unsigned long flags; const struct drm_plane_state *plane_state; + struct drm_connector *conn; + struct drm_connector_state *conn_state; u32 dlist_count = 0; - int ret; + int ret, i; /* The pixelvalve can only feed one encoder (and encoders are * 1:1 with connectors.) @@ -600,6 +655,24 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, if (ret) return ret; + for_each_new_connector_in_state(state->state, conn, conn_state, i) { + if (conn_state->crtc != crtc) + continue; + + /* The writeback connector is implemented using the transposer + * block which is directly taking its data from the HVS FIFO. + */ + if (conn->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) { + state->no_vblank = true; + vc4_state->feed_txp = true; + } else { + state->no_vblank = false; + vc4_state->feed_txp = false; + } + + break; + } + return 0; } @@ -713,7 +786,8 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc) spin_lock_irqsave(&dev->event_lock, flags); if (vc4_crtc->event && - (vc4_state->mm.start == HVS_READ(SCALER_DISPLACTX(chan)))) { + (vc4_state->mm.start == HVS_READ(SCALER_DISPLACTX(chan)) || + vc4_state->feed_txp)) { drm_crtc_send_vblank_event(crtc, vc4_crtc->event); vc4_crtc->event = NULL; drm_crtc_vblank_put(crtc); @@ -721,6 +795,13 @@ static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc) spin_unlock_irqrestore(&dev->event_lock, flags); } +void vc4_crtc_handle_vblank(struct vc4_crtc *crtc) +{ + crtc->t_vblank = ktime_get(); + drm_crtc_handle_vblank(&crtc->base); + vc4_crtc_handle_page_flip(crtc); +} + static irqreturn_t vc4_crtc_irq_handler(int irq, void *data) { struct vc4_crtc *vc4_crtc = data; @@ -728,10 +809,8 @@ static irqreturn_t vc4_crtc_irq_handler(int irq, void *data) irqreturn_t ret = IRQ_NONE; if (stat & PV_INT_VFP_START) { - vc4_crtc->t_vblank = ktime_get(); CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); - drm_crtc_handle_vblank(&vc4_crtc->base); - vc4_crtc_handle_page_flip(vc4_crtc); + vc4_crtc_handle_vblank(vc4_crtc); ret = IRQ_HANDLED; } @@ -884,12 +963,15 @@ static int vc4_page_flip(struct drm_crtc *crtc, static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) { - struct vc4_crtc_state *vc4_state; + struct vc4_crtc_state *vc4_state, *old_vc4_state; vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL); if (!vc4_state) return NULL; + old_vc4_state = to_vc4_crtc_state(crtc->state); + vc4_state->feed_txp = old_vc4_state->feed_txp; + __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base); return &vc4_state->base; } @@ -987,9 +1069,17 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, struct drm_encoder *encoder; drm_for_each_encoder(encoder, drm) { - struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + struct vc4_encoder *vc4_encoder; int i; + /* HVS FIFO2 can feed the TXP IP. */ + if (crtc_data->hvs_channel == 2 && + encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) { + encoder->possible_crtcs |= drm_crtc_mask(crtc); + continue; + } + + vc4_encoder = to_vc4_encoder(encoder); for (i = 0; i < ARRAY_SIZE(crtc_data->encoder_types); i++) { if (vc4_encoder->type == encoder_types[i]) { vc4_encoder->clock_select = i; diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index 5db06bdb5f27..7a0003de71ab 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -21,6 +21,7 @@ static const struct drm_info_list vc4_debugfs_list[] = { {"dsi1_regs", vc4_dsi_debugfs_regs, 0, (void *)(uintptr_t)1}, {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, {"vec_regs", vc4_vec_debugfs_regs, 0}, + {"txp_regs", vc4_txp_debugfs_regs, 0}, {"hvs_regs", vc4_hvs_debugfs_regs, 0}, {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 466d0a27b415..e42fd5ec41cc 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -344,6 +344,7 @@ static struct platform_driver *const component_drivers[] = { &vc4_vec_driver, &vc4_dpi_driver, &vc4_dsi_driver, + &vc4_txp_driver, &vc4_hvs_driver, &vc4_crtc_driver, &vc4_v3d_driver, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index eace76c621a1..bd6ef1f31822 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -73,6 +73,7 @@ struct vc4_dev { struct vc4_dpi *dpi; struct vc4_dsi *dsi1; struct vc4_vec *vec; + struct vc4_txp *txp; struct vc4_hang_state *hang_state; @@ -698,6 +699,8 @@ bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, bool in_vblank_irq, int *vpos, int *hpos, ktime_t *stime, ktime_t *etime, const struct drm_display_mode *mode); +void vc4_crtc_handle_vblank(struct vc4_crtc *crtc); +void vc4_crtc_txp_armed(struct drm_crtc_state *state); /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); @@ -745,6 +748,10 @@ int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); extern struct platform_driver vc4_vec_driver; int vc4_vec_debugfs_regs(struct seq_file *m, void *unused); +/* vc4_txp.c */ +extern struct platform_driver vc4_txp_driver; +int vc4_txp_debugfs_regs(struct seq_file *m, void *unused); + /* vc4_irq.c */ irqreturn_t vc4_irq(int irq, void *arg); void vc4_irq_preinstall(struct drm_device *dev); diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c new file mode 100644 index 000000000000..6e23c50168f9 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright © 2018 Broadcom + * + * Authors: + * Eric Anholt + * Boris Brezillon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vc4_drv.h" +#include "vc4_regs.h" + +/* Base address of the output. Raster formats must be 4-byte aligned, + * T and LT must be 16-byte aligned or maybe utile-aligned (docs are + * inconsistent, but probably utile). + */ +#define TXP_DST_PTR 0x00 + +/* Pitch in bytes for raster images, 16-byte aligned. For tiled, it's + * the width in tiles. + */ +#define TXP_DST_PITCH 0x04 +/* For T-tiled imgaes, DST_PITCH should be the number of tiles wide, + * shifted up. + */ +# define TXP_T_TILE_WIDTH_SHIFT 7 +/* For LT-tiled images, DST_PITCH should be the number of utiles wide, + * shifted up. + */ +# define TXP_LT_TILE_WIDTH_SHIFT 4 + +/* Pre-rotation width/height of the image. Must match HVS config. + * + * If TFORMAT and 32-bit, limit is 1920 for 32-bit and 3840 to 16-bit + * and width/height must be tile or utile-aligned as appropriate. If + * transposing (rotating), width is limited to 1920. + * + * Height is limited to various numbers between 4088 and 4095. I'd + * just use 4088 to be safe. + */ +#define TXP_DIM 0x08 +# define TXP_HEIGHT_SHIFT 16 +# define TXP_HEIGHT_MASK GENMASK(31, 16) +# define TXP_WIDTH_SHIFT 0 +# define TXP_WIDTH_MASK GENMASK(15, 0) + +#define TXP_DST_CTRL 0x0c +/* These bits are set to 0x54 */ +#define TXP_PILOT_SHIFT 24 +#define TXP_PILOT_MASK GENMASK(31, 24) +/* Bits 22-23 are set to 0x01 */ +#define TXP_VERSION_SHIFT 22 +#define TXP_VERSION_MASK GENMASK(23, 22) + +/* Powers down the internal memory. */ +# define TXP_POWERDOWN BIT(21) + +/* Enables storing the alpha component in 8888/4444, instead of + * filling with ~ALPHA_INVERT. + */ +# define TXP_ALPHA_ENABLE BIT(20) + +/* 4 bits, each enables stores for a channel in each set of 4 bytes. + * Set to 0xf for normal operation. + */ +# define TXP_BYTE_ENABLE_SHIFT 16 +# define TXP_BYTE_ENABLE_MASK GENMASK(19, 16) + +/* Debug: Generate VSTART again at EOF. */ +# define TXP_VSTART_AT_EOF BIT(15) + +/* Debug: Terminate the current frame immediately. Stops AXI + * writes. + */ +# define TXP_ABORT BIT(14) + +# define TXP_DITHER BIT(13) + +/* Inverts alpha if TXP_ALPHA_ENABLE, chooses fill value for + * !TXP_ALPHA_ENABLE. + */ +# define TXP_ALPHA_INVERT BIT(12) + +/* Note: I've listed the channels here in high bit (in byte 3/2/1) to + * low bit (in byte 0) order. + */ +# define TXP_FORMAT_SHIFT 8 +# define TXP_FORMAT_MASK GENMASK(11, 8) +# define TXP_FORMAT_ABGR4444 0 +# define TXP_FORMAT_ARGB4444 1 +# define TXP_FORMAT_BGRA4444 2 +# define TXP_FORMAT_RGBA4444 3 +# define TXP_FORMAT_BGR565 6 +# define TXP_FORMAT_RGB565 7 +/* 888s are non-rotated, raster-only */ +# define TXP_FORMAT_BGR888 8 +# define TXP_FORMAT_RGB888 9 +# define TXP_FORMAT_ABGR8888 12 +# define TXP_FORMAT_ARGB8888 13 +# define TXP_FORMAT_BGRA8888 14 +# define TXP_FORMAT_RGBA8888 15 + +/* If TFORMAT is set, generates LT instead of T format. */ +# define TXP_LINEAR_UTILE BIT(7) + +/* Rotate output by 90 degrees. */ +# define TXP_TRANSPOSE BIT(6) + +/* Generate a tiled format for V3D. */ +# define TXP_TFORMAT BIT(5) + +/* Generates some undefined test mode output. */ +# define TXP_TEST_MODE BIT(4) + +/* Request odd field from HVS. */ +# define TXP_FIELD BIT(3) + +/* Raise interrupt when idle. */ +# define TXP_EI BIT(2) + +/* Set when generating a frame, clears when idle. */ +# define TXP_BUSY BIT(1) + +/* Starts a frame. Self-clearing. */ +# define TXP_GO BIT(0) + +/* Number of lines received and committed to memory. */ +#define TXP_PROGRESS 0x10 + +#define TXP_READ(offset) readl(txp->regs + (offset)) +#define TXP_WRITE(offset, val) writel(val, txp->regs + (offset)) + +struct vc4_txp { + struct platform_device *pdev; + + struct drm_writeback_connector connector; + + void __iomem *regs; +}; + +static inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder) +{ + return container_of(encoder, struct vc4_txp, connector.encoder); +} + +static inline struct vc4_txp *connector_to_vc4_txp(struct drm_connector *conn) +{ + return container_of(conn, struct vc4_txp, connector.base); +} + +#define TXP_REG(reg) { reg, #reg } +static const struct { + u32 reg; + const char *name; +} txp_regs[] = { + TXP_REG(TXP_DST_PTR), + TXP_REG(TXP_DST_PITCH), + TXP_REG(TXP_DIM), + TXP_REG(TXP_DST_CTRL), + TXP_REG(TXP_PROGRESS), +}; + +#ifdef CONFIG_DEBUG_FS +int vc4_txp_debugfs_regs(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *dev = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_txp *txp = vc4->txp; + int i; + + if (!txp) + return 0; + + for (i = 0; i < ARRAY_SIZE(txp_regs); i++) { + seq_printf(m, "%s (0x%04x): 0x%08x\n", + txp_regs[i].name, txp_regs[i].reg, + TXP_READ(txp_regs[i].reg)); + } + + return 0; +} +#endif + +static int vc4_txp_connector_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + + return drm_add_modes_noedid(connector, dev->mode_config.max_width, + dev->mode_config.max_height); +} + +static enum drm_mode_status +vc4_txp_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + int w = mode->hdisplay, h = mode->vdisplay; + + if (w < mode_config->min_width || w > mode_config->max_width) + return MODE_BAD_HVALUE; + + if (h < mode_config->min_height || h > mode_config->max_height) + return MODE_BAD_VVALUE; + + return MODE_OK; +} + +static const u32 drm_fmts[] = { + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, +}; + +static const u32 txp_fmts[] = { + TXP_FORMAT_RGB888, + TXP_FORMAT_BGR888, + TXP_FORMAT_ARGB8888, + TXP_FORMAT_ABGR8888, + TXP_FORMAT_ARGB8888, + TXP_FORMAT_ABGR8888, + TXP_FORMAT_RGBA8888, + TXP_FORMAT_BGRA8888, + TXP_FORMAT_RGBA8888, + TXP_FORMAT_BGRA8888, +}; + +static int vc4_txp_connector_atomic_check(struct drm_connector *conn, + struct drm_connector_state *conn_state) +{ + struct drm_crtc_state *crtc_state; + struct drm_gem_cma_object *gem; + struct drm_framebuffer *fb; + int i; + + if (!conn_state->writeback_job || !conn_state->writeback_job->fb) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(conn_state->state, + conn_state->crtc); + + fb = conn_state->writeback_job->fb; + if (fb->width != crtc_state->mode.hdisplay || + fb->height != crtc_state->mode.vdisplay) { + DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n", + fb->width, fb->height); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) { + if (fb->format->format == drm_fmts[i]) + break; + } + + if (i == ARRAY_SIZE(drm_fmts)) + return -EINVAL; + + gem = drm_fb_cma_get_gem_obj(fb, 0); + + /* Pitch must be aligned on 16 bytes. */ + if (fb->pitches[0] & GENMASK(3, 0)) + return -EINVAL; + + vc4_crtc_txp_armed(crtc_state); + + return 0; +} + +static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, + struct drm_connector_state *conn_state) +{ + struct vc4_txp *txp = connector_to_vc4_txp(conn); + struct drm_gem_cma_object *gem; + struct drm_display_mode *mode; + struct drm_framebuffer *fb; + u32 ctrl; + int i; + + if (WARN_ON(!conn_state->writeback_job || + !conn_state->writeback_job->fb)) + return; + + mode = &conn_state->crtc->state->adjusted_mode; + fb = conn_state->writeback_job->fb; + + for (i = 0; i < ARRAY_SIZE(drm_fmts); i++) { + if (fb->format->format == drm_fmts[i]) + break; + } + + if (WARN_ON(i == ARRAY_SIZE(drm_fmts))) + return; + + ctrl = TXP_GO | TXP_VSTART_AT_EOF | TXP_EI | + VC4_SET_FIELD(0xf, TXP_BYTE_ENABLE) | + VC4_SET_FIELD(txp_fmts[i], TXP_FORMAT); + + if (fb->format->has_alpha) + ctrl |= TXP_ALPHA_ENABLE; + + gem = drm_fb_cma_get_gem_obj(fb, 0); + TXP_WRITE(TXP_DST_PTR, gem->paddr + fb->offsets[0]); + TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]); + TXP_WRITE(TXP_DIM, + VC4_SET_FIELD(mode->hdisplay, TXP_WIDTH) | + VC4_SET_FIELD(mode->vdisplay, TXP_HEIGHT)); + + TXP_WRITE(TXP_DST_CTRL, ctrl); + + drm_writeback_queue_job(&txp->connector, conn_state->writeback_job); +} + +static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = { + .get_modes = vc4_txp_connector_get_modes, + .mode_valid = vc4_txp_connector_mode_valid, + .atomic_check = vc4_txp_connector_atomic_check, + .atomic_commit = vc4_txp_connector_atomic_commit, +}; + +static enum drm_connector_status +vc4_txp_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void vc4_txp_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs vc4_txp_connector_funcs = { + .detect = vc4_txp_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vc4_txp_connector_destroy, + .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 void vc4_txp_encoder_disable(struct drm_encoder *encoder) +{ + struct vc4_txp *txp = encoder_to_vc4_txp(encoder); + + if (TXP_READ(TXP_DST_CTRL) & TXP_BUSY) { + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + TXP_WRITE(TXP_DST_CTRL, TXP_ABORT); + + while (TXP_READ(TXP_DST_CTRL) & TXP_BUSY && + time_before(jiffies, timeout)) + ; + + WARN_ON(TXP_READ(TXP_DST_CTRL) & TXP_BUSY); + } + + TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN); +} + +static const struct drm_encoder_helper_funcs vc4_txp_encoder_helper_funcs = { + .disable = vc4_txp_encoder_disable, +}; + +static irqreturn_t vc4_txp_interrupt(int irq, void *data) +{ + struct vc4_txp *txp = data; + + TXP_WRITE(TXP_DST_CTRL, TXP_READ(TXP_DST_CTRL) & ~TXP_EI); + vc4_crtc_handle_vblank(to_vc4_crtc(txp->connector.base.state->crtc)); + drm_writeback_signal_completion(&txp->connector, 0); + + return IRQ_HANDLED; +} + +static int vc4_txp_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_txp *txp; + int ret, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + txp = devm_kzalloc(dev, sizeof(*txp), GFP_KERNEL); + if (!txp) + return -ENOMEM; + + txp->pdev = pdev; + + txp->regs = vc4_ioremap_regs(pdev, 0); + if (IS_ERR(txp->regs)) + return PTR_ERR(txp->regs); + + drm_connector_helper_add(&txp->connector.base, + &vc4_txp_connector_helper_funcs); + ret = drm_writeback_connector_init(drm, &txp->connector, + &vc4_txp_connector_funcs, + &vc4_txp_encoder_helper_funcs, + drm_fmts, ARRAY_SIZE(drm_fmts)); + if (ret) + return ret; + + ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0, + dev_name(dev), txp); + if (ret) + return ret; + + dev_set_drvdata(dev, txp); + vc4->txp = txp; + + return 0; +} + +static void vc4_txp_unbind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_txp *txp = dev_get_drvdata(dev); + + vc4_txp_connector_destroy(&txp->connector.base); + + vc4->txp = NULL; +} + +static const struct component_ops vc4_txp_ops = { + .bind = vc4_txp_bind, + .unbind = vc4_txp_unbind, +}; + +static int vc4_txp_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vc4_txp_ops); +} + +static int vc4_txp_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vc4_txp_ops); + return 0; +} + +static const struct of_device_id vc4_txp_dt_match[] = { + { .compatible = "brcm,bcm2835-txp" }, + { /* sentinel */ }, +}; + +struct platform_driver vc4_txp_driver = { + .probe = vc4_txp_probe, + .remove = vc4_txp_remove, + .driver = { + .name = "vc4_txp", + .of_match_table = vc4_txp_dt_match, + }, +}; -- cgit v1.2.3-55-g7522 From 5ba57babcb40227ceb70d9bc71afa57e004f8417 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 9 Jul 2018 17:48:18 +0200 Subject: drm: vkms: select DRM_KMS_HELPER Without this, we get link errors during randconfig build: drivers/gpu/drm/vkms/vkms_drv.o:(.rodata+0xa0): undefined reference to `drm_atomic_helper_check' drivers/gpu/drm/vkms/vkms_drv.o:(.rodata+0xa8): undefined reference to `drm_atomic_helper_commit' drivers/gpu/drm/vkms/vkms_plane.o:(.rodata+0x0): undefined reference to `drm_atomic_helper_update_plane' drivers/gpu/drm/vkms/vkms_plane.o:(.rodata+0x8): undefined reference to `drm_atomic_helper_disable_plane' drivers/gpu/drm/vkms/vkms_plane.o:(.rodata+0x18): undefined reference to `drm_atomic_helper_plane_reset' drivers/gpu/drm/vkms/vkms_plane.o:(.rodata+0x28): undefined reference to `drm_atomic_helper_plane_duplicate_state' drivers/gpu/drm/vkms/vkms_plane.o:(.rodata+0x30): undefined reference to `drm_atomic_helper_plane_destroy_state' drivers/gpu/drm/vkms/vkms_output.o:(.rodata+0x1c0): undefined reference to `drm_helper_probe_single_connector_modes' drivers/gpu/drm/vkms/vkms_crtc.o:(.rodata+0x40): undefined reference to `drm_atomic_helper_crtc_reset' drivers/gpu/drm/vkms/vkms_crtc.o:(.rodata+0x70): undefined reference to `drm_atomic_helper_set_config' drivers/gpu/drm/vkms/vkms_crtc.o:(.rodata+0x78): undefined reference to `drm_atomic_helper_page_flip' drivers/gpu/drm/vkms/vkms_crtc.o:(.rodata+0x90): undefined reference to `drm_atomic_helper_crtc_duplicate_state' drivers/gpu/drm/vkms/vkms_crtc.o:(.rodata+0x98): undefined reference to `drm_atomic_helper_crtc_destroy_state' Fixes: 854502fa0a38 ("drm/vkms: Add basic CRTC initialization") Fixes: 1c7c5fd916a0 ("drm/vkms: Introduce basic VKMS driver") Signed-off-by: Arnd Bergmann Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180709154901.1989316-1-arnd@arndb.de --- drivers/gpu/drm/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 10f9f01123ea..a8054dde49b5 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -216,6 +216,7 @@ config DRM_VGEM config DRM_VKMS tristate "Virtual KMS (EXPERIMENTAL)" depends on DRM + select DRM_KMS_HELPER default n help Virtual Kernel Mode-Setting (VKMS) is used for testing or for -- cgit v1.2.3-55-g7522 From c76f0f7cb546b661b5e0ac769850a5c854927f65 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Tue, 3 Jul 2018 18:03:47 +0200 Subject: drm: Begin an API for in-kernel clients This the beginning of an API for in-kernel clients. First out is a way to get a framebuffer backed by a dumb buffer. Only GEM drivers are supported. The original idea of using an exported dma-buf was dropped because it also creates an anonomous file descriptor which doesn't work when the buffer is created from a kernel thread. The easy way out is to use drm_driver.gem_prime_vmap to get the virtual address, which requires a GEM object. This excludes the vmwgfx driver which is the only non-GEM driver apart from the legacy ones. A solution for vmwgfx will have to be worked out later if it wants to support the client API which it probably will when we have a bootsplash client. Suggested-by: Daniel Vetter Signed-off-by: Noralf Trønnes Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703160354.59955-2-noralf@tronnes.org --- Documentation/gpu/drm-client.rst | 12 ++ Documentation/gpu/index.rst | 1 + drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_client.c | 387 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_drv.c | 8 + drivers/gpu/drm/drm_file.c | 3 + drivers/gpu/drm/drm_probe_helper.c | 3 + include/drm/drm_client.h | 136 +++++++++++++ include/drm/drm_device.h | 21 ++ 9 files changed, 572 insertions(+), 1 deletion(-) create mode 100644 Documentation/gpu/drm-client.rst create mode 100644 drivers/gpu/drm/drm_client.c create mode 100644 include/drm/drm_client.h (limited to 'drivers/gpu/drm') diff --git a/Documentation/gpu/drm-client.rst b/Documentation/gpu/drm-client.rst new file mode 100644 index 000000000000..7e672063e7eb --- /dev/null +++ b/Documentation/gpu/drm-client.rst @@ -0,0 +1,12 @@ +================= +Kernel clients +================= + +.. kernel-doc:: drivers/gpu/drm/drm_client.c + :doc: overview + +.. kernel-doc:: include/drm/drm_client.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_client.c + :export: diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst index 00288f34c5a6..1fcf8e851e15 100644 --- a/Documentation/gpu/index.rst +++ b/Documentation/gpu/index.rst @@ -10,6 +10,7 @@ Linux GPU Driver Developer's Guide drm-kms drm-kms-helpers drm-uapi + drm-client drivers vga-switcheroo vgaarbiter diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 6ae535ca0914..0e0a3ef1abad 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -18,7 +18,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_encoder.o drm_mode_object.o drm_property.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ - drm_syncobj.o drm_lease.o drm_writeback.o + drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o drm-$(CONFIG_DRM_VM) += drm_vm.o diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c new file mode 100644 index 000000000000..743495f7f833 --- /dev/null +++ b/drivers/gpu/drm/drm_client.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Noralf Trønnes + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drm_crtc_internal.h" +#include "drm_internal.h" + +/** + * DOC: overview + * + * This library provides support for clients running in the kernel like fbdev and bootsplash. + * Currently it's only partially implemented, just enough to support fbdev. + * + * GEM drivers which provide a GEM based dumb buffer with a virtual address are supported. + */ + +static int drm_client_open(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + struct drm_file *file; + + file = drm_file_alloc(dev->primary); + if (IS_ERR(file)) + return PTR_ERR(file); + + mutex_lock(&dev->filelist_mutex); + list_add(&file->lhead, &dev->filelist_internal); + mutex_unlock(&dev->filelist_mutex); + + client->file = file; + + return 0; +} + +static void drm_client_close(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + + mutex_lock(&dev->filelist_mutex); + list_del(&client->file->lhead); + mutex_unlock(&dev->filelist_mutex); + + drm_file_free(client->file); +} +EXPORT_SYMBOL(drm_client_close); + +/** + * drm_client_new - Create a DRM client + * @dev: DRM device + * @client: DRM client + * @name: Client name + * @funcs: DRM client functions (optional) + * + * The caller needs to hold a reference on @dev before calling this function. + * The client is freed when the &drm_device is unregistered. See drm_client_release(). + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_client_new(struct drm_device *dev, struct drm_client_dev *client, + const char *name, const struct drm_client_funcs *funcs) +{ + bool registered; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET) || + !dev->driver->dumb_create || !dev->driver->gem_prime_vmap) + return -ENOTSUPP; + + if (funcs && !try_module_get(funcs->owner)) + return -ENODEV; + + client->dev = dev; + client->name = name; + client->funcs = funcs; + + ret = drm_client_open(client); + if (ret) + goto err_put_module; + + mutex_lock(&dev->clientlist_mutex); + registered = dev->registered; + if (registered) + list_add(&client->list, &dev->clientlist); + mutex_unlock(&dev->clientlist_mutex); + if (!registered) { + ret = -ENODEV; + goto err_close; + } + + drm_dev_get(dev); + + return 0; + +err_close: + drm_client_close(client); +err_put_module: + if (funcs) + module_put(funcs->owner); + + return ret; +} +EXPORT_SYMBOL(drm_client_new); + +/** + * drm_client_release - Release DRM client resources + * @client: DRM client + * + * Releases resources by closing the &drm_file that was opened by drm_client_new(). + * It is called automatically if the &drm_client_funcs.unregister callback is _not_ set. + * + * This function should only be called from the unregister callback. An exception + * is fbdev which cannot free the buffer if userspace has open file descriptors. + * + * Note: + * Clients cannot initiate a release by themselves. This is done to keep the code simple. + * The driver has to be unloaded before the client can be unloaded. + */ +void drm_client_release(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + + DRM_DEV_DEBUG_KMS(dev->dev, "%s\n", client->name); + + drm_client_close(client); + drm_dev_put(dev); + if (client->funcs) + module_put(client->funcs->owner); +} +EXPORT_SYMBOL(drm_client_release); + +void drm_client_dev_unregister(struct drm_device *dev) +{ + struct drm_client_dev *client, *tmp; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry_safe(client, tmp, &dev->clientlist, list) { + list_del(&client->list); + if (client->funcs && client->funcs->unregister) { + client->funcs->unregister(client); + } else { + drm_client_release(client); + kfree(client); + } + } + mutex_unlock(&dev->clientlist_mutex); +} + +/** + * drm_client_dev_hotplug - Send hotplug event to clients + * @dev: DRM device + * + * This function calls the &drm_client_funcs.hotplug callback on the attached clients. + * + * drm_kms_helper_hotplug_event() calls this function, so drivers that use it + * don't need to call this function themselves. + */ +void drm_client_dev_hotplug(struct drm_device *dev) +{ + struct drm_client_dev *client; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) { + if (!client->funcs || !client->funcs->hotplug) + continue; + + ret = client->funcs->hotplug(client); + DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret); + } + mutex_unlock(&dev->clientlist_mutex); +} +EXPORT_SYMBOL(drm_client_dev_hotplug); + +void drm_client_dev_restore(struct drm_device *dev) +{ + struct drm_client_dev *client; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) { + if (!client->funcs || !client->funcs->restore) + continue; + + ret = client->funcs->restore(client); + DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret); + if (!ret) /* The first one to return zero gets the privilege to restore */ + break; + } + mutex_unlock(&dev->clientlist_mutex); +} + +static void drm_client_buffer_delete(struct drm_client_buffer *buffer) +{ + struct drm_device *dev = buffer->client->dev; + + if (buffer->vaddr && dev->driver->gem_prime_vunmap) + dev->driver->gem_prime_vunmap(buffer->gem, buffer->vaddr); + + if (buffer->gem) + drm_gem_object_put_unlocked(buffer->gem); + + drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file); + kfree(buffer); +} + +static struct drm_client_buffer * +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format) +{ + struct drm_mode_create_dumb dumb_args = { }; + struct drm_device *dev = client->dev; + struct drm_client_buffer *buffer; + struct drm_gem_object *obj; + void *vaddr; + int ret; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return ERR_PTR(-ENOMEM); + + buffer->client = client; + + dumb_args.width = width; + dumb_args.height = height; + dumb_args.bpp = drm_format_plane_cpp(format, 0) * 8; + ret = drm_mode_create_dumb(dev, &dumb_args, client->file); + if (ret) + goto err_free; + + buffer->handle = dumb_args.handle; + buffer->pitch = dumb_args.pitch; + + obj = drm_gem_object_lookup(client->file, dumb_args.handle); + if (!obj) { + ret = -ENOENT; + goto err_delete; + } + + buffer->gem = obj; + + /* + * FIXME: The dependency on GEM here isn't required, we could + * convert the driver handle to a dma-buf instead and use the + * backend-agnostic dma-buf vmap support instead. This would + * require that the handle2fd prime ioctl is reworked to pull the + * fd_install step out of the driver backend hooks, to make that + * final step optional for internal users. + */ + vaddr = dev->driver->gem_prime_vmap(obj); + if (!vaddr) { + ret = -ENOMEM; + goto err_delete; + } + + buffer->vaddr = vaddr; + + return buffer; + +err_delete: + drm_client_buffer_delete(buffer); +err_free: + kfree(buffer); + + return ERR_PTR(ret); +} + +static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer) +{ + int ret; + + if (!buffer->fb) + return; + + ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file); + if (ret) + DRM_DEV_ERROR(buffer->client->dev->dev, + "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret); + + buffer->fb = NULL; +} + +static int drm_client_buffer_addfb(struct drm_client_buffer *buffer, + u32 width, u32 height, u32 format) +{ + struct drm_client_dev *client = buffer->client; + struct drm_mode_fb_cmd fb_req = { }; + const struct drm_format_info *info; + int ret; + + info = drm_format_info(format); + fb_req.bpp = info->cpp[0] * 8; + fb_req.depth = info->depth; + fb_req.width = width; + fb_req.height = height; + fb_req.handle = buffer->handle; + fb_req.pitch = buffer->pitch; + + ret = drm_mode_addfb(client->dev, &fb_req, client->file); + if (ret) + return ret; + + buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id); + if (WARN_ON(!buffer->fb)) + return -ENOENT; + + /* drop the reference we picked up in framebuffer lookup */ + drm_framebuffer_put(buffer->fb); + + strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN); + + return 0; +} + +/** + * drm_client_framebuffer_create - Create a client framebuffer + * @client: DRM client + * @width: Framebuffer width + * @height: Framebuffer height + * @format: Buffer format + * + * This function creates a &drm_client_buffer which consists of a + * &drm_framebuffer backed by a dumb buffer. + * Call drm_client_framebuffer_delete() to free the buffer. + * + * Returns: + * Pointer to a client buffer or an error pointer on failure. + */ +struct drm_client_buffer * +drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format) +{ + struct drm_client_buffer *buffer; + int ret; + + buffer = drm_client_buffer_create(client, width, height, format); + if (IS_ERR(buffer)) + return buffer; + + ret = drm_client_buffer_addfb(buffer, width, height, format); + if (ret) { + drm_client_buffer_delete(buffer); + return ERR_PTR(ret); + } + + return buffer; +} +EXPORT_SYMBOL(drm_client_framebuffer_create); + +/** + * drm_client_framebuffer_delete - Delete a client framebuffer + * @buffer: DRM client buffer (can be NULL) + */ +void drm_client_framebuffer_delete(struct drm_client_buffer *buffer) +{ + if (!buffer) + return; + + drm_client_buffer_rmfb(buffer); + drm_client_buffer_delete(buffer); +} +EXPORT_SYMBOL(drm_client_framebuffer_delete); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 7af748ed1c58..6eb935bb2f92 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -505,6 +506,8 @@ int drm_dev_init(struct drm_device *dev, dev->driver = driver; INIT_LIST_HEAD(&dev->filelist); + INIT_LIST_HEAD(&dev->filelist_internal); + INIT_LIST_HEAD(&dev->clientlist); INIT_LIST_HEAD(&dev->ctxlist); INIT_LIST_HEAD(&dev->vmalist); INIT_LIST_HEAD(&dev->maplist); @@ -514,6 +517,7 @@ int drm_dev_init(struct drm_device *dev, spin_lock_init(&dev->event_lock); mutex_init(&dev->struct_mutex); mutex_init(&dev->filelist_mutex); + mutex_init(&dev->clientlist_mutex); mutex_init(&dev->ctxlist_mutex); mutex_init(&dev->master_mutex); @@ -569,6 +573,7 @@ err_minors: err_free: mutex_destroy(&dev->master_mutex); mutex_destroy(&dev->ctxlist_mutex); + mutex_destroy(&dev->clientlist_mutex); mutex_destroy(&dev->filelist_mutex); mutex_destroy(&dev->struct_mutex); return ret; @@ -603,6 +608,7 @@ void drm_dev_fini(struct drm_device *dev) mutex_destroy(&dev->master_mutex); mutex_destroy(&dev->ctxlist_mutex); + mutex_destroy(&dev->clientlist_mutex); mutex_destroy(&dev->filelist_mutex); mutex_destroy(&dev->struct_mutex); kfree(dev->unique); @@ -858,6 +864,8 @@ void drm_dev_unregister(struct drm_device *dev) dev->registered = false; + drm_client_dev_unregister(dev); + if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_modeset_unregister_all(dev); diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 66bb403dc8ab..ffa8dc35515f 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -444,6 +445,8 @@ void drm_lastclose(struct drm_device * dev) if (drm_core_check_feature(dev, DRIVER_LEGACY)) drm_legacy_dev_reinit(dev); + + drm_client_dev_restore(dev); } /** diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 1a901fe9e23e..34fe2704a31c 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -559,6 +560,8 @@ void drm_kms_helper_hotplug_event(struct drm_device *dev) drm_sysfs_hotplug_event(dev); if (dev->mode_config.funcs->output_poll_changed) dev->mode_config.funcs->output_poll_changed(dev); + + drm_client_dev_hotplug(dev); } EXPORT_SYMBOL(drm_kms_helper_hotplug_event); diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h new file mode 100644 index 000000000000..671052d80e38 --- /dev/null +++ b/include/drm/drm_client.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _DRM_CLIENT_H_ +#define _DRM_CLIENT_H_ + +#include + +struct drm_client_dev; +struct drm_device; +struct drm_file; +struct drm_framebuffer; +struct drm_gem_object; +struct module; + +/** + * struct drm_client_funcs - DRM client callbacks + */ +struct drm_client_funcs { + /** + * @owner: The module owner + */ + struct module *owner; + + /** + * @unregister: + * + * Called when &drm_device is unregistered. The client should respond by + * releasing it's resources using drm_client_release(). + * + * This callback is optional. + */ + void (*unregister)(struct drm_client_dev *client); + + /** + * @restore: + * + * Called on drm_lastclose(). The first client instance in the list that + * returns zero gets the privilege to restore and no more clients are + * called. This callback is not called after @unregister has been called. + * + * This callback is optional. + */ + int (*restore)(struct drm_client_dev *client); + + /** + * @hotplug: + * + * Called on drm_kms_helper_hotplug_event(). + * This callback is not called after @unregister has been called. + * + * This callback is optional. + */ + int (*hotplug)(struct drm_client_dev *client); +}; + +/** + * struct drm_client_dev - DRM client instance + */ +struct drm_client_dev { + /** + * @dev: DRM device + */ + struct drm_device *dev; + + /** + * @name: Name of the client. + */ + const char *name; + + /** + * @list: + * + * List of all clients of a DRM device, linked into + * &drm_device.clientlist. Protected by &drm_device.clientlist_mutex. + */ + struct list_head list; + + /** + * @funcs: DRM client functions (optional) + */ + const struct drm_client_funcs *funcs; + + /** + * @file: DRM file + */ + struct drm_file *file; +}; + +int drm_client_new(struct drm_device *dev, struct drm_client_dev *client, + const char *name, const struct drm_client_funcs *funcs); +void drm_client_release(struct drm_client_dev *client); + +void drm_client_dev_unregister(struct drm_device *dev); +void drm_client_dev_hotplug(struct drm_device *dev); +void drm_client_dev_restore(struct drm_device *dev); + +/** + * struct drm_client_buffer - DRM client buffer + */ +struct drm_client_buffer { + /** + * @client: DRM client + */ + struct drm_client_dev *client; + + /** + * @handle: Buffer handle + */ + u32 handle; + + /** + * @pitch: Buffer pitch + */ + u32 pitch; + + /** + * @gem: GEM object backing this buffer + */ + struct drm_gem_object *gem; + + /** + * @vaddr: Virtual address for the buffer + */ + void *vaddr; + + /** + * @fb: DRM framebuffer + */ + struct drm_framebuffer *fb; +}; + +struct drm_client_buffer * +drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format); +void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); + +#endif diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 858ba19a3e29..f9c6e0e3aec7 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -74,6 +74,27 @@ struct drm_device { struct mutex filelist_mutex; struct list_head filelist; + /** + * @filelist_internal: + * + * List of open DRM files for in-kernel clients. Protected by @filelist_mutex. + */ + struct list_head filelist_internal; + + /** + * @clientlist_mutex: + * + * Protects @clientlist access. + */ + struct mutex clientlist_mutex; + + /** + * @clientlist: + * + * List of in-kernel clients. Protected by @clientlist_mutex. + */ + struct list_head clientlist; + /** \name Memory management */ /*@{ */ struct list_head maplist; /**< Linked list of regions */ -- cgit v1.2.3-55-g7522 From d536540f304ce0ee35db9f67f7d880bc09f3743f Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Tue, 3 Jul 2018 18:03:48 +0200 Subject: drm/fb-helper: Add generic fbdev emulation .fb_probe function This is the first step in getting generic fbdev emulation. A drm_fb_helper_funcs.fb_probe function is added which uses the DRM client API to get a framebuffer backed by a dumb buffer. Signed-off-by: Noralf Trønnes Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703160354.59955-3-noralf@tronnes.org --- drivers/gpu/drm/drm_fb_helper.c | 199 +++++++++++++++++++++++++++++++++++++++- include/drm/drm_fb_helper.h | 31 +++++++ 2 files changed, 229 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d697c1c4a067..7e29d5340a17 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -30,6 +30,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -738,6 +739,24 @@ static void drm_fb_helper_resume_worker(struct work_struct *work) console_unlock(); } +static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper, + struct drm_clip_rect *clip) +{ + struct drm_framebuffer *fb = fb_helper->fb; + unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); + size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp; + void *src = fb_helper->fbdev->screen_buffer + offset; + void *dst = fb_helper->buffer->vaddr + offset; + size_t len = (clip->x2 - clip->x1) * cpp; + unsigned int y; + + for (y = clip->y1; y < clip->y2; y++) { + memcpy(dst, src, len); + src += fb->pitches[0]; + dst += fb->pitches[0]; + } +} + static void drm_fb_helper_dirty_work(struct work_struct *work) { struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper, @@ -753,8 +772,12 @@ static void drm_fb_helper_dirty_work(struct work_struct *work) spin_unlock_irqrestore(&helper->dirty_lock, flags); /* call dirty callback only when it has been really touched */ - if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) + if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) { + /* Generic fbdev uses a shadow buffer */ + if (helper->buffer) + drm_fb_helper_dirty_blit_real(helper, &clip_copy); helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1); + } } /** @@ -2921,6 +2944,180 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev) } EXPORT_SYMBOL(drm_fb_helper_output_poll_changed); +/* @user: 1=userspace, 0=fbcon */ +static int drm_fbdev_fb_open(struct fb_info *info, int user) +{ + struct drm_fb_helper *fb_helper = info->par; + + if (!try_module_get(fb_helper->dev->driver->fops->owner)) + return -ENODEV; + + return 0; +} + +static int drm_fbdev_fb_release(struct fb_info *info, int user) +{ + struct drm_fb_helper *fb_helper = info->par; + + module_put(fb_helper->dev->driver->fops->owner); + + return 0; +} + +/* + * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of + * unregister_framebuffer() or fb_release(). + */ +static void drm_fbdev_fb_destroy(struct fb_info *info) +{ + struct drm_fb_helper *fb_helper = info->par; + struct fb_info *fbi = fb_helper->fbdev; + struct fb_ops *fbops = NULL; + void *shadow = NULL; + + if (fbi->fbdefio) { + fb_deferred_io_cleanup(fbi); + shadow = fbi->screen_buffer; + fbops = fbi->fbops; + } + + drm_fb_helper_fini(fb_helper); + + if (shadow) { + vfree(shadow); + kfree(fbops); + } + + drm_client_framebuffer_delete(fb_helper->buffer); + /* + * FIXME: + * Remove conditional when all CMA drivers have been moved over to using + * drm_fbdev_generic_setup(). + */ + if (fb_helper->client.funcs) { + drm_client_release(&fb_helper->client); + kfree(fb_helper); + } +} + +static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct drm_fb_helper *fb_helper = info->par; + + if (fb_helper->dev->driver->gem_prime_mmap) + return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma); + else + return -ENODEV; +} + +static struct fb_ops drm_fbdev_fb_ops = { + .owner = THIS_MODULE, + DRM_FB_HELPER_DEFAULT_OPS, + .fb_open = drm_fbdev_fb_open, + .fb_release = drm_fbdev_fb_release, + .fb_destroy = drm_fbdev_fb_destroy, + .fb_mmap = drm_fbdev_fb_mmap, + .fb_read = drm_fb_helper_sys_read, + .fb_write = drm_fb_helper_sys_write, + .fb_fillrect = drm_fb_helper_sys_fillrect, + .fb_copyarea = drm_fb_helper_sys_copyarea, + .fb_imageblit = drm_fb_helper_sys_imageblit, +}; + +static struct fb_deferred_io drm_fbdev_defio = { + .delay = HZ / 20, + .deferred_io = drm_fb_helper_deferred_io, +}; + +/** + * drm_fb_helper_generic_probe - Generic fbdev emulation probe helper + * @fb_helper: fbdev helper structure + * @sizes: describes fbdev size and scanout surface size + * + * This function uses the client API to crate a framebuffer backed by a dumb buffer. + * + * The _sys_ versions are used for &fb_ops.fb_read, fb_write, fb_fillrect, + * fb_copyarea, fb_imageblit. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_client_dev *client = &fb_helper->client; + struct drm_client_buffer *buffer; + struct drm_framebuffer *fb; + struct fb_info *fbi; + u32 format; + int ret; + + DRM_DEBUG_KMS("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); + + fb_helper->buffer = buffer; + fb_helper->fb = buffer->fb; + fb = buffer->fb; + + fbi = drm_fb_helper_alloc_fbi(fb_helper); + if (IS_ERR(fbi)) { + ret = PTR_ERR(fbi); + goto err_free_buffer; + } + + fbi->par = fb_helper; + fbi->fbops = &drm_fbdev_fb_ops; + fbi->screen_size = fb->height * fb->pitches[0]; + fbi->fix.smem_len = fbi->screen_size; + fbi->screen_buffer = buffer->vaddr; + strcpy(fbi->fix.id, "DRM emulated"); + + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); + drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height); + + if (fb->funcs->dirty) { + struct fb_ops *fbops; + void *shadow; + + /* + * fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per + * instance version is necessary. + */ + fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); + shadow = vzalloc(fbi->screen_size); + if (!fbops || !shadow) { + kfree(fbops); + vfree(shadow); + ret = -ENOMEM; + goto err_fb_info_destroy; + } + + *fbops = *fbi->fbops; + fbi->fbops = fbops; + fbi->screen_buffer = shadow; + fbi->fbdefio = &drm_fbdev_defio; + + fb_deferred_io_init(fbi); + } + + return 0; + +err_fb_info_destroy: + drm_fb_helper_fini(fb_helper); +err_free_buffer: + drm_client_framebuffer_delete(buffer); + + return ret; +} +EXPORT_SYMBOL(drm_fb_helper_generic_probe); + /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT) * but the module doesn't depend on any fb console symbols. At least * attempt to load fbcon to avoid leaving the system without a usable console. diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index b069433e7fc1..c134bbcfd2e9 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -32,6 +32,7 @@ struct drm_fb_helper; +#include #include #include #include @@ -154,6 +155,20 @@ struct drm_fb_helper_connector { * operations. */ struct drm_fb_helper { + /** + * @client: + * + * DRM client used by the generic fbdev emulation. + */ + struct drm_client_dev client; + + /** + * @buffer: + * + * Framebuffer used by the generic fbdev emulation. + */ + struct drm_client_buffer *buffer; + struct drm_framebuffer *fb; struct drm_device *dev; int crtc_count; @@ -234,6 +249,12 @@ struct drm_fb_helper { int preferred_bpp; }; +static inline struct drm_fb_helper * +drm_fb_helper_from_client(struct drm_client_dev *client) +{ + return container_of(client, struct drm_fb_helper, client); +} + /** * define DRM_FB_HELPER_DEFAULT_OPS - helper define for drm drivers * @@ -330,6 +351,9 @@ void drm_fb_helper_fbdev_teardown(struct drm_device *dev); void drm_fb_helper_lastclose(struct drm_device *dev); void drm_fb_helper_output_poll_changed(struct drm_device *dev); + +int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes); #else static inline void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, @@ -564,6 +588,13 @@ static inline void drm_fb_helper_output_poll_changed(struct drm_device *dev) { } +static inline int +drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes) +{ + return 0; +} + #endif static inline int -- cgit v1.2.3-55-g7522 From 244007ecb6bb94fa4e9b9a969fa86f2ad86ec543 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Tue, 3 Jul 2018 18:03:49 +0200 Subject: drm/pl111: Set .gem_prime_vmap and .gem_prime_mmap These are needed for pl111 to use the generic fbdev emulation. Cc: Eric Anholt Signed-off-by: Noralf Trønnes Reviewed-by: Eric Anholt Link: https://patchwork.freedesktop.org/patch/msgid/20180703160354.59955-4-noralf@tronnes.org --- drivers/gpu/drm/pl111/pl111_drv.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index 054b93689d94..17a38e85ba7d 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -250,6 +250,8 @@ static struct drm_driver pl111_drm_driver = { .gem_prime_import_sg_table = pl111_gem_import_sg_table, .gem_prime_export = drm_gem_prime_export, .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + .gem_prime_vmap = drm_gem_cma_prime_vmap, #if defined(CONFIG_DEBUG_FS) .debugfs_init = pl111_debugfs_init, -- cgit v1.2.3-55-g7522 From 894a677f4b3e6d2ab8d01bb46c1fbd5f92e4591b Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Tue, 3 Jul 2018 18:03:50 +0200 Subject: drm/cma-helper: Use the generic fbdev emulation This switches the CMA helper drivers that use its fbdev emulation over to the generic fbdev emulation. It's the first phase of using generic fbdev. A later phase will use DRM client callbacks for the lastclose/hotplug/remove callbacks. There are currently 2 fbdev init/fini functions: - drm_fb_cma_fbdev_init/drm_fb_cma_fbdev_fini - drm_fbdev_cma_init/drm_fbdev_cma_fini This is because the work on generic fbdev came up during a fbdev refactoring and thus wasn't completed. No point in completing that refactoring when drivers will soon move to drm_fb_helper_generic_probe(). tinydrm uses drm_fb_cma_fbdev_init_with_funcs(). Cc: Laurent Pinchart Signed-off-by: Noralf Trønnes Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703160354.59955-5-noralf@tronnes.org --- drivers/gpu/drm/drm_fb_cma_helper.c | 338 ++++-------------------------------- include/drm/drm_fb_cma_helper.h | 3 - 2 files changed, 31 insertions(+), 310 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 186d00adfb5f..718c7f961d8a 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -26,11 +27,8 @@ #include #include -#define DEFAULT_FBDEFIO_DELAY_MS 50 - struct drm_fbdev_cma { struct drm_fb_helper fb_helper; - const struct drm_framebuffer_funcs *fb_funcs; }; /** @@ -44,36 +42,6 @@ struct drm_fbdev_cma { * * An fbdev framebuffer backed by cma is also available by calling * drm_fb_cma_fbdev_init(). drm_fb_cma_fbdev_fini() tears it down. - * If the &drm_framebuffer_funcs.dirty callback is set, fb_deferred_io will be - * set up automatically. &drm_framebuffer_funcs.dirty is called by - * drm_fb_helper_deferred_io() in process context (&struct delayed_work). - * - * Example fbdev deferred io code:: - * - * static int driver_fb_dirty(struct drm_framebuffer *fb, - * struct drm_file *file_priv, - * unsigned flags, unsigned color, - * struct drm_clip_rect *clips, - * unsigned num_clips) - * { - * struct drm_gem_cma_object *cma = drm_fb_cma_get_gem_obj(fb, 0); - * ... push changes ... - * return 0; - * } - * - * static struct drm_framebuffer_funcs driver_fb_funcs = { - * .destroy = drm_gem_fb_destroy, - * .create_handle = drm_gem_fb_create_handle, - * .dirty = driver_fb_dirty, - * }; - * - * Initialize:: - * - * fbdev = drm_fb_cma_fbdev_init_with_funcs(dev, 16, - * dev->mode_config.num_crtc, - * dev->mode_config.num_connector, - * &driver_fb_funcs); - * */ static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper) @@ -131,153 +99,6 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, } EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr); -static int drm_fb_cma_mmap(struct fb_info *info, struct vm_area_struct *vma) -{ - return dma_mmap_writecombine(info->device, vma, info->screen_base, - info->fix.smem_start, info->fix.smem_len); -} - -static struct fb_ops drm_fbdev_cma_ops = { - .owner = THIS_MODULE, - 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_mmap = drm_fb_cma_mmap, -}; - -static int drm_fbdev_cma_deferred_io_mmap(struct fb_info *info, - struct vm_area_struct *vma) -{ - fb_deferred_io_mmap(info, vma); - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - - return 0; -} - -static int drm_fbdev_cma_defio_init(struct fb_info *fbi, - struct drm_gem_cma_object *cma_obj) -{ - struct fb_deferred_io *fbdefio; - struct fb_ops *fbops; - - /* - * Per device structures are needed because: - * fbops: fb_deferred_io_cleanup() clears fbops.fb_mmap - * fbdefio: individual delays - */ - fbdefio = kzalloc(sizeof(*fbdefio), GFP_KERNEL); - fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); - if (!fbdefio || !fbops) { - kfree(fbdefio); - kfree(fbops); - return -ENOMEM; - } - - /* can't be offset from vaddr since dirty() uses cma_obj */ - fbi->screen_buffer = cma_obj->vaddr; - /* fb_deferred_io_fault() needs a physical address */ - fbi->fix.smem_start = page_to_phys(virt_to_page(fbi->screen_buffer)); - - *fbops = *fbi->fbops; - fbi->fbops = fbops; - - fbdefio->delay = msecs_to_jiffies(DEFAULT_FBDEFIO_DELAY_MS); - fbdefio->deferred_io = drm_fb_helper_deferred_io; - fbi->fbdefio = fbdefio; - fb_deferred_io_init(fbi); - fbi->fbops->fb_mmap = drm_fbdev_cma_deferred_io_mmap; - - return 0; -} - -static void drm_fbdev_cma_defio_fini(struct fb_info *fbi) -{ - if (!fbi->fbdefio) - return; - - fb_deferred_io_cleanup(fbi); - kfree(fbi->fbdefio); - kfree(fbi->fbops); -} - -static int -drm_fbdev_cma_create(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct drm_fbdev_cma *fbdev_cma = to_fbdev_cma(helper); - struct drm_device *dev = helper->dev; - struct drm_gem_cma_object *obj; - struct drm_framebuffer *fb; - unsigned int bytes_per_pixel; - unsigned long offset; - struct fb_info *fbi; - size_t size; - int ret; - - DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n", - sizes->surface_width, sizes->surface_height, - sizes->surface_bpp); - - bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); - size = sizes->surface_width * sizes->surface_height * bytes_per_pixel; - obj = drm_gem_cma_create(dev, size); - if (IS_ERR(obj)) - return -ENOMEM; - - fbi = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(fbi)) { - ret = PTR_ERR(fbi); - goto err_gem_free_object; - } - - fb = drm_gem_fbdev_fb_create(dev, sizes, 0, &obj->base, - fbdev_cma->fb_funcs); - if (IS_ERR(fb)) { - dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); - ret = PTR_ERR(fb); - goto err_fb_info_destroy; - } - - helper->fb = fb; - - fbi->par = helper; - fbi->flags = FBINFO_FLAG_DEFAULT; - fbi->fbops = &drm_fbdev_cma_ops; - - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); - drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); - - offset = fbi->var.xoffset * bytes_per_pixel; - offset += fbi->var.yoffset * fb->pitches[0]; - - dev->mode_config.fb_base = (resource_size_t)obj->paddr; - fbi->screen_base = obj->vaddr + offset; - fbi->fix.smem_start = (unsigned long)(obj->paddr + offset); - fbi->screen_size = size; - fbi->fix.smem_len = size; - - if (fb->funcs->dirty) { - ret = drm_fbdev_cma_defio_init(fbi, obj); - if (ret) - goto err_cma_destroy; - } - - return 0; - -err_cma_destroy: - drm_framebuffer_remove(fb); -err_fb_info_destroy: - drm_fb_helper_fini(helper); -err_gem_free_object: - drm_gem_object_put_unlocked(&obj->base); - return ret; -} - -static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { - .fb_probe = drm_fbdev_cma_create, -}; - /** * drm_fb_cma_fbdev_init_with_funcs() - Allocate and initialize fbdev emulation * @dev: DRM device @@ -295,53 +116,7 @@ int drm_fb_cma_fbdev_init_with_funcs(struct drm_device *dev, unsigned int preferred_bpp, unsigned int max_conn_count, const struct drm_framebuffer_funcs *funcs) { - struct drm_fbdev_cma *fbdev_cma; - struct drm_fb_helper *fb_helper; - int ret; - - if (!preferred_bpp) - preferred_bpp = dev->mode_config.preferred_depth; - if (!preferred_bpp) - preferred_bpp = 32; - - if (!max_conn_count) - max_conn_count = dev->mode_config.num_connector; - - fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL); - if (!fbdev_cma) - return -ENOMEM; - - fbdev_cma->fb_funcs = funcs; - fb_helper = &fbdev_cma->fb_helper; - - drm_fb_helper_prepare(dev, fb_helper, &drm_fb_cma_helper_funcs); - - ret = drm_fb_helper_init(dev, fb_helper, max_conn_count); - if (ret < 0) { - DRM_DEV_ERROR(dev->dev, "Failed to initialize fbdev helper.\n"); - goto err_free; - } - - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret < 0) { - DRM_DEV_ERROR(dev->dev, "Failed to add connectors.\n"); - goto err_drm_fb_helper_fini; - } - - ret = drm_fb_helper_initial_config(fb_helper, preferred_bpp); - if (ret < 0) { - DRM_DEV_ERROR(dev->dev, "Failed to set fbdev configuration.\n"); - goto err_drm_fb_helper_fini; - } - - return 0; - -err_drm_fb_helper_fini: - drm_fb_helper_fini(fb_helper); -err_free: - kfree(fbdev_cma); - - return ret; + return drm_fb_cma_fbdev_init(dev, preferred_bpp, max_conn_count); } EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init_with_funcs); @@ -359,8 +134,14 @@ EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init_with_funcs); int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp, unsigned int max_conn_count) { - return drm_fb_cma_fbdev_init_with_funcs(dev, preferred_bpp, - max_conn_count, NULL); + struct drm_fbdev_cma *fbdev_cma; + + /* dev->fb_helper will indirectly point to fbdev_cma after this call */ + fbdev_cma = drm_fbdev_cma_init(dev, preferred_bpp, max_conn_count); + if (IS_ERR(fbdev_cma)) + return PTR_ERR(fbdev_cma); + + return 0; } EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init); @@ -370,104 +151,54 @@ EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init); */ void drm_fb_cma_fbdev_fini(struct drm_device *dev) { - struct drm_fb_helper *fb_helper = dev->fb_helper; - - if (!fb_helper) - return; - - /* Unregister if it hasn't been done already */ - if (fb_helper->fbdev && fb_helper->fbdev->dev) - drm_fb_helper_unregister_fbi(fb_helper); - - if (fb_helper->fbdev) - drm_fbdev_cma_defio_fini(fb_helper->fbdev); - - if (fb_helper->fb) - drm_framebuffer_remove(fb_helper->fb); - - drm_fb_helper_fini(fb_helper); - kfree(to_fbdev_cma(fb_helper)); + if (dev->fb_helper) + drm_fbdev_cma_fini(to_fbdev_cma(dev->fb_helper)); } EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_fini); +static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { + .fb_probe = drm_fb_helper_generic_probe, +}; + /** - * drm_fbdev_cma_init_with_funcs() - Allocate and initializes a drm_fbdev_cma struct + * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct * @dev: DRM device * @preferred_bpp: Preferred bits per pixel for the device * @max_conn_count: Maximum number of connectors - * @funcs: fb helper functions, in particular a custom dirty() callback * * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. */ -struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int max_conn_count, - const struct drm_framebuffer_funcs *funcs) +struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, + unsigned int preferred_bpp, unsigned int max_conn_count) { struct drm_fbdev_cma *fbdev_cma; - struct drm_fb_helper *helper; + struct drm_fb_helper *fb_helper; int ret; fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL); - if (!fbdev_cma) { - dev_err(dev->dev, "Failed to allocate drm fbdev.\n"); + if (!fbdev_cma) return ERR_PTR(-ENOMEM); - } - fbdev_cma->fb_funcs = funcs; - - helper = &fbdev_cma->fb_helper; - drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs); + fb_helper = &fbdev_cma->fb_helper; - ret = drm_fb_helper_init(dev, helper, max_conn_count); - if (ret < 0) { - dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); + ret = drm_client_new(dev, &fb_helper->client, "fbdev", NULL); + if (ret) goto err_free; - } - - ret = drm_fb_helper_single_add_all_connectors(helper); - if (ret < 0) { - dev_err(dev->dev, "Failed to add connectors.\n"); - goto err_drm_fb_helper_fini; - } - - ret = drm_fb_helper_initial_config(helper, preferred_bpp); - if (ret < 0) { - dev_err(dev->dev, "Failed to set initial hw configuration.\n"); - goto err_drm_fb_helper_fini; - } + ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_cma_helper_funcs, + preferred_bpp, max_conn_count); + if (ret) + goto err_client_put; return fbdev_cma; -err_drm_fb_helper_fini: - drm_fb_helper_fini(helper); +err_client_put: + drm_client_release(&fb_helper->client); err_free: kfree(fbdev_cma); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(drm_fbdev_cma_init_with_funcs); - -static const struct drm_framebuffer_funcs drm_fb_cma_funcs = { - .destroy = drm_gem_fb_destroy, - .create_handle = drm_gem_fb_create_handle, -}; - -/** - * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct - * @dev: DRM device - * @preferred_bpp: Preferred bits per pixel for the device - * @max_conn_count: Maximum number of connectors - * - * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. - */ -struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int max_conn_count) -{ - return drm_fbdev_cma_init_with_funcs(dev, preferred_bpp, - max_conn_count, - &drm_fb_cma_funcs); -} EXPORT_SYMBOL_GPL(drm_fbdev_cma_init); /** @@ -477,14 +208,7 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_init); void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) { drm_fb_helper_unregister_fbi(&fbdev_cma->fb_helper); - if (fbdev_cma->fb_helper.fbdev) - drm_fbdev_cma_defio_fini(fbdev_cma->fb_helper.fbdev); - - if (fbdev_cma->fb_helper.fb) - drm_framebuffer_remove(fbdev_cma->fb_helper.fb); - - drm_fb_helper_fini(&fbdev_cma->fb_helper); - kfree(fbdev_cma); + /* All resources have now been freed by drm_fbdev_fb_destroy() */ } EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index d532f88a8d55..a0546c3451f9 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h @@ -23,9 +23,6 @@ int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp, unsigned int max_conn_count); void drm_fb_cma_fbdev_fini(struct drm_device *dev); -struct drm_fbdev_cma *drm_fbdev_cma_init_with_funcs(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int max_conn_count, - const struct drm_framebuffer_funcs *funcs); struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, unsigned int preferred_bpp, unsigned int max_conn_count); void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma); -- cgit v1.2.3-55-g7522 From e896c132eb2cb4975d99477ea7e95aedb6dffa95 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Tue, 3 Jul 2018 18:03:51 +0200 Subject: drm/debugfs: Add internal client debugfs file Print the names of the internal clients currently attached. Reviewed-by: Daniel Vetter Signed-off-by: Noralf Trønnes Link: https://patchwork.freedesktop.org/patch/msgid/20180703160354.59955-6-noralf@tronnes.org --- drivers/gpu/drm/drm_client.c | 28 ++++++++++++++++++++++++++++ drivers/gpu/drm/drm_debugfs.c | 7 +++++++ include/drm/drm_client.h | 3 +++ 3 files changed, 38 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index 743495f7f833..4039a4d103a8 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -385,3 +385,31 @@ void drm_client_framebuffer_delete(struct drm_client_buffer *buffer) drm_client_buffer_delete(buffer); } EXPORT_SYMBOL(drm_client_framebuffer_delete); + +#ifdef CONFIG_DEBUG_FS +static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct drm_printer p = drm_seq_file_printer(m); + struct drm_client_dev *client; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) + drm_printf(&p, "%s\n", client->name); + mutex_unlock(&dev->clientlist_mutex); + + return 0; +} + +static const struct drm_info_list drm_client_debugfs_list[] = { + { "internal_clients", drm_client_debugfs_internal_clients, 0 }, +}; + +int drm_client_debugfs_init(struct drm_minor *minor) +{ + return drm_debugfs_create_files(drm_client_debugfs_list, + ARRAY_SIZE(drm_client_debugfs_list), + minor->debugfs_root, minor); +} +#endif diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index b2482818fee8..50a20bfc07ea 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -164,6 +165,12 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id, DRM_ERROR("Failed to create framebuffer debugfs file\n"); return ret; } + + ret = drm_client_debugfs_init(minor); + if (ret) { + DRM_ERROR("Failed to create client debugfs file\n"); + return ret; + } } if (dev->driver->debugfs_init) { diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 671052d80e38..989f8e52864d 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -10,6 +10,7 @@ struct drm_device; struct drm_file; struct drm_framebuffer; struct drm_gem_object; +struct drm_minor; struct module; /** @@ -133,4 +134,6 @@ struct drm_client_buffer * drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format); void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); +int drm_client_debugfs_init(struct drm_minor *minor); + #endif -- cgit v1.2.3-55-g7522 From 9060d7f49376159dcc500a97cb247ca8e4b98f48 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Tue, 3 Jul 2018 18:03:52 +0200 Subject: drm/fb-helper: Finish the generic fbdev emulation This adds a drm_fbdev_generic_setup() function that sets up generic fbdev emulation with client callbacks for restore, hotplug and unregister. Signed-off-by: Noralf Trønnes Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20180703160354.59955-7-noralf@tronnes.org --- drivers/gpu/drm/drm_fb_helper.c | 117 ++++++++++++++++++++++++++++++++++++++++ include/drm/drm_fb_helper.h | 7 +++ 2 files changed, 124 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 7e29d5340a17..4b0dd20bccb8 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -67,6 +67,9 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); * helper functions used by many drivers to implement the kernel mode setting * interfaces. * + * Drivers that support a dumb buffer with a virtual address and mmap support, + * should try out the generic fbdev emulation using drm_fbdev_generic_setup(). + * * Setup fbdev emulation by calling drm_fb_helper_fbdev_setup() and tear it * down by calling drm_fb_helper_fbdev_teardown(). * @@ -3118,6 +3121,120 @@ err_free_buffer: } EXPORT_SYMBOL(drm_fb_helper_generic_probe); +static const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = { + .fb_probe = drm_fb_helper_generic_probe, +}; + +static void drm_fbdev_client_unregister(struct drm_client_dev *client) +{ + struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); + + if (fb_helper->fbdev) { + drm_fb_helper_unregister_fbi(fb_helper); + /* drm_fbdev_fb_destroy() takes care of cleanup */ + return; + } + + /* Did drm_fb_helper_fbdev_setup() run? */ + if (fb_helper->dev) + drm_fb_helper_fini(fb_helper); + + drm_client_release(client); + kfree(fb_helper); +} + +static int drm_fbdev_client_restore(struct drm_client_dev *client) +{ + struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); + + drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); + + return 0; +} + +static int drm_fbdev_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 drm_fb_helper_fbdev_setup() failed, we only try once */ + if (!fb_helper->dev && fb_helper->funcs) + return 0; + + if (dev->fb_helper) + return drm_fb_helper_hotplug_event(dev->fb_helper); + + if (!dev->mode_config.num_connector) + return 0; + + ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_helper_generic_funcs, + fb_helper->preferred_bpp, 0); + if (ret) { + fb_helper->dev = NULL; + fb_helper->fbdev = NULL; + return ret; + } + + return 0; +} + +static const struct drm_client_funcs drm_fbdev_client_funcs = { + .owner = THIS_MODULE, + .unregister = drm_fbdev_client_unregister, + .restore = drm_fbdev_client_restore, + .hotplug = drm_fbdev_client_hotplug, +}; + +/** + * drm_fb_helper_generic_fbdev_setup() - Setup generic fbdev emulation + * @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 generic fbdev emulation for drivers that supports + * dumb buffers with a virtual address and that can be mmap'ed. + * + * 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(). + * + * Drivers that set the dirty callback on their framebuffer will get a shadow + * fbdev buffer that is blitted onto the real buffer. This is done in order to + * make deferred I/O work with all kinds of buffers. + * + * This function is safe to call even when there are no connectors present. + * Setup will be retried on the next hotplug event. + * + * Returns: + * Zero on success or negative error code on failure. + */ +int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) +{ + struct drm_fb_helper *fb_helper; + int ret; + + if (!drm_fbdev_emulation) + return 0; + + fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL); + if (!fb_helper) + return -ENOMEM; + + ret = drm_client_new(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs); + if (ret) { + kfree(fb_helper); + return ret; + } + + fb_helper->preferred_bpp = preferred_bpp; + + drm_fbdev_client_hotplug(&fb_helper->client); + + return 0; +} +EXPORT_SYMBOL(drm_fbdev_generic_setup); + /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT) * but the module doesn't depend on any fb console symbols. At least * attempt to load fbcon to avoid leaving the system without a usable console. diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index c134bbcfd2e9..5db08c8f1d25 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -354,6 +354,7 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev); int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, struct drm_fb_helper_surface_size *sizes); +int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp); #else static inline void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, @@ -595,6 +596,12 @@ drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, return 0; } +static inline int +drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) +{ + return 0; +} + #endif static inline int -- cgit v1.2.3-55-g7522 From dff1c7032ffea20cca2ad3973ed4c69c8182683d Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Tue, 3 Jul 2018 18:03:53 +0200 Subject: drm/tinydrm: Use drm_fbdev_generic_setup() Make full use of the generic fbdev client. Cc: David Lechner Signed-off-by: Noralf Trønnes Reviewed-by: David Lechner Link: https://patchwork.freedesktop.org/patch/msgid/20180703160354.59955-8-noralf@tronnes.org --- drivers/gpu/drm/tinydrm/core/tinydrm-core.c | 3 +-- drivers/gpu/drm/tinydrm/ili9225.c | 1 - drivers/gpu/drm/tinydrm/ili9341.c | 1 - drivers/gpu/drm/tinydrm/mi0283qt.c | 1 - drivers/gpu/drm/tinydrm/st7586.c | 1 - drivers/gpu/drm/tinydrm/st7735r.c | 1 - 6 files changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c index 24a33bf862fa..19c7f70adfa5 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c @@ -204,7 +204,7 @@ static int tinydrm_register(struct tinydrm_device *tdev) if (ret) return ret; - ret = drm_fb_cma_fbdev_init_with_funcs(drm, 0, 0, tdev->fb_funcs); + ret = drm_fbdev_generic_setup(drm, 0); if (ret) DRM_ERROR("Failed to initialize fbdev: %d\n", ret); @@ -214,7 +214,6 @@ static int tinydrm_register(struct tinydrm_device *tdev) static void tinydrm_unregister(struct tinydrm_device *tdev) { drm_atomic_helper_shutdown(tdev->drm); - drm_fb_cma_fbdev_fini(tdev->drm); drm_dev_unregister(tdev->drm); } diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c index 841c69aba059..455fefe012f5 100644 --- a/drivers/gpu/drm/tinydrm/ili9225.c +++ b/drivers/gpu/drm/tinydrm/ili9225.c @@ -368,7 +368,6 @@ static struct drm_driver ili9225_driver = { DRIVER_ATOMIC, .fops = &ili9225_fops, TINYDRM_GEM_DRIVER_OPS, - .lastclose = drm_fb_helper_lastclose, .name = "ili9225", .desc = "Ilitek ILI9225", .date = "20171106", diff --git a/drivers/gpu/drm/tinydrm/ili9341.c b/drivers/gpu/drm/tinydrm/ili9341.c index 8864dcde6edc..6701037749a7 100644 --- a/drivers/gpu/drm/tinydrm/ili9341.c +++ b/drivers/gpu/drm/tinydrm/ili9341.c @@ -145,7 +145,6 @@ static struct drm_driver ili9341_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &ili9341_fops, TINYDRM_GEM_DRIVER_OPS, - .lastclose = drm_fb_helper_lastclose, .debugfs_init = mipi_dbi_debugfs_init, .name = "ili9341", .desc = "Ilitek ILI9341", diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c index 015d03f2acba..d7bb4c5e6657 100644 --- a/drivers/gpu/drm/tinydrm/mi0283qt.c +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c @@ -154,7 +154,6 @@ static struct drm_driver mi0283qt_driver = { DRIVER_ATOMIC, .fops = &mi0283qt_fops, TINYDRM_GEM_DRIVER_OPS, - .lastclose = drm_fb_helper_lastclose, .debugfs_init = mipi_dbi_debugfs_init, .name = "mi0283qt", .desc = "Multi-Inno MI0283QT", diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c index 5c29e3803ecb..2fcbc3067d71 100644 --- a/drivers/gpu/drm/tinydrm/st7586.c +++ b/drivers/gpu/drm/tinydrm/st7586.c @@ -304,7 +304,6 @@ static struct drm_driver st7586_driver = { DRIVER_ATOMIC, .fops = &st7586_fops, TINYDRM_GEM_DRIVER_OPS, - .lastclose = drm_fb_helper_lastclose, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7586", .desc = "Sitronix ST7586", diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c index 6c7b15c9da4f..3081bc57c116 100644 --- a/drivers/gpu/drm/tinydrm/st7735r.c +++ b/drivers/gpu/drm/tinydrm/st7735r.c @@ -120,7 +120,6 @@ static struct drm_driver st7735r_driver = { DRIVER_ATOMIC, .fops = &st7735r_fops, TINYDRM_GEM_DRIVER_OPS, - .lastclose = drm_fb_helper_lastclose, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7735r", .desc = "Sitronix ST7735R", -- cgit v1.2.3-55-g7522 From 85b5bafb86e64f5d0ef143ab1a8363f5511eaabb Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Tue, 3 Jul 2018 18:03:54 +0200 Subject: drm/cma-helper: Remove drm_fb_cma_fbdev_init_with_funcs() Remove drm_fb_cma_fbdev_init_with_funcs(), its only user tinydrm has moved to drm_fbdev_generic_setup(). Cc: Laurent Pinchart Signed-off-by: Noralf Trønnes Reviewed-by: David Lechner Link: https://patchwork.freedesktop.org/patch/msgid/20180703160354.59955-9-noralf@tronnes.org --- drivers/gpu/drm/drm_fb_cma_helper.c | 21 --------------------- include/drm/drm_fb_cma_helper.h | 3 --- 2 files changed, 24 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 718c7f961d8a..9da36a6271d3 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -99,27 +99,6 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, } EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr); -/** - * drm_fb_cma_fbdev_init_with_funcs() - Allocate and initialize fbdev emulation - * @dev: DRM device - * @preferred_bpp: Preferred bits per pixel for the device. - * @dev->mode_config.preferred_depth is used if this is zero. - * @max_conn_count: Maximum number of connectors. - * @dev->mode_config.num_connector is used if this is zero. - * @funcs: Framebuffer functions, in particular a custom dirty() callback. - * Can be NULL. - * - * Returns: - * Zero on success or negative error code on failure. - */ -int drm_fb_cma_fbdev_init_with_funcs(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int max_conn_count, - const struct drm_framebuffer_funcs *funcs) -{ - return drm_fb_cma_fbdev_init(dev, preferred_bpp, max_conn_count); -} -EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init_with_funcs); - /** * drm_fb_cma_fbdev_init() - Allocate and initialize fbdev emulation * @dev: DRM device diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index a0546c3451f9..96e26e3b9a0c 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h @@ -16,9 +16,6 @@ struct drm_mode_fb_cmd2; struct drm_plane; struct drm_plane_state; -int drm_fb_cma_fbdev_init_with_funcs(struct drm_device *dev, - unsigned int preferred_bpp, unsigned int max_conn_count, - const struct drm_framebuffer_funcs *funcs); int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp, unsigned int max_conn_count); void drm_fb_cma_fbdev_fini(struct drm_device *dev); -- cgit v1.2.3-55-g7522 From 23167fa9a519c5408c4aa7fd1dc85b7c48b37864 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Thu, 7 Jun 2018 19:16:48 +0530 Subject: drm/panel: simple: Add support for Rocktech RK070ER9427 LCD panel This adds support for the Rocktech Display Ltd. RK070ER9427 800(RGB)x480 TFT LCD panel, which can be supported by the simple panel driver. Signed-off-by: Jagan Teki Reviewed-by: Rob Herring Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180607134648.2902-1-jagan@amarulasolutions.com --- .../display/panel/rocktech,rk070er9427.txt | 25 ++++++++++++++++ drivers/gpu/drm/panel/panel-simple.c | 33 ++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/rocktech,rk070er9427.txt (limited to 'drivers/gpu/drm') diff --git a/Documentation/devicetree/bindings/display/panel/rocktech,rk070er9427.txt b/Documentation/devicetree/bindings/display/panel/rocktech,rk070er9427.txt new file mode 100644 index 000000000000..eb1fb9f8d1f4 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/rocktech,rk070er9427.txt @@ -0,0 +1,25 @@ +Rocktech Display Ltd. RK070ER9427 800(RGB)x480 TFT LCD panel + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. + +Required properties: +- compatible: should be "rocktech,rk070er9427" + +Optional properties: +- backlight: phandle of the backlight device attached to the panel + +Optional nodes: +- Video port for LCD panel input. + +Example: + panel { + compatible = "rocktech,rk070er9427"; + backlight = <&backlight_lcd>; + + port { + lcd_panel_in: endpoint { + remote-endpoint = <&lcd_display_out>; + }; + }; + }; diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index ac6aaa174c0b..884fd534c8b3 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1747,6 +1747,36 @@ static const struct panel_desc qd43003c0_40 = { .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; +static const struct display_timing rocktech_rk070er9427_timing = { + .pixelclock = { 26400000, 33300000, 46800000 }, + .hactive = { 800, 800, 800 }, + .hfront_porch = { 16, 210, 354 }, + .hback_porch = { 46, 46, 46 }, + .hsync_len = { 1, 1, 1 }, + .vactive = { 480, 480, 480 }, + .vfront_porch = { 7, 22, 147 }, + .vback_porch = { 23, 23, 23 }, + .vsync_len = { 1, 1, 1 }, + .flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc rocktech_rk070er9427 = { + .timings = &rocktech_rk070er9427_timing, + .num_timings = 1, + .bpc = 6, + .size = { + .width = 154, + .height = 86, + }, + .delay = { + .prepare = 41, + .enable = 50, + .unprepare = 41, + .disable = 50, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, +}; + static const struct drm_display_mode samsung_lsn122dl01_c01_mode = { .clock = 271560, .hdisplay = 2560, @@ -2283,6 +2313,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "qiaodian,qd43003c0-40", .data = &qd43003c0_40, + }, { + .compatible = "rocktech,rk070er9427", + .data = &rocktech_rk070er9427, }, { .compatible = "samsung,lsn122dl01-c01", .data = &samsung_lsn122dl01_c01, -- cgit v1.2.3-55-g7522 From aa7e6455e1ef75113d47889badb21730f1436714 Mon Sep 17 00:00:00 2001 From: Jan Tuerk Date: Tue, 19 Jun 2018 11:55:44 +0200 Subject: drm/panel: Add support for the EDT ETM0700G0BDH6 The Emerging Display Technology ETM0700G0BDH6 is exactly the same display as the ETM0700G0DH6, exept the pixelclock polarity. Therefore re-use the ETM0700G0DH6 modes. It is used by default on emtrion Avari based development kits. Signed-off-by: Jan Tuerk Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180619095546.24445-2-jan.tuerk@emtrion.com --- drivers/gpu/drm/panel/panel-simple.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 884fd534c8b3..e05321d3442a 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -936,6 +936,18 @@ static const struct panel_desc edt_etm0700g0dh6 = { .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE, }; +static const struct panel_desc edt_etm0700g0bdh6 = { + .modes = &edt_etm0700g0dh6_mode, + .num_modes = 1, + .bpc = 6, + .size = { + .width = 152, + .height = 91, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, +}; + static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = { .clock = 32260, .hdisplay = 800, @@ -2217,6 +2229,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "edt,etm0700g0dh6", .data = &edt_etm0700g0dh6, + }, { + .compatible = "edt,etm0700g0bdh6", + .data = &edt_etm0700g0bdh6, }, { .compatible = "foxlink,fl500wvr00-a0t", .data = &foxlink_fl500wvr00_a0t, -- cgit v1.2.3-55-g7522 From aad34de22e6325727f7c716e2e7aa396031ceed5 Mon Sep 17 00:00:00 2001 From: Jan Tuerk Date: Tue, 19 Jun 2018 11:55:45 +0200 Subject: drm/panel: Add support for the EDT ETM0700G0EDH6 The Emerging Display Technology ETM0700G0EDH6 is the uses the same panel as the ETM0700G0BDH6. It differs in the hardware design for the backlight and the touchscreen i2c interface. As the new display type has different requirements for drive-strengths on the i2c-bus, add an additional compatible to allow the handling of it or warn about incompatible cpu and display combinations. Signed-off-by: Jan Tuerk Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180619095546.24445-3-jan.tuerk@emtrion.com --- drivers/gpu/drm/panel/panel-simple.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index e05321d3442a..5eb506256d42 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2232,6 +2232,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "edt,etm0700g0bdh6", .data = &edt_etm0700g0bdh6, + }, { + .compatible = "edt,etm0700g0edh6", + .data = &edt_etm0700g0bdh6, }, { .compatible = "foxlink,fl500wvr00-a0t", .data = &foxlink_fl500wvr00_a0t, -- cgit v1.2.3-55-g7522 From 0ca0c827efdf248dfb4bbd4b9066acb6337e07ac Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 23 May 2018 11:25:04 +0200 Subject: drm/panel: simple: Add DLC DLC0700YZG-1 panel This patch adds support for DLC DLC0700YZG-1 1024x600 LVDS panels to the simple-panel driver. Signed-off-by: Philipp Zabel [m.felsch@pengutronix.de: fix typo in compatible dt-binding] [m.felsch@pengutronix.de: add property bindings] Signed-off-by: Marco Felsch Reviewed-by: Eric Anholt Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180523092504.5142-3-m.felsch@pengutronix.de --- .../bindings/display/panel/dlc,dlc0700yzg-1.txt | 13 +++++++++ drivers/gpu/drm/panel/panel-simple.c | 32 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/dlc,dlc0700yzg-1.txt (limited to 'drivers/gpu/drm') diff --git a/Documentation/devicetree/bindings/display/panel/dlc,dlc0700yzg-1.txt b/Documentation/devicetree/bindings/display/panel/dlc,dlc0700yzg-1.txt new file mode 100644 index 000000000000..bf06bb025b08 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/dlc,dlc0700yzg-1.txt @@ -0,0 +1,13 @@ +DLC Display Co. DLC0700YZG-1 7.0" WSVGA TFT LCD panel + +Required properties: +- compatible: should be "dlc,dlc0700yzg-1" +- power-supply: See simple-panel.txt + +Optional properties: +- reset-gpios: See panel-common.txt +- enable-gpios: See simple-panel.txt +- backlight: See simple-panel.txt + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 5eb506256d42..230f030d1ea9 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -884,6 +884,35 @@ static const struct panel_desc chunghwa_claa101wb01 = { }, }; +static const struct display_timing dlc_dlc0700yzg_1_timing = { + .pixelclock = { 45000000, 51200000, 57000000 }, + .hactive = { 1024, 1024, 1024 }, + .hfront_porch = { 100, 106, 113 }, + .hback_porch = { 100, 106, 113 }, + .hsync_len = { 100, 108, 114 }, + .vactive = { 600, 600, 600 }, + .vfront_porch = { 8, 11, 15 }, + .vback_porch = { 8, 11, 15 }, + .vsync_len = { 9, 13, 15 }, + .flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc dlc_dlc0700yzg_1 = { + .timings = &dlc_dlc0700yzg_1_timing, + .num_timings = 1, + .bpc = 6, + .size = { + .width = 154, + .height = 86, + }, + .delay = { + .prepare = 30, + .enable = 200, + .disable = 200, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, +}; + static const struct drm_display_mode edt_et057090dhu_mode = { .clock = 25175, .hdisplay = 640, @@ -2220,6 +2249,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "chunghwa,claa101wb01", .data = &chunghwa_claa101wb01 + }, { + .compatible = "dlc,dlc0700yzg-1", + .data = &dlc_dlc0700yzg_1, }, { .compatible = "edt,et057090dhu", .data = &edt_et057090dhu, -- cgit v1.2.3-55-g7522 From 5fa8e4a22182df8ea39adeba4bd518506e26a96d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 9 May 2018 15:00:39 +0200 Subject: drm/panel: Make of_drm_find_panel() return an ERR_PTR() instead of NULL Right now, the DRM panel logic returns NULL when a panel pointing to the passed OF node is not present in the list of registered panels. Most drivers interpret this NULL value as -EPROBE_DEFER, but we are about to modify the semantic of of_drm_find_panel() and let the framework return -ENODEV when the device node we're pointing to has a status property that is not equal to "okay" or "ok". Let's first patch the of_drm_find_panel() implementation to return ERR_PTR(-EPROBE_DEFER) instead of NULL and patch all callers to replace the '!panel' check by an 'IS_ERR(panel)' one. Signed-off-by: Boris Brezillon Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180509130042.9435-2-boris.brezillon@bootlin.com --- drivers/gpu/drm/bridge/cdns-dsi.c | 2 +- drivers/gpu/drm/bridge/lvds-encoder.c | 4 ++-- drivers/gpu/drm/drm_of.c | 4 +++- drivers/gpu/drm/drm_panel.c | 6 ++++-- drivers/gpu/drm/exynos/exynos_dp.c | 6 ++++-- drivers/gpu/drm/exynos/exynos_drm_dpi.c | 4 ++-- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 3 +++ drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 5 +++-- drivers/gpu/drm/msm/disp/mdp4/mdp4_lcdc_encoder.c | 4 ++-- drivers/gpu/drm/msm/disp/mdp4/mdp4_lvds_connector.c | 5 ++++- drivers/gpu/drm/msm/dsi/dsi_host.c | 2 +- drivers/gpu/drm/rcar-du/rcar_lvds.c | 4 ++-- drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 2 +- drivers/gpu/drm/sti/sti_dvo.c | 4 +++- drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 4 ++-- drivers/gpu/drm/tegra/dsi.c | 3 +++ drivers/gpu/drm/tegra/output.c | 4 ++-- include/drm/drm_panel.h | 2 +- 18 files changed, 43 insertions(+), 25 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c index f2d43f24acfb..ce9496d13986 100644 --- a/drivers/gpu/drm/bridge/cdns-dsi.c +++ b/drivers/gpu/drm/bridge/cdns-dsi.c @@ -1152,7 +1152,7 @@ static int cdns_dsi_attach(struct mipi_dsi_host *host, np = of_node_get(dev->dev.of_node); panel = of_drm_find_panel(np); - if (panel) { + if (!IS_ERR(panel)) { bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI); } else { bridge = of_drm_find_bridge(dev->dev.of_node); diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c index 75b0d3f6e4de..f56c92f7af7c 100644 --- a/drivers/gpu/drm/bridge/lvds-encoder.c +++ b/drivers/gpu/drm/bridge/lvds-encoder.c @@ -68,9 +68,9 @@ static int lvds_encoder_probe(struct platform_device *pdev) panel = of_drm_find_panel(panel_node); of_node_put(panel_node); - if (!panel) { + if (IS_ERR(panel)) { dev_dbg(&pdev->dev, "panel not found, deferring probe\n"); - return -EPROBE_DEFER; + return PTR_ERR(panel); } lvds_encoder->panel_bridge = diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 3b8c7a6a5720..1f4a16772583 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -241,8 +241,10 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, if (panel) { *panel = of_drm_find_panel(remote); - if (*panel) + if (!IS_ERR(*panel)) ret = 0; + else + *panel = NULL; } /* No panel found yet, check for a bridge next. */ diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 965530a6f4cd..7e5530a04846 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -151,7 +151,9 @@ EXPORT_SYMBOL(drm_panel_detach); * tree node. If a matching panel is found, return a pointer to it. * * Return: A pointer to the panel registered for the specified device tree - * node or NULL if no panel matching the device tree node can be found. + * node or an ERR_PTR() if no panel matching the device tree node can be found. + * The only error that can be reported is -EPROBE_DEFER, meaning that the panel + * device has not been probed yet, and the caller should retry later. */ struct drm_panel *of_drm_find_panel(const struct device_node *np) { @@ -167,7 +169,7 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np) } mutex_unlock(&panel_lock); - return NULL; + return ERR_PTR(-EPROBE_DEFER); } EXPORT_SYMBOL(of_drm_find_panel); #endif diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 86330f396784..af7ab1ceb50f 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -232,9 +232,11 @@ static int exynos_dp_probe(struct platform_device *pdev) np = of_parse_phandle(dev->of_node, "panel", 0); if (np) { dp->plat_data.panel = of_drm_find_panel(np); + of_node_put(np); - if (!dp->plat_data.panel) - return -EPROBE_DEFER; + if (IS_ERR(dp->plat_data.panel)) + return PTR_ERR(dp->plat_data.panel); + goto out; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 66945e0dc57f..5887e8522b70 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -240,8 +240,8 @@ struct drm_encoder *exynos_dpi_probe(struct device *dev) if (ctx->panel_node) { ctx->panel = of_drm_find_panel(ctx->panel_node); - if (!ctx->panel) - return ERR_PTR(-EPROBE_DEFER); + if (IS_ERR(ctx->panel)) + return ERR_CAST(ctx->panel); } return &ctx->encoder; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 6d29777884f9..809e1e0447df 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1519,6 +1519,9 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host, dsi->format = device->format; dsi->mode_flags = device->mode_flags; dsi->panel = of_drm_find_panel(device->dev.of_node); + if (IS_ERR(dsi->panel)) + dsi->panel = NULL; + if (dsi->panel) { drm_panel_attach(dsi->panel, &dsi->connector); dsi->connector.status = connector_status_connected; diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index c54806d08dd7..681e2a07d03b 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -148,8 +148,9 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) if (panel_node) { fsl_dev->connector.panel = of_drm_find_panel(panel_node); of_node_put(panel_node); - if (!fsl_dev->connector.panel) - return -EPROBE_DEFER; + if (IS_ERR(fsl_dev->connector.panel)) + return PTR_ERR(fsl_dev->connector.panel); + return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel); } diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_lcdc_encoder.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_lcdc_encoder.c index 4a645926edb7..2bfb39082f54 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_lcdc_encoder.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_lcdc_encoder.c @@ -341,7 +341,7 @@ static void mdp4_lcdc_encoder_disable(struct drm_encoder *encoder) mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0); panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node); - if (panel) { + if (!IS_ERR(panel)) { drm_panel_disable(panel); drm_panel_unprepare(panel); } @@ -410,7 +410,7 @@ static void mdp4_lcdc_encoder_enable(struct drm_encoder *encoder) dev_err(dev->dev, "failed to enable lcdc_clk: %d\n", ret); panel = of_drm_find_panel(mdp4_lcdc_encoder->panel_node); - if (panel) { + if (!IS_ERR(panel)) { drm_panel_prepare(panel); drm_panel_enable(panel); } diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_lvds_connector.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_lvds_connector.c index e3b1c86b7aae..32fba5664b0e 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_lvds_connector.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_lvds_connector.c @@ -34,9 +34,12 @@ static enum drm_connector_status mdp4_lvds_connector_detect( struct mdp4_lvds_connector *mdp4_lvds_connector = to_mdp4_lvds_connector(connector); - if (!mdp4_lvds_connector->panel) + if (!mdp4_lvds_connector->panel) { mdp4_lvds_connector->panel = of_drm_find_panel(mdp4_lvds_connector->panel_node); + if (IS_ERR(mdp4_lvds_connector->panel)) + mdp4_lvds_connector->panel = NULL; + } return mdp4_lvds_connector->panel ? connector_status_connected : diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 2f1a2780658a..29841f440111 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1898,7 +1898,7 @@ int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer) * output */ if (check_defer && msm_host->device_node) { - if (!of_drm_find_panel(msm_host->device_node)) + if (IS_ERR(of_drm_find_panel(msm_host->device_node))) if (!of_drm_find_bridge(msm_host->device_node)) return -EPROBE_DEFER; } diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 155ad840f3c5..5d8e391e75f4 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -434,8 +434,8 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds) ret = -EPROBE_DEFER; } else { lvds->panel = of_drm_find_panel(remote); - if (!lvds->panel) - ret = -EPROBE_DEFER; + if (IS_ERR(lvds->panel)) + ret = PTR_ERR(lvds->panel); } done: diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c index d53d5a09547f..01642aaf6127 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c @@ -595,7 +595,7 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, dsi->format = device->format; dsi->mode_flags = device->mode_flags; dsi->panel = of_drm_find_panel(device->dev.of_node); - if (dsi->panel) + if (!IS_ERR(dsi->panel)) return drm_panel_attach(dsi->panel, &dsi->connector); return -EINVAL; diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index a5979cd25cc7..030da55a8d30 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -387,7 +387,9 @@ sti_dvo_connector_detect(struct drm_connector *connector, bool force) if (!dvo->panel) { dvo->panel = of_drm_find_panel(dvo->panel_node); - if (dvo->panel) + if (IS_ERR(dvo->panel)) + dvo->panel = NULL; + else drm_panel_attach(dvo->panel, connector); } diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c index da9814f94d00..2b40d1f6aee8 100644 --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c @@ -833,8 +833,8 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host, dsi->device = device; dsi->panel = of_drm_find_panel(device->dev.of_node); - if (!dsi->panel) - return -EINVAL; + if (IS_ERR(dsi->panel)) + return PTR_ERR(dsi->panel); dev_info(host->dev, "Attached device %s\n", device->name); diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 87c5d89bc9ba..ad88ec230329 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -1411,6 +1411,9 @@ static int tegra_dsi_host_attach(struct mipi_dsi_host *host, struct tegra_output *output = &dsi->output; output->panel = of_drm_find_panel(device->dev.of_node); + if (IS_ERR(output->panel)) + output->panel = NULL; + if (output->panel && output->connector.dev) { drm_panel_attach(output->panel, &output->connector); drm_helper_hpd_irq_event(output->connector.dev); diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index ffe34bd0bb9d..0c0936511bb4 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -110,8 +110,8 @@ int tegra_output_probe(struct tegra_output *output) panel = of_parse_phandle(output->of_node, "nvidia,panel", 0); if (panel) { output->panel = of_drm_find_panel(panel); - if (!output->panel) - return -EPROBE_DEFER; + if (IS_ERR(output->panel)) + return PTR_ERR(output->panel); of_node_put(panel); } diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 26a1b5fd8796..582a0ec0aa70 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -200,7 +200,7 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np); #else static inline struct drm_panel *of_drm_find_panel(const struct device_node *np) { - return NULL; + return ERR_PTR(-ENODEV); } #endif -- cgit v1.2.3-55-g7522 From c59eb3cfde1faed960fe6de5040c4bd4310fb1cd Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 9 May 2018 15:00:40 +0200 Subject: drm/panel: Let of_drm_find_panel() return -ENODEV when the panel is disabled DT nodes might be present in the DT but with a status property set to "disabled" or "fail". In this case, we should not return -EPROBE_DEFER when the caller asks for a drm_panel instance. Return -ENODEV instead. Signed-off-by: Boris Brezillon Reviewed-by: Thierry Reding Acked-by: Thierry Reding Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180509130042.9435-3-boris.brezillon@bootlin.com --- drivers/gpu/drm/drm_panel.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 7e5530a04846..b902361dee6e 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -152,13 +152,18 @@ EXPORT_SYMBOL(drm_panel_detach); * * Return: A pointer to the panel registered for the specified device tree * node or an ERR_PTR() if no panel matching the device tree node can be found. - * The only error that can be reported is -EPROBE_DEFER, meaning that the panel - * device has not been probed yet, and the caller should retry later. + * Possible error codes returned by this function: + * - EPROBE_DEFER: the panel device has not been probed yet, and the caller + * should retry later + * - ENODEV: the device is not available (status != "okay" or "ok") */ struct drm_panel *of_drm_find_panel(const struct device_node *np) { struct drm_panel *panel; + if (!of_device_is_available(np)) + return ERR_PTR(-ENODEV); + mutex_lock(&panel_lock); list_for_each_entry(panel, &panel_list, list) { -- cgit v1.2.3-55-g7522 From 2e64a174179a144742fa70f09803a012aceca476 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 9 May 2018 15:00:41 +0200 Subject: drm/of: Make drm_of_find_panel_or_bridge() fail when the device is disabled There's no point searching for a drm_bridge or drm_panel if the OF node we're pointing has a status property that is not "okay" or "ok". Just return -ENODEV in this case. Signed-off-by: Boris Brezillon Reviewed-by: Thierry Reding Acked-by: Thierry Reding Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180509130042.9435-4-boris.brezillon@bootlin.com --- drivers/gpu/drm/drm_of.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 1f4a16772583..260612958cbe 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -239,6 +239,11 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, if (!remote) return -ENODEV; + if (!of_device_is_available(remote)) { + of_node_put(remote); + return -ENODEV; + } + if (panel) { *panel = of_drm_find_panel(remote); if (!IS_ERR(*panel)) -- cgit v1.2.3-55-g7522 From 1b9883eae82251b742627ebd9d544d1b1bddf121 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 9 May 2018 15:00:42 +0200 Subject: drm/vc4: Support the case where the DSI device is disabled Having a device with a status property != "okay" in the DT is a valid use case, and we should not prevent the registration of the DRM device when the DSI device connected to the DSI controller is disabled. Consider the ENODEV return code as a valid result and do not expose the DSI encoder/connector when it happens. Signed-off-by: Boris Brezillon Reviewed-by: Eric Anholt Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180509130042.9435-5-boris.brezillon@bootlin.com --- drivers/gpu/drm/vc4/vc4_dsi.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 9c8e89372d1c..0c607eb33d7e 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1612,8 +1612,18 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, &dsi->bridge); - if (ret) + if (ret) { + /* If the bridge or panel pointed by dev->of_node is not + * enabled, just return 0 here so that we don't prevent the DRM + * dev from being registered. Of course that means the DSI + * encoder won't be exposed, but that's not a problem since + * nothing is connected to it. + */ + if (ret == -ENODEV) + return 0; + return ret; + } if (panel) { dsi->bridge = devm_drm_panel_bridge_add(dev, panel, @@ -1664,7 +1674,8 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_dsi *dsi = dev_get_drvdata(dev); - pm_runtime_disable(dev); + if (dsi->bridge) + pm_runtime_disable(dev); vc4_dsi_encoder_destroy(dsi->encoder); -- cgit v1.2.3-55-g7522 From ae8cf41b6a5e3a0847cf5ef9bf6feb82e75ab5eb Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Tue, 19 Jun 2018 10:19:26 +0200 Subject: drm/panel: simple: Add support for BOE HV070WSA-100 panel to simple-panel The patch adds support for BOE HV070WSA-100 WSVGA 7.01 inch panel to the panel-simple driver. The panel is used in Exynos5250-arndale boards. Signed-off-by: Andrzej Hajda Signed-off-by: Maciej Purski Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/1529396370-18761-6-git-send-email-m.purski@samsung.com --- drivers/gpu/drm/panel/panel-simple.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 230f030d1ea9..cca1168f0881 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -772,6 +772,28 @@ static const struct panel_desc avic_tm070ddh03 = { }, }; +static const struct drm_display_mode boe_hv070wsa_mode = { + .clock = 40800, + .hdisplay = 1024, + .hsync_start = 1024 + 90, + .hsync_end = 1024 + 90 + 90, + .htotal = 1024 + 90 + 90 + 90, + .vdisplay = 600, + .vsync_start = 600 + 3, + .vsync_end = 600 + 3 + 4, + .vtotal = 600 + 3 + 4 + 3, + .vrefresh = 60, +}; + +static const struct panel_desc boe_hv070wsa = { + .modes = &boe_hv070wsa_mode, + .num_modes = 1, + .size = { + .width = 154, + .height = 90, + }, +}; + static const struct drm_display_mode boe_nv101wxmn51_modes[] = { { .clock = 71900, @@ -2237,6 +2259,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "avic,tm070ddh03", .data = &avic_tm070ddh03, + }, { + .compatible = "boe,hv070wsa-100", + .data = &boe_hv070wsa }, { .compatible = "boe,nv101wxmn51", .data = &boe_nv101wxmn51, -- cgit v1.2.3-55-g7522 From 955f60db0f2b87094f64df3d7f608fdac4a6ebb1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 18 Jun 2018 16:22:34 +0300 Subject: drm: Add support for extracting sync signal drive edge from videomode The sync in some panels needs to be driven by different edge of the pixel clock compared to data. This is reflected by the DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in videmode flags. Add similar similar definitions for bus_flags and convert the sync drive edge via drm_bus_flags_from_videomode(). Signed-off-by: Peter Ujfalusi Signed-off-by: Tomi Valkeinen Signed-off-by: Jyri Sarha Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180618132242.8673-2-tomi.valkeinen@ti.com --- drivers/gpu/drm/drm_modes.c | 15 +++++++++++---- include/drm/drm_connector.h | 4 ++++ 2 files changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 7f552d5fa88e..f8f7eae738ab 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -659,10 +659,12 @@ EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode); * drm_bus_flags_from_videomode - extract information about pixelclk and * DE polarity from videomode and store it in a separate variable * @vm: videomode structure to use - * @bus_flags: information about pixelclk and DE polarity will be stored here + * @bus_flags: information about pixelclk, sync and DE polarity will be stored + * here * - * Sets DRM_BUS_FLAG_DE_(LOW|HIGH) and DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE - * in @bus_flags according to DISPLAY_FLAGS found in @vm + * Sets DRM_BUS_FLAG_DE_(LOW|HIGH), DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE and + * DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS + * found in @vm */ void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags) { @@ -672,6 +674,11 @@ void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags) if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) *bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE; + if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE) + *bus_flags |= DRM_BUS_FLAG_SYNC_POSEDGE; + if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE) + *bus_flags |= DRM_BUS_FLAG_SYNC_NEGEDGE; + if (vm->flags & DISPLAY_FLAGS_DE_LOW) *bus_flags |= DRM_BUS_FLAG_DE_LOW; if (vm->flags & DISPLAY_FLAGS_DE_HIGH) @@ -684,7 +691,7 @@ EXPORT_SYMBOL_GPL(drm_bus_flags_from_videomode); * of_get_drm_display_mode - get a drm_display_mode from devicetree * @np: device_node with the timing specification * @dmode: will be set to the return value - * @bus_flags: information about pixelclk and DE polarity + * @bus_flags: information about pixelclk, sync and DE polarity * @index: index into the list of display timings in devicetree * * This function is expensive and should only be used, if only one mode is to be diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index ad397dfc042b..a5179eb9e56f 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -290,6 +290,10 @@ struct drm_display_info { #define DRM_BUS_FLAG_DATA_MSB_TO_LSB (1<<4) /* data is transmitted LSB to MSB on the bus */ #define DRM_BUS_FLAG_DATA_LSB_TO_MSB (1<<5) +/* drive sync on pos. edge */ +#define DRM_BUS_FLAG_SYNC_POSEDGE (1<<6) +/* drive sync on neg. edge */ +#define DRM_BUS_FLAG_SYNC_NEGEDGE (1<<7) /** * @bus_flags: Additional information (like pixel signal polarity) for -- cgit v1.2.3-55-g7522 From 3b39ad7a553f3ecdf16374973e4b0971b861769d Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 18 Jun 2018 16:22:40 +0300 Subject: drm/panel: simple: Add newhaven, nhd-4.3-480272ef-atxl LCD Add support for newhaven,nhd-4.3-480272ef-atxl to panel-simple. Signed-off-by: Tomi Valkeinen Cc: Thierry Reding Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180618132242.8673-8-tomi.valkeinen@ti.com --- .../panel/newhaven,nhd-4.3-480272ef-atxl.txt | 7 +++++ drivers/gpu/drm/panel/panel-simple.c | 30 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt (limited to 'drivers/gpu/drm') diff --git a/Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt b/Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt new file mode 100644 index 000000000000..e78292b1a131 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/newhaven,nhd-4.3-480272ef-atxl.txt @@ -0,0 +1,7 @@ +Newhaven Display International 480 x 272 TFT LCD panel + +Required properties: +- compatible: should be "newhaven,nhd-4.3-480272ef-atxl" + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index cca1168f0881..2c4857e08a76 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1625,6 +1625,33 @@ static const struct panel_desc netron_dy_e231732 = { .bus_format = MEDIA_BUS_FMT_RGB666_1X18, }; +static const struct drm_display_mode newhaven_nhd_43_480272ef_atxl_mode = { + .clock = 9000, + .hdisplay = 480, + .hsync_start = 480 + 2, + .hsync_end = 480 + 2 + 41, + .htotal = 480 + 2 + 41 + 2, + .vdisplay = 272, + .vsync_start = 272 + 2, + .vsync_end = 272 + 2 + 10, + .vtotal = 272 + 2 + 10 + 2, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}; + +static const struct panel_desc newhaven_nhd_43_480272ef_atxl = { + .modes = &newhaven_nhd_43_480272ef_atxl_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 95, + .height = 54, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE | + DRM_BUS_FLAG_SYNC_POSEDGE, +}; + static const struct display_timing nlt_nl192108ac18_02d_timing = { .pixelclock = { 130000000, 148350000, 163000000 }, .hactive = { 1920, 1920, 1920 }, @@ -2367,6 +2394,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "netron-dy,e231732", .data = &netron_dy_e231732, + }, { + .compatible = "newhaven,nhd-4.3-480272ef-atxl", + .data = &newhaven_nhd_43_480272ef_atxl, }, { .compatible = "nlt,nl192108ac18-02d", .data = &nlt_nl192108ac18_02d, -- cgit v1.2.3-55-g7522 From a5d2ade627dcaa825707e20da6994e79c95e2434 Mon Sep 17 00:00:00 2001 From: Christoph Fritz Date: Mon, 4 Jun 2018 13:16:48 +0200 Subject: drm/panel: simple: Add support for Innolux G070Y2-L01 This patch adds support for Innolux G070Y2-L01 7" WVGA (800x480) TFT LCD panel. Signed-off-by: Christoph Fritz Reviewed-by: Rob Herring Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/1528111008.2818.20.camel@googlemail.com --- .../bindings/display/panel/innolux,g070y2-l01.txt | 12 +++++++ drivers/gpu/drm/panel/panel-simple.c | 37 ++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/display/panel/innolux,g070y2-l01.txt (limited to 'drivers/gpu/drm') diff --git a/Documentation/devicetree/bindings/display/panel/innolux,g070y2-l01.txt b/Documentation/devicetree/bindings/display/panel/innolux,g070y2-l01.txt new file mode 100644 index 000000000000..7c234cf68e11 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/innolux,g070y2-l01.txt @@ -0,0 +1,12 @@ +Innolux G070Y2-L01 7" WVGA (800x480) TFT LCD panel + +Required properties: +- compatible: should be "innolux,g070y2-l01" +- power-supply: as specified in the base binding + +Optional properties: +- backlight: as specified in the base binding +- enable-gpios: as specified in the base binding + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 2c4857e08a76..da7fbd2b2a75 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1176,6 +1176,36 @@ static const struct panel_desc innolux_at070tn92 = { .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; +static const struct display_timing innolux_g070y2_l01_timing = { + .pixelclock = { 28000000, 29500000, 32000000 }, + .hactive = { 800, 800, 800 }, + .hfront_porch = { 61, 91, 141 }, + .hback_porch = { 60, 90, 140 }, + .hsync_len = { 12, 12, 12 }, + .vactive = { 480, 480, 480 }, + .vfront_porch = { 4, 9, 30 }, + .vback_porch = { 4, 8, 28 }, + .vsync_len = { 2, 2, 2 }, + .flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc innolux_g070y2_l01 = { + .timings = &innolux_g070y2_l01_timing, + .num_timings = 1, + .bpc = 6, + .size = { + .width = 152, + .height = 91, + }, + .delay = { + .prepare = 10, + .enable = 100, + .disable = 100, + .unprepare = 800, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, +}; + static const struct display_timing innolux_g101ice_l01_timing = { .pixelclock = { 60400000, 71100000, 74700000 }, .hactive = { 1280, 1280, 1280 }, @@ -2341,10 +2371,13 @@ static const struct of_device_id platform_of_match[] = { .compatible = "innolux,at070tn92", .data = &innolux_at070tn92, }, { - .compatible ="innolux,g101ice-l01", + .compatible = "innolux,g070y2-l01", + .data = &innolux_g070y2_l01, + }, { + .compatible = "innolux,g101ice-l01", .data = &innolux_g101ice_l01 }, { - .compatible ="innolux,g121i1-l01", + .compatible = "innolux,g121i1-l01", .data = &innolux_g121i1_l01 }, { .compatible = "innolux,g121x1-l03", -- cgit v1.2.3-55-g7522 From 97ceb1fb08b6a2f78aa44a7c229ca280964860c0 Mon Sep 17 00:00:00 2001 From: Michal Vokáč Date: Mon, 25 Jun 2018 14:41:30 +0200 Subject: drm/panel: simple: Add support for DataImage SCF0700C48GGU18 This adds support for the DataImage SCF0700C48GGU18 7.0" WVGA (800x480) TFT LCD panel. The panel has 24-bit parallel interface and can be supported by the simple panel driver. Signed-off-by: Michal Vokáč Reviewed-by: Rob Herring Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/1529930490-11874-2-git-send-email-michal.vokac@ysoft.com --- .../display/panel/dataimage,scf0700c48ggu18.txt | 8 ++++++ drivers/gpu/drm/panel/panel-simple.c | 29 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/dataimage,scf0700c48ggu18.txt (limited to 'drivers/gpu/drm') diff --git a/Documentation/devicetree/bindings/display/panel/dataimage,scf0700c48ggu18.txt b/Documentation/devicetree/bindings/display/panel/dataimage,scf0700c48ggu18.txt new file mode 100644 index 000000000000..897085ee3cd4 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/dataimage,scf0700c48ggu18.txt @@ -0,0 +1,8 @@ +DataImage, Inc. 7" WVGA (800x480) TFT LCD panel with 24-bit parallel interface. + +Required properties: +- compatible: should be "dataimage,scf0700c48ggu18" +- power-supply: as specified in the base binding + +This binding is compatible with the simple-panel binding, which is specified +in simple-panel.txt in this directory. diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index da7fbd2b2a75..3b226ad8acad 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -906,6 +906,32 @@ static const struct panel_desc chunghwa_claa101wb01 = { }, }; +static const struct drm_display_mode dataimage_scf0700c48ggu18_mode = { + .clock = 33260, + .hdisplay = 800, + .hsync_start = 800 + 40, + .hsync_end = 800 + 40 + 128, + .htotal = 800 + 40 + 128 + 88, + .vdisplay = 480, + .vsync_start = 480 + 10, + .vsync_end = 480 + 10 + 2, + .vtotal = 480 + 10 + 2 + 33, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}; + +static const struct panel_desc dataimage_scf0700c48ggu18 = { + .modes = &dataimage_scf0700c48ggu18_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 152, + .height = 91, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE, +}; + static const struct display_timing dlc_dlc0700yzg_1_timing = { .pixelclock = { 45000000, 51200000, 57000000 }, .hactive = { 1024, 1024, 1024 }, @@ -2331,6 +2357,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "chunghwa,claa101wb01", .data = &chunghwa_claa101wb01 + }, { + .compatible = "dataimage,scf0700c48ggu18", + .data = &dataimage_scf0700c48ggu18, }, { .compatible = "dlc,dlc0700yzg-1", .data = &dlc_dlc0700yzg_1, -- cgit v1.2.3-55-g7522 From 3d5664f95ebe264c16f3ebfadce9eee6fee9acf7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 26 Jun 2018 17:03:54 +0100 Subject: drm/panel: ili9881c: Fix missing assignment to error return ret Currently, ret is being checked for an error condition however it is not being assigned in the previous statement on the call of function mipi_dsi_dcs_exit_sleep_mode. Add in the missing assignment of ret. Detected by CoverityScan, CID#1470174, 1470178 ("Unchecked return value") Fixes: 26aec25593c2 ("drm/panel: Add Ilitek ILI9881c panel driver") Signed-off-by: Colin Ian King Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180626160354.1363-1-colin.king@canonical.com --- drivers/gpu/drm/panel/panel-ilitek-ili9881c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index e848af235df5..3ad4a46c4e94 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -334,7 +334,7 @@ static int ili9881c_prepare(struct drm_panel *panel) if (ret) return ret; - mipi_dsi_dcs_exit_sleep_mode(ctx->dsi); + ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi); if (ret) return ret; -- cgit v1.2.3-55-g7522 From 7ad4e4636c54dcfbcaf43c8737af3c294ed17d11 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Mon, 2 Jul 2018 12:27:18 +0200 Subject: drm/panel: p079zca: Refactor panel driver to support multiple panels Refactor Innolux P079ZCA panel driver, let it support multi panels from Innolux that share similar power sequences. Panels may require different power supplies so use regulator bulk interfaces and define per panel supply-names. Changes in v2: - Change regulator property name to meet the panel datasheet Changes in v3: - this patch only refactor P079ZCA panel to support multi panel, support P097PFG panel in another patch Changes in v4: - Modify the patch which suggest by Thierry Changes in v5: - use regulator_bulk to handle different supply number Signed-off-by: Lin Huang Signed-off-by: Heiko Stuebner Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20180702102721.3546-2-heiko@sntech.de --- drivers/gpu/drm/panel/panel-innolux-p079zca.c | 143 ++++++++++++++++++-------- 1 file changed, 102 insertions(+), 41 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index bb53e0850764..185a55060195 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,12 +20,29 @@ #include