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: Connor McAdams <conmanx360@gmail.com>
Reviewed-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Connor McAdams 2018-05-08 13:20:10 -04:00 committed by Takashi Iwai
parent 447fd8e9a8
commit 7cb9d94c05

View File

@ -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 @@ exit:
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;
ca0132_select_out(spec->codec);
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)
ca0132_select_out(codec);
if (!auto_jack) {
if (spec->use_alt_functions)
ca0132_alt_select_out(codec);
else
ca0132_select_out(codec);
}
return 1;
}
@ -3906,7 +4333,10 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
}
if (nid == VNID_HP_ASEL) {
ca0132_select_out(codec);
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);
}
/* 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;
spec->cur_mic_type = DIGITAL_MIC;
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);
ca0132_select_out(codec);
ca0132_select_mic(codec);
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);