xref: /openbmc/linux/sound/usb/line6/podhd.c (revision 24f7989e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Line 6 Pod HD
4  *
5  * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
6  * Copyright (C) 2015 Andrej Krutak <dev@andree.sk>
7  * Copyright (C) 2017 Hans P. Moller <hmoller@uc.cl>
8  */
9 
10 #include <linux/usb.h>
11 #include <linux/slab.h>
12 #include <linux/module.h>
13 #include <sound/core.h>
14 #include <sound/control.h>
15 #include <sound/pcm.h>
16 
17 #include "driver.h"
18 #include "pcm.h"
19 
20 #define PODHD_STARTUP_DELAY 500
21 
22 enum {
23 	LINE6_PODHD300,
24 	LINE6_PODHD400,
25 	LINE6_PODHD500,
26 	LINE6_PODX3,
27 	LINE6_PODX3LIVE,
28 	LINE6_PODHD500X,
29 	LINE6_PODHDDESKTOP
30 };
31 
32 struct usb_line6_podhd {
33 	/* Generic Line 6 USB data */
34 	struct usb_line6 line6;
35 
36 	/* Serial number of device */
37 	u32 serial_number;
38 
39 	/* Firmware version */
40 	int firmware_version;
41 
42 	/* Monitor level */
43 	int monitor_level;
44 };
45 
46 #define line6_to_podhd(x)	container_of(x, struct usb_line6_podhd, line6)
47 
48 static const struct snd_ratden podhd_ratden = {
49 	.num_min = 48000,
50 	.num_max = 48000,
51 	.num_step = 1,
52 	.den = 1,
53 };
54 
55 static struct line6_pcm_properties podhd_pcm_properties = {
56 	.playback_hw = {
57 				  .info = (SNDRV_PCM_INFO_MMAP |
58 					   SNDRV_PCM_INFO_INTERLEAVED |
59 					   SNDRV_PCM_INFO_BLOCK_TRANSFER |
60 					   SNDRV_PCM_INFO_MMAP_VALID |
61 					   SNDRV_PCM_INFO_PAUSE |
62 					   SNDRV_PCM_INFO_SYNC_START),
63 				  .formats = SNDRV_PCM_FMTBIT_S24_3LE,
64 				  .rates = SNDRV_PCM_RATE_48000,
65 				  .rate_min = 48000,
66 				  .rate_max = 48000,
67 				  .channels_min = 2,
68 				  .channels_max = 2,
69 				  .buffer_bytes_max = 60000,
70 				  .period_bytes_min = 64,
71 				  .period_bytes_max = 8192,
72 				  .periods_min = 1,
73 				  .periods_max = 1024},
74 	.capture_hw = {
75 				 .info = (SNDRV_PCM_INFO_MMAP |
76 					  SNDRV_PCM_INFO_INTERLEAVED |
77 					  SNDRV_PCM_INFO_BLOCK_TRANSFER |
78 					  SNDRV_PCM_INFO_MMAP_VALID |
79 					  SNDRV_PCM_INFO_SYNC_START),
80 				 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
81 				 .rates = SNDRV_PCM_RATE_48000,
82 				 .rate_min = 48000,
83 				 .rate_max = 48000,
84 				 .channels_min = 2,
85 				 .channels_max = 2,
86 				 .buffer_bytes_max = 60000,
87 				 .period_bytes_min = 64,
88 				 .period_bytes_max = 8192,
89 				 .periods_min = 1,
90 				 .periods_max = 1024},
91 	.rates = {
92 			    .nrats = 1,
93 			    .rats = &podhd_ratden},
94 	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
95 };
96 
97 static struct line6_pcm_properties podx3_pcm_properties = {
98 	.playback_hw = {
99 				  .info = (SNDRV_PCM_INFO_MMAP |
100 					   SNDRV_PCM_INFO_INTERLEAVED |
101 					   SNDRV_PCM_INFO_BLOCK_TRANSFER |
102 					   SNDRV_PCM_INFO_MMAP_VALID |
103 					   SNDRV_PCM_INFO_PAUSE |
104 					   SNDRV_PCM_INFO_SYNC_START),
105 				  .formats = SNDRV_PCM_FMTBIT_S24_3LE,
106 				  .rates = SNDRV_PCM_RATE_48000,
107 				  .rate_min = 48000,
108 				  .rate_max = 48000,
109 				  .channels_min = 2,
110 				  .channels_max = 2,
111 				  .buffer_bytes_max = 60000,
112 				  .period_bytes_min = 64,
113 				  .period_bytes_max = 8192,
114 				  .periods_min = 1,
115 				  .periods_max = 1024},
116 	.capture_hw = {
117 				 .info = (SNDRV_PCM_INFO_MMAP |
118 					  SNDRV_PCM_INFO_INTERLEAVED |
119 					  SNDRV_PCM_INFO_BLOCK_TRANSFER |
120 					  SNDRV_PCM_INFO_MMAP_VALID |
121 					  SNDRV_PCM_INFO_SYNC_START),
122 				 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
123 				 .rates = SNDRV_PCM_RATE_48000,
124 				 .rate_min = 48000,
125 				 .rate_max = 48000,
126 				 /* 1+2: Main signal (out), 3+4: Tone 1,
127 				  * 5+6: Tone 2, 7+8: raw
128 				  */
129 				 .channels_min = 8,
130 				 .channels_max = 8,
131 				 .buffer_bytes_max = 60000,
132 				 .period_bytes_min = 64,
133 				 .period_bytes_max = 8192,
134 				 .periods_min = 1,
135 				 .periods_max = 1024},
136 	.rates = {
137 			    .nrats = 1,
138 			    .rats = &podhd_ratden},
139 	.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
140 };
141 static struct usb_driver podhd_driver;
142 
143 static ssize_t serial_number_show(struct device *dev,
144 				  struct device_attribute *attr, char *buf)
145 {
146 	struct snd_card *card = dev_to_snd_card(dev);
147 	struct usb_line6_podhd *pod = card->private_data;
148 
149 	return sysfs_emit(buf, "%u\n", pod->serial_number);
150 }
151 
152 static ssize_t firmware_version_show(struct device *dev,
153 				     struct device_attribute *attr, char *buf)
154 {
155 	struct snd_card *card = dev_to_snd_card(dev);
156 	struct usb_line6_podhd *pod = card->private_data;
157 
158 	return sysfs_emit(buf, "%06x\n", pod->firmware_version);
159 }
160 
161 static DEVICE_ATTR_RO(firmware_version);
162 static DEVICE_ATTR_RO(serial_number);
163 
164 static struct attribute *podhd_dev_attrs[] = {
165 	&dev_attr_firmware_version.attr,
166 	&dev_attr_serial_number.attr,
167 	NULL
168 };
169 
170 static const struct attribute_group podhd_dev_attr_group = {
171 	.name = "podhd",
172 	.attrs = podhd_dev_attrs,
173 };
174 
175 /*
176  * POD X3 startup procedure.
177  *
178  * May be compatible with other POD HD's, since it's also similar to the
179  * previous POD setup. In any case, it doesn't seem to be required for the
180  * audio nor bulk interfaces to work.
181  */
182 
183 static int podhd_dev_start(struct usb_line6_podhd *pod)
184 {
185 	int ret;
186 	u8 init_bytes[8];
187 	int i;
188 	struct usb_device *usbdev = pod->line6.usbdev;
189 
190 	ret = usb_control_msg_send(usbdev, 0,
191 					0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
192 					0x11, 0,
193 					NULL, 0, LINE6_TIMEOUT, GFP_KERNEL);
194 	if (ret) {
195 		dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
196 		goto exit;
197 	}
198 
199 	/* NOTE: looks like some kind of ping message */
200 	ret = usb_control_msg_recv(usbdev, 0, 0x67,
201 					USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
202 					0x11, 0x0,
203 					init_bytes, 3, LINE6_TIMEOUT, GFP_KERNEL);
204 	if (ret) {
205 		dev_err(pod->line6.ifcdev,
206 			"receive length failed (error %d)\n", ret);
207 		goto exit;
208 	}
209 
210 	pod->firmware_version =
211 		(init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0);
212 
213 	for (i = 0; i <= 16; i++) {
214 		ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
215 		if (ret < 0)
216 			goto exit;
217 	}
218 
219 	ret = usb_control_msg_send(usbdev, 0,
220 					USB_REQ_SET_FEATURE,
221 					USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
222 					1, 0,
223 					NULL, 0, LINE6_TIMEOUT, GFP_KERNEL);
224 exit:
225 	return ret;
226 }
227 
228 static void podhd_startup(struct usb_line6 *line6)
229 {
230 	struct usb_line6_podhd *pod = line6_to_podhd(line6);
231 
232 	podhd_dev_start(pod);
233 	line6_read_serial_number(&pod->line6, &pod->serial_number);
234 	if (snd_card_register(line6->card))
235 		dev_err(line6->ifcdev, "Failed to register POD HD card.\n");
236 }
237 
238 static void podhd_disconnect(struct usb_line6 *line6)
239 {
240 	struct usb_line6_podhd *pod = line6_to_podhd(line6);
241 
242 	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO) {
243 		struct usb_interface *intf;
244 
245 		intf = usb_ifnum_to_if(line6->usbdev,
246 					pod->line6.properties->ctrl_if);
247 		if (intf)
248 			usb_driver_release_interface(&podhd_driver, intf);
249 	}
250 }
251 
252 static const unsigned int float_zero_to_one_lookup[] = {
253 0x00000000, 0x3c23d70a, 0x3ca3d70a, 0x3cf5c28f, 0x3d23d70a, 0x3d4ccccd,
254 0x3d75c28f, 0x3d8f5c29, 0x3da3d70a, 0x3db851ec, 0x3dcccccd, 0x3de147ae,
255 0x3df5c28f, 0x3e051eb8, 0x3e0f5c29, 0x3e19999a, 0x3e23d70a, 0x3e2e147b,
256 0x3e3851ec, 0x3e428f5c, 0x3e4ccccd, 0x3e570a3d, 0x3e6147ae, 0x3e6b851f,
257 0x3e75c28f, 0x3e800000, 0x3e851eb8, 0x3e8a3d71, 0x3e8f5c29, 0x3e947ae1,
258 0x3e99999a, 0x3e9eb852, 0x3ea3d70a, 0x3ea8f5c3, 0x3eae147b, 0x3eb33333,
259 0x3eb851ec, 0x3ebd70a4, 0x3ec28f5c, 0x3ec7ae14, 0x3ecccccd, 0x3ed1eb85,
260 0x3ed70a3d, 0x3edc28f6, 0x3ee147ae, 0x3ee66666, 0x3eeb851f, 0x3ef0a3d7,
261 0x3ef5c28f, 0x3efae148, 0x3f000000, 0x3f028f5c, 0x3f051eb8, 0x3f07ae14,
262 0x3f0a3d71, 0x3f0ccccd, 0x3f0f5c29, 0x3f11eb85, 0x3f147ae1, 0x3f170a3d,
263 0x3f19999a, 0x3f1c28f6, 0x3f1eb852, 0x3f2147ae, 0x3f23d70a, 0x3f266666,
264 0x3f28f5c3, 0x3f2b851f, 0x3f2e147b, 0x3f30a3d7, 0x3f333333, 0x3f35c28f,
265 0x3f3851ec, 0x3f3ae148, 0x3f3d70a4, 0x3f400000, 0x3f428f5c, 0x3f451eb8,
266 0x3f47ae14, 0x3f4a3d71, 0x3f4ccccd, 0x3f4f5c29, 0x3f51eb85, 0x3f547ae1,
267 0x3f570a3d, 0x3f59999a, 0x3f5c28f6, 0x3f5eb852, 0x3f6147ae, 0x3f63d70a,
268 0x3f666666, 0x3f68f5c3, 0x3f6b851f, 0x3f6e147b, 0x3f70a3d7, 0x3f733333,
269 0x3f75c28f, 0x3f7851ec, 0x3f7ae148, 0x3f7d70a4, 0x3f800000
270 };
271 
272 static void podhd_set_monitor_level(struct usb_line6_podhd *podhd, int value)
273 {
274 	unsigned int fl;
275 	static const unsigned char msg[16] = {
276 		/* Chunk is 0xc bytes (without first word) */
277 		0x0c, 0x00,
278 		/* First chunk in the message */
279 		0x01, 0x00,
280 		/* Message size is 2 4-byte words */
281 		0x02, 0x00,
282 		/* Unknown */
283 		0x04, 0x41,
284 		/* Unknown */
285 		0x04, 0x00, 0x13, 0x00,
286 		/* Volume, LE float32, 0.0 - 1.0 */
287 		0x00, 0x00, 0x00, 0x00
288 	};
289 	unsigned char *buf;
290 
291 	buf = kmemdup(msg, sizeof(msg), GFP_KERNEL);
292 	if (!buf)
293 		return;
294 
295 	if (value < 0)
296 		value = 0;
297 
298 	if (value >= ARRAY_SIZE(float_zero_to_one_lookup))
299 		value = ARRAY_SIZE(float_zero_to_one_lookup) - 1;
300 
301 	fl = float_zero_to_one_lookup[value];
302 
303 	buf[12] = (fl >> 0) & 0xff;
304 	buf[13] = (fl >> 8) & 0xff;
305 	buf[14] = (fl >> 16) & 0xff;
306 	buf[15] = (fl >> 24) & 0xff;
307 
308 	line6_send_raw_message(&podhd->line6, buf, sizeof(msg));
309 	kfree(buf);
310 
311 	podhd->monitor_level = value;
312 }
313 
314 /* control info callback */
315 static int snd_podhd_control_monitor_info(struct snd_kcontrol *kcontrol,
316 					struct snd_ctl_elem_info *uinfo)
317 {
318 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
319 	uinfo->count = 1;
320 	uinfo->value.integer.min = 0;
321 	uinfo->value.integer.max = 100;
322 	uinfo->value.integer.step = 1;
323 	return 0;
324 }
325 
326 /* control get callback */
327 static int snd_podhd_control_monitor_get(struct snd_kcontrol *kcontrol,
328 				       struct snd_ctl_elem_value *ucontrol)
329 {
330 	struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
331 	struct usb_line6_podhd *podhd = line6_to_podhd(line6pcm->line6);
332 
333 	ucontrol->value.integer.value[0] = podhd->monitor_level;
334 	return 0;
335 }
336 
337 /* control put callback */
338 static int snd_podhd_control_monitor_put(struct snd_kcontrol *kcontrol,
339 				       struct snd_ctl_elem_value *ucontrol)
340 {
341 	struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
342 	struct usb_line6_podhd *podhd = line6_to_podhd(line6pcm->line6);
343 
344 	if (ucontrol->value.integer.value[0] == podhd->monitor_level)
345 		return 0;
346 
347 	podhd_set_monitor_level(podhd, ucontrol->value.integer.value[0]);
348 	return 1;
349 }
350 
351 /* control definition */
352 static const struct snd_kcontrol_new podhd_control_monitor = {
353 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
354 	.name = "Monitor Playback Volume",
355 	.index = 0,
356 	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
357 	.info = snd_podhd_control_monitor_info,
358 	.get = snd_podhd_control_monitor_get,
359 	.put = snd_podhd_control_monitor_put
360 };
361 
362 /*
363 	Try to init POD HD device.
364 */
365 static int podhd_init(struct usb_line6 *line6,
366 		      const struct usb_device_id *id)
367 {
368 	int err;
369 	struct usb_line6_podhd *pod = line6_to_podhd(line6);
370 	struct usb_interface *intf;
371 
372 	line6->disconnect = podhd_disconnect;
373 	line6->startup = podhd_startup;
374 
375 	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
376 		/* claim the data interface */
377 		intf = usb_ifnum_to_if(line6->usbdev,
378 					pod->line6.properties->ctrl_if);
379 		if (!intf) {
380 			dev_err(pod->line6.ifcdev, "interface %d not found\n",
381 				pod->line6.properties->ctrl_if);
382 			return -ENODEV;
383 		}
384 
385 		err = usb_driver_claim_interface(&podhd_driver, intf, NULL);
386 		if (err != 0) {
387 			dev_err(pod->line6.ifcdev, "can't claim interface %d, error %d\n",
388 				pod->line6.properties->ctrl_if, err);
389 			return err;
390 		}
391 	}
392 
393 	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO) {
394 		/* create sysfs entries: */
395 		err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
396 		if (err < 0)
397 			return err;
398 	}
399 
400 	if (pod->line6.properties->capabilities & LINE6_CAP_PCM) {
401 		/* initialize PCM subsystem: */
402 		err = line6_init_pcm(line6,
403 			(id->driver_info == LINE6_PODX3 ||
404 			id->driver_info == LINE6_PODX3LIVE) ? &podx3_pcm_properties :
405 			&podhd_pcm_properties);
406 		if (err < 0)
407 			return err;
408 	}
409 
410 	if (pod->line6.properties->capabilities & LINE6_CAP_HWMON_CTL) {
411 		podhd_set_monitor_level(pod, 100);
412 		err = snd_ctl_add(line6->card,
413 				  snd_ctl_new1(&podhd_control_monitor,
414 					       line6->line6pcm));
415 		if (err < 0)
416 			return err;
417 	}
418 
419 	if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO)) {
420 		/* register USB audio system directly */
421 		return snd_card_register(line6->card);
422 	}
423 
424 	/* init device and delay registering */
425 	schedule_delayed_work(&line6->startup_work,
426 			      msecs_to_jiffies(PODHD_STARTUP_DELAY));
427 	return 0;
428 }
429 
430 #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
431 #define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n)
432 
433 /* table of devices that work with this driver */
434 static const struct usb_device_id podhd_id_table[] = {
435 	/* TODO: no need to alloc data interfaces when only audio is used */
436 	{ LINE6_DEVICE(0x5057),    .driver_info = LINE6_PODHD300 },
437 	{ LINE6_DEVICE(0x5058),    .driver_info = LINE6_PODHD400 },
438 	{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500 },
439 	{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
440 	{ LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
441 	{ LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X },
442 	{ LINE6_IF_NUM(0x4156, 0), .driver_info = LINE6_PODHDDESKTOP },
443 	{}
444 };
445 
446 MODULE_DEVICE_TABLE(usb, podhd_id_table);
447 
448 static const struct line6_properties podhd_properties_table[] = {
449 	[LINE6_PODHD300] = {
450 		.id = "PODHD300",
451 		.name = "POD HD300",
452 		.capabilities	= LINE6_CAP_PCM
453 				| LINE6_CAP_HWMON,
454 		.altsetting = 5,
455 		.ep_ctrl_r = 0x84,
456 		.ep_ctrl_w = 0x03,
457 		.ep_audio_r = 0x82,
458 		.ep_audio_w = 0x01,
459 	},
460 	[LINE6_PODHD400] = {
461 		.id = "PODHD400",
462 		.name = "POD HD400",
463 		.capabilities	= LINE6_CAP_PCM
464 				| LINE6_CAP_HWMON,
465 		.altsetting = 5,
466 		.ep_ctrl_r = 0x84,
467 		.ep_ctrl_w = 0x03,
468 		.ep_audio_r = 0x82,
469 		.ep_audio_w = 0x01,
470 	},
471 	[LINE6_PODHD500] = {
472 		.id = "PODHD500",
473 		.name = "POD HD500",
474 		.capabilities	= LINE6_CAP_PCM | LINE6_CAP_CONTROL
475 				| LINE6_CAP_HWMON | LINE6_CAP_HWMON_CTL,
476 		.altsetting = 1,
477 		.ctrl_if = 1,
478 		.ep_ctrl_r = 0x81,
479 		.ep_ctrl_w = 0x01,
480 		.ep_audio_r = 0x86,
481 		.ep_audio_w = 0x02,
482 	},
483 	[LINE6_PODX3] = {
484 		.id = "PODX3",
485 		.name = "POD X3",
486 		.capabilities	= LINE6_CAP_CONTROL | LINE6_CAP_CONTROL_INFO
487 				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
488 		.altsetting = 1,
489 		.ep_ctrl_r = 0x81,
490 		.ep_ctrl_w = 0x01,
491 		.ctrl_if = 1,
492 		.ep_audio_r = 0x86,
493 		.ep_audio_w = 0x02,
494 	},
495 	[LINE6_PODX3LIVE] = {
496 		.id = "PODX3LIVE",
497 		.name = "POD X3 LIVE",
498 		.capabilities	= LINE6_CAP_CONTROL | LINE6_CAP_CONTROL_INFO
499 				| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
500 		.altsetting = 1,
501 		.ep_ctrl_r = 0x81,
502 		.ep_ctrl_w = 0x01,
503 		.ctrl_if = 1,
504 		.ep_audio_r = 0x86,
505 		.ep_audio_w = 0x02,
506 	},
507 	[LINE6_PODHD500X] = {
508 		.id = "PODHD500X",
509 		.name = "POD HD500X",
510 		.capabilities	= LINE6_CAP_CONTROL | LINE6_CAP_HWMON_CTL
511 				| LINE6_CAP_PCM | LINE6_CAP_HWMON,
512 		.altsetting = 1,
513 		.ep_ctrl_r = 0x81,
514 		.ep_ctrl_w = 0x01,
515 		.ctrl_if = 1,
516 		.ep_audio_r = 0x86,
517 		.ep_audio_w = 0x02,
518 	},
519 	[LINE6_PODHDDESKTOP] = {
520 		.id = "PODHDDESKTOP",
521 		.name = "POD HDDESKTOP",
522 		.capabilities    = LINE6_CAP_CONTROL
523 			| LINE6_CAP_PCM | LINE6_CAP_HWMON,
524 		.altsetting = 1,
525 		.ep_ctrl_r = 0x81,
526 		.ep_ctrl_w = 0x01,
527 		.ctrl_if = 1,
528 		.ep_audio_r = 0x86,
529 		.ep_audio_w = 0x02,
530 	},
531 };
532 
533 /*
534 	Probe USB device.
535 */
536 static int podhd_probe(struct usb_interface *interface,
537 		       const struct usb_device_id *id)
538 {
539 	return line6_probe(interface, id, "Line6-PODHD",
540 			   &podhd_properties_table[id->driver_info],
541 			   podhd_init, sizeof(struct usb_line6_podhd));
542 }
543 
544 static struct usb_driver podhd_driver = {
545 	.name = KBUILD_MODNAME,
546 	.probe = podhd_probe,
547 	.disconnect = line6_disconnect,
548 #ifdef CONFIG_PM
549 	.suspend = line6_suspend,
550 	.resume = line6_resume,
551 	.reset_resume = line6_resume,
552 #endif
553 	.id_table = podhd_id_table,
554 };
555 
556 module_usb_driver(podhd_driver);
557 
558 MODULE_DESCRIPTION("Line 6 PODHD USB driver");
559 MODULE_LICENSE("GPL");
560