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 struct drm_framebuffer *fb, 99 struct dpu_hw_fmt_layout *layout) 100 { 101 int i; 102 int color; 103 bool meta = MSM_FORMAT_IS_UBWC(fmt); 104 105 memset(layout, 0, sizeof(struct dpu_hw_fmt_layout)); 106 layout->width = fb->width; 107 layout->height = fb->height; 108 layout->num_planes = fmt->num_planes; 109 110 color = _dpu_format_get_media_color_ubwc(fmt); 111 if (color < 0) { 112 DRM_ERROR("UBWC format not supported for fmt: %p4cc\n", 113 &fmt->pixel_format); 114 return -EINVAL; 115 } 116 117 if (MSM_FORMAT_IS_YUV(fmt)) { 118 uint32_t y_sclines, uv_sclines; 119 uint32_t y_meta_scanlines = 0; 120 uint32_t uv_meta_scanlines = 0; 121 122 layout->num_planes = 2; 123 layout->plane_pitch[0] = VENUS_Y_STRIDE(color, fb->width); 124 y_sclines = VENUS_Y_SCANLINES(color, fb->height); 125 layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * 126 y_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 127 128 layout->plane_pitch[1] = VENUS_UV_STRIDE(color, fb->width); 129 uv_sclines = VENUS_UV_SCANLINES(color, fb->height); 130 layout->plane_size[1] = MSM_MEDIA_ALIGN(layout->plane_pitch[1] * 131 uv_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 132 133 if (!meta) 134 goto done; 135 136 layout->num_planes += 2; 137 layout->plane_pitch[2] = VENUS_Y_META_STRIDE(color, fb->width); 138 y_meta_scanlines = VENUS_Y_META_SCANLINES(color, fb->height); 139 layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * 140 y_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 141 142 layout->plane_pitch[3] = VENUS_UV_META_STRIDE(color, fb->width); 143 uv_meta_scanlines = VENUS_UV_META_SCANLINES(color, fb->height); 144 layout->plane_size[3] = MSM_MEDIA_ALIGN(layout->plane_pitch[3] * 145 uv_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 146 147 } else { 148 uint32_t rgb_scanlines, rgb_meta_scanlines; 149 150 layout->num_planes = 1; 151 152 layout->plane_pitch[0] = VENUS_RGB_STRIDE(color, fb->width); 153 rgb_scanlines = VENUS_RGB_SCANLINES(color, fb->height); 154 layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * 155 rgb_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 156 157 if (!meta) 158 goto done; 159 layout->num_planes += 2; 160 layout->plane_pitch[2] = VENUS_RGB_META_STRIDE(color, fb->width); 161 rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color, fb->height); 162 layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * 163 rgb_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 164 } 165 166 done: 167 for (i = 0; i < DPU_MAX_PLANES; i++) 168 layout->total_size += layout->plane_size[i]; 169 170 return 0; 171 } 172 173 static int _dpu_format_get_plane_sizes_linear( 174 const struct msm_format *fmt, 175 struct drm_framebuffer *fb, 176 struct dpu_hw_fmt_layout *layout) 177 { 178 int i; 179 180 memset(layout, 0, sizeof(struct dpu_hw_fmt_layout)); 181 layout->width = fb->width; 182 layout->height = fb->height; 183 layout->num_planes = fmt->num_planes; 184 185 /* Due to memset above, only need to set planes of interest */ 186 if (fmt->fetch_type == MDP_PLANE_INTERLEAVED) { 187 layout->num_planes = 1; 188 layout->plane_size[0] = fb->width * fb->height * fmt->bpp; 189 layout->plane_pitch[0] = fb->width * fmt->bpp; 190 } else { 191 uint32_t v_subsample, h_subsample; 192 uint32_t chroma_samp; 193 uint32_t bpp = 1; 194 195 chroma_samp = fmt->chroma_sample; 196 _dpu_get_v_h_subsample_rate(chroma_samp, &v_subsample, 197 &h_subsample); 198 199 if (fb->width % h_subsample || fb->height % v_subsample) { 200 DRM_ERROR("mismatch in subsample vs dimensions\n"); 201 return -EINVAL; 202 } 203 204 if ((fmt->pixel_format == DRM_FORMAT_NV12) && 205 (MSM_FORMAT_IS_DX(fmt))) 206 bpp = 2; 207 layout->plane_pitch[0] = fb->width * bpp; 208 layout->plane_pitch[1] = layout->plane_pitch[0] / h_subsample; 209 layout->plane_size[0] = layout->plane_pitch[0] * fb->height; 210 layout->plane_size[1] = layout->plane_pitch[1] * 211 (fb->height / v_subsample); 212 213 if (fmt->fetch_type == MDP_PLANE_PSEUDO_PLANAR) { 214 layout->num_planes = 2; 215 layout->plane_size[1] *= 2; 216 layout->plane_pitch[1] *= 2; 217 } else { 218 /* planar */ 219 layout->num_planes = 3; 220 layout->plane_size[2] = layout->plane_size[1]; 221 layout->plane_pitch[2] = layout->plane_pitch[1]; 222 } 223 } 224 225 /* 226 * linear format: allow user allocated pitches if they are greater than 227 * the requirement. 228 * ubwc format: pitch values are computed uniformly across 229 * all the components based on ubwc specifications. 230 */ 231 for (i = 0; i < layout->num_planes && i < DPU_MAX_PLANES; ++i) { 232 if (layout->plane_pitch[i] < fb->pitches[i]) 233 layout->plane_pitch[i] = fb->pitches[i]; 234 } 235 236 for (i = 0; i < DPU_MAX_PLANES; i++) 237 layout->total_size += layout->plane_size[i]; 238 239 return 0; 240 } 241 242 static int dpu_format_get_plane_sizes( 243 const struct msm_format *fmt, 244 struct drm_framebuffer *fb, 245 struct dpu_hw_fmt_layout *layout) 246 { 247 if (!layout || !fmt) { 248 DRM_ERROR("invalid pointer\n"); 249 return -EINVAL; 250 } 251 252 if (fb->width > DPU_MAX_IMG_WIDTH || 253 fb->height > DPU_MAX_IMG_HEIGHT) { 254 DRM_ERROR("image dimensions outside max range\n"); 255 return -ERANGE; 256 } 257 258 if (MSM_FORMAT_IS_UBWC(fmt) || MSM_FORMAT_IS_TILE(fmt)) 259 return _dpu_format_get_plane_sizes_ubwc(fmt, fb, layout); 260 261 return _dpu_format_get_plane_sizes_linear(fmt, fb, layout); 262 } 263 264 static int _dpu_format_populate_addrs_ubwc( 265 struct msm_gem_address_space *aspace, 266 struct drm_framebuffer *fb, 267 struct dpu_hw_fmt_layout *layout) 268 { 269 const struct msm_format *fmt; 270 uint32_t base_addr = 0; 271 bool meta; 272 273 if (!fb || !layout) { 274 DRM_ERROR("invalid pointers\n"); 275 return -EINVAL; 276 } 277 278 base_addr = msm_framebuffer_iova(fb, aspace, 0); 279 if (!base_addr) { 280 DRM_ERROR("failed to retrieve base addr\n"); 281 return -EFAULT; 282 } 283 284 fmt = msm_framebuffer_format(fb); 285 meta = MSM_FORMAT_IS_UBWC(fmt); 286 287 /* Per-format logic for verifying active planes */ 288 if (MSM_FORMAT_IS_YUV(fmt)) { 289 /************************************************/ 290 /* UBWC ** */ 291 /* buffer ** DPU PLANE */ 292 /* format ** */ 293 /************************************************/ 294 /* ------------------- ** -------------------- */ 295 /* | Y meta | ** | Y bitstream | */ 296 /* | data | ** | plane | */ 297 /* ------------------- ** -------------------- */ 298 /* | Y bitstream | ** | CbCr bitstream | */ 299 /* | data | ** | plane | */ 300 /* ------------------- ** -------------------- */ 301 /* | Cbcr metadata | ** | Y meta | */ 302 /* | data | ** | plane | */ 303 /* ------------------- ** -------------------- */ 304 /* | CbCr bitstream | ** | CbCr meta | */ 305 /* | data | ** | plane | */ 306 /* ------------------- ** -------------------- */ 307 /************************************************/ 308 309 /* configure Y bitstream plane */ 310 layout->plane_addr[0] = base_addr + layout->plane_size[2]; 311 312 /* configure CbCr bitstream plane */ 313 layout->plane_addr[1] = base_addr + layout->plane_size[0] 314 + layout->plane_size[2] + layout->plane_size[3]; 315 316 if (!meta) 317 return 0; 318 319 /* configure Y metadata plane */ 320 layout->plane_addr[2] = base_addr; 321 322 /* configure CbCr metadata plane */ 323 layout->plane_addr[3] = base_addr + layout->plane_size[0] 324 + layout->plane_size[2]; 325 326 } else { 327 /************************************************/ 328 /* UBWC ** */ 329 /* buffer ** DPU PLANE */ 330 /* format ** */ 331 /************************************************/ 332 /* ------------------- ** -------------------- */ 333 /* | RGB meta | ** | RGB bitstream | */ 334 /* | data | ** | plane | */ 335 /* ------------------- ** -------------------- */ 336 /* | RGB bitstream | ** | NONE | */ 337 /* | data | ** | | */ 338 /* ------------------- ** -------------------- */ 339 /* ** | RGB meta | */ 340 /* ** | plane | */ 341 /* ** -------------------- */ 342 /************************************************/ 343 344 layout->plane_addr[0] = base_addr + layout->plane_size[2]; 345 layout->plane_addr[1] = 0; 346 347 if (!meta) 348 return 0; 349 350 layout->plane_addr[2] = base_addr; 351 layout->plane_addr[3] = 0; 352 } 353 return 0; 354 } 355 356 static int _dpu_format_populate_addrs_linear( 357 struct msm_gem_address_space *aspace, 358 struct drm_framebuffer *fb, 359 struct dpu_hw_fmt_layout *layout) 360 { 361 unsigned int i; 362 363 /* Can now check the pitches given vs pitches expected */ 364 for (i = 0; i < layout->num_planes; ++i) { 365 if (layout->plane_pitch[i] > fb->pitches[i]) { 366 DRM_ERROR("plane %u expected pitch %u, fb %u\n", 367 i, layout->plane_pitch[i], fb->pitches[i]); 368 return -EINVAL; 369 } 370 } 371 372 /* Populate addresses for simple formats here */ 373 for (i = 0; i < layout->num_planes; ++i) { 374 layout->plane_addr[i] = msm_framebuffer_iova(fb, aspace, i); 375 if (!layout->plane_addr[i]) { 376 DRM_ERROR("failed to retrieve base addr\n"); 377 return -EFAULT; 378 } 379 } 380 381 return 0; 382 } 383 384 int dpu_format_populate_layout( 385 struct msm_gem_address_space *aspace, 386 struct drm_framebuffer *fb, 387 struct dpu_hw_fmt_layout *layout) 388 { 389 const struct msm_format *fmt; 390 int ret; 391 392 if (!fb || !layout) { 393 DRM_ERROR("invalid arguments\n"); 394 return -EINVAL; 395 } 396 397 if ((fb->width > DPU_MAX_IMG_WIDTH) || 398 (fb->height > DPU_MAX_IMG_HEIGHT)) { 399 DRM_ERROR("image dimensions outside max range\n"); 400 return -ERANGE; 401 } 402 403 fmt = msm_framebuffer_format(fb); 404 405 /* Populate the plane sizes etc via get_format */ 406 ret = dpu_format_get_plane_sizes(fmt, fb, layout); 407 if (ret) 408 return ret; 409 410 /* Populate the addresses given the fb */ 411 if (MSM_FORMAT_IS_UBWC(fmt) || 412 MSM_FORMAT_IS_TILE(fmt)) 413 ret = _dpu_format_populate_addrs_ubwc(aspace, fb, layout); 414 else 415 ret = _dpu_format_populate_addrs_linear(aspace, fb, layout); 416 417 return ret; 418 } 419