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