ALSA: hda/cs8409: Add Speaker Playback Switch for Cyborg

Add support for a Speaker Playback Switch, which disables
the Amp connected to cs8409. The Switch is not added
automatically because cs8409 does not have an output amp
for the speaker NID.

Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20220511100207.1268321-3-sbinding@opensource.cirrus.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Stefan Binding 2022-05-11 11:02:06 +01:00 committed by Takashi Iwai
parent 22bb82264c
commit f129f26f76
2 changed files with 64 additions and 11 deletions

View File

@ -419,6 +419,39 @@ static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid)
snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP));
}
static int cs8409_spk_sw_gpio_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs8409_spec *spec = codec->spec;
ucontrol->value.integer.value[0] = !!(spec->gpio_data & spec->speaker_pdn_gpio);
return 0;
}
static int cs8409_spk_sw_gpio_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs8409_spec *spec = codec->spec;
unsigned int gpio_data;
gpio_data = (spec->gpio_data & ~spec->speaker_pdn_gpio) |
(ucontrol->value.integer.value[0] ? spec->speaker_pdn_gpio : 0);
if (gpio_data == spec->gpio_data)
return 0;
spec->gpio_data = gpio_data;
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
return 1;
}
static const struct snd_kcontrol_new cs8409_spk_sw_ctrl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_ctl_boolean_mono_info,
.get = cs8409_spk_sw_gpio_get,
.put = cs8409_spk_sw_gpio_put,
};
/******************************************************************************
* CS42L42 Specific Functions
******************************************************************************/
@ -836,7 +869,7 @@ static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
static void cs42l42_resume(struct sub_codec *cs42l42)
{
struct hda_codec *codec = cs42l42->codec;
unsigned int gpio_data;
struct cs8409_spec *spec = codec->spec;
struct cs8409_i2c_param irq_regs[] = {
{ CS42L42_CODEC_STATUS, 0x00 },
{ CS42L42_DET_INT_STATUS1, 0x00 },
@ -846,9 +879,9 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
int fsv_old, fsv_new;
/* Bring CS42L42 out of Reset */
gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
gpio_data |= cs42l42->reset_gpio;
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
spec->gpio_data |= cs42l42->reset_gpio;
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
usleep_range(10000, 15000);
cs42l42->suspended = 0;
@ -880,7 +913,7 @@ static void cs42l42_resume(struct sub_codec *cs42l42)
static void cs42l42_suspend(struct sub_codec *cs42l42)
{
struct hda_codec *codec = cs42l42->codec;
unsigned int gpio_data;
struct cs8409_spec *spec = codec->spec;
int reg_cdc_status = 0;
const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = {
{ CS42L42_DAC_CTL2, 0x02 },
@ -911,9 +944,9 @@ static void cs42l42_suspend(struct sub_codec *cs42l42)
cs42l42->mic_jack_in = 0;
/* Put CS42L42 into Reset */
gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
gpio_data &= ~cs42l42->reset_gpio;
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, gpio_data);
spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
spec->gpio_data &= ~cs42l42->reset_gpio;
snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
}
#endif
@ -1107,6 +1140,8 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
spec->gen.no_primary_hp = 1;
spec->gen.suppress_vmaster = 1;
spec->speaker_pdn_gpio = 0;
/* GPIO 5 out, 3,4 in */
spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio;
spec->gpio_data = 0;
@ -1118,21 +1153,33 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID);
cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID);
/* Set HSBIAS_SENSE_EN and Full Scale volume for some variants. */
spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
switch (codec->fixup_id) {
case CS8409_CYBORG:
spec->scodecs[CS8409_CODEC0]->full_scale_vol =
CS42L42_FULL_SCALE_VOL_MINUS6DB;
spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
break;
case CS8409_ODIN:
spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
break;
case CS8409_WARLOCK_MLK:
case CS8409_WARLOCK_MLK_DUAL_MIC:
spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
break;
default:
spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
spec->scodecs[CS8409_CODEC0]->full_scale_vol =
CS42L42_FULL_SCALE_VOL_MINUS6DB;
break;
}
if (spec->speaker_pdn_gpio > 0) {
spec->gpio_dir |= spec->speaker_pdn_gpio;
spec->gpio_data |= spec->speaker_pdn_gpio;
}
break;
case HDA_FIXUP_ACT_PROBE:
/* Fix Sample Rate to 48kHz */
@ -1149,6 +1196,9 @@ void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix,
&cs42l42_dac_volume_mixer);
snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume",
&cs42l42_adc_volume_mixer);
if (spec->speaker_pdn_gpio > 0)
snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch",
&cs8409_spk_sw_ctrl);
/* Disable Unsolicited Response during boot */
cs8409_enable_ur(codec, 0);
snd_hda_codec_set_name(codec, "CS8409/CS42L42");

View File

@ -238,6 +238,7 @@ enum cs8409_coefficient_index_registers {
#define CS42L42_I2C_ADDR (0x48 << 1)
#define CS8409_CS42L42_RESET GENMASK(5, 5) /* CS8409_GPIO5 */
#define CS8409_CS42L42_INT GENMASK(4, 4) /* CS8409_GPIO4 */
#define CS8409_CYBORG_SPEAKER_PDN GENMASK(2, 2) /* CS8409_GPIO2 */
#define CS8409_CS42L42_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A
#define CS8409_CS42L42_SPK_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A
#define CS8409_CS42L42_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A
@ -326,6 +327,8 @@ struct cs8409_spec {
unsigned int gpio_dir;
unsigned int gpio_data;
int speaker_pdn_gpio;
struct mutex i2c_mux;
unsigned int i2c_clck_enabled;
unsigned int dev_addr;