// SPDX-License-Identifier: GPL-2.0 or MIT use kernel::{ clk::{ Clk, OptionalClk, // }, device::{ Bound, Core, Device, DeviceContext, // }, dma::{ Device as DmaDevice, DmaMask, // }, drm, drm::ioctl, io::{ poll, Io, // }, new_mutex, of, platform, prelude::*, regulator, regulator::Regulator, sizes::SZ_2M, sync::{ Arc, Mutex, // }, time, // }; use crate::{ file::TyrDrmFileData, fw::Firmware, gem::Bo, gpu, gpu::GpuInfo, mmu::Mmu, 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<'bound> { _reg: drm::Registration<'bound, TyrDrmDriver>, } /// Data owned by the DRM [`Registration`]. /// /// This data can have references tied to the parent platform device binding scope /// and is accessible only while the DRM device is registered with userspace. #[pin_data] pub(crate) struct TyrDrmRegistrationData<'drm> { /// Parent platform device. pub(crate) pdev: &'drm platform::Device, /// Firmware sections. pub(crate) fw: Firmware<'drm>, #[pin] clks: Mutex, #[pin] regulators: Mutex, /// GPU MMIO register mapping. pub(crate) iomem: Arc>, /// GPU information read from hardware during probe. 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, ::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<'bound>; 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 = Arc::new(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(&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 unreg_dev = drm::UnregisteredDevice::::new(pdev, Ok(()))?; let mmu = Mmu::new(pdev.as_ref(), iomem.as_arc_borrow(), &gpu_info)?; let firmware = Firmware::new( pdev.as_ref(), iomem.clone(), &unreg_dev, mmu.as_arc_borrow(), &gpu_info, )?; firmware.boot()?; let reg_data = pin_init!(TyrDrmRegistrationData { pdev, fw: firmware, clks <- new_mutex!(Clocks { core: core_clk, stacks: stacks_clk, coregroup: coregroup_clk, }), regulators <- new_mutex!(Regulators { _mali: mali_regulator, _sram: sram_regulator, }), iomem, gpu_info, }); // SAFETY: `reg` is stored in `TyrPlatformDriverData` and dropped when the driver is // unbound; it is never forgotten. let reg = unsafe { drm::Registration::new(pdev.as_ref(), unreg_dev, reg_data, 0)? }; let driver = TyrPlatformDriverData { _reg: reg }; dev_dbg!(pdev, "Tyr initialized correctly."); 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 = (); type RegistrationData<'drm> = TyrDrmRegistrationData<'drm>; type File = TyrDrmFileData; type Object = Bo; type ParentDevice = platform::Device; 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, }