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 */
_dpu_get_v_h_subsample_rate(enum mdp_chroma_samp_type chroma_sample,uint32_t * v_sample,uint32_t * h_sample)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
_dpu_format_get_media_color_ubwc(const struct msm_format * fmt)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
_dpu_format_get_plane_sizes_ubwc(const struct msm_format * fmt,const uint32_t width,const uint32_t height,struct dpu_hw_fmt_layout * layout)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
_dpu_format_get_plane_sizes_linear(const struct msm_format * fmt,const uint32_t width,const uint32_t height,struct dpu_hw_fmt_layout * layout,const uint32_t * pitches)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
dpu_format_get_plane_sizes(const struct msm_format * fmt,const uint32_t w,const uint32_t h,struct dpu_hw_fmt_layout * layout,const uint32_t * pitches)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
_dpu_format_populate_addrs_ubwc(struct msm_gem_address_space * aspace,struct drm_framebuffer * fb,struct dpu_hw_fmt_layout * layout)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
_dpu_format_populate_addrs_linear(struct msm_gem_address_space * aspace,struct drm_framebuffer * fb,struct dpu_hw_fmt_layout * layout)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
dpu_format_populate_layout(struct msm_gem_address_space * aspace,struct drm_framebuffer * fb,struct dpu_hw_fmt_layout * layout)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
dpu_format_check_modified_format(const struct msm_kms * kms,const struct msm_format * fmt,const struct drm_mode_fb_cmd2 * cmd,struct drm_gem_object ** bos)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