From 680d461f099a5dd0377548b8945cbdcac87919c9 Mon Sep 17 00:00:00 2001 From: Chongbiao Chen Date: Mon, 25 May 2026 20:13:01 +0800 Subject: [PATCH 1/4] fix: Add fix for DRM plane switching CRTC on Qualcomm On Qualcomm platforms (SC8280XP), the DRM atomic API does not allow switching a plane from one CRTC to another in a single commit. When toggling mirror/extend mode with multiple displays, mutter's find_unassigned_plane() could assign a plane that is still active on another CRTC, causing drmModeAtomicCommit to return EINVAL. Add a patch that: - Tracks current_crtc_id per plane at init time - Skips planes currently active on a different CRTC in find_unassigned_plane() - Checks mutter's internal assignment state for additional safety Based on upstream MR !4850 and !5055. Bug: https://gitlab.gnome.org/GNOME/mutter/-/issues/4399 Signed-off-by: Chongbiao Chen --- ...assigning-plane-active-on-other-CRTC.patch | 155 ++++++++++++++++++ debian/patches/series | 1 + 2 files changed, 156 insertions(+) create mode 100644 debian/patches/radxa/kms-plane-Avoid-assigning-plane-active-on-other-CRTC.patch diff --git a/debian/patches/radxa/kms-plane-Avoid-assigning-plane-active-on-other-CRTC.patch b/debian/patches/radxa/kms-plane-Avoid-assigning-plane-active-on-other-CRTC.patch new file mode 100644 index 00000000..70be0275 --- /dev/null +++ b/debian/patches/radxa/kms-plane-Avoid-assigning-plane-active-on-other-CRTC.patch @@ -0,0 +1,155 @@ +From: Alessandro Astone +Date: Fri, 12 Dec 2025 11:12:12 +0100 +Subject: kms/plane: Avoid assigning a plane that is active on another CRTC + +The DRM atomic API does not allow switching a plane from one CRTC to +another in a single commit. On Qualcomm platforms (SC8280XP), the +kernel's `plane_switching_crtc()` check rejects atomic commits that +reassign a plane to a different CRTC without first disabling it. + +Add a `current_crtc_id` field to `MetaKmsPlane` (read from the DRM +plane at init time) and use it in `find_unassigned_plane()` to skip +planes that are currently active on a different CRTC. Also skip planes +that are already assigned to another CRTC in mutter's internal state. + +This fixes "drmModeAtomicCommit: Invalid argument" errors when toggling +mirror/extend mode with multiple displays connected. + +Origin: upstream, https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4850 +Bug: https://gitlab.gnome.org/GNOME/mutter/-/issues/4399 +Bug-Radxa: HDMI/DP multi-display mode switching EINVAL on Q8B (SC8280XP) +Forwarded: not-needed +Last-Update: 2025-05-25 +--- + src/backends/native/meta-crtc-kms.c | 53 ++++++++++++++++++++++++++++ + src/backends/native/meta-kms-plane-private.h | 2 + + src/backends/native/meta-kms-plane.c | 8 ++++ + 3 files changed, 63 insertions(+) + +diff --git a/src/backends/native/meta-crtc-kms.c b/src/backends/native/meta-crtc-kms.c +index 3498f922a..9a4051c7b 100644 +--- a/src/backends/native/meta-crtc-kms.c ++++ b/src/backends/native/meta-crtc-kms.c +@@ -30,6 +30,7 @@ + #include "backends/native/meta-kms-device.h" + #include "backends/native/meta-kms-mode.h" + #include "backends/native/meta-kms-plane.h" ++#include "backends/native/meta-kms-plane-private.h" + #include "backends/native/meta-kms-update.h" + #include "backends/native/meta-kms.h" + #include "backends/native/meta-monitor-manager-native.h" +@@ -247,6 +248,44 @@ is_plane_assigned (MetaKmsPlane *plane, + return FALSE; + } + ++static gboolean ++is_plane_active_on_other_crtc (MetaKmsDevice *kms_device, ++ MetaKmsPlane *kms_plane, ++ MetaKmsCrtc *target_kms_crtc, ++ MetaKmsPlaneType kms_plane_type) ++{ ++ GList *l; ++ ++ for (l = meta_kms_device_get_crtcs (kms_device); l; l = l->next) ++ { ++ MetaKmsCrtc *kms_crtc = l->data; ++ MetaCrtcKms *crtc_kms; ++ MetaKmsPlane *assigned = NULL; ++ ++ if (kms_crtc == target_kms_crtc) ++ continue; ++ ++ crtc_kms = meta_crtc_kms_from_kms_crtc (kms_crtc); ++ ++ switch (kms_plane_type) ++ { ++ case META_KMS_PLANE_TYPE_PRIMARY: ++ assigned = meta_crtc_kms_get_assigned_primary_plane (crtc_kms); ++ break; ++ case META_KMS_PLANE_TYPE_CURSOR: ++ assigned = meta_crtc_kms_get_assigned_cursor_plane (crtc_kms); ++ break; ++ default: ++ break; ++ } ++ ++ if (assigned == kms_plane) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ + static MetaKmsPlane * + find_unassigned_plane (MetaCrtcKms *crtc_kms, + MetaKmsPlaneType kms_plane_type, +@@ -254,6 +293,7 @@ find_unassigned_plane (MetaCrtcKms *crtc_kms, + { + MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc_kms); + MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); ++ uint32_t target_crtc_id = meta_kms_crtc_get_id (kms_crtc); + GList *l; + + for (l = meta_kms_device_get_planes (kms_device); l; l = l->next) +@@ -266,6 +306,19 @@ find_unassigned_plane (MetaCrtcKms *crtc_kms, + if (!meta_kms_plane_is_usable_with (kms_plane, kms_crtc)) + continue; + ++ /* ++ * The DRM atomic API does not allow switching a plane from one CRTC ++ * to another in a single commit. Skip planes that are currently ++ * active on a different CRTC to avoid EINVAL from the kernel. ++ */ ++ if (meta_kms_plane_get_current_crtc_id (kms_plane) != 0 && ++ meta_kms_plane_get_current_crtc_id (kms_plane) != target_crtc_id) ++ continue; ++ ++ if (is_plane_active_on_other_crtc (kms_device, kms_plane, ++ kms_crtc, kms_plane_type)) ++ continue; ++ + if (is_plane_assigned (kms_plane, kms_plane_type, + crtc_assignments)) + continue; +diff --git a/src/backends/native/meta-kms-plane-private.h b/src/backends/native/meta-kms-plane-private.h +index 8d3c5f88c..c06d403c3 100644 +--- a/src/backends/native/meta-kms-plane-private.h ++++ b/src/backends/native/meta-kms-plane-private.h +@@ -87,3 +87,5 @@ uint64_t meta_kms_plane_get_prop_drm_value (MetaKmsPlane *plane, + + MetaKmsPropType meta_kms_plane_get_prop_internal_type (MetaKmsPlane *plane, + MetaKmsPlaneProp prop); ++ ++uint32_t meta_kms_plane_get_current_crtc_id (MetaKmsPlane *plane); +diff --git a/src/backends/native/meta-kms-plane.c b/src/backends/native/meta-kms-plane.c +index 69a0b12d3..63da9c4b4 100644 +--- a/src/backends/native/meta-kms-plane.c ++++ b/src/backends/native/meta-kms-plane.c +@@ -44,6 +44,7 @@ struct _MetaKmsPlane + gboolean is_fake; + + uint32_t id; ++ uint32_t current_crtc_id; + + uint32_t possible_crtcs; + +@@ -83,6 +84,12 @@ meta_kms_plane_get_plane_type (MetaKmsPlane *plane) + return plane->type; + } + ++uint32_t ++meta_kms_plane_get_current_crtc_id (MetaKmsPlane *plane) ++{ ++ return plane->current_crtc_id; ++} ++ + uint32_t + meta_kms_plane_get_prop_id (MetaKmsPlane *plane, + MetaKmsPlaneProp prop) +@@ -581,6 +588,7 @@ meta_kms_plane_new (MetaKmsPlaneType type, + plane = g_object_new (META_TYPE_KMS_PLANE, NULL); + plane->type = type; + plane->id = drm_plane->plane_id; ++ plane->current_crtc_id = drm_plane->crtc_id; + plane->possible_crtcs = drm_plane->possible_crtcs; + plane->device = meta_kms_impl_device_get_device (impl_device); diff --git a/debian/patches/series b/debian/patches/series index 2fe73f0e..186c3708 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -62,3 +62,4 @@ window-drag-Handle-pointer-emulated-touch-events.patch backends-output-Allow-checking-whether-an-output-already-.patch compositor-Handle-skipped-transition-in-visual-bell.patch Backport-mutter-merge-request-3184-for-fixing-glxgears-fp.patch +radxa/kms-plane-Avoid-assigning-plane-active-on-other-CRTC.patch From 0922313b38433d9f7e6b4561557cffbf3d147cf9 Mon Sep 17 00:00:00 2001 From: Chongbiao Chen Date: Mon, 25 May 2026 20:16:33 +0800 Subject: [PATCH 2/4] fix: add changelog entry for Radxa display fix Add changelog entry for the DRM plane switching CRTC fix on Qualcomm platforms. Signed-off-by: Chongbiao Chen --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index 18062d16..5e65a15b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +mutter (46.2-1ubuntu0.24.04.15-radxa-1) noble; urgency=medium + + * d/p/radxa/kms-plane-Avoid-assigning-plane-active-on-other-CRTC.patch: + Fix plane switching CRTC on Qualcomm platforms causing EINVAL. + + -- "Radxa Computer Co., Ltd" Mon, 25 May 2026 20:30:00 +0800 + mutter (46.2-1ubuntu0.24.04.15) noble; urgency=medium [ Simon Poirier ] From 2644dbc9f6fa6813cd1eb3ff3c7e6864f7c57507 Mon Sep 17 00:00:00 2001 From: Chongbiao Chen Date: Mon, 25 May 2026 20:16:57 +0800 Subject: [PATCH 3/4] feat: add Makefile Add Makefile with deb build targets and meson buildsystem detection. Signed-off-by: Chongbiao Chen --- Makefile | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..bcd23105 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +PROJECT ?= mutter +DEBIAN_RULES := debian/rules + +.PHONY: all +all: + +# Clean +# +.PHONY: distclean +distclean: clean + +.PHONY: clean +clean: clean-deb + +.PHONY: clean-deb +clean-deb: + rm -rf debian/.debhelper debian/${PROJECT} debian/debhelper-build-stamp debian/files debian/*.debhelper.log debian/*.postrm.debhelper debian/*.substvars + +# +# Release +# +.PHONY: deb +deb: debian pre_debuild debuild post_debuild + +.PHONY: pre_debuild +pre_debuild: + # dh default use Makefile, set meson.build explicitly + @echo "Checking if debian/rules needs --buildsystem=meson..." + @if ! grep -q -- '--buildsystem=meson' $(DEBIAN_RULES); then \ + echo "Patching debian/rules to use meson..."; \ + if grep -q 'dh $$@' $(DEBIAN_RULES); then \ + sed -i 's/dh $$@/dh $$@ --buildsystem=meson/' $(DEBIAN_RULES); \ + fi ;\ + fi + +.PHONY: debuild +debuild: + debuild --no-sign -b + +.PHONY: post_debuild +post_debuild: + +.PHONY: release +release: + gh workflow run .github/workflows/new_version.yaml --ref $(shell git branch --show-current) From 8100607120650b50fff612707a626dc541b64b76 Mon Sep 17 00:00:00 2001 From: Chongbiao Chen Date: Mon, 25 May 2026 20:17:34 +0800 Subject: [PATCH 4/4] feat: add CI/CD supports Add GitHub Actions workflows for CI/CD with direct build on ubuntu-24.04-arm runner (no container overhead). Signed-off-by: Chongbiao Chen --- .github/dependabot.yaml | 7 +++ .github/workflows/new_version.yaml | 33 +++++++++++ .github/workflows/release.yaml | 88 ++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 .github/dependabot.yaml create mode 100644 .github/workflows/new_version.yaml create mode 100644 .github/workflows/release.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 00000000..2c7d1708 --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,7 @@ +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/new_version.yaml b/.github/workflows/new_version.yaml new file mode 100644 index 00000000..c53799ce --- /dev/null +++ b/.github/workflows/new_version.yaml @@ -0,0 +1,33 @@ +name: Create release +on: + workflow_dispatch: + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-24.04-arm + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + token: ${{secrets.GIT_PUSH_TOKEN}} + - name: Create release commit + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends -y git-buildpackage + export DEBEMAIL="dev@radxa.com" + export DEBFULLNAME='"Radxa Computer Co., Ltd"' + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + - name: Test + run: | + sudo apt-get install --no-install-recommends -y devscripts + sudo apt build-dep -y . + make deb + - name: Push + run: | + git push diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..e0720fcf --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,88 @@ +name: Build & Release +on: + workflow_dispatch: + pull_request: + push: + merge_group: + +jobs: + build: + runs-on: ubuntu-24.04-arm + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build + run: | + sudo apt-get update + sudo apt-get install --no-install-recommends -y git-buildpackage devscripts + export DEBEMAIL="dev@radxa.com" + export DEBFULLNAME='"Radxa Computer Co., Ltd"' + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + sudo apt build-dep -y . + export DEB_BUILD_OPTIONS=nocheck + make deb + - name: Workaround actions/upload-artifact#176 + run: | + echo "artifacts_path=$(realpath ..)" >> $GITHUB_ENV + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ github.event.repository.name }} + path: | + ${{ env.artifacts_path }}/*.deb + release: + runs-on: ubuntu-24.04-arm + needs: build + if: github.event_name != 'pull_request' && github.event_name != 'merge_group' && github.ref_name == 'main' + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: actions/download-artifact@v5 + with: + name: ${{ github.event.repository.name }} + - name: Check if the latest version is releasable + run: | + version="$(dpkg-parsechangelog -S Version)" + echo "version=$version" >> $GITHUB_ENV + echo "changes<> $GITHUB_ENV + echo '```' >> $GITHUB_ENV + echo "$(dpkg-parsechangelog -S Changes)" >> $GITHUB_ENV + echo '```' >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + if [[ -n "$(git tag -l "$version")" ]] + then + echo "distro=UNRELEASED" >> $GITHUB_ENV + else + echo "distro=$(dpkg-parsechangelog -S Distribution)" >> $GITHUB_ENV + fi + echo "$version" > VERSION + - name: Release + if: env.distro != 'UNRELEASED' + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.version }} + body_path: README.md + token: ${{ secrets.GITHUB_TOKEN }} + target_commitish: main + draft: false + fail_on_unmatched_files: false + files: | + *.deb + pkg.conf + VERSION + - name: Append changelog + if: env.distro != 'UNRELEASED' + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ env.version }} + body: | + ## Changelog for ${{ env.version }} + ${{ env.changes }} + append_body: true + - name: Update Test repos + if: env.distro != 'UNRELEASED' + uses: radxa-repo/update-repo-action@main + with: + test-repo: true + token: ${{ secrets.RADXA_APT_TEST_REPO_TOKEN }}