Skip to content

Commit 790869d

Browse files
andree182tiwai
authored andcommitted
ALSA: line6: Add support for POD X3
This includes audio in/out and basic initialization via control EP (emulates what original driver does). The initialization is done similarly to original POD, firmware and serial IDs are read and exported via sysfs. Signed-off-by: Andrej Krutak <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent 7811a3a commit 790869d

File tree

2 files changed

+265
-15
lines changed

2 files changed

+265
-15
lines changed

sound/usb/line6/Kconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ config SND_USB_POD
2121
re-amping)
2222

2323
config SND_USB_PODHD
24-
tristate "Line 6 POD HD300/400/500 USB support"
24+
tristate "Line 6 POD X3/HD300/400/500 USB support"
2525
select SND_USB_LINE6
2626
help
27-
This is a driver for POD HD300, 400 and 500 devices.
27+
This is a driver for POD X3, HD300, 400 and 500 devices.
2828

2929
config SND_USB_TONEPORT
3030
tristate "TonePort GX, UX1 and UX2 USB support"

sound/usb/line6/podhd.c

Lines changed: 263 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Line 6 Pod HD
33
*
44
* Copyright (C) 2011 Stefan Hajnoczi <[email protected]>
5+
* Copyright (C) 2015 Andrej Krutak <[email protected]>
56
*
67
* This program is free software; you can redistribute it and/or
78
* modify it under the terms of the GNU General Public License as
@@ -18,11 +19,44 @@
1819
#include "driver.h"
1920
#include "pcm.h"
2021

22+
#define PODHD_STARTUP_DELAY 500
23+
24+
/*
25+
* Stages of POD startup procedure
26+
*/
27+
enum {
28+
PODHD_STARTUP_INIT = 1,
29+
PODHD_STARTUP_SCHEDULE_WORKQUEUE,
30+
PODHD_STARTUP_SETUP,
31+
PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1
32+
};
33+
2134
enum {
2235
LINE6_PODHD300,
2336
LINE6_PODHD400,
2437
LINE6_PODHD500_0,
2538
LINE6_PODHD500_1,
39+
LINE6_PODX3,
40+
};
41+
42+
struct usb_line6_podhd {
43+
/* Generic Line 6 USB data */
44+
struct usb_line6 line6;
45+
46+
/* Timer for device initialization */
47+
struct timer_list startup_timer;
48+
49+
/* Work handler for device initialization */
50+
struct work_struct startup_work;
51+
52+
/* Current progress in startup procedure */
53+
int startup_progress;
54+
55+
/* Serial number of device */
56+
u32 serial_number;
57+
58+
/* Firmware version */
59+
int firmware_version;
2660
};
2761

2862
static struct snd_ratden podhd_ratden = {
@@ -71,40 +105,249 @@ static struct line6_pcm_properties podhd_pcm_properties = {
71105
.rates = {
72106
.nrats = 1,
73107
.rats = &podhd_ratden},
74-
.bytes_per_channel = 3 /* 24bit audio (stereo) */
108+
.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
75109
};
76110

111+
static struct line6_pcm_properties podx3_pcm_properties = {
112+
.playback_hw = {
113+
.info = (SNDRV_PCM_INFO_MMAP |
114+
SNDRV_PCM_INFO_INTERLEAVED |
115+
SNDRV_PCM_INFO_BLOCK_TRANSFER |
116+
SNDRV_PCM_INFO_MMAP_VALID |
117+
SNDRV_PCM_INFO_PAUSE |
118+
SNDRV_PCM_INFO_SYNC_START),
119+
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
120+
.rates = SNDRV_PCM_RATE_48000,
121+
.rate_min = 48000,
122+
.rate_max = 48000,
123+
.channels_min = 2,
124+
.channels_max = 2,
125+
.buffer_bytes_max = 60000,
126+
.period_bytes_min = 64,
127+
.period_bytes_max = 8192,
128+
.periods_min = 1,
129+
.periods_max = 1024},
130+
.capture_hw = {
131+
.info = (SNDRV_PCM_INFO_MMAP |
132+
SNDRV_PCM_INFO_INTERLEAVED |
133+
SNDRV_PCM_INFO_BLOCK_TRANSFER |
134+
SNDRV_PCM_INFO_MMAP_VALID |
135+
SNDRV_PCM_INFO_SYNC_START),
136+
.formats = SNDRV_PCM_FMTBIT_S24_3LE,
137+
.rates = SNDRV_PCM_RATE_48000,
138+
.rate_min = 48000,
139+
.rate_max = 48000,
140+
/* 1+2: Main signal (out), 3+4: Tone 1,
141+
* 5+6: Tone 2, 7+8: raw
142+
*/
143+
.channels_min = 8,
144+
.channels_max = 8,
145+
.buffer_bytes_max = 60000,
146+
.period_bytes_min = 64,
147+
.period_bytes_max = 8192,
148+
.periods_min = 1,
149+
.periods_max = 1024},
150+
.rates = {
151+
.nrats = 1,
152+
.rats = &podhd_ratden},
153+
.bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
154+
};
155+
156+
static void podhd_startup_start_workqueue(unsigned long data);
157+
static void podhd_startup_workqueue(struct work_struct *work);
158+
static int podhd_startup_finalize(struct usb_line6_podhd *pod);
159+
160+
static ssize_t serial_number_show(struct device *dev,
161+
struct device_attribute *attr, char *buf)
162+
{
163+
struct snd_card *card = dev_to_snd_card(dev);
164+
struct usb_line6_podhd *pod = card->private_data;
165+
166+
return sprintf(buf, "%u\n", pod->serial_number);
167+
}
168+
169+
static ssize_t firmware_version_show(struct device *dev,
170+
struct device_attribute *attr, char *buf)
171+
{
172+
struct snd_card *card = dev_to_snd_card(dev);
173+
struct usb_line6_podhd *pod = card->private_data;
174+
175+
return sprintf(buf, "%06x\n", pod->firmware_version);
176+
}
177+
178+
static DEVICE_ATTR_RO(firmware_version);
179+
static DEVICE_ATTR_RO(serial_number);
180+
181+
static struct attribute *podhd_dev_attrs[] = {
182+
&dev_attr_firmware_version.attr,
183+
&dev_attr_serial_number.attr,
184+
NULL
185+
};
186+
187+
static const struct attribute_group podhd_dev_attr_group = {
188+
.name = "podhd",
189+
.attrs = podhd_dev_attrs,
190+
};
191+
192+
/*
193+
* POD X3 startup procedure.
194+
*
195+
* May be compatible with other POD HD's, since it's also similar to the
196+
* previous POD setup. In any case, it doesn't seem to be required for the
197+
* audio nor bulk interfaces to work.
198+
*/
199+
200+
static void podhd_startup(struct usb_line6_podhd *pod)
201+
{
202+
CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT);
203+
204+
/* delay startup procedure: */
205+
line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY,
206+
podhd_startup_start_workqueue, (unsigned long)pod);
207+
}
208+
209+
static void podhd_startup_start_workqueue(unsigned long data)
210+
{
211+
struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data;
212+
213+
CHECK_STARTUP_PROGRESS(pod->startup_progress,
214+
PODHD_STARTUP_SCHEDULE_WORKQUEUE);
215+
216+
/* schedule work for global work queue: */
217+
schedule_work(&pod->startup_work);
218+
}
219+
220+
static int podhd_dev_start(struct usb_line6_podhd *pod)
221+
{
222+
int ret;
223+
u8 init_bytes[8];
224+
int i;
225+
struct usb_device *usbdev = pod->line6.usbdev;
226+
227+
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
228+
0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
229+
0x11, 0,
230+
NULL, 0, LINE6_TIMEOUT * HZ);
231+
if (ret < 0) {
232+
dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
233+
return ret;
234+
}
235+
236+
/* NOTE: looks like some kind of ping message */
237+
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
238+
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
239+
0x11, 0x0,
240+
&init_bytes, 3, LINE6_TIMEOUT * HZ);
241+
if (ret < 0) {
242+
dev_err(pod->line6.ifcdev,
243+
"receive length failed (error %d)\n", ret);
244+
return ret;
245+
}
246+
247+
pod->firmware_version =
248+
(init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0);
249+
250+
for (i = 0; i <= 16; i++) {
251+
ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
252+
if (ret < 0)
253+
return ret;
254+
}
255+
256+
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
257+
USB_REQ_SET_FEATURE,
258+
USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
259+
1, 0,
260+
NULL, 0, LINE6_TIMEOUT * HZ);
261+
if (ret < 0)
262+
return ret;
263+
264+
return 0;
265+
}
266+
267+
static void podhd_startup_workqueue(struct work_struct *work)
268+
{
269+
struct usb_line6_podhd *pod =
270+
container_of(work, struct usb_line6_podhd, startup_work);
271+
272+
CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP);
273+
274+
podhd_dev_start(pod);
275+
line6_read_serial_number(&pod->line6, &pod->serial_number);
276+
277+
podhd_startup_finalize(pod);
278+
}
279+
280+
static int podhd_startup_finalize(struct usb_line6_podhd *pod)
281+
{
282+
struct usb_line6 *line6 = &pod->line6;
283+
284+
/* ALSA audio interface: */
285+
return snd_card_register(line6->card);
286+
}
287+
288+
static void podhd_disconnect(struct usb_line6 *line6)
289+
{
290+
struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
291+
292+
if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
293+
del_timer_sync(&pod->startup_timer);
294+
cancel_work_sync(&pod->startup_work);
295+
}
296+
}
297+
77298
/*
78299
Try to init POD HD device.
79300
*/
80301
static int podhd_init(struct usb_line6 *line6,
81302
const struct usb_device_id *id)
82303
{
83304
int err;
305+
struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6;
306+
307+
line6->disconnect = podhd_disconnect;
308+
309+
if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
310+
/* create sysfs entries: */
311+
err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
312+
if (err < 0)
313+
return err;
314+
}
84315

85316
/* initialize MIDI subsystem: */
86317
err = line6_init_midi(line6);
87318
if (err < 0)
88319
return err;
89320

90321
/* initialize PCM subsystem: */
91-
err = line6_init_pcm(line6, &podhd_pcm_properties);
322+
err = line6_init_pcm(line6,
323+
(id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
324+
&podhd_pcm_properties);
92325
if (err < 0)
93326
return err;
94327

95-
/* register USB audio system: */
96-
return snd_card_register(line6->card);
328+
if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
329+
/* register USB audio system directly */
330+
return podhd_startup_finalize(pod);
331+
}
332+
333+
/* init device and delay registering */
334+
init_timer(&pod->startup_timer);
335+
INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
336+
podhd_startup(pod);
337+
return 0;
97338
}
98339

99340
#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
100341
#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n)
101342

102343
/* table of devices that work with this driver */
103344
static const struct usb_device_id podhd_id_table[] = {
345+
/* TODO: no need to alloc data interfaces when only audio is used */
104346
{ LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 },
105347
{ LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 },
106348
{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
107349
{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
350+
{ LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
108351
{}
109352
};
110353

@@ -114,8 +357,7 @@ static const struct line6_properties podhd_properties_table[] = {
114357
[LINE6_PODHD300] = {
115358
.id = "PODHD300",
116359
.name = "POD HD300",
117-
.capabilities = LINE6_CAP_CONTROL
118-
| LINE6_CAP_PCM
360+
.capabilities = LINE6_CAP_PCM
119361
| LINE6_CAP_HWMON,
120362
.altsetting = 5,
121363
.ep_ctrl_r = 0x84,
@@ -126,8 +368,7 @@ static const struct line6_properties podhd_properties_table[] = {
126368
[LINE6_PODHD400] = {
127369
.id = "PODHD400",
128370
.name = "POD HD400",
129-
.capabilities = LINE6_CAP_CONTROL
130-
| LINE6_CAP_PCM
371+
.capabilities = LINE6_CAP_PCM
131372
| LINE6_CAP_HWMON,
132373
.altsetting = 5,
133374
.ep_ctrl_r = 0x84,
@@ -138,8 +379,7 @@ static const struct line6_properties podhd_properties_table[] = {
138379
[LINE6_PODHD500_0] = {
139380
.id = "PODHD500",
140381
.name = "POD HD500",
141-
.capabilities = LINE6_CAP_CONTROL
142-
| LINE6_CAP_PCM
382+
.capabilities = LINE6_CAP_PCM
143383
| LINE6_CAP_HWMON,
144384
.altsetting = 1,
145385
.ep_ctrl_r = 0x81,
@@ -150,15 +390,25 @@ static const struct line6_properties podhd_properties_table[] = {
150390
[LINE6_PODHD500_1] = {
151391
.id = "PODHD500",
152392
.name = "POD HD500",
153-
.capabilities = LINE6_CAP_CONTROL
154-
| LINE6_CAP_PCM
393+
.capabilities = LINE6_CAP_PCM
155394
| LINE6_CAP_HWMON,
156395
.altsetting = 1,
157396
.ep_ctrl_r = 0x81,
158397
.ep_ctrl_w = 0x01,
159398
.ep_audio_r = 0x86,
160399
.ep_audio_w = 0x02,
161400
},
401+
[LINE6_PODX3] = {
402+
.id = "PODX3",
403+
.name = "POD X3",
404+
.capabilities = LINE6_CAP_CONTROL
405+
| LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
406+
.altsetting = 1,
407+
.ep_ctrl_r = 0x81,
408+
.ep_ctrl_w = 0x01,
409+
.ep_audio_r = 0x86,
410+
.ep_audio_w = 0x02,
411+
},
162412
};
163413

164414
/*
@@ -169,7 +419,7 @@ static int podhd_probe(struct usb_interface *interface,
169419
{
170420
return line6_probe(interface, id, "Line6-PODHD",
171421
&podhd_properties_table[id->driver_info],
172-
podhd_init, sizeof(struct usb_line6));
422+
podhd_init, sizeof(struct usb_line6_podhd));
173423
}
174424

175425
static struct usb_driver podhd_driver = {

0 commit comments

Comments
 (0)