Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0 or MIT
  2/*
  3 * Copyright (C) 2016 Noralf Trønnes
  4 *
  5 * This program is free software; you can redistribute it and/or modify
  6 * it under the terms of the GNU General Public License as published by
  7 * the Free Software Foundation; either version 2 of the License, or
  8 * (at your option) any later version.
  9 */
 10
 11#include <linux/module.h>
 12#include <linux/slab.h>
 13#include <linux/io.h>
 14
 15#include <drm/drm_format_helper.h>
 16#include <drm/drm_framebuffer.h>
 17#include <drm/drm_fourcc.h>
 18#include <drm/drm_rect.h>
 19
 20static unsigned int clip_offset(struct drm_rect *clip,
 21				unsigned int pitch, unsigned int cpp)
 22{
 23	return clip->y1 * pitch + clip->x1 * cpp;
 24}
 25
 26/**
 27 * drm_fb_memcpy - Copy clip buffer
 28 * @dst: Destination buffer
 29 * @vaddr: Source buffer
 30 * @fb: DRM framebuffer
 31 * @clip: Clip rectangle area to copy
 32 *
 33 * This function does not apply clipping on dst, i.e. the destination
 34 * is a small buffer containing the clip rect only.
 35 */
 36void drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
 37		   struct drm_rect *clip)
 38{
 39	unsigned int cpp = fb->format->cpp[0];
 40	size_t len = (clip->x2 - clip->x1) * cpp;
 41	unsigned int y, lines = clip->y2 - clip->y1;
 42
 43	vaddr += clip_offset(clip, fb->pitches[0], cpp);
 44	for (y = 0; y < lines; y++) {
 45		memcpy(dst, vaddr, len);
 46		vaddr += fb->pitches[0];
 47		dst += len;
 48	}
 49}
 50EXPORT_SYMBOL(drm_fb_memcpy);
 51
 52/**
 53 * drm_fb_memcpy_dstclip - Copy clip buffer
 54 * @dst: Destination buffer (iomem)
 55 * @dst_pitch: Number of bytes between two consecutive scanlines within dst
 56 * @vaddr: Source buffer
 57 * @fb: DRM framebuffer
 58 * @clip: Clip rectangle area to copy
 59 *
 60 * This function applies clipping on dst, i.e. the destination is a
 61 * full (iomem) framebuffer but only the clip rect content is copied over.
 62 */
 63void drm_fb_memcpy_dstclip(void __iomem *dst, unsigned int dst_pitch,
 64			   void *vaddr, struct drm_framebuffer *fb,
 65			   struct drm_rect *clip)
 66{
 67	unsigned int cpp = fb->format->cpp[0];
 68	unsigned int offset = clip_offset(clip, dst_pitch, cpp);
 69	size_t len = (clip->x2 - clip->x1) * cpp;
 70	unsigned int y, lines = clip->y2 - clip->y1;
 71
 72	vaddr += offset;
 73	dst += offset;
 74	for (y = 0; y < lines; y++) {
 75		memcpy_toio(dst, vaddr, len);
 76		vaddr += fb->pitches[0];
 77		dst += dst_pitch;
 78	}
 79}
 80EXPORT_SYMBOL(drm_fb_memcpy_dstclip);
 81
 82/**
 83 * drm_fb_swab - Swap bytes into clip buffer
 84 * @dst: Destination buffer
 85 * @src: Source buffer
 86 * @fb: DRM framebuffer
 87 * @clip: Clip rectangle area to copy
 88 * @cached: Source buffer is mapped cached (eg. not write-combined)
 89 *
 90 * If @cached is false a temporary buffer is used to cache one pixel line at a
 91 * time to speed up slow uncached reads.
 92 *
 93 * This function does not apply clipping on dst, i.e. the destination
 94 * is a small buffer containing the clip rect only.
 95 */
 96void drm_fb_swab(void *dst, void *src, struct drm_framebuffer *fb,
 97		 struct drm_rect *clip, bool cached)
 98{
 99	u8 cpp = fb->format->cpp[0];
100	size_t len = drm_rect_width(clip) * cpp;
101	u16 *src16, *dst16 = dst;
102	u32 *src32, *dst32 = dst;
103	unsigned int x, y;
104	void *buf = NULL;
105
106	if (WARN_ON_ONCE(cpp != 2 && cpp != 4))
107		return;
108
109	if (!cached)
110		buf = kmalloc(len, GFP_KERNEL);
111
112	src += clip_offset(clip, fb->pitches[0], cpp);
113
114	for (y = clip->y1; y < clip->y2; y++) {
115		if (buf) {
116			memcpy(buf, src, len);
117			src16 = buf;
118			src32 = buf;
119		} else {
120			src16 = src;
121			src32 = src;
122		}
123
124		for (x = clip->x1; x < clip->x2; x++) {
125			if (cpp == 4)
126				*dst32++ = swab32(*src32++);
127			else
128				*dst16++ = swab16(*src16++);
129		}
130
131		src += fb->pitches[0];
132	}
133
134	kfree(buf);
135}
136EXPORT_SYMBOL(drm_fb_swab);
137
138static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf,
139					   unsigned int pixels,
140					   bool swab)
141{
142	unsigned int x;
143	u16 val16;
144
145	for (x = 0; x < pixels; x++) {
146		val16 = ((sbuf[x] & 0x00F80000) >> 8) |
147			((sbuf[x] & 0x0000FC00) >> 5) |
148			((sbuf[x] & 0x000000F8) >> 3);
149		if (swab)
150			dbuf[x] = swab16(val16);
151		else
152			dbuf[x] = val16;
153	}
154}
155
156/**
157 * drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
158 * @dst: RGB565 destination buffer
159 * @vaddr: XRGB8888 source buffer
160 * @fb: DRM framebuffer
161 * @clip: Clip rectangle area to copy
162 * @swab: Swap bytes
163 *
164 * Drivers can use this function for RGB565 devices that don't natively
165 * support XRGB8888.
166 *
167 * This function does not apply clipping on dst, i.e. the destination
168 * is a small buffer containing the clip rect only.
169 */
170void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr,
171			       struct drm_framebuffer *fb,
172			       struct drm_rect *clip, bool swab)
173{
174	size_t linepixels = clip->x2 - clip->x1;
175	size_t src_len = linepixels * sizeof(u32);
176	size_t dst_len = linepixels * sizeof(u16);
177	unsigned y, lines = clip->y2 - clip->y1;
178	void *sbuf;
179
180	/*
181	 * The cma memory is write-combined so reads are uncached.
182	 * Speed up by fetching one line at a time.
183	 */
184	sbuf = kmalloc(src_len, GFP_KERNEL);
185	if (!sbuf)
186		return;
187
188	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
189	for (y = 0; y < lines; y++) {
190		memcpy(sbuf, vaddr, src_len);
191		drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab);
192		vaddr += fb->pitches[0];
193		dst += dst_len;
194	}
195
196	kfree(sbuf);
197}
198EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
199
200/**
201 * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer
202 * @dst: RGB565 destination buffer (iomem)
203 * @dst_pitch: destination buffer pitch
204 * @vaddr: XRGB8888 source buffer
205 * @fb: DRM framebuffer
206 * @clip: Clip rectangle area to copy
207 * @swab: Swap bytes
208 *
209 * Drivers can use this function for RGB565 devices that don't natively
210 * support XRGB8888.
211 *
212 * This function applies clipping on dst, i.e. the destination is a
213 * full (iomem) framebuffer but only the clip rect content is copied over.
214 */
215void drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch,
216				       void *vaddr, struct drm_framebuffer *fb,
217				       struct drm_rect *clip, bool swab)
218{
219	size_t linepixels = clip->x2 - clip->x1;
220	size_t dst_len = linepixels * sizeof(u16);
221	unsigned y, lines = clip->y2 - clip->y1;
222	void *dbuf;
223
224	dbuf = kmalloc(dst_len, GFP_KERNEL);
225	if (!dbuf)
226		return;
227
228	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
229	dst += clip_offset(clip, dst_pitch, sizeof(u16));
230	for (y = 0; y < lines; y++) {
231		drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab);
232		memcpy_toio(dst, dbuf, dst_len);
233		vaddr += fb->pitches[0];
234		dst += dst_len;
235	}
236
237	kfree(dbuf);
238}
239EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip);
240
241static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf,
242					   unsigned int pixels)
243{
244	unsigned int x;
245
246	for (x = 0; x < pixels; x++) {
247		*dbuf++ = (sbuf[x] & 0x000000FF) >>  0;
248		*dbuf++ = (sbuf[x] & 0x0000FF00) >>  8;
249		*dbuf++ = (sbuf[x] & 0x00FF0000) >> 16;
250	}
251}
252
253/**
254 * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer
255 * @dst: RGB565 destination buffer (iomem)
256 * @dst_pitch: destination buffer pitch
257 * @vaddr: XRGB8888 source buffer
258 * @fb: DRM framebuffer
259 * @clip: Clip rectangle area to copy
260 *
261 * Drivers can use this function for RGB888 devices that don't natively
262 * support XRGB8888.
263 *
264 * This function applies clipping on dst, i.e. the destination is a
265 * full (iomem) framebuffer but only the clip rect content is copied over.
266 */
267void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch,
268				       void *vaddr, struct drm_framebuffer *fb,
269				       struct drm_rect *clip)
270{
271	size_t linepixels = clip->x2 - clip->x1;
272	size_t dst_len = linepixels * 3;
273	unsigned y, lines = clip->y2 - clip->y1;
274	void *dbuf;
275
276	dbuf = kmalloc(dst_len, GFP_KERNEL);
277	if (!dbuf)
278		return;
279
280	vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32));
281	dst += clip_offset(clip, dst_pitch, sizeof(u16));
282	for (y = 0; y < lines; y++) {
283		drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels);
284		memcpy_toio(dst, dbuf, dst_len);
285		vaddr += fb->pitches[0];
286		dst += dst_len;
287	}
288
289	kfree(dbuf);
290}
291EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip);
292
293/**
294 * drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
295 * @dst: 8-bit grayscale destination buffer
296 * @vaddr: XRGB8888 source buffer
297 * @fb: DRM framebuffer
298 * @clip: Clip rectangle area to copy
299 *
300 * Drm doesn't have native monochrome or grayscale support.
301 * Such drivers can announce the commonly supported XR24 format to userspace
302 * and use this function to convert to the native format.
303 *
304 * Monochrome drivers will use the most significant bit,
305 * where 1 means foreground color and 0 background color.
306 *
307 * ITU BT.601 is used for the RGB -> luma (brightness) conversion.
308 */
309void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
310			       struct drm_rect *clip)
311{
312	unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
313	unsigned int x, y;
314	void *buf;
315	u32 *src;
316
317	if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
318		return;
319	/*
320	 * The cma memory is write-combined so reads are uncached.
321	 * Speed up by fetching one line at a time.
322	 */
323	buf = kmalloc(len, GFP_KERNEL);
324	if (!buf)
325		return;
326
327	for (y = clip->y1; y < clip->y2; y++) {
328		src = vaddr + (y * fb->pitches[0]);
329		src += clip->x1;
330		memcpy(buf, src, len);
331		src = buf;
332		for (x = clip->x1; x < clip->x2; x++) {
333			u8 r = (*src & 0x00ff0000) >> 16;
334			u8 g = (*src & 0x0000ff00) >> 8;
335			u8 b =  *src & 0x000000ff;
336
337			/* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
338			*dst++ = (3 * r + 6 * g + b) / 10;
339			src++;
340		}
341	}
342
343	kfree(buf);
344}
345EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
346
347/**
348 * drm_fb_blit_rect_dstclip - Copy parts of a framebuffer to display memory
349 * @dst:	The display memory to copy to
350 * @dst_pitch:	Number of bytes between two consecutive scanlines within dst
351 * @dst_format:	FOURCC code of the display's color format
352 * @vmap:	The framebuffer memory to copy from
353 * @fb:		The framebuffer to copy from
354 * @clip:	Clip rectangle area to copy
355 *
356 * This function copies parts of a framebuffer to display memory. If the
357 * formats of the display and the framebuffer mismatch, the blit function
358 * will attempt to convert between them.
359 *
360 * Use drm_fb_blit_dstclip() to copy the full framebuffer.
361 *
362 * Returns:
363 * 0 on success, or
364 * -EINVAL if the color-format conversion failed, or
365 * a negative error code otherwise.
366 */
367int drm_fb_blit_rect_dstclip(void __iomem *dst, unsigned int dst_pitch,
368			     uint32_t dst_format, void *vmap,
369			     struct drm_framebuffer *fb,
370			     struct drm_rect *clip)
371{
372	uint32_t fb_format = fb->format->format;
373
374	/* treat alpha channel like filler bits */
375	if (fb_format == DRM_FORMAT_ARGB8888)
376		fb_format = DRM_FORMAT_XRGB8888;
377	if (dst_format == DRM_FORMAT_ARGB8888)
378		dst_format = DRM_FORMAT_XRGB8888;
379
380	if (dst_format == fb_format) {
381		drm_fb_memcpy_dstclip(dst, dst_pitch, vmap, fb, clip);
382		return 0;
383
384	} else if (dst_format == DRM_FORMAT_RGB565) {
385		if (fb_format == DRM_FORMAT_XRGB8888) {
386			drm_fb_xrgb8888_to_rgb565_dstclip(dst, dst_pitch,
387							  vmap, fb, clip,
388							  false);
389			return 0;
390		}
391	} else if (dst_format == DRM_FORMAT_RGB888) {
392		if (fb_format == DRM_FORMAT_XRGB8888) {
393			drm_fb_xrgb8888_to_rgb888_dstclip(dst, dst_pitch,
394							  vmap, fb, clip);
395			return 0;
396		}
397	}
398
399	return -EINVAL;
400}
401EXPORT_SYMBOL(drm_fb_blit_rect_dstclip);
402
403/**
404 * drm_fb_blit_dstclip - Copy framebuffer to display memory
405 * @dst:	The display memory to copy to
406 * @dst_pitch:	Number of bytes between two consecutive scanlines within dst
407 * @dst_format:	FOURCC code of the display's color format
408 * @vmap:	The framebuffer memory to copy from
409 * @fb:		The framebuffer to copy from
410 *
411 * This function copies a full framebuffer to display memory. If the formats
412 * of the display and the framebuffer mismatch, the copy function will
413 * attempt to convert between them.
414 *
415 * See drm_fb_blit_rect_dstclip() for more inforamtion.
416 *
417 * Returns:
418 * 0 on success, or a negative error code otherwise.
419 */
420int drm_fb_blit_dstclip(void __iomem *dst, unsigned int dst_pitch,
421			uint32_t dst_format, void *vmap,
422			struct drm_framebuffer *fb)
423{
424	struct drm_rect fullscreen = {
425		.x1 = 0,
426		.x2 = fb->width,
427		.y1 = 0,
428		.y2 = fb->height,
429	};
430	return drm_fb_blit_rect_dstclip(dst, dst_pitch, dst_format, vmap, fb,
431					&fullscreen);
432}
433EXPORT_SYMBOL(drm_fb_blit_dstclip);