[alsa-devel] Surround speaker connection on Acer 8951G
Hi,
I am not sure if this is the right place to ask or if I should just submit a bugreport, but you guys helped me out with a similar problem about 10 years ago, so I'll try here first. If I'm wrong here, then I apologize, just let me know.
I have an Acer Aspire Ethos 8951G notebook which has 6 built in speakers for 5.1 surround sound, it's a Realtek ALC669X.
The problem is that only two speakers are recognized by default.
I tried fiddling around with hdajackretast and it does make a difference, but I somehow could not find a step-by-step approach on how to figure out what is what and how to map everything correctly, so some guidance would be very much appreciated. I even ended up in situations where I got channels supporting only mute which would influence muting of other channels, I really got lost there..
Here's my alsa-info output: http://alsa-project.org/db/?f=3adde87164f5fc349c3c78b211ee63e416ebf10b
It would be great if we could figure it out and make it work by default.
Basically that's what we did 10 years ago, back then I had an Acer Aspire 8920G with a ALC889 which had the same problem that got fixed by you and these days it just works out of the box :)
Kind regards, Jin
Hi again,
after feedback that I'm mostly on my own here because this is not remotely testable I invested quite some time into figuring out what is what and was able to make some progress. Still I am at a point where help would be very much appreciated as I am not sure how to proceed further.
Basically its almost working now, I managed to "connect" 5 speakers, but I can't find the LFE. I tried various combinations in hdajackretask and hda-analyzer, but was not able to get any sound out of it.
My current layout is as follows:
Pin 0x15 Front Left / Front Right Pin 0x18 Center (left channel audible) Pin 0x1b Rear Left / Rear Right, Headphones
I am not listing the mic/line pins here, just focusing on the speakers.
Still need to do some fiddling for 0x1b to make sure the rear speakers get muted when I plug in the headphones, but that's also something for later.
Could someone hint me how to proceed in "finding the LFE"? Where should I be digging? I did also have a look at parser hints documentation and tried some, but this was more a random attempt and did not really help me.
I am attaching my current alsa-info along with the retask configuration that is currently applied.
Kind regards, Jin
On Thu, Apr 04, 2019 at 09:24:30PM +0200, Sergey 'Jin' Bostandzhyan wrote:
Hi,
I am not sure if this is the right place to ask or if I should just submit a bugreport, but you guys helped me out with a similar problem about 10 years ago, so I'll try here first. If I'm wrong here, then I apologize, just let me know.
I have an Acer Aspire Ethos 8951G notebook which has 6 built in speakers for 5.1 surround sound, it's a Realtek ALC669X.
The problem is that only two speakers are recognized by default.
I tried fiddling around with hdajackretast and it does make a difference, but I somehow could not find a step-by-step approach on how to figure out what is what and how to map everything correctly, so some guidance would be very much appreciated. I even ended up in situations where I got channels supporting only mute which would influence muting of other channels, I really got lost there..
Here's my alsa-info output: http://alsa-project.org/db/?f=3adde87164f5fc349c3c78b211ee63e416ebf10b
It would be great if we could figure it out and make it work by default.
Basically that's what we did 10 years ago, back then I had an Acer Aspire 8920G with a ALC889 which had the same problem that got fixed by you and these days it just works out of the box :)
Kind regards, Jin
Alsa-devel mailing list Alsa-devel@alsa-project.org https://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On Fri, 19 Jul 2019 13:12:31 +0200, Sergey 'Jin' Bostandzhyan wrote:
Hi again,
after feedback that I'm mostly on my own here because this is not remotely testable I invested quite some time into figuring out what is what and was able to make some progress. Still I am at a point where help would be very much appreciated as I am not sure how to proceed further.
Basically its almost working now, I managed to "connect" 5 speakers, but I can't find the LFE. I tried various combinations in hdajackretask and hda-analyzer, but was not able to get any sound out of it.
My current layout is as follows:
Pin 0x15 Front Left / Front Right Pin 0x18 Center (left channel audible) Pin 0x1b Rear Left / Rear Right, Headphones
I am not listing the mic/line pins here, just focusing on the speakers.
Still need to do some fiddling for 0x1b to make sure the rear speakers get muted when I plug in the headphones, but that's also something for later.
Could someone hint me how to proceed in "finding the LFE"? Where should I be digging? I did also have a look at parser hints documentation and tried some, but this was more a random attempt and did not really help me.
I am attaching my current alsa-info along with the retask configuration that is currently applied.
It might be some other external stuff like an external amp that is missing. Often it's managed via GPIO or EAPD (that is controlled by HD-audio itself), but sometimes via a vendor-specific verb, or even over a completely different i2c...
In the case of vendor verbs, you can take a look at other quirks for similar models that touches lots of COEF stuff.
Takashi
Hi Takashi,
On Fri, Jul 19, 2019 at 04:44:52PM +0200, Takashi Iwai wrote:
It might be some other external stuff like an external amp that is missing. Often it's managed via GPIO or EAPD (that is controlled by HD-audio itself), but sometimes via a vendor-specific verb, or even over a completely different i2c...
In the case of vendor verbs, you can take a look at other quirks for similar models that touches lots of COEF stuff.
thanks for the pointers, does not sound simple, let's see if I get anywhere, I will for sure try.
Odd that only one speaker would require such a special handling, but I guess it is what it is.
In the meantime I found a more pressing issue where I also failed to find a solution:
Pin 0x1b is resposible for rear speakers and at the same time for external headphones. If I set the pin to "headphones" in hdajackretask, then I "lose" the speaker and can't select a 5.1 profile. If I set it to internal "speaker", the way I have it set up now, then sound comes out of speakers and headphones at the same time.
"hdajacksensetest -a" will detect presence even if sensing is disabled in hdajackretask, so I assume that this could be somehow used to mute the speakers, but I was not able to figure out how to approach this.
This is quite an issue for audio conferencing, so I'd like to tackle this first and leave the LFE problem for later.
What would be the best way to solve this, how should this pin be configured?
Kind regards, Jin
Hi Takashi,
On Sat, Jul 20, 2019 at 06:54:35PM +0200, Sergey 'Jin' Bostandzhyan wrote:
On Fri, Jul 19, 2019 at 04:44:52PM +0200, Takashi Iwai wrote:
It might be some other external stuff like an external amp that is missing. Often it's managed via GPIO or EAPD (that is controlled by HD-audio itself), but sometimes via a vendor-specific verb, or even over a completely different i2c...
In the case of vendor verbs, you can take a look at other quirks for similar models that touches lots of COEF stuff.
thanks for the pointers, does not sound simple, let's see if I get anywhere, I will for sure try.
I am going at a slow pace, but I did not give up and I'd be happy if you or anyone else from the list would find the time to answer some questions from time to time.
Right now I am mostly studying patch_realtek.c, as a first step I want to make sure that at least my known pins get set up by the driver without having to go via hdajackretask.
I got my build set up, I also dug up hda-decode-pincfg from the hda-emu sources and made it compile (very useful if one wants to understand and compare the pin configurations in patch_realtek.c), so now I am trying things out every other evening.
One part that is not quite clear to me: what the heck is ALC669X?
Could someone please explain the meaning of alc_codec_rename_pci_table ?
Entry for my vendor id looks like this: { 0x10ec0670, 0x1025, 0, "ALC669X" },
If I search for that vendor id further in the code, I see that it gets patched as ALC662?
HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
At the same time the documentation in models.rst lists those numbers together:
ALC66x/67x/892
I already looked at the hda-audio specification from Intel to get a general understanding, but I was also pulling some Realtek specs which do describe implemented verbs and things like that (my hope was to see something vendor related which could hint me how to enable the subwoofer).
I was not able to find any 669 Realtek datasheets, I did however find the ones for ALC665 and ALC892. How specific is all of this, i.e. should I keep looking for the exact one or am I on the wrong path here?
Kind regards, Jin
On Mon, 19 Aug 2019 21:57:14 +0200, Sergey 'Jin' Bostandzhyan wrote:
Hi Takashi,
On Sat, Jul 20, 2019 at 06:54:35PM +0200, Sergey 'Jin' Bostandzhyan wrote:
On Fri, Jul 19, 2019 at 04:44:52PM +0200, Takashi Iwai wrote:
It might be some other external stuff like an external amp that is missing. Often it's managed via GPIO or EAPD (that is controlled by HD-audio itself), but sometimes via a vendor-specific verb, or even over a completely different i2c...
In the case of vendor verbs, you can take a look at other quirks for similar models that touches lots of COEF stuff.
thanks for the pointers, does not sound simple, let's see if I get anywhere, I will for sure try.
I am going at a slow pace, but I did not give up and I'd be happy if you or anyone else from the list would find the time to answer some questions from time to time.
Right now I am mostly studying patch_realtek.c, as a first step I want to make sure that at least my known pins get set up by the driver without having to go via hdajackretask.
I got my build set up, I also dug up hda-decode-pincfg from the hda-emu sources and made it compile (very useful if one wants to understand and compare the pin configurations in patch_realtek.c), so now I am trying things out every other evening.
One part that is not quite clear to me: what the heck is ALC669X?
It's just a name string :) Realtek seems to give a different chip name for the certain variant for Dell or whatever big vendors. AFAIK, basically it's the very same chip as ALC670, which is almost compatible with ALC662 variant.
Could someone please explain the meaning of alc_codec_rename_pci_table ?
Entry for my vendor id looks like this: { 0x10ec0670, 0x1025, 0, "ALC669X" },
If I search for that vendor id further in the code, I see that it gets patched as ALC662?
HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
At the same time the documentation in models.rst lists those numbers together:
ALC66x/67x/892
I already looked at the hda-audio specification from Intel to get a general understanding, but I was also pulling some Realtek specs which do describe implemented verbs and things like that (my hope was to see something vendor related which could hint me how to enable the subwoofer).
I was not able to find any 669 Realtek datasheets, I did however find the ones for ALC665 and ALC892. How specific is all of this, i.e. should I keep looking for the exact one or am I on the wrong path here?
The datasheet of ALC662 and similar chips should be available. In general, there is no big difference among Realtek chips; one has more I/O pins available, while one has less.
The vendor-specific stuff like COEF isn't found in the datasheet in details, unfortunately. Also, the GPIO pin connection isn't covered by the codec datasheet, as it's rather device-specific, of course.
HTH,
Takashi
Hi Takashi,
On Thu, Aug 22, 2019 at 04:17:51PM +0200, Takashi Iwai wrote:
I am going at a slow pace, but I did not give up and I'd be happy if you or anyone else from the list would find the time to answer some questions from time to time.
Right now I am mostly studying patch_realtek.c, as a first step I want to make sure that at least my known pins get set up by the driver without having to go via hdajackretask.
I got my build set up, I also dug up hda-decode-pincfg from the hda-emu sources and made it compile (very useful if one wants to understand and compare the pin configurations in patch_realtek.c), so now I am trying things out every other evening.
One part that is not quite clear to me: what the heck is ALC669X?
It's just a name string :) Realtek seems to give a different chip name for the certain variant for Dell or whatever big vendors. AFAIK, basically it's the very same chip as ALC670, which is almost compatible with ALC662 variant.
Oh, OK, that explains why I was not able to find any real "connection" to this entry in the code, I was searching for a deeper meaning there :)
Could someone please explain the meaning of alc_codec_rename_pci_table ?
Entry for my vendor id looks like this: { 0x10ec0670, 0x1025, 0, "ALC669X" },
If I search for that vendor id further in the code, I see that it gets patched as ALC662?
HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
At the same time the documentation in models.rst lists those numbers together:
ALC66x/67x/892
I already looked at the hda-audio specification from Intel to get a general understanding, but I was also pulling some Realtek specs which do describe implemented verbs and things like that (my hope was to see something vendor related which could hint me how to enable the subwoofer).
I was not able to find any 669 Realtek datasheets, I did however find the ones for ALC665 and ALC892. How specific is all of this, i.e. should I keep looking for the exact one or am I on the wrong path here?
The datasheet of ALC662 and similar chips should be available. In general, there is no big difference among Realtek chips; one has more I/O pins available, while one has less.
The vendor-specific stuff like COEF isn't found in the datasheet in details, unfortunately. Also, the GPIO pin connection isn't covered by the codec datasheet, as it's rather device-specific, of course.
Doh... there go my hopes for finding an easy answer... thank you for clarifying that.
As mentioned earlier, I will leave the "subwoofer battle" for the very end.
For starters I added the pin configuration for the Acer Aspire 8951G Ethos to enable surround speakers (without the subwoofer for now).
Not sure how it happened, but since yesterday I lost the ability to unload the module at runtime and I was not able to find out what is using it, so debugging has become a pain now :P
Next thing I am looking at is the problem with automute when HP are plugged in, I hope you can point me in the right direction here.
As described in one of my earlier posts, rear speakers share the pin with the headphones jack and they get correctly muted when headphones are plugged in.
However, all other speakers (front, center) remain unmuted.
I was trying to figure out how to approach this, but did not really get anywhere.
My first idea was to go with the automute hook, however it did not behave the way I would expect it: for some reason it is not triggering on the HP jack, it is however triggering on mic-in and line-out jacks.
I kept the "misc" bit on zero, which means jack-detect is possible, and I set port connectivity to 11b (Both a jack and an internal device are attached).
So if I understood the spec correctly, then this configuration should be appropriate: 0xd1130012 ?
Its worth mentioning, that hdajacksensetest -a will show presence detection correctly.
Assuming that I figure out how to get the auto mute hook to trigger on the HP jack, next question would be: what to do in the hook?
I tried to understand existing hooks, but did not really get anywhere. A lot of them are doing something with VREF80, which seems to be some voltage setting.. but how would that be related to muting?
Could you please hint how to approach this? Am I supposed to mute/unmute the remaining speakers (pins) individually?
I am attaching what I have so far as reference, currently I am hacking vs a Fedora 29 5.2.7-100.fc29.x86_64 kernel (it's just easier to get started like that), I will submit a proper patch when we get this to work properly :>
Kind regards, Jin
On Thu, 22 Aug 2019 22:30:31 +0200, Sergey 'Jin' Bostandzhyan wrote:
Next thing I am looking at is the problem with automute when HP are plugged in, I hope you can point me in the right direction here.
As described in one of my earlier posts, rear speakers share the pin with the headphones jack and they get correctly muted when headphones are plugged in.
However, all other speakers (front, center) remain unmuted.
That's an ugly situation, and currently no clean way to deal with such a shared pin for outputs, unfortunately. The auto-parser can handle the case with input/output switching (e.g. sharing mic and surround), but not about the two outputs.
One possible way with the current code (and your latest patch) would be to create a UCM profile. The driver should still provide the jack detection on the pin 0x1b as "Surround Jack", and this can be used as the headphone jack detection.
In anyway, could you give alsa-info.sh outputs with and without your patch?
thanks,
Takashi
Hi Takashi,
On Thu, Aug 29, 2019 at 11:30:45AM +0200, Takashi Iwai wrote:
On Thu, 22 Aug 2019 22:30:31 +0200, Sergey 'Jin' Bostandzhyan wrote:
Next thing I am looking at is the problem with automute when HP are plugged in, I hope you can point me in the right direction here.
As described in one of my earlier posts, rear speakers share the pin with the headphones jack and they get correctly muted when headphones are plugged in.
However, all other speakers (front, center) remain unmuted.
That's an ugly situation, and currently no clean way to deal with such a shared pin for outputs, unfortunately. The auto-parser can handle the case with input/output switching (e.g. sharing mic and surround), but not about the two outputs.
One possible way with the current code (and your latest patch) would be to create a UCM profile. The driver should still provide the jack detection on the pin 0x1b as "Surround Jack", and this can be used as the headphone jack detection.
I gogled it up and I did find HDAudio-Gigabyte-ALC1220DualCodecs as an example, so basically the solution in this case would be split between the driver and alsa-lib?
Are there any disadvantages to muting the other channels directly in the driver? Or would it be a viable option to extend the auto-parser to handle the remaining channels?
Personally, I would prefer a solution at one place, but I follow your lead here, if you say that UCM is the way to go, then so be it.
I played around with jack detection and I had the feeling that it did not work reliably, or maybe I misunderstood something?
Who is responsible for setting spec->gen.hp_jack_present? I thought that this variable always represents the current presence state of the hp jack?
I tried printing it from my automute hook, but it never changed. My assumption is, that due to 0x1b's pin configuration its not treated by the driver as an hp_out, its also not added the hp_outs array.
I tried to add the pin to the hp_outs array manually, but I could not see any difference.
In the end I configured the 0x1b pin to allow jack sensing, but I noticed that the hook will not get triggered during playback, is this a bug or am I missing something?
I tried the following:
static void alc662_aspire_ethos_hp_automute_hook(struct hda_codec *codec, struct hda_jack_callback *jack) { struct alc_spec *spec = codec->spec; unsigned int hp_jack_old_state = spec->gen.hp_jack_present;
if (snd_hda_jack_detect(codec, 0x1b) == HDA_JACK_PRESENT) { printk("HP AUTOMUTE HOOK: hp plugged in\n"); spec->gen.hp_jack_present = 1; } else { printk("HP AUTOMUTE HOOK: hp unplugged\n"); spec->gen.hp_jack_present = 0; } if (hp_jack_old_state != spec->gen.hp_jack_present) { printk("Detected state change on pin 0x1b, jack present: %d\n", spec->gen.hp_jack_present); } }
I kept watching dmesg, when nothing is playing the plugged in/unplugged messages appear correctly, but if I start speaker test and replug during the playback, nothing is printed.
Meanwhile I made progress on the LFE topic:
Could someone hint me how to proceed in "finding the LFE"? Where should I be digging? I did also have a look at parser hints documentation and tried some, but this was more a random attempt and did not really help me.
It might be some other external stuff like an external amp that is missing. Often it's managed via GPIO or EAPD (that is controlled by HD-audio itself), but sometimes via a vendor-specific verb, or even over a completely different i2c...
In the case of vendor verbs, you can take a look at other quirks for similar models that touches lots of COEF stuff.
Turned out it was indeed a GPIO, I won't describe all the things I tried, in the end it was a lucky click on the dir_out checkbox in hda-analyzer while I was debugging the shared pin issue :)
The below HDA_FIXUP_VERBS does the trick, so I do have all 6 speakers working, finally!
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
The only thing that is not quite clear to me is - does LFE still have its own pin and I was just not able to find it, but managed to unmute it via the GPIO? Or is the LFE speaker somehow "intenrally managed"? It works correctly though, alsa mixer is also capable of controlling LFE volume separately so it's fine, everything is also working with pulseaudio and a 5.1 profile on top.
So right now muting seems to be indeed the last remaining piece of the puzzle.
In anyway, could you give alsa-info.sh outputs with and without your patch?
Here is the original output of the unmodified system: http://alsa-project.org/db/?f=3adde87164f5fc349c3c78b211ee63e416ebf10b
Here is my current state: http://alsa-project.org/db/?f=5f1c8730d3099099b4c7442cb09d475e5618c3c6
I also pushed my code to github: https://github.com/jin-eld/hda-intel-alc669x
Thank you very much for not giving up on me :) Your feedback is very valuable!
Kind regards, Jin
On Thu, 29 Aug 2019 12:38:05 +0200, Sergey 'Jin' Bostandzhyan wrote:
Hi Takashi,
On Thu, Aug 29, 2019 at 11:30:45AM +0200, Takashi Iwai wrote:
On Thu, 22 Aug 2019 22:30:31 +0200, Sergey 'Jin' Bostandzhyan wrote:
Next thing I am looking at is the problem with automute when HP are plugged in, I hope you can point me in the right direction here.
As described in one of my earlier posts, rear speakers share the pin with the headphones jack and they get correctly muted when headphones are plugged in.
However, all other speakers (front, center) remain unmuted.
That's an ugly situation, and currently no clean way to deal with such a shared pin for outputs, unfortunately. The auto-parser can handle the case with input/output switching (e.g. sharing mic and surround), but not about the two outputs.
One possible way with the current code (and your latest patch) would be to create a UCM profile. The driver should still provide the jack detection on the pin 0x1b as "Surround Jack", and this can be used as the headphone jack detection.
I gogled it up and I did find HDAudio-Gigabyte-ALC1220DualCodecs as an example, so basically the solution in this case would be split between the driver and alsa-lib?
Right.
Are there any disadvantages to muting the other channels directly in the driver? Or would it be a viable option to extend the auto-parser to handle the remaining channels?
It's not so trivial to extend. The logic of driver parser assumes the single purpose pins, and if they appear doubly, the behavior becomes confusing. For example, if you just add the same pin to both speaker_pins[] and hp_pins[], all speaker_pins[] get muted by the auto-mute, hence you'll mute the headphone again. OTOH, if you don't list up the pin in speaker_pins[], it won't be used for speakers for multi-channels, obviously.
Personally, I would prefer a solution at one place, but I follow your lead here, if you say that UCM is the way to go, then so be it.
We can give it a try. If it doesn't work as expected, we'd need more extension in the driver side.
I played around with jack detection and I had the feeling that it did not work reliably, or maybe I misunderstood something?
Who is responsible for setting spec->gen.hp_jack_present? I thought that this variable always represents the current presence state of the hp jack?
I tried printing it from my automute hook, but it never changed. My assumption is, that due to 0x1b's pin configuration its not treated by the driver as an hp_out, its also not added the hp_outs array.
I tried to add the pin to the hp_outs array manually, but I could not see any difference.
In the end I configured the 0x1b pin to allow jack sensing, but I noticed that the hook will not get triggered during playback, is this a bug or am I missing something?
I tried the following:
static void alc662_aspire_ethos_hp_automute_hook(struct hda_codec *codec, struct hda_jack_callback *jack) { struct alc_spec *spec = codec->spec; unsigned int hp_jack_old_state = spec->gen.hp_jack_present;
if (snd_hda_jack_detect(codec, 0x1b) == HDA_JACK_PRESENT) { printk("HP AUTOMUTE HOOK: hp plugged in\n"); spec->gen.hp_jack_present = 1; } else { printk("HP AUTOMUTE HOOK: hp unplugged\n"); spec->gen.hp_jack_present = 0; } if (hp_jack_old_state != spec->gen.hp_jack_present) { printk("Detected state change on pin 0x1b, jack present: %d\n", spec->gen.hp_jack_present); }
}
I kept watching dmesg, when nothing is playing the plugged in/unplugged messages appear correctly, but if I start speaker test and replug during the playback, nothing is printed.
Check rather "Speaker Surround Jack" state upon plugging the headphone. Does it change reliably?
Note that even if you set such flags manually like the above, it won't work, because of the reasons I already mentioned.
Meanwhile I made progress on the LFE topic:
Could someone hint me how to proceed in "finding the LFE"? Where should I be digging? I did also have a look at parser hints documentation and tried some, but this was more a random attempt and did not really help me.
It might be some other external stuff like an external amp that is missing. Often it's managed via GPIO or EAPD (that is controlled by HD-audio itself), but sometimes via a vendor-specific verb, or even over a completely different i2c...
In the case of vendor verbs, you can take a look at other quirks for similar models that touches lots of COEF stuff.
Turned out it was indeed a GPIO, I won't describe all the things I tried, in the end it was a lucky click on the dir_out checkbox in hda-analyzer while I was debugging the shared pin issue :)
The below HDA_FIXUP_VERBS does the trick, so I do have all 6 speakers working, finally!
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
Actually this must be paired with the corresponding bit of GPIO_DATA, too. Is the bit 0x02 of GPIO_DATA set or cleared? Usually setting it turns on the amp, but sometimes inverted.
The only thing that is not quite clear to me is - does LFE still have its own pin and I was just not able to find it, but managed to unmute it via the GPIO? Or is the LFE speaker somehow "intenrally managed"? It works correctly though, alsa mixer is also capable of controlling LFE volume separately so it's fine, everything is also working with pulseaudio and a 5.1 profile on top.
It's usually just the external amp controlled via GPIO, and the signal must go through the pin 0x18 as well as the center channel, I suppose.
Takashi
So right now muting seems to be indeed the last remaining piece of the puzzle.
In anyway, could you give alsa-info.sh outputs with and without your patch?
Here is the original output of the unmodified system: http://alsa-project.org/db/?f=3adde87164f5fc349c3c78b211ee63e416ebf10b
Here is my current state: http://alsa-project.org/db/?f=5f1c8730d3099099b4c7442cb09d475e5618c3c6
I also pushed my code to github: https://github.com/jin-eld/hda-intel-alc669x
Thank you very much for not giving up on me :) Your feedback is very valuable!
Hi Takashi,
On Thu, Aug 29, 2019 at 01:29:13PM +0200, Takashi Iwai wrote:
One possible way with the current code (and your latest patch) would be to create a UCM profile. The driver should still provide the jack detection on the pin 0x1b as "Surround Jack", and this can be used as the headphone jack detection.
I gogled it up and I did find HDAudio-Gigabyte-ALC1220DualCodecs as an example, so basically the solution in this case would be split between the driver and alsa-lib?
Right.
I did some digging yesterday and found out that documentation on the UCM profiles is sparse. I cloned your alsa-lib repo from github and looked at the available profiles. One thing that is not clear to me: the configurations have entries for JackControl and JackHWMute, however unless I totally missed it - those strings are not being parsed anywhere in the code? I first looked at parser.c as suggested by the man page and later grepped through the whole tree - nothing.
From various examples which I found when searching for answers I could see,
that in most cases the desired _verb section of the profile is activated via a udev rule, but I was not able to find anything that would be hooked to jack sense to switch between devices?
I know it could be done via acpid, is this the way it is meant to be used or am I missing something, especially in regard to those JackHWMute strings?
How exactly is card longname which can be set in the driver connected to all this, I understand there is no automatic mechanism and that everyone needs to set up their custom udev scripts anyway?
I more or less got things to behave using a custom profile with device sections for Speakers and Headphones, which basically mutes everything except surround in Headphone mode, so technically I could hook it up to acpid and be done with it. To be honest, I'd be more happy with an "out of the box" experience, which leads me back to the driver.
Are there any disadvantages to muting the other channels directly in the driver? Or would it be a viable option to extend the auto-parser to handle the remaining channels?
It's not so trivial to extend. The logic of driver parser assumes the single purpose pins, and if they appear doubly, the behavior becomes confusing. For example, if you just add the same pin to both speaker_pins[] and hp_pins[], all speaker_pins[] get muted by the auto-mute, hence you'll mute the headphone again. OTOH, if you don't list up the pin in speaker_pins[], it won't be used for speakers for multi-channels, obviously.
This confirms what I have seen when trying things out last week, the pin could be either a speaker or a headphone, but not both, meaning I would either "lose" the ability to use 5.1 profiles or the system would not "see" the headphones as headphones.
Personally, I would prefer a solution at one place, but I follow your lead here, if you say that UCM is the way to go, then so be it.
We can give it a try. If it doesn't work as expected, we'd need more extension in the driver side.
Would you accept a model-quirk solution that is purely in the driver, which monitors jack presence on 0x1b and simply mutes the remaining speakers directly?
This way it could simply work as expected without an additional userspace setup of alsaucm, udev, acpid.
I played around with jack detection and I had the feeling that it did not work reliably, or maybe I misunderstood something?
[...]
I kept watching dmesg, when nothing is playing the plugged in/unplugged messages appear correctly, but if I start speaker test and replug during the playback, nothing is printed.
Check rather "Speaker Surround Jack" state upon plugging the headphone. Does it change reliably?
Check - where exactly? I did the following test: - ran speaker test - watched dmesg -wH with my printouts + ran alsa_listen in another terminal
While alsa_listen was correctly showing: jack/lineout LINEOUT plug jack/lineout LINEOUT unplug
dmesg remained silent. I figured that I may be hooking to the wrong function, in the code I pasted earlier I was hooking up to the hp_automute_hook, so I changed it to line_automute_hook, but the behavior is the same:
when something is playing, the automute hook is not being triggered upon plugging/unplugging. When nothing is playing, its being called reliably and the printouts are in sync with acpi_listen.
Which leads me to the following question: is the automute hook the right place for jack sensing in the driver at all? I did search for examples, but I failed at figuring out the proper way to hook up to jack state changes, or rather, I coul dsee that hda_jack_callback is being hooked to automute everywhere.
To make it clearer: at this point its not about the "correct state" of the jack, but rather about being triggered at all when something gets physically plugged into or unplugged from the port during an ongoing playback.
Note that even if you set such flags manually like the above, it won't work, because of the reasons I already mentioned.
Right, makes sense. In my debug code I was mostly misusing spec->gen.hp_jack_present as a "free" variable for remembering the state.
Turned out it was indeed a GPIO, I won't describe all the things I tried, in the end it was a lucky click on the dir_out checkbox in hda-analyzer while I was debugging the shared pin issue :)
The below HDA_FIXUP_VERBS does the trick, so I do have all 6 speakers working, finally!
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
Actually this must be paired with the corresponding bit of GPIO_DATA, too. Is the bit 0x02 of GPIO_DATA set or cleared? Usually setting it turns on the amp, but sometimes inverted.
If I understood everything correctly, then the bit is set, meaning that the GPIO signal is configured as output. I'll be honest, I exported the hda-analyzer setting as a python script (nice feature btw) and deducted the fixup verb setting from there (relevant part of the hda-analyzer export below):
def set(nid, verb, param): verb = (nid << 24) | (verb << 8) | param res = ioctl(FD, IOCTL_VERB_WRITE, struct.pack('II', verb, 0))
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
The only thing that is not quite clear to me is - does LFE still have its own pin and I was just not able to find it, but managed to unmute it via the GPIO? Or is the LFE speaker somehow "intenrally managed"? It works correctly though, alsa mixer is also capable of controlling LFE volume separately so it's fine, everything is also working with pulseaudio and a 5.1 profile on top.
It's usually just the external amp controlled via GPIO, and the signal must go through the pin 0x18 as well as the center channel, I suppose.
Ah, I see, I could try confirming if 0x18 is indeed the LFE speaker or not by "disconnecting" it in the pin configuration, I'd like to know for sure :)
To sum it up:
would you be OK with the "mute remaning channels upon jack sense in the driver" approach?
If the above is a yes, could you please point me to the right way of getting triggered upon jack state changes in the driver, since automute hooks seem not to trigger while something is playing?
Kind regards, Sergey
On Fri, 30 Aug 2019 13:45:10 +0200, Sergey 'Jin' Bostandzhyan wrote:
Hi Takashi,
On Thu, Aug 29, 2019 at 01:29:13PM +0200, Takashi Iwai wrote:
One possible way with the current code (and your latest patch) would be to create a UCM profile. The driver should still provide the jack detection on the pin 0x1b as "Surround Jack", and this can be used as the headphone jack detection.
I gogled it up and I did find HDAudio-Gigabyte-ALC1220DualCodecs as an example, so basically the solution in this case would be split between the driver and alsa-lib?
Right.
I did some digging yesterday and found out that documentation on the UCM profiles is sparse. I cloned your alsa-lib repo from github and looked at the available profiles. One thing that is not clear to me: the configurations have entries for JackControl and JackHWMute, however unless I totally missed it - those strings are not being parsed anywhere in the code? I first looked at parser.c as suggested by the man page and later grepped through the whole tree - nothing.
From various examples which I found when searching for answers I could see,
that in most cases the desired _verb section of the profile is activated via a udev rule, but I was not able to find anything that would be hooked to jack sense to switch between devices?
UCM profile is read directly PulseAudio. If a profile matches, PA uses the UCM profile instead of its mixer own parser.
I know it could be done via acpid, is this the way it is meant to be used or am I missing something, especially in regard to those JackHWMute strings?
How exactly is card longname which can be set in the driver connected to all this, I understand there is no automatic mechanism and that everyone needs to set up their custom udev scripts anyway?
I more or less got things to behave using a custom profile with device sections for Speakers and Headphones, which basically mutes everything except surround in Headphone mode, so technically I could hook it up to acpid and be done with it. To be honest, I'd be more happy with an "out of the box" experience, which leads me back to the driver.
With UCM profile, basically it's "out-of-the-box" for the normal use case.
Are there any disadvantages to muting the other channels directly in the driver? Or would it be a viable option to extend the auto-parser to handle the remaining channels?
It's not so trivial to extend. The logic of driver parser assumes the single purpose pins, and if they appear doubly, the behavior becomes confusing. For example, if you just add the same pin to both speaker_pins[] and hp_pins[], all speaker_pins[] get muted by the auto-mute, hence you'll mute the headphone again. OTOH, if you don't list up the pin in speaker_pins[], it won't be used for speakers for multi-channels, obviously.
This confirms what I have seen when trying things out last week, the pin could be either a speaker or a headphone, but not both, meaning I would either "lose" the ability to use 5.1 profiles or the system would not "see" the headphones as headphones.
Right, and it's impossible with the current code base because the headphone profile and 5.1 profile can't coexist.
Personally, I would prefer a solution at one place, but I follow your lead here, if you say that UCM is the way to go, then so be it.
We can give it a try. If it doesn't work as expected, we'd need more extension in the driver side.
Would you accept a model-quirk solution that is purely in the driver, which monitors jack presence on 0x1b and simply mutes the remaining speakers directly?
If it can be really implementable, that's fine. If you give a patch that is simple enough, I'll happily apply it.
Other than that, the approach with UCM profile would be simpler. It won't need anything else than what you already showed (pincfg and GPIO) in the driver side.
This way it could simply work as expected without an additional userspace setup of alsaucm, udev, acpid.
I played around with jack detection and I had the feeling that it did not work reliably, or maybe I misunderstood something?
[...]
I kept watching dmesg, when nothing is playing the plugged in/unplugged messages appear correctly, but if I start speaker test and replug during the playback, nothing is printed.
Check rather "Speaker Surround Jack" state upon plugging the headphone. Does it change reliably?
Check - where exactly? I did the following test:
- ran speaker test
- watched dmesg -wH with my printouts + ran alsa_listen in another terminal
While alsa_listen was correctly showing: jack/lineout LINEOUT plug jack/lineout LINEOUT unplug
dmesg remained silent. I figured that I may be hooking to the wrong function, in the code I pasted earlier I was hooking up to the hp_automute_hook, so I changed it to line_automute_hook, but the behavior is the same:
when something is playing, the automute hook is not being triggered upon plugging/unplugging. When nothing is playing, its being called reliably and the printouts are in sync with acpi_listen.
Which leads me to the following question: is the automute hook the right place for jack sensing in the driver at all? I did search for examples, but I failed at figuring out the proper way to hook up to jack state changes, or rather, I coul dsee that hda_jack_callback is being hooked to automute everywhere.
To make it clearer: at this point its not about the "correct state" of the jack, but rather about being triggered at all when something gets physically plugged into or unplugged from the port during an ongoing playback.
Forget about the whole auto-mute inside the driver. This won't work without the intrusive changes.
The UCM doesn't need the auto-mute behavior inside the driver. It can switch the profile by itself. And PulseAudio drives it properly when a UCM profile is set up. That's all.
You can still change the individual channel volumes/mutes if you want, too. The only caveat is that there is no individual headphone volume; it's controlled as surround.
The only remaining question is whether the specific Jack control notifies via ALSA control API properly. You can run like % /usr/sbin/alsactl monitor and see which control gets notified when you plug the headhpone jack. Just give which jack control corresponds to the actual headphone jack. Then it can be put into the UCM profile.
Note that even if you set such flags manually like the above, it won't work, because of the reasons I already mentioned.
Right, makes sense. In my debug code I was mostly misusing spec->gen.hp_jack_present as a "free" variable for remembering the state.
Turned out it was indeed a GPIO, I won't describe all the things I tried, in the end it was a lucky click on the dir_out checkbox in hda-analyzer while I was debugging the shared pin issue :)
The below HDA_FIXUP_VERBS does the trick, so I do have all 6 speakers working, finally!
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
Actually this must be paired with the corresponding bit of GPIO_DATA, too. Is the bit 0x02 of GPIO_DATA set or cleared? Usually setting it turns on the amp, but sometimes inverted.
If I understood everything correctly, then the bit is set, meaning that the GPIO signal is configured as output.
The GPIO_DIRECTION specifies whether input or output, and the actual value is passed via GPIO_DATA. Both have to be specified, not only one.
I'll be honest, I exported the hda-analyzer setting as a python script (nice feature btw) and deducted the fixup verb setting from there (relevant part of the hda-analyzer export below):
def set(nid, verb, param): verb = (nid << 24) | (verb << 8) | param res = ioctl(FD, IOCTL_VERB_WRITE, struct.pack('II', verb, 0))
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
This needs the paired SET_GPIO_DATA for a proper operation. Your analysis implicit assumes some default value that is already set by the hardware.
The only thing that is not quite clear to me is - does LFE still have its own pin and I was just not able to find it, but managed to unmute it via the GPIO? Or is the LFE speaker somehow "intenrally managed"? It works correctly though, alsa mixer is also capable of controlling LFE volume separately so it's fine, everything is also working with pulseaudio and a 5.1 profile on top.
It's usually just the external amp controlled via GPIO, and the signal must go through the pin 0x18 as well as the center channel, I suppose.
Ah, I see, I could try confirming if 0x18 is indeed the LFE speaker or not by "disconnecting" it in the pin configuration, I'd like to know for sure :)
To sum it up:
would you be OK with the "mute remaning channels upon jack sense in the driver" approach?
... only if the patch is acceptable. I'm not going to this way by myself, but I'll be happily apply if a reasonable patch is provided.
If the above is a yes, could you please point me to the right way of getting triggered upon jack state changes in the driver, since automute hooks seem not to trigger while something is playing?
This isn't easy, as repeatedly mentioned, since the parser assumes only one role for each pin except for the input/output switching. In your case the pin shares for two outputs to be switched, and this isn't implemented at all.
thanks,
Takashi
Hi Takashi,
On Fri, Aug 30, 2019 at 02:22:42PM +0200, Takashi Iwai wrote:
From various examples which I found when searching for answers I could see,
that in most cases the desired _verb section of the profile is activated via a udev rule, but I was not able to find anything that would be hooked to jack sense to switch between devices?
UCM profile is read directly PulseAudio. If a profile matches, PA uses the UCM profile instead of its mixer own parser.
That was indeed the "missing link", I was searching for ALSA and UCM all the time, but did not think about PulseAudio at all. I was surprised to learn that PulseAudio parses the UCM profiles directly and that alsaucm is barely being used. It also seems that PulseAudio supports more configuration strings than the ones that the alsaucm man page refers to (parser.c).
With UCM profile, basically it's "out-of-the-box" for the normal use case.
I kind of got it to work with UCM. The drawback is, that I will have to manually define all those profiles that PulseAudio generates by default and also make sure to add mic and line-out jacks properly, which are currently being ignored by Pulse, because I did not list them in my UCM conf.
Other than that, the approach with UCM profile would be simpler. It won't need anything else than what you already showed (pincfg and GPIO) in the driver side.
Simpler...yes and no :) From what I have seen, all "default" Pulse profiles are replaced by the UCM, meaning that if I wanted them, I'd have to replicate all of them in my conf. It would work though.
Forget about the whole auto-mute inside the driver. This won't work without the intrusive changes.
I will paste some code with a simple idea that I had at the end of the mail, but of course my view may be very limited since I am pretty new to this, and I may not be seeing the bigger picture. I tried to make it a model quirk only solution, avoiding any changes elsewhere in the driver.
The only remaining question is whether the specific Jack control notifies via ALSA control API properly. You can run like % /usr/sbin/alsactl monitor and see which control gets notified when you plug the headhpone jack. Just give which jack control corresponds to the actual headphone jack. Then it can be put into the UCM profile.
Jack notifications work properly, I figured it out recently and they work with my UCM profile as well. My earlier questions were related to jack detection inside the kernel driver, I simply did not know what to hook up to and I have incorrectly chosen the automute hook which only added to confusion. I got this part working now, more on that further down.
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
Actually this must be paired with the corresponding bit of GPIO_DATA, too. Is the bit 0x02 of GPIO_DATA set or cleared? Usually setting it turns on the amp, but sometimes inverted.
If I understood everything correctly, then the bit is set, meaning that the GPIO signal is configured as output.
The GPIO_DIRECTION specifies whether input or output, and the actual value is passed via GPIO_DATA. Both have to be specified, not only one.
[...]
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
This needs the paired SET_GPIO_DATA for a proper operation. Your analysis implicit assumes some default value that is already set by the hardware.
If I understand it correctly, then "some value" is zero on my hardware:
# hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x0
Meanwhile I also figured out that /proc/asound/card0/codec#0 is providing this info as well:
IO[1]: enable=0, dir=1, wake=0, sticky=0, data=0, unsol=0
So the value seems to be 0 and I can add an explicit SET_GPIO_DATA verb quirk to set it in addition to SET_GPIO_DIRECTION, right?
If the above is a yes, could you please point me to the right way of getting triggered upon jack state changes in the driver, since automute hooks seem not to trigger while something is playing?
This isn't easy, as repeatedly mentioned, since the parser assumes only one role for each pin except for the input/output switching. In your case the pin shares for two outputs to be switched, and this isn't implemented at all.
I think we talked a bit past each other, based on your reply I understand that you were assuming that I am still trying to solve it in the parser in some generic way, which as you indeed repeatedly mentioned would not be easy.
At the same time I do not rule out the possibility, that I simply do not see the "bigger picture" and that you have already accounted for the proposal that follows :)
Given that this is my first driver hacking adventure, I was aiming for a much more localized solution, so I was not going to add generic support for shared pins. Once I learned how to subscribe to jack state changes I was able to implement the idea that I had inside the model quirk:
static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec, struct hda_jack_callback *cb) { /* surround speakers muted automatically when headphones are * plugged in, we'll mute/unmute the remaining channels manually: * 0x15 - front left/front right * 0x18 - front center/ LFE * */ if (snd_hda_jack_detect_state(codec, cb->nid) == HDA_JACK_PRESENT) { snd_hda_set_pin_ctl_cache(codec, 0x15, 0); snd_hda_set_pin_ctl_cache(codec, 0x18, 0); } else { snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT); snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT); } }
static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, const struct hda_fixup *fix, int action) { if (action == HDA_FIXUP_ACT_PRE_PROBE) { if (is_jack_detectable(codec, 0x1b)) { snd_hda_jack_detect_enable_callback(codec, 0x1b, alc662_aspire_ethos_mute_speakers); } } }
This was the idea that I have been referring to, when I was talking about "muting in the driver" after I learned that proper automuting based on hp pins was not possible as you indeed repeatedly stated.
The above seems to work quite well for me and does exactly what I want, PulseAudio presents all the autogenerated profiles and handles mic and line jacks itself, at the same time all unwanted speakers get muted as soon as I plug in my headphones into the jack pin that is shared with my surround speakers. Of course Pulse does not "know" anything about the headphones and does not switch profiles, but I don't mind since the user experience is as expected.
My earlier attempt was to send the pin widget control verbs directly, however then the pin got reconnected as soon as playback started. This does not happen when I use snd_hda_set_pin_ctl_cache(), but I am not quite sure about the cache, should I use the _cache function or the uncached one?
Another thing I am not sure about is, if I somehow disrupt power management by doing what I do? I saw that for instance restore_shutup_pins() does modify those connections as well and I would basically overwrite whatever it did in the case that the user plugs/unplugs the headphones.
Does the above code snippet look like a direction worth exploring, or is this "secretly-muting" idea not acceptable to you, meaning that I should really stop right here and live with UCM as the only solution?
Kind regards, Jin
On Sun, 01 Sep 2019 21:27:37 +0200, Sergey 'Jin' Bostandzhyan wrote:
Other than that, the approach with UCM profile would be simpler. It won't need anything else than what you already showed (pincfg and GPIO) in the driver side.
Simpler...yes and no :) From what I have seen, all "default" Pulse profiles are replaced by the UCM, meaning that if I wanted them, I'd have to replicate all of them in my conf. It would work though.
You just need to override codec->card->longname to some unique string and use it as UCM profile name. Check alc1220_fixup_gb_dual_codecs() as an example.
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
Actually this must be paired with the corresponding bit of GPIO_DATA, too. Is the bit 0x02 of GPIO_DATA set or cleared? Usually setting it turns on the amp, but sometimes inverted.
If I understood everything correctly, then the bit is set, meaning that the GPIO signal is configured as output.
The GPIO_DIRECTION specifies whether input or output, and the actual value is passed via GPIO_DATA. Both have to be specified, not only one.
[...]
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
This needs the paired SET_GPIO_DATA for a proper operation. Your analysis implicit assumes some default value that is already set by the hardware.
If I understand it correctly, then "some value" is zero on my hardware:
# hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x0
Meanwhile I also figured out that /proc/asound/card0/codec#0 is providing this info as well:
IO[1]: enable=0, dir=1, wake=0, sticky=0, data=0, unsol=0
So the value seems to be 0 and I can add an explicit SET_GPIO_DATA verb quirk to set it in addition to SET_GPIO_DIRECTION, right?
Yes. You need to set SET_GPIO_MASK=0x02, SET_GPIO_DIRECTION=0x02 and SET_GPIO_DATA=0x00 for that bit.
If the above is a yes, could you please point me to the right way of getting triggered upon jack state changes in the driver, since automute hooks seem not to trigger while something is playing?
This isn't easy, as repeatedly mentioned, since the parser assumes only one role for each pin except for the input/output switching. In your case the pin shares for two outputs to be switched, and this isn't implemented at all.
I think we talked a bit past each other, based on your reply I understand that you were assuming that I am still trying to solve it in the parser in some generic way, which as you indeed repeatedly mentioned would not be easy.
At the same time I do not rule out the possibility, that I simply do not see the "bigger picture" and that you have already accounted for the proposal that follows :)
Given that this is my first driver hacking adventure, I was aiming for a much more localized solution, so I was not going to add generic support for shared pins. Once I learned how to subscribe to jack state changes I was able to implement the idea that I had inside the model quirk:
static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec, struct hda_jack_callback *cb) { /* surround speakers muted automatically when headphones are * plugged in, we'll mute/unmute the remaining channels manually: * 0x15 - front left/front right * 0x18 - front center/ LFE * */ if (snd_hda_jack_detect_state(codec, cb->nid) == HDA_JACK_PRESENT) { snd_hda_set_pin_ctl_cache(codec, 0x15, 0); snd_hda_set_pin_ctl_cache(codec, 0x18, 0); } else { snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT); snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT); } }
static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, const struct hda_fixup *fix, int action) { if (action == HDA_FIXUP_ACT_PRE_PROBE) { if (is_jack_detectable(codec, 0x1b)) { snd_hda_jack_detect_enable_callback(codec, 0x1b, alc662_aspire_ethos_mute_speakers); } } }
This was the idea that I have been referring to, when I was talking about "muting in the driver" after I learned that proper automuting based on hp pins was not possible as you indeed repeatedly stated.
The above seems to work quite well for me and does exactly what I want, PulseAudio presents all the autogenerated profiles and handles mic and line jacks itself, at the same time all unwanted speakers get muted as soon as I plug in my headphones into the jack pin that is shared with my surround speakers. Of course Pulse does not "know" anything about the headphones and does not switch profiles, but I don't mind since the user experience is as expected.
Hm, OK, this amount of changes are acceptable. The hardware behavior itself is weird, and we have already tricky code, so it's no problem to keep some yet another tricky code as long as it's described enough in the comments and the changelog.
My earlier attempt was to send the pin widget control verbs directly, however then the pin got reconnected as soon as playback started. This does not happen when I use snd_hda_set_pin_ctl_cache(), but I am not quite sure about the cache, should I use the _cache function or the uncached one?
This should work, AFAIK. The *_set_pin_ctl_cache() remembers the last written value, as its name stands. That's restored again at the PM resume, for example.
The PM resume does re-trigger the jack detection callback, so it'll be written up again in anyway, though.
Another thing I am not sure about is, if I somehow disrupt power management by doing what I do? I saw that for instance restore_shutup_pins() does modify those connections as well and I would basically overwrite whatever it did in the case that the user plugs/unplugs the headphones.
This should be fine as-is. The shutup_pins() clears pins temporarily and the pins are resumed to the cached values in return.
One thing to be improved would be to drop the surround jack control. Adjust the pin config to different value with the fixed pin connection, so that the auto-parser won't create the "Surround Jack" control. This isn't needed by PA or else, otherwise it may be confusing.
thanks,
Takashi
Hi Takashi,
On Mon, Sep 02, 2019 at 08:41:48AM +0200, Takashi Iwai wrote:
Simpler...yes and no :) From what I have seen, all "default" Pulse profiles are replaced by the UCM, meaning that if I wanted them, I'd have to replicate all of them in my conf. It would work though.
You just need to override codec->card->longname to some unique string and use it as UCM profile name. Check alc1220_fixup_gb_dual_codecs() as an example.
no, no, that's not what I meant. I did understand how to tell PulseAudio which UCM to load, i.e. via the longname just as you wrote above.
However, what I then observed was: PulseAudio loads my UCM configuration and pavucontrol lists only the profiles which I have specified in the UCM.
So what I was trying to say is that I lose all the stock profiles that PulseAudio creates automatically and that list is quite long (i.e. Analog Surround 5.1 Output + Analog Stereo Input, same for 5.0, 4.1, 4.0 and so on), basically the stock profiles get dropped in favor of the ones that I provide in the UCM.
Meanwhile I also figured out that /proc/asound/card0/codec#0 is providing this info as well:
IO[1]: enable=0, dir=1, wake=0, sticky=0, data=0, unsol=0
So the value seems to be 0 and I can add an explicit SET_GPIO_DATA verb quirk to set it in addition to SET_GPIO_DIRECTION, right?
Yes. You need to set SET_GPIO_MASK=0x02, SET_GPIO_DIRECTION=0x02 and SET_GPIO_DATA=0x00 for that bit.
Thanks a lot, I read the hda-intel spec on GPIOs a couple of times, but I was somehow not getting the idea about the GPIO MASK, now it's clear what I was missing. I'll add those three verbs to my quirk.
[...]
The above seems to work quite well for me and does exactly what I want, PulseAudio presents all the autogenerated profiles and handles mic and line jacks itself, at the same time all unwanted speakers get muted as soon as I plug in my headphones into the jack pin that is shared with my surround speakers. Of course Pulse does not "know" anything about the headphones and does not switch profiles, but I don't mind since the user experience is as expected.
Hm, OK, this amount of changes are acceptable. The hardware behavior itself is weird, and we have already tricky code, so it's no problem to keep some yet another tricky code as long as it's described enough in the comments and the changelog.
Great, thank you! I will prepare a patch then, I like this approach a lot more than the UCM variant.
My earlier attempt was to send the pin widget control verbs directly, however then the pin got reconnected as soon as playback started. This does not happen when I use snd_hda_set_pin_ctl_cache(), but I am not quite sure about the cache, should I use the _cache function or the uncached one?
This should work, AFAIK. The *_set_pin_ctl_cache() remembers the last written value, as its name stands. That's restored again at the PM resume, for example.
The PM resume does re-trigger the jack detection callback, so it'll be written up again in anyway, though.
Thanks for the explanation, seems I picked the right function.
Another thing I am not sure about is, if I somehow disrupt power management by doing what I do? I saw that for instance restore_shutup_pins() does modify those connections as well and I would basically overwrite whatever it did in the case that the user plugs/unplugs the headphones.
This should be fine as-is. The shutup_pins() clears pins temporarily and the pins are resumed to the cached values in return.
I was more thinking of the scenario that shutup_pins() cleared them, some time afterwards the user unplugs headphones which triggers my jack-detect callback where I reconnect the pins, although the "shutup" condition is still valid. Maybe I'm overthinking it. If this is not a problem, then I'm indeed almost done - easier than I thought :)
One thing to be improved would be to drop the surround jack control. Adjust the pin config to different value with the fixed pin connection, so that the auto-parser won't create the "Surround Jack" control. This isn't needed by PA or else, otherwise it may be confusing.
Hmm, if I understand you correctly, then you are referring to bits 31:30 Port Connectivity?
It does not seem to work that way... I tried all combinations and I either lose jack detect support or I lose the 5.1 profile in Pulse.
With these settings snd_hda_jack_detect_state() never returns HDA_JACK_PRESENT: 0x91130012 [Fixed] Speaker at Int Rear 0xd1130012 [Both] Speaker at Int Rear
I can plug or unplug, I get called, but I always receive HDA_JACK_PHANTOM
snd_hda_jack_detect_state() works fine with "no physical connection to port": 0x51130012 [N/A] Speaker at Int Rear
But with the above pin setting I "lose" the 5.1 profile in Pulse...
Which leaves me with with what I had before: 0x11130012 [Jack] Speaker at Int Rear
Am I missing something or did you mean some other setting? Should I be using a different function instead of snd_hda_jack_detect_state() to check my jack state in the callback?
I will study the kernelnewbies howto a bit more (it's my first kernel patch submissoin) and will follow up with a patch soon.
Thank you very much for your help! I would not have come so far without your support, really happy that my audio finally works :)
Kind regards, Jin
On Mon, 02 Sep 2019 23:39:12 +0200, Sergey 'Jin' Bostandzhyan wrote:
One thing to be improved would be to drop the surround jack control. Adjust the pin config to different value with the fixed pin connection, so that the auto-parser won't create the "Surround Jack" control. This isn't needed by PA or else, otherwise it may be confusing.
Hmm, if I understand you correctly, then you are referring to bits 31:30 Port Connectivity?
It does not seem to work that way... I tried all combinations and I either lose jack detect support or I lose the 5.1 profile in Pulse.
With these settings snd_hda_jack_detect_state() never returns HDA_JACK_PRESENT: 0x91130012 [Fixed] Speaker at Int Rear 0xd1130012 [Both] Speaker at Int Rear
I can plug or unplug, I get called, but I always receive HDA_JACK_PHANTOM
snd_hda_jack_detect_state() works fine with "no physical connection to port": 0x51130012 [N/A] Speaker at Int Rear
But with the above pin setting I "lose" the 5.1 profile in Pulse...
Which leaves me with with what I had before: 0x11130012 [Jack] Speaker at Int Rear
Am I missing something or did you mean some other setting? Should I be using a different function instead of snd_hda_jack_detect_state() to check my jack state in the callback?
OK, this would be really tricky to work around it. It's merely a jack control that won't be referred by PA, so we can live with it, unless you see any obvious side effect.
So, when the patch is ready for submission, feel free to send it.
thanks,
Takashi
This notebook has 6 built in speakers for 5.1 surround support, however only two got autodetected and have also not been assigned correctly.
This patch enables all speakers and also fixes muting when headphones are plugged in.
The speaker layout is as follows:
pin 0x15 Front Left / Front Right pin 0x18 Front Center / Subwoofer pin 0x1b Rear Left / Rear Right (Surround)
The quirk will be enabled automatically on this hardware, but can also be activated manually via the model=aspire-ethos module parameter.
Caveat: pin 0x1b is shared between headphones jack and surround speakers. When headphones are plugged in, the surround speakers get muted automatically by the hardware, however all other speakers remain unmuted. Currently it's not possible to make use of the generic automute function in the driver, because such shared pins are not supported.
If we would change the pin settings to identify the pin as headphones, the surround channel and thus the ability to select 5.1 profiles would get lost.
This quirk solves the above problem by monitoring jack state of 0x1b and by connecting/disconnecting all remaining speaker pins when something gets plugged in or unplugged from the headphones jack port.
Signed-off-by: Sergey Bostandzhyan jin@mediatomb.cc --- sound/pci/hda/patch_realtek.c | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e333b3e30e31..5135e13da284 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8251,6 +8251,45 @@ static void alc662_fixup_usi_headset_mic(struct hda_codec *codec, } }
+static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec, + struct hda_jack_callback *cb) +{ + /* surround speakers at 0x1b already get muted automatically when + * headphones are plugged in, but we have to mute/unmute the remaining + * channels manually: + * 0x15 - front left/front right + * 0x18 - front center/ LFE + */ + if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) { + snd_hda_set_pin_ctl_cache(codec, 0x15, 0); + snd_hda_set_pin_ctl_cache(codec, 0x18, 0); + } else { + snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT); + snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT); + } +} + +static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Pin 0x1b: shared headphones jack and surround speakers */ + if (!is_jack_detectable(codec, 0x1b)) + return; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_jack_detect_enable_callback(codec, 0x1b, + alc662_aspire_ethos_mute_speakers); + break; + case HDA_FIXUP_ACT_INIT: + /* Make sure to start in a correct state, i.e. if + * headphones have been plugged in before powering up the system + */ + alc662_aspire_ethos_mute_speakers(codec, NULL); + break; + } +} + static struct coef_fw alc668_coefs[] = { WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0), WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80), @@ -8322,6 +8361,9 @@ enum { ALC662_FIXUP_USI_FUNC, ALC662_FIXUP_USI_HEADSET_MODE, ALC662_FIXUP_LENOVO_MULTI_CODECS, + ALC669_FIXUP_ACER_ASPIRE_ETHOS, + ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER, + ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET, };
static const struct hda_fixup alc662_fixups[] = { @@ -8648,6 +8690,33 @@ static const struct hda_fixup alc662_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc233_alc662_fixup_lenovo_dual_codecs, }, + [ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc662_fixup_aspire_ethos_hp, + }, + [ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER] = { + .type = HDA_FIXUP_VERBS, + /* subwoofer needs an extra GPIO setting to become audible */ + .v.verbs = (const struct hda_verb[]) { + {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, + { } + }, + .chained = true, + .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET + }, + [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x92130110 }, /* front speakers */ + { 0x18, 0x99130111 }, /* center/subwoofer */ + { 0x1b, 0x11130012 }, /* surround plus jack for HP */ + { } + }, + .chained = true, + .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER + }, };
static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -8693,6 +8762,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68), SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), + SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS),
#if 0 /* Below is a quirk table taken from the old code. @@ -8786,6 +8856,7 @@ static const struct hda_model_fixup alc662_fixup_models[] = { {.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"}, {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"}, {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, + {.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"}, {} };
On Fri, 06 Sep 2019 11:33:43 +0200, Sergey Bostandzhyan wrote:
This notebook has 6 built in speakers for 5.1 surround support, however only two got autodetected and have also not been assigned correctly.
This patch enables all speakers and also fixes muting when headphones are plugged in.
The speaker layout is as follows:
pin 0x15 Front Left / Front Right pin 0x18 Front Center / Subwoofer pin 0x1b Rear Left / Rear Right (Surround)
The quirk will be enabled automatically on this hardware, but can also be activated manually via the model=aspire-ethos module parameter.
Caveat: pin 0x1b is shared between headphones jack and surround speakers. When headphones are plugged in, the surround speakers get muted automatically by the hardware, however all other speakers remain unmuted. Currently it's not possible to make use of the generic automute function in the driver, because such shared pins are not supported.
If we would change the pin settings to identify the pin as headphones, the surround channel and thus the ability to select 5.1 profiles would get lost.
This quirk solves the above problem by monitoring jack state of 0x1b and by connecting/disconnecting all remaining speaker pins when something gets plugged in or unplugged from the headphones jack port.
Signed-off-by: Sergey Bostandzhyan jin@mediatomb.cc
Applied now. Thanks.
Takashi
Hi Takashi,
sorry - it's me again about the Acer 8951G LFE speaker.
On Fri, Aug 30, 2019 at 01:45:10PM +0200, Sergey 'Jin' Bostandzhyan wrote:
The below HDA_FIXUP_VERBS does the trick, so I do have all 6 speakers working, finally!
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
Actually this must be paired with the corresponding bit of GPIO_DATA, too. Is the bit 0x02 of GPIO_DATA set or cleared? Usually setting it turns on the amp, but sometimes inverted.
If I understood everything correctly, then the bit is set, meaning that the GPIO signal is configured as output. I'll be honest, I exported the hda-analyzer setting as a python script (nice feature btw) and deducted the fixup verb setting from there (relevant part of the hda-analyzer export below):
def set(nid, verb, param): verb = (nid << 24) | (verb << 8) | param res = ioctl(FD, IOCTL_VERB_WRITE, struct.pack('II', verb, 0))
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
it seems I indeed missed something here regarding GPIO_DATA, I really am not sure what the influence is, but after updating to Fedora 31 my LFE stopped working, even with the self compiled 5.4-rc8 kernel which I am running now (all the time before I was on Fedora 29 and I just backported my patch to 5.2.x and compiled the modules outside the tree after being done with the patch submission).
So ultimately, it seems I now need to do the following in my fixup (original commit was 00066e9733f629e536f6b7957de2ce11a85fe15a):
--- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8875,7 +8875,7 @@ static const struct hda_fixup alc662_fixups[] = { .v.verbs = (const struct hda_verb[]) { {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, { } }, .chained = true,
My question is: could something on the outside have influence on that? I am really very, very sure that I have tested LFE on kernel 5.4-rc before submitting the original patch and it has been working as submitted. Why did the behavior change now? What else could I have missed?
I guess I have to submit the above change again, but I would like to make sure that I am not missing something else somewhere, something that could cause a change of behavior yet again after some future update.
Kind regards, Jin
On Mon, 25 Nov 2019 18:39:02 +0100, Sergey 'Jin' Bostandzhyan wrote:
Hi Takashi,
sorry - it's me again about the Acer 8951G LFE speaker.
On Fri, Aug 30, 2019 at 01:45:10PM +0200, Sergey 'Jin' Bostandzhyan wrote:
The below HDA_FIXUP_VERBS does the trick, so I do have all 6 speakers working, finally!
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
Actually this must be paired with the corresponding bit of GPIO_DATA, too. Is the bit 0x02 of GPIO_DATA set or cleared? Usually setting it turns on the amp, but sometimes inverted.
If I understood everything correctly, then the bit is set, meaning that the GPIO signal is configured as output. I'll be honest, I exported the hda-analyzer setting as a python script (nice feature btw) and deducted the fixup verb setting from there (relevant part of the hda-analyzer export below):
def set(nid, verb, param): verb = (nid << 24) | (verb << 8) | param res = ioctl(FD, IOCTL_VERB_WRITE, struct.pack('II', verb, 0))
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
it seems I indeed missed something here regarding GPIO_DATA, I really am not sure what the influence is, but after updating to Fedora 31 my LFE stopped working, even with the self compiled 5.4-rc8 kernel which I am running now (all the time before I was on Fedora 29 and I just backported my patch to 5.2.x and compiled the modules outside the tree after being done with the patch submission).
So ultimately, it seems I now need to do the following in my fixup (original commit was 00066e9733f629e536f6b7957de2ce11a85fe15a):
--- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8875,7 +8875,7 @@ static const struct hda_fixup alc662_fixups[] = { .v.verbs = (const struct hda_verb[]) { {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
{0x01, AC_VERB_SET_GPIO_DATA, 0x00},
{0x01, AC_VERB_SET_GPIO_DATA, 0x02}, { } }, .chained = true,
That makes more sense. Usually GPIO pin is off as default, and the driver needs to turn it up manually for a special usage.
My question is: could something on the outside have influence on that? I am really very, very sure that I have tested LFE on kernel 5.4-rc before submitting the original patch and it has been working as submitted. Why did the behavior change now? What else could I have missed?
Maybe the chip kept the GPIO pin on after warm boot from Windows or such?
Please make sure that which value actually is on and which is off. You can change the GPIO bit dynamically via hda-verb, so you can check whether the speaker works or not at each flip.
thanks,
Takashi
Hi Takashi,
On Wed, Nov 27, 2019 at 12:28:59PM +0100, Takashi Iwai wrote:
sorry - it's me again about the Acer 8951G LFE speaker.
On Fri, Aug 30, 2019 at 01:45:10PM +0200, Sergey 'Jin' Bostandzhyan wrote:
The below HDA_FIXUP_VERBS does the trick, so I do have all 6 speakers working, finally!
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
Actually this must be paired with the corresponding bit of GPIO_DATA, too. Is the bit 0x02 of GPIO_DATA set or cleared? Usually setting it turns on the amp, but sometimes inverted.
If I understood everything correctly, then the bit is set, meaning that the GPIO signal is configured as output. I'll be honest, I exported the hda-analyzer setting as a python script (nice feature btw) and deducted the fixup verb setting from there (relevant part of the hda-analyzer export below):
def set(nid, verb, param): verb = (nid << 24) | (verb << 8) | param res = ioctl(FD, IOCTL_VERB_WRITE, struct.pack('II', verb, 0))
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
it seems I indeed missed something here regarding GPIO_DATA, I really am not sure what the influence is, but after updating to Fedora 31 my LFE stopped working, even with the self compiled 5.4-rc8 kernel which I am running now (all the time before I was on Fedora 29 and I just backported my patch to 5.2.x and compiled the modules outside the tree after being done with the patch submission).
So ultimately, it seems I now need to do the following in my fixup (original commit was 00066e9733f629e536f6b7957de2ce11a85fe15a):
--- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8875,7 +8875,7 @@ static const struct hda_fixup alc662_fixups[] = { .v.verbs = (const struct hda_verb[]) { {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
{0x01, AC_VERB_SET_GPIO_DATA, 0x00},
{0x01, AC_VERB_SET_GPIO_DATA, 0x02}, { } }, .chained = true,
That makes more sense. Usually GPIO pin is off as default, and the driver needs to turn it up manually for a special usage.
My question is: could something on the outside have influence on that? I am really very, very sure that I have tested LFE on kernel 5.4-rc before submitting the original patch and it has been working as submitted. Why did the behavior change now? What else could I have missed?
Maybe the chip kept the GPIO pin on after warm boot from Windows or such?
This is unlikely as I do not have Windows or any other OS installed on this system. I dug through the thread and found the following:
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
This needs the paired SET_GPIO_DATA for a proper operation. Your analysis implicit assumes some default value that is already set by the hardware.
If I understand it correctly, then "some value" is zero on my hardware:
# hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x0
Meanwhile I also figured out that /proc/asound/card0/codec#0 is providing this info as well:
IO[1]: enable=0, dir=1, wake=0, sticky=0, data=0, unsol=0
So the value seems to be 0 and I can add an explicit SET_GPIO_DATA verb quirk to set it in addition to SET_GPIO_DIRECTION, right?
You then helped me, explaining how I could properly initialize it, which I incorporated in the original patch.
So we did check that and I am positive that the LFE did work back then, which really confuses me now.
Please make sure that which value actually is on and which is off. You can change the GPIO bit dynamically via hda-verb, so you can check whether the speaker works or not at each flip.
OK, so the starting point (now with my local update to the driver): # hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x2
From /proc/asound/card0/codec#0:
State of AFG node 0x01: Power states: D0 D1 D2 D3 CLKSTOP Power: setting=D0, actual=D0 GPIO: io=2, o=0, i=0, unsolicited=1, wake=0 IO[0]: enable=0, dir=0, wake=0, sticky=0, data=0, unsol=0 IO[1]: enable=1, dir=1, wake=0, sticky=0, data=1, unsol=0
Pulse profile "Analog Surround 5.1 Output + Analog Stereo Input" is active, speaker test (via the pulse/sound applet UI) delives audible noise on the LFE.
I'm flipping data in hda-analyzer now and rechecking afterwards:
# hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x0
And: State of AFG node 0x01: Power states: D0 D1 D2 D3 CLKSTOP Power: setting=D0, actual=D0 GPIO: io=2, o=0, i=0, unsolicited=1, wake=0 IO[0]: enable=0, dir=0, wake=0, sticky=0, data=0, unsol=0 IO[1]: enable=1, dir=1, wake=0, sticky=0, data=0, unsol=0
LFE is no longer audible in speaker test.
Reenabling again, this time I just used hda-verb directly: # hda-verb /dev/snd/hwC0D0 0x01 SET_GPIO_DATA 0x02 nid = 0x1, verb = 0x715, param = 0x2 value = 0x0
And checking: # hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x2
LFE becomes audible again.
Now, if that would help, I could try to install Fedora 29 on some external harddrive and reproduce my summer setup, to confirm that it has been working with data pin disabled. Alltough I am certain that it was the case, because I retested this several times prior to submitting the patch.
Question is, if we would learn something from that?
How should I proceed? Just submit an update to have the data pin active on init or is this weirdness worth debugging?
Thanks, Jin
On Wed, 27 Nov 2019 17:17:57 +0100, Sergey 'Jin' Bostandzhyan wrote:
Hi Takashi,
On Wed, Nov 27, 2019 at 12:28:59PM +0100, Takashi Iwai wrote:
sorry - it's me again about the Acer 8951G LFE speaker.
On Fri, Aug 30, 2019 at 01:45:10PM +0200, Sergey 'Jin' Bostandzhyan wrote:
The below HDA_FIXUP_VERBS does the trick, so I do have all 6 speakers working, finally!
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}
Actually this must be paired with the corresponding bit of GPIO_DATA, too. Is the bit 0x02 of GPIO_DATA set or cleared? Usually setting it turns on the amp, but sometimes inverted.
If I understood everything correctly, then the bit is set, meaning that the GPIO signal is configured as output. I'll be honest, I exported the hda-analyzer setting as a python script (nice feature btw) and deducted the fixup verb setting from there (relevant part of the hda-analyzer export below):
def set(nid, verb, param): verb = (nid << 24) | (verb << 8) | param res = ioctl(FD, IOCTL_VERB_WRITE, struct.pack('II', verb, 0))
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
it seems I indeed missed something here regarding GPIO_DATA, I really am not sure what the influence is, but after updating to Fedora 31 my LFE stopped working, even with the self compiled 5.4-rc8 kernel which I am running now (all the time before I was on Fedora 29 and I just backported my patch to 5.2.x and compiled the modules outside the tree after being done with the patch submission).
So ultimately, it seems I now need to do the following in my fixup (original commit was 00066e9733f629e536f6b7957de2ce11a85fe15a):
--- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8875,7 +8875,7 @@ static const struct hda_fixup alc662_fixups[] = { .v.verbs = (const struct hda_verb[]) { {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
{0x01, AC_VERB_SET_GPIO_DATA, 0x00},
{0x01, AC_VERB_SET_GPIO_DATA, 0x02}, { } }, .chained = true,
That makes more sense. Usually GPIO pin is off as default, and the driver needs to turn it up manually for a special usage.
My question is: could something on the outside have influence on that? I am really very, very sure that I have tested LFE on kernel 5.4-rc before submitting the original patch and it has been working as submitted. Why did the behavior change now? What else could I have missed?
Maybe the chip kept the GPIO pin on after warm boot from Windows or such?
This is unlikely as I do not have Windows or any other OS installed on this system. I dug through the thread and found the following:
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
This needs the paired SET_GPIO_DATA for a proper operation. Your analysis implicit assumes some default value that is already set by the hardware.
If I understand it correctly, then "some value" is zero on my hardware:
# hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x0
Meanwhile I also figured out that /proc/asound/card0/codec#0 is providing this info as well:
IO[1]: enable=0, dir=1, wake=0, sticky=0, data=0, unsol=0
So the value seems to be 0 and I can add an explicit SET_GPIO_DATA verb quirk to set it in addition to SET_GPIO_DIRECTION, right?
You then helped me, explaining how I could properly initialize it, which I incorporated in the original patch.
So we did check that and I am positive that the LFE did work back then, which really confuses me now.
Hm, I don't know what happened then. In most cases, the value wasn't correctly reflected when you actually checked (e.g. the runtime PM was triggered and it was cleared by some reason or such...)
Please make sure that which value actually is on and which is off. You can change the GPIO bit dynamically via hda-verb, so you can check whether the speaker works or not at each flip.
OK, so the starting point (now with my local update to the driver): # hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x2
From /proc/asound/card0/codec#0:
State of AFG node 0x01: Power states: D0 D1 D2 D3 CLKSTOP Power: setting=D0, actual=D0 GPIO: io=2, o=0, i=0, unsolicited=1, wake=0 IO[0]: enable=0, dir=0, wake=0, sticky=0, data=0, unsol=0 IO[1]: enable=1, dir=1, wake=0, sticky=0, data=1, unsol=0
Pulse profile "Analog Surround 5.1 Output + Analog Stereo Input" is active, speaker test (via the pulse/sound applet UI) delives audible noise on the LFE.
I'm flipping data in hda-analyzer now and rechecking afterwards:
# hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x0
And: State of AFG node 0x01: Power states: D0 D1 D2 D3 CLKSTOP Power: setting=D0, actual=D0 GPIO: io=2, o=0, i=0, unsolicited=1, wake=0 IO[0]: enable=0, dir=0, wake=0, sticky=0, data=0, unsol=0 IO[1]: enable=1, dir=1, wake=0, sticky=0, data=0, unsol=0
LFE is no longer audible in speaker test.
Reenabling again, this time I just used hda-verb directly: # hda-verb /dev/snd/hwC0D0 0x01 SET_GPIO_DATA 0x02 nid = 0x1, verb = 0x715, param = 0x2 value = 0x0
And checking: # hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x2
LFE becomes audible again.
OK, so it's clear now that the bit on turns on the LFE amp.
Now, if that would help, I could try to install Fedora 29 on some external harddrive and reproduce my summer setup, to confirm that it has been working with data pin disabled. Alltough I am certain that it was the case, because I retested this several times prior to submitting the patch.
Question is, if we would learn something from that?
How should I proceed? Just submit an update to have the data pin active on init or is this weirdness worth debugging?
The patch like below should work, I suppose?
Takashi
--- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8424,6 +8424,8 @@ static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, case HDA_FIXUP_ACT_PRE_PROBE: snd_hda_jack_detect_enable_callback(codec, 0x1b, alc662_aspire_ethos_mute_speakers); + /* subwoofer needs an extra GPIO setting to become audible */ + alc_setup_gpio(codec, 0x02); break; case HDA_FIXUP_ACT_INIT: /* Make sure to start in a correct state, i.e. if @@ -8506,7 +8508,6 @@ enum { ALC662_FIXUP_USI_HEADSET_MODE, ALC662_FIXUP_LENOVO_MULTI_CODECS, ALC669_FIXUP_ACER_ASPIRE_ETHOS, - ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER, ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET, };
@@ -8838,18 +8839,6 @@ static const struct hda_fixup alc662_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc662_fixup_aspire_ethos_hp, }, - [ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER] = { - .type = HDA_FIXUP_VERBS, - /* subwoofer needs an extra GPIO setting to become audible */ - .v.verbs = (const struct hda_verb[]) { - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, - { } - }, - .chained = true, - .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET - }, [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8859,7 +8848,7 @@ static const struct hda_fixup alc662_fixups[] = { { } }, .chained = true, - .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER + .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET }, };
On Wed, Nov 27, 2019 at 05:41:42PM +0100, Takashi Iwai wrote: [...]
This is unlikely as I do not have Windows or any other OS installed on this system. I dug through the thread and found the following:
set(0x01, 0x717, 0x02) # 0x01071702 (SET_GPIO_DIRECTION)
This needs the paired SET_GPIO_DATA for a proper operation. Your analysis implicit assumes some default value that is already set by the hardware.
If I understand it correctly, then "some value" is zero on my hardware:
# hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x0
Meanwhile I also figured out that /proc/asound/card0/codec#0 is providing this info as well:
IO[1]: enable=0, dir=1, wake=0, sticky=0, data=0, unsol=0
So the value seems to be 0 and I can add an explicit SET_GPIO_DATA verb quirk to set it in addition to SET_GPIO_DIRECTION, right?
You then helped me, explaining how I could properly initialize it, which I incorporated in the original patch.
So we did check that and I am positive that the LFE did work back then, which really confuses me now.
Hm, I don't know what happened then. In most cases, the value wasn't correctly reflected when you actually checked (e.g. the runtime PM was triggered and it was cleared by some reason or such...)
I see... it's fine as long as it does not flip again on 5.4 at some later point, otherwise I'd feel somewhat embarrased asking you to switch it back and forth :)
Should it indeed happen, then some deeper investigation would be in order, but since you wrote that the new data value anyway makes much more sense now, then let's hope it will not change anymore.
Please make sure that which value actually is on and which is off. You can change the GPIO bit dynamically via hda-verb, so you can check whether the speaker works or not at each flip.
OK, so the starting point (now with my local update to the driver): # hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x2
From /proc/asound/card0/codec#0:
State of AFG node 0x01: Power states: D0 D1 D2 D3 CLKSTOP Power: setting=D0, actual=D0 GPIO: io=2, o=0, i=0, unsolicited=1, wake=0 IO[0]: enable=0, dir=0, wake=0, sticky=0, data=0, unsol=0 IO[1]: enable=1, dir=1, wake=0, sticky=0, data=1, unsol=0
Pulse profile "Analog Surround 5.1 Output + Analog Stereo Input" is active, speaker test (via the pulse/sound applet UI) delives audible noise on the LFE.
I'm flipping data in hda-analyzer now and rechecking afterwards:
# hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x0
And: State of AFG node 0x01: Power states: D0 D1 D2 D3 CLKSTOP Power: setting=D0, actual=D0 GPIO: io=2, o=0, i=0, unsolicited=1, wake=0 IO[0]: enable=0, dir=0, wake=0, sticky=0, data=0, unsol=0 IO[1]: enable=1, dir=1, wake=0, sticky=0, data=0, unsol=0
LFE is no longer audible in speaker test.
Reenabling again, this time I just used hda-verb directly: # hda-verb /dev/snd/hwC0D0 0x01 SET_GPIO_DATA 0x02 nid = 0x1, verb = 0x715, param = 0x2 value = 0x0
And checking: # hda-verb /dev/snd/hwC0D0 0x01 GET_GPIO_DATA 0x02 nid = 0x1, verb = 0xf15, param = 0x2 value = 0x2
LFE becomes audible again.
OK, so it's clear now that the bit on turns on the LFE amp.
Yes, this is what I am observing with the current setup.
Now, if that would help, I could try to install Fedora 29 on some external harddrive and reproduce my summer setup, to confirm that it has been working with data pin disabled. Alltough I am certain that it was the case, because I retested this several times prior to submitting the patch.
Question is, if we would learn something from that?
How should I proceed? Just submit an update to have the data pin active on init or is this weirdness worth debugging?
The patch like below should work, I suppose?
--- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8424,6 +8424,8 @@ static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec, case HDA_FIXUP_ACT_PRE_PROBE: snd_hda_jack_detect_enable_callback(codec, 0x1b, alc662_aspire_ethos_mute_speakers);
/* subwoofer needs an extra GPIO setting to become audible */
break; case HDA_FIXUP_ACT_INIT: /* Make sure to start in a correct state, i.e. ifalc_setup_gpio(codec, 0x02);
@@ -8506,7 +8508,6 @@ enum { ALC662_FIXUP_USI_HEADSET_MODE, ALC662_FIXUP_LENOVO_MULTI_CODECS, ALC669_FIXUP_ACER_ASPIRE_ETHOS,
- ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER, ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET,
};
@@ -8838,18 +8839,6 @@ static const struct hda_fixup alc662_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc662_fixup_aspire_ethos_hp, },
- [ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER] = {
.type = HDA_FIXUP_VERBS,
/* subwoofer needs an extra GPIO setting to become audible */
.v.verbs = (const struct hda_verb[]) {
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
{0x01, AC_VERB_SET_GPIO_DATA, 0x00},
{ }
},
.chained = true,
.chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET
- }, [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) {
@@ -8859,7 +8848,7 @@ static const struct hda_fixup alc662_fixups[] = { { } }, .chained = true,
.chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_SUBWOOFER
},.chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET
};
Yes! I tested it vs a6ed68d6468bd5a3da78a103344ded1435fed57a in git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git (which was the most recent commit on the master branch at that time) and it works fine, LFE is audible. Could you please apply it?
Thanks a lot for the help! I hope this notebooks sound won't make any further problems :)
Kind regards, Sergey
participants (3)
-
Sergey 'Jin' Bostandzhyan
-
Sergey Bostandzhyan
-
Takashi Iwai