1#![doc = crate::before_snippet!()]
18use core::{
54 fmt::Formatter,
55 marker::PhantomData,
56 mem::{ManuallyDrop, size_of},
57 ops::{Deref, DerefMut},
58};
59
60use crate::{
61 Blocking,
62 DriverMode,
63 clock::Clocks,
64 dma::{ChannelTx, DmaError, DmaPeripheral, DmaTxBuffer, PeripheralTxChannel, TxChannelFor},
65 gpio::{
66 OutputConfig,
67 OutputSignal,
68 interconnect::{self, PeripheralOutput},
69 },
70 lcd_cam::{
71 BitOrder,
72 ByteOrder,
73 ClockError,
74 Instance,
75 LCD_DONE_WAKER,
76 Lcd,
77 calculate_clkm,
78 lcd::{ClockMode, DelayMode, Phase, Polarity},
79 },
80 pac,
81 peripherals::LCD_CAM,
82 system::{self, GenericPeripheralGuard},
83 time::Rate,
84};
85
86#[derive(Debug, Clone, Copy, PartialEq)]
88#[cfg_attr(feature = "defmt", derive(defmt::Format))]
89pub enum ConfigError {
90 Clock(ClockError),
92}
93
94pub struct I8080<'d, Dm: DriverMode> {
96 lcd_cam: LCD_CAM<'d>,
97 tx_channel: ChannelTx<Blocking, PeripheralTxChannel<LCD_CAM<'d>>>,
98 _guard: GenericPeripheralGuard<{ system::Peripheral::LcdCam as u8 }>,
99 _mode: PhantomData<Dm>,
100}
101
102impl<'d, Dm> I8080<'d, Dm>
103where
104 Dm: DriverMode,
105{
106 pub fn new(
108 lcd: Lcd<'d, Dm>,
109 channel: impl TxChannelFor<LCD_CAM<'d>>,
110 mut pins: impl TxPins,
111 config: Config,
112 ) -> Result<Self, ConfigError> {
113 let tx_channel = ChannelTx::new(channel.degrade());
114
115 let mut this = Self {
116 lcd_cam: lcd.lcd_cam,
117 tx_channel,
118 _guard: lcd._guard,
119 _mode: PhantomData,
120 };
121
122 this.apply_config(&config)?;
123 pins.configure();
124
125 Ok(this)
126 }
127
128 fn regs(&self) -> &pac::lcd_cam::RegisterBlock {
129 self.lcd_cam.register_block()
130 }
131
132 pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
139 let clocks = Clocks::get();
140 let (i, divider) = calculate_clkm(
144 (config.frequency.as_hz() * 2) as _,
145 &[
146 clocks.xtal_clock.as_hz() as _,
147 clocks.cpu_clock.as_hz() as _,
148 clocks.crypto_pwm_clock.as_hz() as _,
149 ],
150 )
151 .map_err(ConfigError::Clock)?;
152
153 self.regs().lcd_clock().write(|w| unsafe {
154 w.clk_en().set_bit();
156 w.lcd_clk_sel().bits((i + 1) as _);
157 w.lcd_clkm_div_num().bits(divider.div_num as _);
158 w.lcd_clkm_div_b().bits(divider.div_b as _);
159 w.lcd_clkm_div_a().bits(divider.div_a as _); w.lcd_clk_equ_sysclk().clear_bit();
161 w.lcd_clkcnt_n().bits(2 - 1); w.lcd_ck_idle_edge()
163 .bit(config.clock_mode.polarity == Polarity::IdleHigh);
164 w.lcd_ck_out_edge()
165 .bit(config.clock_mode.phase == Phase::ShiftHigh)
166 });
167
168 self.regs()
169 .lcd_ctrl()
170 .write(|w| w.lcd_rgb_mode_en().clear_bit());
171 self.regs()
172 .lcd_rgb_yuv()
173 .write(|w| w.lcd_conv_bypass().clear_bit());
174
175 self.regs().lcd_user().modify(|_, w| {
176 w.lcd_8bits_order().bit(false);
177 w.lcd_bit_order().bit(false);
178 w.lcd_byte_order().bit(false);
179 w.lcd_2byte_en().bit(false)
180 });
181 self.regs().lcd_misc().write(|w| unsafe {
182 w.lcd_afifo_threshold_num().bits(0);
184 w.lcd_vfk_cyclelen()
187 .bits(config.setup_cycles.saturating_sub(1) as _);
188 w.lcd_vbk_cyclelen()
191 .bits(config.hold_cycles.saturating_sub(1) as _);
192 w.lcd_next_frame_en().clear_bit();
195 w.lcd_bk_en().set_bit();
197 w.lcd_cd_data_set()
200 .bit(config.cd_data_edge != config.cd_idle_edge);
201 w.lcd_cd_dummy_set()
204 .bit(config.cd_dummy_edge != config.cd_idle_edge);
205 w.lcd_cd_cmd_set()
208 .bit(config.cd_cmd_edge != config.cd_idle_edge);
209 w.lcd_cd_idle_edge().bit(config.cd_idle_edge)
211 });
212 self.regs()
213 .lcd_dly_mode()
214 .write(|w| unsafe { w.lcd_cd_mode().bits(config.cd_mode as u8) });
215 self.regs().lcd_data_dout_mode().write(|w| unsafe {
216 w.dout0_mode().bits(config.output_bit_mode as u8);
217 w.dout1_mode().bits(config.output_bit_mode as u8);
218 w.dout2_mode().bits(config.output_bit_mode as u8);
219 w.dout3_mode().bits(config.output_bit_mode as u8);
220 w.dout4_mode().bits(config.output_bit_mode as u8);
221 w.dout5_mode().bits(config.output_bit_mode as u8);
222 w.dout6_mode().bits(config.output_bit_mode as u8);
223 w.dout7_mode().bits(config.output_bit_mode as u8);
224 w.dout8_mode().bits(config.output_bit_mode as u8);
225 w.dout9_mode().bits(config.output_bit_mode as u8);
226 w.dout10_mode().bits(config.output_bit_mode as u8);
227 w.dout11_mode().bits(config.output_bit_mode as u8);
228 w.dout12_mode().bits(config.output_bit_mode as u8);
229 w.dout13_mode().bits(config.output_bit_mode as u8);
230 w.dout14_mode().bits(config.output_bit_mode as u8);
231 w.dout15_mode().bits(config.output_bit_mode as u8)
232 });
233
234 self.regs()
235 .lcd_user()
236 .modify(|_, w| w.lcd_update().set_bit());
237
238 Ok(())
239 }
240
241 pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self {
245 let is_inverted = byte_order != ByteOrder::default();
246 self.regs()
247 .lcd_user()
248 .modify(|_, w| w.lcd_byte_order().bit(is_inverted));
249 self
250 }
251
252 pub fn set_8bits_order(&mut self, byte_order: ByteOrder) -> &mut Self {
256 let is_inverted = byte_order != ByteOrder::default();
257 self.regs()
258 .lcd_user()
259 .modify(|_, w| w.lcd_8bits_order().bit(is_inverted));
260 self
261 }
262
263 pub fn set_bit_order(&mut self, bit_order: BitOrder) -> &mut Self {
265 self.regs()
266 .lcd_user()
267 .modify(|_, w| w.lcd_bit_order().bit(bit_order != BitOrder::default()));
268 self
269 }
270
271 pub fn with_cs(self, cs: impl PeripheralOutput<'d>) -> Self {
273 let cs = cs.into();
274
275 cs.apply_output_config(&OutputConfig::default());
276 cs.set_output_enable(true);
277
278 OutputSignal::LCD_CS.connect_to(&cs);
279
280 self
281 }
282
283 pub fn with_ctrl_pins(
285 self,
286 dc: impl PeripheralOutput<'d>,
287 wrx: impl PeripheralOutput<'d>,
288 ) -> Self {
289 let dc = dc.into();
290 let wrx = wrx.into();
291
292 dc.apply_output_config(&OutputConfig::default());
293 dc.set_output_enable(true);
294 OutputSignal::LCD_DC.connect_to(&dc);
295
296 wrx.apply_output_config(&OutputConfig::default());
297 wrx.set_output_enable(true);
298 OutputSignal::LCD_PCLK.connect_to(&wrx);
299
300 self
301 }
302
303 pub fn send<W: Into<u16> + Copy, BUF: DmaTxBuffer>(
312 mut self,
313 cmd: impl Into<Command<W>>,
314 dummy: u8,
315 mut data: BUF,
316 ) -> Result<I8080Transfer<'d, BUF, Dm>, (DmaError, Self, BUF)> {
317 let cmd = cmd.into();
318
319 self.regs()
321 .lcd_user()
322 .modify(|_, w| w.lcd_reset().set_bit());
323 self.regs()
324 .lcd_misc()
325 .modify(|_, w| w.lcd_afifo_reset().set_bit());
326
327 match cmd {
329 Command::None => {
330 self.regs()
331 .lcd_user()
332 .modify(|_, w| w.lcd_cmd().clear_bit());
333 }
334 Command::One(value) => {
335 self.regs().lcd_user().modify(|_, w| {
336 w.lcd_cmd().set_bit();
337 w.lcd_cmd_2_cycle_en().clear_bit()
338 });
339 self.regs()
340 .lcd_cmd_val()
341 .write(|w| unsafe { w.lcd_cmd_value().bits(value.into() as _) });
342 }
343 Command::Two(first, second) => {
344 self.regs().lcd_user().modify(|_, w| {
345 w.lcd_cmd().set_bit();
346 w.lcd_cmd_2_cycle_en().set_bit()
347 });
348 let cmd = first.into() as u32 | ((second.into() as u32) << 16);
349 self.regs()
350 .lcd_cmd_val()
351 .write(|w| unsafe { w.lcd_cmd_value().bits(cmd) });
352 }
353 }
354
355 let is_2byte_mode = size_of::<W>() == 2;
356 self.regs().lcd_user().modify(|_, w| unsafe {
357 if dummy > 0 {
359 w.lcd_dummy()
361 .set_bit()
362 .lcd_dummy_cyclelen()
364 .bits((dummy - 1) as _)
365 } else {
366 w.lcd_dummy().clear_bit()
367 }
368 .lcd_2byte_en()
369 .bit(is_2byte_mode)
370 });
371
372 self.regs()
378 .lcd_user()
379 .modify(|_, w| w.lcd_always_out_en().set_bit().lcd_dout().set_bit());
380
381 let result = unsafe {
382 self.tx_channel
383 .prepare_transfer(DmaPeripheral::LcdCam, &mut data)
384 }
385 .and_then(|_| self.tx_channel.start_transfer());
386 if let Err(err) = result {
387 return Err((err, self, data));
388 }
389
390 self.regs()
392 .lc_dma_int_clr()
393 .write(|w| w.lcd_trans_done_int_clr().set_bit());
394
395 self.regs().lcd_user().modify(|_, w| {
396 w.lcd_update().set_bit();
397 w.lcd_start().set_bit()
398 });
399
400 Ok(I8080Transfer {
401 i8080: ManuallyDrop::new(self),
402 buf_view: ManuallyDrop::new(data.into_view()),
403 })
404 }
405}
406
407impl<Dm: DriverMode> core::fmt::Debug for I8080<'_, Dm> {
408 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
409 f.debug_struct("I8080").finish()
410 }
411}
412
413pub struct I8080Transfer<'d, BUF: DmaTxBuffer, Dm: DriverMode> {
416 i8080: ManuallyDrop<I8080<'d, Dm>>,
417 buf_view: ManuallyDrop<BUF::View>,
418}
419
420impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> I8080Transfer<'d, BUF, Dm> {
421 pub fn is_done(&self) -> bool {
423 self.i8080
424 .regs()
425 .lcd_user()
426 .read()
427 .lcd_start()
428 .bit_is_clear()
429 }
430
431 pub fn cancel(mut self) -> (I8080<'d, Dm>, BUF) {
433 self.stop_peripherals();
434 let (_, i8080, buf) = self.wait();
435 (i8080, buf)
436 }
437
438 pub fn wait(mut self) -> (Result<(), DmaError>, I8080<'d, Dm>, BUF) {
443 while !self.is_done() {}
444
445 self.i8080
447 .regs()
448 .lc_dma_int_clr()
449 .write(|w| w.lcd_trans_done_int_clr().set_bit());
450
451 let (i8080, view) = unsafe {
454 let i8080 = ManuallyDrop::take(&mut self.i8080);
455 let view = ManuallyDrop::take(&mut self.buf_view);
456 core::mem::forget(self);
457 (i8080, view)
458 };
459
460 let result = if i8080.tx_channel.has_error() {
461 Err(DmaError::DescriptorError)
462 } else {
463 Ok(())
464 };
465
466 (result, i8080, BUF::from_view(view))
467 }
468
469 fn stop_peripherals(&mut self) {
470 self.i8080
472 .regs()
473 .lcd_user()
474 .modify(|_, w| w.lcd_start().clear_bit());
475
476 self.i8080.tx_channel.stop_transfer();
478 }
479}
480
481impl<BUF: DmaTxBuffer, Dm: DriverMode> Deref for I8080Transfer<'_, BUF, Dm> {
482 type Target = BUF::View;
483
484 fn deref(&self) -> &Self::Target {
485 &self.buf_view
486 }
487}
488
489impl<BUF: DmaTxBuffer, Dm: DriverMode> DerefMut for I8080Transfer<'_, BUF, Dm> {
490 fn deref_mut(&mut self) -> &mut Self::Target {
491 &mut self.buf_view
492 }
493}
494
495impl<BUF: DmaTxBuffer> I8080Transfer<'_, BUF, crate::Async> {
496 pub async fn wait_for_done(&mut self) {
498 use core::{
499 future::Future,
500 pin::Pin,
501 task::{Context, Poll},
502 };
503
504 struct LcdDoneFuture {}
505
506 impl Future for LcdDoneFuture {
507 type Output = ();
508
509 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
510 if Instance::is_lcd_done_set() {
511 Poll::Ready(())
516 } else {
517 LCD_DONE_WAKER.register(cx.waker());
518 Instance::listen_lcd_done();
519 Poll::Pending
520 }
521 }
522 }
523
524 impl Drop for LcdDoneFuture {
525 fn drop(&mut self) {
526 Instance::unlisten_lcd_done();
527 }
528 }
529
530 LcdDoneFuture {}.await
531 }
532}
533
534impl<BUF: DmaTxBuffer, Dm: DriverMode> Drop for I8080Transfer<'_, BUF, Dm> {
535 fn drop(&mut self) {
536 self.stop_peripherals();
537
538 let view = unsafe {
541 ManuallyDrop::drop(&mut self.i8080);
542 ManuallyDrop::take(&mut self.buf_view)
543 };
544 let _ = BUF::from_view(view);
545 }
546}
547
548#[derive(Debug, Clone, Copy, PartialEq, procmacros::BuilderLite)]
549#[cfg_attr(feature = "defmt", derive(defmt::Format))]
550pub struct Config {
552 clock_mode: ClockMode,
554
555 frequency: Rate,
557
558 setup_cycles: usize,
560
561 hold_cycles: usize,
563
564 cd_idle_edge: bool,
566 cd_cmd_edge: bool,
568 cd_dummy_edge: bool,
570 cd_data_edge: bool,
572
573 cd_mode: DelayMode,
575 output_bit_mode: DelayMode,
577}
578
579impl Default for Config {
580 fn default() -> Self {
581 Self {
582 clock_mode: Default::default(),
583 frequency: Rate::from_mhz(20),
584 setup_cycles: 1,
585 hold_cycles: 1,
586 cd_idle_edge: false,
587 cd_cmd_edge: false,
588 cd_dummy_edge: false,
589 cd_data_edge: true,
590 cd_mode: Default::default(),
591 output_bit_mode: Default::default(),
592 }
593 }
594}
595
596#[derive(Debug, Clone, Copy, PartialEq)]
600#[cfg_attr(feature = "defmt", derive(defmt::Format))]
601pub enum Command<T> {
602 None,
604 One(T),
606 Two(T, T),
608}
609
610impl From<u8> for Command<u8> {
611 fn from(value: u8) -> Self {
612 Command::One(value)
613 }
614}
615
616impl From<u16> for Command<u16> {
617 fn from(value: u16) -> Self {
618 Command::One(value)
619 }
620}
621
622pub struct TxEightBits<'d> {
625 pins: [interconnect::OutputSignal<'d>; 8],
626}
627
628impl<'d> TxEightBits<'d> {
629 #[allow(clippy::too_many_arguments)]
630 pub fn new(
632 pin_0: impl PeripheralOutput<'d>,
633 pin_1: impl PeripheralOutput<'d>,
634 pin_2: impl PeripheralOutput<'d>,
635 pin_3: impl PeripheralOutput<'d>,
636 pin_4: impl PeripheralOutput<'d>,
637 pin_5: impl PeripheralOutput<'d>,
638 pin_6: impl PeripheralOutput<'d>,
639 pin_7: impl PeripheralOutput<'d>,
640 ) -> Self {
641 Self {
642 pins: [
643 pin_0.into(),
644 pin_1.into(),
645 pin_2.into(),
646 pin_3.into(),
647 pin_4.into(),
648 pin_5.into(),
649 pin_6.into(),
650 pin_7.into(),
651 ],
652 }
653 }
654}
655
656impl TxPins for TxEightBits<'_> {
657 fn configure(&mut self) {
658 const SIGNALS: [OutputSignal; 8] = [
659 OutputSignal::LCD_DATA_0,
660 OutputSignal::LCD_DATA_1,
661 OutputSignal::LCD_DATA_2,
662 OutputSignal::LCD_DATA_3,
663 OutputSignal::LCD_DATA_4,
664 OutputSignal::LCD_DATA_5,
665 OutputSignal::LCD_DATA_6,
666 OutputSignal::LCD_DATA_7,
667 ];
668
669 for (pin, signal) in self.pins.iter().zip(SIGNALS.into_iter()) {
670 pin.apply_output_config(&OutputConfig::default());
671 pin.set_output_enable(true);
672 signal.connect_to(pin);
673 }
674 }
675}
676
677pub struct TxSixteenBits<'d> {
680 pins: [interconnect::OutputSignal<'d>; 16],
681}
682
683impl<'d> TxSixteenBits<'d> {
684 #[allow(clippy::too_many_arguments)]
685 pub fn new(
687 pin_0: impl PeripheralOutput<'d>,
688 pin_1: impl PeripheralOutput<'d>,
689 pin_2: impl PeripheralOutput<'d>,
690 pin_3: impl PeripheralOutput<'d>,
691 pin_4: impl PeripheralOutput<'d>,
692 pin_5: impl PeripheralOutput<'d>,
693 pin_6: impl PeripheralOutput<'d>,
694 pin_7: impl PeripheralOutput<'d>,
695 pin_8: impl PeripheralOutput<'d>,
696 pin_9: impl PeripheralOutput<'d>,
697 pin_10: impl PeripheralOutput<'d>,
698 pin_11: impl PeripheralOutput<'d>,
699 pin_12: impl PeripheralOutput<'d>,
700 pin_13: impl PeripheralOutput<'d>,
701 pin_14: impl PeripheralOutput<'d>,
702 pin_15: impl PeripheralOutput<'d>,
703 ) -> Self {
704 Self {
705 pins: [
706 pin_0.into(),
707 pin_1.into(),
708 pin_2.into(),
709 pin_3.into(),
710 pin_4.into(),
711 pin_5.into(),
712 pin_6.into(),
713 pin_7.into(),
714 pin_8.into(),
715 pin_9.into(),
716 pin_10.into(),
717 pin_11.into(),
718 pin_12.into(),
719 pin_13.into(),
720 pin_14.into(),
721 pin_15.into(),
722 ],
723 }
724 }
725}
726
727impl TxPins for TxSixteenBits<'_> {
728 fn configure(&mut self) {
729 const SIGNALS: [OutputSignal; 16] = [
730 OutputSignal::LCD_DATA_0,
731 OutputSignal::LCD_DATA_1,
732 OutputSignal::LCD_DATA_2,
733 OutputSignal::LCD_DATA_3,
734 OutputSignal::LCD_DATA_4,
735 OutputSignal::LCD_DATA_5,
736 OutputSignal::LCD_DATA_6,
737 OutputSignal::LCD_DATA_7,
738 OutputSignal::LCD_DATA_8,
739 OutputSignal::LCD_DATA_9,
740 OutputSignal::LCD_DATA_10,
741 OutputSignal::LCD_DATA_11,
742 OutputSignal::LCD_DATA_12,
743 OutputSignal::LCD_DATA_13,
744 OutputSignal::LCD_DATA_14,
745 OutputSignal::LCD_DATA_15,
746 ];
747
748 for (pin, signal) in self.pins.iter_mut().zip(SIGNALS.into_iter()) {
749 pin.apply_output_config(&OutputConfig::default());
750 pin.set_output_enable(true);
751 signal.connect_to(pin);
752 }
753 }
754}
755
756#[doc(hidden)]
757pub trait TxPins {
758 fn configure(&mut self);
759}