diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/sor.c')
-rw-r--r-- | drivers/gpu/drm/tegra/sor.c | 1117 |
1 files changed, 754 insertions, 363 deletions
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 476079f1255f..7d2a955fc515 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -22,23 +22,37 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_dp_helper.h> #include <drm/drm_panel.h> +#include <drm/drm_scdc_helper.h> #include "dc.h" #include "drm.h" #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 +63,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,53 +142,170 @@ 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; + unsigned int head_state2; + unsigned int head_state3; + unsigned int head_state4; + unsigned int head_state5; + unsigned int pll0; + unsigned int pll1; + unsigned int pll2; + unsigned int pll3; + unsigned int dp_padctl0; + unsigned int dp_padctl2; +}; + struct tegra_sor_soc { bool supports_edp; bool supports_lvds; bool supports_hdmi; bool supports_dp; + const struct tegra_sor_regs *regs; + bool has_nvdisplay; + const struct tegra_sor_hdmi_settings *settings; unsigned int num_settings; @@ -171,6 +327,7 @@ struct tegra_sor { const struct tegra_sor_soc *soc; void __iomem *regs; + unsigned int index; struct reset_control *rst; struct clk *clk_parent; @@ -183,10 +340,9 @@ struct tegra_sor { struct drm_dp_aux *aux; struct drm_info_list *debugfs_files; - struct drm_minor *minor; - struct dentry *debugfs; const struct tegra_sor_ops *ops; + enum tegra_io_pad pad; /* for HDMI 2.0 */ struct tegra_sor_hdmi_settings *settings; @@ -195,11 +351,16 @@ struct tegra_sor { struct regulator *avdd_io_supply; struct regulator *vdd_pll_supply; struct regulator *hdmi_supply; + + struct delayed_work scdc; + bool scdc_enabled; }; struct tegra_sor_state { struct drm_connector_state base; + unsigned int link_speed; + unsigned long pclk; unsigned int bpc; }; @@ -389,23 +550,23 @@ static int tegra_sor_dp_train_fast(struct tegra_sor *sor, /* disable LVDS mode */ tegra_sor_writel(sor, 0, SOR_LVDS); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_TX_PU_ENABLE; value &= ~SOR_DP_PADCTL_TX_PU_MASK; value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */ - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); usleep_range(10, 100); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0); - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); err = drm_dp_aux_prepare(sor->aux, DP_SET_ANSI_8B10B); if (err < 0) @@ -465,47 +626,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); @@ -897,31 +1017,31 @@ static void tegra_sor_mode_set(struct tegra_sor *sor, */ value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff); - tegra_sor_writel(sor, value, SOR_HEAD_STATE1(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state1 + dc->pipe); /* sync end = sync width - 1 */ vse = mode->vsync_end - mode->vsync_start - 1; hse = mode->hsync_end - mode->hsync_start - 1; value = ((vse & 0x7fff) << 16) | (hse & 0x7fff); - tegra_sor_writel(sor, value, SOR_HEAD_STATE2(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state2 + dc->pipe); /* blank end = sync end + back porch */ vbe = vse + (mode->vtotal - mode->vsync_end); hbe = hse + (mode->htotal - mode->hsync_end); value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff); - tegra_sor_writel(sor, value, SOR_HEAD_STATE3(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state3 + dc->pipe); /* blank start = blank end + active */ vbs = vbe + mode->vdisplay; hbs = hbe + mode->hdisplay; value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff); - tegra_sor_writel(sor, value, SOR_HEAD_STATE4(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state4 + dc->pipe); /* XXX interlacing support */ - tegra_sor_writel(sor, 0x001, SOR_HEAD_STATE5(dc->pipe)); + tegra_sor_writel(sor, 0x001, sor->soc->regs->head_state5 + dc->pipe); } static int tegra_sor_detach(struct tegra_sor *sor) @@ -1003,10 +1123,10 @@ static int tegra_sor_power_down(struct tegra_sor *sor) return err; } - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2); - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); /* stop lane sequencer */ value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP | @@ -1026,20 +1146,20 @@ static int tegra_sor_power_down(struct tegra_sor *sor) if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) return -ETIMEDOUT; - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value |= SOR_PLL2_PORT_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); - value = tegra_sor_readl(sor, SOR_PLL0); + value = tegra_sor_readl(sor, sor->soc->regs->pll0); value |= SOR_PLL0_VCOPD | SOR_PLL0_PWR; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value |= SOR_PLL2_SEQ_PLLCAPPD; value |= SOR_PLL2_SEQ_PLLCAPPD_ENFORCE; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); @@ -1105,12 +1225,133 @@ unlock: return err; } +#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name } + +static const struct debugfs_reg32 tegra_sor_regs[] = { + DEBUGFS_REG32(SOR_CTXSW), + DEBUGFS_REG32(SOR_SUPER_STATE0), + DEBUGFS_REG32(SOR_SUPER_STATE1), + DEBUGFS_REG32(SOR_STATE0), + DEBUGFS_REG32(SOR_STATE1), + DEBUGFS_REG32(SOR_HEAD_STATE0(0)), + DEBUGFS_REG32(SOR_HEAD_STATE0(1)), + DEBUGFS_REG32(SOR_HEAD_STATE1(0)), + DEBUGFS_REG32(SOR_HEAD_STATE1(1)), + DEBUGFS_REG32(SOR_HEAD_STATE2(0)), + DEBUGFS_REG32(SOR_HEAD_STATE2(1)), + DEBUGFS_REG32(SOR_HEAD_STATE3(0)), + DEBUGFS_REG32(SOR_HEAD_STATE3(1)), + DEBUGFS_REG32(SOR_HEAD_STATE4(0)), + DEBUGFS_REG32(SOR_HEAD_STATE4(1)), + DEBUGFS_REG32(SOR_HEAD_STATE5(0)), + DEBUGFS_REG32(SOR_HEAD_STATE5(1)), + DEBUGFS_REG32(SOR_CRC_CNTRL), + DEBUGFS_REG32(SOR_DP_DEBUG_MVID), + DEBUGFS_REG32(SOR_CLK_CNTRL), + DEBUGFS_REG32(SOR_CAP), + DEBUGFS_REG32(SOR_PWR), + DEBUGFS_REG32(SOR_TEST), + DEBUGFS_REG32(SOR_PLL0), + DEBUGFS_REG32(SOR_PLL1), + DEBUGFS_REG32(SOR_PLL2), + DEBUGFS_REG32(SOR_PLL3), + DEBUGFS_REG32(SOR_CSTM), + DEBUGFS_REG32(SOR_LVDS), + DEBUGFS_REG32(SOR_CRCA), + DEBUGFS_REG32(SOR_CRCB), + DEBUGFS_REG32(SOR_BLANK), + DEBUGFS_REG32(SOR_SEQ_CTL), + DEBUGFS_REG32(SOR_LANE_SEQ_CTL), + DEBUGFS_REG32(SOR_SEQ_INST(0)), + DEBUGFS_REG32(SOR_SEQ_INST(1)), + DEBUGFS_REG32(SOR_SEQ_INST(2)), + DEBUGFS_REG32(SOR_SEQ_INST(3)), + DEBUGFS_REG32(SOR_SEQ_INST(4)), + DEBUGFS_REG32(SOR_SEQ_INST(5)), + DEBUGFS_REG32(SOR_SEQ_INST(6)), + DEBUGFS_REG32(SOR_SEQ_INST(7)), + DEBUGFS_REG32(SOR_SEQ_INST(8)), + DEBUGFS_REG32(SOR_SEQ_INST(9)), + DEBUGFS_REG32(SOR_SEQ_INST(10)), + DEBUGFS_REG32(SOR_SEQ_INST(11)), + DEBUGFS_REG32(SOR_SEQ_INST(12)), + DEBUGFS_REG32(SOR_SEQ_INST(13)), + DEBUGFS_REG32(SOR_SEQ_INST(14)), + DEBUGFS_REG32(SOR_SEQ_INST(15)), + DEBUGFS_REG32(SOR_PWM_DIV), + DEBUGFS_REG32(SOR_PWM_CTL), + DEBUGFS_REG32(SOR_VCRC_A0), + DEBUGFS_REG32(SOR_VCRC_A1), + DEBUGFS_REG32(SOR_VCRC_B0), + DEBUGFS_REG32(SOR_VCRC_B1), + DEBUGFS_REG32(SOR_CCRC_A0), + DEBUGFS_REG32(SOR_CCRC_A1), + DEBUGFS_REG32(SOR_CCRC_B0), + DEBUGFS_REG32(SOR_CCRC_B1), + DEBUGFS_REG32(SOR_EDATA_A0), + DEBUGFS_REG32(SOR_EDATA_A1), + DEBUGFS_REG32(SOR_EDATA_B0), + DEBUGFS_REG32(SOR_EDATA_B1), + DEBUGFS_REG32(SOR_COUNT_A0), + DEBUGFS_REG32(SOR_COUNT_A1), + DEBUGFS_REG32(SOR_COUNT_B0), + DEBUGFS_REG32(SOR_COUNT_B1), + DEBUGFS_REG32(SOR_DEBUG_A0), + DEBUGFS_REG32(SOR_DEBUG_A1), + DEBUGFS_REG32(SOR_DEBUG_B0), + DEBUGFS_REG32(SOR_DEBUG_B1), + DEBUGFS_REG32(SOR_TRIG), + DEBUGFS_REG32(SOR_MSCHECK), + DEBUGFS_REG32(SOR_XBAR_CTRL), + DEBUGFS_REG32(SOR_XBAR_POL), + DEBUGFS_REG32(SOR_DP_LINKCTL0), + DEBUGFS_REG32(SOR_DP_LINKCTL1), + DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT0), + DEBUGFS_REG32(SOR_LANE_DRIVE_CURRENT1), + DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT0), + DEBUGFS_REG32(SOR_LANE4_DRIVE_CURRENT1), + DEBUGFS_REG32(SOR_LANE_PREEMPHASIS0), + DEBUGFS_REG32(SOR_LANE_PREEMPHASIS1), + DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS0), + DEBUGFS_REG32(SOR_LANE4_PREEMPHASIS1), + DEBUGFS_REG32(SOR_LANE_POSTCURSOR0), + DEBUGFS_REG32(SOR_LANE_POSTCURSOR1), + DEBUGFS_REG32(SOR_DP_CONFIG0), + DEBUGFS_REG32(SOR_DP_CONFIG1), + DEBUGFS_REG32(SOR_DP_MN0), + 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), + DEBUGFS_REG32(SOR_DP_SPARE1), + DEBUGFS_REG32(SOR_DP_AUDIO_CTRL), + DEBUGFS_REG32(SOR_DP_AUDIO_HBLANK_SYMBOLS), + DEBUGFS_REG32(SOR_DP_AUDIO_VBLANK_SYMBOLS), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_HEADER), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK0), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK1), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK2), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK3), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK4), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK5), + DEBUGFS_REG32(SOR_DP_GENERIC_INFOFRAME_SUBPACK6), + DEBUGFS_REG32(SOR_DP_TPG), + DEBUGFS_REG32(SOR_DP_TPG_CONFIG), + DEBUGFS_REG32(SOR_DP_LQ_CSTM0), + DEBUGFS_REG32(SOR_DP_LQ_CSTM1), + DEBUGFS_REG32(SOR_DP_LQ_CSTM2), +}; + static int tegra_sor_show_regs(struct seq_file *s, void *data) { struct drm_info_node *node = s->private; struct tegra_sor *sor = node->info_ent->data; struct drm_crtc *crtc = sor->output.encoder.crtc; struct drm_device *drm = node->minor->dev; + unsigned int i; int err = 0; drm_modeset_lock_all(drm); @@ -1120,126 +1361,12 @@ static int tegra_sor_show_regs(struct seq_file *s, void *data) goto unlock; } -#define DUMP_REG(name) \ - seq_printf(s, "%-38s %#05x %08x\n", #name, name, \ - tegra_sor_readl(sor, name)) - - DUMP_REG(SOR_CTXSW); - DUMP_REG(SOR_SUPER_STATE0); - DUMP_REG(SOR_SUPER_STATE1); - DUMP_REG(SOR_STATE0); - DUMP_REG(SOR_STATE1); - DUMP_REG(SOR_HEAD_STATE0(0)); - DUMP_REG(SOR_HEAD_STATE0(1)); - DUMP_REG(SOR_HEAD_STATE1(0)); - DUMP_REG(SOR_HEAD_STATE1(1)); - DUMP_REG(SOR_HEAD_STATE2(0)); - DUMP_REG(SOR_HEAD_STATE2(1)); - DUMP_REG(SOR_HEAD_STATE3(0)); - DUMP_REG(SOR_HEAD_STATE3(1)); - DUMP_REG(SOR_HEAD_STATE4(0)); - DUMP_REG(SOR_HEAD_STATE4(1)); - DUMP_REG(SOR_HEAD_STATE5(0)); - DUMP_REG(SOR_HEAD_STATE5(1)); - DUMP_REG(SOR_CRC_CNTRL); - DUMP_REG(SOR_DP_DEBUG_MVID); - DUMP_REG(SOR_CLK_CNTRL); - DUMP_REG(SOR_CAP); - DUMP_REG(SOR_PWR); - DUMP_REG(SOR_TEST); - DUMP_REG(SOR_PLL0); - DUMP_REG(SOR_PLL1); - DUMP_REG(SOR_PLL2); - DUMP_REG(SOR_PLL3); - DUMP_REG(SOR_CSTM); - DUMP_REG(SOR_LVDS); - DUMP_REG(SOR_CRCA); - DUMP_REG(SOR_CRCB); - DUMP_REG(SOR_BLANK); - DUMP_REG(SOR_SEQ_CTL); - DUMP_REG(SOR_LANE_SEQ_CTL); - DUMP_REG(SOR_SEQ_INST(0)); - DUMP_REG(SOR_SEQ_INST(1)); - DUMP_REG(SOR_SEQ_INST(2)); - DUMP_REG(SOR_SEQ_INST(3)); - DUMP_REG(SOR_SEQ_INST(4)); - DUMP_REG(SOR_SEQ_INST(5)); - DUMP_REG(SOR_SEQ_INST(6)); - DUMP_REG(SOR_SEQ_INST(7)); - DUMP_REG(SOR_SEQ_INST(8)); - DUMP_REG(SOR_SEQ_INST(9)); - DUMP_REG(SOR_SEQ_INST(10)); - DUMP_REG(SOR_SEQ_INST(11)); - DUMP_REG(SOR_SEQ_INST(12)); - DUMP_REG(SOR_SEQ_INST(13)); - DUMP_REG(SOR_SEQ_INST(14)); - DUMP_REG(SOR_SEQ_INST(15)); - DUMP_REG(SOR_PWM_DIV); - DUMP_REG(SOR_PWM_CTL); - DUMP_REG(SOR_VCRC_A0); - DUMP_REG(SOR_VCRC_A1); - DUMP_REG(SOR_VCRC_B0); - DUMP_REG(SOR_VCRC_B1); - DUMP_REG(SOR_CCRC_A0); - DUMP_REG(SOR_CCRC_A1); - DUMP_REG(SOR_CCRC_B0); - DUMP_REG(SOR_CCRC_B1); - DUMP_REG(SOR_EDATA_A0); - DUMP_REG(SOR_EDATA_A1); - DUMP_REG(SOR_EDATA_B0); - DUMP_REG(SOR_EDATA_B1); - DUMP_REG(SOR_COUNT_A0); - DUMP_REG(SOR_COUNT_A1); - DUMP_REG(SOR_COUNT_B0); - DUMP_REG(SOR_COUNT_B1); - DUMP_REG(SOR_DEBUG_A0); - DUMP_REG(SOR_DEBUG_A1); - DUMP_REG(SOR_DEBUG_B0); - DUMP_REG(SOR_DEBUG_B1); - DUMP_REG(SOR_TRIG); - DUMP_REG(SOR_MSCHECK); - DUMP_REG(SOR_XBAR_CTRL); - DUMP_REG(SOR_XBAR_POL); - DUMP_REG(SOR_DP_LINKCTL0); - DUMP_REG(SOR_DP_LINKCTL1); - DUMP_REG(SOR_LANE_DRIVE_CURRENT0); - DUMP_REG(SOR_LANE_DRIVE_CURRENT1); - DUMP_REG(SOR_LANE4_DRIVE_CURRENT0); - DUMP_REG(SOR_LANE4_DRIVE_CURRENT1); - DUMP_REG(SOR_LANE_PREEMPHASIS0); - DUMP_REG(SOR_LANE_PREEMPHASIS1); - DUMP_REG(SOR_LANE4_PREEMPHASIS0); - DUMP_REG(SOR_LANE4_PREEMPHASIS1); - DUMP_REG(SOR_LANE_POSTCURSOR0); - DUMP_REG(SOR_LANE_POSTCURSOR1); - DUMP_REG(SOR_DP_CONFIG0); - DUMP_REG(SOR_DP_CONFIG1); - DUMP_REG(SOR_DP_MN0); - DUMP_REG(SOR_DP_MN1); - DUMP_REG(SOR_DP_PADCTL0); - DUMP_REG(SOR_DP_PADCTL1); - DUMP_REG(SOR_DP_DEBUG0); - DUMP_REG(SOR_DP_DEBUG1); - DUMP_REG(SOR_DP_SPARE0); - DUMP_REG(SOR_DP_SPARE1); - DUMP_REG(SOR_DP_AUDIO_CTRL); - DUMP_REG(SOR_DP_AUDIO_HBLANK_SYMBOLS); - DUMP_REG(SOR_DP_AUDIO_VBLANK_SYMBOLS); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_HEADER); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK0); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK1); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK2); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK3); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK4); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK5); - DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK6); - DUMP_REG(SOR_DP_TPG); - DUMP_REG(SOR_DP_TPG_CONFIG); - DUMP_REG(SOR_DP_LQ_CSTM0); - DUMP_REG(SOR_DP_LQ_CSTM1); - DUMP_REG(SOR_DP_LQ_CSTM2); - -#undef DUMP_REG + for (i = 0; i < ARRAY_SIZE(tegra_sor_regs); i++) { + unsigned int offset = tegra_sor_regs[i].offset; + + seq_printf(s, "%-38s %#05x %08x\n", tegra_sor_regs[i].name, + offset, tegra_sor_readl(sor, offset)); + } unlock: drm_modeset_unlock_all(drm); @@ -1251,57 +1378,46 @@ static const struct drm_info_list debugfs_files[] = { { "regs", tegra_sor_show_regs, 0, NULL }, }; -static int tegra_sor_debugfs_init(struct tegra_sor *sor, - struct drm_minor *minor) +static int tegra_sor_late_register(struct drm_connector *connector) { - const char *name = sor->soc->supports_dp ? "sor1" : "sor"; - unsigned int i; + struct tegra_output *output = connector_to_output(connector); + unsigned int i, count = ARRAY_SIZE(debugfs_files); + struct drm_minor *minor = connector->dev->primary; + struct dentry *root = connector->debugfs_entry; + struct tegra_sor *sor = to_sor(output); int err; - sor->debugfs = debugfs_create_dir(name, minor->debugfs_root); - if (!sor->debugfs) - return -ENOMEM; - sor->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files), GFP_KERNEL); - if (!sor->debugfs_files) { - err = -ENOMEM; - goto remove; - } + if (!sor->debugfs_files) + return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(debugfs_files); i++) + for (i = 0; i < count; i++) sor->debugfs_files[i].data = sor; - err = drm_debugfs_create_files(sor->debugfs_files, - ARRAY_SIZE(debugfs_files), - sor->debugfs, minor); + err = drm_debugfs_create_files(sor->debugfs_files, count, root, minor); if (err < 0) goto free; - sor->minor = minor; - return 0; free: kfree(sor->debugfs_files); sor->debugfs_files = NULL; -remove: - debugfs_remove_recursive(sor->debugfs); - sor->debugfs = NULL; + return err; } -static void tegra_sor_debugfs_exit(struct tegra_sor *sor) +static void tegra_sor_early_unregister(struct drm_connector *connector) { - drm_debugfs_remove_files(sor->debugfs_files, ARRAY_SIZE(debugfs_files), - sor->minor); - sor->minor = NULL; + struct tegra_output *output = connector_to_output(connector); + unsigned int count = ARRAY_SIZE(debugfs_files); + struct tegra_sor *sor = to_sor(output); + drm_debugfs_remove_files(sor->debugfs_files, count, + connector->dev->primary); kfree(sor->debugfs_files); sor->debugfs_files = NULL; - - debugfs_remove_recursive(sor->debugfs); - sor->debugfs = NULL; } static void tegra_sor_connector_reset(struct drm_connector *connector) @@ -1354,6 +1470,8 @@ static const struct drm_connector_funcs tegra_sor_connector_funcs = { .destroy = tegra_output_connector_destroy, .atomic_duplicate_state = tegra_sor_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = tegra_sor_late_register, + .early_unregister = tegra_sor_early_unregister, }; static int tegra_sor_connector_get_modes(struct drm_connector *connector) @@ -1377,10 +1495,6 @@ static enum drm_mode_status tegra_sor_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - /* HDMI 2.0 modes are not yet supported */ - if (mode->clock > 340000) - return MODE_NOCLOCK; - return MODE_OK; } @@ -1417,7 +1531,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); @@ -1433,9 +1547,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); @@ -1533,40 +1647,40 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; tegra_sor_writel(sor, value, SOR_CLK_CNTRL); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_BANDGAP_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); - value = tegra_sor_readl(sor, SOR_PLL3); + value = tegra_sor_readl(sor, sor->soc->regs->pll3); value |= SOR_PLL3_PLL_VDD_MODE_3V3; - tegra_sor_writel(sor, value, SOR_PLL3); + tegra_sor_writel(sor, value, sor->soc->regs->pll3); value = SOR_PLL0_ICHPMP(0xf) | SOR_PLL0_VCOCAP_RST | SOR_PLL0_PLLREG_LEVEL_V45 | SOR_PLL0_RESISTOR_EXT; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value |= SOR_PLL2_SEQ_PLLCAPPD; value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE; value |= SOR_PLL2_LVDS_ENABLE; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); value = SOR_PLL1_TERM_COMPOUT | SOR_PLL1_TMDS_TERM; - tegra_sor_writel(sor, value, SOR_PLL1); + tegra_sor_writel(sor, value, sor->soc->regs->pll1); while (true) { - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); if ((value & SOR_PLL2_SEQ_PLLCAPPD_ENFORCE) == 0) break; usleep_range(250, 1000); } - value = tegra_sor_readl(sor, SOR_PLL2); + 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_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); /* * power up @@ -1579,49 +1693,49 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_CLK_CNTRL); /* step 1 */ - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value |= SOR_PLL2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL2_PORT_POWERDOWN | SOR_PLL2_BANDGAP_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); - value = tegra_sor_readl(sor, SOR_PLL0); + value = tegra_sor_readl(sor, sor->soc->regs->pll0); value |= SOR_PLL0_VCOPD | SOR_PLL0_PWR; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + 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); /* step 3 */ - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_BANDGAP_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); /* step 4 */ - value = tegra_sor_readl(sor, SOR_PLL0); + value = tegra_sor_readl(sor, sor->soc->regs->pll0); value &= ~SOR_PLL0_VCOPD; value &= ~SOR_PLL0_PWR; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(200, 1000); /* step 5 */ - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_PORT_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); /* XXX not in TRM */ for (value = 0, i = 0; i < 5; i++) @@ -1637,7 +1751,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) dev_err(sor->dev, "failed to set parent clock: %d\n", err); /* power DP lanes */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); if (link.num_lanes <= 2) value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2); @@ -1654,7 +1768,7 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) else value |= SOR_DP_PADCTL_PD_TXD_0; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); value = tegra_sor_readl(sor, SOR_DP_LINKCTL0); value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; @@ -1698,9 +1812,9 @@ static void tegra_sor_edp_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_DP_TPG); /* enable pad calibration logic */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); err = drm_dp_link_probe(sor->aux, &link); if (err < 0) @@ -1773,7 +1887,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); @@ -1805,6 +1919,18 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder, info = &output->connector.display_info; + /* + * For HBR2 modes, the SOR brick needs to use the x20 multiplier, so + * the pixel clock must be corrected accordingly. + */ + if (pclk >= 340000000) { + state->link_speed = 20; + state->pclk = pclk / 2; + } else { + state->link_speed = 10; + state->pclk = pclk; + } + err = tegra_dc_state_setup_clock(dc, crtc_state, sor->clk_parent, pclk, 0); if (err < 0) { @@ -1955,6 +2081,81 @@ tegra_sor_hdmi_find_settings(struct tegra_sor *sor, unsigned long frequency) return NULL; } +static void tegra_sor_hdmi_disable_scrambling(struct tegra_sor *sor) +{ + u32 value; + + value = tegra_sor_readl(sor, SOR_HDMI2_CTRL); + value &= ~SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4; + value &= ~SOR_HDMI2_CTRL_SCRAMBLE; + tegra_sor_writel(sor, value, SOR_HDMI2_CTRL); +} + +static void tegra_sor_hdmi_scdc_disable(struct tegra_sor *sor) +{ + struct i2c_adapter *ddc = sor->output.ddc; + + drm_scdc_set_high_tmds_clock_ratio(ddc, false); + drm_scdc_set_scrambling(ddc, false); + + tegra_sor_hdmi_disable_scrambling(sor); +} + +static void tegra_sor_hdmi_scdc_stop(struct tegra_sor *sor) +{ + if (sor->scdc_enabled) { + cancel_delayed_work_sync(&sor->scdc); + tegra_sor_hdmi_scdc_disable(sor); + } +} + +static void tegra_sor_hdmi_enable_scrambling(struct tegra_sor *sor) +{ + u32 value; + + value = tegra_sor_readl(sor, SOR_HDMI2_CTRL); + value |= SOR_HDMI2_CTRL_CLOCK_MODE_DIV_BY_4; + value |= SOR_HDMI2_CTRL_SCRAMBLE; + tegra_sor_writel(sor, value, SOR_HDMI2_CTRL); +} + +static void tegra_sor_hdmi_scdc_enable(struct tegra_sor *sor) +{ + struct i2c_adapter *ddc = sor->output.ddc; + + drm_scdc_set_high_tmds_clock_ratio(ddc, true); + drm_scdc_set_scrambling(ddc, true); + + tegra_sor_hdmi_enable_scrambling(sor); +} + +static void tegra_sor_hdmi_scdc_work(struct work_struct *work) +{ + struct tegra_sor *sor = container_of(work, struct tegra_sor, scdc.work); + struct i2c_adapter *ddc = sor->output.ddc; + + if (!drm_scdc_get_scrambling_status(ddc)) { + DRM_DEBUG_KMS("SCDC not scrambled\n"); + tegra_sor_hdmi_scdc_enable(sor); + } + + schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000)); +} + +static void tegra_sor_hdmi_scdc_start(struct tegra_sor *sor) +{ + struct drm_scdc *scdc = &sor->output.connector.display_info.hdmi.scdc; + struct drm_display_mode *mode; + + mode = &sor->output.encoder.crtc->state->adjusted_mode; + + if (mode->clock >= 340000 && scdc->supported) { + schedule_delayed_work(&sor->scdc, msecs_to_jiffies(5000)); + tegra_sor_hdmi_scdc_enable(sor); + sor->scdc_enabled = true; + } +} + static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) { struct tegra_output *output = encoder_to_output(encoder); @@ -1963,6 +2164,8 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder) u32 value; int err; + tegra_sor_hdmi_scdc_stop(sor); + err = tegra_sor_detach(sor); if (err < 0) dev_err(sor->dev, "failed to detach SOR: %d\n", err); @@ -1972,8 +2175,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); @@ -1982,9 +2189,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); } @@ -1998,12 +2205,14 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) struct tegra_sor *sor = to_sor(output); struct tegra_sor_state *state; struct drm_display_mode *mode; + unsigned long rate, pclk; unsigned int div, i; u32 value; int err; state = to_sor_state(output->connector.state); mode = &encoder->crtc->state->adjusted_mode; + pclk = mode->clock * 1000; pm_runtime_get_sync(sor->dev); @@ -2016,44 +2225,44 @@ 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); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_BANDGAP_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); - value = tegra_sor_readl(sor, SOR_PLL3); + value = tegra_sor_readl(sor, sor->soc->regs->pll3); value &= ~SOR_PLL3_PLL_VDD_MODE_3V3; - tegra_sor_writel(sor, value, SOR_PLL3); + tegra_sor_writel(sor, value, sor->soc->regs->pll3); - value = tegra_sor_readl(sor, SOR_PLL0); + value = tegra_sor_readl(sor, sor->soc->regs->pll0); value &= ~SOR_PLL0_VCOPD; value &= ~SOR_PLL0_PWR; - tegra_sor_writel(sor, value, SOR_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - value = tegra_sor_readl(sor, SOR_PLL2); + value = tegra_sor_readl(sor, sor->soc->regs->pll2); value &= ~SOR_PLL2_SEQ_PLLCAPPD_ENFORCE; - tegra_sor_writel(sor, value, SOR_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(200, 400); - value = tegra_sor_readl(sor, SOR_PLL2); + 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_PLL2); + tegra_sor_writel(sor, value, sor->soc->regs->pll2); usleep_range(20, 100); - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); while (true) { value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); @@ -2079,18 +2288,30 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; - if (mode->clock < 340000) + if (mode->clock < 340000) { + DRM_DEBUG_KMS("setting 2.7 GHz link speed\n"); value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70; - else + } else { + DRM_DEBUG_KMS("setting 5.4 GHz link speed\n"); value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G5_40; + } 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) | @@ -2102,9 +2323,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++) @@ -2127,13 +2350,25 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) return; } - value = SOR_INPUT_CONTROL_HDMI_SRC_SELECT(dc->pipe); + /* adjust clock rate for HDMI 2.0 modes */ + rate = clk_get_rate(sor->clk_parent); - /* XXX is this the proper check? */ - if (mode->clock < 75000) - value |= SOR_INPUT_CONTROL_ARM_VIDEO_RANGE_LIMITED; + if (mode->clock >= 340000) + rate /= 2; - tegra_sor_writel(sor, value, SOR_INPUT_CONTROL); + DRM_DEBUG_KMS("setting clock to %lu Hz, mode: %lu Hz\n", rate, pclk); + + clk_set_rate(sor->clk, rate); + + 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); + } max_ac = ((mode->htotal - mode->hdisplay) - SOR_REKEY - 18) / 32; @@ -2141,20 +2376,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); @@ -2171,9 +2409,9 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) tegra_sor_writel(sor, value, SOR_STATE1); /* power up pad calibration */ - value = tegra_sor_readl(sor, SOR_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value &= ~SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0); /* production settings */ settings = tegra_sor_hdmi_find_settings(sor, mode->clock * 1000); @@ -2183,51 +2421,68 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder) return; } - value = tegra_sor_readl(sor, SOR_PLL0); + 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_PLL0); + tegra_sor_writel(sor, value, sor->soc->regs->pll0); - tegra_sor_dp_term_calibrate(sor); - - value = tegra_sor_readl(sor, SOR_PLL1); + /* 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); - tegra_sor_writel(sor, value, SOR_PLL1); + 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_PLL3); + 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); - tegra_sor_writel(sor, value, SOR_PLL3); - - value = settings->drive_current[0] << 24 | - settings->drive_current[1] << 16 | - settings->drive_current[2] << 8 | - settings->drive_current[3] << 0; + 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[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_DP_PADCTL0); + 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); - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + 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_DP_PADCTL0); + value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0); value |= SOR_DP_PADCTL_PAD_CAL_PD; - tegra_sor_writel(sor, value, SOR_DP_PADCTL0); + 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; @@ -2242,6 +2497,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; @@ -2250,40 +2513,65 @@ 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); /* configure dynamic range of output */ - value = tegra_sor_readl(sor, SOR_HEAD_STATE0(dc->pipe)); + value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe); value &= ~SOR_HEAD_STATE_RANGECOMPRESS_MASK; value &= ~SOR_HEAD_STATE_DYNRANGE_MASK; - tegra_sor_writel(sor, value, SOR_HEAD_STATE0(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe); /* configure colorspace */ - value = tegra_sor_readl(sor, SOR_HEAD_STATE0(dc->pipe)); + value = tegra_sor_readl(sor, sor->soc->regs->head_state0 + dc->pipe); value &= ~SOR_HEAD_STATE_COLORSPACE_MASK; value |= SOR_HEAD_STATE_COLORSPACE_RGB; - tegra_sor_writel(sor, value, SOR_HEAD_STATE0(dc->pipe)); + tegra_sor_writel(sor, value, sor->soc->regs->head_state0 + dc->pipe); tegra_sor_mode_set(sor, mode, state); 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); if (err < 0) dev_err(sor->dev, "failed to wakeup SOR: %d\n", err); + + tegra_sor_hdmi_scdc_start(sor); } static const struct drm_encoder_helper_funcs tegra_sor_hdmi_helpers = { @@ -2344,13 +2632,7 @@ static int tegra_sor_init(struct host1x_client *client) return err; } - sor->output.encoder.possible_crtcs = 0x3; - - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_sor_debugfs_init(sor, drm->primary); - if (err < 0) - dev_err(sor->dev, "debugfs setup failed: %d\n", err); - } + tegra_output_find_possible_crtcs(&sor->output, drm); if (sor->aux) { err = drm_dp_aux_attach(sor->aux, &sor->output); @@ -2420,9 +2702,6 @@ static int tegra_sor_exit(struct host1x_client *client) clk_disable_unprepare(sor->clk_dp); clk_disable_unprepare(sor->clk); - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_sor_debugfs_exit(sor); - return 0; } @@ -2480,6 +2759,8 @@ static int tegra_sor_hdmi_probe(struct tegra_sor *sor) return err; } + INIT_DELAYED_WORK(&sor->scdc, tegra_sor_hdmi_scdc_work); + return 0; } @@ -2502,19 +2783,53 @@ static const u8 tegra124_sor_xbar_cfg[5] = { 0, 1, 2, 3, 4 }; +static const struct tegra_sor_regs tegra124_sor_regs = { + .head_state0 = 0x05, + .head_state1 = 0x07, + .head_state2 = 0x09, + .head_state3 = 0x0b, + .head_state4 = 0x0d, + .head_state5 = 0x0f, + .pll0 = 0x17, + .pll1 = 0x18, + .pll2 = 0x19, + .pll3 = 0x1a, + .dp_padctl0 = 0x5c, + .dp_padctl2 = 0x73, +}; + static const struct tegra_sor_soc tegra124_sor = { .supports_edp = true, .supports_lvds = true, .supports_hdmi = false, .supports_dp = false, + .regs = &tegra124_sor_regs, + .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg, }; +static const struct tegra_sor_regs tegra210_sor_regs = { + .head_state0 = 0x05, + .head_state1 = 0x07, + .head_state2 = 0x09, + .head_state3 = 0x0b, + .head_state4 = 0x0d, + .head_state5 = 0x0f, + .pll0 = 0x17, + .pll1 = 0x18, + .pll2 = 0x19, + .pll3 = 0x1a, + .dp_padctl0 = 0x5c, + .dp_padctl2 = 0x73, +}; + static const struct tegra_sor_soc tegra210_sor = { .supports_edp = true, .supports_lvds = false, .supports_hdmi = false, .supports_dp = false, + .regs = &tegra210_sor_regs, + .has_nvdisplay = false, .xbar_cfg = tegra124_sor_xbar_cfg, }; @@ -2528,13 +2843,60 @@ static const struct tegra_sor_soc tegra210_sor1 = { .supports_hdmi = true, .supports_dp = true, + .regs = &tegra210_sor_regs, + .has_nvdisplay = false, + .num_settings = ARRAY_SIZE(tegra210_sor_hdmi_defaults), .settings = tegra210_sor_hdmi_defaults, .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 }, @@ -2542,6 +2904,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; @@ -2577,6 +2962,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; @@ -2587,6 +2973,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; @@ -2596,6 +2983,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); |