// SPDX-License-Identifier: GPL-2.0 or MIT
/*
* Copyright (C) 2016 Noralf Trønnes
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/io.h>
#include <linux/iosys-map.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <drm/drm_device.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_print.h>
#include <drm/drm_rect.h>
static unsigned int clip_offset(const struct drm_rect *clip, unsigned int pitch, unsigned int cpp)
{
return clip->y1 * pitch + clip->x1 * cpp;
}
/**
* drm_fb_clip_offset - Returns the clipping rectangles byte-offset in a framebuffer
* @pitch: Framebuffer line pitch in byte
* @format: Framebuffer format
* @clip: Clip rectangle
*
* Returns:
* The byte offset of the clip rectangle's top-left corner within the framebuffer.
*/
unsigned int drm_fb_clip_offset(unsigned int pitch, const struct drm_format_info *format,
const struct drm_rect *clip)
{
return clip_offset(clip, pitch, format->cpp[0]);
}
EXPORT_SYMBOL(drm_fb_clip_offset);
/* TODO: Make this function work with multi-plane formats. */
static int __drm_fb_xfrm(void *dst, unsigned long dst_pitch, unsigned long dst_pixsize,
const void *vaddr, const struct drm_framebuffer *fb,
const struct drm_rect *clip, bool vaddr_cached_hint,
void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels))
{
unsigned long linepixels = drm_rect_width(clip);
unsigned long lines = drm_rect_height(clip);
size_t sbuf_len = linepixels * fb->format->cpp[0];
void *stmp = NULL;
unsigned long i;
const void *sbuf;
/*
* Some source buffers, such as DMA memory, use write-combine
* caching, so reads are uncached. Speed up access by fetching
* one line at a time.
*/
if (!vaddr_cached_hint) {
stmp = kmalloc(sbuf_len, GFP_KERNEL);
if (!stmp)
return -ENOMEM;
}
if (!dst_pitch)
dst_pitch = drm_rect_width(clip) * dst_pixsize;
vaddr += clip_offset(clip, fb->pitches[0], fb->format->cpp[0]);
for (i = 0; i < lines; ++i) {
if (stmp)
sbuf = memcpy(stmp, vaddr, sbuf_len);
else
sbuf = vaddr;
xfrm_line(dst, sbuf, linepixels);
vaddr += fb->pitches[0];
dst += dst_pitch;
}
kfree(stmp);
return 0;
}
/* TODO: Make this function work with multi-plane formats. */
static int __drm_fb_xfrm_toio(void __iomem *dst, unsigned long dst_pitch, unsigned long dst_pixsize,
const void *vaddr, const struct drm_framebuffer *fb,
const struct drm_rect *clip, bool vaddr_cached_hint,
void (*xfrm_line)(void *dbuf, const void *sbuf, unsigned int npixels))
{
unsigned long linepixels = drm_rect_width(clip);
unsigned long lines = drm_rect_height(clip);
size_t dbuf_len = linepixels * dst_pixsize;
size_t stmp_off = round_up(dbuf_len, ARCH_KMALLOC_MINALIGN); /* for sbuf alignment */
size_t sbuf_len = linepixels * fb->format->cpp[0];
void *stmp = NULL;
unsigned long i;
const void *sbuf;
void *dbuf;
if (vaddr_cached_hint) {
dbuf = kmalloc(dbuf_len, GFP_KERNEL);
} else {
dbuf = kmalloc(stmp_off + sbuf_len, GFP_KERNEL);
stmp = dbuf