From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 32F2C33C51A; Mon, 13 Apr 2026 22:32:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119572; cv=pass; b=KCNFfybpyoFD55b9YhfboRGF2kewkLgaPlk1+Lh7QoDHxigvcRlNwUCU3m5Bmwj/ZLJhyEY8A3dGEX3PKGTYGkUBaUUA1NjiKxNIeZPc3wjcNTBEJUlmTIEtO+P/wMlhI3+b/YnVdknjq/YqQKbNnVW45ZrELlZeikHVHi8N2lM= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119572; c=relaxed/simple; bh=XRxFSamf7saI7+ftNuGJR2myREQ3d+lkhhlapLuBSII=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ofYJX61u9/Fv+8MzpujLUxHuHWN4eDewNxi5EdTqF0JsuCXpyDe97HMtmt0V1mYkrrmYvVoU23XbCX1ncsIA248ph1SEPlA/ISJNdIwvFKrW9ynqj+QjRgXt0PasRQyNcGn5rrx9PY7Dqr3iIng35zhPxD7Q5Q8kL1ATAPhjH0I= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=lUqe5A4d; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="lUqe5A4d" ARC-Seal: i=1; a=rsa-sha256; t=1776119513; cv=none; d=zohomail.com; s=zohoarc; b=LBaXMTKisio+ZuuYLlHNInE9dZEh0YasJ1JrvrxKqnXwEiuGS114ofQn8fYSMPUBnnyQAM5jpaCi7ZfWcKY5Sp6yDd+LlNgkFK+wo7Zt/E6APrf1nxW4oYXuHeG2ng0EPW30ICSUKOer15PFFUDC57U6A4wNJB/r2wgL+cNfLHM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119513; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=JMJVfFlSOzp9g3cU5PEtd6C/2EHSjeZanGaJWg2I270=; b=e0tsH3mwP8+Ph/nK1/yw4f+vr9F8sQG3ogfJjoGi6hemgKRA8AkiXnYNmwlWgeFNgrGwO9jqONawgaIhbZZkvSoavdOpld28iteeW8mdsKx4ub/wnf5BfVDzTevPFc0TRJBcg4nx2jAViUJBDLbDbNiuKki2Mfso9yawNwbjDQg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119513; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=JMJVfFlSOzp9g3cU5PEtd6C/2EHSjeZanGaJWg2I270=; b=lUqe5A4dz3TMilzfLmZRH5DODlXuaHbE/UIb39C8YxZpNlrUXf3XqTnZstdZiCKa qvZB8Q8y2szmeecuU8f0nA0TgX9fsEVyPbCwN+YKGd9YgKSGSpiTCrhA+bi3XkpqTUi PaUKs5phJwnvoHZTzPX/+qp2s3gOhu+iUgExxkPE= Received: by mx.zohomail.com with SMTPS id 1776119512472811.382134910694; Mon, 13 Apr 2026 15:31:52 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:36 -0700 Subject: [PATCH v3 01/13] drm/tyr: remove unused device from platform data Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-1-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1637; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=XRxFSamf7saI7+ftNuGJR2myREQ3d+lkhhlapLuBSII=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl38666/1rf3tKwcJVrv4+rfnVd9vt5of6HjPcecqjOa OGxVWvrKGVhEONikBVTZDlrb9QjXvXeSHf+/2aYOaxMIEMYuDgFYCI+Wxn+8Eh4vXvGuys19Vgo 24/THG3X+Rjy+ZW9ahN513O8dhdoY/jv8iB+H9crn78NBQw7+59eO/+apdHS89kWhQVfVHWP7hR mAAA= X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB TyrPlatformDriverData stores an ARef to the DRM device to keep it alive, but after switching to Registration::new_foreign_owned(), the registration owns the device, so the extra reference is no longer needed. Remove the device field and return an empty TyrPlatformDriverData instead. Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/driver.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 0a1cfbad1e6c..7a27207e52f7 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -52,9 +52,7 @@ pub(crate) type TyrDrmDevice =3D drm::Device; =20 #[pin_data(PinnedDrop)] -pub(crate) struct TyrPlatformDriverData { - _device: ARef, -} +pub(crate) struct TyrPlatformDriverData; =20 #[pin_data(PinnedDrop)] pub(crate) struct TyrDrmDeviceData { @@ -157,15 +155,12 @@ fn probe( gpu_info, }); =20 - let ddev =3D Registration::new_foreign_owned(uninit_ddev, pdev.as_= ref(), data, 0)?; - let driver =3D TyrPlatformDriverData { - _device: ddev.into(), - }; + Registration::new_foreign_owned(uninit_ddev, pdev.as_ref(), data, = 0)?; =20 // We need this to be dev_info!() because dev_dbg!() does not work= at // all in Rust for now, and we need to see whether probe succeeded. dev_info!(pdev, "Tyr initialized correctly.\n"); - Ok(driver) + Ok(TyrPlatformDriverData) } } =20 --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 47A0639B97C; Mon, 13 Apr 2026 22:33:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119584; cv=pass; b=czHXCoqacXQe3zZp1AICHJ3U6U9R5GoNacn7MfLT9SDEPUn6VgOSolkqJi05mOKCRDb7PNGJ/S8swg6/Wz3SCSjAspf5XJFOq7QhoU3gHFJZv6h3iGYnGyuXPSKkMwwWnglLBEYuMg+PeFawe0bHuFYx2el6SqK+OUBJPfVD1DM= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119584; c=relaxed/simple; bh=ivbl2vLKaVvN2BrJiNwbEU0fWVyjo9M4UlpAyaejolc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NVnLohuaG55mJdvbKAKaHQGl/h7Eew5VqyLGz4rzqZwCbk3dyhNvlnkW9RiASgfNxodUPKaAEdFcZ99drmQ98O+rHmE/g4W3rs8opIa56WxjeCYBZ23NFUGEJNUN34rY0BbHzhzWHGkFHlMCJrPKFQVjDshyPb6hd9hlMZHqt2A= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=OAcoUvCG; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="OAcoUvCG" ARC-Seal: i=1; a=rsa-sha256; t=1776119515; cv=none; d=zohomail.com; s=zohoarc; b=XQow1hV0/sZOPxgHYm3eB+bdMJr0gGse0FhmhRi0aAxuL0+jn5cFH/ueVq4dbCbDR+CRWerRMkcBp+36yCbkWa+87KC2ZTlyTVdFunQNS2WC9bk/1Uo8Qs1HN2ulJ7QoZ9twiZPA51tFHXeoa4iqkqCLFttxYtLk+c+dBAC+bIc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119515; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=VXDFGS98Tc4lDYbE888SjtHArlQzwAEr30/idT3TFd4=; b=ky6t3lAPVldAvzf/PVd5/K6SoveoK7/xPhmFrgAI2b4T5fl27JBkGrPZQihgX4cwTGw+Vd0Z7CZ216ORS/jtMMm/K2e+pqpCybPzuDWCaMrAupCoI0LvPbKTQMbxnpmww84BOQRauXAAo5dh+vCi8sJG1Hjf0SxphzLvqRpu5v0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119515; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=VXDFGS98Tc4lDYbE888SjtHArlQzwAEr30/idT3TFd4=; b=OAcoUvCGww4T66Ae+ZlIG+dndoxHrWO1V7luhYGyUy/4UowWvQ3/M9CZrqB640at qA6gwIaATab1saIYb8ryeU6BNKIGu1B+vJLRBjplj7SxGXDw1mCuHTMzStYLyohHbcb RsMl7U9OxX98moV4cAtsxvX32FCJYzLnAcUybZ3E= Received: by mx.zohomail.com with SMTPS id 177611951377042.46367496896971; Mon, 13 Apr 2026 15:31:53 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:37 -0700 Subject: [PATCH v3 02/13] drm/tyr: select required dependencies in Kconfig Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-2-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1840; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=kedRnGAjTEAxNymKdPnucngrkvI5gPskFo8gwuQEDjk=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3867Wc/v22SuuMv55anNrbWLlbZn4Mw83PNUUcerIv LUro+ZlRykLgxgXg6yYIstZe6Me8ar3Rrrz/zfDzGFlAhnCwMUpABOZtpmRYZVNqELI/7iM/1ma HdKfL3AvqE+cs6+J/3ho+J823ac7shn+Z7b9UVo3w2hzx17+suYuaaed7NpHtsnkLnNYo3lnxe+ 5zAA= X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB From: Boris Brezillon Tyr depends on DRM_GPUVM, RUST_DRM_GEM_SHMEM_HELPER, MMU, IOMMU_SUPPORT, and IOMMU_IO_PGTABLE_LPAE. Select or depend on these symbols in Kconfig so the required infrastructure is enabled when Tyr is built. Introduce DRM_TYR_STATIC_DEPS to keep the built-in DRM dependencies selected even when Tyr is built as a module. Signed-off-by: Boris Brezillon Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/Kconfig | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tyr/Kconfig b/drivers/gpu/drm/tyr/Kconfig index e933e6478027..443ce988b570 100644 --- a/drivers/gpu/drm/tyr/Kconfig +++ b/drivers/gpu/drm/tyr/Kconfig @@ -1,5 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 or MIT =20 +config DRM_TYR_STATIC_DEPS + bool + select DRM_GPUVM + help + Ensure required DRM infrastructure is built-in when enabling Tyr + even if Tyr is =3Dm + config DRM_TYR tristate "Tyr (Rust DRM support for ARM Mali CSF-based GPUs)" depends on DRM=3Dy @@ -7,6 +14,11 @@ config DRM_TYR depends on ARM || ARM64 || COMPILE_TEST depends on !GENERIC_ATOMIC64 # for IOMMU_IO_PGTABLE_LPAE depends on COMMON_CLK + depends on MMU + select DRM_TYR_STATIC_DEPS + select IOMMU_IO_PGTABLE_LPAE + select RUST_DRM_GEM_SHMEM_HELPER + depends on IOMMU_SUPPORT default n help Rust DRM driver for ARM Mali CSF-based GPUs. @@ -16,5 +28,5 @@ config DRM_TYR Note that the Mali-G68 and Mali-G78, while Valhall architecture, will be supported with the panfrost driver as they are not CSF GPUs. =20 - if M is selected, the module will be called tyr. This driver is work + If M is selected, the module will be called tyr. This driver is work in progress and may not be functional. --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 52F4731715F; Mon, 13 Apr 2026 22:32:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119552; cv=pass; b=ubuyRN2heACWPHa2feMK9pE7Rwk81ujwqMiGIuv7NMJ7gIgxRzGuWZw7LyXKMPpulEwia4O1cKxSogzwJf22S4bBFEpGLtcBTyFnwNOEC21BBihvsqJHQp8D4c8zaBFnKd59c9x5F6ObSNxN3ioQXPOKWmhdpptIo27XWPQxueQ= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119552; c=relaxed/simple; bh=TJYiaR6HkYI0Jih2JmM5qi5Sd5bcOQQAYqWwO1EUFlU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=X2cMq2T2mcXumDwWYbNtIl88JWdPOqjuhbtNdntFnuqLZ3mJn0ONizAmYhcJeewyhA6Ell48jrQa0AjrUAxNHlBbcAZDaivJULtZT8LaELk38Zo+qPtBwX40379nbJEKYthgJY+D3B0Tn4uHZohY7SAUtyz+slVmfNuV2Soxicc= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=DN3pCSkf; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="DN3pCSkf" ARC-Seal: i=1; a=rsa-sha256; t=1776119517; cv=none; d=zohomail.com; s=zohoarc; b=VxVTjTfY030H4mEJBYWp/BxzRxH8BEt2Pa/BGXTzLKBWe+L+3el2f8+7w7tuNEngO2MeOL15VDkp46h5N23pt0OjNk44M52zC4N559iFVa8qppp/o8n4nsh2CNUwxWWY/YIQjNX9cec7kyp3c5MmYSDrdZzxz38lSPrP9e/J3V0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119517; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=mJKP1SVgsbLnTI6G/aFIyYqGXRmudzvv7JSn+nXBGDU=; b=DRYeeel6zuBkVukjOlcbTUu6/DOlF0X1K5aGZxWB3ibP1qtPfUZY600+HA3OLeuLNUB+FRzjbK5gJTSwQZswnvE/QVpvZfW+AVjvcVBACNjlwHNCdHvcV2m2wCd8dcPahlUlRtYzUwl2tben0rBpp8+YWkOja3sAaoX4RysIl4o= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119517; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=mJKP1SVgsbLnTI6G/aFIyYqGXRmudzvv7JSn+nXBGDU=; b=DN3pCSkfi1+etWj+1LQHJK6DiOzjdPkjohNgtbMkBMePpJfjGgKr/cFxUg/ooo+o V5WvM2hD7GnhPDainnFE2veszb3Pik7F3tdL7CaxrqDWeuNkSFrzWjC6lmlzXnlXTbu hxebh3sbhdyALWKg7o2l2oU6g+brzgcMvCDqkk8A= Received: by mx.zohomail.com with SMTPS id 1776119515072731.0963813374332; Mon, 13 Apr 2026 15:31:55 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:38 -0700 Subject: [PATCH v3 03/13] drm/tyr: move clock cleanup into Clocks Drop impl Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-3-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2601; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=TJYiaR6HkYI0Jih2JmM5qi5Sd5bcOQQAYqWwO1EUFlU=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3867q1v4zrdhhfmPNW+25zbL3Cpe2dhnptCfNfdGk+ WqJ9+aTHaUsDGJcDLJiiixn7Y16xKveG+nO/98MM4eVCWQIAxenAEzk+V2Gf+ZzLrSLx/MZGf+Y 2tDjl7+m53vit8T46VaGUpP6uU32FzL8L4j+b/nksulXw+TI+TIcJ4Lj08LsAvY8nXDja9xVba8 7TAA= X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB Currently Tyr disables its clocks from TyrDrmDeviceData::drop(), which causes them to be shut down before any other fields in TyrDrmDeviceData are dropped. This prevents us from using the clocks when dropping the other fields in TyrDrmDeviceData. In order to better control when the clocks are dropped, move this cleanup logic into a Drop implementation on the Clocks struct itself. Since it serves no further purpose, remove the PinnedDrop implementation for TyrDrmDeviceData. Also, while here, remove the #[pin_data] annotation from both the struct Clocks and struct Regulators since neither of these structs need this macro to create structurally pinned fields. Reviewed-by: Boris Brezillon Reviewed-by: Daniel Almeida Reviewed-by: Alice Ryhl Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/driver.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 7a27207e52f7..3057f3af10e3 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -54,7 +54,7 @@ #[pin_data(PinnedDrop)] pub(crate) struct TyrPlatformDriverData; =20 -#[pin_data(PinnedDrop)] +#[pin_data] pub(crate) struct TyrDrmDeviceData { pub(crate) pdev: ARef, =20 @@ -169,17 +169,6 @@ impl PinnedDrop for TyrPlatformDriverData { fn drop(self: Pin<&mut Self>) {} } =20 -#[pinned_drop] -impl PinnedDrop for TyrDrmDeviceData { - fn drop(self: Pin<&mut Self>) { - // TODO: the type-state pattern for Clks will fix this. - let clks =3D self.clks.lock(); - clks.core.disable_unprepare(); - clks.stacks.disable_unprepare(); - clks.coregroup.disable_unprepare(); - } -} - // We need to retain the name "panthor" to achieve drop-in compatibility w= ith // the C driver in the userspace stack. const INFO: drm::DriverInfo =3D drm::DriverInfo { @@ -203,14 +192,20 @@ impl drm::Driver for TyrDrmDriver { } } =20 -#[pin_data] struct Clocks { core: Clk, stacks: OptionalClk, coregroup: OptionalClk, } =20 -#[pin_data] +impl Drop for Clocks { + fn drop(&mut self) { + self.core.disable_unprepare(); + self.stacks.disable_unprepare(); + self.coregroup.disable_unprepare(); + } +} + struct Regulators { _mali: Regulator, _sram: Regulator, --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0D39C39F183; Mon, 13 Apr 2026 22:32:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119575; cv=pass; b=S+97M8r+4h109DFKeQptp+qO8Lh3B9K8/7TcoHCj9cd678V67CQrhOlE5zUaLEsy63TpVebNU9voKTcQN3r1cp/MCOqlrTahxrYH3FJg9v/DMPyO0yif9vwcdwq6X4GtqNy7Bxrj98+Bo/0D4ISoKYkHJM4Zn43lEk61kBoeyvM= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119575; c=relaxed/simple; bh=md2Qi2xc0FzmOvjnVgq0Mpmfcrkb6Aj4PyfqtguN0fc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=aWZSX3I6JybOAjTx0KoXOmuXjJCPkwWo0P4i3/7NeU/hkvGfwLxg75W1LFeF7iskbeWzJngL7BbScL5oszOCMaNRpwRNZ7Eu4UishX2qr40BI2e/HDbXZjF6ninTleUdna6OK/YMvs/7+1N88t8Oec7guhmSu/W23okSGvZrM0M= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=Tb5lyLiV; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="Tb5lyLiV" ARC-Seal: i=1; a=rsa-sha256; t=1776119517; cv=none; d=zohomail.com; s=zohoarc; b=mfBz4HkNY01gg8a1ZYVJ3cCEHjGs2W/mVLa+7Axr44gztTdpuaLfNEyzO9JvoWQREfIxDTK+Vu8qVqRg+Ud3eI8JVbboVTEbFr235q/9hrSzm6TOfgibx/FC7TZ5n30UA04d2BBLSLnSbFQwXLyJi13i7DWwzRqr8/evON8j7+A= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119517; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=hH4p6y7TKcN2UmMFYjcoILpCMwlTTx9qQRAByxuVZ1s=; b=l/Ir7kbltoX9vpxEp7a4WRlPmsUrDsOSG5DJRhq/Xvi+HLdqlPeoQoeBCeU9JUhbr54xyTiGb5hP2EdDBts6JMm2MUeJWCjDnQpZeX+Z9HZOu2LFddJQtX7j64QPmhyDYwFKFSmSkCROSrd57YIF3DeGlyf29bzMf0vYM+EY7jE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119517; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=hH4p6y7TKcN2UmMFYjcoILpCMwlTTx9qQRAByxuVZ1s=; b=Tb5lyLiV+KML8tURrITt25s+btkZnCfeqxs69rODL4gZRRn8eG72FDIKtZGGMbni WE6LBQV6eQafHm7O38QiAJqUItupQ8dBqKqS2ORiiyIX3SU/tgO8ARb6m/2D1xg5WRR 1TyQ3fAT/SZK2pg2i1E4BaM7mDZiXxyXc+F+Ugnw= Received: by mx.zohomail.com with SMTPS id 1776119516355173.13981149757194; Mon, 13 Apr 2026 15:31:56 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:39 -0700 Subject: [PATCH v3 04/13] drm/tyr: rename TyrObject to BoData Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-4-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=2106; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=O2Pl2BjC/KB8EdcByAb7BBRMRMxruk1BxeLbq8Qs9jU=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3864+y6q80bN3wybV1sSap9dP2vTUzQ7qWbKFR3ny9 LuRW0ViO0pZGMS4GGTFFFnO2hv1iFe9N9Kd/78ZZg4rE8gQBi5OAZjIzkpGhr2btX+yXdmQsLH2 1N1tm95+37tsLbfsy9Osjx/POn/ebcFbRobNKql1S+/eOPXyVv72x50NLzMdd8wr+ftz4iaeFWz PJ5xiAQA= X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB From: Boris Brezillon Currently the GEM inner driver data object is called `TyrObject` which is a fairly generic name. To make the code easier to understand, rename `TyrObject` to `BoData` so that the name better reflects its role. No functional change is intended. Reviewed-by: Daniel Almeida Reviewed-by: Alice Ryhl Signed-off-by: Boris Brezillon Co-developed-by: Deborah Brouwer Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/driver.rs | 4 ++-- drivers/gpu/drm/tyr/gem.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 3057f3af10e3..f2e7157221f1 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -38,7 +38,7 @@ =20 use crate::{ file::TyrDrmFileData, - gem::TyrObject, + gem::BoData, gpu, gpu::GpuInfo, regs::gpu_control::*, // @@ -183,7 +183,7 @@ fn drop(self: Pin<&mut Self>) {} impl drm::Driver for TyrDrmDriver { type Data =3D TyrDrmDeviceData; type File =3D TyrDrmFileData; - type Object =3D drm::gem::Object; + type Object =3D drm::gem::Object; =20 const INFO: drm::DriverInfo =3D INFO; =20 diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs index fa8d663fb523..11951b507b18 100644 --- a/drivers/gpu/drm/tyr/gem.rs +++ b/drivers/gpu/drm/tyr/gem.rs @@ -15,9 +15,9 @@ =20 /// GEM Object inner driver data #[pin_data] -pub(crate) struct TyrObject {} +pub(crate) struct BoData {} =20 -impl gem::DriverObject for TyrObject { +impl gem::DriverObject for BoData { type Driver =3D TyrDrmDriver; type Args =3D (); =20 @@ -26,6 +26,6 @@ fn new( _size: usize, _args: Self::Args, ) -> impl PinInit { - try_pin_init!(TyrObject {}) + try_pin_init!(BoData {}) } } --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 86C333A6B99; Mon, 13 Apr 2026 22:32:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119576; cv=pass; b=dPJ4yb8GsfypZxms+C0C2xl6Oyt/+9kOj6M6imNAlcSzSeQDRcGxCyQVPAAwHNt1W4D9XQh2Azs+GCLT10Yx6RUipMlaOn5lSRMhoMJJsVTAMrBkz92M6zMIMX66BmluwLw4hrlQ/LR9m+1DaDpXd7HEJjFagBr5EFruybJ2mE4= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119576; c=relaxed/simple; bh=n3WZMErEy6oVIqhhH/21DmsPSrqalZYcPgPTfJaLSp8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=U5KcYDWopcB3vL7/MErPCXK91PbnLLQMSEk5lmBKqIOrplKEe8W8Vu2jF9HEMPpjRl4Uz1NT5yFAN7O9h7Anusu+fSikts5JwXb5zPpP105bmCj34PeIPcEcgLpyGpBANc8piw6wAaKNd4oQsXyMYWZqqId2zlOj3GRxzGjrQOc= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=idJCH8HX; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="idJCH8HX" ARC-Seal: i=1; a=rsa-sha256; t=1776119519; cv=none; d=zohomail.com; s=zohoarc; b=DJVOkSYrwD8bg66B8qB51uy5RX9faO4rNwwhgZEmNy4pPzdrDB+JpZXxjJ5R2B8QG7SXHPULb6RzquSe2iUpOMYcNCd9qvyTK9xKnwgh2CpSN1Tv27FGNZD8ECXsbZ3sGaqRmYBWO82LY+R0v/uOWfBflykDRMzvB3Y3rlSFslM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119519; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=3dTFROfvAzEnMmmF6K1xMdBFrCP2mhHx9BiBGFP1Ct0=; b=QnMeSm2MJqbVFn+uZN57v9un6C3OE7mSGmj2A/LuPOveNpYd7gIW8UVVBRIZBvC9TR/H8ZWQAEismCNp0FTy45dnl2+uge8u2A1atH9Qj9fE1CUkCu+d89odylHeQyb1EOemCxy6TCgadKUpHemCXdvl/MklTaQ7/nzntghYBbc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119519; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=3dTFROfvAzEnMmmF6K1xMdBFrCP2mhHx9BiBGFP1Ct0=; b=idJCH8HXEserd4T+O7/x2w+wQdVnJ/XkkgYln55QQKjIa2hQidXjkxEgz/0py/PL 4s0lddFukMGcnD3StnsoVipc2aORJWUR/jLtZSOcG5wyAiqoMTIcWZ46jHtDjflYcF3 zRH5OrdxKjoDpJvuGxB9f/tFashnOvo5CaokS22k= Received: by mx.zohomail.com with SMTPS id 1776119517638801.3669547765522; Mon, 13 Apr 2026 15:31:57 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:40 -0700 Subject: [PATCH v3 05/13] drm/tyr: use shmem GEM object type in TyrDrmDriver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-5-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1127; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=4Ba4ZrUKq66SWVeeJW+wemfXBYB6qyOZr6r0bmtZhd0=; b=kA0DAAoWeagp5wxefesByyZiAGndbtXIUAisblEBsZx8lVIgSEYpYw1l8J1PgsG6R3kfTaLpd Ih1BAAWCgAdFiEEzT8yjBd67zItn/+Deagp5wxefesFAmndbtUACgkQeagp5wxefetAQwEAjeve vo4JExKtAFWE93Pl8HiBwpmGU34gRbHcgKnoSa4A/3tIK5Yhas1Da85yA7oyFYO7jFhVlWLWeEZ B/BS8HQQE X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB From: Alvin Sun Tyr buffer objects are shmem-backed, so the driver should use drm::gem::shmem::Object as its GEM object type instead of the base drm::gem::Object type. Switching to the shmem GEM object type matches how Tyr allocates and manages its buffer objects, and uses the shmem-specific GEM abstraction provided by the DRM Rust bindings. Signed-off-by: Alvin Sun Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index f2e7157221f1..26a20e0ab91c 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -183,7 +183,7 @@ fn drop(self: Pin<&mut Self>) {} impl drm::Driver for TyrDrmDriver { type Data =3D TyrDrmDeviceData; type File =3D TyrDrmFileData; - type Object =3D drm::gem::Object; + type Object =3D drm::gem::shmem::Object; =20 const INFO: drm::DriverInfo =3D INFO; =20 --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A0CD039D6E5; Mon, 13 Apr 2026 22:32:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119565; cv=pass; b=H6Oz8P34YQBAAGjx/YViTlXBiju8AuadKQUsRuSE30CJ5ThKCwNwtcZPlvWDDc9hadyKO9SGvv3vpxOLQr5Y5z1HZ6F2MEtGB6/fHh3ZUmZHzK3qqn63ZWRCoTGndze6k5cfeq6downdsPL+EbYcSQrBz330v/KB9gDc5baNTrY= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119565; c=relaxed/simple; bh=j2+XN5IcQADZ6IsuFpiGP4oQAT8A7zDww6i6AoAwudU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=II3BJLp+81jjWhcykjtzhmS0JktMuh0wIOFYOhcqge6Tud5d9q3IIbQIEzuRR4J7ACbw6OTgfxURBjalWrrC2H59cSnlR5Q6ws2vYzF4mIuuXWt3YpIX/JMWy+uNs82oPcAb324PO+4Tj0lpV63ubZqMl5jNywBxPwZpWeeqZSQ= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=eleT72km; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="eleT72km" ARC-Seal: i=1; a=rsa-sha256; t=1776119519; cv=none; d=zohomail.com; s=zohoarc; b=WL66qqr/YoqH65wlhh9RThOBuQk8rAxzzgwhY1+RtTzSZQXHlDr3s+yXur13mSwmmsgzYuKXQgEmQX61qvWtSUYdWuCipioRMaA0aY5wx4m8aBFQqRD7esV7KAmryR4itvxOCKR6pgEBuulrQ8DgW+yOyxf2uVQeuUHgeVBTzkc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119519; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=Td95XBU/QVVNPvpNOyHFW9D3+lVJZxs6G6aunCPb574=; b=YmA9IWvNHFLzWS2qvGTNjoix7z2C4gIqqT943u7WWUP6dNnNYiW+rJPtldLbvJzehZxucHWsQYylA9w29MDLNJXoYwJ/lggqdjOOKB8w/4RyE9AMTS+rGNlClRHd/P6NqGLof51FnVKtAcBRlQvx2WlOb3PdXHG4E8sqXEAC7EU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119519; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=Td95XBU/QVVNPvpNOyHFW9D3+lVJZxs6G6aunCPb574=; b=eleT72kmB1bxdgQYlfNhl1rZ7ANIagwWPnKbE1pawWu1Nj9X8jbCnkbiIpsqpx+k mi6W7SUvgTnWmpkO9bpxiKpYIIgCGC83QyqbfkBuZezTyOxF0oYzinWZ6f1xxo4IFIp 8hYP1DNPUACBjXiOMR9KR/SoYrpXo4u29mseA6vI= Received: by mx.zohomail.com with SMTPS id 1776119518776629.4288698832687; Mon, 13 Apr 2026 15:31:58 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:41 -0700 Subject: [PATCH v3 06/13] drm/tyr: set DMA mask using GPU physical address Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-6-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1727; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=K3zABDKgOMYL6fCZDCWZYPbstPK4Q3KaSxiOaydBw08=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3867GVWT3zKjjl3/Du6rA77rqrsq0wkW2MiyJLMarg 9v4Mgs6SlkYxLgYZMUUWc7aG/WIV7030p3/vxlmDisTyBAGLk4BmMiv6YwM7xl6UhL5DHptdC+V G8u+L9ggudj3zs1/Mn/PsZ3YvabuBSPDBJaSVWHnvDreKSxV0Vc7LcLHufeAdOaqhb7Tp/Y8/by UFQA= X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB From: Beata Michalska Configure the device DMA mask during probe using the GPU's physical address capability reported in GpuInfo. This ensures DMA allocations use an appropriate address mask. Reviewed-by: Boris Brezillon Reviewed-by: Daniel Almeida Reviewed-by: Alice Ryhl Signed-off-by: Beata Michalska Co-developed-by: Deborah Brouwer Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/driver.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 26a20e0ab91c..1f09f59e271a 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -11,6 +11,10 @@ Device, // }, devres::Devres, + dma::{ + Device as DmaDevice, + DmaMask, // + }, drm, drm::{ driver::Registration, @@ -138,6 +142,14 @@ fn probe( let gpu_info =3D GpuInfo::new(pdev.as_ref(), &iomem)?; gpu_info.log(pdev.as_ref()); =20 + let pa_bits =3D MMU_FEATURES::from_raw(gpu_info.mmu_features) + .pa_bits() + .get(); + // SAFETY: No concurrent DMA allocations or mappings can be made b= ecause + // the device is still being probed and therefore isn't being used= by + // other threads of execution. + unsafe { pdev.dma_set_mask_and_coherent(DmaMask::try_new(pa_bits)?= )? }; + let uninit_ddev =3D UnregisteredDevice::::new(pdev.a= s_ref())?; let platform: ARef =3D pdev.into(); =20 --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA58539B942; Mon, 13 Apr 2026 22:32:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119554; cv=pass; b=pMGntjFRPVZA8iCPUqkIXadCyHqj9whxRKESqiweTm5QqPTS21szwzwSQliUa8aX4IeUO+onGkC9P/0o+xfyuMHTDMaOYVoTD8trkk2Mz2oDvaWReWdEMLayV8Hv138fk9JOaDLKNh/nrk9+HGblhmDyNUTTQeC9W1iakVTeSJs= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119554; c=relaxed/simple; bh=AaPtiNttGuB/t7PYj8M+d94kLl/74wFvY9trrLQs8C0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=L30LEabQYdhJe4cBHbpSZIOAzRQL4WKXNY6pxkkQq2E20EuzJPHzp7Vw+0RKBLlSrxCuTyekr6AAA+vJjV6fk+4QDZl4U7a1Ed6DZJ76QeXu1rzsEigOLGxVEn0hGE2FDSNUWYKOezgyogwIpKl2daYJASLBuverRtGZ4wCdmfY= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=QH31m1kc; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="QH31m1kc" ARC-Seal: i=1; a=rsa-sha256; t=1776119521; cv=none; d=zohomail.com; s=zohoarc; b=UY95dKSUKyBveMffV9DzxQWkpwiUhrf7HdbX1UH4XL1xYDK8drXIPJgCwG+VqHf7e1/fHEnfXHaxJy8gP/Z+jUie0bCDKMfutsQUPntb9tUkzoOrQig2EEDSUrkoWLDA1okVZXc/LXCagosvj/13DcVuC+e2y7shO9TZ7fz2ZLA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119521; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=9x0SAL3Soz6DOFtVmAmkbii1FCN1sclBARu8rFXjdPI=; b=VqL8PnO9q+kSkS8qmgJT2CgdbgSLBElVK+zjJbTV4ATS1zrlyvoVLnFaHeJfOwCXUITyoiHqOpAbLYJhPypHGJOX4RycPyBL1Bcr1I+5qZp3yNuffe3nsTsQdC+iWD6XFp45XuenQB6bem+t4NjOmWZcOt7Tid81t0XfBC06IF8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119521; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=9x0SAL3Soz6DOFtVmAmkbii1FCN1sclBARu8rFXjdPI=; b=QH31m1kcnSt3n0rqQEABRoI3Em/giVHhoe+SG0nwbhq1NBpYt4sQz6sP/5uLI84q SNEuoaQUd1cWPLd1JL7hE4fc48mMvMSjrKhkLL+XKWWpoin0OpYmH0KaeYQ3HthXlMI wejPsE5R+sTV7vSL0rPVnZa9dKYY/Yy03T8CMpGA= Received: by mx.zohomail.com with SMTPS id 1776119519931477.25225554263204; Mon, 13 Apr 2026 15:31:59 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:42 -0700 Subject: [PATCH v3 07/13] drm/tyr: add shmem backing for GEM objects Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-7-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=1796; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=AaPtiNttGuB/t7PYj8M+d94kLl/74wFvY9trrLQs8C0=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3866W7JsuFNPJcvBu4fmvh1blH/P+lsRvNKdh+8Lla z3aQpdv7yhlYRDjYpAVU2Q5a2/UI1713kh3/v9mmDmsTCBDGLg4BWAizY6MDNsWLawzv33w6SY+ xQLNJ3d4OQunbVA9mLKBRSPk65qc07cZ/qcEcD/tmvW0VH2ahNW/xvCoCJ2DR+dt0C2++2hK2Hp tFUYA X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB Add support for GEM buffer objects backed by shared memory. This introduces the BoCreateArgs structure for passing creation parameters including flags, and adds a flags field to BoData. Co-developed-by: Boris Brezillon Signed-off-by: Boris Brezillon Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/gem.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs index 11951b507b18..c6d4d6f9bae3 100644 --- a/drivers/gpu/drm/tyr/gem.rs +++ b/drivers/gpu/drm/tyr/gem.rs @@ -1,4 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 or MIT +//! GEM buffer object management for the Tyr driver. +//! +//! This module provides buffer object (BO) management functionality using +//! DRM's GEM subsystem with shmem backing. =20 use kernel::{ drm::{ @@ -13,19 +17,27 @@ TyrDrmDriver, // }; =20 -/// GEM Object inner driver data +/// Tyr's DriverObject type for GEM objects. #[pin_data] -pub(crate) struct BoData {} +pub(crate) struct BoData { + flags: u32, +} + +/// Provides a way to pass arguments when creating BoData +/// as required by the gem::DriverObject trait. +pub(crate) struct BoCreateArgs { + flags: u32, +} =20 impl gem::DriverObject for BoData { type Driver =3D TyrDrmDriver; - type Args =3D (); + type Args =3D BoCreateArgs; =20 fn new( _dev: &TyrDrmDevice, _size: usize, - _args: Self::Args, + args: BoCreateArgs, ) -> impl PinInit { - try_pin_init!(BoData {}) + try_pin_init!(Self { flags: args.flags }) } } --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B929439BFF6; Mon, 13 Apr 2026 22:32:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119571; cv=pass; b=ojlSG55T8KpMHGtt49Ikf1yITKaf1Pv9gA4vgB/StaqvcRmIVXCL3Z3wMkLQqM9X/DaKNzABxHC98A/ozWdeQKXUXYeSni85lAAH0G5v57nfdrPpPPai1Yc1VGfie9faVwmZKhQp+QOISgsDvL/B8nFPIUJ/R72vCsD7qwftMJ8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119571; c=relaxed/simple; bh=lMKecDyyhF41gtxZWj9H9Eqp8sk5hDcPT2xNSPwpXaY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=iV7Dp5Fi7IyAwKg+ZACWmo8pxurMZXhYoEOlUTHFvWB/vL8kzF0kWsRoWK2e5zCW09NYDlyYpjmNfpX16Me05CullIQXxNWUHbTYXeyXl7BvHfFmbBjXpX8QCspcAvXS6lGRaDVmnjh3yXgeG+RknDnPQMvpZVvN9VZunAzxEys= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=ExaVXdsA; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="ExaVXdsA" ARC-Seal: i=1; a=rsa-sha256; t=1776119523; cv=none; d=zohomail.com; s=zohoarc; b=mEkOGeAFEduyIov2SfdnPvs+l4G5DK6aI4RqhzJcgXC1mguGZU+GwsSPLEqkmX/03hGjaAUblza0+N0IWzNgFLHP4Ua6wYF+gqfnrzlioHtciqENtq+lfIw9E5MBRZmVyKOWZMv6YqaswtfqVxjYN3p5twaQ+aFZMpv/a2c7oSw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119523; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=NVSzguzV5E8I0aAecr1bh6aE0xgm/faWDGEzrzojJ54=; b=MLALrcFuoIwxSeRAvqeSE/3XlZhdwBfMHMJrnpVrIQ7Q0HTGn+Fw7infmOTOBmzZ4BRCeILRRRJKJkXdmqiAohI2G+IIy4O95yKguxsLmTLqAH6PB1GZ2C3zutpVgT039Iu845HuQSSJlEtHie91W3FOa9E05G6pEOAFrO9Giho= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119523; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=NVSzguzV5E8I0aAecr1bh6aE0xgm/faWDGEzrzojJ54=; b=ExaVXdsAh0KXcQ82aOp0I5CHsdWqAa8c23nZgFno1ZU7fb1isLKbxF1twZIszwfA uxYhFMS7mUzWzU0VKdAvgZ0ywv9z3WAej6Cm07oT/DZJ/qdkFaej3rXCs35Q8wfH7S9 LoFGhgkwk4a033mpaM6kammIVLZmV1OXfGqMEMjk= Received: by mx.zohomail.com with SMTPS id 177611952111959.308820664188374; Mon, 13 Apr 2026 15:32:01 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:43 -0700 Subject: [PATCH v3 08/13] drm/tyr: Add generic slot manager Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-8-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=18083; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=2lsx5cWpz1kepWGxKnmDJilNXZeOAHnAUs6sa7ze99k=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3866um/nNvqVdQjH17U27kP/Lr7/9uOAIU96PHu358 47O2fBZqqOUhUGMi0FWTJHlrL1Rj3jVeyPd+f+bYeawMoEMYeDiFICJ9Jsx/M+I3TnzzMxPa7g6 ghPq78lz3FW/+yUuuStl2VmRezkzhGoZ/qnWNKga3t3+brFChatirZX63k1yDy6ln5Y7k3PfdN3 pFZwA X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB From: Boris Brezillon Introduce a generic slot manager to dynamically allocate limited hardware slots to software "seats". It can be used for both address space (AS) and command stream group (CSG) slots. The slot manager initially assigns seats to its free slots. It then continues to reuse the same slot for a seat, as long as another seat did not start to use the slot in the interim. When contention arises because all of the slots are allocated, the slot manager will lazily evict and reuse slots that have become idle (if any). The seat state is protected using the LockedBy pattern with the same lock that guards the SlotManager. This ensures the seat state stays consistent across slot operations. Hardware specific behaviour can be customized using the slot manager's `SlotOperations` trait. Signed-off-by: Boris Brezillon Co-developed-by: Deborah Brouwer Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/slot.rs | 437 ++++++++++++++++++++++++++++++++++++++++= ++++ drivers/gpu/drm/tyr/tyr.rs | 1 + 2 files changed, 438 insertions(+) diff --git a/drivers/gpu/drm/tyr/slot.rs b/drivers/gpu/drm/tyr/slot.rs new file mode 100644 index 000000000000..debba75f6204 --- /dev/null +++ b/drivers/gpu/drm/tyr/slot.rs @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +//! Slot management abstraction for limited hardware resources. +//! +//! This module provides a generic [`SlotManager`] that assigns limited ha= rdware +//! slots to logical "seats". A seat represents an entity (such as a virtu= al memory +//! (VM) address space) that needs access to a hardware slot. +//! +//! The [`SlotManager`] tracks slot allocation using sequence numbers (seq= no) to detect +//! when a seat's binding has been invalidated. When a seat requests activ= ation, +//! the manager will either reuse the seat's existing slot (if still valid= ), +//! allocate a free slot (if any are available), or evict the oldest idle = slot if any +//! slots are idle. +//! +//! Hardware-specific behavior is customized by implementing the [`SlotOpe= rations`] +//! trait, which allows callbacks when slots are activated or evicted. +//! +//! This is currently used for managing address space slots in the GPU, an= d it will +//! also be used to manage Command Stream Group (CSG) interface slots in t= he future. +//! +//! [SlotOperations]: crate::slot::SlotOperations +//! [SlotManager]: crate::slot::SlotManager +#![allow(dead_code)] + +use core::{ + mem::take, + ops::{ + Deref, + DerefMut, // + }, // +}; + +use kernel::{ + prelude::*, + sync::LockedBy, // +}; + +/// Seat information. +/// +/// This can't be accessed directly by the element embedding a `Seat`, +/// but is used by the generic slot manager logic to control residency +/// of a certain object on a hardware slot. +pub(crate) struct SeatInfo { + /// Slot used by this seat. + /// + /// This index is only valid if the slot pointed to by this index + /// has its `SlotInfo::seqno` match `SeatInfo::seqno`. Otherwise, + /// it means the object has been evicted from the hardware slot, + /// and a new slot needs to be acquired to make this object + /// resident again. + slot: u8, + + /// Sequence number encoding the last time this seat was active. + /// We also use it to check if a slot is still bound to a seat. + seqno: u64, +} + +/// Seat state. +/// +/// This is meant to be embedded in the object that wants to acquire +/// hardware slots. It also starts in the `Seat::NoSeat` state, and +/// the slot manager will change the object value when an active/evict +/// request is issued. +#[derive(Default)] +pub(crate) enum Seat { + #[expect(clippy::enum_variant_names)] + /// Resource is not resident. + /// + /// All objects start with a seat in the `Seat::NoSeat` state. The sea= t also + /// gets back to that state if the user requests eviction. It + /// can also end up in that state next time an operation is done + /// on a `Seat::Idle` seat and the slot manager finds out this + /// object has been evicted from the slot. + #[default] + NoSeat, + + /// Resource is actively used and resident. + /// + /// When a seat is in the `Seat::Active` state, it can't be evicted, a= nd the + /// slot pointed to by `SeatInfo::slot` is guaranteed to be reserved + /// for this object as long as the seat stays active. + Active(SeatInfo), + + /// Resource is idle and might or might not be resident. + /// + /// When a seat is in the`Seat::Idle` state, we can't know for sure if= the + /// object is resident or evicted until the next request we issue + /// to the slot manager. This tells the slot manager it can + /// reclaim the underlying slot if needed. + /// In order for the hardware to use this object again, the seat + /// needs to be turned into an `Seat::Active` state again + /// with a `SlotManager::activate()` call. + Idle(SeatInfo), +} + +impl Seat { + /// Get the slot index this seat is pointing to. + /// + /// If the seat is not `Seat::Active` we can't trust the + /// `SeatInfo`. In that case `None` is returned, otherwise + /// `Some(SeatInfo::slot)` is returned. + pub(super) fn slot(&self) -> Option { + match self { + Self::Active(info) =3D> Some(info.slot), + _ =3D> None, + } + } +} + +/// Trait describing the slot-related operations. +pub(crate) trait SlotOperations { + /// Implementation-specific data associated with each slot. + type SlotData; + + /// Called when a slot is being activated for a seat. + /// + /// This callback allows hardware-specific actions to be performed whe= n a slot + /// becomes active, such as updating hardware registers or invalidatin= g caches. + fn activate(&mut self, _slot_idx: usize, _slot_data: &Self::SlotData) = -> Result { + Ok(()) + } + + /// Called when a slot is being evicted and freed. + /// + /// This callback allows hardware-specific cleanup when a slot is being + /// completely freed, either explicitly or when an idle slot is being + /// reused for a different seat. Any hardware state should be invalida= ted. + fn evict(&mut self, _slot_idx: usize, _slot_data: &Self::SlotData) -> = Result { + Ok(()) + } +} + +/// Data attached to a slot. +/// +/// Contains data and the sequence number used to check +/// whether a seat's binding to this slot is still valid. +struct SlotInfo { + /// Type specific data attached to a slot + slot_data: T, + + /// Sequence number from when this slot was last activated + seqno: u64, +} + +/// Slot state. +/// +/// Tracks whether a hardware slot is free, actively in use, or idle and a= vailable +/// for eviction. +#[derive(Default)] +enum Slot { + /// Slot is free. + /// + /// All slots start in the `Slot::Free` state when the slot manager is= created. + #[default] + Free, + + /// Slot is active. + /// + /// When in the `Slot::Active` state, the slot is guaranteed to stay a= ctive + /// for as long as the resource bound to it has its seat in the + /// `Seat::Active` state. No new resource can be bound to it. + Active(SlotInfo), + + /// Slot is idle. + /// + /// Happens when the underlying resource has been flagged + /// `Seat::Idle`. When in the `Slot::Idle` state, the slot manager is = allowed + /// to evict the resource and re-assign the slot to someone else. + /// This process involves updating the `SlotInfo::seqno` which + /// will be checked against the `SeatInfo::seqno` in case the idle + /// resource wants to become active again. + Idle(SlotInfo), +} + +/// Generic slot manager object. +/// +/// It abstracts away all the churn around activeness/idleness tracking +/// and lets the implementer of the SlotOperations trait focus on how to +/// make a resource active or evict it. +/// +/// This structure must be protected by a lock. +/// Seats that want to use this manager must be wrapped with +/// `LockedBy>` to ensure they are protect= ed by the same lock. +/// All operations on seats and slots are synchronized through this shared= lock. +pub(crate) struct SlotManager { + /// Manager specific data + manager: T, + + /// Number of slots actually available + slot_count: usize, + + /// Slots + slots: [Slot; MAX_SLOTS], + + /// Sequence number incremented each time a Seat is successfully activ= ated + use_seqno: u64, +} + +/// A `Seat` protected by the same lock that is used to wrap the `SlotMana= ger`. +type LockedSeat =3D LockedBy>; + +impl SlotManager { + /// Creates a new slot manager. + /// + /// Returns [`EINVAL`] if the slot count is zero or exceeds the maximu= m number of slots. + pub(crate) fn new(manager: T, slot_count: usize) -> Result { + if slot_count =3D=3D 0 { + return Err(EINVAL); + } + if slot_count > MAX_SLOTS { + return Err(EINVAL); + } + Ok(Self { + manager, + slot_count, + slots: [const { Slot::Free }; MAX_SLOTS], + use_seqno: 1, + }) + } + + /// Records a slot as active for the given seat. + /// + /// Updates both the seat state and the slot state to reflect the acti= ve binding, + /// using the current sequence number. Increments the sequence number = for the next + /// activation. + fn record_active_slot( + &mut self, + slot_idx: usize, + locked_seat: &LockedSeat, + slot_data: T::SlotData, + ) -> Result { + let cur_seqno =3D self.use_seqno; + + *locked_seat.access_mut(self) =3D Seat::Active(SeatInfo { + slot: slot_idx as u8, + seqno: cur_seqno, + }); + + self.slots[slot_idx] =3D Slot::Active(SlotInfo { + slot_data, + seqno: cur_seqno, + }); + + self.use_seqno +=3D 1; + Ok(()) + } + + /// Activates a slot for the given seat. + /// + /// Calls the activation callback and then records the slot as active. + fn activate_slot( + &mut self, + slot_idx: usize, + locked_seat: &LockedSeat, + slot_data: T::SlotData, + ) -> Result { + self.manager.activate(slot_idx, &slot_data)?; + self.record_active_slot(slot_idx, locked_seat, slot_data) + } + + /// Allocates a slot for the given seat. + /// + /// Searches for a free slot first. If none are available, finds the o= ldest idle + /// slot (by sequence number) and evicts it. Returns [`EBUSY`] if all = slots are + /// active and none can be evicted. + fn allocate_slot( + &mut self, + locked_seat: &LockedSeat, + slot_data: T::SlotData, + ) -> Result { + let slots =3D &self.slots[..self.slot_count]; + + let mut idle_slot_idx =3D None; + let mut idle_slot_seqno: u64 =3D 0; + + for (slot_idx, slot) in slots.iter().enumerate() { + match slot { + Slot::Free =3D> { + return self.activate_slot(slot_idx, locked_seat, slot_= data); + } + Slot::Idle(slot_info) =3D> { + if idle_slot_idx.is_none() || slot_info.seqno < idle_s= lot_seqno { + idle_slot_idx =3D Some(slot_idx); + idle_slot_seqno =3D slot_info.seqno; + } + } + Slot::Active(_) =3D> (), + } + } + + match idle_slot_idx { + Some(slot_idx) =3D> { + // Lazily evict idle slot just before it is reused + if let Slot::Idle(slot_info) =3D &self.slots[slot_idx] { + self.manager.evict(slot_idx, &slot_info.slot_data)?; + } + self.activate_slot(slot_idx, locked_seat, slot_data) + } + None =3D> { + pr_err!( + "Slot allocation failed: all {} slots in use\n", + self.slot_count + ); + Err(EBUSY) + } + } + } + + /// Transitions a slot from active to idle state. + /// + /// Updates both the slot and seat to idle state, making the slot elig= ible for + /// eviction if needed by another seat. + fn idle_slot(&mut self, slot_idx: usize, locked_seat: &LockedSeat) -> Result { + let slot =3D take(&mut self.slots[slot_idx]); + + if let Slot::Active(slot_info) =3D slot { + self.slots[slot_idx] =3D Slot::Idle(SlotInfo { + slot_data: slot_info.slot_data, + seqno: slot_info.seqno, + }) + }; + + *locked_seat.access_mut(self) =3D match locked_seat.access(self) { + Seat::Active(seat_info) | Seat::Idle(seat_info) =3D> Seat::Idl= e(SeatInfo { + slot: seat_info.slot, + seqno: seat_info.seqno, + }), + Seat::NoSeat =3D> Seat::NoSeat, + }; + Ok(()) + } + + /// Evicts a seat from its slot and marks the slot as free. + /// + /// Calls the eviction callback then frees the slot and resets the sea= t to `NoSeat`. + fn evict_slot(&mut self, slot_idx: usize, locked_seat: &LockedSeat) -> Result { + match &self.slots[slot_idx] { + Slot::Active(slot_info) | Slot::Idle(slot_info) =3D> { + self.manager.evict(slot_idx, &slot_info.slot_data)?; + take(&mut self.slots[slot_idx]); + } + _ =3D> (), + } + + *locked_seat.access_mut(self) =3D Seat::NoSeat; + Ok(()) + } + + /// Checks and updates the seat state based on the slot it points to. + /// + /// Validates that the seat's sequence number matches the slot's seque= nce number. + /// If they don't match, the seat has been evicted and is reset to `No= Seat`. + fn check_seat(&mut self, locked_seat: &LockedSeat) { + let (slot_idx, seqno, is_active) =3D match locked_seat.access(self= ) { + Seat::Active(info) =3D> (info.slot as usize, info.seqno, true), + Seat::Idle(info) =3D> (info.slot as usize, info.seqno, false), + _ =3D> return, + }; + + let valid =3D if is_active { + !kernel::warn_on!(!matches!(&self.slots[slot_idx], Slot::Activ= e(s) if s.seqno =3D=3D seqno)) + } else { + matches!(&self.slots[slot_idx], Slot::Idle(s) if s.seqno =3D= =3D seqno) + }; + + if !valid { + *locked_seat.access_mut(self) =3D Seat::NoSeat; + } + } + + /// Make a resource active on any available/reclaimable slot. + /// + /// Returns [`EBUSY`] if all slots are in use and none can be reclaimed + /// or the reclaim failed. May also return errors from the callbacks. + pub(crate) fn activate( + &mut self, + locked_seat: &LockedSeat, + slot_data: T::SlotData, + ) -> Result { + self.check_seat(locked_seat); + match locked_seat.access(self) { + Seat::Active(seat_info) | Seat::Idle(seat_info) =3D> { + // With lazy eviction, if seqno matches, the hardware stat= e is still + // valid for both Active and Idle slots, so just update ou= r records + self.record_active_slot(seat_info.slot as usize, locked_se= at, slot_data) + } + _ =3D> self.allocate_slot(locked_seat, slot_data), + } + } + + /// Flag a resource idle. + /// + /// The slot manager can decide to reclaim the slot this resource + /// was bound to at any point after function returns. + // The idle() method will be used when we start adding support for use= r VMs. + #[expect(dead_code)] + pub(crate) fn idle(&mut self, locked_seat: &LockedSeat) = -> Result { + self.check_seat(locked_seat); + if let Seat::Active(seat_info) =3D locked_seat.access(self) { + self.idle_slot(seat_info.slot as usize, locked_seat)?; + } + Ok(()) + } + + /// Evict a resource from its slot, and make this slot free again + /// for other users. + /// + /// May return errors from the eviction callback. + pub(crate) fn evict(&mut self, locked_seat: &LockedSeat)= -> Result { + self.check_seat(locked_seat); + + match locked_seat.access(self) { + Seat::Active(seat_info) | Seat::Idle(seat_info) =3D> { + let slot_idx =3D seat_info.slot as usize; + + self.evict_slot(slot_idx, locked_seat)?; + } + _ =3D> (), + } + + Ok(()) + } +} + +impl Deref for SlotManager { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + &self.manager + } +} + +impl DerefMut for SlotManager { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.manager + } +} diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs index 9432ddd6b5b8..20b38120e20e 100644 --- a/drivers/gpu/drm/tyr/tyr.rs +++ b/drivers/gpu/drm/tyr/tyr.rs @@ -12,6 +12,7 @@ mod gem; mod gpu; mod regs; +mod slot; =20 kernel::module_platform_driver! { type: TyrPlatformDriverData, --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7B47236605A; Mon, 13 Apr 2026 22:32:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119554; cv=pass; b=gIOje0G2seoMfS0W+01vYGx0J+7VX4p7/Qj8mlneeMzCr+TiKA+Sx5NI7zb6/2tvlB0QQAekSTyzzo7kd/8OpoV5JX5Ve8RwGrf6ihfqk6dYnBKJGRfS77OwknF3IDLVmGNaKI4+sVonK4dJUtcoCU26WMFmAB7oA8ZzHERMhaU= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119554; c=relaxed/simple; bh=tQsINGSkX/nlZANM6pM+NMyJafJFFDgWYrk05XlRSJY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Yoo+zwX2Vop2jiGtVthDbrzELA1Lm2z/xm5Y5TFoJ2+iyrJnAGThAHWjMfzgNR1zeyJqndlQhrzyWXxg0iXkfRWMCUXq5dk9s3FxiwNCDdhsoSL+LT0aSPfBPBZYu/9Rr8eXwDnL6h6RUkywAsJr/D8HcxbwTth00OlbPOjUsos= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=IvPxR754; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="IvPxR754" ARC-Seal: i=1; a=rsa-sha256; t=1776119523; cv=none; d=zohomail.com; s=zohoarc; b=mFw/ilW2zCsXYeivP3LhG5gOVLeveWaA1JaMMD4gTbVV0n0i0LHW193ezMgGXxf9HngJblvDqpAQQAqHze7jayXyNEjrheun5mLduu5/supeIgg5Svrnz+z5LKhCvJ/22hu+3Q9FQmonN23NpBrP96fn18ICIPDzlpy2AQm++fA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119523; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=C/m0ypL/3oAer/xlqaqK+nR119FCTivyqk+k/gXCT5o=; b=RXFOtwcWDC8cAId/EPXS/k0vf+T/vt2h8xaIc8n2IU2pgFqVLNWKPTTf4DiFnPPIMIppIl+n66qoFiG3LTgpIPrNyS7aVBpMmos2F6T+6E9MrbPzjNfK6BCdxSm5ZqY26mr5raBwRspPoKFV0rnQKomipAs2MIYvulAHAORhXwk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119523; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=C/m0ypL/3oAer/xlqaqK+nR119FCTivyqk+k/gXCT5o=; b=IvPxR754lxhmau8ObA7NEEF56bJbHqbsFLLRIxBIJviBO/mgMV0CsuC3TTM38Nfh zzUBxMdfWqu8p+dqbcGigt4WvTkwL3g4Rzvq9B4QTU8xl+Nu0sUc+aIB+8YlDJAlDJl 5u88zLyHldnfrc0sSGDQTHQqx/MQ4LsRAeRStx3U= Received: by mx.zohomail.com with SMTPS id 1776119522369451.23767583430583; Mon, 13 Apr 2026 15:32:02 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:44 -0700 Subject: [PATCH v3 09/13] drm/tyr: add MMU module Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-9-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=34311; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=Y2oeP8IZFKghbe3KLU5OHdSNN4VeTVKubV5WJG6b5LE=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3865ubD6dlfDE8p3Ij0mufb95fkppNtmoN2toMrY+j In7uOxuRykLgxgXg6yYIstZe6Me8ar3Rrrz/zfDzGFlAhnCwMUpABN5cprhn5qFW11bw36Fowkf ZiX+PvhDfUresq3zXx5T45f9dvulTirDP0MWl3JZx5y0Jjk+Kavd0x1UeaQV023O/p2+mkl0Uaw REwA= X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB From: Boris Brezillon Add a Memory Management Unit (MMU) driver for Tyr. The MMU wraps a SlotManager for allocating hardware address space slots. The underlying AddressSpaceManager performs MMU operations including enabling/disabling address spaces, flushing page tables, and locking regions for page table updates. Signed-off-by: Boris Brezillon Co-developed-by: Deborah Brouwer Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/driver.rs | 3 + drivers/gpu/drm/tyr/mmu.rs | 128 +++++++ drivers/gpu/drm/tyr/mmu/address_space.rs | 571 +++++++++++++++++++++++++++= ++++ drivers/gpu/drm/tyr/regs.rs | 110 ++++++ drivers/gpu/drm/tyr/tyr.rs | 1 + 5 files changed, 813 insertions(+) diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 1f09f59e271a..495021a8657d 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -45,6 +45,7 @@ gem::BoData, gpu, gpu::GpuInfo, + mmu::Mmu, regs::gpu_control::*, // }; =20 @@ -153,6 +154,8 @@ fn probe( let uninit_ddev =3D UnregisteredDevice::::new(pdev.a= s_ref())?; let platform: ARef =3D pdev.into(); =20 + let _mmu =3D Mmu::new(pdev, iomem.as_arc_borrow(), &gpu_info)?; + let data =3D try_pin_init!(TyrDrmDeviceData { pdev: platform.clone(), clks <- new_mutex!(Clocks { diff --git a/drivers/gpu/drm/tyr/mmu.rs b/drivers/gpu/drm/tyr/mmu.rs new file mode 100644 index 000000000000..09df98ffc9e3 --- /dev/null +++ b/drivers/gpu/drm/tyr/mmu.rs @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +//! Memory Management Unit (MMU) driver for the Tyr GPU. +//! +//! This module manages GPU address spaces and virtual memory (VM) operati= ons through +//! hardware MMU slots. It provides functionality for flushing page tables= and +//! managing VM updates for active address spaces. +//! +//! The MMU coordinates with the [`AddressSpaceManager`] to handle hardware +//! address space allocation and page table operations, using [`SlotManage= r`] +//! to track which address spaces are currently active in hardware slots. +//! +//! [`AddressSpaceManager`]: address_space::AddressSpaceManager +//! [`SlotManager`]: crate::slot::SlotManager +#![allow(dead_code)] + +use core::ops::Range; + +use kernel::{ + devres::Devres, + new_mutex, + platform, + prelude::*, + sync::{ + Arc, + ArcBorrow, + Mutex, // + }, // +}; + +use crate::{ + driver::IoMem, + gpu::GpuInfo, + mmu::address_space::{ + AddressSpaceManager, + VmAsData, // + }, + regs::{ + gpu_control::AS_PRESENT, + MAX_AS, // + }, + slot::SlotManager, // +}; + +pub(crate) mod address_space; + +pub(crate) type AsSlotManager =3D SlotManager; + +/// MMU component of the GPU. +/// +/// This is used to bind VM objects to an AS (Address Space) slot +/// and make the VM active on the GPU. +/// +/// All operations acquire an internal lock, allowing concurrent access fr= om multiple +/// threads. Methods may block if another thread holds the lock. +#[pin_data] +pub(crate) struct Mmu { + /// Manages the allocation of hardware MMU slots to GPU address spaces. + /// + /// Tracks which address spaces are currently active in hardware slots= and + /// coordinates address space operations like flushing and VM updates. + /// + /// This mutex also protects individual [`Seat`]s that are wrapped with + /// `LockedBy>` to share the same lock protecti= on. + /// + /// [`Seat`]: crate::slot::Seat + #[pin] + pub(crate) as_manager: Mutex, +} + +impl Mmu { + /// Create an MMU component for this device. + pub(crate) fn new( + pdev: &platform::Device, + iomem: ArcBorrow<'_, Devres>, + gpu_info: &GpuInfo, + ) -> Result> { + let present =3D AS_PRESENT::from_raw(gpu_info.as_present).present(= ).get(); + let slot_count =3D present.count_ones().try_into()?; + + let as_manager =3D AddressSpaceManager::new(pdev, iomem, present)?; + let mmu_init =3D try_pin_init!(Self{ + as_manager <- new_mutex!(SlotManager::new(as_manager, slot_cou= nt)?), + }); + Arc::pin_init(mmu_init, GFP_KERNEL) + } + + /// Make a VM active. + /// + /// This implies assigning the VM to an AS slot through the slot manag= er. + pub(crate) fn activate_vm(&self, vm: ArcBorrow<'_, VmAsData>) -> Resul= t { + self.as_manager.lock().activate_vm(vm) + } + + /// Make the VM inactive. + /// + /// Evicts the VM from its AS slot through the slot manager. + pub(crate) fn deactivate_vm(&self, vm: &VmAsData) -> Result { + self.as_manager.lock().deactivate_vm(vm) + } + + /// Flush caches after a VM update. + /// + /// If the VM is no longer resident, this is a NOP, otherwise, the + /// AS manager will flush the GPU and MMU Translation Lookaside Buffer= (TLB) caches. + pub(crate) fn flush_vm(&self, vm: &VmAsData) -> Result { + self.as_manager.lock().flush_vm(vm) + } + + /// Flags the start of a VM update. + /// + /// If the VM is resident, any GPU access on the memory range being + /// updated will be blocked until `Mmu::end_vm_update()` is called. + /// This guarantees the atomicity of a VM update. + /// If the VM is not resident, this is a NOP. + pub(crate) fn start_vm_update(&self, vm: &VmAsData, region: &Range) -> Result { + self.as_manager.lock().start_vm_update(vm, region) + } + + /// Flags the end of a VM update. + /// + /// If the VM is resident, this will let GPU accesses on the updated + /// range go through, in case any of them were blocked. + /// If the VM is not resident, this is a NOP. + pub(crate) fn end_vm_update(&self, vm: &VmAsData) -> Result { + self.as_manager.lock().end_vm_update(vm) + } +} diff --git a/drivers/gpu/drm/tyr/mmu/address_space.rs b/drivers/gpu/drm/tyr= /mmu/address_space.rs new file mode 100644 index 000000000000..cc2841bab21c --- /dev/null +++ b/drivers/gpu/drm/tyr/mmu/address_space.rs @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +//! GPU address space management and hardware operations. +//! +//! This module manages GPU hardware address spaces (AS), including config= uration, +//! command submission, and page table update regions. It handles the hard= ware +//! interaction for MMU operations through MMIO register access. +//! +//! The [`AddressSpaceManager`] implements [`SlotOperations`] to integrate= with +//! the slot management system, enabling and configuring address spaces in= the +//! hardware slots as needed. +//! +//! [`SlotOperations`]: crate::slot::SlotOperations + +use core::ops::Range; + +use kernel::{ + device::{ + Bound, + Device, // + }, + devres::Devres, + error::Result, + io::{ + poll, + register::Array, + Io, // + }, + iommu::pgtable::{ + Config, + IoPageTable, + ARM64LPAES1, // + }, + platform, + prelude::*, + sizes::{ + SZ_2M, + SZ_4K, // + }, + sync::{ + aref::ARef, + Arc, + ArcBorrow, + LockedBy, // + }, + time::Delta, // +}; + +use crate::{ + driver::IoMem, + mmu::{ + AsSlotManager, + Mmu, // + }, + regs::{ + mmu_control::mmu_as_control, + mmu_control::mmu_as_control::*, + MAX_AS, // + }, + slot::{ + Seat, + SlotOperations, // + }, // +}; + +/// Hardware address space configuration registers. +/// +/// Contains register values for configuring a GPU MMU address space. +#[derive(Clone, Copy)] +struct AddressSpaceConfig { + /// Translation configuration. + /// + /// Controls address translation mode, address range restrictions, tra= nslation table + /// walk attributes, and access permission settings for this address s= pace. + transcfg: u64, + + /// Translation table base address. + /// + /// The address of the top level of a translation table structure. + transtab: u64, + + /// Memory attributes. + /// + /// Defines memory attribute indirection entries that control cacheabi= lity + /// and other memory access properties for the address space. + memattr: u64, +} + +/// Virtual memory (VM) address space data for GPU MMU operations. +/// +/// Contains all resources and information needed by the [`AddressSpaceMan= ager`] +/// to activate a VM in a hardware address space slot. +/// +/// On activation, we will pass an [`Arc`]<[`VmAsData`]> that will be stor= ed in +/// the slot to make sure the page table and the underlying resources +/// (pages) used by the AS slot won't go away while the MMU points to +/// those. +/// +/// The `as_seat` field uses [`LockedBy`] to ensure safe concurrent access= to +/// the slot assignment state, protected by the [`AsSlotManager`] lock. +#[pin_data] +pub(crate) struct VmAsData { + /// Tracks this VM's binding to a hardware address space slot. + as_seat: LockedBy, + + /// Virtual address bits for this address space. + va_bits: u8, + + /// Page table. + /// + /// Managed by devres to ensure proper cleanup. The page table maps + /// GPU virtual addresses to physical addresses for this VM. + #[pin] + pub(crate) page_table: Devres>, +} + +impl VmAsData { + /// Creates a new VM address space data structure. + /// + /// Initializes the page table for the address space. + pub(crate) fn new<'a>( + mmu: &'a Mmu, + pdev: &'a platform::Device, + va_bits: u32, + pa_bits: u32, + ) -> impl pin_init::PinInit + 'a { + // SAFETY: pdev is a bound device. + let dev =3D unsafe { pdev.as_ref().as_bound() }; + + let pt_config =3D Config { + quirks: 0, + pgsize_bitmap: SZ_4K | SZ_2M, + ias: va_bits, + oas: pa_bits, + coherent_walk: false, + }; + + let page_table_init =3D IoPageTable::new(dev, pt_config); + + try_pin_init!(Self { + as_seat: LockedBy::new(&mmu.as_manager, Seat::NoSeat), + va_bits: va_bits as u8, + page_table <- page_table_init, + }? Error) + } + + /// Computes the hardware configuration for this address space. + /// + /// The caller must ensure that the address space is evicted and clean= ed up + /// before the `VmAsData` is dropped. + fn as_config(&self, dev: &Device) -> Result= { + let pt =3D self.page_table.access(dev)?; + + // The hardware computes the valid input address range as: + // INA_BITS_VALID =3D min(HW_INA_BITS, 55 - INA_BITS) + // To configure our desired va_bits, we solve for INA_BITS: + // INA_BITS =3D 55 - va_bits + // This assumes HW_INA_BITS (hardware capability) >=3D va_bits. + let ina_bits_field_value =3D 55 - self.va_bits; + let ina_bits =3D match ina_bits_field_value { + 7 =3D> mmu_as_control::InaBits::Bits48, + 8 =3D> mmu_as_control::InaBits::Bits47, + 9 =3D> mmu_as_control::InaBits::Bits46, + 10 =3D> mmu_as_control::InaBits::Bits45, + 11 =3D> mmu_as_control::InaBits::Bits44, + 12 =3D> mmu_as_control::InaBits::Bits43, + 13 =3D> mmu_as_control::InaBits::Bits42, + 14 =3D> mmu_as_control::InaBits::Bits41, + 15 =3D> mmu_as_control::InaBits::Bits40, + 16 =3D> mmu_as_control::InaBits::Bits39, + 17 =3D> mmu_as_control::InaBits::Bits38, + 18 =3D> mmu_as_control::InaBits::Bits37, + 19 =3D> mmu_as_control::InaBits::Bits36, + 20 =3D> mmu_as_control::InaBits::Bits35, + 21 =3D> mmu_as_control::InaBits::Bits34, + 22 =3D> mmu_as_control::InaBits::Bits33, + 23 =3D> mmu_as_control::InaBits::Bits32, + 24 =3D> mmu_as_control::InaBits::Bits31, + 25 =3D> mmu_as_control::InaBits::Bits30, + 26 =3D> mmu_as_control::InaBits::Bits29, + 27 =3D> mmu_as_control::InaBits::Bits28, + 28 =3D> mmu_as_control::InaBits::Bits27, + 29 =3D> mmu_as_control::InaBits::Bits26, + 30 =3D> mmu_as_control::InaBits::Bits25, + _ =3D> return Err(EINVAL), + }; + + let transcfg =3D mmu_as_control::TRANSCFG::zeroed() + .with_ptw_memattr(mmu_as_control::PtwMemattr::WriteBack) + .with_r_allocate(true) + .with_mode(mmu_as_control::AddressSpaceMode::Aarch64_4K) + .with_ina_bits(ina_bits) + .into_raw(); + + Ok(AddressSpaceConfig { + transcfg, + // SAFETY: Caller ensures proper cleanup. + transtab: unsafe { pt.ttbr() }, + memattr: MEMATTR::from_mair(pt.mair()).into_raw(), + }) + } +} + +/// Manages GPU hardware address spaces via MMIO register operations. +/// +/// Coordinates all hardware-level address space operations including enab= ling, +/// disabling, flushing, and updating address spaces. Implements [`SlotOpe= rations`] +/// to integrate with the generic slot management system. +/// +/// [`SlotOperations`]: crate::slot::SlotOperations +pub(crate) struct AddressSpaceManager { + /// Platform device reference for DMA and device operations. + pdev: ARef, + + /// Memory-mapped I/O region for GPU register access. + iomem: Arc>, + + /// Bitmask of available address space slots from GPU_AS_PRESENT regis= ter. + as_present: u32, +} + +impl SlotOperations for AddressSpaceManager { + /// VM address space data stored in each hardware slot. + type SlotData =3D Arc; + + /// Activates an address space in a hardware slot. + fn activate(&mut self, slot_idx: usize, slot_data: &Self::SlotData) ->= Result { + let as_config =3D slot_data.as_config(self.dev())?; + self.as_enable(slot_idx, &as_config) + } + + /// Evicts an address space from a hardware slot. + fn evict(&mut self, slot_idx: usize, _slot_data: &Self::SlotData) -> R= esult { + if self.iomem.try_access().is_some() { + self.as_flush(slot_idx)?; + self.as_disable(slot_idx)?; + } + Ok(()) + } +} + +impl AddressSpaceManager { + /// Creates a new address space manager. + /// + /// Initializes the manager with references to the platform device and + /// I/O memory region, along with the bitmask of available AS slots. + pub(super) fn new( + pdev: &platform::Device, + iomem: ArcBorrow<'_, Devres>, + as_present: u32, + ) -> Result { + Ok(Self { + pdev: pdev.into(), + iomem: iomem.into(), + as_present, + }) + } + + /// Returns a reference to the bound device. + fn dev(&self) -> &Device { + // SAFETY: pdev is a bound device. + unsafe { self.pdev.as_ref().as_bound() } + } + + /// Validates that an AS slot number is within range and present in ha= rdware. + /// + /// Checks that the slot index is less than [`MAX_AS`] and that + /// the corresponding bit is set in the `as_present` mask read from th= e GPU. + /// + /// Returns [`EINVAL`] if the slot is out of range or not present in h= ardware. + fn validate_as_slot(&self, as_nr: usize) -> Result { + if as_nr >=3D MAX_AS { + pr_err!("AS slot {} out of valid range (max {})\n", as_nr, MAX= _AS); + return Err(EINVAL); + } + + if (self.as_present & (1 << as_nr)) =3D=3D 0 { + pr_err!( + "AS slot {} not present in hardware (AS_PRESENT=3D{:#x})\n= ", + as_nr, + self.as_present + ); + return Err(EINVAL); + } + + Ok(()) + } + + /// Waits for an AS slot to become ready (not active). + /// + /// Returns an error if polling times out after 10ms or if register ac= cess fails. + fn as_wait_ready(&self, as_nr: usize) -> Result { + let dev =3D self.dev(); + let io =3D self.iomem.access(dev)?; + let op =3D || { + let status_reg =3D STATUS::try_at(as_nr).ok_or(EINVAL)?; + Ok(io.read(status_reg)) + }; + let cond =3D |status: &STATUS| -> bool { !status.active_ext() }; + poll::read_poll_timeout(op, cond, Delta::from_millis(0), Delta::fr= om_millis(10))?; + + Ok(()) + } + + /// Sends a command to an AS slot. + /// + /// Returns an error if waiting for ready times out or if register wri= te fails. + fn as_send_cmd(&mut self, as_nr: usize, cmd: MmuCommand) -> Result { + self.as_wait_ready(as_nr)?; + let dev =3D self.dev(); + let io =3D self.iomem.access(dev)?; + let command_reg =3D COMMAND::try_at(as_nr).ok_or(EINVAL)?; + io.write(command_reg, COMMAND::zeroed().with_command(cmd)); + Ok(()) + } + + /// Sends a command to an AS slot and waits for completion. + /// + /// Returns an error if sending the command fails or if waiting for co= mpletion times out. + fn as_send_cmd_and_wait(&mut self, as_nr: usize, cmd: MmuCommand) -> R= esult { + self.as_send_cmd(as_nr, cmd)?; + self.as_wait_ready(as_nr)?; + Ok(()) + } + + /// Enables an AS slot with the provided configuration. + /// + /// Returns an error if the slot is invalid or if register writes/comm= ands fail. + fn as_enable(&mut self, as_nr: usize, as_config: &AddressSpaceConfig) = -> Result { + self.validate_as_slot(as_nr)?; + + let dev =3D self.dev(); + let io =3D self.iomem.access(dev)?; + + let transtab =3D as_config.transtab; + io.write( + TRANSTAB_LO::try_at(as_nr).ok_or(EINVAL)?, + TRANSTAB_LO::from_raw(transtab as u32), + ); + io.write( + TRANSTAB_HI::try_at(as_nr).ok_or(EINVAL)?, + TRANSTAB_HI::from_raw((transtab >> 32) as u32), + ); + + let transcfg =3D as_config.transcfg; + io.write( + TRANSCFG_LO::try_at(as_nr).ok_or(EINVAL)?, + TRANSCFG_LO::from_raw(transcfg as u32), + ); + io.write( + TRANSCFG_HI::try_at(as_nr).ok_or(EINVAL)?, + TRANSCFG_HI::from_raw((transcfg >> 32) as u32), + ); + + let memattr =3D as_config.memattr; + io.write( + MEMATTR_LO::try_at(as_nr).ok_or(EINVAL)?, + MEMATTR_LO::from_raw(memattr as u32), + ); + io.write( + MEMATTR_HI::try_at(as_nr).ok_or(EINVAL)?, + MEMATTR_HI::from_raw((memattr >> 32) as u32), + ); + + self.as_send_cmd_and_wait(as_nr, MmuCommand::Update)?; + + Ok(()) + } + + /// Disables an AS slot and clears its configuration. + /// + /// Returns an error if the slot is invalid or if register writes/comm= ands fail. + fn as_disable(&mut self, as_nr: usize) -> Result { + self.validate_as_slot(as_nr)?; + + // Flush AS before disabling + self.as_send_cmd_and_wait(as_nr, MmuCommand::FlushMem)?; + + let dev =3D self.dev(); + let io =3D self.iomem.access(dev)?; + + io.write( + TRANSTAB_LO::try_at(as_nr).ok_or(EINVAL)?, + TRANSTAB_LO::from_raw(0), + ); + io.write( + TRANSTAB_HI::try_at(as_nr).ok_or(EINVAL)?, + TRANSTAB_HI::from_raw(0), + ); + + io.write( + MEMATTR_LO::try_at(as_nr).ok_or(EINVAL)?, + MEMATTR_LO::from_raw(0), + ); + io.write( + MEMATTR_HI::try_at(as_nr).ok_or(EINVAL)?, + MEMATTR_HI::from_raw(0), + ); + + let transcfg =3D TRANSCFG::zeroed() + .with_mode(AddressSpaceMode::Unmapped) + .into_raw(); + + io.write( + TRANSCFG_LO::try_at(as_nr).ok_or(EINVAL)?, + TRANSCFG_LO::from_raw(transcfg as u32), + ); + io.write( + TRANSCFG_HI::try_at(as_nr).ok_or(EINVAL)?, + TRANSCFG_HI::from_raw((transcfg >> 32) as u32), + ); + + self.as_send_cmd_and_wait(as_nr, MmuCommand::Update)?; + + Ok(()) + } + + /// Locks a region of the translation tables for an atomic update. + /// + /// Programs the MMU LOCKADDR register for the given address space and= issues + /// the lock command. The hardware rounds the requested range up to a + /// power-of-two region aligned to its size. + /// + /// Returns an error if the slot is invalid or if register writes/comm= ands fail. + fn as_start_update(&mut self, as_nr: usize, region: &Range) -> Re= sult { + self.validate_as_slot(as_nr)?; + + // The lock operates on full 64-byte cache lines of translation ta= ble entries. + // Since each translation table entry (TTE) is 8 bytes, a cache li= ne has 8 TTEs. + // Since each TTE maps one page, the minimum locked region size wi= ll be 8 pages. + // + // With 4KiB pages (Aarch64_4K mode), the minimum locked region is= 32KiB. + let lock_region_min_size: u64 =3D 32 * 1024; + + // Count the number of trailing zero bits (zeros at the right/leas= t-significant + // end of the binary representation). For a power-of-two value, th= is equals the + // base-2 exponent (e.g., 32 KiB =3D 2^15 =E2=86=92 15). + let lock_region_min_size_log2 =3D lock_region_min_size.trailing_ze= ros() as u8; + + // XOR the first and last addresses to identify which bits differ = between them. + // The highest set bit in the result determines the exponent of th= e smallest + // power-of-two region that can contain both addresses. + // + // Example: + // addr_xor =3D 0x1000 ^ 0x2FFF =3D 0x3FFF + // highest set bit in 0x3FFF is bit 13 + // minimum region size =3D 2^(13 + 1) =3D 16 KiB + let addr_xor =3D region.start ^ (region.end - 1); + let region_size_log2 =3D 64 - addr_xor.leading_zeros() as u8; + + let lock_region_log2 =3D core::cmp::max(region_size_log2, lock_reg= ion_min_size_log2); + + // Align the LOCKADDR base address down to the lock region size (1= << lock_region_log2). + // + // The MMU ignores the low lock_region_log2 bits of LOCKADDR base,= so ensure + // they are cleared in software to avoid ambiguity. + let lockaddr_base =3D region.start & !((1u64 << lock_region_log2) = - 1); + + // The LOCKADDR size field encodes the lock region size as log2(si= ze) - 1, + // per the hardware definition. For example, a 32 KiB region is en= coded as 14 + // because log2(32 KiB) =3D 15. + let lockaddr_size =3D lock_region_log2 - 1; + + let dev =3D self.dev(); + let io =3D self.iomem.access(dev)?; + + let lockaddr_val =3D LOCKADDR::zeroed() + .try_with_size(lockaddr_size)? + .try_with_base(lockaddr_base)? + .into_raw(); + + io.write( + LOCKADDR_LO::try_at(as_nr).ok_or(EINVAL)?, + LOCKADDR_LO::from_raw(lockaddr_val as u32), + ); + io.write( + LOCKADDR_HI::try_at(as_nr).ok_or(EINVAL)?, + LOCKADDR_HI::from_raw((lockaddr_val >> 32) as u32), + ); + + self.as_send_cmd(as_nr, MmuCommand::Lock) + } + + /// Completes an atomic translation table update. + /// + /// Returns an error if the slot is invalid or if the flush command fa= ils. + fn as_end_update(&mut self, as_nr: usize) -> Result { + self.validate_as_slot(as_nr)?; + self.as_send_cmd_and_wait(as_nr, MmuCommand::FlushPt)?; + Ok(()) + } + + /// Flushes the translation table cache for an AS slot. + /// + /// Returns an error if the slot is invalid or if the flush command fa= ils. + fn as_flush(&mut self, as_nr: usize) -> Result { + self.validate_as_slot(as_nr)?; + self.as_send_cmd(as_nr, MmuCommand::FlushPt) + } +} + +impl AsSlotManager { + /// Locks a region for translation table updates if the VM has an acti= ve slot. + /// + /// If the VM is currently assigned to a hardware slot, locks the spec= ified + /// memory region to make translation table updates atomic. GPU access= es to the + /// region will be blocked until [`end_vm_update`] is called. + /// + /// If the VM is not resident in a hardware slot, this is a no-op. + pub(super) fn start_vm_update(&mut self, vm: &VmAsData, region: &Range= ) -> Result { + let seat =3D vm.as_seat.access(self); + match seat.slot() { + Some(slot) =3D> { + let as_nr =3D slot as usize; + self.as_start_update(as_nr, region) + } + _ =3D> Ok(()), + } + } + + /// Completes translation table updates and unlocks the region. + /// + /// If the VM is currently assigned to a hardware slot, flushes the tr= anslation + /// table cache and unlocks the region that was locked by [`start_vm_u= pdate`], + /// allowing GPU accesses to proceed with the updated translation tabl= es. + /// + /// If the VM is not resident in a hardware slot, this is a no-op. + pub(super) fn end_vm_update(&mut self, vm: &VmAsData) -> Result { + let seat =3D vm.as_seat.access(self); + match seat.slot() { + Some(slot) =3D> { + let as_nr =3D slot as usize; + self.as_end_update(as_nr) + } + _ =3D> Ok(()), + } + } + + /// Flushes translation table cache if the VM has an active slot. + /// + /// If the VM is currently assigned to a hardware slot, invalidates ca= ched + /// translation table entries to ensure subsequent GPU accesses use up= dated translations. + /// + /// If the VM is not resident in a hardware slot, this is a no-op. + pub(super) fn flush_vm(&mut self, vm: &VmAsData) -> Result { + let seat =3D vm.as_seat.access(self); + match seat.slot() { + Some(slot) =3D> { + let as_nr =3D slot as usize; + self.as_flush(as_nr) + } + _ =3D> Ok(()), + } + } + + /// Activates a VM by assigning it to a hardware slot. + /// + /// Allocates a hardware address space slot for the VM and configures + /// it with the VM's translation table and memory attributes. + pub(super) fn activate_vm(&mut self, vm: ArcBorrow<'_, VmAsData>) -> R= esult { + self.activate(&vm.as_seat, vm.into()) + } + + /// Deactivates a VM by evicting it from its hardware slot. + /// + /// Flushes any pending operations and clears the hardware slot's + /// configuration, freeing the slot for use by other VMs. + pub(super) fn deactivate_vm(&mut self, vm: &VmAsData) -> Result { + self.evict(&vm.as_seat) + } +} diff --git a/drivers/gpu/drm/tyr/regs.rs b/drivers/gpu/drm/tyr/regs.rs index 9963294b8625..dafca19e3532 100644 --- a/drivers/gpu/drm/tyr/regs.rs +++ b/drivers/gpu/drm/tyr/regs.rs @@ -45,6 +45,8 @@ pub(crate) fn read_u64_no_tearing(lo_read: impl Fn() -> u= 32, hi_read: impl Fn() } } =20 +pub(crate) use mmu_control::mmu_as_control::MAX_AS; + /// These registers correspond to the GPU_CONTROL register page. /// They are involved in GPU configuration and control. pub(crate) mod gpu_control { @@ -974,6 +976,8 @@ pub(crate) mod mmu_as_control { register, // }; =20 + use pin_init::Zeroable; + /// Maximum number of hardware address space slots. /// The actual number of slots available is usually lower. pub(crate) const MAX_AS: usize =3D 16; @@ -1167,7 +1171,113 @@ fn from(val: MMU_MEMATTR_STAGE1) -> Self { pub(crate) MEMATTR_HI(u32)[MAX_AS, stride =3D STRIDE] @ 0x240c= { 31:0 value; } + } + + impl MEMATTR { + /// ARM MAIR Write-Allocate bit (bit 0 of inner/outer cache po= licy nibble). + /// + /// In the ARM Architecture Reference Manual, the MAIR encodin= g for Normal memory + /// uses the format `0bxxRW` where: + /// - `W` (bit 0) =3D Write-Allocate policy + /// - `R` (bit 1) =3D Read-Allocate policy + /// For example, `0b1111` =3D Write-Back with both Read and = Write allocation. + const ARM_MAIR_WRITE_ALLOCATE: u8 =3D 0x1; + /// ARM MAIR Read-Allocate bit (bit 1 of inner/outer cache pol= icy nibble). + const ARM_MAIR_READ_ALLOCATE: u8 =3D 0x2; + /// ARM MAIR Write-back bit (bit 2 of inner/outer cache policy= nibble). + const ARM_MAIR_WRITE_BACK: u8 =3D 0x4; + /// Mask for the inner cache policy nibble in MAIR attribute b= ytes. + const ARM_MAIR_INNER_MASK: u8 =3D 0x0f; + + fn encode_attribute( + alloc_w: bool, + alloc_r: bool, + alloc_sel: AllocPolicySelect, + coherency: Coherency, + memory_type: MemoryType, + ) -> MMU_MEMATTR_STAGE1 { + MMU_MEMATTR_STAGE1::zeroed() + .with_alloc_w(alloc_w) + .with_alloc_r(alloc_r) + .with_alloc_sel(alloc_sel) + .with_coherency(coherency) + .with_memory_type(memory_type) + } + + fn with_encoded_attribute(self, index: usize, attr: MMU_MEMATT= R_STAGE1) -> Self { + debug_assert!(index < 8); + + let shift =3D index * 8; + let mask =3D !(0xffu64 << shift); + let raw =3D (self.into_raw() & mask) | ((u64::from(attr.in= to_raw())) << shift); + + Self::from_raw(raw) + } + + /// Check if a MAIR attribute byte represents device memory. + /// + /// Device memory (memory-mapped I/O, registers) cannot be cac= hed and must + /// be mapped as GPU `NonCacheable`. + fn is_device_memory(mair_attr: u8) -> bool { + // In AArch64 MAIR, device memory has bits [1:0] of outer = nibble =3D 0. + let outer =3D mair_attr >> 4; + (outer & 0x3) =3D=3D 0 + } =20 + /// Check if normal memory is fully write-back cacheable. + /// + /// ARM MAIR has two cache policy levels (outer [7:4] and inne= r [3:0]). + /// For memory to be truly write-back, BOTH levels must have t= he write-back bit set. + /// If only one level is write-back, treat it as non-cacheable= for GPU purposes. + fn is_writeback_cacheable(mair_attr: u8) -> bool { + let outer =3D mair_attr >> 4; + let inner =3D mair_attr & Self::ARM_MAIR_INNER_MASK; + + (outer & Self::ARM_MAIR_WRITE_BACK) !=3D 0 && (inner & Sel= f::ARM_MAIR_WRITE_BACK) !=3D 0 + } + + // TODO: Add a `coherent` parameter like panthor's mair_to_mem= attr(). + // For now, assume a non-coherent system and always encode wri= te-back + // memory with MidgardInnerDomain coherency. + fn attribute_from_mair(mair_attr: u8) -> MMU_MEMATTR_STAGE1 { + // Device memory or non-writeback normal memory + if Self::is_device_memory(mair_attr) || !Self::is_writebac= k_cacheable(mair_attr) { + return Self::encode_attribute( + false, + false, + AllocPolicySelect::Alloc, + Coherency::MidgardInnerDomain, + MemoryType::NonCacheable, + ); + } + + // Write-back cacheable normal memory + let inner: u8 =3D mair_attr & Self::ARM_MAIR_INNER_MASK; + Self::encode_attribute( + (inner & Self::ARM_MAIR_WRITE_ALLOCATE) !=3D 0, + (inner & Self::ARM_MAIR_READ_ALLOCATE) !=3D 0, + AllocPolicySelect::Alloc, + Coherency::MidgardInnerDomain, + MemoryType::WriteBack, + ) + } + + /// Convert an AArch64 MAIR value into the GPU MEMATTR registe= r encoding. + /// + /// MAIR bytes map to GPU attributes as follows: + /// - device/write-through/non-cacheable =E2=86=92 GPU `NonCac= heable` + /// - write-back =E2=86=92 GPU `WriteBack` (preserving inner a= llocation hints) + pub(crate) fn from_mair(mair: u64) -> Self { + mair.to_le_bytes() + .into_iter() + .enumerate() + .fold(Self::zeroed(), |acc, (i, attr)| { + acc.with_encoded_attribute(i, Self::attribute_from= _mair(attr)) + }) + } + } + + register! { /// Lock region address for each address space. pub(crate) LOCKADDR(u64)[MAX_AS, stride =3D STRIDE] @ 0x2410 { /// Lock region size. diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs index 20b38120e20e..9f9f31ea02e3 100644 --- a/drivers/gpu/drm/tyr/tyr.rs +++ b/drivers/gpu/drm/tyr/tyr.rs @@ -11,6 +11,7 @@ mod file; mod gem; mod gpu; +mod mmu; mod regs; mod slot; =20 --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A7DB539DBC9; Mon, 13 Apr 2026 22:33:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119588; cv=pass; b=EcQQkpVUGmYh4bwVAv9fsw7zmsDDdnHZPU8JGHZ4rJGneEyNN9aANlHvY6CTMq58rLmGSCBY4+4mTfFJcSBv5c4+RYdiBeYy/rDaFOvlgF0DAY3KiAxRrVwBXVT2fyATLCdO9xvOEp8LM/NxFx4F7zMZI2wPLdmTe5TpUaF4p+Q= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119588; c=relaxed/simple; bh=3HRpO4ftJf0rsgKq+cpHMkhF7ZqB+a91sCt3EyyzWF0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WTvd4m7YvBKfGAn5Nh7uwY1iSefa155SwdrZdXUdl3ia4+DByedns+dUJ/pDFGBJOG1VCdRblXKMTaSWYD+UbjkONU+oocyWYyE+3zSCCHEQWS/swKdCVu+TGggXfLTd0mtyw2PP2r6YlXWgk9mamR3hau08M/IijzlJre4SfiY= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=OHXKor6n; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="OHXKor6n" ARC-Seal: i=1; a=rsa-sha256; t=1776119525; cv=none; d=zohomail.com; s=zohoarc; b=Nf3fL1qXM1M/XC7cc3GBGlXQrSH27Cs2avhwmd+1W6KLY9bs+1213chszILNkjVmBbJEtZx93e+DB4+kwe4wMBc5k+zOQghGAbg2p5sC4kCHvrMOVteIYuBzWI2eP3s2oHPgWeGAy2xV5A6F221Tlu68ujB6i5LkS8XtiZVtj8E= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119525; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=FoScaDMABYPPzC+5ryVqgEQ0HmSdTaPsLPP9PeNEt9s=; b=IUSlSEl8TVVuTbx7/QNc7sNk4dZC+6RiJK/3lj5wipUhQ7WkKVt3qns1gYNMsXyAAMHLJxoD7ylDNT+RYHmANi2sri2/4f1Fb28m+zldU5u0SpJr5BgE6enZUKf0P5tReJYTdq7FUUW61JPL+9IhWM42Zo7QA4/ijQgZjXX3pVc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119525; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=FoScaDMABYPPzC+5ryVqgEQ0HmSdTaPsLPP9PeNEt9s=; b=OHXKor6nwjgSrtkO5jaL1/t67x7hFLp4sllV3A8PLs3csxvvrX091oltyNbCdTDf wtCJEFDpVEz6bG1e3S3AdrTs6AeyG1TNKZpVKa25wfkBFZK80NB4ZF9RDAMcg3YNb+T 8sSMJtiVACfISsl60lmRj+dpvSE5eHDUKEjRMiTM= Received: by mx.zohomail.com with SMTPS id 1776119523539239.07694160528308; Mon, 13 Apr 2026 15:32:03 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:45 -0700 Subject: [PATCH v3 10/13] drm/tyr: add GPU virtual memory module Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-10-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=30136; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=DxyGc+41WKh4sqUW0arCjXoRlHozzz3trCSLv1z5bjY=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3865ekOdcbPmWb1/75HrJwkdhcsuF/r3I2i3kfuoxy 6NZFXaBHaUsDGJcDLJiiixn7Y16xKveG+nO/98MM4eVCWQIAxenAEzEy4jhv0fY8ej37Tcv/rwr /r6G4+DdRVfM70T1Hqg/ERa5UFkyfQIjw4tWhU9z1hoy718S3Px/voN/2dOtAlEXUye6l+ic/1u 1lwcA X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB From: Boris Brezillon Add GPU virtual address space management using the DRM GPUVM framework. Each virtual memory (VM) space is backed by ARM64 LPAE Stage 1 page tables and can be mapped into hardware address space (AS) slots for GPU execution. The implementation provides memory isolation and virtual address allocation. VMs support mapping GEM buffer objects with configurable protection flags (readonly, noexec, uncached) and handle both 4KB and 2MB page sizes. A new_dummy_object() helper is provided to create a dummy GEM object for use as a GPUVM root. The vm module integrates with the MMU for address space activation and provides map/unmap/remap operations with page table synchronization. Signed-off-by: Boris Brezillon Co-developed-by: Daniel Almeida Signed-off-by: Daniel Almeida Co-developed-by: Deborah Brouwer Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/gem.rs | 22 +- drivers/gpu/drm/tyr/tyr.rs | 1 + drivers/gpu/drm/tyr/vm.rs | 806 +++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 828 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs index c6d4d6f9bae3..111acf33993f 100644 --- a/drivers/gpu/drm/tyr/gem.rs +++ b/drivers/gpu/drm/tyr/gem.rs @@ -7,9 +7,11 @@ use kernel::{ drm::{ gem, + gem::shmem, DeviceContext, // }, - prelude::*, // + prelude::*, + sync::aref::ARef, // }; =20 use crate::driver::{ @@ -41,3 +43,21 @@ fn new( try_pin_init!(Self { flags: args.flags }) } } + +/// Type alias for Tyr GEM buffer objects. +pub(crate) type Bo =3D gem::shmem::Object; + +/// Creates a dummy GEM object to serve as the root of a GPUVM. +pub(crate) fn new_dummy_object(ddev: &TyrDrmDevice) -> Result> { + let bo =3D gem::shmem::Object::::new( + ddev, + 4096, + shmem::ObjectConfig { + map_wc: true, + parent_resv_obj: None, + }, + BoCreateArgs { flags: 0 }, + )?; + + Ok(bo) +} diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs index 9f9f31ea02e3..b3244670dd79 100644 --- a/drivers/gpu/drm/tyr/tyr.rs +++ b/drivers/gpu/drm/tyr/tyr.rs @@ -14,6 +14,7 @@ mod mmu; mod regs; mod slot; +mod vm; =20 kernel::module_platform_driver! { type: TyrPlatformDriverData, diff --git a/drivers/gpu/drm/tyr/vm.rs b/drivers/gpu/drm/tyr/vm.rs new file mode 100644 index 000000000000..c19300d76194 --- /dev/null +++ b/drivers/gpu/drm/tyr/vm.rs @@ -0,0 +1,806 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +//! GPU virtual memory management using the DRM GPUVM framework. +//! +//! This module manages GPU virtual address spaces, providing memory isola= tion and +//! the illusion of owning the entire virtual address (VA) range, similar = to CPU virtual memory. +//! Each virtual memory (VM) area is backed by ARM64 LPAE Stage 1 page tab= les and can be +//! mapped into hardware address space (AS) slots for GPU execution. +#![allow(dead_code)] + +use core::ops::Range; + +use kernel::{ + c_str, + device::{ + Bound, + Device, // + }, + drm::{ + gpuvm::{ + DriverGpuVm, + GpuVaAlloc, + GpuVm, + GpuVmBo, + OpMap, + OpMapRequest, + OpMapped, + OpRemap, + OpRemapped, + OpUnmap, + OpUnmapped, + UniqueRefGpuVm, // + }, + DeviceContext, // + }, + impl_flags, + iommu::pgtable::{ + prot, + IoPageTable, + ARM64LPAES1, // + }, + new_mutex, + platform, + prelude::*, + sizes::{ + SZ_1G, + SZ_2M, + SZ_4K, // + }, + sync::{ + aref::ARef, + Arc, + ArcBorrow, + Mutex, // + }, + uapi, // +}; + +use crate::{ + driver::{ + TyrDrmDevice, + TyrDrmDriver, // + }, + gem, + gem::Bo, + gpu::GpuInfo, + mmu::{ + address_space::VmAsData, + Mmu, // + }, + regs::gpu_control::MMU_FEATURES, +}; + +impl_flags!( + /// Flags controlling virtual memory mapping behavior. + /// + /// These flags control access permissions and caching behavior for GP= U virtual + /// memory mappings. + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub(crate) struct VmMapFlags(u32); + + /// Individual flags that can be combined in [`VmMapFlags`]. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub(crate) enum VmFlag { + /// Map as read-only. + Readonly =3D uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BIN= D_OP_MAP_READONLY as u32, + /// Map as non-executable. + Noexec =3D uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BIND_= OP_MAP_NOEXEC as u32, + /// Map as uncached. + Uncached =3D uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BIN= D_OP_MAP_UNCACHED as u32, + } +); + +impl VmMapFlags { + /// Convert the flags to `pgtable::prot`. + fn to_prot(self) -> u32 { + let mut prot =3D 0; + + if self.contains(VmFlag::Readonly) { + prot |=3D prot::READ; + } else { + prot |=3D prot::READ | prot::WRITE; + } + + if self.contains(VmFlag::Noexec) { + prot |=3D prot::NOEXEC; + } + + if !self.contains(VmFlag::Uncached) { + prot |=3D prot::CACHE; + } + + prot + } +} + +impl core::fmt::Display for VmMapFlags { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut first =3D true; + + if self.contains(VmFlag::Readonly) { + write!(f, "READONLY")?; + first =3D false; + } + if self.contains(VmFlag::Noexec) { + if !first { + write!(f, " | ")?; + } + write!(f, "NOEXEC")?; + first =3D false; + } + + if self.contains(VmFlag::Uncached) { + if !first { + write!(f, " | ")?; + } + write!(f, "UNCACHED")?; + } + + Ok(()) + } +} + +impl TryFrom for VmMapFlags { + type Error =3D Error; + + fn try_from(value: u32) -> core::result::Result { + let valid =3D (kernel::uapi::drm_panthor_vm_bind_op_flags_DRM_PANT= HOR_VM_BIND_OP_MAP_READONLY + | kernel::uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BI= ND_OP_MAP_NOEXEC + | kernel::uapi::drm_panthor_vm_bind_op_flags_DRM_PANTHOR_VM_BI= ND_OP_MAP_UNCACHED) + as u32; + + if value & !valid !=3D 0 { + pr_err!("Invalid VM map flags: {:#x}\n", value); + return Err(EINVAL); + } + Ok(Self(value)) + } +} + +/// Arguments for a virtual memory map operation. +struct VmMapArgs { + /// Access permissions and caching behavior for the mapping. + flags: VmMapFlags, + /// GEM buffer object registered with the GPUVM framework. + vm_bo: ARef>, + /// Offset in bytes from the start of the buffer object. + bo_offset: u64, +} + +/// Type of virtual memory operation. +enum VmOpType { + /// Map a GEM buffer object into the virtual address space. + Map(VmMapArgs), + /// Unmap a region from the virtual address space. + Unmap, +} + +/// Preallocated resources needed to execute a VM operation. +/// +/// VM operations may require allocating new GPUVA objects to track mappin= gs. +/// To avoid allocation failures during the operation, preallocate the +/// maximum number of GPUVAs that might be needed. +struct VmOpResources { + /// Preallocated GPUVA objects for remap operations. + /// + /// Partial unmap requests or map requests overlapping existing mappin= gs + /// will trigger a remap call, which needs to register up to three VA + /// objects (one for the new mapping, and two for the previous and next + /// mappings). + preallocated_gpuvas: [Option>; 3], +} + +/// Request to execute a virtual memory operation. +struct VmOpRequest { + /// Request type. + op_type: VmOpType, + + /// Region of the virtual address space covered by this request. + region: Range, +} + +/// Arguments for a page table map operation. +struct PtMapArgs { + /// Memory protection flags describing allowed accesses for this mappi= ng. + /// + /// This is directly derived from [`VmMapFlags`] via [`VmMapFlags::to_= prot`]. + prot: u32, +} + +/// Type of page table operation. +enum PtOpType { + /// Map pages into the page table. + Map(PtMapArgs), + /// Unmap pages from the page table. + Unmap, +} + +/// Context for updating the GPU page table. +/// +/// This context is created when beginning a page table update operation a= nd +/// automatically flushes changes when dropped. It ensures that the +/// Memory Management Unit (MMU) state is properly managed and Translation +/// Lookaside Buffer (TLB) entries are flushed. +pub(crate) struct PtUpdateContext<'ctx> { + /// Device used for DMA-mapping GEM shmem SG tables. + dev: &'ctx Device, + + /// Page table. + pt: &'ctx IoPageTable, + + /// MMU manager. + mmu: &'ctx Mmu, + + /// Reference to the address space data to pass to the MMU functions. + as_data: &'ctx VmAsData, + + /// Region of the virtual address space covered by this request. + region: Range, + + /// Operation type. + op_type: PtOpType, + + /// Preallocated resources that can be used when executing the request. + resources: &'ctx mut VmOpResources, +} + +impl<'ctx> PtUpdateContext<'ctx> { + /// Creates a new page table update context. + /// + /// This prepares the MMU for a page table update. + /// The context will automatically flush the TLB and + /// complete the update when dropped. + fn new( + dev: &'ctx Device, + pt: &'ctx IoPageTable, + mmu: &'ctx Mmu, + as_data: &'ctx VmAsData, + region: Range, + op_type: PtOpType, + resources: &'ctx mut VmOpResources, + ) -> Result> { + mmu.start_vm_update(as_data, ®ion)?; + + Ok(Self { + dev, + pt, + mmu, + as_data, + region, + op_type, + resources, + }) + } + + /// Finds one of our pre-allocated VAs. + /// + /// It is a logic error to call this more than three times for a given + /// PtUpdateContext. + fn preallocated_gpuva(&mut self) -> Result> { + self.resources + .preallocated_gpuvas + .iter_mut() + .find_map(|f| f.take()) + .ok_or(EINVAL) + } +} + +impl Drop for PtUpdateContext<'_> { + fn drop(&mut self) { + if let Err(e) =3D self.mmu.end_vm_update(self.as_data) { + pr_err!("Failed to end VM update {:?}\n", e); + } + + if let Err(e) =3D self.mmu.flush_vm(self.as_data) { + pr_err!("Failed to flush VM {:?}\n", e); + } + } +} + +/// Driver implementation for the GPUVM framework. +/// +/// Implements [`DriverGpuVm`] to provide VM operation callbacks (map, unm= ap, remap) +/// and associated types for buffer objects, virtual addresses, and contex= ts. +pub(crate) struct GpuVmData {} + +/// GPU virtual address space. +/// +/// Each VM can be mapped into a hardware address space slot. +#[pin_data] +pub(crate) struct Vm { + /// Data referenced by an AS when the VM is active + as_data: Arc, + /// MMU manager. + mmu: Arc, + /// Platform device reference (needed to access the page table via dev= res). + pdev: ARef, + /// DRM GPUVM core for managing virtual address space. + #[pin] + gpuvm_unique: Mutex>, + /// Non-core part of the GPUVM. Can be used for stuff that doesn't mod= ify the + /// internal mapping tree, like GpuVm::obtain() + gpuvm: ARef>, + /// VA range for this VM. + va_range: Range, +} + +impl Vm { + /// Creates a new GPU virtual address space. + /// + /// The VM is initialized with a page table configured according to th= e GPU's + /// address translation capabilities and registered with the GPUVM fra= mework. + pub(crate) fn new( + pdev: &platform::Device, + ddev: &TyrDrmDevice, + mmu: ArcBorrow<'_, Mmu>, + gpu_info: &GpuInfo, + ) -> Result> { + let mmu_features =3D MMU_FEATURES::from_raw(gpu_info.mmu_features); + let va_bits =3D mmu_features.va_bits().get(); + let pa_bits =3D mmu_features.pa_bits().get(); + + let range =3D 0..(1u64 << va_bits); + let reserve_range =3D 0..0u64; + + // dummy_obj is used to initialize the GPUVM tree. + let dummy_obj =3D gem::new_dummy_object(ddev).inspect_err(|e| { + pr_err!("Failed to create dummy GEM object: {:?}\n", e); + })?; + + let gpuvm_unique =3D kernel::drm::gpuvm::GpuVm::new::( + c_str!("Tyr::GpuVm"), + ddev, + &*dummy_obj, + range.clone(), + reserve_range, + GpuVmData {}, + ) + .inspect_err(|e| { + pr_err!("Failed to create GpuVm: {:?}\n", e); + })?; + let gpuvm =3D ARef::from(&*gpuvm_unique); + + let as_data =3D Arc::pin_init(VmAsData::new(&mmu, pdev, va_bits, p= a_bits), GFP_KERNEL)?; + + let vm =3D Arc::pin_init( + pin_init!(Self{ + as_data, + pdev: pdev.into(), + mmu: mmu.into(), + gpuvm, + gpuvm_unique <- new_mutex!(gpuvm_unique), + va_range: range, + }), + GFP_KERNEL, + )?; + + Ok(vm) + } + + /// Activate the VM in a hardware address space slot. + pub(crate) fn activate(&self) -> Result { + self.mmu + .activate_vm(self.as_data.as_arc_borrow()) + .inspect_err(|e| { + pr_err!("Failed to activate VM: {:?}\n", e); + }) + } + + /// Deactivate the VM by evicting it from its address space slot. + fn deactivate(&self) -> Result { + self.mmu.deactivate_vm(&self.as_data).inspect_err(|e| { + pr_err!("Failed to deactivate VM: {:?}\n", e); + }) + } + + /// Kills the VM by deactivating it and unmapping all regions. + pub(crate) fn kill(&self) { + // TODO: Turn the VM into a state where it can't be used. + let _ =3D self.deactivate().inspect_err(|e| { + pr_err!("Failed to deactivate VM: {:?}\n", e); + }); + let _ =3D self + .unmap_range(self.va_range.start, self.va_range.end - self.va_= range.start) + .inspect_err(|e| { + pr_err!("Failed to unmap range during deactivate: {:?}\n",= e); + }); + } + + /// Executes a virtual memory operation. + /// + /// This handles both map and unmap operations by coordinating between= the + /// GPUVM framework and the hardware page table. + fn exec_op( + &self, + gpuvm_unique: &mut UniqueRefGpuVm, + req: VmOpRequest, + resources: &mut VmOpResources, + ) -> Result { + // SAFETY: pdev is a bound device. + let dev =3D unsafe { self.pdev.as_ref().as_bound() }; + + let pt =3D self.as_data.page_table.access(dev).inspect_err(|e| { + pr_err!("Failed to access page table while mapping pages: {:?}= \n", e); + })?; + + match req.op_type { + VmOpType::Map(args) =3D> { + let mut pt_upd =3D PtUpdateContext::new( + dev, + pt, + &self.mmu, + &self.as_data, + req.region, + PtOpType::Map(PtMapArgs { + prot: args.flags.to_prot(), + }), + resources, + )?; + + gpuvm_unique.sm_map(OpMapRequest { + addr: pt_upd.region.start, + range: pt_upd.region.end - pt_upd.region.start, + gem_offset: args.bo_offset, + vm_bo: &args.vm_bo, + context: &mut pt_upd, + }) + //PtUpdateContext drops here flushing the page table + } + VmOpType::Unmap =3D> { + let mut pt_upd =3D PtUpdateContext::new( + dev, + pt, + &self.mmu, + &self.as_data, + req.region, + PtOpType::Unmap, + resources, + )?; + + gpuvm_unique.sm_unmap( + pt_upd.region.start, + pt_upd.region.end - pt_upd.region.start, + &mut pt_upd, + ) + //PtUpdateContext drops here flushing the page table + } + } + } + + /// Maps a GEM buffer object range into the VM at the specified virtua= l address. + /// + /// This creates a mapping from GPU virtual address `va` to the physic= al pages + /// backing the GEM object, starting at `bo_offset` bytes into the obj= ect and + /// spanning `size` bytes. The mapping respects the access permissions= and + /// caching behavior specified in `flags`. + pub(crate) fn map_bo_range( + &self, + bo: &Bo, + bo_offset: u64, + size: u64, + va: u64, + flags: VmMapFlags, + ) -> Result { + let req =3D VmOpRequest { + op_type: VmOpType::Map(VmMapArgs { + vm_bo: self.gpuvm.obtain(bo, ())?, + flags, + bo_offset, + }), + region: va..(va + size), + }; + let mut resources =3D VmOpResources { + preallocated_gpuvas: [ + Some(GpuVaAlloc::::new(GFP_KERNEL)?), + Some(GpuVaAlloc::::new(GFP_KERNEL)?), + Some(GpuVaAlloc::::new(GFP_KERNEL)?), + ], + }; + let mut gpuvm_unique =3D self.gpuvm_unique.lock(); + + self.exec_op(gpuvm_unique.as_mut().get_mut(), req, &mut resources)= ?; + + // We flush the defer cleanup list now. Things will be different in + // the asynchronous VM_BIND path, where we want the cleanup to + // happen outside the DMA signalling path. + self.gpuvm.deferred_cleanup(); + Ok(()) + } + + /// Unmaps a virtual address range from the VM. + /// + /// This removes any existing mappings in the specified range, freeing= the + /// virtual address space for reuse. + pub(crate) fn unmap_range(&self, va: u64, size: u64) -> Result { + let req =3D VmOpRequest { + op_type: VmOpType::Unmap, + region: va..(va + size), + }; + let mut resources =3D VmOpResources { + preallocated_gpuvas: [ + Some(GpuVaAlloc::::new(GFP_KERNEL)?), + Some(GpuVaAlloc::::new(GFP_KERNEL)?), + None, + ], + }; + let mut gpuvm_unique =3D self.gpuvm_unique.lock(); + + self.exec_op(gpuvm_unique.as_mut().get_mut(), req, &mut resources)= ?; + + // We flush the defer cleanup list now. Things will be different in + // the asynchronous VM_BIND path, where we want the cleanup to + // happen outside the DMA signalling path. + self.gpuvm.deferred_cleanup(); + Ok(()) + } +} + +impl DriverGpuVm for GpuVmData { + type Driver =3D TyrDrmDriver; + type Object =3D Bo; + type VmBoData =3D (); + type VaData =3D (); + type SmContext<'ctx> =3D PtUpdateContext<'ctx>; + + /// Indicates that a new mapping should be created. + fn sm_step_map<'op>( + &mut self, + op: OpMap<'op, Self>, + context: &mut Self::SmContext<'_>, + ) -> Result, Error> { + let start_iova =3D op.addr(); + let mut iova =3D start_iova; + let mut bytes_left_to_map =3D op.length(); + let mut gem_offset =3D op.gem_offset(); + let sgt =3D op.obj().sg_table(context.dev).inspect_err(|e| { + pr_err!("Failed to get sg_table: {:?}\n", e); + })?; + let prot =3D match &context.op_type { + PtOpType::Map(args) =3D> args.prot, + _ =3D> { + return Err(EINVAL); + } + }; + + for sgt_entry in sgt.iter() { + let mut paddr =3D sgt_entry.dma_address(); + let mut sgt_entry_length: u64 =3D sgt_entry.dma_len(); + + if bytes_left_to_map =3D=3D 0 { + break; + } + + if gem_offset > 0 { + // Skip the entire SGT entry if the gem_offset exceeds its= length + let skip =3D sgt_entry_length.min(gem_offset); + paddr +=3D skip; + sgt_entry_length -=3D skip; + gem_offset -=3D skip; + } + + if sgt_entry_length =3D=3D 0 { + continue; + } + + if gem_offset !=3D 0 { + pr_err!("Invalid gem_offset {} in page table mapping.\n", = gem_offset); + return Err(EINVAL); + } + let len =3D sgt_entry_length.min(bytes_left_to_map); + + let segment_mapped =3D match pt_map(context.pt, iova, paddr, l= en, prot) { + Ok(segment_mapped) =3D> segment_mapped, + Err(e) =3D> { + // clean up any successful mappings from previous SGT = entries. + let total_mapped =3D iova - start_iova; + if total_mapped > 0 { + pt_unmap(context.pt, start_iova..(start_iova + tot= al_mapped)).ok(); + } + return Err(e); + } + }; + + // Since there could be a partial mapping, only advance by the= actual amount mapped + bytes_left_to_map -=3D segment_mapped; + iova +=3D segment_mapped; + } + + let gpuva =3D context.preallocated_gpuva()?; + let op =3D op.insert(gpuva, pin_init::init_zeroed()); + + Ok(op) + } + + /// Indicates that an existing mapping should be removed. + fn sm_step_unmap<'op>( + &mut self, + op: OpUnmap<'op, Self>, + context: &mut Self::SmContext<'_>, + ) -> Result, Error> { + let start_iova =3D op.va().addr(); + let length =3D op.va().length(); + + let region =3D start_iova..(start_iova + length); + pt_unmap(context.pt, region.clone()).inspect_err(|e| { + pr_err!( + "Failed to unmap region {:#x}..{:#x}: {:?}\n", + region.start, + region.end, + e + ); + })?; + + let (op_unmapped, _va_removed) =3D op.remove(); + + Ok(op_unmapped) + } + + /// Indicates that an existing mapping should be split up. + fn sm_step_remap<'op>( + &mut self, + op: OpRemap<'op, Self>, + context: &mut Self::SmContext<'_>, + ) -> Result, Error> { + let unmap_start =3D if let Some(prev) =3D op.prev() { + prev.addr() + prev.length() + } else { + op.va_to_unmap().addr() + }; + + let unmap_end =3D if let Some(next) =3D op.next() { + next.addr() + } else { + op.va_to_unmap().addr() + op.va_to_unmap().length() + }; + + let unmap_length =3D unmap_end - unmap_start; + + if unmap_length > 0 { + let region =3D unmap_start..(unmap_start + unmap_length); + pt_unmap(context.pt, region.clone()).inspect_err(|e| { + pr_err!( + "Failed to unmap remap region {:#x}..{:#x}: {:?}\n", + region.start, + region.end, + e + ); + })?; + } + + let prev_va =3D context.preallocated_gpuva()?; + let next_va =3D context.preallocated_gpuva()?; + + let (op_remapped, _remap_ret) =3D op.remap( + [prev_va, next_va], + pin_init::init_zeroed(), + pin_init::init_zeroed(), + ); + + Ok(op_remapped) + } +} + +/// This function selects the largest supported block size (currently 4KB = or 2MB) +/// that can be used for a mapping at the given address and size, respecti= ng alignment constraints. +/// +/// We can map multiple pages at once but we can't exceed the size of the +// table entry itself. So, if mapping 4KB pages, figure out how many pages +// can be mapped before we hit the 2MB boundary. Or, if mapping 2MB pages, +// figure out how many pages can be mapped before hitting the 1GB boundary +// Returns the page size (4KB or 2MB) and the number of pages that can be = mapped at that size. +fn get_pgsize(addr: u64, size: u64) -> (u64, u64) { + // Get the distance to the next boundary of 2MB block + let blk_offset_2m =3D addr.wrapping_neg() % (SZ_2M as u64); + + // Use 4K blocks if the address is not 2MB aligned, or we have less th= an 2MB to map + if blk_offset_2m !=3D 0 || size < SZ_2M as u64 { + let pgcount =3D if blk_offset_2m =3D=3D 0 { + size / SZ_4K as u64 + } else { + blk_offset_2m.min(size) / SZ_4K as u64 + }; + return (SZ_4K as u64, pgcount); + } + + let blk_offset_1g =3D addr.wrapping_neg() % (SZ_1G as u64); + let blk_offset =3D if blk_offset_1g =3D=3D 0 { + SZ_1G as u64 + } else { + blk_offset_1g + }; + let pgcount =3D blk_offset.min(size) / SZ_2M as u64; + + (SZ_2M as u64, pgcount) +} + +/// Maps a physical address range into the page table at the specified vir= tual address. +/// +/// This function maps `len` bytes of physical memory starting at `paddr` = to the +/// virtual address `iova`, using the protection flags specified in `prot`= . It +/// automatically selects optimal page sizes to minimize page table overhe= ad. +/// +/// If the mapping fails partway through, all successfully mapped pages are +/// unmapped before returning an error. +/// +/// Returns the number of bytes successfully mapped. +fn pt_map( + pt: &IoPageTable, + iova: u64, + paddr: u64, + len: u64, + prot: u32, +) -> Result { + let mut segment_mapped =3D 0u64; + while segment_mapped < len { + let remaining =3D len - segment_mapped; + let curr_iova =3D iova + segment_mapped; + let curr_paddr =3D paddr + segment_mapped; + + let (pgsize, pgcount) =3D get_pgsize(curr_iova | curr_paddr, remai= ning); + + // SAFETY: Exclusive access to the page table is ensured because + // the pt reference comes from PtUpdateContext, which is created + // during a VM update operation, ensuring the driver does not conc= urrently + // modify the page table. + let (mapped, result) =3D unsafe { + pt.map_pages( + curr_iova as usize, + (curr_paddr as usize).try_into().unwrap(), + pgsize as usize, + pgcount as usize, + prot, + GFP_KERNEL, + ) + }; + + if let Err(e) =3D result { + pr_err!("pt.map_pages failed at iova {:#x}: {:?}\n", curr_iova= , e); + if segment_mapped > 0 { + pt_unmap(pt, iova..(iova + segment_mapped)).ok(); + } + return Err(e); + } + + if mapped =3D=3D 0 { + pr_err!("Failed to map any pages at iova {:#x}\n", curr_iova); + if segment_mapped > 0 { + pt_unmap(pt, iova..(iova + segment_mapped)).ok(); + } + return Err(ENOMEM); + } + + segment_mapped +=3D mapped as u64; + } + + Ok(segment_mapped) +} + +/// Unmaps a virtual address range from the page table. +/// +/// This function removes all page table entries in the specified range, +/// automatically handling different page sizes that may be present. +fn pt_unmap(pt: &IoPageTable, range: Range) -> Result { + let mut iova =3D range.start; + let mut bytes_left_to_unmap =3D range.end - range.start; + + while bytes_left_to_unmap > 0 { + let (pgsize, pgcount) =3D get_pgsize(iova, bytes_left_to_unmap); + + // SAFETY: Exclusive access to the page table is ensured because + // the pt reference comes from PtUpdateContext, which was + // created while holding &mut Vm, preventing any other access to t= he + // page table for the duration of this operation. + let unmapped =3D unsafe { pt.unmap_pages(iova as usize, pgsize as = usize, pgcount as usize) }; + + if unmapped =3D=3D 0 { + pr_err!("Failed to unmap any bytes at iova {:#x}\n", iova); + return Err(EINVAL); + } + + bytes_left_to_unmap -=3D unmapped as u64; + iova +=3D unmapped as u64; + } + + Ok(()) +} --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D9D1521CFEF; Mon, 13 Apr 2026 22:32:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119552; cv=pass; b=DQuZvxc5ooQJIAUboKtXiXsO8zN/1EZpT+EBZi+FoiO3m9VHfFYxsyHuoi8Bsdi7pIMmCMkP8qjl8TRrFZzXR9uqRu1uYV8xGqIz7BEXv0hvMeXZN0Cupp7fO7iEo8BJ1pkEiT07NbIwK894nUgPquxIQ04yPxvDa1Tb786g1sA= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119552; c=relaxed/simple; bh=rurOKa1tcEIzVJaZcuiDfiEdNQDEfvlVGxqqYMOCT8g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OMMKCe1XxbvoHyoKKyVNMuaQnH4IhH0lel5ukegpnYmFaCSDzM6yR3nxGm7U/kfWdqTOhLIZo111hWYkO7yeBYn2Ho8d8Z+6oLviTVpvwdUdPu7qGwFFXVn8z3NFMROvY8S3k1oyhcAf/dVcrlqLCcq38XOADTLJ7ebWUJghcrE= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=IZlyODWH; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="IZlyODWH" ARC-Seal: i=1; a=rsa-sha256; t=1776119525; cv=none; d=zohomail.com; s=zohoarc; b=nS8JpQ5lktb/ycb7T3y7W1J8eIpPonhLF/NAO1zZbZuWK27fyvbZXtbDyllHsWEbOTUqSOplB2JJs483res2MrZIFrBP8T/J4jtJ4QsSJGT72ZlCtQ7zM89xHpw08Lh9lRxucxVoyL/5IOmf0IBMwVmoZaNC4L+J0nbuTwLCQfQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119525; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=RUqLI28yo3oTGrENvimVHEDD6lDvBVJ/RxaKotmgahE=; b=RIZqimn3jbN73CHjiwmTipVPcYsnSUL8McKG3AQvOL69SD/0k3oXBSCUr9ELohvHh3H4nySEtF+g9Qj654w6cLboVhAxvbf4nOnpNKkLmduu0haltvlS16Yo6QXy/jUsb540kpcrWa4DCwrgzHhGconuJjeqJUbFurjaQkNmZyU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119525; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=RUqLI28yo3oTGrENvimVHEDD6lDvBVJ/RxaKotmgahE=; b=IZlyODWHhFUsOjhGjdF1yiTa2khkp/X04tPOfXwo2OOVZwh0rZogWgJrsDiVExey ZV91GtW6cVClfaABiiCSCK61xU6S19nRLda7eqrGLU1icj6VfGDCR0nyRhCFUZv0oAP GJmnWnnfT4qXVQw5hHpa92o6hLFX4lozJ4UuNqb8= Received: by mx.zohomail.com with SMTPS id 1776119524708844.0388974740306; Mon, 13 Apr 2026 15:32:04 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:46 -0700 Subject: [PATCH v3 11/13] drm/tyr: add a kernel buffer object Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-11-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=5962; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=rurOKa1tcEIzVJaZcuiDfiEdNQDEfvlVGxqqYMOCT8g=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3867OTz+/3bqa4110tPbbkgoBr/bZpc5vHv53TD0yr 3JFjMD1jlIWBjEuBlkxRZaz9kY94lXvjXTn/2+GmcPKBDKEgYtTACZS+ouRoUl71/8TJxab7fvi u0hZ2r/gq+SUzwXlKzw27pzy92lz1kKG36xno7+6MinK6gisX5HlHFAT7nje/jVbVCD7Ltd9m0N dmAE= X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB Introduce a buffer object type (KernelBo) for internal driver allocations that are managed by the kernel rather than userspace. KernelBo wraps a GEM shmem object and automatically handles GPU virtual address space mapping during creation and unmapping on drop. This provides a safe and convenient way for the driver to both allocate and clean up internal buffers for kernel-managed resources. Co-developed-by: Boris Brezillon Signed-off-by: Boris Brezillon Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/gem.rs | 124 +++++++++++++++++++++++++++++++++++++++++= +--- 1 file changed, 117 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs index 111acf33993f..d032a8ae543f 100644 --- a/drivers/gpu/drm/tyr/gem.rs +++ b/drivers/gpu/drm/tyr/gem.rs @@ -4,6 +4,8 @@ //! This module provides buffer object (BO) management functionality using //! DRM's GEM subsystem with shmem backing. =20 +use core::ops::Range; + use kernel::{ drm::{ gem, @@ -11,23 +13,41 @@ DeviceContext, // }, prelude::*, - sync::aref::ARef, // + sync::{ + aref::ARef, + Arc, + ArcBorrow, // + }, }; =20 -use crate::driver::{ - TyrDrmDevice, - TyrDrmDriver, // +use crate::{ + driver::{ + TyrDrmDevice, + TyrDrmDriver, // + }, + vm::{ + Vm, + VmMapFlags, // + }, }; =20 -/// Tyr's DriverObject type for GEM objects. +/// Driver-specific data for Tyr GEM buffer objects. +/// +/// This structure contains Tyr-specific metadata associated with each GEM= object. +/// It implements [`gem::DriverObject`] to provide driver-specific behavio= r for +/// buffer object creation and management. #[pin_data] pub(crate) struct BoData { + /// Buffer object creation flags (currently unused). flags: u32, } =20 -/// Provides a way to pass arguments when creating BoData -/// as required by the gem::DriverObject trait. +/// Arguments for creating a [`BoData`] instance. +/// +/// This structure is used to pass creation parameters when instantiating +/// a new buffer object, as required by the [`gem::DriverObject`] trait. pub(crate) struct BoCreateArgs { + /// Buffer object creation flags (currently unused). flags: u32, } =20 @@ -35,6 +55,12 @@ impl gem::DriverObject for BoData { type Driver =3D TyrDrmDriver; type Args =3D BoCreateArgs; =20 + /// Constructs a new [`BoData`] instance for a GEM object. + /// + /// This function is called by the GEM subsystem when creating a new b= uffer + /// object. It initializes the driver-specific data with the provided = flags. + /// The device and size parameters are currently unused but required b= y the + /// [`gem::DriverObject`] trait. fn new( _dev: &TyrDrmDevice, _size: usize, @@ -61,3 +87,87 @@ pub(crate) fn new_dummy_object(ddev:= &TyrDrmDevice) -> =20 Ok(bo) } + +/// VA allocation strategy for kernel buffer objects. +/// +/// Specifies how the GPU virtual address should be determined when creati= ng +/// a [`KernelBo`]. An automatic VA allocation strategy will be added in t= he future. +pub(crate) enum KernelBoVaAlloc { + /// Explicit VA address specified by the caller. + #[expect(dead_code)] + Explicit(u64), +} + +/// A kernel-owned buffer object with automatic GPU virtual address mappin= g. +/// +/// This structure represents a buffer object that is created and managed = entirely +/// by the kernel driver, as opposed to userspace-created GEM objects. It = combines +/// a GEM object with automatic GPU virtual address (VA) space mapping and= cleanup. +/// +/// When dropped, the buffer is automatically unmapped from the GPU VA spa= ce. +pub(crate) struct KernelBo { + /// The underlying GEM buffer object. + #[expect(dead_code)] + pub(crate) bo: ARef, + /// The GPU VM this buffer is mapped into. + vm: Arc, + /// The GPU VA range occupied by this buffer. + va_range: Range, +} + +impl KernelBo { + /// Creates a new kernel-owned buffer object and maps it into GPU VA s= pace. + /// + /// This function allocates a new shmem-backed GEM object and immediat= ely maps + /// it into the specified GPU virtual memory space. The mapping is aut= omatically + /// cleaned up when the [`KernelBo`] is dropped. + #[expect(dead_code)] + pub(crate) fn new( + ddev: &TyrDrmDevice, + vm: ArcBorrow<'_, Vm>, + size: u64, + va_alloc: KernelBoVaAlloc, + flags: VmMapFlags, + ) -> Result { + if size =3D=3D 0 { + pr_err!("Cannot create KernelBo with size 0\n"); + return Err(EINVAL); + } + + let KernelBoVaAlloc::Explicit(va) =3D va_alloc; + + let bo =3D gem::shmem::Object::::new( + ddev, + size as usize, + shmem::ObjectConfig { + map_wc: true, + parent_resv_obj: None, + }, + BoCreateArgs { flags: 0 }, + )?; + + vm.map_bo_range(&bo, 0, size, va, flags)?; + + Ok(KernelBo { + bo, + vm: vm.into(), + va_range: va..(va + size), + }) + } +} + +impl Drop for KernelBo { + fn drop(&mut self) { + let va =3D self.va_range.start; + let size =3D self.va_range.end - self.va_range.start; + + if let Err(e) =3D self.vm.unmap_range(va, size) { + pr_err!( + "Failed to unmap KernelBo range {:#x}..{:#x}: {:?}\n", + self.va_range.start, + self.va_range.end, + e + ); + } + } +} --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A87339D6E5; Mon, 13 Apr 2026 22:32:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119572; cv=pass; b=tFDN+ItpkIP18FtoEvjJXxv3/v6QgJfLkAcSIMspK8t2FdDKYP4gw7C/VEOD5KVvaTl2aPHtyH0JyyKW0Qf/yoTSrWKRj1Np7gZ4EHAw/WG2Zdhr32wSDIZAU00UGkr8VfSHHv25RnzmvAn5KySMk7lPjIPKiTJ1ZRtWZF97gAk= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119572; c=relaxed/simple; bh=l6DErf34TRzCRafpugB0AflCX5aEHZQzye568x4/03E=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Wf7kn1qrIfjO+OELLC2hElLSDUs9DaGV24LwDT+rNWitGXpENHKm831Z7OpzWZJ0JOTBDTQynwUfNrKnZKSQuUOV1PBJ1rPWd90AnpgAk91XSFZzf5kmuQJyNQsx9fMiLsq8xPGlPIHj9E8ExI8solF6Jkqi1V3bCUyTssKVUyU= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=Whkcs+Cz; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="Whkcs+Cz" ARC-Seal: i=1; a=rsa-sha256; t=1776119527; cv=none; d=zohomail.com; s=zohoarc; b=njAXZMpBVU2bPhiSAmt0y3yy5v0tvjvjrySCD2EgfWmPiyCYt73D3KWHTDgFVV17bj2Mg/ymB6x4oZmXBvfK4ecAbFlDDtQy5sBy97GZtmD2SyP79c0wXRhVz24fe0AsLvKSLmix0vRltDuz2EuH3RspEUzO04rHt0gT5ZxCtoE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119527; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=KcHukzoU8dcgpz87NrenhC9DZ76DzADTeyvlOzIcL9c=; b=dewnemAeopVeWQUkwgTfvTThoy5hvSsIiuJRBNfC9eAQPH5uB2Zsrj6cm/Mthu3a0lE0LEQVOzTUFhzK38lIJGisB25P21lH3n1EGmNmLrEvCaO9gn6FxwD7SU8pXGSmF4JIvYI2TN+b2zGdU3k5PXJHAwJoadca0uoC7vcDvMQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119527; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=KcHukzoU8dcgpz87NrenhC9DZ76DzADTeyvlOzIcL9c=; b=Whkcs+CzqXAupsdgi4of014avOq2X4mRYUhBJcGNydz0CGestcpZhGuPjSClpLIe 1Mms/BWhLHv72WJ5aIpueog800oIo6dtCE5qu7JVsmkMC8o24TOx46k//oKvBN0i5i4 IF0F5ft1aR3u/OIgIZEbIHt0VRrTA5HzV6iNNj68= Received: by mx.zohomail.com with SMTPS id 1776119525866948.4350674528085; Mon, 13 Apr 2026 15:32:05 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:47 -0700 Subject: [PATCH v3 12/13] drm/tyr: add parser for firmware binary Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-12-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=18286; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=b9k99QVltbqyD/Fe9rFI5rWsmu3CuqkqbOW+iRrxeO4=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3865yT3be3vPpQl9C4GTN0xfqosJjn99sLN64QO6Po 5+h1XevjlIWBjEuBlkxRZaz9kY94lXvjXTn/2+GmcPKBDKEgYtTACaSdoXhf1XezU/ppzm3/7jn p3a6MmnrH6aE7e3vNCKjm0KMPQ22STL8L3p3387P8mpvnf+MBzteX+g5/WXOxZtrpKMsjj/PvnI xihsA X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB From: Daniel Almeida Add a parser for the Mali CSF GPU firmware binary format. The firmware consists of a header followed by entries describing how to load firmware sections into the MCU's memory. The parser extracts section metadata including virtual address ranges, data byte offsets within the binary, and section flags controlling permissions and cache modes. It validates the basic firmware structure and alignment and ignores protected-mode sections for now. Signed-off-by: Daniel Almeida Co-developed-by: Beata Michalska Signed-off-by: Beata Michalska Co-developed-by: Boris Brezillon Signed-off-by: Boris Brezillon Co-developed-by: Deborah Brouwer Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/fw/parser.rs | 519 +++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 519 insertions(+) diff --git a/drivers/gpu/drm/tyr/fw/parser.rs b/drivers/gpu/drm/tyr/fw/pars= er.rs new file mode 100644 index 000000000000..638707430701 --- /dev/null +++ b/drivers/gpu/drm/tyr/fw/parser.rs @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +//! Firmware binary parser for Mali CSF (Command Stream Frontend) GPU. +//! +//! This module implements a parser for the Mali GPU firmware binary forma= t. The firmware +//! file contains a header followed by a sequence of entries, each describ= ing how to load +//! firmware sections into the MCU (Microcontroller Unit) memory. The pars= er extracts section metadata including: +//! - Virtual address ranges where sections should be mapped +//! - Data ranges (byte offsets) within the firmware binary +//! - Section flags (permissions, cache modes) + +use core::{ + mem::size_of, + ops::Range, // +}; + +use kernel::{ + bits::bit_u32, + prelude::*, + str::CString, // +}; + +use crate::{ + fw::{ + SectionFlag, + SectionFlags, + CSF_MCU_SHARED_REGION_START, // + }, + vm::{ + VmFlag, + VmMapFlags, // + }, // +}; + +/// A parsed firmware section ready for loading into MCU memory. +/// +/// Represents a single firmware section extracted from the firmware binar= y, containing +/// all information needed to map the section's data into the MCU's virtua= l address space. +pub(super) struct ParsedSection { + /// Byte offset range within the firmware binary where this section's = data resides. + pub(super) data_range: Range, + /// MCU virtual address range where this section should be mapped. + pub(super) va: Range, + /// Memory protection and caching flags for the mapping. + pub(super) vm_map_flags: VmMapFlags, +} + +/// A bare-bones `std::io::Cursor<[u8]>` clone to keep track of the curren= t position in the firmware binary. +/// +/// Provides methods to sequentially read primitive types and byte arrays = from the firmware +/// binary while maintaining the current read position. +struct Cursor<'a> { + data: &'a [u8], + pos: usize, +} + +impl<'a> Cursor<'a> { + fn new(data: &'a [u8]) -> Self { + Self { data, pos: 0 } + } + + fn len(&self) -> usize { + self.data.len() + } + + fn pos(&self) -> usize { + self.pos + } + + /// Returns a view into the cursor's data. + /// + /// This spawns a new cursor, leaving the current cursor unchanged. + fn view(&self, range: Range) -> Result> { + if range.start < self.pos || range.end > self.data.len() { + pr_err!( + "Invalid cursor range {:?} for data of length {}", + range, + self.data.len() + ); + + Err(EINVAL) + } else { + Ok(Self { + data: &self.data[range], + pos: 0, + }) + } + } + + /// Reads a slice of bytes from the current position and advances the = cursor. + /// + /// Returns an error if the read would exceed the data bounds. + fn read(&mut self, nbytes: usize) -> Result<&[u8]> { + let start =3D self.pos; + let end =3D start + nbytes; + + if end > self.data.len() { + pr_err!( + "Invalid firmware file: read of size {} at position {} is = out of bounds", + nbytes, + start, + ); + return Err(EINVAL); + } + + self.pos +=3D nbytes; + Ok(&self.data[start..end]) + } + + /// Reads a little-endian `u8` from the current position and advances = the cursor. + fn read_u8(&mut self) -> Result { + let bytes =3D self.read(size_of::())?; + Ok(bytes[0]) + } + + /// Reads a little-endian `u16` from the current position and advances= the cursor. + fn read_u16(&mut self) -> Result { + let bytes =3D self.read(size_of::())?; + Ok(u16::from_le_bytes(bytes.try_into().unwrap())) + } + + /// Reads a little-endian `u32` from the current position and advances= the cursor. + fn read_u32(&mut self) -> Result { + let bytes =3D self.read(size_of::())?; + Ok(u32::from_le_bytes(bytes.try_into().unwrap())) + } + + /// Advances the cursor position by the specified number of bytes. + /// + /// Returns an error if the advance would exceed the data bounds. + fn advance(&mut self, nbytes: usize) -> Result { + if self.pos + nbytes > self.data.len() { + pr_err!( + "Invalid firmware file: advance of size {} at position {} = is out of bounds", + nbytes, + self.pos, + ); + return Err(EINVAL); + } + self.pos +=3D nbytes; + Ok(()) + } +} + +/// Parser for Mali CSF GPU firmware binaries. +/// +/// Parses the firmware binary format, extracting section metadata includi= ng virtual +/// address ranges, data offsets, and memory protection flags needed to lo= ad firmware +/// into the MCU's memory. +pub(super) struct FwParser<'a> { + cursor: Cursor<'a>, +} + +impl<'a> FwParser<'a> { + /// Creates a new firmware parser for the given firmware binary data. + pub(super) fn new(data: &'a [u8]) -> Self { + Self { + cursor: Cursor::new(data), + } + } + + /// Parses the firmware binary and returns a collection of parsed sect= ions. + /// + /// This method validates the firmware header and iterates through all= entries + /// in the binary, extracting section information needed for loading. + pub(super) fn parse(&mut self) -> Result> { + let fw_header =3D self.parse_fw_header()?; + + let mut parsed_sections =3D KVec::new(); + while (self.cursor.pos() as u32) < fw_header.size { + let entry_section =3D self.parse_entry()?; + + if let Some(inner) =3D entry_section.inner { + parsed_sections.push(inner, GFP_KERNEL)?; + } + } + + Ok(parsed_sections) + } + + fn parse_fw_header(&mut self) -> Result { + let fw_header: FirmwareHeader =3D match FirmwareHeader::new(&mut s= elf.cursor) { + Ok(fw_header) =3D> fw_header, + Err(e) =3D> { + pr_err!("Invalid firmware file: {}", e.to_errno()); + return Err(e); + } + }; + + if fw_header.size > self.cursor.len() as u32 { + pr_err!("Firmware image is truncated"); + return Err(EINVAL); + } + Ok(fw_header) + } + + fn parse_entry(&mut self) -> Result { + let entry_section =3D EntrySection { + entry_hdr: EntryHeader(self.cursor.read_u32()?), + inner: None, + }; + + if self.cursor.pos() % size_of::() !=3D 0 + || entry_section.entry_hdr.size() as usize % size_of::() = !=3D 0 + { + pr_err!( + "Firmware entry isn't 32 bit aligned, offset=3D{:#x} size= =3D{:#x}\n", + self.cursor.pos() - size_of::(), + entry_section.entry_hdr.size() + ); + return Err(EINVAL); + } + + let section_hdr_size =3D entry_section.entry_hdr.size() as usize -= size_of::(); + + let entry_section =3D { + let mut entry_cursor =3D self + .cursor + .view(self.cursor.pos()..self.cursor.pos() + section_hdr_s= ize)?; + + match entry_section.entry_hdr.entry_type() { + Ok(EntryType::Iface) =3D> Ok(EntrySection { + entry_hdr: entry_section.entry_hdr, + inner: Self::parse_section_entry(&mut entry_cursor)?, + }), + Ok( + EntryType::Config + | EntryType::FutfTest + | EntryType::TraceBuffer + | EntryType::TimelineMetadata + | EntryType::BuildInfoMetadata, + ) =3D> Ok(entry_section), + + entry_type =3D> { + if entry_type.is_err() || !entry_section.entry_hdr.opt= ional() { + if !entry_section.entry_hdr.optional() { + pr_err!( + "Failed to handle firmware entry type: {}\= n", + entry_type + .map_or(entry_section.entry_hdr.entry_= type_raw(), |e| e as u8) + ); + Err(EINVAL) + } else { + Ok(entry_section) + } + } else { + Ok(entry_section) + } + } + } + }; + + if entry_section.is_ok() { + self.cursor.advance(section_hdr_size)?; + } + + entry_section + } + + fn parse_section_entry(entry_cursor: &mut Cursor<'_>) -> Result> { + let section_hdr: SectionHeader =3D SectionHeader::new(entry_cursor= )?; + + if section_hdr.data.end < section_hdr.data.start { + pr_err!( + "Firmware corrupted, data.end < data.start (0x{:x} < 0x{:x= })\n", + section_hdr.data.end, + section_hdr.data.start + ); + return Err(EINVAL); + } + + if section_hdr.va.end < section_hdr.va.start { + pr_err!( + "Firmware corrupted, section_hdr.va.end < section_hdr.va.s= tart (0x{:x} < 0x{:x})\n", + section_hdr.va.end, + section_hdr.va.start + ); + return Err(EINVAL); + } + + if section_hdr.section_flags.contains(SectionFlag::Prot) { + pr_info!("Firmware protected mode entry not supported, ignorin= g"); + return Ok(None); + } + + if section_hdr.va.start =3D=3D CSF_MCU_SHARED_REGION_START + && !section_hdr.section_flags.contains(SectionFlag::Shared) + { + pr_err!( + "Interface at 0x{:x} must be shared", + CSF_MCU_SHARED_REGION_START + ); + return Err(EINVAL); + } + + let name_len =3D entry_cursor.len() - entry_cursor.pos(); + let name_bytes =3D entry_cursor.read(name_len)?; + + let mut name =3D KVec::with_capacity(name_bytes.len() + 1, GFP_KER= NEL)?; + name.extend_from_slice(name_bytes, GFP_KERNEL)?; + name.push(0, GFP_KERNEL)?; + + let _name =3D CStr::from_bytes_with_nul(&name) + .ok() + .and_then(|name| CString::try_from(name).ok()); + + let cache_mode =3D section_hdr.section_flags.cache_mode(); + let mut vm_map_flags =3D VmMapFlags::empty(); + + if !section_hdr.section_flags.contains(SectionFlag::Write) { + vm_map_flags |=3D VmFlag::Readonly; + } + if !section_hdr.section_flags.contains(SectionFlag::Exec) { + vm_map_flags |=3D VmFlag::Noexec; + } + if cache_mode !=3D SectionFlag::CacheModeCached.into() { + vm_map_flags |=3D VmFlag::Uncached; + } + + Ok(Some(ParsedSection { + data_range: section_hdr.data.clone(), + va: section_hdr.va, + vm_map_flags, + })) + } +} + +/// Firmware binary header containing version and size information. +/// +/// The header is located at the beginning of the firmware binary and cont= ains +/// a magic value for validation, version information, and the total size = of +/// all structured headers that follow. +#[expect(dead_code)] +struct FirmwareHeader { + /// Magic value to check binary validity. + magic: u32, + + /// Minor firmware version. + minor: u8, + + /// Major firmware version. + major: u8, + + /// Padding. Must be set to zero. + _padding1: u16, + + /// Firmware version hash. + version_hash: u32, + + /// Padding. Must be set to zero. + _padding2: u32, + + /// Total size of all the structured data headers at beginning of firm= ware binary. + size: u32, +} + +impl FirmwareHeader { + const FW_BINARY_MAGIC: u32 =3D 0xc3f13a6e; + const FW_BINARY_MAJOR_MAX: u8 =3D 0; + + /// Reads and validates a firmware header from the cursor. + /// + /// Verifies the magic value, version compatibility, and padding field= s. + fn new(cursor: &mut Cursor<'_>) -> Result { + let magic =3D cursor.read_u32()?; + if magic !=3D Self::FW_BINARY_MAGIC { + pr_err!("Invalid firmware magic"); + return Err(EINVAL); + } + + let minor =3D cursor.read_u8()?; + let major =3D cursor.read_u8()?; + + if major > Self::FW_BINARY_MAJOR_MAX { + pr_err!( + "Unsupported firmware binary header version {}.{} (expecte= d {}.x)\n", + major, + minor, + Self::FW_BINARY_MAJOR_MAX + ); + return Err(EINVAL); + } + + let padding1 =3D cursor.read_u16()?; + let version_hash =3D cursor.read_u32()?; + let padding2 =3D cursor.read_u32()?; + let size =3D cursor.read_u32()?; + + if padding1 !=3D 0 || padding2 !=3D 0 { + pr_err!("Invalid firmware file: header padding is not zero"); + return Err(EINVAL); + } + + let fw_header =3D Self { + magic, + minor, + major, + _padding1: padding1, + version_hash, + _padding2: padding2, + size, + }; + + Ok(fw_header) + } +} + +/// Firmware section header for loading binary sections into MCU memory. +#[derive(Debug)] +struct SectionHeader { + section_flags: SectionFlags, + /// MCU virtual range to map this binary section to. + va: Range, + /// References the data in the FW binary. + data: Range, +} + +impl SectionHeader { + /// Reads and validates a section header from the cursor. + /// + /// Parses section flags, virtual address range, and data range from t= he firmware binary. + fn new(cursor: &mut Cursor<'_>) -> Result { + let section_flags =3D cursor.read_u32()?; + let section_flags =3D SectionFlags::try_from(section_flags)?; + + let va_start =3D cursor.read_u32()?; + let va_end =3D cursor.read_u32()?; + + let va =3D va_start..va_end; + + if va.is_empty() { + pr_err!( + "Invalid firmware file: empty VA range at pos {}\n", + cursor.pos(), + ); + return Err(EINVAL); + } + + let data_start =3D cursor.read_u32()?; + let data_end =3D cursor.read_u32()?; + let data =3D data_start..data_end; + + Ok(Self { + section_flags, + va, + data, + }) + } +} + +/// A firmware entry containing a header and optional parsed section data. +/// +/// Represents a single entry in the firmware binary, which may contain lo= adable +/// section data or metadata that doesn't require loading. +struct EntrySection { + entry_hdr: EntryHeader, + inner: Option, +} + +/// Header for a firmware entry, packed into a single u32. +/// +/// The entry header encodes the entry type, size, and optional flag in a +/// 32-bit value with the following layout: +/// - Bits 0-7: Entry type +/// - Bits 8-15: Size in bytes +/// - Bit 31: Optional flag +struct EntryHeader(u32); + +impl EntryHeader { + fn entry_type_raw(&self) -> u8 { + (self.0 & 0xff) as u8 + } + + fn entry_type(&self) -> Result { + let v =3D self.entry_type_raw(); + EntryType::try_from(v) + } + + fn optional(&self) -> bool { + self.0 & bit_u32(31) !=3D 0 + } + + fn size(&self) -> u32 { + self.0 >> 8 & 0xff + } +} + +#[derive(Clone, Copy, Debug)] +#[repr(u8)] +enum EntryType { + /// Host <-> FW interface. + Iface =3D 0, + /// FW config. + Config =3D 1, + /// Unit tests. + FutfTest =3D 2, + /// Trace buffer interface. + TraceBuffer =3D 3, + /// Timeline metadata interface. + TimelineMetadata =3D 4, + /// Metadata about how the FW binary was built. + BuildInfoMetadata =3D 6, +} + +impl TryFrom for EntryType { + type Error =3D Error; + + fn try_from(value: u8) -> Result { + match value { + 0 =3D> Ok(EntryType::Iface), + 1 =3D> Ok(EntryType::Config), + 2 =3D> Ok(EntryType::FutfTest), + 3 =3D> Ok(EntryType::TraceBuffer), + 4 =3D> Ok(EntryType::TimelineMetadata), + 6 =3D> Ok(EntryType::BuildInfoMetadata), + _ =3D> Err(EINVAL), + } + } +} --=20 2.53.0 From nobody Sat Jun 20 15:21:37 2026 Received: from sender4-pp-f112.zoho.com (sender4-pp-f112.zoho.com [136.143.188.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 85B7539DBE6; Mon, 13 Apr 2026 22:32:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.112 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119556; cv=pass; b=hFw/vIgRPdKmOBOnnIC2DemsTgg5iWF1/WVVkgyuwqOwEfSh1RJASa8U+SRziIgcrEMDDJYFd8bpxaxaxmZ1prl3U9LVtoIol7V6ifGZ4t5HndSlu5eKg7lOddapwey762jNjQ+KzVy29AZgsPLZnaM0CyYUDkjQmMS/5HJhP18= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776119556; c=relaxed/simple; bh=c4q67V7m5J8MVZYjpWxFtUzFl3nup07SAjdC7IcRBEc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EIkqUj2clFTIw70O+QLojJq3W/Y8b+z1jp9ZBiCQXBMNE28Ra2Yb1M2jcVOSNalpB0NqtUW1kRCrAs9G4MKNR3jijgo8YPVc5bJINUZgsgVk4adSoYlL7jPOAGydmbCEiUxCsIKh2+alSsR/P7LoxxGLQidjgfi7YFdYOGmbClE= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b=TNDvf27Q; arc=pass smtp.client-ip=136.143.188.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=deborah.brouwer@collabora.com header.b="TNDvf27Q" ARC-Seal: i=1; a=rsa-sha256; t=1776119527; cv=none; d=zohomail.com; s=zohoarc; b=AUcWEFrAivccskcgztl9V0gqQSwLavtFYPXa9GbYhGC20j9LdtNBQLtoreLABF3qyrPi32fG5SgPLyLEuEqzuoJ1kGGOPxYPKEFO0bTZ/q4wYe/K2w8Q7Tigj72gi7srvPXQWWQTiZJ++eD6T9u/tdzzYquit5WTCvay5mj+Kys= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1776119527; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=0htmUZ7cd0cYpXNM04syGCkizAjtah7yfT8aRdJ9awE=; b=atdncNBfIMbIZ8wj6yuyeaaB5FW9ItRQ+Lt4RjHc1hB2Tcwvrq1tFuVZtjBy/BSTCfQueWu6DtFYKbLkxbtZZgD1cIFbj2DFCbfPXqHkrNgtw7VrpSYR4H7/KxOwDfbZ+LkeIu7f6O5TW8WIl002QdvgvnSWsFZwlQZ2nJYSz+4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=deborah.brouwer@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1776119527; s=zohomail; d=collabora.com; i=deborah.brouwer@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=0htmUZ7cd0cYpXNM04syGCkizAjtah7yfT8aRdJ9awE=; b=TNDvf27QR+YKp7pyYj+UlflxwIrowbIPObatUyhcBvqZgYQ8FYkK2GbQbCnGq5NG MjHFwIl2rYkXkaT1kFlqM8l3W8SfUEjVKj3RWFFplYl1zHPLqbZHC492WjTNY2w4FxH tG14xuj22tI4rno4NsF8XW9h23DZtgrQCbDnCwpU= Received: by mx.zohomail.com with SMTPS id 1776119526912312.1447643201245; Mon, 13 Apr 2026 15:32:06 -0700 (PDT) From: Deborah Brouwer Date: Mon, 13 Apr 2026 15:31:48 -0700 Subject: [PATCH v3 13/13] drm/tyr: add firmware loading and MCU boot support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260413-b4-fw-boot-v3-v3-13-b422f3c03885@collabora.com> References: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> In-Reply-To: <20260413-b4-fw-boot-v3-v3-0-b422f3c03885@collabora.com> To: Daniel Almeida , Alice Ryhl , Danilo Krummrich , David Airlie , Simona Vetter , Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Trevor Gross Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com, beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com, work@onurozkan.dev, alvin.sun@linux.dev, Deborah Brouwer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=13544; i=deborah.brouwer@collabora.com; h=from:subject:message-id; bh=c4q67V7m5J8MVZYjpWxFtUzFl3nup07SAjdC7IcRBEc=; b=owGbwMvMwCVWuULzOU9c7WvG02pJDJl3864Kn+TtM45LuLDpyLujGx2u7fz5M6jIuEvz9CeB4 OZvbYUqHaUsDGJcDLJiiixn7Y16xKveG+nO/98MM4eVCWQIAxenAEzEKZXhf8onubdzla+8WNT9 7EpA+IS6ZT6vTiif/6c2vSc0Sy/85DNGhqnsuh80jh+1jGT9uf/Uht/nquQtLxbeYtiV7J+b8jX 1FgMA X-Developer-Key: i=deborah.brouwer@collabora.com; a=openpgp; fpr=CD3F328C177AEF322D9FFF8379A829E70C5E7DEB Add firmware loading and management for the Mali CSF GPU. This introduces the fw module that loads the Mali GPU firmware binary, parses it into sections, and maps those sections into the MCU VM at the required virtual addresses. On probe, the firmware is loaded, its sections are mapped and populated, the MCU VM is activated, and the MCU is booted. Co-developed-by: Boris Brezillon Signed-off-by: Boris Brezillon Signed-off-by: Deborah Brouwer --- drivers/gpu/drm/tyr/Kconfig | 1 + drivers/gpu/drm/tyr/driver.rs | 16 ++- drivers/gpu/drm/tyr/fw.rs | 272 ++++++++++++++++++++++++++++++++++++++= ++++ drivers/gpu/drm/tyr/gem.rs | 3 - drivers/gpu/drm/tyr/mmu.rs | 1 - drivers/gpu/drm/tyr/slot.rs | 1 - drivers/gpu/drm/tyr/tyr.rs | 1 + drivers/gpu/drm/tyr/vm.rs | 1 - 8 files changed, 289 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/tyr/Kconfig b/drivers/gpu/drm/tyr/Kconfig index 443ce988b570..729643f4db49 100644 --- a/drivers/gpu/drm/tyr/Kconfig +++ b/drivers/gpu/drm/tyr/Kconfig @@ -18,6 +18,7 @@ config DRM_TYR select DRM_TYR_STATIC_DEPS select IOMMU_IO_PGTABLE_LPAE select RUST_DRM_GEM_SHMEM_HELPER + select RUST_FW_LOADER_ABSTRACTIONS depends on IOMMU_SUPPORT default n help diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index 495021a8657d..246bc3cb8580 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -42,6 +42,7 @@ =20 use crate::{ file::TyrDrmFileData, + fw::Firmware, gem::BoData, gpu, gpu::GpuInfo, @@ -63,6 +64,8 @@ pub(crate) struct TyrDrmDeviceData { pub(crate) pdev: ARef, =20 + pub(crate) fw: Arc, + #[pin] clks: Mutex, =20 @@ -154,10 +157,21 @@ fn probe( let uninit_ddev =3D UnregisteredDevice::::new(pdev.a= s_ref())?; let platform: ARef =3D pdev.into(); =20 - let _mmu =3D Mmu::new(pdev, iomem.as_arc_borrow(), &gpu_info)?; + let mmu =3D Mmu::new(pdev, iomem.as_arc_borrow(), &gpu_info)?; + + let firmware =3D Firmware::new( + pdev, + iomem.clone(), + &uninit_ddev, + mmu.as_arc_borrow(), + &gpu_info, + )?; + + firmware.boot()?; =20 let data =3D try_pin_init!(TyrDrmDeviceData { pdev: platform.clone(), + fw: firmware, clks <- new_mutex!(Clocks { core: core_clk, stacks: stacks_clk, diff --git a/drivers/gpu/drm/tyr/fw.rs b/drivers/gpu/drm/tyr/fw.rs new file mode 100644 index 000000000000..cb2546350f0a --- /dev/null +++ b/drivers/gpu/drm/tyr/fw.rs @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT + +//! Firmware loading and management for Mali CSF GPU. +//! +//! This module handles loading the Mali GPU firmware binary, parsing it i= nto sections, +//! and mapping those sections into the MCU's virtual address space. Each = firmware section +//! has specific properties (read/write/execute permissions, cache modes) = and must be loaded +//! at specific virtual addresses expected by the MCU. +//! +//! See [`Firmware`] for the main firmware management interface and [`Sect= ion`] for +//! individual firmware sections. +//! +//! [`Firmware`]: crate::fw::Firmware +//! [`Section`]: crate::fw::Section + +use kernel::{ + bits::genmask_u32, + devres::Devres, + drm::{ + gem::BaseObject, + Uninit, // + }, + impl_flags, + io::{ + poll, + Io, // + }, + platform, + prelude::*, + str::CString, + sync::{ + Arc, + ArcBorrow, // + }, + time, + types::ARef, // +}; + +use crate::{ + driver::{ + IoMem, + TyrDrmDevice, // + }, + fw::parser::{ + FwParser, + ParsedSection, // + }, + gem, + gem::{ + KernelBo, + KernelBoVaAlloc, // + }, + gpu::GpuInfo, + mmu::Mmu, + regs::gpu_control::{ + McuControlMode, + McuStatus, + GPU_ID, + MCU_CONTROL, + MCU_STATUS, // + }, + vm::Vm, // +}; + +mod parser; + +impl_flags!( + #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)] + pub(super) struct SectionFlags(u32); + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub(super) enum SectionFlag { + Read =3D 1 << 0, + Write =3D 1 << 1, + Exec =3D 1 << 2, + CacheModeNone =3D 0 << 3, + CacheModeCached =3D 1 << 3, + CacheModeUncachedCoherent =3D 2 << 3, + CacheModeCachedCoherent =3D 3 << 3, + Prot =3D 1 << 5, + Shared =3D 1 << 30, + Zero =3D 1 << 31, + } +); + +pub(super) const CACHE_MODE_MASK: SectionFlags =3D SectionFlags(genmask_u3= 2(3..=3D4)); + +pub(super) const CSF_MCU_SHARED_REGION_START: u32 =3D 0x04000000; + +impl SectionFlags { + fn cache_mode(&self) -> SectionFlags { + *self & CACHE_MODE_MASK + } +} + +impl TryFrom for SectionFlags { + type Error =3D Error; + + fn try_from(value: u32) -> Result { + let valid_flags =3D SectionFlags::from(SectionFlag::Read) + | SectionFlags::from(SectionFlag::Write) + | SectionFlags::from(SectionFlag::Exec) + | CACHE_MODE_MASK + | SectionFlags::from(SectionFlag::Prot) + | SectionFlags::from(SectionFlag::Shared) + | SectionFlags::from(SectionFlag::Zero); + + if value & valid_flags.0 !=3D value { + Err(EINVAL) + } else { + Ok(Self(value)) + } + } +} + +/// A parsed section of the firmware binary. +struct Section { + // Raw firmware section data for reset purposes + #[expect(dead_code)] + data: KVec, + + // Keep the BO backing this firmware section so that both the + // GPU mapping and CPU mapping remain valid until the Section is dropp= ed. + #[expect(dead_code)] + mem: gem::KernelBo, +} + +/// Loaded firmware with sections mapped into MCU VM. +pub(crate) struct Firmware { + /// Platform device reference (needed to access the MCU JOB_IRQ regist= ers). + pdev: ARef, + + /// Iomem need to access registers. + iomem: Arc>, + + /// MCU VM. + vm: Arc, + + /// List of firmware sections. + #[expect(dead_code)] + sections: KVec
, +} + +impl Drop for Firmware { + fn drop(&mut self) { + // AS slots retain a VM ref, we need to kill the circular ref manu= ally. + self.vm.kill(); + } +} + +impl Firmware { + fn init_section_mem(mem: &mut KernelBo, data: &KVec) -> Result { + if data.is_empty() { + return Ok(()); + } + + let vmap =3D mem.bo.vmap::<0>()?; + let size =3D mem.bo.size(); + + if data.len() > size { + pr_err!("fw section {} bigger than BO {}\n", data.len(), size); + return Err(EINVAL); + } + + for (i, &byte) in data.iter().enumerate() { + vmap.try_write8(byte, i)?; + } + + Ok(()) + } + + fn request( + ddev: &TyrDrmDevice, + gpu_info: &GpuInfo, + ) -> Result { + let gpu_id =3D GPU_ID::from_raw(gpu_info.gpu_id); + + let path =3D CString::try_from_fmt(fmt!( + "arm/mali/arch{}.{}/mali_csffw.bin", + gpu_id.arch_major().get(), + gpu_id.arch_minor().get() + ))?; + + kernel::firmware::Firmware::request(&path, ddev.as_ref()) + } + + fn load( + ddev: &TyrDrmDevice, + gpu_info: &GpuInfo, + ) -> Result<(kernel::firmware::Firmware, KVec)> { + let fw =3D Self::request(ddev, gpu_info)?; + let mut parser =3D FwParser::new(fw.data()); + + let parsed_sections =3D parser.parse()?; + + Ok((fw, parsed_sections)) + } + + /// Load firmware and map sections into MCU VM. + pub(crate) fn new( + pdev: &platform::Device, + iomem: Arc>, + ddev: &TyrDrmDevice, + mmu: ArcBorrow<'_, Mmu>, + gpu_info: &GpuInfo, + ) -> Result> { + let vm =3D Vm::new(pdev, ddev, mmu, gpu_info)?; + + let (fw, parsed_sections) =3D Self::load(ddev, gpu_info)?; + + vm.activate()?; + + let mut sections =3D KVec::new(); + for parsed in parsed_sections { + let size =3D (parsed.va.end - parsed.va.start) as usize; + let va =3D u64::from(parsed.va.start); + + let mut mem =3D KernelBo::new( + ddev, + vm.as_arc_borrow(), + size.try_into().unwrap(), + KernelBoVaAlloc::Explicit(va), + parsed.vm_map_flags, + )?; + + let section_start =3D parsed.data_range.start as usize; + let section_end =3D parsed.data_range.end as usize; + let mut data =3D KVec::new(); + + // Ensure that the firmware slice is not out of bounds. + let fw_data =3D fw.data(); + let bytes =3D fw_data.get(section_start..section_end).ok_or(EI= NVAL)?; + data.extend_from_slice(bytes, GFP_KERNEL)?; + + Self::init_section_mem(&mut mem, &data)?; + + sections.push(Section { data, mem }, GFP_KERNEL)?; + } + + let firmware =3D Arc::new( + Firmware { + pdev: pdev.into(), + iomem, + vm, + sections, + }, + GFP_KERNEL, + )?; + + Ok(firmware) + } + + pub(crate) fn boot(&self) -> Result { + // SAFETY: Boot is currently only called in the probe path, so we'= re sure we have a bound + // device. + let dev =3D unsafe { self.pdev.as_ref().as_bound() }; + let io =3D (self.iomem).access(dev)?; + io.write_reg(MCU_CONTROL::zeroed().with_req(McuControlMode::Auto)); + + if let Err(e) =3D poll::read_poll_timeout( + || Ok(io.read(MCU_STATUS)), + |status| status.value() =3D=3D McuStatus::Enabled, + time::Delta::from_millis(1), + time::Delta::from_millis(100), + ) { + let status =3D io.read(MCU_STATUS); + pr_err!("MCU failed to boot, status: {:?}", status.value()); + return Err(e); + } + Ok(()) + } +} diff --git a/drivers/gpu/drm/tyr/gem.rs b/drivers/gpu/drm/tyr/gem.rs index d032a8ae543f..4ec373e0bcfa 100644 --- a/drivers/gpu/drm/tyr/gem.rs +++ b/drivers/gpu/drm/tyr/gem.rs @@ -94,7 +94,6 @@ pub(crate) fn new_dummy_object(ddev: = &TyrDrmDevice) -> /// a [`KernelBo`]. An automatic VA allocation strategy will be added in t= he future. pub(crate) enum KernelBoVaAlloc { /// Explicit VA address specified by the caller. - #[expect(dead_code)] Explicit(u64), } =20 @@ -107,7 +106,6 @@ pub(crate) enum KernelBoVaAlloc { /// When dropped, the buffer is automatically unmapped from the GPU VA spa= ce. pub(crate) struct KernelBo { /// The underlying GEM buffer object. - #[expect(dead_code)] pub(crate) bo: ARef, /// The GPU VM this buffer is mapped into. vm: Arc, @@ -121,7 +119,6 @@ impl KernelBo { /// This function allocates a new shmem-backed GEM object and immediat= ely maps /// it into the specified GPU virtual memory space. The mapping is aut= omatically /// cleaned up when the [`KernelBo`] is dropped. - #[expect(dead_code)] pub(crate) fn new( ddev: &TyrDrmDevice, vm: ArcBorrow<'_, Vm>, diff --git a/drivers/gpu/drm/tyr/mmu.rs b/drivers/gpu/drm/tyr/mmu.rs index 09df98ffc9e3..935e2102ab30 100644 --- a/drivers/gpu/drm/tyr/mmu.rs +++ b/drivers/gpu/drm/tyr/mmu.rs @@ -12,7 +12,6 @@ //! //! [`AddressSpaceManager`]: address_space::AddressSpaceManager //! [`SlotManager`]: crate::slot::SlotManager -#![allow(dead_code)] =20 use core::ops::Range; =20 diff --git a/drivers/gpu/drm/tyr/slot.rs b/drivers/gpu/drm/tyr/slot.rs index debba75f6204..53abb9eeb970 100644 --- a/drivers/gpu/drm/tyr/slot.rs +++ b/drivers/gpu/drm/tyr/slot.rs @@ -20,7 +20,6 @@ //! //! [SlotOperations]: crate::slot::SlotOperations //! [SlotManager]: crate::slot::SlotManager -#![allow(dead_code)] =20 use core::{ mem::take, diff --git a/drivers/gpu/drm/tyr/tyr.rs b/drivers/gpu/drm/tyr/tyr.rs index b3244670dd79..18b0668bb217 100644 --- a/drivers/gpu/drm/tyr/tyr.rs +++ b/drivers/gpu/drm/tyr/tyr.rs @@ -9,6 +9,7 @@ =20 mod driver; mod file; +mod fw; mod gem; mod gpu; mod mmu; diff --git a/drivers/gpu/drm/tyr/vm.rs b/drivers/gpu/drm/tyr/vm.rs index c19300d76194..1ef7e40ccdb5 100644 --- a/drivers/gpu/drm/tyr/vm.rs +++ b/drivers/gpu/drm/tyr/vm.rs @@ -6,7 +6,6 @@ //! the illusion of owning the entire virtual address (VA) range, similar = to CPU virtual memory. //! Each virtual memory (VM) area is backed by ARM64 LPAE Stage 1 page tab= les and can be //! mapped into hardware address space (AS) slots for GPU execution. -#![allow(dead_code)] =20 use core::ops::Range; =20 --=20 2.53.0