xref: /linux/drivers/gpu/drm/tiny/gm12u320.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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/module.h>
749eafb20SPaul Cercueil #include <linux/pm.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>
15255490f9SVille Syrjälä #include <drm/drm_edid.h>
165bd79b70SThomas Zimmermann #include <drm/drm_fbdev_shmem.h>
1777b8cabfSNoralf Trønnes #include <drm/drm_file.h>
1877b8cabfSNoralf Trønnes #include <drm/drm_format_helper.h>
1977b8cabfSNoralf Trønnes #include <drm/drm_fourcc.h>
20720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
214ac0868dSThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
2277b8cabfSNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h>
234ac0868dSThomas Zimmermann #include <drm/drm_gem_shmem_helper.h>
2477b8cabfSNoralf Trønnes #include <drm/drm_ioctl.h>
25b6731025SDaniel Vetter #include <drm/drm_managed.h>
2677b8cabfSNoralf Trønnes #include <drm/drm_modeset_helper_vtables.h>
2777b8cabfSNoralf Trønnes #include <drm/drm_probe_helper.h>
2877b8cabfSNoralf Trønnes #include <drm/drm_simple_kms_helper.h>
2977b8cabfSNoralf Trønnes 
3077b8cabfSNoralf Trønnes static bool eco_mode;
3177b8cabfSNoralf Trønnes module_param(eco_mode, bool, 0644);
3277b8cabfSNoralf Trønnes MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)");
3377b8cabfSNoralf Trønnes 
3477b8cabfSNoralf Trønnes #define DRIVER_NAME		"gm12u320"
3577b8cabfSNoralf Trønnes #define DRIVER_DESC		"Grain Media GM12U320 USB projector display"
3677b8cabfSNoralf Trønnes #define DRIVER_DATE		"2019"
3777b8cabfSNoralf Trønnes #define DRIVER_MAJOR		1
3877b8cabfSNoralf Trønnes #define DRIVER_MINOR		0
3977b8cabfSNoralf Trønnes 
4077b8cabfSNoralf Trønnes /*
4177b8cabfSNoralf Trønnes  * The DLP has an actual width of 854 pixels, but that is not a multiple
4277b8cabfSNoralf Trønnes  * of 8, breaking things left and right, so we export a width of 848.
4377b8cabfSNoralf Trønnes  */
4477b8cabfSNoralf Trønnes #define GM12U320_USER_WIDTH		848
4577b8cabfSNoralf Trønnes #define GM12U320_REAL_WIDTH		854
4677b8cabfSNoralf Trønnes #define GM12U320_HEIGHT			480
4777b8cabfSNoralf Trønnes 
4877b8cabfSNoralf Trønnes #define GM12U320_BLOCK_COUNT		20
4977b8cabfSNoralf Trønnes 
504abfa2e4SHans de Goede #define GM12U320_ERR(fmt, ...) \
510454bc59SThomas Zimmermann 	DRM_DEV_ERROR(gm12u320->dev.dev, fmt, ##__VA_ARGS__)
524abfa2e4SHans de Goede 
5377b8cabfSNoralf Trønnes #define MISC_RCV_EPT			1
5477b8cabfSNoralf Trønnes #define DATA_RCV_EPT			2
5577b8cabfSNoralf Trønnes #define DATA_SND_EPT			3
5677b8cabfSNoralf Trønnes #define MISC_SND_EPT			4
5777b8cabfSNoralf Trønnes 
5877b8cabfSNoralf Trønnes #define DATA_BLOCK_HEADER_SIZE		84
5977b8cabfSNoralf Trønnes #define DATA_BLOCK_CONTENT_SIZE		64512
6077b8cabfSNoralf Trønnes #define DATA_BLOCK_FOOTER_SIZE		20
6177b8cabfSNoralf Trønnes #define DATA_BLOCK_SIZE			(DATA_BLOCK_HEADER_SIZE + \
6277b8cabfSNoralf Trønnes 					 DATA_BLOCK_CONTENT_SIZE + \
6377b8cabfSNoralf Trønnes 					 DATA_BLOCK_FOOTER_SIZE)
6477b8cabfSNoralf Trønnes #define DATA_LAST_BLOCK_CONTENT_SIZE	4032
6577b8cabfSNoralf Trønnes #define DATA_LAST_BLOCK_SIZE		(DATA_BLOCK_HEADER_SIZE + \
6677b8cabfSNoralf Trønnes 					 DATA_LAST_BLOCK_CONTENT_SIZE + \
6777b8cabfSNoralf Trønnes 					 DATA_BLOCK_FOOTER_SIZE)
6877b8cabfSNoralf Trønnes 
6977b8cabfSNoralf Trønnes #define CMD_SIZE			31
7077b8cabfSNoralf Trønnes #define READ_STATUS_SIZE		13
7177b8cabfSNoralf Trønnes #define MISC_VALUE_SIZE			4
7277b8cabfSNoralf Trønnes 
737583028dSJinjie Ruan #define CMD_TIMEOUT			200
747583028dSJinjie Ruan #define DATA_TIMEOUT			1000
757583028dSJinjie Ruan #define IDLE_TIMEOUT			2000
767583028dSJinjie Ruan #define FIRST_FRAME_TIMEOUT		2000
7777b8cabfSNoralf Trønnes 
7877b8cabfSNoralf Trønnes #define MISC_REQ_GET_SET_ECO_A		0xff
7977b8cabfSNoralf Trønnes #define MISC_REQ_GET_SET_ECO_B		0x35
8077b8cabfSNoralf Trønnes /* Windows driver does once every second, with arg d = 1, other args 0 */
8177b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN1_A		0xff
8277b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN1_B		0x38
8377b8cabfSNoralf Trønnes /* Windows driver does this on init, with arg a, b = 0, c = 0xa0, d = 4 */
8477b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN2_A		0xa5
8577b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN2_B		0x00
8677b8cabfSNoralf Trønnes 
8777b8cabfSNoralf Trønnes struct gm12u320_device {
8877b8cabfSNoralf Trønnes 	struct drm_device	         dev;
89659ab7a4SThomas Zimmermann 	struct device                   *dmadev;
9077b8cabfSNoralf Trønnes 	struct drm_simple_display_pipe   pipe;
9177b8cabfSNoralf Trønnes 	struct drm_connector	         conn;
9277b8cabfSNoralf Trønnes 	unsigned char                   *cmd_buf;
9377b8cabfSNoralf Trønnes 	unsigned char                   *data_buf[GM12U320_BLOCK_COUNT];
9477b8cabfSNoralf Trønnes 	struct {
958f2cb937SDaniel Vetter 		struct delayed_work       work;
9677b8cabfSNoralf Trønnes 		struct mutex             lock;
9777b8cabfSNoralf Trønnes 		struct drm_framebuffer  *fb;
9877b8cabfSNoralf Trønnes 		struct drm_rect          rect;
998f2cb937SDaniel Vetter 		int frame;
1008f2cb937SDaniel Vetter 		int draw_status_timeout;
1017938f421SLucas De Marchi 		struct iosys_map src_map;
10277b8cabfSNoralf Trønnes 	} fb_update;
10377b8cabfSNoralf Trønnes };
10477b8cabfSNoralf Trønnes 
1057ced4801SDaniel Vetter #define to_gm12u320(__dev) container_of(__dev, struct gm12u320_device, dev)
1067ced4801SDaniel Vetter 
10777b8cabfSNoralf Trønnes static const char cmd_data[CMD_SIZE] = {
10877b8cabfSNoralf Trønnes 	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
10977b8cabfSNoralf Trønnes 	0x68, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff,
11077b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x80, 0x00,
11177b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
11277b8cabfSNoralf Trønnes };
11377b8cabfSNoralf Trønnes 
11477b8cabfSNoralf Trønnes static const char cmd_draw[CMD_SIZE] = {
11577b8cabfSNoralf Trønnes 	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
11677b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe,
11777b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0xc0, 0xd1, 0x05, 0x00, 0x40,
11877b8cabfSNoralf Trønnes 	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
11977b8cabfSNoralf Trønnes };
12077b8cabfSNoralf Trønnes 
12177b8cabfSNoralf Trønnes static const char cmd_misc[CMD_SIZE] = {
12277b8cabfSNoralf Trønnes 	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
12377b8cabfSNoralf Trønnes 	0x04, 0x00, 0x00, 0x00, 0x80, 0x01, 0x10, 0xfd,
12477b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00,
12577b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
12677b8cabfSNoralf Trønnes };
12777b8cabfSNoralf Trønnes 
12877b8cabfSNoralf Trønnes static const char data_block_header[DATA_BLOCK_HEADER_SIZE] = {
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 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13377b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13477b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13577b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13677b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13777b8cabfSNoralf Trønnes 	0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13877b8cabfSNoralf Trønnes 	0x00, 0x04, 0x15, 0x00, 0x00, 0xfc, 0x00, 0x00,
13977b8cabfSNoralf Trønnes 	0x01, 0x00, 0x00, 0xdb
14077b8cabfSNoralf Trønnes };
14177b8cabfSNoralf Trønnes 
14277b8cabfSNoralf Trønnes static const char data_last_block_header[DATA_BLOCK_HEADER_SIZE] = {
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 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14777b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14877b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14977b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15077b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15177b8cabfSNoralf Trønnes 	0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15277b8cabfSNoralf Trønnes 	0x2a, 0x00, 0x20, 0x00, 0xc0, 0x0f, 0x00, 0x00,
15377b8cabfSNoralf Trønnes 	0x01, 0x00, 0x00, 0xd7
15477b8cabfSNoralf Trønnes };
15577b8cabfSNoralf Trønnes 
15677b8cabfSNoralf Trønnes static const char data_block_footer[DATA_BLOCK_FOOTER_SIZE] = {
15777b8cabfSNoralf Trønnes 	0xfb, 0x14, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
15877b8cabfSNoralf Trønnes 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15977b8cabfSNoralf Trønnes 	0x80, 0x00, 0x00, 0x4f
16077b8cabfSNoralf Trønnes };
16177b8cabfSNoralf Trønnes 
gm12u320_to_usb_device(struct gm12u320_device * gm12u320)1620454bc59SThomas Zimmermann static inline struct usb_device *gm12u320_to_usb_device(struct gm12u320_device *gm12u320)
1630454bc59SThomas Zimmermann {
1640454bc59SThomas Zimmermann 	return interface_to_usbdev(to_usb_interface(gm12u320->dev.dev));
1650454bc59SThomas Zimmermann }
1660454bc59SThomas Zimmermann 
gm12u320_usb_alloc(struct gm12u320_device * gm12u320)16777b8cabfSNoralf Trønnes static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320)
16877b8cabfSNoralf Trønnes {
16977b8cabfSNoralf Trønnes 	int i, block_size;
17077b8cabfSNoralf Trønnes 	const char *hdr;
17177b8cabfSNoralf Trønnes 
17208373edcSDaniel Vetter 	gm12u320->cmd_buf = drmm_kmalloc(&gm12u320->dev, CMD_SIZE, GFP_KERNEL);
17377b8cabfSNoralf Trønnes 	if (!gm12u320->cmd_buf)
17477b8cabfSNoralf Trønnes 		return -ENOMEM;
17577b8cabfSNoralf Trønnes 
17677b8cabfSNoralf Trønnes 	for (i = 0; i < GM12U320_BLOCK_COUNT; i++) {
17777b8cabfSNoralf Trønnes 		if (i == GM12U320_BLOCK_COUNT - 1) {
17877b8cabfSNoralf Trønnes 			block_size = DATA_LAST_BLOCK_SIZE;
17977b8cabfSNoralf Trønnes 			hdr = data_last_block_header;
18077b8cabfSNoralf Trønnes 		} else {
18177b8cabfSNoralf Trønnes 			block_size = DATA_BLOCK_SIZE;
18277b8cabfSNoralf Trønnes 			hdr = data_block_header;
18377b8cabfSNoralf Trønnes 		}
18477b8cabfSNoralf Trønnes 
18508373edcSDaniel Vetter 		gm12u320->data_buf[i] = drmm_kzalloc(&gm12u320->dev,
18608373edcSDaniel Vetter 						     block_size, GFP_KERNEL);
18777b8cabfSNoralf Trønnes 		if (!gm12u320->data_buf[i])
18877b8cabfSNoralf Trønnes 			return -ENOMEM;
18977b8cabfSNoralf Trønnes 
19077b8cabfSNoralf Trønnes 		memcpy(gm12u320->data_buf[i], hdr, DATA_BLOCK_HEADER_SIZE);
19177b8cabfSNoralf Trønnes 		memcpy(gm12u320->data_buf[i] +
19277b8cabfSNoralf Trønnes 				(block_size - DATA_BLOCK_FOOTER_SIZE),
19377b8cabfSNoralf Trønnes 		       data_block_footer, DATA_BLOCK_FOOTER_SIZE);
19477b8cabfSNoralf Trønnes 	}
19577b8cabfSNoralf Trønnes 
19677b8cabfSNoralf Trønnes 	return 0;
19777b8cabfSNoralf Trønnes }
19877b8cabfSNoralf Trønnes 
gm12u320_misc_request(struct gm12u320_device * gm12u320,u8 req_a,u8 req_b,u8 arg_a,u8 arg_b,u8 arg_c,u8 arg_d)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 {
2030454bc59SThomas Zimmermann 	struct usb_device *udev = gm12u320_to_usb_device(gm12u320);
20477b8cabfSNoralf Trønnes 	int ret, len;
20577b8cabfSNoralf Trønnes 
20677b8cabfSNoralf Trønnes 	memcpy(gm12u320->cmd_buf, &cmd_misc, CMD_SIZE);
20777b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[20] = req_a;
20877b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[21] = req_b;
20977b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[22] = arg_a;
21077b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[23] = arg_b;
21177b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[24] = arg_c;
21277b8cabfSNoralf Trønnes 	gm12u320->cmd_buf[25] = arg_d;
21377b8cabfSNoralf Trønnes 
21477b8cabfSNoralf Trønnes 	/* Send request */
2150454bc59SThomas Zimmermann 	ret = usb_bulk_msg(udev, usb_sndbulkpipe(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 */
2230454bc59SThomas Zimmermann 	ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, MISC_RCV_EPT),
22477b8cabfSNoralf Trønnes 			   gm12u320->cmd_buf, MISC_VALUE_SIZE, &len,
22577b8cabfSNoralf Trønnes 			   DATA_TIMEOUT);
22677b8cabfSNoralf Trønnes 	if (ret || len != MISC_VALUE_SIZE) {
2274abfa2e4SHans de Goede 		GM12U320_ERR("Misc. value error %d\n", ret);
22877b8cabfSNoralf Trønnes 		return -EIO;
22977b8cabfSNoralf Trønnes 	}
23077b8cabfSNoralf Trønnes 	/* cmd_buf[0] now contains the read value, which we don't use */
23177b8cabfSNoralf Trønnes 
23277b8cabfSNoralf Trønnes 	/* Read status */
2330454bc59SThomas Zimmermann 	ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, MISC_RCV_EPT),
23477b8cabfSNoralf Trønnes 			   gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
23577b8cabfSNoralf Trønnes 			   CMD_TIMEOUT);
23677b8cabfSNoralf Trønnes 	if (ret || len != READ_STATUS_SIZE) {
2374abfa2e4SHans de Goede 		GM12U320_ERR("Misc. status error %d\n", ret);
23877b8cabfSNoralf Trønnes 		return -EIO;
23977b8cabfSNoralf Trønnes 	}
24077b8cabfSNoralf Trønnes 
24177b8cabfSNoralf Trønnes 	return 0;
24277b8cabfSNoralf Trønnes }
24377b8cabfSNoralf Trønnes 
gm12u320_32bpp_to_24bpp_packed(u8 * dst,u8 * src,int len)24477b8cabfSNoralf Trønnes static void gm12u320_32bpp_to_24bpp_packed(u8 *dst, u8 *src, int len)
24577b8cabfSNoralf Trønnes {
24677b8cabfSNoralf Trønnes 	while (len--) {
24777b8cabfSNoralf Trønnes 		*dst++ = *src++;
24877b8cabfSNoralf Trønnes 		*dst++ = *src++;
24977b8cabfSNoralf Trønnes 		*dst++ = *src++;
25077b8cabfSNoralf Trønnes 		src++;
25177b8cabfSNoralf Trønnes 	}
25277b8cabfSNoralf Trønnes }
25377b8cabfSNoralf Trønnes 
gm12u320_copy_fb_to_blocks(struct gm12u320_device * gm12u320)25477b8cabfSNoralf Trønnes static void gm12u320_copy_fb_to_blocks(struct gm12u320_device *gm12u320)
25577b8cabfSNoralf Trønnes {
25677b8cabfSNoralf Trønnes 	int block, dst_offset, len, remain, ret, x1, x2, y1, y2;
25777b8cabfSNoralf Trønnes 	struct drm_framebuffer *fb;
25877b8cabfSNoralf Trønnes 	void *vaddr;
25977b8cabfSNoralf Trønnes 	u8 *src;
26077b8cabfSNoralf Trønnes 
26177b8cabfSNoralf Trønnes 	mutex_lock(&gm12u320->fb_update.lock);
26277b8cabfSNoralf Trønnes 
26377b8cabfSNoralf Trønnes 	if (!gm12u320->fb_update.fb)
26477b8cabfSNoralf Trønnes 		goto unlock;
26577b8cabfSNoralf Trønnes 
26677b8cabfSNoralf Trønnes 	fb = gm12u320->fb_update.fb;
26777b8cabfSNoralf Trønnes 	x1 = gm12u320->fb_update.rect.x1;
26877b8cabfSNoralf Trønnes 	x2 = gm12u320->fb_update.rect.x2;
26977b8cabfSNoralf Trønnes 	y1 = gm12u320->fb_update.rect.y1;
27077b8cabfSNoralf Trønnes 	y2 = gm12u320->fb_update.rect.y2;
2714ac0868dSThomas Zimmermann 	vaddr = gm12u320->fb_update.src_map.vaddr; /* TODO: Use mapping abstraction properly */
27277b8cabfSNoralf Trønnes 
273329e2c42SThomas Zimmermann 	ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
27477b8cabfSNoralf Trønnes 	if (ret) {
275329e2c42SThomas Zimmermann 		GM12U320_ERR("drm_gem_fb_begin_cpu_access err: %d\n", ret);
2764ac0868dSThomas Zimmermann 		goto put_fb;
27777b8cabfSNoralf Trønnes 	}
27877b8cabfSNoralf Trønnes 
27977b8cabfSNoralf Trønnes 	src = vaddr + y1 * fb->pitches[0] + x1 * 4;
28077b8cabfSNoralf Trønnes 
28177b8cabfSNoralf Trønnes 	x1 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
28277b8cabfSNoralf Trønnes 	x2 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
28377b8cabfSNoralf Trønnes 
28477b8cabfSNoralf Trønnes 	for (; y1 < y2; y1++) {
28577b8cabfSNoralf Trønnes 		remain = 0;
28677b8cabfSNoralf Trønnes 		len = (x2 - x1) * 3;
28777b8cabfSNoralf Trønnes 		dst_offset = (y1 * GM12U320_REAL_WIDTH + x1) * 3;
28877b8cabfSNoralf Trønnes 		block = dst_offset / DATA_BLOCK_CONTENT_SIZE;
28977b8cabfSNoralf Trønnes 		dst_offset %= DATA_BLOCK_CONTENT_SIZE;
29077b8cabfSNoralf Trønnes 
29177b8cabfSNoralf Trønnes 		if ((dst_offset + len) > DATA_BLOCK_CONTENT_SIZE) {
29277b8cabfSNoralf Trønnes 			remain = dst_offset + len - DATA_BLOCK_CONTENT_SIZE;
29377b8cabfSNoralf Trønnes 			len = DATA_BLOCK_CONTENT_SIZE - dst_offset;
29477b8cabfSNoralf Trønnes 		}
29577b8cabfSNoralf Trønnes 
29677b8cabfSNoralf Trønnes 		dst_offset += DATA_BLOCK_HEADER_SIZE;
29777b8cabfSNoralf Trønnes 		len /= 3;
29877b8cabfSNoralf Trønnes 
29977b8cabfSNoralf Trønnes 		gm12u320_32bpp_to_24bpp_packed(
30077b8cabfSNoralf Trønnes 			gm12u320->data_buf[block] + dst_offset,
30177b8cabfSNoralf Trønnes 			src, len);
30277b8cabfSNoralf Trønnes 
30377b8cabfSNoralf Trønnes 		if (remain) {
30477b8cabfSNoralf Trønnes 			block++;
30577b8cabfSNoralf Trønnes 			dst_offset = DATA_BLOCK_HEADER_SIZE;
30677b8cabfSNoralf Trønnes 			gm12u320_32bpp_to_24bpp_packed(
30777b8cabfSNoralf Trønnes 				gm12u320->data_buf[block] + dst_offset,
30877b8cabfSNoralf Trønnes 				src + len * 4, remain / 3);
30977b8cabfSNoralf Trønnes 		}
31077b8cabfSNoralf Trønnes 		src += fb->pitches[0];
31177b8cabfSNoralf Trønnes 	}
31277b8cabfSNoralf Trønnes 
313329e2c42SThomas Zimmermann 	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
31477b8cabfSNoralf Trønnes put_fb:
31577b8cabfSNoralf Trønnes 	drm_framebuffer_put(fb);
31677b8cabfSNoralf Trønnes 	gm12u320->fb_update.fb = NULL;
31777b8cabfSNoralf Trønnes unlock:
31877b8cabfSNoralf Trønnes 	mutex_unlock(&gm12u320->fb_update.lock);
31977b8cabfSNoralf Trønnes }
32077b8cabfSNoralf Trønnes 
gm12u320_fb_update_work(struct work_struct * work)32177b8cabfSNoralf Trønnes static void gm12u320_fb_update_work(struct work_struct *work)
32277b8cabfSNoralf Trønnes {
32377b8cabfSNoralf Trønnes 	struct gm12u320_device *gm12u320 =
3248f2cb937SDaniel Vetter 		container_of(to_delayed_work(work), struct gm12u320_device,
3258f2cb937SDaniel Vetter 			     fb_update.work);
3260454bc59SThomas Zimmermann 	struct usb_device *udev = gm12u320_to_usb_device(gm12u320);
32777b8cabfSNoralf Trønnes 	int block, block_size, len;
32877b8cabfSNoralf Trønnes 	int ret = 0;
32977b8cabfSNoralf Trønnes 
33077b8cabfSNoralf Trønnes 	gm12u320_copy_fb_to_blocks(gm12u320);
33177b8cabfSNoralf Trønnes 
33277b8cabfSNoralf Trønnes 	for (block = 0; block < GM12U320_BLOCK_COUNT; block++) {
33377b8cabfSNoralf Trønnes 		if (block == GM12U320_BLOCK_COUNT - 1)
33477b8cabfSNoralf Trønnes 			block_size = DATA_LAST_BLOCK_SIZE;
33577b8cabfSNoralf Trønnes 		else
33677b8cabfSNoralf Trønnes 			block_size = DATA_BLOCK_SIZE;
33777b8cabfSNoralf Trønnes 
33877b8cabfSNoralf Trønnes 		/* Send data command to device */
33977b8cabfSNoralf Trønnes 		memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE);
34077b8cabfSNoralf Trønnes 		gm12u320->cmd_buf[8] = block_size & 0xff;
34177b8cabfSNoralf Trønnes 		gm12u320->cmd_buf[9] = block_size >> 8;
34277b8cabfSNoralf Trønnes 		gm12u320->cmd_buf[20] = 0xfc - block * 4;
3438f2cb937SDaniel Vetter 		gm12u320->cmd_buf[21] =
3448f2cb937SDaniel Vetter 			block | (gm12u320->fb_update.frame << 7);
34577b8cabfSNoralf Trønnes 
3460454bc59SThomas Zimmermann 		ret = usb_bulk_msg(udev,
3470454bc59SThomas Zimmermann 				   usb_sndbulkpipe(udev, DATA_SND_EPT),
34877b8cabfSNoralf Trønnes 				   gm12u320->cmd_buf, CMD_SIZE, &len,
34977b8cabfSNoralf Trønnes 				   CMD_TIMEOUT);
35077b8cabfSNoralf Trønnes 		if (ret || len != CMD_SIZE)
35177b8cabfSNoralf Trønnes 			goto err;
35277b8cabfSNoralf Trønnes 
35377b8cabfSNoralf Trønnes 		/* Send data block to device */
3540454bc59SThomas Zimmermann 		ret = usb_bulk_msg(udev,
3550454bc59SThomas Zimmermann 				   usb_sndbulkpipe(udev, DATA_SND_EPT),
35677b8cabfSNoralf Trønnes 				   gm12u320->data_buf[block], block_size,
35777b8cabfSNoralf Trønnes 				   &len, DATA_TIMEOUT);
35877b8cabfSNoralf Trønnes 		if (ret || len != block_size)
35977b8cabfSNoralf Trønnes 			goto err;
36077b8cabfSNoralf Trønnes 
36177b8cabfSNoralf Trønnes 		/* Read status */
3620454bc59SThomas Zimmermann 		ret = usb_bulk_msg(udev,
3630454bc59SThomas Zimmermann 				   usb_rcvbulkpipe(udev, DATA_RCV_EPT),
36477b8cabfSNoralf Trønnes 				   gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
36577b8cabfSNoralf Trønnes 				   CMD_TIMEOUT);
36677b8cabfSNoralf Trønnes 		if (ret || len != READ_STATUS_SIZE)
36777b8cabfSNoralf Trønnes 			goto err;
36877b8cabfSNoralf Trønnes 	}
36977b8cabfSNoralf Trønnes 
37077b8cabfSNoralf Trønnes 	/* Send draw command to device */
37177b8cabfSNoralf Trønnes 	memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE);
3720454bc59SThomas Zimmermann 	ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, DATA_SND_EPT),
37377b8cabfSNoralf Trønnes 			   gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
37477b8cabfSNoralf Trønnes 	if (ret || len != CMD_SIZE)
37577b8cabfSNoralf Trønnes 		goto err;
37677b8cabfSNoralf Trønnes 
37777b8cabfSNoralf Trønnes 	/* Read status */
3780454bc59SThomas Zimmermann 	ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, DATA_RCV_EPT),
37977b8cabfSNoralf Trønnes 			   gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
3808f2cb937SDaniel Vetter 			   gm12u320->fb_update.draw_status_timeout);
38177b8cabfSNoralf Trønnes 	if (ret || len != READ_STATUS_SIZE)
38277b8cabfSNoralf Trønnes 		goto err;
38377b8cabfSNoralf Trønnes 
3848f2cb937SDaniel Vetter 	gm12u320->fb_update.draw_status_timeout = CMD_TIMEOUT;
3858f2cb937SDaniel Vetter 	gm12u320->fb_update.frame = !gm12u320->fb_update.frame;
38677b8cabfSNoralf Trønnes 
38777b8cabfSNoralf Trønnes 	/*
38877b8cabfSNoralf Trønnes 	 * We must draw a frame every 2s otherwise the projector
38977b8cabfSNoralf Trønnes 	 * switches back to showing its logo.
39077b8cabfSNoralf Trønnes 	 */
3918f2cb937SDaniel Vetter 	queue_delayed_work(system_long_wq, &gm12u320->fb_update.work,
3927583028dSJinjie Ruan 			   msecs_to_jiffies(IDLE_TIMEOUT));
3938f2cb937SDaniel Vetter 
39477b8cabfSNoralf Trønnes 	return;
39577b8cabfSNoralf Trønnes err:
39677b8cabfSNoralf Trønnes 	/* Do not log errors caused by module unload or device unplug */
397ac9fd659SHans de Goede 	if (ret != -ENODEV && ret != -ECONNRESET && ret != -ESHUTDOWN)
3984abfa2e4SHans de Goede 		GM12U320_ERR("Frame update error: %d\n", ret);
39977b8cabfSNoralf Trønnes }
40077b8cabfSNoralf Trønnes 
gm12u320_fb_mark_dirty(struct drm_framebuffer * fb,const struct iosys_map * map,struct drm_rect * dirty)4017938f421SLucas De Marchi static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb,
4027938f421SLucas De Marchi 				   const struct iosys_map *map,
40377b8cabfSNoralf Trønnes 				   struct drm_rect *dirty)
40477b8cabfSNoralf Trønnes {
4057ced4801SDaniel Vetter 	struct gm12u320_device *gm12u320 = to_gm12u320(fb->dev);
40677b8cabfSNoralf Trønnes 	struct drm_framebuffer *old_fb = NULL;
40777b8cabfSNoralf Trønnes 	bool wakeup = false;
40877b8cabfSNoralf Trønnes 
40977b8cabfSNoralf Trønnes 	mutex_lock(&gm12u320->fb_update.lock);
41077b8cabfSNoralf Trønnes 
41177b8cabfSNoralf Trønnes 	if (gm12u320->fb_update.fb != fb) {
41277b8cabfSNoralf Trønnes 		old_fb = gm12u320->fb_update.fb;
41377b8cabfSNoralf Trønnes 		drm_framebuffer_get(fb);
41477b8cabfSNoralf Trønnes 		gm12u320->fb_update.fb = fb;
41577b8cabfSNoralf Trønnes 		gm12u320->fb_update.rect = *dirty;
4164ac0868dSThomas Zimmermann 		gm12u320->fb_update.src_map = *map;
41777b8cabfSNoralf Trønnes 		wakeup = true;
41877b8cabfSNoralf Trønnes 	} else {
41977b8cabfSNoralf Trønnes 		struct drm_rect *rect = &gm12u320->fb_update.rect;
42077b8cabfSNoralf Trønnes 
42177b8cabfSNoralf Trønnes 		rect->x1 = min(rect->x1, dirty->x1);
42277b8cabfSNoralf Trønnes 		rect->y1 = min(rect->y1, dirty->y1);
42377b8cabfSNoralf Trønnes 		rect->x2 = max(rect->x2, dirty->x2);
42477b8cabfSNoralf Trønnes 		rect->y2 = max(rect->y2, dirty->y2);
42577b8cabfSNoralf Trønnes 	}
42677b8cabfSNoralf Trønnes 
42777b8cabfSNoralf Trønnes 	mutex_unlock(&gm12u320->fb_update.lock);
42877b8cabfSNoralf Trønnes 
42977b8cabfSNoralf Trønnes 	if (wakeup)
4308f2cb937SDaniel Vetter 		mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0);
43177b8cabfSNoralf Trønnes 
43277b8cabfSNoralf Trønnes 	if (old_fb)
43377b8cabfSNoralf Trønnes 		drm_framebuffer_put(old_fb);
43477b8cabfSNoralf Trønnes }
43577b8cabfSNoralf Trønnes 
gm12u320_stop_fb_update(struct gm12u320_device * gm12u320)43677b8cabfSNoralf Trønnes static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320)
43777b8cabfSNoralf Trønnes {
4388f2cb937SDaniel Vetter 	struct drm_framebuffer *old_fb;
43977b8cabfSNoralf Trønnes 
4408f2cb937SDaniel Vetter 	cancel_delayed_work_sync(&gm12u320->fb_update.work);
44177b8cabfSNoralf Trønnes 
44277b8cabfSNoralf Trønnes 	mutex_lock(&gm12u320->fb_update.lock);
4438f2cb937SDaniel Vetter 	old_fb = gm12u320->fb_update.fb;
44477b8cabfSNoralf Trønnes 	gm12u320->fb_update.fb = NULL;
4457938f421SLucas De Marchi 	iosys_map_clear(&gm12u320->fb_update.src_map);
44677b8cabfSNoralf Trønnes 	mutex_unlock(&gm12u320->fb_update.lock);
4478f2cb937SDaniel Vetter 
4488f2cb937SDaniel Vetter 	drm_framebuffer_put(old_fb);
44977b8cabfSNoralf Trønnes }
45077b8cabfSNoralf Trønnes 
gm12u320_set_ecomode(struct gm12u320_device * gm12u320)45177b8cabfSNoralf Trønnes static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320)
45277b8cabfSNoralf Trønnes {
45377b8cabfSNoralf Trønnes 	return gm12u320_misc_request(gm12u320, MISC_REQ_GET_SET_ECO_A,
45477b8cabfSNoralf Trønnes 				     MISC_REQ_GET_SET_ECO_B, 0x01 /* set */,
45577b8cabfSNoralf Trønnes 				     eco_mode ? 0x01 : 0x00, 0x00, 0x01);
45677b8cabfSNoralf Trønnes }
45777b8cabfSNoralf Trønnes 
45877b8cabfSNoralf Trønnes /* ------------------------------------------------------------------ */
45977b8cabfSNoralf Trønnes /* gm12u320 connector						      */
46077b8cabfSNoralf Trønnes 
46177b8cabfSNoralf Trønnes /*
46277b8cabfSNoralf Trønnes  * We use fake EDID info so that userspace know that it is dealing with
46377b8cabfSNoralf Trønnes  * an Acer projector, rather then listing this as an "unknown" monitor.
46477b8cabfSNoralf Trønnes  * Note this assumes this driver is only ever used with the Acer C120, if we
46577b8cabfSNoralf Trønnes  * add support for other devices the vendor and model should be parameterized.
46677b8cabfSNoralf Trønnes  */
467*84addde4SJani Nikula static const struct edid gm12u320_edid = {
46877b8cabfSNoralf Trønnes 	.header		= { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 },
46977b8cabfSNoralf Trønnes 	.mfg_id		= { 0x04, 0x72 },	/* "ACR" */
47077b8cabfSNoralf Trønnes 	.prod_code	= { 0x20, 0xc1 },	/* C120h */
47177b8cabfSNoralf Trønnes 	.serial		= 0xaa55aa55,
47277b8cabfSNoralf Trønnes 	.mfg_week	= 1,
47377b8cabfSNoralf Trønnes 	.mfg_year	= 16,
47477b8cabfSNoralf Trønnes 	.version	= 1,			/* EDID 1.3 */
47577b8cabfSNoralf Trønnes 	.revision	= 3,			/* EDID 1.3 */
47677b8cabfSNoralf Trønnes 	.input		= 0x08,			/* Analog input */
47777b8cabfSNoralf Trønnes 	.features	= 0x0a,			/* Pref timing in DTD 1 */
47877b8cabfSNoralf Trønnes 	.standard_timings = { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 },
47977b8cabfSNoralf Trønnes 			      { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 } },
48077b8cabfSNoralf Trønnes 	.detailed_timings = { {
48177b8cabfSNoralf Trønnes 		.pixel_clock = 3383,
48277b8cabfSNoralf Trønnes 		/* hactive = 848, hblank = 256 */
48377b8cabfSNoralf Trønnes 		.data.pixel_data.hactive_lo = 0x50,
48477b8cabfSNoralf Trønnes 		.data.pixel_data.hblank_lo = 0x00,
48577b8cabfSNoralf Trønnes 		.data.pixel_data.hactive_hblank_hi = 0x31,
48677b8cabfSNoralf Trønnes 		/* vactive = 480, vblank = 28 */
48777b8cabfSNoralf Trønnes 		.data.pixel_data.vactive_lo = 0xe0,
48877b8cabfSNoralf Trønnes 		.data.pixel_data.vblank_lo = 0x1c,
48977b8cabfSNoralf Trønnes 		.data.pixel_data.vactive_vblank_hi = 0x10,
49077b8cabfSNoralf Trønnes 		/* hsync offset 40 pw 128, vsync offset 1 pw 4 */
49177b8cabfSNoralf Trønnes 		.data.pixel_data.hsync_offset_lo = 0x28,
49277b8cabfSNoralf Trønnes 		.data.pixel_data.hsync_pulse_width_lo = 0x80,
49377b8cabfSNoralf Trønnes 		.data.pixel_data.vsync_offset_pulse_width_lo = 0x14,
49477b8cabfSNoralf Trønnes 		.data.pixel_data.hsync_vsync_offset_pulse_width_hi = 0x00,
49577b8cabfSNoralf Trønnes 		/* Digital separate syncs, hsync+, vsync+ */
49677b8cabfSNoralf Trønnes 		.data.pixel_data.misc = 0x1e,
49777b8cabfSNoralf Trønnes 	}, {
49877b8cabfSNoralf Trønnes 		.pixel_clock = 0,
49977b8cabfSNoralf Trønnes 		.data.other_data.type = 0xfd, /* Monitor ranges */
50077b8cabfSNoralf Trønnes 		.data.other_data.data.range.min_vfreq = 59,
50177b8cabfSNoralf Trønnes 		.data.other_data.data.range.max_vfreq = 61,
50277b8cabfSNoralf Trønnes 		.data.other_data.data.range.min_hfreq_khz = 29,
50377b8cabfSNoralf Trønnes 		.data.other_data.data.range.max_hfreq_khz = 32,
50477b8cabfSNoralf Trønnes 		.data.other_data.data.range.pixel_clock_mhz = 4, /* 40 MHz */
50577b8cabfSNoralf Trønnes 		.data.other_data.data.range.flags = 0,
50677b8cabfSNoralf Trønnes 		.data.other_data.data.range.formula.cvt = {
50777b8cabfSNoralf Trønnes 			0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 },
50877b8cabfSNoralf Trønnes 	}, {
50977b8cabfSNoralf Trønnes 		.pixel_clock = 0,
51077b8cabfSNoralf Trønnes 		.data.other_data.type = 0xfc, /* Model string */
51177b8cabfSNoralf Trønnes 		.data.other_data.data.str.str = {
51277b8cabfSNoralf Trønnes 			'P', 'r', 'o', 'j', 'e', 'c', 't', 'o', 'r', '\n',
51377b8cabfSNoralf Trønnes 			' ', ' ',  ' ' },
51477b8cabfSNoralf Trønnes 	}, {
51577b8cabfSNoralf Trønnes 		.pixel_clock = 0,
51677b8cabfSNoralf Trønnes 		.data.other_data.type = 0xfe, /* Unspecified text / padding */
51777b8cabfSNoralf Trønnes 		.data.other_data.data.str.str = {
51877b8cabfSNoralf Trønnes 			'\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
51977b8cabfSNoralf Trønnes 			' ', ' ',  ' ' },
52077b8cabfSNoralf Trønnes 	} },
52177b8cabfSNoralf Trønnes 	.checksum = 0x13,
52277b8cabfSNoralf Trønnes };
52377b8cabfSNoralf Trønnes 
gm12u320_conn_get_modes(struct drm_connector * connector)52477b8cabfSNoralf Trønnes static int gm12u320_conn_get_modes(struct drm_connector *connector)
52577b8cabfSNoralf Trønnes {
526*84addde4SJani Nikula 	const struct drm_edid *drm_edid;
527*84addde4SJani Nikula 	int count;
528*84addde4SJani Nikula 
529*84addde4SJani Nikula 	drm_edid = drm_edid_alloc(&gm12u320_edid, sizeof(gm12u320_edid));
530*84addde4SJani Nikula 	drm_edid_connector_update(connector, drm_edid);
531*84addde4SJani Nikula 	count = drm_edid_connector_add_modes(connector);
532*84addde4SJani Nikula 	drm_edid_free(drm_edid);
533*84addde4SJani Nikula 
534*84addde4SJani Nikula 	return count;
53577b8cabfSNoralf Trønnes }
53677b8cabfSNoralf Trønnes 
53777b8cabfSNoralf Trønnes static const struct drm_connector_helper_funcs gm12u320_conn_helper_funcs = {
53877b8cabfSNoralf Trønnes 	.get_modes = gm12u320_conn_get_modes,
53977b8cabfSNoralf Trønnes };
54077b8cabfSNoralf Trønnes 
54177b8cabfSNoralf Trønnes static const struct drm_connector_funcs gm12u320_conn_funcs = {
54277b8cabfSNoralf Trønnes 	.fill_modes = drm_helper_probe_single_connector_modes,
54377b8cabfSNoralf Trønnes 	.destroy = drm_connector_cleanup,
54477b8cabfSNoralf Trønnes 	.reset = drm_atomic_helper_connector_reset,
54577b8cabfSNoralf Trønnes 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
54677b8cabfSNoralf Trønnes 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
54777b8cabfSNoralf Trønnes };
54877b8cabfSNoralf Trønnes 
gm12u320_conn_init(struct gm12u320_device * gm12u320)54977b8cabfSNoralf Trønnes static int gm12u320_conn_init(struct gm12u320_device *gm12u320)
55077b8cabfSNoralf Trønnes {
55177b8cabfSNoralf Trønnes 	drm_connector_helper_add(&gm12u320->conn, &gm12u320_conn_helper_funcs);
55277b8cabfSNoralf Trønnes 	return drm_connector_init(&gm12u320->dev, &gm12u320->conn,
55377b8cabfSNoralf Trønnes 				  &gm12u320_conn_funcs, DRM_MODE_CONNECTOR_VGA);
55477b8cabfSNoralf Trønnes }
55577b8cabfSNoralf Trønnes 
55677b8cabfSNoralf Trønnes /* ------------------------------------------------------------------ */
55777b8cabfSNoralf Trønnes /* gm12u320 (simple) display pipe				      */
55877b8cabfSNoralf Trønnes 
gm12u320_pipe_enable(struct drm_simple_display_pipe * pipe,struct drm_crtc_state * crtc_state,struct drm_plane_state * plane_state)55977b8cabfSNoralf Trønnes static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe,
56077b8cabfSNoralf Trønnes 				 struct drm_crtc_state *crtc_state,
56177b8cabfSNoralf Trønnes 				 struct drm_plane_state *plane_state)
56277b8cabfSNoralf Trønnes {
56377b8cabfSNoralf Trønnes 	struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT };
5647ced4801SDaniel Vetter 	struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
5654ac0868dSThomas Zimmermann 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
56677b8cabfSNoralf Trønnes 
5678f2cb937SDaniel Vetter 	gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT;
568229d9468SThomas Zimmermann 	gm12u320_fb_mark_dirty(plane_state->fb, &shadow_plane_state->data[0], &rect);
56977b8cabfSNoralf Trønnes }
57077b8cabfSNoralf Trønnes 
gm12u320_pipe_disable(struct drm_simple_display_pipe * pipe)57177b8cabfSNoralf Trønnes static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
57277b8cabfSNoralf Trønnes {
5737ced4801SDaniel Vetter 	struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
57477b8cabfSNoralf Trønnes 
57577b8cabfSNoralf Trønnes 	gm12u320_stop_fb_update(gm12u320);
57677b8cabfSNoralf Trønnes }
57777b8cabfSNoralf Trønnes 
gm12u320_pipe_update(struct drm_simple_display_pipe * pipe,struct drm_plane_state * old_state)57877b8cabfSNoralf Trønnes static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe,
57977b8cabfSNoralf Trønnes 				 struct drm_plane_state *old_state)
58077b8cabfSNoralf Trønnes {
58177b8cabfSNoralf Trønnes 	struct drm_plane_state *state = pipe->plane.state;
5824ac0868dSThomas Zimmermann 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
58377b8cabfSNoralf Trønnes 	struct drm_rect rect;
58477b8cabfSNoralf Trønnes 
58577b8cabfSNoralf Trønnes 	if (drm_atomic_helper_damage_merged(old_state, state, &rect))
586229d9468SThomas Zimmermann 		gm12u320_fb_mark_dirty(state->fb, &shadow_plane_state->data[0], &rect);
58777b8cabfSNoralf Trønnes }
58877b8cabfSNoralf Trønnes 
58977b8cabfSNoralf Trønnes static const struct drm_simple_display_pipe_funcs gm12u320_pipe_funcs = {
59077b8cabfSNoralf Trønnes 	.enable	    = gm12u320_pipe_enable,
59177b8cabfSNoralf Trønnes 	.disable    = gm12u320_pipe_disable,
59277b8cabfSNoralf Trønnes 	.update	    = gm12u320_pipe_update,
5934ac0868dSThomas Zimmermann 	DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
59477b8cabfSNoralf Trønnes };
59577b8cabfSNoralf Trønnes 
59677b8cabfSNoralf Trønnes static const uint32_t gm12u320_pipe_formats[] = {
59777b8cabfSNoralf Trønnes 	DRM_FORMAT_XRGB8888,
59877b8cabfSNoralf Trønnes };
59977b8cabfSNoralf Trønnes 
60077b8cabfSNoralf Trønnes static const uint64_t gm12u320_pipe_modifiers[] = {
60177b8cabfSNoralf Trønnes 	DRM_FORMAT_MOD_LINEAR,
60277b8cabfSNoralf Trønnes 	DRM_FORMAT_MOD_INVALID
60377b8cabfSNoralf Trønnes };
60477b8cabfSNoralf Trønnes 
605659ab7a4SThomas Zimmermann /*
606659ab7a4SThomas Zimmermann  * FIXME: Dma-buf sharing requires DMA support by the importing device.
607659ab7a4SThomas Zimmermann  *        This function is a workaround to make USB devices work as well.
608659ab7a4SThomas Zimmermann  *        See todo.rst for how to fix the issue in the dma-buf framework.
609659ab7a4SThomas Zimmermann  */
gm12u320_gem_prime_import(struct drm_device * dev,struct dma_buf * dma_buf)610659ab7a4SThomas Zimmermann static struct drm_gem_object *gm12u320_gem_prime_import(struct drm_device *dev,
611659ab7a4SThomas Zimmermann 							struct dma_buf *dma_buf)
612659ab7a4SThomas Zimmermann {
613659ab7a4SThomas Zimmermann 	struct gm12u320_device *gm12u320 = to_gm12u320(dev);
614659ab7a4SThomas Zimmermann 
615659ab7a4SThomas Zimmermann 	if (!gm12u320->dmadev)
616659ab7a4SThomas Zimmermann 		return ERR_PTR(-ENODEV);
617659ab7a4SThomas Zimmermann 
618659ab7a4SThomas Zimmermann 	return drm_gem_prime_import_dev(dev, dma_buf, gm12u320->dmadev);
619659ab7a4SThomas Zimmermann }
620659ab7a4SThomas Zimmermann 
621eee9a2e0SGerd Hoffmann DEFINE_DRM_GEM_FOPS(gm12u320_fops);
62277b8cabfSNoralf Trønnes 
62370a59dd8SDaniel Vetter static const struct drm_driver gm12u320_drm_driver = {
62477b8cabfSNoralf Trønnes 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
62577b8cabfSNoralf Trønnes 
62677b8cabfSNoralf Trønnes 	.name		 = DRIVER_NAME,
62777b8cabfSNoralf Trønnes 	.desc		 = DRIVER_DESC,
62877b8cabfSNoralf Trønnes 	.date		 = DRIVER_DATE,
62977b8cabfSNoralf Trønnes 	.major		 = DRIVER_MAJOR,
63077b8cabfSNoralf Trønnes 	.minor		 = DRIVER_MINOR,
63177b8cabfSNoralf Trønnes 
63277b8cabfSNoralf Trønnes 	.fops		 = &gm12u320_fops,
63377b8cabfSNoralf Trønnes 	DRM_GEM_SHMEM_DRIVER_OPS,
634659ab7a4SThomas Zimmermann 	.gem_prime_import = gm12u320_gem_prime_import,
63577b8cabfSNoralf Trønnes };
63677b8cabfSNoralf Trønnes 
63777b8cabfSNoralf Trønnes static const struct drm_mode_config_funcs gm12u320_mode_config_funcs = {
63877b8cabfSNoralf Trønnes 	.fb_create = drm_gem_fb_create_with_dirty,
63977b8cabfSNoralf Trønnes 	.atomic_check = drm_atomic_helper_check,
64077b8cabfSNoralf Trønnes 	.atomic_commit = drm_atomic_helper_commit,
64177b8cabfSNoralf Trønnes };
64277b8cabfSNoralf Trønnes 
gm12u320_usb_probe(struct usb_interface * interface,const struct usb_device_id * id)64377b8cabfSNoralf Trønnes static int gm12u320_usb_probe(struct usb_interface *interface,
64477b8cabfSNoralf Trønnes 			      const struct usb_device_id *id)
64577b8cabfSNoralf Trønnes {
64677b8cabfSNoralf Trønnes 	struct gm12u320_device *gm12u320;
64777b8cabfSNoralf Trønnes 	struct drm_device *dev;
64877b8cabfSNoralf Trønnes 	int ret;
64977b8cabfSNoralf Trønnes 
65077b8cabfSNoralf Trønnes 	/*
65177b8cabfSNoralf Trønnes 	 * The gm12u320 presents itself to the system as 2 usb mass-storage
65277b8cabfSNoralf Trønnes 	 * interfaces, we only care about / need the first one.
65377b8cabfSNoralf Trønnes 	 */
65477b8cabfSNoralf Trønnes 	if (interface->cur_altsetting->desc.bInterfaceNumber != 0)
65577b8cabfSNoralf Trønnes 		return -ENODEV;
65677b8cabfSNoralf Trønnes 
6579213142dSDaniel Vetter 	gm12u320 = devm_drm_dev_alloc(&interface->dev, &gm12u320_drm_driver,
6589213142dSDaniel Vetter 				      struct gm12u320_device, dev);
6599213142dSDaniel Vetter 	if (IS_ERR(gm12u320))
6609213142dSDaniel Vetter 		return PTR_ERR(gm12u320);
661659ab7a4SThomas Zimmermann 	dev = &gm12u320->dev;
662659ab7a4SThomas Zimmermann 
663659ab7a4SThomas Zimmermann 	gm12u320->dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
664659ab7a4SThomas Zimmermann 	if (!gm12u320->dmadev)
665659ab7a4SThomas Zimmermann 		drm_warn(dev, "buffer sharing not supported"); /* not an error */
66677b8cabfSNoralf Trønnes 
6678f2cb937SDaniel Vetter 	INIT_DELAYED_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work);
66877b8cabfSNoralf Trønnes 	mutex_init(&gm12u320->fb_update.lock);
66977b8cabfSNoralf Trønnes 
67008373edcSDaniel Vetter 	ret = drmm_mode_config_init(dev);
67108373edcSDaniel Vetter 	if (ret)
672659ab7a4SThomas Zimmermann 		goto err_put_device;
67308373edcSDaniel Vetter 
67477b8cabfSNoralf Trønnes 	dev->mode_config.min_width = GM12U320_USER_WIDTH;
67577b8cabfSNoralf Trønnes 	dev->mode_config.max_width = GM12U320_USER_WIDTH;
67677b8cabfSNoralf Trønnes 	dev->mode_config.min_height = GM12U320_HEIGHT;
67777b8cabfSNoralf Trønnes 	dev->mode_config.max_height = GM12U320_HEIGHT;
67877b8cabfSNoralf Trønnes 	dev->mode_config.funcs = &gm12u320_mode_config_funcs;
67977b8cabfSNoralf Trønnes 
68077b8cabfSNoralf Trønnes 	ret = gm12u320_usb_alloc(gm12u320);
68177b8cabfSNoralf Trønnes 	if (ret)
682659ab7a4SThomas Zimmermann 		goto err_put_device;
68377b8cabfSNoralf Trønnes 
68477b8cabfSNoralf Trønnes 	ret = gm12u320_set_ecomode(gm12u320);
68577b8cabfSNoralf Trønnes 	if (ret)
686659ab7a4SThomas Zimmermann 		goto err_put_device;
68777b8cabfSNoralf Trønnes 
68877b8cabfSNoralf Trønnes 	ret = gm12u320_conn_init(gm12u320);
68977b8cabfSNoralf Trønnes 	if (ret)
690659ab7a4SThomas Zimmermann 		goto err_put_device;
69177b8cabfSNoralf Trønnes 
69277b8cabfSNoralf Trønnes 	ret = drm_simple_display_pipe_init(&gm12u320->dev,
69377b8cabfSNoralf Trønnes 					   &gm12u320->pipe,
69477b8cabfSNoralf Trønnes 					   &gm12u320_pipe_funcs,
69577b8cabfSNoralf Trønnes 					   gm12u320_pipe_formats,
69677b8cabfSNoralf Trønnes 					   ARRAY_SIZE(gm12u320_pipe_formats),
69777b8cabfSNoralf Trønnes 					   gm12u320_pipe_modifiers,
69877b8cabfSNoralf Trønnes 					   &gm12u320->conn);
69977b8cabfSNoralf Trønnes 	if (ret)
700659ab7a4SThomas Zimmermann 		goto err_put_device;
70177b8cabfSNoralf Trønnes 
70277b8cabfSNoralf Trønnes 	drm_mode_config_reset(dev);
70377b8cabfSNoralf Trønnes 
70477b8cabfSNoralf Trønnes 	usb_set_intfdata(interface, dev);
70577b8cabfSNoralf Trønnes 	ret = drm_dev_register(dev, 0);
70677b8cabfSNoralf Trønnes 	if (ret)
707659ab7a4SThomas Zimmermann 		goto err_put_device;
70877b8cabfSNoralf Trønnes 
7095bd79b70SThomas Zimmermann 	drm_fbdev_shmem_setup(dev, 0);
71077b8cabfSNoralf Trønnes 
71177b8cabfSNoralf Trønnes 	return 0;
712659ab7a4SThomas Zimmermann 
713659ab7a4SThomas Zimmermann err_put_device:
714659ab7a4SThomas Zimmermann 	put_device(gm12u320->dmadev);
715659ab7a4SThomas Zimmermann 	return ret;
71677b8cabfSNoralf Trønnes }
71777b8cabfSNoralf Trønnes 
gm12u320_usb_disconnect(struct usb_interface * interface)71877b8cabfSNoralf Trønnes static void gm12u320_usb_disconnect(struct usb_interface *interface)
71977b8cabfSNoralf Trønnes {
72077b8cabfSNoralf Trønnes 	struct drm_device *dev = usb_get_intfdata(interface);
721659ab7a4SThomas Zimmermann 	struct gm12u320_device *gm12u320 = to_gm12u320(dev);
72277b8cabfSNoralf Trønnes 
723659ab7a4SThomas Zimmermann 	put_device(gm12u320->dmadev);
724659ab7a4SThomas Zimmermann 	gm12u320->dmadev = NULL;
72577b8cabfSNoralf Trønnes 	drm_dev_unplug(dev);
7267ef64ed1SDaniel Vetter 	drm_atomic_helper_shutdown(dev);
72777b8cabfSNoralf Trønnes }
72877b8cabfSNoralf Trønnes 
gm12u320_suspend(struct usb_interface * interface,pm_message_t message)72949eafb20SPaul Cercueil static int gm12u320_suspend(struct usb_interface *interface,
73077b8cabfSNoralf Trønnes 			    pm_message_t message)
73177b8cabfSNoralf Trønnes {
73277b8cabfSNoralf Trønnes 	struct drm_device *dev = usb_get_intfdata(interface);
73377b8cabfSNoralf Trønnes 
7347ef64ed1SDaniel Vetter 	return drm_mode_config_helper_suspend(dev);
73577b8cabfSNoralf Trønnes }
73677b8cabfSNoralf Trønnes 
gm12u320_resume(struct usb_interface * interface)73749eafb20SPaul Cercueil static int gm12u320_resume(struct usb_interface *interface)
73877b8cabfSNoralf Trønnes {
73977b8cabfSNoralf Trønnes 	struct drm_device *dev = usb_get_intfdata(interface);
7407ced4801SDaniel Vetter 	struct gm12u320_device *gm12u320 = to_gm12u320(dev);
74177b8cabfSNoralf Trønnes 
74277b8cabfSNoralf Trønnes 	gm12u320_set_ecomode(gm12u320);
74377b8cabfSNoralf Trønnes 
7447ef64ed1SDaniel Vetter 	return drm_mode_config_helper_resume(dev);
74577b8cabfSNoralf Trønnes }
74677b8cabfSNoralf Trønnes 
74777b8cabfSNoralf Trønnes static const struct usb_device_id id_table[] = {
74877b8cabfSNoralf Trønnes 	{ USB_DEVICE(0x1de1, 0xc102) },
74977b8cabfSNoralf Trønnes 	{},
75077b8cabfSNoralf Trønnes };
75177b8cabfSNoralf Trønnes MODULE_DEVICE_TABLE(usb, id_table);
75277b8cabfSNoralf Trønnes 
75377b8cabfSNoralf Trønnes static struct usb_driver gm12u320_usb_driver = {
75477b8cabfSNoralf Trønnes 	.name = "gm12u320",
75577b8cabfSNoralf Trønnes 	.probe = gm12u320_usb_probe,
75677b8cabfSNoralf Trønnes 	.disconnect = gm12u320_usb_disconnect,
75777b8cabfSNoralf Trønnes 	.id_table = id_table,
75849eafb20SPaul Cercueil 	.suspend = pm_ptr(gm12u320_suspend),
75949eafb20SPaul Cercueil 	.resume = pm_ptr(gm12u320_resume),
76049eafb20SPaul Cercueil 	.reset_resume = pm_ptr(gm12u320_resume),
76177b8cabfSNoralf Trønnes };
76277b8cabfSNoralf Trønnes 
76377b8cabfSNoralf Trønnes module_usb_driver(gm12u320_usb_driver);
76477b8cabfSNoralf Trønnes MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
7652c232f9bSJeff Johnson MODULE_DESCRIPTION("GM12U320 driver for USB projectors");
76677b8cabfSNoralf Trønnes MODULE_LICENSE("GPL");
767