xref: /kernel/linux/linux-5.10/sound/usb/line6/pod.c (revision 8c2ecf20)
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