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