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 <TyrPlatformDriver as platform::Driver>::IdInfo, 91 [ 92 (of::DeviceId::new(c"rockchip,rk3588-mali"), ()), 93 (of::DeviceId::new(c"arm,mali-valhall-csf"), ()) 94 ] 95 ); 96 97 impl platform::Driver for TyrPlatformDriver { 98 type IdInfo = (); 99 type Data<'bound> = TyrPlatformDriverData; 100 const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); 101 102 fn probe<'bound>( 103 pdev: &'bound platform::Device<Core<'_>>, 104 _info: Option<&'bound Self::IdInfo>, 105 ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound { 106 let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?; 107 let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?; 108 let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?; 109 110 core_clk.prepare_enable()?; 111 stacks_clk.prepare_enable()?; 112 coregroup_clk.prepare_enable()?; 113 114 let mali_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"mali")?; 115 let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?; 116 117 let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; 118 let iomem = request.iomap_sized::<SZ_2M>()?; 119 120 issue_soft_reset(pdev.as_ref(), &iomem)?; 121 gpu::l2_power_on(pdev.as_ref(), &iomem)?; 122 123 let gpu_info = GpuInfo::new(&iomem); 124 gpu_info.log(pdev.as_ref()); 125 126 let pa_bits = MMU_FEATURES::from_raw(gpu_info.mmu_features) 127 .pa_bits() 128 .get(); 129 // SAFETY: No concurrent DMA allocations or mappings can be made because 130 // the device is still being probed and therefore isn't being used by 131 // other threads of execution. 132 unsafe { pdev.dma_set_mask_and_coherent(DmaMask::try_new(pa_bits)?)? }; 133 134 let platform: ARef<platform::Device> = pdev.into(); 135 136 let data = try_pin_init!(TyrDrmDeviceData { 137 pdev: platform.clone(), 138 clks <- new_mutex!(Clocks { 139 core: core_clk, 140 stacks: stacks_clk, 141 coregroup: coregroup_clk, 142 }), 143 regulators <- new_mutex!(Regulators { 144 _mali: mali_regulator, 145 _sram: sram_regulator, 146 }), 147 gpu_info, 148 }); 149 150 let tdev = drm::UnregisteredDevice::<TyrDrmDriver>::new(pdev.as_ref(), data)?; 151 let tdev = drm::driver::Registration::new_foreign_owned(tdev, pdev.as_ref(), 0)?; 152 153 let driver = TyrPlatformDriverData { 154 _device: tdev.into(), 155 }; 156 157 // We need this to be dev_info!() because dev_dbg!() does not work at 158 // all in Rust for now, and we need to see whether probe succeeded. 159 dev_info!(pdev, "Tyr initialized correctly.\n"); 160 Ok(driver) 161 } 162 } 163 164 #[pinned_drop] 165 impl PinnedDrop for TyrPlatformDriverData { 166 fn drop(self: Pin<&mut Self>) {} 167 } 168 169 // We need to retain the name "panthor" to achieve drop-in compatibility with 170 // the C driver in the userspace stack. 171 const INFO: drm::DriverInfo = drm::DriverInfo { 172 major: 1, 173 minor: 5, 174 patchlevel: 0, 175 name: c"panthor", 176 desc: c"ARM Mali Tyr DRM driver", 177 }; 178 179 #[vtable] 180 impl drm::Driver for TyrDrmDriver { 181 type Data = TyrDrmDeviceData; 182 type File = TyrDrmFileData; 183 type Object<R: drm::DeviceContext> = drm::gem::shmem::Object<BoData, R>; 184 185 const INFO: drm::DriverInfo = INFO; 186 const FEAT_RENDER: bool = true; 187 188 kernel::declare_drm_ioctls! { 189 (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, TyrDrmFileData::dev_query), 190 } 191 } 192 193 struct Clocks { 194 core: Clk, 195 stacks: OptionalClk, 196 coregroup: OptionalClk, 197 } 198 199 impl Drop for Clocks { 200 fn drop(&mut self) { 201 self.core.disable_unprepare(); 202 self.stacks.disable_unprepare(); 203 self.coregroup.disable_unprepare(); 204 } 205 } 206 207 struct Regulators { 208 _mali: Regulator<regulator::Enabled>, 209 _sram: Regulator<regulator::Enabled>, 210 } 211