18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2019 Hans de Goede <hdegoede@redhat.com>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/dma-buf.h>
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/usb.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_atomic_state_helper.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_connector.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_damage_helper.h>
148c2ecf20Sopenharmony_ci#include <drm/drm_drv.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h>
168c2ecf20Sopenharmony_ci#include <drm/drm_file.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_format_helper.h>
188c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
198c2ecf20Sopenharmony_ci#include <drm/drm_gem_shmem_helper.h>
208c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
218c2ecf20Sopenharmony_ci#include <drm/drm_ioctl.h>
228c2ecf20Sopenharmony_ci#include <drm/drm_managed.h>
238c2ecf20Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h>
248c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
258c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic bool eco_mode;
288c2ecf20Sopenharmony_cimodule_param(eco_mode, bool, 0644);
298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)");
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define DRIVER_NAME		"gm12u320"
328c2ecf20Sopenharmony_ci#define DRIVER_DESC		"Grain Media GM12U320 USB projector display"
338c2ecf20Sopenharmony_ci#define DRIVER_DATE		"2019"
348c2ecf20Sopenharmony_ci#define DRIVER_MAJOR		1
358c2ecf20Sopenharmony_ci#define DRIVER_MINOR		0
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/*
388c2ecf20Sopenharmony_ci * The DLP has an actual width of 854 pixels, but that is not a multiple
398c2ecf20Sopenharmony_ci * of 8, breaking things left and right, so we export a width of 848.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci#define GM12U320_USER_WIDTH		848
428c2ecf20Sopenharmony_ci#define GM12U320_REAL_WIDTH		854
438c2ecf20Sopenharmony_ci#define GM12U320_HEIGHT			480
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define GM12U320_BLOCK_COUNT		20
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define GM12U320_ERR(fmt, ...) \
488c2ecf20Sopenharmony_ci	DRM_DEV_ERROR(&gm12u320->udev->dev, fmt, ##__VA_ARGS__)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define MISC_RCV_EPT			1
518c2ecf20Sopenharmony_ci#define DATA_RCV_EPT			2
528c2ecf20Sopenharmony_ci#define DATA_SND_EPT			3
538c2ecf20Sopenharmony_ci#define MISC_SND_EPT			4
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define DATA_BLOCK_HEADER_SIZE		84
568c2ecf20Sopenharmony_ci#define DATA_BLOCK_CONTENT_SIZE		64512
578c2ecf20Sopenharmony_ci#define DATA_BLOCK_FOOTER_SIZE		20
588c2ecf20Sopenharmony_ci#define DATA_BLOCK_SIZE			(DATA_BLOCK_HEADER_SIZE + \
598c2ecf20Sopenharmony_ci					 DATA_BLOCK_CONTENT_SIZE + \
608c2ecf20Sopenharmony_ci					 DATA_BLOCK_FOOTER_SIZE)
618c2ecf20Sopenharmony_ci#define DATA_LAST_BLOCK_CONTENT_SIZE	4032
628c2ecf20Sopenharmony_ci#define DATA_LAST_BLOCK_SIZE		(DATA_BLOCK_HEADER_SIZE + \
638c2ecf20Sopenharmony_ci					 DATA_LAST_BLOCK_CONTENT_SIZE + \
648c2ecf20Sopenharmony_ci					 DATA_BLOCK_FOOTER_SIZE)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#define CMD_SIZE			31
678c2ecf20Sopenharmony_ci#define READ_STATUS_SIZE		13
688c2ecf20Sopenharmony_ci#define MISC_VALUE_SIZE			4
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define CMD_TIMEOUT			200
718c2ecf20Sopenharmony_ci#define DATA_TIMEOUT			1000
728c2ecf20Sopenharmony_ci#define IDLE_TIMEOUT			2000
738c2ecf20Sopenharmony_ci#define FIRST_FRAME_TIMEOUT		2000
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define MISC_REQ_GET_SET_ECO_A		0xff
768c2ecf20Sopenharmony_ci#define MISC_REQ_GET_SET_ECO_B		0x35
778c2ecf20Sopenharmony_ci/* Windows driver does once every second, with arg d = 1, other args 0 */
788c2ecf20Sopenharmony_ci#define MISC_REQ_UNKNOWN1_A		0xff
798c2ecf20Sopenharmony_ci#define MISC_REQ_UNKNOWN1_B		0x38
808c2ecf20Sopenharmony_ci/* Windows driver does this on init, with arg a, b = 0, c = 0xa0, d = 4 */
818c2ecf20Sopenharmony_ci#define MISC_REQ_UNKNOWN2_A		0xa5
828c2ecf20Sopenharmony_ci#define MISC_REQ_UNKNOWN2_B		0x00
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistruct gm12u320_device {
858c2ecf20Sopenharmony_ci	struct drm_device	         dev;
868c2ecf20Sopenharmony_ci	struct device                   *dmadev;
878c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe   pipe;
888c2ecf20Sopenharmony_ci	struct drm_connector	         conn;
898c2ecf20Sopenharmony_ci	struct usb_device               *udev;
908c2ecf20Sopenharmony_ci	unsigned char                   *cmd_buf;
918c2ecf20Sopenharmony_ci	unsigned char                   *data_buf[GM12U320_BLOCK_COUNT];
928c2ecf20Sopenharmony_ci	struct {
938c2ecf20Sopenharmony_ci		struct delayed_work       work;
948c2ecf20Sopenharmony_ci		struct mutex             lock;
958c2ecf20Sopenharmony_ci		struct drm_framebuffer  *fb;
968c2ecf20Sopenharmony_ci		struct drm_rect          rect;
978c2ecf20Sopenharmony_ci		int frame;
988c2ecf20Sopenharmony_ci		int draw_status_timeout;
998c2ecf20Sopenharmony_ci	} fb_update;
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#define to_gm12u320(__dev) container_of(__dev, struct gm12u320_device, dev)
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic const char cmd_data[CMD_SIZE] = {
1058c2ecf20Sopenharmony_ci	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
1068c2ecf20Sopenharmony_ci	0x68, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff,
1078c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x80, 0x00,
1088c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic const char cmd_draw[CMD_SIZE] = {
1128c2ecf20Sopenharmony_ci	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
1138c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe,
1148c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0xc0, 0xd1, 0x05, 0x00, 0x40,
1158c2ecf20Sopenharmony_ci	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic const char cmd_misc[CMD_SIZE] = {
1198c2ecf20Sopenharmony_ci	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
1208c2ecf20Sopenharmony_ci	0x04, 0x00, 0x00, 0x00, 0x80, 0x01, 0x10, 0xfd,
1218c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00,
1228c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic const char data_block_header[DATA_BLOCK_HEADER_SIZE] = {
1268c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1278c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1288c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1298c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1308c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1318c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1328c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1338c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1348c2ecf20Sopenharmony_ci	0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1358c2ecf20Sopenharmony_ci	0x00, 0x04, 0x15, 0x00, 0x00, 0xfc, 0x00, 0x00,
1368c2ecf20Sopenharmony_ci	0x01, 0x00, 0x00, 0xdb
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic const char data_last_block_header[DATA_BLOCK_HEADER_SIZE] = {
1408c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1418c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1428c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1438c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1448c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1458c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1468c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1478c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1488c2ecf20Sopenharmony_ci	0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1498c2ecf20Sopenharmony_ci	0x2a, 0x00, 0x20, 0x00, 0xc0, 0x0f, 0x00, 0x00,
1508c2ecf20Sopenharmony_ci	0x01, 0x00, 0x00, 0xd7
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic const char data_block_footer[DATA_BLOCK_FOOTER_SIZE] = {
1548c2ecf20Sopenharmony_ci	0xfb, 0x14, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
1558c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1568c2ecf20Sopenharmony_ci	0x80, 0x00, 0x00, 0x4f
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int gm12u320_usb_alloc(struct gm12u320_device *gm12u320)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	int i, block_size;
1628c2ecf20Sopenharmony_ci	const char *hdr;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	gm12u320->cmd_buf = drmm_kmalloc(&gm12u320->dev, CMD_SIZE, GFP_KERNEL);
1658c2ecf20Sopenharmony_ci	if (!gm12u320->cmd_buf)
1668c2ecf20Sopenharmony_ci		return -ENOMEM;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	for (i = 0; i < GM12U320_BLOCK_COUNT; i++) {
1698c2ecf20Sopenharmony_ci		if (i == GM12U320_BLOCK_COUNT - 1) {
1708c2ecf20Sopenharmony_ci			block_size = DATA_LAST_BLOCK_SIZE;
1718c2ecf20Sopenharmony_ci			hdr = data_last_block_header;
1728c2ecf20Sopenharmony_ci		} else {
1738c2ecf20Sopenharmony_ci			block_size = DATA_BLOCK_SIZE;
1748c2ecf20Sopenharmony_ci			hdr = data_block_header;
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		gm12u320->data_buf[i] = drmm_kzalloc(&gm12u320->dev,
1788c2ecf20Sopenharmony_ci						     block_size, GFP_KERNEL);
1798c2ecf20Sopenharmony_ci		if (!gm12u320->data_buf[i])
1808c2ecf20Sopenharmony_ci			return -ENOMEM;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		memcpy(gm12u320->data_buf[i], hdr, DATA_BLOCK_HEADER_SIZE);
1838c2ecf20Sopenharmony_ci		memcpy(gm12u320->data_buf[i] +
1848c2ecf20Sopenharmony_ci				(block_size - DATA_BLOCK_FOOTER_SIZE),
1858c2ecf20Sopenharmony_ci		       data_block_footer, DATA_BLOCK_FOOTER_SIZE);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int gm12u320_misc_request(struct gm12u320_device *gm12u320,
1928c2ecf20Sopenharmony_ci				 u8 req_a, u8 req_b,
1938c2ecf20Sopenharmony_ci				 u8 arg_a, u8 arg_b, u8 arg_c, u8 arg_d)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	int ret, len;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	memcpy(gm12u320->cmd_buf, &cmd_misc, CMD_SIZE);
1988c2ecf20Sopenharmony_ci	gm12u320->cmd_buf[20] = req_a;
1998c2ecf20Sopenharmony_ci	gm12u320->cmd_buf[21] = req_b;
2008c2ecf20Sopenharmony_ci	gm12u320->cmd_buf[22] = arg_a;
2018c2ecf20Sopenharmony_ci	gm12u320->cmd_buf[23] = arg_b;
2028c2ecf20Sopenharmony_ci	gm12u320->cmd_buf[24] = arg_c;
2038c2ecf20Sopenharmony_ci	gm12u320->cmd_buf[25] = arg_d;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* Send request */
2068c2ecf20Sopenharmony_ci	ret = usb_bulk_msg(gm12u320->udev,
2078c2ecf20Sopenharmony_ci			   usb_sndbulkpipe(gm12u320->udev, MISC_SND_EPT),
2088c2ecf20Sopenharmony_ci			   gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
2098c2ecf20Sopenharmony_ci	if (ret || len != CMD_SIZE) {
2108c2ecf20Sopenharmony_ci		GM12U320_ERR("Misc. req. error %d\n", ret);
2118c2ecf20Sopenharmony_ci		return -EIO;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* Read value */
2158c2ecf20Sopenharmony_ci	ret = usb_bulk_msg(gm12u320->udev,
2168c2ecf20Sopenharmony_ci			   usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT),
2178c2ecf20Sopenharmony_ci			   gm12u320->cmd_buf, MISC_VALUE_SIZE, &len,
2188c2ecf20Sopenharmony_ci			   DATA_TIMEOUT);
2198c2ecf20Sopenharmony_ci	if (ret || len != MISC_VALUE_SIZE) {
2208c2ecf20Sopenharmony_ci		GM12U320_ERR("Misc. value error %d\n", ret);
2218c2ecf20Sopenharmony_ci		return -EIO;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci	/* cmd_buf[0] now contains the read value, which we don't use */
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* Read status */
2268c2ecf20Sopenharmony_ci	ret = usb_bulk_msg(gm12u320->udev,
2278c2ecf20Sopenharmony_ci			   usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT),
2288c2ecf20Sopenharmony_ci			   gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
2298c2ecf20Sopenharmony_ci			   CMD_TIMEOUT);
2308c2ecf20Sopenharmony_ci	if (ret || len != READ_STATUS_SIZE) {
2318c2ecf20Sopenharmony_ci		GM12U320_ERR("Misc. status error %d\n", ret);
2328c2ecf20Sopenharmony_ci		return -EIO;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic void gm12u320_32bpp_to_24bpp_packed(u8 *dst, u8 *src, int len)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	while (len--) {
2418c2ecf20Sopenharmony_ci		*dst++ = *src++;
2428c2ecf20Sopenharmony_ci		*dst++ = *src++;
2438c2ecf20Sopenharmony_ci		*dst++ = *src++;
2448c2ecf20Sopenharmony_ci		src++;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void gm12u320_copy_fb_to_blocks(struct gm12u320_device *gm12u320)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	int block, dst_offset, len, remain, ret, x1, x2, y1, y2;
2518c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb;
2528c2ecf20Sopenharmony_ci	void *vaddr;
2538c2ecf20Sopenharmony_ci	u8 *src;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	mutex_lock(&gm12u320->fb_update.lock);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (!gm12u320->fb_update.fb)
2588c2ecf20Sopenharmony_ci		goto unlock;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	fb = gm12u320->fb_update.fb;
2618c2ecf20Sopenharmony_ci	x1 = gm12u320->fb_update.rect.x1;
2628c2ecf20Sopenharmony_ci	x2 = gm12u320->fb_update.rect.x2;
2638c2ecf20Sopenharmony_ci	y1 = gm12u320->fb_update.rect.y1;
2648c2ecf20Sopenharmony_ci	y2 = gm12u320->fb_update.rect.y2;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	vaddr = drm_gem_shmem_vmap(fb->obj[0]);
2678c2ecf20Sopenharmony_ci	if (IS_ERR(vaddr)) {
2688c2ecf20Sopenharmony_ci		GM12U320_ERR("failed to vmap fb: %ld\n", PTR_ERR(vaddr));
2698c2ecf20Sopenharmony_ci		goto put_fb;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (fb->obj[0]->import_attach) {
2738c2ecf20Sopenharmony_ci		ret = dma_buf_begin_cpu_access(
2748c2ecf20Sopenharmony_ci			fb->obj[0]->import_attach->dmabuf, DMA_FROM_DEVICE);
2758c2ecf20Sopenharmony_ci		if (ret) {
2768c2ecf20Sopenharmony_ci			GM12U320_ERR("dma_buf_begin_cpu_access err: %d\n", ret);
2778c2ecf20Sopenharmony_ci			goto vunmap;
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	src = vaddr + y1 * fb->pitches[0] + x1 * 4;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	x1 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
2848c2ecf20Sopenharmony_ci	x2 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	for (; y1 < y2; y1++) {
2878c2ecf20Sopenharmony_ci		remain = 0;
2888c2ecf20Sopenharmony_ci		len = (x2 - x1) * 3;
2898c2ecf20Sopenharmony_ci		dst_offset = (y1 * GM12U320_REAL_WIDTH + x1) * 3;
2908c2ecf20Sopenharmony_ci		block = dst_offset / DATA_BLOCK_CONTENT_SIZE;
2918c2ecf20Sopenharmony_ci		dst_offset %= DATA_BLOCK_CONTENT_SIZE;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		if ((dst_offset + len) > DATA_BLOCK_CONTENT_SIZE) {
2948c2ecf20Sopenharmony_ci			remain = dst_offset + len - DATA_BLOCK_CONTENT_SIZE;
2958c2ecf20Sopenharmony_ci			len = DATA_BLOCK_CONTENT_SIZE - dst_offset;
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		dst_offset += DATA_BLOCK_HEADER_SIZE;
2998c2ecf20Sopenharmony_ci		len /= 3;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		gm12u320_32bpp_to_24bpp_packed(
3028c2ecf20Sopenharmony_ci			gm12u320->data_buf[block] + dst_offset,
3038c2ecf20Sopenharmony_ci			src, len);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		if (remain) {
3068c2ecf20Sopenharmony_ci			block++;
3078c2ecf20Sopenharmony_ci			dst_offset = DATA_BLOCK_HEADER_SIZE;
3088c2ecf20Sopenharmony_ci			gm12u320_32bpp_to_24bpp_packed(
3098c2ecf20Sopenharmony_ci				gm12u320->data_buf[block] + dst_offset,
3108c2ecf20Sopenharmony_ci				src + len * 4, remain / 3);
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci		src += fb->pitches[0];
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (fb->obj[0]->import_attach) {
3168c2ecf20Sopenharmony_ci		ret = dma_buf_end_cpu_access(fb->obj[0]->import_attach->dmabuf,
3178c2ecf20Sopenharmony_ci					     DMA_FROM_DEVICE);
3188c2ecf20Sopenharmony_ci		if (ret)
3198c2ecf20Sopenharmony_ci			GM12U320_ERR("dma_buf_end_cpu_access err: %d\n", ret);
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_civunmap:
3228c2ecf20Sopenharmony_ci	drm_gem_shmem_vunmap(fb->obj[0], vaddr);
3238c2ecf20Sopenharmony_ciput_fb:
3248c2ecf20Sopenharmony_ci	drm_framebuffer_put(fb);
3258c2ecf20Sopenharmony_ci	gm12u320->fb_update.fb = NULL;
3268c2ecf20Sopenharmony_ciunlock:
3278c2ecf20Sopenharmony_ci	mutex_unlock(&gm12u320->fb_update.lock);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void gm12u320_fb_update_work(struct work_struct *work)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct gm12u320_device *gm12u320 =
3338c2ecf20Sopenharmony_ci		container_of(to_delayed_work(work), struct gm12u320_device,
3348c2ecf20Sopenharmony_ci			     fb_update.work);
3358c2ecf20Sopenharmony_ci	int block, block_size, len;
3368c2ecf20Sopenharmony_ci	int ret = 0;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	gm12u320_copy_fb_to_blocks(gm12u320);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	for (block = 0; block < GM12U320_BLOCK_COUNT; block++) {
3418c2ecf20Sopenharmony_ci		if (block == GM12U320_BLOCK_COUNT - 1)
3428c2ecf20Sopenharmony_ci			block_size = DATA_LAST_BLOCK_SIZE;
3438c2ecf20Sopenharmony_ci		else
3448c2ecf20Sopenharmony_ci			block_size = DATA_BLOCK_SIZE;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		/* Send data command to device */
3478c2ecf20Sopenharmony_ci		memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE);
3488c2ecf20Sopenharmony_ci		gm12u320->cmd_buf[8] = block_size & 0xff;
3498c2ecf20Sopenharmony_ci		gm12u320->cmd_buf[9] = block_size >> 8;
3508c2ecf20Sopenharmony_ci		gm12u320->cmd_buf[20] = 0xfc - block * 4;
3518c2ecf20Sopenharmony_ci		gm12u320->cmd_buf[21] =
3528c2ecf20Sopenharmony_ci			block | (gm12u320->fb_update.frame << 7);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		ret = usb_bulk_msg(gm12u320->udev,
3558c2ecf20Sopenharmony_ci			usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
3568c2ecf20Sopenharmony_ci			gm12u320->cmd_buf, CMD_SIZE, &len,
3578c2ecf20Sopenharmony_ci			CMD_TIMEOUT);
3588c2ecf20Sopenharmony_ci		if (ret || len != CMD_SIZE)
3598c2ecf20Sopenharmony_ci			goto err;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		/* Send data block to device */
3628c2ecf20Sopenharmony_ci		ret = usb_bulk_msg(gm12u320->udev,
3638c2ecf20Sopenharmony_ci			usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
3648c2ecf20Sopenharmony_ci			gm12u320->data_buf[block], block_size,
3658c2ecf20Sopenharmony_ci			&len, DATA_TIMEOUT);
3668c2ecf20Sopenharmony_ci		if (ret || len != block_size)
3678c2ecf20Sopenharmony_ci			goto err;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		/* Read status */
3708c2ecf20Sopenharmony_ci		ret = usb_bulk_msg(gm12u320->udev,
3718c2ecf20Sopenharmony_ci			usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
3728c2ecf20Sopenharmony_ci			gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
3738c2ecf20Sopenharmony_ci			CMD_TIMEOUT);
3748c2ecf20Sopenharmony_ci		if (ret || len != READ_STATUS_SIZE)
3758c2ecf20Sopenharmony_ci			goto err;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* Send draw command to device */
3798c2ecf20Sopenharmony_ci	memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE);
3808c2ecf20Sopenharmony_ci	ret = usb_bulk_msg(gm12u320->udev,
3818c2ecf20Sopenharmony_ci		usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
3828c2ecf20Sopenharmony_ci		gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
3838c2ecf20Sopenharmony_ci	if (ret || len != CMD_SIZE)
3848c2ecf20Sopenharmony_ci		goto err;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Read status */
3878c2ecf20Sopenharmony_ci	ret = usb_bulk_msg(gm12u320->udev,
3888c2ecf20Sopenharmony_ci		usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
3898c2ecf20Sopenharmony_ci		gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
3908c2ecf20Sopenharmony_ci		gm12u320->fb_update.draw_status_timeout);
3918c2ecf20Sopenharmony_ci	if (ret || len != READ_STATUS_SIZE)
3928c2ecf20Sopenharmony_ci		goto err;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	gm12u320->fb_update.draw_status_timeout = CMD_TIMEOUT;
3958c2ecf20Sopenharmony_ci	gm12u320->fb_update.frame = !gm12u320->fb_update.frame;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/*
3988c2ecf20Sopenharmony_ci	 * We must draw a frame every 2s otherwise the projector
3998c2ecf20Sopenharmony_ci	 * switches back to showing its logo.
4008c2ecf20Sopenharmony_ci	 */
4018c2ecf20Sopenharmony_ci	queue_delayed_work(system_long_wq, &gm12u320->fb_update.work,
4028c2ecf20Sopenharmony_ci			   msecs_to_jiffies(IDLE_TIMEOUT));
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return;
4058c2ecf20Sopenharmony_cierr:
4068c2ecf20Sopenharmony_ci	/* Do not log errors caused by module unload or device unplug */
4078c2ecf20Sopenharmony_ci	if (ret != -ENODEV && ret != -ECONNRESET && ret != -ESHUTDOWN)
4088c2ecf20Sopenharmony_ci		GM12U320_ERR("Frame update error: %d\n", ret);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb,
4128c2ecf20Sopenharmony_ci				   struct drm_rect *dirty)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct gm12u320_device *gm12u320 = to_gm12u320(fb->dev);
4158c2ecf20Sopenharmony_ci	struct drm_framebuffer *old_fb = NULL;
4168c2ecf20Sopenharmony_ci	bool wakeup = false;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	mutex_lock(&gm12u320->fb_update.lock);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (gm12u320->fb_update.fb != fb) {
4218c2ecf20Sopenharmony_ci		old_fb = gm12u320->fb_update.fb;
4228c2ecf20Sopenharmony_ci		drm_framebuffer_get(fb);
4238c2ecf20Sopenharmony_ci		gm12u320->fb_update.fb = fb;
4248c2ecf20Sopenharmony_ci		gm12u320->fb_update.rect = *dirty;
4258c2ecf20Sopenharmony_ci		wakeup = true;
4268c2ecf20Sopenharmony_ci	} else {
4278c2ecf20Sopenharmony_ci		struct drm_rect *rect = &gm12u320->fb_update.rect;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		rect->x1 = min(rect->x1, dirty->x1);
4308c2ecf20Sopenharmony_ci		rect->y1 = min(rect->y1, dirty->y1);
4318c2ecf20Sopenharmony_ci		rect->x2 = max(rect->x2, dirty->x2);
4328c2ecf20Sopenharmony_ci		rect->y2 = max(rect->y2, dirty->y2);
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	mutex_unlock(&gm12u320->fb_update.lock);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (wakeup)
4388c2ecf20Sopenharmony_ci		mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (old_fb)
4418c2ecf20Sopenharmony_ci		drm_framebuffer_put(old_fb);
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cistatic void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct drm_framebuffer *old_fb;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&gm12u320->fb_update.work);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	mutex_lock(&gm12u320->fb_update.lock);
4518c2ecf20Sopenharmony_ci	old_fb = gm12u320->fb_update.fb;
4528c2ecf20Sopenharmony_ci	gm12u320->fb_update.fb = NULL;
4538c2ecf20Sopenharmony_ci	mutex_unlock(&gm12u320->fb_update.lock);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	drm_framebuffer_put(old_fb);
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic int gm12u320_set_ecomode(struct gm12u320_device *gm12u320)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	return gm12u320_misc_request(gm12u320, MISC_REQ_GET_SET_ECO_A,
4618c2ecf20Sopenharmony_ci				     MISC_REQ_GET_SET_ECO_B, 0x01 /* set */,
4628c2ecf20Sopenharmony_ci				     eco_mode ? 0x01 : 0x00, 0x00, 0x01);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
4668c2ecf20Sopenharmony_ci/* gm12u320 connector						      */
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci/*
4698c2ecf20Sopenharmony_ci * We use fake EDID info so that userspace know that it is dealing with
4708c2ecf20Sopenharmony_ci * an Acer projector, rather then listing this as an "unknown" monitor.
4718c2ecf20Sopenharmony_ci * Note this assumes this driver is only ever used with the Acer C120, if we
4728c2ecf20Sopenharmony_ci * add support for other devices the vendor and model should be parameterized.
4738c2ecf20Sopenharmony_ci */
4748c2ecf20Sopenharmony_cistatic struct edid gm12u320_edid = {
4758c2ecf20Sopenharmony_ci	.header		= { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 },
4768c2ecf20Sopenharmony_ci	.mfg_id		= { 0x04, 0x72 },	/* "ACR" */
4778c2ecf20Sopenharmony_ci	.prod_code	= { 0x20, 0xc1 },	/* C120h */
4788c2ecf20Sopenharmony_ci	.serial		= 0xaa55aa55,
4798c2ecf20Sopenharmony_ci	.mfg_week	= 1,
4808c2ecf20Sopenharmony_ci	.mfg_year	= 16,
4818c2ecf20Sopenharmony_ci	.version	= 1,			/* EDID 1.3 */
4828c2ecf20Sopenharmony_ci	.revision	= 3,			/* EDID 1.3 */
4838c2ecf20Sopenharmony_ci	.input		= 0x08,			/* Analog input */
4848c2ecf20Sopenharmony_ci	.features	= 0x0a,			/* Pref timing in DTD 1 */
4858c2ecf20Sopenharmony_ci	.standard_timings = { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 },
4868c2ecf20Sopenharmony_ci			      { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 } },
4878c2ecf20Sopenharmony_ci	.detailed_timings = { {
4888c2ecf20Sopenharmony_ci		.pixel_clock = 3383,
4898c2ecf20Sopenharmony_ci		/* hactive = 848, hblank = 256 */
4908c2ecf20Sopenharmony_ci		.data.pixel_data.hactive_lo = 0x50,
4918c2ecf20Sopenharmony_ci		.data.pixel_data.hblank_lo = 0x00,
4928c2ecf20Sopenharmony_ci		.data.pixel_data.hactive_hblank_hi = 0x31,
4938c2ecf20Sopenharmony_ci		/* vactive = 480, vblank = 28 */
4948c2ecf20Sopenharmony_ci		.data.pixel_data.vactive_lo = 0xe0,
4958c2ecf20Sopenharmony_ci		.data.pixel_data.vblank_lo = 0x1c,
4968c2ecf20Sopenharmony_ci		.data.pixel_data.vactive_vblank_hi = 0x10,
4978c2ecf20Sopenharmony_ci		/* hsync offset 40 pw 128, vsync offset 1 pw 4 */
4988c2ecf20Sopenharmony_ci		.data.pixel_data.hsync_offset_lo = 0x28,
4998c2ecf20Sopenharmony_ci		.data.pixel_data.hsync_pulse_width_lo = 0x80,
5008c2ecf20Sopenharmony_ci		.data.pixel_data.vsync_offset_pulse_width_lo = 0x14,
5018c2ecf20Sopenharmony_ci		.data.pixel_data.hsync_vsync_offset_pulse_width_hi = 0x00,
5028c2ecf20Sopenharmony_ci		/* Digital separate syncs, hsync+, vsync+ */
5038c2ecf20Sopenharmony_ci		.data.pixel_data.misc = 0x1e,
5048c2ecf20Sopenharmony_ci	}, {
5058c2ecf20Sopenharmony_ci		.pixel_clock = 0,
5068c2ecf20Sopenharmony_ci		.data.other_data.type = 0xfd, /* Monitor ranges */
5078c2ecf20Sopenharmony_ci		.data.other_data.data.range.min_vfreq = 59,
5088c2ecf20Sopenharmony_ci		.data.other_data.data.range.max_vfreq = 61,
5098c2ecf20Sopenharmony_ci		.data.other_data.data.range.min_hfreq_khz = 29,
5108c2ecf20Sopenharmony_ci		.data.other_data.data.range.max_hfreq_khz = 32,
5118c2ecf20Sopenharmony_ci		.data.other_data.data.range.pixel_clock_mhz = 4, /* 40 MHz */
5128c2ecf20Sopenharmony_ci		.data.other_data.data.range.flags = 0,
5138c2ecf20Sopenharmony_ci		.data.other_data.data.range.formula.cvt = {
5148c2ecf20Sopenharmony_ci			0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 },
5158c2ecf20Sopenharmony_ci	}, {
5168c2ecf20Sopenharmony_ci		.pixel_clock = 0,
5178c2ecf20Sopenharmony_ci		.data.other_data.type = 0xfc, /* Model string */
5188c2ecf20Sopenharmony_ci		.data.other_data.data.str.str = {
5198c2ecf20Sopenharmony_ci			'P', 'r', 'o', 'j', 'e', 'c', 't', 'o', 'r', '\n',
5208c2ecf20Sopenharmony_ci			' ', ' ',  ' ' },
5218c2ecf20Sopenharmony_ci	}, {
5228c2ecf20Sopenharmony_ci		.pixel_clock = 0,
5238c2ecf20Sopenharmony_ci		.data.other_data.type = 0xfe, /* Unspecified text / padding */
5248c2ecf20Sopenharmony_ci		.data.other_data.data.str.str = {
5258c2ecf20Sopenharmony_ci			'\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
5268c2ecf20Sopenharmony_ci			' ', ' ',  ' ' },
5278c2ecf20Sopenharmony_ci	} },
5288c2ecf20Sopenharmony_ci	.checksum = 0x13,
5298c2ecf20Sopenharmony_ci};
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic int gm12u320_conn_get_modes(struct drm_connector *connector)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	drm_connector_update_edid_property(connector, &gm12u320_edid);
5348c2ecf20Sopenharmony_ci	return drm_add_edid_modes(connector, &gm12u320_edid);
5358c2ecf20Sopenharmony_ci}
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs gm12u320_conn_helper_funcs = {
5388c2ecf20Sopenharmony_ci	.get_modes = gm12u320_conn_get_modes,
5398c2ecf20Sopenharmony_ci};
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs gm12u320_conn_funcs = {
5428c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
5438c2ecf20Sopenharmony_ci	.destroy = drm_connector_cleanup,
5448c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
5458c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
5468c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
5478c2ecf20Sopenharmony_ci};
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistatic int gm12u320_conn_init(struct gm12u320_device *gm12u320)
5508c2ecf20Sopenharmony_ci{
5518c2ecf20Sopenharmony_ci	drm_connector_helper_add(&gm12u320->conn, &gm12u320_conn_helper_funcs);
5528c2ecf20Sopenharmony_ci	return drm_connector_init(&gm12u320->dev, &gm12u320->conn,
5538c2ecf20Sopenharmony_ci				  &gm12u320_conn_funcs, DRM_MODE_CONNECTOR_VGA);
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
5578c2ecf20Sopenharmony_ci/* gm12u320 (simple) display pipe				      */
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe,
5608c2ecf20Sopenharmony_ci				 struct drm_crtc_state *crtc_state,
5618c2ecf20Sopenharmony_ci				 struct drm_plane_state *plane_state)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT };
5648c2ecf20Sopenharmony_ci	struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT;
5678c2ecf20Sopenharmony_ci	gm12u320_fb_mark_dirty(plane_state->fb, &rect);
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	gm12u320_stop_fb_update(gm12u320);
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe,
5788c2ecf20Sopenharmony_ci				 struct drm_plane_state *old_state)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	struct drm_plane_state *state = pipe->plane.state;
5818c2ecf20Sopenharmony_ci	struct drm_rect rect;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	if (drm_atomic_helper_damage_merged(old_state, state, &rect))
5848c2ecf20Sopenharmony_ci		gm12u320_fb_mark_dirty(pipe->plane.state->fb, &rect);
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic const struct drm_simple_display_pipe_funcs gm12u320_pipe_funcs = {
5888c2ecf20Sopenharmony_ci	.enable	    = gm12u320_pipe_enable,
5898c2ecf20Sopenharmony_ci	.disable    = gm12u320_pipe_disable,
5908c2ecf20Sopenharmony_ci	.update	    = gm12u320_pipe_update,
5918c2ecf20Sopenharmony_ci};
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic const uint32_t gm12u320_pipe_formats[] = {
5948c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB8888,
5958c2ecf20Sopenharmony_ci};
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic const uint64_t gm12u320_pipe_modifiers[] = {
5988c2ecf20Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
5998c2ecf20Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
6008c2ecf20Sopenharmony_ci};
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci/*
6038c2ecf20Sopenharmony_ci * FIXME: Dma-buf sharing requires DMA support by the importing device.
6048c2ecf20Sopenharmony_ci *        This function is a workaround to make USB devices work as well.
6058c2ecf20Sopenharmony_ci *        See todo.rst for how to fix the issue in the dma-buf framework.
6068c2ecf20Sopenharmony_ci */
6078c2ecf20Sopenharmony_cistatic struct drm_gem_object *gm12u320_gem_prime_import(struct drm_device *dev,
6088c2ecf20Sopenharmony_ci							struct dma_buf *dma_buf)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct gm12u320_device *gm12u320 = to_gm12u320(dev);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (!gm12u320->dmadev)
6138c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	return drm_gem_prime_import_dev(dev, dma_buf, gm12u320->dmadev);
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_FOPS(gm12u320_fops);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic struct drm_driver gm12u320_drm_driver = {
6218c2ecf20Sopenharmony_ci	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	.name		 = DRIVER_NAME,
6248c2ecf20Sopenharmony_ci	.desc		 = DRIVER_DESC,
6258c2ecf20Sopenharmony_ci	.date		 = DRIVER_DATE,
6268c2ecf20Sopenharmony_ci	.major		 = DRIVER_MAJOR,
6278c2ecf20Sopenharmony_ci	.minor		 = DRIVER_MINOR,
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	.fops		 = &gm12u320_fops,
6308c2ecf20Sopenharmony_ci	DRM_GEM_SHMEM_DRIVER_OPS,
6318c2ecf20Sopenharmony_ci	.gem_prime_import = gm12u320_gem_prime_import,
6328c2ecf20Sopenharmony_ci};
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs gm12u320_mode_config_funcs = {
6358c2ecf20Sopenharmony_ci	.fb_create = drm_gem_fb_create_with_dirty,
6368c2ecf20Sopenharmony_ci	.atomic_check = drm_atomic_helper_check,
6378c2ecf20Sopenharmony_ci	.atomic_commit = drm_atomic_helper_commit,
6388c2ecf20Sopenharmony_ci};
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cistatic int gm12u320_usb_probe(struct usb_interface *interface,
6418c2ecf20Sopenharmony_ci			      const struct usb_device_id *id)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	struct gm12u320_device *gm12u320;
6448c2ecf20Sopenharmony_ci	struct drm_device *dev;
6458c2ecf20Sopenharmony_ci	int ret;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	/*
6488c2ecf20Sopenharmony_ci	 * The gm12u320 presents itself to the system as 2 usb mass-storage
6498c2ecf20Sopenharmony_ci	 * interfaces, we only care about / need the first one.
6508c2ecf20Sopenharmony_ci	 */
6518c2ecf20Sopenharmony_ci	if (interface->cur_altsetting->desc.bInterfaceNumber != 0)
6528c2ecf20Sopenharmony_ci		return -ENODEV;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	gm12u320 = devm_drm_dev_alloc(&interface->dev, &gm12u320_drm_driver,
6558c2ecf20Sopenharmony_ci				      struct gm12u320_device, dev);
6568c2ecf20Sopenharmony_ci	if (IS_ERR(gm12u320))
6578c2ecf20Sopenharmony_ci		return PTR_ERR(gm12u320);
6588c2ecf20Sopenharmony_ci	dev = &gm12u320->dev;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	gm12u320->dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
6618c2ecf20Sopenharmony_ci	if (!gm12u320->dmadev)
6628c2ecf20Sopenharmony_ci		drm_warn(dev, "buffer sharing not supported"); /* not an error */
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	gm12u320->udev = interface_to_usbdev(interface);
6658c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work);
6668c2ecf20Sopenharmony_ci	mutex_init(&gm12u320->fb_update.lock);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	ret = drmm_mode_config_init(dev);
6698c2ecf20Sopenharmony_ci	if (ret)
6708c2ecf20Sopenharmony_ci		goto err_put_device;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	dev->mode_config.min_width = GM12U320_USER_WIDTH;
6738c2ecf20Sopenharmony_ci	dev->mode_config.max_width = GM12U320_USER_WIDTH;
6748c2ecf20Sopenharmony_ci	dev->mode_config.min_height = GM12U320_HEIGHT;
6758c2ecf20Sopenharmony_ci	dev->mode_config.max_height = GM12U320_HEIGHT;
6768c2ecf20Sopenharmony_ci	dev->mode_config.funcs = &gm12u320_mode_config_funcs;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	ret = gm12u320_usb_alloc(gm12u320);
6798c2ecf20Sopenharmony_ci	if (ret)
6808c2ecf20Sopenharmony_ci		goto err_put_device;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	ret = gm12u320_set_ecomode(gm12u320);
6838c2ecf20Sopenharmony_ci	if (ret)
6848c2ecf20Sopenharmony_ci		goto err_put_device;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	ret = gm12u320_conn_init(gm12u320);
6878c2ecf20Sopenharmony_ci	if (ret)
6888c2ecf20Sopenharmony_ci		goto err_put_device;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	ret = drm_simple_display_pipe_init(&gm12u320->dev,
6918c2ecf20Sopenharmony_ci					   &gm12u320->pipe,
6928c2ecf20Sopenharmony_ci					   &gm12u320_pipe_funcs,
6938c2ecf20Sopenharmony_ci					   gm12u320_pipe_formats,
6948c2ecf20Sopenharmony_ci					   ARRAY_SIZE(gm12u320_pipe_formats),
6958c2ecf20Sopenharmony_ci					   gm12u320_pipe_modifiers,
6968c2ecf20Sopenharmony_ci					   &gm12u320->conn);
6978c2ecf20Sopenharmony_ci	if (ret)
6988c2ecf20Sopenharmony_ci		goto err_put_device;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	drm_mode_config_reset(dev);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, dev);
7038c2ecf20Sopenharmony_ci	ret = drm_dev_register(dev, 0);
7048c2ecf20Sopenharmony_ci	if (ret)
7058c2ecf20Sopenharmony_ci		goto err_put_device;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	drm_fbdev_generic_setup(dev, 0);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	return 0;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cierr_put_device:
7128c2ecf20Sopenharmony_ci	put_device(gm12u320->dmadev);
7138c2ecf20Sopenharmony_ci	return ret;
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic void gm12u320_usb_disconnect(struct usb_interface *interface)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	struct drm_device *dev = usb_get_intfdata(interface);
7198c2ecf20Sopenharmony_ci	struct gm12u320_device *gm12u320 = to_gm12u320(dev);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	put_device(gm12u320->dmadev);
7228c2ecf20Sopenharmony_ci	gm12u320->dmadev = NULL;
7238c2ecf20Sopenharmony_ci	drm_dev_unplug(dev);
7248c2ecf20Sopenharmony_ci	drm_atomic_helper_shutdown(dev);
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic __maybe_unused int gm12u320_suspend(struct usb_interface *interface,
7288c2ecf20Sopenharmony_ci					   pm_message_t message)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	struct drm_device *dev = usb_get_intfdata(interface);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	return drm_mode_config_helper_suspend(dev);
7338c2ecf20Sopenharmony_ci}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_cistatic __maybe_unused int gm12u320_resume(struct usb_interface *interface)
7368c2ecf20Sopenharmony_ci{
7378c2ecf20Sopenharmony_ci	struct drm_device *dev = usb_get_intfdata(interface);
7388c2ecf20Sopenharmony_ci	struct gm12u320_device *gm12u320 = to_gm12u320(dev);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	gm12u320_set_ecomode(gm12u320);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	return drm_mode_config_helper_resume(dev);
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = {
7468c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x1de1, 0xc102) },
7478c2ecf20Sopenharmony_ci	{},
7488c2ecf20Sopenharmony_ci};
7498c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic struct usb_driver gm12u320_usb_driver = {
7528c2ecf20Sopenharmony_ci	.name = "gm12u320",
7538c2ecf20Sopenharmony_ci	.probe = gm12u320_usb_probe,
7548c2ecf20Sopenharmony_ci	.disconnect = gm12u320_usb_disconnect,
7558c2ecf20Sopenharmony_ci	.id_table = id_table,
7568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
7578c2ecf20Sopenharmony_ci	.suspend = gm12u320_suspend,
7588c2ecf20Sopenharmony_ci	.resume = gm12u320_resume,
7598c2ecf20Sopenharmony_ci	.reset_resume = gm12u320_resume,
7608c2ecf20Sopenharmony_ci#endif
7618c2ecf20Sopenharmony_ci};
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cimodule_usb_driver(gm12u320_usb_driver);
7648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
7658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
766