]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - sound/usb/line6/podhd.c
sound: line6: move to use usb_control_msg_send() and usb_control_msg_recv()
[mirror_ubuntu-jammy-kernel.git] / sound / usb / line6 / podhd.c
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 sprintf(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 sprintf(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 * HZ);
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 * HZ);
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 * HZ);
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
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");