File Index
Files and directories
Sign in
FILE

msnd_pinnacle_mixer.c

File preview with syntax highlighting.

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