linux/sound/pci/oxygen/xonar_cs43xx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * card driver for models with CS4398/CS4362A DACs (Xonar D1/DX)
   4 *
   5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
   6 */
   7
   8/*
   9 * Xonar D1/DX
  10 * -----------
  11 *
  12 * CMI8788:
  13 *
  14 *   I²C <-> CS4398 (addr 1001111) (front)
  15 *       <-> CS4362A (addr 0011000) (surround, center/LFE, back)
  16 *
  17 *   GPI 0 <- external power present (DX only)
  18 *
  19 *   GPIO 0 -> enable output to speakers
  20 *   GPIO 1 -> route output to front panel
  21 *   GPIO 2 -> M0 of CS5361
  22 *   GPIO 3 -> M1 of CS5361
  23 *   GPIO 6 -> ?
  24 *   GPIO 7 -> ?
  25 *   GPIO 8 -> route input jack to line-in (0) or mic-in (1)
  26 *
  27 * CM9780:
  28 *
  29 *   LINE_OUT -> input of ADC
  30 *
  31 *   AUX_IN  <- aux
  32 *   MIC_IN  <- mic
  33 *   FMIC_IN <- front mic
  34 *
  35 *   GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
  36 */
  37
  38#include <linux/pci.h>
  39#include <linux/delay.h>
  40#include <sound/ac97_codec.h>
  41#include <sound/control.h>
  42#include <sound/core.h>
  43#include <sound/pcm.h>
  44#include <sound/pcm_params.h>
  45#include <sound/tlv.h>
  46#include "xonar.h"
  47#include "cm9780.h"
  48#include "cs4398.h"
  49#include "cs4362a.h"
  50
  51#define GPI_EXT_POWER           0x01
  52#define GPIO_D1_OUTPUT_ENABLE   0x0001
  53#define GPIO_D1_FRONT_PANEL     0x0002
  54#define GPIO_D1_MAGIC           0x00c0
  55#define GPIO_D1_INPUT_ROUTE     0x0100
  56
  57#define I2C_DEVICE_CS4398       0x9e    /* 10011, AD1=1, AD0=1, /W=0 */
  58#define I2C_DEVICE_CS4362A      0x30    /* 001100, AD0=0, /W=0 */
  59
  60struct xonar_cs43xx {
  61        struct xonar_generic generic;
  62        u8 cs4398_regs[8];
  63        u8 cs4362a_regs[15];
  64};
  65
  66static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
  67{
  68        struct xonar_cs43xx *data = chip->model_data;
  69
  70        oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
  71        if (reg < ARRAY_SIZE(data->cs4398_regs))
  72                data->cs4398_regs[reg] = value;
  73}
  74
  75static void cs4398_write_cached(struct oxygen *chip, u8 reg, u8 value)
  76{
  77        struct xonar_cs43xx *data = chip->model_data;
  78
  79        if (value != data->cs4398_regs[reg])
  80                cs4398_write(chip, reg, value);
  81}
  82
  83static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
  84{
  85        struct xonar_cs43xx *data = chip->model_data;
  86
  87        oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
  88        if (reg < ARRAY_SIZE(data->cs4362a_regs))
  89                data->cs4362a_regs[reg] = value;
  90}
  91
  92static void cs4362a_write_cached(struct oxygen *chip, u8 reg, u8 value)
  93{
  94        struct xonar_cs43xx *data = chip->model_data;
  95
  96        if (value != data->cs4362a_regs[reg])
  97                cs4362a_write(chip, reg, value);
  98}
  99
 100static void cs43xx_registers_init(struct oxygen *chip)
 101{
 102        struct xonar_cs43xx *data = chip->model_data;
 103        unsigned int i;
 104
 105        /* set CPEN (control port mode) and power down */
 106        cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
 107        cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
 108        /* configure */
 109        cs4398_write(chip, 2, data->cs4398_regs[2]);
 110        cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
 111        cs4398_write(chip, 4, data->cs4398_regs[4]);
 112        cs4398_write(chip, 5, data->cs4398_regs[5]);
 113        cs4398_write(chip, 6, data->cs4398_regs[6]);
 114        cs4398_write(chip, 7, data->cs4398_regs[7]);
 115        cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
 116        cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
 117                      CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
 118        cs4362a_write(chip, 0x04, data->cs4362a_regs[0x04]);
 119        cs4362a_write(chip, 0x05, 0);
 120        for (i = 6; i <= 14; ++i)
 121                cs4362a_write(chip, i, data->cs4362a_regs[i]);
 122        /* clear power down */
 123        cs4398_write(chip, 8, CS4398_CPEN);
 124        cs4362a_write(chip, 0x01, CS4362A_CPEN);
 125}
 126
 127static void xonar_d1_init(struct oxygen *chip)
 128{
 129        struct xonar_cs43xx *data = chip->model_data;
 130
 131        data->generic.anti_pop_delay = 800;
 132        data->generic.output_enable_bit = GPIO_D1_OUTPUT_ENABLE;
 133        data->cs4398_regs[2] =
 134                CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
 135        data->cs4398_regs[4] = CS4398_MUTEP_LOW |
 136                CS4398_MUTE_B | CS4398_MUTE_A | CS4398_PAMUTE;
 137        data->cs4398_regs[5] = 60 * 2;
 138        data->cs4398_regs[6] = 60 * 2;
 139        data->cs4398_regs[7] = CS4398_RMP_DN | CS4398_RMP_UP |
 140                CS4398_ZERO_CROSS | CS4398_SOFT_RAMP;
 141        data->cs4362a_regs[4] = CS4362A_RMP_DN | CS4362A_DEM_NONE;
 142        data->cs4362a_regs[6] = CS4362A_FM_SINGLE |
 143                CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
 144        data->cs4362a_regs[7] = 60 | CS4362A_MUTE;
 145        data->cs4362a_regs[8] = 60 | CS4362A_MUTE;
 146        data->cs4362a_regs[9] = data->cs4362a_regs[6];
 147        data->cs4362a_regs[10] = 60 | CS4362A_MUTE;
 148        data->cs4362a_regs[11] = 60 | CS4362A_MUTE;
 149        data->cs4362a_regs[12] = data->cs4362a_regs[6];
 150        data->cs4362a_regs[13] = 60 | CS4362A_MUTE;
 151        data->cs4362a_regs[14] = 60 | CS4362A_MUTE;
 152
 153        oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
 154                       OXYGEN_2WIRE_LENGTH_8 |
 155                       OXYGEN_2WIRE_INTERRUPT_MASK |
 156                       OXYGEN_2WIRE_SPEED_FAST);
 157
 158        cs43xx_registers_init(chip);
 159
 160        oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
 161                          GPIO_D1_FRONT_PANEL |
 162                          GPIO_D1_MAGIC |
 163                          GPIO_D1_INPUT_ROUTE);
 164        oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
 165                            GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
 166
 167        xonar_init_cs53x1(chip);
 168        xonar_enable_output(chip);
 169
 170        snd_component_add(chip->card, "CS4398");
 171        snd_component_add(chip->card, "CS4362A");
 172        snd_component_add(chip->card, "CS5361");
 173}
 174
 175static void xonar_dx_init(struct oxygen *chip)
 176{
 177        struct xonar_cs43xx *data = chip->model_data;
 178
 179        data->generic.ext_power_reg = OXYGEN_GPI_DATA;
 180        data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
 181        data->generic.ext_power_bit = GPI_EXT_POWER;
 182        xonar_init_ext_power(chip);
 183        xonar_d1_init(chip);
 184}
 185
 186static void xonar_d1_cleanup(struct oxygen *chip)
 187{
 188        xonar_disable_output(chip);
 189        cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
 190        oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
 191}
 192
 193static void xonar_d1_suspend(struct oxygen *chip)
 194{
 195        xonar_d1_cleanup(chip);
 196}
 197
 198static void xonar_d1_resume(struct oxygen *chip)
 199{
 200        oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
 201        msleep(1);
 202        cs43xx_registers_init(chip);
 203        xonar_enable_output(chip);
 204}
 205
 206static void set_cs43xx_params(struct oxygen *chip,
 207                              struct snd_pcm_hw_params *params)
 208{
 209        struct xonar_cs43xx *data = chip->model_data;
 210        u8 cs4398_fm, cs4362a_fm;
 211
 212        if (params_rate(params) <= 50000) {
 213                cs4398_fm = CS4398_FM_SINGLE;
 214                cs4362a_fm = CS4362A_FM_SINGLE;
 215        } else if (params_rate(params) <= 100000) {
 216                cs4398_fm = CS4398_FM_DOUBLE;
 217                cs4362a_fm = CS4362A_FM_DOUBLE;
 218        } else {
 219                cs4398_fm = CS4398_FM_QUAD;
 220                cs4362a_fm = CS4362A_FM_QUAD;
 221        }
 222        cs4398_fm |= CS4398_DEM_NONE | CS4398_DIF_LJUST;
 223        cs4398_write_cached(chip, 2, cs4398_fm);
 224        cs4362a_fm |= data->cs4362a_regs[6] & ~CS4362A_FM_MASK;
 225        cs4362a_write_cached(chip, 6, cs4362a_fm);
 226        cs4362a_write_cached(chip, 12, cs4362a_fm);
 227        cs4362a_fm &= CS4362A_FM_MASK;
 228        cs4362a_fm |= data->cs4362a_regs[9] & ~CS4362A_FM_MASK;
 229        cs4362a_write_cached(chip, 9, cs4362a_fm);
 230}
 231
 232static void update_cs4362a_volumes(struct oxygen *chip)
 233{
 234        unsigned int i;
 235        u8 mute;
 236
 237        mute = chip->dac_mute ? CS4362A_MUTE : 0;
 238        for (i = 0; i < 6; ++i)
 239                cs4362a_write_cached(chip, 7 + i + i / 2,
 240                                     (127 - chip->dac_volume[2 + i]) | mute);
 241}
 242
 243static void update_cs43xx_volume(struct oxygen *chip)
 244{
 245        cs4398_write_cached(chip, 5, (127 - chip->dac_volume[0]) * 2);
 246        cs4398_write_cached(chip, 6, (127 - chip->dac_volume[1]) * 2);
 247        update_cs4362a_volumes(chip);
 248}
 249
 250static void update_cs43xx_mute(struct oxygen *chip)
 251{
 252        u8 reg;
 253
 254        reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
 255        if (chip->dac_mute)
 256                reg |= CS4398_MUTE_B | CS4398_MUTE_A;
 257        cs4398_write_cached(chip, 4, reg);
 258        update_cs4362a_volumes(chip);
 259}
 260
 261static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)
 262{
 263        struct xonar_cs43xx *data = chip->model_data;
 264        u8 reg;
 265
 266        reg = data->cs4362a_regs[9] & ~CS4362A_ATAPI_MASK;
 267        if (mixed)
 268                reg |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR;
 269        else
 270                reg |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
 271        cs4362a_write_cached(chip, 9, reg);
 272}
 273
 274static const struct snd_kcontrol_new front_panel_switch = {
 275        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 276        .name = "Front Panel Playback Switch",
 277        .info = snd_ctl_boolean_mono_info,
 278        .get = xonar_gpio_bit_switch_get,
 279        .put = xonar_gpio_bit_switch_put,
 280        .private_value = GPIO_D1_FRONT_PANEL,
 281};
 282
 283static int rolloff_info(struct snd_kcontrol *ctl,
 284                        struct snd_ctl_elem_info *info)
 285{
 286        static const char *const names[2] = {
 287                "Fast Roll-off", "Slow Roll-off"
 288        };
 289
 290        return snd_ctl_enum_info(info, 1, 2, names);
 291}
 292
 293static int rolloff_get(struct snd_kcontrol *ctl,
 294                       struct snd_ctl_elem_value *value)
 295{
 296        struct oxygen *chip = ctl->private_data;
 297        struct xonar_cs43xx *data = chip->model_data;
 298
 299        value->value.enumerated.item[0] =
 300                (data->cs4398_regs[7] & CS4398_FILT_SEL) != 0;
 301        return 0;
 302}
 303
 304static int rolloff_put(struct snd_kcontrol *ctl,
 305                       struct snd_ctl_elem_value *value)
 306{
 307        struct oxygen *chip = ctl->private_data;
 308        struct xonar_cs43xx *data = chip->model_data;
 309        int changed;
 310        u8 reg;
 311
 312        mutex_lock(&chip->mutex);
 313        reg = data->cs4398_regs[7];
 314        if (value->value.enumerated.item[0])
 315                reg |= CS4398_FILT_SEL;
 316        else
 317                reg &= ~CS4398_FILT_SEL;
 318        changed = reg != data->cs4398_regs[7];
 319        if (changed) {
 320                cs4398_write(chip, 7, reg);
 321                if (reg & CS4398_FILT_SEL)
 322                        reg = data->cs4362a_regs[0x04] | CS4362A_FILT_SEL;
 323                else
 324                        reg = data->cs4362a_regs[0x04] & ~CS4362A_FILT_SEL;
 325                cs4362a_write(chip, 0x04, reg);
 326        }
 327        mutex_unlock(&chip->mutex);
 328        return changed;
 329}
 330
 331static const struct snd_kcontrol_new rolloff_control = {
 332        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 333        .name = "DAC Filter Playback Enum",
 334        .info = rolloff_info,
 335        .get = rolloff_get,
 336        .put = rolloff_put,
 337};
 338
 339static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip,
 340                                          unsigned int reg, unsigned int mute)
 341{
 342        if (reg == AC97_LINE) {
 343                spin_lock_irq(&chip->reg_lock);
 344                oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
 345                                      mute ? GPIO_D1_INPUT_ROUTE : 0,
 346                                      GPIO_D1_INPUT_ROUTE);
 347                spin_unlock_irq(&chip->reg_lock);
 348        }
 349}
 350
 351static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
 352
 353static int xonar_d1_mixer_init(struct oxygen *chip)
 354{
 355        int err;
 356
 357        err = snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
 358        if (err < 0)
 359                return err;
 360        err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
 361        if (err < 0)
 362                return err;
 363        return 0;
 364}
 365
 366static void dump_cs4362a_registers(struct xonar_cs43xx *data,
 367                                   struct snd_info_buffer *buffer)
 368{
 369        unsigned int i;
 370
 371        snd_iprintf(buffer, "\nCS4362A:");
 372        for (i = 1; i <= 14; ++i)
 373                snd_iprintf(buffer, " %02x", data->cs4362a_regs[i]);
 374        snd_iprintf(buffer, "\n");
 375}
 376
 377static void dump_d1_registers(struct oxygen *chip,
 378                              struct snd_info_buffer *buffer)
 379{
 380        struct xonar_cs43xx *data = chip->model_data;
 381        unsigned int i;
 382
 383        snd_iprintf(buffer, "\nCS4398: 7?");
 384        for (i = 2; i < 8; ++i)
 385                snd_iprintf(buffer, " %02x", data->cs4398_regs[i]);
 386        snd_iprintf(buffer, "\n");
 387        dump_cs4362a_registers(data, buffer);
 388}
 389
 390static const struct oxygen_model model_xonar_d1 = {
 391        .longname = "Asus Virtuoso 100",
 392        .chip = "AV200",
 393        .init = xonar_d1_init,
 394        .mixer_init = xonar_d1_mixer_init,
 395        .cleanup = xonar_d1_cleanup,
 396        .suspend = xonar_d1_suspend,
 397        .resume = xonar_d1_resume,
 398        .set_dac_params = set_cs43xx_params,
 399        .set_adc_params = xonar_set_cs53x1_params,
 400        .update_dac_volume = update_cs43xx_volume,
 401        .update_dac_mute = update_cs43xx_mute,
 402        .update_center_lfe_mix = update_cs43xx_center_lfe_mix,
 403        .ac97_switch = xonar_d1_line_mic_ac97_switch,
 404        .dump_registers = dump_d1_registers,
 405        .dac_tlv = cs4362a_db_scale,
 406        .model_data_size = sizeof(struct xonar_cs43xx),
 407        .device_config = PLAYBACK_0_TO_I2S |
 408                         PLAYBACK_1_TO_SPDIF |
 409                         CAPTURE_0_FROM_I2S_2 |
 410                         CAPTURE_1_FROM_SPDIF |
 411                         AC97_FMIC_SWITCH,
 412        .dac_channels_pcm = 8,
 413        .dac_channels_mixer = 8,
 414        .dac_volume_min = 127 - 60,
 415        .dac_volume_max = 127,
 416        .function_flags = OXYGEN_FUNCTION_2WIRE,
 417        .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
 418        .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
 419        .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 420        .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
 421};
 422
 423int get_xonar_cs43xx_model(struct oxygen *chip,
 424                           const struct pci_device_id *id)
 425{
 426        switch (id->subdevice) {
 427        case 0x834f:
 428                chip->model = model_xonar_d1;
 429                chip->model.shortname = "Xonar D1";
 430                break;
 431        case 0x8275:
 432        case 0x8327:
 433                chip->model = model_xonar_d1;
 434                chip->model.shortname = "Xonar DX";
 435                chip->model.init = xonar_dx_init;
 436                break;
 437        default:
 438                return -EINVAL;
 439        }
 440        return 0;
 441}
 442