1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2018 BayLibre, SAS
4  * Author: Neil Armstrong <narmstrong@baylibre.com>
5  * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
6  */
7 
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/mutex.h>
11 #include <linux/bitfield.h>
12 #include <linux/platform_device.h>
13 #include <drm/drmP.h>
14 #include <drm/drm_atomic.h>
15 #include <drm/drm_atomic_helper.h>
16 #include <drm/drm_plane_helper.h>
17 #include <drm/drm_gem_cma_helper.h>
18 #include <drm/drm_fb_cma_helper.h>
19 #include <drm/drm_gem_framebuffer_helper.h>
20 #include <drm/drm_rect.h>
21 
22 #include "meson_overlay.h"
23 #include "meson_vpp.h"
24 #include "meson_viu.h"
25 #include "meson_registers.h"
26 
27 /* VD1_IF0_GEN_REG */
28 #define VD_URGENT_CHROMA		BIT(28)
29 #define VD_URGENT_LUMA			BIT(27)
30 #define VD_HOLD_LINES(lines)		FIELD_PREP(GENMASK(24, 19), lines)
31 #define VD_DEMUX_MODE_RGB		BIT(16)
32 #define VD_BYTES_PER_PIXEL(val)		FIELD_PREP(GENMASK(15, 14), val)
33 #define VD_CHRO_RPT_LASTL_CTRL		BIT(6)
34 #define VD_LITTLE_ENDIAN		BIT(4)
35 #define VD_SEPARATE_EN			BIT(1)
36 #define VD_ENABLE			BIT(0)
37 
38 /* VD1_IF0_CANVAS0 */
39 #define CANVAS_ADDR2(addr)		FIELD_PREP(GENMASK(23, 16), addr)
40 #define CANVAS_ADDR1(addr)		FIELD_PREP(GENMASK(15, 8), addr)
41 #define CANVAS_ADDR0(addr)		FIELD_PREP(GENMASK(7, 0), addr)
42 
43 /* VD1_IF0_LUMA_X0 VD1_IF0_CHROMA_X0 */
44 #define VD_X_START(value)		FIELD_PREP(GENMASK(14, 0), value)
45 #define VD_X_END(value)			FIELD_PREP(GENMASK(30, 16), value)
46 
47 /* VD1_IF0_LUMA_Y0 VD1_IF0_CHROMA_Y0 */
48 #define VD_Y_START(value)		FIELD_PREP(GENMASK(12, 0), value)
49 #define VD_Y_END(value)			FIELD_PREP(GENMASK(28, 16), value)
50 
51 /* VD1_IF0_GEN_REG2 */
52 #define VD_COLOR_MAP(value)		FIELD_PREP(GENMASK(1, 0), value)
53 
54 /* VIU_VD1_FMT_CTRL */
55 #define VD_HORZ_Y_C_RATIO(value)	FIELD_PREP(GENMASK(22, 21), value)
56 #define VD_HORZ_FMT_EN			BIT(20)
57 #define VD_VERT_RPT_LINE0		BIT(16)
58 #define VD_VERT_INITIAL_PHASE(value)	FIELD_PREP(GENMASK(11, 8), value)
59 #define VD_VERT_PHASE_STEP(value)	FIELD_PREP(GENMASK(7, 1), value)
60 #define VD_VERT_FMT_EN			BIT(0)
61 
62 /* VPP_POSTBLEND_VD1_H_START_END */
63 #define VD_H_END(value)			FIELD_PREP(GENMASK(11, 0), value)
64 #define VD_H_START(value)		FIELD_PREP(GENMASK(27, 16), value)
65 
66 /* VPP_POSTBLEND_VD1_V_START_END */
67 #define VD_V_END(value)			FIELD_PREP(GENMASK(11, 0), value)
68 #define VD_V_START(value)		FIELD_PREP(GENMASK(27, 16), value)
69 
70 /* VPP_BLEND_VD2_V_START_END */
71 #define VD2_V_END(value)		FIELD_PREP(GENMASK(11, 0), value)
72 #define VD2_V_START(value)		FIELD_PREP(GENMASK(27, 16), value)
73 
74 /* VIU_VD1_FMT_W */
75 #define VD_V_WIDTH(value)		FIELD_PREP(GENMASK(11, 0), value)
76 #define VD_H_WIDTH(value)		FIELD_PREP(GENMASK(27, 16), value)
77 
78 /* VPP_HSC_REGION12_STARTP VPP_HSC_REGION34_STARTP */
79 #define VD_REGION24_START(value)	FIELD_PREP(GENMASK(11, 0), value)
80 #define VD_REGION13_END(value)		FIELD_PREP(GENMASK(27, 16), value)
81 
82 struct meson_overlay {
83 	struct drm_plane base;
84 	struct meson_drm *priv;
85 };
86 #define to_meson_overlay(x) container_of(x, struct meson_overlay, base)
87 
88 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
89 
90 static int meson_overlay_atomic_check(struct drm_plane *plane,
91 				      struct drm_plane_state *state)
92 {
93 	struct drm_crtc_state *crtc_state;
94 
95 	if (!state->crtc)
96 		return 0;
97 
98 	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
99 	if (IS_ERR(crtc_state))
100 		return PTR_ERR(crtc_state);
101 
102 	return drm_atomic_helper_check_plane_state(state, crtc_state,
103 						   FRAC_16_16(1, 5),
104 						   FRAC_16_16(5, 1),
105 						   true, true);
106 }
107 
108 /* Takes a fixed 16.16 number and converts it to integer. */
109 static inline int64_t fixed16_to_int(int64_t value)
110 {
111 	return value >> 16;
112 }
113 
114 static const uint8_t skip_tab[6] = {
115 	0x24, 0x04, 0x68, 0x48, 0x28, 0x08,
116 };
117 
118 static void meson_overlay_get_vertical_phase(unsigned int ratio_y, int *phase,
119 					     int *repeat, bool interlace)
120 {
121 	int offset_in = 0;
122 	int offset_out = 0;
123 	int repeat_skip = 0;
124 
125 	if (!interlace && ratio_y > (1 << 18))
126 		offset_out = (1 * ratio_y) >> 10;
127 
128 	while ((offset_in + (4 << 8)) <= offset_out) {
129 		repeat_skip++;
130 		offset_in += 4 << 8;
131 	}
132 
133 	*phase = (offset_out - offset_in) >> 2;
134 
135 	if (*phase > 0x100)
136 		repeat_skip++;
137 
138 	*phase = *phase & 0xff;
139 
140 	if (repeat_skip > 5)
141 		repeat_skip = 5;
142 
143 	*repeat = skip_tab[repeat_skip];
144 }
145 
146 static void meson_overlay_setup_scaler_params(struct meson_drm *priv,
147 					      struct drm_plane *plane,
148 					      bool interlace_mode)
149 {
150 	struct drm_crtc_state *crtc_state = priv->crtc->state;
151 	int video_top, video_left, video_width, video_height;
152 	struct drm_plane_state *state = plane->state;
153 	unsigned int vd_start_lines, vd_end_lines;
154 	unsigned int hd_start_lines, hd_end_lines;
155 	unsigned int crtc_height, crtc_width;
156 	unsigned int vsc_startp, vsc_endp;
157 	unsigned int hsc_startp, hsc_endp;
158 	unsigned int crop_top, crop_left;
159 	int vphase, vphase_repeat_skip;
160 	unsigned int ratio_x, ratio_y;
161 	int temp_height, temp_width;
162 	unsigned int w_in, h_in;
163 	int temp, start, end;
164 
165 	if (!crtc_state) {
166 		DRM_ERROR("Invalid crtc_state\n");
167 		return;
168 	}
169 
170 	crtc_height = crtc_state->mode.vdisplay;
171 	crtc_width = crtc_state->mode.hdisplay;
172 
173 	w_in = fixed16_to_int(state->src_w);
174 	h_in = fixed16_to_int(state->src_h);
175 	crop_top = fixed16_to_int(state->src_x);
176 	crop_left = fixed16_to_int(state->src_x);
177 
178 	video_top = state->crtc_y;
179 	video_left = state->crtc_x;
180 	video_width = state->crtc_w;
181 	video_height = state->crtc_h;
182 
183 	DRM_DEBUG("crtc_width %d crtc_height %d interlace %d\n",
184 		  crtc_width, crtc_height, interlace_mode);
185 	DRM_DEBUG("w_in %d h_in %d crop_top %d crop_left %d\n",
186 		  w_in, h_in, crop_top, crop_left);
187 	DRM_DEBUG("video top %d left %d width %d height %d\n",
188 		  video_top, video_left, video_width, video_height);
189 
190 	ratio_x = (w_in << 18) / video_width;
191 	ratio_y = (h_in << 18) / video_height;
192 
193 	if (ratio_x * video_width < (w_in << 18))
194 		ratio_x++;
195 
196 	DRM_DEBUG("ratio x 0x%x y 0x%x\n", ratio_x, ratio_y);
197 
198 	meson_overlay_get_vertical_phase(ratio_y, &vphase, &vphase_repeat_skip,
199 					 interlace_mode);
200 
201 	DRM_DEBUG("vphase 0x%x skip %d\n", vphase, vphase_repeat_skip);
202 
203 	/* Vertical */
204 
205 	start = video_top + video_height / 2 - ((h_in << 17) / ratio_y);
206 	end = (h_in << 18) / ratio_y + start - 1;
207 
208 	if (video_top < 0 && start < 0)
209 		vd_start_lines = (-(start) * ratio_y) >> 18;
210 	else if (start < video_top)
211 		vd_start_lines = ((video_top - start) * ratio_y) >> 18;
212 	else
213 		vd_start_lines = 0;
214 
215 	if (video_top < 0)
216 		temp_height = min_t(unsigned int,
217 				    video_top + video_height - 1,
218 				    crtc_height - 1);
219 	else
220 		temp_height = min_t(unsigned int,
221 				    video_top + video_height - 1,
222 				    crtc_height - 1) - video_top + 1;
223 
224 	temp = vd_start_lines + (temp_height * ratio_y >> 18);
225 	vd_end_lines = (temp <= (h_in - 1)) ? temp : (h_in - 1);
226 
227 	vd_start_lines += crop_left;
228 	vd_end_lines += crop_left;
229 
230 	/*
231 	 * TOFIX: Input frames are handled and scaled like progressive frames,
232 	 * proper handling of interlaced field input frames need to be figured
233 	 * out using the proper framebuffer flags set by userspace.
234 	 */
235 	if (interlace_mode) {
236 		start >>= 1;
237 		end >>= 1;
238 	}
239 
240 	vsc_startp = max_t(int, start,
241 			   max_t(int, 0, video_top));
242 	vsc_endp = min_t(int, end,
243 			 min_t(int, crtc_height - 1,
244 			       video_top + video_height - 1));
245 
246 	DRM_DEBUG("vsc startp %d endp %d start_lines %d end_lines %d\n",
247 		 vsc_startp, vsc_endp, vd_start_lines, vd_end_lines);
248 
249 	/* Horizontal */
250 
251 	start = video_left + video_width / 2 - ((w_in << 17) / ratio_x);
252 	end = (w_in << 18) / ratio_x + start - 1;
253 
254 	if (video_left < 0 && start < 0)
255 		hd_start_lines = (-(start) * ratio_x) >> 18;
256 	else if (start < video_left)
257 		hd_start_lines = ((video_left - start) * ratio_x) >> 18;
258 	else
259 		hd_start_lines = 0;
260 
261 	if (video_left < 0)
262 		temp_width = min_t(unsigned int,
263 				   video_left + video_width - 1,
264 				   crtc_width - 1);
265 	else
266 		temp_width = min_t(unsigned int,
267 				   video_left + video_width - 1,
268 				   crtc_width - 1) - video_left + 1;
269 
270 	temp = hd_start_lines + (temp_width * ratio_x >> 18);
271 	hd_end_lines = (temp <= (w_in - 1)) ? temp : (w_in - 1);
272 
273 	priv->viu.vpp_line_in_length = hd_end_lines - hd_start_lines + 1;
274 	hsc_startp = max_t(int, start, max_t(int, 0, video_left));
275 	hsc_endp = min_t(int, end, min_t(int, crtc_width - 1,
276 					 video_left + video_width - 1));
277 
278 	hd_start_lines += crop_top;
279 	hd_end_lines += crop_top;
280 
281 	DRM_DEBUG("hsc startp %d endp %d start_lines %d end_lines %d\n",
282 		 hsc_startp, hsc_endp, hd_start_lines, hd_end_lines);
283 
284 	priv->viu.vpp_vsc_start_phase_step = ratio_y << 6;
285 
286 	priv->viu.vpp_vsc_ini_phase = vphase << 8;
287 	priv->viu.vpp_vsc_phase_ctrl = (1 << 13) | (4 << 8) |
288 				       vphase_repeat_skip;
289 
290 	priv->viu.vd1_if0_luma_x0 = VD_X_START(hd_start_lines) |
291 				    VD_X_END(hd_end_lines);
292 	priv->viu.vd1_if0_chroma_x0 = VD_X_START(hd_start_lines >> 1) |
293 				      VD_X_END(hd_end_lines >> 1);
294 
295 	priv->viu.viu_vd1_fmt_w =
296 			VD_H_WIDTH(hd_end_lines - hd_start_lines + 1) |
297 			VD_V_WIDTH(hd_end_lines/2 - hd_start_lines/2 + 1);
298 
299 	priv->viu.vd1_if0_luma_y0 = VD_Y_START(vd_start_lines) |
300 				    VD_Y_END(vd_end_lines);
301 
302 	priv->viu.vd1_if0_chroma_y0 = VD_Y_START(vd_start_lines >> 1) |
303 				      VD_Y_END(vd_end_lines >> 1);
304 
305 	priv->viu.vpp_pic_in_height = h_in;
306 
307 	priv->viu.vpp_postblend_vd1_h_start_end = VD_H_START(hsc_startp) |
308 						  VD_H_END(hsc_endp);
309 	priv->viu.vpp_blend_vd2_h_start_end = VD_H_START(hd_start_lines) |
310 					      VD_H_END(hd_end_lines);
311 	priv->viu.vpp_hsc_region12_startp = VD_REGION13_END(0) |
312 					    VD_REGION24_START(hsc_startp);
313 	priv->viu.vpp_hsc_region34_startp =
314 				VD_REGION13_END(hsc_startp) |
315 				VD_REGION24_START(hsc_endp - hsc_startp);
316 	priv->viu.vpp_hsc_region4_endp = hsc_endp - hsc_startp;
317 	priv->viu.vpp_hsc_start_phase_step = ratio_x << 6;
318 	priv->viu.vpp_hsc_region1_phase_slope = 0;
319 	priv->viu.vpp_hsc_region3_phase_slope = 0;
320 	priv->viu.vpp_hsc_phase_ctrl = (1 << 21) | (4 << 16);
321 
322 	priv->viu.vpp_line_in_length = hd_end_lines - hd_start_lines + 1;
323 	priv->viu.vpp_preblend_h_size = hd_end_lines - hd_start_lines + 1;
324 
325 	priv->viu.vpp_postblend_vd1_v_start_end = VD_V_START(vsc_startp) |
326 						  VD_V_END(vsc_endp);
327 	priv->viu.vpp_blend_vd2_v_start_end =
328 				VD2_V_START((vd_end_lines + 1) >> 1) |
329 				VD2_V_END(vd_end_lines);
330 
331 	priv->viu.vpp_vsc_region12_startp = 0;
332 	priv->viu.vpp_vsc_region34_startp =
333 				VD_REGION13_END(vsc_endp - vsc_startp) |
334 				VD_REGION24_START(vsc_endp - vsc_startp);
335 	priv->viu.vpp_vsc_region4_endp = vsc_endp - vsc_startp;
336 	priv->viu.vpp_vsc_start_phase_step = ratio_y << 6;
337 }
338 
339 static void meson_overlay_atomic_update(struct drm_plane *plane,
340 					struct drm_plane_state *old_state)
341 {
342 	struct meson_overlay *meson_overlay = to_meson_overlay(plane);
343 	struct drm_plane_state *state = plane->state;
344 	struct drm_framebuffer *fb = state->fb;
345 	struct meson_drm *priv = meson_overlay->priv;
346 	struct drm_gem_cma_object *gem;
347 	unsigned long flags;
348 	bool interlace_mode;
349 
350 	DRM_DEBUG_DRIVER("\n");
351 
352 	interlace_mode = state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE;
353 
354 	spin_lock_irqsave(&priv->drm->event_lock, flags);
355 
356 	priv->viu.vd1_if0_gen_reg = VD_URGENT_CHROMA |
357 				    VD_URGENT_LUMA |
358 				    VD_HOLD_LINES(9) |
359 				    VD_CHRO_RPT_LASTL_CTRL |
360 				    VD_ENABLE;
361 
362 	/* Setup scaler params */
363 	meson_overlay_setup_scaler_params(priv, plane, interlace_mode);
364 
365 	priv->viu.vd1_if0_repeat_loop = 0;
366 	priv->viu.vd1_if0_luma0_rpt_pat = interlace_mode ? 8 : 0;
367 	priv->viu.vd1_if0_chroma0_rpt_pat = interlace_mode ? 8 : 0;
368 	priv->viu.vd1_range_map_y = 0;
369 	priv->viu.vd1_range_map_cb = 0;
370 	priv->viu.vd1_range_map_cr = 0;
371 
372 	/* Default values for RGB888/YUV444 */
373 	priv->viu.vd1_if0_gen_reg2 = 0;
374 	priv->viu.viu_vd1_fmt_ctrl = 0;
375 
376 	switch (fb->format->format) {
377 	/* TOFIX DRM_FORMAT_RGB888 should be supported */
378 	case DRM_FORMAT_YUYV:
379 		priv->viu.vd1_if0_gen_reg |= VD_BYTES_PER_PIXEL(1);
380 		priv->viu.vd1_if0_canvas0 =
381 					CANVAS_ADDR2(priv->canvas_id_vd1_0) |
382 					CANVAS_ADDR1(priv->canvas_id_vd1_0) |
383 					CANVAS_ADDR0(priv->canvas_id_vd1_0);
384 		priv->viu.viu_vd1_fmt_ctrl = VD_HORZ_Y_C_RATIO(1) | /* /2 */
385 					     VD_HORZ_FMT_EN |
386 					     VD_VERT_RPT_LINE0 |
387 					     VD_VERT_INITIAL_PHASE(12) |
388 					     VD_VERT_PHASE_STEP(16) | /* /2 */
389 					     VD_VERT_FMT_EN;
390 		break;
391 	case DRM_FORMAT_NV12:
392 	case DRM_FORMAT_NV21:
393 		priv->viu.vd1_if0_gen_reg |= VD_SEPARATE_EN;
394 		priv->viu.vd1_if0_canvas0 =
395 					CANVAS_ADDR2(priv->canvas_id_vd1_1) |
396 					CANVAS_ADDR1(priv->canvas_id_vd1_1) |
397 					CANVAS_ADDR0(priv->canvas_id_vd1_0);
398 		if (fb->format->format == DRM_FORMAT_NV12)
399 			priv->viu.vd1_if0_gen_reg2 = VD_COLOR_MAP(1);
400 		else
401 			priv->viu.vd1_if0_gen_reg2 = VD_COLOR_MAP(2);
402 		priv->viu.viu_vd1_fmt_ctrl = VD_HORZ_Y_C_RATIO(1) | /* /2 */
403 					     VD_HORZ_FMT_EN |
404 					     VD_VERT_RPT_LINE0 |
405 					     VD_VERT_INITIAL_PHASE(12) |
406 					     VD_VERT_PHASE_STEP(8) | /* /4 */
407 					     VD_VERT_FMT_EN;
408 		break;
409 	case DRM_FORMAT_YUV444:
410 	case DRM_FORMAT_YUV422:
411 	case DRM_FORMAT_YUV420:
412 	case DRM_FORMAT_YUV411:
413 	case DRM_FORMAT_YUV410:
414 		priv->viu.vd1_if0_gen_reg |= VD_SEPARATE_EN;
415 		priv->viu.vd1_if0_canvas0 =
416 					CANVAS_ADDR2(priv->canvas_id_vd1_2) |
417 					CANVAS_ADDR1(priv->canvas_id_vd1_1) |
418 					CANVAS_ADDR0(priv->canvas_id_vd1_0);
419 		switch (fb->format->format) {
420 		case DRM_FORMAT_YUV422:
421 			priv->viu.viu_vd1_fmt_ctrl =
422 					VD_HORZ_Y_C_RATIO(1) | /* /2 */
423 					VD_HORZ_FMT_EN |
424 					VD_VERT_RPT_LINE0 |
425 					VD_VERT_INITIAL_PHASE(12) |
426 					VD_VERT_PHASE_STEP(16) | /* /2 */
427 					VD_VERT_FMT_EN;
428 			break;
429 		case DRM_FORMAT_YUV420:
430 			priv->viu.viu_vd1_fmt_ctrl =
431 					VD_HORZ_Y_C_RATIO(1) | /* /2 */
432 					VD_HORZ_FMT_EN |
433 					VD_VERT_RPT_LINE0 |
434 					VD_VERT_INITIAL_PHASE(12) |
435 					VD_VERT_PHASE_STEP(8) | /* /4 */
436 					VD_VERT_FMT_EN;
437 			break;
438 		case DRM_FORMAT_YUV411:
439 			priv->viu.viu_vd1_fmt_ctrl =
440 					VD_HORZ_Y_C_RATIO(2) | /* /4 */
441 					VD_HORZ_FMT_EN |
442 					VD_VERT_RPT_LINE0 |
443 					VD_VERT_INITIAL_PHASE(12) |
444 					VD_VERT_PHASE_STEP(16) | /* /2 */
445 					VD_VERT_FMT_EN;
446 			break;
447 		case DRM_FORMAT_YUV410:
448 			priv->viu.viu_vd1_fmt_ctrl =
449 					VD_HORZ_Y_C_RATIO(2) | /* /4 */
450 					VD_HORZ_FMT_EN |
451 					VD_VERT_RPT_LINE0 |
452 					VD_VERT_INITIAL_PHASE(12) |
453 					VD_VERT_PHASE_STEP(8) | /* /4 */
454 					VD_VERT_FMT_EN;
455 			break;
456 		}
457 		break;
458 	}
459 
460 	/* Update Canvas with buffer address */
461 	priv->viu.vd1_planes = fb->format->num_planes;
462 
463 	switch (priv->viu.vd1_planes) {
464 	case 3:
465 		gem = drm_fb_cma_get_gem_obj(fb, 2);
466 		priv->viu.vd1_addr2 = gem->paddr + fb->offsets[2];
467 		priv->viu.vd1_stride2 = fb->pitches[2];
468 		priv->viu.vd1_height2 =
469 			drm_format_info_plane_height(fb->format,
470 						fb->height, 2);
471 		DRM_DEBUG("plane 2 addr 0x%x stride %d height %d\n",
472 			 priv->viu.vd1_addr2,
473 			 priv->viu.vd1_stride2,
474 			 priv->viu.vd1_height2);
475 	/* fallthrough */
476 	case 2:
477 		gem = drm_fb_cma_get_gem_obj(fb, 1);
478 		priv->viu.vd1_addr1 = gem->paddr + fb->offsets[1];
479 		priv->viu.vd1_stride1 = fb->pitches[1];
480 		priv->viu.vd1_height1 =
481 			drm_format_info_plane_height(fb->format,
482 						fb->height, 1);
483 		DRM_DEBUG("plane 1 addr 0x%x stride %d height %d\n",
484 			 priv->viu.vd1_addr1,
485 			 priv->viu.vd1_stride1,
486 			 priv->viu.vd1_height1);
487 	/* fallthrough */
488 	case 1:
489 		gem = drm_fb_cma_get_gem_obj(fb, 0);
490 		priv->viu.vd1_addr0 = gem->paddr + fb->offsets[0];
491 		priv->viu.vd1_stride0 = fb->pitches[0];
492 		priv->viu.vd1_height0 =
493 			drm_format_info_plane_height(fb->format,
494 						fb->height, 0);
495 		DRM_DEBUG("plane 0 addr 0x%x stride %d height %d\n",
496 			 priv->viu.vd1_addr0,
497 			 priv->viu.vd1_stride0,
498 			 priv->viu.vd1_height0);
499 	}
500 
501 	priv->viu.vd1_enabled = true;
502 
503 	spin_unlock_irqrestore(&priv->drm->event_lock, flags);
504 
505 	DRM_DEBUG_DRIVER("\n");
506 }
507 
508 static void meson_overlay_atomic_disable(struct drm_plane *plane,
509 				       struct drm_plane_state *old_state)
510 {
511 	struct meson_overlay *meson_overlay = to_meson_overlay(plane);
512 	struct meson_drm *priv = meson_overlay->priv;
513 
514 	DRM_DEBUG_DRIVER("\n");
515 
516 	priv->viu.vd1_enabled = false;
517 
518 	/* Disable VD1 */
519 	if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
520 		writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
521 		writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL));
522 		writel_relaxed(0, priv->io_base + _REG(VD1_IF0_GEN_REG + 0x17b0));
523 		writel_relaxed(0, priv->io_base + _REG(VD2_IF0_GEN_REG + 0x17b0));
524 	} else
525 		writel_bits_relaxed(VPP_VD1_POSTBLEND | VPP_VD1_PREBLEND, 0,
526 				    priv->io_base + _REG(VPP_MISC));
527 
528 }
529 
530 static const struct drm_plane_helper_funcs meson_overlay_helper_funcs = {
531 	.atomic_check	= meson_overlay_atomic_check,
532 	.atomic_disable	= meson_overlay_atomic_disable,
533 	.atomic_update	= meson_overlay_atomic_update,
534 	.prepare_fb	= drm_gem_fb_prepare_fb,
535 };
536 
537 static const struct drm_plane_funcs meson_overlay_funcs = {
538 	.update_plane		= drm_atomic_helper_update_plane,
539 	.disable_plane		= drm_atomic_helper_disable_plane,
540 	.destroy		= drm_plane_cleanup,
541 	.reset			= drm_atomic_helper_plane_reset,
542 	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
543 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
544 };
545 
546 static const uint32_t supported_drm_formats[] = {
547 	DRM_FORMAT_YUYV,
548 	DRM_FORMAT_NV12,
549 	DRM_FORMAT_NV21,
550 	DRM_FORMAT_YUV444,
551 	DRM_FORMAT_YUV422,
552 	DRM_FORMAT_YUV420,
553 	DRM_FORMAT_YUV411,
554 	DRM_FORMAT_YUV410,
555 };
556 
557 int meson_overlay_create(struct meson_drm *priv)
558 {
559 	struct meson_overlay *meson_overlay;
560 	struct drm_plane *plane;
561 
562 	DRM_DEBUG_DRIVER("\n");
563 
564 	meson_overlay = devm_kzalloc(priv->drm->dev, sizeof(*meson_overlay),
565 				   GFP_KERNEL);
566 	if (!meson_overlay)
567 		return -ENOMEM;
568 
569 	meson_overlay->priv = priv;
570 	plane = &meson_overlay->base;
571 
572 	drm_universal_plane_init(priv->drm, plane, 0xFF,
573 				 &meson_overlay_funcs,
574 				 supported_drm_formats,
575 				 ARRAY_SIZE(supported_drm_formats),
576 				 NULL,
577 				 DRM_PLANE_TYPE_OVERLAY, "meson_overlay_plane");
578 
579 	drm_plane_helper_add(plane, &meson_overlay_helper_funcs);
580 
581 	/* For now, VD Overlay plane is always on the back */
582 	drm_plane_create_zpos_immutable_property(plane, 0);
583 
584 	priv->overlay_plane = plane;
585 
586 	DRM_DEBUG_DRIVER("\n");
587 
588 	return 0;
589 }
590