1b67eb152SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
266410bfdSClemens Ladisch /*
376bc7a0dSClemens Ladisch * card driver for the Xonar DG/DGX
466410bfdSClemens Ladisch *
566410bfdSClemens Ladisch * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
6c4d4390cSRoman Volkov * Copyright (c) Roman Volkov <v1ron@mail.ru>
766410bfdSClemens Ladisch */
866410bfdSClemens Ladisch
966410bfdSClemens Ladisch /*
1076bc7a0dSClemens Ladisch * Xonar DG/DGX
1176bc7a0dSClemens Ladisch * ------------
1266410bfdSClemens Ladisch *
13c4d4390cSRoman Volkov * CS4245 and CS4361 both will mute all outputs if any clock ratio
14c4d4390cSRoman Volkov * is invalid.
15c4d4390cSRoman Volkov *
1666410bfdSClemens Ladisch * CMI8788:
1766410bfdSClemens Ladisch *
1866410bfdSClemens Ladisch * SPI 0 -> CS4245
1966410bfdSClemens Ladisch *
20c4d4390cSRoman Volkov * Playback:
21efbeb071SClemens Ladisch * I²S 1 -> CS4245
22efbeb071SClemens Ladisch * I²S 2 -> CS4361 (center/LFE)
23efbeb071SClemens Ladisch * I²S 3 -> CS4361 (surround)
24efbeb071SClemens Ladisch * I²S 4 -> CS4361 (front)
25c4d4390cSRoman Volkov * Capture:
26c4d4390cSRoman Volkov * I²S ADC 1 <- CS4245
27efbeb071SClemens Ladisch *
2866410bfdSClemens Ladisch * GPIO 3 <- ?
2966410bfdSClemens Ladisch * GPIO 4 <- headphone detect
30c4d4390cSRoman Volkov * GPIO 5 -> enable ADC analog circuit for the left channel
31c4d4390cSRoman Volkov * GPIO 6 -> enable ADC analog circuit for the right channel
32*c7fabbc5SRandy Dunlap * GPIO 7 -> switch green rear output jack between CS4245 and the first
33c4d4390cSRoman Volkov * channel of CS4361 (mechanical relay)
3466410bfdSClemens Ladisch * GPIO 8 -> enable output to speakers
3566410bfdSClemens Ladisch *
3666410bfdSClemens Ladisch * CS4245:
3766410bfdSClemens Ladisch *
38c4d4390cSRoman Volkov * input 0 <- mic
3966410bfdSClemens Ladisch * input 1 <- aux
4066410bfdSClemens Ladisch * input 2 <- front mic
41c4d4390cSRoman Volkov * input 4 <- line
42efbeb071SClemens Ladisch * DAC out -> headphones
4366410bfdSClemens Ladisch * aux out -> front panel headphones
4466410bfdSClemens Ladisch */
4566410bfdSClemens Ladisch
4666410bfdSClemens Ladisch #include <linux/pci.h>
47e92d4575SStephen Rothwell #include <linux/delay.h>
4866410bfdSClemens Ladisch #include <sound/control.h>
4966410bfdSClemens Ladisch #include <sound/core.h>
5066410bfdSClemens Ladisch #include <sound/info.h>
5166410bfdSClemens Ladisch #include <sound/pcm.h>
5266410bfdSClemens Ladisch #include <sound/tlv.h>
5366410bfdSClemens Ladisch #include "oxygen.h"
5466410bfdSClemens Ladisch #include "xonar_dg.h"
5566410bfdSClemens Ladisch #include "cs4245.h"
5666410bfdSClemens Ladisch
cs4245_write_spi(struct oxygen * chip,u8 reg)57bed61935SRoman Volkov int cs4245_write_spi(struct oxygen *chip, u8 reg)
58bed61935SRoman Volkov {
59bed61935SRoman Volkov struct dg *data = chip->model_data;
60bed61935SRoman Volkov unsigned int packet;
61bed61935SRoman Volkov
62bed61935SRoman Volkov packet = reg << 8;
63bed61935SRoman Volkov packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
64bed61935SRoman Volkov packet |= data->cs4245_shadow[reg];
65bed61935SRoman Volkov
66bed61935SRoman Volkov return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
67bed61935SRoman Volkov OXYGEN_SPI_DATA_LENGTH_3 |
68bed61935SRoman Volkov OXYGEN_SPI_CLOCK_1280 |
69bed61935SRoman Volkov (0 << OXYGEN_SPI_CODEC_SHIFT) |
70bed61935SRoman Volkov OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
71bed61935SRoman Volkov packet);
72bed61935SRoman Volkov }
73bed61935SRoman Volkov
cs4245_read_spi(struct oxygen * chip,u8 addr)74bed61935SRoman Volkov int cs4245_read_spi(struct oxygen *chip, u8 addr)
75bed61935SRoman Volkov {
76bed61935SRoman Volkov struct dg *data = chip->model_data;
77bed61935SRoman Volkov int ret;
78bed61935SRoman Volkov
79bed61935SRoman Volkov ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
80bed61935SRoman Volkov OXYGEN_SPI_DATA_LENGTH_2 |
81bed61935SRoman Volkov OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
82bed61935SRoman Volkov OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
83bed61935SRoman Volkov ((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);
84bed61935SRoman Volkov if (ret < 0)
85bed61935SRoman Volkov return ret;
86bed61935SRoman Volkov
87bed61935SRoman Volkov ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
88bed61935SRoman Volkov OXYGEN_SPI_DATA_LENGTH_2 |
89bed61935SRoman Volkov OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
90bed61935SRoman Volkov OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
91bed61935SRoman Volkov (CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);
92bed61935SRoman Volkov if (ret < 0)
93bed61935SRoman Volkov return ret;
94bed61935SRoman Volkov
95bed61935SRoman Volkov data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);
96bed61935SRoman Volkov
97bed61935SRoman Volkov return 0;
98bed61935SRoman Volkov }
99bed61935SRoman Volkov
cs4245_shadow_control(struct oxygen * chip,enum cs4245_shadow_operation op)100bed61935SRoman Volkov int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
101bed61935SRoman Volkov {
102bed61935SRoman Volkov struct dg *data = chip->model_data;
103bed61935SRoman Volkov unsigned char addr;
104bed61935SRoman Volkov int ret;
105bed61935SRoman Volkov
106bed61935SRoman Volkov for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
107bed61935SRoman Volkov ret = (op == CS4245_SAVE_TO_SHADOW ?
108bed61935SRoman Volkov cs4245_read_spi(chip, addr) :
109bed61935SRoman Volkov cs4245_write_spi(chip, addr));
110bed61935SRoman Volkov if (ret < 0)
111bed61935SRoman Volkov return ret;
112bed61935SRoman Volkov }
113bed61935SRoman Volkov return 0;
114bed61935SRoman Volkov }
115bed61935SRoman Volkov
cs4245_init(struct oxygen * chip)11666410bfdSClemens Ladisch static void cs4245_init(struct oxygen *chip)
11766410bfdSClemens Ladisch {
11866410bfdSClemens Ladisch struct dg *data = chip->model_data;
11966410bfdSClemens Ladisch
1203c1611ddSRoman Volkov /* save the initial state: codec version, registers */
1213c1611ddSRoman Volkov cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
12266410bfdSClemens Ladisch
1233c1611ddSRoman Volkov /*
1243c1611ddSRoman Volkov * Power up the CODEC internals, enable soft ramp & zero cross, work in
1253c1611ddSRoman Volkov * async. mode, enable aux output from DAC. Invert DAC output as in the
1263c1611ddSRoman Volkov * Windows driver.
1273c1611ddSRoman Volkov */
1283c1611ddSRoman Volkov data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
1293c1611ddSRoman Volkov data->cs4245_shadow[CS4245_SIGNAL_SEL] =
1303c1611ddSRoman Volkov CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
1313c1611ddSRoman Volkov data->cs4245_shadow[CS4245_DAC_CTRL_1] =
1323c1611ddSRoman Volkov CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
1333c1611ddSRoman Volkov data->cs4245_shadow[CS4245_DAC_CTRL_2] =
1343c1611ddSRoman Volkov CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
1353c1611ddSRoman Volkov data->cs4245_shadow[CS4245_ADC_CTRL] =
1363c1611ddSRoman Volkov CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
1373c1611ddSRoman Volkov data->cs4245_shadow[CS4245_ANALOG_IN] =
1383c1611ddSRoman Volkov CS4245_PGA_SOFT | CS4245_PGA_ZERO;
1393c1611ddSRoman Volkov data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
1403c1611ddSRoman Volkov data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
1413f49a66fSRoman Volkov data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
1423f49a66fSRoman Volkov data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
1433c1611ddSRoman Volkov
1443c1611ddSRoman Volkov cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
1453c1611ddSRoman Volkov snd_component_add(chip->card, "CS4245");
14666410bfdSClemens Ladisch }
14766410bfdSClemens Ladisch
dg_init(struct oxygen * chip)148041f26b6SRoman Volkov void dg_init(struct oxygen *chip)
14966410bfdSClemens Ladisch {
15066410bfdSClemens Ladisch struct dg *data = chip->model_data;
15166410bfdSClemens Ladisch
1523f49a66fSRoman Volkov data->output_sel = PLAYBACK_DST_HP_FP;
1533f49a66fSRoman Volkov data->input_sel = CAPTURE_SRC_MIC;
15466410bfdSClemens Ladisch
15566410bfdSClemens Ladisch cs4245_init(chip);
1563c1611ddSRoman Volkov oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
1573c1611ddSRoman Volkov GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
1583f49a66fSRoman Volkov /* anti-pop delay, wait some time before enabling the output */
1593f49a66fSRoman Volkov msleep(2500);
1603c1611ddSRoman Volkov oxygen_write16(chip, OXYGEN_GPIO_DATA,
1613c1611ddSRoman Volkov GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
16266410bfdSClemens Ladisch }
16366410bfdSClemens Ladisch
dg_cleanup(struct oxygen * chip)164041f26b6SRoman Volkov void dg_cleanup(struct oxygen *chip)
16566410bfdSClemens Ladisch {
16666410bfdSClemens Ladisch oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
16766410bfdSClemens Ladisch }
16866410bfdSClemens Ladisch
dg_suspend(struct oxygen * chip)169041f26b6SRoman Volkov void dg_suspend(struct oxygen *chip)
17066410bfdSClemens Ladisch {
17166410bfdSClemens Ladisch dg_cleanup(chip);
17266410bfdSClemens Ladisch }
17366410bfdSClemens Ladisch
dg_resume(struct oxygen * chip)174041f26b6SRoman Volkov void dg_resume(struct oxygen *chip)
17566410bfdSClemens Ladisch {
1763c1611ddSRoman Volkov cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
1773c1611ddSRoman Volkov msleep(2500);
1783c1611ddSRoman Volkov oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
17966410bfdSClemens Ladisch }
18066410bfdSClemens Ladisch
set_cs4245_dac_params(struct oxygen * chip,struct snd_pcm_hw_params * params)181041f26b6SRoman Volkov void set_cs4245_dac_params(struct oxygen *chip,
18266410bfdSClemens Ladisch struct snd_pcm_hw_params *params)
18366410bfdSClemens Ladisch {
18466410bfdSClemens Ladisch struct dg *data = chip->model_data;
185fddc106bSRoman Volkov unsigned char dac_ctrl;
186fddc106bSRoman Volkov unsigned char mclk_freq;
18766410bfdSClemens Ladisch
188fddc106bSRoman Volkov dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
189fddc106bSRoman Volkov mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
190fddc106bSRoman Volkov if (params_rate(params) <= 50000) {
191fddc106bSRoman Volkov dac_ctrl |= CS4245_DAC_FM_SINGLE;
192fddc106bSRoman Volkov mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
193fddc106bSRoman Volkov } else if (params_rate(params) <= 100000) {
194fddc106bSRoman Volkov dac_ctrl |= CS4245_DAC_FM_DOUBLE;
195fddc106bSRoman Volkov mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
196fddc106bSRoman Volkov } else {
197fddc106bSRoman Volkov dac_ctrl |= CS4245_DAC_FM_QUAD;
198fddc106bSRoman Volkov mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
199fddc106bSRoman Volkov }
200fddc106bSRoman Volkov data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
201fddc106bSRoman Volkov data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
202fddc106bSRoman Volkov cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
203fddc106bSRoman Volkov cs4245_write_spi(chip, CS4245_MCLK_FREQ);
20466410bfdSClemens Ladisch }
20566410bfdSClemens Ladisch
set_cs4245_adc_params(struct oxygen * chip,struct snd_pcm_hw_params * params)206041f26b6SRoman Volkov void set_cs4245_adc_params(struct oxygen *chip,
20766410bfdSClemens Ladisch struct snd_pcm_hw_params *params)
20866410bfdSClemens Ladisch {
20966410bfdSClemens Ladisch struct dg *data = chip->model_data;
210fddc106bSRoman Volkov unsigned char adc_ctrl;
211fddc106bSRoman Volkov unsigned char mclk_freq;
21266410bfdSClemens Ladisch
213fddc106bSRoman Volkov adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
214fddc106bSRoman Volkov mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
215fddc106bSRoman Volkov if (params_rate(params) <= 50000) {
216fddc106bSRoman Volkov adc_ctrl |= CS4245_ADC_FM_SINGLE;
217fddc106bSRoman Volkov mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
218fddc106bSRoman Volkov } else if (params_rate(params) <= 100000) {
219fddc106bSRoman Volkov adc_ctrl |= CS4245_ADC_FM_DOUBLE;
220fddc106bSRoman Volkov mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
221fddc106bSRoman Volkov } else {
222fddc106bSRoman Volkov adc_ctrl |= CS4245_ADC_FM_QUAD;
223fddc106bSRoman Volkov mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
224fddc106bSRoman Volkov }
225fddc106bSRoman Volkov data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
226fddc106bSRoman Volkov data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
227fddc106bSRoman Volkov cs4245_write_spi(chip, CS4245_ADC_CTRL);
228fddc106bSRoman Volkov cs4245_write_spi(chip, CS4245_MCLK_FREQ);
22966410bfdSClemens Ladisch }
23066410bfdSClemens Ladisch
shift_bits(unsigned int value,unsigned int shift_from,unsigned int shift_to,unsigned int mask)23130556441SClemens Ladisch static inline unsigned int shift_bits(unsigned int value,
23230556441SClemens Ladisch unsigned int shift_from,
23330556441SClemens Ladisch unsigned int shift_to,
23430556441SClemens Ladisch unsigned int mask)
23530556441SClemens Ladisch {
23630556441SClemens Ladisch if (shift_from < shift_to)
23730556441SClemens Ladisch return (value << (shift_to - shift_from)) & mask;
23830556441SClemens Ladisch else
23930556441SClemens Ladisch return (value >> (shift_from - shift_to)) & mask;
24030556441SClemens Ladisch }
24130556441SClemens Ladisch
adjust_dg_dac_routing(struct oxygen * chip,unsigned int play_routing)242041f26b6SRoman Volkov unsigned int adjust_dg_dac_routing(struct oxygen *chip,
243efbeb071SClemens Ladisch unsigned int play_routing)
244efbeb071SClemens Ladisch {
2451f91ecc1SRoman Volkov struct dg *data = chip->model_data;
2461f91ecc1SRoman Volkov
2472809cb84SRoman Volkov switch (data->output_sel) {
2481f91ecc1SRoman Volkov case PLAYBACK_DST_HP:
2491f91ecc1SRoman Volkov case PLAYBACK_DST_HP_FP:
2501f91ecc1SRoman Volkov oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
2511f91ecc1SRoman Volkov OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
2521f91ecc1SRoman Volkov OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
2531f91ecc1SRoman Volkov break;
2541f91ecc1SRoman Volkov case PLAYBACK_DST_MULTICH:
2551f91ecc1SRoman Volkov oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
2561f91ecc1SRoman Volkov OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
2571f91ecc1SRoman Volkov break;
2581f91ecc1SRoman Volkov }
25930556441SClemens Ladisch return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
26030556441SClemens Ladisch shift_bits(play_routing,
26130556441SClemens Ladisch OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
26230556441SClemens Ladisch OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
26330556441SClemens Ladisch OXYGEN_PLAY_DAC1_SOURCE_MASK) |
26430556441SClemens Ladisch shift_bits(play_routing,
26530556441SClemens Ladisch OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
26630556441SClemens Ladisch OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
26730556441SClemens Ladisch OXYGEN_PLAY_DAC2_SOURCE_MASK) |
26830556441SClemens Ladisch shift_bits(play_routing,
26930556441SClemens Ladisch OXYGEN_PLAY_DAC0_SOURCE_SHIFT,
27030556441SClemens Ladisch OXYGEN_PLAY_DAC3_SOURCE_SHIFT,
27130556441SClemens Ladisch OXYGEN_PLAY_DAC3_SOURCE_MASK);
272efbeb071SClemens Ladisch }
273efbeb071SClemens Ladisch
dump_cs4245_registers(struct oxygen * chip,struct snd_info_buffer * buffer)274041f26b6SRoman Volkov void dump_cs4245_registers(struct oxygen *chip,
27566410bfdSClemens Ladisch struct snd_info_buffer *buffer)
27666410bfdSClemens Ladisch {
27766410bfdSClemens Ladisch struct dg *data = chip->model_data;
27806f70d0dSRoman Volkov unsigned int addr;
27966410bfdSClemens Ladisch
28066410bfdSClemens Ladisch snd_iprintf(buffer, "\nCS4245:");
28106f70d0dSRoman Volkov cs4245_read_spi(chip, CS4245_INT_STATUS);
28206f70d0dSRoman Volkov for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
28306f70d0dSRoman Volkov snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
28466410bfdSClemens Ladisch snd_iprintf(buffer, "\n");
28566410bfdSClemens Ladisch }
286