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 }, 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 32 use crate::{ 33 file::TyrDrmFileData, 34 gem::TyrObject, 35 gpu, 36 gpu::GpuInfo, 37 regs, // 38 }; 39 40 pub(crate) type IoMem = kernel::io::mem::IoMem<SZ_2M>; 41 42 pub(crate) struct TyrDrmDriver; 43 44 /// Convenience type alias for the DRM device type for this driver. 45 pub(crate) type TyrDrmDevice = drm::Device<TyrDrmDriver>; 46 47 #[pin_data(PinnedDrop)] 48 pub(crate) struct TyrPlatformDriverData { 49 _device: ARef<TyrDrmDevice>, 50 } 51 52 #[pin_data(PinnedDrop)] 53 pub(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 68 // Both `Clk` and `Regulator` do not implement `Send` or `Sync`, but they 69 // should. There are patches on the mailing list to address this, but they have 70 // not landed yet. 71 // 72 // For now, add this workaround so that this patch compiles with the promise 73 // that it will be removed in a future patch. 74 // 75 // SAFETY: This will be removed in a future patch. 76 unsafe impl Send for TyrDrmDeviceData {} 77 // SAFETY: This will be removed in a future patch. 78 unsafe impl Sync for TyrDrmDeviceData {} 79 80 fn issue_soft_reset(dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result { 81 regs::GPU_CMD.write(dev, iomem, regs::GPU_CMD_SOFT_RESET)?; 82 83 poll::read_poll_timeout( 84 || regs::GPU_IRQ_RAWSTAT.read(dev, iomem), 85 |status| *status & regs::GPU_IRQ_RAWSTAT_RESET_COMPLETED != 0, 86 time::Delta::from_millis(1), 87 time::Delta::from_millis(100), 88 ) 89 .inspect_err(|_| dev_err!(dev, "GPU reset failed."))?; 90 91 Ok(()) 92 } 93 94 kernel::of_device_table!( 95 OF_TABLE, 96 MODULE_OF_TABLE, 97 <TyrPlatformDriverData as platform::Driver>::IdInfo, 98 [ 99 (of::DeviceId::new(c"rockchip,rk3588-mali"), ()), 100 (of::DeviceId::new(c"arm,mali-valhall-csf"), ()) 101 ] 102 ); 103 104 impl platform::Driver for TyrPlatformDriverData { 105 type IdInfo = (); 106 const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); 107 108 fn probe( 109 pdev: &platform::Device<Core>, 110 _info: Option<&Self::IdInfo>, 111 ) -> impl PinInit<Self, Error> { 112 let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?; 113 let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?; 114 let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?; 115 116 core_clk.prepare_enable()?; 117 stacks_clk.prepare_enable()?; 118 coregroup_clk.prepare_enable()?; 119 120 let mali_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"mali")?; 121 let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?; 122 123 let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; 124 let iomem = Arc::pin_init(request.iomap_sized::<SZ_2M>(), GFP_KERNEL)?; 125 126 issue_soft_reset(pdev.as_ref(), &iomem)?; 127 gpu::l2_power_on(pdev.as_ref(), &iomem)?; 128 129 let gpu_info = GpuInfo::new(pdev.as_ref(), &iomem)?; 130 gpu_info.log(pdev); 131 132 let platform: ARef<platform::Device> = pdev.into(); 133 134 let data = try_pin_init!(TyrDrmDeviceData { 135 pdev: platform.clone(), 136 clks <- new_mutex!(Clocks { 137 core: core_clk, 138 stacks: stacks_clk, 139 coregroup: coregroup_clk, 140 }), 141 regulators <- new_mutex!(Regulators { 142 _mali: mali_regulator, 143 _sram: sram_regulator, 144 }), 145 gpu_info, 146 }); 147 148 let ddev: ARef<TyrDrmDevice> = drm::Device::new(pdev.as_ref(), data)?; 149 drm::driver::Registration::new_foreign_owned(&ddev, pdev.as_ref(), 0)?; 150 151 let driver = TyrPlatformDriverData { _device: ddev }; 152 153 // We need this to be dev_info!() because dev_dbg!() does not work at 154 // all in Rust for now, and we need to see whether probe succeeded. 155 dev_info!(pdev, "Tyr initialized correctly.\n"); 156 Ok(driver) 157 } 158 } 159 160 #[pinned_drop] 161 impl PinnedDrop for TyrPlatformDriverData { 162 fn drop(self: Pin<&mut Self>) {} 163 } 164 165 #[pinned_drop] 166 impl PinnedDrop for TyrDrmDeviceData { 167 fn drop(self: Pin<&mut Self>) { 168 // TODO: the type-state pattern for Clks will fix this. 169 let clks = self.clks.lock(); 170 clks.core.disable_unprepare(); 171 clks.stacks.disable_unprepare(); 172 clks.coregroup.disable_unprepare(); 173 } 174 } 175 176 // We need to retain the name "panthor" to achieve drop-in compatibility with 177 // the C driver in the userspace stack. 178 const INFO: drm::DriverInfo = drm::DriverInfo { 179 major: 1, 180 minor: 5, 181 patchlevel: 0, 182 name: c"panthor", 183 desc: c"ARM Mali Tyr DRM driver", 184 }; 185 186 #[vtable] 187 impl drm::Driver for TyrDrmDriver { 188 type Data = TyrDrmDeviceData; 189 type File = TyrDrmFileData; 190 type Object = drm::gem::Object<TyrObject>; 191 192 const INFO: drm::DriverInfo = INFO; 193 194 kernel::declare_drm_ioctls! { 195 (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, TyrDrmFileData::dev_query), 196 } 197 } 198 199 #[pin_data] 200 struct Clocks { 201 core: Clk, 202 stacks: OptionalClk, 203 coregroup: OptionalClk, 204 } 205 206 #[pin_data] 207 struct Regulators { 208 _mali: Regulator<regulator::Enabled>, 209 _sram: Regulator<regulator::Enabled>, 210 } 211