brintos

brintos / linux-shallow public Read only

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