@@ -32,6 +32,7 @@
#include <linux/input.h>
#include <sound/core.h>
#include <sound/jack.h>
+#include <sound/tlv.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_auto_parser.h"
@@ -128,6 +129,14 @@ struct alc_spec {
unsigned int coef0;
struct input_dev *kb_dev;
u8 alc_mute_keycode_map[1];
+
+ /* for clevo headphones fix */
+ unsigned int hp_volume;
+ unsigned int num_inits;
+ struct snd_kcontrol *master_kctl;
+ struct snd_kcontrol *pcm_kctl;
+ struct snd_kcontrol embedded_pcm_kctl;
+/* struct snd_kcontrol *master_kctl;*/
};
/*
@@ -1797,6 +1806,7 @@ enum {
ALC882_FIXUP_NO_PRIMARY_HP,
ALC887_FIXUP_ASUS_BASS,
ALC887_FIXUP_BASS_CHMAP,
+ ALC898_FIXUP_CLEVO_SPDIF,
};
static void alc889_fixup_coef(struct hda_codec *codec,
@@ -1956,6 +1966,205 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
}
}
+
+
+
+
+/* Set PCM volume from Master. HP volume is based on PCM volume exclusively.
+ * TODO: Trigger this function on Master Volume Change.
+ * TODO: Ensure appropriate scaling of PCM levels relative to Master.
+ */
+
+#define CLEVO_PCM_MAX_VOLUME 31
+
+static int snd_clevo_pcm_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+
+ printk("SYS76: snd_clevo_pcm_volume_info\n");
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = CLEVO_PCM_MAX_VOLUME;
+ return 0;
+}
+
+static int snd_clevo_pcm_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+
+ printk("SYS76: snd_clevo_pcm_volume_get\n");
+ struct hda_codec *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = kcontrol->id.subdevice;
+
+
+ struct alc_spec *spec = chip->spec;
+
+ printk("SYS76: pcm_volume get...addrs\n");
+ printk("addr spec %lu \n", spec);
+ printk("addr codec %lu \n", chip);
+ printk("spec->hp_volume %u\n", spec->hp_volume);
+
+ if (spec != 0) {
+ printk("meep!\n");
+ ucontrol->value.integer.value[0] = CLEVO_PCM_MAX_VOLUME - spec->hp_volume;//chip->playback_volume[idx][0];
+ ucontrol->value.integer.value[1] = CLEVO_PCM_MAX_VOLUME - spec->hp_volume;//chip->playback_volume[idx][1];
+ }
+ else {
+ printk("leep!\n");
+ ucontrol->value.integer.value[0] = CLEVO_PCM_MAX_VOLUME - 1;//chip->playback_volume[idx][0];
+ ucontrol->value.integer.value[1] = CLEVO_PCM_MAX_VOLUME - 1;//chip->playback_volume[idx][1];
+ }
+ return 0;
+}
+
+//Okay, so this is creating the pcm_volume_ctl, but how is hard?
+static int snd_clevo_pcm_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int idx;
+ unsigned char val;
+ int i, change = 0;
+
+ printk("SYS76: snd_clevo_pcm_volume_put\n");
+
+ struct alc_spec *spec = chip->spec;
+ struct snd_card *card = chip->card;
+
+ printk(kcontrol->id.name);
+
+
+ /* */
+/* struct snd_ctl_elem_id pcm_id;*/
+/* struct snd_kcontrol *pcm_control;*/
+/* memset(&pcm_id, 0, sizeof(pcm_id));*/
+/* pcm_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;*/
+/* pcm_id.device = 0;*/
+/* pcm_id.index = 0;*/
+/* strcpy(pcm_id.name, "PCM Playback Volume");*/
+/* Crashes Here:*/
+/* pcm_control = snd_ctl_find_id(chip->card, &pcm_id);*/
+/* */
+
+ printk("SYS76: PUT snd before find mixer ctl\n");
+
+/* Get the PCM Control to set it. For some reason this fails.*/
+ struct snd_kcontrol * pcm_kctl = snd_hda_find_mixer_ctl(chip, "PCM Playback Volume");
+ struct snd_ctl_elem_value *pcm_uctl;
+
+ printk("SYS76: PUT snd_hda_find_mixer_ctl\n");
+
+ if (!pcm_kctl)
+ printk("SYS76: PUT no kctl\n");
+ return;
+ pcm_uctl = kzalloc(sizeof(*pcm_uctl), GFP_KERNEL);
+ if (!pcm_uctl)
+ return;
+
+/* Set the PCM ucontrol from Master ucontrol*/
+ val = ucontrol->value.integer.value[0];
+ val &= HDA_AMP_VOLMASK;
+
+ pcm_uctl->value.integer.value[0] = val;
+ pcm_uctl->value.integer.value[1] = val;
+ pcm_kctl->put(pcm_kctl, pcm_uctl);
+
+ kfree(pcm_uctl);
+
+ return change;
+}
+
+
+static void alc898_clevo_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ int val;
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_MASK, 2);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0);
+
+ struct alc_spec *spec = codec->spec;
+
+ if (snd_hda_jack_detect(codec, 0x1b) && (snd_hda_codec_read(codec, codec->core.afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0) >= 0)) {
+ val = snd_hda_codec_get_pin_target(codec, 0x1b);
+ val |= AC_PINCTL_VREF_80;
+ snd_hda_set_pin_ctl(codec, 0x1b, val);
+ snd_hda_gen_hp_automute(codec, jack);
+ } else {
+ printk("S76: automute case B\n");
+ val = snd_hda_codec_get_pin_target(codec, 0x1b);
+ val = val & 0xfff8;
+ snd_hda_set_pin_ctl(codec, 0x1b, val);
+ snd_hda_gen_hp_automute(codec, jack);
+
+ /* Don't mute the digital output, needed for ESS DAC operation */
+ /* TODO: This might belong in alc898_fixup_clevo instead. */
+ /* struct snd_kcontrol *kctl;
+ kctl = snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch");
+ if (!kctl)
+ return;
+ kctl->put = NULL; */
+
+ }
+}
+static void alc898_fixup_clevo(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ printk("SYS76: version 0020\n");
+ printk("SYS76: num_mixers = %u\n", spec->num_mixers);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.detect_hp = 1;
+ spec->gen.automute_speaker = 1;
+ spec->gen.autocfg.hp_pins[0] = 0x1b; /* copy it for automute */
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ alc898_clevo_automute_hook);
+ spec->gen.hp_automute_hook = alc898_clevo_automute_hook;
+ }
+
+
+ struct snd_ctl_elem_id mid;
+ memset(&mid, 0, sizeof(mid));
+ strcpy(mid.name, "PCM Playback Volume");
+ mid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ struct snd_kcontrol * pcm_kcontrol;
+ pcm_kcontrol = snd_ctl_find_id(codec->card, &mid);
+ if (!pcm_kcontrol) {
+ printk("SYS76: No PCM_kcontrol\n");
+ printk("SYS76: oh well\n");
+ return; //returning so we don't do the master stuff until pcm exists...maybe fix kernel panic this way
+ }
+ else {
+ spec->pcm_kctl = pcm_kcontrol;
+ spec->embedded_pcm_kctl = *pcm_kcontrol;
+ printk("MY PCM NUMID IS: %x", pcm_kcontrol->id.numid);
+ }
+
+ struct snd_ctl_elem_id sid;
+ memset(&sid, 0, sizeof(sid));
+ strcpy(sid.name, "Master Playback Volume");
+ sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ struct snd_kcontrol * newsid;
+ //huh, find_id and find_numid are different...look into this
+ newsid = snd_ctl_find_id(codec->card, &sid);
+ if (!newsid) {
+ printk("SYS76: No PCM Playback Volume ctl\n");
+ //Now try *replacing* the headphone control instead of adding another
+ //the snd_ctl_activate function is interesting maybe?
+ //snd_ctl_replace(codec->card, snd_ctl_new1(&snd_clevo_pcm_volume_control, codec), 0);
+ }
+ else {
+ printk("SYS76: PCM Playback Volume ctl id is %d\n", newsid->id.numid);
+ newsid->put = snd_clevo_pcm_volume_put;
+ printk("SYS76: My ID hahaha!\n");
+ }
+}
+
+
static void alc_fixup_bass_chmap(struct hda_codec *codec,
const struct hda_fixup *fix, int action);
@@ -2195,6 +2404,10 @@ static const struct hda_fixup alc882_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_bass_chmap,
},
+ [ALC898_FIXUP_CLEVO_SPDIF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc898_fixup_clevo,
+ },
};
static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@@ -2264,6 +2477,41 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX),
+ SND_PCI_QUIRK(0x1558, 0x0872, "Clevo P870DM2/P870DM3", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x0873, "Clevo P870DM2-G/P870DM3-G", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x0874, "Clevo P870DM2-G/P870DM3-G", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x0875, "Clevo P870KM/P870KM1", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x0876, "Clevo P870KM-G/P870KM1_G", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x65a1, "Clevo P65xHS_HP-D0", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x65a2, "Clevo P65xHS_HP-D", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x65a3, "Clevo P65xHS_HP-G0", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x65a4, "Clevo P65xHS_HP-G", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x67a1, "Clevo P67xHS_HP-D0", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x67a2, "Clevo P67xHS_HP-D", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x67a3, "Clevo P67xHS_HP-G0", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x67a4, "Clevo P67xHS_HP-G", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6a01, "Clevo P65xRS_RP-D0", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6a02, "Clevo P65xRS_RP-D", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6a03, "Clevo P65xRS_RP-G0", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6a04, "Clevo P65xRS_RP-G", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6a05, "Clevo P65xRS_RP-V", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6a06, "Clevo P65xRS_RP-Direct", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6b01, "Clevo P67xRS_RP-D0", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6b02, "Clevo P67xRS_RP-D", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6b03, "Clevo P67xRS_RP-G0", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6b04, "Clevo P67xRS_RP-G", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6b05, "Clevo P67xRS_RP-V", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6b06, "Clevo P67xRS_RP-Direct", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x7504, "Clevo P750DM2", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x7505, "Clevo P750DM2G", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x7506, "Clevo P750KM", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x7507, "Clevo P750KMG", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x7705, "Clevo P775DM2", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x7706, "Clevo P775DM2G", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x7707, "Clevo P775KM", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x7708, "Clevo P775KMG", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x7705, "System76 serw10", ALC898_FIXUP_CLEVO_SPDIF),
+ SND_PCI_QUIRK(0x1558, 0x6b01, "System76 oryp2-ess", ALC898_FIXUP_CLEVO_SPDIF),
SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530),
@@ -2277,6 +2525,7 @@ static const struct hda_model_fixup alc882_fixup_models[] = {
{.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
{.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
{.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"},
+ {.id = ALC898_FIXUP_CLEVO_SPDIF, .name = "system76"},
{}
};