1 /*
2 * Copyright (c) 2006-2025, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-11-24 Rbb666 the first version
9 */
10
11 #include <rtthread.h>
12 #include <rtdevice.h>
13
14 #ifdef BSP_USING_LCD
15 #ifdef SOC_SERIES_R7FA8M85
16 #include <ra8/lcd_config.h>
17 #else
18 #include <ra6m3/lcd_config.h>
19 #endif
20 #include <drv_lcd.h>
21
22 #include "hal_data.h"
23
24 #define DRV_DEBUG
25 #define LOG_TAG "drv_lcd"
26 #include <drv_log.h>
27 struct drv_lcd_device
28 {
29 struct rt_device parent;
30 struct rt_device_graphic_info lcd_info;
31 };
32
33 struct drv_lcd_device _lcd;
34 static uint16_t screen_rotation;
35 static struct rt_completion sync_completion;
36
37 static uint16_t *gp_single_buffer = NULL;
38 static uint16_t *gp_double_buffer = NULL;
39 static uint16_t *lcd_current_working_buffer = (uint16_t *) &fb_background[0];
40
41 #ifdef SOC_SERIES_R7FA8M85
42 static uint8_t lcd_framebuffer[LCD_BUF_SIZE] BSP_ALIGN_VARIABLE(64) BSP_PLACE_IN_SECTION(".sdram");
43 #endif
44
45 // G2D
46 extern d2_device *d2_handle0;
47 static d2_device **_d2_handle_user = &d2_handle0;
48 static d2_renderbuffer *renderbuffer;
49
50 #ifdef SOC_SERIES_R7FA8M85
51 extern void ra8_mipi_lcd_init(void);
52 #endif
53
DisplayVsyncCallback(display_callback_args_t * p_args)54 rt_weak void DisplayVsyncCallback(display_callback_args_t *p_args)
55 {
56 rt_interrupt_enter();
57 if (DISPLAY_EVENT_LINE_DETECTION == p_args->event)
58 {
59 rt_completion_done(&sync_completion);
60 }
61 rt_interrupt_leave();
62 }
63
64 // Wait until Vsync is triggered through callback function
vsync_wait(void)65 static void vsync_wait(void)
66 {
67 rt_completion_wait(&sync_completion, RT_WAITING_FOREVER);
68 }
69
turn_on_lcd_backlight(void)70 static void turn_on_lcd_backlight(void)
71 {
72 #ifdef BSP_USING_LCD_PWM_BACKLIGHT
73 struct rt_device_pwm *pwm_dev;
74
75 /* turn on the LCD backlight */
76 pwm_dev = (struct rt_device_pwm *)rt_device_find(LCD_PWM_DEV_NAME);
77 /* pwm frequency:100K = 10000ns */
78 rt_pwm_set(pwm_dev, 0, 10000, 7000);
79 rt_pwm_enable(pwm_dev, 0);
80 #else
81 rt_pin_mode(LCD_BL_PIN, PIN_MODE_OUTPUT); /* LCD_BL */
82 rt_pin_write(LCD_BL_PIN, PIN_HIGH);
83 #endif
84 }
85
ra_bsp_lcd_clear(uint16_t color)86 static void ra_bsp_lcd_clear(uint16_t color)
87 {
88 for (uint32_t i = 0; i < LCD_BUF_SIZE; i++)
89 {
90 lcd_current_working_buffer[i] = color;
91 }
92 }
93
lcd_draw_pixel(uint32_t x,uint32_t y,uint16_t color)94 void lcd_draw_pixel(uint32_t x, uint32_t y, uint16_t color)
95 {
96 // Verify pixel is within LCD range
97 if ((x <= LCD_WIDTH) && (y <= LCD_HEIGHT))
98 {
99 switch (screen_rotation)
100 {
101 case ROTATION_ZERO:
102 {
103 lcd_current_working_buffer[(y * LCD_WIDTH) + x] = color;
104 break;
105 }
106 case ROTATION_180:
107 {
108 lcd_current_working_buffer[((LCD_HEIGHT - y) * LCD_WIDTH) + (LCD_WIDTH - x)] = color;
109 break;
110 }
111 default:
112 {
113 lcd_current_working_buffer[(y * LCD_WIDTH) + x] = color;
114 break;
115 }
116 }
117 }
118 else
119 {
120 LOG_D("draw pixel outof range:%d,%d", x, y);
121 }
122 }
123
lcd_fill_array(uint16_t x_start,uint16_t y_start,uint16_t x_end,uint16_t y_end,void * pcolor)124 void lcd_fill_array(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, void *pcolor)
125 {
126 uint16_t *pixel = RT_NULL;
127 uint16_t cycle_y, x_offset = 0;
128
129 pixel = (uint16_t *)pcolor;
130
131 for (cycle_y = y_start; cycle_y <= y_end;)
132 {
133 for (x_offset = 0; x_start + x_offset <= x_end; x_offset++)
134 {
135 lcd_draw_pixel(x_start + x_offset, cycle_y, *pixel++);
136 }
137 cycle_y++;
138 }
139 }
140
d2_handle_obj_get(void)141 d2_device *d2_handle_obj_get(void)
142 {
143 return *_d2_handle_user;
144 }
145
d2_renderbuffer_get(void)146 d2_renderbuffer *d2_renderbuffer_get(void)
147 {
148 return renderbuffer;
149 }
150
lcd_draw_jpg(int32_t x,int32_t y,const void * p,int32_t xSize,int32_t ySize)151 void lcd_draw_jpg(int32_t x, int32_t y, const void *p, int32_t xSize, int32_t ySize)
152 {
153 uint32_t ModeSrc;
154 ModeSrc = d2_mode_rgb565;
155
156 // Generate render operations
157 d2_framebuffer(d2_handle_obj_get(), (uint16_t *)&fb_background[0], LCD_WIDTH, LCD_WIDTH, LCD_HEIGHT, ModeSrc);
158
159 d2_selectrenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get());
160 d2_cliprect(d2_handle_obj_get(), 0, 0, LCD_WIDTH, LCD_HEIGHT);
161 d2_setblitsrc(d2_handle_obj_get(), (void *) p, xSize, xSize, ySize, ModeSrc);
162 d2_blitcopy(d2_handle_obj_get(), xSize, ySize, 0, 0, (d2_width)(LCD_WIDTH << 4), (d2_width)(LCD_HEIGHT << 4),
163 (d2_point)(x << 4), (d2_point)(y << 4), 0);
164
165 // Execute render operations
166 d2_executerenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get(), 0);
167
168 // In single-buffered mode always wait for DRW to finish before returning
169 d2_flushframe(d2_handle_obj_get());
170 }
171
lcd_gpu_fill_array(size_t x1,size_t y1,size_t x2,size_t y2,uint16_t * color_data)172 void lcd_gpu_fill_array(size_t x1, size_t y1, size_t x2, size_t y2, uint16_t *color_data)
173 {
174 uint32_t ModeSrc;
175 int32_t width;
176 int32_t heigh;
177
178 width = (x2 - x1) + 1;
179 heigh = (y2 - y1) + 1;
180
181 ModeSrc = d2_mode_rgb565;
182
183 // Generate render operations
184 d2_framebuffer(d2_handle_obj_get(), (uint16_t *)&fb_background[0], LCD_WIDTH, LCD_WIDTH, LCD_HEIGHT, ModeSrc);
185
186 d2_selectrenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get());
187 d2_cliprect(d2_handle_obj_get(), 0, 0, LCD_WIDTH, LCD_HEIGHT);
188 d2_setblitsrc(d2_handle_obj_get(), (void *) color_data, width, width, heigh, ModeSrc);
189 d2_blitcopy(d2_handle_obj_get(), width, heigh, 0, 0, (d2_width)(LCD_WIDTH << 4), (d2_width)(LCD_HEIGHT << 4),
190 (d2_point)(x1 << 4), (d2_point)(y1 << 4), 0);
191
192 // Execute render operations
193 d2_executerenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get(), 0);
194 // In single-buffered mode always wait for DRW to finish before returning
195 d2_flushframe(d2_handle_obj_get());
196 }
197
g2d_display_write_area(const void * pSrc,void * pDst,int WidthSrc,int HeightSrc,int x,int y)198 void g2d_display_write_area(const void *pSrc, void *pDst, int WidthSrc, int HeightSrc, int x, int y)
199 {
200 uint32_t ModeSrc;
201 ModeSrc = d2_mode_rgb565;
202
203 /* Set the new buffer to the current draw buffer */
204 d2_framebuffer(d2_handle_obj_get(), (uint16_t *)pDst, LCD_WIDTH, LCD_WIDTH, LCD_HEIGHT, ModeSrc);
205
206 d2_selectrenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get());
207 d2_cliprect(d2_handle_obj_get(), 0, 0, LCD_WIDTH, LCD_HEIGHT);
208 d2_setblitsrc(d2_handle_obj_get(), (void *) pSrc, WidthSrc, WidthSrc, HeightSrc, ModeSrc);
209 d2_blitcopy(d2_handle_obj_get(), WidthSrc, HeightSrc, 0, 0, (d2_width)(WidthSrc << 4), (d2_width)(HeightSrc << 4),
210 (d2_point)(x << 4), (d2_point)(y << 4), 0);
211
212 /* End the current display list */
213 d2_executerenderbuffer(d2_handle_obj_get(), d2_renderbuffer_get(), 0);
214 d2_flushframe(d2_handle_obj_get());
215 }
216
g2d_drv_hwInit(void)217 static int g2d_drv_hwInit(void)
218 {
219 d2_s32 d2_err;
220 uint32_t ModeSrc;
221 ModeSrc = d2_mode_rgb565;
222
223 // Initialize D/AVE 2D driver
224 *_d2_handle_user = d2_opendevice(0);
225 d2_err = d2_inithw(*_d2_handle_user, 0);
226 if (d2_err != D2_OK)
227 {
228 LOG_E("g2d init fail");
229 d2_closedevice(*_d2_handle_user);
230 return -RT_ERROR;
231 }
232
233 // Clear both buffers
234 d2_framebuffer(*_d2_handle_user, (uint16_t *)&fb_background[0], LCD_WIDTH, LCD_WIDTH,
235 LCD_HEIGHT, ModeSrc);
236 d2_clear(*_d2_handle_user, 0x000000);
237
238 // Set various D2 parameters
239 d2_setblendmode(*_d2_handle_user, d2_bm_alpha, d2_bm_one_minus_alpha);
240 d2_setalphamode(*_d2_handle_user, d2_am_constant);
241 d2_setalpha(*_d2_handle_user, UINT8_MAX);
242 d2_setantialiasing(*_d2_handle_user, 1);
243 d2_setlinecap(*_d2_handle_user, d2_lc_butt);
244 d2_setlinejoin(*_d2_handle_user, d2_lj_miter);
245
246 renderbuffer = d2_newrenderbuffer(*_d2_handle_user, 20, 20);
247 if (!renderbuffer)
248 {
249 LOG_E("no renderbuffer");
250 d2_closedevice(*_d2_handle_user);
251 return -RT_ERROR;
252 }
253
254 return RT_EOK;
255 }
256
ra_lcd_control(rt_device_t device,int cmd,void * args)257 static rt_err_t ra_lcd_control(rt_device_t device, int cmd, void *args)
258 {
259 struct drv_lcd_device *lcd = (struct drv_lcd_device *)device;
260
261 switch (cmd)
262 {
263 case RTGRAPHIC_CTRL_RECT_UPDATE:
264 {
265 #ifdef SOC_SERIES_R7FA8M85
266 struct rt_device_rect_info *info = (struct rt_device_rect_info *)args;
267 #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
268 SCB_CleanInvalidateDCache_by_Addr((uint32_t *)lcd->lcd_info.framebuffer, sizeof(fb_background[0]));
269 #endif
270 #if defined(ENABLE_DOUBLE_BUFFER) && ENABLE_DOUBLE_BUFFER
271 /* Swap the active framebuffer */
272 lcd_current_working_buffer = (lcd_current_working_buffer == gp_single_buffer) ? gp_double_buffer : gp_single_buffer;
273 #endif
274
275 g2d_display_write_area((uint8_t *)lcd->lcd_info.framebuffer, lcd_current_working_buffer,
276 info->width, info->height, info->x, info->y);
277 #if defined(ENABLE_DOUBLE_BUFFER) && ENABLE_DOUBLE_BUFFER
278 /* Now that the framebuffer is ready, update the GLCDC buffer pointer on the next Vsync */
279 fsp_err_t err = R_GLCDC_BufferChange(&g_display0_ctrl, (uint8_t *) lcd_current_working_buffer, DISPLAY_FRAME_LAYER_1);
280 RT_ASSERT(err == 0);
281 #endif
282 #endif /* SOC_SERIES_R7FA8M85 */
283 /* wait for vsync interrupt */
284 vsync_wait();
285 }
286 break;
287
288 case RTGRAPHIC_CTRL_POWERON:
289 turn_on_lcd_backlight();
290 break;
291
292 case RTGRAPHIC_CTRL_POWEROFF:
293 rt_pin_write(LCD_BL_PIN, PIN_LOW);
294 break;
295
296 case RTGRAPHIC_CTRL_GET_INFO:
297 {
298 struct rt_device_graphic_info *info = (struct rt_device_graphic_info *)args;
299
300 RT_ASSERT(info != RT_NULL);
301 info->pixel_format = lcd->lcd_info.pixel_format;
302 info->bits_per_pixel = 16;
303 info->width = lcd->lcd_info.width;
304 info->height = lcd->lcd_info.height;
305 info->framebuffer = lcd->lcd_info.framebuffer;
306 }
307 break;
308
309 case RTGRAPHIC_CTRL_SET_MODE:
310 break;
311 }
312
313 return RT_EOK;
314 }
315
drv_lcd_init(struct rt_device * device)316 static rt_err_t drv_lcd_init(struct rt_device *device)
317 {
318 return RT_EOK;
319 }
320
reset_lcd_panel(void)321 static void reset_lcd_panel(void)
322 {
323 #ifdef LCD_RST_PIN
324 rt_pin_mode(LCD_RST_PIN, PIN_MODE_OUTPUT);
325 rt_pin_write(LCD_RST_PIN, PIN_LOW);
326 rt_thread_mdelay(100);
327 rt_pin_write(LCD_RST_PIN, PIN_HIGH);
328 rt_thread_mdelay(100);
329 #endif
330 }
331
ra_bsp_lcd_init(void)332 static rt_err_t ra_bsp_lcd_init(void)
333 {
334 fsp_err_t error;
335
336 /* Set screen rotation to default view */
337 screen_rotation = ROTATION_ZERO;
338
339 /* Display driver open */
340 error = R_GLCDC_Open(&g_display0_ctrl, &g_display0_cfg);
341 if (FSP_SUCCESS == error)
342 {
343 #ifdef SOC_SERIES_R7FA8M85
344 /* config mipi */
345 ra8_mipi_lcd_init();
346 #endif
347 /* Initialize g2d */
348 error = g2d_drv_hwInit();
349
350 /* Display driver start */
351 error = R_GLCDC_Start(&g_display0_ctrl);
352 }
353
354 return error;
355 }
356
rt_hw_lcd_init(void)357 int rt_hw_lcd_init(void)
358 {
359 struct rt_device *device = &_lcd.parent;
360
361 /* memset _lcd to zero */
362 memset(&_lcd, 0x00, sizeof(_lcd));
363
364 /* config LCD dev info */
365 _lcd.lcd_info.height = LCD_HEIGHT;
366 _lcd.lcd_info.width = LCD_WIDTH;
367 _lcd.lcd_info.bits_per_pixel = LCD_BITS_PER_PIXEL;
368 _lcd.lcd_info.pixel_format = LCD_PIXEL_FORMAT;
369 #ifdef SOC_SERIES_R7FA8M85
370 _lcd.lcd_info.framebuffer = (uint8_t *)lcd_framebuffer;
371 #else
372 _lcd.lcd_info.framebuffer = (uint8_t *)&fb_background[0];
373 #endif
374 LOG_D("\nlcd framebuffer address:%#x", _lcd.lcd_info.framebuffer);
375 memset(_lcd.lcd_info.framebuffer, 0x0, LCD_BUF_SIZE);
376
377 device->type = RT_Device_Class_Graphic;
378 #ifdef RT_USING_DEVICE_OPS
379 device->ops = &lcd_ops;
380 #else
381 device->init = drv_lcd_init;
382 device->control = ra_lcd_control;
383 #endif
384
385 /* register lcd device */
386 rt_device_register(device, "lcd", RT_DEVICE_FLAG_RDWR);
387
388 rt_completion_init(&sync_completion);
389
390 /* Initialize buffer pointers */
391 gp_single_buffer = (uint16_t *) g_display0_cfg.input[0].p_base;
392
393 /* Double buffer for drawing color bands with good quality */
394 gp_double_buffer = gp_single_buffer + LCD_BUF_SIZE;
395
396 reset_lcd_panel();
397
398 ra_bsp_lcd_init();
399
400 /* turn on lcd backlight */
401 turn_on_lcd_backlight();
402
403 ra_bsp_lcd_clear(0x0);
404
405 screen_rotation = ROTATION_ZERO;
406
407 return RT_EOK;
408 }
409 INIT_DEVICE_EXPORT(rt_hw_lcd_init);
410
411 #ifdef SOC_SERIES_R7FA8M85
ra8_mipi_lcd_init(void)412 rt_weak void ra8_mipi_lcd_init(void)
413 {
414 LOG_E("please Implementation function %s", __func__);
415 }
416 #endif
417
lcd_test(void)418 int lcd_test(void)
419 {
420 struct drv_lcd_device *lcd;
421 struct rt_device_rect_info rect_info;
422 rect_info.x = 0;
423 rect_info.y = 0;
424 rect_info.width = LCD_WIDTH;
425 rect_info.height = LCD_HEIGHT;
426
427 lcd = (struct drv_lcd_device *)rt_device_find("lcd");
428
429 for (int i = 0; i < 2; i++)
430 {
431 /* red */
432 for (int i = 0; i < LCD_BUF_SIZE / 2; i++)
433 {
434 lcd->lcd_info.framebuffer[2 * i] = 0x00;
435 lcd->lcd_info.framebuffer[2 * i + 1] = 0xF8;
436 }
437 LOG_D("red buffer...");
438 lcd->parent.control(&lcd->parent, RTGRAPHIC_CTRL_RECT_UPDATE, &rect_info);
439 rt_thread_mdelay(1000);
440 /* green */
441 for (int i = 0; i < LCD_BUF_SIZE / 2; i++)
442 {
443 lcd->lcd_info.framebuffer[2 * i] = 0xE0;
444 lcd->lcd_info.framebuffer[2 * i + 1] = 0x07;
445 }
446 LOG_D("green buffer...");
447 lcd->parent.control(&lcd->parent, RTGRAPHIC_CTRL_RECT_UPDATE, &rect_info);
448 rt_thread_mdelay(1000);
449 /* blue */
450 for (int i = 0; i < LCD_BUF_SIZE / 2; i++)
451 {
452 lcd->lcd_info.framebuffer[2 * i] = 0x1F;
453 lcd->lcd_info.framebuffer[2 * i + 1] = 0x00;
454 }
455 LOG_D("blue buffer...");
456 lcd->parent.control(&lcd->parent, RTGRAPHIC_CTRL_RECT_UPDATE, &rect_info);
457 rt_thread_mdelay(1000);
458 }
459 return RT_EOK;
460 }
461 MSH_CMD_EXPORT(lcd_test, lcd test cmd);
462
463 #endif /* BSP_USING_LCD */
464