1 // SPDX-License-Identifier: GPL-2.0 or MIT 2 3 use kernel::clk::Clk; 4 use kernel::clk::OptionalClk; 5 use kernel::device::Bound; 6 use kernel::device::Core; 7 use kernel::device::Device; 8 use kernel::devres::Devres; 9 use kernel::drm; 10 use kernel::drm::ioctl; 11 use kernel::new_mutex; 12 use kernel::of; 13 use kernel::platform; 14 use kernel::prelude::*; 15 use kernel::regulator; 16 use kernel::regulator::Regulator; 17 use kernel::sizes::SZ_2M; 18 use kernel::sync::aref::ARef; 19 use kernel::sync::Arc; 20 use kernel::sync::Mutex; 21 use kernel::time; 22 23 use crate::file::File; 24 use crate::gem::TyrObject; 25 use crate::gpu; 26 use crate::gpu::GpuInfo; 27 use crate::regs; 28 29 pub(crate) type IoMem = kernel::io::mem::IoMem<SZ_2M>; 30 31 /// Convenience type alias for the DRM device type for this driver. 32 pub(crate) type TyrDevice = drm::Device<TyrDriver>; 33 34 #[pin_data(PinnedDrop)] 35 pub(crate) struct TyrDriver { 36 device: ARef<TyrDevice>, 37 } 38 39 #[pin_data(PinnedDrop)] 40 pub(crate) struct TyrData { 41 pub(crate) pdev: ARef<platform::Device>, 42 43 #[pin] 44 clks: Mutex<Clocks>, 45 46 #[pin] 47 regulators: Mutex<Regulators>, 48 49 /// Some information on the GPU. 50 /// 51 /// This is mainly queried by userspace, i.e.: Mesa. 52 pub(crate) gpu_info: GpuInfo, 53 } 54 55 // Both `Clk` and `Regulator` do not implement `Send` or `Sync`, but they 56 // should. There are patches on the mailing list to address this, but they have 57 // not landed yet. 58 // 59 // For now, add this workaround so that this patch compiles with the promise 60 // that it will be removed in a future patch. 61 // 62 // SAFETY: This will be removed in a future patch. 63 unsafe impl Send for TyrData {} 64 // SAFETY: This will be removed in a future patch. 65 unsafe impl Sync for TyrData {} 66 67 fn issue_soft_reset(dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result { 68 regs::GPU_CMD.write(dev, iomem, regs::GPU_CMD_SOFT_RESET)?; 69 70 // TODO: We cannot poll, as there is no support in Rust currently, so we 71 // sleep. Change this when read_poll_timeout() is implemented in Rust. 72 kernel::time::delay::fsleep(time::Delta::from_millis(100)); 73 74 if regs::GPU_IRQ_RAWSTAT.read(dev, iomem)? & regs::GPU_IRQ_RAWSTAT_RESET_COMPLETED == 0 { 75 dev_err!(dev, "GPU reset failed with errno\n"); 76 dev_err!( 77 dev, 78 "GPU_INT_RAWSTAT is {}\n", 79 regs::GPU_IRQ_RAWSTAT.read(dev, iomem)? 80 ); 81 82 return Err(EIO); 83 } 84 85 Ok(()) 86 } 87 88 kernel::of_device_table!( 89 OF_TABLE, 90 MODULE_OF_TABLE, 91 <TyrDriver 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 TyrDriver { 99 type IdInfo = (); 100 const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); 101 102 fn probe( 103 pdev: &platform::Device<Core>, 104 _info: Option<&Self::IdInfo>, 105 ) -> impl PinInit<Self, Error> { 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 = Arc::pin_init(request.iomap_sized::<SZ_2M>(), GFP_KERNEL)?; 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(pdev.as_ref(), &iomem)?; 124 gpu_info.log(pdev); 125 126 let platform: ARef<platform::Device> = pdev.into(); 127 128 let data = try_pin_init!(TyrData { 129 pdev: platform.clone(), 130 clks <- new_mutex!(Clocks { 131 core: core_clk, 132 stacks: stacks_clk, 133 coregroup: coregroup_clk, 134 }), 135 regulators <- new_mutex!(Regulators { 136 mali: mali_regulator, 137 sram: sram_regulator, 138 }), 139 gpu_info, 140 }); 141 142 let tdev: ARef<TyrDevice> = drm::Device::new(pdev.as_ref(), data)?; 143 drm::driver::Registration::new_foreign_owned(&tdev, pdev.as_ref(), 0)?; 144 145 let driver = TyrDriver { device: tdev }; 146 147 // We need this to be dev_info!() because dev_dbg!() does not work at 148 // all in Rust for now, and we need to see whether probe succeeded. 149 dev_info!(pdev.as_ref(), "Tyr initialized correctly.\n"); 150 Ok(driver) 151 } 152 } 153 154 #[pinned_drop] 155 impl PinnedDrop for TyrDriver { 156 fn drop(self: Pin<&mut Self>) {} 157 } 158 159 #[pinned_drop] 160 impl PinnedDrop for TyrData { 161 fn drop(self: Pin<&mut Self>) { 162 // TODO: the type-state pattern for Clks will fix this. 163 let clks = self.clks.lock(); 164 clks.core.disable_unprepare(); 165 clks.stacks.disable_unprepare(); 166 clks.coregroup.disable_unprepare(); 167 } 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 TyrDriver { 182 type Data = TyrData; 183 type File = File; 184 type Object = drm::gem::Object<TyrObject>; 185 186 const INFO: drm::DriverInfo = INFO; 187 188 kernel::declare_drm_ioctls! { 189 (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, File::dev_query), 190 } 191 } 192 193 #[pin_data] 194 struct Clocks { 195 core: Clk, 196 stacks: OptionalClk, 197 coregroup: OptionalClk, 198 } 199 200 #[pin_data] 201 struct Regulators { 202 mali: Regulator<regulator::Enabled>, 203 sram: Regulator<regulator::Enabled>, 204 } 205