| // SPDX-License-Identifier: GPL-2.0 or MIT |
| |
| 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::io::poll; |
| 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::aref::ARef; |
| use kernel::sync::Arc; |
| use kernel::sync::Mutex; |
| use kernel::time; |
| |
| 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<SZ_2M>; |
| |
| /// Convenience type alias for the DRM device type for this driver. |
| pub(crate) type TyrDevice = drm::Device<TyrDriver>; |
| |
| #[pin_data(PinnedDrop)] |
| pub(crate) struct TyrDriver { |
| _device: ARef<TyrDevice>, |
| } |
| |
| #[pin_data(PinnedDrop)] |
| pub(crate) struct TyrData { |
| pub(crate) pdev: ARef<platform::Device>, |
| |
| #[pin] |
| clks: Mutex<Clocks>, |
| |
| #[pin] |
| regulators: Mutex<Regulators>, |
| |
| /// 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<Bound>, iomem: &Devres<IoMem>) -> Result { |
| regs::GPU_CMD.write(dev, iomem, regs::GPU_CMD_SOFT_RESET)?; |
| |
| poll::read_poll_timeout( |
| || regs::GPU_IRQ_RAWSTAT.read(dev, iomem), |
| |status| *status & regs::GPU_IRQ_RAWSTAT_RESET_COMPLETED != 0, |
| 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, |
| <TyrDriver as platform::Driver>::IdInfo, |
| [ |
| (of::DeviceId::new(c"rockchip,rk3588-mali"), ()), |
| (of::DeviceId::new(c"arm,mali-valhall-csf"), ()) |
| ] |
| ); |
| |
| impl platform::Driver for TyrDriver { |
| type IdInfo = (); |
| const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); |
| |
| fn probe( |
| pdev: &platform::Device<Core>, |
| _info: Option<&Self::IdInfo>, |
| ) -> impl PinInit<Self, Error> { |
| 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::pin_init(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(pdev.as_ref(), &iomem)?; |
| gpu_info.log(pdev); |
| |
| let platform: ARef<platform::Device> = 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<TyrDevice> = drm::Device::new(pdev.as_ref(), data)?; |
| drm::driver::Registration::new_foreign_owned(&tdev, pdev.as_ref(), 0)?; |
| |
| let driver = TyrDriver { _device: tdev }; |
| |
| // 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 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"panthor", |
| desc: c"ARM Mali Tyr DRM driver", |
| }; |
| |
| #[vtable] |
| impl drm::Driver for TyrDriver { |
| type Data = TyrData; |
| type File = File; |
| type Object = drm::gem::Object<TyrObject>; |
| |
| 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<regulator::Enabled>, |
| _sram: Regulator<regulator::Enabled>, |
| } |