18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Mixer controls for the Xonar DG/DGX 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 68c2ecf20Sopenharmony_ci * Copyright (c) Roman Volkov <v1ron@mail.ru> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <sound/control.h> 128c2ecf20Sopenharmony_ci#include <sound/core.h> 138c2ecf20Sopenharmony_ci#include <sound/info.h> 148c2ecf20Sopenharmony_ci#include <sound/pcm.h> 158c2ecf20Sopenharmony_ci#include <sound/tlv.h> 168c2ecf20Sopenharmony_ci#include "oxygen.h" 178c2ecf20Sopenharmony_ci#include "xonar_dg.h" 188c2ecf20Sopenharmony_ci#include "cs4245.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* analog output select */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int output_select_apply(struct oxygen *chip) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK; 278c2ecf20Sopenharmony_ci if (data->output_sel == PLAYBACK_DST_HP) { 288c2ecf20Sopenharmony_ci /* mute FP (aux output) amplifier, switch rear jack to CS4245 */ 298c2ecf20Sopenharmony_ci oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); 308c2ecf20Sopenharmony_ci } else if (data->output_sel == PLAYBACK_DST_HP_FP) { 318c2ecf20Sopenharmony_ci /* 328c2ecf20Sopenharmony_ci * Unmute FP amplifier, switch rear jack to CS4361; 338c2ecf20Sopenharmony_ci * I2S channels 2,3,4 should be inactive. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); 368c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC; 378c2ecf20Sopenharmony_ci } else { 388c2ecf20Sopenharmony_ci /* 398c2ecf20Sopenharmony_ci * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp., 408c2ecf20Sopenharmony_ci * and change playback routing. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); 438c2ecf20Sopenharmony_ci } 448c2ecf20Sopenharmony_ci return cs4245_write_spi(chip, CS4245_SIGNAL_SEL); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int output_select_info(struct snd_kcontrol *ctl, 488c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *info) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci static const char *const names[3] = { 518c2ecf20Sopenharmony_ci "Stereo Headphones", 528c2ecf20Sopenharmony_ci "Stereo Headphones FP", 538c2ecf20Sopenharmony_ci "Multichannel", 548c2ecf20Sopenharmony_ci }; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return snd_ctl_enum_info(info, 1, 3, names); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int output_select_get(struct snd_kcontrol *ctl, 608c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *value) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 638c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 668c2ecf20Sopenharmony_ci value->value.enumerated.item[0] = data->output_sel; 678c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int output_select_put(struct snd_kcontrol *ctl, 728c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *value) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 758c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 768c2ecf20Sopenharmony_ci unsigned int new = value->value.enumerated.item[0]; 778c2ecf20Sopenharmony_ci int changed = 0; 788c2ecf20Sopenharmony_ci int ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 818c2ecf20Sopenharmony_ci if (data->output_sel != new) { 828c2ecf20Sopenharmony_ci data->output_sel = new; 838c2ecf20Sopenharmony_ci ret = output_select_apply(chip); 848c2ecf20Sopenharmony_ci changed = ret >= 0 ? 1 : ret; 858c2ecf20Sopenharmony_ci oxygen_update_dac_routing(chip); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return changed; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* CS4245 Headphone Channels A&B Volume Control */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int hp_stereo_volume_info(struct snd_kcontrol *ctl, 958c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *info) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 988c2ecf20Sopenharmony_ci info->count = 2; 998c2ecf20Sopenharmony_ci info->value.integer.min = 0; 1008c2ecf20Sopenharmony_ci info->value.integer.max = 255; 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int hp_stereo_volume_get(struct snd_kcontrol *ctl, 1058c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *val) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 1088c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 1098c2ecf20Sopenharmony_ci unsigned int tmp; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 1128c2ecf20Sopenharmony_ci tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255; 1138c2ecf20Sopenharmony_ci val->value.integer.value[0] = tmp; 1148c2ecf20Sopenharmony_ci tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255; 1158c2ecf20Sopenharmony_ci val->value.integer.value[1] = tmp; 1168c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int hp_stereo_volume_put(struct snd_kcontrol *ctl, 1218c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *val) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 1248c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 1258c2ecf20Sopenharmony_ci int ret; 1268c2ecf20Sopenharmony_ci int changed = 0; 1278c2ecf20Sopenharmony_ci long new1 = val->value.integer.value[0]; 1288c2ecf20Sopenharmony_ci long new2 = val->value.integer.value[1]; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0)) 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 1348c2ecf20Sopenharmony_ci if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) || 1358c2ecf20Sopenharmony_ci (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) { 1368c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1; 1378c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2; 1388c2ecf20Sopenharmony_ci ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL); 1398c2ecf20Sopenharmony_ci if (ret >= 0) 1408c2ecf20Sopenharmony_ci ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL); 1418c2ecf20Sopenharmony_ci changed = ret >= 0 ? 1 : ret; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return changed; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* Headphone Mute */ 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int hp_mute_get(struct snd_kcontrol *ctl, 1518c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *val) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 1548c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 1578c2ecf20Sopenharmony_ci val->value.integer.value[0] = 1588c2ecf20Sopenharmony_ci !(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC); 1598c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int hp_mute_put(struct snd_kcontrol *ctl, 1648c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *val) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 1678c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 1688c2ecf20Sopenharmony_ci int ret; 1698c2ecf20Sopenharmony_ci int changed; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (val->value.integer.value[0] > 1) 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 1748c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC; 1758c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_DAC_CTRL_1] |= 1768c2ecf20Sopenharmony_ci (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC; 1778c2ecf20Sopenharmony_ci ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1); 1788c2ecf20Sopenharmony_ci changed = ret >= 0 ? 1 : ret; 1798c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 1808c2ecf20Sopenharmony_ci return changed; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/* capture volume for all sources */ 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int input_volume_apply(struct oxygen *chip, char left, char right) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 1888c2ecf20Sopenharmony_ci int ret; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_PGA_A_CTRL] = left; 1918c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_PGA_B_CTRL] = right; 1928c2ecf20Sopenharmony_ci ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL); 1938c2ecf20Sopenharmony_ci if (ret < 0) 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci return cs4245_write_spi(chip, CS4245_PGA_B_CTRL); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int input_vol_info(struct snd_kcontrol *ctl, 1998c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *info) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 2028c2ecf20Sopenharmony_ci info->count = 2; 2038c2ecf20Sopenharmony_ci info->value.integer.min = 2 * -12; 2048c2ecf20Sopenharmony_ci info->value.integer.max = 2 * 12; 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int input_vol_get(struct snd_kcontrol *ctl, 2098c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *value) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 2128c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 2138c2ecf20Sopenharmony_ci unsigned int idx = ctl->private_value; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 2168c2ecf20Sopenharmony_ci value->value.integer.value[0] = data->input_vol[idx][0]; 2178c2ecf20Sopenharmony_ci value->value.integer.value[1] = data->input_vol[idx][1]; 2188c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int input_vol_put(struct snd_kcontrol *ctl, 2238c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *value) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 2268c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 2278c2ecf20Sopenharmony_ci unsigned int idx = ctl->private_value; 2288c2ecf20Sopenharmony_ci int changed = 0; 2298c2ecf20Sopenharmony_ci int ret = 0; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (value->value.integer.value[0] < 2 * -12 || 2328c2ecf20Sopenharmony_ci value->value.integer.value[0] > 2 * 12 || 2338c2ecf20Sopenharmony_ci value->value.integer.value[1] < 2 * -12 || 2348c2ecf20Sopenharmony_ci value->value.integer.value[1] > 2 * 12) 2358c2ecf20Sopenharmony_ci return -EINVAL; 2368c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 2378c2ecf20Sopenharmony_ci changed = data->input_vol[idx][0] != value->value.integer.value[0] || 2388c2ecf20Sopenharmony_ci data->input_vol[idx][1] != value->value.integer.value[1]; 2398c2ecf20Sopenharmony_ci if (changed) { 2408c2ecf20Sopenharmony_ci data->input_vol[idx][0] = value->value.integer.value[0]; 2418c2ecf20Sopenharmony_ci data->input_vol[idx][1] = value->value.integer.value[1]; 2428c2ecf20Sopenharmony_ci if (idx == data->input_sel) { 2438c2ecf20Sopenharmony_ci ret = input_volume_apply(chip, 2448c2ecf20Sopenharmony_ci data->input_vol[idx][0], 2458c2ecf20Sopenharmony_ci data->input_vol[idx][1]); 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci changed = ret >= 0 ? 1 : ret; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 2508c2ecf20Sopenharmony_ci return changed; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* Capture Source */ 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int input_source_apply(struct oxygen *chip) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK; 2608c2ecf20Sopenharmony_ci if (data->input_sel == CAPTURE_SRC_FP_MIC) 2618c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2; 2628c2ecf20Sopenharmony_ci else if (data->input_sel == CAPTURE_SRC_LINE) 2638c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4; 2648c2ecf20Sopenharmony_ci else if (data->input_sel != CAPTURE_SRC_MIC) 2658c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1; 2668c2ecf20Sopenharmony_ci return cs4245_write_spi(chip, CS4245_ANALOG_IN); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int input_sel_info(struct snd_kcontrol *ctl, 2708c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *info) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci static const char *const names[4] = { 2738c2ecf20Sopenharmony_ci "Mic", "Front Mic", "Line", "Aux" 2748c2ecf20Sopenharmony_ci }; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return snd_ctl_enum_info(info, 1, 4, names); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int input_sel_get(struct snd_kcontrol *ctl, 2808c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *value) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 2838c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 2868c2ecf20Sopenharmony_ci value->value.enumerated.item[0] = data->input_sel; 2878c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int input_sel_put(struct snd_kcontrol *ctl, 2928c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *value) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 2958c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 2968c2ecf20Sopenharmony_ci int changed; 2978c2ecf20Sopenharmony_ci int ret; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (value->value.enumerated.item[0] > 3) 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 3038c2ecf20Sopenharmony_ci changed = value->value.enumerated.item[0] != data->input_sel; 3048c2ecf20Sopenharmony_ci if (changed) { 3058c2ecf20Sopenharmony_ci data->input_sel = value->value.enumerated.item[0]; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret = input_source_apply(chip); 3088c2ecf20Sopenharmony_ci if (ret >= 0) 3098c2ecf20Sopenharmony_ci ret = input_volume_apply(chip, 3108c2ecf20Sopenharmony_ci data->input_vol[data->input_sel][0], 3118c2ecf20Sopenharmony_ci data->input_vol[data->input_sel][1]); 3128c2ecf20Sopenharmony_ci changed = ret >= 0 ? 1 : ret; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 3158c2ecf20Sopenharmony_ci return changed; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* ADC high-pass filter */ 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci static const char *const names[2] = { "Active", "Frozen" }; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci return snd_ctl_enum_info(info, 1, 2, names); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 3308c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci value->value.enumerated.item[0] = 3338c2ecf20Sopenharmony_ci !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct oxygen *chip = ctl->private_data; 3408c2ecf20Sopenharmony_ci struct dg *data = chip->model_data; 3418c2ecf20Sopenharmony_ci u8 reg; 3428c2ecf20Sopenharmony_ci int changed; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 3458c2ecf20Sopenharmony_ci reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; 3468c2ecf20Sopenharmony_ci if (value->value.enumerated.item[0]) 3478c2ecf20Sopenharmony_ci reg |= CS4245_HPF_FREEZE; 3488c2ecf20Sopenharmony_ci changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; 3498c2ecf20Sopenharmony_ci if (changed) { 3508c2ecf20Sopenharmony_ci data->cs4245_shadow[CS4245_ADC_CTRL] = reg; 3518c2ecf20Sopenharmony_ci cs4245_write_spi(chip, CS4245_ADC_CTRL); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 3548c2ecf20Sopenharmony_ci return changed; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci#define INPUT_VOLUME(xname, index) { \ 3588c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 3598c2ecf20Sopenharmony_ci .name = xname, \ 3608c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 3618c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 3628c2ecf20Sopenharmony_ci .info = input_vol_info, \ 3638c2ecf20Sopenharmony_ci .get = input_vol_get, \ 3648c2ecf20Sopenharmony_ci .put = input_vol_put, \ 3658c2ecf20Sopenharmony_ci .tlv = { .p = pga_db_scale }, \ 3668c2ecf20Sopenharmony_ci .private_value = index, \ 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0); 3698c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200); 3708c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new dg_controls[] = { 3718c2ecf20Sopenharmony_ci { 3728c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3738c2ecf20Sopenharmony_ci .name = "Analog Output Playback Enum", 3748c2ecf20Sopenharmony_ci .info = output_select_info, 3758c2ecf20Sopenharmony_ci .get = output_select_get, 3768c2ecf20Sopenharmony_ci .put = output_select_put, 3778c2ecf20Sopenharmony_ci }, 3788c2ecf20Sopenharmony_ci { 3798c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3808c2ecf20Sopenharmony_ci .name = "Headphone Playback Volume", 3818c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 3828c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ, 3838c2ecf20Sopenharmony_ci .info = hp_stereo_volume_info, 3848c2ecf20Sopenharmony_ci .get = hp_stereo_volume_get, 3858c2ecf20Sopenharmony_ci .put = hp_stereo_volume_put, 3868c2ecf20Sopenharmony_ci .tlv = { .p = hp_db_scale, }, 3878c2ecf20Sopenharmony_ci }, 3888c2ecf20Sopenharmony_ci { 3898c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3908c2ecf20Sopenharmony_ci .name = "Headphone Playback Switch", 3918c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 3928c2ecf20Sopenharmony_ci .info = snd_ctl_boolean_mono_info, 3938c2ecf20Sopenharmony_ci .get = hp_mute_get, 3948c2ecf20Sopenharmony_ci .put = hp_mute_put, 3958c2ecf20Sopenharmony_ci }, 3968c2ecf20Sopenharmony_ci INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC), 3978c2ecf20Sopenharmony_ci INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC), 3988c2ecf20Sopenharmony_ci INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE), 3998c2ecf20Sopenharmony_ci INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX), 4008c2ecf20Sopenharmony_ci { 4018c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4028c2ecf20Sopenharmony_ci .name = "Capture Source", 4038c2ecf20Sopenharmony_ci .info = input_sel_info, 4048c2ecf20Sopenharmony_ci .get = input_sel_get, 4058c2ecf20Sopenharmony_ci .put = input_sel_put, 4068c2ecf20Sopenharmony_ci }, 4078c2ecf20Sopenharmony_ci { 4088c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4098c2ecf20Sopenharmony_ci .name = "ADC High-pass Filter Capture Enum", 4108c2ecf20Sopenharmony_ci .info = hpf_info, 4118c2ecf20Sopenharmony_ci .get = hpf_get, 4128c2ecf20Sopenharmony_ci .put = hpf_put, 4138c2ecf20Sopenharmony_ci }, 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int dg_control_filter(struct snd_kcontrol_new *template) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci if (!strncmp(template->name, "Master Playback ", 16)) 4198c2ecf20Sopenharmony_ci return 1; 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int dg_mixer_init(struct oxygen *chip) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci unsigned int i; 4268c2ecf20Sopenharmony_ci int err; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci output_select_apply(chip); 4298c2ecf20Sopenharmony_ci input_source_apply(chip); 4308c2ecf20Sopenharmony_ci oxygen_update_dac_routing(chip); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { 4338c2ecf20Sopenharmony_ci err = snd_ctl_add(chip->card, 4348c2ecf20Sopenharmony_ci snd_ctl_new1(&dg_controls[i], chip)); 4358c2ecf20Sopenharmony_ci if (err < 0) 4368c2ecf20Sopenharmony_ci return err; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ciconst struct oxygen_model model_xonar_dg = { 4438c2ecf20Sopenharmony_ci .longname = "C-Media Oxygen HD Audio", 4448c2ecf20Sopenharmony_ci .chip = "CMI8786", 4458c2ecf20Sopenharmony_ci .init = dg_init, 4468c2ecf20Sopenharmony_ci .control_filter = dg_control_filter, 4478c2ecf20Sopenharmony_ci .mixer_init = dg_mixer_init, 4488c2ecf20Sopenharmony_ci .cleanup = dg_cleanup, 4498c2ecf20Sopenharmony_ci .suspend = dg_suspend, 4508c2ecf20Sopenharmony_ci .resume = dg_resume, 4518c2ecf20Sopenharmony_ci .set_dac_params = set_cs4245_dac_params, 4528c2ecf20Sopenharmony_ci .set_adc_params = set_cs4245_adc_params, 4538c2ecf20Sopenharmony_ci .adjust_dac_routing = adjust_dg_dac_routing, 4548c2ecf20Sopenharmony_ci .dump_registers = dump_cs4245_registers, 4558c2ecf20Sopenharmony_ci .model_data_size = sizeof(struct dg), 4568c2ecf20Sopenharmony_ci .device_config = PLAYBACK_0_TO_I2S | 4578c2ecf20Sopenharmony_ci PLAYBACK_1_TO_SPDIF | 4588c2ecf20Sopenharmony_ci CAPTURE_0_FROM_I2S_1 | 4598c2ecf20Sopenharmony_ci CAPTURE_1_FROM_SPDIF, 4608c2ecf20Sopenharmony_ci .dac_channels_pcm = 6, 4618c2ecf20Sopenharmony_ci .dac_channels_mixer = 0, 4628c2ecf20Sopenharmony_ci .function_flags = OXYGEN_FUNCTION_SPI, 4638c2ecf20Sopenharmony_ci .dac_mclks = OXYGEN_MCLKS(256, 128, 128), 4648c2ecf20Sopenharmony_ci .adc_mclks = OXYGEN_MCLKS(256, 128, 128), 4658c2ecf20Sopenharmony_ci .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 4668c2ecf20Sopenharmony_ci .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 4678c2ecf20Sopenharmony_ci}; 468