18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux driver for TerraTec DMX 6Fire USB 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Mixer control 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Torsten Schenk <torsten.schenk@zoho.com> 88c2ecf20Sopenharmony_ci * Created: Jan 01, 2011 98c2ecf20Sopenharmony_ci * Copyright: (C) Torsten Schenk 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Thanks to: 128c2ecf20Sopenharmony_ci * - Holger Ruckdeschel: he found out how to control individual channel 138c2ecf20Sopenharmony_ci * volumes and introduced mute switch 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <sound/control.h> 188c2ecf20Sopenharmony_ci#include <sound/tlv.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "control.h" 218c2ecf20Sopenharmony_ci#include "comm.h" 228c2ecf20Sopenharmony_ci#include "chip.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const char * const opt_coax_texts[2] = { "Optical", "Coax" }; 258c2ecf20Sopenharmony_cistatic const char * const line_phono_texts[2] = { "Line", "Phono" }; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * data that needs to be sent to device. sets up card internal stuff. 298c2ecf20Sopenharmony_ci * values dumped from windows driver and filtered by trial'n'error. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic const struct { 328c2ecf20Sopenharmony_ci u8 type; 338c2ecf20Sopenharmony_ci u8 reg; 348c2ecf20Sopenharmony_ci u8 value; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ciinit_data[] = { 378c2ecf20Sopenharmony_ci { 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 }, 388c2ecf20Sopenharmony_ci { 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 }, 398c2ecf20Sopenharmony_ci { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 }, 408c2ecf20Sopenharmony_ci { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 }, 418c2ecf20Sopenharmony_ci { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 }, 428c2ecf20Sopenharmony_ci { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, 438c2ecf20Sopenharmony_ci { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 }, 448c2ecf20Sopenharmony_ci { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 }, 458c2ecf20Sopenharmony_ci { 0 } /* TERMINATING ENTRY */ 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; 498c2ecf20Sopenharmony_ci/* values to write to soundcard register for all samplerates */ 508c2ecf20Sopenharmony_cistatic const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; 518c2ecf20Sopenharmony_cistatic const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0); 548c2ecf20Sopenharmony_cistatic DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cienum { 578c2ecf20Sopenharmony_ci DIGITAL_THRU_ONLY_SAMPLERATE = 3 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void usb6fire_control_output_vol_update(struct control_runtime *rt) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 638c2ecf20Sopenharmony_ci int i; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (comm_rt) 668c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 678c2ecf20Sopenharmony_ci if (!(rt->ovol_updated & (1 << i))) { 688c2ecf20Sopenharmony_ci comm_rt->write8(comm_rt, 0x12, 0x0f + i, 698c2ecf20Sopenharmony_ci 180 - rt->output_vol[i]); 708c2ecf20Sopenharmony_ci rt->ovol_updated |= 1 << i; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void usb6fire_control_output_mute_update(struct control_runtime *rt) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (comm_rt) 798c2ecf20Sopenharmony_ci comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void usb6fire_control_input_vol_update(struct control_runtime *rt) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 858c2ecf20Sopenharmony_ci int i; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (comm_rt) 888c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) 898c2ecf20Sopenharmony_ci if (!(rt->ivol_updated & (1 << i))) { 908c2ecf20Sopenharmony_ci comm_rt->write8(comm_rt, 0x12, 0x1c + i, 918c2ecf20Sopenharmony_ci rt->input_vol[i] & 0x3f); 928c2ecf20Sopenharmony_ci rt->ivol_updated |= 1 << i; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void usb6fire_control_line_phono_update(struct control_runtime *rt) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 998c2ecf20Sopenharmony_ci if (comm_rt) { 1008c2ecf20Sopenharmony_ci comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch); 1018c2ecf20Sopenharmony_ci comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void usb6fire_control_opt_coax_update(struct control_runtime *rt) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 1088c2ecf20Sopenharmony_ci if (comm_rt) { 1098c2ecf20Sopenharmony_ci comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch); 1108c2ecf20Sopenharmony_ci comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int usb6fire_control_set_rate(struct control_runtime *rt, int rate) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci int ret; 1178c2ecf20Sopenharmony_ci struct usb_device *device = rt->chip->dev; 1188c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (rate < 0 || rate >= CONTROL_N_RATES) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ret = usb_set_interface(device, 1, rates_altsetting[rate]); 1248c2ecf20Sopenharmony_ci if (ret < 0) 1258c2ecf20Sopenharmony_ci return ret; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* set soundcard clock */ 1288c2ecf20Sopenharmony_ci ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate], 1298c2ecf20Sopenharmony_ci rates_6fire_vh[rate]); 1308c2ecf20Sopenharmony_ci if (ret < 0) 1318c2ecf20Sopenharmony_ci return ret; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int usb6fire_control_set_channels( 1378c2ecf20Sopenharmony_ci struct control_runtime *rt, int n_analog_out, 1388c2ecf20Sopenharmony_ci int n_analog_in, bool spdif_out, bool spdif_in) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int ret; 1418c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* enable analog inputs and outputs 1448c2ecf20Sopenharmony_ci * (one bit per stereo-channel) */ 1458c2ecf20Sopenharmony_ci ret = comm_rt->write16(comm_rt, 0x02, 0x02, 1468c2ecf20Sopenharmony_ci (1 << (n_analog_out / 2)) - 1, 1478c2ecf20Sopenharmony_ci (1 << (n_analog_in / 2)) - 1); 1488c2ecf20Sopenharmony_ci if (ret < 0) 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* disable digital inputs and outputs */ 1528c2ecf20Sopenharmony_ci /* TODO: use spdif_x to enable/disable digital channels */ 1538c2ecf20Sopenharmony_ci ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); 1548c2ecf20Sopenharmony_ci if (ret < 0) 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int usb6fire_control_streaming_update(struct control_runtime *rt) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (comm_rt) { 1658c2ecf20Sopenharmony_ci if (!rt->usb_streaming && rt->digital_thru_switch) 1668c2ecf20Sopenharmony_ci usb6fire_control_set_rate(rt, 1678c2ecf20Sopenharmony_ci DIGITAL_THRU_ONLY_SAMPLERATE); 1688c2ecf20Sopenharmony_ci return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 1698c2ecf20Sopenharmony_ci (rt->usb_streaming ? 0x01 : 0x00) | 1708c2ecf20Sopenharmony_ci (rt->digital_thru_switch ? 0x08 : 0x00)); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol, 1768c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 1798c2ecf20Sopenharmony_ci uinfo->count = 2; 1808c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 1818c2ecf20Sopenharmony_ci uinfo->value.integer.max = 180; 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol, 1868c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 1898c2ecf20Sopenharmony_ci unsigned int ch = kcontrol->private_value; 1908c2ecf20Sopenharmony_ci int changed = 0; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (ch > 4) { 1938c2ecf20Sopenharmony_ci dev_err(&rt->chip->dev->dev, 1948c2ecf20Sopenharmony_ci "Invalid channel in volume control."); 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) { 1998c2ecf20Sopenharmony_ci rt->output_vol[ch] = ucontrol->value.integer.value[0]; 2008c2ecf20Sopenharmony_ci rt->ovol_updated &= ~(1 << ch); 2018c2ecf20Sopenharmony_ci changed = 1; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) { 2048c2ecf20Sopenharmony_ci rt->output_vol[ch + 1] = ucontrol->value.integer.value[1]; 2058c2ecf20Sopenharmony_ci rt->ovol_updated &= ~(2 << ch); 2068c2ecf20Sopenharmony_ci changed = 1; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (changed) 2108c2ecf20Sopenharmony_ci usb6fire_control_output_vol_update(rt); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return changed; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol, 2168c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 2198c2ecf20Sopenharmony_ci unsigned int ch = kcontrol->private_value; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (ch > 4) { 2228c2ecf20Sopenharmony_ci dev_err(&rt->chip->dev->dev, 2238c2ecf20Sopenharmony_ci "Invalid channel in volume control."); 2248c2ecf20Sopenharmony_ci return -EINVAL; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = rt->output_vol[ch]; 2288c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = rt->output_vol[ch + 1]; 2298c2ecf20Sopenharmony_ci return 0; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol, 2338c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 2368c2ecf20Sopenharmony_ci unsigned int ch = kcontrol->private_value; 2378c2ecf20Sopenharmony_ci u8 old = rt->output_mute; 2388c2ecf20Sopenharmony_ci u8 value = 0; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (ch > 4) { 2418c2ecf20Sopenharmony_ci dev_err(&rt->chip->dev->dev, 2428c2ecf20Sopenharmony_ci "Invalid channel in volume control."); 2438c2ecf20Sopenharmony_ci return -EINVAL; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci rt->output_mute &= ~(3 << ch); 2478c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0]) 2488c2ecf20Sopenharmony_ci value |= 1; 2498c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[1]) 2508c2ecf20Sopenharmony_ci value |= 2; 2518c2ecf20Sopenharmony_ci rt->output_mute |= value << ch; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (rt->output_mute != old) 2548c2ecf20Sopenharmony_ci usb6fire_control_output_mute_update(rt); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return rt->output_mute != old; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol, 2608c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 2638c2ecf20Sopenharmony_ci unsigned int ch = kcontrol->private_value; 2648c2ecf20Sopenharmony_ci u8 value = rt->output_mute >> ch; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (ch > 4) { 2678c2ecf20Sopenharmony_ci dev_err(&rt->chip->dev->dev, 2688c2ecf20Sopenharmony_ci "Invalid channel in volume control."); 2698c2ecf20Sopenharmony_ci return -EINVAL; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 1 & value; 2738c2ecf20Sopenharmony_ci value >>= 1; 2748c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = 1 & value; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol, 2808c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 2838c2ecf20Sopenharmony_ci uinfo->count = 2; 2848c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 2858c2ecf20Sopenharmony_ci uinfo->value.integer.max = 30; 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol, 2908c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 2938c2ecf20Sopenharmony_ci int changed = 0; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (rt->input_vol[0] != ucontrol->value.integer.value[0]) { 2968c2ecf20Sopenharmony_ci rt->input_vol[0] = ucontrol->value.integer.value[0] - 15; 2978c2ecf20Sopenharmony_ci rt->ivol_updated &= ~(1 << 0); 2988c2ecf20Sopenharmony_ci changed = 1; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci if (rt->input_vol[1] != ucontrol->value.integer.value[1]) { 3018c2ecf20Sopenharmony_ci rt->input_vol[1] = ucontrol->value.integer.value[1] - 15; 3028c2ecf20Sopenharmony_ci rt->ivol_updated &= ~(1 << 1); 3038c2ecf20Sopenharmony_ci changed = 1; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (changed) 3078c2ecf20Sopenharmony_ci usb6fire_control_input_vol_update(rt); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return changed; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol, 3138c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = rt->input_vol[0] + 15; 3188c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = rt->input_vol[1] + 15; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol, 3248c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, line_phono_texts); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol, 3308c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 3338c2ecf20Sopenharmony_ci int changed = 0; 3348c2ecf20Sopenharmony_ci if (rt->line_phono_switch != ucontrol->value.integer.value[0]) { 3358c2ecf20Sopenharmony_ci rt->line_phono_switch = ucontrol->value.integer.value[0]; 3368c2ecf20Sopenharmony_ci usb6fire_control_line_phono_update(rt); 3378c2ecf20Sopenharmony_ci changed = 1; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci return changed; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol, 3438c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 3468c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = rt->line_phono_switch; 3478c2ecf20Sopenharmony_ci return 0; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol, 3518c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, opt_coax_texts); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol, 3578c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 3608c2ecf20Sopenharmony_ci int changed = 0; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) { 3638c2ecf20Sopenharmony_ci rt->opt_coax_switch = ucontrol->value.enumerated.item[0]; 3648c2ecf20Sopenharmony_ci usb6fire_control_opt_coax_update(rt); 3658c2ecf20Sopenharmony_ci changed = 1; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci return changed; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol, 3718c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 3748c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = rt->opt_coax_switch; 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol, 3798c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 3828c2ecf20Sopenharmony_ci int changed = 0; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) { 3858c2ecf20Sopenharmony_ci rt->digital_thru_switch = ucontrol->value.integer.value[0]; 3868c2ecf20Sopenharmony_ci usb6fire_control_streaming_update(rt); 3878c2ecf20Sopenharmony_ci changed = 1; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci return changed; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, 3938c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 3968c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = rt->digital_thru_switch; 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new vol_elements[] = { 4018c2ecf20Sopenharmony_ci { 4028c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4038c2ecf20Sopenharmony_ci .name = "Analog Playback Volume", 4048c2ecf20Sopenharmony_ci .index = 0, 4058c2ecf20Sopenharmony_ci .private_value = 0, 4068c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 4078c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 4088c2ecf20Sopenharmony_ci .info = usb6fire_control_output_vol_info, 4098c2ecf20Sopenharmony_ci .get = usb6fire_control_output_vol_get, 4108c2ecf20Sopenharmony_ci .put = usb6fire_control_output_vol_put, 4118c2ecf20Sopenharmony_ci .tlv = { .p = tlv_output } 4128c2ecf20Sopenharmony_ci }, 4138c2ecf20Sopenharmony_ci { 4148c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4158c2ecf20Sopenharmony_ci .name = "Analog Playback Volume", 4168c2ecf20Sopenharmony_ci .index = 1, 4178c2ecf20Sopenharmony_ci .private_value = 2, 4188c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 4198c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 4208c2ecf20Sopenharmony_ci .info = usb6fire_control_output_vol_info, 4218c2ecf20Sopenharmony_ci .get = usb6fire_control_output_vol_get, 4228c2ecf20Sopenharmony_ci .put = usb6fire_control_output_vol_put, 4238c2ecf20Sopenharmony_ci .tlv = { .p = tlv_output } 4248c2ecf20Sopenharmony_ci }, 4258c2ecf20Sopenharmony_ci { 4268c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4278c2ecf20Sopenharmony_ci .name = "Analog Playback Volume", 4288c2ecf20Sopenharmony_ci .index = 2, 4298c2ecf20Sopenharmony_ci .private_value = 4, 4308c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 4318c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 4328c2ecf20Sopenharmony_ci .info = usb6fire_control_output_vol_info, 4338c2ecf20Sopenharmony_ci .get = usb6fire_control_output_vol_get, 4348c2ecf20Sopenharmony_ci .put = usb6fire_control_output_vol_put, 4358c2ecf20Sopenharmony_ci .tlv = { .p = tlv_output } 4368c2ecf20Sopenharmony_ci }, 4378c2ecf20Sopenharmony_ci {} 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new mute_elements[] = { 4418c2ecf20Sopenharmony_ci { 4428c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4438c2ecf20Sopenharmony_ci .name = "Analog Playback Switch", 4448c2ecf20Sopenharmony_ci .index = 0, 4458c2ecf20Sopenharmony_ci .private_value = 0, 4468c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 4478c2ecf20Sopenharmony_ci .info = snd_ctl_boolean_stereo_info, 4488c2ecf20Sopenharmony_ci .get = usb6fire_control_output_mute_get, 4498c2ecf20Sopenharmony_ci .put = usb6fire_control_output_mute_put, 4508c2ecf20Sopenharmony_ci }, 4518c2ecf20Sopenharmony_ci { 4528c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4538c2ecf20Sopenharmony_ci .name = "Analog Playback Switch", 4548c2ecf20Sopenharmony_ci .index = 1, 4558c2ecf20Sopenharmony_ci .private_value = 2, 4568c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 4578c2ecf20Sopenharmony_ci .info = snd_ctl_boolean_stereo_info, 4588c2ecf20Sopenharmony_ci .get = usb6fire_control_output_mute_get, 4598c2ecf20Sopenharmony_ci .put = usb6fire_control_output_mute_put, 4608c2ecf20Sopenharmony_ci }, 4618c2ecf20Sopenharmony_ci { 4628c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4638c2ecf20Sopenharmony_ci .name = "Analog Playback Switch", 4648c2ecf20Sopenharmony_ci .index = 2, 4658c2ecf20Sopenharmony_ci .private_value = 4, 4668c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 4678c2ecf20Sopenharmony_ci .info = snd_ctl_boolean_stereo_info, 4688c2ecf20Sopenharmony_ci .get = usb6fire_control_output_mute_get, 4698c2ecf20Sopenharmony_ci .put = usb6fire_control_output_mute_put, 4708c2ecf20Sopenharmony_ci }, 4718c2ecf20Sopenharmony_ci {} 4728c2ecf20Sopenharmony_ci}; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new elements[] = { 4758c2ecf20Sopenharmony_ci { 4768c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4778c2ecf20Sopenharmony_ci .name = "Line/Phono Capture Route", 4788c2ecf20Sopenharmony_ci .index = 0, 4798c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 4808c2ecf20Sopenharmony_ci .info = usb6fire_control_line_phono_info, 4818c2ecf20Sopenharmony_ci .get = usb6fire_control_line_phono_get, 4828c2ecf20Sopenharmony_ci .put = usb6fire_control_line_phono_put 4838c2ecf20Sopenharmony_ci }, 4848c2ecf20Sopenharmony_ci { 4858c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4868c2ecf20Sopenharmony_ci .name = "Opt/Coax Capture Route", 4878c2ecf20Sopenharmony_ci .index = 0, 4888c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 4898c2ecf20Sopenharmony_ci .info = usb6fire_control_opt_coax_info, 4908c2ecf20Sopenharmony_ci .get = usb6fire_control_opt_coax_get, 4918c2ecf20Sopenharmony_ci .put = usb6fire_control_opt_coax_put 4928c2ecf20Sopenharmony_ci }, 4938c2ecf20Sopenharmony_ci { 4948c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4958c2ecf20Sopenharmony_ci .name = "Digital Thru Playback Route", 4968c2ecf20Sopenharmony_ci .index = 0, 4978c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 4988c2ecf20Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 4998c2ecf20Sopenharmony_ci .get = usb6fire_control_digital_thru_get, 5008c2ecf20Sopenharmony_ci .put = usb6fire_control_digital_thru_put 5018c2ecf20Sopenharmony_ci }, 5028c2ecf20Sopenharmony_ci { 5038c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5048c2ecf20Sopenharmony_ci .name = "Analog Capture Volume", 5058c2ecf20Sopenharmony_ci .index = 0, 5068c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 5078c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 5088c2ecf20Sopenharmony_ci .info = usb6fire_control_input_vol_info, 5098c2ecf20Sopenharmony_ci .get = usb6fire_control_input_vol_get, 5108c2ecf20Sopenharmony_ci .put = usb6fire_control_input_vol_put, 5118c2ecf20Sopenharmony_ci .tlv = { .p = tlv_input } 5128c2ecf20Sopenharmony_ci }, 5138c2ecf20Sopenharmony_ci {} 5148c2ecf20Sopenharmony_ci}; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int usb6fire_control_add_virtual( 5178c2ecf20Sopenharmony_ci struct control_runtime *rt, 5188c2ecf20Sopenharmony_ci struct snd_card *card, 5198c2ecf20Sopenharmony_ci char *name, 5208c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *elems) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci int ret; 5238c2ecf20Sopenharmony_ci int i; 5248c2ecf20Sopenharmony_ci struct snd_kcontrol *vmaster = 5258c2ecf20Sopenharmony_ci snd_ctl_make_virtual_master(name, tlv_output); 5268c2ecf20Sopenharmony_ci struct snd_kcontrol *control; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (!vmaster) 5298c2ecf20Sopenharmony_ci return -ENOMEM; 5308c2ecf20Sopenharmony_ci ret = snd_ctl_add(card, vmaster); 5318c2ecf20Sopenharmony_ci if (ret < 0) 5328c2ecf20Sopenharmony_ci return ret; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci i = 0; 5358c2ecf20Sopenharmony_ci while (elems[i].name) { 5368c2ecf20Sopenharmony_ci control = snd_ctl_new1(&elems[i], rt); 5378c2ecf20Sopenharmony_ci if (!control) 5388c2ecf20Sopenharmony_ci return -ENOMEM; 5398c2ecf20Sopenharmony_ci ret = snd_ctl_add(card, control); 5408c2ecf20Sopenharmony_ci if (ret < 0) 5418c2ecf20Sopenharmony_ci return ret; 5428c2ecf20Sopenharmony_ci ret = snd_ctl_add_follower(vmaster, control); 5438c2ecf20Sopenharmony_ci if (ret < 0) 5448c2ecf20Sopenharmony_ci return ret; 5458c2ecf20Sopenharmony_ci i++; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ciint usb6fire_control_init(struct sfire_chip *chip) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci int i; 5538c2ecf20Sopenharmony_ci int ret; 5548c2ecf20Sopenharmony_ci struct control_runtime *rt = kzalloc(sizeof(struct control_runtime), 5558c2ecf20Sopenharmony_ci GFP_KERNEL); 5568c2ecf20Sopenharmony_ci struct comm_runtime *comm_rt = chip->comm; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!rt) 5598c2ecf20Sopenharmony_ci return -ENOMEM; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci rt->chip = chip; 5628c2ecf20Sopenharmony_ci rt->update_streaming = usb6fire_control_streaming_update; 5638c2ecf20Sopenharmony_ci rt->set_rate = usb6fire_control_set_rate; 5648c2ecf20Sopenharmony_ci rt->set_channels = usb6fire_control_set_channels; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci i = 0; 5678c2ecf20Sopenharmony_ci while (init_data[i].type) { 5688c2ecf20Sopenharmony_ci comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg, 5698c2ecf20Sopenharmony_ci init_data[i].value); 5708c2ecf20Sopenharmony_ci i++; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci usb6fire_control_opt_coax_update(rt); 5748c2ecf20Sopenharmony_ci usb6fire_control_line_phono_update(rt); 5758c2ecf20Sopenharmony_ci usb6fire_control_output_vol_update(rt); 5768c2ecf20Sopenharmony_ci usb6fire_control_output_mute_update(rt); 5778c2ecf20Sopenharmony_ci usb6fire_control_input_vol_update(rt); 5788c2ecf20Sopenharmony_ci usb6fire_control_streaming_update(rt); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci ret = usb6fire_control_add_virtual(rt, chip->card, 5818c2ecf20Sopenharmony_ci "Master Playback Volume", vol_elements); 5828c2ecf20Sopenharmony_ci if (ret) { 5838c2ecf20Sopenharmony_ci dev_err(&chip->dev->dev, "cannot add control.\n"); 5848c2ecf20Sopenharmony_ci kfree(rt); 5858c2ecf20Sopenharmony_ci return ret; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci ret = usb6fire_control_add_virtual(rt, chip->card, 5888c2ecf20Sopenharmony_ci "Master Playback Switch", mute_elements); 5898c2ecf20Sopenharmony_ci if (ret) { 5908c2ecf20Sopenharmony_ci dev_err(&chip->dev->dev, "cannot add control.\n"); 5918c2ecf20Sopenharmony_ci kfree(rt); 5928c2ecf20Sopenharmony_ci return ret; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci i = 0; 5968c2ecf20Sopenharmony_ci while (elements[i].name) { 5978c2ecf20Sopenharmony_ci ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt)); 5988c2ecf20Sopenharmony_ci if (ret < 0) { 5998c2ecf20Sopenharmony_ci kfree(rt); 6008c2ecf20Sopenharmony_ci dev_err(&chip->dev->dev, "cannot add control.\n"); 6018c2ecf20Sopenharmony_ci return ret; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci i++; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci chip->control = rt; 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_civoid usb6fire_control_abort(struct sfire_chip *chip) 6118c2ecf20Sopenharmony_ci{} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_civoid usb6fire_control_destroy(struct sfire_chip *chip) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci kfree(chip->control); 6168c2ecf20Sopenharmony_ci chip->control = NULL; 6178c2ecf20Sopenharmony_ci} 618