drm/tegra: dsi: Add support for Tegra20/Tegra30

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 <clamor95@gmail.com>
Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
Link: https://patch.msgid.link/20260511074538.24563-2-clamor95@gmail.com
This commit is contained in:
Svyatoslav Ryhel 2026-05-11 10:45:36 +03:00 committed by Thierry Reding
parent ac75f6d606
commit 39e535c95f
3 changed files with 98 additions and 31 deletions

View File

@ -1358,10 +1358,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", },

View File

@ -54,6 +54,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;
@ -83,6 +88,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 *
@ -665,39 +672,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;
@ -1174,6 +1188,12 @@ static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi)
struct clk *parent;
int err;
/*
* Tegra124+ uses a clock gate, not a mux, so this step
* should be redundant for configuration; yet, DSI refuses
* to work without it.
*/
parent = clk_get_parent(dsi->clk);
if (!parent)
return -EINVAL;
@ -1562,6 +1582,10 @@ static int tegra_dsi_probe(struct platform_device *pdev)
if (!dsi)
return -ENOMEM;
dsi->config = device_get_match_data(&pdev->dev);
if (!dsi->config)
return -ENODEV;
dsi->output.dev = dsi->dev = &pdev->dev;
dsi->video_fifo_depth = 1920;
dsi->host_fifo_depth = 64;
@ -1600,7 +1624,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");
@ -1621,10 +1645,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);
@ -1688,11 +1714,40 @@ 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,
};
/* TODO: figure out why has_mux_parent_clk = true is necessary on Tegra124+ */
static const struct tegra_dsi_config tegra124_dsi_config = {
.has_multiple_pad_controls = true,
.has_mux_parent_clk = true,
};
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);

View File

@ -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)