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]
109 pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
110 for core in crate::system::Cpu::other() {
111 crate::interrupt::disable(core, Interrupt::LCD_CAM);
112 }
113 crate::interrupt::bind_handler(Interrupt::LCD_CAM, handler);
114 }
115}
116
117impl crate::private::Sealed for LcdCam<'_, Blocking> {}
118#[instability::unstable]
121impl crate::interrupt::InterruptConfigurable for LcdCam<'_, Blocking> {
122 fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
123 self.set_interrupt_handler(handler);
124 }
125}
126
127impl<'d> LcdCam<'d, Async> {
128 pub fn into_blocking(self) -> LcdCam<'d, Blocking> {
130 crate::interrupt::disable(Cpu::current(), Interrupt::LCD_CAM);
131 LcdCam {
132 lcd: self.lcd.into_blocking(),
133 cam: self.cam,
134 }
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Default)]
140#[cfg_attr(feature = "defmt", derive(defmt::Format))]
141pub enum BitOrder {
142 #[default]
144 Native = 0,
145 Inverted = 1,
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Default)]
151#[cfg_attr(feature = "defmt", derive(defmt::Format))]
152pub enum ByteOrder {
153 #[default]
155 Native = 0,
156 Inverted = 1,
158}
159
160pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new();
161
162#[handler]
163fn interrupt_handler() {
164 if Instance::is_lcd_done_set() {
166 Instance::unlisten_lcd_done();
167 LCD_DONE_WAKER.wake()
168 }
169}
170
171pub(crate) struct Instance;
172
173impl Instance {
177 fn enable_listenlcd_done(en: bool) {
178 LCD_CAM::regs()
179 .lc_dma_int_ena()
180 .modify(|_, w| w.lcd_trans_done_int_ena().bit(en));
181 }
182
183 pub(crate) fn listen_lcd_done() {
184 Self::enable_listenlcd_done(true);
185 }
186
187 pub(crate) fn unlisten_lcd_done() {
188 Self::enable_listenlcd_done(false);
189 }
190
191 pub(crate) fn is_lcd_done_set() -> bool {
192 LCD_CAM::regs()
193 .lc_dma_int_raw()
194 .read()
195 .lcd_trans_done_int_raw()
196 .bit()
197 }
198}
199pub(crate) struct ClockDivider {
200 pub div_num: u32,
202
203 pub div_b: u32,
205
206 pub div_a: u32,
208}
209
210impl ClockDivider {
211 fn new(divider: FractionalDivider) -> Self {
212 Self {
213 div_num: divider.integer,
214 div_b: divider.numerator,
215 div_a: divider.denominator.max(1),
218 }
219 }
220}
221
222#[derive(Debug, Clone, Copy, PartialEq)]
224#[cfg_attr(feature = "defmt", derive(defmt::Format))]
225pub enum ClockError {
226 FrequencyTooLow,
228}
229
230pub(crate) fn calculate_clkm(
231 desired_frequency: u32,
232 source_frequencies: &[u32],
233) -> Result<(usize, ClockDivider), ClockError> {
234 let mut result_error = 0;
235 let mut result = None;
236
237 for (i, &source_frequency) in source_frequencies.iter().enumerate() {
238 let Some(divider) = calculate_closest_divider(source_frequency, desired_frequency) else {
239 continue;
240 };
241
242 let error = divider
245 .output_frequency(source_frequency)
246 .abs_diff(desired_frequency);
247 if result.is_none() || error < result_error {
248 result = Some((i, divider));
249 result_error = error;
250 }
251 }
252
253 let (index, divider) = result.ok_or(ClockError::FrequencyTooLow)?;
254
255 Ok((index, ClockDivider::new(divider)))
256}
257
258fn calculate_closest_divider(
259 source_frequency: u32,
260 desired_frequency: u32,
261) -> Option<FractionalDivider> {
262 let (min_divider, max_divider) = property!("clock_tree.lcd_cam.lcd_clock.div_num");
264 let (_, max_denominator) = property!("clock_tree.lcd_cam.lcd_clock.div_a");
265
266 if source_frequency / desired_frequency < min_divider {
267 return Some(FractionalDivider {
270 integer: min_divider,
271 numerator: 0,
272 denominator: 0,
273 });
274 }
275
276 let divider = FractionalDivider::new(source_frequency, desired_frequency, max_denominator);
277
278 (divider.integer <= max_divider).then_some(divider)
280}