| /* |
| * 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); |
| } |