blob: 8c4375bf34ab7a755dd2f5d9e798b472701f4ec3 [file] [log] [blame]
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +00001/*
Chris Rorvickc6fffce2015-01-20 02:20:49 -06002 * Line 6 Pod HD
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +00003 *
4 * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
Andrej Krutak790869d2016-09-18 20:59:28 +02005 * Copyright (C) 2015 Andrej Krutak <dev@andree.sk>
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +00006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, version 2.
10 *
11 */
12
Takashi Iwaiccddbe42015-01-15 08:22:31 +010013#include <linux/usb.h>
14#include <linux/slab.h>
15#include <linux/module.h>
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +000016#include <sound/core.h>
17#include <sound/pcm.h>
18
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +000019#include "driver.h"
20#include "pcm.h"
Takashi Iwaiccddbe42015-01-15 08:22:31 +010021
Andrej Krutak790869d2016-09-18 20:59:28 +020022#define PODHD_STARTUP_DELAY 500
23
24/*
25 * Stages of POD startup procedure
26 */
27enum {
28 PODHD_STARTUP_INIT = 1,
29 PODHD_STARTUP_SCHEDULE_WORKQUEUE,
30 PODHD_STARTUP_SETUP,
31 PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1
32};
33
Takashi Iwaiccddbe42015-01-15 08:22:31 +010034enum {
35 LINE6_PODHD300,
36 LINE6_PODHD400,
37 LINE6_PODHD500_0,
38 LINE6_PODHD500_1,
Andrej Krutak790869d2016-09-18 20:59:28 +020039 LINE6_PODX3,
Andrej Krutakc039aaa2016-09-18 20:59:29 +020040 LINE6_PODX3LIVE
Andrej Krutak790869d2016-09-18 20:59:28 +020041};
42
43struct usb_line6_podhd {
44 /* Generic Line 6 USB data */
45 struct usb_line6 line6;
46
47 /* Timer for device initialization */
48 struct timer_list startup_timer;
49
50 /* Work handler for device initialization */
51 struct work_struct startup_work;
52
53 /* Current progress in startup procedure */
54 int startup_progress;
55
56 /* Serial number of device */
57 u32 serial_number;
58
59 /* Firmware version */
60 int firmware_version;
Takashi Iwaiccddbe42015-01-15 08:22:31 +010061};
62
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +000063static struct snd_ratden podhd_ratden = {
64 .num_min = 48000,
65 .num_max = 48000,
66 .num_step = 1,
67 .den = 1,
68};
69
70static struct line6_pcm_properties podhd_pcm_properties = {
Takashi Iwai1263f612015-01-28 15:08:59 +010071 .playback_hw = {
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +000072 .info = (SNDRV_PCM_INFO_MMAP |
73 SNDRV_PCM_INFO_INTERLEAVED |
74 SNDRV_PCM_INFO_BLOCK_TRANSFER |
75 SNDRV_PCM_INFO_MMAP_VALID |
76 SNDRV_PCM_INFO_PAUSE |
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +000077 SNDRV_PCM_INFO_SYNC_START),
78 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
79 .rates = SNDRV_PCM_RATE_48000,
80 .rate_min = 48000,
81 .rate_max = 48000,
82 .channels_min = 2,
83 .channels_max = 2,
84 .buffer_bytes_max = 60000,
85 .period_bytes_min = 64,
86 .period_bytes_max = 8192,
87 .periods_min = 1,
88 .periods_max = 1024},
Takashi Iwai1263f612015-01-28 15:08:59 +010089 .capture_hw = {
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +000090 .info = (SNDRV_PCM_INFO_MMAP |
91 SNDRV_PCM_INFO_INTERLEAVED |
92 SNDRV_PCM_INFO_BLOCK_TRANSFER |
93 SNDRV_PCM_INFO_MMAP_VALID |
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +000094 SNDRV_PCM_INFO_SYNC_START),
95 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
96 .rates = SNDRV_PCM_RATE_48000,
97 .rate_min = 48000,
98 .rate_max = 48000,
99 .channels_min = 2,
100 .channels_max = 2,
101 .buffer_bytes_max = 60000,
102 .period_bytes_min = 64,
103 .period_bytes_max = 8192,
104 .periods_min = 1,
105 .periods_max = 1024},
Takashi Iwai1263f612015-01-28 15:08:59 +0100106 .rates = {
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +0000107 .nrats = 1,
108 .rats = &podhd_ratden},
Andrej Krutak790869d2016-09-18 20:59:28 +0200109 .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +0000110};
111
Andrej Krutak790869d2016-09-18 20:59:28 +0200112static struct line6_pcm_properties podx3_pcm_properties = {
113 .playback_hw = {
114 .info = (SNDRV_PCM_INFO_MMAP |
115 SNDRV_PCM_INFO_INTERLEAVED |
116 SNDRV_PCM_INFO_BLOCK_TRANSFER |
117 SNDRV_PCM_INFO_MMAP_VALID |
118 SNDRV_PCM_INFO_PAUSE |
119 SNDRV_PCM_INFO_SYNC_START),
120 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
121 .rates = SNDRV_PCM_RATE_48000,
122 .rate_min = 48000,
123 .rate_max = 48000,
124 .channels_min = 2,
125 .channels_max = 2,
126 .buffer_bytes_max = 60000,
127 .period_bytes_min = 64,
128 .period_bytes_max = 8192,
129 .periods_min = 1,
130 .periods_max = 1024},
131 .capture_hw = {
132 .info = (SNDRV_PCM_INFO_MMAP |
133 SNDRV_PCM_INFO_INTERLEAVED |
134 SNDRV_PCM_INFO_BLOCK_TRANSFER |
135 SNDRV_PCM_INFO_MMAP_VALID |
136 SNDRV_PCM_INFO_SYNC_START),
137 .formats = SNDRV_PCM_FMTBIT_S24_3LE,
138 .rates = SNDRV_PCM_RATE_48000,
139 .rate_min = 48000,
140 .rate_max = 48000,
141 /* 1+2: Main signal (out), 3+4: Tone 1,
142 * 5+6: Tone 2, 7+8: raw
143 */
144 .channels_min = 8,
145 .channels_max = 8,
146 .buffer_bytes_max = 60000,
147 .period_bytes_min = 64,
148 .period_bytes_max = 8192,
149 .periods_min = 1,
150 .periods_max = 1024},
151 .rates = {
152 .nrats = 1,
153 .rats = &podhd_ratden},
154 .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
155};
156
157static void podhd_startup_start_workqueue(unsigned long data);
158static void podhd_startup_workqueue(struct work_struct *work);
159static int podhd_startup_finalize(struct usb_line6_podhd *pod);
160
161static ssize_t serial_number_show(struct device *dev,
162 struct device_attribute *attr, char *buf)
163{
164 struct snd_card *card = dev_to_snd_card(dev);
165 struct usb_line6_podhd *pod = card->private_data;
166
167 return sprintf(buf, "%u\n", pod->serial_number);
168}
169
170static ssize_t firmware_version_show(struct device *dev,
171 struct device_attribute *attr, char *buf)
172{
173 struct snd_card *card = dev_to_snd_card(dev);
174 struct usb_line6_podhd *pod = card->private_data;
175
176 return sprintf(buf, "%06x\n", pod->firmware_version);
177}
178
179static DEVICE_ATTR_RO(firmware_version);
180static DEVICE_ATTR_RO(serial_number);
181
182static struct attribute *podhd_dev_attrs[] = {
183 &dev_attr_firmware_version.attr,
184 &dev_attr_serial_number.attr,
185 NULL
186};
187
188static const struct attribute_group podhd_dev_attr_group = {
189 .name = "podhd",
190 .attrs = podhd_dev_attrs,
191};
192
193/*
194 * POD X3 startup procedure.
195 *
196 * May be compatible with other POD HD's, since it's also similar to the
197 * previous POD setup. In any case, it doesn't seem to be required for the
198 * audio nor bulk interfaces to work.
199 */
200
201static void podhd_startup(struct usb_line6_podhd *pod)
202{
203 CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT);
204
205 /* delay startup procedure: */
206 line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY,
207 podhd_startup_start_workqueue, (unsigned long)pod);
208}
209
210static void podhd_startup_start_workqueue(unsigned long data)
211{
212 struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data;
213
214 CHECK_STARTUP_PROGRESS(pod->startup_progress,
215 PODHD_STARTUP_SCHEDULE_WORKQUEUE);
216
217 /* schedule work for global work queue: */
218 schedule_work(&pod->startup_work);
219}
220
221static int podhd_dev_start(struct usb_line6_podhd *pod)
222{
223 int ret;
Greg Kroah-Hartmanb9daeb22019-04-28 18:04:11 +0200224 u8 *init_bytes;
Andrej Krutak790869d2016-09-18 20:59:28 +0200225 int i;
226 struct usb_device *usbdev = pod->line6.usbdev;
227
Greg Kroah-Hartmanb9daeb22019-04-28 18:04:11 +0200228 init_bytes = kmalloc(8, GFP_KERNEL);
229 if (!init_bytes)
230 return -ENOMEM;
231
Andrej Krutak790869d2016-09-18 20:59:28 +0200232 ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
233 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
234 0x11, 0,
235 NULL, 0, LINE6_TIMEOUT * HZ);
236 if (ret < 0) {
237 dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
Greg Kroah-Hartmanb9daeb22019-04-28 18:04:11 +0200238 goto exit;
Andrej Krutak790869d2016-09-18 20:59:28 +0200239 }
240
241 /* NOTE: looks like some kind of ping message */
242 ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
243 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
244 0x11, 0x0,
Greg Kroah-Hartmanb9daeb22019-04-28 18:04:11 +0200245 init_bytes, 3, LINE6_TIMEOUT * HZ);
Andrej Krutak790869d2016-09-18 20:59:28 +0200246 if (ret < 0) {
247 dev_err(pod->line6.ifcdev,
248 "receive length failed (error %d)\n", ret);
Greg Kroah-Hartmanb9daeb22019-04-28 18:04:11 +0200249 goto exit;
Andrej Krutak790869d2016-09-18 20:59:28 +0200250 }
251
252 pod->firmware_version =
253 (init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0);
254
255 for (i = 0; i <= 16; i++) {
256 ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
257 if (ret < 0)
Greg Kroah-Hartmanb9daeb22019-04-28 18:04:11 +0200258 goto exit;
Andrej Krutak790869d2016-09-18 20:59:28 +0200259 }
260
261 ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
262 USB_REQ_SET_FEATURE,
263 USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
264 1, 0,
265 NULL, 0, LINE6_TIMEOUT * HZ);
Greg Kroah-Hartmanb9daeb22019-04-28 18:04:11 +0200266exit:
267 kfree(init_bytes);
268 return ret;
Andrej Krutak790869d2016-09-18 20:59:28 +0200269}
270
271static void podhd_startup_workqueue(struct work_struct *work)
272{
273 struct usb_line6_podhd *pod =
274 container_of(work, struct usb_line6_podhd, startup_work);
275
276 CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP);
277
278 podhd_dev_start(pod);
279 line6_read_serial_number(&pod->line6, &pod->serial_number);
280
281 podhd_startup_finalize(pod);
282}
283
284static int podhd_startup_finalize(struct usb_line6_podhd *pod)
285{
286 struct usb_line6 *line6 = &pod->line6;
287
288 /* ALSA audio interface: */
289 return snd_card_register(line6->card);
290}
291
292static void podhd_disconnect(struct usb_line6 *line6)
293{
294 struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
295
296 if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
297 del_timer_sync(&pod->startup_timer);
298 cancel_work_sync(&pod->startup_work);
299 }
300}
301
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +0000302/*
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +0000303 Try to init POD HD device.
304*/
Takashi Iwaif66fd992015-01-25 18:22:58 +0100305static int podhd_init(struct usb_line6 *line6,
306 const struct usb_device_id *id)
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +0000307{
308 int err;
Andrej Krutak790869d2016-09-18 20:59:28 +0200309 struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6;
310
311 line6->disconnect = podhd_disconnect;
312
Takashi Iwaib65f99b2017-10-09 14:32:15 +0200313 init_timer(&pod->startup_timer);
314 INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
315
Andrej Krutak790869d2016-09-18 20:59:28 +0200316 if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
317 /* create sysfs entries: */
318 err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
319 if (err < 0)
320 return err;
321 }
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +0000322
Andrej Krutakcfa76962016-09-18 20:59:31 +0200323 if (pod->line6.properties->capabilities & LINE6_CAP_PCM) {
324 /* initialize PCM subsystem: */
325 err = line6_init_pcm(line6,
Andrej Krutakb9079002016-10-05 17:46:09 +0200326 (id->driver_info == LINE6_PODX3 ||
327 id->driver_info == LINE6_PODX3LIVE) ? &podx3_pcm_properties :
Andrej Krutakcfa76962016-09-18 20:59:31 +0200328 &podhd_pcm_properties);
329 if (err < 0)
330 return err;
331 }
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +0000332
Andrej Krutak790869d2016-09-18 20:59:28 +0200333 if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
334 /* register USB audio system directly */
335 return podhd_startup_finalize(pod);
336 }
337
338 /* init device and delay registering */
Andrej Krutak790869d2016-09-18 20:59:28 +0200339 podhd_startup(pod);
340 return 0;
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +0000341}
342
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100343#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
344#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n)
345
346/* table of devices that work with this driver */
347static const struct usb_device_id podhd_id_table[] = {
Andrej Krutak790869d2016-09-18 20:59:28 +0200348 /* TODO: no need to alloc data interfaces when only audio is used */
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100349 { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 },
350 { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 },
351 { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
352 { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
Andrej Krutak790869d2016-09-18 20:59:28 +0200353 { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
Andrej Krutakc039aaa2016-09-18 20:59:29 +0200354 { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100355 {}
356};
357
358MODULE_DEVICE_TABLE(usb, podhd_id_table);
359
360static const struct line6_properties podhd_properties_table[] = {
361 [LINE6_PODHD300] = {
362 .id = "PODHD300",
363 .name = "POD HD300",
Andrej Krutak790869d2016-09-18 20:59:28 +0200364 .capabilities = LINE6_CAP_PCM
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100365 | LINE6_CAP_HWMON,
366 .altsetting = 5,
367 .ep_ctrl_r = 0x84,
368 .ep_ctrl_w = 0x03,
369 .ep_audio_r = 0x82,
370 .ep_audio_w = 0x01,
371 },
372 [LINE6_PODHD400] = {
373 .id = "PODHD400",
374 .name = "POD HD400",
Andrej Krutak790869d2016-09-18 20:59:28 +0200375 .capabilities = LINE6_CAP_PCM
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100376 | LINE6_CAP_HWMON,
377 .altsetting = 5,
378 .ep_ctrl_r = 0x84,
379 .ep_ctrl_w = 0x03,
380 .ep_audio_r = 0x82,
381 .ep_audio_w = 0x01,
382 },
383 [LINE6_PODHD500_0] = {
384 .id = "PODHD500",
385 .name = "POD HD500",
Andrej Krutak790869d2016-09-18 20:59:28 +0200386 .capabilities = LINE6_CAP_PCM
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100387 | LINE6_CAP_HWMON,
Kai-Heng Fengec565612019-07-18 17:53:13 +0800388 .altsetting = 0,
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100389 .ep_ctrl_r = 0x81,
390 .ep_ctrl_w = 0x01,
391 .ep_audio_r = 0x86,
392 .ep_audio_w = 0x02,
393 },
394 [LINE6_PODHD500_1] = {
395 .id = "PODHD500",
396 .name = "POD HD500",
Andrej Krutak790869d2016-09-18 20:59:28 +0200397 .capabilities = LINE6_CAP_PCM
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100398 | LINE6_CAP_HWMON,
399 .altsetting = 1,
400 .ep_ctrl_r = 0x81,
401 .ep_ctrl_w = 0x01,
402 .ep_audio_r = 0x86,
403 .ep_audio_w = 0x02,
404 },
Andrej Krutak790869d2016-09-18 20:59:28 +0200405 [LINE6_PODX3] = {
406 .id = "PODX3",
407 .name = "POD X3",
408 .capabilities = LINE6_CAP_CONTROL
409 | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
410 .altsetting = 1,
411 .ep_ctrl_r = 0x81,
412 .ep_ctrl_w = 0x01,
413 .ep_audio_r = 0x86,
414 .ep_audio_w = 0x02,
415 },
Andrej Krutakc039aaa2016-09-18 20:59:29 +0200416 [LINE6_PODX3LIVE] = {
417 .id = "PODX3LIVE",
418 .name = "POD X3 LIVE",
419 .capabilities = LINE6_CAP_CONTROL
420 | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
421 .altsetting = 1,
422 .ep_ctrl_r = 0x81,
423 .ep_ctrl_w = 0x01,
424 .ep_audio_r = 0x86,
425 .ep_audio_w = 0x02,
426 },
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100427};
428
Stefan Hajnoczi16dc1042011-11-23 08:20:42 +0000429/*
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100430 Probe USB device.
431*/
432static int podhd_probe(struct usb_interface *interface,
433 const struct usb_device_id *id)
434{
Chris Rorvick12865ca2015-02-07 10:43:19 -0600435 return line6_probe(interface, id, "Line6-PODHD",
Takashi Iwai85a93392015-01-19 15:54:00 +0100436 &podhd_properties_table[id->driver_info],
Andrej Krutak790869d2016-09-18 20:59:28 +0200437 podhd_init, sizeof(struct usb_line6_podhd));
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100438}
439
440static struct usb_driver podhd_driver = {
441 .name = KBUILD_MODNAME,
442 .probe = podhd_probe,
443 .disconnect = line6_disconnect,
444#ifdef CONFIG_PM
445 .suspend = line6_suspend,
446 .resume = line6_resume,
447 .reset_resume = line6_resume,
448#endif
449 .id_table = podhd_id_table,
450};
451
452module_usb_driver(podhd_driver);
453
Chris Rorvickc6fffce2015-01-20 02:20:49 -0600454MODULE_DESCRIPTION("Line 6 PODHD USB driver");
Takashi Iwaiccddbe42015-01-15 08:22:31 +0100455MODULE_LICENSE("GPL");