/* * vin_video.c for video api * * Copyright (c) 2017 by Allwinnertech Co., Ltd. http://www.allwinnertech.com * * Authors: Zhao Wei * Yang Feng * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include "../utility/vin_io.h" #include "../vin_csi/sunxi_csi.h" #include "../vin_mipi/sunxi_mipi.h" #include "../vin.h" extern struct vin_core *global_vinc[VIN_MAX_VIDEO]; int vin_set_addr(unsigned int id, unsigned int phy_addr) { struct vin_core *vinc = global_vinc[id]; struct vin_addr paddr; paddr.y = phy_addr; paddr.cb = (unsigned int)(phy_addr + global_video[id].o_width * global_video[id].o_height); paddr.cr = 0; if (global_video[id].fourcc == V4L2_PIX_FMT_LBC_1_0X || global_video[id].fourcc == V4L2_PIX_FMT_LBC_1_5X || global_video[id].fourcc == V4L2_PIX_FMT_LBC_2_0X || global_video[id].fourcc == V4L2_PIX_FMT_LBC_2_5X) { paddr.cb = 0; paddr.cr = 0; } csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, paddr.y); csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr.cb); csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr.cr); return 0; } static void vin_detect_buffer_cfg(struct vin_core *vinc) { int ispid = vinc->isp_sel; struct isp_autoflash_config_s *isp_autoflash_cfg = NULL; unsigned int sign; int ret = -1; #ifdef CONFIG_SUPPORT_THREE_CAMERA if (ispid == 0) { /* isp0 */ isp_autoflash_cfg = (struct isp_autoflash_config_s *)ISP0_NORFLASH_SAVE; sign = 0xAA11AA11; } else if (ispid == 1) { /* isp1/isp2 */ isp_autoflash_cfg = (struct isp_autoflash_config_s *)ISP1_NORFLASH_SAVE; sign = 0xBB11BB11; } else { isp_autoflash_cfg = (struct isp_autoflash_config_s *)ISP2_NORFLASH_SAVE; sign = 0xCC11CC11; } #else if (ispid == 0) { /* isp0 */ isp_autoflash_cfg = (struct isp_autoflash_config_s *)ISP0_NORFLASH_SAVE; sign = 0xAA11AA11; } else { /* isp1/isp2 */ isp_autoflash_cfg = (struct isp_autoflash_config_s *)ISP1_NORFLASH_SAVE; sign = 0xBB11BB11; } #endif if (vinc->get_yuv_en) { isp_autoflash_cfg->melisyuv_sign_id = sign; isp_autoflash_cfg->melisyuv_paddr = (unsigned int)vinc->buff[0].phy_addr; isp_autoflash_cfg->melisyuv_size = vinc->buffer_size; } else { isp_autoflash_cfg->melisyuv_sign_id = 0xFFFFFFFF; } } int buffer_queue(unsigned int id) { unsigned int size; unsigned int i; unsigned int buffer_num; size = global_video[id].o_width * global_video[id].o_height * 3 / 2; #ifdef DMA_USE_IRQ_BUFFER_QUEUE if (global_vinc[id]->get_yuv_en) buffer_num = 2; else buffer_num = 1; for (i = 0; i < buffer_num; i++) { global_vinc[id]->buff[i].phy_addr = rt_memheap_alloc_align(&isp_mempool, size, 0x1000); global_vinc[id]->buffer_size = size; if (global_vinc[id]->buff[i].phy_addr == NULL) { vin_err("%s:video%d:alloc bk buffer%d error\n", __func__, id, i); return -1; } vin_log(VIN_LOG_MD, "video%d: buffer[%d] phy_addr is 0x%x\n", id, i, (unsigned int)global_vinc[id]->buff[i].phy_addr); if (global_vinc[id]->get_yuv_en) vin_print("video%d: buffer[%d] phy_addr is 0x%x\n", id, i, (unsigned int)global_vinc[id]->buff[i].phy_addr); } vin_detect_buffer_cfg(global_vinc[id]); #else global_vinc[id]->buff[0].phy_addr = rt_memheap_alloc_align(&isp_mempool, size, 0x1000); if (global_vinc[id]->buff[0].phy_addr == NULL) { vin_err("%s:video%d:alloc bk buffer error\n", __func__, id); return -1; } #endif return 0; } int buffer_free(unsigned int id) { #ifdef DMA_USE_IRQ_BUFFER_QUEUE if (global_vinc[id]->get_yuv_en) rt_memheap_free_align(global_vinc[id]->buff[1].phy_addr); else rt_memheap_free_align(global_vinc[id]->buff[0].phy_addr); #else rt_memheap_free_align(global_vinc[id]->buff[0].phy_addr); #endif vin_log(VIN_LOG_VIDEO, "buf free!\n"); return 0; } #if 0 /* The color format (colplanes, memplanes) must be already configured. */ int vin_set_addr(struct vin_core *vinc, struct vb2_buffer *vb, struct vin_frame *frame, struct vin_addr *paddr) { u32 pix_size, depth, y_stride, u_stride, v_stride; if (vb == NULL || frame == NULL) return -EINVAL; pix_size = ALIGN(frame->o_width, VIN_ALIGN_WIDTH) * frame->o_height; depth = frame->fmt.depth[0] + frame->fmt.depth[1] + frame->fmt.depth[2]; paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0); if (frame->fmt.memplanes == 1) { switch (frame->fmt.colplanes) { case 1: paddr->cb = 0; paddr->cr = 0; break; case 2: /* decompose Y into Y/Cb */ if (frame->fmt.fourcc == V4L2_PIX_FMT_FBC) { paddr->cb = (u32)(paddr->y + CEIL_EXP(frame->o_width, 7) * CEIL_EXP(frame->o_height, 5) * 96); paddr->cr = 0; } else { paddr->cb = (u32)(paddr->y + pix_size); paddr->cr = 0; } break; case 3: paddr->cb = (u32)(paddr->y + pix_size); /* 420 */ if (12 == frame->fmt.depth[0]) paddr->cr = (u32)(paddr->cb + (pix_size >> 2)); /* 422 */ else paddr->cr = (u32)(paddr->cb + (pix_size >> 1)); break; default: return -EINVAL; } } else if (!frame->fmt.mdataplanes) { if (frame->fmt.memplanes >= 2) paddr->cb = vb2_dma_contig_plane_dma_addr(vb, 1); if (frame->fmt.memplanes == 3) paddr->cr = vb2_dma_contig_plane_dma_addr(vb, 2); } if (vinc->vid_cap.frame.fmt.fourcc == V4L2_PIX_FMT_YVU420) { csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, paddr->y); csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cb); csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cr); } else { csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, paddr->y); csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cb); csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cr); } return 0; } #endif static int lbc_mode_select(struct dma_lbc_cmp *lbc_cmp, unsigned int fourcc) { switch (fourcc) { case V4L2_PIX_FMT_LBC_2_0X: /* 2x */ lbc_cmp->is_lossy = 1; lbc_cmp->bit_depth = 8; lbc_cmp->glb_enable = 1; lbc_cmp->dts_enable = 1; lbc_cmp->ots_enable = 1; lbc_cmp->msq_enable = 1; lbc_cmp->cmp_ratio_even = 600; lbc_cmp->cmp_ratio_odd = 450; lbc_cmp->mb_mi_bits[0] = 55; lbc_cmp->mb_mi_bits[1] = 110; lbc_cmp->rc_adv[0] = 60; lbc_cmp->rc_adv[1] = 30; lbc_cmp->rc_adv[2] = 15; lbc_cmp->rc_adv[3] = 8; lbc_cmp->lmtqp_en = 1; lbc_cmp->lmtqp_min = 1; lbc_cmp->updata_adv_en = 1; lbc_cmp->updata_adv_ratio = 2; break; case V4L2_PIX_FMT_LBC_1_5X: /* 1.5x */ lbc_cmp->is_lossy = 1; lbc_cmp->bit_depth = 8; lbc_cmp->glb_enable = 1; lbc_cmp->dts_enable = 1; lbc_cmp->ots_enable = 1; lbc_cmp->msq_enable = 1; lbc_cmp->cmp_ratio_even = 670; lbc_cmp->cmp_ratio_odd = 658; lbc_cmp->mb_mi_bits[0] = 87; lbc_cmp->mb_mi_bits[1] = 167; lbc_cmp->rc_adv[0] = 60; lbc_cmp->rc_adv[1] = 30; lbc_cmp->rc_adv[2] = 15; lbc_cmp->rc_adv[3] = 8; lbc_cmp->lmtqp_en = 1; lbc_cmp->lmtqp_min = 1; lbc_cmp->updata_adv_en = 1; lbc_cmp->updata_adv_ratio = 2; break; case V4L2_PIX_FMT_LBC_2_5X: /* 2.5x */ lbc_cmp->is_lossy = 1; lbc_cmp->bit_depth = 8; lbc_cmp->glb_enable = 1; lbc_cmp->dts_enable = 1; lbc_cmp->ots_enable = 1; lbc_cmp->msq_enable = 1; lbc_cmp->cmp_ratio_even = 440; lbc_cmp->cmp_ratio_odd = 380; lbc_cmp->mb_mi_bits[0] = 55; lbc_cmp->mb_mi_bits[1] = 94; lbc_cmp->rc_adv[0] = 60; lbc_cmp->rc_adv[1] = 30; lbc_cmp->rc_adv[2] = 15; lbc_cmp->rc_adv[3] = 8; lbc_cmp->lmtqp_en = 1; lbc_cmp->lmtqp_min = 1; lbc_cmp->updata_adv_en = 1; lbc_cmp->updata_adv_ratio = 2; break; case V4L2_PIX_FMT_LBC_1_0X: /* lossless */ lbc_cmp->is_lossy = 0; lbc_cmp->bit_depth = 8; lbc_cmp->glb_enable = 1; lbc_cmp->dts_enable = 1; lbc_cmp->ots_enable = 1; lbc_cmp->msq_enable = 1; lbc_cmp->cmp_ratio_even = 1000; lbc_cmp->cmp_ratio_odd = 1000; lbc_cmp->mb_mi_bits[0] = 55; lbc_cmp->mb_mi_bits[1] = 94; lbc_cmp->rc_adv[0] = 60; lbc_cmp->rc_adv[1] = 30; lbc_cmp->rc_adv[2] = 15; lbc_cmp->rc_adv[3] = 8; lbc_cmp->lmtqp_en = 1; lbc_cmp->lmtqp_min = 1; lbc_cmp->updata_adv_en = 1; lbc_cmp->updata_adv_ratio = 2; break; default: return -1; } return 0; } static int vin_subdev_logic_s_stream(unsigned char virtual_id, int on) { unsigned char logic_id = dma_virtual_find_logic[virtual_id]; struct vin_core *logic_vinc = global_vinc[logic_id]; if (logic_vinc->work_mode == BK_ONLINE && virtual_id != logic_id) { vin_err("video%d work on online mode, video%d cannot to work!!\n", logic_id, virtual_id); return -1; } if (on && (logic_vinc->logic_top_stream_count)++ > 0) return 0; else if (!on && (logic_vinc->logic_top_stream_count == 0 || --(logic_vinc->logic_top_stream_count) > 0)) return 0; if (on) { csic_dma_top_enable(logic_id); csic_dma_mul_ch_enable(logic_id, logic_vinc->work_mode); //if (logic_vinc->id == CSI_VE_ONLINE_VIDEO && logic_vinc->ve_online_cfg.ve_online_en) { // csic_ve_online_hs_enable(logic_id); // logic_vinc->ve_ol_ch = CSI_VE_ONLINE_VIDEO; // csic_ve_online_ch_sel(logic_id, logic_vinc->ve_ol_ch); //} csic_dma_buf_length_software_enable(logic_vinc->vipp_sel, 0); csi_dam_flip_software_enable(logic_vinc->vipp_sel, 0); //csic_dma_top_interrupt_en(logic_id, VIDEO_INPUT_TO_INT | CLR_FS_FRM_CNT_INT | FS_PUL_INT);//for debug hal_enable_irq(logic_vinc->irq); } else { //csic_dma_top_interrupt_disable(logic_id, DMA_TOP_INT_ALL); hal_disable_irq(logic_vinc->irq); csic_ve_online_hs_disable(logic_id); csic_dma_top_disable(logic_id); } vin_log(VIN_LOG_FMT, "dma%d top init by video%d, %s\n", logic_id, virtual_id, on ? "steram on" : "steam off"); return 0; } int vin_subdev_s_stream(unsigned int id, int enable) { struct vin_core *vinc = global_vinc[id]; struct csic_dma_cfg cfg; struct csic_dma_flip flip; struct dma_output_size size; struct dma_buf_len buf_len; struct dma_flip_size flip_size; struct dma_lbc_cmp lbc_cmp; int flag = 0; int flip_mul = 2; int wth; int *stream_count; int sensor_id = vinc->rear_sensor; struct sensor_format_struct *sensor_format; if (global_sensors[sensor_id].sensor_core->sensor_g_format) sensor_format = global_sensors[sensor_id].sensor_core->sensor_g_format(sensor_id, vinc->isp_sel); else return -1; stream_count = &vinc->stream_count; if (enable && (*stream_count)++ > 0) return 0; else if (!enable && (*stream_count == 0 || --(*stream_count) > 0)) return 0; vin_log(VIN_LOG_FMT, "csic_dma%d %s, %d*%d hoff: %d voff: %d\n", vinc->id, enable ? "stream on" : "stream off", global_video[id].o_width, global_video[id].o_height, sensor_format->offs_h, sensor_format->offs_v); if (enable) { memset(&cfg, 0, sizeof(cfg)); memset(&size, 0, sizeof(size)); memset(&buf_len, 0, sizeof(buf_len)); if (global_video[id].fourcc == V4L2_PIX_FMT_LBC_1_0X || global_video[id].fourcc == V4L2_PIX_FMT_LBC_2_0X || global_video[id].fourcc == V4L2_PIX_FMT_LBC_2_5X) { lbc_mode_select(&lbc_cmp, global_video[id].fourcc); wth = roundup(global_video[id].o_width, 32); if (lbc_cmp.is_lossy) { lbc_cmp.line_tar_bits[0] = roundup(lbc_cmp.cmp_ratio_even * wth * lbc_cmp.bit_depth/1000, 512); lbc_cmp.line_tar_bits[1] = roundup(lbc_cmp.cmp_ratio_odd * wth * lbc_cmp.bit_depth/500, 512); } else { lbc_cmp.line_tar_bits[0] = roundup(wth * lbc_cmp.bit_depth * 1 + (wth * 1 / 16 * 2), 512); lbc_cmp.line_tar_bits[1] = roundup(wth * lbc_cmp.bit_depth * 2 + (wth * 2 / 16 * 2), 512); } } switch (sensor_format->field) { case V4L2_FIELD_ANY: case V4L2_FIELD_NONE: cfg.field = FIELD_EITHER; break; case V4L2_FIELD_TOP: cfg.field = FIELD_1; flag = 1; break; case V4L2_FIELD_BOTTOM: cfg.field = FIELD_2; flag = 1; break; case V4L2_FIELD_INTERLACED: cfg.field = FIELD_EITHER; flag = 1; break; default: cfg.field = FIELD_EITHER; break; } switch (global_video[id].fourcc) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV12M: case V4L2_PIX_FMT_FBC: cfg.fmt = flag ? FRAME_UV_CB_YUV420 : FIELD_UV_CB_YUV420; buf_len.buf_len_y = global_video[id].o_width; buf_len.buf_len_c = buf_len.buf_len_y; break; case V4L2_PIX_FMT_LBC_2_0X: case V4L2_PIX_FMT_LBC_2_5X: case V4L2_PIX_FMT_LBC_1_5X: case V4L2_PIX_FMT_LBC_1_0X: cfg.fmt = LBC_MODE_OUTPUT; buf_len.buf_len_y = lbc_cmp.line_tar_bits[1] >> 3; buf_len.buf_len_c = lbc_cmp.line_tar_bits[0] >> 3; break; case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV21M: cfg.fmt = flag ? FRAME_VU_CB_YUV420 : FIELD_VU_CB_YUV420; buf_len.buf_len_y = global_video[id].o_width; buf_len.buf_len_c = buf_len.buf_len_y; break; case V4L2_PIX_FMT_YVU420: case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YUV420M: cfg.fmt = flag ? FRAME_PLANAR_YUV420 : FIELD_PLANAR_YUV420; buf_len.buf_len_y = global_video[id].o_width; buf_len.buf_len_c = buf_len.buf_len_y >> 1; break; case V4L2_PIX_FMT_GREY: cfg.fmt = flag ? FRAME_CB_YUV400 : FIELD_CB_YUV400; buf_len.buf_len_y = global_video[id].o_width; break; case V4L2_PIX_FMT_YUV422P: cfg.fmt = flag ? FRAME_PLANAR_YUV422 : FIELD_PLANAR_YUV422; buf_len.buf_len_y = global_video[id].o_width; buf_len.buf_len_c = buf_len.buf_len_y >> 1; break; case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV61M: cfg.fmt = flag ? FRAME_VU_CB_YUV422 : FIELD_VU_CB_YUV422; buf_len.buf_len_y = global_video[id].o_width; buf_len.buf_len_c = buf_len.buf_len_y; break; case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV16M: cfg.fmt = flag ? FRAME_UV_CB_YUV422 : FIELD_UV_CB_YUV422; buf_len.buf_len_y = global_video[id].o_width; buf_len.buf_len_c = buf_len.buf_len_y; break; case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: flip_mul = 1; cfg.fmt = flag ? FRAME_RAW_8 : FIELD_RAW_8; buf_len.buf_len_y = global_video[id].o_width; buf_len.buf_len_c = buf_len.buf_len_y; break; case V4L2_PIX_FMT_SBGGR10: case V4L2_PIX_FMT_SGBRG10: case V4L2_PIX_FMT_SGRBG10: case V4L2_PIX_FMT_SRGGB10: flip_mul = 1; cfg.fmt = flag ? FRAME_RAW_10 : FIELD_RAW_10; buf_len.buf_len_y = global_video[id].o_width * 2; buf_len.buf_len_c = buf_len.buf_len_y; break; case V4L2_PIX_FMT_SBGGR12: case V4L2_PIX_FMT_SGBRG12: case V4L2_PIX_FMT_SGRBG12: case V4L2_PIX_FMT_SRGGB12: flip_mul = 1; cfg.fmt = flag ? FRAME_RAW_12 : FIELD_RAW_12; buf_len.buf_len_y = global_video[id].o_width * 2; buf_len.buf_len_c = buf_len.buf_len_y; break; default: cfg.fmt = flag ? FRAME_UV_CB_YUV420 : FIELD_UV_CB_YUV420; buf_len.buf_len_y = global_video[id].o_width; buf_len.buf_len_c = buf_len.buf_len_y; break; } if (vinc->isp_dbg.debug_en) { buf_len.buf_len_y = 0; buf_len.buf_len_c = 0; } cfg.ds = vinc->fps_ds; vinc->frame_cnt = 0; if (vin_subdev_logic_s_stream(vinc->id, enable)) return -1; csic_dma_config(vinc->vipp_sel, &cfg); size.hor_len = vinc->isp_dbg.debug_en ? 0 : global_video[id].o_width; size.ver_len = vinc->isp_dbg.debug_en ? 0 : global_video[id].o_height; size.hor_start = vinc->isp_dbg.debug_en ? 0 : sensor_format->offs_h; size.ver_start = vinc->isp_dbg.debug_en ? 0 : sensor_format->offs_v; flip_size.hor_len = vinc->isp_dbg.debug_en ? 0 : global_video[id].o_width * flip_mul; flip_size.ver_len = vinc->isp_dbg.debug_en ? 0 : global_video[id].o_height; flip.hflip_en = vinc->hflip; flip.vflip_en = vinc->vflip; csic_dma_output_size_cfg(vinc->vipp_sel, &size); csic_dma_buffer_length(vinc->vipp_sel, &buf_len); csic_dma_flip_size(vinc->vipp_sel, &flip_size); buffer_queue(id); //vin_set_addr(id, (unsigned int)buff[id].phy_addr); vin_set_addr(id, (unsigned int)vinc->buff[0].phy_addr); #ifdef DMA_USE_IRQ_BUFFER_QUEUE csic_dma_int_clear_status(vinc->vipp_sel, DMA_INT_ALL); csic_dma_int_enable(vinc->vipp_sel, DMA_INT_BUF_0_OVERFLOW | DMA_INT_HBLANK_OVERFLOW | DMA_INT_VSYNC_TRIG | DMA_INT_CAPTURE_DONE | DMA_INT_FRAME_DONE | DMA_INT_LBC_HB); //csic_dma_int_enable(vinc->vipp_sel, DMA_INT_ADDR_NO_READY | DMA_INT_ADDR_OVERFLOW); #endif switch (global_video[id].fourcc) { case V4L2_PIX_FMT_LBC_2_0X: case V4L2_PIX_FMT_LBC_2_5X: case V4L2_PIX_FMT_LBC_1_5X: case V4L2_PIX_FMT_LBC_1_0X: csic_lbc_enable(vinc->vipp_sel); csic_lbc_cmp_ratio(vinc->vipp_sel, &lbc_cmp); break; default: csic_dma_flip_en(vinc->vipp_sel, &flip); csic_dma_enable(vinc->vipp_sel); break; } } else { #ifdef DMA_USE_IRQ_BUFFER_QUEUE csic_dma_int_disable(vinc->vipp_sel, DMA_INT_ALL); csic_dma_int_clear_status(vinc->vipp_sel, DMA_INT_ALL); #endif switch (global_video[id].fourcc) { case V4L2_PIX_FMT_FBC: csic_fbc_disable(vinc->vipp_sel); break; case V4L2_PIX_FMT_LBC_2_0X: case V4L2_PIX_FMT_LBC_2_5X: case V4L2_PIX_FMT_LBC_1_5X: case V4L2_PIX_FMT_LBC_1_0X: csic_lbc_disable(vinc->vipp_sel); break; default: csic_dma_disable(vinc->vipp_sel); break; } vin_subdev_logic_s_stream(vinc->id, enable); //buffer_free(id); } return 0; }