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