linux/sound/pci/oxygen/xonar_dg_mixer.c
<<
>>
Prefs
   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};
 468