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 57 int 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 74 int 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 100 int 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 116 static 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 148 void 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 164 void dg_cleanup(struct oxygen *chip) 165 { 166 oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); 167 } 168 169 void dg_suspend(struct oxygen *chip) 170 { 171 dg_cleanup(chip); 172 } 173 174 void 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 181 void 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 206 void 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 231 static 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 242 unsigned 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 274 void 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.