xref: /openbmc/linux/sound/pci/oxygen/xonar_dg.c (revision c7fabbc5)
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 
cs4245_write_spi(struct oxygen * chip,u8 reg)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 
cs4245_read_spi(struct oxygen * chip,u8 addr)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 
cs4245_shadow_control(struct oxygen * chip,enum cs4245_shadow_operation op)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 
cs4245_init(struct oxygen * chip)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 
dg_init(struct oxygen * chip)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 
dg_cleanup(struct oxygen * chip)164 void dg_cleanup(struct oxygen *chip)
165 {
166 	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
167 }
168 
dg_suspend(struct oxygen * chip)169 void dg_suspend(struct oxygen *chip)
170 {
171 	dg_cleanup(chip);
172 }
173 
dg_resume(struct oxygen * chip)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 
set_cs4245_dac_params(struct oxygen * chip,struct snd_pcm_hw_params * params)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 
set_cs4245_adc_params(struct oxygen * chip,struct snd_pcm_hw_params * params)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 
shift_bits(unsigned int value,unsigned int shift_from,unsigned int shift_to,unsigned int mask)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 
adjust_dg_dac_routing(struct oxygen * chip,unsigned int play_routing)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 
dump_cs4245_registers(struct oxygen * chip,struct snd_info_buffer * buffer)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