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 /* 17 * struct dpu_media_color_map - maps drm format to media format 18 * @format: DRM base pixel format 19 * @color: Media API color related to DRM format 20 */ 21 struct dpu_media_color_map { 22 uint32_t format; 23 uint32_t color; 24 }; 25 26 /* _dpu_get_v_h_subsample_rate - Get subsample rates for all formats we support 27 * Note: Not using the drm_format_*_subsampling since we have formats 28 */ 29 static void _dpu_get_v_h_subsample_rate( 30 enum mdp_chroma_samp_type chroma_sample, 31 uint32_t *v_sample, 32 uint32_t *h_sample) 33 { 34 if (!v_sample || !h_sample) 35 return; 36 37 switch (chroma_sample) { 38 case CHROMA_H2V1: 39 *v_sample = 1; 40 *h_sample = 2; 41 break; 42 case CHROMA_H1V2: 43 *v_sample = 2; 44 *h_sample = 1; 45 break; 46 case CHROMA_420: 47 *v_sample = 2; 48 *h_sample = 2; 49 break; 50 default: 51 *v_sample = 1; 52 *h_sample = 1; 53 break; 54 } 55 } 56 57 static int _dpu_format_get_media_color_ubwc(const struct msm_format *fmt) 58 { 59 static const struct dpu_media_color_map dpu_media_ubwc_map[] = { 60 {DRM_FORMAT_ABGR8888, COLOR_FMT_RGBA8888_UBWC}, 61 {DRM_FORMAT_ARGB8888, COLOR_FMT_RGBA8888_UBWC}, 62 {DRM_FORMAT_XBGR8888, COLOR_FMT_RGBA8888_UBWC}, 63 {DRM_FORMAT_XRGB8888, COLOR_FMT_RGBA8888_UBWC}, 64 {DRM_FORMAT_ABGR2101010, COLOR_FMT_RGBA1010102_UBWC}, 65 {DRM_FORMAT_ARGB2101010, COLOR_FMT_RGBA1010102_UBWC}, 66 {DRM_FORMAT_XRGB2101010, COLOR_FMT_RGBA1010102_UBWC}, 67 {DRM_FORMAT_XBGR2101010, COLOR_FMT_RGBA1010102_UBWC}, 68 {DRM_FORMAT_BGR565, COLOR_FMT_RGB565_UBWC}, 69 }; 70 int color_fmt = -1; 71 int i; 72 73 if (fmt->pixel_format == DRM_FORMAT_NV12 || 74 fmt->pixel_format == DRM_FORMAT_P010) { 75 if (MSM_FORMAT_IS_DX(fmt)) { 76 if (fmt->flags & MSM_FORMAT_FLAG_UNPACK_TIGHT) 77 color_fmt = COLOR_FMT_NV12_BPP10_UBWC; 78 else 79 color_fmt = COLOR_FMT_P010_UBWC; 80 } else 81 color_fmt = COLOR_FMT_NV12_UBWC; 82 return color_fmt; 83 } 84 85 for (i = 0; i < ARRAY_SIZE(dpu_media_ubwc_map); ++i) 86 if (fmt->pixel_format == dpu_media_ubwc_map[i].format) { 87 color_fmt = dpu_media_ubwc_map[i].color; 88 break; 89 } 90 return color_fmt; 91 } 92 93 static int _dpu_format_populate_plane_sizes_ubwc( 94 const struct msm_format *fmt, 95 struct drm_framebuffer *fb, 96 struct dpu_hw_fmt_layout *layout) 97 { 98 int i; 99 int color; 100 bool meta = MSM_FORMAT_IS_UBWC(fmt); 101 102 memset(layout, 0, sizeof(struct dpu_hw_fmt_layout)); 103 layout->width = fb->width; 104 layout->height = fb->height; 105 layout->num_planes = fmt->num_planes; 106 107 color = _dpu_format_get_media_color_ubwc(fmt); 108 if (color < 0) { 109 DRM_ERROR("UBWC format not supported for fmt: %p4cc\n", 110 &fmt->pixel_format); 111 return -EINVAL; 112 } 113 114 if (MSM_FORMAT_IS_YUV(fmt)) { 115 uint32_t y_sclines, uv_sclines; 116 uint32_t y_meta_scanlines = 0; 117 uint32_t uv_meta_scanlines = 0; 118 119 layout->num_planes = 2; 120 layout->plane_pitch[0] = VENUS_Y_STRIDE(color, fb->width); 121 y_sclines = VENUS_Y_SCANLINES(color, fb->height); 122 layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * 123 y_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 124 125 layout->plane_pitch[1] = VENUS_UV_STRIDE(color, fb->width); 126 uv_sclines = VENUS_UV_SCANLINES(color, fb->height); 127 layout->plane_size[1] = MSM_MEDIA_ALIGN(layout->plane_pitch[1] * 128 uv_sclines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 129 130 if (!meta) 131 goto done; 132 133 layout->num_planes += 2; 134 layout->plane_pitch[2] = VENUS_Y_META_STRIDE(color, fb->width); 135 y_meta_scanlines = VENUS_Y_META_SCANLINES(color, fb->height); 136 layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * 137 y_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 138 139 layout->plane_pitch[3] = VENUS_UV_META_STRIDE(color, fb->width); 140 uv_meta_scanlines = VENUS_UV_META_SCANLINES(color, fb->height); 141 layout->plane_size[3] = MSM_MEDIA_ALIGN(layout->plane_pitch[3] * 142 uv_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 143 144 } else { 145 uint32_t rgb_scanlines, rgb_meta_scanlines; 146 147 layout->num_planes = 1; 148 149 layout->plane_pitch[0] = VENUS_RGB_STRIDE(color, fb->width); 150 rgb_scanlines = VENUS_RGB_SCANLINES(color, fb->height); 151 layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * 152 rgb_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 153 154 if (!meta) 155 goto done; 156 layout->num_planes += 2; 157 layout->plane_pitch[2] = VENUS_RGB_META_STRIDE(color, fb->width); 158 rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color, fb->height); 159 layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * 160 rgb_meta_scanlines, DPU_UBWC_PLANE_SIZE_ALIGNMENT); 161 } 162 163 done: 164 for (i = 0; i < DPU_MAX_PLANES; i++) 165 layout->total_size += layout->plane_size[i]; 166 167 return 0; 168 } 169 170 static int _dpu_format_populate_plane_sizes_linear( 171 const struct msm_format *fmt, 172 struct drm_framebuffer *fb, 173 struct dpu_hw_fmt_layout *layout) 174 { 175 int i; 176 177 memset(layout, 0, sizeof(struct dpu_hw_fmt_layout)); 178 layout->width = fb->width; 179 layout->height = fb->height; 180 layout->num_planes = fmt->num_planes; 181 182 /* Due to memset above, only need to set planes of interest */ 183 if (fmt->fetch_type == MDP_PLANE_INTERLEAVED) { 184 layout->num_planes = 1; 185 layout->plane_size[0] = fb->width * fb->height * fmt->bpp; 186 layout->plane_pitch[0] = fb->width * fmt->bpp; 187 } else { 188 uint32_t v_subsample, h_subsample; 189 uint32_t chroma_samp; 190 uint32_t bpp = 1; 191 192 chroma_samp = fmt->chroma_sample; 193 _dpu_get_v_h_subsample_rate(chroma_samp, &v_subsample, 194 &h_subsample); 195 196 if (fb->width % h_subsample || fb->height % v_subsample) { 197 DRM_ERROR("mismatch in subsample vs dimensions\n"); 198 return -EINVAL; 199 } 200 201 if ((fmt->pixel_format == DRM_FORMAT_NV12) && 202 (MSM_FORMAT_IS_DX(fmt))) 203 bpp = 2; 204 layout->plane_pitch[0] = fb->width * bpp; 205 layout->plane_pitch[1] = layout->plane_pitch[0] / h_subsample; 206 layout->plane_size[0] = layout->plane_pitch[0] * fb->height; 207 layout->plane_size[1] = layout->plane_pitch[1] * 208 (fb->height / v_subsample); 209 210 if (fmt->fetch_type == MDP_PLANE_PSEUDO_PLANAR) { 211 layout->num_planes = 2; 212 layout->plane_size[1] *= 2; 213 layout->plane_pitch[1] *= 2; 214 } else { 215 /* planar */ 216 layout->num_planes = 3; 217 layout->plane_size[2] = layout->plane_size[1]; 218 layout->plane_pitch[2] = layout->plane_pitch[1]; 219 } 220 } 221 222 /* 223 * linear format: allow user allocated pitches if they are greater than 224 * the requirement. 225 * ubwc format: pitch values are computed uniformly across 226 * all the components based on ubwc specifications. 227 */ 228 for (i = 0; i < layout->num_planes && i < DPU_MAX_PLANES; ++i) { 229 if (layout->plane_pitch[i] <= fb->pitches[i]) { 230 layout->plane_pitch[i] = fb->pitches[i]; 231 } else { 232 DRM_DEBUG("plane %u expected pitch %u, fb %u\n", 233 i, layout->plane_pitch[i], fb->pitches[i]); 234 return -EINVAL; 235 } 236 } 237 238 for (i = 0; i < DPU_MAX_PLANES; i++) 239 layout->total_size += layout->plane_size[i]; 240 241 return 0; 242 } 243 244 /** 245 * dpu_format_populate_plane_sizes - populate non-address part of the layout based on 246 * fb, and format found in the fb 247 * @fb: framebuffer pointer 248 * @layout: format layout structure to populate 249 * 250 * Return: error code on failure or 0 if new addresses were populated 251 */ 252 int dpu_format_populate_plane_sizes( 253 struct drm_framebuffer *fb, 254 struct dpu_hw_fmt_layout *layout) 255 { 256 const struct msm_format *fmt; 257 258 if (!layout || !fb) { 259 DRM_ERROR("invalid pointer\n"); 260 return -EINVAL; 261 } 262 263 if (fb->width > DPU_MAX_IMG_WIDTH || 264 fb->height > DPU_MAX_IMG_HEIGHT) { 265 DRM_ERROR("image dimensions outside max range\n"); 266 return -ERANGE; 267 } 268 269 fmt = msm_framebuffer_format(fb); 270 271 if (MSM_FORMAT_IS_UBWC(fmt) || MSM_FORMAT_IS_TILE(fmt)) 272 return _dpu_format_populate_plane_sizes_ubwc(fmt, fb, layout); 273 274 return _dpu_format_populate_plane_sizes_linear(fmt, fb, layout); 275 } 276 277 static void _dpu_format_populate_addrs_ubwc(struct msm_gem_address_space *aspace, 278 struct drm_framebuffer *fb, 279 struct dpu_hw_fmt_layout *layout) 280 { 281 const struct msm_format *fmt; 282 uint32_t base_addr = 0; 283 bool meta; 284 285 base_addr = msm_framebuffer_iova(fb, aspace, 0); 286 287 fmt = msm_framebuffer_format(fb); 288 meta = MSM_FORMAT_IS_UBWC(fmt); 289 290 /* Per-format logic for verifying active planes */ 291 if (MSM_FORMAT_IS_YUV(fmt)) { 292 /************************************************/ 293 /* UBWC ** */ 294 /* buffer ** DPU PLANE */ 295 /* format ** */ 296 /************************************************/ 297 /* ------------------- ** -------------------- */ 298 /* | Y meta | ** | Y bitstream | */ 299 /* | data | ** | plane | */ 300 /* ------------------- ** -------------------- */ 301 /* | Y bitstream | ** | CbCr bitstream | */ 302 /* | data | ** | plane | */ 303 /* ------------------- ** -------------------- */ 304 /* | Cbcr metadata | ** | Y meta | */ 305 /* | data | ** | plane | */ 306 /* ------------------- ** -------------------- */ 307 /* | CbCr bitstream | ** | CbCr meta | */ 308 /* | data | ** | plane | */ 309 /* ------------------- ** -------------------- */ 310 /************************************************/ 311 312 /* configure Y bitstream plane */ 313 layout->plane_addr[0] = base_addr + layout->plane_size[2]; 314 315 /* configure CbCr bitstream plane */ 316 layout->plane_addr[1] = base_addr + layout->plane_size[0] 317 + layout->plane_size[2] + layout->plane_size[3]; 318 319 if (!meta) 320 return; 321 322 /* configure Y metadata plane */ 323 layout->plane_addr[2] = base_addr; 324 325 /* configure CbCr metadata plane */ 326 layout->plane_addr[3] = base_addr + layout->plane_size[0] 327 + layout->plane_size[2]; 328 329 } else { 330 /************************************************/ 331 /* UBWC ** */ 332 /* buffer ** DPU PLANE */ 333 /* format ** */ 334 /************************************************/ 335 /* ------------------- ** -------------------- */ 336 /* | RGB meta | ** | RGB bitstream | */ 337 /* | data | ** | plane | */ 338 /* ------------------- ** -------------------- */ 339 /* | RGB bitstream | ** | NONE | */ 340 /* | data | ** | | */ 341 /* ------------------- ** -------------------- */ 342 /* ** | RGB meta | */ 343 /* ** | plane | */ 344 /* ** -------------------- */ 345 /************************************************/ 346 347 layout->plane_addr[0] = base_addr + layout->plane_size[2]; 348 layout->plane_addr[1] = 0; 349 350 if (!meta) 351 return; 352 353 layout->plane_addr[2] = base_addr; 354 layout->plane_addr[3] = 0; 355 } 356 } 357 358 static void _dpu_format_populate_addrs_linear(struct msm_gem_address_space *aspace, 359 struct drm_framebuffer *fb, 360 struct dpu_hw_fmt_layout *layout) 361 { 362 unsigned int i; 363 364 /* Populate addresses for simple formats here */ 365 for (i = 0; i < layout->num_planes; ++i) 366 layout->plane_addr[i] = msm_framebuffer_iova(fb, aspace, i); 367 } 368 369 /** 370 * dpu_format_populate_addrs - populate buffer addresses based on 371 * mmu, fb, and format found in the fb 372 * @aspace: address space pointer 373 * @fb: framebuffer pointer 374 * @layout: format layout structure to populate 375 */ 376 void dpu_format_populate_addrs(struct msm_gem_address_space *aspace, 377 struct drm_framebuffer *fb, 378 struct dpu_hw_fmt_layout *layout) 379 { 380 const struct msm_format *fmt; 381 382 fmt = msm_framebuffer_format(fb); 383 384 /* Populate the addresses given the fb */ 385 if (MSM_FORMAT_IS_UBWC(fmt) || 386 MSM_FORMAT_IS_TILE(fmt)) 387 _dpu_format_populate_addrs_ubwc(aspace, fb, layout); 388 else 389 _dpu_format_populate_addrs_linear(aspace, fb, layout); 390 } 391