diff --git a/Cargo.lock b/Cargo.lock index a64922d7d3..fe9dfefa57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -695,6 +695,7 @@ dependencies = [ "rk3588-pci", "rockchip-npu", "rockchip-pm", + "rockchip-rga", "rockchip-soc", "sdhci-host", "sdmmc-protocol", @@ -6782,6 +6783,14 @@ dependencies = [ "tock-registers 0.10.1", ] +[[package]] +name = "rockchip-rga" +version = "0.1.0" +dependencies = [ + "dma-api", + "rdif-base", +] + [[package]] name = "rockchip-soc" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 6131e8b149..3bf8d1856b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ members = [ "drivers/blk/*", "drivers/examples/*", "drivers/firmware/*", + "drivers/gpu/*", "drivers/intc/*", "drivers/interface/*", "drivers/net/*", @@ -242,6 +243,7 @@ rdif-timer = { version = "0.6.3", path = "drivers/interface/rdif-timer" } rdif-vsock = { version = "0.1.2", path = "drivers/interface/rdif-vsock" } rk3588-pci = { version = "0.2.3", path = "drivers/pci/rk3588-pci" } rockchip-npu = { version = "0.2.5", path = "drivers/npu/rockchip-npu" } +rockchip-rga = { version = "0.1.0", path = "drivers/gpu/rockchip-rga" } k230-kpu = { version = "0.1.0", path = "drivers/npu/k230-kpu" } realtek-rtl8125 = { version = "0.2.3", path = "drivers/net/realtek-rtl8125" } rockchip-pm = { version = "0.4.5", path = "drivers/soc/rockchip/rockchip-pm" } diff --git a/drivers/ax-driver/Cargo.toml b/drivers/ax-driver/Cargo.toml index c7c302fa1a..749b630bb1 100644 --- a/drivers/ax-driver/Cargo.toml +++ b/drivers/ax-driver/Cargo.toml @@ -50,6 +50,7 @@ xhci-pci = ["usb", "pci"] serial = ["plat-dyn", "dep:some-serial"] sg2002-placeholder = ["plat-dyn"] rknpu = ["plat-dyn", "rockchip-pm", "rockchip-soc", "dep:rockchip-npu"] +rga = ["plat-dyn", "dep:rockchip-rga"] rockchip-sdhci = [ "block", "plat-dyn", @@ -134,6 +135,7 @@ rdrive.workspace = true rdrive-macros.workspace = true rk3588-pci = { workspace = true, optional = true } rockchip-npu = { workspace = true, optional = true } +rockchip-rga = { workspace = true, optional = true } rockchip-pm = { workspace = true, optional = true } rockchip-soc = { workspace = true, optional = true } phytium-mci-host = { workspace = true, optional = true } diff --git a/drivers/ax-driver/src/lib.rs b/drivers/ax-driver/src/lib.rs index 710f29eb37..7587130451 100644 --- a/drivers/ax-driver/src/lib.rs +++ b/drivers/ax-driver/src/lib.rs @@ -70,6 +70,8 @@ pub mod vsock; #[cfg(feature = "pci")] pub mod pci; +#[cfg(feature = "rga")] +pub mod rga; #[cfg(feature = "rknpu")] pub mod rknpu; #[cfg(feature = "serial")] diff --git a/drivers/ax-driver/src/rga.rs b/drivers/ax-driver/src/rga.rs new file mode 100644 index 0000000000..99e342ccb9 --- /dev/null +++ b/drivers/ax-driver/src/rga.rs @@ -0,0 +1,88 @@ +use alloc::vec::Vec; + +use log::info; +use rdrive::{ + probe::OnProbeError, + register::{FdtInfo, ProbeFdt}, +}; +use rockchip_rga::{RgaCoreConfig, RgaCoreResource, RockchipRga}; + +use crate::mmio::iomap; + +crate::model_register!( + name: "Rockchip RGA", + level: ProbeLevel::PostKernel, + priority: ProbePriority::DEFAULT, + probe_kinds: &[ + ProbeKind::Fdt { + compatibles: &[ + "rockchip,rga3_core0", + "rockchip,rga3_core1", + "rockchip,rga2_core0", + ], + on_probe: probe + } + ], +); + +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); + let config = detect_core_config(&info) + .ok_or_else(|| OnProbeError::other("unsupported Rockchip RGA compatible string"))?; + + let mut resources = Vec::new(); + for reg in info.node.regs() { + let start_raw = reg.address as usize; + let size_raw = reg.size.unwrap_or(0x1000) as usize; + let (start, size, offset) = page_aligned_region(start_raw, size_raw); + let base = unsafe { iomap(start, size)?.add(offset) }; + + resources.push(RgaCoreResource { + base, + size: size_raw, + irq: decode_fdt_irq(&info.interrupts()), + config, + }); + } + + let dma = axklib::dma::device_with_mask(u32::MAX as u64); + let rga = RockchipRga::new(&resources, dma); + let core_count = rga.core_count(); + plat_dev.register(rga); + + info!("RGA registered successfully, cores={core_count}"); + Ok(()) +} + +fn detect_core_config(info: &FdtInfo<'_>) -> Option { + for compatible in info.node.as_node().compatibles() { + match compatible { + "rockchip,rga3_core0" => return Some(RgaCoreConfig::rga3(0)), + "rockchip,rga3_core1" => return Some(RgaCoreConfig::rga3(1)), + "rockchip,rga2_core0" => return Some(RgaCoreConfig::rga2(0)), + _ => {} + } + } + None +} + +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) +} + +fn decode_fdt_irq(interrupts: &[rdrive::probe::fdt::InterruptRef]) -> Option { + let interrupt = interrupts.first()?; + match interrupt.specifier.as_slice() { + [irq] => Some(*irq as usize), + [kind, irq, ..] => match *kind { + 0 => Some(*irq as usize + 32), + 1 => Some(*irq as usize + 16), + _ => Some(*irq as usize), + }, + _ => None, + } +} diff --git a/drivers/gpu/rockchip-rga/Cargo.toml b/drivers/gpu/rockchip-rga/Cargo.toml new file mode 100644 index 0000000000..c38df10557 --- /dev/null +++ b/drivers/gpu/rockchip-rga/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors.workspace = true +categories = ["embedded", "hardware-support", "no-std"] +description = "Low-level device layer for Rockchip RGA 2D accelerators" +edition.workspace = true +keywords = ["rk3588", "rga", "graphics"] +license.workspace = true +name = "rockchip-rga" +repository.workspace = true +version = "0.1.0" + +[dependencies] +dma-api.workspace = true +rdif-base.workspace = true diff --git a/drivers/gpu/rockchip-rga/src/command.rs b/drivers/gpu/rockchip-rga/src/command.rs new file mode 100644 index 0000000000..7d22f43083 --- /dev/null +++ b/drivers/gpu/rockchip-rga/src/command.rs @@ -0,0 +1,401 @@ +//! Dry-run command buffer construction for small RGA bring-up tests. +//! +//! The encoder builds the 32-word mode command block that old Rockchip RGA +//! hardware consumes through `RGA_CMD_BASE`. It does not submit the command to +//! hardware; board glue must still handle DMA allocation, cache sync, IRQ or +//! polling, clocks, and power. + +use crate::registers; + +pub const MIN_DIMENSION: u32 = 34; +pub const MAX_DIMENSION: u32 = 8192; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + AddressNotAligned, + AddressTooLarge, + InvalidDimensions, + InvalidStride, + SizeMismatch, +} + +pub type Result = core::result::Result; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PixelFormat { + Abgr8888, +} + +impl PixelFormat { + pub const fn bytes_per_pixel(self) -> u32 { + match self { + Self::Abgr8888 => 4, + } + } + + const fn hw_format(self) -> u32 { + match self { + Self::Abgr8888 => registers::COLOR_FMT_ABGR8888, + } + } + + const fn color_swap(self) -> u32 { + registers::COLOR_NONE_SWAP + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ImageLayout { + pub width: u32, + pub height: u32, + pub stride_bytes: u32, + pub format: PixelFormat, +} + +impl ImageLayout { + pub const fn new(width: u32, height: u32, stride_bytes: u32, format: PixelFormat) -> Self { + Self { + width, + height, + stride_bytes, + format, + } + } + + fn validate(self) -> Result<()> { + if !(MIN_DIMENSION..=MAX_DIMENSION).contains(&self.width) + || !(MIN_DIMENSION..=MAX_DIMENSION).contains(&self.height) + { + return Err(Error::InvalidDimensions); + } + + let min_stride = self + .width + .checked_mul(self.format.bytes_per_pixel()) + .ok_or(Error::InvalidStride)?; + if self.stride_bytes < min_stride || !self.stride_bytes.is_multiple_of(4) { + return Err(Error::InvalidStride); + } + + let stride_words = self.stride_words(); + if stride_words > 0x03ff { + return Err(Error::InvalidStride); + } + + Ok(()) + } + + const fn stride_words(self) -> u32 { + self.stride_bytes / 4 + } +} + +/// RGA-visible buffer mapping used by the dry-run encoder. +/// +/// `descriptor_dma` follows the old RGA local-MMU command format: command +/// words store a DMA address for a descriptor table, shifted right by 4. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BufferMapping { + pub descriptor_dma: u64, + pub y_offset: u32, + pub u_offset: u32, + pub v_offset: u32, +} + +impl BufferMapping { + pub const fn new(descriptor_dma: u64) -> Self { + Self { + descriptor_dma, + y_offset: 0, + u_offset: 0, + v_offset: 0, + } + } + + fn descriptor_base_word(self) -> Result { + if self.descriptor_dma & 0xf != 0 { + return Err(Error::AddressNotAligned); + } + let shifted = self.descriptor_dma >> 4; + if shifted > u32::MAX as u64 { + return Err(Error::AddressTooLarge); + } + Ok(shifted as u32) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ImageBuffer { + pub mapping: BufferMapping, + pub layout: ImageLayout, +} + +impl ImageBuffer { + pub const fn new(mapping: BufferMapping, layout: ImageLayout) -> Self { + Self { mapping, layout } + } + + fn validate(self) -> Result<()> { + self.mapping.descriptor_base_word()?; + self.layout.validate() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CopyCommand { + pub src: ImageBuffer, + pub dst: ImageBuffer, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct FillCommand { + pub dst: ImageBuffer, + pub color: u32, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CommandBuffer { + words: [u32; registers::CMD_BUFFER_WORDS], +} + +impl CommandBuffer { + pub const fn zeroed() -> Self { + Self { + words: [0; registers::CMD_BUFFER_WORDS], + } + } + + pub fn copy(command: CopyCommand) -> Result { + command.src.validate()?; + command.dst.validate()?; + if command.src.layout.width != command.dst.layout.width + || command.src.layout.height != command.dst.layout.height + { + return Err(Error::SizeMismatch); + } + + let mut buffer = Self::zeroed(); + buffer.set_common_dst(command.dst)?; + buffer.set_src(command.src)?; + buffer.set_register( + registers::MMU_SRC1_BASE, + command.dst.mapping.descriptor_base_word()?, + ); + buffer.set_register( + registers::MMU_CTRL1, + registers::MMU_SRC_ENABLE | registers::MMU_SRC1_ENABLE | registers::MMU_DST_ENABLE, + ); + buffer.set_register( + registers::MODE_CTRL, + encode_mode( + registers::MODE_RENDER_BITBLT, + registers::MODE_BITBLT_SRC_TO_DST, + ), + ); + Ok(buffer) + } + + pub fn fill(command: FillCommand) -> Result { + command.dst.validate()?; + + let mut buffer = Self::zeroed(); + buffer.set_common_dst(command.dst)?; + buffer.set_register(registers::SRC_BG_COLOR, command.color); + buffer.set_register( + registers::MODE_CTRL, + encode_mode(registers::MODE_RENDER_RECTANGLE_FILL, 0), + ); + buffer.set_register(registers::MMU_CTRL1, registers::MMU_DST_ENABLE); + Ok(buffer) + } + + pub fn words(&self) -> &[u32; registers::CMD_BUFFER_WORDS] { + &self.words + } + + pub fn register(&self, register: usize) -> Option { + command_index(register).map(|index| self.words[index]) + } + + fn set_src(&mut self, image: ImageBuffer) -> Result<()> { + self.set_register( + registers::MMU_SRC_BASE, + image.mapping.descriptor_base_word()?, + ); + self.set_register(registers::SRC_Y_RGB_BASE_ADDR, image.mapping.y_offset); + self.set_register(registers::SRC_CB_BASE_ADDR, image.mapping.u_offset); + self.set_register(registers::SRC_CR_BASE_ADDR, image.mapping.v_offset); + self.set_register(registers::SRC_INFO, encode_src_info(image.layout.format)); + self.set_register(registers::SRC_VIR_INFO, encode_src_vir_info(image.layout)); + self.set_register(registers::SRC_ACT_INFO, encode_src_act_info(image.layout)); + self.set_register(registers::SRC_X_FACTOR, 0); + self.set_register(registers::SRC_Y_FACTOR, 0); + Ok(()) + } + + fn set_common_dst(&mut self, image: ImageBuffer) -> Result<()> { + self.set_register( + registers::MMU_DST_BASE, + image.mapping.descriptor_base_word()?, + ); + self.set_register(registers::DST_Y_RGB_BASE_ADDR, image.mapping.y_offset); + self.set_register(registers::DST_CB_BASE_ADDR, image.mapping.u_offset); + self.set_register(registers::DST_CR_BASE_ADDR, image.mapping.v_offset); + self.set_register(registers::DST_INFO, encode_dst_info(image.layout.format)); + self.set_register(registers::DST_VIR_INFO, encode_dst_vir_info(image.layout)); + self.set_register(registers::DST_ACT_INFO, encode_dst_act_info(image.layout)); + Ok(()) + } + + fn set_register(&mut self, register: usize, value: u32) { + if let Some(index) = command_index(register) { + self.words[index] = value; + } + } +} + +fn command_index(register: usize) -> Option { + let offset = register.checked_sub(registers::MODE_BASE)?; + if offset % core::mem::size_of::() != 0 { + return None; + } + let index = offset / core::mem::size_of::(); + (index < registers::CMD_BUFFER_WORDS).then_some(index) +} + +const fn encode_mode(render: u32, bitblt: u32) -> u32 { + (render & 0x7) | ((bitblt & 0x1) << 3) | (1 << 6) +} + +const fn encode_src_info(format: PixelFormat) -> u32 { + format.hw_format() | (format.color_swap() << 4) +} + +const fn encode_dst_info(format: PixelFormat) -> u32 { + format.hw_format() | (format.color_swap() << 4) +} + +const fn encode_src_vir_info(layout: ImageLayout) -> u32 { + let stride = layout.stride_words(); + (stride & 0x7fff) | ((stride & 0x03ff) << 16) +} + +const fn encode_src_act_info(layout: ImageLayout) -> u32 { + ((layout.width - 1) & 0x1fff) | (((layout.height - 1) & 0x1fff) << 16) +} + +const fn encode_dst_vir_info(layout: ImageLayout) -> u32 { + layout.stride_words() & 0x7fff +} + +const fn encode_dst_act_info(layout: ImageLayout) -> u32 { + ((layout.width - 1) & 0x0fff) | (((layout.height - 1) & 0x0fff) << 16) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn layout() -> ImageLayout { + ImageLayout::new(64, 48, 256, PixelFormat::Abgr8888) + } + + #[test] + fn copy_command_encodes_addresses_and_geometry() { + let src = ImageBuffer::new(BufferMapping::new(0x1000), layout()); + let dst = ImageBuffer::new(BufferMapping::new(0x2000), layout()); + + let command = CommandBuffer::copy(CopyCommand { src, dst }).unwrap(); + + assert_eq!(command.register(registers::MMU_SRC_BASE), Some(0x100)); + assert_eq!(command.register(registers::MMU_SRC1_BASE), Some(0x200)); + assert_eq!(command.register(registers::MMU_DST_BASE), Some(0x200)); + assert_eq!( + command.register(registers::MMU_CTRL1), + Some( + registers::MMU_SRC_ENABLE | registers::MMU_SRC1_ENABLE | registers::MMU_DST_ENABLE + ) + ); + assert_eq!(command.register(registers::SRC_INFO), Some(0)); + assert_eq!(command.register(registers::DST_INFO), Some(0)); + assert_eq!( + command.register(registers::SRC_VIR_INFO), + Some(64 | (64 << 16)) + ); + assert_eq!(command.register(registers::DST_VIR_INFO), Some(64)); + assert_eq!( + command.register(registers::SRC_ACT_INFO), + Some(63 | (47 << 16)) + ); + assert_eq!( + command.register(registers::DST_ACT_INFO), + Some(63 | (47 << 16)) + ); + assert_eq!( + command.register(registers::MODE_CTRL), + Some(encode_mode( + registers::MODE_RENDER_BITBLT, + registers::MODE_BITBLT_SRC_TO_DST, + )) + ); + } + + #[test] + fn fill_command_encodes_destination_and_color() { + let dst = ImageBuffer::new(BufferMapping::new(0x3000), layout()); + + let command = CommandBuffer::fill(FillCommand { + dst, + color: 0xff00_00ff, + }) + .unwrap(); + + assert_eq!(command.register(registers::MMU_DST_BASE), Some(0x300)); + assert_eq!( + command.register(registers::MMU_CTRL1), + Some(registers::MMU_DST_ENABLE) + ); + assert_eq!(command.register(registers::SRC_BG_COLOR), Some(0xff00_00ff)); + assert_eq!( + command.register(registers::MODE_CTRL), + Some(encode_mode(registers::MODE_RENDER_RECTANGLE_FILL, 0)) + ); + } + + #[test] + fn rejects_unaligned_descriptor_address() { + let src = ImageBuffer::new(BufferMapping::new(0x1001), layout()); + let dst = ImageBuffer::new(BufferMapping::new(0x2000), layout()); + + assert_eq!( + CommandBuffer::copy(CopyCommand { src, dst }), + Err(Error::AddressNotAligned) + ); + } + + #[test] + fn rejects_stride_smaller_than_pixel_row() { + let bad_layout = ImageLayout::new(64, 48, 128, PixelFormat::Abgr8888); + let src = ImageBuffer::new(BufferMapping::new(0x1000), bad_layout); + let dst = ImageBuffer::new(BufferMapping::new(0x2000), layout()); + + assert_eq!( + CommandBuffer::copy(CopyCommand { src, dst }), + Err(Error::InvalidStride) + ); + } + + #[test] + fn rejects_copy_size_mismatch() { + let src = ImageBuffer::new(BufferMapping::new(0x1000), layout()); + let dst = ImageBuffer::new( + BufferMapping::new(0x2000), + ImageLayout::new(80, 48, 320, PixelFormat::Abgr8888), + ); + + assert_eq!( + CommandBuffer::copy(CopyCommand { src, dst }), + Err(Error::SizeMismatch) + ); + } +} diff --git a/drivers/gpu/rockchip-rga/src/lib.rs b/drivers/gpu/rockchip-rga/src/lib.rs new file mode 100644 index 0000000000..c4a1168896 --- /dev/null +++ b/drivers/gpu/rockchip-rga/src/lib.rs @@ -0,0 +1,156 @@ +//! Low-level building blocks for Rockchip RGA 2D accelerators. +//! +//! This crate intentionally starts with the smallest hardware-facing shape: +//! mapped RGA cores plus a DMA capability. Operation submission will be added +//! once the register programming path is verified against RK3588 hardware. + +#![no_std] + +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::NonNull; + +use dma_api::DeviceDma; +use rdif_base::DriverGeneric; + +pub mod command; +pub mod registers; + +/// Rockchip RGA hardware generation known by this driver. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RgaVersion { + Rga2, + Rga3, +} + +/// Static description for one RGA core. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RgaCoreConfig { + pub version: RgaVersion, + pub core_index: u8, +} + +impl RgaCoreConfig { + pub const fn rga2(core_index: u8) -> Self { + Self { + version: RgaVersion::Rga2, + core_index, + } + } + + pub const fn rga3(core_index: u8) -> Self { + Self { + version: RgaVersion::Rga3, + core_index, + } + } +} + +/// OS-glue supplied resource for one mapped RGA core. +#[derive(Debug, Clone, Copy)] +pub struct RgaCoreResource { + pub base: NonNull, + pub size: usize, + pub irq: Option, + pub config: RgaCoreConfig, +} + +/// One mapped RGA core. +#[derive(Debug)] +pub struct RgaCore { + base: NonNull, + size: usize, + irq: Option, + config: RgaCoreConfig, +} + +/// Raw hardware version decoded from the RGA version register. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RgaHardwareVersion { + pub raw: u32, + pub major: u8, + pub minor: u8, +} + +// The pointer is an MMIO base owned by platform glue. Access to mutable device +// state is kept behind `&mut self` in higher-level operations. +unsafe impl Send for RgaCore {} + +impl RgaCore { + pub fn new(resource: RgaCoreResource) -> Self { + Self { + base: resource.base, + size: resource.size, + irq: resource.irq, + config: resource.config, + } + } + + pub fn base(&self) -> NonNull { + self.base + } + + pub fn size(&self) -> usize { + self.size + } + + pub fn irq(&self) -> Option { + self.irq + } + + pub fn config(&self) -> RgaCoreConfig { + self.config + } + + pub fn read_version_info(&self) -> RgaHardwareVersion { + let raw = self.read32(registers::VERSION_INFO); + RgaHardwareVersion { + raw, + major: ((raw >> 24) & 0xff) as u8, + minor: ((raw >> 20) & 0x0f) as u8, + } + } + + fn read32(&self, offset: usize) -> u32 { + debug_assert_eq!(offset % core::mem::size_of::(), 0); + unsafe { self.base.as_ptr().add(offset).cast::().read_volatile() } + } +} + +/// Rockchip RGA device containing one or more hardware cores. +pub struct RockchipRga { + cores: Vec, + dma: DeviceDma, +} + +impl RockchipRga { + pub fn new(resources: &[RgaCoreResource], dma: DeviceDma) -> Self { + Self { + cores: resources.iter().copied().map(RgaCore::new).collect(), + dma, + } + } + + pub fn core_count(&self) -> usize { + self.cores.len() + } + + pub fn core(&self, index: usize) -> Option<&RgaCore> { + self.cores.get(index) + } + + pub fn cores(&self) -> &[RgaCore] { + &self.cores + } + + pub fn dma(&self) -> &DeviceDma { + &self.dma + } +} + +impl DriverGeneric for RockchipRga { + fn name(&self) -> &str { + "rockchip-rga" + } +} diff --git a/drivers/gpu/rockchip-rga/src/registers.rs b/drivers/gpu/rockchip-rga/src/registers.rs new file mode 100644 index 0000000000..c2bbd5aa0a --- /dev/null +++ b/drivers/gpu/rockchip-rga/src/registers.rs @@ -0,0 +1,47 @@ +//! Minimal RGA register offsets used by the bring-up path. + +pub const CMD_BUFFER_WORDS: usize = 0x20; + +pub const SYS_CTRL: usize = 0x0000; +pub const CMD_CTRL: usize = 0x0004; +pub const CMD_BASE: usize = 0x0008; +pub const INT: usize = 0x0010; +pub const VERSION_INFO: usize = 0x0028; + +pub const MODE_BASE: usize = 0x0100; +pub const MODE_MAX: usize = 0x017c; + +pub const MODE_CTRL: usize = 0x0100; +pub const SRC_INFO: usize = 0x0104; +pub const SRC_Y_RGB_BASE_ADDR: usize = 0x0108; +pub const SRC_CB_BASE_ADDR: usize = 0x010c; +pub const SRC_CR_BASE_ADDR: usize = 0x0110; +pub const SRC_VIR_INFO: usize = 0x0118; +pub const SRC_ACT_INFO: usize = 0x011c; +pub const SRC_X_FACTOR: usize = 0x0120; +pub const SRC_Y_FACTOR: usize = 0x0124; +pub const SRC_BG_COLOR: usize = 0x0128; +pub const SRC_FG_COLOR: usize = 0x012c; +pub const DST_INFO: usize = 0x0138; +pub const DST_Y_RGB_BASE_ADDR: usize = 0x013c; +pub const DST_CB_BASE_ADDR: usize = 0x0140; +pub const DST_CR_BASE_ADDR: usize = 0x0144; +pub const DST_VIR_INFO: usize = 0x0148; +pub const DST_ACT_INFO: usize = 0x014c; +pub const ALPHA_CTRL0: usize = 0x0150; +pub const ALPHA_CTRL1: usize = 0x0154; +pub const MMU_CTRL1: usize = 0x016c; +pub const MMU_SRC_BASE: usize = 0x0170; +pub const MMU_SRC1_BASE: usize = 0x0174; +pub const MMU_DST_BASE: usize = 0x0178; + +pub const MODE_RENDER_BITBLT: u32 = 0; +pub const MODE_RENDER_RECTANGLE_FILL: u32 = 2; +pub const MODE_BITBLT_SRC_TO_DST: u32 = 0; + +pub const COLOR_FMT_ABGR8888: u32 = 0; +pub const COLOR_NONE_SWAP: u32 = 0; + +pub const MMU_SRC_ENABLE: u32 = 0x7; +pub const MMU_SRC1_ENABLE: u32 = 0x7 << 4; +pub const MMU_DST_ENABLE: u32 = 0x7 << 8;