Fixup set PCM/headphones volume from Master

Message ID 1478802091.454740.783828673.10EC9220@webmail.messagingengine.com
State Superseded
Headers show

Commit Message

David Jordan Nov. 10, 2016, 6:21 p.m.
I've attached the patch I've been working with.

-- 
  David Jordan
  david2@system76.com

On Tue, Nov 8, 2016, at 06:42 AM, Takashi Iwai wrote:
> On Mon, 07 Nov 2016 22:36:21 +0100,

> David Jordan wrote:

> > 

> > It's HDA, Realtek ALC898.  The headphones jack (analog 3.5mm output)

> > (0x1b) is connected to a separate ESS DAC, which takes sound input from

> > the PCM SPDIF output.  

> 

> Note that the "Master" volume is a vmaster control, and usually it

> covers all DAC volume controls.

> 

> It'd be better if you provide a patch you're fighting with, together

> with alsa-info.sh output.  Then other people can track the issue

> either with the real h/w or hda-emu.

> 

> 

> Takashi

> 

> 

> > 

> > -- 

> >   David Jordan

> >   david2@system76.com

> > 

> > On Mon, Nov 7, 2016, at 01:19 PM, Clemens Ladisch wrote:

> > > David Jordan wrote:

> > > > I'm working on a kernel-level fixup for a set of hardware

> > > 

> > > What hardware?  If HDA, which codec, and how is it connected?

> > > 

> > > 

> > > Regards,

> > > Clemens

> > _______________________________________________

> > Alsa-devel mailing list

> > Alsa-devel@alsa-project.org

> > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

> >
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

Comments

David Jordan Nov. 10, 2016, 7:16 p.m. | #1
And the alsa-info.txt

-- 
  David Jordan
  david2@system76.com

On Thu, Nov 10, 2016, at 10:21 AM, David Jordan wrote:
> I've attached the patch I've been working with.

> 

> -- 

>   David Jordan

>   david2@system76.com

> 

> On Tue, Nov 8, 2016, at 06:42 AM, Takashi Iwai wrote:

> > On Mon, 07 Nov 2016 22:36:21 +0100,

> > David Jordan wrote:

> > > 

> > > It's HDA, Realtek ALC898.  The headphones jack (analog 3.5mm output)

> > > (0x1b) is connected to a separate ESS DAC, which takes sound input from

> > > the PCM SPDIF output.  

> > 

> > Note that the "Master" volume is a vmaster control, and usually it

> > covers all DAC volume controls.

> > 

> > It'd be better if you provide a patch you're fighting with, together

> > with alsa-info.sh output.  Then other people can track the issue

> > either with the real h/w or hda-emu.

> > 

> > 

> > Takashi

> > 

> > 

> > > 

> > > -- 

> > >   David Jordan

> > >   david2@system76.com

> > > 

> > > On Mon, Nov 7, 2016, at 01:19 PM, Clemens Ladisch wrote:

> > > > David Jordan wrote:

> > > > > I'm working on a kernel-level fixup for a set of hardware

> > > > 

> > > > What hardware?  If HDA, which codec, and how is it connected?

> > > > 

> > > > 

> > > > Regards,

> > > > Clemens

> > > _______________________________________________

> > > Alsa-devel mailing list

> > > Alsa-devel@alsa-project.org

> > > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

> > > 

> _______________________________________________

> Alsa-devel mailing list

> Alsa-devel@alsa-project.org

> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

> Email had 1 attachment:

> + alsa-hda-alc898-pcm-hp-dac-vol-test.patch

>   15k (text/x-patch)
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

Patch

diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 4b6d861..4419a7e 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -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"},
 	{}
 };