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:
parent
447fd8e9a8
commit
7cb9d94c05
1 changed files with 607 additions and 18 deletions
|
@ -50,6 +50,7 @@
|
||||||
#define FLOAT_ONE 0x3f800000
|
#define FLOAT_ONE 0x3f800000
|
||||||
#define FLOAT_TWO 0x40000000
|
#define FLOAT_TWO 0x40000000
|
||||||
#define FLOAT_THREE 0x40400000
|
#define FLOAT_THREE 0x40400000
|
||||||
|
#define FLOAT_EIGHT 0x41000000
|
||||||
#define FLOAT_MINUS_5 0xc0a00000
|
#define FLOAT_MINUS_5 0xc0a00000
|
||||||
|
|
||||||
#define UNSOL_TAG_DSP 0x16
|
#define UNSOL_TAG_DSP 0x16
|
||||||
|
@ -91,9 +92,11 @@ MODULE_FIRMWARE(R3DI_EFX_FILE);
|
||||||
|
|
||||||
static const char *dirstr[2] = { "Playback", "Capture" };
|
static const char *dirstr[2] = { "Playback", "Capture" };
|
||||||
|
|
||||||
|
#define NUM_OF_OUTPUTS 3
|
||||||
enum {
|
enum {
|
||||||
SPEAKER_OUT,
|
SPEAKER_OUT,
|
||||||
HEADPHONE_OUT
|
HEADPHONE_OUT,
|
||||||
|
SURROUND_OUT
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -101,6 +104,15 @@ enum {
|
||||||
LINE_MIC_IN
|
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 {
|
enum {
|
||||||
#define VNODE_START_NID 0x80
|
#define VNODE_START_NID 0x80
|
||||||
VNID_SPK = VNODE_START_NID, /* Speaker vnid */
|
VNID_SPK = VNODE_START_NID, /* Speaker vnid */
|
||||||
|
@ -134,7 +146,9 @@ enum {
|
||||||
VOICEFX = IN_EFFECT_END_NID,
|
VOICEFX = IN_EFFECT_END_NID,
|
||||||
PLAY_ENHANCEMENT,
|
PLAY_ENHANCEMENT,
|
||||||
CRYSTAL_VOICE,
|
CRYSTAL_VOICE,
|
||||||
EFFECT_END_NID
|
EFFECT_END_NID,
|
||||||
|
OUTPUT_SOURCE_ENUM,
|
||||||
|
INPUT_SOURCE_ENUM
|
||||||
#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID)
|
#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 {
|
enum hda_cmd_vendor_io {
|
||||||
/* for DspIO node */
|
/* for DspIO node */
|
||||||
VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000,
|
VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000,
|
||||||
|
@ -763,6 +820,9 @@ struct ca0132_spec {
|
||||||
long effects_switch[EFFECTS_COUNT];
|
long effects_switch[EFFECTS_COUNT];
|
||||||
long voicefx_val;
|
long voicefx_val;
|
||||||
long cur_mic_boost;
|
long cur_mic_boost;
|
||||||
|
/* ca0132_alt control related values */
|
||||||
|
unsigned char in_enum_val;
|
||||||
|
unsigned char out_enum_val;
|
||||||
|
|
||||||
struct hda_codec *codec;
|
struct hda_codec *codec;
|
||||||
struct delayed_work unsol_hp_work;
|
struct delayed_work unsol_hp_work;
|
||||||
|
@ -2959,6 +3019,47 @@ enum r3di_dsp_status {
|
||||||
R3DI_DSP_DOWNLOADED = 1
|
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,
|
static void r3di_gpio_dsp_status_set(struct hda_codec *codec,
|
||||||
enum r3di_dsp_status dsp_status)
|
enum r3di_dsp_status dsp_status)
|
||||||
{
|
{
|
||||||
|
@ -3550,13 +3651,209 @@ exit:
|
||||||
return err < 0 ? err : 0;
|
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)
|
static void ca0132_unsol_hp_delayed(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct ca0132_spec *spec = container_of(
|
struct ca0132_spec *spec = container_of(
|
||||||
to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
|
to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
|
||||||
struct hda_jack_tbl *jack;
|
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);
|
jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp);
|
||||||
if (jack) {
|
if (jack) {
|
||||||
jack->block_report = 0;
|
jack->block_report = 0;
|
||||||
|
@ -3661,6 +3958,122 @@ static int ca0132_select_mic(struct hda_codec *codec)
|
||||||
return 0;
|
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.
|
* 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;
|
val = 0;
|
||||||
|
|
||||||
/* If Voice Focus on SBZ, set to two channel. */
|
/* 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 -
|
if (spec->effects_switch[CRYSTAL_VOICE -
|
||||||
EFFECT_START_NID]) {
|
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
|
* For SBZ noise reduction, there's an extra command
|
||||||
* to module ID 0x47. No clue why.
|
* 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 -
|
if (spec->effects_switch[CRYSTAL_VOICE -
|
||||||
EFFECT_START_NID]) {
|
EFFECT_START_NID]) {
|
||||||
if (spec->effects_switch[NOISE_REDUCTION -
|
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);
|
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",
|
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",
|
codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n",
|
||||||
spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]);
|
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;
|
i = OUT_EFFECT_START_NID - EFFECT_START_NID;
|
||||||
nid = OUT_EFFECT_START_NID;
|
nid = OUT_EFFECT_START_NID;
|
||||||
/* PE affects all out effects */
|
/* PE affects all out effects */
|
||||||
|
@ -3892,8 +4315,12 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
|
||||||
if (nid == VNID_HP_SEL) {
|
if (nid == VNID_HP_SEL) {
|
||||||
auto_jack =
|
auto_jack =
|
||||||
spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
|
spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
|
||||||
if (!auto_jack)
|
if (!auto_jack) {
|
||||||
ca0132_select_out(codec);
|
if (spec->use_alt_functions)
|
||||||
|
ca0132_alt_select_out(codec);
|
||||||
|
else
|
||||||
|
ca0132_select_out(codec);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3906,7 +4333,10 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nid == VNID_HP_ASEL) {
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3935,6 +4365,104 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
|
||||||
}
|
}
|
||||||
/* End of control change helpers. */
|
/* 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,
|
static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
|
||||||
struct snd_ctl_elem_info *uinfo)
|
struct snd_ctl_elem_info *uinfo)
|
||||||
{
|
{
|
||||||
|
@ -4085,10 +4613,15 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
|
||||||
/* mic boost */
|
/* mic boost */
|
||||||
if (nid == spec->input_pins[0]) {
|
if (nid == spec->input_pins[0]) {
|
||||||
spec->cur_mic_boost = *valp;
|
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;
|
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));
|
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
|
* When changing Node IDs for Mixer Controls below, make sure to update
|
||||||
* Node IDs in ca0132_config() as well.
|
* Node IDs in ca0132_config() as well.
|
||||||
|
@ -4322,6 +4888,15 @@ static int ca0132_build_controls(struct hda_codec *codec)
|
||||||
|
|
||||||
add_voicefx(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
|
#ifdef ENABLE_TUNING_CONTROLS
|
||||||
add_tuning_ctls(codec);
|
add_tuning_ctls(codec);
|
||||||
#endif
|
#endif
|
||||||
|
@ -5266,7 +5841,11 @@ static void ca0132_init_chip(struct hda_codec *codec)
|
||||||
mutex_init(&spec->chipio_mutex);
|
mutex_init(&spec->chipio_mutex);
|
||||||
|
|
||||||
spec->cur_out_type = SPEAKER_OUT;
|
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;
|
spec->cur_mic_boost = 0;
|
||||||
|
|
||||||
for (i = 0; i < VNODES_COUNT; i++) {
|
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);
|
VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spec->quirk == QUIRK_SBZ) {
|
if (spec->quirk == QUIRK_SBZ)
|
||||||
ca0132_gpio_setup(codec);
|
ca0132_gpio_setup(codec);
|
||||||
sbz_setup_defaults(codec);
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_hda_sequence_write(codec, spec->spec_init_verbs);
|
snd_hda_sequence_write(codec, spec->spec_init_verbs);
|
||||||
|
switch (spec->quirk) {
|
||||||
ca0132_select_out(codec);
|
case QUIRK_SBZ:
|
||||||
ca0132_select_mic(codec);
|
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);
|
snd_hda_jack_report_sync(codec);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue