|
@@ -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);
|
|
|
|