// SPDX-License-Identifier: GPL-2.0 or MIT use kernel::{ clk::{ Clk, OptionalClk, // }, device::{ Core, Device, // }, dma::{ Device as DmaDevice, DmaMask, // }, drm, drm::ioctl, io::{ poll, Io, // }, new_mutex, of, platform, prelude::*, regulator, regulator::Regulator, sizes::SZ_2M, sync::{ aref::ARef, Mutex, // }, time, // }; use crate::{ file::TyrDrmFileData, gem::BoData, gpu, gpu::GpuInfo, regs::gpu_control::*, // }; pub(crate) type IoMem<'a> = kernel::io::mem::IoMem<'a, SZ_2M>; pub(crate) struct TyrDrmDriver; /// Convenience type alias for the DRM device type for this driver. pub(crate) type TyrDrmDevice = drm::Device; pub(crate) struct TyrPlatformDriver; #[pin_data(PinnedDrop)] pub(crate) struct TyrPlatformDriverData { _device: ARef, } #[pin_data] pub(crate) struct TyrDrmDeviceData { 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, } fn issue_soft_reset(dev: &Device, iomem: &IoMem<'_>) -> Result { iomem.write_reg(GPU_COMMAND::reset(ResetMode::SoftReset)); poll::read_poll_timeout( || Ok(iomem.read(GPU_IRQ_RAWSTAT)), |status| status.reset_completed(), time::Delta::from_millis(1), time::Delta::from_millis(100), ) .inspect_err(|_| dev_err!(dev, "GPU reset failed."))?; Ok(()) } kernel::of_device_table!( OF_TABLE, MODULE_OF_TABLE, ::IdInfo, [ (of::DeviceId::new(c"rockchip,rk3588-mali"), ()), (of::DeviceId::new(c"arm,mali-valhall-csf"), ()) ] ); impl platform::Driver for TyrPlatformDriver { type IdInfo = (); type Data<'bound> = TyrPlatformDriverData; const OF_ID_TABLE: Option> = Some(&OF_TABLE); fn probe<'bound>( pdev: &'bound platform::Device>, _info: Option<&'bound Self::IdInfo>, ) -> impl PinInit, Error> + 'bound { let core_clk = Clk::get(pdev.as_ref(), Some(c"core"))?; let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c"stacks"))?; let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c"coregroup"))?; core_clk.prepare_enable()?; stacks_clk.prepare_enable()?; coregroup_clk.prepare_enable()?; let mali_regulator = Regulator::::get(pdev.as_ref(), c"mali")?; let sram_regulator = Regulator::::get(pdev.as_ref(), c"sram")?; let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; let iomem = request.iomap_sized::()?; issue_soft_reset(pdev.as_ref(), &iomem)?; gpu::l2_power_on(pdev.as_ref(), &iomem)?; let gpu_info = GpuInfo::new(&iomem); gpu_info.log(pdev.as_ref()); let pa_bits = MMU_FEATURES::from_raw(gpu_info.mmu_features) .pa_bits() .get(); // SAFETY: No concurrent DMA allocations or mappings can be made because // the device is still being probed and therefore isn't being used by // other threads of execution. unsafe { pdev.dma_set_mask_and_coherent(DmaMask::try_new(pa_bits)?)? }; let platform: ARef = pdev.into(); let data = try_pin_init!(TyrDrmDeviceData { 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 = drm::UnregisteredDevice::::new(pdev.as_ref(), data)?; let tdev = drm::driver::Registration::new_foreign_owned(tdev, pdev.as_ref(), 0)?; let driver = TyrPlatformDriverData { _device: tdev.into(), }; // 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, "Tyr initialized correctly.\n"); Ok(driver) } } #[pinned_drop] impl PinnedDrop for TyrPlatformDriverData { fn drop(self: Pin<&mut Self>) {} } // 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"panthor", desc: c"ARM Mali Tyr DRM driver", }; #[vtable] impl drm::Driver for TyrDrmDriver { type Data = TyrDrmDeviceData; type File = TyrDrmFileData; type Object = drm::gem::shmem::Object; const INFO: drm::DriverInfo = INFO; const FEAT_RENDER: bool = true; kernel::declare_drm_ioctls! { (PANTHOR_DEV_QUERY, drm_panthor_dev_query, ioctl::RENDER_ALLOW, TyrDrmFileData::dev_query), } } struct Clocks { core: Clk, stacks: OptionalClk, coregroup: OptionalClk, } impl Drop for Clocks { fn drop(&mut self) { self.core.disable_unprepare(); self.stacks.disable_unprepare(); self.coregroup.disable_unprepare(); } } struct Regulators { _mali: Regulator, _sram: Regulator, }