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 */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/wait.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/usb.h> 138c2ecf20Sopenharmony_ci 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_ci/* 228c2ecf20Sopenharmony_ci Locate name in binary program dump 238c2ecf20Sopenharmony_ci*/ 248c2ecf20Sopenharmony_ci#define POD_NAME_OFFSET 0 258c2ecf20Sopenharmony_ci#define POD_NAME_LENGTH 16 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci Other constants 298c2ecf20Sopenharmony_ci*/ 308c2ecf20Sopenharmony_ci#define POD_CONTROL_SIZE 0x80 318c2ecf20Sopenharmony_ci#define POD_BUFSIZE_DUMPREQ 7 328c2ecf20Sopenharmony_ci#define POD_STARTUP_DELAY 1000 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci Stages of POD startup procedure 368c2ecf20Sopenharmony_ci*/ 378c2ecf20Sopenharmony_cienum { 388c2ecf20Sopenharmony_ci POD_STARTUP_VERSIONREQ, 398c2ecf20Sopenharmony_ci POD_STARTUP_SETUP, 408c2ecf20Sopenharmony_ci POD_STARTUP_DONE, 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cienum { 448c2ecf20Sopenharmony_ci LINE6_BASSPODXT, 458c2ecf20Sopenharmony_ci LINE6_BASSPODXTLIVE, 468c2ecf20Sopenharmony_ci LINE6_BASSPODXTPRO, 478c2ecf20Sopenharmony_ci LINE6_POCKETPOD, 488c2ecf20Sopenharmony_ci LINE6_PODXT, 498c2ecf20Sopenharmony_ci LINE6_PODXTLIVE_POD, 508c2ecf20Sopenharmony_ci LINE6_PODXTPRO, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct usb_line6_pod { 548c2ecf20Sopenharmony_ci /* Generic Line 6 USB data */ 558c2ecf20Sopenharmony_ci struct usb_line6 line6; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* Instrument monitor level */ 588c2ecf20Sopenharmony_ci int monitor_level; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* Current progress in startup procedure */ 618c2ecf20Sopenharmony_ci int startup_progress; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* Serial number of device */ 648c2ecf20Sopenharmony_ci u32 serial_number; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Firmware version (x 100) */ 678c2ecf20Sopenharmony_ci int firmware_version; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* Device ID */ 708c2ecf20Sopenharmony_ci int device_id; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define line6_to_pod(x) container_of(x, struct usb_line6_pod, line6) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define POD_SYSEX_CODE 3 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* *INDENT-OFF* */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cienum { 808c2ecf20Sopenharmony_ci POD_SYSEX_SAVE = 0x24, 818c2ecf20Sopenharmony_ci POD_SYSEX_SYSTEM = 0x56, 828c2ecf20Sopenharmony_ci POD_SYSEX_SYSTEMREQ = 0x57, 838c2ecf20Sopenharmony_ci /* POD_SYSEX_UPDATE = 0x6c, */ /* software update! */ 848c2ecf20Sopenharmony_ci POD_SYSEX_STORE = 0x71, 858c2ecf20Sopenharmony_ci POD_SYSEX_FINISH = 0x72, 868c2ecf20Sopenharmony_ci POD_SYSEX_DUMPMEM = 0x73, 878c2ecf20Sopenharmony_ci POD_SYSEX_DUMP = 0x74, 888c2ecf20Sopenharmony_ci POD_SYSEX_DUMPREQ = 0x75 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* dumps entire internal memory of PODxt Pro */ 918c2ecf20Sopenharmony_ci /* POD_SYSEX_DUMPMEM2 = 0x76 */ 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cienum { 958c2ecf20Sopenharmony_ci POD_MONITOR_LEVEL = 0x04, 968c2ecf20Sopenharmony_ci POD_SYSTEM_INVALID = 0x10000 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* *INDENT-ON* */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cienum { 1028c2ecf20Sopenharmony_ci POD_DUMP_MEMORY = 2 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cienum { 1068c2ecf20Sopenharmony_ci POD_BUSY_READ, 1078c2ecf20Sopenharmony_ci POD_BUSY_WRITE, 1088c2ecf20Sopenharmony_ci POD_CHANNEL_DIRTY, 1098c2ecf20Sopenharmony_ci POD_SAVE_PRESSED, 1108c2ecf20Sopenharmony_ci POD_BUSY_MIDISEND 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic const struct snd_ratden pod_ratden = { 1148c2ecf20Sopenharmony_ci .num_min = 78125, 1158c2ecf20Sopenharmony_ci .num_max = 78125, 1168c2ecf20Sopenharmony_ci .num_step = 1, 1178c2ecf20Sopenharmony_ci .den = 2 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct line6_pcm_properties pod_pcm_properties = { 1218c2ecf20Sopenharmony_ci .playback_hw = { 1228c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 1238c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 1248c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 1258c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 1268c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | 1278c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 1288c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S24_3LE, 1298c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, 1308c2ecf20Sopenharmony_ci .rate_min = 39062, 1318c2ecf20Sopenharmony_ci .rate_max = 39063, 1328c2ecf20Sopenharmony_ci .channels_min = 2, 1338c2ecf20Sopenharmony_ci .channels_max = 2, 1348c2ecf20Sopenharmony_ci .buffer_bytes_max = 60000, 1358c2ecf20Sopenharmony_ci .period_bytes_min = 64, 1368c2ecf20Sopenharmony_ci .period_bytes_max = 8192, 1378c2ecf20Sopenharmony_ci .periods_min = 1, 1388c2ecf20Sopenharmony_ci .periods_max = 1024}, 1398c2ecf20Sopenharmony_ci .capture_hw = { 1408c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 1418c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 1428c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 1438c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 1448c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_SYNC_START), 1458c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S24_3LE, 1468c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, 1478c2ecf20Sopenharmony_ci .rate_min = 39062, 1488c2ecf20Sopenharmony_ci .rate_max = 39063, 1498c2ecf20Sopenharmony_ci .channels_min = 2, 1508c2ecf20Sopenharmony_ci .channels_max = 2, 1518c2ecf20Sopenharmony_ci .buffer_bytes_max = 60000, 1528c2ecf20Sopenharmony_ci .period_bytes_min = 64, 1538c2ecf20Sopenharmony_ci .period_bytes_max = 8192, 1548c2ecf20Sopenharmony_ci .periods_min = 1, 1558c2ecf20Sopenharmony_ci .periods_max = 1024}, 1568c2ecf20Sopenharmony_ci .rates = { 1578c2ecf20Sopenharmony_ci .nrats = 1, 1588c2ecf20Sopenharmony_ci .rats = &pod_ratden}, 1598c2ecf20Sopenharmony_ci .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */ 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic const char pod_version_header[] = { 1648c2ecf20Sopenharmony_ci 0xf0, 0x7e, 0x7f, 0x06, 0x02 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, 1688c2ecf20Sopenharmony_ci int size) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, 1718c2ecf20Sopenharmony_ci size); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci Process a completely received message. 1768c2ecf20Sopenharmony_ci*/ 1778c2ecf20Sopenharmony_cistatic void line6_pod_process_message(struct usb_line6 *line6) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct usb_line6_pod *pod = line6_to_pod(line6); 1808c2ecf20Sopenharmony_ci const unsigned char *buf = pod->line6.buffer_message; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { 1838c2ecf20Sopenharmony_ci pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; 1848c2ecf20Sopenharmony_ci pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | 1858c2ecf20Sopenharmony_ci (int) buf[10]; 1868c2ecf20Sopenharmony_ci if (pod->startup_progress == POD_STARTUP_VERSIONREQ) { 1878c2ecf20Sopenharmony_ci pod->startup_progress = POD_STARTUP_SETUP; 1888c2ecf20Sopenharmony_ci schedule_delayed_work(&line6->startup_work, 0); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci return; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Only look for sysex messages from this device */ 1948c2ecf20Sopenharmony_ci if (buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE) && 1958c2ecf20Sopenharmony_ci buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN)) { 1968c2ecf20Sopenharmony_ci return; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) != 0) 1998c2ecf20Sopenharmony_ci return; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (buf[5] == POD_SYSEX_SYSTEM && buf[6] == POD_MONITOR_LEVEL) { 2028c2ecf20Sopenharmony_ci short value = ((int)buf[7] << 12) | ((int)buf[8] << 8) | 2038c2ecf20Sopenharmony_ci ((int)buf[9] << 4) | (int)buf[10]; 2048c2ecf20Sopenharmony_ci pod->monitor_level = value; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* 2098c2ecf20Sopenharmony_ci Send system parameter (from integer). 2108c2ecf20Sopenharmony_ci*/ 2118c2ecf20Sopenharmony_cistatic int pod_set_system_param_int(struct usb_line6_pod *pod, int value, 2128c2ecf20Sopenharmony_ci int code) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci char *sysex; 2158c2ecf20Sopenharmony_ci static const int size = 5; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); 2188c2ecf20Sopenharmony_ci if (!sysex) 2198c2ecf20Sopenharmony_ci return -ENOMEM; 2208c2ecf20Sopenharmony_ci sysex[SYSEX_DATA_OFS] = code; 2218c2ecf20Sopenharmony_ci sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; 2228c2ecf20Sopenharmony_ci sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; 2238c2ecf20Sopenharmony_ci sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; 2248c2ecf20Sopenharmony_ci sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f; 2258c2ecf20Sopenharmony_ci line6_send_sysex_message(&pod->line6, sysex, size); 2268c2ecf20Sopenharmony_ci kfree(sysex); 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* 2318c2ecf20Sopenharmony_ci "read" request on "serial_number" special file. 2328c2ecf20Sopenharmony_ci*/ 2338c2ecf20Sopenharmony_cistatic ssize_t serial_number_show(struct device *dev, 2348c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct snd_card *card = dev_to_snd_card(dev); 2378c2ecf20Sopenharmony_ci struct usb_line6_pod *pod = card->private_data; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", pod->serial_number); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* 2438c2ecf20Sopenharmony_ci "read" request on "firmware_version" special file. 2448c2ecf20Sopenharmony_ci*/ 2458c2ecf20Sopenharmony_cistatic ssize_t firmware_version_show(struct device *dev, 2468c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct snd_card *card = dev_to_snd_card(dev); 2498c2ecf20Sopenharmony_ci struct usb_line6_pod *pod = card->private_data; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, 2528c2ecf20Sopenharmony_ci pod->firmware_version % 100); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* 2568c2ecf20Sopenharmony_ci "read" request on "device_id" special file. 2578c2ecf20Sopenharmony_ci*/ 2588c2ecf20Sopenharmony_cistatic ssize_t device_id_show(struct device *dev, 2598c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct snd_card *card = dev_to_snd_card(dev); 2628c2ecf20Sopenharmony_ci struct usb_line6_pod *pod = card->private_data; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", pod->device_id); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* 2688c2ecf20Sopenharmony_ci POD startup procedure. 2698c2ecf20Sopenharmony_ci This is a sequence of functions with special requirements (e.g., must 2708c2ecf20Sopenharmony_ci not run immediately after initialization, must not run in interrupt 2718c2ecf20Sopenharmony_ci context). After the last one has finished, the device is ready to use. 2728c2ecf20Sopenharmony_ci*/ 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void pod_startup(struct usb_line6 *line6) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct usb_line6_pod *pod = line6_to_pod(line6); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci switch (pod->startup_progress) { 2798c2ecf20Sopenharmony_ci case POD_STARTUP_VERSIONREQ: 2808c2ecf20Sopenharmony_ci /* request firmware version: */ 2818c2ecf20Sopenharmony_ci line6_version_request_async(line6); 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci case POD_STARTUP_SETUP: 2848c2ecf20Sopenharmony_ci /* serial number: */ 2858c2ecf20Sopenharmony_ci line6_read_serial_number(&pod->line6, &pod->serial_number); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* ALSA audio interface: */ 2888c2ecf20Sopenharmony_ci if (snd_card_register(line6->card)) 2898c2ecf20Sopenharmony_ci dev_err(line6->ifcdev, "Failed to register POD card.\n"); 2908c2ecf20Sopenharmony_ci pod->startup_progress = POD_STARTUP_DONE; 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci default: 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* POD special files: */ 2988c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(device_id); 2998c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(firmware_version); 3008c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(serial_number); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic struct attribute *pod_dev_attrs[] = { 3038c2ecf20Sopenharmony_ci &dev_attr_device_id.attr, 3048c2ecf20Sopenharmony_ci &dev_attr_firmware_version.attr, 3058c2ecf20Sopenharmony_ci &dev_attr_serial_number.attr, 3068c2ecf20Sopenharmony_ci NULL 3078c2ecf20Sopenharmony_ci}; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic const struct attribute_group pod_dev_attr_group = { 3108c2ecf20Sopenharmony_ci .name = "pod", 3118c2ecf20Sopenharmony_ci .attrs = pod_dev_attrs, 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* control info callback */ 3158c2ecf20Sopenharmony_cistatic int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, 3168c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3198c2ecf20Sopenharmony_ci uinfo->count = 1; 3208c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 3218c2ecf20Sopenharmony_ci uinfo->value.integer.max = 65535; 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* control get callback */ 3268c2ecf20Sopenharmony_cistatic int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, 3278c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 3308c2ecf20Sopenharmony_ci struct usb_line6_pod *pod = line6_to_pod(line6pcm->line6); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = pod->monitor_level; 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* control put callback */ 3378c2ecf20Sopenharmony_cistatic int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, 3388c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 3418c2ecf20Sopenharmony_ci struct usb_line6_pod *pod = line6_to_pod(line6pcm->line6); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0] == pod->monitor_level) 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci pod->monitor_level = ucontrol->value.integer.value[0]; 3478c2ecf20Sopenharmony_ci pod_set_system_param_int(pod, ucontrol->value.integer.value[0], 3488c2ecf20Sopenharmony_ci POD_MONITOR_LEVEL); 3498c2ecf20Sopenharmony_ci return 1; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci/* control definition */ 3538c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new pod_control_monitor = { 3548c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3558c2ecf20Sopenharmony_ci .name = "Monitor Playback Volume", 3568c2ecf20Sopenharmony_ci .index = 0, 3578c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 3588c2ecf20Sopenharmony_ci .info = snd_pod_control_monitor_info, 3598c2ecf20Sopenharmony_ci .get = snd_pod_control_monitor_get, 3608c2ecf20Sopenharmony_ci .put = snd_pod_control_monitor_put 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/* 3648c2ecf20Sopenharmony_ci Try to init POD device. 3658c2ecf20Sopenharmony_ci*/ 3668c2ecf20Sopenharmony_cistatic int pod_init(struct usb_line6 *line6, 3678c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci int err; 3708c2ecf20Sopenharmony_ci struct usb_line6_pod *pod = line6_to_pod(line6); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci line6->process_message = line6_pod_process_message; 3738c2ecf20Sopenharmony_ci line6->startup = pod_startup; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* create sysfs entries: */ 3768c2ecf20Sopenharmony_ci err = snd_card_add_dev_attr(line6->card, &pod_dev_attr_group); 3778c2ecf20Sopenharmony_ci if (err < 0) 3788c2ecf20Sopenharmony_ci return err; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* initialize PCM subsystem: */ 3818c2ecf20Sopenharmony_ci err = line6_init_pcm(line6, &pod_pcm_properties); 3828c2ecf20Sopenharmony_ci if (err < 0) 3838c2ecf20Sopenharmony_ci return err; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* register monitor control: */ 3868c2ecf20Sopenharmony_ci err = snd_ctl_add(line6->card, 3878c2ecf20Sopenharmony_ci snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); 3888c2ecf20Sopenharmony_ci if (err < 0) 3898c2ecf20Sopenharmony_ci return err; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci When the sound card is registered at this point, the PODxt Live 3938c2ecf20Sopenharmony_ci displays "Invalid Code Error 07", so we do it later in the event 3948c2ecf20Sopenharmony_ci handler. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { 3988c2ecf20Sopenharmony_ci pod->monitor_level = POD_SYSTEM_INVALID; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* initiate startup procedure: */ 4018c2ecf20Sopenharmony_ci schedule_delayed_work(&line6->startup_work, 4028c2ecf20Sopenharmony_ci msecs_to_jiffies(POD_STARTUP_DELAY)); 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) 4098c2ecf20Sopenharmony_ci#define LINE6_IF_NUM(prod, n) USB_DEVICE_INTERFACE_NUMBER(0x0e41, prod, n) 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/* table of devices that work with this driver */ 4128c2ecf20Sopenharmony_cistatic const struct usb_device_id pod_id_table[] = { 4138c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x4250), .driver_info = LINE6_BASSPODXT }, 4148c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x4642), .driver_info = LINE6_BASSPODXTLIVE }, 4158c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x4252), .driver_info = LINE6_BASSPODXTPRO }, 4168c2ecf20Sopenharmony_ci { LINE6_IF_NUM(0x5051, 1), .driver_info = LINE6_POCKETPOD }, 4178c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x5044), .driver_info = LINE6_PODXT }, 4188c2ecf20Sopenharmony_ci { LINE6_IF_NUM(0x4650, 0), .driver_info = LINE6_PODXTLIVE_POD }, 4198c2ecf20Sopenharmony_ci { LINE6_DEVICE(0x5050), .driver_info = LINE6_PODXTPRO }, 4208c2ecf20Sopenharmony_ci {} 4218c2ecf20Sopenharmony_ci}; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, pod_id_table); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic const struct line6_properties pod_properties_table[] = { 4268c2ecf20Sopenharmony_ci [LINE6_BASSPODXT] = { 4278c2ecf20Sopenharmony_ci .id = "BassPODxt", 4288c2ecf20Sopenharmony_ci .name = "BassPODxt", 4298c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_CONTROL 4308c2ecf20Sopenharmony_ci | LINE6_CAP_CONTROL_MIDI 4318c2ecf20Sopenharmony_ci | LINE6_CAP_PCM 4328c2ecf20Sopenharmony_ci | LINE6_CAP_HWMON, 4338c2ecf20Sopenharmony_ci .altsetting = 5, 4348c2ecf20Sopenharmony_ci .ep_ctrl_r = 0x84, 4358c2ecf20Sopenharmony_ci .ep_ctrl_w = 0x03, 4368c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 4378c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 4388c2ecf20Sopenharmony_ci }, 4398c2ecf20Sopenharmony_ci [LINE6_BASSPODXTLIVE] = { 4408c2ecf20Sopenharmony_ci .id = "BassPODxtLive", 4418c2ecf20Sopenharmony_ci .name = "BassPODxt Live", 4428c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_CONTROL 4438c2ecf20Sopenharmony_ci | LINE6_CAP_CONTROL_MIDI 4448c2ecf20Sopenharmony_ci | LINE6_CAP_PCM 4458c2ecf20Sopenharmony_ci | LINE6_CAP_HWMON, 4468c2ecf20Sopenharmony_ci .altsetting = 1, 4478c2ecf20Sopenharmony_ci .ep_ctrl_r = 0x84, 4488c2ecf20Sopenharmony_ci .ep_ctrl_w = 0x03, 4498c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 4508c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 4518c2ecf20Sopenharmony_ci }, 4528c2ecf20Sopenharmony_ci [LINE6_BASSPODXTPRO] = { 4538c2ecf20Sopenharmony_ci .id = "BassPODxtPro", 4548c2ecf20Sopenharmony_ci .name = "BassPODxt Pro", 4558c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_CONTROL 4568c2ecf20Sopenharmony_ci | LINE6_CAP_CONTROL_MIDI 4578c2ecf20Sopenharmony_ci | LINE6_CAP_PCM 4588c2ecf20Sopenharmony_ci | LINE6_CAP_HWMON, 4598c2ecf20Sopenharmony_ci .altsetting = 5, 4608c2ecf20Sopenharmony_ci .ep_ctrl_r = 0x84, 4618c2ecf20Sopenharmony_ci .ep_ctrl_w = 0x03, 4628c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 4638c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 4648c2ecf20Sopenharmony_ci }, 4658c2ecf20Sopenharmony_ci [LINE6_POCKETPOD] = { 4668c2ecf20Sopenharmony_ci .id = "PocketPOD", 4678c2ecf20Sopenharmony_ci .name = "Pocket POD", 4688c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_CONTROL 4698c2ecf20Sopenharmony_ci | LINE6_CAP_CONTROL_MIDI, 4708c2ecf20Sopenharmony_ci .altsetting = 0, 4718c2ecf20Sopenharmony_ci .ep_ctrl_r = 0x82, 4728c2ecf20Sopenharmony_ci .ep_ctrl_w = 0x02, 4738c2ecf20Sopenharmony_ci /* no audio channel */ 4748c2ecf20Sopenharmony_ci }, 4758c2ecf20Sopenharmony_ci [LINE6_PODXT] = { 4768c2ecf20Sopenharmony_ci .id = "PODxt", 4778c2ecf20Sopenharmony_ci .name = "PODxt", 4788c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_CONTROL 4798c2ecf20Sopenharmony_ci | LINE6_CAP_CONTROL_MIDI 4808c2ecf20Sopenharmony_ci | LINE6_CAP_PCM 4818c2ecf20Sopenharmony_ci | LINE6_CAP_HWMON, 4828c2ecf20Sopenharmony_ci .altsetting = 5, 4838c2ecf20Sopenharmony_ci .ep_ctrl_r = 0x84, 4848c2ecf20Sopenharmony_ci .ep_ctrl_w = 0x03, 4858c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 4868c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 4878c2ecf20Sopenharmony_ci }, 4888c2ecf20Sopenharmony_ci [LINE6_PODXTLIVE_POD] = { 4898c2ecf20Sopenharmony_ci .id = "PODxtLive", 4908c2ecf20Sopenharmony_ci .name = "PODxt Live", 4918c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_CONTROL 4928c2ecf20Sopenharmony_ci | LINE6_CAP_CONTROL_MIDI 4938c2ecf20Sopenharmony_ci | LINE6_CAP_PCM 4948c2ecf20Sopenharmony_ci | LINE6_CAP_HWMON, 4958c2ecf20Sopenharmony_ci .altsetting = 1, 4968c2ecf20Sopenharmony_ci .ep_ctrl_r = 0x84, 4978c2ecf20Sopenharmony_ci .ep_ctrl_w = 0x03, 4988c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 4998c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 5008c2ecf20Sopenharmony_ci }, 5018c2ecf20Sopenharmony_ci [LINE6_PODXTPRO] = { 5028c2ecf20Sopenharmony_ci .id = "PODxtPro", 5038c2ecf20Sopenharmony_ci .name = "PODxt Pro", 5048c2ecf20Sopenharmony_ci .capabilities = LINE6_CAP_CONTROL 5058c2ecf20Sopenharmony_ci | LINE6_CAP_CONTROL_MIDI 5068c2ecf20Sopenharmony_ci | LINE6_CAP_PCM 5078c2ecf20Sopenharmony_ci | LINE6_CAP_HWMON, 5088c2ecf20Sopenharmony_ci .altsetting = 5, 5098c2ecf20Sopenharmony_ci .ep_ctrl_r = 0x84, 5108c2ecf20Sopenharmony_ci .ep_ctrl_w = 0x03, 5118c2ecf20Sopenharmony_ci .ep_audio_r = 0x82, 5128c2ecf20Sopenharmony_ci .ep_audio_w = 0x01, 5138c2ecf20Sopenharmony_ci }, 5148c2ecf20Sopenharmony_ci}; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/* 5178c2ecf20Sopenharmony_ci Probe USB device. 5188c2ecf20Sopenharmony_ci*/ 5198c2ecf20Sopenharmony_cistatic int pod_probe(struct usb_interface *interface, 5208c2ecf20Sopenharmony_ci const struct usb_device_id *id) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci return line6_probe(interface, id, "Line6-POD", 5238c2ecf20Sopenharmony_ci &pod_properties_table[id->driver_info], 5248c2ecf20Sopenharmony_ci pod_init, sizeof(struct usb_line6_pod)); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic struct usb_driver pod_driver = { 5288c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 5298c2ecf20Sopenharmony_ci .probe = pod_probe, 5308c2ecf20Sopenharmony_ci .disconnect = line6_disconnect, 5318c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5328c2ecf20Sopenharmony_ci .suspend = line6_suspend, 5338c2ecf20Sopenharmony_ci .resume = line6_resume, 5348c2ecf20Sopenharmony_ci .reset_resume = line6_resume, 5358c2ecf20Sopenharmony_ci#endif 5368c2ecf20Sopenharmony_ci .id_table = pod_id_table, 5378c2ecf20Sopenharmony_ci}; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cimodule_usb_driver(pod_driver); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Line 6 POD USB driver"); 5428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 543