1 /*
2  * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #pragma once
7 
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include "hal/misc.h"
11 #include "soc/lcd_cam_reg.h"
12 #include "soc/lcd_cam_struct.h"
13 #include "hal/assert.h"
14 #include "hal/lcd_types.h"
15 
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 
20 #define LCD_LL_GET_HW(id) (((id) == 0) ? (&LCD_CAM) : NULL)
21 
22 // Interrupt event, bit mask
23 #define LCD_LL_EVENT_VSYNC_END  (1 << 0)
24 #define LCD_LL_EVENT_TRANS_DONE (1 << 1)
25 
26 // Maximum coefficient of clock prescaler
27 #define LCD_LL_CLOCK_PRESCALE_MAX (64)
28 
lcd_ll_enable_clock(lcd_cam_dev_t * dev,bool en)29 static inline void lcd_ll_enable_clock(lcd_cam_dev_t *dev, bool en)
30 {
31     dev->lcd_clock.clk_en = en;
32 }
33 
lcd_ll_set_group_clock_src(lcd_cam_dev_t * dev,lcd_clock_source_t src,int div_num,int div_a,int div_b)34 static inline void lcd_ll_set_group_clock_src(lcd_cam_dev_t *dev, lcd_clock_source_t src, int div_num, int div_a, int div_b)
35 {
36     // lcd_clk = module_clock_src / (div_num + div_b / div_a)
37     HAL_ASSERT(div_num >= 2);
38     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_clock, lcd_clkm_div_num, div_num);
39     dev->lcd_clock.lcd_clkm_div_a = div_a;
40     dev->lcd_clock.lcd_clkm_div_b = div_b;
41     switch (src) {
42     case LCD_CLK_SRC_PLL160M:
43         dev->lcd_clock.lcd_clk_sel = 3;
44         break;
45     case LCD_CLK_SRC_XTAL:
46         dev->lcd_clock.lcd_clk_sel = 1;
47         break;
48     default:
49         HAL_ASSERT(false && "unsupported clock source");
50         break;
51     }
52 }
53 
54 __attribute__((always_inline))
lcd_ll_set_clock_idle_level(lcd_cam_dev_t * dev,bool level)55 static inline void lcd_ll_set_clock_idle_level(lcd_cam_dev_t *dev, bool level)
56 {
57     dev->lcd_clock.lcd_ck_idle_edge = level;
58 }
59 
60 __attribute__((always_inline))
lcd_ll_set_pixel_clock_edge(lcd_cam_dev_t * dev,bool active_on_neg)61 static inline void lcd_ll_set_pixel_clock_edge(lcd_cam_dev_t *dev, bool active_on_neg)
62 {
63     dev->lcd_clock.lcd_clk_equ_sysclk = 0; // if we want to pixel_clk == lcd_clk, just make clkcnt = 0
64     dev->lcd_clock.lcd_ck_out_edge = active_on_neg;
65 }
66 
67 __attribute__((always_inline))
lcd_ll_set_pixel_clock_prescale(lcd_cam_dev_t * dev,uint32_t prescale)68 static inline void lcd_ll_set_pixel_clock_prescale(lcd_cam_dev_t *dev, uint32_t prescale)
69 {
70     // Formula: pixel_clk = lcd_clk / (1 + clkcnt_n)
71     dev->lcd_clock.lcd_clkcnt_n = prescale - 1;
72 }
73 
lcd_ll_enable_rgb_yuv_convert(lcd_cam_dev_t * dev,bool en)74 static inline void lcd_ll_enable_rgb_yuv_convert(lcd_cam_dev_t *dev, bool en)
75 {
76     dev->lcd_rgb_yuv.lcd_conv_bypass = en;
77 }
78 
79 __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)80 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)
81 {
82     HAL_ASSERT(cmd_cycles <= 2);
83     dev->lcd_user.lcd_cmd = (cmd_cycles > 0);
84     dev->lcd_user.lcd_dummy = (dummy_cycles > 0);
85     dev->lcd_user.lcd_dout = (data_cycles > 0);
86     dev->lcd_user.lcd_cmd_2_cycle_en = cmd_cycles > 1;
87     dev->lcd_user.lcd_dummy_cyclelen = dummy_cycles - 1;
88     dev->lcd_user.lcd_dout_cyclelen = data_cycles - 1;
89 }
90 
lcd_ll_set_blank_cycles(lcd_cam_dev_t * dev,uint32_t fk_cycles,uint32_t bk_cycles)91 static inline void lcd_ll_set_blank_cycles(lcd_cam_dev_t *dev, uint32_t fk_cycles, uint32_t bk_cycles)
92 {
93     dev->lcd_misc.lcd_bk_en = (fk_cycles || bk_cycles);
94     dev->lcd_misc.lcd_vfk_cyclelen = fk_cycles - 1;
95     dev->lcd_misc.lcd_vbk_cyclelen = bk_cycles - 1;
96 }
97 
lcd_ll_set_data_width(lcd_cam_dev_t * dev,uint32_t width)98 static inline void lcd_ll_set_data_width(lcd_cam_dev_t *dev, uint32_t width)
99 {
100     dev->lcd_user.lcd_2byte_en = (width == 16);
101 }
102 
lcd_ll_get_data_width(lcd_cam_dev_t * dev)103 static inline uint32_t lcd_ll_get_data_width(lcd_cam_dev_t *dev)
104 {
105     return dev->lcd_user.lcd_2byte_en ? 16 : 8;
106 }
107 
lcd_ll_enable_output_always_on(lcd_cam_dev_t * dev,bool en)108 static inline void lcd_ll_enable_output_always_on(lcd_cam_dev_t *dev, bool en)
109 {
110     dev->lcd_user.lcd_always_out_en = en;
111 }
112 
113 __attribute__((always_inline))
lcd_ll_start(lcd_cam_dev_t * dev)114 static inline void lcd_ll_start(lcd_cam_dev_t *dev)
115 {
116     dev->lcd_user.lcd_update = 1; // update parameters before start transaction
117     dev->lcd_user.lcd_start = 1;
118 }
119 
lcd_ll_stop(lcd_cam_dev_t * dev)120 static inline void lcd_ll_stop(lcd_cam_dev_t *dev)
121 {
122     dev->lcd_user.lcd_start = 0;
123     dev->lcd_user.lcd_update = 1; // self clear
124 }
125 
lcd_ll_reset(lcd_cam_dev_t * dev)126 static inline void lcd_ll_reset(lcd_cam_dev_t *dev)
127 {
128     dev->lcd_user.lcd_reset = 1;
129     dev->lcd_user.lcd_reset = 0;
130 }
131 
132 __attribute__((always_inline))
lcd_ll_reverse_data_bit_order(lcd_cam_dev_t * dev,bool en)133 static inline void lcd_ll_reverse_data_bit_order(lcd_cam_dev_t *dev, bool en)
134 {
135     // whether to change LCD_DATA_out[N:0] to LCD_DATA_out[0:N]
136     dev->lcd_user.lcd_bit_order = en;
137 }
138 
139 __attribute__((always_inline))
lcd_ll_reverse_data_byte_order(lcd_cam_dev_t * dev,bool en)140 static inline void lcd_ll_reverse_data_byte_order(lcd_cam_dev_t *dev, bool en)
141 {
142     dev->lcd_user.lcd_byte_order = en;
143 }
144 
145 __attribute__((always_inline))
lcd_ll_reverse_data_8bits_order(lcd_cam_dev_t * dev,bool en)146 static inline void lcd_ll_reverse_data_8bits_order(lcd_cam_dev_t *dev, bool en)
147 {
148     dev->lcd_user.lcd_8bits_order = en;
149 }
150 
lcd_ll_fifo_reset(lcd_cam_dev_t * dev)151 static inline void lcd_ll_fifo_reset(lcd_cam_dev_t *dev)
152 {
153     dev->lcd_misc.lcd_afifo_reset = 1;
154     dev->lcd_misc.lcd_afifo_reset = 0;
155 }
156 
157 __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)158 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)
159 {
160     dev->lcd_misc.lcd_cd_idle_edge = idle_phase;
161     dev->lcd_misc.lcd_cd_cmd_set = (cmd_phase != idle_phase);
162     dev->lcd_misc.lcd_cd_dummy_set = (dummy_phase != idle_phase);
163     dev->lcd_misc.lcd_cd_data_set = (data_phase != idle_phase);
164 }
165 
lcd_ll_set_dc_delay_ticks(lcd_cam_dev_t * dev,uint32_t delay)166 static inline void lcd_ll_set_dc_delay_ticks(lcd_cam_dev_t *dev, uint32_t delay)
167 {
168     dev->lcd_dly_mode.lcd_cd_mode = delay;
169 }
170 
171 __attribute__((always_inline))
lcd_ll_set_command(lcd_cam_dev_t * dev,uint32_t data_width,uint32_t command)172 static inline void lcd_ll_set_command(lcd_cam_dev_t *dev, uint32_t data_width, uint32_t command)
173 {
174     // if command phase has two cycles, in the first cycle, command[15:0] is sent out via lcd_data_out[15:0]
175     // in the second cycle, command[31:16] is sent out via lcd_data_out[15:0]
176     if (data_width == 8) {
177         command = (command & 0xFF) | (command & 0xFF00) << 8;
178     }
179     dev->lcd_cmd_val.lcd_cmd_value = command;
180 }
181 
lcd_ll_enable_rgb_mode(lcd_cam_dev_t * dev,bool en)182 static inline void lcd_ll_enable_rgb_mode(lcd_cam_dev_t *dev, bool en)
183 {
184     dev->lcd_ctrl.lcd_rgb_mode_en = en;
185 }
186 
lcd_ll_enable_auto_next_frame(lcd_cam_dev_t * dev,bool en)187 static inline void lcd_ll_enable_auto_next_frame(lcd_cam_dev_t *dev, bool en)
188 {
189     // in RGB mode, enabling "next frame" means LCD controller keeps sending frame data
190     dev->lcd_misc.lcd_next_frame_en = en;
191 }
192 
lcd_ll_enable_output_hsync_in_porch_region(lcd_cam_dev_t * dev,bool en)193 static inline void lcd_ll_enable_output_hsync_in_porch_region(lcd_cam_dev_t *dev, bool en)
194 {
195     dev->lcd_ctrl2.lcd_hs_blank_en = en;
196 }
197 
lcd_ll_set_hsync_position(lcd_cam_dev_t * dev,uint32_t offset_in_line)198 static inline void lcd_ll_set_hsync_position(lcd_cam_dev_t *dev, uint32_t offset_in_line)
199 {
200     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_ctrl2, lcd_hsync_position, offset_in_line);
201 }
202 
lcd_ll_set_horizontal_timing(lcd_cam_dev_t * dev,uint32_t hsw,uint32_t hbp,uint32_t active_width,uint32_t hfp)203 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)
204 {
205     dev->lcd_ctrl2.lcd_hsync_width = hsw - 1;
206     dev->lcd_ctrl.lcd_hb_front = hbp + hsw - 1;
207     dev->lcd_ctrl1.lcd_ha_width = active_width - 1;
208     dev->lcd_ctrl1.lcd_ht_width = hsw + hbp + active_width + hfp - 1;
209 }
210 
lcd_ll_set_vertical_timing(lcd_cam_dev_t * dev,uint32_t vsw,uint32_t vbp,uint32_t active_height,uint32_t vfp)211 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)
212 {
213     dev->lcd_ctrl2.lcd_vsync_width = vsw - 1;
214     HAL_FORCE_MODIFY_U32_REG_FIELD(dev->lcd_ctrl1, lcd_vb_front, vbp + vsw - 1);
215     dev->lcd_ctrl.lcd_va_height = active_height - 1;
216     dev->lcd_ctrl.lcd_vt_height = vsw + vbp + active_height + vfp - 1;
217 }
218 
lcd_ll_set_idle_level(lcd_cam_dev_t * dev,bool hsync_idle_level,bool vsync_idle_level,bool de_idle_level)219 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)
220 {
221     dev->lcd_ctrl2.lcd_hsync_idle_pol = hsync_idle_level;
222     dev->lcd_ctrl2.lcd_vsync_idle_pol = vsync_idle_level;
223     dev->lcd_ctrl2.lcd_de_idle_pol = de_idle_level;
224 }
225 
lcd_ll_set_delay_ticks(lcd_cam_dev_t * dev,uint32_t hsync_delay,uint32_t vsync_delay,uint32_t de_delay)226 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)
227 {
228     dev->lcd_dly_mode.lcd_hsync_mode = hsync_delay;
229     dev->lcd_dly_mode.lcd_vsync_mode = vsync_delay;
230     dev->lcd_dly_mode.lcd_de_mode = de_delay;
231 }
232 
lcd_ll_set_data_delay_ticks(lcd_cam_dev_t * dev,uint32_t delay)233 static inline void lcd_ll_set_data_delay_ticks(lcd_cam_dev_t *dev, uint32_t delay)
234 {
235     uint32_t reg_val = 0;
236     for (int i = 0; i < 16; i++) {
237         reg_val |= (delay & 0x03) << (2 * i);
238     }
239     dev->lcd_data_dout_mode.val = reg_val;
240 }
241 
lcd_ll_enable_interrupt(lcd_cam_dev_t * dev,uint32_t mask,bool en)242 static inline void lcd_ll_enable_interrupt(lcd_cam_dev_t *dev, uint32_t mask, bool en)
243 {
244     if (en) {
245         dev->lc_dma_int_ena.val |= mask & 0x03;
246     } else {
247         dev->lc_dma_int_ena.val &= ~(mask & 0x03);
248     }
249 }
250 
251 __attribute__((always_inline))
lcd_ll_get_interrupt_status(lcd_cam_dev_t * dev)252 static inline uint32_t lcd_ll_get_interrupt_status(lcd_cam_dev_t *dev)
253 {
254     return dev->lc_dma_int_st.val & 0x03;
255 }
256 
257 __attribute__((always_inline))
lcd_ll_clear_interrupt_status(lcd_cam_dev_t * dev,uint32_t mask)258 static inline void lcd_ll_clear_interrupt_status(lcd_cam_dev_t *dev, uint32_t mask)
259 {
260     dev->lc_dma_int_clr.val = mask & 0x03;
261 }
262 
lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t * dev)263 static inline volatile void *lcd_ll_get_interrupt_status_reg(lcd_cam_dev_t *dev)
264 {
265     return &dev->lc_dma_int_st;
266 }
267 
268 #ifdef __cplusplus
269 }
270 #endif
271