Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/pci/oxygen/xonar_dg_mixer.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Mixer controls for the Xonar DG/DGX
4
*
5
* Copyright (c) Clemens Ladisch <[email protected]>
6
* Copyright (c) Roman Volkov <[email protected]>
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
458