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