140e1a70bSNoralf Trønnes // SPDX-License-Identifier: MIT
240e1a70bSNoralf Trønnes /*
340e1a70bSNoralf Trønnes * Copyright 2020 Noralf Trønnes
440e1a70bSNoralf Trønnes */
540e1a70bSNoralf Trønnes
640e1a70bSNoralf Trønnes #include <linux/lz4.h>
740e1a70bSNoralf Trønnes #include <linux/usb.h>
8c5738c86SNoralf Trønnes #include <linux/vmalloc.h>
940e1a70bSNoralf Trønnes #include <linux/workqueue.h>
1040e1a70bSNoralf Trønnes
1140e1a70bSNoralf Trønnes #include <drm/drm_atomic.h>
1240e1a70bSNoralf Trønnes #include <drm/drm_connector.h>
1340e1a70bSNoralf Trønnes #include <drm/drm_damage_helper.h>
1440e1a70bSNoralf Trønnes #include <drm/drm_drv.h>
1540e1a70bSNoralf Trønnes #include <drm/drm_format_helper.h>
1640e1a70bSNoralf Trønnes #include <drm/drm_fourcc.h>
1740e1a70bSNoralf Trønnes #include <drm/drm_framebuffer.h>
180029d318SThomas Zimmermann #include <drm/drm_gem.h>
19562fd7ccSNoralf Trønnes #include <drm/drm_gem_atomic_helper.h>
2008b7ef05SThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
2140e1a70bSNoralf Trønnes #include <drm/drm_print.h>
2240e1a70bSNoralf Trønnes #include <drm/drm_rect.h>
2340e1a70bSNoralf Trønnes #include <drm/drm_simple_kms_helper.h>
2440e1a70bSNoralf Trønnes #include <drm/gud.h>
2540e1a70bSNoralf Trønnes
2640e1a70bSNoralf Trønnes #include "gud_internal.h"
2740e1a70bSNoralf Trønnes
2840e1a70bSNoralf Trønnes /*
295ad8e63eSNoralf Trønnes * Some userspace rendering loops run all displays in the same loop.
30a0356899SNoralf Trønnes * This means that a fast display will have to wait for a slow one.
315ad8e63eSNoralf Trønnes * Such users might want to enable this module parameter.
32a0356899SNoralf Trønnes */
335ad8e63eSNoralf Trønnes static bool gud_async_flush;
34a0356899SNoralf Trønnes module_param_named(async_flush, gud_async_flush, bool, 0644);
355ad8e63eSNoralf Trønnes MODULE_PARM_DESC(async_flush, "Enable asynchronous flushing [default=0]");
36a0356899SNoralf Trønnes
37a0356899SNoralf Trønnes /*
3840e1a70bSNoralf Trønnes * FIXME: The driver is probably broken on Big Endian machines.
3940e1a70bSNoralf Trønnes * See discussion:
4040e1a70bSNoralf Trønnes * https://lore.kernel.org/dri-devel/CAKb7UvihLX0hgBOP3VBG7O+atwZcUVCPVuBdfmDMpg0NjXe-cQ@mail.gmail.com/
4140e1a70bSNoralf Trønnes */
4240e1a70bSNoralf Trønnes
gud_is_big_endian(void)4340e1a70bSNoralf Trønnes static bool gud_is_big_endian(void)
4440e1a70bSNoralf Trønnes {
4540e1a70bSNoralf Trønnes #if defined(__BIG_ENDIAN)
4640e1a70bSNoralf Trønnes return true;
4740e1a70bSNoralf Trønnes #else
4840e1a70bSNoralf Trønnes return false;
4940e1a70bSNoralf Trønnes #endif
5040e1a70bSNoralf Trønnes }
5140e1a70bSNoralf Trønnes
gud_xrgb8888_to_r124(u8 * dst,const struct drm_format_info * format,void * src,struct drm_framebuffer * fb,struct drm_rect * rect)5240e1a70bSNoralf Trønnes static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format,
5340e1a70bSNoralf Trønnes void *src, struct drm_framebuffer *fb,
5440e1a70bSNoralf Trønnes struct drm_rect *rect)
5540e1a70bSNoralf Trønnes {
5640e1a70bSNoralf Trønnes unsigned int block_width = drm_format_info_block_width(format, 0);
5740e1a70bSNoralf Trønnes unsigned int bits_per_pixel = 8 / block_width;
5840e1a70bSNoralf Trønnes unsigned int x, y, width, height;
5940e1a70bSNoralf Trønnes u8 pix, *pix8, *block = dst; /* Assign to silence compiler warning */
607bef6449SThomas Zimmermann struct iosys_map dst_map, vmap;
6140e1a70bSNoralf Trønnes size_t len;
6240e1a70bSNoralf Trønnes void *buf;
6340e1a70bSNoralf Trønnes
6440e1a70bSNoralf Trønnes WARN_ON_ONCE(format->char_per_block[0] != 1);
6540e1a70bSNoralf Trønnes
6640e1a70bSNoralf Trønnes /* Start on a byte boundary */
6740e1a70bSNoralf Trønnes rect->x1 = ALIGN_DOWN(rect->x1, block_width);
6840e1a70bSNoralf Trønnes width = drm_rect_width(rect);
6940e1a70bSNoralf Trønnes height = drm_rect_height(rect);
7040e1a70bSNoralf Trønnes len = drm_format_info_min_pitch(format, 0, width) * height;
7140e1a70bSNoralf Trønnes
7240e1a70bSNoralf Trønnes buf = kmalloc(width * height, GFP_KERNEL);
7340e1a70bSNoralf Trønnes if (!buf)
7440e1a70bSNoralf Trønnes return 0;
7540e1a70bSNoralf Trønnes
767bef6449SThomas Zimmermann iosys_map_set_vaddr(&dst_map, buf);
777bef6449SThomas Zimmermann iosys_map_set_vaddr(&vmap, src);
787bef6449SThomas Zimmermann drm_fb_xrgb8888_to_gray8(&dst_map, NULL, &vmap, fb, rect);
7940e1a70bSNoralf Trønnes pix8 = buf;
8040e1a70bSNoralf Trønnes
8140e1a70bSNoralf Trønnes for (y = 0; y < height; y++) {
8240e1a70bSNoralf Trønnes for (x = 0; x < width; x++) {
8340e1a70bSNoralf Trønnes unsigned int pixpos = x % block_width; /* within byte from the left */
8440e1a70bSNoralf Trønnes unsigned int pixshift = (block_width - pixpos - 1) * bits_per_pixel;
8540e1a70bSNoralf Trønnes
8640e1a70bSNoralf Trønnes if (!pixpos) {
8740e1a70bSNoralf Trønnes block = dst++;
8840e1a70bSNoralf Trønnes *block = 0;
8940e1a70bSNoralf Trønnes }
9040e1a70bSNoralf Trønnes
9140e1a70bSNoralf Trønnes pix = (*pix8++) >> (8 - bits_per_pixel);
9240e1a70bSNoralf Trønnes *block |= pix << pixshift;
9340e1a70bSNoralf Trønnes }
9440e1a70bSNoralf Trønnes }
9540e1a70bSNoralf Trønnes
9640e1a70bSNoralf Trønnes kfree(buf);
9740e1a70bSNoralf Trønnes
9840e1a70bSNoralf Trønnes return len;
9940e1a70bSNoralf Trønnes }
10040e1a70bSNoralf Trønnes
gud_xrgb8888_to_color(u8 * dst,const struct drm_format_info * format,void * src,struct drm_framebuffer * fb,struct drm_rect * rect)10140e1a70bSNoralf Trønnes static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *format,
10240e1a70bSNoralf Trønnes void *src, struct drm_framebuffer *fb,
10340e1a70bSNoralf Trønnes struct drm_rect *rect)
10440e1a70bSNoralf Trønnes {
10540e1a70bSNoralf Trønnes unsigned int block_width = drm_format_info_block_width(format, 0);
10640e1a70bSNoralf Trønnes unsigned int bits_per_pixel = 8 / block_width;
10740e1a70bSNoralf Trønnes u8 r, g, b, pix, *block = dst; /* Assign to silence compiler warning */
10840e1a70bSNoralf Trønnes unsigned int x, y, width;
109fa2a87e4SGeert Uytterhoeven __le32 *sbuf32;
110fa2a87e4SGeert Uytterhoeven u32 pix32;
11140e1a70bSNoralf Trønnes size_t len;
11240e1a70bSNoralf Trønnes
11340e1a70bSNoralf Trønnes /* Start on a byte boundary */
11440e1a70bSNoralf Trønnes rect->x1 = ALIGN_DOWN(rect->x1, block_width);
11540e1a70bSNoralf Trønnes width = drm_rect_width(rect);
11640e1a70bSNoralf Trønnes len = drm_format_info_min_pitch(format, 0, width) * drm_rect_height(rect);
11740e1a70bSNoralf Trønnes
11840e1a70bSNoralf Trønnes for (y = rect->y1; y < rect->y2; y++) {
119fa2a87e4SGeert Uytterhoeven sbuf32 = src + (y * fb->pitches[0]);
120fa2a87e4SGeert Uytterhoeven sbuf32 += rect->x1;
12140e1a70bSNoralf Trønnes
12240e1a70bSNoralf Trønnes for (x = 0; x < width; x++) {
12340e1a70bSNoralf Trønnes unsigned int pixpos = x % block_width; /* within byte from the left */
12440e1a70bSNoralf Trønnes unsigned int pixshift = (block_width - pixpos - 1) * bits_per_pixel;
12540e1a70bSNoralf Trønnes
12640e1a70bSNoralf Trønnes if (!pixpos) {
12740e1a70bSNoralf Trønnes block = dst++;
12840e1a70bSNoralf Trønnes *block = 0;
12940e1a70bSNoralf Trønnes }
13040e1a70bSNoralf Trønnes
131fa2a87e4SGeert Uytterhoeven pix32 = le32_to_cpu(*sbuf32++);
132fa2a87e4SGeert Uytterhoeven r = pix32 >> 16;
133fa2a87e4SGeert Uytterhoeven g = pix32 >> 8;
134fa2a87e4SGeert Uytterhoeven b = pix32;
13540e1a70bSNoralf Trønnes
13640e1a70bSNoralf Trønnes switch (format->format) {
13740e1a70bSNoralf Trønnes case GUD_DRM_FORMAT_XRGB1111:
13840e1a70bSNoralf Trønnes pix = ((r >> 7) << 2) | ((g >> 7) << 1) | (b >> 7);
13940e1a70bSNoralf Trønnes break;
14040e1a70bSNoralf Trønnes default:
14140e1a70bSNoralf Trønnes WARN_ON_ONCE(1);
14240e1a70bSNoralf Trønnes return len;
143166633c8Skernel test robot }
14440e1a70bSNoralf Trønnes
14540e1a70bSNoralf Trønnes *block |= pix << pixshift;
14640e1a70bSNoralf Trønnes }
14740e1a70bSNoralf Trønnes }
14840e1a70bSNoralf Trønnes
14940e1a70bSNoralf Trønnes return len;
15040e1a70bSNoralf Trønnes }
15140e1a70bSNoralf Trønnes
gud_prep_flush(struct gud_device * gdrm,struct drm_framebuffer * fb,const struct iosys_map * src,bool cached_reads,const struct drm_format_info * format,struct drm_rect * rect,struct gud_set_buffer_req * req)15240e1a70bSNoralf Trønnes static int gud_prep_flush(struct gud_device *gdrm, struct drm_framebuffer *fb,
153562fd7ccSNoralf Trønnes const struct iosys_map *src, bool cached_reads,
15440e1a70bSNoralf Trønnes const struct drm_format_info *format, struct drm_rect *rect,
15540e1a70bSNoralf Trønnes struct gud_set_buffer_req *req)
15640e1a70bSNoralf Trønnes {
15740e1a70bSNoralf Trønnes u8 compression = gdrm->compression;
158edbe262aSThomas Zimmermann struct iosys_map dst;
15940e1a70bSNoralf Trønnes void *vaddr, *buf;
16040e1a70bSNoralf Trønnes size_t pitch, len;
16140e1a70bSNoralf Trønnes
16240e1a70bSNoralf Trønnes pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(rect));
16340e1a70bSNoralf Trønnes len = pitch * drm_rect_height(rect);
16440e1a70bSNoralf Trønnes if (len > gdrm->bulk_len)
16540e1a70bSNoralf Trønnes return -E2BIG;
16640e1a70bSNoralf Trønnes
167562fd7ccSNoralf Trønnes vaddr = src[0].vaddr;
16840e1a70bSNoralf Trønnes retry:
16940e1a70bSNoralf Trønnes if (compression)
17040e1a70bSNoralf Trønnes buf = gdrm->compress_buf;
17140e1a70bSNoralf Trønnes else
17240e1a70bSNoralf Trønnes buf = gdrm->bulk_buf;
173edbe262aSThomas Zimmermann iosys_map_set_vaddr(&dst, buf);
17440e1a70bSNoralf Trønnes
17540e1a70bSNoralf Trønnes /*
17640e1a70bSNoralf Trønnes * Imported buffers are assumed to be write-combined and thus uncached
17740e1a70bSNoralf Trønnes * with slow reads (at least on ARM).
17840e1a70bSNoralf Trønnes */
17940e1a70bSNoralf Trønnes if (format != fb->format) {
18040e1a70bSNoralf Trønnes if (format->format == GUD_DRM_FORMAT_R1) {
18140e1a70bSNoralf Trønnes len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect);
182562fd7ccSNoralf Trønnes if (!len)
183562fd7ccSNoralf Trønnes return -ENOMEM;
1844cabfedcSNoralf Trønnes } else if (format->format == DRM_FORMAT_R8) {
185562fd7ccSNoralf Trønnes drm_fb_xrgb8888_to_gray8(&dst, NULL, src, fb, rect);
1861f25d005SNoralf Trønnes } else if (format->format == DRM_FORMAT_RGB332) {
187562fd7ccSNoralf Trønnes drm_fb_xrgb8888_to_rgb332(&dst, NULL, src, fb, rect);
18840e1a70bSNoralf Trønnes } else if (format->format == DRM_FORMAT_RGB565) {
189562fd7ccSNoralf Trønnes drm_fb_xrgb8888_to_rgb565(&dst, NULL, src, fb, rect,
190ab298c29SThomas Zimmermann gud_is_big_endian());
19183d7b6d5SNoralf Trønnes } else if (format->format == DRM_FORMAT_RGB888) {
192562fd7ccSNoralf Trønnes drm_fb_xrgb8888_to_rgb888(&dst, NULL, src, fb, rect);
19340e1a70bSNoralf Trønnes } else {
19440e1a70bSNoralf Trønnes len = gud_xrgb8888_to_color(buf, format, vaddr, fb, rect);
19540e1a70bSNoralf Trønnes }
19640e1a70bSNoralf Trønnes } else if (gud_is_big_endian() && format->cpp[0] > 1) {
197562fd7ccSNoralf Trønnes drm_fb_swab(&dst, NULL, src, fb, rect, cached_reads);
198562fd7ccSNoralf Trønnes } else if (compression && cached_reads && pitch == fb->pitches[0]) {
19940e1a70bSNoralf Trønnes /* can compress directly from the framebuffer */
20040e1a70bSNoralf Trønnes buf = vaddr + rect->y1 * pitch;
20140e1a70bSNoralf Trønnes } else {
202562fd7ccSNoralf Trønnes drm_fb_memcpy(&dst, NULL, src, fb, rect);
20340e1a70bSNoralf Trønnes }
20440e1a70bSNoralf Trønnes
20540e1a70bSNoralf Trønnes memset(req, 0, sizeof(*req));
20640e1a70bSNoralf Trønnes req->x = cpu_to_le32(rect->x1);
20740e1a70bSNoralf Trønnes req->y = cpu_to_le32(rect->y1);
20840e1a70bSNoralf Trønnes req->width = cpu_to_le32(drm_rect_width(rect));
20940e1a70bSNoralf Trønnes req->height = cpu_to_le32(drm_rect_height(rect));
21040e1a70bSNoralf Trønnes req->length = cpu_to_le32(len);
21140e1a70bSNoralf Trønnes
21240e1a70bSNoralf Trønnes if (compression & GUD_COMPRESSION_LZ4) {
21340e1a70bSNoralf Trønnes int complen;
21440e1a70bSNoralf Trønnes
21540e1a70bSNoralf Trønnes complen = LZ4_compress_default(buf, gdrm->bulk_buf, len, len, gdrm->lz4_comp_mem);
21640e1a70bSNoralf Trønnes if (complen <= 0) {
21740e1a70bSNoralf Trønnes compression = 0;
21840e1a70bSNoralf Trønnes goto retry;
21940e1a70bSNoralf Trønnes }
22040e1a70bSNoralf Trønnes
22140e1a70bSNoralf Trønnes req->compression = GUD_COMPRESSION_LZ4;
22240e1a70bSNoralf Trønnes req->compressed_length = cpu_to_le32(complen);
22340e1a70bSNoralf Trønnes }
22440e1a70bSNoralf Trønnes
225562fd7ccSNoralf Trønnes return 0;
22640e1a70bSNoralf Trønnes }
22740e1a70bSNoralf Trønnes
2282eecd93bSNoralf Trønnes struct gud_usb_bulk_context {
2292eecd93bSNoralf Trønnes struct timer_list timer;
2302eecd93bSNoralf Trønnes struct usb_sg_request sgr;
2312eecd93bSNoralf Trønnes };
2322eecd93bSNoralf Trønnes
gud_usb_bulk_timeout(struct timer_list * t)2332eecd93bSNoralf Trønnes static void gud_usb_bulk_timeout(struct timer_list *t)
2342eecd93bSNoralf Trønnes {
2352eecd93bSNoralf Trønnes struct gud_usb_bulk_context *ctx = from_timer(ctx, t, timer);
2362eecd93bSNoralf Trønnes
2372eecd93bSNoralf Trønnes usb_sg_cancel(&ctx->sgr);
2382eecd93bSNoralf Trønnes }
2392eecd93bSNoralf Trønnes
gud_usb_bulk(struct gud_device * gdrm,size_t len)2402eecd93bSNoralf Trønnes static int gud_usb_bulk(struct gud_device *gdrm, size_t len)
2412eecd93bSNoralf Trønnes {
2422eecd93bSNoralf Trønnes struct gud_usb_bulk_context ctx;
2432eecd93bSNoralf Trønnes int ret;
2442eecd93bSNoralf Trønnes
2452eecd93bSNoralf Trønnes ret = usb_sg_init(&ctx.sgr, gud_to_usb_device(gdrm), gdrm->bulk_pipe, 0,
2462eecd93bSNoralf Trønnes gdrm->bulk_sgt.sgl, gdrm->bulk_sgt.nents, len, GFP_KERNEL);
2472eecd93bSNoralf Trønnes if (ret)
2482eecd93bSNoralf Trønnes return ret;
2492eecd93bSNoralf Trønnes
2502eecd93bSNoralf Trønnes timer_setup_on_stack(&ctx.timer, gud_usb_bulk_timeout, 0);
2512eecd93bSNoralf Trønnes mod_timer(&ctx.timer, jiffies + msecs_to_jiffies(3000));
2522eecd93bSNoralf Trønnes
2532eecd93bSNoralf Trønnes usb_sg_wait(&ctx.sgr);
2542eecd93bSNoralf Trønnes
2552eecd93bSNoralf Trønnes if (!del_timer_sync(&ctx.timer))
2562eecd93bSNoralf Trønnes ret = -ETIMEDOUT;
2572eecd93bSNoralf Trønnes else if (ctx.sgr.status < 0)
2582eecd93bSNoralf Trønnes ret = ctx.sgr.status;
2592eecd93bSNoralf Trønnes else if (ctx.sgr.bytes != len)
2602eecd93bSNoralf Trønnes ret = -EIO;
2612eecd93bSNoralf Trønnes
2622eecd93bSNoralf Trønnes destroy_timer_on_stack(&ctx.timer);
2632eecd93bSNoralf Trønnes
2642eecd93bSNoralf Trønnes return ret;
2652eecd93bSNoralf Trønnes }
2662eecd93bSNoralf Trønnes
gud_flush_rect(struct gud_device * gdrm,struct drm_framebuffer * fb,const struct iosys_map * src,bool cached_reads,const struct drm_format_info * format,struct drm_rect * rect)26740e1a70bSNoralf Trønnes static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb,
268562fd7ccSNoralf Trønnes const struct iosys_map *src, bool cached_reads,
26940e1a70bSNoralf Trønnes const struct drm_format_info *format, struct drm_rect *rect)
27040e1a70bSNoralf Trønnes {
27140e1a70bSNoralf Trønnes struct gud_set_buffer_req req;
27240e1a70bSNoralf Trønnes size_t len, trlen;
2732eecd93bSNoralf Trønnes int ret;
27440e1a70bSNoralf Trønnes
27540e1a70bSNoralf Trønnes drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
27640e1a70bSNoralf Trønnes
277562fd7ccSNoralf Trønnes ret = gud_prep_flush(gdrm, fb, src, cached_reads, format, rect, &req);
27840e1a70bSNoralf Trønnes if (ret)
27940e1a70bSNoralf Trønnes return ret;
28040e1a70bSNoralf Trønnes
28140e1a70bSNoralf Trønnes len = le32_to_cpu(req.length);
28240e1a70bSNoralf Trønnes
28340e1a70bSNoralf Trønnes if (req.compression)
28440e1a70bSNoralf Trønnes trlen = le32_to_cpu(req.compressed_length);
28540e1a70bSNoralf Trønnes else
28640e1a70bSNoralf Trønnes trlen = len;
28740e1a70bSNoralf Trønnes
28840e1a70bSNoralf Trønnes gdrm->stats_length += len;
28940e1a70bSNoralf Trønnes /* Did it wrap around? */
29040e1a70bSNoralf Trønnes if (gdrm->stats_length <= len && gdrm->stats_actual_length) {
29140e1a70bSNoralf Trønnes gdrm->stats_length = len;
29240e1a70bSNoralf Trønnes gdrm->stats_actual_length = 0;
29340e1a70bSNoralf Trønnes }
29440e1a70bSNoralf Trønnes gdrm->stats_actual_length += trlen;
29540e1a70bSNoralf Trønnes
29640e1a70bSNoralf Trønnes if (!(gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE) || gdrm->prev_flush_failed) {
29740e1a70bSNoralf Trønnes ret = gud_usb_set(gdrm, GUD_REQ_SET_BUFFER, 0, &req, sizeof(req));
29840e1a70bSNoralf Trønnes if (ret)
29940e1a70bSNoralf Trønnes return ret;
30040e1a70bSNoralf Trønnes }
30140e1a70bSNoralf Trønnes
3022eecd93bSNoralf Trønnes ret = gud_usb_bulk(gdrm, trlen);
30340e1a70bSNoralf Trønnes if (ret)
30440e1a70bSNoralf Trønnes gdrm->stats_num_errors++;
30540e1a70bSNoralf Trønnes
30640e1a70bSNoralf Trønnes return ret;
30740e1a70bSNoralf Trønnes }
30840e1a70bSNoralf Trønnes
gud_clear_damage(struct gud_device * gdrm)30940e1a70bSNoralf Trønnes void gud_clear_damage(struct gud_device *gdrm)
31040e1a70bSNoralf Trønnes {
31140e1a70bSNoralf Trønnes gdrm->damage.x1 = INT_MAX;
31240e1a70bSNoralf Trønnes gdrm->damage.y1 = INT_MAX;
31340e1a70bSNoralf Trønnes gdrm->damage.x2 = 0;
31440e1a70bSNoralf Trønnes gdrm->damage.y2 = 0;
31540e1a70bSNoralf Trønnes }
31640e1a70bSNoralf Trønnes
gud_flush_damage(struct gud_device * gdrm,struct drm_framebuffer * fb,const struct iosys_map * src,bool cached_reads,struct drm_rect * damage)317754a6ca8SNoralf Trønnes static void gud_flush_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
318562fd7ccSNoralf Trønnes const struct iosys_map *src, bool cached_reads,
319754a6ca8SNoralf Trønnes struct drm_rect *damage)
320754a6ca8SNoralf Trønnes {
321754a6ca8SNoralf Trønnes const struct drm_format_info *format;
322754a6ca8SNoralf Trønnes unsigned int i, lines;
323754a6ca8SNoralf Trønnes size_t pitch;
324754a6ca8SNoralf Trønnes int ret;
325754a6ca8SNoralf Trønnes
326754a6ca8SNoralf Trønnes format = fb->format;
327754a6ca8SNoralf Trønnes if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
328754a6ca8SNoralf Trønnes format = gdrm->xrgb8888_emulation_format;
329754a6ca8SNoralf Trønnes
330754a6ca8SNoralf Trønnes /* Split update if it's too big */
331754a6ca8SNoralf Trønnes pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(damage));
332754a6ca8SNoralf Trønnes lines = drm_rect_height(damage);
333754a6ca8SNoralf Trønnes
334754a6ca8SNoralf Trønnes if (gdrm->bulk_len < lines * pitch)
335754a6ca8SNoralf Trønnes lines = gdrm->bulk_len / pitch;
336754a6ca8SNoralf Trønnes
337754a6ca8SNoralf Trønnes for (i = 0; i < DIV_ROUND_UP(drm_rect_height(damage), lines); i++) {
338754a6ca8SNoralf Trønnes struct drm_rect rect = *damage;
339754a6ca8SNoralf Trønnes
340754a6ca8SNoralf Trønnes rect.y1 += i * lines;
341754a6ca8SNoralf Trønnes rect.y2 = min_t(u32, rect.y1 + lines, damage->y2);
342754a6ca8SNoralf Trønnes
343562fd7ccSNoralf Trønnes ret = gud_flush_rect(gdrm, fb, src, cached_reads, format, &rect);
344754a6ca8SNoralf Trønnes if (ret) {
345754a6ca8SNoralf Trønnes if (ret != -ENODEV && ret != -ECONNRESET &&
346754a6ca8SNoralf Trønnes ret != -ESHUTDOWN && ret != -EPROTO)
347754a6ca8SNoralf Trønnes dev_err_ratelimited(fb->dev->dev,
348754a6ca8SNoralf Trønnes "Failed to flush framebuffer: error=%d\n", ret);
349754a6ca8SNoralf Trønnes gdrm->prev_flush_failed = true;
350754a6ca8SNoralf Trønnes break;
351754a6ca8SNoralf Trønnes }
352754a6ca8SNoralf Trønnes }
353754a6ca8SNoralf Trønnes }
354754a6ca8SNoralf Trønnes
gud_flush_work(struct work_struct * work)35540e1a70bSNoralf Trønnes void gud_flush_work(struct work_struct *work)
35640e1a70bSNoralf Trønnes {
35740e1a70bSNoralf Trønnes struct gud_device *gdrm = container_of(work, struct gud_device, work);
358c17d0486SNoralf Trønnes struct iosys_map shadow_map;
35940e1a70bSNoralf Trønnes struct drm_framebuffer *fb;
36040e1a70bSNoralf Trønnes struct drm_rect damage;
361c17d0486SNoralf Trønnes int idx;
36240e1a70bSNoralf Trønnes
36340e1a70bSNoralf Trønnes if (!drm_dev_enter(&gdrm->drm, &idx))
36440e1a70bSNoralf Trønnes return;
36540e1a70bSNoralf Trønnes
36640e1a70bSNoralf Trønnes mutex_lock(&gdrm->damage_lock);
36740e1a70bSNoralf Trønnes fb = gdrm->fb;
36840e1a70bSNoralf Trønnes gdrm->fb = NULL;
369c17d0486SNoralf Trønnes iosys_map_set_vaddr(&shadow_map, gdrm->shadow_buf);
37040e1a70bSNoralf Trønnes damage = gdrm->damage;
37140e1a70bSNoralf Trønnes gud_clear_damage(gdrm);
37240e1a70bSNoralf Trønnes mutex_unlock(&gdrm->damage_lock);
37340e1a70bSNoralf Trønnes
37440e1a70bSNoralf Trønnes if (!fb)
37540e1a70bSNoralf Trønnes goto out;
37640e1a70bSNoralf Trønnes
377c17d0486SNoralf Trønnes gud_flush_damage(gdrm, fb, &shadow_map, true, &damage);
37840e1a70bSNoralf Trønnes
37940e1a70bSNoralf Trønnes drm_framebuffer_put(fb);
38040e1a70bSNoralf Trønnes out:
38140e1a70bSNoralf Trønnes drm_dev_exit(idx);
38240e1a70bSNoralf Trønnes }
38340e1a70bSNoralf Trønnes
gud_fb_queue_damage(struct gud_device * gdrm,struct drm_framebuffer * fb,const struct iosys_map * src,struct drm_rect * damage)384c17d0486SNoralf Trønnes static int gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
385c17d0486SNoralf Trønnes const struct iosys_map *src, struct drm_rect *damage)
38640e1a70bSNoralf Trønnes {
38740e1a70bSNoralf Trønnes struct drm_framebuffer *old_fb = NULL;
388c17d0486SNoralf Trønnes struct iosys_map shadow_map;
38940e1a70bSNoralf Trønnes
39040e1a70bSNoralf Trønnes mutex_lock(&gdrm->damage_lock);
39140e1a70bSNoralf Trønnes
392c17d0486SNoralf Trønnes if (!gdrm->shadow_buf) {
393*b0fb8b69SJulia Lawall gdrm->shadow_buf = vcalloc(fb->pitches[0], fb->height);
394c17d0486SNoralf Trønnes if (!gdrm->shadow_buf) {
395c17d0486SNoralf Trønnes mutex_unlock(&gdrm->damage_lock);
396c17d0486SNoralf Trønnes return -ENOMEM;
397c17d0486SNoralf Trønnes }
398c17d0486SNoralf Trønnes }
399c17d0486SNoralf Trønnes
400c17d0486SNoralf Trønnes iosys_map_set_vaddr(&shadow_map, gdrm->shadow_buf);
401c17d0486SNoralf Trønnes iosys_map_incr(&shadow_map, drm_fb_clip_offset(fb->pitches[0], fb->format, damage));
402c17d0486SNoralf Trønnes drm_fb_memcpy(&shadow_map, fb->pitches, src, fb, damage);
403c17d0486SNoralf Trønnes
40440e1a70bSNoralf Trønnes if (fb != gdrm->fb) {
40540e1a70bSNoralf Trønnes old_fb = gdrm->fb;
40640e1a70bSNoralf Trønnes drm_framebuffer_get(fb);
40740e1a70bSNoralf Trønnes gdrm->fb = fb;
40840e1a70bSNoralf Trønnes }
40940e1a70bSNoralf Trønnes
410f531d198SNoralf Trønnes gdrm->damage.x1 = min(gdrm->damage.x1, damage->x1);
411f531d198SNoralf Trønnes gdrm->damage.y1 = min(gdrm->damage.y1, damage->y1);
412f531d198SNoralf Trønnes gdrm->damage.x2 = max(gdrm->damage.x2, damage->x2);
413f531d198SNoralf Trønnes gdrm->damage.y2 = max(gdrm->damage.y2, damage->y2);
41440e1a70bSNoralf Trønnes
41540e1a70bSNoralf Trønnes mutex_unlock(&gdrm->damage_lock);
41640e1a70bSNoralf Trønnes
41740e1a70bSNoralf Trønnes queue_work(system_long_wq, &gdrm->work);
41840e1a70bSNoralf Trønnes
41940e1a70bSNoralf Trønnes if (old_fb)
42040e1a70bSNoralf Trønnes drm_framebuffer_put(old_fb);
421c17d0486SNoralf Trønnes
422c17d0486SNoralf Trønnes return 0;
423c17d0486SNoralf Trønnes }
424c17d0486SNoralf Trønnes
gud_fb_handle_damage(struct gud_device * gdrm,struct drm_framebuffer * fb,const struct iosys_map * src,struct drm_rect * damage)425c17d0486SNoralf Trønnes static void gud_fb_handle_damage(struct gud_device *gdrm, struct drm_framebuffer *fb,
426c17d0486SNoralf Trønnes const struct iosys_map *src, struct drm_rect *damage)
427c17d0486SNoralf Trønnes {
428c17d0486SNoralf Trønnes int ret;
429c17d0486SNoralf Trønnes
430c17d0486SNoralf Trønnes if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE)
431c17d0486SNoralf Trønnes drm_rect_init(damage, 0, 0, fb->width, fb->height);
432c17d0486SNoralf Trønnes
433c17d0486SNoralf Trønnes if (gud_async_flush) {
434c17d0486SNoralf Trønnes ret = gud_fb_queue_damage(gdrm, fb, src, damage);
435c17d0486SNoralf Trønnes if (ret != -ENOMEM)
436c17d0486SNoralf Trønnes return;
437c17d0486SNoralf Trønnes }
438c17d0486SNoralf Trønnes
439c17d0486SNoralf Trønnes /* Imported buffers are assumed to be WriteCombined with uncached reads */
440c17d0486SNoralf Trønnes gud_flush_damage(gdrm, fb, src, !fb->obj[0]->import_attach, damage);
44140e1a70bSNoralf Trønnes }
44240e1a70bSNoralf Trønnes
gud_pipe_check(struct drm_simple_display_pipe * pipe,struct drm_plane_state * new_plane_state,struct drm_crtc_state * new_crtc_state)44340e1a70bSNoralf Trønnes int gud_pipe_check(struct drm_simple_display_pipe *pipe,
44440e1a70bSNoralf Trønnes struct drm_plane_state *new_plane_state,
44540e1a70bSNoralf Trønnes struct drm_crtc_state *new_crtc_state)
44640e1a70bSNoralf Trønnes {
44740e1a70bSNoralf Trønnes struct gud_device *gdrm = to_gud_device(pipe->crtc.dev);
44840e1a70bSNoralf Trønnes struct drm_plane_state *old_plane_state = pipe->plane.state;
44940e1a70bSNoralf Trønnes const struct drm_display_mode *mode = &new_crtc_state->mode;
45040e1a70bSNoralf Trønnes struct drm_atomic_state *state = new_plane_state->state;
45140e1a70bSNoralf Trønnes struct drm_framebuffer *old_fb = old_plane_state->fb;
45240e1a70bSNoralf Trønnes struct drm_connector_state *connector_state = NULL;
45340e1a70bSNoralf Trønnes struct drm_framebuffer *fb = new_plane_state->fb;
45440e1a70bSNoralf Trønnes const struct drm_format_info *format = fb->format;
45540e1a70bSNoralf Trønnes struct drm_connector *connector;
45640e1a70bSNoralf Trønnes unsigned int i, num_properties;
45740e1a70bSNoralf Trønnes struct gud_state_req *req;
45840e1a70bSNoralf Trønnes int idx, ret;
45940e1a70bSNoralf Trønnes size_t len;
46040e1a70bSNoralf Trønnes
46140e1a70bSNoralf Trønnes if (WARN_ON_ONCE(!fb))
46240e1a70bSNoralf Trønnes return -EINVAL;
46340e1a70bSNoralf Trønnes
46440e1a70bSNoralf Trønnes if (old_plane_state->rotation != new_plane_state->rotation)
46540e1a70bSNoralf Trønnes new_crtc_state->mode_changed = true;
46640e1a70bSNoralf Trønnes
46740e1a70bSNoralf Trønnes if (old_fb && old_fb->format != format)
46840e1a70bSNoralf Trønnes new_crtc_state->mode_changed = true;
46940e1a70bSNoralf Trønnes
47040e1a70bSNoralf Trønnes if (!new_crtc_state->mode_changed && !new_crtc_state->connectors_changed)
47140e1a70bSNoralf Trønnes return 0;
47240e1a70bSNoralf Trønnes
47340e1a70bSNoralf Trønnes /* Only one connector is supported */
47440e1a70bSNoralf Trønnes if (hweight32(new_crtc_state->connector_mask) != 1)
47540e1a70bSNoralf Trønnes return -EINVAL;
47640e1a70bSNoralf Trønnes
47740e1a70bSNoralf Trønnes if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format)
47840e1a70bSNoralf Trønnes format = gdrm->xrgb8888_emulation_format;
47940e1a70bSNoralf Trønnes
48040e1a70bSNoralf Trønnes for_each_new_connector_in_state(state, connector, connector_state, i) {
48140e1a70bSNoralf Trønnes if (connector_state->crtc)
48240e1a70bSNoralf Trønnes break;
48340e1a70bSNoralf Trønnes }
48440e1a70bSNoralf Trønnes
48540e1a70bSNoralf Trønnes /*
48640e1a70bSNoralf Trønnes * DRM_IOCTL_MODE_OBJ_SETPROPERTY on the rotation property will not have
48740e1a70bSNoralf Trønnes * the connector included in the state.
48840e1a70bSNoralf Trønnes */
48940e1a70bSNoralf Trønnes if (!connector_state) {
49040e1a70bSNoralf Trønnes struct drm_connector_list_iter conn_iter;
49140e1a70bSNoralf Trønnes
49240e1a70bSNoralf Trønnes drm_connector_list_iter_begin(pipe->crtc.dev, &conn_iter);
49340e1a70bSNoralf Trønnes drm_for_each_connector_iter(connector, &conn_iter) {
49440e1a70bSNoralf Trønnes if (connector->state->crtc) {
49540e1a70bSNoralf Trønnes connector_state = connector->state;
49640e1a70bSNoralf Trønnes break;
49740e1a70bSNoralf Trønnes }
49840e1a70bSNoralf Trønnes }
49940e1a70bSNoralf Trønnes drm_connector_list_iter_end(&conn_iter);
50040e1a70bSNoralf Trønnes }
50140e1a70bSNoralf Trønnes
50240e1a70bSNoralf Trønnes if (WARN_ON_ONCE(!connector_state))
50340e1a70bSNoralf Trønnes return -ENOENT;
50440e1a70bSNoralf Trønnes
50540e1a70bSNoralf Trønnes len = struct_size(req, properties,
50640e1a70bSNoralf Trønnes GUD_PROPERTIES_MAX_NUM + GUD_CONNECTOR_PROPERTIES_MAX_NUM);
50740e1a70bSNoralf Trønnes req = kzalloc(len, GFP_KERNEL);
50840e1a70bSNoralf Trønnes if (!req)
50940e1a70bSNoralf Trønnes return -ENOMEM;
51040e1a70bSNoralf Trønnes
51140e1a70bSNoralf Trønnes gud_from_display_mode(&req->mode, mode);
51240e1a70bSNoralf Trønnes
51340e1a70bSNoralf Trønnes req->format = gud_from_fourcc(format->format);
51440e1a70bSNoralf Trønnes if (WARN_ON_ONCE(!req->format)) {
51540e1a70bSNoralf Trønnes ret = -EINVAL;
51640e1a70bSNoralf Trønnes goto out;
51740e1a70bSNoralf Trønnes }
51840e1a70bSNoralf Trønnes
51940e1a70bSNoralf Trønnes req->connector = drm_connector_index(connector_state->connector);
52040e1a70bSNoralf Trønnes
52140e1a70bSNoralf Trønnes ret = gud_connector_fill_properties(connector_state, req->properties);
52240e1a70bSNoralf Trønnes if (ret < 0)
52340e1a70bSNoralf Trønnes goto out;
52440e1a70bSNoralf Trønnes
52540e1a70bSNoralf Trønnes num_properties = ret;
52640e1a70bSNoralf Trønnes for (i = 0; i < gdrm->num_properties; i++) {
52740e1a70bSNoralf Trønnes u16 prop = gdrm->properties[i];
52840e1a70bSNoralf Trønnes u64 val;
52940e1a70bSNoralf Trønnes
53040e1a70bSNoralf Trønnes switch (prop) {
53140e1a70bSNoralf Trønnes case GUD_PROPERTY_ROTATION:
53240e1a70bSNoralf Trønnes /* DRM UAPI matches the protocol so use value directly */
53340e1a70bSNoralf Trønnes val = new_plane_state->rotation;
53440e1a70bSNoralf Trønnes break;
53540e1a70bSNoralf Trønnes default:
53640e1a70bSNoralf Trønnes WARN_ON_ONCE(1);
53740e1a70bSNoralf Trønnes ret = -EINVAL;
53840e1a70bSNoralf Trønnes goto out;
53940e1a70bSNoralf Trønnes }
54040e1a70bSNoralf Trønnes
54140e1a70bSNoralf Trønnes req->properties[num_properties + i].prop = cpu_to_le16(prop);
54240e1a70bSNoralf Trønnes req->properties[num_properties + i].val = cpu_to_le64(val);
54340e1a70bSNoralf Trønnes num_properties++;
54440e1a70bSNoralf Trønnes }
54540e1a70bSNoralf Trønnes
54640e1a70bSNoralf Trønnes if (drm_dev_enter(fb->dev, &idx)) {
54740e1a70bSNoralf Trønnes len = struct_size(req, properties, num_properties);
54840e1a70bSNoralf Trønnes ret = gud_usb_set(gdrm, GUD_REQ_SET_STATE_CHECK, 0, req, len);
54940e1a70bSNoralf Trønnes drm_dev_exit(idx);
55040e1a70bSNoralf Trønnes } else {
55140e1a70bSNoralf Trønnes ret = -ENODEV;
55240e1a70bSNoralf Trønnes }
55340e1a70bSNoralf Trønnes out:
55440e1a70bSNoralf Trønnes kfree(req);
55540e1a70bSNoralf Trønnes
55640e1a70bSNoralf Trønnes return ret;
55740e1a70bSNoralf Trønnes }
55840e1a70bSNoralf Trønnes
gud_pipe_update(struct drm_simple_display_pipe * pipe,struct drm_plane_state * old_state)55940e1a70bSNoralf Trønnes void gud_pipe_update(struct drm_simple_display_pipe *pipe,
56040e1a70bSNoralf Trønnes struct drm_plane_state *old_state)
56140e1a70bSNoralf Trønnes {
56240e1a70bSNoralf Trønnes struct drm_device *drm = pipe->crtc.dev;
56340e1a70bSNoralf Trønnes struct gud_device *gdrm = to_gud_device(drm);
56440e1a70bSNoralf Trønnes struct drm_plane_state *state = pipe->plane.state;
565c17d0486SNoralf Trønnes struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
56640e1a70bSNoralf Trønnes struct drm_framebuffer *fb = state->fb;
56740e1a70bSNoralf Trønnes struct drm_crtc *crtc = &pipe->crtc;
56840e1a70bSNoralf Trønnes struct drm_rect damage;
569c17d0486SNoralf Trønnes int ret, idx;
57040e1a70bSNoralf Trønnes
57140e1a70bSNoralf Trønnes if (crtc->state->mode_changed || !crtc->state->enable) {
57240e1a70bSNoralf Trønnes cancel_work_sync(&gdrm->work);
57340e1a70bSNoralf Trønnes mutex_lock(&gdrm->damage_lock);
57440e1a70bSNoralf Trønnes if (gdrm->fb) {
57540e1a70bSNoralf Trønnes drm_framebuffer_put(gdrm->fb);
57640e1a70bSNoralf Trønnes gdrm->fb = NULL;
57740e1a70bSNoralf Trønnes }
57840e1a70bSNoralf Trønnes gud_clear_damage(gdrm);
579c17d0486SNoralf Trønnes vfree(gdrm->shadow_buf);
580c17d0486SNoralf Trønnes gdrm->shadow_buf = NULL;
58140e1a70bSNoralf Trønnes mutex_unlock(&gdrm->damage_lock);
58240e1a70bSNoralf Trønnes }
58340e1a70bSNoralf Trønnes
58440e1a70bSNoralf Trønnes if (!drm_dev_enter(drm, &idx))
58540e1a70bSNoralf Trønnes return;
58640e1a70bSNoralf Trønnes
58740e1a70bSNoralf Trønnes if (!old_state->fb)
58840e1a70bSNoralf Trønnes gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 1);
58940e1a70bSNoralf Trønnes
59040e1a70bSNoralf Trønnes if (fb && (crtc->state->mode_changed || crtc->state->connectors_changed))
59140e1a70bSNoralf Trønnes gud_usb_set(gdrm, GUD_REQ_SET_STATE_COMMIT, 0, NULL, 0);
59240e1a70bSNoralf Trønnes
59340e1a70bSNoralf Trønnes if (crtc->state->active_changed)
59440e1a70bSNoralf Trønnes gud_usb_set_u8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, crtc->state->active);
59540e1a70bSNoralf Trønnes
596c17d0486SNoralf Trønnes if (!fb)
597c17d0486SNoralf Trønnes goto ctrl_disable;
59840e1a70bSNoralf Trønnes
599c17d0486SNoralf Trønnes ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
600c17d0486SNoralf Trønnes if (ret)
601c17d0486SNoralf Trønnes goto ctrl_disable;
602c17d0486SNoralf Trønnes
603c17d0486SNoralf Trønnes if (drm_atomic_helper_damage_merged(old_state, state, &damage))
604c17d0486SNoralf Trønnes gud_fb_handle_damage(gdrm, fb, &shadow_plane_state->data[0], &damage);
605c17d0486SNoralf Trønnes
606c17d0486SNoralf Trønnes drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
607c17d0486SNoralf Trønnes
608c17d0486SNoralf Trønnes ctrl_disable:
60940e1a70bSNoralf Trønnes if (!crtc->state->enable)
61040e1a70bSNoralf Trønnes gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);
61140e1a70bSNoralf Trønnes
61240e1a70bSNoralf Trønnes drm_dev_exit(idx);
61340e1a70bSNoralf Trønnes }
614