1/***************************************************************************
2			  msnd_pinnacle_mixer.c  -  description
3			     -------------------
4    begin		: Fre Jun 7 2002
5    copyright 		: (C) 2002 by karsten wiese
6    email		: annabellesgarden@yahoo.de
7 ***************************************************************************/
8
9/***************************************************************************
10 *							      		   *
11 *   This program is free software; you can redistribute it and/or modify  *
12 *   it under the terms of the GNU General Public License as published by  *
13 *   the Free Software Foundation; either version 2 of the License, or     *
14 *   (at your option) any later version.				   *
15 *									   *
16 ***************************************************************************/
17
18#include <linux/io.h>
19#include <linux/export.h>
20
21#include <sound/core.h>
22#include <sound/control.h>
23#include "msnd.h"
24#include "msnd_pinnacle.h"
25
26
27#define MSND_MIXER_VOLUME	0
28#define MSND_MIXER_PCM		1
29#define MSND_MIXER_AUX		2	/* Input source 1  (aux1) */
30#define MSND_MIXER_IMIX		3	/*  Recording monitor  */
31#define MSND_MIXER_SYNTH	4
32#define MSND_MIXER_SPEAKER	5
33#define MSND_MIXER_LINE		6
34#define MSND_MIXER_MIC		7
35#define MSND_MIXER_RECLEV	11	/* Recording level */
36#define MSND_MIXER_IGAIN	12	/* Input gain */
37#define MSND_MIXER_OGAIN	13	/* Output gain */
38#define MSND_MIXER_DIGITAL	17	/* Digital (input) 1 */
39
40/*	Device mask bits	*/
41
42#define MSND_MASK_VOLUME	(1 << MSND_MIXER_VOLUME)
43#define MSND_MASK_SYNTH		(1 << MSND_MIXER_SYNTH)
44#define MSND_MASK_PCM		(1 << MSND_MIXER_PCM)
45#define MSND_MASK_SPEAKER	(1 << MSND_MIXER_SPEAKER)
46#define MSND_MASK_LINE		(1 << MSND_MIXER_LINE)
47#define MSND_MASK_MIC		(1 << MSND_MIXER_MIC)
48#define MSND_MASK_IMIX		(1 << MSND_MIXER_IMIX)
49#define MSND_MASK_RECLEV	(1 << MSND_MIXER_RECLEV)
50#define MSND_MASK_IGAIN		(1 << MSND_MIXER_IGAIN)
51#define MSND_MASK_OGAIN		(1 << MSND_MIXER_OGAIN)
52#define MSND_MASK_AUX		(1 << MSND_MIXER_AUX)
53#define MSND_MASK_DIGITAL	(1 << MSND_MIXER_DIGITAL)
54
55static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,
56				struct snd_ctl_elem_info *uinfo)
57{
58	static const char * const texts[3] = {
59		"Analog", "MASS", "SPDIF",
60	};
61	struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
62	unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;
63
64	return snd_ctl_enum_info(uinfo, 1, items, texts);
65}
66
67static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,
68				struct snd_ctl_elem_value *ucontrol)
69{
70	struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
71	/* MSND_MASK_IMIX is the default */
72	ucontrol->value.enumerated.item[0] = 0;
73
74	if (chip->recsrc & MSND_MASK_SYNTH) {
75		ucontrol->value.enumerated.item[0] = 1;
76	} else if ((chip->recsrc & MSND_MASK_DIGITAL) &&
77		 test_bit(F_HAVEDIGITAL, &chip->flags)) {
78		ucontrol->value.enumerated.item[0] = 2;
79	}
80
81
82	return 0;
83}
84
85static int snd_msndmix_set_mux(struct snd_msnd *chip, int val)
86{
87	unsigned newrecsrc;
88	int change;
89	unsigned char msndbyte;
90
91	switch (val) {
92	case 0:
93		newrecsrc = MSND_MASK_IMIX;
94		msndbyte = HDEXAR_SET_ANA_IN;
95		break;
96	case 1:
97		newrecsrc = MSND_MASK_SYNTH;
98		msndbyte = HDEXAR_SET_SYNTH_IN;
99		break;
100	case 2:
101		newrecsrc = MSND_MASK_DIGITAL;
102		msndbyte = HDEXAR_SET_DAT_IN;
103		break;
104	default:
105		return -EINVAL;
106	}
107	change  = newrecsrc != chip->recsrc;
108	if (change) {
109		change = 0;
110		if (!snd_msnd_send_word(chip, 0, 0, msndbyte))
111			if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) {
112				chip->recsrc = newrecsrc;
113				change = 1;
114			}
115	}
116	return change;
117}
118
119static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol,
120				struct snd_ctl_elem_value *ucontrol)
121{
122	struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
123	return snd_msndmix_set_mux(msnd, ucontrol->value.enumerated.item[0]);
124}
125
126
127static int snd_msndmix_volume_info(struct snd_kcontrol *kcontrol,
128				   struct snd_ctl_elem_info *uinfo)
129{
130	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
131	uinfo->count = 2;
132	uinfo->value.integer.min = 0;
133	uinfo->value.integer.max = 100;
134	return 0;
135}
136
137static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol,
138				  struct snd_ctl_elem_value *ucontrol)
139{
140	struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
141	int addr = kcontrol->private_value;
142	unsigned long flags;
143
144	spin_lock_irqsave(&msnd->mixer_lock, flags);
145	ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100;
146	ucontrol->value.integer.value[0] /= 0xFFFF;
147	ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100;
148	ucontrol->value.integer.value[1] /= 0xFFFF;
149	spin_unlock_irqrestore(&msnd->mixer_lock, flags);
150	return 0;
151}
152
153#define update_volm(a, b)						\
154	do {								\
155		writew((dev->left_levels[a] >> 1) *			\
156		       readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff,	\
157		       dev->SMA + SMA_##b##Left);			\
158		writew((dev->right_levels[a] >> 1)  *			\
159		       readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
160		       dev->SMA + SMA_##b##Right);			\
161	} while (0);
162
163#define update_potm(d, s, ar)						\
164	do {								\
165		writeb((dev->left_levels[d] >> 8) *			\
166		       readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
167		       dev->SMA + SMA_##s##Left);			\
168		writeb((dev->right_levels[d] >> 8) *			\
169		       readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
170		       dev->SMA + SMA_##s##Right);			\
171		if (snd_msnd_send_word(dev, 0, 0, ar) == 0)		\
172			snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);	\
173	} while (0);
174
175#define update_pot(d, s, ar)						\
176	do {								\
177		writeb(dev->left_levels[d] >> 8,			\
178		       dev->SMA + SMA_##s##Left);			\
179		writeb(dev->right_levels[d] >> 8,			\
180		       dev->SMA + SMA_##s##Right);			\
181		if (snd_msnd_send_word(dev, 0, 0, ar) == 0)		\
182			snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);	\
183	} while (0);
184
185
186static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right)
187{
188	int bLeft, bRight;
189	int wLeft, wRight;
190	int updatemaster = 0;
191
192	if (d >= LEVEL_ENTRIES)
193		return -EINVAL;
194
195	bLeft = left * 0xff / 100;
196	wLeft = left * 0xffff / 100;
197
198	bRight = right * 0xff / 100;
199	wRight = right * 0xffff / 100;
200
201	dev->left_levels[d] = wLeft;
202	dev->right_levels[d] = wRight;
203
204	switch (d) {
205		/* master volume unscaled controls */
206	case MSND_MIXER_LINE:			/* line pot control */
207		/* scaled by IMIX in digital mix */
208		writeb(bLeft, dev->SMA + SMA_bInPotPosLeft);
209		writeb(bRight, dev->SMA + SMA_bInPotPosRight);
210		if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
211			snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
212		break;
213	case MSND_MIXER_MIC:			/* mic pot control */
214		if (dev->type == msndClassic)
215			return -EINVAL;
216		/* scaled by IMIX in digital mix */
217		writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft);
218		writeb(bRight, dev->SMA + SMA_bMicPotPosRight);
219		if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
220			snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
221		break;
222	case MSND_MIXER_VOLUME:		/* master volume */
223		writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft);
224		writew(wRight, dev->SMA + SMA_wCurrMastVolRight);
225		/* fall through */
226
227	case MSND_MIXER_AUX:			/* aux pot control */
228		/* scaled by master volume */
229		/* fall through */
230
231		/* digital controls */
232	case MSND_MIXER_SYNTH:			/* synth vol (dsp mix) */
233	case MSND_MIXER_PCM:			/* pcm vol (dsp mix) */
234	case MSND_MIXER_IMIX:			/* input monitor (dsp mix) */
235		/* scaled by master volume */
236		updatemaster = 1;
237		break;
238
239	default:
240		return -EINVAL;
241	}
242
243	if (updatemaster) {
244		/* update master volume scaled controls */
245		update_volm(MSND_MIXER_PCM, wCurrPlayVol);
246		update_volm(MSND_MIXER_IMIX, wCurrInVol);
247		if (dev->type == msndPinnacle)
248			update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
249		update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
250	}
251
252	return 0;
253}
254
255static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
256				  struct snd_ctl_elem_value *ucontrol)
257{
258	struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
259	int change, addr = kcontrol->private_value;
260	int left, right;
261	unsigned long flags;
262
263	left = ucontrol->value.integer.value[0] % 101;
264	right = ucontrol->value.integer.value[1] % 101;
265	spin_lock_irqsave(&msnd->mixer_lock, flags);
266	change = msnd->left_levels[addr] != left
267		|| msnd->right_levels[addr] != right;
268	snd_msndmix_set(msnd, addr, left, right);
269	spin_unlock_irqrestore(&msnd->mixer_lock, flags);
270	return change;
271}
272
273
274#define DUMMY_VOLUME(xname, xindex, addr) \
275{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
276  .info = snd_msndmix_volume_info, \
277  .get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \
278  .private_value = addr }
279
280
281static struct snd_kcontrol_new snd_msnd_controls[] = {
282DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),
283DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),
284DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),
285DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE),
286DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC),
287DUMMY_VOLUME("Monitor",	0, MSND_MIXER_IMIX),
288{
289	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
290	.name = "Capture Source",
291	.info = snd_msndmix_info_mux,
292	.get = snd_msndmix_get_mux,
293	.put = snd_msndmix_put_mux,
294}
295};
296
297
298int snd_msndmix_new(struct snd_card *card)
299{
300	struct snd_msnd *chip = card->private_data;
301	unsigned int idx;
302	int err;
303
304	if (snd_BUG_ON(!chip))
305		return -EINVAL;
306	spin_lock_init(&chip->mixer_lock);
307	strcpy(card->mixername, "MSND Pinnacle Mixer");
308
309	for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) {
310		err = snd_ctl_add(card,
311				  snd_ctl_new1(snd_msnd_controls + idx, chip));
312		if (err < 0)
313			return err;
314	}
315
316	return 0;
317}
318EXPORT_SYMBOL(snd_msndmix_new);
319
320void snd_msndmix_setup(struct snd_msnd *dev)
321{
322	update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
323	update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
324	update_volm(MSND_MIXER_PCM, wCurrPlayVol);
325	update_volm(MSND_MIXER_IMIX, wCurrInVol);
326	if (dev->type == msndPinnacle) {
327		update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
328		update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
329	}
330}
331EXPORT_SYMBOL(snd_msndmix_setup);
332
333int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc)
334{
335	dev->recsrc = -1;
336	return snd_msndmix_set_mux(dev, recsrc);
337}
338EXPORT_SYMBOL(snd_msndmix_force_recsrc);
339