/* * evdi_cursor.c * * Copyright (c) 2016 The Chromium OS Authors * Copyright (c) 2016 - 2017 DisplayLink (UK) Ltd. * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "evdi_cursor.h" #include "evdi_drv.h" /* * EVDI drm cursor private structure. */ struct evdi_cursor { bool enabled; int32_t x; int32_t y; uint32_t width; uint32_t height; int32_t hot_x; int32_t hot_y; uint32_t pixel_format; uint32_t stride; struct evdi_gem_object *obj; struct mutex lock; }; static void evdi_cursor_set_gem(struct evdi_cursor *cursor, struct evdi_gem_object *obj) { if (obj) drm_gem_object_reference(&obj->base); if (cursor->obj) drm_gem_object_unreference_unlocked(&cursor->obj->base); cursor->obj = obj; } struct evdi_gem_object *evdi_cursor_gem(struct evdi_cursor *cursor) { return cursor->obj; } int evdi_cursor_init(struct evdi_cursor **cursor) { if (WARN_ON(*cursor)) return -EINVAL; *cursor = kzalloc(sizeof(struct evdi_cursor), GFP_KERNEL); if (*cursor) { mutex_init(&(*cursor)->lock); return 0; } else { return -ENOMEM; } } void evdi_cursor_lock(struct evdi_cursor *cursor) { mutex_lock(&cursor->lock); } void evdi_cursor_unlock(struct evdi_cursor *cursor) { mutex_unlock(&cursor->lock); } void evdi_cursor_free(struct evdi_cursor *cursor) { if (WARN_ON(!cursor)) return; evdi_cursor_set_gem(cursor, NULL); kfree(cursor); } bool evdi_cursor_enabled(struct evdi_cursor *cursor) { return cursor->enabled; } void evdi_cursor_enable(struct evdi_cursor *cursor, bool enable) { evdi_cursor_lock(cursor); cursor->enabled = enable; if (!enable) evdi_cursor_set_gem(cursor, NULL); evdi_cursor_unlock(cursor); } void evdi_cursor_set(struct evdi_cursor *cursor, struct evdi_gem_object *obj, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y, uint32_t pixel_format, uint32_t stride) { int err = 0; evdi_cursor_lock(cursor); if (obj && !obj->vmapping) err = evdi_gem_vmap(obj); if (err != 0) { EVDI_ERROR("Failed to map cursor.\n"); obj = NULL; } cursor->enabled = obj != NULL; cursor->width = width; cursor->height = height; cursor->hot_x = hot_x; cursor->hot_y = hot_y; cursor->pixel_format = pixel_format; cursor->stride = stride; evdi_cursor_set_gem(cursor, obj); evdi_cursor_unlock(cursor); } void evdi_cursor_move(struct evdi_cursor *cursor, int32_t x, int32_t y) { evdi_cursor_lock(cursor); cursor->x = x; cursor->y = y; evdi_cursor_unlock(cursor); } static inline uint32_t blend_component(uint32_t pixel, uint32_t blend, uint32_t alpha) { uint32_t pre_blend = (pixel * (255 - alpha) + blend * alpha); return (pre_blend + ((pre_blend + 1) << 8)) >> 16; } static inline uint32_t blend_alpha(const uint32_t pixel_val32, uint32_t blend_val32) { uint32_t alpha = (blend_val32 >> 24); return blend_component(pixel_val32 & 0xff, blend_val32 & 0xff, alpha) | blend_component((pixel_val32 & 0xff00) >> 8, (blend_val32 & 0xff00) >> 8, alpha) << 8 | blend_component((pixel_val32 & 0xff0000) >> 16, (blend_val32 & 0xff0000) >> 16, alpha) << 16; } static int evdi_cursor_compose_pixel(char __user *buffer, int const cursor_value, int const fb_value, int cmd_offset) { int const composed_value = blend_alpha(fb_value, cursor_value); return copy_to_user(buffer + cmd_offset, &composed_value, 4); } int evdi_cursor_compose_and_copy(struct evdi_cursor *cursor, struct evdi_framebuffer *ufb, char __user *buffer, int buf_byte_stride) { int x, y; struct drm_framebuffer *fb = &ufb->base; const int h_cursor_w = cursor->width >> 1; const int h_cursor_h = cursor->height >> 1; uint32_t *cursor_buffer = NULL; uint32_t bytespp = 0; if (!cursor->enabled) return 0; if (!cursor->obj) return -EINVAL; if (!cursor->obj->vmapping) return -EINVAL; bytespp = evdi_fb_get_bpp(cursor->pixel_format); bytespp = DIV_ROUND_UP(bytespp, 8); if (bytespp != 4) { EVDI_ERROR("Unsupported cursor format bpp=%u\n", bytespp); return -EINVAL; } if (cursor->width * cursor->height * bytespp > cursor->obj->base.size){ EVDI_ERROR("Wrong cursor size\n"); return -EINVAL; } cursor_buffer = (uint32_t *)cursor->obj->vmapping; for (y = -h_cursor_h; y < h_cursor_h; ++y) { for (x = -h_cursor_w; x < h_cursor_w; ++x) { uint32_t curs_val; int *fbsrc; int fb_value; int cmd_offset; int cursor_pix; int const mouse_pix_x = cursor->x + x + h_cursor_w; int const mouse_pix_y = cursor->y + y + h_cursor_h; bool const is_pix_sane = mouse_pix_x >= 0 && mouse_pix_y >= 0 && mouse_pix_x < fb->width && mouse_pix_y < fb->height; if (!is_pix_sane) continue; cursor_pix = h_cursor_w+x + (h_cursor_h+y)*cursor->width; curs_val = le32_to_cpu(cursor_buffer[cursor_pix]); fbsrc = (int *)ufb->obj->vmapping; fb_value = *(fbsrc + ((fb->pitches[0]>>2) * mouse_pix_y + mouse_pix_x)); cmd_offset = (buf_byte_stride * mouse_pix_y) + (mouse_pix_x * bytespp); if (evdi_cursor_compose_pixel(buffer, curs_val, fb_value, cmd_offset)) { EVDI_ERROR("Failed to compose cursor pixel\n"); return -EFAULT; } } } return 0; } void evdi_cursor_position(struct evdi_cursor *cursor, int32_t *x, int32_t *y) { *x = cursor->x; *y = cursor->y; } void evdi_cursor_hotpoint(struct evdi_cursor *cursor, int32_t *hot_x, int32_t *hot_y) { *hot_x = cursor->hot_x; *hot_y = cursor->hot_y; } void evdi_cursor_size(struct evdi_cursor *cursor, uint32_t *width, uint32_t *height) { *width = cursor->width; *height = cursor->height; } void evdi_cursor_format(struct evdi_cursor *cursor, uint32_t *format) { *format = cursor->pixel_format; } void evdi_cursor_stride(struct evdi_cursor *cursor, uint32_t *stride) { *stride = cursor->stride; }