blob: b90421a1d909ac8fb7a4afce5d31d0c210284cfd [file] [log] [blame]
Thomas Gleixnerb67eb152019-05-29 07:12:38 -07001// SPDX-License-Identifier: GPL-2.0-only
Clemens Ladisch66410bf2011-01-10 16:20:29 +01002/*
Clemens Ladisch76bc7a02012-05-01 17:40:30 +02003 * card driver for the Xonar DG/DGX
Clemens Ladisch66410bf2011-01-10 16:20:29 +01004 *
5 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
Roman Volkovc4d43902014-01-24 16:18:06 +04006 * Copyright (c) Roman Volkov <v1ron@mail.ru>
Clemens Ladisch66410bf2011-01-10 16:20:29 +01007 */
8
9/*
Clemens Ladisch76bc7a02012-05-01 17:40:30 +020010 * Xonar DG/DGX
11 * ------------
Clemens Ladisch66410bf2011-01-10 16:20:29 +010012 *
Roman Volkovc4d43902014-01-24 16:18:06 +040013 * CS4245 and CS4361 both will mute all outputs if any clock ratio
14 * is invalid.
15 *
Clemens Ladisch66410bf2011-01-10 16:20:29 +010016 * CMI8788:
17 *
18 * SPI 0 -> CS4245
19 *
Roman Volkovc4d43902014-01-24 16:18:06 +040020 * Playback:
Clemens Ladischefbeb072011-01-31 11:47:52 +010021 * I²S 1 -> CS4245
22 * I²S 2 -> CS4361 (center/LFE)
23 * I²S 3 -> CS4361 (surround)
24 * I²S 4 -> CS4361 (front)
Roman Volkovc4d43902014-01-24 16:18:06 +040025 * Capture:
26 * I²S ADC 1 <- CS4245
Clemens Ladischefbeb072011-01-31 11:47:52 +010027 *
Clemens Ladisch66410bf2011-01-10 16:20:29 +010028 * GPIO 3 <- ?
29 * GPIO 4 <- headphone detect
Roman Volkovc4d43902014-01-24 16:18:06 +040030 * GPIO 5 -> enable ADC analog circuit for the left channel
31 * GPIO 6 -> enable ADC analog circuit for the right channel
Randy Dunlapc7fabbc2020-08-05 19:19:26 -070032 * GPIO 7 -> switch green rear output jack between CS4245 and the first
Roman Volkovc4d43902014-01-24 16:18:06 +040033 * channel of CS4361 (mechanical relay)
Clemens Ladisch66410bf2011-01-10 16:20:29 +010034 * GPIO 8 -> enable output to speakers
35 *
36 * CS4245:
37 *
Roman Volkovc4d43902014-01-24 16:18:06 +040038 * input 0 <- mic
Clemens Ladisch66410bf2011-01-10 16:20:29 +010039 * input 1 <- aux
40 * input 2 <- front mic
Roman Volkovc4d43902014-01-24 16:18:06 +040041 * input 4 <- line
Clemens Ladischefbeb072011-01-31 11:47:52 +010042 * DAC out -> headphones
Clemens Ladisch66410bf2011-01-10 16:20:29 +010043 * aux out -> front panel headphones
44 */
45
46#include <linux/pci.h>
Stephen Rothwelle92d4572011-01-11 13:20:30 +110047#include <linux/delay.h>
Clemens Ladisch66410bf2011-01-10 16:20:29 +010048#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
Roman Volkovbed61932014-01-24 16:18:08 +040057int 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
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100116static void cs4245_init(struct oxygen *chip)
117{
118 struct dg *data = chip->model_data;
119
Roman Volkov3c1611d2014-01-24 16:18:09 +0400120 /* save the initial state: codec version, registers */
121 cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100122
Roman Volkov3c1611d2014-01-24 16:18:09 +0400123 /*
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;
Roman Volkov3f49a662014-01-24 16:18:20 +0400141 data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
142 data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
Roman Volkov3c1611d2014-01-24 16:18:09 +0400143
144 cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
145 snd_component_add(chip->card, "CS4245");
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100146}
147
Roman Volkov041f26b2014-01-24 16:18:13 +0400148void dg_init(struct oxygen *chip)
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100149{
150 struct dg *data = chip->model_data;
151
Roman Volkov3f49a662014-01-24 16:18:20 +0400152 data->output_sel = PLAYBACK_DST_HP_FP;
153 data->input_sel = CAPTURE_SRC_MIC;
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100154
155 cs4245_init(chip);
Roman Volkov3c1611d2014-01-24 16:18:09 +0400156 oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
157 GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
Roman Volkov3f49a662014-01-24 16:18:20 +0400158 /* anti-pop delay, wait some time before enabling the output */
159 msleep(2500);
Roman Volkov3c1611d2014-01-24 16:18:09 +0400160 oxygen_write16(chip, OXYGEN_GPIO_DATA,
161 GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100162}
163
Roman Volkov041f26b2014-01-24 16:18:13 +0400164void dg_cleanup(struct oxygen *chip)
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100165{
166 oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
167}
168
Roman Volkov041f26b2014-01-24 16:18:13 +0400169void dg_suspend(struct oxygen *chip)
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100170{
171 dg_cleanup(chip);
172}
173
Roman Volkov041f26b2014-01-24 16:18:13 +0400174void dg_resume(struct oxygen *chip)
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100175{
Roman Volkov3c1611d2014-01-24 16:18:09 +0400176 cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
177 msleep(2500);
178 oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100179}
180
Roman Volkov041f26b2014-01-24 16:18:13 +0400181void set_cs4245_dac_params(struct oxygen *chip,
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100182 struct snd_pcm_hw_params *params)
183{
184 struct dg *data = chip->model_data;
Roman Volkovfddc1062014-01-24 16:18:10 +0400185 unsigned char dac_ctrl;
186 unsigned char mclk_freq;
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100187
Roman Volkovfddc1062014-01-24 16:18:10 +0400188 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);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100204}
205
Roman Volkov041f26b2014-01-24 16:18:13 +0400206void set_cs4245_adc_params(struct oxygen *chip,
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100207 struct snd_pcm_hw_params *params)
208{
209 struct dg *data = chip->model_data;
Roman Volkovfddc1062014-01-24 16:18:10 +0400210 unsigned char adc_ctrl;
211 unsigned char mclk_freq;
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100212
Roman Volkovfddc1062014-01-24 16:18:10 +0400213 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);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100229}
230
Clemens Ladisch30556442014-03-18 09:31:18 +0100231static 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
Roman Volkov041f26b2014-01-24 16:18:13 +0400242unsigned int adjust_dg_dac_routing(struct oxygen *chip,
Clemens Ladischefbeb072011-01-31 11:47:52 +0100243 unsigned int play_routing)
244{
Roman Volkov1f91ecc2014-01-24 16:18:11 +0400245 struct dg *data = chip->model_data;
Roman Volkov1f91ecc2014-01-24 16:18:11 +0400246
Roman Volkov2809cb82014-01-24 16:18:15 +0400247 switch (data->output_sel) {
Roman Volkov1f91ecc2014-01-24 16:18:11 +0400248 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:
Roman Volkov1f91ecc2014-01-24 16:18:11 +0400255 oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
256 OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
257 break;
258 }
Clemens Ladisch30556442014-03-18 09:31:18 +0100259 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);
Clemens Ladischefbeb072011-01-31 11:47:52 +0100272}
273
Roman Volkov041f26b2014-01-24 16:18:13 +0400274void dump_cs4245_registers(struct oxygen *chip,
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100275 struct snd_info_buffer *buffer)
276{
277 struct dg *data = chip->model_data;
Roman Volkov06f70d02014-01-24 16:18:12 +0400278 unsigned int addr;
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100279
280 snd_iprintf(buffer, "\nCS4245:");
Roman Volkov06f70d02014-01-24 16:18:12 +0400281 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]);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100284 snd_iprintf(buffer, "\n");
285}