1use core::marker::PhantomData;
9
10use enumset::{EnumSet, EnumSetType};
11use esp_sync::RawMutex;
12
13use crate::{
14 Async,
15 Blocking,
16 asynch::AtomicWaker,
17 clock::dividers::FractionalDivider,
18 handler,
19 interrupt::InterruptHandler,
20 lcd_cam::{cam::Cam, lcd::Lcd},
21 peripherals::{Interrupt, LCD_CAM},
22 system::{Cpu, GenericPeripheralGuard},
23};
24
25pub mod cam;
26pub mod lcd;
27pub(crate) mod ll;
28
29#[diagnostic::on_unimplemented(
33 message = "The DMA channel cannot be used as a TX channel for LCD",
34 label = "This DMA channel"
35)]
36pub trait LcdDmaTxChannel<'d>: Into<ErasedTxChannel<'d>> + crate::private::Sealed {}
37
38#[diagnostic::on_unimplemented(
42 message = "The DMA channel cannot be used as an RX channel for Camera",
43 label = "This DMA channel"
44)]
45pub trait CamDmaRxChannel<'d>: Into<ErasedRxChannel<'d>> + crate::private::Sealed {}
46
47with_lcd_cam_dma_engine! {
48 ($engine:tt, $any_channel:tt) => {
49 type ErasedTxChannel<'d> = <crate::dma::$any_channel<'d> as crate::dma::DmaChannel>::Tx;
50 type ErasedRxChannel<'d> = <crate::dma::$any_channel<'d> as crate::dma::DmaChannel>::Rx;
51
52 crate::macros::impl_dma_channel_trait! {
53 $engine,
54 peri = LCD_CAM,
55 ($peri:path, $ch:path) => {
56 impl<'d> LcdDmaTxChannel<'d> for $ch {}
57 impl<'d> CamDmaRxChannel<'d> for $ch {}
58 }
59 }
60
61 impl<'d> LcdDmaTxChannel<'d> for ErasedTxChannel<'d> {}
64 impl<'d> CamDmaRxChannel<'d> for ErasedRxChannel<'d> {}
65 };
66}
67
68pub struct LcdCam<'d, Dm: crate::DriverMode> {
70 pub lcd: Lcd<'d, Dm>,
72 pub cam: Cam<'d>,
74}
75
76impl<'d> LcdCam<'d, Blocking> {
77 pub fn new(lcd_cam: LCD_CAM<'d>) -> Self {
79 let lcd_guard = GenericPeripheralGuard::new();
80 let cam_guard = GenericPeripheralGuard::new();
81
82 Self {
83 lcd: Lcd {
84 inner: lcd::Inner {
85 lcd_cam: unsafe { lcd_cam.clone_unchecked() },
86 _guard: lcd_guard,
87 clock_requested: false,
88 },
89 _mode: PhantomData,
90 },
91 cam: Cam {
92 lcd_cam,
93 _guard: cam_guard,
94 clock_requested: false,
95 },
96 }
97 }
98
99 pub fn into_async(mut self) -> LcdCam<'d, Async> {
101 self.set_interrupt_handler(interrupt_handler);
102 LcdCam {
103 lcd: self.lcd.into_async(),
104 cam: self.cam,
105 }
106 }
107
108 #[instability::unstable]
112 pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
113 for core in crate::system::Cpu::other() {
114 crate::interrupt::disable(core, Interrupt::LCD_CAM);
115 }
116 crate::interrupt::bind_handler(Interrupt::LCD_CAM, handler);
117 }
118}
119
120impl crate::private::Sealed for LcdCam<'_, Blocking> {}
121#[instability::unstable]
124impl crate::interrupt::InterruptConfigurable for LcdCam<'_, Blocking> {
125 fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
126 self.set_interrupt_handler(handler);
127 }
128}
129
130impl<'d> LcdCam<'d, Async> {
131 pub fn into_blocking(self) -> LcdCam<'d, Blocking> {
133 crate::interrupt::disable(Cpu::current(), Interrupt::LCD_CAM);
134 LcdCam {
135 lcd: self.lcd.into_blocking(),
136 cam: self.cam,
137 }
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Default)]
143#[cfg_attr(feature = "defmt", derive(defmt::Format))]
144pub enum BitOrder {
145 #[default]
147 Native = 0,
148 Inverted = 1,
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Default)]
154#[cfg_attr(feature = "defmt", derive(defmt::Format))]
155pub enum ByteOrder {
156 #[default]
158 Native = 0,
159 Inverted = 1,
161}
162
163pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new();
164pub(crate) static LCD_VSYNC_WAKER: AtomicWaker = AtomicWaker::new();
165static INT_ENA_LOCK: RawMutex = RawMutex::new();
166
167#[handler]
168fn interrupt_handler() {
169 let status = LCD_CAM::regs().lc_dma_int_st().read();
171 if status.lcd_trans_done_int_st().bit_is_set() {
172 Instance::unlisten_lcd_done();
173 LCD_DONE_WAKER.wake();
174 }
175 if status.lcd_vsync_int_st().bit_is_set() {
176 Instance::unlisten(LcdCamInterrupt::LcdVsync.into());
177 LCD_VSYNC_WAKER.wake();
178 }
179}
180
181pub(crate) struct Instance;
182
183#[derive(Debug, EnumSetType)]
189pub(crate) enum LcdCamInterrupt {
190 LcdVsync,
192
193 LcdTransDone,
195
196 CamVsync,
198
199 CamHs,
201}
202
203impl Instance {
206 fn enable_listen(sources: EnumSet<LcdCamInterrupt>, en: bool) {
207 INT_ENA_LOCK.lock(|| {
208 LCD_CAM::regs().lc_dma_int_ena().modify(|_, w| {
209 for source in sources {
210 match source {
211 LcdCamInterrupt::LcdVsync => {
212 w.lcd_vsync_int_ena().bit(en);
213 }
214 LcdCamInterrupt::LcdTransDone => {
215 w.lcd_trans_done_int_ena().bit(en);
216 }
217 LcdCamInterrupt::CamVsync => {
218 w.cam_vsync_int_ena().bit(en);
219 }
220 LcdCamInterrupt::CamHs => {
221 w.cam_hs_int_ena().bit(en);
222 }
223 }
224 }
225 w
226 });
227 });
228 }
229
230 pub(crate) fn listen(sources: EnumSet<LcdCamInterrupt>) {
231 Self::enable_listen(sources, true);
232 }
233
234 pub(crate) fn unlisten(sources: EnumSet<LcdCamInterrupt>) {
235 Self::enable_listen(sources, false);
236 }
237
238 pub(crate) fn interrupts() -> EnumSet<LcdCamInterrupt> {
239 let raw = LCD_CAM::regs().lc_dma_int_raw().read();
240 let mut sources = EnumSet::new();
241 if raw.lcd_vsync_int_raw().bit() {
242 sources.insert(LcdCamInterrupt::LcdVsync);
243 }
244 if raw.lcd_trans_done_int_raw().bit() {
245 sources.insert(LcdCamInterrupt::LcdTransDone);
246 }
247 if raw.cam_vsync_int_raw().bit() {
248 sources.insert(LcdCamInterrupt::CamVsync);
249 }
250 if raw.cam_hs_int_raw().bit() {
251 sources.insert(LcdCamInterrupt::CamHs);
252 }
253 sources
254 }
255
256 pub(crate) fn clear_interrupts(sources: EnumSet<LcdCamInterrupt>) {
257 LCD_CAM::regs().lc_dma_int_clr().write(|w| {
258 for source in sources {
259 match source {
260 LcdCamInterrupt::LcdVsync => {
261 w.lcd_vsync_int_clr().set_bit();
262 }
263 LcdCamInterrupt::LcdTransDone => {
264 w.lcd_trans_done_int_clr().set_bit();
265 }
266 LcdCamInterrupt::CamVsync => {
267 w.cam_vsync_int_clr().set_bit();
268 }
269 LcdCamInterrupt::CamHs => {
270 w.cam_hs_int_clr().set_bit();
271 }
272 }
273 }
274 w
275 });
276 }
277
278 pub(crate) fn listen_lcd_done() {
279 Self::listen(LcdCamInterrupt::LcdTransDone.into());
280 }
281
282 pub(crate) fn unlisten_lcd_done() {
283 Self::unlisten(LcdCamInterrupt::LcdTransDone.into());
284 }
285
286 pub(crate) fn is_lcd_done_set() -> bool {
287 Self::interrupts().contains(LcdCamInterrupt::LcdTransDone)
288 }
289}
290pub(crate) struct ClockDivider {
291 pub div_num: u32,
293
294 pub div_b: u32,
296
297 pub div_a: u32,
299}
300
301impl ClockDivider {
302 fn new(divider: FractionalDivider) -> Self {
303 Self {
304 div_num: divider.integer,
305 div_b: divider.numerator,
306 div_a: divider.denominator.max(1),
309 }
310 }
311}
312
313#[derive(Debug, Clone, Copy, PartialEq)]
315#[cfg_attr(feature = "defmt", derive(defmt::Format))]
316pub enum ClockError {
317 FrequencyTooLow,
319}
320
321pub(crate) fn calculate_clkm(
322 desired_frequency: u32,
323 source_frequencies: &[u32],
324) -> Result<(usize, ClockDivider), ClockError> {
325 let mut result_error = 0;
326 let mut result = None;
327
328 for (i, &source_frequency) in source_frequencies.iter().enumerate() {
329 let Some(divider) = calculate_closest_divider(source_frequency, desired_frequency) else {
330 continue;
331 };
332
333 let error = divider
336 .output_frequency(source_frequency)
337 .abs_diff(desired_frequency);
338 if result.is_none() || error < result_error {
339 result = Some((i, divider));
340 result_error = error;
341 }
342 }
343
344 let (index, divider) = result.ok_or(ClockError::FrequencyTooLow)?;
345
346 Ok((index, ClockDivider::new(divider)))
347}
348
349fn calculate_closest_divider(
350 source_frequency: u32,
351 desired_frequency: u32,
352) -> Option<FractionalDivider> {
353 let (min_divider, max_divider) = property!("clock_tree.lcd_cam.lcd_clock.div_num");
355 let (_, max_denominator) = property!("clock_tree.lcd_cam.lcd_clock.div_a");
356
357 if source_frequency / desired_frequency < min_divider {
358 return Some(FractionalDivider {
361 integer: min_divider,
362 numerator: 0,
363 denominator: 0,
364 });
365 }
366
367 let divider = FractionalDivider::new(source_frequency, desired_frequency, max_denominator);
368
369 (divider.integer <= max_divider).then_some(divider)
371}