Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * card driver for the Xonar DG/DGX
0004  *
0005  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
0006  * Copyright (c) Roman Volkov <v1ron@mail.ru>
0007  */
0008 
0009 /*
0010  * Xonar DG/DGX
0011  * ------------
0012  *
0013  * CS4245 and CS4361 both will mute all outputs if any clock ratio
0014  * is invalid.
0015  *
0016  * CMI8788:
0017  *
0018  *   SPI 0 -> CS4245
0019  *
0020  *   Playback:
0021  *   I²S 1 -> CS4245
0022  *   I²S 2 -> CS4361 (center/LFE)
0023  *   I²S 3 -> CS4361 (surround)
0024  *   I²S 4 -> CS4361 (front)
0025  *   Capture:
0026  *   I²S ADC 1 <- CS4245
0027  *
0028  *   GPIO 3 <- ?
0029  *   GPIO 4 <- headphone detect
0030  *   GPIO 5 -> enable ADC analog circuit for the left channel
0031  *   GPIO 6 -> enable ADC analog circuit for the right channel
0032  *   GPIO 7 -> switch green rear output jack between CS4245 and the first
0033  *             channel of CS4361 (mechanical relay)
0034  *   GPIO 8 -> enable output to speakers
0035  *
0036  * CS4245:
0037  *
0038  *   input 0 <- mic
0039  *   input 1 <- aux
0040  *   input 2 <- front mic
0041  *   input 4 <- line
0042  *   DAC out -> headphones
0043  *   aux out -> front panel headphones
0044  */
0045 
0046 #include <linux/pci.h>
0047 #include <linux/delay.h>
0048 #include <sound/control.h>
0049 #include <sound/core.h>
0050 #include <sound/info.h>
0051 #include <sound/pcm.h>
0052 #include <sound/tlv.h>
0053 #include "oxygen.h"
0054 #include "xonar_dg.h"
0055 #include "cs4245.h"
0056 
0057 int cs4245_write_spi(struct oxygen *chip, u8 reg)
0058 {
0059     struct dg *data = chip->model_data;
0060     unsigned int packet;
0061 
0062     packet = reg << 8;
0063     packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
0064     packet |= data->cs4245_shadow[reg];
0065 
0066     return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
0067                 OXYGEN_SPI_DATA_LENGTH_3 |
0068                 OXYGEN_SPI_CLOCK_1280 |
0069                 (0 << OXYGEN_SPI_CODEC_SHIFT) |
0070                 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
0071                 packet);
0072 }
0073 
0074 int cs4245_read_spi(struct oxygen *chip, u8 addr)
0075 {
0076     struct dg *data = chip->model_data;
0077     int ret;
0078 
0079     ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
0080         OXYGEN_SPI_DATA_LENGTH_2 |
0081         OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
0082         OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
0083         ((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);
0084     if (ret < 0)
0085         return ret;
0086 
0087     ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
0088         OXYGEN_SPI_DATA_LENGTH_2 |
0089         OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
0090         OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
0091         (CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);
0092     if (ret < 0)
0093         return ret;
0094 
0095     data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);
0096 
0097     return 0;
0098 }
0099 
0100 int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
0101 {
0102     struct dg *data = chip->model_data;
0103     unsigned char addr;
0104     int ret;
0105 
0106     for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
0107         ret = (op == CS4245_SAVE_TO_SHADOW ?
0108             cs4245_read_spi(chip, addr) :
0109             cs4245_write_spi(chip, addr));
0110         if (ret < 0)
0111             return ret;
0112     }
0113     return 0;
0114 }
0115 
0116 static void cs4245_init(struct oxygen *chip)
0117 {
0118     struct dg *data = chip->model_data;
0119 
0120     /* save the initial state: codec version, registers */
0121     cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
0122 
0123     /*
0124      * Power up the CODEC internals, enable soft ramp & zero cross, work in
0125      * async. mode, enable aux output from DAC. Invert DAC output as in the
0126      * Windows driver.
0127      */
0128     data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
0129     data->cs4245_shadow[CS4245_SIGNAL_SEL] =
0130         CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
0131     data->cs4245_shadow[CS4245_DAC_CTRL_1] =
0132         CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
0133     data->cs4245_shadow[CS4245_DAC_CTRL_2] =
0134         CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
0135     data->cs4245_shadow[CS4245_ADC_CTRL] =
0136         CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
0137     data->cs4245_shadow[CS4245_ANALOG_IN] =
0138         CS4245_PGA_SOFT | CS4245_PGA_ZERO;
0139     data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
0140     data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
0141     data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
0142     data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
0143 
0144     cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
0145     snd_component_add(chip->card, "CS4245");
0146 }
0147 
0148 void dg_init(struct oxygen *chip)
0149 {
0150     struct dg *data = chip->model_data;
0151 
0152     data->output_sel = PLAYBACK_DST_HP_FP;
0153     data->input_sel = CAPTURE_SRC_MIC;
0154 
0155     cs4245_init(chip);
0156     oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
0157                GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
0158     /* anti-pop delay, wait some time before enabling the output */
0159     msleep(2500);
0160     oxygen_write16(chip, OXYGEN_GPIO_DATA,
0161                GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
0162 }
0163 
0164 void dg_cleanup(struct oxygen *chip)
0165 {
0166     oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
0167 }
0168 
0169 void dg_suspend(struct oxygen *chip)
0170 {
0171     dg_cleanup(chip);
0172 }
0173 
0174 void dg_resume(struct oxygen *chip)
0175 {
0176     cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
0177     msleep(2500);
0178     oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
0179 }
0180 
0181 void set_cs4245_dac_params(struct oxygen *chip,
0182                   struct snd_pcm_hw_params *params)
0183 {
0184     struct dg *data = chip->model_data;
0185     unsigned char dac_ctrl;
0186     unsigned char mclk_freq;
0187 
0188     dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
0189     mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
0190     if (params_rate(params) <= 50000) {
0191         dac_ctrl |= CS4245_DAC_FM_SINGLE;
0192         mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
0193     } else if (params_rate(params) <= 100000) {
0194         dac_ctrl |= CS4245_DAC_FM_DOUBLE;
0195         mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
0196     } else {
0197         dac_ctrl |= CS4245_DAC_FM_QUAD;
0198         mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
0199     }
0200     data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
0201     data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
0202     cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
0203     cs4245_write_spi(chip, CS4245_MCLK_FREQ);
0204 }
0205 
0206 void set_cs4245_adc_params(struct oxygen *chip,
0207                   struct snd_pcm_hw_params *params)
0208 {
0209     struct dg *data = chip->model_data;
0210     unsigned char adc_ctrl;
0211     unsigned char mclk_freq;
0212 
0213     adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
0214     mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
0215     if (params_rate(params) <= 50000) {
0216         adc_ctrl |= CS4245_ADC_FM_SINGLE;
0217         mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
0218     } else if (params_rate(params) <= 100000) {
0219         adc_ctrl |= CS4245_ADC_FM_DOUBLE;
0220         mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
0221     } else {
0222         adc_ctrl |= CS4245_ADC_FM_QUAD;
0223         mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
0224     }
0225     data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
0226     data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
0227     cs4245_write_spi(chip, CS4245_ADC_CTRL);
0228     cs4245_write_spi(chip, CS4245_MCLK_FREQ);
0229 }
0230 
0231 static inline unsigned int shift_bits(unsigned int value,
0232                       unsigned int shift_from,
0233                       unsigned int shift_to,
0234                       unsigned int mask)
0235 {
0236     if (shift_from < shift_to)
0237         return (value << (shift_to - shift_from)) & mask;
0238     else
0239         return (value >> (shift_from - shift_to)) & mask;
0240 }
0241 
0242 unsigned int adjust_dg_dac_routing(struct oxygen *chip,
0243                       unsigned int play_routing)
0244 {
0245     struct dg *data = chip->model_data;
0246 
0247     switch (data->output_sel) {
0248     case PLAYBACK_DST_HP:
0249     case PLAYBACK_DST_HP_FP:
0250         oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
0251             OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
0252             OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
0253         break;
0254     case PLAYBACK_DST_MULTICH:
0255         oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
0256             OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
0257         break;
0258     }
0259     return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
0260            shift_bits(play_routing,
0261               OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
0262               OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
0263               OXYGEN_PLAY_DAC1_SOURCE_MASK) |
0264            shift_bits(play_routing,
0265               OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
0266               OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
0267               OXYGEN_PLAY_DAC2_SOURCE_MASK) |
0268            shift_bits(play_routing,
0269               OXYGEN_PLAY_DAC0_SOURCE_SHIFT,
0270               OXYGEN_PLAY_DAC3_SOURCE_SHIFT,
0271               OXYGEN_PLAY_DAC3_SOURCE_MASK);
0272 }
0273 
0274 void dump_cs4245_registers(struct oxygen *chip,
0275                   struct snd_info_buffer *buffer)
0276 {
0277     struct dg *data = chip->model_data;
0278     unsigned int addr;
0279 
0280     snd_iprintf(buffer, "\nCS4245:");
0281     cs4245_read_spi(chip, CS4245_INT_STATUS);
0282     for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
0283         snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
0284     snd_iprintf(buffer, "\n");
0285 }