Commit | Line | Data |
---|---|---|
705ececd | 1 | /* |
e1a164d7 | 2 | * Line6 Linux USB driver - 0.9.1beta |
705ececd | 3 | * |
1027f476 | 4 | * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) |
705ececd MG |
5 | * Emil Myhrman (emil.myhrman@gmail.com) |
6 | * | |
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 | ||
1027f476 MG |
13 | #include <linux/wait.h> |
14 | #include <sound/control.h> | |
705ececd MG |
15 | |
16 | #include "audio.h" | |
17 | #include "capture.h" | |
1027f476 | 18 | #include "driver.h" |
705ececd MG |
19 | #include "playback.h" |
20 | #include "toneport.h" | |
21 | ||
705ececd MG |
22 | static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); |
23 | ||
1027f476 MG |
24 | #define TONEPORT_PCM_DELAY 1 |
25 | ||
705ececd MG |
26 | static struct snd_ratden toneport_ratden = { |
27 | .num_min = 44100, | |
28 | .num_max = 44100, | |
29 | .num_step = 1, | |
30 | .den = 1 | |
31 | }; | |
32 | ||
33 | static struct line6_pcm_properties toneport_pcm_properties = { | |
63a4a8ba | 34 | .snd_line6_playback_hw = { |
e1a164d7 MG |
35 | .info = (SNDRV_PCM_INFO_MMAP | |
36 | SNDRV_PCM_INFO_INTERLEAVED | | |
37 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
38 | SNDRV_PCM_INFO_MMAP_VALID | | |
39 | SNDRV_PCM_INFO_PAUSE | | |
1027f476 | 40 | #ifdef CONFIG_PM |
e1a164d7 | 41 | SNDRV_PCM_INFO_RESUME | |
1027f476 | 42 | #endif |
e1a164d7 MG |
43 | SNDRV_PCM_INFO_SYNC_START), |
44 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
45 | .rates = SNDRV_PCM_RATE_KNOT, | |
46 | .rate_min = 44100, | |
47 | .rate_max = 44100, | |
48 | .channels_min = 2, | |
49 | .channels_max = 2, | |
50 | .buffer_bytes_max = 60000, | |
51 | .period_bytes_min = 64, | |
52 | .period_bytes_max = 8192, | |
53 | .periods_min = 1, | |
54 | .periods_max = 1024}, | |
63a4a8ba | 55 | .snd_line6_capture_hw = { |
e1a164d7 MG |
56 | .info = (SNDRV_PCM_INFO_MMAP | |
57 | SNDRV_PCM_INFO_INTERLEAVED | | |
58 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
59 | SNDRV_PCM_INFO_MMAP_VALID | | |
1027f476 | 60 | #ifdef CONFIG_PM |
e1a164d7 | 61 | SNDRV_PCM_INFO_RESUME | |
1027f476 | 62 | #endif |
e1a164d7 MG |
63 | SNDRV_PCM_INFO_SYNC_START), |
64 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | |
65 | .rates = SNDRV_PCM_RATE_KNOT, | |
66 | .rate_min = 44100, | |
67 | .rate_max = 44100, | |
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}, | |
705ececd | 75 | .snd_line6_rates = { |
e1a164d7 MG |
76 | .nrats = 1, |
77 | .rats = &toneport_ratden}, | |
705ececd MG |
78 | .bytes_per_frame = 4 |
79 | }; | |
80 | ||
81 | /* | |
82 | For the led on Guitarport. | |
6353773b GKH |
83 | Brightness goes from 0x00 to 0x26. Set a value above this to have led |
84 | blink. | |
705ececd MG |
85 | (void cmd_0x02(byte red, byte green) |
86 | */ | |
87 | static int led_red = 0x00; | |
88 | static int led_green = 0x26; | |
89 | ||
e1a164d7 | 90 | struct ToneportSourceInfo { |
1027f476 MG |
91 | const char *name; |
92 | int code; | |
93 | }; | |
94 | ||
95 | static const struct ToneportSourceInfo toneport_source_info[] = { | |
e1a164d7 MG |
96 | {"Microphone", 0x0a01}, |
97 | {"Line", 0x0801}, | |
98 | {"Instrument", 0x0b01}, | |
99 | {"Inst & Mic", 0x0901} | |
1027f476 MG |
100 | }; |
101 | ||
102 | static bool toneport_has_led(short product) | |
103 | { | |
104 | return | |
e1a164d7 MG |
105 | (product == LINE6_DEVID_GUITARPORT) || |
106 | (product == LINE6_DEVID_TONEPORT_GX); | |
1027f476 MG |
107 | /* add your device here if you are missing support for the LEDs */ |
108 | } | |
109 | ||
6353773b GKH |
110 | static void toneport_update_led(struct device *dev) |
111 | { | |
112 | struct usb_interface *interface = to_usb_interface(dev); | |
113 | struct usb_line6_toneport *tp = usb_get_intfdata(interface); | |
114 | struct usb_line6 *line6; | |
705ececd | 115 | |
6353773b GKH |
116 | if (!tp) |
117 | return; | |
118 | ||
119 | line6 = &tp->line6; | |
120 | if (line6) | |
121 | toneport_send_cmd(line6->usbdev, (led_red << 8) | 0x0002, | |
122 | led_green); | |
705ececd | 123 | } |
6353773b | 124 | |
77491e52 GKH |
125 | static ssize_t toneport_set_led_red(struct device *dev, |
126 | struct device_attribute *attr, | |
6353773b GKH |
127 | const char *buf, size_t count) |
128 | { | |
bb950a16 SB |
129 | int retval; |
130 | long value; | |
131 | ||
132 | retval = strict_strtol(buf, 10, &value); | |
133 | if (retval) | |
134 | return retval; | |
135 | ||
136 | led_red = value; | |
705ececd MG |
137 | toneport_update_led(dev); |
138 | return count; | |
139 | } | |
6353773b | 140 | |
77491e52 GKH |
141 | static ssize_t toneport_set_led_green(struct device *dev, |
142 | struct device_attribute *attr, | |
6353773b GKH |
143 | const char *buf, size_t count) |
144 | { | |
bb950a16 SB |
145 | int retval; |
146 | long value; | |
147 | ||
148 | retval = strict_strtol(buf, 10, &value); | |
149 | if (retval) | |
150 | return retval; | |
151 | ||
152 | led_green = value; | |
705ececd MG |
153 | toneport_update_led(dev); |
154 | return count; | |
155 | } | |
156 | ||
a3a972a0 | 157 | static DEVICE_ATTR(led_red, S_IWUSR | S_IRUGO, line6_nop_read, |
63a4a8ba | 158 | toneport_set_led_red); |
a3a972a0 | 159 | static DEVICE_ATTR(led_green, S_IWUSR | S_IRUGO, line6_nop_read, |
63a4a8ba | 160 | toneport_set_led_green); |
705ececd MG |
161 | |
162 | static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) | |
163 | { | |
164 | int ret; | |
705ececd | 165 | |
6353773b GKH |
166 | ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, |
167 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, | |
168 | cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ); | |
169 | ||
170 | if (ret < 0) { | |
d86938fb | 171 | dev_err(&usbdev->dev, "send failed (error %d)\n", ret); |
705ececd MG |
172 | return ret; |
173 | } | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
1027f476 MG |
178 | /* monitor info callback */ |
179 | static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol, | |
180 | struct snd_ctl_elem_info *uinfo) | |
181 | { | |
182 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
183 | uinfo->count = 1; | |
184 | uinfo->value.integer.min = 0; | |
185 | uinfo->value.integer.max = 256; | |
186 | return 0; | |
187 | } | |
188 | ||
189 | /* monitor get callback */ | |
190 | static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol, | |
191 | struct snd_ctl_elem_value *ucontrol) | |
192 | { | |
193 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
194 | ucontrol->value.integer.value[0] = line6pcm->volume_monitor; | |
195 | return 0; | |
196 | } | |
197 | ||
198 | /* monitor put callback */ | |
199 | static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, | |
200 | struct snd_ctl_elem_value *ucontrol) | |
201 | { | |
202 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
203 | ||
e1a164d7 | 204 | if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) |
1027f476 MG |
205 | return 0; |
206 | ||
207 | line6pcm->volume_monitor = ucontrol->value.integer.value[0]; | |
e1a164d7 MG |
208 | |
209 | if (line6pcm->volume_monitor > 0) | |
0ca54888 | 210 | line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR); |
e1a164d7 | 211 | else |
0ca54888 | 212 | line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); |
e1a164d7 | 213 | |
1027f476 MG |
214 | return 1; |
215 | } | |
216 | ||
217 | /* source info callback */ | |
218 | static int snd_toneport_source_info(struct snd_kcontrol *kcontrol, | |
219 | struct snd_ctl_elem_info *uinfo) | |
220 | { | |
221 | const int size = ARRAY_SIZE(toneport_source_info); | |
222 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | |
223 | uinfo->count = 1; | |
224 | uinfo->value.enumerated.items = size; | |
225 | ||
e1a164d7 | 226 | if (uinfo->value.enumerated.item >= size) |
1027f476 MG |
227 | uinfo->value.enumerated.item = size - 1; |
228 | ||
229 | strcpy(uinfo->value.enumerated.name, | |
230 | toneport_source_info[uinfo->value.enumerated.item].name); | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
235 | /* source get callback */ | |
236 | static int snd_toneport_source_get(struct snd_kcontrol *kcontrol, | |
237 | struct snd_ctl_elem_value *ucontrol) | |
238 | { | |
239 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
e1a164d7 MG |
240 | struct usb_line6_toneport *toneport = |
241 | (struct usb_line6_toneport *)line6pcm->line6; | |
1027f476 MG |
242 | ucontrol->value.enumerated.item[0] = toneport->source; |
243 | return 0; | |
244 | } | |
245 | ||
246 | /* source put callback */ | |
247 | static int snd_toneport_source_put(struct snd_kcontrol *kcontrol, | |
248 | struct snd_ctl_elem_value *ucontrol) | |
249 | { | |
250 | struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); | |
e1a164d7 MG |
251 | struct usb_line6_toneport *toneport = |
252 | (struct usb_line6_toneport *)line6pcm->line6; | |
1027f476 | 253 | |
e1a164d7 | 254 | if (ucontrol->value.enumerated.item[0] == toneport->source) |
1027f476 MG |
255 | return 0; |
256 | ||
257 | toneport->source = ucontrol->value.enumerated.item[0]; | |
e1a164d7 MG |
258 | toneport_send_cmd(toneport->line6.usbdev, |
259 | toneport_source_info[toneport->source].code, 0x0000); | |
1027f476 MG |
260 | return 1; |
261 | } | |
262 | ||
263 | static void toneport_start_pcm(unsigned long arg) | |
264 | { | |
265 | struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; | |
266 | struct usb_line6 *line6 = &toneport->line6; | |
0ca54888 | 267 | line6_pcm_acquire(line6->line6pcm, LINE6_BITS_PCM_MONITOR); |
1027f476 MG |
268 | } |
269 | ||
270 | /* control definition */ | |
271 | static struct snd_kcontrol_new toneport_control_monitor = { | |
272 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
273 | .name = "Monitor Playback Volume", | |
274 | .index = 0, | |
275 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
276 | .info = snd_toneport_monitor_info, | |
277 | .get = snd_toneport_monitor_get, | |
278 | .put = snd_toneport_monitor_put | |
279 | }; | |
280 | ||
281 | /* source selector definition */ | |
282 | static struct snd_kcontrol_new toneport_control_source = { | |
283 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
284 | .name = "PCM Capture Source", | |
285 | .index = 0, | |
286 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
287 | .info = snd_toneport_source_info, | |
288 | .get = snd_toneport_source_get, | |
289 | .put = snd_toneport_source_put | |
290 | }; | |
291 | ||
705ececd MG |
292 | /* |
293 | Toneport destructor. | |
294 | */ | |
295 | static void toneport_destruct(struct usb_interface *interface) | |
296 | { | |
297 | struct usb_line6_toneport *toneport = usb_get_intfdata(interface); | |
705ececd | 298 | |
6353773b GKH |
299 | if (toneport == NULL) |
300 | return; | |
188e6645 | 301 | line6_cleanup_audio(&toneport->line6); |
705ececd MG |
302 | } |
303 | ||
304 | /* | |
1027f476 | 305 | Setup Toneport device. |
705ececd | 306 | */ |
1027f476 | 307 | static void toneport_setup(struct usb_line6_toneport *toneport) |
705ececd | 308 | { |
1027f476 | 309 | int ticks; |
705ececd | 310 | struct usb_line6 *line6 = &toneport->line6; |
1027f476 MG |
311 | struct usb_device *usbdev = line6->usbdev; |
312 | ||
313 | /* sync time on device with host: */ | |
314 | ticks = (int)get_seconds(); | |
315 | line6_write_data(line6, 0x80c6, &ticks, 4); | |
316 | ||
317 | /* enable device: */ | |
318 | toneport_send_cmd(usbdev, 0x0301, 0x0000); | |
319 | ||
320 | /* initialize source select: */ | |
e1a164d7 | 321 | switch (usbdev->descriptor.idProduct) { |
1027f476 | 322 | case LINE6_DEVID_TONEPORT_UX1: |
12177acd | 323 | case LINE6_DEVID_TONEPORT_UX2: |
1027f476 | 324 | case LINE6_DEVID_PODSTUDIO_UX1: |
12177acd | 325 | case LINE6_DEVID_PODSTUDIO_UX2: |
e1a164d7 MG |
326 | toneport_send_cmd(usbdev, |
327 | toneport_source_info[toneport->source].code, | |
328 | 0x0000); | |
1027f476 MG |
329 | } |
330 | ||
331 | if (toneport_has_led(usbdev->descriptor.idProduct)) | |
332 | toneport_update_led(&usbdev->dev); | |
333 | } | |
334 | ||
335 | /* | |
336 | Try to init Toneport device. | |
337 | */ | |
338 | static int toneport_try_init(struct usb_interface *interface, | |
339 | struct usb_line6_toneport *toneport) | |
340 | { | |
341 | int err; | |
342 | struct usb_line6 *line6 = &toneport->line6; | |
343 | struct usb_device *usbdev = line6->usbdev; | |
705ececd | 344 | |
6353773b | 345 | if ((interface == NULL) || (toneport == NULL)) |
705ececd MG |
346 | return -ENODEV; |
347 | ||
348 | /* initialize audio system: */ | |
6353773b | 349 | err = line6_init_audio(line6); |
027360c5 | 350 | if (err < 0) |
705ececd | 351 | return err; |
705ececd MG |
352 | |
353 | /* initialize PCM subsystem: */ | |
6353773b | 354 | err = line6_init_pcm(line6, &toneport_pcm_properties); |
027360c5 | 355 | if (err < 0) |
705ececd | 356 | return err; |
705ececd | 357 | |
1027f476 | 358 | /* register monitor control: */ |
027360c5 GKH |
359 | err = snd_ctl_add(line6->card, |
360 | snd_ctl_new1(&toneport_control_monitor, | |
361 | line6->line6pcm)); | |
362 | if (err < 0) | |
1027f476 | 363 | return err; |
1027f476 MG |
364 | |
365 | /* register source select control: */ | |
e1a164d7 | 366 | switch (usbdev->descriptor.idProduct) { |
1027f476 | 367 | case LINE6_DEVID_TONEPORT_UX1: |
12177acd | 368 | case LINE6_DEVID_TONEPORT_UX2: |
1027f476 | 369 | case LINE6_DEVID_PODSTUDIO_UX1: |
12177acd | 370 | case LINE6_DEVID_PODSTUDIO_UX2: |
e1a164d7 MG |
371 | err = |
372 | snd_ctl_add(line6->card, | |
373 | snd_ctl_new1(&toneport_control_source, | |
374 | line6->line6pcm)); | |
027360c5 | 375 | if (err < 0) |
1027f476 | 376 | return err; |
1027f476 MG |
377 | } |
378 | ||
705ececd | 379 | /* register audio system: */ |
6353773b | 380 | err = line6_register_audio(line6); |
027360c5 | 381 | if (err < 0) |
705ececd | 382 | return err; |
705ececd | 383 | |
705ececd MG |
384 | line6_read_serial_number(line6, &toneport->serial_number); |
385 | line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); | |
386 | ||
1027f476 | 387 | if (toneport_has_led(usbdev->descriptor.idProduct)) { |
e1a164d7 MG |
388 | CHECK_RETURN(device_create_file |
389 | (&interface->dev, &dev_attr_led_red)); | |
390 | CHECK_RETURN(device_create_file | |
391 | (&interface->dev, &dev_attr_led_green)); | |
1027f476 | 392 | } |
705ececd | 393 | |
1027f476 | 394 | toneport_setup(toneport); |
705ececd | 395 | |
1027f476 MG |
396 | init_timer(&toneport->timer); |
397 | toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ; | |
398 | toneport->timer.function = toneport_start_pcm; | |
399 | toneport->timer.data = (unsigned long)toneport; | |
400 | add_timer(&toneport->timer); | |
705ececd MG |
401 | |
402 | return 0; | |
403 | } | |
404 | ||
1027f476 MG |
405 | /* |
406 | Init Toneport device (and clean up in case of failure). | |
407 | */ | |
408 | int line6_toneport_init(struct usb_interface *interface, | |
409 | struct usb_line6_toneport *toneport) | |
410 | { | |
411 | int err = toneport_try_init(interface, toneport); | |
412 | ||
027360c5 | 413 | if (err < 0) |
1027f476 | 414 | toneport_destruct(interface); |
1027f476 MG |
415 | |
416 | return err; | |
417 | } | |
418 | ||
419 | /* | |
420 | Resume Toneport device after reset. | |
421 | */ | |
422 | void line6_toneport_reset_resume(struct usb_line6_toneport *toneport) | |
423 | { | |
424 | toneport_setup(toneport); | |
425 | } | |
426 | ||
705ececd MG |
427 | /* |
428 | Toneport device disconnected. | |
429 | */ | |
1027f476 | 430 | void line6_toneport_disconnect(struct usb_interface *interface) |
705ececd MG |
431 | { |
432 | struct usb_line6_toneport *toneport; | |
433 | ||
6353773b GKH |
434 | if (interface == NULL) |
435 | return; | |
1027f476 | 436 | |
705ececd | 437 | toneport = usb_get_intfdata(interface); |
1027f476 | 438 | del_timer_sync(&toneport->timer); |
705ececd | 439 | |
1027f476 | 440 | if (toneport_has_led(toneport->line6.usbdev->descriptor.idProduct)) { |
705ececd MG |
441 | device_remove_file(&interface->dev, &dev_attr_led_red); |
442 | device_remove_file(&interface->dev, &dev_attr_led_green); | |
443 | } | |
444 | ||
6353773b | 445 | if (toneport != NULL) { |
705ececd MG |
446 | struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; |
447 | ||
6353773b | 448 | if (line6pcm != NULL) { |
0ca54888 | 449 | line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); |
1027f476 | 450 | line6_pcm_disconnect(line6pcm); |
705ececd MG |
451 | } |
452 | } | |
453 | ||
454 | toneport_destruct(interface); | |
455 | } |