817 lines
20 KiB
C
817 lines
20 KiB
C
/*
|
|
* Copyright 2008-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
|
*/
|
|
|
|
/*
|
|
* The code contained herein is licensed under the GNU General Public
|
|
* License. You may obtain a copy of the GNU General Public License
|
|
* Version 2 or later at the following locations:
|
|
*
|
|
* http://www.opensource.org/licenses/gpl-license.html
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
|
|
/*!
|
|
* @file ipu_capture.c
|
|
*
|
|
* @brief IPU capture dase functions
|
|
*
|
|
* @ingroup IPU
|
|
*/
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
#include <linux/ipu-v3.h>
|
|
#include <linux/module.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "ipu_prv.h"
|
|
#include "ipu_regs.h"
|
|
|
|
/*!
|
|
* _ipu_csi_mclk_set
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param pixel_clk desired pixel clock frequency in Hz
|
|
* @param csi csi 0 or csi 1
|
|
*
|
|
* @return Returns 0 on success or negative error code on fail
|
|
*/
|
|
int _ipu_csi_mclk_set(struct ipu_soc *ipu, uint32_t pixel_clk, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
int32_t div_ratio;
|
|
|
|
div_ratio = (clk_get_rate(ipu->ipu_clk) / pixel_clk) - 1;
|
|
|
|
if (div_ratio > 0xFF || div_ratio < 0) {
|
|
dev_dbg(ipu->dev, "value of pixel_clk extends normal range\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
|
|
temp &= ~CSI_SENS_CONF_DIVRATIO_MASK;
|
|
ipu_csi_write(ipu, csi, temp |
|
|
(div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT),
|
|
CSI_SENS_CONF);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* ipu_csi_init_interface
|
|
* Sets initial values for the CSI registers.
|
|
* The width and height of the sensor and the actual frame size will be
|
|
* set to the same values.
|
|
* @param ipu ipu handler
|
|
* @param width Sensor width
|
|
* @param height Sensor height
|
|
* @param pixel_fmt pixel format
|
|
* @param cfg_param ipu_csi_signal_cfg_t structure
|
|
* @param csi csi 0 or csi 1
|
|
*
|
|
* @return 0 for success, -EINVAL for error
|
|
*/
|
|
int32_t
|
|
ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height,
|
|
uint32_t pixel_fmt, ipu_csi_signal_cfg_t cfg_param)
|
|
{
|
|
uint32_t data = 0;
|
|
uint32_t csi = cfg_param.csi;
|
|
|
|
/* Set SENS_DATA_FORMAT bits (8, 9 and 10)
|
|
RGB or YUV444 is 0 which is current value in data so not set
|
|
explicitly
|
|
This is also the default value if attempts are made to set it to
|
|
something invalid. */
|
|
switch (pixel_fmt) {
|
|
case IPU_PIX_FMT_YUYV:
|
|
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
|
|
break;
|
|
case IPU_PIX_FMT_UYVY:
|
|
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
|
|
break;
|
|
case IPU_PIX_FMT_RGB24:
|
|
case IPU_PIX_FMT_BGR24:
|
|
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
|
|
break;
|
|
case IPU_PIX_FMT_GENERIC:
|
|
case IPU_PIX_FMT_GENERIC_16:
|
|
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
|
|
break;
|
|
case IPU_PIX_FMT_RGB565:
|
|
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
|
|
break;
|
|
case IPU_PIX_FMT_RGB555:
|
|
cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Set the CSI_SENS_CONF register remaining fields */
|
|
data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
|
|
cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
|
|
cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT |
|
|
cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT |
|
|
cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT |
|
|
cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT |
|
|
cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT |
|
|
cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT |
|
|
cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT |
|
|
cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT |
|
|
cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT;
|
|
|
|
_ipu_get(ipu);
|
|
|
|
mutex_lock(&ipu->mutex_lock);
|
|
|
|
ipu_csi_write(ipu, csi, data, CSI_SENS_CONF);
|
|
|
|
/* Setup sensor frame size */
|
|
ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE);
|
|
|
|
/* Set CCIR registers */
|
|
if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) {
|
|
ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
|
|
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
|
|
} else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) {
|
|
if (width == 720 && height == 625) {
|
|
/* PAL case */
|
|
/*
|
|
* Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
|
|
* Field0ActiveEnd = 0x4, Field0ActiveStart = 0
|
|
*/
|
|
ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1);
|
|
/*
|
|
* Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
|
|
* Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
|
|
*/
|
|
ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2);
|
|
|
|
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
|
|
|
|
} else if (width == 720 && height == 525) {
|
|
/* NTSC case */
|
|
/*
|
|
* Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
|
|
* Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
|
|
*/
|
|
ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_1);
|
|
/*
|
|
* Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
|
|
* Field1ActiveEnd = 0x4, Field1ActiveStart = 0
|
|
*/
|
|
ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2);
|
|
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
|
|
} else {
|
|
dev_err(ipu->dev, "Unsupported CCIR656 interlaced "
|
|
"video mode\n");
|
|
mutex_unlock(&ipu->mutex_lock);
|
|
_ipu_put(ipu);
|
|
return -EINVAL;
|
|
}
|
|
_ipu_csi_ccir_err_detection_enable(ipu, csi);
|
|
} else if ((cfg_param.clk_mode ==
|
|
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) ||
|
|
(cfg_param.clk_mode ==
|
|
IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) ||
|
|
(cfg_param.clk_mode ==
|
|
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) ||
|
|
(cfg_param.clk_mode ==
|
|
IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) {
|
|
ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1);
|
|
ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3);
|
|
_ipu_csi_ccir_err_detection_enable(ipu, csi);
|
|
} else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) ||
|
|
(cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) {
|
|
_ipu_csi_ccir_err_detection_disable(ipu, csi);
|
|
}
|
|
|
|
dev_dbg(ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
|
|
ipu_csi_read(ipu, csi, CSI_SENS_CONF));
|
|
dev_dbg(ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
|
|
ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE));
|
|
|
|
mutex_unlock(&ipu->mutex_lock);
|
|
|
|
_ipu_put(ipu);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ipu_csi_init_interface);
|
|
|
|
/*!
|
|
* ipu_csi_get_sensor_protocol
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param csi csi 0 or csi 1
|
|
*
|
|
* @return Returns sensor protocol
|
|
*/
|
|
int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi)
|
|
{
|
|
int ret;
|
|
_ipu_get(ipu);
|
|
ret = (ipu_csi_read(ipu, csi, CSI_SENS_CONF) &
|
|
CSI_SENS_CONF_SENS_PRTCL_MASK) >>
|
|
CSI_SENS_CONF_SENS_PRTCL_SHIFT;
|
|
_ipu_put(ipu);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(ipu_csi_get_sensor_protocol);
|
|
|
|
/*!
|
|
* ipu_csi_enable_mclk
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param csi csi 0 or csi 1
|
|
* @param flag true to enable mclk, false to disable mclk
|
|
* @param wait true to wait 100ms make clock stable, false not wait
|
|
*
|
|
* @return Returns 0 on success
|
|
*/
|
|
int ipu_csi_enable_mclk(struct ipu_soc *ipu, int csi, bool flag, bool wait)
|
|
{
|
|
/* Return immediately if there is no csi_clk to manage */
|
|
if (ipu->csi_clk[csi] == NULL)
|
|
return 0;
|
|
|
|
if (flag) {
|
|
clk_enable(ipu->csi_clk[csi]);
|
|
if (wait == true)
|
|
msleep(10);
|
|
} else {
|
|
clk_disable(ipu->csi_clk[csi]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(ipu_csi_enable_mclk);
|
|
|
|
/*!
|
|
* ipu_csi_get_window_size
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param width pointer to window width
|
|
* @param height pointer to window height
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t *height, uint32_t csi)
|
|
{
|
|
uint32_t reg;
|
|
|
|
_ipu_get(ipu);
|
|
|
|
mutex_lock(&ipu->mutex_lock);
|
|
|
|
reg = ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE);
|
|
*width = (reg & 0xFFFF) + 1;
|
|
*height = (reg >> 16 & 0xFFFF) + 1;
|
|
|
|
mutex_unlock(&ipu->mutex_lock);
|
|
|
|
_ipu_put(ipu);
|
|
}
|
|
EXPORT_SYMBOL(ipu_csi_get_window_size);
|
|
|
|
/*!
|
|
* ipu_csi_set_window_size
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param width window width
|
|
* @param height window height
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi)
|
|
{
|
|
_ipu_get(ipu);
|
|
|
|
mutex_lock(&ipu->mutex_lock);
|
|
|
|
ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE);
|
|
|
|
mutex_unlock(&ipu->mutex_lock);
|
|
|
|
_ipu_put(ipu);
|
|
}
|
|
EXPORT_SYMBOL(ipu_csi_set_window_size);
|
|
|
|
/*!
|
|
* ipu_csi_set_window_pos
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param left uint32 window x start
|
|
* @param top uint32 window y start
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
|
|
_ipu_get(ipu);
|
|
|
|
mutex_lock(&ipu->mutex_lock);
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
|
|
temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK);
|
|
temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT));
|
|
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
|
|
|
|
mutex_unlock(&ipu->mutex_lock);
|
|
|
|
_ipu_put(ipu);
|
|
}
|
|
EXPORT_SYMBOL(ipu_csi_set_window_pos);
|
|
|
|
/*!
|
|
* _ipu_csi_horizontal_downsize_enable
|
|
* Enable horizontal downsizing(decimation) by 2.
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void _ipu_csi_horizontal_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
|
|
temp |= CSI_HORI_DOWNSIZE_EN;
|
|
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_horizontal_downsize_disable
|
|
* Disable horizontal downsizing(decimation) by 2.
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void _ipu_csi_horizontal_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
|
|
temp &= ~CSI_HORI_DOWNSIZE_EN;
|
|
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_vertical_downsize_enable
|
|
* Enable vertical downsizing(decimation) by 2.
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void _ipu_csi_vertical_downsize_enable(struct ipu_soc *ipu, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
|
|
temp |= CSI_VERT_DOWNSIZE_EN;
|
|
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_vertical_downsize_disable
|
|
* Disable vertical downsizing(decimation) by 2.
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void _ipu_csi_vertical_downsize_disable(struct ipu_soc *ipu, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL);
|
|
temp &= ~CSI_VERT_DOWNSIZE_EN;
|
|
ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL);
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_set_test_generator
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param active 1 for active and 0 for inactive
|
|
* @param r_value red value for the generated pattern of even pixel
|
|
* @param g_value green value for the generated pattern of even
|
|
* pixel
|
|
* @param b_value blue value for the generated pattern of even pixel
|
|
* @param pixel_clk desired pixel clock frequency in Hz
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void _ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value,
|
|
uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_TST_CTRL);
|
|
|
|
if (active == false) {
|
|
temp &= ~CSI_TEST_GEN_MODE_EN;
|
|
ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
|
|
} else {
|
|
/* Set sensb_mclk div_ratio*/
|
|
_ipu_csi_mclk_set(ipu, pix_clk, csi);
|
|
|
|
temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK |
|
|
CSI_TEST_GEN_B_MASK);
|
|
temp |= CSI_TEST_GEN_MODE_EN;
|
|
temp |= (r_value << CSI_TEST_GEN_R_SHIFT) |
|
|
(g_value << CSI_TEST_GEN_G_SHIFT) |
|
|
(b_value << CSI_TEST_GEN_B_SHIFT);
|
|
ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_ccir_err_detection_en
|
|
* Enable error detection and correction for
|
|
* CCIR interlaced mode with protection bit.
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
|
|
temp |= CSI_CCIR_ERR_DET_EN;
|
|
ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
|
|
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_ccir_err_detection_disable
|
|
* Disable error detection and correction for
|
|
* CCIR interlaced mode with protection bit.
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param csi csi 0 or csi 1
|
|
*/
|
|
void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1);
|
|
temp &= ~CSI_CCIR_ERR_DET_EN;
|
|
ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1);
|
|
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_set_mipi_di
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param num MIPI data identifier 0-3 handled by CSI
|
|
* @param di_val data identifier value
|
|
* @param csi csi 0 or csi 1
|
|
*
|
|
* @return Returns 0 on success or negative error code on fail
|
|
*/
|
|
int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
int retval = 0;
|
|
|
|
if (di_val > 0xFFL) {
|
|
retval = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_MIPI_DI);
|
|
|
|
switch (num) {
|
|
case IPU_CSI_MIPI_DI0:
|
|
temp &= ~CSI_MIPI_DI0_MASK;
|
|
temp |= (di_val << CSI_MIPI_DI0_SHIFT);
|
|
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
|
|
break;
|
|
case IPU_CSI_MIPI_DI1:
|
|
temp &= ~CSI_MIPI_DI1_MASK;
|
|
temp |= (di_val << CSI_MIPI_DI1_SHIFT);
|
|
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
|
|
break;
|
|
case IPU_CSI_MIPI_DI2:
|
|
temp &= ~CSI_MIPI_DI2_MASK;
|
|
temp |= (di_val << CSI_MIPI_DI2_SHIFT);
|
|
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
|
|
break;
|
|
case IPU_CSI_MIPI_DI3:
|
|
temp &= ~CSI_MIPI_DI3_MASK;
|
|
temp |= (di_val << CSI_MIPI_DI3_SHIFT);
|
|
ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI);
|
|
break;
|
|
default:
|
|
retval = -EINVAL;
|
|
}
|
|
|
|
err:
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_set_skip_isp
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param skip select frames to be skipped and set the
|
|
* correspond bits to 1
|
|
* @param max_ratio number of frames in a skipping set and the
|
|
* maximum value of max_ratio is 5
|
|
* @param csi csi 0 or csi 1
|
|
*
|
|
* @return Returns 0 on success or negative error code on fail
|
|
*/
|
|
int _ipu_csi_set_skip_isp(struct ipu_soc *ipu, uint32_t skip, uint32_t max_ratio, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
int retval = 0;
|
|
|
|
if (max_ratio > 5) {
|
|
retval = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_SKIP);
|
|
temp &= ~(CSI_MAX_RATIO_SKIP_ISP_MASK | CSI_SKIP_ISP_MASK);
|
|
temp |= (max_ratio << CSI_MAX_RATIO_SKIP_ISP_SHIFT) |
|
|
(skip << CSI_SKIP_ISP_SHIFT);
|
|
ipu_csi_write(ipu, csi, temp, CSI_SKIP);
|
|
|
|
err:
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_set_skip_smfc
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param skip select frames to be skipped and set the
|
|
* correspond bits to 1
|
|
* @param max_ratio number of frames in a skipping set and the
|
|
* maximum value of max_ratio is 5
|
|
* @param id csi to smfc skipping id
|
|
* @param csi csi 0 or csi 1
|
|
*
|
|
* @return Returns 0 on success or negative error code on fail
|
|
*/
|
|
int _ipu_csi_set_skip_smfc(struct ipu_soc *ipu, uint32_t skip,
|
|
uint32_t max_ratio, uint32_t id, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
int retval = 0;
|
|
|
|
if (max_ratio > 5 || id > 3) {
|
|
retval = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
temp = ipu_csi_read(ipu, csi, CSI_SKIP);
|
|
temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK |
|
|
CSI_SKIP_SMFC_MASK);
|
|
temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) |
|
|
(id << CSI_ID_2_SKIP_SHIFT) |
|
|
(skip << CSI_SKIP_SMFC_SHIFT);
|
|
ipu_csi_write(ipu, csi, temp, CSI_SKIP);
|
|
|
|
err:
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
* _ipu_smfc_init
|
|
* Map CSI frames to IDMAC channels.
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param channel IDMAC channel 0-3
|
|
* @param mipi_id mipi id number 0-3
|
|
* @param csi csi0 or csi1
|
|
*/
|
|
void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_smfc_read(ipu, SMFC_MAP);
|
|
|
|
switch (channel) {
|
|
case CSI_MEM0:
|
|
temp &= ~SMFC_MAP_CH0_MASK;
|
|
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT;
|
|
break;
|
|
case CSI_MEM1:
|
|
temp &= ~SMFC_MAP_CH1_MASK;
|
|
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT;
|
|
break;
|
|
case CSI_MEM2:
|
|
temp &= ~SMFC_MAP_CH2_MASK;
|
|
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT;
|
|
break;
|
|
case CSI_MEM3:
|
|
temp &= ~SMFC_MAP_CH3_MASK;
|
|
temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
ipu_smfc_write(ipu, temp, SMFC_MAP);
|
|
}
|
|
|
|
/*!
|
|
* _ipu_smfc_set_wmc
|
|
* Caution: The number of required channels, the enabled channels
|
|
* and the FIFO size per channel are configured restrictedly.
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param channel IDMAC channel 0-3
|
|
* @param set set 1 or clear 0
|
|
* @param level water mark level when FIFO is on the
|
|
* relative size
|
|
*/
|
|
void _ipu_smfc_set_wmc(struct ipu_soc *ipu, ipu_channel_t channel, bool set, uint32_t level)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_smfc_read(ipu, SMFC_WMC);
|
|
|
|
switch (channel) {
|
|
case CSI_MEM0:
|
|
if (set == true) {
|
|
temp &= ~SMFC_WM0_SET_MASK;
|
|
temp |= level << SMFC_WM0_SET_SHIFT;
|
|
} else {
|
|
temp &= ~SMFC_WM0_CLR_MASK;
|
|
temp |= level << SMFC_WM0_CLR_SHIFT;
|
|
}
|
|
break;
|
|
case CSI_MEM1:
|
|
if (set == true) {
|
|
temp &= ~SMFC_WM1_SET_MASK;
|
|
temp |= level << SMFC_WM1_SET_SHIFT;
|
|
} else {
|
|
temp &= ~SMFC_WM1_CLR_MASK;
|
|
temp |= level << SMFC_WM1_CLR_SHIFT;
|
|
}
|
|
break;
|
|
case CSI_MEM2:
|
|
if (set == true) {
|
|
temp &= ~SMFC_WM2_SET_MASK;
|
|
temp |= level << SMFC_WM2_SET_SHIFT;
|
|
} else {
|
|
temp &= ~SMFC_WM2_CLR_MASK;
|
|
temp |= level << SMFC_WM2_CLR_SHIFT;
|
|
}
|
|
break;
|
|
case CSI_MEM3:
|
|
if (set == true) {
|
|
temp &= ~SMFC_WM3_SET_MASK;
|
|
temp |= level << SMFC_WM3_SET_SHIFT;
|
|
} else {
|
|
temp &= ~SMFC_WM3_CLR_MASK;
|
|
temp |= level << SMFC_WM3_CLR_SHIFT;
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
ipu_smfc_write(ipu, temp, SMFC_WMC);
|
|
}
|
|
|
|
/*!
|
|
* _ipu_smfc_set_burst_size
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param channel IDMAC channel 0-3
|
|
* @param bs burst size of IDMAC channel,
|
|
* the value programmed here shoud be BURST_SIZE-1
|
|
*/
|
|
void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs)
|
|
{
|
|
uint32_t temp;
|
|
|
|
temp = ipu_smfc_read(ipu, SMFC_BS);
|
|
|
|
switch (channel) {
|
|
case CSI_MEM0:
|
|
temp &= ~SMFC_BS0_MASK;
|
|
temp |= bs << SMFC_BS0_SHIFT;
|
|
break;
|
|
case CSI_MEM1:
|
|
temp &= ~SMFC_BS1_MASK;
|
|
temp |= bs << SMFC_BS1_SHIFT;
|
|
break;
|
|
case CSI_MEM2:
|
|
temp &= ~SMFC_BS2_MASK;
|
|
temp |= bs << SMFC_BS2_SHIFT;
|
|
break;
|
|
case CSI_MEM3:
|
|
temp &= ~SMFC_BS3_MASK;
|
|
temp |= bs << SMFC_BS3_SHIFT;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
ipu_smfc_write(ipu, temp, SMFC_BS);
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_init
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param channel IDMAC channel
|
|
* @param csi csi 0 or csi 1
|
|
*
|
|
* @return Returns 0 on success or negative error code on fail
|
|
*/
|
|
int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi)
|
|
{
|
|
uint32_t csi_sens_conf, csi_dest;
|
|
int retval = 0;
|
|
|
|
switch (channel) {
|
|
case CSI_MEM0:
|
|
case CSI_MEM1:
|
|
case CSI_MEM2:
|
|
case CSI_MEM3:
|
|
csi_dest = CSI_DATA_DEST_IDMAC;
|
|
break;
|
|
case CSI_PRP_ENC_MEM:
|
|
case CSI_PRP_VF_MEM:
|
|
csi_dest = CSI_DATA_DEST_IC;
|
|
break;
|
|
default:
|
|
retval = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF);
|
|
csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK;
|
|
ipu_csi_write(ipu, csi, csi_sens_conf | (csi_dest <<
|
|
CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF);
|
|
err:
|
|
return retval;
|
|
}
|
|
|
|
/*!
|
|
* csi_irq_handler
|
|
*
|
|
* @param irq interrupt id
|
|
* @param dev_id pointer to ipu handler
|
|
*
|
|
* @return Returns if irq is handled
|
|
*/
|
|
static irqreturn_t csi_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct ipu_soc *ipu = dev_id;
|
|
struct completion *comp = &ipu->csi_comp;
|
|
|
|
complete(comp);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/*!
|
|
* _ipu_csi_wait4eof
|
|
*
|
|
* @param ipu ipu handler
|
|
* @param channel IDMAC channel
|
|
*
|
|
*/
|
|
void _ipu_csi_wait4eof(struct ipu_soc *ipu, ipu_channel_t channel)
|
|
{
|
|
int ret;
|
|
int irq = 0;
|
|
|
|
if (channel == CSI_MEM0)
|
|
irq = IPU_IRQ_CSI0_OUT_EOF;
|
|
else if (channel == CSI_MEM1)
|
|
irq = IPU_IRQ_CSI1_OUT_EOF;
|
|
else if (channel == CSI_MEM2)
|
|
irq = IPU_IRQ_CSI2_OUT_EOF;
|
|
else if (channel == CSI_MEM3)
|
|
irq = IPU_IRQ_CSI3_OUT_EOF;
|
|
else if (channel == CSI_PRP_ENC_MEM)
|
|
irq = IPU_IRQ_PRP_ENC_OUT_EOF;
|
|
else if (channel == CSI_PRP_VF_MEM)
|
|
irq = IPU_IRQ_PRP_VF_OUT_EOF;
|
|
else{
|
|
dev_err(ipu->dev, "Not a CSI channel\n");
|
|
return;
|
|
}
|
|
|
|
init_completion(&ipu->csi_comp);
|
|
ret = ipu_request_irq(ipu, irq, csi_irq_handler, 0, NULL, ipu);
|
|
if (ret < 0) {
|
|
dev_err(ipu->dev, "CSI irq %d in use\n", irq);
|
|
return;
|
|
}
|
|
ret = wait_for_completion_timeout(&ipu->csi_comp, msecs_to_jiffies(500));
|
|
ipu_free_irq(ipu, irq, ipu);
|
|
dev_dbg(ipu->dev, "CSI stop timeout - %d * 10ms\n", 5 - ret);
|
|
}
|