root/sound/pci/oxygen/xonar_dg_mixer.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. output_select_apply
  2. output_select_info
  3. output_select_get
  4. output_select_put
  5. hp_stereo_volume_info
  6. hp_stereo_volume_get
  7. hp_stereo_volume_put
  8. hp_mute_get
  9. hp_mute_put
  10. input_volume_apply
  11. input_vol_info
  12. input_vol_get
  13. input_vol_put
  14. input_source_apply
  15. input_sel_info
  16. input_sel_get
  17. input_sel_put
  18. hpf_info
  19. hpf_get
  20. hpf_put
  21. dg_control_filter
  22. dg_mixer_init

   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 
  22 static 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 
  47 static 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 
  59 static 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 
  71 static 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 
  94 static 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 
 104 static 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 
 120 static 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 
 150 static 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 
 163 static 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 
 185 static 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 
 198 static 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 
 208 static 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 
 222 static 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 
 255 static 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 
 269 static 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 
 279 static 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 
 291 static 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 
 320 static 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 
 327 static 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 
 337 static 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 }
 368 static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
 369 static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200);
 370 static 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 
 416 static 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 
 423 static 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 
 442 const 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 };

/* [<][>][^][v][top][bottom][index][help] */