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