1 /**
2 * @file lv_lcd_generic_mipi.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9
10 #include "lv_lcd_generic_mipi.h"
11
12 #if LV_USE_GENERIC_MIPI
13
14 /*********************
15 * DEFINES
16 *********************/
17
18 /**********************
19 * TYPEDEFS
20 **********************/
21
22 /**********************
23 * STATIC PROTOTYPES
24 **********************/
25
26 static void send_cmd(lv_lcd_generic_mipi_driver_t * drv, uint8_t cmd, uint8_t * param, size_t param_size);
27 static void send_color(lv_lcd_generic_mipi_driver_t * drv, uint8_t cmd, uint8_t * param, size_t param_size);
28 static void init(lv_lcd_generic_mipi_driver_t * drv, lv_lcd_flag_t flags);
29 static void set_mirror(lv_lcd_generic_mipi_driver_t * drv, bool mirror_x, bool mirror_y);
30 static void set_swap_xy(lv_lcd_generic_mipi_driver_t * drv, bool swap);
31 static void set_rotation(lv_lcd_generic_mipi_driver_t * drv, lv_display_rotation_t rot);
32 static void res_chg_event_cb(lv_event_t * e);
33 static lv_lcd_generic_mipi_driver_t * get_driver(lv_display_t * disp);
34 static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
35
36 /**********************
37 * STATIC VARIABLES
38 **********************/
39
40 /**********************
41 * MACROS
42 **********************/
43
44 /**********************
45 * GLOBAL FUNCTIONS
46 **********************/
47
lv_lcd_generic_mipi_create(uint32_t hor_res,uint32_t ver_res,lv_lcd_flag_t flags,lv_lcd_send_cmd_cb_t send_cmd_cb,lv_lcd_send_color_cb_t send_color_cb)48 lv_display_t * lv_lcd_generic_mipi_create(uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags,
49 lv_lcd_send_cmd_cb_t send_cmd_cb, lv_lcd_send_color_cb_t send_color_cb)
50 {
51 lv_display_t * disp = lv_display_create(hor_res, ver_res);
52 if(disp == NULL) {
53 return NULL;
54 }
55
56 lv_lcd_generic_mipi_driver_t * drv = (lv_lcd_generic_mipi_driver_t *)lv_malloc(sizeof(lv_lcd_generic_mipi_driver_t));
57 if(drv == NULL) {
58 lv_display_delete(disp);
59 return NULL;
60 }
61
62 /* init driver struct */
63 drv->disp = disp;
64 drv->send_cmd = send_cmd_cb;
65 drv->send_color = send_color_cb;
66 lv_display_set_driver_data(disp, (void *)drv);
67
68 /* init controller */
69 init(drv, flags);
70
71 /* register resolution change callback (NOTE: this handles screen rotation as well) */
72 lv_display_add_event_cb(disp, res_chg_event_cb, LV_EVENT_RESOLUTION_CHANGED, NULL);
73
74 /* register flush callback */
75 lv_display_set_flush_cb(disp, flush_cb);
76
77 return disp;
78 }
79
lv_lcd_generic_mipi_set_gap(lv_display_t * disp,uint16_t x,uint16_t y)80 void lv_lcd_generic_mipi_set_gap(lv_display_t * disp, uint16_t x, uint16_t y)
81 {
82 lv_lcd_generic_mipi_driver_t * drv = get_driver(disp);
83 drv->x_gap = x;
84 drv->y_gap = y;
85 }
86
lv_lcd_generic_mipi_set_invert(lv_display_t * disp,bool invert)87 void lv_lcd_generic_mipi_set_invert(lv_display_t * disp, bool invert)
88 {
89 lv_lcd_generic_mipi_driver_t * drv = get_driver(disp);
90 send_cmd(drv, invert ? LV_LCD_CMD_ENTER_INVERT_MODE : LV_LCD_CMD_EXIT_INVERT_MODE, NULL, 0);
91 }
92
lv_lcd_generic_mipi_set_address_mode(lv_display_t * disp,bool mirror_x,bool mirror_y,bool swap_xy,bool bgr)93 void lv_lcd_generic_mipi_set_address_mode(lv_display_t * disp, bool mirror_x, bool mirror_y, bool swap_xy, bool bgr)
94 {
95 lv_lcd_generic_mipi_driver_t * drv = get_driver(disp);
96 uint8_t mad = drv->madctl_reg & ~(LV_LCD_MASK_RGB_ORDER);
97 if(bgr) {
98 mad |= LV_LCD_BIT_RGB_ORDER__BGR;
99 }
100 drv->madctl_reg = mad;
101 drv->mirror_x = mirror_x;
102 drv->mirror_y = mirror_y;
103 drv->swap_xy = swap_xy;
104 set_rotation(drv, lv_display_get_rotation(disp)); /* update screen */
105 }
106
lv_lcd_generic_mipi_set_gamma_curve(lv_display_t * disp,uint8_t gamma)107 void lv_lcd_generic_mipi_set_gamma_curve(lv_display_t * disp, uint8_t gamma)
108 {
109 lv_lcd_generic_mipi_driver_t * drv = get_driver(disp);
110 send_cmd(drv, LV_LCD_CMD_SET_GAMMA_CURVE, (uint8_t[]) {
111 gamma,
112 }, 1);
113 }
114
lv_lcd_generic_mipi_send_cmd_list(lv_display_t * disp,const uint8_t * cmd_list)115 void lv_lcd_generic_mipi_send_cmd_list(lv_display_t * disp, const uint8_t * cmd_list)
116 {
117 lv_lcd_generic_mipi_driver_t * drv = get_driver(disp);
118 while(1) {
119 uint8_t cmd = *cmd_list++;
120 uint8_t num = *cmd_list++;
121 if(cmd == LV_LCD_CMD_DELAY_MS) {
122 if(num == LV_LCD_CMD_EOF) /* end of list */
123 break;
124 else { /* delay in 10 ms units*/
125 lv_delay_ms((uint32_t)(num) * 10);
126 }
127 }
128 else {
129 drv->send_cmd(drv->disp, &cmd, 1, cmd_list, num);
130 cmd_list += num;
131 }
132 }
133 }
134
135 /**********************
136 * STATIC FUNCTIONS
137 **********************/
138
139 /**
140 * Helper function to call the user-supplied 'send_cmd' function
141 * @param drv LCD driver object
142 * @param cmd command byte
143 * @param param parameter buffer
144 * @param param_size number of bytes of the parameters
145 */
send_cmd(lv_lcd_generic_mipi_driver_t * drv,uint8_t cmd,uint8_t * param,size_t param_size)146 static void send_cmd(lv_lcd_generic_mipi_driver_t * drv, uint8_t cmd, uint8_t * param, size_t param_size)
147 {
148 uint8_t cmdbuf = cmd; /* MIPI uses 8 bit commands */
149 drv->send_cmd(drv->disp, &cmdbuf, 1, param, param_size);
150 }
151
152 /**
153 * Helper function to call the user-supplied 'send_color' function
154 * @param drv LCD driver object
155 * @param cmd command byte
156 * @param param parameter buffer
157 * @param param_size number of bytes of the parameters
158 */
send_color(lv_lcd_generic_mipi_driver_t * drv,uint8_t cmd,uint8_t * param,size_t param_size)159 static void send_color(lv_lcd_generic_mipi_driver_t * drv, uint8_t cmd, uint8_t * param, size_t param_size)
160 {
161 uint8_t cmdbuf = cmd; /* MIPI uses 8 bit commands */
162 drv->send_color(drv->disp, &cmdbuf, 1, param, param_size);
163 }
164
165 /**
166 * Initialize LCD driver after a hard reset
167 * @param drv LCD driver object
168 */
init(lv_lcd_generic_mipi_driver_t * drv,lv_lcd_flag_t flags)169 static void init(lv_lcd_generic_mipi_driver_t * drv, lv_lcd_flag_t flags)
170 {
171 drv->x_gap = 0;
172 drv->y_gap = 0;
173
174 /* init color mode and RGB order */
175 drv->madctl_reg = flags & LV_LCD_FLAG_BGR ? LV_LCD_BIT_RGB_ORDER__BGR : LV_LCD_BIT_RGB_ORDER__RGB;
176 drv->colmod_reg = flags & LV_LCD_FLAG_RGB666 ? LV_LCD_PIXEL_FORMAT_RGB666 : LV_LCD_PIXEL_FORMAT_RGB565;
177
178 /* init orientation */
179 drv->mirror_x = flags & LV_LCD_FLAG_MIRROR_X;
180 drv->mirror_y = flags & LV_LCD_FLAG_MIRROR_Y;
181 drv->swap_xy = false;
182 /* update madctl_reg */
183 set_swap_xy(drv, drv->swap_xy);
184 set_mirror(drv, drv->mirror_x, drv->mirror_y);
185
186 /* enter sleep mode first */
187 send_cmd(drv, LV_LCD_CMD_ENTER_SLEEP_MODE, NULL, 0);
188 lv_delay_ms(10);
189
190 /* perform software reset */
191 send_cmd(drv, LV_LCD_CMD_SOFT_RESET, NULL, 0);
192 lv_delay_ms(200);
193
194 /* LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first */
195 send_cmd(drv, LV_LCD_CMD_EXIT_SLEEP_MODE, NULL, 0);
196 lv_delay_ms(300);
197
198 send_cmd(drv, LV_LCD_CMD_ENTER_NORMAL_MODE, NULL, 0);
199
200 send_cmd(drv, LV_LCD_CMD_SET_ADDRESS_MODE, (uint8_t[]) {
201 drv->madctl_reg,
202 }, 1);
203 send_cmd(drv, LV_LCD_CMD_SET_PIXEL_FORMAT, (uint8_t[]) {
204 drv->colmod_reg,
205 }, 1);
206 send_cmd(drv, LV_LCD_CMD_SET_DISPLAY_ON, NULL, 0);
207 }
208
209 /**
210 * Set readout directions (used for rotating the display)
211 * @param drv LCD driver object
212 * @param mirror_x false: normal, true: mirrored
213 * @param mirror_y false: normal, true: mirrored
214 */
set_mirror(lv_lcd_generic_mipi_driver_t * drv,bool mirror_x,bool mirror_y)215 static void set_mirror(lv_lcd_generic_mipi_driver_t * drv, bool mirror_x, bool mirror_y)
216 {
217 uint8_t mad = drv->madctl_reg & ~(LV_LCD_MASK_COLUMN_ADDRESS_ORDER | LV_LCD_MASK_PAGE_ADDRESS_ORDER);
218 if(mirror_x) {
219 mad |= LV_LCD_BIT_COLUMN_ADDRESS_ORDER__RTOL;
220 }
221 if(mirror_y) {
222 mad |= LV_LCD_BIT_PAGE_ADDRESS_ORDER__BTOT;
223 }
224 drv->madctl_reg = mad;
225 }
226
227 /**
228 * Swap horizontal and vertical readout (used for rotating the display)
229 * @param drv LCD driver object
230 * @param swap false: normal, true: swapped
231 */
set_swap_xy(lv_lcd_generic_mipi_driver_t * drv,bool swap)232 static void set_swap_xy(lv_lcd_generic_mipi_driver_t * drv, bool swap)
233 {
234 uint8_t mad = drv->madctl_reg & ~(LV_LCD_MASK_PAGE_COLUMN_ORDER);
235 if(swap) {
236 mad |= LV_LCD_BIT_PAGE_COLUMN_ORDER__REVERSE;
237 }
238 drv->madctl_reg = mad;
239 }
240
241 /**
242 * Flush display buffer to the LCD
243 * @param disp display object
244 * @param hor_res horizontal resolution
245 * @param area area stored in the buffer
246 * @param px_map buffer containing pixel data
247 * @note transfers pixel data to the LCD controller using the callbacks 'send_cmd' and 'send_color', which were
248 * passed to the 'lv_st7789_create()' function
249 */
flush_cb(lv_display_t * disp,const lv_area_t * area,uint8_t * px_map)250 static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
251 {
252 lv_lcd_generic_mipi_driver_t * drv = get_driver(disp);
253
254 int32_t x_start = area->x1;
255 int32_t x_end = area->x2 + 1;
256 int32_t y_start = area->y1;
257 int32_t y_end = area->y2 + 1;
258
259 LV_ASSERT((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
260
261 x_start += drv->x_gap;
262 x_end += drv->x_gap;
263 y_start += drv->y_gap;
264 y_end += drv->y_gap;
265
266 /* define an area of frame memory where MCU can access */
267 send_cmd(drv, LV_LCD_CMD_SET_COLUMN_ADDRESS, (uint8_t[]) {
268 (x_start >> 8) & 0xFF,
269 x_start & 0xFF,
270 ((x_end - 1) >> 8) & 0xFF,
271 (x_end - 1) & 0xFF,
272 }, 4);
273 send_cmd(drv, LV_LCD_CMD_SET_PAGE_ADDRESS, (uint8_t[]) {
274 (y_start >> 8) & 0xFF,
275 y_start & 0xFF,
276 ((y_end - 1) >> 8) & 0xFF,
277 (y_end - 1) & 0xFF,
278 }, 4);
279 /* transfer frame buffer */
280 size_t len = (x_end - x_start) * (y_end - y_start) * lv_color_format_get_size(lv_display_get_color_format(disp));
281 send_color(drv, LV_LCD_CMD_WRITE_MEMORY_START, px_map, len);
282 }
283
284 /**
285 * Set rotation taking into account the current mirror and swap settings
286 * @param drv LCD driver object
287 * @param rot rotation
288 */
set_rotation(lv_lcd_generic_mipi_driver_t * drv,lv_display_rotation_t rot)289 static void set_rotation(lv_lcd_generic_mipi_driver_t * drv, lv_display_rotation_t rot)
290 {
291 switch(rot) {
292 case LV_DISPLAY_ROTATION_0:
293 set_swap_xy(drv, drv->swap_xy);
294 set_mirror(drv, drv->mirror_x, drv->mirror_y);
295 break;
296 case LV_DISPLAY_ROTATION_90:
297 set_swap_xy(drv, !drv->swap_xy);
298 set_mirror(drv, drv->mirror_x, !drv->mirror_y);
299 break;
300 case LV_DISPLAY_ROTATION_180:
301 set_swap_xy(drv, drv->swap_xy);
302 set_mirror(drv, !drv->mirror_x, !drv->mirror_y);
303 break;
304 case LV_DISPLAY_ROTATION_270:
305 set_swap_xy(drv, !drv->swap_xy);
306 set_mirror(drv, !drv->mirror_x, drv->mirror_y);
307 break;
308 }
309 send_cmd(drv, LV_LCD_CMD_SET_ADDRESS_MODE, (uint8_t[]) {
310 drv->madctl_reg
311 }, 1);
312 }
313
314 /**
315 * Handle LV_EVENT_RESOLUTION_CHANGED event (handles both resolution and rotation change)
316 * @param e LV_EVENT_RESOLUTION_CHANGED event
317 */
res_chg_event_cb(lv_event_t * e)318 static void res_chg_event_cb(lv_event_t * e)
319 {
320 lv_display_t * disp = lv_event_get_current_target(e);
321 lv_lcd_generic_mipi_driver_t * drv = get_driver(disp);
322
323 uint16_t hor_res = lv_display_get_horizontal_resolution(disp);
324 uint16_t ver_res = lv_display_get_vertical_resolution(disp);
325 lv_display_rotation_t rot = lv_display_get_rotation(disp);
326
327 /* TODO: implement resolution change */
328 LV_UNUSED(hor_res);
329 LV_UNUSED(ver_res);
330
331 /* handle rotation */
332 set_rotation(drv, rot);
333 }
334
get_driver(lv_display_t * disp)335 static lv_lcd_generic_mipi_driver_t * get_driver(lv_display_t * disp)
336 {
337 return (lv_lcd_generic_mipi_driver_t *)lv_display_get_driver_data(disp);
338 }
339
340 #endif /*LV_USE_GENERIC_MIPI*/
341