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