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