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