diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6790e2d869c49..906752d866225 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -54,6 +54,19 @@ static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) return value; } +bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev) +{ + struct device_node *np = dc->dev->of_node; + struct of_phandle_iterator it; + int err; + + of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0) + if (it.node == dev->of_node) + return true; + + return false; +} + /* * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy. diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 018fea74fb506..336d2c22f521d 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -141,6 +141,7 @@ struct tegra_dc_window { }; /* from dc.c */ +bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev); void tegra_dc_commit(struct tegra_dc *dc); int tegra_dc_state_setup_clock(struct tegra_dc *dc, struct drm_crtc_state *crtc_state, @@ -289,10 +290,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); #define HDMI_ENABLE (1 << 30) #define DSI_ENABLE (1 << 29) #define SOR1_TIMING_CYA (1 << 27) -#define SOR1_ENABLE (1 << 26) -#define SOR_ENABLE (1 << 25) #define CURSOR_ENABLE (1 << 16) +#define SOR_ENABLE(x) (1 << (25 + (x))) + #define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403 #define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24) #define WINDOW_A_THRESHOLD(x) (((x) & 0x7f) << 16) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 8ade997c2b6c2..2fa1b48e14d22 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1296,6 +1296,8 @@ static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra210-vic", }, { .compatible = "nvidia,tegra186-display", }, { .compatible = "nvidia,tegra186-dc", }, + { .compatible = "nvidia,tegra186-sor", }, + { .compatible = "nvidia,tegra186-sor1", }, { .compatible = "nvidia,tegra186-vic", }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index d4dfd239e2a5e..c52bc5978d1c3 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -164,6 +164,8 @@ int tegra_output_probe(struct tegra_output *output); void tegra_output_remove(struct tegra_output *output); int tegra_output_init(struct drm_device *drm, struct tegra_output *output); void tegra_output_exit(struct tegra_output *output); +void tegra_output_find_possible_crtcs(struct tegra_output *output, + struct drm_device *drm); int tegra_output_connector_get_modes(struct drm_connector *connector); enum drm_connector_status diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 24f8a3b712b4a..ffe34bd0bb9d1 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -9,7 +9,9 @@ #include #include + #include "drm.h" +#include "dc.h" #include @@ -218,3 +220,25 @@ void tegra_output_exit(struct tegra_output *output) if (output->panel) drm_panel_detach(output->panel); } + +void tegra_output_find_possible_crtcs(struct tegra_output *output, + struct drm_device *drm) +{ + struct device *dev = output->dev; + struct drm_crtc *crtc; + unsigned int mask = 0; + + drm_for_each_crtc(crtc, drm) { + struct tegra_dc *dc = to_tegra_dc(crtc); + + if (tegra_dc_has_output(dc, dev)) + mask |= drm_crtc_mask(crtc); + } + + if (mask == 0) { + dev_warn(dev, "missing output definition for heads in DT\n"); + mask = 0x3; + } + + output->encoder.possible_crtcs = mask; +} diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index d51399587aca3..1d7f24df0b10d 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -28,17 +28,30 @@ #include "sor.h" #include "trace.h" +/* + * XXX Remove this after the commit adding it to soc/tegra/pmc.h has been + * merged. Having this around after the commit is merged should be safe since + * the preprocessor will effectively replace all occurrences and therefore no + * duplicate will be defined. + */ +#define TEGRA_IO_PAD_HDMI_DP0 26 + #define SOR_REKEY 0x38 struct tegra_sor_hdmi_settings { unsigned long frequency; u8 vcocap; + u8 filter; u8 ichpmp; u8 loadadj; - u8 termadj; - u8 tx_pu; - u8 bg_vref; + u8 tmds_termadj; + u8 tx_pu_value; + u8 bg_temp_coef; + u8 bg_vref_level; + u8 avdd10_level; + u8 avdd14_level; + u8 sparepll; u8 drive_current[4]; u8 preemphasis[4]; @@ -49,51 +62,76 @@ static const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = { { .frequency = 54000000, .vcocap = 0x0, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x10, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x10, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3a, 0x3a, 0x3a }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 75000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x40, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x40, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3a, 0x3a, 0x3a }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 150000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3a, 0x3a, 0x3a }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 300000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0xa, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0xa, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3f, 0x3f, 0x3f }, .preemphasis = { 0x00, 0x17, 0x17, 0x17 }, }, { .frequency = 600000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x33, 0x3f, 0x3f, 0x3f }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, @@ -103,47 +141,146 @@ static const struct tegra_sor_hdmi_settings tegra210_sor_hdmi_defaults[] = { { .frequency = 75000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x40, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x40, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x29, 0x29, 0x29, 0x29 }, .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, }, { .frequency = 150000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x1, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0x8, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0x8, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x30, 0x37, 0x37, 0x37 }, .preemphasis = { 0x01, 0x02, 0x02, 0x02 }, }, { .frequency = 300000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0x6, .loadadj = 0x3, - .termadj = 0x9, - .tx_pu = 0x66, - .bg_vref = 0xf, + .tmds_termadj = 0x9, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0xf, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x30, 0x37, 0x37, 0x37 }, .preemphasis = { 0x10, 0x3e, 0x3e, 0x3e }, }, { .frequency = 600000000, .vcocap = 0x3, + .filter = 0x0, .ichpmp = 0xa, .loadadj = 0x3, - .termadj = 0xb, - .tx_pu = 0x66, - .bg_vref = 0xe, + .tmds_termadj = 0xb, + .tx_pu_value = 0x66, + .bg_temp_coef = 0x3, + .bg_vref_level = 0xe, + .avdd10_level = 0x4, + .avdd14_level = 0x4, + .sparepll = 0x0, .drive_current = { 0x35, 0x3e, 0x3e, 0x3e }, .preemphasis = { 0x02, 0x3f, 0x3f, 0x3f }, }, }; #endif +static const struct tegra_sor_hdmi_settings tegra186_sor_hdmi_defaults[] = { + { + .frequency = 54000000, + .vcocap = 0, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 0xf, + .tx_pu_value = 0, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x54, + .drive_current = { 0x3a, 0x3a, 0x3a, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 75000000, + .vcocap = 1, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 0xf, + .tx_pu_value = 0, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x44, + .drive_current = { 0x3a, 0x3a, 0x3a, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 150000000, + .vcocap = 3, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 15, + .tx_pu_value = 0x66 /* 0 */, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x00, /* 0x34 */ + .drive_current = { 0x3a, 0x3a, 0x3a, 0x37 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 300000000, + .vcocap = 3, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 15, + .tx_pu_value = 64, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x34, + .drive_current = { 0x3d, 0x3d, 0x3d, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + }, { + .frequency = 600000000, + .vcocap = 3, + .filter = 5, + .ichpmp = 5, + .loadadj = 3, + .tmds_termadj = 12, + .tx_pu_value = 96, + .bg_temp_coef = 3, + .bg_vref_level = 8, + .avdd10_level = 4, + .avdd14_level = 4, + .sparepll = 0x34, + .drive_current = { 0x3d, 0x3d, 0x3d, 0x33 }, + .preemphasis = { 0x00, 0x00, 0x00, 0x00 }, + } +}; + struct tegra_sor_regs { unsigned int head_state0; unsigned int head_state1; @@ -166,6 +303,7 @@ struct tegra_sor_soc { bool supports_dp; const struct tegra_sor_regs *regs; + bool has_nvdisplay; const struct tegra_sor_hdmi_settings *settings; unsigned int num_settings; @@ -188,6 +326,7 @@ struct tegra_sor { const struct tegra_sor_soc *soc; void __iomem *regs; + unsigned int index; struct reset_control *rst; struct clk *clk_parent; @@ -202,6 +341,7 @@ struct tegra_sor { struct drm_info_list *debugfs_files; const struct tegra_sor_ops *ops; + enum tegra_io_pad pad; /* for HDMI 2.0 */ struct tegra_sor_hdmi_settings *settings; @@ -480,47 +620,6 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, return 0; } -static void tegra_sor_dp_term_calibrate(struct tegra_sor *sor) -{ - u32 mask = 0x08, adj = 0, value; - - /* enable pad calibration logic */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); - value &= ~SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); - - value = tegra_sor_readl(sor, SOR_PLL1); - value |= SOR_PLL1_TMDS_TERM; - tegra_sor_writel(sor, value, SOR_PLL1); - - while (mask) { - adj |= mask; - - value = tegra_sor_readl(sor, SOR_PLL1); - value &= ~SOR_PLL1_TMDS_TERMADJ_MASK; - value |= SOR_PLL1_TMDS_TERMADJ(adj); - tegra_sor_writel(sor, value, SOR_PLL1); - - usleep_range(100, 200); - - value = tegra_sor_readl(sor, SOR_PLL1); - if (value & SOR_PLL1_TERM_COMPOUT) - adj &= ~mask; - - mask >>= 1; - } - - value = tegra_sor_readl(sor, SOR_PLL1); - value &= ~SOR_PLL1_TMDS_TERMADJ_MASK; - value |= SOR_PLL1_TMDS_TERMADJ(adj); - tegra_sor_writel(sor, value, SOR_PLL1); - - /* disable pad calibration logic */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); - value |= SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); -} - static void tegra_sor_super_update(struct tegra_sor *sor) { tegra_sor_writel(sor, 0, SOR_SUPER_STATE0); @@ -1217,6 +1316,7 @@ static const struct debugfs_reg32 tegra_sor_regs[] = { DEBUGFS_REG32(SOR_DP_MN1), DEBUGFS_REG32(SOR_DP_PADCTL0), DEBUGFS_REG32(SOR_DP_PADCTL1), + DEBUGFS_REG32(SOR_DP_PADCTL2), DEBUGFS_REG32(SOR_DP_DEBUG0), DEBUGFS_REG32(SOR_DP_DEBUG1), DEBUGFS_REG32(SOR_DP_SPARE0), @@ -1429,7 +1529,7 @@ static void tegra_sor_edp_disable(struct drm_encoder *encoder) */ if (dc) { value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value &= ~SOR_ENABLE; + value &= ~SOR_ENABLE(0); tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); @@ -1445,9 +1545,9 @@ static void tegra_sor_edp_disable(struct drm_encoder *encoder) dev_err(sor->dev, "failed to disable DP: %d\n", err); } - err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS); + err = tegra_io_pad_power_disable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power off I/O rail: %d\n", err); + dev_err(sor->dev, "failed to power off I/O pad: %d\n", err); if (output->panel) drm_panel_unprepare(output->panel); @@ -1605,9 +1705,9 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); /* step 2 */ - err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS); + err = tegra_io_pad_power_enable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power on I/O rail: %d\n", err); + dev_err(sor->dev, "failed to power on I/O pad: %d\n", err); usleep_range(5, 100); @@ -1785,7 +1885,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) tegra_sor_update(sor); value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value |= SOR_ENABLE; + value |= SOR_ENABLE(0); tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); @@ -1984,8 +2084,12 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) /* disable display to SOR clock */ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value &= ~SOR1_TIMING_CYA; - value &= ~SOR1_ENABLE; + + if (!sor->soc->has_nvdisplay) + value &= ~(SOR1_TIMING_CYA | SOR_ENABLE(1)); + else + value &= ~SOR_ENABLE(sor->index); + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); @@ -1994,9 +2098,9 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) if (err < 0) dev_err(sor->dev, "failed to power down SOR: %d\n", err); - err = tegra_io_rail_power_off(TEGRA_IO_RAIL_HDMI); + err = tegra_io_pad_power_disable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power off HDMI rail: %d\n", err); + dev_err(sor->dev, "failed to power off I/O pad: %d\n", err); pm_runtime_put(sor->dev); } @@ -2028,9 +2132,9 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) div = clk_get_rate(sor->clk) / 1000000 * 4; - err = tegra_io_rail_power_on(TEGRA_IO_RAIL_HDMI); + err = tegra_io_pad_power_enable(sor->pad); if (err < 0) - dev_err(sor->dev, "failed to power on HDMI rail: %d\n", err); + dev_err(sor->dev, "failed to power on I/O pad: %d\n", err); usleep_range(20, 100); @@ -2099,10 +2203,19 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK; tegra_sor_writel(sor, value, SOR_CLK_CNTRL); + /* SOR pad PLL stabilization time */ + usleep_range(250, 1000); + + value = tegra_sor_readl(sor, SOR_DP_LINKCTL0); + value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; + value |= SOR_DP_LINKCTL_LANE_COUNT(4); + tegra_sor_writel(sor, value, SOR_DP_LINKCTL0); + value = tegra_sor_readl(sor, SOR_DP_SPARE0); - value |= SOR_DP_SPARE_DISP_VIDEO_PREAMBLE; + value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE; value &= ~SOR_DP_SPARE_PANEL_INTERNAL; - value |= SOR_DP_SPARE_SEQ_ENABLE; + value &= ~SOR_DP_SPARE_SEQ_ENABLE; + value &= ~SOR_DP_SPARE_MACRO_SOR_CLK; tegra_sor_writel(sor, value, SOR_DP_SPARE0); value = SOR_SEQ_CTL_PU_PC(0) | SOR_SEQ_CTL_PU_PC_ALT(0) | @@ -2114,9 +2227,11 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_SEQ_INST(0)); tegra_sor_writel(sor, value, SOR_SEQ_INST(8)); - /* program the reference clock */ - value = SOR_REFCLK_DIV_INT(div) | SOR_REFCLK_DIV_FRAC(div); - tegra_sor_writel(sor, value, SOR_REFCLK); + if (!sor->soc->has_nvdisplay) { + /* program the reference clock */ + value = SOR_REFCLK_DIV_INT(div) | SOR_REFCLK_DIV_FRAC(div); + tegra_sor_writel(sor, value, SOR_REFCLK); + } /* XXX not in TRM */ for (value = 0, i = 0; i < 5; i++) @@ -2139,13 +2254,16 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) return; } - value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe); - /* XXX is this the proper check? */ - if (mode->clock < 75000) - value |= SOR_INPUT_CONTROL_ARM_VIDEO_RANGE_LIMITED; + if (!sor->soc->has_nvdisplay) { + value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe); + + /* XXX is this the proper check? */ + if (mode->clock < 75000) + value |= SOR_INPUT_CONTROL_ARM_VIDEO_RANGE_LIMITED; - tegra_sor_writel(sor, value, SOR_INPUT_CONTROL); + tegra_sor_writel(sor, value, SOR_INPUT_CONTROL); + } max_ac = ((mode->htotal - mode->hdisplay) - SOR_REKEY - 18) / 32; @@ -2153,20 +2271,23 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) SOR_HDMI_CTRL_AUDIO_LAYOUT | SOR_HDMI_CTRL_REKEY(SOR_REKEY); tegra_sor_writel(sor, value, SOR_HDMI_CTRL); - /* H_PULSE2 setup */ - pulse_start = h_ref_to_sync + (mode->hsync_end - mode->hsync_start) + - (mode->htotal - mode->hsync_end) - 10; + if (!dc->soc->has_nvdisplay) { + /* H_PULSE2 setup */ + pulse_start = h_ref_to_sync + + (mode->hsync_end - mode->hsync_start) + + (mode->htotal - mode->hsync_end) - 10; - value = PULSE_LAST_END_A | PULSE_QUAL_VACTIVE | - PULSE_POLARITY_HIGH | PULSE_MODE_NORMAL; - tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_CONTROL); + value = PULSE_LAST_END_A | PULSE_QUAL_VACTIVE | + PULSE_POLARITY_HIGH | PULSE_MODE_NORMAL; + tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_CONTROL); - value = PULSE_END(pulse_start + 8) | PULSE_START(pulse_start); - tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_POSITION_A); + value = PULSE_END(pulse_start + 8) | PULSE_START(pulse_start); + tegra_dc_writel(dc, value, DC_DISP_H_PULSE2_POSITION_A); - value = tegra_dc_readl(dc, DC_DISP_DISP_SIGNAL_OPTIONS0); - value |= H_PULSE2_ENABLE; - tegra_dc_writel(dc, value, DC_DISP_DISP_SIGNAL_OPTIONS0); + value = tegra_dc_readl(dc, DC_DISP_DISP_SIGNAL_OPTIONS0); + value |= H_PULSE2_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_SIGNAL_OPTIONS0); + } /* infoframe setup */ err = tegra_sor_hdmi_setup_avi_infoframe(sor, mode); @@ -2197,49 +2318,66 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value = tegra_sor_readl(sor, sor->soc->regs->pll0); value &= ~SOR_PLL0_ICHPMP_MASK; + value &= ~SOR_PLL0_FILTER_MASK; value &= ~SOR_PLL0_VCOCAP_MASK; value |= SOR_PLL0_ICHPMP(settings->ichpmp); + value |= SOR_PLL0_FILTER(settings->filter); value |= SOR_PLL0_VCOCAP(settings->vcocap); tegra_sor_writel(sor, value, sor->soc->regs->pll0); - tegra_sor_dp_term_calibrate(sor); - + /* XXX not in TRM */ value = tegra_sor_readl(sor, sor->soc->regs->pll1); value &= ~SOR_PLL1_LOADADJ_MASK; + value &= ~SOR_PLL1_TMDS_TERMADJ_MASK; value |= SOR_PLL1_LOADADJ(settings->loadadj); + value |= SOR_PLL1_TMDS_TERMADJ(settings->tmds_termadj); + value |= SOR_PLL1_TMDS_TERM; tegra_sor_writel(sor, value, sor->soc->regs->pll1); value = tegra_sor_readl(sor, sor->soc->regs->pll3); + value &= ~SOR_PLL3_BG_TEMP_COEF_MASK; value &= ~SOR_PLL3_BG_VREF_LEVEL_MASK; - value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref); + value &= ~SOR_PLL3_AVDD10_LEVEL_MASK; + value &= ~SOR_PLL3_AVDD14_LEVEL_MASK; + value |= SOR_PLL3_BG_TEMP_COEF(settings->bg_temp_coef); + value |= SOR_PLL3_BG_VREF_LEVEL(settings->bg_vref_level); + value |= SOR_PLL3_AVDD10_LEVEL(settings->avdd10_level); + value |= SOR_PLL3_AVDD14_LEVEL(settings->avdd14_level); tegra_sor_writel(sor, value, sor->soc->regs->pll3); - value = settings->drive_current[0] << 24 | - settings->drive_current[1] << 16 | - settings->drive_current[2] << 8 | - settings->drive_current[3] << 0; + value = settings->drive_current[3] << 24 | + settings->drive_current[2] << 16 | + settings->drive_current[1] << 8 | + settings->drive_current[0] << 0; tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT0); - value = settings->preemphasis[0] << 24 | - settings->preemphasis[1] << 16 | - settings->preemphasis[2] << 8 | - settings->preemphasis[3] << 0; + value = settings->preemphasis[3] << 24 | + settings->preemphasis[2] << 16 | + settings->preemphasis[1] << 8 | + settings->preemphasis[0] << 0; tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS0); value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~SOR_DP_PADCTL_TX_PU_MASK; value |= SOR_DP_PADCTL_TX_PU_ENABLE; - value |= SOR_DP_PADCTL_TX_PU(settings->tx_pu); + value |= SOR_DP_PADCTL_TX_PU(settings->tx_pu_value); tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl2); + value &= ~SOR_DP_PADCTL_SPAREPLL_MASK; + value |= SOR_DP_PADCTL_SPAREPLL(settings->sparepll); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl2); + /* power down pad calibration */ value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_PAD_CAL_PD; tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); - /* miscellaneous display controller settings */ - value = VSYNC_H_POSITION(1); - tegra_dc_writel(dc, value, DC_DISP_DISP_TIMING_OPTIONS); + if (!dc->soc->has_nvdisplay) { + /* miscellaneous display controller settings */ + value = VSYNC_H_POSITION(1); + tegra_dc_writel(dc, value, DC_DISP_DISP_TIMING_OPTIONS); + } value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL); value &= ~DITHER_CONTROL_MASK; @@ -2254,6 +2392,14 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value |= BASE_COLOR_SIZE_888; break; + case 10: + value |= BASE_COLOR_SIZE_101010; + break; + + case 12: + value |= BASE_COLOR_SIZE_121212; + break; + default: WARN(1, "%u bits-per-color not supported\n", state->bpc); value |= BASE_COLOR_SIZE_888; @@ -2262,6 +2408,12 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_dc_writel(dc, value, DC_DISP_DISP_COLOR_CONTROL); + /* XXX set display head owner */ + value = tegra_sor_readl(sor, SOR_STATE1); + value &= ~SOR_STATE_ASY_OWNER_MASK; + value |= SOR_STATE_ASY_OWNER(1 + dc->pipe); + tegra_sor_writel(sor, value, SOR_STATE1); + err = tegra_sor_power_up(sor, 250); if (err < 0) dev_err(sor->dev, "failed to power up SOR: %d\n", err); @@ -2282,15 +2434,32 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_sor_update(sor); + /* program preamble timing in SOR (XXX) */ + value = tegra_sor_readl(sor, SOR_DP_SPARE0); + value &= ~SOR_DP_SPARE_DISP_VIDEO_PREAMBLE; + tegra_sor_writel(sor, value, SOR_DP_SPARE0); + err = tegra_sor_attach(sor); if (err < 0) dev_err(sor->dev, "failed to attach SOR: %d\n", err); /* enable display to SOR clock and generate HDMI preamble */ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value |= SOR1_ENABLE | SOR1_TIMING_CYA; + + if (!sor->soc->has_nvdisplay) + value |= SOR_ENABLE(1) | SOR1_TIMING_CYA; + else + value |= SOR_ENABLE(sor->index); + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); + if (dc->soc->has_nvdisplay) { + value = tegra_dc_readl(dc, DC_DISP_CORE_SOR_SET_CONTROL(sor->index)); + value &= ~PROTOCOL_MASK; + value |= PROTOCOL_SINGLE_TMDS_A; + tegra_dc_writel(dc, value, DC_DISP_CORE_SOR_SET_CONTROL(sor->index)); + } + tegra_dc_commit(dc); err = tegra_sor_wakeup(sor); @@ -2356,7 +2525,7 @@ static int tegra_sor_init(struct host1x_client *client) return err; } - sor->output.encoder.possible_crtcs = 0x3; + tegra_output_find_possible_crtcs(&sor->output, drm); if (sor->aux) { err = drm_dp_aux_attach(sor->aux, &sor->output); @@ -2526,6 +2695,7 @@ static const struct tegra_sor_soc tegra124_sor = { .supports_hdmi = false, .supports_dp = false, .regs = &tegra124_sor_regs, + .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg, }; @@ -2550,6 +2720,7 @@ static const struct tegra_sor_soc tegra210_sor = { .supports_hdmi = false, .supports_dp = false, .regs = &tegra210_sor_regs, + .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg, }; @@ -2564,6 +2735,7 @@ static const struct tegra_sor_soc tegra210_sor1 = { .supports_dp = true, .regs = &tegra210_sor_regs, + .has_nvdisplay = false, .num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults), .settings = tegra210_sor_hdmi_defaults, @@ -2571,7 +2743,51 @@ static const struct tegra_sor_soc tegra210_sor1 = { .xbar_cfg = tegra210_sor_xbar_cfg, }; +static const struct tegra_sor_regs tegra186_sor_regs = { + .head_state0 = 0x151, + .head_state1 = 0x154, + .head_state2 = 0x157, + .head_state3 = 0x15a, + .head_state4 = 0x15d, + .head_state5 = 0x160, + .pll0 = 0x163, + .pll1 = 0x164, + .pll2 = 0x165, + .pll3 = 0x166, + .dp_padctl0 = 0x168, + .dp_padctl2 = 0x16a, +}; + +static const struct tegra_sor_soc tegra186_sor = { + .supports_edp = false, + .supports_lvds = false, + .supports_hdmi = false, + .supports_dp = true, + + .regs = &tegra186_sor_regs, + .has_nvdisplay = true, + + .xbar_cfg = tegra124_sor_xbar_cfg, +}; + +static const struct tegra_sor_soc tegra186_sor1 = { + .supports_edp = false, + .supports_lvds = false, + .supports_hdmi = true, + .supports_dp = true, + + .regs = &tegra186_sor_regs, + .has_nvdisplay = true, + + .num_settings = ARRAY_SIZE(tegra186_sor_hdmi_defaults), + .settings = tegra186_sor_hdmi_defaults, + + .xbar_cfg = tegra124_sor_xbar_cfg, +}; + static const struct of_device_id tegra_sor_of_match[] = { + { .compatible = "nvidia,tegra186-sor1", .data = &tegra186_sor1 }, + { .compatible = "nvidia,tegra186-sor", .data = &tegra186_sor }, { .compatible = "nvidia,tegra210-sor1", .data = &tegra210_sor1 }, { .compatible = "nvidia,tegra210-sor", .data = &tegra210_sor }, { .compatible = "nvidia,tegra124-sor", .data = &tegra124_sor }, @@ -2579,6 +2795,29 @@ static const struct of_device_id tegra_sor_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_sor_of_match); +static int tegra_sor_parse_dt(struct tegra_sor *sor) +{ + struct device_node *np = sor->dev->of_node; + u32 value; + int err; + + if (sor->soc->has_nvdisplay) { + err = of_property_read_u32(np, "nvidia,interface", &value); + if (err < 0) + return err; + + sor->index = value; + + /* + * override the default that we already set for Tegra210 and + * earlier + */ + sor->pad = TEGRA_IO_PAD_HDMI_DP0 + sor->index; + } + + return 0; +} + static int tegra_sor_probe(struct platform_device *pdev) { struct device_node *np; @@ -2614,6 +2853,7 @@ static int tegra_sor_probe(struct platform_device *pdev) if (!sor->aux) { if (sor->soc->supports_hdmi) { sor->ops = &tegra_sor_hdmi_ops; + sor->pad = TEGRA_IO_PAD_HDMI; } else if (sor->soc->supports_lvds) { dev_err(&pdev->dev, "LVDS not supported yet\n"); return -ENODEV; @@ -2624,6 +2864,7 @@ static int tegra_sor_probe(struct platform_device *pdev) } else { if (sor->soc->supports_edp) { sor->ops = &tegra_sor_edp_ops; + sor->pad = TEGRA_IO_PAD_LVDS; } else if (sor->soc->supports_dp) { dev_err(&pdev->dev, "DisplayPort not supported yet\n"); return -ENODEV; @@ -2633,6 +2874,10 @@ static int tegra_sor_probe(struct platform_device *pdev) } } + err = tegra_sor_parse_dt(sor); + if (err < 0) + return err; + err = tegra_output_probe(&sor->output); if (err < 0) { dev_err(&pdev->dev, "failed to probe output: %d\n", err); diff --git a/drivers/gpu/drm/tegra/sor.h b/drivers/gpu/drm/tegra/sor.h index 865c73b489685..e85ffc8d98e4f 100644 --- a/drivers/gpu/drm/tegra/sor.h +++ b/drivers/gpu/drm/tegra/sor.h @@ -89,6 +89,8 @@ #define SOR_PLL0 0x17 #define SOR_PLL0_ICHPMP_MASK (0xf << 24) #define SOR_PLL0_ICHPMP(x) (((x) & 0xf) << 24) +#define SOR_PLL0_FILTER_MASK (0xf << 16) +#define SOR_PLL0_FILTER(x) (((x) & 0xf) << 16) #define SOR_PLL0_VCOCAP_MASK (0xf << 8) #define SOR_PLL0_VCOCAP(x) (((x) & 0xf) << 8) #define SOR_PLL0_VCOCAP_RST SOR_PLL0_VCOCAP(3) @@ -122,10 +124,16 @@ #define SOR_PLL2_SEQ_PLL_PULLDOWN (1 << 16) #define SOR_PLL3 0x1a +#define SOR_PLL3_BG_TEMP_COEF_MASK (0xf << 28) +#define SOR_PLL3_BG_TEMP_COEF(x) (((x) & 0xf) << 28) #define SOR_PLL3_BG_VREF_LEVEL_MASK (0xf << 24) #define SOR_PLL3_BG_VREF_LEVEL(x) (((x) & 0xf) << 24) #define SOR_PLL3_PLL_VDD_MODE_1V8 (0 << 13) #define SOR_PLL3_PLL_VDD_MODE_3V3 (1 << 13) +#define SOR_PLL3_AVDD10_LEVEL_MASK (0xf << 8) +#define SOR_PLL3_AVDD10_LEVEL(x) (((x) & 0xf) << 8) +#define SOR_PLL3_AVDD14_LEVEL_MASK (0xf << 4) +#define SOR_PLL3_AVDD14_LEVEL(x) (((x) & 0xf) << 4) #define SOR_CSTM 0x1b #define SOR_CSTM_ROTCLK_MASK (0xf << 24) @@ -334,6 +342,10 @@ #define SOR_DP_LQ_CSTM1 0x70 #define SOR_DP_LQ_CSTM2 0x71 +#define SOR_DP_PADCTL2 0x73 +#define SOR_DP_PADCTL_SPAREPLL_MASK (0xff << 24) +#define SOR_DP_PADCTL_SPAREPLL(x) (((x) & 0xff) << 24) + #define SOR_HDMI_AUDIO_INFOFRAME_CTRL 0x9a #define SOR_HDMI_AUDIO_INFOFRAME_STATUS 0x9b #define SOR_HDMI_AUDIO_INFOFRAME_HEADER 0x9c