Skip to content

feat: Add aw87xxx codec for AyaNeo/Ayn/Orange Pi handheld speaker amplifiers - #102

Open
hijae wants to merge 8 commits into
OpenGamingCollective:features/ayaneofrom
hijae:add-aw87xxx-flip-speaker
Open

feat: Add aw87xxx codec for AyaNeo/Ayn/Orange Pi handheld speaker amplifiers#102
hijae wants to merge 8 commits into
OpenGamingCollective:features/ayaneofrom
hijae:add-aw87xxx-flip-speaker

Conversation

@hijae

@hijae hijae commented Aug 25, 2026

Copy link
Copy Markdown

Context

Several handhelds (AyaNeo Flip KB/DS, AIR 1S, AIR Plus, Kun; Ayn Loki MiniPro; Orange Pi NEO-01) use the Awinic AW87XXX Smart K PA as their speaker amplifier, identified over ACPI as AWDZ8830. Mainline Linux has no driver for this chip — the closest match (aw87390.c) doesn't recognize it. Without this driver these devices have no speaker output at all.

This driver was lost when Bazzite's kernel source switched to OGC, unrelated to the driver itself. Ported here referencing ChimeraOS's kernel, which carries the same driver. See ublue-os/bazzite#5374 for the resulting regression report (AyaNeo AIR 1S, no audio since the kernel switch).

Changes

  • First commit: the original driver + DMI-based firmware filename selection, ported from hhd-dev/patchwork with attribution preserved.
  • Second commit: drop a dead linux/of_gpio.h include (header no longer exists in this tree) so it builds on current kernels.
  • Third commit: add an ALC269VB pin fixup for the AIR 1S's internal speaker, ported from hhd-dev/patchwork — needed in addition to the aw87xxx driver above, since the AIR 1S's main HDA codec pin routing isn't otherwise configured for internal speaker output.

Testing

Built out-of-tree against this branch's exact source (kernel-devel 7.2.0-ogc4.1) and verified on real AyaNeo Flip KB hardware — chip probes correctly (chip_id 0x5a, product aw87559), and speaker audio works, including after a fresh reboot.

The AIR 1S pin fixup (third commit) sets the same pin/value bl1nk verified fixes internal speaker output on real AIR 1S hardware (see comments below) — but this commit ports the original, simpler hhd-dev/patchwork implementation (a static pin table) rather than bl1nk's own version (a runtime-DMI-checked function), and I don't have an AIR 1S to test with myself. The underlying fix is verified; this exact commit hasn't been re-tested as-is, so it'd be good to confirm it works before merging.

pastaq and others added 6 commits August 13, 2026 18:05
Adds platform driver for AYN Loki and Tectoy Zeenix lines of handheld
devices. This patch implements a hwmon interface for EC provided manual
PWM fan control and user defined fan curves. A global ACPI lock is used
when reading or writing from the EC.

There are 4 fan modes implemented in this patch. Modes 0-3 act in
accordance with the standard hwmon logic where 0 is 100% fan speed, 1 is
manual control, and 2 is automatic control. As the EC only provides 3
modes by default, mode 0 is implemented by setting the device to manual
and then setting fan speed to 100% directly. In mode 1 the PWM duty cycle
is set in sysfs with values [0-255], which are then scaled to the EC max
of 128. Mode 4 is an automatic mode where the fan curve is user defined.
There are 5 total set points and each set point takes a temperature in
Celsius [0-100] and a PWM duty cycle [0-255]. When the CPU temperature
reaches a given set point, the corresponding duty cycle is automatically
set by the EC.

Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
Adds temperature sensors to the ayn-ec hwmon interface. These read-only
values include Battery, Motherboard, Charger IC, vCore, and CPU Core, as
well as labels for each entry. The temperature values provided by the EC
are whole numbers in degrees Celsius. As hwmon expects millidegrees, we
scale the raw value up.

`sensors` output after this patch is applied:
aynec-isa-0000
Adapter: ISA adapter
fan1:        1876 RPM
Battery:      +29.0°C
Motherboard:  +30.0°C
Charger IC:   +30.0°C
vCore:        +36.0°C
CPU Core:     +48.0°C

Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
Adds an EC controlled LED Multicolor Class Device for controlling the
RGB rings around the joysticks.

The EC provides a single register for each of the colors red, green, and
blue, as well as a mode switching register. The EC accepts values
[0-255] for all colors. There are two available effects: breathe, which is
the default when the device is started, and monocolor. When resuming from
sleep the user selected effect will be overwritten by the EC, so the
driver retains the last setting and resets on resume. When setting a
color, each color register is set before a final "write" code is sent to
the device. The EC may briefly reflect the "write" code when writing, but
quickly changes to the "monocolor" value once complete. The driver
interprets both of these values as "monocolor" in _show to simplify the
sysfs exposed to the user.

Two custom attributes are added to the standard LED parent device:
effect, a RW file descriptor used to set the effect, and effect_index,
which enumerates the available valid options.

Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
Adds ABI documentation for the ayn-ec platform driver

Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
…re selection

Adds the Awinic AW87XXX Smart K PA codec driver, ported from
hhd-dev/patchwork. This chip is used as the speaker amplifier on
several handhelds (AyaNeo Flip KB/DS, AIR 1S, AIR Plus, Kun; Ayn
Loki MiniPro; Orange Pi NEO-01 per the DMI match table below),
none of which currently have audio output without this driver.
Verified on AyaNeo Flip KB.

Includes DMI-based firmware filename selection so multiple devices'
firmware blobs can coexist under /lib/firmware without collision.

Co-developed-by: bouhaa <boukehaarsma23@gmail.com>
Co-developed-by: Antheas Kapenekakis <git@antheas.dev>
Signed-off-by: Hijae Song <hijae@naver.com>
linux/of_gpio.h has been removed from the tree. None of these
files actually use anything from it (no of_gpio_* calls), so drop
the dead include to fix the build.

Signed-off-by: Hijae Song <hijae@naver.com>
@hijae

hijae commented Aug 25, 2026

Copy link
Copy Markdown
Author

Companion PR enabling this in the build config: OpenGamingCollective/kernel-packages#36CONFIG_SND_SOC_AW87XXX isn't on by default, so without that PR this driver is present but never actually built.

@pastaq

pastaq commented Aug 25, 2026

Copy link
Copy Markdown
Member

Are we planning to upstream this, or rework it before hand? Authorship makes it somewhat of a challenge.
@BoukeHaarsma23 I would love your input here.

KyleGospo pushed a commit to OpenGamingCollective/kernel-packages that referenced this pull request Aug 25, 2026
CONFIG_SND_SOC_AW87XXX is added by OpenGamingCollective/linux#102.
Enable it as a module so it's actually built.

Signed-off-by: Hijae Song <hijae@naver.com>
@bl1nk

bl1nk commented Aug 26, 2026

Copy link
Copy Markdown

For the AYANEO Air 1S another quirk in the ALC269 codec is necessary to fix audio output. I commented on the initial bug report in ublue-os/bazzite: ublue-os/bazzite#5374 (comment)

@hijae Do you want to include a fix for that in this PR as well or should this be done via a separate PR?

Working patch to fix audio output on the AYANEO Air 1S

I went back and forth with an LLM to write this, but I am neither a kernel contributor nor am I experienced in C. I understand the code, but I do not understand why 0x90170110 results in the correct pinout. It seems to be some conventional value that is used in other fixes for the realtek codec across the tree to indicate what an internal speaker is.

This fix is should even be safer than the upstream one as we are only targeting the Air 1S specifically.

diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index cae327e1d9..390bf19966 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -3998,6 +3998,7 @@
 	ALC269VB_FIXUP_ASUS_ZENBOOK,
 	ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A,
 	ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE,
+	ALC269_FIXUP_AYANEO_AIR_1S,
 	ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED,
 	ALC269VB_FIXUP_ORDISSIMO_EVE2,
 	ALC283_FIXUP_CHROME_BOOK,
@@ -4299,6 +4300,19 @@
 	ALC285_LENOVO_DAC_RENAME,
 };
 
+static void alc269_fixup_ayaneo_air_1s(struct hda_codec *codec,
+				       const struct hda_fixup *fix, int action)
+{
+	if (action != HDA_FIXUP_ACT_PRE_PROBE)
+		return;
+
+	if (!dmi_match(DMI_SYS_VENDOR, "AYANEO") ||
+	    !dmi_match(DMI_PRODUCT_NAME, "AIR 1S"))
+		return;
+
+	snd_hda_codec_set_pincfg(codec, 0x1a, 0x90170110);
+}
+
 /* A special fixup for Lenovo C940 and Yoga Duet 7;
  * both have the very same PCI SSID, and we need to apply different fixups
  * depending on the codec ID
@@ -4530,6 +4544,10 @@
 			{ }
 		},
 	},
+	[ALC269_FIXUP_AYANEO_AIR_1S] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = alc269_fixup_ayaneo_air_1s,
+	},
 	[ALC269_FIXUP_AMIC] = {
 		.type = HDA_FIXUP_PINS,
 		.v.pins = (const struct hda_pintbl[]) {
@@ -8213,6 +8231,7 @@
 	SND_PCI_QUIRK(0x1f4c, 0xb020, "Minisforum AI X1 Pro",
 		      ALC245_FIXUP_MINISFORUM_JACK_DETECT),
 	SND_PCI_QUIRK(0x1f4c, 0xe001, "Minisforum V3 (SE)", ALC245_FIXUP_BASS_HP_DAC),
+	SND_PCI_QUIRK(0x1f66, 0x0103, "AYANEO AIR 1S", ALC269_FIXUP_AYANEO_AIR_1S),
 	SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2),
 	SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
 	SND_PCI_QUIRK(0x2039, 0x0001, "Inspur S14-G1", ALC295_FIXUP_CHROME_BOOK),

@hijae

hijae commented Aug 26, 2026

Copy link
Copy Markdown
Author

@bl1nk
I can include it if you'd like. However, I don't have an Air 1s device and can't test it, so I recommend that you create a separate PR yourself.
Also, I'm not sure if my PR will be accepted because of the authorship issue.

@pastaq

pastaq commented Aug 26, 2026

Copy link
Copy Markdown
Member

We'll most likely carry this patch as it is needed for many devices. The authorship issue is administrative. I asked bl1nk to see if you could cherry-pick that patch as well. It will depend on these being merged so it makes it simpler to include.

The ALC269VB codec's internal speaker pin isn't configured
correctly on the AyaNeo AIR 1S out of the box, so no audio
plays even once the aw87xxx amplifier driver (added in this
same PR) is working. Add a pin fixup matching the AIR 1S's
PCI SSID that sets pin 0x1a to 0x90170110, restoring internal
speaker output.

Ported from hhd-dev/patchwork, where this same fix has
already been carried and verified across several AyaNeo
devices.

Co-developed-by: linh1987
Signed-off-by: Hijae Song <hijae@naver.com>
@hijae

hijae commented Aug 27, 2026

Copy link
Copy Markdown
Author

Thanks for tracking this down and for the fix! I found the original source for this — it's already in hhd-dev/patchwork (89eb9630d9, authored by fewtarius, co-developed by linh1987, carried by Antheas) — so I ported that version instead of yours: it's simpler (a static pin table, no runtime DMI check needed) and keeps clean original authorship. Pushed as a third commit on this PR, same pin/value you verified (0x1a → 0x90170110).

I don't have an AIR 1S myself, so if you get a chance to confirm this exact commit works on your hardware, that'd be great before it merges.

@pastaq

pastaq commented Aug 27, 2026

Copy link
Copy Markdown
Member

IIRC the mini pro and the air 1s have inverted pin configurations or something similar that necessitated the DMI match but I'm not certain what it exactly was. Hopefully we can find a mini pro user to test as well

@bl1nk

bl1nk commented Aug 27, 2026

Copy link
Copy Markdown

Thank you both, again.

I built both modules and loaded them against the current -testing kernel and everything worked just fine (after restarting steam, so it could pick up the new pipewire sink, I think).

If anyone wants to try it out on their device(s) to see if it breaks on something that is not the AYANEO Air 1S

Clone the kernel repo:

git clone https://github.com/OpenGamingCollective/linux.git
cd linux

git fetch origin pull/102/head:pr-102
git switch pr-102

Set some variables:

KDIR=/lib/modules/$(uname -r)/build
REALTEK="$PWD/sound/hda/codecs/realtek"
AW87="$PWD/sound/soc/codecs/aw87xxx"

Then build the patched modules.

Realtek codec modules:

make -C "$KDIR" \
    M="$REALTEK" \
    modules

AW87xxx amplifier driver:

make -C "$KDIR" \
    M="$AW87" \
    CONFIG_SND_SOC_AW87XXX=m \
    modules

The relevant resulting modules are:

sound/hda/codecs/realtek/snd-hda-codec-realtek-lib.ko
sound/hda/codecs/realtek/snd-hda-codec-alc269.ko
sound/soc/codecs/aw87xxx/snd-soc-aw87xxx.ko

Optional check:

strings "$REALTEK/snd-hda-codec-alc269.ko" | grep -i ayaneo

should return something like:

ayaneo-speaker-pin-fix
ALC269VB_FIXUP_AYANEO_SPKR_PIN_FIX

to verify that the compiled module contains the fix.

Temporarily disable PipeWire/WirePlumber:

systemctl --user mask --runtime \
    pipewire.service pipewire.socket \
    pipewire-pulse.service pipewire-pulse.socket \
    wireplumber.service

systemctl --user stop \
    pipewire.service pipewire.socket \
    pipewire-pulse.service pipewire-pulse.socket \
    wireplumber.service

Check that nothing still has the ALSA devices open:

sudo fuser -v /dev/snd/*

(it should return nothing)

Unload the existing HDA modules:

sudo modprobe -r snd_hda_intel
sudo modprobe -r snd_hda_codec_alc269
sudo modprobe -r snd_hda_codec_realtek_lib

Load the patched modules:

sudo modprobe snd_hda_scodec_component
sudo modprobe snd_hda_codec_generic

sudo insmod "$REALTEK/snd-hda-codec-realtek-lib.ko"
sudo insmod "$REALTEK/snd-hda-codec-alc269.ko"
sudo insmod "$AW87/snd-soc-aw87xxx.ko"

sudo modprobe snd_hda_intel

You should be able to play a sound now:

speaker-test -D hw:1,0 -c 2 -r 48000 -t sine

Reboot to reset the device to its previous state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants