@@ -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
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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
@@ -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);
@@ -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;
}
@@ -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) {