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