xref: /linux/drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c (revision 2c142b63c8ee982cdfdba49a616027c266294838)
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