Message ID | 1485444053-8331-2-git-send-email-shawnguo@kernel.org |
---|---|
State | New |
Headers | show |
Series | Add TV Encoder support for ZTE DRM driver | expand |
On Thu, Jan 26, 2017 at 11:20:49PM +0800, Shawn Guo wrote: > From: Shawn Guo <shawn.guo@linaro.org> > > It adds interlace mode support in VOU TIMING_CTRL and channel control > block, so that VOU driver gets ready to support output device in > interlace mode like TV Encoder. > Reviewed-by: Sean Paul <seanpaul@chromium.org> > Signed-off-by: Shawn Guo <shawn.guo@linaro.org> > --- > Changes for v2: > - Save the use of variable 'vactive' by checking interlaced case. > - Rename mask variable for scan register to avoid naming scope > confusion. > - Write a proper comment to explain how vback_porch of the first > and second field are related. > > drivers/gpu/drm/zte/zx_vou.c | 52 +++++++++++++++++++++++++++++++++++++-- > drivers/gpu/drm/zte/zx_vou_regs.h | 15 +++++++++++ > 2 files changed, 65 insertions(+), 2 deletions(-) > > diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c > index 3056b41df518..fe066e0f3df4 100644 > --- a/drivers/gpu/drm/zte/zx_vou.c > +++ b/drivers/gpu/drm/zte/zx_vou.c > @@ -40,6 +40,7 @@ struct zx_crtc_regs { > u32 fir_active; > u32 fir_htiming; > u32 fir_vtiming; > + u32 sec_vtiming; > u32 timing_shift; > u32 timing_pi_shift; > }; > @@ -48,6 +49,7 @@ struct zx_crtc_regs { > .fir_active = FIR_MAIN_ACTIVE, > .fir_htiming = FIR_MAIN_H_TIMING, > .fir_vtiming = FIR_MAIN_V_TIMING, > + .sec_vtiming = SEC_MAIN_V_TIMING, > .timing_shift = TIMING_MAIN_SHIFT, > .timing_pi_shift = TIMING_MAIN_PI_SHIFT, > }; > @@ -56,6 +58,7 @@ struct zx_crtc_regs { > .fir_active = FIR_AUX_ACTIVE, > .fir_htiming = FIR_AUX_H_TIMING, > .fir_vtiming = FIR_AUX_V_TIMING, > + .sec_vtiming = SEC_AUX_V_TIMING, > .timing_shift = TIMING_AUX_SHIFT, > .timing_pi_shift = TIMING_AUX_PI_SHIFT, > }; > @@ -65,6 +68,10 @@ struct zx_crtc_bits { > u32 polarity_shift; > u32 int_frame_mask; > u32 tc_enable; > + u32 sec_vactive_shift; > + u32 sec_vactive_mask; > + u32 interlace_select; > + u32 pi_enable; > }; > > static const struct zx_crtc_bits main_crtc_bits = { > @@ -72,6 +79,10 @@ struct zx_crtc_bits { > .polarity_shift = MAIN_POL_SHIFT, > .int_frame_mask = TIMING_INT_MAIN_FRAME, > .tc_enable = MAIN_TC_EN, > + .sec_vactive_shift = SEC_VACT_MAIN_SHIFT, > + .sec_vactive_mask = SEC_VACT_MAIN_MASK, > + .interlace_select = MAIN_INTERLACE_SEL, > + .pi_enable = MAIN_PI_EN, > }; > > static const struct zx_crtc_bits aux_crtc_bits = { > @@ -79,6 +90,10 @@ struct zx_crtc_bits { > .polarity_shift = AUX_POL_SHIFT, > .int_frame_mask = TIMING_INT_AUX_FRAME, > .tc_enable = AUX_TC_EN, > + .sec_vactive_shift = SEC_VACT_AUX_SHIFT, > + .sec_vactive_mask = SEC_VACT_AUX_MASK, > + .interlace_select = AUX_INTERLACE_SEL, > + .pi_enable = AUX_PI_EN, > }; > > struct zx_crtc { > @@ -196,11 +211,13 @@ static inline void vou_chn_set_update(struct zx_crtc *zcrtc) > static void zx_crtc_enable(struct drm_crtc *crtc) > { > struct drm_display_mode *mode = &crtc->state->adjusted_mode; > + bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; > struct zx_crtc *zcrtc = to_zx_crtc(crtc); > struct zx_vou_hw *vou = zcrtc->vou; > const struct zx_crtc_regs *regs = zcrtc->regs; > const struct zx_crtc_bits *bits = zcrtc->bits; > struct videomode vm; > + u32 scan_mask; > u32 pol = 0; > u32 val; > int ret; > @@ -208,7 +225,7 @@ static void zx_crtc_enable(struct drm_crtc *crtc) > drm_display_mode_to_videomode(mode, &vm); > > /* Set up timing parameters */ > - val = V_ACTIVE(vm.vactive - 1); > + val = V_ACTIVE((interlaced ? vm.vactive / 2 : vm.vactive) - 1); > val |= H_ACTIVE(vm.hactive - 1); > zx_writel(vou->timing + regs->fir_active, val); > > @@ -222,6 +239,25 @@ static void zx_crtc_enable(struct drm_crtc *crtc) > val |= FRONT_PORCH(vm.vfront_porch - 1); > zx_writel(vou->timing + regs->fir_vtiming, val); > > + if (interlaced) { > + u32 shift = bits->sec_vactive_shift; > + u32 mask = bits->sec_vactive_mask; > + > + val = zx_readl(vou->timing + SEC_V_ACTIVE); > + val &= ~mask; > + val |= ((vm.vactive / 2 - 1) << shift) & mask; > + zx_writel(vou->timing + SEC_V_ACTIVE, val); > + > + val = SYNC_WIDE(vm.vsync_len - 1); > + /* > + * The vback_porch for the second field needs to shift one on > + * the value for the first field. > + */ > + val |= BACK_PORCH(vm.vback_porch); > + val |= FRONT_PORCH(vm.vfront_porch - 1); > + zx_writel(vou->timing + regs->sec_vtiming, val); > + } > + > /* Set up polarities */ > if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW) > pol |= 1 << POL_VSYNC_SHIFT; > @@ -232,9 +268,17 @@ static void zx_crtc_enable(struct drm_crtc *crtc) > pol << bits->polarity_shift); > > /* Setup SHIFT register by following what ZTE BSP does */ > - zx_writel(vou->timing + regs->timing_shift, H_SHIFT_VAL); > + val = H_SHIFT_VAL; > + if (interlaced) > + val |= V_SHIFT_VAL << 16; > + zx_writel(vou->timing + regs->timing_shift, val); > zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL); > > + /* Progressive or interlace scan select */ > + scan_mask = bits->interlace_select | bits->pi_enable; > + zx_writel_mask(vou->timing + SCAN_CTRL, scan_mask, > + interlaced ? scan_mask : 0); > + > /* Enable TIMING_CTRL */ > zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable, > bits->tc_enable); > @@ -245,6 +289,10 @@ static void zx_crtc_enable(struct drm_crtc *crtc) > zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK, > vm.vactive << CHN_SCREEN_H_SHIFT); > > + /* Configure channel interlace buffer control */ > + zx_writel_mask(zcrtc->chnreg + CHN_INTERLACE_BUF_CTRL, CHN_INTERLACE_EN, > + interlaced ? CHN_INTERLACE_EN : 0); > + > /* Update channel */ > vou_chn_set_update(zcrtc); > > diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h > index 193c1ce01fe7..e6ed844e068a 100644 > --- a/drivers/gpu/drm/zte/zx_vou_regs.h > +++ b/drivers/gpu/drm/zte/zx_vou_regs.h > @@ -75,6 +75,8 @@ > #define CHN_SCREEN_H_SHIFT 5 > #define CHN_SCREEN_H_MASK (0x1fff << CHN_SCREEN_H_SHIFT) > #define CHN_UPDATE 0x08 > +#define CHN_INTERLACE_BUF_CTRL 0x24 > +#define CHN_INTERLACE_EN BIT(2) > > /* TIMING_CTRL registers */ > #define TIMING_TC_ENABLE 0x04 > @@ -117,6 +119,19 @@ > #define TIMING_MAIN_SHIFT 0x2c > #define TIMING_AUX_SHIFT 0x30 > #define H_SHIFT_VAL 0x0048 > +#define V_SHIFT_VAL 0x0001 > +#define SCAN_CTRL 0x34 > +#define AUX_PI_EN BIT(19) > +#define MAIN_PI_EN BIT(18) > +#define AUX_INTERLACE_SEL BIT(1) > +#define MAIN_INTERLACE_SEL BIT(0) > +#define SEC_V_ACTIVE 0x38 > +#define SEC_VACT_MAIN_SHIFT 0 > +#define SEC_VACT_MAIN_MASK (0xffff << SEC_VACT_MAIN_SHIFT) > +#define SEC_VACT_AUX_SHIFT 16 > +#define SEC_VACT_AUX_MASK (0xffff << SEC_VACT_AUX_SHIFT) > +#define SEC_MAIN_V_TIMING 0x3c > +#define SEC_AUX_V_TIMING 0x40 > #define TIMING_MAIN_PI_SHIFT 0x68 > #define TIMING_AUX_PI_SHIFT 0x6c > #define H_PI_SHIFT_VAL 0x000f > -- > 1.9.1 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel
diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c index 3056b41df518..fe066e0f3df4 100644 --- a/drivers/gpu/drm/zte/zx_vou.c +++ b/drivers/gpu/drm/zte/zx_vou.c @@ -40,6 +40,7 @@ struct zx_crtc_regs { u32 fir_active; u32 fir_htiming; u32 fir_vtiming; + u32 sec_vtiming; u32 timing_shift; u32 timing_pi_shift; }; @@ -48,6 +49,7 @@ struct zx_crtc_regs { .fir_active = FIR_MAIN_ACTIVE, .fir_htiming = FIR_MAIN_H_TIMING, .fir_vtiming = FIR_MAIN_V_TIMING, + .sec_vtiming = SEC_MAIN_V_TIMING, .timing_shift = TIMING_MAIN_SHIFT, .timing_pi_shift = TIMING_MAIN_PI_SHIFT, }; @@ -56,6 +58,7 @@ struct zx_crtc_regs { .fir_active = FIR_AUX_ACTIVE, .fir_htiming = FIR_AUX_H_TIMING, .fir_vtiming = FIR_AUX_V_TIMING, + .sec_vtiming = SEC_AUX_V_TIMING, .timing_shift = TIMING_AUX_SHIFT, .timing_pi_shift = TIMING_AUX_PI_SHIFT, }; @@ -65,6 +68,10 @@ struct zx_crtc_bits { u32 polarity_shift; u32 int_frame_mask; u32 tc_enable; + u32 sec_vactive_shift; + u32 sec_vactive_mask; + u32 interlace_select; + u32 pi_enable; }; static const struct zx_crtc_bits main_crtc_bits = { @@ -72,6 +79,10 @@ struct zx_crtc_bits { .polarity_shift = MAIN_POL_SHIFT, .int_frame_mask = TIMING_INT_MAIN_FRAME, .tc_enable = MAIN_TC_EN, + .sec_vactive_shift = SEC_VACT_MAIN_SHIFT, + .sec_vactive_mask = SEC_VACT_MAIN_MASK, + .interlace_select = MAIN_INTERLACE_SEL, + .pi_enable = MAIN_PI_EN, }; static const struct zx_crtc_bits aux_crtc_bits = { @@ -79,6 +90,10 @@ struct zx_crtc_bits { .polarity_shift = AUX_POL_SHIFT, .int_frame_mask = TIMING_INT_AUX_FRAME, .tc_enable = AUX_TC_EN, + .sec_vactive_shift = SEC_VACT_AUX_SHIFT, + .sec_vactive_mask = SEC_VACT_AUX_MASK, + .interlace_select = AUX_INTERLACE_SEL, + .pi_enable = AUX_PI_EN, }; struct zx_crtc { @@ -196,11 +211,13 @@ static inline void vou_chn_set_update(struct zx_crtc *zcrtc) static void zx_crtc_enable(struct drm_crtc *crtc) { struct drm_display_mode *mode = &crtc->state->adjusted_mode; + bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; struct zx_crtc *zcrtc = to_zx_crtc(crtc); struct zx_vou_hw *vou = zcrtc->vou; const struct zx_crtc_regs *regs = zcrtc->regs; const struct zx_crtc_bits *bits = zcrtc->bits; struct videomode vm; + u32 scan_mask; u32 pol = 0; u32 val; int ret; @@ -208,7 +225,7 @@ static void zx_crtc_enable(struct drm_crtc *crtc) drm_display_mode_to_videomode(mode, &vm); /* Set up timing parameters */ - val = V_ACTIVE(vm.vactive - 1); + val = V_ACTIVE((interlaced ? vm.vactive / 2 : vm.vactive) - 1); val |= H_ACTIVE(vm.hactive - 1); zx_writel(vou->timing + regs->fir_active, val); @@ -222,6 +239,25 @@ static void zx_crtc_enable(struct drm_crtc *crtc) val |= FRONT_PORCH(vm.vfront_porch - 1); zx_writel(vou->timing + regs->fir_vtiming, val); + if (interlaced) { + u32 shift = bits->sec_vactive_shift; + u32 mask = bits->sec_vactive_mask; + + val = zx_readl(vou->timing + SEC_V_ACTIVE); + val &= ~mask; + val |= ((vm.vactive / 2 - 1) << shift) & mask; + zx_writel(vou->timing + SEC_V_ACTIVE, val); + + val = SYNC_WIDE(vm.vsync_len - 1); + /* + * The vback_porch for the second field needs to shift one on + * the value for the first field. + */ + val |= BACK_PORCH(vm.vback_porch); + val |= FRONT_PORCH(vm.vfront_porch - 1); + zx_writel(vou->timing + regs->sec_vtiming, val); + } + /* Set up polarities */ if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW) pol |= 1 << POL_VSYNC_SHIFT; @@ -232,9 +268,17 @@ static void zx_crtc_enable(struct drm_crtc *crtc) pol << bits->polarity_shift); /* Setup SHIFT register by following what ZTE BSP does */ - zx_writel(vou->timing + regs->timing_shift, H_SHIFT_VAL); + val = H_SHIFT_VAL; + if (interlaced) + val |= V_SHIFT_VAL << 16; + zx_writel(vou->timing + regs->timing_shift, val); zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL); + /* Progressive or interlace scan select */ + scan_mask = bits->interlace_select | bits->pi_enable; + zx_writel_mask(vou->timing + SCAN_CTRL, scan_mask, + interlaced ? scan_mask : 0); + /* Enable TIMING_CTRL */ zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable, bits->tc_enable); @@ -245,6 +289,10 @@ static void zx_crtc_enable(struct drm_crtc *crtc) zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK, vm.vactive << CHN_SCREEN_H_SHIFT); + /* Configure channel interlace buffer control */ + zx_writel_mask(zcrtc->chnreg + CHN_INTERLACE_BUF_CTRL, CHN_INTERLACE_EN, + interlaced ? CHN_INTERLACE_EN : 0); + /* Update channel */ vou_chn_set_update(zcrtc); diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h index 193c1ce01fe7..e6ed844e068a 100644 --- a/drivers/gpu/drm/zte/zx_vou_regs.h +++ b/drivers/gpu/drm/zte/zx_vou_regs.h @@ -75,6 +75,8 @@ #define CHN_SCREEN_H_SHIFT 5 #define CHN_SCREEN_H_MASK (0x1fff << CHN_SCREEN_H_SHIFT) #define CHN_UPDATE 0x08 +#define CHN_INTERLACE_BUF_CTRL 0x24 +#define CHN_INTERLACE_EN BIT(2) /* TIMING_CTRL registers */ #define TIMING_TC_ENABLE 0x04 @@ -117,6 +119,19 @@ #define TIMING_MAIN_SHIFT 0x2c #define TIMING_AUX_SHIFT 0x30 #define H_SHIFT_VAL 0x0048 +#define V_SHIFT_VAL 0x0001 +#define SCAN_CTRL 0x34 +#define AUX_PI_EN BIT(19) +#define MAIN_PI_EN BIT(18) +#define AUX_INTERLACE_SEL BIT(1) +#define MAIN_INTERLACE_SEL BIT(0) +#define SEC_V_ACTIVE 0x38 +#define SEC_VACT_MAIN_SHIFT 0 +#define SEC_VACT_MAIN_MASK (0xffff << SEC_VACT_MAIN_SHIFT) +#define SEC_VACT_AUX_SHIFT 16 +#define SEC_VACT_AUX_MASK (0xffff << SEC_VACT_AUX_SHIFT) +#define SEC_MAIN_V_TIMING 0x3c +#define SEC_AUX_V_TIMING 0x40 #define TIMING_MAIN_PI_SHIFT 0x68 #define TIMING_AUX_PI_SHIFT 0x6c #define H_PI_SHIFT_VAL 0x000f