18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * bebob_maudio.c - a part of driver for BeBoB based devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014 Takashi Sakamoto 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "./bebob.h" 98c2ecf20Sopenharmony_ci#include <sound/control.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * Just powering on, Firewire 410/Audiophile/1814 and ProjectMix I/O wait to 138c2ecf20Sopenharmony_ci * download firmware blob. To enable these devices, drivers should upload 148c2ecf20Sopenharmony_ci * firmware blob and send a command to initialize configuration to factory 158c2ecf20Sopenharmony_ci * settings when completing uploading. Then these devices generate bus reset 168c2ecf20Sopenharmony_ci * and are recognized as new devices with the firmware. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * But with firmware version 5058 or later, the firmware is stored to flash 198c2ecf20Sopenharmony_ci * memory in the device and drivers can tell bootloader to load the firmware 208c2ecf20Sopenharmony_ci * by sending a cue. This cue must be sent one time. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * For streaming, both of output and input streams are needed for Firewire 410 238c2ecf20Sopenharmony_ci * and Ozonic. The single stream is OK for the other devices even if the clock 248c2ecf20Sopenharmony_ci * source is not SYT-Match (I note no devices use SYT-Match). 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Without streaming, the devices except for Firewire Audiophile can mix any 278c2ecf20Sopenharmony_ci * input and output. For this reason, Audiophile cannot be used as standalone 288c2ecf20Sopenharmony_ci * mixer. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Firewire 1814 and ProjectMix I/O uses special firmware. It will be freezed 318c2ecf20Sopenharmony_ci * when receiving any commands which the firmware can't understand. These 328c2ecf20Sopenharmony_ci * devices utilize completely different system to control. It is some 338c2ecf20Sopenharmony_ci * write-transaction directly into a certain address. All of addresses for mixer 348c2ecf20Sopenharmony_ci * functionality is between 0xffc700700000 to 0xffc70070009c. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Offset from information register */ 388c2ecf20Sopenharmony_ci#define INFO_OFFSET_SW_DATE 0x20 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Bootloader Protocol Version 1 */ 418c2ecf20Sopenharmony_ci#define MAUDIO_BOOTLOADER_CUE1 0x00000001 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * Initializing configuration to factory settings (= 0x1101), (swapped in line), 448c2ecf20Sopenharmony_ci * Command code is zero (= 0x00), 458c2ecf20Sopenharmony_ci * the number of operands is zero (= 0x00)(at least significant byte) 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci#define MAUDIO_BOOTLOADER_CUE2 0x01110000 488c2ecf20Sopenharmony_ci/* padding */ 498c2ecf20Sopenharmony_ci#define MAUDIO_BOOTLOADER_CUE3 0x00000000 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define MAUDIO_SPECIFIC_ADDRESS 0xffc700000000ULL 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define METER_OFFSET 0x00600000 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* some device has sync info after metering data */ 568c2ecf20Sopenharmony_ci#define METER_SIZE_SPECIAL 84 /* with sync info */ 578c2ecf20Sopenharmony_ci#define METER_SIZE_FW410 76 /* with sync info */ 588c2ecf20Sopenharmony_ci#define METER_SIZE_AUDIOPHILE 60 /* with sync info */ 598c2ecf20Sopenharmony_ci#define METER_SIZE_SOLO 52 /* with sync info */ 608c2ecf20Sopenharmony_ci#define METER_SIZE_OZONIC 48 618c2ecf20Sopenharmony_ci#define METER_SIZE_NRV10 80 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* labels for metering */ 648c2ecf20Sopenharmony_ci#define ANA_IN "Analog In" 658c2ecf20Sopenharmony_ci#define ANA_OUT "Analog Out" 668c2ecf20Sopenharmony_ci#define DIG_IN "Digital In" 678c2ecf20Sopenharmony_ci#define SPDIF_IN "S/PDIF In" 688c2ecf20Sopenharmony_ci#define ADAT_IN "ADAT In" 698c2ecf20Sopenharmony_ci#define DIG_OUT "Digital Out" 708c2ecf20Sopenharmony_ci#define SPDIF_OUT "S/PDIF Out" 718c2ecf20Sopenharmony_ci#define ADAT_OUT "ADAT Out" 728c2ecf20Sopenharmony_ci#define STRM_IN "Stream In" 738c2ecf20Sopenharmony_ci#define AUX_OUT "Aux Out" 748c2ecf20Sopenharmony_ci#define HP_OUT "HP Out" 758c2ecf20Sopenharmony_ci/* for NRV */ 768c2ecf20Sopenharmony_ci#define UNKNOWN_METER "Unknown" 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistruct special_params { 798c2ecf20Sopenharmony_ci bool is1814; 808c2ecf20Sopenharmony_ci unsigned int clk_src; 818c2ecf20Sopenharmony_ci unsigned int dig_in_fmt; 828c2ecf20Sopenharmony_ci unsigned int dig_out_fmt; 838c2ecf20Sopenharmony_ci unsigned int clk_lock; 848c2ecf20Sopenharmony_ci struct snd_ctl_elem_id *ctl_id_sync; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * For some M-Audio devices, this module just send cue to load firmware. After 898c2ecf20Sopenharmony_ci * loading, the device generates bus reset and newly detected. 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * If we make any transactions to load firmware, the operation may failed. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ciint snd_bebob_maudio_load_firmware(struct fw_unit *unit) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct fw_device *device = fw_parent_device(unit); 968c2ecf20Sopenharmony_ci int err, rcode; 978c2ecf20Sopenharmony_ci u64 date; 988c2ecf20Sopenharmony_ci __le32 *cues; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* check date of software used to build */ 1018c2ecf20Sopenharmony_ci err = snd_bebob_read_block(unit, INFO_OFFSET_SW_DATE, 1028c2ecf20Sopenharmony_ci &date, sizeof(u64)); 1038c2ecf20Sopenharmony_ci if (err < 0) 1048c2ecf20Sopenharmony_ci return err; 1058c2ecf20Sopenharmony_ci /* 1068c2ecf20Sopenharmony_ci * firmware version 5058 or later has date later than "20070401", but 1078c2ecf20Sopenharmony_ci * 'date' is not null-terminated. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci if (date < 0x3230303730343031LL) { 1108c2ecf20Sopenharmony_ci dev_err(&unit->device, 1118c2ecf20Sopenharmony_ci "Use firmware version 5058 or later\n"); 1128c2ecf20Sopenharmony_ci return -ENXIO; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci cues = kmalloc_array(3, sizeof(*cues), GFP_KERNEL); 1168c2ecf20Sopenharmony_ci if (!cues) 1178c2ecf20Sopenharmony_ci return -ENOMEM; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci cues[0] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE1); 1208c2ecf20Sopenharmony_ci cues[1] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE2); 1218c2ecf20Sopenharmony_ci cues[2] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE3); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci rcode = fw_run_transaction(device->card, TCODE_WRITE_BLOCK_REQUEST, 1248c2ecf20Sopenharmony_ci device->node_id, device->generation, 1258c2ecf20Sopenharmony_ci device->max_speed, BEBOB_ADDR_REG_REQ, 1268c2ecf20Sopenharmony_ci cues, 3 * sizeof(*cues)); 1278c2ecf20Sopenharmony_ci kfree(cues); 1288c2ecf20Sopenharmony_ci if (rcode != RCODE_COMPLETE) { 1298c2ecf20Sopenharmony_ci dev_err(&unit->device, 1308c2ecf20Sopenharmony_ci "Failed to send a cue to load firmware\n"); 1318c2ecf20Sopenharmony_ci err = -EIO; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return err; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic inline int 1388c2ecf20Sopenharmony_ciget_meter(struct snd_bebob *bebob, void *buf, unsigned int size) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST, 1418c2ecf20Sopenharmony_ci MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET, 1428c2ecf20Sopenharmony_ci buf, size, 0); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int 1468c2ecf20Sopenharmony_cicheck_clk_sync(struct snd_bebob *bebob, unsigned int size, bool *sync) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci int err; 1498c2ecf20Sopenharmony_ci u8 *buf; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci buf = kmalloc(size, GFP_KERNEL); 1528c2ecf20Sopenharmony_ci if (buf == NULL) 1538c2ecf20Sopenharmony_ci return -ENOMEM; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci err = get_meter(bebob, buf, size); 1568c2ecf20Sopenharmony_ci if (err < 0) 1578c2ecf20Sopenharmony_ci goto end; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* if synced, this value is the same as SFC of FDF in CIP header */ 1608c2ecf20Sopenharmony_ci *sync = (buf[size - 2] != 0xff); 1618c2ecf20Sopenharmony_ciend: 1628c2ecf20Sopenharmony_ci kfree(buf); 1638c2ecf20Sopenharmony_ci return err; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/* 1678c2ecf20Sopenharmony_ci * dig_fmt: 0x00:S/PDIF, 0x01:ADAT 1688c2ecf20Sopenharmony_ci * clk_lock: 0x00:unlock, 0x01:lock 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic int 1718c2ecf20Sopenharmony_ciavc_maudio_set_special_clk(struct snd_bebob *bebob, unsigned int clk_src, 1728c2ecf20Sopenharmony_ci unsigned int dig_in_fmt, unsigned int dig_out_fmt, 1738c2ecf20Sopenharmony_ci unsigned int clk_lock) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 1768c2ecf20Sopenharmony_ci int err; 1778c2ecf20Sopenharmony_ci u8 *buf; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (amdtp_stream_running(&bebob->rx_stream) || 1808c2ecf20Sopenharmony_ci amdtp_stream_running(&bebob->tx_stream)) 1818c2ecf20Sopenharmony_ci return -EBUSY; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci buf = kmalloc(12, GFP_KERNEL); 1848c2ecf20Sopenharmony_ci if (buf == NULL) 1858c2ecf20Sopenharmony_ci return -ENOMEM; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci buf[0] = 0x00; /* CONTROL */ 1888c2ecf20Sopenharmony_ci buf[1] = 0xff; /* UNIT */ 1898c2ecf20Sopenharmony_ci buf[2] = 0x00; /* vendor dependent */ 1908c2ecf20Sopenharmony_ci buf[3] = 0x04; /* company ID high */ 1918c2ecf20Sopenharmony_ci buf[4] = 0x00; /* company ID middle */ 1928c2ecf20Sopenharmony_ci buf[5] = 0x04; /* company ID low */ 1938c2ecf20Sopenharmony_ci buf[6] = 0xff & clk_src; /* clock source */ 1948c2ecf20Sopenharmony_ci buf[7] = 0xff & dig_in_fmt; /* input digital format */ 1958c2ecf20Sopenharmony_ci buf[8] = 0xff & dig_out_fmt; /* output digital format */ 1968c2ecf20Sopenharmony_ci buf[9] = 0xff & clk_lock; /* lock these settings */ 1978c2ecf20Sopenharmony_ci buf[10] = 0x00; /* padding */ 1988c2ecf20Sopenharmony_ci buf[11] = 0x00; /* padding */ 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci err = fcp_avc_transaction(bebob->unit, buf, 12, buf, 12, 2018c2ecf20Sopenharmony_ci BIT(1) | BIT(2) | BIT(3) | BIT(4) | 2028c2ecf20Sopenharmony_ci BIT(5) | BIT(6) | BIT(7) | BIT(8) | 2038c2ecf20Sopenharmony_ci BIT(9)); 2048c2ecf20Sopenharmony_ci if ((err > 0) && (err < 10)) 2058c2ecf20Sopenharmony_ci err = -EIO; 2068c2ecf20Sopenharmony_ci else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ 2078c2ecf20Sopenharmony_ci err = -ENOSYS; 2088c2ecf20Sopenharmony_ci else if (buf[0] == 0x0a) /* REJECTED */ 2098c2ecf20Sopenharmony_ci err = -EINVAL; 2108c2ecf20Sopenharmony_ci if (err < 0) 2118c2ecf20Sopenharmony_ci goto end; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci params->clk_src = buf[6]; 2148c2ecf20Sopenharmony_ci params->dig_in_fmt = buf[7]; 2158c2ecf20Sopenharmony_ci params->dig_out_fmt = buf[8]; 2168c2ecf20Sopenharmony_ci params->clk_lock = buf[9]; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (params->ctl_id_sync) 2198c2ecf20Sopenharmony_ci snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE, 2208c2ecf20Sopenharmony_ci params->ctl_id_sync); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci err = 0; 2238c2ecf20Sopenharmony_ciend: 2248c2ecf20Sopenharmony_ci kfree(buf); 2258c2ecf20Sopenharmony_ci return err; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_cistatic void 2288c2ecf20Sopenharmony_cispecial_stream_formation_set(struct snd_bebob *bebob) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci static const unsigned int ch_table[2][2][3] = { 2318c2ecf20Sopenharmony_ci /* AMDTP_OUT_STREAM */ 2328c2ecf20Sopenharmony_ci { { 6, 6, 4 }, /* SPDIF */ 2338c2ecf20Sopenharmony_ci { 12, 8, 4 } }, /* ADAT */ 2348c2ecf20Sopenharmony_ci /* AMDTP_IN_STREAM */ 2358c2ecf20Sopenharmony_ci { { 10, 10, 2 }, /* SPDIF */ 2368c2ecf20Sopenharmony_ci { 16, 12, 2 } } /* ADAT */ 2378c2ecf20Sopenharmony_ci }; 2388c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 2398c2ecf20Sopenharmony_ci unsigned int i, max; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci max = SND_BEBOB_STRM_FMT_ENTRIES - 1; 2428c2ecf20Sopenharmony_ci if (!params->is1814) 2438c2ecf20Sopenharmony_ci max -= 2; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci for (i = 0; i < max; i++) { 2468c2ecf20Sopenharmony_ci bebob->tx_stream_formations[i + 1].pcm = 2478c2ecf20Sopenharmony_ci ch_table[AMDTP_IN_STREAM][params->dig_in_fmt][i / 2]; 2488c2ecf20Sopenharmony_ci bebob->tx_stream_formations[i + 1].midi = 1; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci bebob->rx_stream_formations[i + 1].pcm = 2518c2ecf20Sopenharmony_ci ch_table[AMDTP_OUT_STREAM][params->dig_out_fmt][i / 2]; 2528c2ecf20Sopenharmony_ci bebob->rx_stream_formations[i + 1].midi = 1; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int add_special_controls(struct snd_bebob *bebob); 2578c2ecf20Sopenharmony_ciint 2588c2ecf20Sopenharmony_cisnd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct special_params *params; 2618c2ecf20Sopenharmony_ci int err; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci params = devm_kzalloc(&bebob->card->card_dev, 2648c2ecf20Sopenharmony_ci sizeof(struct special_params), GFP_KERNEL); 2658c2ecf20Sopenharmony_ci if (!params) 2668c2ecf20Sopenharmony_ci return -ENOMEM; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci mutex_lock(&bebob->mutex); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci bebob->maudio_special_quirk = (void *)params; 2718c2ecf20Sopenharmony_ci params->is1814 = is1814; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* initialize these parameters because driver is not allowed to ask */ 2748c2ecf20Sopenharmony_ci bebob->rx_stream.context = ERR_PTR(-1); 2758c2ecf20Sopenharmony_ci bebob->tx_stream.context = ERR_PTR(-1); 2768c2ecf20Sopenharmony_ci err = avc_maudio_set_special_clk(bebob, 0x03, 0x00, 0x00, 0x00); 2778c2ecf20Sopenharmony_ci if (err < 0) { 2788c2ecf20Sopenharmony_ci dev_err(&bebob->unit->device, 2798c2ecf20Sopenharmony_ci "fail to initialize clock params: %d\n", err); 2808c2ecf20Sopenharmony_ci goto end; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci err = add_special_controls(bebob); 2848c2ecf20Sopenharmony_ci if (err < 0) 2858c2ecf20Sopenharmony_ci goto end; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci special_stream_formation_set(bebob); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (params->is1814) { 2908c2ecf20Sopenharmony_ci bebob->midi_input_ports = 1; 2918c2ecf20Sopenharmony_ci bebob->midi_output_ports = 1; 2928c2ecf20Sopenharmony_ci } else { 2938c2ecf20Sopenharmony_ci bebob->midi_input_ports = 2; 2948c2ecf20Sopenharmony_ci bebob->midi_output_ports = 2; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ciend: 2978c2ecf20Sopenharmony_ci mutex_unlock(&bebob->mutex); 2988c2ecf20Sopenharmony_ci return err; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* Input plug shows actual rate. Output plug is needless for this purpose. */ 3028c2ecf20Sopenharmony_cistatic int special_get_rate(struct snd_bebob *bebob, unsigned int *rate) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int err, trials; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci trials = 0; 3078c2ecf20Sopenharmony_ci do { 3088c2ecf20Sopenharmony_ci err = avc_general_get_sig_fmt(bebob->unit, rate, 3098c2ecf20Sopenharmony_ci AVC_GENERAL_PLUG_DIR_IN, 0); 3108c2ecf20Sopenharmony_ci } while (err == -EAGAIN && ++trials < 3); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return err; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_cistatic int special_set_rate(struct snd_bebob *bebob, unsigned int rate) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 3178c2ecf20Sopenharmony_ci int err; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci err = avc_general_set_sig_fmt(bebob->unit, rate, 3208c2ecf20Sopenharmony_ci AVC_GENERAL_PLUG_DIR_OUT, 0); 3218c2ecf20Sopenharmony_ci if (err < 0) 3228c2ecf20Sopenharmony_ci goto end; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* 3258c2ecf20Sopenharmony_ci * Just after changing sampling rate for output, a followed command 3268c2ecf20Sopenharmony_ci * for input is easy to fail. This is a workaround fot this issue. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci msleep(100); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci err = avc_general_set_sig_fmt(bebob->unit, rate, 3318c2ecf20Sopenharmony_ci AVC_GENERAL_PLUG_DIR_IN, 0); 3328c2ecf20Sopenharmony_ci if (err < 0) 3338c2ecf20Sopenharmony_ci goto end; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (params->ctl_id_sync) 3368c2ecf20Sopenharmony_ci snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE, 3378c2ecf20Sopenharmony_ci params->ctl_id_sync); 3388c2ecf20Sopenharmony_ciend: 3398c2ecf20Sopenharmony_ci return err; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/* Clock source control for special firmware */ 3438c2ecf20Sopenharmony_cistatic const enum snd_bebob_clock_type special_clk_types[] = { 3448c2ecf20Sopenharmony_ci SND_BEBOB_CLOCK_TYPE_INTERNAL, /* With digital mute */ 3458c2ecf20Sopenharmony_ci SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* SPDIF/ADAT */ 3468c2ecf20Sopenharmony_ci SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */ 3478c2ecf20Sopenharmony_ci SND_BEBOB_CLOCK_TYPE_INTERNAL, 3488c2ecf20Sopenharmony_ci}; 3498c2ecf20Sopenharmony_cistatic int special_clk_get(struct snd_bebob *bebob, unsigned int *id) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 3528c2ecf20Sopenharmony_ci *id = params->clk_src; 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_cistatic int special_clk_ctl_info(struct snd_kcontrol *kctl, 3568c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *einf) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci static const char *const special_clk_labels[] = { 3598c2ecf20Sopenharmony_ci "Internal with Digital Mute", 3608c2ecf20Sopenharmony_ci "Digital", 3618c2ecf20Sopenharmony_ci "Word Clock", 3628c2ecf20Sopenharmony_ci "Internal" 3638c2ecf20Sopenharmony_ci }; 3648c2ecf20Sopenharmony_ci return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_types), 3658c2ecf20Sopenharmony_ci special_clk_labels); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_cistatic int special_clk_ctl_get(struct snd_kcontrol *kctl, 3688c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uval) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct snd_bebob *bebob = snd_kcontrol_chip(kctl); 3718c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 3728c2ecf20Sopenharmony_ci uval->value.enumerated.item[0] = params->clk_src; 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_cistatic int special_clk_ctl_put(struct snd_kcontrol *kctl, 3768c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uval) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct snd_bebob *bebob = snd_kcontrol_chip(kctl); 3798c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 3808c2ecf20Sopenharmony_ci int err, id; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci id = uval->value.enumerated.item[0]; 3838c2ecf20Sopenharmony_ci if (id >= ARRAY_SIZE(special_clk_types)) 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci mutex_lock(&bebob->mutex); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci err = avc_maudio_set_special_clk(bebob, id, 3898c2ecf20Sopenharmony_ci params->dig_in_fmt, 3908c2ecf20Sopenharmony_ci params->dig_out_fmt, 3918c2ecf20Sopenharmony_ci params->clk_lock); 3928c2ecf20Sopenharmony_ci mutex_unlock(&bebob->mutex); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (err >= 0) 3958c2ecf20Sopenharmony_ci err = 1; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return err; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new special_clk_ctl = { 4008c2ecf20Sopenharmony_ci .name = "Clock Source", 4018c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4028c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 4038c2ecf20Sopenharmony_ci .info = special_clk_ctl_info, 4048c2ecf20Sopenharmony_ci .get = special_clk_ctl_get, 4058c2ecf20Sopenharmony_ci .put = special_clk_ctl_put 4068c2ecf20Sopenharmony_ci}; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci/* Clock synchronization control for special firmware */ 4098c2ecf20Sopenharmony_cistatic int special_sync_ctl_info(struct snd_kcontrol *kctl, 4108c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *einf) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci einf->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 4138c2ecf20Sopenharmony_ci einf->count = 1; 4148c2ecf20Sopenharmony_ci einf->value.integer.min = 0; 4158c2ecf20Sopenharmony_ci einf->value.integer.max = 1; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_cistatic int special_sync_ctl_get(struct snd_kcontrol *kctl, 4208c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uval) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct snd_bebob *bebob = snd_kcontrol_chip(kctl); 4238c2ecf20Sopenharmony_ci int err; 4248c2ecf20Sopenharmony_ci bool synced = 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci err = check_clk_sync(bebob, METER_SIZE_SPECIAL, &synced); 4278c2ecf20Sopenharmony_ci if (err >= 0) 4288c2ecf20Sopenharmony_ci uval->value.integer.value[0] = synced; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new special_sync_ctl = { 4338c2ecf20Sopenharmony_ci .name = "Sync Status", 4348c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4358c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 4368c2ecf20Sopenharmony_ci .info = special_sync_ctl_info, 4378c2ecf20Sopenharmony_ci .get = special_sync_ctl_get, 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci/* Digital input interface control for special firmware */ 4418c2ecf20Sopenharmony_cistatic const char *const special_dig_in_iface_labels[] = { 4428c2ecf20Sopenharmony_ci "S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical" 4438c2ecf20Sopenharmony_ci}; 4448c2ecf20Sopenharmony_cistatic int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl, 4458c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *einf) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci return snd_ctl_enum_info(einf, 1, 4488c2ecf20Sopenharmony_ci ARRAY_SIZE(special_dig_in_iface_labels), 4498c2ecf20Sopenharmony_ci special_dig_in_iface_labels); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_cistatic int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl, 4528c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uval) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct snd_bebob *bebob = snd_kcontrol_chip(kctl); 4558c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 4568c2ecf20Sopenharmony_ci unsigned int dig_in_iface; 4578c2ecf20Sopenharmony_ci int err, val; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci mutex_lock(&bebob->mutex); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci err = avc_audio_get_selector(bebob->unit, 0x00, 0x04, 4628c2ecf20Sopenharmony_ci &dig_in_iface); 4638c2ecf20Sopenharmony_ci if (err < 0) { 4648c2ecf20Sopenharmony_ci dev_err(&bebob->unit->device, 4658c2ecf20Sopenharmony_ci "fail to get digital input interface: %d\n", err); 4668c2ecf20Sopenharmony_ci goto end; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* encoded id for user value */ 4708c2ecf20Sopenharmony_ci val = (params->dig_in_fmt << 1) | (dig_in_iface & 0x01); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* for ADAT Optical */ 4738c2ecf20Sopenharmony_ci if (val > 2) 4748c2ecf20Sopenharmony_ci val = 2; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci uval->value.enumerated.item[0] = val; 4778c2ecf20Sopenharmony_ciend: 4788c2ecf20Sopenharmony_ci mutex_unlock(&bebob->mutex); 4798c2ecf20Sopenharmony_ci return err; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_cistatic int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl, 4828c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uval) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct snd_bebob *bebob = snd_kcontrol_chip(kctl); 4858c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 4868c2ecf20Sopenharmony_ci unsigned int id, dig_in_fmt, dig_in_iface; 4878c2ecf20Sopenharmony_ci int err; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci id = uval->value.enumerated.item[0]; 4908c2ecf20Sopenharmony_ci if (id >= ARRAY_SIZE(special_dig_in_iface_labels)) 4918c2ecf20Sopenharmony_ci return -EINVAL; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* decode user value */ 4948c2ecf20Sopenharmony_ci dig_in_fmt = (id >> 1) & 0x01; 4958c2ecf20Sopenharmony_ci dig_in_iface = id & 0x01; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci mutex_lock(&bebob->mutex); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci err = avc_maudio_set_special_clk(bebob, 5008c2ecf20Sopenharmony_ci params->clk_src, 5018c2ecf20Sopenharmony_ci dig_in_fmt, 5028c2ecf20Sopenharmony_ci params->dig_out_fmt, 5038c2ecf20Sopenharmony_ci params->clk_lock); 5048c2ecf20Sopenharmony_ci if (err < 0) 5058c2ecf20Sopenharmony_ci goto end; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* For ADAT, optical interface is only available. */ 5088c2ecf20Sopenharmony_ci if (params->dig_in_fmt > 0) { 5098c2ecf20Sopenharmony_ci err = 1; 5108c2ecf20Sopenharmony_ci goto end; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* For S/PDIF, optical/coaxial interfaces are selectable. */ 5148c2ecf20Sopenharmony_ci err = avc_audio_set_selector(bebob->unit, 0x00, 0x04, dig_in_iface); 5158c2ecf20Sopenharmony_ci if (err < 0) 5168c2ecf20Sopenharmony_ci dev_err(&bebob->unit->device, 5178c2ecf20Sopenharmony_ci "fail to set digital input interface: %d\n", err); 5188c2ecf20Sopenharmony_ci err = 1; 5198c2ecf20Sopenharmony_ciend: 5208c2ecf20Sopenharmony_ci special_stream_formation_set(bebob); 5218c2ecf20Sopenharmony_ci mutex_unlock(&bebob->mutex); 5228c2ecf20Sopenharmony_ci return err; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new special_dig_in_iface_ctl = { 5258c2ecf20Sopenharmony_ci .name = "Digital Input Interface", 5268c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5278c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 5288c2ecf20Sopenharmony_ci .info = special_dig_in_iface_ctl_info, 5298c2ecf20Sopenharmony_ci .get = special_dig_in_iface_ctl_get, 5308c2ecf20Sopenharmony_ci .put = special_dig_in_iface_ctl_set 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* Digital output interface control for special firmware */ 5348c2ecf20Sopenharmony_cistatic const char *const special_dig_out_iface_labels[] = { 5358c2ecf20Sopenharmony_ci "S/PDIF Optical and Coaxial", "ADAT Optical" 5368c2ecf20Sopenharmony_ci}; 5378c2ecf20Sopenharmony_cistatic int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl, 5388c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *einf) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci return snd_ctl_enum_info(einf, 1, 5418c2ecf20Sopenharmony_ci ARRAY_SIZE(special_dig_out_iface_labels), 5428c2ecf20Sopenharmony_ci special_dig_out_iface_labels); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_cistatic int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl, 5458c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uval) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct snd_bebob *bebob = snd_kcontrol_chip(kctl); 5488c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 5498c2ecf20Sopenharmony_ci mutex_lock(&bebob->mutex); 5508c2ecf20Sopenharmony_ci uval->value.enumerated.item[0] = params->dig_out_fmt; 5518c2ecf20Sopenharmony_ci mutex_unlock(&bebob->mutex); 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_cistatic int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl, 5558c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uval) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct snd_bebob *bebob = snd_kcontrol_chip(kctl); 5588c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 5598c2ecf20Sopenharmony_ci unsigned int id; 5608c2ecf20Sopenharmony_ci int err; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci id = uval->value.enumerated.item[0]; 5638c2ecf20Sopenharmony_ci if (id >= ARRAY_SIZE(special_dig_out_iface_labels)) 5648c2ecf20Sopenharmony_ci return -EINVAL; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci mutex_lock(&bebob->mutex); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci err = avc_maudio_set_special_clk(bebob, 5698c2ecf20Sopenharmony_ci params->clk_src, 5708c2ecf20Sopenharmony_ci params->dig_in_fmt, 5718c2ecf20Sopenharmony_ci id, params->clk_lock); 5728c2ecf20Sopenharmony_ci if (err >= 0) { 5738c2ecf20Sopenharmony_ci special_stream_formation_set(bebob); 5748c2ecf20Sopenharmony_ci err = 1; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci mutex_unlock(&bebob->mutex); 5788c2ecf20Sopenharmony_ci return err; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new special_dig_out_iface_ctl = { 5818c2ecf20Sopenharmony_ci .name = "Digital Output Interface", 5828c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5838c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 5848c2ecf20Sopenharmony_ci .info = special_dig_out_iface_ctl_info, 5858c2ecf20Sopenharmony_ci .get = special_dig_out_iface_ctl_get, 5868c2ecf20Sopenharmony_ci .put = special_dig_out_iface_ctl_set 5878c2ecf20Sopenharmony_ci}; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int add_special_controls(struct snd_bebob *bebob) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 5928c2ecf20Sopenharmony_ci struct special_params *params = bebob->maudio_special_quirk; 5938c2ecf20Sopenharmony_ci int err; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&special_clk_ctl, bebob); 5968c2ecf20Sopenharmony_ci err = snd_ctl_add(bebob->card, kctl); 5978c2ecf20Sopenharmony_ci if (err < 0) 5988c2ecf20Sopenharmony_ci goto end; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&special_sync_ctl, bebob); 6018c2ecf20Sopenharmony_ci err = snd_ctl_add(bebob->card, kctl); 6028c2ecf20Sopenharmony_ci if (err < 0) 6038c2ecf20Sopenharmony_ci goto end; 6048c2ecf20Sopenharmony_ci params->ctl_id_sync = &kctl->id; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&special_dig_in_iface_ctl, bebob); 6078c2ecf20Sopenharmony_ci err = snd_ctl_add(bebob->card, kctl); 6088c2ecf20Sopenharmony_ci if (err < 0) 6098c2ecf20Sopenharmony_ci goto end; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(&special_dig_out_iface_ctl, bebob); 6128c2ecf20Sopenharmony_ci err = snd_ctl_add(bebob->card, kctl); 6138c2ecf20Sopenharmony_ciend: 6148c2ecf20Sopenharmony_ci return err; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci/* Hardware metering for special firmware */ 6188c2ecf20Sopenharmony_cistatic const char *const special_meter_labels[] = { 6198c2ecf20Sopenharmony_ci ANA_IN, ANA_IN, ANA_IN, ANA_IN, 6208c2ecf20Sopenharmony_ci SPDIF_IN, 6218c2ecf20Sopenharmony_ci ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN, 6228c2ecf20Sopenharmony_ci ANA_OUT, ANA_OUT, 6238c2ecf20Sopenharmony_ci SPDIF_OUT, 6248c2ecf20Sopenharmony_ci ADAT_OUT, ADAT_OUT, ADAT_OUT, ADAT_OUT, 6258c2ecf20Sopenharmony_ci HP_OUT, HP_OUT, 6268c2ecf20Sopenharmony_ci AUX_OUT 6278c2ecf20Sopenharmony_ci}; 6288c2ecf20Sopenharmony_cistatic int 6298c2ecf20Sopenharmony_cispecial_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci __be16 *buf; 6328c2ecf20Sopenharmony_ci unsigned int i, c, channels; 6338c2ecf20Sopenharmony_ci int err; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci channels = ARRAY_SIZE(special_meter_labels) * 2; 6368c2ecf20Sopenharmony_ci if (size < channels * sizeof(u32)) 6378c2ecf20Sopenharmony_ci return -EINVAL; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* omit last 4 bytes because it's clock info. */ 6408c2ecf20Sopenharmony_ci buf = kmalloc(METER_SIZE_SPECIAL - 4, GFP_KERNEL); 6418c2ecf20Sopenharmony_ci if (buf == NULL) 6428c2ecf20Sopenharmony_ci return -ENOMEM; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci err = get_meter(bebob, (void *)buf, METER_SIZE_SPECIAL - 4); 6458c2ecf20Sopenharmony_ci if (err < 0) 6468c2ecf20Sopenharmony_ci goto end; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* Its format is u16 and some channels are unknown. */ 6498c2ecf20Sopenharmony_ci i = 0; 6508c2ecf20Sopenharmony_ci for (c = 2; c < channels + 2; c++) 6518c2ecf20Sopenharmony_ci target[i++] = be16_to_cpu(buf[c]) << 16; 6528c2ecf20Sopenharmony_ciend: 6538c2ecf20Sopenharmony_ci kfree(buf); 6548c2ecf20Sopenharmony_ci return err; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci/* last 4 bytes are omitted because it's clock info. */ 6588c2ecf20Sopenharmony_cistatic const char *const fw410_meter_labels[] = { 6598c2ecf20Sopenharmony_ci ANA_IN, DIG_IN, 6608c2ecf20Sopenharmony_ci ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT, 6618c2ecf20Sopenharmony_ci HP_OUT 6628c2ecf20Sopenharmony_ci}; 6638c2ecf20Sopenharmony_cistatic const char *const audiophile_meter_labels[] = { 6648c2ecf20Sopenharmony_ci ANA_IN, DIG_IN, 6658c2ecf20Sopenharmony_ci ANA_OUT, ANA_OUT, DIG_OUT, 6668c2ecf20Sopenharmony_ci HP_OUT, AUX_OUT, 6678c2ecf20Sopenharmony_ci}; 6688c2ecf20Sopenharmony_cistatic const char *const solo_meter_labels[] = { 6698c2ecf20Sopenharmony_ci ANA_IN, DIG_IN, 6708c2ecf20Sopenharmony_ci STRM_IN, STRM_IN, 6718c2ecf20Sopenharmony_ci ANA_OUT, DIG_OUT 6728c2ecf20Sopenharmony_ci}; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/* no clock info */ 6758c2ecf20Sopenharmony_cistatic const char *const ozonic_meter_labels[] = { 6768c2ecf20Sopenharmony_ci ANA_IN, ANA_IN, 6778c2ecf20Sopenharmony_ci STRM_IN, STRM_IN, 6788c2ecf20Sopenharmony_ci ANA_OUT, ANA_OUT 6798c2ecf20Sopenharmony_ci}; 6808c2ecf20Sopenharmony_ci/* TODO: need testers. these positions are based on authour's assumption */ 6818c2ecf20Sopenharmony_cistatic const char *const nrv10_meter_labels[] = { 6828c2ecf20Sopenharmony_ci ANA_IN, ANA_IN, ANA_IN, ANA_IN, 6838c2ecf20Sopenharmony_ci DIG_IN, 6848c2ecf20Sopenharmony_ci ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, 6858c2ecf20Sopenharmony_ci DIG_IN 6868c2ecf20Sopenharmony_ci}; 6878c2ecf20Sopenharmony_cistatic int 6888c2ecf20Sopenharmony_cinormal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci const struct snd_bebob_meter_spec *spec = bebob->spec->meter; 6918c2ecf20Sopenharmony_ci unsigned int c, channels; 6928c2ecf20Sopenharmony_ci int err; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci channels = spec->num * 2; 6958c2ecf20Sopenharmony_ci if (size < channels * sizeof(u32)) 6968c2ecf20Sopenharmony_ci return -EINVAL; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci err = get_meter(bebob, (void *)buf, size); 6998c2ecf20Sopenharmony_ci if (err < 0) 7008c2ecf20Sopenharmony_ci goto end; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci for (c = 0; c < channels; c++) 7038c2ecf20Sopenharmony_ci be32_to_cpus(&buf[c]); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* swap stream channels because inverted */ 7068c2ecf20Sopenharmony_ci if (spec->labels == solo_meter_labels) { 7078c2ecf20Sopenharmony_ci swap(buf[4], buf[6]); 7088c2ecf20Sopenharmony_ci swap(buf[5], buf[7]); 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ciend: 7118c2ecf20Sopenharmony_ci return err; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci/* for special customized devices */ 7158c2ecf20Sopenharmony_cistatic const struct snd_bebob_rate_spec special_rate_spec = { 7168c2ecf20Sopenharmony_ci .get = &special_get_rate, 7178c2ecf20Sopenharmony_ci .set = &special_set_rate, 7188c2ecf20Sopenharmony_ci}; 7198c2ecf20Sopenharmony_cistatic const struct snd_bebob_clock_spec special_clk_spec = { 7208c2ecf20Sopenharmony_ci .num = ARRAY_SIZE(special_clk_types), 7218c2ecf20Sopenharmony_ci .types = special_clk_types, 7228c2ecf20Sopenharmony_ci .get = &special_clk_get, 7238c2ecf20Sopenharmony_ci}; 7248c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec special_meter_spec = { 7258c2ecf20Sopenharmony_ci .num = ARRAY_SIZE(special_meter_labels), 7268c2ecf20Sopenharmony_ci .labels = special_meter_labels, 7278c2ecf20Sopenharmony_ci .get = &special_meter_get 7288c2ecf20Sopenharmony_ci}; 7298c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_special_spec = { 7308c2ecf20Sopenharmony_ci .clock = &special_clk_spec, 7318c2ecf20Sopenharmony_ci .rate = &special_rate_spec, 7328c2ecf20Sopenharmony_ci .meter = &special_meter_spec 7338c2ecf20Sopenharmony_ci}; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci/* Firewire 410 specification */ 7368c2ecf20Sopenharmony_cistatic const struct snd_bebob_rate_spec usual_rate_spec = { 7378c2ecf20Sopenharmony_ci .get = &snd_bebob_stream_get_rate, 7388c2ecf20Sopenharmony_ci .set = &snd_bebob_stream_set_rate, 7398c2ecf20Sopenharmony_ci}; 7408c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec fw410_meter_spec = { 7418c2ecf20Sopenharmony_ci .num = ARRAY_SIZE(fw410_meter_labels), 7428c2ecf20Sopenharmony_ci .labels = fw410_meter_labels, 7438c2ecf20Sopenharmony_ci .get = &normal_meter_get 7448c2ecf20Sopenharmony_ci}; 7458c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_fw410_spec = { 7468c2ecf20Sopenharmony_ci .clock = NULL, 7478c2ecf20Sopenharmony_ci .rate = &usual_rate_spec, 7488c2ecf20Sopenharmony_ci .meter = &fw410_meter_spec 7498c2ecf20Sopenharmony_ci}; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci/* Firewire Audiophile specification */ 7528c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec audiophile_meter_spec = { 7538c2ecf20Sopenharmony_ci .num = ARRAY_SIZE(audiophile_meter_labels), 7548c2ecf20Sopenharmony_ci .labels = audiophile_meter_labels, 7558c2ecf20Sopenharmony_ci .get = &normal_meter_get 7568c2ecf20Sopenharmony_ci}; 7578c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_audiophile_spec = { 7588c2ecf20Sopenharmony_ci .clock = NULL, 7598c2ecf20Sopenharmony_ci .rate = &usual_rate_spec, 7608c2ecf20Sopenharmony_ci .meter = &audiophile_meter_spec 7618c2ecf20Sopenharmony_ci}; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci/* Firewire Solo specification */ 7648c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec solo_meter_spec = { 7658c2ecf20Sopenharmony_ci .num = ARRAY_SIZE(solo_meter_labels), 7668c2ecf20Sopenharmony_ci .labels = solo_meter_labels, 7678c2ecf20Sopenharmony_ci .get = &normal_meter_get 7688c2ecf20Sopenharmony_ci}; 7698c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_solo_spec = { 7708c2ecf20Sopenharmony_ci .clock = NULL, 7718c2ecf20Sopenharmony_ci .rate = &usual_rate_spec, 7728c2ecf20Sopenharmony_ci .meter = &solo_meter_spec 7738c2ecf20Sopenharmony_ci}; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci/* Ozonic specification */ 7768c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec ozonic_meter_spec = { 7778c2ecf20Sopenharmony_ci .num = ARRAY_SIZE(ozonic_meter_labels), 7788c2ecf20Sopenharmony_ci .labels = ozonic_meter_labels, 7798c2ecf20Sopenharmony_ci .get = &normal_meter_get 7808c2ecf20Sopenharmony_ci}; 7818c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_ozonic_spec = { 7828c2ecf20Sopenharmony_ci .clock = NULL, 7838c2ecf20Sopenharmony_ci .rate = &usual_rate_spec, 7848c2ecf20Sopenharmony_ci .meter = &ozonic_meter_spec 7858c2ecf20Sopenharmony_ci}; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci/* NRV10 specification */ 7888c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec nrv10_meter_spec = { 7898c2ecf20Sopenharmony_ci .num = ARRAY_SIZE(nrv10_meter_labels), 7908c2ecf20Sopenharmony_ci .labels = nrv10_meter_labels, 7918c2ecf20Sopenharmony_ci .get = &normal_meter_get 7928c2ecf20Sopenharmony_ci}; 7938c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_nrv10_spec = { 7948c2ecf20Sopenharmony_ci .clock = NULL, 7958c2ecf20Sopenharmony_ci .rate = &usual_rate_spec, 7968c2ecf20Sopenharmony_ci .meter = &nrv10_meter_spec 7978c2ecf20Sopenharmony_ci}; 798