1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Mixer controls 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 #include <linux/pci.h>
10 #include <linux/delay.h>
11 #include <sound/control.h>
12 #include <sound/core.h>
13 #include <sound/info.h>
14 #include <sound/pcm.h>
15 #include <sound/tlv.h>
16 #include "oxygen.h"
17 #include "xonar_dg.h"
18 #include "cs4245.h"
19
20 /* analog output select */
21
output_select_apply(struct oxygen * chip)22 static int output_select_apply(struct oxygen *chip)
23 {
24 struct dg *data = chip->model_data;
25
26 data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK;
27 if (data->output_sel == PLAYBACK_DST_HP) {
28 /* mute FP (aux output) amplifier, switch rear jack to CS4245 */
29 oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
30 } else if (data->output_sel == PLAYBACK_DST_HP_FP) {
31 /*
32 * Unmute FP amplifier, switch rear jack to CS4361;
33 * I2S channels 2,3,4 should be inactive.
34 */
35 oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
36 data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC;
37 } else {
38 /*
39 * 2.0, 4.0, 5.1: switch to CS4361, mute FP amp.,
40 * and change playback routing.
41 */
42 oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
43 }
44 return cs4245_write_spi(chip, CS4245_SIGNAL_SEL);
45 }
46
output_select_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)47 static int output_select_info(struct snd_kcontrol *ctl,
48 struct snd_ctl_elem_info *info)
49 {
50 static const char *const names[3] = {
51 "Stereo Headphones",
52 "Stereo Headphones FP",
53 "Multichannel",
54 };
55
56 return snd_ctl_enum_info(info, 1, 3, names);
57 }
58
output_select_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)59 static int output_select_get(struct snd_kcontrol *ctl,
60 struct snd_ctl_elem_value *value)
61 {
62 struct oxygen *chip = ctl->private_data;
63 struct dg *data = chip->model_data;
64
65 guard(mutex)(&chip->mutex);
66 value->value.enumerated.item[0] = data->output_sel;
67 return 0;
68 }
69
output_select_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)70 static int output_select_put(struct snd_kcontrol *ctl,
71 struct snd_ctl_elem_value *value)
72 {
73 struct oxygen *chip = ctl->private_data;
74 struct dg *data = chip->model_data;
75 unsigned int new = value->value.enumerated.item[0];
76 int changed = 0;
77 int ret;
78
79 guard(mutex)(&chip->mutex);
80 if (data->output_sel != new) {
81 data->output_sel = new;
82 ret = output_select_apply(chip);
83 changed = ret >= 0 ? 1 : ret;
84 oxygen_update_dac_routing(chip);
85 }
86
87 return changed;
88 }
89
90 /* CS4245 Headphone Channels A&B Volume Control */
91
hp_stereo_volume_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)92 static int hp_stereo_volume_info(struct snd_kcontrol *ctl,
93 struct snd_ctl_elem_info *info)
94 {
95 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
96 info->count = 2;
97 info->value.integer.min = 0;
98 info->value.integer.max = 255;
99 return 0;
100 }
101
hp_stereo_volume_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * val)102 static int hp_stereo_volume_get(struct snd_kcontrol *ctl,
103 struct snd_ctl_elem_value *val)
104 {
105 struct oxygen *chip = ctl->private_data;
106 struct dg *data = chip->model_data;
107 unsigned int tmp;
108
109 guard(mutex)(&chip->mutex);
110 tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255;
111 val->value.integer.value[0] = tmp;
112 tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255;
113 val->value.integer.value[1] = tmp;
114 return 0;
115 }
116
hp_stereo_volume_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * val)117 static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
118 struct snd_ctl_elem_value *val)
119 {
120 struct oxygen *chip = ctl->private_data;
121 struct dg *data = chip->model_data;
122 int ret;
123 int changed = 0;
124 long new1 = val->value.integer.value[0];
125 long new2 = val->value.integer.value[1];
126
127 if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
128 return -EINVAL;
129
130 guard(mutex)(&chip->mutex);
131 if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) ||
132 (data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) {
133 data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1;
134 data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2;
135 ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL);
136 if (ret >= 0)
137 ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
138 changed = ret >= 0 ? 1 : ret;
139 }
140
141 return changed;
142 }
143
144 /* Headphone Mute */
145
hp_mute_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * val)146 static int hp_mute_get(struct snd_kcontrol *ctl,
147 struct snd_ctl_elem_value *val)
148 {
149 struct oxygen *chip = ctl->private_data;
150 struct dg *data = chip->model_data;
151
152 guard(mutex)(&chip->mutex);
153 val->value.integer.value[0] =
154 !(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
155 return 0;
156 }
157
hp_mute_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * val)158 static int hp_mute_put(struct snd_kcontrol *ctl,
159 struct snd_ctl_elem_value *val)
160 {
161 struct oxygen *chip = ctl->private_data;
162 struct dg *data = chip->model_data;
163 int ret;
164 int changed;
165
166 if (val->value.integer.value[0] > 1)
167 return -EINVAL;
168 guard(mutex)(&chip->mutex);
169 data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC;
170 data->cs4245_shadow[CS4245_DAC_CTRL_1] |=
171 (~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC;
172 ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
173 changed = ret >= 0 ? 1 : ret;
174 return changed;
175 }
176
177 /* capture volume for all sources */
178
input_volume_apply(struct oxygen * chip,char left,char right)179 static int input_volume_apply(struct oxygen *chip, char left, char right)
180 {
181 struct dg *data = chip->model_data;
182 int ret;
183
184 data->cs4245_shadow[CS4245_PGA_A_CTRL] = left;
185 data->cs4245_shadow[CS4245_PGA_B_CTRL] = right;
186 ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL);
187 if (ret < 0)
188 return ret;
189 return cs4245_write_spi(chip, CS4245_PGA_B_CTRL);
190 }
191
input_vol_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)192 static int input_vol_info(struct snd_kcontrol *ctl,
193 struct snd_ctl_elem_info *info)
194 {
195 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
196 info->count = 2;
197 info->value.integer.min = 2 * -12;
198 info->value.integer.max = 2 * 12;
199 return 0;
200 }
201
input_vol_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)202 static int input_vol_get(struct snd_kcontrol *ctl,
203 struct snd_ctl_elem_value *value)
204 {
205 struct oxygen *chip = ctl->private_data;
206 struct dg *data = chip->model_data;
207 unsigned int idx = ctl->private_value;
208
209 guard(mutex)(&chip->mutex);
210 value->value.integer.value[0] = data->input_vol[idx][0];
211 value->value.integer.value[1] = data->input_vol[idx][1];
212 return 0;
213 }
214
input_vol_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)215 static int input_vol_put(struct snd_kcontrol *ctl,
216 struct snd_ctl_elem_value *value)
217 {
218 struct oxygen *chip = ctl->private_data;
219 struct dg *data = chip->model_data;
220 unsigned int idx = ctl->private_value;
221 int changed = 0;
222 int ret = 0;
223
224 if (value->value.integer.value[0] < 2 * -12 ||
225 value->value.integer.value[0] > 2 * 12 ||
226 value->value.integer.value[1] < 2 * -12 ||
227 value->value.integer.value[1] > 2 * 12)
228 return -EINVAL;
229 guard(mutex)(&chip->mutex);
230 changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
231 data->input_vol[idx][1] != value->value.integer.value[1];
232 if (changed) {
233 data->input_vol[idx][0] = value->value.integer.value[0];
234 data->input_vol[idx][1] = value->value.integer.value[1];
235 if (idx == data->input_sel) {
236 ret = input_volume_apply(chip,
237 data->input_vol[idx][0],
238 data->input_vol[idx][1]);
239 }
240 changed = ret >= 0 ? 1 : ret;
241 }
242 return changed;
243 }
244
245 /* Capture Source */
246
input_source_apply(struct oxygen * chip)247 static int input_source_apply(struct oxygen *chip)
248 {
249 struct dg *data = chip->model_data;
250
251 data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK;
252 if (data->input_sel == CAPTURE_SRC_FP_MIC)
253 data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2;
254 else if (data->input_sel == CAPTURE_SRC_LINE)
255 data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4;
256 else if (data->input_sel != CAPTURE_SRC_MIC)
257 data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1;
258 return cs4245_write_spi(chip, CS4245_ANALOG_IN);
259 }
260
input_sel_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)261 static int input_sel_info(struct snd_kcontrol *ctl,
262 struct snd_ctl_elem_info *info)
263 {
264 static const char *const names[4] = {
265 "Mic", "Front Mic", "Line", "Aux"
266 };
267
268 return snd_ctl_enum_info(info, 1, 4, names);
269 }
270
input_sel_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)271 static int input_sel_get(struct snd_kcontrol *ctl,
272 struct snd_ctl_elem_value *value)
273 {
274 struct oxygen *chip = ctl->private_data;
275 struct dg *data = chip->model_data;
276
277 guard(mutex)(&chip->mutex);
278 value->value.enumerated.item[0] = data->input_sel;
279 return 0;
280 }
281
input_sel_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)282 static int input_sel_put(struct snd_kcontrol *ctl,
283 struct snd_ctl_elem_value *value)
284 {
285 struct oxygen *chip = ctl->private_data;
286 struct dg *data = chip->model_data;
287 int changed;
288 int ret;
289
290 if (value->value.enumerated.item[0] > 3)
291 return -EINVAL;
292
293 guard(mutex)(&chip->mutex);
294 changed = value->value.enumerated.item[0] != data->input_sel;
295 if (changed) {
296 data->input_sel = value->value.enumerated.item[0];
297
298 ret = input_source_apply(chip);
299 if (ret >= 0)
300 ret = input_volume_apply(chip,
301 data->input_vol[data->input_sel][0],
302 data->input_vol[data->input_sel][1]);
303 changed = ret >= 0 ? 1 : ret;
304 }
305 return changed;
306 }
307
308 /* ADC high-pass filter */
309
hpf_info(struct snd_kcontrol * ctl,struct snd_ctl_elem_info * info)310 static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
311 {
312 static const char *const names[2] = { "Active", "Frozen" };
313
314 return snd_ctl_enum_info(info, 1, 2, names);
315 }
316
hpf_get(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)317 static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
318 {
319 struct oxygen *chip = ctl->private_data;
320 struct dg *data = chip->model_data;
321
322 value->value.enumerated.item[0] =
323 !!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
324 return 0;
325 }
326
hpf_put(struct snd_kcontrol * ctl,struct snd_ctl_elem_value * value)327 static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
328 {
329 struct oxygen *chip = ctl->private_data;
330 struct dg *data = chip->model_data;
331 u8 reg;
332 int changed;
333
334 guard(mutex)(&chip->mutex);
335 reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
336 if (value->value.enumerated.item[0])
337 reg |= CS4245_HPF_FREEZE;
338 changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
339 if (changed) {
340 data->cs4245_shadow[CS4245_ADC_CTRL] = reg;
341 cs4245_write_spi(chip, CS4245_ADC_CTRL);
342 }
343 return changed;
344 }
345
346 #define INPUT_VOLUME(xname, index) { \
347 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
348 .name = xname, \
349 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
350 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
351 .info = input_vol_info, \
352 .get = input_vol_get, \
353 .put = input_vol_put, \
354 .tlv = { .p = pga_db_scale }, \
355 .private_value = index, \
356 }
357 static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
358 static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200);
359 static const struct snd_kcontrol_new dg_controls[] = {
360 {
361 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
362 .name = "Analog Output Playback Enum",
363 .info = output_select_info,
364 .get = output_select_get,
365 .put = output_select_put,
366 },
367 {
368 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
369 .name = "Headphone Playback Volume",
370 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
371 SNDRV_CTL_ELEM_ACCESS_TLV_READ,
372 .info = hp_stereo_volume_info,
373 .get = hp_stereo_volume_get,
374 .put = hp_stereo_volume_put,
375 .tlv = { .p = hp_db_scale, },
376 },
377 {
378 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
379 .name = "Headphone Playback Switch",
380 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
381 .info = snd_ctl_boolean_mono_info,
382 .get = hp_mute_get,
383 .put = hp_mute_put,
384 },
385 INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC),
386 INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC),
387 INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE),
388 INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX),
389 {
390 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
391 .name = "Capture Source",
392 .info = input_sel_info,
393 .get = input_sel_get,
394 .put = input_sel_put,
395 },
396 {
397 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
398 .name = "ADC High-pass Filter Capture Enum",
399 .info = hpf_info,
400 .get = hpf_get,
401 .put = hpf_put,
402 },
403 };
404
dg_control_filter(struct snd_kcontrol_new * template)405 static int dg_control_filter(struct snd_kcontrol_new *template)
406 {
407 if (!strncmp(template->name, "Master Playback ", 16))
408 return 1;
409 return 0;
410 }
411
dg_mixer_init(struct oxygen * chip)412 static int dg_mixer_init(struct oxygen *chip)
413 {
414 unsigned int i;
415 int err;
416
417 output_select_apply(chip);
418 input_source_apply(chip);
419 oxygen_update_dac_routing(chip);
420
421 for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
422 err = snd_ctl_add(chip->card,
423 snd_ctl_new1(&dg_controls[i], chip));
424 if (err < 0)
425 return err;
426 }
427
428 return 0;
429 }
430
431 const struct oxygen_model model_xonar_dg = {
432 .longname = "C-Media Oxygen HD Audio",
433 .chip = "CMI8786",
434 .init = dg_init,
435 .control_filter = dg_control_filter,
436 .mixer_init = dg_mixer_init,
437 .cleanup = dg_cleanup,
438 .suspend = dg_suspend,
439 .resume = dg_resume,
440 .set_dac_params = set_cs4245_dac_params,
441 .set_adc_params = set_cs4245_adc_params,
442 .adjust_dac_routing = adjust_dg_dac_routing,
443 .dump_registers = dump_cs4245_registers,
444 .model_data_size = sizeof(struct dg),
445 .device_config = PLAYBACK_0_TO_I2S |
446 PLAYBACK_1_TO_SPDIF |
447 CAPTURE_0_FROM_I2S_1 |
448 CAPTURE_1_FROM_SPDIF,
449 .dac_channels_pcm = 6,
450 .dac_channels_mixer = 0,
451 .function_flags = OXYGEN_FUNCTION_SPI,
452 .dac_mclks = OXYGEN_MCLKS(256, 128, 128),
453 .adc_mclks = OXYGEN_MCLKS(256, 128, 128),
454 .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
455 .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
456 };
457