From 4258a05ef2ea6b46421c025c0f3104f99b2d46a0 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Sat, 17 Oct 2026 12:40:43 +0800 Subject: [PATCH 1/2] [bsp][allwinner] Add F101 YuzukiNeko BSP and driver support Add initial support for the Allwinner F101S3-based YuzukiNeko board. - add the F101 YuzukiNeko BSP with M-mode and S-mode configurations - add Sun252i F101 DM drivers for clock, reset, pinctrl, DMA, SDIO, SPI, USB, audio, thermal, RTC, PWM, ADC, NVMEM, watchdog, DVFS, G2D, and JPEG - add the Sunxi DE0/TCON0 display-output backend for RGB, LVDS, and MIPI-DSI - add ST77916 QSPI framebuffer board configuration and validation commands - add Sunxi MUSB glue support for CherryUSB - fix shared driver framework handling for power domains, DVFS, graphics, UART reset and register shift, QSPI configuration, thermal workqueues, input capture, and DM USB integration Signed-off-by: GuEe-GUI <2991707448@qq.com> --- bsp/README.md | 1 + bsp/allwinner/dm/Kconfig | 24 + bsp/allwinner/dm/SConscript | 15 + bsp/allwinner/dm/adc/Kconfig | 9 + bsp/allwinner/dm/adc/SConscript | 12 + bsp/allwinner/dm/adc/adc-sunxi-gpadc.c | 311 +++ bsp/allwinner/dm/audio/Kconfig | 50 + bsp/allwinner/dm/audio/SConscript | 21 + bsp/allwinner/dm/audio/audio-sunxi-codec.c | 652 +++++ bsp/allwinner/dm/audio/audio-sunxi-i2s.c | 357 +++ bsp/allwinner/dm/audio/audio-sunxi-owa.c | 349 +++ bsp/allwinner/dm/audio/audio-sunxi.c | 309 +++ bsp/allwinner/dm/audio/audio-sunxi.h | 54 + bsp/allwinner/dm/clk/Kconfig | 10 + bsp/allwinner/dm/clk/SConscript | 13 + .../dm/clk/clk-sun252i-f101-cpupll.c | 338 +++ bsp/allwinner/dm/clk/clk-sun252i-f101-rtc.c | 184 ++ bsp/allwinner/dm/clk/clk-sun252i-f101.c | 234 ++ bsp/allwinner/dm/clk/clk-sunxi.c | 364 +++ bsp/allwinner/dm/clock_time/Kconfig | 10 + bsp/allwinner/dm/clock_time/SConscript | 12 + .../dm/clock_time/clock_timer-sunxi.c | 436 ++++ bsp/allwinner/dm/dma/Kconfig | 7 + bsp/allwinner/dm/dma/SConscript | 13 + bsp/allwinner/dm/dma/dma-sunxi.c | 710 +++++ .../dts/sun252i-f101-yuzukineko-co6300.dtsi | 100 + .../dts/sun252i-f101-yuzukineko-common.dtsi | 199 ++ ...sun252i-f101-yuzukineko-radxa-lcd-8hd.dtsi | 248 ++ .../dts/sun252i-f101-yuzukineko-st77916.dtsi | 248 ++ .../dm/dts/sun252i-f101-yuzukineko.dts | 21 + bsp/allwinner/dm/dts/sun252i-f101.dtsi | 751 ++++++ bsp/allwinner/dm/dvfs/SConscript | 21 + bsp/allwinner/dm/dvfs/cpufreq/Kconfig | 8 + bsp/allwinner/dm/dvfs/cpufreq/SConscript | 13 + .../dvfs/cpufreq/dvfs-sun252i-f101-cpufreq.c | 82 + bsp/allwinner/dm/graphic/SConscript | 5 + bsp/allwinner/dm/graphic/framebuffer/Kconfig | 17 + .../dm/graphic/framebuffer/SConscript | 11 + .../dm/graphic/framebuffer/fb-sunxi-de.c | 734 ++++++ .../framebuffer/sunxi-display-output.c | 783 ++++++ .../framebuffer/sunxi-display-output.h | 71 + bsp/allwinner/dm/hwcrypto/Kconfig | 19 + bsp/allwinner/dm/hwcrypto/SConscript | 10 + bsp/allwinner/dm/hwcrypto/hwcrypto-sunxi-ce.c | 457 ++++ bsp/allwinner/dm/i2c/Kconfig | 7 + bsp/allwinner/dm/i2c/SConscript | 13 + bsp/allwinner/dm/i2c/i2c-sunxi.c | 439 ++++ .../dt-bindings/clock/sun252i-f101-ccu.h | 115 + .../dt-bindings/clock/sun252i-f101-cpupll.h | 15 + .../dt-bindings/clock/sun252i-f101-rtc.h | 20 + .../dt-bindings/reset/sun252i-f101-ccu.h | 62 + bsp/allwinner/dm/include/sunxi-g2d.h | 202 ++ bsp/allwinner/dm/include/sunxi-jpegd.h | 68 + bsp/allwinner/dm/include/sunxi-jpege.h | 39 + bsp/allwinner/dm/include/sunxi-pngd.h | 57 + bsp/allwinner/dm/include/sunxi_ccu.h | 40 + bsp/allwinner/dm/include/sunxi_pinctrl.h | 47 + bsp/allwinner/dm/include/sunxi_reset.h | 27 + bsp/allwinner/dm/input/SConscript | 13 + bsp/allwinner/dm/input/touchscreen/Kconfig | 8 + bsp/allwinner/dm/input/touchscreen/SConscript | 13 + .../dm/input/touchscreen/ts-sunxi-tpadc.c | 325 +++ bsp/allwinner/dm/inputcapture/Kconfig | 8 + bsp/allwinner/dm/inputcapture/SConscript | 12 + .../dm/inputcapture/inputcapture-sunxi-irrx.c | 341 +++ bsp/allwinner/dm/nvmem/Kconfig | 8 + bsp/allwinner/dm/nvmem/SConscript | 12 + bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c | 177 ++ bsp/allwinner/dm/pinctrl/Kconfig | 5 + bsp/allwinner/dm/pinctrl/SConscript | 13 + .../dm/pinctrl/pinctrl-sun252i-f101.c | 64 + bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c | 791 ++++++ bsp/allwinner/dm/pwm/Kconfig | 7 + bsp/allwinner/dm/pwm/SConscript | 13 + bsp/allwinner/dm/pwm/pwm-sunxi.c | 376 +++ bsp/allwinner/dm/reset/Kconfig | 6 + bsp/allwinner/dm/reset/SConscript | 11 + bsp/allwinner/dm/reset/reset-sun252i-f101.c | 72 + bsp/allwinner/dm/reset/reset-sunxi.c | 191 ++ bsp/allwinner/dm/rtc/Kconfig | 8 + bsp/allwinner/dm/rtc/SConscript | 12 + bsp/allwinner/dm/rtc/rtc-sunxi.c | 483 ++++ bsp/allwinner/dm/sdio/Kconfig | 7 + bsp/allwinner/dm/sdio/SConscript | 13 + bsp/allwinner/dm/sdio/sdio-sunxi.c | 1193 +++++++++ bsp/allwinner/dm/soc/Kconfig | 42 + bsp/allwinner/dm/soc/SConscript | 15 + bsp/allwinner/dm/soc/g2d/g2d-sunxi.c | 849 ++++++ bsp/allwinner/dm/soc/g2d/g2d.h | 309 +++ bsp/allwinner/dm/soc/g2d/g2d_sunxi_cmdlist.c | 73 + bsp/allwinner/dm/soc/g2d/g2d_sunxi_compose.c | 580 +++++ bsp/allwinner/dm/soc/g2d/g2d_sunxi_format.c | 142 + bsp/allwinner/dm/soc/g2d/g2d_sunxi_hw.h | 98 + bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h | 189 ++ bsp/allwinner/dm/soc/g2d/g2d_sunxi_tables.c | 218 ++ bsp/allwinner/dm/soc/jpegd-sunxi.c | 2315 +++++++++++++++++ bsp/allwinner/dm/spi/Kconfig | 13 + bsp/allwinner/dm/spi/SConscript | 13 + bsp/allwinner/dm/spi/spi-sunxi.c | 537 ++++ bsp/allwinner/dm/thermal/Kconfig | 7 + bsp/allwinner/dm/thermal/SConscript | 12 + bsp/allwinner/dm/thermal/thermal-sunxi.c | 295 +++ bsp/allwinner/dm/usb/Kconfig | 11 + bsp/allwinner/dm/usb/SConscript | 10 + bsp/allwinner/dm/usb/usb-sunxi.c | 271 ++ bsp/allwinner/dm/watchdog/Kconfig | 5 + bsp/allwinner/dm/watchdog/SConscript | 13 + bsp/allwinner/dm/watchdog/watchdog-sunxi.c | 274 ++ bsp/allwinner/f101/.config | 1844 +++++++++++++ bsp/allwinner/f101/Kconfig | 43 + bsp/allwinner/f101/README.md | 162 ++ bsp/allwinner/f101/README_zh.md | 147 ++ bsp/allwinner/f101/SConscript | 18 + bsp/allwinner/f101/SConstruct | 54 + bsp/allwinner/f101/applications/SConscript | 18 + bsp/allwinner/f101/applications/fb_rotate.c | 76 + bsp/allwinner/f101/applications/g2d.c | 671 +++++ bsp/allwinner/f101/applications/jpegd.c | 377 +++ bsp/allwinner/f101/applications/main.c | 14 + bsp/allwinner/f101/board/SConscript | 6 + bsp/allwinner/f101/board/board.c | 32 + bsp/allwinner/f101/board/board.h | 14 + bsp/allwinner/f101/board/usb_config.h | 25 + bsp/allwinner/f101/f101-yuzukineko.Kconfig | 69 + bsp/allwinner/f101/rtconfig.h | 621 +++++ bsp/allwinner/f101/rtconfig.py | 47 + bsp/allwinner/f101/smode-bare.config | 1844 +++++++++++++ bsp/allwinner/f101/usbcrc-send.sh | 36 + .../drivers/graphic/framebuffer/Kconfig | 13 + .../drivers/graphic/framebuffer/SConscript | 3 + .../drivers/graphic/framebuffer/fb-st77916.c | 786 ++++++ components/drivers/graphic/graphic_simple.c | 6 +- components/drivers/graphic/logo/logo.c | 2 +- components/drivers/input/touchscreen/Kconfig | 8 + .../drivers/input/touchscreen/SConscript | 3 + .../drivers/input/touchscreen/ts-cst3530.c | 476 ++++ components/drivers/misc/Kconfig | 6 +- .../drivers/serial/device/8250/8250-dw.c | 19 + components/drivers/serial/device/8250/Kconfig | 1 + components/drivers/spi/dev_qspi_core.c | 6 + components/drivers/thermal/Kconfig | 1 + components/drivers/usb/Kconfig | 4 + .../usb/cherryusb/port/musb/usb_glue_sunxi.c | 4 +- 143 files changed, 27369 insertions(+), 4 deletions(-) create mode 100644 bsp/allwinner/dm/Kconfig create mode 100644 bsp/allwinner/dm/SConscript create mode 100644 bsp/allwinner/dm/adc/Kconfig create mode 100644 bsp/allwinner/dm/adc/SConscript create mode 100644 bsp/allwinner/dm/adc/adc-sunxi-gpadc.c create mode 100644 bsp/allwinner/dm/audio/Kconfig create mode 100644 bsp/allwinner/dm/audio/SConscript create mode 100644 bsp/allwinner/dm/audio/audio-sunxi-codec.c create mode 100644 bsp/allwinner/dm/audio/audio-sunxi-i2s.c create mode 100644 bsp/allwinner/dm/audio/audio-sunxi-owa.c create mode 100644 bsp/allwinner/dm/audio/audio-sunxi.c create mode 100644 bsp/allwinner/dm/audio/audio-sunxi.h create mode 100644 bsp/allwinner/dm/clk/Kconfig create mode 100644 bsp/allwinner/dm/clk/SConscript create mode 100644 bsp/allwinner/dm/clk/clk-sun252i-f101-cpupll.c create mode 100644 bsp/allwinner/dm/clk/clk-sun252i-f101-rtc.c create mode 100644 bsp/allwinner/dm/clk/clk-sun252i-f101.c create mode 100644 bsp/allwinner/dm/clk/clk-sunxi.c create mode 100644 bsp/allwinner/dm/clock_time/Kconfig create mode 100644 bsp/allwinner/dm/clock_time/SConscript create mode 100644 bsp/allwinner/dm/clock_time/clock_timer-sunxi.c create mode 100644 bsp/allwinner/dm/dma/Kconfig create mode 100644 bsp/allwinner/dm/dma/SConscript create mode 100644 bsp/allwinner/dm/dma/dma-sunxi.c create mode 100644 bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-co6300.dtsi create mode 100644 bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-common.dtsi create mode 100644 bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-radxa-lcd-8hd.dtsi create mode 100644 bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-st77916.dtsi create mode 100644 bsp/allwinner/dm/dts/sun252i-f101-yuzukineko.dts create mode 100644 bsp/allwinner/dm/dts/sun252i-f101.dtsi create mode 100644 bsp/allwinner/dm/dvfs/SConscript create mode 100644 bsp/allwinner/dm/dvfs/cpufreq/Kconfig create mode 100644 bsp/allwinner/dm/dvfs/cpufreq/SConscript create mode 100644 bsp/allwinner/dm/dvfs/cpufreq/dvfs-sun252i-f101-cpufreq.c create mode 100644 bsp/allwinner/dm/graphic/SConscript create mode 100644 bsp/allwinner/dm/graphic/framebuffer/Kconfig create mode 100644 bsp/allwinner/dm/graphic/framebuffer/SConscript create mode 100644 bsp/allwinner/dm/graphic/framebuffer/fb-sunxi-de.c create mode 100644 bsp/allwinner/dm/graphic/framebuffer/sunxi-display-output.c create mode 100644 bsp/allwinner/dm/graphic/framebuffer/sunxi-display-output.h create mode 100644 bsp/allwinner/dm/hwcrypto/Kconfig create mode 100644 bsp/allwinner/dm/hwcrypto/SConscript create mode 100644 bsp/allwinner/dm/hwcrypto/hwcrypto-sunxi-ce.c create mode 100644 bsp/allwinner/dm/i2c/Kconfig create mode 100644 bsp/allwinner/dm/i2c/SConscript create mode 100644 bsp/allwinner/dm/i2c/i2c-sunxi.c create mode 100644 bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-ccu.h create mode 100644 bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-cpupll.h create mode 100644 bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-rtc.h create mode 100644 bsp/allwinner/dm/include/dt-bindings/reset/sun252i-f101-ccu.h create mode 100644 bsp/allwinner/dm/include/sunxi-g2d.h create mode 100644 bsp/allwinner/dm/include/sunxi-jpegd.h create mode 100644 bsp/allwinner/dm/include/sunxi-jpege.h create mode 100644 bsp/allwinner/dm/include/sunxi-pngd.h create mode 100644 bsp/allwinner/dm/include/sunxi_ccu.h create mode 100644 bsp/allwinner/dm/include/sunxi_pinctrl.h create mode 100644 bsp/allwinner/dm/include/sunxi_reset.h create mode 100644 bsp/allwinner/dm/input/SConscript create mode 100644 bsp/allwinner/dm/input/touchscreen/Kconfig create mode 100644 bsp/allwinner/dm/input/touchscreen/SConscript create mode 100644 bsp/allwinner/dm/input/touchscreen/ts-sunxi-tpadc.c create mode 100644 bsp/allwinner/dm/inputcapture/Kconfig create mode 100644 bsp/allwinner/dm/inputcapture/SConscript create mode 100644 bsp/allwinner/dm/inputcapture/inputcapture-sunxi-irrx.c create mode 100644 bsp/allwinner/dm/nvmem/Kconfig create mode 100644 bsp/allwinner/dm/nvmem/SConscript create mode 100644 bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c create mode 100644 bsp/allwinner/dm/pinctrl/Kconfig create mode 100644 bsp/allwinner/dm/pinctrl/SConscript create mode 100644 bsp/allwinner/dm/pinctrl/pinctrl-sun252i-f101.c create mode 100644 bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c create mode 100644 bsp/allwinner/dm/pwm/Kconfig create mode 100644 bsp/allwinner/dm/pwm/SConscript create mode 100644 bsp/allwinner/dm/pwm/pwm-sunxi.c create mode 100644 bsp/allwinner/dm/reset/Kconfig create mode 100644 bsp/allwinner/dm/reset/SConscript create mode 100644 bsp/allwinner/dm/reset/reset-sun252i-f101.c create mode 100644 bsp/allwinner/dm/reset/reset-sunxi.c create mode 100644 bsp/allwinner/dm/rtc/Kconfig create mode 100644 bsp/allwinner/dm/rtc/SConscript create mode 100644 bsp/allwinner/dm/rtc/rtc-sunxi.c create mode 100644 bsp/allwinner/dm/sdio/Kconfig create mode 100644 bsp/allwinner/dm/sdio/SConscript create mode 100644 bsp/allwinner/dm/sdio/sdio-sunxi.c create mode 100644 bsp/allwinner/dm/soc/Kconfig create mode 100644 bsp/allwinner/dm/soc/SConscript create mode 100644 bsp/allwinner/dm/soc/g2d/g2d-sunxi.c create mode 100644 bsp/allwinner/dm/soc/g2d/g2d.h create mode 100644 bsp/allwinner/dm/soc/g2d/g2d_sunxi_cmdlist.c create mode 100644 bsp/allwinner/dm/soc/g2d/g2d_sunxi_compose.c create mode 100644 bsp/allwinner/dm/soc/g2d/g2d_sunxi_format.c create mode 100644 bsp/allwinner/dm/soc/g2d/g2d_sunxi_hw.h create mode 100644 bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h create mode 100644 bsp/allwinner/dm/soc/g2d/g2d_sunxi_tables.c create mode 100644 bsp/allwinner/dm/soc/jpegd-sunxi.c create mode 100644 bsp/allwinner/dm/spi/Kconfig create mode 100644 bsp/allwinner/dm/spi/SConscript create mode 100644 bsp/allwinner/dm/spi/spi-sunxi.c create mode 100644 bsp/allwinner/dm/thermal/Kconfig create mode 100644 bsp/allwinner/dm/thermal/SConscript create mode 100644 bsp/allwinner/dm/thermal/thermal-sunxi.c create mode 100644 bsp/allwinner/dm/usb/Kconfig create mode 100644 bsp/allwinner/dm/usb/SConscript create mode 100644 bsp/allwinner/dm/usb/usb-sunxi.c create mode 100644 bsp/allwinner/dm/watchdog/Kconfig create mode 100644 bsp/allwinner/dm/watchdog/SConscript create mode 100644 bsp/allwinner/dm/watchdog/watchdog-sunxi.c create mode 100644 bsp/allwinner/f101/.config create mode 100644 bsp/allwinner/f101/Kconfig create mode 100644 bsp/allwinner/f101/README.md create mode 100644 bsp/allwinner/f101/README_zh.md create mode 100644 bsp/allwinner/f101/SConscript create mode 100644 bsp/allwinner/f101/SConstruct create mode 100644 bsp/allwinner/f101/applications/SConscript create mode 100644 bsp/allwinner/f101/applications/fb_rotate.c create mode 100644 bsp/allwinner/f101/applications/g2d.c create mode 100644 bsp/allwinner/f101/applications/jpegd.c create mode 100644 bsp/allwinner/f101/applications/main.c create mode 100644 bsp/allwinner/f101/board/SConscript create mode 100644 bsp/allwinner/f101/board/board.c create mode 100644 bsp/allwinner/f101/board/board.h create mode 100644 bsp/allwinner/f101/board/usb_config.h create mode 100644 bsp/allwinner/f101/f101-yuzukineko.Kconfig create mode 100644 bsp/allwinner/f101/rtconfig.h create mode 100644 bsp/allwinner/f101/rtconfig.py create mode 100644 bsp/allwinner/f101/smode-bare.config create mode 100755 bsp/allwinner/f101/usbcrc-send.sh create mode 100644 components/drivers/graphic/framebuffer/fb-st77916.c create mode 100644 components/drivers/input/touchscreen/ts-cst3530.c diff --git a/bsp/README.md b/bsp/README.md index 221ac2046859..e55876f770c6 100644 --- a/bsp/README.md +++ b/bsp/README.md @@ -826,6 +826,7 @@ This document is based on the RT-Thread mainline repository and categorizes the |----------|------|------|-----|-----|-----|-----|-----|-----|-------|-------| | [d1](allwinner/d1) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [d1s](allwinner/d1s) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [f101](allwinner/f101) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | #### 🟡 K210 (SMP) diff --git a/bsp/allwinner/dm/Kconfig b/bsp/allwinner/dm/Kconfig new file mode 100644 index 000000000000..980a6af3d43e --- /dev/null +++ b/bsp/allwinner/dm/Kconfig @@ -0,0 +1,24 @@ +SOC_DM_CLK_DIR = $(SOC_DM_DIR)/clk +SOC_DM_ADC_DIR = $(SOC_DM_DIR)/adc +SOC_DM_AUDIO_DIR = $(SOC_DM_DIR)/audio +SOC_DM_CLOCK_TIME_DIR = $(SOC_DM_DIR)/clock_time +SOC_DM_DMA_DIR = $(SOC_DM_DIR)/dma +SOC_DM_DVFS_CPUFREQ_DIR = $(SOC_DM_DIR)/dvfs/cpufreq +SOC_DM_GRAPHIC_FB_DIR = $(SOC_DM_DIR)/graphic/framebuffer +SOC_DM_HWCRYPTO_DIR = $(SOC_DM_DIR)/hwcrypto +SOC_DM_I2C_DIR = $(SOC_DM_DIR)/i2c +SOC_DM_INPUT_TOUCHSCREEN_DIR = $(SOC_DM_DIR)/input/touchscreen +SOC_DM_INPUT_CAPTURE_DIR = $(SOC_DM_DIR)/inputcapture +SOC_DM_NVMEM_DIR = $(SOC_DM_DIR)/nvmem +SOC_DM_PIC_DIR = $(RTT_DIR)/libcpu/risc-v/common/pic +SOC_DM_PINCTRL_DIR = $(SOC_DM_DIR)/pinctrl +SOC_DM_PWM_DIR = $(SOC_DM_DIR)/pwm +SOC_DM_RESET_DIR = $(SOC_DM_DIR)/reset +SOC_DM_RTC_DIR = $(SOC_DM_DIR)/rtc +SOC_DM_SDIO_DIR = $(SOC_DM_DIR)/sdio +SOC_DM_SERIAL_DIR = $(RTT_DIR)/libcpu/risc-v/common/serial +SOC_DM_SOC_DIR = $(SOC_DM_DIR)/soc +SOC_DM_SPI_DIR = $(SOC_DM_DIR)/spi +SOC_DM_THERMAL_DIR = $(SOC_DM_DIR)/thermal +SOC_DM_USB_DIR = $(SOC_DM_DIR)/usb +SOC_DM_WDT_DIR = $(SOC_DM_DIR)/watchdog diff --git a/bsp/allwinner/dm/SConscript b/bsp/allwinner/dm/SConscript new file mode 100644 index 000000000000..9d8df9d34812 --- /dev/null +++ b/bsp/allwinner/dm/SConscript @@ -0,0 +1,15 @@ +# SoC-level Device Model drivers shared by Allwinner BSPs. + +import os +from building import * + +cwd = GetCurrentDir() +objs = [] + +for directory in os.listdir(cwd): + sconscript = os.path.join(directory, 'SConscript') + + if os.path.isfile(os.path.join(cwd, sconscript)): + objs += SConscript(sconscript) + +Return('objs') diff --git a/bsp/allwinner/dm/adc/Kconfig b/bsp/allwinner/dm/adc/Kconfig new file mode 100644 index 000000000000..99a61238c92f --- /dev/null +++ b/bsp/allwinner/dm/adc/Kconfig @@ -0,0 +1,9 @@ +config RT_ADC_SUNXI_GPADC + bool "Allwinner Sunxi GPADC" + depends on RT_USING_ADC + depends on RT_USING_DM + depends on RT_USING_CLK + depends on RT_USING_RESET + default n + help + Enable the General Purpose ADC found on supported Allwinner SoCs. diff --git a/bsp/allwinner/dm/adc/SConscript b/bsp/allwinner/dm/adc/SConscript new file mode 100644 index 000000000000..7fbdb550f4d5 --- /dev/null +++ b/bsp/allwinner/dm/adc/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_ADC_SUNXI_GPADC']): + src += ['adc-sunxi-gpadc.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/adc/adc-sunxi-gpadc.c b/bsp/allwinner/dm/adc/adc-sunxi-gpadc.c new file mode 100644 index 000000000000..5616553ccdfe --- /dev/null +++ b/bsp/allwinner/dm/adc/adc-sunxi-gpadc.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "adc.sunxi" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_GPADC_SR 0x00 +#define SUNXI_GPADC_CTRL 0x04 +#define SUNXI_GPADC_CS_EN 0x08 +#define SUNXI_GPADC_DATA_BASE 0x80 + +#define SUNXI_GPADC_DATA_MASK RT_GENMASK(11, 0) +#define SUNXI_GPADC_VCM_BUF_EN RT_BIT(0) +#define SUNXI_GPADC_CALIBRATE_EN RT_BIT(17) +#define SUNXI_GPADC_EN RT_BIT(16) +#define SUNXI_GPADC_MODE_MASK RT_GENMASK(19, 18) +#define SUNXI_GPADC_MODE_CONTINUOUS (2U << 18) + +#define SUNXI_GPADC_OSC_RATE 24000000U +#define SUNXI_GPADC_MIN_RATE 400U +#define SUNXI_GPADC_MAX_RATE 1000000U +#define SUNXI_GPADC_DEFAULT_RATE 1000000U +#define SUNXI_GPADC_MAX_CHANNELS 12U +#define SUNXI_GPADC_RESOLUTION 12U +#define SUNXI_GPADC_VREF_MV 1800U + +struct sunxi_gpadc +{ + struct rt_adc_device parent; + + void *regs; + struct rt_clk *clk; + struct rt_reset_control *reset; + rt_uint32_t channels; + rt_uint32_t enabled_channels; + struct rt_mutex lock; + rt_bool_t clk_enabled; + rt_bool_t lock_initialized; +}; + +#define to_sunxi_gpadc(raw) rt_container_of(raw, struct sunxi_gpadc, parent) + +static rt_err_t sunxi_gpadc_set_rate(struct sunxi_gpadc *gpadc, + rt_uint32_t sample_rate) +{ + rt_uint32_t div; + rt_uint32_t tacq; + + if (sample_rate < SUNXI_GPADC_MIN_RATE || + sample_rate > SUNXI_GPADC_MAX_RATE) + { + return -RT_EINVAL; + } + + /* One conversion needs TACQ + 1 + 13 conversion + 3 wait cycles. */ + div = SUNXI_GPADC_OSC_RATE / sample_rate - 3; + tacq = div - 13 - 1; + if (div > 0xffff || tacq > 0xffff) + { + return -RT_EINVAL; + } + + HWREG32(gpadc->regs + SUNXI_GPADC_SR) = (div << 16) | tacq; + + return RT_EOK; +} + +static void sunxi_gpadc_set_channels(struct sunxi_gpadc *gpadc, + rt_uint32_t channels) +{ + HWREG32(gpadc->regs + SUNXI_GPADC_CS_EN) = channels; +} + +static rt_bool_t sunxi_gpadc_valid_channel(struct sunxi_gpadc *gpadc, + rt_int8_t channel) +{ + return channel >= 0 && (rt_uint32_t)channel < gpadc->channels; +} + +static rt_err_t sunxi_gpadc_enabled(struct rt_adc_device *adc, + rt_int8_t channel, rt_bool_t enabled) +{ + rt_uint32_t mask; + struct sunxi_gpadc *gpadc = to_sunxi_gpadc(adc); + + if (!sunxi_gpadc_valid_channel(gpadc, channel)) + { + return -RT_EINVAL; + } + + mask = RT_BIT(channel); + rt_mutex_take(&gpadc->lock, RT_WAITING_FOREVER); + + if (enabled) + { + gpadc->enabled_channels |= mask; + } + else + { + gpadc->enabled_channels &= ~mask; + } + sunxi_gpadc_set_channels(gpadc, gpadc->enabled_channels); + + rt_mutex_release(&gpadc->lock); + + return RT_EOK; +} + +static rt_err_t sunxi_gpadc_convert(struct rt_adc_device *adc, + rt_int8_t channel, rt_uint32_t *value) +{ + rt_uint32_t mask; + rt_bool_t temporarily_enabled = RT_FALSE; + struct sunxi_gpadc *gpadc = to_sunxi_gpadc(adc); + + if (!value || !sunxi_gpadc_valid_channel(gpadc, channel)) + { + return -RT_EINVAL; + } + + mask = RT_BIT(channel); + rt_mutex_take(&gpadc->lock, RT_WAITING_FOREVER); + + if (!(gpadc->enabled_channels & mask)) + { + sunxi_gpadc_set_channels(gpadc, gpadc->enabled_channels | mask); + temporarily_enabled = RT_TRUE; + rt_hw_us_delay(2); + } + + *value = HWREG32(gpadc->regs + SUNXI_GPADC_DATA_BASE + + (rt_uint32_t)channel * sizeof(rt_uint32_t)) & SUNXI_GPADC_DATA_MASK; + + if (temporarily_enabled) + { + sunxi_gpadc_set_channels(gpadc, gpadc->enabled_channels); + } + + rt_mutex_release(&gpadc->lock); + + return RT_EOK; +} + +static rt_uint8_t sunxi_gpadc_get_resolution(struct rt_adc_device *adc) +{ + return SUNXI_GPADC_RESOLUTION; +} + +static rt_int16_t sunxi_gpadc_get_vref(struct rt_adc_device *adc) +{ + return SUNXI_GPADC_VREF_MV; +} + +static const struct rt_adc_ops sunxi_gpadc_ops = +{ + .enabled = sunxi_gpadc_enabled, + .convert = sunxi_gpadc_convert, + .get_resolution = sunxi_gpadc_get_resolution, + .get_vref = sunxi_gpadc_get_vref, +}; + +static void sunxi_gpadc_release(struct sunxi_gpadc *gpadc) +{ + if (gpadc->regs) + { + HWREG32(gpadc->regs + SUNXI_GPADC_CS_EN) = 0; + HWREG32(gpadc->regs + SUNXI_GPADC_CTRL) &= ~SUNXI_GPADC_EN; + } + if (!rt_is_err_or_null(gpadc->reset)) + { + rt_reset_control_assert(gpadc->reset); + rt_reset_control_put(gpadc->reset); + } + if (gpadc->clk_enabled) + { + rt_clk_disable_unprepare(gpadc->clk); + } + if (!rt_is_err_or_null(gpadc->clk)) + { + rt_clk_put(gpadc->clk); + } + if (gpadc->lock_initialized) + { + rt_mutex_detach(&gpadc->lock); + } + if (gpadc->regs) + { + rt_iounmap(gpadc->regs); + } + rt_free(gpadc); +} + +static rt_err_t sunxi_gpadc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t channels = SUNXI_GPADC_MAX_CHANNELS; + rt_uint32_t sample_rate = SUNXI_GPADC_DEFAULT_RATE; + rt_uint32_t ctrl; + const char *dev_name; + struct rt_device *dev = &pdev->parent; + struct sunxi_gpadc *gpadc = rt_calloc(1, sizeof(*gpadc)); + + if (!gpadc) + { + return -RT_ENOMEM; + } + + gpadc->regs = rt_dm_dev_iomap(dev, 0); + if (!gpadc->regs) + { + err = -RT_EIO; + goto _fail; + } + + gpadc->clk = rt_clk_get_by_name(dev, "bus"); + gpadc->reset = rt_reset_control_get_by_name(dev, "bus"); + if (rt_is_err(gpadc->clk) || rt_is_err(gpadc->reset)) + { + err = rt_is_err(gpadc->clk) ? rt_ptr_err(gpadc->clk) : + rt_ptr_err(gpadc->reset); + goto _fail; + } + + if ((err = rt_reset_control_deassert(gpadc->reset)) || + (err = rt_clk_prepare_enable(gpadc->clk))) + { + goto _fail; + } + gpadc->clk_enabled = RT_TRUE; + + rt_ofw_prop_read_u32(dev->ofw_node, "allwinner,channel-count", &channels); + if (!channels || channels > SUNXI_GPADC_MAX_CHANNELS) + { + err = -RT_EINVAL; + goto _fail; + } + gpadc->channels = channels; + + rt_ofw_prop_read_u32(dev->ofw_node, "allwinner,sample-rate", &sample_rate); + if ((err = sunxi_gpadc_set_rate(gpadc, sample_rate))) + { + goto _fail; + } + + ctrl = HWREG32(gpadc->regs + SUNXI_GPADC_CTRL); + ctrl |= SUNXI_GPADC_VCM_BUF_EN | SUNXI_GPADC_CALIBRATE_EN | + SUNXI_GPADC_EN; + ctrl &= ~SUNXI_GPADC_MODE_MASK; + ctrl |= SUNXI_GPADC_MODE_CONTINUOUS; + HWREG32(gpadc->regs + SUNXI_GPADC_CTRL) = ctrl; + sunxi_gpadc_set_channels(gpadc, 0); + + if ((err = rt_dm_dev_set_name_auto(&gpadc->parent.parent, "adc")) < 0) + { + goto _fail; + } + dev_name = rt_dm_dev_get_name(&gpadc->parent.parent); + rt_mutex_init(&gpadc->lock, dev_name, RT_IPC_FLAG_PRIO); + gpadc->lock_initialized = RT_TRUE; + + if ((err = rt_hw_adc_register(&gpadc->parent, dev_name, + &sunxi_gpadc_ops, gpadc))) + { + goto _fail; + } + + dev->user_data = gpadc; + rt_dm_dev_bind_fwdata(dev, RT_NULL, gpadc); + + return RT_EOK; + +_fail: + sunxi_gpadc_release(gpadc); + + return err; +} + +static rt_err_t sunxi_gpadc_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct sunxi_gpadc *gpadc = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + rt_device_unregister(&gpadc->parent.parent); + sunxi_gpadc_release(gpadc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_gpadc_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-gpadc" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_gpadc_driver = +{ + .name = "sunxi-gpadc", + .ids = sunxi_gpadc_ofw_ids, + .probe = sunxi_gpadc_probe, + .remove = sunxi_gpadc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_gpadc_driver); diff --git a/bsp/allwinner/dm/audio/Kconfig b/bsp/allwinner/dm/audio/Kconfig new file mode 100644 index 000000000000..54d13e00c19a --- /dev/null +++ b/bsp/allwinner/dm/audio/Kconfig @@ -0,0 +1,50 @@ +config RT_AUDIO_SUNXI_CODEC + bool "Allwinner Sunxi integrated audio codec" + depends on RT_USING_AUDIO + depends on RT_USING_CLK + depends on RT_USING_DM + depends on RT_USING_HEAP + depends on RT_USING_PIN + depends on RT_USING_RESET + select RT_USING_DMA + select RT_DMA_SUNXI + select RT_USING_SYSTEM_WORKQUEUE + default n + help + Enable playback through the F101 integrated DAC. The driver uses the + standard RT-Thread audio and DMA interfaces and supports 48 kHz-family + PCM sample rates with 16-, 24-, or 32-bit mono and stereo streams. + +config RT_AUDIO_SUNXI_I2S0 + bool "Allwinner Sunxi I2S0 output" + depends on RT_USING_AUDIO + depends on RT_USING_CLK + depends on RT_USING_DM + depends on RT_USING_HEAP + depends on RT_USING_PIN + depends on RT_USING_RESET + select RT_USING_DMA + select RT_DMA_SUNXI + select RT_USING_SYSTEM_WORKQUEUE + default n + help + Enable the F101 I2S0 controller as a standard RT-Thread audio output. + The controller is configured as an I2S clock master and supports the + 48 kHz-family PCM rates up to 192 kHz with 16-, 24-, or 32-bit samples. + +config RT_AUDIO_SUNXI_OWA + bool "Allwinner Sunxi OWA output" + depends on RT_USING_AUDIO + depends on RT_USING_CLK + depends on RT_USING_DM + depends on RT_USING_HEAP + depends on RT_USING_PIN + depends on RT_USING_RESET + select RT_USING_DMA + select RT_DMA_SUNXI + select RT_USING_SYSTEM_WORKQUEUE + default n + help + Enable the F101 OWA (S/PDIF) controller as a standard RT-Thread audio + output. It emits IEC 60958 PCM through the OWA TX pin and supports the + 48 kHz-family PCM rates up to 192 kHz. diff --git a/bsp/allwinner/dm/audio/SConscript b/bsp/allwinner/dm/audio/SConscript new file mode 100644 index 000000000000..bca356fccc2d --- /dev/null +++ b/bsp/allwinner/dm/audio/SConscript @@ -0,0 +1,21 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_AUDIO_SUNXI_CODEC']): + src += ['audio-sunxi-codec.c'] + +if GetDepend('RT_AUDIO_SUNXI_I2S0') or GetDepend('RT_AUDIO_SUNXI_OWA'): + src += ['audio-sunxi.c'] + +if GetDepend(['RT_AUDIO_SUNXI_I2S0']): + src += ['audio-sunxi-i2s.c'] + +if GetDepend(['RT_AUDIO_SUNXI_OWA']): + src += ['audio-sunxi-owa.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/audio/audio-sunxi-codec.c b/bsp/allwinner/dm/audio/audio-sunxi-codec.c new file mode 100644 index 000000000000..159a16f7b0a6 --- /dev/null +++ b/bsp/allwinner/dm/audio/audio-sunxi-codec.c @@ -0,0 +1,652 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "audio.sunxi.codec" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_CODEC_DAC_DPC 0x000 +#define SUNXI_CODEC_DAC_VOL_CTL 0x004 +#define SUNXI_CODEC_DAC_FIFO_CTL 0x010 +#define SUNXI_CODEC_DAC_FIFO_STA 0x014 +#define SUNXI_CODEC_DAC_TXDATA 0x020 +#define SUNXI_CODEC_DAC_CNT 0x024 +#define SUNXI_CODEC_DAC_AN_REG 0x310 +#define SUNXI_CODEC_RAMP_REG 0x31c +#define SUNXI_CODEC_HP2_REG 0x340 + +#define SUNXI_CODEC_DAC_DIG_EN RT_BIT(31) +#define SUNXI_CODEC_DAC_DVOL_MASK RT_GENMASK(17, 12) +#define SUNXI_CODEC_DAC_VOL_SEL RT_BIT(16) +#define SUNXI_CODEC_DAC_VOL_L_MASK RT_GENMASK(15, 8) +#define SUNXI_CODEC_DAC_VOL_R_MASK RT_GENMASK(7, 0) +#define SUNXI_CODEC_DAC_FS_MASK RT_GENMASK(31, 29) +#define SUNXI_CODEC_DAC_FIFO_MODE_MASK RT_GENMASK(25, 24) +#define SUNXI_CODEC_DAC_MONO_EN RT_BIT(6) +#define SUNXI_CODEC_DAC_SAMPLE_BITS RT_BIT(5) +#define SUNXI_CODEC_DAC_DRQ_EN RT_BIT(4) +#define SUNXI_CODEC_DAC_FIFO_FLUSH RT_BIT(0) +#define SUNXI_CODEC_DAC_TXE_INT RT_BIT(3) +#define SUNXI_CODEC_DAC_TXU_INT RT_BIT(2) +#define SUNXI_CODEC_DAC_TXO_INT RT_BIT(1) +#define SUNXI_CODEC_DAC_CH_L_EN RT_BIT(15) +#define SUNXI_CODEC_DAC_CH_R_EN RT_BIT(14) +#define SUNXI_CODEC_DAC_LMUTE RT_BIT(12) +#define SUNXI_CODEC_DAC_RMUTE RT_BIT(10) +#define SUNXI_CODEC_RAMP_RK_OPT_EN RT_BIT(21) +#define SUNXI_CODEC_RAMP_RMC_EN RT_BIT(1) +#define SUNXI_CODEC_HP_BUF_EN RT_BIT(31) +#define SUNXI_CODEC_HP_DRVEN RT_BIT(21) +#define SUNXI_CODEC_HP_RSWITCH RT_BIT(19) +#define SUNXI_CODEC_HP_FB_IN_EN RT_BIT(17) +#define SUNXI_CODEC_HP_RAMP_OUT_EN RT_BIT(15) + +#define SUNXI_CODEC_BUFFER_SIZE 16384 +#define SUNXI_CODEC_BLOCK_SIZE 4096 +struct sunxi_codec_rate +{ + rt_uint32_t rate; + rt_uint32_t value; +}; + +struct sunxi_codec +{ + struct rt_audio_device parent; + + struct rt_device *dev; + void *base; + struct rt_clk_array *clks; + struct rt_reset_control *reset; + struct rt_dma_chan *dma_tx; + rt_uint8_t *playback_buffer; + rt_ubase_t playback_dma; + rt_ubase_t fifo_dma; + rt_base_t spk_enable_pin; + rt_uint8_t spk_enable_active; + rt_uint32_t spk_enable_delay_ms; + struct rt_audio_configure config; + struct rt_work dma_done_work; + rt_uint32_t volume; + rt_bool_t clks_enabled; + rt_bool_t running; +}; + +#define to_sunxi_codec(raw) \ + rt_container_of(raw, struct sunxi_codec, parent) + +static const struct sunxi_codec_rate sunxi_codec_rates[] = +{ + { 8000, 5 }, + { 12000, 4 }, + { 16000, 3 }, + { 24000, 2 }, + { 32000, 1 }, + { 48000, 0 }, + { 96000, 7 }, + { 192000, 6 }, +}; + +static rt_uint32_t sunxi_codec_readl(struct sunxi_codec *codec, + rt_uint32_t offset) +{ + return HWREG32(codec->base + offset); +} + +static void sunxi_codec_writel(struct sunxi_codec *codec, rt_uint32_t value, + rt_uint32_t offset) +{ + HWREG32(codec->base + offset) = value; +} + +static void sunxi_codec_update_bits(struct sunxi_codec *codec, + rt_uint32_t offset, rt_uint32_t mask, rt_uint32_t value) +{ + rt_uint32_t reg = sunxi_codec_readl(codec, offset); + + reg &= ~mask; + reg |= value & mask; + sunxi_codec_writel(codec, reg, offset); +} + +static const struct sunxi_codec_rate *sunxi_codec_find_rate(rt_uint32_t rate) +{ + rt_size_t index; + + for (index = 0; index < RT_ARRAY_SIZE(sunxi_codec_rates); ++index) + { + if (sunxi_codec_rates[index].rate == rate) + { + return &sunxi_codec_rates[index]; + } + } + + return RT_NULL; +} + +static void sunxi_codec_set_volume(struct sunxi_codec *codec, + rt_uint32_t volume) +{ + rt_uint32_t value; + + if (volume > AUDIO_VOLUME_MAX) + { + volume = AUDIO_VOLUME_MAX; + } + value = 63 - volume * 63 / AUDIO_VOLUME_MAX; + sunxi_codec_update_bits(codec, SUNXI_CODEC_DAC_DPC, + SUNXI_CODEC_DAC_DVOL_MASK, value << 12); + codec->volume = volume; +} + +static rt_err_t sunxi_codec_apply_config(struct sunxi_codec *codec, + const struct rt_audio_configure *config) +{ + const struct sunxi_codec_rate *rate; + rt_uint32_t fifo_ctl; + + if (!config->channels || config->channels > 2 || + (config->samplebits != 16 && config->samplebits != 24 && + config->samplebits != 32)) + { + return -RT_EINVAL; + } + + rate = sunxi_codec_find_rate(config->samplerate); + if (!rate) + { + return -RT_ENOSYS; + } + + fifo_ctl = sunxi_codec_readl(codec, SUNXI_CODEC_DAC_FIFO_CTL); + fifo_ctl &= ~(SUNXI_CODEC_DAC_FS_MASK | + SUNXI_CODEC_DAC_FIFO_MODE_MASK | + SUNXI_CODEC_DAC_MONO_EN | + SUNXI_CODEC_DAC_SAMPLE_BITS); + fifo_ctl |= rate->value << 29; + if (config->samplebits == 16) + { + fifo_ctl |= 3 << 24; + } + else + { + fifo_ctl |= SUNXI_CODEC_DAC_SAMPLE_BITS; + } + if (config->channels == 1) + { + fifo_ctl |= SUNXI_CODEC_DAC_MONO_EN; + } + sunxi_codec_writel(codec, fifo_ctl, SUNXI_CODEC_DAC_FIFO_CTL); + codec->config = *config; + + return RT_EOK; +} + +static void sunxi_codec_output_enable(struct sunxi_codec *codec) +{ + sunxi_codec_update_bits(codec, SUNXI_CODEC_DAC_FIFO_CTL, + SUNXI_CODEC_DAC_FIFO_FLUSH, SUNXI_CODEC_DAC_FIFO_FLUSH); + sunxi_codec_writel(codec, SUNXI_CODEC_DAC_TXE_INT | + SUNXI_CODEC_DAC_TXU_INT | SUNXI_CODEC_DAC_TXO_INT, + SUNXI_CODEC_DAC_FIFO_STA); + sunxi_codec_writel(codec, 0, SUNXI_CODEC_DAC_CNT); + sunxi_codec_update_bits(codec, SUNXI_CODEC_DAC_AN_REG, + SUNXI_CODEC_DAC_CH_L_EN | SUNXI_CODEC_DAC_CH_R_EN | + SUNXI_CODEC_DAC_LMUTE | SUNXI_CODEC_DAC_RMUTE, + SUNXI_CODEC_DAC_CH_L_EN | SUNXI_CODEC_DAC_CH_R_EN | + SUNXI_CODEC_DAC_LMUTE | SUNXI_CODEC_DAC_RMUTE); + sunxi_codec_update_bits(codec, SUNXI_CODEC_HP2_REG, + SUNXI_CODEC_HP_BUF_EN | SUNXI_CODEC_HP_FB_IN_EN | + SUNXI_CODEC_HP_RAMP_OUT_EN | SUNXI_CODEC_HP_RSWITCH | + SUNXI_CODEC_HP_DRVEN, + SUNXI_CODEC_HP_BUF_EN | SUNXI_CODEC_HP_FB_IN_EN | + SUNXI_CODEC_HP_RAMP_OUT_EN | SUNXI_CODEC_HP_RSWITCH | + SUNXI_CODEC_HP_DRVEN); + sunxi_codec_update_bits(codec, SUNXI_CODEC_DAC_DPC, + SUNXI_CODEC_DAC_DIG_EN, SUNXI_CODEC_DAC_DIG_EN); + if (codec->spk_enable_pin >= 0) + { + rt_pin_write(codec->spk_enable_pin, codec->spk_enable_active); + if (codec->spk_enable_delay_ms) + { + rt_thread_mdelay(codec->spk_enable_delay_ms); + } + } +} + +static void sunxi_codec_output_disable(struct sunxi_codec *codec) +{ + if (codec->spk_enable_pin >= 0) + { + rt_pin_write(codec->spk_enable_pin, !codec->spk_enable_active); + } + sunxi_codec_update_bits(codec, SUNXI_CODEC_DAC_FIFO_CTL, + SUNXI_CODEC_DAC_DRQ_EN, 0); + sunxi_codec_update_bits(codec, SUNXI_CODEC_HP2_REG, + SUNXI_CODEC_HP_DRVEN | SUNXI_CODEC_HP_RAMP_OUT_EN | + SUNXI_CODEC_HP_RSWITCH | SUNXI_CODEC_HP_BUF_EN | + SUNXI_CODEC_HP_FB_IN_EN, 0); + sunxi_codec_update_bits(codec, SUNXI_CODEC_DAC_DPC, + SUNXI_CODEC_DAC_DIG_EN, 0); + sunxi_codec_update_bits(codec, SUNXI_CODEC_DAC_AN_REG, + SUNXI_CODEC_DAC_CH_L_EN | SUNXI_CODEC_DAC_CH_R_EN | + SUNXI_CODEC_DAC_LMUTE | SUNXI_CODEC_DAC_RMUTE, 0); +} + +static rt_err_t sunxi_codec_getcaps(struct rt_audio_device *audio, + struct rt_audio_caps *caps) +{ + struct sunxi_codec *codec = to_sunxi_codec(audio); + + switch (caps->main_type) + { + case AUDIO_TYPE_QUERY: + if (caps->sub_type != AUDIO_TYPE_QUERY) + { + return -RT_ENOSYS; + } + caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER; + return RT_EOK; + + case AUDIO_TYPE_OUTPUT: + switch (caps->sub_type) + { + case AUDIO_DSP_PARAM: + caps->udata.config = codec->config; + return RT_EOK; + case AUDIO_DSP_SAMPLERATE: + caps->udata.config.samplerate = codec->config.samplerate; + return RT_EOK; + case AUDIO_DSP_CHANNELS: + caps->udata.config.channels = codec->config.channels; + return RT_EOK; + case AUDIO_DSP_SAMPLEBITS: + caps->udata.config.samplebits = codec->config.samplebits; + return RT_EOK; + default: + return -RT_ENOSYS; + } + + case AUDIO_TYPE_MIXER: + if (caps->sub_type != AUDIO_MIXER_VOLUME) + { + return -RT_ENOSYS; + } + caps->udata.value = codec->volume; + return RT_EOK; + + default: + return -RT_ENOSYS; + } +} + +static rt_err_t sunxi_codec_configure(struct rt_audio_device *audio, + struct rt_audio_caps *caps) +{ + struct rt_audio_configure config; + struct sunxi_codec *codec = to_sunxi_codec(audio); + + if (codec->running) + { + return -RT_EBUSY; + } + + if (caps->main_type == AUDIO_TYPE_MIXER) + { + if (caps->sub_type != AUDIO_MIXER_VOLUME || caps->udata.value < 0 || + caps->udata.value > AUDIO_VOLUME_MAX) + { + return -RT_EINVAL; + } + sunxi_codec_set_volume(codec, caps->udata.value); + return RT_EOK; + } + + if (caps->main_type != AUDIO_TYPE_OUTPUT) + { + return -RT_ENOSYS; + } + + config = codec->config; + switch (caps->sub_type) + { + case AUDIO_DSP_PARAM: + config = caps->udata.config; + break; + case AUDIO_DSP_SAMPLERATE: + config.samplerate = caps->udata.config.samplerate; + break; + case AUDIO_DSP_CHANNELS: + config.channels = caps->udata.config.channels; + break; + case AUDIO_DSP_SAMPLEBITS: + config.samplebits = caps->udata.config.samplebits; + break; + default: + return -RT_ENOSYS; + } + + return sunxi_codec_apply_config(codec, &config); +} + +static rt_err_t sunxi_codec_init(struct rt_audio_device *audio) +{ + struct sunxi_codec *codec = to_sunxi_codec(audio); + struct rt_audio_configure config; + + config.samplerate = 48000; + config.channels = 2; + config.samplebits = 16; + + sunxi_codec_update_bits(codec, SUNXI_CODEC_RAMP_REG, + SUNXI_CODEC_RAMP_RMC_EN | SUNXI_CODEC_RAMP_RK_OPT_EN, + SUNXI_CODEC_RAMP_RMC_EN | SUNXI_CODEC_RAMP_RK_OPT_EN); + sunxi_codec_update_bits(codec, SUNXI_CODEC_DAC_VOL_CTL, + SUNXI_CODEC_DAC_VOL_SEL | SUNXI_CODEC_DAC_VOL_L_MASK | + SUNXI_CODEC_DAC_VOL_R_MASK, + SUNXI_CODEC_DAC_VOL_SEL | (160 << 8) | 160); + sunxi_codec_set_volume(codec, AUDIO_VOLUME_MAX); + + return sunxi_codec_apply_config(codec, &config); +} + +static rt_err_t sunxi_codec_start(struct rt_audio_device *audio, int stream) +{ + struct sunxi_codec *codec = to_sunxi_codec(audio); + + if (stream != AUDIO_STREAM_REPLAY) + { + return -RT_ENOSYS; + } + + sunxi_codec_output_enable(codec); + codec->running = RT_TRUE; + rt_audio_tx_complete(&codec->parent); + + return RT_EOK; +} + +static rt_err_t sunxi_codec_stop(struct rt_audio_device *audio, int stream) +{ + struct sunxi_codec *codec = to_sunxi_codec(audio); + + if (stream != AUDIO_STREAM_REPLAY) + { + return -RT_ENOSYS; + } + + codec->running = RT_FALSE; + rt_dma_chan_stop(codec->dma_tx); + rt_work_cancel(&codec->dma_done_work); + sunxi_codec_output_disable(codec); + + return RT_EOK; +} + +static rt_ssize_t sunxi_codec_transmit(struct rt_audio_device *audio, + const void *write_buf, void *read_buf, rt_size_t size) +{ + rt_err_t err; + enum rt_dma_slave_buswidth width; + rt_ubase_t offset; + const rt_uint8_t *buffer = write_buf; + struct rt_dma_slave_config config; + struct rt_dma_slave_transfer transfer; + struct sunxi_codec *codec = to_sunxi_codec(audio); + + if (!codec->running || !write_buf || read_buf || !size || + size > SUNXI_CODEC_BLOCK_SIZE) + { + return -RT_EINVAL; + } + + if (buffer < codec->playback_buffer || + buffer >= codec->playback_buffer + SUNXI_CODEC_BUFFER_SIZE) + { + return -RT_EINVAL; + } + + offset = buffer - codec->playback_buffer; + if (offset + size > SUNXI_CODEC_BUFFER_SIZE) + { + return -RT_EINVAL; + } + + width = codec->config.samplebits == 16 ? + RT_DMA_SLAVE_BUSWIDTH_2_BYTES : RT_DMA_SLAVE_BUSWIDTH_4_BYTES; + if ((offset % width) || (size % width)) + { + return -RT_EINVAL; + } + + rt_memset(&config, 0, sizeof(config)); + config.direction = RT_DMA_MEM_TO_DEV; + config.src_addr_width = width; + config.dst_addr_width = width; + config.src_maxburst = 4; + config.dst_maxburst = 4; + config.src_addr = codec->playback_dma + offset; + config.dst_addr = codec->fifo_dma; + if ((err = rt_dma_chan_config(codec->dma_tx, &config))) + { + return err; + } + + rt_memset(&transfer, 0, sizeof(transfer)); + transfer.buffer = (void *)write_buf; + transfer.buffer_len = size; + transfer.src_addr = config.src_addr; + transfer.dst_addr = config.dst_addr; + if ((err = rt_dma_prep_single(codec->dma_tx, &transfer))) + { + return err; + } + + if ((err = rt_dma_chan_start(codec->dma_tx))) + { + return err; + } + sunxi_codec_update_bits(codec, SUNXI_CODEC_DAC_FIFO_CTL, + SUNXI_CODEC_DAC_DRQ_EN, SUNXI_CODEC_DAC_DRQ_EN); + + return size; +} + +static void sunxi_codec_buffer_info(struct rt_audio_device *audio, + struct rt_audio_buf_info *info) +{ + struct sunxi_codec *codec = to_sunxi_codec(audio); + + info->buffer = codec->playback_buffer; + info->total_size = SUNXI_CODEC_BUFFER_SIZE; + info->block_size = SUNXI_CODEC_BLOCK_SIZE; + info->block_count = SUNXI_CODEC_BUFFER_SIZE / SUNXI_CODEC_BLOCK_SIZE; +} + +static const struct rt_audio_ops sunxi_codec_ops = +{ + .getcaps = sunxi_codec_getcaps, + .configure = sunxi_codec_configure, + .init = sunxi_codec_init, + .start = sunxi_codec_start, + .stop = sunxi_codec_stop, + .transmit = sunxi_codec_transmit, + .buffer_info = sunxi_codec_buffer_info, +}; + +static void sunxi_codec_dma_done_work(struct rt_work *work, void *work_data) +{ + struct sunxi_codec *codec = work_data; + + RT_UNUSED(work); + + if (codec->running) + { + rt_audio_tx_complete(&codec->parent); + } +} + +static void sunxi_codec_dma_done(struct rt_dma_chan *chan, rt_size_t size) +{ + struct sunxi_codec *codec = chan->priv; + + if (codec->running && size) + { + rt_work_submit(&codec->dma_done_work, 0); + } +} + +static void sunxi_codec_release(struct sunxi_codec *codec) +{ + codec->running = RT_FALSE; + rt_work_cancel(&codec->dma_done_work); + if (!rt_is_err_or_null(codec->dma_tx)) + { + rt_dma_chan_stop(codec->dma_tx); + rt_dma_chan_release(codec->dma_tx); + } + if (codec->playback_buffer) + { + rt_dma_free_coherent(codec->dev, SUNXI_CODEC_BUFFER_SIZE, + codec->playback_buffer, codec->playback_dma); + } + if (!rt_is_err_or_null(codec->reset)) + { + rt_reset_control_assert(codec->reset); + rt_reset_control_put(codec->reset); + } + if (!rt_is_err_or_null(codec->clks)) + { + if (codec->clks_enabled) + { + rt_clk_array_disable_unprepare(codec->clks); + } + rt_clk_array_put(codec->clks); + } + if (codec->base) + { + rt_iounmap(codec->base); + } + rt_free(codec); +} + +static rt_err_t sunxi_codec_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *name; + struct rt_device *dev = &pdev->parent; + struct sunxi_codec *codec = rt_calloc(1, sizeof(*codec)); + + if (!codec) + { + return -RT_ENOMEM; + } + + rt_work_init(&codec->dma_done_work, sunxi_codec_dma_done_work, codec); + + codec->dev = dev; + codec->spk_enable_pin = PIN_NONE; + codec->base = rt_dm_dev_iomap(dev, 0); + if (!codec->base) + { + err = -RT_EIO; + goto _fail; + } + + codec->clks = rt_clk_get_array(dev); + codec->reset = rt_reset_control_get_by_name(dev, "bus"); + if (rt_is_err(codec->clks) || rt_is_err(codec->reset)) + { + err = rt_is_err(codec->clks) ? rt_ptr_err(codec->clks) : + rt_ptr_err(codec->reset); + goto _fail; + } + if ((err = rt_clk_array_prepare_enable(codec->clks))) + { + goto _fail; + } + codec->clks_enabled = RT_TRUE; + if ((err = rt_reset_control_deassert(codec->reset))) + { + goto _fail; + } + + codec->spk_enable_pin = rt_pin_get_named_pin(dev, "spk-enable", 0, + RT_NULL, &codec->spk_enable_active); + if (codec->spk_enable_pin >= 0) + { + rt_pin_mode(codec->spk_enable_pin, PIN_MODE_OUTPUT); + rt_pin_write(codec->spk_enable_pin, !codec->spk_enable_active); + } + rt_dm_dev_prop_read_u32(dev, "allwinner,spk-enable-delay-ms", + &codec->spk_enable_delay_ms); + + codec->playback_buffer = rt_dma_alloc_coherent(dev, + SUNXI_CODEC_BUFFER_SIZE, &codec->playback_dma); + if (!codec->playback_buffer) + { + err = -RT_ENOMEM; + goto _fail; + } + + codec->dma_tx = rt_dma_chan_request(dev, "tx"); + if (rt_is_err(codec->dma_tx)) + { + err = rt_ptr_err(codec->dma_tx); + LOG_E("request TX DMA channel failed: %s", rt_strerror(err)); + goto _fail; + } + codec->dma_tx->callback = sunxi_codec_dma_done; + codec->dma_tx->priv = codec; + codec->fifo_dma = (rt_ubase_t)rt_kmem_v2p(codec->base) + + SUNXI_CODEC_DAC_TXDATA; + + rt_dm_dev_set_name_auto(&codec->parent.parent, "sound"); + name = rt_dm_dev_get_name(&codec->parent.parent); + codec->parent.ops = (struct rt_audio_ops *)&sunxi_codec_ops; + if ((err = rt_audio_register(&codec->parent, name, + RT_DEVICE_FLAG_WRONLY, codec))) + { + goto _fail; + } + + dev->user_data = codec; + return RT_EOK; + +_fail: + LOG_E("probe failed: %s", rt_strerror(err)); + sunxi_codec_release(codec); + return err; +} + +static rt_err_t sunxi_codec_remove(struct rt_platform_device *pdev) +{ + struct sunxi_codec *codec = pdev->parent.user_data; + + sunxi_codec_stop(&codec->parent, AUDIO_STREAM_REPLAY); + rt_device_unregister(&codec->parent.parent); + sunxi_codec_release(codec); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_codec_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-codec" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_codec_driver = +{ + .name = "sunxi-codec", + .ids = sunxi_codec_ofw_ids, + .probe = sunxi_codec_probe, + .remove = sunxi_codec_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_codec_driver); diff --git a/bsp/allwinner/dm/audio/audio-sunxi-i2s.c b/bsp/allwinner/dm/audio/audio-sunxi-i2s.c new file mode 100644 index 000000000000..905ba79121dc --- /dev/null +++ b/bsp/allwinner/dm/audio/audio-sunxi-i2s.c @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "audio.sunxi.i2s" +#define DBG_LVL DBG_INFO +#include + +#include "audio-sunxi.h" + +#define SUNXI_I2S_CTL 0x000 +#define SUNXI_I2S_FMT0 0x004 +#define SUNXI_I2S_FMT1 0x008 +#define SUNXI_I2S_INTSTA 0x00c +#define SUNXI_I2S_FIFOCTL 0x014 +#define SUNXI_I2S_INTCTL 0x01c +#define SUNXI_I2S_TXFIFO 0x020 +#define SUNXI_I2S_CLKDIV 0x024 +#define SUNXI_I2S_TXCNT 0x028 +#define SUNXI_I2S_CHCFG 0x030 +#define SUNXI_I2S_TX0CHSEL 0x034 +#define SUNXI_I2S_TX0CHMAP0 0x044 +#define SUNXI_I2S_TX0CHMAP1 0x048 + +#define SUNXI_I2S_CTL_BCLK_OUT RT_BIT(18) +#define SUNXI_I2S_CTL_LRCK_OUT RT_BIT(17) +#define SUNXI_I2S_CTL_MODE_I2S RT_BIT(4) +#define SUNXI_I2S_CTL_TX_EN RT_BIT(2) +#define SUNXI_I2S_CTL_GLOBAL_EN RT_BIT(0) +#define SUNXI_I2S_CTL_SDO0_EN RT_BIT(8) + +#define SUNXI_I2S_FMT0_LRCK_PER_MASK RT_GENMASK(17, 8) +#define SUNXI_I2S_FMT0_SAMPLE_RES_MASK RT_GENMASK(6, 4) +#define SUNXI_I2S_FMT0_SLOT_WIDTH_MASK RT_GENMASK(2, 0) +#define SUNXI_I2S_FIFOCTL_FTX RT_BIT(25) +#define SUNXI_I2S_FIFOCTL_TXIM RT_BIT(2) +#define SUNXI_I2S_INTCTL_TXDRQ_EN RT_BIT(7) +#define SUNXI_I2S_CLKDIV_MCLKOUT_EN RT_BIT(8) +#define SUNXI_I2S_CLKDIV_BCLK_DIV_MASK RT_GENMASK(7, 4) +#define SUNXI_I2S_CLKDIV_MCLK_DIV_MASK RT_GENMASK(3, 0) +#define SUNXI_I2S_CHCFG_TX_SLOT_MASK RT_GENMASK(3, 0) +#define SUNXI_I2S_TX0CHSEL_OFFSET_MASK RT_GENMASK(21, 20) +#define SUNXI_I2S_TX0CHSEL_CHSEL_MASK RT_GENMASK(19, 16) +#define SUNXI_I2S_TX0CHSEL_CHEN_MASK RT_GENMASK(15, 0) + +struct sunxi_i2s_rate +{ + rt_uint32_t rate; + rt_uint8_t bclk_div; + rt_uint8_t mclk_div; +}; + +struct sunxi_i2s +{ + struct sunxi_audio_output output; + void *base; + struct rt_clk_array *clks; + struct rt_reset_control *reset; + rt_bool_t clks_enabled; +}; + +#define to_sunxi_i2s(raw) \ + rt_container_of(raw, struct sunxi_i2s, output) + +static const struct sunxi_i2s_rate sunxi_i2s_rates[] = +{ + { 8000, 10, 6 }, + { 12000, 9, 5 }, + { 16000, 8, 4 }, + { 24000, 7, 3 }, + { 32000, 6, 2 }, + { 48000, 5, 2 }, + { 96000, 3, 1 }, + { 192000, 2, 0 }, +}; + +static rt_uint32_t sunxi_i2s_readl(struct sunxi_i2s *i2s, + rt_uint32_t offset) +{ + return HWREG32(i2s->base + offset); +} + +static void sunxi_i2s_writel(struct sunxi_i2s *i2s, rt_uint32_t value, + rt_uint32_t offset) +{ + HWREG32(i2s->base + offset) = value; +} + +static void sunxi_i2s_update_bits(struct sunxi_i2s *i2s, + rt_uint32_t offset, rt_uint32_t mask, rt_uint32_t value) +{ + rt_uint32_t reg = sunxi_i2s_readl(i2s, offset); + + reg &= ~mask; + reg |= value & mask; + sunxi_i2s_writel(i2s, reg, offset); +} + +static const struct sunxi_i2s_rate *sunxi_i2s_find_rate(rt_uint32_t rate) +{ + rt_size_t index; + + for (index = 0; index < RT_ARRAY_SIZE(sunxi_i2s_rates); ++index) + { + if (sunxi_i2s_rates[index].rate == rate) + { + return &sunxi_i2s_rates[index]; + } + } + + return RT_NULL; +} + +static rt_err_t sunxi_i2s_configure(struct sunxi_audio_output *output, + const struct rt_audio_configure *config) +{ + rt_uint32_t sample_resolution; + rt_uint32_t fmt0; + rt_uint32_t clkdiv; + const struct sunxi_i2s_rate *rate; + struct sunxi_i2s *i2s = to_sunxi_i2s(output); + + if (!config->channels || config->channels > 2) + { + return -RT_EINVAL; + } + + switch (config->samplebits) + { + case 16: + sample_resolution = 3; + break; + case 24: + sample_resolution = 5; + break; + case 32: + sample_resolution = 7; + break; + default: + return -RT_EINVAL; + } + + rate = sunxi_i2s_find_rate(config->samplerate); + if (!rate) + { + return -RT_ENOSYS; + } + + fmt0 = (63 << 8) | (sample_resolution << 4) | 7; + sunxi_i2s_writel(i2s, fmt0, SUNXI_I2S_FMT0); + sunxi_i2s_writel(i2s, 0, SUNXI_I2S_FMT1); + sunxi_i2s_writel(i2s, config->channels - 1, SUNXI_I2S_CHCFG); + sunxi_i2s_writel(i2s, (1 << 20) | (1 << 16) | 0x3, + SUNXI_I2S_TX0CHSEL); + sunxi_i2s_writel(i2s, 0xfedcba98, SUNXI_I2S_TX0CHMAP0); + sunxi_i2s_writel(i2s, 0x76543210, SUNXI_I2S_TX0CHMAP1); + + clkdiv = rate->bclk_div << 4; + if (rate->mclk_div) + { + clkdiv |= SUNXI_I2S_CLKDIV_MCLKOUT_EN | rate->mclk_div; + } + sunxi_i2s_writel(i2s, clkdiv, SUNXI_I2S_CLKDIV); + sunxi_i2s_update_bits(i2s, SUNXI_I2S_FIFOCTL, + SUNXI_I2S_FIFOCTL_TXIM, config->samplebits == 16 ? + SUNXI_I2S_FIFOCTL_TXIM : 0); + + return RT_EOK; +} + +static rt_err_t sunxi_i2s_init(struct sunxi_audio_output *output) +{ + struct sunxi_i2s *i2s = to_sunxi_i2s(output); + + sunxi_i2s_writel(i2s, 0, SUNXI_I2S_CTL); + sunxi_i2s_writel(i2s, 0, SUNXI_I2S_INTCTL); + + return RT_EOK; +} + +static rt_err_t sunxi_i2s_start(struct sunxi_audio_output *output) +{ + struct sunxi_i2s *i2s = to_sunxi_i2s(output); + + sunxi_i2s_update_bits(i2s, SUNXI_I2S_FIFOCTL, + SUNXI_I2S_FIFOCTL_FTX, SUNXI_I2S_FIFOCTL_FTX); + sunxi_i2s_writel(i2s, 0, SUNXI_I2S_TXCNT); + sunxi_i2s_writel(i2s, RT_BIT(6) | RT_BIT(5) | RT_BIT(4), + SUNXI_I2S_INTSTA); + sunxi_i2s_update_bits(i2s, SUNXI_I2S_CTL, + SUNXI_I2S_CTL_GLOBAL_EN | SUNXI_I2S_CTL_TX_EN | + SUNXI_I2S_CTL_SDO0_EN | SUNXI_I2S_CTL_BCLK_OUT | + SUNXI_I2S_CTL_LRCK_OUT | SUNXI_I2S_CTL_MODE_I2S, + SUNXI_I2S_CTL_GLOBAL_EN | SUNXI_I2S_CTL_TX_EN | + SUNXI_I2S_CTL_SDO0_EN | SUNXI_I2S_CTL_BCLK_OUT | + SUNXI_I2S_CTL_LRCK_OUT | SUNXI_I2S_CTL_MODE_I2S); + + return RT_EOK; +} + +static void sunxi_i2s_stop(struct sunxi_audio_output *output) +{ + struct sunxi_i2s *i2s = to_sunxi_i2s(output); + + sunxi_i2s_update_bits(i2s, SUNXI_I2S_INTCTL, + SUNXI_I2S_INTCTL_TXDRQ_EN, 0); + sunxi_i2s_update_bits(i2s, SUNXI_I2S_CTL, + SUNXI_I2S_CTL_TX_EN | SUNXI_I2S_CTL_SDO0_EN | + SUNXI_I2S_CTL_GLOBAL_EN, 0); +} + +static void sunxi_i2s_enable_dma(struct sunxi_audio_output *output, + rt_bool_t enable) +{ + struct sunxi_i2s *i2s = to_sunxi_i2s(output); + + sunxi_i2s_update_bits(i2s, SUNXI_I2S_INTCTL, + SUNXI_I2S_INTCTL_TXDRQ_EN, + enable ? SUNXI_I2S_INTCTL_TXDRQ_EN : 0); +} + +static const struct sunxi_audio_output_ops sunxi_i2s_output_ops = +{ + .init = sunxi_i2s_init, + .configure = sunxi_i2s_configure, + .start = sunxi_i2s_start, + .stop = sunxi_i2s_stop, + .enable_dma = sunxi_i2s_enable_dma, +}; + +static void sunxi_i2s_release(struct sunxi_i2s *i2s) +{ + sunxi_audio_output_release(&i2s->output); + if (!rt_is_err_or_null(i2s->reset)) + { + rt_reset_control_assert(i2s->reset); + rt_reset_control_put(i2s->reset); + } + if (!rt_is_err_or_null(i2s->clks)) + { + if (i2s->clks_enabled) + { + rt_clk_array_disable_unprepare(i2s->clks); + } + rt_clk_array_put(i2s->clks); + } + if (i2s->base) + { + rt_iounmap(i2s->base); + } + rt_free(i2s); +} + +static rt_err_t sunxi_i2s_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct sunxi_i2s *i2s = rt_calloc(1, sizeof(*i2s)); + + if (!i2s) + { + return -RT_ENOMEM; + } + + i2s->output.dev = dev; + i2s->base = rt_dm_dev_iomap(dev, 0); + if (!i2s->base) + { + err = -RT_EIO; + goto _fail; + } + i2s->clks = rt_clk_get_array(dev); + i2s->reset = rt_reset_control_get_by_name(dev, "bus"); + if (rt_is_err(i2s->clks) || rt_is_err(i2s->reset)) + { + err = rt_is_err(i2s->clks) ? rt_ptr_err(i2s->clks) : + rt_ptr_err(i2s->reset); + goto _fail; + } + err = rt_clk_array_prepare_enable(i2s->clks); + if (err) + { + goto _fail; + } + i2s->clks_enabled = RT_TRUE; + err = rt_reset_control_deassert(i2s->reset); + if (err) + { + goto _fail; + } + + i2s->output.config.samplerate = 48000; + i2s->output.config.channels = 2; + i2s->output.config.samplebits = 16; + i2s->output.ops = &sunxi_i2s_output_ops; + i2s->output.playback_buffer = rt_dma_alloc_coherent(dev, + SUNXI_AUDIO_BUFFER_SIZE, &i2s->output.playback_dma); + if (!i2s->output.playback_buffer) + { + err = -RT_ENOMEM; + goto _fail; + } + i2s->output.dma_tx = rt_dma_chan_request(dev, "tx"); + if (rt_is_err(i2s->output.dma_tx)) + { + err = rt_ptr_err(i2s->output.dma_tx); + goto _fail; + } + i2s->output.dma_tx->callback = sunxi_audio_output_dma_done; + i2s->output.dma_tx->priv = &i2s->output; + i2s->output.fifo_dma = (rt_ubase_t)rt_kmem_v2p(i2s->base) + + SUNXI_I2S_TXFIFO; + + err = sunxi_audio_output_register(&i2s->output, "sound", i2s); + if (err) + { + goto _fail; + } + dev->user_data = i2s; + return RT_EOK; + +_fail: + sunxi_i2s_release(i2s); + return err; +} + +static rt_err_t sunxi_i2s_remove(struct rt_platform_device *pdev) +{ + struct sunxi_i2s *i2s = pdev->parent.user_data; + + sunxi_audio_output_stop(&i2s->output); + rt_device_unregister(&i2s->output.parent.parent); + sunxi_i2s_release(i2s); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_i2s_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-i2s" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_i2s_driver = +{ + .name = "sunxi-i2s", + .ids = sunxi_i2s_ofw_ids, + .probe = sunxi_i2s_probe, + .remove = sunxi_i2s_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_i2s_driver); diff --git a/bsp/allwinner/dm/audio/audio-sunxi-owa.c b/bsp/allwinner/dm/audio/audio-sunxi-owa.c new file mode 100644 index 000000000000..a5de4b4e497c --- /dev/null +++ b/bsp/allwinner/dm/audio/audio-sunxi-owa.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "audio.sunxi.owa" +#define DBG_LVL DBG_INFO +#include + +#include "audio-sunxi.h" + +#define SUNXI_OWA_CTL 0x000 +#define SUNXI_OWA_TXCFG 0x004 +#define SUNXI_OWA_INT_STA 0x00c +#define SUNXI_OWA_FIFO_CTL 0x014 +#define SUNXI_OWA_INT 0x01c +#define SUNXI_OWA_TXFIFO 0x020 +#define SUNXI_OWA_TXCNT 0x024 +#define SUNXI_OWA_TXCH_STA0 0x02c +#define SUNXI_OWA_TXCH_STA1 0x030 + +#define SUNXI_OWA_CTL_RESET RT_BIT(0) +#define SUNXI_OWA_CTL_GEN_EN RT_BIT(1) +#define SUNXI_OWA_TXCFG_TX_EN RT_BIT(0) +#define SUNXI_OWA_TXCFG_CHAN_STA RT_BIT(1) +#define SUNXI_OWA_TXCFG_BITS_MASK RT_GENMASK(3, 2) +#define SUNXI_OWA_TXCFG_CLKDIV_MASK RT_GENMASK(8, 4) +#define SUNXI_OWA_TXCFG_SINGLE RT_BIT(31) +#define SUNXI_OWA_FIFO_CTL_TXIM RT_BIT(2) +#define SUNXI_OWA_FIFO_CTL_FTX RT_BIT(30) +#define SUNXI_OWA_INT_TXDRQ_EN RT_BIT(7) +#define SUNXI_OWA_TXCH_STA0_SAMFREQ_MASK RT_GENMASK(27, 24) +#define SUNXI_OWA_TXCH_STA0_CHNUM_MASK RT_GENMASK(23, 20) +#define SUNXI_OWA_TXCH_STA1_WORDLEN_MASK RT_GENMASK(3, 0) +#define SUNXI_OWA_TXCH_STA1_ORIGFREQ_MASK RT_GENMASK(7, 4) + +struct sunxi_owa_rate +{ + rt_uint32_t rate; + rt_uint8_t divider; + rt_uint8_t sample_frequency; + rt_uint8_t original_frequency; +}; + +struct sunxi_owa +{ + struct sunxi_audio_output output; + void *base; + struct rt_clk_array *clks; + struct rt_reset_control *reset; + rt_bool_t clks_enabled; +}; + +#define to_sunxi_owa(raw) \ + rt_container_of(raw, struct sunxi_owa, output) + +static const struct sunxi_owa_rate sunxi_owa_rates[] = +{ + { 8000, 23, 0, 0 }, + { 12000, 15, 0, 0 }, + { 16000, 11, 0, 0 }, + { 24000, 7, 6, 9 }, + { 32000, 5, 3, 12 }, + { 48000, 3, 2, 13 }, + { 96000, 1, 10, 5 }, + { 192000, 0, 14, 1 }, +}; + +static rt_uint32_t sunxi_owa_readl(struct sunxi_owa *owa, + rt_uint32_t offset) +{ + return HWREG32(owa->base + offset); +} + +static void sunxi_owa_writel(struct sunxi_owa *owa, rt_uint32_t value, + rt_uint32_t offset) +{ + HWREG32(owa->base + offset) = value; +} + +static void sunxi_owa_update_bits(struct sunxi_owa *owa, + rt_uint32_t offset, rt_uint32_t mask, rt_uint32_t value) +{ + rt_uint32_t reg = sunxi_owa_readl(owa, offset); + + reg &= ~mask; + reg |= value & mask; + sunxi_owa_writel(owa, reg, offset); +} + +static const struct sunxi_owa_rate *sunxi_owa_find_rate(rt_uint32_t rate) +{ + rt_size_t index; + + for (index = 0; index < RT_ARRAY_SIZE(sunxi_owa_rates); ++index) + { + if (sunxi_owa_rates[index].rate == rate) + { + return &sunxi_owa_rates[index]; + } + } + + return RT_NULL; +} + +static rt_err_t sunxi_owa_configure(struct sunxi_audio_output *output, + const struct rt_audio_configure *config) +{ + rt_uint32_t sample_bits; + rt_uint32_t word_length; + const struct sunxi_owa_rate *rate; + struct sunxi_owa *owa = to_sunxi_owa(output); + + if (!config->channels || config->channels > 2) + { + return -RT_EINVAL; + } + + switch (config->samplebits) + { + case 16: + sample_bits = 0; + word_length = 2; + break; + case 24: + case 32: + sample_bits = 2; + word_length = 11; + break; + default: + return -RT_EINVAL; + } + + rate = sunxi_owa_find_rate(config->samplerate); + if (!rate) + { + return -RT_ENOSYS; + } + + sunxi_owa_update_bits(owa, SUNXI_OWA_TXCFG, + SUNXI_OWA_TXCFG_BITS_MASK | SUNXI_OWA_TXCFG_CLKDIV_MASK | + SUNXI_OWA_TXCFG_SINGLE, + (sample_bits << 2) | (rate->divider << 4) | + (config->channels == 1 ? SUNXI_OWA_TXCFG_SINGLE : 0)); + sunxi_owa_update_bits(owa, SUNXI_OWA_FIFO_CTL, + SUNXI_OWA_FIFO_CTL_TXIM, + config->samplebits == 16 ? SUNXI_OWA_FIFO_CTL_TXIM : 0); + sunxi_owa_update_bits(owa, SUNXI_OWA_TXCH_STA0, + SUNXI_OWA_TXCH_STA0_SAMFREQ_MASK | + SUNXI_OWA_TXCH_STA0_CHNUM_MASK, + (rate->sample_frequency << 24) | (2 << 20)); + sunxi_owa_update_bits(owa, SUNXI_OWA_TXCH_STA1, + SUNXI_OWA_TXCH_STA1_WORDLEN_MASK | + SUNXI_OWA_TXCH_STA1_ORIGFREQ_MASK, + word_length | (rate->original_frequency << 4)); + + return RT_EOK; +} + +static rt_err_t sunxi_owa_init(struct sunxi_audio_output *output) +{ + struct sunxi_owa *owa = to_sunxi_owa(output); + + sunxi_owa_writel(owa, 0, SUNXI_OWA_INT); + sunxi_owa_update_bits(owa, SUNXI_OWA_FIFO_CTL, RT_GENMASK(19, 12), + 0x40 << 12); + sunxi_owa_update_bits(owa, SUNXI_OWA_TXCFG, + SUNXI_OWA_TXCFG_CHAN_STA, SUNXI_OWA_TXCFG_CHAN_STA); + + return RT_EOK; +} + +static rt_err_t sunxi_owa_start(struct sunxi_audio_output *output) +{ + struct sunxi_owa *owa = to_sunxi_owa(output); + + sunxi_owa_update_bits(owa, SUNXI_OWA_CTL, + SUNXI_OWA_CTL_GEN_EN, 0); + sunxi_owa_update_bits(owa, SUNXI_OWA_FIFO_CTL, + SUNXI_OWA_FIFO_CTL_FTX, SUNXI_OWA_FIFO_CTL_FTX); + sunxi_owa_writel(owa, 0, SUNXI_OWA_TXCNT); + sunxi_owa_writel(owa, RT_BIT(6) | RT_BIT(5) | RT_BIT(4), + SUNXI_OWA_INT_STA); + sunxi_owa_update_bits(owa, SUNXI_OWA_CTL, + SUNXI_OWA_CTL_RESET | SUNXI_OWA_CTL_GEN_EN, + SUNXI_OWA_CTL_RESET | SUNXI_OWA_CTL_GEN_EN); + sunxi_owa_update_bits(owa, SUNXI_OWA_TXCFG, + SUNXI_OWA_TXCFG_TX_EN, SUNXI_OWA_TXCFG_TX_EN); + + return RT_EOK; +} + +static void sunxi_owa_stop(struct sunxi_audio_output *output) +{ + struct sunxi_owa *owa = to_sunxi_owa(output); + + sunxi_owa_update_bits(owa, SUNXI_OWA_INT, + SUNXI_OWA_INT_TXDRQ_EN, 0); + sunxi_owa_update_bits(owa, SUNXI_OWA_TXCFG, + SUNXI_OWA_TXCFG_TX_EN, 0); + sunxi_owa_update_bits(owa, SUNXI_OWA_CTL, + SUNXI_OWA_CTL_GEN_EN, 0); +} + +static void sunxi_owa_enable_dma(struct sunxi_audio_output *output, + rt_bool_t enable) +{ + struct sunxi_owa *owa = to_sunxi_owa(output); + + sunxi_owa_update_bits(owa, SUNXI_OWA_INT, + SUNXI_OWA_INT_TXDRQ_EN, + enable ? SUNXI_OWA_INT_TXDRQ_EN : 0); +} + +static const struct sunxi_audio_output_ops sunxi_owa_output_ops = +{ + .init = sunxi_owa_init, + .configure = sunxi_owa_configure, + .start = sunxi_owa_start, + .stop = sunxi_owa_stop, + .enable_dma = sunxi_owa_enable_dma, +}; + +static void sunxi_owa_release(struct sunxi_owa *owa) +{ + sunxi_audio_output_release(&owa->output); + if (!rt_is_err_or_null(owa->reset)) + { + rt_reset_control_assert(owa->reset); + rt_reset_control_put(owa->reset); + } + if (!rt_is_err_or_null(owa->clks)) + { + if (owa->clks_enabled) + { + rt_clk_array_disable_unprepare(owa->clks); + } + rt_clk_array_put(owa->clks); + } + if (owa->base) + { + rt_iounmap(owa->base); + } + rt_free(owa); +} + +static rt_err_t sunxi_owa_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct sunxi_owa *owa = rt_calloc(1, sizeof(*owa)); + + if (!owa) + { + return -RT_ENOMEM; + } + + owa->output.dev = dev; + owa->base = rt_dm_dev_iomap(dev, 0); + if (!owa->base) + { + err = -RT_EIO; + goto _fail; + } + owa->clks = rt_clk_get_array(dev); + owa->reset = rt_reset_control_get_by_name(dev, "bus"); + if (rt_is_err(owa->clks) || rt_is_err(owa->reset)) + { + err = rt_is_err(owa->clks) ? rt_ptr_err(owa->clks) : + rt_ptr_err(owa->reset); + goto _fail; + } + err = rt_clk_array_prepare_enable(owa->clks); + if (err) + { + goto _fail; + } + owa->clks_enabled = RT_TRUE; + err = rt_reset_control_deassert(owa->reset); + if (err) + { + goto _fail; + } + + owa->output.config.samplerate = 48000; + owa->output.config.channels = 2; + owa->output.config.samplebits = 16; + owa->output.ops = &sunxi_owa_output_ops; + owa->output.playback_buffer = rt_dma_alloc_coherent(dev, + SUNXI_AUDIO_BUFFER_SIZE, &owa->output.playback_dma); + if (!owa->output.playback_buffer) + { + err = -RT_ENOMEM; + goto _fail; + } + owa->output.dma_tx = rt_dma_chan_request(dev, "tx"); + if (rt_is_err(owa->output.dma_tx)) + { + err = rt_ptr_err(owa->output.dma_tx); + goto _fail; + } + owa->output.dma_tx->callback = sunxi_audio_output_dma_done; + owa->output.dma_tx->priv = &owa->output; + owa->output.fifo_dma = (rt_ubase_t)rt_kmem_v2p(owa->base) + + SUNXI_OWA_TXFIFO; + + err = sunxi_audio_output_register(&owa->output, "sound", owa); + if (err) + { + goto _fail; + } + dev->user_data = owa; + return RT_EOK; + +_fail: + sunxi_owa_release(owa); + return err; +} + +static rt_err_t sunxi_owa_remove(struct rt_platform_device *pdev) +{ + struct sunxi_owa *owa = pdev->parent.user_data; + + sunxi_audio_output_stop(&owa->output); + rt_device_unregister(&owa->output.parent.parent); + sunxi_owa_release(owa); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_owa_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-owa" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_owa_driver = +{ + .name = "sunxi-owa", + .ids = sunxi_owa_ofw_ids, + .probe = sunxi_owa_probe, + .remove = sunxi_owa_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_owa_driver); diff --git a/bsp/allwinner/dm/audio/audio-sunxi.c b/bsp/allwinner/dm/audio/audio-sunxi.c new file mode 100644 index 000000000000..5741d76b70ef --- /dev/null +++ b/bsp/allwinner/dm/audio/audio-sunxi.c @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "audio-sunxi.h" + +static rt_err_t sunxi_audio_output_getcaps(struct rt_audio_device *audio, + struct rt_audio_caps *caps) +{ + struct sunxi_audio_output *output = to_sunxi_audio_output(audio); + + switch (caps->main_type) + { + case AUDIO_TYPE_QUERY: + if (caps->sub_type != AUDIO_TYPE_QUERY) + { + return -RT_ENOSYS; + } + caps->udata.mask = AUDIO_TYPE_OUTPUT; + return RT_EOK; + + case AUDIO_TYPE_OUTPUT: + switch (caps->sub_type) + { + case AUDIO_DSP_PARAM: + caps->udata.config = output->config; + return RT_EOK; + case AUDIO_DSP_SAMPLERATE: + caps->udata.config.samplerate = output->config.samplerate; + return RT_EOK; + case AUDIO_DSP_CHANNELS: + caps->udata.config.channels = output->config.channels; + return RT_EOK; + case AUDIO_DSP_SAMPLEBITS: + caps->udata.config.samplebits = output->config.samplebits; + return RT_EOK; + default: + return -RT_ENOSYS; + } + + default: + return -RT_ENOSYS; + } +} + +static void sunxi_audio_output_dma_done_work(struct rt_work *work, + void *work_data) +{ + struct sunxi_audio_output *output = work_data; + + RT_UNUSED(work); + + if (output->running) + { + rt_audio_tx_complete(&output->parent); + } +} + +static rt_err_t sunxi_audio_output_configure(struct rt_audio_device *audio, + struct rt_audio_caps *caps) +{ + rt_err_t err; + struct rt_audio_configure config; + struct sunxi_audio_output *output = to_sunxi_audio_output(audio); + + if (output->running) + { + return -RT_EBUSY; + } + if (caps->main_type != AUDIO_TYPE_OUTPUT) + { + return -RT_ENOSYS; + } + + config = output->config; + switch (caps->sub_type) + { + case AUDIO_DSP_PARAM: + config = caps->udata.config; + break; + case AUDIO_DSP_SAMPLERATE: + config.samplerate = caps->udata.config.samplerate; + break; + case AUDIO_DSP_CHANNELS: + config.channels = caps->udata.config.channels; + break; + case AUDIO_DSP_SAMPLEBITS: + config.samplebits = caps->udata.config.samplebits; + break; + default: + return -RT_ENOSYS; + } + + err = output->ops->configure(output, &config); + if (!err) + { + output->config = config; + } + + return err; +} + +static rt_err_t sunxi_audio_output_init(struct rt_audio_device *audio) +{ + rt_err_t err; + struct sunxi_audio_output *output = to_sunxi_audio_output(audio); + + if (output->ops->init) + { + err = output->ops->init(output); + if (err) + { + return err; + } + } + + return output->ops->configure(output, &output->config); +} + +static rt_err_t sunxi_audio_output_start(struct rt_audio_device *audio, + int stream) +{ + rt_err_t err; + struct sunxi_audio_output *output = to_sunxi_audio_output(audio); + + if (stream != AUDIO_STREAM_REPLAY) + { + return -RT_ENOSYS; + } + + err = output->ops->start(output); + if (err) + { + return err; + } + + output->running = RT_TRUE; + rt_audio_tx_complete(audio); + + return RT_EOK; +} + +void sunxi_audio_output_stop(struct sunxi_audio_output *output) +{ + output->running = RT_FALSE; + rt_dma_chan_stop(output->dma_tx); + rt_work_cancel(&output->dma_done_work); + output->ops->stop(output); +} + +static rt_err_t sunxi_audio_output_stop_device(struct rt_audio_device *audio, + int stream) +{ + struct sunxi_audio_output *output = to_sunxi_audio_output(audio); + + if (stream != AUDIO_STREAM_REPLAY) + { + return -RT_ENOSYS; + } + + sunxi_audio_output_stop(output); + + return RT_EOK; +} + +static rt_ssize_t sunxi_audio_output_transmit(struct rt_audio_device *audio, + const void *write_buf, void *read_buf, rt_size_t size) +{ + rt_err_t err; + rt_size_t width; + rt_ubase_t offset; + const rt_uint8_t *buffer = write_buf; + struct rt_dma_slave_config config; + struct rt_dma_slave_transfer transfer; + struct sunxi_audio_output *output = to_sunxi_audio_output(audio); + + if (!output->running || !write_buf || read_buf || !size || + size > SUNXI_AUDIO_BLOCK_SIZE) + { + return -RT_EINVAL; + } + if (buffer < output->playback_buffer || + buffer >= output->playback_buffer + SUNXI_AUDIO_BUFFER_SIZE) + { + return -RT_EINVAL; + } + + offset = buffer - output->playback_buffer; + if (offset + size > SUNXI_AUDIO_BUFFER_SIZE) + { + return -RT_EINVAL; + } + + width = output->config.samplebits == 16 ? 2 : 4; + if ((offset % width) || (size % width)) + { + return -RT_EINVAL; + } + + rt_memset(&config, 0, sizeof(config)); + config.direction = RT_DMA_MEM_TO_DEV; + config.src_addr_width = width == 2 ? RT_DMA_SLAVE_BUSWIDTH_2_BYTES : + RT_DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_addr_width = config.src_addr_width; + config.src_maxburst = 8; + config.dst_maxburst = 8; + config.src_addr = output->playback_dma + offset; + config.dst_addr = output->fifo_dma; + err = rt_dma_chan_config(output->dma_tx, &config); + if (err) + { + return err; + } + + rt_memset(&transfer, 0, sizeof(transfer)); + transfer.buffer = (void *)write_buf; + transfer.buffer_len = size; + transfer.src_addr = config.src_addr; + transfer.dst_addr = config.dst_addr; + err = rt_dma_prep_single(output->dma_tx, &transfer); + if (err) + { + return err; + } + err = rt_dma_chan_start(output->dma_tx); + if (err) + { + return err; + } + + output->ops->enable_dma(output, RT_TRUE); + return size; +} + +static void sunxi_audio_output_buffer_info(struct rt_audio_device *audio, + struct rt_audio_buf_info *info) +{ + struct sunxi_audio_output *output = to_sunxi_audio_output(audio); + + info->buffer = output->playback_buffer; + info->total_size = SUNXI_AUDIO_BUFFER_SIZE; + info->block_size = SUNXI_AUDIO_BLOCK_SIZE; + info->block_count = SUNXI_AUDIO_BUFFER_SIZE / SUNXI_AUDIO_BLOCK_SIZE; +} + +const struct rt_audio_ops sunxi_audio_output_audio_ops = +{ + .getcaps = sunxi_audio_output_getcaps, + .configure = sunxi_audio_output_configure, + .init = sunxi_audio_output_init, + .start = sunxi_audio_output_start, + .stop = sunxi_audio_output_stop_device, + .transmit = sunxi_audio_output_transmit, + .buffer_info = sunxi_audio_output_buffer_info, +}; + +rt_err_t sunxi_audio_output_register(struct sunxi_audio_output *output, + const char *prefix, void *data) +{ + const char *name; + + rt_dm_dev_set_name_auto(&output->parent.parent, prefix); + name = rt_dm_dev_get_name(&output->parent.parent); + if (!name) + { + return -RT_EINVAL; + } + + output->parent.ops = (struct rt_audio_ops *)&sunxi_audio_output_audio_ops; + rt_work_init(&output->dma_done_work, sunxi_audio_output_dma_done_work, + output); + output->dma_done_work_initialized = RT_TRUE; + + return rt_audio_register(&output->parent, name, RT_DEVICE_FLAG_WRONLY, + data); +} + +void sunxi_audio_output_dma_done(struct rt_dma_chan *chan, rt_size_t size) +{ + struct sunxi_audio_output *output = chan->priv; + + if (output->running && size) + { + rt_work_submit(&output->dma_done_work, 0); + } +} + +void sunxi_audio_output_release(struct sunxi_audio_output *output) +{ + output->running = RT_FALSE; + if (output->dma_done_work_initialized) + { + rt_work_cancel(&output->dma_done_work); + } + if (!rt_is_err_or_null(output->dma_tx)) + { + rt_dma_chan_stop(output->dma_tx); + rt_dma_chan_release(output->dma_tx); + output->dma_tx = RT_NULL; + } + if (output->playback_buffer) + { + rt_dma_free_coherent(output->dev, SUNXI_AUDIO_BUFFER_SIZE, + output->playback_buffer, output->playback_dma); + output->playback_buffer = RT_NULL; + } +} diff --git a/bsp/allwinner/dm/audio/audio-sunxi.h b/bsp/allwinner/dm/audio/audio-sunxi.h new file mode 100644 index 000000000000..8e552abb6b62 --- /dev/null +++ b/bsp/allwinner/dm/audio/audio-sunxi.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __AUDIO_SUNXI_H__ +#define __AUDIO_SUNXI_H__ + +#include +#include + +#define SUNXI_AUDIO_BUFFER_SIZE 16384 +#define SUNXI_AUDIO_BLOCK_SIZE 4096 + +struct sunxi_audio_output; + +struct sunxi_audio_output_ops +{ + rt_err_t (*init)(struct sunxi_audio_output *output); + rt_err_t (*configure)(struct sunxi_audio_output *output, + const struct rt_audio_configure *config); + rt_err_t (*start)(struct sunxi_audio_output *output); + void (*stop)(struct sunxi_audio_output *output); + void (*enable_dma)(struct sunxi_audio_output *output, rt_bool_t enable); +}; + +struct sunxi_audio_output +{ + struct rt_audio_device parent; + struct rt_device *dev; + struct rt_dma_chan *dma_tx; + rt_uint8_t *playback_buffer; + rt_ubase_t playback_dma; + rt_ubase_t fifo_dma; + struct rt_audio_configure config; + const struct sunxi_audio_output_ops *ops; + struct rt_work dma_done_work; + rt_bool_t running; + rt_bool_t dma_done_work_initialized; +}; + +#define to_sunxi_audio_output(raw) \ + rt_container_of(raw, struct sunxi_audio_output, parent) + +extern const struct rt_audio_ops sunxi_audio_output_audio_ops; + +rt_err_t sunxi_audio_output_register(struct sunxi_audio_output *output, + const char *prefix, void *data); +void sunxi_audio_output_stop(struct sunxi_audio_output *output); +void sunxi_audio_output_dma_done(struct rt_dma_chan *chan, rt_size_t size); +void sunxi_audio_output_release(struct sunxi_audio_output *output); + +#endif /* __AUDIO_SUNXI_H__ */ diff --git a/bsp/allwinner/dm/clk/Kconfig b/bsp/allwinner/dm/clk/Kconfig new file mode 100644 index 000000000000..7c5d9b6ddfb5 --- /dev/null +++ b/bsp/allwinner/dm/clk/Kconfig @@ -0,0 +1,10 @@ +config RT_CLK_SUN252I_F101 + bool "Allwinner Sun252i F101 clock/reset controller" + depends on RT_USING_CLK + depends on RT_USING_OFW + select RT_USING_RESET + select RT_RESET_SUN252I_F101 + default n + help + Provide the F101 CCU clock gates and reset controller used by the + UART, TWI, SPI, and SMHC peripheral blocks. diff --git a/bsp/allwinner/dm/clk/SConscript b/bsp/allwinner/dm/clk/SConscript new file mode 100644 index 000000000000..a7951d81bec8 --- /dev/null +++ b/bsp/allwinner/dm/clk/SConscript @@ -0,0 +1,13 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_CLK_SUN252I_F101']): + src += ['clk-sunxi.c', 'clk-sun252i-f101.c', 'clk-sun252i-f101-cpupll.c', + 'clk-sun252i-f101-rtc.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/clk/clk-sun252i-f101-cpupll.c b/bsp/allwinner/dm/clk/clk-sun252i-f101-cpupll.c new file mode 100644 index 000000000000..36b481a237bd --- /dev/null +++ b/bsp/allwinner/dm/clk/clk-sun252i-f101-cpupll.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#define SUN252I_F101_PLL_CPU_CTRL 0x0000 +#define SUN252I_F101_PLL_CPU_SSC 0x0200 +#define SUN252I_F101_RISCV_CLK 0x0d00 + +#define SUN252I_F101_PLL_CPU_N RT_GENMASK(15, 8) +#define SUN252I_F101_PLL_CPU_P RT_GENMASK(18, 16) +#define SUN252I_F101_PLL_CPU_M0 RT_GENMASK(21, 20) +#define SUN252I_F101_PLL_CPU_M1 RT_GENMASK(3, 0) +#define SUN252I_F101_PLL_CPU_UPDATE RT_BIT(26) +#define SUN252I_F101_PLL_CPU_LOCK RT_BIT(28) +#define SUN252I_F101_PLL_CPU_LOCK_EN RT_BIT(29) + +#define SUN252I_F101_RISCV_DIV RT_GENMASK(4, 0) +#define SUN252I_F101_RISCV_AXI_DIV RT_GENMASK(9, 8) +#define SUN252I_F101_RISCV_CLK_SEL RT_GENMASK(26, 24) +#define SUN252I_F101_RISCV_CLK_CPU_PLL 5 + +#define SUN252I_F101_OSC24M_RATE 24000000UL +#define SUN252I_F101_PLL_CPU_MIN_RATE 480000000UL +/* The C907 is specified at 1 GHz; 24 MHz PLL steps make this 1008 MHz. */ +#define SUN252I_F101_PLL_CPU_MAX_RATE 1008000000UL + +struct sun252i_f101_cpupll +{ + struct rt_clk_node parent; + struct rt_clk_cell clocks[CLK_CPUPLL_NUMBER]; + struct rt_clk_cell *cells[CLK_CPUPLL_NUMBER]; + struct rt_spinlock lock; + void *base; +}; + +static const char * const sun252i_f101_cpupll_clock_names[] = +{ + [CLK_PLL_CPU] = "pll-cpu", + [CLK_RISCV] = "riscv", + [CLK_RISCV_AXI] = "riscv-axi", +}; + +static rt_ubase_t sun252i_f101_cpupll_rate(struct sun252i_f101_cpupll *ccu) +{ + rt_uint32_t value = HWREG32(ccu->base + SUN252I_F101_PLL_CPU_CTRL); + rt_ubase_t rate = SUN252I_F101_OSC24M_RATE; + rt_uint32_t n = (value & SUN252I_F101_PLL_CPU_N) >> 8; + rt_uint32_t p = ((value & SUN252I_F101_PLL_CPU_P) >> 16) + 1; + rt_uint32_t m0 = ((value & SUN252I_F101_PLL_CPU_M0) >> 20) + 1; + rt_uint32_t m1 = (value & SUN252I_F101_PLL_CPU_M1) + 1; + + return rate * n / p / m0 / m1; +} + +static rt_ubase_t sun252i_f101_riscv_rate(struct sun252i_f101_cpupll *ccu) +{ + rt_uint32_t value = HWREG32(ccu->base + SUN252I_F101_RISCV_CLK); + rt_uint32_t source = (value & SUN252I_F101_RISCV_CLK_SEL) >> 24; + rt_ubase_t parent_rate; + + if (source != SUN252I_F101_RISCV_CLK_CPU_PLL) + { + return 0; + } + + parent_rate = sun252i_f101_cpupll_rate(ccu); + return parent_rate / ((value & SUN252I_F101_RISCV_DIV) + 1); +} + +static rt_ubase_t sun252i_f101_cpupll_recalc_rate( + struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + struct sun252i_f101_cpupll *ccu = cell->priv; + + RT_UNUSED(parent_rate); + + if (cell == &ccu->clocks[CLK_PLL_CPU]) + { + return sun252i_f101_cpupll_rate(ccu); + } + + if (cell == &ccu->clocks[CLK_RISCV]) + { + return sun252i_f101_riscv_rate(ccu); + } + + return sun252i_f101_riscv_rate(ccu) / + (((HWREG32(ccu->base + SUN252I_F101_RISCV_CLK) & + SUN252I_F101_RISCV_AXI_DIV) >> 8) + 1); +} + +static rt_base_t sun252i_f101_cpupll_round_rate( + struct rt_clk_cell *cell, rt_ubase_t rate, rt_ubase_t *parent_rate) +{ + struct sun252i_f101_cpupll *ccu = cell->priv; + rt_uint32_t cpu_div = 1; + rt_ubase_t pll_rate; + + RT_UNUSED(parent_rate); + + if (cell == &ccu->clocks[CLK_RISCV]) + { + cpu_div = (HWREG32(ccu->base + SUN252I_F101_RISCV_CLK) & + SUN252I_F101_RISCV_DIV) + 1; + } + else if (cell == &ccu->clocks[CLK_RISCV_AXI]) + { + return -RT_ENOSYS; + } + + pll_rate = rt_clamp(rate * cpu_div, + SUN252I_F101_PLL_CPU_MIN_RATE, SUN252I_F101_PLL_CPU_MAX_RATE); + pll_rate = (pll_rate / SUN252I_F101_OSC24M_RATE) * SUN252I_F101_OSC24M_RATE; + + return (rt_base_t)(pll_rate / cpu_div); +} + +static rt_err_t sun252i_f101_cpupll_wait_update(struct sun252i_f101_cpupll *ccu) +{ + for (rt_uint32_t timeout = 1000; timeout; --timeout) + { + if (!(HWREG32(ccu->base + SUN252I_F101_PLL_CPU_CTRL) & + SUN252I_F101_PLL_CPU_UPDATE)) + { + return RT_EOK; + } + + rt_hw_us_delay(1); + } + + return -RT_ETIMEOUT; +} + +static rt_err_t sun252i_f101_cpupll_wait_lock(struct sun252i_f101_cpupll *ccu) +{ + rt_uint32_t locked = 0; + + for (rt_uint32_t timeout = 1000; timeout; --timeout) + { + if (HWREG32(ccu->base + SUN252I_F101_PLL_CPU_CTRL) & + SUN252I_F101_PLL_CPU_LOCK) + { + if (++locked == 3) + { + return RT_EOK; + } + } + else + { + locked = 0; + } + + rt_hw_us_delay(3); + } + + return -RT_ETIMEOUT; +} + +static rt_err_t sun252i_f101_cpupll_set_rate(struct rt_clk_cell *cell, + rt_ubase_t rate, rt_ubase_t parent_rate) +{ + struct sun252i_f101_cpupll *ccu = cell->priv; + rt_uint32_t value; + rt_uint32_t divider = 1; + rt_ubase_t pll_rate; + rt_uint32_t n; + rt_base_t level; + rt_err_t err; + + RT_UNUSED(parent_rate); + + if (cell == &ccu->clocks[CLK_RISCV_AXI]) + { + return -RT_ENOSYS; + } + + level = rt_spin_lock_irqsave(&ccu->lock); + value = HWREG32(ccu->base + SUN252I_F101_RISCV_CLK); + + if (cell == &ccu->clocks[CLK_RISCV]) + { + if (((value & SUN252I_F101_RISCV_CLK_SEL) >> 24) != + SUN252I_F101_RISCV_CLK_CPU_PLL) + { + rt_spin_unlock_irqrestore(&ccu->lock, level); + return -RT_EBUSY; + } + + divider = (value & SUN252I_F101_RISCV_DIV) + 1; + } + + if (sun252i_f101_cpupll_recalc_rate(cell, 0) == rate) + { + rt_spin_unlock_irqrestore(&ccu->lock, level); + return RT_EOK; + } + + pll_rate = rate * divider; + if (pll_rate < SUN252I_F101_PLL_CPU_MIN_RATE || + pll_rate > SUN252I_F101_PLL_CPU_MAX_RATE || + pll_rate % SUN252I_F101_OSC24M_RATE) + { + rt_spin_unlock_irqrestore(&ccu->lock, level); + return -RT_EINVAL; + } + + n = pll_rate / SUN252I_F101_OSC24M_RATE; + value = HWREG32(ccu->base + SUN252I_F101_PLL_CPU_CTRL); + value &= ~SUN252I_F101_PLL_CPU_N; + value |= n << 8; + HWREG32(ccu->base + SUN252I_F101_PLL_CPU_CTRL) = value; + rt_hw_us_delay(10); + + /* Re-arm PLL lock detection before applying the new N factor. */ + value &= ~SUN252I_F101_PLL_CPU_LOCK_EN; + HWREG32(ccu->base + SUN252I_F101_PLL_CPU_CTRL) = value; + rt_hw_us_delay(10); + value |= SUN252I_F101_PLL_CPU_LOCK_EN; + HWREG32(ccu->base + SUN252I_F101_PLL_CPU_CTRL) = value | + SUN252I_F101_PLL_CPU_UPDATE; + + err = sun252i_f101_cpupll_wait_update(ccu); + if (!err) + { + err = sun252i_f101_cpupll_wait_lock(ccu); + if (!err) + { + rt_hw_us_delay(20); + } + } + + rt_spin_unlock_irqrestore(&ccu->lock, level); + return err; +} + +static const struct rt_clk_ops sun252i_f101_cpupll_clk_ops = +{ + .recalc_rate = sun252i_f101_cpupll_recalc_rate, + .round_rate = sun252i_f101_cpupll_round_rate, + .set_rate = sun252i_f101_cpupll_set_rate, +}; + +static void sun252i_f101_cpupll_release(struct sun252i_f101_cpupll *ccu) +{ + if (ccu->base) + { + rt_iounmap(ccu->base); + } + rt_free(ccu); +} + +static rt_err_t sun252i_f101_cpupll_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_size_t index; + struct rt_device *dev = &pdev->parent; + struct sun252i_f101_cpupll *ccu = rt_calloc(1, sizeof(*ccu)); + + if (!ccu) + { + return -RT_ENOMEM; + } + + ccu->base = rt_dm_dev_iomap(dev, 0); + if (!ccu->base) + { + err = -RT_EIO; + goto _fail; + } + + ccu->parent.dev = dev; + ccu->parent.cells_nr = CLK_CPUPLL_NUMBER; + ccu->parent.cells = ccu->cells; + rt_spin_lock_init(&ccu->lock); + + for (index = 0; index < CLK_CPUPLL_NUMBER; ++index) + { + ccu->clocks[index].name = sun252i_f101_cpupll_clock_names[index]; + ccu->clocks[index].ops = &sun252i_f101_cpupll_clk_ops; + ccu->clocks[index].priv = ccu; + ccu->clocks[index].flags = RT_CLK_F_GET_RATE_NOCACHE | + RT_CLK_F_IS_CRITICAL; + ccu->cells[index] = &ccu->clocks[index]; + } + + if ((err = rt_clk_register(&ccu->parent))) + { + goto _fail; + } + + dev->user_data = ccu; + return RT_EOK; + +_fail: + sun252i_f101_cpupll_release(ccu); + return err; +} + +static rt_err_t sun252i_f101_cpupll_remove(struct rt_platform_device *pdev) +{ + struct sun252i_f101_cpupll *ccu = pdev->parent.user_data; + + if (rt_clk_unregister(&ccu->parent)) + { + return -RT_EBUSY; + } + + sun252i_f101_cpupll_release(ccu); + return RT_EOK; +} + +static const struct rt_ofw_node_id sun252i_f101_cpupll_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-cpupll" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sun252i_f101_cpupll_driver = +{ + .name = "sun252i-f101-cpupll", + .ids = sun252i_f101_cpupll_ofw_ids, + .probe = sun252i_f101_cpupll_probe, + .remove = sun252i_f101_cpupll_remove, +}; + +static int sun252i_f101_cpupll_register(void) +{ + rt_platform_driver_register(&sun252i_f101_cpupll_driver); + return 0; +} +INIT_SUBSYS_EXPORT(sun252i_f101_cpupll_register); diff --git a/bsp/allwinner/dm/clk/clk-sun252i-f101-rtc.c b/bsp/allwinner/dm/clk/clk-sun252i-f101-rtc.c new file mode 100644 index 000000000000..cbeb6f4eda6c --- /dev/null +++ b/bsp/allwinner/dm/clk/clk-sun252i-f101-rtc.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#define SUN252I_F101_LOSC_CTRL 0x000 +#define SUN252I_F101_32K_FOUT_CTRL 0x060 +#define SUN252I_F101_XO_CTRL 0x160 +#define SUN252I_F101_RTC_SPI_CLK_CTRL 0x310 +#define SUN252I_F101_RTC_SPI_DIV_MASK RT_GENMASK(4, 0) +#define SUN252I_F101_RTC_KEY 0x16aa0000 + +struct sun252i_f101_rtc_ccu +{ + struct rt_clk_node parent; + struct rt_clk_fixed_rate clocks[CLK_RTC_NUMBER]; + struct rt_clk_cell *cells[CLK_RTC_NUMBER]; + void *base; +}; + +static const char * const sun252i_f101_rtc_clock_names[] = +{ + [CLK_RTC_IOSC] = "iosc", + [CLK_RTC_IOSC_DIV32K] = "iosc-div32k", + [CLK_RTC_DCXO24M_DIV32K] = "dcxo24m-div32k", + [CLK_RTC_32K] = "rtc32k", + [CLK_RTC_1K] = "rtc-1k", + [CLK_RTC_32K_FANOUT] = "rtc-32k-fanout", + [CLK_RTC_SPI] = "rtc-spi", + [CLK_RTC_CLK1M_RC] = "clk1m-rc", +}; + +/* These outputs are fixed after RTC clock source setup. */ +static const rt_ubase_t sun252i_f101_rtc_clock_rates[] = +{ + [CLK_RTC_IOSC] = 16000000, + [CLK_RTC_IOSC_DIV32K] = 32000, + [CLK_RTC_DCXO24M_DIV32K] = 32768, + [CLK_RTC_32K] = 32768, + [CLK_RTC_1K] = 1024, + [CLK_RTC_32K_FANOUT] = 32768, + [CLK_RTC_SPI] = 100000000, + [CLK_RTC_CLK1M_RC] = 1000000, +}; + +static rt_ubase_t sun252i_f101_rtc_clk_recalc_rate( + struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + struct rt_clk_fixed_rate *clock = rt_container_of(cell, + struct rt_clk_fixed_rate, cell); + struct sun252i_f101_rtc_ccu *ccu = cell->priv; + + RT_UNUSED(parent_rate); + + if (cell == &ccu->clocks[CLK_RTC_SPI].cell) + { + rt_uint32_t divider = HWREG32(ccu->base + SUN252I_F101_RTC_SPI_CLK_CTRL) & + SUN252I_F101_RTC_SPI_DIV_MASK; + + return clock->fixed_rate / (divider + 1); + } + + return clock->fixed_rate; +} + +static const struct rt_clk_ops sun252i_f101_rtc_clk_ops = +{ + .recalc_rate = sun252i_f101_rtc_clk_recalc_rate, +}; + +static void sun252i_f101_rtc_ccu_init(void *base) +{ + rt_uint32_t value; + + value = HWREG32(base + SUN252I_F101_XO_CTRL); + HWREG32(base + SUN252I_F101_XO_CTRL) = value | RT_BIT(1); + + value = HWREG32(base + SUN252I_F101_32K_FOUT_CTRL); + value |= RT_BIT(16); + value &= ~RT_GENMASK(2, 1); + HWREG32(base + SUN252I_F101_32K_FOUT_CTRL) = value; + + value = HWREG32(base + SUN252I_F101_LOSC_CTRL); + value &= ~RT_BIT(0); + HWREG32(base + SUN252I_F101_LOSC_CTRL) = value | SUN252I_F101_RTC_KEY | + RT_BIT(0); +} + +static void sun252i_f101_rtc_ccu_release(struct sun252i_f101_rtc_ccu *ccu) +{ + if (ccu->base) + { + rt_iounmap(ccu->base); + } + rt_free(ccu); +} + +static rt_err_t sun252i_f101_rtc_ccu_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_size_t index; + struct rt_device *dev = &pdev->parent; + struct sun252i_f101_rtc_ccu *ccu = rt_calloc(1, sizeof(*ccu)); + + if (!ccu) + { + return -RT_ENOMEM; + } + + ccu->base = rt_dm_dev_iomap(dev, 0); + if (!ccu->base) + { + err = -RT_EIO; + goto _fail; + } + + sun252i_f101_rtc_ccu_init(ccu->base); + + ccu->parent.dev = dev; + ccu->parent.cells_nr = CLK_RTC_NUMBER; + ccu->parent.cells = ccu->cells; + for (index = 0; index < CLK_RTC_NUMBER; ++index) + { + ccu->clocks[index].fixed_rate = sun252i_f101_rtc_clock_rates[index]; + ccu->clocks[index].cell.name = sun252i_f101_rtc_clock_names[index]; + ccu->clocks[index].cell.ops = &sun252i_f101_rtc_clk_ops; + ccu->clocks[index].cell.priv = ccu; + ccu->cells[index] = &ccu->clocks[index].cell; + } + + if ((err = rt_clk_register(&ccu->parent))) + { + goto _fail; + } + + dev->user_data = ccu; + + rt_platform_ofw_device_probe_child(rt_ofw_get_next_child(dev->ofw_node, RT_NULL)); + + return RT_EOK; + +_fail: + sun252i_f101_rtc_ccu_release(ccu); + return err; +} + +static rt_err_t sun252i_f101_rtc_ccu_remove(struct rt_platform_device *pdev) +{ + struct sun252i_f101_rtc_ccu *ccu = pdev->parent.user_data; + + if (rt_clk_unregister(&ccu->parent)) + { + return -RT_EBUSY; + } + sun252i_f101_rtc_ccu_release(ccu); + return RT_EOK; +} + +static const struct rt_ofw_node_id sun252i_f101_rtc_ccu_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-rtc-ccu" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sun252i_f101_rtc_ccu_driver = +{ + .name = "sun252i-f101-rtc-ccu", + .ids = sun252i_f101_rtc_ccu_ofw_ids, + .probe = sun252i_f101_rtc_ccu_probe, + .remove = sun252i_f101_rtc_ccu_remove, +}; + +static int sun252i_f101_rtc_ccu_register(void) +{ + rt_platform_driver_register(&sun252i_f101_rtc_ccu_driver); + return 0; +} +INIT_SUBSYS_EXPORT(sun252i_f101_rtc_ccu_register); diff --git a/bsp/allwinner/dm/clk/clk-sun252i-f101.c b/bsp/allwinner/dm/clk/clk-sun252i-f101.c new file mode 100644 index 000000000000..27c66233c0d1 --- /dev/null +++ b/bsp/allwinner/dm/clk/clk-sun252i-f101.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include + +#define SUN252I_F101_UART_BGR 0x090c +#define SUN252I_F101_TWI_BGR 0x091c +#define SUN252I_F101_DE_CLK 0x0600 +#define SUN252I_F101_DE_BGR 0x060c +#define SUN252I_F101_G2D_CLK 0x0630 +#define SUN252I_F101_G2D_BGR 0x063c +#define SUN252I_F101_CE_CLK 0x0680 +#define SUN252I_F101_CE_BGR 0x068c +#define SUN252I_F101_VE_CLK 0x0690 +#define SUN252I_F101_VE_BGR 0x069c +#define SUN252I_F101_DMA_BGR 0x070c +#define SUN252I_F101_HSTIMER_BGR 0x073c +#define SUN252I_F101_PWM_BGR 0x07ac +#define SUN252I_F101_MBUS_BGR 0x0804 +#define SUN252I_F101_SPI0_CLK 0x0940 +#define SUN252I_F101_SPI1_CLK 0x0944 +#define SUN252I_F101_SPI_BGR 0x096c +#define SUN252I_F101_SMHC0_CLK 0x0830 +#define SUN252I_F101_SMHC2_CLK 0x0838 +#define SUN252I_F101_SMHC_BGR 0x084c +#define SUN252I_F101_USB_BGR 0x0a8c +#define SUN252I_F101_COMBOPHY0_CLK 0x0aa0 +#define SUN252I_F101_COMBOPHY0_BGR 0x0aa4 +#define SUN252I_F101_DPSS_TOP_BGR 0x0abc +#define SUN252I_F101_IRRX_CLK 0x09c0 +#define SUN252I_F101_IRRX_BGR 0x09cc +#define SUN252I_F101_GPADC_BGR 0x09ec +#define SUN252I_F101_THS_BGR 0x09fc +#define SUN252I_F101_I2S0_CLK 0x0a10 +#define SUN252I_F101_I2S0_BGR 0x0a20 +#define SUN252I_F101_OWA_TX_CLK 0x0a24 +#define SUN252I_F101_OWA_RX_CLK 0x0a28 +#define SUN252I_F101_OWA_BGR 0x0a2c +#define SUN252I_F101_AUDIO_DAC_CLK 0x0a50 +#define SUN252I_F101_AUDIO_ADC_CLK 0x0a54 +#define SUN252I_F101_AUDIO_BGR 0x0a5c +#define SUN252I_F101_DSI_CLK 0x0b24 +#define SUN252I_F101_DSI_BGR 0x0b4c +#define SUN252I_F101_TCONLCD_CLK 0x0b60 +#define SUN252I_F101_TCONLCD_BGR 0x0b7c +#define SUN252I_F101_TPADC_CLK 0x0c50 +#define SUN252I_F101_TPADC_BGR 0x0c5c + +#define SUN252I_F101_CLK(_id, _name, _property, _reg, _mask, _rate) \ + { .id = (_id), .name = (_name), .rate_property = (_property), \ + .gate_reg = (_reg), .gate_mask = (_mask), .rate_default = (_rate) } + +#define SUN252I_F101_MOD_CLK(_id, _name, _property, _reg, _rate) \ + { .id = (_id), .name = (_name), .rate_property = (_property), \ + .gate_reg = (_reg), .gate_mask = RT_BIT(31), .rate_default = (_rate), \ + .parent_rates = sun252i_f101_mod_parents, \ + .parent_rates_nr = RT_ARRAY_SIZE(sun252i_f101_mod_parents), \ + .m_shift = 0, .m_width = 4, .p_shift = 8, .p_width = 2, \ + .mux_shift = 24, .mux_width = 3 } + +/* G2D can only use PERI_2X; its clock register has an M divider only. */ +#define SUN252I_F101_G2D_MOD_CLK(_id, _name, _property, _reg, _rate) \ + { .id = (_id), .name = (_name), .rate_property = (_property), \ + .gate_reg = (_reg), .gate_mask = RT_BIT(31), .rate_default = (_rate), \ + .parent_rates = sun252i_f101_g2d_parents, \ + .parent_rates_nr = RT_ARRAY_SIZE(sun252i_f101_g2d_parents), \ + .m_shift = 0, .m_width = 5, .p_shift = 8, .p_width = 0, \ + .mux_shift = 24, .mux_width = 3 } + +/* The oscillator and peripheral PLL rates are established by boot firmware. */ +static const rt_ubase_t sun252i_f101_mod_parents[] = +{ + 24000000, + 600000000, + 1200000000, + 300000000, + 120000000, +}; + +static const rt_ubase_t sun252i_f101_g2d_parents[] = +{ + 1200000000, +}; + +static const struct sunxi_clk_desc sun252i_f101_clks[] = +{ + SUN252I_F101_CLK(SUN252I_F101_CLK_APB0, "apb0", "allwinner,apb0-rate", + 0, 0, 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_APB1, "apb1", "allwinner,apb1-rate", + 0, 0, 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_APB_UART, "apb-uart", + "allwinner,apb-uart-rate", 0, 0, 100000000), + + /* + * The PLL parent rates below are set by firmware. Keep these clocks + * gate-only until the CCU can discover those rates instead of exposing + * a set_rate operation based on guessed parent frequencies. + */ + SUN252I_F101_CLK(SUN252I_F101_CLK_DE, "de", RT_NULL, + SUN252I_F101_DE_CLK, RT_BIT(31), 300000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_DE, "de-bus", + "allwinner,apb0-rate", SUN252I_F101_DE_BGR, RT_BIT(0), 100000000), + + SUN252I_F101_G2D_MOD_CLK(SUN252I_F101_CLK_G2D, "g2d", RT_NULL, + SUN252I_F101_G2D_CLK, 300000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_G2D, "g2d-bus", + "allwinner,apb0-rate", SUN252I_F101_G2D_BGR, RT_BIT(0), 100000000), + SUN252I_F101_MOD_CLK(SUN252I_F101_CLK_CE, "ce", RT_NULL, + SUN252I_F101_CE_CLK, 300000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_CE, "ce-bus", + "allwinner,apb0-rate", SUN252I_F101_CE_BGR, RT_BIT(0), 100000000), + + SUN252I_F101_CLK(SUN252I_F101_CLK_VE, "ve", RT_NULL, + SUN252I_F101_VE_CLK, RT_BIT(31), 300000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_VE, "ve-bus", + "allwinner,apb0-rate", SUN252I_F101_VE_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_DMA, "dma-bus", + "allwinner,apb0-rate", SUN252I_F101_DMA_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_HSTIMER, "hstimer-bus", + "allwinner,apb0-rate", SUN252I_F101_HSTIMER_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_PWM, "pwm-bus", + "allwinner,apb0-rate", SUN252I_F101_PWM_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_G2D_M, "g2d-m-bus", + "allwinner,apb0-rate", SUN252I_F101_MBUS_BGR, RT_BIT(10), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_VE_M, "ve-m-bus", + "allwinner,apb0-rate", SUN252I_F101_MBUS_BGR, RT_BIT(1), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_DMA_M, "dma-m-bus", + "allwinner,apb0-rate", SUN252I_F101_MBUS_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_DPSS_TOP, "dpss-top-bus", + "allwinner,apb0-rate", SUN252I_F101_DPSS_TOP_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_COMBOPHY0, "combophy0", RT_NULL, + SUN252I_F101_COMBOPHY0_CLK, RT_BIT(31), 300000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_COMBOPHY0, "combophy0-bus", + "allwinner,apb0-rate", SUN252I_F101_COMBOPHY0_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_DSI, "dsi", RT_NULL, + SUN252I_F101_DSI_CLK, RT_BIT(31), 300000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_DSI, "dsi-bus", + "allwinner,apb0-rate", SUN252I_F101_DSI_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_TCONLCD, "tcon-lcd", RT_NULL, + SUN252I_F101_TCONLCD_CLK, RT_BIT(31), 300000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_TCONLCD, "tcon-lcd-bus", + "allwinner,apb0-rate", SUN252I_F101_TCONLCD_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_TPADC, "tpadc", RT_NULL, + SUN252I_F101_TPADC_CLK, RT_BIT(31), 24000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_TPADC, "tpadc-bus", + "allwinner,apb0-rate", SUN252I_F101_TPADC_BGR, RT_BIT(0), 100000000), + + SUN252I_F101_MOD_CLK(SUN252I_F101_CLK_SMHC0, "smhc0", "allwinner,smhc-rate", + SUN252I_F101_SMHC0_CLK, 100000000), + SUN252I_F101_MOD_CLK(SUN252I_F101_CLK_SMHC2, "smhc2", "allwinner,smhc-rate", + SUN252I_F101_SMHC2_CLK, 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_SMHC0, "smhc0-bus", + "allwinner,apb0-rate", SUN252I_F101_SMHC_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_SMHC2, "smhc2-bus", + "allwinner,apb0-rate", SUN252I_F101_SMHC_BGR, RT_BIT(2), 100000000), + + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_UART0, "uart0-bus", + "allwinner,apb-uart-rate", SUN252I_F101_UART_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_UART1, "uart1-bus", + "allwinner,apb-uart-rate", SUN252I_F101_UART_BGR, RT_BIT(1), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_UART2, "uart2-bus", + "allwinner,apb-uart-rate", SUN252I_F101_UART_BGR, RT_BIT(2), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_UART3, "uart3-bus", + "allwinner,apb-uart-rate", SUN252I_F101_UART_BGR, RT_BIT(3), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_UART4, "uart4-bus", + "allwinner,apb-uart-rate", SUN252I_F101_UART_BGR, RT_BIT(4), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_UART5, "uart5-bus", + "allwinner,apb-uart-rate", SUN252I_F101_UART_BGR, RT_BIT(5), 100000000), + + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_TWI0, "twi0-bus", + "allwinner,apb1-rate", SUN252I_F101_TWI_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_TWI1, "twi1-bus", + "allwinner,apb1-rate", SUN252I_F101_TWI_BGR, RT_BIT(1), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_TWI2, "twi2-bus", + "allwinner,apb1-rate", SUN252I_F101_TWI_BGR, RT_BIT(2), 100000000), + + SUN252I_F101_MOD_CLK(SUN252I_F101_CLK_SPI0, "spi0", "allwinner,spi-rate", + SUN252I_F101_SPI0_CLK, 100000000), + SUN252I_F101_MOD_CLK(SUN252I_F101_CLK_SPI1, "spi1", "allwinner,spi-rate", + SUN252I_F101_SPI1_CLK, 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_SPI0, "spi0-bus", + "allwinner,apb0-rate", SUN252I_F101_SPI_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_SPI1, "spi1-bus", + "allwinner,apb0-rate", SUN252I_F101_SPI_BGR, RT_BIT(1), 100000000), + + SUN252I_F101_CLK(SUN252I_F101_CLK_IRRX, "irrx", RT_NULL, + SUN252I_F101_IRRX_CLK, RT_BIT(31), 24000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_IRRX, "irrx-bus", + "allwinner,apb0-rate", SUN252I_F101_IRRX_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_GPADC, "gpadc-bus", + "allwinner,apb0-rate", SUN252I_F101_GPADC_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_THS, "ths-bus", + "allwinner,apb0-rate", SUN252I_F101_THS_BGR, RT_BIT(0), 100000000), + + SUN252I_F101_CLK(SUN252I_F101_CLK_I2S0, "i2s0", RT_NULL, + SUN252I_F101_I2S0_CLK, RT_BIT(31), 24576000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_I2S0, "i2s0-bus", + "allwinner,apb0-rate", SUN252I_F101_I2S0_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_OWA_TX, "owa-tx", RT_NULL, + SUN252I_F101_OWA_TX_CLK, RT_BIT(31), 24576000), + SUN252I_F101_CLK(SUN252I_F101_CLK_OWA_RX, "owa-rx", RT_NULL, + SUN252I_F101_OWA_RX_CLK, RT_BIT(31), 24576000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_OWA, "owa-bus", + "allwinner,apb0-rate", SUN252I_F101_OWA_BGR, RT_BIT(0), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_AUDIO_CODEC_DAC, "audio-dac", RT_NULL, + SUN252I_F101_AUDIO_DAC_CLK, RT_BIT(31), 24576000), + SUN252I_F101_CLK(SUN252I_F101_CLK_AUDIO_CODEC_ADC, "audio-adc", RT_NULL, + SUN252I_F101_AUDIO_ADC_CLK, RT_BIT(31), 24576000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_AUDIO_CODEC, "audio-bus", + "allwinner,apb0-rate", SUN252I_F101_AUDIO_BGR, RT_BIT(0), 100000000), + + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_USB, "usb-bus", + "allwinner,apb0-rate", 0x0a70, RT_BIT(31), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_USBOTG0, "usb-otg", + "allwinner,apb0-rate", SUN252I_F101_USB_BGR, RT_BIT(8), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_USBEHCI0, "usb-ehci", + "allwinner,apb0-rate", SUN252I_F101_USB_BGR, RT_BIT(4), 100000000), + SUN252I_F101_CLK(SUN252I_F101_CLK_BUS_USBOHCI0, "usb-ohci", + "allwinner,apb0-rate", SUN252I_F101_USB_BGR, RT_BIT(0), 100000000), +}; + +const struct sunxi_ccu_data sun252i_f101_ccu_data = +{ + .clks = sun252i_f101_clks, + .clks_nr = RT_ARRAY_SIZE(sun252i_f101_clks), + .cells_nr = SUN252I_F101_CLK_NUMBER, +}; diff --git a/bsp/allwinner/dm/clk/clk-sunxi.c b/bsp/allwinner/dm/clk/clk-sunxi.c new file mode 100644 index 000000000000..617e4f5440f3 --- /dev/null +++ b/bsp/allwinner/dm/clk/clk-sunxi.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define DBG_TAG "clk.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include + +struct sunxi_ccu; + +struct sunxi_clk +{ + struct rt_clk_cell cell; + struct sunxi_ccu *ccu; + + const struct sunxi_clk_desc *desc; + rt_ubase_t rate; +}; + +struct sunxi_ccu +{ + struct rt_clk_node parent; + + void *base; + struct rt_spinlock lock; + + struct sunxi_clk *clks; + struct rt_clk_cell **cells; +}; + +#define to_sunxi_clk(raw) rt_container_of(raw, struct sunxi_clk, cell) + +static rt_ubase_t sunxi_clk_recalc_rate(struct rt_clk_cell *cell, + rt_ubase_t parent_rate) +{ + struct sunxi_clk *clock = to_sunxi_clk(cell); + const struct sunxi_clk_desc *desc = clock->desc; + rt_uint32_t value; + rt_uint32_t mux; + rt_uint32_t m; + rt_uint32_t p; + + RT_UNUSED(parent_rate); + + /* + * A boot loader may leave a module clock at any divider. Report the + * divider currently programmed in the CCU so a later set_rate() request + * is not incorrectly elided by the clock core. + */ + if (!desc->parent_rates || !desc->parent_rates_nr || !desc->m_width) + { + return clock->rate; + } + + value = HWREG32(clock->ccu->base + desc->gate_reg); + mux = desc->mux_width ? (value >> desc->mux_shift) & + (RT_BIT(desc->mux_width) - 1) : 0; + m = (value >> desc->m_shift) & (RT_BIT(desc->m_width) - 1); + p = desc->p_width ? (value >> desc->p_shift) & + (RT_BIT(desc->p_width) - 1) : 0; + + if (mux >= desc->parent_rates_nr) + { + return clock->rate; + } + + return desc->parent_rates[mux] / ((m + 1) * RT_BIT(p)); + +} + +static rt_ubase_t sunxi_clk_find_rate(struct sunxi_clk *clock, + rt_ubase_t requested, rt_uint32_t *out_value) +{ + rt_ubase_t best = 0; + rt_ubase_t lowest = ~(rt_ubase_t)0; + rt_uint32_t best_value = 0; + rt_uint32_t lowest_value = 0; + const struct sunxi_clk_desc *desc = clock->desc; + rt_uint32_t m_max; + rt_uint32_t p_max; + rt_uint32_t mux; + + if (!desc->parent_rates || !desc->parent_rates_nr || !desc->m_width) + { + return 0; + } + + m_max = RT_BIT(desc->m_width); + p_max = desc->p_width ? RT_BIT(desc->p_width) : 1; + + for (mux = 0; mux < desc->parent_rates_nr; ++mux) + { + rt_uint32_t m; + rt_uint32_t p; + + for (m = 0; m < m_max; ++m) + { + for (p = 0; p < p_max; ++p) + { + rt_ubase_t rate = desc->parent_rates[mux] / + ((m + 1) * RT_BIT(p)); + rt_uint32_t value = (m << desc->m_shift) | + (p << desc->p_shift) | + (mux << desc->mux_shift); + + if (rate < lowest) + { + lowest = rate; + lowest_value = value; + } + if (rate <= requested && rate > best) + { + best = rate; + best_value = value; + } + } + } + } + + if (!best) + { + best = lowest; + best_value = lowest_value; + } + if (out_value) + { + *out_value = best_value; + } + + return best; +} + +static rt_base_t sunxi_clk_round_rate(struct rt_clk_cell *cell, + rt_ubase_t requested, rt_ubase_t *parent_rate) +{ + struct sunxi_clk *clock = to_sunxi_clk(cell); + + RT_UNUSED(parent_rate); + + return sunxi_clk_find_rate(clock, requested, RT_NULL); +} + +static rt_err_t sunxi_clk_set_rate(struct rt_clk_cell *cell, + rt_ubase_t requested, rt_ubase_t parent_rate) +{ + rt_ubase_t level; + rt_ubase_t rate; + rt_uint32_t value; + rt_uint32_t mask; + struct sunxi_clk *clock = to_sunxi_clk(cell); + struct sunxi_ccu *ccu = clock->ccu; + const struct sunxi_clk_desc *desc = clock->desc; + + RT_UNUSED(parent_rate); + + rate = sunxi_clk_find_rate(clock, requested, &value); + if (!rate) + { + return -RT_ENOSYS; + } + + mask = (RT_BIT(desc->m_width) - 1) << desc->m_shift; + if (desc->p_width) + { + mask |= (RT_BIT(desc->p_width) - 1) << desc->p_shift; + } + if (desc->mux_width) + { + mask |= (RT_BIT(desc->mux_width) - 1) << desc->mux_shift; + } + + level = rt_spin_lock_irqsave(&ccu->lock); + value |= HWREG32(ccu->base + desc->gate_reg) & ~mask; + HWREG32(ccu->base + desc->gate_reg) = value; + rt_spin_unlock_irqrestore(&ccu->lock, level); + + clock->rate = rate; + return RT_EOK; +} + +static rt_err_t sunxi_clk_enable(struct rt_clk_cell *cell) +{ + rt_ubase_t level; + rt_uint32_t value; + struct sunxi_clk *clock = to_sunxi_clk(cell); + struct sunxi_ccu *ccu = clock->ccu; + + if (!clock->desc->gate_mask) + { + return RT_EOK; + } + + level = rt_spin_lock_irqsave(&ccu->lock); + value = HWREG32(ccu->base + clock->desc->gate_reg); + value |= clock->desc->gate_mask; + HWREG32(ccu->base + clock->desc->gate_reg) = value; + rt_spin_unlock_irqrestore(&ccu->lock, level); + + return RT_EOK; +} + +static void sunxi_clk_disable(struct rt_clk_cell *cell) +{ + rt_ubase_t level; + rt_uint32_t value; + struct sunxi_clk *clock = to_sunxi_clk(cell); + struct sunxi_ccu *ccu = clock->ccu; + + if (!clock->desc->gate_mask) + { + return; + } + + level = rt_spin_lock_irqsave(&ccu->lock); + value = HWREG32(ccu->base + clock->desc->gate_reg); + value &= ~clock->desc->gate_mask; + HWREG32(ccu->base + clock->desc->gate_reg) = value; + rt_spin_unlock_irqrestore(&ccu->lock, level); +} + +static rt_bool_t sunxi_clk_is_enabled(struct rt_clk_cell *cell) +{ + struct sunxi_clk *clock = to_sunxi_clk(cell); + + return !clock->desc->gate_mask || + !!(HWREG32(clock->ccu->base + clock->desc->gate_reg) & + clock->desc->gate_mask); +} + +static const struct rt_clk_ops sunxi_clk_ops = +{ + .enable = sunxi_clk_enable, + .disable = sunxi_clk_disable, + .is_enabled = sunxi_clk_is_enabled, + .recalc_rate = sunxi_clk_recalc_rate, + .round_rate = sunxi_clk_round_rate, + .set_rate = sunxi_clk_set_rate, +}; + +static rt_ubase_t sunxi_clk_read_rate(struct rt_device *dev, + const struct sunxi_clk_desc *desc) +{ + rt_uint32_t rate; + + if (desc->rate_property && + !rt_dm_dev_prop_read_u32(dev, desc->rate_property, &rate) && rate) + { + return rate; + } + + return desc->rate_default; +} + +static void sunxi_ccu_release(struct sunxi_ccu *ccu) +{ + if (ccu->base) + { + rt_iounmap(ccu->base); + } + + rt_free(ccu->cells); + rt_free(ccu->clks); + rt_free(ccu); +} + +static rt_err_t sunxi_ccu_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_size_t index; + struct rt_device *dev = &pdev->parent; + const struct sunxi_ccu_data *data = pdev->id ? pdev->id->data : RT_NULL; + struct sunxi_ccu *ccu; + + if (!data || !data->clks || !data->clks_nr || !data->cells_nr) + { + return -RT_EINVAL; + } + + ccu = rt_calloc(1, sizeof(*ccu)); + if (!ccu) + { + return -RT_ENOMEM; + } + + ccu->clks = rt_calloc(data->clks_nr, sizeof(*ccu->clks)); + ccu->cells = rt_calloc(data->cells_nr, sizeof(*ccu->cells)); + if (!ccu->clks || !ccu->cells) + { + err = -RT_ENOMEM; + goto _fail; + } + + ccu->base = rt_dm_dev_iomap(dev, 0); + if (!ccu->base) + { + err = -RT_EIO; + goto _fail; + } + + ccu->parent.dev = dev; + ccu->parent.cells_nr = data->cells_nr; + ccu->parent.cells = ccu->cells; + rt_spin_lock_init(&ccu->lock); + + for (index = 0; index < data->clks_nr; ++index) + { + struct sunxi_clk *clock = &ccu->clks[index]; + const struct sunxi_clk_desc *desc = &data->clks[index]; + + if (desc->id < 0 || desc->id >= data->cells_nr || !desc->name || + ccu->cells[desc->id]) + { + err = -RT_EINVAL; + goto _fail; + } + + clock->ccu = ccu; + clock->desc = desc; + clock->rate = sunxi_clk_read_rate(dev, desc); + clock->cell.name = desc->name; + clock->cell.ops = &sunxi_clk_ops; + ccu->cells[desc->id] = &clock->cell; + } + + if ((err = rt_clk_register(&ccu->parent))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + sunxi_ccu_release(ccu); + + return err; +} + +static const struct rt_ofw_node_id sunxi_ccu_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-ccu", .data = &sun252i_f101_ccu_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_ccu_driver = +{ + .name = "sunxi-ccu", + .ids = sunxi_ccu_ofw_ids, + .probe = sunxi_ccu_probe, +}; + +static int sunxi_ccu_register(void) +{ + rt_platform_driver_register(&sunxi_ccu_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(sunxi_ccu_register); diff --git a/bsp/allwinner/dm/clock_time/Kconfig b/bsp/allwinner/dm/clock_time/Kconfig new file mode 100644 index 000000000000..22248cbe38b2 --- /dev/null +++ b/bsp/allwinner/dm/clock_time/Kconfig @@ -0,0 +1,10 @@ +config RT_CLOCK_TIMER_SUNXI + bool "Allwinner Sunxi timer and high-speed timer" + depends on RT_USING_CLOCK_TIME + depends on RT_USING_DM + depends on RT_USING_CLK + depends on RT_USING_RESET + default n + help + Enable the F101's two 24 MHz timers and two high-speed timers as + clock_time timer devices. The RISC-V CLINT remains the system timer. diff --git a/bsp/allwinner/dm/clock_time/SConscript b/bsp/allwinner/dm/clock_time/SConscript new file mode 100644 index 000000000000..c3e72e08a84d --- /dev/null +++ b/bsp/allwinner/dm/clock_time/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_CLOCK_TIMER_SUNXI']): + src += ['clock_timer-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/clock_time/clock_timer-sunxi.c b/bsp/allwinner/dm/clock_time/clock_timer-sunxi.c new file mode 100644 index 000000000000..e510ca38444d --- /dev/null +++ b/bsp/allwinner/dm/clock_time/clock_timer-sunxi.c @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "timer.sunxi" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_TIMER_IRQ_EN 0x00 +#define SUNXI_TIMER_IRQ_STA 0x04 +#define SUNXI_TIMER_CTL(id) (0x10 + (id) * 0x10) +#define SUNXI_TIMER_INTV(id) (0x14 + (id) * 0x10) +#define SUNXI_TIMER_CNT(id) (0x18 + (id) * 0x10) + +#define SUNXI_TIMER_CTL_ENABLE RT_BIT(0) +#define SUNXI_TIMER_CTL_RELOAD RT_BIT(1) +#define SUNXI_TIMER_CTL_CLK_SRC_MASK RT_GENMASK(3, 2) +#define SUNXI_TIMER_CTL_CLK_SRC_24M (1U << 2) +#define SUNXI_TIMER_CTL_PRESCALE_MASK RT_GENMASK(6, 4) +#define SUNXI_TIMER_CTL_ONESHOT RT_BIT(7) + +#define SUNXI_HSTIMER_IRQ_EN 0x00 +#define SUNXI_HSTIMER_IRQ_STA 0x04 +#define SUNXI_HSTIMER_CTL(id) (0x20 + (id) * 0x20) +#define SUNXI_HSTIMER_INTV_LO(id) (0x24 + (id) * 0x20) +#define SUNXI_HSTIMER_INTV_HI(id) (0x28 + (id) * 0x20) +#define SUNXI_HSTIMER_CNT_LO(id) (0x2c + (id) * 0x20) + +#define SUNXI_HSTIMER_CTL_ENABLE RT_BIT(0) +#define SUNXI_HSTIMER_CTL_RELOAD RT_BIT(1) +#define SUNXI_HSTIMER_CTL_ONESHOT RT_BIT(7) + +#define SUNXI_TIMER_CHANNELS 2 +#define SUNXI_TIMER_24M_FREQ 24000000U + +enum sunxi_timer_kind +{ + SUNXI_TIMER, + SUNXI_HSTIMER, +}; + +struct sunxi_timer_controller; + +struct sunxi_timer_channel +{ + struct rt_clock_timer_device parent; + + struct sunxi_timer_controller *controller; + int id; + int irq; + rt_bool_t registered; + rt_bool_t irq_attached; +}; + +struct sunxi_timer_controller +{ + enum sunxi_timer_kind kind; + void *base; + struct rt_clk *clk; + struct rt_reset_control *reset; + struct rt_clock_timer_info info; + struct sunxi_timer_channel channels[SUNXI_TIMER_CHANNELS]; + rt_bool_t clk_enabled; +}; + +#define to_sunxi_timer_channel(raw) \ + rt_container_of(raw, struct sunxi_timer_channel, parent) + +static void sunxi_timer_stop(struct sunxi_timer_channel *channel) +{ + rt_uint32_t value; + struct sunxi_timer_controller *controller = channel->controller; + + if (controller->kind == SUNXI_TIMER) + { + value = HWREG32(controller->base + SUNXI_TIMER_CTL(channel->id)); + HWREG32(controller->base + SUNXI_TIMER_CTL(channel->id)) = + value & ~SUNXI_TIMER_CTL_ENABLE; + } + else + { + value = HWREG32(controller->base + SUNXI_HSTIMER_CTL(channel->id)); + HWREG32(controller->base + SUNXI_HSTIMER_CTL(channel->id)) = + value & ~SUNXI_HSTIMER_CTL_ENABLE; + } +} + +static void sunxi_clock_timer_init(struct rt_clock_timer_device *timer, + rt_uint32_t state) +{ + rt_uint32_t mask; + struct sunxi_timer_channel *channel = to_sunxi_timer_channel(timer); + struct sunxi_timer_controller *controller = channel->controller; + + mask = RT_BIT(channel->id); + sunxi_timer_stop(channel); + + if (controller->kind == SUNXI_TIMER) + { + HWREG32(controller->base + SUNXI_TIMER_IRQ_STA) = mask; + if (state) + { + HWREG32(controller->base + SUNXI_TIMER_IRQ_EN) |= mask; + } + else + { + HWREG32(controller->base + SUNXI_TIMER_IRQ_EN) &= ~mask; + } + } + else + { + HWREG32(controller->base + SUNXI_HSTIMER_IRQ_STA) = mask; + if (state) + { + HWREG32(controller->base + SUNXI_HSTIMER_IRQ_EN) |= mask; + } + else + { + HWREG32(controller->base + SUNXI_HSTIMER_IRQ_EN) &= ~mask; + } + } +} + +static rt_err_t sunxi_clock_timer_start(struct rt_clock_timer_device *timer, + rt_uint32_t count, rt_clock_timer_mode_t mode) +{ + rt_uint32_t value; + struct sunxi_timer_channel *channel = to_sunxi_timer_channel(timer); + struct sunxi_timer_controller *controller = channel->controller; + + if (!count || (mode != CLOCK_TIMER_MODE_ONESHOT && + mode != CLOCK_TIMER_MODE_PERIOD)) + { + return -RT_EINVAL; + } + + sunxi_timer_stop(channel); + + if (controller->kind == SUNXI_TIMER) + { + HWREG32(controller->base + SUNXI_TIMER_INTV(channel->id)) = count; + value = HWREG32(controller->base + SUNXI_TIMER_CTL(channel->id)); + value &= ~(SUNXI_TIMER_CTL_CLK_SRC_MASK | + SUNXI_TIMER_CTL_PRESCALE_MASK | SUNXI_TIMER_CTL_ONESHOT); + value |= SUNXI_TIMER_CTL_CLK_SRC_24M; + if (mode == CLOCK_TIMER_MODE_ONESHOT) + { + value |= SUNXI_TIMER_CTL_ONESHOT; + } + HWREG32(controller->base + SUNXI_TIMER_CTL(channel->id)) = + value | SUNXI_TIMER_CTL_ENABLE | SUNXI_TIMER_CTL_RELOAD; + } + else + { + HWREG32(controller->base + SUNXI_HSTIMER_INTV_HI(channel->id)) = 0; + HWREG32(controller->base + SUNXI_HSTIMER_INTV_LO(channel->id)) = count; + value = HWREG32(controller->base + SUNXI_HSTIMER_CTL(channel->id)); + value &= ~SUNXI_HSTIMER_CTL_ONESHOT; + if (mode == CLOCK_TIMER_MODE_ONESHOT) + { + value |= SUNXI_HSTIMER_CTL_ONESHOT; + } + HWREG32(controller->base + SUNXI_HSTIMER_CTL(channel->id)) = + value | SUNXI_HSTIMER_CTL_ENABLE | SUNXI_HSTIMER_CTL_RELOAD; + } + + timer->cycles = count; + timer->mode = mode; + + return RT_EOK; +} + +static void sunxi_clock_timer_stop(struct rt_clock_timer_device *timer) +{ + sunxi_timer_stop(to_sunxi_timer_channel(timer)); +} + +static rt_uint32_t sunxi_clock_timer_count_get(struct rt_clock_timer_device *timer) +{ + struct sunxi_timer_channel *channel = to_sunxi_timer_channel(timer); + struct sunxi_timer_controller *controller = channel->controller; + + if (controller->kind == SUNXI_TIMER) + { + return HWREG32(controller->base + SUNXI_TIMER_CNT(channel->id)); + } + + return HWREG32(controller->base + SUNXI_HSTIMER_CNT_LO(channel->id)); +} + +static rt_err_t sunxi_clock_timer_control(struct rt_clock_timer_device *timer, + rt_uint32_t cmd, void *args) +{ + struct sunxi_timer_channel *channel = to_sunxi_timer_channel(timer); + struct sunxi_timer_controller *controller = channel->controller; + + switch (cmd) + { + case CLOCK_TIMER_CTRL_FREQ_SET: + if (!args || *(rt_int32_t *)args != timer->freq) + { + return -RT_ENOSYS; + } + return RT_EOK; + + case CLOCK_TIMER_CTRL_STOP: + sunxi_timer_stop(channel); + return RT_EOK; + + case CLOCK_TIMER_CTRL_INFO_GET: + if (!args) + { + return -RT_EINVAL; + } + *(struct rt_clock_timer_info *)args = controller->info; + return RT_EOK; + + case CLOCK_TIMER_CTRL_MODE_SET: + return sunxi_clock_timer_start(timer, timer->cycles, + (rt_clock_timer_mode_t)(rt_ubase_t)args); + + default: + return -RT_ENOSYS; + } +} + +static const struct rt_clock_timer_ops sunxi_clock_timer_ops = +{ + .init = sunxi_clock_timer_init, + .start = sunxi_clock_timer_start, + .stop = sunxi_clock_timer_stop, + .count_get = sunxi_clock_timer_count_get, + .control = sunxi_clock_timer_control, +}; + +static void sunxi_clock_timer_isr(int irqno, void *parameter) +{ + rt_uint32_t mask; + struct sunxi_timer_channel *channel = parameter; + struct sunxi_timer_controller *controller = channel->controller; + + RT_UNUSED(irqno); + + mask = RT_BIT(channel->id); + if (controller->kind == SUNXI_TIMER) + { + if (!(HWREG32(controller->base + SUNXI_TIMER_IRQ_STA) & mask)) + { + return; + } + HWREG32(controller->base + SUNXI_TIMER_IRQ_STA) = mask; + } + else + { + if (!(HWREG32(controller->base + SUNXI_HSTIMER_IRQ_STA) & mask)) + { + return; + } + HWREG32(controller->base + SUNXI_HSTIMER_IRQ_STA) = mask; + } + + rt_clock_timer_isr(&channel->parent); +} + +static void sunxi_clock_timer_release(struct sunxi_timer_controller *controller) +{ + int i; + + for (i = 0; i < SUNXI_TIMER_CHANNELS; ++i) + { + struct sunxi_timer_channel *channel = &controller->channels[i]; + + if (channel->irq_attached) + { + rt_pic_irq_mask(channel->irq); + rt_pic_detach_irq(channel->irq, channel); + } + if (channel->registered) + { + rt_device_unregister(&channel->parent.parent); + } + } + if (!rt_is_err_or_null(controller->reset)) + { + rt_reset_control_assert(controller->reset); + rt_reset_control_put(controller->reset); + } + if (controller->clk_enabled) + { + rt_clk_disable_unprepare(controller->clk); + } + if (!rt_is_err_or_null(controller->clk)) + { + rt_clk_put(controller->clk); + } + if (controller->base) + { + rt_iounmap(controller->base); + } + rt_free(controller); +} + +static rt_err_t sunxi_clock_timer_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t frequency = SUNXI_TIMER_24M_FREQ; + const char *prefix; + struct rt_device *dev = &pdev->parent; + enum sunxi_timer_kind kind = (rt_ubase_t)pdev->id->data; + struct sunxi_timer_controller *controller = rt_calloc(1, sizeof(*controller)); + + if (!controller) + { + return -RT_ENOMEM; + } + + controller->kind = kind; + controller->base = rt_dm_dev_iomap(dev, 0); + if (!controller->base) + { + err = -RT_EIO; + goto _fail; + } + + if (kind == SUNXI_HSTIMER) + { + controller->clk = rt_clk_get_by_name(dev, "bus"); + controller->reset = rt_reset_control_get_by_name(dev, "bus"); + if (rt_is_err(controller->clk) || rt_is_err(controller->reset)) + { + err = rt_is_err(controller->clk) ? rt_ptr_err(controller->clk) : + rt_ptr_err(controller->reset); + goto _fail; + } + if ((err = rt_reset_control_deassert(controller->reset)) || + (err = rt_clk_prepare_enable(controller->clk))) + { + goto _fail; + } + controller->clk_enabled = RT_TRUE; + frequency = rt_clk_get_rate(controller->clk); + if (!frequency) + { + err = -RT_EIO; + goto _fail; + } + prefix = "hstimer"; + } + else + { + rt_dm_dev_prop_read_u32(dev, "clock-frequency", &frequency); + prefix = "timer"; + } + + controller->info.maxfreq = frequency; + controller->info.minfreq = frequency; + controller->info.maxcnt = RT_UINT32_MAX; + controller->info.cntmode = CLOCK_TIMER_CNTMODE_DW; + + for (int i = 0; i < SUNXI_TIMER_CHANNELS; ++i) + { + char name[RT_NAME_MAX]; + struct sunxi_timer_channel *channel = &controller->channels[i]; + + channel->controller = controller; + channel->id = i; + channel->irq = rt_dm_dev_get_irq(dev, i); + if (channel->irq < 0) + { + err = channel->irq; + goto _fail; + } + + channel->parent.ops = &sunxi_clock_timer_ops; + channel->parent.info = &controller->info; + channel->parent.freq = frequency; + rt_snprintf(name, sizeof(name), "%s%d", prefix, i); + + if ((err = rt_clock_timer_register(&channel->parent, name, controller))) + { + goto _fail; + } + channel->registered = RT_TRUE; + + if ((err = rt_pic_attach_irq(channel->irq, sunxi_clock_timer_isr, + channel, name, RT_IRQ_F_NONE))) + { + goto _fail; + } + channel->irq_attached = RT_TRUE; + rt_pic_irq_unmask(channel->irq); + } + + dev->user_data = controller; + rt_dm_dev_bind_fwdata(dev, RT_NULL, controller); + + return RT_EOK; + +_fail: + sunxi_clock_timer_release(controller); + + return err; +} + +static rt_err_t sunxi_clock_timer_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct sunxi_timer_controller *controller = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + sunxi_clock_timer_release(controller); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_clock_timer_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-timer", .data = (void *)SUNXI_TIMER }, + { .compatible = "allwinner,sun252i-f101-hstimer", .data = (void *)SUNXI_HSTIMER }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_clock_timer_driver = +{ + .name = "clock-timer-sunxi", + .ids = sunxi_clock_timer_ofw_ids, + .probe = sunxi_clock_timer_probe, + .remove = sunxi_clock_timer_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_clock_timer_driver); diff --git a/bsp/allwinner/dm/dma/Kconfig b/bsp/allwinner/dm/dma/Kconfig new file mode 100644 index 000000000000..d5510a2f88f9 --- /dev/null +++ b/bsp/allwinner/dm/dma/Kconfig @@ -0,0 +1,7 @@ +config RT_DMA_SUNXI + bool "Allwinner Sunxi DMA controller" + depends on RT_USING_CLK + depends on RT_USING_DMA + depends on RT_USING_DM + depends on RT_USING_RESET + default n diff --git a/bsp/allwinner/dm/dma/SConscript b/bsp/allwinner/dm/dma/SConscript new file mode 100644 index 000000000000..ff0423d242b8 --- /dev/null +++ b/bsp/allwinner/dm/dma/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_DMA_SUNXI']): + src += ['dma-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/dma/dma-sunxi.c b/bsp/allwinner/dm/dma/dma-sunxi.c new file mode 100644 index 000000000000..080b3fb2f907 --- /dev/null +++ b/bsp/allwinner/dm/dma/dma-sunxi.c @@ -0,0 +1,710 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define SUNXI_DMA_IRQ_ENABLE(index) ((index) * 0x04) +#define SUNXI_DMA_IRQ_STATUS(index) ((index) * 0x04 + 0x10) +#define SUNXI_DMA_IRQ_HALF RT_BIT(0) +#define SUNXI_DMA_IRQ_PACKAGE RT_BIT(1) +#define SUNXI_DMA_IRQ_QUEUE RT_BIT(2) +#define SUNXI_DMA_IRQ_MASK (SUNXI_DMA_IRQ_PACKAGE | SUNXI_DMA_IRQ_QUEUE) +#define SUNXI_DMA_IRQ_CHANNELS 8 +#define SUNXI_DMA_IRQ_WIDTH 4 + +#define SUNXI_DMA_CHANNEL_BASE 0x100 +#define SUNXI_DMA_CHANNEL_STRIDE 0x040 +#define SUNXI_DMA_CHANNEL_ENABLE 0x00 +#define SUNXI_DMA_CHANNEL_PAUSE 0x04 +#define SUNXI_DMA_CHANNEL_LLI 0x08 + +#define SUNXI_DMA_CHANNEL_START RT_BIT(0) +#define SUNXI_DMA_CHANNEL_PAUSE_RESUME 0 + +#define SUNXI_DMA_CFG_SRC_DRQ(value) ((value) & 0x3f) +#define SUNXI_DMA_CFG_SRC_MODE(value) (((value) & 0x1) << 8) +#define SUNXI_DMA_CFG_SRC_BURST(value) (((value) & 0x3) << 6) +#define SUNXI_DMA_CFG_SRC_WIDTH(value) (((value) & 0x3) << 9) +#define SUNXI_DMA_CFG_DST_DRQ(value) (SUNXI_DMA_CFG_SRC_DRQ(value) << 16) +#define SUNXI_DMA_CFG_DST_MODE(value) (SUNXI_DMA_CFG_SRC_MODE(value) << 16) +#define SUNXI_DMA_CFG_DST_BURST(value) (SUNXI_DMA_CFG_SRC_BURST(value) << 16) +#define SUNXI_DMA_CFG_DST_WIDTH(value) (SUNXI_DMA_CFG_SRC_WIDTH(value) << 16) + +#define SUNXI_DMA_DRQ_SDRAM 1 +#define SUNXI_DMA_MODE_LINEAR 0 +#define SUNXI_DMA_MODE_IO 1 +#define SUNXI_DMA_LLI_LAST 0xfffff800 +#define SUNXI_DMA_LLI_WAIT 64 +#define SUNXI_DMA_MAX_CHANNELS 16 +#define SUNXI_DMA_MAX_REQUESTS 48 +#define SUNXI_DMA_MAX_TRANSFER 0x00ffffff + +struct sunxi_dma_lli +{ + rt_uint32_t config; + rt_uint32_t src; + rt_uint32_t dst; + rt_uint32_t length; + rt_uint32_t parameter; + rt_uint32_t next; +}; + +struct sunxi_dma; + +struct sunxi_dma_channel +{ + struct rt_dma_chan parent; + struct sunxi_dma *dma; + struct sunxi_dma_lli *llis; + rt_ubase_t llis_dma; + rt_size_t llis_size; + rt_size_t transfer_size; + rt_size_t period_size; + rt_ubase_t memory; + rt_uint8_t id; + rt_uint8_t request; + rt_bool_t allocated; + rt_bool_t active; + rt_bool_t cyclic; + enum rt_dma_transfer_direction direction; +}; + +struct sunxi_dma +{ + struct rt_dma_controller parent; + struct sunxi_dma_channel *channels; + void *base; + struct rt_clk *bus_clk; + struct rt_clk *mbus_clk; + struct rt_reset_control *reset; + rt_uint32_t channels_nr; + rt_uint32_t requests_nr; + int irq; + rt_bool_t irq_installed; +}; + +#define to_sunxi_dma(raw) rt_container_of(raw, struct sunxi_dma, parent) +#define to_sunxi_dma_channel(raw) rt_container_of(raw, struct sunxi_dma_channel, parent) + +rt_inline void *sunxi_dma_channel_base(struct sunxi_dma_channel *channel) +{ + return channel->dma->base + SUNXI_DMA_CHANNEL_BASE + + channel->id * SUNXI_DMA_CHANNEL_STRIDE; +} + +rt_inline rt_uint32_t sunxi_dma_width_code(enum rt_dma_slave_buswidth width) +{ + switch (width) + { + case RT_DMA_SLAVE_BUSWIDTH_1_BYTE: + return 0; + case RT_DMA_SLAVE_BUSWIDTH_2_BYTES: + return 1; + case RT_DMA_SLAVE_BUSWIDTH_4_BYTES: + return 2; + case RT_DMA_SLAVE_BUSWIDTH_8_BYTES: + return 3; + default: + return RT_UINT32_MAX; + } +} + +static void sunxi_dma_free_llis(struct sunxi_dma_channel *channel) +{ + if (channel->llis) + { + rt_dma_free_coherent(channel->dma->parent.dev, channel->llis_size, + channel->llis, channel->llis_dma); + channel->llis = RT_NULL; + channel->llis_dma = 0; + channel->llis_size = 0; + } +} + +static rt_err_t sunxi_dma_alloc_llis(struct sunxi_dma_channel *channel, + rt_size_t count) +{ + rt_size_t size; + + if (!count || count > (rt_size_t)-1 / sizeof(*channel->llis)) + { + return -RT_EINVAL; + } + + sunxi_dma_free_llis(channel); + size = count * sizeof(*channel->llis); + channel->llis = rt_dma_alloc_coherent(channel->dma->parent.dev, size, + &channel->llis_dma); + if (!channel->llis) + { + return -RT_ENOMEM; + } + + channel->llis_size = size; + return RT_EOK; +} + +static rt_uint32_t sunxi_dma_burst_code(rt_uint32_t burst) +{ + if (burst <= 1) + { + return 0; + } + if (burst <= 4) + { + return 1; + } + if (burst <= 8) + { + return 2; + } + return 3; +} + +static rt_uint32_t sunxi_dma_config_value(struct sunxi_dma_channel *channel, + enum rt_dma_transfer_direction direction) +{ + struct rt_dma_slave_config *config = &channel->parent.conf; + rt_uint32_t src_width = 2; + rt_uint32_t dst_width = 2; + rt_uint32_t src_burst = 0; + rt_uint32_t dst_burst = 0; + rt_uint32_t src_drq = SUNXI_DMA_DRQ_SDRAM; + rt_uint32_t dst_drq = SUNXI_DMA_DRQ_SDRAM; + rt_uint32_t src_mode = SUNXI_DMA_MODE_LINEAR; + rt_uint32_t dst_mode = SUNXI_DMA_MODE_LINEAR; + + if (config->src_addr_width != RT_DMA_SLAVE_BUSWIDTH_UNDEFINED) + { + src_width = sunxi_dma_width_code(config->src_addr_width); + } + if (config->dst_addr_width != RT_DMA_SLAVE_BUSWIDTH_UNDEFINED) + { + dst_width = sunxi_dma_width_code(config->dst_addr_width); + } + src_burst = sunxi_dma_burst_code(config->src_maxburst); + dst_burst = sunxi_dma_burst_code(config->dst_maxburst); + + if (direction == RT_DMA_MEM_TO_DEV) + { + dst_drq = channel->request; + dst_mode = SUNXI_DMA_MODE_IO; + } + else if (direction == RT_DMA_DEV_TO_MEM) + { + src_drq = channel->request; + src_mode = SUNXI_DMA_MODE_IO; + } + + return SUNXI_DMA_CFG_SRC_DRQ(src_drq) | + SUNXI_DMA_CFG_SRC_MODE(src_mode) | + SUNXI_DMA_CFG_SRC_BURST(src_burst) | + SUNXI_DMA_CFG_SRC_WIDTH(src_width) | + SUNXI_DMA_CFG_DST_DRQ(dst_drq) | + SUNXI_DMA_CFG_DST_MODE(dst_mode) | + SUNXI_DMA_CFG_DST_BURST(dst_burst) | + SUNXI_DMA_CFG_DST_WIDTH(dst_width); +} + +/* + * On F101's no-MMU configuration, rt_ioremap_nocache() is an identity + * mapping. DMA-pool allocations therefore still need cache maintenance at + * the hardware boundary. + */ +static void sunxi_dma_sync_for_device(struct sunxi_dma_channel *channel) +{ +#ifndef ARCH_MM_MMU + if (channel->direction == RT_DMA_MEM_TO_MEM) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, + (void *)channel->llis[0].src, channel->transfer_size); + rt_hw_cpu_dcache_clean_and_invalidate((void *)channel->llis[0].dst, + channel->transfer_size); + } + else if (channel->direction == RT_DMA_MEM_TO_DEV) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void *)channel->memory, + channel->transfer_size); + } + else if (channel->direction == RT_DMA_DEV_TO_MEM) + { + rt_hw_cpu_dcache_clean_and_invalidate((void *)channel->memory, + channel->transfer_size); + } +#else + RT_UNUSED(channel); +#endif +} + +static void sunxi_dma_sync_for_cpu(struct sunxi_dma_channel *channel) +{ +#ifndef ARCH_MM_MMU + if (channel->direction == RT_DMA_MEM_TO_MEM) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, + (void *)channel->llis[0].dst, channel->transfer_size); + } + else if (channel->direction == RT_DMA_DEV_TO_MEM) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, (void *)channel->memory, + channel->transfer_size); + } +#else + RT_UNUSED(channel); +#endif +} + +static rt_err_t sunxi_dma_build_llis(struct sunxi_dma_channel *channel, + rt_ubase_t memory, rt_size_t length, rt_size_t period, + enum rt_dma_transfer_direction direction, rt_bool_t cyclic) +{ + rt_size_t count; + rt_size_t index; + rt_ubase_t source; + rt_ubase_t destination; + rt_uint32_t config; + rt_err_t err; + + if (!length || (!cyclic && length > SUNXI_DMA_MAX_TRANSFER) || + (cyclic && (!period || length % period || + period > SUNXI_DMA_MAX_TRANSFER))) + { + return -RT_EINVAL; + } + + count = cyclic ? length / period : 1; + if ((err = sunxi_dma_alloc_llis(channel, count))) + { + return err; + } + + config = sunxi_dma_config_value(channel, direction); + for (index = 0; index < count; ++index) + { + struct sunxi_dma_lli *lli = &channel->llis[index]; + rt_size_t item_length = cyclic ? period : length; + + if (direction == RT_DMA_MEM_TO_DEV) + { + source = memory + index * item_length; + destination = channel->parent.conf.dst_addr; + } + else if (direction == RT_DMA_DEV_TO_MEM) + { + source = channel->parent.conf.src_addr; + destination = memory + index * item_length; + } + else + { + source = channel->parent.transfer.src_addr; + destination = channel->parent.transfer.dst_addr; + } + + lli->config = config; + lli->src = source; + lli->dst = destination; + lli->length = item_length; + lli->parameter = SUNXI_DMA_LLI_WAIT; + if (index + 1 < count) + { + lli->next = channel->llis_dma + (index + 1) * sizeof(*lli); + } + else if (cyclic) + { + lli->next = channel->llis_dma; + } + else + { + lli->next = SUNXI_DMA_LLI_LAST; + } + } + + channel->transfer_size = length; + channel->period_size = cyclic ? period : length; + channel->memory = memory; + channel->direction = direction; + channel->cyclic = cyclic; + return RT_EOK; +} + +static struct rt_dma_chan *sunxi_dma_request_channel( + struct rt_dma_controller *controller, struct rt_device *slave, + void *fw_data) +{ + struct rt_ofw_cell_args *args = fw_data; + struct sunxi_dma *dma = to_sunxi_dma(controller); + struct sunxi_dma_channel *channel = RT_NULL; + rt_uint32_t request = 0; + rt_size_t index; + + if (args) + { + if (args->args_count != 1 || args->args[0] >= dma->requests_nr) + { + return rt_err_ptr(-RT_EINVAL); + } + request = args->args[0]; + } + + for (index = 0; index < dma->channels_nr; ++index) + { + if (!dma->channels[index].allocated) + { + channel = &dma->channels[index]; + break; + } + } + if (!channel) + { + return rt_err_ptr(-RT_EBUSY); + } + + channel->allocated = RT_TRUE; + channel->active = RT_FALSE; + channel->cyclic = RT_FALSE; + channel->request = request; + channel->parent.slave = slave; + + return &channel->parent; +} + +static rt_err_t sunxi_dma_stop(struct rt_dma_chan *raw); + +static rt_err_t sunxi_dma_release_channel(struct rt_dma_chan *raw) +{ + struct sunxi_dma_channel *channel = to_sunxi_dma_channel(raw); + + sunxi_dma_stop(raw); + sunxi_dma_free_llis(channel); + channel->allocated = RT_FALSE; + channel->active = RT_FALSE; + channel->cyclic = RT_FALSE; + return RT_EOK; +} + +static rt_err_t sunxi_dma_stop(struct rt_dma_chan *raw) +{ + struct sunxi_dma_channel *channel = to_sunxi_dma_channel(raw); + void *base = sunxi_dma_channel_base(channel); + + HWREG32(base + SUNXI_DMA_CHANNEL_ENABLE) = 0; + HWREG32(base + SUNXI_DMA_CHANNEL_PAUSE) = SUNXI_DMA_CHANNEL_PAUSE_RESUME; + channel->active = RT_FALSE; + return RT_EOK; +} + +static rt_err_t sunxi_dma_pause(struct rt_dma_chan *raw) +{ + struct sunxi_dma_channel *channel = to_sunxi_dma_channel(raw); + + HWREG32(sunxi_dma_channel_base(channel) + SUNXI_DMA_CHANNEL_PAUSE) = RT_BIT(1); + return RT_EOK; +} + +static rt_err_t sunxi_dma_config(struct rt_dma_chan *raw, + struct rt_dma_slave_config *config) +{ + RT_UNUSED(raw); + + if (config->direction == RT_DMA_MEM_TO_DEV) + { + if (sunxi_dma_width_code(config->dst_addr_width) == RT_UINT32_MAX || + (config->src_addr_width != RT_DMA_SLAVE_BUSWIDTH_UNDEFINED && + sunxi_dma_width_code(config->src_addr_width) == RT_UINT32_MAX)) + { + return -RT_EINVAL; + } + } + else if (config->direction == RT_DMA_DEV_TO_MEM) + { + if (sunxi_dma_width_code(config->src_addr_width) == RT_UINT32_MAX || + (config->dst_addr_width != RT_DMA_SLAVE_BUSWIDTH_UNDEFINED && + sunxi_dma_width_code(config->dst_addr_width) == RT_UINT32_MAX)) + { + return -RT_EINVAL; + } + } + else if (config->direction != RT_DMA_MEM_TO_MEM) + { + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t sunxi_dma_prep_memcpy(struct rt_dma_chan *raw, + rt_ubase_t source, rt_ubase_t destination, rt_size_t length) +{ + struct sunxi_dma_channel *channel = to_sunxi_dma_channel(raw); + + channel->parent.transfer.src_addr = source; + channel->parent.transfer.dst_addr = destination; + return sunxi_dma_build_llis(channel, 0, length, 0, RT_DMA_MEM_TO_MEM, + RT_FALSE); +} + +static rt_err_t sunxi_dma_prep_single(struct rt_dma_chan *raw, + rt_ubase_t memory, rt_size_t length, enum rt_dma_transfer_direction direction) +{ + return sunxi_dma_build_llis(to_sunxi_dma_channel(raw), memory, length, 0, + direction, RT_FALSE); +} + +static rt_err_t sunxi_dma_prep_cyclic(struct rt_dma_chan *raw, + rt_ubase_t memory, rt_size_t length, rt_size_t period, + enum rt_dma_transfer_direction direction) +{ + return sunxi_dma_build_llis(to_sunxi_dma_channel(raw), memory, length, + period, direction, RT_TRUE); +} + +static rt_err_t sunxi_dma_start(struct rt_dma_chan *raw) +{ + struct sunxi_dma_channel *channel = to_sunxi_dma_channel(raw); + struct sunxi_dma *dma = channel->dma; + rt_uint32_t shift = (channel->id % SUNXI_DMA_IRQ_CHANNELS) * + SUNXI_DMA_IRQ_WIDTH; + rt_uint32_t group = channel->id / SUNXI_DMA_IRQ_CHANNELS; + rt_uint32_t irq_mask = channel->cyclic ? SUNXI_DMA_IRQ_PACKAGE : + SUNXI_DMA_IRQ_QUEUE; + rt_uint32_t value; + void *base = sunxi_dma_channel_base(channel); + + if (!channel->llis || channel->active) + { + return -RT_EBUSY; + } + + value = HWREG32(dma->base + SUNXI_DMA_IRQ_STATUS(group)); + HWREG32(dma->base + SUNXI_DMA_IRQ_STATUS(group)) = value; + value = HWREG32(dma->base + SUNXI_DMA_IRQ_ENABLE(group)); + value &= ~(SUNXI_DMA_IRQ_MASK << shift); + value |= irq_mask << shift; + HWREG32(dma->base + SUNXI_DMA_IRQ_ENABLE(group)) = value; + + sunxi_dma_sync_for_device(channel); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, channel->llis, channel->llis_size); + HWREG32(base + SUNXI_DMA_CHANNEL_LLI) = channel->llis_dma; + HWREG32(base + SUNXI_DMA_CHANNEL_PAUSE) = SUNXI_DMA_CHANNEL_PAUSE_RESUME; + HWREG32(base + SUNXI_DMA_CHANNEL_ENABLE) = SUNXI_DMA_CHANNEL_START; + channel->active = RT_TRUE; + return RT_EOK; +} + +static const struct rt_dma_controller_ops sunxi_dma_ops = +{ + .request_chan = sunxi_dma_request_channel, + .release_chan = sunxi_dma_release_channel, + .start = sunxi_dma_start, + .pause = sunxi_dma_pause, + .stop = sunxi_dma_stop, + .config = sunxi_dma_config, + .prep_memcpy = sunxi_dma_prep_memcpy, + .prep_cyclic = sunxi_dma_prep_cyclic, + .prep_single = sunxi_dma_prep_single, +}; + +static void sunxi_dma_isr(int irq, void *parameter) +{ + struct sunxi_dma *dma = parameter; + rt_uint32_t group; + + RT_UNUSED(irq); + + for (group = 0; group < (dma->channels_nr + SUNXI_DMA_IRQ_CHANNELS - 1) / + SUNXI_DMA_IRQ_CHANNELS; ++group) + { + rt_uint32_t pending = HWREG32(dma->base + SUNXI_DMA_IRQ_STATUS(group)); + rt_uint32_t index; + + if (!pending) + { + continue; + } + HWREG32(dma->base + SUNXI_DMA_IRQ_STATUS(group)) = pending; + + for (index = 0; index < SUNXI_DMA_IRQ_CHANNELS; ++index) + { + rt_uint32_t status = (pending >> (index * SUNXI_DMA_IRQ_WIDTH)) & 0xf; + rt_uint32_t channel_id = group * SUNXI_DMA_IRQ_CHANNELS + index; + struct sunxi_dma_channel *channel; + + if (!status || channel_id >= dma->channels_nr) + { + continue; + } + + channel = &dma->channels[channel_id]; + if (!channel->active) + { + continue; + } + + if (channel->cyclic && (status & SUNXI_DMA_IRQ_PACKAGE)) + { + rt_dma_chan_done(&channel->parent, channel->period_size); + } + else if (!channel->cyclic && (status & SUNXI_DMA_IRQ_QUEUE)) + { + HWREG32(sunxi_dma_channel_base(channel) + + SUNXI_DMA_CHANNEL_ENABLE) = 0; + channel->active = RT_FALSE; + sunxi_dma_sync_for_cpu(channel); + rt_dma_chan_done(&channel->parent, channel->transfer_size); + } + } + } +} + +static void sunxi_dma_release(struct sunxi_dma *dma) +{ + if (dma->irq_installed) + { + rt_hw_interrupt_mask(dma->irq); + rt_pic_detach_irq(dma->irq, dma); + } + if (!rt_is_err_or_null(dma->mbus_clk)) + { + rt_clk_disable_unprepare(dma->mbus_clk); + rt_clk_put(dma->mbus_clk); + } + if (!rt_is_err_or_null(dma->bus_clk)) + { + rt_clk_disable_unprepare(dma->bus_clk); + rt_clk_put(dma->bus_clk); + } + if (!rt_is_err_or_null(dma->reset)) + { + rt_reset_control_assert(dma->reset); + rt_reset_control_put(dma->reset); + } + if (dma->base) + { + rt_iounmap(dma->base); + } + rt_free(dma->channels); + rt_free(dma); +} + +static rt_err_t sunxi_dma_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct sunxi_dma *dma = rt_calloc(1, sizeof(*dma)); + rt_size_t index; + + if (!dma) + { + return -RT_ENOMEM; + } + dma->irq = -1; + dma->channels_nr = SUNXI_DMA_MAX_CHANNELS; + dma->requests_nr = SUNXI_DMA_MAX_REQUESTS; + + dma->base = rt_dm_dev_iomap(dev, 0); + if (!dma->base) + { + err = -RT_EIO; + goto _fail; + } + dma->bus_clk = rt_clk_get_by_index(dev, 0); + dma->mbus_clk = rt_clk_get_by_index(dev, 1); + dma->reset = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(dma->bus_clk) || rt_is_err(dma->mbus_clk) || rt_is_err(dma->reset)) + { + err = rt_is_err(dma->bus_clk) ? rt_ptr_err(dma->bus_clk) : + (rt_is_err(dma->mbus_clk) ? rt_ptr_err(dma->mbus_clk) : + rt_ptr_err(dma->reset)); + goto _fail; + } + if (rt_dm_dev_prop_read_u32(dev, "dma-channels", &dma->channels_nr) || + !dma->channels_nr || dma->channels_nr > SUNXI_DMA_MAX_CHANNELS || + rt_dm_dev_prop_read_u32(dev, "dma-requests", &dma->requests_nr) || + !dma->requests_nr || dma->requests_nr > SUNXI_DMA_MAX_REQUESTS) + { + err = -RT_EINVAL; + goto _fail; + } + if ((dma->irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = dma->irq; + goto _fail; + } + if ((err = rt_reset_control_deassert(dma->reset)) || + (err = rt_clk_prepare_enable(dma->bus_clk)) || + (err = rt_clk_prepare_enable(dma->mbus_clk))) + { + goto _fail; + } + + dma->channels = rt_calloc(dma->channels_nr, sizeof(*dma->channels)); + if (!dma->channels) + { + err = -RT_ENOMEM; + goto _fail; + } + for (index = 0; index < dma->channels_nr; ++index) + { + dma->channels[index].dma = dma; + dma->channels[index].id = index; + } + + dma->parent.dev = dev; + dma->parent.ops = &sunxi_dma_ops; + rt_dma_controller_set_addr_mask(&dma->parent, RT_DMA_ADDR_MASK(32)); + rt_dma_controller_add_direction(&dma->parent, RT_DMA_MEM_TO_MEM); + rt_dma_controller_add_direction(&dma->parent, RT_DMA_MEM_TO_DEV); + rt_dma_controller_add_direction(&dma->parent, RT_DMA_DEV_TO_MEM); + if ((err = rt_dma_controller_register(&dma->parent))) + { + goto _fail; + } + + rt_hw_interrupt_install(dma->irq, sunxi_dma_isr, dma, "sunxi-dma"); + dma->irq_installed = RT_TRUE; + rt_hw_interrupt_umask(dma->irq); + dev->user_data = dma; + return RT_EOK; + +_fail: + sunxi_dma_release(dma); + return err; +} + +static rt_err_t sunxi_dma_remove(struct rt_platform_device *pdev) +{ + struct sunxi_dma *dma = pdev->parent.user_data; + + if (rt_dma_controller_unregister(&dma->parent)) + { + return -RT_EBUSY; + } + sunxi_dma_release(dma); + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_dma_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-dma" }, + { .compatible = "allwinner,sun20i-d1-dma" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_dma_driver = +{ + .name = "sunxi-dma", + .ids = sunxi_dma_ofw_ids, + .probe = sunxi_dma_probe, + .remove = sunxi_dma_remove, +}; + +/* Register the DMA provider before devices request slave channels. */ +static int sunxi_dma_driver_register(void) +{ + return rt_platform_driver_register(&sunxi_dma_driver); +} +INIT_SUBSYS_EXPORT(sunxi_dma_driver_register); diff --git a/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-co6300.dtsi b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-co6300.dtsi new file mode 100644 index 000000000000..c8d59ae6ea69 --- /dev/null +++ b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-co6300.dtsi @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * OSPTEK AM319M262928ZS, a 262x928 AMOLED panel with a CO6300 controller. + * + * The module is a one-lane MIPI-DSI video panel. The complete wiring is: + * + * F101 pin Panel signal Note + * ------------ ---------------------- ----------------------------- + * GND GND/shield Common ground for panel/touch + * PD12 DSI-D0P MIPI DSI lane 0 positive + * PD13 DSI-D0N MIPI DSI lane 0 negative + * PD16 DSI-CKP MIPI DSI clock positive + * PD17 DSI-CKN MIPI DSI clock negative + * PB2 TP-SCL TWI2 SCL, 400 kHz + * PB3 TP-SDA TWI2 SDA, CST3530 address 0x58 + * + * PD14/PD15, PD18/PD19, and PD20/PD21 are DSI lanes D1, D2, and D3 + * respectively; they remain unconnected because this panel uses one lane. + * Route D0 and CK as short, impedance-controlled differential pairs. Do not + * use loose jumper wires for the MIPI DSI signals. + * + * Use the OSPTEK adapter circuit: 3.3 V supply, its pull-ups and reset RC. + * AMOLED has no backlight. Wire PD7 to TP-INT, PD8 to TP-RESET. + * PD9 drives VCI_EN, PD6 drives LCD-RESET; PD22 is reserved for TE. + * The ESP32-P4 example toggles VCI_EN low for 60 ms, high for 50 ms. + * Reset stays asserted through power-up, then is released for 120 ms before + * DCS commands (CO6300 reset timing, including a reset from sleep-out). + * TE is not consumed by the video-mode output driver. + * The timing and CO6300 init sequence are adapted from the OSPTEK reference + * project: https://github.com/osptek/amoled-3.19-262x928-mipi-co6300 + */ +&display0 { + pinctrl-names = "default"; + pinctrl-0 = <&dsi0_pd_pins>; + allwinner,output = "dsi"; + allwinner,tcon-dclk-div = <19>; + dsi,lanes = <1>; + dsi,format = "rgb888"; + enable-gpios = <&pio 3 9 PIN_ACTIVE_HIGH>; + reset-gpios = <&pio 3 6 PIN_ACTIVE_LOW>; + panel-power-off-delay-ms = <60>; + prepare-delay-ms = <50>; + reset-delay-ms = <10>; + init-delay-ms = <120>; + + /* DSI data type, post-command delay in ms, payload size, payload. */ + panel-init-sequence = [ + 15 00 02 fe 20 + 15 00 02 f4 5a + 15 00 02 f5 59 + 15 00 02 fe 80 + 15 00 02 03 00 + 15 00 02 fe 00 + 15 00 02 35 00 + 15 00 02 3a 77 + 39 00 05 2a 00 00 01 05 + 39 00 05 2b 00 00 03 9f + 15 00 02 53 20 + 15 00 02 51 ff + 15 00 02 63 ff + 05 3c 01 11 + 05 00 01 29 + ]; + + status = "okay"; + + panel-timing { + clock-frequency = <16000000>; + hactive = <262>; + hfront-porch = <32>; + hback-porch = <32>; + hsync-len = <4>; + vactive = <928>; + vfront-porch = <8>; + vback-porch = <8>; + vsync-len = <4>; + }; +}; + +/* + * CST3530 reset and interrupt match the adapter schematic. + */ +&i2c2_ctrl { + clock-frequency = <400000>; + + touchscreen@58 { + compatible = "cst,cst3530"; + reg = <0x58>; + interrupt-gpios = <&pio 3 7 PIN_ACTIVE_LOW>; + reset-gpios = <&pio 3 8 PIN_ACTIVE_LOW>; + touchscreen-size-x = <262>; + touchscreen-size-y = <928>; + status = "okay"; + }; +}; diff --git a/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-common.dtsi b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-common.dtsi new file mode 100644 index 000000000000..442a266eb50e --- /dev/null +++ b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-common.dtsi @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sun252i-f101.dtsi" + +/ { + model = "YuzukiNeko sun252i F101 board"; + compatible = "allwinner,sun252i-f101-yuzukineko", "allwinner,sun252i-f101"; + + reg_vcc_3v3: regulator-3v3 { + compatible = "regulator-fixed"; + regulator-name = "vcc-3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + cpu_opp_table: cpu-opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-480000000 { + opp-hz = /bits/ 64 <480000000>; + opp-suspend; + }; + + /* The boot firmware starts at 696 MHz; this is the highest CPU OPP. */ + opp-1008000000 { + opp-hz = /bits/ 64 <1008000000>; + }; + }; + + chosen { + #ifdef ARCH_RISCV_M_MODE + bootargs = "earlycon=uart8250,mmio32,0x02500400 console=ttyS1,115200n8"; + #elif defined(ARCH_MM_MMU) + bootargs = "earlycon=sbi console=ttyS1,115200n8 cma=4M coherent_pool=512K"; + #else + bootargs = "earlycon=sbi console=ttyS1,115200n8"; + #endif + stdout-path = "serial1:115200n8"; + }; + + memory@40000000 { + device_type = "memory"; + reg = <0x0 0x40000000 0x0 0x01000000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + #ifndef ARCH_RISCV_M_MODE + opensbi@40000000 { + reg = <0x0 0x40000000 0x0 0x000c0000>; + no-map; + }; + #endif + + #ifndef ARCH_MM_MMU + dma_pool: dma-pool@40c00000 { + compatible = "rt-thread,dma-pool", "shared-dma-pool"; + reg = <0x0 0x40c00000 0x0 0x00400000>; + }; + #endif + }; +}; + +&cpu0 { + operating-points-v2 = <&cpu_opp_table>; +}; + +/* USB Type-C debug UART on the board's SBU pins. */ +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pb_pins>; + status = "okay"; +}; + +/* The 40-pin header exposes this I2C controller on PB2/PB3. */ +&i2c2_ctrl { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pb_pins>; + status = "okay"; +}; + +/* The on-board microSD socket is a removable 3.3 V SD card slot. */ +&sdc0 { + vmmc-supply = <®_vcc_3v3>; + vqmmc-supply = <®_vcc_3v3>; + status = "okay"; +}; + +&riscv_wdt { + status = "okay"; +}; + +&thermal { + status = "okay"; +}; + +&rtc { + status = "okay"; +}; + +&gpadc { + status = "okay"; +}; + +&timer { + status = "okay"; +}; + +&hstimer { + status = "okay"; +}; + +&dma { + status = "okay"; +}; + +/* HPOUT is exposed directly; an external amplifier owns its enable signal. */ +&codec { + status = "okay"; +}; + +&ve { + status = "okay"; +}; + +&g2d { + status = "okay"; +}; + +/* The board boots from this 16 MiB SPI NOR device. */ +&spi0_ctrl { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pc_pins>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <100000000>; + spi-rx-bus-width = <1>; + spi-tx-bus-width = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + bootloader@0 { + label = "bootloader"; + reg = <0x000000 0x010000>; + read-only; + }; + + dtb@10000 { + label = "dtb"; + reg = <0x010000 0x040000>; + read-only; + }; + + opensbi@50000 { + label = "opensbi"; + reg = <0x050000 0x080000>; + read-only; + }; + + kernel@d0000 { + label = "kernel"; + reg = <0x0d0000 0x600000>; + read-only; + }; + + rootfs@6d0000 { + label = "rootfs"; + reg = <0x6d0000 0x830000>; + read-only; + }; + + overlay@f00000 { + label = "overlay"; + reg = <0xf00000 0x100000>; + }; + }; + }; +}; + +/* The Type-C connector is wired to the OTG controller in device mode. */ +&usb0 { + status = "okay"; +}; diff --git a/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-radxa-lcd-8hd.dtsi b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-radxa-lcd-8hd.dtsi new file mode 100644 index 000000000000..7365a045bb2e --- /dev/null +++ b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-radxa-lcd-8hd.dtsi @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Radxa LCD 8HD with the Raspberry Pi 4B 15-pin to 40-pin accessory, + * including its populated flex and auxiliary header. NOT the passive + * ROCK 5B 39-pin cable. Use only one display adapter at a time. + * + * 15-pin host connector, Raspberry Pi 4B J4 numbering: + * 1/4/7/10/13 GND; 2 PD15 D1N; 3 PD14 D1P; + * 5 PD17 CKN; 6 PD16 CKP; 8 PD13 D0N; 9 PD12 D0P; + * 11 PB2 SCL; 12 PB3 SDA; 14/15 VCC-3V3. + * + * Auxiliary accessory signals (identify by label, not assumed order): + * VCC-5V -> H2-20 VBUS; GND -> H2-19; + * BL-PWM and LCD-RESET -> H1-1 VCC-3V3, as in the Pi reference; + * TP-RST -> PD8; TP-INT -> PD7; seventh signal unused. + * No additional backlight converter, 1.8 V regulator or level shifter. + * Power from the board USB only; verify the total supply budget. + * + * Commands and timing: Radxa panel-radxa-dsi-dkms 1.0.0 Pi driver. + * The current Sunxi driver does not consume dsi,flags or the exit sequence. + * Clock programming and DSI video-mode compatibility still need validation. + */ +&display0 { + pinctrl-names = "default"; + pinctrl-0 = <&dsi0_pd_pins>; + allwinner,output = "dsi"; + /* Divider 4 needs a 283.428 MHz parent for exactly 70.857 MHz. */ + allwinner,tcon-dclk-div = <4>; + dsi,lanes = <2>; + dsi,format = "rgb888"; + /* VIDEO | VIDEO_HSE | CLOCK_NON_CONTINUOUS; init commands use LP. */ + dsi,flags = <0x405>; + + /* DSI data type, post-command delay in ms, payload size, payload. */ + panel-init-sequence = [ + 15 00 02 e0 00 + 15 00 02 e1 93 + 15 00 02 e2 65 + 15 00 02 e3 f8 + 15 00 02 80 03 + 15 00 02 e0 01 + 15 00 02 00 00 + 15 00 02 01 72 + 15 00 02 03 00 + 15 00 02 04 65 + 15 00 02 0c 74 + 15 00 02 17 00 + 15 00 02 18 b7 + 15 00 02 19 00 + 15 00 02 1a 00 + 15 00 02 1b b7 + 15 00 02 1c 00 + 15 00 02 24 fe + 15 00 02 37 19 + 15 00 02 38 05 + 15 00 02 39 00 + 15 00 02 3a 01 + 15 00 02 3b 01 + 15 00 02 3c 70 + 15 00 02 3d ff + 15 00 02 3e ff + 15 00 02 3f ff + 15 00 02 40 06 + 15 00 02 41 a0 + 15 00 02 43 1e + 15 00 02 44 0f + 15 00 02 45 28 + 15 00 02 4b 04 + 15 00 02 55 02 + 15 00 02 56 01 + 15 00 02 57 a9 + 15 00 02 58 0a + 15 00 02 59 0a + 15 00 02 5a 37 + 15 00 02 5b 19 + 15 00 02 5d 78 + 15 00 02 5e 63 + 15 00 02 5f 54 + 15 00 02 60 48 + 15 00 02 61 45 + 15 00 02 62 38 + 15 00 02 63 3d + 15 00 02 64 28 + 15 00 02 65 43 + 15 00 02 66 41 + 15 00 02 67 43 + 15 00 02 68 62 + 15 00 02 69 50 + 15 00 02 6a 57 + 15 00 02 6b 49 + 15 00 02 6c 44 + 15 00 02 6d 37 + 15 00 02 6e 23 + 15 00 02 6f 10 + 15 00 02 70 78 + 15 00 02 71 63 + 15 00 02 72 54 + 15 00 02 73 49 + 15 00 02 74 45 + 15 00 02 75 38 + 15 00 02 76 3d + 15 00 02 77 28 + 15 00 02 78 43 + 15 00 02 79 41 + 15 00 02 7a 43 + 15 00 02 7b 62 + 15 00 02 7c 50 + 15 00 02 7d 57 + 15 00 02 7e 49 + 15 00 02 7f 44 + 15 00 02 80 37 + 15 00 02 81 23 + 15 00 02 82 10 + 15 00 02 e0 02 + 15 00 02 00 47 + 15 00 02 01 47 + 15 00 02 02 45 + 15 00 02 03 45 + 15 00 02 04 4b + 15 00 02 05 4b + 15 00 02 06 49 + 15 00 02 07 49 + 15 00 02 08 41 + 15 00 02 09 1f + 15 00 02 0a 1f + 15 00 02 0b 1f + 15 00 02 0c 1f + 15 00 02 0d 1f + 15 00 02 0e 1f + 15 00 02 0f 5f + 15 00 02 10 5f + 15 00 02 11 57 + 15 00 02 12 77 + 15 00 02 13 35 + 15 00 02 14 1f + 15 00 02 15 1f + 15 00 02 16 46 + 15 00 02 17 46 + 15 00 02 18 44 + 15 00 02 19 44 + 15 00 02 1a 4a + 15 00 02 1b 4a + 15 00 02 1c 48 + 15 00 02 1d 48 + 15 00 02 1e 40 + 15 00 02 1f 1f + 15 00 02 20 1f + 15 00 02 21 1f + 15 00 02 22 1f + 15 00 02 23 1f + 15 00 02 24 1f + 15 00 02 25 5f + 15 00 02 26 5f + 15 00 02 27 57 + 15 00 02 28 77 + 15 00 02 29 35 + 15 00 02 2a 1f + 15 00 02 2b 1f + 15 00 02 58 40 + 15 00 02 59 00 + 15 00 02 5a 00 + 15 00 02 5b 10 + 15 00 02 5c 06 + 15 00 02 5d 40 + 15 00 02 5e 01 + 15 00 02 5f 02 + 15 00 02 60 30 + 15 00 02 61 01 + 15 00 02 62 02 + 15 00 02 63 03 + 15 00 02 64 6b + 15 00 02 65 05 + 15 00 02 66 0c + 15 00 02 67 73 + 15 00 02 68 09 + 15 00 02 69 03 + 15 00 02 6a 56 + 15 00 02 6b 08 + 15 00 02 6c 00 + 15 00 02 6d 04 + 15 00 02 6e 04 + 15 00 02 6f 88 + 15 00 02 70 00 + 15 00 02 71 00 + 15 00 02 72 06 + 15 00 02 73 7b + 15 00 02 74 00 + 15 00 02 75 f8 + 15 00 02 76 00 + 15 00 02 77 d5 + 15 00 02 78 2e + 15 00 02 79 12 + 15 00 02 7a 03 + 15 00 02 7b 00 + 15 00 02 7c 00 + 15 00 02 7d 03 + 15 00 02 7e 7b + 15 00 02 e0 04 + 15 00 02 00 0e + 15 00 02 02 b3 + 15 00 02 09 60 + 15 00 02 0e 2a + 15 00 02 36 59 + 15 00 02 e0 00 + 15 00 02 80 01 + 15 00 02 e0 00 + 05 78 01 11 + 05 00 01 29 + ]; + panel-exit-sequence = [ + 05 00 01 28 + 05 64 01 10 + ]; + + status = "okay"; + + panel-timing { + clock-frequency = <70857000>; + hactive = <800>; + hfront-porch = <40>; + hback-porch = <20>; + hsync-len = <20>; + vactive = <1280>; + vfront-porch = <30>; + vback-porch = <28>; + vsync-len = <4>; + }; +}; + +&i2c2_ctrl { + clock-frequency = <400000>; + + touchscreen@14 { + compatible = "goodix,gt911"; + reg = <0x14>; + tp-supply = <®_vcc_3v3>; + irq-gpios = <&pio 3 7 PIN_ACTIVE_HIGH>; + reset-gpios = <&pio 3 8 PIN_ACTIVE_HIGH>; + max-x = <800>; + max-y = <1280>; + status = "okay"; + }; +}; diff --git a/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-st77916.dtsi b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-st77916.dtsi new file mode 100644 index 000000000000..8e13b267c7b4 --- /dev/null +++ b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko-st77916.dtsi @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * External 360x360 ST77916 panel on SPI1. The complete signal wiring is: + * + * F101 pin Panel signal SPI1/QSPI function + * ------------ --------------------- ----------------- + * GND GND Common ground + * PD0 CS SPI1-CS0 + * PD1 SCL/CLK SPI1-CLK + * PD2 SDA/SDI/D0 SPI1-MOSI/QSPI-D0 + * PD3 SDO/D1 SPI1-MISO/QSPI-D1 + * PD5 D2/WP SPI1-QSPI-D2 + * PD4 D3/HOLD SPI1-QSPI-D3 + * PD6 RESET Active-low GPIO output + * PD7 TE Active-high GPIO input + * + * PD4 and PD5 are intentionally ordered D3 then D2 in the F101 pinmux; do + * not interchange the two panel wires. Commands use D0 and pixel data uses + * all four QSPI data lines, so the panel needs no separate D/C line. Supply + * VCC and the panel backlight according to the display module/adapter + * specification; this binding does not drive a backlight GPIO or PWM. All + * connected GPIO signal levels must be compatible with the panel's 3.3 V IO. + * PD0 through PD7 are dedicated to this panel while this overlay is enabled. + */ +&pio { + spi1_pd_qspi_pins: spi1-pd-qspi-pins { + pins = "PD0", "PD1", "PD2", "PD3", "PD4", "PD5"; + function = "spi1"; + }; +}; + +&spi1_ctrl { + allwinner,qspi-mode; + pinctrl-names = "default"; + pinctrl-0 = <&spi1_pd_qspi_pins>; + status = "okay"; + + display@0 { + compatible = "sitronix,st77916"; + reg = <0>; + /* Temporary signal-integrity baseline for the ST77916 bring-up. */ + spi-max-frequency = <10000000>; + /* Match the panel's proven ESP QSPI transaction: opcode 0x32. */ + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + reset-gpios = <&pio 3 6 PIN_ACTIVE_LOW>; + te-gpios = <&pio 3 7 PIN_ACTIVE_HIGH>; + rt-thread,defer-display-on; + width = <360>; + height = <360>; + format = "r5g6b5"; + + /* command, post-command delay in ms, payload size, payload */ + panel-init-sequence = [ + 36 00 01 00 + 3a 00 01 55 + f0 00 01 28 + f2 00 01 28 + 73 00 01 f0 + 7c 00 01 d1 + 83 00 01 e0 + 84 00 01 61 + f2 00 01 82 + f0 00 01 00 + f0 00 01 01 + f1 00 01 01 + b0 00 01 56 + b1 00 01 4d + b2 00 01 24 + b4 00 01 87 + b5 00 01 44 + b6 00 01 8b + b7 00 01 40 + b8 00 01 86 + ba 00 01 00 + bb 00 01 08 + bc 00 01 08 + bd 00 01 00 + c0 00 01 80 + c1 00 01 10 + c2 00 01 37 + c3 00 01 80 + c4 00 01 10 + c5 00 01 37 + c6 00 01 a9 + c7 00 01 41 + c8 00 01 01 + c9 00 01 a9 + ca 00 01 41 + cb 00 01 01 + d0 00 01 91 + d1 00 01 68 + d2 00 01 68 + f5 00 02 00 a5 + dd 00 01 4f + de 00 01 4f + f1 00 01 10 + f0 00 01 00 + f0 00 01 02 + e0 00 0e f0 0a 10 09 09 36 35 33 4a 29 15 15 2e 34 + e1 00 0e f0 0a 0f 08 08 05 34 33 4a 39 15 15 2d 33 + f0 00 01 10 + f3 00 01 10 + e0 00 01 07 + e1 00 01 00 + e2 00 01 00 + e3 00 01 00 + e4 00 01 e0 + e5 00 01 06 + e6 00 01 21 + e7 00 01 01 + e8 00 01 05 + e9 00 01 02 + ea 00 01 da + eb 00 01 00 + ec 00 01 00 + ed 00 01 0f + ee 00 01 00 + ef 00 01 00 + f8 00 01 00 + f9 00 01 00 + fa 00 01 00 + fb 00 01 00 + fc 00 01 00 + fd 00 01 00 + fe 00 01 00 + ff 00 01 00 + 60 00 01 40 + 61 00 01 04 + 62 00 01 00 + 63 00 01 42 + 64 00 01 d9 + 65 00 01 00 + 66 00 01 00 + 67 00 01 00 + 68 00 01 00 + 69 00 01 00 + 6a 00 01 00 + 6b 00 01 00 + 70 00 01 40 + 71 00 01 03 + 72 00 01 00 + 73 00 01 42 + 74 00 01 d8 + 75 00 01 00 + 76 00 01 00 + 77 00 01 00 + 78 00 01 00 + 79 00 01 00 + 7a 00 01 00 + 7b 00 01 00 + 80 00 01 48 + 81 00 01 00 + 82 00 01 06 + 83 00 01 02 + 84 00 01 d6 + 85 00 01 04 + 86 00 01 00 + 87 00 01 00 + 88 00 01 48 + 89 00 01 00 + 8a 00 01 08 + 8b 00 01 02 + 8c 00 01 d8 + 8d 00 01 04 + 8e 00 01 00 + 8f 00 01 00 + 90 00 01 48 + 91 00 01 00 + 92 00 01 0a + 93 00 01 02 + 94 00 01 da + 95 00 01 04 + 96 00 01 00 + 97 00 01 00 + 98 00 01 48 + 99 00 01 00 + 9a 00 01 0c + 9b 00 01 02 + 9c 00 01 dc + 9d 00 01 04 + 9e 00 01 00 + 9f 00 01 00 + a0 00 01 48 + a1 00 01 00 + a2 00 01 05 + a3 00 01 02 + a4 00 01 d5 + a5 00 01 04 + a6 00 01 00 + a7 00 01 00 + a8 00 01 48 + a9 00 01 00 + aa 00 01 07 + ab 00 01 02 + ac 00 01 d7 + ad 00 01 04 + ae 00 01 00 + af 00 01 00 + b0 00 01 48 + b1 00 01 00 + b2 00 01 09 + b3 00 01 02 + b4 00 01 d9 + b5 00 01 04 + b6 00 01 00 + b7 00 01 00 + b8 00 01 48 + b9 00 01 00 + ba 00 01 0b + bb 00 01 02 + bc 00 01 db + bd 00 01 04 + be 00 01 00 + bf 00 01 00 + c0 00 01 10 + c1 00 01 47 + c2 00 01 56 + c3 00 01 65 + c4 00 01 74 + c5 00 01 88 + c6 00 01 99 + c7 00 01 01 + c8 00 01 bb + c9 00 01 aa + d0 00 01 10 + d1 00 01 47 + d2 00 01 56 + d3 00 01 65 + d4 00 01 74 + d5 00 01 88 + d6 00 01 99 + d7 00 01 01 + d8 00 01 bb + d9 00 01 aa + f3 00 01 01 + f0 00 01 00 + 21 00 00 + 11 78 00 + ]; + status = "okay"; + }; +}; diff --git a/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko.dts b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko.dts new file mode 100644 index 000000000000..0089b6c210a0 --- /dev/null +++ b/bsp/allwinner/dm/dts/sun252i-f101-yuzukineko.dts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include "sun252i-f101-yuzukineko-common.dtsi" + +#ifdef SBC_YUZUKINEKO_ST77916 +#include "sun252i-f101-yuzukineko-st77916.dtsi" +#endif + +#ifdef SBC_YUZUKINEKO_CO6300 +#include "sun252i-f101-yuzukineko-co6300.dtsi" +#endif + +#ifdef SBC_YUZUKINEKO_RADXA_LCD_8HD +#include "sun252i-f101-yuzukineko-radxa-lcd-8hd.dtsi" +#endif diff --git a/bsp/allwinner/dm/dts/sun252i-f101.dtsi b/bsp/allwinner/dm/dts/sun252i-f101.dtsi new file mode 100644 index 000000000000..74d7f0e06252 --- /dev/null +++ b/bsp/allwinner/dm/dts/sun252i-f101.dtsi @@ -0,0 +1,751 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +/ { + #address-cells = <2>; + #size-cells = <2>; + compatible = "allwinner,sun252i-f101"; + interrupt-parent = <&plic>; + + aliases { + serial0 = &uart0; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + serial4 = &uart4; + serial5 = &uart5; + i2c0 = &i2c0_ctrl; + i2c1 = &i2c1_ctrl; + i2c2 = &i2c2_ctrl; + spi0 = &spi0_ctrl; + spi1 = &spi1_ctrl; + mmc0 = &sdc0; + mmc1 = &sdc2; + }; + + osc24M: oscillator-24M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "osc24M"; + }; + + rc16m: oscillator-16M { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <16000000>; + clock-output-names = "rc-16m"; + }; + + /* The reset registers live in the CCU window, shared with clocks. */ + ccu_reset: reset-controller { + compatible = "allwinner,sun252i-f101-ccu-reset"; + allwinner,ccu = <&ccu>; + #reset-cells = <1>; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + timebase-frequency = <24000000>; + + cpu0: cpu@0 { + compatible = "thead,c907", "riscv"; + device_type = "cpu"; + reg = <0>; + mmu-type = "riscv,sv32"; + clocks = <&cpupll_ccu CLK_RISCV>; + riscv,isa = "rv32imafdc"; + riscv,isa-base = "rv32i"; + riscv,cbom-block-size = <64>; + riscv,cboz-block-size = <64>; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "v", + "zicbom", "zicbop", "zicboz", "zicond", "zihintntl", + "zihintpause", "zawrs", "zfa", "zfbfmin", "zfh", "zba", + "zbb", "zbc", "zbs", "sscofpmf", "zvfbfmin", "zvfbfwma", + "svinval", "svnapot", "svpbmt", "xtheadc", "xtheadvdot"; + d-cache-block-size = <64>; + i-cache-block-size = <64>; + + cpu0_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + }; + + soc: soc { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + interrupt-parent = <&plic>; + + plic: interrupt-controller@10000000 { + compatible = "thead,c900-plic"; + reg = <0x0 0x10000000 0x0 0x4000000>; + interrupts-extended = <&cpu0_intc 11>, <&cpu0_intc 9>; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + riscv,max-priority = <7>; + riscv,ndev = <200>; + }; + + clint: timer@14000000 { + compatible = "thead,c900-clint"; + reg = <0x0 0x14000000 0x0 0x10000>; + interrupts-extended = <&cpu0_intc 7>, <&cpu0_intc 3>; + clint,has-no-64bit-mmio; + }; + + timer: timer@2050000 { + compatible = "allwinner,sun252i-f101-timer"; + reg = <0x0 0x02050000 0x0 0x100>; + interrupts = <59 IRQ_TYPE_LEVEL_HIGH>, + <60 IRQ_TYPE_LEVEL_HIGH>; + clock-frequency = <24000000>; + status = "disabled"; + }; + + hstimer: hstimer@3008000 { + compatible = "allwinner,sun252i-f101-hstimer"; + reg = <0x0 0x03008000 0x0 0x100>; + interrupts = <55 IRQ_TYPE_LEVEL_HIGH>, + <56 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu SUN252I_F101_CLK_BUS_HSTIMER>; + clock-names = "bus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_HSTIMER>; + reset-names = "bus"; + status = "disabled"; + }; + + g2d: g2d@5410000 { + compatible = "allwinner,sun252i-f101-g2d200", "allwinner,sunxi-g2d200"; + reg = <0x0 0x05410000 0x0 0x40000>; + interrupts = <111 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu SUN252I_F101_CLK_G2D>, + <&ccu SUN252I_F101_CLK_BUS_G2D>, + <&ccu SUN252I_F101_CLK_BUS_G2D_M>; + clock-names = "mod", "bus", "mbus"; + clock-frequency = <300000000>; + resets = <&ccu_reset SUN252I_F101_RST_BUS_G2D>; + reset-names = "bus"; + status = "disabled"; + }; + + codec: audio-codec@2030000 { + compatible = "allwinner,sun252i-f101-codec"; + reg = <0x0 0x02030000 0x0 0x400>; + dmas = <&dma 7>; + dma-names = "tx"; + clocks = <&ccu SUN252I_F101_CLK_AUDIO_CODEC_DAC>, + <&ccu SUN252I_F101_CLK_BUS_AUDIO_CODEC>; + clock-names = "dac", "bus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_AUDIO>; + reset-names = "bus"; + status = "disabled"; + }; + + i2s0: i2s@2032000 { + compatible = "allwinner,sun252i-f101-i2s"; + reg = <0x0 0x02032000 0x0 0x1000>; + dmas = <&dma 3>; + dma-names = "tx"; + clocks = <&ccu SUN252I_F101_CLK_I2S0>, + <&ccu SUN252I_F101_CLK_BUS_I2S0>; + clock-names = "mod", "bus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_I2S0>; + reset-names = "bus"; + pinctrl-names = "default"; + pinctrl-0 = <&i2s0_pe_pins>; + status = "disabled"; + }; + + owa: owa@2036000 { + compatible = "allwinner,sun252i-f101-owa"; + reg = <0x0 0x02036000 0x0 0x1000>; + dmas = <&dma 2>; + dma-names = "tx"; + clocks = <&ccu SUN252I_F101_CLK_OWA_TX>, + <&ccu SUN252I_F101_CLK_BUS_OWA>; + clock-names = "tx", "bus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_OWA>; + reset-names = "bus"; + pinctrl-names = "default"; + pinctrl-0 = <&owa_pe6_pin>; + status = "disabled"; + }; + + irrx: irrx@2087000 { + compatible = "allwinner,sun252i-f101-irrx"; + reg = <0x0 0x02087000 0x0 0x100>; + interrupts = <38 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu SUN252I_F101_CLK_IRRX>, + <&ccu SUN252I_F101_CLK_BUS_IRRX>; + clock-names = "mod", "bus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_IRRX>; + reset-names = "bus"; + pinctrl-names = "default"; + pinctrl-0 = <&irrx_pd7_pin>; + status = "disabled"; + }; + + rtc_ccu: rtc-ccu@3090000 { + compatible = "allwinner,sun252i-f101-rtc-ccu"; + reg = <0x0 0x03090000 0x0 0x330>; + clocks = <&osc24M>, <&rc16m>; + clock-names = "dcxo24M", "rc-16m"; + #clock-cells = <1>; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x0 0x0 0x03090000 0x0 0x330>; + + rtc: rtc@0 { + compatible = "allwinner,sun252i-f101-rtc"; + reg = <0x0 0x0 0x0 0x100>; + interrupts = <144 IRQ_TYPE_LEVEL_HIGH>; + status = "disabled"; + }; + }; + + cpupll_ccu: cpupll@2001000 { + compatible = "allwinner,sun252i-f101-cpupll"; + reg = <0x0 0x02001000 0x0 0x1000>; + #clock-cells = <1>; + }; + + ddrpll_ccu: ddrpll@2001000 { + compatible = "allwinner,sun252i-f101-ddrpll"; + reg = <0x0 0x02001000 0x0 0x1000>; + #clock-cells = <1>; + }; + + lpsram: memory-controller@2052000 { + compatible = "allwinner,sun252i-f101-lpsram"; + reg = <0x0 0x02052000 0x0 0x400>, + <0x0 0x03102000 0x0 0x100>, + <0x0 0x03103000 0x0 0x400>; + reg-names = "ctl", "master", "phy"; + status = "disabled"; + }; + + ccu: clock-controller@2001000 { + compatible = "allwinner,sun252i-f101-ccu"; + reg = <0x0 0x02001000 0x0 0x1000>; + clocks = <&osc24M>, <&rtc_ccu CLK_RTC_32K>, + <&rtc_ccu CLK_RTC_IOSC>; + clock-names = "hosc", "losc", "iosc"; + #clock-cells = <1>; + #reset-cells = <1>; + allwinner,apb0-rate = <100000000>; + allwinner,apb1-rate = <100000000>; + allwinner,apb-uart-rate = <24000000>; + allwinner,spi-rate = <100000000>; + allwinner,smhc-rate = <100000000>; + }; + + ve: video-codec@1c0e000 { + compatible = "allwinner,sun252i-f101-video-engine"; + reg = <0x0 0x01c0e000 0x0 0x2000>; + interrupts = <66 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu SUN252I_F101_CLK_BUS_VE>, + <&ccu SUN252I_F101_CLK_VE>, + <&ccu SUN252I_F101_CLK_BUS_VE_M>; + clock-names = "ahb", "mod", "ram"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_VE>; + allwinner,sram = <&ve_sram_section 1>; + status = "disabled"; + }; + + crypto: crypto@3040800 { + compatible = "allwinner,sun252i-f101-ce"; + reg = <0x0 0x03040800 0x0 0x400>; + interrupts = <52 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu SUN252I_F101_CLK_CE>, + <&ccu SUN252I_F101_CLK_BUS_CE>; + clock-names = "mod", "bus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_CE>; + reset-names = "bus"; + status = "okay"; + }; + + sid: efuse@3006000 { + compatible = "allwinner,sun252i-f101-sid"; + reg = <0x0 0x03006000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + status = "okay"; + + ths_calibration: thermal-sensor-calibration@14 { + reg = <0x14 0x4>; + }; + }; + + gpadc: adc@2009000 { + compatible = "allwinner,sun252i-f101-gpadc"; + reg = <0x0 0x02009000 0x0 0x100>; + interrupts = <57 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu SUN252I_F101_CLK_BUS_GPADC>; + clock-names = "bus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_GPADC>; + reset-names = "bus"; + allwinner,channel-count = <12>; + allwinner,sample-rate = <1000000>; + status = "disabled"; + }; + + tpadc: touchscreen@2009c00 { + compatible = "allwinner,sun252i-f101-tpadc"; + reg = <0x0 0x02009c00 0x0 0x100>; + interrupts = <62 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu SUN252I_F101_CLK_TPADC>, + <&ccu SUN252I_F101_CLK_BUS_TPADC>; + clock-names = "mod", "bus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_TPADC>; + reset-names = "bus"; + pinctrl-names = "default"; + pinctrl-0 = <&tpadc_pa_pins>; + touchscreen-size-x = <4096>; + touchscreen-size-y = <4096>; + status = "disabled"; + }; + + thermal: thermal-sensor@2009400 { + compatible = "allwinner,sun252i-f101-thermal"; + reg = <0x0 0x02009400 0x0 0x100>, + <0x0 0x02009000 0x0 0x100>; + reg-names = "ths", "gpadc"; + clocks = <&ccu SUN252I_F101_CLK_BUS_THS>, + <&ccu SUN252I_F101_CLK_BUS_GPADC>; + clock-names = "ths", "gpadc"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_THS>, + <&ccu_reset SUN252I_F101_RST_BUS_GPADC>; + reset-names = "ths", "gpadc"; + nvmem-cells = <&ths_calibration>; + nvmem-cell-names = "calibration"; + #thermal-sensor-cells = <0>; + status = "disabled"; + }; + + syscon: syscon@3000000 { + compatible = "allwinner,sun252i-f101-system-control", "syscon"; + reg = <0x0 0x03000000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x0 0x40000000>; + + ve_sram: sram@20000 { + compatible = "mmio-sram"; + reg = <0x20000 0xb000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x20000 0xb000>; + + ve_sram_section: sram-section@0 { + compatible = "allwinner,sun252i-f101-sram-ve"; + reg = <0x0 0xb000>; + }; + }; + }; + + pwm0: pwm@2000c00 { + compatible = "allwinner,sunxi-pwm-v201", + "allwinner,sun252i-f101-pwm"; + reg = <0x0 0x02000c00 0x0 0x400>; + clocks = <&ccu SUN252I_F101_CLK_BUS_PWM>; + resets = <&ccu_reset SUN252I_F101_RST_BUS_PWM>; + #pwm-cells = <3>; + status = "disabled"; + }; + + pio: pinctrl@2000000 { + compatible = "allwinner,sun252i-f101-pinctrl"; + reg = <0x0 0x02000000 0x0 0x500>; + interrupts-extended = <&plic 67 IRQ_TYPE_LEVEL_HIGH>, + <&plic 69 IRQ_TYPE_LEVEL_HIGH>, + <&plic 71 IRQ_TYPE_LEVEL_HIGH>, + <&plic 73 IRQ_TYPE_LEVEL_HIGH>, + <&plic 75 IRQ_TYPE_LEVEL_HIGH>, + <&plic 77 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu SUN252I_F101_CLK_APB0>; + #pinctrl-cells = <0>; + gpio-controller; + #gpio-cells = <3>; + interrupt-controller; + #interrupt-cells = <3>; + + uart0_pe_pins: uart0-pe-pins { + pins = "PE0", "PE1"; + function = "uart0"; + }; + + pwm0_pe2_pins: pwm0-pe2-pins { + pins = "PE2"; + function = "pwm0_0"; + }; + + pwm0_pe3_pins: pwm0-pe3-pins { + pins = "PE3"; + function = "pwm0_1"; + }; + + pwm0_pe4_pins: pwm0-pe4-pins { + pins = "PE4"; + function = "pwm0_2"; + }; + + pwm0_pe5_pins: pwm0-pe5-pins { + pins = "PE5"; + function = "pwm0_3"; + }; + + uart1_pb_pins: uart1-pb-pins { + pins = "PB0", "PB1"; + function = "uart1"; + }; + + tpadc_pa_pins: tpadc-pa-pins { + pins = "PA0", "PA1", "PA2", "PA3"; + function = "tpadc"; + }; + + irrx_pd7_pin: irrx-pd7-pin { + pins = "PD7"; + function = "irrx"; + }; + + i2s0_pe_pins: i2s0-pe-pins { + pins = "PE0", "PE1", "PE2", "PE3", "PE4"; + function = "i2s0"; + }; + + owa_pe6_pin: owa-pe6-pin { + pins = "PE6"; + function = "owa"; + }; + + i2c2_pb_pins: i2c2-pb-pins { + pins = "PB2", "PB3"; + function = "twi2"; + }; + + spi0_pc_pins: spi0-pc-pins { + pins = "PC0", "PC1", "PC2", "PC3", "PC4", "PC5"; + function = "spi0"; + }; + + spi1_pd_pins: spi1-pd-pins { + pins = "PD0", "PD1", "PD2", "PD3"; + function = "spi1"; + }; + + sdc0_pf_pins: sdc0-pf-pins { + pins = "PF0", "PF1", "PF2", "PF3", "PF4", "PF5"; + function = "sdc0"; + drive-strength = <40>; + bias-pull-up; + }; + + sdc2_pc_pins: sdc2-pins { + pins = "PC0", "PC1", "PC2", "PC3", "PC4", "PC5"; + function = "sdc2"; + drive-strength = <40>; + bias-pull-up; + }; + + lcd0_pd_data_pins: lcd0-pd-data-pins { + pins = "PD0", "PD1", "PD2", "PD3", "PD4", + "PD5", "PD6", "PD7", "PD8", "PD9", + "PD12", "PD13", "PD14", "PD15", "PD16", + "PD17", "PD18", "PD19", "PD20", "PD21"; + function = "lcd0"; + }; + + lcd0_pe_sync_pins: lcd0-pe-sync-pins { + pins = "PE6", "PE7"; + function = "lcd0"; + }; + + lvds0_pd_pins: lvds0-pd-pins { + pins = "PD12", "PD13", "PD14", "PD15", "PD16", + "PD17", "PD18", "PD19", "PD20", "PD21"; + function = "lvds0"; + }; + + dsi0_pd_pins: dsi0-pd-pins { + pins = "PD12", "PD13", "PD14", "PD15", "PD16", + "PD17", "PD18", "PD19", "PD20", "PD21"; + function = "dsi0"; + }; + }; + + uart0: serial@2500000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x02500000 0x0 0x400>; + interrupts = <2 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_UART0>, + <&ccu SUN252I_F101_CLK_BUS_UART0>; + clock-names = "baudclk", "apb_pclk"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_UART0>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart1: serial@2500400 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x02500400 0x0 0x400>; + interrupts = <3 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_UART1>, + <&ccu SUN252I_F101_CLK_BUS_UART1>; + clock-names = "baudclk", "apb_pclk"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_UART1>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart2: serial@2500800 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x02500800 0x0 0x400>; + interrupts = <4 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_UART2>, + <&ccu SUN252I_F101_CLK_BUS_UART2>; + clock-names = "baudclk", "apb_pclk"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_UART2>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart3: serial@2500c00 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x02500c00 0x0 0x400>; + interrupts = <5 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_UART3>, + <&ccu SUN252I_F101_CLK_BUS_UART3>; + clock-names = "baudclk", "apb_pclk"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_UART3>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart4: serial@2501000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x02501000 0x0 0x400>; + interrupts = <6 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_UART4>, + <&ccu SUN252I_F101_CLK_BUS_UART4>; + clock-names = "baudclk", "apb_pclk"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_UART4>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + uart5: serial@2501400 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x02501400 0x0 0x400>; + interrupts = <7 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_UART5>, + <&ccu SUN252I_F101_CLK_BUS_UART5>; + clock-names = "baudclk", "apb_pclk"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_UART5>; + reg-shift = <2>; + reg-io-width = <4>; + status = "disabled"; + }; + + i2c0_ctrl: i2c@2502000 { + compatible = "allwinner,sun252i-f101-i2c", + "allwinner,sun20i-d1-i2c", + "allwinner,sun8i-v536-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x0 0x02502000 0x0 0x400>; + interrupts = <9 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_TWI0>; + resets = <&ccu_reset SUN252I_F101_RST_BUS_TWI0>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1_ctrl: i2c@2502400 { + compatible = "allwinner,sun252i-f101-i2c", + "allwinner,sun20i-d1-i2c", + "allwinner,sun8i-v536-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x0 0x02502400 0x0 0x400>; + interrupts = <10 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_TWI1>; + resets = <&ccu_reset SUN252I_F101_RST_BUS_TWI1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c2_ctrl: i2c@2502800 { + compatible = "allwinner,sun252i-f101-i2c", + "allwinner,sun20i-d1-i2c", + "allwinner,sun8i-v536-i2c", + "allwinner,sun6i-a31-i2c"; + reg = <0x0 0x02502800 0x0 0x400>; + interrupts = <11 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_TWI2>; + resets = <&ccu_reset SUN252I_F101_RST_BUS_TWI2>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + dma: dma-controller@3002000 { + compatible = "allwinner,sun252i-f101-dma", + "allwinner,sun20i-d1-dma"; + reg = <0x0 0x03002000 0x0 0x1000>; + interrupts = <50 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&ccu SUN252I_F101_CLK_BUS_DMA>, + <&ccu SUN252I_F101_CLK_BUS_DMA_M>; + clock-names = "bus", "mbus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_DMA>; + dma-channels = <16>; + dma-requests = <48>; + #dma-cells = <1>; + status = "disabled"; + }; + + sdc0: mmc@4020000 { + compatible = "allwinner,sun252i-f101-mmc"; + reg = <0x0 0x04020000 0x0 0x1000>; + interrupts = <40 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_SMHC0>, + <&ccu SUN252I_F101_CLK_SMHC0>; + clock-names = "ahb", "mmc"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_SMHC0>; + reset-names = "ahb"; + pinctrl-names = "default"; + pinctrl-0 = <&sdc0_pf_pins>; + bus-width = <4>; + max-frequency = <50000000>; + cap-sd-highspeed; + no-1-8-v; + no-mmc; + no-sdio; + status = "disabled"; + }; + + sdc2: mmc@4022000 { + compatible = "allwinner,sun252i-f101-emmc"; + reg = <0x0 0x04022000 0x0 0x1000>; + interrupts = <42 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_SMHC2>, + <&ccu SUN252I_F101_CLK_SMHC2>; + clock-names = "ahb", "mmc"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_SMHC2>; + reset-names = "ahb"; + pinctrl-names = "default"; + pinctrl-0 = <&sdc2_pc_pins>; + bus-width = <4>; + max-frequency = <50000000>; + cap-mmc-highspeed; + no-1-8-v; + no-sd; + no-sdio; + non-removable; + status = "disabled"; + }; + + spi0_ctrl: spi@4025000 { + compatible = "allwinner,sun252i-f101-spi", "allwinner,sun50i-r329-spi"; + reg = <0x0 0x04025000 0x0 0x1000>; + interrupts = <15 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_SPI0>, + <&ccu SUN252I_F101_CLK_SPI0>; + clock-names = "ahb", "mod"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_SPI0>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000000>; + status = "disabled"; + }; + + spi1_ctrl: spi@4026000 { + compatible = "allwinner,sun252i-f101-spi", "allwinner,sun50i-r329-spi"; + reg = <0x0 0x04026000 0x0 0x1000>; + interrupts = <16 4>; + clocks = <&ccu SUN252I_F101_CLK_BUS_SPI1>, + <&ccu SUN252I_F101_CLK_SPI1>; + clock-names = "ahb", "mod"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_SPI1>; + #address-cells = <1>; + #size-cells = <0>; + clock-frequency = <100000000>; + status = "disabled"; + }; + + riscv_wdt: watchdog@6011000 { + compatible = "allwinner,sun252i-f101-wdt", + "allwinner,sun20i-d1-wdt"; + reg = <0x0 0x06011000 0x0 0x20>; + interrupts = <131 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&osc24M>, <&rtc_ccu CLK_RTC_32K>; + clock-names = "hosc", "losc"; + status = "disabled"; + }; + + usb0: usb@4100000 { + compatible = "allwinner,sun252i-f101-musb"; + reg = <0x0 0x04100000 0x0 0x1000>; + interrupts = <29 4>; + allwinner,syscon = <&syscon>; + clocks = <&ccu SUN252I_F101_CLK_BUS_USB>, + <&ccu SUN252I_F101_CLK_BUS_USBOTG0>; + clock-names = "bus", "mod"; + resets = <&ccu_reset SUN252I_F101_RST_USB_PHY0>, + <&ccu_reset SUN252I_F101_RST_USB_OTG0>; + reset-names = "phy", "controller"; + dr_mode = "peripheral"; + status = "disabled"; + }; + + display0: display@5000000 { + compatible = "allwinner,sun252i-f101-de0"; + reg = <0x0 0x05000000 0x0 0x200000>, + <0x0 0x05461000 0x0 0x1000>, + <0x0 0x05460000 0x0 0x1000>, + <0x0 0x05450000 0x0 0x2000>; + reg-names = "de", "tcon", "display-top", "dsi"; + clocks = <&ccu SUN252I_F101_CLK_DE>, + <&ccu SUN252I_F101_CLK_BUS_DE>, + <&ccu SUN252I_F101_CLK_BUS_DPSS_TOP>, + <&ccu SUN252I_F101_CLK_TCONLCD>, + <&ccu SUN252I_F101_CLK_BUS_TCONLCD>, + <&ccu SUN252I_F101_CLK_DSI>, + <&ccu SUN252I_F101_CLK_BUS_DSI>, + <&ccu SUN252I_F101_CLK_COMBOPHY0>, + <&ccu SUN252I_F101_CLK_BUS_COMBOPHY0>; + clock-names = "de", "de-bus", "dpss-top", "tcon", "tcon-bus", + "dsi", "dsi-bus", "combophy", "combophy-bus"; + resets = <&ccu_reset SUN252I_F101_RST_BUS_DE>, + <&ccu_reset SUN252I_F101_RST_BUS_DPSS>, + <&ccu_reset SUN252I_F101_RST_BUS_TCONLCD>, + <&ccu_reset SUN252I_F101_RST_BUS_DSI>, + <&ccu_reset SUN252I_F101_RST_BUS_LVDS0>; + reset-names = "de", "dpss-top", "tcon", "dsi", "lvds"; + /* rgb is the default; boards may select lvds or dsi. */ + status = "disabled"; + }; + }; +}; diff --git a/bsp/allwinner/dm/dvfs/SConscript b/bsp/allwinner/dm/dvfs/SConscript new file mode 100644 index 000000000000..07281ac7e76e --- /dev/null +++ b/bsp/allwinner/dm/dvfs/SConscript @@ -0,0 +1,21 @@ +from building import * + +group = [] + +if not GetDepend(['RT_USING_DVFS']): + Return('group') + +cwd = GetCurrentDir() +list = os.listdir(cwd) +CPPPATH = [cwd + '/../include'] + +src = [] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + group = group + SConscript(os.path.join(d, 'SConscript')) + +Return('group') diff --git a/bsp/allwinner/dm/dvfs/cpufreq/Kconfig b/bsp/allwinner/dm/dvfs/cpufreq/Kconfig new file mode 100644 index 000000000000..9435cdd6dad9 --- /dev/null +++ b/bsp/allwinner/dm/dvfs/cpufreq/Kconfig @@ -0,0 +1,8 @@ +config RT_DVFS_SUN252I_F101_CPUFREQ + bool "Allwinner Sun252i F101 CPUfreq driver" + depends on RT_USING_DVFS + default y + help + Register the C907 CPU as a frequency-only DVFS device. The CPU PLL + supports linear frequency modulation; YuzukiNeko OPPs are supplied by + its device tree and do not require a controllable CPU regulator. diff --git a/bsp/allwinner/dm/dvfs/cpufreq/SConscript b/bsp/allwinner/dm/dvfs/cpufreq/SConscript new file mode 100644 index 000000000000..168c7e107c1e --- /dev/null +++ b/bsp/allwinner/dm/dvfs/cpufreq/SConscript @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: Apache-2.0 + +from building import * + +cwd = GetCurrentDir() +src = [] + +if GetDepend(['RT_DVFS_SUN252I_F101_CPUFREQ']): + src += ['dvfs-sun252i-f101-cpufreq.c'] + +group = DefineGroup('DeviceDrivers', src, depend=[''], CPPPATH=[cwd + '/../../include']) + +Return('group') diff --git a/bsp/allwinner/dm/dvfs/cpufreq/dvfs-sun252i-f101-cpufreq.c b/bsp/allwinner/dm/dvfs/cpufreq/dvfs-sun252i-f101-cpufreq.c new file mode 100644 index 000000000000..b84cc2051881 --- /dev/null +++ b/bsp/allwinner/dm/dvfs/cpufreq/dvfs-sun252i-f101-cpufreq.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#define DBG_TAG "dvfs.f101-cpu" +#define DBG_LVL DBG_INFO +#include + +static struct rt_device sun252i_f101_cpufreq_dev; +static struct rt_dvfs_cpufreq sun252i_f101_cpufreq; + +static int sun252i_f101_cpufreq_init(void) +{ + rt_err_t err; + struct rt_clk *clk; + struct rt_dvfs_scaling *scaling; + struct rt_ofw_node *cpu_np; + + cpu_np = rt_ofw_get_next_cpu_node(RT_NULL); + if (!cpu_np) + { + return 0; + } + + sun252i_f101_cpufreq_dev.ofw_node = cpu_np; + if ((err = rt_device_register(&sun252i_f101_cpufreq_dev, "cpufreq0", + RT_DEVICE_FLAG_DEACTIVATE))) + { + LOG_W("register cpufreq device failed: %s", rt_strerror(err)); + rt_ofw_node_put(cpu_np); + sun252i_f101_cpufreq_dev.ofw_node = RT_NULL; + return 0; + } + + if (rt_is_err_or_null(clk = rt_clk_get_by_index(&sun252i_f101_cpufreq_dev, 0))) + { + LOG_W("CPU clock is unavailable"); + rt_device_unregister(&sun252i_f101_cpufreq_dev); + rt_ofw_node_put(cpu_np); + sun252i_f101_cpufreq_dev.ofw_node = RT_NULL; + return 0; + } + + scaling = rt_dvfs_cpufreq_to_scaling(&sun252i_f101_cpufreq); + scaling->dev = &sun252i_f101_cpufreq_dev; + scaling->clk = clk; + scaling->ops = &rt_dvfs_devfreq_ops; + scaling->transition_latency = 100000; + sun252i_f101_cpufreq.master_cpu = 0; + rt_bitmap_set_bit(sun252i_f101_cpufreq.cpus_map, 0); + + /* Keep the bootloader-selected OPP until a governor makes a decision. */ + scaling->cur_freq = rt_clk_get_rate(clk); + + if ((err = rt_dvfs_cpufreq_register(&sun252i_f101_cpufreq))) + { + LOG_W("register cpufreq failed: %s", rt_strerror(err)); + rt_clk_put(clk); + rt_device_unregister(&sun252i_f101_cpufreq_dev); + rt_ofw_node_put(cpu_np); + sun252i_f101_cpufreq_dev.ofw_node = RT_NULL; + return 0; + } + + if ((err = rt_dvfs_scaling_set_governor(scaling, + RT_DVFS_GOVERNOR_TYPE_ONDEMAND))) + { + LOG_W("set ondemand governor failed: %s", rt_strerror(err)); + rt_dvfs_scaling_set_governor(scaling, RT_DVFS_GOVERNOR_TYPE_PERFORMANCE); + } + + LOG_D("C907 CPUfreq ready: %lu Hz", scaling->cur_freq); + return 0; +} +INIT_APP_EXPORT(sun252i_f101_cpufreq_init); diff --git a/bsp/allwinner/dm/graphic/SConscript b/bsp/allwinner/dm/graphic/SConscript new file mode 100644 index 000000000000..6d2d49efcc4c --- /dev/null +++ b/bsp/allwinner/dm/graphic/SConscript @@ -0,0 +1,5 @@ +from building import * + +objs = SConscript('framebuffer/SConscript') + +Return('objs') diff --git a/bsp/allwinner/dm/graphic/framebuffer/Kconfig b/bsp/allwinner/dm/graphic/framebuffer/Kconfig new file mode 100644 index 000000000000..afdb07ae845b --- /dev/null +++ b/bsp/allwinner/dm/graphic/framebuffer/Kconfig @@ -0,0 +1,17 @@ +config RT_GRAPHIC_FB_SUNXI_DE + bool "Allwinner Sunxi DE0/TCON0 framebuffer" + depends on RT_USING_GRAPHIC + depends on RT_USING_OFW + depends on RT_USING_CLK + depends on RT_USING_RESET + depends on RT_USING_PINCTRL + depends on RT_USING_HEAP + default n + help + Enable the Allwinner DE0 and TCON0 framebuffer. The F101 backend can + drive a parallel RGB, single-link LVDS, or MIPI-DSI video + output selected by allwinner,output in the device tree. + The enabled device tree node must supply display-timings, a default + pinctrl state, and allwinner,tcon-dclk-div. A MIPI-DSI panel must use + rgb888 video mode and describe dsi,lanes. Panel power and backlight + remain board concerns. diff --git a/bsp/allwinner/dm/graphic/framebuffer/SConscript b/bsp/allwinner/dm/graphic/framebuffer/SConscript new file mode 100644 index 000000000000..5920beb8029d --- /dev/null +++ b/bsp/allwinner/dm/graphic/framebuffer/SConscript @@ -0,0 +1,11 @@ +from building import * + +src = [] + +if GetDepend(['RT_GRAPHIC_FB_SUNXI_DE']): + src += ['fb-sunxi-de.c', 'sunxi-display-output.c'] + + +group = DefineGroup('DeviceDrivers', src, depend=['']) + +Return('group') diff --git a/bsp/allwinner/dm/graphic/framebuffer/fb-sunxi-de.c b/bsp/allwinner/dm/graphic/framebuffer/fb-sunxi-de.c new file mode 100644 index 000000000000..ee3c0a95e8ac --- /dev/null +++ b/bsp/allwinner/dm/graphic/framebuffer/fb-sunxi-de.c @@ -0,0 +1,734 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "fb.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include + +#include "sunxi-display-output.h" + +#define SUNXI_DE_TOP_RESET 0x0010 +#define SUNXI_DE_TOP_AHB_GATE 0x0014 +#define SUNXI_DE_TOP_MBUS_GATE 0x0018 +#define SUNXI_DE_TOP_CLK_GATE 0x001c +#define SUNXI_DE_GLB_CTL 0x0100 +#define SUNXI_DE_GLB_SIZE 0x0108 +#define SUNXI_DE_UI0 0x161000 +#define SUNXI_DE_UI_ATTR 0x0000 +#define SUNXI_DE_UI_SIZE 0x0004 +#define SUNXI_DE_UI_COORD 0x0008 +#define SUNXI_DE_UI_PITCH 0x000c +#define SUNXI_DE_UI_LADDR 0x0010 +#define SUNXI_DE_UI_HADDR 0x0080 +#define SUNXI_DE_BLD 0x1c1000 +#define SUNXI_DE_BLD_FCOLOR_CTL 0x0000 +#define SUNXI_DE_BLD_PIPE0_FCOLOR 0x0004 +#define SUNXI_DE_BLD_PIPE0_SIZE 0x0008 +#define SUNXI_DE_BLD_PIPE0_OFFSET 0x000c +#define SUNXI_DE_BLD_ROUTE 0x007c +#define SUNXI_DE_BLD_PREMULTIPLY 0x0080 +#define SUNXI_DE_BLD_BKCOLOR 0x0084 +#define SUNXI_DE_BLD_OUTPUT_SIZE 0x0088 +#define SUNXI_DE_BLD_MODE0 0x008c + +#define SUNXI_TCON_GCTL 0x0000 +#define SUNXI_TCON_GINT0 0x0004 +#define SUNXI_TCON0_CTL 0x0040 +#define SUNXI_TCON0_DCLK 0x0044 +#define SUNXI_TCON0_BASIC0 0x0048 +#define SUNXI_TCON0_BASIC1 0x004c +#define SUNXI_TCON0_BASIC2 0x0050 +#define SUNXI_TCON0_BASIC3 0x0054 +#define SUNXI_TCON0_HV_CTL 0x0058 +#define SUNXI_TCON0_CPU_CTL 0x0060 +#define SUNXI_TCON0_IO_POL 0x0088 +#define SUNXI_TCON0_IO_TRI 0x008c +#define SUNXI_TCON_ECC_FIFO 0x00f8 +#define SUNXI_TCON0_CPU_TRI0 0x0160 +#define SUNXI_TCON0_CPU_TRI1 0x0164 +#define SUNXI_TCON0_CPU_TRI2 0x0168 +#define SUNXI_TCON_SAFE_PERIOD 0x01f0 + +#define SUNXI_TCON_TOP_TV_SETUP 0x0000 +#define SUNXI_TCON_TOP_DE_PERH 0x001c + +#define SUNXI_DE_FMT_ARGB8888 0 +#define SUNXI_DE_FMT_ABGR8888 1 +#define SUNXI_DE_UI_CHAN 3 + +struct sunxi_fb +{ + struct rt_graphic_device parent; + + struct rt_device *dev; + struct rt_clk_array *clks; + struct rt_reset_control *resets; + void *de; + void *tcon; + void *top; + void *dsi; + struct sunxi_fb_timing timing; + struct sunxi_display_output output; + rt_uint32_t dclk_div; + rt_uint32_t rgb_swap; + rt_uint32_t tcon_ctl; + rt_ubase_t framebuffer_dma; + rt_bool_t rb_swap; + rt_bool_t clks_enabled; +}; + +static const rt_uint32_t sunxi_fb_modes[] = +{ + RTGRAPHIC_PIXEL_FORMAT_ARGB888, + RTGRAPHIC_PIXEL_FORMAT_ABGR888, +}; + +static rt_err_t sunxi_fb_timing_read_u32(struct rt_ofw_node *np, + const char *prop, rt_uint32_t *value) +{ + int count = rt_ofw_prop_count_of_u32(np, prop); + + if (count <= 0) + { + return count ? (rt_err_t)count : -RT_ENOENT; + } + + /* The timing binding permits ; use its typical value. */ + return rt_ofw_prop_read_u32_index(np, prop, count >= 3 ? 1 : 0, value); +} + +static rt_err_t sunxi_fb_timing_parse_node(struct rt_ofw_node *np, + struct sunxi_fb_timing *timing) +{ + rt_uint32_t value; + rt_uint64_t htotal; + rt_uint64_t vtotal; + + rt_memset(timing, 0, sizeof(*timing)); + + if (sunxi_fb_timing_read_u32(np, "clock-frequency", &timing->pixelclock) || + sunxi_fb_timing_read_u32(np, "hactive", &timing->hactive) || + sunxi_fb_timing_read_u32(np, "hfront-porch", &timing->hfront_porch) || + sunxi_fb_timing_read_u32(np, "hback-porch", &timing->hback_porch) || + sunxi_fb_timing_read_u32(np, "hsync-len", &timing->hsync_len) || + sunxi_fb_timing_read_u32(np, "vactive", &timing->vactive) || + sunxi_fb_timing_read_u32(np, "vfront-porch", &timing->vfront_porch) || + sunxi_fb_timing_read_u32(np, "vback-porch", &timing->vback_porch) || + sunxi_fb_timing_read_u32(np, "vsync-len", &timing->vsync_len)) + { + return -RT_EINVAL; + } + + timing->hsync_active = RT_TRUE; + timing->vsync_active = RT_TRUE; + timing->pixelclk_active = RT_TRUE; + + if (!rt_ofw_prop_read_u32(np, "hsync-active", &value)) + { + timing->hsync_active = !!value; + } + if (!rt_ofw_prop_read_u32(np, "vsync-active", &value)) + { + timing->vsync_active = !!value; + } + if (!rt_ofw_prop_read_u32(np, "pixelclk-active", &value)) + { + timing->pixelclk_active = !!value; + } + + htotal = (rt_uint64_t)timing->hactive + timing->hfront_porch + + timing->hback_porch + timing->hsync_len; + vtotal = (rt_uint64_t)timing->vactive + timing->vfront_porch + + timing->vback_porch + timing->vsync_len; + + if (!timing->pixelclock || !timing->hactive || !timing->vactive || + !timing->hsync_len || !timing->vsync_len || + timing->hactive > 4096 || timing->vactive > 4096 || + timing->hsync_len > 1024 || timing->vsync_len > 1024 || + htotal > 8192 || vtotal > 4095 || + timing->hsync_len + timing->hback_porch > 4096 || + timing->vsync_len + timing->vback_porch > 4096) + { + return -RT_EINVAL; + } + + timing->htotal = (rt_uint32_t)htotal; + timing->vtotal = (rt_uint32_t)vtotal; + + return RT_EOK; +} + +static rt_err_t sunxi_fb_timing_parse(struct sunxi_fb *fb) +{ + rt_err_t err = -RT_ENOENT; + struct rt_ofw_node *child; + struct rt_ofw_node *timings; + struct rt_ofw_node *np = fb->dev->ofw_node; + + if (!(err = sunxi_fb_timing_parse_node(np, &fb->timing))) + { + return RT_EOK; + } + + child = rt_ofw_get_child_by_tag(np, "panel-timing"); + if (child) + { + err = sunxi_fb_timing_parse_node(child, &fb->timing); + rt_ofw_node_put(child); + + if (!err) + { + return RT_EOK; + } + } + + timings = rt_ofw_get_child_by_tag(np, "display-timings"); + if (!timings) + { + return err; + } + + child = rt_ofw_parse_phandle(timings, "native-mode", 0); + if (child) + { + err = sunxi_fb_timing_parse_node(child, &fb->timing); + rt_ofw_node_put(child); + + if (!err) + { + rt_ofw_node_put(timings); + return RT_EOK; + } + } + + rt_ofw_foreach_available_child_node(timings, child) + { + err = sunxi_fb_timing_parse_node(child, &fb->timing); + + if (!err) + { + rt_ofw_node_put(child); + rt_ofw_node_put(timings); + return RT_EOK; + } + } + + rt_ofw_node_put(timings); + + return err; +} + +static rt_uint32_t sunxi_fb_de_format(rt_uint32_t mode) +{ + switch (mode) + { + case RTGRAPHIC_PIXEL_FORMAT_ARGB888: + return SUNXI_DE_FMT_ARGB8888; + + case RTGRAPHIC_PIXEL_FORMAT_ABGR888: + return SUNXI_DE_FMT_ABGR8888; + + default: + return RT_UINT32_MAX; + } +} + +static void sunxi_fb_program_plane(struct sunxi_fb *fb, + struct rt_graphic_plane *plane, rt_ubase_t dma) +{ + rt_uint32_t format = sunxi_fb_de_format(plane->mode); + rt_uint32_t attr; + rt_uint32_t size; + + attr = RT_BIT(0) | (format << 8) | (0xff << 24); + size = (plane->width - 1) | ((plane->height - 1) << 16); + + HWREG32(fb->de + SUNXI_DE_UI0 + SUNXI_DE_UI_SIZE) = size; + HWREG32(fb->de + SUNXI_DE_UI0 + SUNXI_DE_UI_COORD) = 0; + HWREG32(fb->de + SUNXI_DE_UI0 + SUNXI_DE_UI_PITCH) = plane->line_length; + HWREG32(fb->de + SUNXI_DE_UI0 + SUNXI_DE_UI_LADDR) = (rt_uint32_t)dma; + HWREG32(fb->de + SUNXI_DE_UI0 + SUNXI_DE_UI_HADDR) = 0; + HWREG32(fb->de + SUNXI_DE_UI0 + SUNXI_DE_UI_ATTR) = attr; +} + +static void sunxi_fb_program_de(struct sunxi_fb *fb) +{ + const struct sunxi_fb_timing *timing = &fb->timing; + rt_uint32_t size = (timing->hactive - 1) | + ((timing->vactive - 1) << 16); + + HWREG32(fb->de + SUNXI_DE_UI0 + SUNXI_DE_UI_ATTR) = 0; + HWREG32(fb->de + SUNXI_DE_TOP_RESET) |= RT_BIT(0); + HWREG32(fb->de + SUNXI_DE_TOP_AHB_GATE) |= RT_BIT(0); + HWREG32(fb->de + SUNXI_DE_TOP_MBUS_GATE) |= RT_BIT(0); + HWREG32(fb->de + SUNXI_DE_TOP_CLK_GATE) |= RT_BIT(0); + HWREG32(fb->de + SUNXI_DE_GLB_SIZE) = size; + HWREG32(fb->de + SUNXI_DE_BLD + SUNXI_DE_BLD_FCOLOR_CTL) = + RT_BIT(0) | RT_BIT(8); + HWREG32(fb->de + SUNXI_DE_BLD + SUNXI_DE_BLD_PIPE0_FCOLOR) = 0xff000000; + HWREG32(fb->de + SUNXI_DE_BLD + SUNXI_DE_BLD_PIPE0_SIZE) = size; + HWREG32(fb->de + SUNXI_DE_BLD + SUNXI_DE_BLD_PIPE0_OFFSET) = 0; + HWREG32(fb->de + SUNXI_DE_BLD + SUNXI_DE_BLD_ROUTE) = SUNXI_DE_UI_CHAN; + HWREG32(fb->de + SUNXI_DE_BLD + SUNXI_DE_BLD_PREMULTIPLY) = 0; + HWREG32(fb->de + SUNXI_DE_BLD + SUNXI_DE_BLD_BKCOLOR) = 0xff000000; + HWREG32(fb->de + SUNXI_DE_BLD + SUNXI_DE_BLD_OUTPUT_SIZE) = size; + HWREG32(fb->de + SUNXI_DE_BLD + SUNXI_DE_BLD_MODE0) = 0x00010001; + HWREG32(fb->de + SUNXI_DE_GLB_CTL) = RT_BIT(0); +} + +static void sunxi_fb_route_de_to_tcon(struct sunxi_fb *fb) +{ + rt_uint32_t value; + + /* DISPLAY_TOP routes DE port 0 to TCON 0 through bits [1:0]. */ + value = HWREG32(fb->top + SUNXI_TCON_TOP_DE_PERH); + value &= ~0x3; + HWREG32(fb->top + SUNXI_TCON_TOP_DE_PERH) = value; + + /* RGB and LVDS leave TCON 0 through the LCD pads. */ + if (fb->output.type != SUNXI_DISPLAY_OUTPUT_DSI) + { + HWREG32(fb->top + SUNXI_TCON_TOP_TV_SETUP) &= ~RT_BIT(8); + } +} + +static void sunxi_fb_program_tcon(struct sunxi_fb *fb) +{ + const struct sunxi_fb_timing *timing = &fb->timing; + rt_uint32_t hbp = timing->hsync_len + timing->hback_porch; + rt_uint32_t vbp = timing->vsync_len + timing->vback_porch; + rt_int32_t start_delay; + rt_uint32_t dclk_div = fb->dclk_div; + rt_uint32_t safe_period; + rt_uint32_t io_pol = 0; + rt_uint32_t ctl; + + if (timing->hsync_active) + { + io_pol |= RT_BIT(25); + } + if (timing->vsync_active) + { + io_pol |= RT_BIT(24); + } + if (!timing->pixelclk_active) + { + io_pol |= RT_BIT(26); + } + + /* F101's TCON interrupt flags are cleared by writing zero. */ + HWREG32(fb->tcon + SUNXI_TCON_GCTL) = RT_BIT(1); + HWREG32(fb->tcon + SUNXI_TCON_GINT0) = 0; + HWREG32(fb->tcon + SUNXI_TCON0_CTL) = 0; + HWREG32(fb->tcon + SUNXI_TCON0_DCLK) = dclk_div | (0xf << 28); + HWREG32(fb->tcon + SUNXI_TCON0_BASIC0) = + (timing->vactive - 1) | ((timing->hactive - 1) << 16); + HWREG32(fb->tcon + SUNXI_TCON0_BASIC1) = + (hbp - 1) | ((timing->htotal - 1) << 16); + HWREG32(fb->tcon + SUNXI_TCON0_BASIC2) = + (vbp - 1) | ((timing->vtotal * 2) << 16); + HWREG32(fb->tcon + SUNXI_TCON0_BASIC3) = + (timing->vsync_len - 1) | ((timing->hsync_len - 1) << 16); + HWREG32(fb->tcon + SUNXI_TCON0_HV_CTL) = RT_BIT(19); + HWREG32(fb->tcon + SUNXI_TCON0_IO_POL) = io_pol; + HWREG32(fb->tcon + SUNXI_TCON0_IO_TRI) = 0; + + start_delay = (rt_int32_t)timing->vtotal - timing->vactive - 8; + if (start_delay < 10) + { + start_delay = 10; + } + else if (start_delay > 31) + { + start_delay = 31; + } + safe_period = timing->pixelclock / 1000000 * 15; + if (safe_period > 0x1fffU) + { + safe_period = 0x1fffU; + } + HWREG32(fb->tcon + SUNXI_TCON_SAFE_PERIOD) = + 3 | (safe_period << 16); + + if (fb->output.type == SUNXI_DISPLAY_OUTPUT_DSI) + { + rt_uint32_t vbp = timing->vsync_len + timing->vback_porch; + rt_uint32_t delay_lines = vbp > timing->vsync_len + 3 ? 3 : vbp; + rt_uint32_t tri_start_delay = delay_lines * timing->htotal / 8; + + if (tri_start_delay) + { + tri_start_delay--; + } + + /* TCON0 produces one RGB888 video line request for every active line. */ + HWREG32(fb->tcon + SUNXI_TCON0_CPU_CTL) = (1 << 28) | RT_BIT(26) | + RT_BIT(16) | RT_BIT(2) | RT_BIT(0); + HWREG32(fb->tcon + SUNXI_TCON_ECC_FIFO) = 1 << 3; + HWREG32(fb->tcon + SUNXI_TCON0_CPU_TRI0) = + (timing->hactive - 1) | + (((timing->htotal - timing->hactive) / 2) << 16); + HWREG32(fb->tcon + SUNXI_TCON0_CPU_TRI1) = timing->vactive - 1; + HWREG32(fb->tcon + SUNXI_TCON0_CPU_TRI2) = + (tri_start_delay << 16) | 10; + } + + ctl = ((rt_uint32_t)start_delay << 4) | (fb->rgb_swap << 16) | + (fb->rb_swap ? RT_BIT(23) : 0) | + RT_BIT(31); + if (fb->output.type == SUNXI_DISPLAY_OUTPUT_DSI) + { + ctl |= RT_BIT(24); + } + fb->tcon_ctl = ctl; + + sunxi_fb_route_de_to_tcon(fb); +} + +static void sunxi_fb_enable_tcon(struct sunxi_fb *fb) +{ + HWREG32(fb->tcon + SUNXI_TCON0_CTL) = fb->tcon_ctl; + HWREG32(fb->tcon + SUNXI_TCON_GCTL) = RT_BIT(1) | RT_BIT(31); +} + +static void sunxi_fb_hw_disable(struct sunxi_fb *fb) +{ + sunxi_display_output_disable(&fb->output); + + if (fb->de) + { + HWREG32(fb->de + SUNXI_DE_UI0 + SUNXI_DE_UI_ATTR) = 0; + HWREG32(fb->de + SUNXI_DE_GLB_CTL) = 0; + HWREG32(fb->de + SUNXI_DE_TOP_CLK_GATE) &= ~RT_BIT(0); + HWREG32(fb->de + SUNXI_DE_TOP_MBUS_GATE) &= ~RT_BIT(0); + HWREG32(fb->de + SUNXI_DE_TOP_AHB_GATE) &= ~RT_BIT(0); + HWREG32(fb->de + SUNXI_DE_TOP_RESET) &= ~RT_BIT(0); + } + + if (fb->tcon) + { + HWREG32(fb->tcon + SUNXI_TCON0_CTL) = 0; + HWREG32(fb->tcon + SUNXI_TCON0_DCLK) = 0; + HWREG32(fb->tcon + SUNXI_TCON_GCTL) = 0; + } +} + +static rt_err_t sunxi_fb_plane_update(struct rt_graphic_plane *plane, + struct rt_device_rect_info *rect) +{ + struct sunxi_fb *fb = rt_container_of(plane->graphic, struct sunxi_fb, parent); + RT_UNUSED(rect); + + if (!plane->framebuffer || !fb->framebuffer_dma) + { + return -RT_EINVAL; + } + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, plane->framebuffer, plane->screen_len); + sunxi_fb_program_plane(fb, plane, fb->framebuffer_dma); + + return RT_EOK; +} + +static rt_err_t sunxi_fb_plane_fb_remap(struct rt_graphic_plane *plane, + rt_uint32_t mode, struct rt_device_rect_info *rect) +{ + struct sunxi_fb *fb = rt_container_of(plane->graphic, struct sunxi_fb, parent); + rt_uint32_t bpp = rt_graphic_mode_bpp(mode); + rt_size_t screen_len; + rt_ubase_t dma; + void *buffer; + + if (!rect->width || !rect->height || !bpp || + rect->width != fb->timing.hactive || rect->height != fb->timing.vactive || + sunxi_fb_de_format(mode) == RT_UINT32_MAX) + { + return -RT_EINVAL; + } + + plane->line_length = RT_ALIGN(rect->width * bpp, 64) / 8; + screen_len = plane->line_length * rect->height; + buffer = rt_malloc_align(screen_len, 64); + if (!buffer) + { + return -RT_ENOMEM; + } + + dma = (rt_ubase_t)rt_kmem_v2p(buffer); + if ((rt_uint64_t)dma > RT_UINT32_MAX) + { + rt_free_align(buffer); + + return -RT_EINVAL; + } + + rt_memset(buffer, 0, screen_len); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, buffer, screen_len); + + plane->bits_per_pixel = bpp; + plane->framebuffer = buffer; + plane->screen_len = screen_len; + plane->framebuffer_len = screen_len; + plane->width = rect->width; + plane->height = rect->height; + fb->framebuffer_dma = dma; + sunxi_fb_program_plane(fb, plane, dma); + + return RT_EOK; +} + +static rt_err_t sunxi_fb_plane_fb_cleanup(struct rt_graphic_plane *plane) +{ + struct sunxi_fb *fb = rt_container_of(plane->graphic, struct sunxi_fb, parent); + + if (fb->de) + { + HWREG32(fb->de + SUNXI_DE_UI0 + SUNXI_DE_UI_ATTR) = 0; + } + + if (plane->framebuffer) + { + rt_free_align(plane->framebuffer); + plane->framebuffer = RT_NULL; + fb->framebuffer_dma = 0; + plane->screen_len = 0; + plane->framebuffer_len = 0; + } + + return RT_EOK; +} + +static const struct rt_graphic_plane_ops sunxi_fb_plane_ops = +{ + .update = sunxi_fb_plane_update, + .fb_remap = sunxi_fb_plane_fb_remap, + .fb_cleanup = sunxi_fb_plane_fb_cleanup, +}; + +static void sunxi_fb_resource_put(struct sunxi_fb *fb) +{ + if (!rt_is_err_or_null(fb->resets)) + { + rt_reset_control_put(fb->resets); + } + if (!rt_is_err_or_null(fb->clks)) + { + rt_clk_array_put(fb->clks); + } + if (fb->tcon) + { + rt_iounmap(fb->tcon); + } + if (fb->dsi) + { + rt_iounmap(fb->dsi); + } + if (fb->top) + { + rt_iounmap(fb->top); + } + if (fb->de) + { + rt_iounmap(fb->de); + } +} + +static rt_err_t sunxi_fb_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint32_t refresh_hz; + rt_uint64_t frame_clocks; + struct rt_device *dev = &pdev->parent; + struct sunxi_fb *fb = rt_calloc(1, sizeof(*fb)); + + if (!fb) + { + return -RT_ENOMEM; + } + + fb->dev = dev; + + if ((err = sunxi_display_output_parse(dev, &fb->output))) + { + goto _fail; + } + + if ((err = sunxi_fb_timing_parse(fb))) + { + LOG_E("%s: valid display timing is required", rt_dm_dev_get_name(dev)); + goto _fail; + } + + if ((err = rt_dm_dev_prop_read_u32(dev, "allwinner,tcon-dclk-div", + &fb->dclk_div)) || fb->dclk_div > 0x7f) + { + LOG_E("%s: valid tcon dclk divider is required", rt_dm_dev_get_name(dev)); + err = -RT_EINVAL; + goto _fail; + } + + rt_dm_dev_prop_read_u32(dev, "allwinner,rgb-swap", &fb->rgb_swap); + fb->rb_swap = rt_dm_dev_prop_read_bool(dev, "allwinner,rb-swap"); + if (fb->rgb_swap > 7) + { + err = -RT_EINVAL; + goto _fail; + } + + fb->de = rt_ofw_iomap_by_name(dev->ofw_node, "de"); + fb->tcon = rt_ofw_iomap_by_name(dev->ofw_node, "tcon"); + fb->top = rt_ofw_iomap_by_name(dev->ofw_node, "display-top"); + if (!fb->de || !fb->tcon || !fb->top) + { + LOG_E("%s: DE, TCON, and display-top resources are required", + rt_dm_dev_get_name(dev)); + err = -RT_EIO; + goto _fail; + } + + fb->output.tcon = fb->tcon; + fb->output.top = fb->top; + if (fb->output.type != SUNXI_DISPLAY_OUTPUT_RGB) + { + fb->dsi = rt_ofw_iomap_by_name(dev->ofw_node, "dsi"); + if (!fb->dsi) + { + LOG_E("%s: output needs the dsi register resource", rt_dm_dev_get_name(dev)); + err = -RT_EIO; + goto _fail; + } + fb->output.dsi = fb->dsi; + fb->output.dphy = fb->dsi + 0x1000; + } + fb->clks = rt_clk_get_array(dev); + if (rt_is_err(fb->clks)) + { + err = rt_ptr_err(fb->clks); + goto _fail; + } + if (!fb->clks) + { + err = -RT_ENOENT; + goto _fail; + } + + fb->resets = rt_reset_control_get_array(dev); + if (rt_is_err(fb->resets)) + { + err = rt_ptr_err(fb->resets); + goto _fail; + } + if (!fb->resets) + { + err = -RT_ENOENT; + goto _fail; + } + + if ((err = rt_pin_ctrl_confs_apply_by_name(dev, RT_NULL))) + { + LOG_E("%s: default pinctrl state is required", rt_dm_dev_get_name(dev)); + goto _fail; + } + + if ((err = rt_clk_array_prepare_enable(fb->clks))) + { + goto _fail; + } + fb->clks_enabled = RT_TRUE; + + if ((err = rt_reset_control_deassert(fb->resets))) + { + goto _fail; + } + + sunxi_fb_program_de(fb); + sunxi_fb_program_tcon(fb); + if ((err = sunxi_display_output_prepare(&fb->output, &fb->timing))) + { + goto _fail; + } + if ((err = sunxi_display_output_panel_init(&fb->output))) + { + LOG_E("%s: panel initialization failed: %s", rt_dm_dev_get_name(dev), + rt_strerror(err)); + goto _fail; + } + sunxi_fb_enable_tcon(fb); + sunxi_display_output_enable(&fb->output); + + frame_clocks = (rt_uint64_t)fb->timing.htotal * fb->timing.vtotal; + refresh_hz = frame_clocks ? fb->timing.pixelclock / frame_clocks : 0; + + if ((err = rt_graphic_device_simple_register(&fb->parent, + fb->timing.hactive, fb->timing.vactive, refresh_hz, + &sunxi_fb_plane_ops, sunxi_fb_modes, RT_ARRAY_SIZE(sunxi_fb_modes)))) + { + goto _fail; + } + + dev->user_data = fb; + + return RT_EOK; + +_fail: + sunxi_fb_hw_disable(fb); + if (!rt_is_err_or_null(fb->resets)) + { + rt_reset_control_assert(fb->resets); + } + if (fb->clks_enabled) + { + rt_clk_array_disable_unprepare(fb->clks); + } + sunxi_fb_resource_put(fb); + rt_free(fb); + + return err; +} + +static rt_err_t sunxi_fb_remove(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct sunxi_fb *fb = pdev->parent.user_data; + + if ((err = rt_graphic_device_simple_unregister(&fb->parent))) + { + return err; + } + sunxi_fb_hw_disable(fb); + rt_reset_control_assert(fb->resets); + + if (fb->clks_enabled) + { + rt_clk_array_disable_unprepare(fb->clks); + } + + sunxi_fb_resource_put(fb); + rt_free(fb); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_fb_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-de0" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_fb_driver = +{ + .name = "sunxi-de", + .ids = sunxi_fb_ofw_ids, + .probe = sunxi_fb_probe, + .remove = sunxi_fb_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_fb_driver); diff --git a/bsp/allwinner/dm/graphic/framebuffer/sunxi-display-output.c b/bsp/allwinner/dm/graphic/framebuffer/sunxi-display-output.c new file mode 100644 index 000000000000..1282e88a58bc --- /dev/null +++ b/bsp/allwinner/dm/graphic/framebuffer/sunxi-display-output.c @@ -0,0 +1,783 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "disp.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include "sunxi-display-output.h" + +#define SUNXI_TCON0_LVDS_CTL 0x0084 + +#define SUNXI_TOP_DSI_SRC 0x0004 +#define SUNXI_TOP_LCD_CLK_SRC 0x000c +#define SUNXI_TOP_MODULE_GATE 0x0020 + +#define SUNXI_DSI_GCTL 0x0000 +#define SUNXI_DSI_BASIC_CTL 0x000c +#define SUNXI_DSI_BASIC_CTL0 0x0010 +#define SUNXI_DSI_BASIC_CTL1 0x0014 +#define SUNXI_DSI_BASIC_SIZE0 0x0018 +#define SUNXI_DSI_BASIC_SIZE1 0x001c +#define SUNXI_DSI_INST_FUNC0 0x0020 +#define SUNXI_DSI_INST_LOOP_SEL 0x0040 +#define SUNXI_DSI_INST_LOOP_NUM 0x0044 +#define SUNXI_DSI_INST_JUMP_SEL 0x0048 +#define SUNXI_DSI_INST_JUMP_CFG 0x004c +#define SUNXI_DSI_INST_LOOP_NUM2 0x0054 +#define SUNXI_DSI_TRANS_START 0x0060 +#define SUNXI_DSI_TCON_DRQ 0x007c +#define SUNXI_DSI_PIXEL_CTL0 0x0080 +#define SUNXI_DSI_PIXEL_PH 0x0090 +#define SUNXI_DSI_PIXEL_PF0 0x0098 +#define SUNXI_DSI_PIXEL_PF1 0x009c +#define SUNXI_DSI_SYNC_HSS 0x00b0 +#define SUNXI_DSI_SYNC_HSE 0x00b4 +#define SUNXI_DSI_SYNC_VSS 0x00b8 +#define SUNXI_DSI_SYNC_VSE 0x00bc +#define SUNXI_DSI_BLK_HSA0 0x00c0 +#define SUNXI_DSI_BLK_HSA1 0x00c4 +#define SUNXI_DSI_BLK_HBP0 0x00c8 +#define SUNXI_DSI_BLK_HBP1 0x00cc +#define SUNXI_DSI_BLK_HFP0 0x00d0 +#define SUNXI_DSI_BLK_HFP1 0x00d4 +#define SUNXI_DSI_BLK_HBLK0 0x00e0 +#define SUNXI_DSI_BLK_HBLK1 0x00e4 +#define SUNXI_DSI_BLK_VBLK0 0x00e8 +#define SUNXI_DSI_BLK_VBLK1 0x00ec +#define SUNXI_DSI_CMD_CTL 0x0200 +#define SUNXI_DSI_CMD_CTL_TX_FLAG RT_BIT(9) +#define SUNXI_DSI_CMD_TX(n) (0x0300 + (n) * 4) + +#define SUNXI_DPHY_GCTL 0x0000 +#define SUNXI_DPHY_ANA1 0x0050 +#define SUNXI_DPHY_ANA2 0x0054 +#define SUNXI_DPHY_ANA3 0x0058 +#define SUNXI_DPHY_ANA4 0x005c +#define SUNXI_DPHY_PLL0 0x0104 +#define SUNXI_DPHY_PLL1 0x0108 +#define SUNXI_DPHY_PLL2 0x010c +#define SUNXI_DPHY_COMBO0 0x0110 +#define SUNXI_DPHY_COMBO1 0x0114 +#define SUNXI_DPHY_COMBO2 0x0118 + +#define SUNXI_DSI_DT_VSS 0x01 +#define SUNXI_DSI_DT_VSE 0x11 +#define SUNXI_DSI_DT_HSS 0x21 +#define SUNXI_DSI_DT_HSE 0x31 +#define SUNXI_DSI_DT_BLK 0x19 +#define SUNXI_DSI_DT_DCS_SHORT_WRITE 0x05 +#define SUNXI_DSI_DT_DCS_SHORT_WRITE_PARAM 0x15 +#define SUNXI_DSI_DT_DCS_LONG_WRITE 0x39 +#define SUNXI_DSI_DT_RGB888 0x3e + +#define SUNXI_DSI_INST_LP11 0 +#define SUNXI_DSI_INST_TBA 1 +#define SUNXI_DSI_INST_HSC 2 +#define SUNXI_DSI_INST_HSD 3 +#define SUNXI_DSI_INST_LPDT 4 +#define SUNXI_DSI_INST_HSCEXIT 5 +#define SUNXI_DSI_INST_NOP 6 +#define SUNXI_DSI_INST_DLY 7 +#define SUNXI_DSI_INST_END 15 + +#define SUNXI_DSI_MODE_STOP 0 +#define SUNXI_DSI_MODE_TBA 1 +#define SUNXI_DSI_MODE_HS 2 +#define SUNXI_DSI_MODE_ESCAPE 3 +#define SUNXI_DSI_MODE_HSCEXIT 4 +#define SUNXI_DSI_MODE_NOP 5 + +static rt_uint8_t sunxi_dsi_parity(rt_uint32_t value) +{ + value ^= value >> 16; + value ^= value >> 8; + value ^= value >> 4; + value ^= value >> 2; + value ^= value >> 1; + + return value & 1; +} + +/* MIPI DSI uses a shortened Hamming code over the three header bytes. */ +static rt_uint8_t sunxi_dsi_ecc(rt_uint32_t header) +{ + static const rt_uint32_t masks[] = + { + RT_BIT(0) | RT_BIT(1) | RT_BIT(2) | RT_BIT(4) | RT_BIT(5) | + RT_BIT(7) | RT_BIT(10) | RT_BIT(11) | RT_BIT(13) | RT_BIT(16) | + RT_BIT(20) | RT_BIT(21) | RT_BIT(22) | RT_BIT(23), + RT_BIT(0) | RT_BIT(1) | RT_BIT(3) | RT_BIT(4) | RT_BIT(6) | + RT_BIT(8) | RT_BIT(10) | RT_BIT(12) | RT_BIT(14) | RT_BIT(17) | + RT_BIT(20) | RT_BIT(21) | RT_BIT(22), + RT_BIT(0) | RT_BIT(2) | RT_BIT(3) | RT_BIT(5) | RT_BIT(6) | + RT_BIT(9) | RT_BIT(11) | RT_BIT(12) | RT_BIT(15) | RT_BIT(18) | + RT_BIT(20) | RT_BIT(21) | RT_BIT(22), + RT_BIT(1) | RT_BIT(2) | RT_BIT(3) | RT_BIT(7) | RT_BIT(8) | + RT_BIT(9) | RT_BIT(13) | RT_BIT(14) | RT_BIT(15) | RT_BIT(19) | + RT_BIT(20) | RT_BIT(22) | RT_BIT(23), + RT_BIT(4) | RT_BIT(5) | RT_BIT(6) | RT_BIT(7) | RT_BIT(8) | + RT_BIT(9) | RT_BIT(16) | RT_BIT(17) | RT_BIT(18) | RT_BIT(19) | + RT_BIT(20) | RT_BIT(22) | RT_BIT(23), + RT_BIT(10) | RT_BIT(11) | RT_BIT(12) | RT_BIT(13) | RT_BIT(14) | + RT_BIT(15) | RT_BIT(16) | RT_BIT(17) | RT_BIT(18) | RT_BIT(19) | + RT_BIT(21) | RT_BIT(22) | RT_BIT(23), + }; + rt_uint8_t ecc = 0; + rt_size_t index; + + for (index = 0; index < RT_ARRAY_SIZE(masks); index++) + { + ecc |= sunxi_dsi_parity(header & masks[index]) << index; + } + + return ecc; +} + +static rt_uint16_t sunxi_dsi_crc(const rt_uint8_t *data, rt_size_t length) +{ + rt_uint16_t crc = 0xffff; + rt_uint32_t bit; + + while (length--) + { + crc ^= *data++; + for (bit = 0; bit < 8; bit++) + { + crc = (crc >> 1) ^ ((crc & 1) ? 0x8408 : 0); + } + } + + return crc; +} + +static rt_uint16_t sunxi_dsi_crc_zeroes(rt_uint32_t count) +{ + rt_uint16_t crc = 0xffff; + rt_uint32_t bit; + + while (count--) + { + for (bit = 0; bit < 8; bit++) + { + crc = (crc >> 1) ^ ((crc & 1) ? 0x8408 : 0); + } + } + + return crc; +} + +static rt_uint32_t sunxi_dsi_header(rt_uint8_t type, rt_uint16_t word_count) +{ + rt_uint32_t header = type | ((rt_uint32_t)word_count << 8); + + return header | ((rt_uint32_t)sunxi_dsi_ecc(header) << 24); +} + +static rt_uint32_t sunxi_dsi_inst(rt_uint8_t mode, rt_uint8_t packet, + rt_bool_t clock_lane, rt_uint8_t data_lanes) +{ + return data_lanes | (clock_lane ? RT_BIT(4) : 0) | + ((rt_uint32_t)packet << 20) | ((rt_uint32_t)mode << 28); +} + +static void sunxi_dsi_block(void *dsi, rt_uint32_t header_reg, + rt_uint32_t footer_reg, rt_uint16_t word_count) +{ + HWREG32(dsi + header_reg) = sunxi_dsi_header(SUNXI_DSI_DT_BLK, word_count); + HWREG32(dsi + footer_reg) = (rt_uint32_t)sunxi_dsi_crc_zeroes(word_count) << 16; +} + +static void sunxi_dsi_abort(void *dsi) +{ + HWREG32(dsi + SUNXI_DSI_BASIC_CTL0) &= ~RT_BIT(0); +} + +static void sunxi_dsi_commit(void *dsi) +{ + HWREG32(dsi + SUNXI_DSI_BASIC_CTL0) |= RT_BIT(0); +} + +static rt_err_t sunxi_dsi_wait_for_completion(void *dsi) +{ + rt_uint32_t timeout = 500; + + while (HWREG32(dsi + SUNXI_DSI_BASIC_CTL0) & RT_BIT(0)) + { + if (!timeout--) + { + return -RT_ETIMEOUT; + } + + rt_hw_us_delay(10); + } + + return RT_EOK; +} + +static void sunxi_dsi_start_lptx(void *dsi) +{ + HWREG32(dsi + SUNXI_DSI_INST_JUMP_SEL) = + (SUNXI_DSI_INST_LPDT << (4 * SUNXI_DSI_INST_LP11)) | + (SUNXI_DSI_INST_END << (4 * SUNXI_DSI_INST_LPDT)); + sunxi_dsi_abort(dsi); + sunxi_dsi_commit(dsi); +} + +static rt_err_t sunxi_dsi_dcs_write(struct sunxi_display_output *output, + rt_uint8_t type, const rt_uint8_t *payload, rt_size_t length) +{ + void *dsi = output->dsi; + rt_uint16_t crc; + rt_uint32_t value; + rt_size_t index; + rt_size_t byte; + rt_uint8_t data; + rt_err_t err; + + if (!payload || !length) + { + return -RT_EINVAL; + } + + if ((type == SUNXI_DSI_DT_DCS_SHORT_WRITE && length != 1) || + (type == SUNXI_DSI_DT_DCS_SHORT_WRITE_PARAM && length != 2) || + (type == SUNXI_DSI_DT_DCS_LONG_WRITE && length < 3) || + (type != SUNXI_DSI_DT_DCS_SHORT_WRITE && + type != SUNXI_DSI_DT_DCS_SHORT_WRITE_PARAM && + type != SUNXI_DSI_DT_DCS_LONG_WRITE)) + { + return -RT_EINVAL; + } + + err = sunxi_dsi_wait_for_completion(dsi); + if (err) + { + sunxi_dsi_abort(dsi); + } + + HWREG32(dsi + SUNXI_DSI_CMD_CTL) = SUNXI_DSI_CMD_CTL_TX_FLAG; + if (type == SUNXI_DSI_DT_DCS_LONG_WRITE) + { + HWREG32(dsi + SUNXI_DSI_CMD_TX(0)) = sunxi_dsi_header(type, length); + crc = sunxi_dsi_crc(payload, length); + + for (index = 0; index < length + sizeof(crc); index += sizeof(value)) + { + value = 0; + for (byte = 0; byte < sizeof(value) && + index + byte < length + sizeof(crc); byte++) + { + data = index + byte < length ? payload[index + byte] : + ((const rt_uint8_t *)&crc)[index + byte - length]; + + value |= (rt_uint32_t)data << (byte * 8); + } + HWREG32(dsi + SUNXI_DSI_CMD_TX(1 + index / sizeof(value))) = value; + } + HWREG32(dsi + SUNXI_DSI_CMD_CTL) = length + sizeof(crc) + 4 - 1; + } + else + { + value = payload[0]; + if (length == 2) + { + value |= (rt_uint32_t)payload[1] << 8; + } + HWREG32(dsi + SUNXI_DSI_CMD_TX(0)) = sunxi_dsi_header(type, value); + HWREG32(dsi + SUNXI_DSI_CMD_CTL) = 4 - 1; + } + + sunxi_dsi_start_lptx(dsi); + err = sunxi_dsi_wait_for_completion(dsi); + if (err) + { + sunxi_dsi_abort(dsi); + } + + return err; +} + +static rt_err_t sunxi_display_output_parse_dsi(struct rt_device *dev, + struct sunxi_display_output *output) +{ + const char *format = "rgb888"; + rt_ssize_t length; + rt_uint32_t lanes; + rt_err_t err; + const char *delay_props[] = + { + "panel-power-off-delay-ms", "prepare-delay-ms", + "reset-delay-ms", "init-delay-ms", + }; + rt_uint32_t *delays[] = + { + &output->panel_power_off_delay_ms, &output->panel_prepare_delay_ms, + &output->panel_reset_delay_ms, &output->panel_init_delay_ms, + }; + + output->dsi_lanes = 4; + if (!rt_dm_dev_prop_read_u32(dev, "dsi,lanes", &lanes)) + { + if (lanes < 1 || lanes > 4) + { + return -RT_EINVAL; + } + output->dsi_lanes = lanes; + } + if (output->dsi_lanes < 1 || output->dsi_lanes > 4) + { + return -RT_EINVAL; + } + + if (!rt_ofw_prop_read_string(dev->ofw_node, "dsi,format", &format) && + rt_strcmp(format, "rgb888")) + { + LOG_E("%s: only rgb888 DSI video is supported", rt_dm_dev_get_name(dev)); + return -RT_ENOSYS; + } + + output->panel_init_sequence = rt_ofw_prop_read_raw(dev->ofw_node, + "panel-init-sequence", &length); + if (output->panel_init_sequence && length > 0) + { + output->panel_init_sequence_len = length; + } + + if (rt_dm_dev_prop_read_bool(dev, "enable-gpios")) + { + output->panel_enable_pin = rt_pin_get_named_pin(dev, "enable", 0, + RT_NULL, &output->panel_enable_active); + if (output->panel_enable_pin < 0) + { + return output->panel_enable_pin; + } + } + if (rt_dm_dev_prop_read_bool(dev, "reset-gpios")) + { + output->panel_reset_pin = rt_pin_get_named_pin(dev, "reset", 0, + RT_NULL, &output->panel_reset_active); + if (output->panel_reset_pin < 0) + { + return output->panel_reset_pin; + } + } + for (rt_size_t i = 0; i < RT_ARRAY_SIZE(delay_props); ++i) + { + if (!rt_dm_dev_prop_read_bool(dev, delay_props[i])) + { + continue; + } + err = rt_dm_dev_prop_read_u32(dev, delay_props[i], delays[i]); + if (err || *delays[i] > 10000) + { + return err ? err : -RT_EINVAL; + } + } + + return RT_EOK; +} + +rt_err_t sunxi_display_output_parse(struct rt_device *dev, + struct sunxi_display_output *output) +{ + const char *type = "rgb"; + + rt_memset(output, 0, sizeof(*output)); + output->panel_enable_pin = PIN_NONE; + output->panel_reset_pin = PIN_NONE; + output->panel_reset_delay_ms = 10; + output->panel_init_delay_ms = 120; + + if (!rt_ofw_prop_read_string(dev->ofw_node, "allwinner,output", &type)) + { + if (!rt_strcmp(type, "lvds")) + { + output->type = SUNXI_DISPLAY_OUTPUT_LVDS; + } + else if (!rt_strcmp(type, "dsi")) + { + output->type = SUNXI_DISPLAY_OUTPUT_DSI; + } + else if (rt_strcmp(type, "rgb")) + { + LOG_E("%s: unsupported display output %s", rt_dm_dev_get_name(dev), type); + return -RT_EINVAL; + } + } + + output->lvds_8bit = rt_dm_dev_prop_read_bool(dev, "allwinner,lvds-8bit"); + output->lvds_jeida = rt_dm_dev_prop_read_bool(dev, "allwinner,lvds-jeida"); + rt_dm_dev_prop_read_u32(dev, "allwinner,lvds-polarity", &output->lvds_polarity); + + if (output->type == SUNXI_DISPLAY_OUTPUT_DSI) + { + return sunxi_display_output_parse_dsi(dev, output); + } + + return RT_EOK; +} + +static rt_err_t sunxi_dphy_setup(struct sunxi_display_output *output, + const struct sunxi_fb_timing *timing) +{ + rt_uint32_t lane_mask = RT_BIT(output->dsi_lanes) - 1; + rt_uint64_t frequency = (rt_uint64_t)timing->pixelclock * 24 / output->dsi_lanes; + rt_uint64_t vco_frequency = frequency; + rt_uint32_t multiplier = 1; + rt_uint32_t coefficient = 24 / output->dsi_lanes; + rt_uint32_t divider_n; + rt_uint32_t divider_m2; + rt_uint32_t divider_m3; + rt_uint32_t pll0; + + while (vco_frequency < 1100000000) + { + multiplier++; + vco_frequency += frequency; + } + + divider_n = (rt_uint32_t)(vco_frequency / 24000000); + divider_m2 = !(coefficient % 3) ? 3 : (!(coefficient % 2) ? 2 : 1); + divider_m3 = multiplier * coefficient / divider_m2; + + if (!divider_n || divider_n > 0xff || multiplier > 16 || divider_m3 > 16) + { + LOG_E("DSI pixel clock %u is outside Combo PHY range", timing->pixelclock); + return -RT_EINVAL; + } + + /* F101 requires this calibrated analog value before enabling the PHY. */ + HWREG32(output->dphy + SUNXI_DPHY_ANA4) = 0x84313480; + HWREG32(output->dphy + SUNXI_DPHY_ANA2) = RT_BIT(1) | RT_BIT(4); + HWREG32(output->dphy + SUNXI_DPHY_ANA3) = RT_BIT(16) | RT_BIT(17) | + RT_BIT(18) | RT_BIT(19) | RT_BIT(20) | (lane_mask << 21); + HWREG32(output->dphy + SUNXI_DPHY_ANA1) = RT_BIT(31); + + /* The CCU supplies 24 MHz. Program the F101 Combo PHY video PLL. */ + pll0 = ((multiplier - 1) << 0) | (divider_n << 8) | + RT_BIT(20) | RT_BIT(22) | + ((divider_m3 - 1) << 24) | ((divider_m2 - 1) << 28) | RT_BIT(31); + HWREG32(output->dphy + SUNXI_DPHY_PLL0) = pll0; + HWREG32(output->dphy + SUNXI_DPHY_PLL1) = RT_BIT(12) | RT_BIT(23) | RT_BIT(22); + HWREG32(output->dphy + SUNXI_DPHY_PLL2) = 0; + HWREG32(output->dphy + SUNXI_DPHY_COMBO0) = RT_BIT(0) | RT_BIT(1) | RT_BIT(3); + HWREG32(output->dphy + SUNXI_DPHY_COMBO2) = 20; + rt_hw_us_delay(20); + HWREG32(output->dphy + SUNXI_DPHY_GCTL) = RT_BIT(0); + + return RT_EOK; +} + +static rt_err_t sunxi_dsi_setup(struct sunxi_display_output *output, + const struct sunxi_fb_timing *timing) +{ + rt_uint32_t lane_mask = RT_BIT(output->dsi_lanes) - 1; + rt_uint32_t hsa; + rt_uint32_t hbp_bytes; + rt_uint32_t hfp; + rt_uint32_t hblk; + rt_uint32_t vblk = 0; + rt_uint32_t remainder; + + if (timing->hsync_len < 4 || timing->hback_porch < 4 || + timing->hfront_porch < 4) + { + LOG_E("DSI sync and porch values must be at least four pixels"); + return -RT_EINVAL; + } + + hsa = timing->hsync_len * 3 - 10; + hbp_bytes = timing->hback_porch * 3 - 10; + hfp = timing->hfront_porch * 3 - 12; + hblk = (timing->htotal - timing->hsync_len) * 3 - 10; + + if (output->dsi_lanes == 4) + { + remainder = (timing->htotal * 3 * timing->vtotal - hblk - 6) % 4; + vblk = remainder ? 4 - remainder : 0; + } + + /* DSI source and TCON-to-DSI clock gate live in DISPLAY_TOP. */ + HWREG32(output->top + SUNXI_TOP_DSI_SRC) &= ~RT_BIT(0); + HWREG32(output->top + SUNXI_TOP_LCD_CLK_SRC) |= RT_BIT(0) | RT_BIT(4); + HWREG32(output->top + SUNXI_TOP_MODULE_GATE) |= RT_BIT(16); + + HWREG32(output->dsi + SUNXI_DSI_GCTL) = 0; + HWREG32(output->dsi + SUNXI_DSI_BASIC_CTL) = 0; + HWREG32(output->dsi + SUNXI_DSI_BASIC_CTL0) = RT_BIT(16) | RT_BIT(17); + HWREG32(output->dsi + SUNXI_DSI_BASIC_CTL1) = RT_BIT(0) | RT_BIT(1) | + RT_BIT(2) | (1 << 4); + HWREG32(output->dsi + SUNXI_DSI_BASIC_SIZE0) = timing->vsync_len | + (timing->vback_porch << 16); + HWREG32(output->dsi + SUNXI_DSI_BASIC_SIZE1) = timing->vactive | + (timing->vtotal << 16); + HWREG32(output->dsi + SUNXI_DSI_TRANS_START) = 10; + HWREG32(output->dsi + SUNXI_DSI_TCON_DRQ) = timing->hfront_porch > 20 ? + ((timing->hfront_porch - 20) * 3 / 32) | RT_BIT(24) : 0; + + HWREG32(output->dsi + SUNXI_DSI_PIXEL_CTL0) = 8 | RT_BIT(16); + HWREG32(output->dsi + SUNXI_DSI_PIXEL_PH) = + sunxi_dsi_header(SUNXI_DSI_DT_RGB888, timing->hactive * 3); + HWREG32(output->dsi + SUNXI_DSI_PIXEL_PF0) = 0xffff; + HWREG32(output->dsi + SUNXI_DSI_PIXEL_PF1) = 0xffffffff; + + HWREG32(output->dsi + SUNXI_DSI_SYNC_HSS) = sunxi_dsi_header(SUNXI_DSI_DT_HSS, 0); + HWREG32(output->dsi + SUNXI_DSI_SYNC_HSE) = sunxi_dsi_header(SUNXI_DSI_DT_HSE, 0); + HWREG32(output->dsi + SUNXI_DSI_SYNC_VSS) = sunxi_dsi_header(SUNXI_DSI_DT_VSS, 0); + HWREG32(output->dsi + SUNXI_DSI_SYNC_VSE) = sunxi_dsi_header(SUNXI_DSI_DT_VSE, 0); + sunxi_dsi_block(output->dsi, SUNXI_DSI_BLK_HSA0, SUNXI_DSI_BLK_HSA1, hsa); + sunxi_dsi_block(output->dsi, SUNXI_DSI_BLK_HBP0, SUNXI_DSI_BLK_HBP1, hbp_bytes); + sunxi_dsi_block(output->dsi, SUNXI_DSI_BLK_HFP0, SUNXI_DSI_BLK_HFP1, hfp); + sunxi_dsi_block(output->dsi, SUNXI_DSI_BLK_HBLK0, SUNXI_DSI_BLK_HBLK1, hblk); + sunxi_dsi_block(output->dsi, SUNXI_DSI_BLK_VBLK0, SUNXI_DSI_BLK_VBLK1, vblk); + + HWREG32(output->dsi + SUNXI_DSI_INST_FUNC0 + 4 * SUNXI_DSI_INST_LP11) = + sunxi_dsi_inst(SUNXI_DSI_MODE_STOP, 0, RT_TRUE, lane_mask); + HWREG32(output->dsi + SUNXI_DSI_INST_FUNC0 + 4 * SUNXI_DSI_INST_TBA) = + sunxi_dsi_inst(SUNXI_DSI_MODE_TBA, 0, RT_FALSE, 1); + HWREG32(output->dsi + SUNXI_DSI_INST_FUNC0 + 4 * SUNXI_DSI_INST_HSC) = + sunxi_dsi_inst(SUNXI_DSI_MODE_HS, 0, RT_TRUE, 0); + HWREG32(output->dsi + SUNXI_DSI_INST_FUNC0 + 4 * SUNXI_DSI_INST_HSD) = + sunxi_dsi_inst(SUNXI_DSI_MODE_HS, 0, RT_FALSE, lane_mask); + HWREG32(output->dsi + SUNXI_DSI_INST_FUNC0 + 4 * SUNXI_DSI_INST_LPDT) = + sunxi_dsi_inst(SUNXI_DSI_MODE_ESCAPE, 1, RT_FALSE, 1); + HWREG32(output->dsi + SUNXI_DSI_INST_FUNC0 + 4 * SUNXI_DSI_INST_HSCEXIT) = + sunxi_dsi_inst(SUNXI_DSI_MODE_HSCEXIT, 0, RT_TRUE, 0); + HWREG32(output->dsi + SUNXI_DSI_INST_FUNC0 + 4 * SUNXI_DSI_INST_NOP) = + sunxi_dsi_inst(SUNXI_DSI_MODE_STOP, 0, RT_FALSE, lane_mask); + HWREG32(output->dsi + SUNXI_DSI_INST_FUNC0 + 4 * SUNXI_DSI_INST_DLY) = + sunxi_dsi_inst(SUNXI_DSI_MODE_NOP, 0, RT_TRUE, lane_mask); + HWREG32(output->dsi + SUNXI_DSI_INST_LOOP_SEL) = + (SUNXI_DSI_INST_HSC << (4 * SUNXI_DSI_INST_LP11)) | + (SUNXI_DSI_INST_HSD << (4 * SUNXI_DSI_INST_DLY)); + HWREG32(output->dsi + SUNXI_DSI_INST_LOOP_NUM) = 49 | (49 << 16); + HWREG32(output->dsi + SUNXI_DSI_INST_LOOP_NUM2) = 49 | (49 << 16); + HWREG32(output->dsi + SUNXI_DSI_INST_JUMP_CFG) = + 1 | (SUNXI_DSI_INST_NOP << 16) | (SUNXI_DSI_INST_HSCEXIT << 20); + HWREG32(output->dsi + SUNXI_DSI_GCTL) = RT_BIT(0); + + return sunxi_dphy_setup(output, timing); +} + +static void sunxi_lvds_setup(struct sunxi_display_output *output) +{ + rt_uint32_t lvds_ctl = RT_BIT(20) | + (output->lvds_polarity & 0x1f); + + if (output->lvds_8bit) + { + lvds_ctl |= RT_BIT(26); + } + if (output->lvds_jeida) + { + lvds_ctl |= RT_BIT(27); + } + + /* Route TCON0's pixel clock through Combo PHY0 before enabling LVDS. */ + HWREG32(output->top + SUNXI_TOP_LCD_CLK_SRC) |= RT_BIT(0) | RT_BIT(4); + HWREG32(output->tcon + SUNXI_TCON0_LVDS_CTL) = lvds_ctl; + HWREG32(output->dphy + SUNXI_DPHY_COMBO1) = 0x53; + HWREG32(output->dphy + SUNXI_DPHY_COMBO0) = RT_BIT(0); + rt_hw_us_delay(5); + HWREG32(output->dphy + SUNXI_DPHY_COMBO0) = RT_BIT(0) | RT_BIT(2); + rt_hw_us_delay(5); + HWREG32(output->dphy + SUNXI_DPHY_COMBO0) = RT_BIT(0) | RT_BIT(1) | RT_BIT(2); + rt_hw_us_delay(5); + HWREG32(output->dphy + SUNXI_DPHY_ANA4) = 0x84000000; + HWREG32(output->dphy + SUNXI_DPHY_ANA3) = 0x01040000; + HWREG32(output->dphy + SUNXI_DPHY_ANA2) = 0; + HWREG32(output->dphy + SUNXI_DPHY_ANA1) = 0; +} + +static void sunxi_display_panel_prepare(struct sunxi_display_output *output) +{ + if (output->panel_prepared) + { + return; + } + + if (output->panel_reset_pin >= 0) + { + rt_pin_write(output->panel_reset_pin, output->panel_reset_active); + rt_pin_mode(output->panel_reset_pin, PIN_MODE_OUTPUT); + } + if (output->panel_enable_pin >= 0) + { + rt_pin_write(output->panel_enable_pin, !output->panel_enable_active); + rt_pin_mode(output->panel_enable_pin, PIN_MODE_OUTPUT); + if (output->panel_power_off_delay_ms) + { + rt_thread_mdelay(output->panel_power_off_delay_ms); + } + rt_pin_write(output->panel_enable_pin, output->panel_enable_active); + } + if (output->panel_prepare_delay_ms) + { + rt_thread_mdelay(output->panel_prepare_delay_ms); + } + if (output->panel_reset_pin >= 0) + { + rt_thread_mdelay(output->panel_reset_delay_ms); + rt_pin_write(output->panel_reset_pin, !output->panel_reset_active); + rt_thread_mdelay(output->panel_init_delay_ms); + } + output->panel_prepared = RT_TRUE; +} + +rt_err_t sunxi_display_output_prepare(struct sunxi_display_output *output, + const struct sunxi_fb_timing *timing) +{ + switch (output->type) + { + case SUNXI_DISPLAY_OUTPUT_RGB: + return RT_EOK; + + case SUNXI_DISPLAY_OUTPUT_LVDS: + if (!output->dphy || !output->top) + { + return -RT_ENOENT; + } + sunxi_lvds_setup(output); + return RT_EOK; + + case SUNXI_DISPLAY_OUTPUT_DSI: + if (!output->dsi || !output->dphy || !output->top) + { + return -RT_ENOENT; + } + sunxi_display_panel_prepare(output); + return sunxi_dsi_setup(output, timing); + } + + return -RT_EINVAL; +} + +rt_err_t sunxi_display_output_panel_init(struct sunxi_display_output *output) +{ + const rt_uint8_t *sequence; + rt_size_t remaining; + + if (output->type != SUNXI_DISPLAY_OUTPUT_DSI || + !output->panel_init_sequence_len) + { + return RT_EOK; + } + + sequence = output->panel_init_sequence; + remaining = output->panel_init_sequence_len; + while (remaining) + { + rt_uint8_t type; + rt_uint8_t delay_ms; + rt_uint8_t payload_length; + rt_err_t err; + + if (remaining < 3) + { + return -RT_EINVAL; + } + + type = *sequence++; + delay_ms = *sequence++; + payload_length = *sequence++; + remaining -= 3; + if (!payload_length || payload_length > remaining) + { + return -RT_EINVAL; + } + + err = sunxi_dsi_dcs_write(output, type, sequence, payload_length); + if (err) + { + return err; + } + + sequence += payload_length; + remaining -= payload_length; + if (delay_ms) + { + rt_thread_mdelay(delay_ms); + } + } + + return RT_EOK; +} + +void sunxi_display_output_enable(struct sunxi_display_output *output) +{ + if (output->type == SUNXI_DISPLAY_OUTPUT_LVDS) + { + HWREG32(output->tcon + SUNXI_TCON0_LVDS_CTL) |= RT_BIT(31); + } + else if (output->type == SUNXI_DISPLAY_OUTPUT_DSI) + { + /* Enter high speed on the data lanes before starting the clock lane. */ + HWREG32(output->dsi + SUNXI_DSI_INST_JUMP_SEL) = + (SUNXI_DSI_INST_NOP << (4 * SUNXI_DSI_INST_LP11)) | + (SUNXI_DSI_INST_HSD << (4 * SUNXI_DSI_INST_NOP)) | + (SUNXI_DSI_INST_DLY << (4 * SUNXI_DSI_INST_HSD)) | + (SUNXI_DSI_INST_NOP << (4 * SUNXI_DSI_INST_DLY)) | + (SUNXI_DSI_INST_END << (4 * SUNXI_DSI_INST_HSCEXIT)); + HWREG32(output->dsi + SUNXI_DSI_BASIC_CTL0) &= ~RT_BIT(0); + HWREG32(output->dsi + SUNXI_DSI_BASIC_CTL0) |= RT_BIT(0); + HWREG32(output->dsi + SUNXI_DSI_INST_JUMP_SEL) = + (SUNXI_DSI_INST_HSC << (4 * SUNXI_DSI_INST_LP11)) | + (SUNXI_DSI_INST_END << (4 * SUNXI_DSI_INST_HSC)); + HWREG32(output->dsi + SUNXI_DSI_BASIC_CTL0) &= ~RT_BIT(0); + HWREG32(output->dsi + SUNXI_DSI_BASIC_CTL0) |= RT_BIT(0); + } +} + +void sunxi_display_output_disable(struct sunxi_display_output *output) +{ + if (output->type == SUNXI_DISPLAY_OUTPUT_DSI) + { + if (output->dsi) + { + HWREG32(output->dsi + SUNXI_DSI_GCTL) = 0; + } + if (output->dphy) + { + HWREG32(output->dphy + SUNXI_DPHY_GCTL) = 0; + HWREG32(output->dphy + SUNXI_DPHY_COMBO0) = 0; + } + if (output->top) + { + HWREG32(output->top + SUNXI_TOP_MODULE_GATE) &= ~RT_BIT(16); + } + } + else if (output->type == SUNXI_DISPLAY_OUTPUT_LVDS) + { + if (output->tcon) + { + HWREG32(output->tcon + SUNXI_TCON0_LVDS_CTL) = 0; + } + if (output->dphy) + { + HWREG32(output->dphy + SUNXI_DPHY_COMBO1) = 0; + HWREG32(output->dphy + SUNXI_DPHY_COMBO0) = 0; + HWREG32(output->dphy + SUNXI_DPHY_ANA4) = 0; + } + } + + if (output->panel_prepared) + { + if (output->panel_reset_pin >= 0) + { + rt_pin_write(output->panel_reset_pin, output->panel_reset_active); + } + if (output->panel_enable_pin >= 0) + { + rt_pin_write(output->panel_enable_pin, !output->panel_enable_active); + } + output->panel_prepared = RT_FALSE; + } +} diff --git a/bsp/allwinner/dm/graphic/framebuffer/sunxi-display-output.h b/bsp/allwinner/dm/graphic/framebuffer/sunxi-display-output.h new file mode 100644 index 000000000000..d90795477896 --- /dev/null +++ b/bsp/allwinner/dm/graphic/framebuffer/sunxi-display-output.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SUNXI_DISPLAY_OUTPUT_H__ +#define __SUNXI_DISPLAY_OUTPUT_H__ + +#include + +struct rt_device; + +struct sunxi_fb_timing +{ + rt_uint32_t pixelclock; + rt_uint32_t hactive; + rt_uint32_t hfront_porch; + rt_uint32_t hback_porch; + rt_uint32_t hsync_len; + rt_uint32_t vactive; + rt_uint32_t vfront_porch; + rt_uint32_t vback_porch; + rt_uint32_t vsync_len; + rt_uint32_t htotal; + rt_uint32_t vtotal; + rt_bool_t hsync_active; + rt_bool_t vsync_active; + rt_bool_t pixelclk_active; +}; + +enum sunxi_display_output_type +{ + SUNXI_DISPLAY_OUTPUT_RGB, + SUNXI_DISPLAY_OUTPUT_LVDS, + SUNXI_DISPLAY_OUTPUT_DSI, +}; + +struct sunxi_display_output +{ + enum sunxi_display_output_type type; + void *tcon; + void *top; + void *dsi; + void *dphy; + const rt_uint8_t *panel_init_sequence; + rt_size_t panel_init_sequence_len; + rt_base_t panel_enable_pin; + rt_base_t panel_reset_pin; + rt_uint8_t panel_enable_active; + rt_uint8_t panel_reset_active; + rt_uint32_t panel_power_off_delay_ms; + rt_uint32_t panel_prepare_delay_ms; + rt_uint32_t panel_reset_delay_ms; + rt_uint32_t panel_init_delay_ms; + rt_bool_t panel_prepared; + rt_uint8_t dsi_lanes; + rt_uint32_t lvds_polarity; + rt_bool_t lvds_8bit; + rt_bool_t lvds_jeida; +}; + +rt_err_t sunxi_display_output_parse(struct rt_device *dev, + struct sunxi_display_output *output); +rt_err_t sunxi_display_output_prepare(struct sunxi_display_output *output, + const struct sunxi_fb_timing *timing); +rt_err_t sunxi_display_output_panel_init(struct sunxi_display_output *output); +void sunxi_display_output_enable(struct sunxi_display_output *output); +void sunxi_display_output_disable(struct sunxi_display_output *output); + +#endif /* __SUNXI_DISPLAY_OUTPUT_H__ */ diff --git a/bsp/allwinner/dm/hwcrypto/Kconfig b/bsp/allwinner/dm/hwcrypto/Kconfig new file mode 100644 index 000000000000..bcf7411f0c6b --- /dev/null +++ b/bsp/allwinner/dm/hwcrypto/Kconfig @@ -0,0 +1,19 @@ +config RT_HWCRYPTO_SUNXI_CE + bool "Allwinner Sunxi CE 0.6 crypto engine" + depends on RT_USING_CLK + depends on RT_USING_DM + depends on RT_USING_HWCRYPTO + depends on RT_USING_OFW + depends on RT_USING_RESET + select RT_HWCRYPTO_USING_AES + select RT_HWCRYPTO_USING_AES_CBC + select RT_HWCRYPTO_USING_AES_CTR + select RT_HWCRYPTO_USING_RNG + default n + help + Enable the PIO-backed Allwinner CE 0.6 driver. It provides AES ECB, + CBC and CTR operations plus the CE pseudo-random-number generator. + + Hash algorithms are deliberately not selected here: this CE revision + exposes only one-shot hashing while RT-Thread's hwcrypto hash API is + streaming. diff --git a/bsp/allwinner/dm/hwcrypto/SConscript b/bsp/allwinner/dm/hwcrypto/SConscript new file mode 100644 index 000000000000..b34094cc2d83 --- /dev/null +++ b/bsp/allwinner/dm/hwcrypto/SConscript @@ -0,0 +1,10 @@ +from building import * + +src = [] + +if GetDepend(['RT_HWCRYPTO_SUNXI_CE']): + src += ['hwcrypto-sunxi-ce.c'] + +group = DefineGroup('DeviceDrivers', src, depend = ['']) + +Return('group') diff --git a/bsp/allwinner/dm/hwcrypto/hwcrypto-sunxi-ce.c b/bsp/allwinner/dm/hwcrypto/hwcrypto-sunxi-ce.c new file mode 100644 index 000000000000..00f4e0019bd4 --- /dev/null +++ b/bsp/allwinner/dm/hwcrypto/hwcrypto-sunxi-ce.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "crypto.sunxi.ce" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_CE_CTL 0x000 +#define SUNXI_CE_KEY(index) (0x004 + (index) * 4) +#define SUNXI_CE_IV(index) (0x024 + (index) * 4) +#define SUNXI_CE_CNT(index) (0x034 + (index) * 4) +#define SUNXI_CE_FCSR 0x044 +#define SUNXI_CE_ICSR 0x048 +#define SUNXI_CE_MD(index) (0x04c + (index) * 4) +#define SUNXI_CE_RXFIFO 0x200 +#define SUNXI_CE_TXFIFO 0x204 + +#define SUNXI_CE_CTL_START RT_BIT(0) +#define SUNXI_CE_CTL_PRNG_START RT_BIT(1) +#define SUNXI_CE_CTL_DIRECTION RT_BIT(7) +#define SUNXI_CE_CTL_KEY_SIZE_SHIFT 8 +#define SUNXI_CE_CTL_METHOD_SHIFT 4 +#define SUNXI_CE_CTL_MODE_SHIFT 12 + +#define SUNXI_CE_FCSR_TX_READY RT_BIT(22) +#define SUNXI_CE_FCSR_RX_READY RT_BIT(30) +#define SUNXI_CE_ICSR_RX_EMPTY RT_BIT(10) +#define SUNXI_CE_ICSR_TX_AVAILABLE RT_BIT(8) + +#define SUNXI_CE_METHOD_AES 0 +#define SUNXI_CE_METHOD_PRNG 5 + +#define SUNXI_CE_AES_ECB 0 +#define SUNXI_CE_AES_CBC 1 +#define SUNXI_CE_AES_CTR 2 + +#define SUNXI_CE_AES_BLOCK_SIZE 16 +#define SUNXI_CE_PRNG_SEED_WORDS 6 +#define SUNXI_CE_TIMEOUT_US 500000 + +struct sunxi_ce +{ + struct rt_hwcrypto_device parent; + + void *base; + struct rt_clk_array *clks; + struct rt_reset_control *reset; + struct rt_mutex lock; + rt_bool_t clks_enabled; +}; + +#define to_sunxi_ce(raw) rt_container_of(raw, struct sunxi_ce, parent) + +static rt_uint32_t sunxi_ce_readl(struct sunxi_ce *ce, rt_uint32_t offset) +{ + return HWREG32(ce->base + offset); +} + +static void sunxi_ce_writel(struct sunxi_ce *ce, rt_uint32_t value, + rt_uint32_t offset) +{ + HWREG32(ce->base + offset) = value; +} + +static rt_uint32_t sunxi_ce_get_le32(const rt_uint8_t *data) +{ + rt_uint32_t value; + + rt_memcpy(&value, data, sizeof(value)); + return value; +} + +static void sunxi_ce_put_le32(rt_uint8_t *data, rt_uint32_t value) +{ + rt_memcpy(data, &value, sizeof(value)); +} + +static rt_err_t sunxi_ce_wait_fifo(struct sunxi_ce *ce, rt_uint32_t mask) +{ + rt_uint32_t timeout; + + for (timeout = 0; timeout < SUNXI_CE_TIMEOUT_US; ++timeout) + { + if (sunxi_ce_readl(ce, SUNXI_CE_FCSR) & mask) + { + return RT_EOK; + } + + rt_hw_us_delay(1); + } + + return -RT_ETIMEOUT; +} + +static void sunxi_ce_reset_engine(struct sunxi_ce *ce) +{ + rt_uint32_t value; + + sunxi_ce_writel(ce, 0, SUNXI_CE_CTL); + sunxi_ce_writel(ce, 0, SUNXI_CE_FCSR); + value = sunxi_ce_readl(ce, SUNXI_CE_ICSR); + sunxi_ce_writel(ce, value | SUNXI_CE_ICSR_RX_EMPTY | + SUNXI_CE_ICSR_TX_AVAILABLE, SUNXI_CE_ICSR); +} + +static rt_err_t sunxi_ce_aes_mode(hwcrypto_type type, rt_uint32_t *mode) +{ + switch (type) + { + case HWCRYPTO_TYPE_AES_ECB: + *mode = SUNXI_CE_AES_ECB; + return RT_EOK; + case HWCRYPTO_TYPE_AES_CBC: + *mode = SUNXI_CE_AES_CBC; + return RT_EOK; + case HWCRYPTO_TYPE_AES_CTR: + *mode = SUNXI_CE_AES_CTR; + return RT_EOK; + default: + return -RT_ENOSYS; + } +} + +static rt_err_t sunxi_ce_aes_crypt(struct hwcrypto_symmetric *symmetric, + struct hwcrypto_symmetric_info *info) +{ + rt_err_t err; + rt_uint32_t control; + rt_uint32_t aes_mode; + rt_size_t offset; + rt_size_t word; + struct sunxi_ce *ce; + + if (!symmetric->key_bitlen || + (symmetric->key_bitlen != 128 && symmetric->key_bitlen != 192 && + symmetric->key_bitlen != 256) || + (info->length % SUNXI_CE_AES_BLOCK_SIZE) || + (!info->in && info->length) || (!info->out && info->length)) + { + return -RT_EINVAL; + } + + if ((err = sunxi_ce_aes_mode(symmetric->parent.type, &aes_mode))) + { + return err; + } + + if (aes_mode != SUNXI_CE_AES_ECB && + symmetric->iv_len != SUNXI_CE_AES_BLOCK_SIZE) + { + return -RT_EINVAL; + } + + ce = to_sunxi_ce(symmetric->parent.device); + rt_mutex_take(&ce->lock, RT_WAITING_FOREVER); + + sunxi_ce_reset_engine(ce); + control = SUNXI_CE_METHOD_AES << SUNXI_CE_CTL_METHOD_SHIFT; + control |= aes_mode << SUNXI_CE_CTL_MODE_SHIFT; + control |= ((symmetric->key_bitlen - 128) / 64) << SUNXI_CE_CTL_KEY_SIZE_SHIFT; + if (info->mode == HWCRYPTO_MODE_DECRYPT) + { + control |= SUNXI_CE_CTL_DIRECTION; + } + sunxi_ce_writel(ce, control, SUNXI_CE_CTL); + + for (offset = 0; offset < symmetric->key_bitlen / 8; offset += 4) + { + sunxi_ce_writel(ce, sunxi_ce_get_le32(&symmetric->key[offset]), + SUNXI_CE_KEY(offset / 4)); + } + + if (aes_mode == SUNXI_CE_AES_CBC) + { + for (offset = 0; offset < SUNXI_CE_AES_BLOCK_SIZE; offset += 4) + { + sunxi_ce_writel(ce, sunxi_ce_get_le32(&symmetric->iv[offset]), + SUNXI_CE_IV(offset / 4)); + } + } + else if (aes_mode == SUNXI_CE_AES_CTR) + { + for (offset = 0; offset < SUNXI_CE_AES_BLOCK_SIZE; offset += 4) + { + sunxi_ce_writel(ce, sunxi_ce_get_le32(&symmetric->iv[offset]), + SUNXI_CE_CNT(offset / 4)); + } + } + + sunxi_ce_writel(ce, control | SUNXI_CE_CTL_START, SUNXI_CE_CTL); + + for (offset = 0; offset < info->length; offset += SUNXI_CE_AES_BLOCK_SIZE) + { + for (word = 0; word < SUNXI_CE_AES_BLOCK_SIZE; word += 4) + { + if ((err = sunxi_ce_wait_fifo(ce, SUNXI_CE_FCSR_RX_READY))) + { + goto _out; + } + + sunxi_ce_writel(ce, sunxi_ce_get_le32(&info->in[offset + word]), + SUNXI_CE_RXFIFO); + } + + for (word = 0; word < SUNXI_CE_AES_BLOCK_SIZE; word += 4) + { + if ((err = sunxi_ce_wait_fifo(ce, SUNXI_CE_FCSR_TX_READY))) + { + goto _out; + } + + sunxi_ce_put_le32(&info->out[offset + word], + sunxi_ce_readl(ce, SUNXI_CE_TXFIFO)); + } + } + + err = RT_EOK; + +_out: + sunxi_ce_reset_engine(ce); + rt_mutex_release(&ce->lock); + + return err; +} + +static rt_uint32_t sunxi_ce_seed_word(rt_uint32_t salt) +{ + rt_uint32_t i; + rt_uint64_t counter; + rt_uint32_t value; + + counter = rt_clock_time_get_counter(); + value = (rt_uint32_t)counter ^ (rt_uint32_t)(counter >> 32) ^ salt; + + for (i = 0; i < 32; ++i) + { + counter = rt_clock_time_get_counter(); + value ^= value << 13; + value ^= value >> 17; + value ^= value << 5; + value ^= (rt_uint32_t)counter ^ (rt_uint32_t)(counter >> 32); + } + + return value; +} + +static rt_uint32_t sunxi_ce_rng_update(struct hwcrypto_rng *rng) +{ + rt_uint32_t control; + rt_uint32_t i; + rt_uint32_t timeout; + rt_uint32_t value; + struct sunxi_ce *ce; + + value = 0; + ce = to_sunxi_ce(rng->parent.device); + + rt_mutex_take(&ce->lock, RT_WAITING_FOREVER); + sunxi_ce_reset_engine(ce); + + control = SUNXI_CE_METHOD_PRNG << SUNXI_CE_CTL_METHOD_SHIFT; + sunxi_ce_writel(ce, control, SUNXI_CE_CTL); + + for (i = 0; i < SUNXI_CE_PRNG_SEED_WORDS; ++i) + { + sunxi_ce_writel(ce, sunxi_ce_seed_word((i + 1) * 0x9e3779b9U), + SUNXI_CE_KEY(i)); + } + + /* The PRNG trigger is sampled only after the CE engine has started. */ + sunxi_ce_writel(ce, control | SUNXI_CE_CTL_START, SUNXI_CE_CTL); + sunxi_ce_writel(ce, control | SUNXI_CE_CTL_START | + SUNXI_CE_CTL_PRNG_START, SUNXI_CE_CTL); + for (timeout = 0; timeout < SUNXI_CE_TIMEOUT_US; ++timeout) + { + if (!(sunxi_ce_readl(ce, SUNXI_CE_CTL) & SUNXI_CE_CTL_PRNG_START)) + { + value = sunxi_ce_readl(ce, SUNXI_CE_MD(0)); + break; + } + + rt_hw_us_delay(1); + } + + if (timeout == SUNXI_CE_TIMEOUT_US) + { + LOG_E("PRNG operation timed out"); + } + + sunxi_ce_reset_engine(ce); + rt_mutex_release(&ce->lock); + + return value; +} + +static const struct hwcrypto_symmetric_ops sunxi_ce_aes_ops = +{ + .crypt = sunxi_ce_aes_crypt, +}; + +static const struct hwcrypto_rng_ops sunxi_ce_rng_ops = +{ + .update = sunxi_ce_rng_update, +}; + +static rt_err_t sunxi_ce_create(struct rt_hwcrypto_ctx *ctx) +{ + rt_uint32_t aes_mode; + + switch (ctx->type & HWCRYPTO_MAIN_TYPE_MASK) + { + case HWCRYPTO_TYPE_AES: + if (sunxi_ce_aes_mode(ctx->type, &aes_mode)) + { + return -RT_ENOSYS; + } + ((struct hwcrypto_symmetric *)ctx)->ops = &sunxi_ce_aes_ops; + return RT_EOK; + case HWCRYPTO_TYPE_RNG: + ((struct hwcrypto_rng *)ctx)->ops = &sunxi_ce_rng_ops; + return RT_EOK; + default: + return -RT_ENOSYS; + } +} + +static void sunxi_ce_destroy(struct rt_hwcrypto_ctx *ctx) +{ + RT_UNUSED(ctx); +} + +static rt_err_t sunxi_ce_copy(struct rt_hwcrypto_ctx *destination, + const struct rt_hwcrypto_ctx *source) +{ + RT_UNUSED(destination); + RT_UNUSED(source); + return RT_EOK; +} + +static void sunxi_ce_reset(struct rt_hwcrypto_ctx *ctx) +{ + RT_UNUSED(ctx); +} + +static const struct rt_hwcrypto_ops sunxi_ce_ops = +{ + .create = sunxi_ce_create, + .destroy = sunxi_ce_destroy, + .copy = sunxi_ce_copy, + .reset = sunxi_ce_reset, +}; + +static void sunxi_ce_release(struct sunxi_ce *ce) +{ + if (ce->base && ce->clks_enabled) + { + sunxi_ce_reset_engine(ce); + } + if (!rt_is_err_or_null(ce->reset)) + { + rt_reset_control_assert(ce->reset); + rt_reset_control_put(ce->reset); + } + if (!rt_is_err_or_null(ce->clks)) + { + if (ce->clks_enabled) + { + rt_clk_array_disable_unprepare(ce->clks); + } + rt_clk_array_put(ce->clks); + } + if (ce->base) + { + rt_iounmap(ce->base); + } + rt_mutex_detach(&ce->lock); + rt_free(ce); +} + +static rt_err_t sunxi_ce_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct sunxi_ce *ce = rt_calloc(1, sizeof(*ce)); + + if (!ce) + { + return -RT_ENOMEM; + } + + rt_mutex_init(&ce->lock, "sunxi-ce", RT_IPC_FLAG_PRIO); + ce->base = rt_dm_dev_iomap(dev, 0); + if (!ce->base) + { + err = -RT_EIO; + goto _fail; + } + + ce->reset = rt_reset_control_get_by_name(dev, "bus"); + if (rt_is_err(ce->reset)) + { + err = rt_ptr_err(ce->reset); + goto _fail; + } + + ce->clks = rt_clk_get_array(dev); + if (rt_is_err(ce->clks)) + { + err = rt_ptr_err(ce->clks); + goto _fail; + } + + if ((err = rt_clk_array_prepare_enable(ce->clks))) + { + goto _fail; + } + ce->clks_enabled = RT_TRUE; + + rt_reset_control_assert(ce->reset); + rt_hw_us_delay(10); + rt_reset_control_deassert(ce->reset); + + ce->parent.ops = &sunxi_ce_ops; + ce->parent.user_data = ce; + if ((err = rt_hwcrypto_register(&ce->parent, RT_HWCRYPTO_DEFAULT_NAME))) + { + goto _fail; + } + + dev->user_data = ce; + return RT_EOK; + +_fail: + sunxi_ce_release(ce); + return err; +} + +static const struct rt_ofw_node_id sunxi_ce_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-ce" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_ce_driver = +{ + .name = "sunxi-ce", + .ids = sunxi_ce_ofw_ids, + .probe = sunxi_ce_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_ce_driver); diff --git a/bsp/allwinner/dm/i2c/Kconfig b/bsp/allwinner/dm/i2c/Kconfig new file mode 100644 index 000000000000..91855b8ddd05 --- /dev/null +++ b/bsp/allwinner/dm/i2c/Kconfig @@ -0,0 +1,7 @@ +config RT_I2C_SUNXI + bool "Allwinner Sunxi TWI controller" + depends on RT_USING_I2C + depends on RT_USING_DM + depends on RT_USING_CLK + depends on RT_USING_RESET + default n diff --git a/bsp/allwinner/dm/i2c/SConscript b/bsp/allwinner/dm/i2c/SConscript new file mode 100644 index 000000000000..cc1a24eed2fc --- /dev/null +++ b/bsp/allwinner/dm/i2c/SConscript @@ -0,0 +1,13 @@ +from building import * +Import('RTT_ROOT') + +cwd = GetCurrentDir() +src = [] + +if GetDepend('RT_I2C_SUNXI'): + src += ['i2c-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], + CPPPATH = [RTT_ROOT + '/components/drivers/i2c']) + +Return('group') diff --git a/bsp/allwinner/dm/i2c/i2c-sunxi.c b/bsp/allwinner/dm/i2c/i2c-sunxi.c new file mode 100644 index 000000000000..bfe7721d4da3 --- /dev/null +++ b/bsp/allwinner/dm/i2c/i2c-sunxi.c @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define DBG_TAG "i2c.sunxi" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_TWI_ADDR 0x00 +#define SUNXI_TWI_DATA 0x08 +#define SUNXI_TWI_CTL 0x0c +#define SUNXI_TWI_STAT 0x10 +#define SUNXI_TWI_CLK 0x14 +#define SUNXI_TWI_SRST 0x18 + +#define SUNXI_TWI_CTL_ACK RT_BIT(2) +#define SUNXI_TWI_CTL_INTFLG RT_BIT(3) +#define SUNXI_TWI_CTL_STP RT_BIT(4) +#define SUNXI_TWI_CTL_STA RT_BIT(5) +#define SUNXI_TWI_CTL_BUSEN RT_BIT(6) + +#define SUNXI_TWI_STAT_START 0x08 +#define SUNXI_TWI_STAT_RESTART 0x10 +#define SUNXI_TWI_STAT_ADDR_W_ACK 0x18 +#define SUNXI_TWI_STAT_ADDR_W_NAK 0x20 +#define SUNXI_TWI_STAT_DATA_W_ACK 0x28 +#define SUNXI_TWI_STAT_DATA_W_NAK 0x30 +#define SUNXI_TWI_STAT_ARB_LOST 0x38 +#define SUNXI_TWI_STAT_ADDR_R_ACK 0x40 +#define SUNXI_TWI_STAT_ADDR_R_NAK 0x48 +#define SUNXI_TWI_STAT_DATA_R_ACK 0x50 +#define SUNXI_TWI_STAT_DATA_R_NAK 0x58 + +#define SUNXI_TWI_TIMEOUT_US 50000 + +struct sunxi_i2c +{ + struct rt_i2c_bus_device parent; + struct rt_clk *clk; + struct rt_reset_control *reset; + void *base; + rt_uint32_t input_rate; +}; + +#define to_sunxi_i2c(raw) rt_container_of(raw, struct sunxi_i2c, parent) + +static rt_uint32_t sunxi_i2c_read(struct sunxi_i2c *i2c, rt_uint32_t offset) +{ + return HWREG32(i2c->base + offset); +} + +static void sunxi_i2c_write(struct sunxi_i2c *i2c, rt_uint32_t offset, + rt_uint32_t value) +{ + HWREG32(i2c->base + offset) = value; +} + +static rt_err_t sunxi_i2c_wait(struct sunxi_i2c *i2c) +{ + rt_int32_t timeout; + + for (timeout = SUNXI_TWI_TIMEOUT_US; timeout > 0; --timeout) + { + if (sunxi_i2c_read(i2c, SUNXI_TWI_CTL) & SUNXI_TWI_CTL_INTFLG) + { + return RT_EOK; + } + + rt_hw_us_delay(1); + } + + return -RT_ETIMEOUT; +} + +static void sunxi_i2c_continue(struct sunxi_i2c *i2c, rt_bool_t ack) +{ + rt_uint32_t control = sunxi_i2c_read(i2c, SUNXI_TWI_CTL); + + control |= SUNXI_TWI_CTL_BUSEN; + control &= ~(SUNXI_TWI_CTL_STA | SUNXI_TWI_CTL_STP | SUNXI_TWI_CTL_INTFLG); + if (ack) + { + control |= SUNXI_TWI_CTL_ACK; + } + else + { + control &= ~SUNXI_TWI_CTL_ACK; + } + sunxi_i2c_write(i2c, SUNXI_TWI_CTL, control); +} + +static rt_err_t sunxi_i2c_start(struct sunxi_i2c *i2c) +{ + rt_uint32_t control = sunxi_i2c_read(i2c, SUNXI_TWI_CTL); + rt_err_t err; + + control |= SUNXI_TWI_CTL_BUSEN | SUNXI_TWI_CTL_STA; + control &= ~SUNXI_TWI_CTL_INTFLG; + sunxi_i2c_write(i2c, SUNXI_TWI_CTL, control); + + if ((err = sunxi_i2c_wait(i2c))) + { + return err; + } + + control = sunxi_i2c_read(i2c, SUNXI_TWI_STAT) & 0xff; + return control == SUNXI_TWI_STAT_START || control == SUNXI_TWI_STAT_RESTART ? + RT_EOK : -RT_ERROR; +} + +static void sunxi_i2c_stop(struct sunxi_i2c *i2c) +{ + rt_int32_t timeout; + rt_uint32_t control = sunxi_i2c_read(i2c, SUNXI_TWI_CTL); + + control |= SUNXI_TWI_CTL_BUSEN | SUNXI_TWI_CTL_STP; + control &= ~(SUNXI_TWI_CTL_STA | SUNXI_TWI_CTL_ACK | SUNXI_TWI_CTL_INTFLG); + sunxi_i2c_write(i2c, SUNXI_TWI_CTL, control); + + for (timeout = SUNXI_TWI_TIMEOUT_US; timeout > 0; --timeout) + { + if (!(sunxi_i2c_read(i2c, SUNXI_TWI_CTL) & SUNXI_TWI_CTL_STP)) + { + break; + } + rt_hw_us_delay(1); + } +} + +static rt_err_t sunxi_i2c_write_byte(struct sunxi_i2c *i2c, rt_uint8_t byte, + rt_uint8_t expected, rt_bool_t ignore_nack) +{ + rt_err_t err; + rt_uint32_t status; + + sunxi_i2c_write(i2c, SUNXI_TWI_DATA, byte); + sunxi_i2c_continue(i2c, RT_FALSE); + + if ((err = sunxi_i2c_wait(i2c))) + { + return err; + } + + status = sunxi_i2c_read(i2c, SUNXI_TWI_STAT) & 0xff; + if (status == expected || (ignore_nack && + (status == SUNXI_TWI_STAT_ADDR_W_NAK || + status == SUNXI_TWI_STAT_DATA_W_NAK))) + { + return RT_EOK; + } + + return status == SUNXI_TWI_STAT_ARB_LOST ? -RT_EBUSY : -RT_EIO; +} + +static rt_err_t sunxi_i2c_xfer_msg(struct sunxi_i2c *i2c, + struct rt_i2c_msg *msg, rt_bool_t start) +{ + rt_bool_t read = msg->flags & RT_I2C_RD; + rt_bool_t ignore_nack = msg->flags & RT_I2C_IGNORE_NACK; + rt_uint16_t index; + rt_err_t err; + + if (msg->flags & RT_I2C_ADDR_10BIT) + { + return -RT_ENOSYS; + } + + if (start) + { + if ((err = sunxi_i2c_start(i2c))) + { + return err; + } + + err = sunxi_i2c_write_byte(i2c, (msg->addr << 1) | read, + read ? SUNXI_TWI_STAT_ADDR_R_ACK : SUNXI_TWI_STAT_ADDR_W_ACK, + ignore_nack); + if (err) + { + return err; + } + } + + if (read) + { + for (index = 0; index < msg->len; ++index) + { + rt_bool_t ack = index + 1 < msg->len && + !(msg->flags & RT_I2C_NO_READ_ACK); + rt_uint32_t expected = ack ? SUNXI_TWI_STAT_DATA_R_ACK : + SUNXI_TWI_STAT_DATA_R_NAK; + + sunxi_i2c_continue(i2c, ack); + if ((err = sunxi_i2c_wait(i2c))) + { + return err; + } + if ((sunxi_i2c_read(i2c, SUNXI_TWI_STAT) & 0xff) != expected) + { + return -RT_EIO; + } + msg->buf[index] = sunxi_i2c_read(i2c, SUNXI_TWI_DATA); + } + } + else + { + for (index = 0; index < msg->len; ++index) + { + if ((err = sunxi_i2c_write_byte(i2c, msg->buf[index], + SUNXI_TWI_STAT_DATA_W_ACK, ignore_nack))) + { + return err; + } + } + } + + return RT_EOK; +} + +static rt_ssize_t sunxi_i2c_master_xfer(struct rt_i2c_bus_device *bus, + struct rt_i2c_msg msgs[], rt_uint32_t count) +{ + rt_uint32_t index; + rt_err_t err; + struct sunxi_i2c *i2c = to_sunxi_i2c(bus); + + if (!msgs || !count) + { + return -RT_EINVAL; + } + + for (index = 0; index < count; ++index) + { + if ((msgs[index].flags & RT_I2C_NO_START) && !index) + { + return -RT_EINVAL; + } + + err = sunxi_i2c_xfer_msg(i2c, &msgs[index], + !(msgs[index].flags & RT_I2C_NO_START)); + if (err) + { + sunxi_i2c_stop(i2c); + return err; + } + } + + if (!(msgs[count - 1].flags & RT_I2C_NO_STOP)) + { + sunxi_i2c_stop(i2c); + } + + return count; +} + +static rt_err_t sunxi_i2c_set_bus_rate(struct sunxi_i2c *i2c, + rt_uint32_t target_rate) +{ + rt_uint32_t best_rate = 0; + rt_uint32_t best_divider = 0; + int n; + int m; + + if (!target_rate || !i2c->input_rate) + { + return -RT_EINVAL; + } + + for (n = 0; n <= 7; ++n) + { + for (m = 0; m <= 15; ++m) + { + rt_uint32_t rate = i2c->input_rate / ((1u << n) * (m + 1) * 10); + + if (rate <= target_rate && rate > best_rate) + { + best_rate = rate; + best_divider = (m << 3) | n; + } + } + } + + if (!best_rate) + { + return -RT_EINVAL; + } + + sunxi_i2c_write(i2c, SUNXI_TWI_CLK, best_divider); + i2c->parent.config.max_hz = target_rate; + i2c->parent.config.usage_freq = best_rate; + + return RT_EOK; +} + +static rt_err_t sunxi_i2c_control(struct rt_i2c_bus_device *bus, int cmd, + void *args) +{ + struct sunxi_i2c *i2c = to_sunxi_i2c(bus); + + if (cmd == RT_I2C_DEV_CTRL_CLK && args) + { + return sunxi_i2c_set_bus_rate(i2c, *(rt_uint32_t *)args); + } + + return -RT_ENOSYS; +} + +static const struct rt_i2c_bus_device_ops sunxi_i2c_ops = +{ + .master_xfer = sunxi_i2c_master_xfer, + .i2c_bus_control = sunxi_i2c_control, +}; + +static void sunxi_i2c_free(struct sunxi_i2c *i2c) +{ + if (!rt_is_err_or_null(i2c->reset)) + { + rt_reset_control_put(i2c->reset); + } + if (!rt_is_err_or_null(i2c->clk)) + { + rt_clk_disable_unprepare(i2c->clk); + rt_clk_put(i2c->clk); + } + if (i2c->base) + { + rt_iounmap(i2c->base); + } + rt_free(i2c); +} + +static rt_err_t sunxi_i2c_probe(struct rt_platform_device *pdev) +{ + int id; + rt_err_t err; + const char *name; + struct rt_device *dev = &pdev->parent; + struct sunxi_i2c *i2c = rt_calloc(1, sizeof(*i2c)); + + if (!i2c) + { + return -RT_ENOMEM; + } + + i2c->base = rt_dm_dev_iomap(dev, 0); + if (!i2c->base) + { + err = -RT_EIO; + goto _fail; + } + + i2c->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(i2c->clk)) + { + err = rt_ptr_err(i2c->clk); + goto _fail; + } + if ((err = rt_clk_prepare_enable(i2c->clk))) + { + goto _fail; + } + i2c->input_rate = rt_clk_get_rate(i2c->clk); + + i2c->reset = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(i2c->reset)) + { + err = rt_ptr_err(i2c->reset); + goto _fail; + } + if (i2c->reset && (err = rt_reset_control_deassert(i2c->reset))) + { + goto _fail; + } + + sunxi_i2c_write(i2c, SUNXI_TWI_SRST, RT_BIT(0)); + sunxi_i2c_write(i2c, SUNXI_TWI_CTL, SUNXI_TWI_CTL_BUSEN); + if ((err = sunxi_i2c_set_bus_rate(i2c, I2C_MAX_STANDARD_MODE_FREQ))) + { + goto _fail; + } + + id = rt_ofw_get_alias_id(dev->ofw_node, "i2c"); + if (id >= 0) + { + rt_dm_dev_set_name(&i2c->parent.parent, "i2c%u", id); + } + else + { + rt_dm_dev_set_name_auto(&i2c->parent.parent, "i2c"); + } + name = rt_dm_dev_get_name(&i2c->parent.parent); + + i2c->parent.ops = &sunxi_i2c_ops; + i2c->parent.parent.ofw_node = dev->ofw_node; + dev->user_data = i2c; + + if ((err = rt_i2c_bus_device_register(&i2c->parent, name))) + { + goto _fail; + } + rt_dm_dev_bind_fwdata(dev, RT_NULL, i2c); + + return RT_EOK; + +_fail: + sunxi_i2c_free(i2c); + return err; +} + +static rt_err_t sunxi_i2c_remove(struct rt_platform_device *pdev) +{ + struct sunxi_i2c *i2c = pdev->parent.user_data; + + rt_dm_dev_unbind_fwdata(&pdev->parent, RT_NULL); + rt_device_unregister(&i2c->parent.parent); + sunxi_i2c_free(i2c); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_i2c_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-i2c" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_i2c_driver = +{ + .name = "sunxi-i2c", + .ids = sunxi_i2c_ofw_ids, + .probe = sunxi_i2c_probe, + .remove = sunxi_i2c_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_i2c_driver); diff --git a/bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-ccu.h b/bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-ccu.h new file mode 100644 index 000000000000..3053b2440a4d --- /dev/null +++ b/bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-ccu.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_CLOCK_SUN252I_F101_CCU_H__ +#define __DT_BINDINGS_CLOCK_SUN252I_F101_CCU_H__ + +/* Keep these IDs aligned with Linux's sun252i-f101-ccu binding. */ +#define SUN252I_F101_CLK_PLL_PERI 0 +#define SUN252I_F101_CLK_PLL_PERI_2X 1 +#define SUN252I_F101_CLK_PLL_PERI_1X 2 +#define SUN252I_F101_CLK_PLL_PERI_800M 3 +#define SUN252I_F101_CLK_PLL_PERI_480M 4 +#define SUN252I_F101_CLK_PLL_VIDEO0_4X 5 +#define SUN252I_F101_CLK_PLL_VIDEO0_2X 6 +#define SUN252I_F101_CLK_PLL_VIDEO0_1X 7 +#define SUN252I_F101_CLK_PLL_AUDIO1 8 +#define SUN252I_F101_CLK_PLL_AUDIO1_DIV2 9 +#define SUN252I_F101_CLK_PLL_AUDIO1_DIV5 10 +#define SUN252I_F101_CLK_PSI 11 +#define SUN252I_F101_CLK_APB0 12 +#define SUN252I_F101_CLK_APB1 13 +#define SUN252I_F101_CLK_APB_UART 14 +#define SUN252I_F101_CLK_MBUS 15 +#define SUN252I_F101_CLK_DE 16 +#define SUN252I_F101_CLK_BUS_DE 17 +#define SUN252I_F101_CLK_DI 18 +#define SUN252I_F101_CLK_BUS_DI 19 +#define SUN252I_F101_CLK_G2D 20 +#define SUN252I_F101_CLK_BUS_G2D 21 +#define SUN252I_F101_CLK_CE 22 +#define SUN252I_F101_CLK_BUS_CE 23 +#define SUN252I_F101_CLK_VE 24 +#define SUN252I_F101_CLK_BUS_VE 25 +#define SUN252I_F101_CLK_BUS_DMA 26 +#define SUN252I_F101_CLK_BUS_HSTIMER 27 +#define SUN252I_F101_CLK_BUS_AVS 28 +#define SUN252I_F101_CLK_BUS_DBGSYS 29 +#define SUN252I_F101_CLK_PWM_BL 30 +#define SUN252I_F101_CLK_BUS_PWM_BL 31 +#define SUN252I_F101_CLK_BUS_PWM 32 +#define SUN252I_F101_CLK_BUS_G2D_M 33 +#define SUN252I_F101_CLK_BUS_CSI_M 34 +#define SUN252I_F101_CLK_BUS_TVIN_M 35 +#define SUN252I_F101_CLK_BUS_VE_M 36 +#define SUN252I_F101_CLK_BUS_DMA_M 37 +#define SUN252I_F101_CLK_BUS_DRAM 38 +#define SUN252I_F101_CLK_SMHC0 39 +#define SUN252I_F101_CLK_SMHC2 40 +#define SUN252I_F101_CLK_BUS_SMHC2 41 +#define SUN252I_F101_CLK_BUS_SMHC0 42 +#define SUN252I_F101_CLK_BUS_UART5 43 +#define SUN252I_F101_CLK_BUS_UART4 44 +#define SUN252I_F101_CLK_BUS_UART3 45 +#define SUN252I_F101_CLK_BUS_UART2 46 +#define SUN252I_F101_CLK_BUS_UART1 47 +#define SUN252I_F101_CLK_BUS_UART0 48 +#define SUN252I_F101_CLK_BUS_TWI2 49 +#define SUN252I_F101_CLK_BUS_TWI1 50 +#define SUN252I_F101_CLK_BUS_TWI0 51 +#define SUN252I_F101_CLK_SPI0 52 +#define SUN252I_F101_CLK_SPI1 53 +#define SUN252I_F101_CLK_BUS_SPI1 54 +#define SUN252I_F101_CLK_BUS_SPI0 55 +#define SUN252I_F101_CLK_SPIF 56 +#define SUN252I_F101_CLK_BUS_SPIF 57 +#define SUN252I_F101_CLK_IRRX 58 +#define SUN252I_F101_CLK_BUS_IRRX 59 +#define SUN252I_F101_CLK_CAN0 60 +#define SUN252I_F101_CLK_CAN1 61 +#define SUN252I_F101_CLK_BUS_GPADC 62 +#define SUN252I_F101_CLK_BUS_THS 63 +#define SUN252I_F101_CLK_I2S0 64 +#define SUN252I_F101_CLK_BUS_I2S0 65 +#define SUN252I_F101_CLK_OWA_TX 66 +#define SUN252I_F101_CLK_OWA_RX 67 +#define SUN252I_F101_CLK_BUS_OWA 68 +#define SUN252I_F101_CLK_AUDIO_CODEC_DAC 69 +#define SUN252I_F101_CLK_AUDIO_CODEC_ADC 70 +#define SUN252I_F101_CLK_BUS_AUDIO_CODEC 71 +#define SUN252I_F101_CLK_BUS_USB 72 +#define SUN252I_F101_CLK_BUS_USBOTG0 73 +#define SUN252I_F101_CLK_BUS_USBEHCI0 74 +#define SUN252I_F101_CLK_BUS_USBOHCI0 75 +#define SUN252I_F101_CLK_COMBOPHY0 76 +#define SUN252I_F101_CLK_BUS_COMBOPHY0 77 +#define SUN252I_F101_CLK_BUS_DPSS_TOP 78 +#define SUN252I_F101_CLK_DSI 79 +#define SUN252I_F101_CLK_BUS_DSI 80 +#define SUN252I_F101_CLK_TCONLCD 81 +#define SUN252I_F101_CLK_BUS_TCONLCD 82 +#define SUN252I_F101_CLK_CSI 83 +#define SUN252I_F101_CLK_CSI_MASTER 84 +#define SUN252I_F101_CLK_BUS_CSI 85 +#define SUN252I_F101_CLK_TVIN_MASTER 86 +#define SUN252I_F101_CLK_BUS_TVIN 87 +#define SUN252I_F101_CLK_BUS_TVIN_TOP 88 +#define SUN252I_F101_CLK_TPADC 89 +#define SUN252I_F101_CLK_BUS_TPADC 90 +#define SUN252I_F101_CLK_BUS_RISCV_CFG 91 +#define SUN252I_F101_CLK_BUS_CLK32K 92 +#define SUN252I_F101_CLK_BUS_CLK25M 93 +#define SUN252I_F101_CLK_BUS_CLK16M 94 +#define SUN252I_F101_CLK_BUS_CLK12M 95 +#define SUN252I_F101_CLK_BUS_CLK24M 96 +#define SUN252I_F101_CLK_PCLK 97 +#define SUN252I_F101_CLK_BUS_CLK_FANOUT2 98 +#define SUN252I_F101_CLK_BUS_CLK_FANOUT1 99 +#define SUN252I_F101_CLK_BUS_CLK_FANOUT0 100 + +#define SUN252I_F101_CLK_NUMBER 101 + +#endif /* __DT_BINDINGS_CLOCK_SUN252I_F101_CCU_H__ */ diff --git a/bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-cpupll.h b/bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-cpupll.h new file mode 100644 index 000000000000..209e4f2f6b4e --- /dev/null +++ b/bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-cpupll.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_CLOCK_SUN252I_F101_CPUPLL_H__ +#define __DT_BINDINGS_CLOCK_SUN252I_F101_CPUPLL_H__ + +#define CLK_PLL_CPU 0 +#define CLK_RISCV 1 +#define CLK_RISCV_AXI 2 +#define CLK_CPUPLL_NUMBER 3 + +#endif /* __DT_BINDINGS_CLOCK_SUN252I_F101_CPUPLL_H__ */ diff --git a/bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-rtc.h b/bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-rtc.h new file mode 100644 index 000000000000..7a4b0fed51bc --- /dev/null +++ b/bsp/allwinner/dm/include/dt-bindings/clock/sun252i-f101-rtc.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_CLOCK_SUN252I_F101_RTC_H__ +#define __DT_BINDINGS_CLOCK_SUN252I_F101_RTC_H__ + +#define CLK_RTC_IOSC 0 +#define CLK_RTC_IOSC_DIV32K 1 +#define CLK_RTC_DCXO24M_DIV32K 2 +#define CLK_RTC_32K 3 +#define CLK_RTC_1K 4 +#define CLK_RTC_32K_FANOUT 5 +#define CLK_RTC_SPI 6 +#define CLK_RTC_CLK1M_RC 7 +#define CLK_RTC_NUMBER 8 + +#endif /* __DT_BINDINGS_CLOCK_SUN252I_F101_RTC_H__ */ diff --git a/bsp/allwinner/dm/include/dt-bindings/reset/sun252i-f101-ccu.h b/bsp/allwinner/dm/include/dt-bindings/reset/sun252i-f101-ccu.h new file mode 100644 index 000000000000..807a39e22f19 --- /dev/null +++ b/bsp/allwinner/dm/include/dt-bindings/reset/sun252i-f101-ccu.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DT_BINDINGS_RESET_SUN252I_F101_CCU_H__ +#define __DT_BINDINGS_RESET_SUN252I_F101_CCU_H__ + +#define SUN252I_F101_RST_BUS_PLL_SSC 0 +#define SUN252I_F101_RST_MBUS 1 +#define SUN252I_F101_RST_BUS_DE 2 +#define SUN252I_F101_RST_BUS_DI 3 +#define SUN252I_F101_RST_BUS_G2D 4 +#define SUN252I_F101_RST_BUS_CE 5 +#define SUN252I_F101_RST_BUS_VE 6 +#define SUN252I_F101_RST_BUS_DMA 7 +#define SUN252I_F101_RST_BUS_HSTIMER 8 +#define SUN252I_F101_RST_BUS_DBGSYS 9 +#define SUN252I_F101_RST_BUS_PWM_BL 10 +#define SUN252I_F101_RST_BUS_PWM 11 +#define SUN252I_F101_RST_BUS_DRAM_MOD 12 +#define SUN252I_F101_RST_BUS_LPSRAM 13 +#define SUN252I_F101_RST_BUS_DRAM 14 +#define SUN252I_F101_RST_BUS_SMHC2 15 +#define SUN252I_F101_RST_BUS_SMHC0 16 +#define SUN252I_F101_RST_BUS_UART5 17 +#define SUN252I_F101_RST_BUS_UART4 18 +#define SUN252I_F101_RST_BUS_UART3 19 +#define SUN252I_F101_RST_BUS_UART2 20 +#define SUN252I_F101_RST_BUS_UART1 21 +#define SUN252I_F101_RST_BUS_UART0 22 +#define SUN252I_F101_RST_BUS_TWI2 23 +#define SUN252I_F101_RST_BUS_TWI1 24 +#define SUN252I_F101_RST_BUS_TWI0 25 +#define SUN252I_F101_RST_BUS_SPI1 26 +#define SUN252I_F101_RST_BUS_SPI0 27 +#define SUN252I_F101_RST_BUS_SPIF 28 +#define SUN252I_F101_RST_BUS_IRRX 29 +#define SUN252I_F101_RST_BUS_CAN0 30 +#define SUN252I_F101_RST_BUS_CAN1 31 +#define SUN252I_F101_RST_BUS_GPADC 32 +#define SUN252I_F101_RST_BUS_THS 33 +#define SUN252I_F101_RST_BUS_I2S0 34 +#define SUN252I_F101_RST_BUS_OWA 35 +#define SUN252I_F101_RST_BUS_AUDIO 36 +#define SUN252I_F101_RST_USB_PHY0 37 +#define SUN252I_F101_RST_USB_OTG0 38 +#define SUN252I_F101_RST_USB_EHCI0 39 +#define SUN252I_F101_RST_USB_OHCI0 40 +#define SUN252I_F101_RST_BUS_DPSS 41 +#define SUN252I_F101_RST_BUS_DSI 42 +#define SUN252I_F101_RST_BUS_TCONLCD 43 +#define SUN252I_F101_RST_BUS_LVDS0 44 +#define SUN252I_F101_RST_BUS_CSI 45 +#define SUN252I_F101_RST_BUS_TVIN 46 +#define SUN252I_F101_RST_BUS_TVIN_TOP 47 +#define SUN252I_F101_RST_BUS_TPADC 48 + +#define SUN252I_F101_RST_NUMBER 49 + +#endif /* __DT_BINDINGS_RESET_SUN252I_F101_CCU_H__ */ diff --git a/bsp/allwinner/dm/include/sunxi-g2d.h b/bsp/allwinner/dm/include/sunxi-g2d.h new file mode 100644 index 000000000000..487a741d7e11 --- /dev/null +++ b/bsp/allwinner/dm/include/sunxi-g2d.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SUNXI_G2D_H__ +#define __SUNXI_G2D_H__ + +#include + +struct sunxi_g2d_buffer +{ + void *virt_addr; + rt_ubase_t phys_addr; + rt_size_t size; +}; + +/* + * A G2D-200 RCQ block starts with an array of these 16-byte headers. + * The register data addressed by a header must live in the same DMA buffer. + */ +struct sunxi_g2d_rcq_header +{ + rt_uint32_t data_address; + rt_uint32_t data_length_high; + rt_uint32_t control; + rt_uint32_t register_offset; +}; + +struct sunxi_g2d_rcq_submit +{ + struct sunxi_g2d_buffer *buffer; + rt_uint32_t header_offset; + rt_uint16_t header_count; + rt_tick_t timeout; +}; + +enum sunxi_g2d_format +{ + SUNXI_G2D_FORMAT_ARGB8888, + SUNXI_G2D_FORMAT_ABGR8888, + SUNXI_G2D_FORMAT_RGBA8888, + SUNXI_G2D_FORMAT_BGRA8888, + SUNXI_G2D_FORMAT_XRGB8888, + SUNXI_G2D_FORMAT_XBGR8888, + SUNXI_G2D_FORMAT_RGB888, + SUNXI_G2D_FORMAT_BGR888, + SUNXI_G2D_FORMAT_RGB565, + SUNXI_G2D_FORMAT_BGR565, + SUNXI_G2D_FORMAT_ARGB4444, + SUNXI_G2D_FORMAT_ARGB1555, + SUNXI_G2D_FORMAT_NV12, + SUNXI_G2D_FORMAT_NV21, + SUNXI_G2D_FORMAT_I420, + SUNXI_G2D_FORMAT_YUYV, + SUNXI_G2D_FORMAT_MAX, +}; + +enum sunxi_g2d_rotation +{ + SUNXI_G2D_ROTATE_0, + SUNXI_G2D_ROTATE_90, + SUNXI_G2D_ROTATE_180, + SUNXI_G2D_ROTATE_270, +}; + +enum sunxi_g2d_alpha_mode +{ + SUNXI_G2D_ALPHA_PIXEL, + SUNXI_G2D_ALPHA_GLOBAL, + SUNXI_G2D_ALPHA_MIXED, +}; + +enum sunxi_g2d_blend_mode +{ + SUNXI_G2D_BLEND_CLEAR, + SUNXI_G2D_BLEND_SRC, + SUNXI_G2D_BLEND_DST, + SUNXI_G2D_BLEND_SRC_OVER, + SUNXI_G2D_BLEND_DST_OVER, + SUNXI_G2D_BLEND_SRC_IN, + SUNXI_G2D_BLEND_DST_IN, + SUNXI_G2D_BLEND_SRC_OUT, + SUNXI_G2D_BLEND_DST_OUT, + SUNXI_G2D_BLEND_SRC_ATOP, + SUNXI_G2D_BLEND_DST_ATOP, + SUNXI_G2D_BLEND_XOR, +}; + +#define SUNXI_G2D_FLIP_H RT_BIT(0) +#define SUNXI_G2D_FLIP_V RT_BIT(1) +#define SUNXI_G2D_SRC_PREMULTIPLIED RT_BIT(2) +#define SUNXI_G2D_DST_PREMULTIPLIED RT_BIT(3) +#define SUNXI_G2D_NO_CACHE_OPS RT_BIT(4) +#define SUNXI_G2D_YUV_FULL_RANGE RT_BIT(5) +#define SUNXI_G2D_YUV_BT709 RT_BIT(6) + +struct sunxi_g2d_image +{ + struct sunxi_g2d_buffer *buffer; + rt_uint16_t width; + rt_uint16_t height; + rt_uint16_t pitch; + enum sunxi_g2d_format format; +}; + +struct sunxi_g2d_rect +{ + rt_uint16_t x; + rt_uint16_t y; + rt_uint16_t width; + rt_uint16_t height; +}; + +/* One physical plane. Buffers must be allocated or imported through g2d0. */ +struct sunxi_g2d_plane +{ + struct sunxi_g2d_buffer *buffer; + rt_size_t offset; + rt_uint32_t pitch; +}; + +struct sunxi_g2d_surface +{ + enum sunxi_g2d_format format; + rt_uint16_t width; + rt_uint16_t height; + struct sunxi_g2d_plane plane[3]; +}; + +struct sunxi_g2d_blend +{ + enum sunxi_g2d_blend_mode mode; + enum sunxi_g2d_alpha_mode foreground_alpha_mode; + rt_uint8_t foreground_alpha; + enum sunxi_g2d_alpha_mode background_alpha_mode; + rt_uint8_t background_alpha; + rt_bool_t color_key; + rt_uint32_t color_key_min; + rt_uint32_t color_key_max; +}; + +enum sunxi_g2d_operation_type +{ + SUNXI_G2D_OPERATION_FILL, + SUNXI_G2D_OPERATION_BLIT, + SUNXI_G2D_OPERATION_BLEND, +}; + +/* Generic operation for graphics planes and compositor attributes. */ +struct sunxi_g2d_operation +{ + enum sunxi_g2d_operation_type type; + rt_uint32_t flags; + rt_uint32_t color; + struct sunxi_g2d_surface destination; + struct sunxi_g2d_rect destination_rect; + struct sunxi_g2d_surface source; + struct sunxi_g2d_rect source_rect; + enum sunxi_g2d_rotation rotation; + struct sunxi_g2d_surface background; + struct sunxi_g2d_rect background_rect; + struct sunxi_g2d_blend blend; + rt_tick_t timeout; +}; + +struct sunxi_g2d_fill +{ + struct sunxi_g2d_image *destination; + struct sunxi_g2d_rect rect; + rt_uint32_t color; + rt_tick_t timeout; +}; + +struct sunxi_g2d_blit +{ + struct sunxi_g2d_image *source; + struct sunxi_g2d_rect source_rect; + struct sunxi_g2d_image *destination; + struct sunxi_g2d_rect destination_rect; + rt_tick_t timeout; +}; + +#define SUNXI_G2D_CMD_ALLOC_BUFFER \ + (RT_DEVICE_CTRL_BASE(Miscellaneous) + 0) +#define SUNXI_G2D_CMD_FREE_BUFFER \ + (RT_DEVICE_CTRL_BASE(Miscellaneous) + 1) +#define SUNXI_G2D_CMD_SUBMIT_RCQ \ + (RT_DEVICE_CTRL_BASE(Miscellaneous) + 2) +#define SUNXI_G2D_CMD_GET_VERSION \ + (RT_DEVICE_CTRL_BASE(Miscellaneous) + 3) +#define SUNXI_G2D_CMD_FILL \ + (RT_DEVICE_CTRL_BASE(Miscellaneous) + 4) +#define SUNXI_G2D_CMD_BLIT \ + (RT_DEVICE_CTRL_BASE(Miscellaneous) + 5) +#define SUNXI_G2D_CMD_COMPOSE \ + (RT_DEVICE_CTRL_BASE(Miscellaneous) + 6) +#define SUNXI_G2D_CMD_IMPORT_BUFFER \ + (RT_DEVICE_CTRL_BASE(Miscellaneous) + 7) + +#endif /* __SUNXI_G2D_H__ */ diff --git a/bsp/allwinner/dm/include/sunxi-jpegd.h b/bsp/allwinner/dm/include/sunxi-jpegd.h new file mode 100644 index 000000000000..c44e94596589 --- /dev/null +++ b/bsp/allwinner/dm/include/sunxi-jpegd.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SUNXI_JPEGD_H__ +#define __SUNXI_JPEGD_H__ + +#include + +/* + * Buffers passed to the VE must be allocated by a VE codec device. This keeps + * the CPU address and DMA address paired on both MMU and no-MMU systems. + */ +struct sunxi_jpegd_buffer +{ + void *virt_addr; + rt_ubase_t phys_addr; + rt_size_t size; +}; + +enum sunxi_jpegd_format +{ + SUNXI_JPEGD_FORMAT_NV12, + SUNXI_JPEGD_FORMAT_YV12, + SUNXI_JPEGD_FORMAT_MB32_420, +}; + +struct sunxi_jpegd_decode +{ + struct sunxi_jpegd_buffer *input; + struct sunxi_jpegd_buffer *output; + rt_size_t input_size; + rt_tick_t timeout; + + rt_uint16_t width; + rt_uint16_t height; + rt_uint16_t stride; + rt_uint16_t format; + rt_size_t luma_size; +}; + +struct sunxi_jpegd_info +{ + struct sunxi_jpegd_buffer *input; + rt_size_t input_size; + + rt_uint16_t width; + rt_uint16_t height; + rt_uint16_t stride; + rt_uint16_t format; + rt_size_t luma_size; + rt_size_t output_size; +}; + +#define SUNXI_JPEGD_CMD_ALLOC_BUFFER \ + (RT_DEVICE_CTRL_BASE(Char) + 0) +#define SUNXI_JPEGD_CMD_FREE_BUFFER \ + (RT_DEVICE_CTRL_BASE(Char) + 1) +#define SUNXI_JPEGD_CMD_DECODE \ + (RT_DEVICE_CTRL_BASE(Char) + 2) +#define SUNXI_JPEGD_CMD_GET_VERSION \ + (RT_DEVICE_CTRL_BASE(Char) + 3) +#define SUNXI_JPEGD_CMD_GET_INFO \ + (RT_DEVICE_CTRL_BASE(Char) + 4) + +#endif /* __SUNXI_JPEGD_H__ */ diff --git a/bsp/allwinner/dm/include/sunxi-jpege.h b/bsp/allwinner/dm/include/sunxi-jpege.h new file mode 100644 index 000000000000..553c91ec2521 --- /dev/null +++ b/bsp/allwinner/dm/include/sunxi-jpege.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SUNXI_JPEGE_H__ +#define __SUNXI_JPEGE_H__ + +#include + +/* + * The VE encoder consumes contiguous NV12 frames. Buffers must be allocated + * by either jpegd or jpege so their CPU and DMA addresses remain paired. + */ +struct sunxi_jpege_encode +{ + struct sunxi_jpegd_buffer *input; + struct sunxi_jpegd_buffer *output; + rt_tick_t timeout; + rt_size_t output_size; + + rt_uint16_t width; + rt_uint16_t height; + rt_uint16_t stride; + rt_uint16_t format; + rt_uint8_t quality; +}; + +#define SUNXI_JPEGE_CMD_ALLOC_BUFFER \ + SUNXI_JPEGD_CMD_ALLOC_BUFFER +#define SUNXI_JPEGE_CMD_FREE_BUFFER \ + SUNXI_JPEGD_CMD_FREE_BUFFER +#define SUNXI_JPEGE_CMD_ENCODE \ + (RT_DEVICE_CTRL_BASE(Char) + 2) +#define SUNXI_JPEGE_CMD_GET_VERSION \ + SUNXI_JPEGD_CMD_GET_VERSION + +#endif /* __SUNXI_JPEGE_H__ */ diff --git a/bsp/allwinner/dm/include/sunxi-pngd.h b/bsp/allwinner/dm/include/sunxi-pngd.h new file mode 100644 index 000000000000..7a48c22a3ce0 --- /dev/null +++ b/bsp/allwinner/dm/include/sunxi-pngd.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SUNXI_PNGD_H__ +#define __SUNXI_PNGD_H__ + +#include + +/* + * The PNG decoder consumes a complete PNG stream and writes RGBA8888 pixels. + * Both buffers must be allocated by one of the VE codec devices. + */ +enum sunxi_pngd_format +{ + SUNXI_PNGD_FORMAT_RGBA8888, +}; + +struct sunxi_pngd_info +{ + struct sunxi_jpegd_buffer *input; + rt_size_t input_size; + + rt_uint32_t width; + rt_uint32_t height; + rt_uint32_t stride; + rt_uint32_t format; + rt_size_t output_size; +}; + +struct sunxi_pngd_decode +{ + struct sunxi_jpegd_buffer *input; + struct sunxi_jpegd_buffer *output; + rt_size_t input_size; + rt_tick_t timeout; + + rt_uint32_t width; + rt_uint32_t height; + rt_uint32_t stride; + rt_uint32_t format; +}; + +#define SUNXI_PNGD_CMD_ALLOC_BUFFER \ + SUNXI_JPEGD_CMD_ALLOC_BUFFER +#define SUNXI_PNGD_CMD_FREE_BUFFER \ + SUNXI_JPEGD_CMD_FREE_BUFFER +#define SUNXI_PNGD_CMD_DECODE \ + (RT_DEVICE_CTRL_BASE(Char) + 2) +#define SUNXI_PNGD_CMD_GET_VERSION \ + SUNXI_JPEGD_CMD_GET_VERSION +#define SUNXI_PNGD_CMD_GET_INFO \ + (RT_DEVICE_CTRL_BASE(Char) + 4) + +#endif /* __SUNXI_PNGD_H__ */ diff --git a/bsp/allwinner/dm/include/sunxi_ccu.h b/bsp/allwinner/dm/include/sunxi_ccu.h new file mode 100644 index 000000000000..01cdae865220 --- /dev/null +++ b/bsp/allwinner/dm/include/sunxi_ccu.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SUNXI_CCU_H__ +#define __SUNXI_CCU_H__ + +#include + +struct sunxi_clk_desc +{ + int id; + const char *name; + const char *rate_property; + rt_uint16_t gate_reg; + rt_uint32_t gate_mask; + rt_ubase_t rate_default; + + const rt_ubase_t *parent_rates; + rt_uint8_t parent_rates_nr; + rt_uint8_t m_shift; + rt_uint8_t m_width; + rt_uint8_t p_shift; + rt_uint8_t p_width; + rt_uint8_t mux_shift; + rt_uint8_t mux_width; +}; + +struct sunxi_ccu_data +{ + const struct sunxi_clk_desc *clks; + rt_size_t clks_nr; + rt_size_t cells_nr; +}; + +extern const struct sunxi_ccu_data sun252i_f101_ccu_data; + +#endif /* __SUNXI_CCU_H__ */ diff --git a/bsp/allwinner/dm/include/sunxi_pinctrl.h b/bsp/allwinner/dm/include/sunxi_pinctrl.h new file mode 100644 index 000000000000..d582a4ef586d --- /dev/null +++ b/bsp/allwinner/dm/include/sunxi_pinctrl.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SUNXI_PINCTRL_H__ +#define __SUNXI_PINCTRL_H__ + +#include + +struct sunxi_pinctrl_layout +{ + rt_uint16_t bank_stride; + rt_uint16_t mux_reg; + rt_uint16_t data_reg; + rt_uint16_t drive_reg; + rt_uint16_t pull_reg; + rt_uint8_t mux_bits; + rt_uint8_t drive_bits; + rt_uint8_t pull_bits; + rt_uint8_t disabled_mux; +}; + +struct sunxi_pinctrl_function +{ + const char *name; + rt_uint8_t bank; + rt_uint8_t first_pin; + rt_uint8_t pin_count; + rt_uint8_t mux; +}; + +struct sunxi_pinctrl_data +{ + const struct sunxi_pinctrl_layout *layout; + const rt_uint8_t *bank_pins; + const struct sunxi_pinctrl_function *functions; + rt_size_t functions_nr; + rt_uint8_t banks_nr; + rt_uint8_t irq_banks; + rt_uint8_t irq_mux; +}; + +extern const struct sunxi_pinctrl_data sun252i_f101_pinctrl_data; + +#endif /* __SUNXI_PINCTRL_H__ */ diff --git a/bsp/allwinner/dm/include/sunxi_reset.h b/bsp/allwinner/dm/include/sunxi_reset.h new file mode 100644 index 000000000000..9d73dbbd8e9c --- /dev/null +++ b/bsp/allwinner/dm/include/sunxi_reset.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SUNXI_RESET_H__ +#define __SUNXI_RESET_H__ + +#include + +struct sunxi_reset_line +{ + rt_uint16_t reg; + rt_uint32_t mask; +}; + +struct sunxi_reset_data +{ + const struct sunxi_reset_line *lines; + rt_size_t lines_nr; + rt_uint32_t assert_delay_us; +}; + +extern const struct sunxi_reset_data sun252i_f101_reset_data; + +#endif /* __SUNXI_RESET_H__ */ diff --git a/bsp/allwinner/dm/input/SConscript b/bsp/allwinner/dm/input/SConscript new file mode 100644 index 000000000000..3d15055d62d2 --- /dev/null +++ b/bsp/allwinner/dm/input/SConscript @@ -0,0 +1,13 @@ +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/allwinner/dm/input/touchscreen/Kconfig b/bsp/allwinner/dm/input/touchscreen/Kconfig new file mode 100644 index 000000000000..57c03f9bb0fb --- /dev/null +++ b/bsp/allwinner/dm/input/touchscreen/Kconfig @@ -0,0 +1,8 @@ +config RT_INPUT_TOUCHSCREEN_SUNXI_TPADC + bool "Allwinner Sunxi TPADC resistive touchscreen" + depends on RT_INPUT_TOUCHSCREEN + depends on RT_USING_CLK + depends on RT_USING_RESET + depends on RT_USING_OFW + depends on RT_USING_PINCTRL + default n diff --git a/bsp/allwinner/dm/input/touchscreen/SConscript b/bsp/allwinner/dm/input/touchscreen/SConscript new file mode 100644 index 000000000000..ea248876c535 --- /dev/null +++ b/bsp/allwinner/dm/input/touchscreen/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../../include'] + +if GetDepend(['RT_INPUT_TOUCHSCREEN_SUNXI_TPADC']): + src += ['ts-sunxi-tpadc.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/input/touchscreen/ts-sunxi-tpadc.c b/bsp/allwinner/dm/input/touchscreen/ts-sunxi-tpadc.c new file mode 100644 index 000000000000..71030cac4402 --- /dev/null +++ b/bsp/allwinner/dm/input/touchscreen/ts-sunxi-tpadc.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "input.ts.sunxi-tpadc" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_TPADC_CTRL0 0x00 +#define SUNXI_TPADC_CTRL1 0x04 +#define SUNXI_TPADC_CTRL2 0x08 +#define SUNXI_TPADC_CTRL3 0x0c +#define SUNXI_TPADC_FIFO_CTRL 0x10 +#define SUNXI_TPADC_FIFO_STATUS 0x14 +#define SUNXI_TPADC_DATA 0x24 + +#define SUNXI_TPADC_CTRL1_CALIBRATE RT_BIT(7) +#define SUNXI_TPADC_CTRL1_ENABLE RT_BIT(5) +#define SUNXI_TPADC_FIFO_FLUSH RT_BIT(4) +#define SUNXI_TPADC_FIFO_DOWN_IRQ RT_BIT(0) +#define SUNXI_TPADC_FIFO_UP_IRQ RT_BIT(1) +#define SUNXI_TPADC_FIFO_DATA_IRQ RT_BIT(16) +#define SUNXI_TPADC_FIFO_OVERRUN_IRQ RT_BIT(17) +#define SUNXI_TPADC_FIFO_TRIGGER (1U << 8) +#define SUNXI_TPADC_STATUS_UP RT_BIT(1) +#define SUNXI_TPADC_STATUS_DATA RT_BIT(16) +#define SUNXI_TPADC_STATUS_OVERRUN RT_BIT(17) +#define SUNXI_TPADC_DATA_MASK RT_GENMASK(11, 0) + +struct sunxi_tpadc +{ + struct rt_input_device parent; + + void *regs; + struct rt_clk *mod_clk; + struct rt_clk *bus_clk; + struct rt_reset_control *reset; + int irq; + rt_bool_t mod_clk_enabled; + rt_bool_t bus_clk_enabled; + rt_bool_t irq_attached; + rt_bool_t input_registered; + rt_bool_t touch_down; +}; + +static void sunxi_tpadc_report_up(struct sunxi_tpadc *tpadc) +{ + if (!tpadc->touch_down) + { + return; + } + + rt_input_report_touch_inactive(&tpadc->parent, RT_FALSE); + rt_input_report_key(&tpadc->parent, BTN_TOUCH, RT_FALSE); + rt_input_sync(&tpadc->parent); + tpadc->touch_down = RT_FALSE; +} + +static void sunxi_tpadc_isr(int irq, void *param) +{ + rt_uint32_t status; + rt_uint32_t x; + rt_uint32_t y; + struct sunxi_tpadc *tpadc = param; + + RT_UNUSED(irq); + + status = HWREG32(tpadc->regs + SUNXI_TPADC_FIFO_STATUS); + if (status & SUNXI_TPADC_STATUS_DATA) + { + x = HWREG32(tpadc->regs + SUNXI_TPADC_DATA) & SUNXI_TPADC_DATA_MASK; + y = HWREG32(tpadc->regs + SUNXI_TPADC_DATA) & SUNXI_TPADC_DATA_MASK; + + if (!tpadc->touch_down) + { + rt_input_report_key(&tpadc->parent, BTN_TOUCH, RT_TRUE); + tpadc->touch_down = RT_TRUE; + } + + rt_input_report_touch_inactive(&tpadc->parent, RT_TRUE); + rt_input_report_touch_position(&tpadc->parent, x, y, RT_FALSE); + rt_input_sync(&tpadc->parent); + } + + if (status & SUNXI_TPADC_STATUS_UP) + { + sunxi_tpadc_report_up(tpadc); + } + + if (status & SUNXI_TPADC_STATUS_OVERRUN) + { + LOG_W("touch FIFO overrun"); + } + + HWREG32(tpadc->regs + SUNXI_TPADC_FIFO_STATUS) = status; +} + +static rt_err_t sunxi_tpadc_calibrate(struct sunxi_tpadc *tpadc) +{ + rt_uint32_t timeout; + + HWREG32(tpadc->regs + SUNXI_TPADC_CTRL1) |= SUNXI_TPADC_CTRL1_CALIBRATE; + for (timeout = 0; timeout < 1000; ++timeout) + { + if (!(HWREG32(tpadc->regs + SUNXI_TPADC_CTRL1) & + SUNXI_TPADC_CTRL1_CALIBRATE)) + { + return RT_EOK; + } + + rt_hw_us_delay(1); + } + + return -RT_ETIMEOUT; +} + +static rt_err_t sunxi_tpadc_start(struct sunxi_tpadc *tpadc) +{ + rt_err_t err; + rt_uint32_t fifo_ctrl; + + HWREG32(tpadc->regs + SUNXI_TPADC_FIFO_CTRL) = SUNXI_TPADC_FIFO_FLUSH; + HWREG32(tpadc->regs + SUNXI_TPADC_FIFO_STATUS) = RT_UINT32_MAX; + + if ((err = sunxi_tpadc_calibrate(tpadc))) + { + return err; + } + + /* 24 MHz source, 2 us acquisition time, and 1 kHz sample interval. */ + HWREG32(tpadc->regs + SUNXI_TPADC_CTRL0) = 149 | (8U << 16) | + (3U << 20) | RT_BIT(23) | (15U << 24); + HWREG32(tpadc->regs + SUNXI_TPADC_CTRL1) = SUNXI_TPADC_CTRL1_ENABLE; + HWREG32(tpadc->regs + SUNXI_TPADC_CTRL2) = 0x800fff | RT_BIT(24) | + (15U << 28); + HWREG32(tpadc->regs + SUNXI_TPADC_CTRL3) = 1 | RT_BIT(2); + + fifo_ctrl = SUNXI_TPADC_FIFO_DOWN_IRQ | SUNXI_TPADC_FIFO_UP_IRQ | + SUNXI_TPADC_FIFO_DATA_IRQ | SUNXI_TPADC_FIFO_OVERRUN_IRQ | + SUNXI_TPADC_FIFO_TRIGGER; + HWREG32(tpadc->regs + SUNXI_TPADC_FIFO_CTRL) = fifo_ctrl; + HWREG32(tpadc->regs + SUNXI_TPADC_FIFO_STATUS) = RT_UINT32_MAX; + + return RT_EOK; +} + +static rt_err_t sunxi_tpadc_release(struct sunxi_tpadc *tpadc) +{ + rt_err_t err; + + if (tpadc->input_registered) + { + err = rt_input_device_unregister(&tpadc->parent); + if (err) + { + return err; + } + tpadc->input_registered = RT_FALSE; + } + else + { + rt_input_remove_config(&tpadc->parent); + } + + if (tpadc->irq_attached) + { + rt_pic_irq_disable(tpadc->irq); + rt_pic_detach_irq(tpadc->irq, tpadc); + } + if (tpadc->regs) + { + HWREG32(tpadc->regs + SUNXI_TPADC_FIFO_CTRL) = 0; + HWREG32(tpadc->regs + SUNXI_TPADC_CTRL1) = 0; + } + if (!rt_is_err_or_null(tpadc->reset)) + { + rt_reset_control_assert(tpadc->reset); + rt_reset_control_put(tpadc->reset); + } + if (tpadc->mod_clk_enabled) + { + rt_clk_disable_unprepare(tpadc->mod_clk); + } + if (tpadc->bus_clk_enabled) + { + rt_clk_disable_unprepare(tpadc->bus_clk); + } + if (!rt_is_err_or_null(tpadc->mod_clk)) + { + rt_clk_put(tpadc->mod_clk); + } + if (!rt_is_err_or_null(tpadc->bus_clk)) + { + rt_clk_put(tpadc->bus_clk); + } + if (tpadc->regs) + { + rt_iounmap(tpadc->regs); + } + rt_free(tpadc); + + return RT_EOK; +} + +static rt_err_t sunxi_tpadc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_touch_info touch_info = {}; + struct rt_device *dev = &pdev->parent; + struct sunxi_tpadc *tpadc = rt_calloc(1, sizeof(*tpadc)); + + if (!tpadc) + { + return -RT_ENOMEM; + } + + tpadc->regs = rt_dm_dev_iomap(dev, 0); + if (!tpadc->regs) + { + err = -RT_EIO; + goto _fail; + } + + tpadc->mod_clk = rt_clk_get_by_name(dev, "mod"); + tpadc->bus_clk = rt_clk_get_by_name(dev, "bus"); + tpadc->reset = rt_reset_control_get_by_name(dev, "bus"); + if (rt_is_err_or_null(tpadc->mod_clk) || + rt_is_err_or_null(tpadc->bus_clk) || + rt_is_err_or_null(tpadc->reset)) + { + err = rt_is_err(tpadc->mod_clk) ? rt_ptr_err(tpadc->mod_clk) : + (rt_is_err(tpadc->bus_clk) ? rt_ptr_err(tpadc->bus_clk) : + (rt_is_err(tpadc->reset) ? rt_ptr_err(tpadc->reset) : -RT_ENOENT)); + goto _fail; + } + + if ((err = rt_reset_control_deassert(tpadc->reset)) || + (err = rt_clk_prepare_enable(tpadc->mod_clk))) + { + goto _fail; + } + tpadc->mod_clk_enabled = RT_TRUE; + if ((err = rt_clk_prepare_enable(tpadc->bus_clk))) + { + goto _fail; + } + tpadc->bus_clk_enabled = RT_TRUE; + + if ((tpadc->irq = rt_dm_dev_get_irq(dev, 0)) < 0) + { + err = tpadc->irq; + goto _fail; + } + + tpadc->parent.parent.ofw_node = dev->ofw_node; + if ((err = rt_input_set_capability(&tpadc->parent, EV_KEY, BTN_TOUCH)) || + (err = rt_input_set_capability(&tpadc->parent, EV_ABS, ABS_X)) || + (err = rt_input_set_capability(&tpadc->parent, EV_ABS, ABS_Y))) + { + goto _fail; + } + rt_input_set_absinfo(&tpadc->parent, ABS_X, 0, 4095, 0, 0); + rt_input_set_absinfo(&tpadc->parent, ABS_Y, 0, 4095, 0, 0); + + touch_info.type = RT_TOUCH_TYPE_RESISTANCE; + touch_info.vendor = RT_TOUCH_VENDOR_UNKNOWN; + if ((err = rt_input_setup_touch(&tpadc->parent, 0, &touch_info))) + { + goto _fail; + } + if ((err = rt_input_device_register(&tpadc->parent))) + { + goto _fail; + } + tpadc->input_registered = RT_TRUE; + + if ((err = rt_pic_attach_irq(tpadc->irq, sunxi_tpadc_isr, tpadc, + "tpadc", RT_IRQ_F_NONE))) + { + goto _fail; + } + tpadc->irq_attached = RT_TRUE; + + if ((err = sunxi_tpadc_start(tpadc))) + { + goto _fail; + } + rt_pic_irq_enable(tpadc->irq); + + dev->user_data = tpadc; + + return RT_EOK; + +_fail: + sunxi_tpadc_release(tpadc); + return err; +} + +static rt_err_t sunxi_tpadc_remove(struct rt_platform_device *pdev) +{ + struct sunxi_tpadc *tpadc = pdev->parent.user_data; + + return sunxi_tpadc_release(tpadc); +} + +static const struct rt_ofw_node_id sunxi_tpadc_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-tpadc" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_tpadc_driver = +{ + .name = "sunxi-tpadc", + .ids = sunxi_tpadc_ofw_ids, + .probe = sunxi_tpadc_probe, + .remove = sunxi_tpadc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_tpadc_driver); diff --git a/bsp/allwinner/dm/inputcapture/Kconfig b/bsp/allwinner/dm/inputcapture/Kconfig new file mode 100644 index 000000000000..275cedb49012 --- /dev/null +++ b/bsp/allwinner/dm/inputcapture/Kconfig @@ -0,0 +1,8 @@ +config RT_INPUT_CAPTURE_SUNXI_IRRX + bool "Allwinner Sunxi IR receiver" + depends on RT_USING_INPUT_CAPTURE + depends on RT_USING_CLK + depends on RT_USING_RESET + depends on RT_USING_OFW + depends on RT_USING_PINCTRL + default n diff --git a/bsp/allwinner/dm/inputcapture/SConscript b/bsp/allwinner/dm/inputcapture/SConscript new file mode 100644 index 000000000000..5a8153560326 --- /dev/null +++ b/bsp/allwinner/dm/inputcapture/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_INPUT_CAPTURE_SUNXI_IRRX']): + src += ['inputcapture-sunxi-irrx.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/inputcapture/inputcapture-sunxi-irrx.c b/bsp/allwinner/dm/inputcapture/inputcapture-sunxi-irrx.c new file mode 100644 index 000000000000..75a89a6558d6 --- /dev/null +++ b/bsp/allwinner/dm/inputcapture/inputcapture-sunxi-irrx.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "inputcap.sunxi-irrx" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_IRRX_CTRL 0x00 +#define SUNXI_IRRX_RXCTRL 0x10 +#define SUNXI_IRRX_RXFIFO 0x20 +#define SUNXI_IRRX_RXINT 0x2c +#define SUNXI_IRRX_RXSTA 0x30 +#define SUNXI_IRRX_CONFIG 0x34 + +#define SUNXI_IRRX_GEN RT_BIT(0) +#define SUNXI_IRRX_RXEN RT_BIT(1) +#define SUNXI_IRRX_ENABLE RT_GENMASK(5, 4) +#define SUNXI_IRRX_MODE_BOTH (1U << 6) +#define SUNXI_IRRX_POLARITY_INVERT RT_BIT(2) + +#define SUNXI_IRRX_IRQ_OVERRUN RT_BIT(0) +#define SUNXI_IRRX_IRQ_PACKET_END RT_BIT(1) +#define SUNXI_IRRX_IRQ_AVAILABLE RT_BIT(4) +#define SUNXI_IRRX_IRQ_ENABLE RT_GENMASK(5, 0) +#define SUNXI_IRRX_FIFO_LEVEL(n) (((n) - 1U) << 8) +#define SUNXI_IRRX_FIFO_COUNT(v) (((v) >> 8) & 0x7f) + +#define SUNXI_IRRX_SAMPLE_DIV 256U +#define SUNXI_IRRX_SAMPLE_CLOCK_MHZ 24U + +struct sunxi_irrx +{ + struct rt_inputcapture_device parent; + + void *regs; + struct rt_clk *mod_clk; + struct rt_clk *bus_clk; + struct rt_reset_control *reset; + int irq; + rt_uint32_t pulse_cycles; + rt_uint32_t pulsewidth_us; + rt_bool_t pulse_level; + rt_bool_t pulse_pending; + rt_bool_t mod_clk_enabled; + rt_bool_t bus_clk_enabled; + rt_bool_t irq_attached; + rt_bool_t opened; +}; + +#define raw_to_sunxi_irrx(raw) \ + rt_container_of(raw, struct sunxi_irrx, parent) + +static rt_uint32_t sunxi_irrx_cycles_to_us(rt_uint32_t cycles) +{ + rt_uint64_t value; + + value = (rt_uint64_t)cycles * SUNXI_IRRX_SAMPLE_DIV; + + return (rt_uint32_t)((value + SUNXI_IRRX_SAMPLE_CLOCK_MHZ / 2U) / + SUNXI_IRRX_SAMPLE_CLOCK_MHZ); +} + +static rt_err_t sunxi_irrx_get_pulsewidth( + struct rt_inputcapture_device *inputcapture, + rt_uint32_t *pulsewidth_us) +{ + struct sunxi_irrx *irrx = raw_to_sunxi_irrx(inputcapture); + + *pulsewidth_us = irrx->pulsewidth_us; + + return RT_EOK; +} + +static void sunxi_irrx_report(struct sunxi_irrx *irrx) +{ + if (!irrx->pulse_pending) + { + return; + } + + irrx->pulsewidth_us = sunxi_irrx_cycles_to_us(irrx->pulse_cycles); + rt_hw_inputcapture_isr(&irrx->parent, irrx->pulse_level); + irrx->pulse_cycles = 0; + irrx->pulse_pending = RT_FALSE; +} + +static void sunxi_irrx_store_sample(struct sunxi_irrx *irrx, + rt_uint8_t sample) +{ + rt_bool_t level = (sample & RT_BIT(7)) != 0; + rt_uint32_t cycles = sample & RT_GENMASK(6, 0); + + if (!cycles) + { + return; + } + + if (irrx->pulse_pending && irrx->pulse_level == level) + { + irrx->pulse_cycles += cycles; + return; + } + + sunxi_irrx_report(irrx); + irrx->pulse_cycles = cycles; + irrx->pulse_level = level; + irrx->pulse_pending = RT_TRUE; +} + +static void sunxi_irrx_isr(int irq, void *param) +{ + rt_uint32_t count; + rt_uint32_t i; + rt_uint32_t status; + struct sunxi_irrx *irrx = param; + + RT_UNUSED(irq); + + status = HWREG32(irrx->regs + SUNXI_IRRX_RXSTA); + count = SUNXI_IRRX_FIFO_COUNT(status); + for (i = 0; i < count; ++i) + { + sunxi_irrx_store_sample(irrx, + HWREG32(irrx->regs + SUNXI_IRRX_RXFIFO)); + } + + if (status & SUNXI_IRRX_IRQ_PACKET_END) + { + sunxi_irrx_report(irrx); + } + if (status & SUNXI_IRRX_IRQ_OVERRUN) + { + LOG_W("receive FIFO overrun"); + irrx->pulse_cycles = 0; + irrx->pulse_pending = RT_FALSE; + } + + HWREG32(irrx->regs + SUNXI_IRRX_RXSTA) = status; +} + +static rt_err_t sunxi_irrx_open(struct rt_inputcapture_device *inputcapture) +{ + rt_err_t err; + struct sunxi_irrx *irrx = raw_to_sunxi_irrx(inputcapture); + + if (irrx->opened) + { + return RT_EOK; + } + + if ((err = rt_reset_control_deassert(irrx->reset))) + { + return err; + } + if ((err = rt_clk_prepare_enable(irrx->mod_clk))) + { + goto _assert_reset; + } + irrx->mod_clk_enabled = RT_TRUE; + if ((err = rt_clk_prepare_enable(irrx->bus_clk))) + { + goto _disable_mod_clk; + } + irrx->bus_clk_enabled = RT_TRUE; + + irrx->pulse_cycles = 0; + irrx->pulse_pending = RT_FALSE; + HWREG32(irrx->regs + SUNXI_IRRX_CTRL) = 0; + HWREG32(irrx->regs + SUNXI_IRRX_RXINT) = 0; + HWREG32(irrx->regs + SUNXI_IRRX_RXSTA) = RT_UINT32_MAX; + HWREG32(irrx->regs + SUNXI_IRRX_CONFIG) = 2U | (32U << 2) | + (11U << 8) | (32U << 16); + HWREG32(irrx->regs + SUNXI_IRRX_RXCTRL) = SUNXI_IRRX_POLARITY_INVERT; + HWREG32(irrx->regs + SUNXI_IRRX_RXINT) = SUNXI_IRRX_IRQ_OVERRUN | + SUNXI_IRRX_IRQ_PACKET_END | SUNXI_IRRX_IRQ_AVAILABLE | + SUNXI_IRRX_FIFO_LEVEL(20); + HWREG32(irrx->regs + SUNXI_IRRX_CTRL) = SUNXI_IRRX_GEN | + SUNXI_IRRX_RXEN | SUNXI_IRRX_ENABLE | SUNXI_IRRX_MODE_BOTH; + irrx->opened = RT_TRUE; + rt_pic_irq_unmask(irrx->irq); + + return RT_EOK; + +_disable_mod_clk: + rt_clk_disable_unprepare(irrx->mod_clk); + irrx->mod_clk_enabled = RT_FALSE; +_assert_reset: + rt_reset_control_assert(irrx->reset); + + return err; +} + +static rt_err_t sunxi_irrx_close(struct rt_inputcapture_device *inputcapture) +{ + struct sunxi_irrx *irrx = raw_to_sunxi_irrx(inputcapture); + + if (!irrx->opened) + { + return RT_EOK; + } + + rt_pic_irq_mask(irrx->irq); + HWREG32(irrx->regs + SUNXI_IRRX_RXINT) = 0; + HWREG32(irrx->regs + SUNXI_IRRX_CTRL) = 0; + rt_reset_control_assert(irrx->reset); + if (irrx->bus_clk_enabled) + { + rt_clk_disable_unprepare(irrx->bus_clk); + irrx->bus_clk_enabled = RT_FALSE; + } + if (irrx->mod_clk_enabled) + { + rt_clk_disable_unprepare(irrx->mod_clk); + irrx->mod_clk_enabled = RT_FALSE; + } + irrx->pulse_cycles = 0; + irrx->pulse_pending = RT_FALSE; + irrx->opened = RT_FALSE; + + return RT_EOK; +} + +static const struct rt_inputcapture_ops sunxi_irrx_ops = +{ + .open = sunxi_irrx_open, + .close = sunxi_irrx_close, + .get_pulsewidth = sunxi_irrx_get_pulsewidth, +}; + +static void sunxi_irrx_release(struct sunxi_irrx *irrx) +{ + if (irrx->opened) + { + sunxi_irrx_close(&irrx->parent); + } + if (irrx->irq_attached) + { + rt_pic_irq_disable(irrx->irq); + rt_pic_detach_irq(irrx->irq, irrx); + } + if (!rt_is_err_or_null(irrx->reset)) + { + rt_reset_control_put(irrx->reset); + } + if (!rt_is_err_or_null(irrx->mod_clk)) + { + rt_clk_put(irrx->mod_clk); + } + if (!rt_is_err_or_null(irrx->bus_clk)) + { + rt_clk_put(irrx->bus_clk); + } + if (irrx->regs) + { + rt_iounmap(irrx->regs); + } + rt_free(irrx); +} + +static rt_err_t sunxi_irrx_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *name; + struct rt_device *dev = &pdev->parent; + struct sunxi_irrx *irrx = rt_calloc(1, sizeof(*irrx)); + + if (!irrx) + { + return -RT_ENOMEM; + } + + irrx->regs = rt_dm_dev_iomap(dev, 0); + if (!irrx->regs) + { + err = -RT_EIO; + goto _fail; + } + + irrx->mod_clk = rt_clk_get_by_name(dev, "mod"); + irrx->bus_clk = rt_clk_get_by_name(dev, "bus"); + irrx->reset = rt_reset_control_get_by_name(dev, "bus"); + if (rt_is_err_or_null(irrx->mod_clk) || + rt_is_err_or_null(irrx->bus_clk) || + rt_is_err_or_null(irrx->reset)) + { + err = rt_is_err(irrx->mod_clk) ? rt_ptr_err(irrx->mod_clk) : + (rt_is_err(irrx->bus_clk) ? rt_ptr_err(irrx->bus_clk) : + (rt_is_err(irrx->reset) ? rt_ptr_err(irrx->reset) : -RT_ENOENT)); + goto _fail; + } + + irrx->irq = rt_dm_dev_get_irq(dev, 0); + if (irrx->irq < 0) + { + err = irrx->irq; + goto _fail; + } + if ((err = rt_pic_attach_irq(irrx->irq, sunxi_irrx_isr, irrx, + "irrx", RT_IRQ_F_NONE))) + { + goto _fail; + } + irrx->irq_attached = RT_TRUE; + rt_pic_irq_mask(irrx->irq); + + name = rt_dm_dev_get_name(dev); + irrx->parent.ops = &sunxi_irrx_ops; + if ((err = rt_device_inputcapture_register(&irrx->parent, name, irrx))) + { + goto _fail; + } + + dev->user_data = irrx; + + return RT_EOK; + +_fail: + sunxi_irrx_release(irrx); + return err; +} + +static const struct rt_ofw_node_id sunxi_irrx_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-irrx" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_irrx_driver = +{ + .name = "sunxi-irrx", + .ids = sunxi_irrx_ofw_ids, + .probe = sunxi_irrx_probe, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_irrx_driver); diff --git a/bsp/allwinner/dm/nvmem/Kconfig b/bsp/allwinner/dm/nvmem/Kconfig new file mode 100644 index 000000000000..6211b7160d93 --- /dev/null +++ b/bsp/allwinner/dm/nvmem/Kconfig @@ -0,0 +1,8 @@ +config RT_NVMEM_SUNXI_SID + bool "Allwinner Sunxi SID eFuse" + depends on RT_USING_NVMEM + default n + help + Enable read-only access to the Allwinner SID eFuse controller. + + Programming eFuse bits is intentionally not exposed by this driver. diff --git a/bsp/allwinner/dm/nvmem/SConscript b/bsp/allwinner/dm/nvmem/SConscript new file mode 100644 index 000000000000..cb2576b6038e --- /dev/null +++ b/bsp/allwinner/dm/nvmem/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_NVMEM_SUNXI_SID']): + src += ['nvmem-sunxi-sid.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c b/bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c new file mode 100644 index 000000000000..47b6877f5a3c --- /dev/null +++ b/bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "nvmem.sunxi" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_SID_PRCTL 0x00 +#define SUNXI_SID_PR_ADDR 0x04 +#define SUNXI_SID_RDKEY 0x0c + +#define SUNXI_SID_READ_START RT_BIT(1) +#define SUNXI_SID_READ_LOCK 0xadbf0000 +#define SUNXI_SID_READ_TIMEOUT 1000 + +struct sunxi_sid +{ + struct rt_nvmem_device parent; + + void *base; + struct rt_spinlock lock; +}; + +#define to_sunxi_sid(raw) rt_container_of(raw, struct sunxi_sid, parent) + +static rt_err_t sunxi_sid_read_word(struct sunxi_sid *sid, rt_uint32_t offset, + rt_uint32_t *value) +{ + rt_ubase_t level; + int retry; + + level = rt_spin_lock_irqsave(&sid->lock); + HWREG32(sid->base + SUNXI_SID_PR_ADDR) = offset / sizeof(*value); + HWREG32(sid->base + SUNXI_SID_PRCTL) = SUNXI_SID_READ_LOCK | + SUNXI_SID_READ_START; + + for (retry = 0; retry < SUNXI_SID_READ_TIMEOUT; ++retry) + { + if (!(HWREG32(sid->base + SUNXI_SID_PRCTL) & SUNXI_SID_READ_START)) + { + *value = HWREG32(sid->base + SUNXI_SID_RDKEY); + rt_spin_unlock_irqrestore(&sid->lock, level); + return RT_EOK; + } + rt_hw_us_delay(1); + } + + HWREG32(sid->base + SUNXI_SID_PRCTL) = 0; + rt_spin_unlock_irqrestore(&sid->lock, level); + return -RT_ETIMEOUT; +} + +static rt_ssize_t sunxi_sid_read(struct rt_nvmem_device *ndev, int offset, + void *buffer, rt_size_t bytes) +{ + rt_size_t copy; + rt_uint8_t *out = buffer; + struct sunxi_sid *sid = to_sunxi_sid(ndev); + + if (offset < 0 || (rt_size_t)offset > ndev->size || + bytes > ndev->size - offset) + { + return -RT_EINVAL; + } + + while (bytes) + { + rt_uint32_t value; + rt_size_t in_word = offset & (sizeof(value) - 1); + rt_err_t err = sunxi_sid_read_word(sid, offset - in_word, &value); + + if (err) + { + return err; + } + + copy = sizeof(value) - in_word; + if (copy > bytes) + { + copy = bytes; + } + rt_memcpy(out, (rt_uint8_t *)&value + in_word, copy); + out += copy; + offset += copy; + bytes -= copy; + } + + return out - (rt_uint8_t *)buffer; +} + +static void sunxi_sid_release(struct sunxi_sid *sid) +{ + if (sid->base) + { + rt_iounmap(sid->base); + } + rt_free(sid); +} + +static rt_err_t sunxi_sid_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct sunxi_sid *sid = rt_calloc(1, sizeof(*sid)); + + if (!sid) + { + return -RT_ENOMEM; + } + + sid->base = rt_dm_dev_iomap(dev, 0); + if (!sid->base) + { + err = -RT_EIO; + goto _fail; + } + + rt_spin_lock_init(&sid->lock); + sid->parent.parent.ofw_node = dev->ofw_node; + sid->parent.reg_read = sunxi_sid_read; + sid->parent.size = 64; + sid->parent.stride = sizeof(rt_uint32_t); + sid->parent.word_size = sizeof(rt_uint32_t); + sid->parent.read_only = RT_TRUE; + + if ((err = rt_nvmem_device_register(&sid->parent))) + { + goto _fail; + } + + dev->user_data = sid; + rt_dm_dev_bind_fwdata(dev, RT_NULL, sid); + return RT_EOK; + +_fail: + sunxi_sid_release(sid); + return err; +} + +static rt_err_t sunxi_sid_remove(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct sunxi_sid *sid = dev->user_data; + + err = rt_nvmem_device_unregister(&sid->parent); + if (err) + { + return err; + } + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + sunxi_sid_release(sid); + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_sid_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-sid" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_sid_driver = +{ + .name = "nvmem-sunxi-sid", + .ids = sunxi_sid_ofw_ids, + .probe = sunxi_sid_probe, + .remove = sunxi_sid_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_sid_driver); diff --git a/bsp/allwinner/dm/pinctrl/Kconfig b/bsp/allwinner/dm/pinctrl/Kconfig new file mode 100644 index 000000000000..8b4578746f19 --- /dev/null +++ b/bsp/allwinner/dm/pinctrl/Kconfig @@ -0,0 +1,5 @@ +config RT_PINCTRL_SUN252I_F101 + bool "Allwinner Sun252i F101 pin controller" + depends on RT_USING_PINCTRL + depends on RT_USING_OFW + default n diff --git a/bsp/allwinner/dm/pinctrl/SConscript b/bsp/allwinner/dm/pinctrl/SConscript new file mode 100644 index 000000000000..5a07cdcd411a --- /dev/null +++ b/bsp/allwinner/dm/pinctrl/SConscript @@ -0,0 +1,13 @@ +from building import * +Import('RTT_ROOT') + +cwd = GetCurrentDir() +src = [] + +if GetDepend(['RT_PINCTRL_SUN252I_F101']): + src += ['pinctrl-sunxi.c', 'pinctrl-sun252i-f101.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], + CPPPATH = [cwd + '/../include', RTT_ROOT + '/components/drivers/pin']) + +Return('group') diff --git a/bsp/allwinner/dm/pinctrl/pinctrl-sun252i-f101.c b/bsp/allwinner/dm/pinctrl/pinctrl-sun252i-f101.c new file mode 100644 index 000000000000..a1e653b404ab --- /dev/null +++ b/bsp/allwinner/dm/pinctrl/pinctrl-sun252i-f101.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +/* F101 uses the D1-style PIO layout with 4-bit-stride drive fields. */ +static const struct sunxi_pinctrl_layout sun252i_f101_pio_layout = +{ + .bank_stride = 0x30, + .mux_reg = 0x00, + .data_reg = 0x10, + .drive_reg = 0x14, + .pull_reg = 0x24, + .mux_bits = 4, + .drive_bits = 4, + .pull_bits = 2, + .disabled_mux = 0xf, +}; + +/* Hardware bank address space remains 32 pins wide even when a bank is sparse. */ +static const rt_uint8_t sun252i_f101_bank_pins[] = +{ + 4, 4, 6, 23, 15, 7, +}; + +static const struct sunxi_pinctrl_function sun252i_f101_functions[] = +{ + { "uart0", 4, 0, 2, 6 }, + { "uart1", 1, 0, 4, 4 }, + { "tpadc", 0, 0, 4, 2 }, + { "irrx", 3, 7, 1, 3 }, + { "pwm0_0", 4, 2, 1, 10 }, + { "pwm0_1", 4, 3, 1, 10 }, + { "pwm0_2", 4, 4, 1, 10 }, + { "pwm0_3", 4, 5, 1, 10 }, + { "twi2", 1, 2, 2, 3 }, + { "spi0", 2, 0, 6, 3 }, + { "spi1", 3, 0, 7, 3 }, + { "sdc0", 5, 0, 6, 2 }, + { "sdc2", 2, 0, 6, 2 }, + { "lcd0", 3, 0, 22, 2 }, + { "lcd0", 4, 6, 2, 4 }, + { "lvds0", 3, 12, 10, 3 }, + { "dsi0", 3, 12, 10, 4 }, + { "i2s0", 4, 0, 5, 5 }, + { "owa", 4, 6, 1, 6 }, +}; + +const struct sunxi_pinctrl_data sun252i_f101_pinctrl_data = +{ + .layout = &sun252i_f101_pio_layout, + .bank_pins = sun252i_f101_bank_pins, + .functions = sun252i_f101_functions, + .functions_nr = RT_ARRAY_SIZE(sun252i_f101_functions), + .banks_nr = RT_ARRAY_SIZE(sun252i_f101_bank_pins), + .irq_banks = RT_ARRAY_SIZE(sun252i_f101_bank_pins), + .irq_mux = 0xe, +}; diff --git a/bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c b/bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c new file mode 100644 index 000000000000..1ab3813f0977 --- /dev/null +++ b/bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include + +#define DBG_TAG "pinctrl.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include + +#define SUNXI_PINS_PER_BANK 32 +#define SUNXI_MAX_EINT_BANKS 11 +#define SUNXI_GPIO_IN 0 +#define SUNXI_GPIO_OUT 1 +#define SUNXI_PULL_DISABLE 0 +#define SUNXI_PULL_UP 1 +#define SUNXI_PULL_DOWN 2 +#define SUNXI_EINT_CFG 0x200 +#define SUNXI_EINT_CTRL 0x210 +#define SUNXI_EINT_STATUS 0x214 +#define SUNXI_EINT_BANK_STRIDE 0x20 +#define SUNXI_EINT_PINS_PER_CFG 8 +#define SUNXI_EINT_CFG_BITS 4 + +#define SUNXI_EINT_RISING 0 +#define SUNXI_EINT_FALLING 1 +#define SUNXI_EINT_HIGH 2 +#define SUNXI_EINT_LOW 3 +#define SUNXI_EINT_BOTH 4 + +struct sunxi_pinctrl; + +struct sunxi_pinctrl_irq_bank +{ + struct sunxi_pinctrl *pinctrl; + rt_uint8_t bank; + int irq; +}; + +struct sunxi_pinctrl_irq_handler +{ + void (*hdr)(void *args); + void *args; +}; + +struct sunxi_pinctrl +{ + struct rt_device_pin parent; + const struct sunxi_pinctrl_data *data; + + void *base; + struct rt_spinlock lock; + struct sunxi_pinctrl_irq_bank irq_banks[SUNXI_MAX_EINT_BANKS]; + struct sunxi_pinctrl_irq_handler *irq_handlers; +}; + +#define to_sunxi_pinctrl(raw) rt_container_of(raw, struct sunxi_pinctrl, parent) + +/* rt_pin_ops.pin_get has no provider argument; Sunxi systems expose one PIO. */ +static struct sunxi_pinctrl *sunxi_pinctrl_primary; + +static rt_uint32_t sunxi_pinctrl_field_mask(rt_uint8_t width) +{ + return RT_BIT(width) - 1; +} + +static rt_bool_t sunxi_pinctrl_pin_valid(const struct sunxi_pinctrl *pinctrl, + rt_base_t pin) +{ + rt_uint32_t bank; + + if (pin < 0 || pin >= pinctrl->data->banks_nr * SUNXI_PINS_PER_BANK) + { + return RT_FALSE; + } + + bank = pin / SUNXI_PINS_PER_BANK; + + return pin % SUNXI_PINS_PER_BANK < pinctrl->data->bank_pins[bank]; +} + +static rt_err_t sunxi_pinctrl_pin_from_name(const struct sunxi_pinctrl *pinctrl, + const char *name, rt_uint32_t *pin) +{ + rt_uint32_t number = 0; + rt_uint32_t bank; + + if (!name || name[0] != 'P' || name[1] < 'A' || + name[1] >= 'A' + pinctrl->data->banks_nr || !name[2]) + { + return -RT_EINVAL; + } + + bank = name[1] - 'A'; + + for (name += 2; *name; ++name) + { + if (*name < '0' || *name > '9' || + number > (pinctrl->data->bank_pins[bank] - 1) / 10) + { + return -RT_EINVAL; + } + + number = number * 10 + *name - '0'; + + if (number >= pinctrl->data->bank_pins[bank]) + { + return -RT_EINVAL; + } + } + + *pin = bank * SUNXI_PINS_PER_BANK + number; + + return RT_EOK; +} + +static void sunxi_pinctrl_set_field(struct sunxi_pinctrl *pinctrl, + rt_uint32_t pin, rt_uint16_t reg_offset, rt_uint8_t field_bits, + rt_uint32_t value) +{ + rt_ubase_t level; + rt_uint32_t reg_value; + rt_uint32_t pins_per_reg = 32 / field_bits; + rt_uint32_t mask = sunxi_pinctrl_field_mask(field_bits); + rt_uint32_t pin_in_bank = pin % SUNXI_PINS_PER_BANK; + rt_uint32_t bank = pin / SUNXI_PINS_PER_BANK; + rt_uint32_t shift = (pin_in_bank % pins_per_reg) * field_bits; + const struct sunxi_pinctrl_layout *layout = pinctrl->data->layout; + void *reg = pinctrl->base + bank * layout->bank_stride + reg_offset + + (pin_in_bank / pins_per_reg) * sizeof(rt_uint32_t); + + level = rt_spin_lock_irqsave(&pinctrl->lock); + reg_value = HWREG32(reg); + reg_value &= ~(mask << shift); + reg_value |= (value & mask) << shift; + HWREG32(reg) = reg_value; + rt_spin_unlock_irqrestore(&pinctrl->lock, level); +} + +static void sunxi_pinctrl_set_mux(struct sunxi_pinctrl *pinctrl, + rt_uint32_t pin, rt_uint32_t function) +{ + const struct sunxi_pinctrl_layout *layout = pinctrl->data->layout; + + sunxi_pinctrl_set_field(pinctrl, pin, layout->mux_reg, + layout->mux_bits, function); +} + +static rt_err_t sunxi_pinctrl_function(const struct sunxi_pinctrl *pinctrl, + const char *name, rt_uint32_t pin, rt_uint32_t *mux) +{ + rt_size_t index; + rt_uint32_t bank = pin / SUNXI_PINS_PER_BANK; + rt_uint32_t number = pin % SUNXI_PINS_PER_BANK; + + if (!rt_strcmp(name, "gpio_in")) + { + *mux = SUNXI_GPIO_IN; + return RT_EOK; + } + else if (!rt_strcmp(name, "gpio_out")) + { + *mux = SUNXI_GPIO_OUT; + return RT_EOK; + } + else if (!rt_strcmp(name, "io_disabled")) + { + *mux = pinctrl->data->layout->disabled_mux; + return RT_EOK; + } + + for (index = 0; index < pinctrl->data->functions_nr; ++index) + { + const struct sunxi_pinctrl_function *function = + &pinctrl->data->functions[index]; + + if (!rt_strcmp(name, function->name) && bank == function->bank && + number >= function->first_pin && + number < function->first_pin + function->pin_count) + { + *mux = function->mux; + return RT_EOK; + } + } + + return -RT_EINVAL; +} + +static void sunxi_pinctrl_pin_mode(struct rt_device *device, rt_base_t pin, + rt_uint8_t mode) +{ + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(device); + const struct sunxi_pinctrl_layout *layout = pinctrl->data->layout; + + if (!sunxi_pinctrl_pin_valid(pinctrl, pin)) + { + return; + } + + switch (mode) + { + case PIN_MODE_OUTPUT: + case PIN_MODE_OUTPUT_OD: + sunxi_pinctrl_set_mux(pinctrl, pin, SUNXI_GPIO_OUT); + sunxi_pinctrl_set_field(pinctrl, pin, layout->pull_reg, + layout->pull_bits, SUNXI_PULL_DISABLE); + break; + + case PIN_MODE_INPUT_PULLUP: + sunxi_pinctrl_set_mux(pinctrl, pin, SUNXI_GPIO_IN); + sunxi_pinctrl_set_field(pinctrl, pin, layout->pull_reg, + layout->pull_bits, SUNXI_PULL_UP); + break; + + case PIN_MODE_INPUT_PULLDOWN: + sunxi_pinctrl_set_mux(pinctrl, pin, SUNXI_GPIO_IN); + sunxi_pinctrl_set_field(pinctrl, pin, layout->pull_reg, + layout->pull_bits, SUNXI_PULL_DOWN); + break; + + default: + sunxi_pinctrl_set_mux(pinctrl, pin, SUNXI_GPIO_IN); + sunxi_pinctrl_set_field(pinctrl, pin, layout->pull_reg, + layout->pull_bits, SUNXI_PULL_DISABLE); + break; + } +} + +static void sunxi_pinctrl_pin_write(struct rt_device *device, rt_base_t pin, + rt_uint8_t value) +{ + rt_ubase_t level; + rt_uint32_t reg_value; + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(device); + const struct sunxi_pinctrl_layout *layout = pinctrl->data->layout; + rt_uint32_t bank; + rt_uint32_t number; + void *reg; + + if (!sunxi_pinctrl_pin_valid(pinctrl, pin)) + { + return; + } + + bank = pin / SUNXI_PINS_PER_BANK; + number = pin % SUNXI_PINS_PER_BANK; + reg = pinctrl->base + bank * layout->bank_stride + layout->data_reg; + + level = rt_spin_lock_irqsave(&pinctrl->lock); + reg_value = HWREG32(reg); + reg_value = value ? reg_value | RT_BIT(number) : reg_value & ~RT_BIT(number); + HWREG32(reg) = reg_value; + rt_spin_unlock_irqrestore(&pinctrl->lock, level); +} + +static rt_ssize_t sunxi_pinctrl_pin_read(struct rt_device *device, rt_base_t pin) +{ + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(device); + const struct sunxi_pinctrl_layout *layout = pinctrl->data->layout; + rt_uint32_t bank; + rt_uint32_t number; + + if (!sunxi_pinctrl_pin_valid(pinctrl, pin)) + { + return -RT_EINVAL; + } + + bank = pin / SUNXI_PINS_PER_BANK; + number = pin % SUNXI_PINS_PER_BANK; + + return !!(HWREG32(pinctrl->base + bank * layout->bank_stride + + layout->data_reg) & RT_BIT(number)); +} + +static rt_base_t sunxi_pinctrl_pin_get(const char *name) +{ + rt_uint32_t pin; + + if (!sunxi_pinctrl_primary) + { + return -RT_EINVAL; + } + + return sunxi_pinctrl_pin_from_name(sunxi_pinctrl_primary, name, &pin) ? + -RT_EINVAL : pin; +} + +static rt_ssize_t sunxi_pinctrl_pin_parse(struct rt_device *device, + struct rt_ofw_cell_args *args, rt_uint32_t *flags) +{ + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(device); + + if (args->args_count != 3 || args->args[0] >= pinctrl->data->banks_nr || + args->args[1] >= pinctrl->data->bank_pins[args->args[0]]) + { + return -RT_EINVAL; + } + + *flags = args->args[2]; + + return args->args[0] * SUNXI_PINS_PER_BANK + args->args[1]; +} + +static rt_err_t sunxi_pinctrl_irq_mode_to_hw(rt_uint8_t mode, + rt_uint32_t *hw_mode) +{ + switch (mode) + { + case PIN_IRQ_MODE_RISING: + *hw_mode = SUNXI_EINT_RISING; + break; + case PIN_IRQ_MODE_FALLING: + *hw_mode = SUNXI_EINT_FALLING; + break; + case PIN_IRQ_MODE_RISING_FALLING: + *hw_mode = SUNXI_EINT_BOTH; + break; + case PIN_IRQ_MODE_HIGH_LEVEL: + *hw_mode = SUNXI_EINT_HIGH; + break; + case PIN_IRQ_MODE_LOW_LEVEL: + *hw_mode = SUNXI_EINT_LOW; + break; + default: + return -RT_EINVAL; + } + + return RT_EOK; +} + +static rt_err_t sunxi_pinctrl_pin_irq_mode(struct rt_device *device, + rt_base_t pin, rt_uint8_t mode) +{ + rt_ubase_t level; + rt_uint32_t control; + rt_uint32_t hw_mode; + rt_uint32_t bank; + rt_uint32_t number; + rt_uint32_t cfg_offset; + rt_uint32_t cfg_shift; + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(device); + + if (!pinctrl->data->irq_banks || + !sunxi_pinctrl_pin_valid(pinctrl, pin)) + { + return -RT_ENOSYS; + } + if ((bank = pin / SUNXI_PINS_PER_BANK) >= pinctrl->data->irq_banks || + sunxi_pinctrl_irq_mode_to_hw(mode, &hw_mode)) + { + return -RT_EINVAL; + } + + number = pin % SUNXI_PINS_PER_BANK; + cfg_offset = SUNXI_EINT_CFG + bank * SUNXI_EINT_BANK_STRIDE + + number / SUNXI_EINT_PINS_PER_CFG * sizeof(rt_uint32_t); + cfg_shift = number % SUNXI_EINT_PINS_PER_CFG * SUNXI_EINT_CFG_BITS; + + sunxi_pinctrl_set_mux(pinctrl, pin, pinctrl->data->irq_mux); + + level = rt_spin_lock_irqsave(&pinctrl->lock); + control = HWREG32(pinctrl->base + cfg_offset); + control &= ~(0xf << cfg_shift); + control |= hw_mode << cfg_shift; + HWREG32(pinctrl->base + cfg_offset) = control; + HWREG32(pinctrl->base + SUNXI_EINT_STATUS + + bank * SUNXI_EINT_BANK_STRIDE) = RT_BIT(number); + rt_spin_unlock_irqrestore(&pinctrl->lock, level); + + return RT_EOK; +} + +static rt_err_t sunxi_pinctrl_pin_irq_enable(struct rt_device *device, + rt_base_t pin, rt_uint8_t enabled) +{ + rt_ubase_t level; + rt_uint32_t bank; + rt_uint32_t number; + rt_uint32_t control; + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(device); + + if (!pinctrl->data->irq_banks || + !sunxi_pinctrl_pin_valid(pinctrl, pin)) + { + return -RT_ENOSYS; + } + + bank = pin / SUNXI_PINS_PER_BANK; + number = pin % SUNXI_PINS_PER_BANK; + if (bank >= pinctrl->data->irq_banks) + { + return -RT_EINVAL; + } + + level = rt_spin_lock_irqsave(&pinctrl->lock); + control = HWREG32(pinctrl->base + SUNXI_EINT_CTRL + + bank * SUNXI_EINT_BANK_STRIDE); + if (enabled) + { + HWREG32(pinctrl->base + SUNXI_EINT_STATUS + + bank * SUNXI_EINT_BANK_STRIDE) = RT_BIT(number); + control |= RT_BIT(number); + } + else + { + control &= ~RT_BIT(number); + } + HWREG32(pinctrl->base + SUNXI_EINT_CTRL + + bank * SUNXI_EINT_BANK_STRIDE) = control; + rt_spin_unlock_irqrestore(&pinctrl->lock, level); + + return RT_EOK; +} + +static rt_err_t sunxi_pinctrl_pin_attach_irq(struct rt_device *device, + rt_base_t pin, rt_uint8_t mode, void (*hdr)(void *args), void *args) +{ + rt_ubase_t level; + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(device); + struct sunxi_pinctrl_irq_handler *handler; + rt_err_t err; + + if (!hdr || !pinctrl->irq_handlers || + !sunxi_pinctrl_pin_valid(pinctrl, pin)) + { + return -RT_EINVAL; + } + if ((err = sunxi_pinctrl_pin_irq_mode(device, pin, mode))) + { + return err; + } + + level = rt_spin_lock_irqsave(&pinctrl->lock); + handler = &pinctrl->irq_handlers[pin]; + if (handler->hdr) + { + err = -RT_EBUSY; + } + else + { + handler->hdr = hdr; + handler->args = args; + err = RT_EOK; + } + rt_spin_unlock_irqrestore(&pinctrl->lock, level); + + return err; +} + +static rt_err_t sunxi_pinctrl_pin_detach_irq(struct rt_device *device, + rt_base_t pin) +{ + rt_ubase_t level; + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(device); + + if (!pinctrl->irq_handlers || !sunxi_pinctrl_pin_valid(pinctrl, pin)) + { + return -RT_EINVAL; + } + + sunxi_pinctrl_pin_irq_enable(device, pin, PIN_IRQ_DISABLE); + level = rt_spin_lock_irqsave(&pinctrl->lock); + rt_memset(&pinctrl->irq_handlers[pin], 0, + sizeof(pinctrl->irq_handlers[pin])); + rt_spin_unlock_irqrestore(&pinctrl->lock, level); + + return RT_EOK; +} + +static void sunxi_pinctrl_irq_handler(int vector, void *param) +{ + rt_uint32_t enabled; + rt_uint32_t pending; + rt_uint32_t raw_pending; + struct sunxi_pinctrl_irq_bank *irq_bank = param; + struct sunxi_pinctrl *pinctrl = irq_bank->pinctrl; + + RT_UNUSED(vector); + + raw_pending = HWREG32(pinctrl->base + SUNXI_EINT_STATUS + + irq_bank->bank * SUNXI_EINT_BANK_STRIDE); + HWREG32(pinctrl->base + SUNXI_EINT_STATUS + + irq_bank->bank * SUNXI_EINT_BANK_STRIDE) = raw_pending; + enabled = HWREG32(pinctrl->base + SUNXI_EINT_CTRL + + irq_bank->bank * SUNXI_EINT_BANK_STRIDE); + pending = raw_pending & enabled; + + while (pending) + { + rt_uint32_t number = __builtin_ctz(pending); + rt_ubase_t level; + void (*hdr)(void *args); + void *args; + struct sunxi_pinctrl_irq_handler *handler = + &pinctrl->irq_handlers[irq_bank->bank * SUNXI_PINS_PER_BANK + number]; + + pending &= ~RT_BIT(number); + level = rt_spin_lock_irqsave(&pinctrl->lock); + hdr = handler->hdr; + args = handler->args; + rt_spin_unlock_irqrestore(&pinctrl->lock, level); + + if (hdr) + { + hdr(args); + } + } +} + +static void sunxi_pinctrl_irq_deinit(struct sunxi_pinctrl *pinctrl) +{ + rt_size_t index; + + for (index = 0; index < pinctrl->data->irq_banks; ++index) + { + struct sunxi_pinctrl_irq_bank *irq_bank = &pinctrl->irq_banks[index]; + + if (irq_bank->irq >= 0) + { + rt_hw_interrupt_mask(irq_bank->irq); + rt_hw_interrupt_uninstall(irq_bank->irq, + sunxi_pinctrl_irq_handler, irq_bank); + } + } + rt_free(pinctrl->irq_handlers); + pinctrl->irq_handlers = RT_NULL; +} + +static rt_err_t sunxi_pinctrl_irq_init(struct sunxi_pinctrl *pinctrl, + struct rt_device *dev) +{ + rt_size_t index; + rt_size_t pins_nr = pinctrl->data->banks_nr * SUNXI_PINS_PER_BANK; + + if (!pinctrl->data->irq_banks) + { + return RT_EOK; + } + if (rt_dm_dev_get_irq_count(dev) < pinctrl->data->irq_banks) + { + return -RT_EINVAL; + } + + pinctrl->irq_handlers = rt_calloc(pins_nr, + sizeof(*pinctrl->irq_handlers)); + if (!pinctrl->irq_handlers) + { + return -RT_ENOMEM; + } + + for (index = 0; index < pinctrl->data->irq_banks; ++index) + { + struct sunxi_pinctrl_irq_bank *irq_bank = &pinctrl->irq_banks[index]; + + irq_bank->pinctrl = pinctrl; + irq_bank->bank = index; + irq_bank->irq = rt_dm_dev_get_irq(dev, index); + if (irq_bank->irq < 0) + { + sunxi_pinctrl_irq_deinit(pinctrl); + return irq_bank->irq; + } + + rt_hw_interrupt_install(irq_bank->irq, sunxi_pinctrl_irq_handler, + irq_bank, "sunxi-pio"); + rt_hw_interrupt_umask(irq_bank->irq); + } + + return RT_EOK; +} + +static rt_err_t sunxi_pinctrl_confs_apply(struct rt_device *device, + void *fw_conf_np) +{ + rt_err_t err; + rt_uint32_t drive_strength; + rt_uint32_t function; + rt_uint32_t pin; + const char *pin_name; + const char *function_name; + struct rt_ofw_prop *prop; + struct rt_ofw_node *conf_np = fw_conf_np; + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(device); + const struct sunxi_pinctrl_layout *layout = pinctrl->data->layout; + + if ((err = rt_ofw_prop_read_string(conf_np, "function", &function_name))) + { + LOG_E("%s: missing function", rt_ofw_node_full_name(conf_np)); + return err; + } + + if (!rt_ofw_prop_read_u32(conf_np, "drive-strength", &drive_strength)) + { + if (drive_strength < 10 || drive_strength > 40 || drive_strength % 10) + { + return -RT_EINVAL; + } + + drive_strength = drive_strength / 10 - 1; + } + else + { + drive_strength = RT_UINT32_MAX; + } + + prop = rt_ofw_get_prop(conf_np, "pins", RT_NULL); + if (!prop) + { + LOG_E("%s: missing pins", rt_ofw_node_full_name(conf_np)); + return -RT_EINVAL; + } + + for (pin_name = rt_ofw_prop_next_string(prop, RT_NULL); pin_name; + pin_name = rt_ofw_prop_next_string(prop, pin_name)) + { + if ((err = sunxi_pinctrl_pin_from_name(pinctrl, pin_name, &pin))) + { + LOG_E("%s: invalid pin %s", rt_ofw_node_full_name(conf_np), pin_name); + return err; + } + + if ((err = sunxi_pinctrl_function(pinctrl, function_name, pin, &function))) + { + LOG_E("%s: function %s is unavailable on %s", + rt_ofw_node_full_name(conf_np), function_name, pin_name); + return err; + } + + sunxi_pinctrl_set_mux(pinctrl, pin, function); + + if (drive_strength != RT_UINT32_MAX) + { + sunxi_pinctrl_set_field(pinctrl, pin, layout->drive_reg, + layout->drive_bits, drive_strength); + } + + if (rt_ofw_prop_read_bool(conf_np, "bias-pull-up")) + { + sunxi_pinctrl_set_field(pinctrl, pin, layout->pull_reg, + layout->pull_bits, SUNXI_PULL_UP); + } + else if (rt_ofw_prop_read_bool(conf_np, "bias-pull-down")) + { + sunxi_pinctrl_set_field(pinctrl, pin, layout->pull_reg, + layout->pull_bits, SUNXI_PULL_DOWN); + } + else if (rt_ofw_prop_read_bool(conf_np, "bias-disable")) + { + sunxi_pinctrl_set_field(pinctrl, pin, layout->pull_reg, + layout->pull_bits, SUNXI_PULL_DISABLE); + } + } + + return RT_EOK; +} + +static const struct rt_pin_ops sunxi_pinctrl_ops = +{ + .pin_mode = sunxi_pinctrl_pin_mode, + .pin_write = sunxi_pinctrl_pin_write, + .pin_read = sunxi_pinctrl_pin_read, + .pin_attach_irq = sunxi_pinctrl_pin_attach_irq, + .pin_detach_irq = sunxi_pinctrl_pin_detach_irq, + .pin_irq_enable = sunxi_pinctrl_pin_irq_enable, + .pin_get = sunxi_pinctrl_pin_get, + .pin_irq_mode = sunxi_pinctrl_pin_irq_mode, + .pin_parse = sunxi_pinctrl_pin_parse, + .pin_ctrl_confs_apply = sunxi_pinctrl_confs_apply, +}; + +static rt_err_t sunxi_pinctrl_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_size_t index; + struct rt_device *dev = &pdev->parent; + const struct sunxi_pinctrl_data *data = pdev->id ? pdev->id->data : RT_NULL; + struct sunxi_pinctrl *pinctrl; + + if (!data || !data->layout || !data->bank_pins || !data->banks_nr || + !data->layout->bank_stride || !data->layout->mux_bits || + !data->layout->drive_bits || !data->layout->pull_bits || + data->layout->mux_bits > 16 || data->layout->drive_bits > 16 || + data->layout->pull_bits > 16 || + data->irq_banks > data->banks_nr || + (data->irq_banks && data->irq_mux > + sunxi_pinctrl_field_mask(data->layout->mux_bits)) || + data->layout->disabled_mux > sunxi_pinctrl_field_mask(data->layout->mux_bits) || + (data->functions_nr && !data->functions)) + { + return -RT_EINVAL; + } + + for (index = 0; index < data->banks_nr; ++index) + { + if (!data->bank_pins[index] || + data->bank_pins[index] > SUNXI_PINS_PER_BANK) + { + return -RT_EINVAL; + } + } + + for (index = 0; index < data->functions_nr; ++index) + { + const struct sunxi_pinctrl_function *function = &data->functions[index]; + + if (!function->name || !function->pin_count || + function->bank >= data->banks_nr || + function->first_pin + function->pin_count > + data->bank_pins[function->bank] || + function->mux > sunxi_pinctrl_field_mask(data->layout->mux_bits)) + { + return -RT_EINVAL; + } + } + + pinctrl = rt_calloc(1, sizeof(*pinctrl)); + if (!pinctrl) + { + return -RT_ENOMEM; + } + + pinctrl->base = rt_dm_dev_iomap(dev, 0); + if (!pinctrl->base) + { + err = -RT_EIO; + goto _fail; + } + + pinctrl->data = data; + pinctrl->parent.ops = &sunxi_pinctrl_ops; + pinctrl->parent.parent.ofw_node = dev->ofw_node; + rt_spin_lock_init(&pinctrl->lock); + + for (index = 0; index < RT_ARRAY_SIZE(pinctrl->irq_banks); ++index) + { + pinctrl->irq_banks[index].irq = -1; + } + if ((err = sunxi_pinctrl_irq_init(pinctrl, dev))) + { + goto _fail; + } + + if ((err = pin_api_init(&pinctrl->parent, + data->banks_nr * SUNXI_PINS_PER_BANK))) + { + goto _fail; + } + + sunxi_pinctrl_primary = pinctrl; + rt_ofw_data(dev->ofw_node) = &pinctrl->parent; + + return RT_EOK; + +_fail: + sunxi_pinctrl_irq_deinit(pinctrl); + if (pinctrl->base) + { + rt_iounmap(pinctrl->base); + } + rt_free(pinctrl); + + return err; +} + +static const struct rt_ofw_node_id sunxi_pinctrl_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-pinctrl", .data = &sun252i_f101_pinctrl_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_pinctrl_driver = +{ + .name = "sunxi-pinctrl", + .ids = sunxi_pinctrl_ofw_ids, + .probe = sunxi_pinctrl_probe, +}; + +static int sunxi_pinctrl_register(void) +{ + rt_platform_driver_register(&sunxi_pinctrl_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(sunxi_pinctrl_register); diff --git a/bsp/allwinner/dm/pwm/Kconfig b/bsp/allwinner/dm/pwm/Kconfig new file mode 100644 index 000000000000..c5148d2a9905 --- /dev/null +++ b/bsp/allwinner/dm/pwm/Kconfig @@ -0,0 +1,7 @@ +config RT_PWM_SUNXI + bool "Allwinner Sunxi PWM controller" + depends on RT_USING_DM + depends on RT_USING_CLK + depends on RT_USING_PWM + depends on RT_USING_RESET + default n diff --git a/bsp/allwinner/dm/pwm/SConscript b/bsp/allwinner/dm/pwm/SConscript new file mode 100644 index 000000000000..399fb871fcdf --- /dev/null +++ b/bsp/allwinner/dm/pwm/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_PWM_SUNXI']): + src += ['pwm-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/pwm/pwm-sunxi.c b/bsp/allwinner/dm/pwm/pwm-sunxi.c new file mode 100644 index 000000000000..54cbef80e0e8 --- /dev/null +++ b/bsp/allwinner/dm/pwm/pwm-sunxi.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define PWM_PCCR01 0x020 +#define PWM_PCGR 0x040 +#define PWM_PER 0x080 +#define PWM_CH0_CTRL 0x100 +#define PWM_CH0_PERIOD 0x104 +#define PWM_CH_STEP 0x020 + +#define PWM_CLK_SRC_SHIFT 7 +#define PWM_CLK_SRC_MASK RT_GENMASK(8, 7) +#define PWM_DIV_M_MASK RT_GENMASK(3, 0) +#define PWM_PRESCALE_MASK RT_GENMASK(7, 0) +#define PWM_ACT_STA RT_BIT(8) +#define PWM_ACT_CYCLES_MASK RT_GENMASK(15, 0) +#define PWM_PERIOD_CYCLES_MASK RT_GENMASK(31, 16) +#define PWM_PERIOD_CYCLES_SHIFT 16 +#define PWM_CGR_BYPASS_SHIFT 16 + +#define SUNXI_PWM_CHANNELS 4 +#define SUNXI_PWM_CLK_24M 24000000ULL +#define SUNXI_PWM_CLK_100M 100000000ULL +#define SUNXI_PWM_NSEC_PER_SEC 1000000000ULL +#define SUNXI_PWM_COUNTER_MAX 65536U + +static const rt_uint16_t sunxi_pwm_dividers[] = +{ + 1, 2, 4, 8, 16, 32, 64, 128, 256, +}; + +struct sunxi_pwm +{ + struct rt_device_pwm parent; + void *base; + struct rt_clk *clk; + struct rt_reset_control *reset; + struct rt_spinlock lock; +}; + +#define to_sunxi_pwm(raw) rt_container_of(raw, struct sunxi_pwm, parent) + +rt_inline rt_uint32_t sunxi_pwm_pccr(int channel) +{ + return PWM_PCCR01 + ((channel >> 1) * sizeof(rt_uint32_t)); +} + +rt_inline rt_uint32_t sunxi_pwm_ctrl(int channel) +{ + return PWM_CH0_CTRL + channel * PWM_CH_STEP; +} + +rt_inline rt_uint32_t sunxi_pwm_period(int channel) +{ + return PWM_CH0_PERIOD + channel * PWM_CH_STEP; +} + +rt_inline rt_uint32_t sunxi_pwm_read(struct sunxi_pwm *pwm, rt_uint32_t offset) +{ + return HWREG32(pwm->base + offset); +} + +rt_inline void sunxi_pwm_write(struct sunxi_pwm *pwm, rt_uint32_t offset, + rt_uint32_t value) +{ + HWREG32(pwm->base + offset) = value; +} + +static rt_err_t sunxi_pwm_enable(struct sunxi_pwm *pwm, int channel, + rt_bool_t enabled) +{ + rt_uint32_t value; + + if (channel < 0 || channel >= SUNXI_PWM_CHANNELS) + { + return -RT_EINVAL; + } + + value = sunxi_pwm_read(pwm, PWM_PER); + if (enabled) + { + value |= RT_BIT(channel); + } + else + { + value &= ~RT_BIT(channel); + } + sunxi_pwm_write(pwm, PWM_PER, value); + + value = sunxi_pwm_read(pwm, PWM_PCGR); + if (enabled) + { + value |= RT_BIT(channel); + } + else + { + value &= ~RT_BIT(channel); + } + sunxi_pwm_write(pwm, PWM_PCGR, value); + + return RT_EOK; +} + +static rt_err_t sunxi_pwm_config(struct sunxi_pwm *pwm, + struct rt_pwm_configuration *config) +{ + rt_uint64_t source_rate; + rt_uint64_t cycles; + rt_uint32_t pccr; + rt_uint32_t ctrl; + rt_uint32_t period; + rt_uint32_t active; + rt_uint32_t divider_index; + rt_uint32_t prescale; + int channel = config->channel; + + if (channel < 0 || channel >= SUNXI_PWM_CHANNELS || !config->period || + config->pulse > config->period) + { + return -RT_EINVAL; + } + + source_rate = config->period <= 334 ? SUNXI_PWM_CLK_100M : SUNXI_PWM_CLK_24M; + cycles = source_rate * config->period / SUNXI_PWM_NSEC_PER_SEC; + if (!cycles) + { + cycles = 1; + } + + for (divider_index = 0; divider_index < RT_ARRAY_SIZE(sunxi_pwm_dividers); + ++divider_index) + { + for (prescale = 0; prescale <= RT_UINT8_MAX; ++prescale) + { + period = cycles / sunxi_pwm_dividers[divider_index] / (prescale + 1); + if (period && period <= SUNXI_PWM_COUNTER_MAX) + { + goto _configured; + } + } + } + + return -RT_EINVAL; + +_configured: + active = ((rt_uint64_t)period * config->pulse + config->period / 2) / + config->period; + active = rt_min(active, period); + + pccr = sunxi_pwm_read(pwm, sunxi_pwm_pccr(channel)); + pccr &= ~(PWM_CLK_SRC_MASK | PWM_DIV_M_MASK); + pccr |= divider_index; + if (source_rate == SUNXI_PWM_CLK_100M) + { + pccr |= RT_BIT(PWM_CLK_SRC_SHIFT); + } + sunxi_pwm_write(pwm, sunxi_pwm_pccr(channel), pccr); + + ctrl = sunxi_pwm_read(pwm, sunxi_pwm_ctrl(channel)); + ctrl &= ~(PWM_PRESCALE_MASK | PWM_ACT_STA); + ctrl |= prescale; + if (config->complementary) + { + ctrl |= PWM_ACT_STA; + } + sunxi_pwm_write(pwm, sunxi_pwm_ctrl(channel), ctrl); + + sunxi_pwm_write(pwm, sunxi_pwm_period(channel), + (active & PWM_ACT_CYCLES_MASK) | + ((period - 1) << PWM_PERIOD_CYCLES_SHIFT)); + + return RT_EOK; +} + +static rt_err_t sunxi_pwm_get(struct sunxi_pwm *pwm, + struct rt_pwm_configuration *config) +{ + rt_uint64_t source_rate; + rt_uint64_t scale; + rt_uint32_t pccr; + rt_uint32_t ctrl; + rt_uint32_t value; + rt_uint32_t cycles; + rt_uint32_t divider_index; + int channel = config->channel; + + if (channel < 0 || channel >= SUNXI_PWM_CHANNELS) + { + return -RT_EINVAL; + } + + pccr = sunxi_pwm_read(pwm, sunxi_pwm_pccr(channel)); + ctrl = sunxi_pwm_read(pwm, sunxi_pwm_ctrl(channel)); + value = sunxi_pwm_read(pwm, sunxi_pwm_period(channel)); + cycles = ((value & PWM_PERIOD_CYCLES_MASK) >> PWM_PERIOD_CYCLES_SHIFT) + 1; + + divider_index = pccr & PWM_DIV_M_MASK; + if (divider_index >= RT_ARRAY_SIZE(sunxi_pwm_dividers)) + { + return -RT_EIO; + } + + source_rate = (pccr & RT_BIT(PWM_CLK_SRC_SHIFT)) ? + SUNXI_PWM_CLK_100M : SUNXI_PWM_CLK_24M; + scale = sunxi_pwm_dividers[divider_index] * + ((ctrl & PWM_PRESCALE_MASK) + 1); + config->period = (rt_uint64_t)cycles * scale * SUNXI_PWM_NSEC_PER_SEC / + source_rate; + config->pulse = (rt_uint64_t)(value & PWM_ACT_CYCLES_MASK) * scale * + SUNXI_PWM_NSEC_PER_SEC / source_rate; + config->complementary = !!(ctrl & PWM_ACT_STA); + + return RT_EOK; +} + +static rt_err_t sunxi_pwm_control(struct rt_device_pwm *device, int cmd, + void *args) +{ + rt_err_t err; + rt_base_t level; + struct rt_pwm_configuration *config = args; + struct sunxi_pwm *pwm = to_sunxi_pwm(device); + + if (!config) + { + return -RT_EINVAL; + } + + level = rt_spin_lock_irqsave(&pwm->lock); + + switch (cmd) + { + case PWM_CMD_ENABLE: + err = sunxi_pwm_enable(pwm, config->channel, RT_TRUE); + break; + case PWM_CMD_DISABLE: + err = sunxi_pwm_enable(pwm, config->channel, RT_FALSE); + break; + case PWM_CMD_SET: + case PWM_CMD_SET_PERIOD: + case PWM_CMD_SET_PULSE: + err = sunxi_pwm_config(pwm, config); + break; + case PWM_CMD_GET: + err = sunxi_pwm_get(pwm, config); + break; + default: + err = -RT_EINVAL; + break; + } + + rt_spin_unlock_irqrestore(&pwm->lock, level); + return err; +} + +static const struct rt_pwm_ops sunxi_pwm_ops = +{ + .control = sunxi_pwm_control, +}; + +static void sunxi_pwm_release(struct sunxi_pwm *pwm) +{ + if (!rt_is_err_or_null(pwm->clk)) + { + rt_clk_disable_unprepare(pwm->clk); + rt_clk_put(pwm->clk); + } + if (!rt_is_err_or_null(pwm->reset)) + { + rt_reset_control_assert(pwm->reset); + rt_reset_control_put(pwm->reset); + } + if (pwm->base) + { + rt_iounmap(pwm->base); + } + rt_free(pwm); +} + +static rt_err_t sunxi_pwm_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *name; + struct rt_device *dev = &pdev->parent; + struct sunxi_pwm *pwm = rt_calloc(1, sizeof(*pwm)); + + if (!pwm) + { + return -RT_ENOMEM; + } + + pwm->base = rt_dm_dev_iomap(dev, 0); + if (!pwm->base) + { + err = -RT_EIO; + goto _fail; + } + + pwm->clk = rt_clk_get_by_index(dev, 0); + if (rt_is_err(pwm->clk)) + { + err = rt_ptr_err(pwm->clk); + goto _fail; + } + + pwm->reset = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(pwm->reset)) + { + err = rt_ptr_err(pwm->reset); + goto _fail; + } + + if ((err = rt_reset_control_deassert(pwm->reset)) || + (err = rt_clk_prepare_enable(pwm->clk))) + { + goto _fail; + } + + rt_spin_lock_init(&pwm->lock); + pwm->parent.parent.ofw_node = dev->ofw_node; + rt_dm_dev_set_name_auto(&pwm->parent.parent, "pwm"); + name = rt_dm_dev_get_name(&pwm->parent.parent); + + if ((err = rt_device_pwm_register(&pwm->parent, name, &sunxi_pwm_ops, pwm))) + { + goto _fail; + } + + dev->user_data = pwm; + rt_dm_dev_bind_fwdata(dev, RT_NULL, pwm); + + return RT_EOK; + +_fail: + sunxi_pwm_release(pwm); + return err; +} + +static rt_err_t sunxi_pwm_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct sunxi_pwm *pwm = dev->user_data; + int channel; + + for (channel = 0; channel < SUNXI_PWM_CHANNELS; ++channel) + { + sunxi_pwm_enable(pwm, channel, RT_FALSE); + } + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + rt_device_unregister(&pwm->parent.parent); + sunxi_pwm_release(pwm); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_pwm_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-pwm" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_pwm_driver = +{ + .name = "sunxi-pwm", + .ids = sunxi_pwm_ofw_ids, + .probe = sunxi_pwm_probe, + .remove = sunxi_pwm_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_pwm_driver); diff --git a/bsp/allwinner/dm/reset/Kconfig b/bsp/allwinner/dm/reset/Kconfig new file mode 100644 index 000000000000..acf3bce8c7cf --- /dev/null +++ b/bsp/allwinner/dm/reset/Kconfig @@ -0,0 +1,6 @@ +config RT_RESET_SUN252I_F101 + bool "Allwinner Sun252i F101 reset controller" + depends on RT_USING_RESET + depends on RT_USING_OFW + depends on RT_CLK_SUN252I_F101 + default n diff --git a/bsp/allwinner/dm/reset/SConscript b/bsp/allwinner/dm/reset/SConscript new file mode 100644 index 000000000000..40fb91e9efec --- /dev/null +++ b/bsp/allwinner/dm/reset/SConscript @@ -0,0 +1,11 @@ +from building import * + +cwd = GetCurrentDir() +src = [] + +if GetDepend(['RT_RESET_SUN252I_F101']): + src += ['reset-sunxi.c', 'reset-sun252i-f101.c'] + +group = DefineGroup('DeviceDrivers', src, depend=[''], CPPPATH=[cwd + '/../include']) + +Return('group') diff --git a/bsp/allwinner/dm/reset/reset-sun252i-f101.c b/bsp/allwinner/dm/reset/reset-sun252i-f101.c new file mode 100644 index 000000000000..e3fb14e4d975 --- /dev/null +++ b/bsp/allwinner/dm/reset/reset-sun252i-f101.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include + +/* A set reset bit releases the corresponding peripheral from reset. */ +static const struct sunxi_reset_line sun252i_f101_reset_lines[] = +{ + [SUN252I_F101_RST_BUS_PLL_SSC] = { 0x0200, RT_BIT(30) }, + [SUN252I_F101_RST_MBUS] = { 0x0540, RT_BIT(30) }, + [SUN252I_F101_RST_BUS_DE] = { 0x060c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_DI] = { 0x062c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_G2D] = { 0x063c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_CE] = { 0x068c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_VE] = { 0x069c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_DMA] = { 0x070c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_HSTIMER] = { 0x073c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_DBGSYS] = { 0x078c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_PWM_BL] = { 0x0794, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_PWM] = { 0x07ac, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_DRAM_MOD] = { 0x0800, RT_BIT(30) }, + [SUN252I_F101_RST_BUS_LPSRAM] = { 0x0808, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_DRAM] = { 0x080c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_SMHC2] = { 0x084c, RT_BIT(18) }, + [SUN252I_F101_RST_BUS_SMHC0] = { 0x084c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_UART5] = { 0x090c, RT_BIT(21) }, + [SUN252I_F101_RST_BUS_UART4] = { 0x090c, RT_BIT(20) }, + [SUN252I_F101_RST_BUS_UART3] = { 0x090c, RT_BIT(19) }, + [SUN252I_F101_RST_BUS_UART2] = { 0x090c, RT_BIT(18) }, + [SUN252I_F101_RST_BUS_UART1] = { 0x090c, RT_BIT(17) }, + [SUN252I_F101_RST_BUS_UART0] = { 0x090c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_TWI2] = { 0x091c, RT_BIT(18) }, + [SUN252I_F101_RST_BUS_TWI1] = { 0x091c, RT_BIT(17) }, + [SUN252I_F101_RST_BUS_TWI0] = { 0x091c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_SPI1] = { 0x096c, RT_BIT(17) }, + [SUN252I_F101_RST_BUS_SPI0] = { 0x096c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_SPIF] = { 0x0984, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_IRRX] = { 0x09cc, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_CAN0] = { 0x092c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_CAN1] = { 0x092c, RT_BIT(17) }, + [SUN252I_F101_RST_BUS_GPADC] = { 0x09ec, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_THS] = { 0x09fc, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_I2S0] = { 0x0a20, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_OWA] = { 0x0a2c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_AUDIO] = { 0x0a5c, RT_BIT(16) }, + [SUN252I_F101_RST_USB_PHY0] = { 0x0a70, RT_BIT(30) }, + [SUN252I_F101_RST_USB_OTG0] = { 0x0a8c, RT_BIT(24) }, + [SUN252I_F101_RST_USB_EHCI0] = { 0x0a8c, RT_BIT(20) }, + [SUN252I_F101_RST_USB_OHCI0] = { 0x0a8c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_DPSS] = { 0x0abc, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_DSI] = { 0x0b4c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_TCONLCD] = { 0x0b7c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_LVDS0] = { 0x0bac, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_CSI] = { 0x0c1c, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_TVIN] = { 0x0c24, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_TVIN_TOP] = { 0x0c28, RT_BIT(16) }, + [SUN252I_F101_RST_BUS_TPADC] = { 0x0c5c, RT_BIT(16) }, +}; + +const struct sunxi_reset_data sun252i_f101_reset_data = +{ + .lines = sun252i_f101_reset_lines, + .lines_nr = RT_ARRAY_SIZE(sun252i_f101_reset_lines), + .assert_delay_us = 2, +}; diff --git a/bsp/allwinner/dm/reset/reset-sunxi.c b/bsp/allwinner/dm/reset/reset-sunxi.c new file mode 100644 index 000000000000..84e13d639b2c --- /dev/null +++ b/bsp/allwinner/dm/reset/reset-sunxi.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define DBG_TAG "reset.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include + +struct sunxi_reset +{ + struct rt_reset_controller parent; + const struct sunxi_reset_data *data; + + void *base; +}; + +static rt_err_t sunxi_reset_update(struct rt_reset_control *rstc, + rt_bool_t asserted) +{ + rt_ubase_t level; + rt_uint32_t value; + struct sunxi_reset *reset = rstc->rstcer->priv; + const struct sunxi_reset_line *line; + + if (rstc->id < 0 || rstc->id >= reset->data->lines_nr) + { + return -RT_EINVAL; + } + + line = &reset->data->lines[rstc->id]; + if (!line->mask) + { + return -RT_EINVAL; + } + + level = rt_spin_lock_irqsave(&reset->parent.spinlock); + value = HWREG32(reset->base + line->reg); + + if (asserted) + { + value &= ~line->mask; + } + else + { + value |= line->mask; + } + + HWREG32(reset->base + line->reg) = value; + rt_spin_unlock_irqrestore(&reset->parent.spinlock, level); + + return RT_EOK; +} + +static rt_err_t sunxi_reset_assert(struct rt_reset_control *rstc) +{ + return sunxi_reset_update(rstc, RT_TRUE); +} + +static rt_err_t sunxi_reset_deassert(struct rt_reset_control *rstc) +{ + return sunxi_reset_update(rstc, RT_FALSE); +} + +static rt_err_t sunxi_reset_reset(struct rt_reset_control *rstc) +{ + rt_err_t err; + struct sunxi_reset *reset = rstc->rstcer->priv; + + if ((err = sunxi_reset_assert(rstc))) + { + return err; + } + + if (reset->data->assert_delay_us) + { + rt_hw_us_delay(reset->data->assert_delay_us); + } + + return sunxi_reset_deassert(rstc); +} + +static int sunxi_reset_status(struct rt_reset_control *rstc) +{ + struct sunxi_reset *reset = rstc->rstcer->priv; + const struct sunxi_reset_line *line; + + if (rstc->id < 0 || rstc->id >= reset->data->lines_nr) + { + return -RT_EINVAL; + } + + line = &reset->data->lines[rstc->id]; + if (!line->mask) + { + return -RT_EINVAL; + } + + return !(HWREG32(reset->base + line->reg) & line->mask); +} + +static const struct rt_reset_control_ops sunxi_reset_ops = +{ + .reset = sunxi_reset_reset, + .assert = sunxi_reset_assert, + .deassert = sunxi_reset_deassert, + .status = sunxi_reset_status, +}; + +static rt_err_t sunxi_reset_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct rt_ofw_node *ccu_np; + const struct sunxi_reset_data *data = pdev->id ? pdev->id->data : RT_NULL; + struct sunxi_reset *reset; + + if (!data || !data->lines || !data->lines_nr) + { + return -RT_EINVAL; + } + + reset = rt_calloc(1, sizeof(*reset)); + if (!reset) + { + return -RT_ENOMEM; + } + + ccu_np = rt_ofw_parse_phandle(dev->ofw_node, "allwinner,ccu", 0); + if (!ccu_np) + { + err = -RT_EINVAL; + goto _fail; + } + + reset->base = rt_ofw_iomap(ccu_np, 0); + rt_ofw_node_put(ccu_np); + if (!reset->base) + { + err = -RT_EIO; + goto _fail; + } + + reset->data = data; + reset->parent.ofw_node = dev->ofw_node; + reset->parent.priv = reset; + reset->parent.ops = &sunxi_reset_ops; + + if ((err = rt_reset_controller_register(&reset->parent))) + { + goto _fail; + } + + return RT_EOK; + +_fail: + if (reset->base) + { + rt_iounmap(reset->base); + } + rt_free(reset); + + return err; +} + +static const struct rt_ofw_node_id sunxi_reset_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-ccu-reset", .data = &sun252i_f101_reset_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_reset_driver = +{ + .name = "sunxi-reset", + .ids = sunxi_reset_ofw_ids, + .probe = sunxi_reset_probe, +}; + +static int sunxi_reset_register(void) +{ + rt_platform_driver_register(&sunxi_reset_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(sunxi_reset_register); diff --git a/bsp/allwinner/dm/rtc/Kconfig b/bsp/allwinner/dm/rtc/Kconfig new file mode 100644 index 000000000000..5ede93f1a706 --- /dev/null +++ b/bsp/allwinner/dm/rtc/Kconfig @@ -0,0 +1,8 @@ +config RT_RTC_SUNXI + bool "Allwinner Sunxi RTC" + depends on RT_USING_DM + depends on RT_USING_RTC + depends on RT_USING_ALARM + default n + help + Enable the RTC and alarm controller found on supported Allwinner SoCs. diff --git a/bsp/allwinner/dm/rtc/SConscript b/bsp/allwinner/dm/rtc/SConscript new file mode 100644 index 000000000000..1863f77591b5 --- /dev/null +++ b/bsp/allwinner/dm/rtc/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_RTC_SUNXI']): + src += ['rtc-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/rtc/rtc-sunxi.c b/bsp/allwinner/dm/rtc/rtc-sunxi.c new file mode 100644 index 000000000000..942ea56262ba --- /dev/null +++ b/bsp/allwinner/dm/rtc/rtc-sunxi.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "rtc.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include + +#define SUNXI_RTC_LOSC_CTRL 0x000 +#define SUNXI_RTC_YMD 0x010 +#define SUNXI_RTC_HMS 0x014 +#define SUNXI_RTC_ALRM_DAY 0x020 +#define SUNXI_RTC_ALRM_HMS 0x024 +#define SUNXI_RTC_ALRM_EN 0x028 +#define SUNXI_RTC_ALRM_IRQ_EN 0x02c +#define SUNXI_RTC_ALRM_IRQ_STA 0x030 + +#define SUNXI_RTC_HMS_ACC RT_BIT(8) +#define SUNXI_RTC_YMD_ACC RT_BIT(7) +#define SUNXI_RTC_ALRM_EN_COUNTER RT_BIT(0) +#define SUNXI_RTC_ALRM_IRQ_COUNTER RT_BIT(0) + +#define SUNXI_RTC_YEAR_MIN 1970 +#define SUNXI_RTC_YEAR_MAX 2097 +#define SUNXI_RTC_ALARM_MAX_DAYS 255 + +struct sunxi_rtc +{ + struct rt_device parent; + + void *base; + int irq; + struct rt_rtc_wkalarm alarm; + rt_bool_t registered; + rt_bool_t irq_attached; +}; + +#define to_sunxi_rtc(raw) rt_container_of(raw, struct sunxi_rtc, parent) + +static rt_uint32_t sunxi_rtc_read(struct sunxi_rtc *rtc, rt_size_t offset) +{ + return HWREG32(rtc->base + offset); +} + +static void sunxi_rtc_write(struct sunxi_rtc *rtc, rt_size_t offset, + rt_uint32_t value) +{ + HWREG32(rtc->base + offset) = value; +} + +static rt_bool_t sunxi_rtc_is_leap_year(int year) +{ + return !(year % 4) && ((year % 100) || !(year % 400)); +} + +static int sunxi_rtc_days_in_month(int year, int month) +{ + static const rt_uint8_t days[] = + { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, + }; + + if (month == 1 && sunxi_rtc_is_leap_year(year)) + { + return 29; + } + + return days[month]; +} + +static rt_err_t sunxi_rtc_tm_to_day(const struct tm *tm, rt_uint32_t *day) +{ + int year = tm->tm_year + 1900; + int month = tm->tm_mon; + int index; + rt_uint32_t result = 0; + + if (year < SUNXI_RTC_YEAR_MIN || year > SUNXI_RTC_YEAR_MAX || + month < 0 || month >= 12 || tm->tm_mday < 1 || + tm->tm_mday > sunxi_rtc_days_in_month(year, month) || + tm->tm_hour < 0 || tm->tm_hour > 23 || + tm->tm_min < 0 || tm->tm_min > 59 || + tm->tm_sec < 0 || tm->tm_sec > 59) + { + return -RT_EINVAL; + } + + for (index = SUNXI_RTC_YEAR_MIN; index < year; ++index) + { + result += sunxi_rtc_is_leap_year(index) ? 366 : 365; + } + for (index = 0; index < month; ++index) + { + result += sunxi_rtc_days_in_month(year, index); + } + + *day = result + tm->tm_mday; + return RT_EOK; +} + +static rt_err_t sunxi_rtc_day_to_tm(rt_uint32_t day, struct tm *tm) +{ + int year = SUNXI_RTC_YEAR_MIN; + int month = 0; + int days; + + if (!day) + { + return -RT_EINVAL; + } + + --day; + while (year <= SUNXI_RTC_YEAR_MAX) + { + days = sunxi_rtc_is_leap_year(year) ? 366 : 365; + if (day < (rt_uint32_t)days) + { + break; + } + day -= days; + ++year; + } + if (year > SUNXI_RTC_YEAR_MAX) + { + return -RT_EINVAL; + } + + while (month < 12) + { + days = sunxi_rtc_days_in_month(year, month); + if (day < (rt_uint32_t)days) + { + break; + } + day -= days; + ++month; + } + + tm->tm_year = year - 1900; + tm->tm_mon = month; + tm->tm_mday = day + 1; + return RT_EOK; +} + +static rt_err_t sunxi_rtc_read_tm(struct sunxi_rtc *rtc, struct tm *tm, + rt_uint32_t *day) +{ + rt_uint32_t ymd; + rt_uint32_t hms; + int retry; + rt_err_t err; + + for (retry = 0; retry < 3; ++retry) + { + ymd = sunxi_rtc_read(rtc, SUNXI_RTC_YMD); + hms = sunxi_rtc_read(rtc, SUNXI_RTC_HMS); + if (ymd == sunxi_rtc_read(rtc, SUNXI_RTC_YMD) && + hms == sunxi_rtc_read(rtc, SUNXI_RTC_HMS)) + { + break; + } + } + + err = sunxi_rtc_day_to_tm(ymd, tm); + if (err) + { + return err; + } + + tm->tm_sec = hms & 0x3f; + tm->tm_min = (hms >> 8) & 0x3f; + tm->tm_hour = (hms >> 16) & 0x1f; + if (tm->tm_sec > 59 || tm->tm_min > 59 || tm->tm_hour > 23) + { + return -RT_EINVAL; + } + + if (day) + { + *day = ymd; + } + return RT_EOK; +} + +static rt_err_t sunxi_rtc_wait_ready(struct sunxi_rtc *rtc, rt_uint32_t mask) +{ + int retry; + + for (retry = 0; retry < 1000; ++retry) + { + if (!(sunxi_rtc_read(rtc, SUNXI_RTC_LOSC_CTRL) & mask)) + { + return RT_EOK; + } + rt_hw_us_delay(1000); + } + + return -RT_ETIMEOUT; +} + +static rt_err_t sunxi_rtc_write_tm(struct sunxi_rtc *rtc, const struct tm *tm) +{ + rt_uint32_t day; + rt_uint32_t hms; + rt_err_t err; + + if ((err = sunxi_rtc_tm_to_day(tm, &day))) + { + return err; + } + + hms = tm->tm_sec | ((rt_uint32_t)tm->tm_min << 8) | + ((rt_uint32_t)tm->tm_hour << 16); + if ((err = sunxi_rtc_wait_ready(rtc, SUNXI_RTC_HMS_ACC))) + { + return err; + } + sunxi_rtc_write(rtc, SUNXI_RTC_HMS, hms); + + if ((err = sunxi_rtc_wait_ready(rtc, SUNXI_RTC_YMD_ACC))) + { + return err; + } + sunxi_rtc_write(rtc, SUNXI_RTC_YMD, day); + + return sunxi_rtc_wait_ready(rtc, SUNXI_RTC_HMS_ACC | SUNXI_RTC_YMD_ACC); +} + +static rt_err_t sunxi_rtc_get_secs(struct sunxi_rtc *rtc, time_t *secs) +{ + struct tm tm = {0}; + rt_uint32_t day; + rt_uint64_t value; + rt_err_t err; + + if ((err = sunxi_rtc_read_tm(rtc, &tm, &day))) + { + return err; + } + + value = ((rt_uint64_t)day - 1) * 86400 + tm.tm_hour * 3600 + + tm.tm_min * 60 + tm.tm_sec; + *secs = (time_t)value; + return RT_EOK; +} + +static rt_err_t sunxi_rtc_set_secs(struct sunxi_rtc *rtc, const time_t *secs) +{ + struct tm tm; + + if (!gmtime_r(secs, &tm)) + { + return -RT_EINVAL; + } + + return sunxi_rtc_write_tm(rtc, &tm); +} + +static rt_err_t sunxi_rtc_get_alarm(struct sunxi_rtc *rtc, + struct rt_rtc_wkalarm *alarm) +{ + *alarm = rtc->alarm; + alarm->enable = !!(sunxi_rtc_read(rtc, SUNXI_RTC_ALRM_EN) & + SUNXI_RTC_ALRM_EN_COUNTER); + return RT_EOK; +} + +static rt_err_t sunxi_rtc_set_alarm(struct sunxi_rtc *rtc, + const struct rt_rtc_wkalarm *alarm) +{ + struct tm now = {0}; + struct tm target = {0}; + rt_uint32_t now_day; + rt_uint32_t target_day; + rt_uint32_t hms; + rt_err_t err; + + if (!alarm->enable) + { + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_EN, 0); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_EN, 0); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_STA, + SUNXI_RTC_ALRM_IRQ_COUNTER); + rtc->alarm = *alarm; + return RT_EOK; + } + + target.tm_sec = alarm->tm_sec; + target.tm_min = alarm->tm_min; + target.tm_hour = alarm->tm_hour; + target.tm_mday = alarm->tm_mday; + target.tm_mon = alarm->tm_mon; + target.tm_year = alarm->tm_year; + if ((err = sunxi_rtc_tm_to_day(&target, &target_day)) || + (err = sunxi_rtc_read_tm(rtc, &now, &now_day))) + { + return err; + } + + if (target_day < now_day || target_day - now_day > + SUNXI_RTC_ALARM_MAX_DAYS || + (target_day == now_day && + (target.tm_hour < now.tm_hour || + (target.tm_hour == now.tm_hour && target.tm_min < now.tm_min) || + (target.tm_hour == now.tm_hour && target.tm_min == now.tm_min && + target.tm_sec <= now.tm_sec)))) + { + return -RT_EINVAL; + } + + hms = target.tm_sec | ((rt_uint32_t)target.tm_min << 8) | + ((rt_uint32_t)target.tm_hour << 16); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_EN, 0); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_EN, 0); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_STA, SUNXI_RTC_ALRM_IRQ_COUNTER); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_DAY, target_day); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_HMS, hms); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_EN, SUNXI_RTC_ALRM_IRQ_COUNTER); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_EN, SUNXI_RTC_ALRM_EN_COUNTER); + rtc->alarm = *alarm; + + return RT_EOK; +} + +static rt_err_t sunxi_rtc_control(rt_device_t dev, int cmd, void *args) +{ + struct sunxi_rtc *rtc = to_sunxi_rtc(dev); + + if (!args) + { + return -RT_EINVAL; + } + + switch (cmd) + { + case RT_DEVICE_CTRL_RTC_GET_TIME: + return sunxi_rtc_get_secs(rtc, args); + + case RT_DEVICE_CTRL_RTC_SET_TIME: + return sunxi_rtc_set_secs(rtc, args); + + case RT_DEVICE_CTRL_RTC_GET_TIMEVAL: + ((struct timeval *)args)->tv_usec = 0; + return sunxi_rtc_get_secs(rtc, (time_t *)&((struct timeval *)args)->tv_sec); + + case RT_DEVICE_CTRL_RTC_SET_TIMEVAL: + return sunxi_rtc_set_secs(rtc, (time_t *)&((struct timeval *)args)->tv_sec); + + case RT_DEVICE_CTRL_RTC_GET_ALARM: + return sunxi_rtc_get_alarm(rtc, args); + + case RT_DEVICE_CTRL_RTC_SET_ALARM: + return sunxi_rtc_set_alarm(rtc, args); + + default: + return -RT_EINVAL; + } +} + +#ifdef RT_USING_DEVICE_OPS +static const struct rt_device_ops sunxi_rtc_ops = +{ + .control = sunxi_rtc_control, +}; +#endif + +static void sunxi_rtc_isr(int irqno, void *parameter) +{ + struct sunxi_rtc *rtc = parameter; + + RT_UNUSED(irqno); + if (sunxi_rtc_read(rtc, SUNXI_RTC_ALRM_IRQ_STA) & + SUNXI_RTC_ALRM_IRQ_COUNTER) + { + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_STA, + SUNXI_RTC_ALRM_IRQ_COUNTER); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_EN, 0); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_EN, 0); + rtc->alarm.enable = RT_FALSE; + rt_alarm_update(&rtc->parent, 1); + } +} + +static void sunxi_rtc_release(struct sunxi_rtc *rtc) +{ + if (rtc->irq_attached) + { + rt_pic_irq_mask(rtc->irq); + rt_pic_detach_irq(rtc->irq, rtc); + } + if (rtc->registered) + { + rt_device_unregister(&rtc->parent); + } + if (rtc->base) + { + rt_iounmap(rtc->base); + } + rt_free(rtc); +} + +static rt_err_t sunxi_rtc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct sunxi_rtc *rtc = rt_calloc(1, sizeof(*rtc)); + + if (!rtc) + { + return -RT_ENOMEM; + } + + rtc->base = rt_dm_dev_iomap(dev, 0); + rtc->irq = rt_dm_dev_get_irq(dev, 0); + if (!rtc->base || rtc->irq < 0) + { + err = rtc->irq < 0 ? rtc->irq : -RT_EIO; + goto _fail; + } + + rtc->parent.type = RT_Device_Class_RTC; +#ifdef RT_USING_DEVICE_OPS + rtc->parent.ops = &sunxi_rtc_ops; +#else + rtc->parent.control = sunxi_rtc_control; +#endif + if ((err = rt_device_register(&rtc->parent, "rtc", RT_DEVICE_FLAG_RDWR))) + { + goto _fail; + } + rtc->registered = RT_TRUE; + + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_EN, 0); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_EN, 0); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_STA, SUNXI_RTC_ALRM_IRQ_COUNTER); + if ((err = rt_pic_attach_irq(rtc->irq, sunxi_rtc_isr, rtc, "rtc", RT_IRQ_F_NONE))) + { + goto _fail; + } + rtc->irq_attached = RT_TRUE; + rt_pic_irq_unmask(rtc->irq); + + dev->user_data = rtc; + rt_dm_dev_bind_fwdata(dev, RT_NULL, rtc); + return RT_EOK; + +_fail: + sunxi_rtc_release(rtc); + return err; +} + +static rt_err_t sunxi_rtc_remove(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct sunxi_rtc *rtc = dev->user_data; + + rt_dm_dev_unbind_fwdata(dev, RT_NULL); + sunxi_rtc_release(rtc); + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_rtc_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-rtc" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_rtc_driver = +{ + .name = "rtc-sunxi", + .ids = sunxi_rtc_ofw_ids, + .probe = sunxi_rtc_probe, + .remove = sunxi_rtc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_rtc_driver); diff --git a/bsp/allwinner/dm/sdio/Kconfig b/bsp/allwinner/dm/sdio/Kconfig new file mode 100644 index 000000000000..132e6d6929dd --- /dev/null +++ b/bsp/allwinner/dm/sdio/Kconfig @@ -0,0 +1,7 @@ +config RT_SDIO_SUNXI + bool "Allwinner Sunxi MMC controller" + depends on RT_USING_SDIO + depends on RT_USING_DM + depends on RT_USING_CLK + depends on RT_USING_RESET + default n diff --git a/bsp/allwinner/dm/sdio/SConscript b/bsp/allwinner/dm/sdio/SConscript new file mode 100644 index 000000000000..24e3c0882851 --- /dev/null +++ b/bsp/allwinner/dm/sdio/SConscript @@ -0,0 +1,13 @@ +from building import * +Import('RTT_ROOT') + +cwd = GetCurrentDir() +src = [] + +if GetDepend('RT_SDIO_SUNXI'): + src += ['sdio-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], + CPPPATH = [RTT_ROOT + '/components/drivers/sdio']) + +Return('group') diff --git a/bsp/allwinner/dm/sdio/sdio-sunxi.c b/bsp/allwinner/dm/sdio/sdio-sunxi.c new file mode 100644 index 000000000000..0b0a6fff0e33 --- /dev/null +++ b/bsp/allwinner/dm/sdio/sdio-sunxi.c @@ -0,0 +1,1193 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include + +#define DBG_TAG "sdio.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include "dev_sdio_dm.h" + +#define SUNXI_MMC_GCTRL 0x00 +#define SUNXI_MMC_CLKCR 0x04 +#define SUNXI_MMC_TMOUT 0x08 +#define SUNXI_MMC_WIDTH 0x0c +#define SUNXI_MMC_BLKSZ 0x10 +#define SUNXI_MMC_BCNTR 0x14 +#define SUNXI_MMC_CMDR 0x18 +#define SUNXI_MMC_CARG 0x1c +#define SUNXI_MMC_RESP0 0x20 +#define SUNXI_MMC_RESP1 0x24 +#define SUNXI_MMC_RESP2 0x28 +#define SUNXI_MMC_RESP3 0x2c +#define SUNXI_MMC_IMASK 0x30 +#define SUNXI_MMC_RINTR 0x38 +#define SUNXI_MMC_STAS 0x3c +#define SUNXI_MMC_FTRGL 0x40 +#define SUNXI_MMC_FUNS 0x44 +#define SUNXI_MMC_NTSR 0x5c +#define SUNXI_MMC_HWRST 0x78 +#define SUNXI_MMC_DRV_DL 0x140 +#define SUNXI_MMC_FIFO 0x200 +#define SUNXI_MMC_DMAC 0x80 +#define SUNXI_MMC_DLBA 0x84 +#define SUNXI_MMC_IDST 0x88 +#define SUNXI_MMC_IDIE 0x8c +#define SUNXI_MMC_CHDA 0x90 +#define SUNXI_MMC_CBDA 0x94 + +#define SUNXI_MMC_SOFT_RESET RT_BIT(0) +#define SUNXI_MMC_FIFO_RESET RT_BIT(1) +#define SUNXI_MMC_DMA_RESET RT_BIT(2) +#define SUNXI_MMC_INT_ENABLE RT_BIT(4) +#define SUNXI_MMC_DMA_ENABLE RT_BIT(5) +#define SUNXI_MMC_MEMORY_ACCESS_DONE RT_BIT(29) +#define SUNXI_MMC_ACCESS_DONE_DIRECT RT_BIT(30) +#define SUNXI_MMC_ACCESS_BY_AHB RT_BIT(31) +#define SUNXI_MMC_DDR_MODE RT_BIT(10) +#define SUNXI_MMC_HW_RESET (SUNXI_MMC_SOFT_RESET | SUNXI_MMC_FIFO_RESET | SUNXI_MMC_DMA_RESET) + +#define SUNXI_MMC_CARD_CLOCK_ON RT_BIT(16) +#define SUNXI_MMC_MASK_DATA0 RT_BIT(31) + +#define SUNXI_MMC_NTSR_2X_TIMING RT_BIT(31) +#define SUNXI_MMC_NTSR_CMD_SAMPLE_MASK (0x3U << 4) +#define SUNXI_MMC_NTSR_DATA_SAMPLE_MASK (0x3U << 8) +#define SUNXI_MMC_DRV_DL_AUTO_PHASE RT_BIT(7) +#define SUNXI_MMC_DRV_DL_CMD_PHASE RT_BIT(16) +#define SUNXI_MMC_DRV_DL_DATA_PHASE RT_BIT(17) + +#define SUNXI_MMC_RESP_EXPIRE RT_BIT(6) +#define SUNXI_MMC_LONG_RESPONSE RT_BIT(7) +#define SUNXI_MMC_CHECK_RESP_CRC RT_BIT(8) +#define SUNXI_MMC_DATA_EXPIRE RT_BIT(9) +#define SUNXI_MMC_WRITE RT_BIT(10) +#define SUNXI_MMC_SEND_AUTO_STOP RT_BIT(12) +#define SUNXI_MMC_WAIT_PRE_OVER RT_BIT(13) +#define SUNXI_MMC_SEND_INIT RT_BIT(15) +#define SUNXI_MMC_UPCLK_ONLY RT_BIT(21) +#define SUNXI_MMC_START RT_BIT(31) + +#define SUNXI_MMC_RESP_ERROR RT_BIT(1) +#define SUNXI_MMC_COMMAND_DONE RT_BIT(2) +#define SUNXI_MMC_DATA_OVER RT_BIT(3) +#define SUNXI_MMC_TX_DATA_REQUEST RT_BIT(4) +#define SUNXI_MMC_RX_DATA_REQUEST RT_BIT(5) +#define SUNXI_MMC_RESP_CRC_ERROR RT_BIT(6) +#define SUNXI_MMC_DATA_CRC_ERROR RT_BIT(7) +#define SUNXI_MMC_RESP_TIMEOUT RT_BIT(8) +#define SUNXI_MMC_DATA_TIMEOUT RT_BIT(9) +#define SUNXI_MMC_FIFO_RUN_ERROR RT_BIT(11) +#define SUNXI_MMC_HARDWARE_LOCKED RT_BIT(12) +#define SUNXI_MMC_START_BIT_ERROR RT_BIT(13) +#define SUNXI_MMC_END_BIT_ERROR RT_BIT(15) +#define SUNXI_MMC_AUTO_COMMAND_DONE RT_BIT(14) +#define SUNXI_MMC_ERROR_MASK (SUNXI_MMC_RESP_ERROR | \ + SUNXI_MMC_RESP_CRC_ERROR | SUNXI_MMC_DATA_CRC_ERROR | \ + SUNXI_MMC_RESP_TIMEOUT | SUNXI_MMC_DATA_TIMEOUT | \ + SUNXI_MMC_FIFO_RUN_ERROR | SUNXI_MMC_HARDWARE_LOCKED | \ + SUNXI_MMC_START_BIT_ERROR | SUNXI_MMC_END_BIT_ERROR) + +#define SUNXI_MMC_IDMAC_SOFT_RESET RT_BIT(0) +#define SUNXI_MMC_IDMAC_FIX_BURST RT_BIT(1) +#define SUNXI_MMC_IDMAC_ENABLE RT_BIT(7) +#define SUNXI_MMC_IDMAC_TX_INT RT_BIT(0) +#define SUNXI_MMC_IDMAC_RX_INT RT_BIT(1) +#define SUNXI_MMC_IDMAC_FATAL_BUS RT_BIT(2) +#define SUNXI_MMC_IDMAC_DESC_INVALID RT_BIT(4) +#define SUNXI_MMC_IDMAC_CARD_ERROR RT_BIT(5) +#define SUNXI_MMC_IDMAC_ABNORMAL RT_BIT(9) +#define SUNXI_MMC_IDMAC_ERROR_MASK (SUNXI_MMC_IDMAC_FATAL_BUS | \ + SUNXI_MMC_IDMAC_DESC_INVALID | SUNXI_MMC_IDMAC_CARD_ERROR | \ + SUNXI_MMC_IDMAC_ABNORMAL) + +#define SUNXI_MMC_IDMA_DESC_DIC RT_BIT(1) +#define SUNXI_MMC_IDMA_DESC_LAST RT_BIT(2) +#define SUNXI_MMC_IDMA_DESC_FIRST RT_BIT(3) +#define SUNXI_MMC_IDMA_DESC_CHAIN RT_BIT(4) +#define SUNXI_MMC_IDMA_DESC_END_RING RT_BIT(5) +#define SUNXI_MMC_IDMA_DESC_OWN RT_BIT(31) + +#define SUNXI_MMC_FIFO_EMPTY RT_BIT(2) +#define SUNXI_MMC_FIFO_FULL RT_BIT(3) +#define SUNXI_MMC_CARD_PRESENT RT_BIT(8) +#define SUNXI_MMC_CARD_BUSY RT_BIT(9) + +#define SUNXI_MMC_WIDTH_1 0 +#define SUNXI_MMC_WIDTH_4 1 +#define SUNXI_MMC_WIDTH_8 2 +#define SUNXI_MMC_TIMEOUT_MS 1000 +#define SUNXI_MMC_CD_DEBOUNCE_MS 50 +#define SUNXI_MMC_LEGACY_MAX_CLOCK 25000000 +#define SUNXI_MMC_SD_HS_RETRIES 3 +#define SUNXI_MMC_IDMA_DESC_SHIFT 2 +#define SUNXI_MMC_IDMA_SEG_SIZE 4096U +#define SUNXI_MMC_IDMA_DESC_COUNT 8U +#define SUNXI_MMC_IDMA_MAX_SIZE (SUNXI_MMC_IDMA_DESC_COUNT * SUNXI_MMC_IDMA_SEG_SIZE) +#define SUNXI_MMC_PIO_MAX_SIZE 4U +/* Burst 8, RX threshold 7, TX threshold 8. */ +#define SUNXI_MMC_PIO_FTRGL ((2U << 28) | (7U << 16) | 8U) +/* F101 IDMA: burst 16, RX threshold 15, TX threshold 240. */ +#define SUNXI_MMC_IDMA_FTRGL ((3U << 28) | (15U << 16) | 240U) + +struct sunxi_mmc_idma_desc +{ + rt_uint32_t config; + rt_uint32_t buf_size; + rt_uint32_t buf_addr; + rt_uint32_t next_desc; +}; + +struct sunxi_mmc +{ + struct rt_mmcsd_host *host; + struct rt_device *dev; + struct rt_clk *bus_clk; + struct rt_clk *mmc_clk; + struct rt_reset_control *reset; + void *base; + struct sunxi_mmc_idma_desc *idma_desc; + rt_ubase_t idma_desc_dma; + int irq; + volatile rt_uint32_t irq_status; + volatile rt_uint32_t idma_status; + rt_uint32_t data_done_mask; + rt_bool_t request_active; + rt_bool_t wait_idma; + rt_bool_t irq_installed; + struct rt_completion completion; + rt_base_t cd_pin; + rt_uint8_t cd_present_level; + rt_bool_t cd_irq_attached; + struct rt_timer cd_timer; + rt_bool_t cd_timer_initialized; + rt_bool_t card_present; + rt_bool_t card_reported; + rt_bool_t sd_hs_rejected; + rt_bool_t no_sdio; +}; + +static rt_uint32_t sunxi_mmc_read(struct sunxi_mmc *mmc, rt_uint32_t offset) +{ + return HWREG32(mmc->base + offset); +} + +static void sunxi_mmc_write(struct sunxi_mmc *mmc, rt_uint32_t offset, + rt_uint32_t value) +{ + HWREG32(mmc->base + offset) = value; +} + +static rt_err_t sunxi_mmc_wait_clear(struct sunxi_mmc *mmc, rt_uint32_t offset, + rt_uint32_t mask, rt_uint32_t timeout_ms) +{ + rt_tick_t timeout = rt_tick_get() + rt_tick_from_millisecond(timeout_ms); + + while (sunxi_mmc_read(mmc, offset) & mask) + { + if ((rt_int32_t)(rt_tick_get() - timeout) >= 0) + { + return -RT_ETIMEOUT; + } + rt_hw_cpu_relax(); + } + + return RT_EOK; +} + +static rt_err_t sunxi_mmc_update_clock(struct sunxi_mmc *mmc, rt_bool_t enable) +{ + rt_uint32_t clock = sunxi_mmc_read(mmc, SUNXI_MMC_CLKCR); + rt_err_t err; + + if (enable) + { + clock |= SUNXI_MMC_CARD_CLOCK_ON; + } + else + { + clock &= ~SUNXI_MMC_CARD_CLOCK_ON; + } + clock |= SUNXI_MMC_MASK_DATA0; + sunxi_mmc_write(mmc, SUNXI_MMC_CLKCR, clock); + sunxi_mmc_write(mmc, SUNXI_MMC_CMDR, + SUNXI_MMC_START | SUNXI_MMC_UPCLK_ONLY | SUNXI_MMC_WAIT_PRE_OVER); + err = sunxi_mmc_wait_clear(mmc, SUNXI_MMC_CMDR, SUNXI_MMC_START, + SUNXI_MMC_TIMEOUT_MS); + sunxi_mmc_write(mmc, SUNXI_MMC_RINTR, 0xffffffff); + clock = sunxi_mmc_read(mmc, SUNXI_MMC_CLKCR); + sunxi_mmc_write(mmc, SUNXI_MMC_CLKCR, clock & ~SUNXI_MMC_MASK_DATA0); + + return err; +} + +static rt_bool_t sunxi_mmc_is_ddr_timing(rt_uint32_t timing) +{ + return timing == MMCSD_TIMING_UHS_DDR50 || + timing == MMCSD_TIMING_MMC_DDR52 || + timing == MMCSD_TIMING_MMC_HS400 || + timing == MMCSD_TIMING_MMC_HS400_ENH_DS; +} + +static void sunxi_mmc_set_timing(struct sunxi_mmc *mmc, + const struct rt_mmcsd_io_cfg *io_cfg) +{ + rt_uint32_t drv_dl; + rt_uint32_t gctrl; + + gctrl = sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL); + if (sunxi_mmc_is_ddr_timing(io_cfg->timing)) + { + gctrl |= SUNXI_MMC_DDR_MODE; + } + else + { + gctrl &= ~SUNXI_MMC_DDR_MODE; + } + sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, gctrl); + + drv_dl = sunxi_mmc_read(mmc, SUNXI_MMC_DRV_DL); + /* + * SMHC0 uses timing mode 1. The F101 SDK enables 2x clocking in NTSR + * and leaves DRV_DL at its default phase for 25/50 MHz SDR transfers. + * Selecting the delayed CMD/DATA phase here samples each byte's first + * nibble incorrectly on the board's SD socket. + */ + drv_dl &= ~(SUNXI_MMC_DRV_DL_AUTO_PHASE | + SUNXI_MMC_DRV_DL_CMD_PHASE | SUNXI_MMC_DRV_DL_DATA_PHASE); + sunxi_mmc_write(mmc, SUNXI_MMC_DRV_DL, drv_dl); +} + +static void sunxi_mmc_set_clock_phase(struct sunxi_mmc *mmc) +{ + rt_uint32_t ntsr; + + ntsr = sunxi_mmc_read(mmc, SUNXI_MMC_NTSR); + ntsr &= ~(SUNXI_MMC_NTSR_CMD_SAMPLE_MASK | + SUNXI_MMC_NTSR_DATA_SAMPLE_MASK); + ntsr |= SUNXI_MMC_NTSR_2X_TIMING; + sunxi_mmc_write(mmc, SUNXI_MMC_NTSR, ntsr); +} + +static void sunxi_mmc_set_iocfg(struct rt_mmcsd_host *host, + struct rt_mmcsd_io_cfg *io_cfg) +{ + struct sunxi_mmc *mmc = host->private_data; + struct rt_mmcsd_io_cfg effective_cfg = *io_cfg; + rt_uint32_t width = SUNXI_MMC_WIDTH_1; + rt_err_t err; + rt_err_t rate_err = RT_EOK; + + /* + * The SD core retains the HS rate reported by CMD6 even when the card + * rejects the mode switch. Keep the host at the legacy rate in that case. + */ + if (mmc->sd_hs_rejected && + effective_cfg.timing == MMCSD_TIMING_LEGACY && + effective_cfg.clock > SUNXI_MMC_LEGACY_MAX_CLOCK) + { + effective_cfg.clock = SUNXI_MMC_LEGACY_MAX_CLOCK; + } + + if (sunxi_mmc_update_clock(mmc, RT_FALSE)) + { + LOG_E("cannot disable card clock"); + return; + } + + /* SMHC timing and clock-source registers are writable only while gated. */ + rt_clk_disable_unprepare(mmc->mmc_clk); + + if (effective_cfg.clock) + { + /* The controller consumes a module clock twice the card clock. */ + if (rt_clk_set_rate(mmc->mmc_clk, effective_cfg.clock * 2)) + { + LOG_E("cannot set %u Hz card clock", effective_cfg.clock); + rate_err = -RT_ERROR; + } + } + + if (!rate_err) + { + sunxi_mmc_set_timing(mmc, &effective_cfg); + } + + err = rt_clk_prepare_enable(mmc->mmc_clk); + if (err) + { + LOG_E("cannot configure card timing: %d", err); + return; + } + if (rate_err) + { + return; + } + sunxi_mmc_set_clock_phase(mmc); + + switch (effective_cfg.bus_width) + { + case MMCSD_BUS_WIDTH_4: + width = SUNXI_MMC_WIDTH_4; + break; + case MMCSD_BUS_WIDTH_8: + width = SUNXI_MMC_WIDTH_8; + break; + case MMCSD_BUS_WIDTH_1: + default: + break; + } + sunxi_mmc_write(mmc, SUNXI_MMC_WIDTH, width); + if (sunxi_mmc_update_clock(mmc, effective_cfg.clock != 0)) + { + LOG_E("clock update timed out"); + } +} + +static void sunxi_mmc_pio_xfer(struct sunxi_mmc *mmc, + struct rt_mmcsd_data *data, rt_uint32_t *offset) +{ + rt_uint8_t *buffer = (rt_uint8_t *)data->buf; + rt_uint32_t total = data->blksize * data->blks; + rt_uint32_t status = sunxi_mmc_read(mmc, SUNXI_MMC_STAS); + + if (data->flags & DATA_DIR_READ) + { + while (!(status & SUNXI_MMC_FIFO_EMPTY) && *offset < total) + { + rt_uint32_t value = sunxi_mmc_read(mmc, SUNXI_MMC_FIFO); + rt_uint32_t remaining = total - *offset; + rt_uint32_t bytes = remaining < 4 ? remaining : 4; + + rt_memcpy(buffer + *offset, &value, bytes); + *offset += bytes; + status = sunxi_mmc_read(mmc, SUNXI_MMC_STAS); + } + } + else + { + while (!(status & SUNXI_MMC_FIFO_FULL) && *offset < total) + { + rt_uint32_t value = 0; + rt_uint32_t remaining = total - *offset; + rt_uint32_t bytes = remaining < 4 ? remaining : 4; + + rt_memcpy(&value, buffer + *offset, bytes); + sunxi_mmc_write(mmc, SUNXI_MMC_FIFO, value); + *offset += bytes; + status = sunxi_mmc_read(mmc, SUNXI_MMC_STAS); + } + } +} + +static void sunxi_mmc_drain_rxfifo(struct sunxi_mmc *mmc, + struct rt_mmcsd_data *data, rt_uint32_t *offset) +{ + rt_uint8_t *buffer = (rt_uint8_t *)data->buf; + rt_uint32_t total = data->blksize * data->blks; + + while (!(sunxi_mmc_read(mmc, SUNXI_MMC_STAS) & SUNXI_MMC_FIFO_EMPTY) && + *offset < total) + { + rt_uint32_t value = sunxi_mmc_read(mmc, SUNXI_MMC_FIFO); + rt_uint32_t remaining = total - *offset; + rt_uint32_t bytes = remaining < 4 ? remaining : 4; + + rt_memcpy(buffer + *offset, &value, bytes); + *offset += bytes; + } +} + +static void sunxi_mmc_build_idma_desc(struct sunxi_mmc *mmc, + rt_ubase_t buffer_dma, rt_uint32_t total) +{ + rt_uint32_t count = (total + SUNXI_MMC_IDMA_SEG_SIZE - 1) / + SUNXI_MMC_IDMA_SEG_SIZE; + rt_uint32_t offset = 0; + rt_uint32_t index; + + for (index = 0; index < count; index++) + { + struct sunxi_mmc_idma_desc *desc = &mmc->idma_desc[index]; + rt_uint32_t length = rt_min_t(rt_uint32_t, + total - offset, SUNXI_MMC_IDMA_SEG_SIZE); + + desc->config = SUNXI_MMC_IDMA_DESC_CHAIN | + SUNXI_MMC_IDMA_DESC_OWN | SUNXI_MMC_IDMA_DESC_DIC; + desc->buf_size = length; + desc->buf_addr = (buffer_dma + offset) >> + SUNXI_MMC_IDMA_DESC_SHIFT; + desc->next_desc = (mmc->idma_desc_dma + + (index + 1) * sizeof(*desc)) >> SUNXI_MMC_IDMA_DESC_SHIFT; + + if (index == 0) + { + desc->config |= SUNXI_MMC_IDMA_DESC_FIRST; + } + if (index + 1 == count) + { + desc->config &= ~SUNXI_MMC_IDMA_DESC_DIC; + desc->config |= SUNXI_MMC_IDMA_DESC_LAST | + SUNXI_MMC_IDMA_DESC_END_RING; + desc->next_desc = 0; + } + offset += length; + } + + rt_hw_cpu_dcache_clean_and_invalidate(mmc->idma_desc, + count * sizeof(*mmc->idma_desc)); + rt_hw_dsb(); +} + +static rt_err_t sunxi_mmc_start_idma(struct sunxi_mmc *mmc, + struct rt_mmcsd_data *data) +{ + rt_uint32_t total = data->blksize * data->blks; + rt_ubase_t buffer_dma; + rt_uint32_t gctrl; + rt_err_t err; + + if (!mmc->irq_installed || !mmc->idma_desc || + total > SUNXI_MMC_IDMA_MAX_SIZE) + { + return -RT_ENOSYS; + } + + buffer_dma = (rt_ubase_t)rt_kmem_v2p(data->buf); + if (!buffer_dma || (buffer_dma & 0x3)) + { + return -RT_EINVAL; + } + + /* + * Clean dirty lines before a read so they cannot overwrite DMA data, + * and invalidate again after the transfer before the CPU reads it. + */ + if (data->flags & DATA_DIR_READ) + { + rt_hw_cpu_dcache_clean_and_invalidate(data->buf, total); + } + else + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, total); + } + sunxi_mmc_build_idma_desc(mmc, buffer_dma, total); + + gctrl = sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL); + gctrl &= ~SUNXI_MMC_ACCESS_BY_AHB; + gctrl |= SUNXI_MMC_DMA_ENABLE | SUNXI_MMC_DMA_RESET | + SUNXI_MMC_FIFO_RESET; + sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, gctrl); + err = sunxi_mmc_wait_clear(mmc, SUNXI_MMC_GCTRL, + SUNXI_MMC_DMA_RESET | SUNXI_MMC_FIFO_RESET, + SUNXI_MMC_TIMEOUT_MS); + if (err) + { + return err; + } + + sunxi_mmc_write(mmc, SUNXI_MMC_DMAC, SUNXI_MMC_IDMAC_SOFT_RESET); + err = sunxi_mmc_wait_clear(mmc, SUNXI_MMC_DMAC, + SUNXI_MMC_IDMAC_SOFT_RESET, SUNXI_MMC_TIMEOUT_MS); + if (err) + { + return err; + } + + sunxi_mmc_write(mmc, SUNXI_MMC_IDST, 0x337); + sunxi_mmc_write(mmc, SUNXI_MMC_IDIE, + (data->flags & DATA_DIR_READ) ? SUNXI_MMC_IDMAC_RX_INT : 0); + sunxi_mmc_write(mmc, SUNXI_MMC_DLBA, mmc->idma_desc_dma >> + SUNXI_MMC_IDMA_DESC_SHIFT); + sunxi_mmc_write(mmc, SUNXI_MMC_FTRGL, SUNXI_MMC_IDMA_FTRGL); + sunxi_mmc_write(mmc, SUNXI_MMC_DMAC, SUNXI_MMC_IDMAC_FIX_BURST | + SUNXI_MMC_IDMAC_ENABLE); + + return RT_EOK; +} + +static rt_err_t sunxi_mmc_wait_memory_access(struct sunxi_mmc *mmc) +{ + rt_tick_t timeout = rt_tick_get() + + rt_tick_from_millisecond(SUNXI_MMC_TIMEOUT_MS); + + while (!(sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL) & + SUNXI_MMC_MEMORY_ACCESS_DONE)) + { + if ((rt_int32_t)(rt_tick_get() - timeout) >= 0) + { + return -RT_ETIMEOUT; + } + rt_hw_cpu_relax(); + } + + return RT_EOK; +} + +static void sunxi_mmc_stop_idma(struct sunxi_mmc *mmc) +{ + rt_uint32_t gctrl; + + sunxi_mmc_write(mmc, SUNXI_MMC_IMASK, 0); + sunxi_mmc_write(mmc, SUNXI_MMC_IDIE, 0); + sunxi_mmc_write(mmc, SUNXI_MMC_IDST, 0x337); + sunxi_mmc_write(mmc, SUNXI_MMC_DMAC, 0); + + gctrl = sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL); + sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, gctrl | SUNXI_MMC_DMA_RESET); + sunxi_mmc_wait_clear(mmc, SUNXI_MMC_GCTRL, SUNXI_MMC_DMA_RESET, + SUNXI_MMC_TIMEOUT_MS); + + gctrl = sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL); + gctrl &= ~SUNXI_MMC_DMA_ENABLE; + gctrl |= SUNXI_MMC_ACCESS_BY_AHB | SUNXI_MMC_FIFO_RESET; + sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, gctrl); + sunxi_mmc_wait_clear(mmc, SUNXI_MMC_GCTRL, SUNXI_MMC_FIFO_RESET, + SUNXI_MMC_TIMEOUT_MS); + sunxi_mmc_write(mmc, SUNXI_MMC_FTRGL, SUNXI_MMC_PIO_FTRGL); +} + +static void sunxi_mmc_isr(int vector, void *param) +{ + struct sunxi_mmc *mmc = param; + rt_uint32_t status = sunxi_mmc_read(mmc, SUNXI_MMC_RINTR); + rt_uint32_t idma_status = sunxi_mmc_read(mmc, SUNXI_MMC_IDST); + + (void)vector; + if (!status && !idma_status) + { + return; + } + + sunxi_mmc_write(mmc, SUNXI_MMC_RINTR, status); + sunxi_mmc_write(mmc, SUNXI_MMC_IDST, idma_status); + mmc->irq_status |= status; + mmc->idma_status |= idma_status; + + if (!mmc->request_active) + { + return; + } + + if ((mmc->irq_status & SUNXI_MMC_ERROR_MASK) || + (mmc->idma_status & SUNXI_MMC_IDMAC_ERROR_MASK) || + ((mmc->irq_status & mmc->data_done_mask) && + (!mmc->wait_idma || + (mmc->idma_status & SUNXI_MMC_IDMAC_RX_INT)))) + { + rt_completion_done(&mmc->completion); + } +} + +static rt_err_t sunxi_mmc_reset_fifo(struct sunxi_mmc *mmc) +{ + sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, + sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL) | SUNXI_MMC_FIFO_RESET); + + return sunxi_mmc_wait_clear(mmc, SUNXI_MMC_GCTRL, + SUNXI_MMC_FIFO_RESET, SUNXI_MMC_TIMEOUT_MS); +} + +static rt_err_t sunxi_mmc_wait_busy(struct sunxi_mmc *mmc, rt_uint32_t timeout_ms) +{ + rt_tick_t timeout = rt_tick_get() + rt_tick_from_millisecond(timeout_ms); + + while (sunxi_mmc_read(mmc, SUNXI_MMC_STAS) & SUNXI_MMC_CARD_BUSY) + { + if ((rt_int32_t)(rt_tick_get() - timeout) >= 0) + { + return -RT_ETIMEOUT; + } + rt_hw_cpu_relax(); + } + + return RT_EOK; +} + +static void sunxi_mmc_request(struct rt_mmcsd_host *host, + struct rt_mmcsd_req *request) +{ + rt_uint32_t command; + rt_uint32_t status = 0; + rt_uint32_t idma_status = 0; + rt_uint32_t idma_imask = 0; + rt_uint32_t transferred = 0; + rt_tick_t timeout; + rt_err_t err = RT_EOK; + rt_bool_t use_idma = RT_FALSE; + struct rt_mmcsd_cmd *cmd = request->cmd; + struct rt_mmcsd_data *data = request->data; + struct sunxi_mmc *mmc = host->private_data; + rt_uint8_t *switch_status; + rt_uint8_t cmd6_hs_attempt = 0; + + /* Let a fixed SD-only slot skip the generic core's SDIO CMD5 probe. */ + if (mmc->no_sdio && cmd->cmd_code == SD_IO_SEND_OP_COND) + { + cmd->err = -RT_EIO; + if (data) + { + data->err = -RT_EIO; + data->bytes_xfered = 0; + } + mmcsd_req_complete(host); + return; + } + + command = SUNXI_MMC_START | (cmd->cmd_code & 0x3f); + if (cmd->cmd_code == GO_IDLE_STATE) + { + command |= SUNXI_MMC_SEND_INIT; + } + if (resp_type(cmd) != RESP_NONE) + { + command |= SUNXI_MMC_RESP_EXPIRE; + if (resp_type(cmd) == RESP_R2) + { + command |= SUNXI_MMC_LONG_RESPONSE | SUNXI_MMC_CHECK_RESP_CRC; + } + else if (resp_type(cmd) != RESP_R3 && resp_type(cmd) != RESP_R4) + { + command |= SUNXI_MMC_CHECK_RESP_CRC; + } + } + +_retry_cmd: + status = 0; + transferred = 0; + err = RT_EOK; + + if (data) + { + command |= SUNXI_MMC_DATA_EXPIRE | SUNXI_MMC_WAIT_PRE_OVER; + if (data->flags & DATA_DIR_WRITE) + { + command |= SUNXI_MMC_WRITE; + } + if (data->blks > 1) + { + /* F101 SMHC0 uses block mode plus its internal CMD12. */ + command |= SUNXI_MMC_SEND_AUTO_STOP; + } + sunxi_mmc_write(mmc, SUNXI_MMC_BLKSZ, data->blksize); + sunxi_mmc_write(mmc, SUNXI_MMC_BCNTR, data->blksize * data->blks); + + use_idma = data->blksize * data->blks > SUNXI_MMC_PIO_MAX_SIZE; + if (use_idma && sunxi_mmc_start_idma(mmc, data)) + { + sunxi_mmc_stop_idma(mmc); + use_idma = RT_FALSE; + } + } + + sunxi_mmc_write(mmc, SUNXI_MMC_RINTR, 0xffffffff); + if (use_idma) + { + mmc->irq_status = 0; + mmc->idma_status = 0; + mmc->data_done_mask = data->blks > 1 ? SUNXI_MMC_AUTO_COMMAND_DONE : + SUNXI_MMC_DATA_OVER; + mmc->wait_idma = !!(data->flags & DATA_DIR_READ); + mmc->request_active = RT_TRUE; + rt_completion_init(&mmc->completion); + /* CMD_DONE is diagnostic only; data completion remains authoritative. */ + idma_imask = SUNXI_MMC_ERROR_MASK | SUNXI_MMC_COMMAND_DONE | + mmc->data_done_mask; + sunxi_mmc_write(mmc, SUNXI_MMC_IMASK, idma_imask); + sunxi_mmc_write(mmc, SUNXI_MMC_CARG, cmd->arg); + sunxi_mmc_write(mmc, SUNXI_MMC_CMDR, command); + + err = rt_completion_wait(&mmc->completion, + rt_tick_from_millisecond(SUNXI_MMC_TIMEOUT_MS)); + status = mmc->irq_status; + idma_status = mmc->idma_status; + mmc->request_active = RT_FALSE; + sunxi_mmc_write(mmc, SUNXI_MMC_IMASK, 0); + + if (err) + { + LOG_E("IDMA transfer timed out: cmd=%u event=%08x/%08x", + cmd->cmd_code, status, idma_status); + err = -RT_ETIMEOUT; + } + if (status & SUNXI_MMC_ERROR_MASK) + { + err = (status & (SUNXI_MMC_RESP_TIMEOUT | SUNXI_MMC_DATA_TIMEOUT)) ? + -RT_ETIMEOUT : -RT_EIO; + } + if (idma_status & SUNXI_MMC_IDMAC_ERROR_MASK) + { + LOG_E("IDMA transfer failed: cmd=%u rintr=%08x idst=%08x", + cmd->cmd_code, status, idma_status); + err = -RT_EIO; + } + if (!err && (mmc->wait_idma && + !(idma_status & SUNXI_MMC_IDMAC_RX_INT))) + { + err = -RT_EIO; + } + if (!err && !(status & mmc->data_done_mask)) + { + err = -RT_EIO; + } + if (!err) + { + err = sunxi_mmc_wait_memory_access(mmc); + } + if (!err && (data->flags & DATA_DIR_READ)) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, data->buf, + data->blksize * data->blks); + } + if (!err) + { + transferred = data->blksize * data->blks; + } + sunxi_mmc_stop_idma(mmc); + } + else + { + sunxi_mmc_write(mmc, SUNXI_MMC_CARG, cmd->arg); + sunxi_mmc_write(mmc, SUNXI_MMC_CMDR, command); + + timeout = rt_tick_get() + rt_tick_from_millisecond(SUNXI_MMC_TIMEOUT_MS); + for (;;) + { + if (data) + { + sunxi_mmc_pio_xfer(mmc, data, &transferred); + } + + status = sunxi_mmc_read(mmc, SUNXI_MMC_RINTR); + if (status & SUNXI_MMC_ERROR_MASK) + { + err = (status & (SUNXI_MMC_RESP_TIMEOUT | SUNXI_MMC_DATA_TIMEOUT)) ? + -RT_ETIMEOUT : -RT_EIO; + break; + } + if ((!data && (status & SUNXI_MMC_COMMAND_DONE)) || + (data && (status & (SUNXI_MMC_DATA_OVER | + SUNXI_MMC_AUTO_COMMAND_DONE)))) + { + break; + } + if ((rt_int32_t)(rt_tick_get() - timeout) >= 0) + { + err = -RT_ETIMEOUT; + break; + } + rt_hw_cpu_relax(); + } + + if (!err && data && (data->flags & DATA_DIR_READ)) + { + sunxi_mmc_drain_rxfifo(mmc, data, &transferred); + } + if (!err && data && transferred != data->blksize * data->blks) + { + err = -RT_EIO; + } + } + +_complete: + sunxi_mmc_write(mmc, SUNXI_MMC_RINTR, status); + if (!err) + { + if (resp_type(cmd) == RESP_R2) + { + cmd->resp[0] = sunxi_mmc_read(mmc, SUNXI_MMC_RESP3); + cmd->resp[1] = sunxi_mmc_read(mmc, SUNXI_MMC_RESP2); + cmd->resp[2] = sunxi_mmc_read(mmc, SUNXI_MMC_RESP1); + cmd->resp[3] = sunxi_mmc_read(mmc, SUNXI_MMC_RESP0); + } + else if (resp_type(cmd) != RESP_NONE) + { + cmd->resp[0] = sunxi_mmc_read(mmc, SUNXI_MMC_RESP0); + } + if (resp_type(cmd) == RESP_R1B) + { + err = sunxi_mmc_wait_busy(mmc, cmd->busy_timeout ? + cmd->busy_timeout : SUNXI_MMC_TIMEOUT_MS); + } + } + + if (cmd->cmd_code == SD_SWITCH && data && !err) + { + switch_status = (rt_uint8_t *)data->buf; + + if ((cmd->arg & RT_BIT(31)) && (cmd->arg & 0xf) == 1 && + (switch_status[16] & 0xf) != 1) + { + if (++cmd6_hs_attempt < SUNXI_MMC_SD_HS_RETRIES) + { + rt_thread_mdelay(5); + goto _retry_cmd; + } + mmc->sd_hs_rejected = RT_TRUE; + LOG_W("SD card rejected high-speed mode after %u tries; using 25 MHz legacy timing", + cmd6_hs_attempt); + } + } + + cmd->err = err; + if (data) + { + data->err = err; + data->bytes_xfered = err ? 0 : transferred; + } + mmcsd_req_complete(host); +} + +static rt_int32_t sunxi_mmc_get_card_status(struct rt_mmcsd_host *host) +{ + struct sunxi_mmc *mmc = host->private_data; + + if (mmc->cd_pin >= 0) + { + return rt_pin_read(mmc->cd_pin) == mmc->cd_present_level; + } + + return !!(sunxi_mmc_read(mmc, SUNXI_MMC_STAS) & SUNXI_MMC_CARD_PRESENT); +} + +static rt_bool_t sunxi_mmc_sample_card_detect(struct sunxi_mmc *mmc) +{ + return rt_pin_read(mmc->cd_pin) == mmc->cd_present_level; +} + +static void sunxi_mmc_card_detect(void *param); + +static rt_uint8_t sunxi_mmc_card_detect_irq_mode(struct sunxi_mmc *mmc) +{ + rt_uint8_t next_level = mmc->card_present ? + !mmc->cd_present_level : mmc->cd_present_level; + + return next_level ? PIN_IRQ_MODE_RISING : PIN_IRQ_MODE_FALLING; +} + +static void sunxi_mmc_card_detect_disarm(struct sunxi_mmc *mmc) +{ + if (mmc->cd_irq_attached) + { + rt_pin_irq_enable(mmc->cd_pin, PIN_IRQ_DISABLE); + rt_pin_detach_irq(mmc->cd_pin); + mmc->cd_irq_attached = RT_FALSE; + } +} + +static rt_err_t sunxi_mmc_card_detect_arm(struct sunxi_mmc *mmc) +{ + rt_err_t err; + rt_uint8_t mode; + + sunxi_mmc_card_detect_disarm(mmc); + mode = sunxi_mmc_card_detect_irq_mode(mmc); + + err = rt_pin_attach_irq(mmc->cd_pin, mode, sunxi_mmc_card_detect, mmc); + if (err) + { + return err; + } + + err = rt_pin_irq_enable(mmc->cd_pin, PIN_IRQ_ENABLE); + if (err) + { + rt_pin_detach_irq(mmc->cd_pin); + return err; + } + + mmc->cd_irq_attached = RT_TRUE; + + return RT_EOK; +} + +static void sunxi_mmc_card_detect_debounce(void *param) +{ + struct sunxi_mmc *mmc = param; + rt_bool_t present; + rt_err_t err; + + sunxi_mmc_card_detect_disarm(mmc); + rt_pin_mode(mmc->cd_pin, PIN_MODE_INPUT_PULLUP); + present = sunxi_mmc_sample_card_detect(mmc); + mmc->card_present = present; + err = sunxi_mmc_card_detect_arm(mmc); + if (err) + { + LOG_E("cannot arm card-detect interrupt: %d", err); + } + + if (present == mmc->card_reported) + { + return; + } + + mmc->card_reported = present; + if (!present) + { + mmc->sd_hs_rejected = RT_FALSE; + } + + mmcsd_change(mmc->host); +} + +static void sunxi_mmc_card_detect(void *param) +{ + struct sunxi_mmc *mmc = param; + + /* + * PF_DAT is invalid when the pin is multiplexed as an EINT. Use the EINT + * only as a wakeup source and sample the pin after returning to GPIO mode. + */ + rt_pin_irq_enable(mmc->cd_pin, PIN_IRQ_DISABLE); + rt_timer_start(&mmc->cd_timer); +} + +static rt_bool_t sunxi_mmc_card_busy(struct rt_mmcsd_host *host) +{ + struct sunxi_mmc *mmc = host->private_data; + + return !!(sunxi_mmc_read(mmc, SUNXI_MMC_STAS) & SUNXI_MMC_CARD_BUSY); +} + +static const struct rt_mmcsd_host_ops sunxi_mmc_ops = +{ + .request = sunxi_mmc_request, + .set_iocfg = sunxi_mmc_set_iocfg, + .get_card_status = sunxi_mmc_get_card_status, + .card_busy = sunxi_mmc_card_busy, +}; + +static rt_err_t sunxi_mmc_hw_init(struct sunxi_mmc *mmc) +{ + sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, + sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL) | SUNXI_MMC_HW_RESET); + if (sunxi_mmc_wait_clear(mmc, SUNXI_MMC_GCTRL, SUNXI_MMC_HW_RESET, 250)) + { + return -RT_ETIMEOUT; + } + sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, + (sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL) | + SUNXI_MMC_INT_ENABLE | SUNXI_MMC_ACCESS_DONE_DIRECT) & + ~SUNXI_MMC_ACCESS_BY_AHB); + sunxi_mmc_write(mmc, SUNXI_MMC_FTRGL, SUNXI_MMC_PIO_FTRGL); + sunxi_mmc_write(mmc, SUNXI_MMC_TMOUT, 0xffffffff); + sunxi_mmc_write(mmc, SUNXI_MMC_IMASK, 0); + sunxi_mmc_write(mmc, SUNXI_MMC_RINTR, 0xffffffff); + + return RT_EOK; +} + +static void sunxi_mmc_free(struct sunxi_mmc *mmc) +{ + if (mmc->irq_installed) + { + rt_hw_interrupt_mask(mmc->irq); + rt_pic_detach_irq(mmc->irq, mmc); + } + if (mmc->cd_timer_initialized) + { + rt_timer_stop(&mmc->cd_timer); + rt_timer_detach(&mmc->cd_timer); + } + if (mmc->cd_irq_attached) + { + rt_pin_irq_enable(mmc->cd_pin, PIN_IRQ_DISABLE); + rt_pin_detach_irq(mmc->cd_pin); + } + if (mmc->host) + { + mmcsd_free_host(mmc->host); + } + if (mmc->idma_desc) + { + rt_dma_free_coherent(mmc->dev, + SUNXI_MMC_IDMA_DESC_COUNT * sizeof(*mmc->idma_desc), + mmc->idma_desc, mmc->idma_desc_dma); + } + if (!rt_is_err_or_null(mmc->reset)) + { + rt_reset_control_put(mmc->reset); + } + if (!rt_is_err_or_null(mmc->mmc_clk)) + { + rt_clk_disable_unprepare(mmc->mmc_clk); + rt_clk_put(mmc->mmc_clk); + } + if (!rt_is_err_or_null(mmc->bus_clk)) + { + rt_clk_disable_unprepare(mmc->bus_clk); + rt_clk_put(mmc->bus_clk); + } + if (mmc->base) + { + rt_iounmap(mmc->base); + } + rt_free(mmc); +} + +static rt_err_t sunxi_mmc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint8_t unused_mode; + struct rt_device *dev = &pdev->parent; + struct sunxi_mmc *mmc = rt_calloc(1, sizeof(*mmc)); + + if (!mmc) + { + return -RT_ENOMEM; + } + mmc->dev = dev; + mmc->cd_pin = -1; + mmc->irq = -1; + mmc->base = rt_dm_dev_iomap(dev, 0); + if (!mmc->base) + { + err = -RT_EIO; + goto _fail; + } + mmc->bus_clk = rt_clk_get_by_name(dev, "ahb"); + mmc->mmc_clk = rt_clk_get_by_name(dev, "mmc"); + if (rt_is_err(mmc->bus_clk) || rt_is_err(mmc->mmc_clk)) + { + err = rt_is_err(mmc->bus_clk) ? rt_ptr_err(mmc->bus_clk) : + rt_ptr_err(mmc->mmc_clk); + goto _fail; + } + if ((err = rt_clk_prepare_enable(mmc->bus_clk)) || + (err = rt_clk_prepare_enable(mmc->mmc_clk))) + { + goto _fail; + } + mmc->reset = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(mmc->reset)) + { + err = rt_ptr_err(mmc->reset); + goto _fail; + } + if (mmc->reset && (err = rt_reset_control_deassert(mmc->reset))) + { + goto _fail; + } + if ((err = sunxi_mmc_hw_init(mmc))) + { + goto _fail; + } + mmc->host = mmcsd_alloc_host(); + if (!mmc->host) + { + err = -RT_ENOMEM; + goto _fail; + } + mmc->host->ops = &sunxi_mmc_ops; + mmc->host->freq_min = 400000; + mmc->host->freq_max = 50000000; + mmc->host->valid_ocr = VDD_31_32 | VDD_32_33 | VDD_33_34; + mmc->host->private_data = mmc; + mmc->irq = rt_dm_dev_get_irq(dev, 0); + if (mmc->irq >= 0) + { + mmc->idma_desc = rt_dma_alloc_coherent(dev, + SUNXI_MMC_IDMA_DESC_COUNT * sizeof(*mmc->idma_desc), + &mmc->idma_desc_dma); + if (mmc->idma_desc) + { + rt_hw_interrupt_install(mmc->irq, sunxi_mmc_isr, mmc, + "sunxi-mmc"); + rt_hw_interrupt_umask(mmc->irq); + mmc->irq_installed = RT_TRUE; + mmc->host->max_seg_size = SUNXI_MMC_IDMA_SEG_SIZE; + mmc->host->max_dma_segs = SUNXI_MMC_IDMA_DESC_COUNT; + mmc->host->max_blk_size = 512; + mmc->host->max_blk_count = SUNXI_MMC_IDMA_MAX_SIZE / 512; + } + else + { + LOG_W("IDMA descriptor unavailable; using PIO"); + } + } + else + { + LOG_W("MMC interrupt unavailable; using PIO"); + } + mmc->no_sdio = rt_dm_dev_prop_read_bool(dev, "no-sdio"); + if ((err = sdio_ofw_parse(dev->ofw_node, mmc->host))) + { + goto _fail; + } + sdio_host_set_name(mmc->host, RT_NULL); + + mmc->cd_pin = rt_pin_get_named_pin(dev, "cd", 0, &unused_mode, + &mmc->cd_present_level); + if (mmc->cd_pin >= 0) + { + rt_pin_mode(mmc->cd_pin, PIN_MODE_INPUT_PULLUP); + mmc->card_present = sunxi_mmc_sample_card_detect(mmc); + mmc->card_reported = RT_FALSE; + rt_timer_init(&mmc->cd_timer, "sdc-cd", sunxi_mmc_card_detect_debounce, + mmc, rt_tick_from_millisecond(SUNXI_MMC_CD_DEBOUNCE_MS), + RT_TIMER_FLAG_ONE_SHOT); + mmc->cd_timer_initialized = RT_TRUE; + err = sunxi_mmc_card_detect_arm(mmc); + if (err) + { + LOG_W("card-detect interrupt unavailable: %d", err); + } + } + else + { + mmc->cd_pin = -1; + } + + dev->user_data = mmc; + rt_dm_dev_bind_fwdata(dev, RT_NULL, mmc); + if (mmc->cd_timer_initialized) + { + rt_timer_start(&mmc->cd_timer); + } + else + { + mmcsd_change(mmc->host); + } + + return RT_EOK; + +_fail: + sunxi_mmc_free(mmc); + return err; +} + +static rt_err_t sunxi_mmc_remove(struct rt_platform_device *pdev) +{ + struct sunxi_mmc *mmc = pdev->parent.user_data; + + rt_dm_dev_unbind_fwdata(&pdev->parent, RT_NULL); + sunxi_mmc_free(mmc); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_mmc_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-mmc" }, + { .compatible = "allwinner,sun252i-f101-emmc" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_mmc_driver = +{ + .name = "sunxi-mmc", + .ids = sunxi_mmc_ofw_ids, + .probe = sunxi_mmc_probe, + .remove = sunxi_mmc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_mmc_driver); diff --git a/bsp/allwinner/dm/soc/Kconfig b/bsp/allwinner/dm/soc/Kconfig new file mode 100644 index 000000000000..4ee5d9e27eb5 --- /dev/null +++ b/bsp/allwinner/dm/soc/Kconfig @@ -0,0 +1,42 @@ +config RT_SOC_SUNXI_G2D + bool "Allwinner G2D-200 graphics accelerator" + depends on RT_USING_OFW + depends on RT_USING_DMA + depends on RT_USING_CLK + depends on RT_USING_RESET + default y + help + Enable the G2D-200 command-queue transport used by the Allwinner + sun252i F101. The driver owns the hardware resources and provides + DMA buffer management plus synchronous RCQ submission. + +config RT_SOC_SUNXI_JPEG_DEC + bool "Allwinner VE JPEG/MJPEG decoder" + depends on RT_USING_OFW + depends on RT_USING_DMA + depends on RT_USING_CLK + depends on RT_USING_RESET + help + Enable the baseline JPEG and MJPEG decoder in the Allwinner Video + Engine. The jpegd device accepts DMA buffers and produces NV12 frames. + +config RT_SOC_SUNXI_JPEG_ENC + bool "Allwinner VE JPEG/MJPEG encoder" + depends on RT_USING_OFW + depends on RT_USING_DMA + depends on RT_USING_CLK + depends on RT_USING_RESET + help + Enable the baseline JPEG encoder in the Allwinner Video Engine. The + jpege device accepts contiguous NV12 frames and produces JPEG streams. + +config RT_SOC_SUNXI_PNG_DEC + bool "Allwinner VE PNG decoder" + depends on RT_USING_OFW + depends on RT_USING_DMA + depends on RT_USING_CLK + depends on RT_USING_RESET + help + Enable the PNG decoder in the Allwinner Video Engine. The pngd device + accepts complete, non-interlaced 8-bit RGB or RGBA PNG streams and + produces RGBA8888 frames. diff --git a/bsp/allwinner/dm/soc/SConscript b/bsp/allwinner/dm/soc/SConscript new file mode 100644 index 000000000000..752d7f52701b --- /dev/null +++ b/bsp/allwinner/dm/soc/SConscript @@ -0,0 +1,15 @@ +from building import * + +cwd = GetCurrentDir() +src = [] + +if GetDepend(['RT_SOC_SUNXI_G2D']): + src += Glob('g2d/*.c') + +if GetDepend(['RT_SOC_SUNXI_JPEG_DEC']) or GetDepend(['RT_SOC_SUNXI_JPEG_ENC']) or GetDepend(['RT_SOC_SUNXI_PNG_DEC']): + src += ['jpegd-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend=[''], + CPPPATH=[cwd + '/../include', cwd + '/g2d']) + +Return('group') diff --git a/bsp/allwinner/dm/soc/g2d/g2d-sunxi.c b/bsp/allwinner/dm/soc/g2d/g2d-sunxi.c new file mode 100644 index 000000000000..3f2268e47bd1 --- /dev/null +++ b/bsp/allwinner/dm/soc/g2d/g2d-sunxi.c @@ -0,0 +1,849 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define DBG_TAG "g2d.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include +#include "g2d_sunxi_hw.h" + +#define SUNXI_G2D_THREAD0_OFFSET 0x0100 + +#define SUNXI_G2D_RESET 0x0010 +#define SUNXI_G2D_CLK_GATE 0x0014 +#define SUNXI_G2D_MCLK_GATE 0x0018 +#define SUNXI_G2D_MODE_CTL 0x0020 +#define SUNXI_G2D_CORE_STATUS 0x0028 +#define SUNXI_G2D_VERSION 0x0000 +#define SUNXI_G2D_CACHE_CONTROL 0x0050 +#define SUNXI_G2D_CACHE_SIZE0 0x0054 +#define SUNXI_G2D_CACHE_SIZE1 0x0058 +#define SUNXI_G2D_MBUS_LIMIT 0x0064 + +#define SUNXI_G2D_THREAD_IRQ_EN 0x0000 +#define SUNXI_G2D_THREAD_IRQ 0x0004 +#define SUNXI_G2D_THREAD_TASK_ID 0x0008 +#define SUNXI_G2D_THREAD_TIMEOUT_INFO 0x000c +#define SUNXI_G2D_THREAD_RCQ_ADDR_LO 0x0010 +#define SUNXI_G2D_THREAD_RCQ_ADDR_HI_LEN 0x0014 +#define SUNXI_G2D_THREAD_RCQ_ATTR 0x0018 +#define SUNXI_G2D_THREAD_RCQ_UPDATE 0x001c + +#define SUNXI_G2D_CORE_ENABLE RT_BIT(0) +#define SUNXI_G2D_THREAD0_ENABLE RT_BIT(16) +#define SUNXI_G2D_MASTER_MODE RT_BIT(0) +#define SUNXI_G2D_THREAD_DONE_IRQ RT_BIT(0) +#define SUNXI_G2D_THREAD_TIMEOUT_IRQ RT_BIT(8) +#define SUNXI_G2D_THREAD_DONE_IRQ_GEN RT_BIT(16) +#define SUNXI_G2D_RCQ_UPDATE RT_BIT(0) + +#define SUNXI_G2D_MAX_RCQ_HEADERS 256 +#define SUNXI_G2D_DEFAULT_TIMEOUT_MS 100 +#define SUNXI_G2D_COMMAND_BYTES 2560 + +struct sunxi_g2d_buffer_entry +{ + rt_list_t list; + struct sunxi_g2d_buffer buffer; + rt_bool_t allocated; +}; + +struct sunxi_g2d +{ + struct rt_device parent; + struct rt_device *dev; + struct rt_clk *mod_clk; + struct rt_clk *bus_clk; + struct rt_clk *mbus_clk; + struct rt_reset_control *resets; + void *base; + int irq; + + struct rt_mutex lock; + struct rt_completion completion; + rt_list_t buffers; + struct sunxi_g2d_buffer command_buffer; + rt_uint32_t version; + rt_bool_t mod_clock_enabled; + rt_bool_t bus_clock_enabled; + rt_bool_t mbus_clock_enabled; + rt_bool_t lock_initialized; + rt_bool_t device_registered; + rt_bool_t irq_installed; + rt_bool_t running; + rt_uint32_t completion_status; +}; + +#define raw_to_sunxi_g2d(raw) \ + rt_container_of(raw, struct sunxi_g2d, parent) + +static rt_uint32_t sunxi_g2d_readl(struct sunxi_g2d *g2d, + rt_uint32_t offset) +{ + return HWREG32(g2d->base + offset); +} + +static void sunxi_g2d_writel(struct sunxi_g2d *g2d, rt_uint32_t offset, + rt_uint32_t value) +{ + HWREG32(g2d->base + offset) = value; +} + +static void sunxi_g2d_set_bits(struct sunxi_g2d *g2d, rt_uint32_t offset, + rt_uint32_t bits) +{ + sunxi_g2d_writel(g2d, offset, sunxi_g2d_readl(g2d, offset) | bits); +} + +static void sunxi_g2d_hw_reset(struct sunxi_g2d *g2d) +{ + sunxi_g2d_set_bits(g2d, SUNXI_G2D_CLK_GATE, + SUNXI_G2D_CORE_ENABLE); + sunxi_g2d_set_bits(g2d, SUNXI_G2D_MCLK_GATE, + RT_BIT(0) | RT_BIT(16)); + sunxi_g2d_set_bits(g2d, SUNXI_G2D_RESET, + SUNXI_G2D_CORE_ENABLE); + sunxi_g2d_set_bits(g2d, SUNXI_G2D_CLK_GATE, + SUNXI_G2D_THREAD0_ENABLE); + sunxi_g2d_set_bits(g2d, SUNXI_G2D_RESET, + SUNXI_G2D_THREAD0_ENABLE); +} + +static void sunxi_g2d_configure_memory(struct sunxi_g2d *g2d) +{ + rt_uint32_t value; + + sunxi_g2d_set_bits(g2d, SUNXI_G2D_CACHE_CONTROL, RT_BIT(16)); + + value = sunxi_g2d_readl(g2d, SUNXI_G2D_CACHE_SIZE0); + sunxi_g2d_writel(g2d, SUNXI_G2D_CACHE_SIZE0, + (value & ~0xffU) | 0x40U); + value = sunxi_g2d_readl(g2d, SUNXI_G2D_CACHE_SIZE1); + sunxi_g2d_writel(g2d, SUNXI_G2D_CACHE_SIZE1, + (value & ~0xffU) | 0x40U); + + value = sunxi_g2d_readl(g2d, SUNXI_G2D_MBUS_LIMIT); + value &= ~(0x1ffU | RT_BIT(16)); + sunxi_g2d_writel(g2d, SUNXI_G2D_MBUS_LIMIT, + value | 0x90U | RT_BIT(16)); +} + +static struct sunxi_g2d_buffer_entry *sunxi_g2d_find_buffer( + struct sunxi_g2d *g2d, const struct sunxi_g2d_buffer *buffer) +{ + struct sunxi_g2d_buffer_entry *entry; + + if (!buffer) + { + return RT_NULL; + } + + rt_list_for_each_entry(entry, &g2d->buffers, list) + { + if (entry->buffer.virt_addr == buffer->virt_addr && + entry->buffer.phys_addr == buffer->phys_addr && + entry->buffer.size == buffer->size) + { + return entry; + } + } + + return RT_NULL; +} + +/* + * F101 currently runs without an MMU, where the nominally coherent DMA-pool + * mapping is still cacheable. Keep G2D's command and image buffers coherent + * at the hardware boundary just as the native DMA driver does. + */ +static void sunxi_g2d_sync_for_device(struct sunxi_g2d *g2d) +{ +#ifndef ARCH_MM_MMU + struct sunxi_g2d_buffer_entry *entry; + + rt_list_for_each_entry(entry, &g2d->buffers, list) + { + rt_hw_cpu_dcache_clean_and_invalidate(entry->buffer.virt_addr, + entry->buffer.size); + } + rt_hw_cpu_dcache_clean_and_invalidate(g2d->command_buffer.virt_addr, + g2d->command_buffer.size); +#else + RT_UNUSED(g2d); +#endif +} + +static void sunxi_g2d_sync_for_cpu(struct sunxi_g2d *g2d) +{ +#ifndef ARCH_MM_MMU + struct sunxi_g2d_buffer_entry *entry; + + rt_list_for_each_entry(entry, &g2d->buffers, list) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, entry->buffer.virt_addr, + entry->buffer.size); + } +#else + RT_UNUSED(g2d); +#endif +} + +static rt_err_t sunxi_g2d_alloc_buffer(struct sunxi_g2d *g2d, + struct sunxi_g2d_buffer *buffer) +{ + struct sunxi_g2d_buffer_entry *entry; + rt_ubase_t physical; + + if (!buffer || !buffer->size) + { + return -RT_EINVAL; + } + + entry = rt_calloc(1, sizeof(*entry)); + if (!entry) + { + return -RT_ENOMEM; + } + + buffer->virt_addr = rt_dma_alloc_coherent(g2d->dev, buffer->size, + &physical); + if (!buffer->virt_addr) + { + rt_free(entry); + return -RT_ENOMEM; + } + + buffer->phys_addr = physical; + entry->buffer = *buffer; + entry->allocated = RT_TRUE; + rt_list_insert_before(&g2d->buffers, &entry->list); + + return RT_EOK; +} + +static rt_err_t sunxi_g2d_free_buffer(struct sunxi_g2d *g2d, + struct sunxi_g2d_buffer *buffer) +{ + struct sunxi_g2d_buffer_entry *entry; + + entry = sunxi_g2d_find_buffer(g2d, buffer); + if (!entry) + { + return -RT_EINVAL; + } + + rt_list_remove(&entry->list); + if (entry->allocated) + { + rt_dma_free_coherent(g2d->dev, entry->buffer.size, + entry->buffer.virt_addr, entry->buffer.phys_addr); + } + rt_memset(buffer, 0, sizeof(*buffer)); + rt_free(entry); + + return RT_EOK; +} + +static rt_err_t sunxi_g2d_import_buffer(struct sunxi_g2d *g2d, + struct sunxi_g2d_buffer *buffer) +{ + struct sunxi_g2d_buffer_entry *entry; + + if (!buffer || !buffer->virt_addr || !buffer->size || + sunxi_g2d_find_buffer(g2d, buffer)) + { + return -RT_EINVAL; + } + + if (!buffer->phys_addr) + { + buffer->phys_addr = (rt_ubase_t)rt_kmem_v2p(buffer->virt_addr); + } + entry = rt_calloc(1, sizeof(*entry)); + if (!entry) + { + return -RT_ENOMEM; + } + entry->buffer = *buffer; + rt_list_insert_before(&g2d->buffers, &entry->list); + return RT_EOK; +} + +static rt_err_t sunxi_g2d_submit_rcq(struct sunxi_g2d *g2d, + const struct sunxi_g2d_rcq_submit *submit); + +static rt_err_t sunxi_g2d_surface_to_backend(struct sunxi_g2d *g2d, + const struct sunxi_g2d_surface *surface, + struct g2d_surface *backend) +{ + rt_uint32_t plane; + + if (!surface || !backend || + (rt_uint32_t)surface->format >= SUNXI_G2D_FORMAT_MAX) + { + return -RT_EINVAL; + } + + rt_memset(backend, 0, sizeof(*backend)); + backend->format = (enum g2d_format)surface->format; + backend->width = surface->width; + backend->height = surface->height; + + for (plane = 0; plane < RT_ARRAY_SIZE(surface->plane); ++plane) + { + const struct sunxi_g2d_plane *input = &surface->plane[plane]; + struct sunxi_g2d_buffer_entry *entry; + rt_ubase_t address; + + if (!input->buffer) + { + continue; + } + entry = sunxi_g2d_find_buffer(g2d, input->buffer); + if (!entry || input->offset >= entry->buffer.size) + { + return -RT_EINVAL; + } + address = entry->buffer.phys_addr + input->offset; + if ((rt_uint64_t)address >> 32) + { + return -RT_EINVAL; + } + backend->plane[plane] = (void *)address; + backend->pitch[plane] = input->pitch; + } + + return RT_EOK; +} + +static void sunxi_g2d_image_to_surface(const struct sunxi_g2d_image *image, + struct sunxi_g2d_surface *surface) +{ + rt_memset(surface, 0, sizeof(*surface)); + surface->format = image->format; + surface->width = image->width; + surface->height = image->height; + surface->plane[0].buffer = image->buffer; + surface->plane[0].pitch = image->pitch; +} + +static rt_err_t sunxi_g2d_compose(struct sunxi_g2d *g2d, + const struct sunxi_g2d_operation *operation) +{ + struct sunxi_g2d_buffer *command_buffer = &g2d->command_buffer; + struct g2d_cmdlist command_list; + struct g2d_op backend; + struct sunxi_g2d_rcq_submit submit; + rt_size_t header_size; + int ret; + + if (!operation || (rt_uint32_t)operation->type > + SUNXI_G2D_OPERATION_BLEND) + { + return -RT_EINVAL; + } + + rt_memset(&backend, 0, sizeof(backend)); + backend.type = (enum g2d_op_type)operation->type; + backend.flags = operation->flags; + backend.color = operation->color; + backend.rotation = (enum g2d_rotation)operation->rotation; + backend.dst_rect.x = operation->destination_rect.x; + backend.dst_rect.y = operation->destination_rect.y; + backend.dst_rect.width = operation->destination_rect.width; + backend.dst_rect.height = operation->destination_rect.height; + backend.src_rect.x = operation->source_rect.x; + backend.src_rect.y = operation->source_rect.y; + backend.src_rect.width = operation->source_rect.width; + backend.src_rect.height = operation->source_rect.height; + backend.bg_rect.x = operation->background_rect.x; + backend.bg_rect.y = operation->background_rect.y; + backend.bg_rect.width = operation->background_rect.width; + backend.bg_rect.height = operation->background_rect.height; + backend.blend.mode = (enum g2d_blend_mode)operation->blend.mode; + backend.blend.fg_alpha_mode = + (enum g2d_alpha_mode)operation->blend.foreground_alpha_mode; + backend.blend.fg_alpha = operation->blend.foreground_alpha; + backend.blend.bg_alpha_mode = + (enum g2d_alpha_mode)operation->blend.background_alpha_mode; + backend.blend.bg_alpha = operation->blend.background_alpha; + backend.blend.color_key = operation->blend.color_key; + backend.blend.color_key_min = operation->blend.color_key_min; + backend.blend.color_key_max = operation->blend.color_key_max; + + ret = sunxi_g2d_surface_to_backend(g2d, &operation->destination, + &backend.dst); + if (!ret && operation->type != SUNXI_G2D_OPERATION_FILL) + { + ret = sunxi_g2d_surface_to_backend(g2d, &operation->source, + &backend.src); + } + if (!ret && operation->type == SUNXI_G2D_OPERATION_BLEND) + { + ret = sunxi_g2d_surface_to_backend(g2d, &operation->background, + &backend.bg); + } + if (ret) + { + return ret; + } + + g2d_cmdlist_init(&command_list, command_buffer->virt_addr, + command_buffer->size); + ret = g2d_compose(&command_list, &backend); + if (ret) + { + return ret; + } + header_size = g2d_cmdlist_finish(&command_list); + if (!header_size) + { + return -RT_EINVAL; + } + + submit.buffer = command_buffer; + submit.header_offset = 0; + submit.header_count = header_size / + sizeof(struct sunxi_g2d_rcq_header); + submit.timeout = operation->timeout; + return sunxi_g2d_submit_rcq(g2d, &submit); +} + +static rt_err_t sunxi_g2d_fill(struct sunxi_g2d *g2d, + const struct sunxi_g2d_fill *fill) +{ + struct sunxi_g2d_operation operation; + + if (!fill || !fill->destination) + { + return -RT_EINVAL; + } + rt_memset(&operation, 0, sizeof(operation)); + operation.type = SUNXI_G2D_OPERATION_FILL; + operation.color = fill->color; + operation.destination_rect = fill->rect; + operation.timeout = fill->timeout; + sunxi_g2d_image_to_surface(fill->destination, &operation.destination); + return sunxi_g2d_compose(g2d, &operation); +} + +static rt_err_t sunxi_g2d_blit(struct sunxi_g2d *g2d, + const struct sunxi_g2d_blit *blit) +{ + struct sunxi_g2d_operation operation; + + if (!blit || !blit->source || !blit->destination) + { + return -RT_EINVAL; + } + rt_memset(&operation, 0, sizeof(operation)); + operation.type = SUNXI_G2D_OPERATION_BLIT; + operation.source_rect = blit->source_rect; + operation.destination_rect = blit->destination_rect; + operation.timeout = blit->timeout; + sunxi_g2d_image_to_surface(blit->source, &operation.source); + sunxi_g2d_image_to_surface(blit->destination, &operation.destination); + return sunxi_g2d_compose(g2d, &operation); +} + +static rt_err_t sunxi_g2d_submit_rcq(struct sunxi_g2d *g2d, + const struct sunxi_g2d_rcq_submit *submit) +{ + struct sunxi_g2d_buffer_entry *entry; + struct sunxi_g2d_buffer *buffer; + rt_size_t header_bytes; + rt_tick_t timeout; + rt_uint64_t header_address; + rt_uint32_t thread_base = SUNXI_G2D_THREAD0_OFFSET; + rt_err_t err; + + if (!submit || !submit->buffer || !submit->header_count || + submit->header_count > SUNXI_G2D_MAX_RCQ_HEADERS || + (submit->header_offset & 31)) + { + return -RT_EINVAL; + } + + if (submit->buffer == &g2d->command_buffer) + { + buffer = &g2d->command_buffer; + } + else + { + entry = sunxi_g2d_find_buffer(g2d, submit->buffer); + buffer = entry ? &entry->buffer : RT_NULL; + } + header_bytes = (rt_size_t)submit->header_count * + sizeof(struct sunxi_g2d_rcq_header); + if (!buffer || submit->header_offset > buffer->size || + header_bytes > buffer->size - submit->header_offset) + { + return -RT_EINVAL; + } + header_address = (rt_uint64_t)buffer->phys_addr + + submit->header_offset; + if (header_address >> 40) + { + return -RT_EINVAL; + } + + timeout = submit->timeout ? submit->timeout : + rt_tick_from_millisecond(SUNXI_G2D_DEFAULT_TIMEOUT_MS); + g2d->running = RT_TRUE; + g2d->completion_status = 0; + rt_completion_init(&g2d->completion); + + /* Enter master mode for this RCQ submission, as required by G2D-200. */ + sunxi_g2d_set_bits(g2d, SUNXI_G2D_MODE_CTL, SUNXI_G2D_MASTER_MODE); + sunxi_g2d_writel(g2d, thread_base + SUNXI_G2D_THREAD_IRQ, + SUNXI_G2D_THREAD_DONE_IRQ | SUNXI_G2D_THREAD_TIMEOUT_IRQ); + sunxi_g2d_writel(g2d, thread_base + SUNXI_G2D_THREAD_IRQ_EN, + SUNXI_G2D_THREAD_DONE_IRQ | SUNXI_G2D_THREAD_TIMEOUT_IRQ); + sunxi_g2d_writel(g2d, thread_base + SUNXI_G2D_THREAD_RCQ_ADDR_LO, + (rt_uint32_t)header_address); + sunxi_g2d_writel(g2d, thread_base + SUNXI_G2D_THREAD_RCQ_ADDR_HI_LEN, + ((rt_uint32_t)(header_address >> 32)) | + ((rt_uint32_t)header_bytes << 16)); + sunxi_g2d_writel(g2d, thread_base + SUNXI_G2D_THREAD_RCQ_ATTR, + SUNXI_G2D_THREAD_DONE_IRQ_GEN); + sunxi_g2d_sync_for_device(g2d); + rt_hw_dsb(); + + sunxi_g2d_writel(g2d, thread_base + SUNXI_G2D_THREAD_RCQ_UPDATE, + SUNXI_G2D_RCQ_UPDATE); + + err = rt_completion_wait(&g2d->completion, timeout); + g2d->running = RT_FALSE; + if (err) + { + LOG_E("G2D command timed out: irq=%08x task=%08x timeout=%08x core=%08x", + sunxi_g2d_readl(g2d, thread_base + SUNXI_G2D_THREAD_IRQ), + sunxi_g2d_readl(g2d, thread_base + SUNXI_G2D_THREAD_TASK_ID), + sunxi_g2d_readl(g2d, + thread_base + SUNXI_G2D_THREAD_TIMEOUT_INFO), + sunxi_g2d_readl(g2d, SUNXI_G2D_CORE_STATUS)); + sunxi_g2d_hw_reset(g2d); + return -RT_ETIMEOUT; + } + + sunxi_g2d_sync_for_cpu(g2d); + return RT_EOK; +} + +static rt_err_t sunxi_g2d_control(rt_device_t dev, int cmd, void *args) +{ + struct sunxi_g2d *g2d = raw_to_sunxi_g2d(dev); + rt_err_t err; + + rt_mutex_take(&g2d->lock, RT_WAITING_FOREVER); + + switch (cmd) + { + case SUNXI_G2D_CMD_ALLOC_BUFFER: + err = sunxi_g2d_alloc_buffer(g2d, args); + break; + case SUNXI_G2D_CMD_FREE_BUFFER: + err = g2d->running ? -RT_EBUSY : sunxi_g2d_free_buffer(g2d, args); + break; + case SUNXI_G2D_CMD_SUBMIT_RCQ: + err = sunxi_g2d_submit_rcq(g2d, args); + break; + case SUNXI_G2D_CMD_GET_VERSION: + if (!args) + { + err = -RT_EINVAL; + } + else + { + *(rt_uint32_t *)args = g2d->version; + err = RT_EOK; + } + break; + case SUNXI_G2D_CMD_FILL: + err = g2d->running ? -RT_EBUSY : sunxi_g2d_fill(g2d, args); + break; + case SUNXI_G2D_CMD_BLIT: + err = g2d->running ? -RT_EBUSY : sunxi_g2d_blit(g2d, args); + break; + case SUNXI_G2D_CMD_COMPOSE: + err = g2d->running ? -RT_EBUSY : sunxi_g2d_compose(g2d, args); + break; + case SUNXI_G2D_CMD_IMPORT_BUFFER: + err = g2d->running ? -RT_EBUSY : sunxi_g2d_import_buffer(g2d, args); + break; + default: + err = -RT_ENOSYS; + break; + } + + rt_mutex_release(&g2d->lock); + return err; +} + +#ifdef RT_USING_DEVICE_OPS +static const struct rt_device_ops sunxi_g2d_ops = +{ + .control = sunxi_g2d_control, +}; +#endif + +static void sunxi_g2d_isr(int irqno, void *param) +{ + struct sunxi_g2d *g2d = param; + rt_uint32_t status; + + RT_UNUSED(irqno); + + status = sunxi_g2d_readl(g2d, SUNXI_G2D_THREAD0_OFFSET + + SUNXI_G2D_THREAD_IRQ); + if (!(status & (SUNXI_G2D_THREAD_DONE_IRQ | + SUNXI_G2D_THREAD_TIMEOUT_IRQ))) + { + return; + } + + sunxi_g2d_writel(g2d, SUNXI_G2D_THREAD0_OFFSET + + SUNXI_G2D_THREAD_IRQ, status); + g2d->completion_status = status; + rt_completion_done(&g2d->completion); +} + +static void sunxi_g2d_release(struct sunxi_g2d *g2d) +{ + struct sunxi_g2d_buffer_entry *entry; + struct sunxi_g2d_buffer_entry *next; + + rt_list_for_each_entry_safe(entry, next, &g2d->buffers, list) + { + rt_list_remove(&entry->list); + if (entry->allocated) + { + rt_dma_free_coherent(g2d->dev, entry->buffer.size, + entry->buffer.virt_addr, entry->buffer.phys_addr); + } + rt_free(entry); + } + if (g2d->device_registered) + { + rt_device_unregister(&g2d->parent); + } + if (g2d->lock_initialized) + { + rt_mutex_detach(&g2d->lock); + } + if (!rt_is_err_or_null(g2d->resets)) + { + rt_reset_control_assert(g2d->resets); + rt_reset_control_put(g2d->resets); + } + if (!rt_is_err_or_null(g2d->mod_clk)) + { + if (g2d->mod_clock_enabled) + { + rt_clk_disable_unprepare(g2d->mod_clk); + } + rt_clk_put(g2d->mod_clk); + } + if (!rt_is_err_or_null(g2d->mbus_clk)) + { + if (g2d->mbus_clock_enabled) + { + rt_clk_disable_unprepare(g2d->mbus_clk); + } + rt_clk_put(g2d->mbus_clk); + } + if (!rt_is_err_or_null(g2d->bus_clk)) + { + if (g2d->bus_clock_enabled) + { + rt_clk_disable_unprepare(g2d->bus_clk); + } + rt_clk_put(g2d->bus_clk); + } + if (g2d->base) + { + rt_iounmap(g2d->base); + } + if (g2d->command_buffer.virt_addr) + { + rt_free_align(g2d->command_buffer.virt_addr); + } + rt_free(g2d); +} + +static rt_err_t sunxi_g2d_probe(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct sunxi_g2d *g2d; + const char *name; + rt_uint32_t clock_rate = 300000000U; + rt_err_t err; + + g2d = rt_calloc(1, sizeof(*g2d)); + if (!g2d) + { + return -RT_ENOMEM; + } + + g2d->dev = dev; + g2d->irq = -1; + rt_list_init(&g2d->buffers); + g2d->command_buffer.size = SUNXI_G2D_COMMAND_BYTES; + g2d->command_buffer.virt_addr = rt_malloc_align( + g2d->command_buffer.size, 64); + if (!g2d->command_buffer.virt_addr) + { + err = -RT_ENOMEM; + goto _fail; + } + g2d->command_buffer.phys_addr = (rt_ubase_t)rt_kmem_v2p( + g2d->command_buffer.virt_addr); + rt_memset(g2d->command_buffer.virt_addr, 0, + g2d->command_buffer.size); + + g2d->base = rt_dm_dev_iomap(dev, 0); + if (!g2d->base) + { + err = -RT_EIO; + goto _fail; + } + + g2d->mod_clk = rt_clk_get_by_name(dev, "mod"); + if (rt_is_err_or_null(g2d->mod_clk)) + { + err = rt_is_err(g2d->mod_clk) ? rt_ptr_err(g2d->mod_clk) : -RT_ENOENT; + goto _fail; + } + g2d->bus_clk = rt_clk_get_by_name(dev, "bus"); + if (rt_is_err_or_null(g2d->bus_clk)) + { + err = rt_is_err(g2d->bus_clk) ? rt_ptr_err(g2d->bus_clk) : -RT_ENOENT; + goto _fail; + } + g2d->mbus_clk = rt_clk_get_by_name(dev, "mbus"); + if (rt_is_err_or_null(g2d->mbus_clk)) + { + err = rt_is_err(g2d->mbus_clk) ? rt_ptr_err(g2d->mbus_clk) : -RT_ENOENT; + goto _fail; + } + + g2d->resets = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(g2d->resets)) + { + err = rt_ptr_err(g2d->resets); + goto _fail; + } + if ((err = rt_reset_control_assert(g2d->resets)) != RT_EOK) + { + goto _fail; + } + if ((err = rt_clk_prepare_enable(g2d->bus_clk)) != RT_EOK) + { + goto _fail; + } + g2d->bus_clock_enabled = RT_TRUE; + if ((err = rt_clk_prepare_enable(g2d->mbus_clk)) != RT_EOK) + { + goto _fail; + } + g2d->mbus_clock_enabled = RT_TRUE; + if ((err = rt_reset_control_deassert(g2d->resets)) != RT_EOK) + { + goto _fail; + } + rt_dm_dev_prop_read_u32(dev, "clock-frequency", &clock_rate); + if ((err = rt_clk_set_rate(g2d->mod_clk, clock_rate)) != RT_EOK) + { + goto _fail; + } + if ((err = rt_clk_prepare_enable(g2d->mod_clk)) != RT_EOK) + { + goto _fail; + } + g2d->mod_clock_enabled = RT_TRUE; + + g2d->irq = rt_dm_dev_get_irq(dev, 0); + if (g2d->irq < 0) + { + err = g2d->irq; + goto _fail; + } + + sunxi_g2d_hw_reset(g2d); + sunxi_g2d_configure_memory(g2d); + g2d->version = sunxi_g2d_readl(g2d, SUNXI_G2D_VERSION); + rt_dm_dev_set_name_auto(&g2d->parent, "g2d"); + name = rt_dm_dev_get_name(&g2d->parent); + g2d->parent.type = RT_Device_Class_Miscellaneous; +#ifdef RT_USING_DEVICE_OPS + g2d->parent.ops = &sunxi_g2d_ops; +#else + g2d->parent.control = sunxi_g2d_control; +#endif + + if ((err = rt_mutex_init(&g2d->lock, name, RT_IPC_FLAG_PRIO)) != RT_EOK) + { + goto _fail; + } + g2d->lock_initialized = RT_TRUE; + rt_completion_init(&g2d->completion); + + if ((err = rt_device_register(&g2d->parent, name, + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE)) != RT_EOK) + { + goto _fail; + } + g2d->device_registered = RT_TRUE; + + rt_hw_interrupt_install(g2d->irq, sunxi_g2d_isr, g2d, name); + rt_hw_interrupt_umask(g2d->irq); + g2d->irq_installed = RT_TRUE; + dev->user_data = g2d; + + LOG_D("G2D-200 registered as %s, version %08x, clock %u Hz", name, + g2d->version, (rt_uint32_t)rt_clk_get_rate(g2d->mod_clk)); + return RT_EOK; + +_fail: + sunxi_g2d_release(g2d); + return err; +} + +static rt_err_t sunxi_g2d_remove(struct rt_platform_device *pdev) +{ + struct sunxi_g2d *g2d = pdev->parent.user_data; + + if (g2d->irq_installed) + { + rt_hw_interrupt_mask(g2d->irq); + rt_pic_detach_irq(g2d->irq, g2d); + } + sunxi_g2d_release(g2d); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_g2d_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-g2d200" }, + { .compatible = "allwinner,sunxi-g2d200" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_g2d_driver = +{ + .name = "sunxi-g2d", + .ids = sunxi_g2d_ofw_ids, + .probe = sunxi_g2d_probe, + .remove = sunxi_g2d_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_g2d_driver); diff --git a/bsp/allwinner/dm/soc/g2d/g2d.h b/bsp/allwinner/dm/soc/g2d/g2d.h new file mode 100644 index 000000000000..16818b86e568 --- /dev/null +++ b/bsp/allwinner/dm/soc/g2d/g2d.h @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Public API of 2D graphics accelerators (G2D) + * + * The G2D moves and combines rectangles of pixels between memory + * buffers without the CPU: solid fills, copies with pixel format + * conversion, scaling, rotation and flipping, and alpha blending of two + * surfaces. Operations are queued, run asynchronously and complete in + * submission order. + * + * The caller owns the buffers. The driver cleans the data cache of the + * source buffers before an operation and invalidates the destination + * afterwards, unless @ref G2D_FLAG_NO_CACHE_OPS is set. + */ + +#ifndef __G2D_H__ +#define __G2D_H__ + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief G2D interface + * @defgroup g2d_interface G2D interface + * @ingroup io_interfaces + * @{ + */ + +/** Pixel formats. Names list the components from the MSB, as stored in a LE word. */ +enum g2d_format { + G2D_PIXFMT_ARGB8888, + G2D_PIXFMT_ABGR8888, + G2D_PIXFMT_RGBA8888, + G2D_PIXFMT_BGRA8888, + G2D_PIXFMT_XRGB8888, + G2D_PIXFMT_XBGR8888, + G2D_PIXFMT_RGB888, + G2D_PIXFMT_BGR888, + G2D_PIXFMT_RGB565, + G2D_PIXFMT_BGR565, + G2D_PIXFMT_ARGB4444, + G2D_PIXFMT_ARGB1555, + /** YUV 4:2:0, plane 0 Y, plane 1 interleaved UV. Source only. */ + G2D_PIXFMT_NV12, + /** YUV 4:2:0, plane 0 Y, plane 1 interleaved VU. Source only. */ + G2D_PIXFMT_NV21, + /** YUV 4:2:0, three planes Y, U, V. Source only. */ + G2D_PIXFMT_I420, + /** YUV 4:2:2 packed Y0 U Y1 V. Source only. */ + G2D_PIXFMT_YUYV, + G2D_PIXFMT_MAX, +}; + +/** Rotation, applied before the flips of @ref G2D_FLIP_H / @ref G2D_FLIP_V. */ +enum g2d_rotation { + G2D_ROTATE_0, + G2D_ROTATE_90, + G2D_ROTATE_180, + G2D_ROTATE_270, +}; + +/** Porter-Duff composition. "Source" is the foreground, "destination" the background. */ +enum g2d_blend_mode { + G2D_BLEND_CLEAR, + G2D_BLEND_SRC, + G2D_BLEND_DST, + G2D_BLEND_SRC_OVER, + G2D_BLEND_DST_OVER, + G2D_BLEND_SRC_IN, + G2D_BLEND_DST_IN, + G2D_BLEND_SRC_OUT, + G2D_BLEND_DST_OUT, + G2D_BLEND_SRC_ATOP, + G2D_BLEND_DST_ATOP, + G2D_BLEND_XOR, +}; + +/** How the alpha of a layer is taken. */ +enum g2d_alpha_mode { + /** use the alpha channel of the pixels */ + G2D_ALPHA_PIXEL, + /** use the layer alpha only */ + G2D_ALPHA_GLOBAL, + /** pixel alpha multiplied by the layer alpha */ + G2D_ALPHA_MIXED, +}; + +/** @name Operation flags + * @{ + */ +/** Flip horizontally (after the rotation) */ +#define G2D_FLIP_H RT_BIT(0) +/** Flip vertically (after the rotation) */ +#define G2D_FLIP_V RT_BIT(1) +/** The pixels of the source are premultiplied by their alpha */ +#define G2D_FLAG_SRC_PREMULTIPLIED RT_BIT(2) +/** The pixels of the destination are premultiplied by their alpha */ +#define G2D_FLAG_DST_PREMULTIPLIED RT_BIT(3) +/** Do not clean/invalidate the data cache around the operation */ +#define G2D_FLAG_NO_CACHE_OPS RT_BIT(4) +/** YCbCr sources use the full 0..255 range (default: limited, 16..235) */ +#define G2D_FLAG_YUV_FULL_RANGE RT_BIT(5) +/** YCbCr sources use the BT.709 matrix (default: BT.601) */ +#define G2D_FLAG_YUV_BT709 RT_BIT(6) +/** @} */ + +/** Rectangle in pixels */ +struct g2d_rect { + uint16_t x; + uint16_t y; + uint16_t width; + uint16_t height; +}; + +/** A memory buffer holding pixels */ +struct g2d_surface { + enum g2d_format format; + /** size of the buffer in pixels */ + uint16_t width; + uint16_t height; + /** plane base addresses; planar and semi-planar YUV use more than one */ + void *plane[3]; + /** line pitch of each plane in bytes (0: tightly packed) */ + uint32_t pitch[3]; +}; + +struct g2d_op; + +/** + * @brief Completion callback + * + * Called from the driver thread once the hardware finished the operation or + * it failed. Do not block in it. + * + * @param op The operation that completed + * @param status 0 on success, a negative errno otherwise + * @param user_data @ref g2d_op::user_data + */ +typedef void (*g2d_callback_t)(const struct g2d_op *op, int status, void *user_data); + +/** Operation types */ +enum g2d_op_type { + /** fill @ref g2d_op::dst_rect with @ref g2d_op::color */ + G2D_OP_FILL, + /** + * copy @ref g2d_op::src_rect of src to dst_rect of dst; converts the pixel format, + * scales when the rectangle sizes differ, and rotates/flips + */ + G2D_OP_BLIT, + /** + * compose src_rect of src (foreground, scaled to the size of dst_rect when + * they differ) with bg_rect of bg (background, same size as dst_rect) and + * write the result to dst_rect of dst; dst may be bg + */ + G2D_OP_BLEND, +}; + +/** Blend parameters of @ref G2D_OP_BLEND */ +struct g2d_blend { + enum g2d_blend_mode mode; + enum g2d_alpha_mode fg_alpha_mode; + /** foreground layer alpha (0..255), used by the global and mixed modes */ + uint8_t fg_alpha; + enum g2d_alpha_mode bg_alpha_mode; + uint8_t bg_alpha; + /** compare the destination (bg) pixels with the colour key range, in ARGB8888 */ + bool color_key; + uint32_t color_key_min; + uint32_t color_key_max; +}; + +/** One queued operation */ +struct g2d_op { + enum g2d_op_type type; + uint32_t flags; + /** @ref G2D_OP_FILL colour in ARGB8888 */ + uint32_t color; + struct g2d_surface dst; + struct g2d_rect dst_rect; + /** @ref G2D_OP_BLIT and @ref G2D_OP_BLEND source (foreground) */ + struct g2d_surface src; + struct g2d_rect src_rect; + /** @ref G2D_OP_BLIT: rotation and flips */ + enum g2d_rotation rotation; + /** @ref G2D_OP_BLEND background, same size as dst_rect */ + struct g2d_surface bg; + struct g2d_rect bg_rect; + struct g2d_blend blend; + /** optional, called when the operation is done */ + g2d_callback_t callback; + void *user_data; +}; + +/** Capabilities of a G2D device */ +struct g2d_capabilities { + /** bit mask of RT_BIT(enum g2d_format) the device reads */ + uint32_t src_formats; + /** bit mask of RT_BIT(enum g2d_format) the device writes */ + uint32_t dst_formats; + /** bit mask of RT_BIT(enum g2d_op_type) */ + uint32_t ops; + uint16_t max_width; + uint16_t max_height; + /** the rotate engine needs this alignment of the plane addresses and pitches */ + uint8_t rotate_addr_align; + uint8_t rotate_pitch_align; + /** operations that can wait in the queue */ + uint8_t queue_depth; +}; + +/** @brief Queue an operation (copied); -EINVAL unsupported, -ENOMEM queue full */ +int g2d_submit(const struct g2d_op *op); + +/** @brief Get the capabilities of the device */ +int g2d_get_capabilities(struct g2d_capabilities *caps); + +/** @brief Whether the G2D is initialised */ +rt_bool_t g2d_ready(void); + +/** + * @brief Run an operation and wait for it + * + * Wraps @ref g2d_submit. Any callback of @p op is called as well. + * + * @param timeout ticks to wait for the operation to complete (RT_WAITING_FOREVER: no limit) + * @retval 0 done + * @retval -EAGAIN not done in time (the operation may still run) + * @retval negative errno from the submission or from the hardware + */ +int g2d_run(const struct g2d_op *op, rt_int32_t timeout); + +/** @brief Fill a rectangle with an ARGB8888 colour, synchronously */ +int g2d_fill(const struct g2d_surface *dst, + const struct g2d_rect *dst_rect, uint32_t color); + +/** + * @brief Copy, convert, scale and rotate a rectangle, synchronously + * + * The rectangle sizes of @p src_rect and @p dst_rect select the scaling; for a + * 90/270 degree rotation the destination size is the rotated source size. + */ +int g2d_blit(const struct g2d_surface *src, + const struct g2d_rect *src_rect, const struct g2d_surface *dst, + const struct g2d_rect *dst_rect, enum g2d_rotation rotation, uint32_t flags); + +/** @brief Compose @p fg over @p bg into @p dst, synchronously */ +int g2d_blend(const struct g2d_surface *fg, + const struct g2d_rect *fg_rect, const struct g2d_surface *bg, + const struct g2d_rect *bg_rect, const struct g2d_surface *dst, + const struct g2d_rect *dst_rect, const struct g2d_blend *blend, uint32_t flags); + +/** @brief Hardware time of the last operation in 1/100 us: command list start to end interrupt, no cache or scheduling time */ +uint32_t g2d_last_hw_time_x100us(void); + +/** @brief Bandwidth limit inside the G2D (0: off); the memory bus has its own per master limit */ +void g2d_set_ddr_limit(uint32_t level); +uint32_t g2d_get_ddr_limit(void); + +/** @brief Module clock of the G2D: set a rate (returns the rate set, 0 on failure) and read it */ +uint32_t g2d_set_clock_rate(uint32_t hz); +uint32_t g2d_get_clock_rate(void); + +/** @brief Bytes per pixel of the first plane of a format (0: planar/unsupported) */ +static inline unsigned int g2d_format_bytes_per_pixel(enum g2d_format format) +{ + switch (format) { + case G2D_PIXFMT_ARGB8888: + case G2D_PIXFMT_ABGR8888: + case G2D_PIXFMT_RGBA8888: + case G2D_PIXFMT_BGRA8888: + case G2D_PIXFMT_XRGB8888: + case G2D_PIXFMT_XBGR8888: + return 4; + case G2D_PIXFMT_RGB888: + case G2D_PIXFMT_BGR888: + return 3; + case G2D_PIXFMT_RGB565: + case G2D_PIXFMT_BGR565: + case G2D_PIXFMT_ARGB4444: + case G2D_PIXFMT_ARGB1555: + case G2D_PIXFMT_YUYV: + return 2; + default: + return 0; + } +} + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* __G2D_H__ */ diff --git a/bsp/allwinner/dm/soc/g2d/g2d_sunxi_cmdlist.c b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_cmdlist.c new file mode 100644 index 000000000000..799c91f33662 --- /dev/null +++ b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_cmdlist.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2026 Yuzuki Tsuru + * + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#include + +#include +#include + +#include "g2d_sunxi_hw.h" + +/* one entry of the header area, read by the hardware */ +struct g2d_rcq_header { + /** address of the register image (32 byte aligned) */ + uint32_t addr_low; + /** [23:0] length in bytes, [31:24] address bits 39:32 */ + uint32_t len; + /** [0] write this block, [31:16] length of the header area of the next frame */ + uint32_t flags; + /** register offset from the base of the G2D */ + uint32_t reg_offset; +}; + +#define RCQ_HEADER_BYTES (G2D_CMDLIST_MAX_BLOCKS * sizeof(struct g2d_rcq_header)) +#define RCQ_FLAG_DIRTY RT_BIT(0) + +RT_STATIC_ASSERT(g2d_rcq_header_alignment, + RCQ_HEADER_BYTES % G2D_CMDLIST_ALIGN == 0); + +void g2d_cmdlist_init(struct g2d_cmdlist *cl, void *mem, size_t size) +{ + RT_ASSERT(((uintptr_t)mem % G2D_CMDLIST_ALIGN) == 0); + + cl->mem = mem; + cl->size = size; + cl->used = RCQ_HEADER_BYTES; + cl->blocks = 0; + memset(mem, 0, RCQ_HEADER_BYTES); +} + +uint32_t *g2d_cmdlist_block(struct g2d_cmdlist *cl, uint32_t offset, size_t bytes) +{ + struct g2d_rcq_header *hdr = (struct g2d_rcq_header *)cl->mem + cl->blocks; + size_t span = RT_ALIGN(bytes, G2D_CMDLIST_ALIGN); + uint8_t *data; + + if (cl->blocks >= G2D_CMDLIST_MAX_BLOCKS || cl->used + span > cl->size) { + return NULL; + } + + data = cl->mem + cl->used; + memset(data, 0, span); + hdr->addr_low = (uint32_t)(uintptr_t)data; + hdr->len = bytes; + hdr->flags = RCQ_FLAG_DIRTY; + hdr->reg_offset = offset; + + cl->used += span; + cl->blocks++; + return (uint32_t *)data; +} + +size_t g2d_cmdlist_finish(struct g2d_cmdlist *cl) +{ + if (cl->blocks == 0) { + return 0; + } + /* the hardware reads the headers in pairs; unused entries are zero (not dirty) */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, cl->mem, cl->used); + return RT_ALIGN(cl->blocks, 2) * sizeof(struct g2d_rcq_header); +} diff --git a/bsp/allwinner/dm/soc/g2d/g2d_sunxi_compose.c b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_compose.c new file mode 100644 index 000000000000..64bad179352c --- /dev/null +++ b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_compose.c @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2026 Yuzuki Tsuru + * + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +/* + * Turns a g2d_op into the register images of the hardware. + * + * Two engines exist. The mixer is a small pipeline: + * + * V0 (scalable, YUV) -> [video scaler] -+ + * +-> blender -> write back + * UI2 (RGB) -----------------------------+ + * + * Fill, copy/convert/scale and blend all run through it; they differ in + * which overlays are enabled. The rotate engine is a separate DMA to DMA + * path for rotation and flipping without any scaling. + */ + +#include +#include + +#define DBG_TAG "g2d" +#define DBG_LVL DBG_INFO +#include +#include + +#include "g2d_sunxi_hw.h" +#include "g2d_sunxi_regs.h" + + +#define OVL_ALPHA_PIXEL 0 +#define OVL_ALPHA_GLOBAL 1 +#define OVL_ALPHA_MIXED 2 + +/* mixer overlay used for the background of a blend */ +#define BLEND_UI 2 + +/* a single layer in pipe 0, passed through unchanged */ +#define BLD_PIPE0_ONLY 0x03010301 +#define BLD_ROP_BYPASS 0xf0 +#define BLD_ROP_INDEX0_DEFAULT 0x41000 +#define BLD_FILL_YUV_BLACK 0x00108080 + +#define VSU_FRAC_BITS 19 +#define VSU_COARSE_MAX_W 2048 + +#define ROT_MODE_NORMAL 1 + +/* + * Blender factors { pfs, pfd, afs, afd }, packed as the register wants them. + * + * In the equations of the hardware, pipe 1 is the "source" and pipe 0 the + * "destination" (measured: with the vendor's SRCOVER value the pipe 1 layer + * is drawn over pipe 0). The foreground of a blend sits in pipe 0, the V0 + * overlay that can scale and take YUV, so each mode of the API uses the + * vendor value of its mirror image. + */ +static const uint32_t bld_factors[] = { + [G2D_BLEND_CLEAR] = 0x00000000, + [G2D_BLEND_SRC] = 0x01000100, + [G2D_BLEND_DST] = 0x00010001, + [G2D_BLEND_SRC_OVER] = 0x01030103, + [G2D_BLEND_DST_OVER] = 0x03010301, + [G2D_BLEND_SRC_IN] = 0x02000200, + [G2D_BLEND_DST_IN] = 0x00020002, + [G2D_BLEND_SRC_OUT] = 0x03000300, + [G2D_BLEND_DST_OUT] = 0x00030003, + [G2D_BLEND_SRC_ATOP] = 0x02030203, + [G2D_BLEND_DST_ATOP] = 0x03020302, + [G2D_BLEND_XOR] = 0x03030303, +}; + +struct mixer { + struct g2d_cmdlist *cl; + uint32_t *core; + uint32_t *wb; + uint32_t *v0; + uint32_t *ui[3]; + uint32_t *bld; +}; + +static inline void reg_set(uint32_t *blk, uint32_t off, uint32_t val) +{ + blk[off / 4] = val; +} + +static inline void reg_or(uint32_t *blk, uint32_t off, uint32_t val) +{ + blk[off / 4] |= val; +} + +static uint32_t alpha_mode_hw(enum g2d_alpha_mode mode) +{ + switch (mode) { + case G2D_ALPHA_GLOBAL: + return OVL_ALPHA_GLOBAL; + case G2D_ALPHA_MIXED: + return OVL_ALPHA_MIXED; + default: + return OVL_ALPHA_PIXEL; + } +} + +/* the size registers hold size - 1 */ +static uint32_t size_reg(uint32_t w, uint32_t h) +{ + return G2D_SIZE(w, h); +} + +/* ---- mixer building blocks ---------------------------------------------- */ + +static int mixer_begin(struct mixer *m, struct g2d_cmdlist *cl) +{ + unsigned int i; + + m->cl = cl; + m->core = g2d_cmdlist_block(cl, G2D_CORE_CTRL, G2D_CORE_CTRL_SIZE); + m->wb = g2d_cmdlist_block(cl, G2D_WB, G2D_WB_SIZE); + m->v0 = g2d_cmdlist_block(cl, G2D_V0, G2D_V0_SIZE); + for (i = 0; i < RT_ARRAY_SIZE(m->ui); i++) { + m->ui[i] = g2d_cmdlist_block(cl, G2D_UI(i), G2D_UI_SIZE); + } + m->bld = g2d_cmdlist_block(cl, G2D_BLD, G2D_BLD_SIZE); + if (!m->core || !m->wb || !m->v0 || !m->ui[0] || !m->ui[1] || + !m->ui[2] || !m->bld) { + return -ENOMEM; + } + + reg_set(m->core, 0, G2D_CORE_CTRL_MIXER); + /* the blender passes the blend result through, no raster operation */ + reg_set(m->bld, G2D_BLD_ROP_CTRL, BLD_ROP_BYPASS); + reg_set(m->bld, G2D_BLD_ROP_INDEX0, BLD_ROP_INDEX0_DEFAULT); + return 0; +} + +static uint32_t ovl_attr(const struct g2d_hw_buf *b, uint32_t alpha_mode, uint8_t alpha, + bool premul) +{ + return G2D_OVL_ATTR_EN | RT_FIELD_PREP(G2D_OVL_ATTR_ALPHA_MODE_MASK, alpha_mode) | + RT_FIELD_PREP(G2D_OVL_ATTR_FORMAT_MASK, b->fmt->hw) | + (premul ? G2D_OVL_ATTR_PREMUL : 0) | RT_FIELD_PREP(G2D_OVL_ATTR_ALPHA_MASK, alpha); +} + +static void v0_setup(struct mixer *m, const struct g2d_hw_buf *b, uint32_t alpha_mode, + uint8_t alpha, bool premul) +{ + unsigned int p; + + reg_set(m->v0, G2D_OVL_ATTR, ovl_attr(b, alpha_mode, alpha, premul)); + reg_set(m->v0, G2D_OVL_MEM_SIZE, size_reg(b->width, b->height)); + reg_set(m->v0, G2D_V0_WIN_SIZE, size_reg(b->width, b->height)); + reg_set(m->v0, G2D_OVL_PITCH0, b->pitch[0]); + reg_set(m->v0, G2D_V0_PITCH1, b->pitch[1]); + reg_set(m->v0, G2D_V0_PITCH2, b->pitch[2]); + for (p = 0; p < G2D_MAX_PLANES; p++) { + reg_set(m->v0, G2D_V0_ADDR(p), b->addr[p]); + } +} + +static void ui_setup(struct mixer *m, unsigned int n, const struct g2d_hw_buf *b, + uint32_t alpha_mode, uint8_t alpha, bool premul) +{ + reg_set(m->ui[n], G2D_OVL_ATTR, ovl_attr(b, alpha_mode, alpha, premul)); + reg_set(m->ui[n], G2D_OVL_MEM_SIZE, size_reg(b->width, b->height)); + reg_set(m->ui[n], G2D_UI_WIN_SIZE, size_reg(b->width, b->height)); + reg_set(m->ui[n], G2D_OVL_PITCH0, b->pitch[0]); + reg_set(m->ui[n], G2D_UI_ADDR, b->addr[0]); +} + +static void bld_pipe(struct mixer *m, unsigned int pipe, uint32_t w, uint32_t h, bool premul) +{ + if (pipe == 0) { + /* pipe 0 is the bottom layer of the blender: it also fills the area */ + reg_or(m->bld, G2D_BLD_EN, G2D_BLD_EN_P0 | G2D_BLD_EN_P0_FILL); + } else { + reg_or(m->bld, G2D_BLD_EN, G2D_BLD_EN_P1); + } + reg_set(m->bld, G2D_BLD_PIPE_SIZE(pipe), size_reg(w, h)); + reg_set(m->bld, G2D_BLD_PIPE_COOR(pipe), 0); + if (premul) { + reg_or(m->bld, G2D_BLD_PREMUL, RT_BIT(pipe)); + } +} + +static void bld_output(struct mixer *m, uint32_t w, uint32_t h, bool premul, bool yuv_domain) +{ + reg_set(m->bld, G2D_BLD_OUT_SIZE, size_reg(w, h)); + reg_set(m->bld, G2D_BLD_OUT_COLOR, + (premul ? G2D_BLD_OUT_PREMUL : 0) | (yuv_domain ? G2D_BLD_OUT_YUV : 0)); +} + +/* colour space converter @p n takes YCbCr from a pipe/output and produces RGB */ +static void bld_csc_yuv2rgb(struct mixer *m, unsigned int n, uint32_t flags) +{ + const uint32_t (*tab)[G2D_CSC_WORDS] = + (flags & G2D_FLAG_YUV_BT709) ? g2d_csc_yuv2rgb_709 : g2d_csc_yuv2rgb_601; + const uint32_t *matrix = tab[(flags & G2D_FLAG_YUV_FULL_RANGE) ? 1 : 0]; + + reg_or(m->bld, G2D_BLD_CSC_CTRL, G2D_BLD_CSC_EN(n)); + reg_set(m->bld, G2D_BLD_FILL_COLOR(0), BLD_FILL_YUV_BLACK); + reg_set(m->bld, G2D_BLD_FILL_COLOR(1), BLD_FILL_YUV_BLACK); + memcpy(&m->bld[G2D_BLD_CSC(n) / 4], matrix, G2D_CSC_WORDS * sizeof(uint32_t)); +} + +static void wb_setup(struct mixer *m, const struct g2d_hw_buf *d) +{ + reg_set(m->wb, G2D_WB_ATTR, RT_FIELD_PREP(G2D_WB_ATTR_FORMAT_MASK, d->fmt->hw)); + reg_set(m->wb, G2D_WB_DATA_SIZE, size_reg(d->width, d->height)); + reg_set(m->wb, G2D_WB_PITCH(0), d->pitch[0]); + reg_set(m->wb, G2D_WB_ADDR(0), d->addr[0]); +} + +/* + * Coarse down sampling in front of the scaler: the scaler itself handles up + * to 8x horizontally and 4x vertically, larger reductions are pre-reduced by + * the overlay. Returns the size the scaler sees. + */ +static void v0_coarse(struct mixer *m, const struct g2d_fmt_info *fmt, uint32_t in_w, uint32_t in_h, + uint32_t out_w, uint32_t out_h, uint32_t *mid_w, uint32_t *mid_h) +{ + /* chroma planes of the subsampled formats are reduced by the same factor */ + unsigned int hs = fmt->planes > 1 ? fmt->hshift[1] : 0; + unsigned int vs = fmt->planes > 1 ? fmt->vshift[1] : 0; + bool hor = false, ver = false; + + *mid_w = in_w; + *mid_h = in_h; + + if (fmt->planes == 1 && fmt->yuv) { + /* packed YUV: no coarse stage */ + return; + } + if (in_w >= (out_w << 3)) { + *mid_w = rt_min_t(rt_uint32_t, out_w << 3, VSU_COARSE_MAX_W); + hor = true; + } else if (in_w > VSU_COARSE_MAX_W) { + *mid_w = VSU_COARSE_MAX_W; + hor = true; + } + if (in_h >= (out_h << 2)) { + *mid_h = out_h << 2; + ver = true; + } + + if (hor) { + reg_set(m->v0, G2D_V0_HDS0, (*mid_w << 16) | in_w); + reg_set(m->v0, G2D_V0_HDS1, + ((*mid_w >> hs) << 16) | ((in_w + RT_BIT(hs) - 1) >> hs)); + } + if (ver) { + reg_set(m->v0, G2D_V0_VDS0, (*mid_h << 16) | in_h); + reg_set(m->v0, G2D_V0_VDS1, + ((*mid_h >> vs) << 16) | ((in_h + RT_BIT(vs) - 1) >> vs)); + } +} + +/* coefficient set for a scale step (1/8 resolution of the ratio) */ +static unsigned int vsu_coef_set(uint32_t step) +{ + uint32_t ratio = step >> (VSU_FRAC_BITS - 3); + uint32_t ip = ratio >> 3; + uint32_t fp = ratio & 7; + + switch (ip) { + case 0: + return 0; + case 1: + return 1 + fp; + case 2: + return 9 + (fp >> 1); + case 3: + return 13; + case 4: + return 14; + default: + return 15; + } +} + +static void vsu_load_coef(uint32_t *vsu, uint32_t off, const uint32_t *coef) +{ + memcpy(&vsu[off / 4], coef, G2D_VSU_PHASES * sizeof(uint32_t)); +} + +/* enable the video scaler for the V0 overlay: in (after the coarse stage) -> out */ +static int vsu_enable(struct mixer *m, const struct g2d_fmt_info *fmt, uint32_t in_w, uint32_t in_h, + uint32_t out_w, uint32_t out_h, uint8_t alpha) +{ + uint32_t *vsu = g2d_cmdlist_block(m->cl, G2D_VSU, G2D_VSU_SIZE); + uint32_t hstep, vstep, cw, ch; + bool planar420 = fmt->planes > 1; + bool packed422 = fmt->yuv && fmt->planes == 1; + + if (vsu == NULL) { + return -ENOMEM; + } + + hstep = ((uint64_t)in_w << VSU_FRAC_BITS) / out_w; + vstep = ((uint64_t)in_h << VSU_FRAC_BITS) / out_h; + + reg_set(vsu, G2D_VSU_CTRL, + G2D_VSU_CTRL_EN | G2D_VSU_CTRL_COEF_ACCESS | + (planar420 ? G2D_VSU_CTRL_FILTER_PLANAR : 0)); + reg_set(vsu, G2D_VSU_OUT_SIZE, size_reg(out_w, out_h)); + reg_set(vsu, G2D_VSU_GLB_ALPHA, alpha); + reg_set(vsu, G2D_VSU_Y_SIZE, size_reg(in_w, in_h)); + /* the step registers have a reserved bit 0 */ + reg_set(vsu, G2D_VSU_Y_HSTEP, hstep << 1); + reg_set(vsu, G2D_VSU_Y_VSTEP, vstep << 1); + vsu_load_coef(vsu, G2D_VSU_Y_HCOEF, g2d_vsu_coef_lanczos[vsu_coef_set(hstep)]); + + if (planar420) { + /* 4:2:0: chroma has half the luma resolution in both directions */ + cw = (in_w + 1) >> 1; + ch = (in_h + 1) >> 1; + reg_set(vsu, G2D_VSU_C_SIZE, size_reg(cw, ch)); + reg_set(vsu, G2D_VSU_C_HSTEP, hstep); + reg_set(vsu, G2D_VSU_C_VSTEP, vstep); + reg_set(vsu, G2D_VSU_C_HPHASE, 0xfffc0000); + reg_set(vsu, G2D_VSU_C_VPHASE, 0xfffc0000); + vsu_load_coef(vsu, G2D_VSU_C_HCOEF, g2d_vsu_coef_lanczos[vsu_coef_set(hstep >> 1)]); + vsu_load_coef(vsu, G2D_VSU_Y_VCOEF, g2d_vsu_coef_lanczos[vsu_coef_set(vstep)]); + } else if (packed422) { + /* 4:2:2: half the horizontal resolution only */ + cw = (in_w + 1) >> 1; + reg_set(vsu, G2D_VSU_C_SIZE, size_reg(cw, in_h)); + reg_set(vsu, G2D_VSU_C_HSTEP, hstep); + reg_set(vsu, G2D_VSU_C_VSTEP, vstep << 1); + vsu_load_coef(vsu, G2D_VSU_C_HCOEF, g2d_vsu_coef_lanczos[vsu_coef_set(hstep >> 1)]); + vsu_load_coef(vsu, G2D_VSU_Y_VCOEF, g2d_vsu_coef_linear); + } else { + /* RGB: the "chroma" path carries the same samples */ + reg_set(vsu, G2D_VSU_C_SIZE, size_reg(in_w, in_h)); + reg_set(vsu, G2D_VSU_C_HSTEP, hstep << 1); + reg_set(vsu, G2D_VSU_C_VSTEP, vstep << 1); + vsu_load_coef(vsu, G2D_VSU_C_HCOEF, g2d_vsu_coef_lanczos[vsu_coef_set(hstep)]); + vsu_load_coef(vsu, G2D_VSU_Y_VCOEF, g2d_vsu_coef_linear); + } + return 0; +} + +/* Keep the VSU block in the command list even when scaling is disabled. */ +static int vsu_disable(struct mixer *m) +{ + uint32_t *vsu = g2d_cmdlist_block(m->cl, G2D_VSU, sizeof(uint32_t)); + + return vsu ? 0 : -ENOMEM; +} + +/* ---- operations ---------------------------------------------------------- */ + +static int compose_fill(struct g2d_cmdlist *cl, const struct g2d_op *op) +{ + struct g2d_hw_buf d; + struct mixer m; + int ret; + + ret = g2d_hw_buf_init(&d, &op->dst, &op->dst_rect); + if (ret) { + return ret; + } + ret = mixer_begin(&m, cl); + if (ret) { + return ret; + } + + /* V0 supplies a constant colour; the dst rectangle only sizes the output */ + v0_setup(&m, &d, OVL_ALPHA_PIXEL, 0xff, false); + reg_or(m.v0, G2D_OVL_ATTR, G2D_OVL_ATTR_FILL_EN); + reg_set(m.v0, G2D_V0_FILL, op->color); + + bld_pipe(&m, 0, d.width, d.height, false); + reg_set(m.bld, G2D_BLD_CTRL, BLD_PIPE0_ONLY); + bld_output(&m, d.width, d.height, op->flags & G2D_FLAG_DST_PREMULTIPLIED, false); + wb_setup(&m, &d); + + return vsu_disable(&m); +} + +static int compose_blit(struct g2d_cmdlist *cl, const struct g2d_op *op) +{ + struct g2d_hw_buf s, d; + struct mixer m; + bool scaled; + int ret; + + ret = g2d_hw_buf_init(&s, &op->src, &op->src_rect); + if (ret == 0) { + ret = g2d_hw_buf_init(&d, &op->dst, &op->dst_rect); + } + if (ret) { + return ret; + } + ret = mixer_begin(&m, cl); + if (ret) { + return ret; + } + + v0_setup(&m, &s, OVL_ALPHA_PIXEL, 0xff, op->flags & G2D_FLAG_SRC_PREMULTIPLIED); + + scaled = s.fmt->yuv || s.width != d.width || s.height != d.height; + if (scaled) { + uint32_t mid_w, mid_h; + + v0_coarse(&m, s.fmt, s.width, s.height, d.width, d.height, &mid_w, &mid_h); + ret = vsu_enable(&m, s.fmt, mid_w, mid_h, d.width, d.height, 0xff); + } else { + ret = vsu_disable(&m); + } + if (ret) { + return ret; + } + + bld_pipe(&m, 0, d.width, d.height, op->flags & G2D_FLAG_SRC_PREMULTIPLIED); + reg_set(m.bld, G2D_BLD_CTRL, BLD_PIPE0_ONLY); + /* YCbCr stays in its colour space through the blender, converted at the output */ + bld_output(&m, d.width, d.height, op->flags & G2D_FLAG_DST_PREMULTIPLIED, s.fmt->yuv); + if (s.fmt->yuv) { + bld_csc_yuv2rgb(&m, 2, op->flags); + } + wb_setup(&m, &d); + return 0; +} + +static int compose_blend(struct g2d_cmdlist *cl, const struct g2d_op *op) +{ + const struct g2d_blend *bl = &op->blend; + struct g2d_hw_buf f, b, d; + struct mixer m; + bool scaled; + int ret; + + if ((unsigned int)bl->mode >= RT_ARRAY_SIZE(bld_factors)) { + return -EINVAL; + } + ret = g2d_hw_buf_init(&f, &op->src, &op->src_rect); + if (ret == 0) { + ret = g2d_hw_buf_init(&b, &op->bg, &op->bg_rect); + } + if (ret == 0) { + ret = g2d_hw_buf_init(&d, &op->dst, &op->dst_rect); + } + if (ret) { + return ret; + } + /* the background is not scaled and the UI overlay takes RGB only */ + if (b.fmt->yuv || b.width != d.width || b.height != d.height) { + LOG_D("background must be RGB and as big as the destination"); + return -EINVAL; + } + ret = mixer_begin(&m, cl); + if (ret) { + return ret; + } + + v0_setup(&m, &f, alpha_mode_hw(bl->fg_alpha_mode), bl->fg_alpha, + op->flags & G2D_FLAG_SRC_PREMULTIPLIED); + ui_setup(&m, BLEND_UI, &b, alpha_mode_hw(bl->bg_alpha_mode), bl->bg_alpha, + op->flags & G2D_FLAG_DST_PREMULTIPLIED); + + scaled = f.fmt->yuv || f.width != d.width || f.height != d.height; + if (scaled) { + uint32_t mid_w, mid_h; + + v0_coarse(&m, f.fmt, f.width, f.height, d.width, d.height, &mid_w, &mid_h); + ret = vsu_enable(&m, f.fmt, mid_w, mid_h, d.width, d.height, + f.fmt->yuv ? 0xff : bl->fg_alpha); + } else { + ret = vsu_disable(&m); + } + if (ret) { + return ret; + } + if (f.fmt->yuv) { + /* convert the foreground to RGB before it enters the blender */ + bld_csc_yuv2rgb(&m, 0, op->flags); + } + + bld_pipe(&m, 0, d.width, d.height, op->flags & G2D_FLAG_SRC_PREMULTIPLIED); + bld_pipe(&m, 1, d.width, d.height, op->flags & G2D_FLAG_DST_PREMULTIPLIED); + reg_set(m.bld, G2D_BLD_CTRL, bld_factors[bl->mode]); + if (bl->color_key) { + reg_set(m.bld, G2D_BLD_CK_CFG, 0); + reg_set(m.bld, G2D_BLD_CK_MAX, bl->color_key_max & 0x00ffffff); + reg_set(m.bld, G2D_BLD_CK_MIN, bl->color_key_min & 0x00ffffff); + /* match direction 1: the key is compared with the other (background) layer */ + reg_set(m.bld, G2D_BLD_CK, G2D_BLD_CK_EN | RT_FIELD_PREP(G2D_BLD_CK_DIR_MASK, 1)); + } + bld_output(&m, d.width, d.height, op->flags & G2D_FLAG_DST_PREMULTIPLIED, false); + wb_setup(&m, &d); + return 0; +} + +static int compose_rotate(struct g2d_cmdlist *cl, const struct g2d_op *op) +{ + static const uint8_t degree[] = { + [G2D_ROTATE_0] = 0, + [G2D_ROTATE_90] = 1, + [G2D_ROTATE_180] = 2, + [G2D_ROTATE_270] = 3, + }; + bool swap = op->rotation == G2D_ROTATE_90 || op->rotation == G2D_ROTATE_270; + struct g2d_hw_buf s, d; + uint32_t *core, *rot; + unsigned int p; + int ret; + + if ((unsigned int)op->rotation >= RT_ARRAY_SIZE(degree)) { + return -EINVAL; + } + ret = g2d_hw_buf_init(&s, &op->src, &op->src_rect); + if (ret == 0) { + ret = g2d_hw_buf_init(&d, &op->dst, &op->dst_rect); + } + if (ret) { + return ret; + } + /* a pure move: same format, no scaling */ + if (s.fmt != d.fmt || d.width != (swap ? s.height : s.width) || + d.height != (swap ? s.width : s.height)) { + LOG_D("rotation needs the same format and the rotated size"); + return -EINVAL; + } + for (p = 0; p < s.fmt->planes; p++) { + if ((s.addr[p] | d.addr[p]) & 3U || d.pitch[p] & 7U) { + LOG_D("rotation needs 4 byte addresses and 8 byte destination pitch"); + return -EINVAL; + } + } + + core = g2d_cmdlist_block(cl, G2D_CORE_CTRL, G2D_CORE_CTRL_SIZE); + rot = g2d_cmdlist_block(cl, G2D_ROT, G2D_ROT_SIZE); + if (!core || !rot) { + return -ENOMEM; + } + + reg_set(core, 0, G2D_CORE_CTRL_ROTATE); + reg_set(rot, G2D_ROT_CTRL, + RT_FIELD_PREP(G2D_ROT_CTRL_MODE_MASK, ROT_MODE_NORMAL) | + RT_FIELD_PREP(G2D_ROT_CTRL_DEGREE_MASK, degree[op->rotation]) | + ((op->flags & G2D_FLIP_V) ? G2D_ROT_CTRL_VFLIP : 0) | + ((op->flags & G2D_FLIP_H) ? G2D_ROT_CTRL_HFLIP : 0)); + reg_set(rot, G2D_ROT_IN_FORMAT, s.fmt->hw); + reg_set(rot, G2D_ROT_IN_SIZE, size_reg(s.width, s.height)); + reg_set(rot, G2D_ROT_OUT_SIZE, size_reg(d.width, d.height)); + for (p = 0; p < s.fmt->planes; p++) { + reg_set(rot, G2D_ROT_IN_PITCH(p), s.pitch[p]); + reg_set(rot, G2D_ROT_IN_ADDR(p), s.addr[p]); + reg_set(rot, G2D_ROT_OUT_PITCH(p), d.pitch[p]); + reg_set(rot, G2D_ROT_OUT_ADDR(p), d.addr[p]); + } + return 0; +} + +int g2d_compose(struct g2d_cmdlist *cl, const struct g2d_op *op) +{ + const struct g2d_fmt_info *dst_fmt = g2d_fmt_get(op->dst.format); + + /* the write back path produces RGB only */ + if (dst_fmt == NULL || dst_fmt->yuv) { + LOG_D("destination format %d cannot be written", op->dst.format); + return -EINVAL; + } + + switch (op->type) { + case G2D_OP_FILL: + return compose_fill(cl, op); + case G2D_OP_BLIT: + if (op->rotation == G2D_ROTATE_0 && !(op->flags & (G2D_FLIP_H | G2D_FLIP_V))) { + return compose_blit(cl, op); + } + return compose_rotate(cl, op); + case G2D_OP_BLEND: + return compose_blend(cl, op); + default: + return -EINVAL; + } +} diff --git a/bsp/allwinner/dm/soc/g2d/g2d_sunxi_format.c b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_format.c new file mode 100644 index 000000000000..2bc0664619c9 --- /dev/null +++ b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_format.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2026 Yuzuki Tsuru + * + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#include +#include + +#define DBG_TAG "g2d" +#define DBG_LVL DBG_INFO +#include +#include + +#include "g2d_sunxi_hw.h" + + +#define MAX_SIZE 8192 + +#define RGB(_hw, _bytes) \ + { .hw = (_hw), .planes = 1, .bytes = { (_bytes) } } + +static const struct g2d_fmt_info g2d_formats[G2D_PIXFMT_MAX] = { + [G2D_PIXFMT_ARGB8888] = RGB(0x00, 4), + [G2D_PIXFMT_ABGR8888] = RGB(0x01, 4), + [G2D_PIXFMT_RGBA8888] = RGB(0x02, 4), + [G2D_PIXFMT_BGRA8888] = RGB(0x03, 4), + [G2D_PIXFMT_XRGB8888] = RGB(0x04, 4), + [G2D_PIXFMT_XBGR8888] = RGB(0x05, 4), + [G2D_PIXFMT_RGB888] = RGB(0x08, 3), + [G2D_PIXFMT_BGR888] = RGB(0x09, 3), + [G2D_PIXFMT_RGB565] = RGB(0x0a, 2), + [G2D_PIXFMT_BGR565] = RGB(0x0b, 2), + [G2D_PIXFMT_ARGB4444] = RGB(0x0c, 2), + [G2D_PIXFMT_ARGB1555] = RGB(0x10, 2), + [G2D_PIXFMT_NV12] = { + .hw = 0x28, .planes = 2, .yuv = true, + .bytes = { 1, 2 }, + .hshift = { 0, 1 }, + .vshift = { 0, 1 }, + }, + [G2D_PIXFMT_NV21] = { + .hw = 0x29, .planes = 2, .yuv = true, + .bytes = { 1, 2 }, + .hshift = { 0, 1 }, + .vshift = { 0, 1 }, + }, + [G2D_PIXFMT_I420] = { + .hw = 0x2a, .planes = 3, .yuv = true, + .bytes = { 1, 1, 1 }, + .hshift = { 0, 1, 1 }, + .vshift = { 0, 1, 1 }, + }, + [G2D_PIXFMT_YUYV] = { + .hw = 0x20, .planes = 1, .yuv = true, + .bytes = { 2 }, + }, +}; + +const struct g2d_fmt_info *g2d_fmt_get(enum g2d_format format) +{ + if ((unsigned int)format >= G2D_PIXFMT_MAX) { + return NULL; + } + return &g2d_formats[format]; +} + +/* bytes of one line of plane @p p holding @p width luma pixels */ +static uint32_t plane_line_bytes(const struct g2d_fmt_info *fmt, unsigned int p, uint32_t width) +{ + uint32_t samples = (width + RT_BIT(fmt->hshift[p]) - 1) >> fmt->hshift[p]; + + return samples * fmt->bytes[p]; +} + +static uint32_t plane_pitch(const struct g2d_surface *s, const struct g2d_fmt_info *fmt, + unsigned int p) +{ + return s->pitch[p] ? s->pitch[p] : plane_line_bytes(fmt, p, s->width); +} + +int g2d_hw_buf_init(struct g2d_hw_buf *buf, const struct g2d_surface *s, + const struct g2d_rect *rect) +{ + const struct g2d_fmt_info *fmt = g2d_fmt_get(s->format); + unsigned int p; + + if (fmt == NULL || s->width == 0 || s->height == 0 || s->width > MAX_SIZE || + s->height > MAX_SIZE) { + LOG_D("bad surface: format %d, %ux%u", s->format, s->width, s->height); + return -EINVAL; + } + if (rect->width == 0 || rect->height == 0 || rect->x + rect->width > s->width || + rect->y + rect->height > s->height) { + LOG_D("rect %u,%u %ux%u outside the %ux%u surface", rect->x, rect->y, rect->width, + rect->height, s->width, s->height); + return -EINVAL; + } + /* subsampled chroma: the rectangle must not split a chroma sample */ + if ((fmt->yuv && ((rect->x | rect->width) & 1U)) || + (fmt->vshift[1] && ((rect->y | rect->height) & 1U))) { + LOG_D("rect splits a chroma sample"); + return -EINVAL; + } + + buf->fmt = fmt; + buf->width = rect->width; + buf->height = rect->height; + memset(buf->addr, 0, sizeof(buf->addr)); + memset(buf->pitch, 0, sizeof(buf->pitch)); + + for (p = 0; p < fmt->planes; p++) { + uint32_t pitch = plane_pitch(s, fmt, p); + + if (s->plane[p] == NULL || pitch < plane_line_bytes(fmt, p, s->width)) { + LOG_D("plane %u: address %p, pitch %u", p, s->plane[p], pitch); + return -EINVAL; + } + buf->pitch[p] = pitch; + buf->addr[p] = (uint32_t)(uintptr_t)s->plane[p] + + (rect->y >> fmt->vshift[p]) * pitch + + (rect->x >> fmt->hshift[p]) * fmt->bytes[p]; + } + return 0; +} + +int g2d_surface_rect_span(const struct g2d_surface *s, const struct g2d_rect *rect, + unsigned int p, void **start, size_t *len) +{ + const struct g2d_fmt_info *fmt = g2d_fmt_get(s->format); + uint32_t pitch, first, last; + + if (fmt == NULL || p >= fmt->planes || s->plane[p] == NULL) { + return -EINVAL; + } + pitch = plane_pitch(s, fmt, p); + first = rect->y >> fmt->vshift[p]; + last = (rect->y + rect->height + RT_BIT(fmt->vshift[p]) - 1) >> fmt->vshift[p]; + *start = (uint8_t *)s->plane[p] + (size_t)first * pitch; + *len = (size_t)(last - first) * pitch; + return 0; +} diff --git a/bsp/allwinner/dm/soc/g2d/g2d_sunxi_hw.h b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_hw.h new file mode 100644 index 000000000000..1ae59d5f5d27 --- /dev/null +++ b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_hw.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2026 Yuzuki Tsuru + * + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +/* Internal interface of the Allwinner G2D driver: formats, command lists, compose */ + +#ifndef G2D_SUNXI_HW_H_ +#define G2D_SUNXI_HW_H_ + +#include +#include + +#include "g2d.h" + +#define G2D_MAX_PLANES 3 +#define G2D_VSU_PHASES 32 +#define G2D_VSU_COEF_SETS 16 +#define G2D_CSC_WORDS 12 + +/* ---- pixel formats ------------------------------------------------------ */ + +struct g2d_fmt_info { + /** value of the format fields of the overlay, write back and rotate registers */ + uint8_t hw; + uint8_t planes; + bool yuv; + /** bytes per sample of each plane (one UV pair counts as one sample) */ + uint8_t bytes[G2D_MAX_PLANES]; + /** log2 of the horizontal and vertical subsampling of each plane */ + uint8_t hshift[G2D_MAX_PLANES]; + uint8_t vshift[G2D_MAX_PLANES]; +}; + +const struct g2d_fmt_info *g2d_fmt_get(enum g2d_format format); + +/** A rectangle of a surface as seen by the DMA engines */ +struct g2d_hw_buf { + const struct g2d_fmt_info *fmt; + /** address of the top-left sample of each plane */ + uint32_t addr[G2D_MAX_PLANES]; + uint32_t pitch[G2D_MAX_PLANES]; + uint16_t width; + uint16_t height; +}; + +/** Validate @p rect in @p s and describe it. Returns 0 or -EINVAL. */ +int g2d_hw_buf_init(struct g2d_hw_buf *buf, const struct g2d_surface *s, + const struct g2d_rect *rect); + +/** Whole lines of plane @p p that cover @p rect, for cache maintenance */ +int g2d_surface_rect_span(const struct g2d_surface *s, const struct g2d_rect *rect, + unsigned int p, void **start, size_t *len); + +/* ---- register command list --------------------------------------------- */ + +/** Memory the hardware reads its commands from: headers first, then the register images */ +struct g2d_cmdlist { + uint8_t *mem; + size_t size; + size_t used; + unsigned int blocks; +}; + +#define G2D_CMDLIST_MAX_BLOCKS 12 +#define G2D_CMDLIST_ALIGN 32 + +void g2d_cmdlist_init(struct g2d_cmdlist *cl, void *mem, size_t size); + +/** Reserve a zeroed register image written to registers @p offset.. by the hardware */ +uint32_t *g2d_cmdlist_block(struct g2d_cmdlist *cl, uint32_t offset, size_t bytes); + +/** Close the list; returns the length of the header area, 0 when empty */ +size_t g2d_cmdlist_finish(struct g2d_cmdlist *cl); + +/* ---- compose: operations -> register images ----------------------------- */ + +/** Which engine runs the command list */ +enum g2d_engine { + G2D_ENGINE_MIXER, + G2D_ENGINE_ROTATE, +}; + +/** + * Build the command list of @p op. Returns 0 or a negative errno when the + * hardware cannot do it. + */ +int g2d_compose(struct g2d_cmdlist *cl, const struct g2d_op *op); + +/* ---- constant tables ---------------------------------------------------- */ + +extern const uint32_t g2d_vsu_coef_lanczos[G2D_VSU_COEF_SETS][G2D_VSU_PHASES]; +extern const uint32_t g2d_vsu_coef_linear[G2D_VSU_PHASES]; +extern const uint32_t g2d_csc_yuv2rgb_601[2][G2D_CSC_WORDS]; +extern const uint32_t g2d_csc_yuv2rgb_709[2][G2D_CSC_WORDS]; + +#endif /* G2D_SUNXI_HW_H_ */ diff --git a/bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h new file mode 100644 index 000000000000..567b7a2da9cc --- /dev/null +++ b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2026 Yuzuki Tsuru + * + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +/* + * Register map of the Allwinner G2D. + * + * The block is programmed through a register command queue (RCQ): a list of + * { memory block, register offset, length } entries that the hardware copies + * into its register file before it runs. Only the control registers at the + * top are written by the CPU. + */ + +#ifndef G2D_SUNXI_REGS_H_ +#define G2D_SUNXI_REGS_H_ +#include + +/* ---- control registers (CPU) ------------------------------------------ */ + +#define G2D_RESET 0x010 +#define G2D_RESET_CORE RT_BIT(0) +#define G2D_RESET_THREAD(n) RT_BIT(16 + 4 * (n)) +#define G2D_CLK_GATE 0x014 +#define G2D_CLK_GATE_CORE RT_BIT(0) +#define G2D_CLK_GATE_THREAD(n) RT_BIT(16 + 4 * (n)) +#define G2D_MBUS_GATE 0x018 +#define G2D_MBUS_GATE_CLK RT_BIT(0) +#define G2D_MBUS_GATE_RESET RT_BIT(16) +#define G2D_MODE 0x020 +#define G2D_MODE_MASTER RT_BIT(0) +#define G2D_CORE_STATUS 0x028 +#define G2D_CORE_STATUS_BUSY RT_BIT(28) + +/* internal cache and bandwidth limiter */ +#define G2D_CACHE_CTRL 0x050 +#define G2D_CACHE_CTRL_EN RT_BIT(16) +#define G2D_CACHE_SIZE0 0x054 +#define G2D_CACHE_SIZE1 0x058 +#define G2D_CACHE_SIZE_MASK RT_GENMASK(7, 0) +#define G2D_DDR_LIMIT 0x064 +#define G2D_DDR_LIMIT_EN RT_BIT(16) +#define G2D_DDR_LIMIT_MASK RT_GENMASK(8, 0) + +/* RCQ thread n */ +#define G2D_THREAD(n) (0x100 + 0x40 * (n)) +#define G2D_THREAD_IRQ_EN 0x00 +#define G2D_THREAD_IRQ_STATUS 0x04 +#define G2D_THREAD_IRQ_TASK_END RT_BIT(0) +#define G2D_THREAD_IRQ_RCQ_ACCEPT RT_BIT(4) +#define G2D_THREAD_IRQ_TIMEOUT RT_BIT(8) +#define G2D_THREAD_HEAD_LOW 0x10 +#define G2D_THREAD_HEAD_HIGH_LEN 0x14 +#define G2D_THREAD_HEAD_HIGH_MASK RT_GENMASK(7, 0) +#define G2D_THREAD_HEAD_LEN_MASK RT_GENMASK(31, 16) +#define G2D_THREAD_ATTR 0x18 +#define G2D_THREAD_ATTR_CMD_NUM_MASK RT_GENMASK(11, 0) /* commands - 1 */ +#define G2D_THREAD_ATTR_END_IRQ RT_BIT(16) +#define G2D_THREAD_UPDATE 0x1c + +/* ---- register blocks written through the RCQ --------------------------- */ + +/* engine select: the mixer pipeline or the rotate engine */ +#define G2D_CORE_CTRL 0x300 +#define G2D_CORE_CTRL_SIZE 0x04 +#define G2D_CORE_CTRL_MIXER RT_BIT(0) +#define G2D_CORE_CTRL_ROTATE RT_BIT(4) + +/* V0: video overlay (scalable, YUV capable) */ +#define G2D_V0 0x0800 +#define G2D_V0_SIZE 0x40 +/* UI overlays */ +#define G2D_UI(n) (0x1000 + 0x800 * (n)) +#define G2D_UI_SIZE 0x20 + +/* overlay registers, V0 and UI share the leading ones */ +#define G2D_OVL_ATTR 0x00 +#define G2D_OVL_ATTR_EN RT_BIT(0) +#define G2D_OVL_ATTR_ALPHA_MODE_MASK RT_GENMASK(2, 1) +#define G2D_OVL_ATTR_FILL_EN RT_BIT(4) +#define G2D_OVL_ATTR_FORMAT_MASK RT_GENMASK(13, 8) +#define G2D_OVL_ATTR_PREMUL RT_BIT(16) +#define G2D_OVL_ATTR_ALPHA_MASK RT_GENMASK(31, 24) +#define G2D_OVL_MEM_SIZE 0x04 /* (h - 1) << 16 | (w - 1) */ +#define G2D_OVL_COOR 0x08 +#define G2D_OVL_PITCH0 0x0c +/* V0 */ +#define G2D_V0_PITCH1 0x10 +#define G2D_V0_PITCH2 0x14 +#define G2D_V0_ADDR(p) (0x18 + 4 * (p)) +#define G2D_V0_FILL 0x24 +#define G2D_V0_ADDR_HIGH 0x28 +#define G2D_V0_WIN_SIZE 0x2c +#define G2D_V0_HDS0 0x30 /* coarse down sampling */ +#define G2D_V0_HDS1 0x34 +#define G2D_V0_VDS0 0x38 +#define G2D_V0_VDS1 0x3c +/* UI */ +#define G2D_UI_ADDR 0x10 +#define G2D_UI_FILL 0x14 +#define G2D_UI_ADDR_HIGH 0x18 +#define G2D_UI_WIN_SIZE 0x1c + +/* blender */ +#define G2D_BLD 0x0400 +#define G2D_BLD_SIZE 0x1a0 +#define G2D_BLD_EN 0x00 +#define G2D_BLD_EN_P0_FILL RT_BIT(0) +#define G2D_BLD_EN_P1_FILL RT_BIT(1) +#define G2D_BLD_EN_P0 RT_BIT(8) +#define G2D_BLD_EN_P1 RT_BIT(9) +#define G2D_BLD_FILL_COLOR(p) (0x10 + 4 * (p)) +#define G2D_BLD_PIPE_SIZE(p) (0x20 + 4 * (p)) +#define G2D_BLD_PIPE_COOR(p) (0x30 + 4 * (p)) +#define G2D_BLD_PREMUL 0x40 +#define G2D_BLD_BKCOLOR 0x44 +#define G2D_BLD_OUT_SIZE 0x48 +#define G2D_BLD_CTRL 0x4c +#define G2D_BLD_CK 0x50 +#define G2D_BLD_CK_EN RT_BIT(0) +#define G2D_BLD_CK_DIR_MASK RT_GENMASK(2, 1) +#define G2D_BLD_CK_CFG 0x54 +#define G2D_BLD_CK_MAX 0x58 +#define G2D_BLD_CK_MIN 0x5c +#define G2D_BLD_OUT_COLOR 0x60 +#define G2D_BLD_OUT_PREMUL RT_BIT(0) +#define G2D_BLD_OUT_YUV RT_BIT(1) +#define G2D_BLD_ROP_CTRL 0x80 +#define G2D_BLD_ROP_INDEX0 0x84 +#define G2D_BLD_CSC_CTRL 0x100 +#define G2D_BLD_CSC_EN(n) RT_BIT(n) +#define G2D_BLD_CSC(n) (0x110 + 0x30 * (n)) + +/* write back */ +#define G2D_WB 0x3000 +#define G2D_WB_SIZE 0x2c +#define G2D_WB_ATTR 0x00 +#define G2D_WB_ATTR_FORMAT_MASK RT_GENMASK(5, 0) +#define G2D_WB_DATA_SIZE 0x04 +#define G2D_WB_PITCH(p) (0x08 + 4 * (p)) +#define G2D_WB_ADDR(p) (0x14 + 8 * (p)) +#define G2D_WB_ADDR_HIGH(p) (0x18 + 8 * (p)) + +/* video scaler */ +#define G2D_VSU 0x8000 +#define G2D_VSU_SIZE 0x480 +#define G2D_VSU_CTRL 0x00 +#define G2D_VSU_CTRL_EN RT_BIT(0) +#define G2D_VSU_CTRL_COEF_ACCESS RT_BIT(8) +#define G2D_VSU_CTRL_FILTER_PLANAR RT_BIT(16) +#define G2D_VSU_OUT_SIZE 0x40 +#define G2D_VSU_GLB_ALPHA 0x44 +#define G2D_VSU_Y_SIZE 0x80 +#define G2D_VSU_Y_HSTEP 0x88 +#define G2D_VSU_Y_VSTEP 0x8c +#define G2D_VSU_Y_HPHASE 0x90 +#define G2D_VSU_Y_VPHASE 0x98 +#define G2D_VSU_C_SIZE 0xc0 +#define G2D_VSU_C_HSTEP 0xc8 +#define G2D_VSU_C_VSTEP 0xcc +#define G2D_VSU_C_HPHASE 0xd0 +#define G2D_VSU_C_VPHASE 0xd8 +#define G2D_VSU_Y_HCOEF 0x200 +#define G2D_VSU_Y_VCOEF 0x300 +#define G2D_VSU_C_HCOEF 0x400 + +/* rotate engine */ +#define G2D_ROT 0x28000 +#define G2D_ROT_SIZE 0xb8 +#define G2D_ROT_CTRL 0x00 +#define G2D_ROT_CTRL_MODE_MASK RT_GENMASK(1, 0) +#define G2D_ROT_CTRL_DEGREE_MASK RT_GENMASK(5, 4) +#define G2D_ROT_CTRL_VFLIP RT_BIT(6) +#define G2D_ROT_CTRL_HFLIP RT_BIT(7) +#define G2D_ROT_IN_FORMAT 0x20 +#define G2D_ROT_IN_SIZE 0x24 +#define G2D_ROT_IN_PITCH(p) (0x30 + 4 * (p)) +#define G2D_ROT_IN_ADDR(p) (0x40 + 8 * (p)) +#define G2D_ROT_IN_ADDR_HIGH(p) (0x44 + 8 * (p)) +#define G2D_ROT_OUT_SIZE 0x84 +#define G2D_ROT_OUT_PITCH(p) (0x90 + 4 * (p)) +#define G2D_ROT_OUT_ADDR(p) (0xa0 + 8 * (p)) +#define G2D_ROT_OUT_ADDR_HIGH(p) (0xa4 + 8 * (p)) + +/* width/height field pair shared by the size registers (value = size - 1) */ +#define G2D_SIZE(w, h) ((((h) - 1) << 16) | ((w) - 1)) + +#endif /* G2D_SUNXI_REGS_H_ */ diff --git a/bsp/allwinner/dm/soc/g2d/g2d_sunxi_tables.c b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_tables.c new file mode 100644 index 000000000000..417f5010cd3c --- /dev/null +++ b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_tables.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2026 Yuzuki Tsuru + * + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +/* + * Constants of the G2D video scaler and colour space converters. The values + * are hardware data taken from the vendor driver. + */ + +#include "g2d_sunxi_hw.h" + +/* 16 sets of 32 phases, picked by the scale ratio, see g2d_vsu_coef_set() */ +const uint32_t g2d_vsu_coef_lanczos[G2D_VSU_COEF_SETS][G2D_VSU_PHASES] = { + { + 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, + 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb, + 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb, + 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc, + 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd, + 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff, + 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff, + 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, + }, + { + 0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd, + 0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb, + 0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb, + 0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc, + 0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd, + 0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff, + 0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff, + 0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100, + }, + { + 0xff053804, 0xff063803, 0xff083801, 0xff093701, + 0xff0a3700, 0xff0c3500, 0xff0e34ff, 0xff1033fe, + 0xff1232fd, 0xfe1431fd, 0xfe162ffd, 0xfe182dfd, + 0xfd1b2cfc, 0xfd1d2afc, 0xfd1f28fc, 0xfd2126fc, + 0xfd2323fd, 0xfc2621fd, 0xfc281ffd, 0xfc2a1dfd, + 0xfc2c1bfd, 0xfd2d18fe, 0xfd2f16fe, 0xfd3114fe, + 0xfd3212ff, 0xfe3310ff, 0xff340eff, 0x00350cff, + 0x00360a00, 0x01360900, 0x02370700, 0x03370600, + }, + { + 0xff083207, 0xff093206, 0xff0a3205, 0xff0c3203, + 0xff0d3103, 0xff0e3102, 0xfe113001, 0xfe132f00, + 0xfe142e00, 0xfe162dff, 0xfe182bff, 0xfe192aff, + 0xfe1b29fe, 0xfe1d27fe, 0xfe1f25fe, 0xfd2124fe, + 0xfe2222fe, 0xfe2421fd, 0xfe251ffe, 0xfe271dfe, + 0xfe291bfe, 0xff2a19fe, 0xff2b18fe, 0xff2d16fe, + 0x002e14fe, 0x002f12ff, 0x013010ff, 0x02300fff, + 0x03310dff, 0x04310cff, 0x05310a00, 0x06310900, + }, + { + 0xff0a2e09, 0xff0b2e08, 0xff0c2e07, 0xff0e2d06, + 0xff0f2d05, 0xff102d04, 0xff122c03, 0xfe142c02, + 0xfe152b02, 0xfe172a01, 0xfe182901, 0xfe1a2800, + 0xfe1b2700, 0xfe1d2500, 0xff1e24ff, 0xfe2023ff, + 0xff2121ff, 0xff2320fe, 0xff241eff, 0x00251dfe, + 0x00261bff, 0x00281afe, 0x012818ff, 0x012a16ff, + 0x022a15ff, 0x032b13ff, 0x032c12ff, 0x052c10ff, + 0x052d0fff, 0x062d0d00, 0x072d0c00, 0x082d0b00, + }, + { + 0xff0c2a0b, 0xff0d2a0a, 0xff0e2a09, 0xff0f2a08, + 0xff102a07, 0xff112a06, 0xff132905, 0xff142904, + 0xff162803, 0xff172703, 0xff182702, 0xff1a2601, + 0xff1b2501, 0xff1c2401, 0xff1e2300, 0xff1f2200, + 0x00202000, 0x00211f00, 0x01221d00, 0x01231c00, + 0x01251bff, 0x02251aff, 0x032618ff, 0x032717ff, + 0x042815ff, 0x052814ff, 0x052913ff, 0x06291100, + 0x072a10ff, 0x082a0e00, 0x092a0d00, 0x0a2a0c00, + }, + { + 0xff0d280c, 0xff0e280b, 0xff0f280a, 0xff102809, + 0xff112808, 0xff122708, 0xff142706, 0xff152705, + 0xff162605, 0xff172604, 0xff192503, 0xff1a2403, + 0x001b2302, 0x001c2202, 0x001d2201, 0x001e2101, + 0x011f1f01, 0x01211e00, 0x01221d00, 0x02221c00, + 0x02231b00, 0x03241900, 0x04241800, 0x04251700, + 0x052616ff, 0x06261400, 0x072713ff, 0x08271100, + 0x08271100, 0x09271000, 0x0a280e00, 0x0b280d00, + }, + { + 0xff0e260d, 0xff0f260c, 0xff10260b, 0xff11260a, + 0xff122609, 0xff132608, 0xff142508, 0xff152507, + 0x00152506, 0x00172405, 0x00182305, 0x00192304, + 0x001b2203, 0x001c2103, 0x011d2002, 0x011d2002, + 0x011f1f01, 0x021f1e01, 0x02201d01, 0x03211c00, + 0x03221b00, 0x04221a00, 0x04231801, 0x05241700, + 0x06241600, 0x07241500, 0x08251300, 0x09251200, + 0x09261100, 0x0a261000, 0x0b260f00, 0x0c260e00, + }, + { + 0xff0e250e, 0xff0f250d, 0xff10250c, 0xff11250b, + 0x0011250a, 0x00132409, 0x00142408, 0x00152407, + 0x00162307, 0x00172306, 0x00182206, 0x00192205, + 0x011a2104, 0x011b2004, 0x011c2003, 0x021c1f03, + 0x021e1e02, 0x031e1d02, 0x03201c01, 0x04201b01, + 0x04211a01, 0x05221900, 0x05221801, 0x06231700, + 0x07231600, 0x07241500, 0x08241400, 0x09241300, + 0x0a241200, 0x0b241100, 0x0c241000, 0x0d240f00, + }, + { + 0x000e240e, 0x000f240d, 0x0010240c, 0x0011240b, + 0x0013230a, 0x0013230a, 0x00142309, 0x00152308, + 0x00162208, 0x00172207, 0x01182106, 0x01192105, + 0x011a2005, 0x021b1f04, 0x021b1f04, 0x021d1e03, + 0x031d1d03, 0x031e1d02, 0x041e1c02, 0x041f1b02, + 0x05201a01, 0x05211901, 0x06211801, 0x07221700, + 0x07221601, 0x08231500, 0x09231400, 0x0a231300, + 0x0a231300, 0x0b231200, 0x0c231100, 0x0d231000, + }, + { + 0x000f220f, 0x0010220e, 0x0011220d, 0x0012220c, + 0x0013220b, 0x0013220b, 0x0015210a, 0x0015210a, + 0x01162108, 0x01172008, 0x01182007, 0x02191f06, + 0x02191f06, 0x021a1e06, 0x031a1e05, 0x031c1d04, + 0x041c1c04, 0x041d1c03, 0x051d1b03, 0x051e1a03, + 0x061f1902, 0x061f1902, 0x07201801, 0x08201701, + 0x08211601, 0x09211501, 0x0a211500, 0x0b211400, + 0x0b221300, 0x0c221200, 0x0d221100, 0x0e221000, + }, + { + 0x0010210f, 0x0011210e, 0x0011210e, 0x0012210d, + 0x0013210c, 0x0014200c, 0x0114200b, 0x0115200a, + 0x01161f0a, 0x01171f09, 0x02171f08, 0x02181e08, + 0x03181e07, 0x031a1d06, 0x031a1d06, 0x041b1c05, + 0x041c1c04, 0x051c1b04, 0x051d1a04, 0x061d1a03, + 0x071d1903, 0x071e1803, 0x081e1802, 0x081f1702, + 0x091f1602, 0x0a201501, 0x0b1f1501, 0x0b201401, + 0x0c211300, 0x0d211200, 0x0e201200, 0x0e211100, + }, + { + 0x00102010, 0x0011200f, 0x0012200e, 0x0013200d, + 0x0013200d, 0x01141f0c, 0x01151f0b, 0x01151f0b, + 0x01161f0a, 0x02171e09, 0x02171e09, 0x03181d08, + 0x03191d07, 0x03191d07, 0x041a1c06, 0x041b1c05, + 0x051b1b05, 0x051c1b04, 0x061c1a04, 0x071d1903, + 0x071d1903, 0x081d1803, 0x081e1703, 0x091e1702, + 0x0a1f1601, 0x0a1f1502, 0x0b1f1501, 0x0c1f1401, + 0x0d201300, 0x0d201300, 0x0e201200, 0x0f201100, + }, + { + 0x00102010, 0x0011200f, 0x00121f0f, 0x00131f0e, + 0x00141f0d, 0x01141f0c, 0x01141f0c, 0x01151e0c, + 0x02161e0a, 0x02171e09, 0x03171d09, 0x03181d08, + 0x03181d08, 0x04191c07, 0x041a1c06, 0x051a1b06, + 0x051b1b05, 0x061b1a05, 0x061c1a04, 0x071c1904, + 0x081c1903, 0x081d1803, 0x091d1703, 0x091e1702, + 0x0a1e1602, 0x0b1e1502, 0x0c1e1501, 0x0c1f1401, + 0x0d1f1400, 0x0e1f1300, 0x0e1f1201, 0x0f1f1200, + }, + { + 0x00111e11, 0x00121e10, 0x00131e0f, 0x00131e0f, + 0x01131e0e, 0x01141d0e, 0x02151d0c, 0x02151d0c, + 0x02161d0b, 0x03161c0b, 0x03171c0a, 0x04171c09, + 0x04181b09, 0x05181b08, 0x05191b07, 0x06191a07, + 0x061a1a06, 0x071a1906, 0x071b1905, 0x081b1805, + 0x091b1804, 0x091c1704, 0x0a1c1703, 0x0a1c1604, + 0x0b1d1602, 0x0c1d1502, 0x0c1d1502, 0x0d1d1402, + 0x0e1d1401, 0x0e1e1301, 0x0f1e1300, 0x101e1200, + }, + { + 0x00111e11, 0x00121e10, 0x00131d10, 0x01131d0f, + 0x01141d0e, 0x01141d0e, 0x02151c0d, 0x02151c0d, + 0x03161c0b, 0x03161c0b, 0x04171b0a, 0x04171b0a, + 0x05171b09, 0x05181a09, 0x06181a08, 0x06191a07, + 0x07191907, 0x071a1906, 0x081a1806, 0x081a1806, + 0x091a1805, 0x0a1b1704, 0x0a1b1704, 0x0b1c1603, + 0x0b1c1603, 0x0c1c1503, 0x0d1c1502, 0x0d1d1402, + 0x0e1d1401, 0x0f1d1301, 0x0f1d1301, 0x101e1200, + }, +}; + +/* bilinear, used for the vertical direction of RGB and packed YUV */ +const uint32_t g2d_vsu_coef_linear[G2D_VSU_PHASES] = { + 0x00004000, 0x00023e00, 0x00043c00, 0x00063a00, + 0x00083800, 0x000a3600, 0x000c3400, 0x000e3200, + 0x00103000, 0x00122e00, 0x00142c00, 0x00162a00, + 0x00182800, 0x001a2600, 0x001c2400, 0x001e2200, + 0x00202000, 0x00221e00, 0x00241c00, 0x00261a00, + 0x00281800, 0x002a1600, 0x002c1400, 0x002e1200, + 0x00301000, 0x00320e00, 0x00340c00, 0x00360a00, + 0x00380800, 0x003a0600, 0x003c0400, 0x003e0200, +}; + +/* + * YCbCr to RGB, 12 words each: three rows of { c0, c1, c2, constant }. + * [0] limited range YCbCr in, full range RGB out; [1] full range YCbCr in. + */ +const uint32_t g2d_csc_yuv2rgb_601[2][G2D_CSC_WORDS] = { + { + 0x000004a8, 0x00000000, 0x00000662, 0xfffc8480, + 0x000004a8, 0xfffffe6f, 0xfffffcc0, 0x00021e00, + 0x000004a8, 0x00000812, 0x00000000, 0xfffbac80, + }, + { + 0x00000400, 0x00000000, 0x0000059c, 0xfffd31ff, + 0x00000400, 0xfffffe9f, 0xfffffd24, 0x00021d80, + 0x00000400, 0x00000717, 0x00000000, 0xfffc747f, + }, +}; + +const uint32_t g2d_csc_yuv2rgb_709[2][G2D_CSC_WORDS] = { + { + 0x000004a8, 0x00000000, 0x0000072c, 0xfffc1f80, + 0x000004a8, 0xffffff26, 0xfffffdde, 0x00013380, + 0x000004a8, 0x00000873, 0x00000000, 0xfffb7c00, + }, + { + 0x00000400, 0x00000000, 0x0000064d, 0xfffc9980, + 0x00000400, 0xffffff40, 0xfffffe21, 0x00010f80, + 0x00000400, 0x0000076c, 0x00000000, 0xfffc0a00, + }, +}; diff --git a/bsp/allwinner/dm/soc/jpegd-sunxi.c b/bsp/allwinner/dm/soc/jpegd-sunxi.c new file mode 100644 index 000000000000..f0f9c0b27335 --- /dev/null +++ b/bsp/allwinner/dm/soc/jpegd-sunxi.c @@ -0,0 +1,2315 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +#define DBG_TAG "jpegd.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include +#include +#include + +#define SUNXI_VE_MODE 0x0000 +#define SUNXI_VE_VERSION 0x00f0 +#define SUNXI_VE_MODE_DISABLED 0x0007 +/* F101 selects the MPEG decoder with the SoC-specific mode bit set. */ +#define SUNXI_VE_MODE_DEC_MPEG RT_BIT(3) +#define SUNXI_VE_MODE_DEC_PNG 0x000e +#define SUNXI_VE_MODE_ENC_AVC 0x000b +#define SUNXI_VE_MODE_ENC_ISP RT_BIT(6) +#define SUNXI_VE_MODE_ENC_AVC_ENABLE RT_BIT(7) +#define SUNXI_VE_MODE_REC_WR_MODE_2MB RT_BIT(20) +#define SUNXI_VE_MODE_DDR_MODE_BW_128 (3U << 16) + +#define SUNXI_VE_TOP_PRI_CHROMA_BUF_LEN 0x00c4 +#define SUNXI_VE_TOP_PRI_BUF_LINE_STRIDE 0x00c8 +#define SUNXI_VE_TOP_SEC_BUF_LINE_STRIDE 0x00cc +#define SUNXI_VE_TOP_CHROMA_BUF_LEN0 0x00e8 +#define SUNXI_VE_TOP_OUTPUT_FORMAT 0x00ec + +#define SUNXI_VE_MPEG_OFFSET 0x0100 +#define SUNXI_VE_MPEG_CTRL (SUNXI_VE_MPEG_OFFSET + 0x14) +#define SUNXI_VE_MPEG_TRIGGER (SUNXI_VE_MPEG_OFFSET + 0x18) +#define SUNXI_VE_MPEG_STATUS (SUNXI_VE_MPEG_OFFSET + 0x1c) +#define SUNXI_VE_MPEG_VLD_ADDR (SUNXI_VE_MPEG_OFFSET + 0x28) +#define SUNXI_VE_MPEG_VLD_OFFSET (SUNXI_VE_MPEG_OFFSET + 0x2c) +#define SUNXI_VE_MPEG_VLD_LENGTH (SUNXI_VE_MPEG_OFFSET + 0x30) +#define SUNXI_VE_MPEG_VLD_END (SUNXI_VE_MPEG_OFFSET + 0x34) +#define SUNXI_VE_MPEG_IQ_INPUT (SUNXI_VE_MPEG_OFFSET + 0x80) +#define SUNXI_VE_MPEG_JPEG_SIZE (SUNXI_VE_MPEG_OFFSET + 0xb8) +#define SUNXI_VE_MPEG_JPEG_RESTART (SUNXI_VE_MPEG_OFFSET + 0xc0) +#define SUNXI_VE_MPEG_JPEG_LUMA (SUNXI_VE_MPEG_OFFSET + 0xcc) +#define SUNXI_VE_MPEG_JPEG_CHROMA (SUNXI_VE_MPEG_OFFSET + 0xd0) +#define SUNXI_VE_MPEG_JPEG_SCALE (SUNXI_VE_MPEG_OFFSET + 0xd4) +#define SUNXI_VE_MPEG_SRAM_OFFSET (SUNXI_VE_MPEG_OFFSET + 0xe0) +#define SUNXI_VE_MPEG_SRAM_DATA (SUNXI_VE_MPEG_OFFSET + 0xe4) + +#define SUNXI_VE_MPEG_CTRL_JPEG_FORCE_420 RT_BIT(18) +#define SUNXI_VE_MPEG_CTRL_IRQ_MASK (RT_BIT(5) | RT_BIT(4) | RT_BIT(3)) +#define SUNXI_VE_MPEG_CTRL_F101_420 (RT_BIT(8) | RT_BIT(7) | \ + RT_BIT(6) | RT_BIT(5) | \ + RT_BIT(4) | RT_BIT(3)) +#define SUNXI_VE_MPEG_STATUS_MASK (RT_BIT(3) | RT_BIT(2) | \ + RT_BIT(1) | RT_BIT(0)) +#define SUNXI_VE_MPEG_VLD_FIRST RT_BIT(30) +#define SUNXI_VE_MPEG_VLD_LAST RT_BIT(29) +#define SUNXI_VE_MPEG_VLD_VALID RT_BIT(28) +#define SUNXI_VE_MPEG_IQ_LUMA RT_BIT(14) +#define SUNXI_VE_MPEG_TRIGGER_JPEG (3U << 24) +#define SUNXI_VE_MPEG_TRIGGER_JPEG_VLD 0x0e +#define SUNXI_VE_MPEG_TRIGGER_420 (0U << 27) +#define SUNXI_VE_MPEG_TRIGGER_422 (2U << 27) +#define SUNXI_VE_MPEG_TRIGGER_444 (3U << 27) +#define SUNXI_VE_MPEG_TRIGGER_422T (4U << 27) + +#define SUNXI_VE_ISP_OFFSET 0x0a00 +#define SUNXI_VE_ISP_INPUT_SIZE (SUNXI_VE_ISP_OFFSET + 0x00) +#define SUNXI_VE_ISP_INPUT_STRIDE (SUNXI_VE_ISP_OFFSET + 0x04) +#define SUNXI_VE_ISP_CTRL (SUNXI_VE_ISP_OFFSET + 0x08) +#define SUNXI_VE_ISP_INPUT_LUMA (SUNXI_VE_ISP_OFFSET + 0x78) +#define SUNXI_VE_ISP_INPUT_CHROMA (SUNXI_VE_ISP_OFFSET + 0x7c) + +#define SUNXI_VE_AVC_OFFSET 0x0b00 +#define SUNXI_VE_AVC_JPEG_CTRL (SUNXI_VE_AVC_OFFSET + 0x04) +#define SUNXI_VE_AVC_CTRL (SUNXI_VE_AVC_OFFSET + 0x14) +#define SUNXI_VE_AVC_TRIGGER (SUNXI_VE_AVC_OFFSET + 0x18) +#define SUNXI_VE_AVC_STATUS (SUNXI_VE_AVC_OFFSET + 0x1c) +#define SUNXI_VE_AVC_VLE_ADDR (SUNXI_VE_AVC_OFFSET + 0x80) +#define SUNXI_VE_AVC_VLE_END (SUNXI_VE_AVC_OFFSET + 0x84) +#define SUNXI_VE_AVC_VLE_OFFSET (SUNXI_VE_AVC_OFFSET + 0x88) +#define SUNXI_VE_AVC_VLE_MAX (SUNXI_VE_AVC_OFFSET + 0x8c) +#define SUNXI_VE_AVC_VLE_LENGTH (SUNXI_VE_AVC_OFFSET + 0x90) +#define SUNXI_VE_AVC_SRAM_OFFSET (SUNXI_VE_AVC_OFFSET + 0xe0) +#define SUNXI_VE_AVC_SRAM_DATA (SUNXI_VE_AVC_OFFSET + 0xe4) + +#define SUNXI_VE_AVC_CTRL_IRQ_ENABLE RT_BIT(7) +#define SUNXI_VE_AVC_TRIGGER_JPEG RT_BIT(16) +#define SUNXI_VE_AVC_TRIGGER_LAUNCH 0x08 +#define SUNXI_VE_AVC_STATUS_SUCCESS RT_BIT(0) +#define SUNXI_VE_AVC_STATUS_ERROR RT_BIT(1) +#define SUNXI_VE_AVC_STATUS_MASK 0x07 +#define SUNXI_VE_AVC_JPEG_FILL_ONE RT_BIT(31) +#define SUNXI_VE_AVC_JPEG_BYTE_STUFF RT_BIT(30) + +#define SUNXI_VE_PNG_OFFSET 0x0600 +#define SUNXI_VE_PNG_CTRL (SUNXI_VE_PNG_OFFSET + 0x00) +#define SUNXI_VE_PNG_INT_EN (SUNXI_VE_PNG_OFFSET + 0x04) +#define SUNXI_VE_PNG_INT_STATUS (SUNXI_VE_PNG_OFFSET + 0x0c) +#define SUNXI_VE_PNG_STATUS (SUNXI_VE_PNG_OFFSET + 0x10) +#define SUNXI_VE_PNG_RX_ADDR (SUNXI_VE_PNG_OFFSET + 0x14) +#define SUNXI_VE_PNG_RX_LEN (SUNXI_VE_PNG_OFFSET + 0x18) +#define SUNXI_VE_PNG_TX_ADDR (SUNXI_VE_PNG_OFFSET + 0x1c) +#define SUNXI_VE_PNG_TX_LEN (SUNXI_VE_PNG_OFFSET + 0x20) + +#define SUNXI_VE_PNG_CTRL_START RT_BIT(0) +#define SUNXI_VE_PNG_INT_DONE RT_BIT(0) +#define SUNXI_VE_PNG_INT_ERROR RT_BIT(1) +#define SUNXI_VE_PNG_INT_MASK (SUNXI_VE_PNG_INT_DONE | \ + SUNXI_VE_PNG_INT_ERROR) + +#define SUNXI_JPEGD_BUFFER_ALIGN 1024 +#define SUNXI_JPEGD_MAX_WIDTH 2048 +#define SUNXI_JPEGD_MAX_HEIGHT 2048 +#define SUNXI_JPEGD_DEFAULT_TIMEOUT_MS 500 +#define SUNXI_JPEGE_MAX_WIDTH 8192 +#define SUNXI_JPEGE_MAX_HEIGHT 8192 +#define SUNXI_JPEGE_MIN_OUTPUT_SIZE (8 * 1024) +#define SUNXI_JPEGE_HEADER_SIZE 1024 +#define SUNXI_PNGD_DEFAULT_TIMEOUT_MS 1000 + +#define JPEG_MARKER_SOI 0xd8 +#define JPEG_MARKER_EOI 0xd9 +#define JPEG_MARKER_SOS 0xda +#define JPEG_MARKER_DQT 0xdb +#define JPEG_MARKER_DHT 0xc4 +#define JPEG_MARKER_DRI 0xdd +#define JPEG_MARKER_SOF0 0xc0 + +struct sunxi_jpegd_buffer_entry +{ + rt_list_t list; + struct sunxi_jpegd_buffer buffer; + void *alloc_addr; + rt_ubase_t alloc_phys; + rt_size_t alloc_size; +}; + +struct sunxi_jpegd_quant_table +{ + rt_uint16_t value[64]; + rt_bool_t valid; +}; + +struct sunxi_jpegd_huffman_table +{ + rt_uint8_t counts[16]; + rt_uint8_t symbols[256]; + rt_uint16_t symbol_count; + rt_bool_t valid; +}; + +struct sunxi_jpegd_component +{ + rt_uint8_t id; + rt_uint8_t hfactor; + rt_uint8_t vfactor; + rt_uint8_t quant_table; + rt_uint8_t dc_table; + rt_uint8_t ac_table; +}; + +struct sunxi_jpegd_header +{ + struct sunxi_jpegd_quant_table quant[4]; + struct sunxi_jpegd_huffman_table huffman[2][2]; + struct sunxi_jpegd_component component[3]; + rt_size_t entropy_offset; + rt_size_t eoi_offset; + rt_uint16_t width; + rt_uint16_t height; + rt_uint16_t restart_interval; + rt_uint8_t precision; + rt_uint8_t component_count; +}; + +static rt_size_t sunxi_jpegd_find_eoi(const rt_uint8_t *data, rt_size_t size, + rt_size_t position) +{ + while (position + 1 < size) + { + if (data[position++] != 0xff) + { + continue; + } + if (data[position] == 0x00 || + (data[position] >= 0xd0 && data[position] <= 0xd7)) + { + ++position; + continue; + } + if (data[position] == JPEG_MARKER_EOI) + { + return position - 1; + } + } + + return size; +} + +enum sunxi_jpegd_engine +{ + SUNXI_JPEGD_ENGINE_IDLE, + SUNXI_JPEGD_ENGINE_DECODE, + SUNXI_JPEGD_ENGINE_ENCODE, + SUNXI_JPEGD_ENGINE_PNG_DECODE, +}; + +struct sunxi_jpege_header +{ + rt_uint8_t data[SUNXI_JPEGE_HEADER_SIZE]; + rt_size_t size; +}; + +struct sunxi_jpegd +{ + struct rt_device parent; + struct rt_device encoder; + struct rt_device png_decoder; + struct rt_device *dev; + struct rt_clk_array *clks; + struct rt_reset_control *resets; + void *base; + int irq; + + struct rt_mutex lock; + struct rt_completion completion; + rt_list_t buffers; + rt_uint32_t version; + rt_uint32_t irq_status; + rt_err_t error; + rt_bool_t clocks_enabled; + rt_bool_t lock_initialized; + rt_bool_t device_registered; + rt_bool_t encoder_registered; + rt_bool_t png_registered; + rt_bool_t irq_installed; + rt_bool_t running; + enum sunxi_jpegd_engine engine; +}; + +static rt_err_t sunxi_jpegd_claim_sram(struct sunxi_jpegd *jpegd) +{ + struct rt_ofw_node *section_np; + struct rt_ofw_node *sram_np; + struct rt_ofw_node *syscon_np; + struct rt_syscon *syscon; + rt_err_t err = -RT_EINVAL; + + section_np = rt_ofw_parse_phandle(jpegd->dev->ofw_node, + "allwinner,sram", 0); + if (!section_np) + { + return -RT_EINVAL; + } + + if (!rt_ofw_node_is_compatible(section_np, + "allwinner,sun252i-f101-sram-ve")) + { + goto _out_section; + } + + sram_np = rt_ofw_get_parent(section_np); + syscon_np = rt_ofw_get_parent(sram_np); + if (!sram_np || !syscon_np) + { + goto _out_parent; + } + + /* Clear VE_SRAM_BMODE_CTRL so the DT-described SRAM belongs to VE. */ + syscon = rt_syscon_find_by_ofw_node(syscon_np); + if (syscon) + { + err = rt_syscon_update_bits(syscon, 0x4, RT_BIT(24), 0); + } + + rt_ofw_node_put(syscon_np); +_out_parent: + rt_ofw_node_put(sram_np); +_out_section: + rt_ofw_node_put(section_np); + + return err; +} + +static const rt_uint8_t sunxi_jpegd_default_dc_luma_counts[16] = +{ + 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, +}; + +static const rt_uint8_t sunxi_jpegd_default_dc_chroma_counts[16] = +{ + 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, +}; + +static const rt_uint8_t sunxi_jpegd_default_dc_values[12] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +}; + +static const rt_uint8_t sunxi_jpegd_default_ac_luma_counts[16] = +{ + 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125, +}; + +static const rt_uint8_t sunxi_jpegd_default_ac_luma_values[162] = +{ + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, + 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, + 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, + 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, +}; + +static const rt_uint8_t sunxi_jpegd_default_ac_chroma_counts[16] = +{ + 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119, +}; + +static const rt_uint8_t sunxi_jpegd_default_ac_chroma_values[162] = +{ + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, + 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, + 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, + 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, + 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, +}; + +static const rt_uint8_t sunxi_jpege_luma_quant[64] = +{ + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99, +}; + +static const rt_uint8_t sunxi_jpege_chroma_quant[64] = +{ + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, +}; + +static const rt_uint8_t sunxi_jpege_zigzag[64] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, +}; + +#define raw_to_sunxi_jpegd(raw) \ + rt_container_of(raw, struct sunxi_jpegd, parent) +#define raw_to_sunxi_jpege(raw) \ + rt_container_of(raw, struct sunxi_jpegd, encoder) +#define raw_to_sunxi_pngd(raw) \ + rt_container_of(raw, struct sunxi_jpegd, png_decoder) + +static rt_uint32_t sunxi_jpegd_readl(struct sunxi_jpegd *jpegd, + rt_uint32_t offset) +{ + return HWREG32(jpegd->base + offset); +} + +static void sunxi_jpegd_writel(struct sunxi_jpegd *jpegd, + rt_uint32_t offset, rt_uint32_t value) +{ + HWREG32(jpegd->base + offset) = value; +} + +static rt_err_t sunxi_jpegd_read_be16(const rt_uint8_t *data, + rt_size_t size, rt_size_t *position, rt_uint16_t *value) +{ + if (*position > size || size - *position < 2) + { + return -RT_EINVAL; + } + + *value = ((rt_uint16_t)data[*position] << 8) | + data[*position + 1]; + *position += 2; + + return RT_EOK; +} + +static rt_err_t sunxi_jpegd_parse_dqt(const rt_uint8_t *data, + rt_size_t start, rt_size_t end, struct sunxi_jpegd_header *header) +{ + struct sunxi_jpegd_quant_table *table; + rt_uint16_t value; + rt_size_t position = start; + rt_uint8_t selector; + rt_uint8_t precision; + rt_uint8_t index; + int i; + + while (position < end) + { + selector = data[position++]; + precision = selector >> 4; + index = selector & 0x0f; + if (precision > 1 || index >= RT_ARRAY_SIZE(header->quant) || + position > end || end - position < (precision ? 128 : 64)) + { + return -RT_EINVAL; + } + + table = &header->quant[index]; + for (i = 0; i < RT_ARRAY_SIZE(table->value); i++) + { + if (precision) + { + if (sunxi_jpegd_read_be16(data, end, &position, &value)) + { + return -RT_EINVAL; + } + } + else + { + value = data[position++]; + } + + if (!value || value > 0xff) + { + return -RT_EINVAL; + } + table->value[i] = value; + } + table->valid = RT_TRUE; + } + + return position == end ? RT_EOK : -RT_EINVAL; +} + +static rt_err_t sunxi_jpegd_parse_dht(const rt_uint8_t *data, + rt_size_t start, rt_size_t end, struct sunxi_jpegd_header *header) +{ + struct sunxi_jpegd_huffman_table *table; + rt_size_t position = start; + rt_uint16_t count; + rt_uint8_t selector; + rt_uint8_t table_class; + rt_uint8_t table_id; + int i; + + while (position < end) + { + if (end - position < 17) + { + return -RT_EINVAL; + } + + selector = data[position++]; + table_class = selector >> 4; + table_id = selector & 0x0f; + if (table_class > 1 || table_id > 1) + { + return -RT_EINVAL; + } + + table = &header->huffman[table_class][table_id]; + count = 0; + for (i = 0; i < RT_ARRAY_SIZE(table->counts); i++) + { + table->counts[i] = data[position++]; + count += table->counts[i]; + } + if (count > RT_ARRAY_SIZE(table->symbols) || position > end || + end - position < count) + { + return -RT_EINVAL; + } + + rt_memcpy(table->symbols, &data[position], count); + table->symbol_count = count; + table->valid = RT_TRUE; + position += count; + } + + return position == end ? RT_EOK : -RT_EINVAL; +} + +static rt_err_t sunxi_jpegd_parse_sof0(const rt_uint8_t *data, + rt_size_t start, rt_size_t end, struct sunxi_jpegd_header *header) +{ + struct sunxi_jpegd_component *component; + rt_size_t position = start; + rt_uint16_t width; + rt_uint16_t height; + rt_uint8_t count; + rt_uint8_t sampling; + int i; + + if (end - start < 6) + { + return -RT_EINVAL; + } + + header->precision = data[position++]; + if (sunxi_jpegd_read_be16(data, end, &position, &height) || + sunxi_jpegd_read_be16(data, end, &position, &width)) + { + return -RT_EINVAL; + } + + count = data[position++]; + if (count != RT_ARRAY_SIZE(header->component) || + end - position != (rt_size_t)count * 3) + { + return -RT_EINVAL; + } + + for (i = 0; i < count; i++) + { + component = &header->component[i]; + component->id = data[position++]; + sampling = data[position++]; + component->hfactor = sampling >> 4; + component->vfactor = sampling & 0x0f; + component->quant_table = data[position++]; + if (!component->hfactor || !component->vfactor || + component->quant_table >= RT_ARRAY_SIZE(header->quant)) + { + return -RT_EINVAL; + } + } + + header->width = width; + header->height = height; + header->component_count = count; + + return RT_EOK; +} + +static rt_err_t sunxi_jpegd_parse_sos(const rt_uint8_t *data, + rt_size_t start, rt_size_t end, struct sunxi_jpegd_header *header) +{ + struct sunxi_jpegd_component *component; + rt_size_t position = start; + rt_uint8_t count; + rt_uint8_t component_id; + rt_uint8_t tables; + rt_uint8_t scan_start; + rt_uint8_t scan_end; + rt_uint8_t successive; + int index; + int i; + int j; + + if (end - start < 4 || !header->component_count) + { + return -RT_EINVAL; + } + + count = data[position++]; + if (count != header->component_count || + end - position < (rt_size_t)count * 2 + 3) + { + return -RT_EINVAL; + } + + for (i = 0; i < count; i++) + { + component_id = data[position++]; + tables = data[position++]; + index = -1; + for (j = 0; j < header->component_count; j++) + { + if (header->component[j].id == component_id) + { + index = j; + break; + } + } + if (index < 0 || (tables >> 4) > 1 || (tables & 0x0f) > 1) + { + return -RT_EINVAL; + } + + component = &header->component[index]; + component->dc_table = tables >> 4; + component->ac_table = tables & 0x0f; + } + + scan_start = data[position++]; + scan_end = data[position++]; + successive = data[position++]; + if (position != end || scan_start || scan_end != 63 || successive) + { + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static void sunxi_jpegd_set_huffman_table(struct sunxi_jpegd_huffman_table *table, + const rt_uint8_t *counts, const rt_uint8_t *symbols, + rt_uint16_t symbol_count) +{ + if (table->valid) + { + return; + } + + rt_memcpy(table->counts, counts, sizeof(table->counts)); + rt_memcpy(table->symbols, symbols, symbol_count); + table->symbol_count = symbol_count; + table->valid = RT_TRUE; +} + +static void sunxi_jpegd_set_default_huffman_tables( + struct sunxi_jpegd_header *header) +{ + sunxi_jpegd_set_huffman_table(&header->huffman[0][0], + sunxi_jpegd_default_dc_luma_counts, + sunxi_jpegd_default_dc_values, + sizeof(sunxi_jpegd_default_dc_values)); + sunxi_jpegd_set_huffman_table(&header->huffman[1][0], + sunxi_jpegd_default_ac_luma_counts, + sunxi_jpegd_default_ac_luma_values, + sizeof(sunxi_jpegd_default_ac_luma_values)); + sunxi_jpegd_set_huffman_table(&header->huffman[0][1], + sunxi_jpegd_default_dc_chroma_counts, + sunxi_jpegd_default_dc_values, + sizeof(sunxi_jpegd_default_dc_values)); + sunxi_jpegd_set_huffman_table(&header->huffman[1][1], + sunxi_jpegd_default_ac_chroma_counts, + sunxi_jpegd_default_ac_chroma_values, + sizeof(sunxi_jpegd_default_ac_chroma_values)); +} + +static rt_err_t sunxi_jpegd_parse_header(const rt_uint8_t *data, + rt_size_t size, struct sunxi_jpegd_header *header) +{ + rt_size_t position; + rt_size_t segment_start; + rt_size_t segment_end; + rt_uint16_t segment_length; + rt_uint8_t marker; + rt_err_t err; + + if (!data || size < 4 || data[0] != 0xff || data[1] != JPEG_MARKER_SOI) + { + LOG_E("invalid JPEG SOI: size=%u first=%02x%02x", size, + data && size ? data[0] : 0, + data && size > 1 ? data[1] : 0); + return -RT_EINVAL; + } + + rt_memset(header, 0, sizeof(*header)); + position = 2; + while (position < size) + { + while (position < size && data[position] != 0xff) + { + position++; + } + while (position < size && data[position] == 0xff) + { + position++; + } + if (position >= size) + { + LOG_E("JPEG reaches EOF before SOS"); + return -RT_EINVAL; + } + + marker = data[position++]; + if (marker == 0x00 || (marker >= 0xd0 && marker <= 0xd7)) + { + continue; + } + if (marker == JPEG_MARKER_EOI) + { + LOG_E("JPEG EOI appears before SOS"); + return -RT_EINVAL; + } + if (sunxi_jpegd_read_be16(data, size, &position, &segment_length) || + segment_length < 2 || position > size || + size - position < segment_length - 2) + { + LOG_E("invalid JPEG segment %02x at %u", marker, position - 1); + return -RT_EINVAL; + } + + segment_start = position; + segment_end = position + segment_length - 2; + switch (marker) + { + case JPEG_MARKER_DQT: + err = sunxi_jpegd_parse_dqt(data, segment_start, segment_end, + header); + break; + case JPEG_MARKER_DHT: + err = sunxi_jpegd_parse_dht(data, segment_start, segment_end, + header); + break; + case JPEG_MARKER_DRI: + if (segment_end - segment_start != 2) + { + LOG_E("invalid JPEG DRI length %u", segment_end - segment_start); + return -RT_EINVAL; + } + header->restart_interval = ((rt_uint16_t)data[segment_start] << 8) | + data[segment_start + 1]; + err = RT_EOK; + break; + case JPEG_MARKER_SOF0: + err = sunxi_jpegd_parse_sof0(data, segment_start, segment_end, + header); + break; + case JPEG_MARKER_SOS: + err = sunxi_jpegd_parse_sos(data, segment_start, segment_end, + header); + if (err) + { + LOG_E("invalid JPEG SOS: %d", err); + return err; + } + header->entropy_offset = segment_end; + header->eoi_offset = sunxi_jpegd_find_eoi(data, size, + header->entropy_offset); + if (header->eoi_offset == size) + { + LOG_E("JPEG reaches EOF before EOI"); + return -RT_EINVAL; + } + goto _validate; + default: + if ((marker >= 0xc0 && marker <= 0xcf) || + (marker >= 0xd0 && marker <= 0xdf)) + { + return -RT_ENOSYS; + } + err = RT_EOK; + break; + } + if (err) + { + LOG_E("invalid JPEG marker %02x: %d", marker, err); + return err; + } + position = segment_end; + } + + return -RT_EINVAL; + +_validate: + sunxi_jpegd_set_default_huffman_tables(header); + + if (header->precision != 8 || !header->width || !header->height || + header->width > SUNXI_JPEGD_MAX_WIDTH || + header->height > SUNXI_JPEGD_MAX_HEIGHT || !header->entropy_offset || + !header->quant[header->component[0].quant_table].valid || + !header->quant[header->component[1].quant_table].valid || + !header->huffman[0][header->component[0].dc_table].valid || + !header->huffman[1][header->component[0].ac_table].valid || + !header->huffman[0][header->component[1].dc_table].valid || + !header->huffman[1][header->component[1].ac_table].valid) + { + LOG_E("invalid JPEG frame: %ux%u precision=%u", header->width, + header->height, header->precision); + return -RT_EINVAL; + } + + if (header->component[1].quant_table != header->component[2].quant_table || + header->component[1].dc_table != header->component[2].dc_table || + header->component[1].ac_table != header->component[2].ac_table) + { + return -RT_ENOSYS; + } + + LOG_D("JPEG header: %ux%u precision=%u entropy=%u..%u restart=%u", + header->width, header->height, header->precision, + (rt_uint32_t)header->entropy_offset, + (rt_uint32_t)header->eoi_offset, header->restart_interval); + + return RT_EOK; +} + +static rt_err_t sunxi_jpegd_sampling(const struct sunxi_jpegd_header *header, + rt_uint32_t *sampling) +{ + const struct sunxi_jpegd_component *y = &header->component[0]; + const struct sunxi_jpegd_component *cb = &header->component[1]; + const struct sunxi_jpegd_component *cr = &header->component[2]; + + if (y->hfactor == 2 && y->vfactor == 2 && cb->hfactor == 1 && + cb->vfactor == 1 && cr->hfactor == 1 && cr->vfactor == 1) + { + *sampling = SUNXI_VE_MPEG_TRIGGER_420; + } + else if (y->hfactor == 2 && y->vfactor == 1 && cb->hfactor == 1 && + cb->vfactor == 1 && cr->hfactor == 1 && cr->vfactor == 1) + { + *sampling = SUNXI_VE_MPEG_TRIGGER_422; + } + else if (y->hfactor == 1 && y->vfactor == 1 && cb->hfactor == 1 && + cb->vfactor == 1 && cr->hfactor == 1 && cr->vfactor == 1) + { + *sampling = SUNXI_VE_MPEG_TRIGGER_444; + } + else if (y->hfactor == 1 && y->vfactor == 2 && cb->hfactor == 1 && + cb->vfactor == 1 && cr->hfactor == 1 && cr->vfactor == 1) + { + *sampling = SUNXI_VE_MPEG_TRIGGER_422T; + } + else + { + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static rt_err_t sunxi_jpegd_configure_output(struct sunxi_jpegd *jpegd, + const struct sunxi_jpegd_header *header, rt_uint32_t sampling, + struct sunxi_jpegd_buffer_entry *output, rt_size_t *out_stride, + rt_size_t *out_luma_size, rt_size_t *out_size) +{ + rt_size_t stride; + rt_size_t aligned_height; + rt_size_t luma_size; + rt_size_t chroma_size; + + if (sampling != SUNXI_VE_MPEG_TRIGGER_420) + { + return -RT_ENOSYS; + } + + stride = RT_ALIGN(header->width, 32); + aligned_height = RT_ALIGN(header->height, 32); + luma_size = stride * aligned_height; + chroma_size = luma_size / 4; + + if (output->buffer.size < luma_size + chroma_size * 2 || + (output->buffer.phys_addr & (SUNXI_JPEGD_BUFFER_ALIGN - 1))) + { + return -RT_ENOMEM; + } + + /* The F101 VE writes a 32-pixel aligned YV12 image. */ + sunxi_jpegd_writel(jpegd, SUNXI_VE_TOP_OUTPUT_FORMAT, 0x33); + sunxi_jpegd_writel(jpegd, SUNXI_VE_TOP_CHROMA_BUF_LEN0, + chroma_size | RT_BIT(29) | (3U << 30)); + sunxi_jpegd_writel(jpegd, SUNXI_VE_TOP_PRI_CHROMA_BUF_LEN, chroma_size); + sunxi_jpegd_writel(jpegd, SUNXI_VE_TOP_PRI_BUF_LINE_STRIDE, + ((rt_uint32_t)(stride >> 1) << 16) | stride); + sunxi_jpegd_writel(jpegd, SUNXI_VE_TOP_SEC_BUF_LINE_STRIDE, + ((rt_uint32_t)(stride >> 1) << 16) | stride); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_JPEG_LUMA, + output->buffer.phys_addr); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_JPEG_CHROMA, + output->buffer.phys_addr + luma_size); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_JPEG_SCALE, 0); + + *out_stride = stride; + *out_luma_size = luma_size; + *out_size = luma_size + chroma_size * 2; + + return RT_EOK; +} + +static void sunxi_jpegd_write_huffman_header(struct sunxi_jpegd *jpegd, + const struct sunxi_jpegd_huffman_table *table) +{ + rt_uint16_t start_code[16]; + rt_uint8_t offset[16]; + rt_uint32_t code; + rt_uint32_t count; + rt_uint32_t value; + int i; + + code = 0; + count = 0; + for (i = 0; i < RT_ARRAY_SIZE(start_code); i++) + { + start_code[i] = code; + offset[i] = count; + count += table->counts[i]; + code = (code + table->counts[i]) << 1; + } + for (i = RT_ARRAY_SIZE(start_code) - 1; i >= 0 && !table->counts[i]; i--) + { + start_code[i] = 0xffff; + } + + for (i = 0; i < 8; i++) + { + value = start_code[i * 2] | ((rt_uint32_t)start_code[i * 2 + 1] << 16); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_SRAM_DATA, value); + } + for (i = 0; i < 4; i++) + { + value = offset[i * 4] | ((rt_uint32_t)offset[i * 4 + 1] << 8) | + ((rt_uint32_t)offset[i * 4 + 2] << 16) | + ((rt_uint32_t)offset[i * 4 + 3] << 24); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_SRAM_DATA, value); + } + for (i = 0; i < 4; i++) + { + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_SRAM_DATA, 0); + } +} + +static void sunxi_jpegd_write_huffman_data(struct sunxi_jpegd *jpegd, + const struct sunxi_jpegd_huffman_table *table) +{ + rt_uint32_t value; + int i; + int j; + + for (i = 0; i < 64; i++) + { + value = 0; + for (j = 0; j < 4; j++) + { + if ((rt_uint16_t)(i * 4 + j) < table->symbol_count) + { + value |= (rt_uint32_t)table->symbols[i * 4 + j] << (j * 8); + } + } + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_SRAM_DATA, value); + } +} + +static void sunxi_jpegd_write_huffman_tables(struct sunxi_jpegd *jpegd, + const struct sunxi_jpegd_header *header) +{ + const struct sunxi_jpegd_component *y = &header->component[0]; + const struct sunxi_jpegd_component *c = &header->component[1]; + const struct sunxi_jpegd_huffman_table *table[4]; + int i; + + table[0] = &header->huffman[0][y->dc_table]; + table[1] = &header->huffman[1][y->ac_table]; + table[2] = &header->huffman[0][c->dc_table]; + table[3] = &header->huffman[1][c->ac_table]; + + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_SRAM_OFFSET, 0); + for (i = 0; i < RT_ARRAY_SIZE(table); i++) + { + sunxi_jpegd_write_huffman_header(jpegd, table[i]); + } + for (i = 0; i < 192; i++) + { + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_SRAM_DATA, 0); + } + for (i = 0; i < RT_ARRAY_SIZE(table); i++) + { + sunxi_jpegd_write_huffman_data(jpegd, table[i]); + } +} + +static void sunxi_jpegd_write_quant_table(struct sunxi_jpegd *jpegd, + const struct sunxi_jpegd_quant_table *table, rt_uint32_t flags) +{ + rt_uint32_t value; + int i; + + for (i = 0; i < RT_ARRAY_SIZE(table->value); i++) + { + value = ((rt_uint32_t)i << 8) | table->value[i] | flags; + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_IQ_INPUT, value); + } +} + +static rt_err_t sunxi_jpege_header_put(struct sunxi_jpege_header *header, + const void *data, rt_size_t size) +{ + if (size > sizeof(header->data) - header->size) + { + return -RT_ENOMEM; + } + + rt_memcpy(header->data + header->size, data, size); + header->size += size; + + return RT_EOK; +} + +static rt_err_t sunxi_jpege_header_put_u8(struct sunxi_jpege_header *header, + rt_uint8_t value) +{ + return sunxi_jpege_header_put(header, &value, sizeof(value)); +} + +static rt_err_t sunxi_jpege_header_put_be16(struct sunxi_jpege_header *header, + rt_uint16_t value) +{ + rt_uint8_t data[2]; + + data[0] = value >> 8; + data[1] = value; + + return sunxi_jpege_header_put(header, data, sizeof(data)); +} + +static void sunxi_jpege_scale_quant_table(rt_uint16_t *scaled, + const rt_uint8_t *base, rt_uint8_t quality) +{ + rt_uint32_t scale; + rt_uint32_t value; + int i; + + scale = quality < 50 ? 5000 / quality : 200 - quality * 2; + for (i = 0; i < 64; i++) + { + value = (base[i] * scale + 50) / 100; + scaled[i] = value < 1 ? 1 : (value > 255 ? 255 : value); + } +} + +static rt_err_t sunxi_jpege_write_dht(struct sunxi_jpege_header *header, + rt_uint8_t table_id, const rt_uint8_t *counts, + const rt_uint8_t *values, rt_size_t value_count) +{ + rt_err_t err; + + err = sunxi_jpege_header_put_u8(header, table_id); + if (err) + { + return err; + } + err = sunxi_jpege_header_put(header, counts, 16); + if (err) + { + return err; + } + + return sunxi_jpege_header_put(header, values, value_count); +} + +static rt_err_t sunxi_jpege_build_header(struct sunxi_jpege_header *header, + rt_uint16_t width, rt_uint16_t height, rt_uint16_t *luma_quant, + rt_uint16_t *chroma_quant) +{ + static const rt_uint8_t sof0[] = + { + 0xff, 0xc0, 0x00, 0x11, 0x08, + }; + static const rt_uint8_t components[] = + { + 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, + }; + static const rt_uint8_t sos[] = + { + 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, + 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, + }; + rt_err_t err; + int i; + + header->size = 0; + err = sunxi_jpege_header_put_be16(header, 0xffd8); + if (err) + { + return err; + } + err = sunxi_jpege_header_put_be16(header, 0xffdb); + if (err) + { + return err; + } + err = sunxi_jpege_header_put_be16(header, 2 + 2 * (1 + 64)); + if (err) + { + return err; + } + err = sunxi_jpege_header_put_u8(header, 0); + if (err) + { + return err; + } + for (i = 0; i < 64; i++) + { + err = sunxi_jpege_header_put_u8(header, + luma_quant[sunxi_jpege_zigzag[i]]); + if (err) + { + return err; + } + } + err = sunxi_jpege_header_put_u8(header, 1); + if (err) + { + return err; + } + for (i = 0; i < 64; i++) + { + err = sunxi_jpege_header_put_u8(header, + chroma_quant[sunxi_jpege_zigzag[i]]); + if (err) + { + return err; + } + } + err = sunxi_jpege_header_put(header, sof0, sizeof(sof0)); + if (err) + { + return err; + } + err = sunxi_jpege_header_put_be16(header, height); + if (err) + { + return err; + } + err = sunxi_jpege_header_put_be16(header, width); + if (err) + { + return err; + } + err = sunxi_jpege_header_put(header, components, sizeof(components)); + if (err) + { + return err; + } + err = sunxi_jpege_header_put_be16(header, 0xffc4); + if (err) + { + return err; + } + err = sunxi_jpege_header_put_be16(header, 0x01a2); + if (err) + { + return err; + } + err = sunxi_jpege_write_dht(header, 0x00, + sunxi_jpegd_default_dc_luma_counts, + sunxi_jpegd_default_dc_values, + sizeof(sunxi_jpegd_default_dc_values)); + if (err) + { + return err; + } + err = sunxi_jpege_write_dht(header, 0x10, + sunxi_jpegd_default_ac_luma_counts, + sunxi_jpegd_default_ac_luma_values, + sizeof(sunxi_jpegd_default_ac_luma_values)); + if (err) + { + return err; + } + err = sunxi_jpege_write_dht(header, 0x01, + sunxi_jpegd_default_dc_chroma_counts, + sunxi_jpegd_default_dc_values, + sizeof(sunxi_jpegd_default_dc_values)); + if (err) + { + return err; + } + err = sunxi_jpege_write_dht(header, 0x11, + sunxi_jpegd_default_ac_chroma_counts, + sunxi_jpegd_default_ac_chroma_values, + sizeof(sunxi_jpegd_default_ac_chroma_values)); + if (err) + { + return err; + } + + return sunxi_jpege_header_put(header, sos, sizeof(sos)); +} + +static void sunxi_jpege_write_quant_tables(struct sunxi_jpegd *jpegd, + const rt_uint16_t *luma_quant, const rt_uint16_t *chroma_quant) +{ + rt_uint32_t value; + rt_uint16_t quant; + int i; + + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_SRAM_OFFSET, 0); + for (i = 0; i < 128; i++) + { + quant = i < 64 ? luma_quant[i] : chroma_quant[i - 64]; + value = ((quant + 1) / 2 << 16) | (0xffff / quant); + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_SRAM_DATA, value); + } +} + +static struct sunxi_jpegd_buffer_entry *sunxi_jpegd_find_buffer( + struct sunxi_jpegd *jpegd, const struct sunxi_jpegd_buffer *buffer) +{ + struct sunxi_jpegd_buffer_entry *entry; + + if (!buffer) + { + return RT_NULL; + } + + rt_list_for_each_entry(entry, &jpegd->buffers, list) + { + if (entry->buffer.virt_addr == buffer->virt_addr && + entry->buffer.phys_addr == buffer->phys_addr && + entry->buffer.size == buffer->size) + { + return entry; + } + } + + return RT_NULL; +} + +static rt_err_t sunxi_jpegd_alloc_buffer(struct sunxi_jpegd *jpegd, + struct sunxi_jpegd_buffer *buffer) +{ + struct sunxi_jpegd_buffer_entry *entry; + rt_ubase_t physical; + rt_size_t allocated; + rt_size_t offset; + + if (!buffer || !buffer->size || buffer->size > (rt_size_t)-1 - + SUNXI_JPEGD_BUFFER_ALIGN) + { + return -RT_EINVAL; + } + + entry = rt_calloc(1, sizeof(*entry)); + if (!entry) + { + return -RT_ENOMEM; + } + + allocated = buffer->size + SUNXI_JPEGD_BUFFER_ALIGN - 1; + LOG_D("alloc DMA buffer: requested=%u allocated=%u", (rt_uint32_t)buffer->size, + (rt_uint32_t)allocated); + entry->alloc_addr = rt_dma_alloc_coherent(jpegd->dev, allocated, &physical); + if (!entry->alloc_addr) + { + rt_free(entry); + return -RT_ENOMEM; + } + + offset = RT_ALIGN(physical, SUNXI_JPEGD_BUFFER_ALIGN) - physical; + entry->alloc_phys = physical; + entry->alloc_size = allocated; + entry->buffer.virt_addr = (rt_uint8_t *)entry->alloc_addr + offset; + entry->buffer.phys_addr = physical + offset; + entry->buffer.size = buffer->size; + *buffer = entry->buffer; + rt_list_insert_before(&jpegd->buffers, &entry->list); + LOG_D("DMA buffer: va=%p pa=0x%08x", entry->buffer.virt_addr, + (rt_uint32_t)entry->buffer.phys_addr); + + return RT_EOK; +} + +static rt_err_t sunxi_jpegd_free_buffer(struct sunxi_jpegd *jpegd, + struct sunxi_jpegd_buffer *buffer) +{ + struct sunxi_jpegd_buffer_entry *entry; + + entry = sunxi_jpegd_find_buffer(jpegd, buffer); + if (!entry) + { + return -RT_EINVAL; + } + + rt_list_remove(&entry->list); + rt_dma_free_coherent(jpegd->dev, entry->alloc_size, entry->alloc_addr, + entry->alloc_phys); + rt_memset(buffer, 0, sizeof(*buffer)); + rt_free(entry); + + return RT_EOK; +} + +static rt_err_t sunxi_jpegd_reset(struct sunxi_jpegd *jpegd) +{ + rt_err_t err; + + sunxi_jpegd_writel(jpegd, SUNXI_VE_MODE, SUNXI_VE_MODE_DISABLED); + err = rt_reset_control_assert(jpegd->resets); + if (err) + { + LOG_E("VE reset assert failed: %d", err); + return err; + } + rt_hw_us_delay(1); + + err = rt_reset_control_deassert(jpegd->resets); + if (err) + { + LOG_E("VE reset deassert failed: %d", err); + } + + return err; +} + +static rt_err_t sunxi_jpegd_get_info(struct sunxi_jpegd *jpegd, + struct sunxi_jpegd_info *info) +{ + struct sunxi_jpegd_header header; + struct sunxi_jpegd_buffer_entry *input; + rt_uint32_t sampling; + rt_size_t stride; + rt_size_t aligned_height; + rt_size_t luma_size; + rt_err_t err; + + if (!info || !info->input || !info->input_size || + info->input_size > info->input->size) + { + return -RT_EINVAL; + } + + input = sunxi_jpegd_find_buffer(jpegd, info->input); + if (!input) + { + return -RT_EINVAL; + } + + err = sunxi_jpegd_parse_header(input->buffer.virt_addr, + info->input_size, &header); + if (err) + { + LOG_E("unsupported JPEG header: %d", err); + return err; + } + if ((err = sunxi_jpegd_sampling(&header, &sampling)) != RT_EOK) + { + return err; + } + if (sampling != SUNXI_VE_MPEG_TRIGGER_420) + { + return -RT_ENOSYS; + } + + stride = RT_ALIGN(header.width, 32); + aligned_height = RT_ALIGN(header.height, 32); + luma_size = stride * aligned_height; + + info->width = header.width; + info->height = header.height; + info->stride = stride; + /* The F101 VE stores JPEG 4:2:0 reconstruction surfaces as MB32. */ + info->format = SUNXI_JPEGD_FORMAT_MB32_420; + info->luma_size = luma_size; + info->output_size = luma_size + stride * aligned_height / 2; + + LOG_D("JPEG info: input=%u output=%u stride=%u sampling=%08x", + (rt_uint32_t)info->input_size, (rt_uint32_t)info->output_size, + (rt_uint32_t)stride, sampling); + + return RT_EOK; +} + +#ifdef RT_SOC_SUNXI_PNG_DEC +struct sunxi_pngd_header +{ + rt_uint32_t width; + rt_uint32_t height; + rt_uint8_t bit_depth; + rt_uint8_t color_type; + rt_uint8_t compression; + rt_uint8_t filter; + rt_uint8_t interlace; +}; + +static rt_uint32_t sunxi_pngd_read_be32(const rt_uint8_t *data) +{ + return ((rt_uint32_t)data[0] << 24) | + ((rt_uint32_t)data[1] << 16) | + ((rt_uint32_t)data[2] << 8) | data[3]; +} + +static rt_err_t sunxi_pngd_parse_header(const void *data, rt_size_t size, + struct sunxi_pngd_header *header) +{ + static const rt_uint8_t signature[] = + { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, + }; + const rt_uint8_t *png = data; + + if (!data || !header || size < 33 || + rt_memcmp(png, signature, sizeof(signature)) || + sunxi_pngd_read_be32(png + 8) != 13 || + rt_memcmp(png + 12, "IHDR", 4)) + { + return -RT_EINVAL; + } + + header->width = sunxi_pngd_read_be32(png + 16); + header->height = sunxi_pngd_read_be32(png + 20); + header->bit_depth = png[24]; + header->color_type = png[25]; + header->compression = png[26]; + header->filter = png[27]; + header->interlace = png[28]; + if (!header->width || !header->height || header->bit_depth != 8 || + (header->color_type != 2 && header->color_type != 6) || + header->compression || header->filter || header->interlace) + { + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static rt_err_t sunxi_pngd_get_info(struct sunxi_jpegd *jpegd, + struct sunxi_pngd_info *info) +{ + struct sunxi_jpegd_buffer_entry *input; + struct sunxi_pngd_header header; + rt_size_t stride; + rt_err_t err; + + if (!info || !info->input || !info->input_size || + info->input_size > info->input->size) + { + return -RT_EINVAL; + } + + input = sunxi_jpegd_find_buffer(jpegd, info->input); + if (!input) + { + return -RT_EINVAL; + } + + err = sunxi_pngd_parse_header(input->buffer.virt_addr, info->input_size, + &header); + if (err) + { + return err; + } + if (header.width > (rt_size_t)-1 / 4) + { + return -RT_EINVAL; + } + + stride = header.width * 4; + if (header.height > (rt_size_t)-1 / stride) + { + return -RT_EINVAL; + } + + info->width = header.width; + info->height = header.height; + info->stride = stride; + info->format = SUNXI_PNGD_FORMAT_RGBA8888; + info->output_size = stride * header.height; + + return RT_EOK; +} + +static rt_err_t sunxi_pngd_decode(struct sunxi_jpegd *jpegd, + struct sunxi_pngd_decode *request) +{ + struct sunxi_jpegd_buffer_entry *input; + struct sunxi_jpegd_buffer_entry *output; + struct sunxi_pngd_header header; + rt_size_t stride; + rt_size_t output_size; + rt_tick_t timeout; + rt_err_t err; + + if (!request || !request->input || !request->output || + !request->input_size || request->input_size > request->input->size) + { + return -RT_EINVAL; + } + + input = sunxi_jpegd_find_buffer(jpegd, request->input); + output = sunxi_jpegd_find_buffer(jpegd, request->output); + if (!input || !output || input == output) + { + return -RT_EINVAL; + } + + err = sunxi_pngd_parse_header(input->buffer.virt_addr, + request->input_size, &header); + if (err) + { + return err; + } + if (header.width > (rt_size_t)-1 / 4) + { + return -RT_EINVAL; + } + stride = header.width * 4; + if (header.height > (rt_size_t)-1 / stride) + { + return -RT_EINVAL; + } + output_size = stride * header.height; + if (output->buffer.size < output_size) + { + return -RT_ENOMEM; + } + + if ((err = sunxi_jpegd_reset(jpegd)) != RT_EOK) + { + return err; + } + + sunxi_jpegd_writel(jpegd, SUNXI_VE_MODE, SUNXI_VE_MODE_DEC_PNG); + sunxi_jpegd_writel(jpegd, SUNXI_VE_PNG_INT_STATUS, SUNXI_VE_PNG_INT_MASK); + sunxi_jpegd_writel(jpegd, SUNXI_VE_PNG_RX_ADDR, input->buffer.phys_addr); + sunxi_jpegd_writel(jpegd, SUNXI_VE_PNG_RX_LEN, request->input_size); + sunxi_jpegd_writel(jpegd, SUNXI_VE_PNG_TX_ADDR, output->buffer.phys_addr); + sunxi_jpegd_writel(jpegd, SUNXI_VE_PNG_INT_EN, SUNXI_VE_PNG_INT_MASK); + + jpegd->engine = SUNXI_JPEGD_ENGINE_PNG_DECODE; + jpegd->running = RT_TRUE; + jpegd->error = RT_EOK; + jpegd->irq_status = 0; + rt_completion_init(&jpegd->completion); + rt_hw_dsb(); + sunxi_jpegd_writel(jpegd, SUNXI_VE_PNG_CTRL, SUNXI_VE_PNG_CTRL_START); + + timeout = request->timeout ? request->timeout : + rt_tick_from_millisecond(SUNXI_PNGD_DEFAULT_TIMEOUT_MS); + err = rt_completion_wait(&jpegd->completion, timeout); + jpegd->running = RT_FALSE; + jpegd->engine = SUNXI_JPEGD_ENGINE_IDLE; + if (err) + { + LOG_E("PNG decode timed out, status=%08x control=%08x", + sunxi_jpegd_readl(jpegd, SUNXI_VE_PNG_STATUS), + sunxi_jpegd_readl(jpegd, SUNXI_VE_PNG_CTRL)); + sunxi_jpegd_reset(jpegd); + return -RT_ETIMEOUT; + } + if (jpegd->error) + { + return jpegd->error; + } + + request->width = header.width; + request->height = header.height; + request->stride = stride; + request->format = SUNXI_PNGD_FORMAT_RGBA8888; + + return RT_EOK; +} + +#endif /* RT_SOC_SUNXI_PNG_DEC */ + +static rt_err_t sunxi_jpegd_decode(struct sunxi_jpegd *jpegd, + struct sunxi_jpegd_decode *request) +{ + struct sunxi_jpegd_header header; + struct sunxi_jpegd_buffer_entry *input; + struct sunxi_jpegd_buffer_entry *output; + rt_uint32_t sampling; + rt_uint32_t ctrl; + rt_uint32_t address; + rt_size_t stride; + rt_size_t luma_size; + rt_size_t output_size; + rt_tick_t timeout; + rt_err_t err; + + if (!request || !request->input || !request->output || + !request->input_size || request->input_size > request->input->size) + { + return -RT_EINVAL; + } + + input = sunxi_jpegd_find_buffer(jpegd, request->input); + output = sunxi_jpegd_find_buffer(jpegd, request->output); + if (!input || !output) + { + return -RT_EINVAL; + } + + err = sunxi_jpegd_parse_header(input->buffer.virt_addr, + request->input_size, &header); + if (err) + { + LOG_E("unsupported JPEG header: %d", err); + return err; + } + if ((err = sunxi_jpegd_sampling(&header, &sampling)) != RT_EOK) + { + return err; + } + + if ((err = sunxi_jpegd_reset(jpegd)) != RT_EOK) + { + return err; + } + + sunxi_jpegd_writel(jpegd, SUNXI_VE_MODE, SUNXI_VE_MODE_DEC_MPEG); + if ((err = sunxi_jpegd_configure_output(jpegd, &header, sampling, output, + &stride, &luma_size, &output_size)) != RT_EOK) + { + return err; + } + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_TRIGGER, + SUNXI_VE_MPEG_TRIGGER_JPEG | sampling); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_JPEG_RESTART, + header.restart_interval); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_JPEG_SIZE, + (((RT_ALIGN(header.height, 8 * header.component[0].vfactor) / + (8 * header.component[0].vfactor)) - 1) << 16) | + ((RT_ALIGN(header.width, 8 * header.component[0].hfactor) / + (8 * header.component[0].hfactor)) - 1)); + sunxi_jpegd_write_quant_table(jpegd, + &header.quant[header.component[0].quant_table], + SUNXI_VE_MPEG_IQ_LUMA); + sunxi_jpegd_write_quant_table(jpegd, + &header.quant[header.component[1].quant_table], 0); + sunxi_jpegd_write_huffman_tables(jpegd, &header); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_VLD_OFFSET, + header.entropy_offset * 8); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_VLD_LENGTH, + (input->buffer.size - header.entropy_offset) * 8); + /* + * Unlike older VE generations, F101 takes the unmodified physical + * address here. The input buffer is a 1 KiB-aligned VLD ring. + */ + address = input->buffer.phys_addr | SUNXI_VE_MPEG_VLD_FIRST | + SUNXI_VE_MPEG_VLD_LAST | SUNXI_VE_MPEG_VLD_VALID; + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_VLD_ADDR, address); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_VLD_END, + input->buffer.phys_addr + input->buffer.size - 1); + + LOG_D("decode: input=%p/0x%08x size=%u output=%p/0x%08x size=%u vld=%08x..%08x", + input->buffer.virt_addr, (rt_uint32_t)input->buffer.phys_addr, + (rt_uint32_t)request->input_size, output->buffer.virt_addr, + (rt_uint32_t)output->buffer.phys_addr, (rt_uint32_t)output_size, + address, (rt_uint32_t)(input->buffer.phys_addr + input->buffer.size - 1)); + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, input->buffer.virt_addr, + request->input_size); + rt_hw_cpu_dcache_clean_and_invalidate(output->buffer.virt_addr, output_size); + + ctrl = SUNXI_VE_MPEG_CTRL_F101_420; + jpegd->engine = SUNXI_JPEGD_ENGINE_DECODE; + jpegd->running = RT_TRUE; + jpegd->error = RT_EOK; + jpegd->irq_status = 0; + rt_completion_init(&jpegd->completion); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_STATUS, SUNXI_VE_MPEG_STATUS_MASK); + rt_hw_dsb(); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_CTRL, ctrl); + rt_hw_dsb(); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_TRIGGER, + SUNXI_VE_MPEG_TRIGGER_JPEG | sampling | + SUNXI_VE_MPEG_TRIGGER_JPEG_VLD); + + timeout = request->timeout ? request->timeout : + rt_tick_from_millisecond(SUNXI_JPEGD_DEFAULT_TIMEOUT_MS); + err = rt_completion_wait(&jpegd->completion, timeout); + jpegd->running = RT_FALSE; + jpegd->engine = SUNXI_JPEGD_ENGINE_IDLE; + if (err) + { + LOG_E("decode timed out: mode=%08x status=%08x control=%08x vld=%08x offset=%08x length=%08x end=%08x", + sunxi_jpegd_readl(jpegd, SUNXI_VE_MODE), + sunxi_jpegd_readl(jpegd, SUNXI_VE_MPEG_STATUS), + sunxi_jpegd_readl(jpegd, SUNXI_VE_MPEG_CTRL), + sunxi_jpegd_readl(jpegd, SUNXI_VE_MPEG_VLD_ADDR), + sunxi_jpegd_readl(jpegd, SUNXI_VE_MPEG_VLD_OFFSET), + sunxi_jpegd_readl(jpegd, SUNXI_VE_MPEG_VLD_LENGTH), + sunxi_jpegd_readl(jpegd, SUNXI_VE_MPEG_VLD_END)); + sunxi_jpegd_reset(jpegd); + return -RT_ETIMEOUT; + } + if (jpegd->error) + { + LOG_E("decode failed: status=%08x control=%08x", + jpegd->irq_status, + sunxi_jpegd_readl(jpegd, SUNXI_VE_MPEG_CTRL)); + return jpegd->error; + } + + request->width = header.width; + request->height = header.height; + request->stride = stride; + request->format = SUNXI_JPEGD_FORMAT_MB32_420; + request->luma_size = luma_size; + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, output->buffer.virt_addr, + output_size); + + LOG_D("decode complete: irq=%08x status=%08x", jpegd->irq_status, + sunxi_jpegd_readl(jpegd, SUNXI_VE_MPEG_STATUS)); + + return RT_EOK; +} + +static rt_err_t sunxi_jpege_encode(struct sunxi_jpegd *jpegd, + struct sunxi_jpege_encode *request) +{ + struct sunxi_jpegd_buffer_entry *input; + struct sunxi_jpegd_buffer_entry *output; + struct sunxi_jpege_header header; + rt_uint16_t luma_quant[64]; + rt_uint16_t chroma_quant[64]; + rt_size_t aligned_height; + rt_size_t luma_size; + rt_size_t input_size; + rt_size_t bitstream_size; + rt_size_t encoded_size; + rt_uint32_t width_mb; + rt_uint32_t height_mb; + rt_uint32_t jpeg_ctrl; + rt_uint32_t bit_length; + rt_tick_t timeout; + rt_uint8_t quality; + rt_err_t err; + + if (!request || !request->input || !request->output || !request->width || + !request->height || request->width > SUNXI_JPEGE_MAX_WIDTH || + request->height > SUNXI_JPEGE_MAX_HEIGHT || + request->format != SUNXI_JPEGD_FORMAT_NV12) + { + return -RT_EINVAL; + } + + input = sunxi_jpegd_find_buffer(jpegd, request->input); + output = sunxi_jpegd_find_buffer(jpegd, request->output); + if (!input || !output || input == output || + (input->buffer.phys_addr & (SUNXI_JPEGD_BUFFER_ALIGN - 1)) || + (output->buffer.phys_addr & (SUNXI_JPEGD_BUFFER_ALIGN - 1))) + { + return -RT_EINVAL; + } + + if (request->stride < RT_ALIGN(request->width, 16) || + request->stride & 15) + { + return -RT_EINVAL; + } + + aligned_height = RT_ALIGN(request->height, 16); + luma_size = request->stride * aligned_height; + input_size = luma_size + luma_size / 2; + if (input->buffer.size < input_size) + { + return -RT_ENOMEM; + } + + bitstream_size = output->buffer.size & ~(SUNXI_JPEGE_MIN_OUTPUT_SIZE - 1); + if (bitstream_size < SUNXI_JPEGE_MIN_OUTPUT_SIZE) + { + return -RT_ENOMEM; + } + + quality = request->quality ? request->quality : 75; + if (quality > 100) + { + return -RT_EINVAL; + } + sunxi_jpege_scale_quant_table(luma_quant, sunxi_jpege_luma_quant, quality); + sunxi_jpege_scale_quant_table(chroma_quant, sunxi_jpege_chroma_quant, + quality); + err = sunxi_jpege_build_header(&header, request->width, request->height, + luma_quant, chroma_quant); + if (err) + { + return err; + } + if (header.size + 2 > bitstream_size) + { + return -RT_ENOMEM; + } + rt_memcpy(output->buffer.virt_addr, header.data, header.size); + + if ((err = sunxi_jpegd_reset(jpegd)) != RT_EOK) + { + return err; + } + + width_mb = request->stride / 16; + height_mb = aligned_height / 16; + sunxi_jpegd_writel(jpegd, SUNXI_VE_MODE, SUNXI_VE_MODE_ENC_AVC | + SUNXI_VE_MODE_ENC_ISP | SUNXI_VE_MODE_ENC_AVC_ENABLE | + SUNXI_VE_MODE_REC_WR_MODE_2MB | SUNXI_VE_MODE_DDR_MODE_BW_128); + sunxi_jpegd_writel(jpegd, SUNXI_VE_ISP_INPUT_SIZE, + (width_mb << 16) | height_mb); + sunxi_jpegd_writel(jpegd, SUNXI_VE_ISP_INPUT_STRIDE, + (width_mb << 16) | width_mb); + sunxi_jpegd_writel(jpegd, SUNXI_VE_ISP_CTRL, 0); + sunxi_jpegd_writel(jpegd, SUNXI_VE_ISP_INPUT_LUMA, + input->buffer.phys_addr); + sunxi_jpegd_writel(jpegd, SUNXI_VE_ISP_INPUT_CHROMA, + input->buffer.phys_addr + luma_size); + sunxi_jpege_write_quant_tables(jpegd, luma_quant, chroma_quant); + + jpeg_ctrl = SUNXI_VE_AVC_JPEG_FILL_ONE | SUNXI_VE_AVC_JPEG_BYTE_STUFF | + (((0x400 / chroma_quant[0]) & 0x7ff) << 16) | + ((0x400 / luma_quant[0]) & 0x7ff); + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_JPEG_CTRL, jpeg_ctrl); + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_VLE_ADDR, + output->buffer.phys_addr); + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_VLE_END, + output->buffer.phys_addr + bitstream_size - 1); + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_VLE_OFFSET, header.size * 8); + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_VLE_MAX, bitstream_size * 8); + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_STATUS, SUNXI_VE_AVC_STATUS_MASK); + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_CTRL, SUNXI_VE_AVC_CTRL_IRQ_ENABLE); + + jpegd->engine = SUNXI_JPEGD_ENGINE_ENCODE; + jpegd->running = RT_TRUE; + jpegd->error = RT_EOK; + jpegd->irq_status = 0; + rt_completion_init(&jpegd->completion); + rt_hw_dsb(); + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_TRIGGER, + SUNXI_VE_AVC_TRIGGER_JPEG | SUNXI_VE_AVC_TRIGGER_LAUNCH); + + timeout = request->timeout ? request->timeout : + rt_tick_from_millisecond(SUNXI_JPEGD_DEFAULT_TIMEOUT_MS); + err = rt_completion_wait(&jpegd->completion, timeout); + jpegd->running = RT_FALSE; + jpegd->engine = SUNXI_JPEGD_ENGINE_IDLE; + if (err) + { + LOG_E("encode timed out, status=%08x", sunxi_jpegd_readl(jpegd, + SUNXI_VE_AVC_STATUS)); + sunxi_jpegd_reset(jpegd); + return -RT_ETIMEOUT; + } + if (jpegd->error) + { + return jpegd->error; + } + + bit_length = sunxi_jpegd_readl(jpegd, SUNXI_VE_AVC_VLE_LENGTH); + encoded_size = (bit_length + 7) / 8; + if (encoded_size <= header.size || encoded_size + 2 > bitstream_size) + { + LOG_E("invalid encoded bit length: %u", bit_length); + return -RT_EIO; + } + if (((rt_uint8_t *)output->buffer.virt_addr)[encoded_size - 2] == 0xff && + ((rt_uint8_t *)output->buffer.virt_addr)[encoded_size - 1] == 0xd9) + { + request->output_size = encoded_size; + } + else + { + ((rt_uint8_t *)output->buffer.virt_addr)[encoded_size] = 0xff; + ((rt_uint8_t *)output->buffer.virt_addr)[encoded_size + 1] = 0xd9; + request->output_size = encoded_size + 2; + } + + return RT_EOK; +} + +static rt_err_t sunxi_jpegd_control(rt_device_t dev, int cmd, void *args) +{ + struct sunxi_jpegd *jpegd = raw_to_sunxi_jpegd(dev); + rt_err_t err; + + if (cmd == SUNXI_JPEGD_CMD_ALLOC_BUFFER) + { + LOG_D("allocate request: waiting for decoder lock"); + } + rt_mutex_take(&jpegd->lock, RT_WAITING_FOREVER); + if (cmd == SUNXI_JPEGD_CMD_ALLOC_BUFFER) + { + LOG_D("allocate request: decoder lock acquired"); + } + switch (cmd) + { + case SUNXI_JPEGD_CMD_ALLOC_BUFFER: + err = sunxi_jpegd_alloc_buffer(jpegd, args); + break; + case SUNXI_JPEGD_CMD_FREE_BUFFER: + err = jpegd->running ? -RT_EBUSY : sunxi_jpegd_free_buffer(jpegd, args); + break; + case SUNXI_JPEGD_CMD_DECODE: + err = jpegd->running ? -RT_EBUSY : sunxi_jpegd_decode(jpegd, args); + break; + case SUNXI_JPEGD_CMD_GET_INFO: + err = jpegd->running ? -RT_EBUSY : sunxi_jpegd_get_info(jpegd, args); + break; + case SUNXI_JPEGD_CMD_GET_VERSION: + if (!args) + { + err = -RT_EINVAL; + } + else + { + *(rt_uint32_t *)args = jpegd->version; + err = RT_EOK; + } + break; + default: + err = -RT_ENOSYS; + break; + } + rt_mutex_release(&jpegd->lock); + + return err; +} + +static rt_err_t sunxi_jpege_control(rt_device_t dev, int cmd, void *args) +{ + struct sunxi_jpegd *jpegd = raw_to_sunxi_jpege(dev); + rt_err_t err; + + rt_mutex_take(&jpegd->lock, RT_WAITING_FOREVER); + switch (cmd) + { + case SUNXI_JPEGE_CMD_ALLOC_BUFFER: + err = sunxi_jpegd_alloc_buffer(jpegd, args); + break; + case SUNXI_JPEGE_CMD_FREE_BUFFER: + err = jpegd->running ? -RT_EBUSY : sunxi_jpegd_free_buffer(jpegd, args); + break; + case SUNXI_JPEGE_CMD_ENCODE: + err = jpegd->running ? -RT_EBUSY : sunxi_jpege_encode(jpegd, args); + break; + case SUNXI_JPEGE_CMD_GET_VERSION: + if (!args) + { + err = -RT_EINVAL; + } + else + { + *(rt_uint32_t *)args = jpegd->version; + err = RT_EOK; + } + break; + default: + err = -RT_ENOSYS; + break; + } + rt_mutex_release(&jpegd->lock); + + return err; +} + +#ifdef RT_SOC_SUNXI_PNG_DEC +static rt_err_t sunxi_pngd_control(rt_device_t dev, int cmd, void *args) +{ + struct sunxi_jpegd *jpegd = raw_to_sunxi_pngd(dev); + rt_err_t err; + + rt_mutex_take(&jpegd->lock, RT_WAITING_FOREVER); + switch (cmd) + { + case SUNXI_PNGD_CMD_ALLOC_BUFFER: + err = sunxi_jpegd_alloc_buffer(jpegd, args); + break; + case SUNXI_PNGD_CMD_FREE_BUFFER: + err = jpegd->running ? -RT_EBUSY : sunxi_jpegd_free_buffer(jpegd, args); + break; + case SUNXI_PNGD_CMD_DECODE: + err = jpegd->running ? -RT_EBUSY : sunxi_pngd_decode(jpegd, args); + break; + case SUNXI_PNGD_CMD_GET_INFO: + err = jpegd->running ? -RT_EBUSY : sunxi_pngd_get_info(jpegd, args); + break; + case SUNXI_PNGD_CMD_GET_VERSION: + if (!args) + { + err = -RT_EINVAL; + } + else + { + *(rt_uint32_t *)args = jpegd->version; + err = RT_EOK; + } + break; + default: + err = -RT_ENOSYS; + break; + } + rt_mutex_release(&jpegd->lock); + + return err; +} +#endif /* RT_SOC_SUNXI_PNG_DEC */ + +#ifdef RT_USING_DEVICE_OPS +static const struct rt_device_ops sunxi_jpegd_ops = +{ + .control = sunxi_jpegd_control, +}; + +static const struct rt_device_ops sunxi_jpege_ops = +{ + .control = sunxi_jpege_control, +}; + +#ifdef RT_SOC_SUNXI_PNG_DEC +static const struct rt_device_ops sunxi_pngd_ops = +{ + .control = sunxi_pngd_control, +}; +#endif +#endif /* RT_USING_DEVICE_OPS */ + +static void sunxi_jpegd_isr(int irqno, void *parameter) +{ + struct sunxi_jpegd *jpegd = parameter; + rt_uint32_t status; + + RT_UNUSED(irqno); + +#ifdef RT_SOC_SUNXI_PNG_DEC + if (jpegd->engine == SUNXI_JPEGD_ENGINE_PNG_DECODE) + { + status = sunxi_jpegd_readl(jpegd, SUNXI_VE_PNG_INT_STATUS) & + SUNXI_VE_PNG_INT_MASK; + if (!status) + { + return; + } + + sunxi_jpegd_writel(jpegd, SUNXI_VE_PNG_INT_STATUS, status); + jpegd->irq_status = status; + if ((status & SUNXI_VE_PNG_INT_ERROR) || + !(status & SUNXI_VE_PNG_INT_DONE)) + { + jpegd->error = -RT_EIO; + } + rt_completion_done(&jpegd->completion); + return; + } +#endif /* RT_SOC_SUNXI_PNG_DEC */ + + if (jpegd->engine == SUNXI_JPEGD_ENGINE_ENCODE) + { + status = sunxi_jpegd_readl(jpegd, SUNXI_VE_AVC_STATUS) & + SUNXI_VE_AVC_STATUS_MASK; + if (!status) + { + return; + } + + sunxi_jpegd_writel(jpegd, SUNXI_VE_AVC_STATUS, status); + jpegd->irq_status = status; + if (status & SUNXI_VE_AVC_STATUS_ERROR || + !(status & SUNXI_VE_AVC_STATUS_SUCCESS)) + { + jpegd->error = -RT_EIO; + } + rt_completion_done(&jpegd->completion); + return; + } + if (jpegd->engine != SUNXI_JPEGD_ENGINE_DECODE) + { + return; + } + + status = sunxi_jpegd_readl(jpegd, SUNXI_VE_MPEG_STATUS) & + SUNXI_VE_MPEG_STATUS_MASK; + if (!status) + { + return; + } + + sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_STATUS, status); + jpegd->irq_status = status; + /* F101 reports JPEG completion with any of MPEG status bits 0..3. */ + rt_completion_done(&jpegd->completion); +} + +static void sunxi_jpegd_release(struct sunxi_jpegd *jpegd) +{ + struct sunxi_jpegd_buffer_entry *entry; + struct sunxi_jpegd_buffer_entry *next; + + rt_list_for_each_entry_safe(entry, next, &jpegd->buffers, list) + { + rt_list_remove(&entry->list); + rt_dma_free_coherent(jpegd->dev, entry->alloc_size, entry->alloc_addr, + entry->alloc_phys); + rt_free(entry); + } + if (jpegd->png_registered) + { + rt_device_unregister(&jpegd->png_decoder); + } + if (jpegd->encoder_registered) + { + rt_device_unregister(&jpegd->encoder); + } + if (jpegd->device_registered) + { + rt_device_unregister(&jpegd->parent); + } + if (jpegd->lock_initialized) + { + rt_mutex_detach(&jpegd->lock); + } + if (!rt_is_err_or_null(jpegd->resets)) + { + rt_reset_control_assert(jpegd->resets); + rt_reset_control_put(jpegd->resets); + } + if (!rt_is_err_or_null(jpegd->clks)) + { + if (jpegd->clocks_enabled) + { + rt_clk_array_disable_unprepare(jpegd->clks); + } + rt_clk_array_put(jpegd->clks); + } + if (jpegd->base) + { + rt_iounmap(jpegd->base); + } + rt_free(jpegd); +} + +static rt_err_t sunxi_jpegd_probe(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct sunxi_jpegd *jpegd; + const char *decoder_name; + const char *encoder_name; +#ifdef RT_SOC_SUNXI_PNG_DEC + const char *png_name; +#endif + rt_err_t err; + + jpegd = rt_calloc(1, sizeof(*jpegd)); + if (!jpegd) + { + return -RT_ENOMEM; + } + + jpegd->dev = dev; + jpegd->irq = -1; + rt_list_init(&jpegd->buffers); + jpegd->base = rt_dm_dev_iomap(dev, 0); + if (!jpegd->base) + { + err = -RT_EIO; + goto _fail; + } + + if ((err = sunxi_jpegd_claim_sram(jpegd)) != RT_EOK) + { + LOG_E("failed to claim VE SRAM: %d", err); + goto _fail; + } + + jpegd->clks = rt_clk_get_array(dev); + if (rt_is_err(jpegd->clks)) + { + err = rt_ptr_err(jpegd->clks); + goto _fail; + } + if ((err = rt_clk_array_prepare_enable(jpegd->clks)) != RT_EOK) + { + goto _fail; + } + jpegd->clocks_enabled = RT_TRUE; + + jpegd->resets = rt_reset_control_get_array(dev); + if (rt_is_err(jpegd->resets)) + { + err = rt_ptr_err(jpegd->resets); + goto _fail; + } + if ((err = sunxi_jpegd_reset(jpegd)) != RT_EOK) + { + goto _fail; + } + + jpegd->irq = rt_dm_dev_get_irq(dev, 0); + if (jpegd->irq < 0) + { + err = jpegd->irq; + goto _fail; + } + + jpegd->version = sunxi_jpegd_readl(jpegd, SUNXI_VE_VERSION); + rt_dm_dev_set_name_auto(&jpegd->parent, "jpegd"); + decoder_name = rt_dm_dev_get_name(&jpegd->parent); + jpegd->parent.type = RT_Device_Class_Char; + jpegd->parent.bus = dev->bus; + jpegd->parent.ofw_node = dev->ofw_node; +#ifdef RT_USING_DEVICE_OPS + jpegd->parent.ops = &sunxi_jpegd_ops; +#else + jpegd->parent.control = sunxi_jpegd_control; +#endif + if ((err = rt_mutex_init(&jpegd->lock, decoder_name, RT_IPC_FLAG_PRIO)) != RT_EOK) + { + goto _fail; + } + jpegd->lock_initialized = RT_TRUE; + rt_completion_init(&jpegd->completion); +#ifdef RT_SOC_SUNXI_JPEG_DEC + if ((err = rt_device_register(&jpegd->parent, decoder_name, + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE)) != RT_EOK) + { + goto _fail; + } + jpegd->device_registered = RT_TRUE; +#endif /* RT_SOC_SUNXI_JPEG_DEC */ + +#ifdef RT_SOC_SUNXI_JPEG_ENC + rt_dm_dev_set_name_auto(&jpegd->encoder, "jpege"); + encoder_name = rt_dm_dev_get_name(&jpegd->encoder); + jpegd->encoder.type = RT_Device_Class_Char; + jpegd->encoder.bus = dev->bus; + jpegd->encoder.ofw_node = dev->ofw_node; +#ifdef RT_USING_DEVICE_OPS + jpegd->encoder.ops = &sunxi_jpege_ops; +#else + jpegd->encoder.control = sunxi_jpege_control; +#endif + if ((err = rt_device_register(&jpegd->encoder, encoder_name, + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE)) != RT_EOK) + { + goto _fail; + } + jpegd->encoder_registered = RT_TRUE; +#endif /* RT_SOC_SUNXI_JPEG_ENC */ + +#ifdef RT_SOC_SUNXI_PNG_DEC + rt_dm_dev_set_name_auto(&jpegd->png_decoder, "pngd"); + png_name = rt_dm_dev_get_name(&jpegd->png_decoder); + jpegd->png_decoder.type = RT_Device_Class_Char; + jpegd->png_decoder.bus = dev->bus; + jpegd->png_decoder.ofw_node = dev->ofw_node; +#ifdef RT_USING_DEVICE_OPS + jpegd->png_decoder.ops = &sunxi_pngd_ops; +#else + jpegd->png_decoder.control = sunxi_pngd_control; +#endif + if ((err = rt_device_register(&jpegd->png_decoder, png_name, + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE)) != RT_EOK) + { + goto _fail; + } + jpegd->png_registered = RT_TRUE; +#endif /* RT_SOC_SUNXI_PNG_DEC */ + + rt_hw_interrupt_install(jpegd->irq, sunxi_jpegd_isr, jpegd, "ve"); + rt_hw_interrupt_umask(jpegd->irq); + jpegd->irq_installed = RT_TRUE; + dev->user_data = jpegd; + + LOG_D("VE codec version %08x", jpegd->version); + return RT_EOK; + +_fail: + sunxi_jpegd_release(jpegd); + return err; +} + +static rt_err_t sunxi_jpegd_remove(struct rt_platform_device *pdev) +{ + struct sunxi_jpegd *jpegd = pdev->parent.user_data; + + if (jpegd->irq_installed) + { + rt_hw_interrupt_mask(jpegd->irq); + rt_pic_detach_irq(jpegd->irq, jpegd); + } + sunxi_jpegd_release(jpegd); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_jpegd_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-video-engine" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_jpegd_driver = +{ + .name = "sunxi-jpegd", + .ids = sunxi_jpegd_ofw_ids, + .probe = sunxi_jpegd_probe, + .remove = sunxi_jpegd_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_jpegd_driver); diff --git a/bsp/allwinner/dm/spi/Kconfig b/bsp/allwinner/dm/spi/Kconfig new file mode 100644 index 000000000000..7fa4a5d24633 --- /dev/null +++ b/bsp/allwinner/dm/spi/Kconfig @@ -0,0 +1,13 @@ +config RT_SPI_SUNXI + bool "Allwinner Sunxi SPI controller" + depends on RT_USING_SPI + depends on RT_USING_DM + depends on RT_USING_CLK + depends on RT_USING_RESET + default n + +config RT_QSPI_SUNXI + bool "Allwinner Sunxi QSPI controller mode" + depends on RT_USING_QSPI + select RT_SPI_SUNXI + default n diff --git a/bsp/allwinner/dm/spi/SConscript b/bsp/allwinner/dm/spi/SConscript new file mode 100644 index 000000000000..6da6b94e1128 --- /dev/null +++ b/bsp/allwinner/dm/spi/SConscript @@ -0,0 +1,13 @@ +from building import * +Import('RTT_ROOT') + +cwd = GetCurrentDir() +src = [] + +if GetDepend('RT_SPI_SUNXI'): + src += ['spi-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], + CPPPATH = [cwd + '/../include', RTT_ROOT + '/components/drivers/spi']) + +Return('group') diff --git a/bsp/allwinner/dm/spi/spi-sunxi.c b/bsp/allwinner/dm/spi/spi-sunxi.c new file mode 100644 index 000000000000..c1f2e936b108 --- /dev/null +++ b/bsp/allwinner/dm/spi/spi-sunxi.c @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define DBG_TAG "spi.sunxi" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_SPI_GBL_CTL 0x04 +#define SUNXI_SPI_TFR_CTL 0x08 +#define SUNXI_SPI_INT_CTL 0x10 +#define SUNXI_SPI_INT_STA 0x14 +#define SUNXI_SPI_FIFO_CTL 0x18 +#define SUNXI_SPI_FIFO_STA 0x1c +#define SUNXI_SPI_BURST_CNT 0x30 +#define SUNXI_SPI_XMIT_CNT 0x34 +#define SUNXI_SPI_BURST_CTL_CNT 0x38 +#define SUNXI_SPI_TXDATA 0x200 +#define SUNXI_SPI_RXDATA 0x300 + +#define SUNXI_SPI_GBL_BUS_ENABLE RT_BIT(0) +#define SUNXI_SPI_GBL_MASTER RT_BIT(1) +#define SUNXI_SPI_GBL_TP RT_BIT(7) +#define SUNXI_SPI_GBL_RESET RT_BIT(31) + +#define SUNXI_SPI_TFR_CPHA RT_BIT(0) +#define SUNXI_SPI_TFR_CPOL RT_BIT(1) +#define SUNXI_SPI_TFR_CS_MASK RT_GENMASK(5, 4) +#define SUNXI_SPI_TFR_CS(cs) (((cs) & 0x3) << 4) +#define SUNXI_SPI_TFR_CS_MANUAL RT_BIT(6) +#define SUNXI_SPI_TFR_CS_LEVEL RT_BIT(7) +#define SUNXI_SPI_TFR_DHB RT_BIT(8) +#define SUNXI_SPI_TFR_FBS RT_BIT(12) +#define SUNXI_SPI_TFR_SDDM RT_BIT(14) +#define SUNXI_SPI_TFR_XCH RT_BIT(31) + +#define SUNXI_SPI_FIFO_RX_RESET RT_BIT(15) +#define SUNXI_SPI_FIFO_TX_RESET RT_BIT(31) +#define SUNXI_SPI_FIFO_RX_COUNT RT_GENMASK(7, 0) +#define SUNXI_SPI_FIFO_TX_COUNT RT_GENMASK(23, 16) + +#define SUNXI_SPI_INT_TC RT_BIT(12) +#define SUNXI_SPI_BCC_STC_MASK RT_GENMASK(23, 0) +#define SUNXI_SPI_BCC_DUAL_ENABLE RT_BIT(28) +#define SUNXI_SPI_BCC_QUAD_ENABLE RT_BIT(29) +#define SUNXI_SPI_MAX_TRANSFER 0x00ffffff +#define SUNXI_SPI_FIFO_DEPTH 64 +#define SUNXI_SPI_QSPI_PREFIX_MAX 5 + +struct sunxi_spi +{ + struct rt_spi_bus parent; + struct rt_clk *bus_clk; + struct rt_clk *mod_clk; + struct rt_reset_control *reset; + void *base; + rt_bool_t qspi; +}; + +#define to_sunxi_spi(raw) rt_container_of(raw, struct sunxi_spi, parent) + +static rt_uint32_t sunxi_spi_read(struct sunxi_spi *spi, rt_uint32_t offset) +{ + return HWREG32(spi->base + offset); +} + +static void sunxi_spi_write(struct sunxi_spi *spi, rt_uint32_t offset, + rt_uint32_t value) +{ + HWREG32(spi->base + offset) = value; +} + +static void sunxi_spi_set_cs(struct sunxi_spi *spi, struct rt_spi_device *device, + rt_bool_t asserted) +{ + rt_uint32_t control = sunxi_spi_read(spi, SUNXI_SPI_TFR_CTL); + + if (device->config.mode & RT_SPI_NO_CS) + { + return; + } + + control &= ~SUNXI_SPI_TFR_CS_MASK; + control |= SUNXI_SPI_TFR_CS(device->chip_select[0]) | + SUNXI_SPI_TFR_CS_MANUAL; + if (asserted) + { + control &= ~SUNXI_SPI_TFR_CS_LEVEL; + } + else + { + control |= SUNXI_SPI_TFR_CS_LEVEL; + } + sunxi_spi_write(spi, SUNXI_SPI_TFR_CTL, control); +} + +static rt_err_t sunxi_spi_configure(struct rt_spi_device *device, + struct rt_spi_configuration *configuration) +{ + rt_uint32_t control; + struct sunxi_spi *spi = to_sunxi_spi(device->bus); + + if (!configuration->max_hz || configuration->data_width != 8) + { + return -RT_EINVAL; + } + + if (rt_clk_set_rate(spi->mod_clk, configuration->max_hz)) + { + return -RT_EINVAL; + } + + control = sunxi_spi_read(spi, SUNXI_SPI_TFR_CTL); + control &= ~(SUNXI_SPI_TFR_CPHA | SUNXI_SPI_TFR_CPOL | + SUNXI_SPI_TFR_FBS | SUNXI_SPI_TFR_CS_MASK); + control |= SUNXI_SPI_TFR_CS(device->chip_select[0]) | + SUNXI_SPI_TFR_CS_MANUAL; + if (configuration->mode & RT_SPI_CPHA) + { + control |= SUNXI_SPI_TFR_CPHA; + } + if (configuration->mode & RT_SPI_CPOL) + { + control |= SUNXI_SPI_TFR_CPOL; + } + if (configuration->mode & RT_SPI_LSB) + { + control |= SUNXI_SPI_TFR_FBS; + } + sunxi_spi_write(spi, SUNXI_SPI_TFR_CTL, control); + + return RT_EOK; +} + +static rt_err_t sunxi_spi_wait_complete(struct sunxi_spi *spi, + rt_size_t transfer_length, rt_size_t receive_length, + rt_size_t *received, void *buffer) +{ + rt_tick_t timeout = rt_tick_get() + rt_tick_from_millisecond( + rt_max_t(rt_size_t, 20, transfer_length / 64 + 20)); + rt_uint8_t *rx = buffer; + + for (;;) + { + rt_uint32_t status; + rt_uint32_t available = sunxi_spi_read(spi, SUNXI_SPI_FIFO_STA) & + SUNXI_SPI_FIFO_RX_COUNT; + + while (available-- && *received < receive_length) + { + rt_uint8_t value = *(volatile rt_uint8_t *)(spi->base + SUNXI_SPI_RXDATA); + + if (rx) + { + rx[*received] = value; + } + ++*received; + } + + status = sunxi_spi_read(spi, SUNXI_SPI_INT_STA); + if (status & SUNXI_SPI_INT_TC) + { + sunxi_spi_write(spi, SUNXI_SPI_INT_STA, status); + return RT_EOK; + } + if ((rt_int32_t)(rt_tick_get() - timeout) >= 0) + { + return -RT_ETIMEOUT; + } + rt_hw_cpu_relax(); + } +} + +static rt_err_t sunxi_spi_qspi_prefix(const struct rt_qspi_message *message, + rt_uint8_t *prefix, rt_size_t *prefix_length) +{ + rt_size_t address_bytes; + + if (message->instruction.qspi_lines != 1 || + (message->address.size && message->address.qspi_lines != 1) || + message->address.size > 32 || message->address.size % 8 || + message->alternate_bytes.size || message->dummy_cycles) + { + return -RT_EINVAL; + } + + prefix[0] = message->instruction.content; + address_bytes = message->address.size / 8; + + for (rt_size_t i = 0; i < address_bytes; ++i) + { + prefix[i + 1] = message->address.content >> + ((address_bytes - i - 1) * 8); + } + + *prefix_length = address_bytes + 1; + + return RT_EOK; +} + +static rt_uint8_t sunxi_spi_tx_byte(const rt_uint8_t *prefix, + rt_size_t prefix_length, const rt_uint8_t *data, rt_size_t offset) +{ + if (offset < prefix_length) + { + return prefix[offset]; + } + + return data ? data[offset - prefix_length] : 0; +} + +static rt_ssize_t sunxi_spi_xfer(struct rt_spi_device *device, + struct rt_spi_message *message) +{ + rt_size_t sent = 0; + rt_size_t received = 0; + rt_size_t wanted_rx; + rt_size_t prefix_length = 0; + rt_size_t transfer_length; + rt_size_t transmit_length; + rt_uint8_t prefix[SUNXI_SPI_QSPI_PREFIX_MAX]; + const rt_uint8_t *tx = message->send_buf; + struct sunxi_spi *spi = to_sunxi_spi(device->bus); + const struct rt_qspi_message *qspi_message = RT_NULL; + rt_uint8_t data_lines = 1; + rt_uint32_t control; + rt_uint32_t burst_control; + rt_tick_t refill_timeout; + rt_err_t err; + + if (spi->qspi) + { + qspi_message = (const struct rt_qspi_message *)message; + data_lines = qspi_message->qspi_data_lines ? + qspi_message->qspi_data_lines : 1; + + if (data_lines != 1 && data_lines != 2 && data_lines != 4) + { + return -RT_EINVAL; + } + if ((err = sunxi_spi_qspi_prefix(qspi_message, prefix, + &prefix_length)) != RT_EOK) + { + return err; + } + if (message->recv_buf) + { + return -RT_ENOSYS; + } + } + + transfer_length = message->length + prefix_length; + /* + * XMIT_CNT must remain zero for RX-only bursts. The controller still + * needs its initial dummy FIFO seed to start the exchange, but it must + * never be refilled as a transmit transfer. + */ + transmit_length = (message->send_buf || prefix_length) ? + transfer_length : 0; + + if (message->cs_take) + { + sunxi_spi_set_cs(spi, device, RT_TRUE); + } + + if (!transfer_length) + { + if (message->cs_release) + { + sunxi_spi_set_cs(spi, device, RT_FALSE); + } + return 0; + } + if ((!message->send_buf && !message->recv_buf && !prefix_length) || + transfer_length > SUNXI_SPI_MAX_TRANSFER) + { + err = -RT_EINVAL; + goto _release; + } + + sunxi_spi_write(spi, SUNXI_SPI_INT_CTL, 0); + sunxi_spi_write(spi, SUNXI_SPI_INT_STA, ~0u); + sunxi_spi_write(spi, SUNXI_SPI_FIFO_CTL, + SUNXI_SPI_FIFO_RX_RESET | SUNXI_SPI_FIFO_TX_RESET); + + control = sunxi_spi_read(spi, SUNXI_SPI_TFR_CTL); + control &= ~(SUNXI_SPI_TFR_DHB | SUNXI_SPI_TFR_SDDM | + SUNXI_SPI_TFR_XCH); + if (!message->recv_buf) + { + control |= SUNXI_SPI_TFR_DHB; + } + /* + * The controller needs one delayed clock edge when changing from a + * single-line prefix to dual/quad payload data in SPI mode 0. + */ + if (spi->qspi && prefix_length && data_lines > 1 && + !(device->config.mode & (RT_SPI_CPHA | RT_SPI_CPOL))) + { + control |= SUNXI_SPI_TFR_SDDM; + } + sunxi_spi_write(spi, SUNXI_SPI_TFR_CTL, control); + + burst_control = sunxi_spi_read(spi, SUNXI_SPI_BURST_CTL_CNT); + burst_control &= ~(SUNXI_SPI_BCC_STC_MASK | SUNXI_SPI_BCC_DUAL_ENABLE | + SUNXI_SPI_BCC_QUAD_ENABLE); + if (spi->qspi) + { + if (data_lines == 2) + { + burst_control |= SUNXI_SPI_BCC_DUAL_ENABLE; + } + else if (data_lines == 4) + { + burst_control |= SUNXI_SPI_BCC_QUAD_ENABLE; + } + burst_control |= (data_lines == 1 ? transfer_length : prefix_length) & + SUNXI_SPI_BCC_STC_MASK; + } + else + { + burst_control |= (message->send_buf ? message->length : 0) & + SUNXI_SPI_BCC_STC_MASK; + } + sunxi_spi_write(spi, SUNXI_SPI_BURST_CTL_CNT, burst_control); + sunxi_spi_write(spi, SUNXI_SPI_BURST_CNT, transfer_length); + sunxi_spi_write(spi, SUNXI_SPI_XMIT_CNT, transmit_length); + + while (sent < transfer_length && sent < SUNXI_SPI_FIFO_DEPTH) + { + *(volatile rt_uint8_t *)(spi->base + SUNXI_SPI_TXDATA) = + sunxi_spi_tx_byte(prefix, prefix_length, tx, sent); + ++sent; + } + + control = sunxi_spi_read(spi, SUNXI_SPI_GBL_CTL); + sunxi_spi_write(spi, SUNXI_SPI_GBL_CTL, control | SUNXI_SPI_GBL_BUS_ENABLE); + control = sunxi_spi_read(spi, SUNXI_SPI_TFR_CTL); + sunxi_spi_write(spi, SUNXI_SPI_TFR_CTL, control | SUNXI_SPI_TFR_XCH); + + wanted_rx = message->recv_buf ? transfer_length : 0; + refill_timeout = rt_tick_get() + rt_tick_from_millisecond( + rt_max_t(rt_size_t, 20, transfer_length / 64 + 20)); + + while (sent < transmit_length) + { + rt_uint32_t tx_count = (sunxi_spi_read(spi, SUNXI_SPI_FIFO_STA) & + SUNXI_SPI_FIFO_TX_COUNT) >> 16; + rt_uint32_t rx_count = sunxi_spi_read(spi, SUNXI_SPI_FIFO_STA) & + SUNXI_SPI_FIFO_RX_COUNT; + rt_bool_t progressed = RT_FALSE; + + while (rx_count-- && received < wanted_rx) + { + ((rt_uint8_t *)message->recv_buf)[received++] = + *(volatile rt_uint8_t *)(spi->base + SUNXI_SPI_RXDATA); + progressed = RT_TRUE; + } + while (sent < transmit_length && tx_count++ < SUNXI_SPI_FIFO_DEPTH) + { + *(volatile rt_uint8_t *)(spi->base + SUNXI_SPI_TXDATA) = + sunxi_spi_tx_byte(prefix, prefix_length, tx, sent); + ++sent; + progressed = RT_TRUE; + } + if (!progressed) + { + if ((rt_int32_t)(rt_tick_get() - refill_timeout) >= 0) + { + err = -RT_ETIMEOUT; + goto _release; + } + rt_hw_cpu_relax(); + } + } + + err = sunxi_spi_wait_complete(spi, transfer_length, wanted_rx, + &received, message->recv_buf); + +_release: + if (message->cs_release) + { + sunxi_spi_set_cs(spi, device, RT_FALSE); + } + + return err ? err : transfer_length; +} + +static const struct rt_spi_ops sunxi_spi_ops = +{ + .configure = sunxi_spi_configure, + .xfer = sunxi_spi_xfer, +}; + +static void sunxi_spi_free(struct sunxi_spi *spi) +{ + if (!rt_is_err_or_null(spi->reset)) + { + rt_reset_control_put(spi->reset); + } + if (!rt_is_err_or_null(spi->mod_clk)) + { + rt_clk_disable_unprepare(spi->mod_clk); + rt_clk_put(spi->mod_clk); + } + if (!rt_is_err_or_null(spi->bus_clk)) + { + rt_clk_disable_unprepare(spi->bus_clk); + rt_clk_put(spi->bus_clk); + } + if (spi->base) + { + rt_iounmap(spi->base); + } + rt_free(spi); +} + +static rt_err_t sunxi_spi_probe(struct rt_platform_device *pdev) +{ + int id; + rt_err_t err; + const char *name; + struct rt_device *dev = &pdev->parent; + struct sunxi_spi *spi = rt_calloc(1, sizeof(*spi)); + + if (!spi) + { + return -RT_ENOMEM; + } + + spi->base = rt_dm_dev_iomap(dev, 0); + if (!spi->base) + { + err = -RT_EIO; + goto _fail; + } + spi->bus_clk = rt_clk_get_by_index(dev, 0); + spi->mod_clk = rt_clk_get_by_index(dev, 1); + if (rt_is_err(spi->bus_clk) || rt_is_err(spi->mod_clk)) + { + err = rt_is_err(spi->bus_clk) ? rt_ptr_err(spi->bus_clk) : + rt_ptr_err(spi->mod_clk); + goto _fail; + } + if ((err = rt_clk_prepare_enable(spi->bus_clk)) || + (err = rt_clk_prepare_enable(spi->mod_clk))) + { + goto _fail; + } + spi->reset = rt_reset_control_get_by_index(dev, 0); + if (rt_is_err(spi->reset)) + { + err = rt_ptr_err(spi->reset); + goto _fail; + } + if (spi->reset && (err = rt_reset_control_deassert(spi->reset))) + { + goto _fail; + } + + sunxi_spi_write(spi, SUNXI_SPI_GBL_CTL, SUNXI_SPI_GBL_RESET); + rt_hw_us_delay(2); + sunxi_spi_write(spi, SUNXI_SPI_GBL_CTL, + SUNXI_SPI_GBL_MASTER | SUNXI_SPI_GBL_TP); + sunxi_spi_write(spi, SUNXI_SPI_TFR_CTL, SUNXI_SPI_TFR_CS_LEVEL); + spi->parent.mode = RT_SPI_CPHA | RT_SPI_CPOL | RT_SPI_LSB | RT_SPI_NO_CS; + spi->parent.num_chipselect = 4; + spi->parent.parent.ofw_node = dev->ofw_node; + spi->qspi = rt_dm_dev_prop_read_bool(dev, "allwinner,qspi-mode"); + + id = rt_ofw_get_alias_id(dev->ofw_node, "spi"); + if (id >= 0) + { + rt_dm_dev_set_name(&spi->parent.parent, "spi%u", id); + } + else + { + rt_dm_dev_set_name_auto(&spi->parent.parent, "spi"); + } + name = rt_dm_dev_get_name(&spi->parent.parent); + dev->user_data = spi; + + if (spi->qspi) + { +#ifdef RT_USING_QSPI + err = rt_qspi_bus_register(&spi->parent, name, &sunxi_spi_ops); +#else + err = -RT_ENOSYS; +#endif + } + else + { + err = rt_spi_bus_register(&spi->parent, name, &sunxi_spi_ops); + } + if (err) + { + goto _fail; + } + rt_dm_dev_bind_fwdata(dev, RT_NULL, spi); + + return RT_EOK; + +_fail: + sunxi_spi_free(spi); + return err; +} + +static rt_err_t sunxi_spi_remove(struct rt_platform_device *pdev) +{ + struct sunxi_spi *spi = pdev->parent.user_data; + + rt_dm_dev_unbind_fwdata(&pdev->parent, RT_NULL); + rt_device_unregister(&spi->parent.parent); + sunxi_spi_free(spi); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_spi_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-spi" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_spi_driver = +{ + .name = "sunxi-spi", + .ids = sunxi_spi_ofw_ids, + .probe = sunxi_spi_probe, + .remove = sunxi_spi_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_spi_driver); diff --git a/bsp/allwinner/dm/thermal/Kconfig b/bsp/allwinner/dm/thermal/Kconfig new file mode 100644 index 000000000000..9dd580ea73af --- /dev/null +++ b/bsp/allwinner/dm/thermal/Kconfig @@ -0,0 +1,7 @@ +config RT_THERMAL_SUNXI + bool "Allwinner Sunxi thermal sensor" + depends on RT_USING_CLK + depends on RT_USING_NVMEM + depends on RT_USING_RESET + depends on RT_USING_THERMAL + default n diff --git a/bsp/allwinner/dm/thermal/SConscript b/bsp/allwinner/dm/thermal/SConscript new file mode 100644 index 000000000000..7a76faf2fa41 --- /dev/null +++ b/bsp/allwinner/dm/thermal/SConscript @@ -0,0 +1,12 @@ +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_THERMAL_SUNXI']): + src += ['thermal-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/thermal/thermal-sunxi.c b/bsp/allwinner/dm/thermal/thermal-sunxi.c new file mode 100644 index 000000000000..58609653d5d2 --- /dev/null +++ b/bsp/allwinner/dm/thermal/thermal-sunxi.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "thermal.sunxi" +#define DBG_LVL DBG_INFO +#include + +#define SUNXI_THS_CTL 0x00 +#define SUNXI_THS_EN 0x04 +#define SUNXI_THS_PCTL 0x08 +#define SUNXI_THS_MFC 0x30 +#define SUNXI_THS_CALIB 0xa0 +#define SUNXI_THS_DATA 0xc0 + +#define SUNXI_GPADC_CTRL 0x04 +#define SUNXI_GPADC_VCM_BUF_EN RT_BIT(0) + +#define SUN252I_F101_THS_CAL_MASK RT_GENMASK(11, 0) + +struct sunxi_thermal +{ + struct rt_thermal_zone_device zone; + + void *ths; + void *gpadc; + rt_uint32_t sid_calibration; + rt_bool_t sid_calibration_valid; + struct rt_clk *ths_clk; + struct rt_clk *gpadc_clk; + struct rt_reset_control *ths_reset; + struct rt_reset_control *gpadc_reset; + rt_bool_t ths_clk_enabled; + rt_bool_t gpadc_clk_enabled; +}; + +#define to_sunxi_thermal(raw) rt_container_of(raw, struct sunxi_thermal, zone) + +static int sun252i_f101_ths_raw_to_temp(rt_uint32_t raw) +{ + if (raw > 1839) + { + return (2822 - raw) * 66; + } + + return (2835 - raw) * 65; +} + +static void sun252i_f101_ths_calibrate(struct sunxi_thermal *ths) +{ + int ft_temp; + int sensor_temp; + int delta; + int cdata; + rt_uint32_t value; + rt_uint32_t calibration; + + if (!ths->sid_calibration_valid) + { + LOG_W("thermal calibration data is unavailable"); + return; + } + + value = ths->sid_calibration; + ft_temp = value & SUN252I_F101_THS_CAL_MASK; + calibration = (value >> 12) & SUN252I_F101_THS_CAL_MASK; + if (!ft_temp) + { + LOG_W("thermal calibration data is empty"); + return; + } + + sensor_temp = sun252i_f101_ths_raw_to_temp(calibration); + delta = (ft_temp * 100 - sensor_temp) / -66; + cdata = 0x800 - delta; + if (cdata < 0 || cdata > SUN252I_F101_THS_CAL_MASK) + { + LOG_W("thermal calibration value is invalid"); + return; + } + + value = HWREG32(ths->ths + SUNXI_THS_CALIB); + value &= ~SUN252I_F101_THS_CAL_MASK; + value |= cdata; + HWREG32(ths->ths + SUNXI_THS_CALIB) = value; +} + +static rt_err_t sun252i_f101_thermal_get_temp(struct rt_thermal_zone_device *zdev, + int *out_temp) +{ + rt_uint32_t raw; + struct sunxi_thermal *ths = to_sunxi_thermal(zdev); + + raw = HWREG32(ths->ths + SUNXI_THS_DATA) & SUN252I_F101_THS_CAL_MASK; + if (!raw) + { + return -EAGAIN; + } + + *out_temp = sun252i_f101_ths_raw_to_temp(raw); + + return RT_EOK; +} + +static const struct rt_thermal_zone_ops sun252i_f101_thermal_ops = +{ + .get_temp = sun252i_f101_thermal_get_temp, +}; + +static void sun252i_f101_thermal_release(struct sunxi_thermal *ths) +{ + if (ths->ths) + { + HWREG32(ths->ths + SUNXI_THS_EN) = 0; + } + if (!rt_is_err_or_null(ths->ths_reset)) + { + rt_reset_control_assert(ths->ths_reset); + rt_reset_control_put(ths->ths_reset); + } + if (!rt_is_err_or_null(ths->gpadc_reset)) + { + rt_reset_control_assert(ths->gpadc_reset); + rt_reset_control_put(ths->gpadc_reset); + } + if (ths->ths_clk_enabled) + { + rt_clk_disable_unprepare(ths->ths_clk); + } + if (ths->gpadc_clk_enabled) + { + rt_clk_disable_unprepare(ths->gpadc_clk); + } + if (!rt_is_err_or_null(ths->ths_clk)) + { + rt_clk_put(ths->ths_clk); + } + if (!rt_is_err_or_null(ths->gpadc_clk)) + { + rt_clk_put(ths->gpadc_clk); + } + if (ths->gpadc) + { + rt_iounmap(ths->gpadc); + } + if (ths->ths) + { + rt_iounmap(ths->ths); + } + rt_free(ths); +} + +static rt_err_t sun252i_f101_thermal_map_calibration(struct rt_device *dev, + struct sunxi_thermal *ths) +{ + rt_err_t err; + struct rt_nvmem_cell *cell; + + cell = rt_nvmem_get_cell_by_name(dev, "calibration"); + if (rt_is_err_or_null(cell)) + { + return rt_is_err(cell) ? rt_ptr_err(cell) : -RT_ENOENT; + } + + err = rt_nvmem_cell_read_u32(cell, &ths->sid_calibration); + rt_nvmem_put_cell(cell); + if (err == sizeof(ths->sid_calibration)) + { + ths->sid_calibration_valid = RT_TRUE; + return RT_EOK; + } + + return err < 0 ? err : -RT_EIO; +} + +static rt_err_t sun252i_f101_thermal_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct sunxi_thermal *ths = rt_calloc(1, sizeof(*ths)); + + if (!ths) + { + return -RT_ENOMEM; + } + + ths->ths = rt_ofw_iomap_by_name(dev->ofw_node, "ths"); + ths->gpadc = rt_ofw_iomap_by_name(dev->ofw_node, "gpadc"); + if (!ths->ths || !ths->gpadc) + { + err = -RT_EIO; + goto _fail; + } + + ths->ths_clk = rt_clk_get_by_name(dev, "ths"); + ths->gpadc_clk = rt_clk_get_by_name(dev, "gpadc"); + ths->ths_reset = rt_reset_control_get_by_name(dev, "ths"); + ths->gpadc_reset = rt_reset_control_get_by_name(dev, "gpadc"); + if (rt_is_err(ths->ths_clk) || rt_is_err(ths->gpadc_clk) || + rt_is_err(ths->ths_reset) || rt_is_err(ths->gpadc_reset)) + { + err = rt_is_err(ths->ths_clk) ? rt_ptr_err(ths->ths_clk) : + (rt_is_err(ths->gpadc_clk) ? rt_ptr_err(ths->gpadc_clk) : + (rt_is_err(ths->ths_reset) ? rt_ptr_err(ths->ths_reset) : + rt_ptr_err(ths->gpadc_reset))); + goto _fail; + } + + if ((err = rt_reset_control_deassert(ths->gpadc_reset)) || + (err = rt_reset_control_deassert(ths->ths_reset)) || + (err = rt_clk_prepare_enable(ths->gpadc_clk))) + { + goto _fail; + } + ths->gpadc_clk_enabled = RT_TRUE; + if ((err = rt_clk_prepare_enable(ths->ths_clk))) + { + goto _fail; + } + ths->ths_clk_enabled = RT_TRUE; + + HWREG32(ths->gpadc + SUNXI_GPADC_CTRL) |= SUNXI_GPADC_VCM_BUF_EN; + HWREG32(ths->ths + SUNXI_THS_CTL) = (479 << 16) | 47; + HWREG32(ths->ths + SUNXI_THS_MFC) = RT_BIT(2) | 1; + HWREG32(ths->ths + SUNXI_THS_PCTL) = 28 << 12; + HWREG32(ths->ths + SUNXI_THS_EN) = 1; + + if ((err = sun252i_f101_thermal_map_calibration(dev, ths))) + { + LOG_W("thermal sensor will run without eFuse calibration: %s", rt_strerror(err)); + } + else + { + sun252i_f101_ths_calibrate(ths); + } + + /* Four averaged samples take roughly 20 ms with the configured period. */ + rt_hw_us_delay(20000); + + ths->zone.parent.ofw_node = dev->ofw_node; + ths->zone.ops = &sun252i_f101_thermal_ops; + /* The F101 DTS has no thermal-zone policy from which to obtain a delay. */ + ths->zone.polling_delay = rt_tick_from_millisecond(1000); + rt_dm_dev_set_name(&ths->zone.parent, "sun252i-ths"); + if ((err = rt_thermal_zone_device_register(&ths->zone))) + { + goto _fail; + } + + dev->user_data = ths; + + return RT_EOK; + +_fail: + sun252i_f101_thermal_release(ths); + + return err; +} + +static rt_err_t sun252i_f101_thermal_remove(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct sunxi_thermal *ths = pdev->parent.user_data; + + err = rt_thermal_zone_device_unregister(&ths->zone); + if (err) + { + return err; + } + + sun252i_f101_thermal_release(ths); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sun252i_f101_thermal_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-thermal" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sun252i_f101_thermal_driver = +{ + .name = "sun252i-thermal", + .ids = sun252i_f101_thermal_ofw_ids, + .probe = sun252i_f101_thermal_probe, + .remove = sun252i_f101_thermal_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sun252i_f101_thermal_driver); diff --git a/bsp/allwinner/dm/usb/Kconfig b/bsp/allwinner/dm/usb/Kconfig new file mode 100644 index 000000000000..23d9bec16f67 --- /dev/null +++ b/bsp/allwinner/dm/usb/Kconfig @@ -0,0 +1,11 @@ +config RT_USB_SUNXI_MUSB + bool "Allwinner Sunxi MUSB device glue" + depends on RT_USING_CHERRYUSB + depends on RT_CHERRYUSB_DEVICE + depends on RT_CHERRYUSB_DEVICE_MUSB_SUNXI + depends on RT_USING_DM + depends on RT_USING_CLK + depends on RT_USING_RESET + select RT_USING_MFD + select RT_MFD_SYSCON + default y diff --git a/bsp/allwinner/dm/usb/SConscript b/bsp/allwinner/dm/usb/SConscript new file mode 100644 index 000000000000..fc85ebbbfe3e --- /dev/null +++ b/bsp/allwinner/dm/usb/SConscript @@ -0,0 +1,10 @@ +from building import * + +src = [] + +if GetDepend('RT_USB_SUNXI_MUSB'): + src += ['usb-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = ['']) + +Return('group') diff --git a/bsp/allwinner/dm/usb/usb-sunxi.c b/bsp/allwinner/dm/usb/usb-sunxi.c new file mode 100644 index 000000000000..273e5f7e9036 --- /dev/null +++ b/bsp/allwinner/dm/usb/usb-sunxi.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define DBG_TAG "usb.sunxi" +#define DBG_LVL DBG_INFO +#include + +#include + +struct sunxi_musb_data +{ + rt_uint32_t phy_iscr_offset; + rt_uint32_t phy_dpdm_pullup_mask; + rt_uint32_t phy_id_pullup_mask; + rt_uint32_t phy_force_id_mask; + rt_uint32_t phy_force_id_device; + rt_uint32_t phy_force_vbus_mask; + rt_uint32_t phy_force_vbus_valid; + rt_uint32_t phy_ctrl_offset; + rt_uint32_t phy_siddq_mask; + rt_uint32_t phy_vbus_valid_mask; + rt_uint32_t phy_sel_offset; + rt_uint32_t phy_otg_select_mask; + rt_uint32_t vend0_offset; + rt_uint8_t vend0_bus_select_mask; + rt_uint32_t syscfg_boot_rammap_offset; + rt_uint32_t syscfg_usb_sram_mask; +}; + +struct sunxi_musb +{ + struct rt_clk *bus_clk; + struct rt_clk *otg_clk; + struct rt_reset_control *phy_reset; + struct rt_reset_control *controller_reset; + struct rt_syscon *syscon; + const struct sunxi_musb_data *data; + void *base; + int irq; +}; + +static struct sunxi_musb *sunxi_musb0; + +extern void USBD_IRQHandler(rt_uint8_t busid); + +#ifdef RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_CHARDEV +extern void cdc_acm_chardev_init(uint8_t busid, uintptr_t reg_base); +#endif + +static void sunxi_musb_phy_device_mode(struct sunxi_musb *musb) +{ + rt_uint32_t value; + volatile rt_uint8_t *vend0; + + /* Force the shared OTG PHY into peripheral mode before soft-connect. */ + value = HWREG32(musb->base + musb->data->phy_iscr_offset); + value |= musb->data->phy_dpdm_pullup_mask | musb->data->phy_id_pullup_mask; + value &= ~(musb->data->phy_force_id_mask | musb->data->phy_force_vbus_mask); + value |= musb->data->phy_force_id_device | musb->data->phy_force_vbus_valid; + HWREG32(musb->base + musb->data->phy_iscr_offset) = value; + + value = HWREG32(musb->base + musb->data->phy_ctrl_offset); + value |= musb->data->phy_vbus_valid_mask; + value &= ~musb->data->phy_siddq_mask; + HWREG32(musb->base + musb->data->phy_ctrl_offset) = value; + + value = HWREG32(musb->base + musb->data->phy_sel_offset); + value |= musb->data->phy_otg_select_mask; + HWREG32(musb->base + musb->data->phy_sel_offset) = value; + + vend0 = (volatile rt_uint8_t *)(musb->base + musb->data->vend0_offset); + *vend0 |= musb->data->vend0_bus_select_mask; + + rt_syscon_update_bits(musb->syscon, musb->data->syscfg_boot_rammap_offset, + musb->data->syscfg_usb_sram_mask, 0); +} + +static void sunxi_musb_isr(int vector, void *param) +{ + RT_UNUSED(vector); + RT_UNUSED(param); + + USBD_IRQHandler(0); +} + +/* Called by CherryUSB after it has registered the MUSB register base. */ +void usb_dc_low_level_init(void) +{ + RT_ASSERT(sunxi_musb0 != RT_NULL); + + sunxi_musb_phy_device_mode(sunxi_musb0); +} + +static void sunxi_musb_free(struct sunxi_musb *musb) +{ + if (!rt_is_err_or_null(musb->controller_reset)) + { + rt_reset_control_assert(musb->controller_reset); + rt_reset_control_put(musb->controller_reset); + } + if (!rt_is_err_or_null(musb->phy_reset)) + { + rt_reset_control_assert(musb->phy_reset); + rt_reset_control_put(musb->phy_reset); + } + if (!rt_is_err_or_null(musb->otg_clk)) + { + rt_clk_disable_unprepare(musb->otg_clk); + rt_clk_put(musb->otg_clk); + } + if (!rt_is_err_or_null(musb->bus_clk)) + { + rt_clk_disable_unprepare(musb->bus_clk); + rt_clk_put(musb->bus_clk); + } + if (musb->base) + { + rt_iounmap(musb->base); + } + rt_free(musb); +} + +static rt_err_t sunxi_musb_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct sunxi_musb *musb; + + if (sunxi_musb0) + { + return -RT_EBUSY; + } + + musb = rt_calloc(1, sizeof(*musb)); + if (!musb) + { + return -RT_ENOMEM; + } + + musb->irq = -1; + musb->data = pdev->id->data; + musb->base = rt_dm_dev_iomap(dev, 0); + if (!musb->base) + { + err = -RT_EIO; + goto _fail; + } + + musb->syscon = rt_syscon_find_by_ofw_phandle(dev->ofw_node, + "allwinner,syscon"); + if (!musb->syscon) + { + err = -RT_EIO; + goto _fail; + } + + musb->bus_clk = rt_clk_get_by_name(dev, "bus"); + musb->otg_clk = rt_clk_get_by_name(dev, "mod"); + if (rt_is_err(musb->bus_clk) || rt_is_err(musb->otg_clk)) + { + err = rt_is_err(musb->bus_clk) ? rt_ptr_err(musb->bus_clk) : + rt_ptr_err(musb->otg_clk); + goto _fail; + } + if ((err = rt_clk_prepare_enable(musb->bus_clk)) || + (err = rt_clk_prepare_enable(musb->otg_clk))) + { + goto _fail; + } + + musb->phy_reset = rt_reset_control_get_by_name(dev, "phy"); + musb->controller_reset = rt_reset_control_get_by_name(dev, "controller"); + if (rt_is_err(musb->phy_reset) || rt_is_err(musb->controller_reset)) + { + err = rt_is_err(musb->phy_reset) ? rt_ptr_err(musb->phy_reset) : + rt_ptr_err(musb->controller_reset); + goto _fail; + } + + if ((err = rt_reset_control_deassert(musb->phy_reset))) + { + goto _fail; + } + rt_hw_us_delay(1000); + if ((err = rt_reset_control_deassert(musb->controller_reset))) + { + goto _fail; + } + rt_hw_us_delay(1000); + + musb->irq = rt_dm_dev_get_irq(dev, 0); + if (musb->irq < 0) + { + err = musb->irq; + goto _fail; + } + rt_hw_interrupt_install(musb->irq, sunxi_musb_isr, musb, "sunxi-musb"); + rt_hw_interrupt_umask(musb->irq); + + dev->user_data = musb; + rt_dm_dev_bind_fwdata(dev, RT_NULL, musb); + sunxi_musb0 = musb; + +#ifdef RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_CHARDEV + cdc_acm_chardev_init(0, (uintptr_t)musb->base); +#endif + + LOG_D("MUSB device controller ready"); + return RT_EOK; + +_fail: + sunxi_musb_free(musb); + return err; +} + +static rt_err_t sunxi_musb_remove(struct rt_platform_device *pdev) +{ + struct sunxi_musb *musb = pdev->parent.user_data; + + if (musb->irq >= 0) + { + rt_hw_interrupt_mask(musb->irq); + rt_pic_detach_irq(musb->irq, musb); + } + sunxi_musb0 = RT_NULL; + rt_dm_dev_unbind_fwdata(&pdev->parent, RT_NULL); + sunxi_musb_free(musb); + + return RT_EOK; +} + +static const struct sunxi_musb_data sun252i_f101_musb_data = +{ + .phy_iscr_offset = 0x400, + .phy_dpdm_pullup_mask = RT_BIT(16), + .phy_id_pullup_mask = RT_BIT(17), + .phy_force_id_mask = 0xc000, + .phy_force_id_device = 0xc000, + .phy_force_vbus_mask = 0x3000, + .phy_force_vbus_valid = 0x3000, + .phy_ctrl_offset = 0x410, + .phy_siddq_mask = RT_BIT(3), + .phy_vbus_valid_mask = RT_BIT(5), + .phy_sel_offset = 0x420, + .phy_otg_select_mask = RT_BIT(0), + .vend0_offset = 0x43, + .vend0_bus_select_mask = RT_BIT(0), + .syscfg_boot_rammap_offset = 0x4, + .syscfg_usb_sram_mask = RT_BIT(27) | RT_BIT(25), +}; + +static const struct rt_ofw_node_id sunxi_musb_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-musb", .data = &sun252i_f101_musb_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_musb_driver = +{ + .name = "sunxi-musb", + .ids = sunxi_musb_ofw_ids, + .probe = sunxi_musb_probe, + .remove = sunxi_musb_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_musb_driver); diff --git a/bsp/allwinner/dm/watchdog/Kconfig b/bsp/allwinner/dm/watchdog/Kconfig new file mode 100644 index 000000000000..450a29fc0c9e --- /dev/null +++ b/bsp/allwinner/dm/watchdog/Kconfig @@ -0,0 +1,5 @@ +config RT_WDT_SUNXI + bool "Allwinner Sunxi watchdog" + depends on RT_USING_DM + depends on RT_USING_WDT + default n diff --git a/bsp/allwinner/dm/watchdog/SConscript b/bsp/allwinner/dm/watchdog/SConscript new file mode 100644 index 000000000000..8370f0994bd5 --- /dev/null +++ b/bsp/allwinner/dm/watchdog/SConscript @@ -0,0 +1,13 @@ +from building import * + +group = [] +src = [] +cwd = GetCurrentDir() +CPPPATH = [cwd + '/../include'] + +if GetDepend(['RT_WDT_SUNXI']): + src += ['watchdog-sunxi.c'] + +group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/allwinner/dm/watchdog/watchdog-sunxi.c b/bsp/allwinner/dm/watchdog/watchdog-sunxi.c new file mode 100644 index 000000000000..52a2e5cb47cf --- /dev/null +++ b/bsp/allwinner/dm/watchdog/watchdog-sunxi.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define SUNXI_WDT_IRQ_EN 0x00 +#define SUNXI_WDT_CTRL 0x10 +#define SUNXI_WDT_CFG 0x14 +#define SUNXI_WDT_MODE 0x18 + +#define SUNXI_WDT_CTRL_RELOAD (RT_BIT(0) | (0x0a57 << 1)) +#define SUNXI_WDT_MODE_ENABLE RT_BIT(0) +#define SUNXI_WDT_TIMEOUT_SHIFT 4 +#define SUNXI_WDT_TIMEOUT_MASK RT_GENMASK(7, 4) +#define SUNXI_WDT_RESET_MASK RT_GENMASK(1, 0) +#define SUNXI_WDT_RESET_SYSTEM 1 +#define SUNXI_WDT_KEY 0x16aa0000 + +static const rt_uint8_t sunxi_wdt_timeout_codes[] = +{ + [1] = 0x1, [2] = 0x2, [3] = 0x3, [4] = 0x4, + [5] = 0x5, [6] = 0x6, [8] = 0x7, [10] = 0x8, + [12] = 0x9, [14] = 0xa, [16] = 0xb, +}; + +struct sunxi_wdt +{ + rt_watchdog_t parent; + void *base; + rt_uint32_t timeout; + const rt_uint8_t *timeout_codes; +}; + +#define to_sunxi_wdt(raw) rt_container_of(raw, struct sunxi_wdt, parent) + +static struct sunxi_wdt *machine_wdt; + +static const rt_uint8_t sunxi_wdt_timeout_codes_v2[] = +{ + [1] = 0x5, [2] = 0x6, [3] = 0x7, [4] = 0x8, + [5] = 0x9, [6] = 0xa, [8] = 0xb, [10] = 0xc, + [12] = 0xd, [14] = 0xe, [16] = 0xf, +}; + +static rt_uint32_t sunxi_wdt_select_timeout(struct sunxi_wdt *wdt, + rt_uint32_t requested) +{ + rt_uint32_t timeout; + + for (timeout = rt_max_t(rt_uint32_t, requested, 1); timeout <= 16; ++timeout) + { + if (wdt->timeout_codes[timeout]) + { + return timeout; + } + } + + return 0; +} + +static void sunxi_wdt_ping(struct sunxi_wdt *wdt) +{ + HWREG32(wdt->base + SUNXI_WDT_CTRL) = SUNXI_WDT_CTRL_RELOAD; +} + +static rt_err_t sunxi_wdt_set_timeout(struct sunxi_wdt *wdt, + rt_uint32_t requested) +{ + rt_uint32_t timeout = sunxi_wdt_select_timeout(wdt, requested); + rt_uint32_t value; + + if (!timeout) + { + return -RT_EINVAL; + } + + value = HWREG32(wdt->base + SUNXI_WDT_MODE); + value &= ~SUNXI_WDT_TIMEOUT_MASK; + value |= wdt->timeout_codes[timeout] << SUNXI_WDT_TIMEOUT_SHIFT; + HWREG32(wdt->base + SUNXI_WDT_MODE) = value | SUNXI_WDT_KEY; + + wdt->timeout = timeout; + sunxi_wdt_ping(wdt); + + return RT_EOK; +} + +static rt_err_t sunxi_wdt_start(struct sunxi_wdt *wdt) +{ + rt_uint32_t value; + rt_err_t err; + + if ((err = sunxi_wdt_set_timeout(wdt, wdt->timeout))) + { + return err; + } + + value = HWREG32(wdt->base + SUNXI_WDT_CFG); + value &= ~SUNXI_WDT_RESET_MASK; + value |= SUNXI_WDT_RESET_SYSTEM; + HWREG32(wdt->base + SUNXI_WDT_CFG) = value | SUNXI_WDT_KEY; + + value = HWREG32(wdt->base + SUNXI_WDT_MODE); + HWREG32(wdt->base + SUNXI_WDT_MODE) = value | SUNXI_WDT_MODE_ENABLE | + SUNXI_WDT_KEY; + + return RT_EOK; +} + +static void sunxi_wdt_stop(struct sunxi_wdt *wdt) +{ + HWREG32(wdt->base + SUNXI_WDT_MODE) = SUNXI_WDT_KEY; +} + +static void sunxi_wdt_machine_reset(void) +{ + struct sunxi_wdt *wdt = machine_wdt; + + if (!wdt) + { + return; + } + + sunxi_wdt_stop(wdt); + HWREG32(wdt->base + SUNXI_WDT_IRQ_EN) = 0; + HWREG32(wdt->base + SUNXI_WDT_CFG) = SUNXI_WDT_KEY | + SUNXI_WDT_RESET_SYSTEM; + HWREG32(wdt->base + SUNXI_WDT_MODE) = SUNXI_WDT_KEY | + SUNXI_WDT_MODE_ENABLE; + + for (;;) + { + rt_hw_cpu_relax(); + } +} + +static rt_err_t sunxi_wdt_init(rt_watchdog_t *watchdog) +{ + RT_UNUSED(watchdog); + return RT_EOK; +} + +static rt_err_t sunxi_wdt_control(rt_watchdog_t *watchdog, int cmd, + void *args) +{ + struct sunxi_wdt *wdt = to_sunxi_wdt(watchdog); + + switch (cmd) + { + case RT_DEVICE_CTRL_WDT_GET_TIMEOUT: + if (!args) + { + return -RT_EINVAL; + } + *(rt_uint32_t *)args = wdt->timeout; + return RT_EOK; + case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: + if (!args) + { + return -RT_EINVAL; + } + return sunxi_wdt_set_timeout(wdt, *(rt_uint32_t *)args); + case RT_DEVICE_CTRL_WDT_KEEPALIVE: + sunxi_wdt_ping(wdt); + return RT_EOK; + case RT_DEVICE_CTRL_WDT_START: + return sunxi_wdt_start(wdt); + case RT_DEVICE_CTRL_WDT_STOP: + sunxi_wdt_stop(wdt); + return RT_EOK; + default: + return -RT_EINVAL; + } +} + +static const struct rt_watchdog_ops sunxi_wdt_ops = +{ + .init = sunxi_wdt_init, + .control = sunxi_wdt_control, +}; + +static rt_err_t sunxi_wdt_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *name; + struct rt_device *dev = &pdev->parent; + struct sunxi_wdt *wdt = rt_calloc(1, sizeof(*wdt)); + + if (!wdt) + { + return -RT_ENOMEM; + } + + wdt->base = rt_dm_dev_iomap(dev, 0); + if (!wdt->base) + { + err = -RT_EIO; + goto _fail; + } + + if (rt_ofw_node_is_compatible(dev->ofw_node, + "allwinner,sun252i-f101-wdt")) + { + wdt->timeout_codes = sunxi_wdt_timeout_codes_v2; + } + else + { + wdt->timeout_codes = sunxi_wdt_timeout_codes; + } + wdt->timeout = 16; + sunxi_wdt_stop(wdt); + wdt->parent.ops = &sunxi_wdt_ops; + wdt->parent.parent.ofw_node = dev->ofw_node; + rt_dm_dev_set_name_auto(&wdt->parent.parent, "wdt"); + name = rt_dm_dev_get_name(&wdt->parent.parent); + + if ((err = rt_hw_watchdog_register(&wdt->parent, name, 0, wdt))) + { + goto _fail; + } + + dev->user_data = wdt; + if (!rt_dm_machine_reset) + { + machine_wdt = wdt; + rt_dm_machine_reset = sunxi_wdt_machine_reset; + } + return RT_EOK; + +_fail: + if (wdt->base) + { + rt_iounmap(wdt->base); + } + rt_free(wdt); + return err; +} + +static rt_err_t sunxi_wdt_remove(struct rt_platform_device *pdev) +{ + struct sunxi_wdt *wdt = pdev->parent.user_data; + + if (machine_wdt == wdt) + { + machine_wdt = RT_NULL; + rt_dm_machine_reset = RT_NULL; + } + sunxi_wdt_stop(wdt); + rt_device_unregister(&wdt->parent.parent); + rt_iounmap(wdt->base); + rt_free(wdt); + + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_wdt_ofw_ids[] = +{ + { .compatible = "allwinner,sun252i-f101-wdt" }, + { .compatible = "allwinner,sun20i-d1-wdt" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_wdt_driver = +{ + .name = "sunxi-wdt", + .ids = sunxi_wdt_ofw_ids, + .probe = sunxi_wdt_probe, + .remove = sunxi_wdt_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_wdt_driver); diff --git a/bsp/allwinner/f101/.config b/bsp/allwinner/f101/.config new file mode 100644 index 000000000000..9dd94df2d46d --- /dev/null +++ b/bsp/allwinner/f101/.config @@ -0,0 +1,1844 @@ + +# +# RT-Thread Kernel +# + +# +# klibc options +# + +# +# rt_vsnprintf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set +# CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG is not set +# CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD is not set +# end of rt_vsnprintf options + +# +# rt_vsscanf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSSCANF is not set +# end of rt_vsscanf options + +# +# rt_memset options +# +# CONFIG_RT_KLIBC_USING_USER_MEMSET is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMSET is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMSET is not set +# end of rt_memset options + +# +# rt_memcpy options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMCPY is not set +# end of rt_memcpy options + +# +# rt_memmove options +# +# CONFIG_RT_KLIBC_USING_USER_MEMMOVE is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMMOVE is not set +# end of rt_memmove options + +# +# rt_memcmp options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCMP is not set +# end of rt_memcmp options + +# +# rt_strstr options +# +# CONFIG_RT_KLIBC_USING_USER_STRSTR is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRSTR is not set +# end of rt_strstr options + +# +# rt_strcasecmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCASECMP is not set +# end of rt_strcasecmp options + +# +# rt_strncpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCPY is not set +# end of rt_strncpy options + +# +# rt_strcpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCPY is not set +# end of rt_strcpy options + +# +# rt_strncmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCMP is not set +# end of rt_strncmp options + +# +# rt_strcmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCMP is not set +# end of rt_strcmp options + +# +# rt_strlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRLEN is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRLEN is not set +# end of rt_strlen options + +# +# rt_strnlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRNLEN is not set +# end of rt_strnlen options +# end of klibc options + +CONFIG_RT_NAME_MAX=24 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +# CONFIG_RT_USING_NANO is not set +# CONFIG_RT_USING_AMP is not set +# CONFIG_RT_USING_SMP is not set +CONFIG_RT_CPUS_NR=1 +CONFIG_RT_ALIGN_SIZE=8 +# CONFIG_RT_THREAD_PRIORITY_8 is not set +CONFIG_RT_THREAD_PRIORITY_32=y +# CONFIG_RT_THREAD_PRIORITY_256 is not set +CONFIG_RT_THREAD_PRIORITY_MAX=32 +CONFIG_RT_TICK_PER_SECOND=1000 +CONFIG_RT_USING_OVERFLOW_CHECK=y +CONFIG_RT_USING_HOOK=y +CONFIG_RT_HOOK_USING_FUNC_PTR=y +# CONFIG_RT_USING_HOOKLIST is not set +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=1024 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=2048 +# CONFIG_RT_USING_TIMER_ALL_SOFT is not set +# CONFIG_RT_USING_CPU_USAGE_TRACER is not set + +# +# kservice options +# +# CONFIG_RT_USING_TINY_FFS is not set +# end of kservice options + +CONFIG_RT_USING_DEBUG=y +CONFIG_RT_DEBUGING_ASSERT=y +CONFIG_RT_DEBUGING_COLOR=y +# CONFIG_RT_DEBUGING_CONTEXT is not set +# CONFIG_RT_DEBUGING_AUTO_INIT is not set +# CONFIG_RT_USING_CI_ACTION is not set + +# +# Inter-Thread communication +# +CONFIG_RT_USING_SEMAPHORE=y +CONFIG_RT_USING_MUTEX=y +CONFIG_RT_USING_EVENT=y +CONFIG_RT_USING_MAILBOX=y +CONFIG_RT_USING_MESSAGEQUEUE=y +# CONFIG_RT_USING_MESSAGEQUEUE_PRIORITY is not set +# CONFIG_RT_USING_SIGNALS is not set +# end of Inter-Thread communication + +# +# Memory Management +# +CONFIG_RT_USING_MEMPOOL=y +CONFIG_RT_USING_SMALL_MEM=y +# CONFIG_RT_USING_SLAB is not set +# CONFIG_RT_USING_MEMHEAP is not set +CONFIG_RT_USING_SMALL_MEM_AS_HEAP=y +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +# CONFIG_RT_USING_SLAB_AS_HEAP is not set +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +# CONFIG_RT_USING_MEMTRACE is not set +# CONFIG_RT_USING_HEAP_ISR is not set +CONFIG_RT_USING_HEAP=y +# end of Memory Management + +CONFIG_RT_USING_DEVICE=y +# CONFIG_RT_USING_DEVICE_OPS is not set +CONFIG_RT_USING_INTERRUPT_INFO=y +# CONFIG_RT_USING_THREADSAFE_PRINTF is not set +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=256 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart1" +CONFIG_RT_USING_CONSOLE_OUTPUT_CTL=y +CONFIG_RT_VER_NUM=0x50301 +# CONFIG_RT_USING_STDC_ATOMIC is not set +CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32 +# end of RT-Thread Kernel + +CONFIG_ARCH_TEXT_OFFSET=0x10000 +CONFIG_ARCH_RAM_OFFSET=0x40000000 +CONFIG_ARCH_HEAP_SIZE=0x800000 +CONFIG_RT_USING_CACHE=y +CONFIG_RT_USING_HW_ATOMIC=y +CONFIG_RT_USING_CPU_FFS=y +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_RISCV_FPU=y +CONFIG_ARCH_RISCV_FPU_D=y +CONFIG_ARCH_RISCV32=y + +# +# RISC-V Architecture Configuration +# +CONFIG_RT_USING_RISCV_NEW_COMMON=y +CONFIG_ARCH_RISCV_M_MODE=y +CONFIG_ARCH_RISCV_USING_IRQ_STACK=y +CONFIG_ARCH_RISCV_IRQ_STACK_SIZE=1024 +# end of RISC-V Architecture Configuration + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=2048 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=4096 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=5 +# CONFIG_FINSH_USING_WORD_OPERATION is not set +# CONFIG_FINSH_USING_FUNC_EXT is not set +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 +CONFIG_FINSH_USING_OPTION_COMPLETION=y + +# +# DFS: device virtual file system +# +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +# CONFIG_RT_USING_DFS_MNTTABLE is not set +CONFIG_DFS_FD_MAX=16 +CONFIG_RT_USING_DFS_V1=y +# CONFIG_RT_USING_DFS_V2 is not set +CONFIG_DFS_FILESYSTEMS_MAX=4 +CONFIG_DFS_FILESYSTEM_TYPES_MAX=4 +CONFIG_RT_USING_DFS_ELMFAT=y + +# +# elm-chan's FatFs, Generic FAT Filesystem Module +# +CONFIG_RT_DFS_ELM_CODE_PAGE=437 +CONFIG_RT_DFS_ELM_WORD_ACCESS=y +# CONFIG_RT_DFS_ELM_USE_LFN_0 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_1 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set +CONFIG_RT_DFS_ELM_USE_LFN_3=y +CONFIG_RT_DFS_ELM_USE_LFN=3 +CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y +# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set +CONFIG_RT_DFS_ELM_LFN_UNICODE=0 +CONFIG_RT_DFS_ELM_MAX_LFN=255 +CONFIG_RT_DFS_ELM_DRIVES=2 +CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512 +# CONFIG_RT_DFS_ELM_USE_ERASE is not set +CONFIG_RT_DFS_ELM_REENTRANT=y +CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000 +CONFIG_RT_DFS_ELM_USE_EXFAT=y +# end of elm-chan's FatFs, Generic FAT Filesystem Module + +CONFIG_RT_USING_DFS_DEVFS=y +# CONFIG_RT_USING_DFS_ROMFS is not set +# CONFIG_RT_USING_DFS_CROMFS is not set +# CONFIG_RT_USING_DFS_RAMFS is not set +# CONFIG_RT_USING_DFS_TMPFS is not set +# CONFIG_RT_USING_DFS_MQUEUE is not set +# end of DFS: device virtual file system + +# CONFIG_RT_USING_FAL is not set + +# +# Device Drivers +# +CONFIG_RT_USING_DM=y +# CONFIG_RT_USING_DEV_BUS is not set +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_UNAMED_PIPE_NUMBER=64 +CONFIG_RT_USING_SYSTEM_WORKQUEUE=y +CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=2048 +CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +CONFIG_RT_SERIAL_USING_DMA=y +CONFIG_RT_SERIAL_RB_BUFSZ=2048 +# CONFIG_RT_USING_SERIAL_BYPASS is not set +# CONFIG_RT_SERIAL_EARLY_HVC is not set +# CONFIG_RT_SERIAL_PL011 is not set +CONFIG_RT_SERIAL_8250=y +CONFIG_RT_SERIAL_8250_DW=y +# CONFIG_RT_USING_CAN is not set +CONFIG_RT_USING_ADC=y +# CONFIG_RT_USING_ADC_V2 is not set +CONFIG_RT_USING_ADC_V1=y +CONFIG_RT_ADC_SUNXI_GPADC=y +CONFIG_RT_USING_CLOCK_TIME=y +# CONFIG_RT_USING_CLOCK_TIMER_TRIGGER is not set +CONFIG_RT_CLOCK_TIMER_SUNXI=y +CONFIG_RT_USING_I2C=y +# CONFIG_RT_I2C_DEBUG is not set +CONFIG_RT_USING_I2C_BITOPS=y +# CONFIG_RT_I2C_BITOPS_DEBUG is not set +# CONFIG_RT_USING_SOFT_I2C is not set +CONFIG_RT_I2C_SUNXI=y +# CONFIG_RT_USING_PHY is not set +# CONFIG_RT_USING_PHY_V2 is not set +# CONFIG_RT_USING_DAC is not set +# CONFIG_RT_USING_NULL is not set +# CONFIG_RT_USING_ZERO is not set +# CONFIG_RT_USING_RANDOM is not set +CONFIG_RT_USING_PWM=y +CONFIG_RT_PWM_SUNXI=y +# CONFIG_RT_USING_PULSE_ENCODER is not set +CONFIG_RT_USING_INPUT_CAPTURE=y +CONFIG_RT_INPUT_CAPTURE_RB_SIZE=100 +CONFIG_RT_INPUT_CAPTURE_SUNXI_IRRX=y +CONFIG_RT_USING_MTD_NOR=y +# CONFIG_RT_USING_MTD_NOR_CFI is not set +CONFIG_RT_USING_MTD_NOR_SPI=y +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_PM is not set +CONFIG_RT_USING_RTC=y +CONFIG_RT_USING_ALARM=y +CONFIG_RT_ALARM_STACK_SIZE=1024 +CONFIG_RT_ALARM_TIMESLICE=5 +CONFIG_RT_ALARM_PRIORITY=10 +# CONFIG_RT_ALARM_USING_LOCAL_TIME is not set +# CONFIG_RT_USING_SOFT_RTC is not set +# CONFIG_RT_RTC_DS1302 is not set +# CONFIG_RT_RTC_DS1307 is not set +# CONFIG_RT_RTC_GOLDFISH is not set +# CONFIG_RT_RTC_HYM8563 is not set +# CONFIG_RT_RTC_PCF8523 is not set +# CONFIG_RT_RTC_PCF8563 is not set +# CONFIG_RT_RTC_PL031 is not set +# CONFIG_RT_RTC_RX8010 is not set +CONFIG_RT_RTC_SUNXI=y +CONFIG_RT_USING_SDIO=y +CONFIG_RT_SDIO_STACK_SIZE=2048 +CONFIG_RT_SDIO_THREAD_PRIORITY=15 +CONFIG_RT_MMCSD_STACK_SIZE=4096 +CONFIG_RT_MMCSD_THREAD_PRIORITY=22 +CONFIG_RT_MMCSD_MAX_PARTITION=16 +# CONFIG_RT_SDIO_DEBUG is not set +# CONFIG_RT_USING_SDHCI is not set +# CONFIG_RT_SDIO_DW_MMC is not set +# CONFIG_RT_SDIO_SDHCI_DWCMSHC is not set +CONFIG_RT_SDIO_SUNXI=y +CONFIG_RT_USING_SPI=y +CONFIG_RT_USING_SPI_ISR=y +# CONFIG_RT_USING_SPI_BITOPS is not set +# CONFIG_RT_USING_SOFT_SPI is not set +CONFIG_RT_USING_QSPI=y +# CONFIG_RT_USING_SPI_MSD is not set +CONFIG_RT_USING_SFUD=y +CONFIG_RT_SFUD_USING_SFDP=y +CONFIG_RT_SFUD_USING_FLASH_INFO_TABLE=y +# CONFIG_RT_SFUD_USING_QSPI is not set +CONFIG_RT_SFUD_SPI_MAX_HZ=50000000 +# CONFIG_RT_DEBUG_SFUD is not set +# CONFIG_RT_USING_ENC28J60 is not set +# CONFIG_RT_USING_SPI_WIFI is not set +CONFIG_RT_SPI_SUNXI=y +CONFIG_RT_QSPI_SUNXI=y +CONFIG_RT_USING_WDT=y +# CONFIG_RT_WDT_DW is not set +CONFIG_RT_WDT_SUNXI=y +CONFIG_RT_USING_AUDIO=y +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_SIZE=4096 +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_COUNT=2 +CONFIG_RT_AUDIO_RECORD_PIPE_SIZE=2048 +CONFIG_RT_AUDIO_SUNXI_CODEC=y +CONFIG_RT_AUDIO_SUNXI_I2S0=y +CONFIG_RT_AUDIO_SUNXI_OWA=y +# CONFIG_RT_USING_SENSOR is not set +CONFIG_RT_USING_TOUCH=y +# CONFIG_RT_TOUCH_PIN_IRQ is not set +CONFIG_RT_USING_LCD=y +CONFIG_RT_USING_GRAPHIC=y +CONFIG_RT_GRAPHIC_BACKLIGHT=y +# CONFIG_RT_GRAPHIC_BACKLIGHT_GPIO is not set +CONFIG_RT_GRAPHIC_BACKLIGHT_PWM=y +CONFIG_RT_GRAPHIC_FB=y +# CONFIG_RT_GRAPHIC_FB_ILI9486 is not set +CONFIG_RT_GRAPHIC_FB_ST77916=y +# CONFIG_RT_GRAPHIC_FB_SIMPLE is not set +CONFIG_RT_GRAPHIC_FB_SUNXI_DE=y +CONFIG_RT_GRAPHIC_LOGO=y +# CONFIG_RT_GRAPHIC_LOGO_NONE is not set +CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_CLUT224=y +# CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_WHITE_CLUT224 is not set +CONFIG_RT_USING_HWCRYPTO=y +CONFIG_RT_HWCRYPTO_DEFAULT_NAME="hwcryto" +CONFIG_RT_HWCRYPTO_IV_MAX_SIZE=16 +CONFIG_RT_HWCRYPTO_KEYBIT_MAX_SIZE=256 +# CONFIG_RT_HWCRYPTO_USING_GCM is not set +CONFIG_RT_HWCRYPTO_USING_AES=y +CONFIG_RT_HWCRYPTO_USING_AES_ECB=y +CONFIG_RT_HWCRYPTO_USING_AES_CBC=y +# CONFIG_RT_HWCRYPTO_USING_AES_CFB is not set +CONFIG_RT_HWCRYPTO_USING_AES_CTR=y +# CONFIG_RT_HWCRYPTO_USING_AES_OFB is not set +# CONFIG_RT_HWCRYPTO_USING_DES is not set +# CONFIG_RT_HWCRYPTO_USING_3DES is not set +# CONFIG_RT_HWCRYPTO_USING_RC4 is not set +# CONFIG_RT_HWCRYPTO_USING_MD5 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA1 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA2 is not set +CONFIG_RT_HWCRYPTO_USING_RNG=y +# CONFIG_RT_HWCRYPTO_USING_CRC is not set +# CONFIG_RT_HWCRYPTO_USING_BIGNUM is not set +CONFIG_RT_HWCRYPTO_SUNXI_CE=y +# CONFIG_RT_USING_WIFI is not set +# CONFIG_RT_USING_PTP is not set +# CONFIG_RT_USING_LED is not set +CONFIG_RT_USING_INPUT=y +CONFIG_RT_INPUT_POWER=y +# CONFIG_RT_INPUT_JOYSTICK is not set +# CONFIG_RT_INPUT_KEYBOARD is not set +# CONFIG_RT_INPUT_MISC is not set +CONFIG_RT_INPUT_TOUCHSCREEN=y +# CONFIG_RT_INPUT_TOUCHSCREEN_ADS7846 is not set +CONFIG_RT_INPUT_TOUCHSCREEN_GOODIX=y +CONFIG_RT_INPUT_TOUCHSCREEN_SUNXI_TPADC=y +# CONFIG_RT_USING_MBOX is not set +# CONFIG_RT_USING_HWSPINLOCK is not set +# CONFIG_RT_USING_RPMSG is not set +# CONFIG_RT_USING_PHYE is not set +# CONFIG_RT_USING_ATA is not set +# CONFIG_RT_USING_NVME is not set +CONFIG_RT_USING_BLK=y + +# +# Partition Types +# +CONFIG_RT_BLK_PARTITION_DFS=y +CONFIG_RT_BLK_PARTITION_EFI=y +# end of Partition Types + +# CONFIG_RT_USING_SCSI is not set +# CONFIG_RT_USING_FIRMWARE is not set +# CONFIG_RT_USING_TEE is not set +# CONFIG_RT_USING_HWCACHE is not set +CONFIG_RT_USING_DVFS=y +CONFIG_RT_USING_DVFS_EVENT=y +CONFIG_RT_USING_DVFS_OPP_RETRY_MAX=10 + +# +# DVFS Event Drivers +# + +# +# DVFS CPUfreq Drivers +# +CONFIG_RT_DVFS_SUN252I_F101_CPUFREQ=y + +# +# DVFS Devfreq Drivers +# +CONFIG_RT_USING_REGULATOR=y +# CONFIG_RT_REGULATOR_FAN53555 is not set +CONFIG_RT_REGULATOR_FIXED=y +# CONFIG_RT_REGULATOR_GPIO is not set +# CONFIG_RT_REGULATOR_PWM is not set +CONFIG_RT_USING_RESET=y +# CONFIG_RT_RESET_SIMPLE is not set +CONFIG_RT_RESET_SUN252I_F101=y + +# +# Power Management (PM) Domains device drivers +# +# end of Power Management (PM) Domains device drivers + +# CONFIG_RT_USING_POWER_RESET is not set +# CONFIG_RT_USING_POWER_SUPPLY is not set +CONFIG_RT_USING_THERMAL=y + +# +# Thermal Sensors Drivers +# +CONFIG_RT_THERMAL_SUNXI=y + +# +# Thermal Cool Drivers +# +# CONFIG_RT_THERMAL_COOL_DVFS is not set +# CONFIG_RT_THERMAL_COOL_GPIO_FAN is not set +# CONFIG_RT_THERMAL_COOL_PWM_FAN is not set +# CONFIG_RT_USING_VIRTIO is not set +CONFIG_RT_USING_NVMEM=y +CONFIG_RT_NVMEM_SUNXI_SID=y +CONFIG_RT_USING_DMA=y +# CONFIG_RT_DMA_PL330 is not set +# CONFIG_RT_DMA_DW_AXI_DMAC is not set +CONFIG_RT_DMA_SUNXI=y +CONFIG_RT_USING_MFD=y +CONFIG_RT_MFD_SYSCON=y +CONFIG_RT_USING_OFW=y +CONFIG_RT_USING_BUILTIN_FDT=y +CONFIG_RT_BUILTIN_FDT_PATH="../dm/dts/sun252i-f101-yuzukineko.dtb" +CONFIG_RT_FDT_EARLYCON_MSG_SIZE=128 +CONFIG_RT_USING_OFW_BUS_RANGES_NUMBER=4 +# CONFIG_RT_USING_PCI is not set +CONFIG_RT_USING_PIC=y +# CONFIG_RT_USING_PIC_STATISTICS is not set +CONFIG_MAX_HANDLERS=256 +# CONFIG_RT_PIC_ARM_GIC is not set +# CONFIG_RT_PIC_ARM_GIC_V3 is not set +# CONFIG_RT_PIC_RISCV_AIA is not set +# CONFIG_RT_PIC_RISCV_CLIC is not set +CONFIG_RT_PIC_RISCV_INTC=y +CONFIG_RT_PIC_SIFIVE_PLIC=y +CONFIG_RT_USING_PIN=y +# CONFIG_RT_PIN_PL061 is not set +CONFIG_RT_USING_PINCTRL=y +# CONFIG_RT_PINCTRL_SINGLE is not set +CONFIG_RT_PINCTRL_SUN252I_F101=y +CONFIG_RT_USING_CLK=y +CONFIG_RT_CLK_SUN252I_F101=y +CONFIG_RT_USING_CHERRYUSB=y +CONFIG_RT_CHERRYUSB_DEVICE=y +CONFIG_RT_CHERRYUSB_DEVICE_SPEED_FS=y +# CONFIG_RT_CHERRYUSB_DEVICE_SPEED_HS is not set +# CONFIG_RT_CHERRYUSB_DEVICE_SPEED_AUTO is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_FSDEV_ST is not set +# CONFIG_RT_CHERRYUSB_DEVICE_FSDEV_NATION is not set +# CONFIG_RT_CHERRYUSB_DEVICE_FSDEV_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_ST is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_ESP is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_KENDRYTE is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_INFINEON is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_AT is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_HC is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_NATION is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_GD is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MUSB_ES is not set +CONFIG_RT_CHERRYUSB_DEVICE_MUSB_SUNXI=y +# CONFIG_RT_CHERRYUSB_DEVICE_MUSB_BK is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MUSB_SIFLI is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MUSB_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CHIPIDEA_MCX is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CHIPIDEA_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_KINETIS_MCX is not set +# CONFIG_RT_CHERRYUSB_DEVICE_KINETIS_MM32 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_KINETIS_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_BL is not set +# CONFIG_RT_CHERRYUSB_DEVICE_HPM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_AIC is not set +# CONFIG_RT_CHERRYUSB_DEVICE_RP2040 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CH32 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_PUSB2 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_NRF5X is not set +CONFIG_RT_CHERRYUSB_DEVICE_CDC_ACM=y +# CONFIG_RT_CHERRYUSB_DEVICE_HID is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MSC is not set +# CONFIG_RT_CHERRYUSB_DEVICE_AUDIO is not set +# CONFIG_RT_CHERRYUSB_DEVICE_VIDEO is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CDC_RNDIS is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CDC_ECM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CDC_NCM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MTP is not set +# CONFIG_RT_CHERRYUSB_DEVICE_ADB is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DFU is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DISPLAY is not set +CONFIG_RT_CHERRYUSB_DEVICE_CDC_ACM_CHARDEV=y +# CONFIG_RT_CHERRYUSB_DEVICE_MSC_BLKDEV is not set +CONFIG_CONFIG_USBDEV_REQUEST_BUFFER_LEN=512 +CONFIG_CONFIG_USBDEV_MSC_MAX_BUFSIZE=512 +# CONFIG_CONFIG_USBDEV_RNDIS_USING_LWIP is not set +# CONFIG_CONFIG_USBDEV_CDC_ECM_USING_LWIP is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_NONE is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV1 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2_CDC is not set +CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_CHARDEV=y +# CONFIG_RT_CHERRYUSB_HOST is not set +CONFIG_RT_USB_SUNXI_MUSB=y + +# +# SoC (System on Chip) Drivers +# +CONFIG_RT_SOC_SUNXI_G2D=y +CONFIG_RT_SOC_SUNXI_JPEG_DEC=y +CONFIG_RT_SOC_SUNXI_JPEG_ENC=y +CONFIG_RT_SOC_SUNXI_PNG_DEC=y +# end of SoC (System on Chip) Drivers +# end of Device Drivers + +# +# C/C++ and POSIX layer +# + +# +# ISO-ANSI C layer +# + +# +# Timezone and Daylight Saving Time +# +# CONFIG_RT_LIBC_USING_FULL_TZ_DST is not set +CONFIG_RT_LIBC_USING_LIGHT_TZ_DST=y +CONFIG_RT_LIBC_TZ_DEFAULT_HOUR=8 +CONFIG_RT_LIBC_TZ_DEFAULT_MIN=0 +CONFIG_RT_LIBC_TZ_DEFAULT_SEC=0 +# end of Timezone and Daylight Saving Time +# end of ISO-ANSI C layer + +# +# POSIX (Portable Operating System Interface) layer +# +# CONFIG_RT_USING_POSIX_FS is not set +# CONFIG_RT_USING_POSIX_DELAY is not set +# CONFIG_RT_USING_POSIX_CLOCK is not set +# CONFIG_RT_USING_POSIX_TIMER is not set +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +# CONFIG_RT_USING_POSIX_PIPE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# end of Interprocess Communication (IPC) +# end of POSIX (Portable Operating System Interface) layer + +# CONFIG_RT_USING_CPLUSPLUS is not set +# end of C/C++ and POSIX layer + +# +# Network +# +# CONFIG_RT_USING_SAL is not set +# CONFIG_RT_USING_NETDEV is not set +# CONFIG_RT_USING_LWIP is not set +# CONFIG_RT_USING_AT is not set +# end of Network + +# +# Memory protection +# +# CONFIG_RT_USING_MEM_PROTECTION is not set +# CONFIG_RT_USING_HW_STACK_GUARD is not set +# end of Memory protection + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +# CONFIG_RT_USING_UTEST is not set +# CONFIG_RT_USING_VAR_EXPORT is not set +# CONFIG_RT_USING_RESOURCE_ID is not set +CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y +CONFIG_RT_USING_ADT_BITMAP=y +CONFIG_RT_USING_ADT_HASHMAP=y +CONFIG_RT_USING_ADT_REF=y +# CONFIG_RT_USING_RT_LINK is not set +# end of Utilities + +# +# Using USB legacy version +# +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set +# end of Using USB legacy version + +# CONFIG_RT_USING_FDT is not set +# CONFIG_RT_USING_RUST is not set +# end of RT-Thread Components + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set +# end of RT-Thread Utestcases + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LORAWAN_DRIVER is not set +# CONFIG_PKG_USING_PAHOMQTT is not set +# CONFIG_PKG_USING_UMQTT is not set +# CONFIG_PKG_USING_WEBCLIENT is not set +# CONFIG_PKG_USING_WEBNET is not set +# CONFIG_PKG_USING_MONGOOSE is not set +# CONFIG_PKG_USING_MYMQTT is not set +# CONFIG_PKG_USING_KAWAII_MQTT is not set +# CONFIG_PKG_USING_BC28_MQTT is not set +# CONFIG_PKG_USING_WEBTERMINAL is not set +# CONFIG_PKG_USING_FREEMODBUS is not set +# CONFIG_PKG_USING_NANOPB is not set +# CONFIG_PKG_USING_WIFI_HOST_DRIVER is not set +# CONFIG_PKG_USING_ESP_HOSTED is not set + +# +# Wi-Fi +# + +# +# Marvell WiFi +# +# CONFIG_PKG_USING_WLANMARVELL is not set +# end of Marvell WiFi + +# +# Wiced WiFi +# +# CONFIG_PKG_USING_WLAN_WICED is not set +# end of Wiced WiFi + +# CONFIG_PKG_USING_RW007 is not set + +# +# CYW43012 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43012 is not set +# end of CYW43012 WiFi + +# +# BL808 WiFi +# +# CONFIG_PKG_USING_WLAN_BL808 is not set +# end of BL808 WiFi + +# +# CYW43439 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43439 is not set +# end of CYW43439 WiFi +# end of Wi-Fi + +# CONFIG_PKG_USING_COAP is not set +# CONFIG_PKG_USING_NOPOLL is not set +# CONFIG_PKG_USING_NETUTILS is not set +# CONFIG_PKG_USING_CMUX is not set +# CONFIG_PKG_USING_PPP_DEVICE is not set +# CONFIG_PKG_USING_AT_DEVICE is not set +# CONFIG_PKG_USING_ATSRV_SOCKET is not set +# CONFIG_PKG_USING_WIZNET is not set +# CONFIG_PKG_USING_ZB_COORDINATOR is not set + +# +# IoT Cloud +# +# CONFIG_PKG_USING_ONENET is not set +# CONFIG_PKG_USING_GAGENT_CLOUD is not set +# CONFIG_PKG_USING_ALI_IOTKIT is not set +# CONFIG_PKG_USING_AZURE is not set +# CONFIG_PKG_USING_TENCENT_IOT_EXPLORER is not set +# CONFIG_PKG_USING_JIOT-C-SDK is not set +# CONFIG_PKG_USING_UCLOUD_IOT_SDK is not set +# CONFIG_PKG_USING_JOYLINK is not set +# CONFIG_PKG_USING_IOTSHARP_SDK is not set +# end of IoT Cloud + +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER is not set +# CONFIG_PKG_USING_OTA_DOWNLOADER is not set +# CONFIG_PKG_USING_IPMSG is not set +# CONFIG_PKG_USING_LSSDP is not set +# CONFIG_PKG_USING_AIRKISS_OPEN is not set +# CONFIG_PKG_USING_LIBRWS is not set +# CONFIG_PKG_USING_TCPSERVER is not set +# CONFIG_PKG_USING_PROTOBUF_C is not set +# CONFIG_PKG_USING_DLT645 is not set +# CONFIG_PKG_USING_QXWZ is not set +# CONFIG_PKG_USING_SMTP_CLIENT is not set +# CONFIG_PKG_USING_ABUP_FOTA is not set +# CONFIG_PKG_USING_LIBCURL2RTT is not set +# CONFIG_PKG_USING_CAPNP is not set +# CONFIG_PKG_USING_ERPC is not set +# CONFIG_PKG_USING_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB is not set +# CONFIG_PKG_USING_PDULIB is not set +# CONFIG_PKG_USING_BTSTACK is not set +# CONFIG_PKG_USING_BT_CYW43012 is not set +# CONFIG_PKG_USING_CYW43XX is not set +# CONFIG_PKG_USING_LORAWAN_ED_STACK is not set +# CONFIG_PKG_USING_WAYZ_IOTKIT is not set +# CONFIG_PKG_USING_MAVLINK is not set +# CONFIG_PKG_USING_BSAL is not set +# CONFIG_PKG_USING_AGILE_MODBUS is not set +# CONFIG_PKG_USING_AGILE_FTP is not set +# CONFIG_PKG_USING_EMBEDDEDPROTO is not set +# CONFIG_PKG_USING_RT_LINK_HW is not set +# CONFIG_PKG_USING_RYANMQTT is not set +# CONFIG_PKG_USING_RYANW5500 is not set +# CONFIG_PKG_USING_LORA_PKT_FWD is not set +# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set +# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set +# CONFIG_PKG_USING_HM is not set +# CONFIG_PKG_USING_SMALL_MODBUS is not set +# CONFIG_PKG_USING_NET_SERVER is not set +# CONFIG_PKG_USING_ZFTP is not set +# CONFIG_PKG_USING_WOL is not set +# CONFIG_PKG_USING_ZEPHYR_POLLING is not set +# CONFIG_PKG_USING_MATTER_ADAPTATION_LAYER is not set +# CONFIG_PKG_USING_LHC_MODBUS is not set +# CONFIG_PKG_USING_QMODBUS is not set +# CONFIG_PKG_USING_PNET is not set +# CONFIG_PKG_USING_OPENER is not set +# CONFIG_PKG_USING_FREEMQTT is not set +# end of IoT - internet of things + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_LIBSODIUM is not set +# CONFIG_PKG_USING_LIBHYDROGEN is not set +# CONFIG_PKG_USING_TINYCRYPT is not set +# CONFIG_PKG_USING_TFM is not set +# CONFIG_PKG_USING_YD_CRYPTO is not set +# end of security packages + +# +# language packages +# + +# +# JSON: JavaScript Object Notation, a lightweight data-interchange format +# +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set +# CONFIG_PKG_USING_PARSON is not set +# CONFIG_PKG_USING_RYAN_JSON is not set +# end of JSON: JavaScript Object Notation, a lightweight data-interchange format + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# end of XML: Extensible Markup Language + +# CONFIG_PKG_USING_LUATOS_SOC is not set +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set +# CONFIG_PKG_USING_PIKASCRIPT is not set +# CONFIG_PKG_USING_RTT_RUST is not set +# end of language packages + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set +# end of LVGL: powerful and easy-to-use embedded GUI library + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# end of u8g2: a monochrome graphic library + +# CONFIG_PKG_USING_NES_SIMULATOR is not set +# CONFIG_PKG_USING_OPENMV is not set +# CONFIG_PKG_USING_MUPDF is not set +# CONFIG_PKG_USING_STEMWIN is not set +# CONFIG_PKG_USING_WAVPLAYER is not set +# CONFIG_PKG_USING_TJPGD is not set +# CONFIG_PKG_USING_PDFGEN is not set +# CONFIG_PKG_USING_HELIX is not set +# CONFIG_PKG_USING_AZUREGUIX is not set +# CONFIG_PKG_USING_TOUCHGFX2RTT is not set +# CONFIG_PKG_USING_NUEMWIN is not set +# CONFIG_PKG_USING_MP3PLAYER is not set +# CONFIG_PKG_USING_TINYJPEG is not set +# CONFIG_PKG_USING_UGUI is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_TERMBOX is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_GUIENGINE is not set +# CONFIG_PKG_USING_3GPP_AMRNB is not set +# end of multimedia packages + +# +# tools packages +# +# CONFIG_PKG_USING_VECTOR is not set +# CONFIG_PKG_USING_SORCH is not set +# CONFIG_PKG_USING_DICT is not set +# CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_MCOREDUMP is not set +# CONFIG_PKG_USING_EASYFLASH is not set +# CONFIG_PKG_USING_EASYLOGGER is not set +# CONFIG_PKG_USING_SYSTEMVIEW is not set +# CONFIG_PKG_USING_SEGGER_RTT is not set +# CONFIG_PKG_USING_RTT_AUTO_EXE_CMD is not set +# CONFIG_PKG_USING_RDB is not set +# CONFIG_PKG_USING_ULOG_EASYFLASH is not set +# CONFIG_PKG_USING_LOGMGR is not set +# CONFIG_PKG_USING_ADBD is not set +# CONFIG_PKG_USING_COREMARK is not set +# CONFIG_PKG_USING_DHRYSTONE is not set +# CONFIG_PKG_USING_MEMORYPERF is not set +# CONFIG_PKG_USING_NR_MICRO_SHELL is not set +# CONFIG_PKG_USING_CHINESE_FONT_LIBRARY is not set +# CONFIG_PKG_USING_LUNAR_CALENDAR is not set +# CONFIG_PKG_USING_BS8116A is not set +# CONFIG_PKG_USING_GPS_RMC is not set +# CONFIG_PKG_USING_URLENCODE is not set +# CONFIG_PKG_USING_UMCN is not set +# CONFIG_PKG_USING_LWRB2RTT is not set +# CONFIG_PKG_USING_CPU_USAGE is not set +# CONFIG_PKG_USING_GBK2UTF8 is not set +# CONFIG_PKG_USING_VCONSOLE is not set +# CONFIG_PKG_USING_KDB is not set +# CONFIG_PKG_USING_WAMR is not set +# CONFIG_PKG_USING_MICRO_XRCE_DDS_CLIENT is not set +# CONFIG_PKG_USING_LWLOG is not set +# CONFIG_PKG_USING_ANV_TRACE is not set +# CONFIG_PKG_USING_ANV_MEMLEAK is not set +# CONFIG_PKG_USING_ANV_TESTSUIT is not set +# CONFIG_PKG_USING_ANV_BENCH is not set +# CONFIG_PKG_USING_DEVMEM is not set +# CONFIG_PKG_USING_REGEX is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set +# CONFIG_PKG_USING_SOLAR_TERMS is not set +# CONFIG_PKG_USING_GAN_ZHI is not set +# CONFIG_PKG_USING_FDT is not set +# CONFIG_PKG_USING_CBOX is not set +# CONFIG_PKG_USING_SNOWFLAKE is not set +# CONFIG_PKG_USING_HASH_MATCH is not set +# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set +# CONFIG_PKG_USING_VOFA_PLUS is not set +# CONFIG_PKG_USING_ZDEBUG is not set +# CONFIG_PKG_USING_RVBACKTRACE is not set +# CONFIG_PKG_USING_HPATCHLITE is not set +# CONFIG_PKG_USING_THREAD_METRIC is not set +# CONFIG_PKG_USING_UORB is not set +# CONFIG_PKG_USING_RT_TUNNEL is not set +# CONFIG_PKG_USING_VIRTUAL_TERMINAL is not set +# CONFIG_PKG_USING_FEE is not set +# end of tools packages + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# end of enhanced kernel services + +# CONFIG_PKG_USING_AUNITY is not set + +# +# acceleration: Assembly language or algorithmic acceleration packages +# +# CONFIG_PKG_USING_QFPLIB_M0_FULL is not set +# CONFIG_PKG_USING_QFPLIB_M0_TINY is not set +# CONFIG_PKG_USING_QFPLIB_M3 is not set +# end of acceleration: Assembly language or algorithmic acceleration packages + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_CORE is not set +# CONFIG_PKG_USING_CMSIS_NN is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 is not set +# end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard + +# +# Micrium: Micrium software products porting for RT-Thread +# +# CONFIG_PKG_USING_UCOSIII_WRAPPER is not set +# CONFIG_PKG_USING_UCOSII_WRAPPER is not set +# CONFIG_PKG_USING_UC_CRC is not set +# CONFIG_PKG_USING_UC_CLK is not set +# CONFIG_PKG_USING_UC_COMMON is not set +# CONFIG_PKG_USING_UC_MODBUS is not set +# end of Micrium: Micrium software products porting for RT-Thread + +# CONFIG_PKG_USING_FREERTOS_WRAPPER is not set +# CONFIG_PKG_USING_LITEOS_SDK is not set +# CONFIG_PKG_USING_TZ_DATABASE is not set +# CONFIG_PKG_USING_CAIRO is not set +# CONFIG_PKG_USING_PIXMAN is not set +# CONFIG_PKG_USING_PARTITION is not set +# CONFIG_PKG_USING_PERF_COUNTER is not set +# CONFIG_PKG_USING_FILEX is not set +# CONFIG_PKG_USING_LEVELX is not set +# CONFIG_PKG_USING_FLASHDB is not set +# CONFIG_PKG_USING_SQLITE is not set +# CONFIG_PKG_USING_RTI is not set +# CONFIG_PKG_USING_DFS_YAFFS is not set +# CONFIG_PKG_USING_LITTLEFS is not set +# CONFIG_PKG_USING_DFS_JFFS2 is not set +# CONFIG_PKG_USING_DFS_UFFS is not set +# CONFIG_PKG_USING_LWEXT4 is not set +# CONFIG_PKG_USING_THREAD_POOL is not set +# CONFIG_PKG_USING_ROBOTS is not set +# CONFIG_PKG_USING_EV is not set +# CONFIG_PKG_USING_SYSWATCH is not set +# CONFIG_PKG_USING_SYS_LOAD_MONITOR is not set +# CONFIG_PKG_USING_PLCCORE is not set +# CONFIG_PKG_USING_RAMDISK is not set +# CONFIG_PKG_USING_MININI is not set +# CONFIG_PKG_USING_QBOOT is not set +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP is not set +# CONFIG_PKG_USING_RPMSG_LITE is not set +# CONFIG_PKG_USING_LPM is not set +# CONFIG_PKG_USING_TLSF is not set +# CONFIG_PKG_USING_EVENT_RECORDER is not set +# CONFIG_PKG_USING_ARM_2D is not set +# CONFIG_PKG_USING_MCUBOOT is not set +# CONFIG_PKG_USING_TINYUSB is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_SPEED_FS is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_SPEED_HS is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_SPEED_AUTO is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_FSDEV_ST is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_FSDEV_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_ST is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_ESP is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_KENDRYTE is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_INFINEON is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_AT is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_HC is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_NATION is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_GD is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_ES is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_SUNXI is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_BK is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_SIFLI is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_CHIPIDEA_MCX is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_CHIPIDEA_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_KINETIS_MCX is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_KINETIS_MM32 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_KINETIS_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_BL is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_HPM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_AIC is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_RP2040 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_RENESAS is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_CH32 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_PUSB2 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_TEMPLATE_NONE is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV1 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2 is not set +# CONFIG_PKG_USING_KMULTI_RTIMER is not set +# CONFIG_PKG_USING_TFDB is not set +# CONFIG_PKG_USING_QPC is not set +# CONFIG_PKG_USING_AGILE_UPGRADE is not set +# CONFIG_PKG_USING_FLASH_BLOB is not set +# CONFIG_PKG_USING_MLIBC is not set +# CONFIG_PKG_USING_TASK_MSG_BUS is not set +# CONFIG_PKG_USING_UART_FRAMEWORK is not set +# CONFIG_PKG_USING_SFDB is not set +# CONFIG_PKG_USING_RTP is not set +# CONFIG_PKG_USING_REB is not set +# CONFIG_PKG_USING_RMP is not set +# CONFIG_PKG_USING_R_RHEALSTONE is not set +# CONFIG_PKG_USING_HEARTBEAT is not set +# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set +# CONFIG_PKG_USING_CHERRYECAT is not set +# CONFIG_PKG_USING_EVENT_LOOP is not set +# CONFIG_PKG_USING_THREAD_MANAGER is not set +# end of system packages + +# +# peripheral libraries and drivers +# + +# +# HAL & SDK Drivers +# + +# +# STM32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_STM32F0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB55_SDK is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_STM32WL_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WL_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_CMSIS_DRIVER is not set +# end of STM32 HAL & SDK Drivers + +# +# Infineon HAL Packages +# +# CONFIG_PKG_USING_INFINEON_CAT1CM0P is not set +# CONFIG_PKG_USING_INFINEON_CMSIS is not set +# CONFIG_PKG_USING_INFINEON_CORE_LIB is not set +# CONFIG_PKG_USING_INFINEON_MTB_HAL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_MTB_PDL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_RETARGET_IO is not set +# CONFIG_PKG_USING_INFINEON_CAPSENSE is not set +# CONFIG_PKG_USING_INFINEON_CSDIDAC is not set +# CONFIG_PKG_USING_INFINEON_SERIAL_FLASH is not set +# CONFIG_PKG_USING_INFINEON_USBDEV is not set +# end of Infineon HAL Packages + +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_EMBARC_BSP is not set +# CONFIG_PKG_USING_ESP_IDF is not set + +# +# Kendryte SDK +# +# CONFIG_PKG_USING_K210_SDK is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# end of Kendryte SDK + +# CONFIG_PKG_USING_NRF5X_SDK is not set +# CONFIG_PKG_USING_NRFX is not set +# CONFIG_PKG_USING_NUCLEI_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_RP2350_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set + +# +# MM32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_MM32F103XX_CMSIS is not set +# CONFIG_PKG_USING_MM32F3270X_CMSIS is not set +# CONFIG_PKG_USING_MM32F5260X_CMSIS is not set +# CONFIG_PKG_USING_MM32L0XX_CMSIS is not set +# CONFIG_PKG_USING_MM32L3XX_CMSIS is not set +# CONFIG_PKG_USING_MM32F103XX_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32F3270X_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32F5260X_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32L0XX_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32L3XX_HAL_DRIVER is not set +# end of MM32 HAL & SDK Drivers + +# +# WCH HAL & SDK Drivers +# +# CONFIG_PKG_USING_CH32V20x_SDK is not set +# CONFIG_PKG_USING_CH32V307_SDK is not set +# end of WCH HAL & SDK Drivers + +# +# AT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_AT32A403A_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A403A_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_CMSIS_DRIVER is not set +# end of AT32 HAL & SDK Drivers + +# +# HC32 DDL Drivers +# +# CONFIG_PKG_USING_HC32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F3_SERIES_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_SERIES_DRIVER is not set +# end of HC32 DDL Drivers + +# +# NXP HAL & SDK Drivers +# +# CONFIG_PKG_USING_NXP_MCX_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NXP_MCX_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC55S_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6SX_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6UL_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMXRT_DRIVER is not set +# end of NXP HAL & SDK Drivers + +# +# NUVOTON Drivers +# +# CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set +# end of NUVOTON Drivers + +# +# GD32 Drivers +# +# CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set +# CONFIG_PKG_USING_GD32_RISCV_SERIES_DRIVER is not set +# CONFIG_PKG_USING_GD32VW55X_WIFI is not set +# end of GD32 Drivers + +# +# HPMicro SDK +# +# CONFIG_PKG_USING_HPM_SDK is not set +# end of HPMicro SDK + +# +# FT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_FT32F0_STD_DRIVER is not set +# CONFIG_PKG_USING_FT32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_FT32F4_STD_DRIVER is not set +# CONFIG_PKG_USING_FT32F4_CMSIS_DRIVER is not set +# end of FT32 HAL & SDK Drivers + +# +# NOVOSNS Drivers +# +# CONFIG_PKG_USING_NOVOSNS_SERIES_DRIVER is not set +# end of NOVOSNS Drivers + +# +# N32 Drivers +# +# CONFIG_PKG_USING_N32H7XX_STD_DRIVER is not set +# CONFIG_PKG_USING_N32H7XX_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_N32H49X_STD_DRIVER is not set +# CONFIG_PKG_USING_N32H49X_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_N32H47X_48X_STD_DRIVER is not set +# CONFIG_PKG_USING_N32H47X_48X_CMSIS_DRIVER is not set +# end of N32 Drivers +# end of HAL & SDK Drivers + +# +# sensors drivers +# +# CONFIG_PKG_USING_LSM6DSM is not set +# CONFIG_PKG_USING_LSM6DSL is not set +# CONFIG_PKG_USING_LPS22HB is not set +# CONFIG_PKG_USING_HTS221 is not set +# CONFIG_PKG_USING_LSM303AGR is not set +# CONFIG_PKG_USING_BME280 is not set +# CONFIG_PKG_USING_BME680 is not set +# CONFIG_PKG_USING_BMA400 is not set +# CONFIG_PKG_USING_BMI160_BMX160 is not set +# CONFIG_PKG_USING_SPL0601 is not set +# CONFIG_PKG_USING_MS5805 is not set +# CONFIG_PKG_USING_DA270 is not set +# CONFIG_PKG_USING_DF220 is not set +# CONFIG_PKG_USING_HSHCAL001 is not set +# CONFIG_PKG_USING_BH1750 is not set +# CONFIG_PKG_USING_MPU6XXX is not set +# CONFIG_PKG_USING_AHT10 is not set +# CONFIG_PKG_USING_AP3216C is not set +# CONFIG_PKG_USING_TSL4531 is not set +# CONFIG_PKG_USING_DS18B20 is not set +# CONFIG_PKG_USING_DHT11 is not set +# CONFIG_PKG_USING_DHTXX is not set +# CONFIG_PKG_USING_GY271 is not set +# CONFIG_PKG_USING_GP2Y10 is not set +# CONFIG_PKG_USING_SGP30 is not set +# CONFIG_PKG_USING_HDC1000 is not set +# CONFIG_PKG_USING_BMP180 is not set +# CONFIG_PKG_USING_BMP280 is not set +# CONFIG_PKG_USING_SHTC1 is not set +# CONFIG_PKG_USING_BMI088 is not set +# CONFIG_PKG_USING_HMC5883 is not set +# CONFIG_PKG_USING_MAX6675 is not set +# CONFIG_PKG_USING_MAX31855 is not set +# CONFIG_PKG_USING_TMP1075 is not set +# CONFIG_PKG_USING_SR04 is not set +# CONFIG_PKG_USING_CCS811 is not set +# CONFIG_PKG_USING_PMSXX is not set +# CONFIG_PKG_USING_RT3020 is not set +# CONFIG_PKG_USING_MLX90632 is not set +# CONFIG_PKG_USING_MLX90382 is not set +# CONFIG_PKG_USING_MLX90384 is not set +# CONFIG_PKG_USING_MLX90393 is not set +# CONFIG_PKG_USING_MLX90392 is not set +# CONFIG_PKG_USING_MLX90394 is not set +# CONFIG_PKG_USING_MLX90396 is not set +# CONFIG_PKG_USING_MLX90397 is not set +# CONFIG_PKG_USING_MS5611 is not set +# CONFIG_PKG_USING_MAX31865 is not set +# CONFIG_PKG_USING_VL53L0X is not set +# CONFIG_PKG_USING_INA260 is not set +# CONFIG_PKG_USING_MAX30102 is not set +# CONFIG_PKG_USING_INA226 is not set +# CONFIG_PKG_USING_LIS2DH12 is not set +# CONFIG_PKG_USING_HS300X is not set +# CONFIG_PKG_USING_ZMOD4410 is not set +# CONFIG_PKG_USING_ISL29035 is not set +# CONFIG_PKG_USING_MMC3680KJ is not set +# CONFIG_PKG_USING_QMP6989 is not set +# CONFIG_PKG_USING_BALANCE is not set +# CONFIG_PKG_USING_SHT2X is not set +# CONFIG_PKG_USING_SHT3X is not set +# CONFIG_PKG_USING_SHT4X is not set +# CONFIG_PKG_USING_AD7746 is not set +# CONFIG_PKG_USING_ADT74XX is not set +# CONFIG_PKG_USING_MAX17048 is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_CW2015 is not set +# CONFIG_PKG_USING_ICM20608 is not set +# CONFIG_PKG_USING_PAJ7620 is not set +# CONFIG_PKG_USING_STHS34PF80 is not set +# CONFIG_PKG_USING_P3T1755 is not set +# CONFIG_PKG_USING_QMI8658 is not set +# CONFIG_PKG_USING_ICM20948 is not set +# CONFIG_PKG_USING_SCD4X is not set +# end of sensors drivers + +# +# touch drivers +# +# CONFIG_PKG_USING_GT9147 is not set +# CONFIG_PKG_USING_GT1151 is not set +# CONFIG_PKG_USING_GT917S is not set +# CONFIG_PKG_USING_GT911 is not set +# CONFIG_PKG_USING_FT6206 is not set +# CONFIG_PKG_USING_FT5426 is not set +# CONFIG_PKG_USING_FT6236 is not set +# CONFIG_PKG_USING_XPT2046_TOUCH is not set +# CONFIG_PKG_USING_CST816X is not set +# CONFIG_PKG_USING_CST812T is not set +# end of touch drivers + +# CONFIG_PKG_USING_LCD_SPI_DRIVER is not set +# CONFIG_PKG_USING_REALTEK_AMEBA is not set +# CONFIG_PKG_USING_BUTTON is not set +# CONFIG_PKG_USING_PCF8574 is not set +# CONFIG_PKG_USING_SX12XX is not set +# CONFIG_PKG_USING_SIGNAL_LED is not set +# CONFIG_PKG_USING_LEDBLINK is not set +# CONFIG_PKG_USING_LITTLED is not set +# CONFIG_PKG_USING_LKDGUI is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_MULTI_INFRARED is not set +# CONFIG_PKG_USING_AGILE_BUTTON is not set +# CONFIG_PKG_USING_AGILE_LED is not set +# CONFIG_PKG_USING_AT24CXX is not set +# CONFIG_PKG_USING_MOTIONDRIVER2RTT is not set +# CONFIG_PKG_USING_PCA9685 is not set +# CONFIG_PKG_USING_ILI9341 is not set +# CONFIG_PKG_USING_I2C_TOOLS is not set +# CONFIG_PKG_USING_NRF24L01 is not set +# CONFIG_PKG_USING_RPLIDAR is not set +# CONFIG_PKG_USING_AS608 is not set +# CONFIG_PKG_USING_RC522 is not set +# CONFIG_PKG_USING_WS2812B is not set +# CONFIG_PKG_USING_EXTERN_RTC_DRIVERS is not set +# CONFIG_PKG_USING_MULTI_RTIMER is not set +# CONFIG_PKG_USING_MAX7219 is not set +# CONFIG_PKG_USING_BEEP is not set +# CONFIG_PKG_USING_EASYBLINK is not set +# CONFIG_PKG_USING_PMS_SERIES is not set +# CONFIG_PKG_USING_CAN_YMODEM is not set +# CONFIG_PKG_USING_LORA_RADIO_DRIVER is not set +# CONFIG_PKG_USING_QLED is not set +# CONFIG_PKG_USING_AGILE_CONSOLE is not set +# CONFIG_PKG_USING_LD3320 is not set +# CONFIG_PKG_USING_WK2124 is not set +# CONFIG_PKG_USING_LY68L6400 is not set +# CONFIG_PKG_USING_DM9051 is not set +# CONFIG_PKG_USING_CH390 is not set +# CONFIG_PKG_USING_SSD1306 is not set +# CONFIG_PKG_USING_QKEY is not set +# CONFIG_PKG_USING_RS485 is not set +# CONFIG_PKG_USING_RS232 is not set +# CONFIG_PKG_USING_NES is not set +# CONFIG_PKG_USING_VIRTUAL_SENSOR is not set +# CONFIG_PKG_USING_VDEVICE is not set +# CONFIG_PKG_USING_SGM706 is not set +# CONFIG_PKG_USING_RDA58XX is not set +# CONFIG_PKG_USING_LIBNFC is not set +# CONFIG_PKG_USING_MFOC is not set +# CONFIG_PKG_USING_TMC51XX is not set +# CONFIG_PKG_USING_TCA9534 is not set +# CONFIG_PKG_USING_KOBUKI is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_MICRO_ROS is not set +# CONFIG_PKG_USING_MCP23008 is not set +# CONFIG_PKG_USING_MISAKA_AT24CXX is not set +# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set +# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set +# CONFIG_PKG_USING_SOFT_SERIAL is not set +# CONFIG_PKG_USING_MB85RS16 is not set +# CONFIG_PKG_USING_RFM300 is not set +# CONFIG_PKG_USING_IO_INPUT_FILTER is not set +# CONFIG_PKG_USING_LRF_NV7LIDAR is not set +# CONFIG_PKG_USING_AIP650 is not set +# CONFIG_PKG_USING_FINGERPRINT is not set +# CONFIG_PKG_USING_BT_ECB02C is not set +# CONFIG_PKG_USING_UAT is not set +# CONFIG_PKG_USING_ST7789 is not set +# CONFIG_PKG_USING_VS1003 is not set +# CONFIG_PKG_USING_X9555 is not set +# CONFIG_PKG_USING_SYSTEM_RUN_LED is not set +# CONFIG_PKG_USING_BT_MX01 is not set +# CONFIG_PKG_USING_RGPOWER is not set +# CONFIG_PKG_USING_BT_MX02 is not set +# CONFIG_PKG_USING_GC9A01 is not set +# CONFIG_PKG_USING_IK485 is not set +# CONFIG_PKG_USING_SERVO is not set +# CONFIG_PKG_USING_SEAN_WS2812B is not set +# CONFIG_PKG_USING_IC74HC165 is not set +# CONFIG_PKG_USING_IST8310 is not set +# CONFIG_PKG_USING_ST7789_SPI is not set +# CONFIG_PKG_USING_CAN_UDS is not set +# CONFIG_PKG_USING_ISOTP_C is not set +# CONFIG_PKG_USING_IKUNLED is not set +# CONFIG_PKG_USING_INS5T8025 is not set +# CONFIG_PKG_USING_IRUART is not set +# CONFIG_PKG_USING_ST7305 is not set +# CONFIG_PKG_USING_TM1668 is not set +# CONFIG_PKG_USING_TCA9548A is not set +# CONFIG_PKG_USING_SPI_TOOLS is not set +# end of peripheral libraries and drivers + +# +# AI packages +# +# CONFIG_PKG_USING_LIBANN is not set +# CONFIG_PKG_USING_NNOM is not set +# CONFIG_PKG_USING_ONNX_BACKEND is not set +# CONFIG_PKG_USING_ONNX_PARSER is not set +# CONFIG_PKG_USING_TENSORFLOWLITEMICRO is not set +# CONFIG_PKG_USING_ELAPACK is not set +# CONFIG_PKG_USING_ULAPACK is not set +# CONFIG_PKG_USING_QUEST is not set +# CONFIG_PKG_USING_NAXOS is not set +# CONFIG_PKG_USING_R_TINYMAIX is not set +# CONFIG_PKG_USING_LLMCHAT is not set +# CONFIG_PKG_USING_AIFES is not set +# CONFIG_PKG_USING_AGENT is not set +# end of AI packages + +# +# Signal Processing and Control Algorithm Packages +# +# CONFIG_PKG_USING_APID is not set +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_QPID is not set +# CONFIG_PKG_USING_UKAL is not set +# CONFIG_PKG_USING_DIGITALCTRL is not set +# CONFIG_PKG_USING_KISSFFT is not set +# end of Signal Processing and Control Algorithm Packages + +# +# miscellaneous packages +# + +# +# project laboratory +# +# end of project laboratory + +# +# samples: kernel and components samples +# +# CONFIG_PKG_USING_KERNEL_SAMPLES is not set +# CONFIG_PKG_USING_FILESYSTEM_SAMPLES is not set +# CONFIG_PKG_USING_NETWORK_SAMPLES is not set +# CONFIG_PKG_USING_PERIPHERAL_SAMPLES is not set +# end of samples: kernel and components samples + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_CMATRIX is not set +# CONFIG_PKG_USING_SL is not set +# CONFIG_PKG_USING_CAL is not set +# CONFIG_PKG_USING_ACLOCK is not set +# CONFIG_PKG_USING_THREES is not set +# CONFIG_PKG_USING_2048 is not set +# CONFIG_PKG_USING_SNAKE is not set +# CONFIG_PKG_USING_TETRIS is not set +# CONFIG_PKG_USING_DONUT is not set +# CONFIG_PKG_USING_COWSAY is not set +# CONFIG_PKG_USING_MORSE is not set +# end of entertainment: terminal games and other interesting software packages + +# CONFIG_PKG_USING_LIBCSV is not set +# CONFIG_PKG_USING_OPTPARSE is not set +# CONFIG_PKG_USING_FASTLZ is not set +# CONFIG_PKG_USING_MINILZO is not set +# CONFIG_PKG_USING_QUICKLZ is not set +# CONFIG_PKG_USING_LZMA is not set +# CONFIG_PKG_USING_RALARAM is not set +# CONFIG_PKG_USING_MULTIBUTTON is not set +# CONFIG_PKG_USING_FLEXIBLE_BUTTON is not set +# CONFIG_PKG_USING_CANFESTIVAL is not set +# CONFIG_PKG_USING_ZLIB is not set +# CONFIG_PKG_USING_MINIZIP is not set +# CONFIG_PKG_USING_HEATSHRINK is not set +# CONFIG_PKG_USING_DSTR is not set +# CONFIG_PKG_USING_TINYFRAME is not set +# CONFIG_PKG_USING_KENDRYTE_DEMO is not set +# CONFIG_PKG_USING_UPACKER is not set +# CONFIG_PKG_USING_UPARAM is not set +# CONFIG_PKG_USING_HELLO is not set +# CONFIG_PKG_USING_VI is not set +# CONFIG_PKG_USING_KI is not set +# CONFIG_PKG_USING_ARMv7M_DWT is not set +# CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LIBCRC is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_DESIGN_PATTERN is not set +# CONFIG_PKG_USING_CONTROLLER is not set +# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set +# CONFIG_PKG_USING_MFBD is not set +# CONFIG_PKG_USING_SLCAN2RTT is not set +# CONFIG_PKG_USING_SOEM is not set +# CONFIG_PKG_USING_QPARAM is not set +# CONFIG_PKG_USING_CorevMCU_CLI is not set +# CONFIG_PKG_USING_DRMP is not set +# CONFIG_PKG_USING_AUTOGEN_PARAMETER_MANAGER is not set +# end of miscellaneous packages + +# +# Arduino libraries +# +# CONFIG_PKG_USING_RTDUINO is not set + +# +# Projects and Demos +# +# CONFIG_PKG_USING_ARDUINO_MSGQ_C_CPP_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_SKETCH_LOADER_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set +# CONFIG_PKG_USING_ARDUINO_RTDUINO_SENSORFUSION_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_NINEINONE_SENSOR_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set +# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set +# end of Projects and Demos + +# +# Sensors +# +# CONFIG_PKG_USING_ARDUINO_SENSOR_DEVICE_DRIVERS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL375 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L1X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL6180X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31855 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31865 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31856 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX6675 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90614 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS1 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AHTX0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADT7410 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME680 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9808 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4728 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA219 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR390 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DHT is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM6DS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO055 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX1704X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMC56X3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90393 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90395 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ICM20X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DPS310 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTS221 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT4X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL343 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS726X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AMG88XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2320 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2315 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR329_LTR303 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP3XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MS8607 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90640 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMA8451 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MSA301 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X_RVC is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS2MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303DLH_MAG is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LC709203F is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CAP1188 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CCS811 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_NAU7802 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS331 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS2X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS35HW is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303_ACCEL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3DH is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8591 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL3115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPR121 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPRLS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPU6050 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCT2075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PM25AQI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_EMC2101 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXAS21002C is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SCD30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXOS8700 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HMC5883_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP006 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TLA202X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCS34725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI7021 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP40 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHTC3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU21DF is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS7341 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU31D is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA260 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP007_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_L3GD20 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP117 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSC2007 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2591_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VCNL4040 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML7700 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LIS3DHTR is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DHT is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL335 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_H3LIS331DL is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MMA7660 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PAJ7620 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ITG3200 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HP20X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DRV2605L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BBM150 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HMC5883L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM303DLH is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TCS3414CS is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MP503 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HIGHTEMP is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT35 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_AT42QT1070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM6DS3 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HM3301 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LTC2941 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LDC1612 is not set +# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set +# CONFIG_PKG_USING_ARDUINO_JARZEBSKI_MPU6050 is not set +# end of Sensors + +# +# Display +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_GFX_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_U8G2 is not set +# CONFIG_PKG_USING_ARDUINO_TFT_ESPI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ST7735 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SSD1306 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ILI9341 is not set +# CONFIG_PKG_USING_SEEED_TM1637 is not set +# end of Display + +# +# Timing +# +# CONFIG_PKG_USING_ARDUINO_RTCLIB is not set +# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set +# CONFIG_PKG_USING_ARDUINO_TICKER is not set +# CONFIG_PKG_USING_ARDUINO_TASKSCHEDULER is not set +# end of Timing + +# +# Data Processing +# +# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set +# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set +# CONFIG_PKG_USING_ARDUINO_TENSORFLOW_LITE_MICRO is not set +# CONFIG_PKG_USING_ARDUINO_RUNNINGMEDIAN is not set +# end of Data Processing + +# +# Data Storage +# + +# +# Communication +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set +# end of Communication + +# +# Device Control +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TPA2016 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DRV2605 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS1841 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS3502 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PCF85063TP is not set +# end of Device Control + +# +# Other +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MFRC630 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI5351 is not set +# end of Other + +# +# Signal IO +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BUSIO is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCA8418 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP23017 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADS1X15 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AW9523 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP3008 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BD3491FS is not set +# end of Signal IO + +# +# Uncategorized +# +# end of Arduino libraries +# end of RT-Thread online packages + +CONFIG_SOC_ALLWINNER_F101=y +CONFIG_SBC_YUZUKINEKO=y + +# +# YuzukiNeko SBC Configuration +# +CONFIG_SBC_YUZUKINEKO_MMODE=y +# CONFIG_SBC_YUZUKINEKO_SMODE is not set +# CONFIG_SBC_YUZUKINEKO_SMODE_BARE is not set +CONFIG_SBC_YUZUKINEKO_ST77916=y +# end of YuzukiNeko SBC Configuration diff --git a/bsp/allwinner/f101/Kconfig b/bsp/allwinner/f101/Kconfig new file mode 100644 index 000000000000..a5c7c0f4ae46 --- /dev/null +++ b/bsp/allwinner/f101/Kconfig @@ -0,0 +1,43 @@ +mainmenu "Allwinner F101 BSP Configuration" + +BSP_DIR := . + +RTT_DIR := ../../.. + +PKGS_DIR := packages + +SOC_DM_DIR := $BSP_DIR/../dm/ + +source "$(SOC_DM_DIR)/Kconfig" + +source "$(RTT_DIR)/Kconfig" +osource "$PKGS_DIR/Kconfig" + +config SOC_ALLWINNER_F101 + bool + select ARCH_RISCV + select ARCH_RISCV32 + select ARCH_RISCV_FPU_D + select RT_USING_HW_ATOMIC + select RT_USING_CACHE + select RT_USING_RISCV_NEW_COMMON + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + default y + +choice + prompt "Allwinner F101 SBC" + default SBC_YUZUKINEKO + depends on SOC_ALLWINNER_F101 + +config SBC_YUZUKINEKO + bool "YuzukiNeko" + select RT_USING_DM + select RT_USING_OFW + select RT_USING_BUILTIN_FDT + +endchoice + +if SBC_YUZUKINEKO + source "f101-yuzukineko.Kconfig" +endif diff --git a/bsp/allwinner/f101/README.md b/bsp/allwinner/f101/README.md new file mode 100644 index 000000000000..861b74455685 --- /dev/null +++ b/bsp/allwinner/f101/README.md @@ -0,0 +1,162 @@ +# YuzukiNeko (Allwinner F101S3) + +[简体中文版](README_zh.md) + +This BSP supports YuzukiNeko, a Raspberry Pi Pico-sized single-board computer +built around the Allwinner F101S3 (Sun252i F101). Its C907 core is RV32IMAFDC +and the SoC integrates 16 MiB of LPSRAM at `0x40000000`. RT-Thread is linked to load +at `0x40010000`. + +The board uses RT-Thread Device Model (DM) drivers and the built-in device +tree in `bsp/allwinner/dm/dts`. It does not use `libraries/sunxi-hal`. + +## Board Overview + +| Item | YuzukiNeko hardware | +| --- | --- | +| SoC | Allwinner F101S3, single T-Head C907 RISC-V CPU, up to 1 GHz | +| Memory | 16 MiB integrated LPSRAM | +| Boot storage | 16 MiB SPI NOR flash (PY25Q128HA) | +| Removable storage | microSD card socket, SDC0 | +| USB and power | USB Type-C for 5 V power, USB 2.0 device/FEL access | +| Expansion | Two 20-pin headers with GPIO, ADC, 5 V, 3.3 V, and GND | +| Audio | HPOUT analog-audio pads | +| Controls | FEL button and power LED; there is no reset button | +| Form factor | Approximately 51 x 21 mm | + +UART1 is available on H2-11 (PB0/TX) and H2-10 (PB1/RX) at 115200 8N1. These +signals share the Type-C SBU routing, so a connected USB data cable can disturb +an external UART adapter. + +## Toolchain + +Use a bare-metal GNU toolchain with RV32 `imafdc` and `ilp32d` multilibs. The +following downloads the `riscv64-unknown-elf` toolchain used by RT-Thread CI. +Despite its `riscv64` prefix, it contains the required RV32 multilib. + +```sh +toolchain_url=https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz +mkdir -p "$HOME/opt/riscv64-unknown-elf" +curl -L "$toolchain_url" | tar -xz -C "$HOME/opt/riscv64-unknown-elf" --strip-components=1 +export RTT_EXEC_PATH="$HOME/opt/riscv64-unknown-elf/bin" +export RTT_CC_PREFIX=riscv64-unknown-elf- +riscv64-unknown-elf-gcc -v +``` + +Any compatible GNU toolchain can be used by setting the same two variables. +Install SCons through RT-Thread ENV or with `python -m pip install scons`. + +## Build + +```sh +cd bsp/allwinner/f101 +scons --menuconfig +scons +``` + +`YuzukiNeko boot privilege mode` defaults to direct M-mode boot. Select +S-mode only when the installed boot firmware enters RT-Thread through an SBI +implementation and passes the standard RISC-V boot arguments. + +The build creates: + +- `rtthread.elf`, for debugging and symbol lookup; +- `rtthread.bin`, the raw image loaded at `0x40010000`. + +### Display selection and validation + +`YuzukiNeko display` selects one board display configuration at a time. The +default is the 360x360 ST77916 QSPI LCD. Select `CO6300 262x928 one-lane +MIPI-DSI AMOLED` for the OSPTEK AM319M262928ZS module; it enables DE0, TCON0, +and the one-lane RGB888 MIPI-DSI output. The module's VCI supply must already +be enabled by the display adapter. Its DSI pin group occupies PD12 through +PD21, so it cannot be used together with the ST77916 SPI1 panel configuration. + +The same module has a CST3530 capacitive touchscreen at I2C address `0x58`. +The CO6300 board configuration enables it on I2C2 (`PB2`/SDA and `PB3`/SCL) +at 400 kHz and uses 20 ms polling, so only the I2C pair is required. When the +module's interrupt and reset lines are connected, add `interrupt-gpios` and +`reset-gpios` to its DTS node to use interrupt-driven reporting and hardware +reset. + +The DE framebuffer is registered as `fb0`. After boot, these commands cover +the DE scanout and G2D paths: + +```text +g2d_test +g2d_anim +``` + +`g2d_test` presents fill, BitBlt, scale, rotation, and alpha blend results on +`fb0`. `g2d_anim` exercises scaled and rotated composition continuously. Both +commands accept optional `g2d` and framebuffer device names. `jpegshow` and +`mjpegplay` can additionally validate JPEG decode and framebuffer presentation +when a compatible JPEG or MJPEG AVI file is available. + +## Download and Run + +This BSP produces a raw `rtthread.bin`; it does not replace the boot ROM, +boot0, GPT, or the board's boot configuration. The M-mode boot firmware copies +the `rtos` payload to `0x40010000` and jumps to `_start`. When S-mode is +selected, it must instead enter through SBI and pass the standard RISC-V boot +arguments. + +### SPI NOR update + +Use the current [xboot/xfel](https://github.com/xboot/xfel) for F101 FEL +access. Build it on the host, hold the board's FEL button while powering it, +then confirm that `xfel version` reports `F101`. + +```sh +git clone https://github.com/xboot/xfel.git +make -C xfel +XFEL=$PWD/xfel/xfel +$XFEL version +$XFEL spinor +``` + +Do not write `rtthread.bin` directly to SPI NOR. Preserve the current +`melis-config` TOC item and package it together with the new `rtos` item as a +`sunxi-package` `boot_package.fex` using the board's existing +`package_rtos.cfg` and Allwinner `dragonsecboot` packaging tool. Only replace +the `bootA` package after taking a backup: + +```sh +$XFEL spinor read 0xe0000 0x154000 f101-boota-backup.fex +$XFEL spinor write 0xe0000 boot_package.fex +$XFEL reset +``` + +`0xe0000` is the start of the `bootA` partition on the factory 16 MiB SPI NOR +layout. The backup is sufficient to restore the previous RTOS package with the +same `spinor write` command. Do not overwrite flash offset zero or the GPT. + +## Device Support + +| Device | Status | Notes | +| --- | --- | --- | +| ADC | Support | - | +| AUDIO | Support | - | +| UART | Support | - | +| DMA | Support | - | +| HWCrypto | Support | - | +| I2C | Support | - | +| SPI | Support | - | +| PWM | Support | - | +| Flash | Support | SPI-NOR/MTD | +| SDIO | Support | - | +| USB | Support | CDC ACM character-device | +| Watchdog | Support | - | +| IR-RX | Support | - | +| RTC | Support | - | +| Display | Support | QSPI ST77916 or MIPI-DSI CO6300; RGB/LVDS/DSI backend | +| Touch | Support | CST3530 on the CO6300 module | +| G2D | Support | - | +| Video | Support | - | + +## References + +- [YuzukiNeko hardware project](https://github.com/YuzukiHD/YuzukiNeko) +- [YuzukiNeko documentation](https://dshanpi.100ask.net/docs/F101-YuzukiNeko/) +- [OSPTEK AM319M262928ZS CO6300 panel reference](https://github.com/osptek/amoled-3.19-262x928-mipi-co6300) +- [OpenixSuit releases](https://github.com/YuzukiTsuru/OpenixSuit/releases) diff --git a/bsp/allwinner/f101/README_zh.md b/bsp/allwinner/f101/README_zh.md new file mode 100644 index 000000000000..9c3f02234375 --- /dev/null +++ b/bsp/allwinner/f101/README_zh.md @@ -0,0 +1,147 @@ +# YuzukiNeko(Allwinner F101S3) + +[English](README.md) + +本 BSP 面向 YuzukiNeko 开发板。这是一块树莓派 Pico 尺寸的单板计算机,使用 +Allwinner F101S3(Sun252i F101)SoC;C907 内核为 RV32IMAFDC。SoC 内置的 +16 MiB LPSRAM 位于 `0x40000000`,RT-Thread 镜像链接并加载到 `0x40010000`。 + +驱动均基于 RT-Thread Device Model(DM)和 +`bsp/allwinner/dm/dts` 中的内嵌设备树实现,不依赖 `libraries/sunxi-hal`。 + +## 板卡介绍 + +| 项目 | YuzukiNeko 硬件信息 | +| --- | --- | +| SoC | Allwinner F101S3,单核 T-Head C907 RISC-V,最高 1 GHz | +| 内存 | 16 MiB 内置 LPSRAM | +| 启动存储 | 16 MiB SPI NOR Flash(PY25Q128HA) | +| 可移动存储 | microSD 卡槽,SDC0 | +| USB 与供电 | USB Type-C,提供 5 V 供电、USB 2.0 device 与 FEL 访问 | +| 扩展 | 两组 20-pin 排针,提供 GPIO、ADC、5 V、3.3 V 和 GND | +| 音频 | HPOUT 模拟音频焊盘 | +| 按键与指示 | FEL 烧录按键和电源指示灯;没有复位按键 | +| 尺寸 | 约 51 x 21 mm | + +UART1 位于 H2-11(PB0/TX)和 H2-10(PB1/RX),115200 8N1。这两根信号线与 +Type-C 的 SBU 路由共用,连接 USB 数据线时可能干扰外接串口适配器。 + +## 工具链 + +需要具备 RV32 `imafdc` 和 `ilp32d` multilib 的裸机 GNU 工具链。下面下载的是 +RT-Thread CI 使用的 `riscv64-unknown-elf` 工具链。虽然前缀为 `riscv64`,其 +内容包含本 BSP 所需的 RV32 multilib。 + +```sh +toolchain_url=https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz +mkdir -p "$HOME/opt/riscv64-unknown-elf" +curl -L "$toolchain_url" | tar -xz -C "$HOME/opt/riscv64-unknown-elf" --strip-components=1 +export RTT_EXEC_PATH="$HOME/opt/riscv64-unknown-elf/bin" +export RTT_CC_PREFIX=riscv64-unknown-elf- +riscv64-unknown-elf-gcc -v +``` + +其它兼容的 GNU 工具链同样只需设置这两个环境变量。SCons 可由 RT-Thread ENV +安装,也可以使用 `python -m pip install scons` 安装。 + +## 构建 + +```sh +scons --menuconfig +scons +``` + +`YuzukiNeko boot privilege mode` 默认选择直接 M-mode 启动。只有已安装的 boot firmware 能通过 SBI 进入 RT-Thread,且传入标准 RISC-V 启动参数时,才应选择 S-mode。 + +构建产物如下: + +- `rtthread.elf`:用于调试和符号解析; +- `rtthread.bin`:加载地址为 `0x40010000` 的裸镜像。 + +### 显示屏选择与验证 + +`YuzukiNeko display` 一次只能选择一种板级显示配置。默认的 +`ST77916 360x360 QSPI LCD` 对应现有的 QSPI 屏;选择 +`CO6300 262x928 one-lane MIPI-DSI AMOLED` 后,BSP 会启用 OSPTEK +AM319M262928ZS 的 DE0、TCON0 与单 lane RGB888 MIPI-DSI 输出。显示模组的 +VCI 供电应由转接板预先保持为使能状态。DSI 使用 PD12 至 PD21,因此不能和 +ST77916 的 SPI1 屏配置同时使用。 + +该模组的触摸控制器为 CST3530,I2C 地址是 `0x58`。CO6300 板级配置默认将 +它接到 I2C2(`PB2`/SDA、`PB3`/SCL),频率为 400 kHz,并以 20 ms 周期轮询, +因此只连接 I2C 两线即可使用。若额外连接了模组的中断和复位线,可在 DTS 节点 +中添加 `interrupt-gpios`、`reset-gpios`,驱动会自动改用中断上报和硬件复位。 + +DE framebuffer 注册为 `fb0`。启动后可使用以下命令验证 DE 扫描输出和 G2D: + +```text +g2d_test +g2d_anim +``` + +`g2d_test` 会将填充、BitBlt、缩放、旋转和 alpha 混合的结果显示到 `fb0`; +`g2d_anim` 持续执行缩放和旋转合成。两个命令都可以额外指定 `g2d` 和 +framebuffer 设备名。准备兼容的 JPEG 或 MJPEG AVI 文件后,`jpegshow` 和 +`mjpegplay` 还可用于验证 JPEG 解码和 framebuffer 显示路径。 + +## 下载与运行 + +本 BSP 只生成裸 `rtthread.bin`,不会替换 boot ROM、boot0、GPT 或板级启动配置。 +M-mode 启动固件将 `rtos` 负载复制到 `0x40010000`,然后跳转到 `_start`。选择 S-mode +时,启动固件则必须经 SBI 进入,并传入标准 RISC-V 启动参数。 + +### SPI NOR 烧录 + +F101 的 FEL 访问使用当前版本的 [xboot/xfel](https://github.com/xboot/xfel)。在主机上 +构建工具,按住板子的 FEL 按键上电;`xfel version` 应显示 `F101`。 + +```sh +git clone https://github.com/xboot/xfel.git +make -C xfel +XFEL=$PWD/xfel/xfel +$XFEL version +$XFEL spinor +``` + +不要将 `rtthread.bin` 直接写入 SPI NOR。应保留当前 TOC 中的 `melis-config` 条目,使用 +板子既有的 `package_rtos.cfg` 和 Allwinner `dragonsecboot` 打包工具,将其与新的 `rtos` +条目一起封装为 `sunxi-package` 格式的 `boot_package.fex`。备份后只替换 `bootA` 包: + +```sh +$XFEL spinor read 0xe0000 0x154000 f101-boota-backup.fex +$XFEL spinor write 0xe0000 boot_package.fex +$XFEL reset +``` + +`0xe0000` 是出厂 16 MiB SPI NOR 布局中 `bootA` 分区的起始位置。使用相同的 +`spinor write` 命令写回备份即可恢复原 RTOS 包。不要覆盖 flash 偏移零或 GPT。 + +## 设备驱动支持 + +| 设备 | 状态 | 说明 | +| --- | --- | --- | +| ADC | 支持 | - | +| AUDIO | 支持 | - | +| UART | 支持 | - | +| DMA | 支持 | - | +| HWCrypto | 支持 | - | +| I2C | 支持 | - | +| SPI | 支持 | - | +| PWM | 支持 | - | +| Flash | 支持 | SPI-NOR/MTD | +| SDIO | 支持 | - | +| USB | 支持 | CDC ACM character-device | +| Watchdog | 支持 | - | +| IR-RX | 支持 | - | +| RTC | 支持 | - | +| Display | 支持 | QSPI ST77916 或 MIPI-DSI CO6300;RGB/LVDS/DSI 后端 | +| Touch | 支持 | CO6300 模组的 CST3530 | +| G2D | 支持 | - | +| Video | 支持 | - | + +## 参考资料 + +- [YuzukiNeko 硬件工程](https://github.com/YuzukiHD/YuzukiNeko) +- [YuzukiNeko 使用文档](https://dshanpi.100ask.net/docs/F101-YuzukiNeko/) +- [OSPTEK AM319M262928ZS CO6300 屏幕参考工程](https://github.com/osptek/amoled-3.19-262x928-mipi-co6300) +- [OpenixSuit 发布页](https://github.com/YuzukiTsuru/OpenixSuit/releases) diff --git a/bsp/allwinner/f101/SConscript b/bsp/allwinner/f101/SConscript new file mode 100644 index 000000000000..923ea1557b4f --- /dev/null +++ b/bsp/allwinner/f101/SConscript @@ -0,0 +1,18 @@ +# RT-Thread building script for the YuzukiNeko F101 BSP + +import os +from building import * + +cwd = GetCurrentDir() +objs = [] + +for directory in os.listdir(cwd): + sconscript = os.path.join(directory, 'SConscript') + + if os.path.isfile(os.path.join(cwd, sconscript)): + objs += SConscript(sconscript) + +objs += SConscript(os.path.join(cwd, '../dm/SConscript'), + variant_dir='dm', duplicate=0) + +Return('objs') diff --git a/bsp/allwinner/f101/SConstruct b/bsp/allwinner/f101/SConstruct new file mode 100644 index 000000000000..448fdab67d7d --- /dev/null +++ b/bsp/allwinner/f101/SConstruct @@ -0,0 +1,54 @@ +import os +import sys +import glob +import rtconfig + +from rtconfig import RTT_ROOT + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +import dtc +from building import * + +TARGET = 'rtthread.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools=['mingw'], + AS=rtconfig.AS, ASFLAGS=rtconfig.AFLAGS, + CC=rtconfig.CC, CCFLAGS=rtconfig.CFLAGS, + CXX=rtconfig.CXX, CXXFLAGS=rtconfig.CXXFLAGS, + AR=rtconfig.AR, ARFLAGS='-rc', + LINK=rtconfig.LINK, LINKFLAGS=rtconfig.LFLAGS) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) +env['ASCOM'] = env['ASPPCOM'] + +Export('RTT_ROOT') +Export('rtconfig') + +env.Append(CPPPATH=[os.path.join(os.getcwd(), 'board')]) + +objs = PrepareBuilding(env, RTT_ROOT, has_libcpu=False) + +if GetDepend('RT_USING_BUILTIN_FDT'): + if os.getenv('DTC_PATH'): + os.environ['PATH'] = os.path.dirname(os.getenv('DTC_PATH')) + \ + os.pathsep + os.environ['PATH'] + + dtb_name = GetDepend('RT_BUILTIN_FDT_PATH').strip('"') + dts_name = os.path.splitext(dtb_name)[0] + '.dts' + dtb_path = os.path.join(os.getcwd(), dtb_name) + + # dtc.py tracks the root DTS only, while this board uses a shared DTSI. + # Invalidate its generated output whenever a local include has changed. + if os.path.exists(dtb_path): + dts_dir = os.path.dirname(dtb_path) + dtsi_paths = glob.glob(os.path.join(dts_dir, '**', '*.dtsi'), + recursive=True) + dtsi_paths.append(os.path.join(os.getcwd(), 'rtconfig.h')) + if any(os.path.getmtime(path) > os.path.getmtime(dtb_path) + for path in dtsi_paths): + os.remove(dtb_path) + + dtc.dts_to_dtb(RTT_ROOT, [dts_name], + include_paths=[os.getcwd(), os.path.join(os.getcwd(), '../dm/include')]) + +DoBuilding(TARGET, objs) diff --git a/bsp/allwinner/f101/applications/SConscript b/bsp/allwinner/f101/applications/SConscript new file mode 100644 index 000000000000..00fa316b2dcb --- /dev/null +++ b/bsp/allwinner/f101/applications/SConscript @@ -0,0 +1,18 @@ +from building import * + +cwd = GetCurrentDir() +src = ['main.c'] + +if GetDepend(['RT_SOC_SUNXI_JPEG_DEC']): + src += ['jpegd.c'] + +if GetDepend(['RT_SOC_SUNXI_G2D']): + src += ['g2d.c'] + +if GetDepend(['RT_GRAPHIC_FB_ST77916', 'RT_USING_FINSH']): + src += ['fb_rotate.c'] + +group = DefineGroup('Applications', src, depend=[''], + CPPPATH=[cwd, cwd + '/../dm/include']) + +Return('group') diff --git a/bsp/allwinner/f101/applications/fb_rotate.c b/bsp/allwinner/f101/applications/fb_rotate.c new file mode 100644 index 000000000000..36a7f2eec126 --- /dev/null +++ b/bsp/allwinner/f101/applications/fb_rotate.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +static int fb_rotate(int argc, char **argv) +{ + static const char *angles[] = { "0", "90", "180", "270" }; + const char *name; + rt_device_t dev; + struct rt_graphic_device *graphic; + struct rt_graphic_plane *plane; + rt_ubase_t rotation; + rt_err_t err; + + if (argc < 2 || argc > 3) + { + goto usage; + } + for (rotation = 0; rotation < RT_ARRAY_SIZE(angles); ++rotation) + { + if (!rt_strcmp(argv[1], angles[rotation])) + { + break; + } + } + if (rotation == RT_ARRAY_SIZE(angles)) + { + goto usage; + } + + name = argc == 3 ? argv[2] : "fb0"; + dev = rt_device_find(name); + if (!dev || dev->type != RT_Device_Class_Graphic) + { + rt_kprintf("fb_rotate: graphic device %s not found\n", name); + return -RT_ENOENT; + } + err = rt_device_open(dev, RT_DEVICE_OFLAG_RDWR); + if (err) + { + return err; + } + + graphic = rt_container_of(dev, struct rt_graphic_device, parent); + plane = graphic->primary_plane; + if (!plane || !plane->ops || !plane->ops->prop_set) + { + err = -RT_ENOSYS; + } + else + { + err = plane->ops->prop_set(plane, RT_GRAPHIC_PLANE_PROP_ROTATE, + (void *)rotation); + } + rt_device_close(dev); + if (err) + { + rt_kprintf("fb_rotate: %s\n", rt_strerror(err)); + } + else + { + rt_kprintf("fb_rotate: %s %s degrees\n", name, angles[rotation]); + } + return err; + +usage: + rt_kprintf("usage: fb_rotate <0|90|180|270> [fb0]\n"); + return -RT_EINVAL; +} +MSH_CMD_EXPORT(fb_rotate, rotate the primary plane of a framebuffer); diff --git a/bsp/allwinner/f101/applications/g2d.c b/bsp/allwinner/f101/applications/g2d.c new file mode 100644 index 000000000000..53f69794a585 --- /dev/null +++ b/bsp/allwinner/f101/applications/g2d.c @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#if defined(RT_USING_FINSH) && defined(RT_USING_GRAPHIC) && \ + defined(RT_SOC_SUNXI_G2D) + +#include +#include + +#include + +#define F101_G2D_PATTERN_WIDTH 96U +#define F101_G2D_PATTERN_HEIGHT 64U +#define F101_G2D_TEST_MARGIN 12U +#define F101_G2D_SCALE_NUMERATOR 3U +#define F101_G2D_SCALE_DENOMINATOR 2U +#define F101_G2D_ANIM_DEFAULT_FRAMES 180U +#define F101_G2D_ANIM_MAX_FRAMES 3600U + +struct f101_g2d_format +{ + enum sunxi_g2d_format format; + rt_uint8_t bytes_per_pixel; + const char *name; +}; + +static rt_err_t f101_g2d_format_from_info( + const struct rt_device_graphic_info *info, + struct f101_g2d_format *format) +{ + switch (info->pixel_format) + { + case RTGRAPHIC_PIXEL_FORMAT_RGB565: + format->format = SUNXI_G2D_FORMAT_RGB565; + format->bytes_per_pixel = 2; + format->name = "RGB565"; + return RT_EOK; + + case RTGRAPHIC_PIXEL_FORMAT_ARGB888: + format->format = SUNXI_G2D_FORMAT_ARGB8888; + format->bytes_per_pixel = 4; + format->name = "ARGB8888"; + return RT_EOK; + + case RTGRAPHIC_PIXEL_FORMAT_ABGR888: + format->format = SUNXI_G2D_FORMAT_ABGR8888; + format->bytes_per_pixel = 4; + format->name = "ABGR8888"; + return RT_EOK; + + default: + return -RT_ENOSYS; + } +} + +static rt_uint32_t f101_g2d_pixel(const struct f101_g2d_format *format, + rt_uint8_t red, rt_uint8_t green, rt_uint8_t blue) +{ + if (format->format == SUNXI_G2D_FORMAT_RGB565) + { + return ((rt_uint16_t)(red & 0xf8) << 8) | + ((rt_uint16_t)(green & 0xfc) << 3) | (blue >> 3); + } + if (format->format == SUNXI_G2D_FORMAT_ABGR8888) + { + return 0xff000000U | ((rt_uint32_t)blue << 16) | + ((rt_uint32_t)green << 8) | red; + } + + return 0xff000000U | ((rt_uint32_t)red << 16) | + ((rt_uint32_t)green << 8) | blue; +} + +static void f101_g2d_make_pattern(void *buffer, + const struct f101_g2d_format *format, rt_uint16_t width, + rt_uint16_t height) +{ + rt_uint16_t x; + rt_uint16_t y; + + for (y = 0; y < height; ++y) + { + for (x = 0; x < width; ++x) + { + rt_uint8_t red = x * 255U / width; + rt_uint8_t green = y * 255U / height; + rt_uint8_t blue = (x + y) * 127U / (width + height); + + if (x < width / 5U && y < height / 4U) + { + red = 255; + green = 32; + blue = 32; + } + else if (x >= width * 4U / 5U && y < height / 4U) + { + red = 32; + green = 255; + blue = 32; + } + else if (x < width / 5U && y >= height * 3U / 4U) + { + red = 32; + green = 64; + blue = 255; + } + else if (x >= width * 4U / 5U && y >= height * 3U / 4U) + { + red = 255; + green = 255; + blue = 255; + } + + if (x > width / 3U && x < width / 3U + 4U) + { + red = 255; + green = 255; + blue = 0; + } + if (format->bytes_per_pixel == 2) + { + ((rt_uint16_t *)buffer)[(rt_size_t)y * width + x] = + f101_g2d_pixel(format, red, green, blue); + } + else + { + ((rt_uint32_t *)buffer)[(rt_size_t)y * width + x] = + f101_g2d_pixel(format, red, green, blue); + } + } + } +} + +static rt_uint32_t f101_g2d_triangle(rt_uint32_t frame, rt_uint32_t period, + rt_uint32_t amplitude) +{ + rt_uint32_t half = period / 2U; + rt_uint32_t phase = frame % period; + + if (phase > half) + { + phase = period - phase; + } + + return half ? (rt_uint64_t)amplitude * phase / half : 0; +} + +static rt_err_t f101_g2d_parse_frames(const char *text, rt_uint32_t *frames) +{ + char *end; + unsigned long value = strtoul(text, &end, 0); + + if (!text[0] || *end || !value || value > F101_G2D_ANIM_MAX_FRAMES) + { + return -RT_EINVAL; + } + + *frames = value; + return RT_EOK; +} + +static rt_err_t f101_g2d_present(rt_device_t framebuffer, + const struct rt_device_graphic_info *info, + const struct sunxi_g2d_buffer *canvas) +{ + struct rt_device_rect_info rect = + { + .x = 0, + .y = 0, + .width = info->width, + .height = info->height, + }; + + /* + * Keep G2D rendering off-screen. The ST77916 framebuffer driver waits for + * TE from its RECT_UPDATE path, immediately before sending this completed + * buffer to panel GRAM. Calling WAIT_VSYNC here would consume one TE edge + * and make the driver wait for the following frame instead. + */ + rt_memcpy(info->framebuffer, canvas->virt_addr, canvas->size); + return rt_device_control(framebuffer, RTGRAPHIC_CTRL_RECT_UPDATE, &rect); +} + +static rt_err_t f101_g2d_prepare(rt_device_t *g2d, rt_device_t *framebuffer, + struct rt_device_graphic_info *info, int argc, char **argv, + int maximum_arguments, struct f101_g2d_format *format) +{ + const char *g2d_name = argc > 1 ? argv[1] : "g2d0"; + const char *fb_name = argc > 2 ? argv[2] : "fb0"; + rt_err_t err; + + if (argc > maximum_arguments) + { + return -RT_EINVAL; + } + + *g2d = rt_device_find(g2d_name); + *framebuffer = rt_device_find(fb_name); + if (!*g2d || !*framebuffer) + { + rt_kprintf("device not found: %s or %s\n", g2d_name, fb_name); + return -RT_ENOSYS; + } + + err = rt_device_open(*framebuffer, RT_DEVICE_OFLAG_RDWR); + if (err) + { + return err; + } + err = rt_device_control(*framebuffer, RTGRAPHIC_CTRL_GET_INFO, info); + if (err) + { + rt_device_close(*framebuffer); + return err; + } + err = f101_g2d_format_from_info(info, format); + if (err || !info->framebuffer || + info->bits_per_pixel != format->bytes_per_pixel * 8U || + info->pitch < (rt_uint32_t)info->width * format->bytes_per_pixel) + { + rt_kprintf("G2D test requires an RGB565, ARGB8888, or ABGR8888 framebuffer\n"); + rt_device_close(*framebuffer); + return -RT_ENOSYS; + } + + return RT_EOK; +} + +static rt_err_t f101_g2d_allocate_canvas(rt_device_t g2d, + const struct rt_device_graphic_info *info, + const struct f101_g2d_format *format, struct sunxi_g2d_buffer *canvas, + struct sunxi_g2d_image *image) +{ + rt_err_t err; + + canvas->size = (rt_size_t)info->pitch * info->height; + err = rt_device_control(g2d, SUNXI_G2D_CMD_ALLOC_BUFFER, canvas); + if (err) + { + return err; + } + + image->buffer = canvas; + image->width = info->width; + image->height = info->height; + image->pitch = info->pitch; + image->format = format->format; + return RT_EOK; +} + +static rt_err_t f101_g2d_fill(rt_device_t g2d, + struct sunxi_g2d_image *image, rt_uint16_t x, rt_uint16_t y, + rt_uint16_t width, rt_uint16_t height, rt_uint32_t color) +{ + struct sunxi_g2d_fill request = + { + .destination = image, + .rect = { x, y, width, height }, + .color = color, + .timeout = rt_tick_from_millisecond(100), + }; + + return rt_device_control(g2d, SUNXI_G2D_CMD_FILL, &request); +} + +static rt_err_t f101_g2d_blit(rt_device_t g2d, + struct sunxi_g2d_image *source, struct sunxi_g2d_image *destination, + rt_uint16_t destination_x, rt_uint16_t destination_y) +{ + struct sunxi_g2d_blit request = + { + .source = source, + .source_rect = { 0, 0, source->width, source->height }, + .destination = destination, + .destination_rect = + { destination_x, destination_y, source->width, source->height }, + .timeout = rt_tick_from_millisecond(100), + }; + + return rt_device_control(g2d, SUNXI_G2D_CMD_BLIT, &request); +} + +static void f101_g2d_image_to_surface(const struct sunxi_g2d_image *image, + struct sunxi_g2d_surface *surface) +{ + rt_memset(surface, 0, sizeof(*surface)); + surface->format = image->format; + surface->width = image->width; + surface->height = image->height; + surface->plane[0].buffer = image->buffer; + surface->plane[0].pitch = image->pitch; +} + +static rt_err_t f101_g2d_compose_blit(rt_device_t g2d, + const struct sunxi_g2d_image *source, + const struct sunxi_g2d_rect *source_rect, + const struct sunxi_g2d_image *destination, + const struct sunxi_g2d_rect *destination_rect, + enum sunxi_g2d_rotation rotation) +{ + struct sunxi_g2d_operation operation; + + rt_memset(&operation, 0, sizeof(operation)); + operation.type = SUNXI_G2D_OPERATION_BLIT; + operation.source_rect = *source_rect; + operation.destination_rect = *destination_rect; + operation.rotation = rotation; + operation.timeout = rt_tick_from_millisecond(100); + f101_g2d_image_to_surface(source, &operation.source); + f101_g2d_image_to_surface(destination, &operation.destination); + + return rt_device_control(g2d, SUNXI_G2D_CMD_COMPOSE, &operation); +} + +static rt_err_t f101_g2d_compose_blend(rt_device_t g2d, + const struct sunxi_g2d_image *foreground, + const struct sunxi_g2d_rect *foreground_rect, + const struct sunxi_g2d_image *background, + const struct sunxi_g2d_rect *background_rect, + const struct sunxi_g2d_image *destination, + const struct sunxi_g2d_rect *destination_rect) +{ + struct sunxi_g2d_operation operation; + + rt_memset(&operation, 0, sizeof(operation)); + operation.type = SUNXI_G2D_OPERATION_BLEND; + operation.source_rect = *foreground_rect; + operation.background_rect = *background_rect; + operation.destination_rect = *destination_rect; + operation.blend.mode = SUNXI_G2D_BLEND_SRC_OVER; + operation.blend.foreground_alpha_mode = SUNXI_G2D_ALPHA_GLOBAL; + operation.blend.foreground_alpha = 160; + operation.blend.background_alpha_mode = SUNXI_G2D_ALPHA_PIXEL; + operation.blend.background_alpha = 255; + operation.timeout = rt_tick_from_millisecond(100); + f101_g2d_image_to_surface(foreground, &operation.source); + f101_g2d_image_to_surface(background, &operation.background); + f101_g2d_image_to_surface(destination, &operation.destination); + + return rt_device_control(g2d, SUNXI_G2D_CMD_COMPOSE, &operation); +} + +static int g2d_test(int argc, char **argv) +{ + rt_device_t g2d; + rt_device_t framebuffer; + struct rt_device_graphic_info info; + struct sunxi_g2d_buffer canvas = { 0 }; + struct sunxi_g2d_buffer pattern = { 0 }; + struct sunxi_g2d_buffer background = { 0 }; + struct sunxi_g2d_image canvas_image = { 0 }; + struct sunxi_g2d_image pattern_image = { 0 }; + struct sunxi_g2d_image background_image = { 0 }; + struct sunxi_g2d_rect pattern_rect; + struct sunxi_g2d_rect scale_rect; + struct sunxi_g2d_rect rotate_rect; + struct sunxi_g2d_rect blend_rect; + struct f101_g2d_format format; + rt_uint32_t version; + rt_err_t err; + + err = f101_g2d_prepare(&g2d, &framebuffer, &info, argc, argv, 3, &format); + if (err) + { + rt_kprintf("usage: g2d_test [g2d] [fb]\n"); + return err; + } + if (info.width < 2U * F101_G2D_TEST_MARGIN + + F101_G2D_PATTERN_WIDTH * F101_G2D_SCALE_NUMERATOR / + F101_G2D_SCALE_DENOMINATOR || + info.height < F101_G2D_TEST_MARGIN * 2U + + F101_G2D_PATTERN_HEIGHT * F101_G2D_SCALE_NUMERATOR / + F101_G2D_SCALE_DENOMINATOR * 2U) + { + rt_kprintf("G2D test requires a framebuffer of at least 168x216\n"); + err = -RT_EINVAL; + goto _out; + } + + err = f101_g2d_allocate_canvas(g2d, &info, &format, &canvas, &canvas_image); + if (err) + { + goto _out; + } + pattern.size = F101_G2D_PATTERN_WIDTH * F101_G2D_PATTERN_HEIGHT * + format.bytes_per_pixel; + err = rt_device_control(g2d, SUNXI_G2D_CMD_ALLOC_BUFFER, &pattern); + if (err) + { + goto _out; + } + pattern_image.buffer = &pattern; + pattern_image.width = F101_G2D_PATTERN_WIDTH; + pattern_image.height = F101_G2D_PATTERN_HEIGHT; + pattern_image.pitch = F101_G2D_PATTERN_WIDTH * format.bytes_per_pixel; + pattern_image.format = format.format; + f101_g2d_make_pattern(pattern.virt_addr, &format, pattern_image.width, + pattern_image.height); + + err = f101_g2d_allocate_canvas(g2d, &info, &format, &background, + &background_image); + if (err) + { + goto _out; + } + + pattern_rect = (struct sunxi_g2d_rect) + { + 0, 0, pattern_image.width, pattern_image.height + }; + scale_rect = (struct sunxi_g2d_rect) + { + info.width - F101_G2D_TEST_MARGIN - + pattern_image.width * F101_G2D_SCALE_NUMERATOR / + F101_G2D_SCALE_DENOMINATOR, + F101_G2D_TEST_MARGIN, + pattern_image.width * F101_G2D_SCALE_NUMERATOR / + F101_G2D_SCALE_DENOMINATOR, + pattern_image.height * F101_G2D_SCALE_NUMERATOR / + F101_G2D_SCALE_DENOMINATOR, + }; + rotate_rect = (struct sunxi_g2d_rect) + { + F101_G2D_TEST_MARGIN, + F101_G2D_TEST_MARGIN + pattern_image.height + F101_G2D_TEST_MARGIN, + pattern_image.height, + pattern_image.width, + }; + blend_rect = (struct sunxi_g2d_rect) + { + scale_rect.x, + scale_rect.y + scale_rect.height + F101_G2D_TEST_MARGIN, + scale_rect.width, + scale_rect.height, + }; + + err = f101_g2d_fill(g2d, &canvas_image, 0, 0, info.width, info.height, + 0xff182028U); + if (err) + { + goto _out; + } + err = f101_g2d_blit(g2d, &pattern_image, &canvas_image, + F101_G2D_TEST_MARGIN, F101_G2D_TEST_MARGIN); + if (err) + { + goto _out; + } + err = f101_g2d_compose_blit(g2d, &pattern_image, &pattern_rect, + &canvas_image, &scale_rect, SUNXI_G2D_ROTATE_0); + if (err) + { + goto _out; + } + err = f101_g2d_compose_blit(g2d, &pattern_image, &pattern_rect, + &canvas_image, &rotate_rect, SUNXI_G2D_ROTATE_90); + if (err) + { + goto _out; + } + err = f101_g2d_fill(g2d, &background_image, 0, 0, info.width, + info.height, 0xff203060U); + if (err) + { + goto _out; + } + err = f101_g2d_compose_blend(g2d, &pattern_image, &pattern_rect, + &background_image, &blend_rect, &canvas_image, &blend_rect); + if (err) + { + goto _out; + } + err = f101_g2d_present(framebuffer, &info, &canvas); + if (err) + { + goto _out; + } + rt_device_control(g2d, SUNXI_G2D_CMD_GET_VERSION, &version); + rt_kprintf("G2D %08x completed fill, BitBlt, scale, rotate and blend test\n", + version); + +_out: + if (err) + { + rt_kprintf("G2D test failed: %d\n", err); + } + if (pattern.virt_addr) + { + rt_device_control(g2d, SUNXI_G2D_CMD_FREE_BUFFER, &pattern); + } + if (canvas.virt_addr) + { + rt_device_control(g2d, SUNXI_G2D_CMD_FREE_BUFFER, &canvas); + } + if (background.virt_addr) + { + rt_device_control(g2d, SUNXI_G2D_CMD_FREE_BUFFER, &background); + } + rt_device_close(framebuffer); + return err; +} +MSH_CMD_EXPORT(g2d_test, run F101 G2D fill BitBlt scale rotate blend test); + +static int g2d_anim(int argc, char **argv) +{ + rt_device_t g2d; + rt_device_t framebuffer; + struct rt_device_graphic_info info; + struct sunxi_g2d_buffer canvas = { 0 }; + struct sunxi_g2d_buffer pattern = { 0 }; + struct sunxi_g2d_image canvas_image = { 0 }; + struct sunxi_g2d_image pattern_image = { 0 }; + struct f101_g2d_format format; + rt_uint32_t frames = F101_G2D_ANIM_DEFAULT_FRAMES; + rt_tick_t started; + rt_tick_t elapsed; + rt_uint32_t frame; + rt_err_t err; + + if (argc > 4 || (argc == 4 && + f101_g2d_parse_frames(argv[3], &frames) != RT_EOK)) + { + rt_kprintf("usage: g2d_anim [g2d] [fb] [frames: 1-%u]\n", + F101_G2D_ANIM_MAX_FRAMES); + return -RT_EINVAL; + } + err = f101_g2d_prepare(&g2d, &framebuffer, &info, argc, argv, 4, &format); + if (err) + { + return err; + } + if (info.width < F101_G2D_PATTERN_WIDTH * 2U || + info.height < F101_G2D_PATTERN_HEIGHT * 2U) + { + err = -RT_EINVAL; + goto _out; + } + + err = f101_g2d_allocate_canvas(g2d, &info, &format, &canvas, &canvas_image); + if (err) + { + goto _out; + } + pattern.size = F101_G2D_PATTERN_WIDTH * F101_G2D_PATTERN_HEIGHT * + format.bytes_per_pixel; + err = rt_device_control(g2d, SUNXI_G2D_CMD_ALLOC_BUFFER, &pattern); + if (err) + { + goto _out; + } + pattern_image.buffer = &pattern; + pattern_image.width = F101_G2D_PATTERN_WIDTH; + pattern_image.height = F101_G2D_PATTERN_HEIGHT; + pattern_image.pitch = F101_G2D_PATTERN_WIDTH * format.bytes_per_pixel; + pattern_image.format = format.format; + f101_g2d_make_pattern(pattern.virt_addr, &format, pattern_image.width, + pattern_image.height); + + rt_kprintf("G2D animation: %s scale and rotate, %u frames\n", format.name, + frames); + started = rt_tick_get_millisecond(); + for (frame = 0; frame < frames; ++frame) + { + struct sunxi_g2d_rect source_rect = + { + 0, 0, pattern_image.width, pattern_image.height + }; + struct sunxi_g2d_rect scale_rect; + struct sunxi_g2d_rect rotate_rect; + enum sunxi_g2d_rotation rotation; + rt_uint16_t scale_width = pattern_image.width + + f101_g2d_triangle(frame, 127, + pattern_image.width); + rt_uint16_t scale_height = (rt_uint32_t)pattern_image.height * + scale_width / pattern_image.width; + rt_uint16_t rotate_width; + rt_uint16_t rotate_height; + rt_uint8_t red = 20U + f101_g2d_triangle(frame, 181, 48); + rt_uint8_t green = 24U + f101_g2d_triangle(frame + 53U, 157, 48); + rt_uint8_t blue = 32U + f101_g2d_triangle(frame + 101U, 193, 48); + rt_uint32_t background = 0xff000000U | ((rt_uint32_t)red << 16) | + ((rt_uint32_t)green << 8) | blue; + + err = f101_g2d_fill(g2d, &canvas_image, 0, 0, info.width, + info.height, background); + if (err) + { + break; + } + scale_rect = (struct sunxi_g2d_rect) + { + f101_g2d_triangle(frame, 137, info.width - scale_width), + f101_g2d_triangle(frame + 41U, 109, info.height - scale_height), + scale_width, + scale_height, + }; + err = f101_g2d_compose_blit(g2d, &pattern_image, &source_rect, + &canvas_image, &scale_rect, SUNXI_G2D_ROTATE_0); + if (err) + { + break; + } + rotation = (frame / 45U) % 3U + SUNXI_G2D_ROTATE_90; + if (rotation == SUNXI_G2D_ROTATE_90 || + rotation == SUNXI_G2D_ROTATE_270) + { + rotate_width = pattern_image.height; + rotate_height = pattern_image.width; + } + else + { + rotate_width = pattern_image.width; + rotate_height = pattern_image.height; + } + rotate_rect = (struct sunxi_g2d_rect) + { + info.width - F101_G2D_TEST_MARGIN - rotate_width, + info.height - F101_G2D_TEST_MARGIN - rotate_height, + rotate_width, + rotate_height, + }; + err = f101_g2d_compose_blit(g2d, &pattern_image, &source_rect, + &canvas_image, &rotate_rect, rotation); + if (err) + { + break; + } + err = f101_g2d_present(framebuffer, &info, &canvas); + if (err) + { + break; + } + } + elapsed = rt_tick_get_millisecond() - started; + if (!elapsed) + { + elapsed = 1; + } + if (!err) + { + rt_kprintf("G2D animation done: %u frames in %u ms, %u.%u fps\n", + frames, (unsigned int)elapsed, + (unsigned int)((rt_uint64_t)frames * 1000U / elapsed), + (unsigned int)(((rt_uint64_t)frames * 10000U / elapsed) % 10U)); + } + +_out: + if (err) + { + rt_kprintf("G2D animation failed at frame %u: %d\n", frame, err); + } + if (pattern.virt_addr) + { + rt_device_control(g2d, SUNXI_G2D_CMD_FREE_BUFFER, &pattern); + } + if (canvas.virt_addr) + { + rt_device_control(g2d, SUNXI_G2D_CMD_FREE_BUFFER, &canvas); + } + rt_device_close(framebuffer); + return err; +} +MSH_CMD_EXPORT(g2d_anim, animate an F101 G2D scale and rotate scene); + +#endif /* RT_USING_FINSH && RT_USING_GRAPHIC && RT_SOC_SUNXI_G2D */ diff --git a/bsp/allwinner/f101/applications/jpegd.c b/bsp/allwinner/f101/applications/jpegd.c new file mode 100644 index 000000000000..2321cf7ba3cc --- /dev/null +++ b/bsp/allwinner/f101/applications/jpegd.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#if defined(RT_USING_DFS) && defined(RT_USING_FINSH) + +#include +#include +#include +#include + +#include + +#define F101_JPEGD_IO_SIZE 4096 +#define F101_JPEGD_STREAM_ALIGN 1024 + +static rt_err_t f101_jpegd_read_exact(int fd, void *buffer, rt_size_t size) +{ + rt_uint8_t *data = buffer; + + while (size) + { + int length = read(fd, data, size); + + if (length <= 0) + { + return -RT_EIO; + } + + data += length; + size -= length; + } + + return RT_EOK; +} + +static rt_err_t f101_jpegd_read_to_dma(int fd, void *buffer, rt_size_t size) +{ + rt_uint8_t *bounce; + rt_uint8_t *destination = buffer; + rt_err_t err = RT_EOK; + + bounce = rt_malloc(F101_JPEGD_IO_SIZE); + if (!bounce) + { + return -RT_ENOMEM; + } + + while (size) + { + rt_size_t count = rt_min_t(rt_size_t, size, F101_JPEGD_IO_SIZE); + + err = f101_jpegd_read_exact(fd, bounce, count); + if (err) + { + break; + } + + rt_memcpy(destination, bounce, count); + destination += count; + size -= count; + } + + rt_free(bounce); + return err; +} + +static rt_err_t f101_jpegd_write_all(int fd, const void *buffer, + rt_size_t size) +{ + const rt_uint8_t *data = buffer; + + while (size) + { + int length = write(fd, data, size); + + if (length <= 0) + { + return -RT_EIO; + } + + data += length; + size -= length; + } + + return RT_EOK; +} + +static void f101_jpegd_free_buffer(rt_device_t device, + struct sunxi_jpegd_buffer *buffer) +{ + if (buffer->virt_addr) + { + rt_device_control(device, SUNXI_JPEGD_CMD_FREE_BUFFER, buffer); + } +} + +#ifdef RT_USING_GRAPHIC +static rt_size_t f101_jpegd_mb32_offset(unsigned int x, unsigned int y, + unsigned int stride) +{ + return ((rt_size_t)(y / 32) * (stride / 32) + x / 32) * 1024 + + (y % 32) * 32 + x % 32; +} + +static rt_uint8_t f101_jpegd_clip(int value) +{ + return value < 0 ? 0 : value > 255 ? 255 : value; +} + +static rt_err_t f101_jpegd_display(const char *name, + const struct sunxi_jpegd_decode *image) +{ + struct rt_device_graphic_info info = { 0 }; + struct rt_device_rect_info rect = { 0 }; + const rt_uint8_t *luma = image->output->virt_addr; + const rt_uint8_t *chroma = luma + image->luma_size; + rt_device_t fb = rt_device_find(name); + rt_uint8_t *canvas = RT_NULL; + rt_size_t size; + unsigned int width, height, left, top, bytes, x, y; + rt_err_t err; + + if (!fb) + { + rt_kprintf("framebuffer not found: %s\n", name); + return -RT_ENOSYS; + } + if (image->format != SUNXI_JPEGD_FORMAT_MB32_420 || + !image->width || !image->height) + { + return -RT_EINVAL; + } + err = rt_device_open(fb, RT_DEVICE_OFLAG_RDWR); + if (err) + { + return err; + } + err = rt_device_control(fb, RTGRAPHIC_CTRL_GET_INFO, &info); + if (err) + { + goto _out; + } + switch (info.pixel_format) + { + case RTGRAPHIC_PIXEL_FORMAT_RGB565: + bytes = 2; + break; + case RTGRAPHIC_PIXEL_FORMAT_ARGB888: + case RTGRAPHIC_PIXEL_FORMAT_ABGR888: + bytes = 4; + break; + default: + rt_kprintf("unsupported framebuffer format: %u\n", info.pixel_format); + err = -RT_ENOSYS; + goto _out; + } + if (!info.framebuffer || !info.width || !info.height || + info.bits_per_pixel != bytes * 8 || info.pitch < info.width * bytes || + info.height > (rt_size_t)-1 / info.pitch) + { + err = -RT_EINVAL; + goto _out; + } + size = (rt_size_t)info.pitch * info.height; + canvas = rt_calloc(1, size); + if (!canvas) + { + err = -RT_ENOMEM; + goto _out; + } + width = info.width; + height = (rt_uint64_t)image->height * width / image->width; + if (height > info.height) + { + height = info.height; + width = (rt_uint64_t)image->width * height / image->height; + } + width = rt_max(width, 1U); + height = rt_max(height, 1U); + left = (info.width - width) / 2; + top = (info.height - height) / 2; + + /* VE MB32 uses 32x32 byte tiles for Y and interleaved UV. */ + for (y = 0; y < height; ++y) + { + unsigned int sy = (rt_uint64_t)y * image->height / height; + rt_uint8_t *row = canvas + (rt_size_t)(top + y) * info.pitch + left * bytes; + + for (x = 0; x < width; ++x) + { + unsigned int sx = (rt_uint64_t)x * image->width / width; + rt_size_t uv = f101_jpegd_mb32_offset(sx & ~1U, sy / 2, image->stride); + int yy = luma[f101_jpegd_mb32_offset(sx, sy, image->stride)]; + int u = chroma[uv] - 128; + int v = chroma[uv + 1] - 128; + rt_uint8_t r = f101_jpegd_clip(yy + ((359 * v) >> 8)); + rt_uint8_t g = f101_jpegd_clip(yy - ((88 * u + 183 * v) >> 8)); + rt_uint8_t b = f101_jpegd_clip(yy + ((454 * u) >> 8)); + + if (bytes == 2) + { + rt_uint16_t pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); + rt_memcpy(row + x * bytes, &pixel, sizeof(pixel)); + } + else + { + rt_uint32_t pixel = info.pixel_format == RTGRAPHIC_PIXEL_FORMAT_ARGB888 ? + 0xff000000U | ((rt_uint32_t)r << 16) | (g << 8) | b : + 0xff000000U | ((rt_uint32_t)b << 16) | (g << 8) | r; + rt_memcpy(row + x * bytes, &pixel, sizeof(pixel)); + } + } + } + rt_memcpy(info.framebuffer, canvas, size); + rect.width = info.width; + rect.height = info.height; + err = rt_device_control(fb, RTGRAPHIC_CTRL_RECT_UPDATE, &rect); + if (!err) + { + rt_kprintf("JPEG %ux%u displayed on %s\n", image->width, image->height, name); + } + +_out: + rt_free(canvas); + rt_device_close(fb); + return err; +} +#endif + +static int f101_jpegd_run(int argc, char **argv, rt_bool_t display) +{ + struct sunxi_jpegd_buffer input = { 0 }; + struct sunxi_jpegd_buffer output = { 0 }; + struct sunxi_jpegd_info info = { 0 }; + struct sunxi_jpegd_decode request = { 0 }; + struct stat statbuf; + rt_device_t device; + rt_err_t err = -RT_ERROR; + rt_size_t input_size; + int input_fd = -1; + int output_fd = -1; + + if ((!display && argc != 3) || (display && argc != 2 && argc != 3)) + { + rt_kprintf(display ? "Usage: jpegshow [fb0]\n" : + "Usage: jpegd \n"); + return -RT_EINVAL; + } + + device = rt_device_find("jpegd0"); + if (!device) + { + device = rt_device_find("jpegd"); + } + if (!device) + { + rt_kprintf("jpegd device not found\n"); + return -RT_ENOSYS; + } + + input_fd = open(argv[1], O_RDONLY, 0); + if (input_fd < 0 || fstat(input_fd, &statbuf) || statbuf.st_size <= 0) + { + rt_kprintf("cannot read %s\n", argv[1]); + err = -RT_EIO; + goto _out; + } + + input_size = statbuf.st_size; + if (input_size > (rt_size_t)-1 - (F101_JPEGD_STREAM_ALIGN - 1)) + { + err = -RT_EINVAL; + goto _out; + } + + input.size = RT_ALIGN(input_size, F101_JPEGD_STREAM_ALIGN); + err = rt_device_control(device, SUNXI_JPEGD_CMD_ALLOC_BUFFER, &input); + if (err) + { + goto _out; + } + err = f101_jpegd_read_to_dma(input_fd, input.virt_addr, input_size); + if (err) + { + goto _out; + } + rt_memset((rt_uint8_t *)input.virt_addr + input_size, 0xff, + input.size - input_size); + + info.input = &input; + info.input_size = input_size; + err = rt_device_control(device, SUNXI_JPEGD_CMD_GET_INFO, &info); + if (err) + { + rt_kprintf("invalid or unsupported JPEG: %d\n", err); + goto _out; + } + + output.size = info.output_size; + err = rt_device_control(device, SUNXI_JPEGD_CMD_ALLOC_BUFFER, &output); + if (err) + { + goto _out; + } + + request.input = &input; + request.output = &output; + request.input_size = input_size; + request.timeout = rt_tick_from_millisecond(500); + err = rt_device_control(device, SUNXI_JPEGD_CMD_DECODE, &request); + if (err) + { + rt_kprintf("JPEG decode failed: %d\n", err); + goto _out; + } + +#ifdef RT_USING_GRAPHIC + if (display) + { + err = f101_jpegd_display(argc > 2 ? argv[2] : "fb0", &request); + goto _out; + } +#endif + + output_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0); + if (output_fd < 0) + { + rt_kprintf("cannot create %s\n", argv[2]); + err = -RT_EIO; + goto _out; + } + err = f101_jpegd_write_all(output_fd, output.virt_addr, output.size); + if (err) + { + rt_kprintf("write %s failed\n", argv[2]); + goto _out; + } + + rt_kprintf("%ux%u JPEG decoded as MB32_420, stride %u, %u bytes\n", + request.width, request.height, request.stride, + (rt_uint32_t)output.size); + +_out: + if (output_fd >= 0) + { + close(output_fd); + } + if (input_fd >= 0) + { + close(input_fd); + } + f101_jpegd_free_buffer(device, &output); + f101_jpegd_free_buffer(device, &input); + + return err; +} +static int jpegd(int argc, char **argv) +{ + return f101_jpegd_run(argc, argv, RT_FALSE); +} +MSH_CMD_EXPORT(jpegd, decode baseline JPEG to MB32_420: jpegd input.jpg output.mb32); + +#ifdef RT_USING_GRAPHIC +static int jpegshow(int argc, char **argv) +{ + return f101_jpegd_run(argc, argv, RT_TRUE); +} +MSH_CMD_EXPORT(jpegshow, display baseline JPEG: jpegshow input.jpg [fb0]); +#endif + +#endif /* RT_USING_DFS && RT_USING_FINSH */ diff --git a/bsp/allwinner/f101/applications/main.c b/bsp/allwinner/f101/applications/main.c new file mode 100644 index 000000000000..fe4b207e7265 --- /dev/null +++ b/bsp/allwinner/f101/applications/main.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int main(void) +{ + rt_kprintf("Hello RISC-V\n"); + + return 0; +} diff --git a/bsp/allwinner/f101/board/SConscript b/bsp/allwinner/f101/board/SConscript new file mode 100644 index 000000000000..237a132e2e5a --- /dev/null +++ b/bsp/allwinner/f101/board/SConscript @@ -0,0 +1,6 @@ +from building import * + +cwd = GetCurrentDir() +group = DefineGroup('Board', ['board.c'], depend=[''], CPPPATH=[cwd]) + +Return('group') diff --git a/bsp/allwinner/f101/board/board.c b/bsp/allwinner/f101/board/board.c new file mode 100644 index 000000000000..7acf7a604f7e --- /dev/null +++ b/bsp/allwinner/f101/board/board.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#if defined(ARCH_RISCV_M_MODE) && defined(SBC_YUZUKINEKO) +#include + +static void f101_yuzukineko_machine_shutdown(void) +{ + /* YuzukiNeko has no PMIC or board-level power cut control. */ + rt_hw_interrupt_disable(); + + for (;;) + { + __asm__ volatile ("wfi"); + } +} +#endif /* ARCH_RISCV_M_MODE && SBC_YUZUKINEKO */ + +void rt_hw_board_init(void) +{ +#if defined(ARCH_RISCV_M_MODE) && defined(SBC_YUZUKINEKO) + rt_dm_machine_shutdown = f101_yuzukineko_machine_shutdown; +#endif + + rt_hw_common_setup(); +} diff --git a/bsp/allwinner/f101/board/board.h b/bsp/allwinner/f101/board/board.h new file mode 100644 index 000000000000..eca95db5b3c9 --- /dev/null +++ b/bsp/allwinner/f101/board/board.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef BOARD_H__ +#define BOARD_H__ + +#include + +void rt_hw_board_init(void); + +#endif diff --git a/bsp/allwinner/f101/board/usb_config.h b/bsp/allwinner/f101/board/usb_config.h new file mode 100644 index 000000000000..f38640c0a6c6 --- /dev/null +++ b/bsp/allwinner/f101/board/usb_config.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __F101_USB_CONFIG_H__ +#define __F101_USB_CONFIG_H__ + +#ifdef RT_USING_CACHE +#define CONFIG_USB_DCACHE_ENABLE +#endif + +#include + +/* F101 USB0 is the four-endpoint Sunxi MUSB device controller. */ +#undef CONFIG_USB_MUSB_EP_NUM +#define CONFIG_USB_MUSB_EP_NUM 4 + +#undef CONFIG_USB_MUSB_PIPE_NUM +#define CONFIG_USB_MUSB_PIPE_NUM 4 + +#define CONFIG_USB_MUSB_SUNXI + +#endif /* __F101_USB_CONFIG_H__ */ diff --git a/bsp/allwinner/f101/f101-yuzukineko.Kconfig b/bsp/allwinner/f101/f101-yuzukineko.Kconfig new file mode 100644 index 000000000000..10ef3132db15 --- /dev/null +++ b/bsp/allwinner/f101/f101-yuzukineko.Kconfig @@ -0,0 +1,69 @@ +menu "YuzukiNeko SBC Configuration" + +choice + prompt "YuzukiNeko boot privilege mode" + default SBC_YUZUKINEKO_MMODE + help + M-mode boots the RT-Thread image directly. S-mode expects an OpenSBI + firmware to enter the image with the standard RISC-V boot arguments. + +config SBC_YUZUKINEKO_MMODE + bool "M-mode (direct boot)" + select ARCH_RISCV_M_MODE + +config SBC_YUZUKINEKO_SMODE + bool "S-mode with Sv32 MMU (OpenSBI)" + select ARCH_MM_MMU + select RT_USING_CACHE + +config SBC_YUZUKINEKO_SMODE_BARE + bool "S-mode Bare, no MMU (OpenSBI)" + select RT_USING_CACHE + help + Run in supervisor mode with physical addresses and caches enabled. + OpenSBI is still required. Use the fixed reserved DMA pool. + +endchoice + +choice + prompt "YuzukiNeko display" + default SBC_YUZUKINEKO_ST77916 + +config SBC_YUZUKINEKO_ST77916 + bool "ST77916 360x360 QSPI LCD" + select RT_USING_QSPI + select RT_QSPI_SUNXI + select RT_GRAPHIC_FB_ST77916 + help + Enable the F101 SPI1 display connector configuration for a 360x360 + ST77916 QSPI panel. + +config SBC_YUZUKINEKO_CO6300 + bool "CO6300 262x928 one-lane MIPI-DSI AMOLED" + select RT_GRAPHIC_FB_SUNXI_DE + select RT_INPUT_TOUCHSCREEN + select RT_INPUT_TOUCHSCREEN_CST3530 + help + Enable the F101 DE0, TCON0, and MIPI-DSI configuration for the OSPTEK + AM319M262928ZS AMOLED panel with the CO6300 controller and its CST3530 + I2C touchscreen. + +config SBC_YUZUKINEKO_RADXA_LCD_8HD + bool "Radxa LCD 8HD 800x1280 two-lane MIPI-DSI LCD" + select RT_GRAPHIC_FB_SUNXI_DE + select RT_INPUT_TOUCHSCREEN + select RT_INPUT_TOUCHSCREEN_GOODIX + help + Use the Raspberry Pi 4B 15-pin to 40-pin populated accessory with + its auxiliary header and Goodix touchscreen. Backlight enable and + LCD reset are tied high; this configuration does not use PWM. + Mutually exclusive with other displays. TCON clock programming + and DSI video-mode compatibility still need hardware validation. + +endchoice + +config RT_BUILTIN_FDT_PATH + string + default "../dm/dts/sun252i-f101-yuzukineko.dtb" + +endmenu diff --git a/bsp/allwinner/f101/rtconfig.h b/bsp/allwinner/f101/rtconfig.h new file mode 100644 index 000000000000..69b7526c516c --- /dev/null +++ b/bsp/allwinner/f101/rtconfig.h @@ -0,0 +1,621 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* RT-Thread Kernel */ + +/* klibc options */ + +/* rt_vsnprintf options */ + +/* end of rt_vsnprintf options */ + +/* rt_vsscanf options */ + +/* end of rt_vsscanf options */ + +/* rt_memset options */ + +/* end of rt_memset options */ + +/* rt_memcpy options */ + +/* end of rt_memcpy options */ + +/* rt_memmove options */ + +/* end of rt_memmove options */ + +/* rt_memcmp options */ + +/* end of rt_memcmp options */ + +/* rt_strstr options */ + +/* end of rt_strstr options */ + +/* rt_strcasecmp options */ + +/* end of rt_strcasecmp options */ + +/* rt_strncpy options */ + +/* end of rt_strncpy options */ + +/* rt_strcpy options */ + +/* end of rt_strcpy options */ + +/* rt_strncmp options */ + +/* end of rt_strncmp options */ + +/* rt_strcmp options */ + +/* end of rt_strcmp options */ + +/* rt_strlen options */ + +/* end of rt_strlen options */ + +/* rt_strnlen options */ + +/* end of rt_strnlen options */ +/* end of klibc options */ +#define RT_NAME_MAX 24 +#define RT_CPUS_NR 1 +#define RT_ALIGN_SIZE 8 +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 1000 +#define RT_USING_OVERFLOW_CHECK +#define RT_USING_HOOK +#define RT_HOOK_USING_FUNC_PTR +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 1024 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 2048 + +/* kservice options */ + +/* end of kservice options */ +#define RT_USING_DEBUG +#define RT_DEBUGING_ASSERT +#define RT_DEBUGING_COLOR + +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE +/* end of Inter-Thread communication */ + +/* Memory Management */ + +#define RT_USING_MEMPOOL +#define RT_USING_SMALL_MEM +#define RT_USING_SMALL_MEM_AS_HEAP +#define RT_USING_HEAP +/* end of Memory Management */ +#define RT_USING_DEVICE +#define RT_USING_INTERRUPT_INFO +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 256 +#define RT_CONSOLE_DEVICE_NAME "uart1" +#define RT_USING_CONSOLE_OUTPUT_CTL +#define RT_VER_NUM 0x50301 +#define RT_BACKTRACE_LEVEL_MAX_NR 32 +/* end of RT-Thread Kernel */ +#define ARCH_TEXT_OFFSET 0x10000 +#define ARCH_RAM_OFFSET 0x40000000 +#define ARCH_HEAP_SIZE 0x800000 +#define RT_USING_CACHE +#define RT_USING_HW_ATOMIC +#define RT_USING_CPU_FFS +#define ARCH_RISCV +#define ARCH_RISCV_FPU +#define ARCH_RISCV_FPU_D +#define ARCH_RISCV32 + +/* RISC-V Architecture Configuration */ + +#define RT_USING_RISCV_NEW_COMMON +#define ARCH_RISCV_M_MODE +#define ARCH_RISCV_USING_IRQ_STACK +#define ARCH_RISCV_IRQ_STACK_SIZE 1024 +/* end of RISC-V Architecture Configuration */ + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 2048 +#define RT_MAIN_THREAD_PRIORITY 10 +#define RT_USING_MSH +#define RT_USING_FINSH +#define FINSH_USING_MSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 4096 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 +#define FINSH_USING_OPTION_COMPLETION + +/* DFS: device virtual file system */ + +#define RT_USING_DFS +#define DFS_USING_POSIX +#define DFS_USING_WORKDIR +#define DFS_FD_MAX 16 +#define RT_USING_DFS_V1 +#define DFS_FILESYSTEMS_MAX 4 +#define DFS_FILESYSTEM_TYPES_MAX 4 +#define RT_USING_DFS_ELMFAT + +/* elm-chan's FatFs, Generic FAT Filesystem Module */ + +#define RT_DFS_ELM_CODE_PAGE 437 +#define RT_DFS_ELM_WORD_ACCESS +#define RT_DFS_ELM_USE_LFN_3 +#define RT_DFS_ELM_USE_LFN 3 +#define RT_DFS_ELM_LFN_UNICODE_0 +#define RT_DFS_ELM_LFN_UNICODE 0 +#define RT_DFS_ELM_MAX_LFN 255 +#define RT_DFS_ELM_DRIVES 2 +#define RT_DFS_ELM_MAX_SECTOR_SIZE 512 +#define RT_DFS_ELM_REENTRANT +#define RT_DFS_ELM_MUTEX_TIMEOUT 3000 +#define RT_DFS_ELM_USE_EXFAT +/* end of elm-chan's FatFs, Generic FAT Filesystem Module */ +#define RT_USING_DFS_DEVFS +/* end of DFS: device virtual file system */ + +/* Device Drivers */ + +#define RT_USING_DM +#define RT_USING_DEVICE_IPC +#define RT_UNAMED_PIPE_NUMBER 64 +#define RT_USING_SYSTEM_WORKQUEUE +#define RT_SYSTEM_WORKQUEUE_STACKSIZE 2048 +#define RT_SYSTEM_WORKQUEUE_PRIORITY 23 +#define RT_USING_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_USING_DMA +#define RT_SERIAL_RB_BUFSZ 2048 +#define RT_SERIAL_8250 +#define RT_SERIAL_8250_DW +#define RT_USING_ADC +#define RT_USING_ADC_V1 +#define RT_ADC_SUNXI_GPADC +#define RT_USING_CLOCK_TIME +#define RT_CLOCK_TIMER_SUNXI +#define RT_USING_I2C +#define RT_USING_I2C_BITOPS +#define RT_I2C_SUNXI +#define RT_USING_PWM +#define RT_PWM_SUNXI +#define RT_USING_INPUT_CAPTURE +#define RT_INPUT_CAPTURE_RB_SIZE 100 +#define RT_INPUT_CAPTURE_SUNXI_IRRX +#define RT_USING_MTD_NOR +#define RT_USING_MTD_NOR_SPI +#define RT_USING_RTC +#define RT_USING_ALARM +#define RT_ALARM_STACK_SIZE 1024 +#define RT_ALARM_TIMESLICE 5 +#define RT_ALARM_PRIORITY 10 +#define RT_RTC_SUNXI +#define RT_USING_SDIO +#define RT_SDIO_STACK_SIZE 2048 +#define RT_SDIO_THREAD_PRIORITY 15 +#define RT_MMCSD_STACK_SIZE 4096 +#define RT_MMCSD_THREAD_PRIORITY 22 +#define RT_MMCSD_MAX_PARTITION 16 +#define RT_SDIO_SUNXI +#define RT_USING_SPI +#define RT_USING_SPI_ISR +#define RT_USING_QSPI +#define RT_USING_SFUD +#define RT_SFUD_USING_SFDP +#define RT_SFUD_USING_FLASH_INFO_TABLE +#define RT_SFUD_SPI_MAX_HZ 50000000 +#define RT_SPI_SUNXI +#define RT_QSPI_SUNXI +#define RT_USING_WDT +#define RT_WDT_SUNXI +#define RT_USING_AUDIO +#define RT_AUDIO_REPLAY_MP_BLOCK_SIZE 4096 +#define RT_AUDIO_REPLAY_MP_BLOCK_COUNT 2 +#define RT_AUDIO_RECORD_PIPE_SIZE 2048 +#define RT_AUDIO_SUNXI_CODEC +#define RT_AUDIO_SUNXI_I2S0 +#define RT_AUDIO_SUNXI_OWA +#define RT_USING_TOUCH +#define RT_USING_LCD +#define RT_USING_GRAPHIC +#define RT_GRAPHIC_BACKLIGHT +#define RT_GRAPHIC_BACKLIGHT_PWM +#define RT_GRAPHIC_FB +#define RT_GRAPHIC_FB_ST77916 +#define RT_GRAPHIC_FB_SUNXI_DE +#define RT_GRAPHIC_LOGO +#define RT_GRAPHIC_LOGO_RT_THREAD_CLUT224 +#define RT_USING_HWCRYPTO +#define RT_HWCRYPTO_DEFAULT_NAME "hwcryto" +#define RT_HWCRYPTO_IV_MAX_SIZE 16 +#define RT_HWCRYPTO_KEYBIT_MAX_SIZE 256 +#define RT_HWCRYPTO_USING_AES +#define RT_HWCRYPTO_USING_AES_ECB +#define RT_HWCRYPTO_USING_AES_CBC +#define RT_HWCRYPTO_USING_AES_CTR +#define RT_HWCRYPTO_USING_RNG +#define RT_HWCRYPTO_SUNXI_CE +#define RT_USING_INPUT +#define RT_INPUT_POWER +#define RT_INPUT_TOUCHSCREEN +#define RT_INPUT_TOUCHSCREEN_GOODIX +#define RT_INPUT_TOUCHSCREEN_SUNXI_TPADC +#define RT_USING_BLK + +/* Partition Types */ + +#define RT_BLK_PARTITION_DFS +#define RT_BLK_PARTITION_EFI +/* end of Partition Types */ +#define RT_USING_DVFS +#define RT_USING_DVFS_EVENT +#define RT_USING_DVFS_OPP_RETRY_MAX 10 + +/* DVFS Event Drivers */ + +/* DVFS CPUfreq Drivers */ + +#define RT_DVFS_SUN252I_F101_CPUFREQ + +/* DVFS Devfreq Drivers */ + +#define RT_USING_REGULATOR +#define RT_REGULATOR_FIXED +#define RT_USING_RESET +#define RT_RESET_SUN252I_F101 + +/* Power Management (PM) Domains device drivers */ + +/* end of Power Management (PM) Domains device drivers */ +#define RT_USING_THERMAL + +/* Thermal Sensors Drivers */ + +#define RT_THERMAL_SUNXI + +/* Thermal Cool Drivers */ + +#define RT_USING_NVMEM +#define RT_NVMEM_SUNXI_SID +#define RT_USING_DMA +#define RT_DMA_SUNXI +#define RT_USING_MFD +#define RT_MFD_SYSCON +#define RT_USING_OFW +#define RT_USING_BUILTIN_FDT +#define RT_BUILTIN_FDT_PATH "../dm/dts/sun252i-f101-yuzukineko.dtb" +#define RT_FDT_EARLYCON_MSG_SIZE 128 +#define RT_USING_OFW_BUS_RANGES_NUMBER 4 +#define RT_USING_PIC +#define MAX_HANDLERS 256 +#define RT_PIC_RISCV_INTC +#define RT_PIC_SIFIVE_PLIC +#define RT_USING_PIN +#define RT_USING_PINCTRL +#define RT_PINCTRL_SUN252I_F101 +#define RT_USING_CLK +#define RT_CLK_SUN252I_F101 +#define RT_USING_CHERRYUSB +#define RT_CHERRYUSB_DEVICE +#define RT_CHERRYUSB_DEVICE_SPEED_FS +#define RT_CHERRYUSB_DEVICE_MUSB_SUNXI +#define RT_CHERRYUSB_DEVICE_CDC_ACM +#define RT_CHERRYUSB_DEVICE_CDC_ACM_CHARDEV +#define CONFIG_USBDEV_REQUEST_BUFFER_LEN 512 +#define CONFIG_USBDEV_MSC_MAX_BUFSIZE 512 +#define RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_CHARDEV +#define RT_USB_SUNXI_MUSB + +/* SoC (System on Chip) Drivers */ + +#define RT_SOC_SUNXI_G2D +#define RT_SOC_SUNXI_JPEG_DEC +#define RT_SOC_SUNXI_JPEG_ENC +#define RT_SOC_SUNXI_PNG_DEC +/* end of SoC (System on Chip) Drivers */ +/* end of Device Drivers */ + +/* C/C++ and POSIX layer */ + +/* ISO-ANSI C layer */ + +/* Timezone and Daylight Saving Time */ + +#define RT_LIBC_USING_LIGHT_TZ_DST +#define RT_LIBC_TZ_DEFAULT_HOUR 8 +#define RT_LIBC_TZ_DEFAULT_MIN 0 +#define RT_LIBC_TZ_DEFAULT_SEC 0 +/* end of Timezone and Daylight Saving Time */ +/* end of ISO-ANSI C layer */ + +/* POSIX (Portable Operating System Interface) layer */ + + +/* Interprocess Communication (IPC) */ + + +/* Socket is in the 'Network' category */ + +/* end of Interprocess Communication (IPC) */ +/* end of POSIX (Portable Operating System Interface) layer */ +/* end of C/C++ and POSIX layer */ + +/* Network */ + +/* end of Network */ + +/* Memory protection */ + +/* end of Memory protection */ + +/* Utilities */ + +#define RT_USING_ADT +#define RT_USING_ADT_AVL +#define RT_USING_ADT_BITMAP +#define RT_USING_ADT_HASHMAP +#define RT_USING_ADT_REF +/* end of Utilities */ + +/* Using USB legacy version */ + +/* end of Using USB legacy version */ +/* end of RT-Thread Components */ + +/* RT-Thread Utestcases */ + +/* end of RT-Thread Utestcases */ + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + +/* end of Marvell WiFi */ + +/* Wiced WiFi */ + +/* end of Wiced WiFi */ + +/* CYW43012 WiFi */ + +/* end of CYW43012 WiFi */ + +/* BL808 WiFi */ + +/* end of BL808 WiFi */ + +/* CYW43439 WiFi */ + +/* end of CYW43439 WiFi */ +/* end of Wi-Fi */ + +/* IoT Cloud */ + +/* end of IoT Cloud */ +/* end of IoT - internet of things */ + +/* security packages */ + +/* end of security packages */ + +/* language packages */ + +/* JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* end of JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* XML: Extensible Markup Language */ + +/* end of XML: Extensible Markup Language */ +/* end of language packages */ + +/* multimedia packages */ + +/* LVGL: powerful and easy-to-use embedded GUI library */ + +/* end of LVGL: powerful and easy-to-use embedded GUI library */ + +/* u8g2: a monochrome graphic library */ + +/* end of u8g2: a monochrome graphic library */ +/* end of multimedia packages */ + +/* tools packages */ + +/* end of tools packages */ + +/* system packages */ + +/* enhanced kernel services */ + +/* end of enhanced kernel services */ + +/* acceleration: Assembly language or algorithmic acceleration packages */ + +/* end of acceleration: Assembly language or algorithmic acceleration packages */ + +/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* Micrium: Micrium software products porting for RT-Thread */ + +/* end of Micrium: Micrium software products porting for RT-Thread */ +/* end of system packages */ + +/* peripheral libraries and drivers */ + +/* HAL & SDK Drivers */ + +/* STM32 HAL & SDK Drivers */ + +/* end of STM32 HAL & SDK Drivers */ + +/* Infineon HAL Packages */ + +/* end of Infineon HAL Packages */ + +/* Kendryte SDK */ + +/* end of Kendryte SDK */ + +/* MM32 HAL & SDK Drivers */ + +/* end of MM32 HAL & SDK Drivers */ + +/* WCH HAL & SDK Drivers */ + +/* end of WCH HAL & SDK Drivers */ + +/* AT32 HAL & SDK Drivers */ + +/* end of AT32 HAL & SDK Drivers */ + +/* HC32 DDL Drivers */ + +/* end of HC32 DDL Drivers */ + +/* NXP HAL & SDK Drivers */ + +/* end of NXP HAL & SDK Drivers */ + +/* NUVOTON Drivers */ + +/* end of NUVOTON Drivers */ + +/* GD32 Drivers */ + +/* end of GD32 Drivers */ + +/* HPMicro SDK */ + +/* end of HPMicro SDK */ + +/* FT32 HAL & SDK Drivers */ + +/* end of FT32 HAL & SDK Drivers */ + +/* NOVOSNS Drivers */ + +/* end of NOVOSNS Drivers */ + +/* N32 Drivers */ + +/* end of N32 Drivers */ +/* end of HAL & SDK Drivers */ + +/* sensors drivers */ + +/* end of sensors drivers */ + +/* touch drivers */ + +/* end of touch drivers */ +/* end of peripheral libraries and drivers */ + +/* AI packages */ + +/* end of AI packages */ + +/* Signal Processing and Control Algorithm Packages */ + +/* end of Signal Processing and Control Algorithm Packages */ + +/* miscellaneous packages */ + +/* project laboratory */ + +/* end of project laboratory */ + +/* samples: kernel and components samples */ + +/* end of samples: kernel and components samples */ + +/* entertainment: terminal games and other interesting software packages */ + +/* end of entertainment: terminal games and other interesting software packages */ +/* end of miscellaneous packages */ + +/* Arduino libraries */ + + +/* Projects and Demos */ + +/* end of Projects and Demos */ + +/* Sensors */ + +/* end of Sensors */ + +/* Display */ + +/* end of Display */ + +/* Timing */ + +/* end of Timing */ + +/* Data Processing */ + +/* end of Data Processing */ + +/* Data Storage */ + +/* Communication */ + +/* end of Communication */ + +/* Device Control */ + +/* end of Device Control */ + +/* Other */ + +/* end of Other */ + +/* Signal IO */ + +/* end of Signal IO */ + +/* Uncategorized */ + +/* end of Arduino libraries */ +/* end of RT-Thread online packages */ +#define SOC_ALLWINNER_F101 +#define SBC_YUZUKINEKO + +/* YuzukiNeko SBC Configuration */ + +#define SBC_YUZUKINEKO_MMODE +#define SBC_YUZUKINEKO_ST77916 +/* end of YuzukiNeko SBC Configuration */ + +#endif diff --git a/bsp/allwinner/f101/rtconfig.py b/bsp/allwinner/f101/rtconfig.py new file mode 100644 index 000000000000..61203faa48ef --- /dev/null +++ b/bsp/allwinner/f101/rtconfig.py @@ -0,0 +1,47 @@ +import os + +# An RV32 GNU toolchain is selected through RTT_EXEC_PATH/RTT_CC_PREFIX. +ARCH = 'risc-v' +CPU = '' +VENDOR = 't-head' +CROSS_TOOL = 'gcc' +PLATFORM = 'gcc' +BUILD = 'debug' + +RTT_ROOT = os.getenv('RTT_ROOT') or os.path.normpath( + os.path.join(os.getcwd(), '..', '..', '..')) +EXEC_PATH = os.getenv('RTT_EXEC_PATH') or '' +PREFIX = os.getenv('RTT_CC_PREFIX') or 'riscv64-unknown-elf-' + +CC = PREFIX + 'gcc' +CXX = PREFIX + 'g++' +CPP = PREFIX + 'cpp' +AS = PREFIX + 'gcc' +AR = PREFIX + 'ar' +LINK = PREFIX + 'gcc' +SIZE = PREFIX + 'size' +OBJDUMP = PREFIX + 'objdump' +OBJCPY = PREFIX + 'objcopy' +TARGET_EXT = 'elf' + +DEVICE = ' -mcmodel=medany -march=rv32imafdc -mabi=ilp32d ' +CPPFLAGS = ' -nostdinc -undef -E -P -x assembler-with-cpp' +CFLAGS = DEVICE + \ + '-ffreestanding -flax-vector-conversions -Wno-cpp -fno-common ' \ + '-ffunction-sections -fdata-sections -fstrict-volatile-bitfields ' +AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ ' +LFLAGS = DEVICE + \ + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,_start ' \ + '-T link.lds -lsupc++ -lgcc -static' + +if BUILD == 'debug': + CFLAGS += ' -O0 -ggdb -fvar-tracking' + AFLAGS += ' -ggdb' +else: + CFLAGS += ' -O2 -Os' + +CXXFLAGS = CFLAGS + +DUMP_ACTION = OBJDUMP + ' -D -S $TARGET > rtthread.asm\n' +POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' + \ + SIZE + ' $TARGET\n' diff --git a/bsp/allwinner/f101/smode-bare.config b/bsp/allwinner/f101/smode-bare.config new file mode 100644 index 000000000000..106f8321779c --- /dev/null +++ b/bsp/allwinner/f101/smode-bare.config @@ -0,0 +1,1844 @@ + +# +# RT-Thread Kernel +# + +# +# klibc options +# + +# +# rt_vsnprintf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set +# CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG is not set +# CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD is not set +# end of rt_vsnprintf options + +# +# rt_vsscanf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSSCANF is not set +# end of rt_vsscanf options + +# +# rt_memset options +# +# CONFIG_RT_KLIBC_USING_USER_MEMSET is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMSET is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMSET is not set +# end of rt_memset options + +# +# rt_memcpy options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMCPY is not set +# end of rt_memcpy options + +# +# rt_memmove options +# +# CONFIG_RT_KLIBC_USING_USER_MEMMOVE is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMMOVE is not set +# end of rt_memmove options + +# +# rt_memcmp options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCMP is not set +# end of rt_memcmp options + +# +# rt_strstr options +# +# CONFIG_RT_KLIBC_USING_USER_STRSTR is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRSTR is not set +# end of rt_strstr options + +# +# rt_strcasecmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCASECMP is not set +# end of rt_strcasecmp options + +# +# rt_strncpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCPY is not set +# end of rt_strncpy options + +# +# rt_strcpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCPY is not set +# end of rt_strcpy options + +# +# rt_strncmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCMP is not set +# end of rt_strncmp options + +# +# rt_strcmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCMP is not set +# end of rt_strcmp options + +# +# rt_strlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRLEN is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRLEN is not set +# end of rt_strlen options + +# +# rt_strnlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRNLEN is not set +# end of rt_strnlen options +# end of klibc options + +CONFIG_RT_NAME_MAX=24 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +# CONFIG_RT_USING_NANO is not set +# CONFIG_RT_USING_AMP is not set +# CONFIG_RT_USING_SMP is not set +CONFIG_RT_CPUS_NR=1 +CONFIG_RT_ALIGN_SIZE=8 +# CONFIG_RT_THREAD_PRIORITY_8 is not set +CONFIG_RT_THREAD_PRIORITY_32=y +# CONFIG_RT_THREAD_PRIORITY_256 is not set +CONFIG_RT_THREAD_PRIORITY_MAX=32 +CONFIG_RT_TICK_PER_SECOND=1000 +CONFIG_RT_USING_OVERFLOW_CHECK=y +CONFIG_RT_USING_HOOK=y +CONFIG_RT_HOOK_USING_FUNC_PTR=y +# CONFIG_RT_USING_HOOKLIST is not set +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=1024 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=2048 +# CONFIG_RT_USING_TIMER_ALL_SOFT is not set +# CONFIG_RT_USING_CPU_USAGE_TRACER is not set + +# +# kservice options +# +# CONFIG_RT_USING_TINY_FFS is not set +# end of kservice options + +CONFIG_RT_USING_DEBUG=y +CONFIG_RT_DEBUGING_ASSERT=y +CONFIG_RT_DEBUGING_COLOR=y +# CONFIG_RT_DEBUGING_CONTEXT is not set +# CONFIG_RT_DEBUGING_AUTO_INIT is not set +# CONFIG_RT_USING_CI_ACTION is not set + +# +# Inter-Thread communication +# +CONFIG_RT_USING_SEMAPHORE=y +CONFIG_RT_USING_MUTEX=y +CONFIG_RT_USING_EVENT=y +CONFIG_RT_USING_MAILBOX=y +CONFIG_RT_USING_MESSAGEQUEUE=y +# CONFIG_RT_USING_MESSAGEQUEUE_PRIORITY is not set +# CONFIG_RT_USING_SIGNALS is not set +# end of Inter-Thread communication + +# +# Memory Management +# +CONFIG_RT_USING_MEMPOOL=y +CONFIG_RT_USING_SMALL_MEM=y +# CONFIG_RT_USING_SLAB is not set +# CONFIG_RT_USING_MEMHEAP is not set +CONFIG_RT_USING_SMALL_MEM_AS_HEAP=y +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +# CONFIG_RT_USING_SLAB_AS_HEAP is not set +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +# CONFIG_RT_USING_MEMTRACE is not set +# CONFIG_RT_USING_HEAP_ISR is not set +CONFIG_RT_USING_HEAP=y +# end of Memory Management + +CONFIG_RT_USING_DEVICE=y +# CONFIG_RT_USING_DEVICE_OPS is not set +CONFIG_RT_USING_INTERRUPT_INFO=y +# CONFIG_RT_USING_THREADSAFE_PRINTF is not set +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=256 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart1" +CONFIG_RT_USING_CONSOLE_OUTPUT_CTL=y +CONFIG_RT_VER_NUM=0x50301 +# CONFIG_RT_USING_STDC_ATOMIC is not set +CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32 +# end of RT-Thread Kernel + +CONFIG_ARCH_TEXT_OFFSET=0xc0000 +CONFIG_ARCH_RAM_OFFSET=0x40000000 +CONFIG_ARCH_HEAP_SIZE=0x740000 +CONFIG_RT_USING_CACHE=y +CONFIG_RT_USING_HW_ATOMIC=y +CONFIG_RT_USING_CPU_FFS=y +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_RISCV_FPU=y +CONFIG_ARCH_RISCV_FPU_D=y +CONFIG_ARCH_RISCV32=y + +# +# RISC-V Architecture Configuration +# +CONFIG_RT_USING_RISCV_NEW_COMMON=y +CONFIG_ARCH_RISCV_USING_IRQ_STACK=y +CONFIG_ARCH_RISCV_IRQ_STACK_SIZE=1024 +# end of RISC-V Architecture Configuration + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=2048 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=4096 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=5 +# CONFIG_FINSH_USING_WORD_OPERATION is not set +# CONFIG_FINSH_USING_FUNC_EXT is not set +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 +CONFIG_FINSH_USING_OPTION_COMPLETION=y + +# +# DFS: device virtual file system +# +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +# CONFIG_RT_USING_DFS_MNTTABLE is not set +CONFIG_DFS_FD_MAX=16 +CONFIG_RT_USING_DFS_V1=y +# CONFIG_RT_USING_DFS_V2 is not set +CONFIG_DFS_FILESYSTEMS_MAX=4 +CONFIG_DFS_FILESYSTEM_TYPES_MAX=4 +CONFIG_RT_USING_DFS_ELMFAT=y + +# +# elm-chan's FatFs, Generic FAT Filesystem Module +# +CONFIG_RT_DFS_ELM_CODE_PAGE=437 +CONFIG_RT_DFS_ELM_WORD_ACCESS=y +# CONFIG_RT_DFS_ELM_USE_LFN_0 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_1 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set +CONFIG_RT_DFS_ELM_USE_LFN_3=y +CONFIG_RT_DFS_ELM_USE_LFN=3 +CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y +# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set +CONFIG_RT_DFS_ELM_LFN_UNICODE=0 +CONFIG_RT_DFS_ELM_MAX_LFN=255 +CONFIG_RT_DFS_ELM_DRIVES=2 +CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512 +# CONFIG_RT_DFS_ELM_USE_ERASE is not set +CONFIG_RT_DFS_ELM_REENTRANT=y +CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000 +CONFIG_RT_DFS_ELM_USE_EXFAT=y +# end of elm-chan's FatFs, Generic FAT Filesystem Module + +CONFIG_RT_USING_DFS_DEVFS=y +# CONFIG_RT_USING_DFS_ROMFS is not set +# CONFIG_RT_USING_DFS_CROMFS is not set +# CONFIG_RT_USING_DFS_RAMFS is not set +# CONFIG_RT_USING_DFS_TMPFS is not set +# CONFIG_RT_USING_DFS_MQUEUE is not set +# end of DFS: device virtual file system + +# CONFIG_RT_USING_FAL is not set + +# +# Device Drivers +# +CONFIG_RT_USING_DM=y +# CONFIG_RT_USING_DEV_BUS is not set +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_UNAMED_PIPE_NUMBER=64 +CONFIG_RT_USING_SYSTEM_WORKQUEUE=y +CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=2048 +CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +CONFIG_RT_SERIAL_USING_DMA=y +CONFIG_RT_SERIAL_RB_BUFSZ=2048 +# CONFIG_RT_USING_SERIAL_BYPASS is not set +# CONFIG_RT_SERIAL_EARLY_HVC is not set +# CONFIG_RT_SERIAL_PL011 is not set +CONFIG_RT_SERIAL_8250=y +CONFIG_RT_SERIAL_8250_DW=y +CONFIG_RT_SERIAL_EARLY_SBI=y +# CONFIG_RT_USING_CAN is not set +CONFIG_RT_USING_ADC=y +# CONFIG_RT_USING_ADC_V2 is not set +CONFIG_RT_USING_ADC_V1=y +CONFIG_RT_ADC_SUNXI_GPADC=y +CONFIG_RT_USING_CLOCK_TIME=y +# CONFIG_RT_USING_CLOCK_TIMER_TRIGGER is not set +CONFIG_RT_CLOCK_TIMER_SUNXI=y +CONFIG_RT_USING_I2C=y +# CONFIG_RT_I2C_DEBUG is not set +CONFIG_RT_USING_I2C_BITOPS=y +# CONFIG_RT_I2C_BITOPS_DEBUG is not set +# CONFIG_RT_USING_SOFT_I2C is not set +CONFIG_RT_I2C_SUNXI=y +# CONFIG_RT_USING_PHY is not set +# CONFIG_RT_USING_PHY_V2 is not set +# CONFIG_RT_USING_DAC is not set +# CONFIG_RT_USING_NULL is not set +# CONFIG_RT_USING_ZERO is not set +# CONFIG_RT_USING_RANDOM is not set +CONFIG_RT_USING_PWM=y +CONFIG_RT_PWM_SUNXI=y +# CONFIG_RT_USING_PULSE_ENCODER is not set +CONFIG_RT_USING_INPUT_CAPTURE=y +CONFIG_RT_INPUT_CAPTURE_RB_SIZE=100 +CONFIG_RT_INPUT_CAPTURE_SUNXI_IRRX=y +CONFIG_RT_USING_MTD_NOR=y +# CONFIG_RT_USING_MTD_NOR_CFI is not set +CONFIG_RT_USING_MTD_NOR_SPI=y +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_PM is not set +CONFIG_RT_USING_RTC=y +CONFIG_RT_USING_ALARM=y +CONFIG_RT_ALARM_STACK_SIZE=1024 +CONFIG_RT_ALARM_TIMESLICE=5 +CONFIG_RT_ALARM_PRIORITY=10 +# CONFIG_RT_ALARM_USING_LOCAL_TIME is not set +# CONFIG_RT_USING_SOFT_RTC is not set +# CONFIG_RT_RTC_DS1302 is not set +# CONFIG_RT_RTC_DS1307 is not set +# CONFIG_RT_RTC_GOLDFISH is not set +# CONFIG_RT_RTC_HYM8563 is not set +# CONFIG_RT_RTC_PCF8523 is not set +# CONFIG_RT_RTC_PCF8563 is not set +# CONFIG_RT_RTC_PL031 is not set +# CONFIG_RT_RTC_RX8010 is not set +CONFIG_RT_RTC_SUNXI=y +CONFIG_RT_USING_SDIO=y +CONFIG_RT_SDIO_STACK_SIZE=2048 +CONFIG_RT_SDIO_THREAD_PRIORITY=15 +CONFIG_RT_MMCSD_STACK_SIZE=4096 +CONFIG_RT_MMCSD_THREAD_PRIORITY=22 +CONFIG_RT_MMCSD_MAX_PARTITION=16 +# CONFIG_RT_SDIO_DEBUG is not set +# CONFIG_RT_USING_SDHCI is not set +# CONFIG_RT_SDIO_DW_MMC is not set +# CONFIG_RT_SDIO_SDHCI_DWCMSHC is not set +CONFIG_RT_SDIO_SUNXI=y +CONFIG_RT_USING_SPI=y +CONFIG_RT_USING_SPI_ISR=y +# CONFIG_RT_USING_SPI_BITOPS is not set +# CONFIG_RT_USING_SOFT_SPI is not set +CONFIG_RT_USING_QSPI=y +# CONFIG_RT_USING_SPI_MSD is not set +CONFIG_RT_USING_SFUD=y +CONFIG_RT_SFUD_USING_SFDP=y +CONFIG_RT_SFUD_USING_FLASH_INFO_TABLE=y +# CONFIG_RT_SFUD_USING_QSPI is not set +CONFIG_RT_SFUD_SPI_MAX_HZ=50000000 +# CONFIG_RT_DEBUG_SFUD is not set +# CONFIG_RT_USING_ENC28J60 is not set +# CONFIG_RT_USING_SPI_WIFI is not set +CONFIG_RT_SPI_SUNXI=y +CONFIG_RT_QSPI_SUNXI=y +CONFIG_RT_USING_WDT=y +# CONFIG_RT_WDT_DW is not set +CONFIG_RT_WDT_SUNXI=y +CONFIG_RT_USING_AUDIO=y +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_SIZE=4096 +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_COUNT=2 +CONFIG_RT_AUDIO_RECORD_PIPE_SIZE=2048 +CONFIG_RT_AUDIO_SUNXI_CODEC=y +CONFIG_RT_AUDIO_SUNXI_I2S0=y +CONFIG_RT_AUDIO_SUNXI_OWA=y +# CONFIG_RT_USING_SENSOR is not set +CONFIG_RT_USING_TOUCH=y +# CONFIG_RT_TOUCH_PIN_IRQ is not set +CONFIG_RT_USING_LCD=y +CONFIG_RT_USING_GRAPHIC=y +CONFIG_RT_GRAPHIC_BACKLIGHT=y +# CONFIG_RT_GRAPHIC_BACKLIGHT_GPIO is not set +CONFIG_RT_GRAPHIC_BACKLIGHT_PWM=y +CONFIG_RT_GRAPHIC_FB=y +# CONFIG_RT_GRAPHIC_FB_ILI9486 is not set +CONFIG_RT_GRAPHIC_FB_ST77916=y +# CONFIG_RT_GRAPHIC_FB_SIMPLE is not set +CONFIG_RT_GRAPHIC_FB_SUNXI_DE=y +CONFIG_RT_GRAPHIC_LOGO=y +# CONFIG_RT_GRAPHIC_LOGO_NONE is not set +CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_CLUT224=y +# CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_WHITE_CLUT224 is not set +CONFIG_RT_USING_HWCRYPTO=y +CONFIG_RT_HWCRYPTO_DEFAULT_NAME="hwcryto" +CONFIG_RT_HWCRYPTO_IV_MAX_SIZE=16 +CONFIG_RT_HWCRYPTO_KEYBIT_MAX_SIZE=256 +# CONFIG_RT_HWCRYPTO_USING_GCM is not set +CONFIG_RT_HWCRYPTO_USING_AES=y +CONFIG_RT_HWCRYPTO_USING_AES_ECB=y +CONFIG_RT_HWCRYPTO_USING_AES_CBC=y +# CONFIG_RT_HWCRYPTO_USING_AES_CFB is not set +CONFIG_RT_HWCRYPTO_USING_AES_CTR=y +# CONFIG_RT_HWCRYPTO_USING_AES_OFB is not set +# CONFIG_RT_HWCRYPTO_USING_DES is not set +# CONFIG_RT_HWCRYPTO_USING_3DES is not set +# CONFIG_RT_HWCRYPTO_USING_RC4 is not set +# CONFIG_RT_HWCRYPTO_USING_MD5 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA1 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA2 is not set +CONFIG_RT_HWCRYPTO_USING_RNG=y +# CONFIG_RT_HWCRYPTO_USING_CRC is not set +# CONFIG_RT_HWCRYPTO_USING_BIGNUM is not set +CONFIG_RT_HWCRYPTO_SUNXI_CE=y +# CONFIG_RT_USING_WIFI is not set +# CONFIG_RT_USING_PTP is not set +# CONFIG_RT_USING_LED is not set +CONFIG_RT_USING_INPUT=y +CONFIG_RT_INPUT_POWER=y +# CONFIG_RT_INPUT_JOYSTICK is not set +# CONFIG_RT_INPUT_KEYBOARD is not set +# CONFIG_RT_INPUT_MISC is not set +CONFIG_RT_INPUT_TOUCHSCREEN=y +# CONFIG_RT_INPUT_TOUCHSCREEN_ADS7846 is not set +CONFIG_RT_INPUT_TOUCHSCREEN_GOODIX=y +CONFIG_RT_INPUT_TOUCHSCREEN_SUNXI_TPADC=y +# CONFIG_RT_USING_MBOX is not set +# CONFIG_RT_USING_HWSPINLOCK is not set +# CONFIG_RT_USING_RPMSG is not set +# CONFIG_RT_USING_PHYE is not set +# CONFIG_RT_USING_ATA is not set +# CONFIG_RT_USING_NVME is not set +CONFIG_RT_USING_BLK=y + +# +# Partition Types +# +CONFIG_RT_BLK_PARTITION_DFS=y +CONFIG_RT_BLK_PARTITION_EFI=y +# end of Partition Types + +# CONFIG_RT_USING_SCSI is not set +# CONFIG_RT_USING_FIRMWARE is not set +# CONFIG_RT_USING_TEE is not set +# CONFIG_RT_USING_HWCACHE is not set +CONFIG_RT_USING_DVFS=y +CONFIG_RT_USING_DVFS_EVENT=y +CONFIG_RT_USING_DVFS_OPP_RETRY_MAX=10 + +# +# DVFS Event Drivers +# + +# +# DVFS CPUfreq Drivers +# +CONFIG_RT_DVFS_SUN252I_F101_CPUFREQ=y + +# +# DVFS Devfreq Drivers +# +CONFIG_RT_USING_REGULATOR=y +# CONFIG_RT_REGULATOR_FAN53555 is not set +CONFIG_RT_REGULATOR_FIXED=y +# CONFIG_RT_REGULATOR_GPIO is not set +# CONFIG_RT_REGULATOR_PWM is not set +CONFIG_RT_USING_RESET=y +# CONFIG_RT_RESET_SIMPLE is not set +CONFIG_RT_RESET_SUN252I_F101=y + +# +# Power Management (PM) Domains device drivers +# +# end of Power Management (PM) Domains device drivers + +# CONFIG_RT_USING_POWER_RESET is not set +# CONFIG_RT_USING_POWER_SUPPLY is not set +CONFIG_RT_USING_THERMAL=y + +# +# Thermal Sensors Drivers +# +CONFIG_RT_THERMAL_SUNXI=y + +# +# Thermal Cool Drivers +# +# CONFIG_RT_THERMAL_COOL_DVFS is not set +# CONFIG_RT_THERMAL_COOL_GPIO_FAN is not set +# CONFIG_RT_THERMAL_COOL_PWM_FAN is not set +# CONFIG_RT_USING_VIRTIO is not set +CONFIG_RT_USING_NVMEM=y +CONFIG_RT_NVMEM_SUNXI_SID=y +CONFIG_RT_USING_DMA=y +# CONFIG_RT_DMA_PL330 is not set +# CONFIG_RT_DMA_DW_AXI_DMAC is not set +CONFIG_RT_DMA_SUNXI=y +CONFIG_RT_USING_MFD=y +CONFIG_RT_MFD_SYSCON=y +CONFIG_RT_USING_OFW=y +CONFIG_RT_USING_BUILTIN_FDT=y +CONFIG_RT_BUILTIN_FDT_PATH="../dm/dts/sun252i-f101-yuzukineko.dtb" +CONFIG_RT_FDT_EARLYCON_MSG_SIZE=128 +CONFIG_RT_USING_OFW_BUS_RANGES_NUMBER=4 +# CONFIG_RT_USING_PCI is not set +CONFIG_RT_USING_PIC=y +# CONFIG_RT_USING_PIC_STATISTICS is not set +CONFIG_MAX_HANDLERS=256 +# CONFIG_RT_PIC_ARM_GIC is not set +# CONFIG_RT_PIC_ARM_GIC_V3 is not set +# CONFIG_RT_PIC_RISCV_AIA is not set +# CONFIG_RT_PIC_RISCV_CLIC is not set +CONFIG_RT_PIC_RISCV_INTC=y +CONFIG_RT_PIC_SIFIVE_PLIC=y +CONFIG_RT_USING_PIN=y +# CONFIG_RT_PIN_PL061 is not set +CONFIG_RT_USING_PINCTRL=y +# CONFIG_RT_PINCTRL_SINGLE is not set +CONFIG_RT_PINCTRL_SUN252I_F101=y +CONFIG_RT_USING_CLK=y +CONFIG_RT_CLK_SUN252I_F101=y +CONFIG_RT_USING_CHERRYUSB=y +CONFIG_RT_CHERRYUSB_DEVICE=y +CONFIG_RT_CHERRYUSB_DEVICE_SPEED_FS=y +# CONFIG_RT_CHERRYUSB_DEVICE_SPEED_HS is not set +# CONFIG_RT_CHERRYUSB_DEVICE_SPEED_AUTO is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_FSDEV_ST is not set +# CONFIG_RT_CHERRYUSB_DEVICE_FSDEV_NATION is not set +# CONFIG_RT_CHERRYUSB_DEVICE_FSDEV_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_ST is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_ESP is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_KENDRYTE is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_INFINEON is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_AT is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_HC is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_NATION is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_GD is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DWC2_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MUSB_ES is not set +CONFIG_RT_CHERRYUSB_DEVICE_MUSB_SUNXI=y +# CONFIG_RT_CHERRYUSB_DEVICE_MUSB_BK is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MUSB_SIFLI is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MUSB_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CHIPIDEA_MCX is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CHIPIDEA_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_KINETIS_MCX is not set +# CONFIG_RT_CHERRYUSB_DEVICE_KINETIS_MM32 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_KINETIS_CUSTOM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_BL is not set +# CONFIG_RT_CHERRYUSB_DEVICE_HPM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_AIC is not set +# CONFIG_RT_CHERRYUSB_DEVICE_RP2040 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CH32 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_PUSB2 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_NRF5X is not set +CONFIG_RT_CHERRYUSB_DEVICE_CDC_ACM=y +# CONFIG_RT_CHERRYUSB_DEVICE_HID is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MSC is not set +# CONFIG_RT_CHERRYUSB_DEVICE_AUDIO is not set +# CONFIG_RT_CHERRYUSB_DEVICE_VIDEO is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CDC_RNDIS is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CDC_ECM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_CDC_NCM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_MTP is not set +# CONFIG_RT_CHERRYUSB_DEVICE_ADB is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DFU is not set +# CONFIG_RT_CHERRYUSB_DEVICE_DISPLAY is not set +CONFIG_RT_CHERRYUSB_DEVICE_CDC_ACM_CHARDEV=y +# CONFIG_RT_CHERRYUSB_DEVICE_MSC_BLKDEV is not set +CONFIG_CONFIG_USBDEV_REQUEST_BUFFER_LEN=512 +CONFIG_CONFIG_USBDEV_MSC_MAX_BUFSIZE=512 +# CONFIG_CONFIG_USBDEV_RNDIS_USING_LWIP is not set +# CONFIG_CONFIG_USBDEV_CDC_ECM_USING_LWIP is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_NONE is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV1 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2 is not set +# CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2_CDC is not set +CONFIG_RT_CHERRYUSB_DEVICE_TEMPLATE_CDC_ACM_CHARDEV=y +# CONFIG_RT_CHERRYUSB_HOST is not set +CONFIG_RT_USB_SUNXI_MUSB=y + +# +# SoC (System on Chip) Drivers +# +CONFIG_RT_SOC_SUNXI_G2D=y +CONFIG_RT_SOC_SUNXI_JPEG_DEC=y +CONFIG_RT_SOC_SUNXI_JPEG_ENC=y +CONFIG_RT_SOC_SUNXI_PNG_DEC=y +# end of SoC (System on Chip) Drivers +# end of Device Drivers + +# +# C/C++ and POSIX layer +# + +# +# ISO-ANSI C layer +# + +# +# Timezone and Daylight Saving Time +# +# CONFIG_RT_LIBC_USING_FULL_TZ_DST is not set +CONFIG_RT_LIBC_USING_LIGHT_TZ_DST=y +CONFIG_RT_LIBC_TZ_DEFAULT_HOUR=8 +CONFIG_RT_LIBC_TZ_DEFAULT_MIN=0 +CONFIG_RT_LIBC_TZ_DEFAULT_SEC=0 +# end of Timezone and Daylight Saving Time +# end of ISO-ANSI C layer + +# +# POSIX (Portable Operating System Interface) layer +# +# CONFIG_RT_USING_POSIX_FS is not set +# CONFIG_RT_USING_POSIX_DELAY is not set +# CONFIG_RT_USING_POSIX_CLOCK is not set +# CONFIG_RT_USING_POSIX_TIMER is not set +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +# CONFIG_RT_USING_POSIX_PIPE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# end of Interprocess Communication (IPC) +# end of POSIX (Portable Operating System Interface) layer + +# CONFIG_RT_USING_CPLUSPLUS is not set +# end of C/C++ and POSIX layer + +# +# Network +# +# CONFIG_RT_USING_SAL is not set +# CONFIG_RT_USING_NETDEV is not set +# CONFIG_RT_USING_LWIP is not set +# CONFIG_RT_USING_AT is not set +# end of Network + +# +# Memory protection +# +# CONFIG_RT_USING_MEM_PROTECTION is not set +# CONFIG_RT_USING_HW_STACK_GUARD is not set +# end of Memory protection + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +# CONFIG_RT_USING_UTEST is not set +# CONFIG_RT_USING_VAR_EXPORT is not set +# CONFIG_RT_USING_RESOURCE_ID is not set +CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y +CONFIG_RT_USING_ADT_BITMAP=y +CONFIG_RT_USING_ADT_HASHMAP=y +CONFIG_RT_USING_ADT_REF=y +# CONFIG_RT_USING_RT_LINK is not set +# end of Utilities + +# +# Using USB legacy version +# +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set +# end of Using USB legacy version + +# CONFIG_RT_USING_FDT is not set +# CONFIG_RT_USING_RUST is not set +# end of RT-Thread Components + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set +# end of RT-Thread Utestcases + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LORAWAN_DRIVER is not set +# CONFIG_PKG_USING_PAHOMQTT is not set +# CONFIG_PKG_USING_UMQTT is not set +# CONFIG_PKG_USING_WEBCLIENT is not set +# CONFIG_PKG_USING_WEBNET is not set +# CONFIG_PKG_USING_MONGOOSE is not set +# CONFIG_PKG_USING_MYMQTT is not set +# CONFIG_PKG_USING_KAWAII_MQTT is not set +# CONFIG_PKG_USING_BC28_MQTT is not set +# CONFIG_PKG_USING_WEBTERMINAL is not set +# CONFIG_PKG_USING_FREEMODBUS is not set +# CONFIG_PKG_USING_NANOPB is not set +# CONFIG_PKG_USING_WIFI_HOST_DRIVER is not set +# CONFIG_PKG_USING_ESP_HOSTED is not set + +# +# Wi-Fi +# + +# +# Marvell WiFi +# +# CONFIG_PKG_USING_WLANMARVELL is not set +# end of Marvell WiFi + +# +# Wiced WiFi +# +# CONFIG_PKG_USING_WLAN_WICED is not set +# end of Wiced WiFi + +# CONFIG_PKG_USING_RW007 is not set + +# +# CYW43012 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43012 is not set +# end of CYW43012 WiFi + +# +# BL808 WiFi +# +# CONFIG_PKG_USING_WLAN_BL808 is not set +# end of BL808 WiFi + +# +# CYW43439 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43439 is not set +# end of CYW43439 WiFi +# end of Wi-Fi + +# CONFIG_PKG_USING_COAP is not set +# CONFIG_PKG_USING_NOPOLL is not set +# CONFIG_PKG_USING_NETUTILS is not set +# CONFIG_PKG_USING_CMUX is not set +# CONFIG_PKG_USING_PPP_DEVICE is not set +# CONFIG_PKG_USING_AT_DEVICE is not set +# CONFIG_PKG_USING_ATSRV_SOCKET is not set +# CONFIG_PKG_USING_WIZNET is not set +# CONFIG_PKG_USING_ZB_COORDINATOR is not set + +# +# IoT Cloud +# +# CONFIG_PKG_USING_ONENET is not set +# CONFIG_PKG_USING_GAGENT_CLOUD is not set +# CONFIG_PKG_USING_ALI_IOTKIT is not set +# CONFIG_PKG_USING_AZURE is not set +# CONFIG_PKG_USING_TENCENT_IOT_EXPLORER is not set +# CONFIG_PKG_USING_JIOT-C-SDK is not set +# CONFIG_PKG_USING_UCLOUD_IOT_SDK is not set +# CONFIG_PKG_USING_JOYLINK is not set +# CONFIG_PKG_USING_IOTSHARP_SDK is not set +# end of IoT Cloud + +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER is not set +# CONFIG_PKG_USING_OTA_DOWNLOADER is not set +# CONFIG_PKG_USING_IPMSG is not set +# CONFIG_PKG_USING_LSSDP is not set +# CONFIG_PKG_USING_AIRKISS_OPEN is not set +# CONFIG_PKG_USING_LIBRWS is not set +# CONFIG_PKG_USING_TCPSERVER is not set +# CONFIG_PKG_USING_PROTOBUF_C is not set +# CONFIG_PKG_USING_DLT645 is not set +# CONFIG_PKG_USING_QXWZ is not set +# CONFIG_PKG_USING_SMTP_CLIENT is not set +# CONFIG_PKG_USING_ABUP_FOTA is not set +# CONFIG_PKG_USING_LIBCURL2RTT is not set +# CONFIG_PKG_USING_CAPNP is not set +# CONFIG_PKG_USING_ERPC is not set +# CONFIG_PKG_USING_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB is not set +# CONFIG_PKG_USING_PDULIB is not set +# CONFIG_PKG_USING_BTSTACK is not set +# CONFIG_PKG_USING_BT_CYW43012 is not set +# CONFIG_PKG_USING_CYW43XX is not set +# CONFIG_PKG_USING_LORAWAN_ED_STACK is not set +# CONFIG_PKG_USING_WAYZ_IOTKIT is not set +# CONFIG_PKG_USING_MAVLINK is not set +# CONFIG_PKG_USING_BSAL is not set +# CONFIG_PKG_USING_AGILE_MODBUS is not set +# CONFIG_PKG_USING_AGILE_FTP is not set +# CONFIG_PKG_USING_EMBEDDEDPROTO is not set +# CONFIG_PKG_USING_RT_LINK_HW is not set +# CONFIG_PKG_USING_RYANMQTT is not set +# CONFIG_PKG_USING_RYANW5500 is not set +# CONFIG_PKG_USING_LORA_PKT_FWD is not set +# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set +# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set +# CONFIG_PKG_USING_HM is not set +# CONFIG_PKG_USING_SMALL_MODBUS is not set +# CONFIG_PKG_USING_NET_SERVER is not set +# CONFIG_PKG_USING_ZFTP is not set +# CONFIG_PKG_USING_WOL is not set +# CONFIG_PKG_USING_ZEPHYR_POLLING is not set +# CONFIG_PKG_USING_MATTER_ADAPTATION_LAYER is not set +# CONFIG_PKG_USING_LHC_MODBUS is not set +# CONFIG_PKG_USING_QMODBUS is not set +# CONFIG_PKG_USING_PNET is not set +# CONFIG_PKG_USING_OPENER is not set +# CONFIG_PKG_USING_FREEMQTT is not set +# end of IoT - internet of things + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_LIBSODIUM is not set +# CONFIG_PKG_USING_LIBHYDROGEN is not set +# CONFIG_PKG_USING_TINYCRYPT is not set +# CONFIG_PKG_USING_TFM is not set +# CONFIG_PKG_USING_YD_CRYPTO is not set +# end of security packages + +# +# language packages +# + +# +# JSON: JavaScript Object Notation, a lightweight data-interchange format +# +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set +# CONFIG_PKG_USING_PARSON is not set +# CONFIG_PKG_USING_RYAN_JSON is not set +# end of JSON: JavaScript Object Notation, a lightweight data-interchange format + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# end of XML: Extensible Markup Language + +# CONFIG_PKG_USING_LUATOS_SOC is not set +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set +# CONFIG_PKG_USING_PIKASCRIPT is not set +# CONFIG_PKG_USING_RTT_RUST is not set +# end of language packages + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set +# end of LVGL: powerful and easy-to-use embedded GUI library + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# end of u8g2: a monochrome graphic library + +# CONFIG_PKG_USING_NES_SIMULATOR is not set +# CONFIG_PKG_USING_OPENMV is not set +# CONFIG_PKG_USING_MUPDF is not set +# CONFIG_PKG_USING_STEMWIN is not set +# CONFIG_PKG_USING_WAVPLAYER is not set +# CONFIG_PKG_USING_TJPGD is not set +# CONFIG_PKG_USING_PDFGEN is not set +# CONFIG_PKG_USING_HELIX is not set +# CONFIG_PKG_USING_AZUREGUIX is not set +# CONFIG_PKG_USING_TOUCHGFX2RTT is not set +# CONFIG_PKG_USING_NUEMWIN is not set +# CONFIG_PKG_USING_MP3PLAYER is not set +# CONFIG_PKG_USING_TINYJPEG is not set +# CONFIG_PKG_USING_UGUI is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_TERMBOX is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_GUIENGINE is not set +# CONFIG_PKG_USING_3GPP_AMRNB is not set +# end of multimedia packages + +# +# tools packages +# +# CONFIG_PKG_USING_VECTOR is not set +# CONFIG_PKG_USING_SORCH is not set +# CONFIG_PKG_USING_DICT is not set +# CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_MCOREDUMP is not set +# CONFIG_PKG_USING_EASYFLASH is not set +# CONFIG_PKG_USING_EASYLOGGER is not set +# CONFIG_PKG_USING_SYSTEMVIEW is not set +# CONFIG_PKG_USING_SEGGER_RTT is not set +# CONFIG_PKG_USING_RTT_AUTO_EXE_CMD is not set +# CONFIG_PKG_USING_RDB is not set +# CONFIG_PKG_USING_ULOG_EASYFLASH is not set +# CONFIG_PKG_USING_LOGMGR is not set +# CONFIG_PKG_USING_ADBD is not set +# CONFIG_PKG_USING_COREMARK is not set +# CONFIG_PKG_USING_DHRYSTONE is not set +# CONFIG_PKG_USING_MEMORYPERF is not set +# CONFIG_PKG_USING_NR_MICRO_SHELL is not set +# CONFIG_PKG_USING_CHINESE_FONT_LIBRARY is not set +# CONFIG_PKG_USING_LUNAR_CALENDAR is not set +# CONFIG_PKG_USING_BS8116A is not set +# CONFIG_PKG_USING_GPS_RMC is not set +# CONFIG_PKG_USING_URLENCODE is not set +# CONFIG_PKG_USING_UMCN is not set +# CONFIG_PKG_USING_LWRB2RTT is not set +# CONFIG_PKG_USING_CPU_USAGE is not set +# CONFIG_PKG_USING_GBK2UTF8 is not set +# CONFIG_PKG_USING_VCONSOLE is not set +# CONFIG_PKG_USING_KDB is not set +# CONFIG_PKG_USING_WAMR is not set +# CONFIG_PKG_USING_MICRO_XRCE_DDS_CLIENT is not set +# CONFIG_PKG_USING_LWLOG is not set +# CONFIG_PKG_USING_ANV_TRACE is not set +# CONFIG_PKG_USING_ANV_MEMLEAK is not set +# CONFIG_PKG_USING_ANV_TESTSUIT is not set +# CONFIG_PKG_USING_ANV_BENCH is not set +# CONFIG_PKG_USING_DEVMEM is not set +# CONFIG_PKG_USING_REGEX is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set +# CONFIG_PKG_USING_SOLAR_TERMS is not set +# CONFIG_PKG_USING_GAN_ZHI is not set +# CONFIG_PKG_USING_FDT is not set +# CONFIG_PKG_USING_CBOX is not set +# CONFIG_PKG_USING_SNOWFLAKE is not set +# CONFIG_PKG_USING_HASH_MATCH is not set +# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set +# CONFIG_PKG_USING_VOFA_PLUS is not set +# CONFIG_PKG_USING_ZDEBUG is not set +# CONFIG_PKG_USING_RVBACKTRACE is not set +# CONFIG_PKG_USING_HPATCHLITE is not set +# CONFIG_PKG_USING_THREAD_METRIC is not set +# CONFIG_PKG_USING_UORB is not set +# CONFIG_PKG_USING_RT_TUNNEL is not set +# CONFIG_PKG_USING_VIRTUAL_TERMINAL is not set +# CONFIG_PKG_USING_FEE is not set +# end of tools packages + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# end of enhanced kernel services + +# CONFIG_PKG_USING_AUNITY is not set + +# +# acceleration: Assembly language or algorithmic acceleration packages +# +# CONFIG_PKG_USING_QFPLIB_M0_FULL is not set +# CONFIG_PKG_USING_QFPLIB_M0_TINY is not set +# CONFIG_PKG_USING_QFPLIB_M3 is not set +# end of acceleration: Assembly language or algorithmic acceleration packages + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_CORE is not set +# CONFIG_PKG_USING_CMSIS_NN is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 is not set +# end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard + +# +# Micrium: Micrium software products porting for RT-Thread +# +# CONFIG_PKG_USING_UCOSIII_WRAPPER is not set +# CONFIG_PKG_USING_UCOSII_WRAPPER is not set +# CONFIG_PKG_USING_UC_CRC is not set +# CONFIG_PKG_USING_UC_CLK is not set +# CONFIG_PKG_USING_UC_COMMON is not set +# CONFIG_PKG_USING_UC_MODBUS is not set +# end of Micrium: Micrium software products porting for RT-Thread + +# CONFIG_PKG_USING_FREERTOS_WRAPPER is not set +# CONFIG_PKG_USING_LITEOS_SDK is not set +# CONFIG_PKG_USING_TZ_DATABASE is not set +# CONFIG_PKG_USING_CAIRO is not set +# CONFIG_PKG_USING_PIXMAN is not set +# CONFIG_PKG_USING_PARTITION is not set +# CONFIG_PKG_USING_PERF_COUNTER is not set +# CONFIG_PKG_USING_FILEX is not set +# CONFIG_PKG_USING_LEVELX is not set +# CONFIG_PKG_USING_FLASHDB is not set +# CONFIG_PKG_USING_SQLITE is not set +# CONFIG_PKG_USING_RTI is not set +# CONFIG_PKG_USING_DFS_YAFFS is not set +# CONFIG_PKG_USING_LITTLEFS is not set +# CONFIG_PKG_USING_DFS_JFFS2 is not set +# CONFIG_PKG_USING_DFS_UFFS is not set +# CONFIG_PKG_USING_LWEXT4 is not set +# CONFIG_PKG_USING_THREAD_POOL is not set +# CONFIG_PKG_USING_ROBOTS is not set +# CONFIG_PKG_USING_EV is not set +# CONFIG_PKG_USING_SYSWATCH is not set +# CONFIG_PKG_USING_SYS_LOAD_MONITOR is not set +# CONFIG_PKG_USING_PLCCORE is not set +# CONFIG_PKG_USING_RAMDISK is not set +# CONFIG_PKG_USING_MININI is not set +# CONFIG_PKG_USING_QBOOT is not set +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP is not set +# CONFIG_PKG_USING_RPMSG_LITE is not set +# CONFIG_PKG_USING_LPM is not set +# CONFIG_PKG_USING_TLSF is not set +# CONFIG_PKG_USING_EVENT_RECORDER is not set +# CONFIG_PKG_USING_ARM_2D is not set +# CONFIG_PKG_USING_MCUBOOT is not set +# CONFIG_PKG_USING_TINYUSB is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_SPEED_FS is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_SPEED_HS is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_SPEED_AUTO is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_FSDEV_ST is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_FSDEV_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_ST is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_ESP is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_KENDRYTE is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_INFINEON is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_AT is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_HC is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_NATION is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_GD is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_DWC2_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_ES is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_SUNXI is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_BK is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_SIFLI is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_MUSB_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_CHIPIDEA_MCX is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_CHIPIDEA_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_KINETIS_MCX is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_KINETIS_MM32 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_KINETIS_CUSTOM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_BL is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_HPM is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_AIC is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_RP2040 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_RENESAS is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_CH32 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_PUSB2 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_TEMPLATE_NONE is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV1 is not set +# CONFIG_PKG_CHERRYUSB_DEVICE_TEMPLATE_WINUSBV2 is not set +# CONFIG_PKG_USING_KMULTI_RTIMER is not set +# CONFIG_PKG_USING_TFDB is not set +# CONFIG_PKG_USING_QPC is not set +# CONFIG_PKG_USING_AGILE_UPGRADE is not set +# CONFIG_PKG_USING_FLASH_BLOB is not set +# CONFIG_PKG_USING_MLIBC is not set +# CONFIG_PKG_USING_TASK_MSG_BUS is not set +# CONFIG_PKG_USING_UART_FRAMEWORK is not set +# CONFIG_PKG_USING_SFDB is not set +# CONFIG_PKG_USING_RTP is not set +# CONFIG_PKG_USING_REB is not set +# CONFIG_PKG_USING_RMP is not set +# CONFIG_PKG_USING_R_RHEALSTONE is not set +# CONFIG_PKG_USING_HEARTBEAT is not set +# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set +# CONFIG_PKG_USING_CHERRYECAT is not set +# CONFIG_PKG_USING_EVENT_LOOP is not set +# CONFIG_PKG_USING_THREAD_MANAGER is not set +# end of system packages + +# +# peripheral libraries and drivers +# + +# +# HAL & SDK Drivers +# + +# +# STM32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_STM32F0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB55_SDK is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_STM32WL_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WL_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_CMSIS_DRIVER is not set +# end of STM32 HAL & SDK Drivers + +# +# Infineon HAL Packages +# +# CONFIG_PKG_USING_INFINEON_CAT1CM0P is not set +# CONFIG_PKG_USING_INFINEON_CMSIS is not set +# CONFIG_PKG_USING_INFINEON_CORE_LIB is not set +# CONFIG_PKG_USING_INFINEON_MTB_HAL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_MTB_PDL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_RETARGET_IO is not set +# CONFIG_PKG_USING_INFINEON_CAPSENSE is not set +# CONFIG_PKG_USING_INFINEON_CSDIDAC is not set +# CONFIG_PKG_USING_INFINEON_SERIAL_FLASH is not set +# CONFIG_PKG_USING_INFINEON_USBDEV is not set +# end of Infineon HAL Packages + +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_EMBARC_BSP is not set +# CONFIG_PKG_USING_ESP_IDF is not set + +# +# Kendryte SDK +# +# CONFIG_PKG_USING_K210_SDK is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# end of Kendryte SDK + +# CONFIG_PKG_USING_NRF5X_SDK is not set +# CONFIG_PKG_USING_NRFX is not set +# CONFIG_PKG_USING_NUCLEI_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_RP2350_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set + +# +# MM32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_MM32F103XX_CMSIS is not set +# CONFIG_PKG_USING_MM32F3270X_CMSIS is not set +# CONFIG_PKG_USING_MM32F5260X_CMSIS is not set +# CONFIG_PKG_USING_MM32L0XX_CMSIS is not set +# CONFIG_PKG_USING_MM32L3XX_CMSIS is not set +# CONFIG_PKG_USING_MM32F103XX_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32F3270X_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32F5260X_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32L0XX_HAL_DRIVER is not set +# CONFIG_PKG_USING_MM32L3XX_HAL_DRIVER is not set +# end of MM32 HAL & SDK Drivers + +# +# WCH HAL & SDK Drivers +# +# CONFIG_PKG_USING_CH32V20x_SDK is not set +# CONFIG_PKG_USING_CH32V307_SDK is not set +# end of WCH HAL & SDK Drivers + +# +# AT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_AT32A403A_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A403A_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_CMSIS_DRIVER is not set +# end of AT32 HAL & SDK Drivers + +# +# HC32 DDL Drivers +# +# CONFIG_PKG_USING_HC32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F3_SERIES_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_SERIES_DRIVER is not set +# end of HC32 DDL Drivers + +# +# NXP HAL & SDK Drivers +# +# CONFIG_PKG_USING_NXP_MCX_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NXP_MCX_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC55S_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6SX_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6UL_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMXRT_DRIVER is not set +# end of NXP HAL & SDK Drivers + +# +# NUVOTON Drivers +# +# CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set +# end of NUVOTON Drivers + +# +# GD32 Drivers +# +# CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set +# CONFIG_PKG_USING_GD32_RISCV_SERIES_DRIVER is not set +# CONFIG_PKG_USING_GD32VW55X_WIFI is not set +# end of GD32 Drivers + +# +# HPMicro SDK +# +# CONFIG_PKG_USING_HPM_SDK is not set +# end of HPMicro SDK + +# +# FT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_FT32F0_STD_DRIVER is not set +# CONFIG_PKG_USING_FT32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_FT32F4_STD_DRIVER is not set +# CONFIG_PKG_USING_FT32F4_CMSIS_DRIVER is not set +# end of FT32 HAL & SDK Drivers + +# +# NOVOSNS Drivers +# +# CONFIG_PKG_USING_NOVOSNS_SERIES_DRIVER is not set +# end of NOVOSNS Drivers + +# +# N32 Drivers +# +# CONFIG_PKG_USING_N32H7XX_STD_DRIVER is not set +# CONFIG_PKG_USING_N32H7XX_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_N32H49X_STD_DRIVER is not set +# CONFIG_PKG_USING_N32H49X_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_N32H47X_48X_STD_DRIVER is not set +# CONFIG_PKG_USING_N32H47X_48X_CMSIS_DRIVER is not set +# end of N32 Drivers +# end of HAL & SDK Drivers + +# +# sensors drivers +# +# CONFIG_PKG_USING_LSM6DSM is not set +# CONFIG_PKG_USING_LSM6DSL is not set +# CONFIG_PKG_USING_LPS22HB is not set +# CONFIG_PKG_USING_HTS221 is not set +# CONFIG_PKG_USING_LSM303AGR is not set +# CONFIG_PKG_USING_BME280 is not set +# CONFIG_PKG_USING_BME680 is not set +# CONFIG_PKG_USING_BMA400 is not set +# CONFIG_PKG_USING_BMI160_BMX160 is not set +# CONFIG_PKG_USING_SPL0601 is not set +# CONFIG_PKG_USING_MS5805 is not set +# CONFIG_PKG_USING_DA270 is not set +# CONFIG_PKG_USING_DF220 is not set +# CONFIG_PKG_USING_HSHCAL001 is not set +# CONFIG_PKG_USING_BH1750 is not set +# CONFIG_PKG_USING_MPU6XXX is not set +# CONFIG_PKG_USING_AHT10 is not set +# CONFIG_PKG_USING_AP3216C is not set +# CONFIG_PKG_USING_TSL4531 is not set +# CONFIG_PKG_USING_DS18B20 is not set +# CONFIG_PKG_USING_DHT11 is not set +# CONFIG_PKG_USING_DHTXX is not set +# CONFIG_PKG_USING_GY271 is not set +# CONFIG_PKG_USING_GP2Y10 is not set +# CONFIG_PKG_USING_SGP30 is not set +# CONFIG_PKG_USING_HDC1000 is not set +# CONFIG_PKG_USING_BMP180 is not set +# CONFIG_PKG_USING_BMP280 is not set +# CONFIG_PKG_USING_SHTC1 is not set +# CONFIG_PKG_USING_BMI088 is not set +# CONFIG_PKG_USING_HMC5883 is not set +# CONFIG_PKG_USING_MAX6675 is not set +# CONFIG_PKG_USING_MAX31855 is not set +# CONFIG_PKG_USING_TMP1075 is not set +# CONFIG_PKG_USING_SR04 is not set +# CONFIG_PKG_USING_CCS811 is not set +# CONFIG_PKG_USING_PMSXX is not set +# CONFIG_PKG_USING_RT3020 is not set +# CONFIG_PKG_USING_MLX90632 is not set +# CONFIG_PKG_USING_MLX90382 is not set +# CONFIG_PKG_USING_MLX90384 is not set +# CONFIG_PKG_USING_MLX90393 is not set +# CONFIG_PKG_USING_MLX90392 is not set +# CONFIG_PKG_USING_MLX90394 is not set +# CONFIG_PKG_USING_MLX90396 is not set +# CONFIG_PKG_USING_MLX90397 is not set +# CONFIG_PKG_USING_MS5611 is not set +# CONFIG_PKG_USING_MAX31865 is not set +# CONFIG_PKG_USING_VL53L0X is not set +# CONFIG_PKG_USING_INA260 is not set +# CONFIG_PKG_USING_MAX30102 is not set +# CONFIG_PKG_USING_INA226 is not set +# CONFIG_PKG_USING_LIS2DH12 is not set +# CONFIG_PKG_USING_HS300X is not set +# CONFIG_PKG_USING_ZMOD4410 is not set +# CONFIG_PKG_USING_ISL29035 is not set +# CONFIG_PKG_USING_MMC3680KJ is not set +# CONFIG_PKG_USING_QMP6989 is not set +# CONFIG_PKG_USING_BALANCE is not set +# CONFIG_PKG_USING_SHT2X is not set +# CONFIG_PKG_USING_SHT3X is not set +# CONFIG_PKG_USING_SHT4X is not set +# CONFIG_PKG_USING_AD7746 is not set +# CONFIG_PKG_USING_ADT74XX is not set +# CONFIG_PKG_USING_MAX17048 is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_CW2015 is not set +# CONFIG_PKG_USING_ICM20608 is not set +# CONFIG_PKG_USING_PAJ7620 is not set +# CONFIG_PKG_USING_STHS34PF80 is not set +# CONFIG_PKG_USING_P3T1755 is not set +# CONFIG_PKG_USING_QMI8658 is not set +# CONFIG_PKG_USING_ICM20948 is not set +# CONFIG_PKG_USING_SCD4X is not set +# end of sensors drivers + +# +# touch drivers +# +# CONFIG_PKG_USING_GT9147 is not set +# CONFIG_PKG_USING_GT1151 is not set +# CONFIG_PKG_USING_GT917S is not set +# CONFIG_PKG_USING_GT911 is not set +# CONFIG_PKG_USING_FT6206 is not set +# CONFIG_PKG_USING_FT5426 is not set +# CONFIG_PKG_USING_FT6236 is not set +# CONFIG_PKG_USING_XPT2046_TOUCH is not set +# CONFIG_PKG_USING_CST816X is not set +# CONFIG_PKG_USING_CST812T is not set +# end of touch drivers + +# CONFIG_PKG_USING_LCD_SPI_DRIVER is not set +# CONFIG_PKG_USING_REALTEK_AMEBA is not set +# CONFIG_PKG_USING_BUTTON is not set +# CONFIG_PKG_USING_PCF8574 is not set +# CONFIG_PKG_USING_SX12XX is not set +# CONFIG_PKG_USING_SIGNAL_LED is not set +# CONFIG_PKG_USING_LEDBLINK is not set +# CONFIG_PKG_USING_LITTLED is not set +# CONFIG_PKG_USING_LKDGUI is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_MULTI_INFRARED is not set +# CONFIG_PKG_USING_AGILE_BUTTON is not set +# CONFIG_PKG_USING_AGILE_LED is not set +# CONFIG_PKG_USING_AT24CXX is not set +# CONFIG_PKG_USING_MOTIONDRIVER2RTT is not set +# CONFIG_PKG_USING_PCA9685 is not set +# CONFIG_PKG_USING_ILI9341 is not set +# CONFIG_PKG_USING_I2C_TOOLS is not set +# CONFIG_PKG_USING_NRF24L01 is not set +# CONFIG_PKG_USING_RPLIDAR is not set +# CONFIG_PKG_USING_AS608 is not set +# CONFIG_PKG_USING_RC522 is not set +# CONFIG_PKG_USING_WS2812B is not set +# CONFIG_PKG_USING_EXTERN_RTC_DRIVERS is not set +# CONFIG_PKG_USING_MULTI_RTIMER is not set +# CONFIG_PKG_USING_MAX7219 is not set +# CONFIG_PKG_USING_BEEP is not set +# CONFIG_PKG_USING_EASYBLINK is not set +# CONFIG_PKG_USING_PMS_SERIES is not set +# CONFIG_PKG_USING_CAN_YMODEM is not set +# CONFIG_PKG_USING_LORA_RADIO_DRIVER is not set +# CONFIG_PKG_USING_QLED is not set +# CONFIG_PKG_USING_AGILE_CONSOLE is not set +# CONFIG_PKG_USING_LD3320 is not set +# CONFIG_PKG_USING_WK2124 is not set +# CONFIG_PKG_USING_LY68L6400 is not set +# CONFIG_PKG_USING_DM9051 is not set +# CONFIG_PKG_USING_CH390 is not set +# CONFIG_PKG_USING_SSD1306 is not set +# CONFIG_PKG_USING_QKEY is not set +# CONFIG_PKG_USING_RS485 is not set +# CONFIG_PKG_USING_RS232 is not set +# CONFIG_PKG_USING_NES is not set +# CONFIG_PKG_USING_VIRTUAL_SENSOR is not set +# CONFIG_PKG_USING_VDEVICE is not set +# CONFIG_PKG_USING_SGM706 is not set +# CONFIG_PKG_USING_RDA58XX is not set +# CONFIG_PKG_USING_LIBNFC is not set +# CONFIG_PKG_USING_MFOC is not set +# CONFIG_PKG_USING_TMC51XX is not set +# CONFIG_PKG_USING_TCA9534 is not set +# CONFIG_PKG_USING_KOBUKI is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_MICRO_ROS is not set +# CONFIG_PKG_USING_MCP23008 is not set +# CONFIG_PKG_USING_MISAKA_AT24CXX is not set +# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set +# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set +# CONFIG_PKG_USING_SOFT_SERIAL is not set +# CONFIG_PKG_USING_MB85RS16 is not set +# CONFIG_PKG_USING_RFM300 is not set +# CONFIG_PKG_USING_IO_INPUT_FILTER is not set +# CONFIG_PKG_USING_LRF_NV7LIDAR is not set +# CONFIG_PKG_USING_AIP650 is not set +# CONFIG_PKG_USING_FINGERPRINT is not set +# CONFIG_PKG_USING_BT_ECB02C is not set +# CONFIG_PKG_USING_UAT is not set +# CONFIG_PKG_USING_ST7789 is not set +# CONFIG_PKG_USING_VS1003 is not set +# CONFIG_PKG_USING_X9555 is not set +# CONFIG_PKG_USING_SYSTEM_RUN_LED is not set +# CONFIG_PKG_USING_BT_MX01 is not set +# CONFIG_PKG_USING_RGPOWER is not set +# CONFIG_PKG_USING_BT_MX02 is not set +# CONFIG_PKG_USING_GC9A01 is not set +# CONFIG_PKG_USING_IK485 is not set +# CONFIG_PKG_USING_SERVO is not set +# CONFIG_PKG_USING_SEAN_WS2812B is not set +# CONFIG_PKG_USING_IC74HC165 is not set +# CONFIG_PKG_USING_IST8310 is not set +# CONFIG_PKG_USING_ST7789_SPI is not set +# CONFIG_PKG_USING_CAN_UDS is not set +# CONFIG_PKG_USING_ISOTP_C is not set +# CONFIG_PKG_USING_IKUNLED is not set +# CONFIG_PKG_USING_INS5T8025 is not set +# CONFIG_PKG_USING_IRUART is not set +# CONFIG_PKG_USING_ST7305 is not set +# CONFIG_PKG_USING_TM1668 is not set +# CONFIG_PKG_USING_TCA9548A is not set +# CONFIG_PKG_USING_SPI_TOOLS is not set +# end of peripheral libraries and drivers + +# +# AI packages +# +# CONFIG_PKG_USING_LIBANN is not set +# CONFIG_PKG_USING_NNOM is not set +# CONFIG_PKG_USING_ONNX_BACKEND is not set +# CONFIG_PKG_USING_ONNX_PARSER is not set +# CONFIG_PKG_USING_TENSORFLOWLITEMICRO is not set +# CONFIG_PKG_USING_ELAPACK is not set +# CONFIG_PKG_USING_ULAPACK is not set +# CONFIG_PKG_USING_QUEST is not set +# CONFIG_PKG_USING_NAXOS is not set +# CONFIG_PKG_USING_R_TINYMAIX is not set +# CONFIG_PKG_USING_LLMCHAT is not set +# CONFIG_PKG_USING_AIFES is not set +# CONFIG_PKG_USING_AGENT is not set +# end of AI packages + +# +# Signal Processing and Control Algorithm Packages +# +# CONFIG_PKG_USING_APID is not set +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_QPID is not set +# CONFIG_PKG_USING_UKAL is not set +# CONFIG_PKG_USING_DIGITALCTRL is not set +# CONFIG_PKG_USING_KISSFFT is not set +# end of Signal Processing and Control Algorithm Packages + +# +# miscellaneous packages +# + +# +# project laboratory +# +# end of project laboratory + +# +# samples: kernel and components samples +# +# CONFIG_PKG_USING_KERNEL_SAMPLES is not set +# CONFIG_PKG_USING_FILESYSTEM_SAMPLES is not set +# CONFIG_PKG_USING_NETWORK_SAMPLES is not set +# CONFIG_PKG_USING_PERIPHERAL_SAMPLES is not set +# end of samples: kernel and components samples + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_CMATRIX is not set +# CONFIG_PKG_USING_SL is not set +# CONFIG_PKG_USING_CAL is not set +# CONFIG_PKG_USING_ACLOCK is not set +# CONFIG_PKG_USING_THREES is not set +# CONFIG_PKG_USING_2048 is not set +# CONFIG_PKG_USING_SNAKE is not set +# CONFIG_PKG_USING_TETRIS is not set +# CONFIG_PKG_USING_DONUT is not set +# CONFIG_PKG_USING_COWSAY is not set +# CONFIG_PKG_USING_MORSE is not set +# end of entertainment: terminal games and other interesting software packages + +# CONFIG_PKG_USING_LIBCSV is not set +# CONFIG_PKG_USING_OPTPARSE is not set +# CONFIG_PKG_USING_FASTLZ is not set +# CONFIG_PKG_USING_MINILZO is not set +# CONFIG_PKG_USING_QUICKLZ is not set +# CONFIG_PKG_USING_LZMA is not set +# CONFIG_PKG_USING_RALARAM is not set +# CONFIG_PKG_USING_MULTIBUTTON is not set +# CONFIG_PKG_USING_FLEXIBLE_BUTTON is not set +# CONFIG_PKG_USING_CANFESTIVAL is not set +# CONFIG_PKG_USING_ZLIB is not set +# CONFIG_PKG_USING_MINIZIP is not set +# CONFIG_PKG_USING_HEATSHRINK is not set +# CONFIG_PKG_USING_DSTR is not set +# CONFIG_PKG_USING_TINYFRAME is not set +# CONFIG_PKG_USING_KENDRYTE_DEMO is not set +# CONFIG_PKG_USING_UPACKER is not set +# CONFIG_PKG_USING_UPARAM is not set +# CONFIG_PKG_USING_HELLO is not set +# CONFIG_PKG_USING_VI is not set +# CONFIG_PKG_USING_KI is not set +# CONFIG_PKG_USING_ARMv7M_DWT is not set +# CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LIBCRC is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_DESIGN_PATTERN is not set +# CONFIG_PKG_USING_CONTROLLER is not set +# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set +# CONFIG_PKG_USING_MFBD is not set +# CONFIG_PKG_USING_SLCAN2RTT is not set +# CONFIG_PKG_USING_SOEM is not set +# CONFIG_PKG_USING_QPARAM is not set +# CONFIG_PKG_USING_CorevMCU_CLI is not set +# CONFIG_PKG_USING_DRMP is not set +# CONFIG_PKG_USING_AUTOGEN_PARAMETER_MANAGER is not set +# end of miscellaneous packages + +# +# Arduino libraries +# +# CONFIG_PKG_USING_RTDUINO is not set + +# +# Projects and Demos +# +# CONFIG_PKG_USING_ARDUINO_MSGQ_C_CPP_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_SKETCH_LOADER_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set +# CONFIG_PKG_USING_ARDUINO_RTDUINO_SENSORFUSION_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_NINEINONE_SENSOR_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set +# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set +# end of Projects and Demos + +# +# Sensors +# +# CONFIG_PKG_USING_ARDUINO_SENSOR_DEVICE_DRIVERS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL375 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L1X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL6180X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31855 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31865 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31856 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX6675 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90614 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS1 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AHTX0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADT7410 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME680 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9808 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4728 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA219 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR390 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DHT is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM6DS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO055 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX1704X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMC56X3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90393 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90395 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ICM20X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DPS310 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTS221 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT4X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL343 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS726X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AMG88XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2320 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2315 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR329_LTR303 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP3XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MS8607 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90640 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMA8451 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MSA301 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X_RVC is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS2MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303DLH_MAG is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LC709203F is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CAP1188 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CCS811 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_NAU7802 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS331 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS2X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS35HW is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303_ACCEL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3DH is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8591 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL3115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPR121 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPRLS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPU6050 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCT2075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PM25AQI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_EMC2101 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXAS21002C is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SCD30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXOS8700 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HMC5883_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP006 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TLA202X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCS34725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI7021 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP40 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHTC3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU21DF is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS7341 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU31D is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA260 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP007_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_L3GD20 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP117 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSC2007 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2591_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VCNL4040 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML7700 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LIS3DHTR is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DHT is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL335 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_H3LIS331DL is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MMA7660 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PAJ7620 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ITG3200 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HP20X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DRV2605L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BBM150 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HMC5883L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM303DLH is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TCS3414CS is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MP503 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HIGHTEMP is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT35 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_AT42QT1070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM6DS3 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HM3301 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LTC2941 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LDC1612 is not set +# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set +# CONFIG_PKG_USING_ARDUINO_JARZEBSKI_MPU6050 is not set +# end of Sensors + +# +# Display +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_GFX_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_U8G2 is not set +# CONFIG_PKG_USING_ARDUINO_TFT_ESPI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ST7735 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SSD1306 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ILI9341 is not set +# CONFIG_PKG_USING_SEEED_TM1637 is not set +# end of Display + +# +# Timing +# +# CONFIG_PKG_USING_ARDUINO_RTCLIB is not set +# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set +# CONFIG_PKG_USING_ARDUINO_TICKER is not set +# CONFIG_PKG_USING_ARDUINO_TASKSCHEDULER is not set +# end of Timing + +# +# Data Processing +# +# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set +# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set +# CONFIG_PKG_USING_ARDUINO_TENSORFLOW_LITE_MICRO is not set +# CONFIG_PKG_USING_ARDUINO_RUNNINGMEDIAN is not set +# end of Data Processing + +# +# Data Storage +# + +# +# Communication +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set +# end of Communication + +# +# Device Control +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TPA2016 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DRV2605 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS1841 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS3502 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PCF85063TP is not set +# end of Device Control + +# +# Other +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MFRC630 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI5351 is not set +# end of Other + +# +# Signal IO +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BUSIO is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCA8418 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP23017 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADS1X15 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AW9523 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP3008 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BD3491FS is not set +# end of Signal IO + +# +# Uncategorized +# +# end of Arduino libraries +# end of RT-Thread online packages + +CONFIG_SOC_ALLWINNER_F101=y +CONFIG_SBC_YUZUKINEKO=y + +# +# YuzukiNeko SBC Configuration +# +# CONFIG_SBC_YUZUKINEKO_MMODE is not set +# CONFIG_SBC_YUZUKINEKO_SMODE is not set +CONFIG_SBC_YUZUKINEKO_SMODE_BARE=y +CONFIG_SBC_YUZUKINEKO_ST77916=y +# end of YuzukiNeko SBC Configuration diff --git a/bsp/allwinner/f101/usbcrc-send.sh b/bsp/allwinner/f101/usbcrc-send.sh new file mode 100755 index 000000000000..baa7e16a2f95 --- /dev/null +++ b/bsp/allwinner/f101/usbcrc-send.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Send a file to the temporary usbcrc command over the F101 CDC ACM device. + +set -euo pipefail + +usage() +{ + printf 'Usage: %s [tty] [target-command]\n' "$0" >&2 + printf 'Example: %s /home/gui/Desktop/rt-thread/test.jpg /dev/ttyACM0 usbjpeg\n' "$0" >&2 + exit 2 +} + +file=${1:-} +device=${2:-/dev/ttyACM0} +target_command=${3:-usbcrc} +chunk_size=512 +chunk_delay=0.03 + +[[ -n "$file" ]] || usage +[[ -r "$file" ]] || { printf 'Cannot read: %s\n' "$file" >&2; exit 1; } +[[ -c "$device" ]] || { printf 'Not a serial device: %s\n' "$device" >&2; exit 1; } + +size=$(wc -c < "$file") + +printf 'On the board, run:\n\n %s %s\n\n' "$target_command" "$size" +read -r -p 'Press Enter after the board reports ready: ' + +stty -F "$device" raw -echo -ixon -ixoff -icrnl -inlcr -onlcr + +for ((offset = 0; offset < size; offset += chunk_size)); do + dd if="$file" of="$device" bs="$chunk_size" \ + skip=$((offset / chunk_size)) count=1 status=none + sleep "$chunk_delay" +done + +printf 'Sent %s bytes to %s.\n' "$size" "$device" diff --git a/components/drivers/graphic/framebuffer/Kconfig b/components/drivers/graphic/framebuffer/Kconfig index 738fdb7ac0b2..e5f6fef78d95 100644 --- a/components/drivers/graphic/framebuffer/Kconfig +++ b/components/drivers/graphic/framebuffer/Kconfig @@ -9,6 +9,19 @@ config RT_GRAPHIC_FB_ILI9486 depends on RT_USING_SPI default n +config RT_GRAPHIC_FB_ST77916 + bool "ST77916 QSPI framebuffer support" + depends on RT_GRAPHIC_FB + depends on RT_USING_OFW + depends on RT_USING_QSPI + depends on RT_USING_PIN + depends on RT_USING_HEAP + select RT_USING_SEMAPHORE + default n + help + Enable the ST77916 framebuffer driver. The panel uses a single-line + command phase and a four-line QSPI RGB666 pixel phase. + config RT_GRAPHIC_FB_SIMPLE bool "Simple framebuffer support" depends on RT_GRAPHIC_FB diff --git a/components/drivers/graphic/framebuffer/SConscript b/components/drivers/graphic/framebuffer/SConscript index b9bbe32ad8b7..4e59d717202f 100644 --- a/components/drivers/graphic/framebuffer/SConscript +++ b/components/drivers/graphic/framebuffer/SConscript @@ -14,6 +14,9 @@ src = [] if GetDepend(['RT_GRAPHIC_FB_ILI9486']): src += ['fb-ili9486.c'] +if GetDepend(['RT_GRAPHIC_FB_ST77916']): + src += ['fb-st77916.c'] + if GetDepend(['RT_GRAPHIC_FB_SIMPLE']): src += ['fb-simple.c'] diff --git a/components/drivers/graphic/framebuffer/fb-st77916.c b/components/drivers/graphic/framebuffer/fb-st77916.c new file mode 100644 index 000000000000..a4a004f16e62 --- /dev/null +++ b/components/drivers/graphic/framebuffer/fb-st77916.c @@ -0,0 +1,786 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-10-01 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "fb.st77916" +#define DBG_LVL DBG_INFO +#include + +#define ST77916_DEFAULT_WIDTH 360 +#define ST77916_DEFAULT_HEIGHT 360 +#define ST77916_INIT_MAX_HZ 10000000 +#define ST77916_DEFAULT_MAX_HZ 60000000 +#define ST77916_TE_TIMEOUT_MS 100 + +#define ST77916_MADCTL 0x36 +#define ST77916_MADCTL_MY RT_BIT(7) +#define ST77916_MADCTL_MX RT_BIT(6) +#define ST77916_MADCTL_MV RT_BIT(5) + +#define ST77916_QSPI_WRITE_CMD 0x02 +#define ST77916_QSPI_WRITE_COLOR_DUAL 0xa2 +#define ST77916_QSPI_WRITE_COLOR_QUAD 0x32 + +struct st77916_gpio +{ + rt_base_t pin; + rt_uint8_t active_value; +}; + +struct st77916_format +{ + const char *name; + rt_uint32_t mode; + rt_uint8_t bytes_per_pixel; +}; + +static const struct st77916_format st77916_formats[] = +{ + { "r5g6b5", RTGRAPHIC_PIXEL_FORMAT_RGB565, 2 }, + { "r8g8b8", RTGRAPHIC_PIXEL_FORMAT_RGB888, 3 }, +}; + +struct st77916_te +{ + rt_base_t pin; + struct rt_semaphore sem; + rt_bool_t sem_initialized; + rt_bool_t irq_attached; + rt_bool_t irq_enabled; +}; + +struct st77916fb +{ + struct rt_graphic_device parent; + struct rt_qspi_device *qspi; + struct st77916_gpio reset; + struct st77916_te te; + const struct st77916_format *format; + rt_uint8_t *framebuffer; + rt_uint8_t *transfer_buffer; + rt_uint32_t width; + rt_uint32_t height; + rt_uint32_t max_hz; + rt_uint8_t pixel_data_lines; + rt_uint8_t initial_madctl; + rt_bool_t defer_display_on; + rt_bool_t panel_enabled; + rt_uint32_t modes[1]; +}; + +static rt_err_t st77916_transfer(struct st77916fb *fb, rt_uint8_t opcode, + rt_uint8_t cmd, const void *data, rt_size_t data_size, + rt_uint8_t data_lines) +{ + struct rt_qspi_message message = {}; + rt_ssize_t result; + + message.instruction.content = opcode; + message.instruction.qspi_lines = 1; + message.address.content = (rt_uint32_t)cmd << 8; + message.address.size = 24; + message.address.qspi_lines = 1; + message.parent.send_buf = data; + message.parent.length = data_size; + message.parent.cs_take = 1; + message.parent.cs_release = 1; + message.qspi_data_lines = data_lines; + + result = rt_qspi_transfer_message(fb->qspi, &message); + + if (result < 0) + { + LOG_E("QSPI write %02x:%02x (%u bytes, %u line) failed: %s", + opcode, cmd, data_size, data_lines, rt_strerror(result)); + return result; + } + + return result ? RT_EOK : -RT_EIO; +} + +static rt_err_t st77916_write_cmd(struct st77916fb *fb, rt_uint8_t cmd, + const void *data, rt_size_t data_size) +{ + return st77916_transfer(fb, ST77916_QSPI_WRITE_CMD, cmd, data, + data_size, 1); +} + +static void st77916_reset(struct st77916fb *fb) +{ + if (fb->reset.pin < 0) + { + return; + } + + rt_pin_write(fb->reset.pin, fb->reset.active_value); + rt_thread_mdelay(20); + rt_pin_write(fb->reset.pin, !fb->reset.active_value); + rt_thread_mdelay(120); +} + +static void st77916_te_isr(void *param) +{ + struct st77916fb *fb = param; + + rt_sem_release(&fb->te.sem); +} + +static rt_err_t st77916_te_init(struct st77916fb *fb) +{ + rt_err_t err; + + if (fb->te.pin < 0) + { + return RT_EOK; + } + + if ((err = rt_sem_init(&fb->te.sem, "stte", 0, RT_IPC_FLAG_FIFO))) + { + return err; + } + fb->te.sem_initialized = RT_TRUE; + rt_sem_control(&fb->te.sem, RT_IPC_CMD_SET_VLIMIT, (void *)1); + + rt_pin_mode(fb->te.pin, PIN_MODE_INPUT); + if ((err = rt_pin_attach_irq(fb->te.pin, PIN_IRQ_MODE_RISING, + st77916_te_isr, fb))) + { + return err; + } + fb->te.irq_attached = RT_TRUE; + + return RT_EOK; +} + +static rt_err_t st77916_te_enable(struct st77916fb *fb) +{ + rt_err_t err; + + if (fb->te.pin < 0 || fb->te.irq_enabled) + { + return RT_EOK; + } + + if (!fb->te.irq_attached) + { + return -RT_ENOSYS; + } + + if ((err = rt_pin_irq_enable(fb->te.pin, PIN_IRQ_ENABLE)) == RT_EOK) + { + fb->te.irq_enabled = RT_TRUE; + } + + return err; +} + +static rt_err_t st77916_te_disable(struct st77916fb *fb) +{ + rt_err_t err; + + if (!fb->te.irq_enabled) + { + return RT_EOK; + } + + err = rt_pin_irq_enable(fb->te.pin, PIN_IRQ_DISABLE); + if (err == RT_EOK) + { + fb->te.irq_enabled = RT_FALSE; + } + + return err; +} + +static void st77916_te_deinit(struct st77916fb *fb) +{ + if (fb->te.irq_attached) + { + st77916_te_disable(fb); + rt_pin_detach_irq(fb->te.pin); + fb->te.irq_attached = RT_FALSE; + } + if (fb->te.sem_initialized) + { + rt_sem_detach(&fb->te.sem); + fb->te.sem_initialized = RT_FALSE; + } +} + +static rt_err_t st77916_wait_te(struct st77916fb *fb) +{ + if (fb->te.pin < 0) + { + return -RT_ENOSYS; + } + + if (!fb->te.irq_enabled) + { + return -RT_EBUSY; + } + + /* A pending TE edge is already a suitable frame-boundary synchronisation. */ + return rt_sem_take(&fb->te.sem, + rt_tick_from_millisecond(ST77916_TE_TIMEOUT_MS)); +} + +static rt_err_t st77916fb_wait_vsync(struct rt_graphic_device *gdev) +{ + struct st77916fb *fb = rt_container_of(gdev, struct st77916fb, parent); + + return st77916_wait_te(fb); +} + +static rt_err_t st77916fb_dpms_switch(struct rt_graphic_device *gdev, + rt_uint32_t dpms) +{ + struct st77916fb *fb = rt_container_of(gdev, struct st77916fb, parent); + rt_err_t err; + rt_uint8_t te_mode = 0x00; + + switch (dpms) + { + case RT_GRAPHIC_DPMS_ON: + /* The boot logo is drawn while a deferred panel remains blanked. */ + if (fb->defer_display_on && !fb->panel_enabled) + { + return RT_EOK; + } + if (fb->te.pin < 0) + { + return RT_EOK; + } + + /* Enable the panel TE output before accepting its GPIO edges. */ + if ((err = st77916_write_cmd(fb, 0x35, &te_mode, sizeof(te_mode))) != RT_EOK) + { + return err; + } + + err = st77916_te_enable(fb); + if (err != RT_EOK) + { + st77916_write_cmd(fb, 0x34, RT_NULL, 0); + } + + return err; + + case RT_GRAPHIC_DPMS_STANDBY: + case RT_GRAPHIC_DPMS_SUSPEND: + case RT_GRAPHIC_DPMS_OFF: + if (fb->te.pin < 0) + { + return RT_EOK; + } + + if ((err = st77916_te_disable(fb)) != RT_EOK) + { + return err; + } + + return st77916_write_cmd(fb, 0x34, RT_NULL, 0); + + default: + return -RT_EINVAL; + } +} + +static rt_err_t st77916_qspi_configure(struct st77916fb *fb, rt_uint32_t max_hz) +{ + struct rt_qspi_configuration config = fb->qspi->config; + rt_err_t err; + + config.parent.mode = RT_SPI_MODE_0 | RT_SPI_MASTER; + config.parent.data_width = 8; + config.parent.max_hz = max_hz; + config.qspi_dl_width = fb->pixel_data_lines; + + err = rt_qspi_configure(fb->qspi, &config); + + /* The SPI framework applies a non-owner's configuration on its first I/O. */ + return err == -RT_EBUSY ? RT_EOK : err; +} + +/* + * panel-init-sequence records are encoded in DT as: + * + * + */ +static rt_err_t st77916_run_init_sequence(struct st77916fb *fb) +{ + const rt_uint8_t *sequence; + rt_ssize_t length; + rt_size_t remain; + rt_err_t err; + + sequence = rt_ofw_prop_read_raw(fb->parent.parent.ofw_node, + "panel-init-sequence", &length); + if (!sequence || length <= 0) + { + return -RT_ENOENT; + } + + remain = length; + + while (remain) + { + rt_uint8_t command; + rt_uint8_t delay_ms; + rt_uint8_t payload_size; + + if (remain < 3) + { + return -RT_EINVAL; + } + + command = *sequence++; + delay_ms = *sequence++; + payload_size = *sequence++; + remain -= 3; + + if (payload_size > remain) + { + return -RT_EINVAL; + } + + err = st77916_write_cmd(fb, command, sequence, payload_size); + if (err != RT_EOK) + { + LOG_E("panel init command %02x (%u bytes) failed: %s", + command, payload_size, rt_strerror(err)); + return err; + } + + if (command == ST77916_MADCTL && payload_size == 1) + { + fb->initial_madctl = sequence[0]; + } + + sequence += payload_size; + remain -= payload_size; + if (delay_ms) + { + rt_thread_mdelay(delay_ms); + } + } + + return RT_EOK; +} + +static rt_err_t st77916_set_window(struct st77916fb *fb, rt_uint16_t x, + rt_uint16_t y, rt_uint16_t width, rt_uint16_t height) +{ + rt_uint8_t addr[] = + { + (x >> 8) & 0xff, x & 0xff, + ((x + width - 1) >> 8) & 0xff, (x + width - 1) & 0xff, + }; + rt_err_t err; + + if ((err = st77916_write_cmd(fb, 0x2a, addr, sizeof(addr))) != RT_EOK) + { + return err; + } + + addr[0] = (y >> 8) & 0xff; + addr[1] = y & 0xff; + addr[2] = ((y + height - 1) >> 8) & 0xff; + addr[3] = (y + height - 1) & 0xff; + + return st77916_write_cmd(fb, 0x2b, addr, sizeof(addr)); +} + +static rt_err_t st77916_send_pixels(struct st77916fb *fb, + const void *pixels, rt_size_t bytes) +{ + rt_uint8_t opcode; + + if (fb->pixel_data_lines == 1) + { + opcode = ST77916_QSPI_WRITE_CMD; + } + else if (fb->pixel_data_lines == 2) + { + opcode = ST77916_QSPI_WRITE_COLOR_DUAL; + } + else if (fb->pixel_data_lines == 4) + { + opcode = ST77916_QSPI_WRITE_COLOR_QUAD; + } + else + { + return -RT_EINVAL; + } + + if (fb->format->mode == RTGRAPHIC_PIXEL_FORMAT_RGB565) + { + const rt_uint8_t *src = pixels; + rt_uint8_t *dst = fb->transfer_buffer; + + if (!dst || bytes & 1) + { + return -RT_EINVAL; + } + + for (rt_size_t i = 0; i < bytes; i += 2) + { + dst[i] = src[i + 1]; + dst[i + 1] = src[i]; + } + + pixels = dst; + } + + return st77916_transfer(fb, opcode, 0x2c, pixels, + bytes, fb->pixel_data_lines); +} + +static rt_err_t st77916fb_plane_update(struct rt_graphic_plane *plane, + struct rt_device_rect_info *rect) +{ + rt_err_t err; + rt_uint8_t *pixels; + rt_uint32_t y; + struct st77916fb *fb = rt_container_of(plane->graphic, struct st77916fb, + parent); + + if (!rect->width || !rect->height || rect->x + rect->width > fb->width || + rect->y + rect->height > fb->height) + { + return -RT_EINVAL; + } + + pixels = fb->framebuffer + + (rect->y * fb->width + rect->x) * fb->format->bytes_per_pixel; + + if (fb->te.irq_enabled && (err = st77916_wait_te(fb)) != RT_EOK) + { + return err; + } + + if (rect->width == fb->width) + { + if ((err = st77916_set_window(fb, rect->x, rect->y, rect->width, + rect->height)) != RT_EOK) + { + return err; + } + + return st77916_send_pixels(fb, pixels, + rect->width * rect->height * fb->format->bytes_per_pixel); + } + + for (y = 0; y < rect->height; ++y) + { + if ((err = st77916_set_window(fb, rect->x, rect->y + y, + rect->width, 1)) != RT_EOK) + { + return err; + } + if ((err = st77916_send_pixels(fb, pixels, + rect->width * fb->format->bytes_per_pixel)) != RT_EOK) + { + return err; + } + pixels += fb->width * fb->format->bytes_per_pixel; + } + + return RT_EOK; +} + +static rt_err_t st77916fb_plane_fb_remap(struct rt_graphic_plane *plane, + rt_uint32_t mode, struct rt_device_rect_info *rect) +{ + struct st77916fb *fb = rt_container_of(plane->graphic, struct st77916fb, + parent); + + if (mode != fb->format->mode) + { + return -RT_EINVAL; + } + + plane->line_length = fb->width * fb->format->bytes_per_pixel; + plane->bits_per_pixel = rt_graphic_mode_bpp(mode); + plane->framebuffer = fb->framebuffer; + plane->screen_len = fb->width * fb->height * fb->format->bytes_per_pixel; + plane->framebuffer_len = plane->screen_len; + + return RT_EOK; +} + +static rt_err_t st77916fb_plane_prop_set(struct rt_graphic_plane *plane, + enum rt_graphic_plane_prop prop, void *value) +{ + struct st77916fb *fb = rt_container_of(plane->graphic, struct st77916fb, + parent); + rt_ubase_t rotation = (rt_ubase_t)value; + rt_uint8_t madctl = fb->initial_madctl; + struct rt_device_rect_info rect = {0}; + rt_err_t err; + + if (prop != RT_GRAPHIC_PLANE_PROP_ROTATE) + { + return -RT_ENOSYS; + } + if (rotation > RT_GRAPHIC_PLANE_ROTATE_270) + { + return -RT_EINVAL; + } + if ((rotation & 1) && fb->width != fb->height) + { + return -RT_ENOSYS; + } + + /* Compose clockwise rotation with the panel's initial address mapping. */ + switch (rotation) + { + case RT_GRAPHIC_PLANE_ROTATE_90: + madctl ^= ST77916_MADCTL_MV | + ((fb->initial_madctl & ST77916_MADCTL_MV) ? + ST77916_MADCTL_MY : ST77916_MADCTL_MX); + break; + case RT_GRAPHIC_PLANE_ROTATE_180: + madctl ^= ST77916_MADCTL_MX | ST77916_MADCTL_MY; + break; + case RT_GRAPHIC_PLANE_ROTATE_270: + madctl ^= ST77916_MADCTL_MV | + ((fb->initial_madctl & ST77916_MADCTL_MV) ? + ST77916_MADCTL_MX : ST77916_MADCTL_MY); + break; + default: + break; + } + + err = st77916_write_cmd(fb, ST77916_MADCTL, &madctl, sizeof(madctl)); + if (err != RT_EOK) + { + return err; + } + plane->rotate = rotation; + + /* Existing GRAM pixels must be rewritten using the new address order. */ + rect.width = fb->width; + rect.height = fb->height; + return st77916fb_plane_update(plane, &rect); +} + +static const struct rt_graphic_plane_ops st77916fb_plane_ops = +{ + .update = st77916fb_plane_update, + .fb_remap = st77916fb_plane_fb_remap, + .prop_set = st77916fb_plane_prop_set, +}; + +static const struct rt_graphic_device_ops st77916fb_ops = +{ + .dpms_switch = st77916fb_dpms_switch, + .wait_vsync = st77916fb_wait_vsync, +}; + +static rt_err_t st77916fb_probe(struct rt_spi_device *spi_dev) +{ + rt_err_t err; + const char *format_name = "r8g8b8"; + rt_uint32_t init_hz; + rt_uint32_t width = ST77916_DEFAULT_WIDTH; + rt_uint32_t height = ST77916_DEFAULT_HEIGHT; + rt_uint32_t pixel_data_lines = 4; + rt_size_t framebuffer_size; + rt_bool_t graphic_registered = RT_FALSE; + struct rt_device *dev = &spi_dev->parent; + struct st77916fb *fb = rt_calloc(1, sizeof(*fb)); + + if (!fb) + { + return -RT_ENOMEM; + } + + fb->qspi = rt_container_of(spi_dev, struct rt_qspi_device, parent); + fb->parent.parent.ofw_node = dev->ofw_node; + fb->reset.pin = PIN_NONE; + fb->te.pin = PIN_NONE; + fb->max_hz = ST77916_DEFAULT_MAX_HZ; + rt_dm_dev_prop_read_u32(dev, "width", &width); + rt_dm_dev_prop_read_u32(dev, "height", &height); + rt_dm_dev_prop_read_u32(dev, "spi-max-frequency", &fb->max_hz); + rt_dm_dev_prop_read_u32(dev, "spi-tx-bus-width", &pixel_data_lines); + rt_dm_dev_prop_read_string(dev, "format", &format_name); + fb->defer_display_on = rt_dm_dev_prop_read_bool(dev, + "rt-thread,defer-display-on"); + + for (rt_size_t i = 0; i < RT_ARRAY_SIZE(st77916_formats); ++i) + { + if (!rt_strcmp(format_name, st77916_formats[i].name)) + { + fb->format = &st77916_formats[i]; + break; + } + } + if (!fb->format) + { + LOG_E("unsupported pixel format: %s", format_name); + err = -RT_EINVAL; + goto _fail; + } + + if (pixel_data_lines != 1 && pixel_data_lines != 2 && pixel_data_lines != 4) + { + LOG_E("ST77916 requires a single-, dual-, or quad-data bus, got %u", + pixel_data_lines); + err = -RT_EINVAL; + goto _fail; + } + fb->width = width; + fb->height = height; + fb->pixel_data_lines = pixel_data_lines; + + fb->reset.pin = rt_pin_get_named_pin(dev, "reset", 0, RT_NULL, + &fb->reset.active_value); + if (fb->reset.pin < 0 && fb->reset.pin != -RT_EEMPTY) + { + err = fb->reset.pin; + goto _fail; + } + + fb->te.pin = rt_pin_get_named_pin(dev, "te", 0, RT_NULL, RT_NULL); + if (fb->te.pin < 0 && fb->te.pin != -RT_EEMPTY) + { + err = fb->te.pin; + goto _fail; + } + + if (fb->reset.pin >= 0) + { + rt_pin_mode(fb->reset.pin, PIN_MODE_OUTPUT); + } + if ((err = st77916_te_init(fb)) != RT_EOK) + { + goto _fail; + } + + init_hz = fb->max_hz; + if (init_hz > ST77916_INIT_MAX_HZ) + { + init_hz = ST77916_INIT_MAX_HZ; + } + if ((err = st77916_qspi_configure(fb, init_hz)) != RT_EOK) + { + goto _fail; + } + + st77916_reset(fb); + if ((err = st77916_run_init_sequence(fb)) != RT_EOK) + { + goto _fail; + } + fb->panel_enabled = !fb->defer_display_on; + if ((err = st77916_qspi_configure(fb, fb->max_hz)) != RT_EOK) + { + goto _fail; + } + + framebuffer_size = fb->width * fb->height * fb->format->bytes_per_pixel; + fb->framebuffer = rt_calloc(1, framebuffer_size); + if (!fb->framebuffer) + { + err = -RT_ENOMEM; + goto _fail; + } + if (fb->format->mode == RTGRAPHIC_PIXEL_FORMAT_RGB565) + { + fb->transfer_buffer = rt_malloc(framebuffer_size); + if (!fb->transfer_buffer) + { + err = -RT_ENOMEM; + goto _fail; + } + } + + /* Clear panel GRAM before the generic logo updates its centre rectangle. */ + if ((err = st77916_set_window(fb, 0, 0, fb->width, fb->height)) != RT_EOK || + (err = st77916_send_pixels(fb, fb->framebuffer, + framebuffer_size)) != RT_EOK) + { + goto _fail; + } + + fb->modes[0] = fb->format->mode; + if (fb->te.pin >= 0) + { + fb->parent.ops = &st77916fb_ops; + } + if ((err = rt_graphic_device_simple_register(&fb->parent, fb->width, + fb->height, 0, &st77916fb_plane_ops, fb->modes, + RT_ARRAY_SIZE(fb->modes))) != RT_EOK) + { + goto _fail; + } + graphic_registered = RT_TRUE; + + if (fb->defer_display_on) + { + if ((err = st77916_write_cmd(fb, 0x29, RT_NULL, 0)) != RT_EOK) + { + goto _fail; + } + + fb->panel_enabled = RT_TRUE; + if ((err = st77916fb_dpms_switch(&fb->parent, + RT_GRAPHIC_DPMS_ON)) != RT_EOK) + { + goto _fail; + } + } + + spi_dev->parent.user_data = fb; + return RT_EOK; + +_fail: + if (graphic_registered) + { + rt_graphic_device_simple_unregister(&fb->parent); + } + st77916_te_deinit(fb); + rt_free(fb->transfer_buffer); + rt_free(fb->framebuffer); + rt_free(fb); + + return err; +} + +static rt_err_t st77916fb_remove(struct rt_spi_device *spi_dev) +{ + struct st77916fb *fb = spi_dev->parent.user_data; + + st77916_te_deinit(fb); + st77916_write_cmd(fb, 0x28, RT_NULL, 0); + rt_graphic_device_simple_unregister(&fb->parent); + rt_free(fb->transfer_buffer); + rt_free(fb->framebuffer); + rt_free(fb); + + return RT_EOK; +} + +static const struct rt_ofw_node_id st77916fb_ofw_ids[] = +{ + { .compatible = "sitronix,st77916" }, + { /* sentinel */ }, +}; + +static struct rt_spi_driver st77916fb_driver = +{ + .ofw_ids = st77916fb_ofw_ids, + .probe = st77916fb_probe, + .remove = st77916fb_remove, +}; +RT_SPI_DRIVER_EXPORT(st77916fb_driver); diff --git a/components/drivers/graphic/graphic_simple.c b/components/drivers/graphic/graphic_simple.c index 141fc9d91315..a6dc6fd06188 100644 --- a/components/drivers/graphic/graphic_simple.c +++ b/components/drivers/graphic/graphic_simple.c @@ -283,7 +283,11 @@ rt_err_t rt_graphic_device_simple_register(struct rt_graphic_device *gdev, _free_plane: if (err) { - rt_free(plane); + if (gdev->primary_plane == plane) + { + rt_graphic_device_del_plane(gdev, plane); + } + rt_graphic_device_free_plane(plane); } return err; diff --git a/components/drivers/graphic/logo/logo.c b/components/drivers/graphic/logo/logo.c index 34597c8c03a6..449846494d64 100644 --- a/components/drivers/graphic/logo/logo.c +++ b/components/drivers/graphic/logo/logo.c @@ -186,7 +186,7 @@ rt_err_t rt_graphic_logo_render(struct rt_graphic_device *gdev) rt_ubase_t color = color_reordering(logo[0], red_off, logo[1], green_off, logo[2], blue_off, - fb_color_max, startup_logo_color_max) | + startup_logo_color_max, fb_color_max) | none_alpha; rt_memcpy(fb, &color, bytes_per_pixel); diff --git a/components/drivers/input/touchscreen/Kconfig b/components/drivers/input/touchscreen/Kconfig index 4710c6f29f47..9263e27fdf3e 100644 --- a/components/drivers/input/touchscreen/Kconfig +++ b/components/drivers/input/touchscreen/Kconfig @@ -21,6 +21,14 @@ config RT_INPUT_TOUCHSCREEN_GOODIX depends on RT_USING_REGULATOR default n +config RT_INPUT_TOUCHSCREEN_CST3530 + bool "CST3530 capacitive touch screen support" + depends on RT_INPUT_TOUCHSCREEN + depends on RT_USING_I2C + depends on RT_USING_OFW + depends on RT_USING_PIN + default n + if RT_INPUT_TOUCHSCREEN osource "$(SOC_DM_INPUT_TOUCHSCREEN_DIR)/Kconfig" endif diff --git a/components/drivers/input/touchscreen/SConscript b/components/drivers/input/touchscreen/SConscript index 6efa769291cd..9b6c150c4c1f 100644 --- a/components/drivers/input/touchscreen/SConscript +++ b/components/drivers/input/touchscreen/SConscript @@ -16,6 +16,9 @@ if GetDepend(['RT_INPUT_TOUCHSCREEN_ADS7846']): if GetDepend(['RT_INPUT_TOUCHSCREEN_GOODIX']): src += ['ts-goodix.c'] +if GetDepend(['RT_INPUT_TOUCHSCREEN_CST3530']): + src += ['ts-cst3530.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/input/touchscreen/ts-cst3530.c b/components/drivers/input/touchscreen/ts-cst3530.c new file mode 100644 index 000000000000..81b348ee6e9b --- /dev/null +++ b/components/drivers/input/touchscreen/ts-cst3530.c @@ -0,0 +1,476 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-10-07 GuEe-GUI the first version + */ + +#include +#include +#include + +#define DBG_TAG "input.ts.cst3530" +#define DBG_LVL DBG_INFO +#include + +#define CST3530_MAX_CONTACTS 5 +#define CST3530_MAX_WIDTH 4096 +#define CST3530_MAX_HEIGHT 4096 +#define CST3530_REPORT_SIZE 64 +#define CST3530_POLL_INTERVAL_MS 20 + +#define CST3530_DATA_START_REG 0xd0070000U +#define CST3530_DATA_CLEAR_REG 0xd00002abU + +struct cst3530_ts +{ + struct rt_input_device parent; + struct rt_i2c_client *client; + + rt_ssize_t irq_pin; + rt_ssize_t reset_pin; + rt_uint8_t irq_mode; + rt_uint8_t reset_active; + rt_bool_t active_slots[CST3530_MAX_CONTACTS]; + rt_bool_t use_irq; + rt_bool_t irq_sem_inited; + + struct rt_thread *worker; + struct rt_semaphore irq_sem; +}; + +static rt_err_t cst3530_i2c_read(struct cst3530_ts *ts, rt_uint32_t reg, + rt_uint8_t *buf, rt_size_t len) +{ + rt_uint8_t address[] = { + (rt_uint8_t)(reg >> 24), + (rt_uint8_t)(reg >> 16), + (rt_uint8_t)(reg >> 8), + (rt_uint8_t)reg, + }; + struct rt_i2c_msg msgs[] = { + { + .addr = ts->client->client_addr, + .flags = RT_I2C_WR, + .buf = address, + .len = sizeof(address), + }, + { + .addr = ts->client->client_addr, + .flags = RT_I2C_RD, + .buf = buf, + .len = len, + }, + }; + rt_ssize_t res; + + res = rt_i2c_transfer(ts->client->bus, msgs, RT_ARRAY_SIZE(msgs)); + + if (res < 0) + { + return res; + } + + return res == RT_ARRAY_SIZE(msgs) ? RT_EOK : -RT_EIO; +} + +static rt_err_t cst3530_i2c_write_address(struct cst3530_ts *ts, rt_uint32_t reg) +{ + rt_uint8_t address[] = { + (rt_uint8_t)(reg >> 24), + (rt_uint8_t)(reg >> 16), + (rt_uint8_t)(reg >> 8), + (rt_uint8_t)reg, + }; + struct rt_i2c_msg msg = { + .addr = ts->client->client_addr, + .flags = RT_I2C_WR, + .buf = address, + .len = sizeof(address), + }; + rt_ssize_t res; + + res = rt_i2c_transfer(ts->client->bus, &msg, 1); + + if (res < 0) + { + return res; + } + + return res == 1 ? RT_EOK : -RT_EIO; +} + +static void cst3530_release_unused_slots(struct cst3530_ts *ts, + const rt_bool_t *seen_slots) +{ + int slot; + + for (slot = 0; slot < CST3530_MAX_CONTACTS; ++slot) + { + if (ts->active_slots[slot] && !seen_slots[slot]) + { + rt_input_report_touch_slot(&ts->parent, slot); + rt_input_event(&ts->parent, EV_ABS, ABS_MT_TRACKING_ID, -1); + ts->active_slots[slot] = RT_FALSE; + } + } +} + +static rt_err_t cst3530_process_events(struct cst3530_ts *ts) +{ + rt_uint8_t report[CST3530_REPORT_SIZE]; + rt_bool_t seen_slots[CST3530_MAX_CONTACTS] = { RT_FALSE }; + rt_uint8_t report_type; + rt_uint8_t contacts; + rt_uint8_t key_count; + int point; + rt_err_t err; + + err = cst3530_i2c_read(ts, CST3530_DATA_START_REG, report, sizeof(report)); + if (err) + { + return err; + } + + /* The controller requires an address-only write after each report read. */ + err = cst3530_i2c_write_address(ts, CST3530_DATA_CLEAR_REG); + if (err) + { + LOG_D("failed to acknowledge touch report: %s", rt_strerror(err)); + } + + report_type = report[2]; + contacts = report[3] & 0x0f; + key_count = report[3] >> 4; + + if (report_type != 0xff || contacts > CST3530_MAX_CONTACTS) + { + contacts = 0; + } + + for (point = 0; point < contacts; ++point) + { + rt_uint32_t x; + rt_uint32_t y; + rt_uint32_t strength; + rt_size_t offset = (key_count + point) * 5; + + if (offset + 7 >= sizeof(report)) + { + break; + } + + x = report[offset + 4] | ((report[offset + 7] & 0x0f) << 8); + y = report[offset + 5] | ((report[offset + 7] & 0xf0) << 4); + strength = report[offset + 6]; + + if (!strength || x >= CST3530_MAX_WIDTH || y >= CST3530_MAX_HEIGHT) + { + continue; + } + + rt_input_parse_touch_position(&ts->parent, &x, &y); + rt_input_report_touch_slot(&ts->parent, point); + rt_input_event(&ts->parent, EV_ABS, ABS_MT_TRACKING_ID, point); + rt_input_report_touch_position(&ts->parent, x, y, RT_TRUE); + rt_input_report_abs(&ts->parent, ABS_MT_TOUCH_MAJOR, strength); + rt_input_report_abs(&ts->parent, ABS_MT_WIDTH_MAJOR, strength); + ts->active_slots[point] = RT_TRUE; + seen_slots[point] = RT_TRUE; + } + + cst3530_release_unused_slots(ts, seen_slots); + rt_input_sync(&ts->parent); + + return RT_EOK; +} + +static void cst3530_handle_events(struct cst3530_ts *ts) +{ + rt_err_t err; + + err = cst3530_process_events(ts); + if (err) + { + LOG_D("failed to read touch report: %s", rt_strerror(err)); + } +} + +static void cst3530_worker_entry(void *parameter) +{ + struct cst3530_ts *ts = parameter; + + while (RT_TRUE) + { + rt_sem_take(&ts->irq_sem, RT_WAITING_FOREVER); + cst3530_handle_events(ts); + } +} + +static void cst3530_irq_callback(void *parameter) +{ + struct cst3530_ts *ts = parameter; + + rt_sem_release(&ts->irq_sem); +} + +static void cst3530_poll(struct rt_input_device *idev) +{ + struct cst3530_ts *ts = rt_container_of(idev, struct cst3530_ts, parent); + + cst3530_handle_events(ts); +} + +static rt_err_t cst3530_reset(struct cst3530_ts *ts) +{ + if (ts->reset_pin < 0) + { + return RT_EOK; + } + + rt_pin_mode(ts->reset_pin, PIN_MODE_OUTPUT); + rt_pin_write(ts->reset_pin, ts->reset_active); + rt_thread_mdelay(20); + rt_pin_write(ts->reset_pin, !ts->reset_active); + rt_thread_mdelay(200); + + return RT_EOK; +} + +static rt_err_t cst3530_setup_irq(struct cst3530_ts *ts) +{ + rt_err_t err; + + if (ts->irq_pin < 0) + { + return -RT_ENOSYS; + } + + rt_pin_mode(ts->irq_pin, PIN_MODE_INPUT); + + err = rt_pin_attach_irq(ts->irq_pin, ts->irq_mode, cst3530_irq_callback, ts); + if (err) + { + return err; + } + + err = rt_pin_irq_enable(ts->irq_pin, RT_TRUE); + if (err) + { + rt_pin_detach_irq(ts->irq_pin); + } + + return err; +} + +static rt_err_t cst3530_setup_polling(struct cst3530_ts *ts) +{ + rt_err_t err; + + err = rt_input_setup_polling(&ts->parent, cst3530_poll); + if (err) + { + return err; + } + + return rt_input_set_poll_interval(&ts->parent, CST3530_POLL_INTERVAL_MS); +} + +static void cst3530_destroy(struct cst3530_ts *ts) +{ + if (ts->use_irq && ts->irq_pin >= 0) + { + rt_pin_irq_enable(ts->irq_pin, RT_FALSE); + rt_pin_detach_irq(ts->irq_pin); + } + + if (ts->worker) + { + rt_thread_delete(ts->worker); + } + + if (ts->irq_sem_inited) + { + rt_sem_detach(&ts->irq_sem); + } +} + +static rt_err_t cst3530_probe(struct rt_i2c_client *client) +{ + struct rt_device *dev = &client->parent; + struct rt_touch_info touch_info = {}; + struct cst3530_ts *ts; + rt_uint8_t irq_active = 0; + rt_uint8_t report[CST3530_REPORT_SIZE]; + rt_err_t err; + + ts = rt_calloc(1, sizeof(*ts)); + if (!ts) + { + return -RT_ENOMEM; + } + + ts->client = client; + ts->irq_pin = rt_pin_get_named_pin(dev, "interrupt", 0, RT_NULL, &irq_active); + ts->reset_pin = rt_pin_get_named_pin(dev, "reset", 0, RT_NULL, &ts->reset_active); + ts->irq_mode = irq_active ? PIN_IRQ_MODE_RISING : PIN_IRQ_MODE_FALLING; + ts->parent.parent.ofw_node = dev->ofw_node; + dev->user_data = ts; + + if ((ts->irq_pin < 0 && ts->irq_pin != PIN_NONE) || + (ts->reset_pin < 0 && ts->reset_pin != PIN_NONE)) + { + err = -RT_EINVAL; + goto _free; + } + + if ((err = cst3530_reset(ts))) + { + goto _free; + } + + /* CST3530 has no public chip-ID register; an idle report is its probe. */ + if ((err = cst3530_i2c_read(ts, CST3530_DATA_START_REG, report, sizeof(report)))) + { + LOG_E("I2C communication failed at 0x%02x: %s", + client->client_addr, rt_strerror(err)); + goto _free; + } + cst3530_i2c_write_address(ts, CST3530_DATA_CLEAR_REG); + + err = rt_input_set_capability(&ts->parent, EV_ABS, ABS_MT_SLOT); + err |= rt_input_set_capability(&ts->parent, EV_ABS, ABS_MT_TRACKING_ID); + err |= rt_input_set_capability(&ts->parent, EV_ABS, ABS_MT_POSITION_X); + err |= rt_input_set_capability(&ts->parent, EV_ABS, ABS_MT_POSITION_Y); + err |= rt_input_set_capability(&ts->parent, EV_ABS, ABS_MT_TOUCH_MAJOR); + err |= rt_input_set_capability(&ts->parent, EV_ABS, ABS_MT_WIDTH_MAJOR); + if (err) + { + goto _free; + } + + rt_input_set_absinfo(&ts->parent, ABS_MT_POSITION_X, 0, CST3530_MAX_WIDTH - 1, 0, 0); + rt_input_set_absinfo(&ts->parent, ABS_MT_POSITION_Y, 0, CST3530_MAX_HEIGHT - 1, 0, 0); + rt_input_set_absinfo(&ts->parent, ABS_MT_TOUCH_MAJOR, 0, RT_UINT8_MAX, 0, 0); + rt_input_set_absinfo(&ts->parent, ABS_MT_WIDTH_MAJOR, 0, RT_UINT8_MAX, 0, 0); + + touch_info.type = RT_TOUCH_TYPE_CAPACITANCE; + touch_info.vendor = RT_TOUCH_VENDOR_UNKNOWN; + touch_info.point_num = CST3530_MAX_CONTACTS; + + err = rt_input_setup_touch(&ts->parent, CST3530_MAX_CONTACTS, &touch_info); + if (err) + { + goto _free; + } + + err = rt_sem_init(&ts->irq_sem, "cst3530", 0, RT_IPC_FLAG_FIFO); + if (!err) + { + ts->irq_sem_inited = RT_TRUE; + err = cst3530_setup_irq(ts); + } + + if (err) + { + if (ts->irq_sem_inited) + { + rt_sem_detach(&ts->irq_sem); + ts->irq_sem_inited = RT_FALSE; + } + + err = cst3530_setup_polling(ts); + if (err) + { + goto _remove_input; + } + } + else + { + ts->use_irq = RT_TRUE; + ts->worker = rt_thread_create(rt_dm_dev_get_name(dev), cst3530_worker_entry, + ts, DM_THREAD_STACK_SIZE, + RT_THREAD_PRIORITY_MAX / 2, 10); + if (!ts->worker) + { + rt_pin_irq_enable(ts->irq_pin, RT_FALSE); + rt_pin_detach_irq(ts->irq_pin); + ts->use_irq = RT_FALSE; + rt_sem_detach(&ts->irq_sem); + ts->irq_sem_inited = RT_FALSE; + + if ((err = cst3530_setup_polling(ts))) + { + goto _remove_input; + } + } + } + + err = rt_input_device_register(&ts->parent); + if (err) + { + goto _remove_input; + } + + if (ts->worker) + { + rt_thread_startup(ts->worker); + } + + LOG_I("CST3530 touch ready at 0x%02x (%s)", client->client_addr, + ts->use_irq ? "interrupt" : "polling"); + + return RT_EOK; + +_remove_input: + cst3530_destroy(ts); + rt_input_remove_config(&ts->parent); +_free: + if (dev->user_data == ts) + { + dev->user_data = RT_NULL; + } + rt_free(ts); + + return err; +} + +static rt_err_t cst3530_remove(struct rt_i2c_client *client) +{ + struct cst3530_ts *ts = client->parent.user_data; + + if (!ts) + { + return RT_EOK; + } + + client->parent.user_data = RT_NULL; + cst3530_destroy(ts); + rt_input_device_unregister(&ts->parent); + rt_free(ts); + + return RT_EOK; +} + +static const struct rt_i2c_device_id cst3530_ids[] = { + { .name = "cst3530" }, + { /* sentinel */ }, +}; + +static const struct rt_ofw_node_id cst3530_ofw_ids[] = { + { .compatible = "cst,cst3530" }, + { .compatible = "chipsea,cst3530" }, + { /* sentinel */ }, +}; + +static struct rt_i2c_driver cst3530_driver = { + .ids = cst3530_ids, + .ofw_ids = cst3530_ofw_ids, + + .probe = cst3530_probe, + .remove = cst3530_remove, +}; +RT_I2C_DRIVER_EXPORT(cst3530_driver); diff --git a/components/drivers/misc/Kconfig b/components/drivers/misc/Kconfig index 71f42ee67bf6..b28ff72ee707 100644 --- a/components/drivers/misc/Kconfig +++ b/components/drivers/misc/Kconfig @@ -26,7 +26,7 @@ config RT_USING_PULSE_ENCODER bool "Using PULSE ENCODER device drivers" default n -config RT_USING_INPUT_CAPTURE +menuconfig RT_USING_INPUT_CAPTURE bool "Using INPUT CAPTURE device drivers" default n @@ -34,4 +34,8 @@ if RT_USING_INPUT_CAPTURE config RT_INPUT_CAPTURE_RB_SIZE int "Set input capture ringbuffer size" default 100 + +if RT_USING_DM + osource "$(SOC_DM_INPUT_CAPTURE_DIR)/Kconfig" +endif endif diff --git a/components/drivers/serial/device/8250/8250-dw.c b/components/drivers/serial/device/8250/8250-dw.c index b482f05b38f0..5ea1b7742d26 100644 --- a/components/drivers/serial/device/8250/8250-dw.c +++ b/components/drivers/serial/device/8250/8250-dw.c @@ -56,6 +56,7 @@ struct dw8250 struct rt_spinlock spinlock; struct rt_clk *pclk; + struct rt_reset_control *rstc; rt_bool_t uart_16550_compatible; struct dw8250_platform_data *platform_data; @@ -173,6 +174,11 @@ static void dw8250_free_resource(struct dw8250 *dw8250) rt_clk_put(dw8250->pclk); } + if (!rt_is_err_or_null(dw8250->rstc)) + { + rt_reset_control_put(dw8250->rstc); + } + rt_free(dw8250); } @@ -248,6 +254,19 @@ static rt_err_t dw8250_probe(struct rt_platform_device *pdev) goto _free_res; } + dw8250->rstc = rt_reset_control_get_by_index(dev, 0); + + if (rt_is_err(dw8250->rstc)) + { + err = rt_ptr_err(dw8250->rstc); + goto _free_res; + } + + if (dw8250->rstc && (err = rt_reset_control_deassert(dw8250->rstc))) + { + goto _free_res; + } + if (!rt_dm_dev_prop_read_u32(dev, "reg-io-width", &val) && val == 4) { serial->iotype = PORT_MMIO32; diff --git a/components/drivers/serial/device/8250/Kconfig b/components/drivers/serial/device/8250/Kconfig index f482731bfd00..66b6dd5a60ae 100644 --- a/components/drivers/serial/device/8250/Kconfig +++ b/components/drivers/serial/device/8250/Kconfig @@ -5,6 +5,7 @@ menuconfig RT_SERIAL_8250 config RT_SERIAL_8250_DW bool "Synopsys DesignWare 8250" depends on RT_SERIAL_8250 + depends on RT_USING_RESET default n config RT_SERIAL_8250_PCI diff --git a/components/drivers/spi/dev_qspi_core.c b/components/drivers/spi/dev_qspi_core.c index aa536af4dfd7..b0622566d733 100644 --- a/components/drivers/spi/dev_qspi_core.c +++ b/components/drivers/spi/dev_qspi_core.c @@ -53,6 +53,12 @@ rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configu } /* copy configuration items */ + /* + * QSPI transfers share the SPI bus ownership path. Keep its base SPI + * configuration in sync, because the first transfer may configure the + * controller through device->parent.config. + */ + device->parent.config = cfg->parent; device->config.parent.mode = cfg->parent.mode; device->config.parent.max_hz = cfg->parent.max_hz; device->config.parent.data_width = cfg->parent.data_width; diff --git a/components/drivers/thermal/Kconfig b/components/drivers/thermal/Kconfig index 3cbb14b8bd50..30b3a4c259ec 100644 --- a/components/drivers/thermal/Kconfig +++ b/components/drivers/thermal/Kconfig @@ -1,6 +1,7 @@ menuconfig RT_USING_THERMAL bool "Using Thermal Management device drivers" depends on RT_USING_DM + select RT_USING_SYSTEM_WORKQUEUE default n if RT_USING_THERMAL diff --git a/components/drivers/usb/Kconfig b/components/drivers/usb/Kconfig index cd8e4ebf869f..84abdda9ad44 100644 --- a/components/drivers/usb/Kconfig +++ b/components/drivers/usb/Kconfig @@ -1 +1,5 @@ rsource "cherryusb/Kconfig" + +if RT_USING_CHERRYUSB + osource "$(SOC_DM_USB_DIR)/Kconfig" +endif diff --git a/components/drivers/usb/cherryusb/port/musb/usb_glue_sunxi.c b/components/drivers/usb/cherryusb/port/musb/usb_glue_sunxi.c index 6a12bab60aa8..02aea8da8962 100644 --- a/components/drivers/usb/cherryusb/port/musb/usb_glue_sunxi.c +++ b/components/drivers/usb/cherryusb/port/musb/usb_glue_sunxi.c @@ -7,6 +7,8 @@ #include "stdint.h" #include "usb_musb_reg.h" +#include + #ifndef CONFIG_USB_MUSB_SUNXI #error must define CONFIG_USB_MUSB_SUNXI when use sunxi chips #endif @@ -60,5 +62,5 @@ uint32_t usb_get_musb_ram_size(void) void usbd_musb_delay_ms(uint8_t ms) { - /* implement later */ + rt_thread_mdelay(ms); } From 9b6ad7ee4817ddcbd197d83baacc97c24ec12743 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Sat, 24 Oct 2026 01:05:03 +0800 Subject: [PATCH 2/2] [bsp][allwinner] Add H616 BSP and driver support Add initial RT-Thread support for Allwinner H616 boards - add the AArch64 H616 BSP with MMU, SMP, PSCI, and external DTB boot - add H616 DM drivers for CCU/reset, pinctrl, thermal, CPU DVFS, codec, AHUB HDMI audio, CE hwcrypto, DE33 HDMI framebuffer, and TCON TOP - add support for H616 RTC, DMA, SDIO, I2C, SPI, PWM, watchdog, IR, SID NVMEM, GPIO LEDs, JPEG decode, G2D, GPADC, and LRADC - add AXP313A regulator support - extend shared Sunxi SDIO, G2D, and JPEG drivers for H616 resources Signed-off-by: GuEe-GUI <2991707448@qq.com> --- bsp/README.md | 1 + bsp/allwinner/dm/adc/Kconfig | 13 + bsp/allwinner/dm/adc/SConscript | 3 + bsp/allwinner/dm/adc/adc-sunxi-gpadc.c | 2 + bsp/allwinner/dm/adc/input-sunxi-lradc.c | 283 ++++ bsp/allwinner/dm/audio/Kconfig | 35 + bsp/allwinner/dm/audio/SConscript | 11 +- .../dm/audio/audio-sun50i-h616-ahub.c | 455 ++++++ .../dm/audio/audio-sun50i-h616-codec.c | 340 ++++ bsp/allwinner/dm/clk/Kconfig | 20 + bsp/allwinner/dm/clk/SConscript | 4 + bsp/allwinner/dm/clk/clk-sun50i-h616-de33.c | 304 ++++ bsp/allwinner/dm/clk/clk-sun50i-h616.c | 1413 +++++++++++++++++ bsp/allwinner/dm/clk/clk-sunxi-tcon-top.c | 176 ++ bsp/allwinner/dm/dma/dma-sunxi.c | 86 +- bsp/allwinner/dm/dvfs/cpufreq/Kconfig | 10 + bsp/allwinner/dm/dvfs/cpufreq/SConscript | 3 + .../dvfs/cpufreq/dvfs-sun50i-h616-cpufreq.c | 198 +++ bsp/allwinner/dm/graphic/framebuffer/Kconfig | 16 + .../dm/graphic/framebuffer/SConscript | 3 + .../graphic/framebuffer/fb-sun50i-h616-de33.c | 1261 +++++++++++++++ bsp/allwinner/dm/hwcrypto/Kconfig | 16 + bsp/allwinner/dm/hwcrypto/SConscript | 3 + .../dm/hwcrypto/hwcrypto-sun50i-h616-ce.c | 407 +++++ bsp/allwinner/dm/i2c/i2c-sunxi.c | 30 +- bsp/allwinner/dm/include/sunxi-h616-hdmi.h | 16 + bsp/allwinner/dm/include/sunxi_pinctrl.h | 5 + bsp/allwinner/dm/include/sunxi_tcon_top.h | 10 + .../dm/inputcapture/inputcapture-sunxi-irrx.c | 73 +- bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c | 49 +- bsp/allwinner/dm/pinctrl/Kconfig | 9 + bsp/allwinner/dm/pinctrl/SConscript | 8 +- .../dm/pinctrl/pinctrl-sun50i-h616.c | 93 ++ bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c | 78 +- bsp/allwinner/dm/pwm/pwm-sunxi.c | 21 +- bsp/allwinner/dm/rtc/rtc-sunxi.c | 172 +- bsp/allwinner/dm/sdio/sdio-sunxi.c | 181 ++- bsp/allwinner/dm/soc/Kconfig | 12 +- bsp/allwinner/dm/soc/g2d/g2d-sunxi.c | 4 +- bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h | 1 + bsp/allwinner/dm/soc/jpegd-sunxi.c | 75 +- bsp/allwinner/dm/spi/spi-sunxi.c | 1 + bsp/allwinner/dm/thermal/Kconfig | 9 + bsp/allwinner/dm/thermal/SConscript | 3 + .../dm/thermal/thermal-sun50i-h616.c | 286 ++++ bsp/allwinner/dm/watchdog/watchdog-sunxi.c | 67 +- bsp/allwinner/h616/.config | 672 ++++++++ bsp/allwinner/h616/Kconfig | 29 + bsp/allwinner/h616/README.md | 97 ++ bsp/allwinner/h616/README_zh.md | 90 ++ bsp/allwinner/h616/SConscript | 16 + bsp/allwinner/h616/SConstruct | 29 + bsp/allwinner/h616/applications/SConscript | 5 + bsp/allwinner/h616/applications/main.c | 13 + bsp/allwinner/h616/board/SConscript | 5 + bsp/allwinner/h616/board/board.c | 12 + bsp/allwinner/h616/board/board.h | 15 + bsp/allwinner/h616/rtconfig.h | 395 +++++ bsp/allwinner/h616/rtconfig.py | 75 + components/drivers/regulator/Kconfig | 8 + components/drivers/regulator/SConscript | 3 + .../drivers/regulator/regulator-axp313a.c | 566 +++++++ .../drivers/serial/device/8250/8250-dw.c | 11 +- 63 files changed, 8182 insertions(+), 125 deletions(-) create mode 100644 bsp/allwinner/dm/adc/input-sunxi-lradc.c create mode 100644 bsp/allwinner/dm/audio/audio-sun50i-h616-ahub.c create mode 100644 bsp/allwinner/dm/audio/audio-sun50i-h616-codec.c create mode 100644 bsp/allwinner/dm/clk/clk-sun50i-h616-de33.c create mode 100644 bsp/allwinner/dm/clk/clk-sun50i-h616.c create mode 100644 bsp/allwinner/dm/clk/clk-sunxi-tcon-top.c create mode 100644 bsp/allwinner/dm/dvfs/cpufreq/dvfs-sun50i-h616-cpufreq.c create mode 100644 bsp/allwinner/dm/graphic/framebuffer/fb-sun50i-h616-de33.c create mode 100644 bsp/allwinner/dm/hwcrypto/hwcrypto-sun50i-h616-ce.c create mode 100644 bsp/allwinner/dm/include/sunxi-h616-hdmi.h create mode 100644 bsp/allwinner/dm/include/sunxi_tcon_top.h create mode 100644 bsp/allwinner/dm/pinctrl/pinctrl-sun50i-h616.c create mode 100644 bsp/allwinner/dm/thermal/thermal-sun50i-h616.c create mode 100644 bsp/allwinner/h616/.config create mode 100644 bsp/allwinner/h616/Kconfig create mode 100644 bsp/allwinner/h616/README.md create mode 100644 bsp/allwinner/h616/README_zh.md create mode 100644 bsp/allwinner/h616/SConscript create mode 100644 bsp/allwinner/h616/SConstruct create mode 100644 bsp/allwinner/h616/applications/SConscript create mode 100644 bsp/allwinner/h616/applications/main.c create mode 100644 bsp/allwinner/h616/board/SConscript create mode 100644 bsp/allwinner/h616/board/board.c create mode 100644 bsp/allwinner/h616/board/board.h create mode 100644 bsp/allwinner/h616/rtconfig.h create mode 100644 bsp/allwinner/h616/rtconfig.py create mode 100644 components/drivers/regulator/regulator-axp313a.c diff --git a/bsp/README.md b/bsp/README.md index e55876f770c6..d70ded4693b7 100644 --- a/bsp/README.md +++ b/bsp/README.md @@ -827,6 +827,7 @@ This document is based on the RT-Thread mainline repository and categorizes the | [d1](allwinner/d1) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [d1s](allwinner/d1s) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [f101](allwinner/f101) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [h616](allwinner/h616) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | #### 🟡 K210 (SMP) diff --git a/bsp/allwinner/dm/adc/Kconfig b/bsp/allwinner/dm/adc/Kconfig index 99a61238c92f..ddb7fad7da04 100644 --- a/bsp/allwinner/dm/adc/Kconfig +++ b/bsp/allwinner/dm/adc/Kconfig @@ -7,3 +7,16 @@ config RT_ADC_SUNXI_GPADC default n help Enable the General Purpose ADC found on supported Allwinner SoCs. + +config RT_INPUT_SUNXI_LRADC + bool "Allwinner Sunxi LRADC ladder keys" + depends on RT_USING_INPUT + depends on RT_USING_CLK + depends on RT_USING_DM + depends on RT_USING_OFW + depends on RT_USING_RESET + default n + help + Enable the low-resolution ADC keypad controller found on supported + Allwinner SoCs. The driver reads key voltage/code child nodes from the + LRADC device-tree node and registers one input device. diff --git a/bsp/allwinner/dm/adc/SConscript b/bsp/allwinner/dm/adc/SConscript index 7fbdb550f4d5..8189c28e4e8b 100644 --- a/bsp/allwinner/dm/adc/SConscript +++ b/bsp/allwinner/dm/adc/SConscript @@ -7,6 +7,9 @@ CPPPATH = [cwd + '/../include'] if GetDepend(['RT_ADC_SUNXI_GPADC']): src += ['adc-sunxi-gpadc.c'] +if GetDepend(['RT_INPUT_SUNXI_LRADC']): + src += ['input-sunxi-lradc.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/bsp/allwinner/dm/adc/adc-sunxi-gpadc.c b/bsp/allwinner/dm/adc/adc-sunxi-gpadc.c index 5616553ccdfe..db837ba743b3 100644 --- a/bsp/allwinner/dm/adc/adc-sunxi-gpadc.c +++ b/bsp/allwinner/dm/adc/adc-sunxi-gpadc.c @@ -298,6 +298,8 @@ static rt_err_t sunxi_gpadc_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_gpadc_ofw_ids[] = { { .compatible = "allwinner,sun252i-f101-gpadc" }, + { .compatible = "allwinner,sun50i-h616-gpadc" }, + { .compatible = "allwinner,sun20i-d1-gpadc" }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/adc/input-sunxi-lradc.c b/bsp/allwinner/dm/adc/input-sunxi-lradc.c new file mode 100644 index 000000000000..8fa34db7429c --- /dev/null +++ b/bsp/allwinner/dm/adc/input-sunxi-lradc.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define DBG_TAG "input.sunxi-lradc" +#define DBG_LVL DBG_INFO +#include + +#define LRADC_CTRL 0x00 +#define LRADC_INTC 0x04 +#define LRADC_INTS 0x08 +#define LRADC_DATA0 0x0c + +#define LRADC_FIRST_CONVERT_DLY(n) ((n) << 24) +#define LRADC_LEVELA_B_CNT(n) ((n) << 8) +#define LRADC_HOLD_EN RT_BIT(6) +#define LRADC_SAMPLE_RATE(n) ((n) << 2) +#define LRADC_ENABLE RT_BIT(0) + +#define LRADC_CHAN0_KEYUP RT_BIT(4) +#define LRADC_CHAN0_KEYDOWN RT_BIT(1) +#define LRADC_DATA_MASK RT_GENMASK(5, 0) +#define LRADC_DATA_MAX 63U +#define LRADC_DEFAULT_VREF_MV 1800U + +struct sunxi_lradc_key +{ + rt_uint32_t voltage_mv; + rt_uint32_t code; +}; + +struct sunxi_lradc +{ + struct rt_input_device parent; + + void *base; + struct rt_clk *clk; + struct rt_reset_control *reset; + struct sunxi_lradc_key *keys; + int irq; + rt_uint32_t keys_nr; + rt_uint32_t vref_mv; + rt_uint32_t pressed_code; + rt_bool_t clk_enabled; + rt_bool_t irq_attached; + rt_bool_t registered; +}; + +static rt_uint32_t sunxi_lradc_find_key(struct sunxi_lradc *lradc, + rt_uint32_t voltage_mv) +{ + rt_uint32_t key = 0; + rt_uint32_t closest = RT_UINT32_MAX; + rt_uint32_t index; + + for (index = 0; index < lradc->keys_nr; ++index) + { + rt_uint32_t expected = lradc->keys[index].voltage_mv; + rt_uint32_t difference = expected > voltage_mv ? + expected - voltage_mv : voltage_mv - expected; + + if (difference < closest) + { + closest = difference; + key = lradc->keys[index].code; + } + } + + return key; +} + +static void sunxi_lradc_isr(int irq, void *param) +{ + rt_uint32_t status; + struct sunxi_lradc *lradc = param; + + RT_UNUSED(irq); + + status = HWREG32(lradc->base + LRADC_INTS); + if (status & LRADC_CHAN0_KEYUP) + { + if (lradc->pressed_code) + { + rt_input_report_key(&lradc->parent, lradc->pressed_code, 0); + rt_input_sync(&lradc->parent); + lradc->pressed_code = 0; + } + } + if ((status & LRADC_CHAN0_KEYDOWN) && !lradc->pressed_code) + { + rt_uint32_t sample = HWREG32(lradc->base + LRADC_DATA0) & + LRADC_DATA_MASK; + rt_uint32_t voltage_mv = sample * lradc->vref_mv / LRADC_DATA_MAX; + + lradc->pressed_code = sunxi_lradc_find_key(lradc, voltage_mv); + if (lradc->pressed_code) + { + rt_input_report_key(&lradc->parent, lradc->pressed_code, 1); + rt_input_sync(&lradc->parent); + } + } + + HWREG32(lradc->base + LRADC_INTS) = status; +} + +static void sunxi_lradc_release(struct sunxi_lradc *lradc) +{ + if (lradc->base) + { + HWREG32(lradc->base + LRADC_INTC) = 0; + HWREG32(lradc->base + LRADC_CTRL) = 0; + } + if (lradc->irq_attached) + { + rt_pic_irq_mask(lradc->irq); + rt_pic_detach_irq(lradc->irq, lradc); + } + if (lradc->registered) + { + rt_input_device_unregister(&lradc->parent); + } + if (lradc->clk_enabled) + { + rt_clk_disable_unprepare(lradc->clk); + } + if (!rt_is_err_or_null(lradc->reset)) + { + rt_reset_control_assert(lradc->reset); + rt_reset_control_put(lradc->reset); + } + if (!rt_is_err_or_null(lradc->clk)) + { + rt_clk_put(lradc->clk); + } + if (lradc->base) + { + rt_iounmap(lradc->base); + } + rt_free(lradc->keys); + rt_free(lradc); +} + +static rt_err_t sunxi_lradc_read_keymap(struct sunxi_lradc *lradc, + struct rt_ofw_node *np) +{ + rt_uint32_t index = 0; + struct rt_ofw_node *child; + + lradc->keys_nr = rt_ofw_get_child_count(np); + if (!lradc->keys_nr) + { + return -RT_ENOENT; + } + lradc->keys = rt_calloc(lradc->keys_nr, sizeof(*lradc->keys)); + if (!lradc->keys) + { + return -RT_ENOMEM; + } + + rt_ofw_foreach_available_child_node(np, child) + { + const char *code_prop; + struct sunxi_lradc_key *key = &lradc->keys[index]; + + if (rt_ofw_prop_read_u32(child, "voltage", &key->voltage_mv) || + !(code_prop = rt_ofw_get_prop_fuzzy_name(child, ",code$")) || + rt_ofw_prop_read_u32(child, code_prop, &key->code)) + { + rt_ofw_node_put(child); + return -RT_EINVAL; + } + if ((rt_uint64_t)key->voltage_mv * LRADC_DATA_MAX > + (rt_uint64_t)lradc->vref_mv * 2U) + { + rt_ofw_node_put(child); + return -RT_EINVAL; + } + rt_input_set_capability(&lradc->parent, EV_KEY, key->code); + ++index; + } + + lradc->keys_nr = index; + return RT_EOK; +} + +static rt_err_t sunxi_lradc_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *name; + struct rt_device *dev = &pdev->parent; + struct sunxi_lradc *lradc = rt_calloc(1, sizeof(*lradc)); + + if (!lradc) + { + return -RT_ENOMEM; + } + lradc->base = rt_dm_dev_iomap(dev, 0); + lradc->clk = rt_clk_get_by_name(dev, "bus"); + lradc->reset = rt_reset_control_get_by_name(dev, "bus"); + lradc->irq = rt_dm_dev_get_irq(dev, 0); + if (!lradc->base || rt_is_err(lradc->clk) || rt_is_err(lradc->reset) || + lradc->irq < 0) + { + err = lradc->irq < 0 ? lradc->irq : -RT_EIO; + goto _fail; + } + + lradc->vref_mv = LRADC_DEFAULT_VREF_MV * 3U / 4U; + rt_ofw_prop_read_u32(dev->ofw_node, "allwinner,vref-millivolt", + &lradc->vref_mv); + if ((err = sunxi_lradc_read_keymap(lradc, dev->ofw_node))) + { + goto _fail; + } + if ((err = rt_reset_control_deassert(lradc->reset)) || + (err = rt_clk_prepare_enable(lradc->clk))) + { + goto _fail; + } + lradc->clk_enabled = RT_TRUE; + + if ((err = rt_pic_attach_irq(lradc->irq, sunxi_lradc_isr, lradc, + "sunxi-lradc", 0))) + { + goto _fail; + } + lradc->irq_attached = RT_TRUE; + + HWREG32(lradc->base + LRADC_INTS) = RT_UINT32_MAX; + HWREG32(lradc->base + LRADC_CTRL) = LRADC_FIRST_CONVERT_DLY(2) | + LRADC_LEVELA_B_CNT(1) | LRADC_HOLD_EN | + LRADC_SAMPLE_RATE(0) | LRADC_ENABLE; + HWREG32(lradc->base + LRADC_INTC) = LRADC_CHAN0_KEYUP | + LRADC_CHAN0_KEYDOWN; + rt_pic_irq_unmask(lradc->irq); + + if ((err = rt_dm_dev_set_name_auto(&lradc->parent.parent, "lradc")) || + (err = rt_input_device_register(&lradc->parent))) + { + goto _fail; + } + lradc->registered = RT_TRUE; + name = rt_dm_dev_get_name(&lradc->parent.parent); + LOG_I("%s: %u ladder key(s), %u mV reference", name, + lradc->keys_nr, lradc->vref_mv); + dev->user_data = lradc; + + return RT_EOK; + +_fail: + sunxi_lradc_release(lradc); + return err; +} + +static rt_err_t sunxi_lradc_remove(struct rt_platform_device *pdev) +{ + struct sunxi_lradc *lradc = pdev->parent.user_data; + + pdev->parent.user_data = RT_NULL; + sunxi_lradc_release(lradc); + return RT_EOK; +} + +static const struct rt_ofw_node_id sunxi_lradc_ofw_ids[] = +{ + { .compatible = "allwinner,sun50i-h616-lradc" }, + { .compatible = "allwinner,sun50i-r329-lradc" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver sunxi_lradc_driver = +{ + .name = "sunxi-lradc", + .ids = sunxi_lradc_ofw_ids, + .probe = sunxi_lradc_probe, + .remove = sunxi_lradc_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(sunxi_lradc_driver); diff --git a/bsp/allwinner/dm/audio/Kconfig b/bsp/allwinner/dm/audio/Kconfig index 54d13e00c19a..75d4d162590e 100644 --- a/bsp/allwinner/dm/audio/Kconfig +++ b/bsp/allwinner/dm/audio/Kconfig @@ -15,6 +15,41 @@ config RT_AUDIO_SUNXI_CODEC standard RT-Thread audio and DMA interfaces and supports 48 kHz-family PCM sample rates with 16-, 24-, or 32-bit mono and stereo streams. +config RT_AUDIO_SUN50I_H616_CODEC + bool "Allwinner H616 integrated audio codec" + depends on RT_USING_AUDIO + depends on RT_USING_CLK + depends on RT_USING_DM + depends on RT_USING_HEAP + depends on RT_USING_RESET + select RT_USING_DMA + select RT_DMA_SUNXI + select RT_USING_SYSTEM_WORKQUEUE + default n + help + Enable PCM playback through the H616 internal DAC and LINEOUT pins. + The driver supports 16- and 24/32-bit mono or stereo PCM in both + 44.1 kHz and 48 kHz sample-rate families. + +config RT_AUDIO_SUN50I_H616_AHUB + bool "Allwinner H616 AHUB HDMI output" + depends on RT_USING_AUDIO + depends on RT_USING_CLK + depends on RT_USING_DM + depends on RT_USING_HEAP + depends on RT_USING_OFW + depends on RT_USING_RESET + depends on RT_GRAPHIC_FB_SUN50I_H616_DE33 + select RT_USING_DMA + select RT_DMA_SUNXI + select RT_USING_SYSTEM_WORKQUEUE + default n + help + Enable PCM playback through the H616 Audio Hub's fixed APBIF0 to + internal HDMI I2S1 route. The driver pairs the AHUB DAUDIO node with + the APBIF0 DMA node supplied by firmware and supports 16-, 24-, and + 32-bit HDMI LPCM streams with up to eight channels. + config RT_AUDIO_SUNXI_I2S0 bool "Allwinner Sunxi I2S0 output" depends on RT_USING_AUDIO diff --git a/bsp/allwinner/dm/audio/SConscript b/bsp/allwinner/dm/audio/SConscript index bca356fccc2d..7a436b00387c 100644 --- a/bsp/allwinner/dm/audio/SConscript +++ b/bsp/allwinner/dm/audio/SConscript @@ -7,7 +7,16 @@ CPPPATH = [cwd + '/../include'] if GetDepend(['RT_AUDIO_SUNXI_CODEC']): src += ['audio-sunxi-codec.c'] -if GetDepend('RT_AUDIO_SUNXI_I2S0') or GetDepend('RT_AUDIO_SUNXI_OWA'): +if GetDepend(['RT_AUDIO_SUN50I_H616_CODEC']): + src += ['audio-sun50i-h616-codec.c'] + +if GetDepend(['RT_AUDIO_SUN50I_H616_AHUB']): + src += ['audio-sun50i-h616-ahub.c'] + +if (GetDepend('RT_AUDIO_SUNXI_I2S0') or + GetDepend('RT_AUDIO_SUNXI_OWA') or + GetDepend('RT_AUDIO_SUN50I_H616_CODEC') or + GetDepend('RT_AUDIO_SUN50I_H616_AHUB')): src += ['audio-sunxi.c'] if GetDepend(['RT_AUDIO_SUNXI_I2S0']): diff --git a/bsp/allwinner/dm/audio/audio-sun50i-h616-ahub.c b/bsp/allwinner/dm/audio/audio-sun50i-h616-ahub.c new file mode 100644 index 000000000000..268fdc5896f1 --- /dev/null +++ b/bsp/allwinner/dm/audio/audio-sun50i-h616-ahub.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "audio.ahub" +#define DBG_LVL DBG_INFO +#include + +#include "audio-sunxi.h" +#include "sunxi-h616-hdmi.h" + +/* H616 AHUB register layout, shared by all APBIF and I2S instances. */ +#define AHUB_RST 0x008 +#define AHUB_GAT 0x00c +#define AHUB_APBIF_TX_CTL(n) (0x010 + (n) * 0x30) +#define AHUB_APBIF_TX_IRQ_CTL(n) (0x014 + (n) * 0x30) +#define AHUB_APBIF_TX_IRQ_STA(n) (0x018 + (n) * 0x30) +#define AHUB_APBIF_TXFIFO_CTL(n) (0x020 + (n) * 0x30) +#define AHUB_APBIF_TXFIFO(n) (0x030 + (n) * 0x30) +#define AHUB_I2S_CTL(n) (0x200 + (n) * 0x100) +#define AHUB_I2S_FMT0(n) (0x204 + (n) * 0x100) +#define AHUB_I2S_FMT1(n) (0x208 + (n) * 0x100) +#define AHUB_I2S_CHCFG(n) (0x224 + (n) * 0x100) +#define AHUB_I2S_OUT_SLOT0(n) (0x230 + (n) * 0x100) +#define AHUB_I2S_OUT_CH0MAP0(n) (0x234 + (n) * 0x100) +#define AHUB_I2S_OUT_CH1MAP0(n) (0x244 + (n) * 0x100) +#define AHUB_I2S_OUT_CH2MAP0(n) (0x254 + (n) * 0x100) +#define AHUB_I2S_OUT_CH3MAP0(n) (0x264 + (n) * 0x100) + +#define AHUB_APBIF_TX_WS RT_GENMASK(18, 16) +#define AHUB_APBIF_TX_CHANS RT_GENMASK(11, 8) +#define AHUB_APBIF_TX_START RT_BIT(4) +#define AHUB_APBIF_TX_DRQ RT_BIT(3) +#define AHUB_APBIF_TX_FIFO_FLUSH RT_BIT(12) +#define AHUB_APBIF_TX_FIFO_16BIT RT_BIT(0) +#define AHUB_I2S_CTL_CLK_OUT RT_BIT(18) +#define AHUB_I2S_CTL_SDO RT_GENMASK(11, 8) +#define AHUB_I2S_CTL_MODE RT_GENMASK(5, 4) +#define AHUB_I2S_CTL_TXEN RT_BIT(2) +#define AHUB_I2S_CTL_GEN RT_BIT(0) +#define AHUB_I2S_FMT_LRCK_POL RT_BIT(19) +#define AHUB_I2S_FMT_LRCK_PERIOD RT_GENMASK(16, 8) +#define AHUB_I2S_FMT_SAMPLE_WIDTH RT_GENMASK(6, 4) +#define AHUB_I2S_FMT_SLOT_WIDTH RT_GENMASK(2, 0) +#define AHUB_I2S_CHCFG_TX_CHANS RT_GENMASK(3, 0) +#define AHUB_I2S_SLOT_CHANS RT_GENMASK(19, 16) +#define AHUB_I2S_SLOT_ENABLE RT_GENMASK(15, 0) + +/* APBIF0 is wired by the H616 AHUB to the internal HDMI I2S1 endpoint. */ +#define H616_AHUB_HDMI_APBIF 0 +#define H616_AHUB_HDMI_I2S 1 +#define H616_AHUB_RST_APBIF0 RT_BIT(31) +#define H616_AHUB_RST_I2S1 RT_BIT(22) + +struct h616_ahub +{ + struct sunxi_audio_output output; + void *base; + struct rt_clk_array *clks; + struct rt_clk *codec_clk; + struct rt_clk *hub_clk; + struct rt_reset_control *reset; + struct rt_ofw_node *cpudai_np; + rt_uint32_t apbif; + rt_uint32_t i2s; + rt_bool_t clks_enabled; +}; + +#define to_h616_ahub(raw) rt_container_of(raw, struct h616_ahub, output) + +static rt_uint32_t h616_ahub_readl(struct h616_ahub *ahub, rt_uint32_t offset) +{ + return HWREG32(ahub->base + offset); +} + +static void h616_ahub_writel(struct h616_ahub *ahub, rt_uint32_t value, + rt_uint32_t offset) +{ + HWREG32(ahub->base + offset) = value; +} + +static void h616_ahub_update_bits(struct h616_ahub *ahub, rt_uint32_t offset, + rt_uint32_t mask, rt_uint32_t value) +{ + rt_uint32_t reg = h616_ahub_readl(ahub, offset); + + h616_ahub_writel(ahub, (reg & ~mask) | (value & mask), offset); +} + +static rt_err_t h616_ahub_configure(struct sunxi_audio_output *output, + const struct rt_audio_configure *config) +{ + struct h616_ahub *ahub = to_h616_ahub(output); + rt_uint32_t width; + rt_uint32_t channels; + rt_uint32_t slots; + rt_uint32_t rate; + + if (!config->channels || config->channels > 8 || + (config->samplebits != 16 && config->samplebits != 24 && + config->samplebits != 32) || + config->samplerate < 8000 || + config->samplerate > 192000) + { + return -RT_EINVAL; + } + width = config->samplebits == 16 ? 3 : config->samplebits == 24 ? 5 + : 7; + channels = config->channels - 1; + rate = config->samplerate * 512; + if (rt_clk_set_rate(ahub->codec_clk, rate) || + rt_clk_set_rate(ahub->hub_clk, rate)) + { + return -RT_EIO; + } + if (sunxi_h616_hdmi_audio_configure(config->samplerate, + config->channels, config->samplebits)) + { + return -RT_EIO; + } + + h616_ahub_update_bits(ahub, AHUB_APBIF_TX_CTL(ahub->apbif), + AHUB_APBIF_TX_WS | AHUB_APBIF_TX_CHANS, (width << 16) | (channels << 8)); + h616_ahub_update_bits(ahub, AHUB_APBIF_TXFIFO_CTL(ahub->apbif), + AHUB_APBIF_TX_FIFO_16BIT, + config->samplebits == 16 ? AHUB_APBIF_TX_FIFO_16BIT : 0); + h616_ahub_update_bits(ahub, AHUB_I2S_FMT0(ahub->i2s), + AHUB_I2S_FMT_LRCK_POL | AHUB_I2S_FMT_LRCK_PERIOD | + AHUB_I2S_FMT_SAMPLE_WIDTH | AHUB_I2S_FMT_SLOT_WIDTH, + AHUB_I2S_FMT_LRCK_POL | (31 << 8) | (width << 4) | 7); + h616_ahub_writel(ahub, 0x30, AHUB_I2S_FMT1(ahub->i2s)); + h616_ahub_update_bits(ahub, AHUB_I2S_CHCFG(ahub->i2s), + AHUB_I2S_CHCFG_TX_CHANS, channels); + h616_ahub_writel(ahub, 0x10, AHUB_I2S_OUT_CH0MAP0(ahub->i2s)); + h616_ahub_writel(ahub, 0x23, AHUB_I2S_OUT_CH1MAP0(ahub->i2s)); + h616_ahub_writel(ahub, 0x54, AHUB_I2S_OUT_CH2MAP0(ahub->i2s)); + h616_ahub_writel(ahub, 0x76, AHUB_I2S_OUT_CH3MAP0(ahub->i2s)); + slots = (channels << 16) | ((RT_BIT(config->channels) - 1) & 0xffff); + h616_ahub_update_bits(ahub, AHUB_I2S_OUT_SLOT0(ahub->i2s), + AHUB_I2S_SLOT_CHANS | AHUB_I2S_SLOT_ENABLE, slots); + return RT_EOK; +} + +static rt_err_t h616_ahub_init(struct sunxi_audio_output *output) +{ + struct h616_ahub *ahub = to_h616_ahub(output); + + h616_ahub_update_bits(ahub, AHUB_RST, + H616_AHUB_RST_APBIF0 | H616_AHUB_RST_I2S1, + H616_AHUB_RST_APBIF0 | H616_AHUB_RST_I2S1); + h616_ahub_update_bits(ahub, AHUB_GAT, + H616_AHUB_RST_APBIF0 | H616_AHUB_RST_I2S1, + H616_AHUB_RST_APBIF0 | H616_AHUB_RST_I2S1); + h616_ahub_update_bits(ahub, AHUB_I2S_CTL(ahub->i2s), + AHUB_I2S_CTL_CLK_OUT | AHUB_I2S_CTL_SDO | AHUB_I2S_CTL_MODE | + AHUB_I2S_CTL_GEN, + (1 << 4) | AHUB_I2S_CTL_GEN | AHUB_I2S_CTL_SDO); + return RT_EOK; +} + +static rt_err_t h616_ahub_start(struct sunxi_audio_output *output) +{ + struct h616_ahub *ahub = to_h616_ahub(output); + + h616_ahub_update_bits(ahub, AHUB_APBIF_TXFIFO_CTL(ahub->apbif), + AHUB_APBIF_TX_FIFO_FLUSH, AHUB_APBIF_TX_FIFO_FLUSH); + h616_ahub_writel(ahub, RT_BIT(1) | RT_BIT(0), AHUB_APBIF_TX_IRQ_STA(ahub->apbif)); + h616_ahub_update_bits(ahub, AHUB_I2S_CTL(ahub->i2s), AHUB_I2S_CTL_TXEN, + AHUB_I2S_CTL_TXEN); + h616_ahub_update_bits(ahub, AHUB_APBIF_TX_CTL(ahub->apbif), + AHUB_APBIF_TX_START, AHUB_APBIF_TX_START); + sunxi_h616_hdmi_audio_enable(RT_TRUE); + return RT_EOK; +} + +static void h616_ahub_stop(struct sunxi_audio_output *output) +{ + struct h616_ahub *ahub = to_h616_ahub(output); + + sunxi_h616_hdmi_audio_enable(RT_FALSE); + h616_ahub_update_bits(ahub, AHUB_APBIF_TX_IRQ_CTL(ahub->apbif), + AHUB_APBIF_TX_DRQ, 0); + h616_ahub_update_bits(ahub, AHUB_APBIF_TX_CTL(ahub->apbif), + AHUB_APBIF_TX_START, 0); + h616_ahub_update_bits(ahub, AHUB_I2S_CTL(ahub->i2s), AHUB_I2S_CTL_TXEN, 0); +} + +static void h616_ahub_enable_dma(struct sunxi_audio_output *output, + rt_bool_t enable) +{ + struct h616_ahub *ahub = to_h616_ahub(output); + + h616_ahub_update_bits(ahub, AHUB_APBIF_TX_IRQ_CTL(ahub->apbif), + AHUB_APBIF_TX_DRQ, enable ? AHUB_APBIF_TX_DRQ : 0); +} + +static const struct sunxi_audio_output_ops h616_ahub_output_ops = { + .init = h616_ahub_init, + .configure = h616_ahub_configure, + .start = h616_ahub_start, + .stop = h616_ahub_stop, + .enable_dma = h616_ahub_enable_dma, +}; + +static struct rt_ofw_node *h616_ahub_find_cpudai(rt_uint32_t id) +{ + struct rt_ofw_node *np; + rt_uint32_t value; + + rt_ofw_foreach_node_by_compatible(np, "allwinner,sunxi-ahub-cpudai") + { + if (!rt_ofw_prop_read_u32(np, "id", &value) && value == id) + { + return np; + } + } + return RT_NULL; +} + +static void h616_ahub_release(struct h616_ahub *ahub) +{ + sunxi_audio_output_release(&ahub->output); + if (ahub->cpudai_np) + { + rt_ofw_node_put(ahub->cpudai_np); + } + if (!rt_is_err_or_null(ahub->reset)) + { + rt_reset_control_assert(ahub->reset); + rt_reset_control_put(ahub->reset); + } + if (!rt_is_err_or_null(ahub->codec_clk)) + { + rt_clk_put(ahub->codec_clk); + } + if (!rt_is_err_or_null(ahub->hub_clk)) + { + rt_clk_put(ahub->hub_clk); + } + if (!rt_is_err_or_null(ahub->clks)) + { + if (ahub->clks_enabled) + { + rt_clk_array_disable_unprepare(ahub->clks); + } + rt_clk_array_put(ahub->clks); + } + if (ahub->base) + { + rt_iounmap(ahub->base); + } + rt_free(ahub); +} + +static rt_err_t h616_ahub_probe(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct h616_ahub *ahub = rt_calloc(1, sizeof(*ahub)); + rt_uint32_t apbif = H616_AHUB_HDMI_APBIF; + rt_uint32_t tdm = H616_AHUB_HDMI_I2S; + rt_err_t err; + + if (!ahub) + { + return -RT_ENOMEM; + } + rt_ofw_prop_read_u32(dev->ofw_node, "tdm_num", &tdm); + rt_ofw_prop_read_u32(dev->ofw_node, "allwinner,apbif-id", &apbif); + LOG_D("AHUB: apbif=%u i2s=%u", apbif, tdm); + + if (tdm != H616_AHUB_HDMI_I2S || apbif != H616_AHUB_HDMI_APBIF) + { + LOG_E("unsupported AHUB route: apbif=%u i2s=%u", apbif, tdm); + err = -RT_ENOSYS; + goto fail; + } + ahub->output.dev = dev; + ahub->apbif = apbif; + ahub->i2s = tdm; + ahub->base = rt_dm_dev_iomap(dev, 0); + ahub->clks = rt_clk_get_array(dev); + ahub->codec_clk = rt_clk_get_by_name(dev, "audio-codec-1x"); + ahub->hub_clk = rt_clk_get_by_name(dev, "audio-hub"); + ahub->reset = rt_reset_control_get_by_index(dev, 0); + ahub->cpudai_np = h616_ahub_find_cpudai(apbif); + + if (!ahub->base) + { + err = -RT_EIO; + LOG_E("AHUB: failed to map registers"); + goto fail; + } + if (rt_is_err_or_null(ahub->clks)) + { + err = -RT_EIO; + LOG_E("AHUB: invalid clock array"); + goto fail; + } + if (rt_is_err_or_null(ahub->codec_clk)) + { + err = -RT_EIO; + LOG_E("AHUB: missing audio-codec-1x clock"); + goto fail; + } + if (rt_is_err_or_null(ahub->hub_clk)) + { + err = -RT_EIO; + LOG_E("AHUB: missing audio-hub clock"); + goto fail; + } + if (rt_is_err_or_null(ahub->reset)) + { + /* + * Vendor H616 DTs put the shared AHUB reset on the sunxi-ahub + * node, not on individual sunxi-ahub-daudio endpoints. Firmware + * has already released that reset before handing over to us. + */ + LOG_W("AHUB: no endpoint reset; using shared AHUB state"); + } + if (!ahub->cpudai_np) + { + err = -RT_EIO; + LOG_E("AHUB: missing cpudai%u node", apbif); + goto fail; + } + if ((err = rt_platform_ofw_request(ahub->cpudai_np))) + { + LOG_E("AHUB: request cpudai%u failed: %d", apbif, err); + goto fail; + } + if (!ahub->cpudai_np->dev) + { + err = -RT_EIO; + LOG_E("AHUB: cpudai%u did not bind a device", apbif); + goto fail; + } + /* Codec 1x and AHUB use one PLL_AUDIO family at 48 kHz. */ + if ((err = rt_clk_set_rate(ahub->codec_clk, 49152000))) + { + LOG_E("AHUB: set audio-codec-1x rate failed: %d", err); + goto fail; + } + if ((err = rt_clk_set_rate(ahub->hub_clk, 24576000))) + { + LOG_E("AHUB: set audio-hub rate failed: %d", err); + goto fail; + } + if ((err = rt_clk_array_prepare_enable(ahub->clks))) + { + LOG_E("AHUB: enable clocks failed: %d", err); + goto fail; + } + if (!rt_is_err_or_null(ahub->reset) && + (err = rt_reset_control_deassert(ahub->reset))) + { + LOG_E("AHUB: deassert reset failed: %d", err); + goto fail; + } + ahub->clks_enabled = RT_TRUE; + ahub->output.config.samplerate = 48000; + ahub->output.config.channels = 2; + ahub->output.config.samplebits = 16; + ahub->output.ops = &h616_ahub_output_ops; + ahub->output.playback_buffer = rt_dma_alloc_coherent(dev, + SUNXI_AUDIO_BUFFER_SIZE, &ahub->output.playback_dma); + if (!ahub->output.playback_buffer) + { + err = -RT_ENOMEM; + goto fail; + } + ahub->output.dma_tx = rt_dma_chan_request(ahub->cpudai_np->dev, "tx"); + if (rt_is_err(ahub->output.dma_tx)) + { + err = rt_ptr_err(ahub->output.dma_tx); + goto fail; + } + ahub->output.dma_tx->callback = sunxi_audio_output_dma_done; + ahub->output.dma_tx->priv = &ahub->output; + ahub->output.fifo_dma = (rt_ubase_t)rt_kmem_v2p(ahub->base) + + AHUB_APBIF_TXFIFO(ahub->apbif); + if ((err = sunxi_audio_output_register(&ahub->output, "hdmi-sound", ahub))) + { + goto fail; + } + dev->user_data = ahub; + return RT_EOK; + +fail: + h616_ahub_release(ahub); + return err; +} + +static rt_err_t h616_ahub_remove(struct rt_platform_device *pdev) +{ + struct h616_ahub *ahub = pdev->parent.user_data; + + if (!ahub) + { + return RT_EOK; + } + sunxi_audio_output_stop(&ahub->output); + rt_device_unregister(&ahub->output.parent.parent); + h616_ahub_release(ahub); + return RT_EOK; +} + +/* + * AHUB cpudai nodes carry the DMA specifiers for APBIF endpoints. They have + * no separate register block, but need a platform device so the DMA core can + * resolve their dmas property when an AHUB endpoint requests a channel. + */ +static rt_err_t h616_ahub_cpudai_probe(struct rt_platform_device *pdev) +{ + rt_uint32_t id; + + if (rt_ofw_prop_read_u32(pdev->parent.ofw_node, "id", &id) || id > 2) + { + return -RT_EINVAL; + } + + LOG_D("AHUB: cpudai%u DMA endpoint ready", id); + + return RT_EOK; +} + +static const struct rt_ofw_node_id h616_ahub_cpudai_ofw_ids[] = { + { .compatible = "allwinner,sunxi-ahub-cpudai" }, + {} +}; + +static struct rt_platform_driver h616_ahub_cpudai_driver = { + .name = "sun50i-h616-ahub-cpudai", + .ids = h616_ahub_cpudai_ofw_ids, + .probe = h616_ahub_cpudai_probe, +}; + +static const struct rt_ofw_node_id h616_ahub_ofw_ids[] = { + { .compatible = "allwinner,sunxi-ahub-daudio" }, + {} +}; + +static struct rt_platform_driver h616_ahub_driver = { + .name = "sun50i-h616-ahub", + .ids = h616_ahub_ofw_ids, + .probe = h616_ahub_probe, + .remove = h616_ahub_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(h616_ahub_cpudai_driver); +RT_PLATFORM_DRIVER_EXPORT(h616_ahub_driver); diff --git a/bsp/allwinner/dm/audio/audio-sun50i-h616-codec.c b/bsp/allwinner/dm/audio/audio-sun50i-h616-codec.c new file mode 100644 index 000000000000..e05d832efa95 --- /dev/null +++ b/bsp/allwinner/dm/audio/audio-sun50i-h616-codec.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "audio.h616" +#define DBG_LVL DBG_INFO +#include + +#include "audio-sunxi.h" + +#define H616_DAC_DPC 0x000 +#define H616_DAC_FIFOC 0x010 +#define H616_DAC_FIFO_STA 0x014 +#define H616_DAC_TXDATA 0x020 +#define H616_DAC_CNT 0x024 +#define H616_DAC_DAP_CTL 0x0f0 +#define H616_DAC_AC_DAC 0x310 +#define H616_DAC_AC_MIXER 0x314 +#define H616_DAC_AC_RAMP 0x31c + +#define H616_DAC_EN RT_BIT(31) +#define H616_DAC_HPF_EN RT_BIT(18) +#define H616_DAC_DVOL_MASK RT_GENMASK(17, 12) +#define H616_DAC_FS_MASK RT_GENMASK(31, 29) +#define H616_DAC_FIFO_MODE_MASK RT_GENMASK(25, 24) +#define H616_DAC_MONO RT_BIT(6) +#define H616_DAC_SAMPLE_24 RT_BIT(5) +#define H616_DAC_DRQ_EN RT_BIT(4) +#define H616_DAC_FIFO_FLUSH RT_BIT(0) +#define H616_DAC_TXE_INT RT_BIT(3) +#define H616_DAC_TXU_INT RT_BIT(2) +#define H616_DAC_TXO_INT RT_BIT(1) + +#define H616_DAC_LEN RT_BIT(15) +#define H616_DAC_REN RT_BIT(14) +#define H616_LINEOUTL_EN RT_BIT(13) +#define H616_DAC_LMUTE RT_BIT(12) +#define H616_LINEOUTR_EN RT_BIT(11) +#define H616_DAC_RMUTE RT_BIT(10) +#define H616_DAC_RAMP_EN RT_BIT(8) +#define H616_LINEOUT_VOL_MASK RT_GENMASK(4, 0) +#define H616_LMIX_DACL RT_BIT(21) +#define H616_RMIX_DACR RT_BIT(17) +#define H616_LMIX_EN RT_BIT(11) +#define H616_RMIX_EN RT_BIT(10) +#define H616_RAMP_RDEN RT_BIT(0) + +struct h616_codec_rate +{ + rt_uint32_t rate; + rt_uint8_t fifo_rate; + rt_uint32_t mclk_rate; +}; + +struct h616_codec +{ + struct sunxi_audio_output output; + void *base; + struct rt_clk_array *clks; + struct rt_clk *clk_1x; + struct rt_reset_control *reset; + rt_bool_t clks_enabled; +}; + +#define to_h616_codec(raw) \ + rt_container_of(raw, struct h616_codec, output) + +static const struct h616_codec_rate h616_codec_rates[] = { + { 8000, 5, 49152000 }, + { 11025, 4, 45158400 }, + { 12000, 4, 49152000 }, + { 16000, 3, 49152000 }, + { 22050, 2, 45158400 }, + { 24000, 2, 49152000 }, + { 32000, 1, 49152000 }, + { 44100, 0, 45158400 }, + { 48000, 0, 49152000 }, + { 88200, 7, 45158400 }, + { 96000, 7, 49152000 }, + { 176400, 6, 45158400 }, + { 192000, 6, 49152000 }, +}; + +static rt_uint32_t h616_codec_readl(struct h616_codec *codec, + rt_uint32_t offset) +{ + return HWREG32(codec->base + offset); +} + +static void h616_codec_writel(struct h616_codec *codec, rt_uint32_t value, + rt_uint32_t offset) +{ + HWREG32(codec->base + offset) = value; +} + +static void h616_codec_update_bits(struct h616_codec *codec, + rt_uint32_t offset, rt_uint32_t mask, rt_uint32_t value) +{ + rt_uint32_t reg = h616_codec_readl(codec, offset); + + h616_codec_writel(codec, (reg & ~mask) | (value & mask), offset); +} + +static const struct h616_codec_rate *h616_codec_find_rate(rt_uint32_t rate) +{ + rt_size_t i; + + for (i = 0; i < RT_ARRAY_SIZE(h616_codec_rates); ++i) + { + if (h616_codec_rates[i].rate == rate) + { + return &h616_codec_rates[i]; + } + } + + return RT_NULL; +} + +static rt_err_t h616_codec_configure(struct sunxi_audio_output *output, + const struct rt_audio_configure *config) +{ + const struct h616_codec_rate *rate; + rt_uint32_t fifo; + struct h616_codec *codec = to_h616_codec(output); + + if (!config->channels || config->channels > 2 || + (config->samplebits != 16 && config->samplebits != 24 && + config->samplebits != 32)) + { + return -RT_EINVAL; + } + rate = h616_codec_find_rate(config->samplerate); + if (!rate) + { + return -RT_ENOSYS; + } + if (rt_clk_set_rate(codec->clk_1x, rate->mclk_rate)) + { + return -RT_EIO; + } + + fifo = h616_codec_readl(codec, H616_DAC_FIFOC); + fifo &= ~(H616_DAC_FS_MASK | H616_DAC_FIFO_MODE_MASK | + H616_DAC_MONO | H616_DAC_SAMPLE_24); + fifo |= (rt_uint32_t)rate->fifo_rate << 29; + if (config->samplebits == 16) + { + fifo |= 3U << 24; + } + else + { + fifo |= H616_DAC_SAMPLE_24; + } + if (config->channels == 1) + { + fifo |= H616_DAC_MONO; + } + h616_codec_writel(codec, fifo, H616_DAC_FIFOC); + + return RT_EOK; +} + +static rt_err_t h616_codec_init(struct sunxi_audio_output *output) +{ + struct h616_codec *codec = to_h616_codec(output); + + h616_codec_writel(codec, 0, H616_DAC_DAP_CTL); + h616_codec_update_bits(codec, H616_DAC_DPC, H616_DAC_HPF_EN | H616_DAC_DVOL_MASK, H616_DAC_HPF_EN); + h616_codec_update_bits(codec, H616_DAC_AC_DAC, H616_LINEOUT_VOL_MASK | H616_DAC_LMUTE | H616_DAC_RMUTE | H616_DAC_RAMP_EN, + 0x1a | H616_DAC_LMUTE | H616_DAC_RMUTE | H616_DAC_RAMP_EN); + h616_codec_update_bits(codec, H616_DAC_AC_MIXER, H616_LMIX_DACL | H616_RMIX_DACR | H616_LMIX_EN | H616_RMIX_EN, + H616_LMIX_DACL | H616_RMIX_DACR | H616_LMIX_EN | H616_RMIX_EN); + + return RT_EOK; +} + +static rt_err_t h616_codec_start(struct sunxi_audio_output *output) +{ + struct h616_codec *codec = to_h616_codec(output); + + h616_codec_update_bits(codec, H616_DAC_FIFOC, H616_DAC_FIFO_FLUSH, + H616_DAC_FIFO_FLUSH); + h616_codec_writel(codec, H616_DAC_TXE_INT | H616_DAC_TXU_INT | H616_DAC_TXO_INT, H616_DAC_FIFO_STA); + h616_codec_writel(codec, 0, H616_DAC_CNT); + h616_codec_update_bits(codec, H616_DAC_DPC, H616_DAC_EN, H616_DAC_EN); + h616_codec_update_bits(codec, H616_DAC_AC_RAMP, H616_RAMP_RDEN, + H616_RAMP_RDEN); + rt_thread_mdelay(25); + h616_codec_update_bits(codec, H616_DAC_AC_DAC, H616_LINEOUTL_EN | H616_LINEOUTR_EN, H616_LINEOUTL_EN | H616_LINEOUTR_EN); + + return RT_EOK; +} + +static void h616_codec_stop(struct sunxi_audio_output *output) +{ + struct h616_codec *codec = to_h616_codec(output); + + h616_codec_update_bits(codec, H616_DAC_FIFOC, H616_DAC_DRQ_EN, 0); + h616_codec_update_bits(codec, H616_DAC_AC_RAMP, H616_RAMP_RDEN, 0); + rt_thread_mdelay(25); + h616_codec_update_bits(codec, H616_DAC_AC_DAC, H616_LINEOUTL_EN | H616_LINEOUTR_EN, 0); + h616_codec_update_bits(codec, H616_DAC_DPC, H616_DAC_EN, 0); +} + +static void h616_codec_enable_dma(struct sunxi_audio_output *output, + rt_bool_t enable) +{ + struct h616_codec *codec = to_h616_codec(output); + + h616_codec_update_bits(codec, H616_DAC_FIFOC, H616_DAC_DRQ_EN, + enable ? H616_DAC_DRQ_EN : 0); +} + +static const struct sunxi_audio_output_ops h616_codec_output_ops = { + .init = h616_codec_init, + .configure = h616_codec_configure, + .start = h616_codec_start, + .stop = h616_codec_stop, + .enable_dma = h616_codec_enable_dma, +}; + +static void h616_codec_release(struct h616_codec *codec) +{ + sunxi_audio_output_release(&codec->output); + if (!rt_is_err_or_null(codec->reset)) + { + rt_reset_control_assert(codec->reset); + rt_reset_control_put(codec->reset); + } + if (!rt_is_err_or_null(codec->clk_1x)) + { + rt_clk_put(codec->clk_1x); + } + if (!rt_is_err_or_null(codec->clks)) + { + if (codec->clks_enabled) + { + rt_clk_array_disable_unprepare(codec->clks); + } + rt_clk_array_put(codec->clks); + } + if (codec->base) + { + rt_iounmap(codec->base); + } + rt_free(codec); +} + +static rt_err_t h616_codec_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + struct rt_device *dev = &pdev->parent; + struct h616_codec *codec = rt_calloc(1, sizeof(*codec)); + + if (!codec) + { + return -RT_ENOMEM; + } + codec->output.dev = dev; + codec->base = rt_dm_dev_iomap(dev, 0); + codec->clks = rt_clk_get_array(dev); + codec->clk_1x = rt_clk_get_by_name(dev, "audio-codec-1x"); + codec->reset = rt_reset_control_get_by_index(dev, 0); + if (!codec->base || rt_is_err(codec->clks) || rt_is_err(codec->clk_1x) || + rt_is_err(codec->reset)) + { + err = -RT_EIO; + goto fail; + } + /* Program a valid audio PLL before the array enables its module gates. */ + if ((err = rt_clk_set_rate(codec->clk_1x, 49152000)) || + (err = rt_clk_array_prepare_enable(codec->clks)) || + (err = rt_reset_control_deassert(codec->reset))) + { + goto fail; + } + codec->clks_enabled = RT_TRUE; + codec->output.config.samplerate = 48000; + codec->output.config.channels = 2; + codec->output.config.samplebits = 16; + codec->output.ops = &h616_codec_output_ops; + codec->output.playback_buffer = rt_dma_alloc_coherent(dev, + SUNXI_AUDIO_BUFFER_SIZE, &codec->output.playback_dma); + if (!codec->output.playback_buffer) + { + err = -RT_ENOMEM; + goto fail; + } + codec->output.dma_tx = rt_dma_chan_request(dev, "tx"); + if (rt_is_err(codec->output.dma_tx)) + { + err = rt_ptr_err(codec->output.dma_tx); + goto fail; + } + codec->output.dma_tx->callback = sunxi_audio_output_dma_done; + codec->output.dma_tx->priv = &codec->output; + codec->output.fifo_dma = (rt_ubase_t)rt_kmem_v2p(codec->base) + + H616_DAC_TXDATA; + if ((err = sunxi_audio_output_register(&codec->output, "sound", codec))) + { + goto fail; + } + + dev->user_data = codec; + return RT_EOK; + +fail: + LOG_E("probe failed: %s", rt_strerror(err)); + h616_codec_release(codec); + return err; +} + +static rt_err_t h616_codec_remove(struct rt_platform_device *pdev) +{ + struct h616_codec *codec = pdev->parent.user_data; + + sunxi_audio_output_stop(&codec->output); + rt_device_unregister(&codec->output.parent.parent); + h616_codec_release(codec); + return RT_EOK; +} + +static const struct rt_ofw_node_id h616_codec_ofw_ids[] = { + { .compatible = "allwinner,sun50i-h616-codec" }, + {} +}; + +static struct rt_platform_driver h616_codec_driver = { + .name = "sun50i-h616-codec", + .ids = h616_codec_ofw_ids, + .probe = h616_codec_probe, + .remove = h616_codec_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(h616_codec_driver); diff --git a/bsp/allwinner/dm/clk/Kconfig b/bsp/allwinner/dm/clk/Kconfig index 7c5d9b6ddfb5..812d995d2962 100644 --- a/bsp/allwinner/dm/clk/Kconfig +++ b/bsp/allwinner/dm/clk/Kconfig @@ -1,3 +1,23 @@ +config RT_CLK_SUN50I_H616 + bool "Allwinner H616 main and R clock/reset controllers" + depends on RT_USING_CLK && RT_USING_OFW + select RT_USING_RESET + select RT_USING_RTC + select RT_USING_ALARM + select RT_RTC_SUNXI + default n + help + Read firmware-programmed peripheral PLL and bus dividers, and provide + peripheral bus gates, CPU DVFS, MMC and display clocks and resets on + the original CCU nodes. Display dividers use firmware-configured PLL + rates. + Requires the H616 RTC driver for the oscillator clock outputs. + Starts disabled display PLLs at 600 MHz (DE) or 594 MHz (video), + with a bounded lock wait. Running PLL frequencies are preserved. + Display rates unavailable from the running PLLs are rejected rather + than changing shared PLL settings. Also provides DE33 bus enumeration, + internal clocks/resets and the H6/H616 TCON TOP clock and routing block. + config RT_CLK_SUN252I_F101 bool "Allwinner Sun252i F101 clock/reset controller" depends on RT_USING_CLK diff --git a/bsp/allwinner/dm/clk/SConscript b/bsp/allwinner/dm/clk/SConscript index a7951d81bec8..818443525011 100644 --- a/bsp/allwinner/dm/clk/SConscript +++ b/bsp/allwinner/dm/clk/SConscript @@ -4,6 +4,10 @@ cwd = GetCurrentDir() src = [] CPPPATH = [cwd + '/../include'] +if GetDepend(['RT_CLK_SUN50I_H616']): + src += ['clk-sun50i-h616.c', 'clk-sun50i-h616-de33.c', + 'clk-sunxi-tcon-top.c'] + if GetDepend(['RT_CLK_SUN252I_F101']): src += ['clk-sunxi.c', 'clk-sun252i-f101.c', 'clk-sun252i-f101-cpupll.c', 'clk-sun252i-f101-rtc.c'] diff --git a/bsp/allwinner/dm/clk/clk-sun50i-h616-de33.c b/bsp/allwinner/dm/clk/clk-sun50i-h616-de33.c new file mode 100644 index 000000000000..083b146a2811 --- /dev/null +++ b/bsp/allwinner/dm/clk/clk-sun50i-h616-de33.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* DE33 is not register-compatible with the DE2 clock controller. */ +#define DE33_RESET 0x00 +#define DE33_GATE 0x04 +#define DE33_BUS_GATE 0x08 + +struct de33_ccu +{ + struct rt_clk_node node; + /* OFW resolves composite clock/reset providers by adjacent headers. */ + struct rt_reset_controller resets; + struct rt_clk_cell clocks[4]; + struct rt_clk_cell *cells[9]; + struct rt_spinlock lock; + struct rt_clk *bus, *mod; + struct rt_reset_control *reset; + void *base; +}; + +static void de33_update(struct de33_ccu *ccu, unsigned int reg, + rt_uint32_t mask, rt_bool_t enable) +{ + rt_base_t level = rt_spin_lock_irqsave(&ccu->lock); + rt_uint32_t value = HWREG32(ccu->base + reg); + + HWREG32(ccu->base + reg) = enable ? value | mask : value & ~mask; + rt_spin_unlock_irqrestore(&ccu->lock, level); +} + +static rt_err_t de33_enable(struct rt_clk_cell *cell) +{ + struct de33_ccu *ccu = cell->clk_np->priv; + unsigned int index = cell - ccu->clocks; + + de33_update(ccu, index ? DE33_GATE : DE33_BUS_GATE, + RT_BIT(index == 3 ? 4 : (index ? index - 1 : 0)), RT_TRUE); + return RT_EOK; +} + +static void de33_disable(struct rt_clk_cell *cell) +{ + struct de33_ccu *ccu = cell->clk_np->priv; + unsigned int index = cell - ccu->clocks; + + de33_update(ccu, index ? DE33_GATE : DE33_BUS_GATE, + RT_BIT(index == 3 ? 4 : (index ? index - 1 : 0)), RT_FALSE); +} + +static rt_ubase_t de33_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + return parent_rate; +} + +static const struct rt_clk_ops de33_clk_ops = { + .enable = de33_enable, + .disable = de33_disable, + .recalc_rate = de33_rate, +}; + +static struct rt_clk_cell *de33_parse(struct rt_clk_node *node, + struct rt_ofw_cell_args *args) +{ + unsigned int id; + + if (args->args_count != 1) + { + return RT_NULL; + } + id = args->args[0]; + /* Legacy trees name three bus clocks; hardware has one shared gate. */ + if (id <= 2) + { + id = 0; + } + return id < node->cells_nr ? node->cells[id] : RT_NULL; +} + +static rt_err_t de33_reset_update(struct rt_reset_control *rst, rt_bool_t release) +{ + if (rst->id < 0 || rst->id > 2) + { + return -RT_EINVAL; + } + de33_update(rst->rstcer->priv, DE33_RESET, + RT_BIT(rst->id == 2 ? 4 : rst->id), release); + return RT_EOK; +} + +static rt_err_t de33_assert(struct rt_reset_control *rst) +{ + return de33_reset_update(rst, RT_FALSE); +} + +static rt_err_t de33_deassert(struct rt_reset_control *rst) +{ + return de33_reset_update(rst, RT_TRUE); +} + +static int de33_status(struct rt_reset_control *rst) +{ + struct de33_ccu *ccu = rst->rstcer->priv; + + if (rst->id < 0 || rst->id > 2) + { + return -RT_EINVAL; + } + return !(HWREG32(ccu->base + DE33_RESET) & RT_BIT(rst->id == 2 ? 4 : rst->id)); +} + +static const struct rt_reset_control_ops de33_reset_ops = { + .assert = de33_assert, + .deassert = de33_deassert, + .status = de33_status, +}; + +static rt_err_t de33_ccu_probe(struct rt_platform_device *pdev) +{ + static const char * const names[] = { + "de33-bus", + "de33-mixer0", + "de33-mixer1", + "de33-wb", + }; + struct de33_ccu *ccu = rt_calloc(1, sizeof(*ccu)); + rt_err_t err = -RT_ENOMEM; + int i; + + if (!ccu) + { + return err; + } + rt_spin_lock_init(&ccu->lock); + ccu->base = rt_dm_dev_iomap(&pdev->parent, 0); + ccu->bus = rt_clk_get_by_name(&pdev->parent, "bus"); + ccu->mod = rt_clk_get_by_name(&pdev->parent, "mod"); + ccu->reset = rt_reset_control_get_by_index(&pdev->parent, 0); + if (!ccu->base || rt_is_err_or_null(ccu->bus) || + rt_is_err_or_null(ccu->mod) || rt_is_err_or_null(ccu->reset)) + { + err = -RT_EIO; + goto fail; + } + if ((err = rt_clk_prepare_enable(ccu->bus))) + { + goto fail; + } + if ((err = rt_clk_prepare_enable(ccu->mod))) + { + goto disable_bus; + } + if ((err = rt_reset_control_deassert(ccu->reset))) + { + goto disable_mod; + } + ccu->node.dev = &pdev->parent; + ccu->node.priv = ccu; + ccu->node.parents_clk = rt_err_ptr(-RT_ENOSYS); + ccu->node.cells = ccu->cells; + ccu->node.cells_nr = RT_ARRAY_SIZE(ccu->cells); + ccu->node.ofw_parse = de33_parse; + for (i = 0; i < RT_ARRAY_SIZE(ccu->clocks); ++i) + { + struct rt_clk_cell *cell = &ccu->clocks[i]; + + cell->name = names[i]; + cell->ops = &de33_clk_ops; + cell->parents_nr = 1; + cell->parent_name = (i ? ccu->mod : ccu->bus)->cell->name; + cell->flags = RT_CLK_F_GET_RATE_NOCACHE; + if (i) + { + cell->flags |= RT_CLK_F_SET_RATE_PARENT; + } + ccu->cells[i ? i + 5 : 0] = cell; + } + if ((err = rt_clk_register(&ccu->node))) + { + goto assert_reset; + } + ccu->resets.ofw_node = pdev->parent.ofw_node; + ccu->resets.ops = &de33_reset_ops; + ccu->resets.priv = ccu; + if ((err = rt_reset_controller_register(&ccu->resets))) + { + if (rt_clk_unregister(&ccu->node)) + { + return err; + } + goto assert_reset; + } + pdev->parent.user_data = ccu; + return RT_EOK; + +assert_reset: + rt_reset_control_assert(ccu->reset); +disable_mod: + rt_clk_disable_unprepare(ccu->mod); +disable_bus: + rt_clk_disable_unprepare(ccu->bus); +fail: + if (!rt_is_err_or_null(ccu->reset)) + { + rt_reset_control_put(ccu->reset); + } + if (!rt_is_err_or_null(ccu->mod)) + { + rt_clk_put(ccu->mod); + } + if (!rt_is_err_or_null(ccu->bus)) + { + rt_clk_put(ccu->bus); + } + if (ccu->base) + { + rt_iounmap(ccu->base); + } + rt_free(ccu); + return err; +} + +static rt_err_t de33_bus_probe(struct rt_platform_device *pdev) +{ + struct rt_ofw_node *section, *sram, *syscon, *child; + rt_uint32_t selection; + void *base; + rt_err_t err; + + err = rt_ofw_prop_read_u32_index(pdev->parent.ofw_node, "allwinner,sram", 1, &selection); + if (err || selection != 1) + { + return err ? err : -RT_EINVAL; + } + section = rt_ofw_parse_phandle(pdev->parent.ofw_node, "allwinner,sram", 0); + if (!section) + { + return -RT_ENOENT; + } + if (!rt_ofw_node_is_compatible(section, "allwinner,sun50i-a64-sram-c") && + !rt_ofw_node_is_compatible(section, "allwinner,sun50i-h616-sram-c")) + { + rt_ofw_node_put(section); + return -RT_EINVAL; + } + sram = rt_ofw_get_parent(section); + syscon = sram ? rt_ofw_get_parent(sram) : RT_NULL; + base = syscon ? rt_ofw_iomap(syscon, 0) : RT_NULL; + rt_ofw_node_put(syscon); + rt_ofw_node_put(sram); + rt_ofw_node_put(section); + if (!base) + { + return -RT_EIO; + } + /* SRAM C selection: zero routes the SRAM to DE, one to the CPU. */ + HWREG32(base + 4) &= ~RT_BIT(24); + rt_iounmap(base); + rt_ofw_foreach_available_child_node(pdev->parent.ofw_node, child) + { + if ((err = rt_platform_ofw_request(child))) + { + rt_ofw_node_put(child); + return err; + } + } + return RT_EOK; +} + +static const struct rt_ofw_node_id de33_ccu_ids[] = { + { .compatible = "allwinner,sun50i-h616-de33-clk" }, + {} +}; + +static const struct rt_ofw_node_id de33_bus_ids[] = { + { .compatible = "allwinner,sun50i-h616-de33" }, + {} +}; + +static struct rt_platform_driver de33_ccu_driver = { + .name = "de33-ccu", + .ids = de33_ccu_ids, + .probe = de33_ccu_probe, +}; + +static struct rt_platform_driver de33_bus_driver = { + .name = "de33-bus", + .ids = de33_bus_ids, + .probe = de33_bus_probe, +}; + +static int de33_register(void) +{ + rt_platform_driver_register(&de33_ccu_driver); + rt_platform_driver_register(&de33_bus_driver); + return 0; +} +INIT_SUBSYS_EXPORT(de33_register); diff --git a/bsp/allwinner/dm/clk/clk-sun50i-h616.c b/bsp/allwinner/dm/clk/clk-sun50i-h616.c new file mode 100644 index 000000000000..81a665c02161 --- /dev/null +++ b/bsp/allwinner/dm/clk/clk-sun50i-h616.c @@ -0,0 +1,1413 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* IDs follow the Linux sun50i-h616-ccu binding, including its reserved IDs. */ +#define H616_CLK_PLL_PERIPH0 4 +#define H616_CLK_CPUX 21 +#define H616_CLK_PSI_AHB 24 +#define H616_CLK_AHB3 25 +#define H616_CLK_APB1 26 +#define H616_CLK_APB2 27 +#define H616_CLK_UART0 66 +#define H616_CLK_TWI0 72 +#define H616_CLK_AUDIO_CODEC_1X 91 +#define H616_CLK_AUDIO_CODEC_4X 92 +#define H616_CLK_BUS_AUDIO_CODEC 93 +#define H616_CLK_AUDIO_HUB 94 +#define H616_CLK_BUS_AUDIO_HUB 95 +#define H616_CLK_COUNT 134 +#define H616_RST_UART0 17 +#define H616_RST_TWI0 23 +#define H616_RST_G2D 64 +#define H616_UART_BGR 0x90c +#define H616_TWI_BGR 0x91c + +struct h616_ccu +{ + struct rt_clk_node clocks; + /* Keep OFW clock/reset provider headers adjacent for composite lookup. */ + struct rt_reset_controller resets; + struct rt_clk_cell *cells[H616_CLK_COUNT]; + struct rt_clk_cell bus_clocks[H616_CLK_COUNT]; + struct rt_spinlock lock; + void *base; + struct rt_clk *osc[4]; + rt_ubase_t osc_rate[4]; + rt_bool_t is_r; +}; + +struct h616_ccu_soc_data +{ + rt_bool_t is_r; +}; + +static const struct h616_ccu_soc_data h616_ccu_data = { 0 }; +static const struct h616_ccu_soc_data h616_r_ccu_data = +{ + .is_r = RT_TRUE, +}; + +struct h616_clock_desc +{ + const char *name; + unsigned int id; +}; + +static const struct h616_clock_desc h616_clock_descs[] = { + { "h616-pll-periph0", H616_CLK_PLL_PERIPH0 }, + { "h616-cpux", H616_CLK_CPUX }, + { "h616-pll-video0", 9 }, + { "h616-pll-video0-4x", 10 }, + { "h616-pll-video1", 11 }, + { "h616-pll-video1-4x", 12 }, + { "h616-pll-video2", 13 }, + { "h616-pll-video2-4x", 14 }, + { "h616-pll-ve", 15 }, + { "h616-pll-de", 16 }, + { "h616-psi-ahb", H616_CLK_PSI_AHB }, + { "h616-ahb3", H616_CLK_AHB3 }, + { "h616-apb1", H616_CLK_APB1 }, + { "h616-apb2", H616_CLK_APB2 }, + { "h616-de", 29 }, + { "h616-de-bus", 30 }, + { "h616-g2d", 33 }, + { "h616-g2d-bus", 34 }, + { "h616-ce", 38 }, + { "h616-ce-bus", 39 }, + { "h616-ve", 40 }, + { "h616-ve-bus", 41 }, + { "h616-ve-mbus", 51 }, + { "h616-ce-mbus", 52 }, + { "h616-g2d-mbus", 55 }, + { "h616-uart0", H616_CLK_UART0 }, + { "h616-uart1", H616_CLK_UART0 + 1 }, + { "h616-uart2", H616_CLK_UART0 + 2 }, + { "h616-uart3", H616_CLK_UART0 + 3 }, + { "h616-uart4", H616_CLK_UART0 + 4 }, + { "h616-uart5", H616_CLK_UART0 + 5 }, + { "h616-twi0", H616_CLK_TWI0 }, + { "h616-twi1", H616_CLK_TWI0 + 1 }, + { "h616-twi2", H616_CLK_TWI0 + 2 }, + { "h616-twi3", H616_CLK_TWI0 + 3 }, + { "h616-twi4", H616_CLK_TWI0 + 4 }, + { "h616-pwm-bus", 47 }, + { "h616-spi0", 77 }, + { "h616-spi1", 78 }, + { "h616-spi0-bus", 79 }, + { "h616-spi1-bus", 80 }, + { "h616-dma-bus", 42 }, + { "h616-dma-mbus", 50 }, + { "h616-mmc0", 60 }, + { "h616-mmc1", 61 }, + { "h616-mmc2", 62 }, + { "h616-mmc0-bus", 63 }, + { "h616-mmc1-bus", 64 }, + { "h616-mmc2-bus", 65 }, + { "h616-audio-codec-1x", H616_CLK_AUDIO_CODEC_1X }, + { "h616-audio-codec-4x", H616_CLK_AUDIO_CODEC_4X }, + { "h616-audio-codec-bus", H616_CLK_BUS_AUDIO_CODEC }, + { "h616-audio-hub", H616_CLK_AUDIO_HUB }, + { "h616-audio-hub-bus", H616_CLK_BUS_AUDIO_HUB }, + { "h616-emac0-bus", 82 }, + { "h616-emac1-bus", 83 }, + { "h616-ths-bus", 86 }, + { "h616-gpadc-bus", 129 }, + { "h616-keyadc-bus", 113 }, + { "h616-hdmi", 114 }, + { "h616-hdmi-slow", 115 }, + { "h616-hdmi-cec", 116 }, + { "h616-hdmi-bus", 117 }, + { "h616-tcon-top-bus", 118 }, + { "h616-tcon-tv0", 119 }, + { "h616-tcon-tv1", 120 }, + { "h616-tcon-tv0-bus", 121 }, + { "h616-tcon-tv1-bus", 122 }, + { "h616-hdcp", 126 }, + { "h616-hdcp-bus", 127 }, + { "h616-pll-32k", 128 }, +}; + +static const struct h616_clock_desc h616_r_clock_descs[] = { + { "h616-r-ahb", 1 }, + { "h616-r-apb1", 2 }, + { "h616-r-apb2", 3 }, + { "h616-r-twd", 5 }, + { "h616-r-i2c", 8 }, + { "h616-r-ir-bus", 9 }, + { "h616-r-ir", 11 }, + { "h616-r-rsb", 13 }, + { "h616-r-rtc", 14 }, +}; + +static rt_ubase_t h616_nmp_rate(struct h616_ccu *ccu, unsigned int reg) +{ + rt_uint32_t v = HWREG32(ccu->base + reg); + + if (!(v & RT_BIT(31))) + { + return 0; + } + return ccu->osc_rate[0] * (((v >> 8) & 255) + 1) / + ((((v >> 1) & 1) + 1) * ((v & 1) + 1)); +} + +static rt_ubase_t h616_mmc_parent(struct h616_ccu *ccu, unsigned int mux) +{ + return mux == 0 ? ccu->osc_rate[0] : (mux < 3 ? h616_nmp_rate(ccu, mux == 1 ? 0x20 : 0x28) : 0); +} + +static rt_ubase_t h616_r_bus_rate(struct h616_ccu *ccu, unsigned int reg) +{ + rt_uint32_t v = HWREG32(ccu->base + reg); + rt_uint32_t mux = (v >> 24) & 3; + rt_ubase_t rate = ccu->osc_rate[mux]; + + if (mux == 3) + { + rate /= (v & 31) + 1; + } + return rate / RT_BIT((v >> 8) & 3); +} + +static rt_ubase_t h616_pll_rate(struct h616_ccu *ccu) +{ + rt_uint32_t value = HWREG32(ccu->base + 0x20); + + if (!(value & RT_BIT(31))) + { + return 0; + } + + /* N is encoded as N-1; M and P are one-bit linear dividers. */ + return ccu->osc_rate[0] * (((value >> 8) & 0xff) + 1) / + (2 * (((value >> 1) & 1) + 1) * ((value & 1) + 1)); +} + +static rt_ubase_t h616_cpux_rate(struct h616_ccu *ccu) +{ + rt_uint32_t value = HWREG32(ccu->base + 0x500); + + switch ((value >> 24) & 7) + { + case 0: + return ccu->osc_rate[0]; + case 1: + return ccu->osc_rate[1]; + case 2: + return ccu->osc_rate[2]; + case 3: + value = HWREG32(ccu->base); + return (value & RT_BIT(31)) ? ccu->osc_rate[0] * (((value >> 8) & 0xff) + 1) : 0; + case 4: + return h616_pll_rate(ccu); + default: + return 0; + } +} + +static rt_ubase_t h616_bus_rate(struct h616_ccu *ccu, unsigned int reg) +{ + rt_uint32_t value = HWREG32(ccu->base + reg); + rt_uint32_t mux = (value >> 24) & 3; + rt_ubase_t rate; + + if (mux == 3) + { + rate = h616_pll_rate(ccu); + } + else if (mux == 2 && reg != 0x510) + { + rate = h616_bus_rate(ccu, 0x510); + } + else + { + rate = ccu->osc_rate[mux]; + } + + return rate / ((value & 3) + 1) / RT_BIT((value >> 8) & 3); +} + +static rt_ubase_t h616_audio_pll_rate(struct h616_ccu *ccu) +{ + rt_uint32_t pattern = HWREG32(ccu->base + 0x178); + + if (!(HWREG32(ccu->base + 0x78) & RT_BIT(31))) + { + return 0; + } + if (pattern == 0xc001288d) + { + return 90316800; + } + if (pattern == 0xc001eb85) + { + return 98304000; + } + + return 0; +} + +static rt_ubase_t h616_audio_module_rate(struct h616_ccu *ccu, + unsigned int reg) +{ + rt_uint32_t value = HWREG32(ccu->base + reg); + rt_ubase_t parent = h616_audio_pll_rate(ccu); + + switch ((value >> 24) & 3) + { + case 0: + parent /= 4; + break; + case 1: + parent /= 2; + break; + default: + break; + } + + return parent / ((value & 15) + 1); +} + +static rt_ubase_t h616_periph_parent_rate(struct h616_ccu *ccu, + unsigned int mux) +{ + switch (mux) + { + case 0: + return ccu->osc_rate[0]; + case 1: + return h616_pll_rate(ccu); + case 3: + return h616_pll_rate(ccu) * 2; + default: + /* PLL_PERIPH1 is not modelled because firmware owns it. */ + return 0; + } +} + +static rt_ubase_t h616_mp_rate(struct h616_ccu *ccu, unsigned int reg) +{ + rt_uint32_t value = HWREG32(ccu->base + reg); + rt_ubase_t parent = h616_periph_parent_rate(ccu, (value >> 24) & 7); + + return parent / ((value & 15) + 1) / RT_BIT((value >> 8) & 3); +} + +static rt_ubase_t h616_mp_find_rate(struct h616_ccu *ccu, + rt_ubase_t requested, rt_uint32_t *value) +{ + rt_ubase_t best = 0; + unsigned int mux; + unsigned int m; + unsigned int n; + + for (mux = 0; mux < 4; ++mux) + { + rt_ubase_t parent = h616_periph_parent_rate(ccu, mux); + + if (!parent) + { + continue; + } + for (m = 0; m < 16; ++m) + { + for (n = 0; n < 4; ++n) + { + rt_ubase_t rate = parent / (m + 1) / RT_BIT(n); + + if (rate <= requested && rate > best) + { + best = rate; + *value = (mux << 24) | (n << 8) | m; + } + } + } + } + + return best; +} + +struct h616_display_clock +{ + unsigned int id; + unsigned int reg; + unsigned int mux_width; + unsigned int p_width; + unsigned int parents; +}; + +static const struct h616_display_clock h616_display_clocks[] = { + { 29, 0x600, 1, 0, 2 }, + { 114, 0xb00, 2, 0, 4 }, + { 119, 0xb80, 3, 2, 4 }, + { 120, 0xb84, 3, 2, 4 }, + { 126, 0xc40, 2, 0, 2 }, +}; + +static const struct h616_display_clock *h616_display_clock(unsigned int id) +{ + rt_size_t i; + + for (i = 0; i < RT_ARRAY_SIZE(h616_display_clocks); ++i) + { + if (h616_display_clocks[i].id == id) + { + return &h616_display_clocks[i]; + } + } + return RT_NULL; +} + +static rt_ubase_t h616_video_rate(struct h616_ccu *ccu, unsigned int index, + rt_bool_t four_x) +{ + /* Include the test output divider when inheriting firmware settings. */ + return h616_nmp_rate(ccu, 0x40 + index * 8) / (four_x ? 1 : 4); +} + +static rt_ubase_t h616_display_parent(struct h616_ccu *ccu, + const struct h616_display_clock *clock, unsigned int mux) +{ + if (mux >= clock->parents) + { + return 0; + } + if (clock->id == 29) + { + return h616_nmp_rate(ccu, mux ? 0x20 : 0x60); + } + if (clock->id == 126) + { + return h616_nmp_rate(ccu, mux ? 0x28 : 0x20) / 2; + } + return h616_video_rate(ccu, mux < 2 ? 0 : (clock->id == 114 ? 2 : 1), mux & 1); +} + +static rt_ubase_t h616_display_rate(struct h616_ccu *ccu, + const struct h616_display_clock *clock) +{ + rt_uint32_t value = HWREG32(ccu->base + clock->reg); + unsigned int mux = (value >> 24) & (RT_BIT(clock->mux_width) - 1); + unsigned int p = (value >> 8) & (RT_BIT(clock->p_width) - 1); + + return h616_display_parent(ccu, clock, mux) / ((value & 15) + 1) / RT_BIT(p); +} + +static rt_ubase_t h616_display_find_rate(struct h616_ccu *ccu, + const struct h616_display_clock *clock, rt_ubase_t requested, + rt_uint32_t *value) +{ + rt_ubase_t best = 0; + unsigned int mux, m, p; + + /* Only use live firmware PLLs; do not retune a shared parent implicitly. */ + for (mux = 0; mux < clock->parents; ++mux) + { + for (m = 0; m < 16; ++m) + { + for (p = 0; p < RT_BIT(clock->p_width); ++p) + { + rt_ubase_t rate = h616_display_parent(ccu, clock, mux) / (m + 1) / RT_BIT(p); + + if (rate <= requested && rate > best) + { + best = rate; + *value = (mux << 24) | (p << 8) | m; + } + } + } + } + return best; +} + +static rt_ubase_t h616_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + struct h616_ccu *ccu = cell->clk_np->priv; + const struct h616_clock_desc *desc = cell->priv; + const struct h616_display_clock *display; + + if (ccu->is_r) + { + if (desc->id == 3 || desc->id == 8 || desc->id == 13) + { + return h616_r_bus_rate(ccu, 0x10); + } + if (desc->id == 11) + { + rt_uint32_t v = HWREG32(ccu->base + 0x1c0); + rt_ubase_t rate = ccu->osc_rate[((v >> 24) & 1) ? 0 : 1]; + return rate / ((v & 31) + 1) / RT_BIT((v >> 8) & 3); + } + return h616_r_bus_rate(ccu, 0) / + (desc->id == 1 ? 1 : ((HWREG32(ccu->base + 0xc) & 3) + 1)); + } + display = h616_display_clock(desc->id); + if (display) + { + return h616_display_rate(ccu, display); + } + if (desc->id >= 9 && desc->id <= 14) + { + return h616_video_rate(ccu, (desc->id - 9) / 2, !(desc->id & 1)); + } + if (desc->id == 15 || desc->id == 16) + { + return h616_nmp_rate(ccu, desc->id == 15 ? 0x58 : 0x60); + } + if (desc->id == H616_CLK_CPUX) + { + return h616_cpux_rate(ccu); + } + if (desc->id == 115) + { + return ccu->osc_rate[0]; + } + if (desc->id == 116) + { + unsigned int mux = (HWREG32(ccu->base + 0xb10) >> 24) & 3; + + return mux == 0 ? ccu->osc_rate[1] : (mux == 1 ? h616_nmp_rate(ccu, 0x20) / 36621 : 0); + } + if (desc->id == H616_CLK_AUDIO_CODEC_1X) + { + return h616_audio_module_rate(ccu, 0xa50); + } + if (desc->id == H616_CLK_AUDIO_CODEC_4X) + { + return h616_audio_module_rate(ccu, 0xa54); + } + if (desc->id == H616_CLK_AUDIO_HUB) + { + return h616_audio_module_rate(ccu, 0xa60); + } + if (desc->id == 30) + { + return h616_bus_rate(ccu, 0x510); + } + if (desc->id == 117 || desc->id == 118 || desc->id == 121 || + desc->id == 122 || desc->id == 127) + { + return h616_bus_rate(ccu, 0x51c); + } + if (desc->id >= 60 && desc->id <= 62) + { + rt_uint32_t v = HWREG32(ccu->base + 0x830 + (desc->id - 60) * 4); + return h616_mmc_parent(ccu, (v >> 24) & 3) / + (2 * ((v & 15) + 1) * RT_BIT((v >> 8) & 3)); + } + if (desc->id == 128) + { + return h616_pll_rate(ccu) * 2 / 36621; + } + if ((desc->id >= 63 && desc->id <= 65) || desc->id == 82 || desc->id == 83) + { + return h616_bus_rate(ccu, 0x51c); + } + if (desc->id == 42) + { + return h616_bus_rate(ccu, 0x510); + } + if (desc->id == 86) + { + return h616_bus_rate(ccu, 0x520); + } + if (desc->id == 50) + { + rt_uint32_t v = HWREG32(ccu->base + 0x540); + unsigned int mux = (v >> 24) & 3; + rt_ubase_t rate = mux == 0 ? ccu->osc_rate[0] : h616_nmp_rate(ccu, mux == 1 ? 0x20 : (mux == 2 ? 0x10 : 0x18)); + return rate / ((v & 7) + 1); + } + if (desc->id == 77 || desc->id == 78 || desc->id == 38) + { + return h616_mp_rate(ccu, desc->id == 77 ? 0x940 : desc->id == 78 ? 0x944 + : 0x680); + } + if (desc->id == 33) + { + return h616_periph_parent_rate(ccu, + (HWREG32(ccu->base + 0x630) >> 24) & 1) / + ((HWREG32(ccu->base + 0x630) & 15) + 1); + } + if (desc->id == 40) + { + return h616_nmp_rate(ccu, 0x58) / + ((HWREG32(ccu->base + 0x690) & 7) + 1); + } + + switch (desc->id) + { + case H616_CLK_PLL_PERIPH0: + return h616_pll_rate(ccu); + case H616_CLK_PSI_AHB: + return h616_bus_rate(ccu, 0x510); + case H616_CLK_AHB3: + return h616_bus_rate(ccu, 0x51c); + case H616_CLK_APB1: + return h616_bus_rate(ccu, 0x520); + default: + return h616_bus_rate(ccu, 0x524); + } +} + +static rt_bool_t h616_gate(unsigned int id, unsigned int *reg, unsigned int *bit) +{ + const struct h616_display_clock *display = h616_display_clock(id); + + if (display) + { + *reg = display->reg; + *bit = 31; + return RT_TRUE; + } + switch (id) + { + case 15: + *reg = 0x58; + *bit = 31; + return RT_TRUE; + case 30: + *reg = 0x60c; + *bit = 0; + return RT_TRUE; + case 33: + *reg = 0x630; + *bit = 31; + return RT_TRUE; + case 34: + *reg = 0x63c; + *bit = 0; + return RT_TRUE; + case 38: + *reg = 0x680; + *bit = 31; + return RT_TRUE; + case 39: + *reg = 0x68c; + *bit = 0; + return RT_TRUE; + case 40: + *reg = 0x690; + *bit = 31; + return RT_TRUE; + case 41: + *reg = 0x69c; + *bit = 0; + return RT_TRUE; + case 47: + *reg = 0x7ac; + *bit = 0; + return RT_TRUE; + case 51: + *reg = 0x804; + *bit = 1; + return RT_TRUE; + case 52: + *reg = 0x804; + *bit = 2; + return RT_TRUE; + case 55: + *reg = 0x804; + *bit = 10; + return RT_TRUE; + case 77: + *reg = 0x940; + *bit = 31; + return RT_TRUE; + case 78: + *reg = 0x944; + *bit = 31; + return RT_TRUE; + case 79: + *reg = 0x96c; + *bit = 0; + return RT_TRUE; + case 80: + *reg = 0x96c; + *bit = 1; + return RT_TRUE; + case 129: + *reg = 0x9ec; + *bit = 0; + return RT_TRUE; + case 113: + *reg = 0xa9c; + *bit = 0; + return RT_TRUE; + case H616_CLK_AUDIO_CODEC_1X: + *reg = 0xa50; + *bit = 31; + return RT_TRUE; + case H616_CLK_AUDIO_CODEC_4X: + *reg = 0xa54; + *bit = 31; + return RT_TRUE; + case H616_CLK_BUS_AUDIO_CODEC: + *reg = 0xa5c; + *bit = 0; + return RT_TRUE; + case H616_CLK_AUDIO_HUB: + *reg = 0xa60; + *bit = 31; + return RT_TRUE; + case H616_CLK_BUS_AUDIO_HUB: + *reg = 0xa6c; + *bit = 0; + return RT_TRUE; + case 115: + *reg = 0xb04; + *bit = 31; + return RT_TRUE; + case 116: + *reg = 0xb10; + *bit = 31; + return RT_TRUE; + case 117: + *reg = 0xb1c; + *bit = 0; + return RT_TRUE; + case 118: + *reg = 0xb5c; + *bit = 0; + return RT_TRUE; + case 121: + *reg = 0xb9c; + *bit = 0; + return RT_TRUE; + case 122: + *reg = 0xb9c; + *bit = 1; + return RT_TRUE; + case 127: + *reg = 0xc4c; + *bit = 0; + return RT_TRUE; + } + if (id == 42 || id == 50 || id == 86 || id == 82 || id == 83) + { + *reg = id == 42 ? 0x70c : id == 50 ? 0x804 + : id == 86 ? 0x9fc + : 0x97c; + *bit = id == 83 ? 1 : 0; + return RT_TRUE; + } + if (id >= 60 && id <= 65) + { + *reg = id < 63 ? 0x830 + (id - 60) * 4 : 0x84c; + *bit = id < 63 ? 31 : id - 63; + return RT_TRUE; + } + if (id >= H616_CLK_UART0 && id < H616_CLK_UART0 + 6) + { + *reg = H616_UART_BGR; + *bit = id - H616_CLK_UART0; + return RT_TRUE; + } + if (id >= H616_CLK_TWI0 && id < H616_CLK_TWI0 + 5) + { + *reg = H616_TWI_BGR; + *bit = id - H616_CLK_TWI0; + return RT_TRUE; + } + return RT_FALSE; +} + +static rt_bool_t h616_r_gate(unsigned int id, unsigned int *reg, unsigned int *bit) +{ + *bit = 0; + switch (id) + { + case 5: + *reg = 0x12c; + return RT_TRUE; + case 8: + *reg = 0x19c; + return RT_TRUE; + case 9: + *reg = 0x1cc; + return RT_TRUE; + case 11: + *reg = 0x1c0; + *bit = 31; + return RT_TRUE; + case 13: + *reg = 0x1bc; + return RT_TRUE; + case 14: + *reg = 0x20c; + return RT_TRUE; + default: + return RT_FALSE; + } +} + +static void h616_update(struct h616_ccu *ccu, unsigned int reg, + unsigned int bit, rt_bool_t set) +{ + rt_base_t level = rt_spin_lock_irqsave(&ccu->lock); + rt_uint32_t value = HWREG32(ccu->base + reg); + + HWREG32(ccu->base + reg) = set ? value | RT_BIT(bit) : value & ~RT_BIT(bit); + rt_spin_unlock_irqrestore(&ccu->lock, level); +} + +static rt_err_t h616_display_parent_enable(struct h616_ccu *ccu, + const struct h616_display_clock *display) +{ + unsigned int mux, reg, n, waited; + rt_uint32_t before, value; + rt_base_t level; + + mux = (HWREG32(ccu->base + display->reg) >> 24) & + (RT_BIT(display->mux_width) - 1); + if (mux >= display->parents) + { + return -RT_EINVAL; + } + if (display->id == 126 || (display->id == 29 && mux == 1)) + { + /* Peripheral PLLs are shared with storage and other running devices. */ + return h616_display_parent(ccu, display, mux) ? RT_EOK : -RT_EIO; + } + if (ccu->osc_rate[0] != 24000000) + { + return -RT_EINVAL; + } + if (display->id == 29) + { + reg = 0x60; + n = 24; /* 600 MHz DE clock. */ + } + else + { + reg = 0x40 + (mux / 2) * (display->id == 114 ? 16 : 8); + n = 98; /* 2376 MHz VCO, 594 MHz after the fixed /4. */ + } + level = rt_spin_lock_irqsave(&ccu->lock); + before = HWREG32(ccu->base + reg); + value = before; + if (!(value & RT_BIT(31))) + { + value = (value & ~((255U << 8) | 3U)) | (n << 8); + } + /* Enable lock detection and the output gate, as required by H616 CCU. */ + HWREG32(ccu->base + reg) = value | RT_BIT(31) | RT_BIT(29) | RT_BIT(27); + rt_spin_unlock_irqrestore(&ccu->lock, level); + for (waited = 0; waited < 10000; ++waited) + { + if (HWREG32(ccu->base + reg) & RT_BIT(28)) + { + return RT_EOK; + } + rt_hw_us_delay(1); + } + level = rt_spin_lock_irqsave(&ccu->lock); + HWREG32(ccu->base + reg) = before; + rt_spin_unlock_irqrestore(&ccu->lock, level); + return -RT_ETIMEOUT; +} + +static rt_err_t h616_enable(struct rt_clk_cell *cell) +{ + struct h616_ccu *ccu = cell->clk_np->priv; + const struct h616_clock_desc *desc = cell->priv; + unsigned int reg, bit; + + if (!ccu->is_r) + { + const struct h616_display_clock *display = h616_display_clock(desc->id); + rt_err_t err; + + if (display && (err = h616_display_parent_enable(ccu, display))) + { + return err; + } + } + + if (!ccu->is_r && desc->id == 116) + { + rt_base_t level; + + /* OSC32K is not usable by CEC on H616; use PERIPH0-2X / 36621. */ + if (!h616_nmp_rate(ccu, 0x20)) + { + return -RT_EIO; + } + level = rt_spin_lock_irqsave(&ccu->lock); + HWREG32(ccu->base + 0xb10) = (HWREG32(ccu->base + 0xb10) & ~(3U << 24)) | + RT_BIT(24) | RT_BIT(30) | RT_BIT(31); + rt_spin_unlock_irqrestore(&ccu->lock, level); + return RT_EOK; + } + + if (!ccu->is_r && desc->id == 15) + { + h616_update(ccu, 0x58, 31, RT_TRUE); + return h616_nmp_rate(ccu, 0x58) ? RT_EOK : -RT_EIO; + } + + /* VE is clocked solely from PLL-VE, which firmware commonly leaves off. */ + if (!ccu->is_r && desc->id == 40) + { + h616_update(ccu, 0x58, 31, RT_TRUE); + } + + if (!h616_recalc_rate(cell, 0)) + { + return -RT_EIO; + } + if (ccu->is_r ? h616_r_gate(desc->id, ®, &bit) : h616_gate(desc->id, ®, &bit)) + { + h616_update(cell->clk_np->priv, reg, bit, RT_TRUE); + } + return RT_EOK; +} + +static void h616_disable(struct rt_clk_cell *cell) +{ + struct h616_ccu *ccu = cell->clk_np->priv; + const struct h616_clock_desc *desc = cell->priv; + unsigned int reg, bit; + + if (ccu->is_r ? h616_r_gate(desc->id, ®, &bit) : h616_gate(desc->id, ®, &bit)) + { + h616_update(cell->clk_np->priv, reg, bit, RT_FALSE); + if (!ccu->is_r && desc->id == 116) + { + h616_update(ccu, reg, 30, RT_FALSE); + } + } +} + +static rt_ubase_t h616_mmc_find_rate(struct h616_ccu *ccu, rt_ubase_t requested, + rt_uint32_t *value) +{ + rt_ubase_t best = 0; + unsigned int mux, m, p; + + for (mux = 0; mux < 3; ++mux) + { + for (m = 0; m < 16; ++m) + { + for (p = 0; p < 4; ++p) + { + rt_ubase_t rate = h616_mmc_parent(ccu, mux) / (2 * (m + 1) * RT_BIT(p)); + if (rate <= requested && rate > best) + { + best = rate; + *value = (mux << 24) | (p << 8) | m; + } + } + } + } + return best; +} + +static rt_base_t h616_round_rate(struct rt_clk_cell *cell, rt_ubase_t rate, + rt_ubase_t *parent_rate) +{ + struct h616_ccu *ccu = cell->clk_np->priv; + const struct h616_clock_desc *desc = cell->priv; + rt_uint32_t value; + const struct h616_display_clock *display = h616_display_clock(desc->id); + + if (ccu->is_r && desc->id == 11) + { + return rate == 8000000 && ccu->osc_rate[0] == 24000000 ? rate : h616_recalc_rate(cell, 0); + } + if (!ccu->is_r && desc->id == H616_CLK_CPUX) + { + /* PLL_CPUX is an integer multiple of the 24 MHz oscillator. */ + if (rate >= 216000000 && rate <= 1512000000 && + !(rate % ccu->osc_rate[0])) + { + return rate; + } + return -RT_EINVAL; + } + if (!ccu->is_r && display) + { + if (rate && h616_display_find_rate(ccu, display, rate, &value) == rate) + { + return rate; + } + return -RT_EINVAL; + } + + if (!ccu->is_r && desc->id >= 60 && desc->id <= 62) + { + return h616_mmc_find_rate(ccu, rate, &value); + } + if (!ccu->is_r && (desc->id == 77 || desc->id == 78 || desc->id == 38)) + { + return h616_mp_find_rate(ccu, rate, &value); + } + return h616_recalc_rate(cell, 0); +} + +static rt_err_t h616_set_rate(struct rt_clk_cell *cell, rt_ubase_t rate, + rt_ubase_t parent_rate) +{ + struct h616_ccu *ccu = cell->clk_np->priv; + const struct h616_clock_desc *desc = cell->priv; + rt_uint32_t value; + rt_base_t level; + void *reg; + const struct h616_display_clock *display = h616_display_clock(desc->id); + + if (!ccu->is_r && desc->id == H616_CLK_CPUX) + { + rt_uint32_t old_pll; + rt_uint32_t old_cpux; + rt_uint32_t pll; + rt_uint32_t cpux; + rt_uint32_t n; + unsigned int waited; + + if (!rate || rate % ccu->osc_rate[0]) + { + return -RT_EINVAL; + } + n = rate / ccu->osc_rate[0]; + if (n < 9 || n > 63) + { + return -RT_EINVAL; + } + + /* + * The H616 CPU clock must leave PLL_CPUX while that PLL is + * retuned. Linux uses PLL_PERIPH0 (mux value 4) as this bypass + * parent; it is already required by the running system and keeps + * every CPU executing from a stable 600 MHz source. + */ + reg = ccu->base + 0x500; + level = rt_spin_lock_irqsave(&ccu->lock); + old_cpux = HWREG32(reg); + old_pll = HWREG32(ccu->base); + cpux = (old_cpux & ~(7U << 24)) | (4U << 24); + HWREG32(reg) = cpux; + rt_hw_dsb(); + rt_hw_isb(); + + /* M is a factory-test field and must remain zero on H616. */ + pll = (old_pll & ~((0xffU << 8) | RT_BIT(1))) | + ((n - 1) << 8) | RT_BIT(31) | RT_BIT(29); + HWREG32(ccu->base) = pll; + rt_spin_unlock_irqrestore(&ccu->lock, level); + + for (waited = 0; waited < 10000; ++waited) + { + if (HWREG32(ccu->base) & RT_BIT(28)) + { + break; + } + rt_hw_us_delay(1); + } + + level = rt_spin_lock_irqsave(&ccu->lock); + if (waited == 10000) + { + HWREG32(ccu->base) = old_pll; + HWREG32(reg) = old_cpux; + rt_spin_unlock_irqrestore(&ccu->lock, level); + return -RT_ETIMEOUT; + } + HWREG32(reg) = (cpux & ~(7U << 24)) | (3U << 24); + rt_hw_dsb(); + rt_hw_isb(); + rt_spin_unlock_irqrestore(&ccu->lock, level); + return RT_EOK; + } + + if (ccu->is_r && desc->id == 11 && rate == 8000000 && + ccu->osc_rate[0] == 24000000) + { + reg = ccu->base + 0x1c0; + level = rt_spin_lock_irqsave(&ccu->lock); + HWREG32(reg) = (HWREG32(reg) & ~((1U << 24) | (3U << 8) | 31U)) | + RT_BIT(24) | 2; + rt_spin_unlock_irqrestore(&ccu->lock, level); + return RT_EOK; + } + if (!ccu->is_r && display) + { + rt_uint32_t mask = ((RT_BIT(display->mux_width) - 1) << 24) | + ((RT_BIT(display->p_width) - 1) << 8) | 15; + + /* Pixel clocks must not silently become a lower refresh rate. */ + if (!rate || h616_display_find_rate(ccu, display, rate, &value) != rate) + { + return -RT_EINVAL; + } + reg = ccu->base + display->reg; + level = rt_spin_lock_irqsave(&ccu->lock); + HWREG32(reg) = (HWREG32(reg) & ~mask) | value; + rt_spin_unlock_irqrestore(&ccu->lock, level); + return RT_EOK; + } + + if (!ccu->is_r && (desc->id == H616_CLK_AUDIO_CODEC_1X || + desc->id == H616_CLK_AUDIO_CODEC_4X || + desc->id == H616_CLK_AUDIO_HUB)) + { + rt_uint32_t pll; + rt_uint32_t pattern; + rt_uint32_t divider; + + switch (rate) + { + case 22579200: + pll = (22U << 8) | (3U << 16); + pattern = 0xc001288d; + divider = 3; + break; + case 24576000: + pll = (40U << 8) | (5U << 16); + pattern = 0xc001eb85; + divider = 3; + break; + case 45158400: + pll = (22U << 8) | (3U << 16); + pattern = 0xc001288d; + divider = 1; + break; + case 49152000: + pll = (40U << 8) | (5U << 16); + pattern = 0xc001eb85; + divider = 1; + break; + default: + return -RT_EINVAL; + } + + /* PLL_AUDIO_4X feeds codec and AHUB clocks through their own dividers. */ + level = rt_spin_lock_irqsave(&ccu->lock); + HWREG32(ccu->base + 0x178) = pattern; + HWREG32(ccu->base + 0x78) = pll | RT_BIT(31) | RT_BIT(24) | RT_BIT(0); + reg = ccu->base + (desc->id == H616_CLK_AUDIO_CODEC_1X ? 0xa50 : + desc->id == H616_CLK_AUDIO_CODEC_4X ? 0xa54 : 0xa60); + HWREG32(reg) = divider | (2U << 24) | RT_BIT(31); + rt_spin_unlock_irqrestore(&ccu->lock, level); + return RT_EOK; + } + + if (!ccu->is_r && (desc->id == 77 || desc->id == 78 || desc->id == 38)) + { + unsigned int offset = desc->id == 77 ? 0x940 : desc->id == 78 ? 0x944 + : 0x680; + + if (!rate || !h616_mp_find_rate(ccu, rate, &value)) + { + return -RT_EINVAL; + } + reg = ccu->base + offset; + level = rt_spin_lock_irqsave(&ccu->lock); + HWREG32(reg) = (HWREG32(reg) & ~((7U << 24) | (3U << 8) | 15U)) | + value; + rt_spin_unlock_irqrestore(&ccu->lock, level); + return RT_EOK; + } + + if (ccu->is_r || desc->id < 60 || desc->id > 62) + { + return rate == h616_recalc_rate(cell, 0) ? RT_EOK : -RT_ENOSYS; + } + if (!h616_mmc_find_rate(ccu, rate, &value)) + { + return -RT_EINVAL; + } + reg = ccu->base + 0x830 + (desc->id - 60) * 4; + level = rt_spin_lock_irqsave(&ccu->lock); + HWREG32(reg) = (HWREG32(reg) & ~((3U << 24) | (3U << 8) | 15U)) | value; + rt_spin_unlock_irqrestore(&ccu->lock, level); + return RT_EOK; +} + +static const struct rt_clk_ops h616_clk_ops = { + .enable = h616_enable, + .disable = h616_disable, + .recalc_rate = h616_recalc_rate, + .round_rate = h616_round_rate, + .set_rate = h616_set_rate, +}; + +static rt_bool_t h616_reset_line(int id, unsigned int *reg, unsigned int *bit) +{ + *bit = 16; + switch (id) + { + case H616_RST_G2D: + *reg = 0x63c; + return RT_TRUE; + case 1: + *reg = 0x60c; + return RT_TRUE; + case 2: + *reg = 0x62c; + return RT_TRUE; + case 4: + *reg = 0x68c; + return RT_TRUE; + case 5: + *reg = 0x69c; + return RT_TRUE; + case 10: + *reg = 0x7ac; + return RT_TRUE; + case 51: + *reg = 0xb1c; + return RT_TRUE; + case 52: + *reg = 0xb1c; + *bit = 17; + return RT_TRUE; + case 53: + *reg = 0xb5c; + return RT_TRUE; + case 54: + *reg = 0xb9c; + return RT_TRUE; + case 55: + *reg = 0xb9c; + *bit = 17; + return RT_TRUE; + case 58: + *reg = 0xc4c; + return RT_TRUE; + } + if (id == 6) + { + *reg = 0x70c; + return RT_TRUE; + } + if (id == 36) + { + *reg = 0xa5c; + return RT_TRUE; + } + if (id == 37) + { + *reg = 0xa6c; + return RT_TRUE; + } + if (id == 33) + { + *reg = 0x9fc; + return RT_TRUE; + } + if (id == 59) + { + *reg = 0xa9c; + return RT_TRUE; + } + if (id == 60) + { + *reg = 0x9ec; + return RT_TRUE; + } + if (id >= 14 && id <= 16) + { + *reg = 0x84c; + *bit += id - 14; + return RT_TRUE; + } + if (id == 28 || id == 29) + { + *reg = 0x96c; + *bit += id - 28; + return RT_TRUE; + } + if (id == 30 || id == 31) + { + *reg = 0x97c; + *bit += id - 30; + return RT_TRUE; + } + if (id < H616_RST_UART0 || id >= H616_RST_TWI0 + 5) + { + return RT_FALSE; + } + *reg = id < H616_RST_TWI0 ? H616_UART_BGR : H616_TWI_BGR; + *bit = 16 + id - (id < H616_RST_TWI0 ? H616_RST_UART0 : H616_RST_TWI0); + return RT_TRUE; +} + +static rt_bool_t h616_r_reset_line(int id, unsigned int *reg, unsigned int *bit) +{ + *bit = 16; + switch (id) + { + case 1: + *reg = 0x12c; + return RT_TRUE; + case 4: + *reg = 0x19c; + return RT_TRUE; + case 5: + *reg = 0x1cc; + return RT_TRUE; + case 7: + *reg = 0x1bc; + return RT_TRUE; + default: + return RT_FALSE; + } +} + +static rt_err_t h616_reset_update(struct rt_reset_control *rstc, rt_bool_t release) +{ + struct h616_ccu *ccu = rstc->rstcer->priv; + unsigned int reg, bit; + + if (!(ccu->is_r ? h616_r_reset_line(rstc->id, ®, &bit) : h616_reset_line(rstc->id, ®, &bit))) + { + return -RT_EINVAL; + } + h616_update(rstc->rstcer->priv, reg, bit, release); + return RT_EOK; +} + +static rt_err_t h616_assert(struct rt_reset_control *rstc) +{ + return h616_reset_update(rstc, RT_FALSE); +} + +static rt_err_t h616_deassert(struct rt_reset_control *rstc) +{ + return h616_reset_update(rstc, RT_TRUE); +} + +static int h616_status(struct rt_reset_control *rstc) +{ + struct h616_ccu *ccu = rstc->rstcer->priv; + unsigned int reg, bit; + + if (!(ccu->is_r ? h616_r_reset_line(rstc->id, ®, &bit) : h616_reset_line(rstc->id, ®, &bit))) + { + return -RT_EINVAL; + } + return !(HWREG32(ccu->base + reg) & RT_BIT(bit)); +} + +static const struct rt_reset_control_ops h616_reset_ops = { + .assert = h616_assert, + .deassert = h616_deassert, + .status = h616_status, +}; + +static rt_err_t h616_ccu_probe(struct rt_platform_device *pdev) +{ + static const char * const osc_names[] = { "hosc", "losc", "iosc", "pll-periph" }; + const struct h616_ccu_soc_data *soc_data = pdev->id->data; + const struct h616_clock_desc *descs; + rt_size_t count; + struct h616_ccu *ccu; + rt_err_t err = -RT_EIO; + rt_size_t i; + + if (!soc_data) + { + return -RT_EINVAL; + } + ccu = rt_calloc(1, sizeof(*ccu)); + if (!ccu) + { + return -RT_ENOMEM; + } + ccu->is_r = soc_data->is_r; + descs = ccu->is_r ? h616_r_clock_descs : h616_clock_descs; + count = ccu->is_r ? RT_ARRAY_SIZE(h616_r_clock_descs) : RT_ARRAY_SIZE(h616_clock_descs); + ccu->base = rt_dm_dev_iomap(&pdev->parent, 0); + if (!ccu->base) + { + goto fail; + } + rt_spin_lock_init(&ccu->lock); + for (i = 0; i < (ccu->is_r ? 4 : 3); ++i) + { + ccu->osc[i] = rt_clk_get_by_name(&pdev->parent, osc_names[i]); + if (rt_is_err_or_null(ccu->osc[i])) + { + err = rt_is_err(ccu->osc[i]) ? rt_ptr_err(ccu->osc[i]) : -RT_EIO; + goto fail; + } + ccu->osc_rate[i] = rt_clk_get_rate(ccu->osc[i]); + } + if (!ccu->osc_rate[0]) + { + goto fail; + } + ccu->clocks.dev = &pdev->parent; + /* Parent references are owned above; do not acquire the list twice. */ + ccu->clocks.parents_clk = rt_err_ptr(-RT_ENOSYS); + ccu->clocks.priv = ccu; + ccu->clocks.cells = ccu->cells; + ccu->clocks.cells_nr = RT_ARRAY_SIZE(ccu->cells); + for (i = 0; i < count; ++i) + { + struct rt_clk_cell *cell = &ccu->bus_clocks[i]; + + cell->name = descs[i].name; + cell->ops = &h616_clk_ops; + cell->priv = (void *)&descs[i]; + cell->flags = RT_CLK_F_GET_RATE_NOCACHE; + ccu->cells[descs[i].id] = cell; + } + if ((err = rt_clk_register(&ccu->clocks))) + { + goto fail; + } + ccu->resets.ofw_node = pdev->parent.ofw_node; + ccu->resets.priv = ccu; + ccu->resets.ops = &h616_reset_ops; + if ((err = rt_reset_controller_register(&ccu->resets))) + { + if (rt_clk_unregister(&ccu->clocks)) + { + return err; + } + goto fail; + } + pdev->parent.user_data = ccu; + return RT_EOK; + +fail: + for (i = 0; i < RT_ARRAY_SIZE(ccu->osc); ++i) + { + if (!rt_is_err_or_null(ccu->osc[i])) + { + rt_clk_put(ccu->osc[i]); + } + } + if (ccu->base) + { + rt_iounmap(ccu->base); + } + rt_free(ccu); + return err; +} + +static const struct rt_ofw_node_id h616_ccu_ids[] = { + { .compatible = "allwinner,sun50i-h616-ccu", .data = &h616_ccu_data }, + { .compatible = "allwinner,sun50i-h616-r-ccu", .data = &h616_r_ccu_data }, + { /* sentinel */ } +}; + +static struct rt_platform_driver h616_ccu_driver = { + .name = "h616-ccu", + .ids = h616_ccu_ids, + .probe = h616_ccu_probe, +}; + +static int h616_ccu_register(void) +{ + rt_platform_driver_register(&h616_ccu_driver); + return 0; +} +INIT_SUBSYS_EXPORT(h616_ccu_register); diff --git a/bsp/allwinner/dm/clk/clk-sunxi-tcon-top.c b/bsp/allwinner/dm/clk/clk-sunxi-tcon-top.c new file mode 100644 index 000000000000..6cbdfe1debd9 --- /dev/null +++ b/bsp/allwinner/dm/clk/clk-sunxi-tcon-top.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sunxi_tcon_top.h" + +#define TCON_TOP_PORT_SEL 0x1c +#define TCON_TOP_GATE_SRC 0x20 +#define TCON_TOP_TV0_GATE RT_BIT(20) +#define TCON_TOP_HDMI_MASK (3U << 28) + +struct sunxi_tcon_top +{ + struct rt_clk_node node; + struct rt_clk_cell tv0; + struct rt_clk_cell *cells[1]; + struct rt_clk *bus, *parent; + struct rt_reset_control *reset; + struct rt_spinlock lock; + void *base; +}; + +static void tcon_top_update(struct sunxi_tcon_top *top, unsigned int reg, + rt_uint32_t mask, rt_uint32_t value) +{ + rt_base_t level = rt_spin_lock_irqsave(&top->lock); + + HWREG32(top->base + reg) = (HWREG32(top->base + reg) & ~mask) | value; + rt_spin_unlock_irqrestore(&top->lock, level); +} + +static rt_err_t tcon_top_enable(struct rt_clk_cell *cell) +{ + tcon_top_update(cell->clk_np->priv, TCON_TOP_GATE_SRC, + TCON_TOP_TV0_GATE, TCON_TOP_TV0_GATE); + return RT_EOK; +} + +static void tcon_top_disable(struct rt_clk_cell *cell) +{ + tcon_top_update(cell->clk_np->priv, TCON_TOP_GATE_SRC, TCON_TOP_TV0_GATE, 0); +} + +static rt_ubase_t tcon_top_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate) +{ + return parent_rate; +} + +static const struct rt_clk_ops tcon_top_ops = { + .enable = tcon_top_enable, + .disable = tcon_top_disable, + .recalc_rate = tcon_top_rate, +}; + +rt_err_t sunxi_tcon_top_route(struct rt_ofw_node *node, unsigned int mixer, + unsigned int tcon, rt_bool_t hdmi) +{ + struct sunxi_tcon_top *top; + rt_err_t err; + + /* H6/H616 expose TV0, whose hardware routing index is two. */ + if (!node || mixer > 1 || tcon != 2) + { + return -RT_EINVAL; + } + if ((err = rt_platform_ofw_request(node))) + { + return err; + } + if (!node->dev || !(top = node->dev->user_data)) + { + return -RT_EIO; + } + tcon_top_update(top, TCON_TOP_PORT_SEL, 3U << (mixer * 4), tcon << (mixer * 4)); + tcon_top_update(top, TCON_TOP_GATE_SRC, TCON_TOP_HDMI_MASK, hdmi ? 1U << 28 : 0); + return RT_EOK; +} + +static rt_err_t tcon_top_probe(struct rt_platform_device *pdev) +{ + struct sunxi_tcon_top *top = rt_calloc(1, sizeof(*top)); + rt_err_t err = -RT_ENOMEM; + + if (!top) + { + return err; + } + rt_spin_lock_init(&top->lock); + top->base = rt_dm_dev_iomap(&pdev->parent, 0); + top->bus = rt_clk_get_by_name(&pdev->parent, "bus"); + top->parent = rt_clk_get_by_name(&pdev->parent, "tcon-tv0"); + top->reset = rt_reset_control_get_by_index(&pdev->parent, 0); + if (!top->base || rt_is_err_or_null(top->bus) || + rt_is_err_or_null(top->parent) || rt_is_err_or_null(top->reset)) + { + err = -RT_EIO; + goto fail; + } + if ((err = rt_clk_prepare_enable(top->bus))) + { + goto fail; + } + if ((err = rt_reset_control_deassert(top->reset))) + { + goto disable_bus; + } + HWREG32(top->base + TCON_TOP_PORT_SEL) = 0; + HWREG32(top->base + TCON_TOP_GATE_SRC) = 0; + top->node.dev = &pdev->parent; + top->node.priv = top; + top->node.parents_clk = rt_err_ptr(-RT_ENOSYS); + top->node.cells = top->cells; + top->node.cells_nr = RT_ARRAY_SIZE(top->cells); + top->cells[0] = &top->tv0; + if (rt_ofw_prop_read_string_index(pdev->parent.ofw_node, + "clock-output-names", 0, &top->tv0.name)) + { + top->tv0.name = "sunxi-tcon-top-tv0"; + } + top->tv0.ops = &tcon_top_ops; + top->tv0.parents_nr = 1; + top->tv0.parent_name = top->parent->cell->name; + top->tv0.flags = RT_CLK_F_SET_RATE_PARENT | RT_CLK_F_GET_RATE_NOCACHE; + if ((err = rt_clk_register(&top->node))) + { + rt_reset_control_assert(top->reset); + goto disable_bus; + } + pdev->parent.user_data = top; + return RT_EOK; + +disable_bus: + rt_clk_disable_unprepare(top->bus); +fail: + if (!rt_is_err_or_null(top->reset)) + { + rt_reset_control_put(top->reset); + } + if (!rt_is_err_or_null(top->parent)) + { + rt_clk_put(top->parent); + } + if (!rt_is_err_or_null(top->bus)) + { + rt_clk_put(top->bus); + } + if (top->base) + { + rt_iounmap(top->base); + } + rt_free(top); + return err; +} + +static const struct rt_ofw_node_id tcon_top_ids[] = { + { .compatible = "allwinner,sun50i-h6-tcon-top" }, + { .compatible = "allwinner,sun50i-h616-tcon-top" }, + {} +}; + +static struct rt_platform_driver tcon_top_driver = { + .name = "sunxi-tcon-top", + .ids = tcon_top_ids, + .probe = tcon_top_probe, +}; + +static int tcon_top_register(void) +{ + rt_platform_driver_register(&tcon_top_driver); + return 0; +} +INIT_SUBSYS_EXPORT(tcon_top_register); diff --git a/bsp/allwinner/dm/dma/dma-sunxi.c b/bsp/allwinner/dm/dma/dma-sunxi.c index 080b3fb2f907..00ae76799f63 100644 --- a/bsp/allwinner/dm/dma/dma-sunxi.c +++ b/bsp/allwinner/dm/dma/dma-sunxi.c @@ -85,6 +85,26 @@ struct sunxi_dma rt_uint32_t requests_nr; int irq; rt_bool_t irq_installed; + rt_bool_t reset_deasserted; + rt_bool_t bus_clk_enabled; + rt_bool_t mbus_clk_enabled; +}; + +struct sunxi_dma_soc_data +{ + rt_uint32_t requests_max; + rt_bool_t enable_mbus; +}; + +static const struct sunxi_dma_soc_data sunxi_dma_data = +{ + .requests_max = SUNXI_DMA_MAX_REQUESTS, +}; + +static const struct sunxi_dma_soc_data sunxi_dma_h616_data = +{ + .requests_max = 49, + .enable_mbus = RT_TRUE, }; #define to_sunxi_dma(raw) rt_container_of(raw, struct sunxi_dma, parent) @@ -304,6 +324,13 @@ static rt_err_t sunxi_dma_build_llis(struct sunxi_dma_channel *channel, destination = channel->parent.transfer.dst_addr; } + /* The LLI format stores byte addresses in 32 bits. */ + if (source > RT_UINT32_MAX || destination > RT_UINT32_MAX || + item_length - 1 > RT_UINT32_MAX - source || + item_length - 1 > RT_UINT32_MAX - destination) + { + return -RT_EINVAL; + } lli->config = config; lli->src = source; lli->dst = destination; @@ -567,19 +594,28 @@ static void sunxi_dma_release(struct sunxi_dma *dma) rt_hw_interrupt_mask(dma->irq); rt_pic_detach_irq(dma->irq, dma); } - if (!rt_is_err_or_null(dma->mbus_clk)) + if (dma->mbus_clk_enabled) { rt_clk_disable_unprepare(dma->mbus_clk); + } + if (!rt_is_err_or_null(dma->mbus_clk)) + { rt_clk_put(dma->mbus_clk); } - if (!rt_is_err_or_null(dma->bus_clk)) + if (dma->bus_clk_enabled) { rt_clk_disable_unprepare(dma->bus_clk); + } + if (!rt_is_err_or_null(dma->bus_clk)) + { rt_clk_put(dma->bus_clk); } - if (!rt_is_err_or_null(dma->reset)) + if (dma->reset_deasserted) { rt_reset_control_assert(dma->reset); + } + if (!rt_is_err_or_null(dma->reset)) + { rt_reset_control_put(dma->reset); } if (dma->base) @@ -594,16 +630,22 @@ static rt_err_t sunxi_dma_probe(struct rt_platform_device *pdev) { rt_err_t err; struct rt_device *dev = &pdev->parent; - struct sunxi_dma *dma = rt_calloc(1, sizeof(*dma)); + const struct sunxi_dma_soc_data *soc_data = pdev->id->data; + struct sunxi_dma *dma; rt_size_t index; + if (!soc_data) + { + return -RT_EINVAL; + } + dma = rt_calloc(1, sizeof(*dma)); if (!dma) { return -RT_ENOMEM; } dma->irq = -1; dma->channels_nr = SUNXI_DMA_MAX_CHANNELS; - dma->requests_nr = SUNXI_DMA_MAX_REQUESTS; + dma->requests_nr = soc_data->requests_max; dma->base = rt_dm_dev_iomap(dev, 0); if (!dma->base) @@ -621,10 +663,11 @@ static rt_err_t sunxi_dma_probe(struct rt_platform_device *pdev) rt_ptr_err(dma->reset)); goto _fail; } - if (rt_dm_dev_prop_read_u32(dev, "dma-channels", &dma->channels_nr) || - !dma->channels_nr || dma->channels_nr > SUNXI_DMA_MAX_CHANNELS || - rt_dm_dev_prop_read_u32(dev, "dma-requests", &dma->requests_nr) || - !dma->requests_nr || dma->requests_nr > SUNXI_DMA_MAX_REQUESTS) + rt_dm_dev_prop_read_u32(dev, "dma-channels", &dma->channels_nr); + rt_dm_dev_prop_read_u32(dev, "dma-requests", &dma->requests_nr); + if (!dma->channels_nr || dma->channels_nr > SUNXI_DMA_MAX_CHANNELS || + !dma->requests_nr || dma->requests_nr > + soc_data->requests_max) { err = -RT_EINVAL; goto _fail; @@ -634,13 +677,27 @@ static rt_err_t sunxi_dma_probe(struct rt_platform_device *pdev) err = dma->irq; goto _fail; } - if ((err = rt_reset_control_deassert(dma->reset)) || - (err = rt_clk_prepare_enable(dma->bus_clk)) || - (err = rt_clk_prepare_enable(dma->mbus_clk))) + if ((err = rt_reset_control_deassert(dma->reset))) { goto _fail; } + dma->reset_deasserted = RT_TRUE; + if ((err = rt_clk_prepare_enable(dma->bus_clk))) + { + goto _fail; + } + dma->bus_clk_enabled = RT_TRUE; + if ((err = rt_clk_prepare_enable(dma->mbus_clk))) + { + goto _fail; + } + dma->mbus_clk_enabled = RT_TRUE; + if (soc_data->enable_mbus) + { + /* H616 manual: keep the MCLK interface active during initialization. */ + HWREG32(dma->base + 0x28) |= RT_BIT(2); + } dma->channels = rt_calloc(dma->channels_nr, sizeof(*dma->channels)); if (!dma->channels) { @@ -689,8 +746,9 @@ static rt_err_t sunxi_dma_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_dma_ofw_ids[] = { - { .compatible = "allwinner,sun252i-f101-dma" }, - { .compatible = "allwinner,sun20i-d1-dma" }, + { .compatible = "allwinner,sun252i-f101-dma", .data = &sunxi_dma_data }, + { .compatible = "allwinner,sun20i-d1-dma", .data = &sunxi_dma_data }, + { .compatible = "allwinner,sun50i-h616-dma", .data = &sunxi_dma_h616_data }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/dvfs/cpufreq/Kconfig b/bsp/allwinner/dm/dvfs/cpufreq/Kconfig index 9435cdd6dad9..890073df860d 100644 --- a/bsp/allwinner/dm/dvfs/cpufreq/Kconfig +++ b/bsp/allwinner/dm/dvfs/cpufreq/Kconfig @@ -6,3 +6,13 @@ config RT_DVFS_SUN252I_F101_CPUFREQ Register the C907 CPU as a frequency-only DVFS device. The CPU PLL supports linear frequency modulation; YuzukiNeko OPPs are supplied by its device tree and do not require a controllable CPU regulator. + +config RT_DVFS_SUN50I_H616_CPUFREQ + bool "Allwinner H616 CPUfreq driver" + depends on RT_USING_DVFS + depends on RT_CLK_SUN50I_H616 + default n + help + Register the shared Cortex-A53 CPUX clock as a DVFS device. Frequency + and voltage OPPs are read from the U-Boot device tree; CPU voltage is + controlled through the cpu-supply regulator when it is available. diff --git a/bsp/allwinner/dm/dvfs/cpufreq/SConscript b/bsp/allwinner/dm/dvfs/cpufreq/SConscript index 168c7e107c1e..17a0cd4a9859 100644 --- a/bsp/allwinner/dm/dvfs/cpufreq/SConscript +++ b/bsp/allwinner/dm/dvfs/cpufreq/SConscript @@ -8,6 +8,9 @@ src = [] if GetDepend(['RT_DVFS_SUN252I_F101_CPUFREQ']): src += ['dvfs-sun252i-f101-cpufreq.c'] +if GetDepend(['RT_DVFS_SUN50I_H616_CPUFREQ']): + src += ['dvfs-sun50i-h616-cpufreq.c'] + group = DefineGroup('DeviceDrivers', src, depend=[''], CPPPATH=[cwd + '/../../include']) Return('group') diff --git a/bsp/allwinner/dm/dvfs/cpufreq/dvfs-sun50i-h616-cpufreq.c b/bsp/allwinner/dm/dvfs/cpufreq/dvfs-sun50i-h616-cpufreq.c new file mode 100644 index 000000000000..7fe6a875abe5 --- /dev/null +++ b/bsp/allwinner/dm/dvfs/cpufreq/dvfs-sun50i-h616-cpufreq.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define DBG_TAG "dvfs.h616-cpu" +#define DBG_LVL DBG_INFO +#include + +static struct rt_dvfs_cpufreq *h616_cpufreq; +#ifdef RT_THERMAL_COOL_DVFS +static struct rt_platform_device *h616_cool_pdev; +#endif + +static void h616_cpufreq_destroy(struct rt_platform_device *pdev, + struct rt_dvfs_cpufreq *cpufreq) +{ + struct rt_dvfs_scaling *scaling = rt_dvfs_cpufreq_to_scaling(cpufreq); + + if (scaling->opp_table) + { + rt_dvfs_scaling_remove_opp_all(scaling); + rt_free(scaling->opp_table); + } + if (scaling->clk) + { + rt_clk_put(scaling->clk); + } + if (scaling->supply) + { + rt_regulator_put(scaling->supply); + } + if (pdev) + { + if (pdev->parent.ofw_node) + { + rt_ofw_node_put(pdev->parent.ofw_node); + } + rt_free(pdev); + } + rt_free(cpufreq); +} + +static void h616_cpufreq_fill_cpu_map(struct rt_dvfs_cpufreq *cpufreq) +{ + struct rt_ofw_node *np = RT_NULL; + + rt_ofw_foreach_cpu_node(np) + { + rt_uint32_t cpu; + + if (!rt_ofw_prop_read_u32(np, "reg", &cpu) && cpu < RT_CPUS_NR) + { + rt_bitmap_set_bit(cpufreq->cpus_map, cpu); + } + } +} + +#ifdef RT_THERMAL_COOL_DVFS +static void h616_cpufreq_register_cooling(struct rt_ofw_node *cpu_np, + struct rt_dvfs_scaling *scaling) +{ + struct rt_platform_device *cool; + rt_err_t err; + + if (!rt_ofw_prop_read_bool(cpu_np, "#cooling-cells")) + { + return; + } + cool = rt_platform_device_alloc("dvfs-cool"); + if (!cool) + { + LOG_W("allocate DVFS cooling device failed"); + return; + } + cool->parent.ofw_node = rt_ofw_node_get(cpu_np); + cool->priv = scaling; + if ((err = rt_platform_device_register(cool))) + { + LOG_W("register DVFS cooling device failed: %s", rt_strerror(err)); + rt_ofw_node_put(cool->parent.ofw_node); + rt_free(cool); + return; + } + h616_cool_pdev = cool; +} +#endif + +static int h616_cpufreq_init(void) +{ + struct rt_platform_device *pdev; + struct rt_dvfs_scaling *scaling; + struct rt_ofw_node *cpu_np; + struct rt_clk *clk; + rt_err_t err; + + if (h616_cpufreq) + { + return 0; + } + cpu_np = rt_ofw_get_next_cpu_node(RT_NULL); + if (!cpu_np) + { + LOG_W("no enabled CPU node, skip CPUfreq"); + return 0; + } + if (!(pdev = rt_platform_device_alloc("ofw-cpufreq")) || + !(h616_cpufreq = rt_calloc(1, sizeof(*h616_cpufreq)))) + { + if (pdev) + { + rt_free(pdev); + } + rt_ofw_node_put(cpu_np); + h616_cpufreq = RT_NULL; + return 0; + } + + pdev->priv = h616_cpufreq; + pdev->parent.ofw_node = cpu_np; + rt_dm_dev_set_name_auto(&pdev->parent, "cpufreq"); + h616_cpufreq_fill_cpu_map(h616_cpufreq); + scaling = rt_dvfs_cpufreq_to_scaling(h616_cpufreq); + scaling->transition_latency = 250000; + scaling->retry_delay = 100000; + + if ((err = rt_device_register(&pdev->parent, + rt_dm_dev_get_name(&pdev->parent), RT_DEVICE_FLAG_DEACTIVATE))) + { + LOG_W("register CPUfreq device failed: %s", rt_strerror(err)); + h616_cpufreq_destroy(pdev, h616_cpufreq); + h616_cpufreq = RT_NULL; + return 0; + } + clk = rt_clk_get_by_index(&pdev->parent, 0); + if (rt_is_err_or_null(clk)) + { + LOG_W("CPU clock is unavailable"); + rt_device_unregister(&pdev->parent); + h616_cpufreq_destroy(pdev, h616_cpufreq); + h616_cpufreq = RT_NULL; + return 0; + } + scaling->clk = clk; + /* Prevent generic OPP setup from changing the U-Boot boot rate. */ + scaling->cur_freq = rt_clk_get_rate(clk); + if ((err = rt_platform_device_register(pdev))) + { + LOG_W("initialize CPUfreq failed: %s", rt_strerror(err)); + rt_device_unregister(&pdev->parent); + h616_cpufreq_destroy(pdev, h616_cpufreq); + h616_cpufreq = RT_NULL; + return 0; + } + if (!pdev->parent.drv) + { + LOG_W("CPUfreq framework driver is unavailable"); + rt_bus_remove_device(&pdev->parent); + rt_device_unregister(&pdev->parent); + h616_cpufreq_destroy(pdev, h616_cpufreq); + h616_cpufreq = RT_NULL; + return 0; + } + + if (scaling->opp_table) + { + struct rt_dvfs_opp *opp = rt_dvfs_scaling_find_opp(scaling, + scaling->cur_freq); + + if (opp) + { + scaling->opp_table->current_opp = opp; + } + else + { + LOG_W("boot CPU rate %lu Hz is outside the OPP table", + scaling->cur_freq); + } + } +#ifdef RT_THERMAL_COOL_DVFS + h616_cpufreq_register_cooling(cpu_np, scaling); +#endif + + if ((err = rt_dvfs_scaling_set_governor(scaling, + RT_DVFS_GOVERNOR_TYPE_ONDEMAND))) + { + LOG_W("set ondemand governor failed: %s", rt_strerror(err)); + rt_dvfs_scaling_set_governor(scaling, RT_DVFS_GOVERNOR_TYPE_PERFORMANCE); + } + LOG_D("CPUfreq ready: %lu Hz, %lu-%lu Hz", scaling->cur_freq, + scaling->min_freq, scaling->max_freq); + return 0; +} +INIT_APP_EXPORT(h616_cpufreq_init); diff --git a/bsp/allwinner/dm/graphic/framebuffer/Kconfig b/bsp/allwinner/dm/graphic/framebuffer/Kconfig index afdb07ae845b..ddf94600b1ad 100644 --- a/bsp/allwinner/dm/graphic/framebuffer/Kconfig +++ b/bsp/allwinner/dm/graphic/framebuffer/Kconfig @@ -15,3 +15,19 @@ config RT_GRAPHIC_FB_SUNXI_DE pinctrl state, and allwinner,tcon-dclk-div. A MIPI-DSI panel must use rgb888 video mode and describe dsi,lanes. Panel power and backlight remain board concerns. + +config RT_GRAPHIC_FB_SUN50I_H616_DE33 + bool "Allwinner H616 DE33 HDMI framebuffer" + depends on RT_USING_GRAPHIC + depends on RT_USING_OFW + depends on RT_USING_CLK + depends on RT_USING_RESET + depends on RT_USING_HEAP + default n + help + Enable the H616 DE33 mixer, TCON-TV, and DW-HDMI framebuffer path. + This backend is separate from the F101 DE0/TCON0 driver. It uses the + timing supplied by display-timings when present; otherwise it starts in + the conservative CEA-861 1280x720@60 mode, reads the connected sink's + base EDID over DDC, and applies its preferred detailed timing. HDMI + audio is provided separately by the AHUB/I2S driver. diff --git a/bsp/allwinner/dm/graphic/framebuffer/SConscript b/bsp/allwinner/dm/graphic/framebuffer/SConscript index 5920beb8029d..f1cc5cdd80c2 100644 --- a/bsp/allwinner/dm/graphic/framebuffer/SConscript +++ b/bsp/allwinner/dm/graphic/framebuffer/SConscript @@ -5,6 +5,9 @@ src = [] if GetDepend(['RT_GRAPHIC_FB_SUNXI_DE']): src += ['fb-sunxi-de.c', 'sunxi-display-output.c'] +if GetDepend(['RT_GRAPHIC_FB_SUN50I_H616_DE33']): + src += ['fb-sun50i-h616-de33.c'] + group = DefineGroup('DeviceDrivers', src, depend=['']) diff --git a/bsp/allwinner/dm/graphic/framebuffer/fb-sun50i-h616-de33.c b/bsp/allwinner/dm/graphic/framebuffer/fb-sun50i-h616-de33.c new file mode 100644 index 000000000000..a56aa5ca3f78 --- /dev/null +++ b/bsp/allwinner/dm/graphic/framebuffer/fb-sun50i-h616-de33.c @@ -0,0 +1,1261 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "fb.h616" +#define DBG_LVL DBG_INFO +#include + +#include + +#include "sunxi-display-output.h" +#include "sunxi-h616-hdmi.h" +#include "sunxi_tcon_top.h" + +/* DE33 mixer register windows described by sun50i-h616.dtsi. */ +#define DE33_TOP_GLB_CTL 0x0000 +#define DE33_TOP_GLB_SIZE 0x0008 +#define DE33_TOP_GLB_CLK 0x000c +#define DE33_BLD_BASE 0x1000 +#define DE33_UI0_BASE 0xc1000 +#define DE33_UI_ATTR 0x0000 +#define DE33_UI_SIZE 0x0004 +#define DE33_UI_COORD 0x0008 +#define DE33_UI_PITCH 0x000c +#define DE33_UI_LADDR 0x0010 +#define DE33_UI_HADDR 0x0080 +#define DE33_UI_OVL_SIZE 0x0088 +#define DE33_BLD_PIPE_CTL 0x0000 +#define DE33_BLD_FCOLOR0 0x0004 +#define DE33_BLD_INSIZE0 0x0008 +#define DE33_BLD_COORD0 0x000c +#define DE33_BLD_ROUTE 0x0080 +#define DE33_BLD_PREMULTIPLY 0x0084 +#define DE33_BLD_BKCOLOR 0x0088 +#define DE33_BLD_OUTSIZE 0x008c +#define DE33_BLD_MODE0 0x0090 + +#define TCON_GCTL 0x0000 +#define TCON1_CTL 0x0090 +#define TCON1_BASIC0 0x0094 +#define TCON1_BASIC1 0x0098 +#define TCON1_BASIC2 0x009c +#define TCON1_BASIC3 0x00a0 +#define TCON1_BASIC4 0x00a4 +#define TCON1_BASIC5 0x00a8 +#define TCON1_IO_POL 0x00f0 +#define TCON1_IO_TRI 0x00f4 + +/* Synopsys DW-HDMI registers; H616 uses byte-wide register addressing. */ +#define HDMI_IH_PHY_STAT0 0x0104 +#define HDMI_IH_I2CM_STAT0 0x0105 +#define HDMI_IH_I2CMPHY_STAT0 0x0108 +#define HDMI_IH_MUTE_PHY_STAT0 0x0184 +#define HDMI_IH_MUTE_I2CM_STAT0 0x0185 +#define HDMI_TX_INVID0 0x0200 +#define HDMI_TX_INSTUFFING 0x0201 +#define HDMI_VP_PR_CD 0x0801 +#define HDMI_VP_STUFF 0x0802 +#define HDMI_VP_REMAP 0x0803 +#define HDMI_VP_CONF 0x0804 +#define HDMI_FC_INVIDCONF 0x1000 +#define HDMI_FC_INHACTV0 0x1001 +#define HDMI_FC_INHACTV1 0x1002 +#define HDMI_FC_INHBLANK0 0x1003 +#define HDMI_FC_INHBLANK1 0x1004 +#define HDMI_FC_INVACTV0 0x1005 +#define HDMI_FC_INVACTV1 0x1006 +#define HDMI_FC_INVBLANK 0x1007 +#define HDMI_FC_HSYNCINDELAY0 0x1008 +#define HDMI_FC_HSYNCINDELAY1 0x1009 +#define HDMI_FC_HSYNCINWIDTH0 0x100a +#define HDMI_FC_HSYNCINWIDTH1 0x100b +#define HDMI_FC_VSYNCINDELAY 0x100c +#define HDMI_FC_VSYNCINWIDTH 0x100d +#define HDMI_FC_CTRLDUR 0x1011 +#define HDMI_FC_EXCTRLDUR 0x1012 +#define HDMI_FC_EXCTRLSPAC 0x1013 +#define HDMI_FC_CH0PREAM 0x1014 +#define HDMI_FC_CH1PREAM 0x1015 +#define HDMI_FC_CH2PREAM 0x1016 +#define HDMI_FC_AUDICONF0 0x1025 +#define HDMI_FC_AUDICONF1 0x1026 +#define HDMI_FC_AUDICONF2 0x1027 +#define HDMI_FC_AUDICONF3 0x1028 +#define HDMI_FC_AUDSCONF 0x1063 +#define HDMI_FC_AUDSCHNLS0 0x1067 +#define HDMI_FC_AUDSCHNLS2 0x1069 +#define HDMI_FC_AUDSCHNLS3 0x106a +#define HDMI_FC_AUDSCHNLS4 0x106b +#define HDMI_FC_AUDSCHNLS5 0x106c +#define HDMI_FC_AUDSCHNLS6 0x106d +#define HDMI_FC_AUDSCHNLS7 0x106e +#define HDMI_FC_AUDSCHNLS8 0x106f +#define HDMI_FC_DATAUTO3 0x10b7 +#define HDMI_PHY_CONF0 0x3000 +#define HDMI_PHY_TST0 0x3001 +#define HDMI_PHY_STAT0 0x3004 +#define HDMI_PHY_POL0 0x3007 +#define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020 +#define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021 +#define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022 +#define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023 +#define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026 +#define HDMI_I2CM_SLAVE 0x7e00 +#define HDMI_I2CM_ADDRESS 0x7e01 +#define HDMI_I2CM_DATAI 0x7e03 +#define HDMI_I2CM_OPERATION 0x7e04 +#define HDMI_I2CM_INT 0x7e05 +#define HDMI_I2CM_CTLINT 0x7e06 +#define HDMI_I2CM_DIV 0x7e07 +#define HDMI_I2CM_SOFTRSTZ 0x7e09 +#define HDMI_MC_CLKDIS 0x4001 +#define HDMI_MC_SWRSTZ 0x4002 +#define HDMI_MC_FLOWCTRL 0x4004 +#define HDMI_MC_PHYRSTZ 0x4005 +#define HDMI_MC_HEACPHY_RST 0x4007 +#define HDMI_CSC_CFG 0x4100 +#define HDMI_AUD_CONF0 0x3100 +#define HDMI_AUD_CONF1 0x3101 +#define HDMI_AUD_N1 0x3200 +#define HDMI_AUD_N2 0x3201 +#define HDMI_AUD_N3 0x3202 +#define HDMI_AUD_CTS1 0x3203 +#define HDMI_AUD_CTS2 0x3204 +#define HDMI_AUD_CTS3 0x3205 +#define HDMI_AUD_INPUTCLKFS 0x3206 + +#define HDMI_PHY_STAT0_LOCK RT_BIT(0) +#define HDMI_PHY_STAT0_HPD RT_BIT(1) +#define HDMI_PHY_SVSRET RT_BIT(5) +#define HDMI_PHY_PDDQ RT_BIT(4) +#define HDMI_PHY_TXPWRON RT_BIT(3) +#define HDMI_PHY_DATA_EN_POL RT_BIT(1) +#define HDMI_PHY_I2C_DONE RT_BIT(1) +#define HDMI_PHY_I2C_ERROR RT_BIT(0) +#define HDMI_PHY_I2C_WRITE 0x10 +#define HDMI_PHY_I2C_ADDR 0x69 +#define HDMI_PHY_HPD 0x02 +#define HDMI_PHY_RX_SENSE0 0x10 +#define HDMI_IH_PHY_HPD 0x01 +#define HDMI_IH_PHY_RX_SENSE0 0x04 +#define HDMI_IH_I2CM_DONE 0x02 +#define HDMI_IH_I2CM_ERROR 0x01 +#define HDMI_I2CM_READ 0x01 +#define HDMI_I2CM_INT_DONE_POL 0x08 +#define HDMI_I2CM_CTLINT_NAC_POL 0x80 +#define HDMI_I2CM_CTLINT_ARB_POL 0x08 +#define HDMI_MC_CLKDIS_AUDCLK RT_BIT(3) +#define HDMI_AUD_CONF0_I2S RT_BIT(5) +#define HDMI_AUD_CONF1_WIDTH_16 0x10 +#define HDMI_AUD_CONF1_WIDTH_24 0x18 +#define HDMI_AUD_INPUTCLKFS_512FS 2 + +#define H616_HDMI_PHY_REXT_CTRL 0x0004 +#define H616_HDMI_PHY_REXT_EN RT_BIT(31) + +struct h616_fb_block +{ + struct rt_ofw_node *node; + struct rt_clk_array *clks; + struct rt_reset_control *resets; + void *base; + rt_bool_t enabled; +}; + +struct h616_fb +{ + struct rt_graphic_device parent; + struct rt_device *dev; + struct rt_clk_array *mixer_clks; + struct rt_reset_control *mixer_resets; + struct rt_ofw_node *top_np; + void *layer; + void *top; + void *display; + void *phy; + struct h616_fb_block tcon; + struct h616_fb_block hdmi; + struct h616_fb_block hdmi_phy; + struct sunxi_fb_timing timing; + rt_ubase_t framebuffer_dma; + int hpd_irq; + rt_bool_t mixer_enabled; + rt_bool_t timing_from_dt; + rt_bool_t hpd; + rt_bool_t edid_valid; + rt_uint8_t edid[sizeof(struct edid)]; +}; + +struct h616_hdmi_phy_cfg +{ + rt_uint32_t max_pixelclock; + rt_uint16_t cpce, gmp, curr; + rt_uint16_t sym, term, vlev; +}; + +static struct h616_fb *h616_hdmi_active; + +/* Values are the H616 variant from Linux's sun8i_hdmi_phy.c. */ +static const struct h616_hdmi_phy_cfg h616_hdmi_phy_cfgs[] = { + { 27000000, 0x00b3, 0x2153, 0x0012, 0x8009, 0x0007, 0x02b0 }, + { 74250000, 0x0072, 0x2145, 0x0013, 0x8019, 0x0004, 0x0290 }, + { 148500000, 0x0051, 0x214c, 0x0019, 0x8019, 0x0004, 0x0290 }, + { 297000000, 0x0040, 0x3b4c, 0x0019, 0x8039, 0x0004, 0x022b }, + { 594000000, 0x1a40, 0x3b4c, 0x0010, 0x8029, 0x0000, 0x008a }, +}; + +static void h616_program_de(struct h616_fb *fb); +static void h616_program_tcon(struct h616_fb *fb); +static rt_err_t h616_block_set_rate(struct h616_fb_block *block, + const char *name, rt_ubase_t rate); + +static const rt_uint32_t h616_fb_modes[] = { + RTGRAPHIC_PIXEL_FORMAT_ARGB888, + RTGRAPHIC_PIXEL_FORMAT_ABGR888, +}; + +static rt_err_t h616_timing_read(struct rt_ofw_node *np, const char *name, + rt_uint32_t *value) +{ + int count = rt_ofw_prop_count_of_u32(np, name); + + if (count <= 0) + { + return count ? (rt_err_t)count : -RT_ENOENT; + } + return rt_ofw_prop_read_u32_index(np, name, count >= 3 ? 1 : 0, value); +} + +static rt_err_t h616_timing_parse_node(struct rt_ofw_node *np, + struct sunxi_fb_timing *timing) +{ + rt_uint32_t value; + rt_uint64_t htotal, vtotal; + + rt_memset(timing, 0, sizeof(*timing)); + if (h616_timing_read(np, "clock-frequency", &timing->pixelclock) || + h616_timing_read(np, "hactive", &timing->hactive) || + h616_timing_read(np, "hfront-porch", &timing->hfront_porch) || + h616_timing_read(np, "hback-porch", &timing->hback_porch) || + h616_timing_read(np, "hsync-len", &timing->hsync_len) || + h616_timing_read(np, "vactive", &timing->vactive) || + h616_timing_read(np, "vfront-porch", &timing->vfront_porch) || + h616_timing_read(np, "vback-porch", &timing->vback_porch) || + h616_timing_read(np, "vsync-len", &timing->vsync_len)) + { + return -RT_EINVAL; + } + timing->hsync_active = RT_TRUE; + timing->vsync_active = RT_TRUE; + if (!rt_ofw_prop_read_u32(np, "hsync-active", &value)) + { + timing->hsync_active = !!value; + } + if (!rt_ofw_prop_read_u32(np, "vsync-active", &value)) + { + timing->vsync_active = !!value; + } + htotal = (rt_uint64_t)timing->hactive + timing->hfront_porch + + timing->hback_porch + timing->hsync_len; + vtotal = (rt_uint64_t)timing->vactive + timing->vfront_porch + + timing->vback_porch + timing->vsync_len; + if (!timing->pixelclock || !timing->hactive || !timing->vactive || + !timing->hsync_len || !timing->vsync_len || timing->hactive > 4096 || + timing->vactive > 4096 || htotal > 8192 || vtotal > 4095) + { + return -RT_EINVAL; + } + timing->htotal = (rt_uint32_t)htotal; + timing->vtotal = (rt_uint32_t)vtotal; + return RT_EOK; +} + +static rt_err_t h616_timing_parse(struct h616_fb *fb) +{ + struct rt_ofw_node *timings, *child; + rt_err_t err; + + if (!(err = h616_timing_parse_node(fb->dev->ofw_node, &fb->timing))) + { + fb->timing_from_dt = RT_TRUE; + return RT_EOK; + } + timings = rt_ofw_get_child_by_tag(fb->dev->ofw_node, "display-timings"); + if (timings) + { + child = rt_ofw_parse_phandle(timings, "native-mode", 0); + if (child) + { + err = h616_timing_parse_node(child, &fb->timing); + rt_ofw_node_put(child); + } + if (err) + { + rt_ofw_foreach_available_child_node(timings, child) + { + err = h616_timing_parse_node(child, &fb->timing); + rt_ofw_node_put(child); + if (!err) + { + break; + } + } + } + rt_ofw_node_put(timings); + if (!err) + { + fb->timing_from_dt = RT_TRUE; + return RT_EOK; + } + } + + /* Start with a CEA mode, then replace it from a connected sink's EDID. */ + fb->timing.pixelclock = 74250000; + fb->timing.hactive = 1280; + fb->timing.hfront_porch = 110; + fb->timing.hsync_len = 40; + fb->timing.hback_porch = 220; + fb->timing.vactive = 720; + fb->timing.vfront_porch = 5; + fb->timing.vsync_len = 5; + fb->timing.vback_porch = 20; + fb->timing.htotal = 1650; + fb->timing.vtotal = 750; + fb->timing.hsync_active = RT_TRUE; + fb->timing.vsync_active = RT_TRUE; + return RT_EOK; +} + +static rt_uint32_t h616_de_format(rt_uint32_t mode) +{ + switch (mode) + { + case RTGRAPHIC_PIXEL_FORMAT_ARGB888: + return 0; + case RTGRAPHIC_PIXEL_FORMAT_ABGR888: + return 1; + default: + return RT_UINT32_MAX; + } +} + +static void h616_hdmi_write(struct h616_fb *fb, rt_uint32_t reg, rt_uint8_t value) +{ + HWREG8((rt_uint8_t *)fb->hdmi.base + reg) = value; +} + +static rt_uint8_t h616_hdmi_read(struct h616_fb *fb, rt_uint32_t reg) +{ + return HWREG8((rt_uint8_t *)fb->hdmi.base + reg); +} + +static void h616_hdmi_update(struct h616_fb *fb, rt_uint32_t reg, + rt_uint8_t mask, rt_uint8_t value) +{ + h616_hdmi_write(fb, reg, (h616_hdmi_read(fb, reg) & ~mask) | (value & mask)); +} + +static rt_uint32_t h616_hdmi_audio_n(rt_uint32_t samplerate, + rt_uint32_t pixelclock) +{ + rt_uint32_t rate = samplerate; + rt_uint32_t multiplier = 1; + rt_uint32_t n; + + while (rate > 48000) + { + rate /= 2; + multiplier *= 2; + } + switch (rate) + { + case 32000: + n = pixelclock == 27027000 ? 4096 : pixelclock == 74176000 || pixelclock == 148352000 ? 11648 + : 4096; + break; + case 44100: + n = pixelclock == 25175000 ? 7007 : pixelclock == 74176000 ? 17836 + : pixelclock == 148352000 ? 8918 + : 6272; + break; + case 48000: + n = pixelclock == 27027000 ? 6144 : pixelclock == 74176000 ? 11648 + : pixelclock == 148352000 ? 5824 + : 6144; + break; + default: + n = 128 * rate / 1000; + break; + } + return n * multiplier; +} + +static rt_uint8_t h616_hdmi_audio_frequency(rt_uint32_t samplerate) +{ + switch (samplerate) + { + case 32000: + return 0x03; + case 44100: + return 0x00; + case 48000: + return 0x02; + case 88200: + return 0x08; + case 96000: + return 0x0a; + case 176400: + return 0x0c; + case 192000: + return 0x0e; + default: + return 0x01; + } +} + +static rt_uint8_t h616_hdmi_audio_original_frequency(rt_uint32_t samplerate) +{ + switch (samplerate) + { + case 32000: + return 0x0c; + case 44100: + return 0x0f; + case 48000: + return 0x0d; + case 88200: + return 0x07; + case 96000: + return 0x05; + case 176400: + return 0x03; + case 192000: + return 0x01; + default: + return 0; + } +} + +static rt_uint8_t h616_hdmi_channel_allocation(rt_uint32_t channels) +{ + static const rt_uint8_t allocations[] = { 0x00, 0x00, 0x01, 0x03, + 0x07, 0x0b, 0x13, 0x1f }; + + return allocations[channels - 1]; +} + +rt_err_t sunxi_h616_hdmi_audio_configure(rt_uint32_t samplerate, + rt_uint32_t channels, rt_uint32_t samplebits) +{ + struct h616_fb *fb = h616_hdmi_active; + rt_uint32_t n; + rt_uint8_t lanes; + + if (!fb) + { + return -RT_EIO; + } + if (!channels || channels > 8 || (samplebits != 16 && samplebits != 24 && samplebits != 32)) + { + return -RT_EINVAL; + } + n = h616_hdmi_audio_n(samplerate, fb->timing.pixelclock); + lanes = RT_BIT((channels - 1) / 2 + 1) - 1; + h616_hdmi_write(fb, HDMI_AUD_CONF0, 0x80); + h616_hdmi_write(fb, HDMI_AUD_CONF1, samplebits == 16 ? HDMI_AUD_CONF1_WIDTH_16 : HDMI_AUD_CONF1_WIDTH_24); + h616_hdmi_write(fb, HDMI_AUD_INPUTCLKFS, HDMI_AUD_INPUTCLKFS_512FS); + h616_hdmi_write(fb, HDMI_AUD_N1, n); + h616_hdmi_write(fb, HDMI_AUD_N2, n >> 8); + h616_hdmi_write(fb, HDMI_AUD_N3, n >> 16); + h616_hdmi_write(fb, HDMI_AUD_CTS1, 0); + h616_hdmi_write(fb, HDMI_AUD_CTS2, 0); + h616_hdmi_write(fb, HDMI_AUD_CTS3, 0); + h616_hdmi_write(fb, HDMI_FC_AUDICONF0, (channels - 1) << 4); + h616_hdmi_write(fb, HDMI_FC_AUDICONF1, h616_hdmi_audio_frequency(samplerate)); + h616_hdmi_write(fb, HDMI_FC_AUDICONF2, h616_hdmi_channel_allocation(channels)); + h616_hdmi_write(fb, HDMI_FC_AUDICONF3, 0); + h616_hdmi_write(fb, HDMI_FC_AUDSCONF, channels > 2 ? 1 : 0); + h616_hdmi_write(fb, HDMI_FC_AUDSCHNLS0, 1); + h616_hdmi_write(fb, HDMI_FC_AUDSCHNLS2, channels); + h616_hdmi_write(fb, HDMI_FC_AUDSCHNLS3, 0x22); + h616_hdmi_write(fb, HDMI_FC_AUDSCHNLS4, 0x22); + h616_hdmi_write(fb, HDMI_FC_AUDSCHNLS5, 0x11); + h616_hdmi_write(fb, HDMI_FC_AUDSCHNLS6, 0x11); + h616_hdmi_write(fb, HDMI_FC_AUDSCHNLS7, + 0x30 | h616_hdmi_audio_frequency(samplerate)); + h616_hdmi_write(fb, HDMI_FC_AUDSCHNLS8, + (h616_hdmi_audio_original_frequency(samplerate) << 4) | 0x0b); + h616_hdmi_write(fb, HDMI_AUD_CONF0, HDMI_AUD_CONF0_I2S | lanes); + return RT_EOK; +} + +void sunxi_h616_hdmi_audio_enable(rt_bool_t enable) +{ + struct h616_fb *fb = h616_hdmi_active; + + if (!fb) + { + return; + } + h616_hdmi_update(fb, HDMI_MC_CLKDIS, HDMI_MC_CLKDIS_AUDCLK, + enable ? 0 : HDMI_MC_CLKDIS_AUDCLK); +} + +static rt_err_t h616_hdmi_ddc_read_byte(struct h616_fb *fb, rt_uint8_t offset, + rt_uint8_t *value) +{ + rt_uint32_t waited; + + h616_hdmi_write(fb, HDMI_IH_I2CM_STAT0, + HDMI_IH_I2CM_DONE | HDMI_IH_I2CM_ERROR); + h616_hdmi_write(fb, HDMI_I2CM_ADDRESS, offset); + h616_hdmi_write(fb, HDMI_I2CM_OPERATION, HDMI_I2CM_READ); + for (waited = 0; waited < 20000; waited += 100) + { + rt_uint8_t status = h616_hdmi_read(fb, HDMI_IH_I2CM_STAT0); + + if (status & (HDMI_IH_I2CM_DONE | HDMI_IH_I2CM_ERROR)) + { + h616_hdmi_write(fb, HDMI_IH_I2CM_STAT0, status); + if (status & HDMI_IH_I2CM_ERROR) + { + return -RT_EIO; + } + *value = h616_hdmi_read(fb, HDMI_I2CM_DATAI); + return RT_EOK; + } + rt_hw_us_delay(100); + } + return -RT_ETIMEOUT; +} + +static rt_err_t h616_hdmi_read_edid(struct h616_fb *fb) +{ + rt_uint32_t sum = 0; + rt_size_t i; + rt_err_t err; + + if (!(h616_hdmi_read(fb, HDMI_PHY_STAT0) & HDMI_PHY_STAT0_HPD)) + { + return -RT_EIO; + } + h616_hdmi_write(fb, HDMI_I2CM_SOFTRSTZ, 0); + h616_hdmi_write(fb, HDMI_I2CM_DIV, 0); + h616_hdmi_write(fb, HDMI_I2CM_INT, HDMI_I2CM_INT_DONE_POL); + h616_hdmi_write(fb, HDMI_I2CM_CTLINT, + HDMI_I2CM_CTLINT_NAC_POL | HDMI_I2CM_CTLINT_ARB_POL); + h616_hdmi_write(fb, HDMI_IH_MUTE_I2CM_STAT0, + HDMI_IH_I2CM_DONE | HDMI_IH_I2CM_ERROR); + h616_hdmi_write(fb, HDMI_I2CM_SLAVE, 0x50); + + for (i = 0; i < sizeof(fb->edid); i++) + { + if ((err = h616_hdmi_ddc_read_byte(fb, i, &fb->edid[i]))) + { + return err; + } + sum += fb->edid[i]; + } + if (rt_memcmp(fb->edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) || + (sum & 0xff)) + { + return -RT_EINVAL; + } + fb->edid_valid = RT_TRUE; + return RT_EOK; +} + +static rt_err_t h616_timing_from_edid(struct h616_fb *fb) +{ + const rt_uint8_t *dtd = &fb->edid[54]; + struct sunxi_fb_timing timing = { 0 }; + rt_uint32_t hblank, vblank; + rt_uint64_t htotal, vtotal; + + if (!fb->edid_valid || (!dtd[0] && !dtd[1])) + { + return -RT_ENOENT; + } + timing.pixelclock = ((rt_uint32_t)dtd[0] | ((rt_uint32_t)dtd[1] << 8)) * 10000; + timing.hactive = dtd[2] | ((rt_uint32_t)(dtd[4] & 0xf0) << 4); + hblank = dtd[3] | ((rt_uint32_t)(dtd[4] & 0x0f) << 8); + timing.vactive = dtd[5] | ((rt_uint32_t)(dtd[7] & 0xf0) << 4); + vblank = dtd[6] | ((rt_uint32_t)(dtd[7] & 0x0f) << 8); + timing.hfront_porch = dtd[8] | ((rt_uint32_t)(dtd[11] & 0xc0) << 2); + timing.hsync_len = dtd[9] | ((rt_uint32_t)(dtd[11] & 0x30) << 4); + timing.vfront_porch = (dtd[10] >> 4) | ((rt_uint32_t)(dtd[11] & 0x0c) << 2); + timing.vsync_len = (dtd[10] & 0x0f) | ((rt_uint32_t)(dtd[11] & 0x03) << 4); + if (hblank <= timing.hfront_porch + timing.hsync_len || + vblank <= timing.vfront_porch + timing.vsync_len) + { + return -RT_EINVAL; + } + timing.hback_porch = hblank - timing.hfront_porch - timing.hsync_len; + timing.vback_porch = vblank - timing.vfront_porch - timing.vsync_len; + timing.hsync_active = !!(dtd[17] & RT_BIT(1)); + timing.vsync_active = !!(dtd[17] & RT_BIT(2)); + htotal = (rt_uint64_t)timing.hactive + hblank; + vtotal = (rt_uint64_t)timing.vactive + vblank; + if (!timing.pixelclock || !timing.hactive || !timing.vactive || + !timing.hsync_len || !timing.vsync_len || timing.hactive > 4096 || + timing.vactive > 4096 || htotal > 8192 || vtotal > 4095) + { + return -RT_EINVAL; + } + timing.htotal = htotal; + timing.vtotal = vtotal; + fb->timing = timing; + return RT_EOK; +} + +static void h616_hdmi_hpd_isr(int irq, void *param) +{ + struct h616_fb *fb = param; + rt_uint8_t status = h616_hdmi_read(fb, HDMI_IH_PHY_STAT0); + rt_uint8_t events = status & (HDMI_IH_PHY_HPD | HDMI_IH_PHY_RX_SENSE0); + + RT_UNUSED(irq); + if (!events) + { + return; + } + if (events & HDMI_IH_PHY_HPD) + { + h616_hdmi_update(fb, HDMI_PHY_POL0, HDMI_PHY_HPD, + ~h616_hdmi_read(fb, HDMI_PHY_POL0)); + } + if (events & HDMI_IH_PHY_RX_SENSE0) + { + h616_hdmi_update(fb, HDMI_PHY_POL0, HDMI_PHY_RX_SENSE0, + ~h616_hdmi_read(fb, HDMI_PHY_POL0)); + } + fb->hpd = !!(h616_hdmi_read(fb, HDMI_PHY_STAT0) & HDMI_PHY_STAT0_HPD); + h616_hdmi_write(fb, HDMI_IH_PHY_STAT0, status); + h616_hdmi_write(fb, HDMI_IH_MUTE_PHY_STAT0, + (rt_uint8_t) ~(HDMI_IH_PHY_HPD | HDMI_IH_PHY_RX_SENSE0)); +} + +static void h616_hdmi_setup_hpd(struct h616_fb *fb) +{ + h616_hdmi_write(fb, HDMI_PHY_POL0, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE0); + h616_hdmi_write(fb, HDMI_IH_PHY_STAT0, + HDMI_IH_PHY_HPD | HDMI_IH_PHY_RX_SENSE0); + h616_hdmi_write(fb, HDMI_IH_MUTE_PHY_STAT0, + (rt_uint8_t) ~(HDMI_IH_PHY_HPD | HDMI_IH_PHY_RX_SENSE0)); + fb->hpd = !!(h616_hdmi_read(fb, HDMI_PHY_STAT0) & HDMI_PHY_STAT0_HPD); +} + +static rt_err_t h616_hdmi_phy_write(struct h616_fb *fb, rt_uint16_t value, + rt_uint8_t address) +{ + rt_uint32_t waited; + + h616_hdmi_write(fb, HDMI_IH_I2CMPHY_STAT0, 0xff); + h616_hdmi_write(fb, HDMI_PHY_I2CM_ADDRESS_ADDR, address); + h616_hdmi_write(fb, HDMI_PHY_I2CM_DATAO_1_ADDR, value >> 8); + h616_hdmi_write(fb, HDMI_PHY_I2CM_DATAO_0_ADDR, value); + h616_hdmi_write(fb, HDMI_PHY_I2CM_OPERATION_ADDR, HDMI_PHY_I2C_WRITE); + for (waited = 0; waited < 20000; waited += 100) + { + rt_uint8_t status = h616_hdmi_read(fb, HDMI_IH_I2CMPHY_STAT0); + if (status & (HDMI_PHY_I2C_DONE | HDMI_PHY_I2C_ERROR)) + { + h616_hdmi_write(fb, HDMI_IH_I2CMPHY_STAT0, status); + return status & HDMI_PHY_I2C_ERROR ? -RT_EIO : RT_EOK; + } + rt_hw_us_delay(100); + } + return -RT_ETIMEOUT; +} + +static rt_err_t h616_hdmi_configure_phy(struct h616_fb *fb) +{ + const struct h616_hdmi_phy_cfg *cfg = RT_NULL; + const struct sunxi_fb_timing *timing = &fb->timing; + rt_size_t i; + struct + { + rt_uint16_t value; + rt_uint8_t reg; + } writes[8]; + + for (i = 0; i < RT_ARRAY_SIZE(h616_hdmi_phy_cfgs); i++) + { + if (timing->pixelclock <= h616_hdmi_phy_cfgs[i].max_pixelclock) + { + cfg = &h616_hdmi_phy_cfgs[i]; + break; + } + } + if (!cfg) + { + return -RT_EINVAL; + } + + /* H6/H616's external PHY requires its bandgap/reference resistor setup. */ + HWREG32(fb->phy + H616_HDMI_PHY_REXT_CTRL) = + (HWREG32(fb->phy + H616_HDMI_PHY_REXT_CTRL) & 0x0000ffff) | + 0x80c00000 | H616_HDMI_PHY_REXT_EN; + + h616_hdmi_update(fb, HDMI_PHY_CONF0, HDMI_PHY_TXPWRON | HDMI_PHY_PDDQ, + HDMI_PHY_PDDQ); + h616_hdmi_update(fb, HDMI_PHY_CONF0, HDMI_PHY_SVSRET, HDMI_PHY_SVSRET); + h616_hdmi_write(fb, HDMI_MC_PHYRSTZ, 1); + h616_hdmi_write(fb, HDMI_MC_PHYRSTZ, 0); + h616_hdmi_write(fb, HDMI_MC_HEACPHY_RST, 1); + h616_hdmi_update(fb, HDMI_PHY_TST0, RT_BIT(5), RT_BIT(5)); + h616_hdmi_write(fb, HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2C_ADDR); + h616_hdmi_update(fb, HDMI_PHY_TST0, RT_BIT(5), 0); + + writes[0].value = cfg->cpce; + writes[0].reg = 0x06; + writes[1].value = cfg->gmp; + writes[1].reg = 0x15; + writes[2].value = cfg->curr; + writes[2].reg = 0x10; + writes[3].value = 0; + writes[3].reg = 0x13; + writes[4].value = 6; + writes[4].reg = 0x17; + writes[5].value = cfg->term; + writes[5].reg = 0x19; + writes[6].value = cfg->sym; + writes[6].reg = 0x09; + writes[7].value = cfg->vlev; + writes[7].reg = 0x0e; + for (i = 0; i < RT_ARRAY_SIZE(writes); i++) + { + rt_err_t err = h616_hdmi_phy_write(fb, writes[i].value, writes[i].reg); + if (err) + { + return err; + } + } + h616_hdmi_update(fb, HDMI_PHY_CONF0, HDMI_PHY_TXPWRON | HDMI_PHY_PDDQ, + HDMI_PHY_TXPWRON); + for (i = 0; i < 50; i++) + { + if (h616_hdmi_read(fb, HDMI_PHY_STAT0) & HDMI_PHY_STAT0_LOCK) + { + return RT_EOK; + } + rt_hw_us_delay(100); + } + return -RT_ETIMEOUT; +} + +static void h616_hdmi_program(struct h616_fb *fb) +{ + const struct sunxi_fb_timing *t = &fb->timing; + rt_uint32_t hblank = t->hfront_porch + t->hsync_len + t->hback_porch; + rt_uint32_t vblank = t->vfront_porch + t->vsync_len + t->vback_porch; + rt_uint8_t pol = 0x90 | 0x08; + + if (t->hsync_active) + { + pol |= RT_BIT(5); + } + if (t->vsync_active) + { + pol |= RT_BIT(6); + } + h616_hdmi_write(fb, HDMI_TX_INVID0, 1); + h616_hdmi_write(fb, HDMI_TX_INSTUFFING, 7); + h616_hdmi_write(fb, HDMI_FC_INVIDCONF, pol); + h616_hdmi_write(fb, HDMI_FC_INHACTV0, t->hactive); + h616_hdmi_write(fb, HDMI_FC_INHACTV1, t->hactive >> 8); + h616_hdmi_write(fb, HDMI_FC_INVACTV0, t->vactive); + h616_hdmi_write(fb, HDMI_FC_INVACTV1, t->vactive >> 8); + h616_hdmi_write(fb, HDMI_FC_INHBLANK0, hblank); + h616_hdmi_write(fb, HDMI_FC_INHBLANK1, hblank >> 8); + h616_hdmi_write(fb, HDMI_FC_INVBLANK, vblank); + h616_hdmi_write(fb, HDMI_FC_HSYNCINDELAY0, t->hfront_porch); + h616_hdmi_write(fb, HDMI_FC_HSYNCINDELAY1, t->hfront_porch >> 8); + h616_hdmi_write(fb, HDMI_FC_VSYNCINDELAY, t->vfront_porch); + h616_hdmi_write(fb, HDMI_FC_HSYNCINWIDTH0, t->hsync_len); + h616_hdmi_write(fb, HDMI_FC_HSYNCINWIDTH1, t->hsync_len >> 8); + h616_hdmi_write(fb, HDMI_FC_VSYNCINWIDTH, t->vsync_len); + h616_hdmi_write(fb, HDMI_VP_PR_CD, 0); + h616_hdmi_write(fb, HDMI_VP_STUFF, 7); + h616_hdmi_write(fb, HDMI_VP_REMAP, 0); + h616_hdmi_write(fb, HDMI_VP_CONF, 0x47); + h616_hdmi_write(fb, HDMI_CSC_CFG, 0); + h616_hdmi_write(fb, HDMI_FC_CTRLDUR, 12); + h616_hdmi_write(fb, HDMI_FC_EXCTRLDUR, 32); + h616_hdmi_write(fb, HDMI_FC_EXCTRLSPAC, 1); + h616_hdmi_write(fb, HDMI_FC_CH0PREAM, 0x0b); + h616_hdmi_write(fb, HDMI_FC_CH1PREAM, 0x16); + h616_hdmi_write(fb, HDMI_FC_CH2PREAM, 0x21); + h616_hdmi_write(fb, HDMI_MC_FLOWCTRL, 0); + h616_hdmi_write(fb, HDMI_MC_CLKDIS, 0x7c); + h616_hdmi_write(fb, HDMI_MC_SWRSTZ, 0xfd); +} + +static rt_err_t h616_hdmi_apply_mode(struct h616_fb *fb) +{ + rt_err_t err; + + if ((err = h616_block_set_rate(&fb->tcon, "tcon-ch1", + fb->timing.pixelclock)) || + (err = h616_block_set_rate(&fb->hdmi, "tmds", + fb->timing.pixelclock))) + { + return err; + } + h616_program_de(fb); + h616_program_tcon(fb); + if ((err = h616_hdmi_configure_phy(fb))) + { + return err; + } + h616_hdmi_program(fb); + return RT_EOK; +} + +static void h616_program_de(struct h616_fb *fb) +{ + rt_uint32_t size = (fb->timing.hactive - 1) | ((fb->timing.vactive - 1) << 16); + rt_size_t i; + + HWREG32(fb->top + DE33_TOP_GLB_CTL) = RT_BIT(0); + HWREG32(fb->top + DE33_TOP_GLB_CLK) = 1; + HWREG32(fb->top + DE33_TOP_GLB_SIZE) = size; + HWREG32(fb->display + DE33_BLD_BASE + DE33_BLD_BKCOLOR) = 0xff000000; + HWREG32(fb->display + DE33_BLD_BASE + DE33_BLD_FCOLOR0) = 0xff000000; + HWREG32(fb->display + DE33_BLD_BASE + DE33_BLD_INSIZE0) = size; + HWREG32(fb->display + DE33_BLD_BASE + DE33_BLD_COORD0) = 0; + HWREG32(fb->display + DE33_BLD_BASE + DE33_BLD_ROUTE) = 1; + HWREG32(fb->display + DE33_BLD_BASE + DE33_BLD_PREMULTIPLY) = 0; + HWREG32(fb->display + DE33_BLD_BASE + DE33_BLD_OUTSIZE) = size; + for (i = 0; i < 4; i++) + { + HWREG32(fb->display + DE33_BLD_BASE + DE33_BLD_MODE0 + i * 4) = 0x03010301; + } + HWREG32(fb->display + DE33_BLD_BASE + DE33_BLD_PIPE_CTL) = RT_BIT(8); +} + +static void h616_program_tcon(struct h616_fb *fb) +{ + const struct sunxi_fb_timing *t = &fb->timing; + rt_uint32_t hbp = t->hback_porch + t->hsync_len; + rt_uint32_t vbp = t->vback_porch + t->vsync_len; + rt_uint32_t pol = RT_BIT(26); + rt_uint32_t delay; + + if (t->hsync_active) + { + pol |= RT_BIT(25); + } + if (t->vsync_active) + { + pol |= RT_BIT(24); + } + HWREG32(fb->tcon.base + TCON1_CTL) = 0; + HWREG32(fb->tcon.base + TCON1_BASIC0) = (t->hactive - 1) << 16 | (t->vactive - 1); + HWREG32(fb->tcon.base + TCON1_BASIC1) = (t->hactive - 1) << 16 | (t->vactive - 1); + HWREG32(fb->tcon.base + TCON1_BASIC2) = (t->hactive - 1) << 16 | (t->vactive - 1); + HWREG32(fb->tcon.base + TCON1_BASIC3) = (t->htotal - 1) << 16 | (hbp - 1); + HWREG32(fb->tcon.base + TCON1_BASIC4) = (t->vtotal * 2) << 16 | (vbp - 1); + HWREG32(fb->tcon.base + TCON1_BASIC5) = (t->hsync_len - 1) << 16 | (t->vsync_len - 1); + HWREG32(fb->tcon.base + TCON1_IO_POL) = pol; + HWREG32(fb->tcon.base + TCON1_IO_TRI) = 0; + HWREG32(fb->tcon.base + TCON_GCTL) = RT_BIT(31) | RT_BIT(0); + delay = t->vtotal > t->vactive + 2 ? t->vtotal - t->vactive - 2 : 0; + if (delay > 30) + { + delay = 30; + } + HWREG32(fb->tcon.base + TCON1_CTL) = RT_BIT(31) | (delay << 4); +} + +static void h616_program_plane(struct h616_fb *fb, struct rt_graphic_plane *plane, + rt_ubase_t dma) +{ + rt_uint32_t format = h616_de_format(plane->mode); + rt_uint32_t size = (plane->width - 1) | ((plane->height - 1) << 16); + + HWREG32(fb->layer + DE33_UI0_BASE + DE33_UI_SIZE) = size; + HWREG32(fb->layer + DE33_UI0_BASE + DE33_UI_OVL_SIZE) = size; + HWREG32(fb->layer + DE33_UI0_BASE + DE33_UI_COORD) = 0; + HWREG32(fb->layer + DE33_UI0_BASE + DE33_UI_PITCH) = plane->line_length; + HWREG32(fb->layer + DE33_UI0_BASE + DE33_UI_LADDR) = dma; + HWREG32(fb->layer + DE33_UI0_BASE + DE33_UI_HADDR) = 0; + HWREG32(fb->layer + DE33_UI0_BASE + DE33_UI_ATTR) = RT_BIT(0) | (format << 8) | (0xff << 24); +} + +static rt_err_t h616_plane_update(struct rt_graphic_plane *plane, + struct rt_device_rect_info *rect) +{ + struct h616_fb *fb = rt_container_of(plane->graphic, struct h616_fb, parent); + RT_UNUSED(rect); + if (!plane->framebuffer || !fb->framebuffer_dma) + { + return -RT_EINVAL; + } + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, plane->framebuffer, plane->screen_len); + h616_program_plane(fb, plane, fb->framebuffer_dma); + return RT_EOK; +} + +static rt_err_t h616_plane_remap(struct rt_graphic_plane *plane, rt_uint32_t mode, + struct rt_device_rect_info *rect) +{ + struct h616_fb *fb = rt_container_of(plane->graphic, struct h616_fb, parent); + rt_uint32_t bpp = rt_graphic_mode_bpp(mode); + rt_size_t length; + rt_ubase_t dma; + void *buffer; + + if (!bpp || rect->width != fb->timing.hactive || rect->height != fb->timing.vactive || + h616_de_format(mode) == RT_UINT32_MAX) + { + return -RT_EINVAL; + } + plane->line_length = RT_ALIGN(rect->width * bpp, 64) / 8; + length = plane->line_length * rect->height; + buffer = rt_malloc_align(length, 64); + if (!buffer) + { + return -RT_ENOMEM; + } + dma = (rt_ubase_t)rt_kmem_v2p(buffer); + if ((rt_uint64_t)dma > RT_UINT32_MAX) + { + rt_free_align(buffer); + return -RT_EINVAL; + } + rt_memset(buffer, 0, length); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, buffer, length); + plane->bits_per_pixel = bpp; + plane->framebuffer = buffer; + plane->screen_len = length; + plane->framebuffer_len = length; + plane->width = rect->width; + plane->height = rect->height; + fb->framebuffer_dma = dma; + h616_program_plane(fb, plane, dma); + return RT_EOK; +} + +static rt_err_t h616_plane_cleanup(struct rt_graphic_plane *plane) +{ + struct h616_fb *fb = rt_container_of(plane->graphic, struct h616_fb, parent); + if (fb->layer) + { + HWREG32(fb->layer + DE33_UI0_BASE + DE33_UI_ATTR) = 0; + } + if (plane->framebuffer) + { + rt_free_align(plane->framebuffer); + } + plane->framebuffer = RT_NULL; + fb->framebuffer_dma = 0; + return RT_EOK; +} + +static const struct rt_graphic_plane_ops h616_plane_ops = { + .update = h616_plane_update, + .fb_remap = h616_plane_remap, + .fb_cleanup = h616_plane_cleanup, +}; + +static void h616_block_put(struct h616_fb_block *block) +{ + if (block->enabled) + { + rt_clk_array_disable_unprepare(block->clks); + } + if (!rt_is_err_or_null(block->resets)) + { + rt_reset_control_put(block->resets); + } + if (!rt_is_err_or_null(block->clks)) + { + rt_clk_array_put(block->clks); + } + if (block->base) + { + rt_iounmap(block->base); + } + if (block->node) + { + rt_ofw_node_put(block->node); + } + rt_memset(block, 0, sizeof(*block)); +} + +static rt_err_t h616_block_get(struct h616_fb_block *block, const char *compatible) +{ + rt_err_t err; + + block->node = rt_ofw_find_node_by_compatible(RT_NULL, compatible); + if (!block->node) + { + return -RT_ENOENT; + } + if ((err = rt_platform_ofw_request(block->node))) + { + goto fail; + } + if (!block->node->dev) + { + err = -RT_EIO; + goto fail; + } + block->base = rt_ofw_iomap(block->node, 0); + block->clks = rt_clk_get_array(block->node->dev); + block->resets = rt_reset_control_get_array(block->node->dev); + if (!block->base || rt_is_err_or_null(block->clks) || rt_is_err_or_null(block->resets)) + { + err = -RT_EIO; + goto fail; + } + if ((err = rt_clk_array_prepare_enable(block->clks))) + { + goto fail; + } + block->enabled = RT_TRUE; + if ((err = rt_reset_control_deassert(block->resets))) + { + goto fail; + } + return RT_EOK; +fail: + h616_block_put(block); + return err; +} + +static rt_err_t h616_block_set_rate(struct h616_fb_block *block, + const char *name, rt_ubase_t rate) +{ + struct rt_clk *clk; + rt_err_t err; + + clk = rt_clk_get_by_name(block->node->dev, name); + if (rt_is_err_or_null(clk)) + { + return clk ? rt_ptr_err(clk) : -RT_ENOENT; + } + err = rt_clk_set_rate(clk, rate); + rt_clk_put(clk); + return err; +} + +static void h616_hw_disable(struct h616_fb *fb) +{ + if (fb->layer) + { + HWREG32(fb->layer + DE33_UI0_BASE + DE33_UI_ATTR) = 0; + } + if (fb->top) + { + HWREG32(fb->top + DE33_TOP_GLB_CTL) = 0; + } + if (fb->tcon.base) + { + HWREG32(fb->tcon.base + TCON1_CTL) = 0; + HWREG32(fb->tcon.base + TCON_GCTL) = 0; + } + if (fb->hdmi.base) + { + h616_hdmi_update(fb, HDMI_PHY_CONF0, HDMI_PHY_TXPWRON | HDMI_PHY_PDDQ, HDMI_PHY_PDDQ); + } +} + +static void h616_resource_put(struct h616_fb *fb) +{ + if (fb->hpd_irq >= 0) + { + rt_hw_interrupt_mask(fb->hpd_irq); + rt_hw_interrupt_uninstall(fb->hpd_irq, h616_hdmi_hpd_isr, fb); + } + if (h616_hdmi_active == fb) + { + h616_hdmi_active = RT_NULL; + } + h616_block_put(&fb->hdmi_phy); + h616_block_put(&fb->hdmi); + h616_block_put(&fb->tcon); + if (!rt_is_err_or_null(fb->mixer_resets)) + { + rt_reset_control_put(fb->mixer_resets); + } + if (!rt_is_err_or_null(fb->mixer_clks)) + { + rt_clk_array_put(fb->mixer_clks); + } + if (fb->display) + { + rt_iounmap(fb->display); + } + if (fb->top) + { + rt_iounmap(fb->top); + } + if (fb->layer) + { + rt_iounmap(fb->layer); + } + if (fb->top_np) + { + rt_ofw_node_put(fb->top_np); + } +} + +static rt_err_t h616_fb_probe(struct rt_platform_device *pdev) +{ + struct h616_fb *fb = rt_calloc(1, sizeof(*fb)); + rt_err_t err; + rt_uint64_t clocks; + rt_uint32_t refresh; + + if (!fb) + { + return -RT_ENOMEM; + } + fb->dev = &pdev->parent; + fb->hpd_irq = -1; + if ((err = h616_timing_parse(fb))) + { + goto fail; + } + fb->layer = rt_ofw_iomap(fb->dev->ofw_node, 0); + fb->top = rt_ofw_iomap(fb->dev->ofw_node, 1); + fb->display = rt_ofw_iomap(fb->dev->ofw_node, 2); + fb->mixer_clks = rt_clk_get_array(fb->dev); + fb->mixer_resets = rt_reset_control_get_array(fb->dev); + if (!fb->layer || !fb->top || !fb->display || rt_is_err_or_null(fb->mixer_clks) || + rt_is_err_or_null(fb->mixer_resets)) + { + err = -RT_EIO; + goto fail; + } + if ((err = rt_clk_array_prepare_enable(fb->mixer_clks))) + { + goto fail; + } + fb->mixer_enabled = RT_TRUE; + if ((err = rt_reset_control_deassert(fb->mixer_resets))) + { + goto fail; + } + + fb->top_np = rt_ofw_find_node_by_compatible(RT_NULL, "allwinner,sun50i-h6-tcon-top"); + if (!fb->top_np) + { + err = -RT_ENOENT; + goto fail; + } + if ((err = sunxi_tcon_top_route(fb->top_np, 0, 2, RT_TRUE))) + { + goto fail; + } + if ((err = h616_block_get(&fb->tcon, "allwinner,sun50i-h6-tcon-tv"))) + { + goto fail; + } + if ((err = h616_block_get(&fb->hdmi, "allwinner,sun50i-h616-dw-hdmi"))) + { + goto fail; + } + if ((err = h616_block_get(&fb->hdmi_phy, "allwinner,sun50i-h616-hdmi-phy"))) + { + goto fail; + } + fb->phy = fb->hdmi_phy.base; + if ((err = h616_hdmi_apply_mode(fb))) + { + LOG_E("HDMI PHY setup failed: %s", rt_strerror(err)); + goto fail; + } + h616_hdmi_setup_hpd(fb); + if (!fb->timing_from_dt && !h616_hdmi_read_edid(fb) && + !h616_timing_from_edid(fb) && (err = h616_hdmi_apply_mode(fb))) + { + LOG_E("HDMI EDID mode setup failed: %s", rt_strerror(err)); + goto fail; + } + fb->hpd_irq = rt_dm_dev_get_irq(fb->hdmi.node->dev, 0); + if (fb->hpd_irq >= 0) + { + rt_hw_interrupt_install(fb->hpd_irq, h616_hdmi_hpd_isr, fb, "hdmi-hpd"); + rt_hw_interrupt_umask(fb->hpd_irq); + } + clocks = (rt_uint64_t)fb->timing.htotal * fb->timing.vtotal; + refresh = clocks ? fb->timing.pixelclock / clocks : 0; + if ((err = rt_graphic_device_simple_register(&fb->parent, fb->timing.hactive, + fb->timing.vactive, refresh, &h616_plane_ops, h616_fb_modes, + RT_ARRAY_SIZE(h616_fb_modes)))) + { + goto fail; + } + if (fb->edid_valid) + { + rt_memcpy(&fb->parent.edid, fb->edid, sizeof(fb->edid)); + } + fb->dev->user_data = fb; + h616_hdmi_active = fb; + LOG_I("DE33 HDMI %ux%u@%u, HPD=%u, EDID=%s", fb->timing.hactive, + fb->timing.vactive, refresh, fb->hpd, fb->edid_valid ? "valid" : "unavailable"); + return RT_EOK; +fail: + h616_hw_disable(fb); + if (!rt_is_err_or_null(fb->mixer_resets)) + { + rt_reset_control_assert(fb->mixer_resets); + } + if (fb->mixer_enabled) + { + rt_clk_array_disable_unprepare(fb->mixer_clks); + } + h616_resource_put(fb); + rt_free(fb); + return err; +} + +static rt_err_t h616_fb_remove(struct rt_platform_device *pdev) +{ + struct h616_fb *fb = pdev->parent.user_data; + rt_err_t err; + if (!fb) + { + return RT_EOK; + } + if ((err = rt_graphic_device_simple_unregister(&fb->parent))) + { + return err; + } + if (h616_hdmi_active == fb) + { + h616_hdmi_active = RT_NULL; + } + h616_hw_disable(fb); + rt_reset_control_assert(fb->mixer_resets); + if (fb->mixer_enabled) + { + rt_clk_array_disable_unprepare(fb->mixer_clks); + } + h616_resource_put(fb); + rt_free(fb); + return RT_EOK; +} + +static const struct rt_ofw_node_id h616_fb_ids[] = { + { .compatible = "allwinner,sun50i-h616-de33-mixer-0" }, + {} +}; + +static struct rt_platform_driver h616_fb_driver = { + .name = "sun50i-h616-de33", + .ids = h616_fb_ids, + .probe = h616_fb_probe, + .remove = h616_fb_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(h616_fb_driver); diff --git a/bsp/allwinner/dm/hwcrypto/Kconfig b/bsp/allwinner/dm/hwcrypto/Kconfig index bcf7411f0c6b..1dc094460c26 100644 --- a/bsp/allwinner/dm/hwcrypto/Kconfig +++ b/bsp/allwinner/dm/hwcrypto/Kconfig @@ -17,3 +17,19 @@ config RT_HWCRYPTO_SUNXI_CE Hash algorithms are deliberately not selected here: this CE revision exposes only one-shot hashing while RT-Thread's hwcrypto hash API is streaming. + +config RT_HWCRYPTO_SUN50I_H616_CE + bool "Allwinner H616 CE task-DMA crypto engine" + depends on RT_USING_CLK + depends on RT_USING_DM + depends on RT_USING_HWCRYPTO + depends on RT_USING_OFW + depends on RT_USING_RESET + select RT_HWCRYPTO_USING_AES + select RT_HWCRYPTO_USING_AES_CBC + select RT_HWCRYPTO_USING_RNG + default n + help + Enable H616's task-descriptor DMA Crypto Engine. It supports AES ECB + and CBC with 128-, 192-, or 256-bit keys plus the H616 TRNG. This is a + separate CE generation from the F101 PIO engine. diff --git a/bsp/allwinner/dm/hwcrypto/SConscript b/bsp/allwinner/dm/hwcrypto/SConscript index b34094cc2d83..1124126760aa 100644 --- a/bsp/allwinner/dm/hwcrypto/SConscript +++ b/bsp/allwinner/dm/hwcrypto/SConscript @@ -5,6 +5,9 @@ src = [] if GetDepend(['RT_HWCRYPTO_SUNXI_CE']): src += ['hwcrypto-sunxi-ce.c'] +if GetDepend(['RT_HWCRYPTO_SUN50I_H616_CE']): + src += ['hwcrypto-sun50i-h616-ce.c'] + group = DefineGroup('DeviceDrivers', src, depend = ['']) Return('group') diff --git a/bsp/allwinner/dm/hwcrypto/hwcrypto-sun50i-h616-ce.c b/bsp/allwinner/dm/hwcrypto/hwcrypto-sun50i-h616-ce.c new file mode 100644 index 000000000000..67af8fc4a3aa --- /dev/null +++ b/bsp/allwinner/dm/hwcrypto/hwcrypto-sun50i-h616-ce.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "crypto.h616" +#define DBG_LVL DBG_INFO +#include + +#define H616_CE_TDQ 0x000 +#define H616_CE_ICR 0x008 +#define H616_CE_ISR 0x00c +#define H616_CE_TLR 0x010 +#define H616_CE_ESR 0x018 + +#define H616_CE_FLOW_AES 0 +#define H616_CE_FLOW_TRNG 3 +#define H616_CE_COMM_DECRYPT RT_BIT(8) +#define H616_CE_COMM_INTERRUPT RT_BIT(31) +#define H616_CE_AES 0 +#define H616_CE_TRNG_V2 0x1c +#define H616_CE_AES_128 0 +#define H616_CE_AES_192 1 +#define H616_CE_AES_256 2 +#define H616_CE_AES_ECB 0 +#define H616_CE_AES_CBC RT_BIT(8) +#define H616_CE_BLOCK_SIZE 16 +#define H616_CE_TRNG_SIZE 32 +#define H616_CE_TIMEOUT_US 3000000 + +struct h616_ce_sg +{ + rt_uint32_t address; + rt_uint32_t length; +} __attribute__((packed)); + +struct h616_ce_task +{ + rt_uint32_t id; + rt_uint32_t common_ctl; + rt_uint32_t symmetric_ctl; + rt_uint32_t asymmetric_ctl; + rt_uint32_t key; + rt_uint32_t iv; + rt_uint32_t counter; + rt_uint32_t data_length; + struct h616_ce_sg source[8]; + struct h616_ce_sg destination[8]; + rt_uint32_t next; + rt_uint32_t reserved[3]; +} __attribute__((packed, aligned(8))); + +struct h616_ce +{ + struct rt_hwcrypto_device parent; + struct rt_device *dev; + void *base; + struct rt_clk_array *clks; + struct rt_reset_control *reset; + struct rt_mutex lock; + struct h616_ce_task *task; + rt_ubase_t task_dma; + void *trng; + rt_ubase_t trng_dma; + rt_bool_t clks_enabled; +}; + +#define to_h616_ce(raw) rt_container_of(raw, struct h616_ce, parent) + +static rt_uint32_t h616_ce_readl(struct h616_ce *ce, rt_uint32_t offset) +{ + return HWREG32(ce->base + offset); +} + +static void h616_ce_writel(struct h616_ce *ce, rt_uint32_t value, + rt_uint32_t offset) +{ + HWREG32(ce->base + offset) = value; +} + +static rt_err_t h616_ce_word_address(rt_ubase_t address, rt_uint32_t *word) +{ + if ((address & 3) || address > RT_UINT32_MAX) + { + return -RT_EINVAL; + } + *word = address >> 2; + return RT_EOK; +} + +static rt_err_t h616_ce_run_task(struct h616_ce *ce, rt_uint32_t flow) +{ + rt_uint32_t task_address; + rt_uint32_t status; + rt_uint32_t elapsed; + rt_err_t err; + + if ((err = h616_ce_word_address(ce->task_dma, &task_address))) + { + return err; + } + h616_ce_writel(ce, RT_BIT(flow), H616_CE_ISR); + h616_ce_writel(ce, h616_ce_readl(ce, H616_CE_ICR) | RT_BIT(flow), H616_CE_ICR); + h616_ce_writel(ce, task_address, H616_CE_TDQ); + rt_hw_dmb(); + h616_ce_writel(ce, 1 | ((ce->task->common_ctl & 0x7f) << 8), H616_CE_TLR); + for (elapsed = 0; elapsed < H616_CE_TIMEOUT_US; elapsed++) + { + status = h616_ce_readl(ce, H616_CE_ISR); + if (status & RT_BIT(flow)) + { + h616_ce_writel(ce, RT_BIT(flow), H616_CE_ISR); + status = (h616_ce_readl(ce, H616_CE_ESR) >> (flow * 8)) & 0xff; + return status ? -RT_EIO : RT_EOK; + } + rt_hw_us_delay(1); + } + return -RT_ETIMEOUT; +} + +static rt_err_t h616_ce_aes_mode(hwcrypto_type type, rt_uint32_t *mode) +{ + switch (type) + { + case HWCRYPTO_TYPE_AES_ECB: + *mode = H616_CE_AES_ECB; + return RT_EOK; + case HWCRYPTO_TYPE_AES_CBC: + *mode = H616_CE_AES_CBC; + return RT_EOK; + default: + return -RT_ENOSYS; + } +} + +static rt_err_t h616_ce_aes_crypt(struct hwcrypto_symmetric *symmetric, + struct hwcrypto_symmetric_info *info) +{ + struct h616_ce *ce = to_h616_ce(symmetric->parent.device); + struct h616_ce_task *task = ce->task; + rt_ubase_t input_dma; + rt_ubase_t output_dma; + rt_ubase_t key_dma; + rt_ubase_t iv_dma; + rt_uint32_t mode; + rt_uint32_t key_size; + rt_uint32_t input_address; + rt_uint32_t output_address; + rt_uint32_t key_address; + rt_uint32_t iv_address = 0; + rt_err_t err; + + if (!info->length || info->length % H616_CE_BLOCK_SIZE || + !info->in || !info->out || (symmetric->key_bitlen != 128 && symmetric->key_bitlen != 192 && symmetric->key_bitlen != 256)) + { + return -RT_EINVAL; + } + if ((err = h616_ce_aes_mode(symmetric->parent.type, &mode))) + { + return err; + } + if (mode == H616_CE_AES_CBC && symmetric->iv_len != H616_CE_BLOCK_SIZE) + { + return -RT_EINVAL; + } + input_dma = (rt_ubase_t)rt_kmem_v2p((void *)info->in); + output_dma = (rt_ubase_t)rt_kmem_v2p(info->out); + key_dma = (rt_ubase_t)rt_kmem_v2p(symmetric->key); + iv_dma = (rt_ubase_t)rt_kmem_v2p(symmetric->iv); + if ((err = h616_ce_word_address(input_dma, &input_address)) || + (err = h616_ce_word_address(output_dma, &output_address)) || + (err = h616_ce_word_address(key_dma, &key_address))) + { + return err; + } + if (mode == H616_CE_AES_CBC && + (err = h616_ce_word_address(iv_dma, &iv_address))) + { + return err; + } + + key_size = symmetric->key_bitlen == 128 ? H616_CE_AES_128 : symmetric->key_bitlen == 192 ? H616_CE_AES_192 + : H616_CE_AES_256; + rt_mutex_take(&ce->lock, RT_WAITING_FOREVER); + rt_memset(task, 0, sizeof(*task)); + task->id = H616_CE_FLOW_AES; + task->common_ctl = H616_CE_AES | H616_CE_COMM_INTERRUPT | + (info->mode == HWCRYPTO_MODE_DECRYPT ? H616_CE_COMM_DECRYPT : 0); + task->symmetric_ctl = mode | key_size; + task->data_length = info->length; + task->source[0].address = input_address; + task->source[0].length = info->length / 4; + task->destination[0].address = output_address; + task->destination[0].length = info->length / 4; + task->key = key_address; + if (mode == H616_CE_AES_CBC) + { + task->iv = iv_address; + } + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void *)info->in, info->length); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, symmetric->key, + symmetric->key_bitlen / 8); + if (mode == H616_CE_AES_CBC) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, symmetric->iv, H616_CE_BLOCK_SIZE); + } + err = h616_ce_run_task(ce, H616_CE_FLOW_AES); + if (!err) + { + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, info->out, info->length); + } + rt_mutex_release(&ce->lock); + return err; +} + +static rt_uint32_t h616_ce_rng_update(struct hwcrypto_rng *rng) +{ + struct h616_ce *ce = to_h616_ce(rng->parent.device); + struct h616_ce_task *task = ce->task; + rt_uint32_t result = 0; + rt_uint32_t address; + + if (h616_ce_word_address(ce->trng_dma, &address)) + { + return 0; + } + rt_mutex_take(&ce->lock, RT_WAITING_FOREVER); + rt_memset(task, 0, sizeof(*task)); + task->id = H616_CE_FLOW_TRNG; + task->common_ctl = H616_CE_TRNG_V2 | H616_CE_COMM_INTERRUPT; + task->data_length = H616_CE_TRNG_SIZE; + task->destination[0].address = address; + task->destination[0].length = H616_CE_TRNG_SIZE / 4; + if (!h616_ce_run_task(ce, H616_CE_FLOW_TRNG)) + { + rt_memcpy(&result, ce->trng, sizeof(result)); + } + rt_mutex_release(&ce->lock); + return result; +} + +static const struct hwcrypto_symmetric_ops h616_ce_aes_ops = { + .crypt = h616_ce_aes_crypt, +}; + +static const struct hwcrypto_rng_ops h616_ce_rng_ops = { + .update = h616_ce_rng_update, +}; + +static rt_err_t h616_ce_create(struct rt_hwcrypto_ctx *ctx) +{ + rt_uint32_t mode; + + if ((ctx->type & HWCRYPTO_MAIN_TYPE_MASK) == HWCRYPTO_TYPE_AES) + { + if (h616_ce_aes_mode(ctx->type, &mode)) + { + return -RT_ENOSYS; + } + ((struct hwcrypto_symmetric *)ctx)->ops = &h616_ce_aes_ops; + return RT_EOK; + } + if ((ctx->type & HWCRYPTO_MAIN_TYPE_MASK) == HWCRYPTO_TYPE_RNG) + { + ((struct hwcrypto_rng *)ctx)->ops = &h616_ce_rng_ops; + return RT_EOK; + } + return -RT_ENOSYS; +} + +static void h616_ce_destroy(struct rt_hwcrypto_ctx *ctx) +{ + RT_UNUSED(ctx); +} + +static rt_err_t h616_ce_copy(struct rt_hwcrypto_ctx *destination, + const struct rt_hwcrypto_ctx *source) +{ + RT_UNUSED(destination); + RT_UNUSED(source); + return RT_EOK; +} + +static void h616_ce_reset(struct rt_hwcrypto_ctx *ctx) +{ + RT_UNUSED(ctx); +} + +static const struct rt_hwcrypto_ops h616_ce_ops = { + .create = h616_ce_create, + .destroy = h616_ce_destroy, + .copy = h616_ce_copy, + .reset = h616_ce_reset, +}; + +static void h616_ce_release(struct h616_ce *ce) +{ + if (!ce) + { + return; + } + if (ce->trng) + { + rt_dma_free_coherent(ce->dev, H616_CE_TRNG_SIZE, + ce->trng, ce->trng_dma); + } + if (ce->task) + { + rt_dma_free_coherent(ce->dev, sizeof(*ce->task), + ce->task, ce->task_dma); + } + if (!rt_is_err_or_null(ce->reset)) + { + rt_reset_control_assert(ce->reset); + rt_reset_control_put(ce->reset); + } + if (!rt_is_err_or_null(ce->clks)) + { + if (ce->clks_enabled) + { + rt_clk_array_disable_unprepare(ce->clks); + } + rt_clk_array_put(ce->clks); + } + if (ce->base) + { + rt_iounmap(ce->base); + } + rt_mutex_detach(&ce->lock); + rt_free(ce); +} + +static rt_err_t h616_ce_probe(struct rt_platform_device *pdev) +{ + struct h616_ce *ce = rt_calloc(1, sizeof(*ce)); + struct rt_device *dev = &pdev->parent; + rt_err_t err; + + if (!ce) + { + return -RT_ENOMEM; + } + ce->dev = dev; + rt_mutex_init(&ce->lock, "h616-ce", RT_IPC_FLAG_PRIO); + ce->base = rt_dm_dev_iomap(dev, 0); + ce->clks = rt_clk_get_array(dev); + ce->reset = rt_reset_control_get_by_index(dev, 0); + if (!ce->base || rt_is_err_or_null(ce->clks) || rt_is_err_or_null(ce->reset)) + { + err = -RT_EIO; + goto fail; + } + if ((err = rt_clk_array_prepare_enable(ce->clks)) || + (err = rt_reset_control_deassert(ce->reset))) + { + goto fail; + } + ce->clks_enabled = RT_TRUE; + ce->task = rt_dma_alloc_coherent(dev, sizeof(*ce->task), &ce->task_dma); + ce->trng = rt_dma_alloc_coherent(dev, H616_CE_TRNG_SIZE, &ce->trng_dma); + if (!ce->task || !ce->trng) + { + err = -RT_ENOMEM; + goto fail; + } + ce->parent.ops = &h616_ce_ops; + ce->parent.user_data = ce; + if ((err = rt_hwcrypto_register(&ce->parent, "h616-ce"))) + { + goto fail; + } + dev->user_data = ce; + return RT_EOK; + +fail: + h616_ce_release(ce); + return err; +} + +static rt_err_t h616_ce_remove(struct rt_platform_device *pdev) +{ + struct h616_ce *ce = pdev->parent.user_data; + + if (ce) + { + h616_ce_release(ce); + } + return RT_EOK; +} + +static const struct rt_ofw_node_id h616_ce_ofw_ids[] = { + { .compatible = "allwinner,sun50i-h616-crypto" }, + {} +}; + +static struct rt_platform_driver h616_ce_driver = { + .name = "sun50i-h616-ce", + .ids = h616_ce_ofw_ids, + .probe = h616_ce_probe, + .remove = h616_ce_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(h616_ce_driver); diff --git a/bsp/allwinner/dm/i2c/i2c-sunxi.c b/bsp/allwinner/dm/i2c/i2c-sunxi.c index bfe7721d4da3..2a027472ca20 100644 --- a/bsp/allwinner/dm/i2c/i2c-sunxi.c +++ b/bsp/allwinner/dm/i2c/i2c-sunxi.c @@ -45,6 +45,18 @@ struct sunxi_i2c struct rt_reset_control *reset; void *base; rt_uint32_t input_rate; + rt_uint32_t clear_irq; +}; + +struct sunxi_i2c_soc_data +{ + rt_uint32_t clear_irq; +}; + +static const struct sunxi_i2c_soc_data sunxi_i2c_data = { 0 }; +static const struct sunxi_i2c_soc_data sunxi_i2c_h616_data = +{ + .clear_irq = SUNXI_TWI_CTL_INTFLG, }; #define to_sunxi_i2c(raw) rt_container_of(raw, struct sunxi_i2c, parent) @@ -91,7 +103,7 @@ static void sunxi_i2c_continue(struct sunxi_i2c *i2c, rt_bool_t ack) { control &= ~SUNXI_TWI_CTL_ACK; } - sunxi_i2c_write(i2c, SUNXI_TWI_CTL, control); + sunxi_i2c_write(i2c, SUNXI_TWI_CTL, control | i2c->clear_irq); } static rt_err_t sunxi_i2c_start(struct sunxi_i2c *i2c) @@ -101,7 +113,7 @@ static rt_err_t sunxi_i2c_start(struct sunxi_i2c *i2c) control |= SUNXI_TWI_CTL_BUSEN | SUNXI_TWI_CTL_STA; control &= ~SUNXI_TWI_CTL_INTFLG; - sunxi_i2c_write(i2c, SUNXI_TWI_CTL, control); + sunxi_i2c_write(i2c, SUNXI_TWI_CTL, control | i2c->clear_irq); if ((err = sunxi_i2c_wait(i2c))) { @@ -120,7 +132,7 @@ static void sunxi_i2c_stop(struct sunxi_i2c *i2c) control |= SUNXI_TWI_CTL_BUSEN | SUNXI_TWI_CTL_STP; control &= ~(SUNXI_TWI_CTL_STA | SUNXI_TWI_CTL_ACK | SUNXI_TWI_CTL_INTFLG); - sunxi_i2c_write(i2c, SUNXI_TWI_CTL, control); + sunxi_i2c_write(i2c, SUNXI_TWI_CTL, control | i2c->clear_irq); for (timeout = SUNXI_TWI_TIMEOUT_US; timeout > 0; --timeout) { @@ -340,8 +352,14 @@ static rt_err_t sunxi_i2c_probe(struct rt_platform_device *pdev) rt_err_t err; const char *name; struct rt_device *dev = &pdev->parent; - struct sunxi_i2c *i2c = rt_calloc(1, sizeof(*i2c)); + const struct sunxi_i2c_soc_data *soc_data = pdev->id->data; + struct sunxi_i2c *i2c; + if (!soc_data) + { + return -RT_EINVAL; + } + i2c = rt_calloc(1, sizeof(*i2c)); if (!i2c) { return -RT_ENOMEM; @@ -354,6 +372,7 @@ static rt_err_t sunxi_i2c_probe(struct rt_platform_device *pdev) goto _fail; } + i2c->clear_irq = soc_data->clear_irq; i2c->clk = rt_clk_get_by_index(dev, 0); if (rt_is_err(i2c->clk)) { @@ -425,7 +444,8 @@ static rt_err_t sunxi_i2c_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_i2c_ofw_ids[] = { - { .compatible = "allwinner,sun252i-f101-i2c" }, + { .compatible = "allwinner,sun252i-f101-i2c", .data = &sunxi_i2c_data }, + { .compatible = "allwinner,sun50i-h616-i2c", .data = &sunxi_i2c_h616_data }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/include/sunxi-h616-hdmi.h b/bsp/allwinner/dm/include/sunxi-h616-hdmi.h new file mode 100644 index 000000000000..3df8ad7328b8 --- /dev/null +++ b/bsp/allwinner/dm/include/sunxi-h616-hdmi.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SUNXI_H616_HDMI_H__ +#define __SUNXI_H616_HDMI_H__ + +#include + +rt_err_t sunxi_h616_hdmi_audio_configure(rt_uint32_t samplerate, + rt_uint32_t channels, rt_uint32_t samplebits); +void sunxi_h616_hdmi_audio_enable(rt_bool_t enable); + +#endif diff --git a/bsp/allwinner/dm/include/sunxi_pinctrl.h b/bsp/allwinner/dm/include/sunxi_pinctrl.h index d582a4ef586d..2688cf449b71 100644 --- a/bsp/allwinner/dm/include/sunxi_pinctrl.h +++ b/bsp/allwinner/dm/include/sunxi_pinctrl.h @@ -40,8 +40,13 @@ struct sunxi_pinctrl_data rt_uint8_t banks_nr; rt_uint8_t irq_banks; rt_uint8_t irq_mux; + rt_uint8_t bank_base; + const rt_uint8_t *irq_bank_map; + rt_bool_t irq_read_needs_mux; }; extern const struct sunxi_pinctrl_data sun252i_f101_pinctrl_data; +extern const struct sunxi_pinctrl_data sun50i_h616_pinctrl_data; +extern const struct sunxi_pinctrl_data sun50i_h616_r_pinctrl_data; #endif /* __SUNXI_PINCTRL_H__ */ diff --git a/bsp/allwinner/dm/include/sunxi_tcon_top.h b/bsp/allwinner/dm/include/sunxi_tcon_top.h new file mode 100644 index 000000000000..2372e23d0382 --- /dev/null +++ b/bsp/allwinner/dm/include/sunxi_tcon_top.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +#ifndef SUNXI_TCON_TOP_H +#define SUNXI_TCON_TOP_H + +#include + +rt_err_t sunxi_tcon_top_route(struct rt_ofw_node *node, unsigned int mixer, + unsigned int tcon, rt_bool_t hdmi); + +#endif diff --git a/bsp/allwinner/dm/inputcapture/inputcapture-sunxi-irrx.c b/bsp/allwinner/dm/inputcapture/inputcapture-sunxi-irrx.c index 75a89a6558d6..40f164af3661 100644 --- a/bsp/allwinner/dm/inputcapture/inputcapture-sunxi-irrx.c +++ b/bsp/allwinner/dm/inputcapture/inputcapture-sunxi-irrx.c @@ -52,6 +52,18 @@ struct sunxi_irrx rt_bool_t bus_clk_enabled; rt_bool_t irq_attached; rt_bool_t opened; + rt_bool_t h616; +}; + +struct sunxi_irrx_soc_data +{ + rt_bool_t h616; +}; + +static const struct sunxi_irrx_soc_data sunxi_irrx_data = { 0 }; +static const struct sunxi_irrx_soc_data sunxi_irrx_h616_data = +{ + .h616 = RT_TRUE, }; #define raw_to_sunxi_irrx(raw) \ @@ -85,7 +97,8 @@ static void sunxi_irrx_report(struct sunxi_irrx *irrx) return; } - irrx->pulsewidth_us = sunxi_irrx_cycles_to_us(irrx->pulse_cycles); + irrx->pulsewidth_us = irrx->h616 ? irrx->pulse_cycles * 16U : + sunxi_irrx_cycles_to_us(irrx->pulse_cycles); rt_hw_inputcapture_isr(&irrx->parent, irrx->pulse_level); irrx->pulse_cycles = 0; irrx->pulse_pending = RT_FALSE; @@ -97,6 +110,10 @@ static void sunxi_irrx_store_sample(struct sunxi_irrx *irrx, rt_bool_t level = (sample & RT_BIT(7)) != 0; rt_uint32_t cycles = sample & RT_GENMASK(6, 0); + if (irrx->h616) + { + ++cycles; + } if (!cycles) { return; @@ -125,6 +142,21 @@ static void sunxi_irrx_isr(int irq, void *param) status = HWREG32(irrx->regs + SUNXI_IRRX_RXSTA); count = SUNXI_IRRX_FIFO_COUNT(status); + if (irrx->h616 && count > 64) + { + count = 64; + } + if (irrx->h616 && (status & SUNXI_IRRX_IRQ_OVERRUN)) + { + for (i = 0; i < count; ++i) + { + (void)HWREG32(irrx->regs + SUNXI_IRRX_RXFIFO); + } + irrx->pulse_cycles = 0; + irrx->pulse_pending = RT_FALSE; + HWREG32(irrx->regs + SUNXI_IRRX_RXSTA) = status & 0xff; + return; + } for (i = 0; i < count; ++i) { sunxi_irrx_store_sample(irrx, @@ -155,6 +187,17 @@ static rt_err_t sunxi_irrx_open(struct rt_inputcapture_device *inputcapture) return RT_EOK; } + if (irrx->h616) + { + if ((err = rt_clk_set_rate(irrx->mod_clk, 8000000))) + { + return err; + } + if (rt_clk_get_rate(irrx->mod_clk) != 8000000) + { + return -RT_EINVAL; + } + } if ((err = rt_reset_control_deassert(irrx->reset))) { return err; @@ -175,14 +218,16 @@ static rt_err_t sunxi_irrx_open(struct rt_inputcapture_device *inputcapture) HWREG32(irrx->regs + SUNXI_IRRX_CTRL) = 0; HWREG32(irrx->regs + SUNXI_IRRX_RXINT) = 0; HWREG32(irrx->regs + SUNXI_IRRX_RXSTA) = RT_UINT32_MAX; - HWREG32(irrx->regs + SUNXI_IRRX_CONFIG) = 2U | (32U << 2) | - (11U << 8) | (32U << 16); + HWREG32(irrx->regs + SUNXI_IRRX_CONFIG) = irrx->h616 ? + (1U << 2) | (124U << 8) : + 2U | (32U << 2) | (11U << 8) | (32U << 16); HWREG32(irrx->regs + SUNXI_IRRX_RXCTRL) = SUNXI_IRRX_POLARITY_INVERT; HWREG32(irrx->regs + SUNXI_IRRX_RXINT) = SUNXI_IRRX_IRQ_OVERRUN | SUNXI_IRRX_IRQ_PACKET_END | SUNXI_IRRX_IRQ_AVAILABLE | SUNXI_IRRX_FIFO_LEVEL(20); HWREG32(irrx->regs + SUNXI_IRRX_CTRL) = SUNXI_IRRX_GEN | - SUNXI_IRRX_RXEN | SUNXI_IRRX_ENABLE | SUNXI_IRRX_MODE_BOTH; + SUNXI_IRRX_RXEN | SUNXI_IRRX_ENABLE | + (irrx->h616 ? 0 : SUNXI_IRRX_MODE_BOTH); irrx->opened = RT_TRUE; rt_pic_irq_unmask(irrx->irq); @@ -269,8 +314,14 @@ static rt_err_t sunxi_irrx_probe(struct rt_platform_device *pdev) rt_err_t err; const char *name; struct rt_device *dev = &pdev->parent; - struct sunxi_irrx *irrx = rt_calloc(1, sizeof(*irrx)); + const struct sunxi_irrx_soc_data *soc_data = pdev->id->data; + struct sunxi_irrx *irrx; + if (!soc_data) + { + return -RT_EINVAL; + } + irrx = rt_calloc(1, sizeof(*irrx)); if (!irrx) { return -RT_ENOMEM; @@ -283,9 +334,11 @@ static rt_err_t sunxi_irrx_probe(struct rt_platform_device *pdev) goto _fail; } - irrx->mod_clk = rt_clk_get_by_name(dev, "mod"); - irrx->bus_clk = rt_clk_get_by_name(dev, "bus"); - irrx->reset = rt_reset_control_get_by_name(dev, "bus"); + irrx->h616 = soc_data->h616; + irrx->mod_clk = rt_clk_get_by_name(dev, irrx->h616 ? "ir" : "mod"); + irrx->bus_clk = rt_clk_get_by_name(dev, irrx->h616 ? "apb" : "bus"); + irrx->reset = irrx->h616 ? rt_reset_control_get_by_index(dev, 0) : + rt_reset_control_get_by_name(dev, "bus"); if (rt_is_err_or_null(irrx->mod_clk) || rt_is_err_or_null(irrx->bus_clk) || rt_is_err_or_null(irrx->reset)) @@ -310,6 +363,7 @@ static rt_err_t sunxi_irrx_probe(struct rt_platform_device *pdev) irrx->irq_attached = RT_TRUE; rt_pic_irq_mask(irrx->irq); + rt_dm_dev_set_name_auto(dev, "irrx"); name = rt_dm_dev_get_name(dev); irrx->parent.ops = &sunxi_irrx_ops; if ((err = rt_device_inputcapture_register(&irrx->parent, name, irrx))) @@ -328,7 +382,8 @@ static rt_err_t sunxi_irrx_probe(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_irrx_ofw_ids[] = { - { .compatible = "allwinner,sun252i-f101-irrx" }, + { .compatible = "allwinner,sun252i-f101-irrx", .data = &sunxi_irrx_data }, + { .compatible = "allwinner,sun50i-h616-ir", .data = &sunxi_irrx_h616_data }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c b/bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c index 47b6877f5a3c..691a7cbfa47d 100644 --- a/bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c +++ b/bsp/allwinner/dm/nvmem/nvmem-sunxi-sid.c @@ -25,9 +25,33 @@ struct sunxi_sid struct rt_nvmem_device parent; void *base; + rt_uint32_t value_offset; struct rt_spinlock lock; }; +struct sunxi_sid_soc_data +{ + rt_uint32_t value_offset; + rt_uint32_t size; + rt_uint8_t word_size; + rt_uint8_t stride; +}; + +static const struct sunxi_sid_soc_data sunxi_sid_data = +{ + .size = 64, + .word_size = sizeof(rt_uint32_t), + .stride = sizeof(rt_uint32_t), +}; + +static const struct sunxi_sid_soc_data sunxi_sid_h616_data = +{ + .value_offset = 0x200, + .size = 0x100, + .word_size = 1, + .stride = 1, +}; + #define to_sunxi_sid(raw) rt_container_of(raw, struct sunxi_sid, parent) static rt_err_t sunxi_sid_read_word(struct sunxi_sid *sid, rt_uint32_t offset, @@ -36,6 +60,12 @@ static rt_err_t sunxi_sid_read_word(struct sunxi_sid *sid, rt_uint32_t offset, rt_ubase_t level; int retry; + if (sid->value_offset) + { + *value = HWREG32(sid->base + sid->value_offset + offset); + return RT_EOK; + } + level = rt_spin_lock_irqsave(&sid->lock); HWREG32(sid->base + SUNXI_SID_PR_ADDR) = offset / sizeof(*value); HWREG32(sid->base + SUNXI_SID_PRCTL) = SUNXI_SID_READ_LOCK | @@ -108,8 +138,14 @@ static rt_err_t sunxi_sid_probe(struct rt_platform_device *pdev) { rt_err_t err; struct rt_device *dev = &pdev->parent; - struct sunxi_sid *sid = rt_calloc(1, sizeof(*sid)); + const struct sunxi_sid_soc_data *soc_data = pdev->id->data; + struct sunxi_sid *sid; + if (!soc_data) + { + return -RT_EINVAL; + } + sid = rt_calloc(1, sizeof(*sid)); if (!sid) { return -RT_ENOMEM; @@ -125,9 +161,10 @@ static rt_err_t sunxi_sid_probe(struct rt_platform_device *pdev) rt_spin_lock_init(&sid->lock); sid->parent.parent.ofw_node = dev->ofw_node; sid->parent.reg_read = sunxi_sid_read; - sid->parent.size = 64; - sid->parent.stride = sizeof(rt_uint32_t); - sid->parent.word_size = sizeof(rt_uint32_t); + sid->value_offset = soc_data->value_offset; + sid->parent.size = soc_data->size; + sid->parent.stride = soc_data->stride; + sid->parent.word_size = soc_data->word_size; sid->parent.read_only = RT_TRUE; if ((err = rt_nvmem_device_register(&sid->parent))) @@ -163,7 +200,9 @@ static rt_err_t sunxi_sid_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_sid_ofw_ids[] = { - { .compatible = "allwinner,sun252i-f101-sid" }, + { .compatible = "allwinner,sun252i-f101-sid", .data = &sunxi_sid_data }, + { .compatible = "allwinner,sun50i-h616-sid", .data = &sunxi_sid_h616_data }, + { .compatible = "allwinner,sun50i-a64-sid", .data = &sunxi_sid_h616_data }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/pinctrl/Kconfig b/bsp/allwinner/dm/pinctrl/Kconfig index 8b4578746f19..0e0ecf5c3d99 100644 --- a/bsp/allwinner/dm/pinctrl/Kconfig +++ b/bsp/allwinner/dm/pinctrl/Kconfig @@ -1,3 +1,12 @@ +config RT_PINCTRL_SUN50I_H616 + bool "Allwinner H616 main pin controller" + depends on RT_USING_PINCTRL && RT_USING_OFW + default n + help + Provide main PIO GPIO and UART/TWI/MMC/SPI pin multiplexing. + Includes main PIO interrupts and always-on R_PIO pin multiplexing. + Keep firmware-programmed bank supply voltages unchanged. + config RT_PINCTRL_SUN252I_F101 bool "Allwinner Sun252i F101 pin controller" depends on RT_USING_PINCTRL diff --git a/bsp/allwinner/dm/pinctrl/SConscript b/bsp/allwinner/dm/pinctrl/SConscript index 5a07cdcd411a..4666d66be652 100644 --- a/bsp/allwinner/dm/pinctrl/SConscript +++ b/bsp/allwinner/dm/pinctrl/SConscript @@ -5,7 +5,13 @@ cwd = GetCurrentDir() src = [] if GetDepend(['RT_PINCTRL_SUN252I_F101']): - src += ['pinctrl-sunxi.c', 'pinctrl-sun252i-f101.c'] + src += ['pinctrl-sun252i-f101.c'] + +if GetDepend(['RT_PINCTRL_SUN50I_H616']): + src += ['pinctrl-sun50i-h616.c'] + +if src: + src += ['pinctrl-sunxi.c'] group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = [cwd + '/../include', RTT_ROOT + '/components/drivers/pin']) diff --git a/bsp/allwinner/dm/pinctrl/pinctrl-sun50i-h616.c b/bsp/allwinner/dm/pinctrl/pinctrl-sun50i-h616.c new file mode 100644 index 000000000000..0f060f691c4b --- /dev/null +++ b/bsp/allwinner/dm/pinctrl/pinctrl-sun50i-h616.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +static const struct sunxi_pinctrl_layout h616_layout = { + .bank_stride = 0x24, + .mux_reg = 0x00, + .data_reg = 0x10, + .drive_reg = 0x14, + .pull_reg = 0x1c, + .mux_bits = 4, + .drive_bits = 2, + .pull_bits = 2, + .disabled_mux = 7, +}; + +/* Preserve hardware bank numbering and the holes between banks. */ +static const rt_uint8_t h616_bank_pins[] = { 13, 0, 17, 0, 0, 7, 20, 11, 17 }; +static const rt_uint8_t h616_irq_bank_map[] = { 0, 2, 3, 4, 5, 6, 7, 8 }; + +static const struct sunxi_pinctrl_function h616_functions[] = { + { "uart0", 7, 0, 2, 2 }, + { "uart0", 5, 2, 1, 3 }, + { "uart0", 5, 4, 1, 3 }, + { "uart1", 6, 6, 4, 2 }, + { "uart2", 6, 15, 4, 2 }, + { "uart2", 7, 5, 4, 2 }, + { "uart2", 8, 5, 4, 3 }, + { "uart3", 8, 9, 4, 3 }, + { "uart4", 8, 13, 4, 3 }, + { "uart5", 7, 2, 2, 2 }, + { "i2c0", 8, 5, 2, 5 }, + { "i2c1", 7, 0, 2, 5 }, + { "i2c1", 8, 7, 2, 5 }, + { "i2c2", 7, 2, 2, 5 }, + { "i2c2", 8, 9, 2, 5 }, + { "i2c3", 6, 17, 2, 5 }, + { "i2c3", 7, 4, 2, 5 }, + { "i2c3", 0, 10, 2, 2 }, + { "i2c4", 6, 15, 2, 5 }, + { "i2c4", 7, 6, 2, 5 }, + { "mmc0", 5, 0, 6, 2 }, + { "mmc1", 6, 0, 6, 2 }, + { "mmc2", 2, 0, 2, 3 }, + { "mmc2", 2, 5, 2, 3 }, + { "mmc2", 2, 8, 4, 3 }, + { "mmc2", 2, 13, 4, 3 }, + { "spi0", 2, 0, 1, 4 }, + { "spi0", 2, 2, 3, 4 }, + { "spi0", 2, 7, 1, 4 }, + { "spi0", 2, 15, 2, 4 }, + { "spi1", 7, 5, 5, 4 }, + { "pwm1", 7, 3, 1, 4 }, + { "pwm2", 7, 2, 1, 4 }, + { "pwm3", 7, 0, 1, 4 }, + { "pwm4", 7, 1, 1, 4 }, + { "pwm0", 6, 19, 1, 4 }, + { "pwm5", 0, 12, 1, 2 }, + { "i2s3", 7, 5, 5, 3 }, + { "ir_rx", 7, 10, 1, 3 }, +}; + +const struct sunxi_pinctrl_data sun50i_h616_pinctrl_data = { + .layout = &h616_layout, + .bank_pins = h616_bank_pins, + .banks_nr = RT_ARRAY_SIZE(h616_bank_pins), + .functions = h616_functions, + .functions_nr = RT_ARRAY_SIZE(h616_functions), + .irq_banks = RT_ARRAY_SIZE(h616_irq_bank_map), + .irq_bank_map = h616_irq_bank_map, + .irq_mux = 6, + .irq_read_needs_mux = RT_TRUE, +}; + +static const rt_uint8_t h616_r_bank_pins[] = { 2 }; +static const struct sunxi_pinctrl_function h616_r_functions[] = { + { "s_rsb", 0, 0, 2, 2 }, + { "s_i2c", 0, 0, 2, 3 }, +}; + +const struct sunxi_pinctrl_data sun50i_h616_r_pinctrl_data = { + .layout = &h616_layout, + .bank_pins = h616_r_bank_pins, + .bank_base = 11, + .banks_nr = RT_ARRAY_SIZE(h616_r_bank_pins), + .functions = h616_r_functions, + .functions_nr = RT_ARRAY_SIZE(h616_r_functions), +}; diff --git a/bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c b/bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c index 1ab3813f0977..d465af1a9bcf 100644 --- a/bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c +++ b/bsp/allwinner/dm/pinctrl/pinctrl-sunxi.c @@ -92,13 +92,17 @@ static rt_err_t sunxi_pinctrl_pin_from_name(const struct sunxi_pinctrl *pinctrl, rt_uint32_t number = 0; rt_uint32_t bank; - if (!name || name[0] != 'P' || name[1] < 'A' || - name[1] >= 'A' + pinctrl->data->banks_nr || !name[2]) + if (!name || name[0] != 'P' || name[1] < 'A' + pinctrl->data->bank_base || + name[1] >= 'A' + pinctrl->data->bank_base + pinctrl->data->banks_nr || !name[2]) { return -RT_EINVAL; } - bank = name[1] - 'A'; + bank = name[1] - 'A' - pinctrl->data->bank_base; + if (!pinctrl->data->bank_pins[bank]) + { + return -RT_EINVAL; + } for (name += 2; *name; ++name) { @@ -275,6 +279,26 @@ static rt_ssize_t sunxi_pinctrl_pin_read(struct rt_device *device, rt_base_t pin bank = pin / SUNXI_PINS_PER_BANK; number = pin % SUNXI_PINS_PER_BANK; + if (pinctrl->data->irq_read_needs_mux) + { + rt_base_t level = rt_spin_lock_irqsave(&pinctrl->lock); + void *reg = pinctrl->base + bank * layout->bank_stride + + layout->mux_reg + number / 8 * 4; + rt_uint32_t shift = number % 8 * 4; + rt_uint32_t mux = HWREG32(reg); + rt_ssize_t value; + + if (((mux >> shift) & 0xf) == pinctrl->data->irq_mux) + { + HWREG32(reg) = mux & ~(0xfU << shift); + } + value = !!(HWREG32(pinctrl->base + bank * layout->bank_stride + + layout->data_reg) & RT_BIT(number)); + HWREG32(reg) = mux; + rt_spin_unlock_irqrestore(&pinctrl->lock, level); + return value; + } + return !!(HWREG32(pinctrl->base + bank * layout->bank_stride + layout->data_reg) & RT_BIT(number)); } @@ -335,6 +359,22 @@ static rt_err_t sunxi_pinctrl_irq_mode_to_hw(rt_uint8_t mode, return RT_EOK; } +static rt_bool_t sunxi_pinctrl_irq_bank_valid(struct sunxi_pinctrl *pinctrl, + rt_uint32_t bank) +{ + rt_size_t index; + + for (index = 0; index < pinctrl->data->irq_banks; ++index) + { + if (bank == (pinctrl->data->irq_bank_map ? + pinctrl->data->irq_bank_map[index] : index)) + { + return RT_TRUE; + } + } + return RT_FALSE; +} + static rt_err_t sunxi_pinctrl_pin_irq_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode) { @@ -352,7 +392,8 @@ static rt_err_t sunxi_pinctrl_pin_irq_mode(struct rt_device *device, { return -RT_ENOSYS; } - if ((bank = pin / SUNXI_PINS_PER_BANK) >= pinctrl->data->irq_banks || + bank = pin / SUNXI_PINS_PER_BANK; + if (!sunxi_pinctrl_irq_bank_valid(pinctrl, bank) || sunxi_pinctrl_irq_mode_to_hw(mode, &hw_mode)) { return -RT_EINVAL; @@ -394,7 +435,7 @@ static rt_err_t sunxi_pinctrl_pin_irq_enable(struct rt_device *device, bank = pin / SUNXI_PINS_PER_BANK; number = pin % SUNXI_PINS_PER_BANK; - if (bank >= pinctrl->data->irq_banks) + if (!sunxi_pinctrl_irq_bank_valid(pinctrl, bank)) { return -RT_EINVAL; } @@ -560,7 +601,8 @@ static rt_err_t sunxi_pinctrl_irq_init(struct sunxi_pinctrl *pinctrl, struct sunxi_pinctrl_irq_bank *irq_bank = &pinctrl->irq_banks[index]; irq_bank->pinctrl = pinctrl; - irq_bank->bank = index; + irq_bank->bank = pinctrl->data->irq_bank_map ? + pinctrl->data->irq_bank_map[index] : index; irq_bank->irq = rt_dm_dev_get_irq(dev, index); if (irq_bank->irq < 0) { @@ -688,7 +730,7 @@ static rt_err_t sunxi_pinctrl_probe(struct rt_platform_device *pdev) !data->layout->drive_bits || !data->layout->pull_bits || data->layout->mux_bits > 16 || data->layout->drive_bits > 16 || data->layout->pull_bits > 16 || - data->irq_banks > data->banks_nr || + data->irq_banks > data->banks_nr || data->irq_banks > SUNXI_MAX_EINT_BANKS || (data->irq_banks && data->irq_mux > sunxi_pinctrl_field_mask(data->layout->mux_bits)) || data->layout->disabled_mux > sunxi_pinctrl_field_mask(data->layout->mux_bits) || @@ -699,8 +741,15 @@ static rt_err_t sunxi_pinctrl_probe(struct rt_platform_device *pdev) for (index = 0; index < data->banks_nr; ++index) { - if (!data->bank_pins[index] || - data->bank_pins[index] > SUNXI_PINS_PER_BANK) + if (data->bank_pins[index] > SUNXI_PINS_PER_BANK) + { + return -RT_EINVAL; + } + } + + for (index = 0; data->irq_bank_map && index < data->irq_banks; ++index) + { + if (data->irq_bank_map[index] >= data->banks_nr) { return -RT_EINVAL; } @@ -753,7 +802,10 @@ static rt_err_t sunxi_pinctrl_probe(struct rt_platform_device *pdev) goto _fail; } - sunxi_pinctrl_primary = pinctrl; + if (!data->bank_base) + { + sunxi_pinctrl_primary = pinctrl; + } rt_ofw_data(dev->ofw_node) = &pinctrl->parent; return RT_EOK; @@ -771,7 +823,13 @@ static rt_err_t sunxi_pinctrl_probe(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_pinctrl_ofw_ids[] = { +#ifdef RT_PINCTRL_SUN252I_F101 { .compatible = "allwinner,sun252i-f101-pinctrl", .data = &sun252i_f101_pinctrl_data }, +#endif +#ifdef RT_PINCTRL_SUN50I_H616 + { .compatible = "allwinner,sun50i-h616-pinctrl", .data = &sun50i_h616_pinctrl_data }, + { .compatible = "allwinner,sun50i-h616-r-pinctrl", .data = &sun50i_h616_r_pinctrl_data }, +#endif { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/pwm/pwm-sunxi.c b/bsp/allwinner/dm/pwm/pwm-sunxi.c index 54cbef80e0e8..50e9914f39e3 100644 --- a/bsp/allwinner/dm/pwm/pwm-sunxi.c +++ b/bsp/allwinner/dm/pwm/pwm-sunxi.c @@ -24,7 +24,7 @@ #define PWM_PERIOD_CYCLES_SHIFT 16 #define PWM_CGR_BYPASS_SHIFT 16 -#define SUNXI_PWM_CHANNELS 4 +#define SUNXI_PWM_CHANNELS_MAX 6 #define SUNXI_PWM_CLK_24M 24000000ULL #define SUNXI_PWM_CLK_100M 100000000ULL #define SUNXI_PWM_NSEC_PER_SEC 1000000000ULL @@ -42,6 +42,7 @@ struct sunxi_pwm struct rt_clk *clk; struct rt_reset_control *reset; struct rt_spinlock lock; + rt_uint32_t channels; }; #define to_sunxi_pwm(raw) rt_container_of(raw, struct sunxi_pwm, parent) @@ -77,7 +78,7 @@ static rt_err_t sunxi_pwm_enable(struct sunxi_pwm *pwm, int channel, { rt_uint32_t value; - if (channel < 0 || channel >= SUNXI_PWM_CHANNELS) + if (channel < 0 || (rt_uint32_t)channel >= pwm->channels) { return -RT_EINVAL; } @@ -120,7 +121,7 @@ static rt_err_t sunxi_pwm_config(struct sunxi_pwm *pwm, rt_uint32_t prescale; int channel = config->channel; - if (channel < 0 || channel >= SUNXI_PWM_CHANNELS || !config->period || + if (channel < 0 || (rt_uint32_t)channel >= pwm->channels || !config->period || config->pulse > config->period) { return -RT_EINVAL; @@ -190,7 +191,7 @@ static rt_err_t sunxi_pwm_get(struct sunxi_pwm *pwm, rt_uint32_t divider_index; int channel = config->channel; - if (channel < 0 || channel >= SUNXI_PWM_CHANNELS) + if (channel < 0 || (rt_uint32_t)channel >= pwm->channels) { return -RT_EINVAL; } @@ -286,6 +287,7 @@ static void sunxi_pwm_release(struct sunxi_pwm *pwm) static rt_err_t sunxi_pwm_probe(struct rt_platform_device *pdev) { rt_err_t err; + rt_uint32_t channels = 4; const char *name; struct rt_device *dev = &pdev->parent; struct sunxi_pwm *pwm = rt_calloc(1, sizeof(*pwm)); @@ -302,6 +304,14 @@ static rt_err_t sunxi_pwm_probe(struct rt_platform_device *pdev) goto _fail; } + rt_ofw_prop_read_u32(dev->ofw_node, "pwm-number", &channels); + if (!channels || channels > SUNXI_PWM_CHANNELS_MAX) + { + err = -RT_EINVAL; + goto _fail; + } + pwm->channels = channels; + pwm->clk = rt_clk_get_by_index(dev, 0); if (rt_is_err(pwm->clk)) { @@ -348,7 +358,7 @@ static rt_err_t sunxi_pwm_remove(struct rt_platform_device *pdev) struct sunxi_pwm *pwm = dev->user_data; int channel; - for (channel = 0; channel < SUNXI_PWM_CHANNELS; ++channel) + for (channel = 0; (rt_uint32_t)channel < pwm->channels; ++channel) { sunxi_pwm_enable(pwm, channel, RT_FALSE); } @@ -363,6 +373,7 @@ static rt_err_t sunxi_pwm_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_pwm_ofw_ids[] = { { .compatible = "allwinner,sun252i-f101-pwm" }, + { .compatible = "allwinner,sun50i-h616-pwm" }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/rtc/rtc-sunxi.c b/bsp/allwinner/dm/rtc/rtc-sunxi.c index 942ea56262ba..66e264409eff 100644 --- a/bsp/allwinner/dm/rtc/rtc-sunxi.c +++ b/bsp/allwinner/dm/rtc/rtc-sunxi.c @@ -41,10 +41,113 @@ struct sunxi_rtc struct rt_rtc_wkalarm alarm; rt_bool_t registered; rt_bool_t irq_attached; + rt_bool_t day_zero; +#ifdef RT_CLK_SUN50I_H616 + struct rt_clk_node clocks; + struct rt_clk_cell clock_cells[3]; + struct rt_clk_cell *cells[3]; + struct rt_spinlock clock_lock; + rt_bool_t clocks_registered; +#endif +}; + +struct sunxi_rtc_soc_data +{ + rt_bool_t day_zero; +}; + +static const struct sunxi_rtc_soc_data sunxi_rtc_data = { 0 }; +static const struct sunxi_rtc_soc_data sunxi_rtc_h616_data = +{ + .day_zero = RT_TRUE, }; #define to_sunxi_rtc(raw) rt_container_of(raw, struct sunxi_rtc, parent) +#ifdef RT_CLK_SUN50I_H616 +static rt_ubase_t h616_rtc_clock_rate(struct rt_clk_cell *cell, + rt_ubase_t parent_rate) +{ + struct sunxi_rtc *rtc = cell->clk_np->priv; + rt_uint32_t cal = HWREG32(rtc->base + 0x0c); + rt_ubase_t iosc = (cal & RT_BIT(1)) ? cal >> 7 : 16000000; + + if (cell == &rtc->clock_cells[2]) + { + return iosc; + } + if (cell == &rtc->clock_cells[1] && + (HWREG32(rtc->base + 0x60) & RT_GENMASK(2, 1))) + { + /* Fanout is switched to IOSC32K by enable, not a guessed PLL rate. */ + return 0; + } + return (cal & RT_BIT(0)) ? 32768 : + iosc / 32 / ((HWREG32(rtc->base + 0x08) & 31) + 1); +} + +static rt_err_t h616_rtc_clock_enable(struct rt_clk_cell *cell) +{ + struct sunxi_rtc *rtc = cell->clk_np->priv; + rt_base_t level = rt_spin_lock_irqsave(&rtc->clock_lock); + + HWREG32(rtc->base + 0x160) |= RT_BIT(0); + if (cell == &rtc->clock_cells[1]) + { + rt_uint32_t value = HWREG32(rtc->base + 0x60); + HWREG32(rtc->base + 0x60) = (value & ~RT_GENMASK(2, 1)) | RT_BIT(0); + } + rt_spin_unlock_irqrestore(&rtc->clock_lock, level); + return RT_EOK; +} + +static void h616_rtc_clock_disable(struct rt_clk_cell *cell) +{ + struct sunxi_rtc *rtc = cell->clk_np->priv; + rt_base_t level = rt_spin_lock_irqsave(&rtc->clock_lock); + + /* IOSC also supplies the RTC and must stay running. */ + if (cell == &rtc->clock_cells[1]) + { + HWREG32(rtc->base + 0x60) &= ~RT_BIT(0); + } + rt_spin_unlock_irqrestore(&rtc->clock_lock, level); +} + +static const struct rt_clk_ops h616_rtc_clock_ops = +{ + .recalc_rate = h616_rtc_clock_rate, + .enable = h616_rtc_clock_enable, + .disable = h616_rtc_clock_disable, +}; + +static rt_err_t h616_rtc_register_clocks(struct sunxi_rtc *rtc, + struct rt_device *dev) +{ + static const char * const names[] = + { + "h616-osc32k", "h616-osc32k-out", "h616-iosc", + }; + unsigned int index; + + rt_spin_lock_init(&rtc->clock_lock); + rtc->clocks.dev = dev; + rtc->clocks.priv = rtc; + rtc->clocks.cells = rtc->cells; + rtc->clocks.cells_nr = RT_ARRAY_SIZE(rtc->cells); + /* No dependency on main/R CCU: both CCUs depend on this provider. */ + rtc->clocks.parents_clk = rt_err_ptr(-RT_ENOSYS); + for (index = 0; index < RT_ARRAY_SIZE(rtc->cells); ++index) + { + rtc->clock_cells[index].name = names[index]; + rtc->clock_cells[index].ops = &h616_rtc_clock_ops; + rtc->clock_cells[index].flags = RT_CLK_F_GET_RATE_NOCACHE; + rtc->cells[index] = &rtc->clock_cells[index]; + } + return rt_clk_register(&rtc->clocks); +} +#endif + static rt_uint32_t sunxi_rtc_read(struct sunxi_rtc *rtc, rt_size_t offset) { return HWREG32(rtc->base + offset); @@ -169,6 +272,15 @@ static rt_err_t sunxi_rtc_read_tm(struct sunxi_rtc *rtc, struct tm *tm, } } + if (retry == 3) + { + return -RT_EIO; + } + /* Keep the internal helpers one-based, matching the F101 interface. */ + if (rtc->day_zero) + { + ymd = (ymd & 0xffff) + 1; + } err = sunxi_rtc_day_to_tm(ymd, tm); if (err) { @@ -229,7 +341,7 @@ static rt_err_t sunxi_rtc_write_tm(struct sunxi_rtc *rtc, const struct tm *tm) { return err; } - sunxi_rtc_write(rtc, SUNXI_RTC_YMD, day); + sunxi_rtc_write(rtc, SUNXI_RTC_YMD, day - rtc->day_zero); return sunxi_rtc_wait_ready(rtc, SUNXI_RTC_HMS_ACC | SUNXI_RTC_YMD_ACC); } @@ -321,7 +433,7 @@ static rt_err_t sunxi_rtc_set_alarm(struct sunxi_rtc *rtc, sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_EN, 0); sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_EN, 0); sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_STA, SUNXI_RTC_ALRM_IRQ_COUNTER); - sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_DAY, target_day); + sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_DAY, target_day - rtc->day_zero); sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_HMS, hms); sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_IRQ_EN, SUNXI_RTC_ALRM_IRQ_COUNTER); sunxi_rtc_write(rtc, SUNXI_RTC_ALRM_EN, SUNXI_RTC_ALRM_EN_COUNTER); @@ -411,13 +523,20 @@ static rt_err_t sunxi_rtc_probe(struct rt_platform_device *pdev) { rt_err_t err; struct rt_device *dev = &pdev->parent; - struct sunxi_rtc *rtc = rt_calloc(1, sizeof(*rtc)); + const struct sunxi_rtc_soc_data *soc_data = pdev->id->data; + struct sunxi_rtc *rtc; + if (!soc_data) + { + return -RT_EINVAL; + } + rtc = rt_calloc(1, sizeof(*rtc)); if (!rtc) { return -RT_ENOMEM; } + rtc->day_zero = soc_data->day_zero; rtc->base = rt_dm_dev_iomap(dev, 0); rtc->irq = rt_dm_dev_get_irq(dev, 0); if (!rtc->base || rtc->irq < 0) @@ -448,8 +567,28 @@ static rt_err_t sunxi_rtc_probe(struct rt_platform_device *pdev) rtc->irq_attached = RT_TRUE; rt_pic_irq_unmask(rtc->irq); +#ifdef RT_CLK_SUN50I_H616 + if (rtc->day_zero) + { + if ((err = h616_rtc_register_clocks(rtc, dev))) + { + goto _fail; + } + rtc->clocks_registered = RT_TRUE; + } +#endif + dev->user_data = rtc; - rt_dm_dev_bind_fwdata(dev, RT_NULL, rtc); +#ifdef RT_CLK_SUN50I_H616 + /* + * The H616 RTC is also the LOSC/IOSC clock provider. Keep its clock + * node as the FDT data so the main and R CCUs can resolve those clocks. + */ + if (!rtc->clocks_registered) +#endif + { + rt_dm_dev_bind_fwdata(dev, RT_NULL, rtc); + } return RT_EOK; _fail: @@ -462,6 +601,19 @@ static rt_err_t sunxi_rtc_remove(struct rt_platform_device *pdev) struct rt_device *dev = &pdev->parent; struct sunxi_rtc *rtc = dev->user_data; +#ifdef RT_CLK_SUN50I_H616 + /* This oscillator is the root of the live bus-clock tree. */ + if (rtc->clocks_registered) + { + return -RT_EBUSY; + } +#endif + if (rt_device_unregister(&rtc->parent)) + { + return -RT_EBUSY; + } + rtc->registered = RT_FALSE; + rt_dm_dev_unbind_fwdata(dev, RT_NULL); sunxi_rtc_release(rtc); return RT_EOK; @@ -469,7 +621,8 @@ static rt_err_t sunxi_rtc_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_rtc_ofw_ids[] = { - { .compatible = "allwinner,sun252i-f101-rtc" }, + { .compatible = "allwinner,sun252i-f101-rtc", .data = &sunxi_rtc_data }, + { .compatible = "allwinner,sun50i-h616-rtc", .data = &sunxi_rtc_h616_data }, { /* sentinel */ } }; @@ -480,4 +633,13 @@ static struct rt_platform_driver sunxi_rtc_driver = .probe = sunxi_rtc_probe, .remove = sunxi_rtc_remove, }; +#ifdef RT_CLK_SUN50I_H616 +static int sunxi_rtc_driver_register(void) +{ + return rt_platform_driver_register(&sunxi_rtc_driver); +} +/* H616 CCUs need this node's oscillator outputs during platform discovery. */ +INIT_SUBSYS_EXPORT(sunxi_rtc_driver_register); +#else RT_PLATFORM_DRIVER_EXPORT(sunxi_rtc_driver); +#endif diff --git a/bsp/allwinner/dm/sdio/sdio-sunxi.c b/bsp/allwinner/dm/sdio/sdio-sunxi.c index 0b0a6fff0e33..84514cfef859 100644 --- a/bsp/allwinner/dm/sdio/sdio-sunxi.c +++ b/bsp/allwinner/dm/sdio/sdio-sunxi.c @@ -6,15 +6,14 @@ #include #include - -#include -#include -#include +#include #define DBG_TAG "sdio.sunxi" #define DBG_LVL DBG_INFO #include +#include + #include "dev_sdio_dm.h" #define SUNXI_MMC_GCTRL 0x00 @@ -34,9 +33,11 @@ #define SUNXI_MMC_STAS 0x3c #define SUNXI_MMC_FTRGL 0x40 #define SUNXI_MMC_FUNS 0x44 +#define SUNXI_MMC_DBGC 0x50 #define SUNXI_MMC_NTSR 0x5c #define SUNXI_MMC_HWRST 0x78 #define SUNXI_MMC_DRV_DL 0x140 +#define SUNXI_MMC_SAMP_DL 0x144 #define SUNXI_MMC_FIFO 0x200 #define SUNXI_MMC_DMAC 0x80 #define SUNXI_MMC_DLBA 0x84 @@ -110,6 +111,8 @@ SUNXI_MMC_IDMAC_DESC_INVALID | SUNXI_MMC_IDMAC_CARD_ERROR | \ SUNXI_MMC_IDMAC_ABNORMAL) +#define SUNXI_MMC_CEATA_ON (0xceaaU << 16) + #define SUNXI_MMC_IDMA_DESC_DIC RT_BIT(1) #define SUNXI_MMC_IDMA_DESC_LAST RT_BIT(2) #define SUNXI_MMC_IDMA_DESC_FIRST RT_BIT(3) @@ -147,6 +150,17 @@ struct sunxi_mmc_idma_desc rt_uint32_t next_desc; }; +struct sunxi_mmc_soc_data +{ + rt_bool_t h616; +}; + +static const struct sunxi_mmc_soc_data sunxi_mmc_data = { 0 }; +static const struct sunxi_mmc_soc_data sunxi_mmc_h616_data = +{ + .h616 = RT_TRUE, +}; + struct sunxi_mmc { struct rt_mmcsd_host *host; @@ -174,6 +188,11 @@ struct sunxi_mmc rt_bool_t card_reported; rt_bool_t sd_hs_rejected; rt_bool_t no_sdio; + rt_bool_t h616; +#ifdef RT_USING_REGULATOR + struct rt_regulator *supplies[2]; + rt_bool_t supply_enabled[2]; +#endif }; static rt_uint32_t sunxi_mmc_read(struct sunxi_mmc *mmc, rt_uint32_t offset) @@ -255,6 +274,13 @@ static void sunxi_mmc_set_timing(struct sunxi_mmc *mmc, } sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, gctrl); + if (mmc->h616) + { + /* H616 uses software sampling delay, with a zero tap for SDR. */ + sunxi_mmc_write(mmc, SUNXI_MMC_SAMP_DL, RT_BIT(7)); + return; + } + drv_dl = sunxi_mmc_read(mmc, SUNXI_MMC_DRV_DL); /* * SMHC0 uses timing mode 1. The F101 SDK enables 2x clocking in NTSR @@ -272,8 +298,11 @@ static void sunxi_mmc_set_clock_phase(struct sunxi_mmc *mmc) rt_uint32_t ntsr; ntsr = sunxi_mmc_read(mmc, SUNXI_MMC_NTSR); - ntsr &= ~(SUNXI_MMC_NTSR_CMD_SAMPLE_MASK | - SUNXI_MMC_NTSR_DATA_SAMPLE_MASK); + if (!mmc->h616) + { + ntsr &= ~(SUNXI_MMC_NTSR_CMD_SAMPLE_MASK | + SUNXI_MMC_NTSR_DATA_SAMPLE_MASK); + } ntsr |= SUNXI_MMC_NTSR_2X_TIMING; sunxi_mmc_write(mmc, SUNXI_MMC_NTSR, ntsr); } @@ -309,8 +338,9 @@ static void sunxi_mmc_set_iocfg(struct rt_mmcsd_host *host, if (effective_cfg.clock) { - /* The controller consumes a module clock twice the card clock. */ - if (rt_clk_set_rate(mmc->mmc_clk, effective_cfg.clock * 2)) + /* The H616 clock provider already includes the fixed /2 stage. */ + if (rt_clk_set_rate(mmc->mmc_clk, + effective_cfg.clock * (mmc->h616 ? 1 : 2))) { LOG_E("cannot set %u Hz card clock", effective_cfg.clock); rate_err = -RT_ERROR; @@ -320,6 +350,11 @@ static void sunxi_mmc_set_iocfg(struct rt_mmcsd_host *host, if (!rate_err) { sunxi_mmc_set_timing(mmc, &effective_cfg); + if (mmc->h616) + { + sunxi_mmc_write(mmc, SUNXI_MMC_CLKCR, + sunxi_mmc_read(mmc, SUNXI_MMC_CLKCR) & ~0xffU); + } } err = rt_clk_prepare_enable(mmc->mmc_clk); @@ -443,7 +478,7 @@ static void sunxi_mmc_build_idma_desc(struct sunxi_mmc *mmc, offset += length; } - rt_hw_cpu_dcache_clean_and_invalidate(mmc->idma_desc, + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmc->idma_desc, count * sizeof(*mmc->idma_desc)); rt_hw_dsb(); } @@ -469,12 +504,12 @@ static rt_err_t sunxi_mmc_start_idma(struct sunxi_mmc *mmc, } /* - * Clean dirty lines before a read so they cannot overwrite DMA data, - * and invalidate again after the transfer before the CPU reads it. + * Flush dirty lines before a read so they cannot overwrite DMA data; + * invalidate again after the transfer before the CPU reads it. */ if (data->flags & DATA_DIR_READ) { - rt_hw_cpu_dcache_clean_and_invalidate(data->buf, total); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, data->buf, total); } else { @@ -520,6 +555,12 @@ static rt_err_t sunxi_mmc_wait_memory_access(struct sunxi_mmc *mmc) rt_tick_t timeout = rt_tick_get() + rt_tick_from_millisecond(SUNXI_MMC_TIMEOUT_MS); + /* GCTRL[30:13] are reserved on H616; IDMA RX and DATA_OVER are sufficient. */ + if (mmc->h616) + { + return RT_EOK; + } + while (!(sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL) & SUNXI_MMC_MEMORY_ACCESS_DONE)) { @@ -588,15 +629,6 @@ static void sunxi_mmc_isr(int vector, void *param) } } -static rt_err_t sunxi_mmc_reset_fifo(struct sunxi_mmc *mmc) -{ - sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, - sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL) | SUNXI_MMC_FIFO_RESET); - - return sunxi_mmc_wait_clear(mmc, SUNXI_MMC_GCTRL, - SUNXI_MMC_FIFO_RESET, SUNXI_MMC_TIMEOUT_MS); -} - static rt_err_t sunxi_mmc_wait_busy(struct sunxi_mmc *mmc, rt_uint32_t timeout_ms) { rt_tick_t timeout = rt_tick_get() + rt_tick_from_millisecond(timeout_ms); @@ -798,7 +830,6 @@ static void sunxi_mmc_request(struct rt_mmcsd_host *host, } } -_complete: sunxi_mmc_write(mmc, SUNXI_MMC_RINTR, status); if (!err) { @@ -813,6 +844,7 @@ static void sunxi_mmc_request(struct rt_mmcsd_host *host, { cmd->resp[0] = sunxi_mmc_read(mmc, SUNXI_MMC_RESP0); } + if (resp_type(cmd) == RESP_R1B) { err = sunxi_mmc_wait_busy(mmc, cmd->busy_timeout ? @@ -969,26 +1001,42 @@ static const struct rt_mmcsd_host_ops sunxi_mmc_ops = static rt_err_t sunxi_mmc_hw_init(struct sunxi_mmc *mmc) { + rt_uint32_t gctrl; + sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL) | SUNXI_MMC_HW_RESET); if (sunxi_mmc_wait_clear(mmc, SUNXI_MMC_GCTRL, SUNXI_MMC_HW_RESET, 250)) { return -RT_ETIMEOUT; } - sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, - (sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL) | - SUNXI_MMC_INT_ENABLE | SUNXI_MMC_ACCESS_DONE_DIRECT) & - ~SUNXI_MMC_ACCESS_BY_AHB); + gctrl = sunxi_mmc_read(mmc, SUNXI_MMC_GCTRL); + gctrl |= SUNXI_MMC_INT_ENABLE; + if (mmc->h616) + { + /* Match the H616/Linux host setup; direct-access completion is off. */ + gctrl &= ~SUNXI_MMC_ACCESS_DONE_DIRECT; + } + else + { + gctrl |= SUNXI_MMC_ACCESS_DONE_DIRECT; + gctrl &= ~SUNXI_MMC_ACCESS_BY_AHB; + } + sunxi_mmc_write(mmc, SUNXI_MMC_GCTRL, gctrl); sunxi_mmc_write(mmc, SUNXI_MMC_FTRGL, SUNXI_MMC_PIO_FTRGL); sunxi_mmc_write(mmc, SUNXI_MMC_TMOUT, 0xffffffff); sunxi_mmc_write(mmc, SUNXI_MMC_IMASK, 0); sunxi_mmc_write(mmc, SUNXI_MMC_RINTR, 0xffffffff); + sunxi_mmc_write(mmc, SUNXI_MMC_DBGC, 0xdeb); + sunxi_mmc_write(mmc, SUNXI_MMC_FUNS, SUNXI_MMC_CEATA_ON); return RT_EOK; } static void sunxi_mmc_free(struct sunxi_mmc *mmc) { +#ifdef RT_USING_REGULATOR + unsigned int index; +#endif if (mmc->irq_installed) { rt_hw_interrupt_mask(mmc->irq); @@ -1032,6 +1080,19 @@ static void sunxi_mmc_free(struct sunxi_mmc *mmc) { rt_iounmap(mmc->base); } +#ifdef RT_USING_REGULATOR + for (index = 0; index < RT_ARRAY_SIZE(mmc->supplies); ++index) + { + if (!rt_is_err_or_null(mmc->supplies[index])) + { + if (mmc->supply_enabled[index]) + { + rt_regulator_disable(mmc->supplies[index]); + } + rt_regulator_put(mmc->supplies[index]); + } + } +#endif rt_free(mmc); } @@ -1040,15 +1101,56 @@ static rt_err_t sunxi_mmc_probe(struct rt_platform_device *pdev) rt_err_t err; rt_uint8_t unused_mode; struct rt_device *dev = &pdev->parent; - struct sunxi_mmc *mmc = rt_calloc(1, sizeof(*mmc)); + const struct sunxi_mmc_soc_data *soc_data = pdev->id->data; + struct sunxi_mmc *mmc; + + if (!soc_data) + { + return -RT_EINVAL; + } + if (soc_data->h616 && rt_dm_dev_prop_read_bool(dev, "mmc-pwrseq")) + { + LOG_D("skip MMC host with unsupported mmc-pwrseq"); + return RT_EOK; + } + mmc = rt_calloc(1, sizeof(*mmc)); if (!mmc) { return -RT_ENOMEM; } mmc->dev = dev; + mmc->h616 = soc_data->h616; mmc->cd_pin = -1; mmc->irq = -1; +#ifdef RT_USING_REGULATOR + if (mmc->h616) + { + static const char * const names[] = { "vmmc", "vqmmc" }; + static const char * const props[] = { "vmmc-supply", "vqmmc-supply" }; + unsigned int index; + + for (index = 0; index < RT_ARRAY_SIZE(names); ++index) + { + if (!rt_dm_dev_prop_read_bool(dev, props[index])) + { + continue; + } + mmc->supplies[index] = rt_regulator_get(dev, names[index]); + if (rt_is_err_or_null(mmc->supplies[index])) + { + err = rt_is_err(mmc->supplies[index]) ? + rt_ptr_err(mmc->supplies[index]) : -RT_EIO; + goto _fail; + } + if ((err = rt_regulator_enable(mmc->supplies[index]))) + { + goto _fail; + } + mmc->supply_enabled[index] = RT_TRUE; + } + } +#endif mmc->base = rt_dm_dev_iomap(dev, 0); if (!mmc->base) { @@ -1120,10 +1222,29 @@ static rt_err_t sunxi_mmc_probe(struct rt_platform_device *pdev) LOG_W("MMC interrupt unavailable; using PIO"); } mmc->no_sdio = rt_dm_dev_prop_read_bool(dev, "no-sdio"); + /* + * The H616's removable MMC0 slot is SD-only. Its firmware DT describes + * card detection with cd-gpios but, unlike the F101 DT, omits no-sdio. + * Do not issue CMD5 to that slot: SMHC0 completes the ignored command + * with a zero R4 instead of a response timeout, which corrupts probing + * of the following ACMD41 sequence. + */ + if (mmc->h616 && !mmc->no_sdio && + rt_dm_dev_prop_read_bool(dev, "cd-gpios")) + { + mmc->no_sdio = RT_TRUE; + } if ((err = sdio_ofw_parse(dev->ofw_node, mmc->host))) { goto _fail; } + if (mmc->h616) + { + /* Voltage switching, tuning and the SDIO IRQ path are not implemented. */ + mmc->host->flags &= MMCSD_MUTBLKWRITE | MMCSD_BUSWIDTH_4 | + MMCSD_BUSWIDTH_8 | MMCSD_SUP_NONREMOVABLE | MMCSD_SUP_HIGHSPEED; + mmc->host->freq_max = rt_min_t(rt_uint32_t, mmc->host->freq_max, 50000000); + } sdio_host_set_name(mmc->host, RT_NULL); mmc->cd_pin = rt_pin_get_named_pin(dev, "cd", 0, &unused_mode, @@ -1178,8 +1299,10 @@ static rt_err_t sunxi_mmc_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_mmc_ofw_ids[] = { - { .compatible = "allwinner,sun252i-f101-mmc" }, - { .compatible = "allwinner,sun252i-f101-emmc" }, + { .compatible = "allwinner,sun252i-f101-mmc", .data = &sunxi_mmc_data }, + { .compatible = "allwinner,sun252i-f101-emmc", .data = &sunxi_mmc_data }, + { .compatible = "allwinner,sun50i-h616-mmc", .data = &sunxi_mmc_h616_data }, + { .compatible = "allwinner,sun50i-h616-emmc", .data = &sunxi_mmc_h616_data }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/soc/Kconfig b/bsp/allwinner/dm/soc/Kconfig index 4ee5d9e27eb5..9ac6fa373acd 100644 --- a/bsp/allwinner/dm/soc/Kconfig +++ b/bsp/allwinner/dm/soc/Kconfig @@ -1,14 +1,15 @@ config RT_SOC_SUNXI_G2D - bool "Allwinner G2D-200 graphics accelerator" + bool "Allwinner RCQ G2D graphics accelerator" depends on RT_USING_OFW depends on RT_USING_DMA depends on RT_USING_CLK depends on RT_USING_RESET default y help - Enable the G2D-200 command-queue transport used by the Allwinner - sun252i F101. The driver owns the hardware resources and provides - DMA buffer management plus synchronous RCQ submission. + Enable the RCQ command-queue transport used by the Allwinner sun252i + F101 and compatible H616 G2D blocks. The driver owns the hardware + resources and provides DMA buffer management plus synchronous RCQ + submission. config RT_SOC_SUNXI_JPEG_DEC bool "Allwinner VE JPEG/MJPEG decoder" @@ -18,7 +19,8 @@ config RT_SOC_SUNXI_JPEG_DEC depends on RT_USING_RESET help Enable the baseline JPEG and MJPEG decoder in the Allwinner Video - Engine. The jpegd device accepts DMA buffers and produces NV12 frames. + Engine. The jpegd device accepts DMA buffers and produces MB32 4:2:0 + reconstruction surfaces. config RT_SOC_SUNXI_JPEG_ENC bool "Allwinner VE JPEG/MJPEG encoder" diff --git a/bsp/allwinner/dm/soc/g2d/g2d-sunxi.c b/bsp/allwinner/dm/soc/g2d/g2d-sunxi.c index 3f2268e47bd1..61f8d264e61c 100644 --- a/bsp/allwinner/dm/soc/g2d/g2d-sunxi.c +++ b/bsp/allwinner/dm/soc/g2d/g2d-sunxi.c @@ -7,12 +7,12 @@ #include #include #include -#include #define DBG_TAG "g2d.sunxi" #define DBG_LVL DBG_INFO #include +#include #include #include "g2d_sunxi_hw.h" @@ -836,6 +836,8 @@ static const struct rt_ofw_node_id sunxi_g2d_ofw_ids[] = { { .compatible = "allwinner,sun252i-f101-g2d200" }, { .compatible = "allwinner,sunxi-g2d200" }, + { .compatible = "allwinner,sun50i-h616-g2d" }, + { .compatible = "allwinner,sunxi-g2d" }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h index 567b7a2da9cc..89521885cc6d 100644 --- a/bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h +++ b/bsp/allwinner/dm/soc/g2d/g2d_sunxi_regs.h @@ -15,6 +15,7 @@ #ifndef G2D_SUNXI_REGS_H_ #define G2D_SUNXI_REGS_H_ + #include /* ---- control registers (CPU) ------------------------------------------ */ diff --git a/bsp/allwinner/dm/soc/jpegd-sunxi.c b/bsp/allwinner/dm/soc/jpegd-sunxi.c index f0f9c0b27335..77fb84f54504 100644 --- a/bsp/allwinner/dm/soc/jpegd-sunxi.c +++ b/bsp/allwinner/dm/soc/jpegd-sunxi.c @@ -8,8 +8,6 @@ #include #include -#include - #define DBG_TAG "jpegd.sunxi" #define DBG_LVL DBG_INFO #include @@ -58,6 +56,8 @@ #define SUNXI_VE_MPEG_CTRL_F101_420 (RT_BIT(8) | RT_BIT(7) | \ RT_BIT(6) | RT_BIT(5) | \ RT_BIT(4) | RT_BIT(3)) +#define SUNXI_VE_MPEG_CTRL_H6 (RT_BIT(27) | RT_BIT(5) | \ + RT_BIT(4) | RT_BIT(3)) #define SUNXI_VE_MPEG_STATUS_MASK (RT_BIT(3) | RT_BIT(2) | \ RT_BIT(1) | RT_BIT(0)) #define SUNXI_VE_MPEG_VLD_FIRST RT_BIT(30) @@ -213,6 +213,27 @@ enum sunxi_jpegd_engine SUNXI_JPEGD_ENGINE_PNG_DECODE, }; +enum sunxi_jpegd_layout +{ + SUNXI_JPEGD_LAYOUT_F101, + SUNXI_JPEGD_LAYOUT_H6, +}; + +struct sunxi_jpegd_soc_data +{ + enum sunxi_jpegd_layout layout; +}; + +static const struct sunxi_jpegd_soc_data sunxi_jpegd_f101_data = +{ + .layout = SUNXI_JPEGD_LAYOUT_F101, +}; + +static const struct sunxi_jpegd_soc_data sunxi_jpegd_h616_data = +{ + .layout = SUNXI_JPEGD_LAYOUT_H6, +}; + struct sunxi_jpege_header { rt_uint8_t data[SUNXI_JPEGE_HEADER_SIZE]; @@ -243,6 +264,7 @@ struct sunxi_jpegd rt_bool_t png_registered; rt_bool_t irq_installed; rt_bool_t running; + enum sunxi_jpegd_layout layout; enum sunxi_jpegd_engine engine; }; @@ -258,12 +280,13 @@ static rt_err_t sunxi_jpegd_claim_sram(struct sunxi_jpegd *jpegd) "allwinner,sram", 0); if (!section_np) { - return -RT_EINVAL; + return jpegd->layout == SUNXI_JPEGD_LAYOUT_H6 ? RT_EOK : -RT_EINVAL; } if (!rt_ofw_node_is_compatible(section_np, "allwinner,sun252i-f101-sram-ve")) { + err = jpegd->layout == SUNXI_JPEGD_LAYOUT_H6 ? RT_EOK : -RT_EINVAL; goto _out_section; } @@ -857,6 +880,24 @@ static rt_err_t sunxi_jpegd_sampling(const struct sunxi_jpegd_header *header, return RT_EOK; } +static rt_uint32_t sunxi_jpegd_mpeg_mode(const struct sunxi_jpegd *jpegd) +{ + return jpegd->layout == SUNXI_JPEGD_LAYOUT_H6 ? 0 : SUNXI_VE_MODE_DEC_MPEG; +} + +static rt_uint32_t sunxi_jpegd_vld_address(const struct sunxi_jpegd *jpegd, + rt_ubase_t address) +{ + rt_uint32_t value = address; + + if (jpegd->layout == SUNXI_JPEGD_LAYOUT_H6) + { + value = (value & 0x0ffffff0) | (value >> 28 & 0x0f); + } + return value | SUNXI_VE_MPEG_VLD_FIRST | SUNXI_VE_MPEG_VLD_LAST | + SUNXI_VE_MPEG_VLD_VALID; +} + static rt_err_t sunxi_jpegd_configure_output(struct sunxi_jpegd *jpegd, const struct sunxi_jpegd_header *header, rt_uint32_t sampling, struct sunxi_jpegd_buffer_entry *output, rt_size_t *out_stride, @@ -1625,7 +1666,7 @@ static rt_err_t sunxi_jpegd_decode(struct sunxi_jpegd *jpegd, return err; } - sunxi_jpegd_writel(jpegd, SUNXI_VE_MODE, SUNXI_VE_MODE_DEC_MPEG); + sunxi_jpegd_writel(jpegd, SUNXI_VE_MODE, sunxi_jpegd_mpeg_mode(jpegd)); if ((err = sunxi_jpegd_configure_output(jpegd, &header, sampling, output, &stride, &luma_size, &output_size)) != RT_EOK) { @@ -1650,12 +1691,7 @@ static rt_err_t sunxi_jpegd_decode(struct sunxi_jpegd *jpegd, header.entropy_offset * 8); sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_VLD_LENGTH, (input->buffer.size - header.entropy_offset) * 8); - /* - * Unlike older VE generations, F101 takes the unmodified physical - * address here. The input buffer is a 1 KiB-aligned VLD ring. - */ - address = input->buffer.phys_addr | SUNXI_VE_MPEG_VLD_FIRST | - SUNXI_VE_MPEG_VLD_LAST | SUNXI_VE_MPEG_VLD_VALID; + address = sunxi_jpegd_vld_address(jpegd, input->buffer.phys_addr); sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_VLD_ADDR, address); sunxi_jpegd_writel(jpegd, SUNXI_VE_MPEG_VLD_END, input->buffer.phys_addr + input->buffer.size - 1); @@ -1668,9 +1704,11 @@ static rt_err_t sunxi_jpegd_decode(struct sunxi_jpegd *jpegd, rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, input->buffer.virt_addr, request->input_size); - rt_hw_cpu_dcache_clean_and_invalidate(output->buffer.virt_addr, output_size); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, output->buffer.virt_addr, output_size); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, output->buffer.virt_addr, output_size); - ctrl = SUNXI_VE_MPEG_CTRL_F101_420; + ctrl = jpegd->layout == SUNXI_JPEGD_LAYOUT_H6 ? + SUNXI_VE_MPEG_CTRL_H6 : SUNXI_VE_MPEG_CTRL_F101_420; jpegd->engine = SUNXI_JPEGD_ENGINE_DECODE; jpegd->running = RT_TRUE; jpegd->error = RT_EOK; @@ -2018,7 +2056,7 @@ static const struct rt_device_ops sunxi_jpegd_ops = .control = sunxi_jpegd_control, }; -static const struct rt_device_ops sunxi_jpege_ops = +static const struct rt_device_ops sunxi_jpege_ops __attribute__((unused)) = { .control = sunxi_jpege_control, }; @@ -2148,14 +2186,21 @@ static void sunxi_jpegd_release(struct sunxi_jpegd *jpegd) static rt_err_t sunxi_jpegd_probe(struct rt_platform_device *pdev) { struct rt_device *dev = &pdev->parent; + const struct sunxi_jpegd_soc_data *soc_data = pdev->id->data; struct sunxi_jpegd *jpegd; const char *decoder_name; +#ifdef RT_SOC_SUNXI_JPEG_ENC const char *encoder_name; +#endif #ifdef RT_SOC_SUNXI_PNG_DEC const char *png_name; #endif rt_err_t err; + if (!soc_data) + { + return -RT_EINVAL; + } jpegd = rt_calloc(1, sizeof(*jpegd)); if (!jpegd) { @@ -2164,6 +2209,7 @@ static rt_err_t sunxi_jpegd_probe(struct rt_platform_device *pdev) jpegd->dev = dev; jpegd->irq = -1; + jpegd->layout = soc_data->layout; rt_list_init(&jpegd->buffers); jpegd->base = rt_dm_dev_iomap(dev, 0); if (!jpegd->base) @@ -2301,7 +2347,8 @@ static rt_err_t sunxi_jpegd_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_jpegd_ofw_ids[] = { - { .compatible = "allwinner,sun252i-f101-video-engine" }, + { .compatible = "allwinner,sun252i-f101-video-engine", .data = &sunxi_jpegd_f101_data }, + { .compatible = "allwinner,sun50i-h616-video-engine", .data = &sunxi_jpegd_h616_data }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/spi/spi-sunxi.c b/bsp/allwinner/dm/spi/spi-sunxi.c index c1f2e936b108..1945a997ded8 100644 --- a/bsp/allwinner/dm/spi/spi-sunxi.c +++ b/bsp/allwinner/dm/spi/spi-sunxi.c @@ -524,6 +524,7 @@ static rt_err_t sunxi_spi_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_spi_ofw_ids[] = { { .compatible = "allwinner,sun252i-f101-spi" }, + { .compatible = "allwinner,sun50i-h616-spi" }, { /* sentinel */ } }; diff --git a/bsp/allwinner/dm/thermal/Kconfig b/bsp/allwinner/dm/thermal/Kconfig index 9dd580ea73af..cc88395cc5ec 100644 --- a/bsp/allwinner/dm/thermal/Kconfig +++ b/bsp/allwinner/dm/thermal/Kconfig @@ -5,3 +5,12 @@ config RT_THERMAL_SUNXI depends on RT_USING_RESET depends on RT_USING_THERMAL default n + +config RT_THERMAL_SUN50I_H616 + bool "Allwinner H616 thermal sensors" + depends on RT_USING_CLK && RT_USING_NVMEM && RT_USING_RESET + depends on RT_USING_THERMAL + select RT_NVMEM_SUNXI_SID + select RT_USING_MFD + select RT_MFD_SYSCON + default n diff --git a/bsp/allwinner/dm/thermal/SConscript b/bsp/allwinner/dm/thermal/SConscript index 7a76faf2fa41..a3fc986dbdd5 100644 --- a/bsp/allwinner/dm/thermal/SConscript +++ b/bsp/allwinner/dm/thermal/SConscript @@ -7,6 +7,9 @@ CPPPATH = [cwd + '/../include'] if GetDepend(['RT_THERMAL_SUNXI']): src += ['thermal-sunxi.c'] +if GetDepend(['RT_THERMAL_SUN50I_H616']): + src += ['thermal-sun50i-h616.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/bsp/allwinner/dm/thermal/thermal-sun50i-h616.c b/bsp/allwinner/dm/thermal/thermal-sun50i-h616.c new file mode 100644 index 000000000000..898835953e00 --- /dev/null +++ b/bsp/allwinner/dm/thermal/thermal-sun50i-h616.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DBG_TAG "thermal.h616" +#define DBG_LVL DBG_INFO +#include + +#define H616_THS_CTL 0x00 +#define H616_THS_ENABLE 0x04 +#define H616_THS_PERIOD 0x08 +#define H616_THS_IRQ_ENABLE 0x10 +#define H616_THS_FILTER 0x30 +#define H616_THS_CALIB 0xa0 +#define H616_THS_DATA 0xc0 +#define H616_THS_SENSORS 4 +#define H616_THS_MASK 0xfff + +struct h616_thermal +{ + void *base; + struct rt_clk *clk; + struct rt_reset_control *reset; + rt_bool_t clock_enabled; + rt_bool_t reset_deasserted; + struct rt_thermal_zone_device zones[H616_THS_SENSORS]; +}; + +static int h616_ths_temperature(unsigned int raw) +{ + return 263655 - (int)raw * 81; +} + +static rt_err_t h616_ths_get_temp(struct rt_thermal_zone_device *zone, int *temp) +{ + struct h616_thermal *ths = zone->priv; + rt_uint32_t raw = HWREG32(ths->base + H616_THS_DATA + zone->zone_id * 4); + + raw &= H616_THS_MASK; + if (!raw) + { + return -RT_EBUSY; + } + + /* Factory deviation is applied only to readings, not calibration. */ + *temp = h616_ths_temperature(raw) + 8000; + return RT_EOK; +} + +static const struct rt_thermal_zone_ops h616_ths_ops = { + .get_temp = h616_ths_get_temp, +}; + +static rt_err_t h616_ths_calibrate(struct rt_device *dev, struct h616_thermal *ths) +{ + rt_uint8_t data[8]; + rt_uint16_t words[4]; + rt_uint32_t regs[2] = { 0, 0 }; + struct rt_nvmem_cell *cell; + rt_ssize_t size; + int i, factory_temp; + + cell = rt_nvmem_get_cell_by_name(dev, "calibration"); + if (rt_is_err(cell)) + { + return rt_ptr_err(cell); + } + if (!cell) + { + return -RT_ENOENT; + } + size = rt_nvmem_cell_read(cell, data, sizeof(data)); + rt_nvmem_put_cell(cell); + if (size != sizeof(data)) + { + return size < 0 ? size : -RT_EIO; + } + + for (i = 0; i < 4; ++i) + { + words[i] = data[2 * i] | ((rt_uint16_t)data[2 * i + 1] << 8); + } + if (!(words[0] & H616_THS_MASK) || (words[0] & H616_THS_MASK) == H616_THS_MASK) + { + return -RT_EINVAL; + } + factory_temp = (words[0] & H616_THS_MASK) * 100; + + for (i = 0; i < H616_THS_SENSORS; ++i) + { + int raw, calibration; + + /* Sensor 3 occupies the high nibbles of the other three words. */ + raw = i == 3 ? (words[1] >> 12) | ((words[2] >> 12) << 4) | + ((words[3] >> 12) << 8) + : words[i + 1] & H616_THS_MASK; + calibration = 0x800 - (h616_ths_temperature(raw) - factory_temp) / 81; + if (calibration < 0 || calibration > H616_THS_MASK) + { + return -RT_EINVAL; + } + regs[i / 2] |= (rt_uint32_t)calibration << ((i % 2) * 16); + } + + for (i = 0; i < 2; ++i) + { + rt_uint32_t value = HWREG32(ths->base + H616_THS_CALIB + i * 4); + HWREG32(ths->base + H616_THS_CALIB + i * 4) = + (value & ~0x0fff0fffU) | regs[i]; + } + return RT_EOK; +} + +static rt_err_t h616_ths_enable_analog(struct rt_device *dev) +{ + struct rt_ofw_node *np; + struct rt_syscon *syscon; + void *base; + + np = rt_ofw_parse_phandle(dev->ofw_node, "allwinner,sram", 0); + if (!np) + { + /* Older U-Boot trees omit the THS SRAM phandle. */ + np = rt_ofw_find_node_by_compatible(RT_NULL, "allwinner,sun50i-h616-system-control"); + } + if (!np) + { + return -RT_ENOENT; + } + + syscon = rt_syscon_find_by_ofw_node(np); + if (syscon) + { + rt_ofw_node_put(np); + return rt_syscon_update_bits(syscon, 0, RT_BIT(16), 0); + } + + /* Legacy trees lack the generic syscon compatible as well. */ + base = rt_ofw_iomap(np, 0); + rt_ofw_node_put(np); + if (!base) + { + return -RT_EIO; + } + HWREG32(base) &= ~RT_BIT(16); + rt_iounmap(base); + return RT_EOK; +} + +static void h616_ths_release(struct h616_thermal *ths) +{ + if (ths->clock_enabled) + { + HWREG32(ths->base + H616_THS_ENABLE) = 0; + rt_clk_disable_unprepare(ths->clk); + } + if (ths->reset_deasserted) + { + rt_reset_control_assert(ths->reset); + } + if (ths->clk && !rt_is_err(ths->clk)) + { + rt_clk_put(ths->clk); + } + if (ths->reset && !rt_is_err(ths->reset)) + { + rt_reset_control_put(ths->reset); + } + if (ths->base) + { + rt_iounmap(ths->base); + } + rt_free(ths); +} + +static rt_err_t h616_ths_probe(struct rt_platform_device *pdev) +{ + struct rt_device *dev = &pdev->parent; + struct h616_thermal *ths; + rt_err_t err; + int i, registered = 0; + + ths = rt_calloc(1, sizeof(*ths)); + if (!ths) + { + return -RT_ENOMEM; + } + ths->base = rt_dm_dev_iomap(dev, 0); + if (!ths->base) + { + err = -RT_EIO; + goto _fail; + } + ths->clk = rt_clk_get_by_name(dev, "bus"); + ths->reset = rt_reset_control_get_by_index(dev, 0); + if (!ths->clk || rt_is_err(ths->clk) || !ths->reset || rt_is_err(ths->reset)) + { + err = -RT_ENOENT; + goto _fail; + } + if ((err = rt_reset_control_deassert(ths->reset))) + { + goto _fail; + } + ths->reset_deasserted = RT_TRUE; + if ((err = rt_clk_prepare_enable(ths->clk))) + { + goto _fail; + } + ths->clock_enabled = RT_TRUE; + + HWREG32(ths->base + H616_THS_ENABLE) = 0; + HWREG32(ths->base + H616_THS_IRQ_ENABLE) = 0; + if ((err = h616_ths_enable_analog(dev)) || + (err = h616_ths_calibrate(dev, ths))) + { + LOG_E("THS setup/calibration failed: %d", err); + goto _fail; + } + HWREG32(ths->base + H616_THS_CTL) = (479 << 16) | 47; + HWREG32(ths->base + H616_THS_FILTER) = RT_BIT(2) | 1; + HWREG32(ths->base + H616_THS_PERIOD) = 365 << 12; + HWREG32(ths->base + H616_THS_ENABLE) = 0xf; + + for (i = 0; i < H616_THS_SENSORS; ++i) + { + struct rt_thermal_zone_device *zone = &ths->zones[i]; + + zone->parent.ofw_node = dev->ofw_node; + zone->zone_id = i; + zone->priv = ths; + zone->ops = &h616_ths_ops; + zone->polling_delay = rt_tick_from_millisecond(1000); + rt_dm_dev_set_name(&zone->parent, "sun50i-h616-ths"); + err = rt_thermal_zone_device_register(zone); + if (err) + { + LOG_E("sensor %d registration failed: %d", i, err); + continue; + } + /* DT zones with no polling expect IRQs; this driver uses polling. */ + rt_mutex_take(&zone->mutex, RT_WAITING_FOREVER); + if (!zone->polling_delay) + { + zone->polling_delay = rt_tick_from_millisecond(1000); + } + rt_mutex_release(&zone->mutex); + rt_work_submit(&zone->poller, zone->polling_delay); + ++registered; + } + if (!registered) + { + goto _fail; + } + dev->user_data = ths; + return RT_EOK; + +_fail: + h616_ths_release(ths); + return err; +} + +static rt_err_t h616_ths_remove(struct rt_platform_device *pdev) +{ + /* Permanent SoC sensors: keep resources while thermal pollers are live. */ + return -RT_EBUSY; +} + +static const struct rt_ofw_node_id h616_ths_ids[] = { + { .compatible = "allwinner,sun50i-h616-ths" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver h616_ths_driver = { + .name = "thermal-sun50i-h616", + .ids = h616_ths_ids, + .probe = h616_ths_probe, + .remove = h616_ths_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(h616_ths_driver); diff --git a/bsp/allwinner/dm/watchdog/watchdog-sunxi.c b/bsp/allwinner/dm/watchdog/watchdog-sunxi.c index 52a2e5cb47cf..6167b01babed 100644 --- a/bsp/allwinner/dm/watchdog/watchdog-sunxi.c +++ b/bsp/allwinner/dm/watchdog/watchdog-sunxi.c @@ -32,9 +32,16 @@ struct sunxi_wdt rt_watchdog_t parent; void *base; rt_uint32_t timeout; + rt_uint32_t key; const rt_uint8_t *timeout_codes; }; +struct sunxi_wdt_soc_data +{ + const rt_uint8_t *timeout_codes; + rt_uint32_t key; +}; + #define to_sunxi_wdt(raw) rt_container_of(raw, struct sunxi_wdt, parent) static struct sunxi_wdt *machine_wdt; @@ -46,6 +53,23 @@ static const rt_uint8_t sunxi_wdt_timeout_codes_v2[] = [12] = 0xd, [14] = 0xe, [16] = 0xf, }; +static const struct sunxi_wdt_soc_data sunxi_wdt_data = +{ + .timeout_codes = sunxi_wdt_timeout_codes, +}; + +static const struct sunxi_wdt_soc_data sunxi_wdt_f101_data = +{ + .timeout_codes = sunxi_wdt_timeout_codes_v2, + .key = SUNXI_WDT_KEY, +}; + +static const struct sunxi_wdt_soc_data sunxi_wdt_d1_data = +{ + .timeout_codes = sunxi_wdt_timeout_codes, + .key = SUNXI_WDT_KEY, +}; + static rt_uint32_t sunxi_wdt_select_timeout(struct sunxi_wdt *wdt, rt_uint32_t requested) { @@ -81,7 +105,7 @@ static rt_err_t sunxi_wdt_set_timeout(struct sunxi_wdt *wdt, value = HWREG32(wdt->base + SUNXI_WDT_MODE); value &= ~SUNXI_WDT_TIMEOUT_MASK; value |= wdt->timeout_codes[timeout] << SUNXI_WDT_TIMEOUT_SHIFT; - HWREG32(wdt->base + SUNXI_WDT_MODE) = value | SUNXI_WDT_KEY; + HWREG32(wdt->base + SUNXI_WDT_MODE) = value | wdt->key; wdt->timeout = timeout; sunxi_wdt_ping(wdt); @@ -102,18 +126,18 @@ static rt_err_t sunxi_wdt_start(struct sunxi_wdt *wdt) value = HWREG32(wdt->base + SUNXI_WDT_CFG); value &= ~SUNXI_WDT_RESET_MASK; value |= SUNXI_WDT_RESET_SYSTEM; - HWREG32(wdt->base + SUNXI_WDT_CFG) = value | SUNXI_WDT_KEY; + HWREG32(wdt->base + SUNXI_WDT_CFG) = value | wdt->key; value = HWREG32(wdt->base + SUNXI_WDT_MODE); HWREG32(wdt->base + SUNXI_WDT_MODE) = value | SUNXI_WDT_MODE_ENABLE | - SUNXI_WDT_KEY; + wdt->key; return RT_EOK; } static void sunxi_wdt_stop(struct sunxi_wdt *wdt) { - HWREG32(wdt->base + SUNXI_WDT_MODE) = SUNXI_WDT_KEY; + HWREG32(wdt->base + SUNXI_WDT_MODE) = wdt->key; } static void sunxi_wdt_machine_reset(void) @@ -127,9 +151,9 @@ static void sunxi_wdt_machine_reset(void) sunxi_wdt_stop(wdt); HWREG32(wdt->base + SUNXI_WDT_IRQ_EN) = 0; - HWREG32(wdt->base + SUNXI_WDT_CFG) = SUNXI_WDT_KEY | + HWREG32(wdt->base + SUNXI_WDT_CFG) = wdt->key | SUNXI_WDT_RESET_SYSTEM; - HWREG32(wdt->base + SUNXI_WDT_MODE) = SUNXI_WDT_KEY | + HWREG32(wdt->base + SUNXI_WDT_MODE) = wdt->key | SUNXI_WDT_MODE_ENABLE; for (;;) @@ -188,8 +212,14 @@ static rt_err_t sunxi_wdt_probe(struct rt_platform_device *pdev) rt_err_t err; const char *name; struct rt_device *dev = &pdev->parent; - struct sunxi_wdt *wdt = rt_calloc(1, sizeof(*wdt)); + const struct sunxi_wdt_soc_data *soc_data = pdev->id->data; + struct sunxi_wdt *wdt; + if (!soc_data) + { + return -RT_EINVAL; + } + wdt = rt_calloc(1, sizeof(*wdt)); if (!wdt) { return -RT_ENOMEM; @@ -202,15 +232,8 @@ static rt_err_t sunxi_wdt_probe(struct rt_platform_device *pdev) goto _fail; } - if (rt_ofw_node_is_compatible(dev->ofw_node, - "allwinner,sun252i-f101-wdt")) - { - wdt->timeout_codes = sunxi_wdt_timeout_codes_v2; - } - else - { - wdt->timeout_codes = sunxi_wdt_timeout_codes; - } + wdt->timeout_codes = soc_data->timeout_codes; + wdt->key = soc_data->key; wdt->timeout = 16; sunxi_wdt_stop(wdt); wdt->parent.ops = &sunxi_wdt_ops; @@ -243,6 +266,12 @@ static rt_err_t sunxi_wdt_probe(struct rt_platform_device *pdev) static rt_err_t sunxi_wdt_remove(struct rt_platform_device *pdev) { struct sunxi_wdt *wdt = pdev->parent.user_data; + rt_err_t err = rt_device_unregister(&wdt->parent.parent); + + if (err) + { + return err; + } if (machine_wdt == wdt) { @@ -250,7 +279,6 @@ static rt_err_t sunxi_wdt_remove(struct rt_platform_device *pdev) rt_dm_machine_reset = RT_NULL; } sunxi_wdt_stop(wdt); - rt_device_unregister(&wdt->parent.parent); rt_iounmap(wdt->base); rt_free(wdt); @@ -259,8 +287,9 @@ static rt_err_t sunxi_wdt_remove(struct rt_platform_device *pdev) static const struct rt_ofw_node_id sunxi_wdt_ofw_ids[] = { - { .compatible = "allwinner,sun252i-f101-wdt" }, - { .compatible = "allwinner,sun20i-d1-wdt" }, + { .compatible = "allwinner,sun252i-f101-wdt", .data = &sunxi_wdt_f101_data }, + { .compatible = "allwinner,sun20i-d1-wdt", .data = &sunxi_wdt_d1_data }, + { .compatible = "allwinner,sun50i-h616-wdt", .data = &sunxi_wdt_data }, { /* sentinel */ } }; diff --git a/bsp/allwinner/h616/.config b/bsp/allwinner/h616/.config new file mode 100644 index 000000000000..1fa586da7420 --- /dev/null +++ b/bsp/allwinner/h616/.config @@ -0,0 +1,672 @@ + +# +# RT-Thread Kernel +# + +# +# klibc options +# + +# +# rt_vsnprintf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set +# CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG is not set +# CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD is not set +# end of rt_vsnprintf options + +# +# rt_vsscanf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSSCANF is not set +# end of rt_vsscanf options + +# +# rt_memset options +# +# CONFIG_RT_KLIBC_USING_USER_MEMSET is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMSET is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMSET is not set +# end of rt_memset options + +# +# rt_memcpy options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMCPY is not set +# end of rt_memcpy options + +# +# rt_memmove options +# +# CONFIG_RT_KLIBC_USING_USER_MEMMOVE is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMMOVE is not set +# end of rt_memmove options + +# +# rt_memcmp options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCMP is not set +# end of rt_memcmp options + +# +# rt_strstr options +# +# CONFIG_RT_KLIBC_USING_USER_STRSTR is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRSTR is not set +# end of rt_strstr options + +# +# rt_strcasecmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCASECMP is not set +# end of rt_strcasecmp options + +# +# rt_strncpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCPY is not set +# end of rt_strncpy options + +# +# rt_strcpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCPY is not set +# end of rt_strcpy options + +# +# rt_strncmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCMP is not set +# end of rt_strncmp options + +# +# rt_strcmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCMP is not set +# end of rt_strcmp options + +# +# rt_strlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRLEN is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRLEN is not set +# end of rt_strlen options + +# +# rt_strnlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRNLEN is not set +# end of rt_strnlen options +# end of klibc options + +CONFIG_RT_NAME_MAX=24 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +# CONFIG_RT_USING_NANO is not set +# CONFIG_RT_USING_SMART is not set +# CONFIG_RT_USING_AMP is not set +CONFIG_RT_USING_SMP=y +CONFIG_RT_CPUS_NR=4 +CONFIG_RT_ALIGN_SIZE=8 +# CONFIG_RT_THREAD_PRIORITY_8 is not set +CONFIG_RT_THREAD_PRIORITY_32=y +# CONFIG_RT_THREAD_PRIORITY_256 is not set +CONFIG_RT_THREAD_PRIORITY_MAX=32 +CONFIG_RT_TICK_PER_SECOND=1000 +CONFIG_RT_USING_OVERFLOW_CHECK=y +CONFIG_RT_USING_HOOK=y +CONFIG_RT_HOOK_USING_FUNC_PTR=y +# CONFIG_RT_USING_HOOKLIST is not set +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=4096 +CONFIG_SYSTEM_THREAD_STACK_SIZE=8192 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=4096 +# CONFIG_RT_USING_TIMER_ALL_SOFT is not set +# CONFIG_RT_USING_CPU_USAGE_TRACER is not set + +# +# kservice options +# +# CONFIG_RT_USING_TINY_FFS is not set +# end of kservice options + +CONFIG_RT_USING_DEBUG=y +CONFIG_RT_DEBUGING_ASSERT=y +CONFIG_RT_DEBUGING_COLOR=y +CONFIG_RT_DEBUGING_CONTEXT=y +# CONFIG_RT_DEBUGING_AUTO_INIT is not set +# CONFIG_RT_DEBUGING_SPINLOCK is not set +# CONFIG_RT_DEBUGING_CRITICAL is not set +# CONFIG_RT_USING_CI_ACTION is not set + +# +# Inter-Thread communication +# +CONFIG_RT_USING_SEMAPHORE=y +CONFIG_RT_USING_MUTEX=y +CONFIG_RT_USING_EVENT=y +CONFIG_RT_USING_MAILBOX=y +CONFIG_RT_USING_MESSAGEQUEUE=y +# CONFIG_RT_USING_MESSAGEQUEUE_PRIORITY is not set +# CONFIG_RT_USING_SIGNALS is not set +# end of Inter-Thread communication + +# +# Memory Management +# +CONFIG_RT_USING_MEMPOOL=y +# CONFIG_RT_USING_SMALL_MEM is not set +# CONFIG_RT_USING_SLAB is not set +CONFIG_RT_USING_MEMHEAP=y +CONFIG_RT_MEMHEAP_FAST_MODE=y +# CONFIG_RT_MEMHEAP_BEST_MODE is not set +# CONFIG_RT_USING_SMALL_MEM_AS_HEAP is not set +CONFIG_RT_USING_MEMHEAP_AS_HEAP=y +CONFIG_RT_USING_MEMHEAP_AUTO_BINDING=y +# CONFIG_RT_USING_SLAB_AS_HEAP is not set +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +# CONFIG_RT_USING_MEMTRACE is not set +# CONFIG_RT_USING_HEAP_ISR is not set +CONFIG_RT_USING_HEAP=y +# end of Memory Management + +CONFIG_RT_USING_DEVICE=y +CONFIG_RT_USING_DEVICE_OPS=y +CONFIG_RT_USING_INTERRUPT_INFO=y +# CONFIG_RT_USING_THREADSAFE_PRINTF is not set +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=256 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart0" +CONFIG_RT_USING_CONSOLE_OUTPUT_CTL=y +CONFIG_RT_VER_NUM=0x50400 +CONFIG_RT_USING_STDC_ATOMIC=y +CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32 +# end of RT-Thread Kernel + +# +# AArch64 Architecture Configuration +# +CONFIG_ARCH_TEXT_OFFSET=0x80000 +CONFIG_ARCH_RAM_OFFSET=0x40000000 +CONFIG_ARCH_SECONDARY_CPU_STACK_SIZE=4096 +CONFIG_ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USING_GENERIC_CPUID=y +CONFIG_ARCH_HEAP_SIZE=0x4000000 +CONFIG_ARCH_INIT_PAGE_SIZE=0x200000 +# CONFIG_ARCH_USING_CPUIDLE is not set +# end of AArch64 Architecture Configuration + +CONFIG_ARCH_CPU_64BIT=y +CONFIG_RT_USING_CACHE=y +CONFIG_RT_USING_CPU_FFS=y +CONFIG_ARCH_MM_MMU=y +CONFIG_ARCH_ARM=y +CONFIG_ARCH_ARM_MMU=y +CONFIG_ARCH_ARMV8=y +CONFIG_ARCH_USING_ASID=y +CONFIG_ARCH_USING_HW_THREAD_SELF=y +CONFIG_ARCH_USING_IRQ_CTX_LIST=y + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=8192 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=16384 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=5 +# CONFIG_FINSH_USING_WORD_OPERATION is not set +# CONFIG_FINSH_USING_FUNC_EXT is not set +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 +CONFIG_FINSH_USING_OPTION_COMPLETION=y + +# +# DFS: device virtual file system +# +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +# CONFIG_RT_USING_DFS_MNTTABLE is not set +CONFIG_DFS_FD_MAX=16 +CONFIG_RT_USING_DFS_V1=y +# CONFIG_RT_USING_DFS_V2 is not set +CONFIG_DFS_FILESYSTEMS_MAX=4 +CONFIG_DFS_FILESYSTEM_TYPES_MAX=4 +CONFIG_RT_USING_DFS_ELMFAT=y + +# +# elm-chan's FatFs, Generic FAT Filesystem Module +# +CONFIG_RT_DFS_ELM_CODE_PAGE=437 +CONFIG_RT_DFS_ELM_WORD_ACCESS=y +# CONFIG_RT_DFS_ELM_USE_LFN_0 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_1 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set +CONFIG_RT_DFS_ELM_USE_LFN_3=y +CONFIG_RT_DFS_ELM_USE_LFN=3 +CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y +# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set +CONFIG_RT_DFS_ELM_LFN_UNICODE=0 +CONFIG_RT_DFS_ELM_MAX_LFN=255 +CONFIG_RT_DFS_ELM_DRIVES=2 +CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512 +# CONFIG_RT_DFS_ELM_USE_ERASE is not set +CONFIG_RT_DFS_ELM_REENTRANT=y +CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000 +# CONFIG_RT_DFS_ELM_USE_EXFAT is not set +# end of elm-chan's FatFs, Generic FAT Filesystem Module + +CONFIG_RT_USING_DFS_DEVFS=y +# CONFIG_RT_USING_DFS_9PFS is not set +# CONFIG_RT_USING_DFS_ISO9660 is not set +# CONFIG_RT_USING_DFS_ROMFS is not set +# CONFIG_RT_USING_DFS_CROMFS is not set +# CONFIG_RT_USING_DFS_RAMFS is not set +# CONFIG_RT_USING_DFS_TMPFS is not set +# CONFIG_RT_USING_DFS_MQUEUE is not set +# end of DFS: device virtual file system + +# CONFIG_RT_USING_FAL is not set + +# +# Device Drivers +# +CONFIG_RT_USING_DM=y +CONFIG_RT_USING_DEV_BUS=y +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_UNAMED_PIPE_NUMBER=64 +CONFIG_RT_USING_SYSTEM_WORKQUEUE=y +CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=8192 +CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +# CONFIG_RT_SERIAL_USING_DMA is not set +CONFIG_RT_SERIAL_RB_BUFSZ=256 +# CONFIG_RT_USING_SERIAL_BYPASS is not set +# CONFIG_RT_SERIAL_EARLY_HVC is not set +# CONFIG_RT_SERIAL_PL011 is not set +CONFIG_RT_SERIAL_8250=y +CONFIG_RT_SERIAL_8250_DW=y +# CONFIG_RT_USING_CAN is not set +CONFIG_RT_USING_ADC=y +# CONFIG_RT_USING_ADC_V2 is not set +CONFIG_RT_USING_ADC_V1=y +CONFIG_RT_ADC_SUNXI_GPADC=y +CONFIG_RT_INPUT_SUNXI_LRADC=y +CONFIG_RT_USING_CLOCK_TIME=y +# CONFIG_RT_USING_CLOCK_TIMER_TRIGGER is not set +CONFIG_RT_CLOCK_TIME_ARM_ARCH=y +# CONFIG_RT_CLOCK_TIMER_SUNXI is not set +CONFIG_RT_USING_I2C=y +# CONFIG_RT_I2C_DEBUG is not set +CONFIG_RT_USING_I2C_BITOPS=y +# CONFIG_RT_I2C_BITOPS_DEBUG is not set +# CONFIG_RT_USING_SOFT_I2C is not set +CONFIG_RT_I2C_SUNXI=y +# CONFIG_RT_USING_PHY is not set +# CONFIG_RT_USING_PHY_V2 is not set +# CONFIG_RT_USING_DAC is not set +# CONFIG_RT_USING_NULL is not set +# CONFIG_RT_USING_ZERO is not set +# CONFIG_RT_USING_RANDOM is not set +CONFIG_RT_USING_PWM=y +CONFIG_RT_PWM_SUNXI=y +# CONFIG_RT_USING_PULSE_ENCODER is not set +CONFIG_RT_USING_INPUT_CAPTURE=y +CONFIG_RT_INPUT_CAPTURE_RB_SIZE=100 +CONFIG_RT_INPUT_CAPTURE_SUNXI_IRRX=y +CONFIG_RT_USING_MTD_NOR=y +# CONFIG_RT_USING_MTD_NOR_CFI is not set +CONFIG_RT_USING_MTD_NOR_SPI=y +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_PM is not set +CONFIG_RT_USING_RTC=y +CONFIG_RT_USING_ALARM=y +CONFIG_RT_ALARM_STACK_SIZE=4096 +CONFIG_RT_ALARM_TIMESLICE=5 +CONFIG_RT_ALARM_PRIORITY=10 +# CONFIG_RT_ALARM_USING_LOCAL_TIME is not set +# CONFIG_RT_USING_SOFT_RTC is not set +# CONFIG_RT_RTC_DS1302 is not set +# CONFIG_RT_RTC_DS1307 is not set +# CONFIG_RT_RTC_GOLDFISH is not set +# CONFIG_RT_RTC_HYM8563 is not set +# CONFIG_RT_RTC_PCF8523 is not set +# CONFIG_RT_RTC_PCF8563 is not set +# CONFIG_RT_RTC_PL031 is not set +# CONFIG_RT_RTC_RX8010 is not set +CONFIG_RT_RTC_SUNXI=y +CONFIG_RT_USING_SDIO=y +CONFIG_RT_SDIO_STACK_SIZE=8192 +CONFIG_RT_SDIO_THREAD_PRIORITY=15 +CONFIG_RT_MMCSD_STACK_SIZE=8192 +CONFIG_RT_MMCSD_THREAD_PRIORITY=22 +CONFIG_RT_MMCSD_MAX_PARTITION=16 +# CONFIG_RT_SDIO_DEBUG is not set +# CONFIG_RT_USING_SDHCI is not set +# CONFIG_RT_SDIO_DW_MMC is not set +# CONFIG_RT_SDIO_SDHCI_DWCMSHC is not set +CONFIG_RT_SDIO_SUNXI=y +CONFIG_RT_USING_SPI=y +CONFIG_RT_USING_SPI_ISR=y +# CONFIG_RT_USING_SPI_BITOPS is not set +# CONFIG_RT_USING_SOFT_SPI is not set +# CONFIG_RT_USING_QSPI is not set +# CONFIG_RT_USING_SPI_MSD is not set +CONFIG_RT_USING_SFUD=y +CONFIG_RT_SFUD_USING_SFDP=y +CONFIG_RT_SFUD_USING_FLASH_INFO_TABLE=y +# CONFIG_RT_SFUD_USING_QSPI is not set +CONFIG_RT_SFUD_SPI_MAX_HZ=50000000 +# CONFIG_RT_DEBUG_SFUD is not set +# CONFIG_RT_USING_ENC28J60 is not set +# CONFIG_RT_USING_SPI_WIFI is not set +CONFIG_RT_SPI_SUNXI=y +CONFIG_RT_USING_WDT=y +# CONFIG_RT_WDT_DW is not set +CONFIG_RT_WDT_SUNXI=y +CONFIG_RT_USING_AUDIO=y +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_SIZE=4096 +CONFIG_RT_AUDIO_REPLAY_MP_BLOCK_COUNT=2 +CONFIG_RT_AUDIO_RECORD_PIPE_SIZE=2048 +# CONFIG_RT_AUDIO_SUNXI_CODEC is not set +CONFIG_RT_AUDIO_SUN50I_H616_CODEC=y +CONFIG_RT_AUDIO_SUN50I_H616_AHUB=y +# CONFIG_RT_AUDIO_SUNXI_I2S0 is not set +# CONFIG_RT_AUDIO_SUNXI_OWA is not set +# CONFIG_RT_USING_SENSOR is not set +# CONFIG_RT_USING_TOUCH is not set +CONFIG_RT_USING_LCD=y +CONFIG_RT_USING_GRAPHIC=y +# CONFIG_RT_GRAPHIC_BACKLIGHT is not set +CONFIG_RT_GRAPHIC_FB=y +# CONFIG_RT_GRAPHIC_FB_ILI9486 is not set +CONFIG_RT_GRAPHIC_FB_SIMPLE=y +# CONFIG_RT_GRAPHIC_FB_SUNXI_DE is not set +CONFIG_RT_GRAPHIC_FB_SUN50I_H616_DE33=y +CONFIG_RT_GRAPHIC_LOGO=y +# CONFIG_RT_GRAPHIC_LOGO_NONE is not set +CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_CLUT224=y +# CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_WHITE_CLUT224 is not set +CONFIG_RT_USING_HWCRYPTO=y +CONFIG_RT_HWCRYPTO_DEFAULT_NAME="hwcryto" +CONFIG_RT_HWCRYPTO_IV_MAX_SIZE=16 +CONFIG_RT_HWCRYPTO_KEYBIT_MAX_SIZE=256 +# CONFIG_RT_HWCRYPTO_USING_GCM is not set +CONFIG_RT_HWCRYPTO_USING_AES=y +CONFIG_RT_HWCRYPTO_USING_AES_ECB=y +CONFIG_RT_HWCRYPTO_USING_AES_CBC=y +# CONFIG_RT_HWCRYPTO_USING_AES_CFB is not set +# CONFIG_RT_HWCRYPTO_USING_AES_CTR is not set +# CONFIG_RT_HWCRYPTO_USING_AES_OFB is not set +# CONFIG_RT_HWCRYPTO_USING_DES is not set +# CONFIG_RT_HWCRYPTO_USING_3DES is not set +# CONFIG_RT_HWCRYPTO_USING_RC4 is not set +# CONFIG_RT_HWCRYPTO_USING_MD5 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA1 is not set +# CONFIG_RT_HWCRYPTO_USING_SHA2 is not set +CONFIG_RT_HWCRYPTO_USING_RNG=y +# CONFIG_RT_HWCRYPTO_USING_CRC is not set +# CONFIG_RT_HWCRYPTO_USING_BIGNUM is not set +# CONFIG_RT_HWCRYPTO_SUNXI_CE is not set +CONFIG_RT_HWCRYPTO_SUN50I_H616_CE=y +# CONFIG_RT_USING_WIFI is not set +# CONFIG_RT_USING_PTP is not set +CONFIG_RT_USING_LED=y +CONFIG_RT_LED_GPIO=y +# CONFIG_RT_LED_PWM is not set +# CONFIG_RT_LED_SYSCON is not set +CONFIG_RT_USING_INPUT=y +CONFIG_RT_INPUT_POWER=y +# CONFIG_RT_INPUT_JOYSTICK is not set +# CONFIG_RT_INPUT_KEYBOARD is not set +# CONFIG_RT_INPUT_MISC is not set +# CONFIG_RT_INPUT_TOUCHSCREEN is not set +# CONFIG_RT_USING_MBOX is not set +# CONFIG_RT_USING_HWSPINLOCK is not set +# CONFIG_RT_USING_RPMSG is not set +# CONFIG_RT_USING_PHYE is not set +# CONFIG_RT_USING_ATA is not set +# CONFIG_RT_USING_NVME is not set +CONFIG_RT_USING_BLK=y + +# +# Partition Types +# +CONFIG_RT_BLK_PARTITION_DFS=y +CONFIG_RT_BLK_PARTITION_EFI=y +# end of Partition Types + +# CONFIG_RT_USING_SCSI is not set +# CONFIG_RT_USING_FIRMWARE is not set +# CONFIG_RT_USING_TEE is not set +# CONFIG_RT_USING_HWCACHE is not set +CONFIG_RT_USING_DVFS=y +CONFIG_RT_USING_DVFS_EVENT=y +CONFIG_RT_USING_DVFS_OPP_RETRY_MAX=10 + +# +# DVFS Event Drivers +# + +# +# DVFS CPUfreq Drivers +# +# CONFIG_RT_DVFS_SUN252I_F101_CPUFREQ is not set +CONFIG_RT_DVFS_SUN50I_H616_CPUFREQ=y + +# +# DVFS Devfreq Drivers +# +CONFIG_RT_USING_REGULATOR=y +# CONFIG_RT_REGULATOR_FAN53555 is not set +CONFIG_RT_REGULATOR_AXP313A=y +CONFIG_RT_REGULATOR_FIXED=y +CONFIG_RT_REGULATOR_GPIO=y +# CONFIG_RT_REGULATOR_PWM is not set +CONFIG_RT_USING_RESET=y +# CONFIG_RT_RESET_SIMPLE is not set + +# +# Power Management (PM) Domains device drivers +# +# end of Power Management (PM) Domains device drivers + +# CONFIG_RT_USING_POWER_RESET is not set +# CONFIG_RT_USING_POWER_SUPPLY is not set +CONFIG_RT_USING_THERMAL=y + +# +# Thermal Sensors Drivers +# +# CONFIG_RT_THERMAL_SUNXI is not set +CONFIG_RT_THERMAL_SUN50I_H616=y + +# +# Thermal Cool Drivers +# +CONFIG_RT_THERMAL_COOL_DVFS=y +# CONFIG_RT_THERMAL_COOL_GPIO_FAN is not set +# CONFIG_RT_THERMAL_COOL_PWM_FAN is not set +# CONFIG_RT_USING_VIRTIO is not set +CONFIG_RT_USING_NVMEM=y +CONFIG_RT_NVMEM_SUNXI_SID=y +CONFIG_RT_USING_DMA=y +# CONFIG_RT_DMA_PL330 is not set +# CONFIG_RT_DMA_DW_AXI_DMAC is not set +CONFIG_RT_DMA_SUNXI=y +CONFIG_RT_USING_MFD=y +CONFIG_RT_MFD_SYSCON=y +CONFIG_RT_USING_OFW=y +# CONFIG_RT_USING_BUILTIN_FDT is not set +CONFIG_RT_FDT_EARLYCON_MSG_SIZE=128 +CONFIG_RT_USING_OFW_BUS_RANGES_NUMBER=8 +# CONFIG_RT_USING_PCI is not set +CONFIG_RT_USING_PIC=y +# CONFIG_RT_USING_PIC_STATISTICS is not set +CONFIG_MAX_HANDLERS=256 +CONFIG_RT_PIC_ARM_GIC=y +# CONFIG_RT_PIC_ARM_GIC_V3 is not set +CONFIG_RT_PIC_ARM_GIC_MAX_NR=1 +CONFIG_RT_USING_PIN=y +# CONFIG_RT_PIN_PL061 is not set +CONFIG_RT_USING_PINCTRL=y +# CONFIG_RT_PINCTRL_SINGLE is not set +CONFIG_RT_PINCTRL_SUN50I_H616=y +# CONFIG_RT_PINCTRL_SUN252I_F101 is not set +CONFIG_RT_USING_CLK=y +CONFIG_RT_CLK_SUN50I_H616=y +# CONFIG_RT_CLK_SUN252I_F101 is not set +# CONFIG_RT_USING_CHERRYUSB is not set + +# +# SoC (System on Chip) Drivers +# +CONFIG_RT_SOC_SUNXI_G2D=y +CONFIG_RT_SOC_SUNXI_JPEG_DEC=y +# CONFIG_RT_SOC_SUNXI_JPEG_ENC is not set +# CONFIG_RT_SOC_SUNXI_PNG_DEC is not set +# end of SoC (System on Chip) Drivers +# end of Device Drivers + +# +# C/C++ and POSIX layer +# + +# +# ISO-ANSI C layer +# + +# +# Timezone and Daylight Saving Time +# +# CONFIG_RT_LIBC_USING_FULL_TZ_DST is not set +CONFIG_RT_LIBC_USING_LIGHT_TZ_DST=y +CONFIG_RT_LIBC_TZ_DEFAULT_HOUR=8 +CONFIG_RT_LIBC_TZ_DEFAULT_MIN=0 +CONFIG_RT_LIBC_TZ_DEFAULT_SEC=0 +# end of Timezone and Daylight Saving Time +# end of ISO-ANSI C layer + +# +# POSIX (Portable Operating System Interface) layer +# +# CONFIG_RT_USING_POSIX_FS is not set +# CONFIG_RT_USING_POSIX_DELAY is not set +# CONFIG_RT_USING_POSIX_CLOCK is not set +# CONFIG_RT_USING_POSIX_TIMER is not set +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +# CONFIG_RT_USING_POSIX_PIPE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# end of Interprocess Communication (IPC) +# end of POSIX (Portable Operating System Interface) layer + +# CONFIG_RT_USING_CPLUSPLUS is not set +# end of C/C++ and POSIX layer + +# +# Network +# +# CONFIG_RT_USING_SAL is not set +# CONFIG_RT_USING_NETDEV is not set +# CONFIG_RT_USING_LWIP is not set +# CONFIG_RT_USING_AT is not set +# end of Network + +# +# Memory protection +# +# CONFIG_RT_USING_MEM_PROTECTION is not set +# CONFIG_RT_USING_HW_STACK_GUARD is not set +# end of Memory protection + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +# CONFIG_RT_USING_UTEST is not set +# CONFIG_RT_USING_VAR_EXPORT is not set +# CONFIG_RT_USING_RESOURCE_ID is not set +CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y +CONFIG_RT_USING_ADT_BITMAP=y +CONFIG_RT_USING_ADT_HASHMAP=y +CONFIG_RT_USING_ADT_REF=y +# CONFIG_RT_USING_RT_LINK is not set +# end of Utilities + +# +# Memory management +# +# CONFIG_RT_PAGE_MPR_SIZE_DYNAMIC is not set +CONFIG_RT_PAGE_AFFINITY_BLOCK_SIZE=0x1000 +CONFIG_RT_PAGE_MAX_ORDER=11 +CONFIG_RT_USING_MEMBLOCK=y +CONFIG_RT_INIT_MEMORY_REGIONS=128 + +# +# Debugging +# +# CONFIG_RT_DEBUGGING_ALIASING is not set +# CONFIG_RT_DEBUGING_PAGE_LEAK is not set +# CONFIG_RT_DEBUGGING_PAGE_POISON is not set +# end of Debugging +# end of Memory management + +# +# Using USB legacy version +# +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set +# end of Using USB legacy version + +# CONFIG_RT_USING_FDT is not set +# CONFIG_RT_USING_LEGACY_VIRTIO is not set +# CONFIG_RT_USING_RUST is not set +# end of RT-Thread Components + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set +# end of RT-Thread Utestcases + +CONFIG_SOC_H616=y diff --git a/bsp/allwinner/h616/Kconfig b/bsp/allwinner/h616/Kconfig new file mode 100644 index 000000000000..18c203850eb0 --- /dev/null +++ b/bsp/allwinner/h616/Kconfig @@ -0,0 +1,29 @@ +mainmenu "RT-Thread H616 Configuration" + +BSP_DIR := . +RTT_DIR := ../../.. +PKGS_DIR := packages +SOC_DM_DIR := $(BSP_DIR)/../dm + +source "$(SOC_DM_DIR)/Kconfig" +# H616 uses the generic ARM GIC and DesignWare UART drivers. +SOC_DM_PIC_DIR := $(RTT_DIR)/libcpu/aarch64/common/pic +SOC_DM_SERIAL_DIR := $(RTT_DIR)/libcpu/aarch64/common/serial + +source "$(RTT_DIR)/Kconfig" +osource "$(PKGS_DIR)/Kconfig" + +config SOC_H616 + bool + default y + select ARCH_ARMV8 + select ARCH_ARM_MMU + select ARCH_CPU_64BIT + select RT_USING_CACHE + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + select RT_USING_STDC_ATOMIC + help + Boot through U-Boot and TF-A, using the device tree supplied in x0 + by U-Boot. Memory size and board wiring come from that device tree. + No board DTB is built or embedded by this BSP. diff --git a/bsp/allwinner/h616/README.md b/bsp/allwinner/h616/README.md new file mode 100644 index 000000000000..0e0f42849963 --- /dev/null +++ b/bsp/allwinner/h616/README.md @@ -0,0 +1,97 @@ +# Allwinner H616 + +[简体中文版](README_zh.md) + +This BSP targets the Allwinner H616 family on 64-bit ARM systems. It uses +RT-Thread Device Model (DM) drivers and boots as an AArch64 image through the +existing TF-A and U-Boot firmware. `rtthread.bin` is a raw image; it neither +contains nor replaces boot firmware or a device tree. + +## Board Overview + +The BSP is used with an MQ-Quad board. The supplied firmware DTB is +currently named `sun50i-h616-orangepi-zero2.dtb` and therefore reports +`OrangePi Zero2` as its model at boot. This is a firmware-device-tree label, +not an assertion that the MQ-Quad has Orange Pi Zero2 wiring. In particular, +do not enable Ethernet, Wi-Fi, displays, or GPIO-connected peripherals based +only on that board name; use the physical board schematic. + +## Toolchain + +Use a bare-metal AArch64 GNU toolchain. The BSP is regularly built with +`aarch64-none-elf-gcc` from Arm GNU Toolchain 14.3.Rel1. Any compatible +toolchain can be selected through these variables: + +```sh +export RTT_EXEC_PATH=/opt/arm-gnu-toolchain-14.3.rel1-x86_64-aarch64-none-elf/bin +export RTT_CC_PREFIX=aarch64-none-elf- +aarch64-none-elf-gcc --version +``` + +Install SCons with RT-Thread ENV or `python -m pip install scons`. + +## Build + +```sh +cd bsp/allwinner/h616 +scons --menuconfig +scons -j$(nproc) +``` + +The build creates: + +- `rtthread.elf`, for debugging and symbol lookup; +- `rtthread.bin`, the raw AArch64 image loaded by U-Boot. + +`SOC_H616` selects MMU, SMP, cache, DM, and OFW support. + +## Boot + +Keep the board's TF-A, U-Boot, Linux image, and base DTB intact. A U-Boot +script should load `rtthread.bin` to the board's normal kernel load address, +load the base DTB, then invoke the standard AArch64 image boot command. The +supplied firmware uses `kernel_addr_r=0x40080000` and `fdt_addr_r=0x4fa00000`. + +Install the raw image on the boot filesystem, for example as +`/boot/rtthread.bin`. Preserve the existing board boot script and its overlay +processing. + +The boot arguments must retain a serial console. For the current firmware, +UART0 is at `0x05000000`; a suitable early console argument is: + +```text +earlycon=uart8250,mmio32,0x05000000,115200n8 console=ttyS0,115200n8 +``` + +`cma=` and `coherent_pool=` may be added when the enabled DMA/video workload +needs reserved contiguous memory. Choose their sizes for the board's RAM and +do not overlap firmware-reserved regions. + +## Device Support + +| Device | Status | Notes | +| --- | --- | --- | +| UART | Support | DesignWare 8250 console. | +| Clock / reset / pinctrl / regulator | Support | H616 CCU/R CCU, pin controller, and AXP313A PMIC. | +| RTC | Support | Calendar and alarm framework. | +| DMA | Support | Sunxi DMA controller. | +| SDIO | Support | SDC0, SDC1, and SDC2 hosts. | +| I2C | Support | Sunxi I2C controller. | +| SPI | Support | Sunxi SPI controller. | +| PWM | Support | Sunxi PWM controller. | +| LED | Support | DT GPIO LEDs. | +| Watchdog | Support | Sunxi watchdog. | +| Thermal | Support | Four H616 thermal sensors. | +| IR receiver | Support | Sunxi IR receiver. | +| Internal codec | Support | `sound0`; LINEOUT requires an amplifier for a speaker. | +| HDMI audio | Support | AHUB I2S1 LPCM path (`hdmi-sound0`). | +| JPEG decode | Support | Video Engine JPEG/MJPEG decoder (`jpegd0`). | +| DE33 / HDMI video | Support | DE33 mixer, TCON-TV, and DW-HDMI framebuffer path. | +| G2D | Support | H616 graphics accelerator. | +| HWCrypto | Support | H616 CE AES and TRNG. | +| GPADC / LRADC | Support | H616 analog input controllers. | + +## References + +- [Allwinner H616 Linux device tree binding sources](https://github.com/torvalds/linux/tree/master/Documentation/devicetree/bindings) +- [RT-Thread Device Model documentation](https://www.rt-thread.io/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/device) diff --git a/bsp/allwinner/h616/README_zh.md b/bsp/allwinner/h616/README_zh.md new file mode 100644 index 000000000000..2531aecc8b7f --- /dev/null +++ b/bsp/allwinner/h616/README_zh.md @@ -0,0 +1,90 @@ +# Allwinner H616 + +[English](README.md) + +本 BSP 面向 Allwinner H616 系列 64 位 ARM 平台,使用 RT-Thread Device Model +(DM)驱动,并通过板子既有的 TF-A 与 U-Boot 启动 AArch64 镜像。生成的 +`rtthread.bin` 是裸镜像,不包含也不替换启动固件或设备树。 + +## 板卡介绍 + +当前使用 MQ-Quad 板。现有固件提供的 DTB 文件名为 +`sun50i-h616-orangepi-zero2.dtb`,所以启动时会显示 `OrangePi Zero2`。这只是 +固件设备树的型号字符串,并不表示 MQ-Quad 的引脚和 Orange Pi Zero2 完全相同。 +尤其不能只依据该名称启用以太网、Wi-Fi、显示或 GPIO 外设,应以板子原理图为准。 + +## 工具链 + +需要裸机 AArch64 GNU 工具链。本 BSP 使用 Arm GNU Toolchain 14.3.Rel1 的 +`aarch64-none-elf-gcc` 编译验证;其它兼容工具链也可通过以下环境变量选择: + +```sh +export RTT_EXEC_PATH=/opt/arm-gnu-toolchain-14.3.rel1-x86_64-aarch64-none-elf/bin +export RTT_CC_PREFIX=aarch64-none-elf- +aarch64-none-elf-gcc --version +``` + +SCons 可由 RT-Thread ENV 安装,也可以使用 `python -m pip install scons`。 + +## 构建 + +```sh +cd bsp/allwinner/h616 +scons --menuconfig +scons -j$(nproc) +``` + +构建产物: + +- `rtthread.elf`:调试和符号解析使用; +- `rtthread.bin`:由 U-Boot 加载的裸 AArch64 镜像。 + +`SOC_H616` 会选择 MMU、SMP、Cache、DM 与 OFW 支持。 + +## 启动 + +保留板子原有的 TF-A、U-Boot、Linux 镜像和基础 DTB。U-Boot 启动脚本应将 +`rtthread.bin` 加载到正常的内核加载地址,再加载相同的基础 DTB 和该板必须的 DT +overlay,最后使用标准 AArch64 镜像启动命令并传入该 DTB。当前固件使用 +`kernel_addr_r=0x40080000`、`fdt_addr_r=0x4fa00000`。 + +将裸镜像放到启动分区,例如 `/boot/rtthread.bin`。保留现有 `boot.scr` 中的 +overlay 处理。 + +启动参数需要保留串口控制台。当前固件中 UART0 位于 `0x05000000`,可使用: + +```text +earlycon=uart8250,mmio32,0x05000000,115200n8 console=ttyS0,115200n8 +``` + +启用 DMA 或视频负载时,可按需添加 `cma=` 与 `coherent_pool=`。大小应按板卡内存 +规划确定,且不得与固件保留内存重叠。 + +## 设备驱动支持 + +| 设备 | 状态 | 说明 | +| --- | --- | --- | +| UART | 支持 | DesignWare 8250 控制台。 | +| 时钟 / 复位 / pinctrl / 稳压器 | 支持 | H616 CCU/R CCU、引脚控制器和 AXP313A PMIC。 | +| RTC | 支持 | 日历和闹钟框架。 | +| DMA | 支持 | Sunxi DMA 控制器。 | +| SDIO | 支持 | SDC0、SDC1 和 SDC2 host。 | +| I2C | 支持 | Sunxi I2C 控制器。 | +| SPI | 支持 | Sunxi SPI 控制器。 | +| PWM | 支持 | Sunxi PWM 控制器。 | +| LED | 支持 | DT GPIO LED。 | +| Watchdog | 支持 | Sunxi watchdog。 | +| 温度传感器 | 支持 | 四个 H616 thermal sensor。 | +| 红外接收 | 支持 | Sunxi IR receiver。 | +| 内部 codec | 支持 | `sound0`;扬声器需要接功放。 | +| HDMI 音频 | 支持 | AHUB I2S1 LPCM 路径(`hdmi-sound0`)。 | +| JPEG 解码 | 支持 | 视频引擎 JPEG/MJPEG 解码器(`jpegd0`)。 | +| DE33 / HDMI 视频 | 支持 | DE33 mixer、TCON-TV 和 DW-HDMI framebuffer 路径。 | +| G2D | 支持 | H616 图形加速器。 | +| HWCrypto | 支持 | H616 CE AES 和 TRNG。 | +| GPADC / LRADC | 支持 | H616 模拟输入控制器。 | + +## 参考资料 + +- [Allwinner H616 Linux device tree binding sources](https://github.com/torvalds/linux/tree/master/Documentation/devicetree/bindings) +- [RT-Thread Device Model 文档](https://www.rt-thread.io/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/device) diff --git a/bsp/allwinner/h616/SConscript b/bsp/allwinner/h616/SConscript new file mode 100644 index 000000000000..81ce8df78c06 --- /dev/null +++ b/bsp/allwinner/h616/SConscript @@ -0,0 +1,16 @@ +# for module compiling +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +objs = objs + SConscript(cwd + '/../dm/SConscript', variant_dir = 'dm', duplicate = 0) + +Return('objs') diff --git a/bsp/allwinner/h616/SConstruct b/bsp/allwinner/h616/SConstruct new file mode 100644 index 000000000000..ec6fe0187f21 --- /dev/null +++ b/bsp/allwinner/h616/SConstruct @@ -0,0 +1,29 @@ +import os +import sys +import rtconfig + +from rtconfig import RTT_ROOT + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +from building import * + +TARGET = 'rtthread.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS, + CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, + AR = rtconfig.AR, ARFLAGS = '-rc', + LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) +env['ASCOM'] = env['ASPPCOM'] + +Export('RTT_ROOT') +Export('rtconfig') + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT, has_libcpu = False) + +# make a building +DoBuilding(TARGET, objs) diff --git a/bsp/allwinner/h616/applications/SConscript b/bsp/allwinner/h616/applications/SConscript new file mode 100644 index 000000000000..a1754c6b7988 --- /dev/null +++ b/bsp/allwinner/h616/applications/SConscript @@ -0,0 +1,5 @@ +from building import * + +cwd = GetCurrentDir() +group = DefineGroup('Applications', ['main.c'], depend=[''], CPPPATH=[cwd]) +Return('group') diff --git a/bsp/allwinner/h616/applications/main.c b/bsp/allwinner/h616/applications/main.c new file mode 100644 index 000000000000..f942b2511c27 --- /dev/null +++ b/bsp/allwinner/h616/applications/main.c @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int main(void) +{ + rt_kprintf("Hello H616\n"); + return 0; +} diff --git a/bsp/allwinner/h616/board/SConscript b/bsp/allwinner/h616/board/SConscript new file mode 100644 index 000000000000..48d896aeeecb --- /dev/null +++ b/bsp/allwinner/h616/board/SConscript @@ -0,0 +1,5 @@ +from building import * + +cwd = GetCurrentDir() +group = DefineGroup('Board', ['board.c'], depend=[''], CPPPATH=[cwd]) +Return('group') diff --git a/bsp/allwinner/h616/board/board.c b/bsp/allwinner/h616/board/board.c new file mode 100644 index 000000000000..6d7bf8261310 --- /dev/null +++ b/bsp/allwinner/h616/board/board.c @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +void rt_hw_board_init(void) +{ + rt_hw_common_setup(); +} diff --git a/bsp/allwinner/h616/board/board.h b/bsp/allwinner/h616/board/board.h new file mode 100644 index 000000000000..358f6dd053b8 --- /dev/null +++ b/bsp/allwinner/h616/board/board.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __BOARD_H__ +#define __BOARD_H__ + +extern unsigned char __bss_start; +extern unsigned char __bss_end; + +void rt_hw_board_init(void); + +#endif /* __BOARD_H__ */ diff --git a/bsp/allwinner/h616/rtconfig.h b/bsp/allwinner/h616/rtconfig.h new file mode 100644 index 000000000000..903e9b24c139 --- /dev/null +++ b/bsp/allwinner/h616/rtconfig.h @@ -0,0 +1,395 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* RT-Thread Kernel */ + +/* klibc options */ + +/* rt_vsnprintf options */ + +/* end of rt_vsnprintf options */ + +/* rt_vsscanf options */ + +/* end of rt_vsscanf options */ + +/* rt_memset options */ + +/* end of rt_memset options */ + +/* rt_memcpy options */ + +/* end of rt_memcpy options */ + +/* rt_memmove options */ + +/* end of rt_memmove options */ + +/* rt_memcmp options */ + +/* end of rt_memcmp options */ + +/* rt_strstr options */ + +/* end of rt_strstr options */ + +/* rt_strcasecmp options */ + +/* end of rt_strcasecmp options */ + +/* rt_strncpy options */ + +/* end of rt_strncpy options */ + +/* rt_strcpy options */ + +/* end of rt_strcpy options */ + +/* rt_strncmp options */ + +/* end of rt_strncmp options */ + +/* rt_strcmp options */ + +/* end of rt_strcmp options */ + +/* rt_strlen options */ + +/* end of rt_strlen options */ + +/* rt_strnlen options */ + +/* end of rt_strnlen options */ +/* end of klibc options */ +#define RT_NAME_MAX 24 +#define RT_USING_SMP +#define RT_CPUS_NR 4 +#define RT_ALIGN_SIZE 8 +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 1000 +#define RT_USING_OVERFLOW_CHECK +#define RT_USING_HOOK +#define RT_HOOK_USING_FUNC_PTR +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 4096 +#define SYSTEM_THREAD_STACK_SIZE 8192 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 4096 + +/* kservice options */ + +/* end of kservice options */ +#define RT_USING_DEBUG +#define RT_DEBUGING_ASSERT +#define RT_DEBUGING_COLOR +#define RT_DEBUGING_CONTEXT + +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE +/* end of Inter-Thread communication */ + +/* Memory Management */ + +#define RT_USING_MEMPOOL +#define RT_USING_MEMHEAP +#define RT_MEMHEAP_FAST_MODE +#define RT_USING_MEMHEAP_AS_HEAP +#define RT_USING_MEMHEAP_AUTO_BINDING +#define RT_USING_HEAP +/* end of Memory Management */ +#define RT_USING_DEVICE +#define RT_USING_DEVICE_OPS +#define RT_USING_INTERRUPT_INFO +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 256 +#define RT_CONSOLE_DEVICE_NAME "uart0" +#define RT_USING_CONSOLE_OUTPUT_CTL +#define RT_VER_NUM 0x50400 +#define RT_USING_STDC_ATOMIC +#define RT_BACKTRACE_LEVEL_MAX_NR 32 +/* end of RT-Thread Kernel */ + +/* AArch64 Architecture Configuration */ + +#define ARCH_TEXT_OFFSET 0x80000 +#define ARCH_RAM_OFFSET 0x40000000 +#define ARCH_SECONDARY_CPU_STACK_SIZE 4096 +#define ARCH_HAVE_EFFICIENT_UNALIGNED_ACCESS +#define ARCH_USING_GENERIC_CPUID +#define ARCH_HEAP_SIZE 0x4000000 +#define ARCH_INIT_PAGE_SIZE 0x200000 +/* end of AArch64 Architecture Configuration */ +#define ARCH_CPU_64BIT +#define RT_USING_CACHE +#define RT_USING_CPU_FFS +#define ARCH_MM_MMU +#define ARCH_ARM +#define ARCH_ARM_MMU +#define ARCH_ARMV8 +#define ARCH_USING_ASID +#define ARCH_USING_HW_THREAD_SELF +#define ARCH_USING_IRQ_CTX_LIST + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 8192 +#define RT_MAIN_THREAD_PRIORITY 10 +#define RT_USING_MSH +#define RT_USING_FINSH +#define FINSH_USING_MSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 16384 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 +#define FINSH_USING_OPTION_COMPLETION + +/* DFS: device virtual file system */ + +#define RT_USING_DFS +#define DFS_USING_POSIX +#define DFS_USING_WORKDIR +#define DFS_FD_MAX 16 +#define RT_USING_DFS_V1 +#define DFS_FILESYSTEMS_MAX 4 +#define DFS_FILESYSTEM_TYPES_MAX 4 +#define RT_USING_DFS_ELMFAT + +/* elm-chan's FatFs, Generic FAT Filesystem Module */ + +#define RT_DFS_ELM_CODE_PAGE 437 +#define RT_DFS_ELM_WORD_ACCESS +#define RT_DFS_ELM_USE_LFN_3 +#define RT_DFS_ELM_USE_LFN 3 +#define RT_DFS_ELM_LFN_UNICODE_0 +#define RT_DFS_ELM_LFN_UNICODE 0 +#define RT_DFS_ELM_MAX_LFN 255 +#define RT_DFS_ELM_DRIVES 2 +#define RT_DFS_ELM_MAX_SECTOR_SIZE 512 +#define RT_DFS_ELM_REENTRANT +#define RT_DFS_ELM_MUTEX_TIMEOUT 3000 +/* end of elm-chan's FatFs, Generic FAT Filesystem Module */ +#define RT_USING_DFS_DEVFS +/* end of DFS: device virtual file system */ + +/* Device Drivers */ + +#define RT_USING_DM +#define RT_USING_DEV_BUS +#define RT_USING_DEVICE_IPC +#define RT_UNAMED_PIPE_NUMBER 64 +#define RT_USING_SYSTEM_WORKQUEUE +#define RT_SYSTEM_WORKQUEUE_STACKSIZE 8192 +#define RT_SYSTEM_WORKQUEUE_PRIORITY 23 +#define RT_USING_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_RB_BUFSZ 256 +#define RT_SERIAL_8250 +#define RT_SERIAL_8250_DW +#define RT_USING_ADC +#define RT_USING_ADC_V1 +#define RT_ADC_SUNXI_GPADC +#define RT_INPUT_SUNXI_LRADC +#define RT_USING_CLOCK_TIME +#define RT_CLOCK_TIME_ARM_ARCH +#define RT_USING_I2C +#define RT_USING_I2C_BITOPS +#define RT_I2C_SUNXI +#define RT_USING_PWM +#define RT_PWM_SUNXI +#define RT_USING_INPUT_CAPTURE +#define RT_INPUT_CAPTURE_RB_SIZE 100 +#define RT_INPUT_CAPTURE_SUNXI_IRRX +#define RT_USING_MTD_NOR +#define RT_USING_MTD_NOR_SPI +#define RT_USING_RTC +#define RT_USING_ALARM +#define RT_ALARM_STACK_SIZE 4096 +#define RT_ALARM_TIMESLICE 5 +#define RT_ALARM_PRIORITY 10 +#define RT_RTC_SUNXI +#define RT_USING_SDIO +#define RT_SDIO_STACK_SIZE 8192 +#define RT_SDIO_THREAD_PRIORITY 15 +#define RT_MMCSD_STACK_SIZE 8192 +#define RT_MMCSD_THREAD_PRIORITY 22 +#define RT_MMCSD_MAX_PARTITION 16 +#define RT_SDIO_SUNXI +#define RT_USING_SPI +#define RT_USING_SPI_ISR +#define RT_USING_SFUD +#define RT_SFUD_USING_SFDP +#define RT_SFUD_USING_FLASH_INFO_TABLE +#define RT_SFUD_SPI_MAX_HZ 50000000 +#define RT_SPI_SUNXI +#define RT_USING_WDT +#define RT_WDT_SUNXI +#define RT_USING_AUDIO +#define RT_AUDIO_REPLAY_MP_BLOCK_SIZE 4096 +#define RT_AUDIO_REPLAY_MP_BLOCK_COUNT 2 +#define RT_AUDIO_RECORD_PIPE_SIZE 2048 +#define RT_AUDIO_SUN50I_H616_CODEC +#define RT_AUDIO_SUN50I_H616_AHUB +#define RT_USING_LCD +#define RT_USING_GRAPHIC +#define RT_GRAPHIC_FB +#define RT_GRAPHIC_FB_SIMPLE +#define RT_GRAPHIC_FB_SUN50I_H616_DE33 +#define RT_GRAPHIC_LOGO +#define RT_GRAPHIC_LOGO_RT_THREAD_CLUT224 +#define RT_USING_HWCRYPTO +#define RT_HWCRYPTO_DEFAULT_NAME "hwcryto" +#define RT_HWCRYPTO_IV_MAX_SIZE 16 +#define RT_HWCRYPTO_KEYBIT_MAX_SIZE 256 +#define RT_HWCRYPTO_USING_AES +#define RT_HWCRYPTO_USING_AES_ECB +#define RT_HWCRYPTO_USING_AES_CBC +#define RT_HWCRYPTO_USING_RNG +#define RT_HWCRYPTO_SUN50I_H616_CE +#define RT_USING_LED +#define RT_LED_GPIO +#define RT_USING_INPUT +#define RT_INPUT_POWER +#define RT_USING_BLK + +/* Partition Types */ + +#define RT_BLK_PARTITION_DFS +#define RT_BLK_PARTITION_EFI +/* end of Partition Types */ +#define RT_USING_DVFS +#define RT_USING_DVFS_EVENT +#define RT_USING_DVFS_OPP_RETRY_MAX 10 + +/* DVFS Event Drivers */ + +/* DVFS CPUfreq Drivers */ + +#define RT_DVFS_SUN50I_H616_CPUFREQ + +/* DVFS Devfreq Drivers */ + +#define RT_USING_REGULATOR +#define RT_REGULATOR_AXP313A +#define RT_REGULATOR_FIXED +#define RT_REGULATOR_GPIO +#define RT_USING_RESET + +/* Power Management (PM) Domains device drivers */ + +/* end of Power Management (PM) Domains device drivers */ +#define RT_USING_THERMAL + +/* Thermal Sensors Drivers */ + +#define RT_THERMAL_SUN50I_H616 + +/* Thermal Cool Drivers */ + +#define RT_THERMAL_COOL_DVFS +#define RT_USING_NVMEM +#define RT_NVMEM_SUNXI_SID +#define RT_USING_DMA +#define RT_DMA_SUNXI +#define RT_USING_MFD +#define RT_MFD_SYSCON +#define RT_USING_OFW +#define RT_FDT_EARLYCON_MSG_SIZE 128 +#define RT_USING_OFW_BUS_RANGES_NUMBER 8 +#define RT_USING_PIC +#define MAX_HANDLERS 256 +#define RT_PIC_ARM_GIC +#define RT_PIC_ARM_GIC_MAX_NR 1 +#define RT_USING_PIN +#define RT_USING_PINCTRL +#define RT_PINCTRL_SUN50I_H616 +#define RT_USING_CLK +#define RT_CLK_SUN50I_H616 + +/* SoC (System on Chip) Drivers */ + +#define RT_SOC_SUNXI_G2D +#define RT_SOC_SUNXI_JPEG_DEC +/* end of SoC (System on Chip) Drivers */ +/* end of Device Drivers */ + +/* C/C++ and POSIX layer */ + +/* ISO-ANSI C layer */ + +/* Timezone and Daylight Saving Time */ + +#define RT_LIBC_USING_LIGHT_TZ_DST +#define RT_LIBC_TZ_DEFAULT_HOUR 8 +#define RT_LIBC_TZ_DEFAULT_MIN 0 +#define RT_LIBC_TZ_DEFAULT_SEC 0 +/* end of Timezone and Daylight Saving Time */ +/* end of ISO-ANSI C layer */ + +/* POSIX (Portable Operating System Interface) layer */ + + +/* Interprocess Communication (IPC) */ + + +/* Socket is in the 'Network' category */ + +/* end of Interprocess Communication (IPC) */ +/* end of POSIX (Portable Operating System Interface) layer */ +/* end of C/C++ and POSIX layer */ + +/* Network */ + +/* end of Network */ + +/* Memory protection */ + +/* end of Memory protection */ + +/* Utilities */ + +#define RT_USING_ADT +#define RT_USING_ADT_AVL +#define RT_USING_ADT_BITMAP +#define RT_USING_ADT_HASHMAP +#define RT_USING_ADT_REF +/* end of Utilities */ + +/* Memory management */ + +#define RT_PAGE_AFFINITY_BLOCK_SIZE 0x1000 +#define RT_PAGE_MAX_ORDER 11 +#define RT_USING_MEMBLOCK +#define RT_INIT_MEMORY_REGIONS 128 + +/* Debugging */ + +/* end of Debugging */ +/* end of Memory management */ + +/* Using USB legacy version */ + +/* end of Using USB legacy version */ +/* end of RT-Thread Components */ + +/* RT-Thread Utestcases */ + +/* end of RT-Thread Utestcases */ +#define SOC_H616 + +#endif diff --git a/bsp/allwinner/h616/rtconfig.py b/bsp/allwinner/h616/rtconfig.py new file mode 100644 index 000000000000..652452a44792 --- /dev/null +++ b/bsp/allwinner/h616/rtconfig.py @@ -0,0 +1,75 @@ +import os +import subprocess + +# toolchains options +ARCH ='aarch64' +CPU ='cortex-a' +CROSS_TOOL ='gcc' + +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = os.path.join(os.getcwd(), '..', '..', '..') + +if os.getenv('RTT_CC'): + CROSS_TOOL = os.getenv('RTT_CC') + +PLATFORM = 'gcc' +EXEC_PATH = r'/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-elf/bin/' + +EXEC_PATH = os.getenv('RTT_EXEC_PATH') or '/usr/bin' + +BUILD = 'debug' + +if PLATFORM == 'gcc': + # toolchains + PREFIX = os.getenv('RTT_CC_PREFIX') or 'aarch64-none-elf-' + CC = PREFIX + 'gcc' + CXX = PREFIX + 'g++' + CPP = PREFIX + 'cpp' + AS = PREFIX + 'gcc' + AR = PREFIX + 'ar' + LINK = PREFIX + 'gcc' + TARGET_EXT = 'elf' + SIZE = PREFIX + 'size' + OBJDUMP = PREFIX + 'objdump' + OBJCPY = PREFIX + 'objcopy' + + def _ld_option_supported(ld_path, option): + try: + return subprocess.call([ld_path, option], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) == 0 + except OSError: + return False + + _ld_path = os.path.join(EXEC_PATH, PREFIX + 'ld') + _ldflags_rwx = ' -Wl,--no-warn-rwx-segments' if _ld_option_supported(_ld_path, '--no-warn-rwx-segments') else '' + + DEVICE = ' -g -mcpu=cortex-a53 -fdiagnostics-color=always' + CPPFLAGS= ' -nostdinc -undef -E -P -x assembler-with-cpp' + CFLAGS = DEVICE + ' -Wall -Wno-cpp' + AFLAGS = ' -c' + ' -x assembler-with-cpp -D__ASSEMBLY__' + LFLAGS = DEVICE + ' -nostartfiles' + _ldflags_rwx + ' -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors -T link.lds' + CPATH = '' + LPATH = '' + + if BUILD == 'debug': + CFLAGS += ' -O0 -ggdb' + CPPFLAGS += ' -O0 -ggdb' + AFLAGS += ' -ggdb' + else: + CFLAGS += ' -O2 -ggdb' + CPPFLAGS += ' -O2 -ggdb' + + CXXFLAGS = CFLAGS + +DUMP_ACTION = OBJDUMP + ' -D -S $TARGET > rtt.asm\n' +POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' + SIZE + ' $TARGET \n' +POST_ACTION += 'md5sum rtthread.bin\n' + +def dist_handle(BSP_ROOT, dist_dir): + import sys + sys.path.append(os.path.join(os.path.dirname(BSP_ROOT), 'tools')) + from sdk_dist import dist_do_building + dist_do_building(BSP_ROOT, dist_dir) diff --git a/components/drivers/regulator/Kconfig b/components/drivers/regulator/Kconfig index f2d2f2a53066..f528aef973ec 100644 --- a/components/drivers/regulator/Kconfig +++ b/components/drivers/regulator/Kconfig @@ -10,6 +10,14 @@ config RT_REGULATOR_FAN53555 depends on RT_USING_DM default n +config RT_REGULATOR_AXP313A + bool "X-Powers AXP313A / AXP1530 regulators" + depends on RT_USING_REGULATOR + depends on RT_USING_I2C + depends on RT_USING_DM + depends on RT_USING_OFW + default n + config RT_REGULATOR_FIXED bool "Fixed regulator support" depends on RT_USING_REGULATOR diff --git a/components/drivers/regulator/SConscript b/components/drivers/regulator/SConscript index 9d5e0fbb18fe..e00452a3126c 100755 --- a/components/drivers/regulator/SConscript +++ b/components/drivers/regulator/SConscript @@ -10,6 +10,9 @@ CPPPATH = [cwd + '/../include'] src = ['regulator.c', 'regulator_dm.c', 'regulator_cmd.c'] +if GetDepend(['RT_REGULATOR_AXP313A']): + src += ['regulator-axp313a.c'] + if GetDepend(['RT_REGULATOR_FAN53555']): src += ['regulator-fan53555.c'] diff --git a/components/drivers/regulator/regulator-axp313a.c b/components/drivers/regulator/regulator-axp313a.c new file mode 100644 index 000000000000..bd9294a7cffe --- /dev/null +++ b/components/drivers/regulator/regulator-axp313a.c @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * 2026-10-23 GuEe-GUI first version + */ + +#define DBG_TAG "regulator.axp313a" +#define DBG_LVL DBG_INFO +#include + +#include "regulator_dm.h" + +#define AXP313A_VERSION 0x03 +#define AXP313A_OUTPUT_CONTROL 0x10 +#define AXP313A_DCDC1_CONTROL 0x13 +#define AXP313A_DCDC2_CONTROL 0x14 +#define AXP313A_DCDC3_CONTROL 0x15 +#define AXP313A_ALDO1_CONTROL 0x16 +#define AXP313A_DLDO1_CONTROL 0x17 + +#define AXP313A_CHIP_ID_MASK 0xcf +#define AXP1530_CHIP_ID 0x48 +#define AXP313A_CHIP_ID 0x4b + +#define AXP313A_DCDC_VOLTAGE_MASK 0x7f +#define AXP313A_LDO_VOLTAGE_MASK 0x1f + +enum axp313a_rail_id +{ + AXP313A_DCDC1, + AXP313A_DCDC2, + AXP313A_DCDC3, + AXP313A_ALDO1, + AXP313A_DLDO1, + AXP313A_RAIL_COUNT, +}; + +enum axp313a_voltage_range +{ + AXP313A_VOLTAGE_DCDC1, + AXP313A_VOLTAGE_DCDC23, + AXP313A_VOLTAGE_DCDC3, + AXP313A_VOLTAGE_LDO, +}; + +struct axp313a_rail_info +{ + const char *name; + const char *legacy_name; + rt_uint8_t voltage_reg; + rt_uint8_t voltage_mask; + rt_uint8_t enable_mask; + rt_uint8_t max_selector; + enum axp313a_voltage_range voltage_range; +}; + +/* + * DCDC3's 2.2 V to 2.5 V range follows the vendor BSP and Linux driver; + * it differs from the AXP313A datasheet but has been verified in hardware. + */ +static const struct axp313a_rail_info axp313a_rails[] = +{ + [AXP313A_DCDC1] = + { + .name = "dcdc1", + .voltage_reg = AXP313A_DCDC1_CONTROL, + .voltage_mask = AXP313A_DCDC_VOLTAGE_MASK, + .enable_mask = RT_BIT(0), + .max_selector = 106, + .voltage_range = AXP313A_VOLTAGE_DCDC1, + }, + [AXP313A_DCDC2] = + { + .name = "dcdc2", + .voltage_reg = AXP313A_DCDC2_CONTROL, + .voltage_mask = AXP313A_DCDC_VOLTAGE_MASK, + .enable_mask = RT_BIT(1), + .max_selector = 87, + .voltage_range = AXP313A_VOLTAGE_DCDC23, + }, + [AXP313A_DCDC3] = + { + .name = "dcdc3", + .voltage_reg = AXP313A_DCDC3_CONTROL, + .voltage_mask = AXP313A_DCDC_VOLTAGE_MASK, + .enable_mask = RT_BIT(2), + .max_selector = 102, + .voltage_range = AXP313A_VOLTAGE_DCDC3, + }, + [AXP313A_ALDO1] = + { + .name = "aldo1", + .legacy_name = "ldo1", + .voltage_reg = AXP313A_ALDO1_CONTROL, + .voltage_mask = AXP313A_LDO_VOLTAGE_MASK, + .enable_mask = RT_BIT(3), + .max_selector = 30, + .voltage_range = AXP313A_VOLTAGE_LDO, + }, + [AXP313A_DLDO1] = + { + .name = "dldo1", + .legacy_name = "ldo2", + .voltage_reg = AXP313A_DLDO1_CONTROL, + .voltage_mask = AXP313A_LDO_VOLTAGE_MASK, + .enable_mask = RT_BIT(4), + .max_selector = 30, + .voltage_range = AXP313A_VOLTAGE_LDO, + }, +}; + +struct axp313a +{ + struct rt_i2c_client *client; + struct rt_mutex lock; +}; + +struct axp313a_rail +{ + struct rt_regulator_node regulator; + struct rt_regulator_param param; + struct axp313a *pmic; + const struct axp313a_rail_info *info; +}; + +static rt_err_t axp313a_read_unlocked(struct axp313a *pmic, rt_uint8_t reg, + rt_uint8_t *value) +{ + int ret; + struct rt_i2c_msg msgs[] = + { + { + .addr = pmic->client->client_addr, + .flags = RT_I2C_WR, + .buf = ®, + .len = sizeof(reg), + }, + { + .addr = pmic->client->client_addr, + .flags = RT_I2C_RD, + .buf = value, + .len = sizeof(*value), + }, + }; + + ret = rt_i2c_transfer(pmic->client->bus, msgs, RT_ARRAY_SIZE(msgs)); + + return ret == RT_ARRAY_SIZE(msgs) ? RT_EOK : + ret < 0 ? (rt_err_t)ret : -RT_EIO; +} + +static rt_err_t axp313a_write_unlocked(struct axp313a *pmic, rt_uint8_t reg, + rt_uint8_t value) +{ + int ret; + rt_uint8_t values[] = { reg, value }; + struct rt_i2c_msg msg = + { + .addr = pmic->client->client_addr, + .flags = RT_I2C_WR, + .buf = values, + .len = sizeof(values), + }; + + ret = rt_i2c_transfer(pmic->client->bus, &msg, 1); + + return ret == 1 ? RT_EOK : ret < 0 ? (rt_err_t)ret : -RT_EIO; +} + +static rt_err_t axp313a_read(struct axp313a *pmic, rt_uint8_t reg, + rt_uint8_t *value) +{ + rt_err_t err; + + rt_mutex_take(&pmic->lock, RT_WAITING_FOREVER); + err = axp313a_read_unlocked(pmic, reg, value); + rt_mutex_release(&pmic->lock); + + return err; +} + +static rt_err_t axp313a_update_bits(struct axp313a *pmic, rt_uint8_t reg, + rt_uint8_t mask, rt_uint8_t value) +{ + rt_err_t err; + rt_uint8_t old; + rt_uint8_t updated; + + rt_mutex_take(&pmic->lock, RT_WAITING_FOREVER); + + err = axp313a_read_unlocked(pmic, reg, &old); + if (!err) + { + updated = (old & ~mask) | (value & mask); + + if (updated != old) + { + err = axp313a_write_unlocked(pmic, reg, updated); + } + } + + rt_mutex_release(&pmic->lock); + + return err; +} + +static int axp313a_list_voltage(const struct axp313a_rail_info *info, + unsigned int selector) +{ + if (selector > info->max_selector) + { + return -RT_EINVAL; + } + + switch (info->voltage_range) + { + case AXP313A_VOLTAGE_DCDC1: + if (selector >= 88) + { + return 1600000 + (selector - 88) * 100000; + } + /* fall through */ + case AXP313A_VOLTAGE_DCDC23: + case AXP313A_VOLTAGE_DCDC3: + return selector <= 70 ? + 500000 + selector * 10000 : + 1220000 + (selector - 71) * 20000; + + case AXP313A_VOLTAGE_LDO: + return 500000 + selector * 100000; + } + + return -RT_EINVAL; +} + +static rt_err_t axp313a_enable(struct rt_regulator_node *node) +{ + struct axp313a_rail *rail = node->priv; + + return axp313a_update_bits(rail->pmic, AXP313A_OUTPUT_CONTROL, + rail->info->enable_mask, rail->info->enable_mask); +} + +static rt_err_t axp313a_disable(struct rt_regulator_node *node) +{ + struct axp313a_rail *rail = node->priv; + + return axp313a_update_bits(rail->pmic, AXP313A_OUTPUT_CONTROL, + rail->info->enable_mask, 0); +} + +static rt_bool_t axp313a_is_enabled(struct rt_regulator_node *node) +{ + struct axp313a_rail *rail = node->priv; + rt_uint8_t value; + + return !axp313a_read(rail->pmic, AXP313A_OUTPUT_CONTROL, &value) && + !!(value & rail->info->enable_mask); +} + +static int axp313a_get_voltage(struct rt_regulator_node *node) +{ + struct axp313a_rail *rail = node->priv; + rt_uint8_t value; + rt_err_t err; + + err = axp313a_read(rail->pmic, rail->info->voltage_reg, &value); + + return err ? err : axp313a_list_voltage(rail->info, + value & rail->info->voltage_mask); +} + +static rt_err_t axp313a_set_voltage(struct rt_regulator_node *node, + int min_uvolt, int max_uvolt) +{ + struct axp313a_rail *rail = node->priv; + unsigned int selector; + int voltage; + + if (min_uvolt > max_uvolt) + { + return -RT_EINVAL; + } + + for (selector = 0; selector <= rail->info->max_selector; ++selector) + { + voltage = axp313a_list_voltage(rail->info, selector); + + if (voltage >= min_uvolt && voltage <= max_uvolt) + { + return axp313a_update_bits(rail->pmic, rail->info->voltage_reg, + rail->info->voltage_mask, selector); + } + } + + return -RT_EINVAL; +} + +static const struct rt_regulator_ops axp313a_ops = +{ + .enable = axp313a_enable, + .disable = axp313a_disable, + .is_enabled = axp313a_is_enabled, + .set_voltage = axp313a_set_voltage, + .get_voltage = axp313a_get_voltage, +}; + +static rt_err_t axp313a_rail_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + rt_uint8_t output; + struct axp313a_rail *rail; + + if (pdev->dev_id >= AXP313A_RAIL_COUNT) + { + return -RT_EINVAL; + } + + rail = rt_calloc(1, sizeof(*rail)); + if (!rail) + { + return -RT_ENOMEM; + } + + rail->pmic = pdev->priv; + rail->info = &axp313a_rails[pdev->dev_id]; + + if ((err = regulator_ofw_parse(pdev->parent.ofw_node, &rail->param))) + { + goto _fail; + } + + if (!rail->param.name) + { + rail->param.name = ((struct rt_ofw_node *)pdev->parent.ofw_node)->name; + } + + rail->regulator.dev = &pdev->parent; + rail->regulator.priv = rail; + rail->regulator.param = &rail->param; + rail->regulator.supply_name = rail->param.name; + rail->regulator.ops = &axp313a_ops; + + /* Ensure I2C access is working without modifying firmware settings. */ + if ((err = axp313a_read(rail->pmic, AXP313A_OUTPUT_CONTROL, &output))) + { + goto _fail; + } + + if ((rail->param.always_on || rail->param.boot_on) && + !(output & rail->info->enable_mask)) + { + if ((err = axp313a_enable(&rail->regulator))) + { + goto _fail; + } + } + + if ((err = rt_regulator_register(&rail->regulator))) + { + goto _fail; + } + + pdev->parent.user_data = rail; + + return RT_EOK; + +_fail: + rt_free(rail); + + return err; +} + +static rt_err_t axp313a_rail_remove(struct rt_platform_device *pdev) +{ + struct axp313a_rail *rail = pdev->parent.user_data; + rt_err_t err; + + if (!rail) + { + return RT_EOK; + } + + err = rt_regulator_unregister(&rail->regulator); + if (err) + { + return err; + } + + rt_ofw_data(pdev->parent.ofw_node) = RT_NULL; + pdev->parent.user_data = RT_NULL; + rt_free(rail); + + return RT_EOK; +} + +static struct rt_platform_driver axp313a_rail_driver = +{ + .name = "axp313a-regulator", + .probe = axp313a_rail_probe, + .remove = axp313a_rail_remove, +}; + +static void axp313a_free_children(struct rt_platform_device **children) +{ + unsigned int index; + + for (index = 0; index < AXP313A_RAIL_COUNT; ++index) + { + if (children[index]) + { + rt_ofw_node_put(children[index]->parent.ofw_node); + rt_free(children[index]); + } + } +} + +static rt_err_t axp313a_probe(struct rt_i2c_client *client) +{ + rt_err_t err; + rt_uint8_t chip_id; + unsigned int index; + struct axp313a *pmic; + struct rt_ofw_node *regulators; + struct rt_platform_device *children[AXP313A_RAIL_COUNT] = { 0 }; + + pmic = rt_calloc(1, sizeof(*pmic)); + if (!pmic) + { + return -RT_ENOMEM; + } + + pmic->client = client; + if ((err = rt_mutex_init(&pmic->lock, "axp313", RT_IPC_FLAG_PRIO))) + { + goto _free_pmic; + } + + if ((err = axp313a_read(pmic, AXP313A_VERSION, &chip_id))) + { + goto _detach_lock; + } + + chip_id &= AXP313A_CHIP_ID_MASK; + if (chip_id != AXP1530_CHIP_ID && chip_id != AXP313A_CHIP_ID) + { + err = -RT_EINVAL; + goto _detach_lock; + } + + regulators = rt_ofw_get_child_by_tag(client->parent.ofw_node, "regulators"); + if (!regulators) + { + err = -RT_EINVAL; + goto _detach_lock; + } + + for (index = 0; index < AXP313A_RAIL_COUNT; ++index) + { + const struct axp313a_rail_info *info = &axp313a_rails[index]; + struct rt_ofw_node *node; + + node = rt_ofw_get_child_by_tag(regulators, info->name); + if (!node && info->legacy_name) + { + node = rt_ofw_get_child_by_tag(regulators, info->legacy_name); + } + + if (!node) + { + continue; + } + + if (!rt_ofw_node_is_available(node)) + { + rt_ofw_node_put(node); + continue; + } + + children[index] = rt_platform_device_alloc("axp313a-regulator"); + if (!children[index]) + { + rt_ofw_node_put(node); + err = -RT_ENOMEM; + goto _put_regulators; + } + + children[index]->dev_id = index; + children[index]->priv = pmic; + children[index]->parent.ofw_node = node; + } + + rt_ofw_node_put(regulators); + client->parent.user_data = pmic; + + for (index = 0; index < AXP313A_RAIL_COUNT; ++index) + { + struct rt_ofw_node *node; + struct rt_platform_device *child = children[index]; + + if (!child) + { + continue; + } + + node = child->parent.ofw_node; + node->dev = &child->parent; + + err = rt_platform_device_register(child); + if (err) + { + LOG_E("%s registration failed: %s", axp313a_rails[index].name, rt_strerror(err)); + } + else if (!child->parent.drv) + { + LOG_E("%s is not available: regulator probe failed", axp313a_rails[index].name); + } + } + + LOG_D("PMIC ID %02x, preserving firmware voltage settings", chip_id); + + return RT_EOK; + +_put_regulators: + rt_ofw_node_put(regulators); + axp313a_free_children(children); + +_detach_lock: + rt_mutex_detach(&pmic->lock); + +_free_pmic: + rt_free(pmic); + + return err; +} + +static rt_err_t axp313a_remove(struct rt_i2c_client *client) +{ + /* The PMIC supplies the running system and owns permanent child devices. */ + return -RT_EBUSY; +} + +static const struct rt_ofw_node_id axp313a_ids[] = +{ + { .compatible = "x-powers,axp313a" }, + { .compatible = "x-powers,axp1530" }, + { /* sentinel */ }, +}; + +static struct rt_i2c_driver axp313a_driver = +{ + .ofw_ids = axp313a_ids, + .probe = axp313a_probe, + .remove = axp313a_remove, +}; + +static int axp313a_register(void) +{ + rt_platform_driver_register(&axp313a_rail_driver); + rt_i2c_driver_register(&axp313a_driver); + + return 0; +} +INIT_SUBSYS_EXPORT(axp313a_register); diff --git a/components/drivers/serial/device/8250/8250-dw.c b/components/drivers/serial/device/8250/8250-dw.c index 5ea1b7742d26..e9d151a0c126 100644 --- a/components/drivers/serial/device/8250/8250-dw.c +++ b/components/drivers/serial/device/8250/8250-dw.c @@ -222,8 +222,15 @@ static rt_err_t dw8250_probe(struct rt_platform_device *pdev) goto _free_res; } - serial->clk = rt_clk_get_by_name(dev, "baudclk"); - dw8250->pclk = rt_clk_get_by_name(dev, "apb_pclk"); + if (rt_dm_dev_prop_read_bool(dev, "clock-names")) + { + serial->clk = rt_clk_get_by_name(dev, "baudclk"); + dw8250->pclk = rt_clk_get_by_name(dev, "apb_pclk"); + } + else + { + serial->clk = rt_clk_get_by_index(dev, 0); + } if (rt_is_err_or_null(serial->clk)) {