248 lines
7.2 KiB
C
248 lines
7.2 KiB
C
|
/*
|
||
|
* Copyright (c) 2021-2024 HPMicro
|
||
|
*
|
||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||
|
*
|
||
|
* Change Logs:
|
||
|
* Date Author Notes
|
||
|
* 2024-02-20 HPMicro First version
|
||
|
*/
|
||
|
|
||
|
#include <rtthread.h>
|
||
|
|
||
|
#ifdef BSP_USING_RTT_LCD_DRIVER
|
||
|
#include "board.h"
|
||
|
#include "hpm_l1c_drv.h"
|
||
|
#include "hpm_lcdc_drv.h"
|
||
|
#include "hpm_pdma_drv.h"
|
||
|
#include "hpm_panel.h"
|
||
|
|
||
|
|
||
|
#define LCD_BITS_PER_PIXEL 16
|
||
|
#define LCD_PIXEL_FORMAT RTGRAPHIC_PIXEL_FORMAT_RGB565
|
||
|
#define LCD_LAYER_INDEX (0)
|
||
|
#define LCD_LAYER_DONE_MASK (1U << LCD_LAYER_INDEX)
|
||
|
struct hpm_lcd
|
||
|
{
|
||
|
LCDC_Type *lcd_base;
|
||
|
rt_uint8_t lcd_irq;
|
||
|
struct rt_semaphore lcd_lock;
|
||
|
char *bus_name;
|
||
|
struct rt_device parent;
|
||
|
struct rt_device_graphic_info lcd_info;
|
||
|
uint32_t lcd_buffer_size;
|
||
|
};
|
||
|
|
||
|
static rt_err_t hpm_lcd_init(struct rt_device *device);
|
||
|
static rt_err_t hpm_lcd_control(struct rt_device *device, int cmd, void *args);
|
||
|
static int hpm_lcdc_init(struct hpm_lcd *lcd, struct rt_device_graphic_info *info);
|
||
|
|
||
|
static uint8_t __attribute__((section(".framebuffer"), aligned(HPM_L1C_CACHELINE_SIZE))) lcdc_framebuffer[PANEL_SIZE_WIDTH * PANEL_SIZE_HEIGHT * LCD_BITS_PER_PIXEL / 8];
|
||
|
|
||
|
|
||
|
#ifdef RT_USING_DEVICE_OPS
|
||
|
const struct rt_device_ops hpm_lcd_ops = {
|
||
|
.init = hpm_lcd_init,
|
||
|
.open = RT_NULL,
|
||
|
.close = RT_NULL,
|
||
|
.read = RT_NULL,
|
||
|
.write = RT_NULL,
|
||
|
.control = hpm_lcd_control,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static struct hpm_lcd hpm_lcds[] =
|
||
|
{
|
||
|
{
|
||
|
.bus_name = "lcd0",
|
||
|
.lcd_buffer_size = (PANEL_SIZE_WIDTH * PANEL_SIZE_HEIGHT * LCD_BITS_PER_PIXEL / 8),
|
||
|
.lcd_base = HPM_LCDC,
|
||
|
.lcd_irq = BOARD_LCD_IRQ,
|
||
|
.parent.type = RT_Device_Class_Graphic,
|
||
|
#ifdef RT_USING_DEVICE_OPS
|
||
|
.parent.ops = &hpm_lcd_ops,
|
||
|
#else
|
||
|
.parent.init = hpm_lcd_init,
|
||
|
.parent.open = RT_NULL,
|
||
|
.parent.close = RT_NULL,
|
||
|
.parent.read = RT_NULL,
|
||
|
.parent.write = RT_NULL,
|
||
|
.parent.control = hpm_lcd_control,
|
||
|
#endif
|
||
|
},
|
||
|
};
|
||
|
|
||
|
void isr_lcd_d0(void)
|
||
|
{
|
||
|
lcdc_disable_interrupt(hpm_lcds[0].lcd_base, LCDC_INT_EN_VSYNC_MASK);
|
||
|
rt_sem_release(&hpm_lcds[0].lcd_lock);
|
||
|
lcdc_clear_status(hpm_lcds[0].lcd_base, LCDC_ST_VSYNC_MASK);
|
||
|
}
|
||
|
SDK_DECLARE_EXT_ISR_M(BOARD_LCD_IRQ, isr_lcd_d0)
|
||
|
|
||
|
static rt_err_t hpm_lcd_init(struct rt_device *device)
|
||
|
{
|
||
|
/* nothing, right now */
|
||
|
(void *)device;
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
static rt_err_t hpm_lcd_control(struct rt_device *device, int cmd, void *args)
|
||
|
{
|
||
|
uint32_t aligned_start, aligned_end, aligned_size;
|
||
|
struct hpm_lcd *lcd = (struct hpm_lcd *)device->user_data;
|
||
|
hpm_panel_t *panel = hpm_panel_find_device_default();
|
||
|
struct rt_device_graphic_info *info = RT_NULL;
|
||
|
uint32_t buffer;
|
||
|
|
||
|
switch (cmd)
|
||
|
{
|
||
|
case RTGRAPHIC_CTRL_SET_MODE:
|
||
|
info = (struct rt_device_graphic_info *)args;
|
||
|
rt_sem_trytake(&lcd->lcd_lock);
|
||
|
lcdc_disable_interrupt(lcd->lcd_base, LCDC_INT_EN_VSYNC_MASK);
|
||
|
rt_thread_delay(10);
|
||
|
hpm_lcdc_init(lcd, info);
|
||
|
break;
|
||
|
|
||
|
case RTGRAPHIC_CTRL_RECT_UPDATE:
|
||
|
if (args != RT_NULL)
|
||
|
{
|
||
|
buffer = (uint32_t)args;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
buffer = (uint32_t)lcd->lcd_info.framebuffer;
|
||
|
}
|
||
|
if (l1c_dc_is_enabled())
|
||
|
{
|
||
|
aligned_start = HPM_L1C_CACHELINE_ALIGN_DOWN(buffer);
|
||
|
aligned_end = HPM_L1C_CACHELINE_ALIGN_UP(buffer + lcd->lcd_buffer_size);
|
||
|
aligned_size = aligned_end - aligned_start;
|
||
|
l1c_dc_writeback(aligned_start, aligned_size);
|
||
|
}
|
||
|
if (lcdc_layer_control_shadow_loaded(lcd->lcd_base, 0))
|
||
|
{
|
||
|
lcdc_layer_set_next_buffer(lcd->lcd_base, 0, (rt_uint32_t)buffer);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case RTGRAPHIC_CTRL_WAIT_VSYNC:
|
||
|
rt_sem_trytake(&lcd->lcd_lock);
|
||
|
lcdc_enable_interrupt(lcd->lcd_base, LCDC_INT_EN_VSYNC_MASK);
|
||
|
rt_sem_take(&lcd->lcd_lock, RT_WAITING_FOREVER);
|
||
|
break;
|
||
|
|
||
|
case RTGRAPHIC_CTRL_POWERON:
|
||
|
hpm_panel_set_backlight(panel, true);
|
||
|
break;
|
||
|
|
||
|
case RTGRAPHIC_CTRL_POWEROFF:
|
||
|
hpm_panel_set_backlight(panel, false);
|
||
|
break;
|
||
|
|
||
|
case RTGRAPHIC_CTRL_GET_INFO:
|
||
|
info = (struct rt_device_graphic_info *)args;
|
||
|
|
||
|
RT_ASSERT(info != RT_NULL);
|
||
|
info->pixel_format = lcd->lcd_info.pixel_format;
|
||
|
info->bits_per_pixel = 16;
|
||
|
info->width = lcd->lcd_info.width;
|
||
|
info->height = lcd->lcd_info.height;
|
||
|
info->framebuffer = lcd->lcd_info.framebuffer;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int hpm_lcdc_init(struct hpm_lcd *lcd, struct rt_device_graphic_info *info)
|
||
|
{
|
||
|
lcdc_config_t config = {0};
|
||
|
display_pixel_format_t pixel_format;
|
||
|
lcdc_get_default_config(lcd->lcd_base, &config);
|
||
|
board_panel_para_to_lcdc(&config);
|
||
|
|
||
|
if (info->framebuffer == RT_NULL)
|
||
|
{
|
||
|
return -RT_ERROR;
|
||
|
}
|
||
|
rt_memcpy(&lcd->lcd_info, info, sizeof(struct rt_device_graphic_info));
|
||
|
lcd->lcd_info.framebuffer = lcdc_framebuffer;
|
||
|
if (info->pixel_format == RTGRAPHIC_PIXEL_FORMAT_RGB565)
|
||
|
{
|
||
|
pixel_format = display_pixel_format_rgb565;
|
||
|
}
|
||
|
else if (info->pixel_format == RTGRAPHIC_PIXEL_FORMAT_ARGB888)
|
||
|
{
|
||
|
pixel_format = display_pixel_format_rgb565;
|
||
|
}
|
||
|
else {
|
||
|
return -RT_ERROR;
|
||
|
}
|
||
|
lcdc_init(lcd->lcd_base, &config);
|
||
|
memset(lcd->lcd_info.framebuffer, 0, info->width * info->height * info->bits_per_pixel / 8);
|
||
|
lcdc_layer_config_t layer;
|
||
|
lcdc_get_default_layer_config(lcd->lcd_base, &layer, pixel_format, LCD_LAYER_INDEX);
|
||
|
|
||
|
layer.position_x = 0;
|
||
|
layer.position_y = 0;
|
||
|
layer.width = info->width;
|
||
|
layer.height = info->height;
|
||
|
layer.buffer = (rt_uint32_t)lcd->lcd_info.framebuffer;
|
||
|
layer.background.u = 0;
|
||
|
|
||
|
if (status_success != lcdc_config_layer(lcd->lcd_base, LCD_LAYER_INDEX, &layer, true)) {
|
||
|
return -RT_ERROR;
|
||
|
}
|
||
|
|
||
|
lcdc_turn_on_display(lcd->lcd_base);
|
||
|
lcdc_enable_interrupt(lcd->lcd_base, LCDC_INT_EN_VSYNC_MASK);
|
||
|
intc_m_enable_irq_with_priority(lcd->lcd_irq, 7);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int drv_lcd_hw_init(void)
|
||
|
{
|
||
|
rt_err_t result = RT_EOK;
|
||
|
struct rt_device_graphic_info lcd_info;
|
||
|
for (uint32_t i = 0; i < sizeof(hpm_lcds) / sizeof(hpm_lcds[0]); i++)
|
||
|
{
|
||
|
struct hpm_lcd *lcd = &hpm_lcds[i];
|
||
|
struct rt_device *device = &lcd->parent;
|
||
|
lcd->parent.user_data = lcd;
|
||
|
/* init lcd_lock semaphore */
|
||
|
result = rt_sem_init(&hpm_lcds[0].lcd_lock, "lcd_lock", 0, RT_IPC_FLAG_FIFO);
|
||
|
if (result != RT_EOK)
|
||
|
{
|
||
|
result = -RT_ENOMEM;
|
||
|
goto __exit;
|
||
|
}
|
||
|
/* config LCD dev info */
|
||
|
|
||
|
lcd_info.height = PANEL_SIZE_HEIGHT;
|
||
|
lcd_info.width = PANEL_SIZE_WIDTH;
|
||
|
lcd_info.bits_per_pixel = LCD_BITS_PER_PIXEL;
|
||
|
lcd_info.pixel_format = LCD_PIXEL_FORMAT;
|
||
|
lcd_info.framebuffer = lcdc_framebuffer;
|
||
|
/* register lcd device */
|
||
|
rt_device_register(device, hpm_lcds[i].bus_name, RT_DEVICE_FLAG_RDWR);
|
||
|
board_init_lcd();
|
||
|
if (hpm_lcdc_init(&hpm_lcds[i], &lcd_info) != RT_EOK)
|
||
|
{
|
||
|
result = -RT_ERROR;
|
||
|
goto __exit;
|
||
|
}
|
||
|
__exit:
|
||
|
if (result != RT_EOK)
|
||
|
{
|
||
|
rt_sem_delete(&hpm_lcds[i].lcd_lock);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
INIT_BOARD_EXPORT(drv_lcd_hw_init);
|
||
|
|
||
|
#endif /* BSP_USING_RTT_LCD_DRIVER */
|