ALSA: hda/ca0132: Add DSP Volume set and New mixers for SBZ + R3Di
Adds lookup table for floating point decibel volume, and new functions to allow for setting the decibel level on the DSP. Signed-off-by: Connor McAdams <conmanx360@gmail.com> Reviewed-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
7cb9d94c05
commit
017310fbe7
@ -541,6 +541,31 @@ static const struct ca0132_alt_out_set alt_out_presets[] = {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* DSP volume setting structs. Req 1 is left volume, req 2 is right volume,
|
||||
* and I don't know what the third req is, but it's always zero. I assume it's
|
||||
* some sort of update or set command to tell the DSP there's new volume info.
|
||||
*/
|
||||
#define DSP_VOL_OUT 0
|
||||
#define DSP_VOL_IN 1
|
||||
|
||||
struct ct_dsp_volume_ctl {
|
||||
hda_nid_t vnid;
|
||||
int mid; /* module ID*/
|
||||
unsigned int reqs[3]; /* scp req ID */
|
||||
};
|
||||
|
||||
static struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = {
|
||||
{ .vnid = VNID_SPK,
|
||||
.mid = 0x32,
|
||||
.reqs = {3, 4, 2}
|
||||
},
|
||||
{ .vnid = VNID_MIC,
|
||||
.mid = 0x37,
|
||||
.reqs = {2, 3, 1}
|
||||
}
|
||||
};
|
||||
|
||||
enum hda_cmd_vendor_io {
|
||||
/* for DspIO node */
|
||||
VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000,
|
||||
@ -3252,6 +3277,24 @@ static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
|
||||
.tlv = { .c = ca0132_volume_tlv }, \
|
||||
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
|
||||
|
||||
/*
|
||||
* Creates a mixer control that uses defaults of HDA_CODEC_VOL except for the
|
||||
* volume put, which is used for setting the DSP volume. This was done because
|
||||
* the ca0132 functions were taking too much time and causing lag.
|
||||
*/
|
||||
#define CA0132_ALT_CODEC_VOL_MONO(xname, nid, channel, dir) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = xname, \
|
||||
.subdevice = HDA_SUBDEV_AMP_FLAG, \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
|
||||
.info = snd_hda_mixer_amp_volume_info, \
|
||||
.get = snd_hda_mixer_amp_volume_get, \
|
||||
.put = ca0132_alt_volume_put, \
|
||||
.tlv = { .c = snd_hda_mixer_amp_tlv }, \
|
||||
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
|
||||
|
||||
#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = xname, \
|
||||
@ -3264,9 +3307,40 @@ static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
|
||||
/* stereo */
|
||||
#define CA0132_CODEC_VOL(xname, nid, dir) \
|
||||
CA0132_CODEC_VOL_MONO(xname, nid, 3, dir)
|
||||
#define CA0132_ALT_CODEC_VOL(xname, nid, dir) \
|
||||
CA0132_ALT_CODEC_VOL_MONO(xname, nid, 3, dir)
|
||||
#define CA0132_CODEC_MUTE(xname, nid, dir) \
|
||||
CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
|
||||
|
||||
/* lookup tables */
|
||||
/*
|
||||
* Lookup table with decibel values for the DSP. When volume is changed in
|
||||
* Windows, the DSP is also sent the dB value in floating point. In Windows,
|
||||
* these values have decimal points, probably because the Windows driver
|
||||
* actually uses floating point. We can't here, so I made a lookup table of
|
||||
* values -90 to 9. -90 is the lowest decibel value for both the ADC's and the
|
||||
* DAC's, and 9 is the maximum.
|
||||
*/
|
||||
static const unsigned int float_vol_db_lookup[] = {
|
||||
0xC2B40000, 0xC2B20000, 0xC2B00000, 0xC2AE0000, 0xC2AC0000, 0xC2AA0000,
|
||||
0xC2A80000, 0xC2A60000, 0xC2A40000, 0xC2A20000, 0xC2A00000, 0xC29E0000,
|
||||
0xC29C0000, 0xC29A0000, 0xC2980000, 0xC2960000, 0xC2940000, 0xC2920000,
|
||||
0xC2900000, 0xC28E0000, 0xC28C0000, 0xC28A0000, 0xC2880000, 0xC2860000,
|
||||
0xC2840000, 0xC2820000, 0xC2800000, 0xC27C0000, 0xC2780000, 0xC2740000,
|
||||
0xC2700000, 0xC26C0000, 0xC2680000, 0xC2640000, 0xC2600000, 0xC25C0000,
|
||||
0xC2580000, 0xC2540000, 0xC2500000, 0xC24C0000, 0xC2480000, 0xC2440000,
|
||||
0xC2400000, 0xC23C0000, 0xC2380000, 0xC2340000, 0xC2300000, 0xC22C0000,
|
||||
0xC2280000, 0xC2240000, 0xC2200000, 0xC21C0000, 0xC2180000, 0xC2140000,
|
||||
0xC2100000, 0xC20C0000, 0xC2080000, 0xC2040000, 0xC2000000, 0xC1F80000,
|
||||
0xC1F00000, 0xC1E80000, 0xC1E00000, 0xC1D80000, 0xC1D00000, 0xC1C80000,
|
||||
0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
|
||||
0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
|
||||
0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
|
||||
0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
|
||||
0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
|
||||
0x40C00000, 0x40E00000, 0x41000000, 0x41100000
|
||||
};
|
||||
|
||||
/* The following are for tuning of products */
|
||||
#ifdef ENABLE_TUNING_CONTROLS
|
||||
|
||||
@ -4633,6 +4707,41 @@ exit:
|
||||
/*
|
||||
* Volume related
|
||||
*/
|
||||
/*
|
||||
* Sets the internal DSP decibel level to match the DAC for output, and the
|
||||
* ADC for input. Currently only the SBZ sets dsp capture volume level, and
|
||||
* all alternative codecs set DSP playback volume.
|
||||
*/
|
||||
static void ca0132_alt_dsp_volume_put(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
struct ca0132_spec *spec = codec->spec;
|
||||
unsigned int dsp_dir;
|
||||
unsigned int lookup_val;
|
||||
|
||||
if (nid == VNID_SPK)
|
||||
dsp_dir = DSP_VOL_OUT;
|
||||
else
|
||||
dsp_dir = DSP_VOL_IN;
|
||||
|
||||
lookup_val = spec->vnode_lvol[nid - VNODE_START_NID];
|
||||
|
||||
dspio_set_uint_param(codec,
|
||||
ca0132_alt_vol_ctls[dsp_dir].mid,
|
||||
ca0132_alt_vol_ctls[dsp_dir].reqs[0],
|
||||
float_vol_db_lookup[lookup_val]);
|
||||
|
||||
lookup_val = spec->vnode_rvol[nid - VNODE_START_NID];
|
||||
|
||||
dspio_set_uint_param(codec,
|
||||
ca0132_alt_vol_ctls[dsp_dir].mid,
|
||||
ca0132_alt_vol_ctls[dsp_dir].reqs[1],
|
||||
float_vol_db_lookup[lookup_val]);
|
||||
|
||||
dspio_set_uint_param(codec,
|
||||
ca0132_alt_vol_ctls[dsp_dir].mid,
|
||||
ca0132_alt_vol_ctls[dsp_dir].reqs[2], FLOAT_ZERO);
|
||||
}
|
||||
|
||||
static int ca0132_volume_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
@ -4734,6 +4843,51 @@ static int ca0132_volume_put(struct snd_kcontrol *kcontrol,
|
||||
return changed;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the same as the one above, because using an if statement
|
||||
* inside of the above volume control for the DSP volume would cause too much
|
||||
* lag. This is a lot more smooth.
|
||||
*/
|
||||
static int ca0132_alt_volume_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;
|
||||
hda_nid_t nid = get_amp_nid(kcontrol);
|
||||
int ch = get_amp_channels(kcontrol);
|
||||
long *valp = ucontrol->value.integer.value;
|
||||
hda_nid_t vnid = 0;
|
||||
int changed = 1;
|
||||
|
||||
switch (nid) {
|
||||
case 0x02:
|
||||
vnid = VNID_SPK;
|
||||
break;
|
||||
case 0x07:
|
||||
vnid = VNID_MIC;
|
||||
break;
|
||||
}
|
||||
|
||||
/* store the left and right volume */
|
||||
if (ch & 1) {
|
||||
spec->vnode_lvol[vnid - VNODE_START_NID] = *valp;
|
||||
valp++;
|
||||
}
|
||||
if (ch & 2) {
|
||||
spec->vnode_rvol[vnid - VNODE_START_NID] = *valp;
|
||||
valp++;
|
||||
}
|
||||
|
||||
snd_hda_power_up(codec);
|
||||
ca0132_alt_dsp_volume_put(codec, vnid);
|
||||
mutex_lock(&codec->control_mutex);
|
||||
changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
|
||||
mutex_unlock(&codec->control_mutex);
|
||||
snd_hda_power_down(codec);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
|
||||
unsigned int size, unsigned int __user *tlv)
|
||||
{
|
||||
@ -4853,6 +5007,39 @@ static struct snd_kcontrol_new ca0132_mixer[] = {
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
/*
|
||||
* SBZ specific control mixer. Removes auto-detect for mic, and adds surround
|
||||
* controls. Also sets both the Front Playback and Capture Volume controls to
|
||||
* alt so they set the DSP's decibel level.
|
||||
*/
|
||||
static struct snd_kcontrol_new sbz_mixer[] = {
|
||||
CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
|
||||
CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
|
||||
CA0132_ALT_CODEC_VOL("Capture Volume", 0x07, HDA_INPUT),
|
||||
CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
|
||||
CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
|
||||
VNID_HP_ASEL, 1, HDA_OUTPUT),
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
/*
|
||||
* Same as the Sound Blaster Z, except doesn't use the alt volume for capture
|
||||
* because it doesn't set decibel levels for the DSP for capture.
|
||||
*/
|
||||
static struct snd_kcontrol_new r3di_mixer[] = {
|
||||
CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
|
||||
CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
|
||||
CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
|
||||
CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
|
||||
HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
|
||||
HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
|
||||
CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
|
||||
VNID_HP_ASEL, 1, HDA_OUTPUT),
|
||||
{ } /* end */
|
||||
};
|
||||
|
||||
static int ca0132_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
struct ca0132_spec *spec = codec->spec;
|
||||
@ -6566,7 +6753,21 @@ static int patch_ca0132(struct hda_codec *codec)
|
||||
|
||||
spec->dsp_state = DSP_DOWNLOAD_INIT;
|
||||
spec->num_mixers = 1;
|
||||
spec->mixers[0] = ca0132_mixer;
|
||||
|
||||
/* Set which mixers each quirk uses. */
|
||||
switch (spec->quirk) {
|
||||
case QUIRK_SBZ:
|
||||
spec->mixers[0] = sbz_mixer;
|
||||
snd_hda_codec_set_name(codec, "Sound Blaster Z");
|
||||
break;
|
||||
case QUIRK_R3DI:
|
||||
spec->mixers[0] = r3di_mixer;
|
||||
snd_hda_codec_set_name(codec, "Recon3Di");
|
||||
break;
|
||||
default:
|
||||
spec->mixers[0] = ca0132_mixer;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Setup whether or not to use alt functions */
|
||||
switch (spec->quirk) {
|
||||
|
Loading…
Reference in New Issue
Block a user