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