use crate::error::Error;
use crate::interface::Interface;
use crate::models::Model;
use crate::EVE;
pub enum ClockSource {
Internal,
External,
}
#[derive(Debug)]
pub struct VideoTimings {
pub sysclk_freq: ClockFrequency,
pub pclk_div: u8,
pub pclk_pol: ClockPolarity,
pub horiz: VideoTimingDimension,
pub vert: VideoTimingDimension,
}
#[derive(Debug)]
pub struct VideoTimingDimension {
pub total: u16,
pub visible: u16,
pub offset: u16,
pub sync_start: u16,
pub sync_end: u16,
}
impl VideoTimings {
pub const MODE_720P: Self = Self {
sysclk_freq: ClockFrequency::F72MHz,
pclk_div: 1,
pclk_pol: ClockPolarity::RisingEdge,
horiz: VideoTimingDimension::calculate(1280, 110, 40, 220),
vert: VideoTimingDimension::calculate(720, 5, 5, 370),
};
}
impl VideoTimingDimension {
pub const fn calculate(active: u16, front_porch: u16, sync: u16, back_porch: u16) -> Self {
Self {
total: (active + front_porch + sync + back_porch) & DIMENSION_MASK,
visible: (active) & DIMENSION_MASK,
offset: (front_porch + sync + back_porch) & DIMENSION_MASK,
sync_start: (front_porch) & DIMENSION_MASK,
sync_end: (front_porch + sync) & DIMENSION_MASK,
}
}
}
#[derive(Debug, Default)]
pub struct RGBElectricalMode {
pclk_spread: bool,
channel_bits: (u8, u8, u8),
dither: bool,
}
impl RGBElectricalMode {
pub fn new() -> Self {
core::default::Default::default()
}
pub fn pclk_spread<'a>(&'a mut self, v: bool) -> &'a mut Self {
self.pclk_spread = v;
self
}
pub fn channel_bits<'a>(&'a mut self, r: u8, g: u8, b: u8) -> &'a mut Self {
self.channel_bits = (r, g, b);
self
}
pub fn dither<'a>(&'a mut self, v: bool) -> &'a mut Self {
self.dither = v;
self
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ClockPolarity {
RisingEdge,
FallingEdge,
}
impl ClockPolarity {
pub const fn reg_pclk_pol_value(self) -> u8 {
match self {
Self::RisingEdge => 0,
Self::FallingEdge => 1,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ClockFrequency {
F24MHz,
F36MHz,
F48MHz,
F60MHz,
F72MHz,
}
impl ClockFrequency {
pub const DEFAULT_SYSCLK_FREQ: Self = Self::F60MHz;
pub const fn cmd_clksel_args(self) -> (u8, u8) {
match self {
ClockFrequency::F24MHz => (2, 0),
ClockFrequency::F36MHz => (3, 0),
ClockFrequency::F48MHz => (4, 0),
ClockFrequency::F60MHz => (0, 0), ClockFrequency::F72MHz => (6, 0),
}
}
pub const fn reg_frequency_value(self) -> u32 {
match self {
ClockFrequency::F24MHz => 24000000,
ClockFrequency::F36MHz => 36000000,
ClockFrequency::F48MHz => 48000000,
ClockFrequency::F60MHz => 60000000,
ClockFrequency::F72MHz => 72000000,
}
}
}
pub const fn dimension_is_valid(v: u16) -> bool {
(v & !DIMENSION_MASK) == 0
}
const DIMENSION_MASK: u16 = 0b0000111111111111;
pub(crate) fn activate_system_clock<M: Model, I: Interface>(
eve: &mut EVE<M, I>,
source: ClockSource,
video: &VideoTimings,
) -> Result<(), Error<I>> {
use crate::host_commands::HostCmd::*;
let ll = &mut eve.ll;
{
let ei = ll.borrow_interface();
crate::low_level::LowLevel::<M, I>::result(ei.reset())?
};
ll.host_command(PWRDOWN, 0, 0)?;
ll.host_command(ACTIVE, 0, 0)?;
ll.host_command(SLEEP, 0, 0)?;
match source {
ClockSource::Internal => {
ll.host_command(CLKINT, 0, 0)?;
}
ClockSource::External => {
ll.host_command(CLKEXT, 0, 0)?;
}
}
{
let clksel = video.sysclk_freq.cmd_clksel_args();
ll.host_command(CLKSEL, clksel.0, clksel.1)?;
}
ll.host_command(ACTIVE, 0, 0)?;
ll.host_command(RST_PULSE, 0, 0)?;
Ok(())
}
pub(crate) fn poll_for_boot<M: Model, I: Interface>(
eve: &mut EVE<M, I>,
poll_limit: u32,
) -> Result<bool, Error<I>> {
use crate::registers::Register::*;
let ll = &mut eve.ll;
let mut poll = 0;
while poll < poll_limit {
let v = ll.rd8(ll.reg_ptr(ID))?;
if v == 0x7c {
break;
}
poll += 1;
}
while poll < poll_limit {
let v = ll.rd8(ll.reg_ptr(CPURESET))?;
if v == 0x00 {
return Ok(true);
}
poll += 1;
}
return Ok(false);
}
pub(crate) fn activate_pixel_clock<M: Model, I: Interface>(
eve: &mut EVE<M, I>,
c: &VideoTimings,
) -> Result<(), Error<I>> {
use crate::registers::Register::*;
const DIM_MASK: u16 = DIMENSION_MASK;
let ll = &mut eve.ll;
ll.wr32(M::reg_ptr(FREQUENCY), c.sysclk_freq.reg_frequency_value())?;
ll.wr16(M::reg_ptr(VSYNC0), c.vert.sync_start & DIM_MASK)?;
ll.wr16(M::reg_ptr(VSYNC1), c.vert.sync_end & DIM_MASK)?;
ll.wr16(M::reg_ptr(VSIZE), c.vert.visible & DIM_MASK)?;
ll.wr16(M::reg_ptr(VOFFSET), c.vert.offset & DIM_MASK)?;
ll.wr16(M::reg_ptr(VCYCLE), c.vert.total & DIM_MASK)?;
ll.wr16(M::reg_ptr(HSYNC0), c.horiz.sync_start & DIM_MASK)?;
ll.wr16(M::reg_ptr(HSYNC1), c.horiz.sync_end & DIM_MASK)?;
ll.wr16(M::reg_ptr(HSIZE), c.horiz.visible & DIM_MASK)?;
ll.wr16(M::reg_ptr(HOFFSET), c.horiz.offset & DIM_MASK)?;
ll.wr16(M::reg_ptr(HCYCLE), c.horiz.total & DIM_MASK)?;
ll.wr8(M::reg_ptr(PCLK_POL), c.pclk_pol.reg_pclk_pol_value())?;
ll.wr8(M::reg_ptr(PCLK), c.pclk_div)?;
Ok(())
}
pub(crate) fn configure_video_pins<M: Model, I: Interface>(
eve: &mut EVE<M, I>,
_mode: &RGBElectricalMode,
) -> Result<(), Error<I>> {
use crate::registers::Register::*;
let ll = &mut eve.ll;
ll.wr8(M::reg_ptr(OUTBITS), 0)?;
ll.wr8(M::reg_ptr(DITHER), 0)?;
ll.wr8(M::reg_ptr(SWIZZLE), 0)?;
ll.wr8(M::reg_ptr(CSPREAD), 0)?;
ll.wr8(M::reg_ptr(ADAPTIVE_FRAMERATE), 0)?;
ll.wr8(M::reg_ptr(GPIO), 0x83)?;
Ok(())
}