| // 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<Ctx = drm::Normal> = drm::Device<TyrDrmDriver, Ctx>; | |
| 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<Bound>, | |
| /// Firmware sections. | |
| pub(crate) fw: Firmware<'drm>, | |
| #[pin] | |
| clks: Mutex<Clocks>, | |
| #[pin] | |
| regulators: Mutex<Regulators>, | |
| /// GPU MMIO register mapping. | |
| pub(crate) iomem: Arc<IoMem<'drm>>, | |
| /// 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, | |
| <TyrPlatformDriver as platform::Driver>::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<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); | |
| fn probe<'bound>( | |
| pdev: &'bound platform::Device<Core<'_>>, | |
| _info: Option<&'bound Self::IdInfo>, | |
| ) -> impl PinInit<Self::Data<'bound>, 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::<regulator::Enabled>::get(pdev.as_ref(), c"mali")?; | |
| let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?; | |
| let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; | |
| let iomem = Arc::new(request.iomap_sized::<SZ_2M>()?, 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::<TyrDrmDriver>::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<Ctx: DeviceContext> = platform::Device<Ctx>; | |
| 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<regulator::Enabled>, | |
| _sram: Regulator<regulator::Enabled>, | |
| } |