Commit | Line | Data |
---|---|---|
5320918b DA |
1 | /* |
2 | * Copyright (C) 2012 Red Hat | |
3 | * | |
4 | * based in parts on udlfb.c: | |
5 | * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> | |
6 | * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> | |
7 | * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> | |
8 | * | |
9 | * This file is subject to the terms and conditions of the GNU General Public | |
10 | * License v2. See the file COPYING in the main directory of this archive for | |
11 | * more details. | |
12 | */ | |
760285e7 | 13 | #include <drm/drmP.h> |
5320918b DA |
14 | #include "udl_drv.h" |
15 | ||
16 | /* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */ | |
17 | #define BULK_SIZE 512 | |
18 | ||
d1c151dc JL |
19 | #define NR_USB_REQUEST_CHANNEL 0x12 |
20 | ||
5320918b DA |
21 | #define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE) |
22 | #define WRITES_IN_FLIGHT (4) | |
23 | #define MAX_VENDOR_DESCRIPTOR_SIZE 256 | |
24 | ||
25 | #define GET_URB_TIMEOUT HZ | |
26 | #define FREE_URB_TIMEOUT (HZ*2) | |
27 | ||
28 | static int udl_parse_vendor_descriptor(struct drm_device *dev, | |
29 | struct usb_device *usbdev) | |
30 | { | |
31 | struct udl_device *udl = dev->dev_private; | |
32 | char *desc; | |
33 | char *buf; | |
34 | char *desc_end; | |
35 | ||
36 | u8 total_len = 0; | |
37 | ||
38 | buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL); | |
39 | if (!buf) | |
40 | return false; | |
41 | desc = buf; | |
42 | ||
43 | total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */ | |
44 | 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); | |
45 | if (total_len > 5) { | |
08fcd72b AS |
46 | DRM_INFO("vendor descriptor length:%x data:%11ph\n", |
47 | total_len, desc); | |
5320918b DA |
48 | |
49 | if ((desc[0] != total_len) || /* descriptor length */ | |
50 | (desc[1] != 0x5f) || /* vendor descriptor type */ | |
51 | (desc[2] != 0x01) || /* version (2 bytes) */ | |
52 | (desc[3] != 0x00) || | |
53 | (desc[4] != total_len - 2)) /* length after type */ | |
54 | goto unrecognized; | |
55 | ||
56 | desc_end = desc + total_len; | |
57 | desc += 5; /* the fixed header we've already parsed */ | |
58 | ||
59 | while (desc < desc_end) { | |
60 | u8 length; | |
61 | u16 key; | |
62 | ||
d42f0349 | 63 | key = le16_to_cpu(*((u16 *) desc)); |
5320918b DA |
64 | desc += sizeof(u16); |
65 | length = *desc; | |
66 | desc++; | |
67 | ||
68 | switch (key) { | |
69 | case 0x0200: { /* max_area */ | |
70 | u32 max_area; | |
71 | max_area = le32_to_cpu(*((u32 *)desc)); | |
72 | DRM_DEBUG("DL chip limited to %d pixel modes\n", | |
73 | max_area); | |
74 | udl->sku_pixel_limit = max_area; | |
75 | break; | |
76 | } | |
77 | default: | |
78 | break; | |
79 | } | |
80 | desc += length; | |
81 | } | |
82 | } | |
83 | ||
84 | goto success; | |
85 | ||
86 | unrecognized: | |
87 | /* allow udlfb to load for now even if firmware unrecognized */ | |
88 | DRM_ERROR("Unrecognized vendor firmware descriptor\n"); | |
89 | ||
90 | success: | |
91 | kfree(buf); | |
92 | return true; | |
93 | } | |
94 | ||
d1c151dc JL |
95 | /* |
96 | * Need to ensure a channel is selected before submitting URBs | |
97 | */ | |
98 | static int udl_select_std_channel(struct udl_device *udl) | |
99 | { | |
100 | int ret; | |
101 | u8 set_def_chn[] = {0x57, 0xCD, 0xDC, 0xA7, | |
102 | 0x1C, 0x88, 0x5E, 0x15, | |
103 | 0x60, 0xFE, 0xC6, 0x97, | |
104 | 0x16, 0x3D, 0x47, 0xF2}; | |
105 | ||
106 | ret = usb_control_msg(udl->udev, | |
107 | usb_sndctrlpipe(udl->udev, 0), | |
108 | NR_USB_REQUEST_CHANNEL, | |
109 | (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, | |
110 | set_def_chn, sizeof(set_def_chn), | |
111 | USB_CTRL_SET_TIMEOUT); | |
112 | return ret < 0 ? ret : 0; | |
113 | } | |
114 | ||
5320918b DA |
115 | static void udl_release_urb_work(struct work_struct *work) |
116 | { | |
117 | struct urb_node *unode = container_of(work, struct urb_node, | |
118 | release_urb_work.work); | |
119 | ||
120 | up(&unode->dev->urbs.limit_sem); | |
121 | } | |
122 | ||
123 | void udl_urb_completion(struct urb *urb) | |
124 | { | |
125 | struct urb_node *unode = urb->context; | |
126 | struct udl_device *udl = unode->dev; | |
127 | unsigned long flags; | |
128 | ||
129 | /* sync/async unlink faults aren't errors */ | |
130 | if (urb->status) { | |
131 | if (!(urb->status == -ENOENT || | |
132 | urb->status == -ECONNRESET || | |
133 | urb->status == -ESHUTDOWN)) { | |
134 | DRM_ERROR("%s - nonzero write bulk status received: %d\n", | |
135 | __func__, urb->status); | |
136 | atomic_set(&udl->lost_pixels, 1); | |
137 | } | |
138 | } | |
139 | ||
140 | urb->transfer_buffer_length = udl->urbs.size; /* reset to actual */ | |
141 | ||
142 | spin_lock_irqsave(&udl->urbs.lock, flags); | |
143 | list_add_tail(&unode->entry, &udl->urbs.list); | |
144 | udl->urbs.available++; | |
145 | spin_unlock_irqrestore(&udl->urbs.lock, flags); | |
146 | ||
147 | #if 0 | |
148 | /* | |
149 | * When using fb_defio, we deadlock if up() is called | |
150 | * while another is waiting. So queue to another process. | |
151 | */ | |
152 | if (fb_defio) | |
153 | schedule_delayed_work(&unode->release_urb_work, 0); | |
154 | else | |
155 | #endif | |
156 | up(&udl->urbs.limit_sem); | |
157 | } | |
158 | ||
159 | static void udl_free_urb_list(struct drm_device *dev) | |
160 | { | |
161 | struct udl_device *udl = dev->dev_private; | |
162 | int count = udl->urbs.count; | |
163 | struct list_head *node; | |
164 | struct urb_node *unode; | |
165 | struct urb *urb; | |
166 | int ret; | |
167 | unsigned long flags; | |
168 | ||
169 | DRM_DEBUG("Waiting for completes and freeing all render urbs\n"); | |
170 | ||
171 | /* keep waiting and freeing, until we've got 'em all */ | |
172 | while (count--) { | |
173 | ||
174 | /* Getting interrupted means a leak, but ok at shutdown*/ | |
175 | ret = down_interruptible(&udl->urbs.limit_sem); | |
176 | if (ret) | |
177 | break; | |
178 | ||
179 | spin_lock_irqsave(&udl->urbs.lock, flags); | |
180 | ||
181 | node = udl->urbs.list.next; /* have reserved one with sem */ | |
182 | list_del_init(node); | |
183 | ||
184 | spin_unlock_irqrestore(&udl->urbs.lock, flags); | |
185 | ||
186 | unode = list_entry(node, struct urb_node, entry); | |
187 | urb = unode->urb; | |
188 | ||
189 | /* Free each separately allocated piece */ | |
190 | usb_free_coherent(urb->dev, udl->urbs.size, | |
191 | urb->transfer_buffer, urb->transfer_dma); | |
192 | usb_free_urb(urb); | |
193 | kfree(node); | |
194 | } | |
195 | udl->urbs.count = 0; | |
196 | } | |
197 | ||
198 | static int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size) | |
199 | { | |
200 | struct udl_device *udl = dev->dev_private; | |
201 | int i = 0; | |
202 | struct urb *urb; | |
203 | struct urb_node *unode; | |
204 | char *buf; | |
205 | ||
206 | spin_lock_init(&udl->urbs.lock); | |
207 | ||
208 | udl->urbs.size = size; | |
209 | INIT_LIST_HEAD(&udl->urbs.list); | |
210 | ||
211 | while (i < count) { | |
212 | unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL); | |
213 | if (!unode) | |
214 | break; | |
215 | unode->dev = udl; | |
216 | ||
217 | INIT_DELAYED_WORK(&unode->release_urb_work, | |
218 | udl_release_urb_work); | |
219 | ||
220 | urb = usb_alloc_urb(0, GFP_KERNEL); | |
221 | if (!urb) { | |
222 | kfree(unode); | |
223 | break; | |
224 | } | |
225 | unode->urb = urb; | |
226 | ||
d4f68a75 | 227 | buf = usb_alloc_coherent(udl->udev, MAX_TRANSFER, GFP_KERNEL, |
5320918b DA |
228 | &urb->transfer_dma); |
229 | if (!buf) { | |
230 | kfree(unode); | |
231 | usb_free_urb(urb); | |
232 | break; | |
233 | } | |
234 | ||
235 | /* urb->transfer_buffer_length set to actual before submit */ | |
d4f68a75 | 236 | usb_fill_bulk_urb(urb, udl->udev, usb_sndbulkpipe(udl->udev, 1), |
5320918b DA |
237 | buf, size, udl_urb_completion, unode); |
238 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | |
239 | ||
240 | list_add_tail(&unode->entry, &udl->urbs.list); | |
241 | ||
242 | i++; | |
243 | } | |
244 | ||
245 | sema_init(&udl->urbs.limit_sem, i); | |
246 | udl->urbs.count = i; | |
247 | udl->urbs.available = i; | |
248 | ||
249 | DRM_DEBUG("allocated %d %d byte urbs\n", i, (int) size); | |
250 | ||
251 | return i; | |
252 | } | |
253 | ||
254 | struct urb *udl_get_urb(struct drm_device *dev) | |
255 | { | |
256 | struct udl_device *udl = dev->dev_private; | |
257 | int ret = 0; | |
258 | struct list_head *entry; | |
259 | struct urb_node *unode; | |
260 | struct urb *urb = NULL; | |
261 | unsigned long flags; | |
262 | ||
263 | /* Wait for an in-flight buffer to complete and get re-queued */ | |
264 | ret = down_timeout(&udl->urbs.limit_sem, GET_URB_TIMEOUT); | |
265 | if (ret) { | |
266 | atomic_set(&udl->lost_pixels, 1); | |
267 | DRM_INFO("wait for urb interrupted: %x available: %d\n", | |
268 | ret, udl->urbs.available); | |
269 | goto error; | |
270 | } | |
271 | ||
272 | spin_lock_irqsave(&udl->urbs.lock, flags); | |
273 | ||
274 | BUG_ON(list_empty(&udl->urbs.list)); /* reserved one with limit_sem */ | |
275 | entry = udl->urbs.list.next; | |
276 | list_del_init(entry); | |
277 | udl->urbs.available--; | |
278 | ||
279 | spin_unlock_irqrestore(&udl->urbs.lock, flags); | |
280 | ||
281 | unode = list_entry(entry, struct urb_node, entry); | |
282 | urb = unode->urb; | |
283 | ||
284 | error: | |
285 | return urb; | |
286 | } | |
287 | ||
288 | int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len) | |
289 | { | |
290 | struct udl_device *udl = dev->dev_private; | |
291 | int ret; | |
292 | ||
293 | BUG_ON(len > udl->urbs.size); | |
294 | ||
295 | urb->transfer_buffer_length = len; /* set to actual payload len */ | |
296 | ret = usb_submit_urb(urb, GFP_ATOMIC); | |
297 | if (ret) { | |
298 | udl_urb_completion(urb); /* because no one else will */ | |
299 | atomic_set(&udl->lost_pixels, 1); | |
300 | DRM_ERROR("usb_submit_urb error %x\n", ret); | |
301 | } | |
302 | return ret; | |
303 | } | |
304 | ||
305 | int udl_driver_load(struct drm_device *dev, unsigned long flags) | |
306 | { | |
d4f68a75 | 307 | struct usb_device *udev = (void*)flags; |
5320918b | 308 | struct udl_device *udl; |
737583f0 | 309 | int ret = -ENOMEM; |
5320918b DA |
310 | |
311 | DRM_DEBUG("\n"); | |
312 | udl = kzalloc(sizeof(struct udl_device), GFP_KERNEL); | |
313 | if (!udl) | |
314 | return -ENOMEM; | |
315 | ||
d4f68a75 | 316 | udl->udev = udev; |
5320918b DA |
317 | udl->ddev = dev; |
318 | dev->dev_private = udl; | |
319 | ||
d4f68a75 | 320 | if (!udl_parse_vendor_descriptor(dev, udl->udev)) { |
e39a52da | 321 | ret = -ENODEV; |
5320918b DA |
322 | DRM_ERROR("firmware not recognized. Assume incompatible device\n"); |
323 | goto err; | |
324 | } | |
325 | ||
d1c151dc JL |
326 | if (udl_select_std_channel(udl)) |
327 | DRM_ERROR("Selecting channel failed\n"); | |
328 | ||
5320918b | 329 | if (!udl_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { |
5320918b DA |
330 | DRM_ERROR("udl_alloc_urb_list failed\n"); |
331 | goto err; | |
332 | } | |
333 | ||
334 | DRM_DEBUG("\n"); | |
335 | ret = udl_modeset_init(dev); | |
26507b06 SM |
336 | if (ret) |
337 | goto err; | |
5320918b DA |
338 | |
339 | ret = udl_fbdev_init(dev); | |
26507b06 SM |
340 | if (ret) |
341 | goto err; | |
342 | ||
343 | ret = drm_vblank_init(dev, 1); | |
344 | if (ret) | |
345 | goto err_fb; | |
346 | ||
5320918b | 347 | return 0; |
26507b06 SM |
348 | err_fb: |
349 | udl_fbdev_cleanup(dev); | |
5320918b | 350 | err: |
26507b06 SM |
351 | if (udl->urbs.count) |
352 | udl_free_urb_list(dev); | |
5320918b DA |
353 | kfree(udl); |
354 | DRM_ERROR("%d\n", ret); | |
355 | return ret; | |
356 | } | |
357 | ||
358 | int udl_drop_usb(struct drm_device *dev) | |
359 | { | |
360 | udl_free_urb_list(dev); | |
361 | return 0; | |
362 | } | |
363 | ||
364 | int udl_driver_unload(struct drm_device *dev) | |
365 | { | |
366 | struct udl_device *udl = dev->dev_private; | |
367 | ||
26507b06 SM |
368 | drm_vblank_cleanup(dev); |
369 | ||
5320918b DA |
370 | if (udl->urbs.count) |
371 | udl_free_urb_list(dev); | |
372 | ||
373 | udl_fbdev_cleanup(dev); | |
374 | udl_modeset_cleanup(dev); | |
375 | kfree(udl); | |
376 | return 0; | |
377 | } |