1 /*
2  * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #pragma once
7 
8 #include <stddef.h> /* For NULL declaration */
9 #include <stdint.h>
10 #include <stdbool.h>
11 #include "hal/misc.h"
12 #include "soc/lcd_cam_reg.h"
13 #include "soc/lcd_cam_struct.h"
14 #include "hal/assert.h"
15 #include "hal/lcd_types.h"
16 
17 #ifdef __cplusplus
18 extern "C" {
19 #endif
20 
21 #define LCD_LL_GET_HW(id) (((id) == 0) ? (&LCD_CAM) : NULL)
22 
23 // Interrupt event, bit mask
24 #define LCD_LL_EVENT_VSYNC_END  (1 << 0)
25 #define LCD_LL_EVENT_TRANS_DONE (1 << 1)
26 
27 #define LCD_LL_CLK_FRAC_DIV_N_MAX  256 // LCD_CLK = LCD_CLK_S / (N + b/a), the N register is 8 bit-width
28 #define LCD_LL_CLK_FRAC_DIV_AB_MAX 64  // LCD_CLK = LCD_CLK_S / (N + b/a), the a/b register is 6 bit-width
29 #define LCD_LL_PCLK_DIV_MAX        64  // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width
30 #define LCD_LL_FIFO_DEPTH          16  // Async FIFO depth
31 
32 #define LCD_LL_COLOR_RANGE_TO_REG(range) (uint8_t[]){0,1}[(range)]
33 #define LCD_LL_CONV_STD_TO_REG(std)      (uint8_t[]){0,1}[(std)]
34 #define LCD_LL_YUV_SAMPLE_TO_REG(sample) (uint8_t[]){0,1,2}[(sample)]
35 
36 /**
37  * @brief Enable clock gating
38  *
39  * @param dev LCD register base address
40  * @param en True to enable, False to disable
41  */
lcd_ll_enable_clock(lcd_cam_dev_t * dev,bool en)42 static inline void lcd_ll_enable_clock(lcd_cam_dev_t *dev, bool en)
43 {
44     dev->lcd_clock.clk_en = en;
45 }
46 
47 /**
48  * @brief Select clock source for LCD peripheral
49  *
50  * @param dev LCD register base address
51  * @param src Clock source
52  */
lcd_ll_select_clk_src(lcd_cam_dev_t * dev,lcd_clock_source_t src)53 static inline void lcd_ll_select_clk_src(lcd_cam_dev_t *dev, lcd_clock_source_t src)
54 {
55     switch (src) {
56     case LCD_CLK_SRC_PLL160M:
57         dev->lcd_clock.lcd_clk_sel = 3;
58         break;
59     case LCD_CLK_SRC_PLL240M:
60         dev->lcd_clock.lcd_clk_sel = 2;
61         break;
62     case LCD_CLK_SRC_XTAL:
63         dev->lcd_clock.lcd_clk_sel = 1;
64         break;
65     default:
66         // disable LCD clock source
67         dev->lcd_clock.lcd_clk_sel = 0;
68         HAL_ASSERT(false);
69         break;
70     }
71 }
72 
73 /**
74  * @brief Set clock coefficient of LCD peripheral
75  *
76  * @param dev LCD register base address
77  * @param div_num Integer part of the divider
78  * @param div_a denominator of the divider
79  * @param div_b numerator of the divider
80  */
lcd_ll_set_group_clock_coeff(lcd_cam_dev_t * dev,int div_num,int div_a,int div_b)81 static inline void lcd_ll_set_group_clock_coeff(lcd_cam_dev_t *dev, int div_num, int div_a, int div_b)
82 {
83     // lcd_clk = module_clock_src / (div_num + div_b / div_a)
84     HAL_ASSERT(div_num >= 2 && div_num <= LCD_LL_CLK_FRAC_DIV_N_MAX);
85     // dic_num == 0 means LCD_LL_CLK_FRAC_DIV_N_MAX divider in hardware
86     if (div_num >= LCD_LL_CLK_FRAC_DIV_N_MAX) {
87         div_num = 0;
88     }
89     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_clock, lcd_clkm_div_num, div_num);
90     dev->lcd_clock.lcd_clkm_div_a = div_a;
91     dev->lcd_clock.lcd_clkm_div_b = div_b;
92 }
93 
94 
95 /**
96  * @brief Set the PCLK clock level state when there's no transaction undergoing
97  *
98  * @param dev LCD register base address
99  * @param level 1 is high level, 0 is low level
100  */
101 __attribute__((always_inline))
lcd_ll_set_clock_idle_level(lcd_cam_dev_t * dev,bool level)102 static inline void lcd_ll_set_clock_idle_level(lcd_cam_dev_t *dev, bool level)
103 {
104     dev->lcd_clock.lcd_ck_idle_edge = level;
105 }
106 
107 /**
108  * @brief Set the PCLK sample edge
109  *
110  * @param dev LCD register base address
111  * @param active_on_neg True: sample on negedge, False: sample on posedge
112  */
113 __attribute__((always_inline))
lcd_ll_set_pixel_clock_edge(lcd_cam_dev_t * dev,bool active_on_neg)114 static inline void lcd_ll_set_pixel_clock_edge(lcd_cam_dev_t *dev, bool active_on_neg)
115 {
116     dev->lcd_clock.lcd_ck_out_edge = active_on_neg;
117 }
118 
119 /**
120  * @brief Set PCLK prescale
121  *
122  * @param dev LCD register base address
123  * @param prescale Prescale value, PCLK = LCD_CLK / prescale
124  */
125 __attribute__((always_inline))
lcd_ll_set_pixel_clock_prescale(lcd_cam_dev_t * dev,uint32_t prescale)126 static inline void lcd_ll_set_pixel_clock_prescale(lcd_cam_dev_t *dev, uint32_t prescale)
127 {
128     HAL_ASSERT(prescale <= LCD_LL_PCLK_DIV_MAX);
129     // Formula: pixel_clk = lcd_clk / (1 + clkcnt_n)
130     // clkcnt_n can't be zero
131     uint32_t scale = 1;
132     if (prescale == 1) {
133         dev->lcd_clock.lcd_clk_equ_sysclk = 1;
134     } else {
135         dev->lcd_clock.lcd_clk_equ_sysclk = 0;
136         scale = prescale - 1;
137     }
138     dev->lcd_clock.lcd_clkcnt_n = scale;
139 }
140 
141 /**
142  * @brief Enable YUV-RGB converter
143  *
144  * @param dev LCD register base address
145  * @param en True to enable converter, False to disable converter
146  */
lcd_ll_enable_rgb_yuv_convert(lcd_cam_dev_t * dev,bool en)147 static inline void lcd_ll_enable_rgb_yuv_convert(lcd_cam_dev_t *dev, bool en)
148 {
149     dev->lcd_rgb_yuv.lcd_conv_bypass = en;
150 }
151 
152 /**
153  * @brief Set convert data line width
154  *
155  * @param dev LCD register base address
156  * @param width data line width (8 or 16)
157  */
lcd_ll_set_convert_data_width(lcd_cam_dev_t * dev,uint32_t width)158 static inline void lcd_ll_set_convert_data_width(lcd_cam_dev_t *dev, uint32_t width)
159 {
160     HAL_ASSERT(width == 8 || width == 16);
161     dev->lcd_rgb_yuv.lcd_conv_mode_8bits_on = (width == 8) ? 1 : 0;
162 }
163 
164 /**
165  * @brief Set the color range of input data
166  *
167  * @param dev LCD register base address
168  * @param range Color range
169  */
lcd_ll_set_input_color_range(lcd_cam_dev_t * dev,lcd_color_range_t range)170 static inline void lcd_ll_set_input_color_range(lcd_cam_dev_t *dev, lcd_color_range_t range)
171 {
172     dev->lcd_rgb_yuv.lcd_conv_data_in_mode = LCD_LL_COLOR_RANGE_TO_REG(range);
173 }
174 
175 /**
176  * @brief Set the color range of output data
177  *
178  * @param dev LCD register base address
179  * @param range Color range
180  */
lcd_ll_set_output_color_range(lcd_cam_dev_t * dev,lcd_color_range_t range)181 static inline void lcd_ll_set_output_color_range(lcd_cam_dev_t *dev, lcd_color_range_t range)
182 {
183     dev->lcd_rgb_yuv.lcd_conv_data_out_mode = LCD_LL_COLOR_RANGE_TO_REG(range);
184 }
185 
186 /**
187  * @brief Set YUV conversion standard
188  *
189  * @param dev LCD register base address
190  * @param std YUV conversion standard
191  */
lcd_ll_set_yuv_convert_std(lcd_cam_dev_t * dev,lcd_yuv_conv_std_t std)192 static inline void lcd_ll_set_yuv_convert_std(lcd_cam_dev_t *dev, lcd_yuv_conv_std_t std)
193 {
194     dev->lcd_rgb_yuv.lcd_conv_protocol_mode = LCD_LL_CONV_STD_TO_REG(std);
195 }
196 
197 /**
198  * @brief Set the converter mode: RGB565 to YUV
199  *
200  * @param dev LCD register base address
201  * @param yuv_sample YUV sample mode
202  */
lcd_ll_set_convert_mode_rgb_to_yuv(lcd_cam_dev_t * dev,lcd_yuv_sample_t yuv_sample)203 static inline void lcd_ll_set_convert_mode_rgb_to_yuv(lcd_cam_dev_t *dev, lcd_yuv_sample_t yuv_sample)
204 {
205     dev->lcd_rgb_yuv.lcd_conv_trans_mode = 1;
206     dev->lcd_rgb_yuv.lcd_conv_yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(yuv_sample);
207     dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 3;
208 }
209 
210 /**
211  * @brief Set the converter mode: YUV to RGB565
212  *
213  * @param dev LCD register base address
214  * @param yuv_sample YUV sample mode
215  */
lcd_ll_set_convert_mode_yuv_to_rgb(lcd_cam_dev_t * dev,lcd_yuv_sample_t yuv_sample)216 static inline void lcd_ll_set_convert_mode_yuv_to_rgb(lcd_cam_dev_t *dev, lcd_yuv_sample_t yuv_sample)
217 {
218     dev->lcd_rgb_yuv.lcd_conv_trans_mode = 0;
219     dev->lcd_rgb_yuv.lcd_conv_yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(yuv_sample);
220     dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 3;
221 }
222 
223 /**
224  * @brief Set the converter mode: YUV to YUV
225  *
226  * @param dev LCD register base address
227  * @param src_sample Source YUV sample mode
228  * @param dst_sample Destination YUV sample mode
229  */
lcd_ll_set_convert_mode_yuv_to_yuv(lcd_cam_dev_t * dev,lcd_yuv_sample_t src_sample,lcd_yuv_sample_t dst_sample)230 static inline void lcd_ll_set_convert_mode_yuv_to_yuv(lcd_cam_dev_t *dev, lcd_yuv_sample_t src_sample, lcd_yuv_sample_t dst_sample)
231 {
232     HAL_ASSERT(src_sample != dst_sample);
233     dev->lcd_rgb_yuv.lcd_conv_trans_mode = 1;
234     dev->lcd_rgb_yuv.lcd_conv_yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(src_sample);
235     dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(dst_sample);
236 }
237 
238 /**
239  * @brief Set clock cycles of each transaction phases
240  *
241  * @param dev LCD register base address
242  * @param cmd_cycles Clock cycles of CMD phase
243  * @param dummy_cycles Clock cycles of DUMMY phase
244  * @param data_cycles Clock cycles of DATA phase
245  */
246 __attribute__((always_inline))
lcd_ll_set_phase_cycles(lcd_cam_dev_t * dev,uint32_t cmd_cycles,uint32_t dummy_cycles,uint32_t data_cycles)247 static inline void lcd_ll_set_phase_cycles(lcd_cam_dev_t *dev, uint32_t cmd_cycles, uint32_t dummy_cycles, uint32_t data_cycles)
248 {
249     HAL_ASSERT(cmd_cycles <= 2);
250     dev->lcd_user.lcd_cmd = (cmd_cycles > 0);
251     dev->lcd_user.lcd_dummy = (dummy_cycles > 0);
252     dev->lcd_user.lcd_dout = (data_cycles > 0);
253     dev->lcd_user.lcd_cmd_2_cycle_en = cmd_cycles > 1;
254     dev->lcd_user.lcd_dummy_cyclelen = dummy_cycles - 1;
255     dev->lcd_user.lcd_dout_cyclelen = data_cycles - 1;
256 }
257 
258 /**
259  * @brief Set clock cycles of blank phases
260  *
261  * @param dev LCD register base address
262  * @param fk_cycles Clock cycles of front blank
263  * @param bk_cycles Clock cycles of back blank
264  */
lcd_ll_set_blank_cycles(lcd_cam_dev_t * dev,uint32_t fk_cycles,uint32_t bk_cycles)265 static inline void lcd_ll_set_blank_cycles(lcd_cam_dev_t *dev, uint32_t fk_cycles, uint32_t bk_cycles)
266 {
267     dev->lcd_misc.lcd_bk_en = (fk_cycles || bk_cycles);
268     dev->lcd_misc.lcd_vfk_cyclelen = fk_cycles - 1;
269     dev->lcd_misc.lcd_vbk_cyclelen = bk_cycles - 1;
270 }
271 
272 /**
273  * @brief Set data line width
274  *
275  * @param dev LCD register base address
276  * @param width data line width (8 or 16)
277  */
lcd_ll_set_data_width(lcd_cam_dev_t * dev,uint32_t width)278 static inline void lcd_ll_set_data_width(lcd_cam_dev_t *dev, uint32_t width)
279 {
280     HAL_ASSERT(width == 8 || width == 16);
281     dev->lcd_user.lcd_2byte_en = (width == 16);
282 }
283 
284 /**
285  * @brief Whether to continue the data phase when the DMA has content to send
286  *
287  * @param dev LCD register base address
288  * @param en True: The number of data cycles will be controller by DMA buffer size, instead of lcd_dout_cyclelen
289  *           False: The number of data cycles will be controlled by lcd_dout_cyclelen
290  */
lcd_ll_enable_output_always_on(lcd_cam_dev_t * dev,bool en)291 static inline void lcd_ll_enable_output_always_on(lcd_cam_dev_t *dev, bool en)
292 {
293     dev->lcd_user.lcd_always_out_en = en;
294 }
295 
296 /**
297  * @brief Start the LCD transaction
298  *
299  * @param dev LCD register base address
300  */
301 __attribute__((always_inline))
lcd_ll_start(lcd_cam_dev_t * dev)302 static inline void lcd_ll_start(lcd_cam_dev_t *dev)
303 {
304     dev->lcd_user.lcd_update = 1; // update parameters before start transaction
305     dev->lcd_user.lcd_start = 1;
306 }
307 
308 /**
309  * @brief Stop the LCD transaction
310  *
311  * @param dev LCD register base address
312  */
313 __attribute__((always_inline))
lcd_ll_stop(lcd_cam_dev_t * dev)314 static inline void lcd_ll_stop(lcd_cam_dev_t *dev)
315 {
316     dev->lcd_user.lcd_start = 0;
317     dev->lcd_user.lcd_update = 1; // self clear
318 }
319 
320 /**
321  * @brief Reset LCD TX controller and RGB/YUV converter
322  *
323  * @param dev LCD register base address
324  */
lcd_ll_reset(lcd_cam_dev_t * dev)325 static inline void lcd_ll_reset(lcd_cam_dev_t *dev)
326 {
327     dev->lcd_user.lcd_reset = 1; // self clear
328 }
329 
330 /**
331  * @brief Whether to reverse the data bit order
332  *
333  * @param dev LCD register base address
334  * @param en True to reverse, False to not reverse
335  */
336 __attribute__((always_inline))
lcd_ll_reverse_bit_order(lcd_cam_dev_t * dev,bool en)337 static inline void lcd_ll_reverse_bit_order(lcd_cam_dev_t *dev, bool en)
338 {
339     // whether to change LCD_DATA_out[N:0] to LCD_DATA_out[0:N]
340     dev->lcd_user.lcd_bit_order = en;
341 }
342 
343 /**
344  * @brief Whether to swap adjacent two bytes
345  *
346  * @param dev LCD register base address
347  * @param width Bus width
348  * @param en True to swap the byte order, False to not swap
349  */
350 __attribute__((always_inline))
lcd_ll_swap_byte_order(lcd_cam_dev_t * dev,uint32_t width,bool en)351 static inline void lcd_ll_swap_byte_order(lcd_cam_dev_t *dev, uint32_t width, bool en)
352 {
353     HAL_ASSERT(width == 8 || width == 16);
354     if (width == 8) {
355         // {B0}{B1}{B2}{B3} => {B1}{B0}{B3}{B2}
356         dev->lcd_user.lcd_8bits_order = en;
357         dev->lcd_user.lcd_byte_order = 0;
358     } else if (width == 16) {
359         // {B1,B0},{B3,B2} => {B0,B1}{B2,B3}
360         dev->lcd_user.lcd_byte_order = en;
361         dev->lcd_user.lcd_8bits_order = 0;
362     }
363 }
364 
365 /**
366  * @brief Reset Async TX FIFO
367  *
368  * @param dev LCD register base address
369  */
370 __attribute__((always_inline))
lcd_ll_fifo_reset(lcd_cam_dev_t * dev)371 static inline void lcd_ll_fifo_reset(lcd_cam_dev_t *dev)
372 {
373     dev->lcd_misc.lcd_afifo_reset = 1; // self clear
374 }
375 
376 /**
377  * @brief Set the level state of DC line, on different transaction phases
378  *
379  * @param dev LCD register base address
380  * @param idle_phase Level state of DC line on IDLE phase
381  * @param cmd_phase Level state of DC line on CMD phase
382  * @param dummy_phase Level state of DC line on DUMMY phase
383  * @param data_phase Level state of DC line on DATA phase
384  */
385 __attribute__((always_inline))
lcd_ll_set_dc_level(lcd_cam_dev_t * dev,bool idle_phase,bool cmd_phase,bool dummy_phase,bool data_phase)386 static inline void lcd_ll_set_dc_level(lcd_cam_dev_t *dev, bool idle_phase, bool cmd_phase, bool dummy_phase, bool data_phase)
387 {
388     dev->lcd_misc.lcd_cd_idle_edge = idle_phase;
389     dev->lcd_misc.lcd_cd_cmd_set = (cmd_phase != idle_phase);
390     dev->lcd_misc.lcd_cd_dummy_set = (dummy_phase != idle_phase);
391     dev->lcd_misc.lcd_cd_data_set = (data_phase != idle_phase);
392 }
393 
394 /**
395  * @brief Set cycle of delay for DC line
396  *
397  * @param dev LCD register base address
398  * @param delay Ticks of delay
399  */
lcd_ll_set_dc_delay_ticks(lcd_cam_dev_t * dev,uint32_t delay)400 static inline void lcd_ll_set_dc_delay_ticks(lcd_cam_dev_t *dev, uint32_t delay)
401 {
402     dev->lcd_dly_mode.lcd_cd_mode = delay;
403 }
404 
405 /**
406  * @brief Set the LCD command (the data at CMD phase)
407  *
408  * @param dev LCD register base address
409  * @param data_width Data line width
410  * @param command command value
411  */
412 __attribute__((always_inline))
lcd_ll_set_command(lcd_cam_dev_t * dev,uint32_t data_width,uint32_t command)413 static inline void lcd_ll_set_command(lcd_cam_dev_t *dev, uint32_t data_width, uint32_t command)
414 {
415     HAL_ASSERT(data_width == 8 || data_width == 16);
416     // if command phase has two cycles, in the first cycle, command[15:0] is sent out via lcd_data_out[15:0]
417     // in the second cycle, command[31:16] is sent out via lcd_data_out[15:0]
418     if (data_width == 8) {
419         command = (command & 0xFF) | (command & 0xFF00) << 8;
420     }
421     dev->lcd_cmd_val.lcd_cmd_value = command;
422 }
423 
424 /**
425  * @brief Wether to enable RGB interface
426  *
427  * @param dev LCD register base address
428  * @param en True to enable RGB interface, False to disable RGB interface
429  */
lcd_ll_enable_rgb_mode(lcd_cam_dev_t * dev,bool en)430 static inline void lcd_ll_enable_rgb_mode(lcd_cam_dev_t *dev, bool en)
431 {
432     dev->lcd_ctrl.lcd_rgb_mode_en = en;
433 }
434 
435 /**
436  * @brief Whether to send the next frame automatically
437  *
438  * @param dev LCD register base address
439  * @param en True to enable, False to disable
440  */
lcd_ll_enable_auto_next_frame(lcd_cam_dev_t * dev,bool en)441 static inline void lcd_ll_enable_auto_next_frame(lcd_cam_dev_t *dev, bool en)
442 {
443     // in RGB mode, enabling "next frame" means LCD controller keeps sending frame data
444     dev->lcd_misc.lcd_next_frame_en = en;
445 }
446 
447 /**
448  * @brief Wether to output HSYNC signal in porch resion
449  *
450  * @param dev LCD register base address
451  * @param en True to enable, False to disable
452  */
lcd_ll_enable_output_hsync_in_porch_region(lcd_cam_dev_t * dev,bool en)453 static inline void lcd_ll_enable_output_hsync_in_porch_region(lcd_cam_dev_t *dev, bool en)
454 {
455     dev->lcd_ctrl2.lcd_hs_blank_en = en;
456 }
457 
458 /**
459  * @brief Set HSYNC signal offset in the line
460  *
461  * @param dev LCD register base address
462  * @param offset_in_line Offset value
463  */
lcd_ll_set_hsync_position(lcd_cam_dev_t * dev,uint32_t offset_in_line)464 static inline void lcd_ll_set_hsync_position(lcd_cam_dev_t *dev, uint32_t offset_in_line)
465 {
466     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_ctrl2, lcd_hsync_position, offset_in_line);
467 }
468 
469 /**
470  * @brief Set RGB LCD horizontal timing
471  *
472  * @param dev LCD register base address
473  * @param hsw Horizontal sync width
474  * @param hbp Horizontal back porch
475  * @param active_width Horizontal active width
476  * @param hfp Horizontal front porch
477  */
lcd_ll_set_horizontal_timing(lcd_cam_dev_t * dev,uint32_t hsw,uint32_t hbp,uint32_t active_width,uint32_t hfp)478 static inline void lcd_ll_set_horizontal_timing(lcd_cam_dev_t *dev, uint32_t hsw, uint32_t hbp, uint32_t active_width, uint32_t hfp)
479 {
480     dev->lcd_ctrl2.lcd_hsync_width = hsw - 1;
481     dev->lcd_ctrl.lcd_hb_front = hbp + hsw - 1;
482     dev->lcd_ctrl1.lcd_ha_width = active_width - 1;
483     dev->lcd_ctrl1.lcd_ht_width = hsw + hbp + active_width + hfp - 1;
484 }
485 
486 /**
487  * @brief Set RGB vertical timing
488  *
489  * @param dev LCD register base address
490  * @param vsw Vertical sync width
491  * @param vbp Vertical back porch
492  * @param active_height Vertical active height
493  * @param vfp Vertical front porch
494  */
lcd_ll_set_vertical_timing(lcd_cam_dev_t * dev,uint32_t vsw,uint32_t vbp,uint32_t active_height,uint32_t vfp)495 static inline void lcd_ll_set_vertical_timing(lcd_cam_dev_t *dev, uint32_t vsw, uint32_t vbp, uint32_t active_height, uint32_t vfp)
496 {
497     dev->lcd_ctrl2.lcd_vsync_width = vsw - 1;
498     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_ctrl1, lcd_vb_front, vbp + vsw - 1);
499     dev->lcd_ctrl.lcd_va_height = active_height - 1;
500     dev->lcd_ctrl.lcd_vt_height = vsw + vbp + active_height + vfp - 1;
501 }
502 
503 /**
504  * @brief Set level state for hsync, vsync, de at IDLE phase
505  *
506  * @param dev LCD register base address
507  * @param hsync_idle_level HSYNC level on IDLE phase
508  * @param vsync_idle_level VSYNC level on IDLE phase
509  * @param de_idle_level DE level on IDLE phase
510  */
lcd_ll_set_idle_level(lcd_cam_dev_t * dev,bool hsync_idle_level,bool vsync_idle_level,bool de_idle_level)511 static inline void lcd_ll_set_idle_level(lcd_cam_dev_t *dev, bool hsync_idle_level, bool vsync_idle_level, bool de_idle_level)
512 {
513     dev->lcd_ctrl2.lcd_hsync_idle_pol = hsync_idle_level;
514     dev->lcd_ctrl2.lcd_vsync_idle_pol = vsync_idle_level;
515     dev->lcd_ctrl2.lcd_de_idle_pol = de_idle_level;
516 }
517 
518 /**
519  * @brief Set extra delay for HSYNC, VSYNC, and DE signals
520  *
521  * @param dev LCD register base address
522  * @param hsync_delay HSYNC delay
523  * @param vsync_delay VSYNC delay
524  * @param de_delay DE delay
525  */
lcd_ll_set_delay_ticks(lcd_cam_dev_t * dev,uint32_t hsync_delay,uint32_t vsync_delay,uint32_t de_delay)526 static inline void lcd_ll_set_delay_ticks(lcd_cam_dev_t *dev, uint32_t hsync_delay, uint32_t vsync_delay, uint32_t de_delay)
527 {
528     dev->lcd_dly_mode.lcd_hsync_mode = hsync_delay;
529     dev->lcd_dly_mode.lcd_vsync_mode = vsync_delay;
530     dev->lcd_dly_mode.lcd_de_mode = de_delay;
531 }
532 
533 /**
534  * @brief Set extra delay for data lines
535  *
536  * @param dev LCD register base address
537  * @param delay Data line delay
538  */
lcd_ll_set_data_delay_ticks(lcd_cam_dev_t * dev,uint32_t delay)539 static inline void lcd_ll_set_data_delay_ticks(lcd_cam_dev_t *dev, uint32_t delay)
540 {
541     uint32_t reg_val = 0;
542     for (int i = 0; i < 16; i++) {
543         reg_val |= (delay & 0x03) << (2 * i);
544     }
545     dev->lcd_data_dout_mode.val = reg_val;
546 }
547 
548 /**
549  * @brief Enable/disable interrupt by mask
550  *
551  * @param dev LCD register base address
552  * @param mask Interrupt mask
553  * @param en True to enable interrupt, False to disable interrupt
554  */
lcd_ll_enable_interrupt(lcd_cam_dev_t * dev,uint32_t mask,bool en)555 static inline void lcd_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bool en)
556 {
557     if (en) {
558         dev->lc_dma_int_ena.val |= mask & 0x03;
559     } else {
560         dev->lc_dma_int_ena.val &= ~(mask & 0x03);
561     }
562 }
563 
564 /**
565  * @brief Get interrupt status value
566  *
567  * @param dev LCD register base address
568  * @return Interrupt status value
569  */
570 __attribute__((always_inline))
lcd_ll_get_interrupt_status(lcd_cam_dev_t * dev)571 static inline uint32_t lcd_ll_get_interrupt_status(lcd_cam_dev_t *dev)
572 {
573     return dev->lc_dma_int_st.val & 0x03;
574 }
575 
576 /**
577  * @brief Clear interrupt status by mask
578  *
579  * @param dev LCD register base address
580  * @param mask Interupt status mask
581  */
582 __attribute__((always_inline))
lcd_ll_clear_interrupt_status(lcd_cam_dev_t * dev,uint32_t mask)583 static inline void lcd_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask)
584 {
585     dev->lc_dma_int_clr.val = mask & 0x03;
586 }
587 
588 /**
589  * @brief Get address of interrupt status register address
590  *
591  * @param dev LCD register base address
592  * @return Interrupt status register address
593  */
lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t * dev)594 static inline volatile void *lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev)
595 {
596     return &dev->lc_dma_int_st;
597 }
598 
599 #ifdef __cplusplus
600 }
601 #endif
602