xonar_dg.c 7.9 KB
Newer Older
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 8 9 10 11 12 13 14 15 16 17 18 19
 *
 *  This driver is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2.
 *
 *  This driver is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this driver; if not, see <http://www.gnu.org/licenses/>.
 */

/*
20 21
 * Xonar DG/DGX
 * ------------
22
 *
23 24 25
 * CS4245 and CS4361 both will mute all outputs if any clock ratio
 * is invalid.
 *
26 27 28 29
 * CMI8788:
 *
 *   SPI 0 -> CS4245
 *
30
 *   Playback:
31 32 33 34
 *   I²S 1 -> CS4245
 *   I²S 2 -> CS4361 (center/LFE)
 *   I²S 3 -> CS4361 (surround)
 *   I²S 4 -> CS4361 (front)
35 36
 *   Capture:
 *   I²S ADC 1 <- CS4245
37
 *
38 39
 *   GPIO 3 <- ?
 *   GPIO 4 <- headphone detect
40 41 42 43
 *   GPIO 5 -> enable ADC analog circuit for the left channel
 *   GPIO 6 -> enable ADC analog circuit for the right channel
 *   GPIO 7 -> switch green rear output jack between CS4245 and and the first
 *             channel of CS4361 (mechanical relay)
44 45 46 47
 *   GPIO 8 -> enable output to speakers
 *
 * CS4245:
 *
48
 *   input 0 <- mic
49 50
 *   input 1 <- aux
 *   input 2 <- front mic
51
 *   input 4 <- line
52
 *   DAC out -> headphones
53 54 55 56
 *   aux out -> front panel headphones
 */

#include <linux/pci.h>
57
#include <linux/delay.h>
58 59 60 61 62 63 64 65 66
#include <sound/control.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
#include "oxygen.h"
#include "xonar_dg.h"
#include "cs4245.h"

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
int cs4245_write_spi(struct oxygen *chip, u8 reg)
{
	struct dg *data = chip->model_data;
	unsigned int packet;

	packet = reg << 8;
	packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
	packet |= data->cs4245_shadow[reg];

	return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
				OXYGEN_SPI_DATA_LENGTH_3 |
				OXYGEN_SPI_CLOCK_1280 |
				(0 << OXYGEN_SPI_CODEC_SHIFT) |
				OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
				packet);
}

int cs4245_read_spi(struct oxygen *chip, u8 addr)
{
	struct dg *data = chip->model_data;
	int ret;

	ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
		OXYGEN_SPI_DATA_LENGTH_2 |
		OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
		OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
		((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);
	if (ret < 0)
		return ret;

	ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
		OXYGEN_SPI_DATA_LENGTH_2 |
		OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
		OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
		(CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);
	if (ret < 0)
		return ret;

	data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);

	return 0;
}

int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
{
	struct dg *data = chip->model_data;
	unsigned char addr;
	int ret;

	for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
		ret = (op == CS4245_SAVE_TO_SHADOW ?
			cs4245_read_spi(chip, addr) :
			cs4245_write_spi(chip, addr));
		if (ret < 0)
			return ret;
	}
	return 0;
}

126 127 128 129
static void cs4245_init(struct oxygen *chip)
{
	struct dg *data = chip->model_data;

130 131 132 133 134 135 136 137 138 139 140 141
	/* save the initial state: codec version, registers */
	cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);

	/*
	 * Power up the CODEC internals, enable soft ramp & zero cross, work in
	 * async. mode, enable aux output from DAC. Invert DAC output as in the
	 * Windows driver.
	 */
	data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
	data->cs4245_shadow[CS4245_SIGNAL_SEL] =
		CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
	data->cs4245_shadow[CS4245_DAC_CTRL_1] =
142
		CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
143 144 145
	data->cs4245_shadow[CS4245_DAC_CTRL_2] =
		CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
	data->cs4245_shadow[CS4245_ADC_CTRL] =
146
		CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
147 148 149 150
	data->cs4245_shadow[CS4245_ANALOG_IN] =
		CS4245_PGA_SOFT | CS4245_PGA_ZERO;
	data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
	data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
151 152
	data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
	data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
153 154

	cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
155 156 157
	snd_component_add(chip->card, "CS4245");
}

158
void dg_init(struct oxygen *chip)
159 160 161
{
	struct dg *data = chip->model_data;

162 163
	data->output_sel = PLAYBACK_DST_HP_FP;
	data->input_sel = CAPTURE_SRC_MIC;
164 165

	cs4245_init(chip);
166 167
	oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
		       GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
168 169
	/* anti-pop delay, wait some time before enabling the output */
	msleep(2500);
170 171
	oxygen_write16(chip, OXYGEN_GPIO_DATA,
		       GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
172 173
}

174
void dg_cleanup(struct oxygen *chip)
175 176 177 178
{
	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
}

179
void dg_suspend(struct oxygen *chip)
180 181 182 183
{
	dg_cleanup(chip);
}

184
void dg_resume(struct oxygen *chip)
185
{
186 187 188
	cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
	msleep(2500);
	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
189 190
}

191
void set_cs4245_dac_params(struct oxygen *chip,
192 193 194
				  struct snd_pcm_hw_params *params)
{
	struct dg *data = chip->model_data;
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
	unsigned char dac_ctrl;
	unsigned char mclk_freq;

	dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
	mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
	if (params_rate(params) <= 50000) {
		dac_ctrl |= CS4245_DAC_FM_SINGLE;
		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
	} else if (params_rate(params) <= 100000) {
		dac_ctrl |= CS4245_DAC_FM_DOUBLE;
		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
	} else {
		dac_ctrl |= CS4245_DAC_FM_QUAD;
		mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
	}
	data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
	data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
	cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
	cs4245_write_spi(chip, CS4245_MCLK_FREQ);
214 215
}

216
void set_cs4245_adc_params(struct oxygen *chip,
217 218 219
				  struct snd_pcm_hw_params *params)
{
	struct dg *data = chip->model_data;
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
	unsigned char adc_ctrl;
	unsigned char mclk_freq;

	adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
	mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
	if (params_rate(params) <= 50000) {
		adc_ctrl |= CS4245_ADC_FM_SINGLE;
		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
	} else if (params_rate(params) <= 100000) {
		adc_ctrl |= CS4245_ADC_FM_DOUBLE;
		mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
	} else {
		adc_ctrl |= CS4245_ADC_FM_QUAD;
		mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
	}
	data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
	data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
	cs4245_write_spi(chip, CS4245_ADC_CTRL);
	cs4245_write_spi(chip, CS4245_MCLK_FREQ);
239 240
}

241
unsigned int adjust_dg_dac_routing(struct oxygen *chip,
242 243
					  unsigned int play_routing)
{
244 245 246
	struct dg *data = chip->model_data;
	unsigned int routing = 0;

247
	switch (data->output_sel) {
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
	case PLAYBACK_DST_HP:
	case PLAYBACK_DST_HP_FP:
		oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
			OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
			OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
		break;
	case PLAYBACK_DST_MULTICH:
		routing = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
			  (2 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
			  (1 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
			  (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT);
		oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
			OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
		break;
	}
	return routing;
264 265
}

266
void dump_cs4245_registers(struct oxygen *chip,
267 268 269
				  struct snd_info_buffer *buffer)
{
	struct dg *data = chip->model_data;
270
	unsigned int addr;
271 272

	snd_iprintf(buffer, "\nCS4245:");
273 274 275
	cs4245_read_spi(chip, CS4245_INT_STATUS);
	for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
		snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
276 277
	snd_iprintf(buffer, "\n");
}
反馈
建议
客服 返回
顶部