From a9ed3aa9b87ee41e8ab3ff471b1331c154767e45 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Sat, 26 Sep 2026 07:57:59 +1000 Subject: Merge tag 'drm-misc-fixes-2026-09-24' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-fixes A number of fixes: - bridge: - samsung-dsim: fix GPIO lifetime - client: Null pointer dereference fix - imagination: error handling fix, page handling fix - nouveau: fix reference leaks, double-frees, out-of-bounds accesses, use-after-frees, don't reject config without SCDC, a number of workarounds - virtio: fix memory leak, reference leaks, null pointer dereference, add pixel blend mode, cache coherency fix Signed-off-by: Dave Airlie From: Maxime Ripard Link: https://patch.msgid.link/arU22zzqUGDEco1y@houat --- drivers/gpu/drm/mcde/Kconfig | 18 + drivers/gpu/drm/mcde/Makefile | 3 + drivers/gpu/drm/mcde/mcde_clk_div.c | 196 ++++ drivers/gpu/drm/mcde/mcde_display.c | 1523 ++++++++++++++++++++++++++++++ drivers/gpu/drm/mcde/mcde_display_regs.h | 605 ++++++++++++ drivers/gpu/drm/mcde/mcde_drm.h | 118 +++ drivers/gpu/drm/mcde/mcde_drv.c | 522 ++++++++++ drivers/gpu/drm/mcde/mcde_dsi.c | 1242 ++++++++++++++++++++++++ drivers/gpu/drm/mcde/mcde_dsi_regs.h | 385 ++++++++ 9 files changed, 4612 insertions(+) create mode 100644 drivers/gpu/drm/mcde/Kconfig create mode 100644 drivers/gpu/drm/mcde/Makefile create mode 100644 drivers/gpu/drm/mcde/mcde_clk_div.c create mode 100644 drivers/gpu/drm/mcde/mcde_display.c create mode 100644 drivers/gpu/drm/mcde/mcde_display_regs.h create mode 100644 drivers/gpu/drm/mcde/mcde_drm.h create mode 100644 drivers/gpu/drm/mcde/mcde_drv.c create mode 100644 drivers/gpu/drm/mcde/mcde_dsi.c create mode 100644 drivers/gpu/drm/mcde/mcde_dsi_regs.h (limited to 'drivers/gpu/drm/mcde') diff --git a/drivers/gpu/drm/mcde/Kconfig b/drivers/gpu/drm/mcde/Kconfig new file mode 100644 index 000000000..3516c8d2a --- /dev/null +++ b/drivers/gpu/drm/mcde/Kconfig @@ -0,0 +1,18 @@ +config DRM_MCDE + tristate "DRM Support for ST-Ericsson MCDE (Multichannel Display Engine)" + depends on DRM + depends on CMA + depends on ARM || COMPILE_TEST + depends on OF + depends on COMMON_CLK + select MFD_SYSCON + select DRM_CLIENT_SELECTION + select DRM_MIPI_DSI + select DRM_BRIDGE + select DRM_PANEL_BRIDGE + select DRM_KMS_HELPER + select DRM_GEM_DMA_HELPER + help + Choose this option for DRM support for the ST-Ericsson MCDE + Multi-Channel Display Engine. + If M is selected the module will be called mcde_drm. diff --git a/drivers/gpu/drm/mcde/Makefile b/drivers/gpu/drm/mcde/Makefile new file mode 100644 index 000000000..15d9c89a3 --- /dev/null +++ b/drivers/gpu/drm/mcde/Makefile @@ -0,0 +1,3 @@ +mcde_drm-y += mcde_drv.o mcde_dsi.o mcde_clk_div.o mcde_display.o + +obj-$(CONFIG_DRM_MCDE) += mcde_drm.o diff --git a/drivers/gpu/drm/mcde/mcde_clk_div.c b/drivers/gpu/drm/mcde/mcde_clk_div.c new file mode 100644 index 000000000..8c5af2677 --- /dev/null +++ b/drivers/gpu/drm/mcde/mcde_clk_div.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +#include "mcde_drm.h" +#include "mcde_display_regs.h" + +/* The MCDE internal clock dividers for FIFO A and B */ +struct mcde_clk_div { + struct clk_hw hw; + struct mcde *mcde; + u32 cr; + u32 cr_div; +}; + +static int mcde_clk_div_enable(struct clk_hw *hw) +{ + struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw); + struct mcde *mcde = cdiv->mcde; + u32 val; + + spin_lock(&mcde->fifo_crx1_lock); + val = readl(mcde->regs + cdiv->cr); + /* + * Select the PLL72 (LCD) clock as parent + * FIXME: implement other parents. + */ + val &= ~MCDE_CRX1_CLKSEL_MASK; + val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT; + /* Internal clock */ + val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1; + + /* Clear then set the divider */ + val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK); + val |= cdiv->cr_div; + + writel(val, mcde->regs + cdiv->cr); + spin_unlock(&mcde->fifo_crx1_lock); + + return 0; +} + +static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate, + unsigned long *prate, bool set_parent) +{ + int best_div = 1, div; + struct clk_hw *parent = clk_hw_get_parent(hw); + unsigned long best_prate = 0; + unsigned long best_diff = ~0ul; + int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1; + + for (div = 1; div < max_div; div++) { + unsigned long this_prate, div_rate, diff; + + if (set_parent) + this_prate = clk_hw_round_rate(parent, rate * div); + else + this_prate = *prate; + div_rate = DIV_ROUND_UP_ULL(this_prate, div); + diff = abs(rate - div_rate); + + if (diff < best_diff) { + best_div = div; + best_diff = diff; + best_prate = this_prate; + } + } + + *prate = best_prate; + return best_div; +} + +static int mcde_clk_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int div = mcde_clk_div_choose_div(hw, req->rate, + &req->best_parent_rate, true); + + req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div); + + return 0; +} + +static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw, + unsigned long prate) +{ + struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw); + struct mcde *mcde = cdiv->mcde; + u32 cr; + int div; + + /* + * If the MCDE is not powered we can't access registers. + * It will come up with 0 in the divider register bits, which + * means "divide by 2". + */ + if (!regulator_is_enabled(mcde->epod)) + return DIV_ROUND_UP_ULL(prate, 2); + + cr = readl(mcde->regs + cdiv->cr); + if (cr & MCDE_CRX1_BCD) + return prate; + + /* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */ + div = cr & MCDE_CRX1_PCD_MASK; + div += 2; + + return DIV_ROUND_UP_ULL(prate, div); +} + +static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw); + int div = mcde_clk_div_choose_div(hw, rate, &prate, false); + u32 cr = 0; + + /* + * We cache the CR bits to set the divide in the state so that + * we can call this before we can even write to the hardware. + */ + if (div == 1) { + /* Bypass clock divider */ + cr |= MCDE_CRX1_BCD; + } else { + div -= 2; + cr |= div & MCDE_CRX1_PCD_MASK; + } + cdiv->cr_div = cr; + + return 0; +} + +static const struct clk_ops mcde_clk_div_ops = { + .enable = mcde_clk_div_enable, + .recalc_rate = mcde_clk_div_recalc_rate, + .determine_rate = mcde_clk_div_determine_rate, + .set_rate = mcde_clk_div_set_rate, +}; + +int mcde_init_clock_divider(struct mcde *mcde) +{ + struct device *dev = mcde->dev; + struct mcde_clk_div *fifoa; + struct mcde_clk_div *fifob; + const char *parent_name; + struct clk_init_data fifoa_init = { + .name = "fifoa", + .ops = &mcde_clk_div_ops, + .parent_names = &parent_name, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }; + struct clk_init_data fifob_init = { + .name = "fifob", + .ops = &mcde_clk_div_ops, + .parent_names = &parent_name, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }; + int ret; + + spin_lock_init(&mcde->fifo_crx1_lock); + parent_name = __clk_get_name(mcde->lcd_clk); + + /* Allocate 2 clocks */ + fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL); + if (!fifoa) + return -ENOMEM; + fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL); + if (!fifob) + return -ENOMEM; + + fifoa->mcde = mcde; + fifoa->cr = MCDE_CRA1; + fifoa->hw.init = &fifoa_init; + ret = devm_clk_hw_register(dev, &fifoa->hw); + if (ret) { + dev_err(dev, "error registering FIFO A clock divider\n"); + return ret; + } + mcde->fifoa_clk = fifoa->hw.clk; + + fifob->mcde = mcde; + fifob->cr = MCDE_CRB1; + fifob->hw.init = &fifob_init; + ret = devm_clk_hw_register(dev, &fifob->hw); + if (ret) { + dev_err(dev, "error registering FIFO B clock divider\n"); + return ret; + } + mcde->fifob_clk = fifob->hw.clk; + + return 0; +} diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c new file mode 100644 index 000000000..257a6e84d --- /dev/null +++ b/drivers/gpu/drm/mcde/mcde_display.c @@ -0,0 +1,1523 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Linus Walleij + * Parts of this file were based on the MCDE driver by Marcus Lorentzon + * (C) ST-Ericsson SA 2013 + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include