ALSA: hda/ca0132 - Add bass redirection controls.
authorConnor McAdams <conmanx360@gmail.com>
Tue, 25 Aug 2020 20:10:24 +0000 (16:10 -0400)
committerTakashi Iwai <tiwai@suse.de>
Wed, 26 Aug 2020 08:24:19 +0000 (10:24 +0200)
Add bass redirection controls for surround outputs. This uses the DSP to
redirect audio below the bass redirection crossover frequency to the LFE
channel from the front/rear L/R speakers. This only goes into effect if
the speakers aren't set as full range, and only if the surround
configuration has an LFE channel.

Signed-off-by: Connor McAdams <conmanx360@gmail.com>
Link: https://lore.kernel.org/r/20200825201040.30339-6-conmanx360@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_ca0132.c

index 469cefe..8ad2fc5 100644 (file)
@@ -149,6 +149,8 @@ enum {
        SPEAKER_CHANNEL_CFG_ENUM,
        SPEAKER_FULL_RANGE_FRONT,
        SPEAKER_FULL_RANGE_REAR,
+       BASS_REDIRECTION,
+       BASS_REDIRECTION_XOVER,
 #define EFFECTS_COUNT  (EFFECT_END_NID - EFFECT_START_NID)
 };
 
@@ -1123,6 +1125,8 @@ struct ca0132_spec {
        unsigned char speaker_range_val[2];
        unsigned char mic_boost_enum_val;
        unsigned char smart_volume_setting;
+       unsigned char bass_redirection_val;
+       long bass_redirect_xover_freq;
        long fx_ctl_val[EFFECT_LEVEL_SLIDERS];
        long xbass_xover_freq;
        long eq_preset_val;
@@ -4324,6 +4328,35 @@ static int ca0132_alt_set_full_range_speaker(struct hda_codec *codec)
        return 0;
 }
 
+static int ca0132_alt_surround_set_bass_redirection(struct hda_codec *codec,
+               bool val)
+{
+       struct ca0132_spec *spec = codec->spec;
+       unsigned int tmp;
+       int err;
+
+       if (val && spec->channel_cfg_val != SPEAKER_CHANNELS_4_0 &&
+                       spec->channel_cfg_val != SPEAKER_CHANNELS_2_0)
+               tmp = FLOAT_ONE;
+       else
+               tmp = FLOAT_ZERO;
+
+       err = dspio_set_uint_param(codec, 0x96, SPEAKER_BASS_REDIRECT, tmp);
+       if (err < 0)
+               return err;
+
+       /* If it is enabled, make sure to set the crossover frequency. */
+       if (tmp) {
+               tmp = float_xbass_xover_lookup[spec->xbass_xover_freq];
+               err = dspio_set_uint_param(codec, 0x96,
+                               SPEAKER_BASS_REDIRECT_XOVER_FREQ, tmp);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 /*
  * These are the commands needed to setup output on each of the different card
  * types.
@@ -4593,6 +4626,15 @@ static int ca0132_alt_select_out(struct hda_codec *codec)
                ca0132_effects_set(codec, X_BASS,
                        spec->effects_switch[X_BASS - EFFECT_START_NID]);
 
+       if (spec->cur_out_type == SURROUND_OUT)
+               err = ca0132_alt_surround_set_bass_redirection(codec,
+                               spec->bass_redirection_val);
+       else
+               err = ca0132_alt_surround_set_bass_redirection(codec, 0);
+
+       if (err < 0)
+               goto exit;
+
        /* run through the output dsp commands for the selected output. */
        for (i = 0; i < alt_out_presets[spec->cur_out_type].commands; i++) {
                err = dspio_set_uint_param(codec,
@@ -5282,6 +5324,18 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
        return ret;
 }
 /* End of control change helpers. */
+
+static void ca0132_alt_bass_redirection_xover_set(struct hda_codec *codec,
+               long idx)
+{
+       snd_hda_power_up(codec);
+
+       dspio_set_param(codec, 0x96, 0x20, SPEAKER_BASS_REDIRECT_XOVER_FREQ,
+                       &(float_xbass_xover_lookup[idx]), sizeof(unsigned int));
+
+       snd_hda_power_down(codec);
+}
+
 /*
  * Below I've added controls to mess with the effect levels, I've only enabled
  * them on the Sound Blaster Z, but they would probably also work on the
@@ -5290,6 +5344,7 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
  */
 
 /* Sets DSP effect level from the sliders above the controls */
+
 static int ca0132_alt_slider_ctl_set(struct hda_codec *codec, hda_nid_t nid,
                          const unsigned int *lookup, int idx)
 {
@@ -5335,8 +5390,12 @@ static int ca0132_alt_xbass_xover_slider_ctl_get(struct snd_kcontrol *kcontrol,
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct ca0132_spec *spec = codec->spec;
        long *valp = ucontrol->value.integer.value;
+       hda_nid_t nid = get_amp_nid(kcontrol);
 
-       *valp = spec->xbass_xover_freq;
+       if (nid == BASS_REDIRECTION_XOVER)
+               *valp = spec->bass_redirect_xover_freq;
+       else
+               *valp = spec->xbass_xover_freq;
 
        return 0;
 }
@@ -5391,16 +5450,25 @@ static int ca0132_alt_xbass_xover_slider_put(struct snd_kcontrol *kcontrol,
        struct ca0132_spec *spec = codec->spec;
        hda_nid_t nid = get_amp_nid(kcontrol);
        long *valp = ucontrol->value.integer.value;
+       long *cur_val;
        int idx;
 
+       if (nid == BASS_REDIRECTION_XOVER)
+               cur_val = &spec->bass_redirect_xover_freq;
+       else
+               cur_val = &spec->xbass_xover_freq;
+
        /* any change? */
-       if (spec->xbass_xover_freq == *valp)
+       if (*cur_val == *valp)
                return 0;
 
-       spec->xbass_xover_freq = *valp;
+       *cur_val = *valp;
 
        idx = *valp;
-       ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx);
+       if (nid == BASS_REDIRECTION_XOVER)
+               ca0132_alt_bass_redirection_xover_set(codec, *cur_val);
+       else
+               ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx);
 
        return 0;
 }
@@ -5968,6 +6036,11 @@ static int ca0132_switch_get(struct snd_kcontrol *kcontrol,
                return 0;
        }
 
+       if (nid == BASS_REDIRECTION) {
+               *valp = spec->bass_redirection_val;
+               return 0;
+       }
+
        return 0;
 }
 
@@ -6054,6 +6127,14 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
                changed = 0;
        }
 
+       if (nid == BASS_REDIRECTION) {
+               spec->bass_redirection_val = *valp;
+               if (spec->cur_out_type == SURROUND_OUT)
+                       ca0132_alt_surround_set_bass_redirection(codec, *valp);
+
+               changed = 0;
+       }
+
 exit:
        snd_hda_power_down(codec);
        return changed;
@@ -6437,6 +6518,39 @@ static int ca0132_alt_add_rear_full_range_switch(struct hda_codec *codec)
 }
 
 /*
+ * Bass redirection redirects audio below the crossover frequency to the LFE
+ * channel on speakers that are set as not being full-range. On configurations
+ * without an LFE channel, it does nothing. Bass redirection seems to be the
+ * replacement for X-Bass on configurations with an LFE channel.
+ */
+static int ca0132_alt_add_bass_redirection_crossover(struct hda_codec *codec)
+{
+       const char *namestr = "Bass Redirection Crossover";
+       struct snd_kcontrol_new knew =
+               HDA_CODEC_VOLUME_MONO(namestr, BASS_REDIRECTION_XOVER, 1, 0,
+                               HDA_OUTPUT);
+
+       knew.tlv.c = NULL;
+       knew.info = ca0132_alt_xbass_xover_slider_info;
+       knew.get = ca0132_alt_xbass_xover_slider_ctl_get;
+       knew.put = ca0132_alt_xbass_xover_slider_put;
+
+       return snd_hda_ctl_add(codec, BASS_REDIRECTION_XOVER,
+                       snd_ctl_new1(&knew, codec));
+}
+
+static int ca0132_alt_add_bass_redirection_switch(struct hda_codec *codec)
+{
+       const char *namestr = "Bass Redirection";
+       struct snd_kcontrol_new knew =
+               CA0132_CODEC_MUTE_MONO(namestr, BASS_REDIRECTION, 1,
+                               HDA_OUTPUT);
+
+       return snd_hda_ctl_add(codec, BASS_REDIRECTION,
+                       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.
@@ -6751,6 +6865,12 @@ static int ca0132_build_controls(struct hda_codec *codec)
                err = ca0132_alt_add_rear_full_range_switch(codec);
                if (err < 0)
                        return err;
+               err = ca0132_alt_add_bass_redirection_crossover(codec);
+               if (err < 0)
+                       return err;
+               err = ca0132_alt_add_bass_redirection_switch(codec);
+               if (err < 0)
+                       return err;
                err = ca0132_alt_add_mic_boost_enum(codec);
                if (err < 0)
                        return err;
@@ -8102,6 +8222,8 @@ static void ca0132_init_chip(struct hda_codec *codec)
                spec->xbass_xover_freq = 8;
                for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++)
                        spec->fx_ctl_val[i] = effect_slider_defaults[i];
+
+               spec->bass_redirect_xover_freq = 8;
        }
 
        spec->voicefx_val = 0;