Files
TAIXIN/sdk/app/app_lcd/lcd_msi.c

632 lines
20 KiB
C
Raw Normal View History

#include "basic_include.h"
#include "stream_frame.h"
#include "utlist.h"
#include "lib/lcd/lcd.h"
#include "app_lcd.h"
#include "lib/multimedia/msi.h"
#include "lib/heap/av_heap.h"
#include "lib/heap/av_psram_heap.h"
#include "user_work/user_work.h"
enum LCD_SHOW_MAP
{
LCD_OSD = BIT(0),
LCD_P0 = BIT(1),
LCD_P1 = BIT(2),
LCD_P2 = BIT(3),
};
// data申请空间函数
#define STREAM_MALLOC av_psram_malloc
#define STREAM_FREE av_psram_free
#define STREAM_ZALLOC av_psram_zalloc
// 结构体申请空间函数
#define STREAM_LIBC_MALLOC av_malloc
#define STREAM_LIBC_FREE av_free
#define STREAM_LIBC_ZALLOC av_zalloc
#define LCD_MSI_DEBUG(fmt, ...) // os_printf(fmt, ##__VA_ARGS__)
/* 中断 kick lcd 模式 */
typedef int32 (*lcdc_set_rotate_y_src_addr)(struct lcdc_device *p_lcdc, uint32 yaddr);
typedef int32 (*lcdc_set_rotate_u_src_addr)(struct lcdc_device *p_lcdc, uint32 uaddr);
typedef int32 (*lcdc_set_rotate_v_src_addr)(struct lcdc_device *p_lcdc, uint32 vaddr);
static void lcd_msi_irq_video_show(struct app_lcd_s *lcd_s, uint8_t which_video, uint8_t *p0_p1_enable, uint32_t *p_w, uint32_t *p_h)
{
struct msi *v_msi = NULL;
struct lcd_video_msi_s *video = NULL;
struct framebuff *last_fb = NULL;
uint8_t ret;
uint32_t temp_w, temp_h;
uint16_t *x = NULL;
uint16_t *y = NULL;
lcdc_set_rotate_y_src_addr p_lcdc_set_rotate_y_src_addr = NULL;
lcdc_set_rotate_u_src_addr p_lcdc_set_rotate_u_src_addr = NULL;
lcdc_set_rotate_v_src_addr p_lcdc_set_rotate_v_src_addr = NULL;
/*****************************************************************************************************
* fb的条件分别是:
* 1delete的队列是否满了并且已经有显示过的fb,delete队列满了,fb,,
* 2,,fb去显示
*****************************************************************************************************/
if (which_video == 0)
{
video = (struct lcd_video_msi_s *) lcd_s->video_p0_msi->priv;
v_msi = lcd_s->video_p0_msi;
last_fb = lcd_s->p0_fb;
if (v_msi->enable)
{
x = &lcd_s->x0;
y = &lcd_s->y0;
p_lcdc_set_rotate_y_src_addr = lcdc_set_p0_rotate_y_src_addr;
p_lcdc_set_rotate_u_src_addr = lcdc_set_p0_rotate_u_src_addr;
p_lcdc_set_rotate_v_src_addr = lcdc_set_p0_rotate_v_src_addr;
// 如果上一次没有显示,则尝试从队列获取fb去显示
if (!last_fb)
{
RB_GET(&lcd_s->p0_rb, lcd_s->p0_fb);
last_fb = lcd_s->p0_fb;
}
// 曾经显示过,就要检查一下delete rb是否还有空位,有空位才能切换fb
else if (!RB_FULL(&lcd_s->delete_rb))
{
// 检查是否有需要切换的fb
ret = RB_GET(&lcd_s->p0_rb, lcd_s->p0_fb);
// 更新切换的fb
if (ret)
{
// 把需要删除的last fb放到delete rb的队列
RB_SET(&lcd_s->delete_rb, last_fb);
last_fb = lcd_s->p0_fb;
}
}
// last fb存在和delete rb队列满了,则不需要切换,等待下次切换
else
{
}
}
// 使能被关闭,则需要清除显示的fb,那么要检查delete rb的队列是否为空
else
{
if (last_fb && !RB_FULL(&lcd_s->delete_rb))
{
RB_SET(&lcd_s->delete_rb, last_fb);
last_fb = NULL;
lcd_s->p0_fb = NULL;
}
}
}
else if (which_video == 1)
{
v_msi = lcd_s->video_p1_msi;
video = (struct lcd_video_msi_s *) lcd_s->video_p1_msi->priv;
last_fb = lcd_s->p1_fb;
if (v_msi->enable)
{
x = &lcd_s->x1;
y = &lcd_s->y1;
p_lcdc_set_rotate_y_src_addr = lcdc_set_p1_rotate_y_src_addr;
p_lcdc_set_rotate_u_src_addr = lcdc_set_p1_rotate_u_src_addr;
p_lcdc_set_rotate_v_src_addr = lcdc_set_p1_rotate_v_src_addr;
// 如果上一次没有显示,则尝试从队列获取fb去显示
if (!last_fb)
{
RB_GET(&lcd_s->p1_rb, lcd_s->p1_fb);
last_fb = lcd_s->p1_fb;
}
// 曾经显示过,就要检查一下delete rb是否还有空位,有空位才能切换fb
else if (!RB_FULL(&lcd_s->delete_rb))
{
// 检查是否有需要切换的fb
ret = RB_GET(&lcd_s->p1_rb, lcd_s->p1_fb);
// 更新切换的fb
if (ret)
{
// 把需要删除的last fb放到delete rb的队列
RB_SET(&lcd_s->delete_rb, last_fb);
last_fb = lcd_s->p1_fb;
}
}
// last fb存在和delete rb队列满了,则不需要切换,等待下次切换
else
{
}
}
// 使能被关闭,则需要清除显示的fb,那么要检查delete rb的队列是否为空
else
{
if (last_fb && !RB_FULL(&lcd_s->delete_rb))
{
RB_SET(&lcd_s->delete_rb, last_fb);
last_fb = NULL;
lcd_s->p1_fb = NULL;
}
}
}
else if (which_video == 2)
{
v_msi = lcd_s->csc_video_p2_msi;
video = (struct lcd_video_msi_s *) lcd_s->csc_video_p2_msi->priv;
last_fb = lcd_s->p2_fb;
if (v_msi->enable)
{
x = &lcd_s->x0;
y = &lcd_s->y0;
p_lcdc_set_rotate_y_src_addr = lcdc_set_p0_rotate_y_src_addr;
p_lcdc_set_rotate_u_src_addr = lcdc_set_p0_rotate_u_src_addr;
p_lcdc_set_rotate_v_src_addr = lcdc_set_p0_rotate_v_src_addr;
// 如果上一次没有显示,则尝试从队列获取fb去显示
if (!last_fb)
{
RB_GET(&lcd_s->p2_rb, lcd_s->p2_fb);
last_fb = lcd_s->p2_fb;
}
// 曾经显示过,就要检查一下delete rb是否还有空位,有空位才能切换fb
else if (!RB_FULL(&lcd_s->delete_rb))
{
// 检查是否有需要切换的fb
ret = RB_GET(&lcd_s->p2_rb, lcd_s->p2_fb);
// 更新切换的fb
if (ret)
{
// 把需要删除的last fb放到delete rb的队列
RB_SET(&lcd_s->delete_rb, last_fb);
last_fb = lcd_s->p2_fb;
}
}
// last fb存在和delete rb队列满了,则不需要切换,等待下次切换
else
{
}
}
// 使能被关闭,则需要清除显示的fb,那么要检查delete rb的队列是否为空
else
{
if (last_fb && !RB_FULL(&lcd_s->delete_rb))
{
RB_SET(&lcd_s->delete_rb, last_fb);
last_fb = NULL;
lcd_s->p2_fb = NULL;
}
}
}
// 需要将队列的数据清除
if (!v_msi->enable)
{
return;
}
struct framebuff *fb = last_fb;
// 这里再次判断p_fb,保证非空才打开使能,有可能此时外部选择关闭
if (fb)
{
*p0_p1_enable |= BIT(which_video);
struct yuv_arg_s *yuv_msg = (struct yuv_arg_s *) fb->priv;
uint32_t y_off = 0, uv_off = 0;
uint8_t *p_buf = (uint8_t *) fb->data;
temp_w = yuv_msg->out_w;
temp_h = yuv_msg->out_h;
*p_w = temp_w;
*p_h = temp_h;
*x = yuv_msg->x;
*y = yuv_msg->y;
if (lcd_s->video_rotate == LCD_ROTATE_180)
{
y_off = temp_w * (temp_h - 1);
uv_off = ((temp_w / 2 + 3) / 4) * 4 * (temp_h / 2 - 1);
}
LCD_MSI_DEBUG("p%d yuv_msg->y_size:%d \n", which_video, yuv_msg->y_size);
p_lcdc_set_rotate_y_src_addr(lcd_s->lcd_dev, (uint32) p_buf + y_off);
p_lcdc_set_rotate_u_src_addr(lcd_s->lcd_dev, (uint32) p_buf + yuv_msg->y_size + uv_off);
p_lcdc_set_rotate_v_src_addr(lcd_s->lcd_dev, (uint32) p_buf + yuv_msg->y_size + yuv_msg->y_size / 4 + uv_off);
}
}
static void lcd_msi_irq_osd_show(struct app_lcd_s *lcd_s, uint8_t *osd_en)
{
struct msi *v_msi = lcd_s->lcd_osd_msi;
int32_t ret;
struct framebuff *last_fb = lcd_s->osd_fb;
if (v_msi->enable)
{
// 如果上一次没有显示,则尝试从队列获取fb去显示
if (!last_fb)
{
RB_GET(&lcd_s->osd_rb, lcd_s->osd_fb);
last_fb = lcd_s->osd_fb;
}
// 曾经显示过,就要检查一下delete rb是否还有空位,有空位才能切换fb
else if (!RB_FULL(&lcd_s->delete_rb))
{
// 检查是否有需要切换的fb
ret = RB_GET(&lcd_s->osd_rb, lcd_s->osd_fb);
// 更新切换的fb
if (ret)
{
// 把需要删除的last fb放到delete rb的队列
RB_SET(&lcd_s->delete_rb, last_fb);
last_fb = lcd_s->osd_fb;
}
}
// last fb存在和delete rb队列满了,则不需要切换,等待下次切换
else
{
}
}
else
{
if (last_fb && !RB_FULL(&lcd_s->delete_rb))
{
RB_SET(&lcd_s->delete_rb, last_fb);
last_fb = NULL;
lcd_s->osd_fb = NULL;
}
}
// 这里再次判断osd_fb,保证非空才打开使能,有可能此时外部选择关闭
if (last_fb)
{
*osd_en = 1;
lcdc_set_osd_dma_addr(lcd_s->lcd_dev, (uint32_t) last_fb->data);
}
}
int g_osd_en = 1;
void lcd_msi_irq_callback(void *data)
{
struct app_lcd_s *lcd_s = (struct app_lcd_s *) data;
uint8_t p0_p1_enable = 0;
uint8_t osd_en = 0;
uint32_t p0_w = 0, p0_h = 0, p1_w = 0, p1_h = 0;
// 设置 OSD 的 DMA 地址
lcd_msi_irq_osd_show(lcd_s, &osd_en);
// 设置视频 P0 的地址
lcd_msi_irq_video_show(lcd_s, 0, &p0_p1_enable, &p0_w, &p0_h);
// 设置视频 P1 的地址
lcd_msi_irq_video_show(lcd_s, 1, &p0_p1_enable, &p1_w, &p1_h);
// 设置 CSC P2 的地址
lcd_msi_irq_video_show(lcd_s, 2, &p0_p1_enable, &p0_w, &p0_h);
if (lcd_s->p2_fb && lcd_s->osd_fb)
{
uint32_t osd_fb_time = lcd_s->osd_fb->time;
uint32_t p2_fb_time = lcd_s->p2_fb->time;
int32_t diff = osd_fb_time - p2_fb_time;
struct framebuff *del_fb = NULL;
uint8_t del_flag = 0;
// 因为在中断,delete rb有空位,代表可以删除
if (!RB_FULL(&lcd_s->delete_rb))
{
del_flag = 1;
}
// 需要移除一个fb,这里需要保证delete rb有空位,没有空位,则报一下错误(某些情况应该是异常显示,需要优化)
if (diff > 0)
{
del_fb = lcd_s->p2_fb;
p0_p1_enable &= ~BIT(2);
if (del_flag)
{
lcd_s->p2_fb = NULL;
}
}
else if (diff < 0)
{
del_fb = lcd_s->osd_fb;
osd_en = 0;
if (del_flag)
{
lcd_s->osd_fb = NULL;
}
}
else
{
}
// 删除对应的fb
if (del_flag)
{
RB_SET(&lcd_s->delete_rb, del_fb);
}
}
if (osd_en == 0 && p0_p1_enable == 0)
{
os_printf("(@@@@@@@@@@@)\n");
}
// 打开video
if (p0_p1_enable)
{
// 检查一下line_buf是否已经申请了,申请空间就是w*line_num的空间(w应该是和屏旋转有关)
uint16_t rotate_w;
rotate_w = (lcd_s->video_w+15)/16*16;
if (lcd_s->line_buf)
{
lcdc_set_rotate_linebuf_y_addr(lcd_s->lcd_dev, (uint32) lcd_s->line_buf);
lcdc_set_rotate_linebuf_u_addr(lcd_s->lcd_dev, (uint32) lcd_s->line_buf + rotate_w * lcd_s->line_buf_num);
lcdc_set_rotate_linebuf_v_addr(lcd_s->lcd_dev, (uint32) lcd_s->line_buf + rotate_w * lcd_s->line_buf_num + (rotate_w / 2) * lcd_s->line_buf_num);
LCD_MSI_DEBUG("p0_w:%d p0_h:%d p1_w:%d p1_h:%d\n", p0_w, p0_h, p1_w, p1_h);
lcdc_set_rotate_p0p1_size(lcd_s->lcd_dev, p0_w, p0_h, p1_w, p1_h);
lcdc_set_rotate_mirror(lcd_s->lcd_dev, 0, lcd_s->video_rotate);
if (lcd_s->video_rotate == LCD_ROTATE_180)
{
lcdc_set_video_start_location(lcd_s->lcd_dev, 0, 0);
lcdc_set_rotate_p0p1_start_location(lcd_s->lcd_dev, lcd_s->x0, 0, lcd_s->x1, lcd_s->y1);
}
else
{
lcdc_set_video_start_location(lcd_s->lcd_dev, 0, 0);
lcdc_set_rotate_p0p1_start_location(lcd_s->lcd_dev, lcd_s->x0, lcd_s->y0, lcd_s->x1, lcd_s->y1);
}
lcdc_set_p0p1_enable(lcd_s->lcd_dev, (p0_p1_enable & BIT(0)) ? 1 : (p0_p1_enable & BIT(2)), p0_p1_enable & BIT(1));
lcdc_set_video_en(lcd_s->lcd_dev, 1);
}
else
{
lcdc_set_video_en(lcd_s->lcd_dev, 0);
}
}
else
{
if (lcd_s->free_line_buf && lcd_s->line_buf)
{
LCD_MSI_DEBUG("-----------force free video line buff:0x%x-----------\n", lcd_s->free_line_buf);
STREAM_LIBC_FREE(lcd_s->free_line_buf);
lcd_s->free_line_buf = NULL;
}
if (lcd_s->line_buf)
{
lcd_s->free_line_buf = lcd_s->line_buf;
lcd_s->line_buf = NULL;
}
lcdc_set_video_en(lcd_s->lcd_dev, 0);
}
lcdc_set_osd_en(lcd_s->lcd_dev, osd_en);
lcdc_set_timeout_info(lcd_s->lcd_dev, 1, 3);
lcdc_set_start_run(lcd_s->lcd_dev);
lcd_s->start_time = os_jiffies();
}
// 检查如果被关闭了,则清空队列,这里有个问题,如果使能被关闭后立刻打开,是不会清空队列的,这个要考虑后续如何去处理
void clear_msi_fb(struct msi *v_msi)
{
struct framebuff *fb;
while (v_msi)
{
fb = msi_get_fb(v_msi, 0);
if (fb)
{
os_printf("msi name:%s\tfb:%X\n", v_msi->name, fb);
msi_delete_fb(NULL, fb);
}
else
{
break;
}
}
}
int32 lcd_msi_work(struct os_work *work)
{
struct app_lcd_s *lcd_s = (struct app_lcd_s *) work;
struct lcd_video_msi_s *video_p0 = NULL;
struct lcd_video_msi_s *video_p1 = NULL;
struct lcd_video_msi_s *csc_video_p2 = NULL;
struct lcd_osd_msi_s *osd_show = NULL;
struct framebuff *fb;
uint32_t lcd_show_map = 0;
uint32_t temp = 0;
static int count = 0;
osd_show = (struct lcd_osd_msi_s *) lcd_s->lcd_osd_msi->priv;
video_p0 = (struct lcd_video_msi_s *) lcd_s->video_p0_msi->priv;
video_p1 = (struct lcd_video_msi_s *) lcd_s->video_p1_msi->priv;
csc_video_p2 = (struct lcd_video_msi_s *) lcd_s->csc_video_p2_msi->priv;
// 如果不需要显示video,则空间释放吧
if (lcd_s->free_line_buf)
{
LCD_MSI_DEBUG("-----------free video line buff:0x%x-----------\n", lcd_s->free_line_buf);
irq_disable(LCD_IRQn);
temp = (uint32_t) lcd_s->free_line_buf;
lcd_s->free_line_buf = NULL;
irq_enable(LCD_IRQn);
STREAM_LIBC_FREE((void *) temp);
}
// 检查一下是否有fb需要删除,从delete rb里面查找
while (!RB_EMPTY(&lcd_s->delete_rb))
{
RB_GET(&lcd_s->delete_rb, fb);
msi_delete_fb(NULL, fb);
}
// 检查是否需要清空队列,一个一个清空(通过使能位来判断,如果使能被关闭,则清空对应的队列)
// 这里没有考虑正在显示的情况,正在显示的由中断去放到delete rb的队列
// 将对应rb队列清空(由于ringbuf结构体原因,暂时没有通用函数,先在这里清空)
if (!lcd_s->lcd_osd_msi->enable)
{
clear_msi_fb(lcd_s->lcd_osd_msi);
while (!RB_EMPTY(&lcd_s->osd_rb))
{
// 因为get会在中断使用,所以这里采用安全方式去读取,防止异步问题
if (RB_INT_GET(&lcd_s->osd_rb, fb))
{
msi_delete_fb(NULL, fb);
}
}
}
if (!lcd_s->video_p0_msi->enable)
{
clear_msi_fb(lcd_s->video_p0_msi);
while (!RB_EMPTY(&lcd_s->p0_rb))
{
// 因为get会在中断使用,所以这里采用安全方式去读取,防止异步问题
if (RB_INT_GET(&lcd_s->p0_rb, fb))
{
msi_delete_fb(NULL, fb);
}
}
}
if (!lcd_s->video_p1_msi->enable)
{
clear_msi_fb(lcd_s->video_p1_msi);
while (!RB_EMPTY(&lcd_s->p1_rb))
{
// 因为get会在中断使用,所以这里采用安全方式去读取,防止异步问题
if (RB_INT_GET(&lcd_s->p1_rb, fb))
{
msi_delete_fb(NULL, fb);
}
}
}
if (!lcd_s->csc_video_p2_msi->enable)
{
clear_msi_fb(lcd_s->csc_video_p2_msi);
while (!RB_EMPTY(&lcd_s->p2_rb))
{
// 因为get会在中断使用,所以这里采用安全方式去读取,防止异步问题
if (RB_INT_GET(&lcd_s->p2_rb, fb))
{
msi_delete_fb(NULL, fb);
}
}
}
// 将获取到对应的fb放到队列里面,如果被disable,需要将队列清空
if (!RB_FULL(&lcd_s->osd_rb))
{
fb = msi_get_fb(lcd_s->lcd_osd_msi, 0);
if (fb)
{
RB_SET(&lcd_s->osd_rb, fb);
}
}
if (!RB_FULL(&lcd_s->p0_rb))
{
fb = msi_get_fb(lcd_s->video_p0_msi, 0);
if (fb)
{
RB_SET(&lcd_s->p0_rb, fb);
}
}
if (!RB_FULL(&lcd_s->p1_rb))
{
fb = msi_get_fb(lcd_s->video_p1_msi, 0);
if (fb)
{
RB_SET(&lcd_s->p1_rb, fb);
}
}
if (!RB_FULL(&lcd_s->p2_rb))
{
fb = msi_get_fb(lcd_s->csc_video_p2_msi, 0);
if (fb)
{
RB_SET(&lcd_s->p2_rb, fb);
}
}
// 检查队列是否为空
if (lcd_s->osd_fb || !RB_EMPTY(&lcd_s->osd_rb))
{
lcd_show_map |= LCD_OSD;
}
if (lcd_s->p0_fb || !RB_EMPTY(&lcd_s->p0_rb))
{
lcd_show_map |= LCD_P0;
}
if (lcd_s->p1_fb || !RB_EMPTY(&lcd_s->p1_rb))
{
lcd_show_map |= LCD_P1;
}
if (lcd_s->p2_fb || !RB_EMPTY(&lcd_s->p2_rb))
{
lcd_show_map |= LCD_P2;
}
// 如果p0和p1、p2的队列不为空,则要尝试申请空间
if (lcd_show_map & (LCD_P0 | LCD_P1 | LCD_P2))
{
uint16_t rotate_w;
rotate_w = (lcd_s->video_w+15)/16*16;
// 这里理论要申请到空间,如果申请失败,重新申请一下(否则要就额外处理资源数据)
if (!lcd_s->line_buf)
{
lcd_s->line_buf = (void *) STREAM_LIBC_MALLOC(lcd_s->line_buf_num * rotate_w * 2);
LCD_MSI_DEBUG("-----------alloc video line buff:0x%x-----------\n", lcd_s->line_buf);
}
// 空间申请失败,是尝试等待申请空间还是清空队列?这里就是等待下次申请空间去尝试吧
if (!lcd_s->line_buf)
{
}
}
if (lcd_show_map)
{
lcd_s->hardware_ready = 0; // 标记fb已准备好等待刷新
}
else
{
goto __lcd_msi_work_end;
}
if ((!count && !lcd_s->hardware_ready) || lcd_s->rekick_lcd)
{
count++;
lcd_s->rekick_lcd = 0;
LCD_MSI_DEBUG("=========first kick lcd=========\n");
lcd_s->app_lcd_cb(lcd_s);
}
__lcd_msi_work_end:
os_run_work_delay(&lcd_s->work, 1);
return 0;
}