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