File Index
Files and directories
Sign in
FILE

xonar_dg.c

File preview with syntax highlighting.

linux-6.19.14/sound/pci/oxygen/xonar_dg.c C
  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
 57int 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
116static 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
148void 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
164void dg_cleanup(struct oxygen *chip)
165{
166	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
167}
168
169void dg_suspend(struct oxygen *chip)
170{
171	dg_cleanup(chip);
172}
173
174void 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
181void 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
206void 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
231static 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
242unsigned 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
274void 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}
~/ Read-only file preview.