Friday 14 February 2020

Sound [subwoofer and microphone] on Lenovo Yoga S940 in Linux

I did not get microphone and subwoofer working out of the box in Ubuntu 19.10 on Lenovo Yoga S940 (ALC298). I played around with hdajackretask as was suggested on forums. Microphone was not even detected.

Eventually I came across the post about the laptop Lenovo Yoga C940 which I suppose is quite similar to S940. It suggested to use Sound Open Firmware (SOF). Even though the post suggested that one needs kernel 5.5 for me it worked with default 5.3.0-29-generic. SOF support should be enabled in the kernel. To check if current kernel supports SOF run

cat /boot/config-`uname -r` | grep SOF

and see if CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL=y

One also need to  download the sof  firmware and put  it into /lib/firmware/intel/sof/. In my case there  already was a file sof-cnl.ri.
I got the latest  version (1.4.2) from https://github.com/thesofproject/sof/releases/ (file sof-cnl-v1.4.2.ri, cnl stands for cannon lake) and copied it to /lib/firmware/intel/sof/sof-cnl.ri

I added two lines into /etc/modprobe.d/blacklist.conf:

blacklist snd_hda_intel
blacklist snd_soc_skl

after next reboot my laptop switched to SOF. Sound was working but still no subwoofer. Microphone was detectable now but was not working. Interestingly microphone registered input when headphone jack was plugged in and music was on.

I came across another post about SOF and lenovo hardware by DaveOber.
I took following configuration from his instruction:
First create a directory /usr/share/alsa/ucm/sof-skl_hda_card/.
Put following text into /usr/share/alsa/ucm/sof-skl_hda_card/HiFi:

# Use case Configuration for skl-hda-card

SectionVerb {

 EnableSequence [
  cdev "hw:sofsklhdacard"
  cset "name='Master Playback Switch' on"
  cset "name='Capture Switch' on"
  
 ]

 DisableSequence [
  cdev "hw:sofsklhdacard"
 ]

        Value {
        }
}


SectionDevice."HDMI1" {
        Comment "HDMI1/DP1 Output"

#        ConflictingDevice [
                # "Headset"
#        ]

        EnableSequence [
                cdev "hw:sofsklhdacard"
                cset "name='hif5-0 Jack Switch' on"
                cset "name='Pin5-Port0 Mux' 1"
        ]

        DisableSequence [
                cdev "hw:sofsklhdacard"
                cset "name='Pin5-Port0 Mux' 0"
                cset "name='hif5-0 Jack Switch' off"
        ]

        Value {
                PlaybackPCM "hw:0,3"
                PlaybackChannels "2"
                JackControl "HDMI/DP, pcm=11 Jack"
                # JackHWMute "Headset"
        }
}

SectionDevice."HDMI2" {
        Comment "HDMI2/DP2 Output"

#        ConflictingDevice [
                # "Headset"
#        ]

        EnableSequence [
                cdev "hw:sofsklhdacard"
                cset "name='hif6-0 Jack Switch' on"
                cset "name='Pin6-Port0 Mux' 2"
        ]

        DisableSequence [
                cdev "hw:sofsklhdacard"
                cset "name='Pin6-Port0 Mux' 0"
                cset "name='hif6-0 Jack Switch' off"
        ]

        Value {
                PlaybackPCM "hw:0,4"
                PlaybackChannels "2"
                JackControl "HDMI/DP, pcm=12 Jack"
                # JackHWMute "Headset"
        }
}

SectionDevice."HDMI3" {
        Comment "HDMI3/DP3 Output"

#        ConflictingDevice [
                # "Headset"
#        ]

        EnableSequence [
                cdev "hw:sofsklhdacard"
                cset "name='hif7-0 Jack Switch' on"
                cset "name='Pin7-Port0 Mux' 3"
        ]

        DisableSequence [
                cdev "hw:sofsklhdacard"
                cset "name='Pin7-Port0 Mux' 0"
                cset "name='hif7-0 Jack Switch' off"
        ]

        Value {
                PlaybackPCM "hw:0,5"
                PlaybackChannels "2"
                JackControl "HDMI/DP, pcm=13 Jack"
                # JackHWMute "Headset"
        }
}


SectionDevice."Headphone" {
 Comment "Headphone"

 #ConflictingDevice [
        #        "Speaker"
        #]
 
 EnableSequence [
  cdev "hw:sofsklhdacard"
  cset "name='Headphone Playback Volume' 80"
  #cset "name='Headphone Mic Boost Volume' 1"
  cset "name='Headphone Playback Switch' on"
 ]

 DisableSequence [
  cdev "hw:sofsklhdacard"
  cset "name='Headphone Playback Switch' off"
 ]

 Value {
  PlaybackPCM "hw:sofsklhdacard,0"
  PlaybackChannels "2"
  #JackHWMute "Speaker"
  JackName "sof-skl_hda_card Headphone"
  JackType "gpio"
  JackSwitch "12"
  JackControl "Headphone Jack"
 }
}

SectionDevice."Speaker" {
        Comment "Speaker"

        #ConflictingDevice [
        #        "Headphone"
        #]

        EnableSequence [
                cdev "hw:sofsklhdacard"
                cset "name='Speaker Playback Switch' on"
                cset "name='Speaker Playback Volume' 80"
        ]

        DisableSequence [
                cset "name='Speaker Playback Switch' off"
        ]

        Value {
  PlaybackPCM "hw:sofsklhdacard,0"
  JackHWMute "Headphone"
                PlaybackChannels "2"
        }
}

SectionDevice."Bass Speaker" {
        Comment "Bass Speaker"

#       ConflictingDevice [
#                "Headphone"
#        ]

        EnableSequence [
                cdev "hw:sofsklhdacard"
                cset "name='Bass Speaker Playback Switch' on"
  cset "name='Speaker Playback Switch' on"
  cset "name='Speaker Playback Volume' 80"
        ]

        DisableSequence [
                cset "name='Speaker Playback Switch' off"
                cset "name='Bass Speaker Playback Switch' off"
        ]
        Value {
                PlaybackPCM "hw:sofsklhdacard,0"
                JackHWMute "Headphone"
                PlaybackChannels "2"
        }
}

SectionDevice."Headset" {
 Comment "Headset Mic"

        ConflictingDevice [
                "DMIC Stereo"
        ]

        EnableSequence [
                cdev "hw:sofsklhdacard"
#  cset "name='Headphone Mic Boost Volume' 1"
#  cset "name='Capture Switch' on"
        ]

        DisableSequence [
                cdev "hw:sofsklhdacard"
#  cset "name='Capture Switch' off"
        ]

        Value {
                CapturePCM "hw:0,0"
                CaptureChannels "2"
  JackControl "Mic Jack"
#  JackHWMute "DMIC Stereo"
        }
}

SectionDevice."Dmic" {
        Comment "DMIC Stereo"

        ConflictingDevice [
                "Headset Mic"
        ]

        EnableSequence [
                cdev "hw:sofsklhdacard"
#  cset "name='Capture Switch' on"
                cset "name='PGA10.0 10 Master Capture Volume' 75"
        ]

        DisableSequence [
                cdev "hw:sofsklhdacard"
#  cset "name='Capture Switch' off"
        ]

        Value {
                CapturePCM "hw:0,6"
                CaptureChannels "2"
#  JackHWMute "Headset Mic"
        }
}

Whenever I  was plugging and unplugging headphones my system would switch to the last device which in Dave's confing was HDMI, so I rearranged the entries and put HDMI entries higher, now I always have Bass Speaker as  a default one.

And then create a file /usr/share/alsa/ucm/sof-skl_hda_card/sof-skl_hda_card.conf
and put there

SectionUseCase."HiFi" {
 File "HiFi"
 Comment "Play HiFi quality Music"
}

Now you can run

pulseaudio --kill; sudo alsactl kill rescan; pulseaudio --start

and enjoy working microphone and subwoofer. You can compare the  sound when you choose Speaker or Bass Speaker as a sound device. The difference is  stunning.

I also added two lines to /etc/pulse/daemon.conf

 enable-lfe-remixing = yes
 lfe-crossover-freq = 250


It is hard to say it is  doing anything though.

The last issue is that whenever I plug and unplug  headphones volume in alsamixer goes to 35. Have not solved this issue yet.

I also noticed that there was some hissing from the speakers. I could solve it by changing microphone gain in alsamixer to zero. Interestingly microphone still works, but whenever I run the app which uses microphone hissing reappears. Hope it will be properly solved in the next versions of the driver.

No comments: