summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/tegra
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/tegra')
-rw-r--r--drivers/gpu/drm/tegra/sor.c421
1 files changed, 74 insertions, 347 deletions
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index bf2e31199166..63831c37ad35 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -371,10 +371,11 @@ struct tegra_sor_regs {
};
struct tegra_sor_soc {
- bool supports_edp;
bool supports_lvds;
bool supports_hdmi;
bool supports_dp;
+ bool supports_audio;
+ bool supports_hdcp;
const struct tegra_sor_regs *regs;
bool has_nvdisplay;
@@ -1806,306 +1807,6 @@ static const struct drm_encoder_funcs tegra_sor_encoder_funcs = {
.destroy = tegra_output_encoder_destroy,
};
-static void tegra_sor_edp_disable(struct drm_encoder *encoder)
-{
- struct tegra_output *output = encoder_to_output(encoder);
- struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
- struct tegra_sor *sor = to_sor(output);
- u32 value;
- int err;
-
- if (output->panel)
- drm_panel_disable(output->panel);
-
- err = tegra_sor_detach(sor);
- if (err < 0)
- dev_err(sor->dev, "failed to detach SOR: %d\n", err);
-
- tegra_sor_writel(sor, 0, SOR_STATE1);
- tegra_sor_update(sor);
-
- value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
- value &= ~SOR_ENABLE(0);
- tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
- tegra_dc_commit(dc);
-
- err = tegra_sor_power_down(sor);
- if (err < 0)
- dev_err(sor->dev, "failed to power down SOR: %d\n", err);
-
- if (sor->aux) {
- err = drm_dp_aux_disable(sor->aux);
- if (err < 0)
- dev_err(sor->dev, "failed to disable DP: %d\n", err);
- }
-
- err = tegra_io_pad_power_disable(sor->pad);
- if (err < 0)
- dev_err(sor->dev, "failed to power off I/O pad: %d\n", err);
-
- if (output->panel)
- drm_panel_unprepare(output->panel);
-
- pm_runtime_put(sor->dev);
-}
-
-#if 0
-static int calc_h_ref_to_sync(const struct drm_display_mode *mode,
- unsigned int *value)
-{
- unsigned int hfp, hsw, hbp, a = 0, b;
-
- hfp = mode->hsync_start - mode->hdisplay;
- hsw = mode->hsync_end - mode->hsync_start;
- hbp = mode->htotal - mode->hsync_end;
-
- pr_info("hfp: %u, hsw: %u, hbp: %u\n", hfp, hsw, hbp);
-
- b = hfp - 1;
-
- pr_info("a: %u, b: %u\n", a, b);
- pr_info("a + hsw + hbp = %u\n", a + hsw + hbp);
-
- if (a + hsw + hbp <= 11) {
- a = 1 + 11 - hsw - hbp;
- pr_info("a: %u\n", a);
- }
-
- if (a > b)
- return -EINVAL;
-
- if (hsw < 1)
- return -EINVAL;
-
- if (mode->hdisplay < 16)
- return -EINVAL;
-
- if (value) {
- if (b > a && a % 2)
- *value = a + 1;
- else
- *value = a;
- }
-
- return 0;
-}
-#endif
-
-static void tegra_sor_edp_enable(struct drm_encoder *encoder)
-{
- struct tegra_output *output = encoder_to_output(encoder);
- struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
- struct tegra_sor *sor = to_sor(output);
- struct tegra_sor_config config;
- struct tegra_sor_state *state;
- struct drm_display_mode *mode;
- struct drm_display_info *info;
- unsigned int i;
- u32 value;
- int err;
-
- state = to_sor_state(output->connector.state);
- mode = &encoder->crtc->state->adjusted_mode;
- info = &output->connector.display_info;
-
- pm_runtime_get_sync(sor->dev);
-
- /* switch to safe parent clock */
- err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
- if (err < 0)
- dev_err(sor->dev, "failed to set safe parent clock: %d\n", err);
-
- err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS);
- if (err < 0)
- dev_err(sor->dev, "failed to power on LVDS rail: %d\n", err);
-
- usleep_range(20, 100);
-
- err = drm_dp_aux_enable(sor->aux);
- if (err < 0)
- dev_err(sor->dev, "failed to enable DPAUX: %d\n", err);
-
- err = drm_dp_link_probe(sor->aux, &sor->link);
- if (err < 0)
- dev_err(sor->dev, "failed to probe eDP link: %d\n", err);
-
- tegra_sor_filter_rates(sor);
-
- err = drm_dp_link_choose(&sor->link, mode, info);
- if (err < 0)
- dev_err(sor->dev, "failed to choose link: %d\n", err);
-
- if (output->panel)
- drm_panel_prepare(output->panel);
-
- value = tegra_sor_readl(sor, sor->soc->regs->pll2);
- value &= ~SOR_PLL2_BANDGAP_POWERDOWN;
- tegra_sor_writel(sor, value, sor->soc->regs->pll2);
-
- usleep_range(20, 40);
-
- value = tegra_sor_readl(sor, sor->soc->regs->pll3);
- value |= SOR_PLL3_PLL_VDD_MODE_3V3;
- tegra_sor_writel(sor, value, sor->soc->regs->pll3);
-
- value = tegra_sor_readl(sor, sor->soc->regs->pll0);
- value &= ~(SOR_PLL0_VCOPD | SOR_PLL0_PWR);
- tegra_sor_writel(sor, value, sor->soc->regs->pll0);
-
- value = tegra_sor_readl(sor, sor->soc->regs->pll2);
- value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
- value |= SOR_PLL2_SEQ_PLLCAPPD;
- tegra_sor_writel(sor, value, sor->soc->regs->pll2);
-
- usleep_range(200, 400);
-
- value = tegra_sor_readl(sor, sor->soc->regs->pll2);
- value &= ~SOR_PLL2_POWERDOWN_OVERRIDE;
- value &= ~SOR_PLL2_PORT_POWERDOWN;
- tegra_sor_writel(sor, value, sor->soc->regs->pll2);
-
- value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
- value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
- value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
- tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
-
- value = tegra_sor_readl(sor, SOR_DP_SPARE0);
- /* XXX not in TRM */
- value |= SOR_DP_SPARE_PANEL_INTERNAL;
- value |= SOR_DP_SPARE_SEQ_ENABLE;
- tegra_sor_writel(sor, value, SOR_DP_SPARE0);
-
- /* XXX not in TRM */
- tegra_sor_writel(sor, 0, SOR_LVDS);
-
- value = tegra_sor_readl(sor, sor->soc->regs->pll0);
- value &= ~SOR_PLL0_ICHPMP_MASK;
- value &= ~SOR_PLL0_VCOCAP_MASK;
- value |= SOR_PLL0_ICHPMP(0x1);
- value |= SOR_PLL0_VCOCAP(0x3);
- value |= SOR_PLL0_RESISTOR_EXT;
- tegra_sor_writel(sor, value, sor->soc->regs->pll0);
-
- /* XXX not in TRM */
- for (value = 0, i = 0; i < 5; i++)
- value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->xbar_cfg[i]) |
- SOR_XBAR_CTRL_LINK1_XSEL(i, i);
-
- tegra_sor_writel(sor, 0x00000000, SOR_XBAR_POL);
- tegra_sor_writel(sor, value, SOR_XBAR_CTRL);
-
- /*
- * Switch the pad clock to the DP clock. Note that we cannot actually
- * do this because Tegra186 and later don't support clk_set_parent()
- * on the sorX_pad_clkout clocks. We already do the equivalent above
- * using the DP_CLK_SEL mux of the SOR_CLK_CNTRL register.
- */
-#if 0
- err = clk_set_parent(sor->clk_pad, sor->clk_dp);
- if (err < 0) {
- dev_err(sor->dev, "failed to select pad parent clock: %d\n",
- err);
- return;
- }
-#endif
-
- /* switch the SOR clock to the pad clock */
- err = tegra_sor_set_parent_clock(sor, sor->clk_pad);
- if (err < 0) {
- dev_err(sor->dev, "failed to select SOR parent clock: %d\n",
- err);
- return;
- }
-
- /* switch the output clock to the parent pixel clock */
- err = clk_set_parent(sor->clk, sor->clk_parent);
- if (err < 0) {
- dev_err(sor->dev, "failed to select output parent clock: %d\n",
- err);
- return;
- }
-
- /* use DP-A protocol */
- value = tegra_sor_readl(sor, SOR_STATE1);
- value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
- value |= SOR_STATE_ASY_PROTOCOL_DP_A;
- tegra_sor_writel(sor, value, SOR_STATE1);
-
- /* enable port */
- value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
- value |= SOR_DP_LINKCTL_ENABLE;
- tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
-
- tegra_sor_dp_term_calibrate(sor);
-
- err = drm_dp_link_train(&sor->link);
- if (err < 0)
- dev_err(sor->dev, "link training failed: %d\n", err);
- else
- dev_dbg(sor->dev, "link training succeeded\n");
-
- err = drm_dp_link_power_up(sor->aux, &sor->link);
- if (err < 0)
- dev_err(sor->dev, "failed to power up eDP link: %d\n", err);
-
- /* compute configuration */
- memset(&config, 0, sizeof(config));
- config.bits_per_pixel = state->bpc * 3;
-
- err = tegra_sor_compute_config(sor, mode, &config, &sor->link);
- if (err < 0)
- dev_err(sor->dev, "failed to compute configuration: %d\n", err);
-
- tegra_sor_apply_config(sor, &config);
-
- err = tegra_sor_power_up(sor, 250);
- if (err < 0)
- dev_err(sor->dev, "failed to power up SOR: %d\n", err);
-
- /* CSTM (LVDS, link A/B, upper) */
- value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
- SOR_CSTM_UPPER;
- tegra_sor_writel(sor, value, SOR_CSTM);
-
- /* use DP-A protocol */
- value = tegra_sor_readl(sor, SOR_STATE1);
- value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
- value |= SOR_STATE_ASY_PROTOCOL_DP_A;
- tegra_sor_writel(sor, value, SOR_STATE1);
-
- tegra_sor_mode_set(sor, mode, state);
-
- /* PWM setup */
- err = tegra_sor_setup_pwm(sor, 250);
- if (err < 0)
- dev_err(sor->dev, "failed to setup PWM: %d\n", err);
-
- tegra_sor_update(sor);
-
- err = tegra_sor_power_up(sor, 250);
- if (err < 0)
- dev_err(sor->dev, "failed to power up SOR: %d\n", err);
-
- /* attach and wake up */
- err = tegra_sor_attach(sor);
- if (err < 0)
- dev_err(sor->dev, "failed to attach SOR: %d\n", err);
-
- value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
- value |= SOR_ENABLE(0);
- tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
-
- tegra_dc_commit(dc);
-
- err = tegra_sor_wakeup(sor);
- if (err < 0)
- dev_err(sor->dev, "failed to wakeup SOR: %d\n", err);
-
- if (output->panel)
- drm_panel_enable(output->panel);
-}
-
static int
tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
@@ -2155,12 +1856,6 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder,
return 0;
}
-static const struct drm_encoder_helper_funcs tegra_sor_edp_helpers = {
- .disable = tegra_sor_edp_disable,
- .enable = tegra_sor_edp_enable,
- .atomic_check = tegra_sor_encoder_atomic_check,
-};
-
static inline u32 tegra_sor_hdmi_subpack(const u8 *ptr, size_t size)
{
u32 value = 0;
@@ -2535,9 +2230,9 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
if (!sor->soc->has_nvdisplay)
- value &= ~(SOR1_TIMING_CYA | SOR_ENABLE(1));
- else
- value &= ~SOR_ENABLE(sor->index);
+ value &= ~SOR1_TIMING_CYA;
+
+ value &= ~SOR_ENABLE(sor->index);
tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
@@ -2928,9 +2623,9 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
if (!sor->soc->has_nvdisplay)
- value |= SOR_ENABLE(1) | SOR1_TIMING_CYA;
- else
- value |= SOR_ENABLE(sor->index);
+ value |= SOR1_TIMING_CYA;
+
+ value |= SOR_ENABLE(sor->index);
tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
@@ -2965,6 +2660,9 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder)
u32 value;
int err;
+ if (output->panel)
+ drm_panel_disable(output->panel);
+
err = drm_dp_link_power_down(sor->aux, &sor->link);
if (err < 0)
dev_err(sor->dev, "failed to power down link: %d\n", err);
@@ -2977,12 +2675,7 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder)
tegra_sor_update(sor);
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
-
- if (!sor->soc->has_nvdisplay)
- value &= ~(SOR1_TIMING_CYA | SOR_ENABLE(1));
- else
- value &= ~SOR_ENABLE(sor->index);
-
+ value &= ~SOR_ENABLE(sor->index);
tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
tegra_dc_commit(dc);
@@ -3010,6 +2703,9 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder)
if (err < 0)
dev_err(sor->dev, "failed disable DPAUX: %d\n", err);
+ if (output->panel)
+ drm_panel_unprepare(output->panel);
+
pm_runtime_put(sor->dev);
}
@@ -3051,10 +2747,15 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)
if (err < 0)
dev_err(sor->dev, "failed to probe DP link: %d\n", err);
+ tegra_sor_filter_rates(sor);
+
err = drm_dp_link_choose(&sor->link, mode, info);
if (err < 0)
dev_err(sor->dev, "failed to choose link: %d\n", err);
+ if (output->panel)
+ drm_panel_prepare(output->panel);
+
value = tegra_sor_readl(sor, sor->soc->regs->pll2);
value &= ~SOR_PLL2_BANDGAP_POWERDOWN;
tegra_sor_writel(sor, value, sor->soc->regs->pll2);
@@ -3083,14 +2784,23 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)
value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
- value |= SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK;
+
+ if (output->panel)
+ value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK;
+ else
+ value |= SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK;
+
tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
usleep_range(200, 400);
value = tegra_sor_readl(sor, SOR_DP_SPARE0);
/* XXX not in TRM */
- value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
+ if (output->panel)
+ value |= SOR_DP_SPARE_PANEL_INTERNAL;
+ else
+ value &= ~SOR_DP_SPARE_PANEL_INTERNAL;
+
value |= SOR_DP_SPARE_SEQ_ENABLE;
tegra_sor_writel(sor, value, SOR_DP_SPARE0);
@@ -3177,6 +2887,19 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)
tegra_sor_apply_config(sor, &config);
tegra_sor_mode_set(sor, mode, state);
+
+ if (output->panel) {
+ /* CSTM (LVDS, link A/B, upper) */
+ value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
+ SOR_CSTM_UPPER;
+ tegra_sor_writel(sor, value, SOR_CSTM);
+
+ /* PWM setup */
+ err = tegra_sor_setup_pwm(sor, 250);
+ if (err < 0)
+ dev_err(sor->dev, "failed to setup PWM: %d\n", err);
+ }
+
tegra_sor_update(sor);
err = tegra_sor_power_up(sor, 250);
@@ -3197,6 +2920,9 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)
err = tegra_sor_wakeup(sor);
if (err < 0)
dev_err(sor->dev, "failed to wakeup SOR: %d\n", err);
+
+ if (output->panel)
+ drm_panel_enable(output->panel);
}
static const struct drm_encoder_helper_funcs tegra_sor_dp_helpers = {
@@ -3205,10 +2931,6 @@ static const struct drm_encoder_helper_funcs tegra_sor_dp_helpers = {
.atomic_check = tegra_sor_encoder_atomic_check,
};
-static const struct tegra_sor_ops tegra_sor_edp_ops = {
- .name = "eDP",
-};
-
static int tegra_sor_hdmi_probe(struct tegra_sor *sor)
{
int err;
@@ -3330,10 +3052,10 @@ static int tegra_sor_init(struct host1x_client *client)
encoder = DRM_MODE_ENCODER_LVDS;
}
} else {
- if (sor->ops == &tegra_sor_edp_ops) {
+ if (sor->output.panel) {
connector = DRM_MODE_CONNECTOR_eDP;
encoder = DRM_MODE_ENCODER_TMDS;
- helpers = &tegra_sor_edp_helpers;
+ helpers = &tegra_sor_dp_helpers;
} else {
connector = DRM_MODE_CONNECTOR_DisplayPort;
encoder = DRM_MODE_ENCODER_TMDS;
@@ -3575,10 +3297,11 @@ static const u8 tegra124_sor_tx_pu[4][4][4] = {
};
static const struct tegra_sor_soc tegra124_sor = {
- .supports_edp = true,
.supports_lvds = true,
.supports_hdmi = false,
- .supports_dp = false,
+ .supports_dp = true,
+ .supports_audio = false,
+ .supports_hdcp = false,
.regs = &tegra124_sor_regs,
.has_nvdisplay = false,
.xbar_cfg = tegra124_sor_xbar_cfg,
@@ -3614,10 +3337,11 @@ static const u8 tegra132_sor_pre_emphasis[4][4][4] = {
};
static const struct tegra_sor_soc tegra132_sor = {
- .supports_edp = true,
.supports_lvds = true,
.supports_hdmi = false,
- .supports_dp = false,
+ .supports_dp = true,
+ .supports_audio = false,
+ .supports_hdcp = false,
.regs = &tegra124_sor_regs,
.has_nvdisplay = false,
.xbar_cfg = tegra124_sor_xbar_cfg,
@@ -3652,10 +3376,11 @@ static const u8 tegra210_sor_lane_map[4] = {
};
static const struct tegra_sor_soc tegra210_sor = {
- .supports_edp = true,
.supports_lvds = false,
.supports_hdmi = false,
- .supports_dp = false,
+ .supports_dp = true,
+ .supports_audio = false,
+ .supports_hdcp = false,
.regs = &tegra210_sor_regs,
.has_nvdisplay = false,
@@ -3669,10 +3394,11 @@ static const struct tegra_sor_soc tegra210_sor = {
};
static const struct tegra_sor_soc tegra210_sor1 = {
- .supports_edp = false,
.supports_lvds = false,
.supports_hdmi = true,
.supports_dp = true,
+ .supports_audio = true,
+ .supports_hdcp = true,
.regs = &tegra210_sor_regs,
.has_nvdisplay = false,
@@ -3751,10 +3477,11 @@ static const u8 tegra186_sor_pre_emphasis[4][4][4] = {
};
static const struct tegra_sor_soc tegra186_sor = {
- .supports_edp = false,
.supports_lvds = false,
.supports_hdmi = true,
.supports_dp = true,
+ .supports_audio = true,
+ .supports_hdcp = true,
.regs = &tegra186_sor_regs,
.has_nvdisplay = true,
@@ -3785,10 +3512,11 @@ static const struct tegra_sor_regs tegra194_sor_regs = {
};
static const struct tegra_sor_soc tegra194_sor = {
- .supports_edp = true,
.supports_lvds = false,
.supports_hdmi = true,
.supports_dp = true,
+ .supports_audio = true,
+ .supports_hdcp = true,
.regs = &tegra194_sor_regs,
.has_nvdisplay = true,
@@ -3836,7 +3564,7 @@ static int tegra_sor_parse_dt(struct tegra_sor *sor)
*/
sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index;
} else {
- if (sor->soc->supports_edp)
+ if (!sor->soc->supports_audio)
sor->index = 0;
else
sor->index = 1;
@@ -3929,16 +3657,15 @@ static int tegra_sor_probe(struct platform_device *pdev)
return -ENODEV;
}
} else {
- if (sor->soc->supports_edp) {
- sor->ops = &tegra_sor_edp_ops;
- sor->pad = TEGRA_IO_PAD_LVDS;
- } else if (sor->soc->supports_dp) {
- sor->ops = &tegra_sor_dp_ops;
- sor->pad = TEGRA_IO_PAD_LVDS;
- } else {
- dev_err(&pdev->dev, "unknown (DP) support\n");
- return -ENODEV;
- }
+ np = of_parse_phandle(pdev->dev.of_node, "nvidia,panel", 0);
+ /*
+ * No need to keep this around since we only use it as a check
+ * to see if a panel is connected (eDP) or not (DP).
+ */
+ of_node_put(np);
+
+ sor->ops = &tegra_sor_dp_ops;
+ sor->pad = TEGRA_IO_PAD_LVDS;
}
err = tegra_sor_parse_dt(sor);