18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Line 6 Linux USB driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) 68c2ecf20Sopenharmony_ci * Emil Myhrman (emil.myhrman@gmail.com) 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/wait.h> 108c2ecf20Sopenharmony_ci#include <linux/usb.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/leds.h> 148c2ecf20Sopenharmony_ci#include <sound/core.h> 158c2ecf20Sopenharmony_ci#include <sound/control.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "capture.h" 188c2ecf20Sopenharmony_ci#include "driver.h" 198c2ecf20Sopenharmony_ci#include "playback.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cienum line6_device_type { 228c2ecf20Sopenharmony_ci LINE6_GUITARPORT, 238c2ecf20Sopenharmony_ci LINE6_PODSTUDIO_GX, 248c2ecf20Sopenharmony_ci LINE6_PODSTUDIO_UX1, 258c2ecf20Sopenharmony_ci LINE6_PODSTUDIO_UX2, 268c2ecf20Sopenharmony_ci LINE6_TONEPORT_GX, 278c2ecf20Sopenharmony_ci LINE6_TONEPORT_UX1, 288c2ecf20Sopenharmony_ci LINE6_TONEPORT_UX2, 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct usb_line6_toneport; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct toneport_led { 348c2ecf20Sopenharmony_ci struct led_classdev dev; 358c2ecf20Sopenharmony_ci char name[64]; 368c2ecf20Sopenharmony_ci struct usb_line6_toneport *toneport; 378c2ecf20Sopenharmony_ci bool registered; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct usb_line6_toneport { 418c2ecf20Sopenharmony_ci /* Generic Line 6 USB data */ 428c2ecf20Sopenharmony_ci struct usb_line6 line6; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* Source selector */ 458c2ecf20Sopenharmony_ci int source; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* Serial number of device */ 488c2ecf20Sopenharmony_ci u32 serial_number; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* Firmware version (x 100) */ 518c2ecf20Sopenharmony_ci u8 firmware_version; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* Device type */ 548c2ecf20Sopenharmony_ci enum line6_device_type type; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* LED instances */ 578c2ecf20Sopenharmony_ci struct toneport_led leds[2]; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define line6_to_toneport(x) container_of(x, struct usb_line6_toneport, line6) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define TONEPORT_PCM_DELAY 1 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic const struct snd_ratden toneport_ratden = { 678c2ecf20Sopenharmony_ci .num_min = 44100, 688c2ecf20Sopenharmony_ci .num_max = 44100, 698c2ecf20Sopenharmony_ci .num_step = 1, 708c2ecf20Sopenharmony_ci .den = 1 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct line6_pcm_properties toneport_pcm_properties = { 748c2ecf20Sopenharmony_ci .playback_hw = { 758c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 768c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 778c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 788c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 798c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | 808c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 818c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 828c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, 838c2ecf20Sopenharmony_ci .rate_min = 44100, 848c2ecf20Sopenharmony_ci .rate_max = 44100, 858c2ecf20Sopenharmony_ci .channels_min = 2, 868c2ecf20Sopenharmony_ci .channels_max = 2, 878c2ecf20Sopenharmony_ci .buffer_bytes_max = 60000, 888c2ecf20Sopenharmony_ci .period_bytes_min = 64, 898c2ecf20Sopenharmony_ci .period_bytes_max = 8192, 908c2ecf20Sopenharmony_ci .periods_min = 1, 918c2ecf20Sopenharmony_ci .periods_max = 1024}, 928c2ecf20Sopenharmony_ci .capture_hw = { 938c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 948c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 958c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 968c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 978c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 988c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 998c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, 1008c2ecf20Sopenharmony_ci .rate_min = 44100, 1018c2ecf20Sopenharmony_ci .rate_max = 44100, 1028c2ecf20Sopenharmony_ci .channels_min = 2, 1038c2ecf20Sopenharmony_ci .channels_max = 2, 1048c2ecf20Sopenharmony_ci .buffer_bytes_max = 60000, 1058c2ecf20Sopenharmony_ci .period_bytes_min = 64, 1068c2ecf20Sopenharmony_ci .period_bytes_max = 8192, 1078c2ecf20Sopenharmony_ci .periods_min = 1, 1088c2ecf20Sopenharmony_ci .periods_max = 1024}, 1098c2ecf20Sopenharmony_ci .rates = { 1108c2ecf20Sopenharmony_ci .nrats = 1, 1118c2ecf20Sopenharmony_ci .rats = &toneport_ratden}, 1128c2ecf20Sopenharmony_ci .bytes_per_channel = 2 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic const struct { 1168c2ecf20Sopenharmony_ci const char *name; 1178c2ecf20Sopenharmony_ci int code; 1188c2ecf20Sopenharmony_ci} toneport_source_info[] = { 1198c2ecf20Sopenharmony_ci {"Microphone", 0x0a01}, 1208c2ecf20Sopenharmony_ci {"Line", 0x0801}, 1218c2ecf20Sopenharmony_ci {"Instrument", 0x0b01}, 1228c2ecf20Sopenharmony_ci {"Inst & Mic", 0x0901} 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci int ret; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = usb_control_msg_send(usbdev, 0, 0x67, 1308c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 1318c2ecf20Sopenharmony_ci cmd1, cmd2, NULL, 0, LINE6_TIMEOUT, 1328c2ecf20Sopenharmony_ci GFP_KERNEL); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (ret) { 1358c2ecf20Sopenharmony_ci dev_err(&usbdev->dev, "send failed (error %d)\n", ret); 1368c2ecf20Sopenharmony_ci return ret; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* monitor info callback */ 1438c2ecf20Sopenharmony_cistatic int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol, 1448c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1478c2ecf20Sopenharmony_ci uinfo->count = 1; 1488c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 1498c2ecf20Sopenharmony_ci uinfo->value.integer.max = 256; 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* monitor get callback */ 1548c2ecf20Sopenharmony_cistatic int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol, 1558c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = line6pcm->volume_monitor; 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci/* monitor put callback */ 1648c2ecf20Sopenharmony_cistatic int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, 1658c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 1688c2ecf20Sopenharmony_ci int err; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci line6pcm->volume_monitor = ucontrol->value.integer.value[0]; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (line6pcm->volume_monitor > 0) { 1768c2ecf20Sopenharmony_ci err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR, true); 1778c2ecf20Sopenharmony_ci if (err < 0) { 1788c2ecf20Sopenharmony_ci line6pcm->volume_monitor = 0; 1798c2ecf20Sopenharmony_ci line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR); 1808c2ecf20Sopenharmony_ci return err; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } else { 1838c2ecf20Sopenharmony_ci line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR); 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return 1; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* source info callback */ 1908c2ecf20Sopenharmony_cistatic int snd_toneport_source_info(struct snd_kcontrol *kcontrol, 1918c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci const int size = ARRAY_SIZE(toneport_source_info); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 1968c2ecf20Sopenharmony_ci uinfo->count = 1; 1978c2ecf20Sopenharmony_ci uinfo->value.enumerated.items = size; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (uinfo->value.enumerated.item >= size) 2008c2ecf20Sopenharmony_ci uinfo->value.enumerated.item = size - 1; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci strcpy(uinfo->value.enumerated.name, 2038c2ecf20Sopenharmony_ci toneport_source_info[uinfo->value.enumerated.item].name); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* source get callback */ 2098c2ecf20Sopenharmony_cistatic int snd_toneport_source_get(struct snd_kcontrol *kcontrol, 2108c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 2138c2ecf20Sopenharmony_ci struct usb_line6_toneport *toneport = line6_to_toneport(line6pcm->line6); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = toneport->source; 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci/* source put callback */ 2208c2ecf20Sopenharmony_cistatic int snd_toneport_source_put(struct snd_kcontrol *kcontrol, 2218c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 2248c2ecf20Sopenharmony_ci struct usb_line6_toneport *toneport = line6_to_toneport(line6pcm->line6); 2258c2ecf20Sopenharmony_ci unsigned int source; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci source = ucontrol->value.enumerated.item[0]; 2288c2ecf20Sopenharmony_ci if (source >= ARRAY_SIZE(toneport_source_info)) 2298c2ecf20Sopenharmony_ci return -EINVAL; 2308c2ecf20Sopenharmony_ci if (source == toneport->source) 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci toneport->source = source; 2348c2ecf20Sopenharmony_ci toneport_send_cmd(toneport->line6.usbdev, 2358c2ecf20Sopenharmony_ci toneport_source_info[source].code, 0x0000); 2368c2ecf20Sopenharmony_ci return 1; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void toneport_startup(struct usb_line6 *line6) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR, true); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* control definition */ 2458c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new toneport_control_monitor = { 2468c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2478c2ecf20Sopenharmony_ci .name = "Monitor Playback Volume", 2488c2ecf20Sopenharmony_ci .index = 0, 2498c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 2508c2ecf20Sopenharmony_ci .info = snd_toneport_monitor_info, 2518c2ecf20Sopenharmony_ci .get = snd_toneport_monitor_get, 2528c2ecf20Sopenharmony_ci .put = snd_toneport_monitor_put 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* source selector definition */ 2568c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new toneport_control_source = { 2578c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2588c2ecf20Sopenharmony_ci .name = "PCM Capture Source", 2598c2ecf20Sopenharmony_ci .index = 0, 2608c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 2618c2ecf20Sopenharmony_ci .info = snd_toneport_source_info, 2628c2ecf20Sopenharmony_ci .get = snd_toneport_source_get, 2638c2ecf20Sopenharmony_ci .put = snd_toneport_source_put 2648c2ecf20Sopenharmony_ci}; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* 2678c2ecf20Sopenharmony_ci For the led on Guitarport. 2688c2ecf20Sopenharmony_ci Brightness goes from 0x00 to 0x26. Set a value above this to have led 2698c2ecf20Sopenharmony_ci blink. 2708c2ecf20Sopenharmony_ci (void cmd_0x02(byte red, byte green) 2718c2ecf20Sopenharmony_ci*/ 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic bool toneport_has_led(struct usb_line6_toneport *toneport) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci switch (toneport->type) { 2768c2ecf20Sopenharmony_ci case LINE6_GUITARPORT: 2778c2ecf20Sopenharmony_ci case LINE6_TONEPORT_GX: 2788c2ecf20Sopenharmony_ci /* add your device here if you are missing support for the LEDs */ 2798c2ecf20Sopenharmony_ci return true; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci default: 2828c2ecf20Sopenharmony_ci return false; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic const char * const toneport_led_colors[2] = { "red", "green" }; 2878c2ecf20Sopenharmony_cistatic const int toneport_led_init_vals[2] = { 0x00, 0x26 }; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic void toneport_update_led(struct usb_line6_toneport *toneport) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci toneport_send_cmd(toneport->line6.usbdev, 2928c2ecf20Sopenharmony_ci (toneport->leds[0].dev.brightness << 8) | 0x0002, 2938c2ecf20Sopenharmony_ci toneport->leds[1].dev.brightness); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void toneport_led_brightness_set(struct led_classdev *led_cdev, 2978c2ecf20Sopenharmony_ci enum led_brightness brightness) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct toneport_led *leds = 3008c2ecf20Sopenharmony_ci container_of(led_cdev, struct toneport_led, dev); 3018c2ecf20Sopenharmony_ci toneport_update_led(leds->toneport); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int toneport_init_leds(struct usb_line6_toneport *toneport) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct device *dev = &toneport->line6.usbdev->dev; 3078c2ecf20Sopenharmony_ci int i, err; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 3108c2ecf20Sopenharmony_ci struct toneport_led *led = &toneport->leds[i]; 3118c2ecf20Sopenharmony_ci struct led_classdev *leddev = &led->dev; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci led->toneport = toneport; 3148c2ecf20Sopenharmony_ci snprintf(led->name, sizeof(led->name), "%s::%s", 3158c2ecf20Sopenharmony_ci dev_name(dev), toneport_led_colors[i]); 3168c2ecf20Sopenharmony_ci leddev->name = led->name; 3178c2ecf20Sopenharmony_ci leddev->brightness = toneport_led_init_vals[i]; 3188c2ecf20Sopenharmony_ci leddev->max_brightness = 0x26; 3198c2ecf20Sopenharmony_ci leddev->brightness_set = toneport_led_brightness_set; 3208c2ecf20Sopenharmony_ci err = led_classdev_register(dev, leddev); 3218c2ecf20Sopenharmony_ci if (err) 3228c2ecf20Sopenharmony_ci return err; 3238c2ecf20Sopenharmony_ci led->registered = true; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic void toneport_remove_leds(struct usb_line6_toneport *toneport) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct toneport_led *led; 3328c2ecf20Sopenharmony_ci int i; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 3358c2ecf20Sopenharmony_ci led = &toneport->leds[i]; 3368c2ecf20Sopenharmony_ci if (!led->registered) 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci led_classdev_unregister(&led->dev); 3398c2ecf20Sopenharmony_ci led->registered = false; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic bool toneport_has_source_select(struct usb_line6_toneport *toneport) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci switch (toneport->type) { 3468c2ecf20Sopenharmony_ci case LINE6_TONEPORT_UX1: 3478c2ecf20Sopenharmony_ci case LINE6_TONEPORT_UX2: 3488c2ecf20Sopenharmony_ci case LINE6_PODSTUDIO_UX1: 3498c2ecf20Sopenharmony_ci case LINE6_PODSTUDIO_UX2: 3508c2ecf20Sopenharmony_ci return true; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci default: 3538c2ecf20Sopenharmony_ci return false; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* 3588c2ecf20Sopenharmony_ci Setup Toneport device. 3598c2ecf20Sopenharmony_ci*/ 3608c2ecf20Sopenharmony_cistatic int toneport_setup(struct usb_line6_toneport *toneport) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci u32 *ticks; 3638c2ecf20Sopenharmony_ci struct usb_line6 *line6 = &toneport->line6; 3648c2ecf20Sopenharmony_ci struct usb_device *usbdev = line6->usbdev; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ticks = kmalloc(sizeof(*ticks), GFP_KERNEL); 3678c2ecf20Sopenharmony_ci if (!ticks) 3688c2ecf20Sopenharmony_ci return -ENOMEM; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* sync time on device with host: */ 3718c2ecf20Sopenharmony_ci /* note: 32-bit timestamps overflow in year 2106 */ 3728c2ecf20Sopenharmony_ci *ticks = (u32)ktime_get_real_seconds(); 3738c2ecf20Sopenharmony_ci line6_write_data(line6, 0x80c6, ticks, 4); 3748c2ecf20Sopenharmony_ci kfree(ticks); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* enable device: */ 3778c2ecf20Sopenharmony_ci toneport_send_cmd(usbdev, 0x0301, 0x0000); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* initialize source select: */ 3808c2ecf20Sopenharmony_ci if (toneport_has_source_select(toneport)) 3818c2ecf20Sopenharmony_ci toneport_send_cmd(usbdev, 3828c2ecf20Sopenharmony_ci toneport_source_info[toneport->source].code, 3838c2ecf20Sopenharmony_ci 0x0000); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (toneport_has_led(toneport)) 3868c2ecf20Sopenharmony_ci toneport_update_led(toneport); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci schedule_delayed_work(&toneport->line6.startup_work, 3898c2ecf20Sopenharmony_ci msecs_to_jiffies(TONEPORT_PCM_DELAY * 1000)); 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* 3948c2ecf20Sopenharmony_ci Toneport device disconnected. 3958c2ecf20Sopenharmony_ci*/ 3968c2ecf20Sopenharmony_cistatic void line6_toneport_disconnect(struct usb_line6 *line6) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct usb_line6_toneport *toneport = line6_to_toneport(line6); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (toneport_has_led(toneport)) 4018c2ecf20Sopenharmony_ci toneport_remove_leds(toneport); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* 4068c2ecf20Sopenharmony_ci Try to init Toneport device. 4078c2ecf20Sopenharmony_ci*/ 4088c2ecf20Sopenharmony_cistatic int toneport_init(struct usb_line6 *line6, 4098c2ecf20Sopenharmony_ci const struct usb_device_id *id) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci int err; 4128c2ecf20Sopenharmony_ci struct usb_line6_toneport *toneport = line6_to_toneport(line6); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci toneport->type = id->driver_info; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci line6->disconnect = line6_toneport_disconnect; 4178c2ecf20Sopenharmony_ci line6->startup = toneport_startup; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* initialize PCM subsystem: */ 4208c2ecf20Sopenharmony_ci err = line6_init_pcm(line6, &toneport_pcm_properties); 4218c2ecf20Sopenharmony_ci if (err < 0) 4228c2ecf20Sopenharmony_ci return err; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* register monitor control: */ 4258c2ecf20Sopenharmony_ci err = snd_ctl_add(line6->card, 4268c2ecf20Sopenharmony_ci snd_ctl_new1(&toneport_control_monitor, 4278c2ecf20Sopenharmony_ci line6->line6pcm)); 4288c2ecf20Sopenharmony_ci if (err < 0) 4298c2ecf20Sopenharmony_ci return err; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* register source select control: */ 4328c2ecf20Sopenharmony_ci if (toneport_has_source_select(toneport)) { 4338c2ecf20Sopenharmony_ci err = 4348c2ecf20Sopenharmony_ci snd_ctl_add(line6->card, 4358c2ecf20Sopenharmony_ci snd_ctl_new1(&toneport_control_source, 4368c2ecf20Sopenharmony_ci line6->line6pcm)); 4378c2ecf20Sopenharmony_ci if (err < 0) 4388c2ecf20Sopenharmony_ci return err; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci line6_read_serial_number(line6, &toneport->serial_number); 4428c2ecf20Sopenharmony_ci line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (toneport_has_led(toneport)) { 4458c2ecf20Sopenharmony_ci err = toneport_init_leds(toneport); 4468c2ecf20Sopenharmony_ci if (err < 0) 4478c2ecf20Sopenharmony_ci return err; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci err = toneport_setup(toneport); 4518c2ecf20Sopenharmony_ci if (err) 4528c2ecf20Sopenharmony_ci return err; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci /* register audio system: */ 4558c2ecf20Sopenharmony_ci return snd_card_register(line6->card); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4598c2ecf20Sopenharmony_ci/* 4608c2ecf20Sopenharmony_ci Resume Toneport device after reset. 4618c2ecf20Sopenharmony_ci*/ 4628c2ecf20Sopenharmony_cistatic int toneport_reset_resume(struct usb_interface *interface) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci int err; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci err = toneport_setup(usb_get_intfdata(interface)); 4678c2ecf20Sopenharmony_ci if (err) 4688c2ecf20Sopenharmony_ci return err; 4698c2ecf20Sopenharmony_ci return line6_resume(interface); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci#endif 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) 4748c2ecf20Sopenharmony_ci#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci/* table of devices that work with this driver */ 4778c2ecf20Sopenharmony_cistatic const struct usb_device_id toneport_id_table[] = { 4788c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x4750), .driver_info = LINE6_GUITARPORT }, 4798c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x4153), .driver_info = LINE6_PODSTUDIO_GX }, 4808c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x4150), .driver_info = LINE6_PODSTUDIO_UX1 }, 4818c2ecf20Sopenharmony_ci { LINE6_IF_NUM(0x4151, 0), .driver_info = LINE6_PODSTUDIO_UX2 }, 4828c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x4147), .driver_info = LINE6_TONEPORT_GX }, 4838c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x4141), .driver_info = LINE6_TONEPORT_UX1 }, 4848c2ecf20Sopenharmony_ci { LINE6_IF_NUM(0x4142, 0), .driver_info = LINE6_TONEPORT_UX2 }, 4858c2ecf20Sopenharmony_ci {} 4868c2ecf20Sopenharmony_ci}; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, toneport_id_table); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic const struct line6_properties toneport_properties_table[] = { 4918c2ecf20Sopenharmony_ci [LINE6_GUITARPORT] = { 4928c2ecf20Sopenharmony_ci .id = "GuitarPort", 4938c2ecf20Sopenharmony_ci .name = "GuitarPort", 4948c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_PCM, 4958c2ecf20Sopenharmony_ci .altsetting = 2, /* 1..4 seem to be ok */ 4968c2ecf20Sopenharmony_ci /* no control channel */ 4978c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 4988c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 4998c2ecf20Sopenharmony_ci }, 5008c2ecf20Sopenharmony_ci [LINE6_PODSTUDIO_GX] = { 5018c2ecf20Sopenharmony_ci .id = "PODStudioGX", 5028c2ecf20Sopenharmony_ci .name = "POD Studio GX", 5038c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_PCM, 5048c2ecf20Sopenharmony_ci .altsetting = 2, /* 1..4 seem to be ok */ 5058c2ecf20Sopenharmony_ci /* no control channel */ 5068c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 5078c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 5088c2ecf20Sopenharmony_ci }, 5098c2ecf20Sopenharmony_ci [LINE6_PODSTUDIO_UX1] = { 5108c2ecf20Sopenharmony_ci .id = "PODStudioUX1", 5118c2ecf20Sopenharmony_ci .name = "POD Studio UX1", 5128c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_PCM, 5138c2ecf20Sopenharmony_ci .altsetting = 2, /* 1..4 seem to be ok */ 5148c2ecf20Sopenharmony_ci /* no control channel */ 5158c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 5168c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 5178c2ecf20Sopenharmony_ci }, 5188c2ecf20Sopenharmony_ci [LINE6_PODSTUDIO_UX2] = { 5198c2ecf20Sopenharmony_ci .id = "PODStudioUX2", 5208c2ecf20Sopenharmony_ci .name = "POD Studio UX2", 5218c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_PCM, 5228c2ecf20Sopenharmony_ci .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ 5238c2ecf20Sopenharmony_ci /* no control channel */ 5248c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 5258c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 5268c2ecf20Sopenharmony_ci }, 5278c2ecf20Sopenharmony_ci [LINE6_TONEPORT_GX] = { 5288c2ecf20Sopenharmony_ci .id = "TonePortGX", 5298c2ecf20Sopenharmony_ci .name = "TonePort GX", 5308c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_PCM, 5318c2ecf20Sopenharmony_ci .altsetting = 2, /* 1..4 seem to be ok */ 5328c2ecf20Sopenharmony_ci /* no control channel */ 5338c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 5348c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 5358c2ecf20Sopenharmony_ci }, 5368c2ecf20Sopenharmony_ci [LINE6_TONEPORT_UX1] = { 5378c2ecf20Sopenharmony_ci .id = "TonePortUX1", 5388c2ecf20Sopenharmony_ci .name = "TonePort UX1", 5398c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_PCM, 5408c2ecf20Sopenharmony_ci .altsetting = 2, /* 1..4 seem to be ok */ 5418c2ecf20Sopenharmony_ci /* no control channel */ 5428c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 5438c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 5448c2ecf20Sopenharmony_ci }, 5458c2ecf20Sopenharmony_ci [LINE6_TONEPORT_UX2] = { 5468c2ecf20Sopenharmony_ci .id = "TonePortUX2", 5478c2ecf20Sopenharmony_ci .name = "TonePort UX2", 5488c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_PCM, 5498c2ecf20Sopenharmony_ci .altsetting = 2, /* defaults to 44.1kHz, 16-bit */ 5508c2ecf20Sopenharmony_ci /* no control channel */ 5518c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 5528c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 5538c2ecf20Sopenharmony_ci }, 5548c2ecf20Sopenharmony_ci}; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci/* 5578c2ecf20Sopenharmony_ci Probe USB device. 5588c2ecf20Sopenharmony_ci*/ 5598c2ecf20Sopenharmony_cistatic int toneport_probe(struct usb_interface *interface, 5608c2ecf20Sopenharmony_ci const struct usb_device_id *id) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci return line6_probe(interface, id, "Line6-TonePort", 5638c2ecf20Sopenharmony_ci &toneport_properties_table[id->driver_info], 5648c2ecf20Sopenharmony_ci toneport_init, sizeof(struct usb_line6_toneport)); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic struct usb_driver toneport_driver = { 5688c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 5698c2ecf20Sopenharmony_ci .probe = toneport_probe, 5708c2ecf20Sopenharmony_ci .disconnect = line6_disconnect, 5718c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5728c2ecf20Sopenharmony_ci .suspend = line6_suspend, 5738c2ecf20Sopenharmony_ci .resume = line6_resume, 5748c2ecf20Sopenharmony_ci .reset_resume = toneport_reset_resume, 5758c2ecf20Sopenharmony_ci#endif 5768c2ecf20Sopenharmony_ci .id_table = toneport_id_table, 5778c2ecf20Sopenharmony_ci}; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cimodule_usb_driver(toneport_driver); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TonePort USB driver"); 5828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 583