linux/sound/pci/oxygen/xonar_dg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * card driver 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/*
  10 * Xonar DG/DGX
  11 * ------------
  12 *
  13 * CS4245 and CS4361 both will mute all outputs if any clock ratio
  14 * is invalid.
  15 *
  16 * CMI8788:
  17 *
  18 *   SPI 0 -> CS4245
  19 *
  20 *   Playback:
  21 *   I²S 1 -> CS4245
  22 *   I²S 2 -> CS4361 (center/LFE)
  23 *   I²S 3 -> CS4361 (surround)
  24 *   I²S 4 -> CS4361 (front)
  25 *   Capture:
  26 *   I²S ADC 1 <- CS4245
  27 *
  28 *   GPIO 3 <- ?
  29 *   GPIO 4 <- headphone detect
  30 *   GPIO 5 -> enable ADC analog circuit for the left channel
  31 *   GPIO 6 -> enable ADC analog circuit for the right channel
  32 *   GPIO 7 -> switch green rear output jack between CS4245 and the first
  33 *             channel of CS4361 (mechanical relay)
  34 *   GPIO 8 -> enable output to speakers
  35 *
  36 * CS4245:
  37 *
  38 *   input 0 <- mic
  39 *   input 1 <- aux
  40 *   input 2 <- front mic
  41 *   input 4 <- line
  42 *   DAC out -> headphones
  43 *   aux out -> front panel headphones
  44 */
  45
  46#include <linux/pci.h>
  47#include <linux/delay.h>
  48#include <sound/control.h>
  49#include <sound/core.h>
  50#include <sound/info.h>
  51#include <sound/pcm.h>
  52#include <sound/tlv.h>
  53#include "oxygen.h"
  54#include "xonar_dg.h"
  55#include "cs4245.h"
  56
  57int cs4245_write_spi(struct oxygen *chip, u8 reg)
  58{
  59        struct dg *data = chip->model_data;
  60        unsigned int packet;
  61
  62        packet = reg << 8;
  63        packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
  64        packet |= data->cs4245_shadow[reg];
  65
  66        return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
  67                                OXYGEN_SPI_DATA_LENGTH_3 |
  68                                OXYGEN_SPI_CLOCK_1280 |
  69                                (0 << OXYGEN_SPI_CODEC_SHIFT) |
  70                                OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
  71                                packet);
  72}
  73
  74int cs4245_read_spi(struct oxygen *chip, u8 addr)
  75{
  76        struct dg *data = chip->model_data;
  77        int ret;
  78
  79        ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
  80                OXYGEN_SPI_DATA_LENGTH_2 |
  81                OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
  82                OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
  83                ((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);
  84        if (ret < 0)
  85                return ret;
  86
  87        ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
  88                OXYGEN_SPI_DATA_LENGTH_2 |
  89                OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
  90                OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
  91                (CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);
  92        if (ret < 0)
  93                return ret;
  94
  95        data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);
  96
  97        return 0;
  98}
  99
 100int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
 101{
 102        struct dg *data = chip->model_data;
 103        unsigned char addr;
 104        int ret;
 105
 106        for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
 107                ret = (op == CS4245_SAVE_TO_SHADOW ?
 108                        cs4245_read_spi(chip, addr) :
 109                        cs4245_write_spi(chip, addr));
 110                if (ret < 0)
 111                        return ret;
 112        }
 113        return 0;
 114}
 115
 116static void cs4245_init(struct oxygen *chip)
 117{
 118        struct dg *data = chip->model_data;
 119
 120        /* save the initial state: codec version, registers */
 121        cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
 122
 123        /*
 124         * Power up the CODEC internals, enable soft ramp & zero cross, work in
 125         * async. mode, enable aux output from DAC. Invert DAC output as in the
 126         * Windows driver.
 127         */
 128        data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
 129        data->cs4245_shadow[CS4245_SIGNAL_SEL] =
 130                CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
 131        data->cs4245_shadow[CS4245_DAC_CTRL_1] =
 132                CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
 133        data->cs4245_shadow[CS4245_DAC_CTRL_2] =
 134                CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
 135        data->cs4245_shadow[CS4245_ADC_CTRL] =
 136                CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
 137        data->cs4245_shadow[CS4245_ANALOG_IN] =
 138                CS4245_PGA_SOFT | CS4245_PGA_ZERO;
 139        data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
 140        data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
 141        data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
 142        data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
 143
 144        cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
 145        snd_component_add(chip->card, "CS4245");
 146}
 147
 148void dg_init(struct oxygen *chip)
 149{
 150        struct dg *data = chip->model_data;
 151
 152        data->output_sel = PLAYBACK_DST_HP_FP;
 153        data->input_sel = CAPTURE_SRC_MIC;
 154
 155        cs4245_init(chip);
 156        oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
 157                       GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
 158        /* anti-pop delay, wait some time before enabling the output */
 159        msleep(2500);
 160        oxygen_write16(chip, OXYGEN_GPIO_DATA,
 161                       GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
 162}
 163
 164void dg_cleanup(struct oxygen *chip)
 165{
 166        oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
 167}
 168
 169void dg_suspend(struct oxygen *chip)
 170{
 171        dg_cleanup(chip);
 172}
 173
 174void dg_resume(struct oxygen *chip)
 175{
 176        cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
 177        msleep(2500);
 178        oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
 179}
 180
 181void set_cs4245_dac_params(struct oxygen *chip,
 182                                  struct snd_pcm_hw_params *params)
 183{
 184        struct dg *data = chip->model_data;
 185        unsigned char dac_ctrl;
 186        unsigned char mclk_freq;
 187
 188        dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
 189        mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
 190        if (params_rate(params) <= 50000) {
 191                dac_ctrl |= CS4245_DAC_FM_SINGLE;
 192                mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
 193        } else if (params_rate(params) <= 100000) {
 194                dac_ctrl |= CS4245_DAC_FM_DOUBLE;
 195                mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
 196        } else {
 197                dac_ctrl |= CS4245_DAC_FM_QUAD;
 198                mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
 199        }
 200        data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
 201        data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
 202        cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
 203        cs4245_write_spi(chip, CS4245_MCLK_FREQ);
 204}
 205
 206void set_cs4245_adc_params(struct oxygen *chip,
 207                                  struct snd_pcm_hw_params *params)
 208{
 209        struct dg *data = chip->model_data;
 210        unsigned char adc_ctrl;
 211        unsigned char mclk_freq;
 212
 213        adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
 214        mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
 215        if (params_rate(params) <= 50000) {
 216                adc_ctrl |= CS4245_ADC_FM_SINGLE;
 217                mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
 218        } else if (params_rate(params) <= 100000) {
 219                adc_ctrl |= CS4245_ADC_FM_DOUBLE;
 220                mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
 221        } else {
 222                adc_ctrl |= CS4245_ADC_FM_QUAD;
 223                mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
 224        }
 225        data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
 226        data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
 227        cs4245_write_spi(chip, CS4245_ADC_CTRL);
 228        cs4245_write_spi(chip, CS4245_MCLK_FREQ);
 229}
 230
 231static inline unsigned int shift_bits(unsigned int value,
 232                                      unsigned int shift_from,
 233                                      unsigned int shift_to,
 234                                      unsigned int mask)
 235{
 236        if (shift_from < shift_to)
 237                return (value << (shift_to - shift_from)) & mask;
 238        else
 239                return (value >> (shift_from - shift_to)) & mask;
 240}
 241
 242unsigned int adjust_dg_dac_routing(struct oxygen *chip,
 243                                          unsigned int play_routing)
 244{
 245        struct dg *data = chip->model_data;
 246
 247        switch (data->output_sel) {
 248        case PLAYBACK_DST_HP:
 249        case PLAYBACK_DST_HP_FP:
 250                oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
 251                        OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
 252                        OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
 253                break;
 254        case PLAYBACK_DST_MULTICH:
 255                oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
 256                        OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
 257                break;
 258        }
 259        return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
 260               shift_bits(play_routing,
 261                          OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
 262                          OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
 263                          OXYGEN_PLAY_DAC1_SOURCE_MASK) |
 264               shift_bits(play_routing,
 265                          OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
 266                          OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
 267                          OXYGEN_PLAY_DAC2_SOURCE_MASK) |
 268               shift_bits(play_routing,
 269                          OXYGEN_PLAY_DAC0_SOURCE_SHIFT,
 270                          OXYGEN_PLAY_DAC3_SOURCE_SHIFT,
 271                          OXYGEN_PLAY_DAC3_SOURCE_MASK);
 272}
 273
 274void dump_cs4245_registers(struct oxygen *chip,
 275                                  struct snd_info_buffer *buffer)
 276{
 277        struct dg *data = chip->model_data;
 278        unsigned int addr;
 279
 280        snd_iprintf(buffer, "\nCS4245:");
 281        cs4245_read_spi(chip, CS4245_INT_STATUS);
 282        for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
 283                snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
 284        snd_iprintf(buffer, "\n");
 285}
 286