ALSA: hda/ca0132: Init chip, DSP effects and mixer settings
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@creativelabs.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
ef6b2eada3
commit
5aaca44d8d
@ -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;
|
||||
}
|
||||
|
||||
@ -1418,6 +1444,21 @@ static int dspio_scp(struct hda_codec *codec,
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
@ -2597,6 +2638,9 @@ static bool dspload_wait_loaded(struct hda_codec *codec)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Controls stuffs.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Mixer controls helpers.
|
||||
@ -2698,6 +2742,300 @@ static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
|
||||
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = {
|
||||
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user