diff options
Diffstat (limited to 'drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c')
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c | 142 |
1 files changed, 106 insertions, 36 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c index 85043c5..ef3a503 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c @@ -38,35 +38,106 @@ static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data) iowrite32(data, lvds->mmio + reg); } -static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, - struct rcar_du_crtc *rcrtc) +static void rcar_du_lvdsenc_start_gen2(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) + if (freq < 39000) pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; - else if (freq <= 60000) + else if (freq < 61000) pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; - else if (freq <= 121000) + else if (freq < 121000) pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; else pllcr = LVDPLLCR_PLLDLYCNT_150M; rcar_lvds_write(lvds, LVDPLLCR, pllcr); + /* 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_GEN2(3) | LVDCR1_CHSTBY_GEN2(2) | + LVDCR1_CHSTBY_GEN2(1) | LVDCR1_CHSTBY_GEN2(0) | + LVDCR1_CLKSTBY_GEN2); + + /* Turn the PLL on, wait for the startup delay, and turn the output + * on. + */ + lvdcr0 |= LVDCR0_PLLON; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + usleep_range(100, 150); + + lvdcr0 |= LVDCR0_LVRES; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); +} + +static void rcar_du_lvdsenc_start_gen3(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 pllcr; + + /* PLL clock configuration */ + if (freq < 42000) + pllcr = LVDPLLCR_PLLDIVCNT_42M; + else if (freq < 85000) + pllcr = LVDPLLCR_PLLDIVCNT_85M; + else if (freq < 128000) + pllcr = LVDPLLCR_PLLDIVCNT_128M; + else + pllcr = LVDPLLCR_PLLDIVCNT_148M; + + rcar_lvds_write(lvds, LVDPLLCR, pllcr); + + /* Turn the PLL on, set it to LVDS normal mode, wait for the startup + * delay and turn the output on. + */ + lvdcr0 = LVDCR0_PLLON; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + lvdcr0 |= LVDCR0_PWD; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + usleep_range(100, 150); + + lvdcr0 |= LVDCR0_LVRES; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + /* Turn all the channels on. */ + rcar_lvds_write(lvds, LVDCR1, + LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) | + LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) | + LVDCR1_CLKSTBY_GEN3); +} + +static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, + struct rcar_du_crtc *rcrtc) +{ + u32 lvdhcr; + int ret; + + if (lvds->enabled) + return 0; + + ret = clk_prepare_enable(lvds->clock); + if (ret < 0) + return ret; + /* Hardcode the channels and control signals routing for now. * * HSYNC -> CTRL0 @@ -87,30 +158,14 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, 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); + /* Perform generation-specific initialization. */ + if (lvds->dev->info->gen < 3) + rcar_du_lvdsenc_start_gen2(lvds, rcrtc); + else + rcar_du_lvdsenc_start_gen3(lvds, rcrtc); lvds->enabled = true; + return 0; } @@ -140,6 +195,21 @@ int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc, return -EINVAL; } +void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds, + struct drm_display_mode *mode) +{ + struct rcar_du_device *rcdu = lvds->dev; + + /* The internal LVDS encoder has a restricted clock frequency operating + * range (30MHz to 150MHz on Gen2, 25.175MHz to 148.5MHz on Gen3). Clamp + * the clock accordingly. + */ + if (rcdu->info->gen < 3) + mode->clock = clamp(mode->clock, 30000, 150000); + else + mode->clock = clamp(mode->clock, 25175, 148500); +} + static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds, struct platform_device *pdev) { |