1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 2019 Hans de Goede <hdegoede@redhat.com>
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/dma-buf.h>
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/usb.h>
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
11*4882a593Smuzhiyun #include <drm/drm_atomic_state_helper.h>
12*4882a593Smuzhiyun #include <drm/drm_connector.h>
13*4882a593Smuzhiyun #include <drm/drm_damage_helper.h>
14*4882a593Smuzhiyun #include <drm/drm_drv.h>
15*4882a593Smuzhiyun #include <drm/drm_fb_helper.h>
16*4882a593Smuzhiyun #include <drm/drm_file.h>
17*4882a593Smuzhiyun #include <drm/drm_format_helper.h>
18*4882a593Smuzhiyun #include <drm/drm_fourcc.h>
19*4882a593Smuzhiyun #include <drm/drm_gem_shmem_helper.h>
20*4882a593Smuzhiyun #include <drm/drm_gem_framebuffer_helper.h>
21*4882a593Smuzhiyun #include <drm/drm_ioctl.h>
22*4882a593Smuzhiyun #include <drm/drm_managed.h>
23*4882a593Smuzhiyun #include <drm/drm_modeset_helper_vtables.h>
24*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
25*4882a593Smuzhiyun #include <drm/drm_simple_kms_helper.h>
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static bool eco_mode;
28*4882a593Smuzhiyun module_param(eco_mode, bool, 0644);
29*4882a593Smuzhiyun MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)");
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun #define DRIVER_NAME "gm12u320"
32*4882a593Smuzhiyun #define DRIVER_DESC "Grain Media GM12U320 USB projector display"
33*4882a593Smuzhiyun #define DRIVER_DATE "2019"
34*4882a593Smuzhiyun #define DRIVER_MAJOR 1
35*4882a593Smuzhiyun #define DRIVER_MINOR 0
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /*
38*4882a593Smuzhiyun * The DLP has an actual width of 854 pixels, but that is not a multiple
39*4882a593Smuzhiyun * of 8, breaking things left and right, so we export a width of 848.
40*4882a593Smuzhiyun */
41*4882a593Smuzhiyun #define GM12U320_USER_WIDTH 848
42*4882a593Smuzhiyun #define GM12U320_REAL_WIDTH 854
43*4882a593Smuzhiyun #define GM12U320_HEIGHT 480
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun #define GM12U320_BLOCK_COUNT 20
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun #define GM12U320_ERR(fmt, ...) \
48*4882a593Smuzhiyun DRM_DEV_ERROR(&gm12u320->udev->dev, fmt, ##__VA_ARGS__)
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun #define MISC_RCV_EPT 1
51*4882a593Smuzhiyun #define DATA_RCV_EPT 2
52*4882a593Smuzhiyun #define DATA_SND_EPT 3
53*4882a593Smuzhiyun #define MISC_SND_EPT 4
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun #define DATA_BLOCK_HEADER_SIZE 84
56*4882a593Smuzhiyun #define DATA_BLOCK_CONTENT_SIZE 64512
57*4882a593Smuzhiyun #define DATA_BLOCK_FOOTER_SIZE 20
58*4882a593Smuzhiyun #define DATA_BLOCK_SIZE (DATA_BLOCK_HEADER_SIZE + \
59*4882a593Smuzhiyun DATA_BLOCK_CONTENT_SIZE + \
60*4882a593Smuzhiyun DATA_BLOCK_FOOTER_SIZE)
61*4882a593Smuzhiyun #define DATA_LAST_BLOCK_CONTENT_SIZE 4032
62*4882a593Smuzhiyun #define DATA_LAST_BLOCK_SIZE (DATA_BLOCK_HEADER_SIZE + \
63*4882a593Smuzhiyun DATA_LAST_BLOCK_CONTENT_SIZE + \
64*4882a593Smuzhiyun DATA_BLOCK_FOOTER_SIZE)
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun #define CMD_SIZE 31
67*4882a593Smuzhiyun #define READ_STATUS_SIZE 13
68*4882a593Smuzhiyun #define MISC_VALUE_SIZE 4
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun #define CMD_TIMEOUT msecs_to_jiffies(200)
71*4882a593Smuzhiyun #define DATA_TIMEOUT msecs_to_jiffies(1000)
72*4882a593Smuzhiyun #define IDLE_TIMEOUT msecs_to_jiffies(2000)
73*4882a593Smuzhiyun #define FIRST_FRAME_TIMEOUT msecs_to_jiffies(2000)
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun #define MISC_REQ_GET_SET_ECO_A 0xff
76*4882a593Smuzhiyun #define MISC_REQ_GET_SET_ECO_B 0x35
77*4882a593Smuzhiyun /* Windows driver does once every second, with arg d = 1, other args 0 */
78*4882a593Smuzhiyun #define MISC_REQ_UNKNOWN1_A 0xff
79*4882a593Smuzhiyun #define MISC_REQ_UNKNOWN1_B 0x38
80*4882a593Smuzhiyun /* Windows driver does this on init, with arg a, b = 0, c = 0xa0, d = 4 */
81*4882a593Smuzhiyun #define MISC_REQ_UNKNOWN2_A 0xa5
82*4882a593Smuzhiyun #define MISC_REQ_UNKNOWN2_B 0x00
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun struct gm12u320_device {
85*4882a593Smuzhiyun struct drm_device dev;
86*4882a593Smuzhiyun struct device *dmadev;
87*4882a593Smuzhiyun struct drm_simple_display_pipe pipe;
88*4882a593Smuzhiyun struct drm_connector conn;
89*4882a593Smuzhiyun struct usb_device *udev;
90*4882a593Smuzhiyun unsigned char *cmd_buf;
91*4882a593Smuzhiyun unsigned char *data_buf[GM12U320_BLOCK_COUNT];
92*4882a593Smuzhiyun struct {
93*4882a593Smuzhiyun struct delayed_work work;
94*4882a593Smuzhiyun struct mutex lock;
95*4882a593Smuzhiyun struct drm_framebuffer *fb;
96*4882a593Smuzhiyun struct drm_rect rect;
97*4882a593Smuzhiyun int frame;
98*4882a593Smuzhiyun int draw_status_timeout;
99*4882a593Smuzhiyun } fb_update;
100*4882a593Smuzhiyun };
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun #define to_gm12u320(__dev) container_of(__dev, struct gm12u320_device, dev)
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun static const char cmd_data[CMD_SIZE] = {
105*4882a593Smuzhiyun 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
106*4882a593Smuzhiyun 0x68, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff,
107*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x80, 0x00,
108*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
109*4882a593Smuzhiyun };
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun static const char cmd_draw[CMD_SIZE] = {
112*4882a593Smuzhiyun 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
113*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe,
114*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0xc0, 0xd1, 0x05, 0x00, 0x40,
115*4882a593Smuzhiyun 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
116*4882a593Smuzhiyun };
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun static const char cmd_misc[CMD_SIZE] = {
119*4882a593Smuzhiyun 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
120*4882a593Smuzhiyun 0x04, 0x00, 0x00, 0x00, 0x80, 0x01, 0x10, 0xfd,
121*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00,
122*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
123*4882a593Smuzhiyun };
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun static const char data_block_header[DATA_BLOCK_HEADER_SIZE] = {
126*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134*4882a593Smuzhiyun 0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135*4882a593Smuzhiyun 0x00, 0x04, 0x15, 0x00, 0x00, 0xfc, 0x00, 0x00,
136*4882a593Smuzhiyun 0x01, 0x00, 0x00, 0xdb
137*4882a593Smuzhiyun };
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun static const char data_last_block_header[DATA_BLOCK_HEADER_SIZE] = {
140*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148*4882a593Smuzhiyun 0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149*4882a593Smuzhiyun 0x2a, 0x00, 0x20, 0x00, 0xc0, 0x0f, 0x00, 0x00,
150*4882a593Smuzhiyun 0x01, 0x00, 0x00, 0xd7
151*4882a593Smuzhiyun };
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun static const char data_block_footer[DATA_BLOCK_FOOTER_SIZE] = {
154*4882a593Smuzhiyun 0xfb, 0x14, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
155*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156*4882a593Smuzhiyun 0x80, 0x00, 0x00, 0x4f
157*4882a593Smuzhiyun };
158*4882a593Smuzhiyun
gm12u320_usb_alloc(struct gm12u320_device * gm12u320)159*4882a593Smuzhiyun static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun int i, block_size;
162*4882a593Smuzhiyun const char *hdr;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun gm12u320->cmd_buf = drmm_kmalloc(&gm12u320->dev, CMD_SIZE, GFP_KERNEL);
165*4882a593Smuzhiyun if (!gm12u320->cmd_buf)
166*4882a593Smuzhiyun return -ENOMEM;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun for (i = 0; i < GM12U320_BLOCK_COUNT; i++) {
169*4882a593Smuzhiyun if (i == GM12U320_BLOCK_COUNT - 1) {
170*4882a593Smuzhiyun block_size = DATA_LAST_BLOCK_SIZE;
171*4882a593Smuzhiyun hdr = data_last_block_header;
172*4882a593Smuzhiyun } else {
173*4882a593Smuzhiyun block_size = DATA_BLOCK_SIZE;
174*4882a593Smuzhiyun hdr = data_block_header;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun gm12u320->data_buf[i] = drmm_kzalloc(&gm12u320->dev,
178*4882a593Smuzhiyun block_size, GFP_KERNEL);
179*4882a593Smuzhiyun if (!gm12u320->data_buf[i])
180*4882a593Smuzhiyun return -ENOMEM;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun memcpy(gm12u320->data_buf[i], hdr, DATA_BLOCK_HEADER_SIZE);
183*4882a593Smuzhiyun memcpy(gm12u320->data_buf[i] +
184*4882a593Smuzhiyun (block_size - DATA_BLOCK_FOOTER_SIZE),
185*4882a593Smuzhiyun data_block_footer, DATA_BLOCK_FOOTER_SIZE);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun return 0;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
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)191*4882a593Smuzhiyun static int gm12u320_misc_request(struct gm12u320_device *gm12u320,
192*4882a593Smuzhiyun u8 req_a, u8 req_b,
193*4882a593Smuzhiyun u8 arg_a, u8 arg_b, u8 arg_c, u8 arg_d)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun int ret, len;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun memcpy(gm12u320->cmd_buf, &cmd_misc, CMD_SIZE);
198*4882a593Smuzhiyun gm12u320->cmd_buf[20] = req_a;
199*4882a593Smuzhiyun gm12u320->cmd_buf[21] = req_b;
200*4882a593Smuzhiyun gm12u320->cmd_buf[22] = arg_a;
201*4882a593Smuzhiyun gm12u320->cmd_buf[23] = arg_b;
202*4882a593Smuzhiyun gm12u320->cmd_buf[24] = arg_c;
203*4882a593Smuzhiyun gm12u320->cmd_buf[25] = arg_d;
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun /* Send request */
206*4882a593Smuzhiyun ret = usb_bulk_msg(gm12u320->udev,
207*4882a593Smuzhiyun usb_sndbulkpipe(gm12u320->udev, MISC_SND_EPT),
208*4882a593Smuzhiyun gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
209*4882a593Smuzhiyun if (ret || len != CMD_SIZE) {
210*4882a593Smuzhiyun GM12U320_ERR("Misc. req. error %d\n", ret);
211*4882a593Smuzhiyun return -EIO;
212*4882a593Smuzhiyun }
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun /* Read value */
215*4882a593Smuzhiyun ret = usb_bulk_msg(gm12u320->udev,
216*4882a593Smuzhiyun usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT),
217*4882a593Smuzhiyun gm12u320->cmd_buf, MISC_VALUE_SIZE, &len,
218*4882a593Smuzhiyun DATA_TIMEOUT);
219*4882a593Smuzhiyun if (ret || len != MISC_VALUE_SIZE) {
220*4882a593Smuzhiyun GM12U320_ERR("Misc. value error %d\n", ret);
221*4882a593Smuzhiyun return -EIO;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun /* cmd_buf[0] now contains the read value, which we don't use */
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun /* Read status */
226*4882a593Smuzhiyun ret = usb_bulk_msg(gm12u320->udev,
227*4882a593Smuzhiyun usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT),
228*4882a593Smuzhiyun gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
229*4882a593Smuzhiyun CMD_TIMEOUT);
230*4882a593Smuzhiyun if (ret || len != READ_STATUS_SIZE) {
231*4882a593Smuzhiyun GM12U320_ERR("Misc. status error %d\n", ret);
232*4882a593Smuzhiyun return -EIO;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun return 0;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
gm12u320_32bpp_to_24bpp_packed(u8 * dst,u8 * src,int len)238*4882a593Smuzhiyun static void gm12u320_32bpp_to_24bpp_packed(u8 *dst, u8 *src, int len)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun while (len--) {
241*4882a593Smuzhiyun *dst++ = *src++;
242*4882a593Smuzhiyun *dst++ = *src++;
243*4882a593Smuzhiyun *dst++ = *src++;
244*4882a593Smuzhiyun src++;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
gm12u320_copy_fb_to_blocks(struct gm12u320_device * gm12u320)248*4882a593Smuzhiyun static void gm12u320_copy_fb_to_blocks(struct gm12u320_device *gm12u320)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun int block, dst_offset, len, remain, ret, x1, x2, y1, y2;
251*4882a593Smuzhiyun struct drm_framebuffer *fb;
252*4882a593Smuzhiyun void *vaddr;
253*4882a593Smuzhiyun u8 *src;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun mutex_lock(&gm12u320->fb_update.lock);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun if (!gm12u320->fb_update.fb)
258*4882a593Smuzhiyun goto unlock;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun fb = gm12u320->fb_update.fb;
261*4882a593Smuzhiyun x1 = gm12u320->fb_update.rect.x1;
262*4882a593Smuzhiyun x2 = gm12u320->fb_update.rect.x2;
263*4882a593Smuzhiyun y1 = gm12u320->fb_update.rect.y1;
264*4882a593Smuzhiyun y2 = gm12u320->fb_update.rect.y2;
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun vaddr = drm_gem_shmem_vmap(fb->obj[0]);
267*4882a593Smuzhiyun if (IS_ERR(vaddr)) {
268*4882a593Smuzhiyun GM12U320_ERR("failed to vmap fb: %ld\n", PTR_ERR(vaddr));
269*4882a593Smuzhiyun goto put_fb;
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun if (fb->obj[0]->import_attach) {
273*4882a593Smuzhiyun ret = dma_buf_begin_cpu_access(
274*4882a593Smuzhiyun fb->obj[0]->import_attach->dmabuf, DMA_FROM_DEVICE);
275*4882a593Smuzhiyun if (ret) {
276*4882a593Smuzhiyun GM12U320_ERR("dma_buf_begin_cpu_access err: %d\n", ret);
277*4882a593Smuzhiyun goto vunmap;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun src = vaddr + y1 * fb->pitches[0] + x1 * 4;
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun x1 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
284*4882a593Smuzhiyun x2 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun for (; y1 < y2; y1++) {
287*4882a593Smuzhiyun remain = 0;
288*4882a593Smuzhiyun len = (x2 - x1) * 3;
289*4882a593Smuzhiyun dst_offset = (y1 * GM12U320_REAL_WIDTH + x1) * 3;
290*4882a593Smuzhiyun block = dst_offset / DATA_BLOCK_CONTENT_SIZE;
291*4882a593Smuzhiyun dst_offset %= DATA_BLOCK_CONTENT_SIZE;
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun if ((dst_offset + len) > DATA_BLOCK_CONTENT_SIZE) {
294*4882a593Smuzhiyun remain = dst_offset + len - DATA_BLOCK_CONTENT_SIZE;
295*4882a593Smuzhiyun len = DATA_BLOCK_CONTENT_SIZE - dst_offset;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun dst_offset += DATA_BLOCK_HEADER_SIZE;
299*4882a593Smuzhiyun len /= 3;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun gm12u320_32bpp_to_24bpp_packed(
302*4882a593Smuzhiyun gm12u320->data_buf[block] + dst_offset,
303*4882a593Smuzhiyun src, len);
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun if (remain) {
306*4882a593Smuzhiyun block++;
307*4882a593Smuzhiyun dst_offset = DATA_BLOCK_HEADER_SIZE;
308*4882a593Smuzhiyun gm12u320_32bpp_to_24bpp_packed(
309*4882a593Smuzhiyun gm12u320->data_buf[block] + dst_offset,
310*4882a593Smuzhiyun src + len * 4, remain / 3);
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun src += fb->pitches[0];
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun if (fb->obj[0]->import_attach) {
316*4882a593Smuzhiyun ret = dma_buf_end_cpu_access(fb->obj[0]->import_attach->dmabuf,
317*4882a593Smuzhiyun DMA_FROM_DEVICE);
318*4882a593Smuzhiyun if (ret)
319*4882a593Smuzhiyun GM12U320_ERR("dma_buf_end_cpu_access err: %d\n", ret);
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun vunmap:
322*4882a593Smuzhiyun drm_gem_shmem_vunmap(fb->obj[0], vaddr);
323*4882a593Smuzhiyun put_fb:
324*4882a593Smuzhiyun drm_framebuffer_put(fb);
325*4882a593Smuzhiyun gm12u320->fb_update.fb = NULL;
326*4882a593Smuzhiyun unlock:
327*4882a593Smuzhiyun mutex_unlock(&gm12u320->fb_update.lock);
328*4882a593Smuzhiyun }
329*4882a593Smuzhiyun
gm12u320_fb_update_work(struct work_struct * work)330*4882a593Smuzhiyun static void gm12u320_fb_update_work(struct work_struct *work)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun struct gm12u320_device *gm12u320 =
333*4882a593Smuzhiyun container_of(to_delayed_work(work), struct gm12u320_device,
334*4882a593Smuzhiyun fb_update.work);
335*4882a593Smuzhiyun int block, block_size, len;
336*4882a593Smuzhiyun int ret = 0;
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun gm12u320_copy_fb_to_blocks(gm12u320);
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun for (block = 0; block < GM12U320_BLOCK_COUNT; block++) {
341*4882a593Smuzhiyun if (block == GM12U320_BLOCK_COUNT - 1)
342*4882a593Smuzhiyun block_size = DATA_LAST_BLOCK_SIZE;
343*4882a593Smuzhiyun else
344*4882a593Smuzhiyun block_size = DATA_BLOCK_SIZE;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun /* Send data command to device */
347*4882a593Smuzhiyun memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE);
348*4882a593Smuzhiyun gm12u320->cmd_buf[8] = block_size & 0xff;
349*4882a593Smuzhiyun gm12u320->cmd_buf[9] = block_size >> 8;
350*4882a593Smuzhiyun gm12u320->cmd_buf[20] = 0xfc - block * 4;
351*4882a593Smuzhiyun gm12u320->cmd_buf[21] =
352*4882a593Smuzhiyun block | (gm12u320->fb_update.frame << 7);
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun ret = usb_bulk_msg(gm12u320->udev,
355*4882a593Smuzhiyun usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
356*4882a593Smuzhiyun gm12u320->cmd_buf, CMD_SIZE, &len,
357*4882a593Smuzhiyun CMD_TIMEOUT);
358*4882a593Smuzhiyun if (ret || len != CMD_SIZE)
359*4882a593Smuzhiyun goto err;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun /* Send data block to device */
362*4882a593Smuzhiyun ret = usb_bulk_msg(gm12u320->udev,
363*4882a593Smuzhiyun usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
364*4882a593Smuzhiyun gm12u320->data_buf[block], block_size,
365*4882a593Smuzhiyun &len, DATA_TIMEOUT);
366*4882a593Smuzhiyun if (ret || len != block_size)
367*4882a593Smuzhiyun goto err;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun /* Read status */
370*4882a593Smuzhiyun ret = usb_bulk_msg(gm12u320->udev,
371*4882a593Smuzhiyun usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
372*4882a593Smuzhiyun gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
373*4882a593Smuzhiyun CMD_TIMEOUT);
374*4882a593Smuzhiyun if (ret || len != READ_STATUS_SIZE)
375*4882a593Smuzhiyun goto err;
376*4882a593Smuzhiyun }
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun /* Send draw command to device */
379*4882a593Smuzhiyun memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE);
380*4882a593Smuzhiyun ret = usb_bulk_msg(gm12u320->udev,
381*4882a593Smuzhiyun usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
382*4882a593Smuzhiyun gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
383*4882a593Smuzhiyun if (ret || len != CMD_SIZE)
384*4882a593Smuzhiyun goto err;
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun /* Read status */
387*4882a593Smuzhiyun ret = usb_bulk_msg(gm12u320->udev,
388*4882a593Smuzhiyun usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
389*4882a593Smuzhiyun gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
390*4882a593Smuzhiyun gm12u320->fb_update.draw_status_timeout);
391*4882a593Smuzhiyun if (ret || len != READ_STATUS_SIZE)
392*4882a593Smuzhiyun goto err;
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun gm12u320->fb_update.draw_status_timeout = CMD_TIMEOUT;
395*4882a593Smuzhiyun gm12u320->fb_update.frame = !gm12u320->fb_update.frame;
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun /*
398*4882a593Smuzhiyun * We must draw a frame every 2s otherwise the projector
399*4882a593Smuzhiyun * switches back to showing its logo.
400*4882a593Smuzhiyun */
401*4882a593Smuzhiyun queue_delayed_work(system_long_wq, &gm12u320->fb_update.work,
402*4882a593Smuzhiyun IDLE_TIMEOUT);
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun return;
405*4882a593Smuzhiyun err:
406*4882a593Smuzhiyun /* Do not log errors caused by module unload or device unplug */
407*4882a593Smuzhiyun if (ret != -ENODEV && ret != -ECONNRESET && ret != -ESHUTDOWN)
408*4882a593Smuzhiyun GM12U320_ERR("Frame update error: %d\n", ret);
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun
gm12u320_fb_mark_dirty(struct drm_framebuffer * fb,struct drm_rect * dirty)411*4882a593Smuzhiyun static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb,
412*4882a593Smuzhiyun struct drm_rect *dirty)
413*4882a593Smuzhiyun {
414*4882a593Smuzhiyun struct gm12u320_device *gm12u320 = to_gm12u320(fb->dev);
415*4882a593Smuzhiyun struct drm_framebuffer *old_fb = NULL;
416*4882a593Smuzhiyun bool wakeup = false;
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun mutex_lock(&gm12u320->fb_update.lock);
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun if (gm12u320->fb_update.fb != fb) {
421*4882a593Smuzhiyun old_fb = gm12u320->fb_update.fb;
422*4882a593Smuzhiyun drm_framebuffer_get(fb);
423*4882a593Smuzhiyun gm12u320->fb_update.fb = fb;
424*4882a593Smuzhiyun gm12u320->fb_update.rect = *dirty;
425*4882a593Smuzhiyun wakeup = true;
426*4882a593Smuzhiyun } else {
427*4882a593Smuzhiyun struct drm_rect *rect = &gm12u320->fb_update.rect;
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun rect->x1 = min(rect->x1, dirty->x1);
430*4882a593Smuzhiyun rect->y1 = min(rect->y1, dirty->y1);
431*4882a593Smuzhiyun rect->x2 = max(rect->x2, dirty->x2);
432*4882a593Smuzhiyun rect->y2 = max(rect->y2, dirty->y2);
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun mutex_unlock(&gm12u320->fb_update.lock);
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun if (wakeup)
438*4882a593Smuzhiyun mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0);
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun if (old_fb)
441*4882a593Smuzhiyun drm_framebuffer_put(old_fb);
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun
gm12u320_stop_fb_update(struct gm12u320_device * gm12u320)444*4882a593Smuzhiyun static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320)
445*4882a593Smuzhiyun {
446*4882a593Smuzhiyun struct drm_framebuffer *old_fb;
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun cancel_delayed_work_sync(&gm12u320->fb_update.work);
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun mutex_lock(&gm12u320->fb_update.lock);
451*4882a593Smuzhiyun old_fb = gm12u320->fb_update.fb;
452*4882a593Smuzhiyun gm12u320->fb_update.fb = NULL;
453*4882a593Smuzhiyun mutex_unlock(&gm12u320->fb_update.lock);
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun drm_framebuffer_put(old_fb);
456*4882a593Smuzhiyun }
457*4882a593Smuzhiyun
gm12u320_set_ecomode(struct gm12u320_device * gm12u320)458*4882a593Smuzhiyun static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320)
459*4882a593Smuzhiyun {
460*4882a593Smuzhiyun return gm12u320_misc_request(gm12u320, MISC_REQ_GET_SET_ECO_A,
461*4882a593Smuzhiyun MISC_REQ_GET_SET_ECO_B, 0x01 /* set */,
462*4882a593Smuzhiyun eco_mode ? 0x01 : 0x00, 0x00, 0x01);
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
466*4882a593Smuzhiyun /* gm12u320 connector */
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun /*
469*4882a593Smuzhiyun * We use fake EDID info so that userspace know that it is dealing with
470*4882a593Smuzhiyun * an Acer projector, rather then listing this as an "unknown" monitor.
471*4882a593Smuzhiyun * Note this assumes this driver is only ever used with the Acer C120, if we
472*4882a593Smuzhiyun * add support for other devices the vendor and model should be parameterized.
473*4882a593Smuzhiyun */
474*4882a593Smuzhiyun static struct edid gm12u320_edid = {
475*4882a593Smuzhiyun .header = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 },
476*4882a593Smuzhiyun .mfg_id = { 0x04, 0x72 }, /* "ACR" */
477*4882a593Smuzhiyun .prod_code = { 0x20, 0xc1 }, /* C120h */
478*4882a593Smuzhiyun .serial = 0xaa55aa55,
479*4882a593Smuzhiyun .mfg_week = 1,
480*4882a593Smuzhiyun .mfg_year = 16,
481*4882a593Smuzhiyun .version = 1, /* EDID 1.3 */
482*4882a593Smuzhiyun .revision = 3, /* EDID 1.3 */
483*4882a593Smuzhiyun .input = 0x08, /* Analog input */
484*4882a593Smuzhiyun .features = 0x0a, /* Pref timing in DTD 1 */
485*4882a593Smuzhiyun .standard_timings = { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 },
486*4882a593Smuzhiyun { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 } },
487*4882a593Smuzhiyun .detailed_timings = { {
488*4882a593Smuzhiyun .pixel_clock = 3383,
489*4882a593Smuzhiyun /* hactive = 848, hblank = 256 */
490*4882a593Smuzhiyun .data.pixel_data.hactive_lo = 0x50,
491*4882a593Smuzhiyun .data.pixel_data.hblank_lo = 0x00,
492*4882a593Smuzhiyun .data.pixel_data.hactive_hblank_hi = 0x31,
493*4882a593Smuzhiyun /* vactive = 480, vblank = 28 */
494*4882a593Smuzhiyun .data.pixel_data.vactive_lo = 0xe0,
495*4882a593Smuzhiyun .data.pixel_data.vblank_lo = 0x1c,
496*4882a593Smuzhiyun .data.pixel_data.vactive_vblank_hi = 0x10,
497*4882a593Smuzhiyun /* hsync offset 40 pw 128, vsync offset 1 pw 4 */
498*4882a593Smuzhiyun .data.pixel_data.hsync_offset_lo = 0x28,
499*4882a593Smuzhiyun .data.pixel_data.hsync_pulse_width_lo = 0x80,
500*4882a593Smuzhiyun .data.pixel_data.vsync_offset_pulse_width_lo = 0x14,
501*4882a593Smuzhiyun .data.pixel_data.hsync_vsync_offset_pulse_width_hi = 0x00,
502*4882a593Smuzhiyun /* Digital separate syncs, hsync+, vsync+ */
503*4882a593Smuzhiyun .data.pixel_data.misc = 0x1e,
504*4882a593Smuzhiyun }, {
505*4882a593Smuzhiyun .pixel_clock = 0,
506*4882a593Smuzhiyun .data.other_data.type = 0xfd, /* Monitor ranges */
507*4882a593Smuzhiyun .data.other_data.data.range.min_vfreq = 59,
508*4882a593Smuzhiyun .data.other_data.data.range.max_vfreq = 61,
509*4882a593Smuzhiyun .data.other_data.data.range.min_hfreq_khz = 29,
510*4882a593Smuzhiyun .data.other_data.data.range.max_hfreq_khz = 32,
511*4882a593Smuzhiyun .data.other_data.data.range.pixel_clock_mhz = 4, /* 40 MHz */
512*4882a593Smuzhiyun .data.other_data.data.range.flags = 0,
513*4882a593Smuzhiyun .data.other_data.data.range.formula.cvt = {
514*4882a593Smuzhiyun 0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 },
515*4882a593Smuzhiyun }, {
516*4882a593Smuzhiyun .pixel_clock = 0,
517*4882a593Smuzhiyun .data.other_data.type = 0xfc, /* Model string */
518*4882a593Smuzhiyun .data.other_data.data.str.str = {
519*4882a593Smuzhiyun 'P', 'r', 'o', 'j', 'e', 'c', 't', 'o', 'r', '\n',
520*4882a593Smuzhiyun ' ', ' ', ' ' },
521*4882a593Smuzhiyun }, {
522*4882a593Smuzhiyun .pixel_clock = 0,
523*4882a593Smuzhiyun .data.other_data.type = 0xfe, /* Unspecified text / padding */
524*4882a593Smuzhiyun .data.other_data.data.str.str = {
525*4882a593Smuzhiyun '\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
526*4882a593Smuzhiyun ' ', ' ', ' ' },
527*4882a593Smuzhiyun } },
528*4882a593Smuzhiyun .checksum = 0x13,
529*4882a593Smuzhiyun };
530*4882a593Smuzhiyun
gm12u320_conn_get_modes(struct drm_connector * connector)531*4882a593Smuzhiyun static int gm12u320_conn_get_modes(struct drm_connector *connector)
532*4882a593Smuzhiyun {
533*4882a593Smuzhiyun drm_connector_update_edid_property(connector, &gm12u320_edid);
534*4882a593Smuzhiyun return drm_add_edid_modes(connector, &gm12u320_edid);
535*4882a593Smuzhiyun }
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun static const struct drm_connector_helper_funcs gm12u320_conn_helper_funcs = {
538*4882a593Smuzhiyun .get_modes = gm12u320_conn_get_modes,
539*4882a593Smuzhiyun };
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun static const struct drm_connector_funcs gm12u320_conn_funcs = {
542*4882a593Smuzhiyun .fill_modes = drm_helper_probe_single_connector_modes,
543*4882a593Smuzhiyun .destroy = drm_connector_cleanup,
544*4882a593Smuzhiyun .reset = drm_atomic_helper_connector_reset,
545*4882a593Smuzhiyun .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
546*4882a593Smuzhiyun .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
547*4882a593Smuzhiyun };
548*4882a593Smuzhiyun
gm12u320_conn_init(struct gm12u320_device * gm12u320)549*4882a593Smuzhiyun static int gm12u320_conn_init(struct gm12u320_device *gm12u320)
550*4882a593Smuzhiyun {
551*4882a593Smuzhiyun drm_connector_helper_add(&gm12u320->conn, &gm12u320_conn_helper_funcs);
552*4882a593Smuzhiyun return drm_connector_init(&gm12u320->dev, &gm12u320->conn,
553*4882a593Smuzhiyun &gm12u320_conn_funcs, DRM_MODE_CONNECTOR_VGA);
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
557*4882a593Smuzhiyun /* gm12u320 (simple) display pipe */
558*4882a593Smuzhiyun
gm12u320_pipe_enable(struct drm_simple_display_pipe * pipe,struct drm_crtc_state * crtc_state,struct drm_plane_state * plane_state)559*4882a593Smuzhiyun static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe,
560*4882a593Smuzhiyun struct drm_crtc_state *crtc_state,
561*4882a593Smuzhiyun struct drm_plane_state *plane_state)
562*4882a593Smuzhiyun {
563*4882a593Smuzhiyun struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT };
564*4882a593Smuzhiyun struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT;
567*4882a593Smuzhiyun gm12u320_fb_mark_dirty(plane_state->fb, &rect);
568*4882a593Smuzhiyun }
569*4882a593Smuzhiyun
gm12u320_pipe_disable(struct drm_simple_display_pipe * pipe)570*4882a593Smuzhiyun static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
571*4882a593Smuzhiyun {
572*4882a593Smuzhiyun struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun gm12u320_stop_fb_update(gm12u320);
575*4882a593Smuzhiyun }
576*4882a593Smuzhiyun
gm12u320_pipe_update(struct drm_simple_display_pipe * pipe,struct drm_plane_state * old_state)577*4882a593Smuzhiyun static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe,
578*4882a593Smuzhiyun struct drm_plane_state *old_state)
579*4882a593Smuzhiyun {
580*4882a593Smuzhiyun struct drm_plane_state *state = pipe->plane.state;
581*4882a593Smuzhiyun struct drm_rect rect;
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun if (drm_atomic_helper_damage_merged(old_state, state, &rect))
584*4882a593Smuzhiyun gm12u320_fb_mark_dirty(pipe->plane.state->fb, &rect);
585*4882a593Smuzhiyun }
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun static const struct drm_simple_display_pipe_funcs gm12u320_pipe_funcs = {
588*4882a593Smuzhiyun .enable = gm12u320_pipe_enable,
589*4882a593Smuzhiyun .disable = gm12u320_pipe_disable,
590*4882a593Smuzhiyun .update = gm12u320_pipe_update,
591*4882a593Smuzhiyun };
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun static const uint32_t gm12u320_pipe_formats[] = {
594*4882a593Smuzhiyun DRM_FORMAT_XRGB8888,
595*4882a593Smuzhiyun };
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun static const uint64_t gm12u320_pipe_modifiers[] = {
598*4882a593Smuzhiyun DRM_FORMAT_MOD_LINEAR,
599*4882a593Smuzhiyun DRM_FORMAT_MOD_INVALID
600*4882a593Smuzhiyun };
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun /*
603*4882a593Smuzhiyun * FIXME: Dma-buf sharing requires DMA support by the importing device.
604*4882a593Smuzhiyun * This function is a workaround to make USB devices work as well.
605*4882a593Smuzhiyun * See todo.rst for how to fix the issue in the dma-buf framework.
606*4882a593Smuzhiyun */
gm12u320_gem_prime_import(struct drm_device * dev,struct dma_buf * dma_buf)607*4882a593Smuzhiyun static struct drm_gem_object *gm12u320_gem_prime_import(struct drm_device *dev,
608*4882a593Smuzhiyun struct dma_buf *dma_buf)
609*4882a593Smuzhiyun {
610*4882a593Smuzhiyun struct gm12u320_device *gm12u320 = to_gm12u320(dev);
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun if (!gm12u320->dmadev)
613*4882a593Smuzhiyun return ERR_PTR(-ENODEV);
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun return drm_gem_prime_import_dev(dev, dma_buf, gm12u320->dmadev);
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun DEFINE_DRM_GEM_FOPS(gm12u320_fops);
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun static struct drm_driver gm12u320_drm_driver = {
621*4882a593Smuzhiyun .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun .name = DRIVER_NAME,
624*4882a593Smuzhiyun .desc = DRIVER_DESC,
625*4882a593Smuzhiyun .date = DRIVER_DATE,
626*4882a593Smuzhiyun .major = DRIVER_MAJOR,
627*4882a593Smuzhiyun .minor = DRIVER_MINOR,
628*4882a593Smuzhiyun
629*4882a593Smuzhiyun .fops = &gm12u320_fops,
630*4882a593Smuzhiyun DRM_GEM_SHMEM_DRIVER_OPS,
631*4882a593Smuzhiyun .gem_prime_import = gm12u320_gem_prime_import,
632*4882a593Smuzhiyun };
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun static const struct drm_mode_config_funcs gm12u320_mode_config_funcs = {
635*4882a593Smuzhiyun .fb_create = drm_gem_fb_create_with_dirty,
636*4882a593Smuzhiyun .atomic_check = drm_atomic_helper_check,
637*4882a593Smuzhiyun .atomic_commit = drm_atomic_helper_commit,
638*4882a593Smuzhiyun };
639*4882a593Smuzhiyun
gm12u320_usb_probe(struct usb_interface * interface,const struct usb_device_id * id)640*4882a593Smuzhiyun static int gm12u320_usb_probe(struct usb_interface *interface,
641*4882a593Smuzhiyun const struct usb_device_id *id)
642*4882a593Smuzhiyun {
643*4882a593Smuzhiyun struct gm12u320_device *gm12u320;
644*4882a593Smuzhiyun struct drm_device *dev;
645*4882a593Smuzhiyun int ret;
646*4882a593Smuzhiyun
647*4882a593Smuzhiyun /*
648*4882a593Smuzhiyun * The gm12u320 presents itself to the system as 2 usb mass-storage
649*4882a593Smuzhiyun * interfaces, we only care about / need the first one.
650*4882a593Smuzhiyun */
651*4882a593Smuzhiyun if (interface->cur_altsetting->desc.bInterfaceNumber != 0)
652*4882a593Smuzhiyun return -ENODEV;
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun gm12u320 = devm_drm_dev_alloc(&interface->dev, &gm12u320_drm_driver,
655*4882a593Smuzhiyun struct gm12u320_device, dev);
656*4882a593Smuzhiyun if (IS_ERR(gm12u320))
657*4882a593Smuzhiyun return PTR_ERR(gm12u320);
658*4882a593Smuzhiyun dev = &gm12u320->dev;
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun gm12u320->dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
661*4882a593Smuzhiyun if (!gm12u320->dmadev)
662*4882a593Smuzhiyun drm_warn(dev, "buffer sharing not supported"); /* not an error */
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun gm12u320->udev = interface_to_usbdev(interface);
665*4882a593Smuzhiyun INIT_DELAYED_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work);
666*4882a593Smuzhiyun mutex_init(&gm12u320->fb_update.lock);
667*4882a593Smuzhiyun
668*4882a593Smuzhiyun ret = drmm_mode_config_init(dev);
669*4882a593Smuzhiyun if (ret)
670*4882a593Smuzhiyun goto err_put_device;
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun dev->mode_config.min_width = GM12U320_USER_WIDTH;
673*4882a593Smuzhiyun dev->mode_config.max_width = GM12U320_USER_WIDTH;
674*4882a593Smuzhiyun dev->mode_config.min_height = GM12U320_HEIGHT;
675*4882a593Smuzhiyun dev->mode_config.max_height = GM12U320_HEIGHT;
676*4882a593Smuzhiyun dev->mode_config.funcs = &gm12u320_mode_config_funcs;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun ret = gm12u320_usb_alloc(gm12u320);
679*4882a593Smuzhiyun if (ret)
680*4882a593Smuzhiyun goto err_put_device;
681*4882a593Smuzhiyun
682*4882a593Smuzhiyun ret = gm12u320_set_ecomode(gm12u320);
683*4882a593Smuzhiyun if (ret)
684*4882a593Smuzhiyun goto err_put_device;
685*4882a593Smuzhiyun
686*4882a593Smuzhiyun ret = gm12u320_conn_init(gm12u320);
687*4882a593Smuzhiyun if (ret)
688*4882a593Smuzhiyun goto err_put_device;
689*4882a593Smuzhiyun
690*4882a593Smuzhiyun ret = drm_simple_display_pipe_init(&gm12u320->dev,
691*4882a593Smuzhiyun &gm12u320->pipe,
692*4882a593Smuzhiyun &gm12u320_pipe_funcs,
693*4882a593Smuzhiyun gm12u320_pipe_formats,
694*4882a593Smuzhiyun ARRAY_SIZE(gm12u320_pipe_formats),
695*4882a593Smuzhiyun gm12u320_pipe_modifiers,
696*4882a593Smuzhiyun &gm12u320->conn);
697*4882a593Smuzhiyun if (ret)
698*4882a593Smuzhiyun goto err_put_device;
699*4882a593Smuzhiyun
700*4882a593Smuzhiyun drm_mode_config_reset(dev);
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun usb_set_intfdata(interface, dev);
703*4882a593Smuzhiyun ret = drm_dev_register(dev, 0);
704*4882a593Smuzhiyun if (ret)
705*4882a593Smuzhiyun goto err_put_device;
706*4882a593Smuzhiyun
707*4882a593Smuzhiyun drm_fbdev_generic_setup(dev, 0);
708*4882a593Smuzhiyun
709*4882a593Smuzhiyun return 0;
710*4882a593Smuzhiyun
711*4882a593Smuzhiyun err_put_device:
712*4882a593Smuzhiyun put_device(gm12u320->dmadev);
713*4882a593Smuzhiyun return ret;
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun
gm12u320_usb_disconnect(struct usb_interface * interface)716*4882a593Smuzhiyun static void gm12u320_usb_disconnect(struct usb_interface *interface)
717*4882a593Smuzhiyun {
718*4882a593Smuzhiyun struct drm_device *dev = usb_get_intfdata(interface);
719*4882a593Smuzhiyun struct gm12u320_device *gm12u320 = to_gm12u320(dev);
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun put_device(gm12u320->dmadev);
722*4882a593Smuzhiyun gm12u320->dmadev = NULL;
723*4882a593Smuzhiyun drm_dev_unplug(dev);
724*4882a593Smuzhiyun drm_atomic_helper_shutdown(dev);
725*4882a593Smuzhiyun }
726*4882a593Smuzhiyun
gm12u320_suspend(struct usb_interface * interface,pm_message_t message)727*4882a593Smuzhiyun static __maybe_unused int gm12u320_suspend(struct usb_interface *interface,
728*4882a593Smuzhiyun pm_message_t message)
729*4882a593Smuzhiyun {
730*4882a593Smuzhiyun struct drm_device *dev = usb_get_intfdata(interface);
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun return drm_mode_config_helper_suspend(dev);
733*4882a593Smuzhiyun }
734*4882a593Smuzhiyun
gm12u320_resume(struct usb_interface * interface)735*4882a593Smuzhiyun static __maybe_unused int gm12u320_resume(struct usb_interface *interface)
736*4882a593Smuzhiyun {
737*4882a593Smuzhiyun struct drm_device *dev = usb_get_intfdata(interface);
738*4882a593Smuzhiyun struct gm12u320_device *gm12u320 = to_gm12u320(dev);
739*4882a593Smuzhiyun
740*4882a593Smuzhiyun gm12u320_set_ecomode(gm12u320);
741*4882a593Smuzhiyun
742*4882a593Smuzhiyun return drm_mode_config_helper_resume(dev);
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun static const struct usb_device_id id_table[] = {
746*4882a593Smuzhiyun { USB_DEVICE(0x1de1, 0xc102) },
747*4882a593Smuzhiyun {},
748*4882a593Smuzhiyun };
749*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, id_table);
750*4882a593Smuzhiyun
751*4882a593Smuzhiyun static struct usb_driver gm12u320_usb_driver = {
752*4882a593Smuzhiyun .name = "gm12u320",
753*4882a593Smuzhiyun .probe = gm12u320_usb_probe,
754*4882a593Smuzhiyun .disconnect = gm12u320_usb_disconnect,
755*4882a593Smuzhiyun .id_table = id_table,
756*4882a593Smuzhiyun #ifdef CONFIG_PM
757*4882a593Smuzhiyun .suspend = gm12u320_suspend,
758*4882a593Smuzhiyun .resume = gm12u320_resume,
759*4882a593Smuzhiyun .reset_resume = gm12u320_resume,
760*4882a593Smuzhiyun #endif
761*4882a593Smuzhiyun };
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun module_usb_driver(gm12u320_usb_driver);
764*4882a593Smuzhiyun MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
765*4882a593Smuzhiyun MODULE_LICENSE("GPL");
766