xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/zte/zx_vou.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright 2016 Linaro Ltd.
4*4882a593Smuzhiyun  * Copyright 2016 ZTE Corporation.
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/clk.h>
8*4882a593Smuzhiyun #include <linux/component.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/of_address.h>
11*4882a593Smuzhiyun #include <linux/platform_device.h>
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <video/videomode.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
16*4882a593Smuzhiyun #include <drm/drm_crtc.h>
17*4882a593Smuzhiyun #include <drm/drm_fb_cma_helper.h>
18*4882a593Smuzhiyun #include <drm/drm_fb_helper.h>
19*4882a593Smuzhiyun #include <drm/drm_gem_cma_helper.h>
20*4882a593Smuzhiyun #include <drm/drm_of.h>
21*4882a593Smuzhiyun #include <drm/drm_plane_helper.h>
22*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
23*4882a593Smuzhiyun #include <drm/drm_vblank.h>
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include "zx_common_regs.h"
26*4882a593Smuzhiyun #include "zx_drm_drv.h"
27*4882a593Smuzhiyun #include "zx_plane.h"
28*4882a593Smuzhiyun #include "zx_vou.h"
29*4882a593Smuzhiyun #include "zx_vou_regs.h"
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #define GL_NUM	2
32*4882a593Smuzhiyun #define VL_NUM	3
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun enum vou_chn_type {
35*4882a593Smuzhiyun 	VOU_CHN_MAIN,
36*4882a593Smuzhiyun 	VOU_CHN_AUX,
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun struct zx_crtc_regs {
40*4882a593Smuzhiyun 	u32 fir_active;
41*4882a593Smuzhiyun 	u32 fir_htiming;
42*4882a593Smuzhiyun 	u32 fir_vtiming;
43*4882a593Smuzhiyun 	u32 sec_vtiming;
44*4882a593Smuzhiyun 	u32 timing_shift;
45*4882a593Smuzhiyun 	u32 timing_pi_shift;
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun static const struct zx_crtc_regs main_crtc_regs = {
49*4882a593Smuzhiyun 	.fir_active = FIR_MAIN_ACTIVE,
50*4882a593Smuzhiyun 	.fir_htiming = FIR_MAIN_H_TIMING,
51*4882a593Smuzhiyun 	.fir_vtiming = FIR_MAIN_V_TIMING,
52*4882a593Smuzhiyun 	.sec_vtiming = SEC_MAIN_V_TIMING,
53*4882a593Smuzhiyun 	.timing_shift = TIMING_MAIN_SHIFT,
54*4882a593Smuzhiyun 	.timing_pi_shift = TIMING_MAIN_PI_SHIFT,
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun static const struct zx_crtc_regs aux_crtc_regs = {
58*4882a593Smuzhiyun 	.fir_active = FIR_AUX_ACTIVE,
59*4882a593Smuzhiyun 	.fir_htiming = FIR_AUX_H_TIMING,
60*4882a593Smuzhiyun 	.fir_vtiming = FIR_AUX_V_TIMING,
61*4882a593Smuzhiyun 	.sec_vtiming = SEC_AUX_V_TIMING,
62*4882a593Smuzhiyun 	.timing_shift = TIMING_AUX_SHIFT,
63*4882a593Smuzhiyun 	.timing_pi_shift = TIMING_AUX_PI_SHIFT,
64*4882a593Smuzhiyun };
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun struct zx_crtc_bits {
67*4882a593Smuzhiyun 	u32 polarity_mask;
68*4882a593Smuzhiyun 	u32 polarity_shift;
69*4882a593Smuzhiyun 	u32 int_frame_mask;
70*4882a593Smuzhiyun 	u32 tc_enable;
71*4882a593Smuzhiyun 	u32 sec_vactive_shift;
72*4882a593Smuzhiyun 	u32 sec_vactive_mask;
73*4882a593Smuzhiyun 	u32 interlace_select;
74*4882a593Smuzhiyun 	u32 pi_enable;
75*4882a593Smuzhiyun 	u32 div_vga_shift;
76*4882a593Smuzhiyun 	u32 div_pic_shift;
77*4882a593Smuzhiyun 	u32 div_tvenc_shift;
78*4882a593Smuzhiyun 	u32 div_hdmi_pnx_shift;
79*4882a593Smuzhiyun 	u32 div_hdmi_shift;
80*4882a593Smuzhiyun 	u32 div_inf_shift;
81*4882a593Smuzhiyun 	u32 div_layer_shift;
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun static const struct zx_crtc_bits main_crtc_bits = {
85*4882a593Smuzhiyun 	.polarity_mask = MAIN_POL_MASK,
86*4882a593Smuzhiyun 	.polarity_shift = MAIN_POL_SHIFT,
87*4882a593Smuzhiyun 	.int_frame_mask = TIMING_INT_MAIN_FRAME,
88*4882a593Smuzhiyun 	.tc_enable = MAIN_TC_EN,
89*4882a593Smuzhiyun 	.sec_vactive_shift = SEC_VACT_MAIN_SHIFT,
90*4882a593Smuzhiyun 	.sec_vactive_mask = SEC_VACT_MAIN_MASK,
91*4882a593Smuzhiyun 	.interlace_select = MAIN_INTERLACE_SEL,
92*4882a593Smuzhiyun 	.pi_enable = MAIN_PI_EN,
93*4882a593Smuzhiyun 	.div_vga_shift = VGA_MAIN_DIV_SHIFT,
94*4882a593Smuzhiyun 	.div_pic_shift = PIC_MAIN_DIV_SHIFT,
95*4882a593Smuzhiyun 	.div_tvenc_shift = TVENC_MAIN_DIV_SHIFT,
96*4882a593Smuzhiyun 	.div_hdmi_pnx_shift = HDMI_MAIN_PNX_DIV_SHIFT,
97*4882a593Smuzhiyun 	.div_hdmi_shift = HDMI_MAIN_DIV_SHIFT,
98*4882a593Smuzhiyun 	.div_inf_shift = INF_MAIN_DIV_SHIFT,
99*4882a593Smuzhiyun 	.div_layer_shift = LAYER_MAIN_DIV_SHIFT,
100*4882a593Smuzhiyun };
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun static const struct zx_crtc_bits aux_crtc_bits = {
103*4882a593Smuzhiyun 	.polarity_mask = AUX_POL_MASK,
104*4882a593Smuzhiyun 	.polarity_shift = AUX_POL_SHIFT,
105*4882a593Smuzhiyun 	.int_frame_mask = TIMING_INT_AUX_FRAME,
106*4882a593Smuzhiyun 	.tc_enable = AUX_TC_EN,
107*4882a593Smuzhiyun 	.sec_vactive_shift = SEC_VACT_AUX_SHIFT,
108*4882a593Smuzhiyun 	.sec_vactive_mask = SEC_VACT_AUX_MASK,
109*4882a593Smuzhiyun 	.interlace_select = AUX_INTERLACE_SEL,
110*4882a593Smuzhiyun 	.pi_enable = AUX_PI_EN,
111*4882a593Smuzhiyun 	.div_vga_shift = VGA_AUX_DIV_SHIFT,
112*4882a593Smuzhiyun 	.div_pic_shift = PIC_AUX_DIV_SHIFT,
113*4882a593Smuzhiyun 	.div_tvenc_shift = TVENC_AUX_DIV_SHIFT,
114*4882a593Smuzhiyun 	.div_hdmi_pnx_shift = HDMI_AUX_PNX_DIV_SHIFT,
115*4882a593Smuzhiyun 	.div_hdmi_shift = HDMI_AUX_DIV_SHIFT,
116*4882a593Smuzhiyun 	.div_inf_shift = INF_AUX_DIV_SHIFT,
117*4882a593Smuzhiyun 	.div_layer_shift = LAYER_AUX_DIV_SHIFT,
118*4882a593Smuzhiyun };
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun struct zx_crtc {
121*4882a593Smuzhiyun 	struct drm_crtc crtc;
122*4882a593Smuzhiyun 	struct drm_plane *primary;
123*4882a593Smuzhiyun 	struct zx_vou_hw *vou;
124*4882a593Smuzhiyun 	void __iomem *chnreg;
125*4882a593Smuzhiyun 	void __iomem *chncsc;
126*4882a593Smuzhiyun 	void __iomem *dither;
127*4882a593Smuzhiyun 	const struct zx_crtc_regs *regs;
128*4882a593Smuzhiyun 	const struct zx_crtc_bits *bits;
129*4882a593Smuzhiyun 	enum vou_chn_type chn_type;
130*4882a593Smuzhiyun 	struct clk *pixclk;
131*4882a593Smuzhiyun };
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun #define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc)
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun struct vou_layer_bits {
136*4882a593Smuzhiyun 	u32 enable;
137*4882a593Smuzhiyun 	u32 chnsel;
138*4882a593Smuzhiyun 	u32 clksel;
139*4882a593Smuzhiyun };
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun static const struct vou_layer_bits zx_gl_bits[GL_NUM] = {
142*4882a593Smuzhiyun 	{
143*4882a593Smuzhiyun 		.enable = OSD_CTRL0_GL0_EN,
144*4882a593Smuzhiyun 		.chnsel = OSD_CTRL0_GL0_SEL,
145*4882a593Smuzhiyun 		.clksel = VOU_CLK_GL0_SEL,
146*4882a593Smuzhiyun 	}, {
147*4882a593Smuzhiyun 		.enable = OSD_CTRL0_GL1_EN,
148*4882a593Smuzhiyun 		.chnsel = OSD_CTRL0_GL1_SEL,
149*4882a593Smuzhiyun 		.clksel = VOU_CLK_GL1_SEL,
150*4882a593Smuzhiyun 	},
151*4882a593Smuzhiyun };
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun static const struct vou_layer_bits zx_vl_bits[VL_NUM] = {
154*4882a593Smuzhiyun 	{
155*4882a593Smuzhiyun 		.enable = OSD_CTRL0_VL0_EN,
156*4882a593Smuzhiyun 		.chnsel = OSD_CTRL0_VL0_SEL,
157*4882a593Smuzhiyun 		.clksel = VOU_CLK_VL0_SEL,
158*4882a593Smuzhiyun 	}, {
159*4882a593Smuzhiyun 		.enable = OSD_CTRL0_VL1_EN,
160*4882a593Smuzhiyun 		.chnsel = OSD_CTRL0_VL1_SEL,
161*4882a593Smuzhiyun 		.clksel = VOU_CLK_VL1_SEL,
162*4882a593Smuzhiyun 	}, {
163*4882a593Smuzhiyun 		.enable = OSD_CTRL0_VL2_EN,
164*4882a593Smuzhiyun 		.chnsel = OSD_CTRL0_VL2_SEL,
165*4882a593Smuzhiyun 		.clksel = VOU_CLK_VL2_SEL,
166*4882a593Smuzhiyun 	},
167*4882a593Smuzhiyun };
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun struct zx_vou_hw {
170*4882a593Smuzhiyun 	struct device *dev;
171*4882a593Smuzhiyun 	void __iomem *osd;
172*4882a593Smuzhiyun 	void __iomem *timing;
173*4882a593Smuzhiyun 	void __iomem *vouctl;
174*4882a593Smuzhiyun 	void __iomem *otfppu;
175*4882a593Smuzhiyun 	void __iomem *dtrc;
176*4882a593Smuzhiyun 	struct clk *axi_clk;
177*4882a593Smuzhiyun 	struct clk *ppu_clk;
178*4882a593Smuzhiyun 	struct clk *main_clk;
179*4882a593Smuzhiyun 	struct clk *aux_clk;
180*4882a593Smuzhiyun 	struct zx_crtc *main_crtc;
181*4882a593Smuzhiyun 	struct zx_crtc *aux_crtc;
182*4882a593Smuzhiyun };
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun enum vou_inf_data_sel {
185*4882a593Smuzhiyun 	VOU_YUV444	= 0,
186*4882a593Smuzhiyun 	VOU_RGB_101010	= 1,
187*4882a593Smuzhiyun 	VOU_RGB_888	= 2,
188*4882a593Smuzhiyun 	VOU_RGB_666	= 3,
189*4882a593Smuzhiyun };
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun struct vou_inf {
192*4882a593Smuzhiyun 	enum vou_inf_id id;
193*4882a593Smuzhiyun 	enum vou_inf_data_sel data_sel;
194*4882a593Smuzhiyun 	u32 clocks_en_bits;
195*4882a593Smuzhiyun 	u32 clocks_sel_bits;
196*4882a593Smuzhiyun };
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun static struct vou_inf vou_infs[] = {
199*4882a593Smuzhiyun 	[VOU_HDMI] = {
200*4882a593Smuzhiyun 		.data_sel = VOU_YUV444,
201*4882a593Smuzhiyun 		.clocks_en_bits = BIT(24) | BIT(18) | BIT(6),
202*4882a593Smuzhiyun 		.clocks_sel_bits = BIT(13) | BIT(2),
203*4882a593Smuzhiyun 	},
204*4882a593Smuzhiyun 	[VOU_TV_ENC] = {
205*4882a593Smuzhiyun 		.data_sel = VOU_YUV444,
206*4882a593Smuzhiyun 		.clocks_en_bits = BIT(15),
207*4882a593Smuzhiyun 		.clocks_sel_bits = BIT(11) | BIT(0),
208*4882a593Smuzhiyun 	},
209*4882a593Smuzhiyun 	[VOU_VGA] = {
210*4882a593Smuzhiyun 		.data_sel = VOU_RGB_888,
211*4882a593Smuzhiyun 		.clocks_en_bits = BIT(1),
212*4882a593Smuzhiyun 		.clocks_sel_bits = BIT(10),
213*4882a593Smuzhiyun 	},
214*4882a593Smuzhiyun };
215*4882a593Smuzhiyun 
crtc_to_vou(struct drm_crtc * crtc)216*4882a593Smuzhiyun static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	return zcrtc->vou;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun 
vou_inf_hdmi_audio_sel(struct drm_crtc * crtc,enum vou_inf_hdmi_audio aud)223*4882a593Smuzhiyun void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc,
224*4882a593Smuzhiyun 			    enum vou_inf_hdmi_audio aud)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
227*4882a593Smuzhiyun 	struct zx_vou_hw *vou = zcrtc->vou;
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_INF_HDMI_CTRL, VOU_HDMI_AUD_MASK, aud);
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun 
vou_inf_enable(enum vou_inf_id id,struct drm_crtc * crtc)232*4882a593Smuzhiyun void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc)
233*4882a593Smuzhiyun {
234*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
235*4882a593Smuzhiyun 	struct zx_vou_hw *vou = zcrtc->vou;
236*4882a593Smuzhiyun 	struct vou_inf *inf = &vou_infs[id];
237*4882a593Smuzhiyun 	void __iomem *dither = zcrtc->dither;
238*4882a593Smuzhiyun 	void __iomem *csc = zcrtc->chncsc;
239*4882a593Smuzhiyun 	bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
240*4882a593Smuzhiyun 	u32 data_sel_shift = id << 1;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	if (inf->data_sel != VOU_YUV444) {
243*4882a593Smuzhiyun 		/* Enable channel CSC for RGB output */
244*4882a593Smuzhiyun 		zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
245*4882a593Smuzhiyun 			       CSC_BT709_IMAGE_YCBCR2RGB << CSC_COV_MODE_SHIFT);
246*4882a593Smuzhiyun 		zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE,
247*4882a593Smuzhiyun 			       CSC_WORK_ENABLE);
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 		/* Bypass Dither block for RGB output */
250*4882a593Smuzhiyun 		zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS,
251*4882a593Smuzhiyun 			       DITHER_BYSPASS);
252*4882a593Smuzhiyun 	} else {
253*4882a593Smuzhiyun 		zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, 0);
254*4882a593Smuzhiyun 		zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, 0);
255*4882a593Smuzhiyun 	}
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	/* Select data format */
258*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
259*4882a593Smuzhiyun 		       inf->data_sel << data_sel_shift);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	/* Select channel */
262*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << id,
263*4882a593Smuzhiyun 		       zcrtc->chn_type << id);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	/* Select interface clocks */
266*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_CLK_SEL, inf->clocks_sel_bits,
267*4882a593Smuzhiyun 		       is_main ? 0 : inf->clocks_sel_bits);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	/* Enable interface clocks */
270*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits,
271*4882a593Smuzhiyun 		       inf->clocks_en_bits);
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	/* Enable the device */
274*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 1 << id);
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun 
vou_inf_disable(enum vou_inf_id id,struct drm_crtc * crtc)277*4882a593Smuzhiyun void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun 	struct zx_vou_hw *vou = crtc_to_vou(crtc);
280*4882a593Smuzhiyun 	struct vou_inf *inf = &vou_infs[id];
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	/* Disable the device */
283*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 0);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	/* Disable interface clocks */
286*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, 0);
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun 
zx_vou_config_dividers(struct drm_crtc * crtc,struct vou_div_config * configs,int num)289*4882a593Smuzhiyun void zx_vou_config_dividers(struct drm_crtc *crtc,
290*4882a593Smuzhiyun 			    struct vou_div_config *configs, int num)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
293*4882a593Smuzhiyun 	struct zx_vou_hw *vou = zcrtc->vou;
294*4882a593Smuzhiyun 	const struct zx_crtc_bits *bits = zcrtc->bits;
295*4882a593Smuzhiyun 	int i;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	/* Clear update flag bit */
298*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE, 0);
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	for (i = 0; i < num; i++) {
301*4882a593Smuzhiyun 		struct vou_div_config *cfg = configs + i;
302*4882a593Smuzhiyun 		u32 reg, shift;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 		switch (cfg->id) {
305*4882a593Smuzhiyun 		case VOU_DIV_VGA:
306*4882a593Smuzhiyun 			reg = VOU_CLK_SEL;
307*4882a593Smuzhiyun 			shift = bits->div_vga_shift;
308*4882a593Smuzhiyun 			break;
309*4882a593Smuzhiyun 		case VOU_DIV_PIC:
310*4882a593Smuzhiyun 			reg = VOU_CLK_SEL;
311*4882a593Smuzhiyun 			shift = bits->div_pic_shift;
312*4882a593Smuzhiyun 			break;
313*4882a593Smuzhiyun 		case VOU_DIV_TVENC:
314*4882a593Smuzhiyun 			reg = VOU_DIV_PARA;
315*4882a593Smuzhiyun 			shift = bits->div_tvenc_shift;
316*4882a593Smuzhiyun 			break;
317*4882a593Smuzhiyun 		case VOU_DIV_HDMI_PNX:
318*4882a593Smuzhiyun 			reg = VOU_DIV_PARA;
319*4882a593Smuzhiyun 			shift = bits->div_hdmi_pnx_shift;
320*4882a593Smuzhiyun 			break;
321*4882a593Smuzhiyun 		case VOU_DIV_HDMI:
322*4882a593Smuzhiyun 			reg = VOU_DIV_PARA;
323*4882a593Smuzhiyun 			shift = bits->div_hdmi_shift;
324*4882a593Smuzhiyun 			break;
325*4882a593Smuzhiyun 		case VOU_DIV_INF:
326*4882a593Smuzhiyun 			reg = VOU_DIV_PARA;
327*4882a593Smuzhiyun 			shift = bits->div_inf_shift;
328*4882a593Smuzhiyun 			break;
329*4882a593Smuzhiyun 		case VOU_DIV_LAYER:
330*4882a593Smuzhiyun 			reg = VOU_DIV_PARA;
331*4882a593Smuzhiyun 			shift = bits->div_layer_shift;
332*4882a593Smuzhiyun 			break;
333*4882a593Smuzhiyun 		default:
334*4882a593Smuzhiyun 			continue;
335*4882a593Smuzhiyun 		}
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 		/* Each divider occupies 3 bits */
338*4882a593Smuzhiyun 		zx_writel_mask(vou->vouctl + reg, 0x7 << shift,
339*4882a593Smuzhiyun 			       cfg->val << shift);
340*4882a593Smuzhiyun 	}
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	/* Set update flag bit to get dividers effected */
343*4882a593Smuzhiyun 	zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE,
344*4882a593Smuzhiyun 		       DIV_PARA_UPDATE);
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun 
vou_chn_set_update(struct zx_crtc * zcrtc)347*4882a593Smuzhiyun static inline void vou_chn_set_update(struct zx_crtc *zcrtc)
348*4882a593Smuzhiyun {
349*4882a593Smuzhiyun 	zx_writel(zcrtc->chnreg + CHN_UPDATE, 1);
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
zx_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_crtc_state * old_state)352*4882a593Smuzhiyun static void zx_crtc_atomic_enable(struct drm_crtc *crtc,
353*4882a593Smuzhiyun 				  struct drm_crtc_state *old_state)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
356*4882a593Smuzhiyun 	bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
357*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
358*4882a593Smuzhiyun 	struct zx_vou_hw *vou = zcrtc->vou;
359*4882a593Smuzhiyun 	const struct zx_crtc_regs *regs = zcrtc->regs;
360*4882a593Smuzhiyun 	const struct zx_crtc_bits *bits = zcrtc->bits;
361*4882a593Smuzhiyun 	struct videomode vm;
362*4882a593Smuzhiyun 	u32 scan_mask;
363*4882a593Smuzhiyun 	u32 pol = 0;
364*4882a593Smuzhiyun 	u32 val;
365*4882a593Smuzhiyun 	int ret;
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	drm_display_mode_to_videomode(mode, &vm);
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	/* Set up timing parameters */
370*4882a593Smuzhiyun 	val = V_ACTIVE((interlaced ? vm.vactive / 2 : vm.vactive) - 1);
371*4882a593Smuzhiyun 	val |= H_ACTIVE(vm.hactive - 1);
372*4882a593Smuzhiyun 	zx_writel(vou->timing + regs->fir_active, val);
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	val = SYNC_WIDE(vm.hsync_len - 1);
375*4882a593Smuzhiyun 	val |= BACK_PORCH(vm.hback_porch - 1);
376*4882a593Smuzhiyun 	val |= FRONT_PORCH(vm.hfront_porch - 1);
377*4882a593Smuzhiyun 	zx_writel(vou->timing + regs->fir_htiming, val);
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	val = SYNC_WIDE(vm.vsync_len - 1);
380*4882a593Smuzhiyun 	val |= BACK_PORCH(vm.vback_porch - 1);
381*4882a593Smuzhiyun 	val |= FRONT_PORCH(vm.vfront_porch - 1);
382*4882a593Smuzhiyun 	zx_writel(vou->timing + regs->fir_vtiming, val);
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	if (interlaced) {
385*4882a593Smuzhiyun 		u32 shift = bits->sec_vactive_shift;
386*4882a593Smuzhiyun 		u32 mask = bits->sec_vactive_mask;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 		val = zx_readl(vou->timing + SEC_V_ACTIVE);
389*4882a593Smuzhiyun 		val &= ~mask;
390*4882a593Smuzhiyun 		val |= ((vm.vactive / 2 - 1) << shift) & mask;
391*4882a593Smuzhiyun 		zx_writel(vou->timing + SEC_V_ACTIVE, val);
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 		val = SYNC_WIDE(vm.vsync_len - 1);
394*4882a593Smuzhiyun 		/*
395*4882a593Smuzhiyun 		 * The vback_porch for the second field needs to shift one on
396*4882a593Smuzhiyun 		 * the value for the first field.
397*4882a593Smuzhiyun 		 */
398*4882a593Smuzhiyun 		val |= BACK_PORCH(vm.vback_porch);
399*4882a593Smuzhiyun 		val |= FRONT_PORCH(vm.vfront_porch - 1);
400*4882a593Smuzhiyun 		zx_writel(vou->timing + regs->sec_vtiming, val);
401*4882a593Smuzhiyun 	}
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	/* Set up polarities */
404*4882a593Smuzhiyun 	if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW)
405*4882a593Smuzhiyun 		pol |= 1 << POL_VSYNC_SHIFT;
406*4882a593Smuzhiyun 	if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW)
407*4882a593Smuzhiyun 		pol |= 1 << POL_HSYNC_SHIFT;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	zx_writel_mask(vou->timing + TIMING_CTRL, bits->polarity_mask,
410*4882a593Smuzhiyun 		       pol << bits->polarity_shift);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	/* Setup SHIFT register by following what ZTE BSP does */
413*4882a593Smuzhiyun 	val = H_SHIFT_VAL;
414*4882a593Smuzhiyun 	if (interlaced)
415*4882a593Smuzhiyun 		val |= V_SHIFT_VAL << 16;
416*4882a593Smuzhiyun 	zx_writel(vou->timing + regs->timing_shift, val);
417*4882a593Smuzhiyun 	zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL);
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	/* Progressive or interlace scan select */
420*4882a593Smuzhiyun 	scan_mask = bits->interlace_select | bits->pi_enable;
421*4882a593Smuzhiyun 	zx_writel_mask(vou->timing + SCAN_CTRL, scan_mask,
422*4882a593Smuzhiyun 		       interlaced ? scan_mask : 0);
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	/* Enable TIMING_CTRL */
425*4882a593Smuzhiyun 	zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable,
426*4882a593Smuzhiyun 		       bits->tc_enable);
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	/* Configure channel screen size */
429*4882a593Smuzhiyun 	zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_W_MASK,
430*4882a593Smuzhiyun 		       vm.hactive << CHN_SCREEN_W_SHIFT);
431*4882a593Smuzhiyun 	zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK,
432*4882a593Smuzhiyun 		       vm.vactive << CHN_SCREEN_H_SHIFT);
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	/* Configure channel interlace buffer control */
435*4882a593Smuzhiyun 	zx_writel_mask(zcrtc->chnreg + CHN_INTERLACE_BUF_CTRL, CHN_INTERLACE_EN,
436*4882a593Smuzhiyun 		       interlaced ? CHN_INTERLACE_EN : 0);
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	/* Update channel */
439*4882a593Smuzhiyun 	vou_chn_set_update(zcrtc);
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	/* Enable channel */
442*4882a593Smuzhiyun 	zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, CHN_ENABLE);
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	drm_crtc_vblank_on(crtc);
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	ret = clk_set_rate(zcrtc->pixclk, mode->clock * 1000);
447*4882a593Smuzhiyun 	if (ret) {
448*4882a593Smuzhiyun 		DRM_DEV_ERROR(vou->dev, "failed to set pixclk rate: %d\n", ret);
449*4882a593Smuzhiyun 		return;
450*4882a593Smuzhiyun 	}
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	ret = clk_prepare_enable(zcrtc->pixclk);
453*4882a593Smuzhiyun 	if (ret)
454*4882a593Smuzhiyun 		DRM_DEV_ERROR(vou->dev, "failed to enable pixclk: %d\n", ret);
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun 
zx_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_crtc_state * old_state)457*4882a593Smuzhiyun static void zx_crtc_atomic_disable(struct drm_crtc *crtc,
458*4882a593Smuzhiyun 				   struct drm_crtc_state *old_state)
459*4882a593Smuzhiyun {
460*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
461*4882a593Smuzhiyun 	const struct zx_crtc_bits *bits = zcrtc->bits;
462*4882a593Smuzhiyun 	struct zx_vou_hw *vou = zcrtc->vou;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	clk_disable_unprepare(zcrtc->pixclk);
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	drm_crtc_vblank_off(crtc);
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	/* Disable channel */
469*4882a593Smuzhiyun 	zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, 0);
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	/* Disable TIMING_CTRL */
472*4882a593Smuzhiyun 	zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable, 0);
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun 
zx_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_crtc_state * old_state)475*4882a593Smuzhiyun static void zx_crtc_atomic_flush(struct drm_crtc *crtc,
476*4882a593Smuzhiyun 				  struct drm_crtc_state *old_state)
477*4882a593Smuzhiyun {
478*4882a593Smuzhiyun 	struct drm_pending_vblank_event *event = crtc->state->event;
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	if (!event)
481*4882a593Smuzhiyun 		return;
482*4882a593Smuzhiyun 
483*4882a593Smuzhiyun 	crtc->state->event = NULL;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	spin_lock_irq(&crtc->dev->event_lock);
486*4882a593Smuzhiyun 	if (drm_crtc_vblank_get(crtc) == 0)
487*4882a593Smuzhiyun 		drm_crtc_arm_vblank_event(crtc, event);
488*4882a593Smuzhiyun 	else
489*4882a593Smuzhiyun 		drm_crtc_send_vblank_event(crtc, event);
490*4882a593Smuzhiyun 	spin_unlock_irq(&crtc->dev->event_lock);
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun static const struct drm_crtc_helper_funcs zx_crtc_helper_funcs = {
494*4882a593Smuzhiyun 	.atomic_flush = zx_crtc_atomic_flush,
495*4882a593Smuzhiyun 	.atomic_enable = zx_crtc_atomic_enable,
496*4882a593Smuzhiyun 	.atomic_disable = zx_crtc_atomic_disable,
497*4882a593Smuzhiyun };
498*4882a593Smuzhiyun 
zx_vou_enable_vblank(struct drm_crtc * crtc)499*4882a593Smuzhiyun static int zx_vou_enable_vblank(struct drm_crtc *crtc)
500*4882a593Smuzhiyun {
501*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
502*4882a593Smuzhiyun 	struct zx_vou_hw *vou = crtc_to_vou(crtc);
503*4882a593Smuzhiyun 	u32 int_frame_mask = zcrtc->bits->int_frame_mask;
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 	zx_writel_mask(vou->timing + TIMING_INT_CTRL, int_frame_mask,
506*4882a593Smuzhiyun 		       int_frame_mask);
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	return 0;
509*4882a593Smuzhiyun }
510*4882a593Smuzhiyun 
zx_vou_disable_vblank(struct drm_crtc * crtc)511*4882a593Smuzhiyun static void zx_vou_disable_vblank(struct drm_crtc *crtc)
512*4882a593Smuzhiyun {
513*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
514*4882a593Smuzhiyun 	struct zx_vou_hw *vou = crtc_to_vou(crtc);
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	zx_writel_mask(vou->timing + TIMING_INT_CTRL,
517*4882a593Smuzhiyun 		       zcrtc->bits->int_frame_mask, 0);
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun static const struct drm_crtc_funcs zx_crtc_funcs = {
521*4882a593Smuzhiyun 	.destroy = drm_crtc_cleanup,
522*4882a593Smuzhiyun 	.set_config = drm_atomic_helper_set_config,
523*4882a593Smuzhiyun 	.page_flip = drm_atomic_helper_page_flip,
524*4882a593Smuzhiyun 	.reset = drm_atomic_helper_crtc_reset,
525*4882a593Smuzhiyun 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
526*4882a593Smuzhiyun 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
527*4882a593Smuzhiyun 	.enable_vblank = zx_vou_enable_vblank,
528*4882a593Smuzhiyun 	.disable_vblank = zx_vou_disable_vblank,
529*4882a593Smuzhiyun };
530*4882a593Smuzhiyun 
zx_crtc_init(struct drm_device * drm,struct zx_vou_hw * vou,enum vou_chn_type chn_type)531*4882a593Smuzhiyun static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
532*4882a593Smuzhiyun 			enum vou_chn_type chn_type)
533*4882a593Smuzhiyun {
534*4882a593Smuzhiyun 	struct device *dev = vou->dev;
535*4882a593Smuzhiyun 	struct zx_plane *zplane;
536*4882a593Smuzhiyun 	struct zx_crtc *zcrtc;
537*4882a593Smuzhiyun 	int ret;
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	zcrtc = devm_kzalloc(dev, sizeof(*zcrtc), GFP_KERNEL);
540*4882a593Smuzhiyun 	if (!zcrtc)
541*4882a593Smuzhiyun 		return -ENOMEM;
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	zcrtc->vou = vou;
544*4882a593Smuzhiyun 	zcrtc->chn_type = chn_type;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
547*4882a593Smuzhiyun 	if (!zplane)
548*4882a593Smuzhiyun 		return -ENOMEM;
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	zplane->dev = dev;
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	if (chn_type == VOU_CHN_MAIN) {
553*4882a593Smuzhiyun 		zplane->layer = vou->osd + MAIN_GL_OFFSET;
554*4882a593Smuzhiyun 		zplane->csc = vou->osd + MAIN_GL_CSC_OFFSET;
555*4882a593Smuzhiyun 		zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
556*4882a593Smuzhiyun 		zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
557*4882a593Smuzhiyun 		zplane->bits = &zx_gl_bits[0];
558*4882a593Smuzhiyun 		zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
559*4882a593Smuzhiyun 		zcrtc->chncsc = vou->osd + MAIN_CHN_CSC_OFFSET;
560*4882a593Smuzhiyun 		zcrtc->dither = vou->osd + MAIN_DITHER_OFFSET;
561*4882a593Smuzhiyun 		zcrtc->regs = &main_crtc_regs;
562*4882a593Smuzhiyun 		zcrtc->bits = &main_crtc_bits;
563*4882a593Smuzhiyun 	} else {
564*4882a593Smuzhiyun 		zplane->layer = vou->osd + AUX_GL_OFFSET;
565*4882a593Smuzhiyun 		zplane->csc = vou->osd + AUX_GL_CSC_OFFSET;
566*4882a593Smuzhiyun 		zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
567*4882a593Smuzhiyun 		zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
568*4882a593Smuzhiyun 		zplane->bits = &zx_gl_bits[1];
569*4882a593Smuzhiyun 		zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
570*4882a593Smuzhiyun 		zcrtc->chncsc = vou->osd + AUX_CHN_CSC_OFFSET;
571*4882a593Smuzhiyun 		zcrtc->dither = vou->osd + AUX_DITHER_OFFSET;
572*4882a593Smuzhiyun 		zcrtc->regs = &aux_crtc_regs;
573*4882a593Smuzhiyun 		zcrtc->bits = &aux_crtc_bits;
574*4882a593Smuzhiyun 	}
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 	zcrtc->pixclk = devm_clk_get(dev, (chn_type == VOU_CHN_MAIN) ?
577*4882a593Smuzhiyun 					  "main_wclk" : "aux_wclk");
578*4882a593Smuzhiyun 	if (IS_ERR(zcrtc->pixclk)) {
579*4882a593Smuzhiyun 		ret = PTR_ERR(zcrtc->pixclk);
580*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to get pix clk: %d\n", ret);
581*4882a593Smuzhiyun 		return ret;
582*4882a593Smuzhiyun 	}
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_PRIMARY);
585*4882a593Smuzhiyun 	if (ret) {
586*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to init primary plane: %d\n", ret);
587*4882a593Smuzhiyun 		return ret;
588*4882a593Smuzhiyun 	}
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 	zcrtc->primary = &zplane->plane;
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	ret = drm_crtc_init_with_planes(drm, &zcrtc->crtc, zcrtc->primary, NULL,
593*4882a593Smuzhiyun 					&zx_crtc_funcs, NULL);
594*4882a593Smuzhiyun 	if (ret) {
595*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to init drm crtc: %d\n", ret);
596*4882a593Smuzhiyun 		return ret;
597*4882a593Smuzhiyun 	}
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 	drm_crtc_helper_add(&zcrtc->crtc, &zx_crtc_helper_funcs);
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	if (chn_type == VOU_CHN_MAIN)
602*4882a593Smuzhiyun 		vou->main_crtc = zcrtc;
603*4882a593Smuzhiyun 	else
604*4882a593Smuzhiyun 		vou->aux_crtc = zcrtc;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	return 0;
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun 
zx_vou_layer_enable(struct drm_plane * plane)609*4882a593Smuzhiyun void zx_vou_layer_enable(struct drm_plane *plane)
610*4882a593Smuzhiyun {
611*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(plane->state->crtc);
612*4882a593Smuzhiyun 	struct zx_vou_hw *vou = zcrtc->vou;
613*4882a593Smuzhiyun 	struct zx_plane *zplane = to_zx_plane(plane);
614*4882a593Smuzhiyun 	const struct vou_layer_bits *bits = zplane->bits;
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 	if (zcrtc->chn_type == VOU_CHN_MAIN) {
617*4882a593Smuzhiyun 		zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, 0);
618*4882a593Smuzhiyun 		zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, 0);
619*4882a593Smuzhiyun 	} else {
620*4882a593Smuzhiyun 		zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel,
621*4882a593Smuzhiyun 			       bits->chnsel);
622*4882a593Smuzhiyun 		zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel,
623*4882a593Smuzhiyun 			       bits->clksel);
624*4882a593Smuzhiyun 	}
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable);
627*4882a593Smuzhiyun }
628*4882a593Smuzhiyun 
zx_vou_layer_disable(struct drm_plane * plane,struct drm_plane_state * old_state)629*4882a593Smuzhiyun void zx_vou_layer_disable(struct drm_plane *plane,
630*4882a593Smuzhiyun 			  struct drm_plane_state *old_state)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun 	struct zx_crtc *zcrtc = to_zx_crtc(old_state->crtc);
633*4882a593Smuzhiyun 	struct zx_vou_hw *vou = zcrtc->vou;
634*4882a593Smuzhiyun 	struct zx_plane *zplane = to_zx_plane(plane);
635*4882a593Smuzhiyun 	const struct vou_layer_bits *bits = zplane->bits;
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun 	zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0);
638*4882a593Smuzhiyun }
639*4882a593Smuzhiyun 
zx_overlay_init(struct drm_device * drm,struct zx_vou_hw * vou)640*4882a593Smuzhiyun static void zx_overlay_init(struct drm_device *drm, struct zx_vou_hw *vou)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun 	struct device *dev = vou->dev;
643*4882a593Smuzhiyun 	struct zx_plane *zplane;
644*4882a593Smuzhiyun 	int i;
645*4882a593Smuzhiyun 	int ret;
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun 	/*
648*4882a593Smuzhiyun 	 * VL0 has some quirks on scaling support which need special handling.
649*4882a593Smuzhiyun 	 * Let's leave it out for now.
650*4882a593Smuzhiyun 	 */
651*4882a593Smuzhiyun 	for (i = 1; i < VL_NUM; i++) {
652*4882a593Smuzhiyun 		zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
653*4882a593Smuzhiyun 		if (!zplane) {
654*4882a593Smuzhiyun 			DRM_DEV_ERROR(dev, "failed to allocate zplane %d\n", i);
655*4882a593Smuzhiyun 			return;
656*4882a593Smuzhiyun 		}
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 		zplane->layer = vou->osd + OSD_VL_OFFSET(i);
659*4882a593Smuzhiyun 		zplane->hbsc = vou->osd + HBSC_VL_OFFSET(i);
660*4882a593Smuzhiyun 		zplane->rsz = vou->otfppu + RSZ_VL_OFFSET(i);
661*4882a593Smuzhiyun 		zplane->bits = &zx_vl_bits[i];
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 		ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_OVERLAY);
664*4882a593Smuzhiyun 		if (ret) {
665*4882a593Smuzhiyun 			DRM_DEV_ERROR(dev, "failed to init overlay %d\n", i);
666*4882a593Smuzhiyun 			continue;
667*4882a593Smuzhiyun 		}
668*4882a593Smuzhiyun 	}
669*4882a593Smuzhiyun }
670*4882a593Smuzhiyun 
zx_osd_int_update(struct zx_crtc * zcrtc)671*4882a593Smuzhiyun static inline void zx_osd_int_update(struct zx_crtc *zcrtc)
672*4882a593Smuzhiyun {
673*4882a593Smuzhiyun 	struct drm_crtc *crtc = &zcrtc->crtc;
674*4882a593Smuzhiyun 	struct drm_plane *plane;
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	vou_chn_set_update(zcrtc);
677*4882a593Smuzhiyun 
678*4882a593Smuzhiyun 	drm_for_each_plane_mask(plane, crtc->dev, crtc->state->plane_mask)
679*4882a593Smuzhiyun 		zx_plane_set_update(plane);
680*4882a593Smuzhiyun }
681*4882a593Smuzhiyun 
vou_irq_handler(int irq,void * dev_id)682*4882a593Smuzhiyun static irqreturn_t vou_irq_handler(int irq, void *dev_id)
683*4882a593Smuzhiyun {
684*4882a593Smuzhiyun 	struct zx_vou_hw *vou = dev_id;
685*4882a593Smuzhiyun 	u32 state;
686*4882a593Smuzhiyun 
687*4882a593Smuzhiyun 	/* Handle TIMING_CTRL frame interrupts */
688*4882a593Smuzhiyun 	state = zx_readl(vou->timing + TIMING_INT_STATE);
689*4882a593Smuzhiyun 	zx_writel(vou->timing + TIMING_INT_STATE, state);
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	if (state & TIMING_INT_MAIN_FRAME)
692*4882a593Smuzhiyun 		drm_crtc_handle_vblank(&vou->main_crtc->crtc);
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun 	if (state & TIMING_INT_AUX_FRAME)
695*4882a593Smuzhiyun 		drm_crtc_handle_vblank(&vou->aux_crtc->crtc);
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	/* Handle OSD interrupts */
698*4882a593Smuzhiyun 	state = zx_readl(vou->osd + OSD_INT_STA);
699*4882a593Smuzhiyun 	zx_writel(vou->osd + OSD_INT_CLRSTA, state);
700*4882a593Smuzhiyun 
701*4882a593Smuzhiyun 	if (state & OSD_INT_MAIN_UPT)
702*4882a593Smuzhiyun 		zx_osd_int_update(vou->main_crtc);
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	if (state & OSD_INT_AUX_UPT)
705*4882a593Smuzhiyun 		zx_osd_int_update(vou->aux_crtc);
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 	if (state & OSD_INT_ERROR)
708*4882a593Smuzhiyun 		DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state);
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	return IRQ_HANDLED;
711*4882a593Smuzhiyun }
712*4882a593Smuzhiyun 
vou_dtrc_init(struct zx_vou_hw * vou)713*4882a593Smuzhiyun static void vou_dtrc_init(struct zx_vou_hw *vou)
714*4882a593Smuzhiyun {
715*4882a593Smuzhiyun 	/* Clear bit for bypass by ID */
716*4882a593Smuzhiyun 	zx_writel_mask(vou->dtrc + DTRC_DETILE_CTRL,
717*4882a593Smuzhiyun 		       TILE2RASTESCAN_BYPASS_MODE, 0);
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun 	/* Select ARIDR mode */
720*4882a593Smuzhiyun 	zx_writel_mask(vou->dtrc + DTRC_DETILE_CTRL, DETILE_ARIDR_MODE_MASK,
721*4882a593Smuzhiyun 		       DETILE_ARID_IN_ARIDR);
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	/* Bypass decompression for both frames */
724*4882a593Smuzhiyun 	zx_writel_mask(vou->dtrc + DTRC_F0_CTRL, DTRC_DECOMPRESS_BYPASS,
725*4882a593Smuzhiyun 		       DTRC_DECOMPRESS_BYPASS);
726*4882a593Smuzhiyun 	zx_writel_mask(vou->dtrc + DTRC_F1_CTRL, DTRC_DECOMPRESS_BYPASS,
727*4882a593Smuzhiyun 		       DTRC_DECOMPRESS_BYPASS);
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 	/* Set up ARID register */
730*4882a593Smuzhiyun 	zx_writel(vou->dtrc + DTRC_ARID, DTRC_ARID3(0xf) | DTRC_ARID2(0xe) |
731*4882a593Smuzhiyun 		  DTRC_ARID1(0xf) | DTRC_ARID0(0xe));
732*4882a593Smuzhiyun }
733*4882a593Smuzhiyun 
vou_hw_init(struct zx_vou_hw * vou)734*4882a593Smuzhiyun static void vou_hw_init(struct zx_vou_hw *vou)
735*4882a593Smuzhiyun {
736*4882a593Smuzhiyun 	/* Release reset for all VOU modules */
737*4882a593Smuzhiyun 	zx_writel(vou->vouctl + VOU_SOFT_RST, ~0);
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 	/* Enable all VOU module clocks */
740*4882a593Smuzhiyun 	zx_writel(vou->vouctl + VOU_CLK_EN, ~0);
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun 	/* Clear both OSD and TIMING_CTRL interrupt state */
743*4882a593Smuzhiyun 	zx_writel(vou->osd + OSD_INT_CLRSTA, ~0);
744*4882a593Smuzhiyun 	zx_writel(vou->timing + TIMING_INT_STATE, ~0);
745*4882a593Smuzhiyun 
746*4882a593Smuzhiyun 	/* Enable OSD and TIMING_CTRL interrrupts */
747*4882a593Smuzhiyun 	zx_writel(vou->osd + OSD_INT_MSK, OSD_INT_ENABLE);
748*4882a593Smuzhiyun 	zx_writel(vou->timing + TIMING_INT_CTRL, TIMING_INT_ENABLE);
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 	/* Select GPC as input to gl/vl scaler as a sane default setting */
751*4882a593Smuzhiyun 	zx_writel(vou->otfppu + OTFPPU_RSZ_DATA_SOURCE, 0x2a);
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun 	/*
754*4882a593Smuzhiyun 	 * Needs to reset channel and layer logic per frame when frame starts
755*4882a593Smuzhiyun 	 * to get VOU work properly.
756*4882a593Smuzhiyun 	 */
757*4882a593Smuzhiyun 	zx_writel_mask(vou->osd + OSD_RST_CLR, RST_PER_FRAME, RST_PER_FRAME);
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	vou_dtrc_init(vou);
760*4882a593Smuzhiyun }
761*4882a593Smuzhiyun 
zx_crtc_bind(struct device * dev,struct device * master,void * data)762*4882a593Smuzhiyun static int zx_crtc_bind(struct device *dev, struct device *master, void *data)
763*4882a593Smuzhiyun {
764*4882a593Smuzhiyun 	struct platform_device *pdev = to_platform_device(dev);
765*4882a593Smuzhiyun 	struct drm_device *drm = data;
766*4882a593Smuzhiyun 	struct zx_vou_hw *vou;
767*4882a593Smuzhiyun 	struct resource *res;
768*4882a593Smuzhiyun 	int irq;
769*4882a593Smuzhiyun 	int ret;
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	vou = devm_kzalloc(dev, sizeof(*vou), GFP_KERNEL);
772*4882a593Smuzhiyun 	if (!vou)
773*4882a593Smuzhiyun 		return -ENOMEM;
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "osd");
776*4882a593Smuzhiyun 	vou->osd = devm_ioremap_resource(dev, res);
777*4882a593Smuzhiyun 	if (IS_ERR(vou->osd)) {
778*4882a593Smuzhiyun 		ret = PTR_ERR(vou->osd);
779*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to remap osd region: %d\n", ret);
780*4882a593Smuzhiyun 		return ret;
781*4882a593Smuzhiyun 	}
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "timing_ctrl");
784*4882a593Smuzhiyun 	vou->timing = devm_ioremap_resource(dev, res);
785*4882a593Smuzhiyun 	if (IS_ERR(vou->timing)) {
786*4882a593Smuzhiyun 		ret = PTR_ERR(vou->timing);
787*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to remap timing_ctrl region: %d\n",
788*4882a593Smuzhiyun 			      ret);
789*4882a593Smuzhiyun 		return ret;
790*4882a593Smuzhiyun 	}
791*4882a593Smuzhiyun 
792*4882a593Smuzhiyun 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dtrc");
793*4882a593Smuzhiyun 	vou->dtrc = devm_ioremap_resource(dev, res);
794*4882a593Smuzhiyun 	if (IS_ERR(vou->dtrc)) {
795*4882a593Smuzhiyun 		ret = PTR_ERR(vou->dtrc);
796*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to remap dtrc region: %d\n", ret);
797*4882a593Smuzhiyun 		return ret;
798*4882a593Smuzhiyun 	}
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vou_ctrl");
801*4882a593Smuzhiyun 	vou->vouctl = devm_ioremap_resource(dev, res);
802*4882a593Smuzhiyun 	if (IS_ERR(vou->vouctl)) {
803*4882a593Smuzhiyun 		ret = PTR_ERR(vou->vouctl);
804*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to remap vou_ctrl region: %d\n",
805*4882a593Smuzhiyun 			      ret);
806*4882a593Smuzhiyun 		return ret;
807*4882a593Smuzhiyun 	}
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otfppu");
810*4882a593Smuzhiyun 	vou->otfppu = devm_ioremap_resource(dev, res);
811*4882a593Smuzhiyun 	if (IS_ERR(vou->otfppu)) {
812*4882a593Smuzhiyun 		ret = PTR_ERR(vou->otfppu);
813*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to remap otfppu region: %d\n", ret);
814*4882a593Smuzhiyun 		return ret;
815*4882a593Smuzhiyun 	}
816*4882a593Smuzhiyun 
817*4882a593Smuzhiyun 	irq = platform_get_irq(pdev, 0);
818*4882a593Smuzhiyun 	if (irq < 0)
819*4882a593Smuzhiyun 		return irq;
820*4882a593Smuzhiyun 
821*4882a593Smuzhiyun 	vou->axi_clk = devm_clk_get(dev, "aclk");
822*4882a593Smuzhiyun 	if (IS_ERR(vou->axi_clk)) {
823*4882a593Smuzhiyun 		ret = PTR_ERR(vou->axi_clk);
824*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to get axi_clk: %d\n", ret);
825*4882a593Smuzhiyun 		return ret;
826*4882a593Smuzhiyun 	}
827*4882a593Smuzhiyun 
828*4882a593Smuzhiyun 	vou->ppu_clk = devm_clk_get(dev, "ppu_wclk");
829*4882a593Smuzhiyun 	if (IS_ERR(vou->ppu_clk)) {
830*4882a593Smuzhiyun 		ret = PTR_ERR(vou->ppu_clk);
831*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to get ppu_clk: %d\n", ret);
832*4882a593Smuzhiyun 		return ret;
833*4882a593Smuzhiyun 	}
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 	ret = clk_prepare_enable(vou->axi_clk);
836*4882a593Smuzhiyun 	if (ret) {
837*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to enable axi_clk: %d\n", ret);
838*4882a593Smuzhiyun 		return ret;
839*4882a593Smuzhiyun 	}
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun 	clk_prepare_enable(vou->ppu_clk);
842*4882a593Smuzhiyun 	if (ret) {
843*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to enable ppu_clk: %d\n", ret);
844*4882a593Smuzhiyun 		goto disable_axi_clk;
845*4882a593Smuzhiyun 	}
846*4882a593Smuzhiyun 
847*4882a593Smuzhiyun 	vou->dev = dev;
848*4882a593Smuzhiyun 	dev_set_drvdata(dev, vou);
849*4882a593Smuzhiyun 
850*4882a593Smuzhiyun 	vou_hw_init(vou);
851*4882a593Smuzhiyun 
852*4882a593Smuzhiyun 	ret = devm_request_irq(dev, irq, vou_irq_handler, 0, "zx_vou", vou);
853*4882a593Smuzhiyun 	if (ret < 0) {
854*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to request vou irq: %d\n", ret);
855*4882a593Smuzhiyun 		goto disable_ppu_clk;
856*4882a593Smuzhiyun 	}
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun 	ret = zx_crtc_init(drm, vou, VOU_CHN_MAIN);
859*4882a593Smuzhiyun 	if (ret) {
860*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to init main channel crtc: %d\n",
861*4882a593Smuzhiyun 			      ret);
862*4882a593Smuzhiyun 		goto disable_ppu_clk;
863*4882a593Smuzhiyun 	}
864*4882a593Smuzhiyun 
865*4882a593Smuzhiyun 	ret = zx_crtc_init(drm, vou, VOU_CHN_AUX);
866*4882a593Smuzhiyun 	if (ret) {
867*4882a593Smuzhiyun 		DRM_DEV_ERROR(dev, "failed to init aux channel crtc: %d\n",
868*4882a593Smuzhiyun 			      ret);
869*4882a593Smuzhiyun 		goto disable_ppu_clk;
870*4882a593Smuzhiyun 	}
871*4882a593Smuzhiyun 
872*4882a593Smuzhiyun 	zx_overlay_init(drm, vou);
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	return 0;
875*4882a593Smuzhiyun 
876*4882a593Smuzhiyun disable_ppu_clk:
877*4882a593Smuzhiyun 	clk_disable_unprepare(vou->ppu_clk);
878*4882a593Smuzhiyun disable_axi_clk:
879*4882a593Smuzhiyun 	clk_disable_unprepare(vou->axi_clk);
880*4882a593Smuzhiyun 	return ret;
881*4882a593Smuzhiyun }
882*4882a593Smuzhiyun 
zx_crtc_unbind(struct device * dev,struct device * master,void * data)883*4882a593Smuzhiyun static void zx_crtc_unbind(struct device *dev, struct device *master,
884*4882a593Smuzhiyun 			   void *data)
885*4882a593Smuzhiyun {
886*4882a593Smuzhiyun 	struct zx_vou_hw *vou = dev_get_drvdata(dev);
887*4882a593Smuzhiyun 
888*4882a593Smuzhiyun 	clk_disable_unprepare(vou->axi_clk);
889*4882a593Smuzhiyun 	clk_disable_unprepare(vou->ppu_clk);
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun 
892*4882a593Smuzhiyun static const struct component_ops zx_crtc_component_ops = {
893*4882a593Smuzhiyun 	.bind = zx_crtc_bind,
894*4882a593Smuzhiyun 	.unbind = zx_crtc_unbind,
895*4882a593Smuzhiyun };
896*4882a593Smuzhiyun 
zx_crtc_probe(struct platform_device * pdev)897*4882a593Smuzhiyun static int zx_crtc_probe(struct platform_device *pdev)
898*4882a593Smuzhiyun {
899*4882a593Smuzhiyun 	return component_add(&pdev->dev, &zx_crtc_component_ops);
900*4882a593Smuzhiyun }
901*4882a593Smuzhiyun 
zx_crtc_remove(struct platform_device * pdev)902*4882a593Smuzhiyun static int zx_crtc_remove(struct platform_device *pdev)
903*4882a593Smuzhiyun {
904*4882a593Smuzhiyun 	component_del(&pdev->dev, &zx_crtc_component_ops);
905*4882a593Smuzhiyun 	return 0;
906*4882a593Smuzhiyun }
907*4882a593Smuzhiyun 
908*4882a593Smuzhiyun static const struct of_device_id zx_crtc_of_match[] = {
909*4882a593Smuzhiyun 	{ .compatible = "zte,zx296718-dpc", },
910*4882a593Smuzhiyun 	{ /* end */ },
911*4882a593Smuzhiyun };
912*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, zx_crtc_of_match);
913*4882a593Smuzhiyun 
914*4882a593Smuzhiyun struct platform_driver zx_crtc_driver = {
915*4882a593Smuzhiyun 	.probe = zx_crtc_probe,
916*4882a593Smuzhiyun 	.remove = zx_crtc_remove,
917*4882a593Smuzhiyun 	.driver	= {
918*4882a593Smuzhiyun 		.name = "zx-crtc",
919*4882a593Smuzhiyun 		.of_match_table	= zx_crtc_of_match,
920*4882a593Smuzhiyun 	},
921*4882a593Smuzhiyun };
922