1use core::marker::PhantomData;
9
10use crate::{
11 Async,
12 Blocking,
13 asynch::AtomicWaker,
14 clock::dividers::FractionalDivider,
15 handler,
16 interrupt::InterruptHandler,
17 lcd_cam::{cam::Cam, lcd::Lcd},
18 peripherals::{Interrupt, LCD_CAM},
19 system::{Cpu, GenericPeripheralGuard},
20};
21
22pub mod cam;
23pub mod lcd;
24
25#[diagnostic::on_unimplemented(
29 message = "The DMA channel cannot be used as a TX channel for LCD",
30 label = "This DMA channel"
31)]
32pub trait LcdDmaTxChannel<'d>: Into<ErasedTxChannel<'d>> + crate::private::Sealed {}
33
34#[diagnostic::on_unimplemented(
38 message = "The DMA channel cannot be used as an RX channel for Camera",
39 label = "This DMA channel"
40)]
41pub trait CamDmaRxChannel<'d>: Into<ErasedRxChannel<'d>> + crate::private::Sealed {}
42
43with_lcd_cam_dma_engine! {
44 ($engine:tt, $any_channel:tt) => {
45 type ErasedTxChannel<'d> = <crate::dma::$any_channel<'d> as crate::dma::DmaChannel>::Tx;
46 type ErasedRxChannel<'d> = <crate::dma::$any_channel<'d> as crate::dma::DmaChannel>::Rx;
47
48 crate::macros::impl_dma_channel_trait! {
49 $engine,
50 peri = LCD_CAM,
51 ($peri:path, $ch:path) => {
52 impl<'d> LcdDmaTxChannel<'d> for $ch {}
53 impl<'d> CamDmaRxChannel<'d> for $ch {}
54 }
55 }
56
57 impl<'d> LcdDmaTxChannel<'d> for ErasedTxChannel<'d> {}
60 impl<'d> CamDmaRxChannel<'d> for ErasedRxChannel<'d> {}
61 };
62}
63
64pub struct LcdCam<'d, Dm: crate::DriverMode> {
66 pub lcd: Lcd<'d, Dm>,
68 pub cam: Cam<'d>,
70}
71
72impl<'d> LcdCam<'d, Blocking> {
73 pub fn new(lcd_cam: LCD_CAM<'d>) -> Self {
75 let lcd_guard = GenericPeripheralGuard::new();
76 let cam_guard = GenericPeripheralGuard::new();
77
78 Self {
79 lcd: Lcd {
80 inner: lcd::Inner {
81 lcd_cam: unsafe { lcd_cam.clone_unchecked() },
82 _guard: lcd_guard,
83 clock_requested: false,
84 },
85 _mode: PhantomData,
86 },
87 cam: Cam {
88 lcd_cam,
89 _guard: cam_guard,
90 clock_requested: false,
91 },
92 }
93 }
94
95 pub fn into_async(mut self) -> LcdCam<'d, Async> {
97 self.set_interrupt_handler(interrupt_handler);
98 LcdCam {
99 lcd: self.lcd.into_async(),
100 cam: self.cam,
101 }
102 }
103
104 #[instability::unstable]
108 pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
109 for core in crate::system::Cpu::other() {
110 crate::interrupt::disable(core, Interrupt::LCD_CAM);
111 }
112 crate::interrupt::bind_handler(Interrupt::LCD_CAM, handler);
113 }
114}
115
116impl crate::private::Sealed for LcdCam<'_, Blocking> {}
117#[instability::unstable]
120impl crate::interrupt::InterruptConfigurable for LcdCam<'_, Blocking> {
121 fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
122 self.set_interrupt_handler(handler);
123 }
124}
125
126impl<'d> LcdCam<'d, Async> {
127 pub fn into_blocking(self) -> LcdCam<'d, Blocking> {
129 crate::interrupt::disable(Cpu::current(), Interrupt::LCD_CAM);
130 LcdCam {
131 lcd: self.lcd.into_blocking(),
132 cam: self.cam,
133 }
134 }
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Default)]
139#[cfg_attr(feature = "defmt", derive(defmt::Format))]
140pub enum BitOrder {
141 #[default]
143 Native = 0,
144 Inverted = 1,
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Default)]
150#[cfg_attr(feature = "defmt", derive(defmt::Format))]
151pub enum ByteOrder {
152 #[default]
154 Native = 0,
155 Inverted = 1,
157}
158
159pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new();
160
161#[handler]
162fn interrupt_handler() {
163 if Instance::is_lcd_done_set() {
165 Instance::unlisten_lcd_done();
166 LCD_DONE_WAKER.wake()
167 }
168}
169
170pub(crate) struct Instance;
171
172impl Instance {
176 fn enable_listenlcd_done(en: bool) {
177 LCD_CAM::regs()
178 .lc_dma_int_ena()
179 .modify(|_, w| w.lcd_trans_done_int_ena().bit(en));
180 }
181
182 pub(crate) fn listen_lcd_done() {
183 Self::enable_listenlcd_done(true);
184 }
185
186 pub(crate) fn unlisten_lcd_done() {
187 Self::enable_listenlcd_done(false);
188 }
189
190 pub(crate) fn is_lcd_done_set() -> bool {
191 LCD_CAM::regs()
192 .lc_dma_int_raw()
193 .read()
194 .lcd_trans_done_int_raw()
195 .bit()
196 }
197}
198pub(crate) struct ClockDivider {
199 pub div_num: u32,
201
202 pub div_b: u32,
204
205 pub div_a: u32,
207}
208
209impl ClockDivider {
210 fn new(divider: FractionalDivider) -> Self {
211 Self {
212 div_num: divider.integer,
213 div_b: divider.numerator,
214 div_a: divider.denominator.max(1),
217 }
218 }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq)]
223#[cfg_attr(feature = "defmt", derive(defmt::Format))]
224pub enum ClockError {
225 FrequencyTooLow,
227}
228
229pub(crate) fn calculate_clkm(
230 desired_frequency: u32,
231 source_frequencies: &[u32],
232) -> Result<(usize, ClockDivider), ClockError> {
233 let mut result_error = 0;
234 let mut result = None;
235
236 for (i, &source_frequency) in source_frequencies.iter().enumerate() {
237 let Some(divider) = calculate_closest_divider(source_frequency, desired_frequency) else {
238 continue;
239 };
240
241 let error = divider
244 .output_frequency(source_frequency)
245 .abs_diff(desired_frequency);
246 if result.is_none() || error < result_error {
247 result = Some((i, divider));
248 result_error = error;
249 }
250 }
251
252 let (index, divider) = result.ok_or(ClockError::FrequencyTooLow)?;
253
254 Ok((index, ClockDivider::new(divider)))
255}
256
257fn calculate_closest_divider(
258 source_frequency: u32,
259 desired_frequency: u32,
260) -> Option<FractionalDivider> {
261 let (min_divider, max_divider) = property!("clock_tree.lcd_cam.lcd_clock.div_num");
263 let (_, max_denominator) = property!("clock_tree.lcd_cam.lcd_clock.div_a");
264
265 if source_frequency / desired_frequency < min_divider {
266 return Some(FractionalDivider {
269 integer: min_divider,
270 numerator: 0,
271 denominator: 0,
272 });
273 }
274
275 let divider = FractionalDivider::new(source_frequency, desired_frequency, max_denominator);
276
277 (divider.integer <= max_divider).then_some(divider)
279}