xonar_dg_mixer.c (12631B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Mixer controls for the Xonar DG/DGX 4 * 5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 6 * Copyright (c) Roman Volkov <v1ron@mail.ru> 7 */ 8 9#include <linux/pci.h> 10#include <linux/delay.h> 11#include <sound/control.h> 12#include <sound/core.h> 13#include <sound/info.h> 14#include <sound/pcm.h> 15#include <sound/tlv.h> 16#include "oxygen.h" 17#include "xonar_dg.h" 18#include "cs4245.h" 19 20/* analog output select */ 21 22static int output_select_apply(struct oxygen *chip) 23{ 24 struct dg *data = chip->model_data; 25 26 data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK; 27 if (data->output_sel == PLAYBACK_DST_HP) { 28 /* mute FP (aux output) amplifier, switch rear jack to CS4245 */ 29 oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); 30 } else if (data->output_sel == PLAYBACK_DST_HP_FP) { 31 /* 32 * Unmute FP amplifier, switch rear jack to CS4361; 33 * I2S channels 2,3,4 should be inactive. 34 */ 35 oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); 36 data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC; 37 } else { 38 /* 39 * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp., 40 * and change playback routing. 41 */ 42 oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR); 43 } 44 return cs4245_write_spi(chip, CS4245_SIGNAL_SEL); 45} 46 47static int output_select_info(struct snd_kcontrol *ctl, 48 struct snd_ctl_elem_info *info) 49{ 50 static const char *const names[3] = { 51 "Stereo Headphones", 52 "Stereo Headphones FP", 53 "Multichannel", 54 }; 55 56 return snd_ctl_enum_info(info, 1, 3, names); 57} 58 59static int output_select_get(struct snd_kcontrol *ctl, 60 struct snd_ctl_elem_value *value) 61{ 62 struct oxygen *chip = ctl->private_data; 63 struct dg *data = chip->model_data; 64 65 mutex_lock(&chip->mutex); 66 value->value.enumerated.item[0] = data->output_sel; 67 mutex_unlock(&chip->mutex); 68 return 0; 69} 70 71static int output_select_put(struct snd_kcontrol *ctl, 72 struct snd_ctl_elem_value *value) 73{ 74 struct oxygen *chip = ctl->private_data; 75 struct dg *data = chip->model_data; 76 unsigned int new = value->value.enumerated.item[0]; 77 int changed = 0; 78 int ret; 79 80 mutex_lock(&chip->mutex); 81 if (data->output_sel != new) { 82 data->output_sel = new; 83 ret = output_select_apply(chip); 84 changed = ret >= 0 ? 1 : ret; 85 oxygen_update_dac_routing(chip); 86 } 87 mutex_unlock(&chip->mutex); 88 89 return changed; 90} 91 92/* CS4245 Headphone Channels A&B Volume Control */ 93 94static int hp_stereo_volume_info(struct snd_kcontrol *ctl, 95 struct snd_ctl_elem_info *info) 96{ 97 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 98 info->count = 2; 99 info->value.integer.min = 0; 100 info->value.integer.max = 255; 101 return 0; 102} 103 104static int hp_stereo_volume_get(struct snd_kcontrol *ctl, 105 struct snd_ctl_elem_value *val) 106{ 107 struct oxygen *chip = ctl->private_data; 108 struct dg *data = chip->model_data; 109 unsigned int tmp; 110 111 mutex_lock(&chip->mutex); 112 tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255; 113 val->value.integer.value[0] = tmp; 114 tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255; 115 val->value.integer.value[1] = tmp; 116 mutex_unlock(&chip->mutex); 117 return 0; 118} 119 120static int hp_stereo_volume_put(struct snd_kcontrol *ctl, 121 struct snd_ctl_elem_value *val) 122{ 123 struct oxygen *chip = ctl->private_data; 124 struct dg *data = chip->model_data; 125 int ret; 126 int changed = 0; 127 long new1 = val->value.integer.value[0]; 128 long new2 = val->value.integer.value[1]; 129 130 if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0)) 131 return -EINVAL; 132 133 mutex_lock(&chip->mutex); 134 if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) || 135 (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) { 136 data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1; 137 data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2; 138 ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL); 139 if (ret >= 0) 140 ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL); 141 changed = ret >= 0 ? 1 : ret; 142 } 143 mutex_unlock(&chip->mutex); 144 145 return changed; 146} 147 148/* Headphone Mute */ 149 150static int hp_mute_get(struct snd_kcontrol *ctl, 151 struct snd_ctl_elem_value *val) 152{ 153 struct oxygen *chip = ctl->private_data; 154 struct dg *data = chip->model_data; 155 156 mutex_lock(&chip->mutex); 157 val->value.integer.value[0] = 158 !(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC); 159 mutex_unlock(&chip->mutex); 160 return 0; 161} 162 163static int hp_mute_put(struct snd_kcontrol *ctl, 164 struct snd_ctl_elem_value *val) 165{ 166 struct oxygen *chip = ctl->private_data; 167 struct dg *data = chip->model_data; 168 int ret; 169 int changed; 170 171 if (val->value.integer.value[0] > 1) 172 return -EINVAL; 173 mutex_lock(&chip->mutex); 174 data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC; 175 data->cs4245_shadow[CS4245_DAC_CTRL_1] |= 176 (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC; 177 ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1); 178 changed = ret >= 0 ? 1 : ret; 179 mutex_unlock(&chip->mutex); 180 return changed; 181} 182 183/* capture volume for all sources */ 184 185static int input_volume_apply(struct oxygen *chip, char left, char right) 186{ 187 struct dg *data = chip->model_data; 188 int ret; 189 190 data->cs4245_shadow[CS4245_PGA_A_CTRL] = left; 191 data->cs4245_shadow[CS4245_PGA_B_CTRL] = right; 192 ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL); 193 if (ret < 0) 194 return ret; 195 return cs4245_write_spi(chip, CS4245_PGA_B_CTRL); 196} 197 198static int input_vol_info(struct snd_kcontrol *ctl, 199 struct snd_ctl_elem_info *info) 200{ 201 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 202 info->count = 2; 203 info->value.integer.min = 2 * -12; 204 info->value.integer.max = 2 * 12; 205 return 0; 206} 207 208static int input_vol_get(struct snd_kcontrol *ctl, 209 struct snd_ctl_elem_value *value) 210{ 211 struct oxygen *chip = ctl->private_data; 212 struct dg *data = chip->model_data; 213 unsigned int idx = ctl->private_value; 214 215 mutex_lock(&chip->mutex); 216 value->value.integer.value[0] = data->input_vol[idx][0]; 217 value->value.integer.value[1] = data->input_vol[idx][1]; 218 mutex_unlock(&chip->mutex); 219 return 0; 220} 221 222static int input_vol_put(struct snd_kcontrol *ctl, 223 struct snd_ctl_elem_value *value) 224{ 225 struct oxygen *chip = ctl->private_data; 226 struct dg *data = chip->model_data; 227 unsigned int idx = ctl->private_value; 228 int changed = 0; 229 int ret = 0; 230 231 if (value->value.integer.value[0] < 2 * -12 || 232 value->value.integer.value[0] > 2 * 12 || 233 value->value.integer.value[1] < 2 * -12 || 234 value->value.integer.value[1] > 2 * 12) 235 return -EINVAL; 236 mutex_lock(&chip->mutex); 237 changed = data->input_vol[idx][0] != value->value.integer.value[0] || 238 data->input_vol[idx][1] != value->value.integer.value[1]; 239 if (changed) { 240 data->input_vol[idx][0] = value->value.integer.value[0]; 241 data->input_vol[idx][1] = value->value.integer.value[1]; 242 if (idx == data->input_sel) { 243 ret = input_volume_apply(chip, 244 data->input_vol[idx][0], 245 data->input_vol[idx][1]); 246 } 247 changed = ret >= 0 ? 1 : ret; 248 } 249 mutex_unlock(&chip->mutex); 250 return changed; 251} 252 253/* Capture Source */ 254 255static int input_source_apply(struct oxygen *chip) 256{ 257 struct dg *data = chip->model_data; 258 259 data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK; 260 if (data->input_sel == CAPTURE_SRC_FP_MIC) 261 data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2; 262 else if (data->input_sel == CAPTURE_SRC_LINE) 263 data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4; 264 else if (data->input_sel != CAPTURE_SRC_MIC) 265 data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1; 266 return cs4245_write_spi(chip, CS4245_ANALOG_IN); 267} 268 269static int input_sel_info(struct snd_kcontrol *ctl, 270 struct snd_ctl_elem_info *info) 271{ 272 static const char *const names[4] = { 273 "Mic", "Front Mic", "Line", "Aux" 274 }; 275 276 return snd_ctl_enum_info(info, 1, 4, names); 277} 278 279static int input_sel_get(struct snd_kcontrol *ctl, 280 struct snd_ctl_elem_value *value) 281{ 282 struct oxygen *chip = ctl->private_data; 283 struct dg *data = chip->model_data; 284 285 mutex_lock(&chip->mutex); 286 value->value.enumerated.item[0] = data->input_sel; 287 mutex_unlock(&chip->mutex); 288 return 0; 289} 290 291static int input_sel_put(struct snd_kcontrol *ctl, 292 struct snd_ctl_elem_value *value) 293{ 294 struct oxygen *chip = ctl->private_data; 295 struct dg *data = chip->model_data; 296 int changed; 297 int ret; 298 299 if (value->value.enumerated.item[0] > 3) 300 return -EINVAL; 301 302 mutex_lock(&chip->mutex); 303 changed = value->value.enumerated.item[0] != data->input_sel; 304 if (changed) { 305 data->input_sel = value->value.enumerated.item[0]; 306 307 ret = input_source_apply(chip); 308 if (ret >= 0) 309 ret = input_volume_apply(chip, 310 data->input_vol[data->input_sel][0], 311 data->input_vol[data->input_sel][1]); 312 changed = ret >= 0 ? 1 : ret; 313 } 314 mutex_unlock(&chip->mutex); 315 return changed; 316} 317 318/* ADC high-pass filter */ 319 320static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) 321{ 322 static const char *const names[2] = { "Active", "Frozen" }; 323 324 return snd_ctl_enum_info(info, 1, 2, names); 325} 326 327static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 328{ 329 struct oxygen *chip = ctl->private_data; 330 struct dg *data = chip->model_data; 331 332 value->value.enumerated.item[0] = 333 !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE); 334 return 0; 335} 336 337static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) 338{ 339 struct oxygen *chip = ctl->private_data; 340 struct dg *data = chip->model_data; 341 u8 reg; 342 int changed; 343 344 mutex_lock(&chip->mutex); 345 reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE; 346 if (value->value.enumerated.item[0]) 347 reg |= CS4245_HPF_FREEZE; 348 changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL]; 349 if (changed) { 350 data->cs4245_shadow[CS4245_ADC_CTRL] = reg; 351 cs4245_write_spi(chip, CS4245_ADC_CTRL); 352 } 353 mutex_unlock(&chip->mutex); 354 return changed; 355} 356 357#define INPUT_VOLUME(xname, index) { \ 358 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 359 .name = xname, \ 360 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ 361 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ 362 .info = input_vol_info, \ 363 .get = input_vol_get, \ 364 .put = input_vol_put, \ 365 .tlv = { .p = pga_db_scale }, \ 366 .private_value = index, \ 367} 368static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0); 369static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200); 370static const struct snd_kcontrol_new dg_controls[] = { 371 { 372 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 373 .name = "Analog Output Playback Enum", 374 .info = output_select_info, 375 .get = output_select_get, 376 .put = output_select_put, 377 }, 378 { 379 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 380 .name = "Headphone Playback Volume", 381 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 382 SNDRV_CTL_ELEM_ACCESS_TLV_READ, 383 .info = hp_stereo_volume_info, 384 .get = hp_stereo_volume_get, 385 .put = hp_stereo_volume_put, 386 .tlv = { .p = hp_db_scale, }, 387 }, 388 { 389 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 390 .name = "Headphone Playback Switch", 391 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 392 .info = snd_ctl_boolean_mono_info, 393 .get = hp_mute_get, 394 .put = hp_mute_put, 395 }, 396 INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC), 397 INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC), 398 INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE), 399 INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX), 400 { 401 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 402 .name = "Capture Source", 403 .info = input_sel_info, 404 .get = input_sel_get, 405 .put = input_sel_put, 406 }, 407 { 408 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 409 .name = "ADC High-pass Filter Capture Enum", 410 .info = hpf_info, 411 .get = hpf_get, 412 .put = hpf_put, 413 }, 414}; 415 416static int dg_control_filter(struct snd_kcontrol_new *template) 417{ 418 if (!strncmp(template->name, "Master Playback ", 16)) 419 return 1; 420 return 0; 421} 422 423static int dg_mixer_init(struct oxygen *chip) 424{ 425 unsigned int i; 426 int err; 427 428 output_select_apply(chip); 429 input_source_apply(chip); 430 oxygen_update_dac_routing(chip); 431 432 for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) { 433 err = snd_ctl_add(chip->card, 434 snd_ctl_new1(&dg_controls[i], chip)); 435 if (err < 0) 436 return err; 437 } 438 439 return 0; 440} 441 442const struct oxygen_model model_xonar_dg = { 443 .longname = "C-Media Oxygen HD Audio", 444 .chip = "CMI8786", 445 .init = dg_init, 446 .control_filter = dg_control_filter, 447 .mixer_init = dg_mixer_init, 448 .cleanup = dg_cleanup, 449 .suspend = dg_suspend, 450 .resume = dg_resume, 451 .set_dac_params = set_cs4245_dac_params, 452 .set_adc_params = set_cs4245_adc_params, 453 .adjust_dac_routing = adjust_dg_dac_routing, 454 .dump_registers = dump_cs4245_registers, 455 .model_data_size = sizeof(struct dg), 456 .device_config = PLAYBACK_0_TO_I2S | 457 PLAYBACK_1_TO_SPDIF | 458 CAPTURE_0_FROM_I2S_1 | 459 CAPTURE_1_FROM_SPDIF, 460 .dac_channels_pcm = 6, 461 .dac_channels_mixer = 0, 462 .function_flags = OXYGEN_FUNCTION_SPI, 463 .dac_mclks = OXYGEN_MCLKS(256, 128, 128), 464 .adc_mclks = OXYGEN_MCLKS(256, 128, 128), 465 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 466 .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, 467};