Ubuntu CEC not working

Which system do you use? Android, Ubuntu, OOWOW or others?

Ubuntu: official khadas image for Vim3L: vim3l-ubuntu-24.04-gnome-linux-5.15-fenix-1.7.5-250925-emmc.img.xz

Please describe your issue below:

I can’t connect to the TV (Sony or Panasonic) using CEC for the official Ubuntu image. CEC works properly for Android and CoreELEC.

Could you please provide step by step instructions on how to connect to the TV using cec-client or cec-ctl.

I get write errors like below:

cec-client -d 31 -t a

CEC Parser created - libCEC version 7.0.0
no serial port given. trying autodetect:
path: /dev/aocec
com port: AOCEC

opening a connection to the CEC adapter…
DEBUG: [ 2] Broadcast (F): osd name set to ‘Broadcast’
NOTICE: [ 3] connection opened
DEBUG: [ 3] processor thread started
DEBUG: [ 3] << Broadcast (F) → TV (0): POLL
TRAFFIC: [ 3] << f0
WARNING: [ 3] Write: write failed
TRAFFIC: [ 3] << f0
WARNING: [ 3] Write: write failed
DEBUG: [ 3] >> POLL not sent
DEBUG: [ 3] TV (0): device status changed into ‘not present’
DEBUG: [ 3] registering new CEC client - v7.0.0
NOTICE: [ 4] setting HDMI port to 1 on device TV (0)
DEBUG: [ 4] << Broadcast (F) → TV (0): POLL
TRAFFIC: [ 4] << f0
WARNING: [ 4] Write: write failed
TRAFFIC: [ 4] << f0
WARNING: [ 4] Write: write failed
DEBUG: [ 4] >> POLL not sent

[22858.276798] [hdmitx:] system: irq 80000002 0
[22858.276816] [hdmitx:] UTC+0 2026-01-05 05:29:47 HPD HIGH
[22858.276828] EARC ff663800.earc: HDMITX cable is plugin
[22858.279505] EARC ff663800.earc: EARCRX_CMDC_DISC1
[22858.289509] EARC ff663800.earc: EARCRX_CMDC_DISC2
[22858.299501] EARC ff663800.earc: EARCRX_CMDC_DISC1
[22858.309506] EARC ff663800.earc: EARCRX_CMDC_DISC2
[22858.319807] EARC ff663800.earc: EARCRX_CMDC_DISC1
[22858.329792] EARC ff663800.earc: EARCRX_CMDC_DISC2
[22858.333520] EARC ff663800.earc: EARCRX_CMDC_EARC
[22858.333526] EARC ff663800.earc: EARCRX_CMDC_STATUS_CH
[22858.383957] EARC ff663800.earc: EARCRX_CMDC_STATUS_CH
[22858.434693] EARC ff663800.earc: EARCRX_CMDC_STATUS_CH
[22858.485434] EARC ff663800.earc: EARCRX_CMDC_STATUS_CH
[22858.797667] [hdmitx:] system: plugin
[22858.797682] [hdmitx:] plugin sequence id: 4300607008
[22858.930140] [hdmitx:] edid: blk0 raw data
[22858.930177] [hdmitx:] 00ffffffffffff004dd97c07010101010121010380aa5f780a0dc9a05747982712484c2108008180a9c0714fb300010101010101010104740030f2705a80b058
8a00a1bb6300001e023a801871382d40582c4500a1bb6300001e000000fc00534f4e5920545620202a33300a000000fd00174c0e461e000a202020202020013b
[22858.930183] [hdmitx:] edid: blk1 raw data
[22858.930206] [hdmitx:] 02035df0525d5e5f621f101405130420223c3e12031102320f7f071507503d14c05704016754075f7c01830f00006e030c003000b8442b008001020304e200cb
e305d800e50e60616566e6060d01b2b205eb0146d0004803765898aa5c011d007251d01e206e285500a1bb6300001e00000000000000000000000000000000b0
[22858.930420] [drm] drm hdmitx hpd notify
[22858.995632] [drm:meson_hdmitx_atomic_check [aml_drm]] ERROR hdmitx_on changed, force modeset.
[22859.000129] [drm] [meson_hdmitx_encoder_atomic_disable]
[22859.105659] [drm] am_meson_crtc_atomic_disable-[0]:in
[22859.106088] [hdmitx:] hdr_work_func: send zero DRM
[22859.106126] [hdmitx:] Dolby VSIF, VEN_DB2[3]) = 0
[22859.106137] [hdmitx:] Dolby VSIF, switching signal to SDR
[22859.106139] [hdmitx:] vic:16, cd:4, cs:0, cr:2
[22859.106186] [drm] am_meson_crtc_atomic_enable-[0] in: new[1080p60hz], old[1080p60hz], vmode[4-4], uboot[0]
[22859.106206] [drm] am_meson_crtc_atomic_enable-[0]: enable mode 1080p60hz final vmode 0
[22859.107313] [hdmitx:] div40: 0
[22859.133659] [hdmitx:] ERR: SCDC not present, should not send 1:10
[22859.133675] [hdmitx:] hdmitx sub-clock: 5940000 4 1 2 6 1 1 1 -1
[22859.133683] [hdmitx:] config HPLL = 5940000 frac_rate = 1
[22859.133753] [hdmitx:] HPLLv1: 0xdb3a04f7
[22859.133782] [hdmitx:] config hdmitx IP vic = 16 cd:4 cs: 0
[22859.144207] [hdmitx:] system: irq 80000001 0
[22859.185660] [hdmitx:] Sink is HDMI device
[22859.185716] [hdmitx:] update audio N 5824
[22859.188869] DOLBY: vout_notify_callback: VOUT_EVENT_MODE_CHANGE
[22859.188878] vsync_pts_inc 1501 1000/59940
[22859.188917] [drm] meson_vout_update_mode_name: hdmitx update vout 1 name 1080p60hz.
[22859.188945] [hdmitx:] hdmitx support hdcp14: 0, hdcp22: 0
[22859.188949] [drm] meson_hdmitx_get_hdcp_request usr_type: 3, hdcp cap: 0,1
[22859.188953] [drm] [meson_hdmitx_get_hdcp_request]: TX no hdcp key
[22859.188955] [drm:meson_hdmitx_update_hdcp [aml_drm]] ERROR meet stopped hdcp stat
[22859.192296] [drm:meson_hdmitx_update_hdcp [aml_drm]] ERROR No valid hdcp mode exit, maybe hdcp havenot init.
[22860.621656] [hdmitx:] hdr_work_func: send zero DRM

PS. interestingly, the ARC channel works OK.

Best regards,
Krzysztof

Hello @kczyz

We havent checked CEC on Ubuntu, could you tell us for what situation you need to use CEC on Ubuntu ?

Hello @numbqq,

Yes, I have chceked CEC on Ubuntu (vim3l-ubuntu-24.04-gnome-linux-5.15-fenix-1.7.5-250925-emmc.img.xz), driver AOCEC is buggy and it does not work (see logs above, write errors to CEC bus, read seem to be working fine). It works fine for old images with 4.9 kernel.

My use case: I would like to register VIM as an audio system to TV and have a volume control.

Could you please provide step by step how to showing usage of CEC interface (using cec-client or cec-ctrl) for Ubuntu image with eARC support?

Best regards,
Krzysztof

Any updates on the CEC issue?

I dug into this on a VIM3L with the stock Fenix v1.7.5 image (kernel 5.15.137) and found the actual cause. Short version: the CEC hardware works fine, but the kernel exposes it through a private char device that no standard tool can talk to.

What’s wrong

The stock config uses:

CONFIG_AMLOGIC_CEC=y
CONFIG_AMLOGIC_AO_CEC=y
# CONFIG_AMLOGIC_LINUX_STD_CEC is not set

AMLOGIC_AO_CEC registers the driver with misc_register() and creates /dev/cec (major 477) with Amlogic’s own ioctl set. It does not register with the kernel CEC framework, so:

  • /dev/cec0 never appears — cec-ctl, cec-follower, cec-compliance all fail

  • libCEC’s Linux backend finds no adapter

  • read() and write() on /dev/cec are non-functional — write() returns a constant 3 and never puts anything on the bus, read() never returns frames

The CEC framework itself is compiled in (CONFIG_CEC_CORE=y, you can see major 244 cec in /proc/devices) — it just has zero registered adapters.

You can confirm the hardware is fine on a stock image:

bash

echo 1 | sudo tee /sys/class/cec/dbg_en
echo "10 05" | sudo tee /sys/class/cec/cmd
dmesg | tail

You’ll see [TX] len: 2, msg: 10 05 / TX_DONE and even the TV’s reply [RX] len: 4, msg: 01 00 05 00. The bus works — only the userspace API is dead.

The fix

Switch to CONFIG_AMLOGIC_LINUX_STD_CEC, which registers via cec_allocate_adapter() and gives a proper /dev/cec0.

That option exists in the tree but has never been built — it needs three patches before it compiles and runs.

1. common_drivers/drivers/media/cec/Makefile

Both branches key the object list off CONFIG_AMLOGIC_AO_CEC, so with AO_CEC disabled nothing is compiled at all:

diff

 ifdef CONFIG_AMLOGIC_LINUX_STD_CEC
-$(MEDIA_MODULE_NAME)-$(CONFIG_AMLOGIC_AO_CEC)  += cec/std_ao_cec.o cec/hdmi_aocec_api.o cec/hdmi_cec_dump.o
+$(MEDIA_MODULE_NAME)-$(CONFIG_AMLOGIC_LINUX_STD_CEC)  += cec/std_ao_cec.o cec/hdmi_aocec_api.o cec/hdmi_cec_dump.o
 else
 $(MEDIA_MODULE_NAME)-$(CONFIG_AMLOGIC_AO_CEC)  += cec/hdmi_ao_cec.o cec/hdmi_aocec_api.o cec/hdmi_cec_dump.o
 endif

2. common_drivers/drivers/media/media_main.h (line 503)

cec_init() is stubbed out unless AO_CEC is set, so even once linked the driver is never initialised:

diff

-#ifdef CONFIG_AMLOGIC_AO_CEC
+#if IS_ENABLED(CONFIG_AMLOGIC_AO_CEC) || IS_ENABLED(CONFIG_AMLOGIC_LINUX_STD_CEC)
 int cec_init(void);
 #else
 static int cec_init(void)
 {
 	return 0;
 }
 #endif

3. common_drivers/drivers/media/cec/std_ao_cec.c — build fixes

The file has bit-rotted against the shared headers:

bash

cd common_drivers/drivers/media/cec

# stale function name (hdmi_aocec_api.h declares dump_cec_reg)
sed -i 's/\bdump_cecrx_reg\b/dump_cec_reg/g' std_ao_cec.c

# global renamed to cec_mbox_chan
sed -i 's/aml_mbox_transfer_data(mbox_chan,/aml_mbox_transfer_data(cec_mbox_chan,/g' std_ao_cec.c

# typo: struct member is cec_wk_as_msg
sed -i 's/\bec_wk_as_msg\b/cec_wk_as_msg/g' std_ao_cec.c

# cec_mbox_chan is defined in hdmi_ao_cec.c, which is NOT built on this path,
# so it has to be defined here or the module fails to link
LINE=$(grep -n "^#include" std_ao_cec.c | tail -1 | cut -d: -f1)
sed -i "${LINE}a\\
\\
struct mbox_chan *cec_mbox_chan;" std_ao_cec.c

4. Kernel config

CONFIG_AMLOGIC_CEC=y
# CONFIG_AMLOGIC_AO_CEC is not set
CONFIG_AMLOGIC_LINUX_STD_CEC=y

CEC_CORE and CEC_NOTIFIER are pulled in automatically by Kconfig select.

Result

$ dmesg | grep -i cec
cec: 2 irq src, a:49, b:48
rc rc0: std_cec as /devices/platform/ff80023c.aocec/rc/rc0
cec: aml_aocec_probe success end

$ cat /proc/devices | grep cec
244 cec

$ ls -l /dev/cec0
crw-rw---- 1 root video 244, 0 /dev/cec0

cec-ctl, cec-follower and libCEC’s Linux backend all work from here.

Building it yourself

bash

git clone https://github.com/khadas/fenix && cd fenix
git checkout v1.7.5          # match your image
docker pull numbqq/fenix:latest
docker run -it --name fenix -v $(pwd):/home/khadas/fenix --privileged numbqq/fenix

# inside the container
source env/setenv.sh         # VIM3L / Ubuntu / noble / linux 5.15 vendor
make kernel                  # fetches sources
# ... apply the patches above ...
make kernel
make kernel-deb
ls build/images/debs/

Heads-up: releases.linaro.org is dead, so the toolchain download fails. Point the URLs at Khadas’ own mirror first:

bash

grep -rl "releases.linaro.org" packages/ | xargs \
  sed -i 's|https://releases\.linaro\.org/[^ "]*/|http://build.khadas.com/khadas/fenix/toolchains/|g'

Prebuilt packages

If you’d rather not build:

Built for: VIM3L (SM1), Ubuntu 24.04 noble, Fenix v1.7.5, kernel 5.15.137. Won’t match other board/image combinations — check uname -a first.

:warning: The new kernel has the same version string (5.15.137), so it overwrites /lib/modules/5.15.137. There is no fallback entry in the bootloader. Back up first:

bash

sudo cp -a /boot /boot.backup-$(date +%F)
sudo tar czf ~/modules-5.15.137.backup.tar.gz -C /lib/modules 5.15.137
# copy that tarball OFF the board

Then:

bash

sudo dpkg -i linux-image-*.deb
sudo reboot

Verify:

bash

ls -l /dev/cec0
cec-ctl -d /dev/cec0

@Blair_C

If what you’re really after is making the board act as an audio system for your TV over eARC/ARC, with the sound going out to a USB DAC that works well with:

  • alsaloop (ALSA loopback) to route the eARC capture stream to your DAC

  • lautis/cec-alsa-volume to map the TV remote’s volume up/down/mute CEC messages onto an ALSA mixer

bash

alsaloop -C plughw:CARD=AMLAUGESOUND,DEV=0 -f S16_LE -c 2 -r 48000 -P plughw:CARD=DACUAC20,DEV=0

npm install -g cec-alsa-volume
cec-alsa-volume --min-volume 65 --max-volume 85 --mixer-device "hw:1" --mixer-name "DX1 DAC (UAC2.0)" --name "eARC"

It needs libCEC and alsa-utils, so it depends on having a working CEC adapter which is exactly what the kernel fix above gives you.


It’d be great if Khadas/Amlogic picked up patches 1–3 upstream; they’re the difference between a dead config option and a working standard CEC device.

BR,
Krzysztof