From 1123e3fd41e7b23dd0507816f1ff67fc0de6b5d1 Mon Sep 17 00:00:00 2001
From: David Haig <david@ninjametal.com>
Date: Fri, 28 Jun 2024 15:12:17 +0100
Subject: [PATCH] Get dsi_bsp example to compile again

---
 embassy-stm32/src/ltdc.rs        | 224 ++++++++++++++++++-------------
 examples/stm32h7/src/bin/ltdc.rs |   4 +-
 2 files changed, 131 insertions(+), 97 deletions(-)

diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs
index adc9b8862..64558ee52 100644
--- a/embassy-stm32/src/ltdc.rs
+++ b/embassy-stm32/src/ltdc.rs
@@ -175,8 +175,31 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
 }
 
 impl<'d, T: Instance> Ltdc<'d, T> {
+    // Create a new LTDC driver without specifying color and control pins. This is typically used if you want to drive a display though a DsiHost
+    /// Note: Full-Duplex modes are not supported at this time
+    pub fn new(peri: impl Peripheral<P = T> + 'd) -> Self {
+        critical_section::with(|_cs| {
+            // RM says the pllsaidivr should only be changed when pllsai is off. But this could have other unintended side effects. So let's just give it a try like this.
+            // According to the debugger, this bit gets set, anyway.
+            #[cfg(stm32f7)]
+            stm32_metapac::RCC
+                .dckcfgr1()
+                .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2));
+
+            // It is set to RCC_PLLSAIDIVR_2 in ST's BSP example for the STM32469I-DISCO.
+            #[cfg(not(any(stm32f7, stm32u5)))]
+            stm32_metapac::RCC
+                .dckcfgr()
+                .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2));
+        });
+
+        rcc::enable_and_reset::<T>();
+        into_ref!(peri);
+        Self { _peri: peri }
+    }
+
     /// Create a new LTDC driver. 8 pins per color channel for blue, green and red
-    pub fn new(
+    pub fn new_with_pins(
         peri: impl Peripheral<P = T> + 'd,
         _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
         clk: impl Peripheral<P = impl ClkPin<T>> + 'd,
@@ -241,93 +264,7 @@ impl<'d, T: Instance> Ltdc<'d, T> {
         Self { _peri: peri }
     }
 
-    fn clear_interrupt_flags() {
-        T::regs().icr().write(|w| {
-            w.set_cfuif(Cfuif::CLEAR);
-            w.set_clif(Clif::CLEAR);
-            w.set_crrif(Crrif::CLEAR);
-            w.set_cterrif(Cterrif::CLEAR);
-        });
-    }
-
-    fn enable_interrupts(enable: bool) {
-        T::regs().ier().write(|w| {
-            w.set_fuie(enable);
-            w.set_lie(false); // we are not interested in the line interrupt enable event
-            w.set_rrie(enable);
-            w.set_terrie(enable)
-        });
-
-        // enable interrupts for LTDC peripheral
-        T::Interrupt::unpend();
-        if enable {
-            unsafe { T::Interrupt::enable() };
-        } else {
-            T::Interrupt::disable()
-        }
-    }
-
-    /// Set the current buffer. The async function will return when buffer has been completely copied to the LCD screen
-    /// frame_buffer_addr is a pointer to memory that should not move (best to make it static)
-    pub async fn set_buffer(&mut self, layer: LtdcLayer, frame_buffer_addr: *const ()) -> Result<(), Error> {
-        let mut bits = T::regs().isr().read();
-
-        // if all clear
-        if !bits.fuif() && !bits.lif() && !bits.rrif() && !bits.terrif() {
-            // wait for interrupt
-            poll_fn(|cx| {
-                // quick check to avoid registration if already done.
-                let bits = T::regs().isr().read();
-                if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
-                    return Poll::Ready(());
-                }
-
-                LTDC_WAKER.register(cx.waker());
-                Self::clear_interrupt_flags(); // don't poison the request with old flags
-                Self::enable_interrupts(true);
-
-                // set the new frame buffer address
-                let layer = T::regs().layer(layer as usize);
-                layer.cfbar().modify(|w| w.set_cfbadd(frame_buffer_addr as u32));
-
-                // configure a shadow reload for the next blanking period
-                T::regs().srcr().write(|w| {
-                    w.set_vbr(Vbr::RELOAD);
-                });
-
-                // need to check condition after register to avoid a race
-                // condition that would result in lost notifications.
-                let bits = T::regs().isr().read();
-                if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
-                    Poll::Ready(())
-                } else {
-                    Poll::Pending
-                }
-            })
-            .await;
-
-            // re-read the status register after wait.
-            bits = T::regs().isr().read();
-        }
-
-        let result = if bits.fuif() {
-            Err(Error::FifoUnderrun)
-        } else if bits.terrif() {
-            Err(Error::TransferError)
-        } else if bits.lif() {
-            panic!("line interrupt event is disabled")
-        } else if bits.rrif() {
-            // register reload flag is expected
-            Ok(())
-        } else {
-            unreachable!("all interrupt status values checked")
-        };
-
-        Self::clear_interrupt_flags();
-        result
-    }
-
-    /// Initialize the display
+    /// Initialise and enable the display
     pub fn init(&mut self, config: &LtdcConfiguration) {
         use stm32_metapac::ltdc::vals::{Depol, Hspol, Pcpol, Vspol};
         let ltdc = T::regs();
@@ -393,16 +330,27 @@ impl<'d, T: Instance> Ltdc<'d, T> {
             w.set_bcblue(0);
         });
 
-        // enable LTDC by setting LTDCEN bit
-        ltdc.gcr().modify(|w| {
-            w.set_ltdcen(true);
-        });
+        self.enable();
     }
 
-    /// Enable the layer
+    /// Set the enable bit in the control register and assert that it has been enabled
     ///
-    /// clut - color look-up table applies to L8, AL44 and AL88 pixel format and will default to greyscale if None supplied and these pixel formats are used
-    pub fn enable_layer(&mut self, layer_config: &LtdcLayerConfig, clut: Option<&[RgbColor]>) {
+    /// This does need to be called if init has already been called
+    pub fn enable(&mut self) {
+        T::regs().gcr().modify(|w| w.set_ltdcen(true));
+        assert!(T::regs().gcr().read().ltdcen())
+    }
+
+    /// Unset the enable bit in the control register and assert that it has been disabled
+    pub fn disable(&mut self) {
+        T::regs().gcr().modify(|w| w.set_ltdcen(false));
+        assert!(!T::regs().gcr().read().ltdcen())
+    }
+
+    /// Initialise and enable the layer
+    ///
+    /// clut - a 256 length color look-up table applies to L8, AL44 and AL88 pixel format and will default to greyscale if `None` supplied and these pixel formats are used
+    pub fn init_layer(&mut self, layer_config: &LtdcLayerConfig, clut: Option<&[RgbColor]>) {
         let ltdc = T::regs();
         let layer = ltdc.layer(layer_config.layer as usize);
 
@@ -475,6 +423,92 @@ impl<'d, T: Instance> Ltdc<'d, T> {
             w.set_len(true);
         });
     }
+
+    /// Set the current buffer. The async function will return when buffer has been completely copied to the LCD screen
+    /// frame_buffer_addr is a pointer to memory that should not move (best to make it static)
+    pub async fn set_buffer(&mut self, layer: LtdcLayer, frame_buffer_addr: *const ()) -> Result<(), Error> {
+        let mut bits = T::regs().isr().read();
+
+        // if all clear
+        if !bits.fuif() && !bits.lif() && !bits.rrif() && !bits.terrif() {
+            // wait for interrupt
+            poll_fn(|cx| {
+                // quick check to avoid registration if already done.
+                let bits = T::regs().isr().read();
+                if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
+                    return Poll::Ready(());
+                }
+
+                LTDC_WAKER.register(cx.waker());
+                Self::clear_interrupt_flags(); // don't poison the request with old flags
+                Self::enable_interrupts(true);
+
+                // set the new frame buffer address
+                let layer = T::regs().layer(layer as usize);
+                layer.cfbar().modify(|w| w.set_cfbadd(frame_buffer_addr as u32));
+
+                // configure a shadow reload for the next blanking period
+                T::regs().srcr().write(|w| {
+                    w.set_vbr(Vbr::RELOAD);
+                });
+
+                // need to check condition after register to avoid a race
+                // condition that would result in lost notifications.
+                let bits = T::regs().isr().read();
+                if bits.fuif() || bits.lif() || bits.rrif() || bits.terrif() {
+                    Poll::Ready(())
+                } else {
+                    Poll::Pending
+                }
+            })
+            .await;
+
+            // re-read the status register after wait.
+            bits = T::regs().isr().read();
+        }
+
+        let result = if bits.fuif() {
+            Err(Error::FifoUnderrun)
+        } else if bits.terrif() {
+            Err(Error::TransferError)
+        } else if bits.lif() {
+            panic!("line interrupt event is disabled")
+        } else if bits.rrif() {
+            // register reload flag is expected
+            Ok(())
+        } else {
+            unreachable!("all interrupt status values checked")
+        };
+
+        Self::clear_interrupt_flags();
+        result
+    }
+
+    fn clear_interrupt_flags() {
+        T::regs().icr().write(|w| {
+            w.set_cfuif(Cfuif::CLEAR);
+            w.set_clif(Clif::CLEAR);
+            w.set_crrif(Crrif::CLEAR);
+            w.set_cterrif(Cterrif::CLEAR);
+        });
+    }
+
+    fn enable_interrupts(enable: bool) {
+        T::regs().ier().write(|w| {
+            w.set_fuie(enable);
+            w.set_lie(false); // we are not interested in the line interrupt enable event
+            w.set_rrie(enable);
+            w.set_terrie(enable)
+        });
+
+        // enable interrupts for LTDC peripheral
+        T::Interrupt::unpend();
+        if enable {
+            unsafe { T::Interrupt::enable() };
+        } else {
+            T::Interrupt::disable()
+        }
+    }
 }
 
 impl<'d, T: Instance> Drop for Ltdc<'d, T> {
diff --git a/examples/stm32h7/src/bin/ltdc.rs b/examples/stm32h7/src/bin/ltdc.rs
index 3bd307012..3b56bbb6c 100644
--- a/examples/stm32h7/src/bin/ltdc.rs
+++ b/examples/stm32h7/src/bin/ltdc.rs
@@ -87,7 +87,7 @@ async fn main(spawner: Spawner) {
     };
 
     info!("init ltdc");
-    let mut ltdc = Ltdc::new(
+    let mut ltdc = Ltdc::new_with_pins(
         p.LTDC, Irqs, p.PG7, p.PC6, p.PA4, p.PG14, p.PD0, p.PD6, p.PA8, p.PE12, p.PA3, p.PB8, p.PB9, p.PB1, p.PB0,
         p.PA6, p.PE11, p.PH15, p.PH4, p.PC7, p.PD3, p.PE0, p.PH3, p.PH8, p.PH9, p.PH10, p.PH11, p.PE1, p.PE15,
     );
@@ -109,7 +109,7 @@ async fn main(spawner: Spawner) {
     let clut = build_clut(&color_map);
 
     // enable the bottom layer with a 256 color lookup table
-    ltdc.enable_layer(&layer_config, Some(&clut));
+    ltdc.init_layer(&layer_config, Some(&clut));
 
     // Safety: the DoubleBuffer controls access to the statically allocated frame buffers
     // and it is the only thing that mutates their content