From: Connor McAdams <conmanx360@gmail.com> To: unlisted-recipients:; (no To-header on input) Cc: o-takashi@sakamocchi.jp, "Connor McAdams" <conmanx360@gmail.com>, "Jaroslav Kysela" <perex@perex.cz>, "Takashi Iwai" <tiwai@suse.com>, "Jérémy Lefaure" <jeremy.lefaure@lse.epita.fr>, alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org Subject: [PATCH v5 10/13] ALSA: hda/ca0132: add alt_select_in/out for R3Di + SBZ Date: Tue, 8 May 2018 13:20:10 -0400 [thread overview] Message-ID: <1525800015-2920-11-git-send-email-conmanx360@gmail.com> (raw) In-Reply-To: <1525800015-2920-1-git-send-email-conmanx360@gmail.com> 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> --- sound/pci/hda/patch_ca0132.c | 625 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 607 insertions(+), 18 deletions(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 878bbf7..2a42faf 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -46,6 +46,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 @@ -87,9 +88,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 { @@ -97,6 +100,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 */ @@ -130,7 +142,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) }; @@ -480,6 +494,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, @@ -759,6 +816,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; @@ -2955,6 +3015,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) { @@ -3546,13 +3647,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; - 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; @@ -3658,6 +3955,122 @@ static int ca0132_select_mic(struct hda_codec *codec) } /* + * 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. */ static bool ca0132_is_vnode_effective(struct hda_codec *codec, @@ -3739,7 +4152,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]) { @@ -3757,7 +4171,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 - @@ -3770,6 +4185,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", @@ -3797,6 +4217,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 */ @@ -3888,8 +4311,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; } @@ -3902,7 +4329,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; } @@ -3931,6 +4361,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) { @@ -4081,10 +4609,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; } @@ -4258,6 +4791,39 @@ static int add_voicefx(struct hda_codec *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. */ @@ -4318,6 +4884,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 @@ -5262,7 +5837,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++) { @@ -5689,15 +6268,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); -- 2.7.4
WARNING: multiple messages have this Message-ID (diff)
From: Connor McAdams <conmanx360@gmail.com> Cc: alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, "Takashi Iwai" <tiwai@suse.com>, "Jérémy Lefaure" <jeremy.lefaure@lse.epita.fr>, "Connor McAdams" <conmanx360@gmail.com>, o-takashi@sakamocchi.jp Subject: [PATCH v5 10/13] ALSA: hda/ca0132: add alt_select_in/out for R3Di + SBZ Date: Tue, 8 May 2018 13:20:10 -0400 [thread overview] Message-ID: <1525800015-2920-11-git-send-email-conmanx360@gmail.com> (raw) In-Reply-To: <1525800015-2920-1-git-send-email-conmanx360@gmail.com> 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> --- sound/pci/hda/patch_ca0132.c | 625 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 607 insertions(+), 18 deletions(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 878bbf7..2a42faf 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -46,6 +46,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 @@ -87,9 +88,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 { @@ -97,6 +100,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 */ @@ -130,7 +142,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) }; @@ -480,6 +494,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, @@ -759,6 +816,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; @@ -2955,6 +3015,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) { @@ -3546,13 +3647,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; - 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; @@ -3658,6 +3955,122 @@ static int ca0132_select_mic(struct hda_codec *codec) } /* + * 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. */ static bool ca0132_is_vnode_effective(struct hda_codec *codec, @@ -3739,7 +4152,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]) { @@ -3757,7 +4171,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 - @@ -3770,6 +4185,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", @@ -3797,6 +4217,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 */ @@ -3888,8 +4311,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; } @@ -3902,7 +4329,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; } @@ -3931,6 +4361,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) { @@ -4081,10 +4609,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; } @@ -4258,6 +4791,39 @@ static int add_voicefx(struct hda_codec *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. */ @@ -4318,6 +4884,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 @@ -5262,7 +5837,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++) { @@ -5689,15 +6268,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); -- 2.7.4
next prev parent reply other threads:[~2018-05-08 17:23 UTC|newest] Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top 2018-05-08 17:20 [PATCH v5 00/13] ALSA: hda/ca0132: Patch Series for Recon3Di and Sound Blaster Z Support Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 01/13] ALSA: hda/ca0132: R3Di and SBZ quirk entires + alt firmware loading Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 02/13] ALSA: hda/ca0132: Add pincfg for SBZ + R3Di, add fp hp auto-detect Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 03/13] ALSA: hda/ca0132: Add PCI region2 iomap for SBZ Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-13 7:18 ` Takashi Iwai 2018-05-08 17:20 ` [PATCH v5 04/13] ALSA: hda/ca0132: Add extra exit functions for R3Di and SBZ Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 05/13] ALSA: hda/ca0132: add extra init functions for r3di + sbz Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 06/13] ALSA: hda/ca0132: update core functions for sbz + r3di Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 07/13] ALSA: hda/ca0132: add dsp setup related commands for the sbz Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 08/13] ALSA: hda/ca0132: Add dsp setup + gpio functions for r3di Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 09/13] ALSA: hda/ca0132: add the ability to set src_id on scp commands Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` Connor McAdams [this message] 2018-05-08 17:20 ` [PATCH v5 10/13] ALSA: hda/ca0132: add alt_select_in/out for R3Di + SBZ Connor McAdams 2018-05-08 17:20 ` [PATCH v5 11/13] ALSA: hda/ca0132: Add DSP Volume set and New mixers for SBZ + R3Di Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 12/13] ALSA: hda/ca0132: add ca0132_alt_set_vipsource Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-08 17:20 ` [PATCH v5 13/13] ALSA: hda/ca0132: Add new control changes for SBZ + R3Di Connor McAdams 2018-05-08 17:20 ` Connor McAdams 2018-05-11 1:27 ` [PATCH v5 00/13] ALSA: hda/ca0132: Patch Series for Recon3Di and Sound Blaster Z Support Takashi Sakamoto 2018-05-13 7:32 ` Takashi Iwai
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1525800015-2920-11-git-send-email-conmanx360@gmail.com \ --to=conmanx360@gmail.com \ --cc=alsa-devel@alsa-project.org \ --cc=jeremy.lefaure@lse.epita.fr \ --cc=linux-kernel@vger.kernel.org \ --cc=o-takashi@sakamocchi.jp \ --cc=perex@perex.cz \ --cc=tiwai@suse.com \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.