1 /**
2  * @file lv_nuttx_lcd.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_nuttx_lcd.h"
11 
12 #if LV_USE_NUTTX
13 
14 #if LV_USE_NUTTX_LCD
15 
16 #include <sys/ioctl.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <debug.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <nuttx/lcd/lcd_dev.h>
24 
25 #include "../../../lvgl.h"
26 #include "../../lvgl_private.h"
27 
28 /*********************
29  *      DEFINES
30  *********************/
31 
32 /**********************
33  *      TYPEDEFS
34  **********************/
35 
36 typedef struct {
37     /* fd should be defined at the beginning */
38     int fd;
39     lv_display_t * disp;
40     struct lcddev_area_s area;
41     struct lcddev_area_align_s align_info;
42 } lv_nuttx_lcd_t;
43 
44 /**********************
45  *  STATIC PROTOTYPES
46  **********************/
47 
48 static int32_t align_round_up(int32_t v, uint16_t align);
49 static void rounder_cb(lv_event_t * e);
50 static void flush_cb(lv_display_t * disp, const lv_area_t * area_p,
51                      uint8_t * color_p);
52 static lv_display_t * lcd_init(int fd, int hor_res, int ver_res);
53 static void display_release_cb(lv_event_t * e);
54 
55 /**********************
56  *  STATIC VARIABLES
57  **********************/
58 
59 /**********************
60  *      MACROS
61  **********************/
62 
63 /**********************
64  *   GLOBAL FUNCTIONS
65  **********************/
66 
lv_nuttx_lcd_create(const char * dev_path)67 lv_display_t * lv_nuttx_lcd_create(const char * dev_path)
68 {
69     struct fb_videoinfo_s vinfo;
70     struct lcd_planeinfo_s pinfo;
71     lv_display_t * disp;
72     int fd;
73     int ret;
74 
75     LV_ASSERT_NULL(dev_path);
76 
77     LV_LOG_USER("lcd %s opening", dev_path);
78     fd = open(dev_path, 0);
79     if(fd < 0) {
80         perror("Error: cannot open lcd device");
81         return NULL;
82     }
83 
84     LV_LOG_USER("lcd %s open success", dev_path);
85 
86     ret = ioctl(fd, LCDDEVIO_GETVIDEOINFO,
87                 (unsigned long)((uintptr_t)&vinfo));
88     if(ret < 0) {
89         perror("Error: ioctl(LCDDEVIO_GETVIDEOINFO) failed");
90         close(fd);
91         return NULL;
92     }
93 
94     ret = ioctl(fd, LCDDEVIO_GETPLANEINFO,
95                 (unsigned long)((uintptr_t)&pinfo));
96     if(ret < 0) {
97         perror("ERROR: ioctl(LCDDEVIO_GETPLANEINFO) failed");
98         close(fd);
99         return NULL;
100     }
101 
102     disp = lcd_init(fd, vinfo.xres, vinfo.yres);
103     if(disp == NULL) {
104         close(fd);
105     }
106 
107     return disp;
108 }
109 
110 /**********************
111  *   STATIC FUNCTIONS
112  **********************/
113 
align_round_up(int32_t v,uint16_t align)114 static int32_t align_round_up(int32_t v, uint16_t align)
115 {
116     return (v + align - 1) & ~(align - 1);
117 }
118 
rounder_cb(lv_event_t * e)119 static void rounder_cb(lv_event_t * e)
120 {
121     lv_nuttx_lcd_t * lcd = lv_event_get_user_data(e);
122     lv_area_t * area = lv_event_get_param(e);
123     struct lcddev_area_align_s * align_info = &lcd->align_info;
124     int32_t w;
125     int32_t h;
126 
127     area->x1 &= ~(align_info->col_start_align - 1);
128     area->y1 &= ~(align_info->row_start_align - 1);
129 
130     w = align_round_up(lv_area_get_width(area), align_info->width_align);
131     h = align_round_up(lv_area_get_height(area), align_info->height_align);
132 
133     area->x2 = area->x1 + w - 1;
134     area->y2 = area->y1 + h - 1;
135 }
136 
flush_cb(lv_display_t * disp,const lv_area_t * area_p,uint8_t * color_p)137 static void flush_cb(lv_display_t * disp, const lv_area_t * area_p,
138                      uint8_t * color_p)
139 {
140     lv_nuttx_lcd_t * lcd = disp->driver_data;
141 
142     lcd->area.row_start = area_p->y1;
143     lcd->area.row_end = area_p->y2;
144     lcd->area.col_start = area_p->x1;
145     lcd->area.col_end = area_p->x2;
146     lcd->area.data = (uint8_t *)color_p;
147     ioctl(lcd->fd, LCDDEVIO_PUTAREA, (unsigned long) & (lcd->area));
148     lv_display_flush_ready(disp);
149 }
150 
lcd_init(int fd,int hor_res,int ver_res)151 static lv_display_t * lcd_init(int fd, int hor_res, int ver_res)
152 {
153     uint8_t * draw_buf = NULL;
154     uint8_t * draw_buf_2 = NULL;
155     lv_nuttx_lcd_t * lcd = lv_malloc_zeroed(sizeof(lv_nuttx_lcd_t));
156     LV_ASSERT_MALLOC(lcd);
157     if(lcd == NULL) {
158         LV_LOG_ERROR("lv_nuttx_lcd_t malloc failed");
159         return NULL;
160     }
161 
162     lv_display_t * disp = lv_display_create(hor_res, ver_res);
163     if(disp == NULL) {
164         lv_free(lcd);
165         return NULL;
166     }
167 
168     uint32_t px_size = lv_color_format_get_size(lv_display_get_color_format(disp));
169 #if LV_NUTTX_LCD_BUFFER_COUNT > 0
170     uint32_t buf_size = hor_res * ver_res * px_size;
171     lv_display_render_mode_t render_mode = LV_DISPLAY_RENDER_MODE_FULL;
172 #else
173     uint32_t buf_size = hor_res * LV_NUTTX_LCD_BUFFER_SIZE * px_size;
174     lv_display_render_mode_t render_mode = LV_DISPLAY_RENDER_MODE_PARTIAL;
175 #endif
176 
177     draw_buf = lv_malloc(buf_size);
178     if(draw_buf == NULL) {
179         LV_LOG_ERROR("display draw_buf malloc failed");
180         lv_free(lcd);
181         return NULL;
182     }
183 
184 #if LV_NUTTX_LCD_BUFFER_COUNT == 2
185     draw_buf_2 = lv_malloc(buf_size);
186     if(draw_buf_2 == NULL) {
187         LV_LOG_ERROR("display draw_buf_2 malloc failed");
188         lv_free(lcd);
189         lv_free(draw_buf);
190         return NULL;
191     }
192 #endif
193 
194     lcd->fd = fd;
195     if(ioctl(fd, LCDDEVIO_GETAREAALIGN, &lcd->align_info) < 0) {
196         perror("Error: ioctl(LCDDEVIO_GETAREAALIGN) failed");
197     }
198 
199     lcd->disp = disp;
200     lv_display_set_buffers(lcd->disp, draw_buf, draw_buf_2, buf_size, render_mode);
201     lv_display_set_flush_cb(lcd->disp, flush_cb);
202     lv_display_add_event_cb(lcd->disp, rounder_cb, LV_EVENT_INVALIDATE_AREA, lcd);
203     lv_display_add_event_cb(lcd->disp, display_release_cb, LV_EVENT_DELETE, lcd->disp);
204     lv_display_set_driver_data(lcd->disp, lcd);
205 
206     return lcd->disp;
207 }
208 
display_release_cb(lv_event_t * e)209 static void display_release_cb(lv_event_t * e)
210 {
211     lv_display_t * disp = (lv_display_t *) lv_event_get_user_data(e);
212     lv_nuttx_lcd_t * dsc = lv_display_get_driver_data(disp);
213     if(dsc) {
214         lv_display_set_driver_data(disp, NULL);
215         lv_display_set_flush_cb(disp, NULL);
216 
217         /* clear display buffer */
218         if(disp->buf_1) {
219             lv_free(disp->buf_1);
220             disp->buf_1 = NULL;
221         }
222         if(disp->buf_2) {
223             lv_free(disp->buf_2);
224             disp->buf_2 = NULL;
225         }
226 
227         /* close device fb */
228         if(dsc->fd >= 0) {
229             close(dsc->fd);
230             dsc->fd = -1;
231         }
232         lv_free(dsc);
233         LV_LOG_USER("Done");
234     }
235 }
236 #endif /*LV_USE_NUTTX_LCD*/
237 
238 #endif /* LV_USE_NUTTX*/
239