1 /* 2 * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9 #include <drm/drm_atomic.h> 10 #include <drm/drm_atomic_helper.h> 11 #include <drm/drm_plane_helper.h> 12 13 #include "dc.h" 14 #include "plane.h" 15 16 static void tegra_plane_destroy(struct drm_plane *plane) 17 { 18 struct tegra_plane *p = to_tegra_plane(plane); 19 20 drm_plane_cleanup(plane); 21 kfree(p); 22 } 23 24 static void tegra_plane_reset(struct drm_plane *plane) 25 { 26 struct tegra_plane *p = to_tegra_plane(plane); 27 struct tegra_plane_state *state; 28 29 if (plane->state) 30 __drm_atomic_helper_plane_destroy_state(plane->state); 31 32 kfree(plane->state); 33 plane->state = NULL; 34 35 state = kzalloc(sizeof(*state), GFP_KERNEL); 36 if (state) { 37 plane->state = &state->base; 38 plane->state->plane = plane; 39 plane->state->zpos = p->index; 40 plane->state->normalized_zpos = p->index; 41 } 42 } 43 44 static struct drm_plane_state * 45 tegra_plane_atomic_duplicate_state(struct drm_plane *plane) 46 { 47 struct tegra_plane_state *state = to_tegra_plane_state(plane->state); 48 struct tegra_plane_state *copy; 49 unsigned int i; 50 51 copy = kmalloc(sizeof(*copy), GFP_KERNEL); 52 if (!copy) 53 return NULL; 54 55 __drm_atomic_helper_plane_duplicate_state(plane, ©->base); 56 copy->tiling = state->tiling; 57 copy->format = state->format; 58 copy->swap = state->swap; 59 copy->bottom_up = state->bottom_up; 60 copy->opaque = state->opaque; 61 62 for (i = 0; i < 2; i++) 63 copy->blending[i] = state->blending[i]; 64 65 return ©->base; 66 } 67 68 static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, 69 struct drm_plane_state *state) 70 { 71 __drm_atomic_helper_plane_destroy_state(state); 72 kfree(state); 73 } 74 75 static bool tegra_plane_format_mod_supported(struct drm_plane *plane, 76 uint32_t format, 77 uint64_t modifier) 78 { 79 const struct drm_format_info *info = drm_format_info(format); 80 81 if (modifier == DRM_FORMAT_MOD_LINEAR) 82 return true; 83 84 if (info->num_planes == 1) 85 return true; 86 87 return false; 88 } 89 90 const struct drm_plane_funcs tegra_plane_funcs = { 91 .update_plane = drm_atomic_helper_update_plane, 92 .disable_plane = drm_atomic_helper_disable_plane, 93 .destroy = tegra_plane_destroy, 94 .reset = tegra_plane_reset, 95 .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, 96 .atomic_destroy_state = tegra_plane_atomic_destroy_state, 97 .format_mod_supported = tegra_plane_format_mod_supported, 98 }; 99 100 int tegra_plane_state_add(struct tegra_plane *plane, 101 struct drm_plane_state *state) 102 { 103 struct drm_crtc_state *crtc_state; 104 struct tegra_dc_state *tegra; 105 int err; 106 107 /* Propagate errors from allocation or locking failures. */ 108 crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); 109 if (IS_ERR(crtc_state)) 110 return PTR_ERR(crtc_state); 111 112 /* Check plane state for visibility and calculate clipping bounds */ 113 err = drm_atomic_helper_check_plane_state(state, crtc_state, 114 0, INT_MAX, true, true); 115 if (err < 0) 116 return err; 117 118 tegra = to_dc_state(crtc_state); 119 120 tegra->planes |= WIN_A_ACT_REQ << plane->index; 121 122 return 0; 123 } 124 125 int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap) 126 { 127 /* assume no swapping of fetched data */ 128 if (swap) 129 *swap = BYTE_SWAP_NOSWAP; 130 131 switch (fourcc) { 132 case DRM_FORMAT_ARGB4444: 133 *format = WIN_COLOR_DEPTH_B4G4R4A4; 134 break; 135 136 case DRM_FORMAT_ARGB1555: 137 *format = WIN_COLOR_DEPTH_B5G5R5A1; 138 break; 139 140 case DRM_FORMAT_RGB565: 141 *format = WIN_COLOR_DEPTH_B5G6R5; 142 break; 143 144 case DRM_FORMAT_RGBA5551: 145 *format = WIN_COLOR_DEPTH_A1B5G5R5; 146 break; 147 148 case DRM_FORMAT_ARGB8888: 149 *format = WIN_COLOR_DEPTH_B8G8R8A8; 150 break; 151 152 case DRM_FORMAT_ABGR8888: 153 *format = WIN_COLOR_DEPTH_R8G8B8A8; 154 break; 155 156 case DRM_FORMAT_ABGR4444: 157 *format = WIN_COLOR_DEPTH_R4G4B4A4; 158 break; 159 160 case DRM_FORMAT_ABGR1555: 161 *format = WIN_COLOR_DEPTH_R5G5B5A; 162 break; 163 164 case DRM_FORMAT_BGRA5551: 165 *format = WIN_COLOR_DEPTH_AR5G5B5; 166 break; 167 168 case DRM_FORMAT_XRGB1555: 169 *format = WIN_COLOR_DEPTH_B5G5R5X1; 170 break; 171 172 case DRM_FORMAT_RGBX5551: 173 *format = WIN_COLOR_DEPTH_X1B5G5R5; 174 break; 175 176 case DRM_FORMAT_XBGR1555: 177 *format = WIN_COLOR_DEPTH_R5G5B5X1; 178 break; 179 180 case DRM_FORMAT_BGRX5551: 181 *format = WIN_COLOR_DEPTH_X1R5G5B5; 182 break; 183 184 case DRM_FORMAT_BGR565: 185 *format = WIN_COLOR_DEPTH_R5G6B5; 186 break; 187 188 case DRM_FORMAT_BGRA8888: 189 *format = WIN_COLOR_DEPTH_A8R8G8B8; 190 break; 191 192 case DRM_FORMAT_RGBA8888: 193 *format = WIN_COLOR_DEPTH_A8B8G8R8; 194 break; 195 196 case DRM_FORMAT_XRGB8888: 197 *format = WIN_COLOR_DEPTH_B8G8R8X8; 198 break; 199 200 case DRM_FORMAT_XBGR8888: 201 *format = WIN_COLOR_DEPTH_R8G8B8X8; 202 break; 203 204 case DRM_FORMAT_UYVY: 205 *format = WIN_COLOR_DEPTH_YCbCr422; 206 break; 207 208 case DRM_FORMAT_YUYV: 209 if (!swap) 210 return -EINVAL; 211 212 *format = WIN_COLOR_DEPTH_YCbCr422; 213 *swap = BYTE_SWAP_SWAP2; 214 break; 215 216 case DRM_FORMAT_YUV420: 217 *format = WIN_COLOR_DEPTH_YCbCr420P; 218 break; 219 220 case DRM_FORMAT_YUV422: 221 *format = WIN_COLOR_DEPTH_YCbCr422P; 222 break; 223 224 default: 225 return -EINVAL; 226 } 227 228 return 0; 229 } 230 231 bool tegra_plane_format_is_yuv(unsigned int format, bool *planar) 232 { 233 switch (format) { 234 case WIN_COLOR_DEPTH_YCbCr422: 235 case WIN_COLOR_DEPTH_YUV422: 236 if (planar) 237 *planar = false; 238 239 return true; 240 241 case WIN_COLOR_DEPTH_YCbCr420P: 242 case WIN_COLOR_DEPTH_YUV420P: 243 case WIN_COLOR_DEPTH_YCbCr422P: 244 case WIN_COLOR_DEPTH_YUV422P: 245 case WIN_COLOR_DEPTH_YCbCr422R: 246 case WIN_COLOR_DEPTH_YUV422R: 247 case WIN_COLOR_DEPTH_YCbCr422RA: 248 case WIN_COLOR_DEPTH_YUV422RA: 249 if (planar) 250 *planar = true; 251 252 return true; 253 } 254 255 if (planar) 256 *planar = false; 257 258 return false; 259 } 260 261 static bool __drm_format_has_alpha(u32 format) 262 { 263 switch (format) { 264 case DRM_FORMAT_ARGB1555: 265 case DRM_FORMAT_RGBA5551: 266 case DRM_FORMAT_ABGR8888: 267 case DRM_FORMAT_ARGB8888: 268 return true; 269 } 270 271 return false; 272 } 273 274 static int tegra_plane_format_get_alpha(unsigned int opaque, 275 unsigned int *alpha) 276 { 277 if (tegra_plane_format_is_yuv(opaque, NULL)) { 278 *alpha = opaque; 279 return 0; 280 } 281 282 switch (opaque) { 283 case WIN_COLOR_DEPTH_B5G5R5X1: 284 *alpha = WIN_COLOR_DEPTH_B5G5R5A1; 285 return 0; 286 287 case WIN_COLOR_DEPTH_X1B5G5R5: 288 *alpha = WIN_COLOR_DEPTH_A1B5G5R5; 289 return 0; 290 291 case WIN_COLOR_DEPTH_R8G8B8X8: 292 *alpha = WIN_COLOR_DEPTH_R8G8B8A8; 293 return 0; 294 295 case WIN_COLOR_DEPTH_B8G8R8X8: 296 *alpha = WIN_COLOR_DEPTH_B8G8R8A8; 297 return 0; 298 299 case WIN_COLOR_DEPTH_B5G6R5: 300 *alpha = opaque; 301 return 0; 302 } 303 304 return -EINVAL; 305 } 306 307 /* 308 * This is applicable to Tegra20 and Tegra30 only where the opaque formats can 309 * be emulated using the alpha formats and alpha blending disabled. 310 */ 311 static int tegra_plane_setup_opacity(struct tegra_plane *tegra, 312 struct tegra_plane_state *state) 313 { 314 unsigned int format; 315 int err; 316 317 switch (state->format) { 318 case WIN_COLOR_DEPTH_B5G5R5A1: 319 case WIN_COLOR_DEPTH_A1B5G5R5: 320 case WIN_COLOR_DEPTH_R8G8B8A8: 321 case WIN_COLOR_DEPTH_B8G8R8A8: 322 state->opaque = false; 323 break; 324 325 default: 326 err = tegra_plane_format_get_alpha(state->format, &format); 327 if (err < 0) 328 return err; 329 330 state->format = format; 331 state->opaque = true; 332 break; 333 } 334 335 return 0; 336 } 337 338 static int tegra_plane_check_transparency(struct tegra_plane *tegra, 339 struct tegra_plane_state *state) 340 { 341 struct drm_plane_state *old, *plane_state; 342 struct drm_plane *plane; 343 344 old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base); 345 346 /* check if zpos / transparency changed */ 347 if (old->normalized_zpos == state->base.normalized_zpos && 348 to_tegra_plane_state(old)->opaque == state->opaque) 349 return 0; 350 351 /* include all sibling planes into this commit */ 352 drm_for_each_plane(plane, tegra->base.dev) { 353 struct tegra_plane *p = to_tegra_plane(plane); 354 355 /* skip this plane and planes on different CRTCs */ 356 if (p == tegra || p->dc != tegra->dc) 357 continue; 358 359 plane_state = drm_atomic_get_plane_state(state->base.state, 360 plane); 361 if (IS_ERR(plane_state)) 362 return PTR_ERR(plane_state); 363 } 364 365 return 1; 366 } 367 368 static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane, 369 struct tegra_plane *other) 370 { 371 unsigned int index = 0, i; 372 373 WARN_ON(plane == other); 374 375 for (i = 0; i < 3; i++) { 376 if (i == plane->index) 377 continue; 378 379 if (i == other->index) 380 break; 381 382 index++; 383 } 384 385 return index; 386 } 387 388 static void tegra_plane_update_transparency(struct tegra_plane *tegra, 389 struct tegra_plane_state *state) 390 { 391 struct drm_plane_state *new; 392 struct drm_plane *plane; 393 unsigned int i; 394 395 for_each_new_plane_in_state(state->base.state, plane, new, i) { 396 struct tegra_plane *p = to_tegra_plane(plane); 397 unsigned index; 398 399 /* skip this plane and planes on different CRTCs */ 400 if (p == tegra || p->dc != tegra->dc) 401 continue; 402 403 index = tegra_plane_get_overlap_index(tegra, p); 404 405 if (new->fb && __drm_format_has_alpha(new->fb->format->format)) 406 state->blending[index].alpha = true; 407 else 408 state->blending[index].alpha = false; 409 410 if (new->normalized_zpos > state->base.normalized_zpos) 411 state->blending[index].top = true; 412 else 413 state->blending[index].top = false; 414 415 /* 416 * Missing framebuffer means that plane is disabled, in this 417 * case mark B / C window as top to be able to differentiate 418 * windows indices order in regards to zPos for the middle 419 * window X / Y registers programming. 420 */ 421 if (!new->fb) 422 state->blending[index].top = (index == 1); 423 } 424 } 425 426 static int tegra_plane_setup_transparency(struct tegra_plane *tegra, 427 struct tegra_plane_state *state) 428 { 429 struct tegra_plane_state *tegra_state; 430 struct drm_plane_state *new; 431 struct drm_plane *plane; 432 int err; 433 434 /* 435 * If planes zpos / transparency changed, sibling planes blending 436 * state may require adjustment and in this case they will be included 437 * into this atom commit, otherwise blending state is unchanged. 438 */ 439 err = tegra_plane_check_transparency(tegra, state); 440 if (err <= 0) 441 return err; 442 443 /* 444 * All planes are now in the atomic state, walk them up and update 445 * transparency state for each plane. 446 */ 447 drm_for_each_plane(plane, tegra->base.dev) { 448 struct tegra_plane *p = to_tegra_plane(plane); 449 450 /* skip planes on different CRTCs */ 451 if (p->dc != tegra->dc) 452 continue; 453 454 new = drm_atomic_get_new_plane_state(state->base.state, plane); 455 tegra_state = to_tegra_plane_state(new); 456 457 /* 458 * There is no need to update blending state for the disabled 459 * plane. 460 */ 461 if (new->fb) 462 tegra_plane_update_transparency(p, tegra_state); 463 } 464 465 return 0; 466 } 467 468 int tegra_plane_setup_legacy_state(struct tegra_plane *tegra, 469 struct tegra_plane_state *state) 470 { 471 int err; 472 473 err = tegra_plane_setup_opacity(tegra, state); 474 if (err < 0) 475 return err; 476 477 err = tegra_plane_setup_transparency(tegra, state); 478 if (err < 0) 479 return err; 480 481 return 0; 482 } 483