xref: /linux/drivers/gpu/drm/tyr/driver.rs (revision fdc290ff4ab19c7e0dde36c4cd1e2771b61f6bf5)
1 // SPDX-License-Identifier: GPL-2.0 or MIT
2 
3 use kernel::{
4     clk::{
5         Clk,
6         OptionalClk, //
7     },
8     device::{
9         Bound,
10         Core,
11         Device,
12         DeviceContext, //
13     },
14     dma::{
15         Device as DmaDevice,
16         DmaMask, //
17     },
18     drm,
19     drm::ioctl,
20     io::{
21         poll,
22         Io, //
23     },
24     new_mutex,
25     of,
26     platform,
27     prelude::*,
28     regulator,
29     regulator::Regulator,
30     sizes::SZ_2M,
31     sync::{
32         Arc,
33         Mutex, //
34     },
35     time, //
36 };
37 
38 use crate::{
39     file::TyrDrmFileData,
40     fw::Firmware,
41     gem::Bo,
42     gpu,
43     gpu::GpuInfo,
44     mmu::Mmu,
45     regs::gpu_control::*, //
46 };
47 
48 pub(crate) type IoMem<'a> = kernel::io::mem::IoMem<'a, SZ_2M>;
49 
50 pub(crate) struct TyrDrmDriver;
51 
52 /// Convenience type alias for the DRM device type for this driver.
53 pub(crate) type TyrDrmDevice<Ctx = drm::Normal> = drm::Device<TyrDrmDriver, Ctx>;
54 
55 pub(crate) struct TyrPlatformDriver;
56 
57 #[pin_data(PinnedDrop)]
58 pub(crate) struct TyrPlatformDriverData<'bound> {
59     _reg: drm::Registration<'bound, TyrDrmDriver>,
60 }
61 
62 /// Data owned by the DRM [`Registration`].
63 ///
64 /// This data can have references tied to the parent platform device binding scope
65 /// and is accessible only while the DRM device is registered with userspace.
66 #[pin_data]
67 pub(crate) struct TyrDrmRegistrationData<'drm> {
68     /// Parent platform device.
69     pub(crate) pdev: &'drm platform::Device<Bound>,
70 
71     /// Firmware sections.
72     pub(crate) fw: Firmware<'drm>,
73 
74     #[pin]
75     clks: Mutex<Clocks>,
76 
77     #[pin]
78     regulators: Mutex<Regulators>,
79 
80     /// GPU MMIO register mapping.
81     pub(crate) iomem: Arc<IoMem<'drm>>,
82 
83     /// GPU information read from hardware during probe.
84     pub(crate) gpu_info: GpuInfo,
85 }
86 
87 fn issue_soft_reset(dev: &Device, iomem: &IoMem<'_>) -> Result {
88     iomem.write_reg(GPU_COMMAND::reset(ResetMode::SoftReset));
89 
90     poll::read_poll_timeout(
91         || Ok(iomem.read(GPU_IRQ_RAWSTAT)),
92         |status| status.reset_completed(),
93         time::Delta::from_millis(1),
94         time::Delta::from_millis(100),
95     )
96     .inspect_err(|_| dev_err!(dev, "GPU reset failed."))?;
97 
98     Ok(())
99 }
100 
101 kernel::of_device_table!(
102     OF_TABLE,
103     MODULE_OF_TABLE,
104     <TyrPlatformDriver as platform::Driver>::IdInfo,
105     [
106         (of::DeviceId::new(c"rockchip,rk3588-mali"), ()),
107         (of::DeviceId::new(c"arm,mali-valhall-csf"), ())
108     ]
109 );
110 
111 impl platform::Driver for TyrPlatformDriver {
112     type IdInfo = ();
113     type Data<'bound> = TyrPlatformDriverData<'bound>;
114     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
115 
116     fn probe<'bound>(
117         pdev: &'bound platform::Device<Core<'_>>,
118         _info: Option<&'bound Self::IdInfo>,
119     ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
120         let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?;
121         let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?;
122         let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?;
123 
124         core_clk.prepare_enable()?;
125         stacks_clk.prepare_enable()?;
126         coregroup_clk.prepare_enable()?;
127 
128         let mali_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"mali")?;
129         let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?;
130 
131         let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
132 
133         let iomem = Arc::new(request.iomap_sized::<SZ_2M>()?, GFP_KERNEL)?;
134 
135         issue_soft_reset(pdev.as_ref(), &iomem)?;
136         gpu::l2_power_on(pdev.as_ref(), &iomem)?;
137 
138         let gpu_info = GpuInfo::new(&iomem);
139         gpu_info.log(pdev.as_ref());
140 
141         let pa_bits = MMU_FEATURES::from_raw(gpu_info.mmu_features)
142             .pa_bits()
143             .get();
144         // SAFETY: No concurrent DMA allocations or mappings can be made because
145         // the device is still being probed and therefore isn't being used by
146         // other threads of execution.
147         unsafe { pdev.dma_set_mask_and_coherent(DmaMask::try_new(pa_bits)?)? };
148 
149         let unreg_dev = drm::UnregisteredDevice::<TyrDrmDriver>::new(pdev, Ok(()))?;
150 
151         let mmu = Mmu::new(pdev.as_ref(), iomem.as_arc_borrow(), &gpu_info)?;
152 
153         let firmware = Firmware::new(
154             pdev.as_ref(),
155             iomem.clone(),
156             &unreg_dev,
157             mmu.as_arc_borrow(),
158             &gpu_info,
159         )?;
160 
161         firmware.boot()?;
162 
163         let reg_data = pin_init!(TyrDrmRegistrationData {
164                 pdev,
165                 fw: firmware,
166                 clks <- new_mutex!(Clocks {
167                     core: core_clk,
168                     stacks: stacks_clk,
169                     coregroup: coregroup_clk,
170                 }),
171                 regulators <- new_mutex!(Regulators {
172                     _mali: mali_regulator,
173                     _sram: sram_regulator,
174                 }),
175                 iomem,
176                 gpu_info,
177         });
178 
179         // SAFETY: `reg` is stored in `TyrPlatformDriverData` and dropped when the driver is
180         // unbound; it is never forgotten.
181         let reg = unsafe { drm::Registration::new(pdev.as_ref(), unreg_dev, reg_data, 0)? };
182 
183         let driver = TyrPlatformDriverData { _reg: reg };
184 
185         dev_dbg!(pdev, "Tyr initialized correctly.");
186         Ok(driver)
187     }
188 }
189 
190 #[pinned_drop]
191 impl PinnedDrop for TyrPlatformDriverData<'_> {
192     fn drop(self: Pin<&mut Self>) {}
193 }
194 
195 // We need to retain the name "panthor" to achieve drop-in compatibility with
196 // the C driver in the userspace stack.
197 const INFO: drm::DriverInfo = drm::DriverInfo {
198     major: 1,
199     minor: 5,
200     patchlevel: 0,
201     name: c"panthor",
202     desc: c"ARM Mali Tyr DRM driver",
203 };
204 
205 #[vtable]
206 impl drm::Driver for TyrDrmDriver {
207     type Data = ();
208     type RegistrationData<'drm> = TyrDrmRegistrationData<'drm>;
209     type File = TyrDrmFileData;
210     type Object = Bo;
211     type ParentDevice<Ctx: DeviceContext> = platform::Device<Ctx>;
212 
213     const INFO: drm::DriverInfo = INFO;
214     const FEAT_RENDER: bool = true;
215 
216     kernel::declare_drm_ioctls! {
217         (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, TyrDrmFileData::dev_query),
218     }
219 }
220 
221 struct Clocks {
222     core: Clk,
223     stacks: OptionalClk,
224     coregroup: OptionalClk,
225 }
226 
227 impl Drop for Clocks {
228     fn drop(&mut self) {
229         self.core.disable_unprepare();
230         self.stacks.disable_unprepare();
231         self.coregroup.disable_unprepare();
232     }
233 }
234 
235 struct Regulators {
236     _mali: Regulator<regulator::Enabled>,
237     _sram: Regulator<regulator::Enabled>,
238 }
239