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