1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright 2020 Noralf Trønnes |
4 | */ |
5 | |
6 | #include <linux/dma-buf.h> |
7 | #include <linux/dma-mapping.h> |
8 | #include <linux/lz4.h> |
9 | #include <linux/module.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/string_helpers.h> |
12 | #include <linux/usb.h> |
13 | #include <linux/vmalloc.h> |
14 | #include <linux/workqueue.h> |
15 | |
16 | #include <drm/drm_atomic_helper.h> |
17 | #include <drm/drm_blend.h> |
18 | #include <drm/drm_damage_helper.h> |
19 | #include <drm/drm_debugfs.h> |
20 | #include <drm/drm_drv.h> |
21 | #include <drm/drm_fbdev_generic.h> |
22 | #include <drm/drm_fourcc.h> |
23 | #include <drm/drm_gem_atomic_helper.h> |
24 | #include <drm/drm_gem_framebuffer_helper.h> |
25 | #include <drm/drm_gem_shmem_helper.h> |
26 | #include <drm/drm_managed.h> |
27 | #include <drm/drm_print.h> |
28 | #include <drm/drm_probe_helper.h> |
29 | #include <drm/drm_simple_kms_helper.h> |
30 | #include <drm/gud.h> |
31 | |
32 | #include "gud_internal.h" |
33 | |
34 | /* Only used internally */ |
35 | static const struct drm_format_info gud_drm_format_r1 = { |
36 | .format = GUD_DRM_FORMAT_R1, |
37 | .num_planes = 1, |
38 | .char_per_block = { 1, 0, 0 }, |
39 | .block_w = { 8, 0, 0 }, |
40 | .block_h = { 1, 0, 0 }, |
41 | .hsub = 1, |
42 | .vsub = 1, |
43 | }; |
44 | |
45 | static const struct drm_format_info gud_drm_format_xrgb1111 = { |
46 | .format = GUD_DRM_FORMAT_XRGB1111, |
47 | .num_planes = 1, |
48 | .char_per_block = { 1, 0, 0 }, |
49 | .block_w = { 2, 0, 0 }, |
50 | .block_h = { 1, 0, 0 }, |
51 | .hsub = 1, |
52 | .vsub = 1, |
53 | }; |
54 | |
55 | static int gud_usb_control_msg(struct usb_interface *intf, bool in, |
56 | u8 request, u16 value, void *buf, size_t len) |
57 | { |
58 | u8 requesttype = USB_TYPE_VENDOR | USB_RECIP_INTERFACE; |
59 | u8 ifnum = intf->cur_altsetting->desc.bInterfaceNumber; |
60 | struct usb_device *usb = interface_to_usbdev(intf); |
61 | unsigned int pipe; |
62 | |
63 | if (len && !buf) |
64 | return -EINVAL; |
65 | |
66 | if (in) { |
67 | pipe = usb_rcvctrlpipe(usb, 0); |
68 | requesttype |= USB_DIR_IN; |
69 | } else { |
70 | pipe = usb_sndctrlpipe(usb, 0); |
71 | requesttype |= USB_DIR_OUT; |
72 | } |
73 | |
74 | return usb_control_msg(dev: usb, pipe, request, requesttype, value, |
75 | index: ifnum, data: buf, size: len, USB_CTRL_GET_TIMEOUT); |
76 | } |
77 | |
78 | static int gud_get_display_descriptor(struct usb_interface *intf, |
79 | struct gud_display_descriptor_req *desc) |
80 | { |
81 | void *buf; |
82 | int ret; |
83 | |
84 | buf = kmalloc(size: sizeof(*desc), GFP_KERNEL); |
85 | if (!buf) |
86 | return -ENOMEM; |
87 | |
88 | ret = gud_usb_control_msg(intf, in: true, GUD_REQ_GET_DESCRIPTOR, value: 0, buf, len: sizeof(*desc)); |
89 | memcpy(desc, buf, sizeof(*desc)); |
90 | kfree(objp: buf); |
91 | if (ret < 0) |
92 | return ret; |
93 | if (ret != sizeof(*desc)) |
94 | return -EIO; |
95 | |
96 | if (desc->magic != le32_to_cpu(GUD_DISPLAY_MAGIC)) |
97 | return -ENODATA; |
98 | |
99 | DRM_DEV_DEBUG_DRIVER(&intf->dev, |
100 | "version=%u flags=0x%x compression=0x%x max_buffer_size=%u\n" , |
101 | desc->version, le32_to_cpu(desc->flags), desc->compression, |
102 | le32_to_cpu(desc->max_buffer_size)); |
103 | |
104 | if (!desc->version || !desc->max_width || !desc->max_height || |
105 | le32_to_cpu(desc->min_width) > le32_to_cpu(desc->max_width) || |
106 | le32_to_cpu(desc->min_height) > le32_to_cpu(desc->max_height)) |
107 | return -EINVAL; |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | static int gud_status_to_errno(u8 status) |
113 | { |
114 | switch (status) { |
115 | case GUD_STATUS_OK: |
116 | return 0; |
117 | case GUD_STATUS_BUSY: |
118 | return -EBUSY; |
119 | case GUD_STATUS_REQUEST_NOT_SUPPORTED: |
120 | return -EOPNOTSUPP; |
121 | case GUD_STATUS_PROTOCOL_ERROR: |
122 | return -EPROTO; |
123 | case GUD_STATUS_INVALID_PARAMETER: |
124 | return -EINVAL; |
125 | case GUD_STATUS_ERROR: |
126 | return -EREMOTEIO; |
127 | default: |
128 | return -EREMOTEIO; |
129 | } |
130 | } |
131 | |
132 | static int gud_usb_get_status(struct usb_interface *intf) |
133 | { |
134 | int ret, status = -EIO; |
135 | u8 *buf; |
136 | |
137 | buf = kmalloc(size: sizeof(*buf), GFP_KERNEL); |
138 | if (!buf) |
139 | return -ENOMEM; |
140 | |
141 | ret = gud_usb_control_msg(intf, in: true, GUD_REQ_GET_STATUS, value: 0, buf, len: sizeof(*buf)); |
142 | if (ret == sizeof(*buf)) |
143 | status = gud_status_to_errno(status: *buf); |
144 | kfree(objp: buf); |
145 | |
146 | if (ret < 0) |
147 | return ret; |
148 | |
149 | return status; |
150 | } |
151 | |
152 | static int gud_usb_transfer(struct gud_device *gdrm, bool in, u8 request, u16 index, |
153 | void *buf, size_t len) |
154 | { |
155 | struct usb_interface *intf = to_usb_interface(gdrm->drm.dev); |
156 | int idx, ret; |
157 | |
158 | drm_dbg(&gdrm->drm, "%s: request=0x%x index=%u len=%zu\n" , |
159 | in ? "get" : "set" , request, index, len); |
160 | |
161 | if (!drm_dev_enter(dev: &gdrm->drm, idx: &idx)) |
162 | return -ENODEV; |
163 | |
164 | mutex_lock(&gdrm->ctrl_lock); |
165 | |
166 | ret = gud_usb_control_msg(intf, in, request, value: index, buf, len); |
167 | if (ret == -EPIPE || ((gdrm->flags & GUD_DISPLAY_FLAG_STATUS_ON_SET) && !in && ret >= 0)) { |
168 | int status; |
169 | |
170 | status = gud_usb_get_status(intf); |
171 | if (status < 0) { |
172 | ret = status; |
173 | } else if (ret < 0) { |
174 | dev_err_once(gdrm->drm.dev, |
175 | "Unexpected status OK for failed transfer\n" ); |
176 | ret = -EPIPE; |
177 | } |
178 | } |
179 | |
180 | if (ret < 0) { |
181 | drm_dbg(&gdrm->drm, "ret=%d\n" , ret); |
182 | gdrm->stats_num_errors++; |
183 | } |
184 | |
185 | mutex_unlock(lock: &gdrm->ctrl_lock); |
186 | drm_dev_exit(idx); |
187 | |
188 | return ret; |
189 | } |
190 | |
191 | /* |
192 | * @buf cannot be allocated on the stack. |
193 | * Returns number of bytes received or negative error code on failure. |
194 | */ |
195 | int gud_usb_get(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t max_len) |
196 | { |
197 | return gud_usb_transfer(gdrm, in: true, request, index, buf, len: max_len); |
198 | } |
199 | |
200 | /* |
201 | * @buf can be allocated on the stack or NULL. |
202 | * Returns zero on success or negative error code on failure. |
203 | */ |
204 | int gud_usb_set(struct gud_device *gdrm, u8 request, u16 index, void *buf, size_t len) |
205 | { |
206 | void *trbuf = NULL; |
207 | int ret; |
208 | |
209 | if (buf && len) { |
210 | trbuf = kmemdup(p: buf, size: len, GFP_KERNEL); |
211 | if (!trbuf) |
212 | return -ENOMEM; |
213 | } |
214 | |
215 | ret = gud_usb_transfer(gdrm, in: false, request, index, buf: trbuf, len); |
216 | kfree(objp: trbuf); |
217 | if (ret < 0) |
218 | return ret; |
219 | |
220 | return ret != len ? -EIO : 0; |
221 | } |
222 | |
223 | /* |
224 | * @val can be allocated on the stack. |
225 | * Returns zero on success or negative error code on failure. |
226 | */ |
227 | int gud_usb_get_u8(struct gud_device *gdrm, u8 request, u16 index, u8 *val) |
228 | { |
229 | u8 *buf; |
230 | int ret; |
231 | |
232 | buf = kmalloc(size: sizeof(*val), GFP_KERNEL); |
233 | if (!buf) |
234 | return -ENOMEM; |
235 | |
236 | ret = gud_usb_get(gdrm, request, index, buf, max_len: sizeof(*val)); |
237 | *val = *buf; |
238 | kfree(objp: buf); |
239 | if (ret < 0) |
240 | return ret; |
241 | |
242 | return ret != sizeof(*val) ? -EIO : 0; |
243 | } |
244 | |
245 | /* Returns zero on success or negative error code on failure. */ |
246 | int gud_usb_set_u8(struct gud_device *gdrm, u8 request, u8 val) |
247 | { |
248 | return gud_usb_set(gdrm, request, index: 0, buf: &val, len: sizeof(val)); |
249 | } |
250 | |
251 | static int gud_get_properties(struct gud_device *gdrm) |
252 | { |
253 | struct gud_property_req *properties; |
254 | unsigned int i, num_properties; |
255 | int ret; |
256 | |
257 | properties = kcalloc(GUD_PROPERTIES_MAX_NUM, size: sizeof(*properties), GFP_KERNEL); |
258 | if (!properties) |
259 | return -ENOMEM; |
260 | |
261 | ret = gud_usb_get(gdrm, GUD_REQ_GET_PROPERTIES, index: 0, |
262 | buf: properties, GUD_PROPERTIES_MAX_NUM * sizeof(*properties)); |
263 | if (ret <= 0) |
264 | goto out; |
265 | if (ret % sizeof(*properties)) { |
266 | ret = -EIO; |
267 | goto out; |
268 | } |
269 | |
270 | num_properties = ret / sizeof(*properties); |
271 | ret = 0; |
272 | |
273 | gdrm->properties = drmm_kcalloc(dev: &gdrm->drm, n: num_properties, size: sizeof(*gdrm->properties), |
274 | GFP_KERNEL); |
275 | if (!gdrm->properties) { |
276 | ret = -ENOMEM; |
277 | goto out; |
278 | } |
279 | |
280 | for (i = 0; i < num_properties; i++) { |
281 | u16 prop = le16_to_cpu(properties[i].prop); |
282 | u64 val = le64_to_cpu(properties[i].val); |
283 | |
284 | switch (prop) { |
285 | case GUD_PROPERTY_ROTATION: |
286 | /* |
287 | * DRM UAPI matches the protocol so use the value directly, |
288 | * but mask out any additions on future devices. |
289 | */ |
290 | val &= GUD_ROTATION_MASK; |
291 | ret = drm_plane_create_rotation_property(plane: &gdrm->pipe.plane, |
292 | DRM_MODE_ROTATE_0, supported_rotations: val); |
293 | break; |
294 | default: |
295 | /* New ones might show up in future devices, skip those we don't know. */ |
296 | drm_dbg(&gdrm->drm, "Ignoring unknown property: %u\n" , prop); |
297 | continue; |
298 | } |
299 | |
300 | if (ret) |
301 | goto out; |
302 | |
303 | gdrm->properties[gdrm->num_properties++] = prop; |
304 | } |
305 | out: |
306 | kfree(objp: properties); |
307 | |
308 | return ret; |
309 | } |
310 | |
311 | /* |
312 | * FIXME: Dma-buf sharing requires DMA support by the importing device. |
313 | * This function is a workaround to make USB devices work as well. |
314 | * See todo.rst for how to fix the issue in the dma-buf framework. |
315 | */ |
316 | static struct drm_gem_object *gud_gem_prime_import(struct drm_device *drm, struct dma_buf *dma_buf) |
317 | { |
318 | struct gud_device *gdrm = to_gud_device(drm); |
319 | |
320 | if (!gdrm->dmadev) |
321 | return ERR_PTR(error: -ENODEV); |
322 | |
323 | return drm_gem_prime_import_dev(dev: drm, dma_buf, attach_dev: gdrm->dmadev); |
324 | } |
325 | |
326 | static int gud_stats_debugfs(struct seq_file *m, void *data) |
327 | { |
328 | struct drm_debugfs_entry *entry = m->private; |
329 | struct gud_device *gdrm = to_gud_device(drm: entry->dev); |
330 | char buf[10]; |
331 | |
332 | string_get_size(size: gdrm->bulk_len, blk_size: 1, units: STRING_UNITS_2, buf, len: sizeof(buf)); |
333 | seq_printf(m, fmt: "Max buffer size: %s\n" , buf); |
334 | seq_printf(m, fmt: "Number of errors: %u\n" , gdrm->stats_num_errors); |
335 | |
336 | seq_puts(m, s: "Compression: " ); |
337 | if (gdrm->compression & GUD_COMPRESSION_LZ4) |
338 | seq_puts(m, s: " lz4" ); |
339 | if (!gdrm->compression) |
340 | seq_puts(m, s: " none" ); |
341 | seq_puts(m, s: "\n" ); |
342 | |
343 | if (gdrm->compression) { |
344 | u64 remainder; |
345 | u64 ratio = div64_u64_rem(dividend: gdrm->stats_length, divisor: gdrm->stats_actual_length, |
346 | remainder: &remainder); |
347 | u64 ratio_frac = div64_u64(dividend: remainder * 10, divisor: gdrm->stats_actual_length); |
348 | |
349 | seq_printf(m, fmt: "Compression ratio: %llu.%llu\n" , ratio, ratio_frac); |
350 | } |
351 | |
352 | return 0; |
353 | } |
354 | |
355 | static const struct drm_simple_display_pipe_funcs gud_pipe_funcs = { |
356 | .check = gud_pipe_check, |
357 | .update = gud_pipe_update, |
358 | DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS |
359 | }; |
360 | |
361 | static const struct drm_mode_config_funcs gud_mode_config_funcs = { |
362 | .fb_create = drm_gem_fb_create_with_dirty, |
363 | .atomic_check = drm_atomic_helper_check, |
364 | .atomic_commit = drm_atomic_helper_commit, |
365 | }; |
366 | |
367 | static const u64 gud_pipe_modifiers[] = { |
368 | DRM_FORMAT_MOD_LINEAR, |
369 | DRM_FORMAT_MOD_INVALID |
370 | }; |
371 | |
372 | DEFINE_DRM_GEM_FOPS(gud_fops); |
373 | |
374 | static const struct drm_driver gud_drm_driver = { |
375 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, |
376 | .fops = &gud_fops, |
377 | DRM_GEM_SHMEM_DRIVER_OPS, |
378 | .gem_prime_import = gud_gem_prime_import, |
379 | |
380 | .name = "gud" , |
381 | .desc = "Generic USB Display" , |
382 | .date = "20200422" , |
383 | .major = 1, |
384 | .minor = 0, |
385 | }; |
386 | |
387 | static int gud_alloc_bulk_buffer(struct gud_device *gdrm) |
388 | { |
389 | unsigned int i, num_pages; |
390 | struct page **pages; |
391 | void *ptr; |
392 | int ret; |
393 | |
394 | gdrm->bulk_buf = vmalloc_32(size: gdrm->bulk_len); |
395 | if (!gdrm->bulk_buf) |
396 | return -ENOMEM; |
397 | |
398 | num_pages = DIV_ROUND_UP(gdrm->bulk_len, PAGE_SIZE); |
399 | pages = kmalloc_array(n: num_pages, size: sizeof(struct page *), GFP_KERNEL); |
400 | if (!pages) |
401 | return -ENOMEM; |
402 | |
403 | for (i = 0, ptr = gdrm->bulk_buf; i < num_pages; i++, ptr += PAGE_SIZE) |
404 | pages[i] = vmalloc_to_page(addr: ptr); |
405 | |
406 | ret = sg_alloc_table_from_pages(sgt: &gdrm->bulk_sgt, pages, n_pages: num_pages, |
407 | offset: 0, size: gdrm->bulk_len, GFP_KERNEL); |
408 | kfree(objp: pages); |
409 | |
410 | return ret; |
411 | } |
412 | |
413 | static void gud_free_buffers_and_mutex(void *data) |
414 | { |
415 | struct gud_device *gdrm = data; |
416 | |
417 | vfree(addr: gdrm->compress_buf); |
418 | gdrm->compress_buf = NULL; |
419 | sg_free_table(&gdrm->bulk_sgt); |
420 | vfree(addr: gdrm->bulk_buf); |
421 | gdrm->bulk_buf = NULL; |
422 | mutex_destroy(lock: &gdrm->ctrl_lock); |
423 | } |
424 | |
425 | static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id) |
426 | { |
427 | const struct drm_format_info *xrgb8888_emulation_format = NULL; |
428 | bool rgb565_supported = false, xrgb8888_supported = false; |
429 | unsigned int num_formats_dev, num_formats = 0; |
430 | struct usb_endpoint_descriptor *bulk_out; |
431 | struct gud_display_descriptor_req desc; |
432 | struct device *dev = &intf->dev; |
433 | size_t max_buffer_size = 0; |
434 | struct gud_device *gdrm; |
435 | struct drm_device *drm; |
436 | u8 *formats_dev; |
437 | u32 *formats; |
438 | int ret, i; |
439 | |
440 | ret = usb_find_bulk_out_endpoint(alt: intf->cur_altsetting, bulk_out: &bulk_out); |
441 | if (ret) |
442 | return ret; |
443 | |
444 | ret = gud_get_display_descriptor(intf, desc: &desc); |
445 | if (ret) { |
446 | DRM_DEV_DEBUG_DRIVER(dev, "Not a display interface: ret=%d\n" , ret); |
447 | return -ENODEV; |
448 | } |
449 | |
450 | if (desc.version > 1) { |
451 | dev_err(dev, "Protocol version %u is not supported\n" , desc.version); |
452 | return -ENODEV; |
453 | } |
454 | |
455 | gdrm = devm_drm_dev_alloc(dev, &gud_drm_driver, struct gud_device, drm); |
456 | if (IS_ERR(ptr: gdrm)) |
457 | return PTR_ERR(ptr: gdrm); |
458 | |
459 | drm = &gdrm->drm; |
460 | drm->mode_config.funcs = &gud_mode_config_funcs; |
461 | ret = drmm_mode_config_init(dev: drm); |
462 | if (ret) |
463 | return ret; |
464 | |
465 | gdrm->flags = le32_to_cpu(desc.flags); |
466 | gdrm->compression = desc.compression & GUD_COMPRESSION_LZ4; |
467 | |
468 | if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE && gdrm->compression) |
469 | return -EINVAL; |
470 | |
471 | mutex_init(&gdrm->ctrl_lock); |
472 | mutex_init(&gdrm->damage_lock); |
473 | INIT_WORK(&gdrm->work, gud_flush_work); |
474 | gud_clear_damage(gdrm); |
475 | |
476 | ret = devm_add_action(dev, gud_free_buffers_and_mutex, gdrm); |
477 | if (ret) |
478 | return ret; |
479 | |
480 | drm->mode_config.min_width = le32_to_cpu(desc.min_width); |
481 | drm->mode_config.max_width = le32_to_cpu(desc.max_width); |
482 | drm->mode_config.min_height = le32_to_cpu(desc.min_height); |
483 | drm->mode_config.max_height = le32_to_cpu(desc.max_height); |
484 | |
485 | formats_dev = devm_kmalloc(dev, GUD_FORMATS_MAX_NUM, GFP_KERNEL); |
486 | /* Add room for emulated XRGB8888 */ |
487 | formats = devm_kmalloc_array(dev, GUD_FORMATS_MAX_NUM + 1, size: sizeof(*formats), GFP_KERNEL); |
488 | if (!formats_dev || !formats) |
489 | return -ENOMEM; |
490 | |
491 | ret = gud_usb_get(gdrm, GUD_REQ_GET_FORMATS, index: 0, buf: formats_dev, GUD_FORMATS_MAX_NUM); |
492 | if (ret < 0) |
493 | return ret; |
494 | |
495 | num_formats_dev = ret; |
496 | for (i = 0; i < num_formats_dev; i++) { |
497 | const struct drm_format_info *info; |
498 | size_t fmt_buf_size; |
499 | u32 format; |
500 | |
501 | format = gud_to_fourcc(format: formats_dev[i]); |
502 | if (!format) { |
503 | drm_dbg(drm, "Unsupported format: 0x%02x\n" , formats_dev[i]); |
504 | continue; |
505 | } |
506 | |
507 | if (format == GUD_DRM_FORMAT_R1) |
508 | info = &gud_drm_format_r1; |
509 | else if (format == GUD_DRM_FORMAT_XRGB1111) |
510 | info = &gud_drm_format_xrgb1111; |
511 | else |
512 | info = drm_format_info(format); |
513 | |
514 | switch (format) { |
515 | case GUD_DRM_FORMAT_R1: |
516 | fallthrough; |
517 | case DRM_FORMAT_R8: |
518 | fallthrough; |
519 | case GUD_DRM_FORMAT_XRGB1111: |
520 | fallthrough; |
521 | case DRM_FORMAT_RGB332: |
522 | fallthrough; |
523 | case DRM_FORMAT_RGB888: |
524 | if (!xrgb8888_emulation_format) |
525 | xrgb8888_emulation_format = info; |
526 | break; |
527 | case DRM_FORMAT_RGB565: |
528 | rgb565_supported = true; |
529 | if (!xrgb8888_emulation_format) |
530 | xrgb8888_emulation_format = info; |
531 | break; |
532 | case DRM_FORMAT_XRGB8888: |
533 | xrgb8888_supported = true; |
534 | break; |
535 | } |
536 | |
537 | fmt_buf_size = drm_format_info_min_pitch(info, plane: 0, buffer_width: drm->mode_config.max_width) * |
538 | drm->mode_config.max_height; |
539 | max_buffer_size = max(max_buffer_size, fmt_buf_size); |
540 | |
541 | if (format == GUD_DRM_FORMAT_R1 || format == GUD_DRM_FORMAT_XRGB1111) |
542 | continue; /* Internal not for userspace */ |
543 | |
544 | formats[num_formats++] = format; |
545 | } |
546 | |
547 | if (!num_formats && !xrgb8888_emulation_format) { |
548 | dev_err(dev, "No supported pixel formats found\n" ); |
549 | return -EINVAL; |
550 | } |
551 | |
552 | /* Prefer speed over color depth */ |
553 | if (rgb565_supported) |
554 | drm->mode_config.preferred_depth = 16; |
555 | |
556 | if (!xrgb8888_supported && xrgb8888_emulation_format) { |
557 | gdrm->xrgb8888_emulation_format = xrgb8888_emulation_format; |
558 | formats[num_formats++] = DRM_FORMAT_XRGB8888; |
559 | } |
560 | |
561 | if (desc.max_buffer_size) |
562 | max_buffer_size = le32_to_cpu(desc.max_buffer_size); |
563 | /* Prevent a misbehaving device from allocating loads of RAM. 4096x4096@XRGB8888 = 64 MB */ |
564 | if (max_buffer_size > SZ_64M) |
565 | max_buffer_size = SZ_64M; |
566 | |
567 | gdrm->bulk_pipe = usb_sndbulkpipe(interface_to_usbdev(intf), usb_endpoint_num(bulk_out)); |
568 | gdrm->bulk_len = max_buffer_size; |
569 | |
570 | ret = gud_alloc_bulk_buffer(gdrm); |
571 | if (ret) |
572 | return ret; |
573 | |
574 | if (gdrm->compression & GUD_COMPRESSION_LZ4) { |
575 | gdrm->lz4_comp_mem = devm_kmalloc(dev, LZ4_MEM_COMPRESS, GFP_KERNEL); |
576 | if (!gdrm->lz4_comp_mem) |
577 | return -ENOMEM; |
578 | |
579 | gdrm->compress_buf = vmalloc(size: gdrm->bulk_len); |
580 | if (!gdrm->compress_buf) |
581 | return -ENOMEM; |
582 | } |
583 | |
584 | ret = drm_simple_display_pipe_init(dev: drm, pipe: &gdrm->pipe, funcs: &gud_pipe_funcs, |
585 | formats, format_count: num_formats, |
586 | format_modifiers: gud_pipe_modifiers, NULL); |
587 | if (ret) |
588 | return ret; |
589 | |
590 | devm_kfree(dev, p: formats); |
591 | devm_kfree(dev, p: formats_dev); |
592 | |
593 | ret = gud_get_properties(gdrm); |
594 | if (ret) { |
595 | dev_err(dev, "Failed to get properties (error=%d)\n" , ret); |
596 | return ret; |
597 | } |
598 | |
599 | drm_plane_enable_fb_damage_clips(plane: &gdrm->pipe.plane); |
600 | |
601 | ret = gud_get_connectors(gdrm); |
602 | if (ret) { |
603 | dev_err(dev, "Failed to get connectors (error=%d)\n" , ret); |
604 | return ret; |
605 | } |
606 | |
607 | drm_mode_config_reset(dev: drm); |
608 | |
609 | usb_set_intfdata(intf, data: gdrm); |
610 | |
611 | gdrm->dmadev = usb_intf_get_dma_device(intf); |
612 | if (!gdrm->dmadev) |
613 | dev_warn(dev, "buffer sharing not supported" ); |
614 | |
615 | drm_debugfs_add_file(dev: drm, name: "stats" , show: gud_stats_debugfs, NULL); |
616 | |
617 | ret = drm_dev_register(dev: drm, flags: 0); |
618 | if (ret) { |
619 | put_device(dev: gdrm->dmadev); |
620 | return ret; |
621 | } |
622 | |
623 | drm_kms_helper_poll_init(dev: drm); |
624 | |
625 | drm_fbdev_generic_setup(dev: drm, preferred_bpp: 0); |
626 | |
627 | return 0; |
628 | } |
629 | |
630 | static void gud_disconnect(struct usb_interface *interface) |
631 | { |
632 | struct gud_device *gdrm = usb_get_intfdata(intf: interface); |
633 | struct drm_device *drm = &gdrm->drm; |
634 | |
635 | drm_dbg(drm, "%s:\n" , __func__); |
636 | |
637 | drm_kms_helper_poll_fini(dev: drm); |
638 | drm_dev_unplug(dev: drm); |
639 | drm_atomic_helper_shutdown(dev: drm); |
640 | put_device(dev: gdrm->dmadev); |
641 | gdrm->dmadev = NULL; |
642 | } |
643 | |
644 | static int gud_suspend(struct usb_interface *intf, pm_message_t message) |
645 | { |
646 | struct gud_device *gdrm = usb_get_intfdata(intf); |
647 | |
648 | return drm_mode_config_helper_suspend(dev: &gdrm->drm); |
649 | } |
650 | |
651 | static int gud_resume(struct usb_interface *intf) |
652 | { |
653 | struct gud_device *gdrm = usb_get_intfdata(intf); |
654 | |
655 | drm_mode_config_helper_resume(dev: &gdrm->drm); |
656 | |
657 | return 0; |
658 | } |
659 | |
660 | static const struct usb_device_id gud_id_table[] = { |
661 | { USB_DEVICE_INTERFACE_CLASS(0x1d50, 0x614d, USB_CLASS_VENDOR_SPEC) }, |
662 | { USB_DEVICE_INTERFACE_CLASS(0x16d0, 0x10a9, USB_CLASS_VENDOR_SPEC) }, |
663 | { } |
664 | }; |
665 | |
666 | MODULE_DEVICE_TABLE(usb, gud_id_table); |
667 | |
668 | static struct usb_driver gud_usb_driver = { |
669 | .name = "gud" , |
670 | .probe = gud_probe, |
671 | .disconnect = gud_disconnect, |
672 | .id_table = gud_id_table, |
673 | .suspend = gud_suspend, |
674 | .resume = gud_resume, |
675 | .reset_resume = gud_resume, |
676 | }; |
677 | |
678 | module_usb_driver(gud_usb_driver); |
679 | |
680 | MODULE_AUTHOR("Noralf Trønnes" ); |
681 | MODULE_LICENSE("Dual MIT/GPL" ); |
682 | |