Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/usb/line6/podhd.c
26425 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
};
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
561