1 /*
2  * Copyright (c) 2021-2024 HPMicro
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * Change Logs:
7  * Date         Author      Notes
8  * 2024-02-20   HPMicro     First version
9  */
10 
11 #include <rtthread.h>
12 
13 #ifdef BSP_USING_RTT_LCD_DRIVER
14 #include "board.h"
15 #include "hpm_l1c_drv.h"
16 #include "hpm_lcdc_drv.h"
17 #include "hpm_pdma_drv.h"
18 #include "hpm_panel.h"
19 
20 
21 #define LCD_BITS_PER_PIXEL  16
22 #define LCD_PIXEL_FORMAT    RTGRAPHIC_PIXEL_FORMAT_RGB565
23 #define LCD_LAYER_INDEX     (0)
24 #define LCD_LAYER_DONE_MASK (1U << LCD_LAYER_INDEX)
25 struct hpm_lcd
26 {
27     LCDC_Type *lcd_base;
28     rt_uint8_t lcd_irq;
29     struct rt_semaphore lcd_lock;
30     char *bus_name;
31     struct rt_device parent;
32     struct rt_device_graphic_info lcd_info;
33     uint32_t lcd_buffer_size;
34 };
35 
36 static rt_err_t hpm_lcd_init(struct rt_device *device);
37 static rt_err_t hpm_lcd_control(struct rt_device *device, int cmd, void *args);
38 static int hpm_lcdc_init(struct hpm_lcd *lcd, struct rt_device_graphic_info *info);
39 
40 static uint8_t __attribute__((section(".framebuffer"), aligned(HPM_L1C_CACHELINE_SIZE))) lcdc_framebuffer[PANEL_SIZE_WIDTH * PANEL_SIZE_HEIGHT * LCD_BITS_PER_PIXEL / 8];
41 
42 
43 #ifdef RT_USING_DEVICE_OPS
44 const struct rt_device_ops hpm_lcd_ops = {
45     .init = hpm_lcd_init,
46     .open = RT_NULL,
47     .close = RT_NULL,
48     .read = RT_NULL,
49     .write = RT_NULL,
50     .control = hpm_lcd_control,
51 };
52 #endif
53 
54 static struct hpm_lcd hpm_lcds[] =
55 {
56     {
57         .bus_name = "lcd0",
58         .lcd_buffer_size = (PANEL_SIZE_WIDTH * PANEL_SIZE_HEIGHT * LCD_BITS_PER_PIXEL / 8),
59         .lcd_base = HPM_LCDC,
60         .lcd_irq = BOARD_LCD_IRQ,
61         .parent.type = RT_Device_Class_Graphic,
62 #ifdef RT_USING_DEVICE_OPS
63         .parent.ops = &hpm_lcd_ops,
64 #else
65         .parent.init = hpm_lcd_init,
66         .parent.open = RT_NULL,
67         .parent.close = RT_NULL,
68         .parent.read = RT_NULL,
69         .parent.write = RT_NULL,
70         .parent.control = hpm_lcd_control,
71 #endif
72     },
73 };
74 
isr_lcd_d0(void)75 void isr_lcd_d0(void)
76 {
77     lcdc_disable_interrupt(hpm_lcds[0].lcd_base, LCDC_INT_EN_VSYNC_MASK);
78     rt_sem_release(&hpm_lcds[0].lcd_lock);
79     lcdc_clear_status(hpm_lcds[0].lcd_base, LCDC_ST_VSYNC_MASK);
80 }
SDK_DECLARE_EXT_ISR_M(BOARD_LCD_IRQ,isr_lcd_d0)81 SDK_DECLARE_EXT_ISR_M(BOARD_LCD_IRQ, isr_lcd_d0)
82 
83 static rt_err_t hpm_lcd_init(struct rt_device *device)
84 {
85     /* nothing, right now */
86     (void *)device;
87     return RT_EOK;
88 }
89 
hpm_lcd_control(struct rt_device * device,int cmd,void * args)90 static rt_err_t hpm_lcd_control(struct rt_device *device, int cmd, void *args)
91 {
92     uint32_t aligned_start, aligned_end, aligned_size;
93     struct hpm_lcd *lcd = (struct hpm_lcd *)device->user_data;
94     hpm_panel_t *panel = hpm_panel_find_device_default();
95     struct rt_device_graphic_info *info = RT_NULL;
96     uint32_t buffer;
97 
98     switch (cmd)
99     {
100     case RTGRAPHIC_CTRL_SET_MODE:
101         info = (struct rt_device_graphic_info *)args;
102         rt_sem_trytake(&lcd->lcd_lock);
103         lcdc_disable_interrupt(lcd->lcd_base, LCDC_INT_EN_VSYNC_MASK);
104         rt_thread_delay(10);
105         hpm_lcdc_init(lcd, info);
106         break;
107 
108     case RTGRAPHIC_CTRL_RECT_UPDATE:
109         if (args != RT_NULL)
110         {
111             buffer = (uint32_t)args;
112         }
113         else
114         {
115             buffer = (uint32_t)lcd->lcd_info.framebuffer;
116         }
117         if (l1c_dc_is_enabled())
118         {
119             aligned_start = HPM_L1C_CACHELINE_ALIGN_DOWN(buffer);
120             aligned_end = HPM_L1C_CACHELINE_ALIGN_UP(buffer + lcd->lcd_buffer_size);
121             aligned_size = aligned_end - aligned_start;
122             l1c_dc_writeback(aligned_start, aligned_size);
123         }
124         if (lcdc_layer_control_shadow_loaded(lcd->lcd_base, 0))
125         {
126             lcdc_layer_set_next_buffer(lcd->lcd_base, 0, (rt_uint32_t)buffer);
127         }
128         break;
129 
130     case RTGRAPHIC_CTRL_WAIT_VSYNC:
131         rt_sem_trytake(&lcd->lcd_lock);
132         lcdc_enable_interrupt(lcd->lcd_base, LCDC_INT_EN_VSYNC_MASK);
133         rt_sem_take(&lcd->lcd_lock, RT_WAITING_FOREVER);
134         break;
135 
136     case RTGRAPHIC_CTRL_POWERON:
137         hpm_panel_set_backlight(panel, true);
138         break;
139 
140     case RTGRAPHIC_CTRL_POWEROFF:
141         hpm_panel_set_backlight(panel, false);
142         break;
143 
144     case RTGRAPHIC_CTRL_GET_INFO:
145         info = (struct rt_device_graphic_info *)args;
146 
147         RT_ASSERT(info != RT_NULL);
148         info->pixel_format  = lcd->lcd_info.pixel_format;
149         info->bits_per_pixel = 16;
150         info->width         = lcd->lcd_info.width;
151         info->height        = lcd->lcd_info.height;
152         info->framebuffer   = lcd->lcd_info.framebuffer;
153         break;
154 
155     default:
156         break;
157     }
158 }
159 
hpm_lcdc_init(struct hpm_lcd * lcd,struct rt_device_graphic_info * info)160 static int hpm_lcdc_init(struct hpm_lcd *lcd, struct rt_device_graphic_info *info)
161 {
162     lcdc_config_t config = {0};
163     display_pixel_format_t pixel_format;
164     lcdc_get_default_config(lcd->lcd_base, &config);
165     board_panel_para_to_lcdc(&config);
166 
167     if (info->framebuffer == RT_NULL)
168     {
169         return -RT_ERROR;
170     }
171     rt_memcpy(&lcd->lcd_info, info, sizeof(struct rt_device_graphic_info));
172     lcd->lcd_info.framebuffer = lcdc_framebuffer;
173     if (info->pixel_format == RTGRAPHIC_PIXEL_FORMAT_RGB565)
174     {
175         pixel_format = display_pixel_format_rgb565;
176     }
177     else if (info->pixel_format == RTGRAPHIC_PIXEL_FORMAT_ARGB888)
178     {
179         pixel_format = display_pixel_format_rgb565;
180     }
181     else {
182         return -RT_ERROR;
183     }
184     lcdc_init(lcd->lcd_base, &config);
185     memset(lcd->lcd_info.framebuffer, 0, info->width * info->height * info->bits_per_pixel / 8);
186     lcdc_layer_config_t layer;
187     lcdc_get_default_layer_config(lcd->lcd_base, &layer, pixel_format, LCD_LAYER_INDEX);
188 
189     layer.position_x = 0;
190     layer.position_y = 0;
191     layer.width = info->width;
192     layer.height = info->height;
193     layer.buffer = (rt_uint32_t)lcd->lcd_info.framebuffer;
194     layer.background.u = 0;
195 
196     if (status_success != lcdc_config_layer(lcd->lcd_base, LCD_LAYER_INDEX, &layer, true)) {
197         return -RT_ERROR;
198     }
199 
200     lcdc_turn_on_display(lcd->lcd_base);
201     lcdc_enable_interrupt(lcd->lcd_base, LCDC_INT_EN_VSYNC_MASK);
202     intc_m_enable_irq_with_priority(lcd->lcd_irq, 7);
203     return 0;
204 }
205 
drv_lcd_hw_init(void)206 int drv_lcd_hw_init(void)
207 {
208     rt_err_t result = RT_EOK;
209     struct rt_device_graphic_info lcd_info;
210     for (uint32_t i = 0; i < sizeof(hpm_lcds) / sizeof(hpm_lcds[0]); i++)
211     {
212         struct hpm_lcd *lcd = &hpm_lcds[i];
213         struct rt_device *device = &lcd->parent;
214         lcd->parent.user_data = lcd;
215         /* init lcd_lock semaphore */
216         result = rt_sem_init(&hpm_lcds[0].lcd_lock, "lcd_lock", 0, RT_IPC_FLAG_FIFO);
217         if (result != RT_EOK)
218         {
219             result = -RT_ENOMEM;
220             goto __exit;
221         }
222         /* config LCD dev info */
223 
224         lcd_info.height = PANEL_SIZE_HEIGHT;
225         lcd_info.width = PANEL_SIZE_WIDTH;
226         lcd_info.bits_per_pixel = LCD_BITS_PER_PIXEL;
227         lcd_info.pixel_format = LCD_PIXEL_FORMAT;
228         lcd_info.framebuffer = lcdc_framebuffer;
229         /* register lcd device */
230         rt_device_register(device, hpm_lcds[i].bus_name, RT_DEVICE_FLAG_RDWR);
231         board_init_lcd();
232         if (hpm_lcdc_init(&hpm_lcds[i], &lcd_info) != RT_EOK)
233         {
234             result = -RT_ERROR;
235             goto __exit;
236         }
237 __exit:
238         if (result != RT_EOK)
239         {
240             rt_sem_delete(&hpm_lcds[i].lcd_lock);
241         }
242         return result;
243     }
244 }
245 INIT_BOARD_EXPORT(drv_lcd_hw_init);
246 
247 #endif /* BSP_USING_RTT_LCD_DRIVER */
248