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::io::poll; 12 use kernel::new_mutex; 13 use kernel::of; 14 use kernel::platform; 15 use kernel::prelude::*; 16 use kernel::regulator; 17 use kernel::regulator::Regulator; 18 use kernel::sizes::SZ_2M; 19 use kernel::sync::aref::ARef; 20 use kernel::sync::Arc; 21 use kernel::sync::Mutex; 22 use kernel::time; 23 24 use crate::file::File; 25 use crate::gem::TyrObject; 26 use crate::gpu; 27 use crate::gpu::GpuInfo; 28 use crate::regs; 29 30 pub(crate) type IoMem = kernel::io::mem::IoMem<SZ_2M>; 31 32 /// Convenience type alias for the DRM device type for this driver. 33 pub(crate) type TyrDevice = drm::Device<TyrDriver>; 34 35 #[pin_data(PinnedDrop)] 36 pub(crate) struct TyrDriver { 37 _device: ARef<TyrDevice>, 38 } 39 40 #[pin_data(PinnedDrop)] 41 pub(crate) struct TyrData { 42 pub(crate) pdev: ARef<platform::Device>, 43 44 #[pin] 45 clks: Mutex<Clocks>, 46 47 #[pin] 48 regulators: Mutex<Regulators>, 49 50 /// Some information on the GPU. 51 /// 52 /// This is mainly queried by userspace, i.e.: Mesa. 53 pub(crate) gpu_info: GpuInfo, 54 } 55 56 fn issue_soft_reset(dev: &Device<Bound>, iomem: &Devres<IoMem>) -> Result { 57 regs::GPU_CMD.write(dev, iomem, regs::GPU_CMD_SOFT_RESET)?; 58 59 poll::read_poll_timeout( 60 || regs::GPU_IRQ_RAWSTAT.read(dev, iomem), 61 |status| *status & regs::GPU_IRQ_RAWSTAT_RESET_COMPLETED != 0, 62 time::Delta::from_millis(1), 63 time::Delta::from_millis(100), 64 ) 65 .inspect_err(|_| dev_err!(dev, "GPU reset failed."))?; 66 67 Ok(()) 68 } 69 70 kernel::of_device_table!( 71 OF_TABLE, 72 MODULE_OF_TABLE, 73 <TyrDriver as platform::Driver>::IdInfo, 74 [ 75 (of::DeviceId::new(c"rockchip,rk3588-mali"), ()), 76 (of::DeviceId::new(c"arm,mali-valhall-csf"), ()) 77 ] 78 ); 79 80 impl platform::Driver for TyrDriver { 81 type IdInfo = (); 82 const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); 83 84 fn probe( 85 pdev: &platform::Device<Core>, 86 _info: Option<&Self::IdInfo>, 87 ) -> impl PinInit<Self, Error> { 88 let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?; 89 let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?; 90 let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?; 91 92 core_clk.prepare_enable()?; 93 stacks_clk.prepare_enable()?; 94 coregroup_clk.prepare_enable()?; 95 96 let mali_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"mali")?; 97 let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?; 98 99 let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; 100 let iomem = Arc::pin_init(request.iomap_sized::<SZ_2M>(), GFP_KERNEL)?; 101 102 issue_soft_reset(pdev.as_ref(), &iomem)?; 103 gpu::l2_power_on(pdev.as_ref(), &iomem)?; 104 105 let gpu_info = GpuInfo::new(pdev.as_ref(), &iomem)?; 106 gpu_info.log(pdev); 107 108 let platform: ARef<platform::Device> = pdev.into(); 109 110 let data = try_pin_init!(TyrData { 111 pdev: platform.clone(), 112 clks <- new_mutex!(Clocks { 113 core: core_clk, 114 stacks: stacks_clk, 115 coregroup: coregroup_clk, 116 }), 117 regulators <- new_mutex!(Regulators { 118 _mali: mali_regulator, 119 _sram: sram_regulator, 120 }), 121 gpu_info, 122 }); 123 124 let tdev: ARef<TyrDevice> = drm::Device::new(pdev.as_ref(), data)?; 125 drm::driver::Registration::new_foreign_owned(&tdev, pdev.as_ref(), 0)?; 126 127 let driver = TyrDriver { _device: tdev }; 128 129 // We need this to be dev_info!() because dev_dbg!() does not work at 130 // all in Rust for now, and we need to see whether probe succeeded. 131 dev_info!(pdev, "Tyr initialized correctly.\n"); 132 Ok(driver) 133 } 134 } 135 136 #[pinned_drop] 137 impl PinnedDrop for TyrDriver { 138 fn drop(self: Pin<&mut Self>) {} 139 } 140 141 #[pinned_drop] 142 impl PinnedDrop for TyrData { 143 fn drop(self: Pin<&mut Self>) { 144 // TODO: the type-state pattern for Clks will fix this. 145 let clks = self.clks.lock(); 146 clks.core.disable_unprepare(); 147 clks.stacks.disable_unprepare(); 148 clks.coregroup.disable_unprepare(); 149 } 150 } 151 152 // We need to retain the name "panthor" to achieve drop-in compatibility with 153 // the C driver in the userspace stack. 154 const INFO: drm::DriverInfo = drm::DriverInfo { 155 major: 1, 156 minor: 5, 157 patchlevel: 0, 158 name: c"panthor", 159 desc: c"ARM Mali Tyr DRM driver", 160 }; 161 162 #[vtable] 163 impl drm::Driver for TyrDriver { 164 type Data = TyrData; 165 type File = File; 166 type Object = drm::gem::Object<TyrObject>; 167 168 const INFO: drm::DriverInfo = INFO; 169 170 kernel::declare_drm_ioctls! { 171 (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, File::dev_query), 172 } 173 } 174 175 #[pin_data] 176 struct Clocks { 177 core: Clk, 178 stacks: OptionalClk, 179 coregroup: OptionalClk, 180 } 181 182 #[pin_data] 183 struct Regulators { 184 _mali: Regulator<regulator::Enabled>, 185 _sram: Regulator<regulator::Enabled>, 186 } 187