static void rcar_lvds_enable(struct drm_bridge *bridge) { struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); const struct drm_display_mode *mode = &lvds->display_mode; /* * FIXME: We should really retrieve the CRTC through the state, but how * do we get a state pointer? */ struct drm_crtc *crtc = lvds->bridge.encoder->crtc; u32 lvdpllcr; u32 lvdhcr; u32 lvdcr0; int ret; WARN_ON(lvds->enabled); ret = clk_prepare_enable(lvds->clock); if (ret < 0) return; /* * Hardcode the channels and control signals routing for now. * * HSYNC -> CTRL0 * VSYNC -> CTRL1 * DISP -> CTRL2 * 0 -> CTRL3 */ rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO | LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC | LVDCTRCR_CTR0SEL_HSYNC); if (lvds->info->quirks & RCAR_LVDS_QUIRK_LANES) lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3) | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1); else lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1) | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3); rcar_lvds_write(lvds, LVDCHCR, lvdhcr); /* PLL clock configuration. */ if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN2_PLLCR) lvdpllcr = rcar_lvds_lvdpllcr_gen2(mode->clock); else lvdpllcr = rcar_lvds_lvdpllcr_gen3(mode->clock); rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); /* Set the LVDS mode and select the input. */ lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT; if (drm_crtc_index(crtc) == 2) lvdcr0 |= LVDCR0_DUSEL; rcar_lvds_write(lvds, LVDCR0, lvdcr0); /* Turn all the channels on. */ rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) | LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY); if (lvds->info->gen < 3) { /* Enable LVDS operation and turn the bias circuitry on. */ lvdcr0 |= LVDCR0_BEN | LVDCR0_LVEN; rcar_lvds_write(lvds, LVDCR0, lvdcr0); } /* Turn the PLL on. */ lvdcr0 |= LVDCR0_PLLON; rcar_lvds_write(lvds, LVDCR0, lvdcr0); if (lvds->info->gen > 2) { /* Set LVDS normal mode. */ lvdcr0 |= LVDCR0_PWD; rcar_lvds_write(lvds, LVDCR0, lvdcr0); } if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) { /* Turn on the LVDS PHY. */ lvdcr0 |= LVDCR0_LVEN; rcar_lvds_write(lvds, LVDCR0, lvdcr0); } /* Wait for the startup delay. */ usleep_range(100, 150); /* Turn the output on. */ lvdcr0 |= LVDCR0_LVRES; rcar_lvds_write(lvds, LVDCR0, lvdcr0); if (lvds->panel) { drm_panel_prepare(lvds->panel); drm_panel_enable(lvds->panel); } lvds->enabled = true; }
static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, struct rcar_du_crtc *rcrtc) { const struct drm_display_mode *mode = &rcrtc->crtc.mode; unsigned int freq = mode->clock; u32 lvdcr0; u32 lvdhcr; u32 pllcr; int ret; if (lvds->enabled) return 0; ret = clk_prepare_enable(lvds->clock); if (ret < 0) return ret; /* PLL clock configuration */ if (freq <= 38000) pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; else if (freq <= 60000) pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; else if (freq <= 121000) pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; else pllcr = LVDPLLCR_PLLDLYCNT_150M; rcar_lvds_write(lvds, LVDPLLCR, pllcr); /* Hardcode the channels and control signals routing for now. * * HSYNC -> CTRL0 * VSYNC -> CTRL1 * DISP -> CTRL2 * 0 -> CTRL3 */ rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO | LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC | LVDCTRCR_CTR0SEL_HSYNC); if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES)) lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3) | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1); else lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1) | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3); rcar_lvds_write(lvds, LVDCHCR, lvdhcr); /* Select the input, hardcode mode 0, enable LVDS operation and turn * bias circuitry on. */ lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN; if (rcrtc->index == 2) lvdcr0 |= LVDCR0_DUSEL; rcar_lvds_write(lvds, LVDCR0, lvdcr0); /* Turn all the channels on. */ rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) | LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY); /* Turn the PLL on, wait for the startup delay, and turn the output * on. */ lvdcr0 |= LVDCR0_PLLEN; rcar_lvds_write(lvds, LVDCR0, lvdcr0); usleep_range(100, 150); lvdcr0 |= LVDCR0_LVRES; rcar_lvds_write(lvds, LVDCR0, lvdcr0); lvds->enabled = true; return 0; }