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