2025-05-10 21:58:58 +08:00

7229 lines
203 KiB
C

/*
*************************************************************************
* 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 <linux/videodev2.h>
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-dma-sg.h>
#include "cif_isp10_regs.h"
#include "cif_isp10.h"
#include <linux/pm_runtime.h>
#include <linux/vmalloc.h>
#include <dt-bindings/soc/rockchip-system-status.h>
#include <soc/rockchip/rockchip-system-status.h>
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);
}