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
22static 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
47static 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, channels: 1, items: 3, names);
57}
58
59static 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)(T: &chip->mutex);
66 value->value.enumerated.item[0] = data->output_sel;
67 return 0;
68}
69
70static 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)(T: &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
92static 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
102static 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)(T: &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
117static 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)(T: &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
146static 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)(T: &chip->mutex);
153 val->value.integer.value[0] =
154 !(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
155 return 0;
156}
157
158static 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)(T: &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
179static 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
192static 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
202static 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)(T: &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
215static 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)(T: &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 left: data->input_vol[idx][0],
238 right: data->input_vol[idx][1]);
239 }
240 changed = ret >= 0 ? 1 : ret;
241 }
242 return changed;
243}
244
245/* Capture Source */
246
247static 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
261static 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, channels: 1, items: 4, names);
269}
270
271static 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)(T: &chip->mutex);
278 value->value.enumerated.item[0] = data->input_sel;
279 return 0;
280}
281
282static 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)(T: &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 left: data->input_vol[data->input_sel][0],
302 right: 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
310static 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, channels: 1, items: 2, names);
315}
316
317static 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
327static 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)(T: &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}
357static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
358static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200);
359static 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
405static 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
412static 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(card: chip->card,
423 kcontrol: snd_ctl_new1(kcontrolnew: &dg_controls[i], private_data: chip));
424 if (err < 0)
425 return err;
426 }
427
428 return 0;
429}
430
431const 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

source code of linux/sound/pci/oxygen/xonar_dg_mixer.c