mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-12 07:44:18 +00:00
bug fixes to frame buffer driver for cramium target
now works, and works pretty well.
This commit is contained in:
parent
37d35b8e27
commit
f06ceea5e1
1 changed files with 46 additions and 38 deletions
|
|
@ -63,6 +63,8 @@
|
|||
//! is a fairly long setup/hold time requirement on it and there does not seem
|
||||
//! to be a provision in the UDMA block to put guardbands around the CS timing.
|
||||
|
||||
use core::mem::size_of;
|
||||
|
||||
use cram_hal_service::IoxHal;
|
||||
use cramium_hal::udma::PeriphId;
|
||||
use cramium_hal::{iox, udma};
|
||||
|
|
@ -75,7 +77,10 @@ pub const FB_WIDTH_WORDS: usize = 11;
|
|||
pub const FB_WIDTH_PIXELS: usize = WIDTH as usize;
|
||||
pub const FB_LINES: usize = LINES as usize;
|
||||
pub const FB_SIZE: usize = FB_WIDTH_WORDS * FB_LINES; // 44 bytes by 536 lines
|
||||
const CONFIG_CLOCK_FREQUENCY: u32 = 100_000_000;
|
||||
|
||||
// TODO: need to confirm actual clock sent to UDMA block, but empirically, this is what I see
|
||||
// on the FPGA emulator
|
||||
const CONFIG_CLOCK_FREQUENCY: u32 = 50_000_000;
|
||||
|
||||
const SPI_CS_PIN: u8 = 10;
|
||||
const SPI_CLK_PIN: u8 = 7;
|
||||
|
|
@ -117,8 +122,6 @@ impl XousDisplay {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - then convert the line below to create a Spim, instead of an IframRange...
|
||||
let udma_global = cram_hal_service::UdmaGlobal::new();
|
||||
// using bank SPIM_B[1]
|
||||
udma_global.udma_clock_config(PeriphId::Spim1, true);
|
||||
|
|
@ -129,13 +132,15 @@ impl XousDisplay {
|
|||
cramium_hal::udma::Spim::new(
|
||||
udma::SpimChannel::Channel1,
|
||||
2_000_000,
|
||||
100_000_000,
|
||||
CONFIG_CLOCK_FREQUENCY,
|
||||
udma::SpimClkPol::LeadingEdgeRise,
|
||||
udma::SpimClkPha::CaptureOnTrailing,
|
||||
udma::SpimClkPha::CaptureOnLeading,
|
||||
udma::SpimCs::Cs0,
|
||||
3,
|
||||
2,
|
||||
None,
|
||||
// one extra line for handling the addressing setup
|
||||
FB_SIZE + FB_WIDTH_WORDS * 4,
|
||||
(FB_LINES + 1) * FB_WIDTH_WORDS * size_of::<u32>(),
|
||||
0,
|
||||
)
|
||||
.expect("Couldn't allocate SPI channel for LCD")
|
||||
|
|
@ -165,12 +170,14 @@ impl XousDisplay {
|
|||
Some(iox::IoxEnable::Enable),
|
||||
Some(iox::IoxDriveStrength::Drive2mA),
|
||||
);
|
||||
// SPIM_SCSN0_B[1] - as GPIO
|
||||
// SPIM_SCSN0_B[1]
|
||||
// chip select toggle by UDMA has ~6 cycles setup and 1 cycles hold time, which
|
||||
// meets the requirements for the display.
|
||||
iox.setup_io_pin(
|
||||
SPI_PORT,
|
||||
SPI_CS_PIN,
|
||||
Some(iox::IoxDir::Output),
|
||||
Some(iox::IoxFunction::Gpio),
|
||||
Some(iox::IoxFunction::AF2),
|
||||
None,
|
||||
Some(iox::IoxEnable::Enable),
|
||||
Some(iox::IoxEnable::Enable),
|
||||
|
|
@ -197,7 +204,7 @@ impl XousDisplay {
|
|||
/// The addresses of these structures are passed as `u32` and unsafely cast back
|
||||
/// into pointers on the user's side. We do this because the panic handler is special:
|
||||
/// it grabs ahold of the low-level hardware, yanking control from the higher level
|
||||
/// control functons, without having to map its own separate pages.
|
||||
/// control functions, without having to map its own separate pages.
|
||||
///
|
||||
/// Of course, "anyone" with a copy of this data can clobber existing graphics operations. Thus,
|
||||
/// any access to these registers have to be protected with a mutex of some form. In the case of
|
||||
|
|
@ -269,7 +276,7 @@ impl XousDisplay {
|
|||
// this code is safe because u32 is representable on the system
|
||||
let fb = unsafe { self.fb.as_slice_mut::<u32>() };
|
||||
// copy to the soft frame buffer
|
||||
fb.copy_from_slice(bmp);
|
||||
fb[..bmp.len()].copy_from_slice(bmp);
|
||||
// now copy for DMA
|
||||
for line_no in 0..FB_LINES {
|
||||
self.copy_line_to_dma(line_no);
|
||||
|
|
@ -288,51 +295,52 @@ impl XousDisplay {
|
|||
/// Copies a display line to the DMA buffer, while setting up all the bits for
|
||||
/// the DMA operation. Manages the DMA line pointer as well.
|
||||
fn copy_line_to_dma(&mut self, src_line: usize) {
|
||||
// safety: this is safe because the underlying data types (u32) are representable on the system
|
||||
unsafe {
|
||||
let hwfb = self.spim.tx_buf_mut();
|
||||
let fb = self.fb.as_slice::<u32>();
|
||||
// set the mode and address
|
||||
// the very first line is unused, except for the mode & address info
|
||||
// this is done just to keep the math easy for computing strides & alignments
|
||||
hwfb[(self.next_free_line + 1) * FB_WIDTH_WORDS - 1] =
|
||||
(hwfb[(self.next_free_line + 1) * FB_WIDTH_WORDS - 1] & 0x0000_FFFFF)
|
||||
| ((src_line as u32) << 6)
|
||||
| 0b001;
|
||||
// now copy the data
|
||||
hwfb[(self.next_free_line + 1) * FB_WIDTH_WORDS..(self.next_free_line + 2) * FB_WIDTH_WORDS]
|
||||
.copy_from_slice(&fb[src_line * FB_WIDTH_WORDS..(src_line + 1) * FB_WIDTH_WORDS]);
|
||||
if self.devboot && src_line == 7 {
|
||||
for w in hwfb
|
||||
[(self.next_free_line + 1) * FB_WIDTH_WORDS..(self.next_free_line + 2) * FB_WIDTH_WORDS]
|
||||
.iter_mut()
|
||||
{
|
||||
*w = *w & 0xCCCC_CCCC; // hash over the status line
|
||||
}
|
||||
let hwfb = self.spim.tx_buf_mut();
|
||||
// safety: this is safe because `u32` has no invalid values
|
||||
let fb = unsafe { self.fb.as_slice::<u32>() };
|
||||
// set the mode and address
|
||||
// the very first line is unused, except for the mode & address info
|
||||
// this is done just to keep the math easy for computing strides & alignments
|
||||
hwfb[(self.next_free_line + 1) * FB_WIDTH_WORDS - 1] =
|
||||
(hwfb[(self.next_free_line + 1) * FB_WIDTH_WORDS - 1] & 0x0000_FFFF)
|
||||
| (((src_line as u32) << 6) | 0b001) << 16;
|
||||
// now copy the data
|
||||
hwfb[(self.next_free_line + 1) * FB_WIDTH_WORDS..(self.next_free_line + 2) * FB_WIDTH_WORDS]
|
||||
.copy_from_slice(&fb[src_line * FB_WIDTH_WORDS..(src_line + 1) * FB_WIDTH_WORDS]);
|
||||
if self.devboot && src_line == 7 {
|
||||
for w in hwfb
|
||||
[(self.next_free_line + 1) * FB_WIDTH_WORDS..(self.next_free_line + 2) * FB_WIDTH_WORDS]
|
||||
.iter_mut()
|
||||
{
|
||||
*w = *w & 0xCCCC_CCCC; // hash over the status line
|
||||
}
|
||||
}
|
||||
|
||||
if self.next_free_line < LINES as usize {
|
||||
self.next_free_line += 1;
|
||||
} else {
|
||||
log::warn!(
|
||||
"Line overflow in DMA detected. Suspect missing `update_dirty` call. Further lines will overwrite the last line."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_dirty(&mut self) {
|
||||
if self.next_free_line != 0 {
|
||||
// manual toggle of CS pins is necessary in part to meet setup/hold time of the LCD
|
||||
self.iox.set_gpio_pin_value(SPI_PORT, SPI_CS_PIN, iox::IoxValue::High);
|
||||
// safety: this function is safe to call because:
|
||||
// - `is_virtual` is `false` => data should be a physical buffer that is pre-populated with the
|
||||
// transmit data this is done by `copy_line_to_dma()`
|
||||
// - the `data` argument is a physical buffer slice, which is only used as a base/bounds
|
||||
// argument
|
||||
unsafe {
|
||||
self.spim.tx_data_async_from_parts::<u32>(
|
||||
FB_WIDTH_WORDS - 1,
|
||||
(self.next_free_line + 1) * FB_WIDTH_WORDS,
|
||||
false,
|
||||
self.spim.tx_data_async_from_parts::<u16>(
|
||||
FB_WIDTH_WORDS * 2 - 1,
|
||||
// +1 for the trailing dummy bits
|
||||
self.next_free_line * FB_WIDTH_WORDS * 2 + 1,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
self.iox.set_gpio_pin_value(SPI_PORT, SPI_CS_PIN, iox::IoxValue::Low);
|
||||
self.next_free_line = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue