File Index
Files and directories
Sign in
FILE

sb_mixer.c

File preview with syntax highlighting.

linux-6.19.14/sound/isa/sb/sb_mixer.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  4 *  Routines for Sound Blaster mixer control
  5 */
  6
  7#include <linux/io.h>
  8#include <linux/delay.h>
  9#include <linux/string.h>
 10#include <linux/time.h>
 11#include <sound/core.h>
 12#include <sound/sb.h>
 13#include <sound/control.h>
 14
 15#undef IO_DEBUG
 16
 17void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char data)
 18{
 19	outb(reg, SBP(chip, MIXER_ADDR));
 20	udelay(10);
 21	outb(data, SBP(chip, MIXER_DATA));
 22	udelay(10);
 23#ifdef IO_DEBUG
 24	dev_dbg(chip->card->dev, "mixer_write 0x%x 0x%x\n", reg, data);
 25#endif
 26}
 27
 28unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg)
 29{
 30	unsigned char result;
 31
 32	outb(reg, SBP(chip, MIXER_ADDR));
 33	udelay(10);
 34	result = inb(SBP(chip, MIXER_DATA));
 35	udelay(10);
 36#ifdef IO_DEBUG
 37	dev_dbg(chip->card->dev, "mixer_read 0x%x 0x%x\n", reg, result);
 38#endif
 39	return result;
 40}
 41
 42/*
 43 * Single channel mixer element
 44 */
 45
 46static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 47{
 48	int mask = (kcontrol->private_value >> 24) & 0xff;
 49
 50	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
 51	uinfo->count = 1;
 52	uinfo->value.integer.min = 0;
 53	uinfo->value.integer.max = mask;
 54	return 0;
 55}
 56
 57static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 58{
 59	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
 60	int reg = kcontrol->private_value & 0xff;
 61	int shift = (kcontrol->private_value >> 16) & 0xff;
 62	int mask = (kcontrol->private_value >> 24) & 0xff;
 63	unsigned char val;
 64
 65	guard(spinlock_irqsave)(&sb->mixer_lock);
 66	val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
 67	ucontrol->value.integer.value[0] = val;
 68	return 0;
 69}
 70
 71static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 72{
 73	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
 74	int reg = kcontrol->private_value & 0xff;
 75	int shift = (kcontrol->private_value >> 16) & 0x07;
 76	int mask = (kcontrol->private_value >> 24) & 0xff;
 77	int change;
 78	unsigned char val, oval;
 79
 80	val = (ucontrol->value.integer.value[0] & mask) << shift;
 81	guard(spinlock_irqsave)(&sb->mixer_lock);
 82	oval = snd_sbmixer_read(sb, reg);
 83	val = (oval & ~(mask << shift)) | val;
 84	change = val != oval;
 85	if (change)
 86		snd_sbmixer_write(sb, reg, val);
 87	return change;
 88}
 89
 90/*
 91 * Double channel mixer element
 92 */
 93
 94static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 95{
 96	int mask = (kcontrol->private_value >> 24) & 0xff;
 97
 98	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
 99	uinfo->count = 2;
100	uinfo->value.integer.min = 0;
101	uinfo->value.integer.max = mask;
102	return 0;
103}
104
105static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
106{
107	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
108	int left_reg = kcontrol->private_value & 0xff;
109	int right_reg = (kcontrol->private_value >> 8) & 0xff;
110	int left_shift = (kcontrol->private_value >> 16) & 0x07;
111	int right_shift = (kcontrol->private_value >> 19) & 0x07;
112	int mask = (kcontrol->private_value >> 24) & 0xff;
113	unsigned char left, right;
114
115	guard(spinlock_irqsave)(&sb->mixer_lock);
116	left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
117	right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
118	ucontrol->value.integer.value[0] = left;
119	ucontrol->value.integer.value[1] = right;
120	return 0;
121}
122
123static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
124{
125	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
126	int left_reg = kcontrol->private_value & 0xff;
127	int right_reg = (kcontrol->private_value >> 8) & 0xff;
128	int left_shift = (kcontrol->private_value >> 16) & 0x07;
129	int right_shift = (kcontrol->private_value >> 19) & 0x07;
130	int mask = (kcontrol->private_value >> 24) & 0xff;
131	int change;
132	unsigned char left, right, oleft, oright;
133
134	left = (ucontrol->value.integer.value[0] & mask) << left_shift;
135	right = (ucontrol->value.integer.value[1] & mask) << right_shift;
136	guard(spinlock_irqsave)(&sb->mixer_lock);
137	if (left_reg == right_reg) {
138		oleft = snd_sbmixer_read(sb, left_reg);
139		left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
140		change = left != oleft;
141		if (change)
142			snd_sbmixer_write(sb, left_reg, left);
143	} else {
144		oleft = snd_sbmixer_read(sb, left_reg);
145		oright = snd_sbmixer_read(sb, right_reg);
146		left = (oleft & ~(mask << left_shift)) | left;
147		right = (oright & ~(mask << right_shift)) | right;
148		change = left != oleft || right != oright;
149		if (change) {
150			snd_sbmixer_write(sb, left_reg, left);
151			snd_sbmixer_write(sb, right_reg, right);
152		}
153	}
154	return change;
155}
156
157/*
158 * DT-019x / ALS-007 capture/input switch
159 */
160
161static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
162{
163	static const char * const texts[5] = {
164		"CD", "Mic", "Line", "Synth", "Master"
165	};
166
167	return snd_ctl_enum_info(uinfo, 1, 5, texts);
168}
169
170static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
171{
172	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
173	unsigned char oval;
174	
175	scoped_guard(spinlock_irqsave, &sb->mixer_lock) {
176		oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
177	}
178	switch (oval & 0x07) {
179	case SB_DT019X_CAP_CD:
180		ucontrol->value.enumerated.item[0] = 0;
181		break;
182	case SB_DT019X_CAP_MIC:
183		ucontrol->value.enumerated.item[0] = 1;
184		break;
185	case SB_DT019X_CAP_LINE:
186		ucontrol->value.enumerated.item[0] = 2;
187		break;
188	case SB_DT019X_CAP_MAIN:
189		ucontrol->value.enumerated.item[0] = 4;
190		break;
191	/* To record the synth on these cards you must record the main.   */
192	/* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */
193	/* duplicate case labels if left uncommented. */
194	/* case SB_DT019X_CAP_SYNTH:
195	 *	ucontrol->value.enumerated.item[0] = 3;
196	 *	break;
197	 */
198	default:
199		ucontrol->value.enumerated.item[0] = 4;
200		break;
201	}
202	return 0;
203}
204
205static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
206{
207	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
208	int change;
209	unsigned char nval, oval;
210	
211	if (ucontrol->value.enumerated.item[0] > 4)
212		return -EINVAL;
213	switch (ucontrol->value.enumerated.item[0]) {
214	case 0:
215		nval = SB_DT019X_CAP_CD;
216		break;
217	case 1:
218		nval = SB_DT019X_CAP_MIC;
219		break;
220	case 2:
221		nval = SB_DT019X_CAP_LINE;
222		break;
223	case 3:
224		nval = SB_DT019X_CAP_SYNTH;
225		break;
226	case 4:
227		nval = SB_DT019X_CAP_MAIN;
228		break;
229	default:
230		nval = SB_DT019X_CAP_MAIN;
231	}
232	guard(spinlock_irqsave)(&sb->mixer_lock);
233	oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
234	change = nval != oval;
235	if (change)
236		snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);
237	return change;
238}
239
240/*
241 * ALS4000 mono recording control switch
242 */
243
244static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol,
245					     struct snd_ctl_elem_info *uinfo)
246{
247	static const char * const texts[3] = {
248		"L chan only", "R chan only", "L ch/2 + R ch/2"
249	};
250
251	return snd_ctl_enum_info(uinfo, 1, 3, texts);
252}
253
254static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,
255				struct snd_ctl_elem_value *ucontrol)
256{
257	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
258	unsigned char oval;
259
260	guard(spinlock_irqsave)(&sb->mixer_lock);
261	oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
262	oval >>= 6;
263	if (oval > 2)
264		oval = 2;
265
266	ucontrol->value.enumerated.item[0] = oval;
267	return 0;
268}
269
270static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
271				struct snd_ctl_elem_value *ucontrol)
272{
273	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
274	int change;
275	unsigned char nval, oval;
276
277	if (ucontrol->value.enumerated.item[0] > 2)
278		return -EINVAL;
279	guard(spinlock_irqsave)(&sb->mixer_lock);
280	oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
281
282	nval = (oval & ~(3 << 6))
283	     | (ucontrol->value.enumerated.item[0] << 6);
284	change = nval != oval;
285	if (change)
286		snd_sbmixer_write(sb, SB_ALS4000_MONO_IO_CTRL, nval);
287	return change;
288}
289
290/*
291 * SBPRO input multiplexer
292 */
293
294static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
295{
296	static const char * const texts[3] = {
297		"Mic", "CD", "Line"
298	};
299
300	return snd_ctl_enum_info(uinfo, 1, 3, texts);
301}
302
303
304static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
305{
306	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
307	unsigned char oval;
308	
309	guard(spinlock_irqsave)(&sb->mixer_lock);
310	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
311	switch ((oval >> 0x01) & 0x03) {
312	case SB_DSP_MIXS_CD:
313		ucontrol->value.enumerated.item[0] = 1;
314		break;
315	case SB_DSP_MIXS_LINE:
316		ucontrol->value.enumerated.item[0] = 2;
317		break;
318	default:
319		ucontrol->value.enumerated.item[0] = 0;
320		break;
321	}
322	return 0;
323}
324
325static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
326{
327	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
328	int change;
329	unsigned char nval, oval;
330	
331	if (ucontrol->value.enumerated.item[0] > 2)
332		return -EINVAL;
333	switch (ucontrol->value.enumerated.item[0]) {
334	case 1:
335		nval = SB_DSP_MIXS_CD;
336		break;
337	case 2:
338		nval = SB_DSP_MIXS_LINE;
339		break;
340	default:
341		nval = SB_DSP_MIXS_MIC;
342	}
343	nval <<= 1;
344	guard(spinlock_irqsave)(&sb->mixer_lock);
345	oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
346	nval |= oval & ~0x06;
347	change = nval != oval;
348	if (change)
349		snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
350	return change;
351}
352
353/*
354 * SB16 input switch
355 */
356
357static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
358{
359	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
360	uinfo->count = 4;
361	uinfo->value.integer.min = 0;
362	uinfo->value.integer.max = 1;
363	return 0;
364}
365
366static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
367{
368	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
369	int reg1 = kcontrol->private_value & 0xff;
370	int reg2 = (kcontrol->private_value >> 8) & 0xff;
371	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
372	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
373	unsigned char val1, val2;
374
375	guard(spinlock_irqsave)(&sb->mixer_lock);
376	val1 = snd_sbmixer_read(sb, reg1);
377	val2 = snd_sbmixer_read(sb, reg2);
378	ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
379	ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
380	ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
381	ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01;
382	return 0;
383}                                                                                                                   
384
385static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
386{
387	struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
388	int reg1 = kcontrol->private_value & 0xff;
389	int reg2 = (kcontrol->private_value >> 8) & 0xff;
390	int left_shift = (kcontrol->private_value >> 16) & 0x0f;
391	int right_shift = (kcontrol->private_value >> 24) & 0x0f;
392	int change;
393	unsigned char val1, val2, oval1, oval2;
394
395	guard(spinlock_irqsave)(&sb->mixer_lock);
396	oval1 = snd_sbmixer_read(sb, reg1);
397	oval2 = snd_sbmixer_read(sb, reg2);
398	val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
399	val2 = oval2 & ~((1 << left_shift) | (1 << right_shift));
400	val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
401	val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
402	val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
403	val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
404	change = val1 != oval1 || val2 != oval2;
405	if (change) {
406		snd_sbmixer_write(sb, reg1, val1);
407		snd_sbmixer_write(sb, reg2, val2);
408	}
409	return change;
410}
411
412
413/*
414 */
415/*
416 */
417int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)
418{
419	static const struct snd_kcontrol_new newctls[] = {
420		[SB_MIX_SINGLE] = {
421			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
422			.info = snd_sbmixer_info_single,
423			.get = snd_sbmixer_get_single,
424			.put = snd_sbmixer_put_single,
425		},
426		[SB_MIX_DOUBLE] = {
427			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
428			.info = snd_sbmixer_info_double,
429			.get = snd_sbmixer_get_double,
430			.put = snd_sbmixer_put_double,
431		},
432		[SB_MIX_INPUT_SW] = {
433			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
434			.info = snd_sb16mixer_info_input_sw,
435			.get = snd_sb16mixer_get_input_sw,
436			.put = snd_sb16mixer_put_input_sw,
437		},
438		[SB_MIX_CAPTURE_PRO] = {
439			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
440			.info = snd_sb8mixer_info_mux,
441			.get = snd_sb8mixer_get_mux,
442			.put = snd_sb8mixer_put_mux,
443		},
444		[SB_MIX_CAPTURE_DT019X] = {
445			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
446			.info = snd_dt019x_input_sw_info,
447			.get = snd_dt019x_input_sw_get,
448			.put = snd_dt019x_input_sw_put,
449		},
450		[SB_MIX_MONO_CAPTURE_ALS4K] = {
451			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
452			.info = snd_als4k_mono_capture_route_info,
453			.get = snd_als4k_mono_capture_route_get,
454			.put = snd_als4k_mono_capture_route_put,
455		},
456	};
457	struct snd_kcontrol *ctl;
458	int err;
459
460	ctl = snd_ctl_new1(&newctls[type], chip);
461	if (! ctl)
462		return -ENOMEM;
463	strscpy(ctl->id.name, name, sizeof(ctl->id.name));
464	ctl->id.index = index;
465	ctl->private_value = value;
466	err = snd_ctl_add(chip->card, ctl);
467	if (err < 0)
468		return err;
469	return 0;
470}
471
472/*
473 * SB 2.0 specific mixer elements
474 */
475
476static const struct sbmix_elem snd_sb20_controls[] = {
477	SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),
478	SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),
479	SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),
480	SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)
481};
482
483static const unsigned char snd_sb20_init_values[][2] = {
484	{ SB_DSP20_MASTER_DEV, 0 },
485	{ SB_DSP20_FM_DEV, 0 },
486};
487
488/*
489 * SB Pro specific mixer elements
490 */
491static const struct sbmix_elem snd_sbpro_controls[] = {
492	SB_DOUBLE("Master Playback Volume",
493		  SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),
494	SB_DOUBLE("PCM Playback Volume",
495		  SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7),
496	SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1),
497	SB_DOUBLE("Synth Playback Volume",
498		  SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7),
499	SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7),
500	SB_DOUBLE("Line Playback Volume",
501		  SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7),
502	SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3),
503	{
504		.name = "Capture Source",
505		.type = SB_MIX_CAPTURE_PRO
506	},
507	SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1),
508	SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
509};
510
511static const unsigned char snd_sbpro_init_values[][2] = {
512	{ SB_DSP_MASTER_DEV, 0 },
513	{ SB_DSP_PCM_DEV, 0 },
514	{ SB_DSP_FM_DEV, 0 },
515};
516
517/*
518 * SB16 specific mixer elements
519 */
520static const struct sbmix_elem snd_sb16_controls[] = {
521	SB_DOUBLE("Master Playback Volume",
522		  SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
523	SB_DOUBLE("PCM Playback Volume",
524		  SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31),
525	SB16_INPUT_SW("Synth Capture Route",
526		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5),
527	SB_DOUBLE("Synth Playback Volume",
528		  SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31),
529	SB16_INPUT_SW("CD Capture Route",
530		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1),
531	SB_DOUBLE("CD Playback Switch",
532		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
533	SB_DOUBLE("CD Playback Volume",
534		  SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31),
535	SB16_INPUT_SW("Mic Capture Route",
536		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0),
537	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
538	SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
539	SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
540	SB_DOUBLE("Capture Volume",
541		  SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
542	SB_DOUBLE("Playback Volume",
543		  SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
544	SB16_INPUT_SW("Line Capture Route",
545		      SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3),
546	SB_DOUBLE("Line Playback Switch",
547		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
548	SB_DOUBLE("Line Playback Volume",
549		  SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
550	SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
551	SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1),
552	SB_DOUBLE("Tone Control - Bass",
553		  SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),
554	SB_DOUBLE("Tone Control - Treble",
555		  SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15)
556};
557
558static const unsigned char snd_sb16_init_values[][2] = {
559	{ SB_DSP4_MASTER_DEV + 0, 0 },
560	{ SB_DSP4_MASTER_DEV + 1, 0 },
561	{ SB_DSP4_PCM_DEV + 0, 0 },
562	{ SB_DSP4_PCM_DEV + 1, 0 },
563	{ SB_DSP4_SYNTH_DEV + 0, 0 },
564	{ SB_DSP4_SYNTH_DEV + 1, 0 },
565	{ SB_DSP4_INPUT_LEFT, 0 },
566	{ SB_DSP4_INPUT_RIGHT, 0 },
567	{ SB_DSP4_OUTPUT_SW, 0 },
568	{ SB_DSP4_SPEAKER_DEV, 0 },
569};
570
571/*
572 * DT019x specific mixer elements
573 */
574static const struct sbmix_elem snd_dt019x_controls[] = {
575	/* ALS4000 below has some parts which we might be lacking,
576	 * e.g. snd_als4000_ctl_mono_playback_switch - check it! */
577	SB_DOUBLE("Master Playback Volume",
578		  SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4, 0, 15),
579	SB_DOUBLE("PCM Playback Switch",
580		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
581	SB_DOUBLE("PCM Playback Volume",
582		  SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4, 0, 15),
583	SB_DOUBLE("Synth Playback Switch",
584		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
585	SB_DOUBLE("Synth Playback Volume",
586		  SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4, 0, 15),
587	SB_DOUBLE("CD Playback Switch",
588		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
589	SB_DOUBLE("CD Playback Volume",
590		  SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4, 0, 15),
591	SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
592	SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7),
593	SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0,  7),
594	SB_DOUBLE("Line Playback Switch",
595		  SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
596	SB_DOUBLE("Line Playback Volume",
597		  SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4, 0, 15),
598	{
599		.name = "Capture Source",
600		.type = SB_MIX_CAPTURE_DT019X
601	}
602};
603
604static const unsigned char snd_dt019x_init_values[][2] = {
605        { SB_DT019X_MASTER_DEV, 0 },
606        { SB_DT019X_PCM_DEV, 0 },
607        { SB_DT019X_SYNTH_DEV, 0 },
608        { SB_DT019X_CD_DEV, 0 },
609        { SB_DT019X_MIC_DEV, 0 },	/* Includes PC-speaker in high nibble */
610        { SB_DT019X_LINE_DEV, 0 },
611        { SB_DSP4_OUTPUT_SW, 0 },
612        { SB_DT019X_OUTPUT_SW2, 0 },
613        { SB_DT019X_CAPTURE_SW, 0x06 },
614};
615
616/*
617 * ALS4000 specific mixer elements
618 */
619static const struct sbmix_elem snd_als4000_controls[] = {
620	SB_DOUBLE("PCM Playback Switch",
621		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
622	SB_DOUBLE("Synth Playback Switch",
623		  SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
624	SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03),
625	SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1),
626	{
627		.name = "Master Mono Capture Route",
628		.type = SB_MIX_MONO_CAPTURE_ALS4K
629	},
630	SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1),
631	SB_SINGLE("Analog Loopback Switch", SB_ALS4000_MIC_IN_GAIN, 7, 0x01),
632	SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01),
633	SB_SINGLE("Digital Loopback Switch",
634		  SB_ALS4000_CR3_CONFIGURATION, 7, 0x01),
635	/* FIXME: functionality of 3D controls might be swapped, I didn't find
636	 * a description of how to identify what is supposed to be what */
637	SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07),
638	/* FIXME: maybe there's actually some standard 3D ctrl name for it?? */
639	SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03),
640	/* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay,
641	 * but what ALSA 3D attribute is that actually? "Center", "Depth",
642	 * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */
643	SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f),
644	SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01),
645	SB_SINGLE("Master Playback 8kHz / 20kHz LPF Switch",
646		  SB_ALS4000_FMDAC, 5, 0x01),
647#ifdef NOT_AVAILABLE
648	SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01),
649	SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f),
650#endif
651};
652
653static const unsigned char snd_als4000_init_values[][2] = {
654	{ SB_DSP4_MASTER_DEV + 0, 0 },
655	{ SB_DSP4_MASTER_DEV + 1, 0 },
656	{ SB_DSP4_PCM_DEV + 0, 0 },
657	{ SB_DSP4_PCM_DEV + 1, 0 },
658	{ SB_DSP4_SYNTH_DEV + 0, 0 },
659	{ SB_DSP4_SYNTH_DEV + 1, 0 },
660	{ SB_DSP4_SPEAKER_DEV, 0 },
661	{ SB_DSP4_OUTPUT_SW, 0 },
662	{ SB_DSP4_INPUT_LEFT, 0 },
663	{ SB_DSP4_INPUT_RIGHT, 0 },
664	{ SB_DT019X_OUTPUT_SW2, 0 },
665	{ SB_ALS4000_MIC_IN_GAIN, 0 },
666};
667
668/*
669 */
670static int snd_sbmixer_init(struct snd_sb *chip,
671			    const struct sbmix_elem *controls,
672			    int controls_count,
673			    const unsigned char map[][2],
674			    int map_count,
675			    char *name)
676{
677	struct snd_card *card = chip->card;
678	int idx, err;
679
680	/* mixer reset */
681	scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
682		snd_sbmixer_write(chip, 0x00, 0x00);
683	}
684
685	/* mute and zero volume channels */
686	for (idx = 0; idx < map_count; idx++) {
687		guard(spinlock_irqsave)(&chip->mixer_lock);
688		snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
689	}
690
691	for (idx = 0; idx < controls_count; idx++) {
692		err = snd_sbmixer_add_ctl_elem(chip, &controls[idx]);
693		if (err < 0)
694			return err;
695	}
696	snd_component_add(card, name);
697	strscpy(card->mixername, name);
698	return 0;
699}
700
701int snd_sbmixer_new(struct snd_sb *chip)
702{
703	struct snd_card *card;
704	int err;
705
706	if (snd_BUG_ON(!chip || !chip->card))
707		return -EINVAL;
708
709	card = chip->card;
710
711	switch (chip->hardware) {
712	case SB_HW_10:
713		return 0; /* no mixer chip on SB1.x */
714	case SB_HW_20:
715	case SB_HW_201:
716		err = snd_sbmixer_init(chip,
717				       snd_sb20_controls,
718				       ARRAY_SIZE(snd_sb20_controls),
719				       snd_sb20_init_values,
720				       ARRAY_SIZE(snd_sb20_init_values),
721				       "CTL1335");
722		if (err < 0)
723			return err;
724		break;
725	case SB_HW_PRO:
726	case SB_HW_JAZZ16:
727		err = snd_sbmixer_init(chip,
728				       snd_sbpro_controls,
729				       ARRAY_SIZE(snd_sbpro_controls),
730				       snd_sbpro_init_values,
731				       ARRAY_SIZE(snd_sbpro_init_values),
732				       "CTL1345");
733		if (err < 0)
734			return err;
735		break;
736	case SB_HW_16:
737	case SB_HW_ALS100:
738	case SB_HW_CS5530:
739		err = snd_sbmixer_init(chip,
740				       snd_sb16_controls,
741				       ARRAY_SIZE(snd_sb16_controls),
742				       snd_sb16_init_values,
743				       ARRAY_SIZE(snd_sb16_init_values),
744				       "CTL1745");
745		if (err < 0)
746			return err;
747		break;
748	case SB_HW_ALS4000:
749		/* use only the first 16 controls from SB16 */
750		err = snd_sbmixer_init(chip,
751					snd_sb16_controls,
752					16,
753					snd_sb16_init_values,
754					ARRAY_SIZE(snd_sb16_init_values),
755					"ALS4000");
756		if (err < 0)
757			return err;
758		err = snd_sbmixer_init(chip,
759				       snd_als4000_controls,
760				       ARRAY_SIZE(snd_als4000_controls),
761				       snd_als4000_init_values,
762				       ARRAY_SIZE(snd_als4000_init_values),
763				       "ALS4000");
764		if (err < 0)
765			return err;
766		break;
767	case SB_HW_DT019X:
768		err = snd_sbmixer_init(chip,
769				       snd_dt019x_controls,
770				       ARRAY_SIZE(snd_dt019x_controls),
771				       snd_dt019x_init_values,
772				       ARRAY_SIZE(snd_dt019x_init_values),
773				       "DT019X");
774		if (err < 0)
775			return err;
776		break;
777	default:
778		strscpy(card->mixername, "???");
779	}
780	return 0;
781}
782
783#ifdef CONFIG_PM
784static const unsigned char sb20_saved_regs[] = {
785	SB_DSP20_MASTER_DEV,
786	SB_DSP20_PCM_DEV,
787	SB_DSP20_FM_DEV,
788	SB_DSP20_CD_DEV,
789};
790
791static const unsigned char sbpro_saved_regs[] = {
792	SB_DSP_MASTER_DEV,
793	SB_DSP_PCM_DEV,
794	SB_DSP_PLAYBACK_FILT,
795	SB_DSP_FM_DEV,
796	SB_DSP_CD_DEV,
797	SB_DSP_LINE_DEV,
798	SB_DSP_MIC_DEV,
799	SB_DSP_CAPTURE_SOURCE,
800	SB_DSP_CAPTURE_FILT,
801};
802
803static const unsigned char sb16_saved_regs[] = {
804	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
805	SB_DSP4_3DSE,
806	SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,
807	SB_DSP4_TREBLE_DEV, SB_DSP4_TREBLE_DEV + 1,
808	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
809	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
810	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
811	SB_DSP4_OUTPUT_SW,
812	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
813	SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1,
814	SB_DSP4_MIC_DEV,
815	SB_DSP4_SPEAKER_DEV,
816	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
817	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
818	SB_DSP4_MIC_AGC
819};
820
821static const unsigned char dt019x_saved_regs[] = {
822	SB_DT019X_MASTER_DEV,
823	SB_DT019X_PCM_DEV,
824	SB_DT019X_SYNTH_DEV,
825	SB_DT019X_CD_DEV,
826	SB_DT019X_MIC_DEV,
827	SB_DT019X_SPKR_DEV,
828	SB_DT019X_LINE_DEV,
829	SB_DSP4_OUTPUT_SW,
830	SB_DT019X_OUTPUT_SW2,
831	SB_DT019X_CAPTURE_SW,
832};
833
834static const unsigned char als4000_saved_regs[] = {
835	/* please verify in dsheet whether regs to be added
836	   are actually real H/W or just dummy */
837	SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
838	SB_DSP4_OUTPUT_SW,
839	SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
840	SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
841	SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
842	SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,
843	SB_DSP4_MIC_DEV,
844	SB_DSP4_SPEAKER_DEV,
845	SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,
846	SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,
847	SB_DT019X_OUTPUT_SW2,
848	SB_ALS4000_MONO_IO_CTRL,
849	SB_ALS4000_MIC_IN_GAIN,
850	SB_ALS4000_FMDAC,
851	SB_ALS4000_3D_SND_FX,
852	SB_ALS4000_3D_TIME_DELAY,
853	SB_ALS4000_CR3_CONFIGURATION,
854};
855
856static void save_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)
857{
858	unsigned char *val = chip->saved_regs;
859	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
860		return;
861	for (; num_regs; num_regs--)
862		*val++ = snd_sbmixer_read(chip, *regs++);
863}
864
865static void restore_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)
866{
867	unsigned char *val = chip->saved_regs;
868	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
869		return;
870	for (; num_regs; num_regs--)
871		snd_sbmixer_write(chip, *regs++, *val++);
872}
873
874void snd_sbmixer_suspend(struct snd_sb *chip)
875{
876	switch (chip->hardware) {
877	case SB_HW_20:
878	case SB_HW_201:
879		save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
880		break;
881	case SB_HW_PRO:
882	case SB_HW_JAZZ16:
883		save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
884		break;
885	case SB_HW_16:
886	case SB_HW_ALS100:
887	case SB_HW_CS5530:
888		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
889		break;
890	case SB_HW_ALS4000:
891		save_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
892		break;
893	case SB_HW_DT019X:
894		save_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
895		break;
896	default:
897		break;
898	}
899}
900
901void snd_sbmixer_resume(struct snd_sb *chip)
902{
903	switch (chip->hardware) {
904	case SB_HW_20:
905	case SB_HW_201:
906		restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
907		break;
908	case SB_HW_PRO:
909	case SB_HW_JAZZ16:
910		restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
911		break;
912	case SB_HW_16:
913	case SB_HW_ALS100:
914	case SB_HW_CS5530:
915		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
916		break;
917	case SB_HW_ALS4000:
918		restore_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));
919		break;
920	case SB_HW_DT019X:
921		restore_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));
922		break;
923	default:
924		break;
925	}
926}
927#endif
~/ Read-only file preview.