From 44b6535d8acebbbdb8b8e79b22118bb3d7fa93df Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Thu, 1 May 2025 16:24:19 -0700 Subject: [PATCH 001/279] drm/virtio: Fix NULL pointer deref in virtgpu_dma_buf_free_obj() There is a chance that obj->dma_buf would be NULL by the time virtgpu_dma_buf_free_obj() is called. This can happen for imported prime objects, when drm_gem_object_exported_dma_buf_free() gets called on them before drm_gem_object_free(). This is because drm_gem_object_exported_dma_buf_free() explicitly sets obj->dma_buf to NULL. Therefore, fix this issue by storing the dma_buf pointer in the virtio_gpu_object instance and using it in virtgpu_dma_buf_free_obj. This stored pointer is guaranteed to be valid until the object is freed as we took a reference on it in virtgpu_gem_prime_import(). Fixes: 415cb45895f4 ("drm/virtio: Use dma_buf from GEM object instance") Cc: Dmitry Osipenko Cc: Thomas Zimmermann Signed-off-by: Vivek Kasireddy Reviewed-by: Dmitry Osipenko Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20250501232419.180337-1-vivek.kasireddy@intel.com --- drivers/gpu/drm/virtio/virtgpu_drv.h | 1 + drivers/gpu/drm/virtio/virtgpu_prime.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index f17660a71a3e..f7def8b42068 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -88,6 +88,7 @@ struct virtio_gpu_object_params { struct virtio_gpu_object { struct drm_gem_shmem_object base; + struct dma_buf *dma_buf; struct sg_table *sgt; uint32_t hw_res_handle; bool dumb; diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c index 1118a0250279..722cde5e2d86 100644 --- a/drivers/gpu/drm/virtio/virtgpu_prime.c +++ b/drivers/gpu/drm/virtio/virtgpu_prime.c @@ -206,7 +206,7 @@ static void virtgpu_dma_buf_free_obj(struct drm_gem_object *obj) struct virtio_gpu_device *vgdev = obj->dev->dev_private; if (drm_gem_is_imported(obj)) { - struct dma_buf *dmabuf = obj->dma_buf; + struct dma_buf *dmabuf = bo->dma_buf; dma_resv_lock(dmabuf->resv, NULL); virtgpu_dma_buf_unmap(bo); @@ -332,6 +332,7 @@ struct drm_gem_object *virtgpu_gem_prime_import(struct drm_device *dev, obj->import_attach = attach; get_dma_buf(buf); + bo->dma_buf = buf; ret = virtgpu_dma_buf_init_obj(dev, bo, attach); if (ret < 0) From 46462c4e0fa469ca83e0bfea463ab3d14147e8d1 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Wed, 7 May 2025 09:51:47 +0200 Subject: [PATCH 002/279] MAINTAINERS: Add entries for drm_panic, drm_panic_qr_code and drm_log Add myself and Javier as maintainer for drm_panic, drm_panic_qr_code and drm_log. Acked-by: Thomas Zimmermann Acked-by: Javier Martinez Canillas Link: https://lore.kernel.org/r/20250507075529.263355-1-jfalempe@redhat.com Signed-off-by: Jocelyn Falempe --- MAINTAINERS | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fe9773af465a..ef7f0d002a09 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8172,6 +8172,14 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: drivers/gpu/drm/scheduler/ F: include/drm/gpu_scheduler.h +DRM LOG +M: Jocelyn Falempe +M: Javier Martinez Canillas +L: dri-devel@lists.freedesktop.org +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/clients/drm_log.c + DRM PANEL DRIVERS M: Neil Armstrong R: Jessica Zhang @@ -8183,6 +8191,26 @@ F: drivers/gpu/drm/drm_panel.c F: drivers/gpu/drm/panel/ F: include/drm/drm_panel.h +DRM PANIC +M: Jocelyn Falempe +M: Javier Martinez Canillas +L: dri-devel@lists.freedesktop.org +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/drm_draw.c +F: drivers/gpu/drm/drm_draw_internal.h +F: drivers/gpu/drm/drm_panic*.c +F: include/drm/drm_panic* + +DRM PANIC QR CODE +M: Jocelyn Falempe +M: Javier Martinez Canillas +L: dri-devel@lists.freedesktop.org +L: rust-for-linux@vger.kernel.org +S: Supported +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: drivers/gpu/drm/drm_panic_qr.rs + DRM PRIVACY-SCREEN CLASS M: Hans de Goede L: dri-devel@lists.freedesktop.org From 1773ea5caf0b6640aada70411eba6d33fef8e1d5 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Fri, 14 Mar 2025 11:10:23 +0100 Subject: [PATCH 003/279] drm/sched: Fix outdated comments referencing thread The GPU scheduler's comments refer to a "thread" at various places. Those are leftovers from commit a6149f039369 ("drm/sched: Convert drm scheduler to use a work queue rather than kthread"). Replace all references to kthreads. Reviewed-by: Tvrtko Ursulin Signed-off-by: Philipp Stanner Link: https://lore.kernel.org/r/20250314101023.111248-2-phasta@kernel.org --- drivers/gpu/drm/scheduler/sched_entity.c | 8 ++++---- drivers/gpu/drm/scheduler/sched_main.c | 24 +++++++++++++----------- include/drm/gpu_scheduler.h | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index bd39db7bb240..a9ec77a10d93 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -545,10 +545,10 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity) return; /* - * Only when the queue is empty are we guaranteed that the scheduler - * thread cannot change ->last_scheduled. To enforce ordering we need - * a read barrier here. See drm_sched_entity_pop_job() for the other - * side. + * Only when the queue is empty are we guaranteed that + * drm_sched_run_job_work() cannot change entity->last_scheduled. To + * enforce ordering we need a read barrier here. See + * drm_sched_entity_pop_job() for the other side. */ smp_rmb(); diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 829579c41c6b..f7118497e47a 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -391,7 +391,7 @@ static void drm_sched_run_free_queue(struct drm_gpu_scheduler *sched) * drm_sched_job_done - complete a job * @s_job: pointer to the job which is done * - * Finish the job's fence and wake up the worker thread. + * Finish the job's fence and resubmit the work items. */ static void drm_sched_job_done(struct drm_sched_job *s_job, int result) { @@ -551,9 +551,10 @@ static void drm_sched_job_timedout(struct work_struct *work) if (job) { /* - * Remove the bad job so it cannot be freed by concurrent - * drm_sched_cleanup_jobs. It will be reinserted back after sched->thread - * is parked at which point it's safe. + * Remove the bad job so it cannot be freed by a concurrent + * &struct drm_sched_backend_ops.free_job. It will be + * reinserted after the scheduler's work items have been + * cancelled, at which point it's safe. */ list_del_init(&job->list); spin_unlock(&sched->job_list_lock); @@ -599,10 +600,10 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad) /* * Reinsert back the bad job here - now it's safe as - * drm_sched_get_finished_job cannot race against us and release the + * drm_sched_get_finished_job() cannot race against us and release the * bad job at this point - we parked (waited for) any in progress - * (earlier) cleanups and drm_sched_get_finished_job will not be called - * now until the scheduler thread is unparked. + * (earlier) cleanups and drm_sched_get_finished_job() will not be + * called now until the scheduler's work items are submitted again. */ if (bad && bad->sched == sched) /* @@ -615,7 +616,8 @@ void drm_sched_stop(struct drm_gpu_scheduler *sched, struct drm_sched_job *bad) * Iterate the job list from later to earlier one and either deactive * their HW callbacks or remove them from pending list if they already * signaled. - * This iteration is thread safe as sched thread is stopped. + * This iteration is thread safe as the scheduler's work items have been + * cancelled. */ list_for_each_entry_safe_reverse(s_job, tmp, &sched->pending_list, list) { @@ -680,9 +682,9 @@ void drm_sched_start(struct drm_gpu_scheduler *sched, int errno) struct drm_sched_job *s_job, *tmp; /* - * Locking the list is not required here as the sched thread is parked - * so no new jobs are being inserted or removed. Also concurrent - * GPU recovers can't run in parallel. + * Locking the list is not required here as the scheduler's work items + * are currently not running, so no new jobs are being inserted or + * removed. Also concurrent GPU recovers can't run in parallel. */ list_for_each_entry_safe(s_job, tmp, &sched->pending_list, list) { struct dma_fence *fence = s_job->s_fence->parent; diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 1a7e377d4cbb..d860db087ea5 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -192,7 +192,7 @@ struct drm_sched_entity { * @last_scheduled: * * Points to the finished fence of the last scheduled job. Only written - * by the scheduler thread, can be accessed locklessly from + * by drm_sched_entity_pop_job(). Can be accessed locklessly from * drm_sched_job_arm() if the queue is empty. */ struct dma_fence __rcu *last_scheduled; From e33c3f4d935454a6f8a18a5913189f060b9140ef Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Fri, 14 Mar 2025 11:10:24 +0100 Subject: [PATCH 004/279] drm/sched: Remove kthread header The kthread header doesn't need to be included anymore. It's a relict from commit a6149f039369 ("drm/sched: Convert drm scheduler to use a work queue rather than kthread"). Remove the unneeded includes. Reviewed-by: Tvrtko Ursulin Signed-off-by: Philipp Stanner Link: https://lore.kernel.org/r/20250314101023.111248-3-phasta@kernel.org --- drivers/gpu/drm/scheduler/sched_entity.c | 1 - drivers/gpu/drm/scheduler/sched_fence.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index a9ec77a10d93..ff553d8dc5f4 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -21,7 +21,6 @@ * */ -#include #include #include diff --git a/drivers/gpu/drm/scheduler/sched_fence.c b/drivers/gpu/drm/scheduler/sched_fence.c index e971528504a5..d6239e015b66 100644 --- a/drivers/gpu/drm/scheduler/sched_fence.c +++ b/drivers/gpu/drm/scheduler/sched_fence.c @@ -21,7 +21,6 @@ * */ -#include #include #include #include From 64a8d0aa55e98bf2c9b7e1a3df3e8acbfecba619 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Tue, 18 Mar 2025 16:22:35 -0300 Subject: [PATCH 005/279] drm/shmem-helper: Add lockdep asserts to vmap/vunmap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 21aa27ddc582 ("drm/shmem-helper: Switch to reservation lock"), the drm_gem_shmem_vmap and drm_gem_shmem_vunmap functions require that the caller holds the DMA reservation lock for the object. Add lockdep assertions to help validate this. Signed-off-by: Asahi Lina Signed-off-by: Daniel Almeida Reviewed-by: Christian König Signed-off-by: Lyude Paul Link: https://lore.kernel.org/r/20250318-drm-gem-shmem-v1-1-64b96511a84f@collabora.com --- drivers/gpu/drm/drm_gem_shmem_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index aa43265f4f4f..0b41f0346bad 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -348,6 +348,8 @@ int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem, struct drm_gem_object *obj = &shmem->base; int ret = 0; + dma_resv_assert_held(obj->resv); + if (drm_gem_is_imported(obj)) { ret = dma_buf_vmap(obj->dma_buf, map); } else { @@ -408,6 +410,8 @@ void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem, { struct drm_gem_object *obj = &shmem->base; + dma_resv_assert_held(obj->resv); + if (drm_gem_is_imported(obj)) { dma_buf_vunmap(obj->dma_buf, map); } else { From 1f372c1fc6cff841e85913ad2b3b3680e94eabac Mon Sep 17 00:00:00 2001 From: hienhuynh Date: Thu, 8 May 2025 10:50:35 +0100 Subject: [PATCH 006/279] drm: rz-du: Support panels connected directly to the DPAD output This patch is based on the commit 73eb5476df72 ("drm: rcar-du: Support panels connected directly to the DPAD outputs"). The RZ DU driver assumes that a bridge is always connected to the DU output. This is valid for the HDMI output, but the DPAD output can be connected directly to a panel, in which case no bridge is available. To support this use case, detect whether the entities connected to the DU DPAD output is encoders or panels based on the number of ports of their DT node, and retrieve the corresponding type of DRM objects. For panels, additionally create panel bridge instances. Signed-off-by: hienhuynh Signed-off-by: Biju Das Reviewed-by: Tommaso Merciai Tested-by: Tommaso Merciai Link: https://lore.kernel.org/r/20250508095042.25164-1-biju.das.jz@bp.renesas.com --- .../gpu/drm/renesas/rz-du/rzg2l_du_encoder.c | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c index 564ab4cb3d37..5e6dd16705e6 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c @@ -22,6 +22,26 @@ * Encoder */ +static unsigned int rzg2l_du_encoder_count_ports(struct device_node *node) +{ + struct device_node *ports; + struct device_node *port; + unsigned int num_ports = 0; + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = of_node_get(node); + + for_each_child_of_node(ports, port) { + if (of_node_name_eq(port, "port")) + num_ports++; + } + + of_node_put(ports); + + return num_ports; +} + static const struct drm_encoder_funcs rzg2l_du_encoder_funcs = { }; @@ -50,10 +70,26 @@ int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu, struct drm_bridge *bridge; int ret; - /* Locate the DRM bridge from the DT node. */ - bridge = of_drm_find_bridge(enc_node); - if (!bridge) - return -EPROBE_DEFER; + /* + * Locate the DRM bridge from the DT node. For the DPAD outputs, if the + * DT node has a single port, assume that it describes a panel and + * create a panel bridge. + */ + if (output == RZG2L_DU_OUTPUT_DPAD0 && rzg2l_du_encoder_count_ports(enc_node) == 1) { + struct drm_panel *panel = of_drm_find_panel(enc_node); + + if (IS_ERR(panel)) + return PTR_ERR(panel); + + bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + } else { + bridge = of_drm_find_bridge(enc_node); + if (!bridge) + return -EPROBE_DEFER; + } dev_dbg(rcdu->dev, "initializing encoder %pOF for output %s\n", enc_node, rzg2l_du_output_name(output)); From 1c0ff333f2fe69d571798c11f0277985ce146358 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:10 +0800 Subject: [PATCH 007/279] dt-bindings: display: imx: Add i.MX8qxp Display Controller processing units Freescale i.MX8qxp Display Controller is implemented as construction set of building blocks with unified concept and standardized interfaces. Document all existing processing units. Signed-off-by: Liu Ying Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250414035028.1561475-2-victor.liu@nxp.com --- .../display/imx/fsl,imx8qxp-dc-blitblend.yaml | 41 +++++ .../display/imx/fsl,imx8qxp-dc-clut.yaml | 44 ++++++ .../imx/fsl,imx8qxp-dc-constframe.yaml | 44 ++++++ .../display/imx/fsl,imx8qxp-dc-dither.yaml | 45 ++++++ .../display/imx/fsl,imx8qxp-dc-extdst.yaml | 72 +++++++++ .../display/imx/fsl,imx8qxp-dc-fetchunit.yaml | 141 ++++++++++++++++++ .../display/imx/fsl,imx8qxp-dc-filter.yaml | 43 ++++++ .../display/imx/fsl,imx8qxp-dc-framegen.yaml | 64 ++++++++ .../display/imx/fsl,imx8qxp-dc-gammacor.yaml | 32 ++++ .../imx/fsl,imx8qxp-dc-layerblend.yaml | 39 +++++ .../display/imx/fsl,imx8qxp-dc-matrix.yaml | 44 ++++++ .../display/imx/fsl,imx8qxp-dc-rop.yaml | 43 ++++++ .../display/imx/fsl,imx8qxp-dc-safety.yaml | 34 +++++ .../imx/fsl,imx8qxp-dc-scaling-engine.yaml | 83 +++++++++++ .../display/imx/fsl,imx8qxp-dc-signature.yaml | 53 +++++++ .../display/imx/fsl,imx8qxp-dc-store.yaml | 96 ++++++++++++ .../display/imx/fsl,imx8qxp-dc-tcon.yaml | 45 ++++++ 17 files changed, 963 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blitblend.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-clut.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-constframe.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-dither.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-extdst.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-fetchunit.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-filter.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-framegen.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-gammacor.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-layerblend.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-matrix.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-rop.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-safety.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-scaling-engine.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-signature.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-store.yaml create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-tcon.yaml diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blitblend.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blitblend.yaml new file mode 100644 index 000000000000..095e65939fba --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blitblend.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-blitblend.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Blit Blend Unit + +description: + Combines two input frames to a single output frame, all frames having the + same dimension. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-blitblend + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + blitblend@56180920 { + compatible = "fsl,imx8qxp-dc-blitblend"; + reg = <0x56180920 0x10>, <0x56183c00 0x3c>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-clut.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-clut.yaml new file mode 100644 index 000000000000..21d42aa11b52 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-clut.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-clut.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Color Lookup Table + +description: | + The unit implements 3 look-up tables with 256 x 10 bit entries each. These + can be used for different kinds of applications. From 10-bit input values + only upper 8 bits are used. + + The unit supports color lookup, index lookup, dithering and alpha masking. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-clut + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + clut@56180880 { + compatible = "fsl,imx8qxp-dc-clut"; + reg = <0x56180880 0x10>, <0x56182400 0x404>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-constframe.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-constframe.yaml new file mode 100644 index 000000000000..94f678563608 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-constframe.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-constframe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Constant Frame + +description: | + The Constant Frame unit is used instead of a Fetch unit where generation of + constant color frames only is sufficient. This is the case for the background + planes of content and safety streams in a Display Controller. + + The color can be setup to any RGBA value. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-constframe + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + constframe@56180960 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x56180960 0xc>, <0x56184400 0x20>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-dither.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-dither.yaml new file mode 100644 index 000000000000..8e4468d91836 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-dither.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-dither.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Dither Unit + +description: | + The unit can increase the physical color resolution of a display from 5, 6, 7 + or 8 bits per RGB channel to a virtual resolution of 10 bits. The physical + resolution can be set individually for each channel. + + The resolution is increased by mixing the two physical colors that are nearest + to the virtual color code in a variable ratio either by time (temporal + dithering) or by position (spatial dithering). + + An optimized algorithm for temporal dithering minimizes noise artifacts on the + output image. + + The dither operation can be individually enabled or disabled for each pixel + using the alpha input bit. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-dither + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + dither@5618c400 { + compatible = "fsl,imx8qxp-dc-dither"; + reg = <0x5618c400 0x14>; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-extdst.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-extdst.yaml new file mode 100644 index 000000000000..dfc2d4f94f8e --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-extdst.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-extdst.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller External Destination Interface + +description: | + The External Destination unit is the interface between the internal pixel + processing pipeline of the Pixel Engine, which is 30-bit RGB plus 8-bit Alpha, + and a Display Engine. + + It comprises the following built-in Gamma apply function. + + +------X-----------------------+ + | | ExtDst Unit | + | V | + | +-------+ | + | | Gamma | | + | +-------+ | + | | | + | V + + +------X-----------------------+ + + The output format is 24-bit RGB plus 1-bit Alpha. Conversion from 10 to 8 + bits is done by LSBit truncation. Alpha output bit is 1 for input 255, 0 + otherwise. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-extdst + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + + interrupts: + maxItems: 3 + + interrupt-names: + items: + - const: shdload + - const: framecomplete + - const: seqcomplete + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + extdst@56180980 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x56180980 0x1c>, <0x56184800 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <3>, <4>, <5>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-fetchunit.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-fetchunit.yaml new file mode 100644 index 000000000000..97fb6a4598d9 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-fetchunit.yaml @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-fetchunit.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Fetch Unit + +description: | + The Fetch Unit is the interface between the AXI bus for source buffer access + and the internal pixel processing pipeline, which is 30-bit RGB plus 8-bit + Alpha. + + It is used to generate foreground planes in Display Controllers and source + planes in Blit Engines, and comprises the following built-in functions to + convert a wide range of frame buffer types. + + +---------X-----------------------------------------+ + | | Fetch Unit | + | V | + | +---------+ | + | | | | + | | Decode | Decompression [Decode] | + | | | | + | +---------+ | + | | | + | V | + | +---------+ | + | | Clip & | Clip Window [All] | + | | Overlay | Plane composition [Layer, Warp] | + | | | | + | +---------+ | + | | | + | V | + | +---------+ | + | | Re- | Flip/Rotate/Repl./Drop [All] | + X--> | sample | Perspective/Affine warping [Persp] | + | | | | Arbitrary warping [Warp, Persp] | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | | + | | | Palette | Color Palette [Layer, Decode] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | Extract | Raw to RGBA/YUV [All] | + | | | & | Bit width expansion [All] | + | | | Expand | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | Planar to packed | + | |->| Combine | [Decode, Warp, Persp] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | YUV422 to YUV444 | + | | | Chroma | [Decode, Persp] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | YUV to RGB | + | | | Color | [Warp, Persp, Decode, Layer] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | Gamma removal | + | | | Gamma | [Warp, Persp, Decode, Layer] | + | | | | | + | | +---------+ | + | | | | + | | V | + | | +---------+ | + | | | | Alpla multiply, RGB pre-multiply | + | ->| Multiply| [Warp, Persp, Decode, Layer] | + | | | | + | --------- | + | | | + | V | + | +---------+ | + | | | Bilinear filter | + | | Filter | [Warp, Persp] | + | | | | + | +---------+ | + | | | + | V | + +---------X-----------------------------------------+ + + Note that different derivatives of the Fetch Unit exist. Each implements a + specific subset only of the pipeline stages shown above. Restrictions for the + units are specified in [square brackets]. + +maintainers: + - Liu Ying + +properties: + compatible: + enum: + - fsl,imx8qxp-dc-fetchdecode + - fsl,imx8qxp-dc-fetcheco + - fsl,imx8qxp-dc-fetchlayer + - fsl,imx8qxp-dc-fetchwarp + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + + fsl,prg: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Optional Prefetch Resolve Gasket associated with the Fetch Unit. + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + fetchlayer@56180ac0 { + compatible = "fsl,imx8qxp-dc-fetchlayer"; + reg = <0x56180ac0 0xc>, <0x56188400 0x404>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-filter.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-filter.yaml new file mode 100644 index 000000000000..5c54d5179ee3 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-filter.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-filter.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Filter Unit + +description: | + 5x5 FIR filter with 25 programmable coefficients. + + Typical applications are image blurring, sharpening or support for edge + detection algorithms. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-filter + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + filter@56180900 { + compatible = "fsl,imx8qxp-dc-filter"; + reg = <0x56180900 0x10>, <0x56183800 0x30>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-framegen.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-framegen.yaml new file mode 100644 index 000000000000..9d1dc3a9de90 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-framegen.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-framegen.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Frame Generator + +description: + The Frame Generator (FrameGen) module generates a programmable video timing + and optionally allows to synchronize the generated video timing to external + synchronization signals. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-framegen + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 8 + + interrupt-names: + items: + - const: int0 + - const: int1 + - const: int2 + - const: int3 + - const: primsync_on + - const: primsync_off + - const: secsync_on + - const: secsync_off + +required: + - compatible + - reg + - clocks + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + #include + #include + + framegen@5618b800 { + compatible = "fsl,imx8qxp-dc-framegen"; + reg = <0x5618b800 0x98>; + clocks = <&dc0_disp_lpcg IMX_LPCG_CLK_0>; + interrupt-parent = <&dc0_intc>; + interrupts = <18>, <19>, <20>, <21>, <41>, <42>, <43>, <44>; + interrupt-names = "int0", "int1", "int2", "int3", + "primsync_on", "primsync_off", + "secsync_on", "secsync_off"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-gammacor.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-gammacor.yaml new file mode 100644 index 000000000000..25ad85742912 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-gammacor.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-gammacor.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Gamma Correction Unit + +description: The unit supports non-linear color transformation. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-gammacor + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + gammacor@5618c000 { + compatible = "fsl,imx8qxp-dc-gammacor"; + reg = <0x5618c000 0x20>; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-layerblend.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-layerblend.yaml new file mode 100644 index 000000000000..2a6ab8a0ed7f --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-layerblend.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-layerblend.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Layer Blend Unit + +description: Combines two input frames to a single output frame. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-layerblend + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + layerblend@56180ba0 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180ba0 0x10>, <0x5618a400 0x20>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-matrix.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-matrix.yaml new file mode 100644 index 000000000000..d773389dd0dc --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-matrix.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-matrix.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Color Matrix + +description: + The unit supports linear color transformation, alpha pre-multiply and + alpha masking. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-matrix + + reg: + minItems: 1 + maxItems: 2 + + reg-names: + oneOf: + - const: cfg # matrix in display engine + - items: # matrix in pixel engine + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + matrix@5618bc00 { + compatible = "fsl,imx8qxp-dc-matrix"; + reg = <0x5618bc00 0x3c>; + reg-names = "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-rop.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-rop.yaml new file mode 100644 index 000000000000..7115950ecae0 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-rop.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-rop.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Raster Operation Unit + +description: | + The unit can combine up to three input frames to a single output frame, all + having the same dimension. + + The unit supports logic operations, arithmetic operations and packing. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-rop + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + rop@56180860 { + compatible = "fsl,imx8qxp-dc-rop"; + reg = <0x56180860 0x10>, <0x56182000 0x20>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-safety.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-safety.yaml new file mode 100644 index 000000000000..66c12948ab09 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-safety.yaml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-safety.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Safety Unit + +description: + The unit allows corresponding processing units to be configured in a path + leading to multiple endpoints. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-safety + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + safety@56180800 { + compatible = "fsl,imx8qxp-dc-safety"; + reg = <0x56180800 0x1c>; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-scaling-engine.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-scaling-engine.yaml new file mode 100644 index 000000000000..76cbe11a6364 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-scaling-engine.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-scaling-engine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Scaling Engine + +description: | + The unit can change the dimension of the input frame by nearest or linear + re-sampling with 1/32 sub pixel precision. + + Internally it consist of two independent blocks for horizontal and vertical + scaling. The sequence of both operations is arbitrary. + + Any frame dimensions between 1 and 16384 pixels in width and height are + supported, except that the vertical scaler has a frame width maximum + depending of the system's functional limitations. + + In general all scale factors are supported inside the supported frame + dimensions. In range of scale factors 1/16..16 the filtered output colors + are LSBit precise (e.g. DC ripple free). + + +-----------+ + | Line | + | Buffer | + +-----------+ + ^ + | + V + |\ +-----------+ + ------+ | | | + | | +-->| Vertical |---- + | ----+ | | Scaler | | + | | |/ +-----------+ | + | | | + | | | + | | | |\ + | ------------- -------------+-----+ | + Input --+ X | +--> Output + | ------------- -------------+-----+ | + | | | |/ + | | | + | | |\ +-----------+ | + | ----+ | | | | + | | +-->| Horizontal|---- + ------+ | | Scaler | + |/ +-----------+ + + The unit supports downscaling, upscaling, sub pixel translation and bob + de-interlacing. + +maintainers: + - Liu Ying + +properties: + compatible: + enum: + - fsl,imx8qxp-dc-hscaler + - fsl,imx8qxp-dc-vscaler + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + hscaler@561808c0 { + compatible = "fsl,imx8qxp-dc-hscaler"; + reg = <0x561808c0 0x10>, <0x56183000 0x18>; + reg-names = "pec", "cfg"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-signature.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-signature.yaml new file mode 100644 index 000000000000..c495822fdc80 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-signature.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-signature.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Signature Unit + +description: | + In order to control the correctness of display output, signature values can + be computed for each frame and compared against reference values. In case of + a mismatch (signature violation) a HW event can be triggered, for example a + SW interrupt. + + This unit supports signature computation, reference check, evaluation windows, + alpha masking and panic modes. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-signature + + reg: + maxItems: 1 + + interrupts: + maxItems: 3 + + interrupt-names: + items: + - const: shdload + - const: valid + - const: error + +required: + - compatible + - reg + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + signature@5618d000 { + compatible = "fsl,imx8qxp-dc-signature"; + reg = <0x5618d000 0x140>; + interrupt-parent = <&dc0_intc>; + interrupts = <22>, <23>, <24>; + interrupt-names = "shdload", "valid", "error"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-store.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-store.yaml new file mode 100644 index 000000000000..42d1b10906be --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-store.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-store.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Store Unit + +description: | + The Store unit is the interface between the internal pixel processing + pipeline, which is 30-bit RGB plus 8-bit Alpha, and the AXI bus for + destination buffer access. It is used for the destination of Blit Engines. + It comprises a set of built-in functions to generate a wide range of buffer + formats. Note, that these are exactly inverse to corresponding functions in + the Fetch Unit. + + +------X-------------------------+ + | | Store Unit | + | V | + | +-------+ | + | | Gamma | Gamma apply | + | +-------+ | + | | | + | V | + | +-------+ | + | | Color | RGB to YUV | + | +-------+ | + | | | + | V | + | +-------+ | + | | Chroma| YUV444 to 422 | + | +-------+ | + | | | + | V | + | +-------+ | + | | Reduce| Bit width reduction | + | | | dithering | + | +-------+ | + | | | + | V | + | +-------+ | + | | Pack | RGBA/YUV to RAW | + | | Encode| or Compression | + | +-------+ | + | | | + | V | + +------X-------------------------+ + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-store + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + + interrupts: + maxItems: 3 + + interrupt-names: + items: + - const: shdload + - const: framecomplete + - const: seqcomplete + + fsl,lts: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Optional Linear Tile Store associated with the Store Unit. + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + store@56180940 { + compatible = "fsl,imx8qxp-dc-store"; + reg = <0x56180940 0x1c>, <0x56184000 0x5c>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <0>, <1>, <2>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-tcon.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-tcon.yaml new file mode 100644 index 000000000000..7a3b77ea92c7 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-tcon.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-tcon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Timing Controller + +description: + The TCon can generate a wide range of customized synchronization signals and + does the mapping of the color bits to the output. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-tcon + + reg: + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/properties/port + description: video output + +required: + - compatible + - reg + - port + +additionalProperties: false + +examples: + - | + tcon@5618c800 { + compatible = "fsl,imx8qxp-dc-tcon"; + reg = <0x5618c800 0x588>; + + port { + dc0_disp0_dc0_pixel_combiner_ch0: endpoint { + remote-endpoint = <&dc0_pixel_combiner_ch0_dc0_disp0>; + }; + }; + }; From b71d3ace779f3e8d6790dbe40c37883ed8be4106 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:11 +0800 Subject: [PATCH 008/279] dt-bindings: display: imx: Add i.MX8qxp Display Controller blit engine i.MX8qxp Display Controller contains a blit engine for raster graphics. It may read up to 3 source images from memory and computes one destination image from it, which is written back to memory. Signed-off-by: Liu Ying Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250414035028.1561475-3-victor.liu@nxp.com --- .../imx/fsl,imx8qxp-dc-blit-engine.yaml | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blit-engine.yaml diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blit-engine.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blit-engine.yaml new file mode 100644 index 000000000000..45db6da39e20 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-blit-engine.yaml @@ -0,0 +1,204 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-blit-engine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Blit Engine + +description: | + A blit operation (block based image transfer) reads up to 3 source images + from memory and computes one destination image from it, which is written + back to memory. The following basic operations are supported: + + * Buffer Fill + Fills a buffer with constant color + + * Buffer Copy + Copies one source to a destination buffer. + + * Image Blend + Combines two source images by a blending equation and writes result to + destination (which can be one of the sources). + + * Image Rop2/3 + Combines up to three source images by a logical equation (raster operation) + and writes result to destination (which can be one of the sources). + + * Image Flip + Mirrors the source image in horizontal and/or vertical direction. + + * Format Convert + Convert between the supported color and buffer formats. + + * Color Transform + Modify colors by linear or non-linear transformations. + + * Image Scale + Changes size of the source image. + + * Image Rotate + Rotates the source image by any angle. + + * Image Filter + Performs an FIR filter operation on the source image. + + * Image Warp + Performs a re-sampling of the source image with any pattern. The sample + point positions are read from a compressed coordinate buffer. + + * Buffer Pack + Writes an image with color components stored in up to three different + buffers (planar formats) into a single buffer (packed format). + + * Chroma Resample + Converts between different YUV formats that differ in chroma sampling rate + (4:4:4, 4:2:2, 4:2:0). + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-blit-engine + + reg: + maxItems: 2 + + reg-names: + items: + - const: pec + - const: cfg + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^blitblend@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-blitblend + + "^clut@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-clut + + "^fetchdecode@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchdecode + + "^fetcheco@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetcheco + + "^fetchwarp@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchwarp + + "^filter@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-filter + + "^hscaler@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-hscaler + + "^matrix@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-matrix + + "^rop@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-rop + + "^store@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-store + + "^vscaler@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-vscaler + +required: + - compatible + - reg + - reg-names + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + blit-engine@56180820 { + compatible = "fsl,imx8qxp-dc-blit-engine"; + reg = <0x56180820 0x13c>, <0x56181000 0x3400>; + reg-names = "pec", "cfg"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + fetchdecode@56180820 { + compatible = "fsl,imx8qxp-dc-fetchdecode"; + reg = <0x56180820 0x10>, <0x56181000 0x404>; + reg-names = "pec", "cfg"; + }; + + store@56180940 { + compatible = "fsl,imx8qxp-dc-store"; + reg = <0x56180940 0x1c>, <0x56184000 0x5c>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <0>, <1>, <2>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + }; From 33ce3179110db1e83fb7e99d102dd7c3a0e7d55d Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:12 +0800 Subject: [PATCH 009/279] dt-bindings: display: imx: Add i.MX8qxp Display Controller display engine i.MX8qxp Display Controller display engine consists of all processing units that operate in a display clock domain. Signed-off-by: Liu Ying Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250414035028.1561475-4-victor.liu@nxp.com --- .../imx/fsl,imx8qxp-dc-display-engine.yaml | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-display-engine.yaml diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-display-engine.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-display-engine.yaml new file mode 100644 index 000000000000..91f3bb77d8d0 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-display-engine.yaml @@ -0,0 +1,152 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-display-engine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Display Engine + +description: + All Processing Units that operate in a display clock domain. Pixel pipeline + is driven by a video timing and cannot be stalled. Implements all display + specific processing. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-display-engine + + reg: + maxItems: 2 + + reg-names: + items: + - const: top + - const: cfg + + resets: + maxItems: 1 + + interrupts: + maxItems: 3 + + interrupt-names: + items: + - const: shdload + - const: framecomplete + - const: seqcomplete + + power-domains: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^dither@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-dither + + "^framegen@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-framegen + + "^gammacor@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-gammacor + + "^matrix@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-matrix + + "^signature@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-signature + + "^tcon@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-tcon + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - power-domains + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include + #include + + display-engine@5618b400 { + compatible = "fsl,imx8qxp-dc-display-engine"; + reg = <0x5618b400 0x14>, <0x5618b800 0x1c00>; + reg-names = "top", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <15>, <16>, <17>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + power-domains = <&pd IMX_SC_R_DC_0_PLL_0>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + framegen@5618b800 { + compatible = "fsl,imx8qxp-dc-framegen"; + reg = <0x5618b800 0x98>; + clocks = <&dc0_disp_lpcg IMX_LPCG_CLK_0>; + interrupt-parent = <&dc0_intc>; + interrupts = <18>, <19>, <20>, <21>, <41>, <42>, <43>, <44>; + interrupt-names = "int0", "int1", "int2", "int3", + "primsync_on", "primsync_off", + "secsync_on", "secsync_off"; + }; + + tcon@5618c800 { + compatible = "fsl,imx8qxp-dc-tcon"; + reg = <0x5618c800 0x588>; + + port { + dc0_disp0_dc0_pixel_combiner_ch0: endpoint { + remote-endpoint = <&dc0_pixel_combiner_ch0_dc0_disp0>; + }; + }; + }; + }; From 69c78e7e8c2a65a007ba92d8c780365fed0aff5d Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:13 +0800 Subject: [PATCH 010/279] dt-bindings: display: imx: Add i.MX8qxp Display Controller pixel engine i.MX8qxp Display Controller pixel engine consists of all processing units that operate in the AXI bus clock domain. Command sequencer and interrupt controller of the Display Controller work with AXI bus clock, but they are not in pixel engine. Signed-off-by: Liu Ying Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250414035028.1561475-5-victor.liu@nxp.com --- .../imx/fsl,imx8qxp-dc-pixel-engine.yaml | 250 ++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-pixel-engine.yaml diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-pixel-engine.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-pixel-engine.yaml new file mode 100644 index 000000000000..633443a6cc38 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-pixel-engine.yaml @@ -0,0 +1,250 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-pixel-engine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Pixel Engine + +description: + All Processing Units that operate in the AXI bus clock domain. Pixel + pipelines have the ability to stall when a destination is busy. Implements + all communication to memory resources and most of the image processing + functions. Interconnection of Processing Units is re-configurable. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-pixel-engine + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^blit-engine@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-blit-engine + + "^constframe@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-constframe + + "^extdst@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-extdst + + "^fetchdecode@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchdecode + + "^fetcheco@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetcheco + + "^fetchlayer@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchlayer + + "^fetchwarp@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-fetchwarp + + "^hscaler@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-hscaler + + "^layerblend@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-layerblend + + "^matrix@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-matrix + + "^safety@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-safety + + "^vscaler@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-vscaler + +required: + - compatible + - reg + - clocks + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include + + pixel-engine@56180800 { + compatible = "fsl,imx8qxp-dc-pixel-engine"; + reg = <0x56180800 0xac00>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + constframe@56180960 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x56180960 0xc>, <0x56184400 0x20>; + reg-names = "pec", "cfg"; + }; + + extdst@56180980 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x56180980 0x1c>, <0x56184800 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <3>, <4>, <5>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + + constframe@561809a0 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x561809a0 0xc>, <0x56184c00 0x20>; + reg-names = "pec", "cfg"; + }; + + extdst@561809c0 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x561809c0 0x1c>, <0x56185000 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <6>, <7>, <8>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + + constframe@561809e0 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x561809e0 0xc>, <0x56185400 0x20>; + reg-names = "pec", "cfg"; + }; + + extdst@56180a00 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x56180a00 0x1c>, <0x56185800 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <9>, <10>, <11>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + + constframe@56180a20 { + compatible = "fsl,imx8qxp-dc-constframe"; + reg = <0x56180a20 0xc>, <0x56185c00 0x20>; + reg-names = "pec", "cfg"; + }; + + extdst@56180a40 { + compatible = "fsl,imx8qxp-dc-extdst"; + reg = <0x56180a40 0x1c>, <0x56186000 0x28>; + reg-names = "pec", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <12>, <13>, <14>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + }; + + fetchwarp@56180a60 { + compatible = "fsl,imx8qxp-dc-fetchwarp"; + reg = <0x56180a60 0x10>, <0x56186400 0x190>; + reg-names = "pec", "cfg"; + }; + + fetchlayer@56180ac0 { + compatible = "fsl,imx8qxp-dc-fetchlayer"; + reg = <0x56180ac0 0xc>, <0x56188400 0x404>; + reg-names = "pec", "cfg"; + }; + + layerblend@56180ba0 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180ba0 0x10>, <0x5618a400 0x20>; + reg-names = "pec", "cfg"; + }; + + layerblend@56180bc0 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180bc0 0x10>, <0x5618a800 0x20>; + reg-names = "pec", "cfg"; + }; + + layerblend@56180be0 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180be0 0x10>, <0x5618ac00 0x20>; + reg-names = "pec", "cfg"; + }; + + layerblend@56180c00 { + compatible = "fsl,imx8qxp-dc-layerblend"; + reg = <0x56180c00 0x10>, <0x5618b000 0x20>; + reg-names = "pec", "cfg"; + }; + }; From e0390da391b9c240a309790fd226caa3d906cf8c Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:14 +0800 Subject: [PATCH 011/279] dt-bindings: display: imx: Add i.MX8qxp Display Controller AXI performance counter i.MX8qxp Display Controller contains a AXI performance counter which allows measurement of average bandwidth and latency during operation. Signed-off-by: Liu Ying Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250414035028.1561475-6-victor.liu@nxp.com --- ...sl,imx8qxp-dc-axi-performance-counter.yaml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-axi-performance-counter.yaml diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-axi-performance-counter.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-axi-performance-counter.yaml new file mode 100644 index 000000000000..1d6501afc7f2 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-axi-performance-counter.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-axi-performance-counter.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller AXI Performance Counter + +description: | + Performance counters are provided to allow measurement of average bandwidth + and latency during operation. The following features are supported: + + * Manual and timer controlled measurement mode. + + * Measurement counters: + - GLOBAL_COUNTER for overall measurement time + - BUSY_COUNTER for number of data bus busy cycles + - DATA_COUNTER for number of data transfer cycles + - TRANSFER_COUNTER for number of transfers + - ADDRBUSY_COUNTER for number of address bus busy cycles + - LATENCY_COUNTER for average latency + + * Counter overflow detection. + + * Outstanding Transfer Counters (OTC) which are used for latency measurement + have to run immediately after reset, but can be disabled by software when + there is no need for latency measurement. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-axi-performance-counter + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + #include + + pmu@5618f000 { + compatible = "fsl,imx8qxp-dc-axi-performance-counter"; + reg = <0x5618f000 0x90>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + }; From c809469f25fde307190a38c99982f91e5df53ac7 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:15 +0800 Subject: [PATCH 012/279] dt-bindings: display: imx: Add i.MX8qxp Display Controller command sequencer i.MX8qxp Display Controller contains a command sequencer is designed to autonomously process command lists. Signed-off-by: Liu Ying Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250414035028.1561475-7-victor.liu@nxp.com --- .../imx/fsl,imx8qxp-dc-command-sequencer.yaml | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-command-sequencer.yaml diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-command-sequencer.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-command-sequencer.yaml new file mode 100644 index 000000000000..27118f4c0d28 --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc-command-sequencer.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc-command-sequencer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller Command Sequencer + +description: | + The Command Sequencer is designed to autonomously process command lists. + By that it can load setups into the DC configuration and synchronize to + hardware events. This releases a system's CPU from workload, because it + does not need to wait for certain events. Also it simplifies SW architecture, + because no interrupt handlers are required. Setups are read via AXI bus, + while write access to configuration registers occurs directly via an internal + bus. This saves bandwidth for the AXI interconnect and improves the system + architecture in terms of safety aspects. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-command-sequencer + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 5 + + interrupt-names: + items: + - const: error + - const: sw0 + - const: sw1 + - const: sw2 + - const: sw3 + + sram: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle pointing to the mmio-sram device node + +required: + - compatible + - reg + - clocks + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + #include + + command-sequencer@56180400 { + compatible = "fsl,imx8qxp-dc-command-sequencer"; + reg = <0x56180400 0x1a4>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + interrupt-parent = <&dc0_intc>; + interrupts = <36>, <37>, <38>, <39>, <40>; + interrupt-names = "error", "sw0", "sw1", "sw2", "sw3"; + }; From 57e464a30d335c2fd8f64449ac2170ce7c2f3662 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:16 +0800 Subject: [PATCH 013/279] dt-bindings: interrupt-controller: Add i.MX8qxp Display Controller interrupt controller i.MX8qxp Display Controller has a built-in interrupt controller to support Enable/Status/Preset/Clear interrupt bit. Signed-off-by: Liu Ying Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250414035028.1561475-8-victor.liu@nxp.com --- .../fsl,imx8qxp-dc-intc.yaml | 318 ++++++++++++++++++ 1 file changed, 318 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/fsl,imx8qxp-dc-intc.yaml diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,imx8qxp-dc-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,imx8qxp-dc-intc.yaml new file mode 100644 index 000000000000..6985ee644a25 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,imx8qxp-dc-intc.yaml @@ -0,0 +1,318 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/fsl,imx8qxp-dc-intc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller interrupt controller + +description: | + The Display Controller has a built-in interrupt controller with the following + features for all relevant HW events: + + * Enable bit (mask) + * Status bit (set by an HW event) + * Preset bit (can be used by SW to set status) + * Clear bit (used by SW to reset the status) + + Each interrupt can be connected as IRQ (maskable) and/or NMI (non-maskable). + Alternatively the un-masked trigger signals for all HW events are provided, + allowing it to use a global interrupt controller instead. + + Each interrupt can be protected against SW running in user mode. In that case, + only privileged AHB access can control the interrupt status. + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc-intc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 1 + + interrupts: + items: + - description: store9 shadow load interrupt(blit engine) + - description: store9 frame complete interrupt(blit engine) + - description: store9 sequence complete interrupt(blit engine) + - description: + extdst0 shadow load interrupt + (display controller, content stream 0) + - description: + extdst0 frame complete interrupt + (display controller, content stream 0) + - description: + extdst0 sequence complete interrupt + (display controller, content stream 0) + - description: + extdst4 shadow load interrupt + (display controller, safety stream 0) + - description: + extdst4 frame complete interrupt + (display controller, safety stream 0) + - description: + extdst4 sequence complete interrupt + (display controller, safety stream 0) + - description: + extdst1 shadow load interrupt + (display controller, content stream 1) + - description: + extdst1 frame complete interrupt + (display controller, content stream 1) + - description: + extdst1 sequence complete interrupt + (display controller, content stream 1) + - description: + extdst5 shadow load interrupt + (display controller, safety stream 1) + - description: + extdst5 frame complete interrupt + (display controller, safety stream 1) + - description: + extdst5 sequence complete interrupt + (display controller, safety stream 1) + - description: + disengcfg0 shadow load interrupt + (display controller, display stream 0) + - description: + disengcfg0 frame complete interrupt + (display controller, display stream 0) + - description: + disengcfg0 sequence complete interrupt + (display controller, display stream 0) + - description: + framegen0 programmable interrupt0 + (display controller, display stream 0) + - description: + framegen0 programmable interrupt1 + (display controller, display stream 0) + - description: + framegen0 programmable interrupt2 + (display controller, display stream 0) + - description: + framegen0 programmable interrupt3 + (display controller, display stream 0) + - description: + signature0 shadow load interrupt + (display controller, display stream 0) + - description: + signature0 measurement valid interrupt + (display controller, display stream 0) + - description: + signature0 error condition interrupt + (display controller, display stream 0) + - description: + disengcfg1 shadow load interrupt + (display controller, display stream 1) + - description: + disengcfg1 frame complete interrupt + (display controller, display stream 1) + - description: + disengcfg1 sequence complete interrupt + (display controller, display stream 1) + - description: + framegen1 programmable interrupt0 + (display controller, display stream 1) + - description: + framegen1 programmable interrupt1 + (display controller, display stream 1) + - description: + framegen1 programmable interrupt2 + (display controller, display stream 1) + - description: + framegen1 programmable interrupt3 + (display controller, display stream 1) + - description: + signature1 shadow load interrupt + (display controller, display stream 1) + - description: + signature1 measurement valid interrupt + (display controller, display stream 1) + - description: + signature1 error condition interrupt + (display controller, display stream 1) + - description: reserved + - description: + command sequencer error condition interrupt(command sequencer) + - description: + common control software interrupt0(common control) + - description: + common control software interrupt1(common control) + - description: + common control software interrupt2(common control) + - description: + common control software interrupt3(common control) + - description: + framegen0 synchronization status activated interrupt + (display controller, safety stream 0) + - description: + framegen0 synchronization status deactivated interrupt + (display controller, safety stream 0) + - description: + framegen0 synchronization status activated interrupt + (display controller, content stream 0) + - description: + framegen0 synchronization status deactivated interrupt + (display controller, content stream 0) + - description: + framegen1 synchronization status activated interrupt + (display controller, safety stream 1) + - description: + framegen1 synchronization status deactivated interrupt + (display controller, safety stream 1) + - description: + framegen1 synchronization status activated interrupt + (display controller, content stream 1) + - description: + framegen1 synchronization status deactivated interrupt + (display controller, content stream 1) + minItems: 49 + + interrupt-names: + items: + - const: store9_shdload + - const: store9_framecomplete + - const: store9_seqcomplete + - const: extdst0_shdload + - const: extdst0_framecomplete + - const: extdst0_seqcomplete + - const: extdst4_shdload + - const: extdst4_framecomplete + - const: extdst4_seqcomplete + - const: extdst1_shdload + - const: extdst1_framecomplete + - const: extdst1_seqcomplete + - const: extdst5_shdload + - const: extdst5_framecomplete + - const: extdst5_seqcomplete + - const: disengcfg_shdload0 + - const: disengcfg_framecomplete0 + - const: disengcfg_seqcomplete0 + - const: framegen0_int0 + - const: framegen0_int1 + - const: framegen0_int2 + - const: framegen0_int3 + - const: sig0_shdload + - const: sig0_valid + - const: sig0_error + - const: disengcfg_shdload1 + - const: disengcfg_framecomplete1 + - const: disengcfg_seqcomplete1 + - const: framegen1_int0 + - const: framegen1_int1 + - const: framegen1_int2 + - const: framegen1_int3 + - const: sig1_shdload + - const: sig1_valid + - const: sig1_error + - const: reserved + - const: cmdseq_error + - const: comctrl_sw0 + - const: comctrl_sw1 + - const: comctrl_sw2 + - const: comctrl_sw3 + - const: framegen0_primsync_on + - const: framegen0_primsync_off + - const: framegen0_secsync_on + - const: framegen0_secsync_off + - const: framegen1_primsync_on + - const: framegen1_primsync_off + - const: framegen1_secsync_on + - const: framegen1_secsync_off + minItems: 49 + +required: + - compatible + - reg + - clocks + - interrupt-controller + - "#interrupt-cells" + - interrupts + - interrupt-names + +additionalProperties: false + +examples: + - | + #include + + interrupt-controller@56180040 { + compatible = "fsl,imx8qxp-dc-intc"; + reg = <0x56180040 0x60>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + interrupt-controller; + interrupt-parent = <&dc0_irqsteer>; + #interrupt-cells = <1>; + interrupts = <448>, <449>, <450>, <64>, + <65>, <66>, <67>, <68>, + <69>, <70>, <193>, <194>, + <195>, <196>, <197>, <72>, + <73>, <74>, <75>, <76>, + <77>, <78>, <79>, <80>, + <81>, <199>, <200>, <201>, + <202>, <203>, <204>, <205>, + <206>, <207>, <208>, <5>, + <0>, <1>, <2>, <3>, + <4>, <82>, <83>, <84>, + <85>, <209>, <210>, <211>, + <212>; + interrupt-names = "store9_shdload", + "store9_framecomplete", + "store9_seqcomplete", + "extdst0_shdload", + "extdst0_framecomplete", + "extdst0_seqcomplete", + "extdst4_shdload", + "extdst4_framecomplete", + "extdst4_seqcomplete", + "extdst1_shdload", + "extdst1_framecomplete", + "extdst1_seqcomplete", + "extdst5_shdload", + "extdst5_framecomplete", + "extdst5_seqcomplete", + "disengcfg_shdload0", + "disengcfg_framecomplete0", + "disengcfg_seqcomplete0", + "framegen0_int0", + "framegen0_int1", + "framegen0_int2", + "framegen0_int3", + "sig0_shdload", + "sig0_valid", + "sig0_error", + "disengcfg_shdload1", + "disengcfg_framecomplete1", + "disengcfg_seqcomplete1", + "framegen1_int0", + "framegen1_int1", + "framegen1_int2", + "framegen1_int3", + "sig1_shdload", + "sig1_valid", + "sig1_error", + "reserved", + "cmdseq_error", + "comctrl_sw0", + "comctrl_sw1", + "comctrl_sw2", + "comctrl_sw3", + "framegen0_primsync_on", + "framegen0_primsync_off", + "framegen0_secsync_on", + "framegen0_secsync_off", + "framegen1_primsync_on", + "framegen1_primsync_off", + "framegen1_secsync_on", + "framegen1_secsync_off"; + }; From 813f71ac2541d12e146cdb9ac3a81432e6687625 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:17 +0800 Subject: [PATCH 014/279] dt-bindings: display: imx: Add i.MX8qxp Display Controller i.MX8qxp Display Controller(DC) is comprised of three main components that include a blit engine for 2D graphics accelerations, display controller for display output processing, as well as a command sequencer. Signed-off-by: Liu Ying Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250414035028.1561475-9-victor.liu@nxp.com --- .../bindings/display/imx/fsl,imx8qxp-dc.yaml | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc.yaml diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc.yaml new file mode 100644 index 000000000000..0a72f9f0b5fd --- /dev/null +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc.yaml @@ -0,0 +1,236 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/imx/fsl,imx8qxp-dc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8qxp Display Controller + +description: | + The Freescale i.MX8qxp Display Controller(DC) is comprised of three main + components that include a blit engine for 2D graphics accelerations, display + controller for display output processing, as well as a command sequencer. + + Display buffers Source buffers + (AXI read master) (AXI read master) + | .......... | | | | + +---------------------------+------------+------------------+-+-+------+ + | Display Controller (DC) | .......... | | | | | + | | | | | | | + | @@@@@@@@@@@ +----------+------------+------------+ | | | | + A | | Command | | V V | | | | | + X <-+->| Sequencer | | @@@@@@@@@@@@@@@@@@@@@@@@@@@@ | V V V | + I | | (AXI CLK) | | | | | @@@@@@@@@@ | + | @@@@@@@@@@@ | | Pixel Engine | | | | | + | | | | (AXI CLK) | | | | | + | V | @@@@@@@@@@@@@@@@@@@@@@@@@@@@ | | | | + A | *********** | | | | | | | Blit | | + H <-+->| Configure | | V V V V | | Engine | | + B | | (CFG CLK) | | 00000000000 11111111111 | | (AXI CLK)| | + | *********** | | Display | | Display | | | | | + | | | Engine | | Engine | | | | | + | | | (Disp CLK)| | (Disp CLK)| | | | | + | @@@@@@@@@@@ | 00000000000 11111111111 | @@@@@@@@@@ | + I | | Common | | | | | | | + R <-+--| Control | | | Display | | | | + Q | | (AXI CLK) | | | Controller | | | | + | @@@@@@@@@@@ +------------------------------------+ | | + | | | ^ | | + +--------------------------+----------------+-------+---------+--------+ + ^ | | | | + | V V | V + Clocks & Resets Display Display Panic Destination + Output0 Output1 Control buffer + (AXI write master) + +maintainers: + - Liu Ying + +properties: + compatible: + const: fsl,imx8qxp-dc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 2 + + reset-names: + items: + - const: axi + - const: cfg + + power-domains: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + +patternProperties: + "^command-sequencer@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-command-sequencer + + "^display-engine@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-display-engine + + "^interrupt-controller@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-intc + + "^pixel-engine@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-pixel-engine + + "^pmu@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + const: fsl,imx8qxp-dc-axi-performance-counter + +required: + - compatible + - reg + - clocks + - power-domains + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include + #include + + display-controller@56180000 { + compatible = "fsl,imx8qxp-dc"; + reg = <0x56180000 0x40000>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_4>; + power-domains = <&pd IMX_SC_R_DC_0>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + interrupt-controller@56180040 { + compatible = "fsl,imx8qxp-dc-intc"; + reg = <0x56180040 0x60>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + interrupt-controller; + interrupt-parent = <&dc0_irqsteer>; + #interrupt-cells = <1>; + interrupts = <448>, <449>, <450>, <64>, + <65>, <66>, <67>, <68>, + <69>, <70>, <193>, <194>, + <195>, <196>, <197>, <72>, + <73>, <74>, <75>, <76>, + <77>, <78>, <79>, <80>, + <81>, <199>, <200>, <201>, + <202>, <203>, <204>, <205>, + <206>, <207>, <208>, <5>, + <0>, <1>, <2>, <3>, + <4>, <82>, <83>, <84>, + <85>, <209>, <210>, <211>, + <212>; + interrupt-names = "store9_shdload", + "store9_framecomplete", + "store9_seqcomplete", + "extdst0_shdload", + "extdst0_framecomplete", + "extdst0_seqcomplete", + "extdst4_shdload", + "extdst4_framecomplete", + "extdst4_seqcomplete", + "extdst1_shdload", + "extdst1_framecomplete", + "extdst1_seqcomplete", + "extdst5_shdload", + "extdst5_framecomplete", + "extdst5_seqcomplete", + "disengcfg_shdload0", + "disengcfg_framecomplete0", + "disengcfg_seqcomplete0", + "framegen0_int0", + "framegen0_int1", + "framegen0_int2", + "framegen0_int3", + "sig0_shdload", + "sig0_valid", + "sig0_error", + "disengcfg_shdload1", + "disengcfg_framecomplete1", + "disengcfg_seqcomplete1", + "framegen1_int0", + "framegen1_int1", + "framegen1_int2", + "framegen1_int3", + "sig1_shdload", + "sig1_valid", + "sig1_error", + "reserved", + "cmdseq_error", + "comctrl_sw0", + "comctrl_sw1", + "comctrl_sw2", + "comctrl_sw3", + "framegen0_primsync_on", + "framegen0_primsync_off", + "framegen0_secsync_on", + "framegen0_secsync_off", + "framegen1_primsync_on", + "framegen1_primsync_off", + "framegen1_secsync_on", + "framegen1_secsync_off"; + }; + + pixel-engine@56180800 { + compatible = "fsl,imx8qxp-dc-pixel-engine"; + reg = <0x56180800 0xac00>; + clocks = <&dc0_lpcg IMX_LPCG_CLK_5>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + }; + + display-engine@5618b400 { + compatible = "fsl,imx8qxp-dc-display-engine"; + reg = <0x5618b400 0x14>, <0x5618b800 0x1c00>; + reg-names = "top", "cfg"; + interrupt-parent = <&dc0_intc>; + interrupts = <15>, <16>, <17>; + interrupt-names = "shdload", "framecomplete", "seqcomplete"; + power-domains = <&pd IMX_SC_R_DC_0_PLL_0>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + }; + }; From 9f09e3173776b9da4fde0c0641d1d1e9d08fcf46 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:18 +0800 Subject: [PATCH 015/279] drm/imx: Add i.MX8qxp Display Controller display engine i.MX8qxp Display Controller display engine consists of all processing units that operate in a display clock domain. Add minimal feature support with FrameGen and TCon so that the engine can output display timings. The FrameGen driver, TCon driver and display engine driver are components to be aggregated by a master registered in the upcoming DRM driver. Reviewed-by: Maxime Ripard Signed-off-by: Liu Ying Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250414035028.1561475-10-victor.liu@nxp.com --- drivers/gpu/drm/imx/Kconfig | 1 + drivers/gpu/drm/imx/Makefile | 1 + drivers/gpu/drm/imx/dc/Kconfig | 7 + drivers/gpu/drm/imx/dc/Makefile | 5 + drivers/gpu/drm/imx/dc/dc-de.c | 177 +++++++++++++++ drivers/gpu/drm/imx/dc/dc-de.h | 56 +++++ drivers/gpu/drm/imx/dc/dc-drv.c | 32 +++ drivers/gpu/drm/imx/dc/dc-drv.h | 57 +++++ drivers/gpu/drm/imx/dc/dc-fg.c | 376 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/imx/dc/dc-tc.c | 141 ++++++++++++ 10 files changed, 853 insertions(+) create mode 100644 drivers/gpu/drm/imx/dc/Kconfig create mode 100644 drivers/gpu/drm/imx/dc/Makefile create mode 100644 drivers/gpu/drm/imx/dc/dc-de.c create mode 100644 drivers/gpu/drm/imx/dc/dc-de.h create mode 100644 drivers/gpu/drm/imx/dc/dc-drv.c create mode 100644 drivers/gpu/drm/imx/dc/dc-drv.h create mode 100644 drivers/gpu/drm/imx/dc/dc-fg.c create mode 100644 drivers/gpu/drm/imx/dc/dc-tc.c diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index 03535a15dd8f..3e8c6edbc17c 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only +source "drivers/gpu/drm/imx/dc/Kconfig" source "drivers/gpu/drm/imx/dcss/Kconfig" source "drivers/gpu/drm/imx/ipuv3/Kconfig" source "drivers/gpu/drm/imx/lcdc/Kconfig" diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index 86f38e7c7422..c7b317640d71 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_DRM_IMX8_DC) += dc/ obj-$(CONFIG_DRM_IMX_DCSS) += dcss/ obj-$(CONFIG_DRM_IMX) += ipuv3/ obj-$(CONFIG_DRM_IMX_LCDC) += lcdc/ diff --git a/drivers/gpu/drm/imx/dc/Kconfig b/drivers/gpu/drm/imx/dc/Kconfig new file mode 100644 index 000000000000..e1ef76d82830 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/Kconfig @@ -0,0 +1,7 @@ +config DRM_IMX8_DC + tristate "Freescale i.MX8 Display Controller Graphics" + depends on DRM && COMMON_CLK && OF && (ARCH_MXC || COMPILE_TEST) + select REGMAP + select REGMAP_MMIO + help + enable Freescale i.MX8 Display Controller(DC) graphics support diff --git a/drivers/gpu/drm/imx/dc/Makefile b/drivers/gpu/drm/imx/dc/Makefile new file mode 100644 index 000000000000..56de82d53d4d --- /dev/null +++ b/drivers/gpu/drm/imx/dc/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +imx8-dc-drm-objs := dc-de.o dc-drv.o dc-fg.o dc-tc.o + +obj-$(CONFIG_DRM_IMX8_DC) += imx8-dc-drm.o diff --git a/drivers/gpu/drm/imx/dc/dc-de.c b/drivers/gpu/drm/imx/dc/dc-de.c new file mode 100644 index 000000000000..5a3125596fdf --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-de.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dc-de.h" +#include "dc-drv.h" + +#define POLARITYCTRL 0xc +#define POLEN_HIGH BIT(2) + +static const struct dc_subdev_info dc_de_info[] = { + { .reg_start = 0x5618b400, .id = 0, }, + { .reg_start = 0x5618b420, .id = 1, }, +}; + +static const struct regmap_range dc_de_regmap_ranges[] = { + regmap_reg_range(POLARITYCTRL, POLARITYCTRL), +}; + +static const struct regmap_access_table dc_de_regmap_access_table = { + .yes_ranges = dc_de_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_de_regmap_ranges), +}; + +static const struct regmap_config dc_de_top_regmap_config = { + .name = "top", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_de_regmap_access_table, + .rd_table = &dc_de_regmap_access_table, + .max_register = POLARITYCTRL, +}; + +static inline void dc_dec_init(struct dc_de *de) +{ + regmap_write_bits(de->reg_top, POLARITYCTRL, POLARITYCTRL, POLEN_HIGH); +} + +static int dc_de_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_top; + void __iomem *base_top; + struct dc_de *de; + int ret, id; + + de = devm_kzalloc(dev, sizeof(*de), GFP_KERNEL); + if (!de) + return -ENOMEM; + + base_top = devm_platform_get_and_ioremap_resource(pdev, 0, &res_top); + if (IS_ERR(base_top)) + return PTR_ERR(base_top); + + de->reg_top = devm_regmap_init_mmio(dev, base_top, + &dc_de_top_regmap_config); + if (IS_ERR(de->reg_top)) + return PTR_ERR(de->reg_top); + + de->irq_shdload = platform_get_irq_byname(pdev, "shdload"); + if (de->irq_shdload < 0) + return de->irq_shdload; + + de->irq_framecomplete = platform_get_irq_byname(pdev, "framecomplete"); + if (de->irq_framecomplete < 0) + return de->irq_framecomplete; + + de->irq_seqcomplete = platform_get_irq_byname(pdev, "seqcomplete"); + if (de->irq_seqcomplete < 0) + return de->irq_seqcomplete; + + de->dev = dev; + + dev_set_drvdata(dev, de); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + id = dc_subdev_get_id(dc_de_info, ARRAY_SIZE(dc_de_info), res_top); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + dc_drm->de[id] = de; + + return 0; +} + +/* + * It's possible to get the child device pointers from the child component + * bind callbacks, but it depends on the component helper behavior to bind + * the display engine component first. To avoid the dependency, post bind + * to get the pointers from dc_drm in a safe manner. + */ +void dc_de_post_bind(struct dc_drm_device *dc_drm) +{ + struct dc_de *de; + int i; + + for (i = 0; i < DC_DISPLAYS; i++) { + de = dc_drm->de[i]; + de->fg = dc_drm->fg[i]; + de->tc = dc_drm->tc[i]; + } +} + +static const struct component_ops dc_de_ops = { + .bind = dc_de_bind, +}; + +static int dc_de_probe(struct platform_device *pdev) +{ + int ret; + + ret = devm_of_platform_populate(&pdev->dev); + if (ret < 0) + return ret; + + ret = component_add(&pdev->dev, &dc_de_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_de_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_de_ops); +} + +static int dc_de_runtime_resume(struct device *dev) +{ + struct dc_de *de = dev_get_drvdata(dev); + + dc_dec_init(de); + dc_fg_init(de->fg); + dc_tc_init(de->tc); + + return 0; +} + +static const struct dev_pm_ops dc_de_pm_ops = { + RUNTIME_PM_OPS(NULL, dc_de_runtime_resume, NULL) +}; + +static const struct of_device_id dc_de_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-display-engine" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_de_dt_ids); + +struct platform_driver dc_de_driver = { + .probe = dc_de_probe, + .remove = dc_de_remove, + .driver = { + .name = "imx8-dc-display-engine", + .suppress_bind_attrs = true, + .of_match_table = dc_de_dt_ids, + .pm = pm_sleep_ptr(&dc_de_pm_ops), + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-de.h b/drivers/gpu/drm/imx/dc/dc-de.h new file mode 100644 index 000000000000..5dd311cde076 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-de.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_DISPLAY_ENGINE_H__ +#define __DC_DISPLAY_ENGINE_H__ + +#include +#include +#include +#include + +#define DC_DISPLAYS 2 + +struct dc_fg { + struct device *dev; + struct regmap *reg; + struct clk *clk_disp; +}; + +struct dc_tc { + struct device *dev; + struct regmap *reg; +}; + +struct dc_de { + struct device *dev; + struct regmap *reg_top; + struct dc_fg *fg; + struct dc_tc *tc; + int irq_shdload; + int irq_framecomplete; + int irq_seqcomplete; +}; + +/* Frame Generator Unit */ +void dc_fg_cfg_videomode(struct dc_fg *fg, struct drm_display_mode *m); +void dc_fg_enable(struct dc_fg *fg); +void dc_fg_disable(struct dc_fg *fg); +void dc_fg_shdtokgen(struct dc_fg *fg); +u32 dc_fg_get_frame_index(struct dc_fg *fg); +u32 dc_fg_get_line_index(struct dc_fg *fg); +bool dc_fg_wait_for_frame_index_moving(struct dc_fg *fg); +bool dc_fg_secondary_requests_to_read_empty_fifo(struct dc_fg *fg); +void dc_fg_secondary_clear_channel_status(struct dc_fg *fg); +int dc_fg_wait_for_secondary_syncup(struct dc_fg *fg); +void dc_fg_enable_clock(struct dc_fg *fg); +void dc_fg_disable_clock(struct dc_fg *fg); +enum drm_mode_status dc_fg_check_clock(struct dc_fg *fg, int clk_khz); +void dc_fg_init(struct dc_fg *fg); + +/* Timing Controller Unit */ +void dc_tc_init(struct dc_tc *tc); + +#endif /* __DC_DISPLAY_ENGINE_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c new file mode 100644 index 000000000000..e5910a82dd4d --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-drv.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include + +#include "dc-drv.h" + +static struct platform_driver * const dc_drivers[] = { + &dc_de_driver, + &dc_fg_driver, + &dc_tc_driver, +}; + +static int __init dc_drm_init(void) +{ + return platform_register_drivers(dc_drivers, ARRAY_SIZE(dc_drivers)); +} + +static void __exit dc_drm_exit(void) +{ + platform_unregister_drivers(dc_drivers, ARRAY_SIZE(dc_drivers)); +} + +module_init(dc_drm_init); +module_exit(dc_drm_exit); + +MODULE_DESCRIPTION("i.MX8 Display Controller DRM Driver"); +MODULE_AUTHOR("Liu Ying "); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h new file mode 100644 index 000000000000..65ae9c7c3694 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-drv.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_DRV_H__ +#define __DC_DRV_H__ + +#include +#include +#include + +#include + +#include "dc-de.h" + +/** + * struct dc_drm_device - DC specific drm_device + */ +struct dc_drm_device { + /** @base: base drm_device structure */ + struct drm_device base; + /** @de: display engine list */ + struct dc_de *de[DC_DISPLAYS]; + /** @fg: framegen list */ + struct dc_fg *fg[DC_DISPLAYS]; + /** @tc: tcon list */ + struct dc_tc *tc[DC_DISPLAYS]; +}; + +struct dc_subdev_info { + resource_size_t reg_start; + int id; +}; + +extern struct platform_driver dc_de_driver; +extern struct platform_driver dc_fg_driver; +extern struct platform_driver dc_tc_driver; + +static inline int dc_subdev_get_id(const struct dc_subdev_info *info, + int info_cnt, struct resource *res) +{ + int i; + + if (!res) + return -EINVAL; + + for (i = 0; i < info_cnt; i++) + if (info[i].reg_start == res->start) + return info[i].id; + + return -EINVAL; +} + +void dc_de_post_bind(struct dc_drm_device *dc_drm); + +#endif /* __DC_DRV_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-fg.c b/drivers/gpu/drm/imx/dc/dc-fg.c new file mode 100644 index 000000000000..7f6c1852bf72 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fg.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dc-de.h" +#include "dc-drv.h" + +#define FGSTCTRL 0x8 +#define FGSYNCMODE_MASK GENMASK(2, 1) +#define FGSYNCMODE(x) FIELD_PREP(FGSYNCMODE_MASK, (x)) +#define SHDEN BIT(0) + +#define HTCFG1 0xc +#define HTOTAL(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define HACT(x) FIELD_PREP(GENMASK(13, 0), (x)) + +#define HTCFG2 0x10 +#define HSEN BIT(31) +#define HSBP(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define HSYNC(x) FIELD_PREP(GENMASK(13, 0), ((x) - 1)) + +#define VTCFG1 0x14 +#define VTOTAL(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define VACT(x) FIELD_PREP(GENMASK(13, 0), (x)) + +#define VTCFG2 0x18 +#define VSEN BIT(31) +#define VSBP(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define VSYNC(x) FIELD_PREP(GENMASK(13, 0), ((x) - 1)) + +#define PKICKCONFIG 0x2c +#define SKICKCONFIG 0x30 +#define EN BIT(31) +#define ROW(x) FIELD_PREP(GENMASK(29, 16), (x)) +#define COL(x) FIELD_PREP(GENMASK(13, 0), (x)) + +#define PACFG 0x54 +#define SACFG 0x58 +#define STARTY(x) FIELD_PREP(GENMASK(29, 16), ((x) + 1)) +#define STARTX(x) FIELD_PREP(GENMASK(13, 0), ((x) + 1)) + +#define FGINCTRL 0x5c +#define FGINCTRLPANIC 0x60 +#define FGDM_MASK GENMASK(2, 0) +#define ENPRIMALPHA BIT(3) +#define ENSECALPHA BIT(4) + +#define FGCCR 0x64 +#define CCGREEN(x) FIELD_PREP(GENMASK(19, 10), (x)) + +#define FGENABLE 0x68 +#define FGEN BIT(0) + +#define FGSLR 0x6c +#define SHDTOKGEN BIT(0) + +#define FGTIMESTAMP 0x74 +#define FRAMEINDEX(x) FIELD_GET(GENMASK(31, 14), (x)) +#define LINEINDEX(x) FIELD_GET(GENMASK(13, 0), (x)) + +#define FGCHSTAT 0x78 +#define SECSYNCSTAT BIT(24) +#define SFIFOEMPTY BIT(16) + +#define FGCHSTATCLR 0x7c +#define CLRSECSTAT BIT(16) + +enum dc_fg_syncmode { + FG_SYNCMODE_OFF, /* No side-by-side synchronization. */ +}; + +enum dc_fg_dm { + FG_DM_CONSTCOL = 0x1, /* Constant Color Background is shown. */ + FG_DM_SEC_ON_TOP = 0x5, /* Both inputs overlaid with secondary on top. */ +}; + +static const struct dc_subdev_info dc_fg_info[] = { + { .reg_start = 0x5618b800, .id = 0, }, + { .reg_start = 0x5618d400, .id = 1, }, +}; + +static const struct regmap_range dc_fg_regmap_write_ranges[] = { + regmap_reg_range(FGSTCTRL, VTCFG2), + regmap_reg_range(PKICKCONFIG, SKICKCONFIG), + regmap_reg_range(PACFG, FGSLR), + regmap_reg_range(FGCHSTATCLR, FGCHSTATCLR), +}; + +static const struct regmap_range dc_fg_regmap_read_ranges[] = { + regmap_reg_range(FGSTCTRL, VTCFG2), + regmap_reg_range(PKICKCONFIG, SKICKCONFIG), + regmap_reg_range(PACFG, FGENABLE), + regmap_reg_range(FGTIMESTAMP, FGCHSTAT), +}; + +static const struct regmap_access_table dc_fg_regmap_write_table = { + .yes_ranges = dc_fg_regmap_write_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fg_regmap_write_ranges), +}; + +static const struct regmap_access_table dc_fg_regmap_read_table = { + .yes_ranges = dc_fg_regmap_read_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fg_regmap_read_ranges), +}; + +static const struct regmap_config dc_fg_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_fg_regmap_write_table, + .rd_table = &dc_fg_regmap_read_table, + .max_register = FGCHSTATCLR, +}; + +static inline void dc_fg_enable_shden(struct dc_fg *fg) +{ + regmap_write_bits(fg->reg, FGSTCTRL, SHDEN, SHDEN); +} + +static inline void dc_fg_syncmode(struct dc_fg *fg, enum dc_fg_syncmode mode) +{ + regmap_write_bits(fg->reg, FGSTCTRL, FGSYNCMODE_MASK, FGSYNCMODE(mode)); +} + +void dc_fg_cfg_videomode(struct dc_fg *fg, struct drm_display_mode *m) +{ + u32 hact, htotal, hsync, hsbp; + u32 vact, vtotal, vsync, vsbp; + u32 kick_row, kick_col; + int ret; + + hact = m->crtc_hdisplay; + htotal = m->crtc_htotal; + hsync = m->crtc_hsync_end - m->crtc_hsync_start; + hsbp = m->crtc_htotal - m->crtc_hsync_start; + + vact = m->crtc_vdisplay; + vtotal = m->crtc_vtotal; + vsync = m->crtc_vsync_end - m->crtc_vsync_start; + vsbp = m->crtc_vtotal - m->crtc_vsync_start; + + /* video mode */ + regmap_write(fg->reg, HTCFG1, HACT(hact) | HTOTAL(htotal)); + regmap_write(fg->reg, HTCFG2, HSYNC(hsync) | HSBP(hsbp) | HSEN); + regmap_write(fg->reg, VTCFG1, VACT(vact) | VTOTAL(vtotal)); + regmap_write(fg->reg, VTCFG2, VSYNC(vsync) | VSBP(vsbp) | VSEN); + + kick_col = hact + 1; + kick_row = vact; + + /* pkickconfig */ + regmap_write(fg->reg, PKICKCONFIG, COL(kick_col) | ROW(kick_row) | EN); + + /* skikconfig */ + regmap_write(fg->reg, SKICKCONFIG, COL(kick_col) | ROW(kick_row) | EN); + + /* primary and secondary area position configuration */ + regmap_write(fg->reg, PACFG, STARTX(0) | STARTY(0)); + regmap_write(fg->reg, SACFG, STARTX(0) | STARTY(0)); + + /* alpha */ + regmap_write_bits(fg->reg, FGINCTRL, ENPRIMALPHA | ENSECALPHA, 0); + regmap_write_bits(fg->reg, FGINCTRLPANIC, ENPRIMALPHA | ENSECALPHA, 0); + + /* constant color is green(used in panic mode) */ + regmap_write(fg->reg, FGCCR, CCGREEN(0x3ff)); + + ret = clk_set_rate(fg->clk_disp, m->clock * HZ_PER_KHZ); + if (ret < 0) + dev_err(fg->dev, "failed to set display clock rate: %d\n", ret); +} + +static inline void dc_fg_displaymode(struct dc_fg *fg, enum dc_fg_dm mode) +{ + regmap_write_bits(fg->reg, FGINCTRL, FGDM_MASK, mode); +} + +static inline void dc_fg_panic_displaymode(struct dc_fg *fg, enum dc_fg_dm mode) +{ + regmap_write_bits(fg->reg, FGINCTRLPANIC, FGDM_MASK, mode); +} + +void dc_fg_enable(struct dc_fg *fg) +{ + regmap_write(fg->reg, FGENABLE, FGEN); +} + +void dc_fg_disable(struct dc_fg *fg) +{ + regmap_write(fg->reg, FGENABLE, 0); +} + +void dc_fg_shdtokgen(struct dc_fg *fg) +{ + regmap_write(fg->reg, FGSLR, SHDTOKGEN); +} + +u32 dc_fg_get_frame_index(struct dc_fg *fg) +{ + u32 val; + + regmap_read(fg->reg, FGTIMESTAMP, &val); + + return FRAMEINDEX(val); +} + +u32 dc_fg_get_line_index(struct dc_fg *fg) +{ + u32 val; + + regmap_read(fg->reg, FGTIMESTAMP, &val); + + return LINEINDEX(val); +} + +bool dc_fg_wait_for_frame_index_moving(struct dc_fg *fg) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(100); + u32 frame_index, last_frame_index; + + frame_index = dc_fg_get_frame_index(fg); + do { + last_frame_index = frame_index; + frame_index = dc_fg_get_frame_index(fg); + } while (last_frame_index == frame_index && + time_before(jiffies, timeout)); + + return last_frame_index != frame_index; +} + +bool dc_fg_secondary_requests_to_read_empty_fifo(struct dc_fg *fg) +{ + u32 val; + + regmap_read(fg->reg, FGCHSTAT, &val); + + return !!(val & SFIFOEMPTY); +} + +void dc_fg_secondary_clear_channel_status(struct dc_fg *fg) +{ + regmap_write(fg->reg, FGCHSTATCLR, CLRSECSTAT); +} + +int dc_fg_wait_for_secondary_syncup(struct dc_fg *fg) +{ + unsigned int val; + + return regmap_read_poll_timeout(fg->reg, FGCHSTAT, val, + val & SECSYNCSTAT, 5, 100000); +} + +void dc_fg_enable_clock(struct dc_fg *fg) +{ + int ret; + + ret = clk_prepare_enable(fg->clk_disp); + if (ret) + dev_err(fg->dev, "failed to enable display clock: %d\n", ret); +} + +void dc_fg_disable_clock(struct dc_fg *fg) +{ + clk_disable_unprepare(fg->clk_disp); +} + +enum drm_mode_status dc_fg_check_clock(struct dc_fg *fg, int clk_khz) +{ + unsigned long rounded_rate; + + rounded_rate = clk_round_rate(fg->clk_disp, clk_khz * HZ_PER_KHZ); + + if (rounded_rate != clk_khz * HZ_PER_KHZ) + return MODE_NOCLOCK; + + return MODE_OK; +} + +void dc_fg_init(struct dc_fg *fg) +{ + dc_fg_enable_shden(fg); + dc_fg_syncmode(fg, FG_SYNCMODE_OFF); + dc_fg_displaymode(fg, FG_DM_SEC_ON_TOP); + dc_fg_panic_displaymode(fg, FG_DM_CONSTCOL); +} + +static int dc_fg_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res; + void __iomem *base; + struct dc_fg *fg; + int id; + + fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL); + if (!fg) + return -ENOMEM; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + fg->reg = devm_regmap_init_mmio(dev, base, &dc_fg_regmap_config); + if (IS_ERR(fg->reg)) + return PTR_ERR(fg->reg); + + fg->clk_disp = devm_clk_get(dev, NULL); + if (IS_ERR(fg->clk_disp)) + return dev_err_probe(dev, PTR_ERR(fg->clk_disp), + "failed to get display clock\n"); + + id = dc_subdev_get_id(dc_fg_info, ARRAY_SIZE(dc_fg_info), res); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + fg->dev = dev; + dc_drm->fg[id] = fg; + + return 0; +} + +static const struct component_ops dc_fg_ops = { + .bind = dc_fg_bind, +}; + +static int dc_fg_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_fg_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_fg_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_fg_ops); +} + +static const struct of_device_id dc_fg_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-framegen" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_fg_dt_ids); + +struct platform_driver dc_fg_driver = { + .probe = dc_fg_probe, + .remove = dc_fg_remove, + .driver = { + .name = "imx8-dc-framegen", + .suppress_bind_attrs = true, + .of_match_table = dc_fg_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-tc.c b/drivers/gpu/drm/imx/dc/dc-tc.c new file mode 100644 index 000000000000..0bfd381b2cea --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-tc.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include + +#include "dc-drv.h" +#include "dc-de.h" + +#define TCON_CTRL 0x410 +#define CTRL_RST_VAL 0x01401408 + +/* red: MAPBIT 29-20, green: MAPBIT 19-10, blue: MAPBIT 9-0 */ +#define MAPBIT3_0 0x418 +#define MAPBIT7_4 0x41c +#define MAPBIT11_8 0x420 +#define MAPBIT15_12 0x424 +#define MAPBIT19_16 0x428 +#define MAPBIT23_20 0x42c +#define MAPBIT27_24 0x430 +#define MAPBIT31_28 0x434 + +static const struct dc_subdev_info dc_tc_info[] = { + { .reg_start = 0x5618c800, .id = 0, }, + { .reg_start = 0x5618e400, .id = 1, }, +}; + +static const struct regmap_range dc_tc_regmap_ranges[] = { + regmap_reg_range(TCON_CTRL, TCON_CTRL), + regmap_reg_range(MAPBIT3_0, MAPBIT31_28), +}; + +static const struct regmap_access_table dc_tc_regmap_access_table = { + .yes_ranges = dc_tc_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_tc_regmap_ranges), +}; + +static const struct regmap_config dc_tc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_tc_regmap_access_table, + .rd_table = &dc_tc_regmap_access_table, + .max_register = MAPBIT31_28, +}; + +/* + * The pixels reach TCON are always in 30-bit BGR format. + * The first bridge always receives pixels in 30-bit RGB format. + * So, map the format to MEDIA_BUS_FMT_RGB101010_1X30. + */ +static const u32 dc_tc_mapbit[] = { + 0x17161514, 0x1b1a1918, 0x0b0a1d1c, 0x0f0e0d0c, + 0x13121110, 0x03020100, 0x07060504, 0x00000908, +}; + +void dc_tc_init(struct dc_tc *tc) +{ + /* reset TCON_CTRL to POR default so that TCON works in bypass mode */ + regmap_write(tc->reg, TCON_CTRL, CTRL_RST_VAL); + + /* set format */ + regmap_bulk_write(tc->reg, MAPBIT3_0, dc_tc_mapbit, + ARRAY_SIZE(dc_tc_mapbit)); +} + +static int dc_tc_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res; + void __iomem *base; + struct dc_tc *tc; + int id; + + tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); + if (!tc) + return -ENOMEM; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + tc->reg = devm_regmap_init_mmio(dev, base, &dc_tc_regmap_config); + if (IS_ERR(tc->reg)) + return PTR_ERR(tc->reg); + + id = dc_subdev_get_id(dc_tc_info, ARRAY_SIZE(dc_tc_info), res); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + tc->dev = dev; + dc_drm->tc[id] = tc; + + return 0; +} + +static const struct component_ops dc_tc_ops = { + .bind = dc_tc_bind, +}; + +static int dc_tc_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_tc_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_tc_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_tc_ops); +} + +static const struct of_device_id dc_tc_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-tcon" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_tc_dt_ids); + +struct platform_driver dc_tc_driver = { + .probe = dc_tc_probe, + .remove = dc_tc_remove, + .driver = { + .name = "imx8-dc-tcon", + .suppress_bind_attrs = true, + .of_match_table = dc_tc_dt_ids, + }, +}; From 0e177d5ce01ca52c5c754afbe8773d4ed5626cd6 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:19 +0800 Subject: [PATCH 016/279] drm/imx: Add i.MX8qxp Display Controller pixel engine i.MX8qxp Display Controller pixel engine consists of all processing units that operate in the AXI bus clock domain. Add drivers for ConstFrame, ExtDst, FetchLayer, FetchWarp and LayerBlend units, as well as a pixel engine driver, so that two displays with primary planes can be supported. The pixel engine driver and those unit drivers are components to be aggregated by a master registered in the upcoming DRM driver. Reviewed-by: Maxime Ripard Signed-off-by: Liu Ying Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250414035028.1561475-11-victor.liu@nxp.com --- drivers/gpu/drm/imx/dc/Makefile | 3 +- drivers/gpu/drm/imx/dc/dc-cf.c | 172 +++++++++++++++++ drivers/gpu/drm/imx/dc/dc-drv.c | 6 + drivers/gpu/drm/imx/dc/dc-drv.h | 22 +++ drivers/gpu/drm/imx/dc/dc-ed.c | 288 ++++++++++++++++++++++++++++ drivers/gpu/drm/imx/dc/dc-fl.c | 185 ++++++++++++++++++ drivers/gpu/drm/imx/dc/dc-fu.c | 258 +++++++++++++++++++++++++ drivers/gpu/drm/imx/dc/dc-fu.h | 129 +++++++++++++ drivers/gpu/drm/imx/dc/dc-fw.c | 222 ++++++++++++++++++++++ drivers/gpu/drm/imx/dc/dc-lb.c | 325 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/imx/dc/dc-pe.c | 158 ++++++++++++++++ drivers/gpu/drm/imx/dc/dc-pe.h | 101 ++++++++++ 12 files changed, 1868 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/imx/dc/dc-cf.c create mode 100644 drivers/gpu/drm/imx/dc/dc-ed.c create mode 100644 drivers/gpu/drm/imx/dc/dc-fl.c create mode 100644 drivers/gpu/drm/imx/dc/dc-fu.c create mode 100644 drivers/gpu/drm/imx/dc/dc-fu.h create mode 100644 drivers/gpu/drm/imx/dc/dc-fw.c create mode 100644 drivers/gpu/drm/imx/dc/dc-lb.c create mode 100644 drivers/gpu/drm/imx/dc/dc-pe.c create mode 100644 drivers/gpu/drm/imx/dc/dc-pe.h diff --git a/drivers/gpu/drm/imx/dc/Makefile b/drivers/gpu/drm/imx/dc/Makefile index 56de82d53d4d..2942ae6fd5bd 100644 --- a/drivers/gpu/drm/imx/dc/Makefile +++ b/drivers/gpu/drm/imx/dc/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -imx8-dc-drm-objs := dc-de.o dc-drv.o dc-fg.o dc-tc.o +imx8-dc-drm-objs := dc-cf.o dc-de.o dc-drv.o dc-ed.o dc-fg.o dc-fl.o dc-fu.o \ + dc-fw.o dc-lb.o dc-pe.o dc-tc.o obj-$(CONFIG_DRM_IMX8_DC) += imx8-dc-drm.o diff --git a/drivers/gpu/drm/imx/dc/dc-cf.c b/drivers/gpu/drm/imx/dc/dc-cf.c new file mode 100644 index 000000000000..2f077161e912 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-cf.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dc-drv.h" +#include "dc-pe.h" + +#define STATICCONTROL 0x8 + +#define FRAMEDIMENSIONS 0xc +#define HEIGHT(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1)) +#define WIDTH(x) FIELD_PREP(GENMASK(13, 0), ((x) - 1)) + +#define CONSTANTCOLOR 0x10 +#define BLUE(x) FIELD_PREP(GENMASK(15, 8), (x)) + +static const struct dc_subdev_info dc_cf_info[] = { + { .reg_start = 0x56180960, .id = 0, }, + { .reg_start = 0x561809e0, .id = 1, }, + { .reg_start = 0x561809a0, .id = 4, }, + { .reg_start = 0x56180a20, .id = 5, }, +}; + +static const struct regmap_range dc_cf_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, CONSTANTCOLOR), +}; + +static const struct regmap_access_table dc_cf_regmap_access_table = { + .yes_ranges = dc_cf_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_cf_regmap_ranges), +}; + +static const struct regmap_config dc_cf_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_cf_regmap_access_table, + .rd_table = &dc_cf_regmap_access_table, + .max_register = CONSTANTCOLOR, +}; + +static inline void dc_cf_enable_shden(struct dc_cf *cf) +{ + regmap_write(cf->reg_cfg, STATICCONTROL, SHDEN); +} + +enum dc_link_id dc_cf_get_link_id(struct dc_cf *cf) +{ + return cf->link; +} + +void dc_cf_framedimensions(struct dc_cf *cf, unsigned int w, + unsigned int h) +{ + regmap_write(cf->reg_cfg, FRAMEDIMENSIONS, WIDTH(w) | HEIGHT(h)); +} + +void dc_cf_constantcolor_black(struct dc_cf *cf) +{ + regmap_write(cf->reg_cfg, CONSTANTCOLOR, 0); +} + +void dc_cf_constantcolor_blue(struct dc_cf *cf) +{ + regmap_write(cf->reg_cfg, CONSTANTCOLOR, BLUE(0xff)); +} + +void dc_cf_init(struct dc_cf *cf) +{ + dc_cf_enable_shden(cf); +} + +static int dc_cf_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_cfg; + struct dc_cf *cf; + int id; + + cf = devm_kzalloc(dev, sizeof(*cf), GFP_KERNEL); + if (!cf) + return -ENOMEM; + + res_pec = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + cf->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_cf_cfg_regmap_config); + if (IS_ERR(cf->reg_cfg)) + return PTR_ERR(cf->reg_cfg); + + id = dc_subdev_get_id(dc_cf_info, ARRAY_SIZE(dc_cf_info), res_pec); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + switch (id) { + case 0: + cf->link = LINK_ID_CONSTFRAME0; + dc_drm->cf_cont[0] = cf; + break; + case 1: + cf->link = LINK_ID_CONSTFRAME1; + dc_drm->cf_cont[1] = cf; + break; + case 4: + cf->link = LINK_ID_CONSTFRAME4; + dc_drm->cf_safe[0] = cf; + break; + case 5: + cf->link = LINK_ID_CONSTFRAME5; + dc_drm->cf_safe[1] = cf; + break; + } + + return 0; +} + +static const struct component_ops dc_cf_ops = { + .bind = dc_cf_bind, +}; + +static int dc_cf_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_cf_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_cf_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_cf_ops); +} + +static const struct of_device_id dc_cf_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-constframe" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_cf_dt_ids); + +struct platform_driver dc_cf_driver = { + .probe = dc_cf_probe, + .remove = dc_cf_remove, + .driver = { + .name = "imx8-dc-constframe", + .suppress_bind_attrs = true, + .of_match_table = dc_cf_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c index e5910a82dd4d..7c64acc863ad 100644 --- a/drivers/gpu/drm/imx/dc/dc-drv.c +++ b/drivers/gpu/drm/imx/dc/dc-drv.c @@ -9,8 +9,14 @@ #include "dc-drv.h" static struct platform_driver * const dc_drivers[] = { + &dc_cf_driver, &dc_de_driver, + &dc_ed_driver, &dc_fg_driver, + &dc_fl_driver, + &dc_fw_driver, + &dc_lb_driver, + &dc_pe_driver, &dc_tc_driver, }; diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h index 65ae9c7c3694..b9fe12577a19 100644 --- a/drivers/gpu/drm/imx/dc/dc-drv.h +++ b/drivers/gpu/drm/imx/dc/dc-drv.h @@ -13,6 +13,7 @@ #include #include "dc-de.h" +#include "dc-pe.h" /** * struct dc_drm_device - DC specific drm_device @@ -20,10 +21,24 @@ struct dc_drm_device { /** @base: base drm_device structure */ struct drm_device base; + /** @cf_safe: constframe list(safety stream) */ + struct dc_cf *cf_safe[DC_DISPLAYS]; + /** @cf_cont: constframe list(content stream) */ + struct dc_cf *cf_cont[DC_DISPLAYS]; /** @de: display engine list */ struct dc_de *de[DC_DISPLAYS]; + /** @ed_safe: extdst list(safety stream) */ + struct dc_ed *ed_safe[DC_DISPLAYS]; + /** @ed_cont: extdst list(content stream) */ + struct dc_ed *ed_cont[DC_DISPLAYS]; /** @fg: framegen list */ struct dc_fg *fg[DC_DISPLAYS]; + /** @fu_disp: fetchunit list(used by display engine) */ + struct dc_fu *fu_disp[DC_DISP_FU_CNT]; + /** @lb: layerblend list */ + struct dc_lb *lb[DC_LB_CNT]; + /** @pe: pixel engine */ + struct dc_pe *pe; /** @tc: tcon list */ struct dc_tc *tc[DC_DISPLAYS]; }; @@ -33,8 +48,14 @@ struct dc_subdev_info { int id; }; +extern struct platform_driver dc_cf_driver; extern struct platform_driver dc_de_driver; +extern struct platform_driver dc_ed_driver; extern struct platform_driver dc_fg_driver; +extern struct platform_driver dc_fl_driver; +extern struct platform_driver dc_fw_driver; +extern struct platform_driver dc_lb_driver; +extern struct platform_driver dc_pe_driver; extern struct platform_driver dc_tc_driver; static inline int dc_subdev_get_id(const struct dc_subdev_info *info, @@ -53,5 +74,6 @@ static inline int dc_subdev_get_id(const struct dc_subdev_info *info, } void dc_de_post_bind(struct dc_drm_device *dc_drm); +void dc_pe_post_bind(struct dc_drm_device *dc_drm); #endif /* __DC_DRV_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-ed.c b/drivers/gpu/drm/imx/dc/dc-ed.c new file mode 100644 index 000000000000..86ecc22d0a55 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-ed.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dc-drv.h" +#include "dc-pe.h" + +#define PIXENGCFG_STATIC 0x8 +#define POWERDOWN BIT(4) +#define SYNC_MODE BIT(8) +#define SINGLE 0 +#define DIV_MASK GENMASK(23, 16) +#define DIV(x) FIELD_PREP(DIV_MASK, (x)) +#define DIV_RESET 0x80 + +#define PIXENGCFG_DYNAMIC 0xc + +#define PIXENGCFG_TRIGGER 0x14 +#define SYNC_TRIGGER BIT(0) + +#define STATICCONTROL 0x8 +#define KICK_MODE BIT(8) +#define EXTERNAL BIT(8) +#define PERFCOUNTMODE BIT(12) + +#define CONTROL 0xc +#define GAMMAAPPLYENABLE BIT(0) + +static const struct dc_subdev_info dc_ed_info[] = { + { .reg_start = 0x56180980, .id = 0, }, + { .reg_start = 0x56180a00, .id = 1, }, + { .reg_start = 0x561809c0, .id = 4, }, + { .reg_start = 0x56180a40, .id = 5, }, +}; + +static const struct regmap_range dc_ed_pec_regmap_write_ranges[] = { + regmap_reg_range(PIXENGCFG_STATIC, PIXENGCFG_STATIC), + regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), + regmap_reg_range(PIXENGCFG_TRIGGER, PIXENGCFG_TRIGGER), +}; + +static const struct regmap_access_table dc_ed_pec_regmap_write_table = { + .yes_ranges = dc_ed_pec_regmap_write_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_write_ranges), +}; + +static const struct regmap_range dc_ed_pec_regmap_read_ranges[] = { + regmap_reg_range(PIXENGCFG_STATIC, PIXENGCFG_STATIC), + regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), +}; + +static const struct regmap_access_table dc_ed_pec_regmap_read_table = { + .yes_ranges = dc_ed_pec_regmap_read_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_read_ranges), +}; + +static const struct regmap_range dc_ed_pec_regmap_volatile_ranges[] = { + regmap_reg_range(PIXENGCFG_TRIGGER, PIXENGCFG_TRIGGER), +}; + +static const struct regmap_access_table dc_ed_pec_regmap_volatile_table = { + .yes_ranges = dc_ed_pec_regmap_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_volatile_ranges), +}; + +static const struct regmap_config dc_ed_pec_regmap_config = { + .name = "pec", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_ed_pec_regmap_write_table, + .rd_table = &dc_ed_pec_regmap_read_table, + .volatile_table = &dc_ed_pec_regmap_volatile_table, + .max_register = PIXENGCFG_TRIGGER, +}; + +static const struct regmap_range dc_ed_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, STATICCONTROL), + regmap_reg_range(CONTROL, CONTROL), +}; + +static const struct regmap_access_table dc_ed_regmap_access_table = { + .yes_ranges = dc_ed_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ed_regmap_ranges), +}; + +static const struct regmap_config dc_ed_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_ed_regmap_access_table, + .rd_table = &dc_ed_regmap_access_table, + .max_register = CONTROL, +}; + +static const enum dc_link_id src_sels[] = { + LINK_ID_NONE, + LINK_ID_CONSTFRAME0, + LINK_ID_CONSTFRAME1, + LINK_ID_CONSTFRAME4, + LINK_ID_CONSTFRAME5, + LINK_ID_LAYERBLEND3, + LINK_ID_LAYERBLEND2, + LINK_ID_LAYERBLEND1, + LINK_ID_LAYERBLEND0, +}; + +static inline void dc_ed_pec_enable_shden(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, SHDEN, SHDEN); +} + +static inline void dc_ed_pec_poweron(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, POWERDOWN, 0); +} + +static inline void dc_ed_pec_sync_mode_single(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, SYNC_MODE, SINGLE); +} + +static inline void dc_ed_pec_div_reset(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, DIV_MASK, + DIV(DIV_RESET)); +} + +void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(src_sels); i++) { + if (src_sels[i] == src) { + regmap_write(ed->reg_pec, PIXENGCFG_DYNAMIC, src); + return; + } + } +} + +void dc_ed_pec_sync_trigger(struct dc_ed *ed) +{ + regmap_write(ed->reg_pec, PIXENGCFG_TRIGGER, SYNC_TRIGGER); +} + +static inline void dc_ed_enable_shden(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_cfg, STATICCONTROL, SHDEN, SHDEN); +} + +static inline void dc_ed_kick_mode_external(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_cfg, STATICCONTROL, KICK_MODE, EXTERNAL); +} + +static inline void dc_ed_disable_perfcountmode(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_cfg, STATICCONTROL, PERFCOUNTMODE, 0); +} + +static inline void dc_ed_disable_gamma_apply(struct dc_ed *ed) +{ + regmap_write_bits(ed->reg_cfg, CONTROL, GAMMAAPPLYENABLE, 0); +} + +void dc_ed_init(struct dc_ed *ed) +{ + dc_ed_pec_src_sel(ed, LINK_ID_NONE); + dc_ed_pec_enable_shden(ed); + dc_ed_pec_poweron(ed); + dc_ed_pec_sync_mode_single(ed); + dc_ed_pec_div_reset(ed); + dc_ed_enable_shden(ed); + dc_ed_disable_perfcountmode(ed); + dc_ed_kick_mode_external(ed); + dc_ed_disable_gamma_apply(ed); +} + +static int dc_ed_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_pec; + void __iomem *base_cfg; + struct dc_ed *ed; + int id; + + ed = devm_kzalloc(dev, sizeof(*ed), GFP_KERNEL); + if (!ed) + return -ENOMEM; + + base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec); + if (IS_ERR(base_pec)) + return PTR_ERR(base_pec); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + ed->reg_pec = devm_regmap_init_mmio(dev, base_pec, + &dc_ed_pec_regmap_config); + if (IS_ERR(ed->reg_pec)) + return PTR_ERR(ed->reg_pec); + + ed->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_ed_cfg_regmap_config); + if (IS_ERR(ed->reg_cfg)) + return PTR_ERR(ed->reg_cfg); + + ed->irq_shdload = platform_get_irq_byname(pdev, "shdload"); + if (ed->irq_shdload < 0) + return ed->irq_shdload; + + ed->dev = dev; + + id = dc_subdev_get_id(dc_ed_info, ARRAY_SIZE(dc_ed_info), res_pec); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + switch (id) { + case 0: + dc_drm->ed_cont[0] = ed; + break; + case 1: + dc_drm->ed_cont[1] = ed; + break; + case 4: + dc_drm->ed_safe[0] = ed; + break; + case 5: + dc_drm->ed_safe[1] = ed; + break; + } + + return 0; +} + +static const struct component_ops dc_ed_ops = { + .bind = dc_ed_bind, +}; + +static int dc_ed_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_ed_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_ed_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_ed_ops); +} + +static const struct of_device_id dc_ed_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-extdst" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_ed_dt_ids); + +struct platform_driver dc_ed_driver = { + .probe = dc_ed_probe, + .remove = dc_ed_remove, + .driver = { + .name = "imx8-dc-extdst", + .suppress_bind_attrs = true, + .of_match_table = dc_ed_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-fl.c b/drivers/gpu/drm/imx/dc/dc-fl.c new file mode 100644 index 000000000000..3ce24c72aa13 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fl.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "dc-drv.h" +#include "dc-fu.h" + +#define BASEADDRESS(x) (0x10 + FRAC_OFFSET * (x)) +#define SOURCEBUFFERATTRIBUTES(x) (0x14 + FRAC_OFFSET * (x)) +#define SOURCEBUFFERDIMENSION(x) (0x18 + FRAC_OFFSET * (x)) +#define COLORCOMPONENTBITS(x) (0x1c + FRAC_OFFSET * (x)) +#define COLORCOMPONENTSHIFT(x) (0x20 + FRAC_OFFSET * (x)) +#define LAYEROFFSET(x) (0x24 + FRAC_OFFSET * (x)) +#define CLIPWINDOWOFFSET(x) (0x28 + FRAC_OFFSET * (x)) +#define CLIPWINDOWDIMENSIONS(x) (0x2c + FRAC_OFFSET * (x)) +#define CONSTANTCOLOR(x) (0x30 + FRAC_OFFSET * (x)) +#define LAYERPROPERTY(x) (0x34 + FRAC_OFFSET * (x)) +#define FRAMEDIMENSIONS 0x150 + +struct dc_fl { + struct dc_fu fu; +}; + +static const struct dc_subdev_info dc_fl_info[] = { + { .reg_start = 0x56180ac0, .id = 0, }, +}; + +static const struct regmap_range dc_fl_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, FRAMEDIMENSIONS), +}; + +static const struct regmap_access_table dc_fl_regmap_access_table = { + .yes_ranges = dc_fl_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fl_regmap_ranges), +}; + +static const struct regmap_config dc_fl_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_fl_regmap_access_table, + .rd_table = &dc_fl_regmap_access_table, + .max_register = FRAMEDIMENSIONS, +}; + +static void dc_fl_set_fmt(struct dc_fu *fu, enum dc_fu_frac frac, + const struct drm_format_info *format) +{ + u32 bits = 0, shifts = 0; + + dc_fu_set_src_bpp(fu, frac, format->cpp[0] * 8); + + regmap_write_bits(fu->reg_cfg, LAYERPROPERTY(frac), + YUVCONVERSIONMODE_MASK, + YUVCONVERSIONMODE(YUVCONVERSIONMODE_OFF)); + + dc_fu_get_pixel_format_bits(fu, format->format, &bits); + dc_fu_get_pixel_format_shifts(fu, format->format, &shifts); + + regmap_write(fu->reg_cfg, COLORCOMPONENTBITS(frac), bits); + regmap_write(fu->reg_cfg, COLORCOMPONENTSHIFT(frac), shifts); +} + +static void dc_fl_set_framedimensions(struct dc_fu *fu, int w, int h) +{ + regmap_write(fu->reg_cfg, FRAMEDIMENSIONS, + FRAMEWIDTH(w) | FRAMEHEIGHT(h)); +} + +static void dc_fl_init(struct dc_fu *fu) +{ + dc_fu_common_hw_init(fu); + dc_fu_shdldreq_sticky(fu, 0xff); +} + +static void dc_fl_set_ops(struct dc_fu *fu) +{ + memcpy(&fu->ops, &dc_fu_common_ops, sizeof(dc_fu_common_ops)); + fu->ops.init = dc_fl_init; + fu->ops.set_fmt = dc_fl_set_fmt; + fu->ops.set_framedimensions = dc_fl_set_framedimensions; +} + +static int dc_fl_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_cfg; + struct dc_fl *fl; + struct dc_fu *fu; + int i, id; + + fl = devm_kzalloc(dev, sizeof(*fl), GFP_KERNEL); + if (!fl) + return -ENOMEM; + + fu = &fl->fu; + + res_pec = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + fu->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_fl_cfg_regmap_config); + if (IS_ERR(fu->reg_cfg)) + return PTR_ERR(fu->reg_cfg); + + id = dc_subdev_get_id(dc_fl_info, ARRAY_SIZE(dc_fl_info), res_pec); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + fu->link_id = LINK_ID_FETCHLAYER0; + fu->id = DC_FETCHUNIT_FL0; + for (i = 0; i < DC_FETCHUNIT_FRAC_NUM; i++) { + fu->reg_baseaddr[i] = BASEADDRESS(i); + fu->reg_sourcebufferattributes[i] = SOURCEBUFFERATTRIBUTES(i); + fu->reg_sourcebufferdimension[i] = SOURCEBUFFERDIMENSION(i); + fu->reg_layeroffset[i] = LAYEROFFSET(i); + fu->reg_clipwindowoffset[i] = CLIPWINDOWOFFSET(i); + fu->reg_clipwindowdimensions[i] = CLIPWINDOWDIMENSIONS(i); + fu->reg_constantcolor[i] = CONSTANTCOLOR(i); + fu->reg_layerproperty[i] = LAYERPROPERTY(i); + } + snprintf(fu->name, sizeof(fu->name), "FetchLayer%d", id); + + dc_fl_set_ops(fu); + + dc_drm->fu_disp[fu->id] = fu; + + return 0; +} + +static const struct component_ops dc_fl_ops = { + .bind = dc_fl_bind, +}; + +static int dc_fl_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_fl_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_fl_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_fl_ops); +} + +static const struct of_device_id dc_fl_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-fetchlayer" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_fl_dt_ids); + +struct platform_driver dc_fl_driver = { + .probe = dc_fl_probe, + .remove = dc_fl_remove, + .driver = { + .name = "imx8-dc-fetchlayer", + .suppress_bind_attrs = true, + .of_match_table = dc_fl_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-fu.c b/drivers/gpu/drm/imx/dc/dc-fu.c new file mode 100644 index 000000000000..f94c591c8158 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fu.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include + +#include "dc-fu.h" +#include "dc-pe.h" + +/* STATICCONTROL */ +#define SHDLDREQSTICKY_MASK GENMASK(31, 24) +#define SHDLDREQSTICKY(x) FIELD_PREP(SHDLDREQSTICKY_MASK, (x)) +#define BASEADDRESSAUTOUPDATE_MASK GENMASK(23, 16) +#define BASEADDRESSAUTOUPDATE(x) FIELD_PREP(BASEADDRESSAUTOUPDATE_MASK, (x)) + +/* BURSTBUFFERMANAGEMENT */ +#define SETBURSTLENGTH_MASK GENMASK(12, 8) +#define SETBURSTLENGTH(x) FIELD_PREP(SETBURSTLENGTH_MASK, (x)) +#define SETNUMBUFFERS_MASK GENMASK(7, 0) +#define SETNUMBUFFERS(x) FIELD_PREP(SETNUMBUFFERS_MASK, (x)) +#define LINEMODE_MASK BIT(31) + +/* SOURCEBUFFERATTRIBUTES */ +#define BITSPERPIXEL_MASK GENMASK(21, 16) +#define BITSPERPIXEL(x) FIELD_PREP(BITSPERPIXEL_MASK, (x)) +#define STRIDE_MASK GENMASK(15, 0) +#define STRIDE(x) FIELD_PREP(STRIDE_MASK, (x) - 1) + +/* SOURCEBUFFERDIMENSION */ +#define LINEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x)) +#define LINECOUNT(x) FIELD_PREP(GENMASK(29, 16), (x)) + +/* LAYEROFFSET */ +#define LAYERXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x)) +#define LAYERYOFFSET(x) FIELD_PREP(GENMASK(30, 16), (x)) + +/* CLIPWINDOWOFFSET */ +#define CLIPWINDOWXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x)) +#define CLIPWINDOWYOFFSET(x) FIELD_PREP(GENMASK(30, 16), (x)) + +/* CLIPWINDOWDIMENSIONS */ +#define CLIPWINDOWWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x) - 1) +#define CLIPWINDOWHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x) - 1) + +enum dc_linemode { + /* + * Mandatory setting for operation in the Display Controller. + * Works also for Blit Engine with marginal performance impact. + */ + LINEMODE_DISPLAY = 0, +}; + +struct dc_fu_pixel_format { + u32 pixel_format; + u32 bits; + u32 shifts; +}; + +static const struct dc_fu_pixel_format pixel_formats[] = { + { + DRM_FORMAT_XRGB8888, + R_BITS(8) | G_BITS(8) | B_BITS(8) | A_BITS(0), + R_SHIFT(16) | G_SHIFT(8) | B_SHIFT(0) | A_SHIFT(0), + }, +}; + +void dc_fu_get_pixel_format_bits(struct dc_fu *fu, u32 format, u32 *bits) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { + if (pixel_formats[i].pixel_format == format) { + *bits = pixel_formats[i].bits; + return; + } + } +} + +void +dc_fu_get_pixel_format_shifts(struct dc_fu *fu, u32 format, u32 *shifts) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pixel_formats); i++) { + if (pixel_formats[i].pixel_format == format) { + *shifts = pixel_formats[i].shifts; + return; + } + } +} + +static inline void dc_fu_enable_shden(struct dc_fu *fu) +{ + regmap_write_bits(fu->reg_cfg, STATICCONTROL, SHDEN, SHDEN); +} + +static inline void dc_fu_baddr_autoupdate(struct dc_fu *fu, u8 layer_mask) +{ + regmap_write_bits(fu->reg_cfg, STATICCONTROL, + BASEADDRESSAUTOUPDATE_MASK, + BASEADDRESSAUTOUPDATE(layer_mask)); +} + +void dc_fu_shdldreq_sticky(struct dc_fu *fu, u8 layer_mask) +{ + regmap_write_bits(fu->reg_cfg, STATICCONTROL, SHDLDREQSTICKY_MASK, + SHDLDREQSTICKY(layer_mask)); +} + +static inline void dc_fu_set_linemode(struct dc_fu *fu, enum dc_linemode mode) +{ + regmap_write_bits(fu->reg_cfg, BURSTBUFFERMANAGEMENT, LINEMODE_MASK, + mode); +} + +static inline void dc_fu_set_numbuffers(struct dc_fu *fu, unsigned int num) +{ + regmap_write_bits(fu->reg_cfg, BURSTBUFFERMANAGEMENT, + SETNUMBUFFERS_MASK, SETNUMBUFFERS(num)); +} + +static void dc_fu_set_burstlength(struct dc_fu *fu, dma_addr_t baddr) +{ + unsigned int burst_size, burst_length; + + burst_size = 1 << __ffs(baddr); + burst_size = round_up(burst_size, 8); + burst_size = min(burst_size, 128U); + burst_length = burst_size / 8; + + regmap_write_bits(fu->reg_cfg, BURSTBUFFERMANAGEMENT, + SETBURSTLENGTH_MASK, SETBURSTLENGTH(burst_length)); +} + +static void dc_fu_set_baseaddress(struct dc_fu *fu, enum dc_fu_frac frac, + dma_addr_t baddr) +{ + regmap_write(fu->reg_cfg, fu->reg_baseaddr[frac], baddr); +} + +void dc_fu_set_src_bpp(struct dc_fu *fu, enum dc_fu_frac frac, unsigned int bpp) +{ + regmap_write_bits(fu->reg_cfg, fu->reg_sourcebufferattributes[frac], + BITSPERPIXEL_MASK, BITSPERPIXEL(bpp)); +} + +static void dc_fu_set_src_stride(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int stride) +{ + regmap_write_bits(fu->reg_cfg, fu->reg_sourcebufferattributes[frac], + STRIDE_MASK, STRIDE(stride)); +} + +static void dc_fu_set_src_buf_dimensions(struct dc_fu *fu, enum dc_fu_frac frac, + int w, int h) +{ + regmap_write(fu->reg_cfg, fu->reg_sourcebufferdimension[frac], + LINEWIDTH(w) | LINECOUNT(h)); +} + +static inline void dc_fu_layeroffset(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int x, unsigned int y) +{ + regmap_write(fu->reg_cfg, fu->reg_layeroffset[frac], + LAYERXOFFSET(x) | LAYERYOFFSET(y)); +} + +static inline void dc_fu_clipoffset(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int x, unsigned int y) +{ + regmap_write(fu->reg_cfg, fu->reg_clipwindowoffset[frac], + CLIPWINDOWXOFFSET(x) | CLIPWINDOWYOFFSET(y)); +} + +static inline void dc_fu_clipdimensions(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int w, unsigned int h) +{ + regmap_write(fu->reg_cfg, fu->reg_clipwindowdimensions[frac], + CLIPWINDOWWIDTH(w) | CLIPWINDOWHEIGHT(h)); +} + +static inline void +dc_fu_set_pixel_blend_mode(struct dc_fu *fu, enum dc_fu_frac frac) +{ + regmap_write(fu->reg_cfg, fu->reg_layerproperty[frac], 0); + regmap_write(fu->reg_cfg, fu->reg_constantcolor[frac], 0); +} + +static void dc_fu_enable_src_buf(struct dc_fu *fu, enum dc_fu_frac frac) +{ + regmap_write_bits(fu->reg_cfg, fu->reg_layerproperty[frac], + SOURCEBUFFERENABLE, SOURCEBUFFERENABLE); +} + +static void dc_fu_disable_src_buf(struct dc_fu *fu, enum dc_fu_frac frac) +{ + regmap_write_bits(fu->reg_cfg, fu->reg_layerproperty[frac], + SOURCEBUFFERENABLE, 0); + + if (fu->lb) { + dc_lb_pec_clken(fu->lb, CLKEN_DISABLE); + dc_lb_mode(fu->lb, LB_NEUTRAL); + } +} + +static void dc_fu_set_layerblend(struct dc_fu *fu, struct dc_lb *lb) +{ + fu->lb = lb; +} + +static enum dc_link_id dc_fu_get_link_id(struct dc_fu *fu) +{ + return fu->link_id; +} + +static const char *dc_fu_get_name(struct dc_fu *fu) +{ + return fu->name; +} + +const struct dc_fu_ops dc_fu_common_ops = { + .set_burstlength = dc_fu_set_burstlength, + .set_baseaddress = dc_fu_set_baseaddress, + .set_src_stride = dc_fu_set_src_stride, + .set_src_buf_dimensions = dc_fu_set_src_buf_dimensions, + .enable_src_buf = dc_fu_enable_src_buf, + .disable_src_buf = dc_fu_disable_src_buf, + .set_layerblend = dc_fu_set_layerblend, + .get_link_id = dc_fu_get_link_id, + .get_name = dc_fu_get_name, +}; + +const struct dc_fu_ops *dc_fu_get_ops(struct dc_fu *fu) +{ + return &fu->ops; +} + +void dc_fu_common_hw_init(struct dc_fu *fu) +{ + enum dc_fu_frac i; + + dc_fu_baddr_autoupdate(fu, 0x0); + dc_fu_enable_shden(fu); + dc_fu_set_linemode(fu, LINEMODE_DISPLAY); + dc_fu_set_numbuffers(fu, 16); + + for (i = DC_FETCHUNIT_FRAC0; i < DC_FETCHUNIT_FRAC_NUM; i++) { + dc_fu_layeroffset(fu, i, 0, 0); + dc_fu_clipoffset(fu, i, 0, 0); + dc_fu_clipdimensions(fu, i, 1, 1); + dc_fu_disable_src_buf(fu, i); + dc_fu_set_pixel_blend_mode(fu, i); + } +} diff --git a/drivers/gpu/drm/imx/dc/dc-fu.h b/drivers/gpu/drm/imx/dc/dc-fu.h new file mode 100644 index 000000000000..e016e1ea5b4e --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fu.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_FETCHUNIT_H__ +#define __DC_FETCHUNIT_H__ + +#include +#include +#include +#include + +#include + +#include "dc-pe.h" + +#define FRAC_OFFSET 0x28 + +#define STATICCONTROL 0x8 +#define BURSTBUFFERMANAGEMENT 0xc + +/* COLORCOMPONENTBITS */ +#define R_BITS(x) FIELD_PREP_CONST(GENMASK(27, 24), (x)) +#define G_BITS(x) FIELD_PREP_CONST(GENMASK(19, 16), (x)) +#define B_BITS(x) FIELD_PREP_CONST(GENMASK(11, 8), (x)) +#define A_BITS(x) FIELD_PREP_CONST(GENMASK(3, 0), (x)) + +/* COLORCOMPONENTSHIFT */ +#define R_SHIFT(x) FIELD_PREP_CONST(GENMASK(28, 24), (x)) +#define G_SHIFT(x) FIELD_PREP_CONST(GENMASK(20, 16), (x)) +#define B_SHIFT(x) FIELD_PREP_CONST(GENMASK(12, 8), (x)) +#define A_SHIFT(x) FIELD_PREP_CONST(GENMASK(4, 0), (x)) + +/* LAYERPROPERTY */ +#define YUVCONVERSIONMODE_MASK GENMASK(18, 17) +#define YUVCONVERSIONMODE(x) FIELD_PREP(YUVCONVERSIONMODE_MASK, (x)) +#define SOURCEBUFFERENABLE BIT(31) + +/* FRAMEDIMENSIONS */ +#define FRAMEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x)) +#define FRAMEHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x)) + +/* CONTROL */ +#define INPUTSELECT_MASK GENMASK(4, 3) +#define INPUTSELECT(x) FIELD_PREP(INPUTSELECT_MASK, (x)) +#define RASTERMODE_MASK GENMASK(2, 0) +#define RASTERMODE(x) FIELD_PREP(RASTERMODE_MASK, (x)) + +enum dc_yuvconversionmode { + YUVCONVERSIONMODE_OFF, +}; + +enum dc_inputselect { + INPUTSELECT_INACTIVE, +}; + +enum dc_rastermode { + RASTERMODE_NORMAL, +}; + +enum { + DC_FETCHUNIT_FL0, + DC_FETCHUNIT_FW2, +}; + +enum dc_fu_frac { + DC_FETCHUNIT_FRAC0, + DC_FETCHUNIT_FRAC1, + DC_FETCHUNIT_FRAC2, + DC_FETCHUNIT_FRAC3, + DC_FETCHUNIT_FRAC4, + DC_FETCHUNIT_FRAC5, + DC_FETCHUNIT_FRAC6, + DC_FETCHUNIT_FRAC7, + DC_FETCHUNIT_FRAC_NUM +}; + +struct dc_fu; +struct dc_lb; + +struct dc_fu_ops { + void (*init)(struct dc_fu *fu); + void (*set_burstlength)(struct dc_fu *fu, dma_addr_t baddr); + void (*set_baseaddress)(struct dc_fu *fu, enum dc_fu_frac frac, + dma_addr_t baddr); + void (*set_src_stride)(struct dc_fu *fu, enum dc_fu_frac frac, + unsigned int stride); + void (*set_src_buf_dimensions)(struct dc_fu *fu, enum dc_fu_frac frac, + int w, int h); + void (*set_fmt)(struct dc_fu *fu, enum dc_fu_frac frac, + const struct drm_format_info *format); + void (*enable_src_buf)(struct dc_fu *fu, enum dc_fu_frac frac); + void (*disable_src_buf)(struct dc_fu *fu, enum dc_fu_frac frac); + void (*set_framedimensions)(struct dc_fu *fu, int w, int h); + void (*set_layerblend)(struct dc_fu *fu, struct dc_lb *lb); + enum dc_link_id (*get_link_id)(struct dc_fu *fu); + const char *(*get_name)(struct dc_fu *fu); +}; + +struct dc_fu { + struct regmap *reg_pec; + struct regmap *reg_cfg; + char name[21]; + u32 reg_baseaddr[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_sourcebufferattributes[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_sourcebufferdimension[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_layeroffset[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_clipwindowoffset[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_clipwindowdimensions[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_constantcolor[DC_FETCHUNIT_FRAC_NUM]; + u32 reg_layerproperty[DC_FETCHUNIT_FRAC_NUM]; + unsigned int id; + enum dc_link_id link_id; + struct dc_fu_ops ops; + struct dc_lb *lb; +}; + +extern const struct dc_fu_ops dc_fu_common_ops; + +void dc_fu_get_pixel_format_bits(struct dc_fu *fu, u32 format, u32 *bits); +void dc_fu_get_pixel_format_shifts(struct dc_fu *fu, u32 format, u32 *shifts); +void dc_fu_shdldreq_sticky(struct dc_fu *fu, u8 layer_mask); +void dc_fu_set_src_bpp(struct dc_fu *fu, enum dc_fu_frac frac, unsigned int bpp); +void dc_fu_common_hw_init(struct dc_fu *fu); + +const struct dc_fu_ops *dc_fu_get_ops(struct dc_fu *fu); + +#endif /* __DC_FETCHUNIT_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-fw.c b/drivers/gpu/drm/imx/dc/dc-fw.c new file mode 100644 index 000000000000..acb2d4d9e2ec --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-fw.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include + +#include + +#include "dc-drv.h" +#include "dc-fu.h" + +#define PIXENGCFG_DYNAMIC 0x8 + +#define BASEADDRESS(x) (0x10 + FRAC_OFFSET * (x)) +#define SOURCEBUFFERATTRIBUTES(x) (0x14 + FRAC_OFFSET * (x)) +#define SOURCEBUFFERDIMENSION(x) (0x18 + FRAC_OFFSET * (x)) +#define COLORCOMPONENTBITS(x) (0x1c + FRAC_OFFSET * (x)) +#define COLORCOMPONENTSHIFT(x) (0x20 + FRAC_OFFSET * (x)) +#define LAYEROFFSET(x) (0x24 + FRAC_OFFSET * (x)) +#define CLIPWINDOWOFFSET(x) (0x28 + FRAC_OFFSET * (x)) +#define CLIPWINDOWDIMENSIONS(x) (0x2c + FRAC_OFFSET * (x)) +#define CONSTANTCOLOR(x) (0x30 + FRAC_OFFSET * (x)) +#define LAYERPROPERTY(x) (0x34 + FRAC_OFFSET * (x)) +#define FRAMEDIMENSIONS 0x150 +#define CONTROL 0x170 + +struct dc_fw { + struct dc_fu fu; +}; + +static const struct dc_subdev_info dc_fw_info[] = { + { .reg_start = 0x56180a60, .id = 2, }, +}; + +static const struct regmap_range dc_fw_pec_regmap_access_ranges[] = { + regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), +}; + +static const struct regmap_access_table dc_fw_pec_regmap_access_table = { + .yes_ranges = dc_fw_pec_regmap_access_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fw_pec_regmap_access_ranges), +}; + +static const struct regmap_config dc_fw_pec_regmap_config = { + .name = "pec", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_fw_pec_regmap_access_table, + .rd_table = &dc_fw_pec_regmap_access_table, + .max_register = PIXENGCFG_DYNAMIC, +}; + +static const struct regmap_range dc_fw_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, FRAMEDIMENSIONS), + regmap_reg_range(CONTROL, CONTROL), +}; + +static const struct regmap_access_table dc_fw_regmap_access_table = { + .yes_ranges = dc_fw_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_fw_regmap_ranges), +}; + +static const struct regmap_config dc_fw_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_fw_regmap_access_table, + .rd_table = &dc_fw_regmap_access_table, + .max_register = CONTROL, +}; + +static void dc_fw_set_fmt(struct dc_fu *fu, enum dc_fu_frac frac, + const struct drm_format_info *format) +{ + u32 bits = 0, shifts = 0; + + dc_fu_set_src_bpp(fu, frac, format->cpp[0] * 8); + + regmap_write_bits(fu->reg_cfg, CONTROL, INPUTSELECT_MASK, + INPUTSELECT(INPUTSELECT_INACTIVE)); + regmap_write_bits(fu->reg_cfg, CONTROL, RASTERMODE_MASK, + RASTERMODE(RASTERMODE_NORMAL)); + + regmap_write_bits(fu->reg_cfg, LAYERPROPERTY(frac), + YUVCONVERSIONMODE_MASK, + YUVCONVERSIONMODE(YUVCONVERSIONMODE_OFF)); + + dc_fu_get_pixel_format_bits(fu, format->format, &bits); + dc_fu_get_pixel_format_shifts(fu, format->format, &shifts); + + regmap_write(fu->reg_cfg, COLORCOMPONENTBITS(frac), bits); + regmap_write(fu->reg_cfg, COLORCOMPONENTSHIFT(frac), shifts); +} + +static void dc_fw_set_framedimensions(struct dc_fu *fu, int w, int h) +{ + regmap_write(fu->reg_cfg, FRAMEDIMENSIONS, + FRAMEWIDTH(w) | FRAMEHEIGHT(h)); +} + +static void dc_fw_init(struct dc_fu *fu) +{ + regmap_write(fu->reg_pec, PIXENGCFG_DYNAMIC, LINK_ID_NONE); + dc_fu_common_hw_init(fu); + dc_fu_shdldreq_sticky(fu, 0xff); +} + +static void dc_fw_set_ops(struct dc_fu *fu) +{ + memcpy(&fu->ops, &dc_fu_common_ops, sizeof(dc_fu_common_ops)); + fu->ops.init = dc_fw_init; + fu->ops.set_fmt = dc_fw_set_fmt; + fu->ops.set_framedimensions = dc_fw_set_framedimensions; +} + +static int dc_fw_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_pec; + void __iomem *base_cfg; + struct dc_fw *fw; + struct dc_fu *fu; + int i, id; + + fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return -ENOMEM; + + fu = &fw->fu; + + base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec); + if (IS_ERR(base_pec)) + return PTR_ERR(base_pec); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + fu->reg_pec = devm_regmap_init_mmio(dev, base_pec, + &dc_fw_pec_regmap_config); + if (IS_ERR(fu->reg_pec)) + return PTR_ERR(fu->reg_pec); + + fu->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_fw_cfg_regmap_config); + if (IS_ERR(fu->reg_cfg)) + return PTR_ERR(fu->reg_cfg); + + id = dc_subdev_get_id(dc_fw_info, ARRAY_SIZE(dc_fw_info), res_pec); + if (id < 0) { + dev_err(dev, "failed to get instance number: %d\n", id); + return id; + } + + fu->link_id = LINK_ID_FETCHWARP2; + fu->id = DC_FETCHUNIT_FW2; + for (i = 0; i < DC_FETCHUNIT_FRAC_NUM; i++) { + fu->reg_baseaddr[i] = BASEADDRESS(i); + fu->reg_sourcebufferattributes[i] = SOURCEBUFFERATTRIBUTES(i); + fu->reg_sourcebufferdimension[i] = SOURCEBUFFERDIMENSION(i); + fu->reg_layeroffset[i] = LAYEROFFSET(i); + fu->reg_clipwindowoffset[i] = CLIPWINDOWOFFSET(i); + fu->reg_clipwindowdimensions[i] = CLIPWINDOWDIMENSIONS(i); + fu->reg_constantcolor[i] = CONSTANTCOLOR(i); + fu->reg_layerproperty[i] = LAYERPROPERTY(i); + } + snprintf(fu->name, sizeof(fu->name), "FetchWarp%d", id); + + dc_fw_set_ops(fu); + + dc_drm->fu_disp[fu->id] = fu; + + return 0; +} + +static const struct component_ops dc_fw_ops = { + .bind = dc_fw_bind, +}; + +static int dc_fw_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_fw_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_fw_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_fw_ops); +} + +static const struct of_device_id dc_fw_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-fetchwarp" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_fw_dt_ids); + +struct platform_driver dc_fw_driver = { + .probe = dc_fw_probe, + .remove = dc_fw_remove, + .driver = { + .name = "imx8-dc-fetchwarp", + .suppress_bind_attrs = true, + .of_match_table = dc_fw_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-lb.c b/drivers/gpu/drm/imx/dc/dc-lb.c new file mode 100644 index 000000000000..38f966625d38 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-lb.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dc-drv.h" +#include "dc-pe.h" + +#define PIXENGCFG_DYNAMIC 0x8 +#define PIXENGCFG_DYNAMIC_PRIM_SEL_MASK GENMASK(5, 0) +#define PIXENGCFG_DYNAMIC_PRIM_SEL(x) \ + FIELD_PREP(PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, (x)) +#define PIXENGCFG_DYNAMIC_SEC_SEL_MASK GENMASK(13, 8) +#define PIXENGCFG_DYNAMIC_SEC_SEL(x) \ + FIELD_PREP(PIXENGCFG_DYNAMIC_SEC_SEL_MASK, (x)) + +#define STATICCONTROL 0x8 +#define SHDTOKSEL_MASK GENMASK(4, 3) +#define SHDTOKSEL(x) FIELD_PREP(SHDTOKSEL_MASK, (x)) +#define SHDLDSEL_MASK GENMASK(2, 1) +#define SHDLDSEL(x) FIELD_PREP(SHDLDSEL_MASK, (x)) + +#define CONTROL 0xc +#define CTRL_MODE_MASK BIT(0) +#define CTRL_MODE(x) FIELD_PREP(CTRL_MODE_MASK, (x)) + +#define BLENDCONTROL 0x10 +#define ALPHA_MASK GENMASK(23, 16) +#define ALPHA(x) FIELD_PREP(ALPHA_MASK, (x)) +#define PRIM_C_BLD_FUNC_MASK GENMASK(2, 0) +#define PRIM_C_BLD_FUNC(x) \ + FIELD_PREP(PRIM_C_BLD_FUNC_MASK, (x)) +#define SEC_C_BLD_FUNC_MASK GENMASK(6, 4) +#define SEC_C_BLD_FUNC(x) \ + FIELD_PREP(SEC_C_BLD_FUNC_MASK, (x)) +#define PRIM_A_BLD_FUNC_MASK GENMASK(10, 8) +#define PRIM_A_BLD_FUNC(x) \ + FIELD_PREP(PRIM_A_BLD_FUNC_MASK, (x)) +#define SEC_A_BLD_FUNC_MASK GENMASK(14, 12) +#define SEC_A_BLD_FUNC(x) \ + FIELD_PREP(SEC_A_BLD_FUNC_MASK, (x)) + +#define POSITION 0x14 +#define XPOS_MASK GENMASK(15, 0) +#define XPOS(x) FIELD_PREP(XPOS_MASK, (x)) +#define YPOS_MASK GENMASK(31, 16) +#define YPOS(x) FIELD_PREP(YPOS_MASK, (x)) + +enum dc_lb_blend_func { + DC_LAYERBLEND_BLEND_ZERO, + DC_LAYERBLEND_BLEND_ONE, + DC_LAYERBLEND_BLEND_PRIM_ALPHA, + DC_LAYERBLEND_BLEND_ONE_MINUS_PRIM_ALPHA, + DC_LAYERBLEND_BLEND_SEC_ALPHA, + DC_LAYERBLEND_BLEND_ONE_MINUS_SEC_ALPHA, + DC_LAYERBLEND_BLEND_CONST_ALPHA, + DC_LAYERBLEND_BLEND_ONE_MINUS_CONST_ALPHA, +}; + +enum dc_lb_shadow_sel { + BOTH = 0x2, +}; + +static const struct dc_subdev_info dc_lb_info[] = { + { .reg_start = 0x56180ba0, .id = 0, }, + { .reg_start = 0x56180bc0, .id = 1, }, + { .reg_start = 0x56180be0, .id = 2, }, + { .reg_start = 0x56180c00, .id = 3, }, +}; + +static const struct regmap_range dc_lb_pec_regmap_access_ranges[] = { + regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), +}; + +static const struct regmap_access_table dc_lb_pec_regmap_access_table = { + .yes_ranges = dc_lb_pec_regmap_access_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_lb_pec_regmap_access_ranges), +}; + +static const struct regmap_config dc_lb_pec_regmap_config = { + .name = "pec", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_lb_pec_regmap_access_table, + .rd_table = &dc_lb_pec_regmap_access_table, + .max_register = PIXENGCFG_DYNAMIC, +}; + +static const struct regmap_range dc_lb_regmap_ranges[] = { + regmap_reg_range(STATICCONTROL, POSITION), +}; + +static const struct regmap_access_table dc_lb_regmap_access_table = { + .yes_ranges = dc_lb_regmap_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_lb_regmap_ranges), +}; + +static const struct regmap_config dc_lb_cfg_regmap_config = { + .name = "cfg", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_lb_regmap_access_table, + .rd_table = &dc_lb_regmap_access_table, + .max_register = POSITION, +}; + +static const enum dc_link_id prim_sels[] = { + /* common options */ + LINK_ID_NONE, + LINK_ID_CONSTFRAME0, + LINK_ID_CONSTFRAME1, + LINK_ID_CONSTFRAME4, + LINK_ID_CONSTFRAME5, + /* + * special options: + * layerblend(n) has n special options, + * from layerblend0 to layerblend(n - 1), e.g., + * layerblend3 has 3 special options - + * layerblend0/1/2. + */ + LINK_ID_LAYERBLEND0, + LINK_ID_LAYERBLEND1, + LINK_ID_LAYERBLEND2, + LINK_ID_LAYERBLEND3, +}; + +static const enum dc_link_id sec_sels[] = { + LINK_ID_NONE, + LINK_ID_FETCHWARP2, + LINK_ID_FETCHLAYER0, +}; + +enum dc_link_id dc_lb_get_link_id(struct dc_lb *lb) +{ + return lb->link; +} + +void dc_lb_pec_dynamic_prim_sel(struct dc_lb *lb, enum dc_link_id prim) +{ + int fixed_sels_num = ARRAY_SIZE(prim_sels) - 4; + int i; + + for (i = 0; i < fixed_sels_num + lb->id; i++) { + if (prim_sels[i] == prim) { + regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, + PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, + PIXENGCFG_DYNAMIC_PRIM_SEL(prim)); + return; + } + } + + dev_warn(lb->dev, "invalid primary input selection:%d\n", prim); +} + +void dc_lb_pec_dynamic_sec_sel(struct dc_lb *lb, enum dc_link_id sec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sec_sels); i++) { + if (sec_sels[i] == sec) { + regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, + PIXENGCFG_DYNAMIC_SEC_SEL_MASK, + PIXENGCFG_DYNAMIC_SEC_SEL(sec)); + return; + } + } + + dev_warn(lb->dev, "invalid secondary input selection:%d\n", sec); +} + +void dc_lb_pec_clken(struct dc_lb *lb, enum dc_pec_clken clken) +{ + regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, CLKEN_MASK, + CLKEN(clken)); +} + +static inline void dc_lb_enable_shden(struct dc_lb *lb) +{ + regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDEN, SHDEN); +} + +static inline void dc_lb_shdtoksel(struct dc_lb *lb, enum dc_lb_shadow_sel sel) +{ + regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDTOKSEL_MASK, + SHDTOKSEL(sel)); +} + +static inline void dc_lb_shdldsel(struct dc_lb *lb, enum dc_lb_shadow_sel sel) +{ + regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDLDSEL_MASK, + SHDLDSEL(sel)); +} + +void dc_lb_mode(struct dc_lb *lb, enum dc_lb_mode mode) +{ + regmap_write_bits(lb->reg_cfg, CONTROL, CTRL_MODE_MASK, mode); +} + +static inline void dc_lb_blendcontrol(struct dc_lb *lb) +{ + u32 val = PRIM_A_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) | + SEC_A_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) | + PRIM_C_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) | + SEC_C_BLD_FUNC(DC_LAYERBLEND_BLEND_CONST_ALPHA) | + ALPHA(DRM_BLEND_ALPHA_OPAQUE >> 8); + + regmap_write(lb->reg_cfg, BLENDCONTROL, val); +} + +void dc_lb_position(struct dc_lb *lb, int x, int y) +{ + regmap_write(lb->reg_cfg, POSITION, XPOS(x) | YPOS(y)); +} + +int dc_lb_get_id(struct dc_lb *lb) +{ + return lb->id; +} + +void dc_lb_init(struct dc_lb *lb) +{ + dc_lb_pec_dynamic_prim_sel(lb, LINK_ID_NONE); + dc_lb_pec_dynamic_sec_sel(lb, LINK_ID_NONE); + dc_lb_pec_clken(lb, CLKEN_DISABLE); + dc_lb_shdldsel(lb, BOTH); + dc_lb_shdtoksel(lb, BOTH); + dc_lb_blendcontrol(lb); + dc_lb_enable_shden(lb); +} + +static int dc_lb_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dc_drm_device *dc_drm = data; + struct resource *res_pec; + void __iomem *base_pec; + void __iomem *base_cfg; + struct dc_lb *lb; + + lb = devm_kzalloc(dev, sizeof(*lb), GFP_KERNEL); + if (!lb) + return -ENOMEM; + + base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec); + if (IS_ERR(base_pec)) + return PTR_ERR(base_pec); + + base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); + if (IS_ERR(base_cfg)) + return PTR_ERR(base_cfg); + + lb->reg_pec = devm_regmap_init_mmio(dev, base_pec, + &dc_lb_pec_regmap_config); + if (IS_ERR(lb->reg_pec)) + return PTR_ERR(lb->reg_pec); + + lb->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, + &dc_lb_cfg_regmap_config); + if (IS_ERR(lb->reg_cfg)) + return PTR_ERR(lb->reg_cfg); + + lb->id = dc_subdev_get_id(dc_lb_info, ARRAY_SIZE(dc_lb_info), res_pec); + if (lb->id < 0) { + dev_err(dev, "failed to get instance number: %d\n", lb->id); + return lb->id; + } + + lb->dev = dev; + lb->link = LINK_ID_LAYERBLEND0 + lb->id; + + dc_drm->lb[lb->id] = lb; + + return 0; +} + +static const struct component_ops dc_lb_ops = { + .bind = dc_lb_bind, +}; + +static int dc_lb_probe(struct platform_device *pdev) +{ + int ret; + + ret = component_add(&pdev->dev, &dc_lb_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_lb_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_lb_ops); +} + +static const struct of_device_id dc_lb_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-layerblend" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_lb_dt_ids); + +struct platform_driver dc_lb_driver = { + .probe = dc_lb_probe, + .remove = dc_lb_remove, + .driver = { + .name = "imx8-dc-layerblend", + .suppress_bind_attrs = true, + .of_match_table = dc_lb_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-pe.c b/drivers/gpu/drm/imx/dc/dc-pe.c new file mode 100644 index 000000000000..6676c22f3f45 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-pe.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dc-drv.h" +#include "dc-fu.h" +#include "dc-pe.h" + +static int dc_pe_bind(struct device *dev, struct device *master, void *data) +{ + struct dc_drm_device *dc_drm = data; + struct dc_pe *pe; + int ret; + + pe = devm_kzalloc(dev, sizeof(*pe), GFP_KERNEL); + if (!pe) + return -ENOMEM; + + pe->clk_axi = devm_clk_get(dev, NULL); + if (IS_ERR(pe->clk_axi)) + return dev_err_probe(dev, PTR_ERR(pe->clk_axi), + "failed to get AXI clock\n"); + + pe->dev = dev; + + dev_set_drvdata(dev, pe); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + dc_drm->pe = pe; + + return 0; +} + +/* + * It's possible to get the child device pointers from the child component + * bind callbacks, but it depends on the component helper behavior to bind + * the pixel engine component first. To avoid the dependency, post bind to + * get the pointers from dc_drm in a safe manner. + */ +void dc_pe_post_bind(struct dc_drm_device *dc_drm) +{ + struct dc_pe *pe = dc_drm->pe; + int i; + + for (i = 0; i < DC_DISPLAYS; i++) { + pe->cf_safe[i] = dc_drm->cf_safe[i]; + pe->cf_cont[i] = dc_drm->cf_cont[i]; + pe->ed_safe[i] = dc_drm->ed_safe[i]; + pe->ed_cont[i] = dc_drm->ed_cont[i]; + } + + for (i = 0; i < DC_DISP_FU_CNT; i++) + pe->fu_disp[i] = dc_drm->fu_disp[i]; + + for (i = 0; i < DC_LB_CNT; i++) + pe->lb[i] = dc_drm->lb[i]; +} + +static const struct component_ops dc_pe_ops = { + .bind = dc_pe_bind, +}; + +static int dc_pe_probe(struct platform_device *pdev) +{ + int ret; + + ret = devm_of_platform_populate(&pdev->dev); + if (ret < 0) + return ret; + + ret = component_add(&pdev->dev, &dc_pe_ops); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component\n"); + + return 0; +} + +static void dc_pe_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dc_pe_ops); +} + +static int dc_pe_runtime_suspend(struct device *dev) +{ + struct dc_pe *pe = dev_get_drvdata(dev); + + clk_disable_unprepare(pe->clk_axi); + + return 0; +} + +static int dc_pe_runtime_resume(struct device *dev) +{ + struct dc_pe *pe = dev_get_drvdata(dev); + int i, ret; + + ret = clk_prepare_enable(pe->clk_axi); + if (ret) { + dev_err(dev, "failed to enable AXI clock: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(pe->cf_safe); i++) + dc_cf_init(pe->cf_safe[i]); + + for (i = 0; i < ARRAY_SIZE(pe->cf_cont); i++) + dc_cf_init(pe->cf_cont[i]); + + for (i = 0; i < ARRAY_SIZE(pe->ed_safe); i++) + dc_ed_init(pe->ed_safe[i]); + + for (i = 0; i < ARRAY_SIZE(pe->ed_cont); i++) + dc_ed_init(pe->ed_cont[i]); + + for (i = 0; i < ARRAY_SIZE(pe->fu_disp); i++) + pe->fu_disp[i]->ops.init(pe->fu_disp[i]); + + for (i = 0; i < ARRAY_SIZE(pe->lb); i++) + dc_lb_init(pe->lb[i]); + + return 0; +} + +static const struct dev_pm_ops dc_pe_pm_ops = { + RUNTIME_PM_OPS(dc_pe_runtime_suspend, dc_pe_runtime_resume, NULL) +}; + +static const struct of_device_id dc_pe_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-pixel-engine", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_pe_dt_ids); + +struct platform_driver dc_pe_driver = { + .probe = dc_pe_probe, + .remove = dc_pe_remove, + .driver = { + .name = "imx8-dc-pixel-engine", + .suppress_bind_attrs = true, + .of_match_table = dc_pe_dt_ids, + .pm = pm_sleep_ptr(&dc_pe_pm_ops), + }, +}; diff --git a/drivers/gpu/drm/imx/dc/dc-pe.h b/drivers/gpu/drm/imx/dc/dc-pe.h new file mode 100644 index 000000000000..f5e01a6eb9e9 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-pe.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_PIXEL_ENGINE_H__ +#define __DC_PIXEL_ENGINE_H__ + +#include +#include +#include + +#include "dc-de.h" + +#define SHDEN BIT(0) + +#define CLKEN_MASK_SHIFT 24 +#define CLKEN_MASK (0x3 << CLKEN_MASK_SHIFT) +#define CLKEN(n) ((n) << CLKEN_MASK_SHIFT) + +#define DC_DISP_FU_CNT 2 +#define DC_LB_CNT 4 + +enum dc_link_id { + LINK_ID_NONE = 0x00, + LINK_ID_CONSTFRAME0 = 0x0c, + LINK_ID_CONSTFRAME4 = 0x0e, + LINK_ID_CONSTFRAME1 = 0x10, + LINK_ID_CONSTFRAME5 = 0x12, + LINK_ID_FETCHWARP2 = 0x14, + LINK_ID_FETCHLAYER0 = 0x1a, + LINK_ID_LAYERBLEND0 = 0x21, + LINK_ID_LAYERBLEND1 = 0x22, + LINK_ID_LAYERBLEND2 = 0x23, + LINK_ID_LAYERBLEND3 = 0x24, +}; + +enum dc_lb_mode { + LB_NEUTRAL, /* Output is same as primary input. */ + LB_BLEND, +}; + +enum dc_pec_clken { + CLKEN_DISABLE, + CLKEN_AUTOMATIC, +}; + +struct dc_cf { + struct regmap *reg_cfg; + enum dc_link_id link; +}; + +struct dc_ed { + struct device *dev; + struct regmap *reg_pec; + struct regmap *reg_cfg; + int irq_shdload; +}; + +struct dc_lb { + struct device *dev; + struct regmap *reg_pec; + struct regmap *reg_cfg; + int id; + enum dc_link_id link; +}; + +struct dc_pe { + struct device *dev; + struct clk *clk_axi; + struct dc_cf *cf_safe[DC_DISPLAYS]; + struct dc_cf *cf_cont[DC_DISPLAYS]; + struct dc_ed *ed_safe[DC_DISPLAYS]; + struct dc_ed *ed_cont[DC_DISPLAYS]; + struct dc_fu *fu_disp[DC_DISP_FU_CNT]; + struct dc_lb *lb[DC_LB_CNT]; +}; + +/* Constant Frame Unit */ +enum dc_link_id dc_cf_get_link_id(struct dc_cf *cf); +void dc_cf_framedimensions(struct dc_cf *cf, unsigned int w, unsigned int h); +void dc_cf_constantcolor_black(struct dc_cf *cf); +void dc_cf_constantcolor_blue(struct dc_cf *cf); +void dc_cf_init(struct dc_cf *cf); + +/* External Destination Unit */ +void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src); +void dc_ed_pec_sync_trigger(struct dc_ed *ed); +void dc_ed_init(struct dc_ed *ed); + +/* Layer Blend Unit */ +enum dc_link_id dc_lb_get_link_id(struct dc_lb *lb); +void dc_lb_pec_dynamic_prim_sel(struct dc_lb *lb, enum dc_link_id prim); +void dc_lb_pec_dynamic_sec_sel(struct dc_lb *lb, enum dc_link_id sec); +void dc_lb_pec_clken(struct dc_lb *lb, enum dc_pec_clken clken); +void dc_lb_mode(struct dc_lb *lb, enum dc_lb_mode mode); +void dc_lb_position(struct dc_lb *lb, int x, int y); +int dc_lb_get_id(struct dc_lb *lb); +void dc_lb_init(struct dc_lb *lb); + +#endif /* __DC_PIXEL_ENGINE_H__ */ From 37571feb6c08dab97f0a8a37e3c486aa8aead5f7 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:20 +0800 Subject: [PATCH 017/279] drm/imx: Add i.MX8qxp Display Controller interrupt controller i.MX8qxp Display Controller has a built-in interrupt controller to support Enable/Status/Preset/Clear interrupt bit. Add driver for it. Reviewed-by: Maxime Ripard Reviewed-by: Dmitry Baryshkov Signed-off-by: Liu Ying Link: https://lore.kernel.org/r/20250414035028.1561475-12-victor.liu@nxp.com --- drivers/gpu/drm/imx/dc/Kconfig | 1 + drivers/gpu/drm/imx/dc/Makefile | 2 +- drivers/gpu/drm/imx/dc/dc-drv.c | 1 + drivers/gpu/drm/imx/dc/dc-drv.h | 1 + drivers/gpu/drm/imx/dc/dc-ic.c | 282 ++++++++++++++++++++++++++++++++ 5 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/imx/dc/dc-ic.c diff --git a/drivers/gpu/drm/imx/dc/Kconfig b/drivers/gpu/drm/imx/dc/Kconfig index e1ef76d82830..1fc84c7475de 100644 --- a/drivers/gpu/drm/imx/dc/Kconfig +++ b/drivers/gpu/drm/imx/dc/Kconfig @@ -1,6 +1,7 @@ config DRM_IMX8_DC tristate "Freescale i.MX8 Display Controller Graphics" depends on DRM && COMMON_CLK && OF && (ARCH_MXC || COMPILE_TEST) + select GENERIC_IRQ_CHIP select REGMAP select REGMAP_MMIO help diff --git a/drivers/gpu/drm/imx/dc/Makefile b/drivers/gpu/drm/imx/dc/Makefile index 2942ae6fd5bd..1ce3e8a8db22 100644 --- a/drivers/gpu/drm/imx/dc/Makefile +++ b/drivers/gpu/drm/imx/dc/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 imx8-dc-drm-objs := dc-cf.o dc-de.o dc-drv.o dc-ed.o dc-fg.o dc-fl.o dc-fu.o \ - dc-fw.o dc-lb.o dc-pe.o dc-tc.o + dc-fw.o dc-ic.o dc-lb.o dc-pe.o dc-tc.o obj-$(CONFIG_DRM_IMX8_DC) += imx8-dc-drm.o diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c index 7c64acc863ad..fd68861f770a 100644 --- a/drivers/gpu/drm/imx/dc/dc-drv.c +++ b/drivers/gpu/drm/imx/dc/dc-drv.c @@ -15,6 +15,7 @@ static struct platform_driver * const dc_drivers[] = { &dc_fg_driver, &dc_fl_driver, &dc_fw_driver, + &dc_ic_driver, &dc_lb_driver, &dc_pe_driver, &dc_tc_driver, diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h index b9fe12577a19..e4c2d564ab5d 100644 --- a/drivers/gpu/drm/imx/dc/dc-drv.h +++ b/drivers/gpu/drm/imx/dc/dc-drv.h @@ -54,6 +54,7 @@ extern struct platform_driver dc_ed_driver; extern struct platform_driver dc_fg_driver; extern struct platform_driver dc_fl_driver; extern struct platform_driver dc_fw_driver; +extern struct platform_driver dc_ic_driver; extern struct platform_driver dc_lb_driver; extern struct platform_driver dc_pe_driver; extern struct platform_driver dc_tc_driver; diff --git a/drivers/gpu/drm/imx/dc/dc-ic.c b/drivers/gpu/drm/imx/dc/dc-ic.c new file mode 100644 index 000000000000..a270ae4030cd --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-ic.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USERINTERRUPTMASK(n) (0x8 + 4 * (n)) +#define INTERRUPTENABLE(n) (0x10 + 4 * (n)) +#define INTERRUPTPRESET(n) (0x18 + 4 * (n)) +#define INTERRUPTCLEAR(n) (0x20 + 4 * (n)) +#define INTERRUPTSTATUS(n) (0x28 + 4 * (n)) +#define USERINTERRUPTENABLE(n) (0x40 + 4 * (n)) +#define USERINTERRUPTPRESET(n) (0x48 + 4 * (n)) +#define USERINTERRUPTCLEAR(n) (0x50 + 4 * (n)) +#define USERINTERRUPTSTATUS(n) (0x58 + 4 * (n)) + +#define IRQ_COUNT 49 +#define IRQ_RESERVED 35 +#define REG_NUM 2 + +struct dc_ic_data { + struct regmap *regs; + struct clk *clk_axi; + int irq[IRQ_COUNT]; + struct irq_domain *domain; +}; + +struct dc_ic_entry { + struct dc_ic_data *data; + int irq; +}; + +static const struct regmap_range dc_ic_regmap_write_ranges[] = { + regmap_reg_range(USERINTERRUPTMASK(0), INTERRUPTCLEAR(1)), + regmap_reg_range(USERINTERRUPTENABLE(0), USERINTERRUPTCLEAR(1)), +}; + +static const struct regmap_access_table dc_ic_regmap_write_table = { + .yes_ranges = dc_ic_regmap_write_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ic_regmap_write_ranges), +}; + +static const struct regmap_range dc_ic_regmap_read_ranges[] = { + regmap_reg_range(USERINTERRUPTMASK(0), INTERRUPTENABLE(1)), + regmap_reg_range(INTERRUPTSTATUS(0), INTERRUPTSTATUS(1)), + regmap_reg_range(USERINTERRUPTENABLE(0), USERINTERRUPTENABLE(1)), + regmap_reg_range(USERINTERRUPTSTATUS(0), USERINTERRUPTSTATUS(1)), +}; + +static const struct regmap_access_table dc_ic_regmap_read_table = { + .yes_ranges = dc_ic_regmap_read_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ic_regmap_read_ranges), +}; + +static const struct regmap_range dc_ic_regmap_volatile_ranges[] = { + regmap_reg_range(INTERRUPTPRESET(0), INTERRUPTCLEAR(1)), + regmap_reg_range(USERINTERRUPTPRESET(0), USERINTERRUPTCLEAR(1)), +}; + +static const struct regmap_access_table dc_ic_regmap_volatile_table = { + .yes_ranges = dc_ic_regmap_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dc_ic_regmap_volatile_ranges), +}; + +static const struct regmap_config dc_ic_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .wr_table = &dc_ic_regmap_write_table, + .rd_table = &dc_ic_regmap_read_table, + .volatile_table = &dc_ic_regmap_volatile_table, + .max_register = USERINTERRUPTSTATUS(1), +}; + +static void dc_ic_irq_handler(struct irq_desc *desc) +{ + struct dc_ic_entry *entry = irq_desc_get_handler_data(desc); + struct dc_ic_data *data = entry->data; + unsigned int status, enable; + unsigned int virq; + + chained_irq_enter(irq_desc_get_chip(desc), desc); + + regmap_read(data->regs, USERINTERRUPTSTATUS(entry->irq / 32), &status); + regmap_read(data->regs, USERINTERRUPTENABLE(entry->irq / 32), &enable); + + status &= enable; + + if (status & BIT(entry->irq % 32)) { + virq = irq_find_mapping(data->domain, entry->irq); + if (virq) + generic_handle_irq(virq); + } + + chained_irq_exit(irq_desc_get_chip(desc), desc); +} + +static const unsigned long unused_irq[REG_NUM] = {0x00000000, 0xfffe0008}; + +static int dc_ic_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct irq_chip_generic *gc; + struct dc_ic_entry *entry; + struct irq_chip_type *ct; + struct dc_ic_data *data; + void __iomem *base; + int i, ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + entry = devm_kcalloc(dev, IRQ_COUNT, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + dev_err(dev, "failed to initialize reg\n"); + return PTR_ERR(base); + } + + data->regs = devm_regmap_init_mmio(dev, base, &dc_ic_regmap_config); + if (IS_ERR(data->regs)) + return PTR_ERR(data->regs); + + data->clk_axi = devm_clk_get(dev, NULL); + if (IS_ERR(data->clk_axi)) + return dev_err_probe(dev, PTR_ERR(data->clk_axi), + "failed to get AXI clock\n"); + + for (i = 0; i < IRQ_COUNT; i++) { + /* skip the reserved IRQ */ + if (i == IRQ_RESERVED) + continue; + + ret = platform_get_irq(pdev, i); + if (ret < 0) + return ret; + } + + dev_set_drvdata(dev, data); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "failed to get runtime PM sync: %d\n", ret); + return ret; + } + + for (i = 0; i < REG_NUM; i++) { + /* mask and clear all interrupts */ + regmap_write(data->regs, USERINTERRUPTENABLE(i), 0x0); + regmap_write(data->regs, INTERRUPTENABLE(i), 0x0); + regmap_write(data->regs, USERINTERRUPTCLEAR(i), 0xffffffff); + regmap_write(data->regs, INTERRUPTCLEAR(i), 0xffffffff); + + /* set all interrupts to user mode */ + regmap_write(data->regs, USERINTERRUPTMASK(i), 0xffffffff); + } + + data->domain = irq_domain_add_linear(dev->of_node, IRQ_COUNT, + &irq_generic_chip_ops, data); + if (!data->domain) { + dev_err(dev, "failed to create IRQ domain\n"); + pm_runtime_put(dev); + return -ENOMEM; + } + irq_domain_set_pm_device(data->domain, dev); + + ret = irq_alloc_domain_generic_chips(data->domain, 32, 1, "DC", + handle_level_irq, 0, 0, 0); + if (ret) { + dev_err(dev, "failed to alloc generic IRQ chips: %d\n", ret); + irq_domain_remove(data->domain); + pm_runtime_put(dev); + return ret; + } + + for (i = 0; i < IRQ_COUNT; i += 32) { + gc = irq_get_domain_generic_chip(data->domain, i); + gc->reg_base = base; + gc->unused = unused_irq[i / 32]; + ct = gc->chip_types; + ct->chip.irq_ack = irq_gc_ack_set_bit; + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->regs.ack = USERINTERRUPTCLEAR(i / 32); + ct->regs.mask = USERINTERRUPTENABLE(i / 32); + } + + for (i = 0; i < IRQ_COUNT; i++) { + /* skip the reserved IRQ */ + if (i == IRQ_RESERVED) + continue; + + data->irq[i] = irq_of_parse_and_map(dev->of_node, i); + + entry[i].data = data; + entry[i].irq = i; + + irq_set_chained_handler_and_data(data->irq[i], + dc_ic_irq_handler, &entry[i]); + } + + return 0; +} + +static void dc_ic_remove(struct platform_device *pdev) +{ + struct dc_ic_data *data = dev_get_drvdata(&pdev->dev); + int i; + + for (i = 0; i < IRQ_COUNT; i++) { + if (i == IRQ_RESERVED) + continue; + + irq_set_chained_handler_and_data(data->irq[i], NULL, NULL); + } + + irq_domain_remove(data->domain); + + pm_runtime_put_sync(&pdev->dev); +} + +static int dc_ic_runtime_suspend(struct device *dev) +{ + struct dc_ic_data *data = dev_get_drvdata(dev); + + clk_disable_unprepare(data->clk_axi); + + return 0; +} + +static int dc_ic_runtime_resume(struct device *dev) +{ + struct dc_ic_data *data = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(data->clk_axi); + if (ret) + dev_err(dev, "failed to enable AXI clock: %d\n", ret); + + return ret; +} + +static const struct dev_pm_ops dc_ic_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + RUNTIME_PM_OPS(dc_ic_runtime_suspend, dc_ic_runtime_resume, NULL) +}; + +static const struct of_device_id dc_ic_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc-intc", }, + { /* sentinel */ } +}; + +struct platform_driver dc_ic_driver = { + .probe = dc_ic_probe, + .remove = dc_ic_remove, + .driver = { + .name = "imx8-dc-intc", + .suppress_bind_attrs = true, + .of_match_table = dc_ic_dt_ids, + .pm = pm_sleep_ptr(&dc_ic_pm_ops), + }, +}; From 711a3b8783666ffd24ca99ae1c0fde76315b27a9 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:21 +0800 Subject: [PATCH 018/279] drm/imx: Add i.MX8qxp Display Controller KMS i.MX8qxp Display Controller(DC) is comprised of three main components that include a blit engine for 2D graphics accelerations, display controller for display output processing, as well as a command sequencer. Add kernel mode setting support for the display controller part with two CRTCs and two primary planes(backed by FetchLayer and FetchWarp respectively). The registers of the display controller are accessed without command sequencer involved, instead just by using CPU. The command sequencer is supposed to be used by the blit engine. Reviewed-by: Maxime Ripard Reviewed-by: Dmitry Baryshkov Signed-off-by: Liu Ying Link: https://lore.kernel.org/r/20250414035028.1561475-13-victor.liu@nxp.com --- drivers/gpu/drm/imx/dc/Kconfig | 5 + drivers/gpu/drm/imx/dc/Makefile | 5 +- drivers/gpu/drm/imx/dc/dc-crtc.c | 555 ++++++++++++++++++++++++++++++ drivers/gpu/drm/imx/dc/dc-de.h | 3 + drivers/gpu/drm/imx/dc/dc-drv.c | 254 ++++++++++++++ drivers/gpu/drm/imx/dc/dc-drv.h | 22 ++ drivers/gpu/drm/imx/dc/dc-kms.c | 143 ++++++++ drivers/gpu/drm/imx/dc/dc-kms.h | 131 +++++++ drivers/gpu/drm/imx/dc/dc-plane.c | 224 ++++++++++++ 9 files changed, 1340 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/imx/dc/dc-crtc.c create mode 100644 drivers/gpu/drm/imx/dc/dc-kms.c create mode 100644 drivers/gpu/drm/imx/dc/dc-kms.h create mode 100644 drivers/gpu/drm/imx/dc/dc-plane.c diff --git a/drivers/gpu/drm/imx/dc/Kconfig b/drivers/gpu/drm/imx/dc/Kconfig index 1fc84c7475de..415993207f2e 100644 --- a/drivers/gpu/drm/imx/dc/Kconfig +++ b/drivers/gpu/drm/imx/dc/Kconfig @@ -1,6 +1,11 @@ config DRM_IMX8_DC tristate "Freescale i.MX8 Display Controller Graphics" depends on DRM && COMMON_CLK && OF && (ARCH_MXC || COMPILE_TEST) + select DRM_CLIENT_SELECTION + select DRM_GEM_DMA_HELPER + select DRM_KMS_HELPER + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR select GENERIC_IRQ_CHIP select REGMAP select REGMAP_MMIO diff --git a/drivers/gpu/drm/imx/dc/Makefile b/drivers/gpu/drm/imx/dc/Makefile index 1ce3e8a8db22..b9d33c074984 100644 --- a/drivers/gpu/drm/imx/dc/Makefile +++ b/drivers/gpu/drm/imx/dc/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 -imx8-dc-drm-objs := dc-cf.o dc-de.o dc-drv.o dc-ed.o dc-fg.o dc-fl.o dc-fu.o \ - dc-fw.o dc-ic.o dc-lb.o dc-pe.o dc-tc.o +imx8-dc-drm-objs := dc-cf.o dc-crtc.o dc-de.o dc-drv.o dc-ed.o dc-fg.o dc-fl.o \ + dc-fu.o dc-fw.o dc-ic.o dc-kms.o dc-lb.o dc-pe.o \ + dc-plane.o dc-tc.o obj-$(CONFIG_DRM_IMX8_DC) += imx8-dc-drm.o diff --git a/drivers/gpu/drm/imx/dc/dc-crtc.c b/drivers/gpu/drm/imx/dc/dc-crtc.c new file mode 100644 index 000000000000..31d3a982deaf --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-crtc.c @@ -0,0 +1,555 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dc-de.h" +#include "dc-drv.h" +#include "dc-kms.h" +#include "dc-pe.h" + +#define dc_crtc_dbg(crtc, fmt, ...) \ +do { \ + struct drm_crtc *_crtc = (crtc); \ + drm_dbg_kms(_crtc->dev, "[CRTC:%d:%s] " fmt, \ + _crtc->base.id, _crtc->name, ##__VA_ARGS__); \ +} while (0) + +#define dc_crtc_err(crtc, fmt, ...) \ +do { \ + struct drm_crtc *_crtc = (crtc); \ + drm_err(_crtc->dev, "[CRTC:%d:%s] " fmt, \ + _crtc->base.id, _crtc->name, ##__VA_ARGS__); \ +} while (0) + +#define DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(c) \ +do { \ + unsigned long ret; \ + ret = wait_for_completion_timeout(&dc_crtc->c, HZ); \ + if (ret == 0) \ + dc_crtc_err(crtc, "%s: wait for " #c " timeout\n", \ + __func__); \ +} while (0) + +#define DC_CRTC_CHECK_FRAMEGEN_FIFO(fg) \ +do { \ + struct dc_fg *_fg = (fg); \ + if (dc_fg_secondary_requests_to_read_empty_fifo(_fg)) { \ + dc_fg_secondary_clear_channel_status(_fg); \ + dc_crtc_err(crtc, "%s: FrameGen FIFO empty\n", \ + __func__); \ + } \ +} while (0) + +#define DC_CRTC_WAIT_FOR_FRAMEGEN_SECONDARY_SYNCUP(fg) \ +do { \ + if (dc_fg_wait_for_secondary_syncup(fg)) \ + dc_crtc_err(crtc, \ + "%s: FrameGen secondary channel isn't syncup\n",\ + __func__); \ +} while (0) + +static inline struct dc_crtc *to_dc_crtc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct dc_crtc, base); +} + +static u32 dc_crtc_get_vblank_counter(struct drm_crtc *crtc) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + + return dc_fg_get_frame_index(dc_crtc->fg); +} + +static int dc_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + + enable_irq(dc_crtc->irq_dec_framecomplete); + + return 0; +} + +static void dc_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + + /* nosync due to atomic context */ + disable_irq_nosync(dc_crtc->irq_dec_framecomplete); +} + +static const struct drm_crtc_funcs dc_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .get_vblank_counter = dc_crtc_get_vblank_counter, + .enable_vblank = dc_crtc_enable_vblank, + .disable_vblank = dc_crtc_disable_vblank, + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, +}; + +static void dc_crtc_queue_state_event(struct drm_crtc_state *crtc_state) +{ + struct drm_crtc *crtc = crtc_state->crtc; + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc_state->event) { + WARN_ON(drm_crtc_vblank_get(crtc)); + WARN_ON(dc_crtc->event); + dc_crtc->event = crtc_state->event; + crtc_state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static inline enum drm_mode_status +dc_crtc_check_clock(struct dc_crtc *dc_crtc, int clk_khz) +{ + return dc_fg_check_clock(dc_crtc->fg, clk_khz); +} + +static enum drm_mode_status +dc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + enum drm_mode_status status; + + status = dc_crtc_check_clock(dc_crtc, mode->clock); + if (status != MODE_OK) + return status; + + if (mode->crtc_clock > DC_FRAMEGEN_MAX_CLOCK_KHZ) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +static int +dc_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct drm_display_mode *adj = &new_crtc_state->adjusted_mode; + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + enum drm_mode_status status; + + status = dc_crtc_check_clock(dc_crtc, adj->clock); + if (status != MODE_OK) + return -EINVAL; + + return 0; +} + +static void +dc_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev); + int idx, ret; + + if (!drm_atomic_crtc_needs_modeset(new_crtc_state) || + !new_crtc_state->active) + return; + + if (!drm_dev_enter(crtc->dev, &idx)) + return; + + /* request pixel engine power-on when CRTC starts to be active */ + ret = pm_runtime_resume_and_get(dc_drm->pe->dev); + if (ret) + dc_crtc_err(crtc, "failed to get DC pixel engine RPM: %d\n", + ret); + + drm_dev_exit(idx); +} + +static void +dc_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *old_crtc_state = + drm_atomic_get_old_crtc_state(state, crtc); + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + int idx; + + if (drm_atomic_crtc_needs_modeset(new_crtc_state) || + (!old_crtc_state->active && !new_crtc_state->active)) + return; + + if (!drm_dev_enter(crtc->dev, &idx)) + goto out; + + enable_irq(dc_crtc->irq_ed_cont_shdload); + + /* flush plane update out to display */ + dc_ed_pec_sync_trigger(dc_crtc->ed_cont); + + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_cont_shdload_done); + + disable_irq(dc_crtc->irq_ed_cont_shdload); + + DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg); + + drm_dev_exit(idx); + +out: + dc_crtc_queue_state_event(new_crtc_state); +} + +static void +dc_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct drm_display_mode *adj = &new_crtc_state->adjusted_mode; + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + enum dc_link_id cf_link; + int idx, ret; + + dc_crtc_dbg(crtc, "mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(adj)); + + drm_crtc_vblank_on(crtc); + + if (!drm_dev_enter(crtc->dev, &idx)) + goto out; + + /* request display engine power-on when CRTC is enabled */ + ret = pm_runtime_resume_and_get(dc_crtc->de->dev); + if (ret < 0) + dc_crtc_err(crtc, "failed to get DC display engine RPM: %d\n", + ret); + + enable_irq(dc_crtc->irq_dec_shdload); + enable_irq(dc_crtc->irq_ed_cont_shdload); + enable_irq(dc_crtc->irq_ed_safe_shdload); + + dc_fg_cfg_videomode(dc_crtc->fg, adj); + + dc_cf_framedimensions(dc_crtc->cf_cont, + adj->crtc_hdisplay, adj->crtc_vdisplay); + dc_cf_framedimensions(dc_crtc->cf_safe, + adj->crtc_hdisplay, adj->crtc_vdisplay); + + /* constframe in safety stream shows blue frame */ + dc_cf_constantcolor_blue(dc_crtc->cf_safe); + cf_link = dc_cf_get_link_id(dc_crtc->cf_safe); + dc_ed_pec_src_sel(dc_crtc->ed_safe, cf_link); + + /* show CRTC background if no plane is enabled */ + if (new_crtc_state->plane_mask == 0) { + /* constframe in content stream shows black frame */ + dc_cf_constantcolor_black(dc_crtc->cf_cont); + + cf_link = dc_cf_get_link_id(dc_crtc->cf_cont); + dc_ed_pec_src_sel(dc_crtc->ed_cont, cf_link); + } + + dc_fg_enable_clock(dc_crtc->fg); + dc_ed_pec_sync_trigger(dc_crtc->ed_cont); + dc_ed_pec_sync_trigger(dc_crtc->ed_safe); + dc_fg_shdtokgen(dc_crtc->fg); + dc_fg_enable(dc_crtc->fg); + + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_safe_shdload_done); + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(ed_cont_shdload_done); + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_shdload_done); + + disable_irq(dc_crtc->irq_ed_safe_shdload); + disable_irq(dc_crtc->irq_ed_cont_shdload); + disable_irq(dc_crtc->irq_dec_shdload); + + DC_CRTC_WAIT_FOR_FRAMEGEN_SECONDARY_SYNCUP(dc_crtc->fg); + + DC_CRTC_CHECK_FRAMEGEN_FIFO(dc_crtc->fg); + + drm_dev_exit(idx); + +out: + dc_crtc_queue_state_event(new_crtc_state); +} + +static void +dc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state = + drm_atomic_get_new_crtc_state(state, crtc); + struct dc_drm_device *dc_drm = to_dc_drm_device(crtc->dev); + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + int idx, ret; + + if (!drm_dev_enter(crtc->dev, &idx)) + goto out; + + enable_irq(dc_crtc->irq_dec_seqcomplete); + dc_fg_disable(dc_crtc->fg); + DC_CRTC_WAIT_FOR_COMPLETION_TIMEOUT(dec_seqcomplete_done); + disable_irq(dc_crtc->irq_dec_seqcomplete); + + dc_fg_disable_clock(dc_crtc->fg); + + /* request pixel engine power-off as plane is off too */ + ret = pm_runtime_put(dc_drm->pe->dev); + if (ret) + dc_crtc_err(crtc, "failed to put DC pixel engine RPM: %d\n", + ret); + + /* request display engine power-off when CRTC is disabled */ + ret = pm_runtime_put(dc_crtc->de->dev); + if (ret < 0) + dc_crtc_err(crtc, "failed to put DC display engine RPM: %d\n", + ret); + + drm_dev_exit(idx); + +out: + drm_crtc_vblank_off(crtc); + + spin_lock_irq(&crtc->dev->event_lock); + if (new_crtc_state->event && !new_crtc_state->active) { + drm_crtc_send_vblank_event(crtc, new_crtc_state->event); + new_crtc_state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static bool dc_crtc_get_scanout_position(struct drm_crtc *crtc, + bool in_vblank_irq, + int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) +{ + struct dc_crtc *dc_crtc = to_dc_crtc(crtc); + int vdisplay = mode->crtc_vdisplay; + int vtotal = mode->crtc_vtotal; + bool reliable; + int line; + int idx; + + if (stime) + *stime = ktime_get(); + + if (!drm_dev_enter(crtc->dev, &idx)) { + reliable = false; + *vpos = 0; + *hpos = 0; + goto out; + } + + /* line index starts with 0 for the first active output line */ + line = dc_fg_get_line_index(dc_crtc->fg); + + if (line < vdisplay) + /* active scanout area - positive */ + *vpos = line + 1; + else + /* inside vblank - negative */ + *vpos = line - (vtotal - 1); + + *hpos = 0; + + reliable = true; + + drm_dev_exit(idx); +out: + if (etime) + *etime = ktime_get(); + + return reliable; +} + +static const struct drm_crtc_helper_funcs dc_helper_funcs = { + .mode_valid = dc_crtc_mode_valid, + .atomic_check = dc_crtc_atomic_check, + .atomic_begin = dc_crtc_atomic_begin, + .atomic_flush = dc_crtc_atomic_flush, + .atomic_enable = dc_crtc_atomic_enable, + .atomic_disable = dc_crtc_atomic_disable, + .get_scanout_position = dc_crtc_get_scanout_position, +}; + +static irqreturn_t dc_crtc_irq_handler_dec_framecomplete(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + struct drm_crtc *crtc = &dc_crtc->base; + unsigned long flags; + + drm_crtc_handle_vblank(crtc); + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + if (dc_crtc->event) { + drm_crtc_send_vblank_event(crtc, dc_crtc->event); + dc_crtc->event = NULL; + drm_crtc_vblank_put(crtc); + } + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + return IRQ_HANDLED; +} + +static irqreturn_t +dc_crtc_irq_handler_dec_seqcomplete_done(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + + complete(&dc_crtc->dec_seqcomplete_done); + + return IRQ_HANDLED; +} + +static irqreturn_t dc_crtc_irq_handler_dec_shdload_done(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + + complete(&dc_crtc->dec_shdload_done); + + return IRQ_HANDLED; +} + +static irqreturn_t +dc_crtc_irq_handler_ed_cont_shdload_done(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + + complete(&dc_crtc->ed_cont_shdload_done); + + return IRQ_HANDLED; +} + +static irqreturn_t +dc_crtc_irq_handler_ed_safe_shdload_done(int irq, void *dev_id) +{ + struct dc_crtc *dc_crtc = dev_id; + + complete(&dc_crtc->ed_safe_shdload_done); + + return IRQ_HANDLED; +} + +static int dc_crtc_request_irqs(struct drm_device *drm, struct dc_crtc *dc_crtc) +{ + struct { + struct device *dev; + unsigned int irq; + irqreturn_t (*irq_handler)(int irq, void *dev_id); + } irqs[DC_CRTC_IRQS] = { + { + dc_crtc->de->dev, + dc_crtc->irq_dec_framecomplete, + dc_crtc_irq_handler_dec_framecomplete, + }, { + dc_crtc->de->dev, + dc_crtc->irq_dec_seqcomplete, + dc_crtc_irq_handler_dec_seqcomplete_done, + }, { + dc_crtc->de->dev, + dc_crtc->irq_dec_shdload, + dc_crtc_irq_handler_dec_shdload_done, + }, { + dc_crtc->ed_cont->dev, + dc_crtc->irq_ed_cont_shdload, + dc_crtc_irq_handler_ed_cont_shdload_done, + }, { + dc_crtc->ed_safe->dev, + dc_crtc->irq_ed_safe_shdload, + dc_crtc_irq_handler_ed_safe_shdload_done, + }, + }; + int i, ret; + + for (i = 0; i < DC_CRTC_IRQS; i++) { + struct dc_crtc_irq *irq = &dc_crtc->irqs[i]; + + ret = devm_request_irq(irqs[i].dev, irqs[i].irq, + irqs[i].irq_handler, IRQF_NO_AUTOEN, + dev_name(irqs[i].dev), dc_crtc); + if (ret) { + dev_err(irqs[i].dev, "failed to request irq(%u): %d\n", + irqs[i].irq, ret); + return ret; + } + + irq->dc_crtc = dc_crtc; + irq->irq = irqs[i].irq; + } + + return 0; +} + +int dc_crtc_init(struct dc_drm_device *dc_drm, int crtc_index) +{ + struct dc_crtc *dc_crtc = &dc_drm->dc_crtc[crtc_index]; + struct drm_device *drm = &dc_drm->base; + struct dc_de *de = dc_drm->de[crtc_index]; + struct dc_pe *pe = dc_drm->pe; + struct dc_plane *dc_primary; + int ret; + + dc_crtc->de = de; + + init_completion(&dc_crtc->dec_seqcomplete_done); + init_completion(&dc_crtc->dec_shdload_done); + init_completion(&dc_crtc->ed_cont_shdload_done); + init_completion(&dc_crtc->ed_safe_shdload_done); + + dc_crtc->cf_cont = pe->cf_cont[crtc_index]; + dc_crtc->cf_safe = pe->cf_safe[crtc_index]; + dc_crtc->ed_cont = pe->ed_cont[crtc_index]; + dc_crtc->ed_safe = pe->ed_safe[crtc_index]; + dc_crtc->fg = de->fg; + + dc_crtc->irq_dec_framecomplete = de->irq_framecomplete; + dc_crtc->irq_dec_seqcomplete = de->irq_seqcomplete; + dc_crtc->irq_dec_shdload = de->irq_shdload; + dc_crtc->irq_ed_safe_shdload = dc_crtc->ed_safe->irq_shdload; + dc_crtc->irq_ed_cont_shdload = dc_crtc->ed_cont->irq_shdload; + + dc_primary = &dc_drm->dc_primary[crtc_index]; + ret = dc_plane_init(dc_drm, dc_primary); + if (ret) { + dev_err(de->dev, "failed to initialize primary plane: %d\n", + ret); + return ret; + } + + drm_crtc_helper_add(&dc_crtc->base, &dc_helper_funcs); + + ret = drm_crtc_init_with_planes(drm, &dc_crtc->base, &dc_primary->base, + NULL, &dc_crtc_funcs, NULL); + if (ret) + dev_err(de->dev, "failed to add CRTC: %d\n", ret); + + return ret; +} + +int dc_crtc_post_init(struct dc_drm_device *dc_drm, int crtc_index) +{ + struct dc_crtc *dc_crtc = &dc_drm->dc_crtc[crtc_index]; + struct drm_device *drm = &dc_drm->base; + + return dc_crtc_request_irqs(drm, dc_crtc); +} diff --git a/drivers/gpu/drm/imx/dc/dc-de.h b/drivers/gpu/drm/imx/dc/dc-de.h index 5dd311cde076..211f3fcc1a9a 100644 --- a/drivers/gpu/drm/imx/dc/dc-de.h +++ b/drivers/gpu/drm/imx/dc/dc-de.h @@ -13,6 +13,9 @@ #define DC_DISPLAYS 2 +#define DC_FRAMEGEN_MAX_FRAME_INDEX 0x3ffff +#define DC_FRAMEGEN_MAX_CLOCK_KHZ 300000 + struct dc_fg { struct device *dev; struct regmap *reg; diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c index fd68861f770a..04f021d2d6cf 100644 --- a/drivers/gpu/drm/imx/dc/dc-drv.c +++ b/drivers/gpu/drm/imx/dc/dc-drv.c @@ -3,10 +3,263 @@ * Copyright 2024 NXP */ +#include +#include +#include +#include +#include #include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dc-de.h" #include "dc-drv.h" +#include "dc-pe.h" + +struct dc_priv { + struct drm_device *drm; + struct clk *clk_cfg; +}; + +DEFINE_DRM_GEM_DMA_FOPS(dc_drm_driver_fops); + +static struct drm_driver dc_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, + DRM_GEM_DMA_DRIVER_OPS, + DRM_FBDEV_DMA_DRIVER_OPS, + .fops = &dc_drm_driver_fops, + .name = "imx8-dc", + .desc = "i.MX8 DC DRM graphics", + .major = 1, + .minor = 0, + .patchlevel = 0, +}; + +static void +dc_add_components(struct device *dev, struct component_match **matchptr) +{ + struct device_node *child, *grandchild; + + for_each_available_child_of_node(dev->of_node, child) { + /* The interrupt controller is not a component. */ + if (of_device_is_compatible(child, "fsl,imx8qxp-dc-intc")) + continue; + + drm_of_component_match_add(dev, matchptr, component_compare_of, + child); + + for_each_available_child_of_node(child, grandchild) + drm_of_component_match_add(dev, matchptr, + component_compare_of, + grandchild); + } +} + +static int dc_drm_component_bind_all(struct dc_drm_device *dc_drm) +{ + struct drm_device *drm = &dc_drm->base; + int ret; + + ret = component_bind_all(drm->dev, dc_drm); + if (ret) + return ret; + + dc_de_post_bind(dc_drm); + dc_pe_post_bind(dc_drm); + + return 0; +} + +static void dc_drm_component_unbind_all(void *ptr) +{ + struct dc_drm_device *dc_drm = ptr; + struct drm_device *drm = &dc_drm->base; + + component_unbind_all(drm->dev, dc_drm); +} + +static int dc_drm_bind(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + struct dc_drm_device *dc_drm; + struct drm_device *drm; + int ret; + + dc_drm = devm_drm_dev_alloc(dev, &dc_drm_driver, struct dc_drm_device, + base); + if (IS_ERR(dc_drm)) + return PTR_ERR(dc_drm); + + drm = &dc_drm->base; + + ret = dc_drm_component_bind_all(dc_drm); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, dc_drm_component_unbind_all, + dc_drm); + if (ret) + return ret; + + ret = dc_kms_init(dc_drm); + if (ret) + return ret; + + ret = drm_dev_register(drm, 0); + if (ret) { + dev_err(dev, "failed to register drm device: %d\n", ret); + goto err; + } + + drm_client_setup_with_fourcc(drm, DRM_FORMAT_XRGB8888); + + priv->drm = drm; + + return 0; + +err: + dc_kms_uninit(dc_drm); + + return ret; +} + +static void dc_drm_unbind(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + struct dc_drm_device *dc_drm = to_dc_drm_device(priv->drm); + struct drm_device *drm = &dc_drm->base; + + priv->drm = NULL; + drm_dev_unplug(drm); + dc_kms_uninit(dc_drm); + drm_atomic_helper_shutdown(drm); +} + +static const struct component_master_ops dc_drm_ops = { + .bind = dc_drm_bind, + .unbind = dc_drm_unbind, +}; + +static int dc_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct dc_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->clk_cfg = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk_cfg)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cfg), + "failed to get cfg clock\n"); + + dev_set_drvdata(&pdev->dev, priv); + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; + + ret = devm_of_platform_populate(&pdev->dev); + if (ret) + return ret; + + dc_add_components(&pdev->dev, &match); + + ret = component_master_add_with_match(&pdev->dev, &dc_drm_ops, match); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to add component master\n"); + + return 0; +} + +static void dc_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &dc_drm_ops); +} + +static int dc_runtime_suspend(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk_cfg); + + return 0; +} + +static int dc_runtime_resume(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(priv->clk_cfg); + if (ret) + dev_err(dev, "failed to enable cfg clock: %d\n", ret); + + return ret; +} + +static int dc_suspend(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + + return drm_mode_config_helper_suspend(priv->drm); +} + +static int dc_resume(struct device *dev) +{ + struct dc_priv *priv = dev_get_drvdata(dev); + + return drm_mode_config_helper_resume(priv->drm); +} + +static void dc_shutdown(struct platform_device *pdev) +{ + struct dc_priv *priv = dev_get_drvdata(&pdev->dev); + + drm_atomic_helper_shutdown(priv->drm); +} + +static const struct dev_pm_ops dc_pm_ops = { + RUNTIME_PM_OPS(dc_runtime_suspend, dc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(dc_suspend, dc_resume) +}; + +static const struct of_device_id dc_dt_ids[] = { + { .compatible = "fsl,imx8qxp-dc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dc_dt_ids); + +static struct platform_driver dc_driver = { + .probe = dc_probe, + .remove = dc_remove, + .shutdown = dc_shutdown, + .driver = { + .name = "imx8-dc", + .of_match_table = dc_dt_ids, + .pm = pm_sleep_ptr(&dc_pm_ops), + }, +}; static struct platform_driver * const dc_drivers[] = { &dc_cf_driver, @@ -19,6 +272,7 @@ static struct platform_driver * const dc_drivers[] = { &dc_lb_driver, &dc_pe_driver, &dc_tc_driver, + &dc_driver, }; static int __init dc_drm_init(void) diff --git a/drivers/gpu/drm/imx/dc/dc-drv.h b/drivers/gpu/drm/imx/dc/dc-drv.h index e4c2d564ab5d..eb61b8c76269 100644 --- a/drivers/gpu/drm/imx/dc/dc-drv.h +++ b/drivers/gpu/drm/imx/dc/dc-drv.h @@ -6,13 +6,16 @@ #ifndef __DC_DRV_H__ #define __DC_DRV_H__ +#include #include #include #include #include +#include #include "dc-de.h" +#include "dc-kms.h" #include "dc-pe.h" /** @@ -21,6 +24,12 @@ struct dc_drm_device { /** @base: base drm_device structure */ struct drm_device base; + /** @dc_crtc: DC specific CRTC list */ + struct dc_crtc dc_crtc[DC_DISPLAYS]; + /** @dc_primary: DC specific primary plane list */ + struct dc_plane dc_primary[DC_DISPLAYS]; + /** @encoder: encoder list */ + struct drm_encoder encoder[DC_DISPLAYS]; /** @cf_safe: constframe list(safety stream) */ struct dc_cf *cf_safe[DC_DISPLAYS]; /** @cf_cont: constframe list(content stream) */ @@ -48,6 +57,19 @@ struct dc_subdev_info { int id; }; +static inline struct dc_drm_device *to_dc_drm_device(struct drm_device *drm) +{ + return container_of(drm, struct dc_drm_device, base); +} + +int dc_crtc_init(struct dc_drm_device *dc_drm, int crtc_index); +int dc_crtc_post_init(struct dc_drm_device *dc_drm, int crtc_index); + +int dc_kms_init(struct dc_drm_device *dc_drm); +void dc_kms_uninit(struct dc_drm_device *dc_drm); + +int dc_plane_init(struct dc_drm_device *dc_drm, struct dc_plane *dc_plane); + extern struct platform_driver dc_cf_driver; extern struct platform_driver dc_de_driver; extern struct platform_driver dc_ed_driver; diff --git a/drivers/gpu/drm/imx/dc/dc-kms.c b/drivers/gpu/drm/imx/dc/dc-kms.c new file mode 100644 index 000000000000..2b18aa37a4a8 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-kms.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dc-de.h" +#include "dc-drv.h" +#include "dc-kms.h" + +static const struct drm_mode_config_funcs dc_drm_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static int dc_kms_init_encoder_per_crtc(struct dc_drm_device *dc_drm, + int crtc_index) +{ + struct dc_crtc *dc_crtc = &dc_drm->dc_crtc[crtc_index]; + struct drm_device *drm = &dc_drm->base; + struct drm_crtc *crtc = &dc_crtc->base; + struct drm_connector *connector; + struct device *dev = drm->dev; + struct drm_encoder *encoder; + struct drm_bridge *bridge; + int ret; + + bridge = devm_drm_of_get_bridge(dev, dc_crtc->de->tc->dev->of_node, + 0, 0); + if (IS_ERR(bridge)) { + ret = PTR_ERR(bridge); + if (ret == -ENODEV) + return 0; + + return dev_err_probe(dev, ret, + "failed to find bridge for CRTC%u\n", + crtc->index); + } + + encoder = &dc_drm->encoder[crtc_index]; + ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE); + if (ret) { + dev_err(dev, "failed to initialize encoder for CRTC%u: %d\n", + crtc->index, ret); + return ret; + } + + encoder->possible_crtcs = drm_crtc_mask(crtc); + + ret = drm_bridge_attach(encoder, bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + dev_err(dev, + "failed to attach bridge to encoder for CRTC%u: %d\n", + crtc->index, ret); + return ret; + } + + connector = drm_bridge_connector_init(drm, encoder); + if (IS_ERR(connector)) { + ret = PTR_ERR(connector); + dev_err(dev, "failed to init bridge connector for CRTC%u: %d\n", + crtc->index, ret); + return ret; + } + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + dev_err(dev, + "failed to attach encoder to connector for CRTC%u: %d\n", + crtc->index, ret); + + return ret; +} + +int dc_kms_init(struct dc_drm_device *dc_drm) +{ + struct drm_device *drm = &dc_drm->base; + int ret, i; + + ret = drmm_mode_config_init(drm); + if (ret) + return ret; + + drm->mode_config.min_width = 60; + drm->mode_config.min_height = 60; + drm->mode_config.max_width = 8192; + drm->mode_config.max_height = 8192; + drm->mode_config.funcs = &dc_drm_mode_config_funcs; + + drm->vblank_disable_immediate = true; + drm->max_vblank_count = DC_FRAMEGEN_MAX_FRAME_INDEX; + + for (i = 0; i < DC_DISPLAYS; i++) { + ret = dc_crtc_init(dc_drm, i); + if (ret) + return ret; + + ret = dc_kms_init_encoder_per_crtc(dc_drm, i); + if (ret) + return ret; + } + + for (i = 0; i < DC_DISPLAYS; i++) { + ret = dc_crtc_post_init(dc_drm, i); + if (ret) + return ret; + } + + ret = drm_vblank_init(drm, DC_DISPLAYS); + if (ret) { + dev_err(drm->dev, "failed to init vblank support: %d\n", ret); + return ret; + } + + drm_mode_config_reset(drm); + + drm_kms_helper_poll_init(drm); + + return 0; +} + +void dc_kms_uninit(struct dc_drm_device *dc_drm) +{ + drm_kms_helper_poll_fini(&dc_drm->base); +} diff --git a/drivers/gpu/drm/imx/dc/dc-kms.h b/drivers/gpu/drm/imx/dc/dc-kms.h new file mode 100644 index 000000000000..cd7860eff986 --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-kms.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2024 NXP + */ + +#ifndef __DC_KMS_H__ +#define __DC_KMS_H__ + +#include + +#include +#include +#include + +#include "dc-de.h" +#include "dc-fu.h" +#include "dc-pe.h" + +#define DC_CRTC_IRQS 5 + +struct dc_crtc_irq { + struct dc_crtc *dc_crtc; + unsigned int irq; +}; + +/** + * struct dc_crtc - DC specific drm_crtc + * + * Each display controller contains one content stream and one safety stream. + * In general, the two streams have the same functionality. One stream is + * overlaid on the other by @fg. This driver chooses to generate black constant + * color from the content stream as background color, build plane(s) on the + * content stream by using layerblend(s) and always generate a constant color + * from the safety stream. Note that due to the decoupled timing, the safety + * stream still works to show the constant color properly even when the content + * stream has completely hung up due to mal-function of this driver. + */ +struct dc_crtc { + /** @base: base drm_crtc structure */ + struct drm_crtc base; + /** @de: display engine */ + struct dc_de *de; + /** @cf_cont: content stream constframe */ + struct dc_cf *cf_cont; + /** @cf_safe: safety stream constframe */ + struct dc_cf *cf_safe; + /** @ed_cont: content stream extdst */ + struct dc_ed *ed_cont; + /** @ed_safe: safety stream extdst */ + struct dc_ed *ed_safe; + /** @fg: framegen */ + struct dc_fg *fg; + /** + * @irq_dec_framecomplete: + * + * display engine configuration frame complete interrupt + */ + unsigned int irq_dec_framecomplete; + /** + * @irq_dec_seqcomplete: + * + * display engine configuration sequence complete interrupt + */ + unsigned int irq_dec_seqcomplete; + /** + * @irq_dec_shdload: + * + * display engine configuration shadow load interrupt + */ + unsigned int irq_dec_shdload; + /** + * @irq_ed_cont_shdload: + * + * content stream extdst shadow load interrupt + */ + unsigned int irq_ed_cont_shdload; + /** + * @irq_ed_safe_shdload: + * + * safety stream extdst shadow load interrupt + */ + unsigned int irq_ed_safe_shdload; + /** + * @dec_seqcomplete_done: + * + * display engine configuration sequence completion + */ + struct completion dec_seqcomplete_done; + /** + * @dec_shdload_done: + * + * display engine configuration shadow load completion + */ + struct completion dec_shdload_done; + /** + * @ed_cont_shdload_done: + * + * content stream extdst shadow load completion + */ + struct completion ed_cont_shdload_done; + /** + * @ed_safe_shdload_done: + * + * safety stream extdst shadow load completion + */ + struct completion ed_safe_shdload_done; + /** @event: cached pending vblank event */ + struct drm_pending_vblank_event *event; + /** @irqs: interrupt list */ + struct dc_crtc_irq irqs[DC_CRTC_IRQS]; +}; + +/** + * struct dc_plane - DC specific drm_plane + * + * Build a plane on content stream with a fetchunit and a layerblend. + */ +struct dc_plane { + /** @base: base drm_plane structure */ + struct drm_plane base; + /** @fu: fetchunit */ + struct dc_fu *fu; + /** @cf: content stream constframe */ + struct dc_cf *cf; + /** @lb: layerblend */ + struct dc_lb *lb; + /** @ed: content stream extdst */ + struct dc_ed *ed; +}; + +#endif /* __DC_KMS_H__ */ diff --git a/drivers/gpu/drm/imx/dc/dc-plane.c b/drivers/gpu/drm/imx/dc/dc-plane.c new file mode 100644 index 000000000000..d8b946fb90de --- /dev/null +++ b/drivers/gpu/drm/imx/dc/dc-plane.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 NXP + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dc-drv.h" +#include "dc-fu.h" +#include "dc-kms.h" + +#define DC_PLANE_MAX_PITCH 0x10000 +#define DC_PLANE_MAX_PIX_CNT 8192 + +#define dc_plane_dbg(plane, fmt, ...) \ +do { \ + struct drm_plane *_plane = (plane); \ + drm_dbg_kms(_plane->dev, "[PLANE:%d:%s] " fmt, \ + _plane->base.id, _plane->name, ##__VA_ARGS__); \ +} while (0) + +static const uint32_t dc_plane_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static const struct drm_plane_funcs dc_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static inline struct dc_plane *to_dc_plane(struct drm_plane *plane) +{ + return container_of(plane, struct dc_plane, base); +} + +static int dc_plane_check_max_source_resolution(struct drm_plane_state *state) +{ + int src_h = drm_rect_height(&state->src) >> 16; + int src_w = drm_rect_width(&state->src) >> 16; + + if (src_w > DC_PLANE_MAX_PIX_CNT || src_h > DC_PLANE_MAX_PIX_CNT) { + dc_plane_dbg(state->plane, "invalid source resolution\n"); + return -EINVAL; + } + + return 0; +} + +static int dc_plane_check_fb(struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + dma_addr_t baseaddr = drm_fb_dma_get_gem_addr(fb, state, 0); + + /* base address alignment */ + if (baseaddr & 0x3) { + dc_plane_dbg(state->plane, "fb bad baddr alignment\n"); + return -EINVAL; + } + + /* pitches[0] range */ + if (fb->pitches[0] > DC_PLANE_MAX_PITCH) { + dc_plane_dbg(state->plane, "fb pitches[0] is out of range\n"); + return -EINVAL; + } + + /* pitches[0] alignment */ + if (fb->pitches[0] & 0x3) { + dc_plane_dbg(state->plane, "fb bad pitches[0] alignment\n"); + return -EINVAL; + } + + return 0; +} + +static int +dc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc_state *crtc_state; + int ret; + + /* ok to disable */ + if (!plane_state->fb) + return 0; + + if (!plane_state->crtc) { + dc_plane_dbg(plane, "no CRTC in plane state\n"); + return -EINVAL; + } + + crtc_state = + drm_atomic_get_existing_crtc_state(state, plane_state->crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, false); + if (ret) { + dc_plane_dbg(plane, "failed to check plane state: %d\n", ret); + return ret; + } + + ret = dc_plane_check_max_source_resolution(plane_state); + if (ret) + return ret; + + return dc_plane_check_fb(plane_state); +} + +static void +dc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = + drm_atomic_get_new_plane_state(state, plane); + struct dc_plane *dplane = to_dc_plane(plane); + struct drm_framebuffer *fb = new_state->fb; + const struct dc_fu_ops *fu_ops; + struct dc_lb *lb = dplane->lb; + struct dc_fu *fu = dplane->fu; + dma_addr_t baseaddr; + int src_w, src_h; + int idx; + + if (!drm_dev_enter(plane->dev, &idx)) + return; + + src_w = drm_rect_width(&new_state->src) >> 16; + src_h = drm_rect_height(&new_state->src) >> 16; + + baseaddr = drm_fb_dma_get_gem_addr(fb, new_state, 0); + + fu_ops = dc_fu_get_ops(dplane->fu); + + fu_ops->set_layerblend(fu, lb); + fu_ops->set_burstlength(fu, baseaddr); + fu_ops->set_src_stride(fu, DC_FETCHUNIT_FRAC0, fb->pitches[0]); + fu_ops->set_src_buf_dimensions(fu, DC_FETCHUNIT_FRAC0, src_w, src_h); + fu_ops->set_fmt(fu, DC_FETCHUNIT_FRAC0, fb->format); + fu_ops->set_framedimensions(fu, src_w, src_h); + fu_ops->set_baseaddress(fu, DC_FETCHUNIT_FRAC0, baseaddr); + fu_ops->enable_src_buf(fu, DC_FETCHUNIT_FRAC0); + + dc_plane_dbg(plane, "uses %s\n", fu_ops->get_name(fu)); + + dc_lb_pec_dynamic_prim_sel(lb, dc_cf_get_link_id(dplane->cf)); + dc_lb_pec_dynamic_sec_sel(lb, fu_ops->get_link_id(fu)); + dc_lb_mode(lb, LB_BLEND); + dc_lb_position(lb, new_state->dst.x1, new_state->dst.y1); + dc_lb_pec_clken(lb, CLKEN_AUTOMATIC); + + dc_plane_dbg(plane, "uses LayerBlend%d\n", dc_lb_get_id(lb)); + + /* set ExtDst's source to LayerBlend */ + dc_ed_pec_src_sel(dplane->ed, dc_lb_get_link_id(lb)); + + drm_dev_exit(idx); +} + +static void dc_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct dc_plane *dplane = to_dc_plane(plane); + const struct dc_fu_ops *fu_ops; + int idx; + + if (!drm_dev_enter(plane->dev, &idx)) + return; + + /* disable fetchunit in shadow */ + fu_ops = dc_fu_get_ops(dplane->fu); + fu_ops->disable_src_buf(dplane->fu, DC_FETCHUNIT_FRAC0); + + /* set ExtDst's source to ConstFrame */ + dc_ed_pec_src_sel(dplane->ed, dc_cf_get_link_id(dplane->cf)); + + drm_dev_exit(idx); +} + +static const struct drm_plane_helper_funcs dc_plane_helper_funcs = { + .atomic_check = dc_plane_atomic_check, + .atomic_update = dc_plane_atomic_update, + .atomic_disable = dc_plane_atomic_disable, +}; + +int dc_plane_init(struct dc_drm_device *dc_drm, struct dc_plane *dc_plane) +{ + struct drm_plane *plane = &dc_plane->base; + int ret; + + ret = drm_universal_plane_init(&dc_drm->base, plane, 0, &dc_plane_funcs, + dc_plane_formats, + ARRAY_SIZE(dc_plane_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + + drm_plane_helper_add(plane, &dc_plane_helper_funcs); + + dc_plane->fu = dc_drm->pe->fu_disp[plane->index]; + dc_plane->cf = dc_drm->pe->cf_cont[plane->index]; + dc_plane->lb = dc_drm->pe->lb[plane->index]; + dc_plane->ed = dc_drm->pe->ed_cont[plane->index]; + + return 0; +} From 217f80acfcf126b7d7d7b818c9bfea3c96fa85ec Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Mon, 14 Apr 2025 11:50:22 +0800 Subject: [PATCH 019/279] MAINTAINERS: Add maintainer for i.MX8qxp Display Controller Add myself as the maintainer of i.MX8qxp Display Controller. Signed-off-by: Liu Ying Reviewed-by: Maxime Ripard Link: https://lore.kernel.org/r/20250414035028.1561475-14-victor.liu@nxp.com --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ef7f0d002a09..88b17f23ed4f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7929,6 +7929,14 @@ F: Documentation/devicetree/bindings/display/imx/ F: drivers/gpu/drm/imx/ipuv3/ F: drivers/gpu/ipu-v3/ +DRM DRIVERS FOR FREESCALE IMX8 DISPLAY CONTROLLER +M: Liu Ying +L: dri-devel@lists.freedesktop.org +S: Maintained +T: git https://gitlab.freedesktop.org/drm/misc/kernel.git +F: Documentation/devicetree/bindings/display/imx/fsl,imx8qxp-dc*.yaml +F: drivers/gpu/drm/imx/dc/ + DRM DRIVERS FOR FREESCALE IMX BRIDGE M: Liu Ying L: dri-devel@lists.freedesktop.org From 9934ab18051118385c7ea44d8e14175edbe6dc9c Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Wed, 14 May 2025 09:31:27 +0200 Subject: [PATCH 020/279] drm/vmwgfx: Use non-hybrid PCI devres API vmwgfx enables its PCI device with pcim_enable_device(). This, implicitly, switches the function pci_request_regions() into managed mode, where it becomes a devres function. The PCI subsystem wants to remove this hybrid nature from its interfaces. To do so, users of the aforementioned combination of functions must be ported to non-hybrid functions. Moreover, since both functions are already managed in this driver, the calls to pci_release_regions() are unnecessary. Remove the calls to pci_release_regions(). Replace the call to sometimes-managed pci_request_regions() with one to always-managed pcim_request_all_regions(). Signed-off-by: Philipp Stanner Reviewed-by: Zack Rusin Signed-off-by: Zack Rusin Link: https://lore.kernel.org/r/20250514073126.85443-2-phasta@kernel.org --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 0695a342b1ef..37b832e552a4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -713,7 +713,7 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, pci_set_master(pdev); - ret = pci_request_regions(pdev, "vmwgfx probe"); + ret = pcim_request_all_regions(pdev, "vmwgfx probe"); if (ret) return ret; @@ -733,7 +733,6 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, if (!dev->rmmio) { drm_err(&dev->drm, "Failed mapping registers mmio memory.\n"); - pci_release_regions(pdev); return -ENOMEM; } } else if (pci_id == VMWGFX_PCI_ID_SVGA2) { @@ -754,11 +753,9 @@ static int vmw_setup_pci_resources(struct vmw_private *dev, if (IS_ERR(dev->fifo_mem)) { drm_err(&dev->drm, "Failed mapping FIFO memory.\n"); - pci_release_regions(pdev); return PTR_ERR(dev->fifo_mem); } } else { - pci_release_regions(pdev); return -EINVAL; } @@ -836,7 +833,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) int ret; enum vmw_res_type i; bool refuse_dma = false; - struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); vmw_sw_context_init(dev_priv); @@ -852,7 +848,7 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) return ret; ret = vmw_detect_version(dev_priv); if (ret) - goto out_no_pci_or_version; + return ret; for (i = vmw_res_context; i < vmw_res_max; ++i) { @@ -1152,15 +1148,13 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) if (dev_priv->ctx.staged_bindings) vmw_binding_state_free(dev_priv->ctx.staged_bindings); -out_no_pci_or_version: - pci_release_regions(pdev); + return ret; } static void vmw_driver_unload(struct drm_device *dev) { struct vmw_private *dev_priv = vmw_priv(dev); - struct pci_dev *pdev = to_pci_dev(dev->dev); enum vmw_res_type i; unregister_pm_notifier(&dev_priv->pm_nb); @@ -1196,8 +1190,6 @@ static void vmw_driver_unload(struct drm_device *dev) idr_destroy(&dev_priv->res_idr[i]); vmw_mksstat_remove_all(dev_priv); - - pci_release_regions(pdev); } static void vmw_postclose(struct drm_device *dev, From 4963049ea1aed7b5aefe164867e0312185e878bc Mon Sep 17 00:00:00 2001 From: Ryosuke Yasuoka Date: Sun, 27 Apr 2025 19:18:23 +0900 Subject: [PATCH 021/279] drm/hyperv: Replace simple-KMS with regular atomic helpers Drop simple-KMS in favor of regular atomic helpers to make the code more modular. The simple-KMS helper mix up plane and CRTC state, so it is obsolete and should go away [1]. Since it just split the simple-pipe functions into per-plane and per-CRTC, no functional changes is expected. [1] https://lore.kernel.org/lkml/dae5089d-e214-4518-b927-5c4149babad8@suse.de/ Acked-by: Javier Martinez Canillas Signed-off-by: Ryosuke Yasuoka Link: https://lore.kernel.org/r/20250427101825.812766-1-ryasuoka@redhat.com Signed-off-by: Jocelyn Falempe --- drivers/gpu/drm/hyperv/hyperv_drm.h | 4 +- drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 174 +++++++++++++------- 2 files changed, 116 insertions(+), 62 deletions(-) diff --git a/drivers/gpu/drm/hyperv/hyperv_drm.h b/drivers/gpu/drm/hyperv/hyperv_drm.h index d2d8582b36df..9e776112c03e 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm.h +++ b/drivers/gpu/drm/hyperv/hyperv_drm.h @@ -11,7 +11,9 @@ struct hyperv_drm_device { /* drm */ struct drm_device dev; - struct drm_simple_display_pipe pipe; + struct drm_plane plane; + struct drm_crtc crtc; + struct drm_encoder encoder; struct drm_connector connector; /* mode */ diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 6c6b57298797..f7d2e973f79e 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -5,6 +5,8 @@ #include +#include +#include #include #include #include @@ -15,7 +17,7 @@ #include #include #include -#include +#include #include "hyperv_drm.h" @@ -38,18 +40,6 @@ static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb, return 0; } -static int hyperv_blit_to_vram_fullscreen(struct drm_framebuffer *fb, - const struct iosys_map *map) -{ - struct drm_rect fullscreen = { - .x1 = 0, - .x2 = fb->width, - .y1 = 0, - .y2 = fb->height, - }; - return hyperv_blit_to_vram_rect(fb, map, &fullscreen); -} - static int hyperv_connector_get_modes(struct drm_connector *connector) { struct hyperv_drm_device *hv = to_hv(connector->dev); @@ -98,30 +88,66 @@ static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h, return 0; } -static void hyperv_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state, - struct drm_plane_state *plane_state) +static const uint32_t hyperv_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static const uint64_t hyperv_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) { - struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev); - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct hyperv_drm_device *hv = to_hv(crtc->dev); + struct drm_plane *plane = &hv->plane; + struct drm_plane_state *plane_state = plane->state; + struct drm_crtc_state *crtc_state = crtc->state; hyperv_hide_hw_ptr(hv->hdev); hyperv_update_situation(hv->hdev, 1, hv->screen_depth, crtc_state->mode.hdisplay, crtc_state->mode.vdisplay, plane_state->fb->pitches[0]); - hyperv_blit_to_vram_fullscreen(plane_state->fb, &shadow_plane_state->data[0]); } -static int hyperv_pipe_check(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state, - struct drm_crtc_state *crtc_state) -{ - struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev); - struct drm_framebuffer *fb = plane_state->fb; +static const struct drm_crtc_helper_funcs hyperv_crtc_helper_funcs = { + .atomic_check = drm_crtc_helper_atomic_check, + .atomic_enable = hyperv_crtc_helper_atomic_enable, +}; - if (fb->format->format != DRM_FORMAT_XRGB8888) - return -EINVAL; +static const struct drm_crtc_funcs hyperv_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static int hyperv_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct hyperv_drm_device *hv = to_hv(plane->dev); + struct drm_framebuffer *fb = plane_state->fb; + struct drm_crtc *crtc = plane_state->crtc; + struct drm_crtc_state *crtc_state = NULL; + int ret; + + if (crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); + if (ret) + return ret; + + if (!plane_state->visible) + return 0; if (fb->pitches[0] * fb->height > hv->fb_size) { drm_err(&hv->dev, "fb size requested by %s for %dX%d (pitch %d) greater than %ld\n", @@ -132,53 +158,85 @@ static int hyperv_pipe_check(struct drm_simple_display_pipe *pipe, return 0; } -static void hyperv_pipe_update(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *old_state) +static void hyperv_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct hyperv_drm_device *hv = to_hv(pipe->crtc.dev); - struct drm_plane_state *state = pipe->plane.state; - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); - struct drm_rect rect; + struct hyperv_drm_device *hv = to_hv(plane->dev); + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state); + struct drm_rect damage; + struct drm_rect dst_clip; + struct drm_atomic_helper_damage_iter iter; - if (drm_atomic_helper_damage_merged(old_state, state, &rect)) { - hyperv_blit_to_vram_rect(state->fb, &shadow_plane_state->data[0], &rect); - hyperv_update_dirt(hv->hdev, &rect); + drm_atomic_helper_damage_iter_init(&iter, old_state, new_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + dst_clip = new_state->dst; + + if (!drm_rect_intersect(&dst_clip, &damage)) + continue; + + hyperv_blit_to_vram_rect(new_state->fb, &shadow_plane_state->data[0], &damage); + hyperv_update_dirt(hv->hdev, &damage); } } -static const struct drm_simple_display_pipe_funcs hyperv_pipe_funcs = { - .enable = hyperv_pipe_enable, - .check = hyperv_pipe_check, - .update = hyperv_pipe_update, - DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, +static const struct drm_plane_helper_funcs hyperv_plane_helper_funcs = { + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .atomic_check = hyperv_plane_atomic_check, + .atomic_update = hyperv_plane_atomic_update, }; -static const uint32_t hyperv_formats[] = { - DRM_FORMAT_XRGB8888, +static const struct drm_plane_funcs hyperv_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + DRM_GEM_SHADOW_PLANE_FUNCS, }; -static const uint64_t hyperv_modifiers[] = { - DRM_FORMAT_MOD_LINEAR, - DRM_FORMAT_MOD_INVALID +static const struct drm_encoder_funcs hyperv_drm_simple_encoder_funcs_cleanup = { + .destroy = drm_encoder_cleanup, }; static inline int hyperv_pipe_init(struct hyperv_drm_device *hv) { + struct drm_device *dev = &hv->dev; + struct drm_encoder *encoder = &hv->encoder; + struct drm_plane *plane = &hv->plane; + struct drm_crtc *crtc = &hv->crtc; + struct drm_connector *connector = &hv->connector; int ret; - ret = drm_simple_display_pipe_init(&hv->dev, - &hv->pipe, - &hyperv_pipe_funcs, - hyperv_formats, - ARRAY_SIZE(hyperv_formats), - hyperv_modifiers, - &hv->connector); + ret = drm_universal_plane_init(dev, plane, 0, + &hyperv_plane_funcs, + hyperv_formats, ARRAY_SIZE(hyperv_formats), + hyperv_modifiers, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + drm_plane_helper_add(plane, &hyperv_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(plane); + + ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, + &hyperv_crtc_funcs, NULL); + if (ret) + return ret; + drm_crtc_helper_add(crtc, &hyperv_crtc_helper_funcs); + + encoder->possible_crtcs = drm_crtc_mask(crtc); + ret = drm_encoder_init(dev, encoder, + &hyperv_drm_simple_encoder_funcs_cleanup, + DRM_MODE_ENCODER_NONE, NULL); if (ret) return ret; - drm_plane_enable_fb_damage_clips(&hv->pipe.plane); + ret = hyperv_conn_init(hv); + if (ret) { + drm_err(dev, "Failed to initialized connector.\n"); + return ret; + } - return 0; + return drm_connector_attach_encoder(connector, encoder); } static enum drm_mode_status @@ -221,12 +279,6 @@ int hyperv_mode_config_init(struct hyperv_drm_device *hv) dev->mode_config.funcs = &hyperv_mode_config_funcs; - ret = hyperv_conn_init(hv); - if (ret) { - drm_err(dev, "Failed to initialized connector.\n"); - return ret; - } - ret = hyperv_pipe_init(hv); if (ret) { drm_err(dev, "Failed to initialized pipe.\n"); From 549810e918155cc00d65d44ed3e7d2bd0aa89df9 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Thu, 15 May 2025 10:49:56 +0100 Subject: [PATCH 022/279] dma-fence: Change signature of __dma_fence_is_later MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the goal of reducing the need for drivers to touch (and dereference) fence->ops, we change the prototype of __dma_fence_is_later() to take fence instead of fence->ops. Signed-off-by: Tvrtko Ursulin Reviewed-by: Christian König Link: https://lore.kernel.org/r/20250515095004.28318-2-tvrtko.ursulin@igalia.com Signed-off-by: Christian König --- drivers/dma-buf/dma-fence-chain.c | 2 +- drivers/dma-buf/sw_sync.c | 2 +- drivers/gpu/drm/xe/xe_hw_fence.c | 2 +- drivers/gpu/drm/xe/xe_sched_job.c | 14 ++++++++------ include/linux/dma-fence.h | 9 ++++----- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/dma-buf/dma-fence-chain.c b/drivers/dma-buf/dma-fence-chain.c index 9663ba1bb6ac..90424f23fd73 100644 --- a/drivers/dma-buf/dma-fence-chain.c +++ b/drivers/dma-buf/dma-fence-chain.c @@ -252,7 +252,7 @@ void dma_fence_chain_init(struct dma_fence_chain *chain, chain->prev_seqno = 0; /* Try to reuse the context of the previous chain node. */ - if (prev_chain && __dma_fence_is_later(seqno, prev->seqno, prev->ops)) { + if (prev_chain && __dma_fence_is_later(prev, seqno, prev->seqno)) { context = prev->context; chain->prev_seqno = prev->seqno; } else { diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c index 4f27ee93a00c..3c20f1d31cf5 100644 --- a/drivers/dma-buf/sw_sync.c +++ b/drivers/dma-buf/sw_sync.c @@ -170,7 +170,7 @@ static bool timeline_fence_signaled(struct dma_fence *fence) { struct sync_timeline *parent = dma_fence_parent(fence); - return !__dma_fence_is_later(fence->seqno, parent->value, fence->ops); + return !__dma_fence_is_later(fence, fence->seqno, parent->value); } static void timeline_fence_set_deadline(struct dma_fence *fence, ktime_t deadline) diff --git a/drivers/gpu/drm/xe/xe_hw_fence.c b/drivers/gpu/drm/xe/xe_hw_fence.c index 0b4f12be3692..03eb8c6d1616 100644 --- a/drivers/gpu/drm/xe/xe_hw_fence.c +++ b/drivers/gpu/drm/xe/xe_hw_fence.c @@ -165,7 +165,7 @@ static bool xe_hw_fence_signaled(struct dma_fence *dma_fence) u32 seqno = xe_map_rd(xe, &fence->seqno_map, 0, u32); return dma_fence->error || - !__dma_fence_is_later(dma_fence->seqno, seqno, dma_fence->ops); + !__dma_fence_is_later(dma_fence, dma_fence->seqno, seqno); } static bool xe_hw_fence_enable_signaling(struct dma_fence *dma_fence) diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c index 1905ca590965..f0a6ce610948 100644 --- a/drivers/gpu/drm/xe/xe_sched_job.c +++ b/drivers/gpu/drm/xe/xe_sched_job.c @@ -216,15 +216,17 @@ void xe_sched_job_set_error(struct xe_sched_job *job, int error) bool xe_sched_job_started(struct xe_sched_job *job) { + struct dma_fence *fence = dma_fence_chain_contained(job->fence); struct xe_lrc *lrc = job->q->lrc[0]; - return !__dma_fence_is_later(xe_sched_job_lrc_seqno(job), - xe_lrc_start_seqno(lrc), - dma_fence_chain_contained(job->fence)->ops); + return !__dma_fence_is_later(fence, + xe_sched_job_lrc_seqno(job), + xe_lrc_start_seqno(lrc)); } bool xe_sched_job_completed(struct xe_sched_job *job) { + struct dma_fence *fence = dma_fence_chain_contained(job->fence); struct xe_lrc *lrc = job->q->lrc[0]; /* @@ -232,9 +234,9 @@ bool xe_sched_job_completed(struct xe_sched_job *job) * parallel handshake is done. */ - return !__dma_fence_is_later(xe_sched_job_lrc_seqno(job), - xe_lrc_seqno(lrc), - dma_fence_chain_contained(job->fence)->ops); + return !__dma_fence_is_later(fence, + xe_sched_job_lrc_seqno(job), + xe_lrc_seqno(lrc)); } void xe_sched_job_arm(struct xe_sched_job *job) diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h index b12776883d14..48b5202c531d 100644 --- a/include/linux/dma-fence.h +++ b/include/linux/dma-fence.h @@ -441,21 +441,20 @@ dma_fence_is_signaled(struct dma_fence *fence) /** * __dma_fence_is_later - return if f1 is chronologically later than f2 + * @fence: fence in whose context to do the comparison * @f1: the first fence's seqno * @f2: the second fence's seqno from the same context - * @ops: dma_fence_ops associated with the seqno * * Returns true if f1 is chronologically later than f2. Both fences must be * from the same context, since a seqno is not common across contexts. */ -static inline bool __dma_fence_is_later(u64 f1, u64 f2, - const struct dma_fence_ops *ops) +static inline bool __dma_fence_is_later(struct dma_fence *fence, u64 f1, u64 f2) { /* This is for backward compatibility with drivers which can only handle * 32bit sequence numbers. Use a 64bit compare when the driver says to * do so. */ - if (ops->use_64bit_seqno) + if (fence->ops->use_64bit_seqno) return f1 > f2; return (int)(lower_32_bits(f1) - lower_32_bits(f2)) > 0; @@ -475,7 +474,7 @@ static inline bool dma_fence_is_later(struct dma_fence *f1, if (WARN_ON(f1->context != f2->context)) return false; - return __dma_fence_is_later(f1->seqno, f2->seqno, f1->ops); + return __dma_fence_is_later(f1, f1->seqno, f2->seqno); } /** From ceb7b62eaaaacfcf87473bd2e99ac73a758620cb Mon Sep 17 00:00:00 2001 From: Huan Yang Date: Mon, 28 Apr 2025 15:38:29 +0800 Subject: [PATCH 023/279] Revert "udmabuf: fix vmap_udmabuf error page set" This reverts commit 18d7de823b7150344d242c3677e65d68c5271b04. We cannot use vmap_pfn() in vmap_udmabuf() as it would fail the pfn_valid() check in vmap_pfn_apply(). This is because vmap_pfn() is intended to be used for mapping non-struct-page memory such as PCIe BARs. Since, udmabuf mostly works with pages/folios backed by shmem/hugetlbfs/THP, vmap_pfn() is not the right tool or API to invoke for implementing vmap. Signed-off-by: Huan Yang Suggested-by: Vivek Kasireddy Reported-by: Bingbu Cao Closes: https://lore.kernel.org/dri-devel/eb7e0137-3508-4287-98c4-816c5fd98e10@vivo.com/T/#mbda4f64a3532b32e061f4e8763bc8e307bea3ca8 Acked-by: Vivek Kasireddy Signed-off-by: Vivek Kasireddy Link: https://lore.kernel.org/r/20250428073831.19942-2-link@vivo.com --- drivers/dma-buf/Kconfig | 1 - drivers/dma-buf/udmabuf.c | 22 +++++++--------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig index fee04fdb0822..b46eb8a552d7 100644 --- a/drivers/dma-buf/Kconfig +++ b/drivers/dma-buf/Kconfig @@ -36,7 +36,6 @@ config UDMABUF depends on DMA_SHARED_BUFFER depends on MEMFD_CREATE || COMPILE_TEST depends on MMU - select VMAP_PFN help A driver to let userspace turn memfd regions into dma-bufs. Qemu can use this to create host dmabufs for guest framebuffers. diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 7eee3eb47a8e..79845565089d 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -109,29 +109,21 @@ static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma) static int vmap_udmabuf(struct dma_buf *buf, struct iosys_map *map) { struct udmabuf *ubuf = buf->priv; - unsigned long *pfns; + struct page **pages; void *vaddr; pgoff_t pg; dma_resv_assert_held(buf->resv); - /** - * HVO may free tail pages, so just use pfn to map each folio - * into vmalloc area. - */ - pfns = kvmalloc_array(ubuf->pagecount, sizeof(*pfns), GFP_KERNEL); - if (!pfns) + pages = kvmalloc_array(ubuf->pagecount, sizeof(*pages), GFP_KERNEL); + if (!pages) return -ENOMEM; - for (pg = 0; pg < ubuf->pagecount; pg++) { - unsigned long pfn = folio_pfn(ubuf->folios[pg]); + for (pg = 0; pg < ubuf->pagecount; pg++) + pages[pg] = &ubuf->folios[pg]->page; - pfn += ubuf->offsets[pg] >> PAGE_SHIFT; - pfns[pg] = pfn; - } - - vaddr = vmap_pfn(pfns, ubuf->pagecount, PAGE_KERNEL); - kvfree(pfns); + vaddr = vm_map_ram(pages, ubuf->pagecount, -1); + kvfree(pages); if (!vaddr) return -EINVAL; From a26fd92b7223160ad31c3e2971b63178faed9cf5 Mon Sep 17 00:00:00 2001 From: Huan Yang Date: Mon, 28 Apr 2025 15:38:30 +0800 Subject: [PATCH 024/279] udmabuf: fix vmap missed offset page Before invoke vmap, we need offer a pages pointer array which each page need to map in vmalloc area. But currently vmap_udmabuf only set each folio's head page into pages, missed each offset pages when iter. This patch set the correctly offset page in each folio into array. Signed-off-by: Huan Yang Fixes: 5e72b2b41a21 ("udmabuf: convert udmabuf driver to use folios") Acked-by: Vivek Kasireddy Signed-off-by: Vivek Kasireddy Link: https://lore.kernel.org/r/20250428073831.19942-3-link@vivo.com --- drivers/dma-buf/udmabuf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 79845565089d..af5200e360a6 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -120,7 +120,8 @@ static int vmap_udmabuf(struct dma_buf *buf, struct iosys_map *map) return -ENOMEM; for (pg = 0; pg < ubuf->pagecount; pg++) - pages[pg] = &ubuf->folios[pg]->page; + pages[pg] = folio_page(ubuf->folios[pg], + ubuf->offsets[pg] >> PAGE_SHIFT); vaddr = vm_map_ram(pages, ubuf->pagecount, -1); kvfree(pages); From adc215791ab2dac76b847258cdc10bf8046f0659 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Thu, 24 Apr 2025 15:02:51 +0200 Subject: [PATCH 025/279] drm/nouveau: nouveau_fence: Standardize list iterations nouveau_fence.c iterates over lists in a non-canonical way. Since the operations done are just basic for-each-loops and list-empty checks, they should be written in the standard form. Use standard list operations. Signed-off-by: Philipp Stanner Link: https://lore.kernel.org/r/20250424130254.42046-3-phasta@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nouveau_fence.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 6ded8c2b6d3b..761c174cb286 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -83,13 +83,11 @@ nouveau_local_fence(struct dma_fence *fence, struct nouveau_drm *drm) void nouveau_fence_context_kill(struct nouveau_fence_chan *fctx, int error) { - struct nouveau_fence *fence; + struct nouveau_fence *fence, *tmp; unsigned long flags; spin_lock_irqsave(&fctx->lock, flags); - while (!list_empty(&fctx->pending)) { - fence = list_entry(fctx->pending.next, typeof(*fence), head); - + list_for_each_entry_safe(fence, tmp, &fctx->pending, head) { if (error && !dma_fence_is_signaled_locked(&fence->base)) dma_fence_set_error(&fence->base, error); @@ -130,13 +128,11 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx) static int nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) { - struct nouveau_fence *fence; + struct nouveau_fence *fence, *tmp; int drop = 0; u32 seq = fctx->read(chan); - while (!list_empty(&fctx->pending)) { - fence = list_entry(fctx->pending.next, typeof(*fence), head); - + list_for_each_entry_safe(fence, tmp, &fctx->pending, head) { if ((int)(seq - fence->base.seqno) < 0) break; @@ -151,15 +147,14 @@ nouveau_fence_uevent_work(struct work_struct *work) { struct nouveau_fence_chan *fctx = container_of(work, struct nouveau_fence_chan, uevent_work); + struct nouveau_channel *chan; + struct nouveau_fence *fence; unsigned long flags; int drop = 0; spin_lock_irqsave(&fctx->lock, flags); - if (!list_empty(&fctx->pending)) { - struct nouveau_fence *fence; - struct nouveau_channel *chan; - - fence = list_entry(fctx->pending.next, typeof(*fence), head); + fence = list_first_entry_or_null(&fctx->pending, typeof(*fence), head); + if (fence) { chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); if (nouveau_fence_update(chan, fctx)) drop = 1; From 2c0ddff2a60d00fa6f02de053e63e72c06f19e82 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Thu, 24 Apr 2025 15:02:52 +0200 Subject: [PATCH 026/279] drm/nouveau: Simplify calls to nvif_event_block() nouveau_fence_signal() returns a de-facto boolean to indicate when nvif_event_block() shall be called. The code can be made more compact and readable by calling nvif_event_block() in nouveau_fence_update() directly. Make those calls in nouveau_fence.c more canonical. Signed-off-by: Philipp Stanner Link: https://lore.kernel.org/r/20250424130254.42046-4-phasta@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nouveau_fence.c | 31 +++++++++++-------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 761c174cb286..2b79bcb7da16 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -50,10 +50,10 @@ nouveau_fctx(struct nouveau_fence *fence) return container_of(fence->base.lock, struct nouveau_fence_chan, lock); } -static int +static bool nouveau_fence_signal(struct nouveau_fence *fence) { - int drop = 0; + bool drop = false; dma_fence_signal_locked(&fence->base); list_del(&fence->head); @@ -63,7 +63,7 @@ nouveau_fence_signal(struct nouveau_fence *fence) struct nouveau_fence_chan *fctx = nouveau_fctx(fence); if (!--fctx->notify_ref) - drop = 1; + drop = true; } dma_fence_put(&fence->base); @@ -125,21 +125,23 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx) kref_put(&fctx->fence_ref, nouveau_fence_context_put); } -static int +static void nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence, *tmp; - int drop = 0; + bool drop = false; u32 seq = fctx->read(chan); list_for_each_entry_safe(fence, tmp, &fctx->pending, head) { if ((int)(seq - fence->base.seqno) < 0) break; - drop |= nouveau_fence_signal(fence); + if (nouveau_fence_signal(fence)) + drop = true; } - return drop; + if (drop) + nvif_event_block(&fctx->event); } static void @@ -150,18 +152,13 @@ nouveau_fence_uevent_work(struct work_struct *work) struct nouveau_channel *chan; struct nouveau_fence *fence; unsigned long flags; - int drop = 0; spin_lock_irqsave(&fctx->lock, flags); fence = list_first_entry_or_null(&fctx->pending, typeof(*fence), head); if (fence) { chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); - if (nouveau_fence_update(chan, fctx)) - drop = 1; + nouveau_fence_update(chan, fctx); } - if (drop) - nvif_event_block(&fctx->event); - spin_unlock_irqrestore(&fctx->lock, flags); } @@ -241,9 +238,7 @@ nouveau_fence_emit(struct nouveau_fence *fence) return -ENODEV; } - if (nouveau_fence_update(chan, fctx)) - nvif_event_block(&fctx->event); - + nouveau_fence_update(chan, fctx); list_add_tail(&fence->head, &fctx->pending); spin_unlock_irq(&fctx->lock); } @@ -265,8 +260,8 @@ nouveau_fence_done(struct nouveau_fence *fence) spin_lock_irqsave(&fctx->lock, flags); chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); - if (chan && nouveau_fence_update(chan, fctx)) - nvif_event_block(&fctx->event); + if (chan) + nouveau_fence_update(chan, fctx); spin_unlock_irqrestore(&fctx->lock, flags); } return dma_fence_is_signaled(&fence->base); From 2628009dba602d9959c9318c7732851780073523 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Thu, 24 Apr 2025 15:02:53 +0200 Subject: [PATCH 027/279] drm/nouveau: Simplify nouveau_fence_done() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nouveau_fence_done() contains an if branch that checks whether a nouveau_fence has either of the two existing nouveau_fence backend ops, which will always evaluate to true. Remove the surplus check. Signed-off-by: Philipp Stanner Reviewed-by: Christian König Link: https://lore.kernel.org/r/20250424130254.42046-5-phasta@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nouveau_fence.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 2b79bcb7da16..fb9811938c82 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -249,21 +249,19 @@ nouveau_fence_emit(struct nouveau_fence *fence) bool nouveau_fence_done(struct nouveau_fence *fence) { - if (fence->base.ops == &nouveau_fence_ops_legacy || - fence->base.ops == &nouveau_fence_ops_uevent) { - struct nouveau_fence_chan *fctx = nouveau_fctx(fence); - struct nouveau_channel *chan; - unsigned long flags; + struct nouveau_fence_chan *fctx = nouveau_fctx(fence); + struct nouveau_channel *chan; + unsigned long flags; - if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) - return true; + if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) + return true; + + spin_lock_irqsave(&fctx->lock, flags); + chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); + if (chan) + nouveau_fence_update(chan, fctx); + spin_unlock_irqrestore(&fctx->lock, flags); - spin_lock_irqsave(&fctx->lock, flags); - chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock)); - if (chan) - nouveau_fence_update(chan, fctx); - spin_unlock_irqrestore(&fctx->lock, flags); - } return dma_fence_is_signaled(&fence->base); } From e0677e52545349b76a946c75fea89aa698aeb97a Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Thu, 24 Apr 2025 15:02:54 +0200 Subject: [PATCH 028/279] drm/nouveau: Check dma_fence in canonical way In nouveau_fence_done(), a fence is checked for being signaled by manually evaluating the base fence's bits. This can be done in a canonical manner through dma_fence_is_signaled(). Replace the bit-check with dma_fence_is_signaled(). Signed-off-by: Philipp Stanner Link: https://lore.kernel.org/r/20250424130254.42046-6-phasta@kernel.org Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nouveau_fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index fb9811938c82..d5654e26d5bc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -253,7 +253,7 @@ nouveau_fence_done(struct nouveau_fence *fence) struct nouveau_channel *chan; unsigned long flags; - if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) + if (dma_fence_is_signaled(&fence->base)) return true; spin_lock_irqsave(&fctx->lock, flags); From 4b1f230c875d05186604b61a28e6c7db6bd1424d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 30 Apr 2025 11:06:32 +0300 Subject: [PATCH 029/279] drm/nouveau/fifo: small cleanup in nvkm_chan_cctx_get() "&chan->cgrp->mutex" and "&cgrp->mutex" are the same thing. Use "&cgrp->mutex" consistently. It looks nicer and it silences a Smatch static checker warning. Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/aBHaCM66pXaP84ei@stanley.mountain Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c index 7d4716dcd512..f5cd7f7c48b4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c @@ -104,7 +104,7 @@ nvkm_chan_cctx_get(struct nvkm_chan *chan, struct nvkm_engn *engn, struct nvkm_c if (cctx) { refcount_inc(&cctx->refs); *pcctx = cctx; - mutex_unlock(&chan->cgrp->mutex); + mutex_unlock(&cgrp->mutex); return 0; } From 01738c4f4725005b8a8cdf0205761cb24b192a23 Mon Sep 17 00:00:00 2001 From: Zhang Enpei Date: Thu, 15 May 2025 20:11:08 +0800 Subject: [PATCH 030/279] drm/nouveau/dp: convert to use ERR_CAST() As opposed to open-code, use ERR_CAST to clearly indicate that this is a pointer to an error value and a type conversion is performed. Signed-off-by: Zhang Enpei Link: https://lore.kernel.org/r/20250515201108576jof-gkjSxRfMaGDgKo-pc@zte.com.cn Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nvkm/engine/disp/r535.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/r535.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/r535.c index 99110ab2f44d..43ad467d09db 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/r535.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/r535.c @@ -646,7 +646,7 @@ r535_conn_new(struct nvkm_disp *disp, u32 id) ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom, NV0073_CTRL_CMD_SPECIFIC_GET_CONNECTOR_DATA, sizeof(*ctrl)); if (IS_ERR(ctrl)) - return (void *)ctrl; + return ERR_CAST(ctrl); ctrl->subDeviceInstance = 0; ctrl->displayId = BIT(id); From 3330b71caff6cdc387fdad68a895c9c81cc2f477 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 15 May 2025 14:11:10 -0700 Subject: [PATCH 031/279] drm/panel-edp: Add BOE NV133WUM-N61 panel entry Add an eDP panel for BOE NV133WUM-N61, which appears to be a 3rd panel option on the lenevo x13s laptop. edid: 00 ff ff ff ff ff ff 00 09 e5 64 09 00 00 00 00 16 1e 01 04 a5 1d 12 78 03 55 8e a7 51 4c 9c 26 0f 52 53 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 74 3c 80 a0 70 b0 28 40 30 20 36 00 1e b3 10 00 00 1a 5d 30 80 a0 70 b0 28 40 30 20 36 00 1e b3 10 00 00 1a 00 00 00 fe 00 42 4f 45 20 48 46 0a 20 20 20 20 20 20 00 00 00 fe 00 4e 56 31 33 33 57 55 4d 2d 4e 36 31 0a 00 7d datasheet: https://datasheet4u.com/pdf-down/N/V/1/NV133WUM-N61-BOE.pdf v2: Actually get the panel name correct in the table Signed-off-by: Rob Clark Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20250515211110.8806-1-robdclark@gmail.com --- drivers/gpu/drm/panel/panel-edp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 90e8c154a978..00dae545832e 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1923,6 +1923,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x094b, &delay_200_500_e50, "NT116WHM-N21"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0951, &delay_200_500_e80, "NV116WHM-N47"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x095f, &delay_200_500_e50, "NE135FBM-N41 v8.1"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0964, &delay_200_500_e50, "NV133WUM-N61"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x096e, &delay_200_500_e50_po2e200, "NV116WHM-T07 V8.0"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0979, &delay_200_500_e50, "NV116WHM-N49 V8.0"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x098d, &boe_nv110wtm_n61.delay, "NV110WTM-N61"), From 6f446bbe412ab3d75651d1cc52e31aaf801dbea8 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Sun, 18 May 2025 22:54:11 +0000 Subject: [PATCH 032/279] dt-bindings: gpu: mali-utgard: Add Rockchip RK3528 compatible Rockchip RK3528 SoC has a Mali-450 MP2. Add a compatible for it. Signed-off-by: Jonas Karlman Acked-by: Conor Dooley Signed-off-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250518225418.682182-2-jonas@kwiboo.se --- Documentation/devicetree/bindings/gpu/arm,mali-utgard.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-utgard.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.yaml index 9318817ea135..c8d0d9192d92 100644 --- a/Documentation/devicetree/bindings/gpu/arm,mali-utgard.yaml +++ b/Documentation/devicetree/bindings/gpu/arm,mali-utgard.yaml @@ -47,6 +47,7 @@ properties: - hisilicon,hi6220-mali - mediatek,mt7623-mali - rockchip,rk3328-mali + - rockchip,rk3528-mali - const: arm,mali-450 # "arm,mali-300" @@ -148,6 +149,7 @@ allOf: - rockchip,rk3188-mali - rockchip,rk3228-mali - rockchip,rk3328-mali + - rockchip,rk3528-mali then: required: - resets From 099593a28138b48feea5be8ce700e5bc4565e31d Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Fri, 9 May 2025 11:15:59 +0800 Subject: [PATCH 033/279] drm/rockchip: cleanup fb when drm_gem_fb_afbc_init failed In the function drm_gem_fb_init_with_funcs, the framebuffer (fb) and its corresponding object ID have already been registered. So we need to cleanup the drm framebuffer if the subsequent execution of drm_gem_fb_afbc_init fails. Directly call drm_framebuffer_put to ensure that all fb related resources are cleanup. Fixes: 7707f7227f09 ("drm/rockchip: Add support for afbc") Signed-off-by: Andy Yan Signed-off-by: Heiko Stuebner Link: https://lore.kernel.org/r/20250509031607.2542187-1-andyshrk@163.com --- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index dcc1f07632c3..5829ee061c61 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -52,16 +52,9 @@ rockchip_fb_create(struct drm_device *dev, struct drm_file *file, } if (drm_is_afbc(mode_cmd->modifier[0])) { - int ret, i; - ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb); if (ret) { - struct drm_gem_object **obj = afbc_fb->base.obj; - - for (i = 0; i < info->num_planes; ++i) - drm_gem_object_put(obj[i]); - - kfree(afbc_fb); + drm_framebuffer_put(&afbc_fb->base); return ERR_PTR(ret); } } From ce6c4580334a45f3840834c880f395003f5bbc93 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Mar 2025 13:00:25 +0200 Subject: [PATCH 034/279] accel/habanalabs: Switch to use %ptTs Use %ptTs instead of open-coded variant to print contents of time64_t type in human readable form. This changes N/A output to 1970-01-01 00:00:00 for zero timestamps, but it's used only in the dev_err() output and won't break anything. Signed-off-by: Andy Shevchenko Acked-by: Yaron Avizrat Reviewed-by: Koby Elbaz Link: https://lore.kernel.org/r/20250305110126.2134307-1-andriy.shevchenko@linux.intel.com Signed-off-by: Koby Elbaz --- drivers/accel/habanalabs/common/device.c | 25 +++--------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/drivers/accel/habanalabs/common/device.c b/drivers/accel/habanalabs/common/device.c index 68eebed3b050..80fa08bf57bd 100644 --- a/drivers/accel/habanalabs/common/device.c +++ b/drivers/accel/habanalabs/common/device.c @@ -1066,28 +1066,11 @@ static bool is_pci_link_healthy(struct hl_device *hdev) return (device_id == hdev->pdev->device); } -static void stringify_time_of_last_heartbeat(struct hl_device *hdev, char *time_str, size_t size, - bool is_pq_hb) -{ - time64_t seconds = is_pq_hb ? hdev->heartbeat_debug_info.last_pq_heartbeat_ts - : hdev->heartbeat_debug_info.last_eq_heartbeat_ts; - struct tm tm; - - if (!seconds) - return; - - time64_to_tm(seconds, 0, &tm); - - snprintf(time_str, size, "%ld-%02d-%02d %02d:%02d:%02d (UTC)", - tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); -} - static bool hl_device_eq_heartbeat_received(struct hl_device *hdev) { struct eq_heartbeat_debug_info *heartbeat_debug_info = &hdev->heartbeat_debug_info; u32 cpu_q_id = heartbeat_debug_info->cpu_queue_id, pq_pi_mask = (HL_QUEUE_LENGTH << 1) - 1; struct asic_fixed_properties *prop = &hdev->asic_prop; - char pq_time_str[64] = "N/A", eq_time_str[64] = "N/A"; if (!prop->cpucp_info.eq_health_check_supported) return true; @@ -1095,17 +1078,15 @@ static bool hl_device_eq_heartbeat_received(struct hl_device *hdev) if (!hdev->eq_heartbeat_received) { dev_err(hdev->dev, "EQ heartbeat event was not received!\n"); - stringify_time_of_last_heartbeat(hdev, pq_time_str, sizeof(pq_time_str), true); - stringify_time_of_last_heartbeat(hdev, eq_time_str, sizeof(eq_time_str), false); dev_err(hdev->dev, - "EQ: {CI %u, HB counter %u, last HB time: %s}, PQ: {PI: %u, CI: %u (%u), last HB time: %s}\n", + "EQ: {CI %u, HB counter %u, last HB time: %ptTs}, PQ: {PI: %u, CI: %u (%u), last HB time: %ptTs}\n", hdev->event_queue.ci, heartbeat_debug_info->heartbeat_event_counter, - eq_time_str, + &hdev->heartbeat_debug_info.last_eq_heartbeat_ts, hdev->kernel_queues[cpu_q_id].pi, atomic_read(&hdev->kernel_queues[cpu_q_id].ci), atomic_read(&hdev->kernel_queues[cpu_q_id].ci) & pq_pi_mask, - pq_time_str); + &hdev->heartbeat_debug_info.last_pq_heartbeat_ts); hl_eq_dump(hdev, &hdev->event_queue); From cb8d4323302c7ad6b8baa1f5ca29f6186b30f316 Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Wed, 7 May 2025 23:36:29 +0530 Subject: [PATCH 035/279] dt-bindings: display: ti,am65x-dss: Add support for AM62L DSS The DSS controller on TI's AM62L SoC is an update from that on TI's AM625/AM65x/AM62A7 SoC. The AM62L DSS [1] only supports a single display pipeline using a single overlay manager, single video port and a single video lite pipeline which does not support scaling. The output of video port is routed to SoC boundary via DPI interface and the DPI signals from the video port are also routed to DSI Tx controller present within the SoC. [1]: Section 11.7 (Display Subsystem and Peripherals) Link : https://www.ti.com/lit/pdf/sprujb4 Reviewed-by: Krzysztof Kozlowski Reviewed-by: Jayesh Choudhary Reviewed-by: Tomi Valkeinen Signed-off-by: Devarsh Thakkar Link: https://lore.kernel.org/r/20250507180631.874930-2-devarsht@ti.com Signed-off-by: Tomi Valkeinen --- .../bindings/display/ti/ti,am65x-dss.yaml | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml index 31c4ffcb599c..a5b13cb7bc73 100644 --- a/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml +++ b/Documentation/devicetree/bindings/display/ti/ti,am65x-dss.yaml @@ -12,18 +12,25 @@ maintainers: - Tomi Valkeinen description: | - The AM625 and AM65x TI Keystone Display SubSystem with two output + The AM625 and AM65x TI Keystone Display SubSystem has two output ports and two video planes. In AM65x DSS, the first video port supports 1 OLDI TX and in AM625 DSS, the first video port output is internally routed to 2 OLDI TXes. The second video port supports DPI format. The first plane is full video plane with all features and the second is a "lite plane" without scaling support. + The AM62L display subsystem has a single output port which supports DPI + format but it only supports single video "lite plane" which does not support + scaling. The output port is routed to SoC boundary via DPI interface and same + DPI signals are also routed internally to DSI Tx controller present within the + SoC. Due to clocking limitations only one of the interface i.e. either DSI or + DPI can be used at once. properties: compatible: enum: - ti,am625-dss - ti,am62a7-dss + - ti,am62l-dss - ti,am65x-dss reg: @@ -91,6 +98,8 @@ properties: For AM625 DSS, the internal DPI output port node from video port 1. For AM62A7 DSS, the port is tied off inside the SoC. + For AM62L DSS, the DSS DPI output port node from video port 1 + or DSI Tx controller node connected to video port 1. port@1: $ref: /schemas/graph.yaml#/properties/port @@ -123,6 +132,16 @@ allOf: ports: properties: port@0: false + - if: + properties: + compatible: + contains: + const: ti,am62l-dss + then: + properties: + ports: + properties: + port@1: false required: - compatible From e019f515c969cef78187b9cb87c6da06b47568b2 Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Wed, 7 May 2025 23:36:30 +0530 Subject: [PATCH 036/279] drm/tidss: Update infrastructure to support K3 DSS cut-down versions SoCs like AM62Lx support cut-down version of K3 DSS where although same register space is supported as in other K3 DSS supported SoCs such as AM65x, AM62x, AM62Ax but some of the resources such as planes and corresponding register spaces are truncated. For e.g. AM62Lx has only single VIDL pipeline supported, so corresponding register spaces for other video pipelines need to be skipped. To add a generic support for future SoCs where one or more video pipelines can get truncated from the parent register space, move the video plane related information to vid_info struct which will also have a field to indicate hardware index of each of the available video planes, so that driver only maps and programs those video pipes and skips the unavailable ones. While at it, also change the num_planes field in the features structure to num_vid so that all places in code which use vid_info structure are highlighted in the code. Signed-off-by: Devarsh Thakkar Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250507180631.874930-3-devarsht@ti.com Signed-off-by: Tomi Valkeinen --- drivers/gpu/drm/tidss/tidss_crtc.c | 4 +- drivers/gpu/drm/tidss/tidss_dispc.c | 154 +++++++++++++++++++++------- drivers/gpu/drm/tidss/tidss_dispc.h | 11 +- drivers/gpu/drm/tidss/tidss_kms.c | 2 +- drivers/gpu/drm/tidss/tidss_plane.c | 2 +- 5 files changed, 127 insertions(+), 46 deletions(-) diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c index 94f8e3178df5..a2f40a5c7703 100644 --- a/drivers/gpu/drm/tidss/tidss_crtc.c +++ b/drivers/gpu/drm/tidss/tidss_crtc.c @@ -130,7 +130,7 @@ static void tidss_crtc_position_planes(struct tidss_device *tidss, !to_tidss_crtc_state(cstate)->plane_pos_changed) return; - for (layer = 0; layer < tidss->feat->num_planes; layer++) { + for (layer = 0; layer < tidss->feat->num_vids ; layer++) { struct drm_plane_state *pstate; struct drm_plane *plane; bool layer_active = false; @@ -271,7 +271,7 @@ static void tidss_crtc_atomic_disable(struct drm_crtc *crtc, * another videoport, the DSS will report sync lost issues. Disable all * the layers here as a work-around. */ - for (u32 layer = 0; layer < tidss->feat->num_planes; layer++) + for (u32 layer = 0; layer < tidss->feat->num_vids; layer++) dispc_ovr_enable_layer(tidss->dispc, tcrtc->hw_videoport, layer, false); diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index cacb5f3d8085..774c608c88b5 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -103,9 +103,16 @@ const struct dispc_features dispc_k2g_feats = { }, }, - .num_planes = 1, - .vid_name = { "vid1" }, - .vid_lite = { false }, + .num_vids = 1, + + .vid_info = { + { + .name = "vid1", + .is_lite = false, + .hw_id = 0, + }, + }, + .vid_order = { 0 }, }; @@ -178,11 +185,22 @@ const struct dispc_features dispc_am65x_feats = { }, }, - .num_planes = 2, + .num_vids = 2, /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ - .vid_name = { "vid", "vidl1" }, - .vid_lite = { false, true, }, - .vid_order = { 1, 0 }, + .vid_info = { + { + .name = "vid", + .is_lite = false, + .hw_id = 0, + }, + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + }, + }, + + .vid_order = {1, 0}, }; static const u16 tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = { @@ -267,9 +285,32 @@ const struct dispc_features dispc_j721e_feats = { .gamma_type = TIDSS_GAMMA_10BIT, }, }, - .num_planes = 4, - .vid_name = { "vid1", "vidl1", "vid2", "vidl2" }, - .vid_lite = { 0, 1, 0, 1, }, + + .num_vids = 4, + + .vid_info = { + { + .name = "vid1", + .is_lite = false, + .hw_id = 0, + }, + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + }, + { + .name = "vid2", + .is_lite = false, + .hw_id = 2, + }, + { + .name = "vidl2", + .is_lite = true, + .hw_id = 3, + }, + }, + .vid_order = { 1, 3, 0, 2 }, }; @@ -315,11 +356,23 @@ const struct dispc_features dispc_am625_feats = { }, }, - .num_planes = 2, + .num_vids = 2, + /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ - .vid_name = { "vid", "vidl1" }, - .vid_lite = { false, true, }, - .vid_order = { 1, 0 }, + .vid_info = { + { + .name = "vid", + .is_lite = false, + .hw_id = 0, + }, + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + } + }, + + .vid_order = {1, 0}, }; const struct dispc_features dispc_am62a7_feats = { @@ -369,11 +422,22 @@ const struct dispc_features dispc_am62a7_feats = { }, }, - .num_planes = 2, - /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ - .vid_name = { "vid", "vidl1" }, - .vid_lite = { false, true, }, - .vid_order = { 1, 0 }, + .num_vids = 2, + + .vid_info = { + { + .name = "vid", + .is_lite = false, + .hw_id = 0, + }, + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + } + }, + + .vid_order = {1, 0}, }; static const u16 *dispc_common_regmap; @@ -734,7 +798,8 @@ static void dispc_k3_vp_write_irqstatus(struct dispc_device *dispc, static dispc_irq_t dispc_k3_vid_read_irqstatus(struct dispc_device *dispc, u32 hw_plane) { - u32 stat = dispc_read(dispc, DISPC_VID_IRQSTATUS(hw_plane)); + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; + u32 stat = dispc_read(dispc, DISPC_VID_IRQSTATUS(hw_id)); return dispc_vid_irq_from_raw(stat, hw_plane); } @@ -742,9 +807,10 @@ static dispc_irq_t dispc_k3_vid_read_irqstatus(struct dispc_device *dispc, static void dispc_k3_vid_write_irqstatus(struct dispc_device *dispc, u32 hw_plane, dispc_irq_t vidstat) { + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); - dispc_write(dispc, DISPC_VID_IRQSTATUS(hw_plane), stat); + dispc_write(dispc, DISPC_VID_IRQSTATUS(hw_id), stat); } static dispc_irq_t dispc_k3_vp_read_irqenable(struct dispc_device *dispc, @@ -766,7 +832,8 @@ static void dispc_k3_vp_set_irqenable(struct dispc_device *dispc, static dispc_irq_t dispc_k3_vid_read_irqenable(struct dispc_device *dispc, u32 hw_plane) { - u32 stat = dispc_read(dispc, DISPC_VID_IRQENABLE(hw_plane)); + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; + u32 stat = dispc_read(dispc, DISPC_VID_IRQENABLE(hw_id)); return dispc_vid_irq_from_raw(stat, hw_plane); } @@ -774,9 +841,10 @@ static dispc_irq_t dispc_k3_vid_read_irqenable(struct dispc_device *dispc, static void dispc_k3_vid_set_irqenable(struct dispc_device *dispc, u32 hw_plane, dispc_irq_t vidstat) { + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); - dispc_write(dispc, DISPC_VID_IRQENABLE(hw_plane), stat); + dispc_write(dispc, DISPC_VID_IRQENABLE(hw_id), stat); } static @@ -788,7 +856,8 @@ void dispc_k3_clear_irqstatus(struct dispc_device *dispc, dispc_irq_t clearmask) if (clearmask & DSS_IRQ_VP_MASK(i)) dispc_k3_vp_write_irqstatus(dispc, i, clearmask); } - for (i = 0; i < dispc->feat->num_planes; ++i) { + + for (i = 0; i < dispc->feat->num_vids; ++i) { if (clearmask & DSS_IRQ_PLANE_MASK(i)) dispc_k3_vid_write_irqstatus(dispc, i, clearmask); } @@ -809,7 +878,7 @@ dispc_irq_t dispc_k3_read_and_clear_irqstatus(struct dispc_device *dispc) for (i = 0; i < dispc->feat->num_vps; ++i) status |= dispc_k3_vp_read_irqstatus(dispc, i); - for (i = 0; i < dispc->feat->num_planes; ++i) + for (i = 0; i < dispc->feat->num_vids; ++i) status |= dispc_k3_vid_read_irqstatus(dispc, i); dispc_k3_clear_irqstatus(dispc, status); @@ -825,7 +894,7 @@ static dispc_irq_t dispc_k3_read_irqenable(struct dispc_device *dispc) for (i = 0; i < dispc->feat->num_vps; ++i) enable |= dispc_k3_vp_read_irqenable(dispc, i); - for (i = 0; i < dispc->feat->num_planes; ++i) + for (i = 0; i < dispc->feat->num_vids; ++i) enable |= dispc_k3_vid_read_irqenable(dispc, i); return enable; @@ -851,12 +920,15 @@ static void dispc_k3_set_irqenable(struct dispc_device *dispc, main_disable |= BIT(i); /* VP IRQ */ } - for (i = 0; i < dispc->feat->num_planes; ++i) { + for (i = 0; i < dispc->feat->num_vids; ++i) { + u32 hw_id = dispc->feat->vid_info[i].hw_id; + dispc_k3_vid_set_irqenable(dispc, i, mask); + if (mask & DSS_IRQ_PLANE_MASK(i)) - main_enable |= BIT(i + 4); /* VID IRQ */ + main_enable |= BIT(hw_id + 4); /* VID IRQ */ else - main_disable |= BIT(i + 4); /* VID IRQ */ + main_disable |= BIT(hw_id + 4); /* VID IRQ */ } if (main_enable) @@ -1358,8 +1430,10 @@ static void dispc_am65x_ovr_set_plane(struct dispc_device *dispc, u32 hw_plane, u32 hw_videoport, u32 x, u32 y, u32 layer) { + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), - hw_plane, 4, 1); + hw_id, 4, 1); OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), x, 17, 6); OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), @@ -1370,8 +1444,10 @@ static void dispc_j721e_ovr_set_plane(struct dispc_device *dispc, u32 hw_plane, u32 hw_videoport, u32 x, u32 y, u32 layer) { + u32 hw_id = dispc->feat->vid_info[hw_plane].hw_id; + OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), - hw_plane, 4, 1); + hw_id, 4, 1); OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES2(layer), x, 13, 0); OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES2(layer), @@ -2025,7 +2101,7 @@ int dispc_plane_check(struct dispc_device *dispc, u32 hw_plane, const struct drm_plane_state *state, u32 hw_videoport) { - bool lite = dispc->feat->vid_lite[hw_plane]; + bool lite = dispc->feat->vid_info[hw_plane].is_lite; u32 fourcc = state->fb->format->format; bool need_scaling = state->src_w >> 16 != state->crtc_w || state->src_h >> 16 != state->crtc_h; @@ -2096,7 +2172,7 @@ void dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane, const struct drm_plane_state *state, u32 hw_videoport) { - bool lite = dispc->feat->vid_lite[hw_plane]; + bool lite = dispc->feat->vid_info[hw_plane].is_lite; u32 fourcc = state->fb->format->format; u16 cpp = state->fb->format->cpp[0]; u32 fb_width = state->fb->pitches[0] / cpp; @@ -2210,7 +2286,7 @@ static void dispc_k2g_plane_init(struct dispc_device *dispc) /* MFLAG_START = MFLAGNORMALSTARTMODE */ REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); - for (hw_plane = 0; hw_plane < dispc->feat->num_planes; hw_plane++) { + for (hw_plane = 0; hw_plane < dispc->feat->num_vids; hw_plane++) { u32 size = dispc_vid_get_fifo_size(dispc, hw_plane); u32 thr_low, thr_high; u32 mflag_low, mflag_high; @@ -2226,7 +2302,7 @@ static void dispc_k2g_plane_init(struct dispc_device *dispc) dev_dbg(dispc->dev, "%s: bufsize %u, buf_threshold %u/%u, mflag threshold %u/%u preload %u\n", - dispc->feat->vid_name[hw_plane], + dispc->feat->vid_info[hw_plane].name, size, thr_high, thr_low, mflag_high, mflag_low, @@ -2265,7 +2341,7 @@ static void dispc_k3_plane_init(struct dispc_device *dispc) /* MFLAG_START = MFLAGNORMALSTARTMODE */ REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); - for (hw_plane = 0; hw_plane < dispc->feat->num_planes; hw_plane++) { + for (hw_plane = 0; hw_plane < dispc->feat->num_vids; hw_plane++) { u32 size = dispc_vid_get_fifo_size(dispc, hw_plane); u32 thr_low, thr_high; u32 mflag_low, mflag_high; @@ -2281,7 +2357,7 @@ static void dispc_k3_plane_init(struct dispc_device *dispc) dev_dbg(dispc->dev, "%s: bufsize %u, buf_threshold %u/%u, mflag threshold %u/%u preload %u\n", - dispc->feat->vid_name[hw_plane], + dispc->feat->vid_info[hw_plane].name, size, thr_high, thr_low, mflag_high, mflag_low, @@ -2898,8 +2974,8 @@ int dispc_init(struct tidss_device *tidss) if (r) return r; - for (i = 0; i < dispc->feat->num_planes; i++) { - r = dispc_iomap_resource(pdev, dispc->feat->vid_name[i], + for (i = 0; i < dispc->feat->num_vids; i++) { + r = dispc_iomap_resource(pdev, dispc->feat->vid_info[i].name, &dispc->base_vid[i]); if (r) return r; diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index 086327d51a90..72a0146e57d5 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -46,6 +46,12 @@ struct dispc_features_scaling { u32 xinc_max; }; +struct dispc_vid_info { + const char *name; /* Should match dt reg names */ + u32 hw_id; + bool is_lite; +}; + struct dispc_errata { bool i2000; /* DSS Does Not Support YUV Pixel Data Formats */ }; @@ -82,9 +88,8 @@ struct dispc_features { const char *vpclk_name[TIDSS_MAX_PORTS]; /* Should match dt clk names */ const enum dispc_vp_bus_type vp_bus_type[TIDSS_MAX_PORTS]; struct tidss_vp_feat vp_feat; - u32 num_planes; - const char *vid_name[TIDSS_MAX_PLANES]; /* Should match dt reg names */ - bool vid_lite[TIDSS_MAX_PLANES]; + u32 num_vids; + struct dispc_vid_info vid_info[TIDSS_MAX_PLANES]; u32 vid_order[TIDSS_MAX_PLANES]; }; diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index f371518f8697..19432c08ec6b 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -115,7 +115,7 @@ static int tidss_dispc_modeset_init(struct tidss_device *tidss) const struct dispc_features *feat = tidss->feat; u32 max_vps = feat->num_vps; - u32 max_planes = feat->num_planes; + u32 max_planes = feat->num_vids; struct pipe pipes[TIDSS_MAX_PORTS]; u32 num_pipes = 0; diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c index 719412e6c346..142ae81951a0 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.c +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -200,7 +200,7 @@ struct tidss_plane *tidss_plane_create(struct tidss_device *tidss, struct tidss_plane *tplane; enum drm_plane_type type; u32 possible_crtcs; - u32 num_planes = tidss->feat->num_planes; + u32 num_planes = tidss->feat->num_vids; u32 color_encodings = (BIT(DRM_COLOR_YCBCR_BT601) | BIT(DRM_COLOR_YCBCR_BT709)); u32 color_ranges = (BIT(DRM_COLOR_YCBCR_FULL_RANGE) | From 46a7c081be700d802741f26d2e9acf1861ee88f1 Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Wed, 7 May 2025 23:36:31 +0530 Subject: [PATCH 037/279] drm/tidss: Add support for AM62L display subsystem Enable display for AM62L DSS [1] which supports only a single display pipeline using a single overlay manager, single video port and a single video lite pipeline which does not support scaling. The output of video port is routed to SoC boundary via DPI interface and the DPI signals from the video port are also routed to DSI Tx controller present within the SoC. [1]: Section 11.7 (Display Subsystem and Peripherals) Link : https://www.ti.com/lit/pdf/sprujb4 Signed-off-by: Devarsh Thakkar Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20250507180631.874930-4-devarsht@ti.com Signed-off-by: Tomi Valkeinen --- drivers/gpu/drm/tidss/tidss_dispc.c | 41 +++++++++++++++++++++++++++++ drivers/gpu/drm/tidss/tidss_dispc.h | 2 ++ drivers/gpu/drm/tidss/tidss_drv.c | 1 + 3 files changed, 44 insertions(+) diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index 774c608c88b5..21363ccbd763 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -440,6 +440,42 @@ const struct dispc_features dispc_am62a7_feats = { .vid_order = {1, 0}, }; +const struct dispc_features dispc_am62l_feats = { + .max_pclk_khz = { + [DISPC_VP_DPI] = 165000, + }, + + .subrev = DISPC_AM62L, + + .common = "common", + .common_regs = tidss_am65x_common_regs, + + .num_vps = 1, + .vp_name = { "vp1" }, + .ovr_name = { "ovr1" }, + .vpclk_name = { "vp1" }, + .vp_bus_type = { DISPC_VP_DPI }, + + .vp_feat = { .color = { + .has_ctm = true, + .gamma_size = 256, + .gamma_type = TIDSS_GAMMA_8BIT, + }, + }, + + .num_vids = 1, + + .vid_info = { + { + .name = "vidl1", + .is_lite = true, + .hw_id = 1, + } + }, + + .vid_order = {0}, +}; + static const u16 *dispc_common_regmap; struct dss_vp_data { @@ -951,6 +987,7 @@ dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc) return dispc_k2g_read_and_clear_irqstatus(dispc); case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: case DISPC_J721E: return dispc_k3_read_and_clear_irqstatus(dispc); @@ -968,6 +1005,7 @@ void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask) break; case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: case DISPC_J721E: dispc_k3_set_irqenable(dispc, mask); @@ -1464,6 +1502,7 @@ void dispc_ovr_set_plane(struct dispc_device *dispc, u32 hw_plane, break; case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: dispc_am65x_ovr_set_plane(dispc, hw_plane, hw_videoport, x, y, layer); @@ -2384,6 +2423,7 @@ static void dispc_plane_init(struct dispc_device *dispc) break; case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: case DISPC_J721E: dispc_k3_plane_init(dispc); @@ -2492,6 +2532,7 @@ static void dispc_vp_write_gamma_table(struct dispc_device *dispc, break; case DISPC_AM625: case DISPC_AM62A7: + case DISPC_AM62L: case DISPC_AM65X: dispc_am65x_vp_write_gamma_table(dispc, hw_videoport); break; diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index 72a0146e57d5..28958514b8f5 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -67,6 +67,7 @@ enum dispc_vp_bus_type { enum dispc_dss_subrevision { DISPC_K2G, DISPC_AM625, + DISPC_AM62L, DISPC_AM62A7, DISPC_AM65X, DISPC_J721E, @@ -96,6 +97,7 @@ struct dispc_features { extern const struct dispc_features dispc_k2g_feats; extern const struct dispc_features dispc_am625_feats; extern const struct dispc_features dispc_am62a7_feats; +extern const struct dispc_features dispc_am62l_feats; extern const struct dispc_features dispc_am65x_feats; extern const struct dispc_features dispc_j721e_feats; diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index d4652e8cc28c..f2a4f659f574 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -242,6 +242,7 @@ static const struct of_device_id tidss_of_table[] = { { .compatible = "ti,k2g-dss", .data = &dispc_k2g_feats, }, { .compatible = "ti,am625-dss", .data = &dispc_am625_feats, }, { .compatible = "ti,am62a7-dss", .data = &dispc_am62a7_feats, }, + { .compatible = "ti,am62l-dss", .data = &dispc_am62l_feats, }, { .compatible = "ti,am65x-dss", .data = &dispc_am65x_feats, }, { .compatible = "ti,j721e-dss", .data = &dispc_j721e_feats, }, { } From a4b4e3fd536763b3405c70ef97a6e7f9af8a00dc Mon Sep 17 00:00:00 2001 From: Ernest Van Hoecke Date: Tue, 20 May 2025 14:43:28 +0200 Subject: [PATCH 038/279] drm/panel-edp: Add support for AUO G156HAN03.0 panel AUO G156HAN03.0 EDID: 00 ff ff ff ff ff ff 00 06 af ed 30 00 00 00 00 1a 1c 01 04 a5 22 13 78 02 05 b5 94 59 59 92 28 1d 50 54 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 78 37 80 b4 70 38 2e 40 6c 30 aa 00 58 c1 10 00 00 18 00 00 00 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 fe 00 41 55 4f 0a 20 20 20 20 20 20 20 20 20 00 00 00 fe 00 47 31 35 36 48 41 4e 30 33 2e 30 20 0a 00 bb Signed-off-by: Ernest Van Hoecke Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20250520124332.71705-1-ernest.vanhoecke@toradex.com --- drivers/gpu/drm/panel/panel-edp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 00dae545832e..9adbe0f11421 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1869,6 +1869,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x235c, &delay_200_500_e50, "B116XTN02.3"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x239b, &delay_200_500_e50, "B116XAN06.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x255c, &delay_200_500_e50, "B116XTN02.5"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x30ed, &delay_200_500_e50, "G156HAN03.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x403d, &delay_200_500_e50, "B140HAN04.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAN04.0"), EDP_PANEL_ENTRY2('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01.0", From fa3769e09be76142d51c617d7d0c72d9c725a49d Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 17 May 2025 04:59:37 +0300 Subject: [PATCH 039/279] drm/bridge: move private data to the end of the struct WHen adding HDMI fields I didn't notice the private: declaration for HPD fields. Move private fields to the end of the struct drm_bride to have clear distinction between private and public fields. Reviewed-by: Maxime Ripard Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250517-drm-hdmi-connector-cec-v6-1-35651db6f19b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- include/drm/drm_bridge.h | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 4e418a29a9ff..286f6fb3fe2b 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -977,21 +977,6 @@ struct drm_bridge { * @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; /** * @vendor: Vendor of the product to be used for the SPD InfoFrame @@ -1043,6 +1028,22 @@ struct drm_bridge { * not used. */ int hdmi_audio_dai_port; + + /** 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 * From d9f9bae6752f5a0280a80d1bc524cabd0d60c886 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 17 May 2025 04:59:38 +0300 Subject: [PATCH 040/279] drm/bridge: allow limiting I2S formats By default HDMI codec registers all formats supported on the I2S bus. Allow bridges (and connectors) to limit the list of the PCM formats supported by the HDMI codec. Reviewed-by: Maxime Ripard Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250517-drm-hdmi-connector-cec-v6-2-35651db6f19b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/display/drm_bridge_connector.c | 1 + drivers/gpu/drm/display/drm_hdmi_audio_helper.c | 3 +++ drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +- include/drm/display/drm_hdmi_audio_helper.h | 1 + include/drm/drm_bridge.h | 8 ++++++++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 7d2e499ea5de..381a0f9d4259 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -717,6 +717,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, ret = drm_connector_hdmi_audio_init(connector, dev, &drm_bridge_connector_hdmi_audio_funcs, bridge->hdmi_audio_max_i2s_playback_channels, + bridge->hdmi_audio_i2s_formats, bridge->hdmi_audio_spdif_playback, bridge->hdmi_audio_dai_port); if (ret) diff --git a/drivers/gpu/drm/display/drm_hdmi_audio_helper.c b/drivers/gpu/drm/display/drm_hdmi_audio_helper.c index 05afc9f0bdd6..21c93bdd8648 100644 --- a/drivers/gpu/drm/display/drm_hdmi_audio_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_audio_helper.c @@ -142,6 +142,7 @@ static const struct hdmi_codec_ops drm_connector_hdmi_audio_ops = { * @hdmi_codec_dev: device to be used as a parent for the HDMI Codec * @funcs: callbacks for this HDMI Codec * @max_i2s_playback_channels: maximum number of playback I2S channels + * @i2s_formats: set of I2S formats (use 0 for a bus-specific set) * @spdif_playback: set if HDMI codec has S/PDIF playback port * @dai_port: sound DAI port, -1 if it is not enabled * @@ -154,6 +155,7 @@ int drm_connector_hdmi_audio_init(struct drm_connector *connector, struct device *hdmi_codec_dev, const struct drm_connector_hdmi_audio_funcs *funcs, unsigned int max_i2s_playback_channels, + u64 i2s_formats, bool spdif_playback, int dai_port) { @@ -161,6 +163,7 @@ int drm_connector_hdmi_audio_init(struct drm_connector *connector, .ops = &drm_connector_hdmi_audio_ops, .max_i2s_channels = max_i2s_playback_channels, .i2s = !!max_i2s_playback_channels, + .i2s_formats = i2s_formats, .spdif = spdif_playback, .no_i2s_capture = true, .no_spdif_capture = true, diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index a29a6ef266f9..4797ed1c21f4 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -562,7 +562,7 @@ static int vc4_hdmi_connector_init(struct drm_device *dev, ret = drm_connector_hdmi_audio_init(connector, dev->dev, &vc4_hdmi_audio_funcs, - 8, false, -1); + 8, 0, false, -1); if (ret) return ret; diff --git a/include/drm/display/drm_hdmi_audio_helper.h b/include/drm/display/drm_hdmi_audio_helper.h index c9a6faef4109..44d910bdc72d 100644 --- a/include/drm/display/drm_hdmi_audio_helper.h +++ b/include/drm/display/drm_hdmi_audio_helper.h @@ -14,6 +14,7 @@ int drm_connector_hdmi_audio_init(struct drm_connector *connector, struct device *hdmi_codec_dev, const struct drm_connector_hdmi_audio_funcs *funcs, unsigned int max_i2s_playback_channels, + u64 i2s_formats, bool spdif_playback, int sound_dai_port); void drm_connector_hdmi_audio_plugged_notify(struct drm_connector *connector, diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 286f6fb3fe2b..db0d374d863b 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1016,6 +1016,14 @@ struct drm_bridge { */ int hdmi_audio_max_i2s_playback_channels; + /** + * @hdmi_audio_i2s_formats: supported I2S formats, optional. The + * default is to allow all formats supported by the corresponding I2S + * bus driver. This is only used for bridges setting + * @DRM_BRIDGE_OP_HDMI_AUDIO or @DRM_BRIDGE_OP_DP_AUDIO. + */ + u64 hdmi_audio_i2s_formats; + /** * @hdmi_audio_spdif_playback: set if this bridge has S/PDIF playback * port for @DRM_BRIDGE_OP_HDMI_AUDIO or @DRM_BRIDGE_OP_DP_AUDIO. From e72cd597c35012146bfe77b736a30fee3e77e61e Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 17 May 2025 04:59:39 +0300 Subject: [PATCH 041/279] drm/connector: add CEC-related fields As a preparation to adding HDMI CEC helper code, add CEC-related fields to the struct drm_connector. The callbacks abstract CEC infrastructure in order to support CEC adapters and CEC notifiers in a universal way. CEC data is a void pointer as it allows us to make CEC data helper-specific. For example, currently it will be either cec_notifier or cec_adapter + drm_connector_hdmi_cec_funcs. Later cec-pin might store platform callbacks here. DP CEC might need to store AUX pointer, etc. Signed-off-by: Dmitry Baryshkov Reviewed-by: Maxime Ripard Link: https://lore.kernel.org/r/20250517-drm-hdmi-connector-cec-v6-3-35651db6f19b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/drm_connector.c | 41 ++++++++++++++++++++++++++++ include/drm/drm_connector.h | 48 +++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 48b08c9611a7..395e1bf006bd 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -279,6 +279,7 @@ static int drm_connector_init_only(struct drm_device *dev, INIT_LIST_HEAD(&connector->probed_modes); INIT_LIST_HEAD(&connector->modes); mutex_init(&connector->mutex); + mutex_init(&connector->cec.mutex); mutex_init(&connector->eld_mutex); mutex_init(&connector->edid_override_mutex); mutex_init(&connector->hdmi.infoframes.lock); @@ -701,6 +702,46 @@ static void drm_mode_remove(struct drm_connector *connector, drm_mode_destroy(connector->dev, mode); } +/** + * drm_connector_cec_phys_addr_invalidate - invalidate CEC physical address + * @connector: connector undergoing CEC operation + * + * Invalidated CEC physical address set for this DRM connector. + */ +void drm_connector_cec_phys_addr_invalidate(struct drm_connector *connector) +{ + mutex_lock(&connector->cec.mutex); + + if (connector->cec.funcs && + connector->cec.funcs->phys_addr_invalidate) + connector->cec.funcs->phys_addr_invalidate(connector); + + mutex_unlock(&connector->cec.mutex); +} +EXPORT_SYMBOL(drm_connector_cec_phys_addr_invalidate); + +/** + * drm_connector_cec_phys_addr_set - propagate CEC physical address + * @connector: connector undergoing CEC operation + * + * Propagate CEC physical address from the display_info to this DRM connector. + */ +void drm_connector_cec_phys_addr_set(struct drm_connector *connector) +{ + u16 addr; + + mutex_lock(&connector->cec.mutex); + + addr = connector->display_info.source_physical_address; + + if (connector->cec.funcs && + connector->cec.funcs->phys_addr_set) + connector->cec.funcs->phys_addr_set(connector, addr); + + mutex_unlock(&connector->cec.mutex); +} +EXPORT_SYMBOL(drm_connector_cec_phys_addr_set); + /** * drm_connector_cleanup - cleans up an initialised connector * @connector: connector to cleanup diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index f13d597370a3..73903c3c842f 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1191,6 +1191,29 @@ struct drm_connector_hdmi_audio_funcs { bool enable, int direction); }; +void drm_connector_cec_phys_addr_invalidate(struct drm_connector *connector); +void drm_connector_cec_phys_addr_set(struct drm_connector *connector); + +/** + * struct drm_connector_cec_funcs - drm_hdmi_connector control functions + */ +struct drm_connector_cec_funcs { + /** + * @phys_addr_invalidate: mark CEC physical address as invalid + * + * The callback to mark CEC physical address as invalid, abstracting + * the operation. + */ + void (*phys_addr_invalidate)(struct drm_connector *connector); + + /** + * @phys_addr_set: set CEC physical address + * + * The callback to set CEC physical address, abstracting the operation. + */ + void (*phys_addr_set)(struct drm_connector *connector, u16 addr); +}; + /** * struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions */ @@ -1832,6 +1855,26 @@ struct drm_connector_hdmi { } infoframes; }; +/** + * struct drm_connector_cec - DRM Connector CEC-related structure + */ +struct drm_connector_cec { + /** + * @mutex: protects all fields in this structure. + */ + struct mutex mutex; + + /** + * @funcs: CEC Control Functions + */ + const struct drm_connector_cec_funcs *funcs; + + /** + * @data: CEC implementation-specific data + */ + void *data; +}; + /** * struct drm_connector - central DRM connector control structure * @@ -2253,6 +2296,11 @@ struct drm_connector { * @hdmi_audio: HDMI codec properties and non-DRM state. */ struct drm_connector_hdmi_audio hdmi_audio; + + /** + * @cec: CEC-related data. + */ + struct drm_connector_cec cec; }; #define obj_to_connector(x) container_of(x, struct drm_connector, base) From bcc8553b6228d0387ff64978a03efa3c8983dd2f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 17 May 2025 04:59:40 +0300 Subject: [PATCH 042/279] drm/display: move CEC_CORE selection to DRM_DISPLAY_HELPER THe Kconfig symbol DRM_DISPLAY_DP_AUX_CEC is a boolean which simply toggles whether DP_AUX_CEC support should be built into the drm_display_helper (which can be eithera module or built-in into the kernel). If DRM_DISPLAY_DP_AUX_CEC is selected, then CEC_CORE is selected to be built-in into the kernel even if DRM_DISPLAY_HELPER is selected to be built as a module. Move CEC_CORE selection to the latter symbol in order to allow it to be built as a module. Signed-off-by: Dmitry Baryshkov Reviewed-by: Maxime Ripard Link: https://lore.kernel.org/r/20250517-drm-hdmi-connector-cec-v6-4-35651db6f19b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/display/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig index 8d22b7627d41..3666e791d6d6 100644 --- a/drivers/gpu/drm/display/Kconfig +++ b/drivers/gpu/drm/display/Kconfig @@ -8,6 +8,7 @@ config DRM_DISPLAY_DP_AUX_BUS config DRM_DISPLAY_HELPER tristate depends on DRM + select CEC_CORE if DRM_DISPLAY_DP_AUX_CEC help DRM helpers for display adapters. @@ -23,7 +24,6 @@ config DRM_BRIDGE_CONNECTOR config DRM_DISPLAY_DP_AUX_CEC bool "Enable DisplayPort CEC-Tunneling-over-AUX HDMI support" select DRM_DISPLAY_DP_HELPER - select CEC_CORE help Choose this option if you want to enable HDMI CEC support for DisplayPort/USB-C to HDMI adapters. From 8b1a8f8b2002d31136d83e4d730b4cb41e9ee868 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 17 May 2025 04:59:41 +0300 Subject: [PATCH 043/279] drm/display: add CEC helpers code Add generic CEC helpers to be used by HDMI drivers. Both notifier and and adapter are supported for registration. Once registered, the driver can call common set of functions to update physical address, to invalidate it or to unregister CEC data. Unlike drm_connector_cec_funcs (which provides interface common to all implementations, including, but not limited to the CEC adapter, CEC notifier, CEC pin-based adapter, etc) the struct drm_connector_hdmi_cec_adapter_ops provides callbacks specific to the CEC adapter implementations. Signed-off-by: Dmitry Baryshkov Reviewed-by: Maxime Ripard Link: https://lore.kernel.org/r/20250517-drm-hdmi-connector-cec-v6-5-35651db6f19b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/display/Kconfig | 12 +- drivers/gpu/drm/display/Makefile | 4 + drivers/gpu/drm/display/drm_hdmi_cec_helper.c | 192 ++++++++++++++++++ .../display/drm_hdmi_cec_notifier_helper.c | 64 ++++++ include/drm/display/drm_hdmi_cec_helper.h | 72 +++++++ 5 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/display/drm_hdmi_cec_helper.c create mode 100644 drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c create mode 100644 include/drm/display/drm_hdmi_cec_helper.h diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig index 3666e791d6d6..6376ea01ec30 100644 --- a/drivers/gpu/drm/display/Kconfig +++ b/drivers/gpu/drm/display/Kconfig @@ -8,7 +8,7 @@ config DRM_DISPLAY_DP_AUX_BUS config DRM_DISPLAY_HELPER tristate depends on DRM - select CEC_CORE if DRM_DISPLAY_DP_AUX_CEC + select CEC_CORE if DRM_DISPLAY_DP_AUX_CEC || DRM_DISPLAY_HDMI_CEC_HELPER || CEC_NOTIFIER help DRM helpers for display adapters. @@ -82,6 +82,16 @@ config DRM_DISPLAY_HDMI_AUDIO_HELPER DRM display helpers for HDMI Audio functionality (generic HDMI Codec implementation). +config DRM_DISPLAY_HDMI_CEC_HELPER + bool + help + DRM display helpers for HDMI CEC implementation. + +config DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER + def_bool CEC_NOTIFIER + help + DRM display helpers for HDMI CEC notifiers implementation. + config DRM_DISPLAY_HDMI_HELPER bool help diff --git a/drivers/gpu/drm/display/Makefile b/drivers/gpu/drm/display/Makefile index b17879b957d5..0ff4a1ad0222 100644 --- a/drivers/gpu/drm/display/Makefile +++ b/drivers/gpu/drm/display/Makefile @@ -16,6 +16,10 @@ drm_display_helper-$(CONFIG_DRM_DISPLAY_DSC_HELPER) += \ drm_display_helper-$(CONFIG_DRM_DISPLAY_HDCP_HELPER) += drm_hdcp_helper.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_AUDIO_HELPER) += \ drm_hdmi_audio_helper.o +drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_CEC_HELPER) += \ + drm_hdmi_cec_helper.o +drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER) += \ + drm_hdmi_cec_notifier_helper.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_HELPER) += \ drm_hdmi_helper.o \ drm_scdc_helper.o diff --git a/drivers/gpu/drm/display/drm_hdmi_cec_helper.c b/drivers/gpu/drm/display/drm_hdmi_cec_helper.c new file mode 100644 index 000000000000..a25f60509043 --- /dev/null +++ b/drivers/gpu/drm/display/drm_hdmi_cec_helper.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2024 Linaro Ltd + */ + +#include +#include +#include +#include + +#include + +#include + +struct drm_connector_hdmi_cec_data { + struct cec_adapter *adapter; + const struct drm_connector_hdmi_cec_funcs *funcs; +}; + +static int drm_connector_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct drm_connector *connector = cec_get_drvdata(adap); + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + return data->funcs->enable(connector, enable); +} + +static int drm_connector_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) +{ + struct drm_connector *connector = cec_get_drvdata(adap); + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + return data->funcs->log_addr(connector, logical_addr); +} + +static int drm_connector_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct drm_connector *connector = cec_get_drvdata(adap); + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + return data->funcs->transmit(connector, attempts, signal_free_time, msg); +} + +static const struct cec_adap_ops drm_connector_hdmi_cec_adap_ops = { + .adap_enable = drm_connector_hdmi_cec_adap_enable, + .adap_log_addr = drm_connector_hdmi_cec_adap_log_addr, + .adap_transmit = drm_connector_hdmi_cec_adap_transmit, +}; + +static void drm_connector_hdmi_cec_adapter_phys_addr_invalidate(struct drm_connector *connector) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_phys_addr_invalidate(data->adapter); +} + +static void drm_connector_hdmi_cec_adapter_phys_addr_set(struct drm_connector *connector, + u16 addr) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_s_phys_addr(data->adapter, addr, false); +} + +static void drm_connector_hdmi_cec_adapter_unregister(struct drm_device *dev, void *res) +{ + struct drm_connector *connector = res; + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_delete_adapter(data->adapter); + + if (data->funcs->uninit) + data->funcs->uninit(connector); + + kfree(data); + connector->cec.data = NULL; +} + +static struct drm_connector_cec_funcs drm_connector_hdmi_cec_adapter_funcs = { + .phys_addr_invalidate = drm_connector_hdmi_cec_adapter_phys_addr_invalidate, + .phys_addr_set = drm_connector_hdmi_cec_adapter_phys_addr_set, +}; + +int drmm_connector_hdmi_cec_register(struct drm_connector *connector, + const struct drm_connector_hdmi_cec_funcs *funcs, + const char *name, + u8 available_las, + struct device *dev) +{ + struct drm_connector_hdmi_cec_data *data; + struct cec_connector_info conn_info; + struct cec_adapter *cec_adap; + int ret; + + if (!funcs->init || !funcs->enable || !funcs->log_addr || !funcs->transmit) + return -EINVAL; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->funcs = funcs; + + cec_adap = cec_allocate_adapter(&drm_connector_hdmi_cec_adap_ops, connector, name, + CEC_CAP_DEFAULTS | CEC_CAP_CONNECTOR_INFO, + available_las ? : CEC_MAX_LOG_ADDRS); + ret = PTR_ERR_OR_ZERO(cec_adap); + if (ret < 0) + goto err_free; + + cec_fill_conn_info_from_drm(&conn_info, connector); + cec_s_conn_info(cec_adap, &conn_info); + + data->adapter = cec_adap; + + mutex_lock(&connector->cec.mutex); + + connector->cec.data = data; + connector->cec.funcs = &drm_connector_hdmi_cec_adapter_funcs; + + ret = funcs->init(connector); + if (ret < 0) + goto err_delete_adapter; + + /* + * NOTE: the CEC adapter will be unregistered by drmm cleanup from + * drm_managed_release(), which is called from drm_dev_release() + * during device unbind. + * + * However, the CEC framework cleans up the CEC adapter only when the + * last user has closed its file descriptor, so we don't need to handle + * it in DRM. + * + * Before that CEC framework makes sure that even if the userspace + * still holds CEC device open, all calls will be shortcut via + * cec_is_registered(), making sure that there is no access to the + * freed memory. + */ + ret = cec_register_adapter(cec_adap, dev); + if (ret < 0) + goto err_delete_adapter; + + mutex_unlock(&connector->cec.mutex); + + return drmm_add_action_or_reset(connector->dev, + drm_connector_hdmi_cec_adapter_unregister, + connector); + +err_delete_adapter: + cec_delete_adapter(cec_adap); + + connector->cec.data = NULL; + + mutex_unlock(&connector->cec.mutex); + +err_free: + kfree(data); + + return ret; +} +EXPORT_SYMBOL(drmm_connector_hdmi_cec_register); + +void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector, + struct cec_msg *msg) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_received_msg(data->adapter, msg); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_received_msg); + +void drm_connector_hdmi_cec_transmit_attempt_done(struct drm_connector *connector, + u8 status) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_transmit_attempt_done(data->adapter, status); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_attempt_done); + +void drm_connector_hdmi_cec_transmit_done(struct drm_connector *connector, + u8 status, + u8 arb_lost_cnt, u8 nack_cnt, + u8 low_drive_cnt, u8 error_cnt) +{ + struct drm_connector_hdmi_cec_data *data = connector->cec.data; + + cec_transmit_done(data->adapter, status, + arb_lost_cnt, nack_cnt, low_drive_cnt, error_cnt); +} +EXPORT_SYMBOL(drm_connector_hdmi_cec_transmit_done); diff --git a/drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c b/drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c new file mode 100644 index 000000000000..28f8e008cc59 --- /dev/null +++ b/drivers/gpu/drm/display/drm_hdmi_cec_notifier_helper.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2024 Linaro Ltd + */ + +#include +#include +#include +#include + +#include + +#include +#include + +static void drm_connector_hdmi_cec_notifier_phys_addr_invalidate(struct drm_connector *connector) +{ + cec_notifier_phys_addr_invalidate(connector->cec.data); +} + +static void drm_connector_hdmi_cec_notifier_phys_addr_set(struct drm_connector *connector, + u16 addr) +{ + cec_notifier_set_phys_addr(connector->cec.data, addr); +} + +static void drm_connector_hdmi_cec_notifier_unregister(struct drm_device *dev, void *res) +{ + struct drm_connector *connector = res; + + cec_notifier_conn_unregister(connector->cec.data); + connector->cec.data = NULL; +} + +static const struct drm_connector_cec_funcs drm_connector_cec_notifier_funcs = { + .phys_addr_invalidate = drm_connector_hdmi_cec_notifier_phys_addr_invalidate, + .phys_addr_set = drm_connector_hdmi_cec_notifier_phys_addr_set, +}; + +int drmm_connector_hdmi_cec_notifier_register(struct drm_connector *connector, + const char *port_name, + struct device *dev) +{ + struct cec_connector_info conn_info; + struct cec_notifier *notifier; + + cec_fill_conn_info_from_drm(&conn_info, connector); + + notifier = cec_notifier_conn_register(dev, port_name, &conn_info); + if (!notifier) + return -ENOMEM; + + mutex_lock(&connector->cec.mutex); + + connector->cec.data = notifier; + connector->cec.funcs = &drm_connector_cec_notifier_funcs; + + mutex_unlock(&connector->cec.mutex); + + return drmm_add_action_or_reset(connector->dev, + drm_connector_hdmi_cec_notifier_unregister, + connector); +} +EXPORT_SYMBOL(drmm_connector_hdmi_cec_notifier_register); diff --git a/include/drm/display/drm_hdmi_cec_helper.h b/include/drm/display/drm_hdmi_cec_helper.h new file mode 100644 index 000000000000..fd8f4d2f02c1 --- /dev/null +++ b/include/drm/display/drm_hdmi_cec_helper.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_DISPLAY_HDMI_CEC_HELPER +#define DRM_DISPLAY_HDMI_CEC_HELPER + +#include + +struct drm_connector; + +struct cec_msg; +struct device; + +struct drm_connector_hdmi_cec_funcs { + /** + * @init: perform hardware-specific initialization before registering the CEC adapter + */ + int (*init)(struct drm_connector *connector); + + /** + * @uninit: perform hardware-specific teardown for the CEC adapter + */ + void (*uninit)(struct drm_connector *connector); + + /** + * @enable: enable or disable CEC adapter + */ + int (*enable)(struct drm_connector *connector, bool enable); + + /** + * @log_addr: set adapter's logical address, can be called multiple + * times if adapter supports several LAs + */ + int (*log_addr)(struct drm_connector *connector, u8 logical_addr); + + /** + * @transmit: start transmission of the specified CEC message + */ + int (*transmit)(struct drm_connector *connector, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); +}; + +int drmm_connector_hdmi_cec_register(struct drm_connector *connector, + const struct drm_connector_hdmi_cec_funcs *funcs, + const char *name, + u8 available_las, + struct device *dev); + +void drm_connector_hdmi_cec_received_msg(struct drm_connector *connector, + struct cec_msg *msg); + +void drm_connector_hdmi_cec_transmit_done(struct drm_connector *connector, + u8 status, + u8 arb_lost_cnt, u8 nack_cnt, + u8 low_drive_cnt, u8 error_cnt); + +void drm_connector_hdmi_cec_transmit_attempt_done(struct drm_connector *connector, + u8 status); + +#if IS_ENABLED(CONFIG_DRM_DISPLAY_HDMI_CEC_NOTIFIER_HELPER) +int drmm_connector_hdmi_cec_notifier_register(struct drm_connector *connector, + const char *port_name, + struct device *dev); +#else +static inline int drmm_connector_hdmi_cec_notifier_register(struct drm_connector *connector, + const char *port_name, + struct device *dev) +{ + return 0; +} +#endif + +#endif From 603ce85427043ecb29ef737c1b350901ce3ebf09 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 17 May 2025 04:59:42 +0300 Subject: [PATCH 044/279] drm/display: hdmi-state-helper: handle CEC physical address Call HDMI CEC helpers in order to update physical address of the adapter. Reviewed-by: Maxime Ripard Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250517-drm-hdmi-connector-cec-v6-6-35651db6f19b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/display/drm_hdmi_state_helper.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index d9d9948b29e9..bae7aa624f7d 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -1081,9 +1082,10 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector, const struct drm_edid *drm_edid; if (status == connector_status_disconnected) { - // TODO: also handle CEC and scramber, HDMI sink disconnected. + // TODO: also handle scramber, HDMI sink disconnected. drm_connector_hdmi_audio_plugged_notify(connector, false); drm_edid_connector_update(connector, NULL); + drm_connector_cec_phys_addr_invalidate(connector); return; } @@ -1097,8 +1099,9 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector, drm_edid_free(drm_edid); if (status == connector_status_connected) { - // TODO: also handle CEC and scramber, HDMI sink is now connected. + // TODO: also handle scramber, HDMI sink is now connected. drm_connector_hdmi_audio_plugged_notify(connector, true); + drm_connector_cec_phys_addr_set(connector); } } From 65a2575a68e4ff03ba887b5aef679fc95405fcd2 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 17 May 2025 04:59:44 +0300 Subject: [PATCH 045/279] drm/display: bridge-connector: hook in CEC notifier support Allow HDMI DRM bridges to create CEC notifier. Physical address is handled automatically by drm_atomic_helper_connector_hdmi_hotplug() being called from .detect() path. Signed-off-by: Dmitry Baryshkov Reviewed-by: Maxime Ripard Link: https://lore.kernel.org/r/20250517-drm-hdmi-connector-cec-v6-8-35651db6f19b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- .../gpu/drm/display/drm_bridge_connector.c | 24 +++++++++++++++++++ include/drm/drm_bridge.h | 11 +++++++++ 2 files changed, 35 insertions(+) diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 381a0f9d4259..0377dcd691a8 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -113,6 +114,13 @@ struct drm_bridge_connector { * &DRM_BRIDGE_OP_DP_AUDIO). */ struct drm_bridge *bridge_dp_audio; + /** + * @bridge_hdmi_cec: + * + * The bridge in the chain that implements CEC support, if any (see + * DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER). + */ + struct drm_bridge *bridge_hdmi_cec; }; #define to_drm_bridge_connector(x) \ @@ -662,6 +670,13 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, bridge_connector->bridge_dp_audio = bridge; } + if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { + if (bridge_connector->bridge_hdmi_cec) + return ERR_PTR(-EBUSY); + + bridge_connector->bridge_hdmi_cec = bridge; + } + if (!drm_bridge_get_next_bridge(bridge)) connector_type = bridge->type; @@ -724,6 +739,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return ERR_PTR(ret); } + if (bridge_connector->bridge_hdmi_cec && + bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { + ret = drmm_connector_hdmi_cec_notifier_register(connector, + NULL, + bridge->hdmi_cec_dev); + if (ret) + return ERR_PTR(ret); + } + drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs); if (bridge_connector->bridge_hpd) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index db0d374d863b..0e5f6a007d53 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -907,6 +907,11 @@ enum drm_bridge_ops { * flag. */ DRM_BRIDGE_OP_DP_AUDIO = BIT(6), + /** + * @DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER: The bridge requires CEC notifier + * to be present. + */ + DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER = BIT(7), }; /** @@ -1003,6 +1008,12 @@ struct drm_bridge { */ unsigned int max_bpc; + /** + * @hdmi_cec_dev: device to be used as a containing device for CEC + * functions. + */ + struct device *hdmi_cec_dev; + /** * @hdmi_audio_dev: device to be used as a parent for the HDMI Codec if * either of @DRM_BRIDGE_OP_HDMI_AUDIO or @DRM_BRIDGE_OP_DP_AUDIO is set. From a74288c8ded7c34624e50b4aa8ca37ae6cc03df4 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 17 May 2025 04:59:45 +0300 Subject: [PATCH 046/279] drm/display: bridge-connector: handle CEC adapters Implement necessary glue code to let DRM bridge drivers to implement CEC adapters support. Signed-off-by: Dmitry Baryshkov Reviewed-by: Maxime Ripard Link: https://lore.kernel.org/r/20250517-drm-hdmi-connector-cec-v6-9-35651db6f19b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/display/Kconfig | 1 + .../gpu/drm/display/drm_bridge_connector.c | 82 +++++++++++++++++++ include/drm/drm_bridge.h | 26 ++++++ 3 files changed, 109 insertions(+) diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig index 6376ea01ec30..df09cf9a8ca1 100644 --- a/drivers/gpu/drm/display/Kconfig +++ b/drivers/gpu/drm/display/Kconfig @@ -17,6 +17,7 @@ if DRM_DISPLAY_HELPER config DRM_BRIDGE_CONNECTOR bool select DRM_DISPLAY_HDMI_AUDIO_HELPER + select DRM_DISPLAY_HDMI_CEC_HELPER select DRM_DISPLAY_HDMI_STATE_HELPER help DRM connector implementation terminating DRM bridge chains. diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 0377dcd691a8..58846e26f1e1 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -554,6 +554,65 @@ static const struct drm_connector_hdmi_audio_funcs drm_bridge_connector_hdmi_aud .mute_stream = drm_bridge_connector_audio_mute_stream, }; +static int drm_bridge_connector_hdmi_cec_enable(struct drm_connector *connector, bool enable) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi_cec; + + return bridge->funcs->hdmi_cec_enable(bridge, enable); +} + +static int drm_bridge_connector_hdmi_cec_log_addr(struct drm_connector *connector, u8 logical_addr) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi_cec; + + return bridge->funcs->hdmi_cec_log_addr(bridge, logical_addr); +} + +static int drm_bridge_connector_hdmi_cec_transmit(struct drm_connector *connector, + u8 attempts, + u32 signal_free_time, + struct cec_msg *msg) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi_cec; + + return bridge->funcs->hdmi_cec_transmit(bridge, attempts, + signal_free_time, + msg); +} + +static int drm_bridge_connector_hdmi_cec_init(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi_cec; + + if (!bridge->funcs->hdmi_cec_init) + return 0; + + return bridge->funcs->hdmi_cec_init(connector, bridge); +} + +static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_funcs = { + .init = drm_bridge_connector_hdmi_cec_init, + .enable = drm_bridge_connector_hdmi_cec_enable, + .log_addr = drm_bridge_connector_hdmi_cec_log_addr, + .transmit = drm_bridge_connector_hdmi_cec_transmit, +}; + /* ----------------------------------------------------------------------------- * Bridge Connector Initialisation */ @@ -677,6 +736,18 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, bridge_connector->bridge_hdmi_cec = bridge; } + if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { + if (bridge_connector->bridge_hdmi_cec) + return ERR_PTR(-EBUSY); + + bridge_connector->bridge_hdmi_cec = bridge; + + if (!bridge->funcs->hdmi_cec_enable || + !bridge->funcs->hdmi_cec_log_addr || + !bridge->funcs->hdmi_cec_transmit) + return ERR_PTR(-EINVAL); + } + if (!drm_bridge_get_next_bridge(bridge)) connector_type = bridge->type; @@ -748,6 +819,17 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return ERR_PTR(ret); } + if (bridge_connector->bridge_hdmi_cec && + bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { + ret = drmm_connector_hdmi_cec_register(connector, + &drm_bridge_connector_hdmi_cec_funcs, + bridge->hdmi_cec_adapter_name, + bridge->hdmi_cec_available_las, + bridge->hdmi_cec_dev); + if (ret) + return ERR_PTR(ret); + } + drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs); if (bridge_connector->bridge_hpd) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 0e5f6a007d53..cc9f7df38102 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -32,6 +32,7 @@ #include #include +struct cec_msg; struct device_node; struct drm_bridge; @@ -737,6 +738,16 @@ struct drm_bridge_funcs { struct drm_bridge *bridge, bool enable, int direction); + int (*hdmi_cec_init)(struct drm_connector *connector, + struct drm_bridge *bridge); + + int (*hdmi_cec_enable)(struct drm_bridge *bridge, bool enable); + + int (*hdmi_cec_log_addr)(struct drm_bridge *bridge, u8 logical_addr); + + int (*hdmi_cec_transmit)(struct drm_bridge *bridge, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); + /** * @dp_audio_startup: * @@ -912,6 +923,11 @@ enum drm_bridge_ops { * to be present. */ DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER = BIT(7), + /** + * @DRM_BRIDGE_OP_HDMI_CEC_ADAPTER: The bridge requires CEC notifier + * to be present. + */ + DRM_BRIDGE_OP_HDMI_CEC_ADAPTER = BIT(8), }; /** @@ -1048,6 +1064,16 @@ struct drm_bridge { */ int hdmi_audio_dai_port; + /** + * @hdmi_cec_adapter_name: the name of the adapter to register + */ + const char *hdmi_cec_adapter_name; + + /** + * @hdmi_cec_available_las: number of logical addresses, CEC_MAX_LOG_ADDRS if unset + */ + u8 hdmi_cec_available_las; + /** private: */ /** * @hpd_mutex: Protects the @hpd_cb and @hpd_data fields. From ae01d3183d2763ed27ab71f4ef5402b683d9ad8a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 17 May 2025 04:59:46 +0300 Subject: [PATCH 047/279] drm/bridge: adv7511: switch to the HDMI connector helpers Rewrite the ADV7511 driver to use implementation provided by the DRM HDMI connector framework, including the Audio and CEC bits. Drop the in-bridge connector support and use drm_bridge_connector if the host requires the connector to be provided by the bridge. Note: currently only AVI InfoFrames are supported. Existing driver doesn't support programming any other InfoFrames directly and Audio InfoFrame seems to be programmed using individual bits and pieces rather than programming it directly. Signed-off-by: Dmitry Baryshkov Reviewed-by: Maxime Ripard Link: https://lore.kernel.org/r/20250517-drm-hdmi-connector-cec-v6-10-35651db6f19b@oss.qualcomm.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/bridge/adv7511/Kconfig | 5 +- drivers/gpu/drm/bridge/adv7511/adv7511.h | 52 +-- .../gpu/drm/bridge/adv7511/adv7511_audio.c | 77 +--- drivers/gpu/drm/bridge/adv7511/adv7511_cec.c | 57 ++- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 345 ++++++++---------- drivers/gpu/drm/bridge/adv7511/adv7533.c | 4 - 6 files changed, 212 insertions(+), 328 deletions(-) diff --git a/drivers/gpu/drm/bridge/adv7511/Kconfig b/drivers/gpu/drm/bridge/adv7511/Kconfig index f46a5e26b5dd..59a5256ce8a6 100644 --- a/drivers/gpu/drm/bridge/adv7511/Kconfig +++ b/drivers/gpu/drm/bridge/adv7511/Kconfig @@ -5,6 +5,9 @@ config DRM_I2C_ADV7511 select DRM_KMS_HELPER select REGMAP_I2C select DRM_MIPI_DSI + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR + select DRM_DISPLAY_HDMI_STATE_HELPER help Support for the Analog Devices ADV7511(W)/13/33/35 HDMI encoders. @@ -19,7 +22,7 @@ config DRM_I2C_ADV7511_AUDIO config DRM_I2C_ADV7511_CEC bool "ADV7511/33/35 HDMI CEC driver" depends on DRM_I2C_ADV7511 - select CEC_CORE + select DRM_DISPLAY_HDMI_CEC_HELPER default y help When selected the HDMI transmitter will support the CEC feature. diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index ec0b7f3d889c..0ecce9e4947a 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -313,16 +313,11 @@ enum adv7511_csc_scaling { * @csc_enable: Whether to enable color space conversion * @csc_scaling_factor: Color space conversion scaling factor * @csc_coefficents: Color space conversion coefficents - * @hdmi_mode: Whether to use HDMI or DVI output mode - * @avi_infoframe: HDMI infoframe */ struct adv7511_video_config { bool csc_enable; enum adv7511_csc_scaling csc_scaling_factor; const uint16_t *csc_coefficents; - - bool hdmi_mode; - struct hdmi_avi_infoframe avi_infoframe; }; enum adv7511_type { @@ -337,6 +332,7 @@ struct adv7511_chip_info { enum adv7511_type type; unsigned int max_mode_clock_khz; unsigned int max_lane_freq_khz; + const char *name; const char * const *supply_names; unsigned int num_supplies; unsigned int reg_cec_offset; @@ -371,7 +367,7 @@ struct adv7511 { struct work_struct hpd_work; struct drm_bridge bridge; - struct drm_connector connector; + struct drm_connector *cec_connector; bool embedded_sync; enum adv7511_sync_polarity vsync_polarity; @@ -389,9 +385,7 @@ struct adv7511 { bool use_timing_gen; const struct adv7511_chip_info *info; - struct platform_device *audio_pdev; - struct cec_adapter *cec_adap; u8 cec_addr[ADV7511_MAX_ADDRS]; u8 cec_valid_addrs; bool cec_enabled_adap; @@ -399,16 +393,24 @@ struct adv7511 { u32 cec_clk_freq; }; +static inline struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge) +{ + return container_of(bridge, struct adv7511, bridge); +} + #ifdef CONFIG_DRM_I2C_ADV7511_CEC -int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511); +int adv7511_cec_init(struct drm_connector *connector, + struct drm_bridge *bridge); +int adv7511_cec_enable(struct drm_bridge *bridge, bool enable); +int adv7511_cec_log_addr(struct drm_bridge *bridge, u8 addr); +int adv7511_cec_transmit(struct drm_bridge *bridge, u8 attempts, + u32 signal_free_time, struct cec_msg *msg); int adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1); #else -static inline int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) -{ - regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, - ADV7511_CEC_CTRL_POWER_DOWN); - return 0; -} +#define adv7511_cec_init NULL +#define adv7511_cec_enable NULL +#define adv7511_cec_log_addr NULL +#define adv7511_cec_transmit NULL #endif void adv7533_dsi_power_on(struct adv7511 *adv); @@ -421,16 +423,18 @@ int adv7533_attach_dsi(struct adv7511 *adv); int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv); #ifdef CONFIG_DRM_I2C_ADV7511_AUDIO -int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511); -void adv7511_audio_exit(struct adv7511 *adv7511); +int adv7511_hdmi_audio_startup(struct drm_connector *connector, + struct drm_bridge *bridge); +void adv7511_hdmi_audio_shutdown(struct drm_connector *connector, + struct drm_bridge *bridge); +int adv7511_hdmi_audio_prepare(struct drm_connector *connector, + struct drm_bridge *bridge, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms); #else /*CONFIG_DRM_I2C_ADV7511_AUDIO */ -static inline int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511) -{ - return 0; -} -static inline void adv7511_audio_exit(struct adv7511 *adv7511) -{ -} +#define adv7511_hdmi_audio_startup NULL +#define adv7511_hdmi_audio_shutdown NULL +#define adv7511_hdmi_audio_prepare NULL #endif /* CONFIG_DRM_I2C_ADV7511_AUDIO */ #endif /* __DRM_I2C_ADV7511_H__ */ diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c index 1ff8c815ec79..915c3b967216 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_audio.c @@ -55,11 +55,12 @@ static int adv7511_update_cts_n(struct adv7511 *adv7511) return 0; } -static int adv7511_hdmi_hw_params(struct device *dev, void *data, - struct hdmi_codec_daifmt *fmt, - struct hdmi_codec_params *hparms) +int adv7511_hdmi_audio_prepare(struct drm_connector *connector, + struct drm_bridge *bridge, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms) { - struct adv7511 *adv7511 = dev_get_drvdata(dev); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); unsigned int audio_source, i2s_format = 0; unsigned int invert_clock; unsigned int rate; @@ -167,9 +168,10 @@ static int adv7511_hdmi_hw_params(struct device *dev, void *data, return 0; } -static int audio_startup(struct device *dev, void *data) +int adv7511_hdmi_audio_startup(struct drm_connector *connector, + struct drm_bridge *bridge) { - struct adv7511 *adv7511 = dev_get_drvdata(dev); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(7), 0); @@ -204,69 +206,12 @@ static int audio_startup(struct device *dev, void *data) return 0; } -static void audio_shutdown(struct device *dev, void *data) +void adv7511_hdmi_audio_shutdown(struct drm_connector *connector, + struct drm_bridge *bridge) { - struct adv7511 *adv7511 = dev_get_drvdata(dev); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); if (adv7511->audio_source == ADV7511_AUDIO_SOURCE_SPDIF) regmap_update_bits(adv7511->regmap, ADV7511_REG_AUDIO_CONFIG, BIT(7), 0); } - -static int adv7511_hdmi_i2s_get_dai_id(struct snd_soc_component *component, - struct device_node *endpoint, - void *data) -{ - struct of_endpoint of_ep; - int ret; - - ret = of_graph_parse_endpoint(endpoint, &of_ep); - if (ret < 0) - return ret; - - /* - * HDMI sound should be located as reg = <2> - * Then, it is sound port 0 - */ - if (of_ep.port == 2) - return 0; - - return -EINVAL; -} - -static const struct hdmi_codec_ops adv7511_codec_ops = { - .hw_params = adv7511_hdmi_hw_params, - .audio_shutdown = audio_shutdown, - .audio_startup = audio_startup, - .get_dai_id = adv7511_hdmi_i2s_get_dai_id, -}; - -static const struct hdmi_codec_pdata codec_data = { - .ops = &adv7511_codec_ops, - .i2s_formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE), - .max_i2s_channels = 2, - .i2s = 1, - .no_i2s_capture = 1, - .spdif = 1, - .no_spdif_capture = 1, -}; - -int adv7511_audio_init(struct device *dev, struct adv7511 *adv7511) -{ - adv7511->audio_pdev = platform_device_register_data(dev, - HDMI_CODEC_DRV_NAME, - PLATFORM_DEVID_AUTO, - &codec_data, - sizeof(codec_data)); - return PTR_ERR_OR_ZERO(adv7511->audio_pdev); -} - -void adv7511_audio_exit(struct adv7511 *adv7511) -{ - if (adv7511->audio_pdev) { - platform_device_unregister(adv7511->audio_pdev); - adv7511->audio_pdev = NULL; - } -} diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c index 2e9c88a2b5ed..822265426f58 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c @@ -12,6 +12,8 @@ #include +#include + #include "adv7511.h" static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = { @@ -44,8 +46,8 @@ static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status) return; if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) { - cec_transmit_attempt_done(adv7511->cec_adap, - CEC_TX_STATUS_ARB_LOST); + drm_connector_hdmi_cec_transmit_attempt_done(adv7511->cec_connector, + CEC_TX_STATUS_ARB_LOST); return; } if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) { @@ -72,12 +74,14 @@ static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status) if (low_drive_cnt) status |= CEC_TX_STATUS_LOW_DRIVE; } - cec_transmit_done(adv7511->cec_adap, status, - 0, nack_cnt, low_drive_cnt, err_cnt); + drm_connector_hdmi_cec_transmit_done(adv7511->cec_connector, status, + 0, nack_cnt, low_drive_cnt, + err_cnt); return; } if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) { - cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK); + drm_connector_hdmi_cec_transmit_attempt_done(adv7511->cec_connector, + CEC_TX_STATUS_OK); return; } } @@ -116,7 +120,7 @@ static void adv7511_cec_rx(struct adv7511 *adv7511, int rx_buf) regmap_update_bits(adv7511->regmap_cec, ADV7511_REG_CEC_RX_BUFFERS + offset, BIT(rx_buf), 0); - cec_received_msg(adv7511->cec_adap, &msg); + drm_connector_hdmi_cec_received_msg(adv7511->cec_connector, &msg); } int adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) @@ -179,9 +183,9 @@ int adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) return IRQ_HANDLED; } -static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) +int adv7511_cec_enable(struct drm_bridge *bridge, bool enable) { - struct adv7511 *adv7511 = cec_get_drvdata(adap); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); unsigned int offset = adv7511->info->reg_cec_offset; if (adv7511->i2c_cec == NULL) @@ -225,9 +229,9 @@ static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) return 0; } -static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) +int adv7511_cec_log_addr(struct drm_bridge *bridge, u8 addr) { - struct adv7511 *adv7511 = cec_get_drvdata(adap); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); unsigned int offset = adv7511->info->reg_cec_offset; unsigned int i, free_idx = ADV7511_MAX_ADDRS; @@ -293,10 +297,10 @@ static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) return 0; } -static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, - u32 signal_free_time, struct cec_msg *msg) +int adv7511_cec_transmit(struct drm_bridge *bridge, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) { - struct adv7511 *adv7511 = cec_get_drvdata(adap); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); unsigned int offset = adv7511->info->reg_cec_offset; u8 len = msg->len; unsigned int i; @@ -328,12 +332,6 @@ static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, return 0; } -static const struct cec_adap_ops adv7511_cec_adap_ops = { - .adap_enable = adv7511_cec_adap_enable, - .adap_log_addr = adv7511_cec_adap_log_addr, - .adap_transmit = adv7511_cec_adap_transmit, -}; - static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511) { adv7511->cec_clk = devm_clk_get(dev, "cec"); @@ -348,20 +346,18 @@ static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511) return 0; } -int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) +int adv7511_cec_init(struct drm_connector *connector, + struct drm_bridge *bridge) { + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); + struct device *dev = &adv7511->i2c_main->dev; unsigned int offset = adv7511->info->reg_cec_offset; int ret = adv7511_cec_parse_dt(dev, adv7511); if (ret) goto err_cec_parse_dt; - adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops, - adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS); - if (IS_ERR(adv7511->cec_adap)) { - ret = PTR_ERR(adv7511->cec_adap); - goto err_cec_alloc; - } + adv7511->cec_connector = connector; regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, 0); /* cec soft reset */ @@ -378,17 +374,8 @@ int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) ADV7511_REG_CEC_CLK_DIV + offset, ((adv7511->cec_clk_freq / 750000) - 1) << 2); - ret = cec_register_adapter(adv7511->cec_adap, dev); - if (ret) - goto err_cec_register; return 0; -err_cec_register: - cec_delete_adapter(adv7511->cec_adap); - adv7511->cec_adap = NULL; -err_cec_alloc: - dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n", - ret); err_cec_parse_dt: regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, ADV7511_CEC_CTRL_POWER_DOWN); diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 1257009e850c..8b7548448615 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -12,14 +12,17 @@ #include #include -#include +#include #include #include +#include #include #include #include #include +#include +#include #include "adv7511.h" @@ -203,62 +206,37 @@ static const uint16_t adv7511_csc_ycbcr_to_rgb[] = { static void adv7511_set_config_csc(struct adv7511 *adv7511, struct drm_connector *connector, - bool rgb, bool hdmi_mode) + bool rgb) { struct adv7511_video_config config; bool output_format_422, output_format_ycbcr; unsigned int mode; - uint8_t infoframe[17]; - - config.hdmi_mode = hdmi_mode; - - hdmi_avi_infoframe_init(&config.avi_infoframe); - - config.avi_infoframe.scan_mode = HDMI_SCAN_MODE_UNDERSCAN; if (rgb) { config.csc_enable = false; - config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB; + output_format_422 = false; + output_format_ycbcr = false; } else { config.csc_scaling_factor = ADV7511_CSC_SCALING_4; config.csc_coefficents = adv7511_csc_ycbcr_to_rgb; if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCBCR422) && - config.hdmi_mode) { + connector->display_info.is_hdmi) { config.csc_enable = false; - config.avi_infoframe.colorspace = - HDMI_COLORSPACE_YUV422; - } else { - config.csc_enable = true; - config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB; - } - } - - if (config.hdmi_mode) { - mode = ADV7511_HDMI_CFG_MODE_HDMI; - - switch (config.avi_infoframe.colorspace) { - case HDMI_COLORSPACE_YUV444: - output_format_422 = false; - output_format_ycbcr = true; - break; - case HDMI_COLORSPACE_YUV422: output_format_422 = true; output_format_ycbcr = true; - break; - default: + } else { + config.csc_enable = true; output_format_422 = false; output_format_ycbcr = false; - break; } - } else { - mode = ADV7511_HDMI_CFG_MODE_DVI; - output_format_422 = false; - output_format_ycbcr = false; } - adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); + if (connector->display_info.is_hdmi) + mode = ADV7511_HDMI_CFG_MODE_HDMI; + else + mode = ADV7511_HDMI_CFG_MODE_DVI; adv7511_set_colormap(adv7511, config.csc_enable, config.csc_coefficents, @@ -269,15 +247,6 @@ static void adv7511_set_config_csc(struct adv7511 *adv7511, regmap_update_bits(adv7511->regmap, ADV7511_REG_HDCP_HDMI_CFG, ADV7511_HDMI_CFG_MODE_MASK, mode); - - hdmi_avi_infoframe_pack(&config.avi_infoframe, infoframe, - sizeof(infoframe)); - - /* The AVI infoframe id is not configurable */ - regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION, - infoframe + 1, sizeof(infoframe) - 1); - - adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); } static void adv7511_set_link_config(struct adv7511 *adv7511, @@ -446,22 +415,16 @@ static void adv7511_hpd_work(struct work_struct *work) * restore its state. */ if (status == connector_status_connected && - adv7511->connector.status == connector_status_disconnected && + adv7511->status == connector_status_disconnected && adv7511->powered) { regcache_mark_dirty(adv7511->regmap); adv7511_power_on(adv7511); } - if (adv7511->connector.status != status) { - adv7511->connector.status = status; + if (adv7511->status != status) { + adv7511->status = status; - if (adv7511->connector.dev) { - if (status == connector_status_disconnected) - cec_phys_addr_invalidate(adv7511->cec_adap); - drm_kms_helper_hotplug_event(adv7511->connector.dev); - } else { - drm_bridge_hpd_notify(&adv7511->bridge, status); - } + drm_bridge_hpd_notify(&adv7511->bridge, status); } } @@ -636,45 +599,11 @@ static const struct drm_edid *adv7511_edid_read(struct adv7511 *adv7511, if (!adv7511->powered) __adv7511_power_off(adv7511); - if (drm_edid) { - /* - * FIXME: The CEC physical address should be set using - * cec_s_phys_addr(adap, - * connector->display_info.source_physical_address, false) from - * a path that has read the EDID and called - * drm_edid_connector_update(). - */ - const struct edid *edid = drm_edid_raw(drm_edid); - - adv7511_set_config_csc(adv7511, connector, adv7511->rgb, - drm_detect_hdmi_monitor(edid)); - - cec_s_phys_addr_from_edid(adv7511->cec_adap, edid); - } else { - cec_s_phys_addr_from_edid(adv7511->cec_adap, NULL); - } - return drm_edid; } -static int adv7511_get_modes(struct adv7511 *adv7511, - struct drm_connector *connector) -{ - const struct drm_edid *drm_edid; - unsigned int count; - - drm_edid = adv7511_edid_read(adv7511, connector); - - drm_edid_connector_update(connector, drm_edid); - count = drm_edid_connector_add_modes(connector); - - drm_edid_free(drm_edid); - - return count; -} - static enum drm_connector_status -adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector) +adv7511_detect(struct adv7511 *adv7511) { enum drm_connector_status status; unsigned int val; @@ -699,8 +628,6 @@ adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector) if (status == connector_status_connected && hpd && adv7511->powered) { regcache_mark_dirty(adv7511->regmap); adv7511_power_on(adv7511); - if (connector) - adv7511_get_modes(adv7511, connector); if (adv7511->status == connector_status_connected) status = connector_status_disconnected; } else { @@ -719,17 +646,7 @@ adv7511_detect(struct adv7511 *adv7511, struct drm_connector *connector) return status; } -static enum drm_mode_status adv7511_mode_valid(struct adv7511 *adv7511, - const struct drm_display_mode *mode) -{ - if (mode->clock > 165000) - return MODE_CLOCK_HIGH; - - return MODE_OK; -} - static void adv7511_mode_set(struct adv7511 *adv7511, - const struct drm_display_mode *mode, const struct drm_display_mode *adj_mode) { unsigned int low_refresh_rate; @@ -800,11 +717,11 @@ static void adv7511_mode_set(struct adv7511 *adv7511, vsync_polarity = 1; } - if (drm_mode_vrefresh(mode) <= 24) + if (drm_mode_vrefresh(adj_mode) <= 24) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_24HZ; - else if (drm_mode_vrefresh(mode) <= 25) + else if (drm_mode_vrefresh(adj_mode) <= 25) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_25HZ; - else if (drm_mode_vrefresh(mode) <= 30) + else if (drm_mode_vrefresh(adj_mode) <= 30) low_refresh_rate = ADV7511_LOW_REFRESH_RATE_30HZ; else low_refresh_rate = ADV7511_LOW_REFRESH_RATE_NONE; @@ -826,77 +743,21 @@ static void adv7511_mode_set(struct adv7511 *adv7511, * supposed to give better results. */ - adv7511->f_tmds = mode->clock; + adv7511->f_tmds = adj_mode->clock; } -/* ----------------------------------------------------------------------------- - * DRM Connector Operations - */ - -static struct adv7511 *connector_to_adv7511(struct drm_connector *connector) -{ - return container_of(connector, struct adv7511, connector); -} - -static int adv7511_connector_get_modes(struct drm_connector *connector) -{ - struct adv7511 *adv = connector_to_adv7511(connector); - - return adv7511_get_modes(adv, connector); -} - -static enum drm_mode_status -adv7511_connector_mode_valid(struct drm_connector *connector, - const struct drm_display_mode *mode) -{ - struct adv7511 *adv = connector_to_adv7511(connector); - - return adv7511_mode_valid(adv, mode); -} - -static struct drm_connector_helper_funcs adv7511_connector_helper_funcs = { - .get_modes = adv7511_connector_get_modes, - .mode_valid = adv7511_connector_mode_valid, -}; - -static enum drm_connector_status -adv7511_connector_detect(struct drm_connector *connector, bool force) -{ - struct adv7511 *adv = connector_to_adv7511(connector); - - return adv7511_detect(adv, connector); -} - -static const struct drm_connector_funcs adv7511_connector_funcs = { - .fill_modes = drm_helper_probe_single_connector_modes, - .detect = adv7511_connector_detect, - .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - static int adv7511_connector_init(struct adv7511 *adv) { struct drm_bridge *bridge = &adv->bridge; - int ret; + struct drm_connector *connector; - if (adv->i2c_main->irq) - adv->connector.polled = DRM_CONNECTOR_POLL_HPD; - else - adv->connector.polled = DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT; - - ret = drm_connector_init(bridge->dev, &adv->connector, - &adv7511_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); - if (ret < 0) { + connector = drm_bridge_connector_init(bridge->dev, bridge->encoder); + if (IS_ERR(connector)) { DRM_ERROR("Failed to initialize connector with drm\n"); - return ret; + return PTR_ERR(connector); } - drm_connector_helper_add(&adv->connector, - &adv7511_connector_helper_funcs); - drm_connector_attach_encoder(&adv->connector, bridge->encoder); + + drm_connector_attach_encoder(connector, bridge->encoder); return 0; } @@ -905,7 +766,7 @@ static int adv7511_connector_init(struct adv7511 *adv) * DRM Bridge Operations */ -static struct adv7511 *bridge_to_adv7511(struct drm_bridge *bridge) +static const struct adv7511 *bridge_to_adv7511_const(const struct drm_bridge *bridge) { return container_of(bridge, struct adv7511, bridge); } @@ -914,8 +775,29 @@ static void adv7511_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_atomic_state *state) { struct adv7511 *adv = bridge_to_adv7511(bridge); + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; adv7511_power_on(adv); + + 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; + + adv7511_set_config_csc(adv, connector, adv->rgb); + + adv7511_mode_set(adv, &crtc_state->adjusted_mode); + + drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); } static void adv7511_bridge_atomic_disable(struct drm_bridge *bridge, @@ -926,13 +808,17 @@ static void adv7511_bridge_atomic_disable(struct drm_bridge *bridge, adv7511_power_off(adv); } -static void adv7511_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adj_mode) +static enum drm_mode_status +adv7511_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge, + const struct drm_display_mode *mode, + unsigned long long tmds_rate) { - struct adv7511 *adv = bridge_to_adv7511(bridge); + const struct adv7511 *adv = bridge_to_adv7511_const(bridge); - adv7511_mode_set(adv, mode, adj_mode); + if (tmds_rate > 1000ULL * adv->info->max_mode_clock_khz) + return MODE_CLOCK_HIGH; + + return MODE_OK; } static enum drm_mode_status adv7511_bridge_mode_valid(struct drm_bridge *bridge, @@ -941,10 +827,10 @@ static enum drm_mode_status adv7511_bridge_mode_valid(struct drm_bridge *bridge, { struct adv7511 *adv = bridge_to_adv7511(bridge); - if (adv->info->has_dsi) - return adv7533_mode_valid(adv, mode); - else - return adv7511_mode_valid(adv, mode); + if (!adv->info->has_dsi) + return MODE_OK; + + return adv7533_mode_valid(adv, mode); } static int adv7511_bridge_attach(struct drm_bridge *bridge, @@ -978,7 +864,7 @@ static enum drm_connector_status adv7511_bridge_detect(struct drm_bridge *bridge { struct adv7511 *adv = bridge_to_adv7511(bridge); - return adv7511_detect(adv, NULL); + return adv7511_detect(adv); } static const struct drm_edid *adv7511_bridge_edid_read(struct drm_bridge *bridge, @@ -989,28 +875,71 @@ static const struct drm_edid *adv7511_bridge_edid_read(struct drm_bridge *bridge return adv7511_edid_read(adv, connector); } -static void adv7511_bridge_hpd_notify(struct drm_bridge *bridge, - enum drm_connector_status status) +static int adv7511_bridge_hdmi_clear_infoframe(struct drm_bridge *bridge, + enum hdmi_infoframe_type type) { - struct adv7511 *adv = bridge_to_adv7511(bridge); + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); - if (status == connector_status_disconnected) - cec_phys_addr_invalidate(adv->cec_adap); + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); + break; + default: + drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type); + break; + } + + return 0; +} + +static int adv7511_bridge_hdmi_write_infoframe(struct drm_bridge *bridge, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len) +{ + struct adv7511 *adv7511 = bridge_to_adv7511(bridge); + + adv7511_bridge_hdmi_clear_infoframe(bridge, type); + + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + /* The AVI infoframe id is not configurable */ + regmap_bulk_write(adv7511->regmap, ADV7511_REG_AVI_INFOFRAME_VERSION, + buffer + 1, len - 1); + + adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME); + break; + default: + drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type); + break; + } + + return 0; } static const struct drm_bridge_funcs adv7511_bridge_funcs = { - .mode_set = adv7511_bridge_mode_set, .mode_valid = adv7511_bridge_mode_valid, .attach = adv7511_bridge_attach, .detect = adv7511_bridge_detect, .edid_read = adv7511_bridge_edid_read, - .hpd_notify = adv7511_bridge_hpd_notify, .atomic_enable = adv7511_bridge_atomic_enable, .atomic_disable = adv7511_bridge_atomic_disable, .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, + + .hdmi_tmds_char_rate_valid = adv7511_bridge_hdmi_tmds_char_rate_valid, + .hdmi_clear_infoframe = adv7511_bridge_hdmi_clear_infoframe, + .hdmi_write_infoframe = adv7511_bridge_hdmi_write_infoframe, + + .hdmi_audio_startup = adv7511_hdmi_audio_startup, + .hdmi_audio_prepare = adv7511_hdmi_audio_prepare, + .hdmi_audio_shutdown = adv7511_hdmi_audio_shutdown, + + .hdmi_cec_init = adv7511_cec_init, + .hdmi_cec_enable = adv7511_cec_enable, + .hdmi_cec_log_addr = adv7511_cec_log_addr, + .hdmi_cec_transmit = adv7511_cec_transmit, }; /* ----------------------------------------------------------------------------- @@ -1323,22 +1252,44 @@ static int adv7511_probe(struct i2c_client *i2c) if (adv7511->info->link_config) adv7511_set_link_config(adv7511, &link_config); - ret = adv7511_cec_init(dev, adv7511); - if (ret) - goto err_unregister_cec; + regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, + ADV7511_CEC_CTRL_POWER_DOWN); adv7511->bridge.funcs = &adv7511_bridge_funcs; - adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; + adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | + DRM_BRIDGE_OP_EDID | + DRM_BRIDGE_OP_HDMI | + DRM_BRIDGE_OP_HDMI_AUDIO | + DRM_BRIDGE_OP_HDMI_CEC_ADAPTER; if (adv7511->i2c_main->irq) adv7511->bridge.ops |= DRM_BRIDGE_OP_HPD; + adv7511->bridge.vendor = "Analog"; + adv7511->bridge.product = adv7511->info->name; + +#ifdef CONFIG_DRM_I2C_ADV7511_AUDIO + adv7511->bridge.hdmi_audio_dev = dev; + adv7511->bridge.hdmi_audio_max_i2s_playback_channels = 2; + adv7511->bridge.hdmi_audio_i2s_formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE), + adv7511->bridge.hdmi_audio_spdif_playback = 1; + adv7511->bridge.hdmi_audio_dai_port = 2; +#endif + +#ifdef CONFIG_DRM_I2C_ADV7511_CEC + adv7511->bridge.hdmi_cec_dev = dev; + adv7511->bridge.hdmi_cec_adapter_name = dev_name(dev); + adv7511->bridge.hdmi_cec_available_las = ADV7511_MAX_ADDRS; +#endif + adv7511->bridge.of_node = dev->of_node; adv7511->bridge.type = DRM_MODE_CONNECTOR_HDMIA; drm_bridge_add(&adv7511->bridge); - adv7511_audio_init(dev, adv7511); - if (i2c->irq) { init_waitqueue_head(&adv7511->wq); @@ -1360,10 +1311,7 @@ static int adv7511_probe(struct i2c_client *i2c) return 0; err_unregister_audio: - adv7511_audio_exit(adv7511); drm_bridge_remove(&adv7511->bridge); -err_unregister_cec: - cec_unregister_adapter(adv7511->cec_adap); i2c_unregister_device(adv7511->i2c_cec); clk_disable_unprepare(adv7511->cec_clk); err_i2c_unregister_packet: @@ -1388,9 +1336,6 @@ static void adv7511_remove(struct i2c_client *i2c) drm_bridge_remove(&adv7511->bridge); - adv7511_audio_exit(adv7511); - - cec_unregister_adapter(adv7511->cec_adap); i2c_unregister_device(adv7511->i2c_cec); clk_disable_unprepare(adv7511->cec_clk); @@ -1400,6 +1345,8 @@ static void adv7511_remove(struct i2c_client *i2c) static const struct adv7511_chip_info adv7511_chip_info = { .type = ADV7511, + .name = "ADV7511", + .max_mode_clock_khz = 165000, .supply_names = adv7511_supply_names, .num_supplies = ARRAY_SIZE(adv7511_supply_names), .link_config = true, @@ -1407,6 +1354,7 @@ static const struct adv7511_chip_info adv7511_chip_info = { static const struct adv7511_chip_info adv7533_chip_info = { .type = ADV7533, + .name = "ADV7533", .max_mode_clock_khz = 80000, .max_lane_freq_khz = 800000, .supply_names = adv7533_supply_names, @@ -1417,6 +1365,7 @@ static const struct adv7511_chip_info adv7533_chip_info = { static const struct adv7511_chip_info adv7535_chip_info = { .type = ADV7535, + .name = "ADV7535", .max_mode_clock_khz = 148500, .max_lane_freq_khz = 891000, .supply_names = adv7533_supply_names, diff --git a/drivers/gpu/drm/bridge/adv7511/adv7533.c b/drivers/gpu/drm/bridge/adv7511/adv7533.c index 122ad91e8a32..b12d422343fc 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7533.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7533.c @@ -106,10 +106,6 @@ enum drm_mode_status adv7533_mode_valid(struct adv7511 *adv, struct mipi_dsi_device *dsi = adv->dsi; u8 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); - /* Check max clock for either 7533 or 7535 */ - if (mode->clock > adv->info->max_mode_clock_khz) - return MODE_CLOCK_HIGH; - /* Check max clock for each lane */ if (mode->clock * bpp > adv->info->max_lane_freq_khz * adv->num_dsi_lanes) return MODE_CLOCK_HIGH; From 74ca3ba0d00d1a52486347dbe8fb1c8ebb5dde7e Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:32 -0500 Subject: [PATCH 048/279] panel/panel-elida-kd35t133: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-1-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-elida-kd35t133.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-elida-kd35t133.c b/drivers/gpu/drm/panel/panel-elida-kd35t133.c index b904d5437444..1f177834d629 100644 --- a/drivers/gpu/drm/panel/panel-elida-kd35t133.c +++ b/drivers/gpu/drm/panel/panel-elida-kd35t133.c @@ -206,9 +206,10 @@ static int kd35t133_probe(struct mipi_dsi_device *dsi) struct kd35t133 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct kd35t133, panel, + &kd35t133_funcs, DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset_gpio)) { @@ -248,9 +249,6 @@ static int kd35t133_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, &dsi->dev, &kd35t133_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; From 39cfc68b6b24fef5da6c398b654df3686306dfd8 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:33 -0500 Subject: [PATCH 049/279] panel/feixin-k101-im2ba02: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-2-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c index 986e3e192881..6225501cb174 100644 --- a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c +++ b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c @@ -443,9 +443,11 @@ static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi) unsigned int i; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct k101_im2ba02, panel, + &k101_im2ba02_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; @@ -463,9 +465,6 @@ static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset), "Couldn't get our reset GPIO\n"); - drm_panel_init(&ctx->panel, &dsi->dev, &k101_im2ba02_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; From 1017366dd161bbd19dd94ba5c2988a323f3d9d2c Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:34 -0500 Subject: [PATCH 050/279] panel/fy07024di26a30d: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-3-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c index 48e3acaecdf3..4f8d6d8c07e4 100644 --- a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c +++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c @@ -189,16 +189,14 @@ static int feiyang_dsi_probe(struct mipi_dsi_device *dsi) struct feiyang *ctx; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct feiyang, panel, + &feiyang_funcs, DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; - drm_panel_init(&ctx->panel, &dsi->dev, &feiyang_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd"); if (IS_ERR(ctx->dvdd)) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->dvdd), From 7b8c32961bcfec26b6542733dca4b39963a6d20a Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:35 -0500 Subject: [PATCH 051/279] panel/himax-hx83112a: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-4-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-himax-hx83112a.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-himax-hx83112a.c b/drivers/gpu/drm/panel/panel-himax-hx83112a.c index 47bce087e339..142cb1cc067a 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83112a.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83112a.c @@ -269,9 +269,11 @@ static int hx83112a_probe(struct mipi_dsi_device *dsi) struct hx83112a_panel *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct hx83112a_panel, panel, + &hx83112a_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->supplies[0].supply = "vdd1"; ctx->supplies[1].supply = "vsn"; @@ -295,8 +297,6 @@ static int hx83112a_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &hx83112a_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ret = drm_panel_of_backlight(&ctx->panel); From c53c3e87aeb7fc288dcb9dbdb22451e8721771e1 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:36 -0500 Subject: [PATCH 052/279] panel/himax-hx8394: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-5-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-himax-hx8394.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-himax-hx8394.c b/drivers/gpu/drm/panel/panel-himax-hx8394.c index ff994bf0e3cc..0e3bf4ba9189 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx8394.c +++ b/drivers/gpu/drm/panel/panel-himax-hx8394.c @@ -611,9 +611,11 @@ static int hx8394_probe(struct mipi_dsi_device *dsi) struct hx8394 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct hx8394, panel, + &hx8394_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ctx->reset_gpio)) @@ -645,9 +647,6 @@ static int hx8394_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->iovcc), "Failed to request iovcc regulator\n"); - drm_panel_init(&ctx->panel, dev, &hx8394_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; From 9609efa05d5ec63de5e758571f1bb85bdec93561 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:37 -0500 Subject: [PATCH 053/279] panel/ilitek-ili9322: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-6-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-ilitek-ili9322.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c index 94b7dfef3b5e..6ed544a83bdd 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c @@ -722,9 +722,10 @@ static int ili9322_probe(struct spi_device *spi) int ret; int i; - ili = devm_kzalloc(dev, sizeof(struct ili9322), GFP_KERNEL); - if (!ili) - return -ENOMEM; + ili = devm_drm_panel_alloc(dev, struct ili9322, panel, + &ili9322_drm_funcs, DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ili)) + return PTR_ERR(ili); spi_set_drvdata(spi, ili); @@ -883,9 +884,6 @@ static int ili9322_probe(struct spi_device *spi) ili->input = ili->conf->input; } - drm_panel_init(&ili->panel, dev, &ili9322_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_panel_add(&ili->panel); return 0; From 75db23f878e6513de8de3ee08184b09f5bf35a96 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:38 -0500 Subject: [PATCH 054/279] panel/ilitek-ili9341: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-7-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-ilitek-ili9341.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c index ff39f5dd4097..bcd561e06465 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c @@ -490,9 +490,11 @@ static int ili9341_dpi_probe(struct spi_device *spi, struct gpio_desc *dc, struct ili9341 *ili; int ret; - ili = devm_kzalloc(dev, sizeof(struct ili9341), GFP_KERNEL); - if (!ili) - return -ENOMEM; + ili = devm_drm_panel_alloc(dev, struct ili9341, panel, + &ili9341_dpi_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ili)) + return PTR_ERR(ili); ili->dbi = devm_kzalloc(dev, sizeof(struct mipi_dbi), GFP_KERNEL); @@ -526,8 +528,6 @@ static int ili9341_dpi_probe(struct spi_device *spi, struct gpio_desc *dc, } ili->max_spi_speed = ili->conf->max_spi_speed; - drm_panel_init(&ili->panel, dev, &ili9341_dpi_funcs, - DRM_MODE_CONNECTOR_DPI); drm_panel_add(&ili->panel); return 0; From 8500594c9bd132d7e8b1de4b0878786ccdd08723 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:39 -0500 Subject: [PATCH 055/279] panel/panel-ili9805: Use refcounted allocation in place of devm_kzalloc() Start using the new helper that does the refcounted allocations Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-8-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-ilitek-ili9805.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c index 1cbc25758bd2..e6c483851f1f 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c @@ -307,9 +307,12 @@ static int ili9805_dsi_probe(struct mipi_dsi_device *dsi) struct ili9805 *ctx; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct ili9805, panel, + &ili9805_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; ctx->desc = of_device_get_match_data(&dsi->dev); @@ -320,9 +323,6 @@ static int ili9805_dsi_probe(struct mipi_dsi_device *dsi) MIPI_DSI_MODE_VIDEO_SYNC_PULSE | MIPI_DSI_MODE_NO_EOT_PACKET; dsi->lanes = 2; - drm_panel_init(&ctx->panel, &dsi->dev, &ili9805_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd"); if (IS_ERR(ctx->dvdd)) return PTR_ERR(ctx->dvdd); From c15e4acd15e49312ab901b4bde2c789a39e64933 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:40 -0500 Subject: [PATCH 056/279] panel/ilitek-ili9806e: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Michael Walle Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-9-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-ilitek-ili9806e.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c index a3c79ad99d0b..18aa6222b0c5 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e.c @@ -166,9 +166,10 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi) struct ili9806e_panel *ctx; int i, ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct ili9806e_panel, panel, &ili9806e_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->desc = device_get_match_data(dev); @@ -192,9 +193,6 @@ static int ili9806e_dsi_probe(struct mipi_dsi_device *dsi) dsi->format = ctx->desc->format; dsi->lanes = ctx->desc->lanes; - drm_panel_init(&ctx->panel, dev, &ili9806e_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); if (ret) return dev_err_probe(dev, ret, "Failed to get orientation\n"); From 0678c17add9b42b1e8878499eb9f2b2ca4b0e6fc Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:41 -0500 Subject: [PATCH 057/279] panel/ilitek-ili9881c: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-10-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-ilitek-ili9881c.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 28cd7560e5db..aa4192def093 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -1506,16 +1506,15 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) struct ili9881c *ctx; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&dsi->dev, struct ili9881c, panel, &ili9881c_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + mipi_dsi_set_drvdata(dsi, ctx); ctx->dsi = dsi; ctx->desc = of_device_get_match_data(&dsi->dev); - drm_panel_init(&ctx->panel, &dsi->dev, &ili9881c_funcs, - DRM_MODE_CONNECTOR_DSI); - ctx->power = devm_regulator_get(&dsi->dev, "power"); if (IS_ERR(ctx->power)) return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power), From 93b6d2ea6f8ae6c8e779d5c5bfa3462f4c174e76 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:42 -0500 Subject: [PATCH 058/279] panel/innolux-ej030na: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-11-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-innolux-ej030na.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-innolux-ej030na.c b/drivers/gpu/drm/panel/panel-innolux-ej030na.c index f85b7a4cbb42..b2309900873b 100644 --- a/drivers/gpu/drm/panel/panel-innolux-ej030na.c +++ b/drivers/gpu/drm/panel/panel-innolux-ej030na.c @@ -204,9 +204,11 @@ static int ej030na_probe(struct spi_device *spi) struct ej030na *priv; int err; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + priv = devm_drm_panel_alloc(dev, struct ej030na, panel, + &ej030na_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(priv)) + return PTR_ERR(priv); priv->spi = spi; spi_set_drvdata(spi, priv); @@ -231,9 +233,6 @@ static int ej030na_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n"); - drm_panel_init(&priv->panel, dev, &ej030na_funcs, - DRM_MODE_CONNECTOR_DPI); - err = drm_panel_of_backlight(&priv->panel); if (err) return err; From 8015bc283e9025151f5a724f7253c49c1ca939b0 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:43 -0500 Subject: [PATCH 059/279] panel/innolux-p079zca: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-12-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-innolux-p079zca.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index d95c0d4f3e35..80afeeab9475 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -382,9 +382,11 @@ static int innolux_panel_add(struct mipi_dsi_device *dsi, struct device *dev = &dsi->dev; int err, i; - innolux = devm_kzalloc(dev, sizeof(*innolux), GFP_KERNEL); - if (!innolux) - return -ENOMEM; + innolux = devm_drm_panel_alloc(dev, struct innolux_panel, base, + &innolux_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(innolux)) + return PTR_ERR(innolux); innolux->desc = desc; @@ -410,9 +412,6 @@ static int innolux_panel_add(struct mipi_dsi_device *dsi, innolux->enable_gpio = NULL; } - drm_panel_init(&innolux->base, dev, &innolux_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - err = drm_panel_of_backlight(&innolux->base); if (err) return err; From 9d20a28650ed8885704e195510435b54cbf07be0 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:44 -0500 Subject: [PATCH 060/279] panel/jadard-jd9365da-h3: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-13-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c index eb0f8373258c..5c2530598ddb 100644 --- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c @@ -1120,9 +1120,10 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi) struct jadard *jadard; int ret; - jadard = devm_kzalloc(&dsi->dev, sizeof(*jadard), GFP_KERNEL); - if (!jadard) - return -ENOMEM; + jadard = devm_drm_panel_alloc(dev, struct jadard, panel, &jadard_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(jadard)) + return PTR_ERR(jadard); desc = of_device_get_match_data(dev); dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | @@ -1148,9 +1149,6 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi) return PTR_ERR(jadard->vccio); } - drm_panel_init(&jadard->panel, dev, &jadard_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = of_drm_get_panel_orientation(dev->of_node, &jadard->orientation); if (ret < 0) return dev_err_probe(dev, ret, "failed to get orientation\n"); From 608cd2887f78332e4346ae6b7bf1f883c306dfbf Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:45 -0500 Subject: [PATCH 061/279] panel/jdi-fhd-r63452: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-14-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c index 4eb71e85e9e9..cbe354b51bce 100644 --- a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c +++ b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c @@ -175,9 +175,11 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi) struct jdi_fhd_r63452 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct jdi_fhd_r63452, panel, + &jdi_fhd_r63452_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ctx->reset_gpio)) @@ -192,8 +194,6 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_CLOCK_NON_CONTINUOUS; - drm_panel_init(&ctx->panel, dev, &jdi_fhd_r63452_panel_funcs, - DRM_MODE_CONNECTOR_DSI); ctx->panel.prepare_prev_first = true; ret = drm_panel_of_backlight(&ctx->panel); From da93b863bd9bcf4e83cc59ac811fc0d74cb25104 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:46 -0500 Subject: [PATCH 062/279] panel/ltk050h3146w: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-15-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c index 77f74e6c467e..0856df5a6ee2 100644 --- a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c @@ -548,9 +548,11 @@ static int ltk050h3146w_probe(struct mipi_dsi_device *dsi) struct ltk050h3146w *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct ltk050h3146w, panel, + <k050h3146w_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->panel_desc = of_device_get_match_data(dev); if (!ctx->panel_desc) @@ -577,9 +579,6 @@ static int ltk050h3146w_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = ctx->panel_desc->mode_flags; - drm_panel_init(&ctx->panel, &dsi->dev, <k050h3146w_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; From deafcd1f936d2f2f9beebb19215850653a5526f1 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:47 -0500 Subject: [PATCH 063/279] panel/ltk500hd1829: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-16-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c index 6b18cf00fd4a..7f19fd5b8060 100644 --- a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c @@ -604,9 +604,11 @@ static int ltk500hd1829_probe(struct mipi_dsi_device *dsi) struct device *dev = &dsi->dev; int ret; - ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct ltk500hd1829, panel, + <k500hd1829_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->panel_desc = of_device_get_match_data(dev); if (!ctx->panel_desc) @@ -643,9 +645,6 @@ static int ltk500hd1829_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; - drm_panel_init(&ctx->panel, &dsi->dev, <k500hd1829_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; From 89da5d30fa0cc6f4612f9c3e30b61610b6299f64 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:48 -0500 Subject: [PATCH 064/279] panel/lg-lg4573: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-17-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-lg-lg4573.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-lg-lg4573.c b/drivers/gpu/drm/panel/panel-lg-lg4573.c index cf246d15b7b6..dec619902c15 100644 --- a/drivers/gpu/drm/panel/panel-lg-lg4573.c +++ b/drivers/gpu/drm/panel/panel-lg-lg4573.c @@ -243,9 +243,11 @@ static int lg4573_probe(struct spi_device *spi) struct lg4573 *ctx; int ret; - ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(&spi->dev, struct lg4573, panel, + &lg4573_drm_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->spi = spi; @@ -258,9 +260,6 @@ static int lg4573_probe(struct spi_device *spi) return ret; } - drm_panel_init(&ctx->panel, &spi->dev, &lg4573_drm_funcs, - DRM_MODE_CONNECTOR_DPI); - drm_panel_add(&ctx->panel); return 0; From b28994ed389017b857248575794e8c340473b79a Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:49 -0500 Subject: [PATCH 065/279] panel/lincolntech-lcd197: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-18-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-lincolntech-lcd197.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-lincolntech-lcd197.c b/drivers/gpu/drm/panel/panel-lincolntech-lcd197.c index 032c542aab0f..24b34443ace0 100644 --- a/drivers/gpu/drm/panel/panel-lincolntech-lcd197.c +++ b/drivers/gpu/drm/panel/panel-lincolntech-lcd197.c @@ -190,9 +190,11 @@ static int lincoln_lcd197_panel_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST); - lcd = devm_kzalloc(&dsi->dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; + lcd = devm_drm_panel_alloc(dev, struct lincoln_lcd197_panel, panel, + &lincoln_lcd197_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); mipi_dsi_set_drvdata(dsi, lcd); lcd->dsi = dsi; @@ -214,9 +216,6 @@ static int lincoln_lcd197_panel_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(lcd->reset_gpio), "failed to get reset gpio"); - drm_panel_init(&lcd->panel, dev, - &lincoln_lcd197_panel_funcs, DRM_MODE_CONNECTOR_DSI); - err = drm_panel_of_backlight(&lcd->panel); if (err) return err; From 2a2c7d7d363e0a4afa2042378f5797366d49746c Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:50 -0500 Subject: [PATCH 066/279] panel/magnachip-d53e6ea8966: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-19-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c b/drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c index 799c2161fc85..cde168ec631c 100644 --- a/drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c +++ b/drivers/gpu/drm/panel/panel-magnachip-d53e6ea8966.c @@ -370,9 +370,11 @@ static int d53e6ea8966_probe(struct spi_device *spi) .node = NULL, }; - db = devm_kzalloc(dev, sizeof(*db), GFP_KERNEL); - if (!db) - return -ENOMEM; + db = devm_drm_panel_alloc(dev, struct d53e6ea8966, panel, + &d53e6ea8966_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(db)) + return PTR_ERR(db); spi_set_drvdata(spi, db); @@ -425,9 +427,6 @@ static int d53e6ea8966_probe(struct spi_device *spi) db->dsi_dev->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET; - drm_panel_init(&db->panel, dev, &d53e6ea8966_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - if (db->panel_info->backlight_register) { ret = db->panel_info->backlight_register(db); if (ret < 0) From 47b74d7c5e48415cb8d2903077f93760d5d350c3 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:51 -0500 Subject: [PATCH 067/279] panel/mantix-mlaf057we51: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-20-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c index 4db852ffb0f6..55664f5d5aa5 100644 --- a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c +++ b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c @@ -234,9 +234,11 @@ static int mantix_probe(struct mipi_dsi_device *dsi) struct mantix *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct mantix, panel, &mantix_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + ctx->default_mode = of_device_get_match_data(dev); ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); @@ -271,9 +273,6 @@ static int mantix_probe(struct mipi_dsi_device *dsi) if (IS_ERR(ctx->vddi)) return dev_err_probe(dev, PTR_ERR(ctx->vddi), "Failed to request vddi regulator\n"); - drm_panel_init(&ctx->panel, dev, &mantix_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; From 417f0eb92c53fab6a9fdb4f09f9db6505bb7a735 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:52 -0500 Subject: [PATCH 068/279] panel/newvision-nv3051d: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-21-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-newvision-nv3051d.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c index b6429795e8f5..22560384e48e 100644 --- a/drivers/gpu/drm/panel/panel-newvision-nv3051d.c +++ b/drivers/gpu/drm/panel/panel-newvision-nv3051d.c @@ -361,9 +361,11 @@ static int panel_nv3051d_probe(struct mipi_dsi_device *dsi) struct panel_nv3051d *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct panel_nv3051d, panel, + &panel_nv3051d_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; @@ -391,9 +393,6 @@ static int panel_nv3051d_probe(struct mipi_dsi_device *dsi) dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = ctx->panel_info->mode_flags; - drm_panel_init(&ctx->panel, &dsi->dev, &panel_nv3051d_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return ret; From 5d5da5cef9bdabe455804eb000147ace298cde90 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:53 -0500 Subject: [PATCH 069/279] panel/newvision-nv3052c: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-22-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-newvision-nv3052c.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c index 06e16a7c14a7..0db9cadd868e 100644 --- a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c +++ b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c @@ -777,9 +777,10 @@ static int nv3052c_probe(struct spi_device *spi) struct nv3052c *priv; int err; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + priv = devm_drm_panel_alloc(dev, struct nv3052c, panel, &nv3052c_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(priv)) + return PTR_ERR(priv); priv->dev = dev; @@ -803,9 +804,6 @@ static int nv3052c_probe(struct spi_device *spi) spi_set_drvdata(spi, priv); - drm_panel_init(&priv->panel, dev, &nv3052c_funcs, - DRM_MODE_CONNECTOR_DPI); - err = drm_panel_of_backlight(&priv->panel); if (err) return dev_err_probe(dev, err, "Failed to attach backlight\n"); From 3f08c356b881d3d6302de207a98ceec1c511159e Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:54 -0500 Subject: [PATCH 070/279] panel/novatek-nt35510: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-23-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-novatek-nt35510.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35510.c b/drivers/gpu/drm/panel/panel-novatek-nt35510.c index 549b86f2cc28..3189d89c7ca0 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35510.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35510.c @@ -1087,9 +1087,12 @@ static int nt35510_probe(struct mipi_dsi_device *dsi) struct nt35510 *nt; int ret; - nt = devm_kzalloc(dev, sizeof(struct nt35510), GFP_KERNEL); - if (!nt) - return -ENOMEM; + nt = devm_drm_panel_alloc(dev, struct nt35510, panel, + &nt35510_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(nt)) + return PTR_ERR(nt); + mipi_dsi_set_drvdata(dsi, nt); nt->dev = dev; @@ -1142,9 +1145,6 @@ static int nt35510_probe(struct mipi_dsi_device *dsi) return PTR_ERR(nt->reset_gpio); } - drm_panel_init(&nt->panel, dev, &nt35510_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - /* * First, try to locate an external backlight (such as on GPIO) * if this fails, assume we will want to use the internal backlight From 81cb8735420d9376b565e842fc3a7f65f68e0c23 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:55 -0500 Subject: [PATCH 071/279] panel/novatek-nt35560: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-24-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-novatek-nt35560.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35560.c b/drivers/gpu/drm/panel/panel-novatek-nt35560.c index 5bbea734123b..98f0782c8411 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35560.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35560.c @@ -456,9 +456,12 @@ static int nt35560_probe(struct mipi_dsi_device *dsi) struct nt35560 *nt; int ret; - nt = devm_kzalloc(dev, sizeof(struct nt35560), GFP_KERNEL); - if (!nt) - return -ENOMEM; + nt = devm_drm_panel_alloc(dev, struct nt35560, panel, + &nt35560_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(nt)) + return PTR_ERR(nt); + nt->video_mode = of_property_read_bool(dev->of_node, "enforce-video-mode"); @@ -502,9 +505,6 @@ static int nt35560_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(nt->reset_gpio), "failed to request GPIO\n"); - drm_panel_init(&nt->panel, dev, &nt35560_drm_funcs, - DRM_MODE_CONNECTOR_DSI); - nt->panel.backlight = devm_backlight_device_register(dev, "nt35560", dev, nt, &nt35560_bl_ops, &nt35560_bl_props); if (IS_ERR(nt->panel.backlight)) From e59e1f45246efbd0b0da65753a64ecdaf421776c Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:56 -0500 Subject: [PATCH 072/279] panel/novatek-nt35950: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-25-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-novatek-nt35950.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35950.c b/drivers/gpu/drm/panel/panel-novatek-nt35950.c index 08b22b592ab0..94aa6489d99f 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35950.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35950.c @@ -449,9 +449,10 @@ static int nt35950_probe(struct mipi_dsi_device *dsi) const struct mipi_dsi_device_info *info; int i, num_dsis = 1, ret; - nt = devm_kzalloc(dev, sizeof(*nt), GFP_KERNEL); - if (!nt) - return -ENOMEM; + nt = devm_drm_panel_alloc(dev, struct nt35950, panel, &nt35950_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(nt)) + return PTR_ERR(nt); ret = nt35950_sharp_init_vregs(nt, dev); if (ret) @@ -491,9 +492,6 @@ static int nt35950_probe(struct mipi_dsi_device *dsi) nt->dsi[0] = dsi; mipi_dsi_set_drvdata(dsi, nt); - drm_panel_init(&nt->panel, dev, &nt35950_panel_funcs, - DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&nt->panel); if (ret) { if (num_dsis == 2) From 10868521bdd5e7ff3c41d6adfa116c7c264a08bc Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:57 -0500 Subject: [PATCH 073/279] panel/novatek-nt36523: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-26-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-novatek-nt36523.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36523.c b/drivers/gpu/drm/panel/panel-novatek-nt36523.c index 116d67bfa114..32cf64c7c18b 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36523.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36523.c @@ -1171,9 +1171,11 @@ static int nt36523_probe(struct mipi_dsi_device *dsi) const struct mipi_dsi_device_info *info; int i, ret; - pinfo = devm_kzalloc(dev, sizeof(*pinfo), GFP_KERNEL); - if (!pinfo) - return -ENOMEM; + pinfo = devm_drm_panel_alloc(dev, struct panel_info, panel, + &nt36523_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(pinfo)) + return PTR_ERR(pinfo); pinfo->vddio = devm_regulator_get(dev, "vddio"); if (IS_ERR(pinfo->vddio)) @@ -1211,7 +1213,6 @@ static int nt36523_probe(struct mipi_dsi_device *dsi) pinfo->dsi[0] = dsi; mipi_dsi_set_drvdata(dsi, pinfo); - drm_panel_init(&pinfo->panel, dev, &nt36523_panel_funcs, DRM_MODE_CONNECTOR_DSI); ret = of_drm_get_panel_orientation(dev->of_node, &pinfo->orientation); if (ret < 0) { From e41a4813af67b294b9f7573fa9b363845e724399 Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:58 -0500 Subject: [PATCH 074/279] panel/novatek-nt36672e: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-27-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-novatek-nt36672e.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672e.c b/drivers/gpu/drm/panel/panel-novatek-nt36672e.c index 8c9e04207ba9..c5e00eb55722 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36672e.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36672e.c @@ -522,9 +522,11 @@ static int nt36672e_panel_probe(struct mipi_dsi_device *dsi) struct nt36672e_panel *ctx; int i, ret = 0; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_panel_alloc(dev, struct nt36672e_panel, panel, + &nt36672e_drm_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->desc = of_device_get_match_data(dev); if (!ctx->desc) { @@ -553,8 +555,6 @@ static int nt36672e_panel_probe(struct mipi_dsi_device *dsi) dsi->format = ctx->desc->format; dsi->mode_flags = ctx->desc->mode_flags; - drm_panel_init(&ctx->panel, dev, &nt36672e_drm_funcs, DRM_MODE_CONNECTOR_DSI); - ret = drm_panel_of_backlight(&ctx->panel); if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); From 6e8fba44daa7ec63942d95ad730e0d32455ecede Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:03:59 -0500 Subject: [PATCH 075/279] panel/novatek-nt39016: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-28-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-novatek-nt39016.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-novatek-nt39016.c b/drivers/gpu/drm/panel/panel-novatek-nt39016.c index 9fa7654e2b67..a629976bae54 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt39016.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt39016.c @@ -246,9 +246,10 @@ static int nt39016_probe(struct spi_device *spi) struct nt39016 *panel; int err; - panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -ENOMEM; + panel = devm_drm_panel_alloc(dev, struct nt39016, drm_panel, &nt39016_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(panel)) + return PTR_ERR(panel); spi_set_drvdata(spi, panel); @@ -279,9 +280,6 @@ static int nt39016_probe(struct spi_device *spi) return PTR_ERR(panel->map); } - drm_panel_init(&panel->drm_panel, dev, &nt39016_funcs, - DRM_MODE_CONNECTOR_DPI); - err = drm_panel_of_backlight(&panel->drm_panel); if (err) return dev_err_probe(dev, err, "Failed to get backlight handle\n"); From d2b67baa892a6c412f539eea883092e7c580ccea Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:04:00 -0500 Subject: [PATCH 076/279] panel/lcd-olinuxino: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-29-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c index 94ae8c8270b8..66f99982f360 100644 --- a/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c +++ b/drivers/gpu/drm/panel/panel-olimex-lcd-olinuxino.c @@ -175,9 +175,11 @@ static int lcd_olinuxino_probe(struct i2c_client *client) I2C_FUNC_SMBUS_READ_I2C_BLOCK)) return -ENODEV; - lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL); - if (!lcd) - return -ENOMEM; + lcd = devm_drm_panel_alloc(dev, struct lcd_olinuxino, panel, + &lcd_olinuxino_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(lcd)) + return PTR_ERR(lcd); i2c_set_clientdata(client, lcd); lcd->dev = dev; @@ -234,9 +236,6 @@ static int lcd_olinuxino_probe(struct i2c_client *client) if (IS_ERR(lcd->enable_gpio)) return PTR_ERR(lcd->enable_gpio); - drm_panel_init(&lcd->panel, dev, &lcd_olinuxino_funcs, - DRM_MODE_CONNECTOR_DPI); - ret = drm_panel_of_backlight(&lcd->panel); if (ret) return ret; From bdfc5b2927062479d3b918d2990a08f3091ef5fa Mon Sep 17 00:00:00 2001 From: Anusha Srivatsa Date: Tue, 20 May 2025 22:04:01 -0500 Subject: [PATCH 077/279] panel/orisetech-ota5601a: Use refcounted allocation in place of devm_kzalloc() Move to using the new API devm_drm_panel_alloc() to allocate the panel. Reviewed-by: Neil Armstrong Signed-off-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250520-drivers-mass-convert-part2-v3-30-f7ae7b723c68@redhat.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/panel/panel-orisetech-ota5601a.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c b/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c index fc87f61d4400..3231e84dc66c 100644 --- a/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c +++ b/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c @@ -237,9 +237,11 @@ static int ota5601a_probe(struct spi_device *spi) struct ota5601a *panel; int err; - panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); - if (!panel) - return -ENOMEM; + panel = devm_drm_panel_alloc(dev, struct ota5601a, drm_panel, + &ota5601a_funcs, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(panel)) + return PTR_ERR(panel); spi_set_drvdata(spi, panel); @@ -273,9 +275,6 @@ static int ota5601a_probe(struct spi_device *spi) return PTR_ERR(panel->map); } - drm_panel_init(&panel->drm_panel, dev, &ota5601a_funcs, - DRM_MODE_CONNECTOR_DPI); - err = drm_panel_of_backlight(&panel->drm_panel); if (err) { if (err != -EPROBE_DEFER) From 7a909b2beafbdb0139cb953ce6be4813b6433d1f Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:29 +0200 Subject: [PATCH 078/279] drm/bridge: anx7625: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Acked-by: Maxime Ripard Reviewed-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-3-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/bridge/analogix/anx7625.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 8a9079c2ed5c..0ac4a82c5a6e 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2596,7 +2596,6 @@ static int anx7625_link_bridge(struct drm_dp_aux *aux) return ret; } - platform->bridge.funcs = &anx7625_bridge_funcs; platform->bridge.of_node = dev->of_node; if (!anx7625_of_panel_on_aux_bus(dev)) platform->bridge.ops |= DRM_BRIDGE_OP_EDID; @@ -2630,10 +2629,10 @@ static int anx7625_i2c_probe(struct i2c_client *client) return -ENODEV; } - platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); - if (!platform) { + platform = devm_drm_bridge_alloc(dev, struct anx7625_data, bridge, &anx7625_bridge_funcs); + if (IS_ERR(platform)) { DRM_DEV_ERROR(dev, "fail to allocate driver data\n"); - return -ENOMEM; + return PTR_ERR(platform); } pdata = &platform->pdata; From 2b42027f449c9a15e0667d99f76cdb4d775dd201 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:30 +0200 Subject: [PATCH 079/279] drm/bridge: cdns-dsi: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Acked-by: Maxime Ripard Reviewed-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-4-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c index b022dd6e6b6e..7604574da666 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c @@ -1289,9 +1289,10 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev) int ret, irq; u32 val; - dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(&pdev->dev, struct cdns_dsi, input.bridge, + &cdns_dsi_bridge_funcs); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); platform_set_drvdata(pdev, dsi); @@ -1349,7 +1350,6 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev) * CDNS_DPI_INPUT. */ input->id = CDNS_DPI_INPUT; - input->bridge.funcs = &cdns_dsi_bridge_funcs; input->bridge.of_node = pdev->dev.of_node; /* Mask all interrupts before registering the IRQ handler. */ From 0d2577d8f01435b508dd315b6cf7696a8fe0482e Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:31 +0200 Subject: [PATCH 080/279] drm/bridge: megachips-stdpxxxx-ge-b850v3-fw: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Acked-by: Maxime Ripard Reviewed-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-5-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- .../gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) 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 15a5a1f644fc..81dde9ed7bcf 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -225,13 +225,11 @@ static int ge_b850v3_lvds_init(struct device *dev) if (ge_b850v3_lvds_ptr) goto success; - ge_b850v3_lvds_ptr = devm_kzalloc(dev, - sizeof(*ge_b850v3_lvds_ptr), - GFP_KERNEL); - - if (!ge_b850v3_lvds_ptr) { + ge_b850v3_lvds_ptr = devm_drm_bridge_alloc(dev, struct ge_b850v3_lvds, bridge, + &ge_b850v3_lvds_funcs); + if (IS_ERR(ge_b850v3_lvds_ptr)) { mutex_unlock(&ge_b850v3_lvds_dev_mutex); - return -ENOMEM; + return PTR_ERR(ge_b850v3_lvds_ptr); } success: @@ -264,7 +262,6 @@ static int ge_b850v3_register(void) struct device *dev = &stdp4028_i2c->dev; /* drm bridge initialization */ - ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs; ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; ge_b850v3_lvds_ptr->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; From 3cb4fec95ef6904d28c72e67ff3f5c748d86abb5 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:32 +0200 Subject: [PATCH 081/279] drm/bridge: nxp-ptn3460: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Acked-by: Maxime Ripard Reviewed-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-6-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/bridge/nxp-ptn3460.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index 25d7c415478b..7acb11f16dc1 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -261,10 +261,10 @@ static int ptn3460_probe(struct i2c_client *client) struct drm_bridge *panel_bridge; int ret; - ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL); - if (!ptn_bridge) { - return -ENOMEM; - } + ptn_bridge = devm_drm_bridge_alloc(dev, struct ptn3460_bridge, bridge, + &ptn3460_bridge_funcs); + if (IS_ERR(ptn_bridge)) + return PTR_ERR(ptn_bridge); panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); if (IS_ERR(panel_bridge)) @@ -300,7 +300,6 @@ static int ptn3460_probe(struct i2c_client *client) return ret; } - ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs; ptn_bridge->bridge.ops = DRM_BRIDGE_OP_EDID; ptn_bridge->bridge.type = DRM_MODE_CONNECTOR_LVDS; ptn_bridge->bridge.of_node = dev->of_node; From 9cdc50b20509e55855ed53ab831ba73fd059ade9 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:33 +0200 Subject: [PATCH 082/279] drm/bridge: sii902x: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Acked-by: Maxime Ripard Reviewed-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-7-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/bridge/sii902x.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 6de61d9fe064..882973e90062 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -1135,7 +1135,6 @@ static int sii902x_init(struct sii902x *sii902x) if (ret) goto err_unreg_audio; - sii902x->bridge.funcs = &sii902x_bridge_funcs; sii902x->bridge.of_node = dev->of_node; sii902x->bridge.timings = &default_sii902x_timings; sii902x->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; @@ -1170,9 +1169,9 @@ static int sii902x_probe(struct i2c_client *client) return -EIO; } - sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL); - if (!sii902x) - return -ENOMEM; + sii902x = devm_drm_bridge_alloc(dev, struct sii902x, bridge, &sii902x_bridge_funcs); + if (IS_ERR(sii902x)) + return PTR_ERR(sii902x); sii902x->i2c = client; sii902x->regmap = devm_regmap_init_i2c(client, &sii902x_regmap_config); From ac5869aae6f3155bc9b8746f659a432af43c8942 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:34 +0200 Subject: [PATCH 083/279] drm/omap: dss: dpi: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Acked-by: Maxime Ripard Reviewed-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-8-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/omapdrm/dss/dpi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 6eff97a09160..9f86db774c39 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -562,7 +562,6 @@ static const struct drm_bridge_funcs dpi_bridge_funcs = { 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; @@ -707,9 +706,9 @@ int dpi_init_port(struct dss_device *dss, struct platform_device *pdev, u32 datalines; int r; - dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL); - if (!dpi) - return -ENOMEM; + dpi = devm_drm_bridge_alloc(&pdev->dev, struct dpi_data, bridge, &dpi_bridge_funcs); + if (IS_ERR(dpi)) + return PTR_ERR(dpi); ep = of_graph_get_next_port_endpoint(port, NULL); if (!ep) From f35753992cda94d69e5e96224dc470516f2c45cd Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:35 +0200 Subject: [PATCH 084/279] drm/omap: dss: dsi: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Acked-by: Maxime Ripard Reviewed-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-9-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/omapdrm/dss/dsi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 91ee63bfe0bc..b129e5a8d791 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -4701,7 +4701,6 @@ static const struct drm_bridge_funcs dsi_bridge_funcs = { static void dsi_bridge_init(struct dsi_data *dsi) { - dsi->bridge.funcs = &dsi_bridge_funcs; dsi->bridge.of_node = dsi->host.dev->of_node; dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; @@ -4894,9 +4893,9 @@ static int dsi_probe(struct platform_device *pdev) unsigned int i; int r; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(dev, struct dsi_data, bridge, &dsi_bridge_funcs); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); dsi->dev = dev; dev_set_drvdata(dev, dsi); From 0241b190acb8399b23efe7188aa43bbbeed2500e Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:36 +0200 Subject: [PATCH 085/279] drm/omap: dss: hdmi4: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Switching from a non-devm to a devm allocation allows removing the kfree() in the remove function and in the probe error management code, and as a consequence to simplify the code flow by removing now unnecessary gotos. Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-10-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/omapdrm/dss/hdmi4.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index a3b22952fdc3..3cd612af2449 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -505,7 +505,6 @@ static const struct drm_bridge_funcs hdmi4_bridge_funcs = { 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; @@ -761,9 +760,9 @@ static int hdmi4_probe(struct platform_device *pdev) int irq; int r; - hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = devm_drm_bridge_alloc(&pdev->dev, struct omap_hdmi, bridge, &hdmi4_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); hdmi->pdev = pdev; @@ -774,25 +773,24 @@ static int hdmi4_probe(struct platform_device *pdev) r = hdmi4_probe_of(hdmi); if (r) - goto err_free; + return r; r = hdmi_wp_init(pdev, &hdmi->wp, 4); if (r) - goto err_free; + return r; r = hdmi_phy_init(pdev, &hdmi->phy, 4); if (r) - goto err_free; + return r; r = hdmi4_core_init(pdev, &hdmi->core); if (r) - goto err_free; + return r; irq = platform_get_irq(pdev, 0); if (irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_free; + return -ENODEV; } r = devm_request_threaded_irq(&pdev->dev, irq, @@ -800,7 +798,7 @@ static int hdmi4_probe(struct platform_device *pdev) IRQF_ONESHOT, "OMAP HDMI", hdmi); if (r) { DSSERR("HDMI IRQ request failed\n"); - goto err_free; + return r; } hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda"); @@ -808,7 +806,7 @@ static int hdmi4_probe(struct platform_device *pdev) r = PTR_ERR(hdmi->vdda_reg); if (r != -EPROBE_DEFER) DSSERR("can't get VDDA regulator\n"); - goto err_free; + return r; } pm_runtime_enable(&pdev->dev); @@ -827,8 +825,6 @@ static int hdmi4_probe(struct platform_device *pdev) hdmi4_uninit_output(hdmi); err_pm_disable: pm_runtime_disable(&pdev->dev); -err_free: - kfree(hdmi); return r; } @@ -841,8 +837,6 @@ static void hdmi4_remove(struct platform_device *pdev) hdmi4_uninit_output(hdmi); pm_runtime_disable(&pdev->dev); - - kfree(hdmi); } static const struct of_device_id hdmi_of_match[] = { From eb01c3cc550dfb9bdd021138e898fbf4a8ccd12f Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:37 +0200 Subject: [PATCH 086/279] drm/omap: dss: hdmi5: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Switching from a non-devm to a devm allocation allows removing the kfree() in the remove function and in the probe error management code, and as a consequence to simplify the code flow by removing now unnecessary gotos. Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-11-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/omapdrm/dss/hdmi5.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 0c98444d39a9..5636b3dfec1c 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -480,7 +480,6 @@ static const struct drm_bridge_funcs hdmi5_bridge_funcs = { 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; @@ -727,9 +726,9 @@ static int hdmi5_probe(struct platform_device *pdev) int irq; int r; - hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = devm_drm_bridge_alloc(&pdev->dev, struct omap_hdmi, bridge, &hdmi5_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); hdmi->pdev = pdev; @@ -740,25 +739,24 @@ static int hdmi5_probe(struct platform_device *pdev) r = hdmi5_probe_of(hdmi); if (r) - goto err_free; + return r; r = hdmi_wp_init(pdev, &hdmi->wp, 5); if (r) - goto err_free; + return r; r = hdmi_phy_init(pdev, &hdmi->phy, 5); if (r) - goto err_free; + return r; r = hdmi5_core_init(pdev, &hdmi->core); if (r) - goto err_free; + return r; irq = platform_get_irq(pdev, 0); if (irq < 0) { DSSERR("platform_get_irq failed\n"); - r = -ENODEV; - goto err_free; + return -ENODEV; } r = devm_request_threaded_irq(&pdev->dev, irq, @@ -766,7 +764,7 @@ static int hdmi5_probe(struct platform_device *pdev) IRQF_ONESHOT, "OMAP HDMI", hdmi); if (r) { DSSERR("HDMI IRQ request failed\n"); - goto err_free; + return r; } hdmi->vdda_reg = devm_regulator_get(&pdev->dev, "vdda"); @@ -774,7 +772,7 @@ static int hdmi5_probe(struct platform_device *pdev) r = PTR_ERR(hdmi->vdda_reg); if (r != -EPROBE_DEFER) DSSERR("can't get VDDA regulator\n"); - goto err_free; + return r; } pm_runtime_enable(&pdev->dev); @@ -793,8 +791,6 @@ static int hdmi5_probe(struct platform_device *pdev) hdmi5_uninit_output(hdmi); err_pm_disable: pm_runtime_disable(&pdev->dev); -err_free: - kfree(hdmi); return r; } @@ -807,8 +803,6 @@ static void hdmi5_remove(struct platform_device *pdev) hdmi5_uninit_output(hdmi); pm_runtime_disable(&pdev->dev); - - kfree(hdmi); } static const struct of_device_id hdmi_of_match[] = { From af509dfc0b7390a59cdcb64b161bf6c9c7d6cb2a Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:38 +0200 Subject: [PATCH 087/279] drm/omap: dss: sdi: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Switching from a non-devm to a devm allocation allows removing the kfree() in the remove function and in the probe error management code, and as a consequence to simplify the code flow by removing now unnecessary gotos. Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-12-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/omapdrm/dss/sdi.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index e78826e4b560..df4cbc683e2c 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -284,7 +284,6 @@ static const struct drm_bridge_funcs sdi_bridge_funcs = { 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; @@ -344,21 +343,19 @@ int sdi_init_port(struct dss_device *dss, struct platform_device *pdev, u32 datapairs; int r; - sdi = kzalloc(sizeof(*sdi), GFP_KERNEL); - if (!sdi) - return -ENOMEM; + sdi = devm_drm_bridge_alloc(&pdev->dev, struct sdi_device, bridge, &sdi_bridge_funcs); + if (IS_ERR(sdi)) + return PTR_ERR(sdi); ep = of_graph_get_next_port_endpoint(port, NULL); - if (!ep) { - r = 0; - goto err_free; - } + if (!ep) + return 0; r = of_property_read_u32(ep, "datapairs", &datapairs); of_node_put(ep); if (r) { DSSERR("failed to parse datapairs\n"); - goto err_free; + return r; } sdi->datapairs = datapairs; @@ -372,19 +369,14 @@ int sdi_init_port(struct dss_device *dss, struct platform_device *pdev, r = PTR_ERR(sdi->vdds_sdi_reg); if (r != -EPROBE_DEFER) DSSERR("can't get VDDS_SDI regulator\n"); - goto err_free; + return r; } r = sdi_init_output(sdi); if (r) - goto err_free; + return r; return 0; - -err_free: - kfree(sdi); - - return r; } void sdi_uninit_port(struct device_node *port) @@ -395,5 +387,4 @@ void sdi_uninit_port(struct device_node *port) return; sdi_uninit_output(sdi); - kfree(sdi); } From 7e61751b59576e5227d940a4d34f2f6b6400d192 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:39 +0200 Subject: [PATCH 088/279] drm/omap: dss: venc: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Switching from a non-devm to a devm allocation allows removing the kfree() in the remove function and in the probe error management code, and as a consequence to simplify the code flow by removing now unnecessary gotos. Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-13-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/omapdrm/dss/venc.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index 50349518eda1..9b5d53dc361e 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -664,7 +664,6 @@ static const struct drm_bridge_funcs venc_bridge_funcs = { 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; @@ -809,9 +808,9 @@ static int venc_probe(struct platform_device *pdev) struct venc_device *venc; int r; - venc = kzalloc(sizeof(*venc), GFP_KERNEL); - if (!venc) - return -ENOMEM; + venc = devm_drm_bridge_alloc(&pdev->dev, struct venc_device, bridge, &venc_bridge_funcs); + if (IS_ERR(venc)) + return PTR_ERR(venc); venc->pdev = pdev; @@ -824,26 +823,24 @@ static int venc_probe(struct platform_device *pdev) venc->config = &venc_config_pal_trm; venc->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(venc->base)) { - r = PTR_ERR(venc->base); - goto err_free; - } + if (IS_ERR(venc->base)) + return PTR_ERR(venc->base); venc->vdda_dac_reg = devm_regulator_get(&pdev->dev, "vdda"); if (IS_ERR(venc->vdda_dac_reg)) { r = PTR_ERR(venc->vdda_dac_reg); if (r != -EPROBE_DEFER) DSSERR("can't get VDDA_DAC regulator\n"); - goto err_free; + return r; } r = venc_get_clocks(venc); if (r) - goto err_free; + return r; r = venc_probe_of(venc); if (r) - goto err_free; + return r; pm_runtime_enable(&pdev->dev); @@ -861,8 +858,6 @@ static int venc_probe(struct platform_device *pdev) venc_uninit_output(venc); err_pm_disable: pm_runtime_disable(&pdev->dev); -err_free: - kfree(venc); return r; } @@ -875,8 +870,6 @@ static void venc_remove(struct platform_device *pdev) venc_uninit_output(venc); pm_runtime_disable(&pdev->dev); - - kfree(venc); } static __maybe_unused int venc_runtime_suspend(struct device *dev) From db17fbc2d4de215592296a984b688495b62319e5 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:40 +0200 Subject: [PATCH 089/279] drm/rcar-du: dsi: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Acked-by: Maxime Ripard Reviewed-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-14-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c index 7ab8be46c7f6..1af4c73f7a88 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c @@ -918,7 +918,6 @@ static int rcar_mipi_dsi_host_attach(struct mipi_dsi_host *host, } /* Initialize the DRM bridge. */ - dsi->bridge.funcs = &rcar_mipi_dsi_bridge_ops; dsi->bridge.of_node = dsi->dev->of_node; drm_bridge_add(&dsi->bridge); @@ -1004,9 +1003,10 @@ static int rcar_mipi_dsi_probe(struct platform_device *pdev) struct rcar_mipi_dsi *dsi; int ret; - dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); - if (dsi == NULL) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(&pdev->dev, struct rcar_mipi_dsi, bridge, + &rcar_mipi_dsi_bridge_ops); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); platform_set_drvdata(pdev, dsi); From ee81a4a27d3311c79d69b2f446c6528a491dfa26 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:41 +0200 Subject: [PATCH 090/279] drm/bridge: stm_lvds: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. Acked-by: Raphael Gallais-Pou Acked-by: Maxime Ripard Reviewed-by: Anusha Srivatsa Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-15-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/stm/lvds.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c index a3ae9a93ce66..07788e8d3d83 100644 --- a/drivers/gpu/drm/stm/lvds.c +++ b/drivers/gpu/drm/stm/lvds.c @@ -1049,9 +1049,9 @@ static int lvds_probe(struct platform_device *pdev) dev_dbg(dev, "Probing LVDS driver...\n"); - lvds = devm_kzalloc(dev, sizeof(*lvds), GFP_KERNEL); - if (!lvds) - return -ENOMEM; + lvds = devm_drm_bridge_alloc(dev, struct stm_lvds, lvds_bridge, &lvds_bridge_funcs); + if (IS_ERR(lvds)) + return PTR_ERR(lvds); lvds->dev = dev; @@ -1164,7 +1164,6 @@ static int lvds_probe(struct platform_device *pdev) goto err_lvds_probe; } - lvds->lvds_bridge.funcs = &lvds_bridge_funcs; lvds->lvds_bridge.of_node = dev->of_node; lvds->hw_version = lvds_read(lvds, LVDS_VERR); From 3d3f22799c89e6d0d210e0f666c77b3de11429d4 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:42 +0200 Subject: [PATCH 091/279] drm/sti: dvo: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. This driver allocates the DRM bridge separately from the main driver private struct, which prevents using the new devm_drm_bridge_alloc() API. Simplify the code by replacing the struct drm_bridge pointer with an embedded struct drm_bridge inside the private struct, to make use of the new API with the same code flow. Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-16-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/sti/sti_dvo.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c index 74a1eef4674e..7484d3c3f4ed 100644 --- a/drivers/gpu/drm/sti/sti_dvo.c +++ b/drivers/gpu/drm/sti/sti_dvo.c @@ -97,7 +97,7 @@ struct sti_dvo { struct dvo_config *config; bool enabled; struct drm_encoder *encoder; - struct drm_bridge *bridge; + struct drm_bridge bridge; }; struct sti_dvo_connector { @@ -439,7 +439,6 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) struct drm_encoder *encoder; struct sti_dvo_connector *connector; struct drm_connector *drm_connector; - struct drm_bridge *bridge; int err; /* Set the drm device handle */ @@ -455,20 +454,14 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) connector->dvo = dvo; - bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); - if (!bridge) - return -ENOMEM; + dvo->bridge.driver_private = dvo; + dvo->bridge.of_node = dvo->dev.of_node; + drm_bridge_add(&dvo->bridge); - bridge->driver_private = dvo; - bridge->funcs = &sti_dvo_bridge_funcs; - bridge->of_node = dvo->dev.of_node; - drm_bridge_add(bridge); - - err = drm_bridge_attach(encoder, bridge, NULL, 0); + err = drm_bridge_attach(encoder, &dvo->bridge, NULL, 0); if (err) return err; - dvo->bridge = bridge; connector->encoder = encoder; dvo->encoder = encoder; @@ -490,7 +483,7 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data) return 0; err_sysfs: - drm_bridge_remove(bridge); + drm_bridge_remove(&dvo->bridge); return -EINVAL; } @@ -499,7 +492,7 @@ static void sti_dvo_unbind(struct device *dev, { struct sti_dvo *dvo = dev_get_drvdata(dev); - drm_bridge_remove(dvo->bridge); + drm_bridge_remove(&dvo->bridge); } static const struct component_ops sti_dvo_ops = { @@ -515,10 +508,10 @@ static int sti_dvo_probe(struct platform_device *pdev) DRM_INFO("%s\n", __func__); - dvo = devm_kzalloc(dev, sizeof(*dvo), GFP_KERNEL); - if (!dvo) { - DRM_ERROR("Failed to allocate memory for DVO\n"); - return -ENOMEM; + dvo = devm_drm_bridge_alloc(dev, struct sti_dvo, bridge, &sti_dvo_bridge_funcs); + if (IS_ERR(dvo)) { + DRM_ERROR("Failed to allocate DVO\n"); + return PTR_ERR(dvo); } dvo->dev = pdev->dev; From afb903c01b2b7af382392024fb02bbfea62696f7 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:43 +0200 Subject: [PATCH 092/279] drm: zynqmp_dp: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. This driver has a peculiar structure. zynqmp_dpsub.c is the actual driver, which delegates to a submodule (zynqmp_dp.c) the allocation of a sub-structure embedding the drm_bridge and its initialization, however it does not delegate the drm_bridge_add(). Hence, following carefully the code flow, it is correct to change the allocation function and .funcs assignment in the submodule, while the drm_bridge_add() is not in that submodule. Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-17-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 31 ++++++++++------------------- drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 1 - 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 238cbb49963e..02e1feaa6115 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -2439,9 +2439,9 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) struct zynqmp_dp *dp; int ret; - dp = kzalloc(sizeof(*dp), GFP_KERNEL); - if (!dp) - return -ENOMEM; + dp = devm_drm_bridge_alloc(&pdev->dev, struct zynqmp_dp, bridge, &zynqmp_dp_bridge_funcs); + if (IS_ERR(dp)) + return PTR_ERR(dp); dp->dev = &pdev->dev; dp->dpsub = dpsub; @@ -2454,31 +2454,25 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) /* Acquire all resources (IOMEM, IRQ and PHYs). */ dp->iomem = devm_platform_ioremap_resource_byname(pdev, "dp"); - if (IS_ERR(dp->iomem)) { - ret = PTR_ERR(dp->iomem); - goto err_free; - } + if (IS_ERR(dp->iomem)) + return PTR_ERR(dp->iomem); dp->irq = platform_get_irq(pdev, 0); - if (dp->irq < 0) { - ret = dp->irq; - goto err_free; - } + if (dp->irq < 0) + return dp->irq; dp->reset = devm_reset_control_get(dp->dev, NULL); - if (IS_ERR(dp->reset)) { - ret = dev_err_probe(dp->dev, PTR_ERR(dp->reset), + if (IS_ERR(dp->reset)) + return dev_err_probe(dp->dev, PTR_ERR(dp->reset), "failed to get reset\n"); - goto err_free; - } ret = zynqmp_dp_reset(dp, true); if (ret < 0) - goto err_free; + return ret; ret = zynqmp_dp_reset(dp, false); if (ret < 0) - goto err_free; + return ret; ret = zynqmp_dp_phy_probe(dp); if (ret) @@ -2486,7 +2480,6 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) /* Initialize the bridge. */ bridge = &dp->bridge; - bridge->funcs = &zynqmp_dp_bridge_funcs; bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; bridge->type = DRM_MODE_CONNECTOR_DisplayPort; @@ -2539,8 +2532,6 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) zynqmp_dp_phy_exit(dp); err_reset: zynqmp_dp_reset(dp, true); -err_free: - kfree(dp); return ret; } diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c index 3a9544b97bc5..2764c4b17c5e 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c @@ -180,7 +180,6 @@ static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub) void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub) { kfree(dpsub->disp); - kfree(dpsub->dp); kfree(dpsub); } From 5164553d739ef45b2c69c8cfe88b9ef26c9a0035 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:46 +0200 Subject: [PATCH 093/279] drm/bridge: add devm_drm_put_bridge() Bridges obtained via devm_drm_bridge_alloc(dev, ...) will be put when the requesting device (@dev) is removed. However drivers which obtained them may need to put the obtained reference explicitly. One such case is if they bind the devm removal action to a different device than the one implemented by the driver itself and which might be removed at a different time, such as bridge/panel.c. Add devm_drm_put_bridge() to manually release a devm-obtained bridge in such cases. This function is considered only a temporary workaround until the panel bridge is reworked and should be removed afterwards. Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-20-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/drm_bridge.c | 17 +++++++++++++++++ include/drm/drm_bridge.h | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index b4c89ec01998..d0e81639927a 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1392,6 +1392,23 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np) EXPORT_SYMBOL(of_drm_find_bridge); #endif +/** + * devm_drm_put_bridge - Release a bridge reference obtained via devm + * @dev: device that got the bridge via devm + * @bridge: pointer to a struct drm_bridge obtained via devm + * + * Same as drm_bridge_put() for bridge pointers obtained via devm functions + * such as devm_drm_bridge_alloc(). + * + * This function is a temporary workaround and MUST NOT be used. Manual + * handling of bridge lifetime is inherently unsafe. + */ +void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge) +{ + devm_release_action(dev, drm_bridge_put_void, bridge); +} +EXPORT_SYMBOL(devm_drm_put_bridge); + static void drm_bridge_debugfs_show_bridge(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index cc9f7df38102..464da28f9134 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1311,6 +1311,8 @@ static inline struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, return ERR_PTR(-ENODEV); } +static inline void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge) {} + static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node, u32 port, @@ -1320,6 +1322,8 @@ static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, } #endif +void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge); + void drm_bridge_debugfs_params(struct dentry *root); void drm_bridge_debugfs_encoder_params(struct dentry *root, struct drm_encoder *encoder); From 6ad88bf9e74dae83c992d8a16683360117b7e2d8 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:47 +0200 Subject: [PATCH 094/279] drm/bridge: panel: convert to devm_drm_bridge_alloc() API This is the new API for allocating DRM bridges. The devm lifetime management of this driver is peculiar. The underlying device for the panel_bridge is the panel, and the devm lifetime is tied the panel device (panel->dev). However the panel_bridge allocation is not performed by the panel driver, but rather by a separate entity (typically the previous bridge in the encoder chain). Thus when that separate entity is destroyed, the panel_bridge is not removed automatically by devm, so it is rather done explicitly by calling drm_panel_bridge_remove(). This is the function that does devm_kfree() the panel_bridge in current code, so update it as well to put the bridge reference instead. This is a temporary solution until the panel lifetime is reworked, which should make this workaround unnecessary, so add a comment to clarify that. Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-21-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/bridge/panel.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 79b009ab9396..6cbbfb1381a4 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -287,15 +287,14 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, if (!panel) return ERR_PTR(-EINVAL); - panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge), - GFP_KERNEL); - if (!panel_bridge) - return ERR_PTR(-ENOMEM); + panel_bridge = devm_drm_bridge_alloc(panel->dev, struct panel_bridge, bridge, + &panel_bridge_bridge_funcs); + if (IS_ERR(panel_bridge)) + return (void *)panel_bridge; panel_bridge->connector_type = connector_type; panel_bridge->panel = panel; - panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; panel_bridge->bridge.of_node = panel->dev->of_node; panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; panel_bridge->bridge.type = connector_type; @@ -327,7 +326,8 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge) panel_bridge = drm_bridge_to_panel_bridge(bridge); drm_bridge_remove(bridge); - devm_kfree(panel_bridge->panel->dev, bridge); + /* TODO remove this after reworking panel_bridge lifetime */ + devm_drm_put_bridge(panel_bridge->panel->dev, bridge); } EXPORT_SYMBOL(drm_panel_bridge_remove); From a3436f63aa4f93b043a970cc72a196a501191ecc Mon Sep 17 00:00:00 2001 From: Langyan Ye Date: Wed, 21 May 2025 17:37:43 +0800 Subject: [PATCH 095/279] drm/panel-edp: Add KDC KD116N3730A05 Add support for the KDC KD116N3730A05, pleace the EDID here for subsequent reference. 00 ff ff ff ff ff ff 00 2c 83 20 12 00 00 00 00 30 22 01 04 95 1a 0e 78 03 3a 75 9b 5d 5b 96 28 19 50 54 00 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 09 1e 56 dc 50 00 28 30 30 20 36 00 00 90 10 00 00 1a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe 00 4b 44 31 31 36 4e 33 37 33 30 41 30 35 00 e2 Signed-off-by: Langyan Ye Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20250521093743.1057466-1-yelangyan@huaqin.corp-partner.google.com --- drivers/gpu/drm/panel/panel-edp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 9adbe0f11421..5426648e7116 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -2007,6 +2007,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('K', 'D', 'C', 0x044f, &delay_200_500_e50, "KD116N9-30NH-F3"), EDP_PANEL_ENTRY('K', 'D', 'C', 0x05f1, &delay_200_500_e80_d50, "KD116N5-30NV-G7"), EDP_PANEL_ENTRY('K', 'D', 'C', 0x0809, &delay_200_500_e50, "KD116N2930A15"), + EDP_PANEL_ENTRY('K', 'D', 'C', 0x1220, &delay_200_500_e50, "KD116N3730A05"), EDP_PANEL_ENTRY('L', 'G', 'D', 0x0000, &delay_200_500_e200_d200, "Unknown"), EDP_PANEL_ENTRY('L', 'G', 'D', 0x048d, &delay_200_500_e200_d200, "Unknown"), From ee1855582e5e0f8d73428875b91a30785bcc4b7c Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Thu, 22 May 2025 09:12:58 +0200 Subject: [PATCH 096/279] drm/bridge: fix build with CONFIG_OF=n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5164553d739e ("drm/bridge: add devm_drm_put_bridge()") adds two declarations for devm_drm_put_bridge(): 1) an inline declaration in the #else branch of '#if defined(CONFIG_OF)...' 2) one outside of the same #if This results in a build failure with CONFIG_OF=n: ../drivers/gpu/drm/drm_bridge.c:1406:6: error: redefinition of ‘devm_drm_put_bridge’ The function has nothing to do with OF, thus fix by removing declaration 1. Fixes: 5164553d739e ("drm/bridge: add devm_drm_put_bridge()") Reported-by: Ville Syrjala Closes: https://oftc.catirclogs.org/dri-devel/2025-05-21#34288266; Tested-by: Chaitanya Kumar Borah Reviewed-by: Chaitanya Kumar Borah Link: https://lore.kernel.org/r/20250522-devm_drm_put_bridge-fix-non-of-build-v1-1-a05234dea046@bootlin.com Signed-off-by: Luca Ceresoli --- include/drm/drm_bridge.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 464da28f9134..0af5db244db8 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -1311,8 +1311,6 @@ static inline struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, return ERR_PTR(-ENODEV); } -static inline void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge) {} - static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node, u32 port, From 370f86bc07bf8aa7f74cea5f6faa51ca1eb92b6f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 22 May 2025 12:34:44 +0200 Subject: [PATCH 097/279] drm: renesas: rcar-du: use proper naming for R-Car Not RCAR, but R-Car. Signed-off-by: Wolfram Sang Reviewed-by: Kieran Bingham Reviewed-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20250522103530.51972-2-wsa+renesas@sang-engineering.com Signed-off-by: Tomi Valkeinen --- drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h index f9893d7d6dfc..e9e59c5e70d5 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.h @@ -16,7 +16,7 @@ struct rcar_du_format_info; struct rcar_du_group; /* - * The RCAR DU has 8 hardware planes, shared between primary and overlay planes. + * The R-Car DU has 8 hardware planes, shared between primary and overlay planes. * As using overlay planes requires at least one of the CRTCs being enabled, no * more than 7 overlay planes can be available. We thus create 1 primary plane * per CRTC and 7 overlay planes, for a total of up to 9 KMS planes. From 9528e54198f29548b18b0a5b343a31724e83c68b Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 22 May 2025 13:00:36 +0300 Subject: [PATCH 098/279] drm/panel: abstract of_panel_find() Add a helper to wrap OF-specific calls in drm_panel_add_follower() in preparation for adding an ACPI equivalent in the future. No functional changes. Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250522100036.2529624-1-jani.nikula@intel.com Signed-off-by: Jani Nikula --- drivers/gpu/drm/drm_panel.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index 650de4da0853..fee65dc65979 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -473,6 +473,21 @@ int of_drm_get_panel_orientation(const struct device_node *np, EXPORT_SYMBOL(of_drm_get_panel_orientation); #endif +static struct drm_panel *of_find_panel(struct device *follower_dev) +{ + struct device_node *panel_np; + struct drm_panel *panel; + + panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0); + if (!panel_np) + return ERR_PTR(-ENODEV); + + panel = of_drm_find_panel(panel_np); + of_node_put(panel_np); + + return panel; +} + /** * drm_is_panel_follower() - Check if the device is a panel follower * @dev: The 'struct device' to check @@ -518,16 +533,10 @@ EXPORT_SYMBOL(drm_is_panel_follower); int drm_panel_add_follower(struct device *follower_dev, struct drm_panel_follower *follower) { - struct device_node *panel_np; struct drm_panel *panel; int ret; - panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0); - if (!panel_np) - return -ENODEV; - - panel = of_drm_find_panel(panel_np); - of_node_put(panel_np); + panel = of_find_panel(follower_dev); if (IS_ERR(panel)) return PTR_ERR(panel); From 9c399719cfb98fd92c7b76dcd57098e5e3ca5cda Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:28 +0200 Subject: [PATCH 099/279] drm: convert many bridge drivers from devm_kzalloc() to devm_drm_bridge_alloc() API devm_drm_bridge_alloc() is the new API to be used for allocating (and partially initializing) a private driver struct embedding a struct drm_bridge. For many drivers having a simple code flow in the probe function, this commit does a mass conversion automatically with the following semantic patch. The changes have been reviewed manually for correctness as well as to find any false positives. The patch has been applied with the explicit exclusion of bridge/panel.c, handled by a separate patch. After applying the semantic patch, manually fixed these issues: - 4 drivers need ERR_CAST() instead of PTR_ERR() as the function calling devm_drm_bridge_alloc() returns a pointer - re-added empty lines and comments that the script had removed but that should stay @@ type T; identifier C; identifier BR; expression DEV; expression FUNCS; @@ -T *C; +T *C; ... ( -C = devm_kzalloc(DEV, ...); -if (!C) - return -ENOMEM; +C = devm_drm_bridge_alloc(DEV, T, BR, FUNCS); +if (IS_ERR(C)) + return PTR_ERR(C); | -C = devm_kzalloc(DEV, ...); -if (!C) - return ERR_PTR(-ENOMEM); +C = devm_drm_bridge_alloc(DEV, T, BR, FUNCS); +if (IS_ERR(C)) + return PTR_ERR(C); ) ... -C->BR.funcs = FUNCS; Reviewed-by: Manikandan Muralidharan # microchip-lvds.c Reviewed-by: Douglas Anderson # parade-ps8640 Tested-by: Douglas Anderson # parade-ps8640 Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-2-b8bc1f16d7aa@bootlin.com [Luca: fixed trivial patch conflict in adv7511_drv.c while applying] Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/adp/adp-mipi.c | 8 ++++---- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 8 ++++---- drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c | 9 ++++----- drivers/gpu/drm/bridge/aux-bridge.c | 8 ++++---- drivers/gpu/drm/bridge/aux-hpd-bridge.c | 9 +++++---- drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c | 8 ++++---- drivers/gpu/drm/bridge/chipone-icn6211.c | 8 ++++---- drivers/gpu/drm/bridge/chrontel-ch7033.c | 8 ++++---- drivers/gpu/drm/bridge/cros-ec-anx7688.c | 8 ++++---- drivers/gpu/drm/bridge/fsl-ldb.c | 7 +++---- drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c | 8 ++++---- drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c | 8 ++++---- drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c | 8 ++++---- drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c | 8 ++++---- drivers/gpu/drm/bridge/ite-it6263.c | 8 ++++---- drivers/gpu/drm/bridge/ite-it6505.c | 8 ++++---- drivers/gpu/drm/bridge/ite-it66121.c | 8 ++++---- drivers/gpu/drm/bridge/lontium-lt8912b.c | 8 ++++---- drivers/gpu/drm/bridge/lontium-lt9211.c | 7 +++---- drivers/gpu/drm/bridge/lontium-lt9611.c | 8 ++++---- drivers/gpu/drm/bridge/lvds-codec.c | 9 ++++----- drivers/gpu/drm/bridge/microchip-lvds.c | 8 ++++---- drivers/gpu/drm/bridge/nwl-dsi.c | 8 ++++---- drivers/gpu/drm/bridge/parade-ps8622.c | 8 ++++---- drivers/gpu/drm/bridge/parade-ps8640.c | 8 ++++---- drivers/gpu/drm/bridge/sii9234.c | 8 ++++---- drivers/gpu/drm/bridge/sil-sii8620.c | 8 ++++---- drivers/gpu/drm/bridge/simple-bridge.c | 8 ++++---- drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 8 ++++---- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 8 ++++---- drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c | 8 ++++---- drivers/gpu/drm/bridge/tc358762.c | 8 ++++---- drivers/gpu/drm/bridge/tc358764.c | 8 ++++---- drivers/gpu/drm/bridge/tc358768.c | 8 ++++---- drivers/gpu/drm/bridge/tc358775.c | 8 ++++---- drivers/gpu/drm/bridge/thc63lvd1024.c | 8 ++++---- drivers/gpu/drm/bridge/ti-dlpc3433.c | 8 ++++---- drivers/gpu/drm/bridge/ti-tdp158.c | 8 ++++---- drivers/gpu/drm/bridge/ti-tfp410.c | 8 ++++---- drivers/gpu/drm/bridge/ti-tpd12s015.c | 8 ++++---- drivers/gpu/drm/mediatek/mtk_dp.c | 8 ++++---- drivers/gpu/drm/mediatek/mtk_dpi.c | 8 ++++---- drivers/gpu/drm/mediatek/mtk_dsi.c | 8 ++++---- drivers/gpu/drm/mediatek/mtk_hdmi.c | 8 ++++---- drivers/gpu/drm/meson/meson_encoder_cvbs.c | 10 ++++++---- drivers/gpu/drm/meson/meson_encoder_dsi.c | 10 ++++++---- drivers/gpu/drm/meson/meson_encoder_hdmi.c | 10 ++++++---- drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c | 8 ++++---- drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 8 ++++---- 49 files changed, 201 insertions(+), 198 deletions(-) diff --git a/drivers/gpu/drm/adp/adp-mipi.c b/drivers/gpu/drm/adp/adp-mipi.c index 2b60128e2c69..cba7d32150a9 100644 --- a/drivers/gpu/drm/adp/adp-mipi.c +++ b/drivers/gpu/drm/adp/adp-mipi.c @@ -229,9 +229,10 @@ static int adp_mipi_probe(struct platform_device *pdev) { struct adp_mipi_drv_private *adp; - adp = devm_kzalloc(&pdev->dev, sizeof(*adp), GFP_KERNEL); - if (!adp) - return -ENOMEM; + adp = devm_drm_bridge_alloc(&pdev->dev, struct adp_mipi_drv_private, + bridge, &adp_dsi_bridge_funcs); + if (IS_ERR(adp)) + return PTR_ERR(adp); adp->mipi = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(adp->mipi)) { @@ -241,7 +242,6 @@ static int adp_mipi_probe(struct platform_device *pdev) adp->dsi.dev = &pdev->dev; adp->dsi.ops = &adp_dsi_host_ops; - adp->bridge.funcs = &adp_dsi_bridge_funcs; adp->bridge.of_node = pdev->dev.of_node; adp->bridge.type = DRM_MODE_CONNECTOR_DSI; dev_set_drvdata(&pdev->dev, adp); diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 8b7548448615..022b6d9c2a1f 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -1153,9 +1153,10 @@ static int adv7511_probe(struct i2c_client *i2c) if (!dev->of_node) return -EINVAL; - adv7511 = devm_kzalloc(dev, sizeof(*adv7511), GFP_KERNEL); - if (!adv7511) - return -ENOMEM; + adv7511 = devm_drm_bridge_alloc(dev, struct adv7511, bridge, + &adv7511_bridge_funcs); + if (IS_ERR(adv7511)) + return PTR_ERR(adv7511); adv7511->i2c_main = i2c; adv7511->powered = false; @@ -1255,7 +1256,6 @@ static int adv7511_probe(struct i2c_client *i2c) regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, ADV7511_CEC_CTRL_POWER_DOWN); - adv7511->bridge.funcs = &adv7511_bridge_funcs; adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HDMI | diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index a83020d6576f..ba0fc149a9e7 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -1193,9 +1193,10 @@ static int anx78xx_i2c_probe(struct i2c_client *client) bool found = false; int err; - anx78xx = devm_kzalloc(&client->dev, sizeof(*anx78xx), GFP_KERNEL); - if (!anx78xx) - return -ENOMEM; + anx78xx = devm_drm_bridge_alloc(&client->dev, struct anx78xx, bridge, + &anx78xx_bridge_funcs); + if (IS_ERR(anx78xx)) + return PTR_ERR(anx78xx); pdata = &anx78xx->pdata; @@ -1306,8 +1307,6 @@ static int anx78xx_i2c_probe(struct i2c_client *client) goto err_poweroff; } - anx78xx->bridge.funcs = &anx78xx_bridge_funcs; - drm_bridge_add(&anx78xx->bridge); /* If cable is pulled out, just poweroff and wait for HPD event */ diff --git a/drivers/gpu/drm/bridge/aux-bridge.c b/drivers/gpu/drm/bridge/aux-bridge.c index c179b86d208f..5b219e3b87b1 100644 --- a/drivers/gpu/drm/bridge/aux-bridge.c +++ b/drivers/gpu/drm/bridge/aux-bridge.c @@ -109,9 +109,10 @@ static int drm_aux_bridge_probe(struct auxiliary_device *auxdev, { struct drm_aux_bridge_data *data; - data = devm_kzalloc(&auxdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; + data = devm_drm_bridge_alloc(&auxdev->dev, struct drm_aux_bridge_data, + bridge, &drm_aux_bridge_funcs); + if (IS_ERR(data)) + return PTR_ERR(data); data->dev = &auxdev->dev; data->next_bridge = devm_drm_of_get_bridge(&auxdev->dev, auxdev->dev.of_node, 0, 0); @@ -119,7 +120,6 @@ static int drm_aux_bridge_probe(struct auxiliary_device *auxdev, return dev_err_probe(&auxdev->dev, PTR_ERR(data->next_bridge), "failed to acquire drm_bridge\n"); - data->bridge.funcs = &drm_aux_bridge_funcs; data->bridge.of_node = data->dev->of_node; /* passthrough data, allow everything */ diff --git a/drivers/gpu/drm/bridge/aux-hpd-bridge.c b/drivers/gpu/drm/bridge/aux-hpd-bridge.c index b3f588b71a7d..3eb411f874e4 100644 --- a/drivers/gpu/drm/bridge/aux-hpd-bridge.c +++ b/drivers/gpu/drm/bridge/aux-hpd-bridge.c @@ -171,12 +171,13 @@ static int drm_aux_hpd_bridge_probe(struct auxiliary_device *auxdev, { struct drm_aux_hpd_bridge_data *data; - data = devm_kzalloc(&auxdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; + data = devm_drm_bridge_alloc(&auxdev->dev, + struct drm_aux_hpd_bridge_data, bridge, + &drm_aux_hpd_bridge_funcs); + if (IS_ERR(data)) + return PTR_ERR(data); data->dev = &auxdev->dev; - data->bridge.funcs = &drm_aux_hpd_bridge_funcs; data->bridge.of_node = dev_get_platdata(data->dev); data->bridge.ops = DRM_BRIDGE_OP_HPD; data->bridge.type = id->driver_data; diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index b431e7efd1f0..cb5f5a8c539a 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -2389,9 +2389,10 @@ static int cdns_mhdp_probe(struct platform_device *pdev) int ret; int irq; - mhdp = devm_kzalloc(dev, sizeof(*mhdp), GFP_KERNEL); - if (!mhdp) - return -ENOMEM; + mhdp = devm_drm_bridge_alloc(dev, struct cdns_mhdp_device, bridge, + &cdns_mhdp_bridge_funcs); + if (IS_ERR(mhdp)) + return PTR_ERR(mhdp); clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) { @@ -2481,7 +2482,6 @@ static int cdns_mhdp_probe(struct platform_device *pdev) mhdp->display_fmt.bpc = 8; mhdp->bridge.of_node = pdev->dev.of_node; - mhdp->bridge.funcs = &cdns_mhdp_bridge_funcs; mhdp->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; mhdp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index 634c5b030667..814713c5bea9 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -691,9 +691,10 @@ static int chipone_common_probe(struct device *dev, struct chipone **icnr) struct chipone *icn; int ret; - icn = devm_kzalloc(dev, sizeof(struct chipone), GFP_KERNEL); - if (!icn) - return -ENOMEM; + icn = devm_drm_bridge_alloc(dev, struct chipone, bridge, + &chipone_bridge_funcs); + if (IS_ERR(icn)) + return PTR_ERR(icn); icn->dev = dev; @@ -701,7 +702,6 @@ static int chipone_common_probe(struct device *dev, struct chipone **icnr) if (ret) return ret; - icn->bridge.funcs = &chipone_bridge_funcs; icn->bridge.type = DRM_MODE_CONNECTOR_DPI; icn->bridge.of_node = dev->of_node; diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c index 210c45c1efd4..ab9274793356 100644 --- a/drivers/gpu/drm/bridge/chrontel-ch7033.c +++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c @@ -536,9 +536,10 @@ static int ch7033_probe(struct i2c_client *client) unsigned int val; int ret; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + priv = devm_drm_bridge_alloc(dev, struct ch7033_priv, bridge, + &ch7033_bridge_funcs); + if (IS_ERR(priv)) + return PTR_ERR(priv); dev_set_drvdata(dev, priv); @@ -575,7 +576,6 @@ static int ch7033_probe(struct i2c_client *client) } INIT_LIST_HEAD(&priv->bridge.list); - priv->bridge.funcs = &ch7033_bridge_funcs; priv->bridge.of_node = dev->of_node; drm_bridge_add(&priv->bridge); diff --git a/drivers/gpu/drm/bridge/cros-ec-anx7688.c b/drivers/gpu/drm/bridge/cros-ec-anx7688.c index c8abd9920fee..a35dae9b56e2 100644 --- a/drivers/gpu/drm/bridge/cros-ec-anx7688.c +++ b/drivers/gpu/drm/bridge/cros-ec-anx7688.c @@ -103,9 +103,10 @@ static int cros_ec_anx7688_bridge_probe(struct i2c_client *client) u8 buffer[4]; int ret; - anx7688 = devm_kzalloc(dev, sizeof(*anx7688), GFP_KERNEL); - if (!anx7688) - return -ENOMEM; + anx7688 = devm_drm_bridge_alloc(dev, struct cros_ec_anx7688, bridge, + &cros_ec_anx7688_bridge_funcs); + if (IS_ERR(anx7688)) + return PTR_ERR(anx7688); anx7688->client = client; i2c_set_clientdata(client, anx7688); @@ -153,7 +154,6 @@ static int cros_ec_anx7688_bridge_probe(struct i2c_client *client) DRM_WARN("Old ANX7688 FW version (0x%04x), not filtering\n", fw_version); - anx7688->bridge.funcs = &cros_ec_anx7688_bridge_funcs; drm_bridge_add(&anx7688->bridge); return 0; diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c index 2cb6dfc7a6d3..5c3cf37200bc 100644 --- a/drivers/gpu/drm/bridge/fsl-ldb.c +++ b/drivers/gpu/drm/bridge/fsl-ldb.c @@ -298,16 +298,15 @@ static int fsl_ldb_probe(struct platform_device *pdev) struct fsl_ldb *fsl_ldb; int dual_link; - fsl_ldb = devm_kzalloc(dev, sizeof(*fsl_ldb), GFP_KERNEL); - if (!fsl_ldb) - return -ENOMEM; + fsl_ldb = devm_drm_bridge_alloc(dev, struct fsl_ldb, bridge, &funcs); + if (IS_ERR(fsl_ldb)) + return PTR_ERR(fsl_ldb); fsl_ldb->devdata = of_device_get_match_data(dev); if (!fsl_ldb->devdata) return -EINVAL; fsl_ldb->dev = &pdev->dev; - fsl_ldb->bridge.funcs = &funcs; fsl_ldb->bridge.of_node = dev->of_node; fsl_ldb->clk = devm_clk_get(dev, "ldb"); diff --git a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c b/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c index f072c6ed39ef..989bc497b050 100644 --- a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c +++ b/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c @@ -59,9 +59,10 @@ struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev, struct imx_legacy_bridge *imx_bridge; int ret; - imx_bridge = devm_kzalloc(dev, sizeof(*imx_bridge), GFP_KERNEL); - if (!imx_bridge) - return ERR_PTR(-ENOMEM); + imx_bridge = devm_drm_bridge_alloc(dev, struct imx_legacy_bridge, + base, &imx_legacy_bridge_funcs); + if (IS_ERR(imx_bridge)) + return ERR_CAST(imx_bridge); ret = of_get_drm_display_mode(np, &imx_bridge->mode, @@ -72,7 +73,6 @@ struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev, imx_bridge->mode.type |= DRM_MODE_TYPE_DRIVER; - imx_bridge->base.funcs = &imx_legacy_bridge_funcs; imx_bridge->base.of_node = np; imx_bridge->base.ops = DRM_BRIDGE_OP_MODES; imx_bridge->base.type = type; diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c index 8a4fd7d77a8d..3a6f8587a257 100644 --- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c @@ -140,9 +140,10 @@ static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev) struct device_node *remote; struct imx8mp_hdmi_pvi *pvi; - pvi = devm_kzalloc(&pdev->dev, sizeof(*pvi), GFP_KERNEL); - if (!pvi) - return -ENOMEM; + pvi = devm_drm_bridge_alloc(&pdev->dev, struct imx8mp_hdmi_pvi, + bridge, &imx_hdmi_pvi_bridge_funcs); + if (IS_ERR(pvi)) + return PTR_ERR(pvi); platform_set_drvdata(pdev, pvi); pvi->dev = &pdev->dev; @@ -166,7 +167,6 @@ static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); /* Register the bridge. */ - pvi->bridge.funcs = &imx_hdmi_pvi_bridge_funcs; pvi->bridge.of_node = pdev->dev.of_node; pvi->bridge.timings = pvi->next_bridge->timings; diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c index e092c9ea99b0..e5943506981d 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c @@ -327,9 +327,10 @@ static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; int ret; - pl = devm_kzalloc(dev, sizeof(*pl), GFP_KERNEL); - if (!pl) - return -ENOMEM; + pl = devm_drm_bridge_alloc(dev, struct imx8qxp_pixel_link, bridge, + &imx8qxp_pixel_link_bridge_funcs); + if (IS_ERR(pl)) + return PTR_ERR(pl); ret = imx_scu_get_handle(&pl->ipc_handle); if (ret) { @@ -384,7 +385,6 @@ static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pl); pl->bridge.driver_private = pl; - pl->bridge.funcs = &imx8qxp_pixel_link_bridge_funcs; pl->bridge.of_node = np; drm_bridge_add(&pl->bridge); diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c index da138ab51b3b..111310acab2c 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c @@ -392,9 +392,10 @@ static int imx8qxp_pxl2dpi_bridge_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; int ret; - p2d = devm_kzalloc(dev, sizeof(*p2d), GFP_KERNEL); - if (!p2d) - return -ENOMEM; + p2d = devm_drm_bridge_alloc(dev, struct imx8qxp_pxl2dpi, bridge, + &imx8qxp_pxl2dpi_bridge_funcs); + if (IS_ERR(p2d)) + return PTR_ERR(p2d); p2d->regmap = syscon_node_to_regmap(np->parent); if (IS_ERR(p2d->regmap)) { @@ -441,7 +442,6 @@ static int imx8qxp_pxl2dpi_bridge_probe(struct platform_device *pdev) pm_runtime_enable(dev); p2d->bridge.driver_private = p2d; - p2d->bridge.funcs = &imx8qxp_pxl2dpi_bridge_funcs; p2d->bridge.of_node = np; drm_bridge_add(&p2d->bridge); diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c index a3a63a977b0a..c4eedf643f39 100644 --- a/drivers/gpu/drm/bridge/ite-it6263.c +++ b/drivers/gpu/drm/bridge/ite-it6263.c @@ -816,9 +816,10 @@ static int it6263_probe(struct i2c_client *client) struct it6263 *it; int ret; - it = devm_kzalloc(dev, sizeof(*it), GFP_KERNEL); - if (!it) - return -ENOMEM; + it = devm_drm_bridge_alloc(dev, struct it6263, bridge, + &it6263_bridge_funcs); + if (IS_ERR(it)) + return PTR_ERR(it); it->dev = dev; it->hdmi_i2c = client; @@ -866,7 +867,6 @@ static int it6263_probe(struct i2c_client *client) i2c_set_clientdata(client, it); - it->bridge.funcs = &it6263_bridge_funcs; it->bridge.of_node = dev->of_node; /* IT6263 chip doesn't support HPD interrupt. */ it->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 1383d1e21afe..b0dc9280d870 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -3583,9 +3583,10 @@ static int it6505_i2c_probe(struct i2c_client *client) struct extcon_dev *extcon; int err; - it6505 = devm_kzalloc(&client->dev, sizeof(*it6505), GFP_KERNEL); - if (!it6505) - return -ENOMEM; + it6505 = devm_drm_bridge_alloc(&client->dev, struct it6505, bridge, + &it6505_bridge_funcs); + if (IS_ERR(it6505)) + return PTR_ERR(it6505); mutex_init(&it6505->extcon_lock); mutex_init(&it6505->mode_lock); @@ -3660,7 +3661,6 @@ static int it6505_i2c_probe(struct i2c_client *client) it6505->aux.transfer = it6505_aux_transfer; drm_dp_aux_init(&it6505->aux); - it6505->bridge.funcs = &it6505_bridge_funcs; it6505->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; it6505->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 7b110ae53291..6494f0842793 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -1516,9 +1516,10 @@ static int it66121_probe(struct i2c_client *client) return -ENXIO; } - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct it66121_ctx, bridge, + &it66121_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ep = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0); if (!ep) @@ -1577,7 +1578,6 @@ static int it66121_probe(struct i2c_client *client) return -ENODEV; } - ctx->bridge.funcs = &it66121_bridge_funcs; ctx->bridge.of_node = dev->of_node; ctx->bridge.type = DRM_MODE_CONNECTOR_HDMIA; ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID; diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index 3e49d855b364..bd83228b0f0e 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -761,9 +761,10 @@ static int lt8912_probe(struct i2c_client *client) int ret = 0; struct device *dev = &client->dev; - lt = devm_kzalloc(dev, sizeof(struct lt8912), GFP_KERNEL); - if (!lt) - return -ENOMEM; + lt = devm_drm_bridge_alloc(dev, struct lt8912, bridge, + <8912_bridge_funcs); + if (IS_ERR(lt)) + return PTR_ERR(lt); lt->dev = dev; lt->i2c_client[0] = client; @@ -778,7 +779,6 @@ static int lt8912_probe(struct i2c_client *client) i2c_set_clientdata(client, lt); - lt->bridge.funcs = <8912_bridge_funcs; lt->bridge.of_node = dev->of_node; lt->bridge.ops = (DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT); diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index 9b2dac9bd63c..399fa7eebd49 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -727,9 +727,9 @@ static int lt9211_probe(struct i2c_client *client) struct lt9211 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct lt9211, bridge, <9211_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; @@ -755,7 +755,6 @@ static int lt9211_probe(struct i2c_client *client) dev_set_drvdata(dev, ctx); i2c_set_clientdata(client, ctx); - ctx->bridge.funcs = <9211_funcs; ctx->bridge.of_node = dev->of_node; drm_bridge_add(&ctx->bridge); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index a35a8b8ca89c..d6ee79c1e427 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -1072,9 +1072,10 @@ static int lt9611_probe(struct i2c_client *client) return -ENODEV; } - lt9611 = devm_kzalloc(dev, sizeof(*lt9611), GFP_KERNEL); - if (!lt9611) - return -ENOMEM; + lt9611 = devm_drm_bridge_alloc(dev, struct lt9611, bridge, + <9611_bridge_funcs); + if (IS_ERR(lt9611)) + return PTR_ERR(lt9611); lt9611->dev = dev; lt9611->client = client; @@ -1127,7 +1128,6 @@ static int lt9611_probe(struct i2c_client *client) /* Disable Audio InfoFrame, enabled by default */ regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, 0); - lt9611->bridge.funcs = <9611_bridge_funcs; lt9611->bridge.of_node = client->dev.of_node; lt9611->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_MODES | diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index 1646e454e0b0..e6a7147e141b 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -118,9 +118,10 @@ static int lvds_codec_probe(struct platform_device *pdev) u32 val; int ret; - lvds_codec = devm_kzalloc(dev, sizeof(*lvds_codec), GFP_KERNEL); - if (!lvds_codec) - return -ENOMEM; + lvds_codec = devm_drm_bridge_alloc(dev, struct lvds_codec, bridge, + &funcs); + if (IS_ERR(lvds_codec)) + return PTR_ERR(lvds_codec); lvds_codec->dev = &pdev->dev; lvds_codec->connector_type = (uintptr_t)of_device_get_match_data(dev); @@ -156,8 +157,6 @@ static int lvds_codec_probe(struct platform_device *pdev) if (IS_ERR(lvds_codec->panel_bridge)) return PTR_ERR(lvds_codec->panel_bridge); - lvds_codec->bridge.funcs = &funcs; - /* * Decoder input LVDS format is a property of the decoder chip or even * its strapping. Handle data-mapping the same way lvds-panel does. In diff --git a/drivers/gpu/drm/bridge/microchip-lvds.c b/drivers/gpu/drm/bridge/microchip-lvds.c index 1d4ae0097df8..9f4ff82bc6b4 100644 --- a/drivers/gpu/drm/bridge/microchip-lvds.c +++ b/drivers/gpu/drm/bridge/microchip-lvds.c @@ -157,9 +157,10 @@ static int mchp_lvds_probe(struct platform_device *pdev) if (!dev->of_node) return -ENODEV; - lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); - if (!lvds) - return -ENOMEM; + lvds = devm_drm_bridge_alloc(&pdev->dev, struct mchp_lvds, bridge, + &mchp_lvds_bridge_funcs); + if (IS_ERR(lvds)) + return PTR_ERR(lvds); lvds->dev = dev; @@ -192,7 +193,6 @@ static int mchp_lvds_probe(struct platform_device *pdev) lvds->bridge.of_node = dev->of_node; lvds->bridge.type = DRM_MODE_CONNECTOR_LVDS; - lvds->bridge.funcs = &mchp_lvds_bridge_funcs; dev_set_drvdata(dev, lvds); ret = devm_pm_runtime_enable(dev); diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index 55912ae11f46..2f7429b24fc2 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -1149,9 +1149,10 @@ static int nwl_dsi_probe(struct platform_device *pdev) struct nwl_dsi *dsi; int ret; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(dev, struct nwl_dsi, bridge, + &nwl_dsi_bridge_funcs); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); dsi->dev = dev; @@ -1180,7 +1181,6 @@ static int nwl_dsi_probe(struct platform_device *pdev) dsi->quirks = (uintptr_t)attr->data; dsi->bridge.driver_private = dsi; - dsi->bridge.funcs = &nwl_dsi_bridge_funcs; dsi->bridge.of_node = dev->of_node; dsi->bridge.timings = &nwl_dsi_timings; dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 8726fefc5c65..f879a1df077d 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -449,9 +449,10 @@ static int ps8622_probe(struct i2c_client *client) struct drm_bridge *panel_bridge; int ret; - ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL); - if (!ps8622) - return -ENOMEM; + ps8622 = devm_drm_bridge_alloc(dev, struct ps8622_bridge, bridge, + &ps8622_bridge_funcs); + if (IS_ERR(ps8622)) + return PTR_ERR(ps8622); panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); if (IS_ERR(panel_bridge)) @@ -509,7 +510,6 @@ static int ps8622_probe(struct i2c_client *client) ps8622->bl->props.brightness = PS8622_MAX_BRIGHTNESS; } - ps8622->bridge.funcs = &ps8622_bridge_funcs; ps8622->bridge.type = DRM_MODE_CONNECTOR_LVDS; ps8622->bridge.of_node = dev->of_node; drm_bridge_add(&ps8622->bridge); diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 2422ff68c104..825777a5758f 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -636,9 +636,10 @@ static int ps8640_probe(struct i2c_client *client) int ret; u32 i; - ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL); - if (!ps_bridge) - return -ENOMEM; + ps_bridge = devm_drm_bridge_alloc(dev, struct ps8640, bridge, + &ps8640_bridge_funcs); + if (IS_ERR(ps_bridge)) + return PTR_ERR(ps_bridge); mutex_init(&ps_bridge->aux_lock); @@ -662,7 +663,6 @@ static int ps8640_probe(struct i2c_client *client) if (IS_ERR(ps_bridge->gpio_reset)) return PTR_ERR(ps_bridge->gpio_reset); - ps_bridge->bridge.funcs = &ps8640_bridge_funcs; ps_bridge->bridge.of_node = dev->of_node; ps_bridge->bridge.type = DRM_MODE_CONNECTOR_eDP; diff --git a/drivers/gpu/drm/bridge/sii9234.c b/drivers/gpu/drm/bridge/sii9234.c index cd7837c9a6e0..bb1bed03eb5b 100644 --- a/drivers/gpu/drm/bridge/sii9234.c +++ b/drivers/gpu/drm/bridge/sii9234.c @@ -888,9 +888,10 @@ static int sii9234_probe(struct i2c_client *client) struct device *dev = &client->dev; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct sii9234, bridge, + &sii9234_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; mutex_init(&ctx->lock); @@ -921,7 +922,6 @@ static int sii9234_probe(struct i2c_client *client) i2c_set_clientdata(client, ctx); - ctx->bridge.funcs = &sii9234_bridge_funcs; ctx->bridge.of_node = dev->of_node; drm_bridge_add(&ctx->bridge); diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 3af650dc92a1..9e48ad39e1cc 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2291,9 +2291,10 @@ static int sii8620_probe(struct i2c_client *client) struct sii8620 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct sii8620, bridge, + &sii8620_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); ctx->dev = dev; mutex_init(&ctx->lock); @@ -2336,7 +2337,6 @@ static int sii8620_probe(struct i2c_client *client) i2c_set_clientdata(client, ctx); - ctx->bridge.funcs = &sii8620_bridge_funcs; ctx->bridge.of_node = dev->of_node; drm_bridge_add(&ctx->bridge); diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index 70db5b99e5bb..c66bd913e33a 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -168,9 +168,10 @@ static int simple_bridge_probe(struct platform_device *pdev) struct simple_bridge *sbridge; struct device_node *remote; - sbridge = devm_kzalloc(&pdev->dev, sizeof(*sbridge), GFP_KERNEL); - if (!sbridge) - return -ENOMEM; + sbridge = devm_drm_bridge_alloc(&pdev->dev, struct simple_bridge, + bridge, &simple_bridge_bridge_funcs); + if (IS_ERR(sbridge)) + return PTR_ERR(sbridge); sbridge->info = of_device_get_match_data(&pdev->dev); @@ -204,7 +205,6 @@ static int simple_bridge_probe(struct platform_device *pdev) "Unable to retrieve enable GPIO\n"); /* Register the bridge. */ - sbridge->bridge.funcs = &simple_bridge_bridge_funcs; sbridge->bridge.of_node = pdev->dev.of_node; sbridge->bridge.timings = sbridge->info->timings; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index 5e5f8c2f95be..94dddaf49b3c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -1045,9 +1045,10 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, return ERR_PTR(-ENODEV); } - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return ERR_PTR(-ENOMEM); + hdmi = devm_drm_bridge_alloc(dev, struct dw_hdmi_qp, bridge, + &dw_hdmi_qp_bridge_funcs); + if (IS_ERR(hdmi)) + return ERR_CAST(hdmi); hdmi->dev = dev; @@ -1073,7 +1074,6 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, return ERR_PTR(ret); hdmi->bridge.driver_private = hdmi; - hdmi->bridge.funcs = &dw_hdmi_qp_bridge_funcs; hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HDMI | diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index b08ada920a50..c0dc0f2976b9 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -1194,9 +1194,10 @@ __dw_mipi_dsi_probe(struct platform_device *pdev, struct dw_mipi_dsi *dsi; int ret; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return ERR_PTR(-ENOMEM); + dsi = devm_drm_bridge_alloc(dev, struct dw_mipi_dsi, bridge, + &dw_mipi_dsi_bridge_funcs); + if (IS_ERR(dsi)) + return ERR_CAST(dsi); dsi->dev = dev; dsi->plat_data = plat_data; @@ -1265,7 +1266,6 @@ __dw_mipi_dsi_probe(struct platform_device *pdev, } dsi->bridge.driver_private = dsi; - dsi->bridge.funcs = &dw_mipi_dsi_bridge_funcs; dsi->bridge.of_node = pdev->dev.of_node; return dsi; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c index c76f5f2e74d1..fc91aca95d12 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c @@ -914,9 +914,10 @@ __dw_mipi_dsi2_probe(struct platform_device *pdev, struct dw_mipi_dsi2 *dsi2; int ret; - dsi2 = devm_kzalloc(dev, sizeof(*dsi2), GFP_KERNEL); - if (!dsi2) - return ERR_PTR(-ENOMEM); + dsi2 = devm_drm_bridge_alloc(dev, struct dw_mipi_dsi2, bridge, + &dw_mipi_dsi2_bridge_funcs); + if (IS_ERR(dsi2)) + return ERR_CAST(dsi2); dsi2->dev = dev; dsi2->plat_data = plat_data; @@ -981,7 +982,6 @@ __dw_mipi_dsi2_probe(struct platform_device *pdev, } dsi2->bridge.driver_private = dsi2; - dsi2->bridge.funcs = &dw_mipi_dsi2_bridge_funcs; dsi2->bridge.of_node = pdev->dev.of_node; return dsi2; diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c index edf01476f2ef..98df3e667d4a 100644 --- a/drivers/gpu/drm/bridge/tc358762.c +++ b/drivers/gpu/drm/bridge/tc358762.c @@ -265,9 +265,10 @@ static int tc358762_probe(struct mipi_dsi_device *dsi) struct tc358762 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(struct tc358762), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct tc358762, bridge, + &tc358762_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); @@ -288,7 +289,6 @@ static int tc358762_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - ctx->bridge.funcs = &tc358762_bridge_funcs; ctx->bridge.type = DRM_MODE_CONNECTOR_DPI; ctx->bridge.of_node = dev->of_node; ctx->bridge.pre_enable_prev_first = true; diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index 3f76c890fad9..084e9d898e22 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -347,9 +347,10 @@ static int tc358764_probe(struct mipi_dsi_device *dsi) struct tc358764 *ctx; int ret; - ctx = devm_kzalloc(dev, sizeof(struct tc358764), GFP_KERNEL); - if (!ctx) - return -ENOMEM; + ctx = devm_drm_bridge_alloc(dev, struct tc358764, bridge, + &tc358764_bridge_funcs); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); mipi_dsi_set_drvdata(dsi, ctx); @@ -368,7 +369,6 @@ static int tc358764_probe(struct mipi_dsi_device *dsi) if (ret < 0) return ret; - ctx->bridge.funcs = &tc358764_bridge_funcs; ctx->bridge.of_node = dev->of_node; ctx->bridge.pre_enable_prev_first = true; diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 063f217a17b6..fbdc44e16229 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -1287,9 +1287,10 @@ static int tc358768_i2c_probe(struct i2c_client *client) if (!np) return -ENODEV; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + priv = devm_drm_bridge_alloc(dev, struct tc358768_priv, bridge, + &tc358768_bridge_funcs); + if (IS_ERR(priv)) + return PTR_ERR(priv); dev_set_drvdata(dev, priv); priv->dev = dev; @@ -1321,7 +1322,6 @@ static int tc358768_i2c_probe(struct i2c_client *client) priv->dsi_host.dev = dev; priv->dsi_host.ops = &tc358768_dsi_host_ops; - priv->bridge.funcs = &tc358768_bridge_funcs; priv->bridge.timings = &default_tc358768_timings; priv->bridge.of_node = np; diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 1b10e6ee1724..366b12db0e7c 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -659,9 +659,10 @@ static int tc_probe(struct i2c_client *client) struct tc_data *tc; int ret; - tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); - if (!tc) - return -ENOMEM; + tc = devm_drm_bridge_alloc(dev, struct tc_data, bridge, + &tc_bridge_funcs); + if (IS_ERR(tc)) + return PTR_ERR(tc); tc->dev = dev; tc->i2c = client; @@ -701,7 +702,6 @@ static int tc_probe(struct i2c_client *client) return ret; } - tc->bridge.funcs = &tc_bridge_funcs; tc->bridge.of_node = dev->of_node; tc->bridge.pre_enable_prev_first = true; drm_bridge_add(&tc->bridge); diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c index e2fc78adebcf..2cb7cd0c0608 100644 --- a/drivers/gpu/drm/bridge/thc63lvd1024.c +++ b/drivers/gpu/drm/bridge/thc63lvd1024.c @@ -181,9 +181,10 @@ static int thc63_probe(struct platform_device *pdev) struct thc63_dev *thc63; int ret; - thc63 = devm_kzalloc(&pdev->dev, sizeof(*thc63), GFP_KERNEL); - if (!thc63) - return -ENOMEM; + thc63 = devm_drm_bridge_alloc(&pdev->dev, struct thc63_dev, bridge, + &thc63_bridge_func); + if (IS_ERR(thc63)) + return PTR_ERR(thc63); thc63->dev = &pdev->dev; platform_set_drvdata(pdev, thc63); @@ -208,7 +209,6 @@ static int thc63_probe(struct platform_device *pdev) thc63->bridge.driver_private = thc63; thc63->bridge.of_node = pdev->dev.of_node; - thc63->bridge.funcs = &thc63_bridge_func; thc63->bridge.timings = &thc63->timings; drm_bridge_add(&thc63->bridge); diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c index 47638d1c96ec..b07f7c9d5890 100644 --- a/drivers/gpu/drm/bridge/ti-dlpc3433.c +++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c @@ -348,9 +348,10 @@ static int dlpc3433_probe(struct i2c_client *client) struct dlpc *dlpc; int ret; - dlpc = devm_kzalloc(dev, sizeof(*dlpc), GFP_KERNEL); - if (!dlpc) - return -ENOMEM; + dlpc = devm_drm_bridge_alloc(dev, struct dlpc, bridge, + &dlpc_bridge_funcs); + if (IS_ERR(dlpc)) + return PTR_ERR(dlpc); dlpc->dev = dev; @@ -365,7 +366,6 @@ static int dlpc3433_probe(struct i2c_client *client) dev_set_drvdata(dev, dlpc); i2c_set_clientdata(client, dlpc); - dlpc->bridge.funcs = &dlpc_bridge_funcs; dlpc->bridge.of_node = dev->of_node; drm_bridge_add(&dlpc->bridge); diff --git a/drivers/gpu/drm/bridge/ti-tdp158.c b/drivers/gpu/drm/bridge/ti-tdp158.c index cca75443f012..27053d020df7 100644 --- a/drivers/gpu/drm/bridge/ti-tdp158.c +++ b/drivers/gpu/drm/bridge/ti-tdp158.c @@ -68,9 +68,10 @@ static int tdp158_probe(struct i2c_client *client) struct tdp158 *tdp158; struct device *dev = &client->dev; - tdp158 = devm_kzalloc(dev, sizeof(*tdp158), GFP_KERNEL); - if (!tdp158) - return -ENOMEM; + tdp158 = devm_drm_bridge_alloc(dev, struct tdp158, bridge, + &tdp158_bridge_funcs); + if (IS_ERR(tdp158)) + return PTR_ERR(tdp158); tdp158->next = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); if (IS_ERR(tdp158->next)) @@ -89,7 +90,6 @@ static int tdp158_probe(struct i2c_client *client) return dev_err_probe(dev, PTR_ERR(tdp158->enable), "enable"); tdp158->bridge.of_node = dev->of_node; - tdp158->bridge.funcs = &tdp158_bridge_funcs; tdp158->bridge.driver_private = tdp158; tdp158->dev = dev; diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index e15d232ddbac..549e8e8edeb4 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -341,14 +341,14 @@ static int tfp410_init(struct device *dev, bool i2c) return -ENXIO; } - dvi = devm_kzalloc(dev, sizeof(*dvi), GFP_KERNEL); - if (!dvi) - return -ENOMEM; + dvi = devm_drm_bridge_alloc(dev, struct tfp410, bridge, + &tfp410_bridge_funcs); + if (IS_ERR(dvi)) + return PTR_ERR(dvi); 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->bridge.type = DRM_MODE_CONNECTOR_DVID; diff --git a/drivers/gpu/drm/bridge/ti-tpd12s015.c b/drivers/gpu/drm/bridge/ti-tpd12s015.c index 1c289051a598..0919364e80d1 100644 --- a/drivers/gpu/drm/bridge/ti-tpd12s015.c +++ b/drivers/gpu/drm/bridge/ti-tpd12s015.c @@ -116,13 +116,13 @@ static int tpd12s015_probe(struct platform_device *pdev) struct gpio_desc *gpio; int ret; - tpd = devm_kzalloc(&pdev->dev, sizeof(*tpd), GFP_KERNEL); - if (!tpd) - return -ENOMEM; + tpd = devm_drm_bridge_alloc(&pdev->dev, struct tpd12s015_device, + bridge, &tpd12s015_bridge_funcs); + if (IS_ERR(tpd)) + return PTR_ERR(tpd); 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; diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index b2408abb9d49..bb80686a70e1 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -2725,9 +2725,10 @@ static int mtk_dp_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret; - mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL); - if (!mtk_dp) - return -ENOMEM; + mtk_dp = devm_drm_bridge_alloc(dev, struct mtk_dp, bridge, + &mtk_dp_bridge_funcs); + if (IS_ERR(mtk_dp)) + return PTR_ERR(mtk_dp); mtk_dp->dev = dev; mtk_dp->data = (struct mtk_dp_data *)of_device_get_match_data(dev); @@ -2785,7 +2786,6 @@ static int mtk_dp_probe(struct platform_device *pdev) if (ret) return ret; - mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs; mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = mtk_dp->data->bridge_type; diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index 0f3b1ef8e497..9669bf4b80a1 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -1179,9 +1179,10 @@ static int mtk_dpi_probe(struct platform_device *pdev) struct mtk_dpi *dpi; int ret; - dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL); - if (!dpi) - return -ENOMEM; + dpi = devm_drm_bridge_alloc(dev, struct mtk_dpi, bridge, + &mtk_dpi_bridge_funcs); + if (IS_ERR(dpi)) + return PTR_ERR(dpi); dpi->dev = dev; dpi->conf = (struct mtk_dpi_conf *)of_device_get_match_data(dev); @@ -1233,7 +1234,6 @@ static int mtk_dpi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dpi); - dpi->bridge.funcs = &mtk_dpi_bridge_funcs; dpi->bridge.of_node = dev->of_node; dpi->bridge.type = DRM_MODE_CONNECTOR_DPI; diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 4fe1f38a3c4b..d7726091819c 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -1196,9 +1196,10 @@ static int mtk_dsi_probe(struct platform_device *pdev) int irq_num; int ret; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(dev, struct mtk_dsi, bridge, + &mtk_dsi_bridge_funcs); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); dsi->driver_data = of_device_get_match_data(dev); @@ -1246,7 +1247,6 @@ static int mtk_dsi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dsi); - dsi->bridge.funcs = &mtk_dsi_bridge_funcs; dsi->bridge.of_node = dev->of_node; dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index c9d0c335c519..76aecad116ba 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1690,9 +1690,10 @@ static int mtk_hdmi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int ret; - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = devm_drm_bridge_alloc(dev, struct mtk_hdmi, bridge, + &mtk_hdmi_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); hdmi->dev = dev; hdmi->conf = of_device_get_match_data(dev); @@ -1719,7 +1720,6 @@ static int mtk_hdmi_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to register audio driver\n"); - hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs; hdmi->bridge.of_node = pdev->dev.of_node; hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c index c9678dc68fa1..dc374bfc5951 100644 --- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c @@ -227,9 +227,12 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv) struct device_node *remote; int ret; - meson_encoder_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_encoder_cvbs), GFP_KERNEL); - if (!meson_encoder_cvbs) - return -ENOMEM; + meson_encoder_cvbs = devm_drm_bridge_alloc(priv->dev, + struct meson_encoder_cvbs, + bridge, + &meson_encoder_cvbs_bridge_funcs); + if (IS_ERR(meson_encoder_cvbs)) + return PTR_ERR(meson_encoder_cvbs); /* CVBS Connector Bridge */ remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0); @@ -245,7 +248,6 @@ int meson_encoder_cvbs_probe(struct meson_drm *priv) "Failed to find CVBS Connector bridge\n"); /* CVBS Encoder Bridge */ - meson_encoder_cvbs->bridge.funcs = &meson_encoder_cvbs_bridge_funcs; meson_encoder_cvbs->bridge.of_node = priv->dev->of_node; meson_encoder_cvbs->bridge.type = DRM_MODE_CONNECTOR_Composite; meson_encoder_cvbs->bridge.ops = DRM_BRIDGE_OP_MODES; diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.c b/drivers/gpu/drm/meson/meson_encoder_dsi.c index 3db518e5f95d..6c6624f9ba24 100644 --- a/drivers/gpu/drm/meson/meson_encoder_dsi.c +++ b/drivers/gpu/drm/meson/meson_encoder_dsi.c @@ -106,9 +106,12 @@ int meson_encoder_dsi_probe(struct meson_drm *priv) struct device_node *remote; int ret; - meson_encoder_dsi = devm_kzalloc(priv->dev, sizeof(*meson_encoder_dsi), GFP_KERNEL); - if (!meson_encoder_dsi) - return -ENOMEM; + meson_encoder_dsi = devm_drm_bridge_alloc(priv->dev, + struct meson_encoder_dsi, + bridge, + &meson_encoder_dsi_bridge_funcs); + if (IS_ERR(meson_encoder_dsi)) + return PTR_ERR(meson_encoder_dsi); /* DSI Transceiver Bridge */ remote = of_graph_get_remote_node(priv->dev->of_node, 2, 0); @@ -123,7 +126,6 @@ int meson_encoder_dsi_probe(struct meson_drm *priv) "Failed to find DSI transceiver bridge\n"); /* DSI Encoder Bridge */ - meson_encoder_dsi->bridge.funcs = &meson_encoder_dsi_bridge_funcs; meson_encoder_dsi->bridge.of_node = priv->dev->of_node; meson_encoder_dsi->bridge.type = DRM_MODE_CONNECTOR_DSI; diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c index 040c04b5dff9..5a0d37a257f5 100644 --- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c @@ -376,9 +376,12 @@ int meson_encoder_hdmi_probe(struct meson_drm *priv) struct device_node *remote; int ret; - meson_encoder_hdmi = devm_kzalloc(priv->dev, sizeof(*meson_encoder_hdmi), GFP_KERNEL); - if (!meson_encoder_hdmi) - return -ENOMEM; + meson_encoder_hdmi = devm_drm_bridge_alloc(priv->dev, + struct meson_encoder_hdmi, + bridge, + &meson_encoder_hdmi_bridge_funcs); + if (IS_ERR(meson_encoder_hdmi)) + return PTR_ERR(meson_encoder_hdmi); /* HDMI Transceiver Bridge */ remote = of_graph_get_remote_node(priv->dev->of_node, 1, 0); @@ -395,7 +398,6 @@ int meson_encoder_hdmi_probe(struct meson_drm *priv) } /* HDMI Encoder Bridge */ - meson_encoder_hdmi->bridge.funcs = &meson_encoder_hdmi_bridge_funcs; meson_encoder_hdmi->bridge.of_node = priv->dev->of_node; meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; meson_encoder_hdmi->bridge.interlace_allowed = true; diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c index a9145253294f..af58b814e588 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c @@ -878,9 +878,10 @@ static int rcar_lvds_probe(struct platform_device *pdev) struct rcar_lvds *lvds; int ret; - lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); - if (lvds == NULL) - return -ENOMEM; + lvds = devm_drm_bridge_alloc(&pdev->dev, struct rcar_lvds, bridge, + &rcar_lvds_bridge_ops); + if (IS_ERR(lvds)) + return PTR_ERR(lvds); platform_set_drvdata(pdev, lvds); @@ -895,7 +896,6 @@ static int rcar_lvds_probe(struct platform_device *pdev) if (ret < 0) return ret; - lvds->bridge.funcs = &rcar_lvds_bridge_ops; lvds->bridge.of_node = pdev->dev.of_node; lvds->mmio = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index dc6ab012cdb6..8ba0dde8d482 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -701,9 +701,10 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) u32 txsetr; int ret; - dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; + dsi = devm_drm_bridge_alloc(&pdev->dev, struct rzg2l_mipi_dsi, bridge, + &rzg2l_mipi_dsi_bridge_ops); + if (IS_ERR(dsi)) + return PTR_ERR(dsi); platform_set_drvdata(pdev, dsi); dsi->dev = &pdev->dev; @@ -761,7 +762,6 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) pm_runtime_put(dsi->dev); /* Initialize the DRM bridge. */ - dsi->bridge.funcs = &rzg2l_mipi_dsi_bridge_ops; dsi->bridge.of_node = dsi->dev->of_node; /* Init host device */ From 027ce1eff321684e028e0d80d5c1c29f97cf0da3 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Fri, 9 May 2025 15:53:48 +0200 Subject: [PATCH 100/279] drm/todo: add entry to remove devm_drm_put_bridge() devm_drm_put_bridge() is a temporary workaround waiting for the panel bridge lifetime rework. Add a TODO entry to not forget it must be removed after such rework. Suggested-by: Maxime Ripard Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20250509-drm-bridge-convert-to-alloc-api-v3-22-b8bc1f16d7aa@bootlin.com Signed-off-by: Luca Ceresoli --- Documentation/gpu/todo.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index c57777a24e03..be8637da3fe9 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -515,6 +515,21 @@ Contact: Douglas Anderson Level: Starter +Remove devm_drm_put_bridge() +---------------------------- + +Due to how the panel bridge handles the drm_bridge object lifetime, special +care must be taken to dispose of the drm_bridge object when the +panel_bridge is removed. This is currently managed using +devm_drm_put_bridge(), but that is an unsafe, temporary workaround. To fix +that, the DRM panel lifetime needs to be reworked. After the rework is +done, remove devm_drm_put_bridge() and the TODO in +drm_panel_bridge_remove(). + +Contact: Maxime Ripard , + Luca Ceresoli + +Level: Intermediate Core refactorings ================= From 43adabbe3a7912b2db199a17d446a5d9fcde6fc7 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 6 May 2025 12:27:15 +0300 Subject: [PATCH 101/279] dt-bindings: display: panel: Document Renesas R61307 based DSI panel R61307 is liquid crystal driver for high-definition amorphous silicon (a-Si) panels and is ideal for tablets and smartphones. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Rob Herring (Arm) Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250506092718.106088-2-clamor95@gmail.com Signed-off-by: Dmitry Baryshkov --- .../display/panel/renesas,r61307.yaml | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/renesas,r61307.yaml diff --git a/Documentation/devicetree/bindings/display/panel/renesas,r61307.yaml b/Documentation/devicetree/bindings/display/panel/renesas,r61307.yaml new file mode 100644 index 000000000000..90cce221c0d1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/renesas,r61307.yaml @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/renesas,r61307.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R61307 based DSI Display Panel + +maintainers: + - Svyatoslav Ryhel + +description: + The Renesas R61307 is a generic DSI Panel IC used to control LCD panels. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + # KOE/HITACHI TX13D100VM0EAA 5.0" XGA TFT LCD panel + - hit,tx13d100vm0eaa + - koe,tx13d100vm0eaa + - const: renesas,r61307 + + reg: + maxItems: 1 + + vcc-supply: + description: Regulator for main power supply. + + iovcc-supply: + description: Regulator for 1.8V IO power supply. + + backlight: true + + renesas,gamma: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + 0 - disabled + 1-3 - gamma setting A presets + enum: [0, 1, 2, 3] + + renesas,column-inversion: + type: boolean + description: switch between line and column inversion. The line + inversion is set by default. + + renesas,contrast: + type: boolean + description: digital contrast adjustment + + reset-gpios: true + port: true + +required: + - compatible + - port + - backlight + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@1 { + compatible = "koe,tx13d100vm0eaa", "renesas,r61307"; + reg = <1>; + + reset-gpios = <&gpio 176 GPIO_ACTIVE_LOW>; + + renesas,gamma = <3>; + renesas,column-inversion; + renesas,contrast; + + vcc-supply = <&vcc_3v0_lcd>; + iovcc-supply = <&iovcc_1v8_lcd>; + + backlight = <&backlight>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; +... From cb6c01ead1eb78f7676ea09fe407c4aa1c5855b3 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Tue, 6 May 2025 12:27:16 +0300 Subject: [PATCH 102/279] drm: panel: Add support for Renesas R61307 based MIPI DSI panel R61307 is liquid crystal driver for high-definition amorphous silicon (a-Si) panels and is ideal for tablets and smartphones. Supported compatibles are: - hit,tx13d100vm0eaa - koe,tx13d100vm0eaa Signed-off-by: Svyatoslav Ryhel Reviewed-by: Neil Armstrong Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250506092718.106088-3-clamor95@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/panel/Kconfig | 13 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-renesas-r61307.c | 325 +++++++++++++++++++ 3 files changed, 339 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-renesas-r61307.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 721581d425b4..b058959de717 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -645,6 +645,19 @@ config DRM_PANEL_RAYDIUM_RM69380 This panel controller can be found in the Lenovo Xiaoxin Pad Pro 2021 in combination with an EDO OLED panel. +config DRM_PANEL_RENESAS_R61307 + tristate "Renesas R61307 DSI video mode panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for KOE tx13d100vm0eaa + IPS-LCD module with Renesas R69328 IC. The panel has a 1024x768 + resolution and uses 24 bit RGB per pixel. + + This panel controller can be found in LG Optimus Vu P895 smartphone + in combination with LCD panel. + config DRM_PANEL_RONBO_RB070D30 tristate "Ronbo Electronics RB070D30 panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 714cbac830e3..df0105ed7622 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67200) += panel-raydium-rm67200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o +obj-$(CONFIG_DRM_PANEL_RENESAS_R61307) += panel-renesas-r61307.o obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS581VF01) += panel-samsung-ams581vf01.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS639RQ08) += panel-samsung-ams639rq08.o diff --git a/drivers/gpu/drm/panel/panel-renesas-r61307.c b/drivers/gpu/drm/panel/panel-renesas-r61307.c new file mode 100644 index 000000000000..319415194839 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-renesas-r61307.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include