From 3e138a63d6674a4567a018a31e467567c40b14d5 Mon Sep 17 00:00:00 2001 From: Torsten Duwe Date: Tue, 18 Feb 2020 16:57:44 +0100 Subject: [PATCH 01/78] drm/bridge: analogix-anx78xx: Fix drm_dp_link helper removal drm_dp_link_rate_to_bw_code and ...bw_code_to_link_rate simply divide by and multiply with 27000, respectively. Avoid an overflow in the u8 dpcd[0] and the multiply+divide alltogether. Signed-off-by: Torsten Duwe Fixes: ff1e8fb68ea0 ("drm/bridge: analogix-anx78xx: Avoid drm_dp_link helpers") Cc: Thierry Reding Cc: Daniel Vetter Cc: Andrzej Hajda Cc: Neil Armstrong Cc: Laurent Pinchart Cc: Jonas Karlman Cc: Jernej Skrabec Cc: # v5.5+ Reviewed-by: Thierry Reding Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20200218155744.9675368BE1@verein.lst.de --- drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index 41867be03751..864423f59d66 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -722,10 +722,9 @@ static int anx78xx_dp_link_training(struct anx78xx *anx78xx) if (err) return err; - dpcd[0] = drm_dp_max_link_rate(anx78xx->dpcd); - dpcd[0] = drm_dp_link_rate_to_bw_code(dpcd[0]); err = regmap_write(anx78xx->map[I2C_IDX_TX_P0], - SP_DP_MAIN_LINK_BW_SET_REG, dpcd[0]); + SP_DP_MAIN_LINK_BW_SET_REG, + anx78xx->dpcd[DP_MAX_LINK_RATE]); if (err) return err; From 6726ca1a2d531f5a6efc1f785b15606ce837c4dc Mon Sep 17 00:00:00 2001 From: Torsten Duwe Date: Tue, 18 Feb 2020 16:54:40 +0100 Subject: [PATCH 02/78] drm/bridge: analogix-anx6345: Avoid duplicate -supply suffix of_get_regulator() will unconditionally add "-supply" to form the property name. This is documented in commit 69511a452e6dc ("map consumer regulator based on device tree"). Remove the suffix from the requests. Signed-off-by: Torsten Duwe Reviewed-by: Mark Brown Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20200218155440.BEFB968C65@verein.lst.de --- drivers/gpu/drm/bridge/analogix/analogix-anx6345.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c index 56f55c53abfd..0d8d083b0207 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -712,14 +712,14 @@ static int anx6345_i2c_probe(struct i2c_client *client, DRM_DEBUG("No panel found\n"); /* 1.2V digital core power regulator */ - anx6345->dvdd12 = devm_regulator_get(dev, "dvdd12-supply"); + anx6345->dvdd12 = devm_regulator_get(dev, "dvdd12"); if (IS_ERR(anx6345->dvdd12)) { DRM_ERROR("dvdd12-supply not found\n"); return PTR_ERR(anx6345->dvdd12); } /* 2.5V digital core power regulator */ - anx6345->dvdd25 = devm_regulator_get(dev, "dvdd25-supply"); + anx6345->dvdd25 = devm_regulator_get(dev, "dvdd25"); if (IS_ERR(anx6345->dvdd25)) { DRM_ERROR("dvdd25-supply not found\n"); return PTR_ERR(anx6345->dvdd25); From cf650f2cba8efaf6a5e29baf718eb037f2dbf126 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Fri, 14 Feb 2020 13:09:34 +0100 Subject: [PATCH 03/78] drm/sun4i: tcon: Support LVDS on the A33 The A33 TCON supports LVDS, so we can toggle the support switch. Signed-off-by: Maxime Ripard Acked-by: Chen-Yu Tsai Link: https://patchwork.freedesktop.org/patch/msgid/20200214120934.107924-1-maxime@cerno.tech --- drivers/gpu/drm/sun4i/sun4i_tcon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index df8bd30b67f0..624437b27cdc 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -1501,6 +1501,7 @@ static const struct sun4i_tcon_quirks sun8i_a33_quirks = { .has_lvds_alt = true, .dclk_min_div = 1, .setup_lvds_phy = sun6i_tcon_setup_lvds_phy, + .supports_lvds = true, }; static const struct sun4i_tcon_quirks sun8i_a83t_lcd_quirks = { From fb0629eeeedb6622c16da1aa76a4520daf9a46e2 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 14 Nov 2019 11:39:50 +0200 Subject: [PATCH 04/78] drm/panel: simple: fix osd070t1718_19ts sync drive edge The panel datasheet says that the panel samples at falling edge, but does not say anything about h/v sync signals. Testing shows that if the sync signals are driven on falling edge, the picture on the panel will be slightly shifted right. Setting sync drive edge to the same as data drive edge fixes this issue. Signed-off-by: Tomi Valkeinen Acked-by: Laurent Pinchart Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20191114093950.4101-4-tomi.valkeinen@ti.com --- drivers/gpu/drm/panel/panel-simple.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 82363d05bad4..05f1397e7ad1 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2580,7 +2580,8 @@ static const struct panel_desc osddisplays_osd070t1718_19ts = { .height = 91, }, .bus_format = MEDIA_BUS_FMT_RGB888_1X24, - .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE | + DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE, .connector_type = DRM_MODE_CONNECTOR_DPI, }; From 6915db346039b3dfe65496d36de958cc7b12969b Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 20 Feb 2020 13:07:00 +0100 Subject: [PATCH 05/78] drm/panel: ld9040: add MODULE_DEVICE_TABLE with SPI IDs Add proper MODULE_DEVICE_TABLE structure with SPI IDs to allow proper creation of SPI modalias string and fix autoloading module for this driver. Signed-off-by: Marek Szyprowski Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200220120700.12257-1-m.szyprowski@samsung.com --- drivers/gpu/drm/panel/panel-samsung-ld9040.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-samsung-ld9040.c b/drivers/gpu/drm/panel/panel-samsung-ld9040.c index 3c52f15f7a1c..9bb2e8c7934a 100644 --- a/drivers/gpu/drm/panel/panel-samsung-ld9040.c +++ b/drivers/gpu/drm/panel/panel-samsung-ld9040.c @@ -373,6 +373,12 @@ static const struct of_device_id ld9040_of_match[] = { }; MODULE_DEVICE_TABLE(of, ld9040_of_match); +static const struct spi_device_id ld9040_ids[] = { + { "ld9040", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, ld9040_ids); + static struct spi_driver ld9040_driver = { .probe = ld9040_probe, .remove = ld9040_remove, From a8dada7a84dad2e024eb23313b8d9ca68ab198a2 Mon Sep 17 00:00:00 2001 From: Xinliang Liu Date: Thu, 20 Feb 2020 09:03:28 +0000 Subject: [PATCH 06/78] MAINTAINERS: Update myself email address Update myself email address. Add John Stultz as a reviewer. Thanks John. Update git tree to drm-misc Signed-off-by: Xinliang Liu Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200220090328.25932-1-xinliang.liu@linaro.org --- MAINTAINERS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 978766c6715e..befc3c0afc75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5600,12 +5600,13 @@ S: Maintained F: drivers/gpu/drm/gma500/ DRM DRIVERS FOR HISILICON -M: Xinliang Liu +M: Xinliang Liu M: Rongrong Zou +R: John Stultz R: Xinwei Kong R: Chen Feng L: dri-devel@lists.freedesktop.org -T: git git://github.com/xin3liang/linux.git +T: git git://anongit.freedesktop.org/drm/drm-misc S: Maintained F: drivers/gpu/drm/hisilicon/ F: Documentation/devicetree/bindings/display/hisilicon/ From 30349f8f643febc8d94684bc31e847b84bab2c05 Mon Sep 17 00:00:00 2001 From: Gurchetan Singh Date: Mon, 24 Feb 2020 16:07:57 -0800 Subject: [PATCH 07/78] drm/virtio: use consistent names for drm_files Minor cleanup, change: - file_priv--> file, - drm_file --> file. Reviewed-by: Chia-I Wu Reviewed-by: Emil Velikov Signed-off-by: Gurchetan Singh Link: http://patchwork.freedesktop.org/patch/msgid/20200225000800.2966-1-gurchetansingh@chromium.org Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_ioctl.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index bbc31aef51f1..baad7e1c9505 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -34,12 +34,12 @@ #include "virtgpu_drv.h" static int virtio_gpu_map_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file) { struct virtio_gpu_device *vgdev = dev->dev_private; struct drm_virtgpu_map *virtio_gpu_map = data; - return virtio_gpu_mode_dumb_mmap(file_priv, vgdev->ddev, + return virtio_gpu_mode_dumb_mmap(file, vgdev->ddev, virtio_gpu_map->handle, &virtio_gpu_map->offset); } @@ -51,11 +51,11 @@ static int virtio_gpu_map_ioctl(struct drm_device *dev, void *data, * VIRTIO_GPUReleaseInfo struct (first XXX bytes) */ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, - struct drm_file *drm_file) + struct drm_file *file) { struct drm_virtgpu_execbuffer *exbuf = data; struct virtio_gpu_device *vgdev = dev->dev_private; - struct virtio_gpu_fpriv *vfpriv = drm_file->driver_priv; + struct virtio_gpu_fpriv *vfpriv = file->driver_priv; struct virtio_gpu_fence *out_fence; int ret; uint32_t *bo_handles = NULL; @@ -116,7 +116,7 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, goto out_unused_fd; } - buflist = virtio_gpu_array_from_handles(drm_file, bo_handles, + buflist = virtio_gpu_array_from_handles(file, bo_handles, exbuf->num_bo_handles); if (!buflist) { ret = -ENOENT; @@ -178,7 +178,7 @@ out_unused_fd: } static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file) { struct virtio_gpu_device *vgdev = dev->dev_private; struct drm_virtgpu_getparam *param = data; @@ -201,7 +201,7 @@ static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data, } static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file) { struct virtio_gpu_device *vgdev = dev->dev_private; struct drm_virtgpu_resource_create *rc = data; @@ -252,7 +252,7 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, return ret; obj = &qobj->base.base; - ret = drm_gem_handle_create(file_priv, obj, &handle); + ret = drm_gem_handle_create(file, obj, &handle); if (ret) { drm_gem_object_release(obj); return ret; @@ -265,13 +265,13 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, } static int virtio_gpu_resource_info_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file) { struct drm_virtgpu_resource_info *ri = data; struct drm_gem_object *gobj = NULL; struct virtio_gpu_object *qobj = NULL; - gobj = drm_gem_object_lookup(file_priv, ri->bo_handle); + gobj = drm_gem_object_lookup(file, ri->bo_handle); if (gobj == NULL) return -ENOENT; From 40cadedd20a5fefba7ebca7cee7a8743a55e2509 Mon Sep 17 00:00:00 2001 From: Gurchetan Singh Date: Mon, 24 Feb 2020 16:07:58 -0800 Subject: [PATCH 08/78] drm/virtio: factor out context create hypercall We currently create an OpenGL context when opening the DRM fd if 3D is available. We may need other context types (VK,..) in the future, and the plan is to have explicit initialization for that. For explicit initialization to work, we need to factor out virtio_gpu_create_context from driver initialization. v2: Move context handle initialization too (olv) v6: Remove redundant 3D check (emil.velikov) Reviewed-by: Chia-I Wu Reviewed-by: Emil Velikov Signed-off-by: Gurchetan Singh Link: http://patchwork.freedesktop.org/patch/msgid/20200225000800.2966-2-gurchetansingh@chromium.org Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_drv.h | 2 ++ drivers/gpu/drm/virtio/virtgpu_ioctl.c | 13 +++++++++++++ drivers/gpu/drm/virtio/virtgpu_kms.c | 26 ++++++-------------------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 2f6c4ccbfd14..72c1d9b59dfe 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -214,6 +214,8 @@ struct virtio_gpu_fpriv { /* virtio_ioctl.c */ #define DRM_VIRTIO_NUM_IOCTLS 10 extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS]; +void virtio_gpu_create_context(struct drm_device *dev, + struct drm_file *file); /* virtio_kms.c */ int virtio_gpu_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index baad7e1c9505..00ef9fd3fbf6 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -33,6 +33,19 @@ #include "virtgpu_drv.h" +void virtio_gpu_create_context(struct drm_device *dev, + struct drm_file *file) +{ + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_fpriv *vfpriv = file->driver_priv; + char dbgname[TASK_COMM_LEN]; + + get_task_comm(dbgname, current); + virtio_gpu_cmd_context_create(vgdev, vfpriv->ctx_id, + strlen(dbgname), dbgname); + virtio_gpu_notify(vgdev); +} + static int virtio_gpu_map_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index ad3b673f5796..f7e3712502ca 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -52,19 +52,6 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) events_clear, &events_clear); } -static int virtio_gpu_context_create(struct virtio_gpu_device *vgdev, - uint32_t nlen, const char *name) -{ - int handle = ida_alloc(&vgdev->ctx_id_ida, GFP_KERNEL); - - if (handle < 0) - return handle; - handle += 1; - virtio_gpu_cmd_context_create(vgdev, handle, nlen, name); - virtio_gpu_notify(vgdev); - return handle; -} - static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev, uint32_t ctx_id) { @@ -260,8 +247,7 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) { struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_fpriv *vfpriv; - int id; - char dbgname[TASK_COMM_LEN]; + int handle; /* can't create contexts without 3d renderer */ if (!vgdev->has_virgl_3d) @@ -272,15 +258,15 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) if (!vfpriv) return -ENOMEM; - get_task_comm(dbgname, current); - id = virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname); - if (id < 0) { + handle = ida_alloc(&vgdev->ctx_id_ida, GFP_KERNEL); + if (handle < 0) { kfree(vfpriv); - return id; + return handle; } - vfpriv->ctx_id = id; + vfpriv->ctx_id = handle + 1; file->driver_priv = vfpriv; + virtio_gpu_create_context(dev, file); return 0; } From d2a983b220ac6628c53e4cf27a7335d1d29ce5e9 Mon Sep 17 00:00:00 2001 From: Gurchetan Singh Date: Mon, 24 Feb 2020 16:07:59 -0800 Subject: [PATCH 09/78] drm/virtio: track whether or not a context has been initiated Use an boolean variable to track whether a context has been initiated. v5: Fix possible race and sleep via mutex (olv) Reviewed-by: Chia-I Wu Reviewed-by: Emil Velikov Signed-off-by: Gurchetan Singh Link: http://patchwork.freedesktop.org/patch/msgid/20200225000800.2966-3-gurchetansingh@chromium.org Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_drv.h | 2 ++ drivers/gpu/drm/virtio/virtgpu_ioctl.c | 8 ++++++++ drivers/gpu/drm/virtio/virtgpu_kms.c | 3 +++ 3 files changed, 13 insertions(+) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 72c1d9b59dfe..76b7b7c30e10 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -209,6 +209,8 @@ struct virtio_gpu_device { struct virtio_gpu_fpriv { uint32_t ctx_id; + bool context_created; + struct mutex context_lock; }; /* virtio_ioctl.c */ diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index 00ef9fd3fbf6..ec38cf5573aa 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -40,10 +40,18 @@ void virtio_gpu_create_context(struct drm_device *dev, struct virtio_gpu_fpriv *vfpriv = file->driver_priv; char dbgname[TASK_COMM_LEN]; + mutex_lock(&vfpriv->context_lock); + if (vfpriv->context_created) + goto out_unlock; + get_task_comm(dbgname, current); virtio_gpu_cmd_context_create(vgdev, vfpriv->ctx_id, strlen(dbgname), dbgname); virtio_gpu_notify(vgdev); + vfpriv->context_created = true; + +out_unlock: + mutex_unlock(&vfpriv->context_lock); } static int virtio_gpu_map_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index f7e3712502ca..424729cb81d1 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -258,6 +258,8 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) if (!vfpriv) return -ENOMEM; + mutex_init(&vfpriv->context_lock); + handle = ida_alloc(&vgdev->ctx_id_ida, GFP_KERNEL); if (handle < 0) { kfree(vfpriv); @@ -281,6 +283,7 @@ void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file) vfpriv = file->driver_priv; virtio_gpu_context_destroy(vgdev, vfpriv->ctx_id); + mutex_destroy(&vfpriv->context_lock); kfree(vfpriv); file->driver_priv = NULL; } From 72b48ae800da8ac7d9a43dc667745eee5b5d28a5 Mon Sep 17 00:00:00 2001 From: Gurchetan Singh Date: Mon, 24 Feb 2020 16:08:00 -0800 Subject: [PATCH 10/78] drm/virtio: enqueue virtio_gpu_create_context after the first 3D ioctl For old userspace, initialization will still be implicit. For backwards compatibility, enqueue virtio_gpu_cmd_context_create after the first 3D ioctl. v3: staticify virtio_gpu_create_context remove notify to batch vm-exit v6: Remove nested 3D checks (emil.velikov): - unify 3D check in resource create v7: Remove check when getting capabilities Reviewed-by: Chia-I Wu Reviewed-by: Emil Velikov Signed-off-by: Gurchetan Singh Link: http://patchwork.freedesktop.org/patch/msgid/20200225000800.2966-4-gurchetansingh@chromium.org Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_drv.h | 2 -- drivers/gpu/drm/virtio/virtgpu_ioctl.c | 29 ++++++++++++++------------ drivers/gpu/drm/virtio/virtgpu_kms.c | 1 - 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 76b7b7c30e10..95a7443baaba 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -216,8 +216,6 @@ struct virtio_gpu_fpriv { /* virtio_ioctl.c */ #define DRM_VIRTIO_NUM_IOCTLS 10 extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS]; -void virtio_gpu_create_context(struct drm_device *dev, - struct drm_file *file); /* virtio_kms.c */ int virtio_gpu_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index ec38cf5573aa..336cc9143205 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -33,8 +33,8 @@ #include "virtgpu_drv.h" -void virtio_gpu_create_context(struct drm_device *dev, - struct drm_file *file) +static void virtio_gpu_create_context(struct drm_device *dev, + struct drm_file *file) { struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_fpriv *vfpriv = file->driver_priv; @@ -95,6 +95,7 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, exbuf->fence_fd = -1; + virtio_gpu_create_context(dev, file); if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_IN) { struct dma_fence *in_fence; @@ -233,7 +234,17 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, uint32_t handle = 0; struct virtio_gpu_object_params params = { 0 }; - if (vgdev->has_virgl_3d == false) { + if (vgdev->has_virgl_3d) { + virtio_gpu_create_context(dev, file); + params.virgl = true; + params.target = rc->target; + params.bind = rc->bind; + params.depth = rc->depth; + params.array_size = rc->array_size; + params.last_level = rc->last_level; + params.nr_samples = rc->nr_samples; + params.flags = rc->flags; + } else { if (rc->depth > 1) return -EINVAL; if (rc->nr_samples > 1) @@ -250,16 +261,6 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, params.width = rc->width; params.height = rc->height; params.size = rc->size; - if (vgdev->has_virgl_3d) { - params.virgl = true; - params.target = rc->target; - params.bind = rc->bind; - params.depth = rc->depth; - params.array_size = rc->array_size; - params.last_level = rc->last_level; - params.nr_samples = rc->nr_samples; - params.flags = rc->flags; - } /* allocate a single page size object */ if (params.size == 0) params.size = PAGE_SIZE; @@ -319,6 +320,7 @@ static int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev, if (vgdev->has_virgl_3d == false) return -ENOSYS; + virtio_gpu_create_context(dev, file); objs = virtio_gpu_array_from_handles(file, &args->bo_handle, 1); if (objs == NULL) return -ENOENT; @@ -367,6 +369,7 @@ static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data, args->box.w, args->box.h, args->box.x, args->box.y, objs, NULL); } else { + virtio_gpu_create_context(dev, file); ret = virtio_gpu_array_lock_resv(objs); if (ret != 0) goto err_put_free; diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 424729cb81d1..023a030ca7b9 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -268,7 +268,6 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) vfpriv->ctx_id = handle + 1; file->driver_priv = vfpriv; - virtio_gpu_create_context(dev, file); return 0; } From 65c04add090f029abe8255f28448d9a12dd27828 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 25 Feb 2020 00:56:45 +0200 Subject: [PATCH 11/78] drm/bridge: lvds-codec: Add to_lvds_codec() function Factor out the manual container_of() uses to a common to_lvds_codec() macro to shorten lines (and provide better type safety, although that won't matter much in this case). Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Signed-off-by: Boris Brezillon Link: https://patchwork.freedesktop.org/patch/msgid/20200224225645.28060-1-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/lvds-codec.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index 5f04cc11227e..ae173d84dc8f 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -21,10 +21,14 @@ struct lvds_codec { u32 connector_type; }; +static inline struct lvds_codec *to_lvds_codec(struct drm_bridge *bridge) +{ + return container_of(bridge, struct lvds_codec, bridge); +} + static int lvds_codec_attach(struct drm_bridge *bridge) { - struct lvds_codec *lvds_codec = container_of(bridge, - struct lvds_codec, bridge); + struct lvds_codec *lvds_codec = to_lvds_codec(bridge); return drm_bridge_attach(bridge->encoder, lvds_codec->panel_bridge, bridge); @@ -32,8 +36,7 @@ static int lvds_codec_attach(struct drm_bridge *bridge) static void lvds_codec_enable(struct drm_bridge *bridge) { - struct lvds_codec *lvds_codec = container_of(bridge, - struct lvds_codec, bridge); + struct lvds_codec *lvds_codec = to_lvds_codec(bridge); if (lvds_codec->powerdown_gpio) gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 0); @@ -41,8 +44,7 @@ static void lvds_codec_enable(struct drm_bridge *bridge) static void lvds_codec_disable(struct drm_bridge *bridge) { - struct lvds_codec *lvds_codec = container_of(bridge, - struct lvds_codec, bridge); + struct lvds_codec *lvds_codec = to_lvds_codec(bridge); if (lvds_codec->powerdown_gpio) gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 1); From a628a073157b59bd3af7225331f89ba5bd6abb7a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 25 Feb 2020 01:00:56 +0200 Subject: [PATCH 12/78] drm/bridge: lvds-codec: Constify the drm_bridge_funcs structure The drm_bridge_funcs structure is never modified, make it const. Making it read-only can improve security as the structure contains function pointers. Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Signed-off-by: Boris Brezillon Link: https://patchwork.freedesktop.org/patch/msgid/20200224230056.2157-1-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/lvds-codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index ae173d84dc8f..f7ae28ed1c14 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -50,7 +50,7 @@ static void lvds_codec_disable(struct drm_bridge *bridge) gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 1); } -static struct drm_bridge_funcs funcs = { +static const struct drm_bridge_funcs funcs = { .attach = lvds_codec_attach, .enable = lvds_codec_enable, .disable = lvds_codec_disable, From cf52925a862e16ec0151627f64446f0eba853e45 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 28 Jan 2020 14:55:12 +0100 Subject: [PATCH 13/78] drm/bridge: panel: Propagate bus format/flags Propagate bus format/flags so that the previous bridge element in the chain knows which input format the panel bridge expects. v11: * Fix a typo in the subject * Update the commit message so it's readable by itself v10: * Add changelog to the commit message v8 -> v9: * No changes v7: * Set atomic state hooks explicitly v4 -> v6: * Not part of the series v3: * Adjust things to match the new bus-format negotiation approach * Use drm_atomic_helper_bridge_propagate_bus_fmt * Don't implement ->atomic_check() (the core now takes care of bus flags propagation) v2: * Adjust things to match the new bus-format negotiation approach Signed-off-by: Boris Brezillon Reviewed-by: Laurent Pinchart Link: https://patchwork.freedesktop.org/patch/msgid/20200128135514.108171-11-boris.brezillon@collabora.com --- drivers/gpu/drm/bridge/panel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index fa764f9a91b6..9660736ddbf3 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -127,6 +127,10 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .enable = panel_bridge_enable, .disable = panel_bridge_disable, .post_disable = panel_bridge_post_disable, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, }; /** From a9d73b30bb46afb2da40257d680249dc029702d4 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 7 Feb 2020 13:26:23 +0800 Subject: [PATCH 14/78] drm/panfrost: Improve error reporting in panfrost_gpu_power_on It is useful to know which component cannot be powered on. Signed-off-by: Nicolas Boichat Reviewed-by: Alyssa Rosenzweig Reviewed-by: Steven Price Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20200207052627.130118-4-drinkcat@chromium.org --- drivers/gpu/drm/panfrost/panfrost_gpu.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c index 1b9b79cd5804..f2c1ddc41a9b 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c @@ -308,17 +308,20 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev) gpu_write(pfdev, L2_PWRON_LO, pfdev->features.l2_present); ret = readl_relaxed_poll_timeout(pfdev->iomem + L2_READY_LO, val, val == pfdev->features.l2_present, 100, 1000); + if (ret) + dev_err(pfdev->dev, "error powering up gpu L2"); gpu_write(pfdev, SHADER_PWRON_LO, pfdev->features.shader_present); - ret |= readl_relaxed_poll_timeout(pfdev->iomem + SHADER_READY_LO, + ret = readl_relaxed_poll_timeout(pfdev->iomem + SHADER_READY_LO, val, val == pfdev->features.shader_present, 100, 1000); + if (ret) + dev_err(pfdev->dev, "error powering up gpu shader"); gpu_write(pfdev, TILER_PWRON_LO, pfdev->features.tiler_present); - ret |= readl_relaxed_poll_timeout(pfdev->iomem + TILER_READY_LO, + ret = readl_relaxed_poll_timeout(pfdev->iomem + TILER_READY_LO, val, val == pfdev->features.tiler_present, 100, 1000); - if (ret) - dev_err(pfdev->dev, "error powering up gpu"); + dev_err(pfdev->dev, "error powering up gpu tiler"); } void panfrost_gpu_power_off(struct panfrost_device *pfdev) From 3e1399bccf5163c59f41298b4faf768e199f4322 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 7 Feb 2020 13:26:24 +0800 Subject: [PATCH 15/78] drm/panfrost: Add support for multiple regulators Some GPUs, namely, the bifrost/g72 part on MT8183, have a second regulator for their SRAM, let's add support for that. We extend the framework in a generic manner so that we could support more than 2 regulators, if required. Signed-off-by: Nicolas Boichat Reviewed-by: Steven Price Reviwed-by: Mark Brown Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20200207052627.130118-5-drinkcat@chromium.org --- drivers/gpu/drm/panfrost/panfrost_device.c | 26 +++++++++++++------- drivers/gpu/drm/panfrost/panfrost_device.h | 15 +++++++++++- drivers/gpu/drm/panfrost/panfrost_drv.c | 28 +++++++++++++++------- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 238fb6d54df4..3720d50f6d9f 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -87,18 +87,27 @@ static void panfrost_clk_fini(struct panfrost_device *pfdev) static int panfrost_regulator_init(struct panfrost_device *pfdev) { - int ret; + int ret, i; - pfdev->regulator = devm_regulator_get(pfdev->dev, "mali"); - if (IS_ERR(pfdev->regulator)) { - ret = PTR_ERR(pfdev->regulator); - dev_err(pfdev->dev, "failed to get regulator: %d\n", ret); + if (WARN(pfdev->comp->num_supplies > ARRAY_SIZE(pfdev->regulators), + "Too many supplies in compatible structure.\n")) + return -EINVAL; + + for (i = 0; i < pfdev->comp->num_supplies; i++) + pfdev->regulators[i].supply = pfdev->comp->supply_names[i]; + + ret = devm_regulator_bulk_get(pfdev->dev, + pfdev->comp->num_supplies, + pfdev->regulators); + if (ret < 0) { + dev_err(pfdev->dev, "failed to get regulators: %d\n", ret); return ret; } - ret = regulator_enable(pfdev->regulator); + ret = regulator_bulk_enable(pfdev->comp->num_supplies, + pfdev->regulators); if (ret < 0) { - dev_err(pfdev->dev, "failed to enable regulator: %d\n", ret); + dev_err(pfdev->dev, "failed to enable regulators: %d\n", ret); return ret; } @@ -107,7 +116,8 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev) static void panfrost_regulator_fini(struct panfrost_device *pfdev) { - regulator_disable(pfdev->regulator); + regulator_bulk_disable(pfdev->comp->num_supplies, + pfdev->regulators); } int panfrost_device_init(struct panfrost_device *pfdev) diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index 06713811b92c..c9468bc5573a 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,7 @@ struct panfrost_job; struct panfrost_perfcnt; #define NUM_JOB_SLOTS 3 +#define MAX_REGULATORS 2 struct panfrost_features { u16 id; @@ -51,6 +53,16 @@ struct panfrost_features { unsigned long hw_issues[64 / BITS_PER_LONG]; }; +/* + * Features that cannot be automatically detected and need matching using the + * compatible string, typically SoC-specific. + */ +struct panfrost_compatible { + /* Supplies count and names. */ + int num_supplies; + const char * const *supply_names; +}; + struct panfrost_device { struct device *dev; struct drm_device *ddev; @@ -59,10 +71,11 @@ struct panfrost_device { void __iomem *iomem; struct clk *clock; struct clk *bus_clock; - struct regulator *regulator; + struct regulator_bulk_data regulators[MAX_REGULATORS]; struct reset_control *rstc; struct panfrost_features features; + const struct panfrost_compatible *comp; spinlock_t as_lock; unsigned long as_in_use_mask; diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index b7a618db3ee2..4d0850752623 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -584,6 +584,10 @@ static int panfrost_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pfdev); + pfdev->comp = of_device_get_match_data(&pdev->dev); + if (!pfdev->comp) + return -ENODEV; + /* Allocate and initialze the DRM device. */ ddev = drm_dev_alloc(&panfrost_drm_driver, &pdev->dev); if (IS_ERR(ddev)) @@ -655,16 +659,22 @@ static int panfrost_remove(struct platform_device *pdev) return 0; } +const char * const default_supplies[] = { "mali" }; +static const struct panfrost_compatible default_data = { + .num_supplies = ARRAY_SIZE(default_supplies), + .supply_names = default_supplies, +}; + static const struct of_device_id dt_match[] = { - { .compatible = "arm,mali-t604" }, - { .compatible = "arm,mali-t624" }, - { .compatible = "arm,mali-t628" }, - { .compatible = "arm,mali-t720" }, - { .compatible = "arm,mali-t760" }, - { .compatible = "arm,mali-t820" }, - { .compatible = "arm,mali-t830" }, - { .compatible = "arm,mali-t860" }, - { .compatible = "arm,mali-t880" }, + { .compatible = "arm,mali-t604", .data = &default_data, }, + { .compatible = "arm,mali-t624", .data = &default_data, }, + { .compatible = "arm,mali-t628", .data = &default_data, }, + { .compatible = "arm,mali-t720", .data = &default_data, }, + { .compatible = "arm,mali-t760", .data = &default_data, }, + { .compatible = "arm,mali-t820", .data = &default_data, }, + { .compatible = "arm,mali-t830", .data = &default_data, }, + { .compatible = "arm,mali-t860", .data = &default_data, }, + { .compatible = "arm,mali-t880", .data = &default_data, }, {} }; MODULE_DEVICE_TABLE(of, dt_match); From 506629c868d0b1d641fc3afa491ddbd85fa1bdc3 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 7 Feb 2020 13:26:25 +0800 Subject: [PATCH 16/78] drm/panfrost: Add support for multiple power domains When there is a single power domain per device, the core will ensure the power domain is switched on (so it is technically equivalent to having not power domain specified at all). However, when there are multiple domains, as in MT8183 Bifrost GPU, we need to handle them in driver code. Signed-off-by: Nicolas Boichat Reviewed-by: Ulf Hansson Reviewed-by: Steven Price Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20200207052627.130118-6-drinkcat@chromium.org --- drivers/gpu/drm/panfrost/panfrost_device.c | 97 ++++++++++++++++++++-- drivers/gpu/drm/panfrost/panfrost_device.h | 11 +++ drivers/gpu/drm/panfrost/panfrost_drv.c | 2 + 3 files changed, 102 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 3720d50f6d9f..8136babd3ba9 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "panfrost_device.h" @@ -120,6 +121,79 @@ static void panfrost_regulator_fini(struct panfrost_device *pfdev) pfdev->regulators); } +static void panfrost_pm_domain_fini(struct panfrost_device *pfdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pfdev->pm_domain_devs); i++) { + if (!pfdev->pm_domain_devs[i]) + break; + + if (pfdev->pm_domain_links[i]) + device_link_del(pfdev->pm_domain_links[i]); + + dev_pm_domain_detach(pfdev->pm_domain_devs[i], true); + } +} + +static int panfrost_pm_domain_init(struct panfrost_device *pfdev) +{ + int err; + int i, num_domains; + + num_domains = of_count_phandle_with_args(pfdev->dev->of_node, + "power-domains", + "#power-domain-cells"); + + /* + * Single domain is handled by the core, and, if only a single power + * the power domain is requested, the property is optional. + */ + if (num_domains < 2 && pfdev->comp->num_pm_domains < 2) + return 0; + + if (num_domains != pfdev->comp->num_pm_domains) { + dev_err(pfdev->dev, + "Incorrect number of power domains: %d provided, %d needed\n", + num_domains, pfdev->comp->num_pm_domains); + return -EINVAL; + } + + if (WARN(num_domains > ARRAY_SIZE(pfdev->pm_domain_devs), + "Too many supplies in compatible structure.\n")) + return -EINVAL; + + for (i = 0; i < num_domains; i++) { + pfdev->pm_domain_devs[i] = + dev_pm_domain_attach_by_name(pfdev->dev, + pfdev->comp->pm_domain_names[i]); + if (IS_ERR_OR_NULL(pfdev->pm_domain_devs[i])) { + err = PTR_ERR(pfdev->pm_domain_devs[i]) ? : -ENODATA; + pfdev->pm_domain_devs[i] = NULL; + dev_err(pfdev->dev, + "failed to get pm-domain %s(%d): %d\n", + pfdev->comp->pm_domain_names[i], i, err); + goto err; + } + + pfdev->pm_domain_links[i] = device_link_add(pfdev->dev, + pfdev->pm_domain_devs[i], DL_FLAG_PM_RUNTIME | + DL_FLAG_STATELESS | DL_FLAG_RPM_ACTIVE); + if (!pfdev->pm_domain_links[i]) { + dev_err(pfdev->pm_domain_devs[i], + "adding device link failed!\n"); + err = -ENODEV; + goto err; + } + } + + return 0; + +err: + panfrost_pm_domain_fini(pfdev); + return err; +} + int panfrost_device_init(struct panfrost_device *pfdev) { int err; @@ -150,37 +224,43 @@ int panfrost_device_init(struct panfrost_device *pfdev) goto err_out1; } + err = panfrost_pm_domain_init(pfdev); + if (err) + goto err_out2; + res = platform_get_resource(pfdev->pdev, IORESOURCE_MEM, 0); pfdev->iomem = devm_ioremap_resource(pfdev->dev, res); if (IS_ERR(pfdev->iomem)) { dev_err(pfdev->dev, "failed to ioremap iomem\n"); err = PTR_ERR(pfdev->iomem); - goto err_out2; + goto err_out3; } err = panfrost_gpu_init(pfdev); if (err) - goto err_out2; + goto err_out3; err = panfrost_mmu_init(pfdev); if (err) - goto err_out3; + goto err_out4; err = panfrost_job_init(pfdev); if (err) - goto err_out4; + goto err_out5; err = panfrost_perfcnt_init(pfdev); if (err) - goto err_out5; + goto err_out6; return 0; -err_out5: +err_out6: panfrost_job_fini(pfdev); -err_out4: +err_out5: panfrost_mmu_fini(pfdev); -err_out3: +err_out4: panfrost_gpu_fini(pfdev); +err_out3: + panfrost_pm_domain_fini(pfdev); err_out2: panfrost_reset_fini(pfdev); err_out1: @@ -196,6 +276,7 @@ void panfrost_device_fini(struct panfrost_device *pfdev) panfrost_job_fini(pfdev); panfrost_mmu_fini(pfdev); panfrost_gpu_fini(pfdev); + panfrost_pm_domain_fini(pfdev); panfrost_reset_fini(pfdev); panfrost_regulator_fini(pfdev); panfrost_clk_fini(pfdev); diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index c9468bc5573a..c30c719a8059 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -21,6 +21,7 @@ struct panfrost_perfcnt; #define NUM_JOB_SLOTS 3 #define MAX_REGULATORS 2 +#define MAX_PM_DOMAINS 3 struct panfrost_features { u16 id; @@ -61,6 +62,13 @@ struct panfrost_compatible { /* Supplies count and names. */ int num_supplies; const char * const *supply_names; + /* + * Number of power domains required, note that values 0 and 1 are + * handled identically, as only values > 1 need special handling. + */ + int num_pm_domains; + /* Only required if num_pm_domains > 1. */ + const char * const *pm_domain_names; }; struct panfrost_device { @@ -73,6 +81,9 @@ struct panfrost_device { struct clk *bus_clock; struct regulator_bulk_data regulators[MAX_REGULATORS]; struct reset_control *rstc; + /* pm_domains for devices with more than one. */ + struct device *pm_domain_devs[MAX_PM_DOMAINS]; + struct device_link *pm_domain_links[MAX_PM_DOMAINS]; struct panfrost_features features; const struct panfrost_compatible *comp; diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 4d0850752623..a6e162236d67 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -663,6 +663,8 @@ const char * const default_supplies[] = { "mali" }; static const struct panfrost_compatible default_data = { .num_supplies = ARRAY_SIZE(default_supplies), .supply_names = default_supplies, + .num_pm_domains = 1, /* optional */ + .pm_domain_names = NULL, }; static const struct of_device_id dt_match[] = { From 5ee0caf161a9a21b9410b29f3cae8e1e98d1b100 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:21 +0200 Subject: [PATCH 17/78] video: hdmi: Change return type of hdmi_avi_infoframe_init() to void The hdmi_avi_infoframe_init() never needs to return an error, change its return type to void. Signed-off-by: Laurent Pinchart Reviewed-by: Andrzej Hajda Acked-by: Bartlomiej Zolnierkiewicz Reviewed-by: Boris Brezillon Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-2-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/drm_edid.c | 5 +---- drivers/video/hdmi.c | 11 ++--------- include/linux/hdmi.h | 2 +- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index ec10dc3e859c..5f3bc3486fde 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5449,14 +5449,11 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, { enum hdmi_picture_aspect picture_aspect; u8 vic, hdmi_vic; - int err; if (!frame || !mode) return -EINVAL; - err = hdmi_avi_infoframe_init(frame); - if (err < 0) - return err; + hdmi_avi_infoframe_init(frame); if (mode->flags & DRM_MODE_FLAG_DBLCLK) frame->pixel_repeat = 1; diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 9c82e2a0a411..856a8c4e84a2 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -53,18 +53,14 @@ static void hdmi_infoframe_set_checksum(void *buffer, size_t size) /** * hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe * @frame: HDMI AVI infoframe - * - * Returns 0 on success or a negative error code on failure. */ -int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) +void hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) { memset(frame, 0, sizeof(*frame)); frame->type = HDMI_INFOFRAME_TYPE_AVI; frame->version = 2; frame->length = HDMI_AVI_INFOFRAME_SIZE; - - return 0; } EXPORT_SYMBOL(hdmi_avi_infoframe_init); @@ -1553,7 +1549,6 @@ static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, const void *buffer, size_t size) { const u8 *ptr = buffer; - int ret; if (size < HDMI_INFOFRAME_SIZE(AVI)) return -EINVAL; @@ -1566,9 +1561,7 @@ static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AVI)) != 0) return -EINVAL; - ret = hdmi_avi_infoframe_init(frame); - if (ret) - return ret; + hdmi_avi_infoframe_init(frame); ptr += HDMI_INFOFRAME_HEADER_SIZE; diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 9918a6c910c5..9613d796cfb1 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -207,7 +207,7 @@ struct hdmi_drm_infoframe { u16 max_fall; }; -int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame); +void hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame); ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, size_t size); ssize_t hdmi_avi_infoframe_pack_only(const struct hdmi_avi_infoframe *frame, From b35f90f2d4a63cf2bc427f5c4e33eb86d963e716 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:22 +0200 Subject: [PATCH 18/78] drm/connector: Add helper to get a connector type name drm_connector.c contains a map of connector types (DRM_MODE_CONNECTOR_*) to name strings, but doesn't expose it. This leads to drivers having to store a similar map. Add a new drm_get_connector_type_name() helper function that return a name string for a connector type. Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Reviewed-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-3-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/drm_connector.c | 15 +++++++++++++++ include/drm/drm_connector.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index f632ca05960e..644f0ad10671 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -111,6 +111,21 @@ void drm_connector_ida_destroy(void) ida_destroy(&drm_connector_enum_list[i].ida); } +/** + * drm_get_connector_type_name - return a string for connector type + * @type: The connector type (DRM_MODE_CONNECTOR_*) + * + * Returns: the name of the connector type, or NULL if the type is not valid. + */ +const char *drm_get_connector_type_name(unsigned int type) +{ + if (type < ARRAY_SIZE(drm_connector_enum_list)) + return drm_connector_enum_list[type].name; + + return NULL; +} +EXPORT_SYMBOL(drm_get_connector_type_name); + /** * drm_connector_get_cmdline_mode - reads the user's cmdline mode * @connector: connector to quwery diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index b3815371c271..c3bd5262db9c 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1518,6 +1518,7 @@ drm_connector_is_unregistered(struct drm_connector *connector) DRM_CONNECTOR_UNREGISTERED; } +const char *drm_get_connector_type_name(unsigned int connector_type); const char *drm_get_connector_status_name(enum drm_connector_status status); const char *drm_get_subpixel_order_name(enum subpixel_order order); const char *drm_get_dpms_name(int val); From a92d083d08b008d0507f1191c3610b3e185309eb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:23 +0200 Subject: [PATCH 19/78] drm/edid: Add flag to drm_display_info to identify HDMI sinks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drm_display_info structure contains many fields related to HDMI sinks, but none that identifies if a sink compliant with CEA-861 (EDID) shall be treated as an HDMI sink or a DVI sink. Add such a flag, and populate it according to section 8.3.3 ("DVI/HDMI Device Discrimination") of the HDMI v1.3 specification. Signed-off-by: Laurent Pinchart Reviewed-by: Andrzej Hajda Reviewed-by: Ville Syrjälä Reviewed-by: Daniel Vetter Reviewed-by: Boris Brezillon Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-4-laurent.pinchart@ideasonboard.com --- Documentation/gpu/todo.rst | 14 ++++++++++++++ drivers/gpu/drm/drm_edid.c | 6 ++++++ include/drm/drm_connector.h | 8 ++++++++ 3 files changed, 28 insertions(+) diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 370ac678106e..ccf5e8e34222 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -407,6 +407,20 @@ Contact: Daniel Vetter Level: Intermediate +Replace drm_detect_hdmi_monitor() with drm_display_info.is_hdmi +--------------------------------------------------------------- + +Once EDID is parsed, the monitor HDMI support information is available through +drm_display_info.is_hdmi. Many drivers still call drm_detect_hdmi_monitor() to +retrieve the same information, which is less efficient. + +Audit each individual driver calling drm_detect_hdmi_monitor() and switch to +drm_display_info.is_hdmi if applicable. + +Contact: Laurent Pinchart, respective driver maintainers + +Level: Intermediate + Core refactorings ================= diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 5f3bc3486fde..ad41764a4ebe 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -4647,6 +4647,9 @@ EXPORT_SYMBOL(drm_av_sync_delay); * * Parse the CEA extension according to CEA-861-B. * + * Drivers that have added the modes parsed from EDID to drm_display_info + * should use &drm_display_info.is_hdmi instead of calling this function. + * * Return: True if the monitor is HDMI, false if not or unknown. */ bool drm_detect_hdmi_monitor(struct edid *edid) @@ -4881,6 +4884,8 @@ drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db) struct drm_display_info *info = &connector->display_info; u8 len = cea_db_payload_len(db); + info->is_hdmi = true; + if (len >= 6) info->dvi_dual = db[6] & 1; if (len >= 7) @@ -4949,6 +4954,7 @@ drm_reset_display_info(struct drm_connector *connector) info->cea_rev = 0; info->max_tmds_clock = 0; info->dvi_dual = false; + info->is_hdmi = false; info->has_hdmi_infoframe = false; info->rgb_quant_range_selectable = false; memset(&info->hdmi, 0, sizeof(info->hdmi)); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index c3bd5262db9c..0df7a95ca5d9 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -434,6 +434,14 @@ struct drm_display_info { */ bool dvi_dual; + /** + * @is_hdmi: True if the sink is an HDMI device. + * + * This field shall be used instead of calling + * drm_detect_hdmi_monitor() when possible. + */ + bool is_hdmi; + /** * @has_hdmi_infoframe: Does the sink support the HDMI infoframe? */ From be39f3db0146152b1f0b55df0891b7479f7e9f46 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:24 +0200 Subject: [PATCH 20/78] drm/bridge: Document the drm_encoder.bridge_chain field as private The drm_encoder.bridge_chain is not meant to be touched manually by drivers. Make this clear in the documentation. Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-5-laurent.pinchart@ideasonboard.com --- include/drm/drm_encoder.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 5623994b6e9e..4370e039c015 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -174,7 +174,8 @@ struct drm_encoder { struct drm_crtc *crtc; /** - * @bridge_chain: Bridges attached to this encoder. + * @bridge_chain: Bridges attached to this encoder. Drivers shall not + * access this field directly. */ struct list_head bridge_chain; From 282f713c65f74b34df013759485995f7a7ddd4ab Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:25 +0200 Subject: [PATCH 21/78] drm/bridge: Fix atomic state ops documentation The drm_bridge_funcs atomic_state_duplicate and atomic_state_destroy operations are erroneously documented as having a default implementation if not implemented in bridge drivers. This isn't correct, fix the documentation. Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-6-laurent.pinchart@ideasonboard.com --- include/drm/drm_bridge.h | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 999faaaab9a1..38de129d5947 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -349,9 +349,11 @@ struct drm_bridge_funcs { * Duplicate the current bridge state object (which is guaranteed to be * non-NULL). * - * The atomic_duplicate_state() hook is optional. When not implemented - * the core allocates a drm_bridge_state object and calls - * __drm_atomic_helper_bridge_duplicate_state() to initialize it. + * The atomic_duplicate_state hook is mandatory if the bridge + * implements any of the atomic hooks, and should be left unassigned + * otherwise. For bridges that don't subclass &drm_bridge_state, the + * drm_atomic_helper_bridge_duplicate_state() helper function shall be + * used to implement this hook. * * RETURNS: * A valid drm_bridge_state object or NULL if the allocation fails. @@ -364,8 +366,11 @@ struct drm_bridge_funcs { * Destroy a bridge state object previously allocated by * &drm_bridge_funcs.atomic_duplicate_state(). * - * The atomic_destroy_state hook is optional. When not implemented the - * core calls kfree() on the state. + * The atomic_destroy_state hook is mandatory if the bridge implements + * any of the atomic hooks, and should be left unassigned otherwise. + * For bridges that don't subclass &drm_bridge_state, the + * drm_atomic_helper_bridge_destroy_state() helper function shall be + * used to implement this hook. */ void (*atomic_destroy_state)(struct drm_bridge *bridge, struct drm_bridge_state *state); @@ -474,7 +479,10 @@ struct drm_bridge_funcs { * This function is called at attach time. * * The atomic_reset hook is mandatory if the bridge implements any of - * the atomic hooks, and should be left unassigned otherwise. + * the atomic hooks, and should be left unassigned otherwise. For + * bridges that don't subclass &drm_bridge_state, the + * drm_atomic_helper_bridge_reset() helper function shall be used to + * implement this hook. * * Note that the atomic_reset() semantics is not exactly matching the * reset() semantics found on other components (connector, plane, ...). From 0451369bc5c3eda098df27f1638902fae073a0d7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:26 +0200 Subject: [PATCH 22/78] drm/bridge: Improve overview documentation Clean up the drm_bridge overview documentation, and expand the operations documentation to provide more details on API usage. Signed-off-by: Laurent Pinchart Reviewed-by: Daniel Vetter Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-7-laurent.pinchart@ideasonboard.com --- Documentation/gpu/drm-kms-helpers.rst | 6 +- drivers/gpu/drm/drm_bridge.c | 89 +++++++++++++++++++++------ 2 files changed, 73 insertions(+), 22 deletions(-) diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 9668a7fe2408..fe155c6ae175 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -139,11 +139,11 @@ Overview .. kernel-doc:: drivers/gpu/drm/drm_bridge.c :doc: overview -Default bridge callback sequence --------------------------------- +Bridge Operations +----------------- .. kernel-doc:: drivers/gpu/drm/drm_bridge.c - :doc: bridge callbacks + :doc: bridge operations Bridge Helper Reference diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 68ab933ee430..52dfc67a6cf8 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -39,26 +39,50 @@ * encoder chain. * * A bridge is always attached to a single &drm_encoder at a time, but can be - * either connected to it directly, or through an intermediate bridge:: + * either connected to it directly, or through a chain of bridges:: * - * encoder ---> bridge B ---> bridge A + * [ CRTC ---> ] Encoder ---> Bridge A ---> Bridge B * - * Here, the output of the encoder feeds to bridge B, and that furthers feeds to - * bridge A. + * Here, the output of the encoder feeds to bridge A, and that furthers feeds to + * bridge B. Bridge chains can be arbitrarily long, and shall be fully linear: + * Chaining multiple bridges to the output of a bridge, or the same bridge to + * the output of different bridges, is not supported. * - * The driver using the bridge is responsible to make the associations between - * the encoder and bridges. Once these links are made, the bridges will - * participate along with encoder functions to perform mode_set/enable/disable - * through the ops provided in &drm_bridge_funcs. + * Display drivers are responsible for linking encoders with the first bridge + * in the chains. This is done by acquiring the appropriate bridge with + * of_drm_find_bridge() or drm_of_find_panel_or_bridge(), or creating it for a + * panel with drm_panel_bridge_add_typed() (or the managed version + * devm_drm_panel_bridge_add_typed()). Once acquired, the bridge shall be + * attached to the encoder with a call to drm_bridge_attach(). * - * drm_bridge, like drm_panel, aren't drm_mode_object entities like planes, + * Bridges are responsible for linking themselves with the next bridge in the + * chain, if any. This is done the same way as for encoders, with the call to + * drm_bridge_attach() occurring in the &drm_bridge_funcs.attach operation. + * + * Once these links are created, the bridges can participate along with encoder + * functions to perform mode validation and fixup (through + * drm_bridge_chain_mode_valid() and drm_atomic_bridge_chain_check()), mode + * setting (through drm_bridge_chain_mode_set()), enable (through + * drm_atomic_bridge_chain_pre_enable() and drm_atomic_bridge_chain_enable()) + * and disable (through drm_atomic_bridge_chain_disable() and + * drm_atomic_bridge_chain_post_disable()). Those functions call the + * corresponding operations provided in &drm_bridge_funcs in sequence for all + * bridges in the chain. + * + * For display drivers that use the atomic helpers + * drm_atomic_helper_check_modeset(), + * drm_atomic_helper_commit_modeset_enables() and + * drm_atomic_helper_commit_modeset_disables() (either directly in hand-rolled + * commit check and commit tail handlers, or through the higher-level + * drm_atomic_helper_check() and drm_atomic_helper_commit_tail() or + * drm_atomic_helper_commit_tail_rpm() helpers), this is done transparently and + * requires no intervention from the driver. For other drivers, the relevant + * DRM bridge chain functions shall be called manually. + * + * &drm_bridge, like &drm_panel, aren't &drm_mode_object entities like planes, * CRTCs, encoders or connectors and hence are not visible to userspace. They * just provide additional hooks to get the desired output at the end of the * encoder chain. - * - * Bridges can also be chained up using the &drm_bridge.chain_node field. - * - * Both legacy CRTC helpers and the new atomic modeset helpers support bridges. */ static DEFINE_MUTEX(bridge_lock); @@ -212,14 +236,41 @@ void drm_bridge_detach(struct drm_bridge *bridge) } /** - * DOC: bridge callbacks + * DOC: bridge operations * - * The &drm_bridge_funcs ops are populated by the bridge driver. The DRM - * internals (atomic and CRTC helpers) use the helpers defined in drm_bridge.c - * These helpers call a specific &drm_bridge_funcs op for all the bridges - * during encoder configuration. + * Bridge drivers expose operations through the &drm_bridge_funcs structure. + * The DRM internals (atomic and CRTC helpers) use the helpers defined in + * drm_bridge.c to call bridge operations. Those operations are divided in + * two big categories to support different parts of the bridge usage. * - * For detailed specification of the bridge callbacks see &drm_bridge_funcs. + * - The encoder-related operations support control of the bridges in the + * chain, and are roughly counterparts to the &drm_encoder_helper_funcs + * operations. They are used by the legacy CRTC and the atomic modeset + * helpers to perform mode validation, fixup and setting, and enable and + * disable the bridge automatically. + * + * The enable and disable operations are split in + * &drm_bridge_funcs.pre_enable, &drm_bridge_funcs.enable, + * &drm_bridge_funcs.disable and &drm_bridge_funcs.post_disable to provide + * finer-grained control. + * + * Bridge drivers may implement the legacy version of those operations, or + * the atomic version (prefixed with atomic\_), in which case they shall also + * implement the atomic state bookkeeping operations + * (&drm_bridge_funcs.atomic_duplicate_state, + * &drm_bridge_funcs.atomic_destroy_state and &drm_bridge_funcs.reset). + * Mixing atomic and non-atomic versions of the operations is not supported. + * + * - The bus format negotiation operations + * &drm_bridge_funcs.atomic_get_output_bus_fmts and + * &drm_bridge_funcs.atomic_get_input_bus_fmts allow bridge drivers to + * negotiate the formats transmitted between bridges in the chain when + * multiple formats are supported. Negotiation for formats is performed + * transparently for display drivers by the atomic modeset helpers. Only + * atomic versions of those operations exist, bridge drivers that need to + * implement them shall thus also implement the atomic version of the + * encoder-related operations. This feature is not supported by the legacy + * CRTC helpers. */ /** From 11f6c4b1b2599d04ae94a25ce3dcdc46d19434b0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:27 +0200 Subject: [PATCH 23/78] drm/bridge: Add connector-related bridge operations and data To support implementation of DRM connectors on top of DRM bridges instead of by bridges, the drm_bridge needs to expose new operations and data: - Output detection, hot-plug notification, mode retrieval and EDID retrieval operations - Bitmask of supported operations - Bridge output type - I2C adapter for DDC access Add and document these. Three new bridge helper functions are also added to handle hot plug notification in a way that is as transparent as possible for the bridges. Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Reviewed-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Acked-by: Daniel Vetter Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-8-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/drm_bridge.c | 201 ++++++++++++++++++++++++++++++++++- include/drm/drm_bridge.h | 192 ++++++++++++++++++++++++++++++++- 2 files changed, 391 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 52dfc67a6cf8..5a8f7d7e05f3 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -95,6 +95,8 @@ static LIST_HEAD(bridge_list); */ void drm_bridge_add(struct drm_bridge *bridge) { + mutex_init(&bridge->hpd_mutex); + mutex_lock(&bridge_lock); list_add_tail(&bridge->list, &bridge_list); mutex_unlock(&bridge_lock); @@ -111,6 +113,8 @@ void drm_bridge_remove(struct drm_bridge *bridge) mutex_lock(&bridge_lock); list_del_init(&bridge->list); mutex_unlock(&bridge_lock); + + mutex_destroy(&bridge->hpd_mutex); } EXPORT_SYMBOL(drm_bridge_remove); @@ -241,7 +245,7 @@ void drm_bridge_detach(struct drm_bridge *bridge) * Bridge drivers expose operations through the &drm_bridge_funcs structure. * The DRM internals (atomic and CRTC helpers) use the helpers defined in * drm_bridge.c to call bridge operations. Those operations are divided in - * two big categories to support different parts of the bridge usage. + * three big categories to support different parts of the bridge usage. * * - The encoder-related operations support control of the bridges in the * chain, and are roughly counterparts to the &drm_encoder_helper_funcs @@ -271,6 +275,43 @@ void drm_bridge_detach(struct drm_bridge *bridge) * implement them shall thus also implement the atomic version of the * encoder-related operations. This feature is not supported by the legacy * CRTC helpers. + * + * - The connector-related operations support implementing a &drm_connector + * based on a chain of bridges. DRM bridges traditionally create a + * &drm_connector for bridges meant to be used at the end of the chain. This + * puts additional burden on bridge drivers, especially for bridges that may + * be used in the middle of a chain or at the end of it. Furthermore, it + * requires all operations of the &drm_connector to be handled by a single + * bridge, which doesn't always match the hardware architecture. + * + * To simplify bridge drivers and make the connector implementation more + * flexible, a new model allows bridges to unconditionally skip creation of + * &drm_connector and instead expose &drm_bridge_funcs operations to support + * an externally-implemented &drm_connector. Those operations are + * &drm_bridge_funcs.detect, &drm_bridge_funcs.get_modes, + * &drm_bridge_funcs.get_edid, &drm_bridge_funcs.hpd_notify, + * &drm_bridge_funcs.hpd_enable and &drm_bridge_funcs.hpd_disable. When + * implemented, display drivers shall create a &drm_connector instance for + * each chain of bridges, and implement those connector instances based on + * the bridge connector operations. + * + * Bridge drivers shall implement the connector-related operations for all + * the features that the bridge hardware support. For instance, if a bridge + * supports reading EDID, the &drm_bridge_funcs.get_edid shall be + * implemented. This however doesn't mean that the DDC lines are wired to the + * bridge on a particular platform, as they could also be connected to an I2C + * controller of the SoC. Support for the connector-related operations on the + * running platform is reported through the &drm_bridge.ops flags. Bridge + * drivers shall detect which operations they can support on the platform + * (usually this information is provided by ACPI or DT), and set the + * &drm_bridge.ops flags for all supported operations. A flag shall only be + * set if the corresponding &drm_bridge_funcs operation is implemented, but + * an implemented operation doesn't necessarily imply that the corresponding + * flag will be set. Display drivers shall use the &drm_bridge.ops flags to + * decide which bridge to delegate a connector operation to. This mechanism + * allows providing a single static const &drm_bridge_funcs instance in + * bridge drivers, improving security by storing function pointers in + * read-only memory. */ /** @@ -970,6 +1011,164 @@ int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_atomic_bridge_chain_check); +/** + * drm_bridge_detect - check if anything is attached to the bridge output + * @bridge: bridge control structure + * + * If the bridge supports output detection, as reported by the + * DRM_BRIDGE_OP_DETECT bridge ops flag, call &drm_bridge_funcs.detect for the + * bridge and return the connection status. Otherwise return + * connector_status_unknown. + * + * RETURNS: + * The detection status on success, or connector_status_unknown if the bridge + * doesn't support output detection. + */ +enum drm_connector_status drm_bridge_detect(struct drm_bridge *bridge) +{ + if (!(bridge->ops & DRM_BRIDGE_OP_DETECT)) + return connector_status_unknown; + + return bridge->funcs->detect(bridge); +} +EXPORT_SYMBOL_GPL(drm_bridge_detect); + +/** + * drm_bridge_get_modes - fill all modes currently valid for the sink into the + * @connector + * @bridge: bridge control structure + * @connector: the connector to fill with modes + * + * If the bridge supports output modes retrieval, as reported by the + * DRM_BRIDGE_OP_MODES bridge ops flag, call &drm_bridge_funcs.get_modes to + * fill the connector with all valid modes and return the number of modes + * added. Otherwise return 0. + * + * RETURNS: + * The number of modes added to the connector. + */ +int drm_bridge_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + if (!(bridge->ops & DRM_BRIDGE_OP_MODES)) + return 0; + + return bridge->funcs->get_modes(bridge, connector); +} +EXPORT_SYMBOL_GPL(drm_bridge_get_modes); + +/** + * drm_bridge_get_edid - get the EDID data of the connected display + * @bridge: bridge control structure + * @connector: the connector to read EDID for + * + * If the bridge supports output EDID retrieval, as reported by the + * DRM_BRIDGE_OP_EDID bridge ops flag, call &drm_bridge_funcs.get_edid to + * get the EDID and return it. Otherwise return ERR_PTR(-ENOTSUPP). + * + * RETURNS: + * The retrieved EDID on success, or an error pointer otherwise. + */ +struct edid *drm_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + if (!(bridge->ops & DRM_BRIDGE_OP_EDID)) + return ERR_PTR(-ENOTSUPP); + + return bridge->funcs->get_edid(bridge, connector); +} +EXPORT_SYMBOL_GPL(drm_bridge_get_edid); + +/** + * drm_bridge_hpd_enable - enable hot plug detection for the bridge + * @bridge: bridge control structure + * @cb: hot-plug detection callback + * @data: data to be passed to the hot-plug detection callback + * + * Call &drm_bridge_funcs.hpd_enable if implemented and register the given @cb + * and @data as hot plug notification callback. From now on the @cb will be + * called with @data when an output status change is detected by the bridge, + * until hot plug notification gets disabled with drm_bridge_hpd_disable(). + * + * Hot plug detection is supported only if the DRM_BRIDGE_OP_HPD flag is set in + * bridge->ops. This function shall not be called when the flag is not set. + * + * Only one hot plug detection callback can be registered at a time, it is an + * error to call this function when hot plug detection is already enabled for + * the bridge. + */ +void drm_bridge_hpd_enable(struct drm_bridge *bridge, + void (*cb)(void *data, + enum drm_connector_status status), + void *data) +{ + if (!(bridge->ops & DRM_BRIDGE_OP_HPD)) + return; + + mutex_lock(&bridge->hpd_mutex); + + if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n")) + goto unlock; + + bridge->hpd_cb = cb; + bridge->hpd_data = data; + + if (bridge->funcs->hpd_enable) + bridge->funcs->hpd_enable(bridge); + +unlock: + mutex_unlock(&bridge->hpd_mutex); +} +EXPORT_SYMBOL_GPL(drm_bridge_hpd_enable); + +/** + * drm_bridge_hpd_disable - disable hot plug detection for the bridge + * @bridge: bridge control structure + * + * Call &drm_bridge_funcs.hpd_disable if implemented and unregister the hot + * plug detection callback previously registered with drm_bridge_hpd_enable(). + * Once this function returns the callback will not be called by the bridge + * when an output status change occurs. + * + * Hot plug detection is supported only if the DRM_BRIDGE_OP_HPD flag is set in + * bridge->ops. This function shall not be called when the flag is not set. + */ +void drm_bridge_hpd_disable(struct drm_bridge *bridge) +{ + if (!(bridge->ops & DRM_BRIDGE_OP_HPD)) + return; + + mutex_lock(&bridge->hpd_mutex); + if (bridge->funcs->hpd_disable) + bridge->funcs->hpd_disable(bridge); + + bridge->hpd_cb = NULL; + bridge->hpd_data = NULL; + mutex_unlock(&bridge->hpd_mutex); +} +EXPORT_SYMBOL_GPL(drm_bridge_hpd_disable); + +/** + * drm_bridge_hpd_notify - notify hot plug detection events + * @bridge: bridge control structure + * @status: output connection status + * + * Bridge drivers shall call this function to report hot plug events when they + * detect a change in the output status, when hot plug detection has been + * enabled by drm_bridge_hpd_enable(). + * + * This function shall be called in a context that can sleep. + */ +void drm_bridge_hpd_notify(struct drm_bridge *bridge, + enum drm_connector_status status) +{ + mutex_lock(&bridge->hpd_mutex); + if (bridge->hpd_cb) + bridge->hpd_cb(bridge->hpd_data, status); + mutex_unlock(&bridge->hpd_mutex); +} +EXPORT_SYMBOL_GPL(drm_bridge_hpd_notify); + #ifdef CONFIG_OF /** * of_drm_find_bridge - find the bridge corresponding to the device node in diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 38de129d5947..9d5d750973e9 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -23,8 +23,9 @@ #ifndef __DRM_BRIDGE_H__ #define __DRM_BRIDGE_H__ -#include #include +#include +#include #include #include @@ -33,7 +34,10 @@ struct drm_bridge; struct drm_bridge_timings; +struct drm_connector; struct drm_panel; +struct edid; +struct i2c_adapter; /** * struct drm_bridge_funcs - drm_bridge control functions @@ -497,6 +501,119 @@ struct drm_bridge_funcs { * giving the reason of the failure otherwise. */ struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge); + + /** + * @detect: + * + * Check if anything is attached to the bridge output. + * + * This callback is optional, if not implemented the bridge will be + * considered as always having a component attached to its output. + * Bridges that implement this callback shall set the + * DRM_BRIDGE_OP_DETECT flag in their &drm_bridge->ops. + * + * RETURNS: + * + * drm_connector_status indicating the bridge output status. + */ + enum drm_connector_status (*detect)(struct drm_bridge *bridge); + + /** + * @get_modes: + * + * Fill all modes currently valid for the sink into the &drm_connector + * with drm_mode_probed_add(). + * + * The @get_modes callback is mostly intended to support non-probeable + * displays such as many fixed panels. Bridges that support reading + * EDID shall leave @get_modes unimplemented and implement the + * &drm_bridge_funcs->get_edid callback instead. + * + * This callback is optional. Bridges that implement it shall set the + * DRM_BRIDGE_OP_MODES flag in their &drm_bridge->ops. + * + * The connector parameter shall be used for the sole purpose of + * filling modes, and shall not be stored internally by bridge drivers + * for future usage. + * + * RETURNS: + * + * The number of modes added by calling drm_mode_probed_add(). + */ + int (*get_modes)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @get_edid: + * + * Read and parse the EDID data of the connected display. + * + * The @get_edid callback is the preferred way of reporting mode + * information for a display connected to the bridge output. Bridges + * that support reading EDID shall implement this callback and leave + * the @get_modes callback unimplemented. + * + * The caller of this operation shall first verify the output + * connection status and refrain from reading EDID from a disconnected + * output. + * + * This callback is optional. Bridges that implement it shall set the + * DRM_BRIDGE_OP_EDID flag in their &drm_bridge->ops. + * + * The connector parameter shall be used for the sole purpose of EDID + * retrieval and parsing, and shall not be stored internally by bridge + * drivers for future usage. + * + * RETURNS: + * + * An edid structure newly allocated with kmalloc() (or similar) on + * success, or NULL otherwise. The caller is responsible for freeing + * the returned edid structure with kfree(). + */ + struct edid *(*get_edid)(struct drm_bridge *bridge, + struct drm_connector *connector); + + /** + * @hpd_notify: + * + * Notify the bridge of hot plug detection. + * + * This callback is optional, it may be implemented by bridges that + * need to be notified of display connection or disconnection for + * internal reasons. One use case is to reset the internal state of CEC + * controllers for HDMI bridges. + */ + void (*hpd_notify)(struct drm_bridge *bridge, + enum drm_connector_status status); + + /** + * @hpd_enable: + * + * Enable hot plug detection. From now on the bridge shall call + * drm_bridge_hpd_notify() each time a change is detected in the output + * connection status, until hot plug detection gets disabled with + * @hpd_disable. + * + * This callback is optional and shall only be implemented by bridges + * that support hot-plug notification without polling. Bridges that + * implement it shall also implement the @hpd_disable callback and set + * the DRM_BRIDGE_OP_HPD flag in their &drm_bridge->ops. + */ + void (*hpd_enable)(struct drm_bridge *bridge); + + /** + * @hpd_disable: + * + * Disable hot plug detection. Once this function returns the bridge + * shall not call drm_bridge_hpd_notify() when a change in the output + * connection status occurs. + * + * This callback is optional and shall only be implemented by bridges + * that support hot-plug notification without polling. Bridges that + * implement it shall also implement the @hpd_enable callback and set + * the DRM_BRIDGE_OP_HPD flag in their &drm_bridge->ops. + */ + void (*hpd_disable)(struct drm_bridge *bridge); }; /** @@ -535,6 +652,39 @@ struct drm_bridge_timings { bool dual_link; }; +/** + * enum drm_bridge_ops - Bitmask of operations supported by the bridge + */ +enum drm_bridge_ops { + /** + * @DRM_BRIDGE_OP_DETECT: The bridge can detect displays connected to + * its output. Bridges that set this flag shall implement the + * &drm_bridge_funcs->detect callback. + */ + DRM_BRIDGE_OP_DETECT = BIT(0), + /** + * @DRM_BRIDGE_OP_EDID: The bridge can retrieve the EDID of the display + * connected to its output. Bridges that set this flag shall implement + * the &drm_bridge_funcs->get_edid callback. + */ + DRM_BRIDGE_OP_EDID = BIT(1), + /** + * @DRM_BRIDGE_OP_HPD: The bridge can detect hot-plug and hot-unplug + * without requiring polling. Bridges that set this flag shall + * implement the &drm_bridge_funcs->hpd_enable and + * &drm_bridge_funcs->hpd_disable callbacks if they support enabling + * and disabling hot-plug detection dynamically. + */ + DRM_BRIDGE_OP_HPD = BIT(2), + /** + * @DRM_BRIDGE_OP_MODES: The bridge can retrieve the modes supported + * by the display at its output. This does not include reading EDID + * which is separately covered by @DRM_BRIDGE_OP_EDID. Bridges that set + * this flag shall implement the &drm_bridge_funcs->get_modes callback. + */ + DRM_BRIDGE_OP_MODES = BIT(3), +}; + /** * struct drm_bridge - central DRM bridge control structure */ @@ -563,6 +713,33 @@ struct drm_bridge { const struct drm_bridge_funcs *funcs; /** @driver_private: pointer to the bridge driver's internal context */ void *driver_private; + /** @ops: bitmask of operations supported by the bridge */ + enum drm_bridge_ops ops; + /** + * @type: Type of the connection at the bridge output + * (DRM_MODE_CONNECTOR_*). For bridges at the end of this chain this + * identifies the type of connected display. + */ + int type; + /** + * @ddc: Associated I2C adapter for DDC access, if any. + */ + struct i2c_adapter *ddc; + /** private: */ + /** + * @hpd_mutex: Protects the @hpd_cb and @hpd_data fields. + */ + struct mutex hpd_mutex; + /** + * @hpd_cb: Hot plug detection callback, registered with + * drm_bridge_hpd_enable(). + */ + void (*hpd_cb)(void *data, enum drm_connector_status status); + /** + * @hpd_data: Private data passed to the Hot plug detection callback + * @hpd_cb. + */ + void *hpd_data; }; static inline struct drm_bridge * @@ -669,6 +846,19 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, u32 output_fmt, unsigned int *num_input_fmts); +enum drm_connector_status drm_bridge_detect(struct drm_bridge *bridge); +int drm_bridge_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector); +struct edid *drm_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector); +void drm_bridge_hpd_enable(struct drm_bridge *bridge, + void (*cb)(void *data, + enum drm_connector_status status), + void *data); +void drm_bridge_hpd_disable(struct drm_bridge *bridge); +void drm_bridge_hpd_notify(struct drm_bridge *bridge, + enum drm_connector_status status); + #ifdef CONFIG_DRM_PANEL_BRIDGE struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel); struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, From 64d05ff758e39b7c908b96f5e19a5e519092eac8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:28 +0200 Subject: [PATCH 24/78] drm/bridge: Add interlace_allowed flag to drm_bridge In preparation for a connector creation helper based on a chain of bridges, add a flag to the drm_bridge structure to report support for interlaced modes. This will be used to set the connector's interlace_allowed flag. Signed-off-by: Laurent Pinchart Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-9-laurent.pinchart@ideasonboard.com --- include/drm/drm_bridge.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 9d5d750973e9..018e195c4808 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -721,6 +721,11 @@ struct drm_bridge { * identifies the type of connected display. */ int type; + /** + * @interlace_allowed: Indicate that the bridge can handle interlaced + * modes. + */ + bool interlace_allowed; /** * @ddc: Associated I2C adapter for DDC access, if any. */ From a25b988ff83f3ca0d8f5acf855fb1717c1c61a69 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:29 +0200 Subject: [PATCH 25/78] drm/bridge: Extend bridge API to disable connector creation Most bridge drivers create a DRM connector to model the connector at the output of the bridge. This model is historical and has worked pretty well so far, but causes several issues: - It prevents supporting more complex display pipelines where DRM connector operations are split over multiple components. For instance a pipeline with a bridge connected to the DDC signals to read EDID data, and another one connected to the HPD signal to detect connection and disconnection, will not be possible to support through this model. - It requires every bridge driver to implement similar connector handling code, resulting in code duplication. - It assumes that a bridge will either be wired to a connector or to another bridge, but doesn't support bridges that can be used in both positions very well (although there is some ad-hoc support for this in the analogix_dp bridge driver). In order to solve these issues, ownership of the connector should be moved to the display controller driver (where it can be implemented using helpers provided by the core). Extend the bridge API to allow disabling connector creation in bridge drivers as a first step towards the new model. The new flags argument to the bridge .attach() operation allows instructing the bridge driver to skip creating a connector. Unconditionally set the new flags argument to 0 for now to keep the existing behaviour, and modify all existing bridge drivers to return an error when connector creation is not requested as they don't support this feature yet. The change is based on the following semantic patch, with manual review and edits. @ rule1 @ identifier funcs; identifier fn; @@ struct drm_bridge_funcs funcs = { ..., .attach = fn }; @ depends on rule1 @ identifier rule1.fn; identifier bridge; statement S, S1; @@ int fn( struct drm_bridge *bridge + , enum drm_bridge_attach_flags flags ) { ... when != S + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + S1 ... } @ depends on rule1 @ identifier rule1.fn; identifier bridge, flags; expression E1, E2, E3; @@ int fn( struct drm_bridge *bridge, enum drm_bridge_attach_flags flags ) { <... drm_bridge_attach(E1, E2, E3 + , flags ) ...> } @@ expression E1, E2, E3; @@ drm_bridge_attach(E1, E2, E3 + , 0 ) Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Acked-by: Sam Ravnborg Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Acked-by: Daniel Vetter Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-10-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/arc/arcpgu_hdmi.c | 2 +- .../gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 2 +- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 8 +++++++- .../drm/bridge/analogix/analogix-anx6345.c | 8 +++++++- .../drm/bridge/analogix/analogix-anx78xx.c | 8 +++++++- .../drm/bridge/analogix/analogix_dp_core.c | 10 ++++++++-- drivers/gpu/drm/bridge/cdns-dsi.c | 6 ++++-- drivers/gpu/drm/bridge/dumb-vga-dac.c | 8 +++++++- drivers/gpu/drm/bridge/lvds-codec.c | 5 +++-- .../bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 8 +++++++- drivers/gpu/drm/bridge/nxp-ptn3460.c | 8 +++++++- drivers/gpu/drm/bridge/panel.c | 8 +++++++- drivers/gpu/drm/bridge/parade-ps8622.c | 8 +++++++- drivers/gpu/drm/bridge/parade-ps8640.c | 5 +++-- drivers/gpu/drm/bridge/sii902x.c | 8 +++++++- drivers/gpu/drm/bridge/sil-sii8620.c | 3 ++- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 10 ++++++++-- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 8 +++++--- drivers/gpu/drm/bridge/tc358764.c | 8 +++++++- drivers/gpu/drm/bridge/tc358767.c | 9 ++++++++- drivers/gpu/drm/bridge/tc358768.c | 6 ++++-- drivers/gpu/drm/bridge/thc63lvd1024.c | 5 +++-- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 8 +++++++- drivers/gpu/drm/bridge/ti-tfp410.c | 8 +++++++- drivers/gpu/drm/drm_bridge.c | 20 +++++++++++++++++-- drivers/gpu/drm/drm_simple_kms_helper.c | 2 +- drivers/gpu/drm/exynos/exynos_dp.c | 3 ++- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 4 ++-- drivers/gpu/drm/exynos/exynos_hdmi.c | 2 +- drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 2 +- drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c | 2 +- drivers/gpu/drm/i2c/tda998x_drv.c | 10 ++++++++-- drivers/gpu/drm/imx/imx-ldb.c | 2 +- drivers/gpu/drm/imx/parallel-display.c | 4 ++-- drivers/gpu/drm/ingenic/ingenic-drm.c | 2 +- drivers/gpu/drm/mcde/mcde_dsi.c | 5 +++-- drivers/gpu/drm/mediatek/mtk_dpi.c | 2 +- drivers/gpu/drm/mediatek/mtk_dsi.c | 2 +- drivers/gpu/drm/mediatek/mtk_hdmi.c | 10 ++++++++-- drivers/gpu/drm/msm/dsi/dsi_manager.c | 4 ++-- drivers/gpu/drm/msm/edp/edp.c | 2 +- drivers/gpu/drm/msm/edp/edp_bridge.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi.c | 2 +- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 2 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 2 +- drivers/gpu/drm/rcar-du/rcar_lvds.c | 11 ++++++++-- drivers/gpu/drm/rockchip/rockchip_lvds.c | 2 +- drivers/gpu/drm/rockchip/rockchip_rgb.c | 2 +- drivers/gpu/drm/sti/sti_dvo.c | 2 +- drivers/gpu/drm/sti/sti_hda.c | 2 +- drivers/gpu/drm/sti/sti_hdmi.c | 2 +- drivers/gpu/drm/stm/ltdc.c | 2 +- drivers/gpu/drm/sun4i/sun4i_lvds.c | 2 +- drivers/gpu/drm/sun4i/sun4i_rgb.c | 2 +- drivers/gpu/drm/tidss/tidss_kms.c | 2 +- drivers/gpu/drm/tilcdc/tilcdc_external.c | 2 +- drivers/gpu/drm/vc4/vc4_dpi.c | 2 +- drivers/gpu/drm/vc4/vc4_dsi.c | 2 +- include/drm/drm_bridge.h | 20 ++++++++++++++++--- 60 files changed, 231 insertions(+), 79 deletions(-) diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c index 8fd7094beece..52839934f2fb 100644 --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c @@ -40,7 +40,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np) return ret; /* Link drm_bridge to encoder */ - ret = drm_bridge_attach(encoder, bridge, NULL); + ret = drm_bridge_attach(encoder, bridge, NULL, 0); if (ret) drm_encoder_cleanup(encoder); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 121b62682d80..e2019fe97fff 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -114,7 +114,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) } if (bridge) { - ret = drm_bridge_attach(&output->encoder, bridge, NULL); + ret = drm_bridge_attach(&output->encoder, bridge, NULL, 0); if (!ret) return 0; diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index a275e6c91bd7..87b58c1acff4 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -847,11 +847,17 @@ static void adv7511_bridge_mode_set(struct drm_bridge *bridge, adv7511_mode_set(adv, mode, adj_mode); } -static int adv7511_bridge_attach(struct drm_bridge *bridge) +static int adv7511_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct adv7511 *adv = bridge_to_adv7511(bridge); int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c index 0d8d083b0207..5139c3724890 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -520,11 +520,17 @@ static const struct drm_connector_funcs anx6345_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int anx6345_bridge_attach(struct drm_bridge *bridge) +static int anx6345_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct anx6345 *anx6345 = bridge_to_anx6345(bridge); int err; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index 864423f59d66..0d5a5ad0c9ee 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -886,11 +886,17 @@ static const struct drm_connector_funcs anx78xx_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int anx78xx_bridge_attach(struct drm_bridge *bridge) +static int anx78xx_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct anx78xx *anx78xx = bridge_to_anx78xx(bridge); int err; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index dfb59a5fefea..9ded2cef57dd 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1216,13 +1216,19 @@ static const struct drm_connector_funcs analogix_dp_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int analogix_dp_bridge_attach(struct drm_bridge *bridge) +static int analogix_dp_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct analogix_dp_device *dp = bridge->driver_private; struct drm_encoder *encoder = dp->encoder; struct drm_connector *connector = NULL; int ret = 0; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; @@ -1598,7 +1604,7 @@ static int analogix_dp_create_bridge(struct drm_device *drm_dev, bridge->driver_private = dp; bridge->funcs = &analogix_dp_bridge_funcs; - ret = drm_bridge_attach(dp->encoder, bridge, NULL); + ret = drm_bridge_attach(dp->encoder, bridge, NULL, 0); if (ret) { DRM_ERROR("failed to attach drm bridge\n"); return -EINVAL; diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c index b7c97f060241..69c3892caee5 100644 --- a/drivers/gpu/drm/bridge/cdns-dsi.c +++ b/drivers/gpu/drm/bridge/cdns-dsi.c @@ -644,7 +644,8 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi, return 0; } -static int cdns_dsi_bridge_attach(struct drm_bridge *bridge) +static int cdns_dsi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); struct cdns_dsi *dsi = input_to_dsi(input); @@ -656,7 +657,8 @@ static int cdns_dsi_bridge_attach(struct drm_bridge *bridge) return -ENOTSUPP; } - return drm_bridge_attach(bridge->encoder, output->bridge, bridge); + return drm_bridge_attach(bridge->encoder, output->bridge, bridge, + flags); } static enum drm_mode_status diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c index cc33dc411b9e..ad5b5a849e43 100644 --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c +++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c @@ -100,11 +100,17 @@ static const struct drm_connector_funcs dumb_vga_con_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int dumb_vga_attach(struct drm_bridge *bridge) +static int dumb_vga_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge); int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { DRM_ERROR("Missing encoder\n"); return -ENODEV; diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index f7ae28ed1c14..24fb1befdfa2 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -26,12 +26,13 @@ static inline struct lvds_codec *to_lvds_codec(struct drm_bridge *bridge) return container_of(bridge, struct lvds_codec, bridge); } -static int lvds_codec_attach(struct drm_bridge *bridge) +static int lvds_codec_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct lvds_codec *lvds_codec = to_lvds_codec(bridge); return drm_bridge_attach(bridge->encoder, lvds_codec->panel_bridge, - bridge); + bridge, flags); } static void lvds_codec_enable(struct drm_bridge *bridge) diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index e8a49f6146c6..6200f12a37e6 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -206,13 +206,19 @@ static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static int ge_b850v3_lvds_attach(struct drm_bridge *bridge) +static int ge_b850v3_lvds_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector; struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c; int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index 57ff01339559..438e566ce0a4 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -236,11 +236,17 @@ static const struct drm_connector_funcs ptn3460_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int ptn3460_bridge_attach(struct drm_bridge *bridge) +static int ptn3460_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 9660736ddbf3..5c92d7c9fd61 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -53,12 +53,18 @@ static const struct drm_connector_funcs panel_bridge_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int panel_bridge_attach(struct drm_bridge *bridge) +static int panel_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); struct drm_connector *connector = &panel_bridge->connector; int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { DRM_ERROR("Missing encoder\n"); return -ENODEV; diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 10c47c008b40..d789ea2a7fb9 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -476,11 +476,17 @@ static const struct drm_connector_funcs ps8622_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int ps8622_attach(struct drm_bridge *bridge) +static int ps8622_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { DRM_ERROR("Parent encoder object not found"); return -ENODEV; diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index c6c06688aff2..d3a53442d449 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -187,7 +187,8 @@ static void ps8640_post_disable(struct drm_bridge *bridge) DRM_ERROR("cannot disable regulators %d\n", ret); } -static int ps8640_bridge_attach(struct drm_bridge *bridge) +static int ps8640_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); struct device *dev = &ps_bridge->page[0]->dev; @@ -234,7 +235,7 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge) /* Attach the panel-bridge to the dsi bridge */ return drm_bridge_attach(bridge->encoder, ps_bridge->panel_bridge, - &ps_bridge->bridge); + &ps_bridge->bridge, flags); err_dsi_attach: mipi_dsi_device_unregister(dsi); diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index b70e8c5cf2e1..6dad025f8da7 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -399,12 +399,18 @@ out: mutex_unlock(&sii902x->mutex); } -static int sii902x_bridge_attach(struct drm_bridge *bridge) +static int sii902x_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct sii902x *sii902x = bridge_to_sii902x(bridge); struct drm_device *drm = bridge->dev; int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + drm_connector_helper_add(&sii902x->connector, &sii902x_connector_helper_funcs); diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 4c0eef406eb1..92acd336aa89 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2202,7 +2202,8 @@ static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge) return container_of(bridge, struct sii8620, bridge); } -static int sii8620_attach(struct drm_bridge *bridge) +static int sii8620_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct sii8620 *ctx = bridge_to_sii8620(bridge); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 67fca439bbfb..9bad194cfd0a 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -2371,7 +2371,8 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = .atomic_check = dw_hdmi_connector_atomic_check, }; -static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) +static int dw_hdmi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct dw_hdmi *hdmi = bridge->driver_private; struct drm_encoder *encoder = bridge->encoder; @@ -2379,6 +2380,11 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) struct cec_connector_info conn_info; struct cec_notifier *notifier; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + connector->interlace_allowed = 1; connector->polled = DRM_CONNECTOR_POLL_HPD; @@ -3076,7 +3082,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, if (IS_ERR(hdmi)) return hdmi; - ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL); + ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, 0); if (ret) { dw_hdmi_remove(hdmi); DRM_ERROR("Failed to initialize bridge with drm\n"); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index 12823ae91065..5ef0f154aa7b 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -936,7 +936,8 @@ dw_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge, return mode_status; } -static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge) +static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); @@ -949,7 +950,8 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge) bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI; /* Attach the panel-bridge to the dsi bridge */ - return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge); + return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge, + flags); } static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { @@ -1120,7 +1122,7 @@ int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder) { int ret; - ret = drm_bridge_attach(encoder, &dsi->bridge, NULL); + ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, 0); if (ret) { DRM_ERROR("Failed to initialize bridge with drm\n"); return ret; diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index 96207fcfde19..283e4a8dd923 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -349,12 +349,18 @@ static void tc358764_enable(struct drm_bridge *bridge) dev_err(ctx->dev, "error enabling panel (%d)\n", ret); } -static int tc358764_attach(struct drm_bridge *bridge) +static int tc358764_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct tc358764 *ctx = bridge_to_tc358764(bridge); struct drm_device *drm = bridge->dev; int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + ctx->connector.polled = DRM_CONNECTOR_POLL_HPD; ret = drm_connector_init(drm, &ctx->connector, &tc358764_connector_funcs, diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 3709e5ace724..402a690d1fe5 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -31,6 +31,7 @@ #include #include #include +#include #include /* Registers */ @@ -1403,13 +1404,19 @@ static const struct drm_connector_funcs tc_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int tc_bridge_attach(struct drm_bridge *bridge) +static int tc_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; struct tc_data *tc = bridge_to_tc(bridge); struct drm_device *drm = bridge->dev; int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + /* Create DP/eDP connector */ drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs); ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs, diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index da7af03256f6..1b39e8d37834 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -511,7 +511,8 @@ static const struct mipi_dsi_host_ops tc358768_dsi_host_ops = { .transfer = tc358768_dsi_host_transfer, }; -static int tc358768_bridge_attach(struct drm_bridge *bridge) +static int tc358768_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct tc358768_priv *priv = bridge_to_tc358768(bridge); @@ -520,7 +521,8 @@ static int tc358768_bridge_attach(struct drm_bridge *bridge) return -ENOTSUPP; } - return drm_bridge_attach(bridge->encoder, priv->output.bridge, bridge); + return drm_bridge_attach(bridge->encoder, priv->output.bridge, bridge, + flags); } static enum drm_mode_status diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c index 3d74129b2995..97d8129760e9 100644 --- a/drivers/gpu/drm/bridge/thc63lvd1024.c +++ b/drivers/gpu/drm/bridge/thc63lvd1024.c @@ -42,11 +42,12 @@ static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge) return container_of(bridge, struct thc63_dev, bridge); } -static int thc63_attach(struct drm_bridge *bridge) +static int thc63_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct thc63_dev *thc63 = to_thc63(bridge); - return drm_bridge_attach(bridge->encoder, thc63->next, bridge); + return drm_bridge_attach(bridge->encoder, thc63->next, bridge, flags); } static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge, diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 6ce60a64b603..6ad688b320ae 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -266,7 +266,8 @@ static int ti_sn_bridge_parse_regulators(struct ti_sn_bridge *pdata) pdata->supplies); } -static int ti_sn_bridge_attach(struct drm_bridge *bridge) +static int ti_sn_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { int ret, val; struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); @@ -277,6 +278,11 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge) .node = NULL, }; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + ret = drm_connector_init(bridge->dev, &pdata->connector, &ti_sn_bridge_connector_funcs, DRM_MODE_CONNECTOR_eDP); diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 108e8cd7ab68..193c9368f664 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -118,11 +118,17 @@ static const struct drm_connector_funcs tfp410_con_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int tfp410_attach(struct drm_bridge *bridge) +static int tfp410_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + if (!bridge->encoder) { dev_err(dvi->dev, "Missing encoder\n"); return -ENODEV; diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 5a8f7d7e05f3..96eace94fea8 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -149,6 +149,7 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = { * @encoder: DRM encoder * @bridge: bridge to attach * @previous: previous bridge in the chain (optional) + * @flags: DRM_BRIDGE_ATTACH_* flags * * Called by a kms driver to link the bridge to an encoder's chain. The previous * argument specifies the previous bridge in the chain. If NULL, the bridge is @@ -166,7 +167,8 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = { * Zero on success, error code on failure */ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, - struct drm_bridge *previous) + struct drm_bridge *previous, + enum drm_bridge_attach_flags flags) { int ret; @@ -188,7 +190,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, list_add(&bridge->chain_node, &encoder->bridge_chain); if (bridge->funcs->attach) { - ret = bridge->funcs->attach(bridge); + ret = bridge->funcs->attach(bridge, flags); if (ret < 0) goto err_reset_bridge; } @@ -312,6 +314,20 @@ void drm_bridge_detach(struct drm_bridge *bridge) * allows providing a single static const &drm_bridge_funcs instance in * bridge drivers, improving security by storing function pointers in * read-only memory. + * + * In order to ease transition, bridge drivers may support both the old and + * new models by making connector creation optional and implementing the + * connected-related bridge operations. Connector creation is then controlled + * by the flags argument to the drm_bridge_attach() function. Display drivers + * that support the new model and create connectors themselves shall set the + * %DRM_BRIDGE_ATTACH_NO_CONNECTOR flag, and bridge drivers shall then skip + * connector creation. For intermediate bridges in the chain, the flag shall + * be passed to the drm_bridge_attach() call for the downstream bridge. + * Bridge drivers that implement the new model only shall return an error + * from their &drm_bridge_funcs.attach handler when the + * %DRM_BRIDGE_ATTACH_NO_CONNECTOR flag is not set. New display drivers + * should use the new model, and convert the bridge drivers they use if + * needed, in order to gradually transition to the new model. */ /** diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 15fb516ae2d8..8ff4504161d5 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -229,7 +229,7 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe, struct drm_bridge *bridge) { - return drm_bridge_attach(&pipe->encoder, bridge, NULL); + return drm_bridge_attach(&pipe->encoder, bridge, NULL, 0); } EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge); diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 4785885c0f4f..d23d3502ca91 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -106,7 +106,8 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data, /* Pre-empt DP connector creation if there's a bridge */ if (dp->ptn_bridge) { - ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge); + ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge, + 0); if (ret) { DRM_DEV_ERROR(dp->dev, "Failed to attach bridge to drm\n"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 33628d85edad..669d3857502a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1540,7 +1540,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host, out_bridge = of_drm_find_bridge(device->dev.of_node); if (out_bridge) { - drm_bridge_attach(encoder, out_bridge, NULL); + drm_bridge_attach(encoder, out_bridge, NULL, 0); dsi->out_bridge = out_bridge; list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain); } else { @@ -1717,7 +1717,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master, if (dsi->in_bridge_node) { in_bridge = of_drm_find_bridge(dsi->in_bridge_node); if (in_bridge) - drm_bridge_attach(encoder, in_bridge, NULL); + drm_bridge_attach(encoder, in_bridge, NULL, 0); } return mipi_dsi_host_register(&dsi->dsi_host); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 9ff921f43a93..3e5f1a77286d 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -960,7 +960,7 @@ static int hdmi_create_connector(struct drm_encoder *encoder) drm_connector_attach_encoder(connector, encoder); if (hdata->bridge) { - ret = drm_bridge_attach(encoder, hdata->bridge, NULL); + ret = drm_bridge_attach(encoder, hdata->bridge, NULL, 0); if (ret) DRM_DEV_ERROR(hdata->dev, "Failed to attach bridge\n"); } 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 9598ee3cc4d2..cff344367f81 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -151,5 +151,5 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) return fsl_dcu_attach_panel(fsl_dev, panel); } - return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL); + return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL, 0); } diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index bdcf9c6ae9e9..f31068d74b18 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -777,7 +777,7 @@ static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi) int ret; /* associate the bridge to dsi encoder */ - ret = drm_bridge_attach(encoder, bridge, NULL); + ret = drm_bridge_attach(encoder, bridge, NULL, 0); if (ret) { DRM_ERROR("failed to attach external bridge\n"); return ret; diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index a63790d32d75..c3332209f27a 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -1356,10 +1356,16 @@ static int tda998x_connector_init(struct tda998x_priv *priv, /* DRM bridge functions */ -static int tda998x_bridge_attach(struct drm_bridge *bridge) +static int tda998x_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge); + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + return tda998x_connector_init(priv, bridge->dev); } @@ -2022,7 +2028,7 @@ static int tda998x_encoder_init(struct device *dev, struct drm_device *drm) if (ret) goto err_encoder; - ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL); + ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL, 0); if (ret) goto err_bridge; diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index 8cb2665b2c74..4da22a94790c 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -446,7 +446,7 @@ static int imx_ldb_register(struct drm_device *drm, if (imx_ldb_ch->bridge) { ret = drm_bridge_attach(&imx_ldb_ch->encoder, - imx_ldb_ch->bridge, NULL); + imx_ldb_ch->bridge, NULL, 0); if (ret) { DRM_ERROR("Failed to initialize bridge with drm\n"); return ret; diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 142acff5ab37..08fafa4bf8c2 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -292,7 +292,7 @@ static int imx_pd_register(struct drm_device *drm, DRM_MODE_ENCODER_NONE, NULL); imxpd->bridge.funcs = &imx_pd_bridge_funcs; - drm_bridge_attach(encoder, &imxpd->bridge, NULL); + drm_bridge_attach(encoder, &imxpd->bridge, NULL, 0); if (!imxpd->next_bridge) { drm_connector_helper_add(&imxpd->connector, @@ -307,7 +307,7 @@ static int imx_pd_register(struct drm_device *drm, if (imxpd->next_bridge) { ret = drm_bridge_attach(encoder, imxpd->next_bridge, - &imxpd->bridge); + &imxpd->bridge, 0); if (ret < 0) { dev_err(imxpd->dev, "failed to attach bridge: %d\n", ret); diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c index 6d47ef7b148c..9dfe7cb530e1 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c @@ -737,7 +737,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) return ret; } - ret = drm_bridge_attach(&priv->encoder, bridge, NULL); + ret = drm_bridge_attach(&priv->encoder, bridge, NULL, 0); if (ret) { dev_err(dev, "Unable to attach bridge"); return ret; diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index bb6528b01cd0..7af5ebb0c436 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -986,7 +986,8 @@ static void mcde_dsi_bridge_disable(struct drm_bridge *bridge) clk_disable_unprepare(d->lp_clk); } -static int mcde_dsi_bridge_attach(struct drm_bridge *bridge) +static int mcde_dsi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); struct drm_device *drm = bridge->dev; @@ -998,7 +999,7 @@ static int mcde_dsi_bridge_attach(struct drm_bridge *bridge) } /* Attach the DSI bridge to the output (panel etc) bridge */ - ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge); + ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge, flags); if (ret) { dev_err(d->dev, "failed to attach the DSI bridge\n"); return ret; diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index 01fa8b8d763d..14fbe1c09ce9 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -607,7 +607,7 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data) /* Currently DPI0 is fixed to be driven by OVL1 */ dpi->encoder.possible_crtcs = BIT(1); - ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL); + ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL, 0); if (ret) { dev_err(dev, "Failed to attach bridge: %d\n", ret); goto err_cleanup; diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 5fa1073cf26b..0ede69830a9d 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -904,7 +904,7 @@ static int mtk_dsi_create_conn_enc(struct drm_device *drm, struct mtk_dsi *dsi) /* If there's a bridge, attach to it and let it create the connector */ if (dsi->bridge) { - ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL); + ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL, 0); if (ret) { DRM_ERROR("Failed to attach bridge to drm\n"); goto err_encoder_cleanup; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index 5e4a4dbda443..a8b20557539b 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1297,11 +1297,17 @@ static void mtk_hdmi_hpd_event(bool hpd, struct device *dev) * Bridge callbacks */ -static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge) +static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge); int ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } + ret = drm_connector_init_with_ddc(bridge->encoder->dev, &hdmi->conn, &mtk_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA, @@ -1326,7 +1332,7 @@ static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge) if (hdmi->next_bridge) { ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, - bridge); + bridge, flags); if (ret) { dev_err(hdmi->dev, "Failed to attach external bridge: %d\n", ret); diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 104115d112eb..6af26ab5b09d 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -684,7 +684,7 @@ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id) bridge = &dsi_bridge->base; bridge->funcs = &dsi_mgr_bridge_funcs; - ret = drm_bridge_attach(encoder, bridge, NULL); + ret = drm_bridge_attach(encoder, bridge, NULL, 0); if (ret) goto fail; @@ -713,7 +713,7 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id) encoder = msm_dsi->encoder; /* link the internal dsi bridge to the external bridge */ - drm_bridge_attach(encoder, ext_bridge, int_bridge); + drm_bridge_attach(encoder, ext_bridge, int_bridge, 0); /* * we need the drm_connector created by the external bridge diff --git a/drivers/gpu/drm/msm/edp/edp.c b/drivers/gpu/drm/msm/edp/edp.c index ad4e963ccd9b..a78d6077802b 100644 --- a/drivers/gpu/drm/msm/edp/edp.c +++ b/drivers/gpu/drm/msm/edp/edp.c @@ -178,7 +178,7 @@ int msm_edp_modeset_init(struct msm_edp *edp, struct drm_device *dev, goto fail; } - ret = drm_bridge_attach(encoder, edp->bridge, NULL); + ret = drm_bridge_attach(encoder, edp->bridge, NULL, 0); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c index b65b5cc2dba2..c69a37e0c708 100644 --- a/drivers/gpu/drm/msm/edp/edp_bridge.c +++ b/drivers/gpu/drm/msm/edp/edp_bridge.c @@ -97,7 +97,7 @@ struct drm_bridge *msm_edp_bridge_init(struct msm_edp *edp) bridge = &edp_bridge->base; bridge->funcs = &edp_bridge_funcs; - ret = drm_bridge_attach(edp->encoder, bridge, NULL); + ret = drm_bridge_attach(edp->encoder, bridge, NULL, 0); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 1a9b6289637d..3a8646535c14 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -327,7 +327,7 @@ int msm_hdmi_modeset_init(struct hdmi *hdmi, goto fail; } - ret = drm_bridge_attach(encoder, hdmi->bridge, NULL); + ret = drm_bridge_attach(encoder, hdmi->bridge, NULL, 0); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index ba81338a9bf8..6e380db9287b 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -287,7 +287,7 @@ struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi) bridge = &hdmi_bridge->base; bridge->funcs = &msm_hdmi_bridge_funcs; - ret = drm_bridge_attach(hdmi->encoder, bridge, NULL); + ret = drm_bridge_attach(hdmi->encoder, bridge, NULL, 0); if (ret) goto fail; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index d2750f60f519..390e0662a8b8 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -297,7 +297,7 @@ static int omap_modeset_init(struct drm_device *dev) if (pipe->output->bridge) { ret = drm_bridge_attach(pipe->encoder, - pipe->output->bridge, NULL); + pipe->output->bridge, NULL, 0); if (ret < 0) return ret; } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 3cd83a030a04..c07c6a88aff0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -121,7 +121,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, * Attach the bridge to the encoder. The bridge will create the * connector. */ - ret = drm_bridge_attach(encoder, bridge, NULL); + ret = drm_bridge_attach(encoder, bridge, NULL, 0); if (ret) { drm_encoder_cleanup(encoder); return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 06432c881e07..ab0d49618cf9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "rcar_lvds.h" @@ -643,7 +644,8 @@ static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge, return true; } -static int rcar_lvds_attach(struct drm_bridge *bridge) +static int rcar_lvds_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); struct drm_connector *connector = &lvds->connector; @@ -653,7 +655,12 @@ static int rcar_lvds_attach(struct drm_bridge *bridge) /* If we have a next bridge just attach it. */ if (lvds->next_bridge) return drm_bridge_attach(bridge->encoder, lvds->next_bridge, - bridge); + bridge, flags); + + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { + DRM_ERROR("Fix bridge driver to make connector optional!"); + return -EINVAL; + } /* Otherwise if we have a panel, create a connector. */ if (!lvds->panel) diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index f25a36743cbd..449a62908d21 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -646,7 +646,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, goto err_free_connector; } } else { - ret = drm_bridge_attach(encoder, lvds->bridge, NULL); + ret = drm_bridge_attach(encoder, lvds->bridge, NULL, 0); if (ret) { DRM_DEV_ERROR(drm_dev->dev, "failed to attach bridge: %d\n", ret); diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c index ae730275a34f..3e2484985955 100644 --- a/drivers/gpu/drm/rockchip/rockchip_rgb.c +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c @@ -144,7 +144,7 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev, rgb->bridge = bridge; - ret = drm_bridge_attach(encoder, rgb->bridge, NULL); + ret = drm_bridge_attach(encoder, rgb->bridge, NULL, 0); if (ret) { DRM_DEV_ERROR(drm_dev->dev, "failed to attach bridge: %d\n", ret); diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index b2778ec1cdd7..3d04bfca21a0 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -467,7 +467,7 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) bridge->of_node = dvo->dev.of_node; drm_bridge_add(bridge); - err = drm_bridge_attach(encoder, bridge, NULL); + err = drm_bridge_attach(encoder, bridge, NULL, 0); if (err) { DRM_ERROR("Failed to attach bridge\n"); return err; diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 2bb32009d117..f3f28d79b0e4 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -701,7 +701,7 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) bridge->driver_private = hda; bridge->funcs = &sti_hda_bridge_funcs; - drm_bridge_attach(encoder, bridge, NULL); + drm_bridge_attach(encoder, bridge, NULL, 0); connector->encoder = encoder; diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 64ed102033c8..18eaf786ffa4 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -1281,7 +1281,7 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) bridge->driver_private = hdmi; bridge->funcs = &sti_hdmi_bridge_funcs; - drm_bridge_attach(encoder, bridge, NULL); + drm_bridge_attach(encoder, bridge, NULL, 0); connector->encoder = encoder; diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 99bf93e8b36f..df585fe64f61 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -1109,7 +1109,7 @@ static int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge) drm_encoder_helper_add(encoder, <dc_encoder_helper_funcs); - ret = drm_bridge_attach(encoder, bridge, NULL); + ret = drm_bridge_attach(encoder, bridge, NULL, 0); if (ret) { drm_encoder_cleanup(encoder); return -EINVAL; diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c index 65b7a8739666..26e5c7ceb8ff 100644 --- a/drivers/gpu/drm/sun4i/sun4i_lvds.c +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c @@ -156,7 +156,7 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon) } if (bridge) { - ret = drm_bridge_attach(encoder, bridge, NULL); + ret = drm_bridge_attach(encoder, bridge, NULL, 0); if (ret) { dev_err(drm->dev, "Couldn't attach our bridge\n"); goto err_cleanup_connector; diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index b27f16af50f5..3b23d5be3cf3 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -253,7 +253,7 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon) } if (rgb->bridge) { - ret = drm_bridge_attach(encoder, rgb->bridge, NULL); + ret = drm_bridge_attach(encoder, rgb->bridge, NULL, 0); if (ret) { dev_err(drm->dev, "Couldn't attach our bridge\n"); goto err_cleanup_connector; diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index 5311e0f1c551..3b6f8d54a016 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -172,7 +172,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) return PTR_ERR(enc); } - ret = drm_bridge_attach(enc, pipes[i].bridge, NULL); + ret = drm_bridge_attach(enc, pipes[i].bridge, NULL, 0); if (ret) { dev_err(tidss->dev, "bridge attach failed: %d\n", ret); return ret; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c index 51d034e095f4..28b7f703236e 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c @@ -95,7 +95,7 @@ int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge) priv->external_encoder->possible_crtcs = BIT(0); - ret = drm_bridge_attach(priv->external_encoder, bridge, NULL); + ret = drm_bridge_attach(priv->external_encoder, bridge, NULL, 0); if (ret) { dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret); return ret; diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index c586325de2a5..6dfede03396e 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -252,7 +252,7 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DPI); - return drm_bridge_attach(dpi->encoder, bridge, NULL); + return drm_bridge_attach(dpi->encoder, bridge, NULL, 0); } static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index fd8a2eb60505..d99b1d526651 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1619,7 +1619,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) DRM_MODE_ENCODER_DSI, NULL); drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); - ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL); + ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL, 0); if (ret) { dev_err(dev, "bridge attach failed: %d\n", ret); return ret; diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 018e195c4808..ea2aa5ebae34 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -39,6 +39,17 @@ struct drm_panel; struct edid; struct i2c_adapter; +/** + * enum drm_bridge_attach_flags - Flags for &drm_bridge_funcs.attach + */ +enum drm_bridge_attach_flags { + /** + * @DRM_BRIDGE_ATTACH_NO_CONNECTOR: When this flag is set the bridge + * shall not create a drm_connector. + */ + DRM_BRIDGE_ATTACH_NO_CONNECTOR = BIT(0), +}; + /** * struct drm_bridge_funcs - drm_bridge control functions */ @@ -47,7 +58,8 @@ struct drm_bridge_funcs { * @attach: * * This callback is invoked whenever our bridge is being attached to a - * &drm_encoder. + * &drm_encoder. The flags argument tunes the behaviour of the attach + * operation (see DRM_BRIDGE_ATTACH_*). * * The @attach callback is optional. * @@ -55,7 +67,8 @@ struct drm_bridge_funcs { * * Zero on success, error code on failure. */ - int (*attach)(struct drm_bridge *bridge); + int (*attach)(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags); /** * @detach: @@ -757,7 +770,8 @@ void drm_bridge_add(struct drm_bridge *bridge); void drm_bridge_remove(struct drm_bridge *bridge); struct drm_bridge *of_drm_find_bridge(struct device_node *np); int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, - struct drm_bridge *previous); + struct drm_bridge *previous, + enum drm_bridge_attach_flags flags); /** * drm_bridge_get_next_bridge() - Get the next bridge in the chain From 94ded532ffdb42c2b32bfb508759a951506dd7dd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:30 +0200 Subject: [PATCH 26/78] drm/bridge: dumb-vga-dac: Rename internal symbols to simple-bridge The dumb-vga-dac driver is a simple DRM bridge driver for simple VGA DACs that don't require configuration. Other non-VGA bridges fall in a similar category, and would benefit from a common driver. Prepare for this by renaming the internal symbols from dumb-vga-dac to simple-bridge. Signed-off-by: Laurent Pinchart Reviewed-by: Andrzej Hajda Reviewed-by: Boris Brezillon Reviewed-by: Maxime Ripard Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-11-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/dumb-vga-dac.c | 154 +++++++++++++------------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c index ad5b5a849e43..7287be2d3220 100644 --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c +++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c @@ -17,7 +17,7 @@ #include #include -struct dumb_vga { +struct simple_bridge { struct drm_bridge bridge; struct drm_connector connector; @@ -25,28 +25,28 @@ struct dumb_vga { struct regulator *vdd; }; -static inline struct dumb_vga * -drm_bridge_to_dumb_vga(struct drm_bridge *bridge) +static inline struct simple_bridge * +drm_bridge_to_simple_bridge(struct drm_bridge *bridge) { - return container_of(bridge, struct dumb_vga, bridge); + return container_of(bridge, struct simple_bridge, bridge); } -static inline struct dumb_vga * -drm_connector_to_dumb_vga(struct drm_connector *connector) +static inline struct simple_bridge * +drm_connector_to_simple_bridge(struct drm_connector *connector) { - return container_of(connector, struct dumb_vga, connector); + return container_of(connector, struct simple_bridge, connector); } -static int dumb_vga_get_modes(struct drm_connector *connector) +static int simple_bridge_get_modes(struct drm_connector *connector) { - struct dumb_vga *vga = drm_connector_to_dumb_vga(connector); + struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector); struct edid *edid; int ret; - if (!vga->ddc) + if (!sbridge->ddc) goto fallback; - edid = drm_get_edid(connector, vga->ddc); + edid = drm_get_edid(connector, sbridge->ddc); if (!edid) { DRM_INFO("EDID readout failed, falling back to standard modes\n"); goto fallback; @@ -70,14 +70,14 @@ fallback: return ret; } -static const struct drm_connector_helper_funcs dumb_vga_con_helper_funcs = { - .get_modes = dumb_vga_get_modes, +static const struct drm_connector_helper_funcs simple_bridge_con_helper_funcs = { + .get_modes = simple_bridge_get_modes, }; static enum drm_connector_status -dumb_vga_connector_detect(struct drm_connector *connector, bool force) +simple_bridge_connector_detect(struct drm_connector *connector, bool force) { - struct dumb_vga *vga = drm_connector_to_dumb_vga(connector); + struct simple_bridge *sbridge = drm_connector_to_simple_bridge(connector); /* * Even if we have an I2C bus, we can't assume that the cable @@ -85,14 +85,14 @@ dumb_vga_connector_detect(struct drm_connector *connector, bool force) * wire the DDC pins, or the I2C bus might not be working at * all. */ - if (vga->ddc && drm_probe_ddc(vga->ddc)) + if (sbridge->ddc && drm_probe_ddc(sbridge->ddc)) return connector_status_connected; return connector_status_unknown; } -static const struct drm_connector_funcs dumb_vga_con_funcs = { - .detect = dumb_vga_connector_detect, +static const struct drm_connector_funcs simple_bridge_con_funcs = { + .detect = simple_bridge_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, @@ -100,10 +100,10 @@ static const struct drm_connector_funcs dumb_vga_con_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int dumb_vga_attach(struct drm_bridge *bridge, - enum drm_bridge_attach_flags flags) +static int simple_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { - struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge); + struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge); int ret; if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { @@ -116,50 +116,50 @@ static int dumb_vga_attach(struct drm_bridge *bridge, return -ENODEV; } - drm_connector_helper_add(&vga->connector, - &dumb_vga_con_helper_funcs); - ret = drm_connector_init_with_ddc(bridge->dev, &vga->connector, - &dumb_vga_con_funcs, + drm_connector_helper_add(&sbridge->connector, + &simple_bridge_con_helper_funcs); + ret = drm_connector_init_with_ddc(bridge->dev, &sbridge->connector, + &simple_bridge_con_funcs, DRM_MODE_CONNECTOR_VGA, - vga->ddc); + sbridge->ddc); if (ret) { DRM_ERROR("Failed to initialize connector\n"); return ret; } - drm_connector_attach_encoder(&vga->connector, + drm_connector_attach_encoder(&sbridge->connector, bridge->encoder); return 0; } -static void dumb_vga_enable(struct drm_bridge *bridge) +static void simple_bridge_enable(struct drm_bridge *bridge) { - struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge); + struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge); int ret = 0; - if (vga->vdd) - ret = regulator_enable(vga->vdd); + if (sbridge->vdd) + ret = regulator_enable(sbridge->vdd); if (ret) DRM_ERROR("Failed to enable vdd regulator: %d\n", ret); } -static void dumb_vga_disable(struct drm_bridge *bridge) +static void simple_bridge_disable(struct drm_bridge *bridge) { - struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge); + struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge); - if (vga->vdd) - regulator_disable(vga->vdd); + if (sbridge->vdd) + regulator_disable(sbridge->vdd); } -static const struct drm_bridge_funcs dumb_vga_bridge_funcs = { - .attach = dumb_vga_attach, - .enable = dumb_vga_enable, - .disable = dumb_vga_disable, +static const struct drm_bridge_funcs simple_bridge_bridge_funcs = { + .attach = simple_bridge_attach, + .enable = simple_bridge_enable, + .disable = simple_bridge_disable, }; -static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev) +static struct i2c_adapter *simple_bridge_retrieve_ddc(struct device *dev) { struct device_node *phandle, *remote; struct i2c_adapter *ddc; @@ -181,53 +181,53 @@ static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev) return ddc; } -static int dumb_vga_probe(struct platform_device *pdev) +static int simple_bridge_probe(struct platform_device *pdev) { - struct dumb_vga *vga; + struct simple_bridge *sbridge; - vga = devm_kzalloc(&pdev->dev, sizeof(*vga), GFP_KERNEL); - if (!vga) + sbridge = devm_kzalloc(&pdev->dev, sizeof(*sbridge), GFP_KERNEL); + if (!sbridge) return -ENOMEM; - platform_set_drvdata(pdev, vga); + platform_set_drvdata(pdev, sbridge); - vga->vdd = devm_regulator_get_optional(&pdev->dev, "vdd"); - if (IS_ERR(vga->vdd)) { - int ret = PTR_ERR(vga->vdd); + sbridge->vdd = devm_regulator_get_optional(&pdev->dev, "vdd"); + if (IS_ERR(sbridge->vdd)) { + int ret = PTR_ERR(sbridge->vdd); if (ret == -EPROBE_DEFER) return -EPROBE_DEFER; - vga->vdd = NULL; + sbridge->vdd = NULL; dev_dbg(&pdev->dev, "No vdd regulator found: %d\n", ret); } - vga->ddc = dumb_vga_retrieve_ddc(&pdev->dev); - if (IS_ERR(vga->ddc)) { - if (PTR_ERR(vga->ddc) == -ENODEV) { + sbridge->ddc = simple_bridge_retrieve_ddc(&pdev->dev); + if (IS_ERR(sbridge->ddc)) { + if (PTR_ERR(sbridge->ddc) == -ENODEV) { dev_dbg(&pdev->dev, "No i2c bus specified. Disabling EDID readout\n"); - vga->ddc = NULL; + sbridge->ddc = NULL; } else { dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n"); - return PTR_ERR(vga->ddc); + return PTR_ERR(sbridge->ddc); } } - vga->bridge.funcs = &dumb_vga_bridge_funcs; - vga->bridge.of_node = pdev->dev.of_node; - vga->bridge.timings = of_device_get_match_data(&pdev->dev); + sbridge->bridge.funcs = &simple_bridge_bridge_funcs; + sbridge->bridge.of_node = pdev->dev.of_node; + sbridge->bridge.timings = of_device_get_match_data(&pdev->dev); - drm_bridge_add(&vga->bridge); + drm_bridge_add(&sbridge->bridge); return 0; } -static int dumb_vga_remove(struct platform_device *pdev) +static int simple_bridge_remove(struct platform_device *pdev) { - struct dumb_vga *vga = platform_get_drvdata(pdev); + struct simple_bridge *sbridge = platform_get_drvdata(pdev); - drm_bridge_remove(&vga->bridge); + drm_bridge_remove(&sbridge->bridge); - if (vga->ddc) - i2c_put_adapter(vga->ddc); + if (sbridge->ddc) + i2c_put_adapter(sbridge->ddc); return 0; } @@ -238,7 +238,7 @@ static int dumb_vga_remove(struct platform_device *pdev) * NOTE: the ADV7123EP seems to have other timings and need a new timings * set if used. */ -static const struct drm_bridge_timings default_dac_timings = { +static const struct drm_bridge_timings default_bridge_timings = { /* Timing specifications, datasheet page 7 */ .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, .setup_time_ps = 500, @@ -249,7 +249,7 @@ static const struct drm_bridge_timings default_dac_timings = { * Information taken from the THS8134, THS8134A, THS8134B datasheet named * "SLVS205D", dated May 1990, revised March 2000. */ -static const struct drm_bridge_timings ti_ths8134_dac_timings = { +static const struct drm_bridge_timings ti_ths8134_bridge_timings = { /* From timing diagram, datasheet page 9 */ .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, /* From datasheet, page 12 */ @@ -262,7 +262,7 @@ static const struct drm_bridge_timings ti_ths8134_dac_timings = { * Information taken from the THS8135 datasheet named "SLAS343B", dated * May 2001, revised April 2013. */ -static const struct drm_bridge_timings ti_ths8135_dac_timings = { +static const struct drm_bridge_timings ti_ths8135_bridge_timings = { /* From timing diagram, datasheet page 14 */ .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, /* From datasheet, page 16 */ @@ -270,37 +270,37 @@ static const struct drm_bridge_timings ti_ths8135_dac_timings = { .hold_time_ps = 500, }; -static const struct of_device_id dumb_vga_match[] = { +static const struct of_device_id simple_bridge_match[] = { { .compatible = "dumb-vga-dac", .data = NULL, }, { .compatible = "adi,adv7123", - .data = &default_dac_timings, + .data = &default_bridge_timings, }, { .compatible = "ti,ths8135", - .data = &ti_ths8135_dac_timings, + .data = &ti_ths8135_bridge_timings, }, { .compatible = "ti,ths8134", - .data = &ti_ths8134_dac_timings, + .data = &ti_ths8134_bridge_timings, }, {}, }; -MODULE_DEVICE_TABLE(of, dumb_vga_match); +MODULE_DEVICE_TABLE(of, simple_bridge_match); -static struct platform_driver dumb_vga_driver = { - .probe = dumb_vga_probe, - .remove = dumb_vga_remove, +static struct platform_driver simple_bridge_driver = { + .probe = simple_bridge_probe, + .remove = simple_bridge_remove, .driver = { .name = "dumb-vga-dac", - .of_match_table = dumb_vga_match, + .of_match_table = simple_bridge_match, }, }; -module_platform_driver(dumb_vga_driver); +module_platform_driver(simple_bridge_driver); MODULE_AUTHOR("Maxime Ripard "); -MODULE_DESCRIPTION("Dumb VGA DAC bridge driver"); +MODULE_DESCRIPTION("Simple DRM bridge driver"); MODULE_LICENSE("GPL"); From 0411374bdf2b3cc12a9a4c23f0a1c19814d041b3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:31 +0200 Subject: [PATCH 27/78] drm/bridge: dumb-vga-dac: Rename driver to simple-bridge The dumb-vga-dac driver can support simple DRM bridges without being limited to VGA DACs. Rename it to simple-bridge. Signed-off-by: Laurent Pinchart Reviewed-by: Andrzej Hajda Reviewed-by: Boris Brezillon Acked-by: Maxime Ripard Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-12-laurent.pinchart@ideasonboard.com --- arch/arm/configs/davinci_all_defconfig | 2 +- arch/arm/configs/integrator_defconfig | 2 +- arch/arm/configs/multi_v7_defconfig | 2 +- arch/arm/configs/shmobile_defconfig | 2 +- arch/arm/configs/sunxi_defconfig | 2 +- arch/arm/configs/versatile_defconfig | 2 +- drivers/gpu/drm/bridge/Kconfig | 16 ++++++++-------- drivers/gpu/drm/bridge/Makefile | 2 +- .../bridge/{dumb-vga-dac.c => simple-bridge.c} | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) rename drivers/gpu/drm/bridge/{dumb-vga-dac.c => simple-bridge.c} (99%) diff --git a/arch/arm/configs/davinci_all_defconfig b/arch/arm/configs/davinci_all_defconfig index b5ba8d731a25..e849367c0566 100644 --- a/arch/arm/configs/davinci_all_defconfig +++ b/arch/arm/configs/davinci_all_defconfig @@ -158,7 +158,7 @@ CONFIG_VIDEO_TVP514X=m CONFIG_VIDEO_ADV7343=m CONFIG_DRM=m CONFIG_DRM_TILCDC=m -CONFIG_DRM_DUMB_VGA_DAC=m +CONFIG_DRM_SIMPLE_BRIDGE=m CONFIG_DRM_TINYDRM=m CONFIG_TINYDRM_ST7586=m CONFIG_FB=y diff --git a/arch/arm/configs/integrator_defconfig b/arch/arm/configs/integrator_defconfig index 2f0a762dc3a0..a9755c501bec 100644 --- a/arch/arm/configs/integrator_defconfig +++ b/arch/arm/configs/integrator_defconfig @@ -55,7 +55,7 @@ CONFIG_SMC91X=y # CONFIG_KEYBOARD_ATKBD is not set # CONFIG_SERIO_SERPORT is not set CONFIG_DRM=y -CONFIG_DRM_DUMB_VGA_DAC=y +CONFIG_DRM_SIMPLE_BRIDGE=y CONFIG_DRM_PL111=y CONFIG_FB_MODE_HELPERS=y CONFIG_FB_MATROX=y diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index 017d65f86eba..0b020863abdb 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -670,11 +670,11 @@ CONFIG_DRM_PANEL_ORISETECH_OTM8009A=m CONFIG_DRM_PANEL_RAYDIUM_RM68200=m CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03=m CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=m -CONFIG_DRM_DUMB_VGA_DAC=m CONFIG_DRM_NXP_PTN3460=m CONFIG_DRM_PARADE_PS8622=m CONFIG_DRM_SII902X=m CONFIG_DRM_SII9234=m +CONFIG_DRM_SIMPLE_BRIDGE=m CONFIG_DRM_TOSHIBA_TC358764=m CONFIG_DRM_I2C_ADV7511=m CONFIG_DRM_I2C_ADV7511_AUDIO=y diff --git a/arch/arm/configs/shmobile_defconfig b/arch/arm/configs/shmobile_defconfig index 64fa849f8bbe..838307a9bb92 100644 --- a/arch/arm/configs/shmobile_defconfig +++ b/arch/arm/configs/shmobile_defconfig @@ -125,9 +125,9 @@ CONFIG_VIDEO_ML86V7667=y CONFIG_DRM=y CONFIG_DRM_RCAR_DU=y CONFIG_DRM_PANEL_SIMPLE=y -CONFIG_DRM_DUMB_VGA_DAC=y CONFIG_DRM_LVDS_CODEC=y CONFIG_DRM_SII902X=y +CONFIG_DRM_SIMPLE_BRIDGE=y CONFIG_DRM_I2C_ADV7511=y CONFIG_DRM_I2C_ADV7511_AUDIO=y CONFIG_FB_SH_MOBILE_LCDC=y diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig index e9fb57374b9f..61b8be19e527 100644 --- a/arch/arm/configs/sunxi_defconfig +++ b/arch/arm/configs/sunxi_defconfig @@ -101,7 +101,7 @@ CONFIG_RC_DEVICES=y CONFIG_IR_SUNXI=y CONFIG_DRM=y CONFIG_DRM_SUN4I=y -CONFIG_DRM_DUMB_VGA_DAC=y +CONFIG_DRM_SIMPLE_BRIDGE=y CONFIG_FB_SIMPLE=y CONFIG_SOUND=y CONFIG_SND=y diff --git a/arch/arm/configs/versatile_defconfig b/arch/arm/configs/versatile_defconfig index fe4d4b596585..767935337413 100644 --- a/arch/arm/configs/versatile_defconfig +++ b/arch/arm/configs/versatile_defconfig @@ -59,7 +59,7 @@ CONFIG_GPIO_PL061=y CONFIG_DRM=y CONFIG_DRM_PANEL_ARM_VERSATILE=y CONFIG_DRM_PANEL_SIMPLE=y -CONFIG_DRM_DUMB_VGA_DAC=y +CONFIG_DRM_SIMPLE_BRIDGE=y CONFIG_DRM_PL111=y CONFIG_FB_MODE_HELPERS=y CONFIG_BACKLIGHT_CLASS_DEVICE=y diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 20a439199cb8..10073ad88283 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -27,14 +27,6 @@ config DRM_CDNS_DSI Support Cadence DPI to DSI bridge. This is an internal bridge and is meant to be directly embedded in a SoC. -config DRM_DUMB_VGA_DAC - tristate "Dumb VGA DAC Bridge support" - depends on OF - select DRM_KMS_HELPER - help - Support for non-programmable RGB to VGA DAC bridges, such as ADI - ADV7123, TI THS8134 and THS8135 or passive resistor ladder DACs. - config DRM_LVDS_CODEC tristate "Transparent LVDS encoders and decoders support" depends on OF @@ -110,6 +102,14 @@ config DRM_SII9234 It is an I2C driver, that detects connection of MHL bridge and starts encapsulation of HDMI signal. +config DRM_SIMPLE_BRIDGE + tristate "Simple DRM bridge support" + depends on OF + select DRM_KMS_HELPER + help + Support for non-programmable DRM bridges, such as ADI ADV7123, TI + THS8134 and THS8135 or passive resistor ladder DACs. + config DRM_THINE_THC63LVD1024 tristate "Thine THC63LVD1024 LVDS decoder bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index b0d5c3af0b5a..b6b2e7029a78 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o -obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o @@ -9,6 +8,7 @@ obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o obj-$(CONFIG_DRM_SII902X) += sii902x.o obj-$(CONFIG_DRM_SII9234) += sii9234.o +obj-$(CONFIG_DRM_SIMPLE_BRIDGE) += simple-bridge.o obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o obj-$(CONFIG_DRM_TOSHIBA_TC358764) += tc358764.o obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/simple-bridge.c similarity index 99% rename from drivers/gpu/drm/bridge/dumb-vga-dac.c rename to drivers/gpu/drm/bridge/simple-bridge.c index 7287be2d3220..00d810c99193 100644 --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -295,7 +295,7 @@ static struct platform_driver simple_bridge_driver = { .probe = simple_bridge_probe, .remove = simple_bridge_remove, .driver = { - .name = "dumb-vga-dac", + .name = "simple-bridge", .of_match_table = simple_bridge_match, }, }; From 272378ec0eb972100496c3be44be03b86caa9ca3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:32 +0200 Subject: [PATCH 28/78] drm/bridge: simple-bridge: Add support for non-VGA bridges Create a new simple_bridge_info structure that stores information about the bridge model, and store the bridge timings in there, along with the connector type. Use that new structure for of_device_id data. This enables support for non-VGA bridges. Signed-off-by: Laurent Pinchart Reviewed-by: Andrzej Hajda Reviewed-by: Stefan Agner Reviewed-by: Boris Brezillon Reviewed-by: Maxime Ripard Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-13-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/simple-bridge.c | 41 ++++++++++++++++++-------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index 00d810c99193..20866c1230de 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -17,10 +17,17 @@ #include #include +struct simple_bridge_info { + const struct drm_bridge_timings *timings; + unsigned int connector_type; +}; + struct simple_bridge { struct drm_bridge bridge; struct drm_connector connector; + const struct simple_bridge_info *info; + struct i2c_adapter *ddc; struct regulator *vdd; }; @@ -120,7 +127,7 @@ static int simple_bridge_attach(struct drm_bridge *bridge, &simple_bridge_con_helper_funcs); ret = drm_connector_init_with_ddc(bridge->dev, &sbridge->connector, &simple_bridge_con_funcs, - DRM_MODE_CONNECTOR_VGA, + sbridge->info->connector_type, sbridge->ddc); if (ret) { DRM_ERROR("Failed to initialize connector\n"); @@ -190,6 +197,8 @@ static int simple_bridge_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, sbridge); + sbridge->info = of_device_get_match_data(&pdev->dev); + sbridge->vdd = devm_regulator_get_optional(&pdev->dev, "vdd"); if (IS_ERR(sbridge->vdd)) { int ret = PTR_ERR(sbridge->vdd); @@ -213,7 +222,7 @@ static int simple_bridge_probe(struct platform_device *pdev) sbridge->bridge.funcs = &simple_bridge_bridge_funcs; sbridge->bridge.of_node = pdev->dev.of_node; - sbridge->bridge.timings = of_device_get_match_data(&pdev->dev); + sbridge->bridge.timings = sbridge->info->timings; drm_bridge_add(&sbridge->bridge); @@ -273,19 +282,27 @@ static const struct drm_bridge_timings ti_ths8135_bridge_timings = { static const struct of_device_id simple_bridge_match[] = { { .compatible = "dumb-vga-dac", - .data = NULL, - }, - { + .data = &(const struct simple_bridge_info) { + .connector_type = DRM_MODE_CONNECTOR_VGA, + }, + }, { .compatible = "adi,adv7123", - .data = &default_bridge_timings, - }, - { + .data = &(const struct simple_bridge_info) { + .timings = &default_bridge_timings, + .connector_type = DRM_MODE_CONNECTOR_VGA, + }, + }, { .compatible = "ti,ths8135", - .data = &ti_ths8135_bridge_timings, - }, - { + .data = &(const struct simple_bridge_info) { + .timings = &ti_ths8135_bridge_timings, + .connector_type = DRM_MODE_CONNECTOR_VGA, + }, + }, { .compatible = "ti,ths8134", - .data = &ti_ths8134_bridge_timings, + .data = &(const struct simple_bridge_info) { + .timings = &ti_ths8134_bridge_timings, + .connector_type = DRM_MODE_CONNECTOR_VGA, + }, }, {}, }; From 2df6428e290a61367b1a54db56fe49000dc4c49d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:33 +0200 Subject: [PATCH 29/78] drm/bridge: simple-bridge: Add support for enable GPIO If an enable GPIO is declared in the firmware, assert it when enabling the bridge and deassert it when disabling it. Signed-off-by: Laurent Pinchart Reviewed-by: Andrzej Hajda Reviewed-by: Stefan Agner Reviewed-by: Boris Brezillon Reviewed-by: Maxime Ripard Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-14-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/simple-bridge.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index 20866c1230de..70e6092bdf6c 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -6,6 +6,7 @@ * Maxime Ripard */ +#include #include #include #include @@ -30,6 +31,7 @@ struct simple_bridge { struct i2c_adapter *ddc; struct regulator *vdd; + struct gpio_desc *enable; }; static inline struct simple_bridge * @@ -143,19 +145,23 @@ static int simple_bridge_attach(struct drm_bridge *bridge, static void simple_bridge_enable(struct drm_bridge *bridge) { struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge); - int ret = 0; + int ret; - if (sbridge->vdd) + if (sbridge->vdd) { ret = regulator_enable(sbridge->vdd); + if (ret) + DRM_ERROR("Failed to enable vdd regulator: %d\n", ret); + } - if (ret) - DRM_ERROR("Failed to enable vdd regulator: %d\n", ret); + gpiod_set_value_cansleep(sbridge->enable, 1); } static void simple_bridge_disable(struct drm_bridge *bridge) { struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge); + gpiod_set_value_cansleep(sbridge->enable, 0); + if (sbridge->vdd) regulator_disable(sbridge->vdd); } @@ -208,6 +214,14 @@ static int simple_bridge_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "No vdd regulator found: %d\n", ret); } + sbridge->enable = devm_gpiod_get_optional(&pdev->dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(sbridge->enable)) { + if (PTR_ERR(sbridge->enable) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Unable to retrieve enable GPIO\n"); + return PTR_ERR(sbridge->enable); + } + sbridge->ddc = simple_bridge_retrieve_ddc(&pdev->dev); if (IS_ERR(sbridge->ddc)) { if (PTR_ERR(sbridge->ddc) == -ENODEV) { From ca00e10b50ad63681288c4bf738e9dddffc7cf2c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:34 +0200 Subject: [PATCH 30/78] drm/bridge: simple-bridge: Add support for the TI OPA362 The TI OPA362 is an analog video amplifier controlled through a GPIO. Add support for it to the simple-bridge driver. Signed-off-by: Laurent Pinchart Reviewed-by: Andrzej Hajda Reviewed-by: Boris Brezillon Reviewed-by: Maxime Ripard Reviewed-by: Tomi Valkeinen Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-15-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/simple-bridge.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index 70e6092bdf6c..a2dca7a3ef03 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -305,6 +305,11 @@ static const struct of_device_id simple_bridge_match[] = { .timings = &default_bridge_timings, .connector_type = DRM_MODE_CONNECTOR_VGA, }, + }, { + .compatible = "ti,opa362", + .data = &(const struct simple_bridge_info) { + .connector_type = DRM_MODE_CONNECTOR_Composite, + }, }, { .compatible = "ti,ths8135", .data = &(const struct simple_bridge_info) { From 0c275c30176b2e7871c6ac5fb0ed548f81e0fa27 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:35 +0200 Subject: [PATCH 31/78] drm/bridge: Add bridge driver for display connectors Display connectors are modelled in DT as a device node, but have so far been handled manually in several bridge drivers. This resulted in duplicate code in several bridge drivers, with slightly different (and thus confusing) logics. In order to fix this, implement a bridge driver for display connectors. The driver centralises logic for the DVI, HDMI, VGAn composite and S-video connectors and exposes corresponding bridge operations. This driver in itself doesn't solve the issue completely, changes in bridge and display controller drivers are needed to make use of the new connector driver. Signed-off-by: Laurent Pinchart Reviewed-by: Maxime Ripard Reviewed-by: Boris Brezillon Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-16-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/Kconfig | 11 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/display-connector.c | 295 +++++++++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 drivers/gpu/drm/bridge/display-connector.c diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 10073ad88283..d63283661850 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -27,6 +27,17 @@ config DRM_CDNS_DSI Support Cadence DPI to DSI bridge. This is an internal bridge and is meant to be directly embedded in a SoC. +config DRM_DISPLAY_CONNECTOR + tristate "Display connector support" + depends on OF + help + Driver for display connectors with support for DDC and hot-plug + detection. Most display controller handle display connectors + internally and don't need this driver, but the DRM subsystem is + moving towards separating connector handling from display controllers + on ARM-based platforms. Saying Y here when this driver is not needed + will not cause any issue. + config DRM_LVDS_CODEC tristate "Transparent LVDS encoders and decoders support" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index b6b2e7029a78..17f1f155e803 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o +obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c new file mode 100644 index 000000000000..4d278573cdb9 --- /dev/null +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Laurent Pinchart + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct display_connector { + struct drm_bridge bridge; + + struct gpio_desc *hpd_gpio; + int hpd_irq; +}; + +static inline struct display_connector * +to_display_connector(struct drm_bridge *bridge) +{ + return container_of(bridge, struct display_connector, bridge); +} + +static int display_connector_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL; +} + +static enum drm_connector_status +display_connector_detect(struct drm_bridge *bridge) +{ + struct display_connector *conn = to_display_connector(bridge); + + if (conn->hpd_gpio) { + if (gpiod_get_value_cansleep(conn->hpd_gpio)) + return connector_status_connected; + else + return connector_status_disconnected; + } + + if (conn->bridge.ddc && drm_probe_ddc(conn->bridge.ddc)) + return connector_status_connected; + + switch (conn->bridge.type) { + case DRM_MODE_CONNECTOR_DVIA: + case DRM_MODE_CONNECTOR_DVID: + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_HDMIA: + case DRM_MODE_CONNECTOR_HDMIB: + /* + * For DVI and HDMI connectors a DDC probe failure indicates + * that no cable is connected. + */ + return connector_status_disconnected; + + case DRM_MODE_CONNECTOR_Composite: + case DRM_MODE_CONNECTOR_SVIDEO: + case DRM_MODE_CONNECTOR_VGA: + default: + /* + * Composite and S-Video connectors have no other detection + * mean than the HPD GPIO. For VGA connectors, even if we have + * an I2C bus, we can't assume that the cable is disconnected + * if drm_probe_ddc fails, as some cables don't wire the DDC + * pins. + */ + return connector_status_unknown; + } +} + +static struct edid *display_connector_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct display_connector *conn = to_display_connector(bridge); + + return drm_get_edid(connector, conn->bridge.ddc); +} + +static const struct drm_bridge_funcs display_connector_bridge_funcs = { + .attach = display_connector_attach, + .detect = display_connector_detect, + .get_edid = display_connector_get_edid, +}; + +static irqreturn_t display_connector_hpd_irq(int irq, void *arg) +{ + struct display_connector *conn = arg; + struct drm_bridge *bridge = &conn->bridge; + + drm_bridge_hpd_notify(bridge, display_connector_detect(bridge)); + + return IRQ_HANDLED; +} + +static int display_connector_probe(struct platform_device *pdev) +{ + struct display_connector *conn; + unsigned int type; + const char *label; + int ret; + + conn = devm_kzalloc(&pdev->dev, sizeof(*conn), GFP_KERNEL); + if (!conn) + return -ENOMEM; + + platform_set_drvdata(pdev, conn); + + type = (uintptr_t)of_device_get_match_data(&pdev->dev); + + /* Get the exact connector type. */ + switch (type) { + case DRM_MODE_CONNECTOR_DVII: { + bool analog, digital; + + analog = of_property_read_bool(pdev->dev.of_node, "analog"); + digital = of_property_read_bool(pdev->dev.of_node, "digital"); + if (analog && !digital) { + conn->bridge.type = DRM_MODE_CONNECTOR_DVIA; + } else if (!analog && digital) { + conn->bridge.type = DRM_MODE_CONNECTOR_DVID; + } else if (analog && digital) { + conn->bridge.type = DRM_MODE_CONNECTOR_DVII; + } else { + dev_err(&pdev->dev, "DVI connector with no type\n"); + return -EINVAL; + } + break; + } + + case DRM_MODE_CONNECTOR_HDMIA: { + const char *hdmi_type; + + ret = of_property_read_string(pdev->dev.of_node, "type", + &hdmi_type); + if (ret < 0) { + dev_err(&pdev->dev, "HDMI connector with no type\n"); + return -EINVAL; + } + + if (!strcmp(hdmi_type, "a") || !strcmp(hdmi_type, "c") || + !strcmp(hdmi_type, "d") || !strcmp(hdmi_type, "e")) { + conn->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + } else if (!strcmp(hdmi_type, "b")) { + conn->bridge.type = DRM_MODE_CONNECTOR_HDMIB; + } else { + dev_err(&pdev->dev, + "Unsupported HDMI connector type '%s'\n", + hdmi_type); + return -EINVAL; + } + + break; + } + + default: + conn->bridge.type = type; + break; + } + + /* All the supported connector types support interlaced modes. */ + conn->bridge.interlace_allowed = true; + + /* Get the optional connector label. */ + of_property_read_string(pdev->dev.of_node, "label", &label); + + /* + * Get the HPD GPIO for DVI and HDMI connectors. If the GPIO can provide + * edge interrupts, register an interrupt handler. + */ + if (type == DRM_MODE_CONNECTOR_DVII || + type == DRM_MODE_CONNECTOR_HDMIA) { + conn->hpd_gpio = devm_gpiod_get_optional(&pdev->dev, "hpd", + GPIOD_IN); + if (IS_ERR(conn->hpd_gpio)) { + if (PTR_ERR(conn->hpd_gpio) != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Unable to retrieve HPD GPIO\n"); + return PTR_ERR(conn->hpd_gpio); + } + + conn->hpd_irq = gpiod_to_irq(conn->hpd_gpio); + } else { + conn->hpd_irq = -EINVAL; + } + + if (conn->hpd_irq >= 0) { + ret = devm_request_threaded_irq(&pdev->dev, conn->hpd_irq, + NULL, display_connector_hpd_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "HPD", conn); + if (ret) { + dev_info(&pdev->dev, + "Failed to request HPD edge interrupt, falling back to polling\n"); + conn->hpd_irq = -EINVAL; + } + } + + /* Retrieve the DDC I2C adapter for DVI, HDMI and VGA connectors. */ + if (type == DRM_MODE_CONNECTOR_DVII || + type == DRM_MODE_CONNECTOR_HDMIA || + type == DRM_MODE_CONNECTOR_VGA) { + struct device_node *phandle; + + phandle = of_parse_phandle(pdev->dev.of_node, "ddc-i2c-bus", 0); + if (phandle) { + conn->bridge.ddc = of_get_i2c_adapter_by_node(phandle); + of_node_put(phandle); + if (!conn->bridge.ddc) + return -EPROBE_DEFER; + } else { + dev_dbg(&pdev->dev, + "No I2C bus specified, disabling EDID readout\n"); + } + } + + conn->bridge.funcs = &display_connector_bridge_funcs; + conn->bridge.of_node = pdev->dev.of_node; + + if (conn->bridge.ddc) + conn->bridge.ops |= DRM_BRIDGE_OP_EDID + | DRM_BRIDGE_OP_DETECT; + if (conn->hpd_gpio) + conn->bridge.ops |= DRM_BRIDGE_OP_DETECT; + if (conn->hpd_irq >= 0) + conn->bridge.ops |= DRM_BRIDGE_OP_HPD; + + dev_dbg(&pdev->dev, + "Found %s display connector '%s' %s DDC bus and %s HPD GPIO (ops 0x%x)\n", + drm_get_connector_type_name(conn->bridge.type), + label ? label : "", + conn->bridge.ddc ? "with" : "without", + conn->hpd_gpio ? "with" : "without", + conn->bridge.ops); + + drm_bridge_add(&conn->bridge); + + return 0; +} + +static int display_connector_remove(struct platform_device *pdev) +{ + struct display_connector *conn = platform_get_drvdata(pdev); + + drm_bridge_remove(&conn->bridge); + + if (!IS_ERR(conn->bridge.ddc)) + i2c_put_adapter(conn->bridge.ddc); + + return 0; +} + +static const struct of_device_id display_connector_match[] = { + { + .compatible = "composite-video-connector", + .data = (void *)DRM_MODE_CONNECTOR_Composite, + }, { + .compatible = "dvi-connector", + .data = (void *)DRM_MODE_CONNECTOR_DVII, + }, { + .compatible = "hdmi-connector", + .data = (void *)DRM_MODE_CONNECTOR_HDMIA, + }, { + .compatible = "svideo-connector", + .data = (void *)DRM_MODE_CONNECTOR_SVIDEO, + }, { + .compatible = "vga-connector", + .data = (void *)DRM_MODE_CONNECTOR_VGA, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, display_connector_match); + +static struct platform_driver display_connector_driver = { + .probe = display_connector_probe, + .remove = display_connector_remove, + .driver = { + .name = "display-connector", + .of_match_table = display_connector_match, + }, +}; +module_platform_driver(display_connector_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Display connector driver"); +MODULE_LICENSE("GPL"); From cff5e6f7e83f6271ed75972e9a2920e2c7f62d6c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:36 +0200 Subject: [PATCH 32/78] drm/bridge: Add driver for the TI TPD12S015 HDMI level shifter The TI TPD12S015 is an HDMI level shifter and ESD protector controlled through GPIOs. Add a DRM bridge driver for the device. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-17-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/Kconfig | 8 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ti-tpd12s015.c | 211 ++++++++++++++++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 drivers/gpu/drm/bridge/ti-tpd12s015.c diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index d63283661850..aaed2347ace9 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -172,6 +172,14 @@ config DRM_TI_SN65DSI86 help Texas Instruments SN65DSI86 DSI to eDP Bridge driver +config DRM_TI_TPD12S015 + tristate "TI TPD12S015 HDMI level shifter and ESD protection" + depends on OF + select DRM_KMS_HELPER + help + Texas Instruments TPD12S015 HDMI level shifter and ESD protection + driver. + source "drivers/gpu/drm/bridge/analogix/Kconfig" source "drivers/gpu/drm/bridge/adv7511/Kconfig" diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 17f1f155e803..6fb062b5b0f0 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_DRM_TOSHIBA_TC358768) += tc358768.o obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/ obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o +obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o obj-y += analogix/ obj-y += synopsys/ diff --git a/drivers/gpu/drm/bridge/ti-tpd12s015.c b/drivers/gpu/drm/bridge/ti-tpd12s015.c new file mode 100644 index 000000000000..514cbf0eac75 --- /dev/null +++ b/drivers/gpu/drm/bridge/ti-tpd12s015.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TPD12S015 HDMI ESD protection & level shifter chip driver + * + * Copyright (C) 2019 Texas Instruments Incorporated + * + * Based on the omapdrm-specific encoder-opa362 driver + * + * Copyright (C) 2013 Texas Instruments Incorporated + * Author: Tomi Valkeinen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct tpd12s015_device { + struct drm_bridge bridge; + + struct gpio_desc *ct_cp_hpd_gpio; + struct gpio_desc *ls_oe_gpio; + struct gpio_desc *hpd_gpio; + int hpd_irq; + + struct drm_bridge *next_bridge; +}; + +static inline struct tpd12s015_device *to_tpd12s015(struct drm_bridge *bridge) +{ + return container_of(bridge, struct tpd12s015_device, bridge); +} + +static int tpd12s015_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct tpd12s015_device *tpd = to_tpd12s015(bridge); + int ret; + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + ret = drm_bridge_attach(bridge->encoder, tpd->next_bridge, + bridge, flags); + if (ret < 0) + return ret; + + gpiod_set_value_cansleep(tpd->ls_oe_gpio, 1); + + /* DC-DC converter needs at max 300us to get to 90% of 5V. */ + usleep_range(300, 1000); + + return 0; +} + +static void tpd12s015_detach(struct drm_bridge *bridge) +{ + struct tpd12s015_device *tpd = to_tpd12s015(bridge); + + gpiod_set_value_cansleep(tpd->ls_oe_gpio, 0); +} + +static enum drm_connector_status tpd12s015_detect(struct drm_bridge *bridge) +{ + struct tpd12s015_device *tpd = to_tpd12s015(bridge); + + if (gpiod_get_value_cansleep(tpd->hpd_gpio)) + return connector_status_connected; + else + return connector_status_disconnected; +} + +static void tpd12s015_hpd_enable(struct drm_bridge *bridge) +{ + struct tpd12s015_device *tpd = to_tpd12s015(bridge); + + gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 1); +} + +static void tpd12s015_hpd_disable(struct drm_bridge *bridge) +{ + struct tpd12s015_device *tpd = to_tpd12s015(bridge); + + gpiod_set_value_cansleep(tpd->ct_cp_hpd_gpio, 0); +} + +static const struct drm_bridge_funcs tpd12s015_bridge_funcs = { + .attach = tpd12s015_attach, + .detach = tpd12s015_detach, + .detect = tpd12s015_detect, + .hpd_enable = tpd12s015_hpd_enable, + .hpd_disable = tpd12s015_hpd_disable, +}; + +static irqreturn_t tpd12s015_hpd_isr(int irq, void *data) +{ + struct tpd12s015_device *tpd = data; + struct drm_bridge *bridge = &tpd->bridge; + + drm_bridge_hpd_notify(bridge, tpd12s015_detect(bridge)); + + return IRQ_HANDLED; +} + +static int tpd12s015_probe(struct platform_device *pdev) +{ + struct tpd12s015_device *tpd; + struct device_node *node; + struct gpio_desc *gpio; + int ret; + + tpd = devm_kzalloc(&pdev->dev, sizeof(*tpd), GFP_KERNEL); + if (!tpd) + return -ENOMEM; + + platform_set_drvdata(pdev, tpd); + + tpd->bridge.funcs = &tpd12s015_bridge_funcs; + tpd->bridge.of_node = pdev->dev.of_node; + tpd->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + tpd->bridge.ops = DRM_BRIDGE_OP_DETECT; + + /* Get the next bridge, connected to port@1. */ + node = of_graph_get_remote_node(pdev->dev.of_node, 1, -1); + if (!node) + return -ENODEV; + + tpd->next_bridge = of_drm_find_bridge(node); + of_node_put(node); + + if (!tpd->next_bridge) + return -EPROBE_DEFER; + + /* Get the control and HPD GPIOs. */ + gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0, + GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + tpd->ct_cp_hpd_gpio = gpio; + + gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1, + GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + tpd->ls_oe_gpio = gpio; + + gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_IN); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + tpd->hpd_gpio = gpio; + + /* Register the IRQ if the HPD GPIO is IRQ-capable. */ + tpd->hpd_irq = gpiod_to_irq(tpd->hpd_gpio); + if (tpd->hpd_irq) { + ret = devm_request_threaded_irq(&pdev->dev, tpd->hpd_irq, NULL, + tpd12s015_hpd_isr, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "tpd12s015 hpd", tpd); + if (ret) + return ret; + + tpd->bridge.ops |= DRM_BRIDGE_OP_HPD; + } + + /* Register the DRM bridge. */ + drm_bridge_add(&tpd->bridge); + + return 0; +} + +static int __exit tpd12s015_remove(struct platform_device *pdev) +{ + struct tpd12s015_device *tpd = platform_get_drvdata(pdev); + + drm_bridge_remove(&tpd->bridge); + + return 0; +} + +static const struct of_device_id tpd12s015_of_match[] = { + { .compatible = "ti,tpd12s015", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, tpd12s015_of_match); + +static struct platform_driver tpd12s015_driver = { + .probe = tpd12s015_probe, + .remove = __exit_p(tpd12s015_remove), + .driver = { + .name = "tpd12s015", + .of_match_table = tpd12s015_of_match, + }, +}; + +module_platform_driver(tpd12s015_driver); + +MODULE_AUTHOR("Tomi Valkeinen "); +MODULE_DESCRIPTION("TPD12S015 HDMI level shifter and ESD protection driver"); +MODULE_LICENSE("GPL"); From 2be68b59e5d035f7d404477f8df2262ca5de981f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:37 +0200 Subject: [PATCH 33/78] drm/bridge: panel: Implement bridge connector operations Implement the newly added bridge connector operations, allowing the usage of drm_bridge_panel with drm_bridge_connector. Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Reviewed-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-18-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/panel.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 5c92d7c9fd61..8461ee8304ba 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -60,10 +60,8 @@ static int panel_bridge_attach(struct drm_bridge *bridge, struct drm_connector *connector = &panel_bridge->connector; int ret; - if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; if (!bridge->encoder) { DRM_ERROR("Missing encoder\n"); @@ -126,6 +124,14 @@ static void panel_bridge_post_disable(struct drm_bridge *bridge) drm_panel_unprepare(panel_bridge->panel); } +static int panel_bridge_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); + + return drm_panel_get_modes(panel_bridge->panel, connector); +} + static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .attach = panel_bridge_attach, .detach = panel_bridge_detach, @@ -133,6 +139,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .enable = panel_bridge_enable, .disable = panel_bridge_disable, .post_disable = panel_bridge_post_disable, + .get_modes = panel_bridge_get_modes, .atomic_reset = drm_atomic_helper_bridge_reset, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, @@ -206,6 +213,8 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, #ifdef CONFIG_OF panel_bridge->bridge.of_node = panel->dev->of_node; #endif + panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; + panel_bridge->bridge.type = connector_type; drm_bridge_add(&panel_bridge->bridge); From 5cafa0f1853b1a2a8845d908d84ff12579532a3c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:38 +0200 Subject: [PATCH 34/78] drm/bridge: tfp410: Replace manual connector handling with bridge Now that a driver is available for display connectors, replace the manual connector handling code with usage of the DRM bridge API. The tfp410 driver doesn't deal with the display connector directly anymore, but still delegates drm_connector operations to the next bridge. This brings us one step closer to having the tfp410 driver handling the TFP410 only. Signed-off-by: Laurent Pinchart Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-19-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/ti-tfp410.c | 216 ++++++++++------------------- 1 file changed, 75 insertions(+), 141 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 193c9368f664..2b8741ebc696 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -4,14 +4,12 @@ * Author: Jyri Sarha */ -#include -#include #include #include -#include #include #include #include +#include #include #include @@ -24,16 +22,13 @@ struct tfp410 { struct drm_bridge bridge; struct drm_connector connector; - unsigned int connector_type; u32 bus_format; - struct i2c_adapter *ddc; - struct gpio_desc *hpd; - int hpd_irq; struct delayed_work hpd_work; struct gpio_desc *powerdown; struct drm_bridge_timings timings; + struct drm_bridge *next_bridge; struct device *dev; }; @@ -56,13 +51,18 @@ static int tfp410_get_modes(struct drm_connector *connector) struct edid *edid; int ret; - if (!dvi->ddc) - goto fallback; + edid = drm_bridge_get_edid(dvi->next_bridge, connector); + if (IS_ERR_OR_NULL(edid)) { + if (edid != ERR_PTR(-ENOTSUPP)) + DRM_INFO("EDID read failed. Fallback to standard modes\n"); - edid = drm_get_edid(connector, dvi->ddc); - if (!edid) { - DRM_INFO("EDID read failed. Fallback to standard modes\n"); - goto fallback; + /* + * No EDID, fallback on the XGA standard modes and prefer a mode + * pretty much anything can handle. + */ + ret = drm_add_modes_noedid(connector, 1920, 1200); + drm_set_preferred_mode(connector, 1024, 768); + return ret; } drm_connector_update_edid_property(connector, edid); @@ -71,15 +71,6 @@ static int tfp410_get_modes(struct drm_connector *connector) kfree(edid); - return ret; - -fallback: - /* No EDID, fallback on the XGA standard modes */ - ret = drm_add_modes_noedid(connector, 1920, 1200); - - /* And prefer a mode pretty much anything can handle */ - drm_set_preferred_mode(connector, 1024, 768); - return ret; } @@ -92,21 +83,7 @@ tfp410_connector_detect(struct drm_connector *connector, bool force) { struct tfp410 *dvi = drm_connector_to_tfp410(connector); - if (dvi->hpd) { - if (gpiod_get_value_cansleep(dvi->hpd)) - return connector_status_connected; - else - return connector_status_disconnected; - } - - if (dvi->ddc) { - if (drm_probe_ddc(dvi->ddc)) - return connector_status_connected; - else - return connector_status_disconnected; - } - - return connector_status_unknown; + return drm_bridge_detect(dvi->next_bridge); } static const struct drm_connector_funcs tfp410_con_funcs = { @@ -118,12 +95,35 @@ static const struct drm_connector_funcs tfp410_con_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; +static void tfp410_hpd_work_func(struct work_struct *work) +{ + struct tfp410 *dvi; + + dvi = container_of(work, struct tfp410, hpd_work.work); + + if (dvi->bridge.dev) + drm_helper_hpd_irq_event(dvi->bridge.dev); +} + +static void tfp410_hpd_callback(void *arg, enum drm_connector_status status) +{ + struct tfp410 *dvi = arg; + + mod_delayed_work(system_wq, &dvi->hpd_work, + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); +} + static int tfp410_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) { struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); int ret; + ret = drm_bridge_attach(bridge->encoder, dvi->next_bridge, bridge, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret < 0) + return ret; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { DRM_ERROR("Fix bridge driver to make connector optional!"); return -EINVAL; @@ -134,17 +134,23 @@ static int tfp410_attach(struct drm_bridge *bridge, return -ENODEV; } - if (dvi->hpd_irq >= 0) + if (dvi->next_bridge->ops & DRM_BRIDGE_OP_DETECT) dvi->connector.polled = DRM_CONNECTOR_POLL_HPD; else dvi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + if (dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) { + INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func); + drm_bridge_hpd_enable(dvi->next_bridge, tfp410_hpd_callback, + dvi); + } + drm_connector_helper_add(&dvi->connector, &tfp410_con_helper_funcs); ret = drm_connector_init_with_ddc(bridge->dev, &dvi->connector, &tfp410_con_funcs, - dvi->connector_type, - dvi->ddc); + dvi->next_bridge->type, + dvi->next_bridge->ddc); if (ret) { dev_err(dvi->dev, "drm_connector_init() failed: %d\n", ret); return ret; @@ -153,12 +159,21 @@ static int tfp410_attach(struct drm_bridge *bridge, drm_display_info_set_bus_formats(&dvi->connector.display_info, &dvi->bus_format, 1); - drm_connector_attach_encoder(&dvi->connector, - bridge->encoder); + drm_connector_attach_encoder(&dvi->connector, bridge->encoder); return 0; } +static void tfp410_detach(struct drm_bridge *bridge) +{ + struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); + + if (dvi->connector.dev && dvi->next_bridge->ops & DRM_BRIDGE_OP_HPD) { + drm_bridge_hpd_disable(dvi->next_bridge); + cancel_delayed_work_sync(&dvi->hpd_work); + } +} + static void tfp410_enable(struct drm_bridge *bridge) { struct tfp410 *dvi = drm_bridge_to_tfp410(bridge); @@ -187,31 +202,12 @@ static enum drm_mode_status tfp410_mode_valid(struct drm_bridge *bridge, static const struct drm_bridge_funcs tfp410_bridge_funcs = { .attach = tfp410_attach, + .detach = tfp410_detach, .enable = tfp410_enable, .disable = tfp410_disable, .mode_valid = tfp410_mode_valid, }; -static void tfp410_hpd_work_func(struct work_struct *work) -{ - struct tfp410 *dvi; - - dvi = container_of(work, struct tfp410, hpd_work.work); - - if (dvi->bridge.dev) - drm_helper_hpd_irq_event(dvi->bridge.dev); -} - -static irqreturn_t tfp410_hpd_irq_thread(int irq, void *arg) -{ - struct tfp410 *dvi = arg; - - mod_delayed_work(system_wq, &dvi->hpd_work, - msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); - - return IRQ_HANDLED; -} - static const struct drm_bridge_timings tfp410_default_timings = { .input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE | DRM_BUS_FLAG_DE_HIGH, @@ -289,51 +285,9 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c) return 0; } -static int tfp410_get_connector_properties(struct tfp410 *dvi) -{ - struct device_node *connector_node, *ddc_phandle; - int ret = 0; - - /* port@1 is the connector node */ - connector_node = of_graph_get_remote_node(dvi->dev->of_node, 1, -1); - if (!connector_node) - return -ENODEV; - - if (of_device_is_compatible(connector_node, "hdmi-connector")) - dvi->connector_type = DRM_MODE_CONNECTOR_HDMIA; - else - dvi->connector_type = DRM_MODE_CONNECTOR_DVID; - - dvi->hpd = fwnode_gpiod_get_index(&connector_node->fwnode, - "hpd", 0, GPIOD_IN, "hpd"); - if (IS_ERR(dvi->hpd)) { - ret = PTR_ERR(dvi->hpd); - dvi->hpd = NULL; - if (ret == -ENOENT) - ret = 0; - else - goto fail; - } - - ddc_phandle = of_parse_phandle(connector_node, "ddc-i2c-bus", 0); - if (!ddc_phandle) - goto fail; - - dvi->ddc = of_get_i2c_adapter_by_node(ddc_phandle); - if (dvi->ddc) - dev_info(dvi->dev, "Connector's ddc i2c bus found\n"); - else - ret = -EPROBE_DEFER; - - of_node_put(ddc_phandle); - -fail: - of_node_put(connector_node); - return ret; -} - static int tfp410_init(struct device *dev, bool i2c) { + struct device_node *node; struct tfp410 *dvi; int ret; @@ -345,21 +299,31 @@ static int tfp410_init(struct device *dev, bool i2c) dvi = devm_kzalloc(dev, sizeof(*dvi), GFP_KERNEL); if (!dvi) return -ENOMEM; + + dvi->dev = dev; dev_set_drvdata(dev, dvi); dvi->bridge.funcs = &tfp410_bridge_funcs; dvi->bridge.of_node = dev->of_node; dvi->bridge.timings = &dvi->timings; - dvi->dev = dev; + dvi->bridge.type = DRM_MODE_CONNECTOR_DVID; ret = tfp410_parse_timings(dvi, i2c); if (ret) - goto fail; + return ret; - ret = tfp410_get_connector_properties(dvi); - if (ret) - goto fail; + /* Get the next bridge, connected to port@1. */ + node = of_graph_get_remote_node(dev->of_node, 1, -1); + if (!node) + return -ENODEV; + dvi->next_bridge = of_drm_find_bridge(node); + of_node_put(node); + + if (!dvi->next_bridge) + return -EPROBE_DEFER; + + /* Get the powerdown GPIO. */ dvi->powerdown = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(dvi->powerdown)) { @@ -367,48 +331,18 @@ static int tfp410_init(struct device *dev, bool i2c) return PTR_ERR(dvi->powerdown); } - if (dvi->hpd) - dvi->hpd_irq = gpiod_to_irq(dvi->hpd); - else - dvi->hpd_irq = -ENXIO; - - if (dvi->hpd_irq >= 0) { - INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func); - - ret = devm_request_threaded_irq(dev, dvi->hpd_irq, - NULL, tfp410_hpd_irq_thread, IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "hdmi-hpd", dvi); - if (ret) { - DRM_ERROR("failed to register hpd interrupt\n"); - goto fail; - } - } - + /* Register the DRM bridge. */ drm_bridge_add(&dvi->bridge); return 0; -fail: - i2c_put_adapter(dvi->ddc); - if (dvi->hpd) - gpiod_put(dvi->hpd); - return ret; } static int tfp410_fini(struct device *dev) { struct tfp410 *dvi = dev_get_drvdata(dev); - if (dvi->hpd_irq >= 0) - cancel_delayed_work_sync(&dvi->hpd_work); - drm_bridge_remove(&dvi->bridge); - if (dvi->ddc) - i2c_put_adapter(dvi->ddc); - if (dvi->hpd) - gpiod_put(dvi->hpd); - return 0; } From e00a5caafac0276ea5f6ab3999087e9d3fe6ee7e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:39 +0200 Subject: [PATCH 35/78] drm/bridge: tfp410: Allow operation without drm_connector The tfp410 driver can operate as part of a pipeline where the drm_connector is created by the display controller. Enable this mode of operation by skipping creation of a drm_connector internally. Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-20-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/bridge/ti-tfp410.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 2b8741ebc696..40c4d4a5517b 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -124,10 +124,8 @@ static int tfp410_attach(struct drm_bridge *bridge, if (ret < 0) return ret; - if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; if (!bridge->encoder) { dev_err(dvi->dev, "Missing encoder\n"); From 5e20bdf3d3ded5c241650d590781bfc84d6e20ae Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:40 +0200 Subject: [PATCH 36/78] drm: Add helper to create a connector for a chain of bridges Most bridge drivers create a DRM connector to model the connector at the output of the bridge. This model is historical and has worked pretty well so far, but causes several issues: - It prevents supporting more complex display pipelines where DRM connector operations are split over multiple components. For instance a pipeline with a bridge connected to the DDC signals to read EDID data, and another one connected to the HPD signal to detect connection and disconnection, will not be possible to support through this model. - It requires every bridge driver to implement similar connector handling code, resulting in code duplication. - It assumes that a bridge will either be wired to a connector or to another bridge, but doesn't support bridges that can be used in both positions very well (although there is some ad-hoc support for this in the analogix_dp bridge driver). In order to solve these issues, ownership of the connector needs to be moved to the display controller driver. To avoid code duplication in display controller drivers, add a new helper to create and manage a DRM connector backed by a chain of bridges. All connector operations are delegating to the appropriate bridge in the chain. Signed-off-by: Laurent Pinchart Reviewed-by: Boris Brezillon Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-21-laurent.pinchart@ideasonboard.com --- Documentation/gpu/drm-kms-helpers.rst | 12 + drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_bridge.c | 6 + drivers/gpu/drm/drm_bridge_connector.c | 379 +++++++++++++++++++++++++ include/drm/drm_bridge_connector.h | 18 ++ 5 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_bridge_connector.c create mode 100644 include/drm/drm_bridge_connector.h diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index fe155c6ae175..ee730457bf4e 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -145,6 +145,12 @@ Bridge Operations .. kernel-doc:: drivers/gpu/drm/drm_bridge.c :doc: bridge operations +Bridge Connector Helper +----------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_bridge_connector.c + :doc: overview + Bridge Helper Reference ------------------------- @@ -155,6 +161,12 @@ Bridge Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_bridge.c :export: +Bridge Connector Helper Reference +--------------------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_bridge_connector.c + :export: + Panel-Bridge Helper Reference ----------------------------- diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index ca0ca775d37f..7f72ef5e7811 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -39,7 +39,8 @@ obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o drm_ttm_helper-y := drm_gem_ttm_helper.o obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o -drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper.o \ +drm_kms_helper-y := drm_bridge_connector.o drm_crtc_helper.o drm_dp_helper.o \ + drm_dsc.o drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ drm_simple_kms_helper.o drm_modeset_helper.o \ diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 96eace94fea8..afdec8e5fc68 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -79,6 +79,12 @@ * requires no intervention from the driver. For other drivers, the relevant * DRM bridge chain functions shall be called manually. * + * Bridges also participate in implementing the &drm_connector at the end of + * the bridge chain. Display drivers may use the drm_bridge_connector_init() + * helper to create the &drm_connector, or implement it manually on top of the + * connector-related operations exposed by the bridge (see the overview + * documentation of bridge operations for more details). + * * &drm_bridge, like &drm_panel, aren't &drm_mode_object entities like planes, * CRTCs, encoders or connectors and hence are not visible to userspace. They * just provide additional hooks to get the desired output at the end of the diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c new file mode 100644 index 000000000000..c6994fe673f3 --- /dev/null +++ b/drivers/gpu/drm/drm_bridge_connector.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Laurent Pinchart + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: overview + * + * The DRM bridge connector helper object provides a DRM connector + * implementation that wraps a chain of &struct drm_bridge. The connector + * operations are fully implemented based on the operations of the bridges in + * the chain, and don't require any intervention from the display controller + * driver at runtime. + * + * To use the helper, display controller drivers create a bridge connector with + * a call to drm_bridge_connector_init(). This associates the newly created + * connector with the chain of bridges passed to the function and registers it + * with the DRM device. At that point the connector becomes fully usable, no + * further operation is needed. + * + * The DRM bridge connector operations are implemented based on the operations + * provided by the bridges in the chain. Each connector operation is delegated + * to the bridge closest to the connector (at the end of the chain) that + * provides the relevant functionality. + * + * To make use of this helper, all bridges in the chain shall report bridge + * operation flags (&drm_bridge->ops) and bridge output type + * (&drm_bridge->type), as well as the DRM_BRIDGE_ATTACH_NO_CONNECTOR attach + * flag (none of the bridges shall create a DRM connector directly). + */ + +/** + * struct drm_bridge_connector - A connector backed by a chain of bridges + */ +struct drm_bridge_connector { + /** + * @base: The base DRM connector + */ + struct drm_connector base; + /** + * @encoder: + * + * The encoder at the start of the bridges chain. + */ + struct drm_encoder *encoder; + /** + * @bridge_edid: + * + * The last bridge in the chain (closest to the connector) that provides + * EDID read support, if any (see &DRM_BRIDGE_OP_EDID). + */ + struct drm_bridge *bridge_edid; + /** + * @bridge_hpd: + * + * The last bridge in the chain (closest to the connector) that provides + * hot-plug detection notification, if any (see &DRM_BRIDGE_OP_HPD). + */ + struct drm_bridge *bridge_hpd; + /** + * @bridge_detect: + * + * The last bridge in the chain (closest to the connector) that provides + * connector detection, if any (see &DRM_BRIDGE_OP_DETECT). + */ + struct drm_bridge *bridge_detect; + /** + * @bridge_modes: + * + * The last bridge in the chain (closest to the connector) that provides + * connector modes detection, if any (see &DRM_BRIDGE_OP_MODES). + */ + struct drm_bridge *bridge_modes; +}; + +#define to_drm_bridge_connector(x) \ + container_of(x, struct drm_bridge_connector, base) + +/* ----------------------------------------------------------------------------- + * Bridge Connector Hot-Plug Handling + */ + +static void drm_bridge_connector_hpd_notify(struct drm_connector *connector, + enum drm_connector_status status) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + /* Notify all bridges in the pipeline of hotplug events. */ + drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) { + if (bridge->funcs->hpd_notify) + bridge->funcs->hpd_notify(bridge, status); + } +} + +static void drm_bridge_connector_hpd_cb(void *cb_data, + enum drm_connector_status status) +{ + struct drm_bridge_connector *drm_bridge_connector = cb_data; + struct drm_connector *connector = &drm_bridge_connector->base; + struct drm_device *dev = connector->dev; + enum drm_connector_status old_status; + + mutex_lock(&dev->mode_config.mutex); + old_status = connector->status; + connector->status = status; + mutex_unlock(&dev->mode_config.mutex); + + if (old_status == status) + return; + + drm_bridge_connector_hpd_notify(connector, status); + + drm_kms_helper_hotplug_event(dev); +} + +/** + * drm_bridge_connector_enable_hpd - Enable hot-plug detection for the connector + * @connector: The DRM bridge connector + * + * This function enables hot-plug detection for the given bridge connector. + * This is typically used by display drivers in their resume handler. + */ +void drm_bridge_connector_enable_hpd(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *hpd = bridge_connector->bridge_hpd; + + if (hpd) + drm_bridge_hpd_enable(hpd, drm_bridge_connector_hpd_cb, + bridge_connector); +} +EXPORT_SYMBOL_GPL(drm_bridge_connector_enable_hpd); + +/** + * drm_bridge_connector_disable_hpd - Disable hot-plug detection for the + * connector + * @connector: The DRM bridge connector + * + * This function disables hot-plug detection for the given bridge connector. + * This is typically used by display drivers in their suspend handler. + */ +void drm_bridge_connector_disable_hpd(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *hpd = bridge_connector->bridge_hpd; + + if (hpd) + drm_bridge_hpd_disable(hpd); +} +EXPORT_SYMBOL_GPL(drm_bridge_connector_disable_hpd); + +/* ----------------------------------------------------------------------------- + * Bridge Connector Functions + */ + +static enum drm_connector_status +drm_bridge_connector_detect(struct drm_connector *connector, bool force) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *detect = bridge_connector->bridge_detect; + enum drm_connector_status status; + + if (detect) { + status = detect->funcs->detect(detect); + + drm_bridge_connector_hpd_notify(connector, status); + } else { + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DPI: + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_DSI: + status = connector_status_connected; + break; + default: + status = connector_status_unknown; + break; + } + } + + return status; +} + +static void drm_bridge_connector_destroy(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + + if (bridge_connector->bridge_hpd) { + struct drm_bridge *hpd = bridge_connector->bridge_hpd; + + drm_bridge_hpd_disable(hpd); + } + + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + + kfree(bridge_connector); +} + +static const struct drm_connector_funcs drm_bridge_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .detect = drm_bridge_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_bridge_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +/* ----------------------------------------------------------------------------- + * Bridge Connector Helper Functions + */ + +static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector, + struct drm_bridge *bridge) +{ + enum drm_connector_status status; + struct edid *edid; + int n; + + status = drm_bridge_connector_detect(connector, false); + if (status != connector_status_connected) + goto no_edid; + + edid = bridge->funcs->get_edid(bridge, connector); + if (!edid || !drm_edid_is_valid(edid)) { + kfree(edid); + goto no_edid; + } + + drm_connector_update_edid_property(connector, edid); + n = drm_add_edid_modes(connector, edid); + + kfree(edid); + return n; + +no_edid: + drm_connector_update_edid_property(connector, NULL); + return 0; +} + +static int drm_bridge_connector_get_modes(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + /* + * If display exposes EDID, then we parse that in the normal way to + * build table of supported modes. + */ + bridge = bridge_connector->bridge_edid; + if (bridge) + return drm_bridge_connector_get_modes_edid(connector, bridge); + + /* + * Otherwise if the display pipeline reports modes (e.g. with a fixed + * resolution panel or an analog TV output), query it. + */ + bridge = bridge_connector->bridge_modes; + if (bridge) + return bridge->funcs->get_modes(bridge, connector); + + /* + * We can't retrieve modes, which can happen for instance for a DVI or + * VGA output with the DDC bus unconnected. The KMS core will add the + * default modes. + */ + return 0; +} + +static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = { + .get_modes = drm_bridge_connector_get_modes, + /* No need for .mode_valid(), the bridges are checked by the core. */ +}; + +/* ----------------------------------------------------------------------------- + * Bridge Connector Initialisation + */ + +/** + * drm_bridge_connector_init - Initialise a connector for a chain of bridges + * @drm: the DRM device + * @encoder: the encoder where the bridge chain starts + * + * Allocate, initialise and register a &drm_bridge_connector with the @drm + * device. The connector is associated with a chain of bridges that starts at + * the @encoder. All bridges in the chain shall report bridge operation flags + * (&drm_bridge->ops) and bridge output type (&drm_bridge->type), and none of + * them may create a DRM connector directly. + * + * Returns a pointer to the new connector on success, or a negative error + * pointer otherwise. + */ +struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, + struct drm_encoder *encoder) +{ + struct drm_bridge_connector *bridge_connector; + struct drm_connector *connector; + struct i2c_adapter *ddc = NULL; + struct drm_bridge *bridge; + int connector_type; + + bridge_connector = kzalloc(sizeof(*bridge_connector), GFP_KERNEL); + if (!bridge_connector) + return ERR_PTR(-ENOMEM); + + bridge_connector->encoder = encoder; + + /* + * TODO: Handle doublescan_allowed, stereo_allowed and + * ycbcr_420_allowed. + */ + connector = &bridge_connector->base; + connector->interlace_allowed = true; + + /* + * Initialise connector status handling. First locate the furthest + * bridges in the pipeline that support HPD and output detection. Then + * initialise the connector polling mode, using HPD if available and + * falling back to polling if supported. If neither HPD nor output + * detection are available, we don't support hotplug detection at all. + */ + connector_type = DRM_MODE_CONNECTOR_Unknown; + drm_for_each_bridge_in_chain(encoder, bridge) { + if (!bridge->interlace_allowed) + connector->interlace_allowed = false; + + if (bridge->ops & DRM_BRIDGE_OP_EDID) + bridge_connector->bridge_edid = bridge; + if (bridge->ops & DRM_BRIDGE_OP_HPD) + bridge_connector->bridge_hpd = bridge; + if (bridge->ops & DRM_BRIDGE_OP_DETECT) + bridge_connector->bridge_detect = bridge; + if (bridge->ops & DRM_BRIDGE_OP_MODES) + bridge_connector->bridge_modes = bridge; + + if (!drm_bridge_get_next_bridge(bridge)) + connector_type = bridge->type; + + if (bridge->ddc) + ddc = bridge->ddc; + } + + if (connector_type == DRM_MODE_CONNECTOR_Unknown) { + kfree(bridge_connector); + return ERR_PTR(-EINVAL); + } + + drm_connector_init_with_ddc(drm, connector, &drm_bridge_connector_funcs, + connector_type, ddc); + drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs); + + if (bridge_connector->bridge_hpd) + connector->polled = DRM_CONNECTOR_POLL_HPD; + else if (bridge_connector->bridge_detect) + connector->polled = DRM_CONNECTOR_POLL_CONNECT + | DRM_CONNECTOR_POLL_DISCONNECT; + + return connector; +} +EXPORT_SYMBOL_GPL(drm_bridge_connector_init); diff --git a/include/drm/drm_bridge_connector.h b/include/drm/drm_bridge_connector.h new file mode 100644 index 000000000000..33f6c3bbdb4a --- /dev/null +++ b/include/drm/drm_bridge_connector.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 Laurent Pinchart + */ + +#ifndef __DRM_BRIDGE_CONNECTOR_H__ +#define __DRM_BRIDGE_CONNECTOR_H__ + +struct drm_connector; +struct drm_device; +struct drm_encoder; + +void drm_bridge_connector_enable_hpd(struct drm_connector *connector); +void drm_bridge_connector_disable_hpd(struct drm_connector *connector); +struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, + struct drm_encoder *encoder); + +#endif /* __DRM_BRIDGE_CONNECTOR_H__ */ From 2a0a3ae17d36fa86dcf7c8e8d7b7f056ebd6c064 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:41 +0200 Subject: [PATCH 37/78] drm/omap: dss: Cleanup DSS ports on initialisation failure When the DSS initialises its output DPI and SDI ports, failures don't clean up previous successfully initialised ports. This can lead to resource leak or memory corruption. Fix it. Reported-by: Hans Verkuil Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-22-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/dss.c | 55 ++++++++++++++++++------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c b/drivers/gpu/drm/omapdrm/dss/dss.c index 225ec808b01a..67b92b5d8dd7 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.c +++ b/drivers/gpu/drm/omapdrm/dss/dss.c @@ -1151,6 +1151,31 @@ static const struct dss_features dra7xx_dss_feats = { .has_lcd_clk_src = true, }; +static void __dss_uninit_ports(struct dss_device *dss, unsigned int num_ports) +{ + struct platform_device *pdev = dss->pdev; + struct device_node *parent = pdev->dev.of_node; + struct device_node *port; + unsigned int i; + + for (i = 0; i < num_ports; i++) { + port = of_graph_get_port_by_id(parent, i); + if (!port) + continue; + + switch (dss->feat->ports[i]) { + case OMAP_DISPLAY_TYPE_DPI: + dpi_uninit_port(port); + break; + case OMAP_DISPLAY_TYPE_SDI: + sdi_uninit_port(port); + break; + default: + break; + } + } +} + static int dss_init_ports(struct dss_device *dss) { struct platform_device *pdev = dss->pdev; @@ -1168,13 +1193,13 @@ static int dss_init_ports(struct dss_device *dss) case OMAP_DISPLAY_TYPE_DPI: r = dpi_init_port(dss, pdev, port, dss->feat->model); if (r) - return r; + goto error; break; case OMAP_DISPLAY_TYPE_SDI: r = sdi_init_port(dss, pdev, port); if (r) - return r; + goto error; break; default: @@ -1183,31 +1208,15 @@ static int dss_init_ports(struct dss_device *dss) } return 0; + +error: + __dss_uninit_ports(dss, i); + return r; } static void dss_uninit_ports(struct dss_device *dss) { - struct platform_device *pdev = dss->pdev; - struct device_node *parent = pdev->dev.of_node; - struct device_node *port; - int i; - - for (i = 0; i < dss->feat->num_ports; i++) { - port = of_graph_get_port_by_id(parent, i); - if (!port) - continue; - - switch (dss->feat->ports[i]) { - case OMAP_DISPLAY_TYPE_DPI: - dpi_uninit_port(port); - break; - case OMAP_DISPLAY_TYPE_SDI: - sdi_uninit_port(port); - break; - default: - break; - } - } + __dss_uninit_ports(dss, dss->feat->num_ports); } static int dss_video_pll_probe(struct dss_device *dss) From 1516d11923d0f9f5db88041e8c4e097a59af0d5f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:42 +0200 Subject: [PATCH 38/78] drm/omap: Simplify HDMI mode and infoframe configuration Remove the omap_connector_get_hdmi_mode() function as the HDMI mode can be accessed directly from the connector's display info. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-23-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_connector.c | 11 ----------- drivers/gpu/drm/omapdrm/omap_connector.h | 1 - drivers/gpu/drm/omapdrm/omap_encoder.c | 4 +--- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 94cded387174..88dbf3fa473f 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -21,7 +21,6 @@ struct omap_connector { struct drm_connector base; struct omap_dss_device *output; struct omap_dss_device *hpd; - bool hdmi_mode; }; static void omap_connector_hpd_notify(struct drm_connector *connector, @@ -84,13 +83,6 @@ void omap_connector_disable_hpd(struct drm_connector *connector) hpd->ops->unregister_hpd_cb(hpd); } -bool omap_connector_get_hdmi_mode(struct drm_connector *connector) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - - return omap_connector->hdmi_mode; -} - static struct omap_dss_device * omap_connector_find_device(struct drm_connector *connector, enum omap_dss_device_ops_flag op) @@ -167,7 +159,6 @@ static void omap_connector_destroy(struct drm_connector *connector) static int omap_connector_get_modes_edid(struct drm_connector *connector, struct omap_dss_device *dssdev) { - struct omap_connector *omap_connector = to_omap_connector(connector); enum drm_connector_status status; void *edid; int n; @@ -189,8 +180,6 @@ static int omap_connector_get_modes_edid(struct drm_connector *connector, drm_connector_update_edid_property(connector, edid); n = drm_add_edid_modes(connector, edid); - omap_connector->hdmi_mode = drm_detect_hdmi_monitor(edid); - kfree(edid); return n; diff --git a/drivers/gpu/drm/omapdrm/omap_connector.h b/drivers/gpu/drm/omapdrm/omap_connector.h index 13607bda33d8..4aa5608f4bbe 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.h +++ b/drivers/gpu/drm/omapdrm/omap_connector.h @@ -21,7 +21,6 @@ struct omap_dss_device; struct drm_connector *omap_connector_init(struct drm_device *dev, struct omap_dss_device *output, struct drm_encoder *encoder); -bool omap_connector_get_hdmi_mode(struct drm_connector *connector); void omap_connector_enable_hpd(struct drm_connector *connector); void omap_connector_disable_hpd(struct drm_connector *connector); enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev, diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 4f2165a37795..cb5aa01d2f87 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -76,9 +76,7 @@ static void omap_encoder_hdmi_mode_set(struct drm_connector *connector, { struct omap_encoder *omap_encoder = to_omap_encoder(encoder); struct omap_dss_device *dssdev = omap_encoder->output; - bool hdmi_mode; - - hdmi_mode = omap_connector_get_hdmi_mode(connector); + bool hdmi_mode = connector->display_info.is_hdmi; if (dssdev->ops->hdmi.set_hdmi_mode) dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode); From 514fc91083f6c7e6faac17614361a39beeff615b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:43 +0200 Subject: [PATCH 39/78] drm/omap: Factor out display type to connector type conversion Move the code that computes the DRM connector type for the omapdss_device display type to a new omapdss_device_connector_type() function for later reuse. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Acked-by: Sam Ravnborg Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-24-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/base.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/omapdrm/dss/omapdss.h | 1 + drivers/gpu/drm/omapdrm/omap_connector.c | 19 +------------------ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/base.c b/drivers/gpu/drm/omapdrm/dss/base.c index a1970b9db6ab..cae5687822e2 100644 --- a/drivers/gpu/drm/omapdrm/dss/base.c +++ b/drivers/gpu/drm/omapdrm/dss/base.c @@ -285,6 +285,29 @@ void omapdss_device_post_disable(struct omap_dss_device *dssdev) } EXPORT_SYMBOL_GPL(omapdss_device_post_disable); +unsigned int omapdss_device_connector_type(enum omap_display_type type) +{ + switch (type) { + case OMAP_DISPLAY_TYPE_HDMI: + return DRM_MODE_CONNECTOR_HDMIA; + case OMAP_DISPLAY_TYPE_DVI: + return DRM_MODE_CONNECTOR_DVID; + case OMAP_DISPLAY_TYPE_DSI: + return DRM_MODE_CONNECTOR_DSI; + case OMAP_DISPLAY_TYPE_DPI: + case OMAP_DISPLAY_TYPE_DBI: + return DRM_MODE_CONNECTOR_DPI; + case OMAP_DISPLAY_TYPE_VENC: + /* TODO: This could also be composite */ + return DRM_MODE_CONNECTOR_SVIDEO; + case OMAP_DISPLAY_TYPE_SDI: + return DRM_MODE_CONNECTOR_LVDS; + default: + return DRM_MODE_CONNECTOR_Unknown; + } +} +EXPORT_SYMBOL_GPL(omapdss_device_connector_type); + /* ----------------------------------------------------------------------------- * Components Handling */ diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 79f6b195c7cf..c5672e5174c5 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -479,6 +479,7 @@ void omapdss_device_pre_enable(struct omap_dss_device *dssdev); void omapdss_device_enable(struct omap_dss_device *dssdev); void omapdss_device_disable(struct omap_dss_device *dssdev); void omapdss_device_post_disable(struct omap_dss_device *dssdev); +unsigned int omapdss_device_connector_type(enum omap_display_type type); int omap_dss_get_num_overlay_managers(void); diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 88dbf3fa473f..38c7a79c5d4a 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -296,24 +296,7 @@ static int omap_connector_get_type(struct omap_dss_device *output) type = display->type; omapdss_device_put(display); - switch (type) { - case OMAP_DISPLAY_TYPE_HDMI: - return DRM_MODE_CONNECTOR_HDMIA; - case OMAP_DISPLAY_TYPE_DVI: - return DRM_MODE_CONNECTOR_DVID; - case OMAP_DISPLAY_TYPE_DSI: - return DRM_MODE_CONNECTOR_DSI; - case OMAP_DISPLAY_TYPE_DPI: - case OMAP_DISPLAY_TYPE_DBI: - return DRM_MODE_CONNECTOR_DPI; - case OMAP_DISPLAY_TYPE_VENC: - /* TODO: This could also be composite */ - return DRM_MODE_CONNECTOR_SVIDEO; - case OMAP_DISPLAY_TYPE_SDI: - return DRM_MODE_CONNECTOR_LVDS; - default: - return DRM_MODE_CONNECTOR_Unknown; - } + return omapdss_device_connector_type(type); } /* initialize connector */ From a779618b4a07d90fd332b6dbb4e836678d0a1d43 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:44 +0200 Subject: [PATCH 40/78] drm/omap: Use the drm_panel_bridge API Replace the manual panel handling code by a drm_panel_bridge. This simplifies the driver and allows all components in the display pipeline to be treated as bridges, paving the way to generic connector handling. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-25-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/base.c | 12 ++++----- drivers/gpu/drm/omapdrm/dss/output.c | 31 +++++++++++++++++++++--- drivers/gpu/drm/omapdrm/omap_connector.c | 10 -------- drivers/gpu/drm/omapdrm/omap_drv.c | 13 ---------- drivers/gpu/drm/omapdrm/omap_encoder.c | 13 ---------- 5 files changed, 32 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/base.c b/drivers/gpu/drm/omapdrm/dss/base.c index cae5687822e2..80d48936d177 100644 --- a/drivers/gpu/drm/omapdrm/dss/base.c +++ b/drivers/gpu/drm/omapdrm/dss/base.c @@ -149,8 +149,7 @@ struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from) goto done; } - if (dssdev->id && - (dssdev->next || dssdev->bridge || dssdev->panel)) + if (dssdev->id && (dssdev->next || dssdev->bridge)) goto done; } @@ -185,11 +184,10 @@ int omapdss_device_connect(struct dss_device *dss, if (!dst) { /* * The destination is NULL when the source is connected to a - * bridge or panel instead of a DSS device. Stop here, we will - * attach the bridge or panel later when we will have a DRM - * encoder. + * bridge instead of a DSS device. Stop here, we will attach + * the bridge later when we will have a DRM encoder. */ - return src && (src->bridge || src->panel) ? 0 : -EINVAL; + return src && src->bridge ? 0 : -EINVAL; } if (omapdss_device_is_connected(dst)) @@ -217,7 +215,7 @@ void omapdss_device_disconnect(struct omap_dss_device *src, dst ? dev_name(dst->dev) : "NULL"); if (!dst) { - WARN_ON(!src->bridge && !src->panel); + WARN_ON(!src->bridge); return; } diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index 0693d34fca1b..99a253a424c1 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -21,6 +21,7 @@ int omapdss_device_init_output(struct omap_dss_device *out) { struct device_node *remote_node; + int ret; remote_node = of_graph_get_remote_node(out->dev->of_node, ffs(out->of_ports) - 1, 0); @@ -39,17 +40,39 @@ int omapdss_device_init_output(struct omap_dss_device *out) if (out->next && out->type != out->next->type) { dev_err(out->dev, "output type and display type don't match\n"); - omapdss_device_put(out->next); - out->next = NULL; - return -EINVAL; + ret = -EINVAL; + goto error; } - return out->next || out->bridge || out->panel ? 0 : -EPROBE_DEFER; + if (out->panel) { + struct drm_bridge *bridge; + + bridge = drm_panel_bridge_add(out->panel); + if (IS_ERR(bridge)) { + dev_err(out->dev, + "unable to create panel bridge (%ld)\n", + PTR_ERR(bridge)); + ret = PTR_ERR(bridge); + goto error; + } + + out->bridge = bridge; + } + + return out->next || out->bridge ? 0 : -EPROBE_DEFER; + +error: + omapdss_device_put(out->next); + out->next = NULL; + return ret; } EXPORT_SYMBOL(omapdss_device_init_output); void omapdss_device_cleanup_output(struct omap_dss_device *out) { + if (out->bridge && out->panel) + drm_panel_bridge_remove(out->bridge); + if (out->next) omapdss_device_put(out->next); } diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 38c7a79c5d4a..b0cb2ecb30ab 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -6,7 +6,6 @@ #include #include -#include #include #include "omap_drv.h" @@ -190,7 +189,6 @@ no_edid: static int omap_connector_get_modes(struct drm_connector *connector) { - struct omap_connector *omap_connector = to_omap_connector(connector); struct omap_dss_device *dssdev; DBG("%s", connector->name); @@ -213,14 +211,6 @@ static int omap_connector_get_modes(struct drm_connector *connector) if (dssdev) return dssdev->ops->get_modes(dssdev, connector); - /* - * Otherwise if the display pipeline uses a drm_panel, we delegate the - * operation to the panel API. - */ - if (omap_connector->output->panel) - return drm_panel_get_modes(omap_connector->output->panel, - connector); - /* * We can't retrieve modes, which can happen for instance for a DVI or * VGA output with the DDC bus unconnected. The KMS core will add the diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 390e0662a8b8..aefcf86d4045 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -134,9 +133,6 @@ static void omap_disconnect_pipelines(struct drm_device *ddev) for (i = 0; i < priv->num_pipes; i++) { struct omap_drm_pipeline *pipe = &priv->pipes[i]; - if (pipe->output->panel) - drm_panel_detach(pipe->output->panel); - omapdss_device_disconnect(NULL, pipe->output); omapdss_device_put(pipe->output); @@ -221,8 +217,6 @@ static int omap_display_id(struct omap_dss_device *output) bridge = drm_bridge_get_next_bridge(bridge); node = bridge->of_node; - } else if (output->panel) { - node = output->panel->dev->of_node; } return node ? of_alias_get_id(node, "display") : -ENODEV; @@ -337,13 +331,6 @@ static int omap_modeset_init(struct drm_device *dev) return -ENOMEM; drm_connector_attach_encoder(pipe->connector, encoder); - - if (pipe->output->panel) { - ret = drm_panel_attach(pipe->output->panel, - pipe->connector); - if (ret < 0) - return ret; - } } crtc = omap_crtc_init(dev, pipe, priv->planes[i]); diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index cb5aa01d2f87..a270173a2411 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -10,7 +10,6 @@ #include #include #include -#include #include "omap_drv.h" @@ -157,12 +156,6 @@ static void omap_encoder_disable(struct drm_encoder *encoder) dev_dbg(dev->dev, "disable(%s)\n", dssdev->name); - /* Disable the panel if present. */ - if (dssdev->panel) { - drm_panel_disable(dssdev->panel); - drm_panel_unprepare(dssdev->panel); - } - /* * Disable the chain of external devices, starting at the one at the * internal encoder's output. @@ -212,12 +205,6 @@ static void omap_encoder_enable(struct drm_encoder *encoder) * internal encoder's output. */ omapdss_device_enable(dssdev->next); - - /* Enable the panel if present. */ - if (dssdev->panel) { - drm_panel_prepare(dssdev->panel); - drm_panel_enable(dssdev->panel); - } } static int omap_encoder_atomic_check(struct drm_encoder *encoder, From c83fefd738344cf5bf22e1e1442e6f746b85c1d4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:45 +0200 Subject: [PATCH 41/78] drm/omap: dss: Fix output next device lookup in DT The DSS core looks up the next device connected to an output by traversing the OF graph. It currently hardcodes the local port number to 0, which breaks any output with a different port number (SDI on OMAP3 and any DPI output but the first one). Fix this by repurposing the currently unused of_ports bitmask in omap_dss_device with an of_port output port number, and use it to traverse the OF graph. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-26-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c | 2 +- drivers/gpu/drm/omapdrm/displays/connector-hdmi.c | 2 +- drivers/gpu/drm/omapdrm/displays/encoder-opa362.c | 2 +- drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c | 2 +- drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c | 2 +- drivers/gpu/drm/omapdrm/dss/dpi.c | 2 +- drivers/gpu/drm/omapdrm/dss/dsi.c | 2 +- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 2 +- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 2 +- drivers/gpu/drm/omapdrm/dss/omapdss.h | 4 ++-- drivers/gpu/drm/omapdrm/dss/output.c | 3 +-- drivers/gpu/drm/omapdrm/dss/sdi.c | 2 +- drivers/gpu/drm/omapdrm/dss/venc.c | 2 +- 13 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c index 0d20fab605d7..f36aa1885d39 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c @@ -55,7 +55,7 @@ static int tvc_probe(struct platform_device *pdev) dssdev->type = OMAP_DISPLAY_TYPE_VENC; dssdev->display = true; dssdev->owner = THIS_MODULE; - dssdev->of_ports = BIT(0); + dssdev->of_port = 0; omapdss_display_init(dssdev); omapdss_device_register(dssdev); diff --git a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c index f5d69d810bb8..37c212491cd3 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c @@ -139,7 +139,7 @@ static int hdmic_probe(struct platform_device *pdev) dssdev->type = OMAP_DISPLAY_TYPE_HDMI; dssdev->display = true; dssdev->owner = THIS_MODULE; - dssdev->of_ports = BIT(0); + dssdev->of_port = 0; dssdev->ops_flags = ddata->hpd_gpio ? OMAP_DSS_DEVICE_OP_DETECT | OMAP_DSS_DEVICE_OP_HPD : 0; diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c index b992387ed674..252705222ef1 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c @@ -86,7 +86,7 @@ static int opa362_probe(struct platform_device *pdev) dssdev->dev = &pdev->dev; dssdev->type = OMAP_DISPLAY_TYPE_VENC; dssdev->owner = THIS_MODULE; - dssdev->of_ports = BIT(1) | BIT(0); + dssdev->of_port = 1; dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1); if (IS_ERR(dssdev->next)) { diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c index 089105c5aa0a..857ae84cd7d1 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c @@ -165,7 +165,7 @@ static int tpd_probe(struct platform_device *pdev) dssdev->dev = &pdev->dev; dssdev->type = OMAP_DISPLAY_TYPE_HDMI; dssdev->owner = THIS_MODULE; - dssdev->of_ports = BIT(1) | BIT(0); + dssdev->of_port = 1; dssdev->ops_flags = OMAP_DSS_DEVICE_OP_DETECT | OMAP_DSS_DEVICE_OP_HPD; diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c index 3ec6a55e932a..3484b5d4a91c 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c @@ -1265,7 +1265,7 @@ static int dsicm_probe(struct platform_device *pdev) dssdev->type = OMAP_DISPLAY_TYPE_DSI; dssdev->display = true; dssdev->owner = THIS_MODULE; - dssdev->of_ports = BIT(0); + dssdev->of_port = 0; dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES; dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 95147437b990..462ed6f3118a 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -625,7 +625,7 @@ static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port) out->id = OMAP_DSS_OUTPUT_DPI; out->type = OMAP_DISPLAY_TYPE_DPI; out->dispc_channel = dpi_get_channel(dpi); - out->of_ports = BIT(port_num); + out->of_port = port_num; out->ops = &dpi_ops; out->owner = THIS_MODULE; diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index da16ea095f13..6379eea124d1 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -5116,7 +5116,7 @@ static int dsi_init_output(struct dsi_data *dsi) out->dispc_channel = dsi_get_channel(dsi); out->ops = &dsi_ops; out->owner = THIS_MODULE; - out->of_ports = BIT(0); + out->of_port = 0; out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE | DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE; diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 0f557fad4513..44075718407b 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -673,7 +673,7 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi) out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; out->ops = &hdmi_ops; out->owner = THIS_MODULE; - out->of_ports = BIT(0); + out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; r = omapdss_device_init_output(out); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index d9463b332554..1b5bd44ee09d 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -657,7 +657,7 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi) out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; out->ops = &hdmi_ops; out->owner = THIS_MODULE; - out->of_ports = BIT(0); + out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; r = omapdss_device_init_output(out); diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index c5672e5174c5..b48a51d11310 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -436,8 +436,8 @@ struct omap_dss_device { /* output instance */ enum omap_dss_output_id id; - /* bitmask of port numbers in DT */ - unsigned int of_ports; + /* port number in DT */ + unsigned int of_port; }; struct omap_dss_driver { diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index 99a253a424c1..c1ec9d343e53 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -4,7 +4,6 @@ * Author: Archit Taneja */ -#include #include #include #include @@ -24,7 +23,7 @@ int omapdss_device_init_output(struct omap_dss_device *out) int ret; remote_node = of_graph_get_remote_node(out->dev->of_node, - ffs(out->of_ports) - 1, 0); + out->of_port, 0); if (!remote_node) { dev_dbg(out->dev, "failed to find video sink\n"); return 0; diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index 3b447c01fa2a..9092ed3d0ef1 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -265,7 +265,7 @@ static int sdi_init_output(struct sdi_device *sdi) out->name = "sdi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_LCD; /* We have SDI only on OMAP3, where it's on port 1 */ - out->of_ports = BIT(1); + out->of_port = 1; out->ops = &sdi_ops; out->owner = THIS_MODULE; out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE /* 15.5.9.1.2 */ diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index 596a297d5813..e2f480f689b8 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -754,7 +754,7 @@ static int venc_init_output(struct venc_device *venc) out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; out->ops = &venc_ops; out->owner = THIS_MODULE; - out->of_ports = BIT(0); + out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_MODES; r = omapdss_device_init_output(out); From 326a1166ca0826e2fdccc2b9174a8f7802bd5100 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:46 +0200 Subject: [PATCH 42/78] drm/omap: Add infrastructure to support drm_bridge local to DSS outputs In order to support drm_bridge-based pipeline, the internal HDMI encoders will need to expose the EDID read operation through the drm_bridge API, and thus to expose a drm_bridge instance corresponding to the encoder. The HDMI encoders are however handled as omap_dss_device instances, which conflicts with this requirement. In order to move forward with the drm_bridge transition, add support for creating drm_bridge instances local to DSS outputs. If a local bridge is passed to the omapdss_device_init_output() function, it is used as the first bridge in the chain, and the omap_dss_device.next_bridge field is set to the next bridge for the use of the internal encoders' bridges. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-27-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/dpi.c | 2 +- drivers/gpu/drm/omapdrm/dss/dsi.c | 2 +- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 2 +- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 2 +- drivers/gpu/drm/omapdrm/dss/omapdss.h | 4 +++- drivers/gpu/drm/omapdrm/dss/output.c | 20 ++++++++++++++++---- drivers/gpu/drm/omapdrm/dss/sdi.c | 2 +- drivers/gpu/drm/omapdrm/dss/venc.c | 2 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- 9 files changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 462ed6f3118a..2d0eb5fcbb5b 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -629,7 +629,7 @@ static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port) out->ops = &dpi_ops; out->owner = THIS_MODULE; - r = omapdss_device_init_output(out); + r = omapdss_device_init_output(out, NULL); if (r < 0) return r; diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 6379eea124d1..79ddfbfd1b58 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -5121,7 +5121,7 @@ static int dsi_init_output(struct dsi_data *dsi) | DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE; - r = omapdss_device_init_output(out); + r = omapdss_device_init_output(out, NULL); if (r < 0) return r; diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 44075718407b..dd4a14fe7e59 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -676,7 +676,7 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi) out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; - r = omapdss_device_init_output(out); + r = omapdss_device_init_output(out, NULL); if (r < 0) return r; diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 1b5bd44ee09d..8e3790dd8b98 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -660,7 +660,7 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi) out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; - r = omapdss_device_init_output(out); + r = omapdss_device_init_output(out, NULL); if (r < 0) return r; diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index b48a51d11310..82e9bfa5530a 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -400,6 +400,7 @@ struct omap_dss_device { struct dss_device *dss; struct omap_dss_device *next; struct drm_bridge *bridge; + struct drm_bridge *next_bridge; struct drm_panel *panel; struct list_head list; @@ -488,7 +489,8 @@ int omap_dss_get_num_overlays(void); #define for_each_dss_output(d) \ while ((d = omapdss_device_next_output(d)) != NULL) struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from); -int omapdss_device_init_output(struct omap_dss_device *out); +int omapdss_device_init_output(struct omap_dss_device *out, + struct drm_bridge *local_bridge); void omapdss_device_cleanup_output(struct omap_dss_device *out); typedef void (*omap_dispc_isr_t) (void *arg, u32 mask); diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index c1ec9d343e53..9ba7cc8539a1 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -17,7 +17,8 @@ #include "dss.h" #include "omapdss.h" -int omapdss_device_init_output(struct omap_dss_device *out) +int omapdss_device_init_output(struct omap_dss_device *out, + struct drm_bridge *local_bridge) { struct device_node *remote_node; int ret; @@ -58,10 +59,20 @@ int omapdss_device_init_output(struct omap_dss_device *out) out->bridge = bridge; } - return out->next || out->bridge ? 0 : -EPROBE_DEFER; + if (local_bridge) { + out->next_bridge = out->bridge; + out->bridge = local_bridge; + } + + if (!out->next && !out->bridge) { + ret = -EPROBE_DEFER; + goto error; + } + + return 0; error: - omapdss_device_put(out->next); + omapdss_device_cleanup_output(out); out->next = NULL; return ret; } @@ -70,7 +81,8 @@ EXPORT_SYMBOL(omapdss_device_init_output); void omapdss_device_cleanup_output(struct omap_dss_device *out) { if (out->bridge && out->panel) - drm_panel_bridge_remove(out->bridge); + drm_panel_bridge_remove(out->next_bridge ? + out->next_bridge : out->bridge); if (out->next) omapdss_device_put(out->next); diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index 9092ed3d0ef1..11aa2f712ff4 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -271,7 +271,7 @@ static int sdi_init_output(struct sdi_device *sdi) out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE /* 15.5.9.1.2 */ | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE; - r = omapdss_device_init_output(out); + r = omapdss_device_init_output(out, NULL); if (r < 0) return r; diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index e2f480f689b8..977d8d525b43 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -757,7 +757,7 @@ static int venc_init_output(struct venc_device *venc) out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_MODES; - r = omapdss_device_init_output(out); + r = omapdss_device_init_output(out, NULL); if (r < 0) return r; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index aefcf86d4045..1df509342b5d 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -324,7 +324,7 @@ static int omap_modeset_init(struct drm_device *dev) struct drm_encoder *encoder = pipe->encoder; struct drm_crtc *crtc; - if (!pipe->output->bridge) { + if (pipe->output->next) { pipe->connector = omap_connector_init(dev, pipe->output, encoder); if (!pipe->connector) From db0fefd1b90d7d2a23090e9178ce742fe1b0aadd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:47 +0200 Subject: [PATCH 43/78] drm/omap: dss: Make omap_dss_device_ops optional As part of the move to drm_bridge ops, the dssdev ops will become empty for some of the internal encoders. Make them optional in the driver to allow them to be removed completely, easing the transition. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-28-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/base.c | 21 ++++++++++++--------- drivers/gpu/drm/omapdrm/dss/dss.c | 3 ++- drivers/gpu/drm/omapdrm/omap_connector.c | 2 +- drivers/gpu/drm/omapdrm/omap_encoder.c | 12 +++++++----- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/base.c b/drivers/gpu/drm/omapdrm/dss/base.c index 80d48936d177..2db3bd2f19db 100644 --- a/drivers/gpu/drm/omapdrm/dss/base.c +++ b/drivers/gpu/drm/omapdrm/dss/base.c @@ -195,10 +195,12 @@ int omapdss_device_connect(struct dss_device *dss, dst->dss = dss; - ret = dst->ops->connect(src, dst); - if (ret < 0) { - dst->dss = NULL; - return ret; + if (dst->ops && dst->ops->connect) { + ret = dst->ops->connect(src, dst); + if (ret < 0) { + dst->dss = NULL; + return ret; + } } return 0; @@ -226,7 +228,8 @@ void omapdss_device_disconnect(struct omap_dss_device *src, WARN_ON(dst->state != OMAP_DSS_DISPLAY_DISABLED); - dst->ops->disconnect(src, dst); + if (dst->ops && dst->ops->disconnect) + dst->ops->disconnect(src, dst); dst->dss = NULL; } EXPORT_SYMBOL_GPL(omapdss_device_disconnect); @@ -238,7 +241,7 @@ void omapdss_device_pre_enable(struct omap_dss_device *dssdev) omapdss_device_pre_enable(dssdev->next); - if (dssdev->ops->pre_enable) + if (dssdev->ops && dssdev->ops->pre_enable) dssdev->ops->pre_enable(dssdev); } EXPORT_SYMBOL_GPL(omapdss_device_pre_enable); @@ -248,7 +251,7 @@ void omapdss_device_enable(struct omap_dss_device *dssdev) if (!dssdev) return; - if (dssdev->ops->enable) + if (dssdev->ops && dssdev->ops->enable) dssdev->ops->enable(dssdev); omapdss_device_enable(dssdev->next); @@ -264,7 +267,7 @@ void omapdss_device_disable(struct omap_dss_device *dssdev) omapdss_device_disable(dssdev->next); - if (dssdev->ops->disable) + if (dssdev->ops && dssdev->ops->disable) dssdev->ops->disable(dssdev); } EXPORT_SYMBOL_GPL(omapdss_device_disable); @@ -274,7 +277,7 @@ void omapdss_device_post_disable(struct omap_dss_device *dssdev) if (!dssdev) return; - if (dssdev->ops->post_disable) + if (dssdev->ops && dssdev->ops->post_disable) dssdev->ops->post_disable(dssdev); omapdss_device_post_disable(dssdev->next); diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c b/drivers/gpu/drm/omapdrm/dss/dss.c index 67b92b5d8dd7..b76fc2b56227 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.c +++ b/drivers/gpu/drm/omapdrm/dss/dss.c @@ -1552,7 +1552,8 @@ static void dss_shutdown(struct platform_device *pdev) DSSDBG("shutdown\n"); for_each_dss_output(dssdev) { - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE && + dssdev->ops && dssdev->ops->disable) dssdev->ops->disable(dssdev); } } diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index b0cb2ecb30ab..a24cec4b0bb9 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -228,7 +228,7 @@ enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev, drm_mode_copy(adjusted_mode, mode); for (; dssdev; dssdev = dssdev->next) { - if (!dssdev->ops->check_timings) + if (!dssdev->ops || !dssdev->ops->check_timings) continue; ret = dssdev->ops->check_timings(dssdev, adjusted_mode); diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index a270173a2411..b232acd3bc3d 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -77,10 +77,10 @@ static void omap_encoder_hdmi_mode_set(struct drm_connector *connector, struct omap_dss_device *dssdev = omap_encoder->output; bool hdmi_mode = connector->display_info.is_hdmi; - if (dssdev->ops->hdmi.set_hdmi_mode) + if (dssdev->ops && dssdev->ops->hdmi.set_hdmi_mode) dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode); - if (hdmi_mode && dssdev->ops->hdmi.set_infoframe) { + if (hdmi_mode && dssdev->ops && dssdev->ops->hdmi.set_infoframe) { struct hdmi_avi_infoframe avi; int r; @@ -139,7 +139,7 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder, dss_mgr_set_timings(output, &vm); for (dssdev = output; dssdev; dssdev = dssdev->next) { - if (dssdev->ops->set_timings) + if (dssdev->ops && dssdev->ops->set_timings) dssdev->ops->set_timings(dssdev, adjusted_mode); } @@ -168,7 +168,8 @@ static void omap_encoder_disable(struct drm_encoder *encoder) * flow where the pipeline output controls the encoder. */ if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) { - dssdev->ops->disable(dssdev); + if (dssdev->ops && dssdev->ops->disable) + dssdev->ops->disable(dssdev); dssdev->state = OMAP_DSS_DISPLAY_DISABLED; } @@ -196,7 +197,8 @@ static void omap_encoder_enable(struct drm_encoder *encoder) * flow where the pipeline output controls the encoder. */ if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) { - dssdev->ops->enable(dssdev); + if (dssdev->ops && dssdev->ops->enable) + dssdev->ops->enable(dssdev); dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; } From 0fe37173ce0ec7ca23232a1a242059db73c0ff16 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:48 +0200 Subject: [PATCH 44/78] drm/omap: hdmi: Allocate EDID in the .read_edid() operation Bring the omapdss-specific .read_edid() operation in sync with the drm_bridge .get_edid() operation to ease code reuse. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-29-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 36 ++++++++++++++++-------- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 24 ++++++++++++---- drivers/gpu/drm/omapdrm/dss/omapdss.h | 2 +- drivers/gpu/drm/omapdrm/omap_connector.c | 12 ++------ 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index dd4a14fe7e59..e15fa3862922 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -405,31 +405,45 @@ static void hdmi_disconnect(struct omap_dss_device *src, omapdss_device_disconnect(dst, dst->next); } -static int hdmi_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) +#define MAX_EDID 512 + +static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) { struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); bool need_enable; + u8 *edid; int r; + edid = kzalloc(MAX_EDID, GFP_KERNEL); + if (!edid) + return NULL; + need_enable = hdmi->core_enabled == false; if (need_enable) { r = hdmi4_core_enable(&hdmi->core); - if (r) - return r; + if (r) { + kfree(edid); + return NULL; + } + } + + r = read_edid(hdmi, edid, MAX_EDID); + if (r < 0) { + kfree(edid); + edid = NULL; + } else { + unsigned int cec_addr; + + cec_addr = r >= 256 ? cec_get_edid_phys_addr(edid, r, NULL) + : CEC_PHYS_ADDR_INVALID; + hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr); } - r = read_edid(hdmi, edid, len); - if (r >= 256) - hdmi4_cec_set_phys_addr(&hdmi->core, - cec_get_edid_phys_addr(edid, r, NULL)); - else - hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); if (need_enable) hdmi4_core_disable(&hdmi->core); - return r; + return (struct edid *)edid; } static void hdmi_lost_hotplug(struct omap_dss_device *dssdev) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 8e3790dd8b98..99720dfc5769 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -410,27 +410,39 @@ static void hdmi_disconnect(struct omap_dss_device *src, omapdss_device_disconnect(dst, dst->next); } -static int hdmi_read_edid(struct omap_dss_device *dssdev, - u8 *edid, int len) +#define MAX_EDID 512 + +static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) { struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); bool need_enable; + u8 *edid; int r; + edid = kzalloc(MAX_EDID, GFP_KERNEL); + if (!edid) + return NULL; + need_enable = hdmi->core_enabled == false; if (need_enable) { r = hdmi_core_enable(hdmi); - if (r) - return r; + if (r) { + kfree(edid); + return NULL; + } } - r = read_edid(hdmi, edid, len); + r = read_edid(hdmi, edid, MAX_EDID); + if (r < 0) { + kfree(edid); + edid = NULL; + } if (need_enable) hdmi_core_disable(hdmi); - return r; + return (struct edid *)edid; } static int hdmi_set_infoframe(struct omap_dss_device *dssdev, diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 82e9bfa5530a..269e143d57be 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -367,7 +367,7 @@ struct omap_dss_device_ops { void *cb_data); void (*unregister_hpd_cb)(struct omap_dss_device *dssdev); - int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len); + struct edid *(*read_edid)(struct omap_dss_device *dssdev); int (*get_modes)(struct omap_dss_device *dssdev, struct drm_connector *connector); diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index a24cec4b0bb9..c636ae228130 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -153,25 +153,19 @@ static void omap_connector_destroy(struct drm_connector *connector) kfree(omap_connector); } -#define MAX_EDID 512 - static int omap_connector_get_modes_edid(struct drm_connector *connector, struct omap_dss_device *dssdev) { enum drm_connector_status status; - void *edid; + struct edid *edid; int n; status = omap_connector_detect(connector, false); if (status != connector_status_connected) goto no_edid; - edid = kzalloc(MAX_EDID, GFP_KERNEL); - if (!edid) - goto no_edid; - - if (dssdev->ops->read_edid(dssdev, edid, MAX_EDID) <= 0 || - !drm_edid_is_valid(edid)) { + edid = dssdev->ops->read_edid(dssdev); + if (!edid || !drm_edid_is_valid(edid)) { kfree(edid); goto no_edid; } From a4659694a7e5dd9bfaa07f14ecd5d9e903ae0a0b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:49 +0200 Subject: [PATCH 45/78] drm/omap: hdmi4: Rework EDID read to isolate data read In preparation of adding DRM bridge support to the hdmi4 encoder code, rework the EDID read to isolate data read. The hdmi_read_edid() function is the main entry point. It performs all initialisation steps required prior to reading the EDID (such as ensuring the device is powered on), as well as corresponding cleanup steps afterwards. EDID read itself is handled by hdmi_read_edid_data() that calls the hdmi4_core_ddc_read() function to read individual blocks. This new code architecture will allow reusing hdmi_read_edid() and hdmi4_core_ddc_read() for the drm_bridge EDID read implementation, while swapping out hdmi_read_edid_data() for the DRM drm_do_get_edid() function. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-30-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 94 +++++++++++++++--------- drivers/gpu/drm/omapdrm/dss/hdmi4_core.c | 59 +++------------ drivers/gpu/drm/omapdrm/dss/hdmi4_core.h | 4 +- 3 files changed, 73 insertions(+), 84 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index e15fa3862922..37536b9f3114 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -272,23 +272,6 @@ static int hdmi_dump_regs(struct seq_file *s, void *p) return 0; } -static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len) -{ - int r; - - mutex_lock(&hdmi->lock); - - r = hdmi_runtime_get(hdmi); - BUG_ON(r); - - r = hdmi4_read_edid(&hdmi->core, buf, len); - - hdmi_runtime_put(hdmi); - mutex_unlock(&hdmi->lock); - - return r; -} - static void hdmi_start_audio_stream(struct omap_hdmi *hd) { hdmi_wp_audio_enable(&hd->wp, true); @@ -407,10 +390,8 @@ static void hdmi_disconnect(struct omap_dss_device *src, #define MAX_EDID 512 -static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) +static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - bool need_enable; u8 *edid; int r; @@ -418,32 +399,79 @@ static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) if (!edid) return NULL; + r = hdmi4_core_ddc_read(&hdmi->core, edid, 0, EDID_LENGTH); + if (r) + goto error; + + if (edid[0x7e] > 0) { + char checksum = 0; + unsigned int i; + + r = hdmi4_core_ddc_read(&hdmi->core, edid + EDID_LENGTH, 1, + EDID_LENGTH); + if (r) + goto error; + + for (i = 0; i < EDID_LENGTH; ++i) + checksum += edid[EDID_LENGTH + i]; + + if (checksum != 0) { + DSSERR("E-EDID checksum failed!!\n"); + goto error; + } + } + + return (struct edid *)edid; + +error: + kfree(edid); + return NULL; +} + +static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) +{ + struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + struct edid *edid = NULL; + unsigned int cec_addr; + bool need_enable; + int r; + need_enable = hdmi->core_enabled == false; if (need_enable) { r = hdmi4_core_enable(&hdmi->core); - if (r) { - kfree(edid); + if (r) return NULL; - } } - r = read_edid(hdmi, edid, MAX_EDID); - if (r < 0) { - kfree(edid); - edid = NULL; + mutex_lock(&hdmi->lock); + r = hdmi_runtime_get(hdmi); + BUG_ON(r); + + r = hdmi4_core_ddc_init(&hdmi->core); + if (r) + goto done; + + edid = hdmi_read_edid_data(hdmi); + +done: + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); + + if (edid && edid->extensions) { + unsigned int len = (edid->extensions + 1) * EDID_LENGTH; + + cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL); } else { - unsigned int cec_addr; - - cec_addr = r >= 256 ? cec_get_edid_phys_addr(edid, r, NULL) - : CEC_PHYS_ADDR_INVALID; - hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr); + cec_addr = CEC_PHYS_ADDR_INVALID; } + hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr); + if (need_enable) hdmi4_core_disable(&hdmi->core); - return (struct edid *)edid; + return edid; } static void hdmi_lost_hotplug(struct omap_dss_device *dssdev) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c index ea5d5c228534..751985a2679a 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c @@ -32,7 +32,7 @@ static inline void __iomem *hdmi_av_base(struct hdmi_core_data *core) return core->base + HDMI_CORE_AV; } -static int hdmi_core_ddc_init(struct hdmi_core_data *core) +int hdmi4_core_ddc_init(struct hdmi_core_data *core) { void __iomem *base = core->base; @@ -74,13 +74,11 @@ static int hdmi_core_ddc_init(struct hdmi_core_data *core) return 0; } -static int hdmi_core_ddc_edid(struct hdmi_core_data *core, - u8 *pedid, int ext) +int hdmi4_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len) { + struct hdmi_core_data *core = data; void __iomem *base = core->base; u32 i; - char checksum; - u32 offset = 0; /* HDMI_CORE_DDC_STATUS_IN_PROG */ if (hdmi_wait_for_bit_change(base, HDMI_CORE_DDC_STATUS, @@ -89,24 +87,21 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, return -ETIMEDOUT; } - if (ext % 2 != 0) - offset = 0x80; - /* Load Segment Address Register */ - REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, ext / 2, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_DDC_SEGM, block / 2, 7, 0); /* Load Slave Address Register */ REG_FLD_MOD(base, HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1); /* Load Offset Address Register */ - REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, offset, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_DDC_OFFSET, block % 2 ? 0x80 : 0, 7, 0); /* Load Byte Count */ - REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, 0x80, 7, 0); + REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT1, len, 7, 0); REG_FLD_MOD(base, HDMI_CORE_DDC_COUNT2, 0x0, 1, 0); /* Set DDC_CMD */ - if (ext) + if (block) REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x4, 3, 0); else REG_FLD_MOD(base, HDMI_CORE_DDC_CMD, 0x2, 3, 0); @@ -122,7 +117,7 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, return -EIO; } - for (i = 0; i < 0x80; ++i) { + for (i = 0; i < len; ++i) { int t; /* IN_PROG */ @@ -141,48 +136,12 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, udelay(1); } - pedid[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0); - } - - checksum = 0; - for (i = 0; i < 0x80; ++i) - checksum += pedid[i]; - - if (checksum != 0) { - DSSERR("E-EDID checksum failed!!\n"); - return -EIO; + buf[i] = REG_GET(base, HDMI_CORE_DDC_DATA, 7, 0); } return 0; } -int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len) -{ - int r, l; - - if (len < 128) - return -EINVAL; - - r = hdmi_core_ddc_init(core); - if (r) - return r; - - r = hdmi_core_ddc_edid(core, edid, 0); - if (r) - return r; - - l = 128; - - if (len >= 128 * 2 && edid[0x7e] > 0) { - r = hdmi_core_ddc_edid(core, edid + 0x80, 1); - if (r) - return r; - l += 128; - } - - return l; -} - static void hdmi_core_init(struct hdmi_core_video_config *video_cfg) { DSSDBG("Enter hdmi_core_init\n"); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h index 11c4b7ba1eee..dc64ae2aa300 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.h @@ -249,7 +249,9 @@ struct hdmi_core_packet_enable_repeat { u32 generic_pkt_repeat; }; -int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len); +int hdmi4_core_ddc_init(struct hdmi_core_data *core); +int hdmi4_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len); + void hdmi4_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, struct hdmi_config *cfg); void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s); From 1edc1a1cbe7f5b5692267cdcf227528c2f39f198 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:50 +0200 Subject: [PATCH 46/78] drm/omap: hdmi5: Rework EDID read to isolate data read In preparation of adding DRM bridge support to the hdmi5 encoder code, rework the EDID read to isolate data read. The hdmi_read_edid() function is the main entry point. It performs all initialisation steps required prior to reading the EDID (such as ensuring the device is powered on), as well as corresponding cleanup steps afterwards. EDID read itself is handled by hdmi_read_edid_data() that calls the hdmi5_core_ddc_read() function to read individual blocks. This new code architecture will allow reusing hdmi_read_edid() and hdmi5_core_ddc_read() for the drm_bridge EDID read implementation, while swapping out hdmi_read_edid_data() for the DRM drm_do_get_edid() function. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-31-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 89 ++++++++++++++---------- drivers/gpu/drm/omapdrm/dss/hdmi5_core.c | 48 +++---------- drivers/gpu/drm/omapdrm/dss/hdmi5_core.h | 5 +- 3 files changed, 65 insertions(+), 77 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 99720dfc5769..2b02b0a11696 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -271,30 +271,6 @@ static int hdmi_dump_regs(struct seq_file *s, void *p) return 0; } -static int read_edid(struct omap_hdmi *hdmi, u8 *buf, int len) -{ - int r; - int idlemode; - - mutex_lock(&hdmi->lock); - - r = hdmi_runtime_get(hdmi); - BUG_ON(r); - - idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2); - /* No-idle mode */ - REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); - - r = hdmi5_read_edid(&hdmi->core, buf, len); - - REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); - - hdmi_runtime_put(hdmi); - mutex_unlock(&hdmi->lock); - - return r; -} - static void hdmi_start_audio_stream(struct omap_hdmi *hd) { REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); @@ -412,32 +388,73 @@ static void hdmi_disconnect(struct omap_dss_device *src, #define MAX_EDID 512 -static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) +static struct edid *hdmi_read_edid_data(struct hdmi_core_data *core) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - bool need_enable; + int max_ext_blocks = 3; + int r, n, i; u8 *edid; - int r; edid = kzalloc(MAX_EDID, GFP_KERNEL); if (!edid) return NULL; + r = hdmi5_core_ddc_read(core, edid, 0, EDID_LENGTH); + if (r) + goto error; + + n = edid[0x7e]; + + if (n > max_ext_blocks) + n = max_ext_blocks; + + for (i = 1; i <= n; i++) { + r = hdmi5_core_ddc_read(core, edid + i * EDID_LENGTH, i, + EDID_LENGTH); + if (r) + goto error; + } + + return (struct edid *)edid; + +error: + kfree(edid); + return NULL; +} + +static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) +{ + struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); + struct edid *edid; + bool need_enable; + int idlemode; + int r; + need_enable = hdmi->core_enabled == false; if (need_enable) { r = hdmi_core_enable(hdmi); - if (r) { - kfree(edid); + if (r) return NULL; - } } - r = read_edid(hdmi, edid, MAX_EDID); - if (r < 0) { - kfree(edid); - edid = NULL; - } + mutex_lock(&hdmi->lock); + r = hdmi_runtime_get(hdmi); + BUG_ON(r); + + idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2); + /* No-idle mode */ + REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); + + hdmi5_core_ddc_init(&hdmi->core); + + edid = hdmi_read_edid_data(&hdmi->core); + + hdmi5_core_ddc_uninit(&hdmi->core); + + REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); + + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); if (need_enable) hdmi_core_disable(hdmi); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c index ff4d35c8771f..7dd587035160 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c @@ -23,7 +23,7 @@ #include "hdmi5_core.h" -static void hdmi_core_ddc_init(struct hdmi_core_data *core) +void hdmi5_core_ddc_init(struct hdmi_core_data *core) { void __iomem *base = core->base; const unsigned long long iclk = 266000000; /* DSS L3 ICLK */ @@ -102,7 +102,7 @@ static void hdmi_core_ddc_init(struct hdmi_core_data *core) REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x0, 2, 2); } -static void hdmi_core_ddc_uninit(struct hdmi_core_data *core) +void hdmi5_core_ddc_uninit(struct hdmi_core_data *core) { void __iomem *base = core->base; @@ -112,14 +112,14 @@ static void hdmi_core_ddc_uninit(struct hdmi_core_data *core) REG_FLD_MOD(base, HDMI_CORE_I2CM_INT, 0x1, 2, 2); } -static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext) +int hdmi5_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len) { + struct hdmi_core_data *core = data; void __iomem *base = core->base; u8 cur_addr; - char checksum = 0; const int retries = 1000; - u8 seg_ptr = ext / 2; - u8 edidbase = ((ext % 2) * 0x80); + u8 seg_ptr = block / 2; + u8 edidbase = ((block % 2) * EDID_LENGTH); REG_FLD_MOD(base, HDMI_CORE_I2CM_SEGPTR, seg_ptr, 7, 0); @@ -127,7 +127,7 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext) * TODO: We use polling here, although we probably should use proper * interrupts. */ - for (cur_addr = 0; cur_addr < 128; ++cur_addr) { + for (cur_addr = 0; cur_addr < len; ++cur_addr) { int i; /* clear ERROR and DONE */ @@ -164,45 +164,13 @@ static int hdmi_core_ddc_edid(struct hdmi_core_data *core, u8 *pedid, u8 ext) return -EIO; } - pedid[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0); - checksum += pedid[cur_addr]; + buf[cur_addr] = REG_GET(base, HDMI_CORE_I2CM_DATAI, 7, 0); } return 0; } -int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len) -{ - int r, n, i; - int max_ext_blocks = (len / 128) - 1; - - if (len < 128) - return -EINVAL; - - hdmi_core_ddc_init(core); - - r = hdmi_core_ddc_edid(core, edid, 0); - if (r) - goto out; - - n = edid[0x7e]; - - if (n > max_ext_blocks) - n = max_ext_blocks; - - for (i = 1; i <= n; i++) { - r = hdmi_core_ddc_edid(core, edid + i * EDID_LENGTH, i); - if (r) - goto out; - } - -out: - hdmi_core_ddc_uninit(core); - - return r ? r : len; -} - void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s) { diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.h b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.h index f10b8a283011..65eadefdb3f9 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.h @@ -281,7 +281,10 @@ struct csc_table { u16 c1, c2, c3, c4; }; -int hdmi5_read_edid(struct hdmi_core_data *core, u8 *edid, int len); +void hdmi5_core_ddc_init(struct hdmi_core_data *core); +int hdmi5_core_ddc_read(void *data, u8 *buf, unsigned int block, size_t len); +void hdmi5_core_ddc_uninit(struct hdmi_core_data *core); + void hdmi5_core_dump(struct hdmi_core_data *core, struct seq_file *s); int hdmi5_core_handle_irqs(struct hdmi_core_data *core); void hdmi5_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp, From ee34f23ae82e568f441835a1bd5476b4be2dd6fc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:51 +0200 Subject: [PATCH 47/78] drm/omap: hdmi4: Register a drm_bridge for EDID read In order to integrate with a chain of drm_bridge, the internal HDMI4 encoder has to expose the EDID read operation through the drm_bridge API. Register a bridge at initialisation time to do so. For the time being make the next bridge in the chain optional as the HDMI output is still based on omap_dss_device. The create_connector argument to the bridge attach function is also ignored for the same reason. This will be changed later when removing the related omapdrm-specific display drivers. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-32-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi.h | 3 ++ drivers/gpu/drm/omapdrm/dss/hdmi4.c | 78 ++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h b/drivers/gpu/drm/omapdrm/dss/hdmi.h index c867552c925c..bd43f6abf27b 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "omapdss.h" #include "dss.h" @@ -364,6 +365,7 @@ struct omap_hdmi { bool core_enabled; struct omap_dss_device output; + struct drm_bridge bridge; struct platform_device *audio_pdev; void (*audio_abort_cb)(struct device *dev); @@ -379,5 +381,6 @@ struct omap_hdmi { }; #define dssdev_to_hdmi(dssdev) container_of(dssdev, struct omap_hdmi, output) +#define drm_bridge_to_hdmi(b) container_of(b, struct omap_hdmi, bridge) #endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 37536b9f3114..67994287447b 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -390,7 +390,8 @@ static void hdmi_disconnect(struct omap_dss_device *src, #define MAX_EDID 512 -static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi) +static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi, + struct drm_connector *connector) { u8 *edid; int r; @@ -428,9 +429,12 @@ error: return NULL; } -static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) +static struct edid * +hdmi_do_read_edid(struct omap_hdmi *hdmi, + struct edid *(*read)(struct omap_hdmi *hdmi, + struct drm_connector *connector), + struct drm_connector *connector) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); struct edid *edid = NULL; unsigned int cec_addr; bool need_enable; @@ -452,7 +456,7 @@ static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) if (r) goto done; - edid = hdmi_read_edid_data(hdmi); + edid = read(hdmi, connector); done: hdmi_runtime_put(hdmi); @@ -474,6 +478,12 @@ done: return edid; } +static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) +{ + return hdmi_do_read_edid(dssdev_to_hdmi(dssdev), hdmi_read_edid_data, + NULL); +} + static void hdmi_lost_hotplug(struct omap_dss_device *dssdev) { struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); @@ -517,6 +527,56 @@ static const struct omap_dss_device_ops hdmi_ops = { }, }; +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ + +static int hdmi4_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + if (!hdmi->output.next_bridge) + return 0; + + return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge, + bridge, flags); +} + +static struct edid *hdmi4_bridge_read_edid(struct omap_hdmi *hdmi, + struct drm_connector *connector) +{ + return drm_do_get_edid(connector, hdmi4_core_ddc_read, &hdmi->core); +} + +static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + return hdmi_do_read_edid(hdmi, hdmi4_bridge_read_edid, connector); +} + +static const struct drm_bridge_funcs hdmi4_bridge_funcs = { + .attach = hdmi4_bridge_attach, + .get_edid = hdmi4_bridge_get_edid, +}; + +static void hdmi4_bridge_init(struct omap_hdmi *hdmi) +{ + hdmi->bridge.funcs = &hdmi4_bridge_funcs; + hdmi->bridge.of_node = hdmi->pdev->dev.of_node; + hdmi->bridge.ops = DRM_BRIDGE_OP_EDID; + hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + + drm_bridge_add(&hdmi->bridge); +} + +static void hdmi4_bridge_cleanup(struct omap_hdmi *hdmi) +{ + drm_bridge_remove(&hdmi->bridge); +} + /* ----------------------------------------------------------------------------- * Audio Callbacks */ @@ -708,6 +768,8 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi) struct omap_dss_device *out = &hdmi->output; int r; + hdmi4_bridge_init(hdmi); + out->dev = &hdmi->pdev->dev; out->id = OMAP_DSS_OUTPUT_HDMI; out->type = OMAP_DISPLAY_TYPE_HDMI; @@ -718,9 +780,11 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi) out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; - r = omapdss_device_init_output(out, NULL); - if (r < 0) + r = omapdss_device_init_output(out, &hdmi->bridge); + if (r < 0) { + hdmi4_bridge_cleanup(hdmi); return r; + } omapdss_device_register(out); @@ -733,6 +797,8 @@ static void hdmi4_uninit_output(struct omap_hdmi *hdmi) omapdss_device_unregister(out); omapdss_device_cleanup_output(out); + + hdmi4_bridge_cleanup(hdmi); } static int hdmi4_probe_of(struct omap_hdmi *hdmi) From 6c623d6bf3e36f9ba1ac116e2ea552091af221a5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:52 +0200 Subject: [PATCH 48/78] drm/omap: hdmi5: Register a drm_bridge for EDID read In order to integrate with a chain of drm_bridge, the internal HDMI5 encoder has to expose the EDID read operation through the drm_bridge API. Register a bridge at initialisation time to do so. For the time being make the next bridge in the chain optional as the HDMI output is still based on omap_dss_device. The create_connector argument to the bridge attach function is also ignored for the same reason. This will be changed later when removing the related omapdrm-specific display drivers. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-33-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 79 ++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 2b02b0a11696..e7fe2a24a3e1 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -388,8 +388,10 @@ static void hdmi_disconnect(struct omap_dss_device *src, #define MAX_EDID 512 -static struct edid *hdmi_read_edid_data(struct hdmi_core_data *core) +static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi, + struct drm_connector *connector) { + struct hdmi_core_data *core = &hdmi->core; int max_ext_blocks = 3; int r, n, i; u8 *edid; @@ -421,9 +423,12 @@ error: return NULL; } -static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) +static struct edid * +hdmi_do_read_edid(struct omap_hdmi *hdmi, + struct edid *(*read)(struct omap_hdmi *hdmi, + struct drm_connector *connector), + struct drm_connector *connector) { - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); struct edid *edid; bool need_enable; int idlemode; @@ -447,7 +452,7 @@ static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) hdmi5_core_ddc_init(&hdmi->core); - edid = hdmi_read_edid_data(&hdmi->core); + edid = read(hdmi, connector); hdmi5_core_ddc_uninit(&hdmi->core); @@ -462,6 +467,12 @@ static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) return (struct edid *)edid; } +static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) +{ + return hdmi_do_read_edid(dssdev_to_hdmi(dssdev), hdmi_read_edid_data, + NULL); +} + static int hdmi_set_infoframe(struct omap_dss_device *dssdev, const struct hdmi_avi_infoframe *avi) { @@ -497,6 +508,56 @@ static const struct omap_dss_device_ops hdmi_ops = { }, }; +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ + +static int hdmi5_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + if (!hdmi->output.next_bridge) + return 0; + + return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge, + bridge, flags); +} + +static struct edid *hdmi5_bridge_read_edid(struct omap_hdmi *hdmi, + struct drm_connector *connector) +{ + return drm_do_get_edid(connector, hdmi5_core_ddc_read, &hdmi->core); +} + +static struct edid *hdmi5_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + return hdmi_do_read_edid(hdmi, hdmi5_bridge_read_edid, connector); +} + +static const struct drm_bridge_funcs hdmi5_bridge_funcs = { + .attach = hdmi5_bridge_attach, + .get_edid = hdmi5_bridge_get_edid, +}; + +static void hdmi5_bridge_init(struct omap_hdmi *hdmi) +{ + hdmi->bridge.funcs = &hdmi5_bridge_funcs; + hdmi->bridge.of_node = hdmi->pdev->dev.of_node; + hdmi->bridge.ops = DRM_BRIDGE_OP_EDID; + hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + + drm_bridge_add(&hdmi->bridge); +} + +static void hdmi5_bridge_cleanup(struct omap_hdmi *hdmi) +{ + drm_bridge_remove(&hdmi->bridge); +} + /* ----------------------------------------------------------------------------- * Audio Callbacks */ @@ -679,6 +740,8 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi) struct omap_dss_device *out = &hdmi->output; int r; + hdmi5_bridge_init(hdmi); + out->dev = &hdmi->pdev->dev; out->id = OMAP_DSS_OUTPUT_HDMI; out->type = OMAP_DISPLAY_TYPE_HDMI; @@ -689,9 +752,11 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi) out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; - r = omapdss_device_init_output(out, NULL); - if (r < 0) + r = omapdss_device_init_output(out, &hdmi->bridge); + if (r < 0) { + hdmi5_bridge_cleanup(hdmi); return r; + } omapdss_device_register(out); @@ -704,6 +769,8 @@ static void hdmi5_uninit_output(struct omap_hdmi *hdmi) omapdss_device_unregister(out); omapdss_device_cleanup_output(out); + + hdmi5_bridge_cleanup(hdmi); } static int hdmi5_probe_of(struct omap_hdmi *hdmi) From 3c983905d2b4b86976438f67e946437d47d873e9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:53 +0200 Subject: [PATCH 49/78] drm/omap: hdmi4: Move mode set, enable and disable operations to bridge Move the omap_dss_device .set_timings(), .enable() and .disable() operations to the drm_bridge functions. As the drm_bridge for the HDMI encoder is unconditionally registered and attached, those operations will be called at the appropriate time. The omapdss device .set_infoframe() and .set_hdmi_mode() operations have no equivalent in drm_bridge. Thir content is thus moved to the bridge .enable() operation as the data they store is not needed before the HDMI encoder gets enabled. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-34-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 206 +++++++++++++++------------- 1 file changed, 111 insertions(+), 95 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 67994287447b..a8d13a081a9a 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -28,6 +28,9 @@ #include #include +#include +#include + #include "omapdss.h" #include "hdmi4_core.h" #include "hdmi4_cec.h" @@ -237,20 +240,6 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi) hdmi_power_off_core(hdmi); } -static void hdmi_display_set_timings(struct omap_dss_device *dssdev, - const struct drm_display_mode *mode) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - mutex_lock(&hdmi->lock); - - drm_display_mode_to_videomode(mode, &hdmi->cfg.vm); - - dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000); - - mutex_unlock(&hdmi->lock); -} - static int hdmi_dump_regs(struct seq_file *s, void *p) { struct omap_hdmi *hdmi = s->private; @@ -284,62 +273,6 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd) hdmi_wp_audio_enable(&hd->wp, false); } -static void hdmi_display_enable(struct omap_dss_device *dssdev) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - unsigned long flags; - int r; - - DSSDBG("ENTER hdmi_display_enable\n"); - - mutex_lock(&hdmi->lock); - - r = hdmi_power_on_full(hdmi); - if (r) { - DSSERR("failed to power on device\n"); - goto done; - } - - if (hdmi->audio_configured) { - r = hdmi4_audio_config(&hdmi->core, &hdmi->wp, - &hdmi->audio_config, - hdmi->cfg.vm.pixelclock); - if (r) { - DSSERR("Error restoring audio configuration: %d", r); - hdmi->audio_abort_cb(&hdmi->pdev->dev); - hdmi->audio_configured = false; - } - } - - spin_lock_irqsave(&hdmi->audio_playing_lock, flags); - if (hdmi->audio_configured && hdmi->audio_playing) - hdmi_start_audio_stream(hdmi); - hdmi->display_enabled = true; - spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); - -done: - mutex_unlock(&hdmi->lock); -} - -static void hdmi_display_disable(struct omap_dss_device *dssdev) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - unsigned long flags; - - DSSDBG("Enter hdmi_display_disable\n"); - - mutex_lock(&hdmi->lock); - - spin_lock_irqsave(&hdmi->audio_playing_lock, flags); - hdmi_stop_audio_stream(hdmi); - hdmi->display_enabled = false; - spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); - - hdmi_power_off_full(hdmi); - - mutex_unlock(&hdmi->lock); -} - int hdmi4_core_enable(struct hdmi_core_data *core) { struct omap_hdmi *hdmi = container_of(core, struct omap_hdmi, core); @@ -491,39 +424,14 @@ static void hdmi_lost_hotplug(struct omap_dss_device *dssdev) hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); } -static int hdmi_set_infoframe(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - hdmi->cfg.infoframe = *avi; - return 0; -} - -static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, - bool hdmi_mode) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; - return 0; -} - static const struct omap_dss_device_ops hdmi_ops = { .connect = hdmi_connect, .disconnect = hdmi_disconnect, - .enable = hdmi_display_enable, - .disable = hdmi_display_disable, - - .set_timings = hdmi_display_set_timings, - .read_edid = hdmi_read_edid, .hdmi = { .lost_hotplug = hdmi_lost_hotplug, - .set_infoframe = hdmi_set_infoframe, - .set_hdmi_mode = hdmi_set_hdmi_mode, }, }; @@ -543,6 +451,108 @@ static int hdmi4_bridge_attach(struct drm_bridge *bridge, bridge, flags); } +static void hdmi4_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + mutex_lock(&hdmi->lock); + + drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm); + + dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000); + + mutex_unlock(&hdmi->lock); +} + +static void hdmi4_bridge_enable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct drm_atomic_state *state = bridge_state->base.state; + struct drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_crtc_state *crtc_state; + unsigned long flags; + int ret; + + /* + * None of these should fail, as the bridge can't be enabled without a + * valid CRTC to connector path with fully populated new states. + */ + connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!connector)) + return; + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi + ? HDMI_HDMI : HDMI_DVI; + + if (connector->display_info.is_hdmi) { + const struct drm_display_mode *mode; + struct hdmi_avi_infoframe avi; + + mode = &crtc_state->adjusted_mode; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector, + mode); + if (ret == 0) + hdmi->cfg.infoframe = avi; + } + + mutex_lock(&hdmi->lock); + + ret = hdmi_power_on_full(hdmi); + if (ret) { + DSSERR("failed to power on device\n"); + goto done; + } + + if (hdmi->audio_configured) { + ret = hdmi4_audio_config(&hdmi->core, &hdmi->wp, + &hdmi->audio_config, + hdmi->cfg.vm.pixelclock); + if (ret) { + DSSERR("Error restoring audio configuration: %d", ret); + hdmi->audio_abort_cb(&hdmi->pdev->dev); + hdmi->audio_configured = false; + } + } + + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + if (hdmi->audio_configured && hdmi->audio_playing) + hdmi_start_audio_stream(hdmi); + hdmi->display_enabled = true; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); + +done: + mutex_unlock(&hdmi->lock); +} + +static void hdmi4_bridge_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + unsigned long flags; + + mutex_lock(&hdmi->lock); + + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + hdmi_stop_audio_stream(hdmi); + hdmi->display_enabled = false; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); + + hdmi_power_off_full(hdmi); + + mutex_unlock(&hdmi->lock); +} + static struct edid *hdmi4_bridge_read_edid(struct omap_hdmi *hdmi, struct drm_connector *connector) { @@ -559,6 +569,12 @@ static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge, static const struct drm_bridge_funcs hdmi4_bridge_funcs = { .attach = hdmi4_bridge_attach, + .mode_set = hdmi4_bridge_mode_set, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_enable = hdmi4_bridge_enable, + .atomic_disable = hdmi4_bridge_disable, .get_edid = hdmi4_bridge_get_edid, }; From 615de6ceb6b6a227a242059939b2ed54e54b4db8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:54 +0200 Subject: [PATCH 50/78] drm/omap: hdmi5: Move mode set, enable and disable operations to bridge Move the omap_dss_device .set_timings(), .enable() and .disable() operations to the drm_bridge functions. As the drm_bridge for the HDMI encoder is unconditionally registered and attached, those operations will be called at the appropriate time. The omapdss device .set_infoframe() and .set_hdmi_mode() operations have no equivalent in drm_bridge. Thir content is thus moved to the bridge .enable() operation as the data they store is not needed before the HDMI encoder gets enabled. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-35-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 209 +++++++++++++++------------- 1 file changed, 111 insertions(+), 98 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index e7fe2a24a3e1..52184797c858 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -31,6 +31,9 @@ #include #include +#include +#include + #include "omapdss.h" #include "hdmi5_core.h" #include "dss.h" @@ -236,20 +239,6 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi) hdmi_power_off_core(hdmi); } -static void hdmi_display_set_timings(struct omap_dss_device *dssdev, - const struct drm_display_mode *mode) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - mutex_lock(&hdmi->lock); - - drm_display_mode_to_videomode(mode, &hdmi->cfg.vm); - - dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000); - - mutex_unlock(&hdmi->lock); -} - static int hdmi_dump_regs(struct seq_file *s, void *p) { struct omap_hdmi *hdmi = s->private; @@ -285,62 +274,6 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd) REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2); } -static void hdmi_display_enable(struct omap_dss_device *dssdev) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - unsigned long flags; - int r; - - DSSDBG("ENTER hdmi_display_enable\n"); - - mutex_lock(&hdmi->lock); - - r = hdmi_power_on_full(hdmi); - if (r) { - DSSERR("failed to power on device\n"); - goto done; - } - - if (hdmi->audio_configured) { - r = hdmi5_audio_config(&hdmi->core, &hdmi->wp, - &hdmi->audio_config, - hdmi->cfg.vm.pixelclock); - if (r) { - DSSERR("Error restoring audio configuration: %d", r); - hdmi->audio_abort_cb(&hdmi->pdev->dev); - hdmi->audio_configured = false; - } - } - - spin_lock_irqsave(&hdmi->audio_playing_lock, flags); - if (hdmi->audio_configured && hdmi->audio_playing) - hdmi_start_audio_stream(hdmi); - hdmi->display_enabled = true; - spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); - -done: - mutex_unlock(&hdmi->lock); -} - -static void hdmi_display_disable(struct omap_dss_device *dssdev) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - unsigned long flags; - - DSSDBG("Enter hdmi_display_disable\n"); - - mutex_lock(&hdmi->lock); - - spin_lock_irqsave(&hdmi->audio_playing_lock, flags); - hdmi_stop_audio_stream(hdmi); - hdmi->display_enabled = false; - spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); - - hdmi_power_off_full(hdmi); - - mutex_unlock(&hdmi->lock); -} - static int hdmi_core_enable(struct omap_hdmi *hdmi) { int r = 0; @@ -473,39 +406,11 @@ static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) NULL); } -static int hdmi_set_infoframe(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - hdmi->cfg.infoframe = *avi; - return 0; -} - -static int hdmi_set_hdmi_mode(struct omap_dss_device *dssdev, - bool hdmi_mode) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - hdmi->cfg.hdmi_dvi_mode = hdmi_mode ? HDMI_HDMI : HDMI_DVI; - return 0; -} - static const struct omap_dss_device_ops hdmi_ops = { .connect = hdmi_connect, .disconnect = hdmi_disconnect, - .enable = hdmi_display_enable, - .disable = hdmi_display_disable, - - .set_timings = hdmi_display_set_timings, - .read_edid = hdmi_read_edid, - - .hdmi = { - .set_infoframe = hdmi_set_infoframe, - .set_hdmi_mode = hdmi_set_hdmi_mode, - }, }; /* ----------------------------------------------------------------------------- @@ -524,6 +429,108 @@ static int hdmi5_bridge_attach(struct drm_bridge *bridge, bridge, flags); } +static void hdmi5_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + mutex_lock(&hdmi->lock); + + drm_display_mode_to_videomode(adjusted_mode, &hdmi->cfg.vm); + + dispc_set_tv_pclk(hdmi->dss->dispc, adjusted_mode->clock * 1000); + + mutex_unlock(&hdmi->lock); +} + +static void hdmi5_bridge_enable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct drm_atomic_state *state = bridge_state->base.state; + struct drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_crtc_state *crtc_state; + unsigned long flags; + int ret; + + /* + * None of these should fail, as the bridge can't be enabled without a + * valid CRTC to connector path with fully populated new states. + */ + connector = drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!connector)) + return; + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + hdmi->cfg.hdmi_dvi_mode = connector->display_info.is_hdmi + ? HDMI_HDMI : HDMI_DVI; + + if (connector->display_info.is_hdmi) { + const struct drm_display_mode *mode; + struct hdmi_avi_infoframe avi; + + mode = &crtc_state->adjusted_mode; + ret = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector, + mode); + if (ret == 0) + hdmi->cfg.infoframe = avi; + } + + mutex_lock(&hdmi->lock); + + ret = hdmi_power_on_full(hdmi); + if (ret) { + DSSERR("failed to power on device\n"); + goto done; + } + + if (hdmi->audio_configured) { + ret = hdmi5_audio_config(&hdmi->core, &hdmi->wp, + &hdmi->audio_config, + hdmi->cfg.vm.pixelclock); + if (ret) { + DSSERR("Error restoring audio configuration: %d", ret); + hdmi->audio_abort_cb(&hdmi->pdev->dev); + hdmi->audio_configured = false; + } + } + + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + if (hdmi->audio_configured && hdmi->audio_playing) + hdmi_start_audio_stream(hdmi); + hdmi->display_enabled = true; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); + +done: + mutex_unlock(&hdmi->lock); +} + +static void hdmi5_bridge_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + unsigned long flags; + + mutex_lock(&hdmi->lock); + + spin_lock_irqsave(&hdmi->audio_playing_lock, flags); + hdmi_stop_audio_stream(hdmi); + hdmi->display_enabled = false; + spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags); + + hdmi_power_off_full(hdmi); + + mutex_unlock(&hdmi->lock); +} + static struct edid *hdmi5_bridge_read_edid(struct omap_hdmi *hdmi, struct drm_connector *connector) { @@ -540,6 +547,12 @@ static struct edid *hdmi5_bridge_get_edid(struct drm_bridge *bridge, static const struct drm_bridge_funcs hdmi5_bridge_funcs = { .attach = hdmi5_bridge_attach, + .mode_set = hdmi5_bridge_mode_set, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_enable = hdmi5_bridge_enable, + .atomic_disable = hdmi5_bridge_disable, .get_edid = hdmi5_bridge_get_edid, }; From 6886b346452ff379888d72faec60fb24622faeb6 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:55 +0200 Subject: [PATCH 51/78] drm/omap: hdmi4: Implement drm_bridge .hpd_notify() operation The HDMI4 encoder is transitioning to the drm_bridge API, implement the last missing operation. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-36-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index a8d13a081a9a..73f1fab346e9 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -553,6 +553,15 @@ static void hdmi4_bridge_disable(struct drm_bridge *bridge, mutex_unlock(&hdmi->lock); } +static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge, + enum drm_connector_status status) +{ + struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + + if (status == connector_status_disconnected) + hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); +} + static struct edid *hdmi4_bridge_read_edid(struct omap_hdmi *hdmi, struct drm_connector *connector) { @@ -575,6 +584,7 @@ static const struct drm_bridge_funcs hdmi4_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset, .atomic_enable = hdmi4_bridge_enable, .atomic_disable = hdmi4_bridge_disable, + .hpd_notify = hdmi4_bridge_hpd_notify, .get_edid = hdmi4_bridge_get_edid, }; From 4fcbfbae7222eb399caa2cc04082fd07c6bd164b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:56 +0200 Subject: [PATCH 52/78] drm/omap: dss: Remove .set_hdmi_mode() and .set_infoframe() operations The omapdss_hdmi_ops .set_hdmi_mode() and .set_infoframe() operations operations are not used anymore, remove them. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-37-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/omapdss.h | 3 --- drivers/gpu/drm/omapdrm/omap_encoder.c | 26 -------------------------- 2 files changed, 29 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 269e143d57be..30a12cf91cbb 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -287,9 +287,6 @@ struct omap_dss_writeback_info { struct omapdss_hdmi_ops { void (*lost_hotplug)(struct omap_dss_device *dssdev); - int (*set_hdmi_mode)(struct omap_dss_device *dssdev, bool hdmi_mode); - int (*set_infoframe)(struct omap_dss_device *dssdev, - const struct hdmi_avi_infoframe *avi); }; struct omapdss_dsi_ops { diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index b232acd3bc3d..18a79dde6815 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -69,28 +69,6 @@ static void omap_encoder_update_videomode_flags(struct videomode *vm, } } -static void omap_encoder_hdmi_mode_set(struct drm_connector *connector, - struct drm_encoder *encoder, - struct drm_display_mode *adjusted_mode) -{ - struct omap_encoder *omap_encoder = to_omap_encoder(encoder); - struct omap_dss_device *dssdev = omap_encoder->output; - bool hdmi_mode = connector->display_info.is_hdmi; - - if (dssdev->ops && dssdev->ops->hdmi.set_hdmi_mode) - dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode); - - if (hdmi_mode && dssdev->ops && dssdev->ops->hdmi.set_infoframe) { - struct hdmi_avi_infoframe avi; - int r; - - r = drm_hdmi_avi_infoframe_from_display_mode(&avi, connector, - adjusted_mode); - if (r == 0) - dssdev->ops->hdmi.set_infoframe(dssdev, &avi); - } -} - static void omap_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -142,10 +120,6 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder, if (dssdev->ops && dssdev->ops->set_timings) dssdev->ops->set_timings(dssdev, adjusted_mode); } - - /* Set the HDMI mode and HDMI infoframe if applicable. */ - if (output->type == OMAP_DISPLAY_TYPE_HDMI) - omap_encoder_hdmi_mode_set(connector, encoder, adjusted_mode); } static void omap_encoder_disable(struct drm_encoder *encoder) From 2f004792adadcf017fde50339b432a26039fff0c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:57 +0200 Subject: [PATCH 53/78] drm/omap: venc: Register a drm_bridge In order to integrate with a chain of drm_bridge, the internal VENC encoder has to expose the mode valid, fixup and set, the enable and disable and the get modes operations through the drm_bridge API. Register a bridge at initialisation time to do so. Most of those operations are removed from the omap_dss_device as they are now called through the drm_bridge API by the DRM atomic helpers. The only exception is the .get_modes() operation that is still invoked through the omap_dss_device-based pipeline. For the time being make the next bridge in the chain optional as the VENC output is still based on omap_dss_device. The create_connector argument to the bridge attach function is also ignored for the same reason. This will be changed later when removing the related omapdrm-specific display drivers. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-38-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/venc.c | 244 ++++++++++++++++++----------- 1 file changed, 155 insertions(+), 89 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index 977d8d525b43..cb9a689ed612 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -26,6 +25,8 @@ #include #include +#include + #include "omapdss.h" #include "dss.h" @@ -289,7 +290,6 @@ static const struct drm_display_mode omap_dss_ntsc_mode = { struct venc_device { struct platform_device *pdev; void __iomem *base; - struct mutex venc_lock; struct regulator *vdda_dac_reg; struct dss_device *dss; @@ -303,9 +303,11 @@ struct venc_device { bool requires_tv_dac_clk; struct omap_dss_device output; + struct drm_bridge bridge; }; #define dssdev_to_venc(dssdev) container_of(dssdev, struct venc_device, output) +#define drm_bridge_to_venc(b) container_of(b, struct venc_device, bridge) static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val) { @@ -477,32 +479,6 @@ static void venc_power_off(struct venc_device *venc) venc_runtime_put(venc); } -static void venc_display_enable(struct omap_dss_device *dssdev) -{ - struct venc_device *venc = dssdev_to_venc(dssdev); - - DSSDBG("venc_display_enable\n"); - - mutex_lock(&venc->venc_lock); - - venc_power_on(venc); - - mutex_unlock(&venc->venc_lock); -} - -static void venc_display_disable(struct omap_dss_device *dssdev) -{ - struct venc_device *venc = dssdev_to_venc(dssdev); - - DSSDBG("venc_display_disable\n"); - - mutex_lock(&venc->venc_lock); - - venc_power_off(venc); - - mutex_unlock(&venc->venc_lock); -} - static int venc_get_modes(struct omap_dss_device *dssdev, struct drm_connector *connector) { @@ -545,57 +521,6 @@ static enum venc_videomode venc_get_videomode(const struct drm_display_mode *mod return VENC_MODE_UNKNOWN; } -static void venc_set_timings(struct omap_dss_device *dssdev, - const struct drm_display_mode *mode) -{ - struct venc_device *venc = dssdev_to_venc(dssdev); - enum venc_videomode venc_mode = venc_get_videomode(mode); - - DSSDBG("venc_set_timings\n"); - - mutex_lock(&venc->venc_lock); - - switch (venc_mode) { - default: - WARN_ON_ONCE(1); - /* Fall-through */ - case VENC_MODE_PAL: - venc->config = &venc_config_pal_trm; - break; - - case VENC_MODE_NTSC: - venc->config = &venc_config_ntsc_trm; - break; - } - - dispc_set_tv_pclk(venc->dss->dispc, 13500000); - - mutex_unlock(&venc->venc_lock); -} - -static int venc_check_timings(struct omap_dss_device *dssdev, - struct drm_display_mode *mode) -{ - DSSDBG("venc_check_timings\n"); - - switch (venc_get_videomode(mode)) { - case VENC_MODE_PAL: - drm_mode_copy(mode, &omap_dss_pal_mode); - break; - - case VENC_MODE_NTSC: - drm_mode_copy(mode, &omap_dss_ntsc_mode); - break; - - default: - return -EINVAL; - } - - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); - drm_mode_set_name(mode); - return 0; -} - static int venc_dump_regs(struct seq_file *s, void *p) { struct venc_device *venc = s->private; @@ -689,15 +614,152 @@ static const struct omap_dss_device_ops venc_ops = { .connect = venc_connect, .disconnect = venc_disconnect, - .enable = venc_display_enable, - .disable = venc_display_disable, - - .check_timings = venc_check_timings, - .set_timings = venc_set_timings, - .get_modes = venc_get_modes, }; +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ + +static int venc_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct venc_device *venc = drm_bridge_to_venc(bridge); + + if (venc->output.next_bridge) + return 0; + + return drm_bridge_attach(bridge->encoder, venc->output.next_bridge, + bridge, flags); +} + +static enum drm_mode_status +venc_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) +{ + switch (venc_get_videomode(mode)) { + case VENC_MODE_PAL: + case VENC_MODE_NTSC: + return MODE_OK; + + default: + return MODE_BAD; + } +} + +static bool venc_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + const struct drm_display_mode *venc_mode; + + switch (venc_get_videomode(adjusted_mode)) { + case VENC_MODE_PAL: + venc_mode = &omap_dss_pal_mode; + break; + + case VENC_MODE_NTSC: + venc_mode = &omap_dss_ntsc_mode; + break; + + default: + return false; + } + + drm_mode_copy(adjusted_mode, venc_mode); + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); + drm_mode_set_name(adjusted_mode); + + return true; +} + +static void venc_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct venc_device *venc = drm_bridge_to_venc(bridge); + enum venc_videomode venc_mode = venc_get_videomode(adjusted_mode); + + switch (venc_mode) { + default: + WARN_ON_ONCE(1); + /* Fall-through */ + case VENC_MODE_PAL: + venc->config = &venc_config_pal_trm; + break; + + case VENC_MODE_NTSC: + venc->config = &venc_config_ntsc_trm; + break; + } + + dispc_set_tv_pclk(venc->dss->dispc, 13500000); +} + +static void venc_bridge_enable(struct drm_bridge *bridge) +{ + struct venc_device *venc = drm_bridge_to_venc(bridge); + + venc_power_on(venc); +} + +static void venc_bridge_disable(struct drm_bridge *bridge) +{ + struct venc_device *venc = drm_bridge_to_venc(bridge); + + venc_power_off(venc); +} + +static int venc_bridge_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + static const struct drm_display_mode *modes[] = { + &omap_dss_pal_mode, + &omap_dss_ntsc_mode, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(modes); ++i) { + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, modes[i]); + if (!mode) + return i; + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + } + + return ARRAY_SIZE(modes); +} + +static const struct drm_bridge_funcs venc_bridge_funcs = { + .attach = venc_bridge_attach, + .mode_valid = venc_bridge_mode_valid, + .mode_fixup = venc_bridge_mode_fixup, + .mode_set = venc_bridge_mode_set, + .enable = venc_bridge_enable, + .disable = venc_bridge_disable, + .get_modes = venc_bridge_get_modes, +}; + +static void venc_bridge_init(struct venc_device *venc) +{ + venc->bridge.funcs = &venc_bridge_funcs; + venc->bridge.of_node = venc->pdev->dev.of_node; + venc->bridge.ops = DRM_BRIDGE_OP_MODES; + venc->bridge.type = DRM_MODE_CONNECTOR_SVIDEO; + venc->bridge.interlace_allowed = true; + + drm_bridge_add(&venc->bridge); +} + +static void venc_bridge_cleanup(struct venc_device *venc) +{ + drm_bridge_remove(&venc->bridge); +} + /* ----------------------------------------------------------------------------- * Component Bind & Unbind */ @@ -747,6 +809,8 @@ static int venc_init_output(struct venc_device *venc) struct omap_dss_device *out = &venc->output; int r; + venc_bridge_init(venc); + out->dev = &venc->pdev->dev; out->id = OMAP_DSS_OUTPUT_VENC; out->type = OMAP_DISPLAY_TYPE_VENC; @@ -757,9 +821,11 @@ static int venc_init_output(struct venc_device *venc) out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_MODES; - r = omapdss_device_init_output(out, NULL); - if (r < 0) + r = omapdss_device_init_output(out, &venc->bridge); + if (r < 0) { + venc_bridge_cleanup(venc); return r; + } omapdss_device_register(out); @@ -770,6 +836,8 @@ static void venc_uninit_output(struct venc_device *venc) { omapdss_device_unregister(&venc->output); omapdss_device_cleanup_output(&venc->output); + + venc_bridge_cleanup(venc); } static int venc_probe_of(struct venc_device *venc) @@ -839,8 +907,6 @@ static int venc_probe(struct platform_device *pdev) if (soc_device_match(venc_soc_devices)) venc->requires_tv_dac_clk = true; - mutex_init(&venc->venc_lock); - venc->config = &venc_config_pal_trm; venc_mem = platform_get_resource(venc->pdev, IORESOURCE_MEM, 0); From f40f4e45df12fe51727db4a42340214c0244bc9d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:58 +0200 Subject: [PATCH 54/78] drm/omap: Create connector for bridges Use the drm_bridge_connector helper to create a connector for pipelines that use drm_bridge. This allows splitting connector operations across multiple bridges when necessary, instead of having the last bridge in the chain creating the connector and handling all connector operations internally. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-39-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_drv.c | 74 +++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 1df509342b5d..47afa37055b3 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -12,10 +12,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -291,9 +293,14 @@ static int omap_modeset_init(struct drm_device *dev) if (pipe->output->bridge) { ret = drm_bridge_attach(pipe->encoder, - pipe->output->bridge, NULL, 0); - if (ret < 0) + pipe->output->bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret < 0) { + dev_err(priv->dev, + "unable to attach bridge %pOF\n", + pipe->output->bridge->of_node); return ret; + } } id = omap_display_id(pipe->output); @@ -329,8 +336,23 @@ static int omap_modeset_init(struct drm_device *dev) encoder); if (!pipe->connector) return -ENOMEM; + } else { + pipe->connector = drm_bridge_connector_init(dev, encoder); + if (IS_ERR(pipe->connector)) { + dev_err(priv->dev, + "unable to create bridge connector for %s\n", + pipe->output->name); + return PTR_ERR(pipe->connector); + } + } - drm_connector_attach_encoder(pipe->connector, encoder); + drm_connector_attach_encoder(pipe->connector, encoder); + + if (pipe->output->panel) { + ret = drm_panel_attach(pipe->output->panel, + pipe->connector); + if (ret < 0) + return ret; } crtc = omap_crtc_init(dev, pipe, priv->planes[i]); @@ -369,6 +391,23 @@ static int omap_modeset_init(struct drm_device *dev) return 0; } +static void omap_modeset_fini(struct drm_device *ddev) +{ + struct omap_drm_private *priv = ddev->dev_private; + unsigned int i; + + omap_drm_irq_uninstall(ddev); + + for (i = 0; i < priv->num_pipes; i++) { + struct omap_drm_pipeline *pipe = &priv->pipes[i]; + + if (pipe->output->panel) + drm_panel_detach(pipe->output->panel); + } + + drm_mode_config_cleanup(ddev); +} + /* * Enable the HPD in external components if supported */ @@ -378,8 +417,15 @@ static void omap_modeset_enable_external_hpd(struct drm_device *ddev) unsigned int i; for (i = 0; i < priv->num_pipes; i++) { - if (priv->pipes[i].connector) - omap_connector_enable_hpd(priv->pipes[i].connector); + struct drm_connector *connector = priv->pipes[i].connector; + + if (!connector) + continue; + + if (priv->pipes[i].output->next) + omap_connector_enable_hpd(connector); + else + drm_bridge_connector_enable_hpd(connector); } } @@ -392,8 +438,15 @@ static void omap_modeset_disable_external_hpd(struct drm_device *ddev) unsigned int i; for (i = 0; i < priv->num_pipes; i++) { - if (priv->pipes[i].connector) - omap_connector_disable_hpd(priv->pipes[i].connector); + struct drm_connector *connector = priv->pipes[i].connector; + + if (!connector) + continue; + + if (priv->pipes[i].output->next) + omap_connector_disable_hpd(connector); + else + drm_bridge_connector_disable_hpd(connector); } } @@ -616,8 +669,7 @@ err_cleanup_helpers: omap_fbdev_fini(ddev); err_cleanup_modeset: - drm_mode_config_cleanup(ddev); - omap_drm_irq_uninstall(ddev); + omap_modeset_fini(ddev); err_gem_deinit: omap_gem_deinit(ddev); destroy_workqueue(priv->wq); @@ -642,9 +694,7 @@ static void omapdrm_cleanup(struct omap_drm_private *priv) drm_atomic_helper_shutdown(ddev); - drm_mode_config_cleanup(ddev); - - omap_drm_irq_uninstall(ddev); + omap_modeset_fini(ddev); omap_gem_deinit(ddev); destroy_workqueue(priv->wq); From e7e67d9a2f1dd2f938adcc219b3769f5cc3f0df7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:24:59 +0200 Subject: [PATCH 55/78] drm/omap: Switch the HDMI and VENC outputs to drm_bridge The TPD12S015, OPA362 and analog and HDMI connectors are now supported by DRM bridge drivers, and the omapdrm HDMI and VENC outputs can be handled through the drm_bridge API. Switch the outputs to drm_bridge by making the next bridge mandatory and removing the related omapdrm-specific display drivers. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-40-laurent.pinchart@ideasonboard.com --- arch/arm/configs/omap2plus_defconfig | 7 +- drivers/gpu/drm/omapdrm/displays/Kconfig | 22 -- drivers/gpu/drm/omapdrm/displays/Makefile | 4 - .../omapdrm/displays/connector-analog-tv.c | 97 -------- .../gpu/drm/omapdrm/displays/connector-hdmi.c | 183 --------------- .../gpu/drm/omapdrm/displays/encoder-opa362.c | 137 ----------- .../drm/omapdrm/displays/encoder-tpd12s015.c | 217 ------------------ drivers/gpu/drm/omapdrm/dss/hdmi4.c | 4 +- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 4 +- .../gpu/drm/omapdrm/dss/omapdss-boot-init.c | 5 - drivers/gpu/drm/omapdrm/dss/output.c | 5 + drivers/gpu/drm/omapdrm/dss/venc.c | 4 +- 12 files changed, 14 insertions(+), 675 deletions(-) delete mode 100644 drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c delete mode 100644 drivers/gpu/drm/omapdrm/displays/connector-hdmi.c delete mode 100644 drivers/gpu/drm/omapdrm/displays/encoder-opa362.c delete mode 100644 drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index c32c338f7704..b9698e217aa7 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -350,14 +350,13 @@ CONFIG_DRM_OMAP=m CONFIG_OMAP5_DSS_HDMI=y CONFIG_OMAP2_DSS_SDI=y CONFIG_OMAP2_DSS_DSI=y -CONFIG_DRM_OMAP_ENCODER_OPA362=m -CONFIG_DRM_OMAP_ENCODER_TPD12S015=m -CONFIG_DRM_OMAP_CONNECTOR_HDMI=m -CONFIG_DRM_OMAP_CONNECTOR_ANALOG_TV=m CONFIG_DRM_OMAP_PANEL_DSI_CM=m CONFIG_DRM_TILCDC=m CONFIG_DRM_PANEL_SIMPLE=m +CONFIG_DRM_DISPLAY_CONNECTOR=m +CONFIG_DRM_SIMPLE_BRIDGE=m CONFIG_DRM_TI_TFP410=m +CONFIG_DRM_TI_TPD12S015=m CONFIG_DRM_PANEL_LG_LB035Q02=m CONFIG_DRM_PANEL_NEC_NL8048HL11=m CONFIG_DRM_PANEL_SHARP_LS037V7DW01=m diff --git a/drivers/gpu/drm/omapdrm/displays/Kconfig b/drivers/gpu/drm/omapdrm/displays/Kconfig index b562a8cd61bf..f2be594c7eff 100644 --- a/drivers/gpu/drm/omapdrm/displays/Kconfig +++ b/drivers/gpu/drm/omapdrm/displays/Kconfig @@ -1,28 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "OMAPDRM External Display Device Drivers" -config DRM_OMAP_ENCODER_OPA362 - tristate "OPA362 external analog amplifier" - help - Driver for OPA362 external analog TV amplifier controlled - through a GPIO. - -config DRM_OMAP_ENCODER_TPD12S015 - tristate "TPD12S015 HDMI ESD protection and level shifter" - help - Driver for TPD12S015, which offers HDMI ESD protection and level - shifting. - -config DRM_OMAP_CONNECTOR_HDMI - tristate "HDMI Connector" - help - Driver for a generic HDMI connector. - -config DRM_OMAP_CONNECTOR_ANALOG_TV - tristate "Analog TV Connector" - help - Driver for a generic analog TV connector. - config DRM_OMAP_PANEL_DSI_CM tristate "Generic DSI Command Mode Panel" depends on BACKLIGHT_CLASS_DEVICE diff --git a/drivers/gpu/drm/omapdrm/displays/Makefile b/drivers/gpu/drm/omapdrm/displays/Makefile index cb76859dc574..488ddf153613 100644 --- a/drivers/gpu/drm/omapdrm/displays/Makefile +++ b/drivers/gpu/drm/omapdrm/displays/Makefile @@ -1,6 +1,2 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_DRM_OMAP_ENCODER_OPA362) += encoder-opa362.o -obj-$(CONFIG_DRM_OMAP_ENCODER_TPD12S015) += encoder-tpd12s015.o -obj-$(CONFIG_DRM_OMAP_CONNECTOR_HDMI) += connector-hdmi.o -obj-$(CONFIG_DRM_OMAP_CONNECTOR_ANALOG_TV) += connector-analog-tv.o obj-$(CONFIG_DRM_OMAP_PANEL_DSI_CM) += panel-dsi-cm.o diff --git a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c deleted file mode 100644 index f36aa1885d39..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Analog TV Connector driver - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ - * Author: Tomi Valkeinen - */ - -#include -#include -#include -#include - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - - struct device *dev; -}; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int tvc_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - return 0; -} - -static void tvc_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ -} - -static const struct omap_dss_device_ops tvc_ops = { - .connect = tvc_connect, - .disconnect = tvc_disconnect, -}; - -static int tvc_probe(struct platform_device *pdev) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - ddata->dev = &pdev->dev; - - dssdev = &ddata->dssdev; - dssdev->ops = &tvc_ops; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_VENC; - dssdev->display = true; - dssdev->owner = THIS_MODULE; - dssdev->of_port = 0; - - omapdss_display_init(dssdev); - omapdss_device_register(dssdev); - - return 0; -} - -static int __exit tvc_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - - omapdss_device_unregister(&ddata->dssdev); - - return 0; -} - -static const struct of_device_id tvc_of_match[] = { - { .compatible = "omapdss,svideo-connector", }, - { .compatible = "omapdss,composite-video-connector", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, tvc_of_match); - -static struct platform_driver tvc_connector_driver = { - .probe = tvc_probe, - .remove = __exit_p(tvc_remove), - .driver = { - .name = "connector-analog-tv", - .of_match_table = tvc_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(tvc_connector_driver); - -MODULE_AUTHOR("Tomi Valkeinen "); -MODULE_DESCRIPTION("Analog TV Connector driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c deleted file mode 100644 index 37c212491cd3..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * HDMI Connector driver - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ - * Author: Tomi Valkeinen - */ - -#include -#include -#include -#include -#include - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - void (*hpd_cb)(void *cb_data, enum drm_connector_status status); - void *hpd_cb_data; - struct mutex hpd_lock; - - struct device *dev; - - struct gpio_desc *hpd_gpio; -}; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int hdmic_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - return 0; -} - -static void hdmic_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ -} - -static bool hdmic_detect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - return gpiod_get_value_cansleep(ddata->hpd_gpio); -} - -static void hdmic_register_hpd_cb(struct omap_dss_device *dssdev, - void (*cb)(void *cb_data, - enum drm_connector_status status), - void *cb_data) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - mutex_lock(&ddata->hpd_lock); - ddata->hpd_cb = cb; - ddata->hpd_cb_data = cb_data; - mutex_unlock(&ddata->hpd_lock); -} - -static void hdmic_unregister_hpd_cb(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - mutex_lock(&ddata->hpd_lock); - ddata->hpd_cb = NULL; - ddata->hpd_cb_data = NULL; - mutex_unlock(&ddata->hpd_lock); -} - -static const struct omap_dss_device_ops hdmic_ops = { - .connect = hdmic_connect, - .disconnect = hdmic_disconnect, - - .detect = hdmic_detect, - .register_hpd_cb = hdmic_register_hpd_cb, - .unregister_hpd_cb = hdmic_unregister_hpd_cb, -}; - -static irqreturn_t hdmic_hpd_isr(int irq, void *data) -{ - struct panel_drv_data *ddata = data; - - mutex_lock(&ddata->hpd_lock); - if (ddata->hpd_cb) { - enum drm_connector_status status; - - if (hdmic_detect(&ddata->dssdev)) - status = connector_status_connected; - else - status = connector_status_disconnected; - - ddata->hpd_cb(ddata->hpd_cb_data, status); - } - mutex_unlock(&ddata->hpd_lock); - - return IRQ_HANDLED; -} - -static int hdmic_probe(struct platform_device *pdev) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - struct gpio_desc *gpio; - int r; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - ddata->dev = &pdev->dev; - - mutex_init(&ddata->hpd_lock); - - /* HPD GPIO */ - gpio = devm_gpiod_get_optional(&pdev->dev, "hpd", GPIOD_IN); - if (IS_ERR(gpio)) { - dev_err(&pdev->dev, "failed to parse HPD gpio\n"); - return PTR_ERR(gpio); - } - - ddata->hpd_gpio = gpio; - - if (ddata->hpd_gpio) { - r = devm_request_threaded_irq(&pdev->dev, - gpiod_to_irq(ddata->hpd_gpio), - NULL, hdmic_hpd_isr, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - "hdmic hpd", ddata); - if (r) - return r; - } - - dssdev = &ddata->dssdev; - dssdev->ops = &hdmic_ops; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_HDMI; - dssdev->display = true; - dssdev->owner = THIS_MODULE; - dssdev->of_port = 0; - dssdev->ops_flags = ddata->hpd_gpio - ? OMAP_DSS_DEVICE_OP_DETECT | OMAP_DSS_DEVICE_OP_HPD - : 0; - - omapdss_display_init(dssdev); - omapdss_device_register(dssdev); - - return 0; -} - -static int __exit hdmic_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - - omapdss_device_unregister(&ddata->dssdev); - - return 0; -} - -static const struct of_device_id hdmic_of_match[] = { - { .compatible = "omapdss,hdmi-connector", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, hdmic_of_match); - -static struct platform_driver hdmi_connector_driver = { - .probe = hdmic_probe, - .remove = __exit_p(hdmic_remove), - .driver = { - .name = "connector-hdmi", - .of_match_table = hdmic_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(hdmi_connector_driver); - -MODULE_AUTHOR("Tomi Valkeinen "); -MODULE_DESCRIPTION("HDMI Connector driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c deleted file mode 100644 index 252705222ef1..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * OPA362 analog video amplifier with output/power control - * - * Copyright (C) 2014 Golden Delicious Computers - * Author: H. Nikolaus Schaller - * - * based on encoder-tfp410 - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ - * Author: Tomi Valkeinen - */ - -#include -#include -#include -#include - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - - struct gpio_desc *enable_gpio; -}; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int opa362_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - return omapdss_device_connect(dst->dss, dst, dst->next); -} - -static void opa362_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - omapdss_device_disconnect(dst, dst->next); -} - -static void opa362_enable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - if (ddata->enable_gpio) - gpiod_set_value_cansleep(ddata->enable_gpio, 1); -} - -static void opa362_disable(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - if (ddata->enable_gpio) - gpiod_set_value_cansleep(ddata->enable_gpio, 0); -} - -static const struct omap_dss_device_ops opa362_ops = { - .connect = opa362_connect, - .disconnect = opa362_disconnect, - .enable = opa362_enable, - .disable = opa362_disable, -}; - -static int opa362_probe(struct platform_device *pdev) -{ - struct panel_drv_data *ddata; - struct omap_dss_device *dssdev; - struct gpio_desc *gpio; - - dev_dbg(&pdev->dev, "probe\n"); - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - - gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); - - ddata->enable_gpio = gpio; - - dssdev = &ddata->dssdev; - dssdev->ops = &opa362_ops; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_VENC; - dssdev->owner = THIS_MODULE; - dssdev->of_port = 1; - - dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1); - if (IS_ERR(dssdev->next)) { - if (PTR_ERR(dssdev->next) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to find video sink\n"); - return PTR_ERR(dssdev->next); - } - - omapdss_device_register(dssdev); - - return 0; -} - -static int __exit opa362_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - - if (dssdev->next) - omapdss_device_put(dssdev->next); - omapdss_device_unregister(&ddata->dssdev); - - opa362_disable(dssdev); - - return 0; -} - -static const struct of_device_id opa362_of_match[] = { - { .compatible = "omapdss,ti,opa362", }, - {}, -}; -MODULE_DEVICE_TABLE(of, opa362_of_match); - -static struct platform_driver opa362_driver = { - .probe = opa362_probe, - .remove = __exit_p(opa362_remove), - .driver = { - .name = "amplifier-opa362", - .of_match_table = opa362_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(opa362_driver); - -MODULE_AUTHOR("H. Nikolaus Schaller "); -MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c deleted file mode 100644 index 857ae84cd7d1..000000000000 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * TPD12S015 HDMI ESD protection & level shifter chip driver - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ - * Author: Tomi Valkeinen - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "../dss/omapdss.h" - -struct panel_drv_data { - struct omap_dss_device dssdev; - void (*hpd_cb)(void *cb_data, enum drm_connector_status status); - void *hpd_cb_data; - struct mutex hpd_lock; - - struct gpio_desc *ct_cp_hpd_gpio; - struct gpio_desc *ls_oe_gpio; - struct gpio_desc *hpd_gpio; -}; - -#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) - -static int tpd_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - struct panel_drv_data *ddata = to_panel_data(dst); - int r; - - r = omapdss_device_connect(dst->dss, dst, dst->next); - if (r) - return r; - - gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); - gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1); - - /* DC-DC converter needs at max 300us to get to 90% of 5V */ - udelay(300); - - return 0; -} - -static void tpd_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - struct panel_drv_data *ddata = to_panel_data(dst); - - gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0); - gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0); - - omapdss_device_disconnect(dst, dst->next); -} - -static bool tpd_detect(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - return gpiod_get_value_cansleep(ddata->hpd_gpio); -} - -static void tpd_register_hpd_cb(struct omap_dss_device *dssdev, - void (*cb)(void *cb_data, - enum drm_connector_status status), - void *cb_data) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - mutex_lock(&ddata->hpd_lock); - ddata->hpd_cb = cb; - ddata->hpd_cb_data = cb_data; - mutex_unlock(&ddata->hpd_lock); -} - -static void tpd_unregister_hpd_cb(struct omap_dss_device *dssdev) -{ - struct panel_drv_data *ddata = to_panel_data(dssdev); - - mutex_lock(&ddata->hpd_lock); - ddata->hpd_cb = NULL; - ddata->hpd_cb_data = NULL; - mutex_unlock(&ddata->hpd_lock); -} - -static const struct omap_dss_device_ops tpd_ops = { - .connect = tpd_connect, - .disconnect = tpd_disconnect, - .detect = tpd_detect, - .register_hpd_cb = tpd_register_hpd_cb, - .unregister_hpd_cb = tpd_unregister_hpd_cb, -}; - -static irqreturn_t tpd_hpd_isr(int irq, void *data) -{ - struct panel_drv_data *ddata = data; - - mutex_lock(&ddata->hpd_lock); - if (ddata->hpd_cb) { - enum drm_connector_status status; - - if (tpd_detect(&ddata->dssdev)) - status = connector_status_connected; - else - status = connector_status_disconnected; - - ddata->hpd_cb(ddata->hpd_cb_data, status); - } - mutex_unlock(&ddata->hpd_lock); - - return IRQ_HANDLED; -} - -static int tpd_probe(struct platform_device *pdev) -{ - struct omap_dss_device *dssdev; - struct panel_drv_data *ddata; - int r; - struct gpio_desc *gpio; - - ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - platform_set_drvdata(pdev, ddata); - - gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0, - GPIOD_OUT_LOW); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); - - ddata->ct_cp_hpd_gpio = gpio; - - gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1, - GPIOD_OUT_LOW); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); - - ddata->ls_oe_gpio = gpio; - - gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, - GPIOD_IN); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); - - ddata->hpd_gpio = gpio; - - mutex_init(&ddata->hpd_lock); - - r = devm_request_threaded_irq(&pdev->dev, gpiod_to_irq(ddata->hpd_gpio), - NULL, tpd_hpd_isr, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "tpd12s015 hpd", ddata); - if (r) - return r; - - dssdev = &ddata->dssdev; - dssdev->ops = &tpd_ops; - dssdev->dev = &pdev->dev; - dssdev->type = OMAP_DISPLAY_TYPE_HDMI; - dssdev->owner = THIS_MODULE; - dssdev->of_port = 1; - dssdev->ops_flags = OMAP_DSS_DEVICE_OP_DETECT - | OMAP_DSS_DEVICE_OP_HPD; - - dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1); - if (IS_ERR(dssdev->next)) { - if (PTR_ERR(dssdev->next) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to find video sink\n"); - return PTR_ERR(dssdev->next); - } - - omapdss_device_register(dssdev); - - return 0; -} - -static int __exit tpd_remove(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev = &ddata->dssdev; - - if (dssdev->next) - omapdss_device_put(dssdev->next); - omapdss_device_unregister(&ddata->dssdev); - - return 0; -} - -static const struct of_device_id tpd_of_match[] = { - { .compatible = "omapdss,ti,tpd12s015", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, tpd_of_match); - -static struct platform_driver tpd_driver = { - .probe = tpd_probe, - .remove = __exit_p(tpd_remove), - .driver = { - .name = "tpd12s015", - .of_match_table = tpd_of_match, - .suppress_bind_attrs = true, - }, -}; - -module_platform_driver(tpd_driver); - -MODULE_AUTHOR("Tomi Valkeinen "); -MODULE_DESCRIPTION("TPD12S015 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 73f1fab346e9..52daae36935a 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -444,8 +444,8 @@ static int hdmi4_bridge_attach(struct drm_bridge *bridge, { struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - if (!hdmi->output.next_bridge) - return 0; + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge, bridge, flags); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 52184797c858..f07fd5c6dc39 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -422,8 +422,8 @@ static int hdmi5_bridge_attach(struct drm_bridge *bridge, { struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); - if (!hdmi->output.next_bridge) - return 0; + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; return drm_bridge_attach(bridge->encoder, hdmi->output.next_bridge, bridge, flags); diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c b/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c index ce67891eedd4..00372f4ce711 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c +++ b/drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c @@ -174,12 +174,7 @@ static const struct of_device_id omapdss_of_match[] __initconst = { }; static const struct of_device_id omapdss_of_fixups_whitelist[] __initconst = { - { .compatible = "composite-video-connector" }, - { .compatible = "hdmi-connector" }, { .compatible = "panel-dsi-cm" }, - { .compatible = "svideo-connector" }, - { .compatible = "ti,opa362" }, - { .compatible = "ti,tpd12s015" }, {}, }; diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index 9ba7cc8539a1..ce21c798cca6 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -60,6 +60,11 @@ int omapdss_device_init_output(struct omap_dss_device *out, } if (local_bridge) { + if (!out->bridge) { + ret = -EPROBE_DEFER; + goto error; + } + out->next_bridge = out->bridge; out->bridge = local_bridge; } diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index cb9a689ed612..c8c19967a42f 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -626,8 +626,8 @@ static int venc_bridge_attach(struct drm_bridge *bridge, { struct venc_device *venc = drm_bridge_to_venc(bridge); - if (venc->output.next_bridge) - return 0; + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; return drm_bridge_attach(bridge->encoder, venc->output.next_bridge, bridge, flags); From 75fb968b83d0530e2be2cb51e90b7bc849dd433b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:00 +0200 Subject: [PATCH 56/78] drm/omap: Remove HPD, detect and EDID omapdss operations Due to the removal of several omapdrm display drivers, the omapdss HPD, detected and EDID operations are not used anymore. Remove them and all related code. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-41-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 61 -------- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 46 ------ drivers/gpu/drm/omapdrm/dss/omapdss.h | 25 +-- drivers/gpu/drm/omapdrm/omap_connector.c | 190 +++-------------------- drivers/gpu/drm/omapdrm/omap_connector.h | 2 - drivers/gpu/drm/omapdrm/omap_drv.c | 8 +- 6 files changed, 22 insertions(+), 310 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 52daae36935a..b9bcd6e681e8 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -321,47 +321,6 @@ static void hdmi_disconnect(struct omap_dss_device *src, omapdss_device_disconnect(dst, dst->next); } -#define MAX_EDID 512 - -static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi, - struct drm_connector *connector) -{ - u8 *edid; - int r; - - edid = kzalloc(MAX_EDID, GFP_KERNEL); - if (!edid) - return NULL; - - r = hdmi4_core_ddc_read(&hdmi->core, edid, 0, EDID_LENGTH); - if (r) - goto error; - - if (edid[0x7e] > 0) { - char checksum = 0; - unsigned int i; - - r = hdmi4_core_ddc_read(&hdmi->core, edid + EDID_LENGTH, 1, - EDID_LENGTH); - if (r) - goto error; - - for (i = 0; i < EDID_LENGTH; ++i) - checksum += edid[EDID_LENGTH + i]; - - if (checksum != 0) { - DSSERR("E-EDID checksum failed!!\n"); - goto error; - } - } - - return (struct edid *)edid; - -error: - kfree(edid); - return NULL; -} - static struct edid * hdmi_do_read_edid(struct omap_hdmi *hdmi, struct edid *(*read)(struct omap_hdmi *hdmi, @@ -411,28 +370,9 @@ done: return edid; } -static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) -{ - return hdmi_do_read_edid(dssdev_to_hdmi(dssdev), hdmi_read_edid_data, - NULL); -} - -static void hdmi_lost_hotplug(struct omap_dss_device *dssdev) -{ - struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev); - - hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); -} - static const struct omap_dss_device_ops hdmi_ops = { .connect = hdmi_connect, .disconnect = hdmi_disconnect, - - .read_edid = hdmi_read_edid, - - .hdmi = { - .lost_hotplug = hdmi_lost_hotplug, - }, }; /* ----------------------------------------------------------------------------- @@ -804,7 +744,6 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi) out->ops = &hdmi_ops; out->owner = THIS_MODULE; out->of_port = 0; - out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; r = omapdss_device_init_output(out, &hdmi->bridge); if (r < 0) { diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index f07fd5c6dc39..effe4a9401ff 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -319,43 +319,6 @@ static void hdmi_disconnect(struct omap_dss_device *src, omapdss_device_disconnect(dst, dst->next); } -#define MAX_EDID 512 - -static struct edid *hdmi_read_edid_data(struct omap_hdmi *hdmi, - struct drm_connector *connector) -{ - struct hdmi_core_data *core = &hdmi->core; - int max_ext_blocks = 3; - int r, n, i; - u8 *edid; - - edid = kzalloc(MAX_EDID, GFP_KERNEL); - if (!edid) - return NULL; - - r = hdmi5_core_ddc_read(core, edid, 0, EDID_LENGTH); - if (r) - goto error; - - n = edid[0x7e]; - - if (n > max_ext_blocks) - n = max_ext_blocks; - - for (i = 1; i <= n; i++) { - r = hdmi5_core_ddc_read(core, edid + i * EDID_LENGTH, i, - EDID_LENGTH); - if (r) - goto error; - } - - return (struct edid *)edid; - -error: - kfree(edid); - return NULL; -} - static struct edid * hdmi_do_read_edid(struct omap_hdmi *hdmi, struct edid *(*read)(struct omap_hdmi *hdmi, @@ -400,17 +363,9 @@ hdmi_do_read_edid(struct omap_hdmi *hdmi, return (struct edid *)edid; } -static struct edid *hdmi_read_edid(struct omap_dss_device *dssdev) -{ - return hdmi_do_read_edid(dssdev_to_hdmi(dssdev), hdmi_read_edid_data, - NULL); -} - static const struct omap_dss_device_ops hdmi_ops = { .connect = hdmi_connect, .disconnect = hdmi_disconnect, - - .read_edid = hdmi_read_edid, }; /* ----------------------------------------------------------------------------- @@ -763,7 +718,6 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi) out->ops = &hdmi_ops; out->owner = THIS_MODULE; out->of_port = 0; - out->ops_flags = OMAP_DSS_DEVICE_OP_EDID; r = omapdss_device_init_output(out, &hdmi->bridge); if (r < 0) { diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 30a12cf91cbb..cb79e05c902d 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -285,10 +285,6 @@ struct omap_dss_writeback_info { u8 pre_mult_alpha; }; -struct omapdss_hdmi_ops { - void (*lost_hotplug)(struct omap_dss_device *dssdev); -}; - struct omapdss_dsi_ops { void (*disable)(struct omap_dss_device *dssdev, bool disconnect_lanes, bool enter_ulps); @@ -356,36 +352,17 @@ struct omap_dss_device_ops { void (*set_timings)(struct omap_dss_device *dssdev, const struct drm_display_mode *mode); - bool (*detect)(struct omap_dss_device *dssdev); - - void (*register_hpd_cb)(struct omap_dss_device *dssdev, - void (*cb)(void *cb_data, - enum drm_connector_status status), - void *cb_data); - void (*unregister_hpd_cb)(struct omap_dss_device *dssdev); - - struct edid *(*read_edid)(struct omap_dss_device *dssdev); - int (*get_modes)(struct omap_dss_device *dssdev, struct drm_connector *connector); - union { - const struct omapdss_hdmi_ops hdmi; - const struct omapdss_dsi_ops dsi; - }; + const struct omapdss_dsi_ops dsi; }; /** * enum omap_dss_device_ops_flag - Indicates which device ops are supported - * @OMAP_DSS_DEVICE_OP_DETECT: The device supports output connection detection - * @OMAP_DSS_DEVICE_OP_HPD: The device supports all hot-plug-related operations - * @OMAP_DSS_DEVICE_OP_EDID: The device supports reading EDID * @OMAP_DSS_DEVICE_OP_MODES: The device supports reading modes */ enum omap_dss_device_ops_flag { - OMAP_DSS_DEVICE_OP_DETECT = BIT(0), - OMAP_DSS_DEVICE_OP_HPD = BIT(1), - OMAP_DSS_DEVICE_OP_EDID = BIT(2), OMAP_DSS_DEVICE_OP_MODES = BIT(3), }; diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index c636ae228130..baa31ed1f993 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -19,111 +19,22 @@ struct omap_connector { struct drm_connector base; struct omap_dss_device *output; - struct omap_dss_device *hpd; }; -static void omap_connector_hpd_notify(struct drm_connector *connector, - enum drm_connector_status status) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev; - - if (status != connector_status_disconnected) - return; - - /* - * Notify all devics in the pipeline of disconnection. This is required - * to let the HDMI encoders reset their internal state related to - * connection status, such as the CEC address. - */ - for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) { - if (dssdev->ops && dssdev->ops->hdmi.lost_hotplug) - dssdev->ops->hdmi.lost_hotplug(dssdev); - } -} - -static void omap_connector_hpd_cb(void *cb_data, - enum drm_connector_status status) -{ - struct omap_connector *omap_connector = cb_data; - struct drm_connector *connector = &omap_connector->base; - struct drm_device *dev = connector->dev; - enum drm_connector_status old_status; - - mutex_lock(&dev->mode_config.mutex); - old_status = connector->status; - connector->status = status; - mutex_unlock(&dev->mode_config.mutex); - - if (old_status == status) - return; - - omap_connector_hpd_notify(connector, status); - - drm_kms_helper_hotplug_event(dev); -} - -void omap_connector_enable_hpd(struct drm_connector *connector) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *hpd = omap_connector->hpd; - - if (hpd) - hpd->ops->register_hpd_cb(hpd, omap_connector_hpd_cb, - omap_connector); -} - -void omap_connector_disable_hpd(struct drm_connector *connector) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *hpd = omap_connector->hpd; - - if (hpd) - hpd->ops->unregister_hpd_cb(hpd); -} - -static struct omap_dss_device * -omap_connector_find_device(struct drm_connector *connector, - enum omap_dss_device_ops_flag op) -{ - struct omap_connector *omap_connector = to_omap_connector(connector); - struct omap_dss_device *dssdev = NULL; - struct omap_dss_device *d; - - for (d = omap_connector->output; d; d = d->next) { - if (d->ops_flags & op) - dssdev = d; - } - - return dssdev; -} - static enum drm_connector_status omap_connector_detect( struct drm_connector *connector, bool force) { - struct omap_dss_device *dssdev; enum drm_connector_status status; - dssdev = omap_connector_find_device(connector, - OMAP_DSS_DEVICE_OP_DETECT); - - if (dssdev) { - status = dssdev->ops->detect(dssdev) - ? connector_status_connected - : connector_status_disconnected; - - omap_connector_hpd_notify(connector, status); - } else { - switch (connector->connector_type) { - case DRM_MODE_CONNECTOR_DPI: - case DRM_MODE_CONNECTOR_LVDS: - case DRM_MODE_CONNECTOR_DSI: - status = connector_status_connected; - break; - default: - status = connector_status_unknown; - break; - } + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DPI: + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_DSI: + status = connector_status_connected; + break; + default: + status = connector_status_unknown; + break; } VERB("%s: %d (force=%d)", connector->name, status, force); @@ -137,14 +48,6 @@ static void omap_connector_destroy(struct drm_connector *connector) DBG("%s", connector->name); - if (omap_connector->hpd) { - struct omap_dss_device *hpd = omap_connector->hpd; - - hpd->ops->unregister_hpd_cb(hpd); - omapdss_device_put(hpd); - omap_connector->hpd = NULL; - } - drm_connector_unregister(connector); drm_connector_cleanup(connector); @@ -153,63 +56,27 @@ static void omap_connector_destroy(struct drm_connector *connector) kfree(omap_connector); } -static int omap_connector_get_modes_edid(struct drm_connector *connector, - struct omap_dss_device *dssdev) -{ - enum drm_connector_status status; - struct edid *edid; - int n; - - status = omap_connector_detect(connector, false); - if (status != connector_status_connected) - goto no_edid; - - edid = dssdev->ops->read_edid(dssdev); - if (!edid || !drm_edid_is_valid(edid)) { - kfree(edid); - goto no_edid; - } - - drm_connector_update_edid_property(connector, edid); - n = drm_add_edid_modes(connector, edid); - - kfree(edid); - return n; - -no_edid: - drm_connector_update_edid_property(connector, NULL); - return 0; -} - static int omap_connector_get_modes(struct drm_connector *connector) { - struct omap_dss_device *dssdev; + struct omap_connector *omap_connector = to_omap_connector(connector); + struct omap_dss_device *dssdev = NULL; + struct omap_dss_device *d; DBG("%s", connector->name); /* - * If display exposes EDID, then we parse that in the normal way to - * build table of supported modes. + * If the display pipeline reports modes (e.g. with a fixed resolution + * panel or an analog TV output), query it. */ - dssdev = omap_connector_find_device(connector, - OMAP_DSS_DEVICE_OP_EDID); - if (dssdev) - return omap_connector_get_modes_edid(connector, dssdev); + for (d = omap_connector->output; d; d = d->next) { + if (d->ops_flags & OMAP_DSS_DEVICE_OP_MODES) + dssdev = d; + } - /* - * Otherwise if the display pipeline reports modes (e.g. with a fixed - * resolution panel or an analog TV output), query it. - */ - dssdev = omap_connector_find_device(connector, - OMAP_DSS_DEVICE_OP_MODES); if (dssdev) return dssdev->ops->get_modes(dssdev, connector); - /* - * We can't retrieve modes, which can happen for instance for a DVI or - * VGA output with the DDC bus unconnected. The KMS core will add the - * default modes. - */ + /* We can't retrieve modes. The KMS core will add the default modes. */ return 0; } @@ -290,7 +157,6 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, { struct drm_connector *connector = NULL; struct omap_connector *omap_connector; - struct omap_dss_device *dssdev; DBG("%s", output->name); @@ -308,24 +174,6 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, omap_connector_get_type(output)); drm_connector_helper_add(connector, &omap_connector_helper_funcs); - /* - * Initialize connector status handling. First try to find a device that - * supports hot-plug reporting. If it fails, fall back to a device that - * support polling. If that fails too, we don't support hot-plug - * detection at all. - */ - dssdev = omap_connector_find_device(connector, OMAP_DSS_DEVICE_OP_HPD); - if (dssdev) { - omap_connector->hpd = omapdss_device_get(dssdev); - connector->polled = DRM_CONNECTOR_POLL_HPD; - } else { - dssdev = omap_connector_find_device(connector, - OMAP_DSS_DEVICE_OP_DETECT); - if (dssdev) - connector->polled = DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT; - } - return connector; fail: diff --git a/drivers/gpu/drm/omapdrm/omap_connector.h b/drivers/gpu/drm/omapdrm/omap_connector.h index 4aa5608f4bbe..0ecd4f1655b7 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.h +++ b/drivers/gpu/drm/omapdrm/omap_connector.h @@ -21,8 +21,6 @@ struct omap_dss_device; struct drm_connector *omap_connector_init(struct drm_device *dev, struct omap_dss_device *output, struct drm_encoder *encoder); -void omap_connector_enable_hpd(struct drm_connector *connector); -void omap_connector_disable_hpd(struct drm_connector *connector); enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 47afa37055b3..e6a065030523 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -422,9 +422,7 @@ static void omap_modeset_enable_external_hpd(struct drm_device *ddev) if (!connector) continue; - if (priv->pipes[i].output->next) - omap_connector_enable_hpd(connector); - else + if (priv->pipes[i].output->bridge) drm_bridge_connector_enable_hpd(connector); } } @@ -443,9 +441,7 @@ static void omap_modeset_disable_external_hpd(struct drm_device *ddev) if (!connector) continue; - if (priv->pipes[i].output->next) - omap_connector_disable_hpd(connector); - else + if (priv->pipes[i].output->bridge) drm_bridge_connector_disable_hpd(connector); } } From a559b4407d2bc559a8149056f44c780b92c7526a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:01 +0200 Subject: [PATCH 57/78] drm/omap: hdmi: Remove omap_dss_device operations Now that the HDMI outputs are driven fully through the drm_bridge API their omap_dss_device operations are not used anymore. Remove them. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-42-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi.h | 1 - drivers/gpu/drm/omapdrm/dss/hdmi4.c | 18 ------------------ drivers/gpu/drm/omapdrm/dss/hdmi5.c | 18 ------------------ 3 files changed, 37 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi.h b/drivers/gpu/drm/omapdrm/dss/hdmi.h index bd43f6abf27b..3a40833d3368 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi.h +++ b/drivers/gpu/drm/omapdrm/dss/hdmi.h @@ -380,7 +380,6 @@ struct omap_hdmi { bool display_enabled; }; -#define dssdev_to_hdmi(dssdev) container_of(dssdev, struct omap_hdmi, output) #define drm_bridge_to_hdmi(b) container_of(b, struct omap_hdmi, bridge) #endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index b9bcd6e681e8..96ef7bd52199 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -309,18 +309,6 @@ void hdmi4_core_disable(struct hdmi_core_data *core) mutex_unlock(&hdmi->lock); } -static int hdmi_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - return omapdss_device_connect(dst->dss, dst, dst->next); -} - -static void hdmi_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - omapdss_device_disconnect(dst, dst->next); -} - static struct edid * hdmi_do_read_edid(struct omap_hdmi *hdmi, struct edid *(*read)(struct omap_hdmi *hdmi, @@ -370,11 +358,6 @@ done: return edid; } -static const struct omap_dss_device_ops hdmi_ops = { - .connect = hdmi_connect, - .disconnect = hdmi_disconnect, -}; - /* ----------------------------------------------------------------------------- * DRM Bridge Operations */ @@ -741,7 +724,6 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi) out->type = OMAP_DISPLAY_TYPE_HDMI; out->name = "hdmi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; - out->ops = &hdmi_ops; out->owner = THIS_MODULE; out->of_port = 0; diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index effe4a9401ff..6cb709c775d6 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -307,18 +307,6 @@ static void hdmi_core_disable(struct omap_hdmi *hdmi) mutex_unlock(&hdmi->lock); } -static int hdmi_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - return omapdss_device_connect(dst->dss, dst, dst->next); -} - -static void hdmi_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - omapdss_device_disconnect(dst, dst->next); -} - static struct edid * hdmi_do_read_edid(struct omap_hdmi *hdmi, struct edid *(*read)(struct omap_hdmi *hdmi, @@ -363,11 +351,6 @@ hdmi_do_read_edid(struct omap_hdmi *hdmi, return (struct edid *)edid; } -static const struct omap_dss_device_ops hdmi_ops = { - .connect = hdmi_connect, - .disconnect = hdmi_disconnect, -}; - /* ----------------------------------------------------------------------------- * DRM Bridge Operations */ @@ -715,7 +698,6 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi) out->type = OMAP_DISPLAY_TYPE_HDMI; out->name = "hdmi.0"; out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; - out->ops = &hdmi_ops; out->owner = THIS_MODULE; out->of_port = 0; From 96b0a34d27333e9df724a3efe95daeb68097b2ca Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:02 +0200 Subject: [PATCH 58/78] drm/omap: venc: Remove omap_dss_device operations Now that the VENC output is driven fully through the drm_bridge API its omap_dss_device operations are not used anymore. Remove them. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-43-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/venc.c | 45 ------------------------------ 1 file changed, 45 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index c8c19967a42f..766553bb2f87 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -306,7 +306,6 @@ struct venc_device { struct drm_bridge bridge; }; -#define dssdev_to_venc(dssdev) container_of(dssdev, struct venc_device, output) #define drm_bridge_to_venc(b) container_of(b, struct venc_device, bridge) static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val) @@ -479,30 +478,6 @@ static void venc_power_off(struct venc_device *venc) venc_runtime_put(venc); } -static int venc_get_modes(struct omap_dss_device *dssdev, - struct drm_connector *connector) -{ - static const struct drm_display_mode *modes[] = { - &omap_dss_pal_mode, - &omap_dss_ntsc_mode, - }; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(modes); ++i) { - struct drm_display_mode *mode; - - mode = drm_mode_duplicate(connector->dev, modes[i]); - if (!mode) - return i; - - mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; - drm_mode_set_name(mode); - drm_mode_probed_add(connector, mode); - } - - return ARRAY_SIZE(modes); -} - static enum venc_videomode venc_get_videomode(const struct drm_display_mode *mode) { if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) @@ -598,25 +573,6 @@ static int venc_get_clocks(struct venc_device *venc) return 0; } -static int venc_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - return omapdss_device_connect(dst->dss, dst, dst->next); -} - -static void venc_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - omapdss_device_disconnect(dst, dst->next); -} - -static const struct omap_dss_device_ops venc_ops = { - .connect = venc_connect, - .disconnect = venc_disconnect, - - .get_modes = venc_get_modes, -}; - /* ----------------------------------------------------------------------------- * DRM Bridge Operations */ @@ -816,7 +772,6 @@ static int venc_init_output(struct venc_device *venc) out->type = OMAP_DISPLAY_TYPE_VENC; out->name = "venc.0"; out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; - out->ops = &venc_ops; out->owner = THIS_MODULE; out->of_port = 0; out->ops_flags = OMAP_DSS_DEVICE_OP_MODES; From 7f113085af93a2949ffdde95e5ea21ef8c65c76d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:03 +0200 Subject: [PATCH 59/78] drm/omap: hdmi4: Simplify EDID read Now that the omap_dss_device EDID read operation has been removed, simplify the bridge-based EDID access by merging multiple functions together. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-44-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 96 ++++++++++++----------------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 96ef7bd52199..2578c95570f6 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -309,55 +309,6 @@ void hdmi4_core_disable(struct hdmi_core_data *core) mutex_unlock(&hdmi->lock); } -static struct edid * -hdmi_do_read_edid(struct omap_hdmi *hdmi, - struct edid *(*read)(struct omap_hdmi *hdmi, - struct drm_connector *connector), - struct drm_connector *connector) -{ - struct edid *edid = NULL; - unsigned int cec_addr; - bool need_enable; - int r; - - need_enable = hdmi->core_enabled == false; - - if (need_enable) { - r = hdmi4_core_enable(&hdmi->core); - if (r) - return NULL; - } - - mutex_lock(&hdmi->lock); - r = hdmi_runtime_get(hdmi); - BUG_ON(r); - - r = hdmi4_core_ddc_init(&hdmi->core); - if (r) - goto done; - - edid = read(hdmi, connector); - -done: - hdmi_runtime_put(hdmi); - mutex_unlock(&hdmi->lock); - - if (edid && edid->extensions) { - unsigned int len = (edid->extensions + 1) * EDID_LENGTH; - - cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL); - } else { - cec_addr = CEC_PHYS_ADDR_INVALID; - } - - hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr); - - if (need_enable) - hdmi4_core_disable(&hdmi->core); - - return edid; -} - /* ----------------------------------------------------------------------------- * DRM Bridge Operations */ @@ -485,18 +436,51 @@ static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge, hdmi4_cec_set_phys_addr(&hdmi->core, CEC_PHYS_ADDR_INVALID); } -static struct edid *hdmi4_bridge_read_edid(struct omap_hdmi *hdmi, - struct drm_connector *connector) -{ - return drm_do_get_edid(connector, hdmi4_core_ddc_read, &hdmi->core); -} - static struct edid *hdmi4_bridge_get_edid(struct drm_bridge *bridge, struct drm_connector *connector) { struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct edid *edid = NULL; + unsigned int cec_addr; + bool need_enable; + int r; - return hdmi_do_read_edid(hdmi, hdmi4_bridge_read_edid, connector); + need_enable = hdmi->core_enabled == false; + + if (need_enable) { + r = hdmi4_core_enable(&hdmi->core); + if (r) + return NULL; + } + + mutex_lock(&hdmi->lock); + r = hdmi_runtime_get(hdmi); + BUG_ON(r); + + r = hdmi4_core_ddc_init(&hdmi->core); + if (r) + goto done; + + edid = drm_do_get_edid(connector, hdmi4_core_ddc_read, &hdmi->core); + +done: + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); + + if (edid && edid->extensions) { + unsigned int len = (edid->extensions + 1) * EDID_LENGTH; + + cec_addr = cec_get_edid_phys_addr((u8 *)edid, len, NULL); + } else { + cec_addr = CEC_PHYS_ADDR_INVALID; + } + + hdmi4_cec_set_phys_addr(&hdmi->core, cec_addr); + + if (need_enable) + hdmi4_core_disable(&hdmi->core); + + return edid; } static const struct drm_bridge_funcs hdmi4_bridge_funcs = { From b6067da4ebf9db5e4b026220b13dce3528032b88 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:04 +0200 Subject: [PATCH 60/78] drm/omap: hdmi5: Simplify EDID read Now that the omap_dss_device EDID read operation has been removed, simplify the bridge-based EDID access by merging multiple functions together. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-45-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 86 ++++++++++++----------------- 1 file changed, 35 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 6cb709c775d6..4d4c1fabd0a1 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -307,50 +307,6 @@ static void hdmi_core_disable(struct omap_hdmi *hdmi) mutex_unlock(&hdmi->lock); } -static struct edid * -hdmi_do_read_edid(struct omap_hdmi *hdmi, - struct edid *(*read)(struct omap_hdmi *hdmi, - struct drm_connector *connector), - struct drm_connector *connector) -{ - struct edid *edid; - bool need_enable; - int idlemode; - int r; - - need_enable = hdmi->core_enabled == false; - - if (need_enable) { - r = hdmi_core_enable(hdmi); - if (r) - return NULL; - } - - mutex_lock(&hdmi->lock); - r = hdmi_runtime_get(hdmi); - BUG_ON(r); - - idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2); - /* No-idle mode */ - REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); - - hdmi5_core_ddc_init(&hdmi->core); - - edid = read(hdmi, connector); - - hdmi5_core_ddc_uninit(&hdmi->core); - - REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); - - hdmi_runtime_put(hdmi); - mutex_unlock(&hdmi->lock); - - if (need_enable) - hdmi_core_disable(hdmi); - - return (struct edid *)edid; -} - /* ----------------------------------------------------------------------------- * DRM Bridge Operations */ @@ -469,18 +425,46 @@ static void hdmi5_bridge_disable(struct drm_bridge *bridge, mutex_unlock(&hdmi->lock); } -static struct edid *hdmi5_bridge_read_edid(struct omap_hdmi *hdmi, - struct drm_connector *connector) -{ - return drm_do_get_edid(connector, hdmi5_core_ddc_read, &hdmi->core); -} - static struct edid *hdmi5_bridge_get_edid(struct drm_bridge *bridge, struct drm_connector *connector) { struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge); + struct edid *edid; + bool need_enable; + int idlemode; + int r; - return hdmi_do_read_edid(hdmi, hdmi5_bridge_read_edid, connector); + need_enable = hdmi->core_enabled == false; + + if (need_enable) { + r = hdmi_core_enable(hdmi); + if (r) + return NULL; + } + + mutex_lock(&hdmi->lock); + r = hdmi_runtime_get(hdmi); + BUG_ON(r); + + idlemode = REG_GET(hdmi->wp.base, HDMI_WP_SYSCONFIG, 3, 2); + /* No-idle mode */ + REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, 1, 3, 2); + + hdmi5_core_ddc_init(&hdmi->core); + + edid = drm_do_get_edid(connector, hdmi5_core_ddc_read, &hdmi->core); + + hdmi5_core_ddc_uninit(&hdmi->core); + + REG_FLD_MOD(hdmi->wp.base, HDMI_WP_SYSCONFIG, idlemode, 3, 2); + + hdmi_runtime_put(hdmi); + mutex_unlock(&hdmi->lock); + + if (need_enable) + hdmi_core_disable(hdmi); + + return (struct edid *)edid; } static const struct drm_bridge_funcs hdmi5_bridge_funcs = { From b04df00bb5c01bac8bf58debae258ad923b3e125 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:05 +0200 Subject: [PATCH 61/78] drm/omap: dpi: Sort includes alphabetically This makes it easier to quickly locate duplicate includes. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-46-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/dpi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 2d0eb5fcbb5b..f8354271ce6f 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -9,20 +9,20 @@ #define DSS_SUBSYS_NAME "DPI" -#include +#include #include -#include #include #include +#include +#include +#include #include #include #include -#include -#include #include -#include "omapdss.h" #include "dss.h" +#include "omapdss.h" struct dpi_data { struct platform_device *pdev; From 825281cd6ba1f9045288c63c0eff4a393cd24dc4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:06 +0200 Subject: [PATCH 62/78] drm/omap: dpi: Reorder functions in sections Group functions based on their purpose and split them in sections to make the source code easier to navigate. No functional change is included. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-47-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/dpi.c | 146 ++++++++++++++++-------------- 1 file changed, 79 insertions(+), 67 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index f8354271ce6f..dccf81e4ce64 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -48,6 +48,10 @@ static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev) return container_of(dssdev, struct dpi_data, output); } +/* ----------------------------------------------------------------------------- + * Clock Handling and PLL + */ + static enum dss_clk_source dpi_get_clk_src_dra7xx(struct dpi_data *dpi, enum omap_channel channel) { @@ -366,6 +370,62 @@ static void dpi_config_lcd_manager(struct dpi_data *dpi) dss_mgr_set_lcd_config(&dpi->output, &dpi->mgr_config); } +static int dpi_verify_pll(struct dss_pll *pll) +{ + int r; + + /* do initial setup with the PLL to see if it is operational */ + + r = dss_pll_enable(pll); + if (r) + return r; + + dss_pll_disable(pll); + + return 0; +} + +static void dpi_init_pll(struct dpi_data *dpi) +{ + struct dss_pll *pll; + + if (dpi->pll) + return; + + dpi->clk_src = dpi_get_clk_src(dpi); + + pll = dss_pll_find_by_src(dpi->dss, dpi->clk_src); + if (!pll) + return; + + if (dpi_verify_pll(pll)) { + DSSWARN("PLL not operational\n"); + return; + } + + dpi->pll = pll; +} + +/* ----------------------------------------------------------------------------- + * omap_dss_device Operations + */ + +static int dpi_connect(struct omap_dss_device *src, + struct omap_dss_device *dst) +{ + struct dpi_data *dpi = dpi_get_data_from_dssdev(dst); + + dpi_init_pll(dpi); + + return omapdss_device_connect(dst->dss, dst, dst->next); +} + +static void dpi_disconnect(struct omap_dss_device *src, + struct omap_dss_device *dst) +{ + omapdss_device_disconnect(dst, dst->next); +} + static void dpi_display_enable(struct omap_dss_device *dssdev) { struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); @@ -446,20 +506,6 @@ static void dpi_display_disable(struct omap_dss_device *dssdev) mutex_unlock(&dpi->lock); } -static void dpi_set_timings(struct omap_dss_device *dssdev, - const struct drm_display_mode *mode) -{ - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - - DSSDBG("dpi_set_timings\n"); - - mutex_lock(&dpi->lock); - - dpi->pixelclock = mode->clock * 1000; - - mutex_unlock(&dpi->lock); -} - static int dpi_check_timings(struct omap_dss_device *dssdev, struct drm_display_mode *mode) { @@ -500,41 +546,30 @@ static int dpi_check_timings(struct omap_dss_device *dssdev, return 0; } -static int dpi_verify_pll(struct dss_pll *pll) +static void dpi_set_timings(struct omap_dss_device *dssdev, + const struct drm_display_mode *mode) { - int r; + struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - /* do initial setup with the PLL to see if it is operational */ + DSSDBG("dpi_set_timings\n"); - r = dss_pll_enable(pll); - if (r) - return r; + mutex_lock(&dpi->lock); - dss_pll_disable(pll); + dpi->pixelclock = mode->clock * 1000; - return 0; + mutex_unlock(&dpi->lock); } -static void dpi_init_pll(struct dpi_data *dpi) -{ - struct dss_pll *pll; +static const struct omap_dss_device_ops dpi_ops = { + .connect = dpi_connect, + .disconnect = dpi_disconnect, - if (dpi->pll) - return; + .enable = dpi_display_enable, + .disable = dpi_display_disable, - dpi->clk_src = dpi_get_clk_src(dpi); - - pll = dss_pll_find_by_src(dpi->dss, dpi->clk_src); - if (!pll) - return; - - if (dpi_verify_pll(pll)) { - DSSWARN("PLL not operational\n"); - return; - } - - dpi->pll = pll; -} + .check_timings = dpi_check_timings, + .set_timings = dpi_set_timings, +}; /* * Return a hardcoded channel for the DPI output. This should work for @@ -572,33 +607,6 @@ static enum omap_channel dpi_get_channel(struct dpi_data *dpi) } } -static int dpi_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - struct dpi_data *dpi = dpi_get_data_from_dssdev(dst); - - dpi_init_pll(dpi); - - return omapdss_device_connect(dst->dss, dst, dst->next); -} - -static void dpi_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - omapdss_device_disconnect(dst, dst->next); -} - -static const struct omap_dss_device_ops dpi_ops = { - .connect = dpi_connect, - .disconnect = dpi_disconnect, - - .enable = dpi_display_enable, - .disable = dpi_display_disable, - - .check_timings = dpi_check_timings, - .set_timings = dpi_set_timings, -}; - static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port) { struct omap_dss_device *out = &dpi->output; @@ -647,6 +655,10 @@ static void dpi_uninit_output_port(struct device_node *port) omapdss_device_cleanup_output(out); } +/* ----------------------------------------------------------------------------- + * Initialisation and Cleanup + */ + static const struct soc_device_attribute dpi_soc_devices[] = { { .machine = "OMAP3[456]*" }, { .machine = "[AD]M37*" }, From 76777d6c30fb9fc1553a22743304f58ec33f621d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:07 +0200 Subject: [PATCH 63/78] drm/omap: dpi: Simplify clock setting API The dpi_set_pll_clk() and dpi_set_dispc_clk() return various information through pointer arguments that are never used by the callers. Remove them to simplify the clock setting API. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-48-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/dpi.c | 32 ++++++++----------------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index dccf81e4ce64..c167bd1116ec 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -287,9 +287,7 @@ static bool dpi_dss_clk_calc(struct dpi_data *dpi, unsigned long pck, -static int dpi_set_pll_clk(struct dpi_data *dpi, enum omap_channel channel, - unsigned long pck_req, unsigned long *fck, int *lck_div, - int *pck_div) +static int dpi_set_pll_clk(struct dpi_data *dpi, unsigned long pck_req) { struct dpi_clk_calc_ctx ctx; int r; @@ -303,19 +301,15 @@ static int dpi_set_pll_clk(struct dpi_data *dpi, enum omap_channel channel, if (r) return r; - dss_select_lcd_clk_source(dpi->dss, channel, dpi->clk_src); + dss_select_lcd_clk_source(dpi->dss, dpi->output.dispc_channel, + dpi->clk_src); dpi->mgr_config.clock_info = ctx.dispc_cinfo; - *fck = ctx.pll_cinfo.clkout[ctx.clkout_idx]; - *lck_div = ctx.dispc_cinfo.lck_div; - *pck_div = ctx.dispc_cinfo.pck_div; - return 0; } -static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req, - unsigned long *fck, int *lck_div, int *pck_div) +static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req) { struct dpi_clk_calc_ctx ctx; int r; @@ -331,29 +325,19 @@ static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req, dpi->mgr_config.clock_info = ctx.dispc_cinfo; - *fck = ctx.fck; - *lck_div = ctx.dispc_cinfo.lck_div; - *pck_div = ctx.dispc_cinfo.pck_div; - return 0; } static int dpi_set_mode(struct dpi_data *dpi) { - int lck_div = 0, pck_div = 0; - unsigned long fck = 0; - int r = 0; + int r; if (dpi->pll) - r = dpi_set_pll_clk(dpi, dpi->output.dispc_channel, - dpi->pixelclock, &fck, &lck_div, &pck_div); + r = dpi_set_pll_clk(dpi, dpi->pixelclock); else - r = dpi_set_dispc_clk(dpi, dpi->pixelclock, &fck, - &lck_div, &pck_div); - if (r) - return r; + r = dpi_set_dispc_clk(dpi, dpi->pixelclock); - return 0; + return r; } static void dpi_config_lcd_manager(struct dpi_data *dpi) From ac3f6915efb521644b2fd7495b59abd115f6c7b2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:08 +0200 Subject: [PATCH 64/78] drm/omap: dpi: Register a drm_bridge In order to integrate with a chain of drm_bridge, the internal DPI output has to expose its operations through the drm_bridge API. Register a bridge at initialisation time to do so and remove the omap_dss_device operations that are now unused. Signed-off-by: Laurent Pinchart Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Reviewed-by: Tomi Valkeinen Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-49-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/dpi.c | 221 +++++++++++++++++------------- 1 file changed, 122 insertions(+), 99 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index c167bd1116ec..5110acb0c6c1 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -21,6 +21,8 @@ #include #include +#include + #include "dss.h" #include "omapdss.h" @@ -34,19 +36,15 @@ struct dpi_data { enum dss_clk_source clk_src; struct dss_pll *pll; - struct mutex lock; - struct dss_lcd_mgr_config mgr_config; unsigned long pixelclock; int data_lines; struct omap_dss_device output; + struct drm_bridge bridge; }; -static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev) -{ - return container_of(dssdev, struct dpi_data, output); -} +#define drm_bridge_to_dpi(bridge) container_of(bridge, struct dpi_data, bridge) /* ----------------------------------------------------------------------------- * Clock Handling and PLL @@ -354,6 +352,32 @@ static void dpi_config_lcd_manager(struct dpi_data *dpi) dss_mgr_set_lcd_config(&dpi->output, &dpi->mgr_config); } +static int dpi_clock_update(struct dpi_data *dpi, unsigned long *clock) +{ + int lck_div, pck_div; + unsigned long fck; + struct dpi_clk_calc_ctx ctx; + + if (dpi->pll) { + if (!dpi_pll_clk_calc(dpi, *clock, &ctx)) + return -EINVAL; + + fck = ctx.pll_cinfo.clkout[ctx.clkout_idx]; + } else { + if (!dpi_dss_clk_calc(dpi, *clock, &ctx)) + return -EINVAL; + + fck = ctx.fck; + } + + lck_div = ctx.dispc_cinfo.lck_div; + pck_div = ctx.dispc_cinfo.pck_div; + + *clock = fck / lck_div / pck_div; + + return 0; +} + static int dpi_verify_pll(struct dss_pll *pll) { int r; @@ -391,44 +415,86 @@ static void dpi_init_pll(struct dpi_data *dpi) } /* ----------------------------------------------------------------------------- - * omap_dss_device Operations + * DRM Bridge Operations */ -static int dpi_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) +static int dpi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { - struct dpi_data *dpi = dpi_get_data_from_dssdev(dst); + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; dpi_init_pll(dpi); - return omapdss_device_connect(dst->dss, dst, dst->next); + return drm_bridge_attach(bridge->encoder, dpi->output.next_bridge, + bridge, flags); } -static void dpi_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) +static enum drm_mode_status +dpi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) { - omapdss_device_disconnect(dst, dst->next); + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); + unsigned long clock = mode->clock * 1000; + int ret; + + if (mode->hdisplay % 8 != 0) + return MODE_BAD_WIDTH; + + if (mode->clock == 0) + return MODE_NOCLOCK; + + ret = dpi_clock_update(dpi, &clock); + if (ret < 0) + return MODE_CLOCK_RANGE; + + return MODE_OK; } -static void dpi_display_enable(struct omap_dss_device *dssdev) +static bool dpi_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - struct omap_dss_device *out = &dpi->output; + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); + unsigned long clock = mode->clock * 1000; + int ret; + + ret = dpi_clock_update(dpi, &clock); + if (ret < 0) + return false; + + adjusted_mode->clock = clock / 1000; + + return true; +} + +static void dpi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); + + dpi->pixelclock = adjusted_mode->clock * 1000; +} + +static void dpi_bridge_enable(struct drm_bridge *bridge) +{ + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); int r; - mutex_lock(&dpi->lock); - if (dpi->vdds_dsi_reg) { r = regulator_enable(dpi->vdds_dsi_reg); if (r) - goto err_reg_enable; + return; } r = dispc_runtime_get(dpi->dss->dispc); if (r) goto err_get_dispc; - r = dss_dpi_select_source(dpi->dss, dpi->id, out->dispc_channel); + r = dss_dpi_select_source(dpi->dss, dpi->id, dpi->output.dispc_channel); if (r) goto err_src_sel; @@ -450,8 +516,6 @@ static void dpi_display_enable(struct omap_dss_device *dssdev) if (r) goto err_mgr_enable; - mutex_unlock(&dpi->lock); - return; err_mgr_enable: @@ -464,15 +528,11 @@ err_src_sel: err_get_dispc: if (dpi->vdds_dsi_reg) regulator_disable(dpi->vdds_dsi_reg); -err_reg_enable: - mutex_unlock(&dpi->lock); } -static void dpi_display_disable(struct omap_dss_device *dssdev) +static void dpi_bridge_disable(struct drm_bridge *bridge) { - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - - mutex_lock(&dpi->lock); + struct dpi_data *dpi = drm_bridge_to_dpi(bridge); dss_mgr_disable(&dpi->output); @@ -486,75 +546,35 @@ static void dpi_display_disable(struct omap_dss_device *dssdev) if (dpi->vdds_dsi_reg) regulator_disable(dpi->vdds_dsi_reg); - - mutex_unlock(&dpi->lock); } -static int dpi_check_timings(struct omap_dss_device *dssdev, - struct drm_display_mode *mode) -{ - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - int lck_div, pck_div; - unsigned long fck; - unsigned long pck; - struct dpi_clk_calc_ctx ctx; - bool ok; - - if (mode->hdisplay % 8 != 0) - return -EINVAL; - - if (mode->clock == 0) - return -EINVAL; - - if (dpi->pll) { - ok = dpi_pll_clk_calc(dpi, mode->clock * 1000, &ctx); - if (!ok) - return -EINVAL; - - fck = ctx.pll_cinfo.clkout[ctx.clkout_idx]; - } else { - ok = dpi_dss_clk_calc(dpi, mode->clock * 1000, &ctx); - if (!ok) - return -EINVAL; - - fck = ctx.fck; - } - - lck_div = ctx.dispc_cinfo.lck_div; - pck_div = ctx.dispc_cinfo.pck_div; - - pck = fck / lck_div / pck_div; - - mode->clock = pck / 1000; - - return 0; -} - -static void dpi_set_timings(struct omap_dss_device *dssdev, - const struct drm_display_mode *mode) -{ - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - - DSSDBG("dpi_set_timings\n"); - - mutex_lock(&dpi->lock); - - dpi->pixelclock = mode->clock * 1000; - - mutex_unlock(&dpi->lock); -} - -static const struct omap_dss_device_ops dpi_ops = { - .connect = dpi_connect, - .disconnect = dpi_disconnect, - - .enable = dpi_display_enable, - .disable = dpi_display_disable, - - .check_timings = dpi_check_timings, - .set_timings = dpi_set_timings, +static const struct drm_bridge_funcs dpi_bridge_funcs = { + .attach = dpi_bridge_attach, + .mode_valid = dpi_bridge_mode_valid, + .mode_fixup = dpi_bridge_mode_fixup, + .mode_set = dpi_bridge_mode_set, + .enable = dpi_bridge_enable, + .disable = dpi_bridge_disable, }; +static void dpi_bridge_init(struct dpi_data *dpi) +{ + dpi->bridge.funcs = &dpi_bridge_funcs; + dpi->bridge.of_node = dpi->pdev->dev.of_node; + dpi->bridge.type = DRM_MODE_CONNECTOR_DPI; + + drm_bridge_add(&dpi->bridge); +} + +static void dpi_bridge_cleanup(struct dpi_data *dpi) +{ + drm_bridge_remove(&dpi->bridge); +} + +/* ----------------------------------------------------------------------------- + * Initialisation and Cleanup + */ + /* * Return a hardcoded channel for the DPI output. This should work for * current use cases, but this can be later expanded to either resolve @@ -597,6 +617,8 @@ static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port) u32 port_num = 0; int r; + dpi_bridge_init(dpi); + of_property_read_u32(port, "reg", &port_num); dpi->id = port_num <= 2 ? port_num : 0; @@ -618,12 +640,13 @@ static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port) out->type = OMAP_DISPLAY_TYPE_DPI; out->dispc_channel = dpi_get_channel(dpi); out->of_port = port_num; - out->ops = &dpi_ops; out->owner = THIS_MODULE; - r = omapdss_device_init_output(out, NULL); - if (r < 0) + r = omapdss_device_init_output(out, &dpi->bridge); + if (r < 0) { + dpi_bridge_cleanup(dpi); return r; + } omapdss_device_register(out); @@ -637,6 +660,8 @@ static void dpi_uninit_output_port(struct device_node *port) omapdss_device_unregister(out); omapdss_device_cleanup_output(out); + + dpi_bridge_cleanup(dpi); } /* ----------------------------------------------------------------------------- @@ -702,8 +727,6 @@ int dpi_init_port(struct dss_device *dss, struct platform_device *pdev, dpi->dss = dss; port->data = dpi; - mutex_init(&dpi->lock); - r = dpi_init_regulator(dpi); if (r) return r; From 13d2d52f59c0c79398d9c9e2ea3d661a0e5b6bbc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:09 +0200 Subject: [PATCH 65/78] drm/omap: sdi: Sort includes alphabetically This makes it easier to quickly locate duplicate includes. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-50-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/sdi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index 11aa2f712ff4..7dedfcc86922 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -6,17 +6,17 @@ #define DSS_SUBSYS_NAME "SDI" -#include #include #include -#include #include -#include -#include +#include #include +#include +#include +#include -#include "omapdss.h" #include "dss.h" +#include "omapdss.h" struct sdi_device { struct platform_device *pdev; From 8bef8a6d5da81b909a190822b96805a47348146f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:10 +0200 Subject: [PATCH 66/78] drm/omap: sdi: Register a drm_bridge In order to integrate with a chain of drm_bridge, the internal SDI output has to expose its operations through the drm_bridge API. Register a bridge at initialisation time to do so and remove the omap_dss_device operations that are now unused. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-51-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/sdi.c | 182 +++++++++++++++++++----------- 1 file changed, 116 insertions(+), 66 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index 7dedfcc86922..417a8740ad0a 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -15,6 +15,8 @@ #include #include +#include + #include "dss.h" #include "omapdss.h" @@ -30,9 +32,11 @@ struct sdi_device { int datapairs; struct omap_dss_device output; + struct drm_bridge bridge; }; -#define dssdev_to_sdi(dssdev) container_of(dssdev, struct sdi_device, output) +#define drm_bridge_to_sdi(bridge) \ + container_of(bridge, struct sdi_device, bridge) struct sdi_clk_calc_ctx { struct sdi_device *sdi; @@ -118,9 +122,82 @@ static void sdi_config_lcd_manager(struct sdi_device *sdi) dss_mgr_set_lcd_config(&sdi->output, &sdi->mgr_config); } -static void sdi_display_enable(struct omap_dss_device *dssdev) +/* ----------------------------------------------------------------------------- + * DRM Bridge Operations + */ + +static int sdi_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) { - struct sdi_device *sdi = dssdev_to_sdi(dssdev); + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + return drm_bridge_attach(bridge->encoder, sdi->output.next_bridge, + bridge, flags); +} + +static enum drm_mode_status +sdi_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) +{ + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); + unsigned long pixelclock = mode->clock * 1000; + struct dispc_clock_info dispc_cinfo; + unsigned long fck; + int ret; + + if (pixelclock == 0) + return MODE_NOCLOCK; + + ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo); + if (ret < 0) + return MODE_CLOCK_RANGE; + + return MODE_OK; +} + +static bool sdi_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); + unsigned long pixelclock = mode->clock * 1000; + struct dispc_clock_info dispc_cinfo; + unsigned long fck; + unsigned long pck; + int ret; + + ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo); + if (ret < 0) + return false; + + pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div; + + if (pck != pixelclock) + dev_dbg(&sdi->pdev->dev, + "pixel clock adjusted from %lu Hz to %lu Hz\n", + pixelclock, pck); + + adjusted_mode->clock = pck / 1000; + + return true; +} + +static void sdi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); + + sdi->pixelclock = adjusted_mode->clock * 1000; +} + +static void sdi_bridge_enable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) +{ + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); struct dispc_clock_info dispc_cinfo; unsigned long fck; int r; @@ -181,9 +258,10 @@ err_get_dispc: regulator_disable(sdi->vdds_sdi_reg); } -static void sdi_display_disable(struct omap_dss_device *dssdev) +static void sdi_bridge_disable(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state) { - struct sdi_device *sdi = dssdev_to_sdi(dssdev); + struct sdi_device *sdi = drm_bridge_to_sdi(bridge); dss_mgr_disable(&sdi->output); @@ -194,71 +272,40 @@ static void sdi_display_disable(struct omap_dss_device *dssdev) regulator_disable(sdi->vdds_sdi_reg); } -static void sdi_set_timings(struct omap_dss_device *dssdev, - const struct drm_display_mode *mode) -{ - struct sdi_device *sdi = dssdev_to_sdi(dssdev); - - sdi->pixelclock = mode->clock * 1000; -} - -static int sdi_check_timings(struct omap_dss_device *dssdev, - struct drm_display_mode *mode) -{ - struct sdi_device *sdi = dssdev_to_sdi(dssdev); - struct dispc_clock_info dispc_cinfo; - unsigned long pixelclock = mode->clock * 1000; - unsigned long fck; - unsigned long pck; - int r; - - if (pixelclock == 0) - return -EINVAL; - - r = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo); - if (r) - return r; - - pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div; - - if (pck != pixelclock) { - DSSWARN("Pixel clock adjusted from %lu Hz to %lu Hz\n", - pixelclock, pck); - - mode->clock = pck / 1000; - } - - return 0; -} - -static int sdi_connect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - return omapdss_device_connect(dst->dss, dst, dst->next); -} - -static void sdi_disconnect(struct omap_dss_device *src, - struct omap_dss_device *dst) -{ - omapdss_device_disconnect(dst, dst->next); -} - -static const struct omap_dss_device_ops sdi_ops = { - .connect = sdi_connect, - .disconnect = sdi_disconnect, - - .enable = sdi_display_enable, - .disable = sdi_display_disable, - - .check_timings = sdi_check_timings, - .set_timings = sdi_set_timings, +static const struct drm_bridge_funcs sdi_bridge_funcs = { + .attach = sdi_bridge_attach, + .mode_valid = sdi_bridge_mode_valid, + .mode_fixup = sdi_bridge_mode_fixup, + .mode_set = sdi_bridge_mode_set, + .atomic_enable = sdi_bridge_enable, + .atomic_disable = sdi_bridge_disable, }; +static void sdi_bridge_init(struct sdi_device *sdi) +{ + sdi->bridge.funcs = &sdi_bridge_funcs; + sdi->bridge.of_node = sdi->pdev->dev.of_node; + sdi->bridge.type = DRM_MODE_CONNECTOR_LVDS; + + drm_bridge_add(&sdi->bridge); +} + +static void sdi_bridge_cleanup(struct sdi_device *sdi) +{ + drm_bridge_remove(&sdi->bridge); +} + +/* ----------------------------------------------------------------------------- + * Initialisation and Cleanup + */ + static int sdi_init_output(struct sdi_device *sdi) { struct omap_dss_device *out = &sdi->output; int r; + sdi_bridge_init(sdi); + out->dev = &sdi->pdev->dev; out->id = OMAP_DSS_OUTPUT_SDI; out->type = OMAP_DISPLAY_TYPE_SDI; @@ -266,14 +313,15 @@ static int sdi_init_output(struct sdi_device *sdi) out->dispc_channel = OMAP_DSS_CHANNEL_LCD; /* We have SDI only on OMAP3, where it's on port 1 */ out->of_port = 1; - out->ops = &sdi_ops; out->owner = THIS_MODULE; out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE /* 15.5.9.1.2 */ | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE; - r = omapdss_device_init_output(out, NULL); - if (r < 0) + r = omapdss_device_init_output(out, &sdi->bridge); + if (r < 0) { + sdi_bridge_cleanup(sdi); return r; + } omapdss_device_register(out); @@ -284,6 +332,8 @@ static void sdi_uninit_output(struct sdi_device *sdi) { omapdss_device_unregister(&sdi->output); omapdss_device_cleanup_output(&sdi->output); + + sdi_bridge_cleanup(sdi); } int sdi_init_port(struct dss_device *dss, struct platform_device *pdev, From 5d79598ba8444fffaf04b93dfb696fdb6eb24c06 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:11 +0200 Subject: [PATCH 67/78] drm/omap: Hardcode omap_connector type to DSI The omap_connector implementation is now used for DSI only. Hardcode its type and drop unused code. Signed-off-by: Laurent Pinchart Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Reviewed-by: Tomi Valkeinen Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-52-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/base.c | 23 ------------------ drivers/gpu/drm/omapdrm/dss/omapdss.h | 1 - drivers/gpu/drm/omapdrm/omap_connector.c | 31 ++---------------------- 3 files changed, 2 insertions(+), 53 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/base.c b/drivers/gpu/drm/omapdrm/dss/base.c index 2db3bd2f19db..455b410f7401 100644 --- a/drivers/gpu/drm/omapdrm/dss/base.c +++ b/drivers/gpu/drm/omapdrm/dss/base.c @@ -286,29 +286,6 @@ void omapdss_device_post_disable(struct omap_dss_device *dssdev) } EXPORT_SYMBOL_GPL(omapdss_device_post_disable); -unsigned int omapdss_device_connector_type(enum omap_display_type type) -{ - switch (type) { - case OMAP_DISPLAY_TYPE_HDMI: - return DRM_MODE_CONNECTOR_HDMIA; - case OMAP_DISPLAY_TYPE_DVI: - return DRM_MODE_CONNECTOR_DVID; - case OMAP_DISPLAY_TYPE_DSI: - return DRM_MODE_CONNECTOR_DSI; - case OMAP_DISPLAY_TYPE_DPI: - case OMAP_DISPLAY_TYPE_DBI: - return DRM_MODE_CONNECTOR_DPI; - case OMAP_DISPLAY_TYPE_VENC: - /* TODO: This could also be composite */ - return DRM_MODE_CONNECTOR_SVIDEO; - case OMAP_DISPLAY_TYPE_SDI: - return DRM_MODE_CONNECTOR_LVDS; - default: - return DRM_MODE_CONNECTOR_Unknown; - } -} -EXPORT_SYMBOL_GPL(omapdss_device_connector_type); - /* ----------------------------------------------------------------------------- * Components Handling */ diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index cb79e05c902d..2e5453df2293 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -454,7 +454,6 @@ void omapdss_device_pre_enable(struct omap_dss_device *dssdev); void omapdss_device_enable(struct omap_dss_device *dssdev); void omapdss_device_disable(struct omap_dss_device *dssdev); void omapdss_device_post_disable(struct omap_dss_device *dssdev); -unsigned int omapdss_device_connector_type(enum omap_display_type type); int omap_dss_get_num_overlay_managers(void); diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index baa31ed1f993..528764566b17 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -24,22 +24,7 @@ struct omap_connector { static enum drm_connector_status omap_connector_detect( struct drm_connector *connector, bool force) { - enum drm_connector_status status; - - switch (connector->connector_type) { - case DRM_MODE_CONNECTOR_DPI: - case DRM_MODE_CONNECTOR_LVDS: - case DRM_MODE_CONNECTOR_DSI: - status = connector_status_connected; - break; - default: - status = connector_status_unknown; - break; - } - - VERB("%s: %d (force=%d)", connector->name, status, force); - - return status; + return connector_status_connected; } static void omap_connector_destroy(struct drm_connector *connector) @@ -138,18 +123,6 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { .mode_valid = omap_connector_mode_valid, }; -static int omap_connector_get_type(struct omap_dss_device *output) -{ - struct omap_dss_device *display; - enum omap_display_type type; - - display = omapdss_display_get(output); - type = display->type; - omapdss_device_put(display); - - return omapdss_device_connector_type(type); -} - /* initialize connector */ struct drm_connector *omap_connector_init(struct drm_device *dev, struct omap_dss_device *output, @@ -171,7 +144,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev, connector->doublescan_allowed = 0; drm_connector_init(dev, connector, &omap_connector_funcs, - omap_connector_get_type(output)); + DRM_MODE_CONNECTOR_DSI); drm_connector_helper_add(connector, &omap_connector_helper_funcs); return connector; From 263a983eb26a550314448ddce7a0fd5caa08c140 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:12 +0200 Subject: [PATCH 68/78] drm/omap: dss: Inline the omapdss_display_get() function Inline the omapdss_display_get() in its only caller to simplify the code. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-53-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/display.c | 9 --------- drivers/gpu/drm/omapdrm/dss/omapdss.h | 1 - drivers/gpu/drm/omapdrm/omap_drv.c | 7 ++++--- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/display.c b/drivers/gpu/drm/omapdrm/dss/display.c index 8a3f61f5825f..3b82158b1bfd 100644 --- a/drivers/gpu/drm/omapdrm/dss/display.c +++ b/drivers/gpu/drm/omapdrm/dss/display.c @@ -40,15 +40,6 @@ void omapdss_display_init(struct omap_dss_device *dssdev) } EXPORT_SYMBOL_GPL(omapdss_display_init); -struct omap_dss_device *omapdss_display_get(struct omap_dss_device *output) -{ - while (output->next) - output = output->next; - - return omapdss_device_get(output); -} -EXPORT_SYMBOL_GPL(omapdss_display_get); - int omapdss_display_get_modes(struct drm_connector *connector, const struct videomode *vm) { diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 2e5453df2293..315a356fa0c0 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -436,7 +436,6 @@ static inline bool omapdss_is_initialized(void) } void omapdss_display_init(struct omap_dss_device *dssdev); -struct omap_dss_device *omapdss_display_get(struct omap_dss_device *output); int omapdss_display_get_modes(struct drm_connector *connector, const struct videomode *vm); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index e6a065030523..cdafd7ef1c32 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -207,11 +207,12 @@ static int omap_display_id(struct omap_dss_device *output) struct device_node *node = NULL; if (output->next) { - struct omap_dss_device *display; + struct omap_dss_device *display = output; + + while (display->next) + display = display->next; - display = omapdss_display_get(output); node = display->dev->of_node; - omapdss_device_put(display); } else if (output->bridge) { struct drm_bridge *bridge = output->bridge; From 984f6c91d38a80f4ae2ff03489c2ac8a542bf6a8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:13 +0200 Subject: [PATCH 69/78] drm/omap: dss: Remove unused omapdss_of_find_connected_device() function The omapdss_of_find_connected_device() function isn't used anymore, remove it. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-54-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/Makefile | 2 +- drivers/gpu/drm/omapdrm/dss/dss-of.c | 28 --------------------------- drivers/gpu/drm/omapdrm/dss/omapdss.h | 3 --- 3 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 drivers/gpu/drm/omapdrm/dss/dss-of.c diff --git a/drivers/gpu/drm/omapdrm/dss/Makefile b/drivers/gpu/drm/omapdrm/dss/Makefile index 5950c3f52c2e..f967e6948f2e 100644 --- a/drivers/gpu/drm/omapdrm/dss/Makefile +++ b/drivers/gpu/drm/omapdrm/dss/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o obj-$(CONFIG_OMAP_DSS_BASE) += omapdss-base.o -omapdss-base-y := base.o display.o dss-of.o output.o +omapdss-base-y := base.o display.o output.o obj-$(CONFIG_OMAP2_DSS) += omapdss.o # Core DSS files diff --git a/drivers/gpu/drm/omapdrm/dss/dss-of.c b/drivers/gpu/drm/omapdrm/dss/dss-of.c deleted file mode 100644 index b7981f3b80ad..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/dss-of.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ - * Author: Tomi Valkeinen - */ - -#include -#include -#include - -#include "omapdss.h" - -struct omap_dss_device * -omapdss_of_find_connected_device(struct device_node *node, unsigned int port) -{ - struct device_node *remote_node; - struct omap_dss_device *dssdev; - - remote_node = of_graph_get_remote_node(node, port, 0); - if (!remote_node) - return NULL; - - dssdev = omapdss_find_device_by_node(remote_node); - of_node_put(remote_node); - - return dssdev ? dssdev : ERR_PTR(-EPROBE_DEFER); -} -EXPORT_SYMBOL_GPL(omapdss_of_find_connected_device); diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 315a356fa0c0..cbbe10b2b60d 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -477,9 +477,6 @@ static inline bool omapdss_device_is_enabled(struct omap_dss_device *dssdev) return dssdev->state == OMAP_DSS_DISPLAY_ACTIVE; } -struct omap_dss_device * -omapdss_of_find_connected_device(struct device_node *node, unsigned int port); - enum dss_writeback_channel { DSS_WB_LCD1_MGR = 0, DSS_WB_LCD2_MGR = 1, From e086558ae923adc71c9718140ee0f6ba9508ec30 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Feb 2020 13:25:14 +0200 Subject: [PATCH 70/78] drm/omap: dss: Remove unused omap_dss_device operations The omap_dss_device .pre_enable(), .post_disable() and .set_timings() are not used anymore. Remove them. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Tested-by: Sebastian Reichel Reviewed-by: Sebastian Reichel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-55-laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/base.c | 26 --------------- drivers/gpu/drm/omapdrm/dss/omapdss.h | 6 ---- drivers/gpu/drm/omapdrm/omap_encoder.c | 44 +++----------------------- 3 files changed, 5 insertions(+), 71 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/base.c b/drivers/gpu/drm/omapdrm/dss/base.c index 455b410f7401..c7650a7c155d 100644 --- a/drivers/gpu/drm/omapdrm/dss/base.c +++ b/drivers/gpu/drm/omapdrm/dss/base.c @@ -234,18 +234,6 @@ void omapdss_device_disconnect(struct omap_dss_device *src, } EXPORT_SYMBOL_GPL(omapdss_device_disconnect); -void omapdss_device_pre_enable(struct omap_dss_device *dssdev) -{ - if (!dssdev) - return; - - omapdss_device_pre_enable(dssdev->next); - - if (dssdev->ops && dssdev->ops->pre_enable) - dssdev->ops->pre_enable(dssdev); -} -EXPORT_SYMBOL_GPL(omapdss_device_pre_enable); - void omapdss_device_enable(struct omap_dss_device *dssdev) { if (!dssdev) @@ -272,20 +260,6 @@ void omapdss_device_disable(struct omap_dss_device *dssdev) } EXPORT_SYMBOL_GPL(omapdss_device_disable); -void omapdss_device_post_disable(struct omap_dss_device *dssdev) -{ - if (!dssdev) - return; - - if (dssdev->ops && dssdev->ops->post_disable) - dssdev->ops->post_disable(dssdev); - - omapdss_device_post_disable(dssdev->next); - - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; -} -EXPORT_SYMBOL_GPL(omapdss_device_post_disable); - /* ----------------------------------------------------------------------------- * Components Handling */ diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index cbbe10b2b60d..ab19d4af8de7 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -342,15 +342,11 @@ struct omap_dss_device_ops { void (*disconnect)(struct omap_dss_device *dssdev, struct omap_dss_device *dst); - void (*pre_enable)(struct omap_dss_device *dssdev); void (*enable)(struct omap_dss_device *dssdev); void (*disable)(struct omap_dss_device *dssdev); - void (*post_disable)(struct omap_dss_device *dssdev); int (*check_timings)(struct omap_dss_device *dssdev, struct drm_display_mode *mode); - void (*set_timings)(struct omap_dss_device *dssdev, - const struct drm_display_mode *mode); int (*get_modes)(struct omap_dss_device *dssdev, struct drm_connector *connector); @@ -449,10 +445,8 @@ int omapdss_device_connect(struct dss_device *dss, struct omap_dss_device *dst); void omapdss_device_disconnect(struct omap_dss_device *src, struct omap_dss_device *dst); -void omapdss_device_pre_enable(struct omap_dss_device *dssdev); void omapdss_device_enable(struct omap_dss_device *dssdev); void omapdss_device_disable(struct omap_dss_device *dssdev); -void omapdss_device_post_disable(struct omap_dss_device *dssdev); int omap_dss_get_num_overlay_managers(void); diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 18a79dde6815..ae4b867a67a3 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -113,13 +113,8 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder, bus_flags = connector->display_info.bus_flags; omap_encoder_update_videomode_flags(&vm, bus_flags); - /* Set timings for all devices in the display pipeline. */ + /* Set timings for the dss manager. */ dss_mgr_set_timings(output, &vm); - - for (dssdev = output; dssdev; dssdev = dssdev->next) { - if (dssdev->ops && dssdev->ops->set_timings) - dssdev->ops->set_timings(dssdev, adjusted_mode); - } } static void omap_encoder_disable(struct drm_encoder *encoder) @@ -132,26 +127,10 @@ static void omap_encoder_disable(struct drm_encoder *encoder) /* * Disable the chain of external devices, starting at the one at the - * internal encoder's output. + * internal encoder's output. This is used for DSI outputs only, as + * dssdev->next is NULL for all other outputs. */ omapdss_device_disable(dssdev->next); - - /* - * Disable the internal encoder. This will disable the DSS output. The - * DSI is treated as an exception as DSI pipelines still use the legacy - * flow where the pipeline output controls the encoder. - */ - if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) { - if (dssdev->ops && dssdev->ops->disable) - dssdev->ops->disable(dssdev); - dssdev->state = OMAP_DSS_DISPLAY_DISABLED; - } - - /* - * Perform the post-disable operations on the chain of external devices - * to complete the display pipeline disable. - */ - omapdss_device_post_disable(dssdev->next); } static void omap_encoder_enable(struct drm_encoder *encoder) @@ -162,23 +141,10 @@ static void omap_encoder_enable(struct drm_encoder *encoder) dev_dbg(dev->dev, "enable(%s)\n", dssdev->name); - /* Prepare the chain of external devices for pipeline enable. */ - omapdss_device_pre_enable(dssdev->next); - - /* - * Enable the internal encoder. This will enable the DSS output. The - * DSI is treated as an exception as DSI pipelines still use the legacy - * flow where the pipeline output controls the encoder. - */ - if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) { - if (dssdev->ops && dssdev->ops->enable) - dssdev->ops->enable(dssdev); - dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - } - /* * Enable the chain of external devices, starting at the one at the - * internal encoder's output. + * internal encoder's output. This is used for DSI outputs only, as + * dssdev->next is NULL for all other outputs. */ omapdss_device_enable(dssdev->next); } From bac511833da98ebffa342f790f66f2df0b964f3c Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Tue, 31 Dec 2019 14:42:51 +0800 Subject: [PATCH 71/78] drm/hisilicon: Add new clock/resolution configurations Add the three new pll config for corresponding resolution 1440x900 and 1600x900, 640x480 for hibmc Signed-off-by: Tian Tao Signed-off-by: Gong junjie Signed-off-by: Xinliang Liu Link: https://patchwork.freedesktop.org/patch/msgid/1577774571-60493-1-git-send-email-tiantao6@hisilicon.com --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c | 3 +++ drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index 561b398ca93d..66ed6b0441e6 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -40,6 +40,7 @@ struct hibmc_dislay_pll_config { }; static const struct hibmc_dislay_pll_config hibmc_pll_table[] = { + {640, 480, CRT_PLL1_HS_25MHZ, CRT_PLL2_HS_25MHZ}, {800, 600, CRT_PLL1_HS_40MHZ, CRT_PLL2_HS_40MHZ}, {1024, 768, CRT_PLL1_HS_65MHZ, CRT_PLL2_HS_65MHZ}, {1152, 864, CRT_PLL1_HS_80MHZ_1152, CRT_PLL2_HS_80MHZ}, @@ -47,6 +48,8 @@ static const struct hibmc_dislay_pll_config hibmc_pll_table[] = { {1280, 720, CRT_PLL1_HS_74MHZ, CRT_PLL2_HS_74MHZ}, {1280, 960, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, {1280, 1024, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, + {1440, 900, CRT_PLL1_HS_106MHZ, CRT_PLL2_HS_106MHZ}, + {1600, 900, CRT_PLL1_HS_108MHZ, CRT_PLL2_HS_108MHZ}, {1600, 1200, CRT_PLL1_HS_162MHZ, CRT_PLL2_HS_162MHZ}, {1920, 1080, CRT_PLL1_HS_148MHZ, CRT_PLL2_HS_148MHZ}, {1920, 1200, CRT_PLL1_HS_193MHZ, CRT_PLL2_HS_193MHZ}, diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h index 9b7e85947113..17b30c393b10 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_regs.h @@ -179,6 +179,7 @@ #define CRT_PLL1_HS_74MHZ 0x23941dc2 #define CRT_PLL1_HS_80MHZ 0x23941001 #define CRT_PLL1_HS_80MHZ_1152 0x23540fc2 +#define CRT_PLL1_HS_106MHZ 0x237C1641 #define CRT_PLL1_HS_108MHZ 0x23b41b01 #define CRT_PLL1_HS_162MHZ 0x23480681 #define CRT_PLL1_HS_148MHZ 0x23541dc2 @@ -191,6 +192,7 @@ #define CRT_PLL2_HS_78MHZ 0x50E147AE #define CRT_PLL2_HS_74MHZ 0x602B6AE7 #define CRT_PLL2_HS_80MHZ 0x70000000 +#define CRT_PLL2_HS_106MHZ 0x0075c28f #define CRT_PLL2_HS_108MHZ 0x80000000 #define CRT_PLL2_HS_162MHZ 0xA0000000 #define CRT_PLL2_HS_148MHZ 0xB0CCCCCD From 31d6feebec8e502d3a8e58beeef96e90d836e70b Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Sat, 11 Jan 2020 11:09:06 +0800 Subject: [PATCH 72/78] drm/hisilicon: Enable the shadowfb for hibmc set the prefer_shadow as 1,so we use the shadowfb to acceleration. Signed-off-by: Tian Tao Signed-off-by: Gong junjie Signed-off-by: Xinliang Liu Link: https://patchwork.freedesktop.org/patch/msgid/1578712146-16327-1-git-send-email-tiantao6@hisilicon.com --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 4a8a4cfb4b75..5f612f6ac223 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -95,7 +95,7 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) priv->dev->mode_config.fb_base = priv->fb_base; priv->dev->mode_config.preferred_depth = 24; - priv->dev->mode_config.prefer_shadow = 0; + priv->dev->mode_config.prefer_shadow = 1; priv->dev->mode_config.funcs = (void *)&hibmc_mode_funcs; From de3c3ed4c8ff2e8a5a5f9784f1d727e8dd8ed168 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Fri, 3 Jan 2020 11:55:56 +0800 Subject: [PATCH 73/78] drm/hisilicon: fixed the wrong resolution configurations The maximum resolution supported by hibmc is 1920 * 1200 instead of 1920 * 1440, this patch fixed this problem Signed-off-by: Tian Tao Signed-off-by: Gong junjie Signed-off-by: Xinliang Liu Link: https://patchwork.freedesktop.org/patch/msgid/1578023756-46567-1-git-send-email-tiantao6@hisilicon.com --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 5f612f6ac223..119135fc3e1a 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -91,7 +91,7 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) priv->dev->mode_config.min_width = 0; priv->dev->mode_config.min_height = 0; priv->dev->mode_config.max_width = 1920; - priv->dev->mode_config.max_height = 1440; + priv->dev->mode_config.max_height = 1200; priv->dev->mode_config.fb_base = priv->fb_base; priv->dev->mode_config.preferred_depth = 24; From bc17e557495429ac8f8c1e68a8f0e7adefdb793b Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Wed, 8 Jan 2020 17:41:41 +0800 Subject: [PATCH 74/78] drm/hisilicon: Add the mode_valid function add mode_valid function, we can make sure the resolution is valid. Signed-off-by: Tian Tao Signed-off-by: Gong junjie Reviewed-by: Thomas Zimmermann Signed-off-by: Xinliang Liu Link: https://patchwork.freedesktop.org/patch/msgid/1578476501-45807-1-git-send-email-tiantao6@hisilicon.com --- .../gpu/drm/hisilicon/hibmc/hibmc_drm_de.c | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index 66ed6b0441e6..55b46a7150a5 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -243,6 +243,25 @@ static void hibmc_crtc_atomic_disable(struct drm_crtc *crtc, hibmc_set_current_gate(priv, reg); } +static enum drm_mode_status +hibmc_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + int i = 0; + int vrefresh = drm_mode_vrefresh(mode); + + if (vrefresh < 59 || vrefresh > 61) + return MODE_NOCLOCK; + + for (i = 0; i < ARRAY_SIZE(hibmc_pll_table); i++) { + if (hibmc_pll_table[i].hdisplay == mode->hdisplay && + hibmc_pll_table[i].vdisplay == mode->vdisplay) + return MODE_OK; + } + + return MODE_BAD; +} + static unsigned int format_pll_reg(void) { unsigned int pllreg = 0; @@ -511,6 +530,7 @@ static const struct drm_crtc_helper_funcs hibmc_crtc_helper_funcs = { .atomic_flush = hibmc_crtc_atomic_flush, .atomic_enable = hibmc_crtc_atomic_enable, .atomic_disable = hibmc_crtc_atomic_disable, + .mode_valid = hibmc_crtc_mode_valid, }; int hibmc_de_init(struct hibmc_drm_private *priv) From 29b7216b57f17293848c94590fce6d783df3d0f1 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Wed, 19 Feb 2020 10:51:47 +0800 Subject: [PATCH 75/78] drm/hisilicon: Set preferred mode resolution and maximum resolution set the preferred mode resolution to 1024 * 768 and maximum resolution to 1920 * 1200. Signed-off-by: Tian Tao Signed-off-by: Gong junjie Signed-off-by: Xinliang Liu Link: https://patchwork.freedesktop.org/patch/msgid/1582080707-18825-1-git-send-email-tiantao6@hisilicon.com --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 6d98fdc06f6c..678ac2ef2a93 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -11,8 +11,10 @@ * Jianhua Li */ +#include #include #include +#include #include #include "hibmc_drm_drv.h" @@ -20,7 +22,14 @@ static int hibmc_connector_get_modes(struct drm_connector *connector) { - return drm_add_modes_noedid(connector, 800, 600); + int count; + + count = drm_add_modes_noedid(connector, + connector->dev->mode_config.max_width, + connector->dev->mode_config.max_height); + drm_set_preferred_mode(connector, 1024, 768); + + return count; } static enum drm_mode_status hibmc_connector_mode_valid(struct drm_connector *connector, From c3480301d750bd58bab35994ecf015a4682a17dd Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 24 Feb 2020 14:01:52 +0800 Subject: [PATCH 76/78] drm/hisilicon: Fixed pcie resource conflict between drm and firmware use the drm_fb_helper_remove_conflicting_pci_framebuffer to remove the framebuffer initialized by fireware/bootloader to avoid resource conflict. Signed-off-by: Tian Tao Acked-by: Thomas Zimmermann Signed-off-by: Xinliang Liu Link: https://patchwork.freedesktop.org/patch/msgid/1582524112-5628-1-git-send-email-tiantao6@hisilicon.com --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 119135fc3e1a..222356a4f9a8 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -327,6 +327,11 @@ static int hibmc_pci_probe(struct pci_dev *pdev, struct drm_device *dev; int ret; + ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, + "hibmcdrmfb"); + if (ret) + return ret; + dev = drm_dev_alloc(&hibmc_driver, &pdev->dev); if (IS_ERR(dev)) { DRM_ERROR("failed to allocate drm_device\n"); From 068a8fea373fe79016abf327d88eb733cca8f6a7 Mon Sep 17 00:00:00 2001 From: Gurchetan Singh Date: Wed, 26 Feb 2020 16:25:54 -0800 Subject: [PATCH 77/78] drm/virtio: make mmap callback consistent with callbacks This is a very, very minor cleanup. Signed-off-by: Gurchetan Singh Link: http://patchwork.freedesktop.org/patch/msgid/20200227002601.745-2-gurchetansingh@chromium.org Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 3d2a6d489bfc..07de3260118a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -106,7 +106,7 @@ static const struct drm_gem_object_funcs virtio_gpu_gem_funcs = { .get_sg_table = drm_gem_shmem_get_sg_table, .vmap = drm_gem_shmem_vmap, .vunmap = drm_gem_shmem_vunmap, - .mmap = &drm_gem_shmem_mmap, + .mmap = drm_gem_shmem_mmap, }; struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev, From 18b39fb975b79d6526449ca7c8ab504bc92e2825 Mon Sep 17 00:00:00 2001 From: Gurchetan Singh Date: Wed, 26 Feb 2020 16:25:55 -0800 Subject: [PATCH 78/78] drm/virtio: add virtio_gpu_is_shmem helper The plan is use have both shmem and virtual "vram" running side-by-side in virtio-gpu. It looks like we'll eventually use struct drm_gem_object as a base class, and we'll need to convert to shmem and vram objects on the fly. As a first step, add a virtio_gpu_is_shmem helper. Thanks to kraxel for suggesting this approach on Gitlab. Suggested-by: Gerd Hoffman Signed-off-by: Gurchetan Singh Link: http://patchwork.freedesktop.org/patch/msgid/20200227002601.745-3-gurchetansingh@chromium.org Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_drv.h | 3 +++ drivers/gpu/drm/virtio/virtgpu_object.c | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 95a7443baaba..ce73895cf74b 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -365,6 +365,9 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, struct virtio_gpu_object_params *params, struct virtio_gpu_object **bo_ptr, struct virtio_gpu_fence *fence); + +bool virtio_gpu_is_shmem(struct drm_gem_object *obj); + /* virtgpu_prime.c */ struct drm_gem_object *virtgpu_gem_prime_import_sg_table( struct drm_device *dev, struct dma_buf_attachment *attach, diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 07de3260118a..c5cad949eb8d 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -95,7 +95,7 @@ static void virtio_gpu_free_object(struct drm_gem_object *obj) virtio_gpu_cleanup_object(bo); } -static const struct drm_gem_object_funcs virtio_gpu_gem_funcs = { +static const struct drm_gem_object_funcs virtio_gpu_shmem_funcs = { .free = virtio_gpu_free_object, .open = virtio_gpu_gem_object_open, .close = virtio_gpu_gem_object_close, @@ -109,6 +109,11 @@ static const struct drm_gem_object_funcs virtio_gpu_gem_funcs = { .mmap = drm_gem_shmem_mmap, }; +bool virtio_gpu_is_shmem(struct drm_gem_object *obj) +{ + return obj->funcs == &virtio_gpu_shmem_funcs; +} + struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev, size_t size) { @@ -118,7 +123,7 @@ struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev, if (!bo) return NULL; - bo->base.base.funcs = &virtio_gpu_gem_funcs; + bo->base.base.funcs = &virtio_gpu_shmem_funcs; return &bo->base.base; }