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