linux/drivers/gpu/drm/udl/udl_fb.c

219 lines
5.3 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Red Hat
*
* based in parts on udlfb.c:
* Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
* Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
* Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
*/
#include <linux/moduleparam.h>
#include <linux/dma-buf.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_modeset_helper.h>
#include "udl_drv.h"
/** Read the red component (0..255) of a 32 bpp colour. */
#define DLO_RGB_GETRED(col) (uint8_t)((col) & 0xFF)
/** Read the green component (0..255) of a 32 bpp colour. */
#define DLO_RGB_GETGRN(col) (uint8_t)(((col) >> 8) & 0xFF)
/** Read the blue component (0..255) of a 32 bpp colour. */
#define DLO_RGB_GETBLU(col) (uint8_t)(((col) >> 16) & 0xFF)
/** Return red/green component of a 16 bpp colour number. */
#define DLO_RG16(red, grn) (uint8_t)((((red) & 0xF8) | ((grn) >> 5)) & 0xFF)
/** Return green/blue component of a 16 bpp colour number. */
#define DLO_GB16(grn, blu) (uint8_t)(((((grn) & 0x1C) << 3) | ((blu) >> 3)) & 0xFF)
/** Return 8 bpp colour number from red, green and blue components. */
#define DLO_RGB8(red, grn, blu) ((((red) << 5) | (((grn) & 3) << 3) | ((blu) & 7)) & 0xFF)
#if 0
static uint8_t rgb8(uint32_t col)
{
uint8_t red = DLO_RGB_GETRED(col);
uint8_t grn = DLO_RGB_GETGRN(col);
uint8_t blu = DLO_RGB_GETBLU(col);
return DLO_RGB8(red, grn, blu);
}
static uint16_t rgb16(uint32_t col)
{
uint8_t red = DLO_RGB_GETRED(col);
uint8_t grn = DLO_RGB_GETGRN(col);
uint8_t blu = DLO_RGB_GETBLU(col);
return (DLO_RG16(red, grn) << 8) + DLO_GB16(grn, blu);
}
#endif
static long udl_log_cpp(unsigned int cpp)
{
if (WARN_ON(!is_power_of_2(cpp)))
return -EINVAL;
return __ffs(cpp);
}
static int udl_aligned_damage_clip(struct drm_rect *clip, int x, int y,
int width, int height)
{
int x1, x2;
if (WARN_ON_ONCE(x < 0) ||
WARN_ON_ONCE(y < 0) ||
WARN_ON_ONCE(width < 0) ||
WARN_ON_ONCE(height < 0))
return -EINVAL;
x1 = ALIGN_DOWN(x, sizeof(unsigned long));
x2 = ALIGN(width + (x - x1), sizeof(unsigned long)) + x1;
clip->x1 = x1;
clip->y1 = y;
clip->x2 = x2;
clip->y2 = y + height;
return 0;
}
int udl_handle_damage(struct drm_framebuffer *fb, int x, int y,
int width, int height)
{
struct drm_device *dev = fb->dev;
struct udl_device *udl = to_udl(dev);
int i, ret;
char *cmd;
struct urb *urb;
struct drm_rect clip;
int log_bpp;
void *vaddr;
spin_lock(&udl->active_fb_16_lock);
if (udl->active_fb_16 != fb) {
spin_unlock(&udl->active_fb_16_lock);
return 0;
}
spin_unlock(&udl->active_fb_16_lock);
ret = udl_log_cpp(fb->format->cpp[0]);
if (ret < 0)
return ret;
log_bpp = ret;
ret = udl_aligned_damage_clip(&clip, x, y, width, height);
if (ret)
return ret;
else if ((clip.x2 > fb->width) || (clip.y2 > fb->height))
return -EINVAL;
vaddr = drm_gem_shmem_vmap(fb->obj[0]);
if (IS_ERR(vaddr)) {
DRM_ERROR("failed to vmap fb\n");
return 0;
}
urb = udl_get_urb(dev);
if (!urb)
goto out;
cmd = urb->transfer_buffer;
for (i = clip.y1; i < clip.y2; i++) {
const int line_offset = fb->pitches[0] * i;
const int byte_offset = line_offset + (clip.x1 << log_bpp);
const int dev_byte_offset = (fb->width * i + clip.x1) << log_bpp;
const int byte_width = (clip.x2 - clip.x1) << log_bpp;
if (udl_render_hline(dev, log_bpp, &urb, (char *)vaddr,
&cmd, byte_offset, dev_byte_offset,
byte_width))
goto out;
}
if (cmd > (char *) urb->transfer_buffer) {
/* Send partial buffer remaining before exiting */
int len;
if (cmd < (char *) urb->transfer_buffer + urb->transfer_buffer_length)
*cmd++ = 0xAF;
len = cmd - (char *) urb->transfer_buffer;
ret = udl_submit_urb(dev, urb, len);
} else
udl_urb_completion(urb);
out:
drm_gem_shmem_vunmap(fb->obj[0], vaddr);
return 0;
}
static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb,
struct drm_file *file,
unsigned flags, unsigned color,
struct drm_clip_rect *clips,
unsigned num_clips)
{
struct udl_device *udl = fb->dev->dev_private;
struct dma_buf_attachment *import_attach;
int i;
int ret = 0;
drm_modeset_lock_all(fb->dev);
spin_lock(&udl->active_fb_16_lock);
if (udl->active_fb_16 != fb) {
spin_unlock(&udl->active_fb_16_lock);
goto unlock;
}
spin_unlock(&udl->active_fb_16_lock);
import_attach = fb->obj[0]->import_attach;
if (import_attach) {
ret = dma_buf_begin_cpu_access(import_attach->dmabuf,
DMA_FROM_DEVICE);
if (ret)
goto unlock;
}
for (i = 0; i < num_clips; i++) {
ret = udl_handle_damage(fb, clips[i].x1, clips[i].y1,
clips[i].x2 - clips[i].x1,
clips[i].y2 - clips[i].y1);
if (ret)
break;
}
if (import_attach)
ret = dma_buf_end_cpu_access(import_attach->dmabuf,
dma-buf, drm, ion: Propagate error code from dma_buf_start_cpu_access() Drivers, especially i915.ko, can fail during the initial migration of a dma-buf for CPU access. However, the error code from the driver was not being propagated back to ioctl and so userspace was blissfully ignorant of the failure. Rendering corruption ensues. Whilst fixing the ioctl to return the error code from dma_buf_start_cpu_access(), also do the same for dma_buf_end_cpu_access(). For most drivers, dma_buf_end_cpu_access() cannot fail. i915.ko however, as most drivers would, wants to avoid being uninterruptible (as would be required to guarrantee no failure when flushing the buffer to the device). As userspace already has to handle errors from the SYNC_IOCTL, take advantage of this to be able to restart the syscall across signals. This fixes a coherency issue for i915.ko as well as reducing the uninterruptible hold upon its BKL, the struct_mutex. Fixes commit c11e391da2a8fe973c3c2398452000bed505851e Author: Daniel Vetter <daniel.vetter@ffwll.ch> Date: Thu Feb 11 20:04:51 2016 -0200 dma-buf: Add ioctls to allow userspace to flush Testcase: igt/gem_concurrent_blit/*dmabuf*interruptible Testcase: igt/prime_mmap_coherency/ioctl-errors Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Tiago Vignatti <tiago.vignatti@intel.com> Cc: Stéphane Marchesin <marcheu@chromium.org> Cc: David Herrmann <dh.herrmann@gmail.com> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Daniel Vetter <daniel.vetter@intel.com> CC: linux-media@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org Cc: intel-gfx@lists.freedesktop.org Cc: devel@driverdev.osuosl.org Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: http://patchwork.freedesktop.org/patch/msgid/1458331359-2634-1-git-send-email-chris@chris-wilson.co.uk
2016-03-18 20:02:39 +00:00
DMA_FROM_DEVICE);
unlock:
drm_modeset_unlock_all(fb->dev);
return ret;
}
static const struct drm_framebuffer_funcs udlfb_funcs = {
.destroy = drm_gem_fb_destroy,
.create_handle = drm_gem_fb_create_handle,
.dirty = udl_user_framebuffer_dirty,
};
struct drm_framebuffer *
udl_fb_user_fb_create(struct drm_device *dev,
struct drm_file *file,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
return drm_gem_fb_create_with_funcs(dev, file, mode_cmd,
&udlfb_funcs);
}