#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的条件分别是: * 1、检查delete的队列是否满了并且已经有显示过的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; }