From 3dcb444cdfbb4e6bd591e57c5ada2e512f6ff1ef Mon Sep 17 00:00:00 2001 From: Patrick Hennig Date: Thu, 6 Aug 2026 19:50:53 +0200 Subject: [PATCH 01/24] linux: build regulatory.db into the kernel - pre_make_target(): copy regulatory.db + regulatory.db.p7s from wireless-regdb into external-firmware/ and append both to CONFIG_EXTRA_FIRMWARE, gated on CONFIG_CFG80211=y. - add wireless-regdb to PKG_DEPENDS_UNPACK under the same gate. --- projects/ROCKNIX/packages/linux/package.mk | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/projects/ROCKNIX/packages/linux/package.mk b/projects/ROCKNIX/packages/linux/package.mk index 86675fe567..79786c3dd6 100644 --- a/projects/ROCKNIX/packages/linux/package.mk +++ b/projects/ROCKNIX/packages/linux/package.mk @@ -95,6 +95,12 @@ if [ "${DEVICE}" = "SM8650" -o "${DEVICE}" = "SM8750" ]; then PKG_DEPENDS_UNPACK+=" extra-firmware" fi +# A built-in cfg80211 needs regulatory.db inside the kernel image (see +# pre_make_target), so unpack wireless-regdb before the kernel is built. +if grep -q '^CONFIG_CFG80211=y' ${PKG_KERNEL_CFG_FILE}; then + PKG_DEPENDS_UNPACK+=" wireless-regdb" +fi + post_patch() { # linux was already built and its build dir autoremoved - prepare it again for kernel packages if [ -d ${PKG_INSTALL}/.image ]; then @@ -281,6 +287,27 @@ pre_make_target() { ${PKG_BUILD}/scripts/config --set-str CONFIG_EXTRA_FIRMWARE_DIR "external-firmware" fi + # cfg80211 requests regulatory.db as soon as it initialises. Built in (=y on + # every device but AMD64) that is during kernel init, while /usr/lib/firmware + # is still a dangling symlink to the kernel-overlay tmpfs that + # kernel-overlays-setup only populates later (scripts/image) - so the request + # fails with -ENOENT, the failure is cached, and the radio stays in domain 00 + # with 5 GHz no-IR. Build the db into the kernel instead. The .p7s signature + # goes with it because CONFIG_CFG80211_REQUIRE_SIGNED_REGDB is set. Appends to + # whatever the per-device blocks above already listed (~7 KB in the image). + if grep -q '^CONFIG_CFG80211=y' ${PKG_BUILD}/.config; then + mkdir -p ${PKG_BUILD}/external-firmware + cp -Lv $(get_build_dir wireless-regdb)/regulatory.db ${PKG_BUILD}/external-firmware + cp -Lv $(get_build_dir wireless-regdb)/regulatory.db.p7s ${PKG_BUILD}/external-firmware + + FW_LIST="$(${PKG_BUILD}/scripts/config --state CONFIG_EXTRA_FIRMWARE)" + [ "${FW_LIST}" = "undef" ] && FW_LIST="" + + ${PKG_BUILD}/scripts/config --set-str CONFIG_EXTRA_FIRMWARE \ + "$(echo ${FW_LIST} regulatory.db regulatory.db.p7s | xargs)" + ${PKG_BUILD}/scripts/config --set-str CONFIG_EXTRA_FIRMWARE_DIR "external-firmware" + fi + kernel_make listnewconfig if [ "${INTERACTIVE_CONFIG}" = "yes" ]; then # manually answer .config changes From ef264a238d5e2ba960145e3fda663dc27de49a80 Mon Sep 17 00:00:00 2001 From: sunshineinabox Date: Sun, 9 Aug 2026 11:47:22 -0700 Subject: [PATCH 02/24] sm8550/sm8750: fix gx clock issues --- ..._cc_power_requirements_reality_check.patch | 9 +-- ...com-rpmhpd-no-max-clamp-on-gmu-rails.patch | 26 --------- ...-qcom-rpmhpd-presync-floor-gmu-rails.patch | 56 +++++++++++++++++++ ...com-rpmhpd-no-max-clamp-on-gmu-rails.patch | 26 --------- ...-qcom-rpmhpd-presync-floor-gmu-rails.patch | 56 +++++++++++++++++++ 5 files changed, 117 insertions(+), 56 deletions(-) delete mode 100644 projects/ROCKNIX/devices/SM8550/patches/linux/0121-pmdomain-qcom-rpmhpd-no-max-clamp-on-gmu-rails.patch create mode 100644 projects/ROCKNIX/devices/SM8550/patches/linux/0121-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch delete mode 100644 projects/ROCKNIX/devices/SM8750/patches/linux/0052-pmdomain-qcom-rpmhpd-no-max-clamp-on-gmu-rails.patch create mode 100644 projects/ROCKNIX/devices/SM8750/patches/linux/0052-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0120-20250728_konradybcio_gpu_cc_power_requirements_reality_check.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0120-20250728_konradybcio_gpu_cc_power_requirements_reality_check.patch index 4f96196809..2d34203cd4 100644 --- a/projects/ROCKNIX/devices/SM8550/patches/linux/0120-20250728_konradybcio_gpu_cc_power_requirements_reality_check.patch +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/0120-20250728_konradybcio_gpu_cc_power_requirements_reality_check.patch @@ -13,22 +13,23 @@ function. Ensure that's conveyed to the OS. Fixes: 9f7579423d2d ("arm64: dts: qcom: sm8550: Add graphics clock controller") Signed-off-by: Konrad Dybcio +[Modified 8/9/2026 Rocknix: drop RPMHPD_GFX, GX rail is GMU owned confirmed in +downstream kernel.] --- - arch/arm64/boot/dts/qcom/sm8550.dtsi | 6 ++++++ - 1 file changed, 6 insertions(+) + arch/arm64/boot/dts/qcom/sm8550.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi index 45713d46f3c52487d2638b7ab194c111f58679ce..28eade49526dc9bb0a7b211f96dd350873489029 100644 --- a/arch/arm64/boot/dts/qcom/sm8550.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi -@@ -2572,6 +2572,12 @@ gpucc: clock-controller@3d90000 { +@@ -2572,6 +2572,11 @@ gpucc: clock-controller@3d90000 { clocks = <&bi_tcxo_div2>, <&gcc GCC_GPU_GPLL0_CLK_SRC>, <&gcc GCC_GPU_GPLL0_DIV_CLK_SRC>; + + power-domains = <&rpmhpd RPMHPD_CX>, + <&rpmhpd RPMHPD_MX>, -+ <&rpmhpd RPMHPD_GFX>, + <&rpmhpd RPMHPD_MXC>; + #clock-cells = <1>; diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0121-pmdomain-qcom-rpmhpd-no-max-clamp-on-gmu-rails.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0121-pmdomain-qcom-rpmhpd-no-max-clamp-on-gmu-rails.patch deleted file mode 100644 index 8b37f6fee7..0000000000 --- a/projects/ROCKNIX/devices/SM8550/patches/linux/0121-pmdomain-qcom-rpmhpd-no-max-clamp-on-gmu-rails.patch +++ /dev/null @@ -1,26 +0,0 @@ -Subject: [PATCH] pmdomain: qcom: rpmhpd: don't clamp GMU-managed rails pre-sync_state - -Mark gfx/gmxc state_synced from boot so pre-sync votes use the real -lowest corner instead. These rails carry no bootloader state and -have no other consumers, so the clamp protects nothing on them. - ---- -diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c ---- a/drivers/pmdomain/qcom/rpmhpd.c -+++ b/drivers/pmdomain/qcom/rpmhpd.c -@@ -120,6 +120,7 @@ static struct rpmhpd gbx = { - static struct rpmhpd gfx = { - .pd = { .name = "gfx", }, - .res_name = "gfx.lvl", -+ .state_synced = true, - }; - - static struct rpmhpd lcx = { -@@ -225,6 +226,7 @@ static struct rpmhpd qphy = { - static struct rpmhpd gmxc = { - .pd = { .name = "gmxc", }, - .res_name = "gmxc.lvl", -+ .state_synced = true, - }; - - /* Eliza RPMH powerdomains */ diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0121-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0121-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch new file mode 100644 index 0000000000..ea9f020ea6 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/0121-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch @@ -0,0 +1,56 @@ +Subject: [PATCH] pmdomain: qcom: rpmhpd: floor GMU managed rails low pre sync_state + +Floor gfx/gmxc at their lowest functional corner until sync_state +instead: low enough for ACD to calibrate against real levels, alive +enough for the GMU's first ramp. Skip the retention level so the +floor is a corner logic can run at. + +--- +diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c +--- a/drivers/pmdomain/qcom/rpmhpd.c ++++ b/drivers/pmdomain/qcom/rpmhpd.c +@@ -42,6 +42,7 @@ + * cmd-db + * @state_synced: Indicator that sync_state has been invoked for the rpmhpd resource + * @skip_retention_level: Indicate that retention level should not be used for the power domain ++ * @presync_floor: Clamp to the lowest functional corner instead of the highest before sync_state + */ + struct rpmhpd { + struct device *dev; +@@ -59,6 +60,7 @@ struct rpmhpd { + u32 addr; + bool state_synced; + bool skip_retention_level; ++ bool presync_floor; + }; + + struct rpmhpd_desc { +@@ -120,6 +122,8 @@ static struct rpmhpd gbx = { + static struct rpmhpd gfx = { + .pd = { .name = "gfx", }, + .res_name = "gfx.lvl", ++ .presync_floor = true, ++ .skip_retention_level = true, + }; + + static struct rpmhpd lcx = { +@@ -225,6 +229,8 @@ static struct rpmhpd qphy = { + static struct rpmhpd gmxc = { + .pd = { .name = "gmxc", }, + .res_name = "gmxc.lvl", ++ .presync_floor = true, ++ .skip_retention_level = true, + }; + + /* Eliza RPMH powerdomains */ +@@ -936,6 +942,10 @@ static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner) + + if (pd->state_synced) { + to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner); ++ } else if (pd->presync_floor) { ++ /* Keep GMU managed rails alive but low until sync_state */ ++ to_active_sleep(pd, max(corner, pd->enable_corner), ++ &this_active_corner, &this_sleep_corner); + } else { + /* Clamp to highest corner if sync_state hasn't happened */ + this_active_corner = pd->level_count - 1; diff --git a/projects/ROCKNIX/devices/SM8750/patches/linux/0052-pmdomain-qcom-rpmhpd-no-max-clamp-on-gmu-rails.patch b/projects/ROCKNIX/devices/SM8750/patches/linux/0052-pmdomain-qcom-rpmhpd-no-max-clamp-on-gmu-rails.patch deleted file mode 100644 index 8b37f6fee7..0000000000 --- a/projects/ROCKNIX/devices/SM8750/patches/linux/0052-pmdomain-qcom-rpmhpd-no-max-clamp-on-gmu-rails.patch +++ /dev/null @@ -1,26 +0,0 @@ -Subject: [PATCH] pmdomain: qcom: rpmhpd: don't clamp GMU-managed rails pre-sync_state - -Mark gfx/gmxc state_synced from boot so pre-sync votes use the real -lowest corner instead. These rails carry no bootloader state and -have no other consumers, so the clamp protects nothing on them. - ---- -diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c ---- a/drivers/pmdomain/qcom/rpmhpd.c -+++ b/drivers/pmdomain/qcom/rpmhpd.c -@@ -120,6 +120,7 @@ static struct rpmhpd gbx = { - static struct rpmhpd gfx = { - .pd = { .name = "gfx", }, - .res_name = "gfx.lvl", -+ .state_synced = true, - }; - - static struct rpmhpd lcx = { -@@ -225,6 +226,7 @@ static struct rpmhpd qphy = { - static struct rpmhpd gmxc = { - .pd = { .name = "gmxc", }, - .res_name = "gmxc.lvl", -+ .state_synced = true, - }; - - /* Eliza RPMH powerdomains */ diff --git a/projects/ROCKNIX/devices/SM8750/patches/linux/0052-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch b/projects/ROCKNIX/devices/SM8750/patches/linux/0052-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch new file mode 100644 index 0000000000..ea9f020ea6 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8750/patches/linux/0052-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch @@ -0,0 +1,56 @@ +Subject: [PATCH] pmdomain: qcom: rpmhpd: floor GMU managed rails low pre sync_state + +Floor gfx/gmxc at their lowest functional corner until sync_state +instead: low enough for ACD to calibrate against real levels, alive +enough for the GMU's first ramp. Skip the retention level so the +floor is a corner logic can run at. + +--- +diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c +--- a/drivers/pmdomain/qcom/rpmhpd.c ++++ b/drivers/pmdomain/qcom/rpmhpd.c +@@ -42,6 +42,7 @@ + * cmd-db + * @state_synced: Indicator that sync_state has been invoked for the rpmhpd resource + * @skip_retention_level: Indicate that retention level should not be used for the power domain ++ * @presync_floor: Clamp to the lowest functional corner instead of the highest before sync_state + */ + struct rpmhpd { + struct device *dev; +@@ -59,6 +60,7 @@ struct rpmhpd { + u32 addr; + bool state_synced; + bool skip_retention_level; ++ bool presync_floor; + }; + + struct rpmhpd_desc { +@@ -120,6 +122,8 @@ static struct rpmhpd gbx = { + static struct rpmhpd gfx = { + .pd = { .name = "gfx", }, + .res_name = "gfx.lvl", ++ .presync_floor = true, ++ .skip_retention_level = true, + }; + + static struct rpmhpd lcx = { +@@ -225,6 +229,8 @@ static struct rpmhpd qphy = { + static struct rpmhpd gmxc = { + .pd = { .name = "gmxc", }, + .res_name = "gmxc.lvl", ++ .presync_floor = true, ++ .skip_retention_level = true, + }; + + /* Eliza RPMH powerdomains */ +@@ -936,6 +942,10 @@ static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner) + + if (pd->state_synced) { + to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner); ++ } else if (pd->presync_floor) { ++ /* Keep GMU managed rails alive but low until sync_state */ ++ to_active_sleep(pd, max(corner, pd->enable_corner), ++ &this_active_corner, &this_sleep_corner); + } else { + /* Clamp to highest corner if sync_state hasn't happened */ + this_active_corner = pd->level_count - 1; From 607e0933f638a6a7f9043c47e8ee00cd820d4abb Mon Sep 17 00:00:00 2001 From: sunshineinabox Date: Sun, 9 Aug 2026 13:08:53 -0700 Subject: [PATCH 03/24] sm8650: gx rail patches from sm8550 --- ...-dts-qcom-sm8650-gpucc-power-domains.patch | 35 ++++++++++++ ...-qcom-rpmhpd-presync-floor-gmu-rails.patch | 56 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 projects/ROCKNIX/devices/SM8650/patches/linux/0120-arm64-dts-qcom-sm8650-gpucc-power-domains.patch create mode 100644 projects/ROCKNIX/devices/SM8650/patches/linux/0121-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch diff --git a/projects/ROCKNIX/devices/SM8650/patches/linux/0120-arm64-dts-qcom-sm8650-gpucc-power-domains.patch b/projects/ROCKNIX/devices/SM8650/patches/linux/0120-arm64-dts-qcom-sm8650-gpucc-power-domains.patch new file mode 100644 index 0000000000..fcc6be1939 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8650/patches/linux/0120-arm64-dts-qcom-sm8650-gpucc-power-domains.patch @@ -0,0 +1,35 @@ +From git@z Thu Jan 1 00:00:00 1970 +Subject: [PATCH RFC 22/24] arm64: dts: qcom: sm8650: Describe GPU_CC power + plumbing requirements +From: Konrad Dybcio +Date: Mon, 28 Jul 2025 18:16:22 +0200 +Message-Id: <20250728-topic-gpucc_power_plumbing-v1-22-09c2480fe3e6@oss.qualcomm.com> +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +A number of power rails must be powered on in order for GPU_CC to +function. Ensure that's conveyed to the OS. + +Fixes: 9f7579423d2d ("arm64: dts: qcom: sm8650: Add graphics clock controller") +Signed-off-by: Konrad Dybcio +[Modified 8/9/2026 Rocknix: drop RPMHPD_GFX, GX rail is GMU owned confirmed in +downstream kernel. Same for sm8650 confirmed downstream.] + +Based on above patch for SM8550. + +--- +diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi +--- a/arch/arm64/boot/dts/qcom/sm8650.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi +@@ -4306,6 +4306,10 @@ + <&gcc GCC_GPU_GPLL0_CLK_SRC>, + <&gcc GCC_GPU_GPLL0_DIV_CLK_SRC>; + ++ power-domains = <&rpmhpd RPMHPD_CX>, ++ <&rpmhpd RPMHPD_MX>, ++ <&rpmhpd RPMHPD_MXC>; ++ + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; diff --git a/projects/ROCKNIX/devices/SM8650/patches/linux/0121-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch b/projects/ROCKNIX/devices/SM8650/patches/linux/0121-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch new file mode 100644 index 0000000000..ea9f020ea6 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8650/patches/linux/0121-pmdomain-qcom-rpmhpd-presync-floor-gmu-rails.patch @@ -0,0 +1,56 @@ +Subject: [PATCH] pmdomain: qcom: rpmhpd: floor GMU managed rails low pre sync_state + +Floor gfx/gmxc at their lowest functional corner until sync_state +instead: low enough for ACD to calibrate against real levels, alive +enough for the GMU's first ramp. Skip the retention level so the +floor is a corner logic can run at. + +--- +diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c +--- a/drivers/pmdomain/qcom/rpmhpd.c ++++ b/drivers/pmdomain/qcom/rpmhpd.c +@@ -42,6 +42,7 @@ + * cmd-db + * @state_synced: Indicator that sync_state has been invoked for the rpmhpd resource + * @skip_retention_level: Indicate that retention level should not be used for the power domain ++ * @presync_floor: Clamp to the lowest functional corner instead of the highest before sync_state + */ + struct rpmhpd { + struct device *dev; +@@ -59,6 +60,7 @@ struct rpmhpd { + u32 addr; + bool state_synced; + bool skip_retention_level; ++ bool presync_floor; + }; + + struct rpmhpd_desc { +@@ -120,6 +122,8 @@ static struct rpmhpd gbx = { + static struct rpmhpd gfx = { + .pd = { .name = "gfx", }, + .res_name = "gfx.lvl", ++ .presync_floor = true, ++ .skip_retention_level = true, + }; + + static struct rpmhpd lcx = { +@@ -225,6 +229,8 @@ static struct rpmhpd qphy = { + static struct rpmhpd gmxc = { + .pd = { .name = "gmxc", }, + .res_name = "gmxc.lvl", ++ .presync_floor = true, ++ .skip_retention_level = true, + }; + + /* Eliza RPMH powerdomains */ +@@ -936,6 +942,10 @@ static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner) + + if (pd->state_synced) { + to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner); ++ } else if (pd->presync_floor) { ++ /* Keep GMU managed rails alive but low until sync_state */ ++ to_active_sleep(pd, max(corner, pd->enable_corner), ++ &this_active_corner, &this_sleep_corner); + } else { + /* Clamp to highest corner if sync_state hasn't happened */ + this_active_corner = pd->level_count - 1; From 9e268fe04eb8a870ffa18c48cd7ea0b7b17001b7 Mon Sep 17 00:00:00 2001 From: sunshineinabox Date: Sun, 9 Aug 2026 16:04:14 -0700 Subject: [PATCH 04/24] sm8750: match values to stock firmware --- ...-arm64-dts-qcom-sm8750-add-GPU-nodes.patch | 87 +++++++++++++------ ...-drm-msm-a8xx-add-adreno-830-catalog.patch | 12 ++- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/projects/ROCKNIX/devices/SM8750/patches/linux/0038-arm64-dts-qcom-sm8750-add-GPU-nodes.patch b/projects/ROCKNIX/devices/SM8750/patches/linux/0038-arm64-dts-qcom-sm8750-add-GPU-nodes.patch index a349f71527..44b0b0b0cc 100644 --- a/projects/ROCKNIX/devices/SM8750/patches/linux/0038-arm64-dts-qcom-sm8750-add-GPU-nodes.patch +++ b/projects/ROCKNIX/devices/SM8750/patches/linux/0038-arm64-dts-qcom-sm8750-add-GPU-nodes.patch @@ -6,15 +6,17 @@ Subject: [PATCH 38/54] arm64: dts: qcom: sm8750: add GPU nodes Add GPU nodes for the SM8750 platform. Signed-off-by: Teguh Sobirin +[Modified 8/9/2026 by Rocknix: v2 ACD + bus tables per official Odin 3/KPFE and downstream.] --- - arch/arm64/boot/dts/qcom/sm8750.dtsi | 182 +++++++++++++++++++++++++++ - 1 file changed, 182 insertions(+) + arch/arm64/boot/dts/qcom/sm8750.dtsi | 211 +30 +++++++++++++++++++++ + 1 file changed, 211 +30 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sm8750.dtsi b/arch/arm64/boot/dts/qcom/sm8750.dtsi -index c19912178fb8..1a713ef2e006 100644 --- a/arch/arm64/boot/dts/qcom/sm8750.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8750.dtsi -@@ -3028,6 +3028,188 @@ videocc: clock-controller@aaf0000 { +@@ -3460,6 +3460,205 @@ #power-domain-cells = <1>; }; @@ -34,6 +36,9 @@ index c19912178fb8..1a713ef2e006 100644 + + operating-points-v2 = <&gpu_opp_table>; + ++ nvmem-cells = <&gpu_speed_bin>; ++ nvmem-cell-names = "speed_bin"; ++ + qcom,gmu = <&gmu>; + #cooling-cells = <2>; + @@ -54,56 +59,64 @@ index c19912178fb8..1a713ef2e006 100644 + opp-1100000000 { + opp-hz = /bits/ 64 <1100000000>; + opp-level = ; -+ opp-peak-kBps = <20832031>; -+ qcom,opp-acd-level = <0x882a5ffd>; ++ opp-peak-kBps = <18597656>; ++ opp-supported-hw = <0xc>; ++ qcom,opp-acd-level = <0x88295ffd>; + }; + + opp-1050000000 { + opp-hz = /bits/ 64 <1050000000>; + opp-level = ; -+ opp-peak-kBps = <20832031>; -+ qcom,opp-acd-level = <0x882a5ffd>; ++ opp-peak-kBps = <18597656>; ++ opp-supported-hw = <0xe>; ++ qcom,opp-acd-level = <0xa8295ffd>; + }; + + opp-967000000 { + opp-hz = /bits/ 64 <967000000>; + opp-level = ; -+ opp-peak-kBps = <12449218>; -+ qcom,opp-acd-level = <0x882b5ffd>; ++ opp-peak-kBps = <18597656>; ++ opp-supported-hw = <0xe>; ++ qcom,opp-acd-level = <0x882a5ffd>; + }; + + opp-900000000 { + opp-hz = /bits/ 64 <900000000>; + opp-level = ; -+ opp-peak-kBps = <12449218>; -+ qcom,opp-acd-level = <0x882b5ffd>; ++ opp-peak-kBps = <16500000>; ++ opp-supported-hw = <0xf>; ++ qcom,opp-acd-level = <0x882a5ffd>; + }; + + opp-832000000 { + opp-hz = /bits/ 64 <832000000>; + opp-level = ; -+ opp-peak-kBps = <12449218>; -+ qcom,opp-acd-level = <0x882b5ffd>; ++ opp-peak-kBps = <16500000>; ++ opp-supported-hw = <0xf>; ++ qcom,opp-acd-level = <0x882a5ffd>; + }; + + opp-734000000 { + opp-hz = /bits/ 64 <734000000>; + opp-level = ; -+ opp-peak-kBps = <8171875>; -+ qcom,opp-acd-level = <0xa82b5ffd>; ++ opp-peak-kBps = <12449218>; ++ opp-supported-hw = <0xf>; ++ qcom,opp-acd-level = <0xa82a5ffd>; + }; + + opp-660000000 { + opp-hz = /bits/ 64 <660000000>; + opp-level = ; + opp-peak-kBps = <8171875>; -+ qcom,opp-acd-level = <0x882d5ffd>; ++ opp-supported-hw = <0xf>; ++ qcom,opp-acd-level = <0x882c5ffd>; + }; + + opp-607000000 { + opp-hz = /bits/ 64 <607000000>; + opp-level = ; -+ opp-peak-kBps = <6074218>; ++ opp-peak-kBps = <8171875>; ++ opp-supported-hw = <0xf>; + qcom,opp-acd-level = <0xa82e5ffd>; + }; + @@ -111,39 +124,45 @@ index c19912178fb8..1a713ef2e006 100644 + opp-hz = /bits/ 64 <525000000>; + opp-level = ; + opp-peak-kBps = <6074218>; -+ qcom,opp-acd-level = <0xc0285ffd>; ++ opp-supported-hw = <0xf>; ++ qcom,opp-acd-level = <0xc02e5ffd>; + }; + + opp-443000000 { + opp-hz = /bits/ 64 <443000000>; + opp-level = ; + opp-peak-kBps = <6074218>; -+ qcom,opp-acd-level = <0xe02d5ffd>; ++ opp-supported-hw = <0xf>; ++ qcom,opp-acd-level = <0xc02e5ffd>; + }; + + opp-389000000 { + opp-hz = /bits/ 64 <389000000>; + opp-level = ; -+ opp-peak-kBps = <5285156>; -+ qcom,opp-acd-level = <0xe02f5ffd>; ++ opp-peak-kBps = <6074218>; ++ opp-supported-hw = <0xf>; ++ qcom,opp-acd-level = <0xc8285ffd>; + }; + + opp-342000000 { + opp-hz = /bits/ 64 <342000000>; + opp-level = ; + opp-peak-kBps = <5285156>; ++ opp-supported-hw = <0xf>; + }; + + opp-222000000 { + opp-hz = /bits/ 64 <222000000>; + opp-level = ; + opp-peak-kBps = <2136718>; ++ opp-supported-hw = <0xf>; + }; + + opp-160000000 { + opp-hz = /bits/ 64 <160000000>; + opp-level = ; + opp-peak-kBps = <2136718>; ++ opp-supported-hw = <0xf>; + }; + }; + }; @@ -195,7 +214,7 @@ index c19912178fb8..1a713ef2e006 100644 + + opp-687500000 { + opp-hz = /bits/ 64 <687500000>; -+ opp-level = ; ++ opp-level = ; + }; + }; + }; @@ -203,6 +222,22 @@ index c19912178fb8..1a713ef2e006 100644 gxclkctl: clock-controller@3d64000 { compatible = "qcom,sm8750-gxclkctl"; reg = <0x0 0x03d64000 0x0 0x6000>; --- -2.43.0 - +@@ -5763,6 +5962,18 @@ + }; + }; + ++ qfprom: efuse@221c8000 { ++ compatible = "qcom,sm8750-qfprom", "qcom,qfprom"; ++ reg = <0x0 0x221c8000 0x0 0x1000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ gpu_speed_bin: gpu-speed-bin@138 { ++ reg = <0x138 0x2>; ++ bits = <0 9>; ++ }; ++ }; ++ + /* cluster0 */ + pmu@240b3400 { + compatible = "qcom,sm8750-cpu-bwmon", "qcom,sdm845-bwmon"; diff --git a/projects/ROCKNIX/devices/SM8750/patches/linux/0049-drm-msm-a8xx-add-adreno-830-catalog.patch b/projects/ROCKNIX/devices/SM8750/patches/linux/0049-drm-msm-a8xx-add-adreno-830-catalog.patch index 09325ba69c..3636a7b02f 100644 --- a/projects/ROCKNIX/devices/SM8750/patches/linux/0049-drm-msm-a8xx-add-adreno-830-catalog.patch +++ b/projects/ROCKNIX/devices/SM8750/patches/linux/0049-drm-msm-a8xx-add-adreno-830-catalog.patch @@ -5,6 +5,8 @@ Subject: [PATCH] drm/msm/a8xx: add Adreno 830 (SM8750/Odin3) catalog entry (chip 0x44050000). Without it the GPU is 'Unknown GPU revision' and never binds -> no Vulkan -> sway can't render -> no GUI. Carry the a830 reglists + catalog entry on top of 7.1's a8xx support. + +[Modified 8/9/2026 Rocknix: to match downstream and odin 3] --- diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c index 550ff3a9b82e..a97bf2c0c89d 100644 @@ -283,7 +285,7 @@ index 550ff3a9b82e..a97bf2c0c89d 100644 static const struct adreno_reglist_pipe a840_nonctxt_regs[] = { { REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) }, { REG_A8XX_GRAS_DBG_ECO_CNTL, 0x00000800, BIT(PIPE_BV) | BIT(PIPE_BR) }, -@@ -2150,6 +2416,43 @@ static const struct adreno_info a8xx_gpus[] = { +@@ -2150,6 +2416,49 @@ static const struct adreno_info a8xx_gpus[] = { { 357, 2 }, { 284, 3 }, ), @@ -318,12 +320,18 @@ index 550ff3a9b82e..a97bf2c0c89d 100644 + .name = "ACV", + .fixed = true, + .perfmode = BIT(2), -+ .perfmode_bw = 8171875, ++ .perfmode_bw = 10687500, + }, + { /* sentinel */ }, + }, + }, + .preempt_record_size = 13536 * SZ_1K, ++ .speedbins = ADRENO_SPEEDBINS( ++ { 0xbe, 0 }, ++ { 0xdd, 1 }, ++ { 0xe8, 2 }, ++ { 0xf2, 3 }, ++ ), }, { .chip_ids = ADRENO_CHIP_IDS(0x44050a01), .family = ADRENO_8XX_GEN2, From 7ac671d9e8164854586491fd511b2c148792d582 Mon Sep 17 00:00:00 2001 From: John Williams Date: Mon, 10 Aug 2026 23:21:01 +1000 Subject: [PATCH 05/24] box64: bump to v0.4.4, enable box32 --- .../ROCKNIX/packages/compat/box64/config/box64.conf | 2 -- projects/ROCKNIX/packages/compat/box64/package.mk | 11 ++++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 projects/ROCKNIX/packages/compat/box64/config/box64.conf diff --git a/projects/ROCKNIX/packages/compat/box64/config/box64.conf b/projects/ROCKNIX/packages/compat/box64/config/box64.conf deleted file mode 100644 index 73af4ed504..0000000000 --- a/projects/ROCKNIX/packages/compat/box64/config/box64.conf +++ /dev/null @@ -1,2 +0,0 @@ -# /etc/binfmt.d/box64.conf -:x86_64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00:\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/box64: diff --git a/projects/ROCKNIX/packages/compat/box64/package.mk b/projects/ROCKNIX/packages/compat/box64/package.mk index fe910808f0..2af6f3dee0 100644 --- a/projects/ROCKNIX/packages/compat/box64/package.mk +++ b/projects/ROCKNIX/packages/compat/box64/package.mk @@ -2,7 +2,7 @@ # Copyright (C) 2023 JELOS (https://github.com/JustEnoughLinuxOS) PKG_NAME="box64" -PKG_VERSION="7eeb5016493dab4e143d53da50dd47bfb44a9509" # v0.4.2 +PKG_VERSION="2f130fab1d6e1a4ee8a71dc60cfdfcc839ad192a" # v0.4.4 PKG_ARCH="aarch64" PKG_LICENSE="MIT" PKG_SITE="https://github.com/ptitSeb/box64" @@ -11,7 +11,9 @@ PKG_DEPENDS_TARGET="toolchain ncurses SDL2 cabextract libXss libXdmcp libXft gtk PKG_LONGDESC="Box64 lets you run x86_64 Linux programs (such as games) on non-x86_64 Linux systems, like ARM." PKG_TOOLCHAIN="cmake" -PKG_CMAKE_OPTS_TARGET="-DCMAKE_BUILD_TYPE=Release" +PKG_CMAKE_OPTS_TARGET="-DCMAKE_BUILD_TYPE=Release \ + -DBOX32=On \ + -DBOX32_BINFMT=On" case ${DEVICE} in RK3588) @@ -29,6 +31,9 @@ makeinstall_target() { mkdir -p ${INSTALL}/usr/share/box64/lib cp ${PKG_BUILD}/x64lib/* ${INSTALL}/usr/share/box64/lib + mkdir -p ${INSTALL}/usr/share/box32/lib + cp ${PKG_BUILD}/x86lib/* ${INSTALL}/usr/share/box32/lib + mkdir -p ${INSTALL}/usr/bin cp ${PKG_BUILD}/.${TARGET_NAME}/box64 ${INSTALL}/usr/bin cp ${PKG_BUILD}/tests/box64-bash ${INSTALL}/usr/bin/bash-x64 @@ -40,5 +45,5 @@ makeinstall_target() { ln -sf /storage/.config/box64.box64rc ${INSTALL}/etc/box64.box64rc mkdir -p ${INSTALL}/etc/binfmt.d - cp -f ${PKG_DIR}/config/box64.conf ${INSTALL}/etc/binfmt.d/box64.conf + cp ${PKG_BUILD}/.${TARGET_NAME}/system/*.conf ${INSTALL}/etc/binfmt.d } From d2395fe6d7b1c04eb1834a4ff996e474caac1d4c Mon Sep 17 00:00:00 2001 From: John Williams Date: Mon, 10 Aug 2026 23:21:27 +1000 Subject: [PATCH 06/24] box86: use upstream binfmt conf --- projects/ROCKNIX/packages/compat/box86/config/box86.conf | 2 -- projects/ROCKNIX/packages/compat/box86/package.mk | 9 ++++++--- 2 files changed, 6 insertions(+), 5 deletions(-) delete mode 100644 projects/ROCKNIX/packages/compat/box86/config/box86.conf diff --git a/projects/ROCKNIX/packages/compat/box86/config/box86.conf b/projects/ROCKNIX/packages/compat/box86/config/box86.conf deleted file mode 100644 index 8692bca59d..0000000000 --- a/projects/ROCKNIX/packages/compat/box86/config/box86.conf +++ /dev/null @@ -1,2 +0,0 @@ -# /etc/binfmt.d/box86.conf -:x86:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/box86: diff --git a/projects/ROCKNIX/packages/compat/box86/package.mk b/projects/ROCKNIX/packages/compat/box86/package.mk index 0347e18ae6..608af01073 100644 --- a/projects/ROCKNIX/packages/compat/box86/package.mk +++ b/projects/ROCKNIX/packages/compat/box86/package.mk @@ -59,6 +59,9 @@ makeinstall_target() { cp ${PKG_BUILD}/.${TARGET_NAME}/box86 ${INSTALL}/usr/bin/ cp ${PKG_BUILD}/tests/bash ${INSTALL}/usr/bin/bash-x86 + mkdir -p ${INSTALL}/etc/binfmt.d + cp ${PKG_BUILD}/.${TARGET_NAME}/system/box86.conf ${INSTALL}/etc/binfmt.d + ;; aarch64) mkdir -p ${INSTALL}/usr/share/box86/lib @@ -70,6 +73,9 @@ makeinstall_target() { mkdir -p ${INSTALL}/usr/config cp -vP ${ROOT}/build.${DISTRO}-${DEVICE}.arm/install_pkg/${PKG_NAME}-*/usr/config/box86.box86rc ${INSTALL}/usr/config/box86.box86rc + mkdir -p ${INSTALL}/etc/binfmt.d + cp -vP ${ROOT}/build.${DISTRO}-${DEVICE}.arm/install_pkg/${PKG_NAME}-*/etc/binfmt.d/box86.conf ${INSTALL}/etc/binfmt.d + ;; esac @@ -78,9 +84,6 @@ makeinstall_target() { mkdir -p ${INSTALL}/etc ln -sf /storage/.config/box86.box86rc ${INSTALL}/etc/box86.box86rc - - mkdir -p ${INSTALL}/etc/binfmt.d - cp -f ${PKG_DIR}/config/box86.conf ${INSTALL}/etc/binfmt.d/box86.conf } if [ ! "${ENABLE_32BIT}" == "true" ]; then From cb47cf4a6e4d2dfddebcec59c9144fe953848e64 Mon Sep 17 00:00:00 2001 From: John Williams Date: Mon, 10 Aug 2026 23:21:45 +1000 Subject: [PATCH 07/24] steam: updated box32 / box64 binfmt names --- .../emulators/standalone/steam/scripts/start_steam.sh | 4 +++- .../packages/virtual/emulators/sources/Install Steam.sh | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/projects/ROCKNIX/packages/emulators/standalone/steam/scripts/start_steam.sh b/projects/ROCKNIX/packages/emulators/standalone/steam/scripts/start_steam.sh index e068d77aab..d004a45ee7 100755 --- a/projects/ROCKNIX/packages/emulators/standalone/steam/scripts/start_steam.sh +++ b/projects/ROCKNIX/packages/emulators/standalone/steam/scripts/start_steam.sh @@ -158,8 +158,10 @@ steam_dual_screen_end() { } steam_arm64_binfmt_and_proton_prep() { - echo 0 >/proc/sys/fs/binfmt_misc/x86_64 echo 0 >/proc/sys/fs/binfmt_misc/x86 + echo 0 > /proc/sys/fs/binfmt_misc/box32 + echo 0 > /proc/sys/fs/binfmt_misc/box64 + mkdir -p "/storage/.local/share/Steam/steamapps/common/Proton 11.0 (ARM64)/" cp -f "/usr/share/steam/toolmanifest.vdf" "/storage/.local/share/Steam/steamapps/common/Proton 11.0 (ARM64)/" } diff --git a/projects/ROCKNIX/packages/virtual/emulators/sources/Install Steam.sh b/projects/ROCKNIX/packages/virtual/emulators/sources/Install Steam.sh index 66b278c0ec..dc36b51588 100755 --- a/projects/ROCKNIX/packages/virtual/emulators/sources/Install Steam.sh +++ b/projects/ROCKNIX/packages/virtual/emulators/sources/Install Steam.sh @@ -190,8 +190,9 @@ install_proton_ge() { run_steam_first_launch() { log_info "Running Steam first launch routine..." - echo 0 > /proc/sys/fs/binfmt_misc/x86_64 || true echo 0 > /proc/sys/fs/binfmt_misc/x86 || true + echo 0 > /proc/sys/fs/binfmt_misc/box32 || true + echo 0 > /proc/sys/fs/binfmt_misc/box64 || true if [ "${DEVICE_HAS_DUAL_SCREEN}" = "true" ]; then swaymsg 'seat seat1 fallback true' || log_info "Swaymsg dual screen setup failed, ignoring." From 0ad7ee83c2b87a9f43d9a5459b56c665375de4c6 Mon Sep 17 00:00:00 2001 From: Philippe Simons Date: Sun, 9 Aug 2026 23:19:13 +0200 Subject: [PATCH 08/24] feat: H700 use upstream H616 PWM driver Replace the out-of-tree pwm-sun20i driver with the v8 sun8i series and drive the backlight from PWM0 (PD28) instead of switching it with a GPIO. The board keeps its own lcd_backlight_pin muxing node and the &pwm override stays at the end of the DTS: 0140 and 0150 anchor their &pio hunks on them, and dropping either makes those hunks apply with fuzz into the wrong node, which silently breaks the joypad pinctrl. --- .../devices/H700/linux/linux.aarch64.conf | 2 +- .../linux/0008-sun20i-add-pwm-driver.patch | 495 ----- ...roduce_allwinner_h616_pwm_controller.patch | 1924 +++++++++++++++++ .../linux/0009-h616-add-pwm-node.patch | 37 - .../0010-rg35xx-enable-pwm-backlight.patch | 18 +- .../linux/0204-dts-Enable-hdmi-sound.patch | 8 +- 6 files changed, 1942 insertions(+), 542 deletions(-) delete mode 100644 projects/ROCKNIX/devices/H700/patches/linux/0008-sun20i-add-pwm-driver.patch create mode 100644 projects/ROCKNIX/devices/H700/patches/linux/0008-v8_20260804_james_hilliard1_introduce_allwinner_h616_pwm_controller.patch delete mode 100644 projects/ROCKNIX/devices/H700/patches/linux/0009-h616-add-pwm-node.patch diff --git a/projects/ROCKNIX/devices/H700/linux/linux.aarch64.conf b/projects/ROCKNIX/devices/H700/linux/linux.aarch64.conf index a63521f77d..e962ab0a79 100644 --- a/projects/ROCKNIX/devices/H700/linux/linux.aarch64.conf +++ b/projects/ROCKNIX/devices/H700/linux/linux.aarch64.conf @@ -6921,7 +6921,7 @@ CONFIG_PWM_GPIO=y # CONFIG_PWM_MC33XS2410 is not set # CONFIG_PWM_PCA9685 is not set CONFIG_PWM_SUN4I=y -CONFIG_PWM_SUN20I=y +CONFIG_PWM_SUN8I=y # CONFIG_PWM_XILINX is not set # diff --git a/projects/ROCKNIX/devices/H700/patches/linux/0008-sun20i-add-pwm-driver.patch b/projects/ROCKNIX/devices/H700/patches/linux/0008-sun20i-add-pwm-driver.patch deleted file mode 100644 index ca667b1b86..0000000000 --- a/projects/ROCKNIX/devices/H700/patches/linux/0008-sun20i-add-pwm-driver.patch +++ /dev/null @@ -1,495 +0,0 @@ -From 83753f7dd56bbb36af578f0152d66cbe46272109 Mon Sep 17 00:00:00 2001 -From: Philippe Simons -Date: Thu, 23 Jan 2025 23:39:15 +0100 -Subject: [PATCH 7/9] sun20i: add pwm driver - ---- - arch/arm64/configs/defconfig | 1 + - drivers/pwm/Kconfig | 10 + - drivers/pwm/Makefile | 1 + - drivers/pwm/pwm-sun20i.c | 428 +++++++++++++++++++++++++++++++++++ - 4 files changed, 440 insertions(+) - create mode 100644 drivers/pwm/pwm-sun20i.c - -diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig -index c62831e61..7d631556b 100644 ---- a/arch/arm64/configs/defconfig -+++ b/arch/arm64/configs/defconfig -@@ -1517,6 +1517,7 @@ CONFIG_PWM_RZ_MTU3=m - CONFIG_PWM_SAMSUNG=y - CONFIG_PWM_SL28CPLD=m - CONFIG_PWM_SUN4I=m -+CONFIG_PWM_SUN20I=m - CONFIG_PWM_TEGRA=m - CONFIG_PWM_TIECAP=m - CONFIG_PWM_TIEHRPWM=m -diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig -index 0915c1e7d..a4a7eb286 100644 ---- a/drivers/pwm/Kconfig -+++ b/drivers/pwm/Kconfig -@@ -652,6 +652,16 @@ config PWM_SUN4I - To compile this driver as a module, choose M here: the module - will be called pwm-sun4i. - -+config PWM_SUN20I -+ tristate "Allwinner D1/H616 PWM support" -+ depends on ARCH_SUNXI || COMPILE_TEST -+ depends on HAS_IOMEM && COMMON_CLK -+ help -+ Generic PWM framework driver for Newer Allwinner SoCs. -+ -+ To compile this driver as a module, choose M here: the module -+ will be called pwm-sun20i. -+ - config PWM_SUNPLUS - tristate "Sunplus PWM support" - depends on ARCH_SUNPLUS || COMPILE_TEST -diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile -index 9081e0c0e..85ad1fe0d 100644 ---- a/drivers/pwm/Makefile -+++ b/drivers/pwm/Makefile -@@ -60,6 +60,7 @@ obj-$(CONFIG_PWM_STM32) += pwm-stm32.o - obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o - obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o - obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o -+obj-$(CONFIG_PWM_SUN20I) += pwm-sun20i.o - obj-$(CONFIG_PWM_SUNPLUS) += pwm-sunplus.o - obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o - obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o -diff --git a/drivers/pwm/pwm-sun20i.c b/drivers/pwm/pwm-sun20i.c -new file mode 100644 -index 000000000..7262a99ab ---- /dev/null -+++ b/drivers/pwm/pwm-sun20i.c -@@ -0,0 +1,428 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Driver for Allwinner's new PWM Controller (such as D1 / H616 / T5 series) -+ * -+ * Copyright (C) 2024 Hironori KIKUCHI -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+// SoC specific offsets -+#define SUN20I_REG_OFFSET_PER_SUN20I (0x0080) -+#define SUN20I_REG_OFFSET_PCR_SUN20I (0x0100 + 0x0000) -+#define SUN20I_REG_OFFSET_PPR_SUN20I (0x0100 + 0x0004) -+#define SUN20I_REG_OFFSET_PER_SUN50I (0x0040) -+#define SUN20I_REG_OFFSET_PCR_SUN50I (0x0060 + 0x0000) -+#define SUN20I_REG_OFFSET_PPR_SUN50I (0x0060 + 0x0004) -+ -+// Register offsets -+#define SUN20I_REG_OFFSET_PCCR(chip, ch) (0x0020 + 0x04 * ((ch) >> 1)) -+#define SUN20I_REG_OFFSET_PCGR(chip, ch) (0x0040) -+#define SUN20I_REG_OFFSET_PER(chip, ch) ((chip)->data->reg_per) -+#define SUN20I_REG_OFFSET_PCR(chip, ch) ((chip)->data->reg_pcr + 0x20 * (ch)) -+#define SUN20I_REG_OFFSET_PPR(chip, ch) ((chip)->data->reg_ppr + 0x20 * (ch)) -+ -+// PCCR: PWMxx Clock Configuration Register -+#define SUN20I_REG_PCCR_CLK_SRC_MASK GENMASK(8, 7) -+#define SUN20I_REG_PCCR_CLK_DIV_M_MASK GENMASK(3, 0) -+#define SUN20I_REG_PCCR_CLK_BYPASS(ch) BIT(5 + ((ch) & 1)) -+#define SUN20I_REG_PCCR_CLK_GATING(ch) BIT(4) -+ -+// PCGR: PWM Clock Gating Register -+#define SUN20I_REG_PCGR_CLK_BYPASS(ch) BIT(16 + (ch)) -+#define SUN20I_REG_PCGR_CLK_GATING(ch) BIT(ch) -+ -+// PER: PWM Enable Regsiter -+#define SUN20I_REG_PER_ENABLE(ch) BIT(ch) -+ -+// PCR: PWM Control Register -+#define SUN20I_REG_PCR_ACT_STA BIT(8) -+#define SUN20I_REG_PCR_PRESCAL_K_MASK GENMASK(7, 0) -+ -+// PPR: PWM Period Register -+#define SUN20I_REG_PPR_ENTIRE_CYCLE_MASK GENMASK(31, 16) -+#define SUN20I_REG_PPR_ACT_CYCLE_MASK GENMASK(15, 0) -+ -+// Constants -+#define SUN20I_PWM_CHANNELS_MAX (16) -+#define SUN20I_PWM_CLOCK_SRC_HOSC (0) -+#define SUN20I_PWM_CLOCK_SRC_APB (1) -+#define SUN20I_PWM_DIV_M_SHIFT_MAX (8) -+#define SUN20I_PWM_PRESCALE_K_MAX (256) -+#define SUN20I_PWM_ENT_CYCLE_MAX (0xffffULL) -+ -+// Configuration -+#define SUN20I_PWM_DIV_M_SHIFT_DEFAULT (0) -+#define SUN20I_PWM_CLOCK_SRC_DEFAULT SUN20I_PWM_CLOCK_SRC_HOSC -+ -+struct sun20i_pwm_data { -+ unsigned long reg_per; -+ unsigned long reg_pcr; -+ unsigned long reg_ppr; -+ bool has_pcgr; -+}; -+ -+struct sun20i_pwm_chip { -+ struct clk *clk_bus; -+ struct clk *clk_hosc; -+ struct clk *clk_apb; -+ struct reset_control *rst; -+ void __iomem *base; -+ spinlock_t ctrl_lock; -+ const struct sun20i_pwm_data *data; -+ -+ unsigned int clk_src[(SUN20I_PWM_CHANNELS_MAX + 1) / 2]; -+ unsigned int div_m_shift[(SUN20I_PWM_CHANNELS_MAX + 1) / 2]; -+}; -+ -+static inline struct sun20i_pwm_chip *to_sun20i_pwm_chip(struct pwm_chip *chip) -+{ -+ return pwmchip_get_drvdata(chip); -+} -+ -+static inline u32 sun20i_pwm_readl(struct sun20i_pwm_chip *sun20i_chip, unsigned long offset) -+{ -+ return readl(sun20i_chip->base + offset); -+} -+ -+static inline void sun20i_pwm_writel(struct sun20i_pwm_chip *sun20i_chip, u32 val, unsigned long offset) -+{ -+ writel(val, sun20i_chip->base + offset); -+} -+ -+static inline bool sun20i_pwm_is_channel_enabled(struct sun20i_pwm_chip *sun20i_chip, int hwpwm) -+{ -+ u32 val; -+ val = sun20i_pwm_readl(sun20i_chip, SUN20I_REG_OFFSET_PER(sun20i_chip, hwpwm)); -+ return val & SUN20I_REG_PER_ENABLE(hwpwm) ? true : false; -+} -+ -+static inline void sun20i_pwm_enable_channel(struct sun20i_pwm_chip *sun20i_chip, struct pwm_device *pwm, bool enable) -+{ -+ u32 val; -+ -+ // PWM Clock Configuration Register -+ u32 pccr = sun20i_pwm_readl(sun20i_chip, SUN20I_REG_OFFSET_PCCR(sun20i_chip, pwm->hwpwm)); -+ if (enable) { -+ unsigned int idx = pwm->hwpwm / 2; -+ // Set clock source -+ pccr &= ~SUN20I_REG_PCCR_CLK_SRC_MASK; -+ pccr |= FIELD_PREP(SUN20I_REG_PCCR_CLK_SRC_MASK, sun20i_chip->clk_src[idx]); -+ -+ // Set DIV_M shift -+ pccr &= ~SUN20I_REG_PCCR_CLK_DIV_M_MASK; -+ pccr |= FIELD_PREP(SUN20I_REG_PCCR_CLK_DIV_M_MASK, sun20i_chip->div_m_shift[idx]); -+ } -+ -+ if (sun20i_chip->data->has_pcgr) { -+ // PWM Clock Gating Register -+ val = sun20i_pwm_readl(sun20i_chip, SUN20I_REG_OFFSET_PCGR(sun20i_chip, pwm->hwpwm)); -+ if (enable) { -+ val &= ~SUN20I_REG_PCGR_CLK_BYPASS(pwm->hwpwm); -+ val |= SUN20I_REG_PCGR_CLK_GATING(pwm->hwpwm); -+ } else { -+ val &= ~SUN20I_REG_PCGR_CLK_GATING(pwm->hwpwm); -+ } -+ sun20i_pwm_writel(sun20i_chip, val, SUN20I_REG_OFFSET_PCGR(sun20i_chip, pwm->hwpwm)); -+ } else { -+ // CLK_BYPASS and CLK_GATING are in PCCR otherwise -+ if (enable) { -+ pccr &= ~SUN20I_REG_PCCR_CLK_BYPASS(pwm->hwpwm); -+ pccr |= SUN20I_REG_PCCR_CLK_GATING(pwm->hwpwm); -+ } else if (!sun20i_pwm_is_channel_enabled(sun20i_chip, pwm->hwpwm ^ 1)) { -+ // drop the flag if and only if the counterpart is disabled -+ pccr &= ~SUN20I_REG_PCCR_CLK_GATING(pwm->hwpwm); -+ } -+ } -+ -+ sun20i_pwm_writel(sun20i_chip, pccr, SUN20I_REG_OFFSET_PCCR(sun20i_chip, pwm->hwpwm)); -+ -+ // PWM Enabling -+ val = sun20i_pwm_readl(sun20i_chip, SUN20I_REG_OFFSET_PER(sun20i_chip, pwm->hwpwm)); -+ if (enable) -+ val |= SUN20I_REG_PER_ENABLE(pwm->hwpwm); -+ else -+ val &= ~SUN20I_REG_PER_ENABLE(pwm->hwpwm); -+ -+ sun20i_pwm_writel(sun20i_chip, val, SUN20I_REG_OFFSET_PER(sun20i_chip, pwm->hwpwm)); -+} -+ -+static inline u64 sun20i_pwm_get_clock_rate(struct sun20i_pwm_chip *sun20i_chip, unsigned int clksrc) -+{ -+ switch (clksrc) { -+ case SUN20I_PWM_CLOCK_SRC_HOSC: -+ return clk_get_rate(sun20i_chip->clk_hosc); -+ -+ case SUN20I_PWM_CLOCK_SRC_APB: -+ return clk_get_rate(sun20i_chip->clk_apb); -+ -+ default: -+ return 0; -+ } -+} -+ -+static int sun20i_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) -+{ -+ struct sun20i_pwm_chip *sun20i_chip = to_sun20i_pwm_chip(chip); -+ u64 clk_rate; -+ -+ u32 pccr = sun20i_pwm_readl(sun20i_chip, SUN20I_REG_OFFSET_PCCR(sun20i_chip, pwm->hwpwm)); -+ u32 pcr = sun20i_pwm_readl(sun20i_chip, SUN20I_REG_OFFSET_PCR(sun20i_chip, pwm->hwpwm)); -+ u32 ppr = sun20i_pwm_readl(sun20i_chip, SUN20I_REG_OFFSET_PPR(sun20i_chip, pwm->hwpwm)); -+ -+ clk_rate = sun20i_pwm_get_clock_rate(sun20i_chip, FIELD_GET(SUN20I_REG_PCCR_CLK_SRC_MASK, pccr)); -+ if (!clk_rate) { -+ dev_err(pwmchip_parent(chip), "Invalid CLK_SRC is detected\n"); -+ return -EINVAL; -+ } -+ -+ unsigned int prescale_k = FIELD_GET(SUN20I_REG_PCR_PRESCAL_K_MASK, pcr) + 1; -+ u32 ent_cycle = FIELD_GET(SUN20I_REG_PPR_ENTIRE_CYCLE_MASK, ppr) + 1; -+ u32 act_cycle = min(ent_cycle, FIELD_GET(SUN20I_REG_PPR_ACT_CYCLE_MASK, ppr)); -+ -+ unsigned int div_m_shift = FIELD_GET(SUN20I_REG_PCCR_CLK_DIV_M_MASK, pccr); -+ if (div_m_shift > SUN20I_PWM_DIV_M_SHIFT_MAX) { -+ dev_err(pwmchip_parent(chip), "Invalid DIV_M is detected\n"); -+ return -EINVAL; -+ } -+ unsigned int div_m = 1 << div_m_shift; -+ -+ // set to the state -+ state->enabled = sun20i_pwm_is_channel_enabled(sun20i_chip, pwm->hwpwm); -+ state->polarity = (pcr & SUN20I_REG_PCR_ACT_STA) ? PWM_POLARITY_NORMAL : PWM_POLARITY_INVERSED; -+ state->period = DIV_ROUND_CLOSEST_ULL(ent_cycle * prescale_k * div_m * NSEC_PER_SEC, clk_rate); -+ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(act_cycle * prescale_k * div_m * NSEC_PER_SEC, clk_rate); -+ -+ return 0; -+} -+ -+static inline unsigned long sun20i_pwm_find_prescale_k(unsigned long long ent_cycle, unsigned long long act_cycle) -+{ -+ if (ent_cycle == 0 || ent_cycle > SUN20I_PWM_ENT_CYCLE_MAX * SUN20I_PWM_PRESCALE_K_MAX || ent_cycle < act_cycle) -+ return 0; -+ -+ return clamp(DIV_ROUND_UP_ULL(ent_cycle, SUN20I_PWM_ENT_CYCLE_MAX), 1, SUN20I_PWM_PRESCALE_K_MAX); -+} -+ -+static int sun20i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) -+{ -+ struct sun20i_pwm_chip *sun20i_chip = to_sun20i_pwm_chip(chip); -+ u64 clk_rate; -+ u32 ctrl; -+ -+ spin_lock(&sun20i_chip->ctrl_lock); -+ -+ // Period and Duty cycle -+ if (state->duty_cycle != pwm->state.duty_cycle || state->period != pwm->state.period) { -+ unsigned int idx = pwm->hwpwm / 2; -+ clk_rate = sun20i_pwm_get_clock_rate(sun20i_chip, sun20i_chip->clk_src[idx]); -+ unsigned long div_m = 1U << sun20i_chip->div_m_shift[idx]; -+ unsigned long long ent_cycle = DIV_ROUND_CLOSEST(state->period * clk_rate, NSEC_PER_SEC * div_m); -+ unsigned long long act_cycle = min(DIV_ROUND_CLOSEST(state->duty_cycle * clk_rate, NSEC_PER_SEC * div_m), ent_cycle); -+ -+ unsigned long prescale_k = sun20i_pwm_find_prescale_k(ent_cycle, act_cycle); -+ if (!prescale_k) -+ goto err; -+ -+ ent_cycle = clamp(DIV_ROUND_CLOSEST_ULL(ent_cycle, prescale_k), 1, SUN20I_PWM_ENT_CYCLE_MAX); -+ act_cycle = clamp(DIV_ROUND_CLOSEST_ULL(act_cycle, prescale_k), 0, ent_cycle); -+ -+ // Set prescale_k -+ ctrl = sun20i_pwm_readl(sun20i_chip, SUN20I_REG_OFFSET_PCR(sun20i_chip, pwm->hwpwm)); -+ ctrl &= ~SUN20I_REG_PCR_PRESCAL_K_MASK; -+ ctrl |= FIELD_PREP(SUN20I_REG_PCR_PRESCAL_K_MASK, prescale_k - 1); -+ sun20i_pwm_writel(sun20i_chip, ctrl, SUN20I_REG_OFFSET_PCR(sun20i_chip, pwm->hwpwm)); -+ -+ // Set period and duty cycle -+ ctrl = (((ent_cycle - 1) & 0xFFFF) << 16) | (act_cycle & 0xFFFF); -+ sun20i_pwm_writel(sun20i_chip, ctrl, SUN20I_REG_OFFSET_PPR(sun20i_chip, pwm->hwpwm)); -+ } -+ -+ // Polarity -+ if (state->polarity != pwm->state.polarity) { -+ ctrl = sun20i_pwm_readl(sun20i_chip, SUN20I_REG_OFFSET_PCR(sun20i_chip, pwm->hwpwm)); -+ if (state->polarity == PWM_POLARITY_NORMAL) { -+ ctrl |= SUN20I_REG_PCR_ACT_STA; -+ } else { -+ ctrl &= ~SUN20I_REG_PCR_ACT_STA; -+ } -+ sun20i_pwm_writel(sun20i_chip, ctrl, SUN20I_REG_OFFSET_PCR(sun20i_chip, pwm->hwpwm)); -+ } -+ -+ // Enable -+ if (state->enabled != pwm->state.enabled) -+ sun20i_pwm_enable_channel(sun20i_chip, pwm, state->enabled); -+ -+ spin_unlock(&sun20i_chip->ctrl_lock); -+ return 0; -+ -+err: -+ spin_unlock(&sun20i_chip->ctrl_lock); -+ return -EINVAL; -+} -+ -+static const struct pwm_ops sun20i_pwm_ops = { -+ .apply = sun20i_pwm_apply, -+ .get_state = sun20i_pwm_get_state, -+}; -+ -+static const struct sun20i_pwm_data sun20i_d1_pwm_data = { -+ .reg_per = SUN20I_REG_OFFSET_PER_SUN20I, -+ .reg_pcr = SUN20I_REG_OFFSET_PCR_SUN20I, -+ .reg_ppr = SUN20I_REG_OFFSET_PPR_SUN20I, -+ .has_pcgr = true, -+}; -+ -+static const struct sun20i_pwm_data sun50i_h616_pwm_data = { -+ .reg_per = SUN20I_REG_OFFSET_PER_SUN50I, -+ .reg_pcr = SUN20I_REG_OFFSET_PCR_SUN50I, -+ .reg_ppr = SUN20I_REG_OFFSET_PPR_SUN50I, -+ .has_pcgr = false, -+}; -+ -+static const struct of_device_id sun20i_pwm_dt_ids[] = { -+ { -+ .compatible = "allwinner,sun20i-d1-pwm", -+ .data = &sun20i_d1_pwm_data, -+ }, -+ { -+ .compatible = "allwinner,sun50i-h616-pwm", -+ .data = &sun50i_h616_pwm_data, -+ }, -+ { -+ /* sentinel */ -+ }, -+}; -+MODULE_DEVICE_TABLE(of, sun20i_pwm_dt_ids); -+ -+static int sun20i_pwm_probe(struct platform_device *pdev) -+{ -+ struct pwm_chip *chip; -+ const struct sun20i_pwm_data *data; -+ struct sun20i_pwm_chip *sun20i_chip; -+ u32 npwm; -+ int ret; -+ -+ struct device_node *node = pdev->dev.of_node; -+ -+ data = of_device_get_match_data(&pdev->dev); -+ if (!data) -+ return -ENODEV; -+ -+ ret = of_property_read_u32(node, "allwinner,pwm-channels", &npwm); -+ if (ret) -+ return dev_err_probe(&pdev->dev, -EINVAL, "No PWM channels are configured\n"); -+ -+ if (npwm > SUN20I_PWM_CHANNELS_MAX) -+ return dev_err_probe(&pdev->dev, -EINVAL, "Too many PWM channels are configured\n"); -+ -+ chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*sun20i_chip)); -+ if (IS_ERR(chip)) -+ return PTR_ERR(chip); -+ sun20i_chip = to_sun20i_pwm_chip(chip); -+ -+ sun20i_chip->data = data; -+ -+ for (int i = 0; i < (npwm + 1) / 2; i++) { -+ sun20i_chip->clk_src[i] = SUN20I_PWM_CLOCK_SRC_DEFAULT; -+ sun20i_chip->div_m_shift[i] = SUN20I_PWM_DIV_M_SHIFT_DEFAULT; -+ -+ const char *source; -+ ret = of_property_read_string_index(node, "allwinner,pwm-paired-channel-clock-sources", i, &source); -+ if (!ret) { -+ if (!strcasecmp(source, "hosc")) -+ sun20i_chip->clk_src[i] = SUN20I_PWM_CLOCK_SRC_HOSC; -+ else if (!strcasecmp(source, "apb")) -+ sun20i_chip->clk_src[i] = SUN20I_PWM_CLOCK_SRC_APB; -+ else -+ return dev_err_probe(&pdev->dev, -EINVAL, "Unknown clock source: %s\n", source); -+ } -+ -+ u32 value; -+ ret = of_property_read_u32_index(node, "allwinner,pwm-paired-channel-clock-prescales", i, &value); -+ if (!ret) { -+ if (value <= SUN20I_PWM_DIV_M_SHIFT_MAX) -+ sun20i_chip->div_m_shift[i] = value; -+ else -+ return dev_err_probe(&pdev->dev, -EINVAL, "Invalid prescale value: %u\n", value); -+ } -+ } -+ -+ sun20i_chip->base = devm_platform_ioremap_resource(pdev, 0); -+ if (IS_ERR(sun20i_chip->base)) -+ return PTR_ERR(sun20i_chip->base); -+ -+ sun20i_chip->clk_bus = devm_clk_get_enabled(&pdev->dev, "bus"); -+ if (IS_ERR(sun20i_chip->clk_bus)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(sun20i_chip->clk_bus), "Failed to get `bus` clock\n"); -+ -+ sun20i_chip->clk_hosc = devm_clk_get_enabled(&pdev->dev, "hosc"); -+ if (IS_ERR(sun20i_chip->clk_hosc)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(sun20i_chip->clk_hosc), "Failed to get `hosc` clock\n"); -+ -+ sun20i_chip->clk_apb = devm_clk_get_enabled(&pdev->dev, "apb"); -+ if (IS_ERR(sun20i_chip->clk_apb)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(sun20i_chip->clk_apb), "Failed to get `apb` clock\n"); -+ -+ sun20i_chip->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); -+ if (IS_ERR(sun20i_chip->rst)) -+ return dev_err_probe(&pdev->dev, PTR_ERR(sun20i_chip->rst), "Failed to get bus reset\n"); -+ -+ ret = reset_control_deassert(sun20i_chip->rst); -+ if (ret) -+ return dev_err_probe(&pdev->dev, ret, "Failed to deassert reset control\n"); -+ -+ chip->ops = &sun20i_pwm_ops; -+ -+ spin_lock_init(&sun20i_chip->ctrl_lock); -+ -+ ret = pwmchip_add(chip); -+ if (ret < 0) { -+ reset_control_assert(sun20i_chip->rst); -+ return dev_err_probe(&pdev->dev, ret, "Failed to add PWM chip\n"); -+ } -+ -+ platform_set_drvdata(pdev, chip); -+ -+ return 0; -+} -+ -+static void sun20i_pwm_remove(struct platform_device *pdev) -+{ -+ struct pwm_chip *chip = platform_get_drvdata(pdev); -+ struct sun20i_pwm_chip *sun20i_chip = to_sun20i_pwm_chip(chip); -+ -+ pwmchip_remove(chip); -+ -+ reset_control_assert(sun20i_chip->rst); -+} -+ -+static struct platform_driver sun20i_pwm_driver = { -+ .driver = { -+ .name = "sun20i-pwm", -+ .of_match_table = sun20i_pwm_dt_ids, -+ }, -+ .probe = sun20i_pwm_probe, -+ .remove = sun20i_pwm_remove, -+}; -+module_platform_driver(sun20i_pwm_driver); -+ -+MODULE_ALIAS("platform:sun20i-pwm"); -+MODULE_AUTHOR("Hironori KIKUCHI "); -+MODULE_DESCRIPTION("Allwinner sun20i PWM driver"); -+MODULE_LICENSE("GPL v2"); --- -2.47.1 - diff --git a/projects/ROCKNIX/devices/H700/patches/linux/0008-v8_20260804_james_hilliard1_introduce_allwinner_h616_pwm_controller.patch b/projects/ROCKNIX/devices/H700/patches/linux/0008-v8_20260804_james_hilliard1_introduce_allwinner_h616_pwm_controller.patch new file mode 100644 index 0000000000..52114d3695 --- /dev/null +++ b/projects/ROCKNIX/devices/H700/patches/linux/0008-v8_20260804_james_hilliard1_introduce_allwinner_h616_pwm_controller.patch @@ -0,0 +1,1924 @@ +From git@z Thu Jan 1 00:00:00 1970 +Subject: [PATCH v8 0/4] Introduce Allwinner H616 PWM controller +From: James Hilliard +Date: Tue, 04 Aug 2026 15:27:23 -0600 +Message-Id: <20260804-h616-pwm-v8-v8-0-db37ab8624ae@gmail.com> +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 7bit + +Squashed v8 series, applies to H700 unchanged: H700 is an H616 die and all +H700 boards include sun50i-h616.dtsi, so the controller node and the pwm0..5 +pins land on them too. pwm0 is PD28, the RG35XX backlight pin. + +Not yet merged upstream (absent from v7.1 and mainline as of 2026-08); refresh +this file on each respin. + +https://lore.kernel.org/linux-sunxi/20260804-h616-pwm-v8-v8-0-db37ab8624ae@gmail.com/ + +Replaces the out-of-tree pwm-sun20i driver previously carried as +0008-sun20i-add-pwm-driver.patch and 0009-h616-add-pwm-node.patch. + + +Subject: [PATCH v8 1/4] dt-bindings: pwm: allwinner: add h616 pwm compatible +From: Richard Genoud +Date: Tue, 04 Aug 2026 15:27:24 -0600 +Message-Id: <20260804-h616-pwm-v8-v8-1-db37ab8624ae@gmail.com> + + +The Allwinner H616 PWM block differs from the A10 and H6 blocks, but it +uses the same input clocks as H6, so extend the existing Allwinner PWM +binding. + +The block has six channels that can generate PWM waveforms. Each channel +also has a bypass that turns its output into a clock suitable for consumers +such as the integrated X-Powers AC200 or AC300 Ethernet PHY. + +Require #clock-cells for H616 because the bypass clocks are part of its +provider interface, while continuing to disallow the property for the older +variants. + +Acked-by: Conor Dooley +Signed-off-by: Richard Genoud +Signed-off-by: James Hilliard +--- +Changes v7 -> v8: + - require both the module and bus input clocks for H616 + - require #clock-cells for the H616 clock provider (reported by Sashiko) + - describe the channel-indexed hardware bypass clock cells +--- + .../bindings/pwm/allwinner,sun4i-a10-pwm.yaml | 33 +++++++++++++++++++++- + 1 file changed, 32 insertions(+), 1 deletion(-) + +diff --git a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml +index 1197858e431f..3b1819c8630c 100644 +--- a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml ++++ b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml +@@ -14,6 +14,13 @@ properties: + "#pwm-cells": + const: 3 + ++ "#clock-cells": ++ const: 1 ++ description: ++ The cell selects the PWM channel whose hardware bypass path is exported ++ as a clock. Its rate follows the selected shared pair source and divider; ++ the PWM period, duty-cycle and polarity logic is bypassed. ++ + compatible: + oneOf: + - const: allwinner,sun4i-a10-pwm +@@ -36,6 +43,7 @@ properties: + - const: allwinner,sun50i-h5-pwm + - const: allwinner,sun5i-a13-pwm + - const: allwinner,sun50i-h6-pwm ++ - const: allwinner,sun50i-h616-pwm + + reg: + maxItems: 1 +@@ -62,7 +70,9 @@ allOf: + properties: + compatible: + contains: +- const: allwinner,sun50i-h6-pwm ++ enum: ++ - allwinner,sun50i-h6-pwm ++ - allwinner,sun50i-h616-pwm + + then: + properties: +@@ -83,6 +93,27 @@ allOf: + clocks: + maxItems: 1 + ++ - if: ++ properties: ++ compatible: ++ contains: ++ const: allwinner,sun50i-h616-pwm ++ ++ then: ++ properties: ++ clocks: ++ minItems: 2 ++ ++ clock-names: ++ minItems: 2 ++ ++ required: ++ - "#clock-cells" ++ ++ else: ++ properties: ++ "#clock-cells": false ++ + required: + - compatible + - reg + + +Subject: [PATCH v8 2/4] pwm: sun8i: Add H616 PWM support +From: Richard Genoud +Date: Tue, 04 Aug 2026 15:27:25 -0600 +Message-Id: <20260804-h616-pwm-v8-v8-2-db37ab8624ae@gmail.com> + + +Add a driver for the Allwinner H616 PWM controller, supporting six +channels. Each channel output can carry either a PWM waveform or a clock +from its bypass path. + +The channels are paired as 0/1, 2/3 and 4/5. Each pair shares a mux, gate +and prescaler, while each channel has its own prescaler and bypass. + +Register each shared pair mux, divider and gate with the Common Clock +Framework, and expose each channel bypass as a clock output. Program the +channel-specific divider directly in the PWM path. Arbitrate each output +when the PWM is requested or the bypass clock is prepared, and hold the +shared pair rate exclusively while either sibling output is active. + +CCF reference counts do not describe outputs left active by firmware. +Before disabling a pair gate or changing its rate, also inspect the +hardware channel-enable bits. Preserve inherited outputs through generic +unused-clock cleanup so declared consumers can claim them. At driver +sync_state(), stop channels which remain unowned and reconcile an empty +shared pair gate through CCF. This prevents both premature shutdown of a +late-probing consumer and indefinite unclaimed outputs. + +The clock-provider interface is needed because pwm-clock cannot accurately +represent the bypass path. For example, pwm-clock represents 24 MHz as an +integer 42 ns PWM period. The PWM waveform-rounding rules happen to select +the H616 bypass and produce 24 MHz for that request, but the pwm-clock API +does not require its advertised clock rate to equal the rounded hardware +waveform. Exposing the bypass through CCF provides an exact rate request, +conveys that duty-cycle and polarity control are not needed, and makes the +shared pair clock visible to CCF rate arbitration. + +Wait for the hardware period-update handshake before replacing PPR. Track +the active and newly programmed periods so normal updates use a tight +timeout, while an unknown firmware update conservatively allows the maximum +hardware period. Scale the sleep interval to keep the number of MMIO polls +bounded even for very long periods. + +Dead-zone mode is not representable by the PWM API. Reject it while either +output in the pair is active, but clear dormant dead-zone enable state when +both outputs are disabled so stale firmware configuration does not +permanently prevent ordinary PWM use. + +Signed-off-by: Richard Genoud +Co-developed-by: James Hilliard +Signed-off-by: James Hilliard +--- +Changes v7 -> v8: + - clear the complete two-bit clock-source selector field + - leave rate and period registers untouched when disabling a channel + - distinguish PWM-owned bypass signals from clock-owned bypass outputs + - round waveforms over all parent, pair-divider, channel-divider and bypass + choices according to the PWM core ordering + - encode constant levels without overflowing the 16-bit active-cycle field + - allocate clock topology state per device and use the fixed parent map + (reported by Sashiko) + - use managed clock registrations for complete probe-error cleanup + (reported by Sashiko and suggested by Philipp) + - deassert reset before registering clocks and keep the reset handle local + (reported by Sashiko and suggested by Philipp) + - arbitrate channel ownership in clock prepare/unprepare and select bypass + only while the clock output is enabled (reported by Sashiko) + - roll back failed PWM requests and serialize channel release + (reported by Sashiko) + - protect the shared pair source while either sibling channel is active + (reported by Sashiko) + - preserve the 65536-tick duty intermediate before polarity conversion + (reported by Sashiko) + - avoid replaying write-one control bits while changing channel settings + (reported by Sashiko) + - keep the hardware-waveform state within the PWM core storage limit + - clarify that either co-packaged AC200 or AC300 PHY uses the bypass clock + - drop Tested-by tags after the clock implementation was reworked + - quantize inverted waveforms in hardware ticks and report their + physical offset + - force released channels off so stale waveforms cannot restart with a + sibling + - track the active and pending cycle for the period-update handshake and + adapt the sleep interval to bound MMIO polling on long periods + - quiesce rate, polarity and bypass transitions before reprogramming the + output + - clear unsupported pulse mode when programming periodic waveforms + - preserve firmware-active outputs through generic unused-clock cleanup, + stop any still-unclaimed channels at sync_state(), reconcile empty pair + gates through CCF, and reject pair retuning while either output is + active + - hold pair-rate exclusivity only while an output is active + - make pair-rate changes transactional and restore a quiesced output on + failure + - clear dormant dead-zone state when both pair outputs are disabled, + while rejecting active dead-zone and pulse modes which the PWM API + cannot represent + - correct the hardware topology to show the pair gate before div_m + - remove unused register definitions and simplify the fixed parent + topology +--- + drivers/pwm/Kconfig | 12 + + drivers/pwm/Makefile | 1 + + drivers/pwm/pwm-sun8i.c | 1542 +++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1555 insertions(+) + +diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig +index e8886a9b64d9..e7fd2e66b1aa 100644 +--- a/drivers/pwm/Kconfig ++++ b/drivers/pwm/Kconfig +@@ -748,6 +748,18 @@ config PWM_SUN4I + To compile this driver as a module, choose M here: the module + will be called pwm-sun4i. + ++config PWM_SUN8I ++ tristate "Allwinner sun8i/sun50i PWM support" ++ depends on ARCH_SUNXI || COMPILE_TEST ++ depends on HAS_IOMEM && COMMON_CLK ++ help ++ PWM framework driver for Allwinner controllers whose channels share ++ paired source clocks. In addition to PWM operation, each channel's ++ hardware bypass path can be exported as a clock output. ++ ++ To compile this driver as a module, choose M here: the module ++ will be called pwm-sun8i. ++ + config PWM_SUNPLUS + tristate "Sunplus PWM support" + depends on ARCH_SUNPLUS || COMPILE_TEST +diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile +index 5630a521a7cf..9c922b1fd0b4 100644 +--- a/drivers/pwm/Makefile ++++ b/drivers/pwm/Makefile +@@ -68,6 +68,7 @@ obj-$(CONFIG_PWM_STM32) += pwm-stm32.o + obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o + obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o + obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o ++obj-$(CONFIG_PWM_SUN8I) += pwm-sun8i.o + obj-$(CONFIG_PWM_SUNPLUS) += pwm-sunplus.o + obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o + obj-$(CONFIG_PWM_TH1520) += pwm_th1520.o +diff --git a/drivers/pwm/pwm-sun8i.c b/drivers/pwm/pwm-sun8i.c +new file mode 100644 +index 000000000000..5c3580a0924d +--- /dev/null ++++ b/drivers/pwm/pwm-sun8i.c +@@ -0,0 +1,1542 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Driver for Allwinner sun8i Pulse Width Modulation Controller ++ * ++ * (C) Copyright 2025 Richard Genoud, Bootlin ++ * (C) Copyright 2026 James Hilliard ++ * ++ * Based on drivers/pwm/pwm-sun4i.c with Copyright: ++ * ++ * Copyright (C) 2014 Alexandre Belloni ++ * ++ * Channels are paired (0/1, 2/3, 4/5). Each pair shares a clock source and ++ * first prescaler (div_m), while each channel has its own second prescaler ++ * (div_k) and bypass path. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* PWMCC Pairs Clock Configuration Registers */ ++#define SUN8I_PWM_PCCR(pair) (0x20 + ((pair) * 0x4)) ++#define SUN8I_PWM_PCCR_SRC_SHIFT 7 ++#define SUN8I_PWM_PCCR_SRC_MASK GENMASK(1, 0) ++#define SUN8I_PWM_PCCR_GATE_BIT 4 ++#define SUN8I_PWM_PCCR_GATE BIT(SUN8I_PWM_PCCR_GATE_BIT) ++#define SUN8I_PWM_PCCR_BYPASS_BIT(chan) ((chan) % 2 + 5) ++#define SUN8I_PWM_PCCR_DIV_M_SHIFT 0 ++#define SUN8I_PWM_PCCR_DIV_M_WIDTH 4 ++ ++/* PWM Enable Register */ ++#define SUN8I_PWM_PER 0x40 ++#define SUN8I_PWM_ENABLE(chan) BIT(chan) ++ ++/* PWM Control Register */ ++#define SUN8I_PWM_PCR(chan) (0x60 + (chan) * 0x20) ++#define SUN8I_PWM_PCR_PRESCAL_K_MASK GENMASK(7, 0) ++#define SUN8I_PWM_PCR_ACTIVE_STATE BIT(8) ++#define SUN8I_PWM_PCR_MODE BIT(9) ++#define SUN8I_PWM_PCR_PULSE_START BIT(10) ++#define SUN8I_PWM_PCR_PERIOD_READY BIT(11) /* 0: ready, 1: busy */ ++ ++/* PWM Period Register */ ++#define SUN8I_PWM_PPR(chan) (0x64 + (chan) * 0x20) ++#define SUN8I_PWM_PPR_PERIOD_MASK GENMASK(31, 16) ++#define SUN8I_PWM_PPR_DUTY_MASK GENMASK(15, 0) ++#define SUN8I_PWM_PPR_PERIOD_VALUE(reg) (FIELD_GET(SUN8I_PWM_PPR_PERIOD_MASK, reg) + 1) ++#define SUN8I_PWM_PPR_DUTY_VALUE(reg) FIELD_GET(SUN8I_PWM_PPR_DUTY_MASK, reg) ++#define SUN8I_PWM_PPR_PERIOD(prd) FIELD_PREP(SUN8I_PWM_PPR_PERIOD_MASK, (prd) - 1) ++#define SUN8I_PWM_PPR_DUTY(dty) FIELD_PREP(SUN8I_PWM_PPR_DUTY_MASK, dty) ++#define SUN8I_PWM_PPR_PERIOD_MAX (FIELD_MAX(SUN8I_PWM_PPR_PERIOD_MASK) + 1) ++ ++/* PWM pair dead-zone control registers. */ ++#define SUN8I_PWM_PDZCR(pair) (0x30 + ((pair) * 0x4)) ++#define SUN8I_PWM_PDZCR_ENABLE BIT(0) ++ ++#define SUN8I_PWM_PERIOD_READY_MARGIN_US 50 ++#define SUN8I_PWM_PERIOD_READY_MIN_POLL_US 10 ++#define SUN8I_PWM_PERIOD_READY_MAX_POLL_US 10000 ++#define SUN8I_PWM_PERIOD_READY_POLL_COUNT 1024 ++ ++#define SUN8I_PWM_PAIR_IDX(chan) ((chan) >> 1) ++ ++/* ++ * Block diagram of the PWM clock controller: ++ * ++ * _____ ______ ________ ++ * OSC24M --->| | | | | | ++ * APB1 ----->| Mux |--->| Gate |--->| /div_m |-----> SUN8I_PWM_clock_src_xy ++ * |_____| |______| |________| ++ * ________ ++ * | | ++ * +->| /div_k |---> SUN8I_PWM_clock_x ++ * | |________| ++ * | ______ ++ * | | | ++ * +-->| Gate |----> SUN8I_PWM_bypass_clock_x ++ * | |______| ++ * SUN8I_PWM_clock_src_xy ---+ ________ ++ * | | | ++ * +->| /div_k |---> SUN8I_PWM_clock_y ++ * | |________| ++ * | ______ ++ * | | | ++ * +-->| Gate |----> SUN8I_PWM_bypass_clock_y ++ * |______| ++ * ++ * NB: when the bypass is set, all the PWM logic is bypassed. ++ * So, the duty cycle and polarity can't be modified (we just have a clock). ++ * The bypass in PWM mode is used to achieve a 1/2 relative duty cycle with the ++ * fastest clock. ++ * ++ * SUN8I_PWM_clock_x/y serve for the PWM purpose. ++ * SUN8I_PWM_bypass_clock_x/y serve for the clock-provider purpose. ++ * ++ */ ++ ++/* /div_m is a power-of-two divider limited to /256. */ ++static const struct clk_div_table sun8i_pwm_div_m_table[] = { ++ { .val = 0, .div = 1 }, ++ { .val = 1, .div = 2 }, ++ { .val = 2, .div = 4 }, ++ { .val = 3, .div = 8 }, ++ { .val = 4, .div = 16 }, ++ { .val = 5, .div = 32 }, ++ { .val = 6, .div = 64 }, ++ { .val = 7, .div = 128 }, ++ { .val = 8, .div = 256 }, ++ { /* sentinel */ } ++}; ++ ++enum sun8i_pwm_mode { ++ SUN8I_PWM_MODE_NONE, ++ SUN8I_PWM_MODE_PWM, ++ SUN8I_PWM_MODE_CLK, ++}; ++ ++struct sun8i_pwm_data { ++ unsigned int npwm; ++}; ++ ++struct sun8i_pwm_chip; ++ ++struct sun8i_pwm_pair { ++ struct clk_mux mux; ++ struct clk_hw gate_hw; ++ struct clk_divider divider; ++ struct clk_hw *hw; ++ struct notifier_block rate_nb; ++ struct sun8i_pwm_chip *chip; ++ unsigned int index; ++}; ++ ++struct sun8i_pwm_bypass { ++ struct clk_hw hw; ++ struct sun8i_pwm_chip *chip; ++ unsigned int index; ++}; ++ ++struct sun8i_pwm_channel { ++ struct sun8i_pwm_bypass bypass; ++ struct clk *pair_clk; ++ enum sun8i_pwm_mode mode; ++ u64 pending_period_ns; ++ bool rate_exclusive; ++}; ++ ++struct sun8i_pwm_chip { ++ struct clk_hw_onecell_data *hw_data; ++ struct sun8i_pwm_pair *pairs; ++ struct sun8i_pwm_channel *channels; ++ struct clk *bus_clk; ++ void __iomem *base; ++ const struct sun8i_pwm_data *data; ++ /* Protects shared registers and channel ownership. */ ++ spinlock_t lock; ++}; ++ ++struct sun8i_pwm_waveform { ++ u32 duty_ticks; ++ u32 period_ticks; ++ u32 pair_rate; ++ u16 div_k; ++ u8 enabled:1; ++ u8 active_state:1; ++ u8 bypass_en:1; ++}; ++ ++static inline struct sun8i_pwm_chip *sun8i_pwm_from_chip(const struct pwm_chip *chip) ++{ ++ return pwmchip_get_drvdata(chip); ++} ++ ++static inline u32 sun8i_pwm_readl(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned long offset) ++{ ++ return readl(sun8i_chip->base + offset); ++} ++ ++static inline void sun8i_pwm_writel(struct sun8i_pwm_chip *sun8i_chip, ++ u32 val, unsigned long offset) ++{ ++ writel(val, sun8i_chip->base + offset); ++} ++ ++static void sun8i_pwm_set_bypass_locked(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned int idx, bool enable) ++{ ++ unsigned long reg_offset; ++ u32 val; ++ ++ lockdep_assert_held(&sun8i_chip->lock); ++ ++ reg_offset = SUN8I_PWM_PCCR(SUN8I_PWM_PAIR_IDX(idx)); ++ val = sun8i_pwm_readl(sun8i_chip, reg_offset); ++ if (enable) ++ val |= BIT(SUN8I_PWM_PCCR_BYPASS_BIT(idx)); ++ else ++ val &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(idx)); ++ ++ sun8i_pwm_writel(sun8i_chip, val, reg_offset); ++} ++ ++static void sun8i_pwm_set_enabled_locked(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned int idx, bool enable) ++{ ++ u32 val; ++ ++ lockdep_assert_held(&sun8i_chip->lock); ++ ++ val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); ++ if (enable) ++ val |= SUN8I_PWM_ENABLE(idx); ++ else ++ val &= ~SUN8I_PWM_ENABLE(idx); ++ sun8i_pwm_writel(sun8i_chip, val, SUN8I_PWM_PER); ++} ++ ++static bool ++sun8i_pwm_channel_is_enabled_locked(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned int idx) ++{ ++ unsigned int pair = SUN8I_PWM_PAIR_IDX(idx); ++ u32 pccr, per; ++ ++ lockdep_assert_held(&sun8i_chip->lock); ++ ++ pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); ++ per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); ++ ++ return (pccr & SUN8I_PWM_PCCR_GATE) && ++ (per & SUN8I_PWM_ENABLE(idx)); ++} ++ ++static u32 sun8i_pwm_pair_enable_mask(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned int pair) ++{ ++ unsigned int first = pair * 2; ++ u32 mask = SUN8I_PWM_ENABLE(first); ++ ++ if (first + 1 < sun8i_chip->data->npwm) ++ mask |= SUN8I_PWM_ENABLE(first + 1); ++ ++ return mask; ++} ++ ++static u32 ++sun8i_pwm_pair_owner_mask_locked(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned int pair) ++{ ++ unsigned int first = pair * 2; ++ u32 mask = 0; ++ ++ lockdep_assert_held(&sun8i_chip->lock); ++ ++ if (sun8i_chip->channels[first].mode != SUN8I_PWM_MODE_NONE) ++ mask |= SUN8I_PWM_ENABLE(first); ++ if (first + 1 < sun8i_chip->data->npwm && ++ sun8i_chip->channels[first + 1].mode != SUN8I_PWM_MODE_NONE) ++ mask |= SUN8I_PWM_ENABLE(first + 1); ++ ++ return mask; ++} ++ ++static inline struct sun8i_pwm_pair * ++sun8i_pwm_pair_from_gate_hw(struct clk_hw *hw) ++{ ++ return container_of(hw, struct sun8i_pwm_pair, gate_hw); ++} ++ ++static int sun8i_pwm_pair_gate_enable(struct clk_hw *hw) ++{ ++ struct sun8i_pwm_pair *pair = sun8i_pwm_pair_from_gate_hw(hw); ++ struct sun8i_pwm_chip *sun8i_chip = pair->chip; ++ unsigned long flags; ++ u32 pccr; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair->index)); ++ pccr |= SUN8I_PWM_PCCR_GATE; ++ sun8i_pwm_writel(sun8i_chip, pccr, SUN8I_PWM_PCCR(pair->index)); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return 0; ++} ++ ++static void sun8i_pwm_pair_gate_disable(struct clk_hw *hw) ++{ ++ struct sun8i_pwm_pair *pair = sun8i_pwm_pair_from_gate_hw(hw); ++ struct sun8i_pwm_chip *sun8i_chip = pair->chip; ++ unsigned long reg = SUN8I_PWM_PCCR(pair->index); ++ unsigned long flags; ++ u32 pccr, per; ++ ++ /* CCF counts do not include outputs awaiting firmware-state handoff. */ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); ++ if (!(per & sun8i_pwm_pair_enable_mask(sun8i_chip, pair->index))) { ++ pccr = sun8i_pwm_readl(sun8i_chip, reg); ++ pccr &= ~SUN8I_PWM_PCCR_GATE; ++ sun8i_pwm_writel(sun8i_chip, pccr, reg); ++ } ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++} ++ ++static int sun8i_pwm_pair_gate_is_enabled(struct clk_hw *hw) ++{ ++ struct sun8i_pwm_pair *pair = sun8i_pwm_pair_from_gate_hw(hw); ++ struct sun8i_pwm_chip *sun8i_chip = pair->chip; ++ unsigned long reg = SUN8I_PWM_PCCR(pair->index); ++ unsigned long flags; ++ bool enabled; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ enabled = sun8i_pwm_readl(sun8i_chip, reg) & SUN8I_PWM_PCCR_GATE; ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return enabled; ++} ++ ++static const struct clk_ops sun8i_pwm_pair_gate_ops = { ++ .enable = sun8i_pwm_pair_gate_enable, ++ .disable = sun8i_pwm_pair_gate_disable, ++ .is_enabled = sun8i_pwm_pair_gate_is_enabled, ++}; ++ ++static int sun8i_pwm_pair_rate_notifier(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ struct sun8i_pwm_pair *pair = ++ container_of(nb, struct sun8i_pwm_pair, rate_nb); ++ struct sun8i_pwm_chip *sun8i_chip = pair->chip; ++ unsigned long flags; ++ bool active; ++ ++ if (event != PRE_RATE_CHANGE) ++ return NOTIFY_DONE; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ active = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER) & ++ sun8i_pwm_pair_enable_mask(sun8i_chip, pair->index); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return active ? NOTIFY_BAD : NOTIFY_OK; ++} ++ ++static inline struct sun8i_pwm_bypass * ++sun8i_pwm_bypass_from_hw(struct clk_hw *hw) ++{ ++ return container_of(hw, struct sun8i_pwm_bypass, hw); ++} ++ ++static int sun8i_pwm_bypass_prepare(struct clk_hw *hw) ++{ ++ struct sun8i_pwm_bypass *bypass = sun8i_pwm_bypass_from_hw(hw); ++ struct sun8i_pwm_chip *sun8i_chip = bypass->chip; ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[bypass->index]; ++ unsigned long flags; ++ int ret = 0; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ if (chan->mode != SUN8I_PWM_MODE_NONE) ++ ret = -EBUSY; ++ else ++ chan->mode = SUN8I_PWM_MODE_CLK; ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return ret; ++} ++ ++static void sun8i_pwm_bypass_unprepare(struct clk_hw *hw) ++{ ++ struct sun8i_pwm_bypass *bypass = sun8i_pwm_bypass_from_hw(hw); ++ struct sun8i_pwm_chip *sun8i_chip = bypass->chip; ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[bypass->index]; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ if (WARN_ON_ONCE(chan->mode != SUN8I_PWM_MODE_CLK)) ++ goto out_unlock; ++ ++ chan->mode = SUN8I_PWM_MODE_NONE; ++ ++out_unlock: ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++} ++ ++static int sun8i_pwm_bypass_enable(struct clk_hw *hw) ++{ ++ struct sun8i_pwm_bypass *bypass = sun8i_pwm_bypass_from_hw(hw); ++ struct sun8i_pwm_chip *sun8i_chip = bypass->chip; ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[bypass->index]; ++ unsigned int pair = SUN8I_PWM_PAIR_IDX(bypass->index); ++ unsigned long flags; ++ u32 pccr, per; ++ int ret = 0; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ if (WARN_ON_ONCE(chan->mode != SUN8I_PWM_MODE_CLK)) { ++ ret = -EBUSY; ++ goto out_unlock; ++ } ++ ++ pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); ++ per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); ++ if (!(pccr & BIT(SUN8I_PWM_PCCR_BYPASS_BIT(bypass->index))) || ++ !(per & SUN8I_PWM_ENABLE(bypass->index))) { ++ sun8i_pwm_set_enabled_locked(sun8i_chip, bypass->index, false); ++ sun8i_pwm_set_bypass_locked(sun8i_chip, bypass->index, true); ++ sun8i_pwm_set_enabled_locked(sun8i_chip, bypass->index, true); ++ } ++ ++out_unlock: ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return ret; ++} ++ ++static void sun8i_pwm_bypass_disable(struct clk_hw *hw) ++{ ++ struct sun8i_pwm_bypass *bypass = sun8i_pwm_bypass_from_hw(hw); ++ struct sun8i_pwm_chip *sun8i_chip = bypass->chip; ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[bypass->index]; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ if (WARN_ON_ONCE(chan->mode != SUN8I_PWM_MODE_CLK)) ++ goto out_unlock; ++ ++ sun8i_pwm_set_enabled_locked(sun8i_chip, bypass->index, false); ++ sun8i_pwm_set_bypass_locked(sun8i_chip, bypass->index, false); ++ ++out_unlock: ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++} ++ ++static void sun8i_pwm_bypass_disable_unused(struct clk_hw *hw) ++{ ++ /* Defer inherited-output cleanup to sun8i_pwm_sync_state(). */ ++} ++ ++static int sun8i_pwm_bypass_is_enabled(struct clk_hw *hw) ++{ ++ struct sun8i_pwm_bypass *bypass = sun8i_pwm_bypass_from_hw(hw); ++ struct sun8i_pwm_chip *sun8i_chip = bypass->chip; ++ unsigned int pair = SUN8I_PWM_PAIR_IDX(bypass->index); ++ unsigned long flags; ++ bool enabled; ++ u32 val; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ ++ val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); ++ enabled = (val & SUN8I_PWM_PCCR_GATE) && ++ (val & BIT(SUN8I_PWM_PCCR_BYPASS_BIT(bypass->index))); ++ ++ val = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); ++ enabled = enabled && (val & SUN8I_PWM_ENABLE(bypass->index)); ++ ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return enabled; ++} ++ ++static unsigned long sun8i_pwm_bypass_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ return parent_rate; ++} ++ ++static const struct clk_ops sun8i_pwm_bypass_ops = { ++ .prepare = sun8i_pwm_bypass_prepare, ++ .unprepare = sun8i_pwm_bypass_unprepare, ++ .enable = sun8i_pwm_bypass_enable, ++ .disable = sun8i_pwm_bypass_disable, ++ .disable_unused = sun8i_pwm_bypass_disable_unused, ++ .is_enabled = sun8i_pwm_bypass_is_enabled, ++ .recalc_rate = sun8i_pwm_bypass_recalc_rate, ++}; ++ ++static int sun8i_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm]; ++ unsigned int idx = pwm->hwpwm; ++ unsigned long flags; ++ bool was_enabled = false; ++ int ret; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ if (chan->mode != SUN8I_PWM_MODE_NONE) { ++ ret = -EBUSY; ++ } else { ++ was_enabled = ++ sun8i_pwm_channel_is_enabled_locked(sun8i_chip, idx); ++ chan->mode = SUN8I_PWM_MODE_PWM; ++ ret = 0; ++ } ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ if (ret) ++ return ret; ++ ++ if (was_enabled) { ++ ret = clk_rate_exclusive_get(chan->pair_clk); ++ if (ret) ++ goto err_release_channel; ++ WRITE_ONCE(chan->rate_exclusive, true); ++ } ++ ++ ret = clk_prepare_enable(chan->pair_clk); ++ if (ret) { ++ if (READ_ONCE(chan->rate_exclusive)) { ++ clk_rate_exclusive_put(chan->pair_clk); ++ WRITE_ONCE(chan->rate_exclusive, false); ++ } ++ goto err_release_channel; ++ } ++ ++ return 0; ++ ++err_release_channel: ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ chan->mode = SUN8I_PWM_MODE_NONE; ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return ret; ++} ++ ++static void sun8i_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm]; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ if (WARN_ON_ONCE(chan->mode != SUN8I_PWM_MODE_PWM)) { ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ return; ++ } ++ ++ sun8i_pwm_set_enabled_locked(sun8i_chip, pwm->hwpwm, false); ++ sun8i_pwm_set_bypass_locked(sun8i_chip, pwm->hwpwm, false); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ if (READ_ONCE(chan->rate_exclusive)) { ++ clk_rate_exclusive_put(chan->pair_clk); ++ WRITE_ONCE(chan->rate_exclusive, false); ++ } ++ clk_disable_unprepare(chan->pair_clk); ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ chan->mode = SUN8I_PWM_MODE_NONE; ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++} ++ ++static int sun8i_pwm_read_waveform(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ void *_wfhw) ++{ ++ struct sun8i_pwm_waveform *wfhw = _wfhw; ++ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm]; ++ unsigned int pair = SUN8I_PWM_PAIR_IDX(pwm->hwpwm); ++ unsigned long flags, pair_rate; ++ u32 pccr, pcr, pdzcr, per, ppr; ++ ++ pair_rate = clk_get_rate(chan->pair_clk); ++ if (pair_rate > U32_MAX) ++ return -ERANGE; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); ++ per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); ++ pcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(pwm->hwpwm)); ++ ppr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PPR(pwm->hwpwm)); ++ pdzcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PDZCR(pair)); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ *wfhw = (struct sun8i_pwm_waveform) { ++ .enabled = !!(pccr & SUN8I_PWM_PCCR_GATE) && ++ !!(per & SUN8I_PWM_ENABLE(pwm->hwpwm)), ++ .bypass_en = !!(pccr & ++ BIT(SUN8I_PWM_PCCR_BYPASS_BIT(pwm->hwpwm))), ++ .active_state = !!(pcr & SUN8I_PWM_PCR_ACTIVE_STATE), ++ .duty_ticks = SUN8I_PWM_PPR_DUTY_VALUE(ppr), ++ .period_ticks = SUN8I_PWM_PPR_PERIOD_VALUE(ppr), ++ .pair_rate = pair_rate, ++ .div_k = FIELD_GET(SUN8I_PWM_PCR_PRESCAL_K_MASK, pcr) + 1, ++ }; ++ ++ if (wfhw->enabled && !wfhw->bypass_en && ++ (pdzcr & SUN8I_PWM_PDZCR_ENABLE)) ++ return -EOPNOTSUPP; ++ if (wfhw->enabled && !wfhw->bypass_en && ++ (pcr & SUN8I_PWM_PCR_MODE)) ++ return -EOPNOTSUPP; ++ ++ return 0; ++} ++ ++static u64 sun8i_pwm_ticks_to_ns(u32 ticks, u16 div_k, u32 pair_rate) ++{ ++ return DIV_ROUND_UP_ULL(NSEC_PER_SEC * (u64)ticks * div_k, ++ pair_rate); ++} ++ ++static void ++sun8i_pwm_waveform_to_ns(const struct sun8i_pwm_waveform *wfhw, ++ struct pwm_waveform *wf) ++{ ++ u32 pair_rate = wfhw->pair_rate; ++ u16 div_k = wfhw->div_k; ++ ++ wf->duty_offset_ns = 0; ++ ++ if (!wfhw->enabled || !pair_rate) { ++ wf->period_length_ns = 0; ++ wf->duty_length_ns = 0; ++ return; ++ } ++ ++ if (wfhw->bypass_en) { ++ wf->period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, ++ pair_rate); ++ wf->duty_length_ns = ++ DIV_ROUND_UP_ULL(NSEC_PER_SEC, ++ 2ULL * pair_rate); ++ } else { ++ wf->period_length_ns = sun8i_pwm_ticks_to_ns(wfhw->period_ticks, ++ div_k, pair_rate); ++ if (wfhw->active_state) { ++ u32 duty_ticks = min(wfhw->duty_ticks, ++ wfhw->period_ticks); ++ ++ wf->duty_length_ns = ++ sun8i_pwm_ticks_to_ns(duty_ticks, div_k, pair_rate); ++ } else if (!wfhw->duty_ticks) { ++ wf->duty_length_ns = wf->period_length_ns; ++ } else if (wfhw->duty_ticks >= wfhw->period_ticks) { ++ wf->duty_length_ns = 0; ++ } else { ++ u32 low_ticks = wfhw->duty_ticks; ++ u32 high_ticks = wfhw->period_ticks - wfhw->duty_ticks; ++ ++ wf->duty_length_ns = ++ sun8i_pwm_ticks_to_ns(high_ticks, div_k, pair_rate); ++ wf->duty_offset_ns = ++ sun8i_pwm_ticks_to_ns(low_ticks, div_k, pair_rate); ++ } ++ } ++} ++ ++static int sun8i_pwm_round_waveform_fromhw(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const void *_wfhw, ++ struct pwm_waveform *wf) ++{ ++ const struct sun8i_pwm_waveform *wfhw = _wfhw; ++ ++ sun8i_pwm_waveform_to_ns(wfhw, wf); ++ ++ dev_dbg(pwmchip_parent(chip), ++ "pwm#%u: pair-rate=%u, div-k=%u, period=%u, duty=%u, bypass=%u, active-high=%u -> %llu/%llu [+%llu]\n", ++ pwm->hwpwm, wfhw->pair_rate, wfhw->div_k, ++ wfhw->period_ticks, wfhw->duty_ticks, wfhw->bypass_en, ++ wfhw->active_state, ++ wf->duty_length_ns, wf->period_length_ns, ++ wf->duty_offset_ns); ++ ++ return 0; ++} ++ ++static bool ++sun8i_pwm_pair_rate_constrained(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned int idx) ++{ ++ unsigned int sibling = idx ^ 1; ++ unsigned long flags; ++ bool constrained; ++ ++ if (sibling >= sun8i_chip->data->npwm) ++ return false; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ constrained = sun8i_chip->channels[sibling].mode == ++ SUN8I_PWM_MODE_CLK || ++ READ_ONCE(sun8i_chip->channels[sibling].rate_exclusive) || ++ sun8i_pwm_channel_is_enabled_locked(sun8i_chip, ++ sibling); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return constrained; ++} ++ ++static int sun8i_pwm_clear_idle_deadzone(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned int idx) ++{ ++ unsigned int pair = SUN8I_PWM_PAIR_IDX(idx); ++ unsigned long flags; ++ u32 pdzcr, per; ++ int ret = 0; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ pdzcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PDZCR(pair)); ++ if (!(pdzcr & SUN8I_PWM_PDZCR_ENABLE)) ++ goto out_unlock; ++ ++ per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); ++ if (per & sun8i_pwm_pair_enable_mask(sun8i_chip, pair)) { ++ ret = -EOPNOTSUPP; ++ goto out_unlock; ++ } ++ ++ pdzcr &= ~SUN8I_PWM_PDZCR_ENABLE; ++ sun8i_pwm_writel(sun8i_chip, pdzcr, SUN8I_PWM_PDZCR(pair)); ++ ++out_unlock: ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return ret; ++} ++ ++static u64 sun8i_pwm_max_period_ns(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned int idx) ++{ ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[idx]; ++ unsigned long flags, pair_rate; ++ u32 pcr; ++ u16 div_k; ++ ++ pair_rate = clk_get_rate(chan->pair_clk); ++ if (!pair_rate || pair_rate > U32_MAX) ++ return 0; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ pcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(idx)); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ div_k = FIELD_GET(SUN8I_PWM_PCR_PRESCAL_K_MASK, pcr) + 1; ++ ++ return sun8i_pwm_ticks_to_ns(SUN8I_PWM_PPR_PERIOD_MAX, div_k, ++ pair_rate); ++} ++ ++static int sun8i_pwm_wait_period_ready(struct sun8i_pwm_chip *sun8i_chip, ++ unsigned int idx, u64 *timeout_us) ++{ ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[idx]; ++ unsigned long poll_us; ++ u64 period_ns; ++ u32 val; ++ int ret; ++ ++ /* PPR contains the pending value, not necessarily the active period. */ ++ period_ns = chan->pending_period_ns; ++ if (!period_ns) ++ period_ns = sun8i_pwm_max_period_ns(sun8i_chip, idx); ++ *timeout_us = DIV_ROUND_UP_ULL(period_ns, NSEC_PER_USEC) + ++ SUN8I_PWM_PERIOD_READY_MARGIN_US; ++ poll_us = clamp_t(u64, ++ DIV_ROUND_UP_ULL(*timeout_us, ++ SUN8I_PWM_PERIOD_READY_POLL_COUNT), ++ SUN8I_PWM_PERIOD_READY_MIN_POLL_US, ++ SUN8I_PWM_PERIOD_READY_MAX_POLL_US); ++ ++ ret = readl_poll_timeout(sun8i_chip->base + SUN8I_PWM_PCR(idx), val, ++ !(val & SUN8I_PWM_PCR_PERIOD_READY), poll_us, ++ *timeout_us); ++ if (!ret) ++ chan->pending_period_ns = 0; ++ ++ return ret; ++} ++ ++static int sun8i_pwm_write_waveform(struct pwm_chip *chip, ++ struct pwm_device *pwm, const void *_wfhw) ++{ ++ const struct sun8i_pwm_waveform *wfhw = _wfhw; ++ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm]; ++ struct device *dev = pwmchip_parent(chip); ++ unsigned long bus_rate, current_rate, restored_rate; ++ unsigned long flags; ++ u64 new_ns, old_ns, timeout_us; ++ bool acquired_exclusive = false; ++ bool can_restore_enable = true; ++ bool current_bypass, needs_quiesce, was_enabled; ++ u16 current_div_k; ++ u32 old_ticks, pcr, ppr, val; ++ int restore_ret, ret; ++ ++ if (!wfhw->enabled) { ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ sun8i_pwm_set_enabled_locked(sun8i_chip, pwm->hwpwm, false); ++ sun8i_pwm_set_bypass_locked(sun8i_chip, pwm->hwpwm, false); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ if (READ_ONCE(chan->rate_exclusive)) { ++ clk_rate_exclusive_put(chan->pair_clk); ++ WRITE_ONCE(chan->rate_exclusive, false); ++ } ++ ++ return 0; ++ } ++ ++ if (!wfhw->pair_rate || ++ (!wfhw->bypass_en && ++ (!wfhw->div_k || wfhw->div_k > 256 || ++ !wfhw->period_ticks || ++ wfhw->period_ticks > SUN8I_PWM_PPR_PERIOD_MAX || ++ wfhw->duty_ticks > FIELD_MAX(SUN8I_PWM_PPR_DUTY_MASK)))) ++ return -EINVAL; ++ ++ if (!wfhw->bypass_en) { ++ ret = sun8i_pwm_clear_idle_deadzone(sun8i_chip, pwm->hwpwm); ++ if (ret) ++ return ret; ++ } ++ ++ current_rate = clk_get_rate(chan->pair_clk); ++ if (!current_rate || current_rate > U32_MAX) ++ return -ERANGE; ++ bus_rate = clk_get_rate(sun8i_chip->bus_clk); ++ ++ if (!wfhw->bypass_en) { ++ /* Do not overwrite a PPR update which has not latched yet. */ ++ ret = sun8i_pwm_wait_period_ready(sun8i_chip, pwm->hwpwm, ++ &timeout_us); ++ if (ret) { ++ dev_err_ratelimited(dev, "pwm#%u: update timeout after %llu us\n", ++ pwm->hwpwm, timeout_us); ++ return ret; ++ } ++ } ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ was_enabled = sun8i_pwm_channel_is_enabled_locked(sun8i_chip, ++ pwm->hwpwm); ++ val = sun8i_pwm_readl(sun8i_chip, ++ SUN8I_PWM_PCCR(SUN8I_PWM_PAIR_IDX(pwm->hwpwm))); ++ current_bypass = !!(val & ++ BIT(SUN8I_PWM_PCCR_BYPASS_BIT(pwm->hwpwm))); ++ pcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCR(pwm->hwpwm)); ++ ppr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PPR(pwm->hwpwm)); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ current_div_k = FIELD_GET(SUN8I_PWM_PCR_PRESCAL_K_MASK, pcr) + 1; ++ ++ if (current_rate != wfhw->pair_rate && ++ sun8i_pwm_pair_rate_constrained(sun8i_chip, pwm->hwpwm)) ++ return -EBUSY; ++ ++ if (was_enabled && !READ_ONCE(chan->rate_exclusive)) { ++ ret = clk_rate_exclusive_get(chan->pair_clk); ++ if (ret) ++ return ret; ++ WRITE_ONCE(chan->rate_exclusive, true); ++ } ++ ++ /* ++ * Updating PPR while running is safe only if the input clocks and ++ * polarity remain unchanged and PCLK is faster than the PWM clock. ++ * Otherwise stop the channel before touching its configuration. ++ */ ++ needs_quiesce = was_enabled && ++ (current_bypass != wfhw->bypass_en || ++ current_rate != wfhw->pair_rate || ++ (!wfhw->bypass_en && ++ (!!(pcr & SUN8I_PWM_PCR_ACTIVE_STATE) != ++ wfhw->active_state || ++ current_div_k != wfhw->div_k || ++ (pcr & SUN8I_PWM_PCR_MODE) || ++ !bus_rate || ++ bus_rate <= DIV_ROUND_UP_ULL(wfhw->pair_rate, ++ wfhw->div_k)))); ++ ++ if (needs_quiesce) { ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ sun8i_pwm_set_enabled_locked(sun8i_chip, pwm->hwpwm, false); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ } ++ ++ if (!was_enabled && !READ_ONCE(chan->rate_exclusive)) { ++ if (current_rate == wfhw->pair_rate) ++ ret = clk_rate_exclusive_get(chan->pair_clk); ++ else ++ ret = clk_set_rate_exclusive(chan->pair_clk, ++ wfhw->pair_rate); ++ if (ret) ++ return ret; ++ WRITE_ONCE(chan->rate_exclusive, true); ++ acquired_exclusive = true; ++ } else if (current_rate != wfhw->pair_rate) { ++ ret = clk_set_rate(chan->pair_clk, wfhw->pair_rate); ++ if (ret) ++ goto restore_enable; ++ } ++ ++ if (clk_get_rate(chan->pair_clk) != wfhw->pair_rate) { ++ ret = -EINVAL; ++ goto restore_rate; ++ } ++ ++ if (!wfhw->bypass_en) { ++ /* Return the channel to cycle mode without replaying W1S bits. */ ++ pcr &= ~(SUN8I_PWM_PCR_PRESCAL_K_MASK | ++ SUN8I_PWM_PCR_ACTIVE_STATE | SUN8I_PWM_PCR_MODE | ++ SUN8I_PWM_PCR_PULSE_START | ++ SUN8I_PWM_PCR_PERIOD_READY); ++ pcr |= FIELD_PREP(SUN8I_PWM_PCR_PRESCAL_K_MASK, ++ wfhw->div_k - 1); ++ if (wfhw->active_state) ++ pcr |= SUN8I_PWM_PCR_ACTIVE_STATE; ++ sun8i_pwm_writel(sun8i_chip, pcr, SUN8I_PWM_PCR(pwm->hwpwm)); ++ ++ val = SUN8I_PWM_PPR_DUTY(wfhw->duty_ticks); ++ val |= SUN8I_PWM_PPR_PERIOD(wfhw->period_ticks); ++ old_ns = 0; ++ if (was_enabled && !current_bypass) { ++ old_ticks = SUN8I_PWM_PPR_PERIOD_VALUE(ppr); ++ old_ns = sun8i_pwm_ticks_to_ns(old_ticks, current_div_k, ++ current_rate); ++ } ++ new_ns = sun8i_pwm_ticks_to_ns(wfhw->period_ticks, ++ wfhw->div_k, wfhw->pair_rate); ++ chan->pending_period_ns = max(old_ns, new_ns); ++ sun8i_pwm_writel(sun8i_chip, val, SUN8I_PWM_PPR(pwm->hwpwm)); ++ } ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ sun8i_pwm_set_bypass_locked(sun8i_chip, pwm->hwpwm, wfhw->bypass_en); ++ sun8i_pwm_set_enabled_locked(sun8i_chip, pwm->hwpwm, true); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ return 0; ++ ++restore_rate: ++ if (current_rate != wfhw->pair_rate) { ++ restore_ret = clk_set_rate(chan->pair_clk, current_rate); ++ restored_rate = clk_get_rate(chan->pair_clk); ++ if (restore_ret || restored_rate != current_rate) { ++ dev_err(dev, ++ "pwm#%u: failed to restore pair clock rate %lu: %d (got %lu)\n", ++ pwm->hwpwm, current_rate, restore_ret, ++ restored_rate); ++ can_restore_enable = false; ++ } ++ } ++ if (acquired_exclusive) { ++ clk_rate_exclusive_put(chan->pair_clk); ++ WRITE_ONCE(chan->rate_exclusive, false); ++ } ++restore_enable: ++ if (needs_quiesce && can_restore_enable) { ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ sun8i_pwm_set_enabled_locked(sun8i_chip, pwm->hwpwm, true); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ } ++ ++ return ret; ++} ++ ++struct sun8i_pwm_rounding { ++ const struct pwm_waveform *requested; ++ struct sun8i_pwm_waveform best; ++ struct pwm_waveform best_wf; ++ u32 current_pair_rate; ++ bool have_best; ++}; ++ ++static bool ++sun8i_pwm_candidate_is_better(const struct sun8i_pwm_rounding *rounding, ++ const struct sun8i_pwm_waveform *candidate, ++ const struct pwm_waveform *candidate_wf) ++{ ++ const struct pwm_waveform *requested = rounding->requested; ++ bool candidate_period_down, best_period_down; ++ ++ /* A zero-duty, zero-offset cycle-mode candidate always exists. */ ++ if (candidate_wf->duty_length_ns > requested->duty_length_ns || ++ candidate_wf->duty_offset_ns > requested->duty_offset_ns) ++ return false; ++ ++ if (!rounding->have_best) ++ return true; ++ ++ candidate_period_down = ++ candidate_wf->period_length_ns <= requested->period_length_ns; ++ best_period_down = ++ rounding->best_wf.period_length_ns <= requested->period_length_ns; ++ if (candidate_period_down != best_period_down) ++ return candidate_period_down; ++ ++ if (candidate_wf->period_length_ns != ++ rounding->best_wf.period_length_ns) { ++ if (candidate_period_down) ++ return candidate_wf->period_length_ns > ++ rounding->best_wf.period_length_ns; ++ ++ return candidate_wf->period_length_ns < ++ rounding->best_wf.period_length_ns; ++ } ++ ++ if (candidate_wf->duty_length_ns != ++ rounding->best_wf.duty_length_ns) ++ return candidate_wf->duty_length_ns > ++ rounding->best_wf.duty_length_ns; ++ ++ if (candidate_wf->duty_offset_ns != ++ rounding->best_wf.duty_offset_ns) ++ return candidate_wf->duty_offset_ns > ++ rounding->best_wf.duty_offset_ns; ++ ++ /* Avoid a shared pair-rate change when two settings are equivalent. */ ++ return candidate->pair_rate == rounding->current_pair_rate && ++ rounding->best.pair_rate != rounding->current_pair_rate; ++} ++ ++static void ++sun8i_pwm_consider_candidate(struct sun8i_pwm_rounding *rounding, ++ const struct sun8i_pwm_waveform *candidate) ++{ ++ struct pwm_waveform candidate_wf; ++ ++ sun8i_pwm_waveform_to_ns(candidate, &candidate_wf); ++ if (!sun8i_pwm_candidate_is_better(rounding, candidate, ++ &candidate_wf)) ++ return; ++ ++ rounding->best = *candidate; ++ rounding->best_wf = candidate_wf; ++ rounding->have_best = true; ++} ++ ++static void ++sun8i_pwm_consider_normal(struct sun8i_pwm_rounding *rounding, u32 pair_rate, ++ u16 div_k) ++{ ++ const struct pwm_waveform *requested = rounding->requested; ++ struct sun8i_pwm_waveform candidate = { ++ .enabled = true, ++ .pair_rate = pair_rate, ++ .div_k = div_k, ++ }; ++ u64 denominator = NSEC_PER_SEC * (u64)div_k; ++ u64 duty_ticks, period_ticks; ++ u64 duty_ns = requested->duty_length_ns; ++ u64 period_ns = requested->period_length_ns; ++ u32 inactive_ticks; ++ ++ period_ticks = mul_u64_u64_div_u64(period_ns, pair_rate, denominator); ++ period_ticks = clamp_t(u64, period_ticks, 1, SUN8I_PWM_PPR_PERIOD_MAX); ++ candidate.period_ticks = period_ticks; ++ if (candidate.period_ticks > 1 && ++ sun8i_pwm_ticks_to_ns(candidate.period_ticks, div_k, pair_rate) > ++ requested->period_length_ns) ++ candidate.period_ticks--; ++ ++ duty_ticks = mul_u64_u64_div_u64(duty_ns, pair_rate, denominator); ++ duty_ticks = min_t(u64, duty_ticks, candidate.period_ticks); ++ if (duty_ticks && ++ sun8i_pwm_ticks_to_ns(duty_ticks, div_k, pair_rate) > ++ requested->duty_length_ns) ++ duty_ticks--; ++ ++ /* ++ * Zero active ticks encode either constant level. For a toggling ++ * waveform, active-low mode places the rising edge after the inactive ++ * part, which is the only non-zero offset supported by the hardware. ++ */ ++ if (!duty_ticks) { ++ candidate.active_state = true; ++ candidate.duty_ticks = 0; ++ } else if (duty_ticks == candidate.period_ticks) { ++ candidate.active_state = false; ++ candidate.duty_ticks = 0; ++ } else { ++ inactive_ticks = candidate.period_ticks - duty_ticks; ++ if (sun8i_pwm_ticks_to_ns(inactive_ticks, div_k, pair_rate) <= ++ requested->duty_offset_ns) { ++ candidate.active_state = false; ++ candidate.duty_ticks = inactive_ticks; ++ } else { ++ candidate.active_state = true; ++ candidate.duty_ticks = duty_ticks; ++ } ++ } ++ ++ sun8i_pwm_consider_candidate(rounding, &candidate); ++} ++ ++static void ++sun8i_pwm_consider_pair_rate(struct sun8i_pwm_rounding *rounding, ++ u32 pair_rate) ++{ ++ struct sun8i_pwm_waveform bypass = { ++ .duty_ticks = 1, ++ .period_ticks = 1, ++ .pair_rate = pair_rate, ++ .div_k = 1, ++ .enabled = true, ++ .active_state = true, ++ .bypass_en = true, ++ }; ++ ++ for (unsigned int div_k = 1; div_k <= 256; div_k++) ++ sun8i_pwm_consider_normal(rounding, pair_rate, div_k); ++ ++ sun8i_pwm_consider_candidate(rounding, &bypass); ++} ++ ++static int sun8i_pwm_round_waveform_tohw(struct pwm_chip *chip, ++ struct pwm_device *pwm, ++ const struct pwm_waveform *wf, ++ void *_wfhw) ++{ ++ struct sun8i_pwm_chip *sun8i_chip = sun8i_pwm_from_chip(chip); ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[pwm->hwpwm]; ++ struct sun8i_pwm_pair *pair = ++ &sun8i_chip->pairs[SUN8I_PWM_PAIR_IDX(pwm->hwpwm)]; ++ struct sun8i_pwm_waveform *wfhw = _wfhw; ++ struct sun8i_pwm_rounding rounding = { ++ .requested = wf, ++ }; ++ unsigned long current_rate; ++ ++ if (!wf->period_length_ns) { ++ *wfhw = (struct sun8i_pwm_waveform) { ++ .enabled = false, ++ }; ++ return 0; ++ } ++ ++ current_rate = clk_get_rate(chan->pair_clk); ++ if (!current_rate || current_rate > U32_MAX) ++ return -ERANGE; ++ rounding.current_pair_rate = current_rate; ++ ++ if (sun8i_pwm_pair_rate_constrained(sun8i_chip, pwm->hwpwm)) { ++ sun8i_pwm_consider_pair_rate(&rounding, current_rate); ++ } else { ++ for (unsigned int parent_idx = 0; ++ parent_idx < clk_hw_get_num_parents(pair->hw); ++ parent_idx++) { ++ struct clk_hw *parent; ++ unsigned long parent_rate; ++ ++ parent = clk_hw_get_parent_by_index(pair->hw, parent_idx); ++ if (!parent) ++ continue; ++ parent_rate = clk_hw_get_rate(parent); ++ if (!parent_rate) ++ continue; ++ ++ for (unsigned int i = 0; sun8i_pwm_div_m_table[i].div; ++ i++) { ++ u16 div_m = sun8i_pwm_div_m_table[i].div; ++ u64 pair_rate; ++ ++ pair_rate = DIV_ROUND_UP_ULL(parent_rate, div_m); ++ if (!pair_rate || pair_rate > U32_MAX) ++ continue; ++ sun8i_pwm_consider_pair_rate(&rounding, pair_rate); ++ } ++ } ++ } ++ ++ if (!rounding.have_best) ++ return -EINVAL; ++ *wfhw = rounding.best; ++ ++ dev_dbg(pwmchip_parent(chip), ++ "pwm#%u: %llu/%llu [+%llu] -> pair-rate=%u, div-k=%u, period=%u, duty=%u, bypass=%u, active-high=%u\n", ++ pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, ++ wf->duty_offset_ns, wfhw->pair_rate, wfhw->div_k, ++ wfhw->period_ticks, wfhw->duty_ticks, wfhw->bypass_en, ++ wfhw->active_state); ++ ++ return rounding.best_wf.period_length_ns > wf->period_length_ns; ++} ++ ++static const struct pwm_ops sun8i_pwm_ops = { ++ .request = sun8i_pwm_request, ++ .free = sun8i_pwm_free, ++ .sizeof_wfhw = sizeof(struct sun8i_pwm_waveform), ++ .round_waveform_tohw = sun8i_pwm_round_waveform_tohw, ++ .round_waveform_fromhw = sun8i_pwm_round_waveform_fromhw, ++ .read_waveform = sun8i_pwm_read_waveform, ++ .write_waveform = sun8i_pwm_write_waveform, ++}; ++ ++/* Register the shared mux, gate and /div_m clock for each channel pair. */ ++static int sun8i_pwm_register_pair_clocks(struct device *dev, ++ struct sun8i_pwm_chip *sun8i_chip) ++{ ++ static const struct clk_parent_data parent_data[] = { ++ { .index = 0 }, ++ { .index = 1 }, ++ }; ++ unsigned int num_pairs = DIV_ROUND_UP(sun8i_chip->data->npwm, 2); ++ ++ for (unsigned int i = 0; i < num_pairs; i++) { ++ struct sun8i_pwm_pair *pair = &sun8i_chip->pairs[i]; ++ void __iomem *reg = sun8i_chip->base + SUN8I_PWM_PCCR(i); ++ struct clk *pair_clk; ++ const char *name; ++ int ret; ++ ++ name = devm_kasprintf(dev, GFP_KERNEL, ++ "%s#pwm-clk-src%u%u", dev_name(dev), ++ i * 2, i * 2 + 1); ++ if (!name) ++ return -ENOMEM; ++ ++ pair->mux.reg = reg; ++ pair->mux.shift = SUN8I_PWM_PCCR_SRC_SHIFT; ++ pair->mux.mask = SUN8I_PWM_PCCR_SRC_MASK; ++ pair->mux.flags = CLK_MUX_ROUND_CLOSEST; ++ pair->mux.lock = &sun8i_chip->lock; ++ ++ pair->chip = sun8i_chip; ++ pair->index = i; ++ ++ pair->divider.reg = reg; ++ pair->divider.shift = SUN8I_PWM_PCCR_DIV_M_SHIFT; ++ pair->divider.width = SUN8I_PWM_PCCR_DIV_M_WIDTH; ++ pair->divider.table = sun8i_pwm_div_m_table; ++ pair->divider.lock = &sun8i_chip->lock; ++ ++ pair->hw = devm_clk_hw_register_composite_pdata(dev, name, ++ parent_data, ++ ARRAY_SIZE(parent_data), ++ &pair->mux.hw, &clk_mux_ops, ++ &pair->divider.hw, &clk_divider_ops, ++ &pair->gate_hw, &sun8i_pwm_pair_gate_ops, 0); ++ if (IS_ERR(pair->hw)) ++ return dev_err_probe(dev, PTR_ERR(pair->hw), ++ "Failed to register pair %u clock\n", i); ++ ++ pair_clk = devm_clk_hw_get_clk(dev, pair->hw, NULL); ++ if (IS_ERR(pair_clk)) ++ return dev_err_probe(dev, PTR_ERR(pair_clk), ++ "Failed to get pair %u clock\n", i); ++ ++ pair->rate_nb.notifier_call = sun8i_pwm_pair_rate_notifier; ++ ret = devm_clk_notifier_register(dev, pair_clk, &pair->rate_nb); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to protect pair %u clock rate\n", i); ++ } ++ ++ return 0; ++} ++ ++/* Register a pair-clock consumer and the bypass clock for each channel. */ ++static int sun8i_pwm_register_channel_clocks(struct device *dev, ++ struct sun8i_pwm_chip *sun8i_chip) ++{ ++ for (unsigned int i = 0; i < sun8i_chip->data->npwm; i++) { ++ struct sun8i_pwm_channel *chan = &sun8i_chip->channels[i]; ++ struct clk_hw *parent = sun8i_chip->pairs[SUN8I_PWM_PAIR_IDX(i)].hw; ++ struct clk_parent_data parent_data = { .hw = parent }; ++ struct clk_init_data init = {}; ++ const char *name; ++ int ret; ++ ++ /* Separate handles let CCF arbitrate sibling rate exclusivity. */ ++ chan->pair_clk = devm_clk_hw_get_clk(dev, parent, NULL); ++ if (IS_ERR(chan->pair_clk)) ++ return dev_err_probe(dev, PTR_ERR(chan->pair_clk), ++ "Failed to get pair clock for PWM %u\n", i); ++ ++ name = devm_kasprintf(dev, GFP_KERNEL, "%s#pwm-bypass%u", ++ dev_name(dev), i); ++ if (!name) ++ return -ENOMEM; ++ ++ chan->bypass.chip = sun8i_chip; ++ chan->bypass.index = i; ++ init.name = name; ++ init.ops = &sun8i_pwm_bypass_ops; ++ init.parent_data = &parent_data; ++ init.num_parents = 1; ++ /* Keep the shared pair rate stable for the prepared lifetime. */ ++ init.flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE; ++ chan->bypass.hw.init = &init; ++ ++ ret = devm_clk_hw_register(dev, &chan->bypass.hw); ++ if (ret) ++ return dev_err_probe(dev, ret, ++ "Failed to register bypass clock %u\n", i); ++ ++ sun8i_chip->hw_data->hws[i] = &chan->bypass.hw; ++ } ++ ++ return 0; ++} ++ ++/* ++ * A disabled pair gate makes any set PER bits ineffective. Clear those stale ++ * enables before a clock consumer can turn the shared gate on and expose an ++ * unrequested output. ++ */ ++static void ++sun8i_pwm_sanitize_disabled_pairs(struct sun8i_pwm_chip *sun8i_chip) ++{ ++ unsigned int num_pairs = DIV_ROUND_UP(sun8i_chip->data->npwm, 2); ++ unsigned long flags; ++ u32 per; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); ++ for (unsigned int pair = 0; pair < num_pairs; pair++) { ++ unsigned int first = pair * 2; ++ u32 pccr; ++ ++ pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); ++ if (pccr & SUN8I_PWM_PCCR_GATE) ++ continue; ++ ++ per &= ~SUN8I_PWM_ENABLE(first); ++ pccr &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(first)); ++ if (first + 1 < sun8i_chip->data->npwm) { ++ per &= ~SUN8I_PWM_ENABLE(first + 1); ++ pccr &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(first + 1)); ++ } ++ sun8i_pwm_writel(sun8i_chip, pccr, SUN8I_PWM_PCCR(pair)); ++ } ++ sun8i_pwm_writel(sun8i_chip, per, SUN8I_PWM_PER); ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++} ++ ++/* ++ * Keep firmware-active outputs running until all declared consumers have had ++ * a chance to claim them. Once the driver core calls sync_state(), any channel ++ * which still has no PWM or clock owner can be stopped. ++ */ ++static void sun8i_pwm_sync_state(struct device *dev) ++{ ++ struct sun8i_pwm_chip *sun8i_chip = dev_get_drvdata(dev); ++ unsigned int num_pairs = DIV_ROUND_UP(sun8i_chip->data->npwm, 2); ++ unsigned long empty_pairs = 0; ++ unsigned long flags; ++ u32 active, per, unclaimed = 0; ++ ++ spin_lock_irqsave(&sun8i_chip->lock, flags); ++ per = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PER); ++ for (unsigned int pair = 0; pair < num_pairs; pair++) { ++ u32 pair_mask = sun8i_pwm_pair_enable_mask(sun8i_chip, pair); ++ u32 owner_mask; ++ u32 pdzcr; ++ ++ owner_mask = sun8i_pwm_pair_owner_mask_locked(sun8i_chip, pair); ++ pdzcr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PDZCR(pair)); ++ /* Dead-zone mode couples both outputs into one waveform. */ ++ if ((pdzcr & SUN8I_PWM_PDZCR_ENABLE) && ++ (per & owner_mask)) ++ continue; ++ ++ unclaimed |= pair_mask & ~owner_mask; ++ } ++ ++ active = per & unclaimed; ++ per &= ~unclaimed; ++ sun8i_pwm_writel(sun8i_chip, per, SUN8I_PWM_PER); ++ ++ for (unsigned int pair = 0; pair < num_pairs; pair++) { ++ unsigned int first = pair * 2; ++ u32 pccr; ++ ++ pccr = sun8i_pwm_readl(sun8i_chip, SUN8I_PWM_PCCR(pair)); ++ if (unclaimed & SUN8I_PWM_ENABLE(first)) ++ pccr &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(first)); ++ if (first + 1 < sun8i_chip->data->npwm && ++ unclaimed & SUN8I_PWM_ENABLE(first + 1)) ++ pccr &= ~BIT(SUN8I_PWM_PCCR_BYPASS_BIT(first + 1)); ++ sun8i_pwm_writel(sun8i_chip, pccr, SUN8I_PWM_PCCR(pair)); ++ ++ if (!(per & sun8i_pwm_pair_enable_mask(sun8i_chip, pair))) ++ empty_pairs |= BIT(pair); ++ } ++ spin_unlock_irqrestore(&sun8i_chip->lock, flags); ++ ++ /* ++ * Reconcile empty pair gates through CCF. Taking and dropping a ++ * temporary reference leaves a gate with another CCF user enabled, but ++ * disables a firmware-enabled gate whose software count is still zero. ++ */ ++ for (unsigned int pair = 0; pair < num_pairs; pair++) { ++ struct clk *pair_clk; ++ int ret; ++ ++ if (!(empty_pairs & BIT(pair))) ++ continue; ++ ++ pair_clk = sun8i_chip->channels[pair * 2].pair_clk; ++ ret = clk_prepare_enable(pair_clk); ++ if (ret) { ++ dev_warn(dev, "Failed to synchronize pair %u gate: %pe\n", ++ pair, ERR_PTR(ret)); ++ continue; ++ } ++ clk_disable_unprepare(pair_clk); ++ } ++ ++ if (active) ++ dev_dbg(dev, "disabled unclaimed firmware outputs %#x\n", active); ++} ++ ++static int sun8i_pwm_init_clocks(struct device *dev, ++ struct sun8i_pwm_chip *sun8i_chip) ++{ ++ unsigned int num_pairs = DIV_ROUND_UP(sun8i_chip->data->npwm, 2); ++ size_t hw_data_size; ++ int ret; ++ ++ sun8i_chip->pairs = devm_kcalloc(dev, num_pairs, ++ sizeof(*sun8i_chip->pairs), GFP_KERNEL); ++ if (!sun8i_chip->pairs) ++ return -ENOMEM; ++ ++ hw_data_size = struct_size(sun8i_chip->hw_data, hws, ++ sun8i_chip->data->npwm); ++ sun8i_chip->hw_data = devm_kzalloc(dev, hw_data_size, GFP_KERNEL); ++ if (!sun8i_chip->hw_data) ++ return -ENOMEM; ++ sun8i_chip->hw_data->num = sun8i_chip->data->npwm; ++ sun8i_pwm_sanitize_disabled_pairs(sun8i_chip); ++ ++ ret = sun8i_pwm_register_pair_clocks(dev, sun8i_chip); ++ if (ret) ++ return ret; ++ ++ return sun8i_pwm_register_channel_clocks(dev, sun8i_chip); ++} ++ ++static int sun8i_pwm_probe(struct platform_device *pdev) ++{ ++ const struct sun8i_pwm_data *data; ++ struct device *dev = &pdev->dev; ++ struct sun8i_pwm_chip *sun8i_chip; ++ struct reset_control *rst; ++ struct pwm_chip *chip; ++ int ret; ++ ++ data = of_device_get_match_data(dev); ++ if (!data) ++ return dev_err_probe(dev, -ENODEV, ++ "Missing specific data structure\n"); ++ ++ chip = devm_pwmchip_alloc(dev, data->npwm, sizeof(*sun8i_chip)); ++ if (IS_ERR(chip)) ++ return dev_err_probe(dev, PTR_ERR(chip), ++ "Failed to allocate pwmchip\n"); ++ ++ sun8i_chip = sun8i_pwm_from_chip(chip); ++ sun8i_chip->data = data; ++ spin_lock_init(&sun8i_chip->lock); ++ sun8i_chip->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(sun8i_chip->base)) ++ return dev_err_probe(dev, PTR_ERR(sun8i_chip->base), ++ "Failed to get PWM base address\n"); ++ ++ sun8i_chip->bus_clk = devm_clk_get_enabled(dev, "bus"); ++ if (IS_ERR(sun8i_chip->bus_clk)) ++ return dev_err_probe(dev, PTR_ERR(sun8i_chip->bus_clk), ++ "Failed to get bus clock\n"); ++ ++ rst = devm_reset_control_get_shared_deasserted(dev, NULL); ++ if (IS_ERR(rst)) ++ return dev_err_probe(dev, PTR_ERR(rst), ++ "Failed to get reset control\n"); ++ ++ sun8i_chip->channels = devm_kcalloc(dev, data->npwm, ++ sizeof(*sun8i_chip->channels), ++ GFP_KERNEL); ++ if (!sun8i_chip->channels) ++ return dev_err_probe(dev, -ENOMEM, ++ "Failed to allocate %d channels array\n", ++ data->npwm); ++ ++ chip->ops = &sun8i_pwm_ops; ++ ++ ret = sun8i_pwm_init_clocks(dev, sun8i_chip); ++ if (ret) ++ return ret; ++ ++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, ++ sun8i_chip->hw_data); ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to add HW clock provider\n"); ++ ++ ret = devm_pwmchip_add(dev, chip); ++ if (ret < 0) ++ return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); ++ ++ platform_set_drvdata(pdev, sun8i_chip); ++ ++ return 0; ++} ++ ++static const struct sun8i_pwm_data sun50i_h616_pwm_data = { ++ .npwm = 6, ++}; ++ ++static const struct of_device_id sun8i_pwm_dt_ids[] = { ++ { ++ .compatible = "allwinner,sun50i-h616-pwm", ++ .data = &sun50i_h616_pwm_data, ++ }, { ++ /* sentinel */ ++ } ++}; ++MODULE_DEVICE_TABLE(of, sun8i_pwm_dt_ids); ++ ++static struct platform_driver sun8i_pwm_driver = { ++ .driver = { ++ .name = "sun8i-pwm", ++ .of_match_table = sun8i_pwm_dt_ids, ++ .sync_state = sun8i_pwm_sync_state, ++ }, ++ .probe = sun8i_pwm_probe, ++}; ++module_platform_driver(sun8i_pwm_driver); ++ ++MODULE_AUTHOR("Richard Genoud "); ++MODULE_DESCRIPTION("Allwinner sun8i PWM driver"); ++MODULE_LICENSE("GPL"); + + +Subject: [PATCH v8 3/4] arm64: dts: allwinner: h616: add PWM controller +From: Richard Genoud +Date: Tue, 04 Aug 2026 15:27:26 -0600 +Message-Id: <20260804-h616-pwm-v8-v8-3-db37ab8624ae@gmail.com> + + +The H616 has a PWM controller that can provide PWM signals, but also +plain clocks. + +Add the PWM controller node and pins in the device tree. + +Signed-off-by: Richard Genoud +Signed-off-by: James Hilliard +--- + arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi | 47 ++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +index 1598e86259ab..90fe56a1d3cd 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi +@@ -281,6 +281,17 @@ watchdog: watchdog@30090a0 { + clocks = <&osc24M>; + }; + ++ pwm: pwm@300a000 { ++ compatible = "allwinner,sun50i-h616-pwm"; ++ reg = <0x0300a000 0x400>; ++ clocks = <&osc24M>, <&ccu CLK_BUS_PWM>; ++ clock-names = "mod", "bus"; ++ resets = <&ccu RST_BUS_PWM>; ++ #pwm-cells = <3>; ++ #clock-cells = <1>; ++ status = "disabled"; ++ }; ++ + pio: pinctrl@300b000 { + compatible = "allwinner,sun50i-h616-pinctrl"; + reg = <0x0300b000 0x400>; +@@ -385,6 +396,42 @@ nand_rb1_pin: nand-rb1-pin { + bias-pull-up; + }; + ++ /omit-if-no-ref/ ++ pwm0_pin: pwm0-pin { ++ pins = "PD28"; ++ function = "pwm0"; ++ }; ++ ++ /omit-if-no-ref/ ++ pwm1_pin: pwm1-pin { ++ pins = "PG19"; ++ function = "pwm1"; ++ }; ++ ++ /omit-if-no-ref/ ++ pwm2_pin: pwm2-pin { ++ pins = "PH2"; ++ function = "pwm2"; ++ }; ++ ++ /omit-if-no-ref/ ++ pwm3_pin: pwm3-pin { ++ pins = "PH0"; ++ function = "pwm3"; ++ }; ++ ++ /omit-if-no-ref/ ++ pwm4_pin: pwm4-pin { ++ pins = "PI14"; ++ function = "pwm4"; ++ }; ++ ++ /omit-if-no-ref/ ++ pwm5_pin: pwm5-pin { ++ pins = "PA12"; ++ function = "pwm5"; ++ }; ++ + /omit-if-no-ref/ + spi0_pins: spi0-pins { + pins = "PC0", "PC2", "PC4"; + + +Subject: [PATCH v8 4/4] MAINTAINERS: Add entry on Allwinner sun8i/H616 PWM driver +From: Richard Genoud +Date: Tue, 04 Aug 2026 15:27:27 -0600 +Message-Id: <20260804-h616-pwm-v8-v8-4-db37ab8624ae@gmail.com> + + +Add myself as maintainer of Allwinner SUN8I PWM driver. + +Signed-off-by: Richard Genoud +Signed-off-by: James Hilliard +--- + MAINTAINERS | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 5114e6db7307..9a778d1242c5 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -919,6 +919,11 @@ S: Maintained + F: Documentation/devicetree/bindings/hwlock/allwinner,sun6i-a31-hwspinlock.yaml + F: drivers/hwspinlock/sun6i_hwspinlock.c + ++ALLWINNER SUN8I PWM DRIVER ++M: Richard Genoud ++S: Maintained ++F: drivers/pwm/pwm-sun8i.c ++ + ALLWINNER THERMAL DRIVER + M: Vasily Khoruzhick + M: Yangtao Li + diff --git a/projects/ROCKNIX/devices/H700/patches/linux/0009-h616-add-pwm-node.patch b/projects/ROCKNIX/devices/H700/patches/linux/0009-h616-add-pwm-node.patch deleted file mode 100644 index 6ccb9b1910..0000000000 --- a/projects/ROCKNIX/devices/H700/patches/linux/0009-h616-add-pwm-node.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 193e59a21f1a828e1860cd6fdfcb3fbf041a3f19 Mon Sep 17 00:00:00 2001 -From: Philippe Simons -Date: Thu, 23 Jan 2025 23:39:47 +0100 -Subject: [PATCH 8/9] h616: add pwm node - ---- - arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - -diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi -index c335e81a5..7417a214a 100644 ---- a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi -+++ b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi -@@ -257,6 +257,20 @@ ccu: clock@3001000 { - #reset-cells = <1>; - }; - -+ pwm: pwm@300a000 { -+ compatible = "allwinner,sun50i-h616-pwm"; -+ reg = <0x0300a000 0x400>; -+ clocks = <&ccu CLK_BUS_PWM>, -+ <&osc24M>, -+ <&ccu CLK_APB1>; -+ clock-names = "bus", "hosc", "apb"; -+ resets = <&ccu RST_BUS_PWM>; -+ #pwm-cells = <0x3>; -+ status = "disabled"; -+ -+ allwinner,pwm-channels = <6>; -+ }; -+ - dma: dma-controller@3002000 { - compatible = "allwinner,sun50i-h616-dma", - "allwinner,sun50i-a100-dma"; --- -2.47.1 - diff --git a/projects/ROCKNIX/devices/H700/patches/linux/0010-rg35xx-enable-pwm-backlight.patch b/projects/ROCKNIX/devices/H700/patches/linux/0010-rg35xx-enable-pwm-backlight.patch index ff46226022..f235dfe764 100644 --- a/projects/ROCKNIX/devices/H700/patches/linux/0010-rg35xx-enable-pwm-backlight.patch +++ b/projects/ROCKNIX/devices/H700/patches/linux/0010-rg35xx-enable-pwm-backlight.patch @@ -3,9 +3,19 @@ From: Philippe Simons Date: Thu, 23 Jan 2025 23:41:16 +0100 Subject: [PATCH 9/9] rg35xx: enable pwm backlight +Drive the backlight from PWM0 (PD28) instead of switching it with a GPIO. + +sun50i-h616.dtsi now carries a pwm0_pin for PD28, but the board keeps its own +lcd_backlight_pin node: 0140 and 0150 anchor their &pio hunks on it, and the +&pwm override has to stay at the end of the file because 0140's trailing +context needs &r_i2c to follow the &pio block directly. + +The parent clock, the shared pair prescaler and the per-channel divider are +picked by the driver from the requested waveform, so the +allwinner,pwm-paired-channel-clock-* properties are gone. --- - .../sun50i-h700-anbernic-rg35xx-2024.dts | 18 +++++++++++++++--- - 1 file changed, 15 insertions(+), 3 deletions(-) + .../sun50i-h700-anbernic-rg35xx-2024.dts | 16 ++++++++++++---- + 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h700-anbernic-rg35xx-2024.dts b/arch/arm64/boot/dts/allwinner/sun50i-h700-anbernic-rg35xx-2024.dts index 5d2b13aa7..3397cad0c 100644 @@ -37,14 +47,12 @@ index 5d2b13aa7..3397cad0c 100644 }; &r_i2c { -@@ -458,3 +464,9 @@ &usbotg { +@@ -458,3 +464,7 @@ &usbotg { &usbphy { status = "okay"; }; + +&pwm { -+ allwinner,pwm-paired-channel-clock-sources = "hosc", "hosc", "hosc"; -+ allwinner,pwm-paired-channel-clock-prescales = <0>, <0>, <0>; + status = "okay"; +}; -- diff --git a/projects/ROCKNIX/devices/H700/patches/linux/0204-dts-Enable-hdmi-sound.patch b/projects/ROCKNIX/devices/H700/patches/linux/0204-dts-Enable-hdmi-sound.patch index 8e0180c499..48870671d1 100644 --- a/projects/ROCKNIX/devices/H700/patches/linux/0204-dts-Enable-hdmi-sound.patch +++ b/projects/ROCKNIX/devices/H700/patches/linux/0204-dts-Enable-hdmi-sound.patch @@ -5,8 +5,8 @@ Subject: [PATCH] dts: Enable hdmi sound --- .../arm64/boot/dts/allwinner/sun50i-h616.dtsi | 68 +++++++++++++++++++ - .../sun50i-h700-anbernic-rg35xx-2024.dts | 13 ++++ - 2 files changed, 81 insertions(+) + .../sun50i-h700-anbernic-rg35xx-2024.dts | 12 ++++ + 2 files changed, 80 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi index 8118e40d2..f630f779b 100644 @@ -91,8 +91,8 @@ diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h700-anbernic-rg35xx-2024.dts index 9e18fb2a8..db64d1406 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h700-anbernic-rg35xx-2024.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-h700-anbernic-rg35xx-2024.dts -@@ -615,3 +615,16 @@ &pwm { - allwinner,pwm-paired-channel-clock-prescales = <0>, <0>, <0>; +@@ -616,3 +616,16 @@ &usbphy { + &pwm { status = "okay"; }; + From 56af713e723cfd6ed785c1ce12ce5047f3d4753c Mon Sep 17 00:00:00 2001 From: Philippe Simons Date: Mon, 10 Aug 2026 17:19:03 +0200 Subject: [PATCH 09/24] feat: H700 re-enable rumble This reverts commit a69c703206165cd91fff3a478fc4bf5f4d8b7f57. Rumble drives PWM2 (PH2) on the RG35XX 2024 and PWM3 (PI13) on the RG40XX H/V and RGcubeXX. The sun8i driver pairs channels 0/1, 2/3 and 4/5, so the rumble channel never shares a clock source or prescaler with the PWM0 backlight. The trailing-whitespace cleanup from the reverted commit is kept. --- .../linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-h.dts | 4 ++-- .../linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-v.dts | 4 ++-- .../linux/dts/allwinner/sun50i-h700-anbernic-rgcubexx.dts | 4 ++-- ...cefeedback.patch.disabled => 0150-add-forcefeedback.patch} | 0 4 files changed, 6 insertions(+), 6 deletions(-) rename projects/ROCKNIX/devices/H700/patches/linux/{0150-add-forcefeedback.patch.disabled => 0150-add-forcefeedback.patch} (100%) diff --git a/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-h.dts b/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-h.dts index 1257887a1a..a34b784ac3 100644 --- a/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-h.dts +++ b/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-h.dts @@ -15,8 +15,8 @@ invert-absrx; invert-absry; amux-count = <4>; - // pwms = <&pwm 3 1000000 0>; - // pinctrl-0 = <&joy_mux_pin>, <&pwm3_pin>; + pwms = <&pwm 3 1000000 0>; + pinctrl-0 = <&joy_mux_pin>, <&pwm3_pin>; }; &uart5 { diff --git a/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-v.dts b/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-v.dts index f97d66acaa..c2a0dd287f 100644 --- a/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-v.dts +++ b/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rg40xx-v.dts @@ -13,8 +13,8 @@ &joypad { amux-count = <4>; - // pwms = <&pwm 3 1000000 0>; - // pinctrl-0 = <&joy_mux_pin>, <&pwm3_pin>; + pwms = <&pwm 3 1000000 0>; + pinctrl-0 = <&joy_mux_pin>, <&pwm3_pin>; }; &uart5 { diff --git a/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rgcubexx.dts b/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rgcubexx.dts index 60836405cb..03c678a9ac 100644 --- a/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rgcubexx.dts +++ b/projects/ROCKNIX/devices/H700/linux/dts/allwinner/sun50i-h700-anbernic-rgcubexx.dts @@ -16,8 +16,8 @@ invert-absrx; invert-absry; amux-count = <4>; - // pwms = <&pwm 3 1000000 0>; - // pinctrl-0 = <&joy_mux_pin>, <&pwm3_pin>; + pwms = <&pwm 3 1000000 0>; + pinctrl-0 = <&joy_mux_pin>, <&pwm3_pin>; }; &uart5 { diff --git a/projects/ROCKNIX/devices/H700/patches/linux/0150-add-forcefeedback.patch.disabled b/projects/ROCKNIX/devices/H700/patches/linux/0150-add-forcefeedback.patch similarity index 100% rename from projects/ROCKNIX/devices/H700/patches/linux/0150-add-forcefeedback.patch.disabled rename to projects/ROCKNIX/devices/H700/patches/linux/0150-add-forcefeedback.patch From 544d3a38f4b07c08c8c98ce637fae0d763c44306 Mon Sep 17 00:00:00 2001 From: spycat88 Date: Mon, 10 Aug 2026 18:41:03 +0000 Subject: [PATCH 10/24] packages: restore folder to upstream state and move local changes --- packages/devel/ccache/package.mk | 1 - .../linux-firmware/intel-ucode/package.mk | 12 ++ .../sources/intel-microcode2ucode.c | 154 ++++++++++++++++++ packages/sysutils/busybox/scripts/fs-resize | 2 +- .../ROCKNIX/packages/devel/ccache/package.mk | 23 +++ .../vkmark-0002-fix-custom-dispatchers.patch | 0 6 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 packages/linux-firmware/intel-ucode/package.mk create mode 100644 packages/linux-firmware/intel-ucode/sources/intel-microcode2ucode.c rename {packages/graphics/vulkan/vkmark/patches => projects/ROCKNIX/patches/vkmark}/vkmark-0002-fix-custom-dispatchers.patch (100%) diff --git a/packages/devel/ccache/package.mk b/packages/devel/ccache/package.mk index 9ec45fd947..553510f507 100644 --- a/packages/devel/ccache/package.mk +++ b/packages/devel/ccache/package.mk @@ -35,7 +35,6 @@ post_makeinstall_host() { if [ -z "${CCACHE_DISABLE}" ]; then CCACHE_DIR="${BUILD_CCACHE_DIR}" ${TOOLCHAIN}/bin/ccache --max-size=${CCACHE_CACHE_SIZE} CCACHE_DIR="${BUILD_CCACHE_DIR}" ${TOOLCHAIN}/bin/ccache --set-config compression_level=${CCACHE_COMPRESSLEVEL} - CCACHE_DIR="${BUILD_CCACHE_DIR}" ${TOOLCHAIN}/bin/ccache --set-config sloppiness=${CCACHE_SLOPPINESS} fi cat >${TOOLCHAIN}/bin/host-gcc < + * Changed to create a single file by Thomas Bächler + */ + + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct microcode_header_intel { + unsigned int hdrver; + unsigned int rev; + unsigned int date; + unsigned int sig; + unsigned int cksum; + unsigned int ldrver; + unsigned int pf; + unsigned int datasize; + unsigned int totalsize; + unsigned int reserved[3]; +}; + +union mcbuf { + struct microcode_header_intel hdr; + unsigned int i[0]; + char c[0]; +}; + +int main(int argc, char *argv[]) +{ + const char *filename = "/lib/firmware/microcode.dat"; + FILE *f; + char line[LINE_MAX]; + char buf[4000000]; + union mcbuf *mc; + size_t bufsize, count, start; + int rc = EXIT_SUCCESS; + + if (argv[1] != NULL) + filename = argv[1]; + + count = 0; + mc = (union mcbuf *) buf; + f = fopen(filename, "re"); + if (f == NULL) { + printf("open %s: %m\n", filename); + rc = EXIT_FAILURE; + goto out; + } + + while (fgets(line, sizeof(line), f) != NULL) { + if (sscanf(line, "%x, %x, %x, %x", + &mc->i[count], + &mc->i[count + 1], + &mc->i[count + 2], + &mc->i[count + 3]) != 4) + continue; + count += 4; + } + fclose(f); + + bufsize = count * sizeof(int); + printf("%s: %lu(%luk) bytes, %zu integers\n", + filename, + bufsize, + bufsize / 1024, + count); + + if (bufsize < sizeof(struct microcode_header_intel)) + goto out; + + f = fopen("microcode.bin", "we"); + if (f == NULL) { + printf("open microcode.bin: %m\n"); + rc = EXIT_FAILURE; + goto out; + } + + start = 0; + for (;;) { + size_t size; + unsigned int family, model, stepping; + unsigned int year, month, day; + + mc = (union mcbuf *) &buf[start]; + + if (mc->hdr.totalsize) + size = mc->hdr.totalsize; + else + size = 2000 + sizeof(struct microcode_header_intel); + + if (mc->hdr.ldrver != 1 || mc->hdr.hdrver != 1) { + printf("unknown version/format:\n"); + rc = EXIT_FAILURE; + break; + } + + /* + * 0- 3 stepping + * 4- 7 model + * 8-11 family + * 12-13 type + * 16-19 extended model + * 20-27 extended family + */ + family = (mc->hdr.sig >> 8) & 0xf; + if (family == 0xf) + family += (mc->hdr.sig >> 20) & 0xff; + model = (mc->hdr.sig >> 4) & 0x0f; + if (family == 0x06) + model += ((mc->hdr.sig >> 16) & 0x0f) << 4; + stepping = mc->hdr.sig & 0x0f; + + year = mc->hdr.date & 0xffff; + month = mc->hdr.date >> 24; + day = (mc->hdr.date >> 16) & 0xff; + + printf("\n"); + printf("signature: 0x%02x\n", mc->hdr.sig); + printf("flags: 0x%02x\n", mc->hdr.pf); + printf("revision: 0x%02x\n", mc->hdr.rev); + printf("date: %04x-%02x-%02x\n", year, month, day); + printf("size: %zu\n", size); + + if (fwrite(mc, size, 1, f) != 1) { + printf("write microcode.bin: %m\n"); + rc = EXIT_FAILURE; + goto out; + } + + start += size; + if (start >= bufsize) + break; + } + fclose(f); + printf("\n"); +out: + return rc; +} diff --git a/packages/sysutils/busybox/scripts/fs-resize b/packages/sysutils/busybox/scripts/fs-resize index ac03e4c430..15df5bcae4 100755 --- a/packages/sysutils/busybox/scripts/fs-resize +++ b/packages/sysutils/busybox/scripts/fs-resize @@ -62,7 +62,7 @@ if [ -e /storage/.please_resize_me ] ; then echo "" StartProgressLog spinner "Resizing partition... " "parted -s -f -m $DISK resizepart 2 100% >>$LOG 2>&1" - StartProgressLog spinner "Creating file system... " "mke2fs -F -q -t ext4 -T ext4 -m 0 -L \"$LABEL\" -U \"$UUID\" $PART >>$LOG 2>&1" + StartProgressLog spinner "Creating file system... " "mke2fs -F -q -t ext4 -m 0 -L \"$LABEL\" -U \"$UUID\" $PART >>$LOG 2>&1" StartProgressLog spinner "Checking file system... " "e2fsck -f -p $PART >>$LOG 2>&1" StartProgress countdown "Rebooting in 15s... " 15 "NOW" else diff --git a/projects/ROCKNIX/packages/devel/ccache/package.mk b/projects/ROCKNIX/packages/devel/ccache/package.mk index 859e0839e7..71bb202c7f 100644 --- a/projects/ROCKNIX/packages/devel/ccache/package.mk +++ b/projects/ROCKNIX/packages/devel/ccache/package.mk @@ -6,3 +6,26 @@ pre_configure_host() { export CXXFLAGS+=" -Wno-error=restrict" } + +post_makeinstall_host() { + # setup ccache + if [ -z "${CCACHE_DISABLE}" ]; then + CCACHE_DIR="${BUILD_CCACHE_DIR}" ${TOOLCHAIN}/bin/ccache --max-size=${CCACHE_CACHE_SIZE} + CCACHE_DIR="${BUILD_CCACHE_DIR}" ${TOOLCHAIN}/bin/ccache --set-config compression_level=${CCACHE_COMPRESSLEVEL} + CCACHE_DIR="${BUILD_CCACHE_DIR}" ${TOOLCHAIN}/bin/ccache --set-config sloppiness=${CCACHE_SLOPPINESS} + fi + + cat >${TOOLCHAIN}/bin/host-gcc <${TOOLCHAIN}/bin/host-g++ < Date: Mon, 10 Aug 2026 18:56:06 +0000 Subject: [PATCH 11/24] busybox: remove non-existant dependency --- projects/ROCKNIX/packages/sysutils/busybox/package.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/ROCKNIX/packages/sysutils/busybox/package.mk b/projects/ROCKNIX/packages/sysutils/busybox/package.mk index 17f0219b63..ce37264ab3 100644 --- a/projects/ROCKNIX/packages/sysutils/busybox/package.mk +++ b/projects/ROCKNIX/packages/sysutils/busybox/package.mk @@ -9,7 +9,7 @@ PKG_LICENSE="GPL" PKG_SITE="http://www.busybox.net" PKG_URL="http://busybox.net/downloads/${PKG_NAME}-${PKG_VERSION}.tar.bz2" PKG_DEPENDS_HOST="gcc:host" -PKG_DEPENDS_TARGET="toolchain busybox:host dosfstools e2fsprogs zip usbutils parted procps-ng gptfdisk libtirpc" +PKG_DEPENDS_TARGET="toolchain busybox:host dosfstools e2fsprogs usbutils parted procps-ng gptfdisk libtirpc" PKG_DEPENDS_INIT="toolchain libc:init glibc:init libtirpc" PKG_LONGDESC="BusyBox combines tiny versions of many common UNIX utilities into a single small executable." # busybox fails to build with GOLD support enabled with binutils-2.25 From 8ee48d4aa8f80f3460f0c3f01faee516d8aac203 Mon Sep 17 00:00:00 2001 From: spycat88 Date: Mon, 10 Aug 2026 18:56:29 +0000 Subject: [PATCH 12/24] flycast-sa: remove non-existant dependency --- .../ROCKNIX/packages/emulators/standalone/flycast-sa/package.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/ROCKNIX/packages/emulators/standalone/flycast-sa/package.mk b/projects/ROCKNIX/packages/emulators/standalone/flycast-sa/package.mk index a3c1968629..e7fb752679 100644 --- a/projects/ROCKNIX/packages/emulators/standalone/flycast-sa/package.mk +++ b/projects/ROCKNIX/packages/emulators/standalone/flycast-sa/package.mk @@ -7,7 +7,7 @@ PKG_VERSION="392a429e8b040b3e5bf6696cb4f984274fc44123" #v2.6 PKG_LICENSE="GPLv2" PKG_SITE="https://github.com/flyinghead/flycast" PKG_URL="${PKG_SITE}.git" -PKG_DEPENDS_TARGET="toolchain alsa SDL2 libzip zip curl miniupnpc lua54 libao" +PKG_DEPENDS_TARGET="toolchain alsa SDL2 libzip curl miniupnpc lua54 libao" PKG_LONGDESC="Flycast is a multiplatform Sega Dreamcast, Naomi and Atomiswave emulator" PKG_TOOLCHAIN="cmake" PKG_PATCH_DIRS+="${DEVICE}" From 4b4b539bb226ad85bae0e96defccdda416f65815 Mon Sep 17 00:00:00 2001 From: spycat88 Date: Mon, 10 Aug 2026 18:56:36 +0000 Subject: [PATCH 13/24] ppsspp-sa: remove non-existant dependency --- .../ROCKNIX/packages/emulators/standalone/ppsspp-sa/package.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/ROCKNIX/packages/emulators/standalone/ppsspp-sa/package.mk b/projects/ROCKNIX/packages/emulators/standalone/ppsspp-sa/package.mk index c6922df016..fe01a04183 100644 --- a/projects/ROCKNIX/packages/emulators/standalone/ppsspp-sa/package.mk +++ b/projects/ROCKNIX/packages/emulators/standalone/ppsspp-sa/package.mk @@ -8,7 +8,7 @@ PKG_URL="${PKG_SITE}.git" PKG_VERSION="afbc66a318b86432642b532c575241f3716642ef" # v1.20.2 CHEAT_DB_VERSION="7c9fe1ae71155626cea767aed53f968de9f4051f" # Update cheat.db (17/01/2026) PKG_LICENSE="GPLv2" -PKG_DEPENDS_TARGET="toolchain libzip SDL2 zlib zip" +PKG_DEPENDS_TARGET="toolchain libzip SDL2 zlib" PKG_LONGDESC="PPSSPPDL" GET_HANDLER_SUPPORT="git" From 932b2e65ed217ad5f103ec22618b221b630c5ba2 Mon Sep 17 00:00:00 2001 From: spycat88 Date: Mon, 10 Aug 2026 18:57:11 +0000 Subject: [PATCH 14/24] imagemagick: treat incompatible-pointer-types as a warning --- projects/ROCKNIX/packages/graphics/imagemagick/package.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/ROCKNIX/packages/graphics/imagemagick/package.mk b/projects/ROCKNIX/packages/graphics/imagemagick/package.mk index e919f545c2..861b37cacb 100644 --- a/projects/ROCKNIX/packages/graphics/imagemagick/package.mk +++ b/projects/ROCKNIX/packages/graphics/imagemagick/package.mk @@ -27,6 +27,7 @@ pre_configure_target() { else PKG_CONFIGURE_OPTS_TARGET+=" --with-x=no" fi + export CFLAGS+=" -Wno-error=incompatible-pointer-types" export LDFLAGS+=" -lsharpyuv -lwebp" } From 450a23a32193fde479bb7d5aafe19244db35cabf Mon Sep 17 00:00:00 2001 From: spycat88 Date: Mon, 10 Aug 2026 19:03:30 +0000 Subject: [PATCH 15/24] rapidjson: pin package --- .../packages/devel/rapidjson/package.mk | 19 ++++++++++++ ...pidjson-0001-remove_custom_cxx_flags.patch | 29 +++++++++++++++++++ ...iling-assignment-operator.-Fixed-718.patch | 25 ++++++++++++++++ ...tly-disable-copy-assignment-operator.patch | 25 ++++++++++++++++ ...apidjson-0004-build-with-cmake-4.0.0.patch | 26 +++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 projects/ROCKNIX/packages/devel/rapidjson/package.mk create mode 100644 projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0001-remove_custom_cxx_flags.patch create mode 100644 projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0002-Removed-non-compiling-assignment-operator.-Fixed-718.patch create mode 100644 projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0003-Explicitly-disable-copy-assignment-operator.patch create mode 100644 projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0004-build-with-cmake-4.0.0.patch diff --git a/projects/ROCKNIX/packages/devel/rapidjson/package.mk b/projects/ROCKNIX/packages/devel/rapidjson/package.mk new file mode 100644 index 0000000000..bfaf3a9b7e --- /dev/null +++ b/projects/ROCKNIX/packages/devel/rapidjson/package.mk @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2017-present Team LibreELEC (https://libreelec.tv) + +PKG_NAME="rapidjson" +PKG_VERSION="1.1.0" +PKG_SHA256="bf7ced29704a1e696fbccf2a2b4ea068e7774fa37f6d7dd4039d0787f8bed98e" +PKG_LICENSE="MIT" +PKG_SITE="https://github.com/miloyip/rapidjson" +PKG_URL="https://github.com/miloyip/rapidjson/archive/v${PKG_VERSION}.tar.gz" +PKG_DEPENDS_TARGET="toolchain" +PKG_LONGDESC="A fast JSON parser/generator for C++ with both SAX/DOM style API" + +PKG_CMAKE_OPTS_TARGET="-DRAPIDJSON_BUILD_DOC=OFF \ + -DRAPIDJSON_BUILD_EXAMPLES=OFF \ + -DRAPIDJSON_BUILD_TESTS=OFF \ + -DRAPIDJSON_BUILD_THIRDPARTY_GTEST=OFF \ + -DRAPIDJSON_BUILD_ASAN=OFF \ + -DRAPIDJSON_BUILD_UBSAN=OFF \ + -DRAPIDJSON_HAS_STDSTRING=ON" diff --git a/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0001-remove_custom_cxx_flags.patch b/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0001-remove_custom_cxx_flags.patch new file mode 100644 index 0000000000..51434ff52e --- /dev/null +++ b/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0001-remove_custom_cxx_flags.patch @@ -0,0 +1,29 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index ceda71b..efb259e 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -50,7 +50,6 @@ if(CCACHE_FOUND) + endif(CCACHE_FOUND) + + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror") + if (RAPIDJSON_BUILD_CXX11) + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") +@@ -73,7 +72,6 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + endif() + endif() + elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wall -Wextra -Werror -Wno-missing-field-initializers") + if (RAPIDJSON_BUILD_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + endif() +@@ -88,7 +86,6 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + endif() + endif() + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") +- add_definitions(-D_CRT_SECURE_NO_WARNINGS=1) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") + endif() + + diff --git a/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0002-Removed-non-compiling-assignment-operator.-Fixed-718.patch b/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0002-Removed-non-compiling-assignment-operator.-Fixed-718.patch new file mode 100644 index 0000000000..ab02199baf --- /dev/null +++ b/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0002-Removed-non-compiling-assignment-operator.-Fixed-718.patch @@ -0,0 +1,25 @@ +From 3b2441b87f99ab65f37b141a7b548ebadb607b96 Mon Sep 17 00:00:00 2001 +From: Janusz Chorko +Date: Fri, 26 Aug 2016 21:17:38 +0200 +Subject: [PATCH] Removed non-compiling assignment operator. Fixed #718 + +--- + include/rapidjson/document.h | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h +index e3e20dfb..b0f1f70b 100644 +--- a/include/rapidjson/document.h ++++ b/include/rapidjson/document.h +@@ -316,8 +316,6 @@ struct GenericStringRef { + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + +- GenericStringRef& operator=(const GenericStringRef& rhs) { s = rhs.s; length = rhs.length; } +- + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + +-- +2.43.0 + diff --git a/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0003-Explicitly-disable-copy-assignment-operator.patch b/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0003-Explicitly-disable-copy-assignment-operator.patch new file mode 100644 index 0000000000..4a3670d376 --- /dev/null +++ b/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0003-Explicitly-disable-copy-assignment-operator.patch @@ -0,0 +1,25 @@ +From 862c39be371278a45a88d4d1d75164be57bb7e2d Mon Sep 17 00:00:00 2001 +From: Janusz Chorko +Date: Fri, 26 Aug 2016 21:26:50 +0200 +Subject: [PATCH] Explicitly disable copy assignment operator + +--- + include/rapidjson/document.h | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h +index b0f1f70b..19f5a6a5 100644 +--- a/include/rapidjson/document.h ++++ b/include/rapidjson/document.h +@@ -326,6 +326,8 @@ private: + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; ++ //! Copy assignment operator not permitted - immutable type ++ GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; + }; + + //! Mark a character pointer as constant string +-- +2.43.0 + diff --git a/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0004-build-with-cmake-4.0.0.patch b/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0004-build-with-cmake-4.0.0.patch new file mode 100644 index 0000000000..7be5078c0c --- /dev/null +++ b/projects/ROCKNIX/packages/devel/rapidjson/patches/rapidjson-0004-build-with-cmake-4.0.0.patch @@ -0,0 +1,26 @@ +From ebd87cb468fb4cb060b37e579718c4a4125416c1 Mon Sep 17 00:00:00 2001 +From: Christian Fersch +Date: Mon, 15 Jan 2024 07:44:16 +0100 +Subject: [PATCH] Increase CMake minimum version to 3.5 (fixes #2159) + +--- + CMakeLists.txt | 16 ++-------------- + 1 file changed, 2 insertions(+), 14 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 1b3a79de9..c02301c98 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,11 +1,4 @@ +-CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +-if(POLICY CMP0025) +- # detect Apple's Clang +- cmake_policy(SET CMP0025 NEW) +-endif() +-if(POLICY CMP0054) +- cmake_policy(SET CMP0054 NEW) +-endif() ++CMAKE_MINIMUM_REQUIRED(VERSION 3.5) + + SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules) + From 591a4f5a39bf7c243dbee32088ff103fdf0a9011 Mon Sep 17 00:00:00 2001 From: spycat88 Date: Mon, 10 Aug 2026 20:21:23 +0000 Subject: [PATCH 16/24] opus: pin package --- projects/ROCKNIX/packages/audio/opus/package.mk | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 projects/ROCKNIX/packages/audio/opus/package.mk diff --git a/projects/ROCKNIX/packages/audio/opus/package.mk b/projects/ROCKNIX/packages/audio/opus/package.mk new file mode 100644 index 0000000000..d59ed4da6a --- /dev/null +++ b/projects/ROCKNIX/packages/audio/opus/package.mk @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2024-present ROCKNIX (https://github.com/ROCKNIX) + +. ${ROOT}/packages/audio/opus/package.mk + +PKG_VERSION="1.5.2" +PKG_SHA256="65c1d2f78b9f2fb20082c38cbe47c951ad5839345876e46941612ee87f9a7ce1" +PKG_URL="https://ftp.osuosl.org/pub/xiph/releases/opus/${PKG_NAME}-${PKG_VERSION}.tar.gz" From 297536c3e82b3b1ada216a49be598f8eda307f14 Mon Sep 17 00:00:00 2001 From: spycat88 Date: Mon, 10 Aug 2026 20:21:49 +0000 Subject: [PATCH 17/24] supermodel-sa: fix parallel build --- .../supermodel-sa/patches/06-fix-parallel-build.patch | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/projects/ROCKNIX/packages/emulators/standalone/supermodel-sa/patches/06-fix-parallel-build.patch b/projects/ROCKNIX/packages/emulators/standalone/supermodel-sa/patches/06-fix-parallel-build.patch index 82fea71e8d..64940ddf7a 100644 --- a/projects/ROCKNIX/packages/emulators/standalone/supermodel-sa/patches/06-fix-parallel-build.patch +++ b/projects/ROCKNIX/packages/emulators/standalone/supermodel-sa/patches/06-fix-parallel-build.patch @@ -16,3 +16,13 @@ $(info Generating dependencies: $< -> $(OBJ_DIR)/$(*F).d) $(SILENT)$(CC) -MM -MP -MT $(OBJ_DIR)/$(*F).o -MT $(OBJ_DIR)/$(*F).d $(CFLAGS) $< > $(OBJ_DIR)/$(*F).d $(info Compiling : $< -> $@) + +@@ -302,7 +302,7 @@ + MUSASHI_CFLAGS = -ISrc/CPU/68K/Musashi -I$(OBJ_DIR) -DINLINE="static inline" -Wno-unused-variable + MUSASHI_LDFLAGS = -o $(MUSASHI_OUTFILE) $(OBJ_DIR)/m68kmake.o -s + +-$(MUSASHI_OUTFILE): Src/CPU/68K/Musashi/m68kmake.c Src/CPU/68K/Musashi/m68k_in.c ++$(MUSASHI_OUTFILE): Src/CPU/68K/Musashi/m68kmake.c Src/CPU/68K/Musashi/m68k_in.c | $(OBJ_DIR) + $(info --------------------------------------------------------------------------------) + $(info Compiling : $< -> $(OBJ_DIR)/m68kmake.o) + $(SILENT)$(CC) $< $(CFLAGS) -o $(OBJ_DIR)/m68kmake.o From 32126907ab947f281f1c1b519e2ef2d3632cec6b Mon Sep 17 00:00:00 2001 From: spycat88 Date: Mon, 10 Aug 2026 20:22:09 +0000 Subject: [PATCH 18/24] daedalusx64-sa: fix building package with newer cmake --- .../emulators/standalone/daedalusx64-sa/package.mk | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/projects/ROCKNIX/packages/emulators/standalone/daedalusx64-sa/package.mk b/projects/ROCKNIX/packages/emulators/standalone/daedalusx64-sa/package.mk index ad5183a700..606d58ef8b 100644 --- a/projects/ROCKNIX/packages/emulators/standalone/daedalusx64-sa/package.mk +++ b/projects/ROCKNIX/packages/emulators/standalone/daedalusx64-sa/package.mk @@ -22,9 +22,12 @@ elif [ "${OPENGLES_SUPPORT}" = yes ]; then PKG_DEPENDS_TARGET+=" ${OPENGLES}" fi +pre_configure_target() { + sed -i 's|message (${CMAKE_CXX_FLAGS_RELEASE})|message("${CMAKE_CXX_FLAGS_RELEASE}")|g' ${PKG_BUILD}/Source/CMakeLists.txt +} + makeinstall_target() { - if [ "${ARCH}" = "aarch64" ] - then + if [ "${ARCH}" = "aarch64" ]; then mkdir -p ${INSTALL}/usr cp -r ${ROOT}/build.${DISTRO}-${DEVICE}.arm/install_pkg/daedalusx64-sa-${PKG_VERSION}/usr/* ${INSTALL}/usr/ chmod +x ${INSTALL}/usr/bin/* From 7aebbb936669c7656bc334054ecc9b763ae97f49 Mon Sep 17 00:00:00 2001 From: sunshineinabox Date: Mon, 10 Aug 2026 14:38:38 -0700 Subject: [PATCH 19/24] armsx2: bump again --- .../emulators/standalone/armsx2-sa/package.mk | 2 +- .../patches/001-cmake-version-fallback.patch | 27 ------------------- 2 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/patches/001-cmake-version-fallback.patch diff --git a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk index 1fcaf3a67a..e7a952ca84 100644 --- a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk +++ b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk @@ -2,7 +2,7 @@ # Copyright (C) 2025-present ROCKNIX (https://github.com/ROCKNIX) PKG_NAME="armsx2-sa" -PKG_VERSION="2.6.6.4" +PKG_VERSION="2.6.6.5" PKG_LICENSE="GPLv3" PKG_SITE="https://github.com/ARMSX2/ARMSX2" PKG_URL="${PKG_SITE}/archive/refs/tags/${PKG_VERSION}.tar.gz" diff --git a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/patches/001-cmake-version-fallback.patch b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/patches/001-cmake-version-fallback.patch deleted file mode 100644 index d5054d949f..0000000000 --- a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/patches/001-cmake-version-fallback.patch +++ /dev/null @@ -1,27 +0,0 @@ -Submitted upstream: https://github.com/ARMSX2/ARMSX2/pull/551 cmake: allow setting version as fallback - -diff --git a/cmake/BuildParameters.cmake b/cmake/BuildParameters.cmake ---- a/cmake/BuildParameters.cmake -+++ b/cmake/BuildParameters.cmake -@@ -21,6 +21,7 @@ option(USE_VTUNE "Plug VTUNE to profile GS JIT.") - option(USE_PERF_JITDUMP "Emit Linux perf jitdump (jit-.dump) for recompiled JIT blocks; use with perf record/inject." OFF) - option(USE_PERF_MAP "Emit simple /tmp/perf-.map symbol table for recompiled JIT blocks." OFF) - option(PACKAGE_MODE "Use this option to ease packaging of PCSX2 (developer/distribution option)") -+set(ARMSX2_VERSION "" CACHE STRING "Reported version for builds without a git checkout") - option(BUNDLE_EMOJI_FONT "Bundles Noto Color Emoji for systems whose system emoji font isn't usable by freetype" ON) - option(POSITION_INDEPENDENT_CODE "Generate position-independent code. It is recommended that you leave this on." ON) - -diff --git a/cmake/Pcsx2Utils.cmake b/cmake/Pcsx2Utils.cmake ---- a/cmake/Pcsx2Utils.cmake -+++ b/cmake/Pcsx2Utils.cmake -@@ -84,6 +84,10 @@ function(get_git_version_info) - OUTPUT_VARIABLE PCSX2_GIT_DATE - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET) -+ elseif (ARMSX2_VERSION) -+ # Version for builds without a git checkout. -+ set(PCSX2_GIT_REV "${ARMSX2_VERSION}") -+ set(PCSX2_GIT_TAG "${ARMSX2_VERSION}") - endif() - if (NOT PCSX2_GIT_REV) - EXECUTE_PROCESS(WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD From 2726fd3883631f768cd6e77f92d4afd327edff01 Mon Sep 17 00:00:00 2001 From: aleksei74 Date: Tue, 11 Aug 2026 07:36:00 +0900 Subject: [PATCH 20/24] armsx2-sa: Update license and dependencies in package.mk --- .../packages/emulators/standalone/armsx2-sa/package.mk | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk index 506812f357..488cef0745 100644 --- a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk +++ b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk @@ -3,14 +3,11 @@ PKG_NAME="armsx2-sa" PKG_VERSION="2.6.6.5" -PKG_ARCH="aarch64" -PKG_LICENSE="GPL-3.0-or-later" +PKG_LICENSE="GPLv3" PKG_SITE="https://github.com/ARMSX2/ARMSX2" PKG_URL="${PKG_SITE}/archive/refs/tags/${PKG_VERSION}.tar.gz" PKG_LONGDESC="ARMSX2 is a native ARM64 PlayStation 2 (PS2) emulator, a fork of PCSX2 that ports the EE/IOP/VU JIT recompilers to ARM64." -PKG_DEPENDS_TARGET="toolchain llvm:host SDL3 libpng zlib libjpeg-turbo zstd lz4 libwebp freetype \ - plutosvg curl libpcap ffmpeg libX11 libXext qt6 ecm shaderc rapidyaml \ - kddockwidgets alsa-lib dbus ${VULKAN}" +PKG_DEPENDS_TARGET="toolchain llvm:host SDL3 libpng zlib libjpeg-turbo zstd lz4 libwebp freetype plutosvg curl libpcap ffmpeg libX11 libXext qt6 shaderc" PKG_TOOLCHAIN="manual" PKG_BUILD_FLAGS="speed" From e276e498118ac3be35920480eec36524a095bb23 Mon Sep 17 00:00:00 2001 From: aleksei74 Date: Tue, 11 Aug 2026 08:15:04 +0900 Subject: [PATCH 21/24] armsx2-sa: align package.mk with official upstream and remove standalone kddockwidgets/rapidyaml packages --- .../packages/devel/kddockwidgets/package.mk | 21 ------ .../packages/devel/rapidyaml/package.mk | 20 ------ .../emulators/standalone/armsx2-sa/package.mk | 72 ++++++------------- 3 files changed, 20 insertions(+), 93 deletions(-) delete mode 100644 projects/ROCKNIX/packages/devel/kddockwidgets/package.mk delete mode 100644 projects/ROCKNIX/packages/devel/rapidyaml/package.mk diff --git a/projects/ROCKNIX/packages/devel/kddockwidgets/package.mk b/projects/ROCKNIX/packages/devel/kddockwidgets/package.mk deleted file mode 100644 index ddddaf49ef..0000000000 --- a/projects/ROCKNIX/packages/devel/kddockwidgets/package.mk +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (C) 2026-present ROCKNIX (https://github.com/ROCKNIX) - -PKG_NAME="kddockwidgets" -PKG_VERSION="2.4.0" -PKG_LICENSE="GPL-2.0-only OR GPL-3.0-only" -PKG_SITE="https://github.com/KDAB/KDDockWidgets" -PKG_URL="${PKG_SITE}/archive/refs/tags/v${PKG_VERSION}.tar.gz" -PKG_LONGDESC="An advanced docking system for Qt." -PKG_DEPENDS_TARGET="toolchain qt6" -PKG_TOOLCHAIN="cmake" - -PKG_CMAKE_OPTS_TARGET="-DCMAKE_BUILD_TYPE=Release \ - -DKDDockWidgets_QT6=ON \ - -DKDDockWidgets_STATIC=OFF \ - -DKDDockWidgets_FRONTENDS=qtwidgets \ - -DKDDockWidgets_NO_SPDLOG=ON \ - -DKDDockWidgets_EXAMPLES=OFF \ - -DKDDockWidgets_TESTS=OFF \ - -DKDDockWidgets_DOCS=OFF \ - -DKDDockWidgets_PYTHON_BINDINGS=OFF" diff --git a/projects/ROCKNIX/packages/devel/rapidyaml/package.mk b/projects/ROCKNIX/packages/devel/rapidyaml/package.mk deleted file mode 100644 index 073cf854f6..0000000000 --- a/projects/ROCKNIX/packages/devel/rapidyaml/package.mk +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (C) 2026-present ROCKNIX (https://github.com/ROCKNIX) - -PKG_NAME="rapidyaml" -PKG_VERSION="0.12.1" -PKG_LICENSE="MIT" -PKG_SITE="https://github.com/biojppm/rapidyaml" -PKG_URL="${PKG_SITE}/releases/download/v${PKG_VERSION}/rapidyaml-${PKG_VERSION}-src.tgz" -PKG_LONGDESC="A fast YAML parser and emitter for C++." -PKG_DEPENDS_TARGET="toolchain" -PKG_TOOLCHAIN="cmake" -PKG_BUILD_FLAGS="+pic" - -PKG_CMAKE_OPTS_TARGET="-DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=OFF \ - -DRYML_INSTALL=ON \ - -DRYML_BUILD_TESTS=OFF \ - -DRYML_BUILD_BENCHMARKS=OFF \ - -DRYML_BUILD_TOOLS=OFF \ - -DRYML_BUILD_API=OFF" diff --git a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk index 488cef0745..e7a952ca84 100644 --- a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk +++ b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/package.mk @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (C) 2026-present ROCKNIX (https://github.com/ROCKNIX) +# Copyright (C) 2025-present ROCKNIX (https://github.com/ROCKNIX) PKG_NAME="armsx2-sa" PKG_VERSION="2.6.6.5" @@ -11,31 +11,23 @@ PKG_DEPENDS_TARGET="toolchain llvm:host SDL3 libpng zlib libjpeg-turbo zstd lz4 PKG_TOOLCHAIN="manual" PKG_BUILD_FLAGS="speed" -if [ "${DISPLAYSERVER}" = "wl" ]; then - PKG_DEPENDS_TARGET+=" wayland ${WINDOWMANAGER} xwayland xrandr libXi" -fi - -if [ "${OPENGL_SUPPORT}" = "yes" ]; then - PKG_DEPENDS_TARGET+=" ${OPENGL} libglvnd" -elif [ "${OPENGLES_SUPPORT}" = "yes" ]; then - PKG_DEPENDS_TARGET+=" ${OPENGLES}" -fi - -PATCHES_URL="https://github.com/PCSX2/pcsx2_patches/releases/download/latest/patches.zip" +PATCHES_URL="https://github.com/PCSX2/pcsx2_patches/archive/refs/tags/latest.zip" get_graphicdrivers -if listcontains "${GRAPHIC_DRIVERS}" "(panfrost)"; then - GRAPHICS_DRIVER="panfrost" -elif listcontains "${GRAPHIC_DRIVERS}" "(freedreno)"; then - GRAPHICS_DRIVER="freedreno" -fi + if listcontains "${GRAPHIC_DRIVERS}" "(panfrost)"; then + GRAPHICS_DRIVER="panfrost" + elif listcontains "${GRAPHIC_DRIVERS}" "(freedreno)"; then + GRAPHICS_DRIVER="freedreno" + fi pre_configure_target() { PCSX2_CMAKE_BASE=( # Reported version -DARMSX2_VERSION=${PKG_VERSION} -DCMAKE_BUILD_TYPE=Release + # Full-tree IPO stays off for Qt (not worth it)... -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF + # ...but stays on for just the recompiler/VU/EE/IOP core: -DLTO_PCSX2_CORE=ON -DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON -DUSE_VULKAN=ON @@ -46,33 +38,11 @@ pre_configure_target() { -DWAYLAND_API=ON -DX11_API=ON -DCMAKE_LINKER_TYPE=LLD - -DOVERRIDE_HOST_PAGE_SIZE=0x1000 - -DOVERRIDE_HOST_CACHE_LINE_SIZE=64 ) - case ${DEVICE} in - RK3576) - PCSX2_CMAKE_BASE+=( - -DCMAKE_C_FLAGS=-march=armv8-a+crc+crypto - -DCMAKE_CXX_FLAGS=-march=armv8-a+crc+crypto - ) - ;; - esac - - case ${TARGET_ARCH} in - aarch64) - for _v in CFLAGS CXXFLAGS LDFLAGS; do - export ${_v}="$(echo ${!_v} | sed 's/-march=[^ ]*//g; s/-mabi=lp64//g; s/-mtune=[^ ]*//g')" - done - ;; - x86_64) - for _v in CFLAGS CXXFLAGS LDFLAGS; do - export ${_v}="$(echo ${!_v} | sed 's/-mabi=lp64//g; s/-mtune=[^ ]*//g')" - done - ;; - esac - - export LDFLAGS="$(echo ${LDFLAGS} | sed 's/-fuse-ld=[^ ]*//g')" + for _v in CFLAGS CXXFLAGS LDFLAGS; do + export ${_v}="$(echo ${!_v} | sed 's/-mabi=lp64//g; s/-mtune=[^ ]*//g')" + done } make_target() { @@ -87,12 +57,6 @@ make_target() { -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_C_COMPILER="${TOOLCHAIN}/bin/clang" -DCMAKE_CXX_COMPILER="${TOOLCHAIN}/bin/clang++" - -DCMAKE_AR="${TOOLCHAIN}/bin/llvm-ar" - -DCMAKE_RANLIB="${TOOLCHAIN}/bin/llvm-ranlib" - -DCMAKE_NM="${TOOLCHAIN}/bin/llvm-nm" - -DCMAKE_OBJCOPY="${TOOLCHAIN}/bin/llvm-objcopy" - -DCMAKE_OBJDUMP="${TOOLCHAIN}/bin/llvm-objdump" - -DCMAKE_STRIP="${TOOLCHAIN}/bin/llvm-strip" -DCMAKE_C_COMPILER_AR="${TOOLCHAIN}/bin/llvm-ar" -DCMAKE_CXX_COMPILER_AR="${TOOLCHAIN}/bin/llvm-ar" -DCMAKE_C_COMPILER_RANLIB="${TOOLCHAIN}/bin/llvm-ranlib" @@ -109,14 +73,16 @@ make_target() { -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY - -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER -DLLVM_DIR="${TOOLCHAIN}/lib/cmake/llvm" + -DCMAKE_AR="${TOOLCHAIN}/bin/llvm-ar" + -DCMAKE_RANLIB="${TOOLCHAIN}/bin/llvm-ranlib" + -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER "${PCSX2_CMAKE_BASE[@]}" ) - cmake "${tgt_opts[@]}" cmake --build "${PKG_BUILD}/.${TARGET_NAME}" - wget -c -t 5 -O "${PKG_BUILD}/.${TARGET_NAME}/bin/resources/patches.zip" "${PATCHES_URL}" + ninja install + wget -c -t 5 -O "bin/resources/patches.zip" ${PATCHES_URL} } makeinstall_target() { @@ -128,6 +94,8 @@ makeinstall_target() { cp -rf ${PKG_BUILD}/.${TARGET_NAME}/bin/* ${INSTALL}/usr/share/armsx2-sa mkdir -p ${INSTALL}/usr/config + cp -rf ${PKG_DIR}/config/common/ARMSX2 ${INSTALL}/usr/config + case ${DEVICE} in S922X) cp -rf ${PKG_DIR}/config/S922X/ARMSX2 ${INSTALL}/usr/config @@ -149,5 +117,5 @@ post_install() { esac sed -e "s/@GRAPHICS@/${GRAPHICS}/g" \ - -i ${INSTALL}/usr/bin/start_armsx2.sh + -i ${INSTALL}/usr/bin/start_armsx2.sh } From d034f48d1ff5da39037dbeb88647dee1c154353e Mon Sep 17 00:00:00 2001 From: aleksei74 Date: Tue, 11 Aug 2026 08:21:24 +0900 Subject: [PATCH 22/24] armsx2-sa: remove obsolete config/ARMSX2/inis/PCSX2.ini --- .../armsx2-sa/config/ARMSX2/inis/PCSX2.ini | 620 ------------------ 1 file changed, 620 deletions(-) delete mode 100644 projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/config/ARMSX2/inis/PCSX2.ini diff --git a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/config/ARMSX2/inis/PCSX2.ini b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/config/ARMSX2/inis/PCSX2.ini deleted file mode 100644 index 5baee00a2d..0000000000 --- a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/config/ARMSX2/inis/PCSX2.ini +++ /dev/null @@ -1,620 +0,0 @@ -[UI] -SettingsVersion = 1 -InhibitScreensaver = true -ConfirmShutdown = true -StartPaused = false -PauseOnFocusLoss = false -StartFullscreen = false -DoubleClickTogglesFullscreen = true -HideMouseCursor = false -RenderToSeparateWindow = false -HideMainWindowWhenRunning = false -DisableWindowResize = false -PreferEnglishGameList = false -Theme = darkfusionblue -SetupWizardIncomplete = false -MainWindowGeometry = AdnQywADAAAAAAAAAAAAAAAAAn8AAALPAAAAAAAAAAAAAAJ/AAACzwAAAAAAAAAABQAAAAAAAAAAAAAAAn8AAALP -MainWindowState = AAAA/wAAAAD9AAAAAAAAAoAAAAKnAAAABAAAAAQAAAAIAAAACPwAAAABAAAAAgAAAAEAAAAOAHQAbwBvAGwAQgBhAHIAAAAAAP////8AAAAAAAAAAA== - - -[Folders] -Bios = /storage/roms/bios/ps2 -Snapshots = /storage/roms/screenshots -Savestates = /storage/roms/savestates/ps2 -MemoryCards = /storage/roms/ps2 -Logs = logs -Cheats = cheats -Patches = patches -UserResources = resources -Cache = cache -Textures = /storage/roms/ps2/textures -InputProfiles = inputprofiles -Videos = videos -DebuggerLayouts = debuggerlayouts -DebuggerSettings = debuggersettings - - -[EmuCore] -CdvdVerboseReads = false -CdvdDumpBlocks = false -CdvdPrecache = false -EnablePatches = true -EnableCheats = false -EnablePINE = false -EnableWideScreenPatches = false -EnableNoInterlacingPatches = false -EnableFastBoot = true -EnableFastBootFastForward = false -EnableThreadPinning = false -EnableRecordingTools = true -EnableGameFixes = true -SaveStateOnShutdown = false -UseSavestateSelector = true -EnableDiscordPresence = false -InhibitScreensaver = true -HostFs = false -BackupSavestate = true -WarnAboutUnsafeSettings = true -ManuallySetRealTimeClock = false -UseSystemLocaleFormat = false -SavestateCompressionType = 2 -SavestateCompressionRatio = 1 -GzipIsoIndexTemplate = $(f).pindex.tmp -PINESlot = 28011 -RtcYear = 0 -RtcMonth = 1 -RtcDay = 1 -RtcHour = 0 -RtcMinute = 0 -RtcSecond = 0 -BlockDumpSaveDirectory = - - -[EmuCore/Speedhacks] -EECycleRate = 0 -EECycleSkip = 0 -fastCDVD = false -IntcStat = true -WaitLoop = true -vuFlagHack = true -vuThread = true -vu1Instant = true - - -[EmuCore/CPU] -FPU.DenormalsAreZero = true -FPU.Roundmode = 3 -FPUDiv.DenormalsAreZero = true -FPUDiv.Roundmode = 0 -VU0.DenormalsAreZero = true -VU0.Roundmode = 3 -VU1.DenormalsAreZero = true -VU1.Roundmode = 3 -ExtraMemory = false - - -[EmuCore/CPU/Recompiler] -EnableEE = true -EnableIOP = true -EnableEECache = false -EnableVU0 = true -EnableVU1 = true -EnableFastmem = true -PauseOnTLBMiss = false -EnableVUProgramCache = false -vu0Overflow = true -vu0ExtraOverflow = false -vu0SignOverflow = false -vu0Underflow = false -vu1Overflow = true -vu1ExtraOverflow = false -vu1SignOverflow = false -vu1Underflow = false -fpuOverflow = true -fpuExtraOverflow = false -fpuFullMode = false - - -[EmuCore/GS] -VsyncEnable = false -DisableMailboxPresentation = false -ExtendedUpscalingMultipliers = false -VsyncQueueSize = 2 -FramerateNTSC = 59.94 -FrameratePAL = 50 -AspectRatio = Auto 4:3/3:2 -FMVAspectRatioSwitch = Off -DisplayRotation = 0 -ScreenshotSize = 0 -ScreenshotFormat = 0 -ScreenshotQuality = 90 -OrganizeScreenshotsByGame = false -OrganizeVideoCaptureByGame = false -StretchY = 100 -CropLeft = 0 -CropTop = 0 -CropRight = 0 -CropBottom = 0 -pcrtc_antiblur = true -disable_interlace_offset = false -pcrtc_offsets = false -pcrtc_overscan = false -IntegerScaling = false -UseDebugDevice = false -UseBlitSwapChain = false -DisableShaderCache = false -DisableFramebufferFetch = false -DisablePS2DepthQuantization = false -DisableVertexShaderExpand = false -SkipDuplicateFrames = true -OsdShowSpeed = false -OsdShowFPS = false -OsdShowVPS = false -OsdShowCPU = false -OsdShowGPU = false -OsdShowGPUDebug = false -OsdShowResolution = false -OsdShowGSStats = false -OsdShowIndicators = true -OsdShowSettings = false -OsdshowPatches = false -OsdShowInputs = false -OsdShowFrameTimes = false -OsdShowVersion = false -OsdShowHardwareInfo = false -OsdShowVideoCapture = true -OsdShowInputRec = true -OsdShowTextureReplacements = false -OsdBoldText = false -HWSpinGPUForReadbacks = false -HWSpinCPUForReadbacks = false -paltex = false -autoflush_sw = true -preload_frame_with_gs_data = false -mipmap = true -UserHacks = false -UserHacks_align_sprite_X = false -UserHacks_AutoFlushLevel = 0 -UserHacks_CPU_FB_Conversion = false -UserHacks_ReadTCOnClose = false -UserHacks_DisableDepthSupport = false -UserHacks_DisablePartialInvalidation = false -UserHacks_Disable_Safe_Features = false -UserHacks_DisableRenderFixes = false -UserHacks_merge_pp_sprite = false -UserHacks_ForceEvenSpritePosition = false -UserHacks_BilinearHack = 0 -UserHacks_NativePaletteDraw = false -UserHacks_TextureInsideRt = 0 -UserHacks_Limit24BitDepth = 0 -UserHacks_EstimateTextureRegion = false -UserHacks_DrawBuffering = false -fxaa = false -ShadeBoost = false -DumpGSData = false -SaveRT = false -SaveFrame = false -SaveTexture = false -SaveDepth = false -SaveAlpha = false -SaveInfo = false -SaveTransferImages = false -SaveDrawStats = false -SaveFrameStats = false -SaveHWConfig = false -DumpReplaceableTextures = false -DumpReplaceableMipmaps = false -DumpTexturesWithFMVActive = false -DumpDirectTextures = true -DumpPaletteTextures = true -LoadTextureReplacements = false -LoadTextureReplacementsAsync = true -PrecacheTextureReplacements = false -EnableVideoCapture = true -EnableVideoCaptureParameters = false -VideoCaptureAutoResolution = false -EnableAudioCapture = true -EnableAudioCaptureParameters = false -linear_present_mode = 1 -deinterlace_mode = 0 -OsdScale = 100 -OsdMargin = 10 -OsdFontPath = -OsdMessagesPos = 1 -OsdPerformancePos = 3 -Renderer = -1 -upscale_multiplier = 1 -hw_mipmap = true -HWAccurateAlphaTest = false -HWAA1 = false -UseDebugBlend = false -HWROV = false -HWROVLogging = false -HWROVBarriersVK = false -accurate_blending_unit = 1 -filter = 2 -texture_preloading = 2 -GSDumpCompression = 2 -HWDownloadMode = 0 -CASMode = 0 -CASSharpness = 50 -dithering_ps2 = 2 -MaxAnisotropy = 0 -extrathreads = 3 -extrathreads_height = 4 -TVShader = 0 -UserHacks_SkipDraw_Start = 0 -UserHacks_SkipDraw_End = 0 -UserHacks_HalfPixelOffset = 0 -UserHacks_round_sprite_offset = 0 -UserHacks_native_scaling = 0 -UserHacks_TCOffsetX = 0 -UserHacks_TCOffsetY = 0 -UserHacks_CPUSpriteRenderBW = 0 -UserHacks_CPUSpriteRenderLevel = 0 -UserHacks_CPUCLUTRender = 0 -UserHacks_GPUTargetCLUTMode = 0 -TriFilter = -1 -OverrideTextureBarriers = 0 -DepthFeedbackMode = 1 -ShadeBoost_Brightness = 50 -ShadeBoost_Contrast = 50 -ShadeBoost_Saturation = 50 -ShadeBoost_Gamma = 50 -ExclusiveFullscreenControl = -1 -png_compression_level = 1 -SaveDrawStart = 0 -SaveDrawCount = 5000 -SaveDrawBy = 1 -SaveFrameStart = 0 -SaveFrameCount = -1 -SaveFrameBy = 1 -CaptureContainer = mp4 -VideoCaptureCodec = -VideoCaptureFormat = -VideoCaptureParameters = -AudioCaptureCodec = -AudioCaptureParameters = -VideoCaptureBitrate = 6000 -VideoCaptureWidth = 640 -VideoCaptureHeight = 480 -AudioCaptureBitrate = 192 -Adapter = -HWDumpDirectory = -SWDumpDirectory = -SyncToHostRefreshRate = false -UseVSyncForTiming = false - - -[SPU2/Debug] -Global_Enable = false -Show_Messages = false -Show_Messages_Key_On_Off = false -Show_Messages_Voice_Off = false -Show_Messages_DMA_Transfer = false -Show_Messages_AutoDMA = false -Show_Messages_CacheStats = false -Log_Register_Access = false -Log_DMA_Transfers = false -Log_WAVE_Output = false -Dump_Info = false -Dump_Memory = false -Dump_Regs = false - - -[SPU2/Output] -StandardVolume = 100 -FastForwardVolume = 100 -OutputMuted = false -Backend = Cubeb -SyncMode = TimeStretch -DriverName = -DeviceName = -ExpansionMode = Disabled -OutputLatencyMinimal = false -BufferMS = 50 -OutputLatencyMS = 20 -StretchSequenceLengthMS = 30 -StretchSeekWindowMS = 20 -StretchOverlapMS = 10 -StretchUseQuickSeek = false -StretchUseAAFilter = false -ExpandBlockSize = 2048 -ExpandCircularWrap = 90 -ExpandShift = 0 -ExpandDepth = 1 -ExpandFocus = 0 -ExpandCenterImage = 1 -ExpandFrontSeparation = 1 -ExpandRearSeparation = 1 -ExpandLowCutoff = 40 -ExpandHighCutoff = 90 - - -[DEV9/Eth] -EthEnable = false -EthApi = Unset -EthDevice = -EthLogDHCP = false -EthLogDNS = false -InterceptDHCP = false -PS2IP = 0.0.0.0 -Mask = 0.0.0.0 -Gateway = 0.0.0.0 -DNS1 = 0.0.0.0 -DNS2 = 0.0.0.0 -AutoMask = true -AutoGateway = true -ModeDNS1 = Auto -ModeDNS2 = Auto - - -[DEV9/Eth/Hosts] -Count = 0 - - -[DEV9/Hdd] -HddEnable = false -HddFile = DEV9hdd.raw - - -[EmuCore/Gamefixes] -VuAddSubHack = false -FpuMulHack = false -XgKickHack = false -EETimingHack = false -InstantDMAHack = false -SoftwareRendererFMVHack = false -SkipMPEGHack = false -OPHFlagHack = false -DMABusyHack = false -VIFFIFOHack = false -VIF1StallHack = false -GIFFIFOHack = false -GoemonTlbHack = false -IbitHack = false -VUSyncHack = false -VUOverflowHack = false -BlitInternalFPSHack = false -FullVU0SyncHack = false - - -[EmuCore/Profiler] -Enabled = false -RecBlocks_EE = true -RecBlocks_IOP = true -RecBlocks_VU0 = true -RecBlocks_VU1 = true -EnablePerfDump = false - - -[Debugger/Analysis] -RunCondition = If Debugger Is Open -GenerateSymbolsForIRXExports = true -AutomaticallySelectSymbolsToClear = true -ImportSymbolsFromELF = true -DemangleSymbols = true -DemangleParameters = true -FunctionScanMode = Scan From ELF -CustomFunctionScanRange = false -FunctionScanStartAddress = -FunctionScanEndAddress = -GenerateFunctionHashes = true - - -[Debugger/Analysis/SymbolSources] -Count = 0 - - -[Debugger/Analysis/ExtraSymbolFiles] -Count = 0 - - -[EmuCore/TraceLog] -Enabled = false -EE.bios = false -EE.memory = false -EE.giftag = false -EE.vifcode = false -EE.mskpath3 = false -EE.r5900 = false -EE.cop0 = false -EE.cop1 = false -EE.cop2 = false -EE.cache = false -EE.knownhw = false -EE.unknownhw = false -EE.dmahw = false -EE.ipu = false -EE.dmac = false -EE.counters = false -EE.spr = false -EE.vif = false -EE.gif = false -IOP.bios = false -IOP.memcards = false -IOP.pad = false -IOP.r3000a = false -IOP.cop2 = false -IOP.memory = false -IOP.knownhw = false -IOP.unknownhw = false -IOP.dmahw = false -IOP.dmac = false -IOP.counters = false -IOP.cdvd = false -IOP.mdec = false -MISC.sif = false - - -[Achievements] -Enabled = false -ChallengeMode = false -EncoreMode = false -SpectatorMode = false -UnofficialTestMode = false -Notifications = true -LeaderboardNotifications = true -SoundEffects = true -InfoSound = true -UnlockSound = true -LBSubmitSound = true -Overlays = true -LBOverlays = true -NotificationsDuration = 5 -LeaderboardsDuration = 10 -OverlayPosition = 8 -NotificationPosition = 1 -InfoSoundName = -UnlockSoundName = -LBSubmitSoundName = - - -[Filenames] -BIOS = - - -[Framerate] -NominalScalar = 1 -TurboScalar = 2 -SlomoScalar = 0.5 - - -[MemoryCards] -Slot1_Enable = true -Slot1_Filename = Mcd001.ps2 -Slot2_Enable = true -Slot2_Filename = Mcd002.ps2 -Multitap1_Slot2_Enable = false -Multitap1_Slot2_Filename = Mcd-Multitap1-Slot02.ps2 -Multitap1_Slot3_Enable = false -Multitap1_Slot3_Filename = Mcd-Multitap1-Slot03.ps2 -Multitap1_Slot4_Enable = false -Multitap1_Slot4_Filename = Mcd-Multitap1-Slot04.ps2 -Multitap2_Slot2_Enable = false -Multitap2_Slot2_Filename = Mcd-Multitap2-Slot02.ps2 -Multitap2_Slot3_Enable = false -Multitap2_Slot3_Filename = Mcd-Multitap2-Slot03.ps2 -Multitap2_Slot4_Enable = false -Multitap2_Slot4_Filename = Mcd-Multitap2-Slot04.ps2 - - -[Logging] -EnableSystemConsole = false -EnableFileLogging = true -EnableTimestamps = true -EnableEESIOInput = false -EnableVerbose = false -EnableEEConsole = false -EnableIOPConsole = false -EnableInputRecordingLogs = true -EnableControllerLogs = false - - -[InputSources] -Keyboard = true -Mouse = true -SDL = true -SDLControllerEnhancedMode = true -SDLPS5PlayerLED = true - - -[Hotkeys] -LoadStateFromSlot = SDL-0/Guide & SDL-0/LeftShoulder -SaveStateToSlot = SDL-0/Guide & SDL-0/RightShoulder -OpenPauseMenu = SDL-0/Guide & SDL-0/FaceNorth -GSDumpSingleFrame = SDL-0/Guide & SDL-0/FaceEast - - -[Pad] -MultitapPort1 = false -MultitapPort2 = false -PointerXScale = 8 -PointerYScale = 8 - - -[Pad1] -Type = DualShock2 -InvertL = 0 -InvertR = 0 -Deadzone = 0 -AxisScale = 1.33 -LargeMotorScale = 1 -SmallMotorScale = 1 -ButtonDeadzone = 0 -PressureModifier = 0.5 -Up = SDL-0/DPadUp -Right = SDL-0/DPadRight -Down = SDL-0/DPadDown -Left = SDL-0/DPadLeft -Triangle = SDL-0/FaceNorth -Circle = SDL-0/FaceEast -Cross = SDL-0/FaceSouth -Square = SDL-0/FaceWest -Select = SDL-0/Back -Start = SDL-0/Start -L1 = SDL-0/LeftShoulder -L2 = SDL-0/+LeftTrigger -R1 = SDL-0/RightShoulder -R2 = SDL-0/+RightTrigger -L3 = SDL-0/LeftStick -R3 = SDL-0/RightStick -LUp = SDL-0/-LeftY -LRight = SDL-0/+LeftX -LDown = SDL-0/+LeftY -LLeft = SDL-0/-LeftX -RUp = SDL-0/-RightY -RRight = SDL-0/+RightX -RDown = SDL-0/+RightY -RLeft = SDL-0/-RightX -Analog = SDL-0/Guide -LargeMotor = SDL-0/LargeMotor -SmallMotor = SDL-0/SmallMotor - - -[Pad2] -Type = None - - -[Pad3] -Type = None - - -[Pad4] -Type = None - - -[Pad5] -Type = None - - -[Pad6] -Type = None - - -[Pad7] -Type = None - - -[Pad8] -Type = None - - -[USB1] -Type = None - - -[USB2] -Type = None - - -[AutoUpdater] -CheckAtStartup = false - - -[GameList] -Paths = /storage/roms/ps2 - - -[GameListTableView] -HeaderState = AAAA/wAAAAAAAAABAAAAAAAAAAIBAAAAAAAAAAAAAAALGAQAAAADAAAACgAAAGQAAAAEAAAASwAAAAMAAABkAAAEGAAAAAsBAQAAAAAAAAAAAAAAAAAAZP////8AAACEAAAAAAAAAAsAAAA3AAAAAQAAAAAAAABVAAAAAQAAAAAAAAHPAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAAAAABfAAAAAQAAAAAAAABaAAAAAQAAAAAAAABQAAAAAQAAAAAAAAA8AAAAAQAAAAAAAAB4AAAAAQAAAAAAAAAAAAAAAQAAAAAAAAPoAAAAAAAAAAAAAAAAAAAAAAAAAAAB From 1f9d5143d35347b4ddd8b777e4b40e0bcb143225 Mon Sep 17 00:00:00 2001 From: aleksei74 Date: Tue, 11 Aug 2026 08:22:12 +0900 Subject: [PATCH 23/24] armsx2-sa: restore start_armsx2.sh to official upstream version --- .../armsx2-sa/scripts/start_armsx2.sh | 256 +++++++++++++----- 1 file changed, 182 insertions(+), 74 deletions(-) mode change 100644 => 100755 projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/scripts/start_armsx2.sh diff --git a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/scripts/start_armsx2.sh b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/scripts/start_armsx2.sh old mode 100644 new mode 100755 index cc4fd0b187..a1c89de23b --- a/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/scripts/start_armsx2.sh +++ b/projects/ROCKNIX/packages/emulators/standalone/armsx2-sa/scripts/start_armsx2.sh @@ -1,32 +1,39 @@ #!/bin/bash # SPDX-License-Identifier: GPL-2.0-or-later -# Copyright (C) 2026-present ROCKNIX (https://github.com/ROCKNIX) +# Copyright (C) 2022-present JELOS (https://github.com/JustEnoughLinuxOS) . /etc/profile -DATA_ROOT="/storage/.config/ARMSX2" -mkdir -p "${DATA_ROOT}/inis" +#Check if ARMSX2 exists in .config +if [ ! -d "/storage/.config/ARMSX2" ]; then + mkdir -p "/storage/.config/ARMSX2" + cp -r "/usr/config/ARMSX2" "/storage/.config/" +fi -if [ ! -f "${DATA_ROOT}/inis/PCSX2.ini" ]; then - cp -r /usr/config/ARMSX2/inis/PCSX2.ini "${DATA_ROOT}/inis/PCSX2.ini" +#Check if ARMSX2 ini exists in .config +if [ ! -f "/storage/.config/ARMSX2/inis/PCSX2.ini" ]; then + cp -r "/usr/config/ARMSX2/inis/PCSX2.ini" "/storage/.config/ARMSX2/inis/" fi -if [ ! -f "${DATA_ROOT}/inis/secrets.ini" ]; then - cp -r /usr/config/ARMSX2/inis/secrets.ini "${DATA_ROOT}/inis/secrets.ini" +#Check if secrets ini exists in .config +if [ ! -f "/storage/.config/ARMSX2/inis/secrets.ini" ]; then + cp -r "/usr/config/ARMSX2/inis/secrets.ini" "/storage/.config/ARMSX2/inis/" fi -mkdir -p /storage/roms/bios/ps2 -mkdir -p /storage/roms/savestates/ps2 -mkdir -p /storage/roms/ps2/textures -sed -i '/^Textures =/c\Textures = /storage/roms/ps2/textures' "${DATA_ROOT}/inis/PCSX2.ini" +#Make ARMSX2 bios folder +if [ ! -d "/storage/roms/bios/armsx2" ]; then + mkdir -p "/storage/roms/bios/armsx2" +fi -# Use Rocknix's curated SDL controller DB -ln -sf /usr/config/SDL-GameControllerDB/gamecontrollerdb.txt "${DATA_ROOT}/game_controller_db.txt" 2>/dev/null +#Create PS2 savestates folder +if [ ! -d "/storage/roms/savestates/ps2" ]; then + mkdir -p "/storage/roms/savestates/ps2" +fi -# EmulationStation Features -GAME=$(echo "${1}" | sed "s#^/.*/##") -PLATFORM=$(echo "${2}" | sed "s#^/.*/##") +#Emulation Station Features +GAME=$(echo "${1}"| sed "s#^/.*/##") +PLATFORM=$(echo "${2}"| sed "s#^/.*/##") ASPECT=$(get_setting aspect_ratio "${PLATFORM}" "${GAME}") FILTER=$(get_setting bilinear_filtering "${PLATFORM}" "${GAME}") FPS=$(get_setting show_fps "${PLATFORM}" "${GAME}") @@ -38,73 +45,174 @@ IRES=$(get_setting internal_resolution "${PLATFORM}" "${GAME}") VSYNC=$(get_setting vsync "${PLATFORM}" "${GAME}") ENABLE_WIDESCREEN_PATCHES=$(get_setting enable_widescreen_patches "${PLATFORM}" "${GAME}") +#Set the cores to use CORES=$(get_setting "cores" "${PLATFORM}" "${GAME}") -if [ "${CORES}" = "little" ]; then +if [ "${CORES}" = "little" ] +then EMUPERF="${SLOW_CORES}" -elif [ "${CORES}" = "big" ]; then +elif [ "${CORES}" = "big" ] +then EMUPERF="${FAST_CORES}" else + #All.. unset EMUPERF fi -if [ "$ASPECT" = "0" ]; then - sed -i '/^AspectRatio =/c\AspectRatio = 4:3' "${DATA_ROOT}/inis/PCSX2.ini" -elif [ "$ASPECT" = "1" ]; then - sed -i '/^AspectRatio =/c\AspectRatio = 16:9' "${DATA_ROOT}/inis/PCSX2.ini" -elif [ "$ASPECT" = "2" ]; then - sed -i '/^AspectRatio =/c\AspectRatio = Stretch' "${DATA_ROOT}/inis/PCSX2.ini" -fi - -if [ "$FILTER" = "0" ]; then - sed -i '/^filter =/c\filter = 0' "${DATA_ROOT}/inis/PCSX2.ini" -elif [ "$FILTER" = "1" ]; then - sed -i '/^filter =/c\filter = 1' "${DATA_ROOT}/inis/PCSX2.ini" -elif [ "$FILTER" = "2" ]; then - sed -i '/^filter =/c\filter = 2' "${DATA_ROOT}/inis/PCSX2.ini" -elif [ "$FILTER" = "3" ]; then - sed -i '/^filter =/c\filter = 3' "${DATA_ROOT}/inis/PCSX2.ini" -fi - -if [ "$GRENDERER" = "0" ]; then - sed -i '/^Renderer =/c\Renderer = -1' "${DATA_ROOT}/inis/PCSX2.ini" -elif [ "$GRENDERER" = "1" ]; then - sed -i '/^Renderer =/c\Renderer = 12' "${DATA_ROOT}/inis/PCSX2.ini" -elif [ "$GRENDERER" = "2" ]; then - sed -i '/^Renderer =/c\Renderer = 14' "${DATA_ROOT}/inis/PCSX2.ini" -elif [ "$GRENDERER" = "3" ]; then - sed -i '/^Renderer =/c\Renderer = 13' "${DATA_ROOT}/inis/PCSX2.ini" -fi - -if [ "$IRES" > "0" ]; then - sed -i "/^upscale_multiplier =/c\upscale_multiplier = $IRES" "${DATA_ROOT}/inis/PCSX2.ini" -else - sed -i '/^upscale_multiplier =/c\upscale_multiplier = 1' "${DATA_ROOT}/inis/PCSX2.ini" -fi - -if [ "$FPS" = "false" ]; then - sed -i '/^OsdShowFPS =/c\OsdShowFPS = false' "${DATA_ROOT}/inis/PCSX2.ini" -elif [ "$FPS" = "true" ]; then - sed -i '/^OsdShowFPS =/c\OsdShowFPS = true' "${DATA_ROOT}/inis/PCSX2.ini" -fi - -if [ "$ENABLE_WIDESCREEN_PATCHES" = "true" ]; then - sed -i '/^EnableWideScreenPatches =/c\EnableWideScreenPatches = true' "${DATA_ROOT}/inis/PCSX2.ini" -else - sed -i '/^EnableWideScreenPatches =/c\EnableWideScreenPatches = false' "${DATA_ROOT}/inis/PCSX2.ini" -fi - -if [ -f /usr/bin/cheevos_armsx2.sh ]; then + #Aspect Ratio + if [ "$ASPECT" = "0" ] + then + sed -i '/^AspectRatio =/c\AspectRatio = 4:3' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$ASPECT" = "1" ] + then + sed -i '/^AspectRatio =/c\AspectRatio = 16:9' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$ASPECT" = "2" ] + then + sed -i '/^AspectRatio =/c\AspectRatio = Stretch' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + + #Bilinear Filtering + if [ "$FILTER" = "0" ] + then + sed -i '/^filter =/c\filter = 0' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$FILTER" = "1" ] + then + sed -i '/^filter =/c\filter = 1' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$FILTER" = "2" ] + then + sed -i '/^filter =/c\filter = 2' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$FILTER" = "3" ] + then + sed -i '/^filter =/c\filter = 3' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + + #Graphics Backend + if [ "$GRENDERER" = "0" ] + then + sed -i '/^Renderer =/c\Renderer = -1' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$GRENDERER" = "1" ] + then + sed -i '/^Renderer =/c\Renderer = 12' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$GRENDERER" = "2" ] + then + sed -i '/^Renderer =/c\Renderer = 14' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$GRENDERER" = "3" ] + then + sed -i '/^Renderer =/c\Renderer = 13' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + + #Internal Resolution + if [ "$IRES" > "0" ] + then + sed -i "/^upscale_multiplier =/c\upscale_multiplier = $IRES" /storage/.config/ARMSX2/inis/PCSX2.ini + else + sed -i '/^upscale_multiplier =/c\upscale_multiplier = 1' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + + #Show FPS + if [ "$FPS" = "false" ] + then + sed -i '/^OsdShowFPS =/c\OsdShowFPS = false' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$FPS" = "true" ] + then + sed -i '/^OsdShowFPS =/c\OsdShowFPS = true' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + + #EE Cycle Rate + sed -i '/^EECycleRate =/c\EECycleRate = 0' /storage/.config/ARMSX2/inis/PCSX2.ini + if [ "$RATE" = "0" ] + then + sed -i '/^EECycleRate =/c\EECycleRate = -3' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$RATE" = "1" ] + then + sed -i '/^EECycleRate =/c\EECycleRate = -2' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$RATE" = "2" ] + then + sed -i '/^EECycleRate =/c\EECycleRate = -1' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$RATE" = "3" ] + then + sed -i '/^EECycleRate =/c\EECycleRate = 0' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$RATE" = "4" ] + then + sed -i '/^EECycleRate =/c\EECycleRate = 1' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$RATE" = "5" ] + then + sed -i '/^EECycleRate =/c\EECycleRate = 2' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$RATE" = "6" ] + then + sed -i '/^EECycleRate =/c\EECycleRate = 3' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + + #EE Cycle Skip + sed -i '/^EECycleSkip =/c\EECycleSkip = 0' /storage/.config/ARMSX2/inis/PCSX2.ini + if [ "$SKIP" = "0" ] + then + sed -i '/^EECycleSkip =/c\EECycleSkip = 0' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$SKIP" = "1" ] + then + sed -i '/^EECycleSkip =/c\EECycleSkip = 1' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$SKIP" = "2" ] + then + sed -i '/^EECycleSkip =/c\EECycleSkip = 2' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$SKIP" = "3" ] + then + sed -i '/^EECycleSkip =/c\EECycleSkip = 3' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + +#HW download mode + sed -i '/^HWDownloadMode =/c\HWDownloadMode = 0' /storage/.config/ARMSX2/inis/PCSX2.ini + if [ "$HWDOWNLOAD" = "0" ] + then + sed -i '/^HWDownloadMode =/c\HWDownloadMode = 0' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$HWDOWNLOAD" = "1" ] + then + sed -i '/^HWDownloadMode =/c\HWDownloadMode = 1' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$HWDOWNLOAD" = "2" ] + then + sed -i '/^HWDownloadMode =/c\HWDownloadMode = 2' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + if [ "$HWDOWNLOAD" = "3" ] + then + sed -i '/^HWDownloadMode =/c\HWDownloadMode = 3' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + +#Widescreen patches + if [ "$ENABLE_WIDESCREEN_PATCHES" = "true" ] + then + sed -i '/^EnableWideScreenPatches =/c\EnableWideScreenPatches = true' /storage/.config/ARMSX2/inis/PCSX2.ini + else + sed -i '/^EnableWideScreenPatches =/c\EnableWideScreenPatches = false' /storage/.config/ARMSX2/inis/PCSX2.ini + fi + +#Retroachievements /usr/bin/cheevos_armsx2.sh -fi +#Graphic driver fixes @GRAPHICS@ -export QT_QPA_PLATFORM=wayland -export SDL_AUDIODRIVER=pulseaudio +#Set QT enviornment to wayland + export QT_QPA_PLATFORM=wayland -set_kill set "-9 armsx2-qt" -${EMUPERF} /usr/share/armsx2-sa/armsx2-qt \ - -bigpicture \ - -fullscreen \ - -datapath /storage/.config \ - -- "${1}" +#Run ARMSX2 emulator + export SDL_AUDIODRIVER=pulseaudio + set_kill set "-9 armsx2-qt" + ${EMUPERF} /usr/share/armsx2-sa/armsx2-qt -bigpicture -fullscreen "${1}" From 66d85a76b0359b821d6c6a2f7aea2ea79fc10d09 Mon Sep 17 00:00:00 2001 From: aleksei74 Date: Tue, 11 Aug 2026 08:34:56 +0900 Subject: [PATCH 24/24] yaps2-sa" Delete projects/ROCKNIX/packages/emulators/standalone/yaps2-sa/patches/003-legacy-texture-replacements.patch --- .../003-legacy-texture-replacements.patch | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 projects/ROCKNIX/packages/emulators/standalone/yaps2-sa/patches/003-legacy-texture-replacements.patch diff --git a/projects/ROCKNIX/packages/emulators/standalone/yaps2-sa/patches/003-legacy-texture-replacements.patch b/projects/ROCKNIX/packages/emulators/standalone/yaps2-sa/patches/003-legacy-texture-replacements.patch deleted file mode 100644 index 86cf24e5dd..0000000000 --- a/projects/ROCKNIX/packages/emulators/standalone/yaps2-sa/patches/003-legacy-texture-replacements.patch +++ /dev/null @@ -1,77 +0,0 @@ ---- a/pcsx2/GS/Renderers/HW/GSTextureReplacements.h -+++ b/pcsx2/GS/Renderers/HW/GSTextureReplacements.h -@@ -39,6 +39,7 @@ namespace GSTextureReplacements - u32 CalcMipmapLevelsForReplacement(u32 width, u32 height); - - bool HasAnyReplacementTextures(); -+ bool HasLegacyTextureReplacements(); - bool HasReplacementTextureWithOtherPalette(const GSTextureCache::HashCacheKey& hash); - GSTexture* LookupReplacementTexture(const GSTextureCache::HashCacheKey& hash, bool mipmap, bool* pending, std::pair* alpha_minmax); - GSTexture* CreateReplacementTexture(const ReplacementTexture& rtex, bool mipmap); ---- a/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp -+++ b/pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp -@@ -127,6 +127,7 @@ namespace GSTextureReplacements - static std::mutex s_dumped_textures_mutex; - - /// Lookup map of texture names to replacements, if they exist. -+ static bool s_has_legacy_replacements; - static std::unordered_map s_replacement_texture_filenames; - - /// Lookup map of texture names without CLUT hash, to know when we need to disable paltex. -@@ -381,6 +382,7 @@ void GSTextureReplacements::ReloadReplacementMap() - // clear out the caches - { - s_replacement_texture_filenames.clear(); -+ s_has_legacy_replacements = false; - s_replacement_textures_without_clut_hash.clear(); - - std::unique_lock lock(s_replacement_texture_cache_mutex); -@@ -429,6 +431,21 @@ void GSTextureReplacements::ReloadReplacementMap() - if (!name.has_value()) - continue; - -+ // Old PCSX2 filenames retain TEX0.TCC in bit 14. Detect that bit -+ // instead of treating every non-region filename as legacy, since -+ // current packs can legitimately contain both full and region names. -+ const std::string_view title = Path::GetFileTitle(filename); -+ const size_t mip_pos = title.find("-mip"); -+ const size_t bits_end = (mip_pos == std::string_view::npos) ? title.size() : mip_pos; -+ const size_t bits_start = bits_end ? title.rfind('-', bits_end - 1) : std::string_view::npos; -+ if (bits_start != std::string_view::npos) -+ { -+ u32 raw_bits = 0; -+ const std::string bits_string(title.substr(bits_start + 1, bits_end - bits_start - 1)); -+ if (std::sscanf(bits_string.c_str(), "%x", &raw_bits) == 1) -+ s_has_legacy_replacements |= ((raw_bits & (1u << 14)) != 0); -+ } -+ - DbgCon.WriteLn("Found %ux%u replacement '%.*s'", name->Width(), name->Height(), static_cast(filename.size()), filename.data()); - s_replacement_texture_filenames.emplace(name.value(), std::move(fd.FileName)); - -@@ -496,6 +513,11 @@ bool GSTextureReplacements::HasAnyReplacementTextures() - return !s_replacement_texture_filenames.empty(); - } - -+bool GSTextureReplacements::HasLegacyTextureReplacements() -+{ -+ return s_has_legacy_replacements; -+} -+ - bool GSTextureReplacements::HasReplacementTextureWithOtherPalette(const GSTextureCache::HashCacheKey& hash) - { - const TextureName name(CreateTextureName(hash.WithRemovedCLUTHash(), 0)); ---- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp -+++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp -@@ -8925,6 +8925,12 @@ void GSTextureCache::SourceRegion::AdjustTEX0(GIFRegTEX0* TEX0) const - - GSTextureCache::SourceRegion GSTextureCache::SourceRegion::Create(GIFRegTEX0 TEX0, GIFRegCLAMP CLAMP) - { -+ // PCSX2 1.7 texture packs were hashed as complete textures. If the pack -+ // contains legacy filenames, retain that layout instead of generating -+ // region hashes which cannot match the old replacements. -+ if (GSTextureReplacements::HasLegacyTextureReplacements()) -+ return {}; -+ - SourceRegion region = {}; - - if (CLAMP.WMS == CLAMP_REGION_CLAMP && CLAMP.MAXU >= CLAMP.MINU)