Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7cb9d94c authored by Connor McAdams's avatar Connor McAdams Committed by Takashi Iwai
Browse files

ALSA: hda/ca0132: add alt_select_in/out for R3Di + SBZ



Add functions ca0132_alt_select_out and ca0132_alt_select_in for
switching outputs and inputs for r3di and sbz. Also, add enumerated
controls for selecting output and input source.

Signed-off-by: default avatarConnor McAdams <conmanx360@gmail.com>
Reviewed-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 447fd8e9
Loading
Loading
Loading
Loading
+607 −18
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@
#define FLOAT_ONE	0x3f800000
#define FLOAT_TWO	0x40000000
#define FLOAT_THREE     0x40400000
#define FLOAT_EIGHT     0x41000000
#define FLOAT_MINUS_5	0xc0a00000

#define UNSOL_TAG_DSP	0x16
@@ -91,9 +92,11 @@ MODULE_FIRMWARE(R3DI_EFX_FILE);

static const char *dirstr[2] = { "Playback", "Capture" };

#define NUM_OF_OUTPUTS 3
enum {
	SPEAKER_OUT,
	HEADPHONE_OUT
	HEADPHONE_OUT,
	SURROUND_OUT
};

enum {
@@ -101,6 +104,15 @@ enum {
	LINE_MIC_IN
};

/* Strings for Input Source Enum Control */
static const char *in_src_str[3] = {"Rear Mic", "Line", "Front Mic" };
#define IN_SRC_NUM_OF_INPUTS 3
enum {
	REAR_MIC,
	REAR_LINE_IN,
	FRONT_MIC,
};

enum {
#define VNODE_START_NID    0x80
	VNID_SPK = VNODE_START_NID,			/* Speaker vnid */
@@ -134,7 +146,9 @@ enum {
	VOICEFX = IN_EFFECT_END_NID,
	PLAY_ENHANCEMENT,
	CRYSTAL_VOICE,
	EFFECT_END_NID
	EFFECT_END_NID,
	OUTPUT_SOURCE_ENUM,
	INPUT_SOURCE_ENUM
#define EFFECTS_COUNT  (EFFECT_END_NID - EFFECT_START_NID)
};

@@ -484,6 +498,49 @@ static struct ct_voicefx_preset ca0132_voicefx_presets[] = {
	}
};

/* DSP command sequences for ca0132_alt_select_out */
#define ALT_OUT_SET_MAX_COMMANDS 9 /* Max number of commands in sequence */
struct ca0132_alt_out_set {
	char *name; /*preset name*/
	unsigned char commands;
	unsigned int mids[ALT_OUT_SET_MAX_COMMANDS];
	unsigned int reqs[ALT_OUT_SET_MAX_COMMANDS];
	unsigned int vals[ALT_OUT_SET_MAX_COMMANDS];
};

static const struct ca0132_alt_out_set alt_out_presets[] = {
	{ .name = "Line Out",
	  .commands = 7,
	  .mids = { 0x96, 0x96, 0x96, 0x8F,
		    0x96, 0x96, 0x96 },
	  .reqs = { 0x19, 0x17, 0x18, 0x01,
		    0x1F, 0x15, 0x3A },
	  .vals = { 0x3F000000, 0x42A00000, 0x00000000,
		    0x00000000, 0x00000000, 0x00000000,
		    0x00000000 }
	},
	{ .name = "Headphone",
	  .commands = 7,
	  .mids = { 0x96, 0x96, 0x96, 0x8F,
		    0x96, 0x96, 0x96 },
	  .reqs = { 0x19, 0x17, 0x18, 0x01,
		    0x1F, 0x15, 0x3A },
	  .vals = { 0x3F000000, 0x42A00000, 0x00000000,
		    0x00000000, 0x00000000, 0x00000000,
		    0x00000000 }
	},
	{ .name = "Surround",
	  .commands = 8,
	  .mids = { 0x96, 0x8F, 0x96, 0x96,
		    0x96, 0x96, 0x96, 0x96 },
	  .reqs = { 0x18, 0x01, 0x1F, 0x15,
		    0x3A, 0x1A, 0x1B, 0x1C },
	  .vals = { 0x00000000, 0x00000000, 0x00000000,
		    0x00000000, 0x00000000, 0x00000000,
		    0x00000000, 0x00000000 }
	}
};

enum hda_cmd_vendor_io {
	/* for DspIO node */
	VENDOR_DSPIO_SCP_WRITE_DATA_LOW      = 0x000,
@@ -763,6 +820,9 @@ struct ca0132_spec {
	long effects_switch[EFFECTS_COUNT];
	long voicefx_val;
	long cur_mic_boost;
	/* ca0132_alt control related values */
	unsigned char in_enum_val;
	unsigned char out_enum_val;

	struct hda_codec *codec;
	struct delayed_work unsol_hp_work;
@@ -2959,6 +3019,47 @@ enum r3di_dsp_status {
	R3DI_DSP_DOWNLOADED = 1
};


static void r3di_gpio_mic_set(struct hda_codec *codec,
		enum r3di_mic_select cur_mic)
{
	unsigned int cur_gpio;

	/* Get the current GPIO Data setup */
	cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);

	switch (cur_mic) {
	case R3DI_REAR_MIC:
		cur_gpio &= ~(1 << R3DI_MIC_SELECT_BIT);
		break;
	case R3DI_FRONT_MIC:
		cur_gpio |= (1 << R3DI_MIC_SELECT_BIT);
		break;
	}
	snd_hda_codec_write(codec, codec->core.afg, 0,
			    AC_VERB_SET_GPIO_DATA, cur_gpio);
}

static void r3di_gpio_out_set(struct hda_codec *codec,
		enum r3di_out_select cur_out)
{
	unsigned int cur_gpio;

	/* Get the current GPIO Data setup */
	cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);

	switch (cur_out) {
	case R3DI_HEADPHONE_OUT:
		cur_gpio &= ~(1 << R3DI_OUT_SELECT_BIT);
		break;
	case R3DI_LINE_OUT:
		cur_gpio |= (1 << R3DI_OUT_SELECT_BIT);
		break;
	}
	snd_hda_codec_write(codec, codec->core.afg, 0,
			    AC_VERB_SET_GPIO_DATA, cur_gpio);
}

static void r3di_gpio_dsp_status_set(struct hda_codec *codec,
		enum r3di_dsp_status dsp_status)
{
@@ -3550,13 +3651,209 @@ static int ca0132_select_out(struct hda_codec *codec)
	return err < 0 ? err : 0;
}

/*
 * This function behaves similarly to the ca0132_select_out funciton above,
 * except with a few differences. It adds the ability to select the current
 * output with an enumerated control "output source" if the auto detect
 * mute switch is set to off. If the auto detect mute switch is enabled, it
 * will detect either headphone or lineout(SPEAKER_OUT) from jack detection.
 * It also adds the ability to auto-detect the front headphone port. The only
 * way to select surround is to disable auto detect, and set Surround with the
 * enumerated control.
 */
static int ca0132_alt_select_out(struct hda_codec *codec)
{
	struct ca0132_spec *spec = codec->spec;
	unsigned int pin_ctl;
	int jack_present;
	int auto_jack;
	unsigned int i;
	unsigned int tmp;
	int err;
	/* Default Headphone is rear headphone */
	hda_nid_t headphone_nid = spec->out_pins[1];

	codec_dbg(codec, "%s\n", __func__);

	snd_hda_power_up_pm(codec);

	auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];

	/*
	 * If headphone rear or front is plugged in, set to headphone.
	 * If neither is plugged in, set to rear line out. Only if
	 * hp/speaker auto detect is enabled.
	 */
	if (auto_jack) {
		jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp) ||
			   snd_hda_jack_detect(codec, spec->unsol_tag_front_hp);

		if (jack_present)
			spec->cur_out_type = HEADPHONE_OUT;
		else
			spec->cur_out_type = SPEAKER_OUT;
	} else
		spec->cur_out_type = spec->out_enum_val;

	/* Begin DSP output switch */
	tmp = FLOAT_ONE;
	err = dspio_set_uint_param(codec, 0x96, 0x3A, tmp);
	if (err < 0)
		goto exit;

	switch (spec->cur_out_type) {
	case SPEAKER_OUT:
		codec_dbg(codec, "%s speaker\n", __func__);
		/*speaker out config*/
		switch (spec->quirk) {
		case QUIRK_SBZ:
			writew(0x0007, spec->mem_base + 0x320);
			writew(0x0104, spec->mem_base + 0x320);
			writew(0x0101, spec->mem_base + 0x320);
			chipio_set_control_param(codec, 0x0D, 0x18);
			break;
		case QUIRK_R3DI:
			chipio_set_control_param(codec, 0x0D, 0x24);
			r3di_gpio_out_set(codec, R3DI_LINE_OUT);
			break;
		}

		/* disable headphone node */
		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
		snd_hda_set_pin_ctl(codec, spec->out_pins[1],
				    pin_ctl & ~PIN_HP);
		/* enable line-out node */
		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
		snd_hda_set_pin_ctl(codec, spec->out_pins[0],
				    pin_ctl | PIN_OUT);
		/* Enable EAPD */
		snd_hda_codec_write(codec, spec->out_pins[0], 0,
			AC_VERB_SET_EAPD_BTLENABLE, 0x01);

		/* If PlayEnhancement is enabled, set different source */
		if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
		else
			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT);
		break;
	case HEADPHONE_OUT:
		codec_dbg(codec, "%s hp\n", __func__);
		/* Headphone out config*/
		switch (spec->quirk) {
		case QUIRK_SBZ:
			writew(0x0107, spec->mem_base + 0x320);
			writew(0x0104, spec->mem_base + 0x320);
			writew(0x0001, spec->mem_base + 0x320);
			chipio_set_control_param(codec, 0x0D, 0x12);
			break;
		case QUIRK_R3DI:
			chipio_set_control_param(codec, 0x0D, 0x21);
			r3di_gpio_out_set(codec, R3DI_HEADPHONE_OUT);
			break;
		}

		snd_hda_codec_write(codec, spec->out_pins[0], 0,
			AC_VERB_SET_EAPD_BTLENABLE, 0x00);

		/* disable speaker*/
		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
		snd_hda_set_pin_ctl(codec, spec->out_pins[0],
				pin_ctl & ~PIN_HP);

		/* enable headphone, either front or rear */

		if (snd_hda_jack_detect(codec, spec->unsol_tag_front_hp))
			headphone_nid = spec->out_pins[2];
		else if (snd_hda_jack_detect(codec, spec->unsol_tag_hp))
			headphone_nid = spec->out_pins[1];

		pin_ctl = snd_hda_codec_read(codec, headphone_nid, 0,
					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
		snd_hda_set_pin_ctl(codec, headphone_nid,
				    pin_ctl | PIN_HP);

		if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
		else
			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO);
		break;
	case SURROUND_OUT:
		codec_dbg(codec, "%s surround\n", __func__);
		/* Surround out config*/
		switch (spec->quirk) {
		case QUIRK_SBZ:
			writew(0x0007, spec->mem_base + 0x320);
			writew(0x0104, spec->mem_base + 0x320);
			writew(0x0101, spec->mem_base + 0x320);
			chipio_set_control_param(codec, 0x0D, 0x18);
			break;
		case QUIRK_R3DI:
			chipio_set_control_param(codec, 0x0D, 0x24);
			r3di_gpio_out_set(codec, R3DI_LINE_OUT);
			break;
		}
		/* enable line out node */
		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
		snd_hda_set_pin_ctl(codec, spec->out_pins[0],
						pin_ctl | PIN_OUT);
		/* Disable headphone out */
		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
		snd_hda_set_pin_ctl(codec, spec->out_pins[1],
				    pin_ctl & ~PIN_HP);
		/* Enable EAPD on line out */
		snd_hda_codec_write(codec, spec->out_pins[0], 0,
			AC_VERB_SET_EAPD_BTLENABLE, 0x01);
		/* enable center/lfe out node */
		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[2], 0,
					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
		snd_hda_set_pin_ctl(codec, spec->out_pins[2],
				    pin_ctl | PIN_OUT);
		/* Now set rear surround node as out. */
		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[3], 0,
					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
		snd_hda_set_pin_ctl(codec, spec->out_pins[3],
				    pin_ctl | PIN_OUT);

		if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
		else
			dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT);
		break;
	}

	/* run through the output dsp commands for line-out */
	for (i = 0; i < alt_out_presets[spec->cur_out_type].commands; i++) {
		err = dspio_set_uint_param(codec,
		alt_out_presets[spec->cur_out_type].mids[i],
		alt_out_presets[spec->cur_out_type].reqs[i],
		alt_out_presets[spec->cur_out_type].vals[i]);

		if (err < 0)
			goto exit;
	}

exit:
	snd_hda_power_down_pm(codec);

	return err < 0 ? err : 0;
}

static void ca0132_unsol_hp_delayed(struct work_struct *work)
{
	struct ca0132_spec *spec = container_of(
		to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
	struct hda_jack_tbl *jack;

	if (spec->use_alt_functions)
		ca0132_alt_select_out(spec->codec);
	else
		ca0132_select_out(spec->codec);

	jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp);
	if (jack) {
		jack->block_report = 0;
@@ -3661,6 +3958,122 @@ static int ca0132_select_mic(struct hda_codec *codec)
	return 0;
}

/*
 * Select the active input.
 * Mic detection isn't used, because it's kind of pointless on the SBZ.
 * The front mic has no jack-detection, so the only way to switch to it
 * is to do it manually in alsamixer.
 */
static int ca0132_alt_select_in(struct hda_codec *codec)
{
	struct ca0132_spec *spec = codec->spec;
	unsigned int tmp;

	codec_dbg(codec, "%s\n", __func__);

	snd_hda_power_up_pm(codec);

	chipio_set_stream_control(codec, 0x03, 0);
	chipio_set_stream_control(codec, 0x04, 0);

	spec->cur_mic_type = spec->in_enum_val;

	switch (spec->cur_mic_type) {
	case REAR_MIC:
		switch (spec->quirk) {
		case QUIRK_SBZ:
			writew(0x0000, spec->mem_base + 0x320);
			tmp = FLOAT_THREE;
			break;
		case QUIRK_R3DI:
			r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
			tmp = FLOAT_ONE;
			break;
		default:
			tmp = FLOAT_ONE;
			break;
		}

		chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
		chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
		if (spec->quirk == QUIRK_R3DI)
			chipio_set_conn_rate(codec, 0x0F, SR_96_000);

		dspio_set_uint_param(codec, 0x80, 0x00, tmp);

		chipio_set_stream_control(codec, 0x03, 1);
		chipio_set_stream_control(codec, 0x04, 1);

		if (spec->quirk == QUIRK_SBZ) {
			chipio_write(codec, 0x18B098, 0x0000000C);
			chipio_write(codec, 0x18B09C, 0x0000000C);
		}
		break;
	case REAR_LINE_IN:
		ca0132_mic_boost_set(codec, 0);
		switch (spec->quirk) {
		case QUIRK_SBZ:
			writew(0x0000, spec->mem_base + 0x320);
			break;
		case QUIRK_R3DI:
			r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
			break;
		}

		chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
		chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
		if (spec->quirk == QUIRK_R3DI)
			chipio_set_conn_rate(codec, 0x0F, SR_96_000);

		tmp = FLOAT_ZERO;
		dspio_set_uint_param(codec, 0x80, 0x00, tmp);

		if (spec->quirk == QUIRK_SBZ) {
			chipio_write(codec, 0x18B098, 0x00000000);
			chipio_write(codec, 0x18B09C, 0x00000000);
		}

		chipio_set_stream_control(codec, 0x03, 1);
		chipio_set_stream_control(codec, 0x04, 1);
		break;
	case FRONT_MIC:
		switch (spec->quirk) {
		case QUIRK_SBZ:
			writew(0x0100, spec->mem_base + 0x320);
			writew(0x0005, spec->mem_base + 0x320);
			tmp = FLOAT_THREE;
			break;
		case QUIRK_R3DI:
			r3di_gpio_mic_set(codec, R3DI_FRONT_MIC);
			tmp = FLOAT_ONE;
			break;
		default:
			tmp = FLOAT_ONE;
			break;
		}

		chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
		chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
		if (spec->quirk == QUIRK_R3DI)
			chipio_set_conn_rate(codec, 0x0F, SR_96_000);

		dspio_set_uint_param(codec, 0x80, 0x00, tmp);

		chipio_set_stream_control(codec, 0x03, 1);
		chipio_set_stream_control(codec, 0x04, 1);

		if (spec->quirk == QUIRK_SBZ) {
			chipio_write(codec, 0x18B098, 0x0000000C);
			chipio_write(codec, 0x18B09C, 0x000000CC);
		}
		break;
	}

	snd_hda_power_down_pm(codec);
	return 0;

}

/*
 * Check if VNODE settings take effect immediately.
 */
@@ -3743,7 +4156,8 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
			val = 0;

		/* If Voice Focus on SBZ, set to two channel. */
		if ((nid == VOICE_FOCUS) && (spec->quirk == QUIRK_SBZ)) {
		if ((nid == VOICE_FOCUS) && (spec->quirk == QUIRK_SBZ)
				&& (spec->cur_mic_type != REAR_LINE_IN)) {
			if (spec->effects_switch[CRYSTAL_VOICE -
						 EFFECT_START_NID]) {

@@ -3761,7 +4175,8 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
		 * For SBZ noise reduction, there's an extra command
		 * to module ID 0x47. No clue why.
		 */
		if ((nid == NOISE_REDUCTION) && (spec->quirk == QUIRK_SBZ)) {
		if ((nid == NOISE_REDUCTION) && (spec->quirk == QUIRK_SBZ)
				&& (spec->cur_mic_type != REAR_LINE_IN)) {
			if (spec->effects_switch[CRYSTAL_VOICE -
						 EFFECT_START_NID]) {
				if (spec->effects_switch[NOISE_REDUCTION -
@@ -3774,6 +4189,11 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)

			dspio_set_uint_param(codec, 0x47, 0x00, tmp);
		}

		/* If rear line in disable effects. */
		if (spec->use_alt_functions &&
				spec->in_enum_val == REAR_LINE_IN)
			val = 0;
	}

	codec_dbg(codec, "ca0132_effect_set: nid=0x%x, val=%ld\n",
@@ -3801,6 +4221,9 @@ static int ca0132_pe_switch_set(struct hda_codec *codec)
	codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n",
		    spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]);

	if (spec->use_alt_functions)
		ca0132_alt_select_out(codec);

	i = OUT_EFFECT_START_NID - EFFECT_START_NID;
	nid = OUT_EFFECT_START_NID;
	/* PE affects all out effects */
@@ -3892,8 +4315,12 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
	if (nid == VNID_HP_SEL) {
		auto_jack =
			spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
		if (!auto_jack)
		if (!auto_jack) {
			if (spec->use_alt_functions)
				ca0132_alt_select_out(codec);
			else
				ca0132_select_out(codec);
		}
		return 1;
	}

@@ -3906,6 +4333,9 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
	}

	if (nid == VNID_HP_ASEL) {
		if (spec->use_alt_functions)
			ca0132_alt_select_out(codec);
		else
			ca0132_select_out(codec);
		return 1;
	}
@@ -3935,6 +4365,104 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
}
/* End of control change helpers. */

/*
 * Input Select Control for alternative ca0132 codecs. This exists because
 * front microphone has no auto-detect, and we need a way to set the rear
 * as line-in
 */
static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_info *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->count = 1;
	uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS;
	if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS)
		uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1;
	strcpy(uinfo->value.enumerated.name,
			in_src_str[uinfo->value.enumerated.item]);
	return 0;
}

static int ca0132_alt_input_source_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct ca0132_spec *spec = codec->spec;

	ucontrol->value.enumerated.item[0] = spec->in_enum_val;
	return 0;
}

static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct ca0132_spec *spec = codec->spec;
	int sel = ucontrol->value.enumerated.item[0];
	unsigned int items = IN_SRC_NUM_OF_INPUTS;

	if (sel >= items)
		return 0;

	codec_dbg(codec, "ca0132_alt_input_select: sel=%d, preset=%s\n",
		    sel, in_src_str[sel]);

	spec->in_enum_val = sel;

	ca0132_alt_select_in(codec);

	return 1;
}

/* Sound Blaster Z Output Select Control */
static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_info *uinfo)
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->count = 1;
	uinfo->value.enumerated.items = NUM_OF_OUTPUTS;
	if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS)
		uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1;
	strcpy(uinfo->value.enumerated.name,
			alt_out_presets[uinfo->value.enumerated.item].name);
	return 0;
}

static int ca0132_alt_output_select_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct ca0132_spec *spec = codec->spec;

	ucontrol->value.enumerated.item[0] = spec->out_enum_val;
	return 0;
}

static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
{
	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
	struct ca0132_spec *spec = codec->spec;
	int sel = ucontrol->value.enumerated.item[0];
	unsigned int items = NUM_OF_OUTPUTS;
	unsigned int auto_jack;

	if (sel >= items)
		return 0;

	codec_dbg(codec, "ca0132_alt_output_select: sel=%d, preset=%s\n",
		    sel, alt_out_presets[sel].name);

	spec->out_enum_val = sel;

	auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];

	if (!auto_jack)
		ca0132_alt_select_out(codec);

	return 1;
}

static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_info *uinfo)
{
@@ -4085,10 +4613,15 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
	/* mic boost */
	if (nid == spec->input_pins[0]) {
		spec->cur_mic_boost = *valp;

		if (spec->use_alt_functions) {
			if (spec->in_enum_val != REAR_LINE_IN)
				changed = ca0132_mic_boost_set(codec, *valp);
		} else {
			/* Mic boost does not apply to Digital Mic */
			if (spec->cur_mic_type != DIGITAL_MIC)
				changed = ca0132_mic_boost_set(codec, *valp);
		}

		goto exit;
	}

@@ -4261,6 +4794,39 @@ static int add_voicefx(struct hda_codec *codec)
	return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec));
}

/*
 * Create an Output Select enumerated control for codecs with surround
 * out capabilities.
 */
static int ca0132_alt_add_output_enum(struct hda_codec *codec)
{
	struct snd_kcontrol_new knew =
		HDA_CODEC_MUTE_MONO("Output Select",
				    OUTPUT_SOURCE_ENUM, 1, 0, HDA_OUTPUT);
	knew.info = ca0132_alt_output_select_get_info;
	knew.get = ca0132_alt_output_select_get;
	knew.put = ca0132_alt_output_select_put;
	return snd_hda_ctl_add(codec, OUTPUT_SOURCE_ENUM,
				snd_ctl_new1(&knew, codec));
}

/*
 * Create an Input Source enumerated control for the alternate ca0132 codecs
 * because the front microphone has no auto-detect, and Line-in has to be set
 * somehow.
 */
static int ca0132_alt_add_input_enum(struct hda_codec *codec)
{
	struct snd_kcontrol_new knew =
		HDA_CODEC_MUTE_MONO("Input Source",
				    INPUT_SOURCE_ENUM, 1, 0, HDA_INPUT);
	knew.info = ca0132_alt_input_source_info;
	knew.get = ca0132_alt_input_source_get;
	knew.put = ca0132_alt_input_source_put;
	return snd_hda_ctl_add(codec, INPUT_SOURCE_ENUM,
				snd_ctl_new1(&knew, codec));
}

/*
 * When changing Node IDs for Mixer Controls below, make sure to update
 * Node IDs in ca0132_config() as well.
@@ -4322,6 +4888,15 @@ static int ca0132_build_controls(struct hda_codec *codec)

	add_voicefx(codec);

	/*
	 * If the codec uses alt_functions, you need the enumerated controls
	 * to select the new outputs and inputs, plus add the new mic boost
	 * setting control.
	 */
	if (spec->use_alt_functions) {
		ca0132_alt_add_output_enum(codec);
		ca0132_alt_add_input_enum(codec);
	}
#ifdef ENABLE_TUNING_CONTROLS
	add_tuning_ctls(codec);
#endif
@@ -5266,7 +5841,11 @@ static void ca0132_init_chip(struct hda_codec *codec)
	mutex_init(&spec->chipio_mutex);

	spec->cur_out_type = SPEAKER_OUT;
	if (!spec->use_alt_functions)
		spec->cur_mic_type = DIGITAL_MIC;
	else
		spec->cur_mic_type = REAR_MIC;

	spec->cur_mic_boost = 0;

	for (i = 0; i < VNODES_COUNT; i++) {
@@ -5693,15 +6272,25 @@ static int ca0132_init(struct hda_codec *codec)
			    VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20);
	}

	if (spec->quirk == QUIRK_SBZ) {
	if (spec->quirk == QUIRK_SBZ)
		ca0132_gpio_setup(codec);
		sbz_setup_defaults(codec);
	}

	snd_hda_sequence_write(codec, spec->spec_init_verbs);

	switch (spec->quirk) {
	case QUIRK_SBZ:
		sbz_setup_defaults(codec);
		ca0132_alt_select_out(codec);
		ca0132_alt_select_in(codec);
		break;
	case QUIRK_R3DI:
		ca0132_alt_select_out(codec);
		ca0132_alt_select_in(codec);
		break;
	default:
		ca0132_select_out(codec);
		ca0132_select_mic(codec);
		break;
	}

	snd_hda_jack_report_sync(codec);