Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/pci/oxygen/xonar_dg.c
26442 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* card driver for the Xonar DG/DGX
4
*
5
* Copyright (c) Clemens Ladisch <[email protected]>
6
* Copyright (c) Roman Volkov <[email protected]>
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
57
int 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
74
int 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
100
int 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
116
static 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
148
void 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
164
void dg_cleanup(struct oxygen *chip)
165
{
166
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
167
}
168
169
void dg_suspend(struct oxygen *chip)
170
{
171
dg_cleanup(chip);
172
}
173
174
void 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
181
void 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
206
void 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
231
static 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
242
unsigned 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
274
void 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
}
286
287