mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
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:
parent
ac75f6d606
commit
39e535c95f
|
|
@ -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", },
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user