xref: /linux/drivers/gpu/drm/vkms/vkms_formats.c (revision 2c1ed907520c50326b8f604907a8478b27881a2e)
1 // SPDX-License-Identifier: GPL-2.0+
2 
3 #include <linux/kernel.h>
4 #include <linux/minmax.h>
5 
6 #include <drm/drm_blend.h>
7 #include <drm/drm_rect.h>
8 #include <drm/drm_fixed.h>
9 
10 #include "vkms_formats.h"
11 
12 /**
13  * packed_pixels_offset() - Get the offset of the block containing the pixel at coordinates x/y
14  *
15  * @frame_info: Buffer metadata
16  * @x: The x coordinate of the wanted pixel in the buffer
17  * @y: The y coordinate of the wanted pixel in the buffer
18  * @plane_index: The index of the plane to use
19  * @offset: The returned offset inside the buffer of the block
20  * @rem_x: The returned X coordinate of the requested pixel in the block
21  * @rem_y: The returned Y coordinate of the requested pixel in the block
22  *
23  * As some pixel formats store multiple pixels in a block (DRM_FORMAT_R* for example), some
24  * pixels are not individually addressable. This function return 3 values: the offset of the
25  * whole block, and the coordinate of the requested pixel inside this block.
26  * For example, if the format is DRM_FORMAT_R1 and the requested coordinate is 13,5, the offset
27  * will point to the byte 5*pitches + 13/8 (second byte of the 5th line), and the rem_x/rem_y
28  * coordinates will be (13 % 8, 5 % 1) = (5, 0)
29  *
30  * With this function, the caller just have to extract the correct pixel from the block.
31  */
packed_pixels_offset(const struct vkms_frame_info * frame_info,int x,int y,int plane_index,int * offset,int * rem_x,int * rem_y)32 static void packed_pixels_offset(const struct vkms_frame_info *frame_info, int x, int y,
33 				 int plane_index, int *offset, int *rem_x, int *rem_y)
34 {
35 	struct drm_framebuffer *fb = frame_info->fb;
36 	const struct drm_format_info *format = frame_info->fb->format;
37 	/* Directly using x and y to multiply pitches and format->ccp is not sufficient because
38 	 * in some formats a block can represent multiple pixels.
39 	 *
40 	 * Dividing x and y by the block size allows to extract the correct offset of the block
41 	 * containing the pixel.
42 	 */
43 
44 	int block_x = x / drm_format_info_block_width(format, plane_index);
45 	int block_y = y / drm_format_info_block_height(format, plane_index);
46 	int block_pitch = fb->pitches[plane_index] * drm_format_info_block_height(format,
47 										  plane_index);
48 	*rem_x = x % drm_format_info_block_width(format, plane_index);
49 	*rem_y = y % drm_format_info_block_height(format, plane_index);
50 	*offset = fb->offsets[plane_index] +
51 		  block_y * block_pitch +
52 		  block_x * format->char_per_block[plane_index];
53 }
54 
55 /**
56  * packed_pixels_addr() - Get the pointer to the block containing the pixel at the given
57  * coordinates
58  *
59  * @frame_info: Buffer metadata
60  * @x: The x (width) coordinate inside the plane
61  * @y: The y (height) coordinate inside the plane
62  * @plane_index: The index of the plane
63  * @addr: The returned pointer
64  * @rem_x: The returned X coordinate of the requested pixel in the block
65  * @rem_y: The returned Y coordinate of the requested pixel in the block
66  *
67  * Takes the information stored in the frame_info, a pair of coordinates, and returns the address
68  * of the block containing this pixel and the pixel position inside this block.
69  *
70  * See @packed_pixels_offset for details about rem_x/rem_y behavior.
71  */
packed_pixels_addr(const struct vkms_frame_info * frame_info,int x,int y,int plane_index,u8 ** addr,int * rem_x,int * rem_y)72 static void packed_pixels_addr(const struct vkms_frame_info *frame_info,
73 			       int x, int y, int plane_index, u8 **addr, int *rem_x,
74 			       int *rem_y)
75 {
76 	int offset;
77 
78 	packed_pixels_offset(frame_info, x, y, plane_index, &offset, rem_x, rem_y);
79 	*addr = (u8 *)frame_info->map[0].vaddr + offset;
80 }
81 
82 /**
83  * get_block_step_bytes() - Common helper to compute the correct step value between each pixel block
84  * to read in a certain direction.
85  *
86  * @fb: Framebuffer to iter on
87  * @direction: Direction of the reading
88  * @plane_index: Plane to get the step from
89  *
90  * As the returned count is the number of bytes between two consecutive blocks in a direction,
91  * the caller may have to read multiple pixels before using the next one (for example, to read from
92  * left to right in a DRM_FORMAT_R1 plane, each block contains 8 pixels, so the step must be used
93  * only every 8 pixels).
94  */
get_block_step_bytes(struct drm_framebuffer * fb,enum pixel_read_direction direction,int plane_index)95 static int get_block_step_bytes(struct drm_framebuffer *fb, enum pixel_read_direction direction,
96 				int plane_index)
97 {
98 	switch (direction) {
99 	case READ_LEFT_TO_RIGHT:
100 		return fb->format->char_per_block[plane_index];
101 	case READ_RIGHT_TO_LEFT:
102 		return -fb->format->char_per_block[plane_index];
103 	case READ_TOP_TO_BOTTOM:
104 		return (int)fb->pitches[plane_index] * drm_format_info_block_width(fb->format,
105 										   plane_index);
106 	case READ_BOTTOM_TO_TOP:
107 		return -(int)fb->pitches[plane_index] * drm_format_info_block_width(fb->format,
108 										    plane_index);
109 	}
110 
111 	return 0;
112 }
113 
114 /**
115  * packed_pixels_addr_1x1() - Get the pointer to the block containing the pixel at the given
116  * coordinates
117  *
118  * @frame_info: Buffer metadata
119  * @x: The x (width) coordinate inside the plane
120  * @y: The y (height) coordinate inside the plane
121  * @plane_index: The index of the plane
122  * @addr: The returned pointer
123  *
124  * This function can only be used with format where block_h == block_w == 1.
125  */
packed_pixels_addr_1x1(const struct vkms_frame_info * frame_info,int x,int y,int plane_index,u8 ** addr)126 static void packed_pixels_addr_1x1(const struct vkms_frame_info *frame_info,
127 				   int x, int y, int plane_index, u8 **addr)
128 {
129 	int offset, rem_x, rem_y;
130 
131 	WARN_ONCE(drm_format_info_block_width(frame_info->fb->format,
132 					      plane_index) != 1,
133 		"%s() only support formats with block_w == 1", __func__);
134 	WARN_ONCE(drm_format_info_block_height(frame_info->fb->format,
135 					       plane_index) != 1,
136 		"%s() only support formats with block_h == 1", __func__);
137 
138 	packed_pixels_offset(frame_info, x, y, plane_index, &offset, &rem_x,
139 			     &rem_y);
140 	*addr = (u8 *)frame_info->map[0].vaddr + offset;
141 }
142 
143 /*
144  * The following functions take pixel data (a, r, g, b, pixel, ...) and convert them to
145  * &struct pixel_argb_u16
146  *
147  * They are used in the `read_line`s functions to avoid duplicate work for some pixel formats.
148  */
149 
argb_u16_from_u8888(u8 a,u8 r,u8 g,u8 b)150 static struct pixel_argb_u16 argb_u16_from_u8888(u8 a, u8 r, u8 g, u8 b)
151 {
152 	struct pixel_argb_u16 out_pixel;
153 	/*
154 	 * The 257 is the "conversion ratio". This number is obtained by the
155 	 * (2^16 - 1) / (2^8 - 1) division. Which, in this case, tries to get
156 	 * the best color value in a pixel format with more possibilities.
157 	 * A similar idea applies to others RGB color conversions.
158 	 */
159 	out_pixel.a = (u16)a * 257;
160 	out_pixel.r = (u16)r * 257;
161 	out_pixel.g = (u16)g * 257;
162 	out_pixel.b = (u16)b * 257;
163 
164 	return out_pixel;
165 }
166 
argb_u16_from_u16161616(u16 a,u16 r,u16 g,u16 b)167 static struct pixel_argb_u16 argb_u16_from_u16161616(u16 a, u16 r, u16 g, u16 b)
168 {
169 	struct pixel_argb_u16 out_pixel;
170 
171 	out_pixel.a = a;
172 	out_pixel.r = r;
173 	out_pixel.g = g;
174 	out_pixel.b = b;
175 
176 	return out_pixel;
177 }
178 
argb_u16_from_le16161616(__le16 a,__le16 r,__le16 g,__le16 b)179 static struct pixel_argb_u16 argb_u16_from_le16161616(__le16 a, __le16 r, __le16 g, __le16 b)
180 {
181 	return argb_u16_from_u16161616(le16_to_cpu(a), le16_to_cpu(r), le16_to_cpu(g),
182 				       le16_to_cpu(b));
183 }
184 
argb_u16_from_RGB565(const __le16 * pixel)185 static struct pixel_argb_u16 argb_u16_from_RGB565(const __le16 *pixel)
186 {
187 	struct pixel_argb_u16 out_pixel;
188 
189 	s64 fp_rb_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(31));
190 	s64 fp_g_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(63));
191 
192 	u16 rgb_565 = le16_to_cpu(*pixel);
193 	s64 fp_r = drm_int2fixp((rgb_565 >> 11) & 0x1f);
194 	s64 fp_g = drm_int2fixp((rgb_565 >> 5) & 0x3f);
195 	s64 fp_b = drm_int2fixp(rgb_565 & 0x1f);
196 
197 	out_pixel.a = (u16)0xffff;
198 	out_pixel.r = drm_fixp2int_round(drm_fixp_mul(fp_r, fp_rb_ratio));
199 	out_pixel.g = drm_fixp2int_round(drm_fixp_mul(fp_g, fp_g_ratio));
200 	out_pixel.b = drm_fixp2int_round(drm_fixp_mul(fp_b, fp_rb_ratio));
201 
202 	return out_pixel;
203 }
204 
205 /*
206  * The following functions are read_line function for each pixel format supported by VKMS.
207  *
208  * They read a line starting at the point @x_start,@y_start following the @direction. The result
209  * is stored in @out_pixel and in the format ARGB16161616.
210  *
211  * These functions are very repetitive, but the innermost pixel loops must be kept inside these
212  * functions for performance reasons. Some benchmarking was done in [1] where having the innermost
213  * loop factored out of these functions showed a slowdown by a factor of three.
214  *
215  * [1]: https://lore.kernel.org/dri-devel/d258c8dc-78e9-4509-9037-a98f7f33b3a3@riseup.net/
216  */
217 
ARGB8888_read_line(const struct vkms_plane_state * plane,int x_start,int y_start,enum pixel_read_direction direction,int count,struct pixel_argb_u16 out_pixel[])218 static void ARGB8888_read_line(const struct vkms_plane_state *plane, int x_start, int y_start,
219 			       enum pixel_read_direction direction, int count,
220 			       struct pixel_argb_u16 out_pixel[])
221 {
222 	struct pixel_argb_u16 *end = out_pixel + count;
223 	u8 *src_pixels;
224 
225 	packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, &src_pixels);
226 
227 	int step = get_block_step_bytes(plane->frame_info->fb, direction, 0);
228 
229 	while (out_pixel < end) {
230 		u8 *px = (u8 *)src_pixels;
231 		*out_pixel = argb_u16_from_u8888(px[3], px[2], px[1], px[0]);
232 		out_pixel += 1;
233 		src_pixels += step;
234 	}
235 }
236 
XRGB8888_read_line(const struct vkms_plane_state * plane,int x_start,int y_start,enum pixel_read_direction direction,int count,struct pixel_argb_u16 out_pixel[])237 static void XRGB8888_read_line(const struct vkms_plane_state *plane, int x_start, int y_start,
238 			       enum pixel_read_direction direction, int count,
239 			       struct pixel_argb_u16 out_pixel[])
240 {
241 	struct pixel_argb_u16 *end = out_pixel + count;
242 	u8 *src_pixels;
243 
244 	packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, &src_pixels);
245 
246 	int step = get_block_step_bytes(plane->frame_info->fb, direction, 0);
247 
248 	while (out_pixel < end) {
249 		u8 *px = (u8 *)src_pixels;
250 		*out_pixel = argb_u16_from_u8888(255, px[2], px[1], px[0]);
251 		out_pixel += 1;
252 		src_pixels += step;
253 	}
254 }
255 
ARGB16161616_read_line(const struct vkms_plane_state * plane,int x_start,int y_start,enum pixel_read_direction direction,int count,struct pixel_argb_u16 out_pixel[])256 static void ARGB16161616_read_line(const struct vkms_plane_state *plane, int x_start,
257 				   int y_start, enum pixel_read_direction direction, int count,
258 				   struct pixel_argb_u16 out_pixel[])
259 {
260 	struct pixel_argb_u16 *end = out_pixel + count;
261 	u8 *src_pixels;
262 
263 	packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, &src_pixels);
264 
265 	int step = get_block_step_bytes(plane->frame_info->fb, direction, 0);
266 
267 	while (out_pixel < end) {
268 		u16 *px = (u16 *)src_pixels;
269 		*out_pixel = argb_u16_from_u16161616(px[3], px[2], px[1], px[0]);
270 		out_pixel += 1;
271 		src_pixels += step;
272 	}
273 }
274 
XRGB16161616_read_line(const struct vkms_plane_state * plane,int x_start,int y_start,enum pixel_read_direction direction,int count,struct pixel_argb_u16 out_pixel[])275 static void XRGB16161616_read_line(const struct vkms_plane_state *plane, int x_start,
276 				   int y_start, enum pixel_read_direction direction, int count,
277 				   struct pixel_argb_u16 out_pixel[])
278 {
279 	struct pixel_argb_u16 *end = out_pixel + count;
280 	u8 *src_pixels;
281 
282 	packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, &src_pixels);
283 
284 	int step = get_block_step_bytes(plane->frame_info->fb, direction, 0);
285 
286 	while (out_pixel < end) {
287 		__le16 *px = (__le16 *)src_pixels;
288 		*out_pixel = argb_u16_from_le16161616(cpu_to_le16(0xFFFF), px[2], px[1], px[0]);
289 		out_pixel += 1;
290 		src_pixels += step;
291 	}
292 }
293 
RGB565_read_line(const struct vkms_plane_state * plane,int x_start,int y_start,enum pixel_read_direction direction,int count,struct pixel_argb_u16 out_pixel[])294 static void RGB565_read_line(const struct vkms_plane_state *plane, int x_start,
295 			     int y_start, enum pixel_read_direction direction, int count,
296 			     struct pixel_argb_u16 out_pixel[])
297 {
298 	struct pixel_argb_u16 *end = out_pixel + count;
299 	u8 *src_pixels;
300 
301 	packed_pixels_addr_1x1(plane->frame_info, x_start, y_start, 0, &src_pixels);
302 
303 	int step = get_block_step_bytes(plane->frame_info->fb, direction, 0);
304 
305 	while (out_pixel < end) {
306 		__le16 *px = (__le16 *)src_pixels;
307 
308 		*out_pixel = argb_u16_from_RGB565(px);
309 		out_pixel += 1;
310 		src_pixels += step;
311 	}
312 }
313 
314 /*
315  * The following functions take one &struct pixel_argb_u16 and convert it to a specific format.
316  * The result is stored in @out_pixel.
317  *
318  * They are used in vkms_writeback_row() to convert and store a pixel from the src_buffer to
319  * the writeback buffer.
320  */
argb_u16_to_ARGB8888(u8 * out_pixel,const struct pixel_argb_u16 * in_pixel)321 static void argb_u16_to_ARGB8888(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
322 {
323 	/*
324 	 * This sequence below is important because the format's byte order is
325 	 * in little-endian. In the case of the ARGB8888 the memory is
326 	 * organized this way:
327 	 *
328 	 * | Addr     | = blue channel
329 	 * | Addr + 1 | = green channel
330 	 * | Addr + 2 | = Red channel
331 	 * | Addr + 3 | = Alpha channel
332 	 */
333 	out_pixel[3] = DIV_ROUND_CLOSEST(in_pixel->a, 257);
334 	out_pixel[2] = DIV_ROUND_CLOSEST(in_pixel->r, 257);
335 	out_pixel[1] = DIV_ROUND_CLOSEST(in_pixel->g, 257);
336 	out_pixel[0] = DIV_ROUND_CLOSEST(in_pixel->b, 257);
337 }
338 
argb_u16_to_XRGB8888(u8 * out_pixel,const struct pixel_argb_u16 * in_pixel)339 static void argb_u16_to_XRGB8888(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
340 {
341 	out_pixel[3] = 0xff;
342 	out_pixel[2] = DIV_ROUND_CLOSEST(in_pixel->r, 257);
343 	out_pixel[1] = DIV_ROUND_CLOSEST(in_pixel->g, 257);
344 	out_pixel[0] = DIV_ROUND_CLOSEST(in_pixel->b, 257);
345 }
346 
argb_u16_to_ARGB16161616(u8 * out_pixel,const struct pixel_argb_u16 * in_pixel)347 static void argb_u16_to_ARGB16161616(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
348 {
349 	__le16 *pixel = (__le16 *)out_pixel;
350 
351 	pixel[3] = cpu_to_le16(in_pixel->a);
352 	pixel[2] = cpu_to_le16(in_pixel->r);
353 	pixel[1] = cpu_to_le16(in_pixel->g);
354 	pixel[0] = cpu_to_le16(in_pixel->b);
355 }
356 
argb_u16_to_XRGB16161616(u8 * out_pixel,const struct pixel_argb_u16 * in_pixel)357 static void argb_u16_to_XRGB16161616(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
358 {
359 	__le16 *pixel = (__le16 *)out_pixel;
360 
361 	pixel[3] = cpu_to_le16(0xffff);
362 	pixel[2] = cpu_to_le16(in_pixel->r);
363 	pixel[1] = cpu_to_le16(in_pixel->g);
364 	pixel[0] = cpu_to_le16(in_pixel->b);
365 }
366 
argb_u16_to_RGB565(u8 * out_pixel,const struct pixel_argb_u16 * in_pixel)367 static void argb_u16_to_RGB565(u8 *out_pixel, const struct pixel_argb_u16 *in_pixel)
368 {
369 	__le16 *pixel = (__le16 *)out_pixel;
370 
371 	s64 fp_rb_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(31));
372 	s64 fp_g_ratio = drm_fixp_div(drm_int2fixp(65535), drm_int2fixp(63));
373 
374 	s64 fp_r = drm_int2fixp(in_pixel->r);
375 	s64 fp_g = drm_int2fixp(in_pixel->g);
376 	s64 fp_b = drm_int2fixp(in_pixel->b);
377 
378 	u16 r = drm_fixp2int(drm_fixp_div(fp_r, fp_rb_ratio));
379 	u16 g = drm_fixp2int(drm_fixp_div(fp_g, fp_g_ratio));
380 	u16 b = drm_fixp2int(drm_fixp_div(fp_b, fp_rb_ratio));
381 
382 	*pixel = cpu_to_le16(r << 11 | g << 5 | b);
383 }
384 
385 /**
386  * vkms_writeback_row() - Generic loop for all supported writeback format. It is executed just
387  * after the blending to write a line in the writeback buffer.
388  *
389  * @wb: Job where to insert the final image
390  * @src_buffer: Line to write
391  * @y: Row to write in the writeback buffer
392  */
vkms_writeback_row(struct vkms_writeback_job * wb,const struct line_buffer * src_buffer,int y)393 void vkms_writeback_row(struct vkms_writeback_job *wb,
394 			const struct line_buffer *src_buffer, int y)
395 {
396 	struct vkms_frame_info *frame_info = &wb->wb_frame_info;
397 	int x_dst = frame_info->dst.x1;
398 	u8 *dst_pixels;
399 	int rem_x, rem_y;
400 
401 	packed_pixels_addr(frame_info, x_dst, y, 0, &dst_pixels, &rem_x, &rem_y);
402 	struct pixel_argb_u16 *in_pixels = src_buffer->pixels;
403 	int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst), src_buffer->n_pixels);
404 
405 	for (size_t x = 0; x < x_limit; x++, dst_pixels += frame_info->fb->format->cpp[0])
406 		wb->pixel_write(dst_pixels, &in_pixels[x]);
407 }
408 
409 /**
410  * get_pixel_read_line_function() - Retrieve the correct read_line function for a specific
411  * format. The returned pointer is NULL for unsupported pixel formats. The caller must ensure that
412  * the pointer is valid before using it in a vkms_plane_state.
413  *
414  * @format: DRM_FORMAT_* value for which to obtain a conversion function (see [drm_fourcc.h])
415  */
get_pixel_read_line_function(u32 format)416 pixel_read_line_t get_pixel_read_line_function(u32 format)
417 {
418 	switch (format) {
419 	case DRM_FORMAT_ARGB8888:
420 		return &ARGB8888_read_line;
421 	case DRM_FORMAT_XRGB8888:
422 		return &XRGB8888_read_line;
423 	case DRM_FORMAT_ARGB16161616:
424 		return &ARGB16161616_read_line;
425 	case DRM_FORMAT_XRGB16161616:
426 		return &XRGB16161616_read_line;
427 	case DRM_FORMAT_RGB565:
428 		return &RGB565_read_line;
429 	default:
430 		/*
431 		 * This is a bug in vkms_plane_atomic_check(). All the supported
432 		 * format must:
433 		 * - Be listed in vkms_formats in vkms_plane.c
434 		 * - Have a pixel_read callback defined here
435 		 */
436 		pr_err("Pixel format %p4cc is not supported by VKMS planes. This is a kernel bug, atomic check must forbid this configuration.\n",
437 		       &format);
438 		BUG();
439 	}
440 }
441 
442 /**
443  * get_pixel_write_function() - Retrieve the correct write_pixel function for a specific format.
444  * The returned pointer is NULL for unsupported pixel formats. The caller must ensure that the
445  * pointer is valid before using it in a vkms_writeback_job.
446  *
447  * @format: DRM_FORMAT_* value for which to obtain a conversion function (see [drm_fourcc.h])
448  */
get_pixel_write_function(u32 format)449 pixel_write_t get_pixel_write_function(u32 format)
450 {
451 	switch (format) {
452 	case DRM_FORMAT_ARGB8888:
453 		return &argb_u16_to_ARGB8888;
454 	case DRM_FORMAT_XRGB8888:
455 		return &argb_u16_to_XRGB8888;
456 	case DRM_FORMAT_ARGB16161616:
457 		return &argb_u16_to_ARGB16161616;
458 	case DRM_FORMAT_XRGB16161616:
459 		return &argb_u16_to_XRGB16161616;
460 	case DRM_FORMAT_RGB565:
461 		return &argb_u16_to_RGB565;
462 	default:
463 		/*
464 		 * This is a bug in vkms_writeback_atomic_check. All the supported
465 		 * format must:
466 		 * - Be listed in vkms_wb_formats in vkms_writeback.c
467 		 * - Have a pixel_write callback defined here
468 		 */
469 		pr_err("Pixel format %p4cc is not supported by VKMS writeback. This is a kernel bug, atomic check must forbid this configuration.\n",
470 		       &format);
471 		BUG();
472 	}
473 }
474