Linux Audio

Check our new training course

Loading...
v6.2
  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}
v4.10.11
 
  1/*
  2 * card driver for the Xonar DG/DGX
  3 *
  4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  5 * Copyright (c) Roman Volkov <v1ron@mail.ru>
  6 *
  7 *  This driver is free software; you can redistribute it and/or modify
  8 *  it under the terms of the GNU General Public License, version 2.
  9 *
 10 *  This driver is distributed in the hope that it will be useful,
 11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 *  GNU General Public License for more details.
 14 *
 15 *  You should have received a copy of the GNU General Public License
 16 *  along with this driver; if not, see <http://www.gnu.org/licenses/>.
 17 */
 18
 19/*
 20 * Xonar DG/DGX
 21 * ------------
 22 *
 23 * CS4245 and CS4361 both will mute all outputs if any clock ratio
 24 * is invalid.
 25 *
 26 * CMI8788:
 27 *
 28 *   SPI 0 -> CS4245
 29 *
 30 *   Playback:
 31 *   I²S 1 -> CS4245
 32 *   I²S 2 -> CS4361 (center/LFE)
 33 *   I²S 3 -> CS4361 (surround)
 34 *   I²S 4 -> CS4361 (front)
 35 *   Capture:
 36 *   I²S ADC 1 <- CS4245
 37 *
 38 *   GPIO 3 <- ?
 39 *   GPIO 4 <- headphone detect
 40 *   GPIO 5 -> enable ADC analog circuit for the left channel
 41 *   GPIO 6 -> enable ADC analog circuit for the right channel
 42 *   GPIO 7 -> switch green rear output jack between CS4245 and and the first
 43 *             channel of CS4361 (mechanical relay)
 44 *   GPIO 8 -> enable output to speakers
 45 *
 46 * CS4245:
 47 *
 48 *   input 0 <- mic
 49 *   input 1 <- aux
 50 *   input 2 <- front mic
 51 *   input 4 <- line
 52 *   DAC out -> headphones
 53 *   aux out -> front panel headphones
 54 */
 55
 56#include <linux/pci.h>
 57#include <linux/delay.h>
 58#include <sound/control.h>
 59#include <sound/core.h>
 60#include <sound/info.h>
 61#include <sound/pcm.h>
 62#include <sound/tlv.h>
 63#include "oxygen.h"
 64#include "xonar_dg.h"
 65#include "cs4245.h"
 66
 67int cs4245_write_spi(struct oxygen *chip, u8 reg)
 68{
 69	struct dg *data = chip->model_data;
 70	unsigned int packet;
 71
 72	packet = reg << 8;
 73	packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
 74	packet |= data->cs4245_shadow[reg];
 75
 76	return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
 77				OXYGEN_SPI_DATA_LENGTH_3 |
 78				OXYGEN_SPI_CLOCK_1280 |
 79				(0 << OXYGEN_SPI_CODEC_SHIFT) |
 80				OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
 81				packet);
 82}
 83
 84int cs4245_read_spi(struct oxygen *chip, u8 addr)
 85{
 86	struct dg *data = chip->model_data;
 87	int ret;
 88
 89	ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
 90		OXYGEN_SPI_DATA_LENGTH_2 |
 91		OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
 92		OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
 93		((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);
 94	if (ret < 0)
 95		return ret;
 96
 97	ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
 98		OXYGEN_SPI_DATA_LENGTH_2 |
 99		OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
100		OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
101		(CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);
102	if (ret < 0)
103		return ret;
104
105	data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);
106
107	return 0;
108}
109
110int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
111{
112	struct dg *data = chip->model_data;
113	unsigned char addr;
114	int ret;
115
116	for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
117		ret = (op == CS4245_SAVE_TO_SHADOW ?
118			cs4245_read_spi(chip, addr) :
119			cs4245_write_spi(chip, addr));
120		if (ret < 0)
121			return ret;
122	}
123	return 0;
124}
125
126static void cs4245_init(struct oxygen *chip)
127{
128	struct dg *data = chip->model_data;
129
130	/* save the initial state: codec version, registers */
131	cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
132
133	/*
134	 * Power up the CODEC internals, enable soft ramp & zero cross, work in
135	 * async. mode, enable aux output from DAC. Invert DAC output as in the
136	 * Windows driver.
137	 */
138	data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
139	data->cs4245_shadow[CS4245_SIGNAL_SEL] =
140		CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
141	data->cs4245_shadow[CS4245_DAC_CTRL_1] =
142		CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
143	data->cs4245_shadow[CS4245_DAC_CTRL_2] =
144		CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
145	data->cs4245_shadow[CS4245_ADC_CTRL] =
146		CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
147	data->cs4245_shadow[CS4245_ANALOG_IN] =
148		CS4245_PGA_SOFT | CS4245_PGA_ZERO;
149	data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
150	data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
151	data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
152	data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
153
154	cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
155	snd_component_add(chip->card, "CS4245");
156}
157
158void dg_init(struct oxygen *chip)
159{
160	struct dg *data = chip->model_data;
161
162	data->output_sel = PLAYBACK_DST_HP_FP;
163	data->input_sel = CAPTURE_SRC_MIC;
164
165	cs4245_init(chip);
166	oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
167		       GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
168	/* anti-pop delay, wait some time before enabling the output */
169	msleep(2500);
170	oxygen_write16(chip, OXYGEN_GPIO_DATA,
171		       GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
172}
173
174void dg_cleanup(struct oxygen *chip)
175{
176	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
177}
178
179void dg_suspend(struct oxygen *chip)
180{
181	dg_cleanup(chip);
182}
183
184void dg_resume(struct oxygen *chip)
185{
186	cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
187	msleep(2500);
188	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
189}
190
191void set_cs4245_dac_params(struct oxygen *chip,
192				  struct snd_pcm_hw_params *params)
193{
194	struct dg *data = chip->model_data;
195	unsigned char dac_ctrl;
196	unsigned char mclk_freq;
197
198	dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
199	mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
200	if (params_rate(params) <= 50000) {
201		dac_ctrl |= CS4245_DAC_FM_SINGLE;
202		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
203	} else if (params_rate(params) <= 100000) {
204		dac_ctrl |= CS4245_DAC_FM_DOUBLE;
205		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
206	} else {
207		dac_ctrl |= CS4245_DAC_FM_QUAD;
208		mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
209	}
210	data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
211	data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
212	cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
213	cs4245_write_spi(chip, CS4245_MCLK_FREQ);
214}
215
216void set_cs4245_adc_params(struct oxygen *chip,
217				  struct snd_pcm_hw_params *params)
218{
219	struct dg *data = chip->model_data;
220	unsigned char adc_ctrl;
221	unsigned char mclk_freq;
222
223	adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
224	mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
225	if (params_rate(params) <= 50000) {
226		adc_ctrl |= CS4245_ADC_FM_SINGLE;
227		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
228	} else if (params_rate(params) <= 100000) {
229		adc_ctrl |= CS4245_ADC_FM_DOUBLE;
230		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
231	} else {
232		adc_ctrl |= CS4245_ADC_FM_QUAD;
233		mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
234	}
235	data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
236	data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
237	cs4245_write_spi(chip, CS4245_ADC_CTRL);
238	cs4245_write_spi(chip, CS4245_MCLK_FREQ);
239}
240
241static inline unsigned int shift_bits(unsigned int value,
242				      unsigned int shift_from,
243				      unsigned int shift_to,
244				      unsigned int mask)
245{
246	if (shift_from < shift_to)
247		return (value << (shift_to - shift_from)) & mask;
248	else
249		return (value >> (shift_from - shift_to)) & mask;
250}
251
252unsigned int adjust_dg_dac_routing(struct oxygen *chip,
253					  unsigned int play_routing)
254{
255	struct dg *data = chip->model_data;
256
257	switch (data->output_sel) {
258	case PLAYBACK_DST_HP:
259	case PLAYBACK_DST_HP_FP:
260		oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
261			OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
262			OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
263		break;
264	case PLAYBACK_DST_MULTICH:
265		oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
266			OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
267		break;
268	}
269	return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
270	       shift_bits(play_routing,
271			  OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
272			  OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
273			  OXYGEN_PLAY_DAC1_SOURCE_MASK) |
274	       shift_bits(play_routing,
275			  OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
276			  OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
277			  OXYGEN_PLAY_DAC2_SOURCE_MASK) |
278	       shift_bits(play_routing,
279			  OXYGEN_PLAY_DAC0_SOURCE_SHIFT,
280			  OXYGEN_PLAY_DAC3_SOURCE_SHIFT,
281			  OXYGEN_PLAY_DAC3_SOURCE_MASK);
282}
283
284void dump_cs4245_registers(struct oxygen *chip,
285				  struct snd_info_buffer *buffer)
286{
287	struct dg *data = chip->model_data;
288	unsigned int addr;
289
290	snd_iprintf(buffer, "\nCS4245:");
291	cs4245_read_spi(chip, CS4245_INT_STATUS);
292	for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
293		snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
294	snd_iprintf(buffer, "\n");
295}