Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0 or MIT
2
3use kernel::{
4 clk::{
5 Clk,
6 OptionalClk, //
7 },
8 device::{
9 Bound,
10 Core,
11 Device, //
12 },
13 devres::Devres,
14 drm,
15 drm::ioctl,
16 io::poll,
17 new_mutex,
18 of,
19 platform,
20 prelude::*,
21 regulator,
22 regulator::Regulator,
23 sizes::SZ_2M,
24 sync::{
25 aref::ARef,
26 Arc,
27 Mutex, //
28 },
29 time, //
30};
31
32use crate::{
33 file::TyrDrmFileData,
34 gem::TyrObject,
35 gpu,
36 gpu::GpuInfo,
37 regs, //
38};
39
40pub(crate) type IoMem = kernel::io::mem::IoMem<SZ_2M>;
41
42pub(crate) struct TyrDrmDriver;
43
44/// Convenience type alias for the DRM device type for this driver.
45pub(crate) type TyrDrmDevice = drm::Device<TyrDrmDriver>;
46
47#[pin_data(PinnedDrop)]
48pub(crate) struct TyrPlatformDriverData {
49 _device: ARef<TyrDrmDevice>,
50}
51
52#[pin_data(PinnedDrop)]
53pub(crate) struct TyrDrmDeviceData {
54 pub(crate) pdev: ARef<platform::Device>,
55
56 #[pin]
57 clks: Mutex<Clocks>,
58
59 #[pin]
60 regulators: Mutex<Regulators>,
61
62 /// Some information on the GPU.
63 ///
64 /// This is mainly queried by userspace, i.e.: Mesa.
65 pub(crate) gpu_info: GpuInfo,
66}
67
68fn issue_soft_reset(dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result {
69 regs::GPU_CMD.write(dev, iomem, regs::GPU_CMD_SOFT_RESET)?;
70
71 poll::read_poll_timeout(
72 || regs::GPU_IRQ_RAWSTAT.read(dev, iomem),
73 |status| *status & regs::GPU_IRQ_RAWSTAT_RESET_COMPLETED != 0,
74 time::Delta::from_millis(1),
75 time::Delta::from_millis(100),
76 )
77 .inspect_err(|_| dev_err!(dev, "GPU reset failed."))?;
78
79 Ok(())
80}
81
82kernel::of_device_table!(
83 OF_TABLE,
84 MODULE_OF_TABLE,
85 <TyrPlatformDriverData as platform::Driver>::IdInfo,
86 [
87 (of::DeviceId::new(c"rockchip,rk3588-mali"), ()),
88 (of::DeviceId::new(c"arm,mali-valhall-csf"), ())
89 ]
90);
91
92impl platform::Driver for TyrPlatformDriverData {
93 type IdInfo = ();
94 const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
95
96 fn probe(
97 pdev: &platform::Device<Core>,
98 _info: Option<&Self::IdInfo>,
99 ) -> impl PinInit<Self, Error> {
100 let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?;
101 let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?;
102 let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?;
103
104 core_clk.prepare_enable()?;
105 stacks_clk.prepare_enable()?;
106 coregroup_clk.prepare_enable()?;
107
108 let mali_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"mali")?;
109 let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?;
110
111 let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
112 let iomem = Arc::pin_init(request.iomap_sized::<SZ_2M>(), GFP_KERNEL)?;
113
114 issue_soft_reset(pdev.as_ref(), &iomem)?;
115 gpu::l2_power_on(pdev.as_ref(), &iomem)?;
116
117 let gpu_info = GpuInfo::new(pdev.as_ref(), &iomem)?;
118 gpu_info.log(pdev);
119
120 let platform: ARef<platform::Device> = pdev.into();
121
122 let data = try_pin_init!(TyrDrmDeviceData {
123 pdev: platform.clone(),
124 clks <- new_mutex!(Clocks {
125 core: core_clk,
126 stacks: stacks_clk,
127 coregroup: coregroup_clk,
128 }),
129 regulators <- new_mutex!(Regulators {
130 _mali: mali_regulator,
131 _sram: sram_regulator,
132 }),
133 gpu_info,
134 });
135
136 let ddev: ARef<TyrDrmDevice> = drm::Device::new(pdev.as_ref(), data)?;
137 drm::driver::Registration::new_foreign_owned(&ddev, pdev.as_ref(), 0)?;
138
139 let driver = TyrPlatformDriverData { _device: ddev };
140
141 // We need this to be dev_info!() because dev_dbg!() does not work at
142 // all in Rust for now, and we need to see whether probe succeeded.
143 dev_info!(pdev, "Tyr initialized correctly.\n");
144 Ok(driver)
145 }
146}
147
148#[pinned_drop]
149impl PinnedDrop for TyrPlatformDriverData {
150 fn drop(self: Pin<&mut Self>) {}
151}
152
153#[pinned_drop]
154impl PinnedDrop for TyrDrmDeviceData {
155 fn drop(self: Pin<&mut Self>) {
156 // TODO: the type-state pattern for Clks will fix this.
157 let clks = self.clks.lock();
158 clks.core.disable_unprepare();
159 clks.stacks.disable_unprepare();
160 clks.coregroup.disable_unprepare();
161 }
162}
163
164// We need to retain the name "panthor" to achieve drop-in compatibility with
165// the C driver in the userspace stack.
166const INFO: drm::DriverInfo = drm::DriverInfo {
167 major: 1,
168 minor: 5,
169 patchlevel: 0,
170 name: c"panthor",
171 desc: c"ARM Mali Tyr DRM driver",
172};
173
174#[vtable]
175impl drm::Driver for TyrDrmDriver {
176 type Data = TyrDrmDeviceData;
177 type File = TyrDrmFileData;
178 type Object = drm::gem::Object<TyrObject>;
179
180 const INFO: drm::DriverInfo = INFO;
181
182 kernel::declare_drm_ioctls! {
183 (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, TyrDrmFileData::dev_query),
184 }
185}
186
187#[pin_data]
188struct Clocks {
189 core: Clk,
190 stacks: OptionalClk,
191 coregroup: OptionalClk,
192}
193
194#[pin_data]
195struct Regulators {
196 _mali: Regulator<regulator::Enabled>,
197 _sram: Regulator<regulator::Enabled>,
198}