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