// SPDX-License-Identifier: GPL-2.0 or MIT use kernel::c_str; use kernel::clk::Clk; use kernel::clk::OptionalClk; use kernel::device::Bound; use kernel::device::Core; use kernel::device::Device; use kernel::devres::Devres; use kernel::drm; use kernel::drm::ioctl; use kernel::new_mutex; use kernel::of; use kernel::platform; use kernel::prelude::*; use kernel::regulator; use kernel::regulator::Regulator; use kernel::sizes::SZ_2M; use kernel::sync::Arc; use kernel::sync::Mutex; use kernel::time; use kernel::types::ARef; use crate::file::File; use crate::gem::TyrObject; use crate::gpu; use crate::gpu::GpuInfo; use crate::regs; pub(crate) type IoMem = kernel::io::mem::IoMem; /// Convenience type alias for the DRM device type for this driver. pub(crate) type TyrDevice = drm::Device; #[pin_data(PinnedDrop)] pub(crate) struct TyrDriver { device: ARef, } #[pin_data(PinnedDrop)] pub(crate) struct TyrData { pub(crate) pdev: ARef, #[pin] clks: Mutex, #[pin] regulators: Mutex, /// Some information on the GPU. /// /// This is mainly queried by userspace, i.e.: Mesa. pub(crate) gpu_info: GpuInfo, } // Both `Clk` and `Regulator` do not implement `Send` or `Sync`, but they // should. There are patches on the mailing list to address this, but they have // not landed yet. // // For now, add this workaround so that this patch compiles with the promise // that it will be removed in a future patch. // // SAFETY: This will be removed in a future patch. unsafe impl Send for TyrData {} // SAFETY: This will be removed in a future patch. unsafe impl Sync for TyrData {} fn issue_soft_reset(dev: &Device, iomem: &Devres) -> Result { regs::GPU_CMD.write(dev, iomem, regs::GPU_CMD_SOFT_RESET)?; // TODO: We cannot poll, as there is no support in Rust currently, so we // sleep. Change this when read_poll_timeout() is implemented in Rust. kernel::time::delay::fsleep(time::Delta::from_millis(100)); if regs::GPU_IRQ_RAWSTAT.read(dev, iomem)? & regs::GPU_IRQ_RAWSTAT_RESET_COMPLETED == 0 { dev_err!(dev, "GPU reset failed with errno\n"); dev_err!( dev, "GPU_INT_RAWSTAT is {}\n", regs::GPU_IRQ_RAWSTAT.read(dev, iomem)? ); return Err(EIO); } Ok(()) } kernel::of_device_table!( OF_TABLE, MODULE_OF_TABLE, ::IdInfo, [ (of::DeviceId::new(c_str!("rockchip,rk3588-mali")), ()), (of::DeviceId::new(c_str!("arm,mali-valhall-csf")), ()) ] ); impl platform::Driver for TyrDriver { type IdInfo = (); const OF_ID_TABLE: Option> = Some(&OF_TABLE); fn probe( pdev: &platform::Device, _info: Option<&Self::IdInfo>, ) -> Result>> { let core_clk = Clk::get(pdev.as_ref(), Some(c_str!("core")))?; let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c_str!("stacks")))?; let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c_str!("coregroup")))?; core_clk.prepare_enable()?; stacks_clk.prepare_enable()?; coregroup_clk.prepare_enable()?; let mali_regulator = Regulator::::get(pdev.as_ref(), c_str!("mali"))?; let sram_regulator = Regulator::::get(pdev.as_ref(), c_str!("sram"))?; let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; let iomem = Arc::pin_init(request.iomap_sized::(), GFP_KERNEL)?; issue_soft_reset(pdev.as_ref(), &iomem)?; gpu::l2_power_on(pdev.as_ref(), &iomem)?; let gpu_info = GpuInfo::new(pdev.as_ref(), &iomem)?; gpu_info.log(pdev); let platform: ARef = pdev.into(); let data = try_pin_init!(TyrData { pdev: platform.clone(), clks <- new_mutex!(Clocks { core: core_clk, stacks: stacks_clk, coregroup: coregroup_clk, }), regulators <- new_mutex!(Regulators { mali: mali_regulator, sram: sram_regulator, }), gpu_info, }); let tdev: ARef = drm::Device::new(pdev.as_ref(), data)?; drm::driver::Registration::new_foreign_owned(&tdev, pdev.as_ref(), 0)?; let driver = KBox::pin_init(try_pin_init!(TyrDriver { device: tdev }), GFP_KERNEL)?; // We need this to be dev_info!() because dev_dbg!() does not work at // all in Rust for now, and we need to see whether probe succeeded. dev_info!(pdev.as_ref(), "Tyr initialized correctly.\n"); Ok(driver) } } #[pinned_drop] impl PinnedDrop for TyrDriver { fn drop(self: Pin<&mut Self>) {} } #[pinned_drop] impl PinnedDrop for TyrData { fn drop(self: Pin<&mut Self>) { // TODO: the type-state pattern for Clks will fix this. let clks = self.clks.lock(); clks.core.disable_unprepare(); clks.stacks.disable_unprepare(); clks.coregroup.disable_unprepare(); } } // We need to retain the name "panthor" to achieve drop-in compatibility with // the C driver in the userspace stack. const INFO: drm::DriverInfo = drm::DriverInfo { major: 1, minor: 5, patchlevel: 0, name: c_str!("panthor"), desc: c_str!("ARM Mali Tyr DRM driver"), }; #[vtable] impl drm::Driver for TyrDriver { type Data = TyrData; type File = File; type Object = drm::gem::Object; const INFO: drm::DriverInfo = INFO; kernel::declare_drm_ioctls! { (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, File::dev_query), } } #[pin_data] struct Clocks { core: Clk, stacks: OptionalClk, coregroup: OptionalClk, } #[pin_data] struct Regulators { mali: Regulator, sram: Regulator, }