/* * Implementing ->set_parent() here isn't really required because the parent * will be explicitly selected in the driver code via the DP_CLK_SEL mux in * the SOR_CLK_CNTRL register. This is primarily for compatibility with the * Tegra186 and later SoC generations where the BPMP implements this clock * and doesn't expose the mux via the common clock framework.
*/
/* Tegra only supports RBR, HBR and HBR2 */ for (i = 0; i < link->num_rates; i++) { switch (link->rates[i]) { case 1620000: case 2700000: case 5400000: break;
/* * Clear or set the PD_TXD bit corresponding to each lane, depending * on whether it is used or not.
*/
value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
if (lanes <= 2)
value &= ~(SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[3]) |
SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[2])); else
value |= SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[3]) |
SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[2]);
if (lanes <= 1)
value &= ~SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[1]); else
value |= SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[1]);
if (lanes == 0)
value &= ~SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[0]); else
value |= SOR_DP_PADCTL_PD_TXD(sor->soc->lane_map[0]);
/* power down all lanes */
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->soc->regs->dp_padctl0);
/* start lane sequencer */
value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
SOR_LANE_SEQ_CTL_POWER_STATE_DOWN;
tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) break;
usleep_range(25, 100);
}
if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) return -ETIMEDOUT;
/* pre-charge all used lanes */
value = tegra_sor_readl(sor, sor->soc->regs->dp_padctl0);
if (lanes <= 2)
value &= ~(SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[3]) |
SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[2])); else
value |= SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[3]) |
SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[2]);
if (lanes <= 1)
value &= ~SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[1]); else
value |= SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[1]);
if (lanes == 0)
value &= ~SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[0]); else
value |= SOR_DP_PADCTL_CM_TXD(sor->soc->lane_map[0]);
/* enable pad calibration logic */
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);
value = tegra_sor_readl(sor, sor->soc->regs->pll1);
value |= SOR_PLL1_TMDS_TERM;
tegra_sor_writel(sor, value, sor->soc->regs->pll1);
while (mask) {
adj |= mask;
value = tegra_sor_readl(sor, sor->soc->regs->pll1);
value &= ~SOR_PLL1_TMDS_TERMADJ_MASK;
value |= SOR_PLL1_TMDS_TERMADJ(adj);
tegra_sor_writel(sor, value, sor->soc->regs->pll1);
usleep_range(100, 200);
value = tegra_sor_readl(sor, sor->soc->regs->pll1); if (value & SOR_PLL1_TERM_COMPOUT)
adj &= ~mask;
mask >>= 1;
}
value = tegra_sor_readl(sor, sor->soc->regs->pll1);
value &= ~SOR_PLL1_TMDS_TERMADJ_MASK;
value |= SOR_PLL1_TMDS_TERMADJ(adj);
tegra_sor_writel(sor, value, sor->soc->regs->pll1);
/* disable pad calibration logic */
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);
}
for (value = 0, i = 0; i < link->lanes; i++) {
u8 vs = link->train.request.voltage_swing[i];
u8 pe = link->train.request.pre_emphasis[i];
u8 pc = link->train.request.post_cursor[i];
u8 shift = sor->soc->lane_map[i] << 3;
if (link->caps.tps3_supported)
tegra_sor_writel(sor, post_cursor, SOR_LANE_POSTCURSOR0);
tegra_sor_writel(sor, pattern, SOR_DP_TPG);
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(tx_pu);
tegra_sor_writel(sor, value, sor->soc->regs->dp_padctl0);
/* configure link speed and lane count */
value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate);
tegra_sor_writel(sor, value, SOR_CLK_CNTRL);
value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK;
value |= SOR_DP_LINKCTL_LANE_COUNT(lanes);
if (link->caps.enhanced_framing)
value |= SOR_DP_LINKCTL_ENHANCED_FRAME;
tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
usleep_range(400, 1000);
/* configure load pulse position adjustment */
value = tegra_sor_readl(sor, sor->soc->regs->pll1);
value &= ~SOR_PLL1_LOADADJ_MASK;
switch (rate) { case DP_LINK_BW_1_62:
value |= SOR_PLL1_LOADADJ(0x3); break;
case DP_LINK_BW_2_7:
value |= SOR_PLL1_LOADADJ(0x4); break;
case DP_LINK_BW_5_4:
value |= SOR_PLL1_LOADADJ(0x6); break;
}
/* wake up in normal mode */
value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE;
value |= SOR_SUPER_STATE_MODE_NORMAL;
tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
tegra_sor_super_update(sor);
/* attach */
value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
value |= SOR_SUPER_STATE_ATTACHED;
tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
tegra_sor_super_update(sor);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_TEST); if ((value & SOR_TEST_ATTACHED) != 0) return 0;
value = tegra_sor_readl(sor, SOR_PWR);
value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU;
tegra_sor_writel(sor, value, SOR_PWR);
timeout = jiffies + msecs_to_jiffies(timeout);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_PWR); if ((value & SOR_PWR_TRIGGER) == 0) return 0;
usleep_range(25, 100);
}
return -ETIMEDOUT;
}
struct tegra_sor_params { /* number of link clocks per line */ unsignedint num_clocks; /* ratio between input and output */
u64 ratio; /* precision factor */
u64 precision;
if (config->watermark > 30) {
config->watermark = 30;
dev_err(sor->dev, "unable to compute TU size, forcing watermark to %u\n",
config->watermark);
} elseif (config->watermark > num_syms_per_line) {
config->watermark = num_syms_per_line;
dev_err(sor->dev, "watermark too high, forcing to %u\n",
config->watermark);
}
/* compute the number of symbols per horizontal blanking interval */
num = ((mode->htotal - mode->hdisplay) - 7) * link_rate;
config->hblank_symbols = div_u64(num, pclk);
if (link->caps.enhanced_framing)
config->hblank_symbols -= 3;
config->hblank_symbols -= 12 / link->lanes;
/* compute the number of symbols per vertical blanking interval */
num = (mode->hdisplay - 25) * link_rate;
config->vblank_symbols = div_u64(num, pclk);
config->vblank_symbols -= 36 / link->lanes + 4;
value = tegra_sor_readl(sor, SOR_DP_LINKCTL0);
value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK;
value |= SOR_DP_LINKCTL_TU_SIZE(config->tu_size);
tegra_sor_writel(sor, value, SOR_DP_LINKCTL0);
value = tegra_sor_readl(sor, SOR_DP_CONFIG0);
value &= ~SOR_DP_CONFIG_WATERMARK_MASK;
value |= SOR_DP_CONFIG_WATERMARK(config->watermark);
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK;
value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config->active_count);
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK;
value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config->active_frac);
if (config->active_polarity)
value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; else
value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY;
value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE;
value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE;
tegra_sor_writel(sor, value, SOR_DP_CONFIG0);
value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS);
value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK;
value |= config->hblank_symbols & 0xffff;
tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS);
value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS);
value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK;
value |= config->vblank_symbols & 0xffff;
tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS);
}
value = tegra_sor_readl(sor, SOR_STATE1);
value &= ~SOR_STATE_ASY_PIXELDEPTH_MASK;
value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
value &= ~SOR_STATE_ASY_OWNER_MASK;
value |= SOR_STATE_ASY_CRC_MODE_COMPLETE |
SOR_STATE_ASY_OWNER(dc->pipe + 1);
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
value &= ~SOR_STATE_ASY_HSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
value |= SOR_STATE_ASY_HSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
value &= ~SOR_STATE_ASY_VSYNCPOL;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
value |= SOR_STATE_ASY_VSYNCPOL;
switch (state->bpc) { case 16:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_48_444; break;
case 12:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_36_444; break;
case 10:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_30_444; break;
case 8:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444; break;
case 6:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444; break;
default:
value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444; break;
}
tegra_sor_writel(sor, value, SOR_STATE1);
/* * TODO: The video timing programming below doesn't seem to match the * register definitions.
*/
/* switch to safe mode */
value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
value &= ~SOR_SUPER_STATE_MODE_NORMAL;
tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
tegra_sor_super_update(sor);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_PWR); if (value & SOR_PWR_MODE_SAFE) break;
}
if ((value & SOR_PWR_MODE_SAFE) == 0) return -ETIMEDOUT;
/* go to sleep */
value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK;
tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
tegra_sor_super_update(sor);
/* detach */
value = tegra_sor_readl(sor, SOR_SUPER_STATE1);
value &= ~SOR_SUPER_STATE_ATTACHED;
tegra_sor_writel(sor, value, SOR_SUPER_STATE1);
tegra_sor_super_update(sor);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_TEST); if ((value & SOR_TEST_ATTACHED) == 0) break;
usleep_range(25, 100);
}
if ((value & SOR_TEST_ATTACHED) != 0) return -ETIMEDOUT;
return 0;
}
staticint tegra_sor_power_down(struct tegra_sor *sor)
{ unsignedlong value, timeout; int err;
value = tegra_sor_readl(sor, SOR_PWR);
value &= ~SOR_PWR_NORMAL_STATE_PU;
value |= SOR_PWR_TRIGGER;
tegra_sor_writel(sor, value, SOR_PWR);
timeout = jiffies + msecs_to_jiffies(250);
while (time_before(jiffies, timeout)) {
value = tegra_sor_readl(sor, SOR_PWR); if ((value & SOR_PWR_TRIGGER) == 0) return 0;
usleep_range(25, 100);
}
if ((value & SOR_PWR_TRIGGER) != 0) return -ETIMEDOUT;
/* 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); return err;
}
value = tegra_sor_readl(sor, sor->soc->regs->pll2);
value |= SOR_PLL2_PORT_POWERDOWN;
tegra_sor_writel(sor, value, sor->soc->regs->pll2);
usleep_range(20, 100);
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;
value |= SOR_PLL2_SEQ_PLLCAPPD_ENFORCE;
tegra_sor_writel(sor, value, sor->soc->regs->pll2);
/* * 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;
}
/* disable AVI infoframe */
value = tegra_sor_readl(sor, SOR_HDMI_AVI_INFOFRAME_CTRL);
value &= ~INFOFRAME_CTRL_SINGLE;
value &= ~INFOFRAME_CTRL_OTHER;
value &= ~INFOFRAME_CTRL_ENABLE;
tegra_sor_writel(sor, value, SOR_HDMI_AVI_INFOFRAME_CTRL);
err = drm_hdmi_avi_infoframe_from_display_mode(&frame,
&sor->output.connector, mode); if (err < 0) {
dev_err(sor->dev, "failed to setup AVI infoframe: %d\n", err); return err;
}
err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); if (err < 0) {
dev_err(sor->dev, "failed to pack AVI infoframe: %d\n", err); return err;
}
tegra_sor_hdmi_write_infopack(sor, buffer, err);
/* enable AVI infoframe */
value = tegra_sor_readl(sor, SOR_HDMI_AVI_INFOFRAME_CTRL);
value |= INFOFRAME_CTRL_CHECKSUM_ENABLE;
value |= INFOFRAME_CTRL_ENABLE;
tegra_sor_writel(sor, value, SOR_HDMI_AVI_INFOFRAME_CTRL);
for (i = 0; i < length; i++)
tegra_sor_writel(sor, i << 8 | sor->output.connector.eld[i],
SOR_AUDIO_HDA_ELD_BUFWR);
/* * The HDA codec will always report an ELD buffer size of 96 bytes and * the HDA codec driver will check that each byte read from the buffer * is valid. Therefore every byte must be written, even if no 96 bytes * were parsed from EDID.
*/ for (i = length; i < 96; i++)
tegra_sor_writel(sor, i << 8 | 0, SOR_AUDIO_HDA_ELD_BUFWR);
}
/* * Enable and unmask the HDA codec SCRATCH0 register interrupt. This * is used for interoperability between the HDA codec driver and the * HDMI/DP driver.
*/
value = SOR_INT_CODEC_SCRATCH1 | SOR_INT_CODEC_SCRATCH0;
tegra_sor_writel(sor, value, SOR_INT_ENABLE);
tegra_sor_writel(sor, value, SOR_INT_MASK);
tegra_sor_write_eld(sor);
value = SOR_AUDIO_HDA_PRESENSE_ELDV | SOR_AUDIO_HDA_PRESENSE_PD;
tegra_sor_writel(sor, value, SOR_AUDIO_HDA_PRESENSE);
}
value = tegra_sor_readl(sor, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
value |= INFOFRAME_CTRL_CHECKSUM_ENABLE;
value |= INFOFRAME_CTRL_ENABLE;
tegra_sor_writel(sor, value, SOR_HDMI_AUDIO_INFOFRAME_CTRL);
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);
}
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);
}
err = host1x_client_resume(&sor->client); if (err < 0) {
dev_err(sor->dev, "failed to resume: %d\n", err); return;
}
/* 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); return;
}
div = clk_get_rate(sor->clk) / 1000000 * 4;
err = tegra_io_pad_power_enable(sor->pad); if (err < 0)
dev_err(sor->dev, "failed to power on I/O pad: %d\n", err);
usleep_range(20, 100);
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, 100);
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;
value &= ~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;
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);
usleep_range(20, 100);
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->soc->regs->dp_padctl0);
while (true) {
value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); if ((value & SOR_LANE_SEQ_CTL_STATE_BUSY) == 0) break;
while (true) {
value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) break;
usleep_range(250, 1000);
}
value = tegra_sor_readl(sor, SOR_CLK_CNTRL);
value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK;
value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK;
if (mode->clock < 340000) {
DRM_DEBUG_KMS("setting 2.7 GHz link speed\n");
value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70;
} 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_PANEL_INTERNAL;
value &= ~SOR_DP_SPARE_SEQ_ENABLE;
value &= ~SOR_DP_SPARE_MACRO_SOR_CLK;
tegra_sor_writel(sor, value, SOR_DP_SPARE0);
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++)
value |= SOR_XBAR_CTRL_LINK0_XSEL(i, sor->xbar_cfg[i]) |
SOR_XBAR_CTRL_LINK1_XSEL(i, i);
/* * 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;
}
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);
}
/* infoframe setup */
err = tegra_sor_hdmi_setup_avi_infoframe(sor, mode); if (err < 0)
dev_err(sor->dev, "failed to setup AVI infoframe: %d\n", err);
/* XXX HDMI audio support not implemented yet */
tegra_sor_hdmi_disable_audio_infoframe(sor);
/* use single TMDS protocol */
value = tegra_sor_readl(sor, SOR_STATE1);
value &= ~SOR_STATE_ASY_PROTOCOL_MASK;
value |= SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A;
tegra_sor_writel(sor, value, SOR_STATE1);
/* power up 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);
/* production settings */
settings = tegra_sor_hdmi_find_settings(sor, mode->clock * 1000); if (!settings) {
dev_err(sor->dev, "no settings for pixel clock %d Hz\n",
mode->clock * 1000); return;
}
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);
/* 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_AVDD10_LEVEL_MASK;
value &= ~SOR_PLL3_AVDD14_LEVEL_MASK;
value |= SOR_PLL3_BG_TEMP_COEF(settings->bg_temp_coef);
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.