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_populate_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_populate_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 } else { 235 DRM_DEBUG("plane %u expected pitch %u, fb %u\n", 236 i, layout->plane_pitch[i], fb->pitches[i]); 237 return -EINVAL; 238 } 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 /* 248 * dpu_format_populate_addrs - populate non-address part of the layout based on 249 * fb, and format found in the fb 250 * @fb: framebuffer pointer 251 * @layout: format layout structure to populate 252 * 253 * Return: error code on failure or 0 if new addresses were populated 254 */ 255 int dpu_format_populate_plane_sizes( 256 struct drm_framebuffer *fb, 257 struct dpu_hw_fmt_layout *layout) 258 { 259 const struct msm_format *fmt; 260 261 if (!layout || !fb) { 262 DRM_ERROR("invalid pointer\n"); 263 return -EINVAL; 264 } 265 266 if (fb->width > DPU_MAX_IMG_WIDTH || 267 fb->height > DPU_MAX_IMG_HEIGHT) { 268 DRM_ERROR("image dimensions outside max range\n"); 269 return -ERANGE; 270 } 271 272 fmt = msm_framebuffer_format(fb); 273 274 if (MSM_FORMAT_IS_UBWC(fmt) || MSM_FORMAT_IS_TILE(fmt)) 275 return _dpu_format_populate_plane_sizes_ubwc(fmt, fb, layout); 276 277 return _dpu_format_populate_plane_sizes_linear(fmt, fb, layout); 278 } 279 280 static void _dpu_format_populate_addrs_ubwc(struct msm_gem_address_space *aspace, 281 struct drm_framebuffer *fb, 282 struct dpu_hw_fmt_layout *layout) 283 { 284 const struct msm_format *fmt; 285 uint32_t base_addr = 0; 286 bool meta; 287 288 base_addr = msm_framebuffer_iova(fb, aspace, 0); 289 290 fmt = msm_framebuffer_format(fb); 291 meta = MSM_FORMAT_IS_UBWC(fmt); 292 293 /* Per-format logic for verifying active planes */ 294 if (MSM_FORMAT_IS_YUV(fmt)) { 295 /************************************************/ 296 /* UBWC ** */ 297 /* buffer ** DPU PLANE */ 298 /* format ** */ 299 /************************************************/ 300 /* ------------------- ** -------------------- */ 301 /* | Y meta | ** | Y bitstream | */ 302 /* | data | ** | plane | */ 303 /* ------------------- ** -------------------- */ 304 /* | Y bitstream | ** | CbCr bitstream | */ 305 /* | data | ** | plane | */ 306 /* ------------------- ** -------------------- */ 307 /* | Cbcr metadata | ** | Y meta | */ 308 /* | data | ** | plane | */ 309 /* ------------------- ** -------------------- */ 310 /* | CbCr bitstream | ** | CbCr meta | */ 311 /* | data | ** | plane | */ 312 /* ------------------- ** -------------------- */ 313 /************************************************/ 314 315 /* configure Y bitstream plane */ 316 layout->plane_addr[0] = base_addr + layout->plane_size[2]; 317 318 /* configure CbCr bitstream plane */ 319 layout->plane_addr[1] = base_addr + layout->plane_size[0] 320 + layout->plane_size[2] + layout->plane_size[3]; 321 322 if (!meta) 323 return; 324 325 /* configure Y metadata plane */ 326 layout->plane_addr[2] = base_addr; 327 328 /* configure CbCr metadata plane */ 329 layout->plane_addr[3] = base_addr + layout->plane_size[0] 330 + layout->plane_size[2]; 331 332 } else { 333 /************************************************/ 334 /* UBWC ** */ 335 /* buffer ** DPU PLANE */ 336 /* format ** */ 337 /************************************************/ 338 /* ------------------- ** -------------------- */ 339 /* | RGB meta | ** | RGB bitstream | */ 340 /* | data | ** | plane | */ 341 /* ------------------- ** -------------------- */ 342 /* | RGB bitstream | ** | NONE | */ 343 /* | data | ** | | */ 344 /* ------------------- ** -------------------- */ 345 /* ** | RGB meta | */ 346 /* ** | plane | */ 347 /* ** -------------------- */ 348 /************************************************/ 349 350 layout->plane_addr[0] = base_addr + layout->plane_size[2]; 351 layout->plane_addr[1] = 0; 352 353 if (!meta) 354 return; 355 356 layout->plane_addr[2] = base_addr; 357 layout->plane_addr[3] = 0; 358 } 359 } 360 361 static void _dpu_format_populate_addrs_linear(struct msm_gem_address_space *aspace, 362 struct drm_framebuffer *fb, 363 struct dpu_hw_fmt_layout *layout) 364 { 365 unsigned int i; 366 367 /* Populate addresses for simple formats here */ 368 for (i = 0; i < layout->num_planes; ++i) 369 layout->plane_addr[i] = msm_framebuffer_iova(fb, aspace, i); 370 } 371 372 void dpu_format_populate_addrs(struct msm_gem_address_space *aspace, 373 struct drm_framebuffer *fb, 374 struct dpu_hw_fmt_layout *layout) 375 { 376 const struct msm_format *fmt; 377 378 fmt = msm_framebuffer_format(fb); 379 380 /* Populate the addresses given the fb */ 381 if (MSM_FORMAT_IS_UBWC(fmt) || 382 MSM_FORMAT_IS_TILE(fmt)) 383 _dpu_format_populate_addrs_ubwc(aspace, fb, layout); 384 else 385 _dpu_format_populate_addrs_linear(aspace, fb, layout); 386 } 387