1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. 3 */ 4 5 #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ 6 7 #include <uapi/drm/drm_fourcc.h> 8 #include <drm/drm_framebuffer.h> 9 10 #include "msm_media_info.h" 11 #include "dpu_kms.h" 12 #include "dpu_formats.h" 13 14 #define DPU_UBWC_PLANE_SIZE_ALIGNMENT 4096 15 16 #define DPU_MAX_IMG_WIDTH 0x3FFF 17 #define DPU_MAX_IMG_HEIGHT 0x3FFF 18 19 /* 20 * struct dpu_media_color_map - maps drm format to media format 21 * @format: DRM base pixel format 22 * @color: Media API color related to DRM format 23 */ 24 struct dpu_media_color_map { 25 uint32_t format; 26 uint32_t color; 27 }; 28 29 /* _dpu_get_v_h_subsample_rate - Get subsample rates for all formats we support 30 * Note: Not using the drm_format_*_subsampling since we have formats 31 */ 32 static void _dpu_get_v_h_subsample_rate( 33 enum mdp_chroma_samp_type chroma_sample, 34 uint32_t *v_sample, 35 uint32_t *h_sample) 36 { 37 if (!v_sample || !h_sample) 38 return; 39 40 switch (chroma_sample) { 41 case CHROMA_H2V1: 42 *v_sample = 1; 43 *h_sample = 2; 44 break; 45 case CHROMA_H1V2: 46 *v_sample = 2; 47 *h_sample = 1; 48 break; 49 case CHROMA_420: 50 *v_sample = 2; 51 *h_sample = 2; 52 break; 53 default: 54 *v_sample = 1; 55 *h_sample = 1; 56 break; 57 } 58 } 59 60 static int _dpu_format_get_media_color_ubwc(const struct msm_format *fmt) 61 { 62 static const struct dpu_media_color_map dpu_media_ubwc_map[] = { 63 {DRM_FORMAT_ABGR8888, COLOR_FMT_RGBA8888_UBWC}, 64 {DRM_FORMAT_ARGB8888, COLOR_FMT_RGBA8888_UBWC}, 65 {DRM_FORMAT_XBGR8888, COLOR_FMT_RGBA8888_UBWC}, 66 {DRM_FORMAT_XRGB8888, COLOR_FMT_RGBA8888_UBWC}, 67 {DRM_FORMAT_ABGR2101010, COLOR_FMT_RGBA1010102_UBWC}, 68 {DRM_FORMAT_ARGB2101010, COLOR_FMT_RGBA1010102_UBWC}, 69 {DRM_FORMAT_XRGB2101010, COLOR_FMT_RGBA1010102_UBWC}, 70 {DRM_FORMAT_XBGR2101010, COLOR_FMT_RGBA1010102_UBWC}, 71 {DRM_FORMAT_BGR565, COLOR_FMT_RGB565_UBWC}, 72 }; 73 int color_fmt = -1; 74 int i; 75 76 if (fmt->pixel_format == DRM_FORMAT_NV12 || 77 fmt->pixel_format == DRM_FORMAT_P010) { 78 if (MSM_FORMAT_IS_DX(fmt)) { 79 if (fmt->flags & MSM_FORMAT_FLAG_UNPACK_TIGHT) 80 color_fmt = COLOR_FMT_NV12_BPP10_UBWC; 81 else 82 color_fmt = COLOR_FMT_P010_UBWC; 83 } else 84 color_fmt = COLOR_FMT_NV12_UBWC; 85 return color_fmt; 86 } 87 88 for (i = 0; i < ARRAY_SIZE(dpu_media_ubwc_map); ++i) 89 if (fmt->pixel_format == dpu_media_ubwc_map[i].format) { 90 color_fmt = dpu_media_ubwc_map[i].color; 91 break; 92 } 93 return color_fmt; 94 } 95 96 static int _dpu_format_get_plane_sizes_ubwc( 97 const struct msm_format *fmt, 98 const uint32_t width, 99 const uint32_t height, 100 struct dpu_hw_fmt_layout *layout) 101 { 102 int i; 103 int color; 104 bool meta = MSM_FORMAT_IS_UBWC(fmt); 105 106 memset(layout, 0, sizeof(struct dpu_hw_fmt_layout)); 107 layout->format = fmt; 108 layout->width = width; 109 layout->height = height; 110 layout->num_planes = fmt->num_planes; 111 112 color = _dpu_format_get_media_color_ubwc(fmt); 113 if (color < 0) { 114 DRM_ERROR("UBWC format not supported for fmt: %p4cc\n", 115 &fmt->pixel_format); 116 return -EINVAL; 117 } 118 119 if (MSM_FORMAT_IS_YUV(layout->format)) { 120 uint32_t y_sclines, uv_sclines; 121 uint32_t y_meta_scanlines = 0; 122 uint32_t uv_meta_scanlines = 0; 123 124 layout->num_planes = 2; 125 layout->plane_pitch[0] = VENUS_Y_STRIDE(color, width); 126 y_sclines = VENUS_Y_SCANLINES(color, height); 127 layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * 128 y_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 129 130 layout->plane_pitch[1] = VENUS_UV_STRIDE(color, width); 131 uv_sclines = VENUS_UV_SCANLINES(color, height); 132 layout->plane_size[1] = MSM_MEDIA_ALIGN(layout->plane_pitch[1] * 133 uv_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 134 135 if (!meta) 136 goto done; 137 138 layout->num_planes += 2; 139 layout->plane_pitch[2] = VENUS_Y_META_STRIDE(color, width); 140 y_meta_scanlines = VENUS_Y_META_SCANLINES(color, height); 141 layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * 142 y_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 143 144 layout->plane_pitch[3] = VENUS_UV_META_STRIDE(color, width); 145 uv_meta_scanlines = VENUS_UV_META_SCANLINES(color, height); 146 layout->plane_size[3] = MSM_MEDIA_ALIGN(layout->plane_pitch[3] * 147 uv_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 148 149 } else { 150 uint32_t rgb_scanlines, rgb_meta_scanlines; 151 152 layout->num_planes = 1; 153 154 layout->plane_pitch[0] = VENUS_RGB_STRIDE(color, width); 155 rgb_scanlines = VENUS_RGB_SCANLINES(color, height); 156 layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * 157 rgb_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 158 159 if (!meta) 160 goto done; 161 layout->num_planes += 2; 162 layout->plane_pitch[2] = VENUS_RGB_META_STRIDE(color, width); 163 rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color, height); 164 layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * 165 rgb_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 166 } 167 168 done: 169 for (i = 0; i < DPU_MAX_PLANES; i++) 170 layout->total_size += layout->plane_size[i]; 171 172 return 0; 173 } 174 175 static int _dpu_format_get_plane_sizes_linear( 176 const struct msm_format *fmt, 177 const uint32_t width, 178 const uint32_t height, 179 struct dpu_hw_fmt_layout *layout, 180 const uint32_t *pitches) 181 { 182 int i; 183 184 memset(layout, 0, sizeof(struct dpu_hw_fmt_layout)); 185 layout->format = fmt; 186 layout->width = width; 187 layout->height = height; 188 layout->num_planes = fmt->num_planes; 189 190 /* Due to memset above, only need to set planes of interest */ 191 if (fmt->fetch_type == MDP_PLANE_INTERLEAVED) { 192 layout->num_planes = 1; 193 layout->plane_size[0] = width * height * layout->format->bpp; 194 layout->plane_pitch[0] = width * layout->format->bpp; 195 } else { 196 uint32_t v_subsample, h_subsample; 197 uint32_t chroma_samp; 198 uint32_t bpp = 1; 199 200 chroma_samp = fmt->chroma_sample; 201 _dpu_get_v_h_subsample_rate(chroma_samp, &v_subsample, 202 &h_subsample); 203 204 if (width % h_subsample || height % v_subsample) { 205 DRM_ERROR("mismatch in subsample vs dimensions\n"); 206 return -EINVAL; 207 } 208 209 if ((fmt->pixel_format == DRM_FORMAT_NV12) && 210 (MSM_FORMAT_IS_DX(fmt))) 211 bpp = 2; 212 layout->plane_pitch[0] = width * bpp; 213 layout->plane_pitch[1] = layout->plane_pitch[0] / h_subsample; 214 layout->plane_size[0] = layout->plane_pitch[0] * height; 215 layout->plane_size[1] = layout->plane_pitch[1] * 216 (height / v_subsample); 217 218 if (fmt->fetch_type == MDP_PLANE_PSEUDO_PLANAR) { 219 layout->num_planes = 2; 220 layout->plane_size[1] *= 2; 221 layout->plane_pitch[1] *= 2; 222 } else { 223 /* planar */ 224 layout->num_planes = 3; 225 layout->plane_size[2] = layout->plane_size[1]; 226 layout->plane_pitch[2] = layout->plane_pitch[1]; 227 } 228 } 229 230 /* 231 * linear format: allow user allocated pitches if they are greater than 232 * the requirement. 233 * ubwc format: pitch values are computed uniformly across 234 * all the components based on ubwc specifications. 235 */ 236 for (i = 0; i < layout->num_planes && i < DPU_MAX_PLANES; ++i) { 237 if (pitches && layout->plane_pitch[i] < pitches[i]) 238 layout->plane_pitch[i] = pitches[i]; 239 } 240 241 for (i = 0; i < DPU_MAX_PLANES; i++) 242 layout->total_size += layout->plane_size[i]; 243 244 return 0; 245 } 246 247 static int dpu_format_get_plane_sizes( 248 const struct msm_format *fmt, 249 const uint32_t w, 250 const uint32_t h, 251 struct dpu_hw_fmt_layout *layout, 252 const uint32_t *pitches) 253 { 254 if (!layout || !fmt) { 255 DRM_ERROR("invalid pointer\n"); 256 return -EINVAL; 257 } 258 259 if ((w > DPU_MAX_IMG_WIDTH) || (h > DPU_MAX_IMG_HEIGHT)) { 260 DRM_ERROR("image dimensions outside max range\n"); 261 return -ERANGE; 262 } 263 264 if (MSM_FORMAT_IS_UBWC(fmt) || MSM_FORMAT_IS_TILE(fmt)) 265 return _dpu_format_get_plane_sizes_ubwc(fmt, w, h, layout); 266 267 return _dpu_format_get_plane_sizes_linear(fmt, w, h, layout, pitches); 268 } 269 270 static int _dpu_format_populate_addrs_ubwc( 271 struct msm_gem_address_space *aspace, 272 struct drm_framebuffer *fb, 273 struct dpu_hw_fmt_layout *layout) 274 { 275 uint32_t base_addr = 0; 276 bool meta; 277 278 if (!fb || !layout) { 279 DRM_ERROR("invalid pointers\n"); 280 return -EINVAL; 281 } 282 283 if (aspace) 284 base_addr = msm_framebuffer_iova(fb, aspace, 0); 285 if (!base_addr) { 286 DRM_ERROR("failed to retrieve base addr\n"); 287 return -EFAULT; 288 } 289 290 meta = MSM_FORMAT_IS_UBWC(layout->format); 291 292 /* Per-format logic for verifying active planes */ 293 if (MSM_FORMAT_IS_YUV(layout->format)) { 294 /************************************************/ 295 /* UBWC ** */ 296 /* buffer ** DPU PLANE */ 297 /* format ** */ 298 /************************************************/ 299 /* ------------------- ** -------------------- */ 300 /* | Y meta | ** | Y bitstream | */ 301 /* | data | ** | plane | */ 302 /* ------------------- ** -------------------- */ 303 /* | Y bitstream | ** | CbCr bitstream | */ 304 /* | data | ** | plane | */ 305 /* ------------------- ** -------------------- */ 306 /* | Cbcr metadata | ** | Y meta | */ 307 /* | data | ** | plane | */ 308 /* ------------------- ** -------------------- */ 309 /* | CbCr bitstream | ** | CbCr meta | */ 310 /* | data | ** | plane | */ 311 /* ------------------- ** -------------------- */ 312 /************************************************/ 313 314 /* configure Y bitstream plane */ 315 layout->plane_addr[0] = base_addr + layout->plane_size[2]; 316 317 /* configure CbCr bitstream plane */ 318 layout->plane_addr[1] = base_addr + layout->plane_size[0] 319 + layout->plane_size[2] + layout->plane_size[3]; 320 321 if (!meta) 322 return 0; 323 324 /* configure Y metadata plane */ 325 layout->plane_addr[2] = base_addr; 326 327 /* configure CbCr metadata plane */ 328 layout->plane_addr[3] = base_addr + layout->plane_size[0] 329 + layout->plane_size[2]; 330 331 } else { 332 /************************************************/ 333 /* UBWC ** */ 334 /* buffer ** DPU PLANE */ 335 /* format ** */ 336 /************************************************/ 337 /* ------------------- ** -------------------- */ 338 /* | RGB meta | ** | RGB bitstream | */ 339 /* | data | ** | plane | */ 340 /* ------------------- ** -------------------- */ 341 /* | RGB bitstream | ** | NONE | */ 342 /* | data | ** | | */ 343 /* ------------------- ** -------------------- */ 344 /* ** | RGB meta | */ 345 /* ** | plane | */ 346 /* ** -------------------- */ 347 /************************************************/ 348 349 layout->plane_addr[0] = base_addr + layout->plane_size[2]; 350 layout->plane_addr[1] = 0; 351 352 if (!meta) 353 return 0; 354 355 layout->plane_addr[2] = base_addr; 356 layout->plane_addr[3] = 0; 357 } 358 return 0; 359 } 360 361 static int _dpu_format_populate_addrs_linear( 362 struct msm_gem_address_space *aspace, 363 struct drm_framebuffer *fb, 364 struct dpu_hw_fmt_layout *layout) 365 { 366 unsigned int i; 367 368 /* Can now check the pitches given vs pitches expected */ 369 for (i = 0; i < layout->num_planes; ++i) { 370 if (layout->plane_pitch[i] > fb->pitches[i]) { 371 DRM_ERROR("plane %u expected pitch %u, fb %u\n", 372 i, layout->plane_pitch[i], fb->pitches[i]); 373 return -EINVAL; 374 } 375 } 376 377 /* Populate addresses for simple formats here */ 378 for (i = 0; i < layout->num_planes; ++i) { 379 if (aspace) 380 layout->plane_addr[i] = 381 msm_framebuffer_iova(fb, aspace, i); 382 if (!layout->plane_addr[i]) { 383 DRM_ERROR("failed to retrieve base addr\n"); 384 return -EFAULT; 385 } 386 } 387 388 return 0; 389 } 390 391 int dpu_format_populate_layout( 392 struct msm_gem_address_space *aspace, 393 struct drm_framebuffer *fb, 394 struct dpu_hw_fmt_layout *layout) 395 { 396 int ret; 397 398 if (!fb || !layout) { 399 DRM_ERROR("invalid arguments\n"); 400 return -EINVAL; 401 } 402 403 if ((fb->width > DPU_MAX_IMG_WIDTH) || 404 (fb->height > DPU_MAX_IMG_HEIGHT)) { 405 DRM_ERROR("image dimensions outside max range\n"); 406 return -ERANGE; 407 } 408 409 layout->format = msm_framebuffer_format(fb); 410 411 /* Populate the plane sizes etc via get_format */ 412 ret = dpu_format_get_plane_sizes(layout->format, fb->width, fb->height, 413 layout, fb->pitches); 414 if (ret) 415 return ret; 416 417 /* Populate the addresses given the fb */ 418 if (MSM_FORMAT_IS_UBWC(layout->format) || 419 MSM_FORMAT_IS_TILE(layout->format)) 420 ret = _dpu_format_populate_addrs_ubwc(aspace, fb, layout); 421 else 422 ret = _dpu_format_populate_addrs_linear(aspace, fb, layout); 423 424 return ret; 425 } 426 427 int dpu_format_check_modified_format( 428 const struct msm_kms *kms, 429 const struct msm_format *fmt, 430 const struct drm_mode_fb_cmd2 *cmd, 431 struct drm_gem_object **bos) 432 { 433 const struct drm_format_info *info; 434 struct dpu_hw_fmt_layout layout; 435 uint32_t bos_total_size = 0; 436 int ret, i; 437 438 if (!fmt || !cmd || !bos) { 439 DRM_ERROR("invalid arguments\n"); 440 return -EINVAL; 441 } 442 443 info = drm_format_info(fmt->pixel_format); 444 if (!info) 445 return -EINVAL; 446 447 ret = dpu_format_get_plane_sizes(fmt, cmd->width, cmd->height, 448 &layout, cmd->pitches); 449 if (ret) 450 return ret; 451 452 for (i = 0; i < info->num_planes; i++) { 453 if (!bos[i]) { 454 DRM_ERROR("invalid handle for plane %d\n", i); 455 return -EINVAL; 456 } 457 if ((i == 0) || (bos[i] != bos[0])) 458 bos_total_size += bos[i]->size; 459 } 460 461 if (bos_total_size < layout.total_size) { 462 DRM_ERROR("buffers total size too small %u expected %u\n", 463 bos_total_size, layout.total_size); 464 return -EINVAL; 465 } 466 467 return 0; 468 } 469