162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk> 462306a36Sopenharmony_ci * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit 562306a36Sopenharmony_ci * Version: 0.0.18 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * FEATURES currently supported: 862306a36Sopenharmony_ci * See ca0106_main.c for features. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Changelog: 1162306a36Sopenharmony_ci * Support interrupts per period. 1262306a36Sopenharmony_ci * Removed noise from Center/LFE channel when in Analog mode. 1362306a36Sopenharmony_ci * Rename and remove mixer controls. 1462306a36Sopenharmony_ci * 0.0.6 1562306a36Sopenharmony_ci * Use separate card based DMA buffer for periods table list. 1662306a36Sopenharmony_ci * 0.0.7 1762306a36Sopenharmony_ci * Change remove and rename ctrls into lists. 1862306a36Sopenharmony_ci * 0.0.8 1962306a36Sopenharmony_ci * Try to fix capture sources. 2062306a36Sopenharmony_ci * 0.0.9 2162306a36Sopenharmony_ci * Fix AC3 output. 2262306a36Sopenharmony_ci * Enable S32_LE format support. 2362306a36Sopenharmony_ci * 0.0.10 2462306a36Sopenharmony_ci * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".) 2562306a36Sopenharmony_ci * 0.0.11 2662306a36Sopenharmony_ci * Add Model name recognition. 2762306a36Sopenharmony_ci * 0.0.12 2862306a36Sopenharmony_ci * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period. 2962306a36Sopenharmony_ci * Remove redundent "voice" handling. 3062306a36Sopenharmony_ci * 0.0.13 3162306a36Sopenharmony_ci * Single trigger call for multi channels. 3262306a36Sopenharmony_ci * 0.0.14 3362306a36Sopenharmony_ci * Set limits based on what the sound card hardware can do. 3462306a36Sopenharmony_ci * playback periods_min=2, periods_max=8 3562306a36Sopenharmony_ci * capture hw constraints require period_size = n * 64 bytes. 3662306a36Sopenharmony_ci * playback hw constraints require period_size = n * 64 bytes. 3762306a36Sopenharmony_ci * 0.0.15 3862306a36Sopenharmony_ci * Separated ca0106.c into separate functional .c files. 3962306a36Sopenharmony_ci * 0.0.16 4062306a36Sopenharmony_ci * Modified Copyright message. 4162306a36Sopenharmony_ci * 0.0.17 4262306a36Sopenharmony_ci * Implement Mic and Line in Capture. 4362306a36Sopenharmony_ci * 0.0.18 4462306a36Sopenharmony_ci * Add support for mute control on SB Live 24bit (cards w/ SPI DAC) 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * This code was initially based on code from ALSA's emu10k1x.c which is: 4762306a36Sopenharmony_ci * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci#include <linux/delay.h> 5062306a36Sopenharmony_ci#include <linux/init.h> 5162306a36Sopenharmony_ci#include <linux/interrupt.h> 5262306a36Sopenharmony_ci#include <linux/moduleparam.h> 5362306a36Sopenharmony_ci#include <sound/core.h> 5462306a36Sopenharmony_ci#include <sound/initval.h> 5562306a36Sopenharmony_ci#include <sound/pcm.h> 5662306a36Sopenharmony_ci#include <sound/ac97_codec.h> 5762306a36Sopenharmony_ci#include <sound/info.h> 5862306a36Sopenharmony_ci#include <sound/tlv.h> 5962306a36Sopenharmony_ci#include <linux/io.h> 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#include "ca0106.h" 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void ca0106_spdif_enable(struct snd_ca0106 *emu) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci unsigned int val; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (emu->spdif_enable) { 6862306a36Sopenharmony_ci /* Digital */ 6962306a36Sopenharmony_ci snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 7062306a36Sopenharmony_ci snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000); 7162306a36Sopenharmony_ci val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000; 7262306a36Sopenharmony_ci snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); 7362306a36Sopenharmony_ci val = inl(emu->port + CA0106_GPIO) & ~0x101; 7462306a36Sopenharmony_ci outl(val, emu->port + CA0106_GPIO); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci } else { 7762306a36Sopenharmony_ci /* Analog */ 7862306a36Sopenharmony_ci snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf); 7962306a36Sopenharmony_ci snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000); 8062306a36Sopenharmony_ci val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000; 8162306a36Sopenharmony_ci snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val); 8262306a36Sopenharmony_ci val = inl(emu->port + CA0106_GPIO) | 0x101; 8362306a36Sopenharmony_ci outl(val, emu->port + CA0106_GPIO); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void ca0106_set_capture_source(struct snd_ca0106 *emu) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci unsigned int val = emu->capture_source; 9062306a36Sopenharmony_ci unsigned int source, mask; 9162306a36Sopenharmony_ci source = (val << 28) | (val << 24) | (val << 20) | (val << 16); 9262306a36Sopenharmony_ci mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff; 9362306a36Sopenharmony_ci snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void ca0106_set_i2c_capture_source(struct snd_ca0106 *emu, 9762306a36Sopenharmony_ci unsigned int val, int force) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned int ngain, ogain; 10062306a36Sopenharmony_ci u32 source; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ 10362306a36Sopenharmony_ci ngain = emu->i2c_capture_volume[val][0]; /* Left */ 10462306a36Sopenharmony_ci ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ 10562306a36Sopenharmony_ci if (force || ngain != ogain) 10662306a36Sopenharmony_ci snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ngain & 0xff); 10762306a36Sopenharmony_ci ngain = emu->i2c_capture_volume[val][1]; /* Right */ 10862306a36Sopenharmony_ci ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */ 10962306a36Sopenharmony_ci if (force || ngain != ogain) 11062306a36Sopenharmony_ci snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ngain & 0xff); 11162306a36Sopenharmony_ci source = 1 << val; 11262306a36Sopenharmony_ci snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ 11362306a36Sopenharmony_ci emu->i2c_capture_source = val; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci u32 tmp; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (emu->capture_mic_line_in) { 12162306a36Sopenharmony_ci /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ 12262306a36Sopenharmony_ci tmp = inl(emu->port + CA0106_GPIO) & ~0x400; 12362306a36Sopenharmony_ci tmp = tmp | 0x400; 12462306a36Sopenharmony_ci outl(tmp, emu->port + CA0106_GPIO); 12562306a36Sopenharmony_ci /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */ 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci /* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */ 12862306a36Sopenharmony_ci tmp = inl(emu->port + CA0106_GPIO) & ~0x400; 12962306a36Sopenharmony_ci outl(tmp, emu->port + CA0106_GPIO); 13062306a36Sopenharmony_ci /* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */ 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void ca0106_set_spdif_bits(struct snd_ca0106 *emu, int idx) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, emu->spdif_str_bits[idx]); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); 14262306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol, 14762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = emu->spdif_enable; 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol, 15662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 15962306a36Sopenharmony_ci unsigned int val; 16062306a36Sopenharmony_ci int change = 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci val = !!ucontrol->value.integer.value[0]; 16362306a36Sopenharmony_ci change = (emu->spdif_enable != val); 16462306a36Sopenharmony_ci if (change) { 16562306a36Sopenharmony_ci emu->spdif_enable = val; 16662306a36Sopenharmony_ci ca0106_spdif_enable(emu); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci return change; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol, 17262306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci static const char * const texts[6] = { 17562306a36Sopenharmony_ci "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out" 17662306a36Sopenharmony_ci }; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 6, texts); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol, 18262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = emu->capture_source; 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol, 19162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 19462306a36Sopenharmony_ci unsigned int val; 19562306a36Sopenharmony_ci int change = 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci val = ucontrol->value.enumerated.item[0] ; 19862306a36Sopenharmony_ci if (val >= 6) 19962306a36Sopenharmony_ci return -EINVAL; 20062306a36Sopenharmony_ci change = (emu->capture_source != val); 20162306a36Sopenharmony_ci if (change) { 20262306a36Sopenharmony_ci emu->capture_source = val; 20362306a36Sopenharmony_ci ca0106_set_capture_source(emu); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci return change; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, 20962306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci static const char * const texts[4] = { 21262306a36Sopenharmony_ci "Phone", "Mic", "Line in", "Aux" 21362306a36Sopenharmony_ci }; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 4, texts); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, 21962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = emu->i2c_capture_source; 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, 22862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 23162306a36Sopenharmony_ci unsigned int source_id; 23262306a36Sopenharmony_ci int change = 0; 23362306a36Sopenharmony_ci /* If the capture source has changed, 23462306a36Sopenharmony_ci * update the capture volume from the cached value 23562306a36Sopenharmony_ci * for the particular source. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci source_id = ucontrol->value.enumerated.item[0] ; 23862306a36Sopenharmony_ci if (source_id >= 4) 23962306a36Sopenharmony_ci return -EINVAL; 24062306a36Sopenharmony_ci change = (emu->i2c_capture_source != source_id); 24162306a36Sopenharmony_ci if (change) { 24262306a36Sopenharmony_ci ca0106_set_i2c_capture_source(emu, source_id, 0); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci return change; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, 24862306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci static const char * const texts[2] = { "Side out", "Line in" }; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, texts); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, 25662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci static const char * const texts[2] = { "Line in", "Mic in" }; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, texts); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol, 26462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in; 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol, 27362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 27662306a36Sopenharmony_ci unsigned int val; 27762306a36Sopenharmony_ci int change = 0; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci val = ucontrol->value.enumerated.item[0] ; 28062306a36Sopenharmony_ci if (val > 1) 28162306a36Sopenharmony_ci return -EINVAL; 28262306a36Sopenharmony_ci change = (emu->capture_mic_line_in != val); 28362306a36Sopenharmony_ci if (change) { 28462306a36Sopenharmony_ci emu->capture_mic_line_in = val; 28562306a36Sopenharmony_ci ca0106_set_capture_mic_line_in(emu); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci return change; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ca0106_capture_mic_line_in = 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 29362306a36Sopenharmony_ci .name = "Shared Mic/Line in Capture Switch", 29462306a36Sopenharmony_ci .info = snd_ca0106_capture_mic_line_in_info, 29562306a36Sopenharmony_ci .get = snd_ca0106_capture_mic_line_in_get, 29662306a36Sopenharmony_ci .put = snd_ca0106_capture_mic_line_in_put 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out = 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 30262306a36Sopenharmony_ci .name = "Shared Line in/Side out Capture Switch", 30362306a36Sopenharmony_ci .info = snd_ca0106_capture_line_in_side_out_info, 30462306a36Sopenharmony_ci .get = snd_ca0106_capture_mic_line_in_get, 30562306a36Sopenharmony_ci .put = snd_ca0106_capture_mic_line_in_put 30662306a36Sopenharmony_ci}; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol, 31062306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 31362306a36Sopenharmony_ci uinfo->count = 1; 31462306a36Sopenharmony_ci return 0; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void decode_spdif_bits(unsigned char *status, unsigned int bits) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci status[0] = (bits >> 0) & 0xff; 32062306a36Sopenharmony_ci status[1] = (bits >> 8) & 0xff; 32162306a36Sopenharmony_ci status[2] = (bits >> 16) & 0xff; 32262306a36Sopenharmony_ci status[3] = (bits >> 24) & 0xff; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int snd_ca0106_spdif_get_default(struct snd_kcontrol *kcontrol, 32662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 32962306a36Sopenharmony_ci unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci decode_spdif_bits(ucontrol->value.iec958.status, 33262306a36Sopenharmony_ci emu->spdif_bits[idx]); 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int snd_ca0106_spdif_get_stream(struct snd_kcontrol *kcontrol, 33762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 34062306a36Sopenharmony_ci unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci decode_spdif_bits(ucontrol->value.iec958.status, 34362306a36Sopenharmony_ci emu->spdif_str_bits[idx]); 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol, 34862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci ucontrol->value.iec958.status[0] = 0xff; 35162306a36Sopenharmony_ci ucontrol->value.iec958.status[1] = 0xff; 35262306a36Sopenharmony_ci ucontrol->value.iec958.status[2] = 0xff; 35362306a36Sopenharmony_ci ucontrol->value.iec958.status[3] = 0xff; 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic unsigned int encode_spdif_bits(unsigned char *status) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci return ((unsigned int)status[0] << 0) | 36062306a36Sopenharmony_ci ((unsigned int)status[1] << 8) | 36162306a36Sopenharmony_ci ((unsigned int)status[2] << 16) | 36262306a36Sopenharmony_ci ((unsigned int)status[3] << 24); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int snd_ca0106_spdif_put_default(struct snd_kcontrol *kcontrol, 36662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 36962306a36Sopenharmony_ci unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 37062306a36Sopenharmony_ci unsigned int val; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci val = encode_spdif_bits(ucontrol->value.iec958.status); 37362306a36Sopenharmony_ci if (val != emu->spdif_bits[idx]) { 37462306a36Sopenharmony_ci emu->spdif_bits[idx] = val; 37562306a36Sopenharmony_ci /* FIXME: this isn't safe, but needed to keep the compatibility 37662306a36Sopenharmony_ci * with older alsa-lib config 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci emu->spdif_str_bits[idx] = val; 37962306a36Sopenharmony_ci ca0106_set_spdif_bits(emu, idx); 38062306a36Sopenharmony_ci return 1; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int snd_ca0106_spdif_put_stream(struct snd_kcontrol *kcontrol, 38662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 38962306a36Sopenharmony_ci unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 39062306a36Sopenharmony_ci unsigned int val; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci val = encode_spdif_bits(ucontrol->value.iec958.status); 39362306a36Sopenharmony_ci if (val != emu->spdif_str_bits[idx]) { 39462306a36Sopenharmony_ci emu->spdif_str_bits[idx] = val; 39562306a36Sopenharmony_ci ca0106_set_spdif_bits(emu, idx); 39662306a36Sopenharmony_ci return 1; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol, 40262306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 40562306a36Sopenharmony_ci uinfo->count = 2; 40662306a36Sopenharmony_ci uinfo->value.integer.min = 0; 40762306a36Sopenharmony_ci uinfo->value.integer.max = 255; 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol, 41262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 41562306a36Sopenharmony_ci unsigned int value; 41662306a36Sopenharmony_ci int channel_id, reg; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci channel_id = (kcontrol->private_value >> 8) & 0xff; 41962306a36Sopenharmony_ci reg = kcontrol->private_value & 0xff; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci value = snd_ca0106_ptr_read(emu, reg, channel_id); 42262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */ 42362306a36Sopenharmony_ci ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */ 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol, 42862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 43162306a36Sopenharmony_ci unsigned int oval, nval; 43262306a36Sopenharmony_ci int channel_id, reg; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci channel_id = (kcontrol->private_value >> 8) & 0xff; 43562306a36Sopenharmony_ci reg = kcontrol->private_value & 0xff; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci oval = snd_ca0106_ptr_read(emu, reg, channel_id); 43862306a36Sopenharmony_ci nval = ((0xff - ucontrol->value.integer.value[0]) << 24) | 43962306a36Sopenharmony_ci ((0xff - ucontrol->value.integer.value[1]) << 16); 44062306a36Sopenharmony_ci nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) | 44162306a36Sopenharmony_ci ((0xff - ucontrol->value.integer.value[1]) ); 44262306a36Sopenharmony_ci if (oval == nval) 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci snd_ca0106_ptr_write(emu, reg, channel_id, nval); 44562306a36Sopenharmony_ci return 1; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol, 44962306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 45262306a36Sopenharmony_ci uinfo->count = 2; 45362306a36Sopenharmony_ci uinfo->value.integer.min = 0; 45462306a36Sopenharmony_ci uinfo->value.integer.max = 255; 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol, 45962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 46262306a36Sopenharmony_ci int source_id; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci source_id = kcontrol->private_value; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0]; 46762306a36Sopenharmony_ci ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1]; 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, 47262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 47562306a36Sopenharmony_ci unsigned int ogain; 47662306a36Sopenharmony_ci unsigned int ngain; 47762306a36Sopenharmony_ci int source_id; 47862306a36Sopenharmony_ci int change = 0; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci source_id = kcontrol->private_value; 48162306a36Sopenharmony_ci ogain = emu->i2c_capture_volume[source_id][0]; /* Left */ 48262306a36Sopenharmony_ci ngain = ucontrol->value.integer.value[0]; 48362306a36Sopenharmony_ci if (ngain > 0xff) 48462306a36Sopenharmony_ci return -EINVAL; 48562306a36Sopenharmony_ci if (ogain != ngain) { 48662306a36Sopenharmony_ci if (emu->i2c_capture_source == source_id) 48762306a36Sopenharmony_ci snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) ); 48862306a36Sopenharmony_ci emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0]; 48962306a36Sopenharmony_ci change = 1; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci ogain = emu->i2c_capture_volume[source_id][1]; /* Right */ 49262306a36Sopenharmony_ci ngain = ucontrol->value.integer.value[1]; 49362306a36Sopenharmony_ci if (ngain > 0xff) 49462306a36Sopenharmony_ci return -EINVAL; 49562306a36Sopenharmony_ci if (ogain != ngain) { 49662306a36Sopenharmony_ci if (emu->i2c_capture_source == source_id) 49762306a36Sopenharmony_ci snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); 49862306a36Sopenharmony_ci emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1]; 49962306a36Sopenharmony_ci change = 1; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return change; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci#define spi_mute_info snd_ctl_boolean_mono_info 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int spi_mute_get(struct snd_kcontrol *kcontrol, 50862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 51162306a36Sopenharmony_ci unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 51262306a36Sopenharmony_ci unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit); 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int spi_mute_put(struct snd_kcontrol *kcontrol, 51962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); 52262306a36Sopenharmony_ci unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; 52362306a36Sopenharmony_ci unsigned int bit = kcontrol->private_value & SPI_REG_MASK; 52462306a36Sopenharmony_ci int ret; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci ret = emu->spi_dac_reg[reg] & bit; 52762306a36Sopenharmony_ci if (ucontrol->value.integer.value[0]) { 52862306a36Sopenharmony_ci if (!ret) /* bit already cleared, do nothing */ 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci emu->spi_dac_reg[reg] &= ~bit; 53162306a36Sopenharmony_ci } else { 53262306a36Sopenharmony_ci if (ret) /* bit already set, do nothing */ 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci emu->spi_dac_reg[reg] |= bit; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]); 53862306a36Sopenharmony_ci return ret ? -EINVAL : 1; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci#define CA_VOLUME(xname,chid,reg) \ 54262306a36Sopenharmony_ci{ \ 54362306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 54462306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 54562306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 54662306a36Sopenharmony_ci .info = snd_ca0106_volume_info, \ 54762306a36Sopenharmony_ci .get = snd_ca0106_volume_get, \ 54862306a36Sopenharmony_ci .put = snd_ca0106_volume_put, \ 54962306a36Sopenharmony_ci .tlv = { .p = snd_ca0106_db_scale1 }, \ 55062306a36Sopenharmony_ci .private_value = ((chid) << 8) | (reg) \ 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ca0106_volume_ctls[] = { 55462306a36Sopenharmony_ci CA_VOLUME("Analog Front Playback Volume", 55562306a36Sopenharmony_ci CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2), 55662306a36Sopenharmony_ci CA_VOLUME("Analog Rear Playback Volume", 55762306a36Sopenharmony_ci CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2), 55862306a36Sopenharmony_ci CA_VOLUME("Analog Center/LFE Playback Volume", 55962306a36Sopenharmony_ci CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2), 56062306a36Sopenharmony_ci CA_VOLUME("Analog Side Playback Volume", 56162306a36Sopenharmony_ci CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2), 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci CA_VOLUME("IEC958 Front Playback Volume", 56462306a36Sopenharmony_ci CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1), 56562306a36Sopenharmony_ci CA_VOLUME("IEC958 Rear Playback Volume", 56662306a36Sopenharmony_ci CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1), 56762306a36Sopenharmony_ci CA_VOLUME("IEC958 Center/LFE Playback Volume", 56862306a36Sopenharmony_ci CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1), 56962306a36Sopenharmony_ci CA_VOLUME("IEC958 Unknown Playback Volume", 57062306a36Sopenharmony_ci CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1), 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci CA_VOLUME("CAPTURE feedback Playback Volume", 57362306a36Sopenharmony_ci 1, CAPTURE_CONTROL), 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci { 57662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 57762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 57862306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 57962306a36Sopenharmony_ci .count = 4, 58062306a36Sopenharmony_ci .info = snd_ca0106_spdif_info, 58162306a36Sopenharmony_ci .get = snd_ca0106_spdif_get_mask 58262306a36Sopenharmony_ci }, 58362306a36Sopenharmony_ci { 58462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 58562306a36Sopenharmony_ci .name = "IEC958 Playback Switch", 58662306a36Sopenharmony_ci .info = snd_ca0106_shared_spdif_info, 58762306a36Sopenharmony_ci .get = snd_ca0106_shared_spdif_get, 58862306a36Sopenharmony_ci .put = snd_ca0106_shared_spdif_put 58962306a36Sopenharmony_ci }, 59062306a36Sopenharmony_ci { 59162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 59262306a36Sopenharmony_ci .name = "Digital Source Capture Enum", 59362306a36Sopenharmony_ci .info = snd_ca0106_capture_source_info, 59462306a36Sopenharmony_ci .get = snd_ca0106_capture_source_get, 59562306a36Sopenharmony_ci .put = snd_ca0106_capture_source_put 59662306a36Sopenharmony_ci }, 59762306a36Sopenharmony_ci { 59862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 59962306a36Sopenharmony_ci .name = "Analog Source Capture Enum", 60062306a36Sopenharmony_ci .info = snd_ca0106_i2c_capture_source_info, 60162306a36Sopenharmony_ci .get = snd_ca0106_i2c_capture_source_get, 60262306a36Sopenharmony_ci .put = snd_ca0106_i2c_capture_source_put 60362306a36Sopenharmony_ci }, 60462306a36Sopenharmony_ci { 60562306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 60662306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 60762306a36Sopenharmony_ci .count = 4, 60862306a36Sopenharmony_ci .info = snd_ca0106_spdif_info, 60962306a36Sopenharmony_ci .get = snd_ca0106_spdif_get_default, 61062306a36Sopenharmony_ci .put = snd_ca0106_spdif_put_default 61162306a36Sopenharmony_ci }, 61262306a36Sopenharmony_ci { 61362306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 61462306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), 61562306a36Sopenharmony_ci .count = 4, 61662306a36Sopenharmony_ci .info = snd_ca0106_spdif_info, 61762306a36Sopenharmony_ci .get = snd_ca0106_spdif_get_stream, 61862306a36Sopenharmony_ci .put = snd_ca0106_spdif_put_stream 61962306a36Sopenharmony_ci }, 62062306a36Sopenharmony_ci}; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci#define I2C_VOLUME(xname,chid) \ 62362306a36Sopenharmony_ci{ \ 62462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 62562306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 62662306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 62762306a36Sopenharmony_ci .info = snd_ca0106_i2c_volume_info, \ 62862306a36Sopenharmony_ci .get = snd_ca0106_i2c_volume_get, \ 62962306a36Sopenharmony_ci .put = snd_ca0106_i2c_volume_put, \ 63062306a36Sopenharmony_ci .tlv = { .p = snd_ca0106_db_scale2 }, \ 63162306a36Sopenharmony_ci .private_value = chid \ 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] = { 63562306a36Sopenharmony_ci I2C_VOLUME("Phone Capture Volume", 0), 63662306a36Sopenharmony_ci I2C_VOLUME("Mic Capture Volume", 1), 63762306a36Sopenharmony_ci I2C_VOLUME("Line in Capture Volume", 2), 63862306a36Sopenharmony_ci I2C_VOLUME("Aux Capture Volume", 3), 63962306a36Sopenharmony_ci}; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic const int spi_dmute_reg[] = { 64262306a36Sopenharmony_ci SPI_DMUTE0_REG, 64362306a36Sopenharmony_ci SPI_DMUTE1_REG, 64462306a36Sopenharmony_ci SPI_DMUTE2_REG, 64562306a36Sopenharmony_ci 0, 64662306a36Sopenharmony_ci SPI_DMUTE4_REG, 64762306a36Sopenharmony_ci}; 64862306a36Sopenharmony_cistatic const int spi_dmute_bit[] = { 64962306a36Sopenharmony_ci SPI_DMUTE0_BIT, 65062306a36Sopenharmony_ci SPI_DMUTE1_BIT, 65162306a36Sopenharmony_ci SPI_DMUTE2_BIT, 65262306a36Sopenharmony_ci 0, 65362306a36Sopenharmony_ci SPI_DMUTE4_BIT, 65462306a36Sopenharmony_ci}; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic struct snd_kcontrol_new 65762306a36Sopenharmony_cisnd_ca0106_volume_spi_dac_ctl(const struct snd_ca0106_details *details, 65862306a36Sopenharmony_ci int channel_id) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct snd_kcontrol_new spi_switch = {0}; 66162306a36Sopenharmony_ci int reg, bit; 66262306a36Sopenharmony_ci int dac_id; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci spi_switch.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 66562306a36Sopenharmony_ci spi_switch.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 66662306a36Sopenharmony_ci spi_switch.info = spi_mute_info; 66762306a36Sopenharmony_ci spi_switch.get = spi_mute_get; 66862306a36Sopenharmony_ci spi_switch.put = spi_mute_put; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci switch (channel_id) { 67162306a36Sopenharmony_ci case PCM_FRONT_CHANNEL: 67262306a36Sopenharmony_ci spi_switch.name = "Analog Front Playback Switch"; 67362306a36Sopenharmony_ci dac_id = (details->spi_dac & 0xf000) >> (4 * 3); 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci case PCM_REAR_CHANNEL: 67662306a36Sopenharmony_ci spi_switch.name = "Analog Rear Playback Switch"; 67762306a36Sopenharmony_ci dac_id = (details->spi_dac & 0x0f00) >> (4 * 2); 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci case PCM_CENTER_LFE_CHANNEL: 68062306a36Sopenharmony_ci spi_switch.name = "Analog Center/LFE Playback Switch"; 68162306a36Sopenharmony_ci dac_id = (details->spi_dac & 0x00f0) >> (4 * 1); 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci case PCM_UNKNOWN_CHANNEL: 68462306a36Sopenharmony_ci spi_switch.name = "Analog Side Playback Switch"; 68562306a36Sopenharmony_ci dac_id = (details->spi_dac & 0x000f) >> (4 * 0); 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci default: 68862306a36Sopenharmony_ci /* Unused channel */ 68962306a36Sopenharmony_ci spi_switch.name = NULL; 69062306a36Sopenharmony_ci dac_id = 0; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci reg = spi_dmute_reg[dac_id]; 69362306a36Sopenharmony_ci bit = spi_dmute_bit[dac_id]; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci spi_switch.private_value = (reg << SPI_REG_SHIFT) | bit; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci return spi_switch; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int remove_ctl(struct snd_card *card, const char *name) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct snd_ctl_elem_id id; 70362306a36Sopenharmony_ci memset(&id, 0, sizeof(id)); 70462306a36Sopenharmony_ci strcpy(id.name, name); 70562306a36Sopenharmony_ci id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 70662306a36Sopenharmony_ci return snd_ctl_remove_id(card, &id); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int rename_ctl(struct snd_card *card, const char *src, const char *dst) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src); 71262306a36Sopenharmony_ci if (kctl) { 71362306a36Sopenharmony_ci snd_ctl_rename(card, kctl, dst); 71462306a36Sopenharmony_ci return 0; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci return -ENOENT; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci#define ADD_CTLS(emu, ctls) \ 72062306a36Sopenharmony_ci do { \ 72162306a36Sopenharmony_ci int i, _err; \ 72262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctls); i++) { \ 72362306a36Sopenharmony_ci _err = snd_ctl_add(card, snd_ctl_new1(&ctls[i], emu)); \ 72462306a36Sopenharmony_ci if (_err < 0) \ 72562306a36Sopenharmony_ci return _err; \ 72662306a36Sopenharmony_ci } \ 72762306a36Sopenharmony_ci } while (0) 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic 73062306a36Sopenharmony_ciDECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic const char * const follower_vols[] = { 73362306a36Sopenharmony_ci "Analog Front Playback Volume", 73462306a36Sopenharmony_ci "Analog Rear Playback Volume", 73562306a36Sopenharmony_ci "Analog Center/LFE Playback Volume", 73662306a36Sopenharmony_ci "Analog Side Playback Volume", 73762306a36Sopenharmony_ci "IEC958 Front Playback Volume", 73862306a36Sopenharmony_ci "IEC958 Rear Playback Volume", 73962306a36Sopenharmony_ci "IEC958 Center/LFE Playback Volume", 74062306a36Sopenharmony_ci "IEC958 Unknown Playback Volume", 74162306a36Sopenharmony_ci "CAPTURE feedback Playback Volume", 74262306a36Sopenharmony_ci NULL 74362306a36Sopenharmony_ci}; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic const char * const follower_sws[] = { 74662306a36Sopenharmony_ci "Analog Front Playback Switch", 74762306a36Sopenharmony_ci "Analog Rear Playback Switch", 74862306a36Sopenharmony_ci "Analog Center/LFE Playback Switch", 74962306a36Sopenharmony_ci "Analog Side Playback Switch", 75062306a36Sopenharmony_ci "IEC958 Playback Switch", 75162306a36Sopenharmony_ci NULL 75262306a36Sopenharmony_ci}; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ciint snd_ca0106_mixer(struct snd_ca0106 *emu) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci int err; 75762306a36Sopenharmony_ci struct snd_card *card = emu->card; 75862306a36Sopenharmony_ci const char * const *c; 75962306a36Sopenharmony_ci struct snd_kcontrol *vmaster; 76062306a36Sopenharmony_ci static const char * const ca0106_remove_ctls[] = { 76162306a36Sopenharmony_ci "Master Mono Playback Switch", 76262306a36Sopenharmony_ci "Master Mono Playback Volume", 76362306a36Sopenharmony_ci "3D Control - Switch", 76462306a36Sopenharmony_ci "3D Control Sigmatel - Depth", 76562306a36Sopenharmony_ci "PCM Playback Switch", 76662306a36Sopenharmony_ci "PCM Playback Volume", 76762306a36Sopenharmony_ci "CD Playback Switch", 76862306a36Sopenharmony_ci "CD Playback Volume", 76962306a36Sopenharmony_ci "Phone Playback Switch", 77062306a36Sopenharmony_ci "Phone Playback Volume", 77162306a36Sopenharmony_ci "Video Playback Switch", 77262306a36Sopenharmony_ci "Video Playback Volume", 77362306a36Sopenharmony_ci "Beep Playback Switch", 77462306a36Sopenharmony_ci "Beep Playback Volume", 77562306a36Sopenharmony_ci "Mono Output Select", 77662306a36Sopenharmony_ci "Capture Source", 77762306a36Sopenharmony_ci "Capture Switch", 77862306a36Sopenharmony_ci "Capture Volume", 77962306a36Sopenharmony_ci "External Amplifier", 78062306a36Sopenharmony_ci "Sigmatel 4-Speaker Stereo Playback Switch", 78162306a36Sopenharmony_ci "Surround Phase Inversion Playback Switch", 78262306a36Sopenharmony_ci NULL 78362306a36Sopenharmony_ci }; 78462306a36Sopenharmony_ci static const char * const ca0106_rename_ctls[] = { 78562306a36Sopenharmony_ci "Master Playback Switch", "Capture Switch", 78662306a36Sopenharmony_ci "Master Playback Volume", "Capture Volume", 78762306a36Sopenharmony_ci "Line Playback Switch", "AC97 Line Capture Switch", 78862306a36Sopenharmony_ci "Line Playback Volume", "AC97 Line Capture Volume", 78962306a36Sopenharmony_ci "Aux Playback Switch", "AC97 Aux Capture Switch", 79062306a36Sopenharmony_ci "Aux Playback Volume", "AC97 Aux Capture Volume", 79162306a36Sopenharmony_ci "Mic Playback Switch", "AC97 Mic Capture Switch", 79262306a36Sopenharmony_ci "Mic Playback Volume", "AC97 Mic Capture Volume", 79362306a36Sopenharmony_ci "Mic Select", "AC97 Mic Select", 79462306a36Sopenharmony_ci "Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)", 79562306a36Sopenharmony_ci NULL 79662306a36Sopenharmony_ci }; 79762306a36Sopenharmony_ci#if 1 79862306a36Sopenharmony_ci for (c = ca0106_remove_ctls; *c; c++) 79962306a36Sopenharmony_ci remove_ctl(card, *c); 80062306a36Sopenharmony_ci for (c = ca0106_rename_ctls; *c; c += 2) 80162306a36Sopenharmony_ci rename_ctl(card, c[0], c[1]); 80262306a36Sopenharmony_ci#endif 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci ADD_CTLS(emu, snd_ca0106_volume_ctls); 80562306a36Sopenharmony_ci if (emu->details->i2c_adc == 1) { 80662306a36Sopenharmony_ci ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls); 80762306a36Sopenharmony_ci if (emu->details->gpio_type == 1) 80862306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); 80962306a36Sopenharmony_ci else /* gpio_type == 2 */ 81062306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu)); 81162306a36Sopenharmony_ci if (err < 0) 81262306a36Sopenharmony_ci return err; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci if (emu->details->spi_dac) { 81562306a36Sopenharmony_ci int i; 81662306a36Sopenharmony_ci for (i = 0;; i++) { 81762306a36Sopenharmony_ci struct snd_kcontrol_new ctl; 81862306a36Sopenharmony_ci ctl = snd_ca0106_volume_spi_dac_ctl(emu->details, i); 81962306a36Sopenharmony_ci if (!ctl.name) 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&ctl, emu)); 82262306a36Sopenharmony_ci if (err < 0) 82362306a36Sopenharmony_ci return err; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* Create virtual master controls */ 82862306a36Sopenharmony_ci vmaster = snd_ctl_make_virtual_master("Master Playback Volume", 82962306a36Sopenharmony_ci snd_ca0106_master_db_scale); 83062306a36Sopenharmony_ci if (!vmaster) 83162306a36Sopenharmony_ci return -ENOMEM; 83262306a36Sopenharmony_ci err = snd_ctl_add(card, vmaster); 83362306a36Sopenharmony_ci if (err < 0) 83462306a36Sopenharmony_ci return err; 83562306a36Sopenharmony_ci err = snd_ctl_add_followers(card, vmaster, follower_vols); 83662306a36Sopenharmony_ci if (err < 0) 83762306a36Sopenharmony_ci return err; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (emu->details->spi_dac) { 84062306a36Sopenharmony_ci vmaster = snd_ctl_make_virtual_master("Master Playback Switch", 84162306a36Sopenharmony_ci NULL); 84262306a36Sopenharmony_ci if (!vmaster) 84362306a36Sopenharmony_ci return -ENOMEM; 84462306a36Sopenharmony_ci err = snd_ctl_add(card, vmaster); 84562306a36Sopenharmony_ci if (err < 0) 84662306a36Sopenharmony_ci return err; 84762306a36Sopenharmony_ci err = snd_ctl_add_followers(card, vmaster, follower_sws); 84862306a36Sopenharmony_ci if (err < 0) 84962306a36Sopenharmony_ci return err; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci strcpy(card->mixername, "CA0106"); 85362306a36Sopenharmony_ci return 0; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 85762306a36Sopenharmony_cistruct ca0106_vol_tbl { 85862306a36Sopenharmony_ci unsigned int channel_id; 85962306a36Sopenharmony_ci unsigned int reg; 86062306a36Sopenharmony_ci}; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic const struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = { 86362306a36Sopenharmony_ci { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 }, 86462306a36Sopenharmony_ci { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 }, 86562306a36Sopenharmony_ci { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 }, 86662306a36Sopenharmony_ci { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2 }, 86762306a36Sopenharmony_ci { CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1 }, 86862306a36Sopenharmony_ci { CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1 }, 86962306a36Sopenharmony_ci { CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1 }, 87062306a36Sopenharmony_ci { CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1 }, 87162306a36Sopenharmony_ci { 1, CAPTURE_CONTROL }, 87262306a36Sopenharmony_ci}; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_civoid snd_ca0106_mixer_suspend(struct snd_ca0106 *chip) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci int i; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* save volumes */ 87962306a36Sopenharmony_ci for (i = 0; i < NUM_SAVED_VOLUMES; i++) 88062306a36Sopenharmony_ci chip->saved_vol[i] = 88162306a36Sopenharmony_ci snd_ca0106_ptr_read(chip, saved_volumes[i].reg, 88262306a36Sopenharmony_ci saved_volumes[i].channel_id); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_civoid snd_ca0106_mixer_resume(struct snd_ca0106 *chip) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci int i; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci for (i = 0; i < NUM_SAVED_VOLUMES; i++) 89062306a36Sopenharmony_ci snd_ca0106_ptr_write(chip, saved_volumes[i].reg, 89162306a36Sopenharmony_ci saved_volumes[i].channel_id, 89262306a36Sopenharmony_ci chip->saved_vol[i]); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ca0106_spdif_enable(chip); 89562306a36Sopenharmony_ci ca0106_set_capture_source(chip); 89662306a36Sopenharmony_ci ca0106_set_i2c_capture_source(chip, chip->i2c_capture_source, 1); 89762306a36Sopenharmony_ci for (i = 0; i < 4; i++) 89862306a36Sopenharmony_ci ca0106_set_spdif_bits(chip, i); 89962306a36Sopenharmony_ci if (chip->details->i2c_adc) 90062306a36Sopenharmony_ci ca0106_set_capture_mic_line_in(chip); 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 903