[alsa-devel] [PATCHv2 2/9] ALSA: CA0132: Init chip, DSP effects and mixer settings

Ian Minett ian_minett at creativelabs.com
Fri Dec 21 03:53:34 CET 2012


From: Ian Minett <ian_minett at creativelabs.com>

This patch adds the framework to set effect parameters: ca0132_effects_set() 
and ca0132_setup_defaults() are general functions for parameter setting and
initializing to default values. dspio_set_param() and dspio_set_uint_param() 
are lower-level fns to simplify setting individual DSP parameters via an
SCP buffer transfer to the firmware.
The CA0132 chip parameter init code is added in ca0132_init_params().
In chipio_[write,read]_data(), the current chip address is auto-incremented
if no error has occurred.
ca0132_select_out() selects the current output. If autodetect is enabled, 
use headphones (if jack detected) or speakers (if no jack).
ca0132_select_mic() selects the current mic in. If autodetect is enabled, 
use exterior mic (if jack detected) or built-in mic (if no jack).
Init digital mic and switch between dmic and amic with ca0132_init_dmic(),
ca0132_set_dmic(). amic2 is initialized in ca0132_init_analog_mic2().
Finally, add verb tables for configuring DSP firmware.

Signed-off-by: Ian Minett <ian_minett at creativelabs.com>

diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 38ac07b..e4e1684 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -32,6 +32,7 @@
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_auto_parser.h"
+#include "hda_jack.h"
 
 #include "ca0132_regs.h"
 
@@ -672,22 +673,11 @@ enum ca0132_sample_rate {
 	SR_RATE_UNKNOWN = 0x1F
 };
 
-/*
- *  Scp Helper function
- */
-enum get_set {
-	IS_SET = 0,
-	IS_GET = 1,
-};
-
-/*
- * Duplicated from ca0110 codec
- */
-
 static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 {
 	if (pin) {
-		snd_hda_set_pin_ctl(codec, pin, PIN_HP);
+		snd_hda_codec_write(codec, pin, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
 		if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
 			snd_hda_codec_write(codec, pin, 0,
 					    AC_VERB_SET_AMP_GAIN_MUTE,
@@ -701,16 +691,23 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
 {
 	if (pin) {
-		snd_hda_set_pin_ctl(codec, pin, PIN_IN |
-				    snd_hda_get_default_vref(codec, pin));
+		snd_hda_codec_write(codec, pin, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
 		if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
 			snd_hda_codec_write(codec, pin, 0,
 					    AC_VERB_SET_AMP_GAIN_MUTE,
 					    AMP_IN_UNMUTE(0));
 	}
-	if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP))
+	if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) {
 		snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
 				    AMP_IN_UNMUTE(0));
+
+		/* init to 0 dB and unmute. */
+		snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+					 HDA_AMP_VOLMASK, 0x5a);
+		snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+					 HDA_AMP_MUTE, 0);
+	}
 }
 
 static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
@@ -774,11 +771,18 @@ enum dsp_download_state {
  */
 
 struct ca0132_spec {
+	const struct hda_verb *base_init_verbs;
+	const struct hda_verb *base_exit_verbs;
+	const struct hda_verb *init_verbs[5];
+	unsigned int num_init_verbs;  /* exclude base init verbs */
 	struct auto_pin_cfg autocfg;
+
+	/* Nodes configurations */
 	struct hda_multi_out multiout;
 	hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
 	hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
 	hda_nid_t hp_dac;
+	unsigned int num_outputs;
 	hda_nid_t input_pins[AUTO_PIN_LAST];
 	hda_nid_t adcs[AUTO_PIN_LAST];
 	hda_nid_t dig_out;
@@ -788,7 +792,7 @@ struct ca0132_spec {
 	long curr_hp_volume[2];
 	long curr_speaker_switch;
 	const char *input_labels[AUTO_PIN_LAST];
-	struct hda_pcm pcm_rec[2]; /* PCM information */
+	struct hda_pcm pcm_rec[5]; /* PCM information */
 
 	/* chip access */
 	struct mutex chipio_mutex; /* chip access mutex */
@@ -803,6 +807,18 @@ struct ca0132_spec {
 	unsigned int scp_resp_header;
 	unsigned int scp_resp_data[4];
 	unsigned int scp_resp_count;
+
+	/* mixer and effects related */
+	unsigned char dmic_ctl;
+	int cur_out_type;
+	int cur_mic_type;
+	long vnode_lvol[VNODES_COUNT];
+	long vnode_rvol[VNODES_COUNT];
+	long vnode_lswitch[VNODES_COUNT];
+	long vnode_rswitch[VNODES_COUNT];
+	long effects_switch[EFFECTS_COUNT];
+	long voicefx_val;
+	long cur_mic_boost;
 };
 
 /*
@@ -886,6 +902,7 @@ static int chipio_write_address(struct hda_codec *codec,
  */
 static int chipio_write_data(struct hda_codec *codec, unsigned int data)
 {
+	struct ca0132_spec *spec = codec->spec;
 	int res;
 
 	/* send low 16 bits of the data */
@@ -897,6 +914,10 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
 				  data >> 16);
 	}
 
+	/*If no error encountered, automatically increment the address
+	as per chip behaviour*/
+	spec->curr_chip_addx = (res != -EIO) ?
+					(spec->curr_chip_addx + 4) : ~0UL;
 	return res;
 }
 
@@ -926,6 +947,7 @@ static int chipio_write_data_multiple(struct hda_codec *codec,
  */
 static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
 {
+	struct ca0132_spec *spec = codec->spec;
 	int res;
 
 	/* post read */
@@ -943,6 +965,10 @@ static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
 					   0);
 	}
 
+	/*If no error encountered, automatically increment the address
+	as per chip behaviour*/
+	spec->curr_chip_addx = (res != -EIO) ?
+					(spec->curr_chip_addx + 4) : ~0UL;
 	return res;
 }
 
@@ -1419,6 +1445,21 @@ static int dspio_scp(struct hda_codec *codec,
 }
 
 /*
+ * Set DSP parameters
+ */
+static int dspio_set_param(struct hda_codec *codec, int mod_id,
+			int req, void *data, unsigned int len)
+{
+	return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL);
+}
+
+static int dspio_set_uint_param(struct hda_codec *codec, int mod_id,
+			int req, unsigned int data)
+{
+	return dspio_set_param(codec, mod_id, req, &data, sizeof(unsigned int));
+}
+
+/*
  * Allocate a DSP DMA channel via an SCP message
  */
 static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
@@ -2597,6 +2638,9 @@ static bool dspload_wait_loaded(struct hda_codec *codec)
 	return false;
 }
 
+/*
+ * Controls stuffs.
+ */
 
 /*
  * Mixer controls helpers.
@@ -2699,6 +2743,300 @@ static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 }
 
 /*
+ * Select the active output.
+ * If autodetect is enabled, output will be selected based on jack detection.
+ * If jack inserted, headphone will be selected, else built-in speakers
+ * If autodetect is disabled, output will be selected based on selection.
+ */
+static int ca0132_select_out(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+	unsigned int pin_ctl;
+	int jack_present;
+	int auto_jack;
+	unsigned int tmp;
+	int err;
+
+	snd_printdd(KERN_INFO "ca0132_select_out\n");
+
+	snd_hda_power_up(codec);
+
+	auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+	if (auto_jack)
+		jack_present = snd_hda_jack_detect(codec, spec->out_pins[1]);
+	else
+		jack_present =
+			spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID];
+
+	if (jack_present)
+		spec->cur_out_type = HEADPHONE_OUT;
+	else
+		spec->cur_out_type = SPEAKER_OUT;
+
+	if (spec->cur_out_type == SPEAKER_OUT) {
+		snd_printdd(KERN_INFO "ca0132_select_out speaker\n");
+		/*speaker out config*/
+		tmp = FLOAT_ONE;
+		err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+		if (err < 0)
+			goto exit;
+		/*enable speaker EQ*/
+		tmp = FLOAT_ONE;
+		err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+		if (err < 0)
+			goto exit;
+
+		/* Setup EAPD */
+		snd_hda_codec_write(codec, spec->out_pins[1], 0,
+				    VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+				    AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+				    VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+
+		/* disable headphone node */
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_codec_write(codec, spec->out_pins[1], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    pin_ctl & 0xBF);
+		/* enable speaker node */
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+					     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    pin_ctl | 0x40);
+	} else {
+		snd_printdd(KERN_INFO "ca0132_select_out hp\n");
+		/*headphone out config*/
+		tmp = FLOAT_ZERO;
+		err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+		if (err < 0)
+			goto exit;
+		/*disable speaker EQ*/
+		tmp = FLOAT_ZERO;
+		err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+		if (err < 0)
+			goto exit;
+
+		/* Setup EAPD */
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+				    VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+				    AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+		snd_hda_codec_write(codec, spec->out_pins[1], 0,
+				    VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+
+		/* disable speaker*/
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_codec_write(codec, spec->out_pins[0], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    pin_ctl & 0xBF);
+		/* enable headphone*/
+		pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+		snd_hda_codec_write(codec, spec->out_pins[1], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    pin_ctl | 0x40);
+	}
+
+exit:
+	snd_hda_power_down(codec);
+
+	return err < 0 ? err : 0;
+}
+
+static void ca0132_set_dmic(struct hda_codec *codec, int enable);
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val);
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val);
+
+/*
+ * Select the active VIP source
+ */
+static int ca0132_set_vipsource(struct hda_codec *codec, int val)
+{
+	struct ca0132_spec *spec = codec->spec;
+	unsigned int tmp;
+
+	if (!dspload_is_loaded(codec))
+		return 0;
+
+	/* if CrystalVoice if off, vipsource should be 0 */
+	if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ||
+	    (val == 0)) {
+		chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+		chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+		chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+		if (spec->cur_mic_type == DIGITAL_MIC)
+			tmp = FLOAT_TWO;
+		else
+			tmp = FLOAT_ONE;
+		dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+		tmp = FLOAT_ZERO;
+		dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+	} else {
+		chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+		chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+		if (spec->cur_mic_type == DIGITAL_MIC)
+			tmp = FLOAT_TWO;
+		else
+			tmp = FLOAT_ONE;
+		dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+		tmp = FLOAT_ONE;
+		dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+		msleep(20);
+		chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val);
+	}
+
+	return 1;
+}
+
+/*
+ * Select the active microphone.
+ * If autodetect is enabled, mic will be selected based on jack detection.
+ * If jack inserted, ext.mic will be selected, else built-in mic
+ * If autodetect is disabled, mic will be selected based on selection.
+ */
+static int ca0132_select_mic(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int jack_present;
+	int auto_jack;
+
+	snd_printdd(KERN_INFO "ca0132_select_mic\n");
+
+	snd_hda_power_up(codec);
+
+	auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
+
+	if (auto_jack)
+		jack_present = snd_hda_jack_detect(codec, spec->input_pins[0]);
+	else
+		jack_present =
+			spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID];
+
+	if (jack_present)
+		spec->cur_mic_type = LINE_MIC_IN;
+	else
+		spec->cur_mic_type = DIGITAL_MIC;
+
+	if (spec->cur_mic_type == DIGITAL_MIC) {
+		/* enable digital Mic */
+		chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_32_000);
+		ca0132_set_dmic(codec, 1);
+		ca0132_mic_boost_set(codec, 0);
+		/* set voice focus */
+		ca0132_effects_set(codec, VOICE_FOCUS,
+				   spec->effects_switch
+				   [VOICE_FOCUS - EFFECT_START_NID]);
+	} else {
+		/* disable digital Mic */
+		chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_96_000);
+		ca0132_set_dmic(codec, 0);
+		ca0132_mic_boost_set(codec, spec->cur_mic_boost);
+		/* disable voice focus */
+		ca0132_effects_set(codec, VOICE_FOCUS, 0);
+	}
+
+	snd_hda_power_down(codec);
+
+	return 0;
+}
+
+/*
+ * Set the effects parameters
+ */
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
+{
+	struct ca0132_spec *spec = codec->spec;
+	unsigned int on;
+	int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+	int err = 0;
+	int idx = nid - EFFECT_START_NID;
+
+	if ((idx < 0) || (idx >= num_fx))
+		return 0; /* no changed */
+
+	/* for out effect, qualify with PE */
+	if ((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) {
+		/* if PE if off, turn off out effects. */
+		if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+			val = 0;
+	}
+
+	/* for in effect, qualify with CrystalVoice */
+	if ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID)) {
+		/* if CrystalVoice if off, turn off in effects. */
+		if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+			val = 0;
+
+		/* Voice Focus applies to 2-ch Mic, Digital Mic */
+		if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC))
+			val = 0;
+	}
+
+	snd_printdd(KERN_INFO, "ca0132_effect_set: nid=0x%x, val=%ld\n",
+		    nid, val);
+
+	on = (val == 0) ? FLOAT_ZERO : FLOAT_ONE;
+	err = dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+				   ca0132_effects[idx].reqs[0], on);
+
+	if (err < 0)
+		return 0; /* no changed */
+
+	return 1;
+}
+
+/* Check if Mic1 is streaming, if so, stop streaming */
+static int stop_mic1(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+	unsigned int oldval = snd_hda_codec_read(codec, spec->adcs[0], 0,
+						 AC_VERB_GET_CONV, 0);
+	if (oldval != 0)
+		snd_hda_codec_write(codec, spec->adcs[0], 0,
+				    AC_VERB_SET_CHANNEL_STREAMID,
+				    0);
+	return oldval;
+}
+
+/* Resume Mic1 streaming if it was stopped. */
+static void resume_mic1(struct hda_codec *codec, unsigned int oldval)
+{
+	struct ca0132_spec *spec = codec->spec;
+	/* Restore the previous stream and channel */
+	if (oldval != 0)
+		snd_hda_codec_write(codec, spec->adcs[0], 0,
+				    AC_VERB_SET_CHANNEL_STREAMID,
+				    oldval);
+}
+
+/*
+ * Set Mic Boost
+ */
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int ret = 0;
+
+	if (val) /* on */
+		ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+					HDA_INPUT, 0, HDA_AMP_VOLMASK, 3);
+	else /* off */
+		ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+					HDA_INPUT, 0, HDA_AMP_VOLMASK, 0);
+
+	return ret;
+}
+
+/*
  */
 static struct hda_pcm_stream ca0132_pcm_analog_playback = {
 	.substreams = 1,
@@ -2892,7 +3230,7 @@ static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol,
 
 	/* any change? */
 	if ((spec->curr_hp_volume[0] == left_vol) &&
-		(spec->curr_hp_volume[1] == right_vol))
+	    (spec->curr_hp_volume[1] == right_vol))
 		return 0;
 
 	snd_hda_power_up(codec);
@@ -2925,7 +3263,7 @@ static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct snd_kcontrol_new knew =
 		HDA_CODEC_MUTE_MONO("Headphone Playback Switch",
-				     nid, 1, 0, HDA_OUTPUT);
+				    nid, 1, 0, HDA_OUTPUT);
 	knew.get = ca0132_hp_switch_get;
 	knew.put = ca0132_hp_switch_put;
 	return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
@@ -2935,7 +3273,7 @@ static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct snd_kcontrol_new knew =
 		HDA_CODEC_VOLUME_MONO("Headphone Playback Volume",
-				       nid, 3, 0, HDA_OUTPUT);
+				      nid, 3, 0, HDA_OUTPUT);
 	knew.get = ca0132_hp_volume_get;
 	knew.put = ca0132_hp_volume_put;
 	return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
@@ -2945,7 +3283,7 @@ static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct snd_kcontrol_new knew =
 		HDA_CODEC_MUTE_MONO("Speaker Playback Switch",
-				     nid, 1, 0, HDA_OUTPUT);
+				    nid, 1, 0, HDA_OUTPUT);
 	knew.get = ca0132_speaker_switch_get;
 	knew.put = ca0132_speaker_switch_put;
 	return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
@@ -3021,6 +3359,215 @@ static int ca0132_build_controls(struct hda_codec *codec)
 	return 0;
 }
 
+static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+	unsigned int caps;
+
+	caps = snd_hda_param_read(codec, nid, dir == HDA_OUTPUT ?
+				  AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+	snd_hda_override_amp_caps(codec, nid, dir, caps);
+}
+
+/*
+ * Switch between Digital built-in mic and analog mic.
+ */
+static void ca0132_set_dmic(struct hda_codec *codec, int enable)
+{
+	struct ca0132_spec *spec = codec->spec;
+	unsigned int tmp;
+	u8 val;
+	unsigned int oldval;
+
+	snd_printdd(KERN_INFO "ca0132_set_dmic: enable=%d\n", enable);
+
+	oldval = stop_mic1(codec);
+	ca0132_set_vipsource(codec, 0);
+	if (enable) {
+		/* set DMic input as 2-ch */
+		tmp = FLOAT_TWO;
+		dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+		val = spec->dmic_ctl;
+		val |= 0x80;
+		snd_hda_codec_write(codec, spec->input_pins[0], 0,
+				    VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+		if (!(spec->dmic_ctl & 0x20))
+			chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 1);
+	} else {
+		/* set AMic input as mono */
+		tmp = FLOAT_ONE;
+		dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+		val = spec->dmic_ctl;
+		/* clear bit7 and bit5 to disable dmic */
+		val &= 0x5f;
+		snd_hda_codec_write(codec, spec->input_pins[0], 0,
+				    VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+		if (!(spec->dmic_ctl & 0x20))
+			chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 0);
+	}
+	ca0132_set_vipsource(codec, 1);
+	resume_mic1(codec, oldval);
+}
+
+/*
+ * Initialization for Digital Mic.
+ */
+static void ca0132_init_dmic(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+	u8 val;
+
+	/* Setup Digital Mic here, but don't enable.
+	 * Enable based on jack detect.
+	 */
+
+	/* MCLK uses MPIO1, set to enable.
+	 * Bit 2-0: MPIO select
+	 * Bit   3: set to disable
+	 * Bit 7-4: reserved
+	 */
+	val = 0x01;
+	snd_hda_codec_write(codec, spec->input_pins[0], 0,
+			    VENDOR_CHIPIO_DMIC_MCLK_SET, val);
+
+	/* Data1 uses MPIO3. Data2 not use
+	 * Bit 2-0: Data1 MPIO select
+	 * Bit   3: set disable Data1
+	 * Bit 6-4: Data2 MPIO select
+	 * Bit   7: set disable Data2
+	 */
+	val = 0x83;
+	snd_hda_codec_write(codec, spec->input_pins[0], 0,
+			    VENDOR_CHIPIO_DMIC_PIN_SET, val);
+
+	/* Use Ch-0 and Ch-1. Rate is 48K, mode 1. Disable DMic first.
+	 * Bit 3-0: Channel mask
+	 * Bit   4: set for 48KHz, clear for 32KHz
+	 * Bit   5: mode
+	 * Bit   6: set to select Data2, clear for Data1
+	 * Bit   7: set to enable DMic, clear for AMic
+	 */
+	val = 0x23;
+	/* keep a copy of dmic ctl val for enable/disable dmic purpuse */
+	spec->dmic_ctl = val;
+	snd_hda_codec_write(codec, spec->input_pins[0], 0,
+			    VENDOR_CHIPIO_DMIC_CTL_SET, val);
+}
+
+/*
+ * Initialization for Analog Mic 2
+ */
+static void ca0132_init_analog_mic2(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+
+	mutex_lock(&spec->chipio_mutex);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x2D);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
+	mutex_unlock(&spec->chipio_mutex);
+}
+
+static void ca0132_refresh_widget_caps(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int i;
+	hda_nid_t nid;
+
+	snd_printdd(KERN_INFO "ca0132_refresh_widget_caps.\n");
+	nid = codec->start_nid;
+	for (i = 0; i < codec->num_nodes; i++, nid++)
+		codec->wcaps[i] = snd_hda_param_read(codec, nid,
+						     AC_PAR_AUDIO_WIDGET_CAP);
+
+	for (i = 0; i < spec->multiout.num_dacs; i++)
+		refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT);
+
+	for (i = 0; i < spec->num_outputs; i++)
+		refresh_amp_caps(codec, spec->out_pins[i], HDA_OUTPUT);
+
+	for (i = 0; i < spec->num_inputs; i++) {
+		refresh_amp_caps(codec, spec->adcs[i], HDA_INPUT);
+		refresh_amp_caps(codec, spec->input_pins[i], HDA_INPUT);
+	}
+}
+
+/*
+ * Setup default parameters for DSP
+ */
+static void ca0132_setup_defaults(struct hda_codec *codec)
+{
+	unsigned int tmp;
+	int num_fx;
+	int idx, i;
+
+	if (!dspload_is_loaded(codec))
+		return;
+
+	/* out, in effects + voicefx */
+	num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+	for (idx = 0; idx < num_fx; idx++) {
+		for (i = 0; i <= ca0132_effects[idx].params; i++) {
+			dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+					     ca0132_effects[idx].reqs[i],
+					     ca0132_effects[idx].def_vals[i]);
+		}
+	}
+
+	/*remove DSP headroom*/
+	tmp = FLOAT_ZERO;
+	dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+	/*set speaker EQ bypass attenuation*/
+	dspio_set_uint_param(codec, 0x8f, 0x01, tmp);
+
+	/* set AMic1 and AMic2 as mono mic */
+	tmp = FLOAT_ONE;
+	dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+	dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+
+	/* set AMic1 as CrystalVoice input */
+	tmp = FLOAT_ONE;
+	dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+	/* set WUH source */
+	tmp = FLOAT_TWO;
+	dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+}
+
+/*
+ * Initialization of flags in chip
+ */
+static void ca0132_init_flags(struct hda_codec *codec)
+{
+	chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+	chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
+	chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
+	chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
+	chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+	chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
+}
+
+/*
+ * Initialization of parameters in chip
+ */
+static void ca0132_init_params(struct hda_codec *codec)
+{
+	chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6);
+	chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6);
+}
 
 static void ca0132_set_ct_ext(struct hda_codec *codec, int enable)
 {
@@ -3038,7 +3585,6 @@ static void ca0132_config(struct hda_codec *codec)
 	struct ca0132_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 
-	codec->pcm_format_first = 1;
 	codec->no_sticky_stream = 1;
 
 	/* line-outs */
@@ -3088,16 +3634,115 @@ static void ca0132_config(struct hda_codec *codec)
 	cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
 }
 
+/*
+ * Verbs tables.
+ */
+
+/* Sends before DSP download. */
+static struct hda_verb ca0132_base_init_verbs[] = {
+	/*enable ct extension*/
+	{0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1},
+	/*enable DSP node unsol, needed for DSP download*/
+	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_DSP},
+	{}
+};
+
+/* Send at exit. */
+static struct hda_verb ca0132_base_exit_verbs[] = {
+	/*set afg to D3*/
+	{0x01, AC_VERB_SET_POWER_STATE, 0x03},
+	/*disable ct extension*/
+	{0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0},
+	{}
+};
+
+/* Other verbs tables.  Sends after DSP download. */
+static struct hda_verb ca0132_init_verbs0[] = {
+	/* chip init verbs */
+	{0x15, 0x70D, 0xF0},
+	{0x15, 0x70E, 0xFE},
+	{0x15, 0x707, 0x75},
+	{0x15, 0x707, 0xD3},
+	{0x15, 0x707, 0x09},
+	{0x15, 0x707, 0x53},
+	{0x15, 0x707, 0xD4},
+	{0x15, 0x707, 0xEF},
+	{0x15, 0x707, 0x75},
+	{0x15, 0x707, 0xD3},
+	{0x15, 0x707, 0x09},
+	{0x15, 0x707, 0x02},
+	{0x15, 0x707, 0x37},
+	{0x15, 0x707, 0x78},
+	{0x15, 0x53C, 0xCE},
+	{0x15, 0x575, 0xC9},
+	{0x15, 0x53D, 0xCE},
+	{0x15, 0x5B7, 0xC9},
+	{0x15, 0x70D, 0xE8},
+	{0x15, 0x70E, 0xFE},
+	{0x15, 0x707, 0x02},
+	{0x15, 0x707, 0x68},
+	{0x15, 0x707, 0x62},
+	{0x15, 0x53A, 0xCE},
+	{0x15, 0x546, 0xC9},
+	{0x15, 0x53B, 0xCE},
+	{0x15, 0x5E8, 0xC9},
+	{0x15, 0x717, 0x0D},
+	{0x15, 0x718, 0x20},
+	{}
+};
+
+static struct hda_verb ca0132_init_verbs1[] = {
+	{0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_HP},
+	{0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_AMIC1},
+	/* config EAPD */
+	{0x0b, 0x78D, 0x00},
+	/*{0x0b, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/
+	/*{0x10, 0x78D, 0x02},*/
+	/*{0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/
+	{}
+};
+
 static void ca0132_init_chip(struct hda_codec *codec)
 {
 	struct ca0132_spec *spec = codec->spec;
+	int num_fx;
+	int i;
+	unsigned int on;
 
 	mutex_init(&spec->chipio_mutex);
+
+	spec->cur_out_type = SPEAKER_OUT;
+	spec->cur_mic_type = DIGITAL_MIC;
+	spec->cur_mic_boost = 0;
+
+	for (i = 0; i < VNODES_COUNT; i++) {
+		spec->vnode_lvol[i] = 0x5a;
+		spec->vnode_rvol[i] = 0x5a;
+		spec->vnode_lswitch[i] = 0;
+		spec->vnode_rswitch[i] = 0;
+	}
+
+	/*
+	 * Default states for effects are in ca0132_effects[].
+	 */
+	num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+	for (i = 0; i < num_fx; i++) {
+		on = (unsigned int)ca0132_effects[i].reqs[0];
+		spec->effects_switch[i] = on ? 1 : 0;
+	}
+
+	spec->voicefx_val = 0;
+	spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1;
+	spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0;
+
 }
 
 static void ca0132_exit_chip(struct hda_codec *codec)
 {
 	/* put any chip cleanup stuffs here. */
+
+	if (dspload_is_loaded(codec))
+		dsp_reset(codec);
 }
 
 static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
@@ -3155,15 +3800,25 @@ static int ca0132_init(struct hda_codec *codec)
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	int i;
 
+	spec->dsp_state = DSP_DOWNLOAD_INIT;
+	spec->curr_chip_addx = (unsigned int)INVALID_CHIP_ADDRESS;
+
+	snd_hda_power_up(codec);
+
+	ca0132_init_params(codec);
+	ca0132_init_flags(codec);
+	snd_hda_sequence_write(codec, spec->base_init_verbs);
 #ifdef CONFIG_SND_HDA_DSP_LOADER
 	ca0132_download_dsp(codec);
 #endif
+	ca0132_refresh_widget_caps(codec);
+	ca0132_setup_defaults(codec);
+	ca0132_init_analog_mic2(codec);
+	ca0132_init_dmic(codec);
+
+	for (i = 0; i < spec->num_outputs; i++)
+		init_output(codec, spec->out_pins[i], spec->dacs[0]);
 
-	for (i = 0; i < spec->multiout.num_dacs; i++) {
-		init_output(codec, spec->out_pins[i],
-			    spec->multiout.dac_nids[i]);
-	}
-	init_output(codec, cfg->hp_pins[0], spec->hp_dac);
 	init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
 
 	for (i = 0; i < spec->num_inputs; i++)
@@ -3171,16 +3826,25 @@ static int ca0132_init(struct hda_codec *codec)
 
 	init_input(codec, cfg->dig_in_pin, spec->dig_in);
 
-	ca0132_set_ct_ext(codec, 1);
+	for (i = 0; i < spec->num_init_verbs; i++)
+		snd_hda_sequence_write(codec, spec->init_verbs[i]);
+
+	ca0132_select_out(codec);
+	ca0132_select_mic(codec);
+
+	snd_hda_power_down(codec);
 
 	return 0;
 }
 
-
 static void ca0132_free(struct hda_codec *codec)
 {
-	ca0132_set_ct_ext(codec, 0);
+	struct ca0132_spec *spec = codec->spec;
+
+	snd_hda_power_up(codec);
+	snd_hda_sequence_write(codec, spec->base_exit_verbs);
 	ca0132_exit_chip(codec);
+	snd_hda_power_down(codec);
 	kfree(codec->spec);
 }
 
@@ -3191,8 +3855,6 @@ static struct hda_codec_ops ca0132_patch_ops = {
 	.free = ca0132_free,
 };
 
-
-
 static int patch_ca0132(struct hda_codec *codec)
 {
 	struct ca0132_spec *spec;
@@ -3204,6 +3866,12 @@ static int patch_ca0132(struct hda_codec *codec)
 		return -ENOMEM;
 	codec->spec = spec;
 
+	spec->base_init_verbs = ca0132_base_init_verbs;
+	spec->base_exit_verbs = ca0132_base_exit_verbs;
+	spec->init_verbs[0] = ca0132_init_verbs0;
+	spec->init_verbs[1] = ca0132_init_verbs1;
+	spec->num_init_verbs = 2;
+
 	ca0132_init_chip(codec);
 
 	ca0132_config(codec);
-- 
1.7.4.1



More information about the Alsa-devel mailing list