чт, 5 бер. 2026 р. о 17:21 Svyatoslav Ryhel <[email protected]> пише:
>
> нд, 25 січ. 2026 р. о 15:13 Svyatoslav Ryhel <[email protected]> пише:
> >
> > Tegra20 and Tegra30 are fully compatible with existing Tegra DSI driver
> > apart from clock configuration and pad calibration which are addressed by
> > this patch.
> >
> > Signed-off-by: Svyatoslav Ryhel <[email protected]>
> > ---
> > drivers/gpu/drm/tegra/drm.c | 2 +
> > drivers/gpu/drm/tegra/dsi.c | 107 +++++++++++++++++++++++++-----------
> > drivers/gpu/drm/tegra/dsi.h | 10 ++++
> > 3 files changed, 88 insertions(+), 31 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
> > index 4596073fe28f..5d64cd57e764 100644
> > --- a/drivers/gpu/drm/tegra/drm.c
> > +++ b/drivers/gpu/drm/tegra/drm.c
> > @@ -1359,10 +1359,12 @@ static SIMPLE_DEV_PM_OPS(host1x_drm_pm_ops,
> > host1x_drm_suspend,
> >
> > static const struct of_device_id host1x_drm_subdevs[] = {
> > { .compatible = "nvidia,tegra20-dc", },
> > + { .compatible = "nvidia,tegra20-dsi", },
> > { .compatible = "nvidia,tegra20-hdmi", },
> > { .compatible = "nvidia,tegra20-gr2d", },
> > { .compatible = "nvidia,tegra20-gr3d", },
> > { .compatible = "nvidia,tegra30-dc", },
> > + { .compatible = "nvidia,tegra30-dsi", },
> > { .compatible = "nvidia,tegra30-hdmi", },
> > { .compatible = "nvidia,tegra30-gr2d", },
> > { .compatible = "nvidia,tegra30-gr3d", },
> > diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
> > index 02a661d86751..ebc78dceaee6 100644
> > --- a/drivers/gpu/drm/tegra/dsi.c
> > +++ b/drivers/gpu/drm/tegra/dsi.c
> > @@ -53,6 +53,11 @@ to_dsi_state(struct drm_connector_state *state)
> > return container_of(state, struct tegra_dsi_state, base);
> > }
> >
> > +struct tegra_dsi_config {
> > + bool has_multiple_pad_controls;
> > + bool has_mux_parent_clk;
> > +};
> > +
> > struct tegra_dsi {
> > struct host1x_client client;
> > struct tegra_output output;
> > @@ -82,6 +87,8 @@ struct tegra_dsi {
> > /* for ganged-mode support */
> > struct tegra_dsi *master;
> > struct tegra_dsi *slave;
> > +
> > + const struct tegra_dsi_config *config;
> > };
> >
> > static inline struct tegra_dsi *
> > @@ -663,39 +670,46 @@ static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
> > {
> > u32 value;
> >
> > - value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0);
> > - tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
> > + if (dsi->config->has_multiple_pad_controls) {
> > + /*
> > + * XXX Is this still needed? The module reset is deasserted
> > right
> > + * before this function is called.
> > + */
> > + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
> > + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
> > + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
> > + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
> > + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
> > +
> > + value = DSI_PAD_CONTROL_VS1_PULLDN(0) |
> > DSI_PAD_CONTROL_VS1_PDIO(0);
> > + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
> > +
> > + value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) |
> > + DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) |
> > + DSI_PAD_OUT_CLK(0x0);
> > + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2);
> > +
> > + value = DSI_PAD_PREEMP_PD_CLK(0x3) |
> > DSI_PAD_PREEMP_PU_CLK(0x3) |
> > + DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3);
> > + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3);
> > + } else {
> > + value = DSI_PAD_CONTROL_LPUPADJ(0x1) |
> > DSI_PAD_CONTROL_LPDNADJ(0x1) |
> > + DSI_PAD_CONTROL_PREEMP_EN(0x1) |
> > DSI_PAD_CONTROL_SLEWDNADJ(0x6) |
> > + DSI_PAD_CONTROL_SLEWUPADJ(0x6) |
> > DSI_PAD_CONTROL_PDIO(0) |
> > + DSI_PAD_CONTROL_PDIO_CLK(0) |
> > DSI_PAD_CONTROL_PULLDN_ENAB(0);
> > + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
> > + }
> >
> > return 0;
> > }
> >
> > static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi)
> > {
> > - u32 value;
> > int err;
> >
> > - /*
> > - * XXX Is this still needed? The module reset is deasserted right
> > - * before this function is called.
> > - */
> > - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
> > - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
> > - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
> > - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
> > - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
> > -
> > /* start calibration */
> > tegra_dsi_pad_enable(dsi);
> >
> > - value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) |
> > - DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) |
> > - DSI_PAD_OUT_CLK(0x0);
> > - tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2);
> > -
> > - value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) |
> > - DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3);
> > - tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3);
> > -
> > err = tegra_mipi_start_calibration(dsi->mipi);
> > if (err < 0)
> > return err;
> > @@ -1568,6 +1582,7 @@ static int tegra_dsi_probe(struct platform_device
> > *pdev)
> > if (!dsi)
> > return -ENOMEM;
> >
> > + dsi->config = of_device_get_match_data(&pdev->dev);
> > dsi->output.dev = dsi->dev = &pdev->dev;
> > dsi->video_fifo_depth = 1920;
> > dsi->host_fifo_depth = 64;
> > @@ -1606,7 +1621,7 @@ static int tegra_dsi_probe(struct platform_device
> > *pdev)
> > goto remove;
> > }
> >
> > - dsi->clk_lp = devm_clk_get(&pdev->dev, "lp");
> > + dsi->clk_lp = devm_clk_get_optional(&pdev->dev, "lp");
> > if (IS_ERR(dsi->clk_lp)) {
> > err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk_lp),
> > "cannot get low-power clock\n");
> > @@ -1627,10 +1642,12 @@ static int tegra_dsi_probe(struct platform_device
> > *pdev)
> > goto remove;
> > }
> >
> > - err = tegra_dsi_setup_clocks(dsi);
> > - if (err < 0) {
> > - dev_err(&pdev->dev, "cannot setup clocks\n");
> > - goto remove;
> > + if (dsi->config->has_mux_parent_clk) {
> > + err = tegra_dsi_setup_clocks(dsi);
> > + if (err < 0) {
> > + dev_err(&pdev->dev, "cannot setup clocks\n");
> > + goto remove;
> > + }
> > }
> >
> > dsi->regs = devm_platform_ioremap_resource(pdev, 0);
> > @@ -1694,11 +1711,39 @@ static void tegra_dsi_remove(struct platform_device
> > *pdev)
> > tegra_mipi_free(dsi->mipi);
> > }
> >
> > +static const struct tegra_dsi_config tegra20_dsi_config = {
> > + .has_multiple_pad_controls = false,
> > + .has_mux_parent_clk = false,
> > +};
> > +
> > +/*
> > + * Tegra30 allows DSIA/DSIB to be muxed to either PLL_D or PLL_D2; this is
> > + * simply not modeled in the clock driver yet. If this functionality is
> > + * required, the has_mux_parent_clk flag can be set to true once the clock
> > + * driver is patched.
> > + */
> > +static const struct tegra_dsi_config tegra30_dsi_config = {
> > + .has_multiple_pad_controls = false,
> > + .has_mux_parent_clk = false,
> > +};
> > +
> > +static const struct tegra_dsi_config tegra114_dsi_config = {
> > + .has_multiple_pad_controls = true,
> > + .has_mux_parent_clk = true,
> > +};
> > +
> > +static const struct tegra_dsi_config tegra124_dsi_config = {
> > + .has_multiple_pad_controls = true,
> > + .has_mux_parent_clk = false,
> > +};
>
> Mikko, I have an update regarding Tegra124 config. If
> tegra_dsi_setup_clocks is not called for Tegra124 (has_mux_parent_clk
> = false) DSI will not work. I cannot say for sure what is going on
> with clocks since my tegra124 device has broken usb in mainline Linux
> and without panel is no go. I would like to address this before this
> series is picked.
>
Ok, from what I can tell, tegra_dsi_setup_clocks ensures that the
Tegra124 DSI parent gate clock is properly configured. I propose
setting has_mux_parent_clk to true for Tegra124, and I will add a
comment to explain why.
Smth like this: "Tegra124 and Tegra210 don't have an actual mux parent
for DSI clocks, but the gate parent clock they use requires the same
setup."
Or flag name can be changed to has_mux_gate_parent_clk or any other
name which fits.
> > +
> > static const struct of_device_id tegra_dsi_of_match[] = {
> > - { .compatible = "nvidia,tegra210-dsi", },
> > - { .compatible = "nvidia,tegra132-dsi", },
> > - { .compatible = "nvidia,tegra124-dsi", },
> > - { .compatible = "nvidia,tegra114-dsi", },
> > + { .compatible = "nvidia,tegra210-dsi", .data = &tegra124_dsi_config
> > },
> > + { .compatible = "nvidia,tegra132-dsi", .data = &tegra124_dsi_config
> > },
> > + { .compatible = "nvidia,tegra124-dsi", .data = &tegra124_dsi_config
> > },
> > + { .compatible = "nvidia,tegra114-dsi", .data = &tegra114_dsi_config
> > },
> > + { .compatible = "nvidia,tegra30-dsi", .data = &tegra30_dsi_config },
> > + { .compatible = "nvidia,tegra20-dsi", .data = &tegra20_dsi_config },
> > { },
> > };
> > MODULE_DEVICE_TABLE(of, tegra_dsi_of_match);
> > diff --git a/drivers/gpu/drm/tegra/dsi.h b/drivers/gpu/drm/tegra/dsi.h
> > index f39594e65e97..d834ac0c47ab 100644
> > --- a/drivers/gpu/drm/tegra/dsi.h
> > +++ b/drivers/gpu/drm/tegra/dsi.h
> > @@ -95,6 +95,16 @@
> > #define DSI_TALLY_LRX(x) (((x) & 0xff) << 8)
> > #define DSI_TALLY_HTX(x) (((x) & 0xff) << 0)
> > #define DSI_PAD_CONTROL_0 0x4b
> > +/* Tegra20/Tegra30 */
> > +#define DSI_PAD_CONTROL_PULLDN_ENAB(x) (((x) & 0x1) << 28)
> > +#define DSI_PAD_CONTROL_SLEWUPADJ(x) (((x) & 0x7) << 24)
> > +#define DSI_PAD_CONTROL_SLEWDNADJ(x) (((x) & 0x7) << 20)
> > +#define DSI_PAD_CONTROL_PREEMP_EN(x) (((x) & 0x1) << 19)
> > +#define DSI_PAD_CONTROL_PDIO_CLK(x) (((x) & 0x1) << 18)
> > +#define DSI_PAD_CONTROL_PDIO(x) (((x) & 0x3) << 16)
> > +#define DSI_PAD_CONTROL_LPUPADJ(x) (((x) & 0x3) << 14)
> > +#define DSI_PAD_CONTROL_LPDNADJ(x) (((x) & 0x3) << 12)
> > +/* Tegra114+ */
> > #define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0)
> > #define DSI_PAD_CONTROL_VS1_PDIO_CLK (1 << 8)
> > #define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16)
> > --
> > 2.51.0
> >