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