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