1*b67eb152SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2041f26b6SRoman Volkov /* 3041f26b6SRoman Volkov * Mixer controls for the Xonar DG/DGX 4041f26b6SRoman Volkov * 5041f26b6SRoman Volkov * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 6041f26b6SRoman Volkov * Copyright (c) Roman Volkov <v1ron@mail.ru> 7041f26b6SRoman Volkov */ 8041f26b6SRoman Volkov 9041f26b6SRoman Volkov #include <linux/pci.h> 10041f26b6SRoman Volkov #include <linux/delay.h> 11041f26b6SRoman Volkov #include <sound/control.h> 12041f26b6SRoman Volkov #include <sound/core.h> 13041f26b6SRoman Volkov #include <sound/info.h> 14041f26b6SRoman Volkov #include <sound/pcm.h> 15041f26b6SRoman Volkov #include <sound/tlv.h> 16041f26b6SRoman Volkov #include "oxygen.h" 17041f26b6SRoman Volkov #include "xonar_dg.h" 18041f26b6SRoman Volkov #include "cs4245.h" 19041f26b6SRoman Volkov 202809cb84SRoman Volkov /* analog output select */ 212809cb84SRoman Volkov 222809cb84SRoman Volkov static int output_select_apply(struct oxygen *chip) 232809cb84SRoman Volkov { 242809cb84SRoman Volkov struct dg *data = chip->model_data; 252809cb84SRoman Volkov 262809cb84SRoman Volkov data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK; 272809cb84SRoman Volkov if (data->output_sel == PLAYBACK_DST_HP) { 282809cb84SRoman Volkov /* mute FP (aux output) amplifier, switch rear jack to CS4245 */ 292809cb84SRoman Volkov oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); 302809cb84SRoman Volkov } else if (data->output_sel == PLAYBACK_DST_HP_FP) { 312809cb84SRoman Volkov /* 322809cb84SRoman Volkov * Unmute FP amplifier, switch rear jack to CS4361; 332809cb84SRoman Volkov * I2S channels 2,3,4 should be inactive. 342809cb84SRoman Volkov */ 352809cb84SRoman Volkov oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); 362809cb84SRoman Volkov data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC; 372809cb84SRoman Volkov } else { 382809cb84SRoman Volkov /* 392809cb84SRoman Volkov * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp., 402809cb84SRoman Volkov * and change playback routing. 412809cb84SRoman Volkov */ 422809cb84SRoman Volkov oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); 432809cb84SRoman Volkov } 442809cb84SRoman Volkov return cs4245_write_spi(chip, CS4245_SIGNAL_SEL); 452809cb84SRoman Volkov } 462809cb84SRoman Volkov 472809cb84SRoman Volkov static int output_select_info(struct snd_kcontrol *ctl, 48041f26b6SRoman Volkov struct snd_ctl_elem_info *info) 49041f26b6SRoman Volkov { 50041f26b6SRoman Volkov static const char *const names[3] = { 512809cb84SRoman Volkov "Stereo Headphones", 522809cb84SRoman Volkov "Stereo Headphones FP", 532809cb84SRoman Volkov "Multichannel", 54041f26b6SRoman Volkov }; 55041f26b6SRoman Volkov 56041f26b6SRoman Volkov return snd_ctl_enum_info(info, 1, 3, names); 57041f26b6SRoman Volkov } 58041f26b6SRoman Volkov 592809cb84SRoman Volkov static int output_select_get(struct snd_kcontrol *ctl, 60041f26b6SRoman Volkov struct snd_ctl_elem_value *value) 61041f26b6SRoman Volkov { 62041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 63041f26b6SRoman Volkov struct dg *data = chip->model_data; 64041f26b6SRoman Volkov 65041f26b6SRoman Volkov mutex_lock(&chip->mutex); 66041f26b6SRoman Volkov value->value.enumerated.item[0] = data->output_sel; 67041f26b6SRoman Volkov mutex_unlock(&chip->mutex); 68041f26b6SRoman Volkov return 0; 69041f26b6SRoman Volkov } 70041f26b6SRoman Volkov 712809cb84SRoman Volkov static int output_select_put(struct snd_kcontrol *ctl, 72041f26b6SRoman Volkov struct snd_ctl_elem_value *value) 73041f26b6SRoman Volkov { 74041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 75041f26b6SRoman Volkov struct dg *data = chip->model_data; 762809cb84SRoman Volkov unsigned int new = value->value.enumerated.item[0]; 772809cb84SRoman Volkov int changed = 0; 782809cb84SRoman Volkov int ret; 79041f26b6SRoman Volkov 80041f26b6SRoman Volkov mutex_lock(&chip->mutex); 812809cb84SRoman Volkov if (data->output_sel != new) { 822809cb84SRoman Volkov data->output_sel = new; 832809cb84SRoman Volkov ret = output_select_apply(chip); 842809cb84SRoman Volkov changed = ret >= 0 ? 1 : ret; 852809cb84SRoman Volkov oxygen_update_dac_routing(chip); 86041f26b6SRoman Volkov } 87041f26b6SRoman Volkov mutex_unlock(&chip->mutex); 882809cb84SRoman Volkov 89041f26b6SRoman Volkov return changed; 90041f26b6SRoman Volkov } 91041f26b6SRoman Volkov 92c754639aSRoman Volkov /* CS4245 Headphone Channels A&B Volume Control */ 93c754639aSRoman Volkov 94c754639aSRoman Volkov static int hp_stereo_volume_info(struct snd_kcontrol *ctl, 95041f26b6SRoman Volkov struct snd_ctl_elem_info *info) 96041f26b6SRoman Volkov { 97c754639aSRoman Volkov info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 98c754639aSRoman Volkov info->count = 2; 99c754639aSRoman Volkov info->value.integer.min = 0; 100c754639aSRoman Volkov info->value.integer.max = 255; 101c754639aSRoman Volkov return 0; 102041f26b6SRoman Volkov } 103041f26b6SRoman Volkov 104c754639aSRoman Volkov static int hp_stereo_volume_get(struct snd_kcontrol *ctl, 105c754639aSRoman Volkov struct snd_ctl_elem_value *val) 106041f26b6SRoman Volkov { 107041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 108041f26b6SRoman Volkov struct dg *data = chip->model_data; 109c754639aSRoman Volkov unsigned int tmp; 110041f26b6SRoman Volkov 111041f26b6SRoman Volkov mutex_lock(&chip->mutex); 112c754639aSRoman Volkov tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255; 113c754639aSRoman Volkov val->value.integer.value[0] = tmp; 114c754639aSRoman Volkov tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255; 115c754639aSRoman Volkov val->value.integer.value[1] = tmp; 116041f26b6SRoman Volkov mutex_unlock(&chip->mutex); 117041f26b6SRoman Volkov return 0; 118041f26b6SRoman Volkov } 119041f26b6SRoman Volkov 120c754639aSRoman Volkov static int hp_stereo_volume_put(struct snd_kcontrol *ctl, 121c754639aSRoman Volkov struct snd_ctl_elem_value *val) 122041f26b6SRoman Volkov { 123041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 124041f26b6SRoman Volkov struct dg *data = chip->model_data; 125c754639aSRoman Volkov int ret; 126c754639aSRoman Volkov int changed = 0; 127c754639aSRoman Volkov long new1 = val->value.integer.value[0]; 128c754639aSRoman Volkov long new2 = val->value.integer.value[1]; 129c754639aSRoman Volkov 130c754639aSRoman Volkov if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0)) 131c754639aSRoman Volkov return -EINVAL; 132c754639aSRoman Volkov 133c754639aSRoman Volkov mutex_lock(&chip->mutex); 134c754639aSRoman Volkov if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) || 135c754639aSRoman Volkov (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) { 136c754639aSRoman Volkov data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1; 137c754639aSRoman Volkov data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2; 138c754639aSRoman Volkov ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL); 139c754639aSRoman Volkov if (ret >= 0) 140c754639aSRoman Volkov ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL); 141c754639aSRoman Volkov changed = ret >= 0 ? 1 : ret; 142c754639aSRoman Volkov } 143c754639aSRoman Volkov mutex_unlock(&chip->mutex); 144c754639aSRoman Volkov 145c754639aSRoman Volkov return changed; 146c754639aSRoman Volkov } 147c754639aSRoman Volkov 148c754639aSRoman Volkov /* Headphone Mute */ 149c754639aSRoman Volkov 150c754639aSRoman Volkov static int hp_mute_get(struct snd_kcontrol *ctl, 151c754639aSRoman Volkov struct snd_ctl_elem_value *val) 152c754639aSRoman Volkov { 153c754639aSRoman Volkov struct oxygen *chip = ctl->private_data; 154c754639aSRoman Volkov struct dg *data = chip->model_data; 155c754639aSRoman Volkov 156c754639aSRoman Volkov mutex_lock(&chip->mutex); 157c754639aSRoman Volkov val->value.integer.value[0] = 158c754639aSRoman Volkov !(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC); 159c754639aSRoman Volkov mutex_unlock(&chip->mutex); 160c754639aSRoman Volkov return 0; 161c754639aSRoman Volkov } 162c754639aSRoman Volkov 163c754639aSRoman Volkov static int hp_mute_put(struct snd_kcontrol *ctl, 164c754639aSRoman Volkov struct snd_ctl_elem_value *val) 165c754639aSRoman Volkov { 166c754639aSRoman Volkov struct oxygen *chip = ctl->private_data; 167c754639aSRoman Volkov struct dg *data = chip->model_data; 168c754639aSRoman Volkov int ret; 169041f26b6SRoman Volkov int changed; 170041f26b6SRoman Volkov 171c754639aSRoman Volkov if (val->value.integer.value[0] > 1) 172041f26b6SRoman Volkov return -EINVAL; 173041f26b6SRoman Volkov mutex_lock(&chip->mutex); 174c754639aSRoman Volkov data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC; 175c754639aSRoman Volkov data->cs4245_shadow[CS4245_DAC_CTRL_1] |= 176c754639aSRoman Volkov (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC; 177c754639aSRoman Volkov ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1); 178c754639aSRoman Volkov changed = ret >= 0 ? 1 : ret; 179041f26b6SRoman Volkov mutex_unlock(&chip->mutex); 180041f26b6SRoman Volkov return changed; 181041f26b6SRoman Volkov } 182041f26b6SRoman Volkov 183cf218b2eSRoman Volkov /* capture volume for all sources */ 184cf218b2eSRoman Volkov 185cf218b2eSRoman Volkov static int input_volume_apply(struct oxygen *chip, char left, char right) 186cf218b2eSRoman Volkov { 187cf218b2eSRoman Volkov struct dg *data = chip->model_data; 188cf218b2eSRoman Volkov int ret; 189cf218b2eSRoman Volkov 190cf218b2eSRoman Volkov data->cs4245_shadow[CS4245_PGA_A_CTRL] = left; 191cf218b2eSRoman Volkov data->cs4245_shadow[CS4245_PGA_B_CTRL] = right; 192cf218b2eSRoman Volkov ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL); 193cf218b2eSRoman Volkov if (ret < 0) 194cf218b2eSRoman Volkov return ret; 195cf218b2eSRoman Volkov return cs4245_write_spi(chip, CS4245_PGA_B_CTRL); 196cf218b2eSRoman Volkov } 197cf218b2eSRoman Volkov 198041f26b6SRoman Volkov static int input_vol_info(struct snd_kcontrol *ctl, 199041f26b6SRoman Volkov struct snd_ctl_elem_info *info) 200041f26b6SRoman Volkov { 201041f26b6SRoman Volkov info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 202041f26b6SRoman Volkov info->count = 2; 203041f26b6SRoman Volkov info->value.integer.min = 2 * -12; 204041f26b6SRoman Volkov info->value.integer.max = 2 * 12; 205041f26b6SRoman Volkov return 0; 206041f26b6SRoman Volkov } 207041f26b6SRoman Volkov 208041f26b6SRoman Volkov static int input_vol_get(struct snd_kcontrol *ctl, 209041f26b6SRoman Volkov struct snd_ctl_elem_value *value) 210041f26b6SRoman Volkov { 211041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 212041f26b6SRoman Volkov struct dg *data = chip->model_data; 213041f26b6SRoman Volkov unsigned int idx = ctl->private_value; 214041f26b6SRoman Volkov 215041f26b6SRoman Volkov mutex_lock(&chip->mutex); 216041f26b6SRoman Volkov value->value.integer.value[0] = data->input_vol[idx][0]; 217041f26b6SRoman Volkov value->value.integer.value[1] = data->input_vol[idx][1]; 218041f26b6SRoman Volkov mutex_unlock(&chip->mutex); 219041f26b6SRoman Volkov return 0; 220041f26b6SRoman Volkov } 221041f26b6SRoman Volkov 222041f26b6SRoman Volkov static int input_vol_put(struct snd_kcontrol *ctl, 223041f26b6SRoman Volkov struct snd_ctl_elem_value *value) 224041f26b6SRoman Volkov { 225041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 226041f26b6SRoman Volkov struct dg *data = chip->model_data; 227041f26b6SRoman Volkov unsigned int idx = ctl->private_value; 228041f26b6SRoman Volkov int changed = 0; 229cf218b2eSRoman Volkov int ret = 0; 230041f26b6SRoman Volkov 231041f26b6SRoman Volkov if (value->value.integer.value[0] < 2 * -12 || 232041f26b6SRoman Volkov value->value.integer.value[0] > 2 * 12 || 233041f26b6SRoman Volkov value->value.integer.value[1] < 2 * -12 || 234041f26b6SRoman Volkov value->value.integer.value[1] > 2 * 12) 235041f26b6SRoman Volkov return -EINVAL; 236041f26b6SRoman Volkov mutex_lock(&chip->mutex); 237041f26b6SRoman Volkov changed = data->input_vol[idx][0] != value->value.integer.value[0] || 238041f26b6SRoman Volkov data->input_vol[idx][1] != value->value.integer.value[1]; 239041f26b6SRoman Volkov if (changed) { 240041f26b6SRoman Volkov data->input_vol[idx][0] = value->value.integer.value[0]; 241041f26b6SRoman Volkov data->input_vol[idx][1] = value->value.integer.value[1]; 242041f26b6SRoman Volkov if (idx == data->input_sel) { 243cf218b2eSRoman Volkov ret = input_volume_apply(chip, 244cf218b2eSRoman Volkov data->input_vol[idx][0], 245041f26b6SRoman Volkov data->input_vol[idx][1]); 246041f26b6SRoman Volkov } 247cf218b2eSRoman Volkov changed = ret >= 0 ? 1 : ret; 248041f26b6SRoman Volkov } 249041f26b6SRoman Volkov mutex_unlock(&chip->mutex); 250041f26b6SRoman Volkov return changed; 251041f26b6SRoman Volkov } 252041f26b6SRoman Volkov 25370e0d82dSRoman Volkov /* Capture Source */ 25470e0d82dSRoman Volkov 25570e0d82dSRoman Volkov static int input_source_apply(struct oxygen *chip) 25670e0d82dSRoman Volkov { 25770e0d82dSRoman Volkov struct dg *data = chip->model_data; 25870e0d82dSRoman Volkov 25970e0d82dSRoman Volkov data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK; 26070e0d82dSRoman Volkov if (data->input_sel == CAPTURE_SRC_FP_MIC) 26170e0d82dSRoman Volkov data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2; 26270e0d82dSRoman Volkov else if (data->input_sel == CAPTURE_SRC_LINE) 26370e0d82dSRoman Volkov data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4; 26470e0d82dSRoman Volkov else if (data->input_sel != CAPTURE_SRC_MIC) 26570e0d82dSRoman Volkov data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1; 26670e0d82dSRoman Volkov return cs4245_write_spi(chip, CS4245_ANALOG_IN); 26770e0d82dSRoman Volkov } 26870e0d82dSRoman Volkov 269041f26b6SRoman Volkov static int input_sel_info(struct snd_kcontrol *ctl, 270041f26b6SRoman Volkov struct snd_ctl_elem_info *info) 271041f26b6SRoman Volkov { 272041f26b6SRoman Volkov static const char *const names[4] = { 27370e0d82dSRoman Volkov "Mic", "Front Mic", "Line", "Aux" 274041f26b6SRoman Volkov }; 275041f26b6SRoman Volkov 276041f26b6SRoman Volkov return snd_ctl_enum_info(info, 1, 4, names); 277041f26b6SRoman Volkov } 278041f26b6SRoman Volkov 279041f26b6SRoman Volkov static int input_sel_get(struct snd_kcontrol *ctl, 280041f26b6SRoman Volkov struct snd_ctl_elem_value *value) 281041f26b6SRoman Volkov { 282041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 283041f26b6SRoman Volkov struct dg *data = chip->model_data; 284041f26b6SRoman Volkov 285041f26b6SRoman Volkov mutex_lock(&chip->mutex); 286041f26b6SRoman Volkov value->value.enumerated.item[0] = data->input_sel; 287041f26b6SRoman Volkov mutex_unlock(&chip->mutex); 288041f26b6SRoman Volkov return 0; 289041f26b6SRoman Volkov } 290041f26b6SRoman Volkov 291041f26b6SRoman Volkov static int input_sel_put(struct snd_kcontrol *ctl, 292041f26b6SRoman Volkov struct snd_ctl_elem_value *value) 293041f26b6SRoman Volkov { 294041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 295041f26b6SRoman Volkov struct dg *data = chip->model_data; 296041f26b6SRoman Volkov int changed; 29770e0d82dSRoman Volkov int ret; 298041f26b6SRoman Volkov 299041f26b6SRoman Volkov if (value->value.enumerated.item[0] > 3) 300041f26b6SRoman Volkov return -EINVAL; 301041f26b6SRoman Volkov 302041f26b6SRoman Volkov mutex_lock(&chip->mutex); 303041f26b6SRoman Volkov changed = value->value.enumerated.item[0] != data->input_sel; 304041f26b6SRoman Volkov if (changed) { 305041f26b6SRoman Volkov data->input_sel = value->value.enumerated.item[0]; 306041f26b6SRoman Volkov 30770e0d82dSRoman Volkov ret = input_source_apply(chip); 30870e0d82dSRoman Volkov if (ret >= 0) 30970e0d82dSRoman Volkov ret = input_volume_apply(chip, 31070e0d82dSRoman Volkov data->input_vol[data->input_sel][0], 311041f26b6SRoman Volkov data->input_vol[data->input_sel][1]); 31270e0d82dSRoman Volkov changed = ret >= 0 ? 1 : ret; 313041f26b6SRoman Volkov } 314041f26b6SRoman Volkov mutex_unlock(&chip->mutex); 315041f26b6SRoman Volkov return changed; 316041f26b6SRoman Volkov } 317041f26b6SRoman Volkov 318fc114e9fSRoman Volkov /* ADC high-pass filter */ 319fc114e9fSRoman Volkov 320041f26b6SRoman Volkov static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) 321041f26b6SRoman Volkov { 322041f26b6SRoman Volkov static const char *const names[2] = { "Active", "Frozen" }; 323041f26b6SRoman Volkov 324041f26b6SRoman Volkov return snd_ctl_enum_info(info, 1, 2, names); 325041f26b6SRoman Volkov } 326041f26b6SRoman Volkov 327041f26b6SRoman Volkov static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 328041f26b6SRoman Volkov { 329041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 330041f26b6SRoman Volkov struct dg *data = chip->model_data; 331041f26b6SRoman Volkov 332041f26b6SRoman Volkov value->value.enumerated.item[0] = 333041f26b6SRoman Volkov !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); 334041f26b6SRoman Volkov return 0; 335041f26b6SRoman Volkov } 336041f26b6SRoman Volkov 337041f26b6SRoman Volkov static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 338041f26b6SRoman Volkov { 339041f26b6SRoman Volkov struct oxygen *chip = ctl->private_data; 340041f26b6SRoman Volkov struct dg *data = chip->model_data; 341041f26b6SRoman Volkov u8 reg; 342041f26b6SRoman Volkov int changed; 343041f26b6SRoman Volkov 344041f26b6SRoman Volkov mutex_lock(&chip->mutex); 345041f26b6SRoman Volkov reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; 346041f26b6SRoman Volkov if (value->value.enumerated.item[0]) 347041f26b6SRoman Volkov reg |= CS4245_HPF_FREEZE; 348041f26b6SRoman Volkov changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; 349fc114e9fSRoman Volkov if (changed) { 350fc114e9fSRoman Volkov data->cs4245_shadow[CS4245_ADC_CTRL] = reg; 351fc114e9fSRoman Volkov cs4245_write_spi(chip, CS4245_ADC_CTRL); 352fc114e9fSRoman Volkov } 353041f26b6SRoman Volkov mutex_unlock(&chip->mutex); 354041f26b6SRoman Volkov return changed; 355041f26b6SRoman Volkov } 356041f26b6SRoman Volkov 357041f26b6SRoman Volkov #define INPUT_VOLUME(xname, index) { \ 358041f26b6SRoman Volkov .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 359041f26b6SRoman Volkov .name = xname, \ 360cf218b2eSRoman Volkov .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 361cf218b2eSRoman Volkov SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 362041f26b6SRoman Volkov .info = input_vol_info, \ 363041f26b6SRoman Volkov .get = input_vol_get, \ 364041f26b6SRoman Volkov .put = input_vol_put, \ 365cf218b2eSRoman Volkov .tlv = { .p = pga_db_scale }, \ 366041f26b6SRoman Volkov .private_value = index, \ 367041f26b6SRoman Volkov } 368c754639aSRoman Volkov static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0); 369cf218b2eSRoman Volkov static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200); 370041f26b6SRoman Volkov static const struct snd_kcontrol_new dg_controls[] = { 371041f26b6SRoman Volkov { 372041f26b6SRoman Volkov .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 373041f26b6SRoman Volkov .name = "Analog Output Playback Enum", 3742809cb84SRoman Volkov .info = output_select_info, 3752809cb84SRoman Volkov .get = output_select_get, 3762809cb84SRoman Volkov .put = output_select_put, 377041f26b6SRoman Volkov }, 378041f26b6SRoman Volkov { 379041f26b6SRoman Volkov .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 380c754639aSRoman Volkov .name = "Headphone Playback Volume", 381c754639aSRoman Volkov .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 382c754639aSRoman Volkov SNDRV_CTL_ELEM_ACCESS_TLV_READ, 383c754639aSRoman Volkov .info = hp_stereo_volume_info, 384c754639aSRoman Volkov .get = hp_stereo_volume_get, 385c754639aSRoman Volkov .put = hp_stereo_volume_put, 386c754639aSRoman Volkov .tlv = { .p = hp_db_scale, }, 387c754639aSRoman Volkov }, 388c754639aSRoman Volkov { 389c754639aSRoman Volkov .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 390c754639aSRoman Volkov .name = "Headphone Playback Switch", 391c754639aSRoman Volkov .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 392c754639aSRoman Volkov .info = snd_ctl_boolean_mono_info, 393c754639aSRoman Volkov .get = hp_mute_get, 394c754639aSRoman Volkov .put = hp_mute_put, 395041f26b6SRoman Volkov }, 39670e0d82dSRoman Volkov INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC), 39770e0d82dSRoman Volkov INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC), 39870e0d82dSRoman Volkov INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE), 39970e0d82dSRoman Volkov INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX), 400041f26b6SRoman Volkov { 401041f26b6SRoman Volkov .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 402041f26b6SRoman Volkov .name = "Capture Source", 403041f26b6SRoman Volkov .info = input_sel_info, 404041f26b6SRoman Volkov .get = input_sel_get, 405041f26b6SRoman Volkov .put = input_sel_put, 406041f26b6SRoman Volkov }, 407041f26b6SRoman Volkov { 408041f26b6SRoman Volkov .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 409041f26b6SRoman Volkov .name = "ADC High-pass Filter Capture Enum", 410041f26b6SRoman Volkov .info = hpf_info, 411041f26b6SRoman Volkov .get = hpf_get, 412041f26b6SRoman Volkov .put = hpf_put, 413041f26b6SRoman Volkov }, 414041f26b6SRoman Volkov }; 415041f26b6SRoman Volkov 416041f26b6SRoman Volkov static int dg_control_filter(struct snd_kcontrol_new *template) 417041f26b6SRoman Volkov { 418041f26b6SRoman Volkov if (!strncmp(template->name, "Master Playback ", 16)) 419041f26b6SRoman Volkov return 1; 420041f26b6SRoman Volkov return 0; 421041f26b6SRoman Volkov } 422041f26b6SRoman Volkov 423041f26b6SRoman Volkov static int dg_mixer_init(struct oxygen *chip) 424041f26b6SRoman Volkov { 425041f26b6SRoman Volkov unsigned int i; 426041f26b6SRoman Volkov int err; 427041f26b6SRoman Volkov 4283f49a66fSRoman Volkov output_select_apply(chip); 4293f49a66fSRoman Volkov input_source_apply(chip); 4303f49a66fSRoman Volkov oxygen_update_dac_routing(chip); 4313f49a66fSRoman Volkov 432041f26b6SRoman Volkov for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { 433041f26b6SRoman Volkov err = snd_ctl_add(chip->card, 434041f26b6SRoman Volkov snd_ctl_new1(&dg_controls[i], chip)); 435041f26b6SRoman Volkov if (err < 0) 436041f26b6SRoman Volkov return err; 437041f26b6SRoman Volkov } 4383f49a66fSRoman Volkov 439041f26b6SRoman Volkov return 0; 440041f26b6SRoman Volkov } 441041f26b6SRoman Volkov 442621c51beSBhumika Goyal const struct oxygen_model model_xonar_dg = { 443041f26b6SRoman Volkov .longname = "C-Media Oxygen HD Audio", 444041f26b6SRoman Volkov .chip = "CMI8786", 445041f26b6SRoman Volkov .init = dg_init, 446041f26b6SRoman Volkov .control_filter = dg_control_filter, 447041f26b6SRoman Volkov .mixer_init = dg_mixer_init, 448041f26b6SRoman Volkov .cleanup = dg_cleanup, 449041f26b6SRoman Volkov .suspend = dg_suspend, 450041f26b6SRoman Volkov .resume = dg_resume, 451041f26b6SRoman Volkov .set_dac_params = set_cs4245_dac_params, 452041f26b6SRoman Volkov .set_adc_params = set_cs4245_adc_params, 453041f26b6SRoman Volkov .adjust_dac_routing = adjust_dg_dac_routing, 454041f26b6SRoman Volkov .dump_registers = dump_cs4245_registers, 455041f26b6SRoman Volkov .model_data_size = sizeof(struct dg), 456041f26b6SRoman Volkov .device_config = PLAYBACK_0_TO_I2S | 457041f26b6SRoman Volkov PLAYBACK_1_TO_SPDIF | 4583dd77654SRoman Volkov CAPTURE_0_FROM_I2S_1 | 459041f26b6SRoman Volkov CAPTURE_1_FROM_SPDIF, 460041f26b6SRoman Volkov .dac_channels_pcm = 6, 461041f26b6SRoman Volkov .dac_channels_mixer = 0, 462041f26b6SRoman Volkov .function_flags = OXYGEN_FUNCTION_SPI, 463041f26b6SRoman Volkov .dac_mclks = OXYGEN_MCLKS(256, 128, 128), 464041f26b6SRoman Volkov .adc_mclks = OXYGEN_MCLKS(256, 128, 128), 465041f26b6SRoman Volkov .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 466041f26b6SRoman Volkov .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 467041f26b6SRoman Volkov }; 468