Skip to main content

esp_hal/mipi_dsi/
dbi.rs

1//! MIPI DSI command-mode (DBI) interface.
2
3use crate::{mipi_dsi::MipiDsi, peripherals::MIPI_DSI_HOST};
4
5// DCS data type identifiers (MIPI DSI spec Table 7-1).
6const DT_DCS_SHORT_WRITE_0: u8 = 0x05;
7const DT_DCS_SHORT_WRITE_1: u8 = 0x15;
8const DT_DCS_LONG_WRITE: u8 = 0x39;
9const DT_DCS_READ_0: u8 = 0x06;
10const DT_SET_MAX_RETURN_PKT: u8 = 0x37;
11
12/// Error returned by DBI operations.
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub enum Error {
16    /// The parameter buffer is larger than the FIFO can handle.
17    PayloadTooLarge,
18}
19
20/// Command-mode (DBI) handle.
21///
22/// Holds a mutable borrow of the [`MipiDsi`] bus so that command and video
23/// modes cannot coexist without explicit sequencing.
24pub struct DsiDbi<'bus, 'd> {
25    _bus: &'bus mut MipiDsi<'d>,
26    virtual_channel: u8,
27}
28
29impl<'bus, 'd> DsiDbi<'bus, 'd> {
30    pub(crate) fn new(bus: &'bus mut MipiDsi<'d>, virtual_channel: u8) -> Self {
31        let h = MIPI_DSI_HOST::regs();
32
33        // All TX paths use LP mode; disable TE ack; enable cmd ack.
34        h.cmd_mode_cfg().modify(|_, w| {
35            w.tear_fx_en().clear_bit();
36            w.ack_rqst_en().set_bit();
37            w.gen_sw_0p_tx().set_bit();
38            w.gen_sw_1p_tx().set_bit();
39            w.gen_sw_2p_tx().set_bit();
40            w.gen_sr_0p_tx().set_bit();
41            w.gen_sr_1p_tx().set_bit();
42            w.gen_sr_2p_tx().set_bit();
43            w.gen_lw_tx().set_bit();
44            w.dcs_sw_0p_tx().set_bit();
45            w.dcs_sw_1p_tx().set_bit();
46            w.dcs_sr_0p_tx().set_bit();
47            w.dcs_lw_tx().set_bit();
48            w.max_rd_pkt_size().set_bit()
49        });
50
51        Self {
52            _bus: bus,
53            virtual_channel,
54        }
55    }
56
57    /// Returns the virtual channel ID this interface was configured with.
58    pub fn virtual_channel(&self) -> u8 {
59        self.virtual_channel
60    }
61
62    /// Send a DCS write command with zero or more parameters.
63    ///
64    /// Uses a short-write packet for 0 or 1 parameters, a long-write packet
65    /// otherwise.
66    pub fn write_cmd(&mut self, cmd: u8, params: &[u8]) -> Result<(), Error> {
67        let h = MIPI_DSI_HOST::regs();
68        let vc = self.virtual_channel;
69        let payload_size = 1 + params.len(); // cmd byte + params
70
71        if payload_size > u16::MAX as usize {
72            return Err(Error::PayloadTooLarge);
73        }
74
75        if payload_size > 2 {
76            // Long write: push payload into FIFO 4 bytes at a time.
77            // First word: [cmd, params[0..3]].
78            let merged = params.len().min(3);
79            let mut word: u32 = cmd as u32;
80            for (i, &b) in params[..merged].iter().enumerate() {
81                word |= (b as u32) << (8 * (i + 1));
82            }
83            while h.cmd_pkt_status().read().gen_pld_w_full().bit_is_set() {}
84            h.gen_pld_data().write(|w| unsafe { w.bits(word) });
85
86            let rest = &params[merged..];
87            let (chunks, tail) = rest.as_chunks::<4>();
88            for chunk in chunks {
89                let w32 = u32::from_le_bytes(*chunk);
90                while h.cmd_pkt_status().read().gen_pld_w_full().bit_is_set() {}
91                h.gen_pld_data().write(|w| unsafe { w.bits(w32) });
92            }
93            if !tail.is_empty() {
94                let mut w32: u32 = 0;
95                for (i, &b) in tail.iter().enumerate() {
96                    w32 |= (b as u32) << (8 * i);
97                }
98                while h.cmd_pkt_status().read().gen_pld_w_full().bit_is_set() {}
99                h.gen_pld_data().write(|w| unsafe { w.bits(w32) });
100            }
101
102            let wc = payload_size as u16;
103            while h.cmd_pkt_status().read().gen_cmd_full().bit_is_set() {}
104            h.gen_hdr().write(|w| unsafe {
105                w.gen_vc().bits(vc);
106                w.gen_dt().bits(DT_DCS_LONG_WRITE);
107                w.gen_wc_lsbyte().bits((wc & 0xFF) as u8);
108                w.gen_wc_msbyte().bits((wc >> 8) as u8)
109            });
110        } else if payload_size == 2 {
111            // Short write with 1 param.
112            while h.cmd_pkt_status().read().gen_cmd_full().bit_is_set() {}
113            h.gen_hdr().write(|w| unsafe {
114                w.gen_vc().bits(vc);
115                w.gen_dt().bits(DT_DCS_SHORT_WRITE_1);
116                w.gen_wc_lsbyte().bits(cmd);
117                w.gen_wc_msbyte().bits(params[0])
118            });
119        } else {
120            // Short write with 0 params.
121            while h.cmd_pkt_status().read().gen_cmd_full().bit_is_set() {}
122            h.gen_hdr().write(|w| unsafe {
123                w.gen_vc().bits(vc);
124                w.gen_dt().bits(DT_DCS_SHORT_WRITE_0);
125                w.gen_wc_lsbyte().bits(cmd);
126                w.gen_wc_msbyte().bits(0)
127            });
128        }
129
130        Ok(())
131    }
132
133    /// Issue a DCS read command (BTA) and drain the response into `out`.
134    ///
135    /// Returns the number of bytes placed into `out`.
136    pub fn read_cmd(&mut self, cmd: u8, out: &mut [u8]) -> Result<usize, Error> {
137        let h = MIPI_DSI_HOST::regs();
138        let vc = self.virtual_channel;
139
140        if out.len() > u16::MAX as usize {
141            return Err(Error::PayloadTooLarge);
142        }
143
144        // SET_MAXIMUM_RETURN_PKT_SIZE.
145        let max_ret = out.len() as u16;
146        while h.cmd_pkt_status().read().gen_cmd_full().bit_is_set() {}
147        h.gen_hdr().write(|w| unsafe {
148            w.gen_vc().bits(vc);
149            w.gen_dt().bits(DT_SET_MAX_RETURN_PKT);
150            w.gen_wc_lsbyte().bits((max_ret & 0xFF) as u8);
151            w.gen_wc_msbyte().bits((max_ret >> 8) as u8)
152        });
153
154        // Ensure command mode is active.
155        h.mode_cfg().modify(|_, w| w.cmd_video_mode().set_bit());
156
157        // Enable BTA and set RX virtual channel.
158        h.pckhdl_cfg().modify(|_, w| w.bta_en().set_bit());
159        h.gen_vcid().modify(|_, w| unsafe { w.rx().bits(vc) });
160
161        // Send DCS READ_0.
162        while h.cmd_pkt_status().read().gen_cmd_full().bit_is_set() {}
163        h.gen_hdr().write(|w| unsafe {
164            w.gen_vc().bits(vc);
165            w.gen_dt().bits(DT_DCS_READ_0);
166            w.gen_wc_lsbyte().bits(cmd);
167            w.gen_wc_msbyte().bits(0)
168        });
169
170        // Wait for BTA read to complete.
171        while h.cmd_pkt_status().read().gen_rd_cmd_busy().bit_is_set() {}
172
173        // Drain the read FIFO.
174        while h.cmd_pkt_status().read().gen_pld_r_empty().bit_is_set() {}
175        let mut count = 0usize;
176        while !h.cmd_pkt_status().read().gen_pld_r_empty().bit_is_set() {
177            let word = h.gen_pld_data().read().bits();
178            for i in 0..4 {
179                if count < out.len() {
180                    out[count] = ((word >> (8 * i)) & 0xFF) as u8;
181                    count += 1;
182                }
183            }
184        }
185
186        Ok(count)
187    }
188}