]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm: rcar-du: Don't leak device_link to CMM
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Mon, 23 Mar 2026 16:45:26 +0000 (18:45 +0200)
committerTomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Thu, 26 Mar 2026 13:16:25 +0000 (15:16 +0200)
The DU driver creates device_link instances between the DU and CMMs, but
never deletes them. Fix it by introducing a rcar_du_cmm structure to
group the CMM device and device_link, and deleting the links at cleanup
time.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Link: https://patch.msgid.link/20260323164526.2292491-5-laurent.pinchart+renesas@ideasonboard.com
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.h
drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.h
drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c

index 28a5aa5a14d803edca790f8033f38ff68b3a13b3..7c36c30a75b63ece95ff8d24652f040cb359ebd1 100644 (file)
@@ -513,13 +513,13 @@ static void rcar_du_cmm_setup(struct drm_crtc *crtc)
        struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
        struct rcar_cmm_config cmm_config = {};
 
-       if (!rcrtc->cmm)
+       if (!rcrtc->cmm->dev)
                return;
 
        if (drm_lut)
                cmm_config.lut.table = (struct drm_color_lut *)drm_lut->data;
 
-       rcar_cmm_setup(rcrtc->cmm, &cmm_config);
+       rcar_cmm_setup(rcrtc->cmm->dev, &cmm_config);
 }
 
 /* -----------------------------------------------------------------------------
@@ -667,8 +667,8 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
        if (rcar_du_has(rcrtc->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
                rcar_du_vsp_disable(rcrtc);
 
-       if (rcrtc->cmm)
-               rcar_cmm_disable(rcrtc->cmm);
+       if (rcrtc->cmm->dev)
+               rcar_cmm_disable(rcrtc->cmm->dev);
 
        /*
         * Select switch sync mode. This stops display operation and configures
@@ -726,8 +726,8 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
        struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state);
        struct rcar_du_device *rcdu = rcrtc->dev;
 
-       if (rcrtc->cmm)
-               rcar_cmm_enable(rcrtc->cmm);
+       if (rcrtc->cmm->dev)
+               rcar_cmm_enable(rcrtc->cmm->dev);
        rcar_du_crtc_get(rcrtc);
 
        /*
@@ -1300,8 +1300,8 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,
                return ret;
 
        /* CMM might be disabled for this CRTC. */
-       if (rcdu->cmms[swindex]) {
-               rcrtc->cmm = rcdu->cmms[swindex];
+       if (rcdu->cmms[swindex].dev) {
+               rcrtc->cmm = &rcdu->cmms[swindex];
                rgrp->cmms_mask |= BIT(hwindex % 2);
 
                drm_mode_crtc_set_gamma_size(crtc, CM2_LUT_SIZE);
index 07a40b305be82b30f187d7cf3aa78a547734ac7d..8857926e109a29d71a29d99a241bd7fd9328c8ac 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <media/vsp1.h>
 
+struct rcar_du_cmm;
 struct rcar_du_group;
 struct rcar_du_vsp;
 
@@ -65,7 +66,7 @@ struct rcar_du_crtc {
        unsigned int vblank_count;
 
        struct rcar_du_group *group;
-       struct device *cmm;
+       struct rcar_du_cmm *cmm;
        struct rcar_du_vsp *vsp;
        unsigned int vsp_pipe;
 
index 9e160dede4e62b338b60745a143d307272fd7775..de9c6617a2d4d2745ca1c9920f2ee2e5ce12f16e 100644 (file)
@@ -22,6 +22,7 @@
 
 struct clk;
 struct device;
+struct device_link;
 struct drm_bridge;
 struct drm_property;
 struct rcar_du_device;
@@ -88,6 +89,11 @@ struct rcar_du_device_info {
        unsigned int lvds_clk_mask;
 };
 
+struct rcar_du_cmm {
+       struct device *dev;
+       struct device_link *link;
+};
+
 #define RCAR_DU_MAX_CRTCS              4
 #define RCAR_DU_MAX_GROUPS             DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
 #define RCAR_DU_MAX_VSPS               4
@@ -106,7 +112,7 @@ struct rcar_du_device {
        unsigned int num_crtcs;
 
        struct rcar_du_group groups[RCAR_DU_MAX_GROUPS];
-       struct device *cmms[RCAR_DU_MAX_CRTCS];
+       struct rcar_du_cmm cmms[RCAR_DU_MAX_CRTCS];
        struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS];
        struct drm_bridge *lvds[RCAR_DU_MAX_LVDS];
        struct drm_bridge *dsi[RCAR_DU_MAX_DSI];
index 9a53b5a86c8268a0c7b355d892c2f50fa04071b5..b2d0e4651e3523075835a77c7da1a111a164e2ec 100644 (file)
@@ -769,23 +769,23 @@ static int rcar_du_cmm_init(struct rcar_du_device *rcdu)
        }
 
        for (i = 0; i < cells; ++i) {
-               struct device_node *cmm __free(device_node) = NULL;
+               struct device_node *cmm_node __free(device_node) = NULL;
+               struct rcar_du_cmm *cmm = &rcdu->cmms[i];
                struct platform_device *pdev;
-               struct device_link *link;
                int ret;
 
-               cmm = of_parse_phandle(np, "renesas,cmms", i);
-               if (!cmm) {
+               cmm_node = of_parse_phandle(np, "renesas,cmms", i);
+               if (!cmm_node) {
                        dev_err(rcdu->dev,
                                "Failed to parse 'renesas,cmms' property\n");
                        return -EINVAL;
                }
 
-               if (!of_device_is_available(cmm))
+               if (!of_device_is_available(cmm_node))
                        /* It's fine to have a phandle to a non-enabled CMM. */
                        continue;
 
-               pdev = of_find_device_by_node(cmm);
+               pdev = of_find_device_by_node(cmm_node);
                if (!pdev) {
                        dev_err(rcdu->dev, "No device found for CMM%u\n", i);
                        return -EINVAL;
@@ -801,14 +801,15 @@ static int rcar_du_cmm_init(struct rcar_du_device *rcdu)
                        return ret == -ENODEV ? 0 : ret;
                }
 
-               rcdu->cmms[i] = &pdev->dev;
+               cmm->dev = &pdev->dev;
 
                /*
                 * Enforce suspend/resume ordering by making the CMM a provider
                 * of the DU: CMM is suspended after and resumed before the DU.
                 */
-               link = device_link_add(rcdu->dev, &pdev->dev, DL_FLAG_STATELESS);
-               if (!link) {
+               cmm->link = device_link_add(rcdu->dev, cmm->dev,
+                                           DL_FLAG_STATELESS);
+               if (!cmm->link) {
                        dev_err(rcdu->dev,
                                "Failed to create device link to CMM%u\n", i);
                        return -EINVAL;
@@ -823,8 +824,14 @@ static void rcar_du_modeset_cleanup(struct drm_device *dev, void *res)
        struct rcar_du_device *rcdu = to_rcar_du_device(dev);
        unsigned int i;
 
-       for (i = 0; i < ARRAY_SIZE(rcdu->cmms); ++i)
-               put_device(rcdu->cmms[i]);
+       for (i = 0; i < ARRAY_SIZE(rcdu->cmms); ++i) {
+               struct rcar_du_cmm *cmm = &rcdu->cmms[i];
+
+               if (cmm->link)
+                       device_link_del(cmm->link);
+
+               put_device(cmm->dev);
+       }
 }
 
 int rcar_du_modeset_init(struct rcar_du_device *rcdu)