Update: 09/01/2024
Kernel 6.7 now includes full and proper fixes for the 2023 ROG laptops with Cirrus amps. If you are running Fedora 39, the kernel by default includes the new patches.
For other laptops you may still require some hacking in the kernel, but with the new framework for the cirrus amps it should be much easier to do so correctly.
Below this line is all previous info, kept for informational purposes and reference if someone needs to add new quirks.
In general for an unlisted laptop you will need to do 3 things:
- Find and add the firmware (see the Firmware section below)
- Ensure the amp is initalised (see Kernel patch section below)
- Finally, add your laptop data to this table and the one further down in the same file.
For step 3 you may find the Dump ACPI
section helpful, but most likely not if you've ended up here. It will at least help you with ID and SPI vs I2c. For the actual kernel work finding the correct values may be easier by using the SSDT patch method first, then building the kernel patch from those values. To find the nanohenry
, microfarad
, and milliamp
values I'm not so sure how, you may find some info by ripgrepping through the Windows install or driver data.
Original article follows...
The issue
Most of the 2023 ASUS laptops appear to have a Cirrus CS35L41 amp inside to driver the bass/mid-range speakers. Unfortunately the driver for it won't use it as it is missing a series of properties stored in the _DSD
of the ACPI.
You will know if this is the case for you if you look in dmesg
with dmesg |grep _DSD
and see similar to:
Jun 28 20:02:56: cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: Error: ACPI _DSD Properties are missing for HID CSC3551.
Jun 28 20:02:56: cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: error -EINVAL: Platform not supported
Jun 28 20:02:56: cs35l41-hda: probe of spi0-CSC3551:00-cs35l41-hda.0 failed with error -22
Jun 28 20:02:56: iwlwifi 0000:00:14.3: firmware: failed to load iwl-debug-yoyo.bin (-2)
Jun 28 20:02:56: iwlwifi 0000:00:14.3: firmware: failed to load iwl-debug-yoyo.bin (-2)
Jun 28 20:02:56: iwlwifi 0000:00:14.3: loaded firmware version 72.a764baac.0 so-a0-gf-a0-72.ucode op_mode iwlmvm
Jun 28 20:02:56: cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: Error: ACPI _DSD Properties are missing for HID CSC3551.
Jun 28 20:02:56: cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: error -EINVAL: Platform not supported
Jun 28 20:02:56: cs35l41-hda: probe of spi0-CSC3551:00-cs35l41-hda.1 failed with error -22
This means you need:
- An ssdt patch to add the missing _DSD (device specific data) containing the Cirrus amplifier properties
- The firmware
- A kernel patch to add the required quirk to enable either the I2C connected amp, or an SPI connected amp
Dump ACPI
The first thing to do is dump the ACPI tables so we can find information required.
mkdir cirrus && cd cirrus
sudo cat /sys/firmware/acpi/tables/DSDT > dsdt.dat
iasl -d dsdt.dat
If you want to get full ACPI:
sudo acpidump > acpi.log
acpixtract acpi.log
iasl -d *.dat
Find the CSC3551
device in _DSD
This will show if your device amp is I2C or SPI connected, and what will be required for the ssdt patch you will write. Open the dumped dsdt.dsl
and search for CSC3551
.
Example SPI connected
Scope (_SB.PC00.SPI3)
{
Device (SPK1)
{
Name (_HID, "CSC3551") // _HID: Hardware ID
Name (_SUB, "10431CAF") // _SUB: Subsystem ID
Name (_UID, One) // _UID: Unique ID
Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings
{
Name (SBUF, ResourceTemplate ()
{
SpiSerialBusV2 (0x0000, PolarityLow, FourWireMode, 0x08,
ControllerInitiated, 0x003D0900, ClockPolarityLow,
ClockPhaseFirst, "\\_SB.PC00.SPI3",
0x00, ResourceConsumer, , Exclusive,
)
SpiSerialBusV2 (0x0001, PolarityLow, FourWireMode, 0x08,
ControllerInitiated, 0x003D0900, ClockPolarityLow,
ClockPhaseFirst, "\\_SB.PC00.SPI3",
0x00, ResourceConsumer, , Exclusive,
)
Notice that this is Scope (_SB.PC00.SPI3)
, this means the amp is on SPI3
.
**Example I2C connected
Scope (_SB.I2CD)
{
Device (ASPK)
{
Name (_HID, "CSC3551") // _HID: Hardware ID
Name (_SUB, "10431433") // _SUB: Subsystem ID
Name (_UID, One) // _UID: Unique ID
Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings
{
Name (RBUF, ResourceTemplate ()
{
I2cSerialBusV2 (0x0040, ControllerInitiated, 0x000F4240,
AddressingMode7Bit, "\\_SB.I2CD",
0x00, ResourceConsumer, , Exclusive,
)
I2cSerialBusV2 (0x0041, ControllerInitiated, 0x000F4240,
AddressingMode7Bit, "\\_SB.I2CD",
0x00, ResourceConsumer, , Exclusive,
)
Notice that this is Scope (_SB.I2CD)
, this means the amp is on I2C
. The I2C connected device also requires the addresses of each amp, in this example they are 0x0040
and 0x0041
as seen in I2cSerialBusV2 (0x0040,
and I2cSerialBusV2 (0x0041,
.
In both examples, also note the _SUB: Subsystem ID
, such as 10431CAF
and 10431433
. These are required to match the kernel quirk.
Lastly, you should also notice that SPK1
and ASPK
are the names of the device for each, this is important to get right.
Create the SSDT patch
Starting off with a template:
DefinitionBlock ("", "SSDT", 1, "CUSTOM", "CSC3551", 0x00000001)
{
External (<EXTERNAL>, DeviceObj)
External (<EXTERNAL>.<SPK>, DeviceObj)
Scope (<SCOPE>.<SPK>)
{
Name (_DSD, Package () // _DSD: Device-Specific Data
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package () { "cirrus,dev-index", Package () { <ADDRESS_PAIR> }},
Package () { "reset-gpios", Package () {
<SPK>, Zero, Zero, Zero,
<SPK>, Zero, Zero, Zero,
} },
Package () { "spk-id-gpios", Package () {
<SPK>, 0x02, Zero, Zero,
<SPK>, 0x02, Zero, Zero,
} },
Package () { "cirrus,speaker-position", Package () { Zero, One } },
// gpioX-func: 0 not used, 1 VPSK_SWITCH, 2: INTERRUPT, 3: SYNC
Package () { "cirrus,gpio1-func", Package () { One, One } },
Package () { "cirrus,gpio2-func", Package () { 0x02, 0x02 } },
// boost-type: 0 internal, 1 external
Package () { "cirrus,boost-type", Package () { One, One } },
},
})
}
}
Save the file as something like cirrus_ssdt_patch.dsl
<EXTERNAL>
will be either_SB_.I2CD
, or the SPI address such as_SB_.PC00.SPI3
. Note the_SB_
, also note thatSPI3
may be numbered differently.<SPK>
will be the name of the device, such asASPK
<SCOPE>
will be similar to step 1, minus the trailing_
such as_SB.I2CD
<ADDRESS_PAIR>
will be either0x0040, 0x0041
for I2C, orZero, One
for SPI
The two other items you may need to configure will be "reset-gpios"
and "cirrus,gpio1-func"
, we will get to these later.
Install the SSDT patch
Perform the following steps:
iasl -tc cirrus_ssdt_patch.dsl
mkdir -p kernel/firmware/acpi
cp cirrus_ssdt_patch.aml kernel/firmware/acpi
find kernel | cpio -H newc --create > patched_cirrus_acpi.cpio
sudo cp patched_cirrus_acpi.cpio /boot/patched_cirrus_acpi.cpio
grub
If you use grub for booting, update your /etc/default/grub
to add the line:
GRUB_EARLY_INITRD_LINUX_CUSTOM="patched_cirrus_acpi.cpio"
and update grub with sudo grub2-mkconfig -o /etc/grub2-efi.cfg
(this command is for fedora, your distro may be different).
systemd-boot
If you use systemd-boot
for booting, the process can be a little different depending on how you use it.
mkinitcpio
You may have one or more /etc/mkinitcpio.d/linux.preset
files depending on your kernel setup/name, such as /etc/mkinitcpio.d/linux-rog.preset
for my custom kernel. In each of these you will need to edit the line: ALL_microcode=(/boot/*-ucode.img)
in to ALL_microcode=(/boot/*-ucode.img /boot/patched_cirrus_acpi.cpio)
You will of course need to adjust paths depending on your own config, then run the appropriate command such as sudo mkinitcpio -P
.
systemd-boot loader
If you are using something like /boot/efi/loader/entries/
to configure boot you will need to add a line above the existing initrd
lines like:
initrd /boot/boot/patched_cirrus_acpi.cpio
initrd /boot/intel-ucode.img
initrd /boot/initramfs-linux.img
Firmware
You will need the firmware from https://gitlab.com/asus-linux/firmware, fetch this and search in the cirrus
dir for files matching the Subsystem ID
you noted earlier. The files are lowercased.
UPDATE: linux-firmware git has almost all firmwares as of this commit
ls /lib/firmware/cirrus/ |grep 10431caf
cs35l41-dsp1-spk-cali-10431caf-spkid0-l0.bin
cs35l41-dsp1-spk-cali-10431caf-spkid0-r0.bin
cs35l41-dsp1-spk-cali-10431caf-spkid1-l0.bin
cs35l41-dsp1-spk-cali-10431caf-spkid1-r0.bin
cs35l41-dsp1-spk-cali-10431caf.wmfw -> cs35l41/v6.61.1/halo_cspl_RAM_revB2_29.63.1.wmfw
cs35l41-dsp1-spk-prot-10431caf-spkid0-l0.bin
cs35l41-dsp1-spk-prot-10431caf-spkid0-r0.bin
cs35l41-dsp1-spk-prot-10431caf-spkid1-l0.bin
cs35l41-dsp1-spk-prot-10431caf-spkid1-r0.bin
cs35l41-dsp1-spk-prot-10431caf.wmfw -> cs35l41/v6.61.1/halo_cspl_RAM_revB2_29.63.1.wmfw
you may have more or less than shown. If you have matches then cp -r cirrus /lib/firmware/
.
If you don't have anything matching then you will need to search in your c:\windows\system*\
for files matching the subsystem ID. The path may look like csaudioext.inf_amd64_bc8b7c0798187cc2/tunings/ASUS_G634_10431CAF_221125
containing:
10431CAF_221125_V0_A0.bin 10431CAF_221125_V1_A0.bin
10431CAF_221125_V0_A0_cal.bin 10431CAF_221125_V1_A0_cal.bin
10431CAF_221125_V0_A1.bin 10431CAF_221125_V1_A1.bin
10431CAF_221125_V0_A1_cal.bin 10431CAF_221125_V1_A1_cal.bin
In the dir of these files run:
#!/bin/sh
# Make correct files from dir of 10431CAF_221125_V0_A0.bin etc
if ! command -v tr &> /dev/null
then
echo "The tool `tr` could not be found"
exit 1
fi
pattern="*.bin"
files=( $pattern )
filename=${files[0]}
SUBU=$(printf '%s\n' "${filename//_*}")
SUBL=$(echo $SUBU |tr '[:upper:]' '[:lower:]')
cp ${SUBU}_*_V0_A0.bin cs35l41-dsp1-spk-prot-${SUBL}-spkid0-l0.bin 2> /dev/null
cp ${SUBU}_*_V0_A1.bin cs35l41-dsp1-spk-prot-${SUBL}-spkid0-r0.bin 2> /dev/null
cp ${SUBU}_*_V0_A0_cal.bin cs35l41-dsp1-spk-cali-${SUBL}-spkid0-l0.bin 2> /dev/null
cp ${SUBU}_*_V0_A1_cal.bin cs35l41-dsp1-spk-cali-${SUBL}-spkid0-r0.bin 2> /dev/null
cp ${SUBU}_*_V1_A0.bin cs35l41-dsp1-spk-prot-${SUBL}-spkid1-l0.bin 2> /dev/null
cp ${SUBU}_*_V1_A1.bin cs35l41-dsp1-spk-prot-${SUBL}-spkid1-r0.bin 2> /dev/null
cp ${SUBU}_*_V1_A0_cal.bin cs35l41-dsp1-spk-cali-${SUBL}-spkid1-l0.bin 2> /dev/null
cp ${SUBU}_*_V1_A1_cal.bin cs35l41-dsp1-spk-cali-${SUBL}-spkid1-r0.bin 2> /dev/null
cp ${SUBU}_*_V01_A0.bin cs35l41-dsp1-spk-prot-${SUBL}-spkid1-l0.bin 2> /dev/null
cp ${SUBU}_*_V01_A1.bin cs35l41-dsp1-spk-prot-${SUBL}-spkid1-r0.bin 2> /dev/null
cp ${SUBU}_*_V01_A0_cal.bin cs35l41-dsp1-spk-cali-${SUBL}-spkid1-l0.bin 2> /dev/null
cp ${SUBU}_*_V01_A1_cal.bin cs35l41-dsp1-spk-cali-${SUBL}-spkid1-r0.bin 2> /dev/null
ln -s cs35l41/v6.61.1/halo_cspl_RAM_revB2_29.63.1.wmfw cs35l41-dsp1-spk-cali-${SUBL}.wmfw
ln -s cs35l41/v6.61.1/halo_cspl_RAM_revB2_29.63.1.wmfw cs35l41-dsp1-spk-prot-${SUBL}.wmfw
# mv cs35l41* /home/luke/firmware/
printf "\nDone! copy or move the files: 'mv cs35l41* /lib/firmware/cirrus'\n"
it would also be appreciated if the resulting renamed files could be submitted to the repo.
Kernel patch
Patches for these subsystems/laptops are merged upstream and will be in stable releases (as of 22/08/23) and 6.5-rc7+:
- SPI_2
- 0x1043, 0x1473, "ASUS GU604V"
- 0x1043, 0x1483, "ASUS GU603V"
- 0x1043, 0x1493, "ASUS GV601V"
- 0x1043, 0x1573, "ASUS GZ301V"
- 0x1043, 0x1c9f, "ASUS G614JI"
- SPI_4
- 0x1043, 0x1caf, "ASUS G634JYR/JZR"
- I2C_2
- 0x1043, 0x1d1f, "ASUS ROG Strix G17
- 0x1043, 0x1463, "Asus GA402X"
- 0x1043, 0x1433, "ASUS GX650P"
There is also work progressing on a patch to remove the requirement of the DSD table patching listed above. The initial patch is athttps://lore.kernel.org/all/20230815161033.3519-1-sbinding@opensource.cirrus.com/ and this will require adding entries for our laptops.
Note: if you are using the 6.5 kernel, or a patched kernel, you do not need to do the following.
At the time of writing this, if you have kernel 6.3.10+ and SPI amp with quad speakers, then you can quickly test things with an alsa fw patch. Create or edit /etc/modprobe.d/alsa-base.conf
and add:
options snd-hda-intel model=1043:1caf
and reboot. Change 1caf
to 1c9f
for dual speaker systems.
This option changes the subsystem ID to match a device with an already added quirk chain. So far most of the ROG range seem to require the exact same quirk chain (variations for SPI vs I2C taken in to account).
If you have an I2C connected amp you may try matching against the ROG Ally:
options snd-hda-intel model=1043:17f3
or try 1433
in place of 17f3
if you are using a kernel with the latest patches linked above.
If the above worked for you please drop us a line with your actual subsystem ID and laptop model number, and the correct quirk will be added to the Linux kernel.
Manually patching the kernel is a little out of scope of this article for now, and should be unrequired as long as you are running a 6.3.10+ kernel where the two subsystem quirks above have been added.
How do I know it worked?
First, you'll have sound. But also you will want to check the dmesg
output for something similar to the following:
dmesg |grep CSC3551
[ 0.017487] ACPI: Table Upgrade: install [SSDT-CUSTOM- CSC3551]
[ 0.017488] ACPI: SSDT 0x000000003959B000 0001A0 (v01 CUSTOM CSC3551 00000001 INTL 20220331)
[ 6.458328] Serial bus multi instantiate pseudo device driver CSC3551:00: Instantiated 2 SPI devices.
[ 6.585069] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: Reset line busy, assuming shared reset
[ 6.619422] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: Cirrus Logic CS35L41 (35a40), Revision: B2
[ 6.619635] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: Reset line busy, assuming shared reset
[ 6.643196] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: Cirrus Logic CS35L41 (35a40), Revision: B2
[ 6.846341] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: DSP1: Firmware version: 3
[ 6.846343] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: DSP1: cirrus/cs35l41-dsp1-spk-prot-10431caf.wmfw: Fri 27 Aug 2021 14:58:19 W. Europe Daylight Time
[ 6.999610] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: DSP1: Firmware: 400a4 vendor: 0x2 v0.43.1, 2 algorithms
[ 7.000479] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: DSP1: 0: ID cd v29.63.1 XM@94 YM@e
[ 7.000483] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: DSP1: 1: ID f20b v0.1.0 XM@176 YM@0
[ 7.000485] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: DSP1: spk-prot: C:\Users\dchunyi\Documents\Asus_ROG\Project\G6_Strix16\Tuning\20220930\G634\10431CAF_220930_V1_A0.bin
[ 7.036740] snd_hda_codec_realtek hdaudioC0D0: bound spi0-CSC3551:00-cs35l41-hda.0 (ops cs35l41_hda_comp_ops [snd_hda_scodec_cs35l41])
[ 7.037472] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: DSP1: Firmware version: 3
[ 7.037474] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: DSP1: cirrus/cs35l41-dsp1-spk-prot-10431caf.wmfw: Fri 27 Aug 2021 14:58:19 W. Europe Daylight Time
[ 7.178782] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: DSP1: Firmware: 400a4 vendor: 0x2 v0.43.1, 2 algorithms
[ 7.179824] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: DSP1: 0: ID cd v29.63.1 XM@94 YM@e
[ 7.179827] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: DSP1: 1: ID f20b v0.1.0 XM@176 YM@0
[ 7.179829] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: DSP1: spk-prot: C:\Users\dchunyi\Documents\Asus_ROG\Project\G6_Strix16\Tuning\20220930\G634\10431CAF_220930_V1_A1.bin
[ 7.218481] snd_hda_codec_realtek hdaudioC0D0: bound spi0-CSC3551:00-cs35l41-hda.1 (ops cs35l41_hda_comp_ops [snd_hda_scodec_cs35l41])
the line:
[ 7.000485] cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: DSP1: spk-prot: C:\Users\dchunyi\Documents\Asus_ROG\Project\G6_Strix16\Tuning\20220930\G634\10431CAF_220930_V1_A0.bin
shows that the proper tuned firmware was loaded, matching my laptop name, and model number of G634
.
If you have a message about "falling back to default firmware" or similar then the ssdt patch made need checking. If the patch looks correct check the following section.
Additional info
Are you still having problems?
Try modifying "reset-gpios"
to be:
<SPK>, One, Zero, Zero,
<SPK>, One, Zero, Zero,
or try modifying "cirrus,gpio1-func"
to be:
Package () { "cirrus,gpio1-func", Package () { Zero, Zero } },
don't forget to update the patched_cirrus_acpi.cpio
using the provided instructions.
The SPI version may also need:
Scope (_SB.PC00.SPI0)
{
Name (_DSD, Package ()
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package () { "cs-gpios", Package () {
Zero, // Native CS
SPK1, Zero, Zero, Zero // GPIO CS
} }
}
})
}
added inside the:
DefinitionBlock ("", "SSDT", 1, "CUSTOM", "CSC3551", 0x00000001)
{
...
<add to end before closing brace>
}
Example ssdt for I2C
DefinitionBlock ("", "SSDT", 1, "CUSTOM", "CSC3551", 0x00000001)
{
External (_SB_.I2CD, DeviceObj)
External (_SB_.I2CD.ASPK, DeviceObj)
Scope (_SB.I2CD.ASPK)
{
Name (_DSD, Package () // _DSD: Device-Specific Data
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package () { "cirrus,dev-index", Package () { 0x0040, 0x0041 }},
Package () { "reset-gpios", Package () {
ASPK, Zero, Zero, Zero,
ASPK, Zero, Zero, Zero,
} },
Package () { "spk-id-gpios", Package () {
ASPK, 0x02, Zero, Zero,
ASPK, 0x02, Zero, Zero,
} },
Package () { "cirrus,speaker-position", Package () { Zero, One } },
Package () { "cirrus,gpio1-func", Package () { One, One } },
Package () { "cirrus,gpio2-func", Package () { 0x02, 0x02 } },
Package () { "cirrus,boost-type", Package () { One, One } },
},
})
}
}
Example ssdt for SPI
DefinitionBlock ("", "SSDT", 1, "CUSTOM", "CSC3551", 0x00000001)
{
External (_SB_.PC00.SPI3, DeviceObj)
External (_SB_.PC00.SPI3.SPK1, DeviceObj)
Scope (_SB.PC00.SPI3.SPK1)
{
Name (_DSD, Package ()
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package () { "cirrus,dev-index", Package () { Zero, One }},
Package () { "reset-gpios", Package () {
SPK1, Zero, Zero, Zero,
SPK1, Zero, Zero, Zero
} },
Package () { "spk-id-gpios", Package () {
SPK1, 0x02, Zero, Zero,
SPK1, 0x02, Zero, Zero
} },
Package () { "cirrus,speaker-position", Package () { Zero, One } },
Package () { "cirrus,gpio1-func", Package () { One, One } },
Package () { "cirrus,gpio2-func", Package () { 0x02, 0x02 } },
Package () { "cirrus,boost-type", Package () { One, One } }
}
})
}
Scope (_SB.PC00.SPI3)
{
Name (_DSD, Package ()
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package () { "cs-gpios", Package () {
Zero, // Native CS
SPK1, Zero, Zero, Zero // GPIO CS
} }
}
})
}
}
Credits
This article was enabled by many sources around the internet. The goal of this article was to provide an easily accessible guide for people to work from.
Sources:
- https://github.com/bno1/linux-xanmod-gu604/tree/master/acpi
- https://gist.github.com/lamperez/862763881c0e1c812392b5574727f6ff
- various forum posts that worked from the above sources
- folks who kindly tested patches for me and provided reports