From: Douglas Anderson <dianders@xxxxxxxxxxxx> Port applicable parts of CrOS udl cursor implementation from 5.4 CrOS kernel fork. - removed legacy non-atomic udl_cursor_move, udl_cursor_set ioctl's implementation - modified udl_cursor_download to copy cursor content to the buffer from iosys_map - removed unnecessary cursor copy in udl_handle_damage - simplify code by making struct udl_cursor public Cursor was tested on ChromeOS and Ubuntu 22.04 with gnome-wayland. (cherry picked from commit efb4c23afa3e1de185a1a4f8ff5b7ec412aec0fe) ChromiumOS fork at https://chromium.googlesource.com/chromiumos/third_party/kernel) Signed-off-by: Haixia Shi <hshi@xxxxxxxxxxxx> Signed-off-by: Ross Zwisler <zwisler@xxxxxxxxxx> Signed-off-by: Douglas Anderson <dianders@xxxxxxxxxxxx> Signed-off-by: Guenter Roeck <groeck@xxxxxxxxxxxx> Signed-off-by: Łukasz Spintzyk <Lukasz.Spintzyk@xxxxxxxxxxxxx> --- drivers/gpu/drm/udl/Makefile | 2 +- drivers/gpu/drm/udl/udl_cursor.c | 78 ++++++++++++++++++++++++++++++ drivers/gpu/drm/udl/udl_cursor.h | 47 ++++++++++++++++++ drivers/gpu/drm/udl/udl_drv.h | 6 ++- drivers/gpu/drm/udl/udl_modeset.c | 9 +++- drivers/gpu/drm/udl/udl_transfer.c | 36 +++++++++++++- 6 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 drivers/gpu/drm/udl/udl_cursor.c create mode 100644 drivers/gpu/drm/udl/udl_cursor.h diff --git a/drivers/gpu/drm/udl/Makefile b/drivers/gpu/drm/udl/Makefile index 3f6db179455d..0abd04b0e829 100644 --- a/drivers/gpu/drm/udl/Makefile +++ b/drivers/gpu/drm/udl/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -udl-y := udl_drv.o udl_modeset.o udl_main.o udl_transfer.o +udl-y := udl_drv.o udl_modeset.o udl_main.o udl_transfer.o udl_cursor.o obj-$(CONFIG_DRM_UDL) := udl.o diff --git a/drivers/gpu/drm/udl/udl_cursor.c b/drivers/gpu/drm/udl/udl_cursor.c new file mode 100644 index 000000000000..594bb3b6b056 --- /dev/null +++ b/drivers/gpu/drm/udl/udl_cursor.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * udl_cursor.c + * + * Copyright (c) 2015 The Chromium OS Authors + * + * 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 <http://www.gnu.org/licenses/>. + */ +#include <linux/iosys-map.h> +#include <drm/drm_crtc_helper.h> + +#include "udl_cursor.h" +#include "udl_drv.h" + +void udl_cursor_get_hline(struct udl_cursor *cursor, int x, int y, + struct udl_cursor_hline *hline) +{ + if (!cursor || !cursor->enabled || + x >= cursor->x + UDL_CURSOR_W || + y < cursor->y || y >= cursor->y + UDL_CURSOR_H) { + hline->buffer = NULL; + return; + } + + hline->buffer = &cursor->buffer[UDL_CURSOR_W * (y - cursor->y)]; + hline->width = UDL_CURSOR_W; + hline->offset = x - cursor->x; +} + +/* + * Return pre-computed cursor blend value defined as: + * R: 5 bits (bit 0:4) + * G: 6 bits (bit 5:10) + * B: 5 bits (bit 11:15) + * A: 7 bits (bit 16:22) + */ +static uint32_t cursor_blend_val32(uint32_t pix) +{ + /* range of alpha_scaled is 0..64 */ + uint32_t alpha_scaled = ((pix >> 24) * 65) >> 8; + + return ((pix >> 3) & 0x1f) | + ((pix >> 5) & 0x7e0) | + ((pix >> 8) & 0xf800) | + (alpha_scaled << 16); +} + +int udl_cursor_download(struct udl_cursor *cursor, + const struct iosys_map *map) +{ + uint32_t *src_ptr, *dst_ptr; + size_t i; + + src_ptr = map->vaddr; + dst_ptr = cursor->buffer; + for (i = 0; i < UDL_CURSOR_BUF; ++i) + dst_ptr[i] = cursor_blend_val32(le32_to_cpu(src_ptr[i])); + return 0; +} + + +int udl_cursor_move(struct udl_cursor *cursor, int x, int y) +{ + cursor->x = x; + cursor->y = y; + return 0; +} diff --git a/drivers/gpu/drm/udl/udl_cursor.h b/drivers/gpu/drm/udl/udl_cursor.h new file mode 100644 index 000000000000..6a848accc106 --- /dev/null +++ b/drivers/gpu/drm/udl/udl_cursor.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * udl_cursor.h + * + * Copyright (c) 2015 The Chromium OS Authors + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _UDL_CURSOR_H_ +#define _UDL_CURSOR_H_ + +#include <linux/module.h> +#include <drm/drm_crtc.h> + +#define UDL_CURSOR_W 64 +#define UDL_CURSOR_H 64 +#define UDL_CURSOR_BUF (UDL_CURSOR_W * UDL_CURSOR_H) +struct udl_cursor { + uint32_t buffer[UDL_CURSOR_BUF]; + bool enabled; + int x; + int y; +}; +struct udl_cursor_hline { + uint32_t *buffer; + int width; + int offset; +}; + +extern void udl_cursor_get_hline(struct udl_cursor *cursor, int x, int y, + struct udl_cursor_hline *hline); +extern int udl_cursor_move(struct udl_cursor *cursor, int x, int y); +extern int udl_cursor_download(struct udl_cursor *cursor, const struct iosys_map *map); + +#endif diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 282ebd6c02fd..ccd813bec1a9 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -21,6 +21,7 @@ #include <drm/drm_framebuffer.h> #include <drm/drm_gem.h> #include <drm/drm_plane.h> +#include "udl_cursor.h" struct drm_mode_create_dumb; @@ -60,6 +61,7 @@ static inline struct udl_connector *to_udl_connector(struct drm_connector *conne return container_of(connector, struct udl_connector, connector); } +struct udl_cursor_hline; struct udl_device { struct drm_device drm; struct device *dev; @@ -74,6 +76,7 @@ struct udl_device { int sku_pixel_limit; struct urb_list urbs; + struct udl_cursor cursor; }; #define to_udl(x) container_of(x, struct udl_device, drm) @@ -97,7 +100,8 @@ int udl_init(struct udl_device *udl); int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, - u32 byte_offset, u32 device_byte_offset, u32 byte_width); + u32 byte_offset, u32 device_byte_offset, u32 byte_width, + struct udl_cursor_hline *cursor_hline); int udl_drop_usb(struct drm_device *dev); int udl_select_std_channel(struct udl_device *udl); diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index 7702359c90c2..21594144fec5 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -26,6 +26,9 @@ #include "udl_drv.h" #include "udl_proto.h" +#include "udl_cursor.h" + +#define UDL_COLOR_DEPTH_16BPP 0 /* * All DisplayLink bulk operations start with 0xaf (UDL_MSG_BULK), followed by @@ -204,6 +207,7 @@ static int udl_handle_damage(struct drm_framebuffer *fb, const struct drm_rect *clip) { struct drm_device *dev = fb->dev; + struct udl_device *udl = to_udl(dev); void *vaddr = map->vaddr; /* TODO: Use mapping abstraction properly */ int i, ret; char *cmd; @@ -225,9 +229,12 @@ static int udl_handle_damage(struct drm_framebuffer *fb, 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 = drm_rect_width(clip) << log_bpp; + struct udl_cursor_hline cursor_hline; + + udl_cursor_get_hline(&udl->cursor, clip->x1, i, &cursor_hline); ret = udl_render_hline(dev, log_bpp, &urb, (char *)vaddr, &cmd, byte_offset, dev_byte_offset, - byte_width); + byte_width, &cursor_hline); if (ret) return ret; } diff --git a/drivers/gpu/drm/udl/udl_transfer.c b/drivers/gpu/drm/udl/udl_transfer.c index 5ff1037a3453..ba3a3ae08943 100644 --- a/drivers/gpu/drm/udl/udl_transfer.c +++ b/drivers/gpu/drm/udl/udl_transfer.c @@ -11,6 +11,7 @@ #include "udl_drv.h" #include "udl_proto.h" +#include "udl_cursor.h" #define MAX_CMD_PIXELS 255 @@ -43,6 +44,19 @@ static inline u16 get_pixel_val16(const uint8_t *pixel, int log_bpp) return pixel_val16; } +static inline u16 blend_alpha(const uint16_t pixel_val16, uint32_t blend_val32) +{ + uint32_t alpha = (blend_val32 >> 16); + uint32_t alpha_inv = 64 - alpha; + + return (((pixel_val16 & 0x1f) * alpha_inv + + (blend_val32 & 0x1f) * alpha) >> 6) | + ((((pixel_val16 & 0x7e0) * alpha_inv + + (blend_val32 & 0x7e0) * alpha) >> 6) & 0x7e0) | + ((((pixel_val16 & 0xf800) * alpha_inv + + (blend_val32 & 0xf800) * alpha) >> 6) & 0xf800); +} + /* * Render a command stream for an encoded horizontal line segment of pixels. * @@ -74,6 +88,7 @@ static void udl_compress_hline16( const u8 **pixel_start_ptr, const u8 *const pixel_end, uint32_t *device_address_ptr, + struct udl_cursor_hline *cursor_hline, uint8_t **command_buffer_ptr, const uint8_t *const cmd_buffer_end, int log_bpp) { @@ -81,6 +96,9 @@ static void udl_compress_hline16( const u8 *pixel = *pixel_start_ptr; uint32_t dev_addr = *device_address_ptr; uint8_t *cmd = *command_buffer_ptr; + const uint32_t *cursor_buf = cursor_hline ? cursor_hline->buffer : NULL; + int cursor_pos = cursor_buf ? cursor_hline->offset : 0; + int cursor_width = cursor_buf ? cursor_hline->width : 0; while ((pixel_end > pixel) && (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) { @@ -107,6 +125,11 @@ static void udl_compress_hline16( (unsigned long)(cmd_buffer_end - 1 - cmd) / 2) << log_bpp); pixel_val16 = get_pixel_val16(pixel, log_bpp); + if (cursor_buf && cursor_pos >= 0 && + cursor_pos < cursor_width) { + pixel_val16 = blend_alpha(pixel_val16, + cursor_buf[cursor_pos]); + } while (pixel < cmd_pixel_end) { const u8 *const start = pixel; @@ -116,12 +139,19 @@ static void udl_compress_hline16( cmd += 2; pixel += bpp; + cursor_pos++; while (pixel < cmd_pixel_end) { pixel_val16 = get_pixel_val16(pixel, log_bpp); + if (cursor_buf && cursor_pos >= 0 && + cursor_pos < cursor_width) { + pixel_val16 = blend_alpha(pixel_val16, + cursor_buf[cursor_pos]); + } if (pixel_val16 != repeating_pixel_val16) break; pixel += bpp; + cursor_pos++; } if (unlikely(pixel > start + bpp)) { @@ -160,6 +190,8 @@ static void udl_compress_hline16( *command_buffer_ptr = cmd; *pixel_start_ptr = pixel; *device_address_ptr = dev_addr; + if (cursor_buf) + cursor_hline->offset = cursor_pos; return; } @@ -173,7 +205,7 @@ static void udl_compress_hline16( int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, const char *front, char **urb_buf_ptr, u32 byte_offset, u32 device_byte_offset, - u32 byte_width) + u32 byte_width, struct udl_cursor_hline *cursor_hline) { const u8 *line_start, *line_end, *next_pixel; u32 base16 = 0 + (device_byte_offset >> log_bpp) * 2; @@ -194,7 +226,7 @@ int udl_render_hline(struct drm_device *dev, int log_bpp, struct urb **urb_ptr, while (next_pixel < line_end) { udl_compress_hline16(&next_pixel, - line_end, &base16, + line_end, &base16, cursor_hline, (u8 **) &cmd, (u8 *) cmd_end, log_bpp); if (cmd >= cmd_end) { -- 2.34.1