blob: 4cf3200e988b03adf40129af71cfe1c9bb51bb69 [file] [log] [blame]
Clemens Ladisch66410bf2011-01-10 16:20:29 +01001/*
Clemens Ladisch76bc7a02012-05-01 17:40:30 +02002 * card driver for the Xonar DG/DGX
Clemens Ladisch66410bf2011-01-10 16:20:29 +01003 *
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
Roman Volkovc4d43902014-01-24 16:18:06 +04005 * Copyright (c) Roman Volkov <v1ron@mail.ru>
Clemens Ladisch66410bf2011-01-10 16:20:29 +01006 *
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/*
Clemens Ladisch76bc7a02012-05-01 17:40:30 +020020 * Xonar DG/DGX
21 * ------------
Clemens Ladisch66410bf2011-01-10 16:20:29 +010022 *
Roman Volkovc4d43902014-01-24 16:18:06 +040023 * CS4245 and CS4361 both will mute all outputs if any clock ratio
24 * is invalid.
25 *
Clemens Ladisch66410bf2011-01-10 16:20:29 +010026 * CMI8788:
27 *
28 * SPI 0 -> CS4245
29 *
Roman Volkovc4d43902014-01-24 16:18:06 +040030 * Playback:
Clemens Ladischefbeb072011-01-31 11:47:52 +010031 * I²S 1 -> CS4245
32 * I²S 2 -> CS4361 (center/LFE)
33 * I²S 3 -> CS4361 (surround)
34 * I²S 4 -> CS4361 (front)
Roman Volkovc4d43902014-01-24 16:18:06 +040035 * Capture:
36 * I²S ADC 1 <- CS4245
Clemens Ladischefbeb072011-01-31 11:47:52 +010037 *
Clemens Ladisch66410bf2011-01-10 16:20:29 +010038 * GPIO 3 <- ?
39 * GPIO 4 <- headphone detect
Roman Volkovc4d43902014-01-24 16:18:06 +040040 * 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)
Clemens Ladisch66410bf2011-01-10 16:20:29 +010044 * GPIO 8 -> enable output to speakers
45 *
46 * CS4245:
47 *
Roman Volkovc4d43902014-01-24 16:18:06 +040048 * input 0 <- mic
Clemens Ladisch66410bf2011-01-10 16:20:29 +010049 * input 1 <- aux
50 * input 2 <- front mic
Roman Volkovc4d43902014-01-24 16:18:06 +040051 * input 4 <- line
Clemens Ladischefbeb072011-01-31 11:47:52 +010052 * DAC out -> headphones
Clemens Ladisch66410bf2011-01-10 16:20:29 +010053 * aux out -> front panel headphones
54 */
55
56#include <linux/pci.h>
Stephen Rothwelle92d4572011-01-11 13:20:30 +110057#include <linux/delay.h>
Clemens Ladisch66410bf2011-01-10 16:20:29 +010058#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
Roman Volkovbed61932014-01-24 16:18:08 +040067int 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
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100126static void cs4245_init(struct oxygen *chip)
127{
128 struct dg *data = chip->model_data;
129
Roman Volkov3c1611d2014-01-24 16:18:09 +0400130 /* save the initial state: codec version, registers */
131 cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100132
Roman Volkov3c1611d2014-01-24 16:18:09 +0400133 /*
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;
Roman Volkov3f49a662014-01-24 16:18:20 +0400151 data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
152 data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
Roman Volkov3c1611d2014-01-24 16:18:09 +0400153
154 cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
155 snd_component_add(chip->card, "CS4245");
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100156}
157
Roman Volkov041f26b2014-01-24 16:18:13 +0400158void dg_init(struct oxygen *chip)
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100159{
160 struct dg *data = chip->model_data;
161
Roman Volkov3f49a662014-01-24 16:18:20 +0400162 data->output_sel = PLAYBACK_DST_HP_FP;
163 data->input_sel = CAPTURE_SRC_MIC;
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100164
165 cs4245_init(chip);
Roman Volkov3c1611d2014-01-24 16:18:09 +0400166 oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
167 GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
Roman Volkov3f49a662014-01-24 16:18:20 +0400168 /* anti-pop delay, wait some time before enabling the output */
169 msleep(2500);
Roman Volkov3c1611d2014-01-24 16:18:09 +0400170 oxygen_write16(chip, OXYGEN_GPIO_DATA,
171 GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100172}
173
Roman Volkov041f26b2014-01-24 16:18:13 +0400174void dg_cleanup(struct oxygen *chip)
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100175{
176 oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
177}
178
Roman Volkov041f26b2014-01-24 16:18:13 +0400179void dg_suspend(struct oxygen *chip)
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100180{
181 dg_cleanup(chip);
182}
183
Roman Volkov041f26b2014-01-24 16:18:13 +0400184void dg_resume(struct oxygen *chip)
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100185{
Roman Volkov3c1611d2014-01-24 16:18:09 +0400186 cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
187 msleep(2500);
188 oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100189}
190
Roman Volkov041f26b2014-01-24 16:18:13 +0400191void set_cs4245_dac_params(struct oxygen *chip,
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100192 struct snd_pcm_hw_params *params)
193{
194 struct dg *data = chip->model_data;
Roman Volkovfddc1062014-01-24 16:18:10 +0400195 unsigned char dac_ctrl;
196 unsigned char mclk_freq;
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100197
Roman Volkovfddc1062014-01-24 16:18:10 +0400198 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);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100214}
215
Roman Volkov041f26b2014-01-24 16:18:13 +0400216void set_cs4245_adc_params(struct oxygen *chip,
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100217 struct snd_pcm_hw_params *params)
218{
219 struct dg *data = chip->model_data;
Roman Volkovfddc1062014-01-24 16:18:10 +0400220 unsigned char adc_ctrl;
221 unsigned char mclk_freq;
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100222
Roman Volkovfddc1062014-01-24 16:18:10 +0400223 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);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100239}
240
Clemens Ladisch30556442014-03-18 09:31:18 +0100241static 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
Roman Volkov041f26b2014-01-24 16:18:13 +0400252unsigned int adjust_dg_dac_routing(struct oxygen *chip,
Clemens Ladischefbeb072011-01-31 11:47:52 +0100253 unsigned int play_routing)
254{
Roman Volkov1f91ecc2014-01-24 16:18:11 +0400255 struct dg *data = chip->model_data;
Roman Volkov1f91ecc2014-01-24 16:18:11 +0400256
Roman Volkov2809cb82014-01-24 16:18:15 +0400257 switch (data->output_sel) {
Roman Volkov1f91ecc2014-01-24 16:18:11 +0400258 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:
Roman Volkov1f91ecc2014-01-24 16:18:11 +0400265 oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
266 OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
267 break;
268 }
Clemens Ladisch30556442014-03-18 09:31:18 +0100269 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);
Clemens Ladischefbeb072011-01-31 11:47:52 +0100282}
283
Roman Volkov041f26b2014-01-24 16:18:13 +0400284void dump_cs4245_registers(struct oxygen *chip,
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100285 struct snd_info_buffer *buffer)
286{
287 struct dg *data = chip->model_data;
Roman Volkov06f70d02014-01-24 16:18:12 +0400288 unsigned int addr;
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100289
290 snd_iprintf(buffer, "\nCS4245:");
Roman Volkov06f70d02014-01-24 16:18:12 +0400291 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]);
Clemens Ladisch66410bf2011-01-10 16:20:29 +0100294 snd_iprintf(buffer, "\n");
295}