On 9/28/26 10:08 AM, Julien Stephan wrote:
> The DSI controller is the last stage of the MT8365 and MT8366 display
> pipelines: it serialises the pixel stream coming out of the pipeline
> onto the MIPI D-PHY lanes, and carries the DCS commands the panel needs
> to come up.
> 
> Add a video uclass driver bringing up that whole pipeline: power the
> D-PHY and the controller up with its D-PHY timings, ungate the
> components, route the overlay to the DSI through the mmsys, start the
> components in order, then program the video timings and the pixel stream
> format from the panel's own description, and start the video stream once
> the panel and its backlight are on. The MT8365 runs the stream through
> the colour pipeline, the MT8366 straight from the rdma, which the
> per-compatible data selects.
> 
> The driver is also the MIPI DSI host the panel talks to: the panel init
> sequence goes out through the controller's command queue, in command
> mode, before the video stream is started.
> 
> The driver matches "mediatek,mt8183-dsi", the generic fallback the
> MT8365 dsi0 node carries behind "mediatek,mt8365-dsi", and
> "mediatek,mt8366-dsi", which the MT8366 node carries on its own as that
> SoC is not upstream yet. Beyond the colour pipeline, the two generations
> differ in the offset of the command queues, in the mt8183-style shadow
> register control, which the MT8366 does not have, and in the command
> queue size of the long packets, which the MT8366 has to set itself
> (CMDQ_SIZE_SEL): all of it is per-compatible data.
> 
> The objects both paths need move out of the HDMI object list into
> Kconfig symbols of their own: VIDEO_MEDIATEK_DISP_COMP for the component
> helpers and the mutex, selected by both paths, and VIDEO_MEDIATEK_DPI
> for the DPI, selected by the HDMI path. VIDEO_MEDIATEK_DISP_PIPELINE
> covers the mmsys routing and the display rdma of the DSI path.
> 
> Signed-off-by: Julien Stephan <[email protected]>
> ---
>  drivers/video/mediatek/Kconfig   |   32 +
>  drivers/video/mediatek/Makefile  |   19 +-
>  drivers/video/mediatek/mtk_dsi.c | 1220 
> ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1268 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/video/mediatek/Kconfig b/drivers/video/mediatek/Kconfig
> index a10f2360391..9d779a26da5 100644
> --- a/drivers/video/mediatek/Kconfig
> +++ b/drivers/video/mediatek/Kconfig
> @@ -4,6 +4,36 @@
>  #
>  # Author: Julien Stephan <[email protected]>
>  
> +# Display pipeline component helpers, selected by the output paths below
> +config VIDEO_MEDIATEK_DISP_COMP
> +     bool
> +
> +# mmsys routing and display RDMA, used by the DSI path
> +config VIDEO_MEDIATEK_DISP_PIPELINE
> +     bool
> +
> +# DPI timing generator programming, shared by the DPI and HDMI paths
> +config VIDEO_MEDIATEK_DPI
> +     bool
> +
> +config VIDEO_MEDIATEK_DSI
> +     bool "DSI video path for MediaTek SoCs"
> +     depends on VIDEO && ARCH_MEDIATEK
> +     depends on ARM64 && CLK && PHY

&& POWER_DOMAIN

> +     select MISC
> +     select MTK_SMI
> +     select PANEL
> +     select PHY_MTK_MIPI_DSI
> +     select SYSCON
> +     select VIDEO_MEDIATEK_DISP_COMP
> +     select VIDEO_MEDIATEK_DISP_PIPELINE
> +     select VIDEO_MIPI_DSI
> +     help
> +       Enable support for the DSI controller found on MediaTek SoCs such
> +       as the MT8365 and the MT8366. The driver drives the mmsys display
> +       pipeline (overlay, RDMA and, on the SoCs having one, the colour
> +       pipeline) to scan out the U-Boot framebuffer to a DSI panel.
> +
>  config VIDEO_MEDIATEK_HDMI
>       bool "Video HDMI Support for MediaTek SoCs"
>       depends on VIDEO && ARCH_MEDIATEK
> @@ -14,6 +44,8 @@ config VIDEO_MEDIATEK_HDMI
>       select MTK_SMI
>       select PHY_MTK_HDMI
>       select SYS_I2C_DDC_MTK
> +     select VIDEO_MEDIATEK_DISP_COMP
> +     select VIDEO_MEDIATEK_DPI
>       help
>         Enable support for the HDMI TX output found on recent MediaTek
>         SoCs such as the MT8188. The driver reads the EDID of the
> diff --git a/drivers/video/mediatek/Makefile b/drivers/video/mediatek/Makefile
> index 38ab13e94fd..9de9a9c78f1 100644
> --- a/drivers/video/mediatek/Makefile
> +++ b/drivers/video/mediatek/Makefile

...

> +/**
> + * struct mtk_dsi_data - per-SoC DSI data
> + * @has_color_pipeline: the DSI is fed through the color, ccorr, aal, gamma
> + *                   and dither blocks rather than straight by the rdma
> + * @cmdq_long_packet_ctl: the command queue size is not set automatically for
> + *                     long packets, CMDQ_SIZE_SEL must be set
> + * @shadow_dbg_off: offset of the shadow control, zero on the SoCs without it
> + * @vm_cmdq_off: offset of the video mode command register
> + * @host_cmdq_off: offset of the host command queue
> + */
> +struct mtk_dsi_data {
> +     bool has_color_pipeline;

I've been looking ahead to other targets, and I think has_color_pipeline
doesn't actually belong here. To be more like Linux, it rather belongs on
the mmsys component. And it is not just a boolean. It should describe the
individual components in the pipline.

I.e. for DSI each conpatible would have an array (or read this from of_graph
but I think that was avoided for simplicity since we know what it is anyway?)

For example:

MT8365: ovl0, rdma0, color0, ccorr0, aal0, gamma0, dither0
MT8366/MT8189: ovl0, rdma0
MT8188: ovl0, rdma0, color0, ccorr0, aal0, gamma0, postmask0, dither0
MT8195: ovl0, rdma0, color0, ccorr0, aal0, gamma0, dither0, dsc0, merge0
MT8183: ovl0, ovl_2l0, rdma0, color0, ccorr0, aal0, gamma0, dither0

Plus HDMI and DP could have different piplines.



> +     bool cmdq_long_packet_ctl;
> +     u32 shadow_dbg_off;
> +     u32 vm_cmdq_off;
> +     u32 host_cmdq_off;
> +};
> +

...

> +static int mtk_dsi_power_on(struct mtk_dsi_priv *dsi,
> +                         const struct display_timing *timing)
> +{
> +     struct mipi_dsi_panel_plat *plat = dev_get_plat(dsi->panel);
> +     struct phy_configure_opts_mipi_dphy opts = { };
> +     int ret, bit_per_pixel;
> +
> +     bit_per_pixel = mipi_dsi_pixel_format_to_bpp(plat->format);
> +     if (bit_per_pixel < 0) {
> +             dev_err(dsi->dev, "unknown MIPI DSI format %d\n", plat->format);
> +             return bit_per_pixel;
> +     }
> +
> +     dsi->data_rate = DIV_ROUND_UP_ULL((u64)timing->pixelclock.typ *
> +                                       bit_per_pixel, plat->lanes);
> +
> +     opts.hs_clk_rate = dsi->data_rate;
> +     opts.lanes = plat->lanes;
> +
> +     ret = generic_phy_configure(&dsi->phy, &opts);
> +     if (ret) {
> +             dev_err(dsi->dev, "failed to set the PHY to %llu Hz: %d\n",
> +                     dsi->data_rate, ret);
> +             return ret;
> +     }
> +
> +     ret = generic_phy_power_on(&dsi->phy);
> +     if (ret) {
> +             dev_err(dsi->dev, "failed to power on the PHY: %d\n", ret);
> +             return ret;
> +     }
> +
> +     ret = clk_enable(dsi->engine_clk);
> +     if (ret) {
> +             dev_err(dsi->dev, "failed to enable engine clk: %d\n", ret);
> +             goto err_phy_power_off;
> +     }
> +
> +     ret = clk_enable(dsi->digital_clk);
> +     if (ret) {
> +             dev_err(dsi->dev, "failed to enable digital clk: %d\n", ret);
> +             goto err_disable_engine_clk;
> +     }
> +
> +     ret = mtk_disp_comp_enable(dsi->mutex);
> +     if (ret) {
> +             dev_err(dsi->dev, "failed to enable mutex: %d\n", ret);
> +             goto err_disable_digital_clk;
> +     }
> +
> +     ret = mtk_disp_comp_enable(dsi->ovl);
> +     if (ret) {
> +             dev_err(dsi->dev, "failed to enable ovl: %d\n", ret);
> +             goto err_disable_mutex;
> +     }
> +
> +     ret = mtk_disp_comp_enable(dsi->rdma);
> +     if (ret) {
> +             dev_err(dsi->dev, "failed to enable rdma: %d\n", ret);
> +             goto err_disable_ovl;
> +     }
> +

On the subject of replacing has_color_pipeline again. This would become
something like a for loop to enable the components used by the chosen dsi0
pipline.

Such code could also be shared with HDMI and DP implementations. They would
just have a different array (or in some cases the same) of pipline members.

> +     if (dsi->data->has_color_pipeline) {
> +             ret = mtk_disp_comp_enable(dsi->color);
> +             if (ret) {
> +                     dev_err(dsi->dev, "failed to enable color: %d\n", ret);
> +                     goto err_disable_rdma;
> +             }
> +
> +             ret = mtk_disp_comp_enable(dsi->ccorr);
> +             if (ret) {
> +                     dev_err(dsi->dev, "failed to enable ccorr: %d\n", ret);
> +                     goto err_disable_color;
> +             }
> +
> +             ret = mtk_disp_comp_enable(dsi->aal);
> +             if (ret) {
> +                     dev_err(dsi->dev, "failed to enable aal: %d\n", ret);
> +                     goto err_disable_ccorr;
> +             }
> +
> +             ret = mtk_disp_comp_enable(dsi->gamma);
> +             if (ret) {
> +                     dev_err(dsi->dev, "failed to enable gamma: %d\n", ret);
> +                     goto err_disable_aal;
> +             }
> +
> +             ret = mtk_disp_comp_enable(dsi->dither);
> +             if (ret) {
> +                     dev_err(dsi->dev, "failed to enable dither: %d\n", ret);
> +                     goto err_disable_gamma;
> +             }
> +     }
> +
> +     mtk_dsi_enable(dsi);
> +
> +     if (dsi->data->shadow_dbg_off)
> +             mtk_dsi_shadow_debug(dsi);
> +
> +     mtk_dsi_reset_engine(dsi);
> +
> +     mtk_dsi_phy_timconfig(dsi);
> +
> +     mtk_dsi_set_vm_cmd(dsi);
> +
> +     mtk_dsi_set_interrupt_enable(dsi);
> +
> +     return 0;
> +

...

> +static int mtk_dsi_probe(struct udevice *dev)
> +{
> +     struct mtk_dsi_priv *dsi = dev_get_priv(dev);
> +     struct video_uc_plat *plat = dev_get_uclass_plat(dev);
> +     struct display_timing timing;
> +     struct udevice *mmsys_dev;
> +     struct regmap *regmap;
> +     int ret;
> +
> +     /* Before relocation we don't need to do anything */
> +     if (!(gd->flags & GD_FLG_RELOC))
> +             return 0;
> +
> +     dsi->dev = dev;
> +     dsi->data = (const struct mtk_dsi_data *)dev_get_driver_data(dev);
> +     if (!dsi->data) {
> +             dev_err(dev, "missing dsi match data\n");
> +             return -EINVAL;
> +     }
> +
> +     dsi->base = dev_remap_addr(dev);
> +     if (!dsi->base)
> +             return -EINVAL;
> +
> +     dsi->engine_clk = devm_clk_get(dev, "engine");
> +     if (IS_ERR(dsi->engine_clk))
> +             return PTR_ERR(dsi->engine_clk);
> +
> +     /*
> +      * The DSI host's "hs" clock is the MIPI TX PLL. U-Boot's MIPI TX driver
> +      * does not register it as a clock, so it is not fetched here; its rate
> +      * is set through generic_phy_configure() instead.
> +      */
> +     dsi->digital_clk = devm_clk_get(dev, "digital");
> +     if (IS_ERR(dsi->digital_clk))
> +             return PTR_ERR(dsi->digital_clk);
> +
> +     ret = uclass_get_device_by_of_path(UCLASS_MISC, "ovl0", &dsi->ovl);
> +     if (ret) {
> +             dev_err(dev, "cannot get ovl device: %s\n", errno_str(ret));
> +             return ret;
> +     }
> +
> +     ret = uclass_get_device_by_of_path(UCLASS_MISC, "rdma0", &dsi->rdma);
> +     if (ret) {
> +             dev_err(dev, "cannot get rdma device: %s\n", errno_str(ret));
> +             return ret;
> +     }
> +
> +     if (dsi->data->has_color_pipeline) {

Obviosuly, more pipline stuff here...

> +             ret = uclass_get_device_by_of_path(UCLASS_MISC, "color0", 
> &dsi->color);
> +             if (ret) {
> +                     dev_err(dev, "cannot get color device: %s\n", 
> errno_str(ret));
> +                     return ret;
> +             }
> +
> +             ret = uclass_get_device_by_of_path(UCLASS_MISC, "ccorr0", 
> &dsi->ccorr);
> +             if (ret) {
> +                     dev_err(dev, "cannot get ccorr device: %s\n", 
> errno_str(ret));
> +                     return ret;
> +             }
> +
> +             ret = uclass_get_device_by_of_path(UCLASS_MISC, "aal0", 
> &dsi->aal);
> +             if (ret) {
> +                     dev_err(dev, "cannot get aal device: %s\n", 
> errno_str(ret));
> +                     return ret;
> +             }
> +
> +             ret = uclass_get_device_by_of_path(UCLASS_MISC, "gamma0", 
> &dsi->gamma);
> +             if (ret) {
> +                     dev_err(dev, "cannot get gamma device: %s\n", 
> errno_str(ret));
> +                     return ret;
> +             }
> +
> +             ret = uclass_get_device_by_of_path(UCLASS_MISC, "dither0", 
> &dsi->dither);
> +             if (ret) {
> +                     dev_err(dev, "cannot get dither device: %s\n", 
> errno_str(ret));
> +                     return ret;
> +             }
> +     }
> +

...

> +     mtk_dsi_set_display_mode(dsi, &timing);
> +
> +     mtk_dsi_lane_ready(dsi);
> +
> +     ret = mtk_ovl_layer(dsi->ovl, plat, &timing);
> +     if (ret)
> +             goto err_power_off;
> +
> +     mmu_set_region_dcache_behaviour(plat->base, ALIGN(plat->size, 
> MMU_SECTION_SIZE),
> +                                     DCACHE_WRITEBACK);

Do we actually need mmu_set_region_dcache_behaviour()? AFAIKT, this
DCACHE_WRITEBACK attribte is already set by default since we are using
PTE_BLOCK_MEMTYPE(MT_NORMAL) for the whole DRAM.

I think the ALIGN() would cause it to touch memory we didn't allocate too.

> +     video_set_flush_dcache(dev, true);
> +
> +     ret = panel_set_backlight(dsi->panel, BACKLIGHT_DEFAULT);
> +     if (ret && ret != -ENOSYS) {
> +             dev_err(dev, "failed to set backlight\n");
> +             goto err_power_off;
> +     }
> +
> +     ret = panel_enable_backlight(dsi->panel);
> +     if (ret) {
> +             dev_err(dev, "failed to enable backlight\n");
> +             goto err_power_off;
> +     }
> +
> +     mtk_dsi_output_enable(dsi);
> +
> +     return 0;
> +

...

> +U_BOOT_DRIVER(mtk_dsi) = {
> +     .name      = "mtk_dsi",
> +     .id        = UCLASS_VIDEO,
> +     .of_match  = mtk_dsi_ids,
> +     .probe     = mtk_dsi_probe,
> +     .bind      = mtk_dsi_bind,
> +     .priv_auto = sizeof(struct mtk_dsi_priv),
> +     .flags     = DM_FLAG_PRE_RELOC | DM_FLAG_LEAVE_PD_ON,

I think we should swap DM_FLAG_LEAVE_PD_ON for DM_FLAG_OS_PREPARE and add a
.remove() callback that shuts everything down. AFAICT, there is nothing in
Linux that knows how to properly take over from U-Boot here. And when I run
this on Genio 360, when Linux starts, the screen gets corrupted and slowly
fades to black. With a proper .remove() callback, then Linux works as it
should.

We would need to fix Linux first in order to make this actually work as-is.

> +};
> 

Reply via email to