/* ************************************************************************* * Rockchip driver for CIF ISP 1.0 * (Based on Intel driver for sofiaxxx) * * Copyright (C) 2015 Intel Mobile Communications GmbH * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. ************************************************************************* */ #include #include #include #include "cif_isp10_regs.h" #include "cif_isp10.h" #include #include #include #include static int cif_isp10_mipi_isr( unsigned int mis, void *cntxt); static int cif_isp10_isp_isr( unsigned int mis, void *cntxt); static void init_output_formats(void); struct v4l2_fmtdesc output_formats[MAX_NB_FORMATS]; /* JPEG quantization tables for JPEG encoding */ /* DC luma table according to ISO/IEC 10918-1 annex K */ static const unsigned char dc_luma_table_annex_k[] = { 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b }; /* DC chroma table according to ISO/IEC 10918-1 annex K */ static const unsigned char dc_chroma_table_annex_k[] = { 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b }; /* AC luma table according to ISO/IEC 10918-1 annex K */ static const unsigned char ac_luma_table_annex_k[] = { 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; /* AC Chroma table according to ISO/IEC 10918-1 annex K */ static const unsigned char ac_chroma_table_annex_k[] = { 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; /* Standard JPEG quantization tables */ /* luma */ /* According to JPEG spec: * [ * 16, 11, 10, 16, 24, 40, 51, 61, * 12, 12, 14, 19, 26, 58, 60, 55, * 14, 13, 16, 24, 40, 57, 69, 56, * 14, 17, 22, 29, 51, 87, 80, 62, * 18, 22, 37, 56, 68, 109, 103, 77, * 24, 35, 55, 64, 81, 104, 113, 92, * 49, 64, 78, 87, 103, 121, 120, 101, * 72, 92, 95, 98, 112, 100, 103, 99 * ] */ /* CIF needs it in zigzag order */ static const unsigned char yq_table_base_zigzag[] = { 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, 101, 103, 99 }; /* chroma */ /* According to JPEG spec: * [ * 17, 18, 24, 47, 99, 99, 99, 99, * 18, 21, 26, 66, 99, 99, 99, 99, * 24, 26, 56, 99, 99, 99, 99, 99, * 47, 66, 99, 99, 99, 99, 99, 99, * 99, 99, 99, 99, 99, 99, 99, 99, * 99, 99, 99, 99, 99, 99, 99, 99, * 99, 99, 99, 99, 99, 99, 99, 99, * 99, 99, 99, 99, 99, 99, 99, 99 * ] */ /* CIF needs it in zigzag order */ static const unsigned char uvq_table_base_zigzag[] = { 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 }; static struct cif_isp10_fmt cif_isp10_output_format[] = { /* ************* YUV422 ************* */ // index 0 { .name = "YUV422-Interleaved", .fourcc = V4L2_PIX_FMT_YUYV, .flags = 0, .depth = 16, .rotation = false, .overlay = false, }, // index 1 { .name = "YUV422-Interleaved", .fourcc = V4L2_PIX_FMT_YUYV, .flags = 0, .depth = 16, .rotation = false, .overlay = false, }, // index 2 { .name = "YVU422-Interleaved", .fourcc = V4L2_PIX_FMT_UYVY, .flags = 0, .depth = 16, .rotation = false, .overlay = false, }, // index 3 { .name = "YUV422-Planar", .fourcc = V4L2_PIX_FMT_YUV422P, .flags = 0, .depth = 16, .rotation = false, .overlay = false, }, // index 4 { .name = "YUV422-Semi-Planar", .fourcc = V4L2_PIX_FMT_NV16, .flags = 0, .depth = 16, .rotation = false, .overlay = false, }, /* ************* YUV420 ************* */ // index 5 { .name = "YUV420-Planar", .fourcc = V4L2_PIX_FMT_YUV420, .flags = 0, .depth = 12, .rotation = false, .overlay = false, }, // index 6 { .name = "YUV420-Planar", .fourcc = V4L2_PIX_FMT_YUV420, .flags = 0, .depth = 12, .rotation = false, .overlay = false, }, // index 7 { .name = "YVU420-Planar", .fourcc = V4L2_PIX_FMT_YVU420, .flags = 0, .depth = 12, .rotation = false, .overlay = false, }, // index 8 { .name = "YUV420-Semi-Planar", .fourcc = V4L2_PIX_FMT_NV12, .flags = 0, .depth = 12, .rotation = false, .overlay = false, }, // index 9 { .name = "YVU420-Semi-Planar", .fourcc = V4L2_PIX_FMT_NV21, .flags = 0, .depth = 12, .rotation = false, .overlay = false, }, /* ************* YUV400 ************* */ // index 10 { .name = "YVU400-Grey-Planar", .fourcc = V4L2_PIX_FMT_GREY, .flags = 0, .depth = 8, .rotation = false, .overlay = false, }, /* ************* YUV444 ************* */ // index 11 { .name = "YVU444-Planar", .fourcc = V4L2_PIX_FMT_YUV444, .flags = 0, .depth = 16, .rotation = false, .overlay = false, }, // index 12 { .name = "YVU444-Semi-Planar", .fourcc = V4L2_PIX_FMT_NV24, .flags = 0, .depth = 16, .rotation = false, .overlay = false, }, /* ************* JPEG ************* */ // index 13 { .name = "JPEG", .fourcc = V4L2_PIX_FMT_JPEG, .flags = 0, .depth = 16, .rotation = false, .overlay = false, }, /* ************ RGB565 *********** */ // index 14 { .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565, .flags = 0, .depth = 16, .rotation = false, .overlay = false, }, /* ************ SGRBG8 *********** */ // index 15 { .name = "SGRBG8", .fourcc = V4L2_PIX_FMT_SGRBG8, .flags = 0, .depth = 8, .rotation = false, .overlay = false, }, }; struct cif_isp10_hw_error { char *name; unsigned int count; unsigned int mask; unsigned int type; /* isp:0 ;mipi:1 */ }; static struct cif_isp10_hw_error cif_isp10_hw_errors[] = { { .name = "isp_data_loss", .count = 0, .mask = CIF_ISP_DATA_LOSS, .type = 0, }, { .name = "isp_pic_size_err", .count = 0, .mask = CIF_ISP_PIC_SIZE_ERROR, .type = 0, }, { .name = "mipi_fifo_err", .count = 0, .mask = CIF_MIPI_SYNC_FIFO_OVFLW(1), .type = 1, }, { .name = "dphy_err_sot", .count = 0, .mask = CIF_MIPI_ERR_SOT(3), .type = 1, }, { .name = "dphy_err_sot_sync", .count = 0, .mask = CIF_MIPI_ERR_SOT_SYNC(3), .type = 1, }, { .name = "dphy_err_eot_sync", .count = 0, .mask = CIF_MIPI_ERR_EOT_SYNC(3), .type = 1, }, { .name = "dphy_err_ctrl", .count = 0, .mask = CIF_MIPI_ERR_CTRL(3), .type = 1, }, { .name = "csi_err_protocol", .count = 0, .mask = CIF_MIPI_ERR_PROTOCOL, .type = 2, }, { .name = "csi_err_ecc1", .count = 0, .mask = CIF_MIPI_ERR_ECC1, .type = 2, }, { .name = "csi_err_ecc2", .count = 0, .mask = CIF_MIPI_ERR_ECC2, .type = 2, }, { .name = "csi_err_cs", .count = 0, .mask = CIF_MIPI_ERR_CS, .type = 2, }, { .name = "fifo_ovf", .count = 0, .mask = (3 << 0), .type = 2, }, { .name = "isp_outform", .count = 0, .mask = CIF_ISP_ERR_OUTFORM_SIZE, .type = 0, }, { .name = "isp_stab", .count = 0, .mask = CIF_ISP_ERR_IS_SIZE, .type = 0, }, { .name = "isp_inform", .count = 0, .mask = CIF_ISP_ERR_INFORM_SIZE, .type = 0, } }; /* Defines */ #define CIF_ISP10_INVALID_BUFF_ADDR ((u32)~0) #define CIF_ISP10_MI_IS_BUSY(dev)\ (dev->config.mi_config.mp.busy ||\ dev->config.mi_config.sp.busy ||\ dev->config.mi_config.dma.busy) enum { CIF_ISP10_ASYNC_JPEG = 0x1, CIF_ISP10_ASYNC_YCFLT = 0x2, CIF_ISP10_ASYNC_ISM = 0x4, CIF_ISP10_ASYNC_DMA = 0x8 }; #define CIF_ISP10_ALWAYS_ASYNC 0x00 #define CIF_ISP10_ALWAYS_STALL_ON_NO_BUFS (false) #ifndef DIV_ROUND_UP #define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y)) #endif #ifndef DIV_TRUNCATE #define DIV_TRUNCATE(x, y) ((x) / (y)) #endif /* Structures and Types */ /* Static Functions */ static const char *cif_isp10_interface_string( enum pltfrm_cam_itf_type itf) { switch (itf) { case PLTFRM_CAM_ITF_MIPI: return "MIPI"; case PLTFRM_CAM_ITF_BT601_8: return "DVP_BT601_8Bit"; case PLTFRM_CAM_ITF_BT656_8: return "DVP_BT656_8Bit"; case PLTFRM_CAM_ITF_BT601_10: return "DVP_BT601_10Bit"; case PLTFRM_CAM_ITF_BT656_10: return "DVP_BT656_10Bit"; case PLTFRM_CAM_ITF_BT601_12: return "DVP_BT601_12Bit"; case PLTFRM_CAM_ITF_BT656_12: return "DVP_BT656_12Bit"; case PLTFRM_CAM_ITF_BT601_16: return "DVP_BT601_16Bit"; case PLTFRM_CAM_ITF_BT656_16: return "DVP_BT656_16Bit"; case PLTFRM_CAM_ITF_BT656_8I: return "DVP_BT656_8Bit_interlace"; default: return "UNKNOWN/UNSUPPORTED"; } } static const char *cif_isp10_img_src_state_string( enum cif_isp10_img_src_state state) { switch (state) { case CIF_ISP10_IMG_SRC_STATE_OFF: return "OFF"; case CIF_ISP10_IMG_SRC_STATE_SW_STNDBY: return "SW_STNDBY"; case CIF_ISP10_IMG_SRC_STATE_STREAMING: return "STREAMING"; default: return "UNKNOWN/UNSUPPORTED"; } } static const char *cif_isp10_state_string( enum cif_isp10_state state) { switch (state) { case CIF_ISP10_STATE_DISABLED: return "DISABLED"; case CIF_ISP10_STATE_INACTIVE: return "INACTIVE"; case CIF_ISP10_STATE_READY: return "READY"; case CIF_ISP10_STATE_STREAMING: return "STREAMING"; default: return "UNKNOWN/UNSUPPORTED"; } } static const char *cif_isp10_pm_state_string( enum cif_isp10_pm_state pm_state) { switch (pm_state) { case CIF_ISP10_PM_STATE_OFF: return "OFF"; case CIF_ISP10_PM_STATE_SW_STNDBY: return "STANDBY"; case CIF_ISP10_PM_STATE_SUSPENDED: return "SUSPENDED"; case CIF_ISP10_PM_STATE_STREAMING: return "STREAMING"; default: return "UNKNOWN/UNSUPPORTED"; } } static const char *cif_isp10_stream_id_string( enum cif_isp10_stream_id stream_id) { switch (stream_id) { case CIF_ISP10_STREAM_SP: return "SP"; case CIF_ISP10_STREAM_MP: return "MP"; case CIF_ISP10_STREAM_DMA: return "DMA"; case CIF_ISP10_STREAM_ISP: return "ISP"; default: return "UNKNOWN/UNSUPPORTED"; } } static const char *cif_isp10_inp_string( enum cif_isp10_inp inp) { switch (inp) { case CIF_ISP10_INP_CSI: return "CSI"; case CIF_ISP10_INP_CPI: return "CPI"; case CIF_ISP10_INP_DMA: return "DMA(ISP)"; case CIF_ISP10_INP_DMA_IE: return "DMA(Image Effects)"; case CIF_ISP10_INP_DMA_SP: return "DMA(SP)"; default: return "UNKNOWN/UNSUPPORTED"; } } enum cif_isp10_inp cif_isp10_input_index2inp( struct cif_isp10_device *dev, unsigned int input) { struct pltfrm_cam_itf itf; if (input >= dev->img_src_cnt) return input - dev->img_src_cnt + CIF_ISP10_INP_DMA; cif_isp10_pltfrm_g_interface_config( dev->img_src_array[input], &itf); if (PLTFRM_CAM_ITF_IS_MIPI(itf.type)) return CIF_ISP10_INP_CSI; if (PLTFRM_CAM_ITF_IS_DVP(itf.type)) return CIF_ISP10_INP_CPI; return -EINVAL; } static const char *cif_isp10_pix_fmt_string(int pixfmt) { switch (pixfmt) { case CIF_YUV400: return "YUV400"; case CIF_YUV420I: return "YUV420I"; case CIF_YUV420SP: return "YUV420SP"; case CIF_YUV420P: return "YUV420P"; case CIF_YVU420I: return "YVU420I"; case CIF_YVU420SP: return "YVU420SP"; case CIF_YVU420P: return "YVU420P"; case CIF_YUV422I: return "YUV422I"; case CIF_YUV422SP: return "YUV422SP"; case CIF_YUV422P: return "YUV422P"; case CIF_YVU422I: return "YVU422I"; case CIF_YVU422SP: return "YVU422SP"; case CIF_YVU422P: return "YVU422P"; case CIF_YUV444I: return "YUV444I"; case CIF_YUV444SP: return "YUV444SP"; case CIF_YUV444P: return "YUV444P"; case CIF_YVU444I: return "YVU444I"; case CIF_YVU444SP: return "YVU444SP"; case CIF_YVU444P: return "YVU444SP"; case CIF_UYV400: return "UYV400"; case CIF_UYV420I: return "UYV420I"; case CIF_UYV420SP: return "UYV420SP"; case CIF_UYV420P: return "UYV420P"; case CIF_VYU420I: return "VYU420I"; case CIF_VYU420SP: return "VYU420SP"; case CIF_VYU420P: return "VYU420P"; case CIF_UYV422I: return "UYV422I"; case CIF_UYV422SP: return "UYV422I"; case CIF_UYV422P: return "UYV422P"; case CIF_VYU422I: return "VYU422I"; case CIF_VYU422SP: return "VYU422SP"; case CIF_VYU422P: return "VYU422P"; case CIF_UYV444I: return "UYV444I"; case CIF_UYV444SP: return "UYV444SP"; case CIF_UYV444P: return "UYV444P"; case CIF_VYU444I: return "VYU444I"; case CIF_VYU444SP: return "VYU444SP"; case CIF_VYU444P: return "VYU444P"; case CIF_RGB565: return "RGB565"; case CIF_RGB666: return "RGB666"; case CIF_RGB888: return "RGB888"; case CIF_BAYER_SBGGR8: return "BAYER BGGR8"; case CIF_BAYER_SGBRG8: return "BAYER GBRG8"; case CIF_BAYER_SGRBG8: return "BAYER GRBG8"; case CIF_BAYER_SRGGB8: return "BAYER RGGB8"; case CIF_BAYER_SBGGR10: return "BAYER BGGR10"; case CIF_BAYER_SGBRG10: return "BAYER GBRG10"; case CIF_BAYER_SGRBG10: return "BAYER GRBG10"; case CIF_BAYER_SRGGB10: return "BAYER RGGB10"; case CIF_BAYER_SBGGR12: return "BAYER BGGR12"; case CIF_BAYER_SGBRG12: return "BAYER GBRG12"; case CIF_BAYER_SGRBG12: return "BAYER GRBG12"; case CIF_BAYER_SRGGB12: return "BAYER RGGB12"; case CIF_DATA: return "DATA"; case CIF_JPEG: return "JPEG"; case CIF_Y10: return "Y10"; case CIF_Y12: return "Y12"; default: return "unknown/unsupported"; } } static void cif_isp10_debug_print_mi_sp(struct cif_isp10_device *dev) { cif_isp10_pltfrm_pr_info(dev->dev, "\n MI_CTRL 0x%08x/0x%08x\n" " MI_STATUS 0x%08x\n" " MI_RIS 0x%08x/0x%08x\n" " MI_IMSC 0x%08x\n" " MI_SP_Y_SIZE %d/%d\n" " MI_SP_CB_SIZE %d/%d\n" " MI_SP_CR_SIZE %d/%d\n" " MI_SP_PIC_WIDTH %d\n" " MI_SP_PIC_HEIGHT %d\n" " MI_SP_PIC_LLENGTH %d\n" " MI_SP_PIC_SIZE %d\n" " MI_SP_Y_BASE_AD 0x%08x/0x%08x\n" " MI_SP_Y_OFFS_CNT %d/%d\n" " MI_SP_Y_OFFS_CNT_START %d\n" " MI_SP_CB_OFFS_CNT %d/%d\n" " MI_SP_CB_OFFS_CNT_START %d\n" " MI_SP_CR_OFFS_CNT %d/%d\n" " MI_SP_CR_OFFS_CNT_START %d\n", cif_ioread32(dev->config.base_addr + CIF_MI_CTRL), cif_ioread32(dev->config.base_addr + CIF_MI_CTRL_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_STATUS), cif_ioread32(dev->config.base_addr + CIF_MI_RIS), cif_ioread32(dev->config.base_addr + CIF_MI_MIS), cif_ioread32(dev->config.base_addr + CIF_MI_IMSC), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_SIZE_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_SIZE_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_SIZE_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_PIC_WIDTH), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_PIC_HEIGHT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_LLENGTH), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_PIC_SIZE), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_OFFS_CNT_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_OFFS_CNT_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_OFFS_CNT_START), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_OFFS_CNT_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_OFFS_CNT_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_OFFS_CNT_START), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_OFFS_CNT_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_OFFS_CNT_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_OFFS_CNT_START)); } static void cif_isp10_debug_print_mi_mp(struct cif_isp10_device *dev) { cif_isp10_pltfrm_pr_info(dev->dev, "\n MI_CTRL 0x%08x/0x%08x\n" " MI_STATUS 0x%08x\n" " MI_BYTE_CNT %d\n" " MI_RIS 0x%08x/0x%08x\n" " MI_IMSC 0x%08x\n" " MI_MP_Y_SIZE %d/%d\n" " MI_MP_CB_SIZE %d/%d\n" " MI_MP_CR_SIZE %d/%d\n" " MI_MP_Y_BASE_AD 0x%08x/0x%08x\n" " MI_MP_Y_OFFS_CNT %d/%d\n" " MI_MP_Y_OFFS_CNT_START %d\n" " MI_MP_CB_OFFS_CNT %d/%d\n" " MI_MP_CB_OFFS_CNT_START %d\n" " MI_MP_CR_OFFS_CNT %d/%d\n" " MI_MP_CR_OFFS_CNT_START %d\n", cif_ioread32(dev->config.base_addr + CIF_MI_CTRL), cif_ioread32(dev->config.base_addr + CIF_MI_CTRL_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_STATUS), cif_ioread32(dev->config.base_addr + CIF_MI_BYTE_CNT), cif_ioread32(dev->config.base_addr + CIF_MI_RIS), cif_ioread32(dev->config.base_addr + CIF_MI_MIS), cif_ioread32(dev->config.base_addr + CIF_MI_IMSC), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_SIZE_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CB_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CB_SIZE_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CR_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CR_SIZE_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_OFFS_CNT_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_OFFS_CNT_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_OFFS_CNT_START), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CB_OFFS_CNT_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CB_OFFS_CNT_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CB_OFFS_CNT_START), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CR_OFFS_CNT_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CR_OFFS_CNT_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CR_OFFS_CNT_START)); } static void cif_isp10_debug_print_srsz(struct cif_isp10_device *dev) { cif_isp10_pltfrm_pr_info(dev->dev, "\n SRSZ_CTRL 0x%08x/0x%08x\n" " SRSZ_SCALE_HY %d/%d\n" " SRSZ_SCALE_HCB %d/%d\n" " SRSZ_SCALE_HCR %d/%d\n" " SRSZ_SCALE_VY %d/%d\n" " SRSZ_SCALE_VC %d/%d\n" " SRSZ_PHASE_HY %d/%d\n" " SRSZ_PHASE_HC %d/%d\n" " SRSZ_PHASE_VY %d/%d\n" " SRSZ_PHASE_VC %d/%d\n", cif_ioread32(dev->config.base_addr + CIF_SRSZ_CTRL), cif_ioread32(dev->config.base_addr + CIF_SRSZ_CTRL_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HY), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HY_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HCB), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HCB_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HCR), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HCR_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_VY), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_VY_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_VC), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_VC_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_HY), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_HY_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_HC), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_HC_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_VY), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_VY_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_VC), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_VC_SHD)); } static void cif_isp10_debug_print_mrsz(struct cif_isp10_device *dev) { cif_isp10_pltfrm_pr_info(dev->dev, "\n MRSZ_CTRL 0x%08x/0x%08x\n" " MRSZ_SCALE_HY %d/%d\n" " MRSZ_SCALE_HCB %d/%d\n" " MRSZ_SCALE_HCR %d/%d\n" " MRSZ_SCALE_VY %d/%d\n" " MRSZ_SCALE_VC %d/%d\n" " MRSZ_PHASE_HY %d/%d\n" " MRSZ_PHASE_HC %d/%d\n" " MRSZ_PHASE_VY %d/%d\n" " MRSZ_PHASE_VC %d/%d\n", cif_ioread32(dev->config.base_addr + CIF_MRSZ_CTRL), cif_ioread32(dev->config.base_addr + CIF_MRSZ_CTRL_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HY), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HY_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HCB), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HCB_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HCR), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HCR_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_VY), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_VY_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_VC), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_VC_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_HY), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_HY_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_HC), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_HC_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_VY), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_VY_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_VC), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_VC_SHD)); } static void cif_isp10_debug_print_block( struct cif_isp10_device *dev, const char *block_name) { if (!strncmp(block_name, "all", 3)) { cif_isp10_debug_print_srsz(dev); cif_isp10_debug_print_mrsz(dev); cif_isp10_debug_print_mi_sp(dev); cif_isp10_debug_print_mi_mp(dev); } else if (!strncmp(block_name, "srsz", 4)) { cif_isp10_debug_print_srsz(dev); } else if (!strncmp(block_name, "mrsz", 4)) { cif_isp10_debug_print_mrsz(dev); } else if (!strncmp(block_name, "mi_sp", 5)) { cif_isp10_debug_print_mi_sp(dev); } else if (!strncmp(block_name, "mi_mp", 5)) { cif_isp10_debug_print_mi_mp(dev); } else { cif_isp10_pltfrm_pr_err(dev->dev, "unknown block %s\n", block_name); } } static u32 cif_isp10_calc_llength( u32 width, u32 stride, enum cif_isp10_pix_fmt pix_fmt) { if (stride == 0) return width; if (CIF_ISP10_PIX_FMT_IS_YUV(pix_fmt)) { u32 num_cplanes = CIF_ISP10_PIX_FMT_YUV_GET_NUM_CPLANES(pix_fmt); if (num_cplanes == 0) return 8 * stride / CIF_ISP10_PIX_FMT_GET_BPP(pix_fmt); else return stride; } else if (CIF_ISP10_PIX_FMT_IS_RGB(pix_fmt)) { return 8 * stride / CIF_ISP10_PIX_FMT_GET_BPP(pix_fmt); } else { return width; } } static int cif_isp10_set_pm_state( struct cif_isp10_device *dev, enum cif_isp10_pm_state pm_state) { cif_isp10_pltfrm_pr_dbg(dev->dev, "%s -> %s\n", cif_isp10_pm_state_string(dev->pm_state), cif_isp10_pm_state_string(pm_state)); if (dev->pm_state == pm_state) return 0; switch (pm_state) { case CIF_ISP10_PM_STATE_OFF: case CIF_ISP10_PM_STATE_SUSPENDED: if ((dev->pm_state == CIF_ISP10_PM_STATE_SW_STNDBY) || (dev->pm_state == CIF_ISP10_PM_STATE_STREAMING)) { pm_runtime_put_sync(dev->dev); } break; case CIF_ISP10_PM_STATE_SW_STNDBY: case CIF_ISP10_PM_STATE_STREAMING: if ((dev->pm_state == CIF_ISP10_PM_STATE_OFF) || (dev->pm_state == CIF_ISP10_PM_STATE_SUSPENDED)) { pm_runtime_get_sync(dev->dev); } break; default: cif_isp10_pltfrm_pr_err(dev->dev, "unknown or unsupported PM state %d\n", pm_state); return -EINVAL; } dev->pm_state = pm_state; return 0; } static int cif_isp10_img_src_set_state( struct cif_isp10_device *dev, enum cif_isp10_img_src_state state) { int ret = 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "%s -> %s\n", cif_isp10_img_src_state_string(dev->img_src_state), cif_isp10_img_src_state_string(state)); if (dev->img_src_state == state) return 0; switch (state) { case CIF_ISP10_IMG_SRC_STATE_OFF: ret = cif_isp10_img_src_s_power(dev->img_src, false); break; case CIF_ISP10_IMG_SRC_STATE_SW_STNDBY: if (dev->img_src_state == CIF_ISP10_IMG_SRC_STATE_STREAMING) { ret = cif_isp10_img_src_s_streaming( dev->img_src, false); } else { ret = cif_isp10_img_src_s_power(dev->img_src, true); } break; case CIF_ISP10_IMG_SRC_STATE_STREAMING: if (dev->config.flash_mode != CIF_ISP10_FLASH_MODE_OFF) cif_isp10_img_src_s_ctrl(dev->img_src, CIF_ISP10_CID_FLASH_MODE, dev->config.flash_mode); ret = cif_isp10_img_src_s_streaming(dev->img_src, true); break; default: break; } if ((dev->config.flash_mode != CIF_ISP10_FLASH_MODE_OFF) && (IS_ERR_VALUE(ret) || (state == CIF_ISP10_IMG_SRC_STATE_OFF))) cif_isp10_img_src_s_ctrl(dev->img_src, CIF_ISP10_CID_FLASH_MODE, CIF_ISP10_FLASH_MODE_OFF); if (!IS_ERR_VALUE(ret)) dev->img_src_state = state; else cif_isp10_pltfrm_pr_err(dev->dev, "failed with err %d\n", ret); return ret; } static int cif_isp10_img_srcs_init( struct cif_isp10_device *dev) { int ret = 0; memset(dev->img_src_array, 0x00, sizeof(dev->img_src_array)); dev->img_src_cnt = cif_isp10_pltfrm_get_img_src_device(dev->dev, dev->img_src_array, CIF_ISP10_NUM_INPUTS); if (dev->img_src_cnt > 0) return 0; dev->img_src_cnt = 0; ret = -EFAULT; cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static int cif_isp10_img_src_select_strm_fmt( struct cif_isp10_device *dev) { int ret = 0; u32 index; struct cif_isp10_strm_fmt_desc strm_fmt_desc; struct cif_isp10_strm_fmt request_strm_fmt; bool matching_format_found = false; bool better_match = false; u32 target_width, target_height; u32 img_src_width, img_src_height; u32 best_diff = ~0; int vblanking; if (IS_ERR_OR_NULL(dev->img_src)) { cif_isp10_pltfrm_pr_err(dev->dev, "no image source selected as input (call s_input first)\n"); ret = -EFAULT; goto err; } ret = cif_isp10_get_target_frm_size(dev, &target_width, &target_height); if (IS_ERR_VALUE(ret)) goto err; /* find the best matching format from the image source */ /* TODO: frame interval and pixel format handling */ for (index = 0;; index++) { if (IS_ERR_VALUE(cif_isp10_img_src_enum_strm_fmts(dev->img_src, index, &strm_fmt_desc))) break; if (!strm_fmt_desc.discrete_frmsize) { if (strm_fmt_desc.min_frmsize.width >= target_width) img_src_width = strm_fmt_desc.min_frmsize.width; else if (strm_fmt_desc.max_frmsize.width >= target_width) img_src_width = target_width; else img_src_width = strm_fmt_desc.max_frmsize.width; if (strm_fmt_desc.min_frmsize.height >= target_height) img_src_height = strm_fmt_desc.min_frmsize.height; else if (strm_fmt_desc.max_frmsize.height >= target_height) img_src_height = target_height; else img_src_height = strm_fmt_desc.max_frmsize.height; } else { img_src_width = strm_fmt_desc.min_frmsize.width; img_src_height = strm_fmt_desc.min_frmsize.height; } if ((img_src_width >= target_width) && (img_src_height >= target_height)) { u32 diff = abs( target_height - (target_width * img_src_height / img_src_width)); if (matching_format_found) { if (dev->config.jpeg_config.enable && ((img_src_width >= request_strm_fmt.frm_fmt.width) && (img_src_height > request_strm_fmt.frm_fmt.height))) /* * for image capturing we try to * maximize the size */ better_match = true; else if (!dev->config.jpeg_config.enable && ((strm_fmt_desc.min_intrvl.denominator / strm_fmt_desc.min_intrvl.numerator) > (request_strm_fmt.frm_intrvl.denominator / request_strm_fmt.frm_intrvl.numerator))) /* maximize fps */ better_match = true; else if (!dev->config.jpeg_config.enable && ((strm_fmt_desc.min_intrvl.denominator / strm_fmt_desc.min_intrvl.numerator) == (request_strm_fmt.frm_intrvl.denominator / request_strm_fmt.frm_intrvl.numerator)) && (diff < best_diff)) /* * chose better aspect ratio * match if fps equal */ better_match = true; else better_match = false; } if (!matching_format_found || better_match) { request_strm_fmt.frm_fmt.width = strm_fmt_desc.min_frmsize.width; request_strm_fmt.frm_fmt.height = strm_fmt_desc.min_frmsize.height; request_strm_fmt.frm_fmt.std_id = strm_fmt_desc.std_id; request_strm_fmt.frm_fmt.pix_fmt = strm_fmt_desc.pix_fmt; request_strm_fmt.frm_intrvl.numerator = strm_fmt_desc.min_intrvl.numerator; request_strm_fmt.frm_intrvl.denominator = strm_fmt_desc.min_intrvl.denominator; request_strm_fmt.frm_fmt.defrect = strm_fmt_desc.defrect; best_diff = diff; matching_format_found = true; } // FIXME::GST set fomat(any@32768x32768) failed, force pass } else if (!matching_format_found) { request_strm_fmt.frm_fmt.width = strm_fmt_desc.min_frmsize.width; request_strm_fmt.frm_fmt.height = strm_fmt_desc.min_frmsize.height; request_strm_fmt.frm_fmt.std_id = strm_fmt_desc.std_id; request_strm_fmt.frm_fmt.pix_fmt = strm_fmt_desc.pix_fmt; request_strm_fmt.frm_intrvl.numerator = strm_fmt_desc.min_intrvl.numerator; request_strm_fmt.frm_intrvl.denominator = strm_fmt_desc.min_intrvl.denominator; request_strm_fmt.frm_fmt.defrect = strm_fmt_desc.defrect; matching_format_found = true; } } if (!matching_format_found) { cif_isp10_pltfrm_pr_err(dev->dev, "no matching image source format (%dx%d) found\n", target_width, target_height); ret = -EINVAL; goto err; } cif_isp10_pltfrm_pr_dbg( dev->dev, "requesting format %s %dx%d(%d,%d,%d,%d)@%d/%dfps from %s\n", cif_isp10_pix_fmt_string(request_strm_fmt.frm_fmt.pix_fmt), request_strm_fmt.frm_fmt.width, request_strm_fmt.frm_fmt.height, request_strm_fmt.frm_fmt.defrect.left, request_strm_fmt.frm_fmt.defrect.top, request_strm_fmt.frm_fmt.defrect.width, request_strm_fmt.frm_fmt.defrect.height, request_strm_fmt.frm_intrvl.denominator, request_strm_fmt.frm_intrvl.numerator, cif_isp10_img_src_g_name(dev->img_src)); ret = cif_isp10_img_src_s_strm_fmt(dev->img_src, &request_strm_fmt); if (IS_ERR_VALUE(ret)) goto err; dev->config.img_src_output = request_strm_fmt; ret = cif_isp10_img_src_g_ctrl(dev->img_src, CIF_ISP10_CID_VBLANKING, &vblanking); if (IS_ERR_VALUE(ret)) { cif_isp10_pltfrm_pr_dbg( dev->dev, "get vblanking failed: %d\n", ret); vblanking = 0; } if (vblanking >= 0) dev->isp_dev.v_blanking_us = vblanking; return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with err %d\n", ret); return ret; } /* * This should only be called when configuring CIF * or at the frame end interrupt */ static void cif_isp10_config_ism(struct cif_isp10_device *dev, bool async) { const struct cif_isp10_ism_config *pconfig = &dev->config.isp_config.ism_config; if (pconfig->ism_en) { cif_isp10_pltfrm_pr_dbg(dev->dev, "%dx%d -> %dx%d@(%d,%d)\n", dev->isp_dev.input_width, dev->isp_dev.input_height, pconfig->ism_params.h_size, pconfig->ism_params.v_size, pconfig->ism_params.h_offs, pconfig->ism_params.v_offs); cif_iowrite32(pconfig->ism_params.recenter, dev->config.base_addr + CIF_ISP_IS_RECENTER); cif_iowrite32(pconfig->ism_params.max_dx, dev->config.base_addr + CIF_ISP_IS_MAX_DX); cif_iowrite32(pconfig->ism_params.max_dy, dev->config.base_addr + CIF_ISP_IS_MAX_DY); cif_iowrite32(pconfig->ism_params.displace, dev->config.base_addr + CIF_ISP_IS_DISPLACE); cif_iowrite32(pconfig->ism_params.h_offs, dev->config.base_addr + CIF_ISP_IS_H_OFFS); cif_iowrite32(pconfig->ism_params.v_offs, dev->config.base_addr + CIF_ISP_IS_V_OFFS); cif_iowrite32(pconfig->ism_params.h_size, dev->config.base_addr + CIF_ISP_IS_H_SIZE); cif_iowrite32(pconfig->ism_params.v_size, dev->config.base_addr + CIF_ISP_IS_V_SIZE); cif_iowrite32OR(1, dev->config.base_addr + CIF_ISP_IS_CTRL); dev->config.isp_config.output.width = dev->config.isp_config.ism_config.ism_params.h_size; dev->config.isp_config.output.height = dev->config.isp_config.ism_config.ism_params.v_size; } else { cif_iowrite32(pconfig->ism_params.recenter, dev->config.base_addr + CIF_ISP_IS_RECENTER); cif_iowrite32(pconfig->ism_params.max_dx, dev->config.base_addr + CIF_ISP_IS_MAX_DX); cif_iowrite32(pconfig->ism_params.max_dy, dev->config.base_addr + CIF_ISP_IS_MAX_DY); cif_iowrite32(pconfig->ism_params.displace, dev->config.base_addr + CIF_ISP_IS_DISPLACE); cif_iowrite32(0, dev->config.base_addr + CIF_ISP_IS_H_OFFS); cif_iowrite32(0, dev->config.base_addr + CIF_ISP_IS_V_OFFS); cif_iowrite32(dev->config.isp_config.output.width, dev->config.base_addr + CIF_ISP_IS_H_SIZE); cif_iowrite32(dev->config.isp_config.output.height, dev->config.base_addr + CIF_ISP_IS_V_SIZE); if (PLTFRM_CAM_ITF_INTERLACE(dev->config.cam_itf.type)) cif_iowrite32(dev->config.isp_config.output.height / 2, dev->config.base_addr + CIF_ISP_IS_V_SIZE); cif_iowrite32(0, dev->config.base_addr + CIF_ISP_IS_CTRL); } if (async) cif_iowrite32OR(CIF_ISP_CTRL_ISP_CFG_UPD, dev->config.base_addr + CIF_ISP_CTRL); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n ISP_IS_H_OFFS %d/%d\n" " ISP_IS_V_OFFS %d/%d\n" " ISP_IS_H_SIZE %d/%d\n" " ISP_IS_V_SIZE %d/%d\n" " ISP_IS_RECENTER 0x%08x\n" " ISP_IS_MAX_DX %d\n" " ISP_IS_MAX_DY %d\n" " ISP_IS_DISPLACE 0x%08x\n" " ISP_IS_CTRL 0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_ISP_IS_H_OFFS), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_H_OFFS_SHD), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_V_OFFS), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_V_OFFS_SHD), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_H_SIZE), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_H_SIZE_SHD), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_V_SIZE), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_V_SIZE_SHD), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_RECENTER), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_MAX_DX), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_MAX_DY), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_DISPLACE), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_CTRL)); } static void cif_isp10_program_jpeg_tables( struct cif_isp10_device *dev) { unsigned int ratio = dev->config.jpeg_config.ratio; unsigned int i = 0; unsigned int q, q_next, scale; cif_isp10_pltfrm_pr_dbg(NULL, "ratio %d\n", ratio); /* Y q-table 0 programming */ cif_iowrite32(CIF_JPE_TAB_ID_QUANT0, dev->config.base_addr + CIF_JPE_TABLE_ID); if (ratio != 50) { scale = (ratio < 50) ? 5000 / ratio : 200 - (ratio << 1); for (i = 0; i < 32; i++) { q = yq_table_base_zigzag[i * 2]; q_next = yq_table_base_zigzag[i * 2 + 1]; q = (scale * q + 50) / 100; q = (q > 1) ? ((q < 255) ? q : 255) : 1; q_next = (scale * q_next + 50) / 100; q_next = (q_next > 1) ? ((q_next < 255) ? q_next : 255) : 1; cif_iowrite32(q_next + (q << 8), dev->config.base_addr + CIF_JPE_TABLE_DATA); } } else { for (i = 0; i < 32; i++) { q = yq_table_base_zigzag[i * 2]; q_next = yq_table_base_zigzag[i * 2 + 1]; cif_iowrite32(q_next + (q << 8), dev->config.base_addr + CIF_JPE_TABLE_DATA); } } /* U/V q-table 0 programming */ cif_iowrite32(CIF_JPE_TAB_ID_QUANT1, dev->config.base_addr + CIF_JPE_TABLE_ID); if (ratio != 50) { for (i = 0; i < 32; i++) { q = uvq_table_base_zigzag[i * 2]; q_next = uvq_table_base_zigzag[i * 2 + 1]; q = (scale * q + 50) / 100; q = (q > 1) ? ((q < 255) ? q : 255) : 1; q_next = (scale * q_next + 50) / 100; q_next = (q_next > 1) ? ((q_next < 255) ? q_next : 255) : 1; cif_iowrite32(q_next + (q << 8), dev->config.base_addr + CIF_JPE_TABLE_DATA); } } else { for (i = 0; i < 32; i++) { q = uvq_table_base_zigzag[i * 2]; q_next = uvq_table_base_zigzag[i * 2 + 1]; cif_iowrite32(q_next + (q << 8), dev->config.base_addr + CIF_JPE_TABLE_DATA); } } /* Y AC-table 0 programming */ cif_iowrite32(CIF_JPE_TAB_ID_HUFFAC0, dev->config.base_addr + CIF_JPE_TABLE_ID); cif_iowrite32(178, dev->config.base_addr + CIF_JPE_TAC0_LEN); for (i = 0; i < (178 / 2); i++) { cif_iowrite32(ac_luma_table_annex_k[i * 2 + 1] + (ac_luma_table_annex_k[i * 2] << 8), dev->config.base_addr + CIF_JPE_TABLE_DATA); } /* U/V AC-table 1 programming */ cif_iowrite32(CIF_JPE_TAB_ID_HUFFAC1, dev->config.base_addr + CIF_JPE_TABLE_ID); cif_iowrite32(178, dev->config.base_addr + CIF_JPE_TAC1_LEN); for (i = 0; i < (178 / 2); i++) { cif_iowrite32(ac_chroma_table_annex_k[i * 2 + 1] + (ac_chroma_table_annex_k[i * 2] << 8), dev->config.base_addr + CIF_JPE_TABLE_DATA); } /* Y DC-table 0 programming */ cif_iowrite32(CIF_JPE_TAB_ID_HUFFDC0, dev->config.base_addr + CIF_JPE_TABLE_ID); cif_iowrite32(28, dev->config.base_addr + CIF_JPE_TDC0_LEN); for (i = 0; i < (28 / 2); i++) { cif_iowrite32(dc_luma_table_annex_k[i * 2 + 1] + (dc_luma_table_annex_k[i * 2] << 8), dev->config.base_addr + CIF_JPE_TABLE_DATA); } /* U/V DC-table 1 programming */ cif_iowrite32(CIF_JPE_TAB_ID_HUFFDC1, dev->config.base_addr + CIF_JPE_TABLE_ID); cif_iowrite32(28, dev->config.base_addr + CIF_JPE_TDC1_LEN); for (i = 0; i < (28 / 2); i++) { cif_iowrite32(dc_chroma_table_annex_k[i * 2 + 1] + (dc_chroma_table_annex_k[i * 2] << 8), dev->config.base_addr + CIF_JPE_TABLE_DATA); } } static void cif_isp10_select_jpeg_tables( struct cif_isp10_device *dev) { cif_isp10_pltfrm_pr_dbg(NULL, "\n"); /* Selects quantization table for Y */ cif_iowrite32(CIF_JPE_TQ_TAB0, dev->config.base_addr + CIF_JPE_TQ_Y_SELECT); /* Selects quantization table for U */ cif_iowrite32(CIF_JPE_TQ_TAB1, dev->config.base_addr + CIF_JPE_TQ_U_SELECT); /* Selects quantization table for V */ cif_iowrite32(CIF_JPE_TQ_TAB1, dev->config.base_addr + CIF_JPE_TQ_V_SELECT); /* Selects Huffman DC table */ cif_iowrite32(CIF_DC_V_TABLE | CIF_DC_U_TABLE, dev->config.base_addr + CIF_JPE_DC_TABLE_SELECT); /* Selects Huffman AC table */ cif_iowrite32(CIF_AC_V_TABLE | CIF_AC_U_TABLE, dev->config.base_addr + CIF_JPE_AC_TABLE_SELECT); cif_isp10_pltfrm_pr_dbg(NULL, "\n JPE_TQ_Y_SELECT 0x%08x\n" " JPE_TQ_U_SELECT 0x%08x\n" " JPE_TQ_V_SELECT 0x%08x\n" " JPE_DC_TABLE_SELECT 0x%08x\n" " JPE_AC_TABLE_SELECT 0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_JPE_TQ_Y_SELECT), cif_ioread32(dev->config.base_addr + CIF_JPE_TQ_U_SELECT), cif_ioread32(dev->config.base_addr + CIF_JPE_TQ_V_SELECT), cif_ioread32(dev->config.base_addr + CIF_JPE_DC_TABLE_SELECT), cif_ioread32(dev->config.base_addr + CIF_JPE_AC_TABLE_SELECT)); } static int cif_isp10_config_img_src_exps( struct cif_isp10_device *dev) { int ret = 0; struct isp_supplemental_sensor_mode_data sensor_mode; ret = (int)cif_isp10_img_src_ioctl(dev->img_src, RK_VIDIOC_SENSOR_MODE_DATA, &sensor_mode); if (IS_ERR_VALUE(ret)) { dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] = 4; dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX] = 4; } else { if ((sensor_mode.exposure_valid_frame[VALID_FR_EXP_T_INDEX] < 2) || (sensor_mode.exposure_valid_frame[VALID_FR_EXP_T_INDEX] > 6)) dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] = 4; else dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] = sensor_mode.exposure_valid_frame[VALID_FR_EXP_T_INDEX]; if ((sensor_mode.exposure_valid_frame[VALID_FR_EXP_G_INDEX] < 2) || (sensor_mode.exposure_valid_frame[VALID_FR_EXP_G_INDEX] > 6)) dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX] = 2; else dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX] = sensor_mode.exposure_valid_frame[VALID_FR_EXP_G_INDEX]; } return ret; } static int cif_isp10_config_img_src( struct cif_isp10_device *dev) { int ret = 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "\n"); ret = cif_isp10_img_src_set_state(dev, CIF_ISP10_IMG_SRC_STATE_SW_STNDBY); if (IS_ERR_VALUE(ret)) goto err; if (!dev->sp_stream.updt_cfg && !dev->mp_stream.updt_cfg) return 0; ret = cif_isp10_pltfrm_g_interface_config(dev->img_src, &dev->config.cam_itf); if (IS_ERR_VALUE(ret)) goto err; if (!dev->img_src_exps.inited) { cif_isp10_config_img_src_exps(dev); dev->img_src_exps.inited = true; } cif_isp10_pltfrm_pr_dbg(dev->dev, "cam_itf: (type: 0x%x, dphy: %d, vc: %d, nb_lanes: %d, bitrate: %d)", dev->config.cam_itf.type, dev->config.cam_itf.cfg.mipi.dphy_index, dev->config.cam_itf.cfg.mipi.vc, dev->config.cam_itf.cfg.mipi.nb_lanes, dev->config.cam_itf.cfg.mipi.bit_rate); return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static int cif_isp10_config_isp( struct cif_isp10_device *dev) { int ret = 0; u32 h_offs; u32 v_offs; u32 yuv_seq = 0; u32 bpp; u32 isp_input_sel = 0; u32 isp_bayer_pat = 0; u32 acq_mult = 1; u32 irq_mask = 0; u32 signal = 0; enum cif_isp10_pix_fmt in_pix_fmt; struct cif_isp10_frm_fmt *output; struct pltfrm_cam_itf *cam_itf; if (dev->config.input_sel == CIF_ISP10_INP_DMA_IE) { dev->config.isp_config.output = dev->config.mi_config.dma.output; cifisp_disable_isp(&dev->isp_dev); return 0; } else if (dev->config.input_sel == CIF_ISP10_INP_DMA_SP) { cif_iowrite32AND(~CIF_ICCL_ISP_CLK, dev->config.base_addr + CIF_ICCL); cif_isp10_pltfrm_pr_dbg(NULL, "ISP disabled\n"); return 0; } cif_iowrite32OR(CIF_ICCL_ISP_CLK, dev->config.base_addr + CIF_ICCL); in_pix_fmt = dev->config.isp_config.input->pix_fmt; output = &dev->config.isp_config.output; cam_itf = &dev->config.cam_itf; if (CIF_ISP10_PIX_FMT_IS_RAW_BAYER(in_pix_fmt) || CIF_ISP10_PIX_FMT_IS_Y_ONLY(in_pix_fmt)) { if (!dev->config.mi_config.raw_enable) { output->pix_fmt = CIF_YUV422I; if ((dev->mp_stream.state == CIF_ISP10_STATE_READY) && (dev->sp_stream.state == CIF_ISP10_STATE_READY)) { if (dev->config.mi_config.mp.output. quantization != dev->config.mi_config.sp.output. quantization) { cif_isp10_pltfrm_pr_err(dev->dev, "colorspace quantization (mp: %d, sp: %d) is not support!\n", dev-> config.mi_config.mp.output. quantization, dev-> config.mi_config.sp.output. quantization); } } if (dev->sp_stream.state == CIF_ISP10_STATE_READY) { output->quantization = dev->config.mi_config.sp.output.quantization; } if (dev->mp_stream.state == CIF_ISP10_STATE_READY) { output->quantization = dev->config.mi_config.mp.output.quantization; } if (CIF_ISP10_PIX_FMT_IS_Y_ONLY(in_pix_fmt)) { cif_iowrite32(0x40c, dev->config.base_addr + CIF_ISP_DEMOSAIC); } else { cif_iowrite32(0xc, dev->config.base_addr + CIF_ISP_DEMOSAIC); } if (PLTFRM_CAM_ITF_IS_BT656(dev->config.cam_itf.type)) { cif_iowrite32( CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656, dev->config.base_addr + CIF_ISP_CTRL); } else { cif_iowrite32( CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601, dev->config.base_addr + CIF_ISP_CTRL); } } else { output->pix_fmt = in_pix_fmt; if (PLTFRM_CAM_ITF_IS_BT656(dev->config.cam_itf.type)) { cif_iowrite32( CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656, dev->config.base_addr + CIF_ISP_CTRL); } else { cif_iowrite32(CIF_ISP_CTRL_ISP_MODE_RAW_PICT, dev->config.base_addr + CIF_ISP_CTRL); } } bpp = CIF_ISP10_PIX_FMT_GET_BPP(in_pix_fmt); if (bpp == 8) { isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_8B_MSB; } else if (bpp == 10) { isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_10B_MSB; } else if (bpp == 12) { isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_12B; } else { cif_isp10_pltfrm_pr_err(dev->dev, "%d bits per pixel not supported\n", bpp); ret = -EINVAL; goto err; } if (CIF_ISP10_PIX_FMT_BAYER_PAT_IS_BGGR(in_pix_fmt)) { isp_bayer_pat = CIF_ISP_ACQ_PROP_BAYER_PAT_BGGR; } else if (CIF_ISP10_PIX_FMT_BAYER_PAT_IS_GBRG(in_pix_fmt)) { isp_bayer_pat = CIF_ISP_ACQ_PROP_BAYER_PAT_GBRG; } else if (CIF_ISP10_PIX_FMT_BAYER_PAT_IS_GRBG(in_pix_fmt)) { isp_bayer_pat = CIF_ISP_ACQ_PROP_BAYER_PAT_GRBG; } else if (CIF_ISP10_PIX_FMT_BAYER_PAT_IS_RGGB(in_pix_fmt)) { isp_bayer_pat = CIF_ISP_ACQ_PROP_BAYER_PAT_RGGB; } else { cif_isp10_pltfrm_pr_err(dev->dev, "BAYER pattern not supported\n"); ret = -EINVAL; goto err; } } else if (CIF_ISP10_PIX_FMT_IS_YUV(in_pix_fmt)) { output->pix_fmt = in_pix_fmt; acq_mult = 2; if (dev->config.input_sel == CIF_ISP10_INP_DMA) { bpp = CIF_ISP10_PIX_FMT_GET_BPP(in_pix_fmt); bpp = bpp * 4 / (4 + (CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS( in_pix_fmt) * CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS( in_pix_fmt) / 2)); if (bpp == 8) { isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_8B_MSB; } else if (bpp == 10) { isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_10B_MSB; } else if (bpp == 12) { isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_12B; } else { cif_isp10_pltfrm_pr_err(dev->dev, "format %s not supported, invalid bpp %d\n", cif_isp10_pix_fmt_string(in_pix_fmt), bpp); ret = -EINVAL; goto err; } cif_iowrite32(CIF_ISP_CTRL_ISP_MODE_ITU601, dev->config.base_addr + CIF_ISP_CTRL); } else{ if (PLTFRM_CAM_ITF_IS_MIPI( dev->config.cam_itf.type)) { isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_12B; cif_iowrite32(CIF_ISP_CTRL_ISP_MODE_ITU601, dev->config.base_addr + CIF_ISP_CTRL); } else if (PLTFRM_CAM_ITF_IS_DVP( dev->config.cam_itf.type)) { if (PLTFRM_CAM_ITF_IS_BT656( dev->config.cam_itf.type)) { cif_iowrite32( CIF_ISP_CTRL_ISP_MODE_ITU656, dev->config.base_addr + CIF_ISP_CTRL); } else { cif_iowrite32( CIF_ISP_CTRL_ISP_MODE_ITU601, dev->config.base_addr + CIF_ISP_CTRL); } switch (PLTFRM_CAM_ITF_DVP_BW( dev->config.cam_itf.type)) { case 8: isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO; break; case 10: isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO; break; case 12: isp_input_sel = CIF_ISP_ACQ_PROP_IN_SEL_12B; break; default: cif_isp10_pltfrm_pr_err(dev->dev, "%s isn't support for cif isp10\n", cif_isp10_interface_string( dev->config.cam_itf.type)); break; } } else { cif_isp10_pltfrm_pr_err(dev->dev, "%s isn't support for cif isp10\n", cif_isp10_interface_string( dev->config.cam_itf.type)); } /* * ISP DATA LOSS is only meaningful * when input is not DMA */ irq_mask |= CIF_ISP_DATA_LOSS; } if (CIF_ISP10_PIX_FMT_YUV_IS_YC_SWAPPED(in_pix_fmt)) { yuv_seq = CIF_ISP_ACQ_PROP_CBYCRY; cif_isp10_pix_fmt_set_yc_swapped(output->pix_fmt, 0); } else if (CIF_ISP10_PIX_FMT_YUV_IS_UV_SWAPPED(in_pix_fmt)) { yuv_seq = CIF_ISP_ACQ_PROP_YCRYCB; } else { yuv_seq = CIF_ISP_ACQ_PROP_YCBYCR; } } else { cif_isp10_pltfrm_pr_err(dev->dev, "format %s not supported\n", cif_isp10_pix_fmt_string(in_pix_fmt)); ret = -EINVAL; goto err; } /* Set up input acquisition properties*/ if (PLTFRM_CAM_ITF_IS_DVP(cam_itf->type)) { signal = ((cam_itf->cfg.dvp.pclk == PLTFRM_CAM_SDR_POS_EDG) ? CIF_ISP_ACQ_PROP_POS_EDGE : CIF_ISP_ACQ_PROP_NEG_EDGE); if (PLTFRM_CAM_ITF_IS_BT601(cam_itf->type)) { signal |= (cam_itf->cfg.dvp.vsync == PLTFRM_CAM_SIGNAL_HIGH_LEVEL) ? CIF_ISP_ACQ_PROP_VSYNC_HIGH : CIF_ISP_ACQ_PROP_VSYNC_LOW; signal |= (cam_itf->cfg.dvp.hsync == PLTFRM_CAM_SIGNAL_HIGH_LEVEL) ? CIF_ISP_ACQ_PROP_HSYNC_HIGH : CIF_ISP_ACQ_PROP_HSYNC_LOW; } else { signal |= CIF_ISP_ACQ_PROP_HSYNC_HIGH | CIF_ISP_ACQ_PROP_VSYNC_HIGH; } } else { signal = CIF_ISP_ACQ_PROP_NEG_EDGE | CIF_ISP_ACQ_PROP_HSYNC_HIGH | CIF_ISP_ACQ_PROP_VSYNC_HIGH; } cif_iowrite32(signal | yuv_seq | CIF_ISP_ACQ_PROP_FIELD_SEL_ALL | isp_input_sel | isp_bayer_pat | (0 << 20), /* input_selection_no_app */ dev->config.base_addr + CIF_ISP_ACQ_PROP); cif_iowrite32(0, dev->config.base_addr + CIF_ISP_ACQ_NR_FRAMES); /* Acquisition Size */ cif_iowrite32(dev->config.isp_config.input->defrect.left, dev->config.base_addr + CIF_ISP_ACQ_H_OFFS); cif_iowrite32(dev->config.isp_config.input->defrect.top, dev->config.base_addr + CIF_ISP_ACQ_V_OFFS); cif_iowrite32( acq_mult * dev->config.isp_config.input->defrect.width, dev->config.base_addr + CIF_ISP_ACQ_H_SIZE); cif_iowrite32( dev->config.isp_config.input->defrect.height, dev->config.base_addr + CIF_ISP_ACQ_V_SIZE); /* do cropping to match output aspect ratio */ ret = cif_isp10_calc_isp_cropping(dev, &output->width, &output->height, &h_offs, &v_offs); if (IS_ERR_VALUE(ret)) goto err; cif_iowrite32(v_offs, dev->config.base_addr + CIF_ISP_OUT_V_OFFS); cif_iowrite32(h_offs, dev->config.base_addr + CIF_ISP_OUT_H_OFFS); cif_iowrite32(output->width, dev->config.base_addr + CIF_ISP_OUT_H_SIZE); cif_iowrite32(output->height, dev->config.base_addr + CIF_ISP_OUT_V_SIZE); if (PLTFRM_CAM_ITF_INTERLACE(cam_itf->type)) { cif_isp10_pltfrm_pr_info(dev->dev, "type %s: input.size %dx%d, output.size %dx%d\n", cif_isp10_interface_string(cam_itf->type), dev->config.isp_config.input->defrect.width, dev->config.isp_config.input->defrect.height, output->width, output->height); cif_iowrite32( dev->config.isp_config.input->defrect.height / 2, dev->config.base_addr + CIF_ISP_ACQ_V_SIZE); cif_iowrite32( output->height / 2, dev->config.base_addr + CIF_ISP_OUT_V_SIZE); } dev->isp_dev.input_width = dev->config.isp_config.input->defrect.width; dev->isp_dev.input_height = dev->config.isp_config.input->defrect.height; /* interrupt mask */ irq_mask |= CIF_ISP_FRAME | CIF_ISP_PIC_SIZE_ERROR | CIF_ISP_FRAME_IN | CIF_ISP_V_START | CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN | CIF_ISP_EXP_END | CIF_ISP_HIST_MEASURE_RDY; cif_iowrite32(irq_mask, dev->config.base_addr + CIF_ISP_IMSC); if (!dev->config.mi_config.raw_enable) cifisp_configure_isp(&dev->isp_dev, in_pix_fmt, output->quantization); else cifisp_disable_isp(&dev->isp_dev); cif_isp10_pltfrm_pr_dbg( dev->dev, "\n ISP_CTRL 0x%08x\n" " ISP_IMSC 0x%08x\n" " ISP_ACQ_PROP 0x%08x\n" " ISP_ACQ %dx%d@(%d,%d)\n" " ISP_OUT %dx%d@(%d,%d)\n" " ISP_IS %dx%d@(%d,%d)\n", cif_ioread32(dev->config.base_addr + CIF_ISP_CTRL), cif_ioread32(dev->config.base_addr + CIF_ISP_IMSC), cif_ioread32(dev->config.base_addr + CIF_ISP_ACQ_PROP), cif_ioread32(dev->config.base_addr + CIF_ISP_ACQ_H_SIZE), cif_ioread32(dev->config.base_addr + CIF_ISP_ACQ_V_SIZE), cif_ioread32(dev->config.base_addr + CIF_ISP_ACQ_H_OFFS), cif_ioread32(dev->config.base_addr + CIF_ISP_ACQ_V_OFFS), cif_ioread32(dev->config.base_addr + CIF_ISP_OUT_H_SIZE), cif_ioread32(dev->config.base_addr + CIF_ISP_OUT_V_SIZE), cif_ioread32(dev->config.base_addr + CIF_ISP_OUT_H_OFFS), cif_ioread32(dev->config.base_addr + CIF_ISP_OUT_V_OFFS), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_H_SIZE), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_V_SIZE), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_H_OFFS), cif_ioread32(dev->config.base_addr + CIF_ISP_IS_V_OFFS)); return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static int cif_isp10_config_mipi( struct cif_isp10_device *dev) { int ret = 0; u32 data_type; u32 mipi_ctrl; u32 shutdown_lanes; u32 i; enum cif_isp10_pix_fmt in_pix_fmt; if (!CIF_ISP10_INP_IS_MIPI(dev->config.input_sel)) { cif_iowrite32AND(~CIF_ICCL_MIPI_CLK, dev->config.base_addr + CIF_ICCL); cif_isp10_pltfrm_pr_dbg(NULL, "MIPI disabled\n"); return 0; } cif_iowrite32OR(CIF_ICCL_MIPI_CLK, dev->config.base_addr + CIF_ICCL); in_pix_fmt = dev->config.img_src_output.frm_fmt.pix_fmt; cif_isp10_pltfrm_pr_dbg(dev->dev, "input %s, vc = %d, nb_lanes = %d\n", cif_isp10_inp_string(dev->config.input_sel), dev->config.cam_itf.cfg.mipi.vc, dev->config.cam_itf.cfg.mipi.nb_lanes); if ((dev->config.cam_itf.cfg.mipi.nb_lanes == 0) || (dev->config.cam_itf.cfg.mipi.nb_lanes > 4)) { cif_isp10_pltfrm_pr_err(dev->dev, "invalid number (%d) of MIPI lanes, valid range is [1..4]\n", dev->config.cam_itf.cfg.mipi.nb_lanes); ret = -EINVAL; goto err; } shutdown_lanes = 0x00; for (i = 0; i < dev->config.cam_itf.cfg.mipi.nb_lanes; i++) shutdown_lanes |= (1 << i); mipi_ctrl = CIF_MIPI_CTRL_NUM_LANES( dev->config.cam_itf.cfg.mipi.nb_lanes - 1) | CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP | CIF_MIPI_CTRL_SHUTDOWNLANES(shutdown_lanes) | CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_ENA | CIF_MIPI_CTRL_ERR_SOT_HS_ENA | CIF_MIPI_CTRL_CLOCKLANE_ENA; cif_iowrite32(mipi_ctrl, dev->config.base_addr + CIF_MIPI_CTRL); /* mipi_dphy */ cif_isp10_pltfrm_mipi_dphy_config(dev); /* Configure Data Type and Virtual Channel */ if (CIF_ISP10_PIX_FMT_IS_YUV(in_pix_fmt)) { if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(in_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(in_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_GET_BPP(in_pix_fmt) == 12)) data_type = CSI2_DT_YUV420_8b; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(in_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(in_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_GET_BPP(in_pix_fmt) == 15)) data_type = CSI2_DT_YUV420_10b; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(in_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(in_pix_fmt) == 4) && (CIF_ISP10_PIX_FMT_GET_BPP(in_pix_fmt) == 16)) data_type = CSI2_DT_YUV422_8b; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(in_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(in_pix_fmt) == 4) && (CIF_ISP10_PIX_FMT_GET_BPP(in_pix_fmt) == 20)) data_type = CSI2_DT_YUV422_10b; else if (in_pix_fmt == CIF_YUV400) data_type = CSI2_DT_RAW8; else if (in_pix_fmt == CIF_Y10) data_type = CSI2_DT_RAW10; else if (in_pix_fmt == CIF_Y12) data_type = CSI2_DT_RAW12; else { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported format %s\n", cif_isp10_pix_fmt_string(in_pix_fmt)); ret = -EINVAL; goto err; } } else if (CIF_ISP10_PIX_FMT_IS_RAW_BAYER(in_pix_fmt)) { if (CIF_ISP10_PIX_FMT_GET_BPP(in_pix_fmt) == 8) { data_type = CSI2_DT_RAW8; } else if (CIF_ISP10_PIX_FMT_GET_BPP(in_pix_fmt) == 10) { data_type = CSI2_DT_RAW10; } else if (CIF_ISP10_PIX_FMT_GET_BPP(in_pix_fmt) == 12) { data_type = CSI2_DT_RAW12; } else { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported format %s\n", cif_isp10_pix_fmt_string(in_pix_fmt)); ret = -EINVAL; goto err; } } else if (in_pix_fmt == CIF_RGB565) { data_type = CSI2_DT_RGB565; } else if (in_pix_fmt == CIF_RGB666) { data_type = CSI2_DT_RGB666; } else if (in_pix_fmt == CIF_RGB888) { data_type = CSI2_DT_RGB888; } else { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported format %s\n", cif_isp10_pix_fmt_string(in_pix_fmt)); ret = -EINVAL; goto err; } cif_iowrite32( CIF_MIPI_DATA_SEL_DT(data_type) | CIF_MIPI_DATA_SEL_VC( dev->config.cam_itf.cfg.mipi.vc), dev->config.base_addr + CIF_MIPI_IMG_DATA_SEL); /* Enable MIPI interrupts */ cif_iowrite32(~0, dev->config.base_addr + CIF_MIPI_ICR); /* * Disable CIF_MIPI_ERR_DPHY interrupt here temporary for * isp bus may be dead when switch isp. */ cif_iowrite32( CIF_MIPI_FRAME_END | CIF_MIPI_ERR_CSI | CIF_MIPI_ERR_DPHY | CIF_MIPI_SYNC_FIFO_OVFLW(3) | CIF_MIPI_ADD_DATA_OVFLW, dev->config.base_addr + CIF_MIPI_IMSC); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MIPI_CTRL 0x%08x\n" " MIPI_IMG_DATA_SEL 0x%08x\n" " MIPI_STATUS 0x%08x\n" " MIPI_IMSC 0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_MIPI_CTRL), cif_ioread32(dev->config.base_addr + CIF_MIPI_IMG_DATA_SEL), cif_ioread32(dev->config.base_addr + CIF_MIPI_STATUS), cif_ioread32(dev->config.base_addr + CIF_MIPI_IMSC)); return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static u32 calc_burst_len(struct cif_isp10_mi_path_config *config) { u32 cb_offs = config->cb_offs; u32 cr_offs = config->cr_offs; u32 burst; /* MI64bit Y/C base addr should be burstN * 8 aligned */ if (!(cb_offs % 128) && !(cr_offs % 128)) burst = CIF_MI_CTRL_BURST_LEN_LUM_16 | CIF_MI_CTRL_BURST_LEN_CHROM_16; else if (!(cb_offs % 64) && !(cr_offs % 64)) burst = CIF_MI_CTRL_BURST_LEN_LUM_8 | CIF_MI_CTRL_BURST_LEN_CHROM_8; else burst = CIF_MI_CTRL_BURST_LEN_LUM_4 | CIF_MI_CTRL_BURST_LEN_CHROM_4; if (cb_offs % 32 || cr_offs % 32) cif_isp10_pltfrm_pr_warn(dev->dev, "%s %dx%d not support, should be %d aligned\n", cif_isp10_pix_fmt_string(config->output.pix_fmt), config->output.width, config->output.height, (cb_offs == cr_offs) ? 32 : 128); return burst; } static int cif_isp10_config_mi_mp( struct cif_isp10_device *dev) { enum cif_isp10_pix_fmt out_pix_fmt = dev->config.mi_config.mp.output.pix_fmt; u32 llength = dev->config.mi_config.mp.llength; u32 width = dev->config.mi_config.mp.output.width; u32 height = dev->config.mi_config.mp.output.height; u32 writeformat = CIF_ISP10_BUFF_FMT_PLANAR; u32 swap_cb_cr = 0; u32 bpp = CIF_ISP10_PIX_FMT_GET_BPP(out_pix_fmt); u32 size = llength * height * bpp / 8; u32 mi_ctrl; u32 burst_len; dev->config.mi_config.mp.input = &dev->config.mp_config.rsz_config.output; cif_isp10_pltfrm_pr_dbg(dev->dev, "%s %dx%d, llength = %d\n", cif_isp10_pix_fmt_string(out_pix_fmt), width, height, llength); dev->config.mi_config.mp.y_size = size; dev->config.mi_config.mp.cb_size = 0; dev->config.mi_config.mp.cr_size = 0; burst_len = CIF_MI_CTRL_BURST_LEN_LUM_16 | CIF_MI_CTRL_BURST_LEN_CHROM_16; if (CIF_ISP10_PIX_FMT_IS_YUV(out_pix_fmt)) { u32 num_cplanes = CIF_ISP10_PIX_FMT_YUV_GET_NUM_CPLANES(out_pix_fmt); if (num_cplanes == 0) { writeformat = CIF_ISP10_BUFF_FMT_INTERLEAVED; } else { dev->config.mi_config.mp.y_size = (dev->config.mi_config.mp.y_size * 4) / (4 + (CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS( out_pix_fmt) * CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS( out_pix_fmt) / 2)); dev->config.mi_config.mp.cb_size = size - dev->config.mi_config.mp.y_size; if (num_cplanes == 1) { writeformat = CIF_ISP10_BUFF_FMT_SEMIPLANAR; } else if (num_cplanes == 2) { writeformat = CIF_ISP10_BUFF_FMT_PLANAR; dev->config.mi_config.mp.cb_size /= 2; } /* for U<->V swapping: */ dev->config.mi_config.mp.cr_size = dev->config.mi_config.mp.cb_size; } if (CIF_ISP10_PIX_FMT_YUV_IS_UV_SWAPPED(out_pix_fmt)) swap_cb_cr = CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP; if (writeformat == CIF_ISP10_BUFF_FMT_SEMIPLANAR) { dev->config.mi_config.mp.cb_offs = dev->config.mi_config.mp.y_size; dev->config.mi_config.mp.cr_offs = dev->config.mi_config.mp.cb_offs; } else if (writeformat == CIF_ISP10_BUFF_FMT_PLANAR) { if (swap_cb_cr) { swap_cb_cr = 0; dev->config.mi_config.mp.cr_offs = dev->config.mi_config.mp.y_size; dev->config.mi_config.mp.cb_offs = dev->config.mi_config.mp.cr_offs + dev->config.mi_config.mp.cr_size; } else { dev->config.mi_config.mp.cb_offs = dev->config.mi_config.mp.y_size; dev->config.mi_config.mp.cr_offs = dev->config.mi_config.mp.cb_offs + dev->config.mi_config.mp.cb_size; } } burst_len = calc_burst_len(&dev->config.mi_config.mp); } else if (CIF_ISP10_PIX_FMT_IS_RAW_BAYER(out_pix_fmt)) { if (CIF_ISP10_PIX_FMT_GET_BPP(out_pix_fmt) > 8) { writeformat = CIF_ISP10_BUFF_FMT_RAW12; dev->config.mi_config.mp.y_size = width * height * 2; } else { writeformat = CIF_ISP10_BUFF_FMT_RAW8; dev->config.mi_config.mp.y_size = width * height; } dev->config.mi_config.mp.cb_offs = 0x00; dev->config.mi_config.mp.cr_offs = 0x00; dev->config.mi_config.mp.cb_size = 0x00; dev->config.mi_config.mp.cr_size = 0x00; } cif_iowrite32_verify(dev->config.mi_config.mp.y_size, dev->config.base_addr + CIF_MI_MP_Y_SIZE_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.mp.cb_size, dev->config.base_addr + CIF_MI_MP_CB_SIZE_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.mp.cr_size, dev->config.base_addr + CIF_MI_MP_CR_SIZE_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32OR_verify(CIF_MI_MP_FRAME, dev->config.base_addr + CIF_MI_IMSC, ~0); if (swap_cb_cr) { cif_iowrite32OR(CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP, dev->config.base_addr + CIF_MI_XTD_FORMAT_CTRL); } else { cif_iowrite32AND(~CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP, dev->config.base_addr + CIF_MI_XTD_FORMAT_CTRL); } /* check MP/SP burst config */ dev->config.mi_config.mp.burst_len = burst_len; burst_len = (dev->config.mi_config.sp.burst_len > dev->config.mi_config.mp.burst_len) ? dev->config.mi_config.mp.burst_len : dev->config.mi_config.sp.burst_len; mi_ctrl = cif_ioread32(dev->config.base_addr + CIF_MI_CTRL); mi_ctrl &= ~(CIF_MI_CTRL_MP_WRITE_FMT(3)); mi_ctrl |= CIF_MI_CTRL_MP_WRITE_FMT(writeformat) | burst_len | CIF_MI_CTRL_INIT_BASE_EN | CIF_MI_CTRL_INIT_OFFSET_EN | CIF_MI_MP_AUTOUPDATE_ENABLE; cif_iowrite32_verify(mi_ctrl, dev->config.base_addr + CIF_MI_CTRL, ~0); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MI_CTRL 0x%08x\n" " MI_STATUS 0x%08x\n" " MI_MP_Y_SIZE %d\n" " MI_MP_CB_SIZE %d\n" " MI_MP_CR_SIZE %d\n" " MI_XTD_FORMAT_CTRL %d\n", cif_ioread32(dev->config.base_addr + CIF_MI_CTRL), cif_ioread32(dev->config.base_addr + CIF_MI_STATUS), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CB_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CR_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_XTD_FORMAT_CTRL)); return 0; } static int cif_isp10_config_mi_sp( struct cif_isp10_device *dev) { int ret = 0; enum cif_isp10_pix_fmt out_pix_fmt = dev->config.mi_config.sp.output.pix_fmt; enum cif_isp10_pix_fmt in_pix_fmt = dev->config.sp_config.rsz_config.output.pix_fmt; u32 llength = dev->config.mi_config.sp.llength; u32 width = dev->config.mi_config.sp.output.width; u32 height = dev->config.mi_config.sp.output.height; u32 writeformat = CIF_ISP10_BUFF_FMT_PLANAR; u32 swap_cb_cr = 0; u32 bpp = CIF_ISP10_PIX_FMT_GET_BPP(out_pix_fmt); u32 size = llength * height * bpp / 8; u32 input_format = 0; u32 output_format; u32 burst_len; u32 mi_ctrl; if (out_pix_fmt == CIF_YUV400) in_pix_fmt = CIF_YUV400; dev->config.mi_config.sp.input = &dev->config.sp_config.rsz_config.output; cif_isp10_pltfrm_pr_dbg(dev->dev, "%s %dx%d, llength = %d\n", cif_isp10_pix_fmt_string(out_pix_fmt), width, height, llength); if (PLTFRM_CAM_ITF_INTERLACE(dev->config.cam_itf.type)) { llength = 2 * llength; height = height / 2; dev->config.mi_config.sp.vir_len_offset = width; } if (llength != width) { if (!(width % 128)) burst_len = CIF_MI_CTRL_BURST_LEN_LUM_16 | CIF_MI_CTRL_BURST_LEN_CHROM_16; else if (!(width % 64)) burst_len = CIF_MI_CTRL_BURST_LEN_LUM_8 | CIF_MI_CTRL_BURST_LEN_CHROM_8; else burst_len = CIF_MI_CTRL_BURST_LEN_LUM_4 | CIF_MI_CTRL_BURST_LEN_CHROM_4; if (width % 32) cif_isp10_pltfrm_pr_warn(dev->dev, "The width should be aligned to 32\n"); } else { burst_len = CIF_MI_CTRL_BURST_LEN_LUM_16 | CIF_MI_CTRL_BURST_LEN_CHROM_16; } if (!CIF_ISP10_PIX_FMT_IS_YUV(in_pix_fmt)) { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported format %s (must be YUV)\n", cif_isp10_pix_fmt_string(in_pix_fmt)); ret = -EINVAL; goto err; } dev->config.mi_config.sp.y_size = size; dev->config.mi_config.sp.cb_size = 0; dev->config.mi_config.sp.cr_size = 0; if (CIF_ISP10_PIX_FMT_IS_YUV(out_pix_fmt)) { u32 num_cplanes = CIF_ISP10_PIX_FMT_YUV_GET_NUM_CPLANES(out_pix_fmt); if (num_cplanes == 0) { writeformat = CIF_ISP10_BUFF_FMT_INTERLEAVED; if (out_pix_fmt == CIF_YUV400) writeformat = CIF_ISP10_BUFF_FMT_PLANAR; } else { dev->config.mi_config.sp.y_size = (dev->config.mi_config.sp.y_size * 4) / (4 + (CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS( out_pix_fmt) * CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS( out_pix_fmt) / 2)); dev->config.mi_config.sp.cb_size = size - dev->config.mi_config.sp.y_size; if (num_cplanes == 1) { writeformat = CIF_ISP10_BUFF_FMT_SEMIPLANAR; } else if (num_cplanes == 2) { writeformat = CIF_ISP10_BUFF_FMT_PLANAR; dev->config.mi_config.sp.cb_size /= 2; } /* for U<->V swapping: */ dev->config.mi_config.sp.cr_size = dev->config.mi_config.sp.cb_size; } if (CIF_ISP10_PIX_FMT_YUV_IS_UV_SWAPPED(out_pix_fmt)) swap_cb_cr = CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP; if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(out_pix_fmt) == 0) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(out_pix_fmt) == 0)) output_format = CIF_MI_CTRL_SP_OUTPUT_FMT_YUV400; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(out_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(out_pix_fmt) == 2)) { output_format = CIF_MI_CTRL_SP_OUTPUT_FMT_YUV420; dev->config.mi_config.sp.vir_len_offset = width; } else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(out_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(out_pix_fmt) == 4)) { output_format = CIF_MI_CTRL_SP_OUTPUT_FMT_YUV422; dev->config.mi_config.sp.vir_len_offset = width * 2; } else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(out_pix_fmt) == 4) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(out_pix_fmt) == 4)) output_format = CIF_MI_CTRL_SP_OUTPUT_FMT_YUV444; else { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported YUV output format %s\n", cif_isp10_pix_fmt_string(out_pix_fmt)); ret = -EINVAL; goto err; } } else if (CIF_ISP10_PIX_FMT_IS_RGB(out_pix_fmt)) { if (out_pix_fmt == CIF_RGB565) { output_format = CIF_MI_CTRL_SP_OUTPUT_FMT_RGB565; } else if (out_pix_fmt == CIF_RGB666) { output_format = CIF_MI_CTRL_SP_OUTPUT_FMT_RGB666; } else if (out_pix_fmt == CIF_RGB888) { output_format = CIF_MI_CTRL_SP_OUTPUT_FMT_RGB888; } else { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported RGB output format %s\n", cif_isp10_pix_fmt_string(out_pix_fmt)); ret = -EINVAL; goto err; } } else { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported output format %s\n", cif_isp10_pix_fmt_string(out_pix_fmt)); ret = -EINVAL; goto err; } if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(in_pix_fmt) == 0) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(in_pix_fmt) == 0)) input_format = CIF_MI_CTRL_SP_INPUT_FMT_YUV400; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(in_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(in_pix_fmt) == 2)) input_format = CIF_MI_CTRL_SP_INPUT_FMT_YUV420; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(in_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(in_pix_fmt) == 4)) input_format = CIF_MI_CTRL_SP_INPUT_FMT_YUV422; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(in_pix_fmt) == 4) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(in_pix_fmt) == 4)) input_format = CIF_MI_CTRL_SP_INPUT_FMT_YUV444; else { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported YUV input format %s\n", cif_isp10_pix_fmt_string(in_pix_fmt)); ret = -EINVAL; goto err; } if (writeformat == CIF_ISP10_BUFF_FMT_SEMIPLANAR) { dev->config.mi_config.sp.cb_offs = dev->config.mi_config.sp.y_size; dev->config.mi_config.sp.cr_offs = dev->config.mi_config.sp.cb_offs; } else if (writeformat == CIF_ISP10_BUFF_FMT_PLANAR) { if (swap_cb_cr) { swap_cb_cr = 0; dev->config.mi_config.sp.cr_offs = dev->config.mi_config.sp.y_size; dev->config.mi_config.sp.cb_offs = dev->config.mi_config.sp.cr_offs + dev->config.mi_config.sp.cr_size; } else { dev->config.mi_config.sp.cb_offs = dev->config.mi_config.sp.y_size; dev->config.mi_config.sp.cr_offs = dev->config.mi_config.sp.cb_offs + dev->config.mi_config.sp.cb_size; } } if (llength == width && CIF_ISP10_PIX_FMT_IS_YUV(out_pix_fmt)) burst_len = calc_burst_len(&dev->config.mi_config.sp); cif_iowrite32_verify(dev->config.mi_config.sp.y_size, dev->config.base_addr + CIF_MI_SP_Y_SIZE_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.sp.y_size, dev->config.base_addr + CIF_MI_SP_Y_PIC_SIZE, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.sp.cb_size, dev->config.base_addr + CIF_MI_SP_CB_SIZE_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.sp.cr_size, dev->config.base_addr + CIF_MI_SP_CR_SIZE_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(width, dev->config.base_addr + CIF_MI_SP_Y_PIC_WIDTH, ~0x3); cif_iowrite32_verify(height, dev->config.base_addr + CIF_MI_SP_Y_PIC_HEIGHT, ~0x3); cif_iowrite32_verify(llength, dev->config.base_addr + CIF_MI_SP_Y_LLENGTH, ~0x3); cif_iowrite32OR_verify(CIF_MI_SP_FRAME, dev->config.base_addr + CIF_MI_IMSC, ~0); if (swap_cb_cr) { cif_iowrite32OR(CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP, dev->config.base_addr + CIF_MI_XTD_FORMAT_CTRL); } else { cif_iowrite32AND(~CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP, dev->config.base_addr + CIF_MI_XTD_FORMAT_CTRL); } dev->config.mi_config.sp.burst_len = burst_len; burst_len = (dev->config.mi_config.sp.burst_len > dev->config.mi_config.mp.burst_len) ? dev->config.mi_config.mp.burst_len : dev->config.mi_config.sp.burst_len; mi_ctrl = cif_ioread32(dev->config.base_addr + CIF_MI_CTRL); mi_ctrl &= ~(CIF_MI_CTRL_SP_WRITE_FMT(3) |/* SP_WRITE_FMT */ (0x7 << 28) | /* SP_OUTPUT_FMT */ (0x3 << 26) | /* SP_INPUT_FMT */ (0xF << 16)); /* burst_len_chrom & burst_len_lum */ mi_ctrl = mi_ctrl | CIF_MI_CTRL_SP_WRITE_FMT(writeformat) | input_format | output_format | burst_len | CIF_MI_CTRL_INIT_BASE_EN | CIF_MI_CTRL_INIT_OFFSET_EN | CIF_MI_SP_AUTOUPDATE_ENABLE; cif_iowrite32_verify(mi_ctrl, dev->config.base_addr + CIF_MI_CTRL, ~0); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MI_CTRL 0x%08x\n" " MI_STATUS 0x%08x\n" " MI_SP_Y_SIZE %d\n" " MI_SP_CB_SIZE %d\n" " MI_SP_CR_SIZE %d\n" " MI_SP_PIC_WIDTH %d\n" " MI_SP_PIC_HEIGHT %d\n" " MI_SP_PIC_LLENGTH %d\n" " MI_SP_PIC_SIZE %d\n" " MI_XTD_FORMAT_CTRL %d\n", cif_ioread32(dev->config.base_addr + CIF_MI_CTRL), cif_ioread32(dev->config.base_addr + CIF_MI_STATUS), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_SIZE_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_PIC_WIDTH), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_PIC_HEIGHT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_LLENGTH), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_PIC_SIZE), cif_ioread32(dev->config.base_addr + CIF_MI_XTD_FORMAT_CTRL)); return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static int cif_isp10_config_mi_dma( struct cif_isp10_device *dev) { int ret = 0; enum cif_isp10_pix_fmt out_pix_fmt = dev->config.mi_config.dma.output.pix_fmt; u32 llength = dev->config.mi_config.dma.llength; u32 width = dev->config.mi_config.dma.output.width; u32 height = dev->config.mi_config.dma.output.height; u32 readformat = CIF_ISP10_BUFF_FMT_PLANAR; u32 bpp = CIF_ISP10_PIX_FMT_GET_BPP(out_pix_fmt); u32 size = llength * height * bpp / 8; u32 output_format; u32 mi_ctrl; cif_isp10_pltfrm_pr_dbg(dev->dev, "%s %dx%d, llength = %d\n", cif_isp10_pix_fmt_string(out_pix_fmt), width, height, llength); dev->config.mi_config.dma.y_size = size; dev->config.mi_config.dma.cb_size = 0; dev->config.mi_config.dma.cr_size = 0; if (CIF_ISP10_PIX_FMT_IS_YUV(out_pix_fmt)) { u32 num_cplanes = CIF_ISP10_PIX_FMT_YUV_GET_NUM_CPLANES(out_pix_fmt); if (num_cplanes == 0) { readformat = CIF_ISP10_BUFF_FMT_INTERLEAVED; } else { dev->config.mi_config.dma.y_size = (dev->config.mi_config.dma.y_size * 4) / (4 + (CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS( out_pix_fmt) * CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS( out_pix_fmt) / 2)); dev->config.mi_config.dma.cb_size = size - dev->config.mi_config.dma.y_size; if (num_cplanes == 1) { readformat = CIF_ISP10_BUFF_FMT_SEMIPLANAR; } else if (num_cplanes == 2) { readformat = CIF_ISP10_BUFF_FMT_PLANAR; dev->config.mi_config.dma.cb_size /= 2; } /* for U<->V swapping: */ dev->config.mi_config.dma.cr_size = dev->config.mi_config.dma.cb_size; } if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(out_pix_fmt) == 0) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(out_pix_fmt) == 0)) output_format = CIF_MI_DMA_CTRL_FMT_YUV400; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(out_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(out_pix_fmt) == 2)) output_format = CIF_MI_DMA_CTRL_FMT_YUV420; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(out_pix_fmt) == 2) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(out_pix_fmt) == 4)) output_format = CIF_MI_DMA_CTRL_FMT_YUV422; else if ((CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(out_pix_fmt) == 4) && (CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(out_pix_fmt) == 4)) output_format = CIF_MI_DMA_CTRL_FMT_YUV444; else { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported YUV output format %s\n", cif_isp10_pix_fmt_string(out_pix_fmt)); ret = -EINVAL; goto err; } } else { cif_isp10_pltfrm_pr_err(dev->dev, "unsupported output format %s\n", cif_isp10_pix_fmt_string(out_pix_fmt)); ret = -EINVAL; goto err; } if (readformat == CIF_ISP10_BUFF_FMT_SEMIPLANAR) { dev->config.mi_config.dma.cb_offs = dev->config.mi_config.dma.y_size; dev->config.mi_config.dma.cr_offs = dev->config.mi_config.dma.cb_offs; } else if (readformat == CIF_ISP10_BUFF_FMT_PLANAR) { dev->config.mi_config.dma.cb_offs = dev->config.mi_config.dma.y_size; dev->config.mi_config.dma.cr_offs = dev->config.mi_config.dma.cb_offs + dev->config.mi_config.dma.cb_size; } cif_iowrite32_verify(dev->config.mi_config.dma.y_size, dev->config.base_addr + CIF_MI_DMA_Y_PIC_SIZE, ~0x3); cif_iowrite32_verify(width, dev->config.base_addr + CIF_MI_DMA_Y_PIC_WIDTH, ~0x3); cif_iowrite32_verify(llength, dev->config.base_addr + CIF_MI_DMA_Y_LLENGTH, ~0x3); mi_ctrl = cif_ioread32(dev->config.base_addr + CIF_MI_DMA_CTRL); mi_ctrl &= ~(CIF_MI_DMA_CTRL_READ_FMT(3) & CIF_MI_DMA_CTRL_FMT_YUV444); mi_ctrl |= CIF_MI_DMA_CTRL_READ_FMT(readformat) | output_format | CIF_MI_DMA_CTRL_BURST_LEN_LUM_64 | CIF_MI_DMA_CTRL_BURST_LEN_CHROM_64; cif_iowrite32_verify(mi_ctrl, dev->config.base_addr + CIF_MI_DMA_CTRL, ~0); cif_iowrite32OR_verify(CIF_MI_DMA_READY, dev->config.base_addr + CIF_MI_IMSC, ~0); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MI_DMA_CTRL 0x%08x\n" " MI_DMA_STATUS 0x%08x\n" " MI_DMA_Y_PIC_WIDTH %d\n" " MI_DMA_Y_LLENGTH %d\n" " MI_DMA_Y_PIC_SIZE %d\n" " MI_DMA_Y_PIC_START_AD %d\n" " MI_DMA_CB_PIC_START_AD %d\n" " MI_DMA_CR_PIC_START_AD %d\n", cif_ioread32(dev->config.base_addr + CIF_MI_DMA_CTRL), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_STATUS), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_Y_PIC_WIDTH), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_Y_LLENGTH), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_Y_PIC_SIZE), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_Y_PIC_START_AD), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_CB_PIC_START_AD), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_CR_PIC_START_AD)); return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static int cif_isp10_config_jpeg_enc( struct cif_isp10_device *dev) { struct cif_isp10_frm_fmt *inp_fmt = &dev->config.mp_config.rsz_config.output; dev->config.jpeg_config.input = inp_fmt; cif_isp10_pltfrm_pr_dbg(NULL, "%s %dx%d\n", cif_isp10_pix_fmt_string(inp_fmt->pix_fmt), inp_fmt->width, inp_fmt->height); /* * Reset JPEG-Encoder. In contrast to other software * resets this triggers the modules asynchronous reset * resulting in loss of all data */ cif_iowrite32OR(CIF_IRCL_JPEG_SW_RST, dev->config.base_addr + CIF_IRCL); cif_iowrite32AND(~CIF_IRCL_JPEG_SW_RST, dev->config.base_addr + CIF_IRCL); cif_iowrite32(CIF_JPE_ERROR_MASK, dev->config.base_addr + CIF_JPE_ERROR_IMSC); /* Set configuration for the Jpeg capturing */ cif_iowrite32(inp_fmt->width, dev->config.base_addr + CIF_JPE_ENC_HSIZE); cif_iowrite32(inp_fmt->height, dev->config.base_addr + CIF_JPE_ENC_VSIZE); if (CIF_ISP10_INP_IS_DMA(dev->config.input_sel) || !CIF_ISP10_PIX_FMT_IS_RAW_BAYER( dev->config.isp_config.input->pix_fmt)) { /* * upscaling of BT601 color space to full range 0..255 * TODO: DMA or YUV sensor input in full range. */ cif_iowrite32(CIF_JPE_LUM_SCALE_ENABLE, dev->config.base_addr + CIF_JPE_Y_SCALE_EN); cif_iowrite32(CIF_JPE_CHROM_SCALE_ENABLE, dev->config.base_addr + CIF_JPE_CBCR_SCALE_EN); } switch (inp_fmt->pix_fmt) { case CIF_YUV422I: case CIF_YVU422I: case CIF_YUV422SP: case CIF_YVU422SP: case CIF_YUV422P: case CIF_YVU422P: cif_iowrite32(CIF_JPE_PIC_FORMAT_YUV422, dev->config.base_addr + CIF_JPE_PIC_FORMAT); break; case CIF_YUV400: case CIF_YVU400: cif_iowrite32(CIF_JPE_PIC_FORMAT_YUV400, dev->config.base_addr + CIF_JPE_PIC_FORMAT); break; default: cif_isp10_pltfrm_pr_err(NULL, "format %s (%.8x) not supported as input for JPEG encoder\n", cif_isp10_pix_fmt_string(inp_fmt->pix_fmt), inp_fmt->pix_fmt); WARN_ON(1); break; } /* * Set to normal operation (wait for encoded image data * to fill output buffer) */ cif_iowrite32(0, dev->config.base_addr + CIF_JPE_TABLE_FLUSH); /* * CIF Spec 4.7 * 3.14 JPEG Encoder Programming * Do not forget to re-program all AC and DC tables * after system reset as well as after * module software reset because after any reset * the internal RAM is filled with FFH which * is an illegal symbol. This filling takes * approximately 400 clock cycles. So do not start * any table programming during the first 400 clock * cycles after reset is de-asserted. * Note: depends on CIF clock setting * 400 clock cycles at 312 Mhz CIF clock-> 1.3 us * 400 clock cycles at 208 Mhz CIF clock-> 1.93 us * -> 2us ok for both */ udelay(2); /* Program JPEG tables */ cif_isp10_program_jpeg_tables(dev); /* Select JPEG tables */ cif_isp10_select_jpeg_tables(dev); switch (dev->config.jpeg_config.header) { case CIF_ISP10_JPEG_HEADER_JFIF: cif_isp10_pltfrm_pr_dbg(NULL, "generate JFIF header\n"); cif_iowrite32(CIF_JPE_HEADER_MODE_JFIF, dev->config.base_addr + CIF_JPE_HEADER_MODE); break; case CIF_ISP10_JPEG_HEADER_NONE: cif_isp10_pltfrm_pr_dbg(NULL, "generate no JPEG header\n"); cif_iowrite32(CIF_JPE_HEADER_MODE_NOAPPN, dev->config.base_addr + CIF_JPE_HEADER_MODE); break; default: cif_isp10_pltfrm_pr_err(NULL, "unknown/unsupport JPEG header type %d\n", dev->config.jpeg_config.header); WARN_ON(1); break; } cif_isp10_pltfrm_pr_dbg(dev->dev, "\n JPE_PIC_FORMAT 0x%08x\n" " JPE_ENC_HSIZE %d\n" " JPE_ENC_VSIZE %d\n" " JPE_Y_SCALE_EN 0x%08x\n" " JPE_CBCR_SCALE_EN 0x%08x\n" " JPE_ERROR_RIS 0x%08x\n" " JPE_ERROR_IMSC 0x%08x\n" " JPE_STATUS_RIS 0x%08x\n" " JPE_STATUS_IMSC 0x%08x\n" " JPE_DEBUG 0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_JPE_PIC_FORMAT), cif_ioread32(dev->config.base_addr + CIF_JPE_ENC_HSIZE), cif_ioread32(dev->config.base_addr + CIF_JPE_ENC_VSIZE), cif_ioread32(dev->config.base_addr + CIF_JPE_Y_SCALE_EN), cif_ioread32(dev->config.base_addr + CIF_JPE_CBCR_SCALE_EN), cif_ioread32(dev->config.base_addr + CIF_JPE_ERROR_RIS), cif_ioread32(dev->config.base_addr + CIF_JPE_ERROR_IMSC), cif_ioread32(dev->config.base_addr + CIF_JPE_STATUS_RIS), cif_ioread32(dev->config.base_addr + CIF_JPE_STATUS_IMSC), cif_ioread32(dev->config.base_addr + CIF_JPE_DEBUG)); return 0; } static int cif_isp10_config_path( struct cif_isp10_device *dev, u32 stream_ids) { u32 dpcl = 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "\n"); /* if_sel */ if (dev->config.input_sel == CIF_ISP10_INP_DMA) { dpcl |= CIF_VI_DPCL_DMA_SW_ISP; } else if (dev->config.input_sel == CIF_ISP10_INP_DMA_IE) { dpcl |= CIF_VI_DPCL_DMA_IE_MUX_DMA | CIF_VI_DPCL_DMA_SW_IE; } else if (dev->config.input_sel == CIF_ISP10_INP_DMA_SP) { dpcl |= CIF_VI_DPCL_DMA_SP_MUX_DMA; } else { if (PLTFRM_CAM_ITF_IS_DVP(dev->config.cam_itf.type)) { dpcl |= CIF_VI_DPCL_IF_SEL_PARALLEL; } else if (PLTFRM_CAM_ITF_IS_MIPI(dev->config.cam_itf.type)) { dpcl |= CIF_VI_DPCL_IF_SEL_MIPI; } else { cif_isp10_pltfrm_pr_err(dev->dev, "Sensor Interface: 0x%x isn't support\n", dev->config.cam_itf.type); return -EINVAL; } } /* chan_mode */ if (stream_ids & CIF_ISP10_STREAM_SP) dpcl |= CIF_VI_DPCL_CHAN_MODE_SP; if ((stream_ids & CIF_ISP10_STREAM_MP) && !(dev->config.input_sel == CIF_ISP10_INP_DMA_SP)) { dpcl |= CIF_VI_DPCL_CHAN_MODE_MP; /* mp_dmux */ if (dev->config.jpeg_config.enable) dpcl |= CIF_VI_DPCL_MP_MUX_MRSZ_JPEG; else dpcl |= CIF_VI_DPCL_MP_MUX_MRSZ_MI; } cif_iowrite32(dpcl, dev->config.base_addr + CIF_VI_DPCL); cif_isp10_pltfrm_pr_dbg(dev->dev, "CIF_DPCL 0x%08x\n", dpcl); return 0; } int cif_isp10_config_dcrop( struct cif_isp10_device *dev, enum cif_isp10_stream_id stream_id, bool async) { unsigned int dc_ctrl = cif_ioread32( dev->config.base_addr + CIF_DUAL_CROP_CTRL); if (stream_id == CIF_ISP10_STREAM_MP) { cif_iowrite32(0, dev->config.base_addr + CIF_DUAL_CROP_M_H_OFFS); cif_iowrite32(0, dev->config.base_addr + CIF_DUAL_CROP_M_V_OFFS); cif_iowrite32(0, dev->config.base_addr + CIF_DUAL_CROP_M_H_SIZE); cif_iowrite32(0, dev->config.base_addr + CIF_DUAL_CROP_M_V_SIZE); dc_ctrl |= CIF_DUAL_CROP_MP_MODE_BYPASS; if (async) dc_ctrl |= CIF_DUAL_CROP_GEN_CFG_UPD; else dc_ctrl |= CIF_DUAL_CROP_CFG_UPD; cif_iowrite32(dc_ctrl, dev->config.base_addr + CIF_DUAL_CROP_CTRL); } else if (stream_id == CIF_ISP10_STREAM_SP) { cif_iowrite32(0, dev->config.base_addr + CIF_DUAL_CROP_S_H_OFFS); cif_iowrite32(0, dev->config.base_addr + CIF_DUAL_CROP_S_V_OFFS); cif_iowrite32(0, dev->config.base_addr + CIF_DUAL_CROP_S_H_SIZE); cif_iowrite32(0, dev->config.base_addr + CIF_DUAL_CROP_S_V_SIZE); dc_ctrl |= CIF_DUAL_CROP_MP_MODE_BYPASS; if (async) dc_ctrl |= CIF_DUAL_CROP_GEN_CFG_UPD; else dc_ctrl |= CIF_DUAL_CROP_CFG_UPD; cif_iowrite32(dc_ctrl, dev->config.base_addr + CIF_DUAL_CROP_CTRL); } return 0; } int cif_isp10_config_rsz( struct cif_isp10_device *dev, enum cif_isp10_stream_id stream_id, bool async) { int ret; u32 i; CIF_ISP10_PLTFRM_MEM_IO_ADDR scale_h_y_addr = dev->config.base_addr; CIF_ISP10_PLTFRM_MEM_IO_ADDR scale_h_cr_addr = dev->config.base_addr; CIF_ISP10_PLTFRM_MEM_IO_ADDR scale_h_cb_addr = dev->config.base_addr; CIF_ISP10_PLTFRM_MEM_IO_ADDR scale_v_y_addr = dev->config.base_addr; CIF_ISP10_PLTFRM_MEM_IO_ADDR scale_v_c_addr = dev->config.base_addr; CIF_ISP10_PLTFRM_MEM_IO_ADDR rsz_ctrl_addr = dev->config.base_addr; struct cif_isp10_frm_fmt *rsz_input; struct cif_isp10_frm_fmt *rsz_output; struct cif_isp10_frm_fmt *mi_output; u32 rsz_ctrl; u32 input_width_y; u32 output_width_y; u32 input_height_y; u32 output_height_y; u32 input_width_c; u32 output_width_c; u32 input_height_c; u32 output_height_c; u32 scale_h_c; if (stream_id == CIF_ISP10_STREAM_MP) { rsz_ctrl_addr += CIF_MRSZ_CTRL; scale_h_y_addr += CIF_MRSZ_SCALE_HY; scale_v_y_addr += CIF_MRSZ_SCALE_VY; scale_h_cb_addr += CIF_MRSZ_SCALE_HCB; scale_h_cr_addr += CIF_MRSZ_SCALE_HCR; scale_v_c_addr += CIF_MRSZ_SCALE_VC; dev->config.mp_config.rsz_config.input = &dev->config.isp_config.output; rsz_input = dev->config.mp_config.rsz_config.input; rsz_output = &dev->config.mp_config.rsz_config.output; mi_output = &dev->config.mi_config.mp.output; /* No phase offset */ cif_iowrite32(0, dev->config.base_addr + CIF_MRSZ_PHASE_HY); cif_iowrite32(0, dev->config.base_addr + CIF_MRSZ_PHASE_HC); cif_iowrite32(0, dev->config.base_addr + CIF_MRSZ_PHASE_VY); cif_iowrite32(0, dev->config.base_addr + CIF_MRSZ_PHASE_VC); /* Linear interpolation */ for (i = 0; i < 64; i++) { cif_iowrite32(i, dev->config.base_addr + CIF_MRSZ_SCALE_LUT_ADDR); cif_iowrite32(i, dev->config.base_addr + CIF_MRSZ_SCALE_LUT); } } else { rsz_ctrl_addr += CIF_SRSZ_CTRL; scale_h_y_addr += CIF_SRSZ_SCALE_HY; scale_v_y_addr += CIF_SRSZ_SCALE_VY; scale_h_cb_addr += CIF_SRSZ_SCALE_HCB; scale_h_cr_addr += CIF_SRSZ_SCALE_HCR; scale_v_c_addr += CIF_SRSZ_SCALE_VC; if (dev->config.input_sel == CIF_ISP10_INP_DMA_SP) dev->config.sp_config.rsz_config.input = &dev->config.mi_config.dma.output; else dev->config.sp_config.rsz_config.input = &dev->config.isp_config.output; rsz_input = dev->config.sp_config.rsz_config.input; rsz_output = &dev->config.sp_config.rsz_config.output; mi_output = &dev->config.mi_config.sp.output; /* No phase offset */ cif_iowrite32(0, dev->config.base_addr + CIF_SRSZ_PHASE_HY); cif_iowrite32(0, dev->config.base_addr + CIF_SRSZ_PHASE_HC); cif_iowrite32(0, dev->config.base_addr + CIF_SRSZ_PHASE_VY); cif_iowrite32(0, dev->config.base_addr + CIF_SRSZ_PHASE_VC); /* Linear interpolation */ for (i = 0; i < 64; i++) { cif_iowrite32(i, dev->config.base_addr + CIF_SRSZ_SCALE_LUT_ADDR); cif_iowrite32(i, dev->config.base_addr + CIF_SRSZ_SCALE_LUT); } } /* set RSZ input and output */ rsz_output->width = mi_output->width; rsz_output->height = mi_output->height; rsz_output->pix_fmt = rsz_input->pix_fmt; if (CIF_ISP10_PIX_FMT_IS_YUV(mi_output->pix_fmt)) { cif_isp10_pix_fmt_set_y_subs( rsz_output->pix_fmt, CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS(mi_output->pix_fmt)); cif_isp10_pix_fmt_set_x_subs( rsz_output->pix_fmt, CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS(mi_output->pix_fmt)); cif_isp10_pix_fmt_set_bpp( rsz_output->pix_fmt, CIF_ISP10_PIX_FMT_GET_BPP(mi_output->pix_fmt)); } else if (CIF_ISP10_PIX_FMT_IS_JPEG(mi_output->pix_fmt)) { cif_isp10_pix_fmt_set_y_subs( rsz_output->pix_fmt, 4); cif_isp10_pix_fmt_set_x_subs( rsz_output->pix_fmt, 2); cif_isp10_pix_fmt_set_bpp( rsz_output->pix_fmt, 16); } cif_isp10_pltfrm_pr_dbg(dev->dev, "%s %s %dx%d -> %s %dx%d\n", cif_isp10_stream_id_string(stream_id), cif_isp10_pix_fmt_string(rsz_input->pix_fmt), rsz_input->width, rsz_input->height, cif_isp10_pix_fmt_string(rsz_output->pix_fmt), rsz_output->width, rsz_output->height); /* set input and output sizes for scale calculation */ input_width_y = rsz_input->width; output_width_y = rsz_output->width; input_height_y = rsz_input->height; output_height_y = rsz_output->height; input_width_c = input_width_y; output_width_c = output_width_y; input_height_c = input_height_y; output_height_c = output_height_y; if (CIF_ISP10_PIX_FMT_IS_YUV(rsz_output->pix_fmt)) { input_width_c = (input_width_c * CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS( rsz_input->pix_fmt)) / 4; input_height_c = (input_height_c * CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS( rsz_input->pix_fmt)) / 4; output_width_c = (output_width_c * CIF_ISP10_PIX_FMT_YUV_GET_X_SUBS( rsz_output->pix_fmt)) / 4; output_height_c = (output_height_c * CIF_ISP10_PIX_FMT_YUV_GET_Y_SUBS( rsz_output->pix_fmt)) / 4; cif_isp10_pltfrm_pr_dbg(NULL, "chroma scaling %dx%d -> %dx%d\n", input_width_c, input_height_c, output_width_c, output_height_c); if (((input_width_c == 0) && (output_width_c > 0)) || ((input_height_c == 0) && (output_height_c > 0))) { cif_isp10_pltfrm_pr_err(NULL, "input is black and white, cannot output colour\n"); ret = -EINVAL; goto err; } } else { if ((input_width_y != output_width_y) || (input_height_y != output_height_y)) { cif_isp10_pltfrm_pr_err(NULL, "%dx%d -> %dx%d isn't support, can only scale YUV input\n", input_width_y, input_height_y, output_width_y, output_height_y); ret = -EINVAL; goto err; } } /* calculate and set scale */ rsz_ctrl = 0; if (input_width_y < output_width_y) { rsz_ctrl |= CIF_RSZ_CTRL_SCALE_HY_ENABLE | CIF_RSZ_CTRL_SCALE_HY_UP; cif_iowrite32( DIV_TRUNCATE((input_width_y - 1) * CIF_RSZ_SCALER_BYPASS, output_width_y - 1), scale_h_y_addr); } else if (input_width_y > output_width_y) { rsz_ctrl |= CIF_RSZ_CTRL_SCALE_HY_ENABLE; cif_iowrite32( DIV_TRUNCATE((output_width_y - 1) * CIF_RSZ_SCALER_BYPASS, input_width_y - 1) + 1, scale_h_y_addr); } if (input_width_c < output_width_c) { rsz_ctrl |= CIF_RSZ_CTRL_SCALE_HC_ENABLE | CIF_RSZ_CTRL_SCALE_HC_UP; scale_h_c = DIV_TRUNCATE((input_width_c - 1) * CIF_RSZ_SCALER_BYPASS, output_width_c - 1); cif_iowrite32(scale_h_c, scale_h_cb_addr); cif_iowrite32(scale_h_c, scale_h_cr_addr); } else if (input_width_c > output_width_c) { rsz_ctrl |= CIF_RSZ_CTRL_SCALE_HC_ENABLE; scale_h_c = DIV_TRUNCATE((output_width_c - 1) * CIF_RSZ_SCALER_BYPASS, input_width_c - 1) + 1; cif_iowrite32(scale_h_c, scale_h_cb_addr); cif_iowrite32(scale_h_c, scale_h_cr_addr); } if (input_height_y < output_height_y) { rsz_ctrl |= CIF_RSZ_CTRL_SCALE_VY_ENABLE | CIF_RSZ_CTRL_SCALE_VY_UP; cif_iowrite32( DIV_TRUNCATE((input_height_y - 1) * CIF_RSZ_SCALER_BYPASS, output_height_y - 1), scale_v_y_addr); } else if (input_height_y > output_height_y) { rsz_ctrl |= CIF_RSZ_CTRL_SCALE_VY_ENABLE; cif_iowrite32( DIV_TRUNCATE((output_height_y - 1) * CIF_RSZ_SCALER_BYPASS, input_height_y - 1) + 1, scale_v_y_addr); } if (input_height_c < output_height_c) { rsz_ctrl |= CIF_RSZ_CTRL_SCALE_VC_ENABLE | CIF_RSZ_CTRL_SCALE_VC_UP; cif_iowrite32( DIV_TRUNCATE((input_height_c - 1) * CIF_RSZ_SCALER_BYPASS, output_height_c - 1), scale_v_c_addr); } else if (input_height_c > output_height_c) { rsz_ctrl |= CIF_RSZ_CTRL_SCALE_VC_ENABLE; cif_iowrite32( DIV_TRUNCATE((output_height_c - 1) * CIF_RSZ_SCALER_BYPASS, input_height_c - 1) + 1, scale_v_c_addr); } cif_iowrite32(rsz_ctrl, rsz_ctrl_addr); if (stream_id == CIF_ISP10_STREAM_MP) { if (async) cif_iowrite32OR(CIF_RSZ_CTRL_CFG_UPD, dev->config.base_addr + CIF_MRSZ_CTRL); dev->config.mp_config.rsz_config.ycflt_adjust = false; dev->config.mp_config.rsz_config.ism_adjust = false; cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MRSZ_CTRL 0x%08x/0x%08x\n" " MRSZ_SCALE_HY %d/%d\n" " MRSZ_SCALE_HCB %d/%d\n" " MRSZ_SCALE_HCR %d/%d\n" " MRSZ_SCALE_VY %d/%d\n" " MRSZ_SCALE_VC %d/%d\n" " MRSZ_PHASE_HY %d/%d\n" " MRSZ_PHASE_HC %d/%d\n" " MRSZ_PHASE_VY %d/%d\n" " MRSZ_PHASE_VC %d/%d\n", cif_ioread32(dev->config.base_addr + CIF_MRSZ_CTRL), cif_ioread32(dev->config.base_addr + CIF_MRSZ_CTRL_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HY), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HY_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HCB), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HCB_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HCR), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_HCR_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_VY), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_VY_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_VC), cif_ioread32(dev->config.base_addr + CIF_MRSZ_SCALE_VC_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_HY), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_HY_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_HC), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_HC_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_VY), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_VY_SHD), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_VC), cif_ioread32(dev->config.base_addr + CIF_MRSZ_PHASE_VC_SHD)); } else { if (async) cif_iowrite32OR(CIF_RSZ_CTRL_CFG_UPD, dev->config.base_addr + CIF_SRSZ_CTRL); dev->config.sp_config.rsz_config.ycflt_adjust = false; dev->config.sp_config.rsz_config.ism_adjust = false; cif_isp10_pltfrm_pr_dbg(dev->dev, "\n SRSZ_CTRL 0x%08x/0x%08x\n" " SRSZ_SCALE_HY %d/%d\n" " SRSZ_SCALE_HCB %d/%d\n" " SRSZ_SCALE_HCR %d/%d\n" " SRSZ_SCALE_VY %d/%d\n" " SRSZ_SCALE_VC %d/%d\n" " SRSZ_PHASE_HY %d/%d\n" " SRSZ_PHASE_HC %d/%d\n" " SRSZ_PHASE_VY %d/%d\n" " SRSZ_PHASE_VC %d/%d\n", cif_ioread32(dev->config.base_addr + CIF_SRSZ_CTRL), cif_ioread32(dev->config.base_addr + CIF_SRSZ_CTRL_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HY), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HY_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HCB), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HCB_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HCR), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_HCR_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_VY), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_VY_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_VC), cif_ioread32(dev->config.base_addr + CIF_SRSZ_SCALE_VC_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_HY), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_HY_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_HC), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_HC_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_VY), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_VY_SHD), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_VC), cif_ioread32(dev->config.base_addr + CIF_SRSZ_PHASE_VC_SHD)); } return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with err %d\n", ret); return ret; } static int cif_isp10_config_sp( struct cif_isp10_device *dev) { int ret = 0; cif_isp10_pltfrm_pr_dbg(NULL, "\n"); ret = cif_isp10_config_rsz(dev, CIF_ISP10_STREAM_SP, true); if (IS_ERR_VALUE(ret)) goto err; ret = cif_isp10_config_dcrop(dev, CIF_ISP10_STREAM_SP, true); if (IS_ERR_VALUE(ret)) goto err; ret = cif_isp10_config_mi_sp(dev); if (IS_ERR_VALUE(ret)) goto err; dev->sp_stream.updt_cfg = false; return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static int cif_isp10_config_mp( struct cif_isp10_device *dev) { int ret = 0; cif_isp10_pltfrm_pr_dbg(NULL, "\n"); ret = cif_isp10_config_rsz(dev, CIF_ISP10_STREAM_MP, true); if (IS_ERR_VALUE(ret)) goto err; ret = cif_isp10_config_dcrop(dev, CIF_ISP10_STREAM_MP, true); if (IS_ERR_VALUE(ret)) goto err; ret = cif_isp10_config_mi_mp(dev); if (IS_ERR_VALUE(ret)) goto err; if (dev->config.jpeg_config.enable) { ret = cif_isp10_config_jpeg_enc(dev); if (IS_ERR_VALUE(ret)) goto err; dev->config.jpeg_config.busy = false; } dev->mp_stream.updt_cfg = false; return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static void cif_isp10_config_clk( struct cif_isp10_device *dev) { cif_iowrite32(CIF_CCL_CIF_CLK_ENA, dev->config.base_addr + CIF_CCL); cif_iowrite32(0x0000187B, dev->config.base_addr + CIF_ICCL); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n CIF_CCL 0x%08x\n" " CIF_ICCL 0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_CCL), cif_ioread32(dev->config.base_addr + CIF_ICCL)); } static int cif_isp10_config_cif( struct cif_isp10_device *dev, u32 stream_ids) { int ret = 0; u32 cif_id; cif_isp10_pltfrm_pr_dbg(dev->dev, "config MP = %d, config SP = %d, img_src state = %s, PM state = %s, SP state = %s, MP state = %s\n", (stream_ids & CIF_ISP10_STREAM_MP) == CIF_ISP10_STREAM_MP, (stream_ids & CIF_ISP10_STREAM_SP) == CIF_ISP10_STREAM_SP, cif_isp10_img_src_state_string(dev->img_src_state), cif_isp10_pm_state_string(dev->pm_state), cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state)); cif_isp10_pltfrm_rtrace_printf(NULL, "start configuring CIF...\n"); if ((stream_ids & CIF_ISP10_STREAM_MP) || (stream_ids & CIF_ISP10_STREAM_SP)) { ret = cif_isp10_set_pm_state(dev, CIF_ISP10_PM_STATE_SW_STNDBY); if (IS_ERR_VALUE(ret)) goto err; if (!CIF_ISP10_INP_IS_DMA(dev->config.input_sel)) { /* configure sensor */ ret = cif_isp10_config_img_src(dev); if (IS_ERR_VALUE(ret)) goto err; } cif_id = cif_ioread32(dev->config.base_addr + CIF_VI_ID); dev->config.out_of_buffer_stall = CIF_ISP10_ALWAYS_STALL_ON_NO_BUFS ? 1 : 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "CIF_ID 0x%08x\n", cif_id); cif_isp10_pltfrm_reset(dev); cif_isp10_config_clk(dev); cifisp_frame_id_reset(&dev->isp_dev); dev->isp_dev.frame_id_setexp = 0; /* Decide when to switch to asynchronous mode */ /* * TODO: remove dev->isp_dev.ycflt_en check for * HW with the scaler fix. */ dev->config.mi_config.async_updt = CIF_ISP10_ALWAYS_ASYNC; if (CIF_ISP10_INP_IS_DMA(dev->config.input_sel)) { dev->config.mi_config.async_updt |= CIF_ISP10_ASYNC_DMA; ret = cif_isp10_config_mi_dma(dev); if (IS_ERR_VALUE(ret)) goto err; } if ((stream_ids & CIF_ISP10_STREAM_MP) && (dev->config.jpeg_config.enable)) dev->config.mi_config.async_updt |= CIF_ISP10_ASYNC_JPEG; if (dev->config.isp_config.ism_config.ism_en) dev->config.mi_config.async_updt |= CIF_ISP10_ASYNC_ISM; if (PLTFRM_CAM_ITF_IS_MIPI(dev->config.cam_itf.type)) { ret = cif_isp10_config_mipi(dev); if (IS_ERR_VALUE(ret)) goto err; } ret = cif_isp10_config_isp(dev); if (IS_ERR_VALUE(ret)) goto err; cif_isp10_config_ism(dev, true); dev->config.isp_config.ism_config.ism_update_needed = false; if (stream_ids & CIF_ISP10_STREAM_SP) dev->config.sp_config.rsz_config.ism_adjust = true; if (stream_ids & CIF_ISP10_STREAM_MP) dev->config.mp_config.rsz_config.ism_adjust = true; if (stream_ids & CIF_ISP10_STREAM_SP) { ret = cif_isp10_config_sp(dev); if (IS_ERR_VALUE(ret)) goto err; } if (stream_ids & CIF_ISP10_STREAM_MP) { ret = cif_isp10_config_mp(dev); if (IS_ERR_VALUE(ret)) goto err; } ret = cif_isp10_config_path(dev, stream_ids); if (IS_ERR_VALUE(ret)) goto err; } /* Turn off XNR vertical subsampling when ism cropping is enabled */ if (dev->config.isp_config.ism_config.ism_en) { if (!dev->isp_dev.cif_ism_cropping) dev->isp_dev.cif_ism_cropping = true; } else { if (dev->isp_dev.cif_ism_cropping) dev->isp_dev.cif_ism_cropping = false; } if (dev->config.sp_config.rsz_config.ycflt_adjust || dev->config.sp_config.rsz_config.ism_adjust) { if (dev->sp_stream.state == CIF_ISP10_STATE_READY) { ret = cif_isp10_config_rsz(dev, CIF_ISP10_STREAM_SP, true); if (IS_ERR_VALUE(ret)) goto err; } else { /* Disable SRSZ if SP is not used */ cif_iowrite32(0, dev->config.base_addr + CIF_SRSZ_CTRL); cif_iowrite32OR(CIF_RSZ_CTRL_CFG_UPD, dev->config.base_addr + CIF_SRSZ_CTRL); dev->config.sp_config.rsz_config.ycflt_adjust = false; dev->config.sp_config.rsz_config.ism_adjust = false; } } if (dev->config.mp_config.rsz_config.ycflt_adjust || dev->config.mp_config.rsz_config.ism_adjust) { if (dev->mp_stream.state == CIF_ISP10_STATE_READY) { ret = cif_isp10_config_rsz(dev, CIF_ISP10_STREAM_MP, true); if (IS_ERR_VALUE(ret)) goto err; } else { /* Disable MRSZ if MP is not used */ cif_iowrite32(0, dev->config.base_addr + CIF_MRSZ_CTRL); cif_iowrite32OR(CIF_RSZ_CTRL_CFG_UPD, dev->config.base_addr + CIF_MRSZ_CTRL); dev->config.mp_config.rsz_config.ycflt_adjust = false; dev->config.mp_config.rsz_config.ism_adjust = false; } } if (dev->config.mi_config.async_updt) cif_isp10_pltfrm_pr_dbg(dev->dev, "CIF in asynchronous mode (0x%08x)\n", dev->config.mi_config.async_updt); return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static void cif_isp10_init_stream( struct cif_isp10_device *dev, enum cif_isp10_stream_id stream_id) { int ret = 0; struct cif_isp10_stream *stream = NULL; struct cif_isp10_frm_fmt frm_fmt; switch (stream_id) { case CIF_ISP10_STREAM_SP: stream = &dev->sp_stream; stream->state = CIF_ISP10_STATE_READY; dev->config.sp_config.rsz_config.ycflt_adjust = false; dev->config.sp_config.rsz_config.ism_adjust = false; dev->config.mi_config.sp.busy = false; dev->config.mi_config.sp.burst_len = CIF_MI_CTRL_BURST_LEN_LUM_16 | CIF_MI_CTRL_BURST_LEN_CHROM_16; dev->sp_stream.updt_cfg = true; ret = cif_isp10_img_src_select_strm_fmt(dev); if (IS_ERR_VALUE(ret)) { dev->sp_stream.updt_cfg = false; } else { frm_fmt.pix_fmt = CIF_YUV420SP; frm_fmt.width = dev->config.img_src_output.frm_fmt.width; frm_fmt.height = dev->config.img_src_output.frm_fmt.height; frm_fmt.stride = 0; frm_fmt.quantization = 0; // init default formats dev->config.mi_config.sp.output = frm_fmt; dev->config.mi_config.sp.llength = cif_isp10_calc_llength( frm_fmt.width, frm_fmt.stride, frm_fmt.pix_fmt); } break; case CIF_ISP10_STREAM_MP: stream = &dev->mp_stream; stream->state = CIF_ISP10_STATE_READY; dev->config.jpeg_config.ratio = 50; dev->config.jpeg_config.header = CIF_ISP10_JPEG_HEADER_JFIF; dev->config.jpeg_config.enable = false; dev->config.mi_config.raw_enable = false; dev->config.mp_config.rsz_config.ycflt_adjust = false; dev->config.mp_config.rsz_config.ism_adjust = false; dev->config.mi_config.mp.busy = false; dev->config.mi_config.mp.burst_len = CIF_MI_CTRL_BURST_LEN_LUM_16 | CIF_MI_CTRL_BURST_LEN_CHROM_16; dev->mp_stream.updt_cfg = true; ret = cif_isp10_img_src_select_strm_fmt(dev); if (IS_ERR_VALUE(ret)) { dev->mp_stream.updt_cfg = false; } else { frm_fmt.pix_fmt = CIF_YUV420SP; frm_fmt.width = dev->config.img_src_output.frm_fmt.width; frm_fmt.height = dev->config.img_src_output.frm_fmt.height; frm_fmt.stride = 0; frm_fmt.quantization = 0; // init default formats dev->config.mi_config.mp.output = frm_fmt; dev->config.mi_config.mp.output.stride = frm_fmt.stride; dev->config.mi_config.mp.llength = cif_isp10_calc_llength( frm_fmt.width, frm_fmt.stride, frm_fmt.pix_fmt); } break; case CIF_ISP10_STREAM_DMA: stream = &dev->dma_stream; dev->config.mi_config.dma.busy = false; dev->dma_stream.updt_cfg = false; break; default: cif_isp10_pltfrm_pr_err(NULL, "unknown/unsupported stream ID %d\n", stream_id); WARN_ON(1); break; } INIT_LIST_HEAD(&stream->buf_queue); stream->next_buf = NULL; stream->curr_buf = NULL; stream->stop = false; stream->stall = false; spin_lock_init(&stream->metadata.spinlock); cif_isp10_pltfrm_event_clear(dev->dev, &stream->done); if (!stream->updt_cfg) stream->state = CIF_ISP10_STATE_INACTIVE; return; } static int cif_isp10_jpeg_gen_header( struct cif_isp10_device *dev) { unsigned int timeout = 10000; cif_isp10_pltfrm_pr_dbg(NULL, "\n"); cif_iowrite32(CIF_JPE_GEN_HEADER_ENABLE, dev->config.base_addr + CIF_JPE_GEN_HEADER); while (timeout--) { if (cif_ioread32(dev->config.base_addr + CIF_JPE_STATUS_RIS) & CIF_JPE_STATUS_GENHEADER_DONE) { cif_isp10_pltfrm_pr_dbg(NULL, "JPEG header generated\n"); cif_iowrite32(CIF_JPE_STATUS_GENHEADER_DONE, dev->config.base_addr + CIF_JPE_STATUS_ICR); break; } } if (!timeout) { cif_isp10_pltfrm_pr_err(NULL, "JPEG header generation timeout\n"); cif_isp10_pltfrm_pr_err(NULL, "failed with error %d\n", -ETIMEDOUT); return -ETIMEDOUT; } #ifdef CIF_ISP10_VERIFY_JPEG_HEADER { u32 *buff = (u32 *)phys_to_virt( dev->config.mi_config.mp.curr_buff_addr); if (buff[0] != 0xe0ffd8ff) cif_isp10_pltfrm_pr_err(NULL, "JPEG HEADER WRONG: 0x%08x\n" "curr_buff_addr 0x%08x\n" "MI_MP_Y_SIZE_SHD 0x%08x\n" "MI_MP_Y_BASE_AD_SHD 0x%08x\n", buff[0], dev->config.mi_config.mp.curr_buff_addr, cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_SIZE_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_SHD)); } #endif return 0; } static void cif_isp10_mi_update_buff_addr( struct cif_isp10_device *dev, enum cif_isp10_stream_id strm_id) { if (strm_id == CIF_ISP10_STREAM_SP) { cif_iowrite32_verify(dev->config.mi_config.sp.next_buff_addr, dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.sp.next_buff_addr + dev->config.mi_config.sp.cb_offs, dev->config.base_addr + CIF_MI_SP_CB_BASE_AD_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.sp.next_buff_addr + dev->config.mi_config.sp.cr_offs, dev->config.base_addr + CIF_MI_SP_CR_BASE_AD_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); /* * There have bee repeatedly issues with * the offset registers, it is safer to write * them each time, even though it is always * 0 and even though that is the * register's default value */ cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_SP_Y_OFFS_CNT_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_SP_CB_OFFS_CNT_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_SP_CR_OFFS_CNT_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MI_SP_Y_BASE_AD 0x%08x/0x%08x\n" " MI_SP_CB_BASE_AD 0x%08x/0x%08x\n" " MI_SP_CR_BASE_AD 0x%08x/0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_BASE_AD_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_BASE_AD_SHD)); } else if (strm_id == CIF_ISP10_STREAM_MP) { cif_iowrite32_verify(dev->config.mi_config.mp.next_buff_addr, dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.mp.next_buff_addr + dev->config.mi_config.mp.cb_offs, dev->config.base_addr + CIF_MI_MP_CB_BASE_AD_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.mp.next_buff_addr + dev->config.mi_config.mp.cr_offs, dev->config.base_addr + CIF_MI_MP_CR_BASE_AD_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); /* * There have bee repeatedly issues with * the offset registers, it is safer to write * them each time, even though it is always * 0 and even though that is the * register's default value */ cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_MP_Y_OFFS_CNT_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_MP_CB_OFFS_CNT_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_MP_CR_OFFS_CNT_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MI_MP_Y_BASE_AD 0x%08x/0x%08x\n" " MI_MP_CB_BASE_AD 0x%08x/0x%08x\n" " MI_MP_CR_BASE_AD 0x%08x/0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CB_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CB_BASE_AD_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CR_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_MP_CR_BASE_AD_SHD)); } else { /* DMA */ cif_iowrite32_verify(dev->config.mi_config.dma.next_buff_addr, dev->config.base_addr + CIF_MI_DMA_Y_PIC_START_AD, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.dma.next_buff_addr + dev->config.mi_config.dma.cb_offs, dev->config.base_addr + CIF_MI_DMA_CB_PIC_START_AD, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.dma.next_buff_addr + dev->config.mi_config.dma.cr_offs, dev->config.base_addr + CIF_MI_DMA_CR_PIC_START_AD, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MI_DMA_Y_PIC_START_AD 0x%08x\n" " MI_DMA_CB_PIC_START_AD 0x%08x\n" " MI_DMA_CR_PIC_START_AD 0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_MI_DMA_Y_PIC_START_AD), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_CB_PIC_START_AD), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_CR_PIC_START_AD)); } } static int cif_isp10_update_mi_mp( struct cif_isp10_device *dev) { int ret = 0; enum cif_isp10_pix_fmt out_pix_fmt = dev->config.mi_config.mp.output.pix_fmt; cif_isp10_pltfrm_pr_dbg(NULL, "curr 0x%08x next 0x%08x\n", dev->config.mi_config.mp.curr_buff_addr, dev->config.mi_config.mp.next_buff_addr); if (dev->config.jpeg_config.enable) { /* * in case of jpeg encoding, we don't have to disable the * MI, because the encoding * anyway has to be started explicitly */ if (!dev->config.jpeg_config.busy) { if ((dev->config.mi_config.mp.curr_buff_addr != dev->config.mi_config.mp.next_buff_addr) && (dev->config.mi_config.mp.curr_buff_addr != CIF_ISP10_INVALID_BUFF_ADDR)) { ret = cif_isp10_jpeg_gen_header(dev); if (IS_ERR_VALUE(ret)) goto err; cif_isp10_pltfrm_pr_dbg(NULL, "Starting JPEG encoding\n"); cif_isp10_pltfrm_rtrace_printf(dev->dev, "Starting JPEG encoding\n"); cif_iowrite32(CIF_JPE_ENCODE_ENABLE, dev->config.base_addr + CIF_JPE_ENCODE); cif_iowrite32(CIF_JPE_INIT_ENABLE, dev->config.base_addr + CIF_JPE_INIT); dev->config.jpeg_config.busy = true; } if (dev->config.mi_config.mp.next_buff_addr != CIF_ISP10_INVALID_BUFF_ADDR) cif_isp10_mi_update_buff_addr(dev, CIF_ISP10_STREAM_MP); dev->config.mi_config.mp.curr_buff_addr = dev->config.mi_config.mp.next_buff_addr; } } else { if (dev->config.mi_config.mp.next_buff_addr != dev->config.mi_config.mp.curr_buff_addr) { if (dev->config.mi_config.mp.next_buff_addr == CIF_ISP10_INVALID_BUFF_ADDR) { /* disable MI MP */ cif_isp10_pltfrm_pr_dbg(NULL, "disabling MP MI\n"); cif_iowrite32AND_verify( ~(CIF_MI_CTRL_MP_ENABLE_IN | CIF_MI_CTRL_JPEG_ENABLE | CIF_MI_CTRL_RAW_ENABLE), dev->config.base_addr + CIF_MI_CTRL, ~0); } else if (dev->config.mi_config.mp.curr_buff_addr == CIF_ISP10_INVALID_BUFF_ADDR) { /* re-enable MI MP */ cif_isp10_pltfrm_pr_dbg(NULL, "enabling MP MI\n"); cif_iowrite32(CIF_MI_MP_FRAME, dev->config.base_addr + CIF_MI_ICR); cif_iowrite32AND_verify( ~(CIF_MI_CTRL_MP_ENABLE_IN | CIF_MI_CTRL_JPEG_ENABLE | CIF_MI_CTRL_RAW_ENABLE), dev->config.base_addr + CIF_MI_CTRL, ~0); if (CIF_ISP10_PIX_FMT_IS_RAW_BAYER (out_pix_fmt)) { cif_iowrite32OR_verify( CIF_MI_CTRL_RAW_ENABLE, dev->config.base_addr + CIF_MI_CTRL, ~0); } else if (CIF_ISP10_PIX_FMT_IS_YUV (out_pix_fmt)) { cif_iowrite32OR_verify( CIF_MI_CTRL_MP_ENABLE_IN, dev->config.base_addr + CIF_MI_CTRL, ~0); } } cif_isp10_mi_update_buff_addr(dev, CIF_ISP10_STREAM_MP); dev->config.mi_config.mp.curr_buff_addr = dev->config.mi_config.mp.next_buff_addr; } } return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with err %d\n", ret); return ret; } static int cif_isp10_update_mi_sp( struct cif_isp10_device *dev) { u32 vir_len_offset = dev->config.mi_config.sp.vir_len_offset; cif_isp10_pltfrm_pr_dbg(NULL, "curr 0x%08x next 0x%08x\n", dev->config.mi_config.sp.curr_buff_addr, dev->config.mi_config.sp.next_buff_addr); if (dev->config.mi_config.sp.next_buff_addr != dev->config.mi_config.sp.curr_buff_addr) { if (dev->config.mi_config.sp.next_buff_addr == CIF_ISP10_INVALID_BUFF_ADDR) { /* disable MI SP */ cif_isp10_pltfrm_pr_dbg(NULL, "disabling SP MI\n"); /* 'switch off' MI interface */ cif_iowrite32AND_verify(~CIF_MI_CTRL_SP_ENABLE, dev->config.base_addr + CIF_MI_CTRL, ~0); } else if (dev->config.mi_config.sp.curr_buff_addr == CIF_ISP10_INVALID_BUFF_ADDR) { /* re-enable MI SP */ cif_isp10_pltfrm_pr_dbg(NULL, "enabling SP MI\n"); cif_iowrite32(CIF_MI_SP_FRAME, dev->config.base_addr + CIF_MI_ICR); cif_iowrite32OR_verify(CIF_MI_CTRL_SP_ENABLE, dev->config.base_addr + CIF_MI_CTRL, ~0); } cif_isp10_mi_update_buff_addr(dev, CIF_ISP10_STREAM_SP); dev->config.mi_config.sp.curr_buff_addr = dev->config.mi_config.sp.next_buff_addr; } else if (PLTFRM_CAM_ITF_INTERLACE(dev->config.cam_itf.type) && dev->config.mi_config.sp.field_flag == FIELD_FLAGS_ODD) { cif_iowrite32_verify(dev->config.mi_config.sp.next_buff_addr + vir_len_offset, dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.sp.next_buff_addr + vir_len_offset + dev->config.mi_config.sp.cb_offs, dev->config.base_addr + CIF_MI_SP_CB_BASE_AD_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(dev->config.mi_config.sp.next_buff_addr + vir_len_offset + dev->config.mi_config.sp.cr_offs, dev->config.base_addr + CIF_MI_SP_CR_BASE_AD_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); /* * There have bee repeatedly issues with * the offset registers, it is safer to write * them each time, even though it is always * 0 and even though that is the * register's default value */ cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_SP_Y_OFFS_CNT_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_SP_CB_OFFS_CNT_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_SP_CR_OFFS_CNT_INIT, CIF_MI_ADDR_SIZE_ALIGN_MASK); cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MI_SP_Y_BASE_AD 0x%08x/0x%08x\n" " MI_SP_CB_BASE_AD 0x%08x/0x%08x\n" " MI_SP_CR_BASE_AD 0x%08x/0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CB_BASE_AD_SHD), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_BASE_AD_INIT), cif_ioread32(dev->config.base_addr + CIF_MI_SP_CR_BASE_AD_SHD)); } return 0; } static int cif_isp10_s_fmt_mp( struct cif_isp10_device *dev, struct cif_isp10_strm_fmt *strm_fmt, u32 stride) { int ret = 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "%s %dx%d@%d/%dfps, stride = %d, quantization: %d\n", cif_isp10_pix_fmt_string(strm_fmt->frm_fmt.pix_fmt), strm_fmt->frm_fmt.width, strm_fmt->frm_fmt.height, strm_fmt->frm_intrvl.numerator, strm_fmt->frm_intrvl.denominator, stride, strm_fmt->frm_fmt.quantization); /* TBD: check whether format is a valid format for MP */ if (strm_fmt->frm_fmt.pix_fmt == CIF_YUV400) { cif_isp10_pltfrm_pr_err(dev->dev, "format %s %dx%d@%d/%dfps, stride = %d not supported on MP\n", cif_isp10_pix_fmt_string(strm_fmt->frm_fmt.pix_fmt), strm_fmt->frm_fmt.width, strm_fmt->frm_fmt.height, strm_fmt->frm_intrvl.numerator, strm_fmt->frm_intrvl.denominator, stride); ret = -EINVAL; goto err; } if (PLTFRM_CAM_ITF_INTERLACE(dev->config.cam_itf.type)) { ret = -EINVAL; cif_isp10_pltfrm_pr_err(dev->dev, "MP doesn't support the interlace format!\n"); goto err; } if (CIF_ISP10_PIX_FMT_IS_JPEG(strm_fmt->frm_fmt.pix_fmt)) { dev->config.jpeg_config.enable = true; } else if (CIF_ISP10_PIX_FMT_IS_RAW_BAYER (strm_fmt->frm_fmt.pix_fmt)) { if ((dev->sp_stream.state == CIF_ISP10_STATE_READY) || (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING)) cif_isp10_pltfrm_pr_warn(dev->dev, "cannot output RAW data when SP is active, you will not be able to (re-)start streaming\n"); dev->config.mi_config.raw_enable = true; } dev->config.mi_config.mp.output = strm_fmt->frm_fmt; dev->config.mi_config.mp.output.stride = stride; dev->config.mi_config.mp.llength = cif_isp10_calc_llength( strm_fmt->frm_fmt.width, stride, strm_fmt->frm_fmt.pix_fmt); cif_isp10_pltfrm_pr_dbg(dev->dev, "mp llength=0x%x\n", dev->config.mi_config.mp.llength); dev->mp_stream.updt_cfg = true; dev->mp_stream.state = CIF_ISP10_STATE_READY; if (!CIF_ISP10_INP_IS_DMA(dev->config.input_sel)) { ret = cif_isp10_img_src_select_strm_fmt(dev); if (IS_ERR_VALUE(ret)) { dev->mp_stream.updt_cfg = false; dev->mp_stream.state = CIF_ISP10_STATE_INACTIVE; goto err; } } return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static int cif_isp10_s_fmt_sp( struct cif_isp10_device *dev, struct cif_isp10_strm_fmt *strm_fmt, u32 stride) { int ret = 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "%s %dx%d@%d/%dfps, stride = %d, quantization: %d\n", cif_isp10_pix_fmt_string(strm_fmt->frm_fmt.pix_fmt), strm_fmt->frm_fmt.width, strm_fmt->frm_fmt.height, strm_fmt->frm_intrvl.numerator, strm_fmt->frm_intrvl.denominator, stride, strm_fmt->frm_fmt.quantization); if (dev->config.mi_config.raw_enable) cif_isp10_pltfrm_pr_warn(dev->dev, "cannot activate SP when MP is set to RAW data output, you will not be able to (re-)start streaming\n"); /* TBD: more detailed check whether format is a valid format for SP */ /* TBD: remove the mode stuff */ if (!CIF_ISP10_PIX_FMT_IS_YUV(strm_fmt->frm_fmt.pix_fmt) && !CIF_ISP10_PIX_FMT_IS_RGB(strm_fmt->frm_fmt.pix_fmt)) { cif_isp10_pltfrm_pr_err(dev->dev, "format %s %dx%d@%d/%dfps, stride = %d not supported on SP\n", cif_isp10_pix_fmt_string(strm_fmt->frm_fmt.pix_fmt), strm_fmt->frm_fmt.width, strm_fmt->frm_fmt.height, strm_fmt->frm_intrvl.numerator, strm_fmt->frm_intrvl.denominator, stride); ret = -EINVAL; goto err; } dev->config.mi_config.sp.output = strm_fmt->frm_fmt; dev->config.mi_config.sp.llength = cif_isp10_calc_llength( strm_fmt->frm_fmt.width, stride, strm_fmt->frm_fmt.pix_fmt); dev->sp_stream.updt_cfg = true; dev->sp_stream.state = CIF_ISP10_STATE_READY; if (!CIF_ISP10_INP_IS_DMA(dev->config.input_sel)) { ret = cif_isp10_img_src_select_strm_fmt(dev); if (IS_ERR_VALUE(ret)) { dev->sp_stream.updt_cfg = false; dev->sp_stream.state = CIF_ISP10_STATE_INACTIVE; goto err; } } return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static int cif_isp10_s_fmt_dma( struct cif_isp10_device *dev, struct cif_isp10_strm_fmt *strm_fmt, u32 stride) { int ret = 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "%s %dx%d@%d/%dfps, stride = %d\n", cif_isp10_pix_fmt_string(strm_fmt->frm_fmt.pix_fmt), strm_fmt->frm_fmt.width, strm_fmt->frm_fmt.height, strm_fmt->frm_intrvl.numerator, strm_fmt->frm_intrvl.denominator, stride); if (!CIF_ISP10_PIX_FMT_IS_YUV(strm_fmt->frm_fmt.pix_fmt) && !CIF_ISP10_PIX_FMT_IS_RAW_BAYER(strm_fmt->frm_fmt.pix_fmt)) { cif_isp10_pltfrm_pr_err(dev->dev, "format %s %dx%d@%d/%dfps, stride = %d not supported for DMA\n", cif_isp10_pix_fmt_string(strm_fmt->frm_fmt.pix_fmt), strm_fmt->frm_fmt.width, strm_fmt->frm_fmt.height, strm_fmt->frm_intrvl.numerator, strm_fmt->frm_intrvl.denominator, stride); ret = -EINVAL; goto err; } dev->config.mi_config.dma.output = strm_fmt->frm_fmt; dev->config.mi_config.dma.llength = cif_isp10_calc_llength( strm_fmt->frm_fmt.width, stride, strm_fmt->frm_fmt.pix_fmt); dev->dma_stream.updt_cfg = true; dev->dma_stream.state = CIF_ISP10_STATE_READY; return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } static void cif_isp10_dma_next_buff( struct cif_isp10_device *dev) { #ifdef CIF_ISP10_MODE_DMA_SG struct sg_table *sgt = NULL; #endif cif_isp10_pltfrm_pr_dbg(NULL, "\n"); if (!list_empty(&dev->dma_stream.buf_queue) && !dev->dma_stream.stop) { if (dev->dma_stream.curr_buf) WARN_ON(1); dev->dma_stream.curr_buf = list_first_entry(&dev->dma_stream.buf_queue, struct cif_isp10_buffer, queue); list_del(&dev->dma_stream.curr_buf->queue); #ifdef CIF_ISP10_MODE_DMA_CONTIG dev->config.mi_config.dma.next_buff_addr = vb2_dma_contig_plane_dma_addr( &dev->dma_stream.curr_buf->vb.vb2_buf, 0); #endif #ifdef CIF_ISP10_MODE_DMA_SG sgt = vb2_dma_sg_plane_desc( &dev->dma_stream.curr_buf->vb.vb2_buf, 0); dev->config.mi_config.dma.next_buff_addr = sg_dma_address(sgt->sgl); #endif cif_isp10_mi_update_buff_addr(dev, CIF_ISP10_STREAM_DMA); dev->config.mi_config.dma.busy = true; if ((dev->sp_stream.state == CIF_ISP10_STATE_STREAMING) && dev->sp_stream.curr_buf) dev->config.mi_config.sp.busy = true; if ((dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) && dev->mp_stream.curr_buf) dev->config.mi_config.mp.busy = true; /* workaround for write register failure bug */ do { cif_iowrite32(CIF_MI_DMA_START_ENABLE, dev->config.base_addr + CIF_MI_DMA_START); udelay(1); } while (!cif_ioread32( dev->config.base_addr + CIF_MI_DMA_STATUS)); } cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MI_DMA_CTRL 0x%08x\n" " MI_DMA_STATUS 0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_MI_DMA_CTRL), cif_ioread32(dev->config.base_addr + CIF_MI_DMA_STATUS)); } static void cif_isp10_dma_ready( struct cif_isp10_device *dev) { unsigned int mi_mis_tmp; cif_isp10_pltfrm_pr_dbg(NULL, "\n"); cif_iowrite32(CIF_MI_DMA_READY, dev->config.base_addr + CIF_MI_ICR); mi_mis_tmp = cif_ioread32(dev->config.base_addr + CIF_MI_MIS); if (mi_mis_tmp & CIF_MI_DMA_READY) cif_isp10_pltfrm_pr_err(dev->dev, "dma icr err: 0x%x\n", mi_mis_tmp); wake_up(&dev->dma_stream.curr_buf->vb.vb2_buf.vb2_queue->done_wq); dev->dma_stream.curr_buf = NULL; dev->config.mi_config.dma.busy = false; cif_isp10_pltfrm_event_signal(dev->dev, &dev->dma_stream.done); } static int cif_isp10_mi_frame_end( struct cif_isp10_device *dev, enum cif_isp10_stream_id stream_id) { struct cif_isp10_stream *stream = NULL; u32 cur_buff_addr = -1; u32 *next_buff_addr = NULL; struct vb2_buffer *vb2_buf; dma_addr_t tmp_addr; #ifdef CIF_ISP10_MODE_DMA_SG struct sg_table *sgt = NULL; struct scatterlist *sg; dma_addr_t tmp_phy_addr; int i; #endif CIF_ISP10_PLTFRM_MEM_IO_ADDR y_base_addr; int (*update_mi)( struct cif_isp10_device *dev); struct cif_isp10_isp_readout_work *work; cif_isp10_pltfrm_pr_dbg(NULL, "%s\n", cif_isp10_stream_id_string(stream_id)); if (stream_id == CIF_ISP10_STREAM_MP) { stream = &dev->mp_stream; y_base_addr = dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_SHD; next_buff_addr = &dev->config.mi_config.mp.next_buff_addr; update_mi = cif_isp10_update_mi_mp; if (dev->config.jpeg_config.enable) { unsigned int jpe_status = cif_ioread32(dev->config.base_addr + CIF_JPE_STATUS_RIS); if (jpe_status & CIF_JPE_STATUS_ENCODE_DONE) { cif_iowrite32(CIF_JPE_STATUS_ENCODE_DONE, dev->config.base_addr + CIF_JPE_STATUS_ICR); if (stream->curr_buf) { stream->curr_buf->size = cif_ioread32(dev->config.base_addr + CIF_MI_BYTE_CNT); cif_isp10_pltfrm_pr_dbg(NULL, "JPEG encoding done, size %lu\n", stream->curr_buf->size); if (cif_ioread32(dev->config.base_addr + CIF_MI_RIS) & CIF_MI_WRAP_MP_Y) cif_isp10_pltfrm_pr_err(NULL, "buffer wrap around detected, JPEG presumably corrupted (%d/%d/%lu)\n", dev->config.mi_config. mp.y_size, cif_ioread32( dev->config.base_addr + CIF_MI_MP_Y_SIZE_SHD), stream->curr_buf->size); } } } } else if (stream_id == CIF_ISP10_STREAM_SP) { stream = &dev->sp_stream; y_base_addr = dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_SHD; next_buff_addr = &dev->config.mi_config.sp.next_buff_addr; update_mi = cif_isp10_update_mi_sp; } else { WARN_ON(1); } cif_isp10_pltfrm_pr_dbg(dev->dev, "%s Y_BASE_AD_INIT/Y_BASE_AD_SHD (0x%08x/0x%08x)\n", cif_isp10_stream_id_string(stream_id), (stream_id & CIF_ISP10_STREAM_MP) ? cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_INIT) : cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_INIT), cif_ioread32(y_base_addr)); if ((!stream->next_buf) && !(dev->config.jpeg_config.enable && (stream_id == CIF_ISP10_STREAM_MP))) { stream->stall = dev->config.out_of_buffer_stall; } else if ((stream->next_buf)) { #ifdef CIF_ISP10_MODE_DMA_CONTIG tmp_addr = vb2_dma_contig_plane_dma_addr( &stream->next_buf->vb.vb2_buf, 0); #endif #ifdef CIF_ISP10_MODE_DMA_SG sgt = vb2_dma_sg_plane_desc( &stream->next_buf->vb.vb2_buf, 0); tmp_addr = sg_dma_address(sgt->sgl); #endif if (tmp_addr != cif_ioread32(y_base_addr) && (!PLTFRM_CAM_ITF_INTERLACE(dev->config.cam_itf.type) || dev->config.mi_config.sp.field_flag == FIELD_FLAGS_ODD)) { cif_isp10_pltfrm_pr_warn(dev->dev, "%s buffer queue is not advancing (0x%08x/0x%08x)\n", cif_isp10_stream_id_string(stream_id), (stream_id & CIF_ISP10_STREAM_MP) ? cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_INIT) : cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_INIT), cif_ioread32(y_base_addr)); stream->stall = true; } } if (!stream->stall) { /* * If mi restart after switch off for buffer is empty, * mi may be restart failed. So mi write data to last * buffer, the last buffer isn't been release to user * until new buffer queue; */ if ((stream->curr_buf) && (stream->next_buf) && (!PLTFRM_CAM_ITF_INTERLACE(dev->config.cam_itf.type) || dev->config.mi_config.sp.field_flag == FIELD_FLAGS_EVEN)) { bool wake_now; vb2_buf = &stream->curr_buf->vb.vb2_buf; v4l2_get_timestamp(&stream->curr_buf->vb.timestamp); vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE); wake_now = false; spin_lock(&stream->metadata.spinlock); if (stream->metadata.d && dev->isp_dev.streamon) { struct v4l2_buffer_metadata_s *metadata; unsigned int meta_read_id; metadata = (struct v4l2_buffer_metadata_s *) (stream->metadata.d + stream->curr_buf->vb.vb2_buf.index * CAMERA_METADATA_LEN); meta_read_id = dev->isp_dev.meta_info.read_id; metadata->frame_id = dev->isp_dev.meta_info.frame_id[meta_read_id]; metadata->frame_t.vs_t = dev->isp_dev.meta_info.vs_t[meta_read_id]; metadata->frame_t.fi_t = dev->isp_dev.meta_info.fi_t[meta_read_id]; dev->isp_dev.meta_info.read_cnt++; if (dev->isp_dev.meta_info.read_cnt >= dev->isp_dev.meta_info.read_max) { dev->isp_dev.meta_info.read_cnt = 0; dev->isp_dev.meta_info.read_id = (meta_read_id + 1) % CIF_ISP10_META_INFO_NUM; } spin_unlock(&stream->metadata.spinlock); work = (struct cif_isp10_isp_readout_work *) kmalloc( sizeof( struct cif_isp10_isp_readout_work), GFP_ATOMIC); if (work) { INIT_WORK((struct work_struct *)work, cifisp_isp_readout_work); work->readout = CIF_ISP10_ISP_READOUT_META; work->isp_dev = &dev->isp_dev; work->frame_id = metadata->frame_id; work->vb = &stream->curr_buf->vb.vb2_buf; work->stream_id = stream->id; work->isp_metadata = (struct cifisp_isp_metadata *)metadata->isp; if (!queue_work(dev->isp_dev.readout_wq, (struct work_struct *)work)) { cif_isp10_pltfrm_pr_err( dev->dev, "Could not schedule work\n"); wake_now = true; kfree((void *)work); } } else { cif_isp10_pltfrm_pr_err(dev->dev, "Could not allocate work\n"); wake_now = true; } } else { spin_unlock(&stream->metadata.spinlock); wake_now = true; } if (wake_now) { cif_isp10_pltfrm_pr_dbg(NULL, "frame done\n"); vb2_buf = &stream->curr_buf->vb.vb2_buf; wake_up(&vb2_buf->vb2_queue->done_wq); } stream->curr_buf = NULL; } if (!stream->curr_buf) { stream->curr_buf = stream->next_buf; stream->next_buf = NULL; } } if (!stream->next_buf) { /* * in case of jpeg encoding, we are only programming * a new buffer, if the jpeg header was generated, because * we need the curent buffer for the jpeg encoding * in the current frame period */ if (!list_empty(&stream->buf_queue)) { stream->next_buf = list_first_entry(&stream->buf_queue, struct cif_isp10_buffer, queue); list_del(&stream->next_buf->queue); #ifdef CIF_ISP10_MODE_DMA_CONTIG *next_buff_addr = vb2_dma_contig_plane_dma_addr( &stream->next_buf->vb.vb2_buf, 0); #endif #ifdef CIF_ISP10_MODE_DMA_SG sgt = vb2_dma_sg_plane_desc( &stream->next_buf->vb.vb2_buf, 0); *next_buff_addr = sg_dma_address(sgt->sgl); #endif } else if ( (!dev->config.out_of_buffer_stall || (dev->config.jpeg_config.enable && (stream_id == CIF_ISP10_STREAM_MP))) && (stream->curr_buf)) { /* * If mi restart after switch off for buffer is empty, * mi may be restart failed. So mi write data to last * buffer, the last buffer isn't been release to user * until new buffer queue; * * if * *next_buff_addr = CIF_ISP10_INVALID_BUFF_ADDR; * mi will stop; */ vb2_buf = &stream->curr_buf->vb.vb2_buf; #ifdef CIF_ISP10_MODE_DMA_CONTIG *next_buff_addr = vb2_dma_contig_plane_dma_addr(vb2_buf, 0); #endif #ifdef CIF_ISP10_MODE_DMA_SG sgt = vb2_dma_sg_plane_desc(vb2_buf, 0); *next_buff_addr = sg_dma_address(sgt->sgl); #endif } } (void)update_mi(dev); stream->stall = false; if (stream->curr_buf) { // TODO::current vb2 buffer is null if (!stream->curr_buf->vb.vb2_buf.planes[0].mem_priv) { cif_isp10_pltfrm_pr_err(dev->dev, "error isp buffer is null\n"); return 0; } #ifdef CIF_ISP10_MODE_DMA_CONTIG cur_buff_addr = vb2_dma_contig_plane_dma_addr (&stream->curr_buf->vb.vb2_buf, 0); #endif #ifdef CIF_ISP10_MODE_DMA_SG sgt = vb2_dma_sg_plane_desc(&stream->curr_buf->vb.vb2_buf, 0); cur_buff_addr = sg_dma_address(sgt->sgl); cif_isp10_pltfrm_pr_dbg(dev->dev, "current buffer scatter list: "); for_each_sg(sgt->sgl, sg, sgt->nents, i) { tmp_addr = sg_dma_address(sg); tmp_phy_addr = sg_phys(sg); cif_isp10_pltfrm_pr_dbg(dev->dev, "dma_addr: %pad tmp_phy: %pad length: %.8x\n", &tmp_addr, &tmp_phy_addr, (int)sg_dma_len(sg)); } #endif } cif_isp10_pltfrm_pr_dbg(dev->dev, "%s curr_buff: %d, 0x%08x next_buf: %d, 0x%x\n", cif_isp10_stream_id_string(stream_id), (stream->curr_buf) ? stream->curr_buf->vb.vb2_buf.index : -1, cur_buff_addr, (stream->next_buf) ? stream->next_buf->vb.vb2_buf.index : -1, *next_buff_addr); return 0; } static int cif_isp10_update_mi_immediate( struct cif_isp10_device *dev, enum cif_isp10_stream_id stream_id) { struct cif_isp10_stream *stream; u32 *next_buff_addr; CIF_ISP10_PLTFRM_MEM_IO_ADDR y_base_addr; #ifdef CIF_ISP10_MODE_DMA_SG struct sg_table *sgt = NULL; #endif int (*update_mi)( struct cif_isp10_device *dev); if (stream_id == CIF_ISP10_STREAM_MP) { stream = &dev->mp_stream; y_base_addr = dev->config.base_addr + CIF_MI_MP_Y_BASE_AD_SHD; next_buff_addr = &dev->config.mi_config.mp.next_buff_addr; update_mi = cif_isp10_update_mi_mp; } else if (stream_id == CIF_ISP10_STREAM_SP) { stream = &dev->sp_stream; y_base_addr = dev->config.base_addr + CIF_MI_SP_Y_BASE_AD_SHD; next_buff_addr = &dev->config.mi_config.sp.next_buff_addr; update_mi = cif_isp10_update_mi_sp; } else { BUG(); } if (stream->state != CIF_ISP10_STATE_STREAMING) return 0; if (stream->next_buf) return 0; if (!list_empty(&stream->buf_queue)) { stream->next_buf = list_first_entry(&stream->buf_queue, struct cif_isp10_buffer, queue); list_del(&stream->next_buf->queue); #ifdef CIF_ISP10_MODE_DMA_CONTIG *next_buff_addr = vb2_dma_contig_plane_dma_addr( &stream->next_buf->vb.vb2_buf, 0); #endif #ifdef CIF_ISP10_MODE_DMA_SG sgt = vb2_dma_sg_plane_desc( &stream->next_buf->vb.vb2_buf, 0); *next_buff_addr = sg_dma_address(sgt->sgl); #endif } update_mi(dev); cif_isp10_pltfrm_pr_dbg(dev->dev, "%s curr_buff: %d, next_buf: %d, 0x%x\n", cif_isp10_stream_id_string(stream_id), (stream->curr_buf) ? stream->curr_buf->vb.vb2_buf.index : -1, (stream->next_buf) ? stream->next_buf->vb.vb2_buf.index : -1, *next_buff_addr); return 0; } static void cif_isp10_stream_metadata_reset( struct cif_isp10_stream *stream_dev ) { unsigned int i; unsigned long flags = 0; struct v4l2_buffer_metadata_s *metadata; struct cifisp_isp_metadata *isp_metadata; spin_lock_irqsave(&stream_dev->metadata.spinlock, flags); if (stream_dev->metadata.d) { for (i = 0; i < stream_dev->metadata.cnt; i++) { metadata = (struct v4l2_buffer_metadata_s *) (stream_dev->metadata.d + i * CAMERA_METADATA_LEN); isp_metadata = (struct cifisp_isp_metadata *) metadata->isp; isp_metadata->other_cfg.s_frame_id = 0xffffffff; isp_metadata->meas_cfg.s_frame_id = 0xffffffff; } } spin_unlock_irqrestore(&stream_dev->metadata.spinlock, flags); } static void cif_isp10_start_mi( struct cif_isp10_device *dev, bool start_mi_sp, bool start_mi_mp) { cif_isp10_pltfrm_pr_dbg(dev->dev, "\n"); if (start_mi_sp && (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING)) start_mi_sp = false; if (start_mi_mp && (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING)) start_mi_mp = false; if (!start_mi_sp && !start_mi_mp) return; if ((start_mi_sp && (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING)) || (start_mi_mp && (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING))) WARN_ON(1); if (start_mi_sp) { cif_isp10_stream_metadata_reset(&dev->sp_stream); dev->config.mi_config.sp.next_buff_addr = CIF_ISP10_INVALID_BUFF_ADDR; dev->config.mi_config.sp.curr_buff_addr = CIF_ISP10_INVALID_BUFF_ADDR; spin_lock(&dev->vbq_lock); cif_isp10_mi_frame_end(dev, CIF_ISP10_STREAM_SP); spin_unlock(&dev->vbq_lock); dev->sp_stream.stall = false; } if (start_mi_mp) { cif_isp10_stream_metadata_reset(&dev->mp_stream); dev->config.mi_config.mp.next_buff_addr = CIF_ISP10_INVALID_BUFF_ADDR; dev->config.mi_config.mp.curr_buff_addr = CIF_ISP10_INVALID_BUFF_ADDR; spin_lock(&dev->vbq_lock); cif_isp10_mi_frame_end(dev, CIF_ISP10_STREAM_MP); spin_unlock(&dev->vbq_lock); dev->mp_stream.stall = false; } cif_iowrite32OR(CIF_MI_INIT_SOFT_UPD, dev->config.base_addr + CIF_MI_INIT); cif_isp10_pltfrm_pr_dbg(NULL, "CIF_MI_INIT_SOFT_UPD\n"); if (start_mi_sp) { spin_lock(&dev->vbq_lock); cif_isp10_mi_frame_end(dev, CIF_ISP10_STREAM_SP); spin_unlock(&dev->vbq_lock); if (dev->sp_stream.curr_buf && (!CIF_ISP10_INP_IS_DMA(dev->config.input_sel))) dev->config.mi_config.sp.busy = true; } if (start_mi_mp) { spin_lock(&dev->vbq_lock); cif_isp10_mi_frame_end(dev, CIF_ISP10_STREAM_MP); spin_unlock(&dev->vbq_lock); if (dev->mp_stream.curr_buf && (!CIF_ISP10_INP_IS_DMA(dev->config.input_sel))) dev->config.mi_config.mp.busy = true; } if (!dev->config.mi_config.async_updt) cif_iowrite32OR(CIF_ISP_CTRL_ISP_GEN_CFG_UPD, dev->config.base_addr + CIF_ISP_CTRL); } static void cif_isp10_stop_mi( struct cif_isp10_device *dev, bool stop_mi_sp, bool stop_mi_mp) { cif_isp10_pltfrm_pr_dbg(dev->dev, "\n"); if (stop_mi_sp && (dev->sp_stream.state != CIF_ISP10_STATE_STREAMING)) stop_mi_sp = false; if (stop_mi_mp && (dev->mp_stream.state != CIF_ISP10_STATE_STREAMING)) stop_mi_mp = false; if (!stop_mi_sp && !stop_mi_mp) return; if (stop_mi_sp && stop_mi_mp) { cif_iowrite32AND_verify(~(CIF_MI_SP_FRAME | CIF_MI_MP_FRAME | CIF_JPE_STATUS_ENCODE_DONE), dev->config.base_addr + CIF_MI_IMSC, ~0); cif_iowrite32(CIF_MI_SP_FRAME | CIF_MI_MP_FRAME | CIF_JPE_STATUS_ENCODE_DONE, dev->config.base_addr + CIF_MI_ICR); cif_iowrite32AND_verify(~CIF_MI_CTRL_SP_ENABLE, dev->config.base_addr + CIF_MI_CTRL, ~0); cif_iowrite32AND_verify(~(CIF_MI_CTRL_MP_ENABLE_IN | CIF_MI_CTRL_SP_ENABLE | CIF_MI_CTRL_JPEG_ENABLE | CIF_MI_CTRL_RAW_ENABLE), dev->config.base_addr + CIF_MI_CTRL, ~0); cif_iowrite32(CIF_MI_INIT_SOFT_UPD, dev->config.base_addr + CIF_MI_INIT); } else if (stop_mi_sp) { cif_iowrite32(CIF_MI_SP_FRAME, dev->config.base_addr + CIF_MI_ICR); cif_iowrite32AND_verify(~CIF_MI_CTRL_SP_ENABLE, dev->config.base_addr + CIF_MI_CTRL, ~0); dev->config.mi_config.sp.burst_len = CIF_MI_CTRL_BURST_LEN_LUM_16 | CIF_MI_CTRL_BURST_LEN_CHROM_16; } else if (stop_mi_mp) { cif_iowrite32(CIF_MI_MP_FRAME | CIF_JPE_STATUS_ENCODE_DONE, dev->config.base_addr + CIF_MI_ICR); cif_iowrite32AND_verify(~(CIF_MI_CTRL_MP_ENABLE_IN | CIF_MI_CTRL_JPEG_ENABLE | CIF_MI_CTRL_RAW_ENABLE), dev->config.base_addr + CIF_MI_CTRL, ~0); dev->config.mi_config.mp.burst_len = CIF_MI_CTRL_BURST_LEN_LUM_16 | CIF_MI_CTRL_BURST_LEN_CHROM_16; } } static void cif_isp10_requeue_bufs( struct cif_isp10_device *dev, struct cif_isp10_stream *stream) { INIT_LIST_HEAD(&stream->buf_queue); stream->next_buf = NULL; stream->curr_buf = NULL; dev->requeue_bufs(dev, stream->id); } static void cif_isp10_stop_sp( struct cif_isp10_device *dev) { int ret; if (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING) { dev->sp_stream.stop = true; ret = cif_isp10_pltfrm_event_wait_timeout(dev->dev, &dev->sp_stream.done, dev->sp_stream.state != CIF_ISP10_STATE_STREAMING, 1000000); dev->sp_stream.stop = false; if (IS_ERR_VALUE(ret)) { cif_isp10_pltfrm_pr_warn(NULL, "waiting on event returned with error %d\n", ret); } if (dev->config.mi_config.sp.busy) cif_isp10_pltfrm_pr_warn(NULL, "SP path still active while stopping it\n"); } } static void cif_isp10_stop_mp( struct cif_isp10_device *dev) { int ret; if (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) { dev->mp_stream.stop = true; ret = cif_isp10_pltfrm_event_wait_timeout(dev->dev, &dev->mp_stream.done, dev->mp_stream.state != CIF_ISP10_STATE_STREAMING, 1000000); dev->mp_stream.stop = false; if (IS_ERR_VALUE(ret)) { cif_isp10_pltfrm_pr_warn(NULL, "waiting on event returned with error %d\n", ret); } if (dev->config.mi_config.mp.busy || dev->config.jpeg_config.busy) cif_isp10_pltfrm_pr_warn(NULL, "MP path still active while stopping it\n"); } } static void cif_isp10_stop_dma( struct cif_isp10_device *dev) { unsigned long flags = 0; cif_isp10_pltfrm_pr_dbg(NULL, "\n"); if (dev->dma_stream.state == CIF_ISP10_STATE_STREAMING) { /* we should not stop during an active DMA transfer */ dev->dma_stream.stop = true; (void)cif_isp10_pltfrm_event_wait_timeout(dev->dev, &dev->dma_stream.done, dev->dma_stream.state != CIF_ISP10_STATE_STREAMING, 50000); /* intentionally NOT checking dma.busy again */ if (dev->config.mi_config.dma.busy) cif_isp10_pltfrm_pr_warn(NULL, "DMA transfer still active while stopping it\n"); dev->dma_stream.state = CIF_ISP10_STATE_READY; spin_lock_irqsave(&dev->vbq_lock, flags); cif_isp10_requeue_bufs(dev, &dev->dma_stream); spin_unlock_irqrestore(&dev->vbq_lock, flags); } } static int cif_isp10_stop( struct cif_isp10_device *dev, bool stop_sp, bool stop_mp) { unsigned long flags = 0; bool stop_all; unsigned long isp_ctrl; cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, img_src state = %s, stop_sp = %d, stop_mp = %d\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_img_src_state_string(dev->img_src_state), stop_sp, stop_mp); if (!((stop_mp && (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING)) || (stop_sp && (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING)))) { return 0; } stop_all = ((stop_mp && stop_sp) || (stop_sp && (dev->mp_stream.state != CIF_ISP10_STATE_STREAMING)) || (stop_mp && (dev->sp_stream.state != CIF_ISP10_STATE_STREAMING))); if (stop_all) { /* * Modify ISP stop sequence for isp bus dead: * ISP(mi) stop in mi frame end -> Stop ISP(mipi) -> * Stop ISP(isp) ->wait for ISP isp off */ cif_isp10_stop_mp(dev); cif_isp10_stop_sp(dev); cif_isp10_stop_dma(dev); /* stop and clear MI, MIPI, and ISP interrupts */ cif_iowrite32(0, dev->config.base_addr + CIF_MIPI_IMSC); cif_iowrite32(~0, dev->config.base_addr + CIF_MIPI_ICR); spin_lock_irqsave(&dev->isp_state_lock, flags); dev->isp_state = CIF_ISP10_STATE_STOPPING; spin_unlock_irqrestore(&dev->isp_state_lock, flags); dev->isp_stop_flags = 0; cif_iowrite32(CIF_ISP_OFF, dev->config.base_addr + CIF_ISP_IMSC); cif_iowrite32(~0, dev->config.base_addr + CIF_ISP_ICR); cif_iowrite32_verify(0, dev->config.base_addr + CIF_MI_IMSC, ~0); cif_iowrite32(~0, dev->config.base_addr + CIF_MI_ICR); cif_iowrite32AND(~CIF_MIPI_CTRL_OUTPUT_ENA, dev->config.base_addr + CIF_MIPI_CTRL); /* stop ISP */ cif_iowrite32AND(~(CIF_ISP_CTRL_ISP_INFORM_ENABLE | CIF_ISP_CTRL_ISP_ENABLE), dev->config.base_addr + CIF_ISP_CTRL); cif_iowrite32OR(CIF_ISP_CTRL_ISP_CFG_UPD, dev->config.base_addr + CIF_ISP_CTRL); wait_event_interruptible_timeout(dev->isp_stop_wait, dev->isp_stop_flags != 0, HZ); isp_ctrl = cif_ioread32(dev->config.base_addr + CIF_ISP_CTRL); if ((isp_ctrl & 0x0001) != 0) { cif_isp10_pltfrm_pr_err(dev->dev, "Stop ISP Failure(0x%lx)!\n", isp_ctrl); } else { spin_lock_irqsave(&dev->isp_state_lock, flags); dev->isp_state = CIF_ISP10_STATE_IDLE; spin_unlock_irqrestore(&dev->isp_state_lock, flags); } cifisp_clr_readout_wq(&dev->isp_dev); if (!CIF_ISP10_INP_IS_DMA(dev->config.input_sel)) { if (IS_ERR_VALUE(cif_isp10_img_src_set_state(dev, CIF_ISP10_IMG_SRC_STATE_SW_STNDBY))) cif_isp10_pltfrm_pr_dbg(dev->dev, "unable to put image source into standby\n"); } if (IS_ERR_VALUE(cif_isp10_set_pm_state(dev, CIF_ISP10_PM_STATE_SW_STNDBY))) cif_isp10_pltfrm_pr_dbg(dev->dev, "unable to put CIF into standby\n"); dev->mp_stream.updt_cfg = true; dev->sp_stream.updt_cfg = true; } else if (stop_sp) { if (!dev->config.mi_config.async_updt) { cif_isp10_stop_mi(dev, true, false); } cif_isp10_stop_sp(dev); cif_iowrite32AND_verify(~CIF_MI_SP_FRAME, dev->config.base_addr + CIF_MI_IMSC, ~0); } else /* stop_mp */ { if (!dev->config.mi_config.async_updt) { cif_isp10_stop_mi(dev, false, true); } cif_isp10_stop_mp(dev); cif_iowrite32AND_verify(~(CIF_MI_MP_FRAME | CIF_JPE_STATUS_ENCODE_DONE), dev->config.base_addr + CIF_MI_IMSC, ~0); } if (stop_mp && (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING)) dev->mp_stream.state = CIF_ISP10_STATE_READY; if (stop_sp && (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING)) dev->sp_stream.state = CIF_ISP10_STATE_READY; spin_lock(&dev->vbq_lock); if (stop_sp) { dev->config.mi_config.sp.busy = false; cif_isp10_requeue_bufs(dev, &dev->sp_stream); } if (stop_mp) { dev->config.mi_config.mp.busy = false; cif_isp10_requeue_bufs(dev, &dev->mp_stream); } spin_unlock(&dev->vbq_lock); cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s, img_src state = %s\n" " MI_CTRL 0x%08x\n" " ISP_CTRL 0x%08x\n" " MIPI_CTRL 0x%08x\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state), cif_isp10_img_src_state_string(dev->img_src_state), cif_ioread32(dev->config.base_addr + CIF_MI_CTRL), cif_ioread32(dev->config.base_addr + CIF_ISP_CTRL), cif_ioread32(dev->config.base_addr + CIF_MIPI_CTRL)); return 0; } static int cif_isp10_start( struct cif_isp10_device *dev, bool start_sp, bool start_mp) { unsigned int ret; struct vb2_buffer *vb, *n; unsigned long flags; unsigned int i; cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s, img_src state = %s, start_sp = %d, start_mp = %d\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state), cif_isp10_img_src_state_string(dev->img_src_state), start_sp, start_mp); if (!((start_mp && (dev->mp_stream.state != CIF_ISP10_STATE_STREAMING)) || (start_sp && (dev->sp_stream.state != CIF_ISP10_STATE_STREAMING)))) return 0; if (CIF_ISP10_INP_IS_DMA(dev->config.input_sel) && (dev->dma_stream.state < CIF_ISP10_STATE_READY)) { cif_isp10_pltfrm_pr_err(NULL, "cannot start streaming, input source (DMA) not ready\n"); ret = -EFAULT; goto err; } /* Activate MI */ cif_isp10_start_mi(dev, start_sp, start_mp); if ((dev->sp_stream.state != CIF_ISP10_STATE_STREAMING) && (dev->mp_stream.state != CIF_ISP10_STATE_STREAMING)) { /* Activate MIPI */ if (CIF_ISP10_INP_IS_MIPI(dev->config.input_sel)) cif_iowrite32OR(CIF_MIPI_CTRL_OUTPUT_ENA, dev->config.base_addr + CIF_MIPI_CTRL); /* Activate ISP ! */ if (CIF_ISP10_INP_NEED_ISP(dev->config.input_sel)) cif_iowrite32OR(CIF_ISP_CTRL_ISP_CFG_UPD | CIF_ISP_CTRL_ISP_INFORM_ENABLE | CIF_ISP_CTRL_ISP_ENABLE, dev->config.base_addr + CIF_ISP_CTRL); spin_lock_irqsave(&dev->isp_state_lock, flags); dev->isp_state = CIF_ISP10_STATE_RUNNING; spin_unlock_irqrestore(&dev->isp_state_lock, flags); } if (start_sp && (dev->sp_stream.state != CIF_ISP10_STATE_STREAMING)) { dev->sp_stream.state = CIF_ISP10_STATE_STREAMING; } if (start_mp && (dev->mp_stream.state != CIF_ISP10_STATE_STREAMING)) { dev->mp_stream.state = CIF_ISP10_STATE_STREAMING; } ret = cif_isp10_set_pm_state(dev, CIF_ISP10_PM_STATE_STREAMING); if (IS_ERR_VALUE(ret)) goto err; if (!CIF_ISP10_INP_IS_DMA(dev->config.input_sel)) { /* * CIF spec says to wait for sufficient time after enabling * the MIPI interface and before starting the sensor output. */ mdelay(1); /* start sensor output! */ cifisp_frame_id_reset(&dev->isp_dev); dev->isp_dev.frame_id_setexp = 0; list_for_each_entry_safe( vb, n, &dev->isp_dev.stat, queued_entry) { if (vb->state == VB2_BUF_STATE_DONE) { cif_isp10_pltfrm_pr_info( dev->dev, "discard vb: %d\n", vb->index); } } // spin_unlock_irq(&dev->isp_dev.lock); mutex_lock(&dev->img_src_exps.mutex); cif_isp10_img_src_ioctl(dev->img_src, RK_VIDIOC_SENSOR_MODE_DATA, &dev->img_src_exps.data[0].data); dev->img_src_exps.data[0].v_frame_id = 0; dev->img_src_exps.data[0].data.isp_input_width = dev->config.isp_config.input->defrect.width; dev->img_src_exps.data[0].data.isp_input_height = dev->config.isp_config.input->defrect.height; dev->img_src_exps.data[0].data.isp_input_horizontal_start = dev->config.isp_config.input->defrect.left; dev->img_src_exps.data[0].data.isp_input_vertical_start = dev->config.isp_config.input->defrect.top; dev->img_src_exps.data[0].data.isp_output_width = dev->config.isp_config.output.width; dev->img_src_exps.data[0].data.isp_output_height = dev->config.isp_config.output.height; for (i = 1; i < CIF_ISP10_IMG_SRC_DATA_NUM; i++) dev->img_src_exps.data[i] = dev->img_src_exps.data[0]; dev->img_src_exps.exp_idx = 0; mutex_unlock(&dev->img_src_exps.mutex); cif_isp10_pltfrm_rtrace_printf(dev->dev, "starting image source...\n"); ret = cif_isp10_img_src_set_state(dev, CIF_ISP10_IMG_SRC_STATE_STREAMING); if (IS_ERR_VALUE(ret)) goto err; } else { cif_isp10_pltfrm_rtrace_printf(dev->dev, "starting DMA...\n"); dev->dma_stream.state = CIF_ISP10_STATE_STREAMING; dev->dma_stream.stop = false; cif_isp10_dma_next_buff(dev); } dev->isp_dev.meta_info.read_cnt = 0; dev->isp_dev.meta_info.read_max = 0; if (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING) dev->isp_dev.meta_info.read_max++; if (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) dev->isp_dev.meta_info.read_max++; cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s, img_src state = %s\n" " MI_CTRL 0x%08x\n" " ISP_CTRL 0x%08x\n" " MIPI_CTRL 0x%08x\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state), cif_isp10_img_src_state_string(dev->img_src_state), cif_ioread32(dev->config.base_addr + CIF_MI_CTRL), cif_ioread32(dev->config.base_addr + CIF_ISP_CTRL), cif_ioread32(dev->config.base_addr + CIF_MIPI_CTRL)); return 0; err: cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s, img_src state = %s\n" " MI_CTRL 0x%08x\n" " ISP_CTRL 0x%08x\n" " MIPI_CTRL 0x%08x\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state), cif_isp10_img_src_state_string(dev->img_src_state), cif_ioread32(dev->config.base_addr + CIF_MI_CTRL), cif_ioread32(dev->config.base_addr + CIF_ISP_CTRL), cif_ioread32(dev->config.base_addr + CIF_MIPI_CTRL)); cif_isp10_pltfrm_pr_err(dev->dev, "failed with err %d\n", ret); return ret; } /* Function to be called inside ISR to update CIF ISM/DCROP/RSZ */ static int cif_isp10_update_ism_dcr_rsz( struct cif_isp10_device *dev) { int ret = 0; if (dev->config.isp_config.ism_config.ism_update_needed) { if (dev->config.isp_config.ism_config.ism_en) { if (!dev->isp_dev.cif_ism_cropping) dev->isp_dev.cif_ism_cropping = true; } else { if (dev->isp_dev.cif_ism_cropping) dev->isp_dev.cif_ism_cropping = false; } } /* * Update ISM, cif_isp10_config_ism() changes the output size of isp, * so it must be called before cif_isp10_config_rsz() */ if (dev->config.isp_config.ism_config.ism_update_needed) { cif_isp10_config_ism(dev, false); if (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) dev->config.mp_config.rsz_config.ism_adjust = true; if (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING) dev->config.sp_config.rsz_config.ism_adjust = true; dev->config.isp_config.ism_config.ism_update_needed = false; cif_iowrite32OR(CIF_ISP_CTRL_ISP_CFG_UPD, dev->config.base_addr + CIF_ISP_CTRL); if (dev->config.isp_config.ism_config.ism_en) dev->config.mi_config.async_updt |= CIF_ISP10_ASYNC_ISM; } /* Update RSZ */ if ((dev->config.mp_config.rsz_config.ycflt_adjust || dev->config.mp_config.rsz_config.ism_adjust)) { ret = cif_isp10_config_rsz(dev, CIF_ISP10_STREAM_MP, true); if (IS_ERR_VALUE(ret)) goto err; } if ((dev->config.sp_config.rsz_config.ycflt_adjust || dev->config.sp_config.rsz_config.ism_adjust)) { ret = cif_isp10_config_rsz(dev, CIF_ISP10_STREAM_SP, true); if (IS_ERR_VALUE(ret)) goto err; } return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with err %d\n", ret); return ret; } static int cif_isp10_mi_isr(unsigned int mi_mis, void *cntxt) { struct cif_isp10_device *dev = cntxt; unsigned int mi_mis_tmp; cif_isp10_pltfrm_pr_dbg(dev->dev, "\n MI_RIS 0x%08x\n" " MI_IMSC 0x%08x\n" " MI_MIS 0x%08x\n", cif_ioread32(dev->config.base_addr + CIF_MI_RIS), cif_ioread32(dev->config.base_addr + CIF_MI_IMSC), mi_mis); cif_isp10_pltfrm_rtrace_printf(dev->dev, "MI_MIS %08x, MI_RIS %08x, MI_IMSC %08x\n", mi_mis, cif_ioread32(dev->config.base_addr + CIF_MI_RIS), cif_ioread32(dev->config.base_addr + CIF_MI_IMSC)); if (mi_mis & CIF_MI_SP_FRAME) { dev->config.mi_config.sp.busy = false; cif_iowrite32(CIF_MI_SP_FRAME, dev->config.base_addr + CIF_MI_ICR); mi_mis_tmp = cif_ioread32(dev->config.base_addr + CIF_MI_MIS); if (mi_mis_tmp & CIF_MI_SP_FRAME) cif_isp10_pltfrm_pr_err(dev->dev, "sp icr err: 0x%x\n", mi_mis_tmp); } if (mi_mis & CIF_MI_MP_FRAME) { dev->config.mi_config.mp.busy = false; cif_iowrite32(CIF_MI_MP_FRAME, dev->config.base_addr + CIF_MI_ICR); mi_mis_tmp = cif_ioread32(dev->config.base_addr + CIF_MI_MIS); if (mi_mis_tmp & CIF_MI_MP_FRAME) cif_isp10_pltfrm_pr_err(dev->dev, "mp icr err: 0x%x\n", mi_mis_tmp); } if (mi_mis & CIF_MI_DMA_READY) (void)cif_isp10_dma_ready(dev); if (dev->config.jpeg_config.enable && (cif_ioread32(dev->config.base_addr + CIF_JPE_STATUS_RIS) & CIF_JPE_STATUS_ENCODE_DONE)) dev->config.jpeg_config.busy = false; if (!CIF_ISP10_MI_IS_BUSY(dev) && !dev->config.jpeg_config.busy) { if (dev->config.mi_config.async_updt) { u32 mp_y_off_cnt_shd = cif_ioread32(dev->config.base_addr + CIF_MI_MP_Y_OFFS_CNT_SHD); u32 sp_y_off_cnt_shd = cif_ioread32(dev->config.base_addr + CIF_MI_SP_Y_OFFS_CNT_SHD); cif_iowrite32(CIF_MI_INIT_SOFT_UPD, dev->config.base_addr + CIF_MI_INIT); cif_isp10_pltfrm_pr_dbg(NULL, "CIF_MI_INIT_SOFT_UPD\n"); if (!dev->config.isp_config.ism_config.ism_en && (dev->config.mi_config.async_updt & CIF_ISP10_ASYNC_ISM)) dev->config.mi_config.async_updt &= ~CIF_ISP10_ASYNC_ISM; if (sp_y_off_cnt_shd != 0) { spin_lock(&dev->vbq_lock); cif_isp10_requeue_bufs(dev, &dev->sp_stream); spin_unlock(&dev->vbq_lock); } if ((mp_y_off_cnt_shd != 0) && (!dev->config.jpeg_config.enable)) { spin_lock(&dev->vbq_lock); cif_isp10_requeue_bufs(dev, &dev->mp_stream); spin_unlock(&dev->vbq_lock); } if (((mp_y_off_cnt_shd != 0) && !dev->config.jpeg_config.enable) || (sp_y_off_cnt_shd != 0)) { cif_isp10_pltfrm_pr_dbg(dev->dev, "soft update too late (SP offset %d, MP offset %d)\n", sp_y_off_cnt_shd, mp_y_off_cnt_shd); } } if (dev->mp_stream.stop && (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING)) { cif_isp10_stop_mi(dev, false, true); dev->mp_stream.state = CIF_ISP10_STATE_READY; dev->mp_stream.stop = false; /* Turn off MRSZ since it is not needed */ cif_iowrite32(0, dev->config.base_addr + CIF_MRSZ_CTRL); cif_iowrite32OR(CIF_RSZ_CTRL_CFG_UPD, dev->config.base_addr + CIF_MRSZ_CTRL); cif_isp10_pltfrm_pr_dbg(NULL, "MP has stopped\n"); cif_isp10_pltfrm_event_signal(dev->dev, &dev->mp_stream.done); } if (dev->sp_stream.stop && (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING)) { cif_isp10_stop_mi(dev, true, false); dev->sp_stream.state = CIF_ISP10_STATE_READY; dev->sp_stream.stop = false; /* Turn off SRSZ since it is not needed */ cif_iowrite32(0, dev->config.base_addr + CIF_SRSZ_CTRL); cif_iowrite32OR(CIF_RSZ_CTRL_CFG_UPD, dev->config.base_addr + CIF_SRSZ_CTRL); cif_isp10_pltfrm_pr_dbg(NULL, "SP has stopped\n"); cif_isp10_pltfrm_event_signal(dev->dev, &dev->sp_stream.done); } if (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING) { spin_lock(&dev->vbq_lock); (void)cif_isp10_mi_frame_end(dev, CIF_ISP10_STREAM_SP); spin_unlock(&dev->vbq_lock); } if (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) { spin_lock(&dev->vbq_lock); (void)cif_isp10_mi_frame_end(dev, CIF_ISP10_STREAM_MP); spin_unlock(&dev->vbq_lock); } dev->b_mi_frame_end = true; if (dev->dma_stream.state == CIF_ISP10_STATE_STREAMING) { cif_isp10_dma_next_buff(dev); } else { if ((dev->sp_stream.state == CIF_ISP10_STATE_STREAMING) && dev->sp_stream.curr_buf) dev->config.mi_config.sp.busy = true; if ((dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) && dev->mp_stream.curr_buf) dev->config.mi_config.mp.busy = true; } if (dev->b_isp_frame_in && ((dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) || (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING))) cif_isp10_update_ism_dcr_rsz(dev); } cif_iowrite32(~(CIF_MI_MP_FRAME | CIF_MI_SP_FRAME | CIF_MI_DMA_READY), dev->config.base_addr + CIF_MI_ICR); return 0; } static int cif_isp10_register_isrs(struct cif_isp10_device *dev) { int ret = 0; cif_isp10_pltfrm_irq_register_isr( dev->dev, CIF_ISP_MIS, cif_isp10_isp_isr, dev); if (IS_ERR_VALUE(ret)) cif_isp10_pltfrm_pr_warn(dev->dev, "unable to register ISP ISR, some processing errors may go unnoticed\n"); cif_isp10_pltfrm_irq_register_isr( dev->dev, CIF_MIPI_MIS, cif_isp10_mipi_isr, dev); if (IS_ERR_VALUE(ret)) cif_isp10_pltfrm_pr_warn(dev->dev, "unable to register MIPI ISR, MIPI errors may go unnoticed\n"); ret = cif_isp10_pltfrm_irq_register_isr( dev->dev, CIF_MI_MIS, cif_isp10_mi_isr, dev); if (IS_ERR_VALUE(ret)) { cif_isp10_pltfrm_pr_err(dev->dev, "unable to register MI ISR, aborting\n"); goto err; } return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d", ret); return ret; } static void cif_isp10_vs_work(struct work_struct *work) { struct cif_isp10_isp_vs_work *vs_wk = container_of(work, struct cif_isp10_isp_vs_work, work); struct cif_isp10_device *dev = vs_wk->dev; unsigned int v_frame_id; unsigned char exp_idx, i; struct isp_supplemental_sensor_mode_data sensor_data; switch (vs_wk->cmd) { case CIF_ISP10_VS_EXP: { struct cif_isp10_img_src_exp *exp = (struct cif_isp10_img_src_exp *)vs_wk->param; struct cif_isp10_img_src_ext_ctrl *exp_ctrl = &exp->exp; struct cif_isp10_img_src_data *new_data; if (dev->img_src) cif_isp10_img_src_s_ext_ctrls(dev->img_src, exp_ctrl); else cif_isp10_pltfrm_pr_err(dev->dev, "dev->img_src is NULL\n"); mutex_lock(&dev->img_src_exps.mutex); cif_isp10_img_src_ioctl(dev->img_src, RK_VIDIOC_SENSOR_MODE_DATA, &sensor_data); exp_idx = dev->img_src_exps.exp_idx; if (((dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] + 2) == dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX])) { new_data = &dev->img_src_exps.data[exp_idx]; v_frame_id = dev->isp_dev.frame_id + dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX]; if (v_frame_id == new_data->v_frame_id) new_data->data.exp_time = sensor_data.exp_time; } else if ((dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] == (dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX] + 2))) { new_data = &dev->img_src_exps.data[exp_idx]; v_frame_id = dev->isp_dev.frame_id + dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX]; if (v_frame_id == new_data->v_frame_id) new_data->data.gain = sensor_data.gain; } exp_idx = (exp_idx + 1) % CIF_ISP10_IMG_SRC_DATA_NUM; new_data = &dev->img_src_exps.data[exp_idx]; if (exp_ctrl->ctrls->id == V4L2_CID_EXPOSURE) v_frame_id = dev->isp_dev.frame_id + dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX]; else v_frame_id = dev->isp_dev.frame_id + dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX]; for (i = 0; i < CIF_ISP10_IMG_SRC_DATA_NUM; i++) { if (v_frame_id == dev->img_src_exps.data[i].v_frame_id) { exp_idx = i; new_data = &dev->img_src_exps.data[i]; } } new_data->v_frame_id = v_frame_id; new_data->data = sensor_data; new_data->data.isp_input_width = dev->config.isp_config.input->defrect.width; new_data->data.isp_input_height = dev->config.isp_config.input->defrect.height; new_data->data.isp_input_horizontal_start = dev->config.isp_config.input->defrect.left; new_data->data.isp_input_vertical_start = dev->config.isp_config.input->defrect.top; new_data->data.isp_output_width = dev->config.isp_config.output.width; new_data->data.isp_output_height = dev->config.isp_config.output.height; dev->img_src_exps.exp_idx = exp_idx; mutex_unlock(&dev->img_src_exps.mutex); /* * pr_info("%s: exp_time: %d gain: %d, frame_id: s %d, v %d\n", * __func__, * new_data->data.exp_time, * new_data->data.gain, * dev->isp_dev.frame_id, * new_data->v_frame_id); */ kfree(exp->exp.ctrls); exp->exp.ctrls = NULL; kfree(exp); exp = NULL; break; } default: break; } kfree(vs_wk); vs_wk = NULL; } static int cif_isp10_vs_work_cmd( struct cif_isp10_device *dev, enum cif_isp10_isp_vs_cmd cmd, void *para) { struct cif_isp10_isp_vs_work *vs_wk; if (cmd == CIF_ISP10_VS_EXP) vs_wk = kmalloc(sizeof(struct cif_isp10_isp_vs_work), GFP_ATOMIC); else vs_wk = kmalloc(sizeof(struct cif_isp10_isp_vs_work), GFP_KERNEL); if (vs_wk) { vs_wk->cmd = cmd; vs_wk->dev = dev; vs_wk->param = (void *)para; INIT_WORK((struct work_struct *)&vs_wk->work, cif_isp10_vs_work); if (!queue_work(dev->vs_wq, (struct work_struct *)&vs_wk->work)) { cif_isp10_pltfrm_pr_err(dev->dev, "%s: queue work failed\n", __func__); kfree(vs_wk); } return 0; } else { return -ENOMEM; } } static struct cif_isp10_img_src_exp *cif_isp10_get_last_exp( struct cif_isp10_device *dev) { struct cif_isp10_img_src_exp *exp = NULL; if (!list_empty(&dev->img_src_exps.list)) { exp = list_last_entry(&dev->img_src_exps.list, struct cif_isp10_img_src_exp, list); } return exp; } /* Public Functions */ void cif_isp10_sensor_mode_data_sync( struct cif_isp10_device *dev, unsigned int frame_id, struct isp_supplemental_sensor_mode_data *data) { struct cif_isp10_img_src_data *match_data; unsigned int i; unsigned char exp_idx; mutex_lock(&dev->img_src_exps.mutex); exp_idx = dev->img_src_exps.exp_idx; exp_idx = (exp_idx + 1) % CIF_ISP10_IMG_SRC_DATA_NUM; match_data = &dev->img_src_exps.data[exp_idx]; for (i = 0; i < CIF_ISP10_IMG_SRC_DATA_NUM; i++) { if (frame_id >= dev->img_src_exps.data[exp_idx].v_frame_id) match_data = &dev->img_src_exps.data[exp_idx]; exp_idx = (exp_idx + 1) % CIF_ISP10_IMG_SRC_DATA_NUM; } memcpy(data, &match_data->data, sizeof(struct isp_supplemental_sensor_mode_data)); mutex_unlock(&dev->img_src_exps.mutex); } int cif_isp10_streamon( struct cif_isp10_device *dev, u32 stream_ids) { int ret = 0; bool streamon_sp = stream_ids & CIF_ISP10_STREAM_SP; bool streamon_mp = stream_ids & CIF_ISP10_STREAM_MP; bool streamon_dma = stream_ids & CIF_ISP10_STREAM_DMA; cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s, streamon SP = %d, streamon MP = %d, streamon DMA = %d\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state), streamon_sp, streamon_mp, streamon_dma); if (!((streamon_sp && (dev->sp_stream.state != CIF_ISP10_STATE_STREAMING)) || (streamon_mp && (dev->mp_stream.state != CIF_ISP10_STATE_STREAMING)))) return 0; if (streamon_sp && (dev->sp_stream.state != CIF_ISP10_STATE_READY)) { cif_isp10_pltfrm_pr_err(dev->dev, "cannot start streaming on SP path, path not yet enabled\n"); ret = -EFAULT; goto err; } if (streamon_mp && (dev->mp_stream.state != CIF_ISP10_STATE_READY)) { cif_isp10_pltfrm_pr_err(dev->dev, "cannot start streaming on MP path, path not yet enabled\n"); ret = -EFAULT; goto err; } if (streamon_sp && dev->config.mi_config.raw_enable && (streamon_mp || (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING))) { cif_isp10_pltfrm_pr_err(dev->dev, "cannot start streaming on SP path when MP is active and set to RAW output\n"); ret = -EBUSY; goto err; } if (streamon_mp && (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING)) dev->mp_stream.updt_cfg = true; if (streamon_sp && (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING)) dev->sp_stream.updt_cfg = true; if (streamon_sp && dev->sp_stream.updt_cfg && (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING)) { ret = cif_isp10_stop(dev, false, true); if (IS_ERR_VALUE(ret)) goto err; streamon_mp = true; dev->mp_stream.updt_cfg = true; } if (streamon_mp && dev->mp_stream.updt_cfg && (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING)) { ret = cif_isp10_stop(dev, true, false); if (IS_ERR_VALUE(ret)) goto err; streamon_sp = true; dev->sp_stream.updt_cfg = true; } stream_ids = 0; if (streamon_mp && dev->mp_stream.updt_cfg) stream_ids |= CIF_ISP10_STREAM_MP; if (streamon_sp && dev->sp_stream.updt_cfg) stream_ids |= CIF_ISP10_STREAM_SP; ret = cif_isp10_config_cif(dev, stream_ids); if (IS_ERR_VALUE(ret)) goto err; rockchip_set_system_status(SYS_STATUS_ISP); ret = cif_isp10_start(dev, streamon_sp, streamon_mp); if (IS_ERR_VALUE(ret)) { rockchip_clear_system_status(SYS_STATUS_ISP); goto err; } cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state)); return 0; err: cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state)); cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } int cif_isp10_streamoff( struct cif_isp10_device *dev, u32 stream_ids) { int ret = 0; bool streamoff_sp = stream_ids & CIF_ISP10_STREAM_SP; bool streamoff_mp = stream_ids & CIF_ISP10_STREAM_MP; bool streamoff_dma = stream_ids & CIF_ISP10_STREAM_DMA; unsigned int streamoff_all = 0; unsigned long lock_flags; cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s, streamoff SP = %d, streamoff MP = %d, streamoff DMA = %d\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state), streamoff_sp, streamoff_mp, streamoff_dma); if (dev->config.flash_mode != CIF_ISP10_FLASH_MODE_OFF && ((streamoff_sp && (dev->mp_stream.state == CIF_ISP10_STATE_INACTIVE)) || (streamoff_mp && (dev->sp_stream.state == CIF_ISP10_STATE_INACTIVE)))) cif_isp10_img_src_s_ctrl(dev->img_src, CIF_ISP10_CID_FLASH_MODE, CIF_ISP10_FLASH_MODE_OFF); streamoff_all = 0; if (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING) { if (streamoff_sp) streamoff_all |= CIF_ISP10_STREAM_SP; } else { streamoff_all |= CIF_ISP10_STREAM_SP; } if (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) { if (streamoff_mp) streamoff_all |= CIF_ISP10_STREAM_MP; } else { streamoff_all |= CIF_ISP10_STREAM_MP; } if (streamoff_all == (CIF_ISP10_STREAM_MP | CIF_ISP10_STREAM_SP)) { struct cif_isp10_img_src_exp *exp; spin_lock_irqsave(&dev->img_src_exps.lock, lock_flags); while (!list_empty(&dev->img_src_exps.list)) { exp = list_first_entry(&dev->img_src_exps.list, struct cif_isp10_img_src_exp, list); list_del(&exp->list); kfree(exp->exp.ctrls); kfree(exp); } spin_unlock_irqrestore(&dev->img_src_exps.lock, lock_flags); drain_workqueue(dev->vs_wq); } ret = cif_isp10_stop(dev, streamoff_sp, streamoff_mp); if (IS_ERR_VALUE(ret)) goto err; if (streamoff_dma) cif_isp10_stop_dma(dev); rockchip_clear_system_status(SYS_STATUS_ISP); cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s, # frames received = %d\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state), dev->isp_dev.frame_id >> 1); return 0; err: rockchip_clear_system_status(SYS_STATUS_ISP); cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s, DMA state = %s\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state), cif_isp10_state_string(dev->dma_stream.state)); cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } int cif_isp10_suspend( struct cif_isp10_device *dev) { int ret = 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state)); if ((dev->pm_state == CIF_ISP10_PM_STATE_SUSPENDED) || (dev->pm_state == CIF_ISP10_PM_STATE_OFF)) return 0; dev->sp_stream.saved_state = dev->sp_stream.state; dev->mp_stream.saved_state = dev->mp_stream.state; ret = cif_isp10_stop(dev, true, true); if (IS_ERR_VALUE(ret)) goto err; ret = cif_isp10_set_pm_state(dev, CIF_ISP10_PM_STATE_SUSPENDED); if (IS_ERR_VALUE(ret)) goto err; ret = cif_isp10_img_src_set_state(dev, CIF_ISP10_IMG_SRC_STATE_OFF); if (IS_ERR_VALUE(ret)) goto err; return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } int cif_isp10_resume( struct cif_isp10_device *dev) { u32 stream_ids = 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "SP state = %s, MP state = %s\n", cif_isp10_state_string(dev->sp_stream.state), cif_isp10_state_string(dev->mp_stream.state)); if ((dev->sp_stream.saved_state == CIF_ISP10_STATE_READY) || (dev->sp_stream.saved_state == CIF_ISP10_STATE_STREAMING)) { dev->sp_stream.updt_cfg = true; dev->sp_stream.state = CIF_ISP10_STATE_READY; if (dev->sp_stream.saved_state == CIF_ISP10_STATE_STREAMING) stream_ids |= CIF_ISP10_STREAM_SP; } if ((dev->mp_stream.saved_state == CIF_ISP10_STATE_READY) || (dev->mp_stream.saved_state == CIF_ISP10_STATE_STREAMING)) { dev->mp_stream.updt_cfg = true; dev->mp_stream.state = CIF_ISP10_STATE_READY; if (dev->mp_stream.saved_state == CIF_ISP10_STATE_STREAMING) stream_ids |= CIF_ISP10_STREAM_MP; } if ((dev->dma_stream.saved_state == CIF_ISP10_STATE_READY) || (dev->dma_stream.saved_state == CIF_ISP10_STATE_STREAMING)) { dev->dma_stream.state = CIF_ISP10_STATE_READY; if (dev->dma_stream.saved_state == CIF_ISP10_STATE_STREAMING) stream_ids |= CIF_ISP10_STREAM_DMA; } return cif_isp10_streamon(dev, stream_ids); } int cif_isp10_s_fmt( struct cif_isp10_device *dev, enum cif_isp10_stream_id stream_id, struct cif_isp10_strm_fmt *strm_fmt, u32 stride) { int ret; cif_isp10_pltfrm_pr_dbg(NULL, "%s\n", cif_isp10_stream_id_string(stream_id)); switch (stream_id) { case CIF_ISP10_STREAM_SP: ret = cif_isp10_s_fmt_sp(dev, strm_fmt, stride); break; case CIF_ISP10_STREAM_MP: ret = cif_isp10_s_fmt_mp(dev, strm_fmt, stride); break; case CIF_ISP10_STREAM_DMA: ret = cif_isp10_s_fmt_dma(dev, strm_fmt, stride); break; default: cif_isp10_pltfrm_pr_err(NULL, "unknown/unsupported stream ID %d\n", stream_id); ret = -EINVAL; goto err; } return ret; err: cif_isp10_pltfrm_pr_err(NULL, "failed with error %d\n", ret); return ret; } int cif_isp10_init( struct cif_isp10_device *dev, u32 stream_ids) { int ret; cif_isp10_pltfrm_pr_dbg(NULL, "0x%08x\n", stream_ids); if (stream_ids & ~(CIF_ISP10_ALL_STREAMS)) { cif_isp10_pltfrm_pr_err(NULL, "unknown/unsupported stream IDs 0x%08x\n", stream_ids); ret = -EINVAL; goto err; } /* set default input, failure is not fatal here */ if ((dev->sp_stream.state == CIF_ISP10_STATE_DISABLED) && (dev->mp_stream.state == CIF_ISP10_STATE_DISABLED)) { ret = cif_isp10_s_input(dev, 0); if (IS_ERR_VALUE(ret)) goto err; dev->config.isp_config.si_enable = false; dev->config.isp_config.ie_config.effect = CIF_ISP10_IE_NONE; memset(&dev->config.isp_config.output, 0, sizeof(dev->config.isp_config.output)); } if (stream_ids & CIF_ISP10_STREAM_SP) cif_isp10_init_stream(dev, CIF_ISP10_STREAM_SP); if (stream_ids & CIF_ISP10_STREAM_MP) cif_isp10_init_stream(dev, CIF_ISP10_STREAM_MP); if (stream_ids & CIF_ISP10_STREAM_DMA) cif_isp10_init_stream(dev, CIF_ISP10_STREAM_DMA); return 0; err: cif_isp10_pltfrm_pr_err(NULL, "failed with error %d\n", ret); return ret; } int cif_isp10_release( struct cif_isp10_device *dev, int stream_ids) { int ret; unsigned char *meta_addr = NULL; unsigned long flags = 0; struct cif_isp10_stream *strm_dev; cif_isp10_pltfrm_pr_dbg(NULL, "0x%08x\n", stream_ids); if ((dev->sp_stream.state == CIF_ISP10_STATE_DISABLED) && (dev->mp_stream.state == CIF_ISP10_STATE_DISABLED) && (dev->dma_stream.state == CIF_ISP10_STATE_DISABLED)) return 0; if (stream_ids & ~(CIF_ISP10_ALL_STREAMS)) { cif_isp10_pltfrm_pr_err(NULL, "unknown/unsupported stream IDs 0x%08x\n", stream_ids); ret = -EINVAL; goto err; } if (stream_ids == CIF_ISP10_STREAM_MP) strm_dev = &dev->mp_stream; else if (stream_ids == CIF_ISP10_STREAM_SP) strm_dev = &dev->sp_stream; else strm_dev = NULL; if (strm_dev) { spin_lock_irqsave(&strm_dev->metadata.spinlock, flags); if (strm_dev->metadata.d) { meta_addr = strm_dev->metadata.d; strm_dev->metadata.d = NULL; strm_dev->metadata.cnt = 0; } spin_unlock_irqrestore(&strm_dev->metadata.spinlock, flags); if (meta_addr) vfree(meta_addr); } if (stream_ids & CIF_ISP10_STREAM_SP) { if (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING) { cif_isp10_pltfrm_pr_warn(dev->dev, "CIF SP in streaming state, should be stopped before release, trying to stop it\n"); ret = cif_isp10_stop(dev, true, false); if (IS_ERR_VALUE(ret)) goto err; } dev->sp_stream.state = CIF_ISP10_STATE_DISABLED; } if (stream_ids & CIF_ISP10_STREAM_MP) { if (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) { cif_isp10_pltfrm_pr_warn(dev->dev, "CIF MP in streaming state, should be stopped before release, trying to stop it\n"); ret = cif_isp10_stop(dev, false, true); if (IS_ERR_VALUE(ret)) goto err; } dev->mp_stream.state = CIF_ISP10_STATE_DISABLED; } if ((dev->sp_stream.state == CIF_ISP10_STATE_DISABLED) && (dev->mp_stream.state == CIF_ISP10_STATE_DISABLED)) { if (IS_ERR_VALUE(cif_isp10_set_pm_state(dev, CIF_ISP10_PM_STATE_OFF))) cif_isp10_pltfrm_pr_warn(dev->dev, "CIF power off failed\n"); if (dev->img_src) { if (IS_ERR_VALUE(cif_isp10_img_src_set_state(dev, CIF_ISP10_IMG_SRC_STATE_OFF))) cif_isp10_pltfrm_pr_warn(dev->dev, "image source power off failed\n"); dev->img_src = NULL; } } return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with error %d\n", ret); return ret; } struct cif_isp10_device *cif_isp10_create( CIF_ISP10_PLTFRM_DEVICE pdev, void (*sof_event)(struct cif_isp10_device *dev, __u32 frame_sequence), void (*requeue_bufs)(struct cif_isp10_device *dev, enum cif_isp10_stream_id stream_id), struct pltfrm_soc_cfg *soc_cfg) { int ret; struct cif_isp10_device *dev; cif_isp10_pltfrm_pr_dbg(NULL, "\n"); /* Allocate needed structures */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { cif_isp10_pltfrm_pr_err(dev->dev, "memory allocation failed\n"); ret = -ENOMEM; goto err; } dev->sof_event = sof_event; dev->requeue_bufs = requeue_bufs; ret = cif_isp10_pltfrm_dev_init(dev, &pdev, &dev->config.base_addr); if (IS_ERR_VALUE(ret)) goto err; cif_isp10_pltfrm_debug_register_print_cb( dev->dev, (void (*)(void *, const char *))cif_isp10_debug_print_block, dev); cif_isp10_pltfrm_soc_init(dev, soc_cfg); ret = cif_isp10_img_srcs_init(dev); if (IS_ERR_VALUE(ret)) { cif_isp10_pltfrm_pr_err(dev->dev, "cif_isp10_img_srcs_init failed\n"); goto err; } ret = cif_isp10_register_isrs(dev); if (IS_ERR_VALUE(ret)) goto err; dev->pm_state = CIF_ISP10_PM_STATE_OFF; dev->sp_stream.state = CIF_ISP10_STATE_DISABLED; dev->sp_stream.id = CIF_ISP10_STREAM_SP; dev->mp_stream.state = CIF_ISP10_STATE_DISABLED; dev->mp_stream.id = CIF_ISP10_STREAM_MP; dev->dma_stream.state = CIF_ISP10_STATE_DISABLED; dev->dma_stream.id = CIF_ISP10_STREAM_DMA; dev->config.mi_config.async_updt = 0; (void)cif_isp10_init(dev, CIF_ISP10_ALL_STREAMS); cif_isp10_pltfrm_event_init(dev->dev, &dev->dma_stream.done); cif_isp10_pltfrm_event_init(dev->dev, &dev->sp_stream.done); cif_isp10_pltfrm_event_init(dev->dev, &dev->mp_stream.done); dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] = 4; dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX] = 4; dev->img_src_exps.inited = false; mutex_init(&dev->img_src_exps.mutex); memset(&dev->img_src_exps.data, 0x00, sizeof(dev->img_src_exps.data)); spin_lock_init(&dev->img_src_exps.lock); INIT_LIST_HEAD(&dev->img_src_exps.list); dev->vs_wq = alloc_workqueue("cif isp10 vs workqueue", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); /* TBD: clean this up */ init_output_formats(); return dev; err: cif_isp10_pltfrm_pr_err(NULL, "failed with error %d\n", ret); if (!IS_ERR_OR_NULL(dev)) kfree(dev); return ERR_PTR(ret); } void cif_isp10_destroy( struct cif_isp10_device *dev) { cif_isp10_pltfrm_pr_dbg(NULL, "\n"); if (!IS_ERR_OR_NULL(dev)) kfree(dev); } int cif_isp10_g_input( struct cif_isp10_device *dev, unsigned int *input) { unsigned int i; for (i = 0; i < dev->img_src_cnt; i++) { if (dev->img_src != NULL && dev->img_src == dev->img_src_array[i]) { *input = i; return 0; } } return -EINVAL; } int cif_isp10_s_input( struct cif_isp10_device *dev, unsigned int input) { int ret; enum cif_isp10_inp inp; cif_isp10_pltfrm_pr_dbg(dev->dev, "setting input to %s, [w-h]=[%d-%d], defrect=[%d-%d-%d-%d]\n", cif_isp10_inp_string( cif_isp10_input_index2inp(dev, input)), dev->config.img_src_output.frm_fmt.width, dev->config.img_src_output.frm_fmt.height, dev->config.img_src_output.frm_fmt.defrect.left, dev->config.img_src_output.frm_fmt.defrect.top, dev->config.img_src_output.frm_fmt.defrect.width, dev->config.img_src_output.frm_fmt.defrect.height); if (input >= dev->img_src_cnt + CIF_ISP10_INP_DMA_CNT()) { cif_isp10_pltfrm_pr_err(NULL, "invalid input %d\n", input); ret = -EINVAL; goto err; } dev->img_src = NULL; inp = cif_isp10_input_index2inp(dev, input); /* DMA -> ISP or DMA -> IE */ if ((inp == CIF_ISP10_INP_DMA) || (inp == CIF_ISP10_INP_DMA_IE)) dev->config.isp_config.input = &dev->config.mi_config.dma.output; else { dev->img_src = dev->img_src_array[input]; dev->config.isp_config.input = &dev->config.img_src_output.frm_fmt; } dev->config.input_sel = inp; return 0; err: cif_isp10_pltfrm_pr_err(NULL, "failed with error %d\n", ret); return ret; } const char *cif_isp10_g_input_name( struct cif_isp10_device *dev, unsigned int input_index) { if (input_index >= dev->img_src_cnt + CIF_ISP10_INP_DMA_CNT()) { cif_isp10_pltfrm_pr_dbg(NULL, "index %d out of bounds\n", input_index); return NULL; } if (input_index < dev->img_src_cnt) return cif_isp10_img_src_g_name( dev->img_src_array[input_index]); else return cif_isp10_inp_string(CIF_ISP10_INP_DMA + ((input_index - dev->img_src_cnt) << 24)); } int cif_isp10_qbuf( struct cif_isp10_device *dev, enum cif_isp10_stream_id stream, struct cif_isp10_buffer *buf) { int ret = 0; cif_isp10_pltfrm_pr_dbg(dev->dev, "%s\n", cif_isp10_stream_id_string(stream)); switch (stream) { case CIF_ISP10_STREAM_SP: list_add_tail(&buf->queue, &dev->sp_stream.buf_queue); cif_isp10_update_mi_immediate(dev, CIF_ISP10_STREAM_SP); break; case CIF_ISP10_STREAM_MP: list_add_tail(&buf->queue, &dev->mp_stream.buf_queue); cif_isp10_update_mi_immediate(dev, CIF_ISP10_STREAM_MP); break; case CIF_ISP10_STREAM_DMA: list_add_tail(&buf->queue, &dev->dma_stream.buf_queue); if ((dev->dma_stream.state == CIF_ISP10_STATE_STREAMING) && !CIF_ISP10_MI_IS_BUSY(dev)) cif_isp10_dma_next_buff(dev); break; case CIF_ISP10_STREAM_ISP: WARN_ON(1); break; default: cif_isp10_pltfrm_pr_err(dev->dev, "unknown stream %d\n", stream); ret = -EINVAL; goto err; } return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with err %d\n", ret); return ret; } int cif_isp10_reqbufs( struct cif_isp10_device *dev, enum cif_isp10_stream_id strm, struct v4l2_requestbuffers *req) { unsigned long flags = 0; struct cif_isp10_stream *strm_dev = NULL; switch (strm) { case CIF_ISP10_STREAM_MP: strm_dev = &dev->mp_stream; break; case CIF_ISP10_STREAM_SP: strm_dev = &dev->sp_stream; break; default: cif_isp10_pltfrm_pr_err(dev->dev, "unknown stream id%d\n", strm); break; } spin_lock_irqsave(&strm_dev->metadata.spinlock, flags); strm_dev->metadata.cnt = req->count; spin_unlock_irqrestore(&strm_dev->metadata.spinlock, flags); return 0; } int cif_isp10_s_exp( struct cif_isp10_device *dev, struct cif_isp10_img_src_ext_ctrl *exp_ctrl, bool cls_exp) { struct cif_isp10_img_src_ctrl *ctrl_exp_t = NULL, *ctrl_exp_g = NULL; struct cif_isp10_img_src_exp *exp = NULL, *exp_t = NULL, *exp_g = NULL; struct cif_isp10_img_src_exp *last_exp = NULL; unsigned long lock_flags; int retval, i, j, exp_cnt, val; if (!dev->vs_wq) return -ENODEV; if (!dev->img_src_exps.inited) { cif_isp10_config_img_src_exps(dev); dev->img_src_exps.inited = true; } /* clean exposure list before */ if (cls_exp) { spin_lock_irqsave(&dev->img_src_exps.lock, lock_flags); while (!list_empty(&dev->img_src_exps.list)) { exp = list_first_entry(&dev->img_src_exps.list, struct cif_isp10_img_src_exp, list); list_del(&exp->list); kfree(exp->exp.ctrls); kfree(exp); } spin_unlock_irqrestore(&dev->img_src_exps.lock, lock_flags); exp = NULL; } if (dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] == dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX]) { exp = kmalloc(sizeof(*exp), GFP_KERNEL); if (!exp) { retval = -ENOMEM; goto failed; } exp->exp = *exp_ctrl; spin_lock_irqsave(&dev->img_src_exps.lock, lock_flags); list_add_tail(&exp->list, &dev->img_src_exps.list); spin_unlock_irqrestore(&dev->img_src_exps.lock, lock_flags); } else if (((dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] + 2) == dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX]) || (dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] == (dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX] + 2))) { exp = kmalloc(sizeof(*exp), GFP_KERNEL); if (!exp) { retval = -ENOMEM; goto failed; } exp->exp = *exp_ctrl; spin_lock_irqsave(&dev->img_src_exps.lock, lock_flags); last_exp = cif_isp10_get_last_exp(dev); if (last_exp) { for (i = 0; i < last_exp->exp.cnt; i++) { for (j = 0; j < exp->exp.cnt; j++) { if (last_exp->exp.ctrls[i].id == exp->exp.ctrls[j].id) { val = last_exp->exp.ctrls[i].val; last_exp->exp.ctrls[i].val = exp->exp.ctrls[j].val; exp->exp.ctrls[j].val = val; } } } list_replace(&last_exp->list, &exp->list); list_add_tail(&last_exp->list, &dev->img_src_exps.list); spin_unlock_irqrestore(&dev->img_src_exps.lock, lock_flags); } else { spin_unlock_irqrestore(&dev->img_src_exps.lock, lock_flags); kfree(exp); exp = NULL; goto TIME_GAIN_DIVIDE; } } else { TIME_GAIN_DIVIDE: exp_t = kmalloc(sizeof(*exp_t), GFP_KERNEL); if (!exp_t) { retval = -ENOMEM; goto failed; } ctrl_exp_t = kmalloc(sizeof(*ctrl_exp_t) * 2, GFP_KERNEL); if (!ctrl_exp_t) { retval = -ENOMEM; goto failed; } exp_g = kmalloc(sizeof(*exp_g), GFP_KERNEL); if (!exp_g) { retval = -ENOMEM; goto failed; } ctrl_exp_g = kmalloc(sizeof(*ctrl_exp_g)*2, GFP_KERNEL); if (!ctrl_exp_g) { retval = -ENOMEM; goto failed; } exp_cnt = 0; for (i = 0; i < exp_ctrl->cnt; i++) { if (exp_ctrl->ctrls[i].id == V4L2_CID_EXPOSURE) { *ctrl_exp_t = exp_ctrl->ctrls[i]; exp_cnt++; } if (exp_ctrl->ctrls[i].id == RK_V4L2_CID_VTS) { *(ctrl_exp_t + 1) = exp_ctrl->ctrls[i]; exp_cnt++; } if (exp_ctrl->ctrls[i].id == V4L2_CID_GAIN) { *ctrl_exp_g = exp_ctrl->ctrls[i]; } if (exp_ctrl->ctrls[i].id == RK_V4L2_CID_GAIN_PERCENT) { *(ctrl_exp_g+1) = exp_ctrl->ctrls[i]; } } kfree(exp_ctrl->ctrls); exp_ctrl->ctrls = NULL; exp_t->exp.cnt = exp_cnt; exp_t->exp.class = exp_ctrl->class; exp_t->exp.ctrls = ctrl_exp_t; exp_g->exp.cnt = 2; exp_g->exp.class = exp_ctrl->class; exp_g->exp.ctrls = ctrl_exp_g; if (dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_T_INDEX] < dev->img_src_exps.exp_valid_frms[VALID_FR_EXP_G_INDEX]) { spin_lock_irqsave(&dev->img_src_exps.lock, lock_flags); list_add_tail(&exp_g->list, &dev->img_src_exps.list); list_add_tail(&exp_t->list, &dev->img_src_exps.list); spin_unlock_irqrestore(&dev->img_src_exps.lock, lock_flags); } else { spin_lock_irqsave(&dev->img_src_exps.lock, lock_flags); list_add_tail(&exp_t->list, &dev->img_src_exps.list); list_add_tail(&exp_g->list, &dev->img_src_exps.list); spin_unlock_irqrestore(&dev->img_src_exps.lock, lock_flags); } } return 0; failed: if (exp) { kfree(exp); exp = NULL; } if (exp_t) { kfree(exp_t); exp_t = NULL; } if (exp_g) { kfree(exp_g); exp_g = NULL; } if (ctrl_exp_t) { kfree(ctrl_exp_t); ctrl_exp_t = NULL; } if (ctrl_exp_g) { kfree(ctrl_exp_g); ctrl_exp_g = NULL; } return retval; } int cif_isp10_s_vcm( struct cif_isp10_device *dev, unsigned int id, int val) { if (dev->img_src != NULL) cif_isp10_img_src_ioctl(dev->img_src, PLTFRM_CIFCAM_SET_VCM_POS, &val); return 0; } int cif_isp10_s_vb_metadata( struct cif_isp10_device *dev, struct cif_isp10_isp_readout_work *readout_work) { unsigned long flags = 0; unsigned int stream_id = readout_work->stream_id; struct vb2_buffer *vb = readout_work->vb; struct cif_isp10_stream *strm_dev = NULL; struct v4l2_buffer_metadata_s *metadata; struct isp_supplemental_sensor_mode_data sensor_mode; switch (stream_id) { case CIF_ISP10_STREAM_MP: strm_dev = &dev->mp_stream; break; case CIF_ISP10_STREAM_SP: strm_dev = &dev->sp_stream; break; default: cif_isp10_pltfrm_pr_err(dev->dev, "unknown stream id%d\n", stream_id); return -1; } if (strm_dev->state != CIF_ISP10_STATE_STREAMING) { cif_isp10_pltfrm_pr_err(dev->dev, "stream id%d is not streaming\n", stream_id); return -1; } cif_isp10_sensor_mode_data_sync(dev, readout_work->frame_id, &sensor_mode); spin_lock_irqsave(&strm_dev->metadata.spinlock, flags); if (vb && strm_dev->metadata.d) { if (vb->index >= strm_dev->metadata.cnt) { cif_isp10_pltfrm_pr_err(dev->dev, "vb->index %d is bigger than metadata.cnt %d\n", vb->index, strm_dev->metadata.cnt); spin_unlock_irqrestore(&strm_dev->metadata.spinlock, flags); return -1; } metadata = (struct v4l2_buffer_metadata_s *) (strm_dev->metadata.d + vb->index * CAMERA_METADATA_LEN); metadata->frame_id = readout_work->frame_id; metadata->sensor.exp_time = sensor_mode.exp_time; metadata->sensor.gain = sensor_mode.gain; } spin_unlock_irqrestore(&strm_dev->metadata.spinlock, flags); if (vb) { cif_isp10_pltfrm_pr_dbg(NULL, "frame done\n"); wake_up(&vb->vb2_queue->done_wq); } return 0; } int cif_isp10_mmap( struct cif_isp10_device *dev, enum cif_isp10_stream_id stream_id, struct vm_area_struct *vma) { struct cif_isp10_stream *strm_dev; void *mem_vaddr; int retval = 0, pages; unsigned long mem_size; unsigned long flags = 0; switch (stream_id) { case CIF_ISP10_STREAM_MP: strm_dev = &dev->mp_stream; break; case CIF_ISP10_STREAM_SP: strm_dev = &dev->sp_stream; break; default: cif_isp10_pltfrm_pr_err(dev->dev, "unknown stream id%d\n", stream_id); return -ENODEV; } mem_size = vma->vm_end - vma->vm_start; if (mem_size > strm_dev->metadata.cnt * CAMERA_METADATA_LEN) { retval = -ENOMEM; cif_isp10_pltfrm_pr_err(dev->dev, "mmap size(0x%lx) > metadata memory size(0x%lx), so failed!", mem_size, (unsigned long)(strm_dev->metadata.cnt * CAMERA_METADATA_LEN)); goto done; } pages = PAGE_ALIGN(vma->vm_end - vma->vm_start); mem_vaddr = (struct v4l2_buffer_metadata_s *)vmalloc_user(pages); if (!mem_vaddr) { cif_isp10_pltfrm_pr_err(dev->dev, "vmalloc (%d bytes) failed for %s metadata\n", pages, (stream_id == CIF_ISP10_STREAM_MP) ? "mp" : "sp"); retval = -ENOMEM; goto done; } /* Try to remap memory */ retval = remap_vmalloc_range(vma, mem_vaddr, 0); if (retval < 0) { cif_isp10_pltfrm_pr_err(dev->dev, "mmap: remap failed with error %d. ", retval); vfree(mem_vaddr); goto done; } spin_lock_irqsave(&strm_dev->metadata.spinlock, flags); strm_dev->metadata.d = (unsigned char *)mem_vaddr; spin_unlock_irqrestore(&strm_dev->metadata.spinlock, flags); vma->vm_private_data = (void *)&strm_dev->metadata; done: return retval; } int cif_isp10_get_target_frm_size( struct cif_isp10_device *dev, u32 *target_width, u32 *target_height) { if (dev->sp_stream.state >= CIF_ISP10_STATE_READY) { if ((dev->mp_stream.state >= CIF_ISP10_STATE_READY) && (dev->config.mi_config.mp.output.width > dev->config.mi_config.sp.output.width)) *target_width = dev->config.mi_config.mp.output.width; else *target_width = dev->config.mi_config.sp.output.width; if ((dev->mp_stream.state >= CIF_ISP10_STATE_READY) && (dev->config.mi_config.mp.output.height > dev->config.mi_config.sp.output.height)) *target_height = dev->config.mi_config.mp.output.height; else *target_height = dev->config.mi_config.sp.output.height; } else if (dev->mp_stream.state >= CIF_ISP10_STATE_READY) { *target_width = dev->config.mi_config.mp.output.width; *target_height = dev->config.mi_config.mp.output.height; } else { cif_isp10_pltfrm_pr_err(dev->dev, "cannot get target frame size, no path ready, state(mp[%d]-sp[%d])\n", dev->mp_stream.state, dev->sp_stream.state); return -EFAULT; } return 0; } int cif_isp10_calc_isp_cropping( struct cif_isp10_device *dev, u32 *width, u32 *height, u32 *h_offs, u32 *v_offs) { int ret = 0; u32 input_width; u32 input_height; u32 target_width; u32 target_height; if (IS_ERR_OR_NULL(dev->config.isp_config.input)) { cif_isp10_pltfrm_pr_err(dev->dev, "no input selected for ISP\n"); ret = -EFAULT; goto err; } input_width = dev->config.isp_config.input->defrect.width; input_height = dev->config.isp_config.input->defrect.height; ret = cif_isp10_get_target_frm_size(dev, &target_width, &target_height); if (IS_ERR_VALUE(ret)) goto err; *width = input_width; *height = input_width * target_height / target_width; *v_offs = 0; *h_offs = 0; *height &= ~1; if (*height < input_height) /* vertical cropping */ *v_offs = (input_height - *height) >> 1; else if (*height > input_height) { /* horizontal cropping */ *height = input_height; *width = input_height * target_width / target_height; *width &= ~1; *h_offs = (input_width - *width) >> 1; } return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with err %d\n", ret); return ret; } int cif_isp10_calc_min_out_buff_size( struct cif_isp10_device *dev, enum cif_isp10_stream_id stream_id, u32 *size, bool payload) { int ret = 0; enum cif_isp10_pix_fmt pix_fmt; u32 llength; u32 height; u32 bpp; struct cif_isp10_mi_path_config *mi_path; struct cif_isp10_stream *stream; cif_isp10_pltfrm_pr_dbg(NULL, "%s\n", cif_isp10_stream_id_string(stream_id)); if (stream_id == CIF_ISP10_STREAM_SP) { mi_path = &dev->config.mi_config.sp; stream = &dev->sp_stream; } else if (stream_id == CIF_ISP10_STREAM_MP) { mi_path = &dev->config.mi_config.mp; stream = &dev->mp_stream; } else if (stream_id == CIF_ISP10_STREAM_DMA) { mi_path = &dev->config.mi_config.dma; stream = &dev->dma_stream; } else { cif_isp10_pltfrm_pr_err(dev->dev, "cannot calculate buffer size for this stream (%s)\n", cif_isp10_stream_id_string(stream_id)); ret = -EINVAL; goto err; } if (stream->state < CIF_ISP10_STATE_READY) { cif_isp10_pltfrm_pr_err(NULL, "cannot calculate buffer size, %s stream not ready\n", cif_isp10_stream_id_string(stream_id)); ret = -EINVAL; goto err; } pix_fmt = mi_path->output.pix_fmt; llength = mi_path->llength; height = mi_path->output.height; cif_isp10_pltfrm_pr_dbg(NULL, "mi_path->llength: 0x%x\n", mi_path->llength); if ((CIF_ISP10_PIX_FMT_IS_RAW_BAYER(pix_fmt) && CIF_ISP10_PIX_FMT_GET_BPP(pix_fmt) > 8) || payload) /* RAW input > 8BPP is stored with 16BPP by MI */ bpp = 16; else bpp = CIF_ISP10_PIX_FMT_GET_BPP(pix_fmt); *size = llength * height * bpp / 8; cif_isp10_pltfrm_pr_dbg(NULL, "calculated buffer size: %d\n", *size); return 0; err: cif_isp10_pltfrm_pr_err(dev->dev, "failed with err %d\n", ret); return ret; } int cif_isp10_s_ctrl( struct cif_isp10_device *dev, const enum cif_isp10_cid id, int val) { cif_isp10_pltfrm_pr_dbg(NULL, "id %d, val %d\n", id, val); switch (id) { case CIF_ISP10_CID_SUPER_IMPOSE: dev->config.isp_config.si_enable = val; break; case CIF_ISP10_CID_IMAGE_EFFECT: if ((u32)val > CIF_ISP10_IE_NONE) { cif_isp10_pltfrm_pr_err(NULL, "unknown/unsupported image effect %d\n", val); return -EINVAL; } dev->config.isp_config.ie_config.effect = val; break; case CIF_ISP10_CID_JPEG_QUALITY: if ((u32)val > 100) { cif_isp10_pltfrm_pr_err(NULL, "JPEG quality (%d) must be in [1..100]\n", val); return -EINVAL; } dev->config.jpeg_config.ratio = val; break; case CIF_ISP10_CID_FLASH_MODE: if ((u32)val > CIF_ISP10_FLASH_MODE_TORCH) { cif_isp10_pltfrm_pr_err(NULL, "unknown/unsupported flash mode (%d)\n", val); return -EINVAL; } dev->config.flash_mode = val; cif_isp10_img_src_s_ctrl( dev->img_src, CIF_ISP10_CID_FLASH_MODE, dev->config.flash_mode); if (dev->config.flash_mode == CIF_ISP10_FLASH_MODE_FLASH) { do_gettimeofday(&dev->flash_t.mainflash_start_t); dev->flash_t.mainflash_start_t.tv_usec += dev->flash_t.flash_turn_on_time; dev->flash_t.mainflash_end_t = dev->flash_t.mainflash_start_t; dev->flash_t.mainflash_end_t.tv_sec += dev->flash_t.flash_on_timeout; } else if (dev->config.flash_mode == CIF_ISP10_FLASH_MODE_TORCH) { do_gettimeofday(&dev->flash_t.preflash_start_t); dev->flash_t.preflash_end_t = dev->flash_t.preflash_start_t; dev->flash_t.preflash_end_t.tv_sec = 0x00; dev->flash_t.preflash_end_t.tv_usec = 0x00; } else if (dev->config.flash_mode == CIF_ISP10_FLASH_MODE_OFF) { do_gettimeofday(&dev->flash_t.preflash_end_t); if (dev->flash_t.preflash_end_t.tv_sec * 1000000 + dev->flash_t.preflash_end_t.tv_usec < dev->flash_t.mainflash_end_t.tv_sec * 1000000 + dev->flash_t.mainflash_end_t.tv_usec) { dev->flash_t.mainflash_end_t = dev->flash_t.preflash_end_t; } } break; case CIF_ISP10_CID_WB_TEMPERATURE: case CIF_ISP10_CID_ANALOG_GAIN: case CIF_ISP10_CID_EXPOSURE_TIME: case CIF_ISP10_CID_BLACK_LEVEL: case CIF_ISP10_CID_AUTO_N_PRESET_WHITE_BALANCE: case CIF_ISP10_CID_SCENE_MODE: case CIF_ISP10_CID_AUTO_FPS: case CIF_ISP10_CID_HFLIP: case CIF_ISP10_CID_VFLIP: case CIF_ISP10_CID_TEST_PATTERN: return cif_isp10_img_src_s_ctrl(dev->img_src, id, val); case CIF_ISP10_CID_FOCUS_ABSOLUTE: return cif_isp10_s_vcm(dev, id, val); default: cif_isp10_pltfrm_pr_err(dev->dev, "unknown/unsupported control %d\n", id); return -EINVAL; } return 0; } /* end */ enum { isp_data_loss = 0, isp_pic_size_err, mipi_fifo_err, dphy_err_sot, dphy_err_sot_sync, dphy_err_eot_sync, dphy_err_ctrl, csi_err_protocol, csi_ecc1_err, csi_ecc2_err, csi_cs_err, }; static void cif_isp10_hw_restart(struct cif_isp10_device *dev) { cif_isp10_pltfrm_pr_dbg(NULL, "\n"); cif_isp10_hw_errors[isp_pic_size_err].count = 0; cif_isp10_hw_errors[isp_data_loss].count = 0; cif_isp10_hw_errors[csi_err_protocol].count = 0; cif_isp10_hw_errors[csi_ecc1_err].count = 0; cif_isp10_hw_errors[csi_ecc2_err].count = 0; cif_isp10_hw_errors[csi_cs_err].count = 0; cif_iowrite32(0x00000841, dev->config.base_addr + CIF_IRCL); cif_iowrite32(0x0, dev->config.base_addr + CIF_IRCL); /* enable mipi frame end interrupt */ cif_iowrite32(CIF_MIPI_FRAME_END, dev->config.base_addr + CIF_MIPI_IMSC); /* enable csi protocol errors interrupts */ cif_iowrite32OR(CIF_MIPI_ERR_CSI, dev->config.base_addr + CIF_MIPI_IMSC); /* enable dphy errors interrupts */ cif_iowrite32OR(CIF_MIPI_ERR_DPHY, dev->config.base_addr + CIF_MIPI_IMSC); /* add fifo error */ cif_iowrite32OR(CIF_MIPI_SYNC_FIFO_OVFLW(3), dev->config.base_addr + CIF_MIPI_IMSC); /* add data overflow_error */ cif_iowrite32OR(CIF_MIPI_ADD_DATA_OVFLW, dev->config.base_addr + CIF_MIPI_IMSC); cif_iowrite32(0x0, dev->config.base_addr + CIF_MI_MP_Y_OFFS_CNT_INIT); cif_iowrite32(0x0, dev->config.base_addr + CIF_MI_MP_CR_OFFS_CNT_INIT); cif_iowrite32(0x0, dev->config.base_addr + CIF_MI_MP_CB_OFFS_CNT_INIT); cif_iowrite32(0x0, dev->config.base_addr + CIF_MI_SP_Y_OFFS_CNT_INIT); cif_iowrite32(0x0, dev->config.base_addr + CIF_MI_SP_CR_OFFS_CNT_INIT); cif_iowrite32(0x0, dev->config.base_addr + CIF_MI_SP_CB_OFFS_CNT_INIT); cif_iowrite32OR(CIF_MI_CTRL_INIT_OFFSET_EN, dev->config.base_addr + CIF_MI_CTRL); /* Enable ISP ! */ cif_iowrite32OR(CIF_ISP_CTRL_ISP_CFG_UPD | CIF_ISP_CTRL_ISP_INFORM_ENABLE | CIF_ISP_CTRL_ISP_ENABLE, dev->config.base_addr + CIF_ISP_CTRL); /* enable MIPI */ cif_iowrite32OR(CIF_MIPI_CTRL_OUTPUT_ENA, dev->config.base_addr + CIF_MIPI_CTRL); } int cif_isp10_mipi_isr(unsigned int mipi_mis, void *cntxt) { struct cif_isp10_device *dev = (struct cif_isp10_device *)cntxt; unsigned int mipi_ris = 0; mipi_ris = cif_ioread32(dev->config.base_addr + CIF_MIPI_RIS); mipi_mis = cif_ioread32(dev->config.base_addr + CIF_MIPI_MIS); cif_isp10_pltfrm_rtrace_printf(dev->dev, "MIPI_MIS %08x, MIPI_RIS %08x, MIPI_IMSC %08x\n", mipi_mis, mipi_ris, cif_ioread32(dev->config.base_addr + CIF_MIPI_IMSC)); cif_iowrite32(~0, dev->config.base_addr + CIF_MIPI_ICR); if (mipi_mis & CIF_MIPI_ERR_DPHY) { cif_isp10_pltfrm_pr_warn(dev->dev, "CIF_MIPI_ERR_DPHY: 0x%x\n", mipi_mis); /* * Disable DPHY errctrl interrupt, because this dphy * erctrl signal is assert and until the next changes * in line state. This time is may be too long and cpu * is hold in this interrupt. */ if (mipi_mis & CIF_MIPI_ERR_CTRL(3)) cif_iowrite32AND(~(CIF_MIPI_ERR_CTRL(3)), dev->config.base_addr + CIF_MIPI_IMSC); } if (mipi_mis & CIF_MIPI_ERR_CSI) { cif_isp10_pltfrm_pr_warn(dev->dev, "CIF_MIPI_ERR_CSI: 0x%x\n", mipi_mis); } if (mipi_mis & CIF_MIPI_SYNC_FIFO_OVFLW(3)) { cif_isp10_pltfrm_pr_warn(dev->dev, "CIF_MIPI_SYNC_FIFO_OVFLW: 0x%x\n", mipi_mis); } if (mipi_mis == CIF_MIPI_FRAME_END) { /* * Enable DPHY errctrl interrupt again, if mipi have receive * the whole frame without any error. */ cif_iowrite32OR(CIF_MIPI_ERR_CTRL(3), dev->config.base_addr + CIF_MIPI_IMSC); } mipi_mis = cif_ioread32(dev->config.base_addr + CIF_MIPI_MIS); if (mipi_mis) { cif_isp10_pltfrm_pr_err(dev->dev, "mipi_mis icr err: 0x%x\n", mipi_mis); } return 0; } /* ======================================================================== */ int cif_isp10_isp_isr(unsigned int isp_mis, void *cntxt) { struct cif_isp10_device *dev = (struct cif_isp10_device *)cntxt; unsigned int isp_mis_tmp = 0; unsigned int isp_err = 0; struct timeval tv; cif_isp10_pltfrm_pr_dbg(dev->dev, "ISP_MIS %08x, ISP_RIS %08x, ISP_IMSC %08x\n", isp_mis, cif_ioread32(dev->config.base_addr + CIF_ISP_RIS), cif_ioread32(dev->config.base_addr + CIF_ISP_IMSC)); if (isp_mis & CIF_ISP_OFF) { cif_iowrite32(CIF_ISP_OFF, dev->config.base_addr + CIF_ISP_ICR); cif_isp10_pltfrm_pr_dbg(dev->dev, "ISP Stop Interrupt!\n"); dev->isp_stop_flags = 1; wake_up_interruptible(&dev->isp_stop_wait); return 0; } if (isp_mis & CIF_ISP_V_START) { struct cif_isp10_img_src_exp *exp; do_gettimeofday(&tv); dev->b_isp_frame_in = false; dev->b_mi_frame_end = false; cifisp_v_start(&dev->isp_dev, &tv); /* BIT 2(current field information): 0 = odd, 1 = even */ if (PLTFRM_CAM_ITF_INTERLACE(dev->config.cam_itf.type)) dev->config.mi_config.sp.field_flag = (cif_ioread32(dev->config.base_addr + CIF_ISP_FLAGS_SHD) & CIF_ISP_FLAGS_SHD_FIELD_INFO) >> CIF_ISP_FLAGS_SHD_FIELD_BIT; cif_iowrite32(CIF_ISP_V_START, dev->config.base_addr + CIF_ISP_ICR); isp_mis_tmp = cif_ioread32(dev->config.base_addr + CIF_ISP_MIS); if (isp_mis_tmp & CIF_ISP_V_START) cif_isp10_pltfrm_pr_err(dev->dev, "isp icr v_statr err: 0x%x\n", isp_mis_tmp); if (!dev->config.mi_config.async_updt) { cif_iowrite32OR(CIF_ISP_CTRL_ISP_GEN_CFG_UPD, dev->config.base_addr + CIF_ISP_CTRL); cif_isp10_pltfrm_pr_dbg(NULL, "CIF_ISP_CTRL_ISP_GEN_CFG_UPD\n"); } if (dev->sof_event) dev->sof_event(dev, dev->isp_dev.frame_id >> 1); spin_lock(&dev->img_src_exps.lock); if (!list_empty(&dev->img_src_exps.list)) { exp = list_first_entry(&dev->img_src_exps.list, struct cif_isp10_img_src_exp, list); list_del(&exp->list); } else { exp = NULL; } spin_unlock(&dev->img_src_exps.lock); if (exp) cif_isp10_vs_work_cmd(dev, CIF_ISP10_VS_EXP, (void *)exp); } if (isp_mis & CIF_ISP_FRAME_IN) { do_gettimeofday(&tv); cif_iowrite32(CIF_ISP_FRAME_IN, dev->config.base_addr + CIF_ISP_ICR); cifisp_frame_in(&dev->isp_dev, &tv); } if ((isp_mis & (CIF_ISP_DATA_LOSS | CIF_ISP_PIC_SIZE_ERROR))) { dev->sp_stream.stall = true; dev->mp_stream.stall = true; if ((isp_mis & CIF_ISP_PIC_SIZE_ERROR)) { /* Clear pic_size_error */ cif_iowrite32(CIF_ISP_PIC_SIZE_ERROR, dev->config.base_addr + CIF_ISP_ICR); cif_isp10_hw_errors[isp_pic_size_err].count++; isp_err = cif_ioread32(dev->config.base_addr + CIF_ISP_ERR); dev_err(dev->dev, "CIF_ISP_PIC_SIZE_ERROR (0x%08x)", isp_err); cif_iowrite32(isp_err, dev->config.base_addr + CIF_ISP_ERR_CLR); } else if ((isp_mis & CIF_ISP_DATA_LOSS)) { /* Clear data_loss */ cif_iowrite32(CIF_ISP_DATA_LOSS, dev->config.base_addr + CIF_ISP_ICR); cif_isp10_hw_errors[isp_data_loss].count++; dev_err(dev->dev, "CIF_ISP_DATA_LOSS\n"); cif_iowrite32(CIF_ISP_DATA_LOSS, dev->config.base_addr + CIF_ISP_ICR); } spin_lock(&dev->isp_state_lock); if (dev->isp_state == CIF_ISP10_STATE_RUNNING) { /* Stop ISP */ cif_iowrite32AND(~CIF_ISP_CTRL_ISP_INFORM_ENABLE & ~CIF_ISP_CTRL_ISP_ENABLE, dev->config.base_addr + CIF_ISP_CTRL); /* isp_update */ cif_iowrite32OR(CIF_ISP_CTRL_ISP_CFG_UPD, dev->config.base_addr + CIF_ISP_CTRL); cif_isp10_hw_restart(dev); } spin_unlock(&dev->isp_state_lock); } if (isp_mis & CIF_ISP_FRAME_IN) { cif_iowrite32(CIF_ISP_FRAME_IN, dev->config.base_addr + CIF_ISP_ICR); isp_mis_tmp = cif_ioread32(dev->config.base_addr + CIF_ISP_MIS); if (isp_mis_tmp & CIF_ISP_FRAME_IN) cif_isp10_pltfrm_pr_err(dev->dev, "isp icr frame_in err: 0x%x\n", isp_mis_tmp); /* restart MI if CIF has run out of buffers */ if (!CIF_ISP10_INP_IS_DMA(dev->config.input_sel) && !CIF_ISP10_MI_IS_BUSY(dev) && !dev->config.jpeg_config.busy && (dev->config.mi_config.async_updt || (!dev->sp_stream.next_buf && !dev->mp_stream.next_buf))){ u32 mi_isr = 0; if (dev->sp_stream.state == CIF_ISP10_STATE_STREAMING) mi_isr |= CIF_MI_SP_FRAME; if (dev->mp_stream.state == CIF_ISP10_STATE_STREAMING) mi_isr |= CIF_MI_MP_FRAME; cif_iowrite32(mi_isr, dev->config.base_addr + CIF_MI_ISR); } } if (isp_mis & CIF_ISP_FRAME) { /* Clear Frame In (ISP) */ cif_iowrite32(CIF_ISP_FRAME, dev->config.base_addr + CIF_ISP_ICR); isp_mis_tmp = cif_ioread32(dev->config.base_addr + CIF_ISP_MIS); if (isp_mis_tmp & CIF_ISP_FRAME) cif_isp10_pltfrm_pr_err(dev->dev, "isp icr frame end err: 0x%x\n", isp_mis_tmp); if (dev->b_mi_frame_end) cif_isp10_update_ism_dcr_rsz(dev); dev->b_isp_frame_in = true; } cifisp_isp_isr(&dev->isp_dev, isp_mis); return 0; } /* ======================================================================== */ void init_output_formats(void) { unsigned int i = 0; int ret = 0; /* RF*/ int xgold_num_format = 0; /*RF*/ xgold_num_format = (sizeof(cif_isp10_output_format) / sizeof(struct cif_isp10_fmt)); for (i = 0; i < xgold_num_format; i++) { struct v4l2_fmtdesc fmtdesc; memset(&fmtdesc, 0, sizeof(fmtdesc)); fmtdesc.index = i; fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; strlcpy((&fmtdesc)->description, cif_isp10_output_format[(&fmtdesc)->index].name, sizeof((&fmtdesc)->description)); (&fmtdesc)->pixelformat = cif_isp10_output_format[(&fmtdesc)->index].fourcc; (&fmtdesc)->flags = cif_isp10_output_format[(&fmtdesc)->index].flags; if (ret < 0) break; output_formats[i] = fmtdesc; } } int get_cif_isp10_output_format_size(void) { return sizeof(cif_isp10_output_format) / sizeof(struct cif_isp10_fmt); } struct cif_isp10_fmt *get_cif_isp10_output_format(int index) { struct cif_isp10_fmt *fmt = NULL; if ((index >= 0) && (index < get_cif_isp10_output_format_size())) fmt = &cif_isp10_output_format[index]; return fmt; } struct v4l2_fmtdesc *get_cif_isp10_output_format_desc(int index) { struct v4l2_fmtdesc *desc = NULL; if ((index >= 0) && (index < get_cif_isp10_output_format_desc_size())) desc = &output_formats[index]; return desc; } int get_cif_isp10_output_format_desc_size(void) { return ARRAY_SIZE(cif_isp10_output_format); }