162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Linux driver for TerraTec DMX 6Fire USB 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Mixer control 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Torsten Schenk <torsten.schenk@zoho.com> 862306a36Sopenharmony_ci * Created: Jan 01, 2011 962306a36Sopenharmony_ci * Copyright: (C) Torsten Schenk 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Thanks to: 1262306a36Sopenharmony_ci * - Holger Ruckdeschel: he found out how to control individual channel 1362306a36Sopenharmony_ci * volumes and introduced mute switch 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <sound/control.h> 1862306a36Sopenharmony_ci#include <sound/tlv.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "control.h" 2162306a36Sopenharmony_ci#include "comm.h" 2262306a36Sopenharmony_ci#include "chip.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic const char * const opt_coax_texts[2] = { "Optical", "Coax" }; 2562306a36Sopenharmony_cistatic const char * const line_phono_texts[2] = { "Line", "Phono" }; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * data that needs to be sent to device. sets up card internal stuff. 2962306a36Sopenharmony_ci * values dumped from windows driver and filtered by trial'n'error. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cistatic const struct { 3262306a36Sopenharmony_ci u8 type; 3362306a36Sopenharmony_ci u8 reg; 3462306a36Sopenharmony_ci u8 value; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ciinit_data[] = { 3762306a36Sopenharmony_ci { 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 }, 3862306a36Sopenharmony_ci { 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 }, 3962306a36Sopenharmony_ci { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 }, 4062306a36Sopenharmony_ci { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 }, 4162306a36Sopenharmony_ci { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 }, 4262306a36Sopenharmony_ci { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, 4362306a36Sopenharmony_ci { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 }, 4462306a36Sopenharmony_ci { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 }, 4562306a36Sopenharmony_ci { 0 } /* TERMINATING ENTRY */ 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; 4962306a36Sopenharmony_ci/* values to write to soundcard register for all samplerates */ 5062306a36Sopenharmony_cistatic const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; 5162306a36Sopenharmony_cistatic const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0); 5462306a36Sopenharmony_cistatic DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cienum { 5762306a36Sopenharmony_ci DIGITAL_THRU_ONLY_SAMPLERATE = 3 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void usb6fire_control_output_vol_update(struct control_runtime *rt) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 6362306a36Sopenharmony_ci int i; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (comm_rt) 6662306a36Sopenharmony_ci for (i = 0; i < 6; i++) 6762306a36Sopenharmony_ci if (!(rt->ovol_updated & (1 << i))) { 6862306a36Sopenharmony_ci comm_rt->write8(comm_rt, 0x12, 0x0f + i, 6962306a36Sopenharmony_ci 180 - rt->output_vol[i]); 7062306a36Sopenharmony_ci rt->ovol_updated |= 1 << i; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void usb6fire_control_output_mute_update(struct control_runtime *rt) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (comm_rt) 7962306a36Sopenharmony_ci comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void usb6fire_control_input_vol_update(struct control_runtime *rt) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 8562306a36Sopenharmony_ci int i; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (comm_rt) 8862306a36Sopenharmony_ci for (i = 0; i < 2; i++) 8962306a36Sopenharmony_ci if (!(rt->ivol_updated & (1 << i))) { 9062306a36Sopenharmony_ci comm_rt->write8(comm_rt, 0x12, 0x1c + i, 9162306a36Sopenharmony_ci rt->input_vol[i] & 0x3f); 9262306a36Sopenharmony_ci rt->ivol_updated |= 1 << i; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void usb6fire_control_line_phono_update(struct control_runtime *rt) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 9962306a36Sopenharmony_ci if (comm_rt) { 10062306a36Sopenharmony_ci comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch); 10162306a36Sopenharmony_ci comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void usb6fire_control_opt_coax_update(struct control_runtime *rt) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 10862306a36Sopenharmony_ci if (comm_rt) { 10962306a36Sopenharmony_ci comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch); 11062306a36Sopenharmony_ci comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int usb6fire_control_set_rate(struct control_runtime *rt, int rate) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci int ret; 11762306a36Sopenharmony_ci struct usb_device *device = rt->chip->dev; 11862306a36Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (rate < 0 || rate >= CONTROL_N_RATES) 12162306a36Sopenharmony_ci return -EINVAL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci ret = usb_set_interface(device, 1, rates_altsetting[rate]); 12462306a36Sopenharmony_ci if (ret < 0) 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* set soundcard clock */ 12862306a36Sopenharmony_ci ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate], 12962306a36Sopenharmony_ci rates_6fire_vh[rate]); 13062306a36Sopenharmony_ci if (ret < 0) 13162306a36Sopenharmony_ci return ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int usb6fire_control_set_channels( 13762306a36Sopenharmony_ci struct control_runtime *rt, int n_analog_out, 13862306a36Sopenharmony_ci int n_analog_in, bool spdif_out, bool spdif_in) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int ret; 14162306a36Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* enable analog inputs and outputs 14462306a36Sopenharmony_ci * (one bit per stereo-channel) */ 14562306a36Sopenharmony_ci ret = comm_rt->write16(comm_rt, 0x02, 0x02, 14662306a36Sopenharmony_ci (1 << (n_analog_out / 2)) - 1, 14762306a36Sopenharmony_ci (1 << (n_analog_in / 2)) - 1); 14862306a36Sopenharmony_ci if (ret < 0) 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* disable digital inputs and outputs */ 15262306a36Sopenharmony_ci /* TODO: use spdif_x to enable/disable digital channels */ 15362306a36Sopenharmony_ci ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); 15462306a36Sopenharmony_ci if (ret < 0) 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int usb6fire_control_streaming_update(struct control_runtime *rt) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct comm_runtime *comm_rt = rt->chip->comm; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (comm_rt) { 16562306a36Sopenharmony_ci if (!rt->usb_streaming && rt->digital_thru_switch) 16662306a36Sopenharmony_ci usb6fire_control_set_rate(rt, 16762306a36Sopenharmony_ci DIGITAL_THRU_ONLY_SAMPLERATE); 16862306a36Sopenharmony_ci return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 16962306a36Sopenharmony_ci (rt->usb_streaming ? 0x01 : 0x00) | 17062306a36Sopenharmony_ci (rt->digital_thru_switch ? 0x08 : 0x00)); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci return -EINVAL; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol, 17662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 17962306a36Sopenharmony_ci uinfo->count = 2; 18062306a36Sopenharmony_ci uinfo->value.integer.min = 0; 18162306a36Sopenharmony_ci uinfo->value.integer.max = 180; 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol, 18662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 18962306a36Sopenharmony_ci unsigned int ch = kcontrol->private_value; 19062306a36Sopenharmony_ci int changed = 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (ch > 4) { 19362306a36Sopenharmony_ci dev_err(&rt->chip->dev->dev, 19462306a36Sopenharmony_ci "Invalid channel in volume control."); 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) { 19962306a36Sopenharmony_ci rt->output_vol[ch] = ucontrol->value.integer.value[0]; 20062306a36Sopenharmony_ci rt->ovol_updated &= ~(1 << ch); 20162306a36Sopenharmony_ci changed = 1; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) { 20462306a36Sopenharmony_ci rt->output_vol[ch + 1] = ucontrol->value.integer.value[1]; 20562306a36Sopenharmony_ci rt->ovol_updated &= ~(2 << ch); 20662306a36Sopenharmony_ci changed = 1; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (changed) 21062306a36Sopenharmony_ci usb6fire_control_output_vol_update(rt); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return changed; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol, 21662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 21962306a36Sopenharmony_ci unsigned int ch = kcontrol->private_value; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (ch > 4) { 22262306a36Sopenharmony_ci dev_err(&rt->chip->dev->dev, 22362306a36Sopenharmony_ci "Invalid channel in volume control."); 22462306a36Sopenharmony_ci return -EINVAL; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ucontrol->value.integer.value[0] = rt->output_vol[ch]; 22862306a36Sopenharmony_ci ucontrol->value.integer.value[1] = rt->output_vol[ch + 1]; 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol, 23362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 23662306a36Sopenharmony_ci unsigned int ch = kcontrol->private_value; 23762306a36Sopenharmony_ci u8 old = rt->output_mute; 23862306a36Sopenharmony_ci u8 value = 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (ch > 4) { 24162306a36Sopenharmony_ci dev_err(&rt->chip->dev->dev, 24262306a36Sopenharmony_ci "Invalid channel in volume control."); 24362306a36Sopenharmony_ci return -EINVAL; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci rt->output_mute &= ~(3 << ch); 24762306a36Sopenharmony_ci if (ucontrol->value.integer.value[0]) 24862306a36Sopenharmony_ci value |= 1; 24962306a36Sopenharmony_ci if (ucontrol->value.integer.value[1]) 25062306a36Sopenharmony_ci value |= 2; 25162306a36Sopenharmony_ci rt->output_mute |= value << ch; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (rt->output_mute != old) 25462306a36Sopenharmony_ci usb6fire_control_output_mute_update(rt); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return rt->output_mute != old; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol, 26062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 26362306a36Sopenharmony_ci unsigned int ch = kcontrol->private_value; 26462306a36Sopenharmony_ci u8 value = rt->output_mute >> ch; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (ch > 4) { 26762306a36Sopenharmony_ci dev_err(&rt->chip->dev->dev, 26862306a36Sopenharmony_ci "Invalid channel in volume control."); 26962306a36Sopenharmony_ci return -EINVAL; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 1 & value; 27362306a36Sopenharmony_ci value >>= 1; 27462306a36Sopenharmony_ci ucontrol->value.integer.value[1] = 1 & value; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol, 28062306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 28362306a36Sopenharmony_ci uinfo->count = 2; 28462306a36Sopenharmony_ci uinfo->value.integer.min = 0; 28562306a36Sopenharmony_ci uinfo->value.integer.max = 30; 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol, 29062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 29362306a36Sopenharmony_ci int changed = 0; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (rt->input_vol[0] != ucontrol->value.integer.value[0]) { 29662306a36Sopenharmony_ci rt->input_vol[0] = ucontrol->value.integer.value[0] - 15; 29762306a36Sopenharmony_ci rt->ivol_updated &= ~(1 << 0); 29862306a36Sopenharmony_ci changed = 1; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci if (rt->input_vol[1] != ucontrol->value.integer.value[1]) { 30162306a36Sopenharmony_ci rt->input_vol[1] = ucontrol->value.integer.value[1] - 15; 30262306a36Sopenharmony_ci rt->ivol_updated &= ~(1 << 1); 30362306a36Sopenharmony_ci changed = 1; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (changed) 30762306a36Sopenharmony_ci usb6fire_control_input_vol_update(rt); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return changed; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol, 31362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ucontrol->value.integer.value[0] = rt->input_vol[0] + 15; 31862306a36Sopenharmony_ci ucontrol->value.integer.value[1] = rt->input_vol[1] + 15; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol, 32462306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, line_phono_texts); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol, 33062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 33362306a36Sopenharmony_ci int changed = 0; 33462306a36Sopenharmony_ci if (rt->line_phono_switch != ucontrol->value.integer.value[0]) { 33562306a36Sopenharmony_ci rt->line_phono_switch = ucontrol->value.integer.value[0]; 33662306a36Sopenharmony_ci usb6fire_control_line_phono_update(rt); 33762306a36Sopenharmony_ci changed = 1; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci return changed; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol, 34362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 34662306a36Sopenharmony_ci ucontrol->value.integer.value[0] = rt->line_phono_switch; 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol, 35162306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, opt_coax_texts); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol, 35762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 36062306a36Sopenharmony_ci int changed = 0; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) { 36362306a36Sopenharmony_ci rt->opt_coax_switch = ucontrol->value.enumerated.item[0]; 36462306a36Sopenharmony_ci usb6fire_control_opt_coax_update(rt); 36562306a36Sopenharmony_ci changed = 1; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci return changed; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol, 37162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 37462306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = rt->opt_coax_switch; 37562306a36Sopenharmony_ci return 0; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol, 37962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 38262306a36Sopenharmony_ci int changed = 0; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) { 38562306a36Sopenharmony_ci rt->digital_thru_switch = ucontrol->value.integer.value[0]; 38662306a36Sopenharmony_ci usb6fire_control_streaming_update(rt); 38762306a36Sopenharmony_ci changed = 1; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci return changed; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, 39362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct control_runtime *rt = snd_kcontrol_chip(kcontrol); 39662306a36Sopenharmony_ci ucontrol->value.integer.value[0] = rt->digital_thru_switch; 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic const struct snd_kcontrol_new vol_elements[] = { 40162306a36Sopenharmony_ci { 40262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 40362306a36Sopenharmony_ci .name = "Analog Playback Volume", 40462306a36Sopenharmony_ci .index = 0, 40562306a36Sopenharmony_ci .private_value = 0, 40662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 40762306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 40862306a36Sopenharmony_ci .info = usb6fire_control_output_vol_info, 40962306a36Sopenharmony_ci .get = usb6fire_control_output_vol_get, 41062306a36Sopenharmony_ci .put = usb6fire_control_output_vol_put, 41162306a36Sopenharmony_ci .tlv = { .p = tlv_output } 41262306a36Sopenharmony_ci }, 41362306a36Sopenharmony_ci { 41462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 41562306a36Sopenharmony_ci .name = "Analog Playback Volume", 41662306a36Sopenharmony_ci .index = 1, 41762306a36Sopenharmony_ci .private_value = 2, 41862306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 41962306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 42062306a36Sopenharmony_ci .info = usb6fire_control_output_vol_info, 42162306a36Sopenharmony_ci .get = usb6fire_control_output_vol_get, 42262306a36Sopenharmony_ci .put = usb6fire_control_output_vol_put, 42362306a36Sopenharmony_ci .tlv = { .p = tlv_output } 42462306a36Sopenharmony_ci }, 42562306a36Sopenharmony_ci { 42662306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 42762306a36Sopenharmony_ci .name = "Analog Playback Volume", 42862306a36Sopenharmony_ci .index = 2, 42962306a36Sopenharmony_ci .private_value = 4, 43062306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 43162306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 43262306a36Sopenharmony_ci .info = usb6fire_control_output_vol_info, 43362306a36Sopenharmony_ci .get = usb6fire_control_output_vol_get, 43462306a36Sopenharmony_ci .put = usb6fire_control_output_vol_put, 43562306a36Sopenharmony_ci .tlv = { .p = tlv_output } 43662306a36Sopenharmony_ci }, 43762306a36Sopenharmony_ci {} 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic const struct snd_kcontrol_new mute_elements[] = { 44162306a36Sopenharmony_ci { 44262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 44362306a36Sopenharmony_ci .name = "Analog Playback Switch", 44462306a36Sopenharmony_ci .index = 0, 44562306a36Sopenharmony_ci .private_value = 0, 44662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 44762306a36Sopenharmony_ci .info = snd_ctl_boolean_stereo_info, 44862306a36Sopenharmony_ci .get = usb6fire_control_output_mute_get, 44962306a36Sopenharmony_ci .put = usb6fire_control_output_mute_put, 45062306a36Sopenharmony_ci }, 45162306a36Sopenharmony_ci { 45262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 45362306a36Sopenharmony_ci .name = "Analog Playback Switch", 45462306a36Sopenharmony_ci .index = 1, 45562306a36Sopenharmony_ci .private_value = 2, 45662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 45762306a36Sopenharmony_ci .info = snd_ctl_boolean_stereo_info, 45862306a36Sopenharmony_ci .get = usb6fire_control_output_mute_get, 45962306a36Sopenharmony_ci .put = usb6fire_control_output_mute_put, 46062306a36Sopenharmony_ci }, 46162306a36Sopenharmony_ci { 46262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 46362306a36Sopenharmony_ci .name = "Analog Playback Switch", 46462306a36Sopenharmony_ci .index = 2, 46562306a36Sopenharmony_ci .private_value = 4, 46662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 46762306a36Sopenharmony_ci .info = snd_ctl_boolean_stereo_info, 46862306a36Sopenharmony_ci .get = usb6fire_control_output_mute_get, 46962306a36Sopenharmony_ci .put = usb6fire_control_output_mute_put, 47062306a36Sopenharmony_ci }, 47162306a36Sopenharmony_ci {} 47262306a36Sopenharmony_ci}; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic const struct snd_kcontrol_new elements[] = { 47562306a36Sopenharmony_ci { 47662306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 47762306a36Sopenharmony_ci .name = "Line/Phono Capture Route", 47862306a36Sopenharmony_ci .index = 0, 47962306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 48062306a36Sopenharmony_ci .info = usb6fire_control_line_phono_info, 48162306a36Sopenharmony_ci .get = usb6fire_control_line_phono_get, 48262306a36Sopenharmony_ci .put = usb6fire_control_line_phono_put 48362306a36Sopenharmony_ci }, 48462306a36Sopenharmony_ci { 48562306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 48662306a36Sopenharmony_ci .name = "Opt/Coax Capture Route", 48762306a36Sopenharmony_ci .index = 0, 48862306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 48962306a36Sopenharmony_ci .info = usb6fire_control_opt_coax_info, 49062306a36Sopenharmony_ci .get = usb6fire_control_opt_coax_get, 49162306a36Sopenharmony_ci .put = usb6fire_control_opt_coax_put 49262306a36Sopenharmony_ci }, 49362306a36Sopenharmony_ci { 49462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 49562306a36Sopenharmony_ci .name = "Digital Thru Playback Route", 49662306a36Sopenharmony_ci .index = 0, 49762306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 49862306a36Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 49962306a36Sopenharmony_ci .get = usb6fire_control_digital_thru_get, 50062306a36Sopenharmony_ci .put = usb6fire_control_digital_thru_put 50162306a36Sopenharmony_ci }, 50262306a36Sopenharmony_ci { 50362306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 50462306a36Sopenharmony_ci .name = "Analog Capture Volume", 50562306a36Sopenharmony_ci .index = 0, 50662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 50762306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 50862306a36Sopenharmony_ci .info = usb6fire_control_input_vol_info, 50962306a36Sopenharmony_ci .get = usb6fire_control_input_vol_get, 51062306a36Sopenharmony_ci .put = usb6fire_control_input_vol_put, 51162306a36Sopenharmony_ci .tlv = { .p = tlv_input } 51262306a36Sopenharmony_ci }, 51362306a36Sopenharmony_ci {} 51462306a36Sopenharmony_ci}; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int usb6fire_control_add_virtual( 51762306a36Sopenharmony_ci struct control_runtime *rt, 51862306a36Sopenharmony_ci struct snd_card *card, 51962306a36Sopenharmony_ci char *name, 52062306a36Sopenharmony_ci const struct snd_kcontrol_new *elems) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci int ret; 52362306a36Sopenharmony_ci int i; 52462306a36Sopenharmony_ci struct snd_kcontrol *vmaster = 52562306a36Sopenharmony_ci snd_ctl_make_virtual_master(name, tlv_output); 52662306a36Sopenharmony_ci struct snd_kcontrol *control; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (!vmaster) 52962306a36Sopenharmony_ci return -ENOMEM; 53062306a36Sopenharmony_ci ret = snd_ctl_add(card, vmaster); 53162306a36Sopenharmony_ci if (ret < 0) 53262306a36Sopenharmony_ci return ret; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci i = 0; 53562306a36Sopenharmony_ci while (elems[i].name) { 53662306a36Sopenharmony_ci control = snd_ctl_new1(&elems[i], rt); 53762306a36Sopenharmony_ci if (!control) 53862306a36Sopenharmony_ci return -ENOMEM; 53962306a36Sopenharmony_ci ret = snd_ctl_add(card, control); 54062306a36Sopenharmony_ci if (ret < 0) 54162306a36Sopenharmony_ci return ret; 54262306a36Sopenharmony_ci ret = snd_ctl_add_follower(vmaster, control); 54362306a36Sopenharmony_ci if (ret < 0) 54462306a36Sopenharmony_ci return ret; 54562306a36Sopenharmony_ci i++; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ciint usb6fire_control_init(struct sfire_chip *chip) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci int i; 55362306a36Sopenharmony_ci int ret; 55462306a36Sopenharmony_ci struct control_runtime *rt = kzalloc(sizeof(struct control_runtime), 55562306a36Sopenharmony_ci GFP_KERNEL); 55662306a36Sopenharmony_ci struct comm_runtime *comm_rt = chip->comm; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (!rt) 55962306a36Sopenharmony_ci return -ENOMEM; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci rt->chip = chip; 56262306a36Sopenharmony_ci rt->update_streaming = usb6fire_control_streaming_update; 56362306a36Sopenharmony_ci rt->set_rate = usb6fire_control_set_rate; 56462306a36Sopenharmony_ci rt->set_channels = usb6fire_control_set_channels; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci i = 0; 56762306a36Sopenharmony_ci while (init_data[i].type) { 56862306a36Sopenharmony_ci comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg, 56962306a36Sopenharmony_ci init_data[i].value); 57062306a36Sopenharmony_ci i++; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci usb6fire_control_opt_coax_update(rt); 57462306a36Sopenharmony_ci usb6fire_control_line_phono_update(rt); 57562306a36Sopenharmony_ci usb6fire_control_output_vol_update(rt); 57662306a36Sopenharmony_ci usb6fire_control_output_mute_update(rt); 57762306a36Sopenharmony_ci usb6fire_control_input_vol_update(rt); 57862306a36Sopenharmony_ci usb6fire_control_streaming_update(rt); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci ret = usb6fire_control_add_virtual(rt, chip->card, 58162306a36Sopenharmony_ci "Master Playback Volume", vol_elements); 58262306a36Sopenharmony_ci if (ret) { 58362306a36Sopenharmony_ci dev_err(&chip->dev->dev, "cannot add control.\n"); 58462306a36Sopenharmony_ci kfree(rt); 58562306a36Sopenharmony_ci return ret; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci ret = usb6fire_control_add_virtual(rt, chip->card, 58862306a36Sopenharmony_ci "Master Playback Switch", mute_elements); 58962306a36Sopenharmony_ci if (ret) { 59062306a36Sopenharmony_ci dev_err(&chip->dev->dev, "cannot add control.\n"); 59162306a36Sopenharmony_ci kfree(rt); 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci i = 0; 59662306a36Sopenharmony_ci while (elements[i].name) { 59762306a36Sopenharmony_ci ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt)); 59862306a36Sopenharmony_ci if (ret < 0) { 59962306a36Sopenharmony_ci kfree(rt); 60062306a36Sopenharmony_ci dev_err(&chip->dev->dev, "cannot add control.\n"); 60162306a36Sopenharmony_ci return ret; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci i++; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci chip->control = rt; 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_civoid usb6fire_control_abort(struct sfire_chip *chip) 61162306a36Sopenharmony_ci{} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_civoid usb6fire_control_destroy(struct sfire_chip *chip) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci kfree(chip->control); 61662306a36Sopenharmony_ci chip->control = NULL; 61762306a36Sopenharmony_ci} 618