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 */
_dpu_get_v_h_subsample_rate(enum mdp_chroma_samp_type chroma_sample,uint32_t * v_sample,uint32_t * h_sample)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
_dpu_format_get_media_color_ubwc(const struct msm_format * fmt)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
_dpu_format_populate_plane_sizes_ubwc(const struct msm_format * fmt,struct drm_framebuffer * fb,struct dpu_hw_fmt_layout * layout)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
_dpu_format_populate_plane_sizes_linear(const struct msm_format * fmt,struct drm_framebuffer * fb,struct dpu_hw_fmt_layout * layout)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 */
dpu_format_populate_plane_sizes(struct drm_framebuffer * fb,struct dpu_hw_fmt_layout * layout)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
_dpu_format_populate_addrs_ubwc(struct msm_gem_address_space * aspace,struct drm_framebuffer * fb,struct dpu_hw_fmt_layout * layout)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
_dpu_format_populate_addrs_linear(struct msm_gem_address_space * aspace,struct drm_framebuffer * fb,struct dpu_hw_fmt_layout * layout)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 */
dpu_format_populate_addrs(struct msm_gem_address_space * aspace,struct drm_framebuffer * fb,struct dpu_hw_fmt_layout * layout)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