use core::ptr::NonNull;
use log::info;
use rdrive::{
probe::{
OnProbeError,
fdt::{ClockLine, ResetLine},
},
register::ProbeFdt,
};
use rockchip_jpeg::RockchipJpeg;
use crate::mmio::iomap;
const IOMMU_OFFSET: usize = 0x480;
const RK_MMU_DTE_ADDR: usize = 0x00;
const RK_MMU_STATUS: usize = 0x04;
const RK_MMU_COMMAND: usize = 0x08;
const RK_MMU_INT_MASK: usize = 0x1c;
const RK_MMU_CMD_DISABLE_PAGING: u32 = 1;
const RK_MMU_CMD_FORCE_RESET: u32 = 6;
#[cfg(feature = "jpu-selftest")]
const SELFTEST_TIMEOUT_NS: u64 = 200_000_000;
crate::model_register!(
name: "Rockchip JPEG Decoder",
level: ProbeLevel::PostKernel,
priority: ProbePriority::DEFAULT,
probe_kinds: &[
ProbeKind::Fdt {
compatibles: &["rockchip,rkv-jpeg-decoder-v1"],
on_probe: probe
}
],
);
fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> {
let (info, plat_dev) = probe.into_parts();
let reg = info
.node
.regs()
.into_iter()
.next()
.ok_or_else(|| OnProbeError::other("jpegd node has no reg"))?;
let start_raw = reg.address as usize;
let size_raw = reg.size.unwrap_or(0x400) as usize;
let (start, size, offset) = page_aligned_region(start_raw, size_raw);
let base = unsafe { iomap(start, size)?.add(offset) };
let clocks = info.clock_lines()?;
let resets = info.reset_lines()?;
bring_up_clocks_and_resets(&clocks, &resets);
bypass_iommu(base);
let dma = axklib::dma::device_with_mask(u32::MAX as u64);
let jpeg = RockchipJpeg::new(base, dma);
info!(
"JPEG decoder probed: base={start_raw:#x} id={:#010x}",
jpeg.read_id()
);
#[cfg(feature = "jpu-selftest")]
let jpeg = {
let mut jpeg = jpeg;
run_selftest(&mut jpeg);
jpeg
};
plat_dev.register(jpeg);
info!("JPEG decoder registered");
Ok(())
}
fn bring_up_clocks_and_resets(clocks: &[ClockLine], resets: &[ResetLine]) {
for clock in clocks {
if let Err(e) = clock.enable() {
info!(
"JPEG: enable clock {:?} ({:#x}) failed (continuing): {e}",
clock.name(),
clock.id().raw()
);
}
}
for reset in resets {
if let Err(e) = reset.reset() {
info!(
"JPEG: pulse reset {:?} ({:#x}) failed (continuing): {e}",
reset.name(),
reset.id().raw()
);
}
}
}
fn bypass_iommu(base: NonNull<u8>) {
unsafe {
let mmu = base.as_ptr().add(IOMMU_OFFSET);
mmu.add(RK_MMU_COMMAND)
.cast::<u32>()
.write_volatile(RK_MMU_CMD_FORCE_RESET);
for _ in 0..1000 {
if mmu.add(RK_MMU_STATUS).cast::<u32>().read_volatile() == 0 {
break;
}
core::hint::spin_loop();
}
mmu.add(RK_MMU_DTE_ADDR).cast::<u32>().write_volatile(0);
mmu.add(RK_MMU_COMMAND)
.cast::<u32>()
.write_volatile(RK_MMU_CMD_DISABLE_PAGING);
mmu.add(RK_MMU_INT_MASK).cast::<u32>().write_volatile(0);
}
}
#[cfg(feature = "jpu-selftest")]
fn run_selftest(jpeg: &mut RockchipJpeg) {
let mut clock = axklib::time::monotonic_nanos;
let mut buf = [0u8; rockchip_jpeg::SELFTEST_JPEG_CAPACITY];
let Some(len) = rockchip_jpeg::write_selftest_jpeg(&mut buf) else {
info!("JPU_SELFTEST_FAIL: could not encode the self-test JPEG");
return;
};
match jpeg.decode_jpeg(&buf[..len], &mut clock, SELFTEST_TIMEOUT_NS) {
Ok(status) if status.is_success() => {
info!("JPU_SELFTEST_PASS reg1={:#010x}", status.raw())
}
Ok(status) => info!(
"JPU_SELFTEST_FAIL (not done, no error bit) reg1={:#010x}",
status.raw()
),
Err(e) => info!("JPU_SELFTEST_FAIL: {e:?}"),
}
}
fn page_aligned_region(start_raw: usize, size_raw: usize) -> (usize, usize, usize) {
let page_size = 0x1000;
let start = start_raw & !(page_size - 1);
let offset = start_raw - start;
let end = (start_raw + size_raw + page_size - 1) & !(page_size - 1);
(start, end - start, offset)
}
pub use rockchip_jpeg::{JpuError, mpp, registers};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
NotFound,
Busy,
Timeout,
Decode,
}
pub fn is_available() -> bool {
rdrive::get_one::<RockchipJpeg>().is_some()
}
pub fn read_id() -> Result<u32, Error> {
let dev = rdrive::get_one::<RockchipJpeg>()
.ok_or(Error::NotFound)?
.try_lock()
.map_err(|_| Error::Busy)?;
Ok(dev.read_id())
}
pub fn run_raw(
regs: &[u32; registers::REG_COUNT],
readback: &mut [u32; registers::REG_COUNT],
timeout_ns: u64,
) -> Result<(), Error> {
let mut dev = rdrive::get_one::<RockchipJpeg>()
.ok_or(Error::NotFound)?
.try_lock()
.map_err(|_| Error::Busy)?;
let mut clock = axklib::time::monotonic_nanos;
dev.core()
.run_raw(regs, readback, &mut clock, timeout_ns)
.map(|_| ())
.map_err(|e| match e {
JpuError::DecodeTimeout => Error::Timeout,
_ => Error::Decode,
})
}