xref: /linux/drivers/gpu/drm/tiny/gm12u320.c (revision 7ef64ed121c7b9bef7b79b08022a5a847bac0849)
177b8cabfSNoralf Trønnes // SPDX-License-Identifier: GPL-2.0+
277b8cabfSNoralf Trønnes /*
377b8cabfSNoralf Trønnes  * Copyright 2019 Hans de Goede <hdegoede@redhat.com>
477b8cabfSNoralf Trønnes  */
577b8cabfSNoralf Trønnes 
677b8cabfSNoralf Trønnes #include <linux/dma-buf.h>
777b8cabfSNoralf Trønnes #include <linux/module.h>
877b8cabfSNoralf Trønnes #include <linux/usb.h>
977b8cabfSNoralf Trønnes 
1077b8cabfSNoralf Trønnes #include <drm/drm_atomic_helper.h>
1177b8cabfSNoralf Trønnes #include <drm/drm_atomic_state_helper.h>
1277b8cabfSNoralf Trønnes #include <drm/drm_connector.h>
1377b8cabfSNoralf Trønnes #include <drm/drm_damage_helper.h>
1477b8cabfSNoralf Trønnes #include <drm/drm_drv.h>
1577b8cabfSNoralf Trønnes #include <drm/drm_fb_helper.h>
1677b8cabfSNoralf Trønnes #include <drm/drm_file.h>
1777b8cabfSNoralf Trønnes #include <drm/drm_format_helper.h>
1877b8cabfSNoralf Trønnes #include <drm/drm_fourcc.h>
1977b8cabfSNoralf Trønnes #include <drm/drm_gem_shmem_helper.h>
2077b8cabfSNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h>
2177b8cabfSNoralf Trønnes #include <drm/drm_ioctl.h>
22b6731025SDaniel Vetter #include <drm/drm_managed.h>
2377b8cabfSNoralf Trønnes #include <drm/drm_modeset_helper_vtables.h>
2477b8cabfSNoralf Trønnes #include <drm/drm_probe_helper.h>
2577b8cabfSNoralf Trønnes #include <drm/drm_simple_kms_helper.h>
2677b8cabfSNoralf Trønnes 
2777b8cabfSNoralf Trønnes static bool eco_mode;
2877b8cabfSNoralf Trønnes module_param(eco_mode, bool, 0644);
2977b8cabfSNoralf Trønnes MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)");
3077b8cabfSNoralf Trønnes 
3177b8cabfSNoralf Trønnes #define DRIVER_NAME		"gm12u320"
3277b8cabfSNoralf Trønnes #define DRIVER_DESC		"Grain Media GM12U320 USB projector display"
3377b8cabfSNoralf Trønnes #define DRIVER_DATE		"2019"
3477b8cabfSNoralf Trønnes #define DRIVER_MAJOR		1
3577b8cabfSNoralf Trønnes #define DRIVER_MINOR		0
3677b8cabfSNoralf Trønnes 
3777b8cabfSNoralf Trønnes /*
3877b8cabfSNoralf Trønnes  * The DLP has an actual width of 854 pixels, but that is not a multiple
3977b8cabfSNoralf Trønnes  * of 8, breaking things left and right, so we export a width of 848.
4077b8cabfSNoralf Trønnes  */
4177b8cabfSNoralf Trønnes #define GM12U320_USER_WIDTH		848
4277b8cabfSNoralf Trønnes #define GM12U320_REAL_WIDTH		854
4377b8cabfSNoralf Trønnes #define GM12U320_HEIGHT			480
4477b8cabfSNoralf Trønnes 
4577b8cabfSNoralf Trønnes #define GM12U320_BLOCK_COUNT		20
4677b8cabfSNoralf Trønnes 
474abfa2e4SHans de Goede #define GM12U320_ERR(fmt, ...) \
484abfa2e4SHans de Goede 	DRM_DEV_ERROR(&gm12u320->udev->dev, fmt, ##__VA_ARGS__)
494abfa2e4SHans de Goede 
5077b8cabfSNoralf Trønnes #define MISC_RCV_EPT			1
5177b8cabfSNoralf Trønnes #define DATA_RCV_EPT			2
5277b8cabfSNoralf Trønnes #define DATA_SND_EPT			3
5377b8cabfSNoralf Trønnes #define MISC_SND_EPT			4
5477b8cabfSNoralf Trønnes 
5577b8cabfSNoralf Trønnes #define DATA_BLOCK_HEADER_SIZE		84
5677b8cabfSNoralf Trønnes #define DATA_BLOCK_CONTENT_SIZE		64512
5777b8cabfSNoralf Trønnes #define DATA_BLOCK_FOOTER_SIZE		20
5877b8cabfSNoralf Trønnes #define DATA_BLOCK_SIZE			(DATA_BLOCK_HEADER_SIZE + \
5977b8cabfSNoralf Trønnes 					 DATA_BLOCK_CONTENT_SIZE + \
6077b8cabfSNoralf Trønnes 					 DATA_BLOCK_FOOTER_SIZE)
6177b8cabfSNoralf Trønnes #define DATA_LAST_BLOCK_CONTENT_SIZE	4032
6277b8cabfSNoralf Trønnes #define DATA_LAST_BLOCK_SIZE		(DATA_BLOCK_HEADER_SIZE + \
6377b8cabfSNoralf Trønnes 					 DATA_LAST_BLOCK_CONTENT_SIZE + \
6477b8cabfSNoralf Trønnes 					 DATA_BLOCK_FOOTER_SIZE)
6577b8cabfSNoralf Trønnes 
6677b8cabfSNoralf Trønnes #define CMD_SIZE			31
6777b8cabfSNoralf Trønnes #define READ_STATUS_SIZE		13
6877b8cabfSNoralf Trønnes #define MISC_VALUE_SIZE			4
6977b8cabfSNoralf Trønnes 
7077b8cabfSNoralf Trønnes #define CMD_TIMEOUT			msecs_to_jiffies(200)
7177b8cabfSNoralf Trønnes #define DATA_TIMEOUT			msecs_to_jiffies(1000)
7277b8cabfSNoralf Trønnes #define IDLE_TIMEOUT			msecs_to_jiffies(2000)
7377b8cabfSNoralf Trønnes #define FIRST_FRAME_TIMEOUT		msecs_to_jiffies(2000)
7477b8cabfSNoralf Trønnes 
7577b8cabfSNoralf Trønnes #define MISC_REQ_GET_SET_ECO_A		0xff
7677b8cabfSNoralf Trønnes #define MISC_REQ_GET_SET_ECO_B		0x35
7777b8cabfSNoralf Trønnes /* Windows driver does once every second, with arg d = 1, other args 0 */
7877b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN1_A		0xff
7977b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN1_B		0x38
8077b8cabfSNoralf Trønnes /* Windows driver does this on init, with arg a, b = 0, c = 0xa0, d = 4 */
8177b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN2_A		0xa5
8277b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN2_B		0x00
8377b8cabfSNoralf Trønnes 
8477b8cabfSNoralf Trønnes struct gm12u320_device {
8577b8cabfSNoralf Trønnes 	struct drm_device	         dev;
8677b8cabfSNoralf Trønnes 	struct drm_simple_display_pipe   pipe;
8777b8cabfSNoralf Trønnes 	struct drm_connector	         conn;
8877b8cabfSNoralf Trønnes 	struct usb_device               *udev;
8977b8cabfSNoralf Trønnes 	unsigned char                   *cmd_buf;
9077b8cabfSNoralf Trønnes 	unsigned char                   *data_buf[GM12U320_BLOCK_COUNT];
9177b8cabfSNoralf Trønnes 	struct {
9277b8cabfSNoralf Trønnes 		bool                     run;
9377b8cabfSNoralf Trønnes 		struct workqueue_struct *workq;
9477b8cabfSNoralf Trønnes 		struct work_struct       work;
9577b8cabfSNoralf Trønnes 		wait_queue_head_t        waitq;
9677b8cabfSNoralf Trønnes 		struct mutex             lock;
9777b8cabfSNoralf Trønnes 		struct drm_framebuffer  *fb;
9877b8cabfSNoralf Trønnes 		struct drm_rect          rect;
9977b8cabfSNoralf Trønnes 	} fb_update;
10077b8cabfSNoralf Trønnes };
10177b8cabfSNoralf Trønnes 
10277b8cabfSNoralf Trønnes static const char cmd_data[CMD_SIZE] = {
10377b8cabfSNoralf Trønnes 	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
10477b8cabfSNoralf Trønnes 	0x68, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff,
10577b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x80, 0x00,
10677b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
10777b8cabfSNoralf Trønnes };
10877b8cabfSNoralf Trønnes 
10977b8cabfSNoralf Trønnes static const char cmd_draw[CMD_SIZE] = {
11077b8cabfSNoralf Trønnes 	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
11177b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe,
11277b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0xc0, 0xd1, 0x05, 0x00, 0x40,
11377b8cabfSNoralf Trønnes 	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
11477b8cabfSNoralf Trønnes };
11577b8cabfSNoralf Trønnes 
11677b8cabfSNoralf Trønnes static const char cmd_misc[CMD_SIZE] = {
11777b8cabfSNoralf Trønnes 	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
11877b8cabfSNoralf Trønnes 	0x04, 0x00, 0x00, 0x00, 0x80, 0x01, 0x10, 0xfd,
11977b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00,
12077b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
12177b8cabfSNoralf Trønnes };
12277b8cabfSNoralf Trønnes 
12377b8cabfSNoralf Trønnes static const char data_block_header[DATA_BLOCK_HEADER_SIZE] = {
12477b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12577b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12677b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12777b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12877b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
12977b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13077b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13177b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13277b8cabfSNoralf Trønnes 	0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13377b8cabfSNoralf Trønnes 	0x00, 0x04, 0x15, 0x00, 0x00, 0xfc, 0x00, 0x00,
13477b8cabfSNoralf Trønnes 	0x01, 0x00, 0x00, 0xdb
13577b8cabfSNoralf Trønnes };
13677b8cabfSNoralf Trønnes 
13777b8cabfSNoralf Trønnes static const char data_last_block_header[DATA_BLOCK_HEADER_SIZE] = {
13877b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13977b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14077b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14177b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14277b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14377b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14477b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14577b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14677b8cabfSNoralf Trønnes 	0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14777b8cabfSNoralf Trønnes 	0x2a, 0x00, 0x20, 0x00, 0xc0, 0x0f, 0x00, 0x00,
14877b8cabfSNoralf Trønnes 	0x01, 0x00, 0x00, 0xd7
14977b8cabfSNoralf Trønnes };
15077b8cabfSNoralf Trønnes 
15177b8cabfSNoralf Trønnes static const char data_block_footer[DATA_BLOCK_FOOTER_SIZE] = {
15277b8cabfSNoralf Trønnes 	0xfb, 0x14, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
15377b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15477b8cabfSNoralf Trønnes 	0x80, 0x00, 0x00, 0x4f
15577b8cabfSNoralf Trønnes };
15677b8cabfSNoralf Trønnes 
15777b8cabfSNoralf Trønnes static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320)
15877b8cabfSNoralf Trønnes {
15977b8cabfSNoralf Trønnes 	int i, block_size;
16077b8cabfSNoralf Trønnes 	const char *hdr;
16177b8cabfSNoralf Trønnes 
16208373edcSDaniel Vetter 	gm12u320->cmd_buf = drmm_kmalloc(&gm12u320->dev, CMD_SIZE, GFP_KERNEL);
16377b8cabfSNoralf Trønnes 	if (!gm12u320->cmd_buf)
16477b8cabfSNoralf Trønnes 		return -ENOMEM;
16577b8cabfSNoralf Trønnes 
16677b8cabfSNoralf Trønnes 	for (i = 0; i < GM12U320_BLOCK_COUNT; i++) {
16777b8cabfSNoralf Trønnes 		if (i == GM12U320_BLOCK_COUNT - 1) {
16877b8cabfSNoralf Trønnes 			block_size = DATA_LAST_BLOCK_SIZE;
16977b8cabfSNoralf Trønnes 			hdr = data_last_block_header;
17077b8cabfSNoralf Trønnes 		} else {
17177b8cabfSNoralf Trønnes 			block_size = DATA_BLOCK_SIZE;
17277b8cabfSNoralf Trønnes 			hdr = data_block_header;
17377b8cabfSNoralf Trønnes 		}
17477b8cabfSNoralf Trønnes 
17508373edcSDaniel Vetter 		gm12u320->data_buf[i] = drmm_kzalloc(&gm12u320->dev,
17608373edcSDaniel Vetter 						     block_size, GFP_KERNEL);
17777b8cabfSNoralf Trønnes 		if (!gm12u320->data_buf[i])
17877b8cabfSNoralf Trønnes 			return -ENOMEM;
17977b8cabfSNoralf Trønnes 
18077b8cabfSNoralf Trønnes 		memcpy(gm12u320->data_buf[i], hdr, DATA_BLOCK_HEADER_SIZE);
18177b8cabfSNoralf Trønnes 		memcpy(gm12u320->data_buf[i] +
18277b8cabfSNoralf Trønnes 				(block_size - DATA_BLOCK_FOOTER_SIZE),
18377b8cabfSNoralf Trønnes 		       data_block_footer, DATA_BLOCK_FOOTER_SIZE);
18477b8cabfSNoralf Trønnes 	}
18577b8cabfSNoralf Trønnes 
18677b8cabfSNoralf Trønnes 	gm12u320->fb_update.workq = create_singlethread_workqueue(DRIVER_NAME);
18777b8cabfSNoralf Trønnes 	if (!gm12u320->fb_update.workq)
18877b8cabfSNoralf Trønnes 		return -ENOMEM;
18977b8cabfSNoralf Trønnes 
19077b8cabfSNoralf Trønnes 	return 0;
19177b8cabfSNoralf Trønnes }
19277b8cabfSNoralf Trønnes 
19377b8cabfSNoralf Trønnes static void gm12u320_usb_free(struct gm12u320_device *gm12u320)
19477b8cabfSNoralf Trønnes {
19577b8cabfSNoralf Trønnes 	if (gm12u320->fb_update.workq)
19677b8cabfSNoralf Trønnes 		destroy_workqueue(gm12u320->fb_update.workq);
19777b8cabfSNoralf Trønnes }
19877b8cabfSNoralf Trønnes 
19977b8cabfSNoralf Trønnes static int gm12u320_misc_request(struct gm12u320_device *gm12u320,
20077b8cabfSNoralf Trønnes 				 u8 req_a, u8 req_b,
20177b8cabfSNoralf Trønnes 				 u8 arg_a, u8 arg_b, u8 arg_c, u8 arg_d)
20277b8cabfSNoralf Trønnes {
20377b8cabfSNoralf Trønnes 	int ret, len;
20477b8cabfSNoralf Trønnes 
20577b8cabfSNoralf Trønnes 	memcpy(gm12u320->cmd_buf, &cmd_misc, CMD_SIZE);
20677b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[20] = req_a;
20777b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[21] = req_b;
20877b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[22] = arg_a;
20977b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[23] = arg_b;
21077b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[24] = arg_c;
21177b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[25] = arg_d;
21277b8cabfSNoralf Trønnes 
21377b8cabfSNoralf Trønnes 	/* Send request */
21477b8cabfSNoralf Trønnes 	ret = usb_bulk_msg(gm12u320->udev,
21577b8cabfSNoralf Trønnes 			   usb_sndbulkpipe(gm12u320->udev, MISC_SND_EPT),
21677b8cabfSNoralf Trønnes 			   gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
21777b8cabfSNoralf Trønnes 	if (ret || len != CMD_SIZE) {
2184abfa2e4SHans de Goede 		GM12U320_ERR("Misc. req. error %d\n", ret);
21977b8cabfSNoralf Trønnes 		return -EIO;
22077b8cabfSNoralf Trønnes 	}
22177b8cabfSNoralf Trønnes 
22277b8cabfSNoralf Trønnes 	/* Read value */
22377b8cabfSNoralf Trønnes 	ret = usb_bulk_msg(gm12u320->udev,
22477b8cabfSNoralf Trønnes 			   usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT),
22577b8cabfSNoralf Trønnes 			   gm12u320->cmd_buf, MISC_VALUE_SIZE, &len,
22677b8cabfSNoralf Trønnes 			   DATA_TIMEOUT);
22777b8cabfSNoralf Trønnes 	if (ret || len != MISC_VALUE_SIZE) {
2284abfa2e4SHans de Goede 		GM12U320_ERR("Misc. value error %d\n", ret);
22977b8cabfSNoralf Trønnes 		return -EIO;
23077b8cabfSNoralf Trønnes 	}
23177b8cabfSNoralf Trønnes 	/* cmd_buf[0] now contains the read value, which we don't use */
23277b8cabfSNoralf Trønnes 
23377b8cabfSNoralf Trønnes 	/* Read status */
23477b8cabfSNoralf Trønnes 	ret = usb_bulk_msg(gm12u320->udev,
23577b8cabfSNoralf Trønnes 			   usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT),
23677b8cabfSNoralf Trønnes 			   gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
23777b8cabfSNoralf Trønnes 			   CMD_TIMEOUT);
23877b8cabfSNoralf Trønnes 	if (ret || len != READ_STATUS_SIZE) {
2394abfa2e4SHans de Goede 		GM12U320_ERR("Misc. status error %d\n", ret);
24077b8cabfSNoralf Trønnes 		return -EIO;
24177b8cabfSNoralf Trønnes 	}
24277b8cabfSNoralf Trønnes 
24377b8cabfSNoralf Trønnes 	return 0;
24477b8cabfSNoralf Trønnes }
24577b8cabfSNoralf Trønnes 
24677b8cabfSNoralf Trønnes static void gm12u320_32bpp_to_24bpp_packed(u8 *dst, u8 *src, int len)
24777b8cabfSNoralf Trønnes {
24877b8cabfSNoralf Trønnes 	while (len--) {
24977b8cabfSNoralf Trønnes 		*dst++ = *src++;
25077b8cabfSNoralf Trønnes 		*dst++ = *src++;
25177b8cabfSNoralf Trønnes 		*dst++ = *src++;
25277b8cabfSNoralf Trønnes 		src++;
25377b8cabfSNoralf Trønnes 	}
25477b8cabfSNoralf Trønnes }
25577b8cabfSNoralf Trønnes 
25677b8cabfSNoralf Trønnes static void gm12u320_copy_fb_to_blocks(struct gm12u320_device *gm12u320)
25777b8cabfSNoralf Trønnes {
25877b8cabfSNoralf Trønnes 	int block, dst_offset, len, remain, ret, x1, x2, y1, y2;
25977b8cabfSNoralf Trønnes 	struct drm_framebuffer *fb;
26077b8cabfSNoralf Trønnes 	void *vaddr;
26177b8cabfSNoralf Trønnes 	u8 *src;
26277b8cabfSNoralf Trønnes 
26377b8cabfSNoralf Trønnes 	mutex_lock(&gm12u320->fb_update.lock);
26477b8cabfSNoralf Trønnes 
26577b8cabfSNoralf Trønnes 	if (!gm12u320->fb_update.fb)
26677b8cabfSNoralf Trønnes 		goto unlock;
26777b8cabfSNoralf Trønnes 
26877b8cabfSNoralf Trønnes 	fb = gm12u320->fb_update.fb;
26977b8cabfSNoralf Trønnes 	x1 = gm12u320->fb_update.rect.x1;
27077b8cabfSNoralf Trønnes 	x2 = gm12u320->fb_update.rect.x2;
27177b8cabfSNoralf Trønnes 	y1 = gm12u320->fb_update.rect.y1;
27277b8cabfSNoralf Trønnes 	y2 = gm12u320->fb_update.rect.y2;
27377b8cabfSNoralf Trønnes 
27477b8cabfSNoralf Trønnes 	vaddr = drm_gem_shmem_vmap(fb->obj[0]);
27577b8cabfSNoralf Trønnes 	if (IS_ERR(vaddr)) {
2764abfa2e4SHans de Goede 		GM12U320_ERR("failed to vmap fb: %ld\n", PTR_ERR(vaddr));
27777b8cabfSNoralf Trønnes 		goto put_fb;
27877b8cabfSNoralf Trønnes 	}
27977b8cabfSNoralf Trønnes 
28077b8cabfSNoralf Trønnes 	if (fb->obj[0]->import_attach) {
28177b8cabfSNoralf Trønnes 		ret = dma_buf_begin_cpu_access(
28277b8cabfSNoralf Trønnes 			fb->obj[0]->import_attach->dmabuf, DMA_FROM_DEVICE);
28377b8cabfSNoralf Trønnes 		if (ret) {
2844abfa2e4SHans de Goede 			GM12U320_ERR("dma_buf_begin_cpu_access err: %d\n", ret);
28577b8cabfSNoralf Trønnes 			goto vunmap;
28677b8cabfSNoralf Trønnes 		}
28777b8cabfSNoralf Trønnes 	}
28877b8cabfSNoralf Trønnes 
28977b8cabfSNoralf Trønnes 	src = vaddr + y1 * fb->pitches[0] + x1 * 4;
29077b8cabfSNoralf Trønnes 
29177b8cabfSNoralf Trønnes 	x1 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
29277b8cabfSNoralf Trønnes 	x2 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
29377b8cabfSNoralf Trønnes 
29477b8cabfSNoralf Trønnes 	for (; y1 < y2; y1++) {
29577b8cabfSNoralf Trønnes 		remain = 0;
29677b8cabfSNoralf Trønnes 		len = (x2 - x1) * 3;
29777b8cabfSNoralf Trønnes 		dst_offset = (y1 * GM12U320_REAL_WIDTH + x1) * 3;
29877b8cabfSNoralf Trønnes 		block = dst_offset / DATA_BLOCK_CONTENT_SIZE;
29977b8cabfSNoralf Trønnes 		dst_offset %= DATA_BLOCK_CONTENT_SIZE;
30077b8cabfSNoralf Trønnes 
30177b8cabfSNoralf Trønnes 		if ((dst_offset + len) > DATA_BLOCK_CONTENT_SIZE) {
30277b8cabfSNoralf Trønnes 			remain = dst_offset + len - DATA_BLOCK_CONTENT_SIZE;
30377b8cabfSNoralf Trønnes 			len = DATA_BLOCK_CONTENT_SIZE - dst_offset;
30477b8cabfSNoralf Trønnes 		}
30577b8cabfSNoralf Trønnes 
30677b8cabfSNoralf Trønnes 		dst_offset += DATA_BLOCK_HEADER_SIZE;
30777b8cabfSNoralf Trønnes 		len /= 3;
30877b8cabfSNoralf Trønnes 
30977b8cabfSNoralf Trønnes 		gm12u320_32bpp_to_24bpp_packed(
31077b8cabfSNoralf Trønnes 			gm12u320->data_buf[block] + dst_offset,
31177b8cabfSNoralf Trønnes 			src, len);
31277b8cabfSNoralf Trønnes 
31377b8cabfSNoralf Trønnes 		if (remain) {
31477b8cabfSNoralf Trønnes 			block++;
31577b8cabfSNoralf Trønnes 			dst_offset = DATA_BLOCK_HEADER_SIZE;
31677b8cabfSNoralf Trønnes 			gm12u320_32bpp_to_24bpp_packed(
31777b8cabfSNoralf Trønnes 				gm12u320->data_buf[block] + dst_offset,
31877b8cabfSNoralf Trønnes 				src + len * 4, remain / 3);
31977b8cabfSNoralf Trønnes 		}
32077b8cabfSNoralf Trønnes 		src += fb->pitches[0];
32177b8cabfSNoralf Trønnes 	}
32277b8cabfSNoralf Trønnes 
32377b8cabfSNoralf Trønnes 	if (fb->obj[0]->import_attach) {
32477b8cabfSNoralf Trønnes 		ret = dma_buf_end_cpu_access(fb->obj[0]->import_attach->dmabuf,
32577b8cabfSNoralf Trønnes 					     DMA_FROM_DEVICE);
32677b8cabfSNoralf Trønnes 		if (ret)
3274abfa2e4SHans de Goede 			GM12U320_ERR("dma_buf_end_cpu_access err: %d\n", ret);
32877b8cabfSNoralf Trønnes 	}
32977b8cabfSNoralf Trønnes vunmap:
33077b8cabfSNoralf Trønnes 	drm_gem_shmem_vunmap(fb->obj[0], vaddr);
33177b8cabfSNoralf Trønnes put_fb:
33277b8cabfSNoralf Trønnes 	drm_framebuffer_put(fb);
33377b8cabfSNoralf Trønnes 	gm12u320->fb_update.fb = NULL;
33477b8cabfSNoralf Trønnes unlock:
33577b8cabfSNoralf Trønnes 	mutex_unlock(&gm12u320->fb_update.lock);
33677b8cabfSNoralf Trønnes }
33777b8cabfSNoralf Trønnes 
33877b8cabfSNoralf Trønnes static void gm12u320_fb_update_work(struct work_struct *work)
33977b8cabfSNoralf Trønnes {
34077b8cabfSNoralf Trønnes 	struct gm12u320_device *gm12u320 =
34177b8cabfSNoralf Trønnes 		container_of(work, struct gm12u320_device, fb_update.work);
34277b8cabfSNoralf Trønnes 	int draw_status_timeout = FIRST_FRAME_TIMEOUT;
34377b8cabfSNoralf Trønnes 	int block, block_size, len;
34477b8cabfSNoralf Trønnes 	int frame = 0;
34577b8cabfSNoralf Trønnes 	int ret = 0;
34677b8cabfSNoralf Trønnes 
34777b8cabfSNoralf Trønnes 	while (gm12u320->fb_update.run) {
34877b8cabfSNoralf Trønnes 		gm12u320_copy_fb_to_blocks(gm12u320);
34977b8cabfSNoralf Trønnes 
35077b8cabfSNoralf Trønnes 		for (block = 0; block < GM12U320_BLOCK_COUNT; block++) {
35177b8cabfSNoralf Trønnes 			if (block == GM12U320_BLOCK_COUNT - 1)
35277b8cabfSNoralf Trønnes 				block_size = DATA_LAST_BLOCK_SIZE;
35377b8cabfSNoralf Trønnes 			else
35477b8cabfSNoralf Trønnes 				block_size = DATA_BLOCK_SIZE;
35577b8cabfSNoralf Trønnes 
35677b8cabfSNoralf Trønnes 			/* Send data command to device */
35777b8cabfSNoralf Trønnes 			memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE);
35877b8cabfSNoralf Trønnes 			gm12u320->cmd_buf[8] = block_size & 0xff;
35977b8cabfSNoralf Trønnes 			gm12u320->cmd_buf[9] = block_size >> 8;
36077b8cabfSNoralf Trønnes 			gm12u320->cmd_buf[20] = 0xfc - block * 4;
36177b8cabfSNoralf Trønnes 			gm12u320->cmd_buf[21] = block | (frame << 7);
36277b8cabfSNoralf Trønnes 
36377b8cabfSNoralf Trønnes 			ret = usb_bulk_msg(gm12u320->udev,
36477b8cabfSNoralf Trønnes 				usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
36577b8cabfSNoralf Trønnes 				gm12u320->cmd_buf, CMD_SIZE, &len,
36677b8cabfSNoralf Trønnes 				CMD_TIMEOUT);
36777b8cabfSNoralf Trønnes 			if (ret || len != CMD_SIZE)
36877b8cabfSNoralf Trønnes 				goto err;
36977b8cabfSNoralf Trønnes 
37077b8cabfSNoralf Trønnes 			/* Send data block to device */
37177b8cabfSNoralf Trønnes 			ret = usb_bulk_msg(gm12u320->udev,
37277b8cabfSNoralf Trønnes 				usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
37377b8cabfSNoralf Trønnes 				gm12u320->data_buf[block], block_size,
37477b8cabfSNoralf Trønnes 				&len, DATA_TIMEOUT);
37577b8cabfSNoralf Trønnes 			if (ret || len != block_size)
37677b8cabfSNoralf Trønnes 				goto err;
37777b8cabfSNoralf Trønnes 
37877b8cabfSNoralf Trønnes 			/* Read status */
37977b8cabfSNoralf Trønnes 			ret = usb_bulk_msg(gm12u320->udev,
38077b8cabfSNoralf Trønnes 				usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
38177b8cabfSNoralf Trønnes 				gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
38277b8cabfSNoralf Trønnes 				CMD_TIMEOUT);
38377b8cabfSNoralf Trønnes 			if (ret || len != READ_STATUS_SIZE)
38477b8cabfSNoralf Trønnes 				goto err;
38577b8cabfSNoralf Trønnes 		}
38677b8cabfSNoralf Trønnes 
38777b8cabfSNoralf Trønnes 		/* Send draw command to device */
38877b8cabfSNoralf Trønnes 		memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE);
38977b8cabfSNoralf Trønnes 		ret = usb_bulk_msg(gm12u320->udev,
39077b8cabfSNoralf Trønnes 			usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
39177b8cabfSNoralf Trønnes 			gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
39277b8cabfSNoralf Trønnes 		if (ret || len != CMD_SIZE)
39377b8cabfSNoralf Trønnes 			goto err;
39477b8cabfSNoralf Trønnes 
39577b8cabfSNoralf Trønnes 		/* Read status */
39677b8cabfSNoralf Trønnes 		ret = usb_bulk_msg(gm12u320->udev,
39777b8cabfSNoralf Trønnes 			usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
39877b8cabfSNoralf Trønnes 			gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
39977b8cabfSNoralf Trønnes 			draw_status_timeout);
40077b8cabfSNoralf Trønnes 		if (ret || len != READ_STATUS_SIZE)
40177b8cabfSNoralf Trønnes 			goto err;
40277b8cabfSNoralf Trønnes 
40377b8cabfSNoralf Trønnes 		draw_status_timeout = CMD_TIMEOUT;
40477b8cabfSNoralf Trønnes 		frame = !frame;
40577b8cabfSNoralf Trønnes 
40677b8cabfSNoralf Trønnes 		/*
40777b8cabfSNoralf Trønnes 		 * We must draw a frame every 2s otherwise the projector
40877b8cabfSNoralf Trønnes 		 * switches back to showing its logo.
40977b8cabfSNoralf Trønnes 		 */
41077b8cabfSNoralf Trønnes 		wait_event_timeout(gm12u320->fb_update.waitq,
4119b61db1aSHans de Goede 				   !gm12u320->fb_update.run ||
4129b61db1aSHans de Goede 					gm12u320->fb_update.fb != NULL,
41377b8cabfSNoralf Trønnes 				   IDLE_TIMEOUT);
41477b8cabfSNoralf Trønnes 	}
41577b8cabfSNoralf Trønnes 	return;
41677b8cabfSNoralf Trønnes err:
41777b8cabfSNoralf Trønnes 	/* Do not log errors caused by module unload or device unplug */
418ac9fd659SHans de Goede 	if (ret != -ENODEV && ret != -ECONNRESET && ret != -ESHUTDOWN)
4194abfa2e4SHans de Goede 		GM12U320_ERR("Frame update error: %d\n", ret);
42077b8cabfSNoralf Trønnes }
42177b8cabfSNoralf Trønnes 
42277b8cabfSNoralf Trønnes static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb,
42377b8cabfSNoralf Trønnes 				   struct drm_rect *dirty)
42477b8cabfSNoralf Trønnes {
42577b8cabfSNoralf Trønnes 	struct gm12u320_device *gm12u320 = fb->dev->dev_private;
42677b8cabfSNoralf Trønnes 	struct drm_framebuffer *old_fb = NULL;
42777b8cabfSNoralf Trønnes 	bool wakeup = false;
42877b8cabfSNoralf Trønnes 
42977b8cabfSNoralf Trønnes 	mutex_lock(&gm12u320->fb_update.lock);
43077b8cabfSNoralf Trønnes 
43177b8cabfSNoralf Trønnes 	if (gm12u320->fb_update.fb != fb) {
43277b8cabfSNoralf Trønnes 		old_fb = gm12u320->fb_update.fb;
43377b8cabfSNoralf Trønnes 		drm_framebuffer_get(fb);
43477b8cabfSNoralf Trønnes 		gm12u320->fb_update.fb = fb;
43577b8cabfSNoralf Trønnes 		gm12u320->fb_update.rect = *dirty;
43677b8cabfSNoralf Trønnes 		wakeup = true;
43777b8cabfSNoralf Trønnes 	} else {
43877b8cabfSNoralf Trønnes 		struct drm_rect *rect = &gm12u320->fb_update.rect;
43977b8cabfSNoralf Trønnes 
44077b8cabfSNoralf Trønnes 		rect->x1 = min(rect->x1, dirty->x1);
44177b8cabfSNoralf Trønnes 		rect->y1 = min(rect->y1, dirty->y1);
44277b8cabfSNoralf Trønnes 		rect->x2 = max(rect->x2, dirty->x2);
44377b8cabfSNoralf Trønnes 		rect->y2 = max(rect->y2, dirty->y2);
44477b8cabfSNoralf Trønnes 	}
44577b8cabfSNoralf Trønnes 
44677b8cabfSNoralf Trønnes 	mutex_unlock(&gm12u320->fb_update.lock);
44777b8cabfSNoralf Trønnes 
44877b8cabfSNoralf Trønnes 	if (wakeup)
44977b8cabfSNoralf Trønnes 		wake_up(&gm12u320->fb_update.waitq);
45077b8cabfSNoralf Trønnes 
45177b8cabfSNoralf Trønnes 	if (old_fb)
45277b8cabfSNoralf Trønnes 		drm_framebuffer_put(old_fb);
45377b8cabfSNoralf Trønnes }
45477b8cabfSNoralf Trønnes 
45577b8cabfSNoralf Trønnes static void gm12u320_start_fb_update(struct gm12u320_device *gm12u320)
45677b8cabfSNoralf Trønnes {
45777b8cabfSNoralf Trønnes 	mutex_lock(&gm12u320->fb_update.lock);
45877b8cabfSNoralf Trønnes 	gm12u320->fb_update.run = true;
45977b8cabfSNoralf Trønnes 	mutex_unlock(&gm12u320->fb_update.lock);
46077b8cabfSNoralf Trønnes 
46177b8cabfSNoralf Trønnes 	queue_work(gm12u320->fb_update.workq, &gm12u320->fb_update.work);
46277b8cabfSNoralf Trønnes }
46377b8cabfSNoralf Trønnes 
46477b8cabfSNoralf Trønnes static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320)
46577b8cabfSNoralf Trønnes {
46677b8cabfSNoralf Trønnes 	mutex_lock(&gm12u320->fb_update.lock);
46777b8cabfSNoralf Trønnes 	gm12u320->fb_update.run = false;
46877b8cabfSNoralf Trønnes 	mutex_unlock(&gm12u320->fb_update.lock);
46977b8cabfSNoralf Trønnes 
47077b8cabfSNoralf Trønnes 	wake_up(&gm12u320->fb_update.waitq);
47177b8cabfSNoralf Trønnes 	cancel_work_sync(&gm12u320->fb_update.work);
47277b8cabfSNoralf Trønnes 
47377b8cabfSNoralf Trønnes 	mutex_lock(&gm12u320->fb_update.lock);
47477b8cabfSNoralf Trønnes 	if (gm12u320->fb_update.fb) {
47577b8cabfSNoralf Trønnes 		drm_framebuffer_put(gm12u320->fb_update.fb);
47677b8cabfSNoralf Trønnes 		gm12u320->fb_update.fb = NULL;
47777b8cabfSNoralf Trønnes 	}
47877b8cabfSNoralf Trønnes 	mutex_unlock(&gm12u320->fb_update.lock);
47977b8cabfSNoralf Trønnes }
48077b8cabfSNoralf Trønnes 
48177b8cabfSNoralf Trønnes static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320)
48277b8cabfSNoralf Trønnes {
48377b8cabfSNoralf Trønnes 	return gm12u320_misc_request(gm12u320, MISC_REQ_GET_SET_ECO_A,
48477b8cabfSNoralf Trønnes 				     MISC_REQ_GET_SET_ECO_B, 0x01 /* set */,
48577b8cabfSNoralf Trønnes 				     eco_mode ? 0x01 : 0x00, 0x00, 0x01);
48677b8cabfSNoralf Trønnes }
48777b8cabfSNoralf Trønnes 
48877b8cabfSNoralf Trønnes /* ------------------------------------------------------------------ */
48977b8cabfSNoralf Trønnes /* gm12u320 connector						      */
49077b8cabfSNoralf Trønnes 
49177b8cabfSNoralf Trønnes /*
49277b8cabfSNoralf Trønnes  * We use fake EDID info so that userspace know that it is dealing with
49377b8cabfSNoralf Trønnes  * an Acer projector, rather then listing this as an "unknown" monitor.
49477b8cabfSNoralf Trønnes  * Note this assumes this driver is only ever used with the Acer C120, if we
49577b8cabfSNoralf Trønnes  * add support for other devices the vendor and model should be parameterized.
49677b8cabfSNoralf Trønnes  */
49777b8cabfSNoralf Trønnes static struct edid gm12u320_edid = {
49877b8cabfSNoralf Trønnes 	.header		= { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 },
49977b8cabfSNoralf Trønnes 	.mfg_id		= { 0x04, 0x72 },	/* "ACR" */
50077b8cabfSNoralf Trønnes 	.prod_code	= { 0x20, 0xc1 },	/* C120h */
50177b8cabfSNoralf Trønnes 	.serial		= 0xaa55aa55,
50277b8cabfSNoralf Trønnes 	.mfg_week	= 1,
50377b8cabfSNoralf Trønnes 	.mfg_year	= 16,
50477b8cabfSNoralf Trønnes 	.version	= 1,			/* EDID 1.3 */
50577b8cabfSNoralf Trønnes 	.revision	= 3,			/* EDID 1.3 */
50677b8cabfSNoralf Trønnes 	.input		= 0x08,			/* Analog input */
50777b8cabfSNoralf Trønnes 	.features	= 0x0a,			/* Pref timing in DTD 1 */
50877b8cabfSNoralf Trønnes 	.standard_timings = { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 },
50977b8cabfSNoralf Trønnes 			      { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 } },
51077b8cabfSNoralf Trønnes 	.detailed_timings = { {
51177b8cabfSNoralf Trønnes 		.pixel_clock = 3383,
51277b8cabfSNoralf Trønnes 		/* hactive = 848, hblank = 256 */
51377b8cabfSNoralf Trønnes 		.data.pixel_data.hactive_lo = 0x50,
51477b8cabfSNoralf Trønnes 		.data.pixel_data.hblank_lo = 0x00,
51577b8cabfSNoralf Trønnes 		.data.pixel_data.hactive_hblank_hi = 0x31,
51677b8cabfSNoralf Trønnes 		/* vactive = 480, vblank = 28 */
51777b8cabfSNoralf Trønnes 		.data.pixel_data.vactive_lo = 0xe0,
51877b8cabfSNoralf Trønnes 		.data.pixel_data.vblank_lo = 0x1c,
51977b8cabfSNoralf Trønnes 		.data.pixel_data.vactive_vblank_hi = 0x10,
52077b8cabfSNoralf Trønnes 		/* hsync offset 40 pw 128, vsync offset 1 pw 4 */
52177b8cabfSNoralf Trønnes 		.data.pixel_data.hsync_offset_lo = 0x28,
52277b8cabfSNoralf Trønnes 		.data.pixel_data.hsync_pulse_width_lo = 0x80,
52377b8cabfSNoralf Trønnes 		.data.pixel_data.vsync_offset_pulse_width_lo = 0x14,
52477b8cabfSNoralf Trønnes 		.data.pixel_data.hsync_vsync_offset_pulse_width_hi = 0x00,
52577b8cabfSNoralf Trønnes 		/* Digital separate syncs, hsync+, vsync+ */
52677b8cabfSNoralf Trønnes 		.data.pixel_data.misc = 0x1e,
52777b8cabfSNoralf Trønnes 	}, {
52877b8cabfSNoralf Trønnes 		.pixel_clock = 0,
52977b8cabfSNoralf Trønnes 		.data.other_data.type = 0xfd, /* Monitor ranges */
53077b8cabfSNoralf Trønnes 		.data.other_data.data.range.min_vfreq = 59,
53177b8cabfSNoralf Trønnes 		.data.other_data.data.range.max_vfreq = 61,
53277b8cabfSNoralf Trønnes 		.data.other_data.data.range.min_hfreq_khz = 29,
53377b8cabfSNoralf Trønnes 		.data.other_data.data.range.max_hfreq_khz = 32,
53477b8cabfSNoralf Trønnes 		.data.other_data.data.range.pixel_clock_mhz = 4, /* 40 MHz */
53577b8cabfSNoralf Trønnes 		.data.other_data.data.range.flags = 0,
53677b8cabfSNoralf Trønnes 		.data.other_data.data.range.formula.cvt = {
53777b8cabfSNoralf Trønnes 			0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 },
53877b8cabfSNoralf Trønnes 	}, {
53977b8cabfSNoralf Trønnes 		.pixel_clock = 0,
54077b8cabfSNoralf Trønnes 		.data.other_data.type = 0xfc, /* Model string */
54177b8cabfSNoralf Trønnes 		.data.other_data.data.str.str = {
54277b8cabfSNoralf Trønnes 			'P', 'r', 'o', 'j', 'e', 'c', 't', 'o', 'r', '\n',
54377b8cabfSNoralf Trønnes 			' ', ' ',  ' ' },
54477b8cabfSNoralf Trønnes 	}, {
54577b8cabfSNoralf Trønnes 		.pixel_clock = 0,
54677b8cabfSNoralf Trønnes 		.data.other_data.type = 0xfe, /* Unspecified text / padding */
54777b8cabfSNoralf Trønnes 		.data.other_data.data.str.str = {
54877b8cabfSNoralf Trønnes 			'\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
54977b8cabfSNoralf Trønnes 			' ', ' ',  ' ' },
55077b8cabfSNoralf Trønnes 	} },
55177b8cabfSNoralf Trønnes 	.checksum = 0x13,
55277b8cabfSNoralf Trønnes };
55377b8cabfSNoralf Trønnes 
55477b8cabfSNoralf Trønnes static int gm12u320_conn_get_modes(struct drm_connector *connector)
55577b8cabfSNoralf Trønnes {
55677b8cabfSNoralf Trønnes 	drm_connector_update_edid_property(connector, &gm12u320_edid);
55777b8cabfSNoralf Trønnes 	return drm_add_edid_modes(connector, &gm12u320_edid);
55877b8cabfSNoralf Trønnes }
55977b8cabfSNoralf Trønnes 
56077b8cabfSNoralf Trønnes static const struct drm_connector_helper_funcs gm12u320_conn_helper_funcs = {
56177b8cabfSNoralf Trønnes 	.get_modes = gm12u320_conn_get_modes,
56277b8cabfSNoralf Trønnes };
56377b8cabfSNoralf Trønnes 
56477b8cabfSNoralf Trønnes static const struct drm_connector_funcs gm12u320_conn_funcs = {
56577b8cabfSNoralf Trønnes 	.fill_modes = drm_helper_probe_single_connector_modes,
56677b8cabfSNoralf Trønnes 	.destroy = drm_connector_cleanup,
56777b8cabfSNoralf Trønnes 	.reset = drm_atomic_helper_connector_reset,
56877b8cabfSNoralf Trønnes 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
56977b8cabfSNoralf Trønnes 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
57077b8cabfSNoralf Trønnes };
57177b8cabfSNoralf Trønnes 
57277b8cabfSNoralf Trønnes static int gm12u320_conn_init(struct gm12u320_device *gm12u320)
57377b8cabfSNoralf Trønnes {
57477b8cabfSNoralf Trønnes 	drm_connector_helper_add(&gm12u320->conn, &gm12u320_conn_helper_funcs);
57577b8cabfSNoralf Trønnes 	return drm_connector_init(&gm12u320->dev, &gm12u320->conn,
57677b8cabfSNoralf Trønnes 				  &gm12u320_conn_funcs, DRM_MODE_CONNECTOR_VGA);
57777b8cabfSNoralf Trønnes }
57877b8cabfSNoralf Trønnes 
57977b8cabfSNoralf Trønnes /* ------------------------------------------------------------------ */
58077b8cabfSNoralf Trønnes /* gm12u320 (simple) display pipe				      */
58177b8cabfSNoralf Trønnes 
58277b8cabfSNoralf Trønnes static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe,
58377b8cabfSNoralf Trønnes 				 struct drm_crtc_state *crtc_state,
58477b8cabfSNoralf Trønnes 				 struct drm_plane_state *plane_state)
58577b8cabfSNoralf Trønnes {
58677b8cabfSNoralf Trønnes 	struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private;
58777b8cabfSNoralf Trønnes 	struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT };
58877b8cabfSNoralf Trønnes 
58977b8cabfSNoralf Trønnes 	gm12u320_fb_mark_dirty(plane_state->fb, &rect);
59077b8cabfSNoralf Trønnes 	gm12u320_start_fb_update(gm12u320);
59177b8cabfSNoralf Trønnes }
59277b8cabfSNoralf Trønnes 
59377b8cabfSNoralf Trønnes static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
59477b8cabfSNoralf Trønnes {
59577b8cabfSNoralf Trønnes 	struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private;
59677b8cabfSNoralf Trønnes 
59777b8cabfSNoralf Trønnes 	gm12u320_stop_fb_update(gm12u320);
59877b8cabfSNoralf Trønnes }
59977b8cabfSNoralf Trønnes 
60077b8cabfSNoralf Trønnes static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe,
60177b8cabfSNoralf Trønnes 				 struct drm_plane_state *old_state)
60277b8cabfSNoralf Trønnes {
60377b8cabfSNoralf Trønnes 	struct drm_plane_state *state = pipe->plane.state;
60477b8cabfSNoralf Trønnes 	struct drm_rect rect;
60577b8cabfSNoralf Trønnes 
60677b8cabfSNoralf Trønnes 	if (drm_atomic_helper_damage_merged(old_state, state, &rect))
60777b8cabfSNoralf Trønnes 		gm12u320_fb_mark_dirty(pipe->plane.state->fb, &rect);
60877b8cabfSNoralf Trønnes }
60977b8cabfSNoralf Trønnes 
61077b8cabfSNoralf Trønnes static const struct drm_simple_display_pipe_funcs gm12u320_pipe_funcs = {
61177b8cabfSNoralf Trønnes 	.enable	    = gm12u320_pipe_enable,
61277b8cabfSNoralf Trønnes 	.disable    = gm12u320_pipe_disable,
61377b8cabfSNoralf Trønnes 	.update	    = gm12u320_pipe_update,
61477b8cabfSNoralf Trønnes };
61577b8cabfSNoralf Trønnes 
61677b8cabfSNoralf Trønnes static const uint32_t gm12u320_pipe_formats[] = {
61777b8cabfSNoralf Trønnes 	DRM_FORMAT_XRGB8888,
61877b8cabfSNoralf Trønnes };
61977b8cabfSNoralf Trønnes 
62077b8cabfSNoralf Trønnes static const uint64_t gm12u320_pipe_modifiers[] = {
62177b8cabfSNoralf Trønnes 	DRM_FORMAT_MOD_LINEAR,
62277b8cabfSNoralf Trønnes 	DRM_FORMAT_MOD_INVALID
62377b8cabfSNoralf Trønnes };
62477b8cabfSNoralf Trønnes 
62577b8cabfSNoralf Trønnes static void gm12u320_driver_release(struct drm_device *dev)
62677b8cabfSNoralf Trønnes {
62777b8cabfSNoralf Trønnes 	struct gm12u320_device *gm12u320 = dev->dev_private;
62877b8cabfSNoralf Trønnes 
62977b8cabfSNoralf Trønnes 	gm12u320_usb_free(gm12u320);
63077b8cabfSNoralf Trønnes }
63177b8cabfSNoralf Trønnes 
632eee9a2e0SGerd Hoffmann DEFINE_DRM_GEM_FOPS(gm12u320_fops);
63377b8cabfSNoralf Trønnes 
63477b8cabfSNoralf Trønnes static struct drm_driver gm12u320_drm_driver = {
63577b8cabfSNoralf Trønnes 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
63677b8cabfSNoralf Trønnes 
63777b8cabfSNoralf Trønnes 	.name		 = DRIVER_NAME,
63877b8cabfSNoralf Trønnes 	.desc		 = DRIVER_DESC,
63977b8cabfSNoralf Trønnes 	.date		 = DRIVER_DATE,
64077b8cabfSNoralf Trønnes 	.major		 = DRIVER_MAJOR,
64177b8cabfSNoralf Trønnes 	.minor		 = DRIVER_MINOR,
64277b8cabfSNoralf Trønnes 
64377b8cabfSNoralf Trønnes 	.release	 = gm12u320_driver_release,
64477b8cabfSNoralf Trønnes 	.fops		 = &gm12u320_fops,
64577b8cabfSNoralf Trønnes 	DRM_GEM_SHMEM_DRIVER_OPS,
64677b8cabfSNoralf Trønnes };
64777b8cabfSNoralf Trønnes 
64877b8cabfSNoralf Trønnes static const struct drm_mode_config_funcs gm12u320_mode_config_funcs = {
64977b8cabfSNoralf Trønnes 	.fb_create = drm_gem_fb_create_with_dirty,
65077b8cabfSNoralf Trønnes 	.atomic_check = drm_atomic_helper_check,
65177b8cabfSNoralf Trønnes 	.atomic_commit = drm_atomic_helper_commit,
65277b8cabfSNoralf Trønnes };
65377b8cabfSNoralf Trønnes 
65477b8cabfSNoralf Trønnes static int gm12u320_usb_probe(struct usb_interface *interface,
65577b8cabfSNoralf Trønnes 			      const struct usb_device_id *id)
65677b8cabfSNoralf Trønnes {
65777b8cabfSNoralf Trønnes 	struct gm12u320_device *gm12u320;
65877b8cabfSNoralf Trønnes 	struct drm_device *dev;
65977b8cabfSNoralf Trønnes 	int ret;
66077b8cabfSNoralf Trønnes 
66177b8cabfSNoralf Trønnes 	/*
66277b8cabfSNoralf Trønnes 	 * The gm12u320 presents itself to the system as 2 usb mass-storage
66377b8cabfSNoralf Trønnes 	 * interfaces, we only care about / need the first one.
66477b8cabfSNoralf Trønnes 	 */
66577b8cabfSNoralf Trønnes 	if (interface->cur_altsetting->desc.bInterfaceNumber != 0)
66677b8cabfSNoralf Trønnes 		return -ENODEV;
66777b8cabfSNoralf Trønnes 
66877b8cabfSNoralf Trønnes 	gm12u320 = kzalloc(sizeof(*gm12u320), GFP_KERNEL);
66977b8cabfSNoralf Trønnes 	if (gm12u320 == NULL)
67077b8cabfSNoralf Trønnes 		return -ENOMEM;
67177b8cabfSNoralf Trønnes 
67277b8cabfSNoralf Trønnes 	gm12u320->udev = interface_to_usbdev(interface);
67377b8cabfSNoralf Trønnes 	INIT_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work);
67477b8cabfSNoralf Trønnes 	mutex_init(&gm12u320->fb_update.lock);
67577b8cabfSNoralf Trønnes 	init_waitqueue_head(&gm12u320->fb_update.waitq);
67677b8cabfSNoralf Trønnes 
67777b8cabfSNoralf Trønnes 	dev = &gm12u320->dev;
678993f5b19SDaniel Vetter 	ret = devm_drm_dev_init(&interface->dev, dev, &gm12u320_drm_driver);
67977b8cabfSNoralf Trønnes 	if (ret) {
68077b8cabfSNoralf Trønnes 		kfree(gm12u320);
68177b8cabfSNoralf Trønnes 		return ret;
68277b8cabfSNoralf Trønnes 	}
68377b8cabfSNoralf Trønnes 	dev->dev_private = gm12u320;
684b6731025SDaniel Vetter 	drmm_add_final_kfree(dev, gm12u320);
68577b8cabfSNoralf Trønnes 
68608373edcSDaniel Vetter 	ret = drmm_mode_config_init(dev);
68708373edcSDaniel Vetter 	if (ret)
688993f5b19SDaniel Vetter 		return ret;
68908373edcSDaniel Vetter 
69077b8cabfSNoralf Trønnes 	dev->mode_config.min_width = GM12U320_USER_WIDTH;
69177b8cabfSNoralf Trønnes 	dev->mode_config.max_width = GM12U320_USER_WIDTH;
69277b8cabfSNoralf Trønnes 	dev->mode_config.min_height = GM12U320_HEIGHT;
69377b8cabfSNoralf Trønnes 	dev->mode_config.max_height = GM12U320_HEIGHT;
69477b8cabfSNoralf Trønnes 	dev->mode_config.funcs = &gm12u320_mode_config_funcs;
69577b8cabfSNoralf Trønnes 
69677b8cabfSNoralf Trønnes 	ret = gm12u320_usb_alloc(gm12u320);
69777b8cabfSNoralf Trønnes 	if (ret)
698993f5b19SDaniel Vetter 		return ret;
69977b8cabfSNoralf Trønnes 
70077b8cabfSNoralf Trønnes 	ret = gm12u320_set_ecomode(gm12u320);
70177b8cabfSNoralf Trønnes 	if (ret)
702993f5b19SDaniel Vetter 		return ret;
70377b8cabfSNoralf Trønnes 
70477b8cabfSNoralf Trønnes 	ret = gm12u320_conn_init(gm12u320);
70577b8cabfSNoralf Trønnes 	if (ret)
706993f5b19SDaniel Vetter 		return ret;
70777b8cabfSNoralf Trønnes 
70877b8cabfSNoralf Trønnes 	ret = drm_simple_display_pipe_init(&gm12u320->dev,
70977b8cabfSNoralf Trønnes 					   &gm12u320->pipe,
71077b8cabfSNoralf Trønnes 					   &gm12u320_pipe_funcs,
71177b8cabfSNoralf Trønnes 					   gm12u320_pipe_formats,
71277b8cabfSNoralf Trønnes 					   ARRAY_SIZE(gm12u320_pipe_formats),
71377b8cabfSNoralf Trønnes 					   gm12u320_pipe_modifiers,
71477b8cabfSNoralf Trønnes 					   &gm12u320->conn);
71577b8cabfSNoralf Trønnes 	if (ret)
716993f5b19SDaniel Vetter 		return ret;
71777b8cabfSNoralf Trønnes 
71877b8cabfSNoralf Trønnes 	drm_mode_config_reset(dev);
71977b8cabfSNoralf Trønnes 
72077b8cabfSNoralf Trønnes 	usb_set_intfdata(interface, dev);
72177b8cabfSNoralf Trønnes 	ret = drm_dev_register(dev, 0);
72277b8cabfSNoralf Trønnes 	if (ret)
723993f5b19SDaniel Vetter 		return ret;
72477b8cabfSNoralf Trønnes 
7258515090cSHans de Goede 	drm_fbdev_generic_setup(dev, 0);
72677b8cabfSNoralf Trønnes 
72777b8cabfSNoralf Trønnes 	return 0;
72877b8cabfSNoralf Trønnes }
72977b8cabfSNoralf Trønnes 
73077b8cabfSNoralf Trønnes static void gm12u320_usb_disconnect(struct usb_interface *interface)
73177b8cabfSNoralf Trønnes {
73277b8cabfSNoralf Trønnes 	struct drm_device *dev = usb_get_intfdata(interface);
73377b8cabfSNoralf Trønnes 
73477b8cabfSNoralf Trønnes 	drm_dev_unplug(dev);
735*7ef64ed1SDaniel Vetter 	drm_atomic_helper_shutdown(dev);
73677b8cabfSNoralf Trønnes }
73777b8cabfSNoralf Trønnes 
7388515090cSHans de Goede static __maybe_unused int gm12u320_suspend(struct usb_interface *interface,
73977b8cabfSNoralf Trønnes 					   pm_message_t message)
74077b8cabfSNoralf Trønnes {
74177b8cabfSNoralf Trønnes 	struct drm_device *dev = usb_get_intfdata(interface);
74277b8cabfSNoralf Trønnes 
743*7ef64ed1SDaniel Vetter 	return drm_mode_config_helper_suspend(dev);
74477b8cabfSNoralf Trønnes }
74577b8cabfSNoralf Trønnes 
7468515090cSHans de Goede static __maybe_unused int gm12u320_resume(struct usb_interface *interface)
74777b8cabfSNoralf Trønnes {
74877b8cabfSNoralf Trønnes 	struct drm_device *dev = usb_get_intfdata(interface);
74977b8cabfSNoralf Trønnes 	struct gm12u320_device *gm12u320 = dev->dev_private;
75077b8cabfSNoralf Trønnes 
75177b8cabfSNoralf Trønnes 	gm12u320_set_ecomode(gm12u320);
75277b8cabfSNoralf Trønnes 
753*7ef64ed1SDaniel Vetter 	return drm_mode_config_helper_resume(dev);
75477b8cabfSNoralf Trønnes }
75577b8cabfSNoralf Trønnes 
75677b8cabfSNoralf Trønnes static const struct usb_device_id id_table[] = {
75777b8cabfSNoralf Trønnes 	{ USB_DEVICE(0x1de1, 0xc102) },
75877b8cabfSNoralf Trønnes 	{},
75977b8cabfSNoralf Trønnes };
76077b8cabfSNoralf Trønnes MODULE_DEVICE_TABLE(usb, id_table);
76177b8cabfSNoralf Trønnes 
76277b8cabfSNoralf Trønnes static struct usb_driver gm12u320_usb_driver = {
76377b8cabfSNoralf Trønnes 	.name = "gm12u320",
76477b8cabfSNoralf Trønnes 	.probe = gm12u320_usb_probe,
76577b8cabfSNoralf Trønnes 	.disconnect = gm12u320_usb_disconnect,
76677b8cabfSNoralf Trønnes 	.id_table = id_table,
76777b8cabfSNoralf Trønnes #ifdef CONFIG_PM
76877b8cabfSNoralf Trønnes 	.suspend = gm12u320_suspend,
76977b8cabfSNoralf Trønnes 	.resume = gm12u320_resume,
77077b8cabfSNoralf Trønnes 	.reset_resume = gm12u320_resume,
77177b8cabfSNoralf Trønnes #endif
77277b8cabfSNoralf Trønnes };
77377b8cabfSNoralf Trønnes 
77477b8cabfSNoralf Trønnes module_usb_driver(gm12u320_usb_driver);
77577b8cabfSNoralf Trønnes MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
77677b8cabfSNoralf Trønnes MODULE_LICENSE("GPL");
777