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