diff --git a/drivers/media/platform/msm/camera_v2/Kconfig b/drivers/media/platform/msm/camera_v2/Kconfig index cabc61251013..e5439d85357c 100644 --- a/drivers/media/platform/msm/camera_v2/Kconfig +++ b/drivers/media/platform/msm/camera_v2/Kconfig @@ -95,13 +95,22 @@ config MSM_EEPROM and also provides support for writing data in case of FLASH ROM. Currently supports I2C, CCI and SPI protocol +config MSM_ISP_V1 + bool "QTI MSM Image Signal Processing interface support" + depends on MSMB_CAMERA + ---help--- + Enable support for Image Signal Processing interface module. + This module acts as a crossbar between CSID and VFE. Output + of any CID of CSID can be routed to of pix or raw + data interface in VFE. + config MSM_ISPIF bool "QTI MSM Image Signal Processing interface support" depends on MSMB_CAMERA ---help--- Enable support for Image Signal Processing interface module. This module acts as a crossbar between CSID and VFE. Output - of any CID of CSID can be routed to of of pix or raw + of any CID of CSID can be routed to of pix or raw data interface in VFE. config MSM_ISPIF_V1 diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c index 22f489166ede..d32a0f27dc9d 100644 --- a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c +++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c @@ -20,8 +20,6 @@ #define EMPTY_QSEECOM_HANDLE NULL #define QSEECOM_SBUFF_SIZE SZ_128K -#define MSM_CAMERA_TZ_UTIL_VERBOSE - #define MSM_CAMERA_TZ_BOOT_PROTECTED (false) /* Update version major number in case the HLOS-TA interface is changed*/ diff --git a/drivers/media/platform/msm/camera_v2/isp/Makefile b/drivers/media/platform/msm/camera_v2/isp/Makefile index 621d81d381a1..d36b1e2d598d 100644 --- a/drivers/media/platform/msm/camera_v2/isp/Makefile +++ b/drivers/media/platform/msm/camera_v2/isp/Makefile @@ -1,5 +1,10 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2 ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io ccflags-y += -Idrivers/media/platform/msm/camera_v2/common/ +ifeq ($(CONFIG_MSM_ISP_V1),y) +obj-$(CONFIG_MSMB_CAMERA) += msm_isp_32.o msm_buf_mgr.o msm_isp_util_32.o msm_isp_axi_util_32.o msm_isp_stats_util_32.o +obj-$(CONFIG_MSMB_CAMERA) += msm_isp32.o +else obj-$(CONFIG_MSMB_CAMERA) += msm_buf_mgr.o msm_isp_util.o msm_isp_axi_util.o msm_isp_stats_util.o obj-$(CONFIG_MSMB_CAMERA) += msm_isp48.o msm_isp47.o msm_isp46.o msm_isp44.o msm_isp40.o msm_isp.o +endif diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c index 691b492dd52f..6196a8c2b34d 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c @@ -189,7 +189,9 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr, int i, rc = -1; int ret; struct msm_isp_buffer_mapped_info *mapped_info; +#ifndef CONFIG_MSM_ISP_V1 uint32_t accu_length = 0; +#endif struct msm_isp_bufq *bufq = NULL; bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle); @@ -228,8 +230,12 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr, goto get_phy_err; } +#ifdef CONFIG_MSM_ISP_V1 + mapped_info->paddr += qbuf_buf->planes[i].offset; +#else mapped_info->paddr += accu_length; accu_length += qbuf_buf->planes[i].length; +#endif CDBG("%s: plane: %d addr:%pK\n", __func__, i, (void *)mapped_info->paddr); @@ -732,10 +738,17 @@ static int msm_isp_buf_divert(struct msm_isp_buf_mgr *buf_mgr, spin_lock_irqsave(&bufq->bufq_lock, flags); buf_info->frame_id = frame_id; +#ifdef CONFIG_MSM_ISP_V1 + if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED) { + buf_info->state = MSM_ISP_BUFFER_STATE_DIVERTED; + buf_info->tv = tv; + } +#else if (BUF_SRC(bufq->stream_id) == MSM_ISP_BUFFER_SRC_NATIVE) { buf_info->state = MSM_ISP_BUFFER_STATE_DIVERTED; buf_info->tv = tv; } +#endif spin_unlock_irqrestore(&bufq->bufq_lock, flags); return 0; } @@ -1077,7 +1090,6 @@ static void msm_isp_release_all_bufq( } } - /** * msm_isp_buf_put_scratch() - Release scratch buffers * @buf_mgr: The buffer structure for h/w @@ -1220,7 +1232,6 @@ err1: return rc; } - static int msm_isp_init_isp_buf_mgr(struct msm_isp_buf_mgr *buf_mgr, const char *ctx_name) { diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c index 6da1360a5fcc..a95917c76902 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp32.c @@ -12,21 +12,15 @@ #include #include - #include "msm_isp32.h" -#include "msm_isp_util.h" -#include "msm_isp_axi_util.h" -#include "msm_isp_stats_util.h" -#include "msm_isp.h" +#include "msm_isp_util_32.h" +#include "msm_isp_axi_util_32.h" +#include "msm_isp_stats_util_32.h" +#include "msm_isp_32.h" #include "msm.h" #include "msm_camera_io_util.h" -static const struct platform_device_id msm_vfe32_dev_id[] = { - {"msm_vfe32", (kernel_ulong_t) &vfe32_hw_info}, - {} -}; - -#define VFE32_BURST_LEN 2 +#define VFE32_BURST_LEN 3 #define VFE32_UB_SIZE 1024 #define VFE32_UB_SIZE_32KB 2048 #define VFE32_EQUAL_SLICE_UB 194 @@ -36,7 +30,7 @@ static const struct platform_device_id msm_vfe32_dev_id[] = { #define VFE32_XBAR_BASE(idx) (0x40 + 0x4 * (idx / 4)) #define VFE32_XBAR_SHIFT(idx) ((idx % 4) * 8) #define VFE32_PING_PONG_BASE(wm, ping_pong) \ - (VFE32_WM_BASE(wm) + 0x4 * (1 + ((~ping_pong) & 0x1))) + (VFE32_WM_BASE(wm) + 0x4 * (1 + (~(ping_pong >> wm) & 0x1))) static uint8_t stats_pingpong_offset_map[] = { 7, 8, 9, 10, 11, 12, 13}; @@ -60,16 +54,6 @@ static struct msm_cam_clk_info msm_vfe32_2_clk_info[] = { {"csi_vfe_clk", -1}, }; -static uint32_t msm_vfe32_ub_reg_offset(struct vfe_device *vfe_dev, int idx) -{ - return (VFE32_WM_BASE(idx) + 0x10); -} - -static uint32_t msm_vfe32_get_ub_size(struct vfe_device *vfe_dev) -{ - return MSM_ISP32_TOTAL_WM_UB; -} - static int32_t msm_vfe32_init_qos_parms(struct vfe_device *vfe_dev, struct msm_vfe_hw_init_parms *qos_parms, struct msm_vfe_hw_init_parms *ds_parms) @@ -284,8 +268,6 @@ static int msm_vfe32_init_hardware(struct vfe_device *vfe_dev) pr_err("%s: vfe ioremap failed\n", __func__); goto vfe_remap_failed; } - vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = - vfe_dev->vfe_base; vfe_dev->vfe_vbif_base = ioremap(vfe_dev->vfe_vbif_mem->start, resource_size(vfe_dev->vfe_vbif_mem)); @@ -330,14 +312,12 @@ bus_scale_register_failed: static void msm_vfe32_release_hardware(struct vfe_device *vfe_dev) { - msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x1C); - msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x20); - disable_irq(vfe_dev->vfe_irq->start); free_irq(vfe_dev->vfe_irq->start, vfe_dev); tasklet_kill(&vfe_dev->vfe_tasklet); - msm_isp_flush_tasklet(vfe_dev); iounmap(vfe_dev->vfe_vbif_base); vfe_dev->vfe_vbif_base = NULL; + iounmap(vfe_dev->vfe_base); + vfe_dev->vfe_base = NULL; if (vfe_dev->vfe_clk_idx == 1) msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_1_clk_info, vfe_dev->vfe_clk, @@ -346,9 +326,6 @@ static void msm_vfe32_release_hardware(struct vfe_device *vfe_dev) msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_2_clk_info, vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_2_clk_info), 0); - vfe_dev->common_data->dual_vfe_res->vfe_base[vfe_dev->pdev->id] = NULL; - iounmap(vfe_dev->vfe_base); - vfe_dev->vfe_base = NULL; kfree(vfe_dev->vfe_clk); regulator_disable(vfe_dev->fs_vfe); msm_isp_deinit_bandwidth_mgr(ISP_VFE0 + vfe_dev->pdev->id); @@ -369,7 +346,6 @@ static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev) ds_parms.entries = "ds-entries"; ds_parms.regs = "ds-regs"; ds_parms.settings = "ds-settings"; - msm_vfe32_init_qos_parms(vfe_dev, &qos_parms, &ds_parms); msm_vfe32_init_vbif_parms(vfe_dev, &vbif_parms); @@ -381,6 +357,11 @@ static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev) msm_camera_io_w_mb(0x1CFFFFFF, vfe_dev->vfe_base + 0x20); msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x24); msm_camera_io_w_mb(0x1FFFFFFF, vfe_dev->vfe_base + 0x28); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x6FC); + msm_camera_io_w(0x10000000, vfe_dev->vfe_base + VFE32_RDI_BASE(1)); + msm_camera_io_w(0x10000000, vfe_dev->vfe_base + VFE32_RDI_BASE(2)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(0)); + msm_camera_io_w(0x0, vfe_dev->vfe_base + VFE32_XBAR_BASE(4)); } @@ -396,13 +377,30 @@ static void msm_vfe32_clear_status_reg(struct vfe_device *vfe_dev) static void msm_vfe32_process_reset_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1) { - if (irq_status1 & BIT(23)) + if (irq_status1 & BIT(23)) { + if (vfe_dev->vfe_reset_timeout_processed == 1) { + pr_err("%s:vfe reset was processed.\n", __func__); + return; + } complete(&vfe_dev->reset_complete); + } } static void msm_vfe32_process_halt_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1) { + if (irq_status1 & (1 << 24)) + msm_camera_io_w_mb(0, vfe_dev->vfe_base + 0x1D8); +} + +static void msm_vfe32_process_epoch_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts) +{ + if (!(irq_status0 & 0x18)) + return; + if (irq_status0 & (1 << 3)) + msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts); } static void msm_vfe32_process_camif_irq(struct vfe_device *vfe_dev, @@ -416,10 +414,12 @@ static void msm_vfe32_process_camif_irq(struct vfe_device *vfe_dev, ISP_DBG("%s: SOF IRQ\n", __func__); if (vfe_dev->axi_data.src_info[VFE_PIX_0].raw_stream_count > 0 && vfe_dev->axi_data.src_info[VFE_PIX_0]. - stream_count == 0) { + pix_stream_count == 0) { msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts); - msm_isp_axi_stream_update(vfe_dev, VFE_PIX_0, ts); - msm_isp_update_framedrop_reg(vfe_dev, VFE_PIX_0); + if (vfe_dev->axi_data.stream_update) + msm_isp_axi_stream_update(vfe_dev, + (1 << VFE_PIX_0)); + msm_isp_update_framedrop_reg(vfe_dev, (1 << VFE_PIX_0)); } } } @@ -485,7 +485,20 @@ static void msm_vfe32_process_violation_status(struct vfe_device *vfe_dev) static void msm_vfe32_get_overflow_mask(uint32_t *overflow_mask) { - *overflow_mask = 0x0; + *overflow_mask = 0x003FFF7E; +} + +static void msm_vfe32_get_rdi_wm_mask(struct vfe_device *vfe_dev, + uint32_t *rdi_wm_mask) +{ + *rdi_wm_mask = vfe_dev->axi_data.rdi_wm_mask; +} + +static void msm_vfe32_get_irq_mask(struct vfe_device *vfe_dev, + uint32_t *irq0_mask, uint32_t *irq1_mask) +{ + *irq0_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); + *irq1_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x20); } static void msm_vfe32_process_error_status(struct vfe_device *vfe_dev) @@ -571,7 +584,7 @@ static void msm_vfe32_process_error_status(struct vfe_device *vfe_dev) pr_err("%s: axi error\n", __func__); } -static void msm_vfe32_read_and_clear_irq_status(struct vfe_device *vfe_dev, +static void msm_vfe32_read_irq_status(struct vfe_device *vfe_dev, uint32_t *irq_status0, uint32_t *irq_status1) { *irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); @@ -589,122 +602,127 @@ static void msm_vfe32_read_and_clear_irq_status(struct vfe_device *vfe_dev, msm_camera_io_r(vfe_dev->vfe_base + 0x7B4); } -static void msm_vfe32_read_irq_status(struct vfe_device *vfe_dev, - uint32_t *irq_status0, uint32_t irq_status1) -{ - *irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); - *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x30); -} - static void msm_vfe32_process_reg_update(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, struct msm_isp_timestamp *ts) { - uint32_t rdi_status; - enum msm_vfe_input_src i; + uint8_t input_src = 0x0; if (!(irq_status0 & 0x20) && !(irq_status1 & 0x1C000000)) return; if (irq_status0 & BIT(5)) { - msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_PIX_0, ts); - vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, - VFE_PIX_0); - if (vfe_dev->axi_data.stream_update[VFE_PIX_0]) { - rdi_status = msm_camera_io_r(vfe_dev->vfe_base + - VFE32_XBAR_BASE(0)); - rdi_status |= msm_camera_io_r(vfe_dev->vfe_base + - VFE32_XBAR_BASE(4)); - - if ((rdi_status & BIT(7)) && (!(irq_status0 & 0x20))) - return; - } - msm_isp_process_stats_reg_upd_epoch_irq(vfe_dev, - MSM_ISP_COMP_IRQ_REG_UPD); + msm_isp_notify(vfe_dev, ISP_EVENT_REG_UPDATE, VFE_PIX_0, ts); + input_src |= (1 << VFE_PIX_0); + } + if (irq_status1 & BIT(26)) { + msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_RAW_0, ts); + input_src |= (1 << VFE_RAW_0); + } + if (irq_status1 & BIT(27)) { + msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_RAW_1, ts); + input_src |= (1 << VFE_RAW_1); + } + if (irq_status1 & BIT(28)) { + msm_isp_notify(vfe_dev, ISP_EVENT_SOF, VFE_RAW_2, ts); + input_src |= (1 << VFE_RAW_2); } - for (i = VFE_RAW_0; i <= VFE_RAW_2; i++) { - if (irq_status1 & BIT(26 + (i - VFE_RAW_0))) { - msm_isp_notify(vfe_dev, ISP_EVENT_SOF, i, ts); - msm_isp_axi_stream_update(vfe_dev, i, ts); - msm_isp_update_framedrop_reg(vfe_dev, i); - - vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, - i); + if (vfe_dev->axi_data.stream_update) + msm_isp_axi_stream_update(vfe_dev, input_src); + if (atomic_read(&vfe_dev->stats_data.stats_update)) + msm_isp_stats_stream_update(vfe_dev); + if (vfe_dev->axi_data.stream_update || + atomic_read(&vfe_dev->stats_data.stats_update)) { + if (input_src & (1 << VFE_PIX_0)) { + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, (1 << VFE_PIX_0)); } } - + msm_isp_update_framedrop_reg(vfe_dev, input_src); + msm_isp_update_stats_framedrop_reg(vfe_dev); msm_isp_update_error_frame_count(vfe_dev); - -} - -static void msm_vfe32_process_epoch_irq(struct vfe_device *vfe_dev, - uint32_t irq_status0, uint32_t irq_status1, - struct msm_isp_timestamp *ts) -{ - /* Not supported */ -} - -static void msm_vfe32_reg_update(struct vfe_device *vfe_dev, - enum msm_vfe_input_src frame_src) -{ - if (vfe_dev->is_split && vfe_dev->pdev->id == ISP_VFE1) { - msm_camera_io_w_mb(0xF, - vfe_dev->common_data->dual_vfe_res->vfe_base[ISP_VFE0] - + 0x260); - msm_camera_io_w_mb(0xF, vfe_dev->vfe_base + 0x260); - } else if (!vfe_dev->is_split) { - msm_camera_io_w_mb(0xF, vfe_dev->vfe_base + 0x260); + if ((input_src & (1 << VFE_RAW_0)) || + (input_src & (1 << VFE_RAW_1)) || + (input_src & (1 << VFE_RAW_2))) { + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, input_src); } + + return; +} + +static void msm_vfe32_reg_update( + struct vfe_device *vfe_dev, uint32_t input_src) +{ + msm_camera_io_w_mb(input_src, vfe_dev->vfe_base + 0x260); } static long msm_vfe32_reset_hardware(struct vfe_device *vfe_dev, uint32_t first_start, uint32_t blocking) { - init_completion(&vfe_dev->reset_complete); - msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4); - return wait_for_completion_timeout( - &vfe_dev->reset_complete, msecs_to_jiffies(50)); + long rc = 0; + uint32_t irq_status1; + + if (blocking) { + init_completion(&vfe_dev->reset_complete); + msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4); + vfe_dev->vfe_reset_timeout_processed = 0; + rc = wait_for_completion_timeout( + &vfe_dev->reset_complete, msecs_to_jiffies(500)); + } else { + msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4); + } + + if (blocking && rc <= 0) { + /*read ISP status register*/ + irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x30); + pr_err("%s: handling vfe reset time out error. irq_status1 0x%x\n", + __func__, irq_status1); + if (irq_status1 & BIT(23)) { + pr_err("%s: vfe reset has done actually\n", __func__); + vfe_dev->vfe_reset_timeout_processed = 1; + return 1; + } + } + return rc; } static void msm_vfe32_axi_reload_wm( - struct vfe_device *vfe_dev, void __iomem *vfe_base, - uint32_t reload_mask) + struct vfe_device *vfe_dev, uint32_t reload_mask) { if (!vfe_dev->pdev->dev.of_node) { /*vfe32 A-family: 8960*/ - msm_camera_io_w_mb(reload_mask, vfe_base + 0x38); + msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x38); } else { /*vfe32 B-family: 8610*/ - msm_camera_io_w(0x0, vfe_base + 0x24); - msm_camera_io_w(0x0, vfe_base + 0x28); - msm_camera_io_w(0x0, vfe_base + 0x20); - msm_camera_io_w_mb(0x1, vfe_base + 0x18); - msm_camera_io_w(0x9AAAAAAA, vfe_base + 0x600); - msm_camera_io_w(reload_mask, vfe_base + 0x38); + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x28); + msm_camera_io_w(0x1C800000, vfe_dev->vfe_base + 0x20); + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x18); + msm_camera_io_w(0x9AAAAAAA, vfe_dev->vfe_base + 0x600); + msm_camera_io_w(reload_mask, vfe_dev->vfe_base + 0x38); } } -static void msm_vfe32_axi_enable_wm(void __iomem *vfe_base, +static void msm_vfe32_axi_enable_wm(struct vfe_device *vfe_dev, uint8_t wm_idx, uint8_t enable) { uint32_t val = msm_camera_io_r( - vfe_base + VFE32_WM_BASE(wm_idx)); + vfe_dev->vfe_base + VFE32_WM_BASE(wm_idx)); if (enable) val |= 0x1; else val &= ~0x1; msm_camera_io_w_mb(val, - vfe_base + VFE32_WM_BASE(wm_idx)); + vfe_dev->vfe_base + VFE32_WM_BASE(wm_idx)); } static void msm_vfe32_axi_cfg_comp_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; - int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); uint32_t comp_mask, comp_mask_index = - stream_info->comp_mask_index[vfe_idx]; + stream_info->comp_mask_index; uint32_t irq_mask; comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34); @@ -721,9 +739,7 @@ static void msm_vfe32_axi_cfg_comp_mask(struct vfe_device *vfe_dev, static void msm_vfe32_axi_clear_comp_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { - int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - uint32_t comp_mask, comp_mask_index = - stream_info->comp_mask_index[vfe_idx]; + uint32_t comp_mask, comp_mask_index = stream_info->comp_mask_index; uint32_t irq_mask; comp_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x34); @@ -739,10 +755,9 @@ static void msm_vfe32_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { uint32_t irq_mask; - int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); - irq_mask |= BIT(stream_info->wm[vfe_idx][0] + 6); + irq_mask |= BIT(stream_info->wm[0] + 6); msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C); } @@ -750,31 +765,40 @@ static void msm_vfe32_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) { uint32_t irq_mask; - int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); - irq_mask &= ~BIT(stream_info->wm[vfe_idx][0] + 6); + irq_mask &= ~BIT(stream_info->wm[0] + 6); msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C); } static void msm_vfe32_cfg_framedrop(struct vfe_device *vfe_dev, - struct msm_vfe_axi_stream *stream_info, uint32_t framedrop_pattern, - uint32_t framedrop_period) + struct msm_vfe_axi_stream *stream_info) { - void __iomem *vfe_base = vfe_dev->vfe_base; + uint32_t framedrop_pattern = 0, framedrop_period = 0; + + if (stream_info->runtime_init_frame_drop == 0) { + framedrop_pattern = stream_info->framedrop_pattern; + framedrop_period = stream_info->framedrop_period; + } + + if (stream_info->stream_type == BURST_STREAM && + stream_info->runtime_burst_frame_count == 0) { + framedrop_pattern = 0; + framedrop_period = 0; + } if (stream_info->stream_src == PIX_ENCODER) { - msm_camera_io_w(framedrop_period - 1, vfe_base + 0x504); - msm_camera_io_w(framedrop_period - 1, vfe_base + 0x508); - msm_camera_io_w(framedrop_pattern, vfe_base + 0x50C); - msm_camera_io_w(framedrop_pattern, vfe_base + 0x510); + msm_camera_io_w(framedrop_period, vfe_dev->vfe_base + 0x504); + msm_camera_io_w(framedrop_period, vfe_dev->vfe_base + 0x508); + msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x50C); + msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x510); } else if (stream_info->stream_src == PIX_VIEWFINDER) { - msm_camera_io_w(framedrop_period - 1, vfe_base + 0x514); - msm_camera_io_w(framedrop_period - 1, vfe_base + 0x518); - msm_camera_io_w(framedrop_pattern, vfe_base + 0x51C); - msm_camera_io_w(framedrop_pattern, vfe_base + 0x520); + msm_camera_io_w(framedrop_period, vfe_dev->vfe_base + 0x514); + msm_camera_io_w(framedrop_period, vfe_dev->vfe_base + 0x518); + msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x51C); + msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + 0x520); } - msm_camera_io_w_mb(0x1, vfe_base + 0x260); + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x260); } static void msm_vfe32_clear_framedrop(struct vfe_device *vfe_dev, @@ -877,6 +901,7 @@ static void msm_vfe32_cfg_fetch_engine(struct vfe_device *vfe_dev, struct msm_vfe_pix_cfg *pix_cfg) { pr_err("%s: Fetch engine not supported\n", __func__); + return; } static void msm_vfe32_cfg_camif(struct vfe_device *vfe_dev, @@ -924,6 +949,7 @@ static void msm_vfe32_cfg_input_mux(struct vfe_device *vfe_dev, pr_err("%s: Unsupported input mux %d\n", __func__, pix_cfg->input_mux); } + return; } static void msm_vfe32_update_camif_state( @@ -938,19 +964,20 @@ static void msm_vfe32_update_camif_state( if (update_state == ENABLE_CAMIF) { val = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); - val |= 0x1; + val |= 0x19; msm_camera_io_w_mb(val, vfe_dev->vfe_base + 0x1C); - + msm_camera_io_w_mb(0xA, vfe_dev->vfe_base + 0x200); val = msm_camera_io_r(vfe_dev->vfe_base + 0x1E4); bus_en = ((vfe_dev->axi_data.src_info[ VFE_PIX_0].raw_stream_count > 0) ? 1 : 0); vfe_en = ((vfe_dev->axi_data.src_info[ - VFE_PIX_0].stream_count > 0) ? 1 : 0); + VFE_PIX_0].pix_stream_count > 0) ? 1 : 0); val &= 0xFFFFFF3F; val = val | bus_en << 7 | vfe_en << 6; msm_camera_io_w(val, vfe_dev->vfe_base + 0x1E4); + msm_camera_io_w_mb(0x4, vfe_dev->vfe_base + 0x1E0); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1E0); vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1; } else if (update_state == DISABLE_CAMIF) { @@ -990,17 +1017,16 @@ static void msm_vfe32_axi_cfg_wm_reg( uint8_t plane_idx) { uint32_t val; - int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[vfe_idx][plane_idx]); + uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]); if (!stream_info->frame_based) { /*WR_IMAGE_SIZE*/ val = ((msm_isp_cal_word_per_line( stream_info->output_format, - stream_info->plane_cfg[vfe_idx][plane_idx]. + stream_info->plane_cfg[plane_idx]. output_width)+1)/2 - 1) << 16 | - (stream_info->plane_cfg[vfe_idx][plane_idx]. + (stream_info->plane_cfg[plane_idx]. output_height - 1); msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10); @@ -1008,9 +1034,9 @@ static void msm_vfe32_axi_cfg_wm_reg( val = msm_isp_cal_word_per_line( stream_info->output_format, - stream_info->plane_cfg[vfe_idx][plane_idx]. + stream_info->plane_cfg[plane_idx]. output_stride) << 16 | - (stream_info->plane_cfg[vfe_idx][plane_idx]. + (stream_info->plane_cfg[plane_idx]. output_height - 1) << 4 | VFE32_BURST_LEN; msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14); } else { @@ -1018,12 +1044,13 @@ static void msm_vfe32_axi_cfg_wm_reg( val = msm_isp_cal_word_per_line( stream_info->output_format, - stream_info->plane_cfg[vfe_idx][plane_idx]. + stream_info->plane_cfg[plane_idx]. output_width) << 16 | - (stream_info->plane_cfg[vfe_idx][plane_idx]. + (stream_info->plane_cfg[plane_idx]. output_height - 1) << 4 | VFE32_BURST_LEN; msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14); } + return; } static void msm_vfe32_axi_clear_wm_reg( @@ -1031,22 +1058,24 @@ static void msm_vfe32_axi_clear_wm_reg( struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx) { uint32_t val = 0; - int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[vfe_idx][plane_idx]); + uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]); + + /* FRAME BASED */ + msm_camera_io_w(val, vfe_dev->vfe_base + wm_base); /*WR_IMAGE_SIZE*/ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10); /*WR_BUFFER_CFG*/ msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14); + return; } static void msm_vfe32_axi_cfg_wm_xbar_reg( struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx) { - int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); struct msm_vfe_axi_plane_cfg *plane_cfg = - &stream_info->plane_cfg[vfe_idx][plane_idx]; - uint8_t wm = stream_info->wm[vfe_idx][plane_idx]; + &stream_info->plane_cfg[plane_idx]; + uint8_t wm = stream_info->wm[plane_idx]; uint32_t xbar_cfg = 0; uint32_t xbar_reg_cfg = 0; @@ -1093,14 +1122,14 @@ static void msm_vfe32_axi_cfg_wm_xbar_reg( xbar_reg_cfg &= ~(0xFF << VFE32_XBAR_SHIFT(wm)); xbar_reg_cfg |= (xbar_cfg << VFE32_XBAR_SHIFT(wm)); msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm)); + return; } static void msm_vfe32_axi_clear_wm_xbar_reg( struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx) { - int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - uint8_t wm = stream_info->wm[vfe_idx][plane_idx]; + uint8_t wm = stream_info->wm[plane_idx]; uint32_t xbar_reg_cfg = 0; xbar_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_XBAR_BASE(wm)); @@ -1108,21 +1137,95 @@ static void msm_vfe32_axi_clear_wm_xbar_reg( msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm)); } -static void msm_vfe32_update_ping_pong_addr(void __iomem *vfe_base, - uint8_t wm_idx, uint32_t pingpong_bit, dma_addr_t paddr, - int32_t buf_size) +static void msm_vfe32_cfg_axi_ub_equal_default(struct vfe_device *vfe_dev) +{ + int i; + uint32_t ub_offset = 0; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + uint32_t total_image_size = 0; + uint32_t num_used_wms = 0; + uint32_t prop_size = 0; + uint32_t wm_ub_size; + uint64_t delta; + + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + if (axi_data->free_wm[i] > 0) { + num_used_wms++; + total_image_size += axi_data->wm_image_size[i]; + } + } + prop_size = MSM_ISP32_TOTAL_WM_UB - + axi_data->hw_info->min_wm_ub * num_used_wms; + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + if (axi_data->free_wm[i]) { + delta = + (uint64_t)(axi_data->wm_image_size[i] * + prop_size); + do_div(delta, total_image_size); + wm_ub_size = axi_data->hw_info->min_wm_ub + + (uint32_t)delta; + msm_camera_io_w(ub_offset << 16 | + (wm_ub_size - 1), vfe_dev->vfe_base + + VFE32_WM_BASE(i) + 0xC); + ub_offset += wm_ub_size; + } else { + msm_camera_io_w(0, + vfe_dev->vfe_base + VFE32_WM_BASE(i) + 0xC); + } + } +} + +static void msm_vfe32_cfg_axi_ub_equal_slicing(struct vfe_device *vfe_dev) +{ + int i; + uint32_t ub_offset = 0; + uint32_t final_ub_slice_size; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + if (ub_offset + VFE32_EQUAL_SLICE_UB > VFE32_AXI_SLICE_UB) { + final_ub_slice_size = VFE32_AXI_SLICE_UB - ub_offset; + msm_camera_io_w(ub_offset << 16 | + (final_ub_slice_size - 1), vfe_dev->vfe_base + + VFE32_WM_BASE(i) + 0xC); + ub_offset += final_ub_slice_size; + } else { + msm_camera_io_w(ub_offset << 16 | + (VFE32_EQUAL_SLICE_UB - 1), vfe_dev->vfe_base + + VFE32_WM_BASE(i) + 0xC); + ub_offset += VFE32_EQUAL_SLICE_UB; + } + } +} + +static void msm_vfe32_cfg_axi_ub(struct vfe_device *vfe_dev) +{ + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + + axi_data->wm_ub_cfg_policy = MSM_WM_UB_CFG_DEFAULT; + if (axi_data->wm_ub_cfg_policy == MSM_WM_UB_EQUAL_SLICING) + msm_vfe32_cfg_axi_ub_equal_slicing(vfe_dev); + else + msm_vfe32_cfg_axi_ub_equal_default(vfe_dev); +} + +static void msm_vfe32_update_ping_pong_addr(struct vfe_device *vfe_dev, + uint8_t wm_idx, uint32_t pingpong_status, dma_addr_t paddr) { uint32_t paddr32 = (paddr & 0xFFFFFFFF); - msm_camera_io_w(paddr32, vfe_base + - VFE32_PING_PONG_BASE(wm_idx, pingpong_bit)); + msm_camera_io_w(paddr32, vfe_dev->vfe_base + + VFE32_PING_PONG_BASE(wm_idx, pingpong_status)); } static int msm_vfe32_axi_halt(struct vfe_device *vfe_dev, uint32_t blocking) { - uint32_t halt_mask; uint32_t axi_busy_flag = true; + /* Keep only halt and restart mask */ + msm_camera_io_w(0x01800000, vfe_dev->vfe_base + 0x20); + /*Clear IRQ Status */ + msm_camera_io_w(0xFE7FFFFF, vfe_dev->vfe_base + 0x28); msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1D8); while (axi_busy_flag) { if (msm_camera_io_r( @@ -1130,10 +1233,27 @@ static int msm_vfe32_axi_halt(struct vfe_device *vfe_dev, uint32_t blocking) axi_busy_flag = false; } msm_camera_io_w_mb(0, vfe_dev->vfe_base + 0x1D8); - halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x20); - halt_mask &= 0xFEFFFFFF; - /* Disable AXI IRQ */ - msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x20); + return 0; +} + +static int msm_vfe32_axi_restart(struct vfe_device *vfe_dev, + uint32_t blocking, uint32_t enable_camif) +{ + vfe_dev->hw_info->vfe_ops.core_ops.restore_irq_mask(vfe_dev); + + /*Clear IRQ Status */ + msm_camera_io_w(0xFE7FFFFF, vfe_dev->vfe_base + 0x28); + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1D8); + msm_camera_io_w_mb(0xA, vfe_dev->vfe_base + 0x200); + /* Start AXI */ + msm_camera_io_w(0x0, vfe_dev->vfe_base + 0x1D8); + vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, 0xF); + memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info)); + atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW); + if (enable_camif) { + vfe_dev->hw_info->vfe_ops.core_ops. + update_camif_state(vfe_dev, ENABLE_CAMIF); + } return 0; } @@ -1187,20 +1307,36 @@ static int msm_vfe32_stats_check_streams( } static void msm_vfe32_stats_cfg_comp_mask(struct vfe_device *vfe_dev, - uint32_t stats_mask, uint8_t comp_idx, uint8_t enable) + uint32_t stats_mask, uint8_t enable) { + uint32_t i = 0; + atomic_t *stats_comp; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + + stats_mask = stats_mask & 0x7F; + + for (i = 0; + i < vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask; i++) { + stats_comp = &stats_data->stats_comp_mask[i]; + if (enable) + atomic_add(stats_mask, stats_comp); + else + atomic_sub(stats_mask, stats_comp); + ISP_DBG("%s: comp_mask: %x\n", + __func__, atomic_read(&stats_data->stats_comp_mask[i])); + } + return; } static void msm_vfe32_stats_cfg_wm_irq_mask(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, - stream_info); uint32_t irq_mask; irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); - irq_mask |= BIT(STATS_IDX(stream_info->stream_handle[vfe_idx]) + 13); + irq_mask |= BIT(STATS_IDX(stream_info->stream_handle) + 13); msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C); + return; } static void msm_vfe32_stats_clear_wm_irq_mask(struct vfe_device *vfe_dev, @@ -1211,18 +1347,21 @@ static void msm_vfe32_stats_clear_wm_irq_mask(struct vfe_device *vfe_dev, irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); irq_mask &= ~(BIT(STATS_IDX(stream_info->stream_handle) + 13)); msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C); + return; } static void msm_vfe32_stats_cfg_wm_reg(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { /*Nothing to configure for VFE3.x*/ + return; } static void msm_vfe32_stats_clear_wm_reg(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { /*Nothing to configure for VFE3.x*/ + return; } static void msm_vfe32_stats_cfg_ub(struct vfe_device *vfe_dev) @@ -1247,12 +1386,7 @@ static void msm_vfe32_stats_cfg_ub(struct vfe_device *vfe_dev) msm_camera_io_w(ub_offset << 16 | (ub_size[i] - 1), vfe_dev->vfe_base + VFE32_STATS_BASE(i) + 0x8); } -} - -static bool msm_vfe32_is_module_cfg_lock_needed( - uint32_t reg_offset) -{ - return false; + return; } static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev, @@ -1294,15 +1428,13 @@ static void msm_vfe32_stats_enable_module(struct vfe_device *vfe_dev, static void msm_vfe32_stats_update_ping_pong_addr(struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status, - dma_addr_t paddr, uint32_t buf_sz) + dma_addr_t paddr) { - void __iomem *vfe_base = vfe_dev->vfe_base; - int vfe_idx = msm_isp_get_vfe_idx_for_stats_stream(vfe_dev, - stream_info); uint32_t paddr32 = (paddr & 0xFFFFFFFF); - int stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]); - msm_camera_io_w(paddr32, vfe_base + + int stats_idx = STATS_IDX(stream_info->stream_handle); + + msm_camera_io_w(paddr32, vfe_dev->vfe_base + VFE32_STATS_PING_PONG_BASE(stats_idx, pingpong_status)); } @@ -1361,28 +1493,6 @@ static int msm_vfe32_get_platform_data(struct vfe_device *vfe_dev) goto vfe_no_resource; } - if (!vfe_dev->pdev->dev.of_node) - vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe_imgwr"); - else - vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe"); - - if (!vfe_dev->iommu_ctx[0]) { - pr_err("%s: no iommux ctx resource?\n", __func__); - rc = -ENODEV; - goto vfe_no_resource; - } - - if (!vfe_dev->pdev->dev.of_node) - vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe_misc"); - else - vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe"); - - if (!vfe_dev->iommu_ctx[1]) { - pr_err("%s: no iommux ctx resource?\n", __func__); - rc = -ENODEV; - goto vfe_no_resource; - } - vfe_no_resource: return rc; } @@ -1394,13 +1504,27 @@ static void msm_vfe32_get_error_mask(uint32_t *error_mask0, *error_mask1 = 0x007FFFFF; } -struct msm_vfe_axi_hardware_info msm_vfe32_axi_hw_info = { - .num_wm = 5, + +static void msm_vfe32_restore_irq_mask(struct vfe_device *vfe_dev) +{ + msm_camera_io_w(vfe_dev->error_info.overflow_recover_irq_mask0, + vfe_dev->vfe_base + 0x1C); + msm_camera_io_w(vfe_dev->error_info.overflow_recover_irq_mask1, + vfe_dev->vfe_base + 0x20); +} + +static void msm_vfe32_get_halt_restart_mask(uint32_t *irq0_mask, + uint32_t *irq1_mask) +{ + *irq1_mask = 0x01800000; +} + +static struct msm_vfe_axi_hardware_info msm_vfe32_axi_hw_info = { + .num_wm = 6, .num_comp_mask = 3, .num_rdi = 3, .num_rdi_master = 3, .min_wm_ub = 64, - .scratch_buf_range = SZ_32M, }; static struct msm_vfe_stats_hardware_info msm_vfe32_stats_hw_info = { @@ -1412,7 +1536,22 @@ static struct msm_vfe_stats_hardware_info msm_vfe32_stats_hw_info = { 1 << MSM_ISP_STATS_SKIN | 1 << MSM_ISP_STATS_BHIST, .stats_ping_pong_offset = stats_pingpong_offset_map, .num_stats_type = VFE32_NUM_STATS_TYPE, - .num_stats_comp_mask = 0, + .num_stats_comp_mask = 1, +}; + +static struct v4l2_subdev_core_ops msm_vfe32_subdev_core_ops = { + .ioctl = msm_isp_ioctl, + .subscribe_event = msm_isp_subscribe_event, + .unsubscribe_event = msm_isp_unsubscribe_event, +}; + +static struct v4l2_subdev_ops msm_vfe32_subdev_ops = { + .core = &msm_vfe32_subdev_core_ops, +}; + +static struct v4l2_subdev_internal_ops msm_vfe32_internal_ops = { + .open = msm_isp_open_node, + .close = msm_isp_close_node, }; struct msm_vfe_hardware_info vfe32_hw_info = { @@ -1421,16 +1560,14 @@ struct msm_vfe_hardware_info vfe32_hw_info = { .vfe_clk_idx = VFE32_CLK_IDX, .vfe_ops = { .irq_ops = { - .read_and_clear_irq_status = - msm_vfe32_read_and_clear_irq_status, .read_irq_status = msm_vfe32_read_irq_status, .process_camif_irq = msm_vfe32_process_camif_irq, .process_reset_irq = msm_vfe32_process_reset_irq, .process_halt_irq = msm_vfe32_process_halt_irq, .process_reg_update = msm_vfe32_process_reg_update, + .process_epoch_irq = msm_vfe32_process_epoch_irq, .process_axi_irq = msm_isp_process_axi_irq, .process_stats_irq = msm_isp_process_stats_irq, - .process_epoch_irq = msm_vfe32_process_epoch_irq, }, .axi_ops = { .reload_wm = msm_vfe32_axi_reload_wm, @@ -1446,15 +1583,14 @@ struct msm_vfe_hardware_info vfe32_hw_info = { .clear_wm_reg = msm_vfe32_axi_clear_wm_reg, .cfg_wm_xbar_reg = msm_vfe32_axi_cfg_wm_xbar_reg, .clear_wm_xbar_reg = msm_vfe32_axi_clear_wm_xbar_reg, - .cfg_ub = msm_vfe47_cfg_axi_ub, + .cfg_ub = msm_vfe32_cfg_axi_ub, .update_ping_pong_addr = msm_vfe32_update_ping_pong_addr, .get_comp_mask = msm_vfe32_get_comp_mask, .get_wm_mask = msm_vfe32_get_wm_mask, .get_pingpong_status = msm_vfe32_get_pingpong_status, .halt = msm_vfe32_axi_halt, - .ub_reg_offset = msm_vfe40_ub_reg_offset, - .get_ub_size = msm_vfe40_get_ub_size, + .restart = msm_vfe32_axi_restart, }, .core_ops = { .reg_update = msm_vfe32_reg_update, @@ -1469,13 +1605,13 @@ struct msm_vfe_hardware_info vfe32_hw_info = { .release_hw = msm_vfe32_release_hardware, .get_platform_data = msm_vfe32_get_platform_data, .get_error_mask = msm_vfe32_get_error_mask, - .process_error_status = msm_vfe32_process_error_status, .get_overflow_mask = msm_vfe32_get_overflow_mask, - .is_module_cfg_lock_needed = - msm_vfe32_is_module_cfg_lock_needed, - .ahb_clk_cfg = NULL, - .set_bus_err_ign_mask = NULL, - .get_bus_err_mask = NULL, + .get_rdi_wm_mask = msm_vfe32_get_rdi_wm_mask, + .get_irq_mask = msm_vfe32_get_irq_mask, + .restore_irq_mask = msm_vfe32_restore_irq_mask, + .get_halt_restart_mask = + msm_vfe32_get_halt_restart_mask, + .process_error_status = msm_vfe32_process_error_status, }, .stats_ops = { .get_stats_idx = msm_vfe32_get_stats_idx, @@ -1493,47 +1629,12 @@ struct msm_vfe_hardware_info vfe32_hw_info = { .get_wm_mask = msm_vfe32_stats_get_wm_mask, .get_frame_id = msm_vfe32_stats_get_frame_id, .get_pingpong_status = msm_vfe32_get_pingpong_status, - .enable_stats_wm = NULL, }, }, .dmi_reg_offset = 0x5A0, .axi_hw_info = &msm_vfe32_axi_hw_info, .stats_hw_info = &msm_vfe32_stats_hw_info, + .subdev_ops = &msm_vfe32_subdev_ops, + .subdev_internal_ops = &msm_vfe32_internal_ops, }; EXPORT_SYMBOL(vfe32_hw_info); - -static const struct of_device_id msm_vfe32_dt_match[] = { - { - .compatible = "qcom,vfe32", - .data = &vfe32_hw_info, - }, - {} -}; - -MODULE_DEVICE_TABLE(of, msm_vfe32_dt_match); - -static struct platform_driver vfe32_driver = { - .probe = vfe_hw_probe, - .driver = { - .name = "msm_vfe32", - .owner = THIS_MODULE, - .of_match_table = msm_vfe32_dt_match, - }, - .id_table = msm_vfe32_dev_id, -}; - -static int __init msm_vfe32_init_module(void) -{ - return platform_driver_register(&vfe32_driver); -} - -static void __exit msm_vfe32_exit_module(void) -{ - platform_driver_unregister(&vfe32_driver); -} - -module_init(msm_vfe32_init_module); -module_exit(msm_vfe32_exit_module); -MODULE_DESCRIPTION("MSM VFE32 driver"); -MODULE_LICENSE("GPL v2"); - diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.c new file mode 100644 index 000000000000..eada5fa125aa --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.c @@ -0,0 +1,557 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_isp_32.h" +#include "msm_isp_util_32.h" +#include "msm_isp_axi_util_32.h" +#include "msm_isp_stats_util_32.h" +#include "msm_sd.h" +#include "msm_isp32.h" + +static struct msm_sd_req_vb2_q vfe_vb2_ops; + +static const struct of_device_id msm_vfe_dt_match[] = { + { + .compatible = "qcom,vfe32", + .data = &vfe32_hw_info, + }, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_vfe_dt_match); + +static const struct platform_device_id msm_vfe_dev_id[] = { + {"msm_vfe32", (kernel_ulong_t) &vfe32_hw_info}, + {} +}; +#define MAX_OVERFLOW_COUNTERS 29 +#define OVERFLOW_LENGTH 1024 +#define OVERFLOW_BUFFER_LENGTH 64 +static char stat_line[OVERFLOW_LENGTH]; + +static struct msm_isp_buf_mgr vfe_buf_mgr; +static int msm_isp_enable_debugfs(struct vfe_device *vfe_dev, + struct msm_isp_bw_req_info *isp_req_hist); +static char *stats_str[MAX_OVERFLOW_COUNTERS] = { + "imgmaster0_overflow_cnt", + "imgmaster1_overflow_cnt", + "imgmaster2_overflow_cnt", + "imgmaster3_overflow_cnt", + "imgmaster4_overflow_cnt", + "imgmaster5_overflow_cnt", + "imgmaster6_overflow_cnt", + "be_overflow_cnt", + "bg_overflow_cnt", + "bf_overflow_cnt", + "awb_overflow_cnt", + "rs_overflow_cnt", + "cs_overflow_cnt", + "ihist_overflow_cnt", + "skinbhist_overflow_cnt", + "bfscale_overflow_cnt", + "ISP_VFE0_client_info.active", + "ISP_VFE0_client_info.ab", + "ISP_VFE0_client_info.ib", + "ISP_VFE1_client_info.active", + "ISP_VFE1_client_info.ab", + "ISP_VFE1_client_info.ib", + "ISP_CPP_client_info.active", + "ISP_CPP_client_info.ab", + "ISP_CPP_client_info.ib", + "ISP_last_overflow.ab", + "ISP_last_overflow.ib", + "ISP_VFE_CLK_RATE", + "ISP_CPP_CLK_RATE", +}; + +#define MAX_DEPTH_BW_REQ_HISTORY 25 +#define MAX_BW_HISTORY_BUFF_LEN 6144 +#define MAX_BW_HISTORY_LINE_BUFF_LEN 512 + +#define MAX_UB_INFO_BUFF_LEN 1024 +#define MAX_UB_INFO_LINE_BUFF_LEN 256 + +static struct msm_isp_bw_req_info + msm_isp_bw_request_history[MAX_DEPTH_BW_REQ_HISTORY]; +static int msm_isp_bw_request_history_idx; +static char bw_request_history_buff[MAX_BW_HISTORY_BUFF_LEN]; +static char ub_info_buffer[MAX_UB_INFO_BUFF_LEN]; +static spinlock_t req_history_lock; +static int vfe_debugfs_statistics_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t vfe_debugfs_statistics_read(struct file *t_file, + char __user *t_char, size_t t_size_t, loff_t *t_loff_t) +{ + int i; + uint64_t *ptr; + char buffer[OVERFLOW_BUFFER_LENGTH] = {0}; + struct vfe_device *vfe_dev = (struct vfe_device *) + t_file->private_data; + struct msm_isp_statistics *stats = vfe_dev->stats; + + memset(stat_line, 0, sizeof(stat_line)); + msm_isp_util_get_bandwidth_stats(vfe_dev, stats); + ptr = (uint64_t *)(stats); + for (i = 0; i < MAX_OVERFLOW_COUNTERS; i++) { + strlcat(stat_line, stats_str[i], sizeof(stat_line)); + strlcat(stat_line, " ", sizeof(stat_line)); + snprintf(buffer, sizeof(buffer), "%llu", ptr[i]); + strlcat(stat_line, buffer, sizeof(stat_line)); + strlcat(stat_line, "\r\n", sizeof(stat_line)); + } + return simple_read_from_buffer(t_char, t_size_t, + t_loff_t, stat_line, strlen(stat_line)); +} + +static ssize_t vfe_debugfs_statistics_write(struct file *t_file, + const char __user *t_char, size_t t_size_t, loff_t *t_loff_t) +{ + struct vfe_device *vfe_dev = (struct vfe_device *) + t_file->private_data; + struct msm_isp_statistics *stats = vfe_dev->stats; + + memset(stats, 0, sizeof(struct msm_isp_statistics)); + + return sizeof(struct msm_isp_statistics); +} + +static int bw_history_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t bw_history_read(struct file *t_file, char __user *t_char, + size_t t_size_t, loff_t *t_loff_t) +{ + int i; + char *out_buffer = bw_request_history_buff; + char line_buffer[MAX_BW_HISTORY_LINE_BUFF_LEN] = {0}; + struct msm_isp_bw_req_info *isp_req_hist = + (struct msm_isp_bw_req_info *) t_file->private_data; + + memset(out_buffer, 0, MAX_BW_HISTORY_BUFF_LEN); + + snprintf(line_buffer, sizeof(line_buffer), + "Bus bandwidth request history in chronological order:\n"); + strlcat(out_buffer, line_buffer, sizeof(bw_request_history_buff)); + + snprintf(line_buffer, sizeof(line_buffer), + "MSM_ISP_MIN_AB = %u, MSM_ISP_MIN_IB = %u\n\n", + MSM_ISP_MIN_AB, MSM_ISP_MIN_IB); + strlcat(out_buffer, line_buffer, sizeof(bw_request_history_buff)); + + for (i = 0; i < MAX_DEPTH_BW_REQ_HISTORY; i++) { + snprintf(line_buffer, sizeof(line_buffer), + "idx = %d, client = %u, timestamp = %llu, ab = %llu, ib = %llu\n" + "ISP0.active = %x, ISP0.ab = %llu, ISP0.ib = %llu\n" + "ISP1.active = %x, ISP1.ab = %llu, ISP1.ib = %llu\n" + "CPP.active = %x, CPP.ab = %llu, CPP.ib = %llu\n\n", + i, isp_req_hist[i].client, isp_req_hist[i].timestamp, + isp_req_hist[i].total_ab, isp_req_hist[i].total_ib, + isp_req_hist[i].client_info[0].active, + isp_req_hist[i].client_info[0].ab, + isp_req_hist[i].client_info[0].ib, + isp_req_hist[i].client_info[1].active, + isp_req_hist[i].client_info[1].ab, + isp_req_hist[i].client_info[1].ib, + isp_req_hist[i].client_info[2].active, + isp_req_hist[i].client_info[2].ab, + isp_req_hist[i].client_info[2].ib); + strlcat(out_buffer, line_buffer, + sizeof(bw_request_history_buff)); + } + return simple_read_from_buffer(t_char, t_size_t, + t_loff_t, out_buffer, strlen(out_buffer)); +} + +static ssize_t bw_history_write(struct file *t_file, + const char __user *t_char, size_t t_size_t, loff_t *t_loff_t) +{ + struct msm_isp_bw_req_info *isp_req_hist = + (struct msm_isp_bw_req_info *) t_file->private_data; + + memset(isp_req_hist, 0, sizeof(msm_isp_bw_request_history)); + msm_isp_bw_request_history_idx = 0; + return sizeof(msm_isp_bw_request_history); +} + +static int ub_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t ub_info_read(struct file *t_file, char __user *t_char, + size_t t_size_t, loff_t *t_loff_t) +{ + int i; + char *out_buffer = ub_info_buffer; + char line_buffer[MAX_UB_INFO_LINE_BUFF_LEN] = {0}; + struct vfe_device *vfe_dev = + (struct vfe_device *) t_file->private_data; + struct msm_isp_ub_info *ub_info = vfe_dev->ub_info; + + memset(out_buffer, 0, MAX_UB_INFO_LINE_BUFF_LEN); + snprintf(line_buffer, sizeof(line_buffer), + "wm_ub_policy_type = %d\n" + "num_wm = %d\n" + "wm_ub = %d\n", + ub_info->policy, ub_info->num_wm, ub_info->wm_ub); + strlcat(out_buffer, line_buffer, + sizeof(ub_info_buffer)); + for (i = 0; i < ub_info->num_wm; i++) { + snprintf(line_buffer, sizeof(line_buffer), + "data[%d] = 0x%x, addr[%d] = 0x%llx\n", + i, ub_info->data[i], i, ub_info->addr[i]); + strlcat(out_buffer, line_buffer, + sizeof(ub_info_buffer)); + } + + return simple_read_from_buffer(t_char, t_size_t, + t_loff_t, out_buffer, strlen(out_buffer)); +} + +static ssize_t ub_info_write(struct file *t_file, + const char __user *t_char, size_t t_size_t, loff_t *t_loff_t) +{ + struct vfe_device *vfe_dev = + (struct vfe_device *) t_file->private_data; + struct msm_isp_ub_info *ub_info = vfe_dev->ub_info; + + memset(ub_info, 0, sizeof(struct msm_isp_ub_info)); + + return sizeof(struct msm_isp_ub_info); +} + +static const struct file_operations vfe_debugfs_error = { + .open = vfe_debugfs_statistics_open, + .read = vfe_debugfs_statistics_read, + .write = vfe_debugfs_statistics_write, +}; + +static const struct file_operations bw_history_ops = { + .open = bw_history_open, + .read = bw_history_read, + .write = bw_history_write, +}; + +static const struct file_operations ub_info_ops = { + .open = ub_info_open, + .read = ub_info_read, + .write = ub_info_write, +}; + +static int msm_isp_enable_debugfs(struct vfe_device *vfe_dev, + struct msm_isp_bw_req_info *isp_req_hist) +{ + struct dentry *debugfs_base; + char dirname[32] = {0}; + + snprintf(dirname, sizeof(dirname), "msm_isp%d", vfe_dev->pdev->id); + debugfs_base = debugfs_create_dir(dirname, NULL); + if (!debugfs_base) + return -ENOMEM; + if (!debugfs_create_file("stats", 0644, debugfs_base, + vfe_dev, &vfe_debugfs_error)) + return -ENOMEM; + + if (!debugfs_create_file("bw_req_history", 0644, + debugfs_base, isp_req_hist, &bw_history_ops)) + return -ENOMEM; + + if (!debugfs_create_file("ub_info", 0644, + debugfs_base, vfe_dev, &ub_info_ops)) + return -ENOMEM; + + return 0; +} + +void msm_isp_update_req_history(uint32_t client, uint64_t ab, + uint64_t ib, + struct msm_isp_bandwidth_info *client_info, + unsigned long long ts) +{ + int i; + + spin_lock(&req_history_lock); + msm_isp_bw_request_history[msm_isp_bw_request_history_idx].client = + client; + msm_isp_bw_request_history[msm_isp_bw_request_history_idx].timestamp = + ts; + msm_isp_bw_request_history[msm_isp_bw_request_history_idx].total_ab = + ab; + msm_isp_bw_request_history[msm_isp_bw_request_history_idx].total_ib = + ib; + + for (i = 0; i < MAX_ISP_CLIENT; i++) { + msm_isp_bw_request_history[msm_isp_bw_request_history_idx]. + client_info[i].active = client_info[i].active; + msm_isp_bw_request_history[msm_isp_bw_request_history_idx]. + client_info[i].ab = client_info[i].ab; + msm_isp_bw_request_history[msm_isp_bw_request_history_idx]. + client_info[i].ib = client_info[i].ib; + } + + msm_isp_bw_request_history_idx = (msm_isp_bw_request_history_idx + 1) + % MAX_DEPTH_BW_REQ_HISTORY; + spin_unlock(&req_history_lock); +} + +#ifdef CONFIG_COMPAT +static long msm_isp_dqevent(struct file *file, struct v4l2_fh *vfh, void *arg) +{ + long rc; + + if (is_compat_task()) { + struct msm_isp32_event_data32 *event_data32; + struct msm_isp32_event_data *event_data; + struct v4l2_event isp_event; + struct v4l2_event *isp_event_user; + + memset(&isp_event, 0, sizeof(isp_event)); + rc = v4l2_event_dequeue(vfh, &isp_event, + file->f_flags & O_NONBLOCK); + if (rc) + return rc; + event_data = (struct msm_isp32_event_data *) + isp_event.u.data; + isp_event_user = (struct v4l2_event *)arg; + memcpy(isp_event_user, &isp_event, + sizeof(*isp_event_user)); + event_data32 = (struct msm_isp32_event_data32 *) + isp_event_user->u.data; + memset(event_data32, 0, + sizeof(struct msm_isp32_event_data32)); + event_data32->timestamp.tv_sec = + event_data->timestamp.tv_sec; + event_data32->timestamp.tv_usec = + event_data->timestamp.tv_usec; + event_data32->mono_timestamp.tv_sec = + event_data->mono_timestamp.tv_sec; + event_data32->mono_timestamp.tv_usec = + event_data->mono_timestamp.tv_usec; + event_data32->input_intf = event_data->input_intf; + event_data32->frame_id = event_data->frame_id; + memcpy(&(event_data32->u), &(event_data->u), + sizeof(event_data32->u)); + } else { + rc = v4l2_event_dequeue(vfh, arg, + file->f_flags & O_NONBLOCK); + } + return rc; +} +#else +static long msm_isp_dqevent(struct file *file, struct v4l2_fh *vfh, void *arg) +{ + return v4l2_event_dequeue(vfh, arg, + file->f_flags & O_NONBLOCK); +} +#endif + +static long msm_isp_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + struct v4l2_fh *vfh = file->private_data; + + switch (cmd) { + case VIDIOC_DQEVENT: { + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) + return -ENOIOCTLCMD; + return msm_isp_dqevent(file, vfh, arg); + } + break; + case VIDIOC_SUBSCRIBE_EVENT: + return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg); + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); + + default: + return v4l2_subdev_call(sd, core, ioctl, cmd, arg); + } +} + +static long msm_isp_subdev_fops_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_isp_subdev_do_ioctl); +} + +static struct v4l2_file_operations msm_isp_v4l2_subdev_fops = { +#ifdef CONFIG_COMPAT + .compat_ioctl32 = msm_isp_subdev_fops_ioctl, +#endif + .unlocked_ioctl = msm_isp_subdev_fops_ioctl +}; + +static int vfe_probe(struct platform_device *pdev) +{ + struct vfe_device *vfe_dev; + /*struct msm_cam_subdev_info sd_info;*/ + const struct of_device_id *match_dev; + int rc = 0; + + vfe_dev = kzalloc(sizeof(struct vfe_device), GFP_KERNEL); + if (!vfe_dev) { + rc = -ENOMEM; + goto end; + } + vfe_dev->stats = kzalloc(sizeof(struct msm_isp_statistics), GFP_KERNEL); + if (!vfe_dev->stats) { + rc = -ENOMEM; + goto probe_fail1; + } + + vfe_dev->ub_info = kzalloc(sizeof(struct msm_isp_ub_info), GFP_KERNEL); + if (!vfe_dev->ub_info) { + rc = -ENOMEM; + goto probe_fail2; + } + if (pdev->dev.of_node) { + of_property_read_u32((&pdev->dev)->of_node, + "cell-index", &pdev->id); + match_dev = of_match_device(msm_vfe_dt_match, &pdev->dev); + if (!match_dev) { + pr_err("%s: No vfe hardware info\n", __func__); + rc = -EINVAL; + goto probe_fail3; + } + vfe_dev->hw_info = + (struct msm_vfe_hardware_info *) match_dev->data; + } else { + vfe_dev->hw_info = (struct msm_vfe_hardware_info *) + platform_get_device_id(pdev)->driver_data; + } + + if (!vfe_dev->hw_info) { + pr_err("%s: No vfe hardware info\n", __func__); + rc = -EINVAL; + goto probe_fail3; + } + ISP_DBG("%s: device id = %d\n", __func__, pdev->id); + + vfe_dev->pdev = pdev; + rc = vfe_dev->hw_info->vfe_ops.core_ops.get_platform_data(vfe_dev); + if (rc < 0) { + pr_err("%s: failed to get platform resources\n", __func__); + rc = -ENOMEM; + goto probe_fail3; + } + + INIT_LIST_HEAD(&vfe_dev->tasklet_q); + tasklet_init(&vfe_dev->vfe_tasklet, + msm_isp_do_tasklet, (unsigned long)vfe_dev); + + v4l2_subdev_init(&vfe_dev->subdev.sd, vfe_dev->hw_info->subdev_ops); + vfe_dev->subdev.sd.internal_ops = + vfe_dev->hw_info->subdev_internal_ops; + snprintf(vfe_dev->subdev.sd.name, + ARRAY_SIZE(vfe_dev->subdev.sd.name), + "vfe"); + vfe_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + vfe_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; + v4l2_set_subdevdata(&vfe_dev->subdev.sd, vfe_dev); + platform_set_drvdata(pdev, &vfe_dev->subdev.sd); + mutex_init(&vfe_dev->realtime_mutex); + mutex_init(&vfe_dev->core_mutex); + spin_lock_init(&vfe_dev->tasklet_lock); + spin_lock_init(&vfe_dev->shared_data_lock); + spin_lock_init(&req_history_lock); + media_entity_pads_init(&vfe_dev->subdev.sd.entity, 0, NULL); + vfe_dev->subdev.sd.entity.function = MSM_CAMERA_SUBDEV_VFE; + vfe_dev->subdev.sd.entity.name = pdev->name; + vfe_dev->subdev.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x2; + rc = msm_sd_register(&vfe_dev->subdev); + if (rc != 0) { + pr_err("%s: msm_sd_register error = %d\n", __func__, rc); + goto probe_fail3; + } + + msm_isp_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner; + msm_isp_v4l2_subdev_fops.open = v4l2_subdev_fops.open; + msm_isp_v4l2_subdev_fops.release = v4l2_subdev_fops.release; + msm_isp_v4l2_subdev_fops.poll = v4l2_subdev_fops.poll; + + vfe_dev->subdev.sd.devnode->fops = &msm_isp_v4l2_subdev_fops; + + vfe_dev->buf_mgr = &vfe_buf_mgr; + v4l2_subdev_notify(&vfe_dev->subdev.sd, + MSM_SD_NOTIFY_REQ_CB, &vfe_vb2_ops); + rc = msm_isp_create_isp_buf_mgr(vfe_dev->buf_mgr, + &vfe_vb2_ops, &pdev->dev, + vfe_dev->hw_info->axi_hw_info->scratch_buf_range); + if (rc < 0) { + pr_err("%s: Unable to create buffer manager\n", __func__); + rc = -EINVAL; + goto probe_fail3; + } + msm_isp_enable_debugfs(vfe_dev, msm_isp_bw_request_history); + + vfe_dev->buf_mgr->init_done = 1; + vfe_dev->vfe_open_cnt = 0; + return rc; + +probe_fail3: + kfree(vfe_dev->ub_info); +probe_fail2: + kfree(vfe_dev->stats); +probe_fail1: + kfree(vfe_dev); +end: + return rc; +} + +static struct platform_driver vfe_driver = { + .probe = vfe_probe, + .driver = { + .name = "msm_vfe", + .owner = THIS_MODULE, + .of_match_table = msm_vfe_dt_match, + }, + .id_table = msm_vfe_dev_id, +}; + +static int __init msm_vfe_init_module(void) +{ + return platform_driver_register(&vfe_driver); +} + +static void __exit msm_vfe_exit_module(void) +{ + platform_driver_unregister(&vfe_driver); +} + +module_init(msm_vfe_init_module); +module_exit(msm_vfe_exit_module); +MODULE_DESCRIPTION("MSM VFE driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.h new file mode 100644 index 000000000000..cbe92faa876c --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_32.h @@ -0,0 +1,599 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_VFE_H__ +#define __MSM_VFE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_buf_mgr.h" + +#define VFE40_8974V1_VERSION 0x10000018 +#define VFE40_8974V2_VERSION 0x1001001A +#define VFE40_8974V3_VERSION 0x1001001B +#define VFE40_8x26_VERSION 0x20000013 +#define VFE40_8x26V2_VERSION 0x20010014 +#define VFE40_8916_VERSION 0x10030000 +#define VFE40_8939_VERSION 0x10040000 +#define VFE32_8909_VERSION 0x30600 + +#define MAX_IOMMU_CTX 2 +#define MAX_NUM_WM 7 +#define MAX_NUM_RDI 3 +#define MAX_NUM_RDI_MASTER 3 +#define MAX_NUM_COMPOSITE_MASK 4 +#define MAX_NUM_STATS_COMP_MASK 2 +#define MAX_INIT_FRAME_DROP 31 +#define ISP_Q2 (1 << 2) +#define ISP_Q10 (1 << 10) + +#define VFE_PING_FLAG 0xFFFFFFFF +#define VFE_PONG_FLAG 0x0 + +#define VFE_MAX_CFG_TIMEOUT 3000 +#define VFE_CLK_INFO_MAX 16 +#define STATS_COMP_BIT_MASK 0xFF0000 + +#define MSM_ISP_MIN_AB 11000000 +#define MSM_ISP_MIN_IB 11000000 + +struct vfe_device; +struct msm_vfe_axi_stream; +struct msm_vfe_stats_stream; + +struct vfe_subscribe_info { + struct v4l2_fh *vfh; + uint32_t active; +}; + +enum msm_isp_pack_fmt { + QCOM, + MIPI, + DPCM6, + DPCM8, + PLAIN8, + PLAIN16, + MAX_ISP_PACK_FMT, +}; + +enum msm_isp_camif_update_state { + NO_UPDATE, + ENABLE_CAMIF, + DISABLE_CAMIF, + DISABLE_CAMIF_IMMEDIATELY +}; + +struct msm_isp_timestamp { + /*Monotonic clock for v4l2 buffer*/ + struct timeval buf_time; + /*Monotonic clock for VT */ + struct timeval vt_time; + /*Wall clock for userspace event*/ + struct timeval event_time; +}; + +struct msm_vfe_irq_ops { + void (*read_irq_status)(struct vfe_device *vfe_dev, + uint32_t *irq_status0, uint32_t *irq_status1); + void (*process_reg_update)(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts); + void (*process_epoch_irq)(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts); + void (*process_reset_irq)(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1); + void (*process_halt_irq)(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1); + void (*process_camif_irq)(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts); + void (*process_axi_irq)(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts); + void (*process_stats_irq)(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts); +}; + +struct msm_vfe_axi_ops { + void (*reload_wm)(struct vfe_device *vfe_dev, + uint32_t reload_mask); + void (*enable_wm)(struct vfe_device *vfe_dev, + uint8_t wm_idx, uint8_t enable); + int32_t (*cfg_io_format)(struct vfe_device *vfe_dev, + enum msm_vfe_axi_stream_src stream_src, + uint32_t io_format); + void (*cfg_framedrop)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info); + void (*clear_framedrop)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info); + void (*cfg_comp_mask)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info); + void (*clear_comp_mask)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info); + void (*cfg_wm_irq_mask)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info); + void (*clear_wm_irq_mask)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info); + + void (*cfg_wm_reg)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, + uint8_t plane_idx); + void (*clear_wm_reg)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx); + + void (*cfg_wm_xbar_reg)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, + uint8_t plane_idx); + void (*clear_wm_xbar_reg)(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx); + + void (*cfg_ub)(struct vfe_device *vfe_dev); + + void (*update_ping_pong_addr)(struct vfe_device *vfe_dev, + uint8_t wm_idx, uint32_t pingpong_status, dma_addr_t paddr); + + uint32_t (*get_wm_mask)(uint32_t irq_status0, uint32_t irq_status1); + uint32_t (*get_comp_mask)(uint32_t irq_status0, uint32_t irq_status1); + uint32_t (*get_pingpong_status)(struct vfe_device *vfe_dev); + int (*halt)(struct vfe_device *vfe_dev, uint32_t blocking); + int (*restart)(struct vfe_device *vfe_dev, uint32_t blocking, + uint32_t enable_camif); + void (*update_cgc_override)(struct vfe_device *vfe_dev, + uint8_t wm_idx, uint8_t cgc_override); +}; + +struct msm_vfe_core_ops { + void (*reg_update)(struct vfe_device *vfe_dev, uint32_t input_src); + long (*reset_hw)(struct vfe_device *vfe_dev, uint32_t first_start, + uint32_t blocking_call); + int (*init_hw)(struct vfe_device *vfe_dev); + void (*init_hw_reg)(struct vfe_device *vfe_dev); + void (*clear_status_reg)(struct vfe_device *vfe_dev); + void (*release_hw)(struct vfe_device *vfe_dev); + void (*cfg_input_mux)(struct vfe_device *vfe_dev, + struct msm_vfe_pix_cfg *pix_cfg); + int (*start_fetch_eng)(struct vfe_device *vfe_dev, + void *arg); + void (*update_camif_state)(struct vfe_device *vfe_dev, + enum msm_isp_camif_update_state update_state); + void (*cfg_rdi_reg)(struct vfe_device *vfe_dev, + struct msm_vfe_rdi_cfg *rdi_cfg, + enum msm_vfe_input_src input_src); + int (*get_platform_data)(struct vfe_device *vfe_dev); + void (*get_error_mask)(uint32_t *error_mask0, uint32_t *error_mask1); + void (*process_error_status)(struct vfe_device *vfe_dev); + void (*get_overflow_mask)(uint32_t *overflow_mask); + void (*get_irq_mask)(struct vfe_device *vfe_dev, + uint32_t *irq0_mask, uint32_t *irq1_mask); + void (*restore_irq_mask)(struct vfe_device *vfe_dev); + void (*get_halt_restart_mask)(uint32_t *irq0_mask, + uint32_t *irq1_mask); + void (*get_rdi_wm_mask)(struct vfe_device *vfe_dev, + uint32_t *rdi_wm_mask); +}; +struct msm_vfe_stats_ops { + int (*get_stats_idx)(enum msm_isp_stats_type stats_type); + int (*check_streams)(struct msm_vfe_stats_stream *stream_info); + void (*cfg_framedrop)(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info); + void (*clear_framedrop)(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info); + void (*cfg_comp_mask)(struct vfe_device *vfe_dev, + uint32_t stats_mask, uint8_t enable); + void (*cfg_wm_irq_mask)(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info); + void (*clear_wm_irq_mask)(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info); + + void (*cfg_wm_reg)(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info); + void (*clear_wm_reg)(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info); + + void (*cfg_ub)(struct vfe_device *vfe_dev); + + void (*enable_module)(struct vfe_device *vfe_dev, + uint32_t stats_mask, uint8_t enable); + + void (*update_ping_pong_addr)(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info, + uint32_t pingpong_status, dma_addr_t paddr); + + uint32_t (*get_frame_id)(struct vfe_device *vfe_dev); + uint32_t (*get_wm_mask)(uint32_t irq_status0, uint32_t irq_status1); + uint32_t (*get_comp_mask)(uint32_t irq_status0, uint32_t irq_status1); + uint32_t (*get_pingpong_status)(struct vfe_device *vfe_dev); + + void (*update_cgc_override)(struct vfe_device *vfe_dev, + uint32_t stats_mask, uint8_t enable); +}; + +struct msm_vfe_ops { + struct msm_vfe_irq_ops irq_ops; + struct msm_vfe_axi_ops axi_ops; + struct msm_vfe_core_ops core_ops; + struct msm_vfe_stats_ops stats_ops; +}; + +struct msm_vfe_hardware_info { + int num_iommu_ctx; + /* secure iommu ctx nums */ + int num_iommu_secure_ctx; + int vfe_clk_idx; + struct msm_vfe_ops vfe_ops; + struct msm_vfe_axi_hardware_info *axi_hw_info; + struct msm_vfe_stats_hardware_info *stats_hw_info; + struct v4l2_subdev_internal_ops *subdev_internal_ops; + struct v4l2_subdev_ops *subdev_ops; + uint32_t dmi_reg_offset; +}; + +struct msm_vfe_axi_hardware_info { + uint8_t num_wm; + uint8_t num_rdi; + uint8_t num_rdi_master; + uint8_t num_comp_mask; + uint32_t min_wm_ub; + uint32_t scratch_buf_range; +}; + +enum msm_vfe_axi_state { + AVAILABLE, + INACTIVE, + ACTIVE, + PAUSED, + START_PENDING, + STOP_PENDING, + PAUSE_PENDING, + RESUME_PENDING, + STARTING, + STOPPING, + PAUSING, + RESUMING, +}; + +enum msm_vfe_axi_cfg_update_state { + NO_AXI_CFG_UPDATE, + APPLYING_UPDATE_RESUME, + UPDATE_REQUESTED, +}; + +#define VFE_NO_DROP 0xFFFFFFFF +#define VFE_DROP_EVERY_2FRAME 0x55555555 +#define VFE_DROP_EVERY_4FRAME 0x11111111 +#define VFE_DROP_EVERY_8FRAME 0x01010101 +#define VFE_DROP_EVERY_16FRAME 0x00010001 +#define VFE_DROP_EVERY_32FRAME 0x00000001 + +enum msm_vfe_axi_stream_type { + CONTINUOUS_STREAM, + BURST_STREAM, +}; + +struct msm_vfe_axi_stream { + uint32_t frame_id; + enum msm_vfe_axi_state state; + enum msm_vfe_axi_stream_src stream_src; + uint8_t num_planes; + uint8_t wm[MAX_PLANES_PER_STREAM]; + uint32_t output_format;/*Planar/RAW/Misc*/ + struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM]; + uint8_t comp_mask_index; + struct msm_isp_buffer *buf[2]; + uint32_t session_id; + uint32_t stream_id; + uint32_t bufq_handle; + uint32_t bufq_scratch_handle; + uint32_t controllable_output; + uint32_t stream_handle; + uint32_t request_frm_num; + uint8_t buf_divert; + enum msm_vfe_axi_stream_type stream_type; + uint32_t frame_based; + enum msm_vfe_frame_skip_pattern frame_skip_pattern; + uint32_t framedrop_period; + uint32_t framedrop_pattern; + uint32_t num_burst_capture;/*number of frame to capture*/ + uint32_t init_frame_drop; + uint32_t burst_frame_count;/*number of sof before burst stop*/ + uint8_t framedrop_update; + spinlock_t lock; + + /*Bandwidth calculation info*/ + uint32_t max_width; + /*Based on format plane size in Q2. e.g NV12 = 1.5*/ + uint32_t format_factor; + uint32_t bandwidth; + + /*Run time update variables*/ + uint32_t runtime_init_frame_drop; + uint32_t runtime_burst_frame_count;/*number of sof before burst stop*/ + uint32_t runtime_num_burst_capture; + uint8_t runtime_framedrop_update; + uint8_t runtime_framedrop_update_burst; + uint32_t runtime_output_format; + enum msm_stream_memory_input_t memory_input; +}; + +struct msm_vfe_axi_composite_info { + uint32_t stream_handle; + uint32_t stream_composite_mask; +}; + +struct msm_vfe_src_info { + uint32_t frame_id; + uint8_t active; + uint8_t pix_stream_count; + uint8_t raw_stream_count; + enum msm_vfe_inputmux input_mux; + uint32_t width; + long pixel_clock; + uint32_t input_format;/*V4L2 pix format with bayer pattern*/ + uint32_t last_updt_frm_id; +}; + +struct msm_vfe_fetch_engine_info { + uint32_t session_id; + uint32_t stream_id; + uint32_t bufq_handle; + uint32_t buf_idx; + uint8_t is_busy; +}; + +enum msm_wm_ub_cfg_type { + MSM_WM_UB_CFG_DEFAULT, + MSM_WM_UB_EQUAL_SLICING, + MSM_WM_UB_CFG_MAX_NUM +}; + +struct msm_vfe_axi_shared_data { + struct msm_vfe_axi_hardware_info *hw_info; + struct msm_vfe_axi_stream stream_info[VFE_AXI_SRC_MAX]; + uint32_t free_wm[MAX_NUM_WM]; + uint32_t wm_image_size[MAX_NUM_WM]; + enum msm_wm_ub_cfg_type wm_ub_cfg_policy; + uint8_t num_used_wm; + uint8_t num_active_stream; + uint8_t num_rdi_stream; + uint8_t num_pix_stream; + uint32_t rdi_wm_mask; + struct msm_vfe_axi_composite_info + composite_info[MAX_NUM_COMPOSITE_MASK]; + uint8_t num_used_composite_mask; + uint32_t stream_update; + atomic_t axi_cfg_update; + enum msm_isp_camif_update_state pipeline_update; + struct msm_vfe_src_info src_info[VFE_SRC_MAX]; + uint16_t stream_handle_cnt; + uint32_t event_mask; +}; + +struct msm_vfe_stats_hardware_info { + uint32_t stats_capability_mask; + uint8_t *stats_ping_pong_offset; + uint8_t num_stats_type; + uint8_t num_stats_comp_mask; +}; + +enum msm_vfe_stats_state { + STATS_AVAILABLE, + STATS_INACTIVE, + STATS_ACTIVE, + STATS_START_PENDING, + STATS_STOP_PENDING, + STATS_STARTING, + STATS_STOPPING, +}; + +struct msm_vfe_stats_stream { + uint32_t session_id; + uint32_t stream_id; + uint32_t stream_handle; + uint32_t composite_flag; + enum msm_isp_stats_type stats_type; + enum msm_vfe_stats_state state; + uint32_t framedrop_pattern; + uint32_t framedrop_period; + uint32_t irq_subsample_pattern; + uint32_t init_stats_frame_drop; + + uint32_t buffer_offset; + struct msm_isp_buffer *buf[2]; + uint32_t bufq_handle; +}; + +struct msm_vfe_stats_shared_data { + struct msm_vfe_stats_stream stream_info[MSM_ISP_STATS_MAX]; + uint8_t num_active_stream; + atomic_t stats_comp_mask[MAX_NUM_STATS_COMP_MASK]; + uint32_t reg_mask; + uint16_t stream_handle_cnt; + atomic_t stats_update; +}; + +struct msm_vfe_tasklet_queue_cmd { + struct list_head list; + uint32_t vfeInterruptStatus0; + uint32_t vfeInterruptStatus1; + struct msm_isp_timestamp ts; + uint8_t cmd_used; +}; + +#define MSM_VFE_TASKLETQ_SIZE 200 + +enum msm_vfe_overflow_state { + NO_OVERFLOW, + OVERFLOW_DETECTED, + HALT_REQUESTED, + RESTART_REQUESTED, +}; + +struct msm_vfe_error_info { + atomic_t overflow_state; + uint32_t overflow_recover_irq_mask0; + uint32_t overflow_recover_irq_mask1; + uint32_t error_mask0; + uint32_t error_mask1; + uint32_t violation_status; + uint32_t camif_status; + uint32_t stream_framedrop_count[MAX_NUM_STREAM]; + uint32_t stats_framedrop_count[MSM_ISP_STATS_MAX]; + uint32_t info_dump_frame_count; + uint32_t error_count; +}; + +struct msm_isp_statistics { + int64_t imagemaster0_overflow; + int64_t imagemaster1_overflow; + int64_t imagemaster2_overflow; + int64_t imagemaster3_overflow; + int64_t imagemaster4_overflow; + int64_t imagemaster5_overflow; + int64_t imagemaster6_overflow; + int64_t be_overflow; + int64_t bg_overflow; + int64_t bf_overflow; + int64_t awb_overflow; + int64_t rs_overflow; + int64_t cs_overflow; + int64_t ihist_overflow; + int64_t skinbhist_overflow; + int64_t bfscale_overflow; + + int64_t isp_vfe0_active; + int64_t isp_vfe0_ab; + int64_t isp_vfe0_ib; + + int64_t isp_vfe1_active; + int64_t isp_vfe1_ab; + int64_t isp_vfe1_ib; + + int64_t isp_cpp_active; + int64_t isp_cpp_ab; + int64_t isp_cpp_ib; + + int64_t last_overflow_ab; + int64_t last_overflow_ib; + + int64_t vfe_clk_rate; + int64_t cpp_clk_rate; +}; + +enum msm_isp_hw_client { + ISP_VFE0, + ISP_VFE1, + ISP_CPP, + MAX_ISP_CLIENT, +}; + +struct msm_isp_bandwidth_info { + uint32_t active; + uint64_t ab; + uint64_t ib; +}; + +struct msm_isp_bw_req_info { + uint32_t client; + unsigned long long timestamp; + uint64_t total_ab; + uint64_t total_ib; + struct msm_isp_bandwidth_info client_info[MAX_ISP_CLIENT]; +}; + +#define MSM_ISP_MAX_WM 7 +struct msm_isp_ub_info { + enum msm_wm_ub_cfg_type policy; + uint8_t num_wm; + uint32_t wm_ub; + uint32_t data[MSM_ISP_MAX_WM]; + uint64_t addr[MSM_ISP_MAX_WM]; +}; + +struct msm_vfe_hw_init_parms { + const char *entries; + const char *regs; + const char *settings; +}; + +struct vfe_device { + struct platform_device *pdev; + struct msm_sd_subdev subdev; + struct resource *vfe_irq; + struct resource *vfe_mem; + struct resource *vfe_vbif_mem; + struct resource *vfe_io; + struct resource *vfe_vbif_io; + void __iomem *vfe_base; + void __iomem *vfe_vbif_base; + + struct device *iommu_ctx[MAX_IOMMU_CTX]; + /*Add secure context banks*/ + struct device *iommu_secure_ctx[MAX_IOMMU_CTX]; + + struct regulator *fs_vfe; + struct clk **vfe_clk; + uint32_t num_clk; + + uint32_t bus_perf_client; + + struct completion reset_complete; + struct completion halt_complete; + struct completion stream_config_complete; + struct completion stats_config_complete; + struct mutex realtime_mutex; + struct mutex core_mutex; + + atomic_t irq_cnt; + uint8_t taskletq_idx; + spinlock_t tasklet_lock; + spinlock_t shared_data_lock; + struct list_head tasklet_q; + struct tasklet_struct vfe_tasklet; + struct msm_vfe_tasklet_queue_cmd + tasklet_queue_cmd[MSM_VFE_TASKLETQ_SIZE]; + + uint32_t vfe_hw_version; + struct msm_vfe_hardware_info *hw_info; + struct msm_vfe_axi_shared_data axi_data; + struct msm_vfe_stats_shared_data stats_data; + struct msm_vfe_error_info error_info; + struct msm_isp_buf_mgr *buf_mgr; + int dump_reg; + int vfe_clk_idx; + uint32_t vfe_open_cnt; + uint8_t vt_enable; + uint8_t ignore_error; + struct msm_isp_statistics *stats; + struct msm_vfe_fetch_engine_info fetch_engine_info; + uint64_t msm_isp_last_overflow_ab; + uint64_t msm_isp_last_overflow_ib; + uint64_t msm_isp_vfe_clk_rate; + struct msm_isp_ub_info *ub_info; + uint32_t vfe_ub_policy; + uint32_t isp_sof_debug; + uint8_t reset_pending; + uint32_t bus_util_factor; + uint8_t vfe_reset_timeout_processed; +}; + +#endif diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.c new file mode 100644 index 000000000000..55a10ca7e559 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.c @@ -0,0 +1,2095 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include "msm_isp_util_32.h" +#include "msm_isp_axi_util_32.h" + +#define SRC_TO_INTF(src) \ + ((src < RDI_INTF_0 || src == VFE_AXI_SRC_MAX) ? VFE_PIX_0 : \ + (VFE_RAW_0 + src - RDI_INTF_0)) + +#define HANDLE_TO_IDX(handle) (handle & 0xFF) + +int msm_isp_axi_create_stream( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd) +{ + uint32_t i = stream_cfg_cmd->stream_src; + + if (i >= VFE_AXI_SRC_MAX) { + pr_err("%s:%d invalid stream_src %d\n", __func__, __LINE__, + stream_cfg_cmd->stream_src); + return -EINVAL; + } + + if ((axi_data->stream_handle_cnt << 8) == 0) + axi_data->stream_handle_cnt++; + + stream_cfg_cmd->axi_stream_handle = + (++axi_data->stream_handle_cnt) << 8 | i; + + memset(&axi_data->stream_info[i], 0, + sizeof(struct msm_vfe_axi_stream)); + spin_lock_init(&axi_data->stream_info[i].lock); + axi_data->stream_info[i].session_id = stream_cfg_cmd->session_id; + axi_data->stream_info[i].stream_id = stream_cfg_cmd->stream_id; + axi_data->stream_info[i].buf_divert = stream_cfg_cmd->buf_divert; + axi_data->stream_info[i].state = INACTIVE; + axi_data->stream_info[i].stream_handle = + stream_cfg_cmd->axi_stream_handle; + axi_data->stream_info[i].controllable_output = + stream_cfg_cmd->controllable_output; + if (stream_cfg_cmd->controllable_output) + stream_cfg_cmd->frame_skip_pattern = SKIP_ALL; + return 0; +} + +void msm_isp_axi_destroy_stream( + struct msm_vfe_axi_shared_data *axi_data, int stream_idx) +{ + if (axi_data->stream_info[stream_idx].state != AVAILABLE) { + axi_data->stream_info[stream_idx].state = AVAILABLE; + axi_data->stream_info[stream_idx].stream_handle = 0; + } else { + pr_err("%s: stream does not exist\n", __func__); + } +} + +int msm_isp_validate_axi_request(struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd) +{ + int rc = -1, i; + struct msm_vfe_axi_stream *stream_info = NULL; + + if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle) < MAX_NUM_STREAM) { + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)]; + } else { + pr_err("%s: Invalid axi_stream_handle\n", __func__); + return rc; + } + + if (!stream_info) { + pr_err("%s: Stream info is NULL\n", __func__); + return -EINVAL; + } + + switch (stream_cfg_cmd->output_format) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + case V4L2_PIX_FMT_SBGGR14: + case V4L2_PIX_FMT_SGBRG14: + case V4L2_PIX_FMT_SGRBG14: + case V4L2_PIX_FMT_SRGGB14: + case V4L2_PIX_FMT_QBGGR8: + case V4L2_PIX_FMT_QGBRG8: + case V4L2_PIX_FMT_QGRBG8: + case V4L2_PIX_FMT_QRGGB8: + case V4L2_PIX_FMT_QBGGR10: + case V4L2_PIX_FMT_QGBRG10: + case V4L2_PIX_FMT_QGRBG10: + case V4L2_PIX_FMT_QRGGB10: + case V4L2_PIX_FMT_QBGGR12: + case V4L2_PIX_FMT_QGBRG12: + case V4L2_PIX_FMT_QGRBG12: + case V4L2_PIX_FMT_QRGGB12: + case V4L2_PIX_FMT_QBGGR14: + case V4L2_PIX_FMT_QGBRG14: + case V4L2_PIX_FMT_QGRBG14: + case V4L2_PIX_FMT_QRGGB14: + case V4L2_PIX_FMT_P16BGGR10: + case V4L2_PIX_FMT_P16GBRG10: + case V4L2_PIX_FMT_P16GRBG10: + case V4L2_PIX_FMT_P16RGGB10: + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_META: + stream_info->num_planes = 1; + stream_info->format_factor = ISP_Q2; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV14: + case V4L2_PIX_FMT_NV41: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + stream_info->num_planes = 2; + stream_info->format_factor = 1.5 * ISP_Q2; + break; + /*TD: Add more image format*/ + default: + msm_isp_print_fourcc_error(__func__, + stream_cfg_cmd->output_format); + return rc; + } + + if (axi_data->hw_info->num_wm - axi_data->num_used_wm < + stream_info->num_planes) { + pr_err("%s: No free write masters\n", __func__); + return rc; + } + + if ((stream_info->num_planes > 1) && + (axi_data->hw_info->num_comp_mask - + axi_data->num_used_composite_mask < 1)) { + pr_err("%s: No free composite mask\n", __func__); + return rc; + } + + if (stream_cfg_cmd->init_frame_drop >= MAX_INIT_FRAME_DROP) { + pr_err("%s: Invalid skip pattern\n", __func__); + return rc; + } + + if (stream_cfg_cmd->frame_skip_pattern >= MAX_SKIP) { + pr_err("%s: Invalid skip pattern\n", __func__); + return rc; + } + + for (i = 0; i < stream_info->num_planes; i++) { + stream_info->plane_cfg[i] = stream_cfg_cmd->plane_cfg[i]; + stream_info->max_width = max(stream_info->max_width, + stream_cfg_cmd->plane_cfg[i].output_width); + } + + stream_info->output_format = stream_cfg_cmd->output_format; + stream_info->runtime_output_format = stream_info->output_format; + stream_info->stream_src = stream_cfg_cmd->stream_src; + stream_info->frame_based = stream_cfg_cmd->frame_base; + return 0; +} + +static uint32_t msm_isp_axi_get_plane_size( + struct msm_vfe_axi_stream *stream_info, int plane_idx) +{ + uint32_t size = 0; + struct msm_vfe_axi_plane_cfg *plane_cfg = stream_info->plane_cfg; + + switch (stream_info->output_format) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_QBGGR8: + case V4L2_PIX_FMT_QGBRG8: + case V4L2_PIX_FMT_QGRBG8: + case V4L2_PIX_FMT_QRGGB8: + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_META: + size = plane_cfg[plane_idx].output_height * + plane_cfg[plane_idx].output_width; + break; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_QBGGR10: + case V4L2_PIX_FMT_QGBRG10: + case V4L2_PIX_FMT_QGRBG10: + case V4L2_PIX_FMT_QRGGB10: + /* TODO: fix me */ + size = plane_cfg[plane_idx].output_height * + plane_cfg[plane_idx].output_width; + break; + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + case V4L2_PIX_FMT_QBGGR12: + case V4L2_PIX_FMT_QGBRG12: + case V4L2_PIX_FMT_QGRBG12: + case V4L2_PIX_FMT_QRGGB12: + case V4L2_PIX_FMT_SBGGR14: + case V4L2_PIX_FMT_SGBRG14: + case V4L2_PIX_FMT_SGRBG14: + case V4L2_PIX_FMT_SRGGB14: + case V4L2_PIX_FMT_QBGGR14: + case V4L2_PIX_FMT_QGBRG14: + case V4L2_PIX_FMT_QGRBG14: + case V4L2_PIX_FMT_QRGGB14: + /* TODO: fix me */ + size = plane_cfg[plane_idx].output_height * + plane_cfg[plane_idx].output_width; + break; + case V4L2_PIX_FMT_P16BGGR10: + case V4L2_PIX_FMT_P16GBRG10: + case V4L2_PIX_FMT_P16GRBG10: + case V4L2_PIX_FMT_P16RGGB10: + size = plane_cfg[plane_idx].output_height * + plane_cfg[plane_idx].output_width; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + if (plane_cfg[plane_idx].output_plane_format == Y_PLANE) + size = plane_cfg[plane_idx].output_height * + plane_cfg[plane_idx].output_width; + else + size = plane_cfg[plane_idx].output_height * + plane_cfg[plane_idx].output_width; + break; + case V4L2_PIX_FMT_NV14: + case V4L2_PIX_FMT_NV41: + if (plane_cfg[plane_idx].output_plane_format == Y_PLANE) + size = plane_cfg[plane_idx].output_height * + plane_cfg[plane_idx].output_width; + else + size = plane_cfg[plane_idx].output_height * + plane_cfg[plane_idx].output_width; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + size = plane_cfg[plane_idx].output_height * + plane_cfg[plane_idx].output_width; + break; + /*TD: Add more image format*/ + default: + msm_isp_print_fourcc_error(__func__, + stream_info->output_format); + break; + } + return size; +} + +void msm_isp_axi_reserve_wm(struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info) +{ + int i, j; + + for (i = 0; i < stream_info->num_planes; i++) { + for (j = 0; j < axi_data->hw_info->num_wm; j++) { + if (!axi_data->free_wm[j]) { + axi_data->free_wm[j] = + stream_info->stream_handle; + axi_data->wm_image_size[j] = + msm_isp_axi_get_plane_size( + stream_info, i); + axi_data->num_used_wm++; + break; + } + } + stream_info->wm[i] = j; + } +} + +void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info) +{ + int i; + + for (i = 0; i < stream_info->num_planes; i++) { + axi_data->free_wm[stream_info->wm[i]] = 0; + axi_data->num_used_wm--; + } + if (stream_info->stream_src <= IDEAL_RAW) + axi_data->num_pix_stream++; + else if (stream_info->stream_src < VFE_AXI_SRC_MAX) + axi_data->num_rdi_stream++; +} + +void msm_isp_axi_reserve_comp_mask( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info) +{ + int i; + uint8_t comp_mask = 0; + + for (i = 0; i < stream_info->num_planes; i++) + comp_mask |= 1 << stream_info->wm[i]; + + for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) { + if (!axi_data->composite_info[i].stream_handle) { + axi_data->composite_info[i].stream_handle = + stream_info->stream_handle; + axi_data->composite_info[i]. + stream_composite_mask = comp_mask; + axi_data->num_used_composite_mask++; + break; + } + } + stream_info->comp_mask_index = i; +} + +static void msm_isp_axi_free_comp_mask(struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info) +{ + axi_data->composite_info[stream_info->comp_mask_index]. + stream_composite_mask = 0; + axi_data->composite_info[stream_info->comp_mask_index]. + stream_handle = 0; + axi_data->num_used_composite_mask--; +} + +static int msm_isp_axi_get_bufq_handles( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + int rc = 0; + + if (stream_info->stream_id & ISP_SCRATCH_BUF_BIT) { + stream_info->bufq_handle = + vfe_dev->buf_mgr->ops->get_bufq_handle( + vfe_dev->buf_mgr, stream_info->session_id, + stream_info->stream_id & ~ISP_SCRATCH_BUF_BIT); + if (stream_info->bufq_handle == 0) { + pr_err("%s: Stream 0x%x has no valid buffer queue\n", + __func__, (unsigned int)stream_info->stream_id); + rc = -EINVAL; + return rc; + } + + stream_info->bufq_scratch_handle = + vfe_dev->buf_mgr->ops->get_bufq_handle( + vfe_dev->buf_mgr, stream_info->session_id, + stream_info->stream_id); + if (stream_info->bufq_scratch_handle == 0) { + pr_err("%s: Stream 0x%x has no valid buffer queue\n", + __func__, (unsigned int)stream_info->stream_id); + rc = -EINVAL; + return rc; + } + } else { + stream_info->bufq_handle = + vfe_dev->buf_mgr->ops->get_bufq_handle( + vfe_dev->buf_mgr, stream_info->session_id, + stream_info->stream_id); + if (stream_info->bufq_handle == 0) { + pr_err("%s: Stream 0x%x has no valid buffer queue\n", + __func__, (unsigned int)stream_info->stream_id); + rc = -EINVAL; + return rc; + } + } + return rc; +} + +int msm_isp_axi_check_stream_state( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd) +{ + int rc = 0, i; + unsigned long flags; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info; + enum msm_vfe_axi_state valid_state = + (stream_cfg_cmd->cmd == START_STREAM) ? INACTIVE : ACTIVE; + + if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) + return -EINVAL; + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= + MAX_NUM_STREAM) { + return -EINVAL; + } + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])]; + spin_lock_irqsave(&stream_info->lock, flags); + if (stream_info->state != valid_state) { + if ((stream_info->state == PAUSING || + stream_info->state == PAUSED || + stream_info->state == RESUME_PENDING || + stream_info->state == RESUMING) && + (stream_cfg_cmd->cmd == STOP_STREAM || + stream_cfg_cmd->cmd == STOP_IMMEDIATELY)) { + stream_info->state = ACTIVE; + } else { + pr_err("%s: Invalid stream state: %d\n", + __func__, stream_info->state); + spin_unlock_irqrestore( + &stream_info->lock, flags); + rc = -EINVAL; + break; + } + } + spin_unlock_irqrestore(&stream_info->lock, flags); + + if (stream_cfg_cmd->cmd == START_STREAM) { + rc = msm_isp_axi_get_bufq_handles(vfe_dev, stream_info); + if (rc) + break; + } + } + return rc; +} + +void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev, + uint8_t input_src) +{ + int i; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info; + + for (i = 0; i < MAX_NUM_STREAM; i++) { + stream_info = &axi_data->stream_info[i]; + if (stream_info->state != ACTIVE) + continue; + + if (stream_info->runtime_framedrop_update) { + stream_info->runtime_init_frame_drop--; + if (stream_info->runtime_init_frame_drop == 0) { + stream_info->runtime_framedrop_update = 0; + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_framedrop(vfe_dev, stream_info); + } + } + if (stream_info->stream_type == BURST_STREAM && + ((1 << SRC_TO_INTF(stream_info->stream_src)) & + input_src)) { + if (stream_info->runtime_framedrop_update_burst) { + stream_info->runtime_framedrop_update_burst = 0; + stream_info->runtime_burst_frame_count = + stream_info->runtime_init_frame_drop + + (stream_info->runtime_num_burst_capture - + 1) * + (stream_info->framedrop_period + 1) + 1; + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_framedrop(vfe_dev, stream_info); + } else { + stream_info->runtime_burst_frame_count--; + if (stream_info-> + runtime_burst_frame_count == 0) { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_framedrop(vfe_dev, stream_info); + } + } + } + } +} + +void msm_isp_reset_framedrop(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + stream_info->runtime_init_frame_drop = stream_info->init_frame_drop; + stream_info->runtime_burst_frame_count = + stream_info->burst_frame_count; + stream_info->runtime_num_burst_capture = + stream_info->num_burst_capture; + stream_info->runtime_framedrop_update = stream_info->framedrop_update; + vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(vfe_dev, stream_info); +} + +void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type, + enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts) +{ + struct msm_isp32_event_data event_data; + + memset(&event_data, 0, sizeof(event_data)); + switch (event_type) { + case ISP_EVENT_SOF: + if ((frame_src == VFE_PIX_0) && (vfe_dev->isp_sof_debug < 5)) { + pr_err("%s: PIX0 frame id: %u\n", __func__, + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); + vfe_dev->isp_sof_debug++; + } + vfe_dev->axi_data.src_info[frame_src].frame_id++; + if (vfe_dev->axi_data.src_info[frame_src].frame_id == 0) + vfe_dev->axi_data.src_info[frame_src].frame_id = 1; + ISP_DBG("%s: frame_src %d frame id: %u\n", __func__, + frame_src, + vfe_dev->axi_data.src_info[frame_src].frame_id); + break; + case ISP_EVENT_REG_UPDATE: + vfe_dev->axi_data.src_info[frame_src].last_updt_frm_id = 0; + break; + default: + break; + } + + event_data.input_intf = frame_src; + event_data.frame_id = vfe_dev->axi_data.src_info[frame_src].frame_id; + event_data.timestamp = ts->event_time; + event_data.mono_timestamp = ts->buf_time; + msm_isp_send_event(vfe_dev, event_type | frame_src, &event_data); +} + +void msm_isp_calculate_framedrop( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd) +{ + uint32_t framedrop_period = 0; + struct msm_vfe_axi_stream *stream_info = NULL; + + if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle) < MAX_NUM_STREAM) { + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)]; + } else { + pr_err("%s: Invalid stream handle", __func__); + return; + } + if (!stream_info) { + pr_err("%s: Stream info is NULL\n", __func__); + return; + } + + framedrop_period = msm_isp_get_framedrop_period( + stream_cfg_cmd->frame_skip_pattern); + stream_info->frame_skip_pattern = + stream_cfg_cmd->frame_skip_pattern; + if (stream_cfg_cmd->frame_skip_pattern == SKIP_ALL) + stream_info->framedrop_pattern = 0x0; + else + stream_info->framedrop_pattern = 0x1; + stream_info->framedrop_period = framedrop_period - 1; + + if (stream_cfg_cmd->init_frame_drop < framedrop_period) { + stream_info->framedrop_pattern <<= + stream_cfg_cmd->init_frame_drop; + stream_info->init_frame_drop = 0; + stream_info->framedrop_update = 0; + } else { + stream_info->init_frame_drop = stream_cfg_cmd->init_frame_drop; + stream_info->framedrop_update = 1; + } + + if (stream_cfg_cmd->burst_count > 0) { + stream_info->stream_type = BURST_STREAM; + stream_info->num_burst_capture = + stream_cfg_cmd->burst_count; + stream_info->burst_frame_count = + stream_cfg_cmd->init_frame_drop + + (stream_cfg_cmd->burst_count - 1) * + framedrop_period + 1; + } else { + stream_info->stream_type = CONTINUOUS_STREAM; + stream_info->burst_frame_count = 0; + stream_info->num_burst_capture = 0; + } +} + +static void msm_isp_calculate_bandwidth( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info) +{ + int bpp = 0; + + if (stream_info->stream_src < RDI_INTF_0) { + stream_info->bandwidth = + (axi_data->src_info[VFE_PIX_0].pixel_clock / + axi_data->src_info[VFE_PIX_0].width) * + stream_info->max_width; + stream_info->bandwidth = (unsigned long)stream_info->bandwidth * + stream_info->format_factor / ISP_Q2; + } else { + int rdi = SRC_TO_INTF(stream_info->stream_src); + + bpp = msm_isp_get_bit_per_pixel(stream_info->output_format); + if (rdi < VFE_SRC_MAX) + stream_info->bandwidth = + (axi_data->src_info[rdi].pixel_clock / 8) * bpp; + else + pr_err("%s: Invalid rdi interface\n", __func__); + } +} + +#ifdef CONFIG_MSM_AVTIMER +void msm_isp_start_avtimer(void) +{ + avcs_core_open(); + avcs_core_disable_power_collapse(1); +} + +static inline void msm_isp_get_avtimer_ts( + struct msm_isp_timestamp *time_stamp) +{ + int rc = 0; + uint32_t avtimer_usec = 0; + uint64_t avtimer_tick = 0; + + rc = avcs_core_query_timer(&avtimer_tick); + if (rc < 0) { + pr_err("%s: Error: Invalid AVTimer Tick, rc=%d\n", + __func__, rc); + /* In case of error return zero AVTimer Tick Value */ + time_stamp->vt_time.tv_sec = 0; + time_stamp->vt_time.tv_usec = 0; + } else { + avtimer_usec = do_div(avtimer_tick, USEC_PER_SEC); + time_stamp->vt_time.tv_sec = (uint32_t)(avtimer_tick); + time_stamp->vt_time.tv_usec = avtimer_usec; + pr_debug("%s: AVTimer TS = %u:%u\n", __func__, + (uint32_t)(avtimer_tick), avtimer_usec); + } +} +#else +void msm_isp_start_avtimer(void) +{ + pr_err("AV Timer is not supported\n"); +} + +inline void msm_isp_get_avtimer_ts( + struct msm_isp_timestamp *time_stamp) +{ + pr_err_ratelimited("%s: Error: AVTimer driver not available\n", + __func__); + time_stamp->vt_time.tv_sec = 0; + time_stamp->vt_time.tv_usec = 0; +} +#endif + +int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0, i; + uint32_t io_format = 0; + struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd = arg; + struct msm_vfe_axi_stream *stream_info; + + rc = msm_isp_axi_create_stream( + &vfe_dev->axi_data, stream_cfg_cmd); + if (rc) { + pr_err("%s: create stream failed\n", __func__); + return rc; + } + + rc = msm_isp_validate_axi_request( + &vfe_dev->axi_data, stream_cfg_cmd); + if (rc) { + pr_err("%s: Request validation failed\n", __func__); + if (HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle) < + MAX_NUM_STREAM) + msm_isp_axi_destroy_stream(&vfe_dev->axi_data, + HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)); + return rc; + } + stream_info = &vfe_dev->axi_data. + stream_info[HANDLE_TO_IDX(stream_cfg_cmd->axi_stream_handle)]; + if (!stream_info) { + pr_err("%s: can not find stream handle %x\n", __func__, + stream_cfg_cmd->axi_stream_handle); + return -EINVAL; + } + + stream_info->memory_input = stream_cfg_cmd->memory_input; + + msm_isp_axi_reserve_wm(&vfe_dev->axi_data, stream_info); + + if (stream_info->stream_src < RDI_INTF_0) { + io_format = vfe_dev->axi_data.src_info[VFE_PIX_0].input_format; + if (stream_info->stream_src == CAMIF_RAW || + stream_info->stream_src == IDEAL_RAW) { + if (stream_info->stream_src == CAMIF_RAW && + io_format != stream_info->output_format) + pr_debug("%s: Overriding input format\n", + __func__); + + io_format = stream_info->output_format; + } + rc = vfe_dev->hw_info->vfe_ops.axi_ops.cfg_io_format( + vfe_dev, stream_info->stream_src, io_format); + if (rc) { + pr_err("%s: cfg io format failed\n", __func__); + msm_isp_axi_free_wm(&vfe_dev->axi_data, + stream_info); + msm_isp_axi_destroy_stream(&vfe_dev->axi_data, + HANDLE_TO_IDX( + stream_cfg_cmd->axi_stream_handle)); + return rc; + } + } + + msm_isp_calculate_framedrop(&vfe_dev->axi_data, stream_cfg_cmd); + if (stream_cfg_cmd->vt_enable && !vfe_dev->vt_enable) { + vfe_dev->vt_enable = stream_cfg_cmd->vt_enable; + msm_isp_start_avtimer(); + } + if (stream_info->num_planes > 1) { + msm_isp_axi_reserve_comp_mask( + &vfe_dev->axi_data, stream_info); + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_comp_mask(vfe_dev, stream_info); + } else { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_irq_mask(vfe_dev, stream_info); + } + + for (i = 0; i < stream_info->num_planes; i++) { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_reg(vfe_dev, stream_info, i); + + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_xbar_reg(vfe_dev, stream_info, i); + } + return rc; +} + +int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0, i; + struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_stream_cfg_cmd stream_cfg; + + + if (HANDLE_TO_IDX(stream_release_cmd->stream_handle) >= + MAX_NUM_STREAM) { + pr_err("%s: Invalid stream handle\n", __func__); + return -EINVAL; + } + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(stream_release_cmd->stream_handle)]; + if (stream_info->state == AVAILABLE) { + pr_err("%s: Stream already released\n", __func__); + return -EINVAL; + } else if (stream_info->state != INACTIVE) { + stream_cfg.cmd = STOP_STREAM; + stream_cfg.num_streams = 1; + stream_cfg.stream_handle[0] = stream_release_cmd->stream_handle; + msm_isp_cfg_axi_stream(vfe_dev, (void *) &stream_cfg); + } + + for (i = 0; i < stream_info->num_planes; i++) { + vfe_dev->hw_info->vfe_ops.axi_ops. + clear_wm_reg(vfe_dev, stream_info, i); + + vfe_dev->hw_info->vfe_ops.axi_ops. + clear_wm_xbar_reg(vfe_dev, stream_info, i); + } + + if (stream_info->num_planes > 1) { + vfe_dev->hw_info->vfe_ops.axi_ops. + clear_comp_mask(vfe_dev, stream_info); + msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info); + } else { + vfe_dev->hw_info->vfe_ops.axi_ops. + clear_wm_irq_mask(vfe_dev, stream_info); + } + + vfe_dev->hw_info->vfe_ops.axi_ops.clear_framedrop(vfe_dev, stream_info); + msm_isp_axi_free_wm(axi_data, stream_info); + + msm_isp_axi_destroy_stream(&vfe_dev->axi_data, + HANDLE_TO_IDX(stream_release_cmd->stream_handle)); + + return rc; +} + +static void msm_isp_axi_stream_enable_cfg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + int i; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + + if (stream_info->state == INACTIVE) + return; + for (i = 0; i < stream_info->num_planes; i++) { + if (stream_info->state == START_PENDING || + stream_info->state == RESUME_PENDING) { + vfe_dev->hw_info->vfe_ops.axi_ops. + enable_wm(vfe_dev, stream_info->wm[i], 1); + } else { + vfe_dev->hw_info->vfe_ops.axi_ops. + enable_wm(vfe_dev, stream_info->wm[i], 0); + /* Issue a reg update for Raw Snapshot Case + * since we dont have reg update ack + */ + if (stream_info->stream_src == CAMIF_RAW || + stream_info->stream_src == IDEAL_RAW) { + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, (1 << VFE_PIX_0)); + } + } + } + + if (stream_info->state == START_PENDING) + axi_data->num_active_stream++; + else if (stream_info->state == STOP_PENDING) + axi_data->num_active_stream--; +} + +void msm_isp_axi_stream_update(struct vfe_device *vfe_dev, uint8_t input_src) +{ + int i; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + + for (i = 0; i < MAX_NUM_STREAM; i++) { + if (axi_data->stream_info[i].state == START_PENDING || + axi_data->stream_info[i].state == + STOP_PENDING) { + if ((1 << + SRC_TO_INTF(axi_data->stream_info[i]. + stream_src)) & + input_src) { + msm_isp_axi_stream_enable_cfg( + vfe_dev, &axi_data->stream_info[i]); + axi_data->stream_info[i].state = + axi_data->stream_info[i].state == + START_PENDING ? STARTING : STOPPING; + } + } else if (axi_data->stream_info[i].state == STARTING || + axi_data->stream_info[i].state == STOPPING) { + if ((1 << + SRC_TO_INTF(axi_data->stream_info[i]. + stream_src)) & + input_src) { + axi_data->stream_info[i].state = + axi_data->stream_info[i].state == STARTING ? + ACTIVE : INACTIVE; + vfe_dev->axi_data.stream_update--; + } + } + } + + if (vfe_dev->axi_data.pipeline_update == DISABLE_CAMIF || + (vfe_dev->axi_data.pipeline_update == + DISABLE_CAMIF_IMMEDIATELY)) { + vfe_dev->hw_info->vfe_ops.stats_ops. + enable_module(vfe_dev, 0xFF, 0); + vfe_dev->axi_data.pipeline_update = NO_UPDATE; + } + + if (vfe_dev->axi_data.stream_update == 0) + complete(&vfe_dev->stream_config_complete); +} + +static void msm_isp_reload_ping_pong_offset(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + int i, j; + uint32_t flag; + struct msm_isp_buffer *buf; + + for (i = 0; i < 2; i++) { + buf = stream_info->buf[i]; + if (!buf) + continue; + flag = i ? VFE_PONG_FLAG : VFE_PING_FLAG; + for (j = 0; j < stream_info->num_planes; j++) { + vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr( + vfe_dev, stream_info->wm[j], flag, + buf->mapped_info[j].paddr + + stream_info->plane_cfg[j].plane_addr_offset); + } + } +} + +void msm_isp_axi_cfg_update(struct vfe_device *vfe_dev) +{ + int i, j; + uint32_t update_state; + unsigned long flags; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info; + + for (i = 0; i < MAX_NUM_STREAM; i++) { + stream_info = &axi_data->stream_info[i]; + if (stream_info->stream_type == BURST_STREAM || + stream_info->state == AVAILABLE) + continue; + spin_lock_irqsave(&stream_info->lock, flags); + if (stream_info->state == PAUSING) { + /*AXI Stopped, apply update*/ + stream_info->state = PAUSED; + msm_isp_reload_ping_pong_offset(vfe_dev, stream_info); + for (j = 0; j < stream_info->num_planes; j++) + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_reg(vfe_dev, stream_info, j); + /*Resume AXI*/ + stream_info->state = RESUME_PENDING; + msm_isp_axi_stream_enable_cfg( + vfe_dev, &axi_data->stream_info[i]); + stream_info->state = RESUMING; + } else if (stream_info->state == RESUMING) { + stream_info->runtime_output_format = + stream_info->output_format; + stream_info->state = ACTIVE; + } + spin_unlock_irqrestore(&stream_info->lock, flags); + } + + update_state = atomic_dec_return(&axi_data->axi_cfg_update); +} + +static void msm_isp_cfg_pong_address(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + int i; + struct msm_isp_buffer *buf = stream_info->buf[0]; + + for (i = 0; i < stream_info->num_planes; i++) + vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr( + vfe_dev, stream_info->wm[i], + VFE_PONG_FLAG, buf->mapped_info[i].paddr + + stream_info->plane_cfg[i].plane_addr_offset); + stream_info->buf[1] = buf; +} + +static void msm_isp_get_done_buf(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status, + struct msm_isp_buffer **done_buf) +{ + uint32_t pingpong_bit = 0, i; + + pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1); + for (i = 0; i < stream_info->num_planes; i++) { + if (pingpong_bit != + (~(pingpong_status >> stream_info->wm[i]) & 0x1)) { + pr_debug("%s: Write master ping pong mismatch. Status: 0x%x\n", + __func__, pingpong_status); + } + } + + *done_buf = stream_info->buf[pingpong_bit]; + + if (stream_info->controllable_output) { + stream_info->buf[pingpong_bit] = NULL; + stream_info->request_frm_num--; + } +} + +static int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status, + uint32_t pingpong_bit) +{ + int i, rc = -1; + struct msm_isp_buffer *buf = NULL; + uint32_t bufq_handle = 0, frame_id = 0; + uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle); + + if (stream_idx >= MAX_NUM_STREAM) { + pr_err("%s: Invalid stream_idx", __func__); + return rc; + } + + if (stream_info->controllable_output && !stream_info->request_frm_num) + return 0; + + frame_id = vfe_dev->axi_data. + src_info[SRC_TO_INTF(stream_info->stream_src)].frame_id; + if (frame_id && stream_info->frame_id && + stream_info->frame_id == frame_id) { + /* This could happen if reg update ack is delayed */ + pr_err("%s: Duplicate frame streamId:%d stream_fid:%d frame_id:%d\n", + __func__, stream_info->stream_id, stream_info->frame_id, + frame_id); + vfe_dev->error_info.stream_framedrop_count[stream_idx]++; + return rc; + } + + bufq_handle = stream_info->bufq_handle; + if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX) + rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr, + vfe_dev->pdev->id, bufq_handle, + MSM_ISP_INVALID_BUF_INDEX, &buf); + else { + pr_err("%s: Invalid stream index\n", __func__); + rc = -1; + } + + if (rc < 0) { + vfe_dev->error_info.stream_framedrop_count[stream_idx]++; + return rc; + } + + if (buf->num_planes != stream_info->num_planes) { + pr_err("%s: Invalid buffer\n", __func__); + rc = -EINVAL; + goto buf_error; + } + + for (i = 0; i < stream_info->num_planes; i++) + vfe_dev->hw_info->vfe_ops.axi_ops.update_ping_pong_addr( + vfe_dev, stream_info->wm[i], + pingpong_status, buf->mapped_info[i].paddr + + stream_info->plane_cfg[i].plane_addr_offset); + + stream_info->buf[pingpong_bit] = buf; + + return 0; +buf_error: + vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr, + buf->bufq_handle, buf->buf_idx); + return rc; +} + +static void msm_isp_process_done_buf(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, struct msm_isp_buffer *buf, + struct msm_isp_timestamp *ts) +{ + int rc; + struct msm_isp32_event_data buf_event; + struct timeval *time_stamp; + uint32_t stream_idx = HANDLE_TO_IDX(stream_info->stream_handle); + uint32_t frame_id; + uint32_t buf_src; + + memset(&buf_event, 0, sizeof(buf_event)); + + if (stream_idx >= MAX_NUM_STREAM) { + pr_err("%s: Invalid stream_idx", __func__); + return; + } + + if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX) + frame_id = vfe_dev->axi_data. + src_info[SRC_TO_INTF(stream_info->stream_src)].frame_id; + else { + pr_err("%s: Invalid stream index, put buf back to vb2 queue\n", + __func__); + vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr, + buf->bufq_handle, buf->buf_idx); + return; + } + + if (buf && ts) { + if (vfe_dev->vt_enable) { + msm_isp_get_avtimer_ts(ts); + time_stamp = &ts->vt_time; + } else + time_stamp = &ts->buf_time; + + rc = vfe_dev->buf_mgr->ops->get_buf_src(vfe_dev->buf_mgr, + buf->bufq_handle, &buf_src); + if (stream_info->buf_divert && rc == 0 && + buf_src != MSM_ISP_BUFFER_SRC_SCRATCH) { + rc = vfe_dev->buf_mgr->ops->buf_divert(vfe_dev->buf_mgr, + buf->bufq_handle, buf->buf_idx, + time_stamp, frame_id); + /* Buf divert return value represent whether the buf + * can be diverted. A positive return value means + * other ISP hardware is still processing the frame. + */ + if (rc == 0) { + buf_event.input_intf = + SRC_TO_INTF(stream_info->stream_src); + buf_event.frame_id = frame_id; + buf_event.timestamp = *time_stamp; + buf_event.u.buf_done.session_id = + stream_info->session_id; + buf_event.u.buf_done.stream_id = + stream_info->stream_id; + buf_event.u.buf_done.handle = + stream_info->bufq_handle; + buf_event.u.buf_done.buf_idx = buf->buf_idx; + buf_event.u.buf_done.output_format = + stream_info->runtime_output_format; + msm_isp_send_event(vfe_dev, + ISP_EVENT_BUF_DIVERT + stream_idx, + &buf_event); + } + } else { + buf_event.input_intf = + SRC_TO_INTF(stream_info->stream_src); + buf_event.frame_id = frame_id; + buf_event.timestamp = ts->buf_time; + buf_event.u.buf_done.session_id = + stream_info->session_id; + buf_event.u.buf_done.stream_id = + stream_info->stream_id; + buf_event.u.buf_done.output_format = + stream_info->runtime_output_format; + msm_isp_send_event(vfe_dev, + ISP_EVENT_BUF_DONE, &buf_event); + vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr, + buf->bufq_handle, buf->buf_idx, + time_stamp, frame_id, + stream_info->runtime_output_format); + } + } +} + +static enum msm_isp_camif_update_state + msm_isp_get_camif_update_state(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd) +{ + int i; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + uint8_t pix_stream_cnt = 0, cur_pix_stream_cnt; + + cur_pix_stream_cnt = + axi_data->src_info[VFE_PIX_0].pix_stream_count + + axi_data->src_info[VFE_PIX_0].raw_stream_count; + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + stream_info = + &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])]; + if (stream_info->stream_src < RDI_INTF_0) + pix_stream_cnt++; + } + + if ((pix_stream_cnt) && + (axi_data->src_info[VFE_PIX_0].input_mux != EXTERNAL_READ)) { + + if (cur_pix_stream_cnt == 0 && pix_stream_cnt && + stream_cfg_cmd->cmd == START_STREAM) + return ENABLE_CAMIF; + else if (cur_pix_stream_cnt && + (cur_pix_stream_cnt - pix_stream_cnt) == 0 && + stream_cfg_cmd->cmd == STOP_STREAM) + return DISABLE_CAMIF; + else if (cur_pix_stream_cnt && + (cur_pix_stream_cnt - pix_stream_cnt) == 0 && + stream_cfg_cmd->cmd == STOP_IMMEDIATELY) + return DISABLE_CAMIF_IMMEDIATELY; + } + + return NO_UPDATE; +} + +static void msm_isp_update_camif_output_count( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd) +{ + int i; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + + if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) + return; + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= + MAX_NUM_STREAM) { + return; + } + stream_info = + &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])]; + if (stream_info->stream_src >= RDI_INTF_0) + continue; + if (stream_info->stream_src == PIX_ENCODER || + stream_info->stream_src == PIX_VIEWFINDER || + stream_info->stream_src == PIX_VIDEO || + stream_info->stream_src == IDEAL_RAW) { + if (stream_cfg_cmd->cmd == START_STREAM) + vfe_dev->axi_data.src_info[VFE_PIX_0]. + pix_stream_count++; + else + vfe_dev->axi_data.src_info[VFE_PIX_0]. + pix_stream_count--; + } else if (stream_info->stream_src == CAMIF_RAW) { + if (stream_cfg_cmd->cmd == START_STREAM) + vfe_dev->axi_data.src_info[VFE_PIX_0]. + raw_stream_count++; + else + vfe_dev->axi_data.src_info[VFE_PIX_0]. + raw_stream_count--; + } + } +} + + +static void msm_isp_update_rdi_output_count( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd) +{ + int i; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + + if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) + return; + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) + > MAX_NUM_STREAM) + return; + stream_info = + &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])]; + if (stream_info->stream_src < RDI_INTF_0) + continue; + if (stream_info->stream_src == RDI_INTF_0) { + if (stream_cfg_cmd->cmd == START_STREAM) + vfe_dev->axi_data.src_info[VFE_RAW_0]. + raw_stream_count++; + else + vfe_dev->axi_data.src_info[VFE_RAW_0]. + raw_stream_count--; + } else if (stream_info->stream_src == RDI_INTF_1) { + if (stream_cfg_cmd->cmd == START_STREAM) + vfe_dev->axi_data.src_info[VFE_RAW_1]. + raw_stream_count++; + else + vfe_dev->axi_data.src_info[VFE_RAW_1]. + raw_stream_count--; + } else if (stream_info->stream_src == RDI_INTF_2) { + if (stream_cfg_cmd->cmd == START_STREAM) + vfe_dev->axi_data.src_info[VFE_RAW_2]. + raw_stream_count++; + else + vfe_dev->axi_data.src_info[VFE_RAW_2]. + raw_stream_count--; + } + + } +} + +static uint8_t msm_isp_get_curr_stream_cnt( + struct vfe_device *vfe_dev) +{ + uint8_t curr_stream_cnt = 0; + + curr_stream_cnt = vfe_dev->axi_data.src_info[VFE_RAW_0]. + raw_stream_count + + vfe_dev->axi_data.src_info[VFE_RAW_1]. + raw_stream_count + + vfe_dev->axi_data.src_info[VFE_RAW_2]. + raw_stream_count + + vfe_dev->axi_data.src_info[VFE_PIX_0]. + pix_stream_count + + vfe_dev->axi_data.src_info[VFE_PIX_0]. + raw_stream_count; + return curr_stream_cnt; +} + +/*Factor in Q2 format*/ +#define ISP_DEFAULT_FORMAT_FACTOR 6 +#define ISP_BUS_UTILIZATION_FACTOR 1536 /* 1.5 in Q10 format */ +static int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev) +{ + int i, rc = 0; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + uint64_t total_pix_bandwidth = 0, total_rdi_bandwidth = 0; + uint32_t num_pix_streams = 0; + uint32_t num_rdi_streams = 0; + uint32_t total_streams = 0; + uint64_t total_bandwidth = 0; + uint32_t bus_util_factor = ISP_BUS_UTILIZATION_FACTOR; + + for (i = 0; i < MAX_NUM_STREAM; i++) { + stream_info = &axi_data->stream_info[i]; + if (stream_info->state == ACTIVE || + stream_info->state == START_PENDING) { + if (stream_info->stream_src < RDI_INTF_0) { + total_pix_bandwidth += stream_info->bandwidth; + num_pix_streams++; + } else { + total_rdi_bandwidth += stream_info->bandwidth; + num_rdi_streams++; + } + } + } + total_bandwidth = total_pix_bandwidth + total_rdi_bandwidth; + total_streams = num_pix_streams + num_rdi_streams; + if (vfe_dev->bus_util_factor) + bus_util_factor = vfe_dev->bus_util_factor; + ISP_DBG("%s: bus_util_factor = %u\n", __func__, bus_util_factor); + + if (total_streams == 1) + rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id, + total_bandwidth, + (total_bandwidth * bus_util_factor / ISP_Q10)); + else + rc = msm_isp_update_bandwidth(ISP_VFE0 + vfe_dev->pdev->id, + (total_bandwidth + MSM_ISP_MIN_AB), (total_bandwidth * + bus_util_factor / ISP_Q10 + MSM_ISP_MIN_IB)); + if (rc < 0) + pr_err("%s: update failed\n", __func__); + + return rc; +} + +static int msm_isp_axi_wait_for_cfg_done(struct vfe_device *vfe_dev, + enum msm_isp_camif_update_state camif_update) +{ + int rc; + unsigned long flags; + + spin_lock_irqsave(&vfe_dev->shared_data_lock, flags); + init_completion(&vfe_dev->stream_config_complete); + vfe_dev->axi_data.pipeline_update = camif_update; + spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags); + rc = wait_for_completion_timeout( + &vfe_dev->stream_config_complete, + msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT)); + if (rc == 0) { + pr_err("%s: wait timeout\n", __func__); + rc = -1; + } else { + rc = 0; + } + return rc; +} + +static int msm_isp_init_stream_ping_pong_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + int rc = 0; + /*Set address for both PING & PONG register */ + rc = msm_isp_cfg_ping_pong_address(vfe_dev, + stream_info, VFE_PING_FLAG, 0); + if (rc < 0) { + pr_err("%s: No free buffer for ping\n", + __func__); + return rc; + } + + /* For burst stream of one capture, only one buffer + * is allocated. Duplicate ping buffer address to pong + * buffer to ensure hardware write to a valid address + */ + if (stream_info->stream_type == BURST_STREAM && + stream_info->runtime_num_burst_capture <= 1) { + msm_isp_cfg_pong_address(vfe_dev, stream_info); + } else { + rc = msm_isp_cfg_ping_pong_address(vfe_dev, + stream_info, VFE_PONG_FLAG, 1); + if (rc < 0) { + pr_err("%s: No free buffer for pong\n", + __func__); + return rc; + } + } + return rc; +} + +static void msm_isp_deinit_stream_ping_pong_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + int i; + + for (i = 0; i < 2; i++) { + struct msm_isp_buffer *buf; + + buf = stream_info->buf[i]; + if (buf) { + vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr, + buf->bufq_handle, buf->buf_idx); + } + } +} + +static void msm_isp_get_stream_wm_mask( + struct msm_vfe_axi_stream *stream_info, + uint32_t *wm_reload_mask) +{ + int i; + + for (i = 0; i < stream_info->num_planes; i++) + *wm_reload_mask |= (1 << stream_info->wm[i]); +} + +int msm_isp_axi_halt(struct vfe_device *vfe_dev, + struct msm_vfe_axi_halt_cmd *halt_cmd) +{ + int rc = 0; + + if (halt_cmd->overflow_detected) { + /*Store current IRQ mask*/ + if (vfe_dev->error_info.overflow_recover_irq_mask0 == 0) { + vfe_dev->hw_info->vfe_ops.core_ops.get_irq_mask(vfe_dev, + &vfe_dev->error_info.overflow_recover_irq_mask0, + &vfe_dev->error_info.overflow_recover_irq_mask1); + } + atomic_set(&vfe_dev->error_info.overflow_state, + OVERFLOW_DETECTED); + } + + rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1); + + if (halt_cmd->stop_camif) { + vfe_dev->hw_info->vfe_ops.core_ops. + update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY); + } + + return rc; +} + +int msm_isp_axi_reset(struct vfe_device *vfe_dev, + struct msm_vfe_axi_reset_cmd *reset_cmd) +{ + int rc = 0, i, j; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_isp_bufq *bufq = NULL; + struct msm_isp_timestamp timestamp; + + if (!reset_cmd) { + pr_err("%s: NULL pointer reset cmd %pK\n", __func__, reset_cmd); + rc = -1; + return rc; + } + + msm_isp_get_timestamp(×tamp, vfe_dev); + rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, + 0, reset_cmd->blocking); + + for (i = 0, j = 0; j < axi_data->num_active_stream && + i < MAX_NUM_STREAM; i++, j++) { + stream_info = &axi_data->stream_info[i]; + if (stream_info->stream_src >= VFE_AXI_SRC_MAX) { + rc = -1; + pr_err("%s invalid stream src = %d\n", __func__, + stream_info->stream_src); + break; + } + if (stream_info->state != ACTIVE) { + j--; + continue; + } + + bufq = vfe_dev->buf_mgr->ops->get_bufq(vfe_dev->buf_mgr, + stream_info->bufq_handle); + if (!bufq) { + pr_err("%s: bufq null %pK by handle %x\n", __func__, + bufq, stream_info->bufq_handle); + continue; + } + + if (bufq->buf_type != ISP_SHARE_BUF) { + msm_isp_deinit_stream_ping_pong_reg(vfe_dev, + stream_info); + } else { + vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr, + stream_info->bufq_handle, + MSM_ISP_BUFFER_FLUSH_ALL, + ×tamp.buf_time, + reset_cmd->frame_id); + } + axi_data->src_info[SRC_TO_INTF(stream_info->stream_src)]. + frame_id = reset_cmd->frame_id; + msm_isp_reset_burst_count_and_frame_drop(vfe_dev, stream_info); + } + + if (rc < 0) + pr_err("%s Error! reset hw Timed out\n", __func__); + + return rc; +} + +int msm_isp_axi_restart(struct vfe_device *vfe_dev, + struct msm_vfe_axi_restart_cmd *restart_cmd) +{ + int rc = 0, i, j; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + uint32_t wm_reload_mask = 0x0; + + for (i = 0, j = 0; j < axi_data->num_active_stream && + i < MAX_NUM_STREAM; i++, j++) { + stream_info = &axi_data->stream_info[i]; + if (stream_info->state != ACTIVE) { + j--; + continue; + } + msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask); + msm_isp_init_stream_ping_pong_reg(vfe_dev, stream_info); + } + + vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev, wm_reload_mask); + rc = vfe_dev->hw_info->vfe_ops.axi_ops.restart(vfe_dev, 0, + restart_cmd->enable_camif); + if (rc < 0) + pr_err("%s Error restarting HW\n", __func__); + + return rc; +} + +static int msm_isp_axi_update_cgc_override(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd, + uint8_t cgc_override) +{ + int i = 0, j = 0; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + + if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) + return -EINVAL; + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= + MAX_NUM_STREAM) { + return -EINVAL; + } + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])]; + for (j = 0; j < stream_info->num_planes; j++) { + if (vfe_dev->hw_info->vfe_ops.axi_ops. + update_cgc_override) + vfe_dev->hw_info->vfe_ops.axi_ops. + update_cgc_override(vfe_dev, + stream_info->wm[j], cgc_override); + } + } + return 0; +} + +static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd, + enum msm_isp_camif_update_state camif_update) +{ + int i, rc = 0; + uint8_t src_state, wait_for_complete = 0; + uint32_t wm_reload_mask = 0x0; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + uint8_t init_frm_drop = 0; + + if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM) + return -EINVAL; + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= + MAX_NUM_STREAM) { + return -EINVAL; + } + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])]; + if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX) + src_state = axi_data->src_info[ + SRC_TO_INTF(stream_info->stream_src)].active; + else { + pr_err("%s: invalid src info index\n", __func__); + return -EINVAL; + } + + msm_isp_calculate_bandwidth(axi_data, stream_info); + msm_isp_reset_framedrop(vfe_dev, stream_info); + init_frm_drop = stream_info->init_frame_drop; + msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask); + rc = msm_isp_init_stream_ping_pong_reg(vfe_dev, stream_info); + if (rc < 0) { + pr_err("%s: No buffer for stream %d\n", + __func__, + HANDLE_TO_IDX( + stream_cfg_cmd->stream_handle[i])); + return rc; + } + + stream_info->state = START_PENDING; + if (src_state) { + wait_for_complete = 1; + } else { + if (vfe_dev->dump_reg) + msm_camera_io_dump_2(vfe_dev->vfe_base, 0x900); + + /*Configure AXI start bits to start immediately*/ + msm_isp_axi_stream_enable_cfg(vfe_dev, stream_info); + stream_info->state = ACTIVE; + } + if (SRC_TO_INTF(stream_info->stream_src) != VFE_PIX_0 && + stream_info->stream_src < VFE_AXI_SRC_MAX) { + vfe_dev->axi_data.src_info[SRC_TO_INTF( + stream_info->stream_src)].frame_id = + init_frm_drop; + } + } + msm_isp_update_stream_bandwidth(vfe_dev); + vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev, wm_reload_mask); + vfe_dev->hw_info->vfe_ops.core_ops.reg_update(vfe_dev, 0xF); + msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd); + msm_isp_update_rdi_output_count(vfe_dev, stream_cfg_cmd); + if (camif_update == ENABLE_CAMIF) { + atomic_set(&vfe_dev->error_info.overflow_state, + NO_OVERFLOW); + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id = 0; + vfe_dev->hw_info->vfe_ops.core_ops. + update_camif_state(vfe_dev, camif_update); + } + + if (wait_for_complete) { + vfe_dev->axi_data.stream_update = stream_cfg_cmd->num_streams; + rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update); + } + + return rc; +} + +static int msm_isp_stop_axi_stream(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd, + enum msm_isp_camif_update_state camif_update) +{ + int i, rc = 0; + uint8_t wait_for_complete_for_this_stream = 0, cur_stream_cnt = 0; + uint8_t wait_for_complete = 0; + struct msm_vfe_axi_stream *stream_info = NULL; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + int ext_read = + axi_data->src_info[VFE_PIX_0].input_mux == EXTERNAL_READ; + + if (stream_cfg_cmd->num_streams > MAX_NUM_STREAM || + stream_cfg_cmd->num_streams == 0) + return -EINVAL; + + msm_isp_update_camif_output_count(vfe_dev, stream_cfg_cmd); + msm_isp_update_rdi_output_count(vfe_dev, stream_cfg_cmd); + cur_stream_cnt = msm_isp_get_curr_stream_cnt(vfe_dev); + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= + MAX_NUM_STREAM) { + return -EINVAL; + } + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])]; + wait_for_complete_for_this_stream = 0; + stream_info->state = STOP_PENDING; + if (stream_info->stream_src == CAMIF_RAW || + stream_info->stream_src == IDEAL_RAW) { + /* We dont get reg update IRQ for raw snapshot + * so frame skip cant be ocnfigured + */ + if ((camif_update != DISABLE_CAMIF_IMMEDIATELY) && + (!ext_read)) + wait_for_complete_for_this_stream = 1; + } else if (stream_info->stream_type == BURST_STREAM && + stream_info->runtime_num_burst_capture == 0) { + /* Configure AXI writemasters to stop immediately + * since for burst case, write masters already skip + * all frames. + */ + if (stream_info->stream_src == RDI_INTF_0 || + stream_info->stream_src == RDI_INTF_1 || + stream_info->stream_src == RDI_INTF_2) + wait_for_complete_for_this_stream = 1; + } else { + if ((camif_update != DISABLE_CAMIF_IMMEDIATELY) && + (!ext_read) && + !(stream_info->stream_src == RDI_INTF_0 || + stream_info->stream_src == RDI_INTF_1 || + stream_info->stream_src == RDI_INTF_2)) + wait_for_complete_for_this_stream = 1; + } + if (!wait_for_complete_for_this_stream) { + msm_isp_axi_stream_enable_cfg(vfe_dev, stream_info); + stream_info->state = INACTIVE; + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, 0xF); + } + wait_for_complete |= wait_for_complete_for_this_stream; + } + if (wait_for_complete) { + vfe_dev->axi_data.stream_update = stream_cfg_cmd->num_streams; + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, 0xF); + rc = msm_isp_axi_wait_for_cfg_done(vfe_dev, camif_update); + if (rc < 0) { + pr_err("%s: wait for config done failed\n", __func__); + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX( + stream_cfg_cmd->stream_handle[i])]; + stream_info->state = STOP_PENDING; + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, 0xF); + msm_isp_axi_stream_enable_cfg( + vfe_dev, stream_info); + stream_info->state = INACTIVE; + } + } + } + if (camif_update == DISABLE_CAMIF) { + vfe_dev->hw_info->vfe_ops.core_ops. + update_camif_state(vfe_dev, DISABLE_CAMIF); + } else if ((camif_update == DISABLE_CAMIF_IMMEDIATELY) || + (ext_read)) { + /*during stop immediately, stop output then stop input*/ + if (!ext_read) + vfe_dev->hw_info->vfe_ops.core_ops. + update_camif_state(vfe_dev, + DISABLE_CAMIF_IMMEDIATELY); + } + if (cur_stream_cnt == 0) { + vfe_dev->ignore_error = 1; + vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1); + vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 1, 1); + vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev); + vfe_dev->ignore_error = 0; + } + msm_isp_update_stream_bandwidth(vfe_dev); + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])]; + msm_isp_deinit_stream_ping_pong_reg(vfe_dev, stream_info); + } + + return rc; +} + + +int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0; + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + enum msm_isp_camif_update_state camif_update; + + rc = msm_isp_axi_check_stream_state(vfe_dev, stream_cfg_cmd); + if (rc < 0) { + pr_err("%s: Invalid stream state\n", __func__); + return rc; + } + + if (axi_data->num_active_stream == 0) { + /*Configure UB*/ + vfe_dev->hw_info->vfe_ops.axi_ops.cfg_ub(vfe_dev); + } + camif_update = msm_isp_get_camif_update_state(vfe_dev, stream_cfg_cmd); + + if (stream_cfg_cmd->cmd == START_STREAM) { + msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 1); + + rc = msm_isp_start_axi_stream( + vfe_dev, stream_cfg_cmd, camif_update); + } else { + rc = msm_isp_stop_axi_stream( + vfe_dev, stream_cfg_cmd, camif_update); + + msm_isp_axi_update_cgc_override(vfe_dev, stream_cfg_cmd, 0); + } + + if (rc < 0) + pr_err("%s: start/stop stream failed\n", __func__); + return rc; +} + +static int msm_isp_request_frame(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, uint32_t request_frm_num) +{ + struct msm_vfe32_axi_stream_request_cmd stream_cfg_cmd; + int rc = 0; + uint32_t pingpong_status, pingpong_bit, wm_reload_mask = 0x0; + + if (!stream_info->controllable_output) + return 0; + + if (!request_frm_num) { + pr_err("%s: Invalid frame request.\n", __func__); + return -EINVAL; + } + + stream_info->request_frm_num += request_frm_num; + + stream_cfg_cmd.axi_stream_handle = stream_info->stream_handle; + stream_cfg_cmd.frame_skip_pattern = NO_SKIP; + stream_cfg_cmd.init_frame_drop = 0; + stream_cfg_cmd.burst_count = stream_info->request_frm_num; + msm_isp_calculate_framedrop(&vfe_dev->axi_data, &stream_cfg_cmd); + msm_isp_reset_framedrop(vfe_dev, stream_info); + + if (stream_info->request_frm_num != request_frm_num) { + pingpong_status = + vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status( + vfe_dev); + pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1); + + if (!stream_info->buf[pingpong_bit]) { + rc = msm_isp_cfg_ping_pong_address(vfe_dev, stream_info, + pingpong_status, pingpong_bit); + if (rc) { + pr_err("%s:%d fail to set ping pong address\n", + __func__, __LINE__); + return rc; + } + } + + if (!stream_info->buf[!pingpong_bit] && request_frm_num > 1) { + rc = msm_isp_cfg_ping_pong_address(vfe_dev, stream_info, + ~pingpong_status, !pingpong_bit); + if (rc) { + pr_err("%s:%d fail to set ping pong address\n", + __func__, __LINE__); + return rc; + } + } + } else { + if (!stream_info->buf[0]) { + rc = msm_isp_cfg_ping_pong_address(vfe_dev, stream_info, + VFE_PING_FLAG, 0); + if (rc) { + pr_err("%s:%d fail to set ping pong address\n", + __func__, __LINE__); + return rc; + } + } + + if (!stream_info->buf[1] && request_frm_num > 1) { + rc = msm_isp_cfg_ping_pong_address(vfe_dev, stream_info, + VFE_PONG_FLAG, 1); + if (rc) { + pr_err("%s:%d fail to set ping pong address\n", + __func__, __LINE__); + return rc; + } + } + + msm_isp_get_stream_wm_mask(stream_info, &wm_reload_mask); + vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev, + wm_reload_mask); + } + + return rc; +} + +int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0, i, j; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream_update_cmd *update_cmd = arg; + struct msm_vfe_axi_stream_cfg_update_info *update_info; + uint32_t frame_id; + struct msm_isp_timestamp timestamp; + + if (update_cmd->update_type == UPDATE_STREAM_AXI_CONFIG && + atomic_read(&axi_data->axi_cfg_update)) { + pr_err("%s: AXI stream config updating\n", __func__); + return -EBUSY; + } + + /*num_stream is uint32 and update_info[] bound by MAX_NUM_STREAM*/ + if (update_cmd->num_streams > MAX_NUM_STREAM) + return -EINVAL; + + for (i = 0; i < update_cmd->num_streams; i++) { + update_info = &update_cmd->update_info[i]; + /*check array reference bounds*/ + if (HANDLE_TO_IDX(update_info->stream_handle) >= + MAX_NUM_STREAM) { + return -EINVAL; + } + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(update_info->stream_handle)]; + if (stream_info->state != ACTIVE && + stream_info->state != INACTIVE) { + pr_err("%s: Invalid stream state\n", __func__); + return -EINVAL; + } + } + + for (i = 0; i < update_cmd->num_streams; i++) { + update_info = &update_cmd->update_info[i]; + stream_info = &axi_data->stream_info[ + HANDLE_TO_IDX(update_info->stream_handle)]; + + switch (update_cmd->update_type) { + case ENABLE_STREAM_BUF_DIVERT: + stream_info->buf_divert = 1; + break; + case DISABLE_STREAM_BUF_DIVERT: + msm_isp_get_timestamp(×tamp, vfe_dev); + stream_info->buf_divert = 0; + frame_id = vfe_dev->axi_data. + src_info[SRC_TO_INTF( + stream_info->stream_src)]. + frame_id; + vfe_dev->buf_mgr->ops->flush_buf(vfe_dev->buf_mgr, + stream_info->bufq_handle, + MSM_ISP_BUFFER_FLUSH_DIVERTED, + ×tamp.buf_time, frame_id); + break; + case UPDATE_STREAM_FRAMEDROP_PATTERN: { + uint32_t framedrop_period = + msm_isp_get_framedrop_period( + update_info->skip_pattern); + if (update_info->skip_pattern == SKIP_ALL) + stream_info->framedrop_pattern = 0x0; + else + stream_info->framedrop_pattern = 0x1; + stream_info->framedrop_period = framedrop_period - 1; + if (stream_info->stream_type == BURST_STREAM) { + stream_info->runtime_framedrop_update_burst = 1; + } else { + stream_info->runtime_init_frame_drop = 0; + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_framedrop(vfe_dev, stream_info); + } + break; + } + case UPDATE_STREAM_AXI_CONFIG: { + for (j = 0; j < stream_info->num_planes; j++) { + stream_info->plane_cfg[j] = + update_info->plane_cfg[j]; + } + stream_info->output_format = update_info->output_format; + if (stream_info->state == ACTIVE) { + stream_info->state = PAUSE_PENDING; + msm_isp_axi_stream_enable_cfg( + vfe_dev, stream_info); + stream_info->state = PAUSING; + atomic_set(&axi_data->axi_cfg_update, + UPDATE_REQUESTED); + } else { + for (j = 0; j < stream_info->num_planes; j++) + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_reg(vfe_dev, stream_info, j); + stream_info->runtime_output_format = + stream_info->output_format; + } + break; + } + case UPDATE_STREAM_REQUEST_FRAMES: { + rc = msm_isp_request_frame(vfe_dev, stream_info, + update_info->frame_id); + if (rc) + pr_err("%s failed to request frame!\n", + __func__); + break; + } + default: + pr_err("%s: Invalid update type\n", __func__); + return -EINVAL; + } + } + return rc; +} + +void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts) +{ + int i, rc = 0; + struct msm_isp_buffer *done_buf = NULL; + uint32_t comp_mask = 0, wm_mask = 0; + uint32_t pingpong_status, stream_idx; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_composite_info *comp_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + uint32_t pingpong_bit = 0, frame_id = 0; + + comp_mask = vfe_dev->hw_info->vfe_ops.axi_ops. + get_comp_mask(irq_status0, irq_status1); + wm_mask = vfe_dev->hw_info->vfe_ops.axi_ops. + get_wm_mask(irq_status0, irq_status1); + if (!(comp_mask || wm_mask)) + return; + + ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0); + pingpong_status = + vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev); + for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) { + rc = 0; + comp_info = &axi_data->composite_info[i]; + if (comp_mask & (1 << i)) { + stream_idx = HANDLE_TO_IDX(comp_info->stream_handle); + if ((!comp_info->stream_handle) || + (stream_idx >= MAX_NUM_STREAM)) { + pr_err("%s: Invalid handle for composite irq\n", + __func__); + } else { + stream_idx = + HANDLE_TO_IDX(comp_info->stream_handle); + stream_info = + &axi_data->stream_info[stream_idx]; + + pingpong_bit = (~(pingpong_status >> + stream_info->wm[0]) & 0x1); + + if (stream_info->stream_type == BURST_STREAM) + stream_info-> + runtime_num_burst_capture--; + + msm_isp_get_done_buf(vfe_dev, stream_info, + pingpong_status, &done_buf); + if (stream_info->stream_type == + CONTINUOUS_STREAM || + stream_info-> + runtime_num_burst_capture > 1) { + rc = msm_isp_cfg_ping_pong_address( + vfe_dev, stream_info, + pingpong_status, + pingpong_bit); + } + frame_id = vfe_dev->axi_data. + src_info[SRC_TO_INTF( + stream_info->stream_src)]. + frame_id; + stream_info->frame_id = frame_id; + ISP_DBG("%s: stream id:%d frame id:%d\n", + __func__, stream_info->stream_id, + stream_info->frame_id); + if (done_buf && !rc) + msm_isp_process_done_buf(vfe_dev, + stream_info, done_buf, ts); + } + } + wm_mask &= ~(comp_info->stream_composite_mask); + } + + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + if (wm_mask & (1 << i)) { + stream_idx = HANDLE_TO_IDX(axi_data->free_wm[i]); + if ((!axi_data->free_wm[i]) || + (stream_idx >= MAX_NUM_STREAM)) { + pr_err("%s: Invalid handle for wm irq\n", + __func__); + continue; + } + stream_info = &axi_data->stream_info[stream_idx]; + + pingpong_bit = (~(pingpong_status >> + stream_info->wm[0]) & 0x1); + + if (stream_info->stream_type == BURST_STREAM) + stream_info->runtime_num_burst_capture--; + + msm_isp_get_done_buf(vfe_dev, stream_info, + pingpong_status, &done_buf); + if (stream_info->stream_type == CONTINUOUS_STREAM || + stream_info->runtime_num_burst_capture > 1) { + rc = msm_isp_cfg_ping_pong_address(vfe_dev, + stream_info, pingpong_status, + pingpong_bit); + } + stream_info->frame_id = frame_id; + ISP_DBG("%s: stream id:%d frame id:%d\n", + __func__, stream_info->stream_id, + stream_info->frame_id); + if (done_buf && !rc) + msm_isp_process_done_buf(vfe_dev, + stream_info, done_buf, ts); + } + } +} +int msm_isp_user_buf_done(struct vfe_device *vfe_dev, + struct msm_isp32_event_data *buf_cmd) +{ + int rc = 0; + struct msm_isp32_event_data buf_event; + + memset(&buf_event, 0, sizeof(buf_event)); + buf_event.input_intf = buf_cmd->input_intf; + buf_event.frame_id = buf_cmd->frame_id; + buf_event.timestamp = buf_cmd->timestamp; + buf_event.u.buf_done.session_id = + buf_cmd->u.buf_done.session_id; + buf_event.u.buf_done.stream_id = + buf_cmd->u.buf_done.stream_id; + buf_event.u.buf_done.output_format = + buf_cmd->u.buf_done.output_format; + buf_event.u.buf_done.buf_idx = + buf_cmd->u.buf_done.buf_idx; + buf_event.u.buf_done.handle = + buf_cmd->u.buf_done.handle; + + vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr, + buf_event.u.buf_done.handle, + buf_event.u.buf_done.buf_idx, + &buf_event.timestamp, buf_event.frame_id, + buf_event.u.buf_done.output_format); + return rc; +} diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.h new file mode 100644 index 000000000000..5d89a8481602 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util_32.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MSM_ISP_AXI_UTIL_H__ +#define __MSM_ISP_AXI_UTIL_H__ + +#include "msm_isp_32.h" + +int msm_isp_axi_create_stream( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd); + +void msm_isp_axi_destroy_stream( + struct msm_vfe_axi_shared_data *axi_data, int stream_idx); + +int msm_isp_validate_axi_request( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd); + +void msm_isp_axi_reserve_wm( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info); + +void msm_isp_axi_reserve_comp_mask( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info); + +int msm_isp_axi_check_stream_state( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd); + +void msm_isp_calculate_framedrop( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe32_axi_stream_request_cmd *stream_cfg_cmd); +void msm_isp_reset_framedrop(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info); + +void msm_isp_start_avtimer(void); +int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg); +int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg); +int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg); +int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg); +void msm_isp_axi_cfg_update(struct vfe_device *vfe_dev); +int msm_isp_axi_halt(struct vfe_device *vfe_dev, + struct msm_vfe_axi_halt_cmd *halt_cmd); +int msm_isp_axi_reset(struct vfe_device *vfe_dev, + struct msm_vfe_axi_reset_cmd *reset_cmd); +int msm_isp_axi_restart(struct vfe_device *vfe_dev, + struct msm_vfe_axi_restart_cmd *restart_cmd); +int msm_isp_user_buf_done(struct vfe_device *vfe_dev, + struct msm_isp32_event_data *buf_cmd); +void msm_isp_axi_stream_update(struct vfe_device *vfe_dev, + uint8_t input_src); + +void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev, + uint8_t input_src); +void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type, + enum msm_vfe_input_src frame_src, struct msm_isp_timestamp *ts); +void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts); +void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info); +#endif /* __MSM_ISP_AXI_UTIL_H__ */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.c new file mode 100644 index 000000000000..8273298cdda6 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.c @@ -0,0 +1,709 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include "msm_isp_util_32.h" +#include "msm_isp_stats_util_32.h" + +static int msm_isp_stats_cfg_ping_pong_address(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info, uint32_t pingpong_status, + struct msm_isp_buffer **done_buf) +{ + int rc = -1; + struct msm_isp_buffer *buf; + uint32_t pingpong_bit = 0; + uint32_t bufq_handle = stream_info->bufq_handle; + uint32_t stats_pingpong_offset; + uint32_t stats_idx = STATS_IDX(stream_info->stream_handle); + + if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type || + stats_idx >= MSM_ISP_STATS_MAX) { + pr_err("%s Invalid stats index %d", __func__, stats_idx); + return -EINVAL; + } + + stats_pingpong_offset = + vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset[ + stats_idx]; + + pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1); + rc = vfe_dev->buf_mgr->ops->get_buf(vfe_dev->buf_mgr, + vfe_dev->pdev->id, bufq_handle, + MSM_ISP_INVALID_BUF_INDEX, &buf); + if (rc < 0) { + vfe_dev->error_info.stats_framedrop_count[stats_idx]++; + return rc; + } + + if (buf->num_planes != 1) { + pr_err("%s: Invalid buffer\n", __func__); + rc = -EINVAL; + goto buf_error; + } + + vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr( + vfe_dev, stream_info, + pingpong_status, buf->mapped_info[0].paddr + + stream_info->buffer_offset); + + if (stream_info->buf[pingpong_bit] && done_buf) + *done_buf = stream_info->buf[pingpong_bit]; + + stream_info->buf[pingpong_bit] = buf; + return 0; +buf_error: + vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr, + buf->bufq_handle, buf->buf_idx); + return rc; +} + +void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts) +{ + int i, j, rc; + struct msm_isp32_event_data buf_event; + struct msm_isp_stats_event *stats_event = &buf_event.u.stats; + struct msm_isp_buffer *done_buf; + struct msm_vfe_stats_stream *stream_info = NULL; + uint32_t pingpong_status; + uint32_t comp_stats_type_mask = 0, atomic_stats_mask = 0; + uint32_t stats_comp_mask = 0, stats_irq_mask = 0; + uint32_t num_stats_comp_mask = + vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask; + stats_comp_mask = vfe_dev->hw_info->vfe_ops.stats_ops. + get_comp_mask(irq_status0, irq_status1); + stats_irq_mask = vfe_dev->hw_info->vfe_ops.stats_ops. + get_wm_mask(irq_status0, irq_status1); + if (!(stats_comp_mask || stats_irq_mask)) + return; + ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0); + + /* + * If any of composite mask is set, clear irq bits from mask, + * they will be restored by comp mask + */ + if (stats_comp_mask) { + for (j = 0; j < num_stats_comp_mask; j++) { + stats_irq_mask &= ~atomic_read( + &vfe_dev->stats_data.stats_comp_mask[j]); + } + } + + for (j = 0; j < num_stats_comp_mask; j++) { + atomic_stats_mask = atomic_read( + &vfe_dev->stats_data.stats_comp_mask[j]); + if (!stats_comp_mask) { + stats_irq_mask &= ~atomic_stats_mask; + } else { + /* restore irq bits from composite mask */ + if (stats_comp_mask & (1 << j)) + stats_irq_mask |= atomic_stats_mask; + } + /* if no irq bits set from this composite mask continue*/ + if (!stats_irq_mask) + continue; + memset(&buf_event, 0, sizeof(struct msm_isp32_event_data)); + buf_event.timestamp = ts->event_time; + buf_event.frame_id = + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; + buf_event.input_intf = VFE_PIX_0; + pingpong_status = vfe_dev->hw_info-> + vfe_ops.stats_ops.get_pingpong_status(vfe_dev); + + for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; + i++) { + if (!(stats_irq_mask & (1 << i))) + continue; + + stats_irq_mask &= ~(1 << i); + stream_info = &vfe_dev->stats_data.stream_info[i]; + done_buf = NULL; + msm_isp_stats_cfg_ping_pong_address(vfe_dev, + stream_info, pingpong_status, &done_buf); + if (done_buf) { + rc = vfe_dev->buf_mgr->ops->buf_divert( + vfe_dev->buf_mgr, done_buf->bufq_handle, + done_buf->buf_idx, &ts->buf_time, + vfe_dev->axi_data. + src_info[VFE_PIX_0].frame_id); + if (rc != 0) + continue; + + stats_event->stats_buf_idxs + [stream_info->stats_type] = + done_buf->buf_idx; + if (!stream_info->composite_flag) { + stats_event->stats_mask = + 1 << stream_info->stats_type; + ISP_DBG("%s: stats frameid: 0x%x %d\n", + __func__, buf_event.frame_id, + stream_info->stats_type); + msm_isp_send_event(vfe_dev, + ISP_EVENT_STATS_NOTIFY + + stream_info->stats_type, + &buf_event); + } else { + comp_stats_type_mask |= + 1 << stream_info->stats_type; + } + } + } + + if (comp_stats_type_mask) { + ISP_DBG("%s: comp_stats frameid: 0x%x, 0x%x\n", + __func__, buf_event.frame_id, + comp_stats_type_mask); + stats_event->stats_mask = comp_stats_type_mask; + msm_isp_send_event(vfe_dev, + ISP_EVENT_COMP_STATS_NOTIFY, &buf_event); + comp_stats_type_mask = 0; + } + } +} + +int msm_isp_stats_create_stream(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream_request_cmd *stream_req_cmd) +{ + int rc = -1; + struct msm_vfe_stats_stream *stream_info = NULL; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + uint32_t stats_idx; + + if (!(vfe_dev->hw_info->stats_hw_info->stats_capability_mask & + (1 << stream_req_cmd->stats_type))) { + pr_err("%s: Stats type not supported\n", __func__); + return rc; + } + + stats_idx = vfe_dev->hw_info->vfe_ops.stats_ops. + get_stats_idx(stream_req_cmd->stats_type); + + if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { + pr_err("%s Invalid stats index %d", __func__, stats_idx); + return -EINVAL; + } + + stream_info = &stats_data->stream_info[stats_idx]; + if (stream_info->state != STATS_AVAILABLE) { + pr_err("%s: Stats already requested\n", __func__); + return rc; + } + + if (stream_req_cmd->framedrop_pattern >= MAX_SKIP) { + pr_err("%s: Invalid framedrop pattern\n", __func__); + return rc; + } + + if (stream_req_cmd->irq_subsample_pattern >= MAX_SKIP) { + pr_err("%s: Invalid irq subsample pattern\n", __func__); + return rc; + } + + stream_info->session_id = stream_req_cmd->session_id; + stream_info->stream_id = stream_req_cmd->stream_id; + stream_info->composite_flag = stream_req_cmd->composite_flag; + stream_info->stats_type = stream_req_cmd->stats_type; + stream_info->buffer_offset = stream_req_cmd->buffer_offset; + stream_info->framedrop_pattern = stream_req_cmd->framedrop_pattern; + stream_info->init_stats_frame_drop = stream_req_cmd->init_frame_drop; + stream_info->irq_subsample_pattern = + stream_req_cmd->irq_subsample_pattern; + stream_info->state = STATS_INACTIVE; + + if ((vfe_dev->stats_data.stream_handle_cnt << 8) == 0) + vfe_dev->stats_data.stream_handle_cnt++; + + stream_req_cmd->stream_handle = + (++vfe_dev->stats_data.stream_handle_cnt) << 8 | stats_idx; + + stream_info->stream_handle = stream_req_cmd->stream_handle; + return 0; +} + +int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = -1; + struct msm_vfe_stats_stream_request_cmd *stream_req_cmd = arg; + struct msm_vfe_stats_stream *stream_info = NULL; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + uint32_t framedrop_period; + uint32_t stats_idx; + + rc = msm_isp_stats_create_stream(vfe_dev, stream_req_cmd); + if (rc < 0) { + pr_err("%s: create stream failed\n", __func__); + return rc; + } + + stats_idx = STATS_IDX(stream_req_cmd->stream_handle); + + if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { + pr_err("%s Invalid stats index %d", __func__, stats_idx); + return -EINVAL; + } + + stream_info = &stats_data->stream_info[stats_idx]; + + framedrop_period = msm_isp_get_framedrop_period( + stream_req_cmd->framedrop_pattern); + + if (stream_req_cmd->framedrop_pattern == SKIP_ALL) + stream_info->framedrop_pattern = 0x0; + else + stream_info->framedrop_pattern = 0x1; + stream_info->framedrop_period = framedrop_period - 1; + + if (!stream_info->composite_flag) + vfe_dev->hw_info->vfe_ops.stats_ops. + cfg_wm_irq_mask(vfe_dev, stream_info); + + if (stream_info->init_stats_frame_drop == 0) + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg(vfe_dev, + stream_info); + + return rc; +} + +int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = -1; + struct msm_vfe_stats_stream_cfg_cmd stream_cfg_cmd; + struct msm_vfe_stats_stream_release_cmd *stream_release_cmd = arg; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + int stats_idx = STATS_IDX(stream_release_cmd->stream_handle); + struct msm_vfe_stats_stream *stream_info = NULL; + + if (stats_idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { + pr_err("%s Invalid stats index %d", __func__, stats_idx); + return -EINVAL; + } + + stream_info = &stats_data->stream_info[stats_idx]; + if (stream_info->state == STATS_AVAILABLE) { + pr_err("%s: stream already release\n", __func__); + return rc; + } else if (stream_info->state != STATS_INACTIVE) { + stream_cfg_cmd.enable = 0; + stream_cfg_cmd.num_streams = 1; + stream_cfg_cmd.stream_handle[0] = + stream_release_cmd->stream_handle; + rc = msm_isp_cfg_stats_stream(vfe_dev, &stream_cfg_cmd); + } + + if (!stream_info->composite_flag) + vfe_dev->hw_info->vfe_ops.stats_ops. + clear_wm_irq_mask(vfe_dev, stream_info); + + vfe_dev->hw_info->vfe_ops.stats_ops.clear_wm_reg(vfe_dev, stream_info); + memset(stream_info, 0, sizeof(struct msm_vfe_stats_stream)); + return 0; +} + +static int msm_isp_init_stats_ping_pong_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info) +{ + int rc = 0; + + stream_info->bufq_handle = + vfe_dev->buf_mgr->ops->get_bufq_handle( + vfe_dev->buf_mgr, stream_info->session_id, + stream_info->stream_id); + if (stream_info->bufq_handle == 0) { + pr_err("%s: no buf configured for stream: 0x%x\n", + __func__, stream_info->stream_handle); + return -EINVAL; + } + + rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev, + stream_info, VFE_PING_FLAG, NULL); + if (rc < 0) { + pr_err("%s: No free buffer for ping\n", __func__); + return rc; + } + rc = msm_isp_stats_cfg_ping_pong_address(vfe_dev, + stream_info, VFE_PONG_FLAG, NULL); + if (rc < 0) { + pr_err("%s: No free buffer for pong\n", __func__); + return rc; + } + return rc; +} + +static void msm_isp_deinit_stats_ping_pong_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info) +{ + int i; + struct msm_isp_buffer *buf; + + for (i = 0; i < 2; i++) { + buf = stream_info->buf[i]; + if (buf) + vfe_dev->buf_mgr->ops->put_buf(vfe_dev->buf_mgr, + buf->bufq_handle, buf->buf_idx); + } +} + +void msm_isp_update_stats_framedrop_reg(struct vfe_device *vfe_dev) +{ + int i; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + struct msm_vfe_stats_stream *stream_info = NULL; + + for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) { + stream_info = &stats_data->stream_info[i]; + if (stream_info->state != STATS_ACTIVE) + continue; + + if (stream_info->init_stats_frame_drop) { + stream_info->init_stats_frame_drop--; + if (stream_info->init_stats_frame_drop == 0) { + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg( + vfe_dev, stream_info); + } + } + } +} + +void msm_isp_stats_stream_update(struct vfe_device *vfe_dev) +{ + int i; + uint32_t stats_mask = 0, comp_stats_mask = 0; + uint32_t enable = 0; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + + for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) { + if (stats_data->stream_info[i].state == STATS_START_PENDING || + stats_data->stream_info[i].state == + STATS_STOP_PENDING) { + stats_mask |= i; + enable = stats_data->stream_info[i].state == + STATS_START_PENDING ? 1 : 0; + stats_data->stream_info[i].state = + stats_data->stream_info[i].state == + STATS_START_PENDING ? + STATS_STARTING : STATS_STOPPING; + vfe_dev->hw_info->vfe_ops.stats_ops.enable_module( + vfe_dev, BIT(i), enable); + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask( + vfe_dev, BIT(i), enable); + } else if (stats_data->stream_info[i].state == STATS_STARTING || + stats_data->stream_info[i].state == STATS_STOPPING) { + if (stats_data->stream_info[i].composite_flag) + comp_stats_mask |= i; + stats_data->stream_info[i].state = + stats_data->stream_info[i].state == + STATS_STARTING ? STATS_ACTIVE : STATS_INACTIVE; + } + } + atomic_sub(1, &stats_data->stats_update); + if (!atomic_read(&stats_data->stats_update)) + complete(&vfe_dev->stats_config_complete); +} + +static int msm_isp_stats_wait_for_cfg_done(struct vfe_device *vfe_dev) +{ + int rc; + + init_completion(&vfe_dev->stats_config_complete); + atomic_set(&vfe_dev->stats_data.stats_update, 2); + rc = wait_for_completion_timeout( + &vfe_dev->stats_config_complete, + msecs_to_jiffies(VFE_MAX_CFG_TIMEOUT)); + if (rc == 0) { + pr_err("%s: wait timeout\n", __func__); + rc = -1; + } else { + rc = 0; + } + return rc; +} + +static int msm_isp_stats_update_cgc_override(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd) +{ + int i; + uint32_t stats_mask = 0, idx; + + if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s invalid num_streams %d\n", __func__, + stream_cfg_cmd->num_streams); + return -EINVAL; + } + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); + + if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { + pr_err("%s Invalid stats index %d", __func__, idx); + return -EINVAL; + } + stats_mask |= 1 << idx; + } + + if (vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override) { + vfe_dev->hw_info->vfe_ops.stats_ops.update_cgc_override( + vfe_dev, stats_mask, stream_cfg_cmd->enable); + } + return 0; +} + +static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd) +{ + int i, rc = 0; + uint32_t stats_mask = 0, idx; + uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0}; + uint32_t num_stats_comp_mask = 0; + struct msm_vfe_stats_stream *stream_info; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + + if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s invalid num_streams %d\n", __func__, + stream_cfg_cmd->num_streams); + return -EINVAL; + } + + num_stats_comp_mask = + vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask; + rc = vfe_dev->hw_info->vfe_ops.stats_ops.check_streams( + stats_data->stream_info); + if (rc < 0) + return rc; + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); + + if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { + pr_err("%s Invalid stats index %d", __func__, idx); + return -EINVAL; + } + + stream_info = &stats_data->stream_info[idx]; + if (stream_info->stream_handle != + stream_cfg_cmd->stream_handle[i]) { + pr_err("%s: Invalid stream handle: 0x%x received\n", + __func__, stream_cfg_cmd->stream_handle[i]); + continue; + } + + if (stream_info->composite_flag > num_stats_comp_mask) { + pr_err("%s: comp grp %d exceed max %d\n", + __func__, stream_info->composite_flag, + num_stats_comp_mask); + return -EINVAL; + } + rc = msm_isp_init_stats_ping_pong_reg(vfe_dev, stream_info); + if (rc < 0) { + pr_err("%s: No buffer for stream%d type:%d stmID:0x%x\n", + __func__, idx, stream_info->stats_type, + stream_info->stream_id); + return rc; + } + + if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) + stream_info->state = STATS_START_PENDING; + else + stream_info->state = STATS_ACTIVE; + + stats_data->num_active_stream++; + stats_mask |= 1 << idx; + + if (stream_info->composite_flag > 0) + comp_stats_mask[stream_info->composite_flag-1] |= + 1 << idx; + + ISP_DBG("%s: stats_mask %x %x active streams %d\n", + __func__, comp_stats_mask[0], + comp_stats_mask[1], + stats_data->num_active_stream); + + } + + if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) { + rc = msm_isp_stats_wait_for_cfg_done(vfe_dev); + } else { + vfe_dev->hw_info->vfe_ops.stats_ops.enable_module( + vfe_dev, stats_mask, stream_cfg_cmd->enable); + for (i = 0; i < num_stats_comp_mask; i++) { + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask( + vfe_dev, comp_stats_mask[i], 1); + } + } + return rc; +} + +static int msm_isp_stop_stats_stream(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd) +{ + int i, rc = 0; + uint32_t stats_mask = 0, idx; + uint32_t comp_stats_mask[MAX_NUM_STATS_COMP_MASK] = {0}; + uint32_t num_stats_comp_mask = 0; + struct msm_vfe_stats_stream *stream_info; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + + num_stats_comp_mask = + vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask; + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + + idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); + + if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { + pr_err("%s Invalid stats index %d", __func__, idx); + return -EINVAL; + } + + stream_info = &stats_data->stream_info[idx]; + if (stream_info->stream_handle != + stream_cfg_cmd->stream_handle[i]) { + pr_err("%s: Invalid stream handle: 0x%x received\n", + __func__, stream_cfg_cmd->stream_handle[i]); + continue; + } + + if (stream_info->composite_flag > num_stats_comp_mask) { + pr_err("%s: comp grp %d exceed max %d\n", + __func__, stream_info->composite_flag, + num_stats_comp_mask); + return -EINVAL; + } + + if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) + stream_info->state = STATS_STOP_PENDING; + else + stream_info->state = STATS_INACTIVE; + + stats_data->num_active_stream--; + stats_mask |= 1 << idx; + + if (stream_info->composite_flag > 0) + comp_stats_mask[stream_info->composite_flag-1] |= + 1 << idx; + + ISP_DBG("%s: stats_mask %x %x active streams %d\n", + __func__, comp_stats_mask[0], + comp_stats_mask[1], + stats_data->num_active_stream); + } + + if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) { + rc = msm_isp_stats_wait_for_cfg_done(vfe_dev); + } else { + vfe_dev->hw_info->vfe_ops.stats_ops.enable_module( + vfe_dev, stats_mask, stream_cfg_cmd->enable); + for (i = 0; i < num_stats_comp_mask; i++) { + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask( + vfe_dev, comp_stats_mask[i], 0); + } + } + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); + + if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { + pr_err("%s Invalid stats index %d", __func__, idx); + return -EINVAL; + } + + stream_info = &stats_data->stream_info[idx]; + msm_isp_deinit_stats_ping_pong_reg(vfe_dev, stream_info); + } + return rc; +} + +int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0; + struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd = arg; + + if (vfe_dev->stats_data.num_active_stream == 0) + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev); + + if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s invalid num_streams %d\n", __func__, + stream_cfg_cmd->num_streams); + return -EINVAL; + } + + if (stream_cfg_cmd->enable) { + msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd); + + rc = msm_isp_start_stats_stream(vfe_dev, stream_cfg_cmd); + } else { + rc = msm_isp_stop_stats_stream(vfe_dev, stream_cfg_cmd); + + msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd); + } + + return rc; +} + +int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0, i; + struct msm_vfe_stats_stream *stream_info; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + struct msm_vfe_axi_stream_update_cmd *update_cmd = arg; + struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL; + + /*validate request*/ + for (i = 0; i < update_cmd->num_streams; i++) { + update_info = &update_cmd->update_info[i]; + /*check array reference bounds*/ + if (STATS_IDX(update_info->stream_handle) + > vfe_dev->hw_info->stats_hw_info->num_stats_type) { + pr_err("%s: stats idx %d out of bound!", __func__, + STATS_IDX(update_info->stream_handle)); + return -EINVAL; + } + } + + for (i = 0; i < update_cmd->num_streams; i++) { + update_info = &update_cmd->update_info[i]; + stream_info = &stats_data->stream_info[ + STATS_IDX(update_info->stream_handle)]; + if (stream_info->stream_handle != + update_info->stream_handle) { + pr_err("%s: stats stream handle %x %x mismatch!\n", + __func__, stream_info->stream_handle, + update_info->stream_handle); + continue; + } + + switch (update_cmd->update_type) { + case UPDATE_STREAM_STATS_FRAMEDROP_PATTERN: { + uint32_t framedrop_period = + msm_isp_get_framedrop_period( + update_info->skip_pattern); + if (update_info->skip_pattern == SKIP_ALL) + stream_info->framedrop_pattern = 0x0; + else + stream_info->framedrop_pattern = 0x1; + stream_info->framedrop_period = framedrop_period - 1; + if (stream_info->init_stats_frame_drop == 0) + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_wm_reg( + vfe_dev, stream_info); + break; + } + + default: + pr_err("%s: Invalid update type\n", __func__); + return -EINVAL; + } + } + return rc; +} diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.h new file mode 100644 index 000000000000..11c0c28bcbe9 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util_32.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MSM_ISP_STATS_UTIL_H__ +#define __MSM_ISP_STATS_UTIL_H__ + +#include "msm_isp_32.h" +#define STATS_IDX(idx) (idx & 0xFF) + +void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct msm_isp_timestamp *ts); +int msm_isp_stats_create_stream(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream_request_cmd *stream_req_cmd); +void msm_isp_stats_stream_update(struct vfe_device *vfe_dev); +int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg); +int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg); +int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg); +int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg); +void msm_isp_update_stats_framedrop_reg(struct vfe_device *vfe_dev); +#endif /* __MSM_ISP_STATS_UTIL_H__ */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.c new file mode 100644 index 000000000000..f6ca41cd9211 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.c @@ -0,0 +1,1939 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include + +#include "msm.h" +#include "msm_isp_util_32.h" +#include "msm_isp_axi_util_32.h" +#include "msm_isp_stats_util_32.h" +#include "msm_camera_io_util.h" +#include "cam_smmu_api.h" + +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif + +#define MAX_ISP_V4l2_EVENTS 100 +#define MAX_ISP_REG_LIST 100 +static DEFINE_MUTEX(bandwidth_mgr_mutex); +static struct msm_isp_bandwidth_mgr isp_bandwidth_mgr; + +static uint64_t msm_isp_cpp_clk_rate; + +#define VFE40_8974V2_VERSION 0x1001001A +static struct msm_bus_vectors msm_isp_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors msm_isp_ping_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = MSM_ISP_MIN_AB, + .ib = MSM_ISP_MIN_IB, + }, +}; + +static struct msm_bus_vectors msm_isp_pong_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = MSM_ISP_MIN_AB, + .ib = MSM_ISP_MIN_IB, + }, +}; + +static struct msm_bus_paths msm_isp_bus_client_config[] = { + { + ARRAY_SIZE(msm_isp_init_vectors), + msm_isp_init_vectors, + }, + { + ARRAY_SIZE(msm_isp_ping_vectors), + msm_isp_ping_vectors, + }, + { + ARRAY_SIZE(msm_isp_pong_vectors), + msm_isp_pong_vectors, + }, +}; + +static struct msm_bus_scale_pdata msm_isp_bus_client_pdata = { + msm_isp_bus_client_config, + NULL, + ARRAY_SIZE(msm_isp_bus_client_config), + .name = "msm_camera_isp", + 0, +}; + + +void msm_camera_io_dump_2(void __iomem *addr, int size) +{ + char line_str[128], *p_str; + int i; + u32 __iomem *p = (u32 __iomem *) addr; + u32 data; + + pr_err("%s: %pK %d\n", __func__, addr, size); + line_str[0] = '\0'; + p_str = line_str; + for (i = 0; i < size/4; i++) { + if (i % 4 == 0) { +#ifdef CONFIG_COMPAT + snprintf(p_str, 20, "%016lx: ", (unsigned long) p); + p_str += 18; +#else + snprintf(p_str, 12, "%08lx: ", (unsigned long) p); + p_str += 10; +#endif + } + data = readl_relaxed(p++); + snprintf(p_str, 12, "%08x ", data); + p_str += 9; + if ((i + 1) % 4 == 0) { + pr_err("%s\n", line_str); + line_str[0] = '\0'; + p_str = line_str; + } + } + if (line_str[0] != '\0') + pr_err("%s\n", line_str); +} + +void msm_isp_print_fourcc_error(const char *origin, uint32_t fourcc_format) +{ + int i; + char text[5]; + + text[4] = '\0'; + for (i = 0; i < 4; i++) { + text[i] = (char)(((fourcc_format) >> (i * 8)) & 0xFF); + if ((text[i] < '0') || (text[i] > 'z')) { + pr_err("%s: Invalid output format %d (unprintable)\n", + origin, fourcc_format); + return; + } + } + pr_err("%s: Invalid output format %s\n", + origin, text); +} + +int msm_isp_init_bandwidth_mgr(enum msm_isp_hw_client client) +{ + int rc = 0; + + mutex_lock(&bandwidth_mgr_mutex); + isp_bandwidth_mgr.client_info[client].active = 1; + if (isp_bandwidth_mgr.use_count++) { + mutex_unlock(&bandwidth_mgr_mutex); + return rc; + } + isp_bandwidth_mgr.bus_client = + msm_bus_scale_register_client(&msm_isp_bus_client_pdata); + if (!isp_bandwidth_mgr.bus_client) { + pr_err("%s: client register failed\n", __func__); + mutex_unlock(&bandwidth_mgr_mutex); + return -EINVAL; + } + + isp_bandwidth_mgr.bus_vector_active_idx = 1; + msm_bus_scale_client_update_request( + isp_bandwidth_mgr.bus_client, + isp_bandwidth_mgr.bus_vector_active_idx); + + mutex_unlock(&bandwidth_mgr_mutex); + return 0; +} + +int msm_isp_update_bandwidth(enum msm_isp_hw_client client, + uint64_t ab, uint64_t ib) +{ + int i; + struct msm_bus_paths *path; + + mutex_lock(&bandwidth_mgr_mutex); + if (!isp_bandwidth_mgr.use_count || + !isp_bandwidth_mgr.bus_client) { + pr_err("%s:error bandwidth manager inactive use_cnt:%d bus_clnt:%d\n", + __func__, isp_bandwidth_mgr.use_count, + isp_bandwidth_mgr.bus_client); + return -EINVAL; + } + + isp_bandwidth_mgr.client_info[client].ab = ab; + isp_bandwidth_mgr.client_info[client].ib = ib; + ALT_VECTOR_IDX(isp_bandwidth_mgr.bus_vector_active_idx); + path = + &(msm_isp_bus_client_pdata.usecase[ + isp_bandwidth_mgr.bus_vector_active_idx]); + path->vectors[0].ab = 0; + path->vectors[0].ib = 0; + for (i = 0; i < MAX_ISP_CLIENT; i++) { + if (isp_bandwidth_mgr.client_info[i].active) { + path->vectors[0].ab += + isp_bandwidth_mgr.client_info[i].ab; + path->vectors[0].ib += + isp_bandwidth_mgr.client_info[i].ib; + } + } + ISP_DBG("%s: Total AB = %llu IB = %llu\n", __func__, + path->vectors[0].ab, path->vectors[0].ib); + msm_bus_scale_client_update_request(isp_bandwidth_mgr.bus_client, + isp_bandwidth_mgr.bus_vector_active_idx); + /* Insert into circular buffer */ + msm_isp_update_req_history(isp_bandwidth_mgr.bus_client, + path->vectors[0].ab, + path->vectors[0].ib, + isp_bandwidth_mgr.client_info, + sched_clock()); + mutex_unlock(&bandwidth_mgr_mutex); + return 0; +} + +void msm_isp_deinit_bandwidth_mgr(enum msm_isp_hw_client client) +{ + if (client >= MAX_ISP_CLIENT) { + pr_err("invalid Client id %d", client); + return; + } + mutex_lock(&bandwidth_mgr_mutex); + memset(&isp_bandwidth_mgr.client_info[client], 0, + sizeof(struct msm_isp_bandwidth_info)); + if (--isp_bandwidth_mgr.use_count) { + mutex_unlock(&bandwidth_mgr_mutex); + return; + } + + if (!isp_bandwidth_mgr.bus_client) { + pr_err("%s:%d error: bus client invalid\n", __func__, __LINE__); + mutex_unlock(&bandwidth_mgr_mutex); + return; + } + + msm_bus_scale_client_update_request( + isp_bandwidth_mgr.bus_client, 0); + msm_bus_scale_unregister_client(isp_bandwidth_mgr.bus_client); + isp_bandwidth_mgr.bus_client = 0; + mutex_unlock(&bandwidth_mgr_mutex); +} + +void msm_isp_util_get_bandwidth_stats(struct vfe_device *vfe_dev, + struct msm_isp_statistics *stats) +{ + stats->isp_vfe0_active = isp_bandwidth_mgr.client_info[ISP_VFE0].active; + stats->isp_vfe0_ab = isp_bandwidth_mgr.client_info[ISP_VFE0].ab; + stats->isp_vfe0_ib = isp_bandwidth_mgr.client_info[ISP_VFE0].ib; + + stats->isp_vfe1_active = isp_bandwidth_mgr.client_info[ISP_VFE1].active; + stats->isp_vfe1_ab = isp_bandwidth_mgr.client_info[ISP_VFE1].ab; + stats->isp_vfe1_ib = isp_bandwidth_mgr.client_info[ISP_VFE1].ib; + + stats->isp_cpp_active = isp_bandwidth_mgr.client_info[ISP_CPP].active; + stats->isp_cpp_ab = isp_bandwidth_mgr.client_info[ISP_CPP].ab; + stats->isp_cpp_ib = isp_bandwidth_mgr.client_info[ISP_CPP].ib; + stats->last_overflow_ab = vfe_dev->msm_isp_last_overflow_ab; + stats->last_overflow_ib = vfe_dev->msm_isp_last_overflow_ib; + stats->vfe_clk_rate = vfe_dev->msm_isp_vfe_clk_rate; + stats->cpp_clk_rate = msm_isp_cpp_clk_rate; +} + +void msm_isp_util_update_last_overflow_ab_ib(struct vfe_device *vfe_dev) +{ + struct msm_bus_paths *path; + path = &(msm_isp_bus_client_pdata.usecase[ + isp_bandwidth_mgr.bus_vector_active_idx]); + vfe_dev->msm_isp_last_overflow_ab = path->vectors[0].ab; + vfe_dev->msm_isp_last_overflow_ib = path->vectors[0].ib; +} + +void msm_isp_util_update_clk_rate(long clock_rate) +{ + msm_isp_cpp_clk_rate = clock_rate; +} + +uint32_t msm_isp_get_framedrop_period( + enum msm_vfe_frame_skip_pattern frame_skip_pattern) +{ + switch (frame_skip_pattern) { + case NO_SKIP: + case EVERY_2FRAME: + case EVERY_3FRAME: + case EVERY_4FRAME: + case EVERY_5FRAME: + case EVERY_6FRAME: + case EVERY_7FRAME: + case EVERY_8FRAME: + return frame_skip_pattern + 1; + case EVERY_16FRAME: + return 16; + case EVERY_32FRAME: + return 32; + case SKIP_ALL: + return 1; + default: + return 1; + } + return 1; +} + +int msm_isp_get_clk_info(struct vfe_device *vfe_dev, + struct platform_device *pdev, struct msm_cam_clk_info *vfe_clk_info) +{ + int i, count, rc; + uint32_t rates[VFE_CLK_INFO_MAX]; + + struct device_node *of_node; + + of_node = pdev->dev.of_node; + + count = of_property_count_strings(of_node, "clock-names"); + + ISP_DBG("count = %d\n", count); + if (count <= 0) { + pr_err("no clocks found in device tree, count=%d", count); + return 0; + } + + if (count > VFE_CLK_INFO_MAX) { + pr_err("invalid count=%d, max is %d\n", count, + VFE_CLK_INFO_MAX); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, "clock-names", + i, &(vfe_clk_info[i].clk_name)); + ISP_DBG("clock-names[%d] = %s\n", i, vfe_clk_info[i].clk_name); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return rc; + } + } + rc = of_property_read_u32_array(of_node, "qcom,clock-rates", + rates, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return rc; + } + for (i = 0; i < count; i++) { + vfe_clk_info[i].clk_rate = + (rates[i] == 0) ? (long)-1 : rates[i]; + ISP_DBG("clk_rate[%d] = %ld\n", i, vfe_clk_info[i].clk_rate); + } + vfe_dev->num_clk = count; + return 0; +} + +void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp, + struct vfe_device *vfe_dev) +{ + struct timespec ts; + + do_gettimeofday(&(time_stamp->event_time)); + if (vfe_dev->vt_enable) { + msm_isp_get_avtimer_ts(time_stamp); + time_stamp->buf_time.tv_sec = time_stamp->vt_time.tv_sec; + time_stamp->buf_time.tv_usec = time_stamp->vt_time.tv_usec; + } else { + get_monotonic_boottime(&ts); + time_stamp->buf_time.tv_sec = ts.tv_sec; + time_stamp->buf_time.tv_usec = ts.tv_nsec/1000; + } +} + +int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd); + int rc = 0; + + rc = v4l2_event_subscribe(fh, sub, MAX_ISP_V4l2_EVENTS, NULL); + if (rc == 0) { + if (sub->type == V4L2_EVENT_ALL) { + int i; + + vfe_dev->axi_data.event_mask = 0; + for (i = 0; i < ISP_EVENT_MAX; i++) + vfe_dev->axi_data.event_mask |= (1 << i); + } else { + int event_idx = sub->type - ISP_EVENT_BASE; + + vfe_dev->axi_data.event_mask |= (1 << event_idx); + } + } + return rc; +} + +int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd); + int rc = 0; + + rc = v4l2_event_unsubscribe(fh, sub); + if (sub->type == V4L2_EVENT_ALL) { + vfe_dev->axi_data.event_mask = 0; + } else { + int event_idx = sub->type - ISP_EVENT_BASE; + + vfe_dev->axi_data.event_mask &= ~(1 << event_idx); + } + return rc; +} + +static int msm_isp_get_max_clk_rate(struct vfe_device *vfe_dev, long *rate) +{ + int clk_idx = 0; + unsigned long max_value = ~0; + long round_rate = 0; + + if (!vfe_dev || !rate) { + pr_err("%s:%d failed: vfe_dev %pK rate %pK\n", + __func__, __LINE__, vfe_dev, rate); + return -EINVAL; + } + + *rate = 0; + if (!vfe_dev->hw_info) { + pr_err("%s:%d failed: vfe_dev->hw_info %pK\n", __func__, + __LINE__, vfe_dev->hw_info); + return -EINVAL; + } + + clk_idx = vfe_dev->hw_info->vfe_clk_idx; + if (clk_idx >= vfe_dev->num_clk) { + pr_err("%s:%d failed: clk_idx %d max array size %d\n", + __func__, __LINE__, clk_idx, + vfe_dev->num_clk); + return -EINVAL; + } + + round_rate = clk_round_rate(vfe_dev->vfe_clk[clk_idx], max_value); + if (round_rate < 0) { + pr_err("%s: Invalid vfe clock rate\n", __func__); + return -EINVAL; + } + + *rate = round_rate; + return 0; +} + +static int msm_isp_set_clk_rate(struct vfe_device *vfe_dev, long *rate) +{ + int rc = 0; + int clk_idx = vfe_dev->hw_info->vfe_clk_idx; + long round_rate = + clk_round_rate(vfe_dev->vfe_clk[clk_idx], *rate); + if (round_rate < 0) { + pr_err("%s: Invalid vfe clock rate\n", __func__); + return round_rate; + } + + rc = clk_set_rate(vfe_dev->vfe_clk[clk_idx], round_rate); + if (rc < 0) { + pr_err("%s: Vfe set rate error\n", __func__); + return rc; + } + *rate = round_rate; + vfe_dev->msm_isp_vfe_clk_rate = round_rate; + return 0; +} + +void msm_isp_fetch_engine_done_notify(struct vfe_device *vfe_dev, + struct msm_vfe_fetch_engine_info *fetch_engine_info) +{ + struct msm_isp32_event_data fe_rd_done_event; + + if (!fetch_engine_info->is_busy) + return; + memset(&fe_rd_done_event, 0, sizeof(struct msm_isp32_event_data)); + fe_rd_done_event.frame_id = + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; + fe_rd_done_event.u.buf_done.session_id = fetch_engine_info->session_id; + fe_rd_done_event.u.buf_done.stream_id = fetch_engine_info->stream_id; + fe_rd_done_event.u.buf_done.handle = fetch_engine_info->bufq_handle; + fe_rd_done_event.u.buf_done.buf_idx = fetch_engine_info->buf_idx; + ISP_DBG("%s:VFE%d ISP_EVENT_FE_READ_DONE buf_idx %d\n", + __func__, vfe_dev->pdev->id, fetch_engine_info->buf_idx); + fetch_engine_info->is_busy = 0; + msm_isp_send_event(vfe_dev, ISP_EVENT_FE_READ_DONE, &fe_rd_done_event); +} + +static int msm_isp_cfg_pix(struct vfe_device *vfe_dev, + struct msm_vfe_input_cfg *input_cfg) +{ + int rc = 0; + + if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) { + pr_err("%s: src %d path is active\n", __func__, VFE_PIX_0); + return -EINVAL; + } + + vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock = + input_cfg->input_pix_clk; + vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux = + input_cfg->d.pix_cfg.input_mux; + vfe_dev->axi_data.src_info[VFE_PIX_0].input_format = + input_cfg->d.pix_cfg.input_format; + + rc = msm_isp_set_clk_rate(vfe_dev, + &vfe_dev->axi_data.src_info[VFE_PIX_0].pixel_clock); + if (rc < 0) { + pr_err("%s: clock set rate failed\n", __func__); + return rc; + } + + ISP_DBG("%s: input mux is %d CAMIF %d io_format 0x%x\n", __func__, + input_cfg->d.pix_cfg.input_mux, CAMIF, + input_cfg->d.pix_cfg.input_format); + + if (input_cfg->d.pix_cfg.input_mux == CAMIF) { + vfe_dev->axi_data.src_info[VFE_PIX_0].width = + input_cfg->d.pix_cfg.camif_cfg.pixels_per_line; + } else if (input_cfg->d.pix_cfg.input_mux == EXTERNAL_READ) { + vfe_dev->axi_data.src_info[VFE_PIX_0].width = + input_cfg->d.pix_cfg.fetch_engine_cfg.buf_stride; + } + vfe_dev->hw_info->vfe_ops.core_ops.cfg_input_mux( + vfe_dev, &input_cfg->d.pix_cfg); + return rc; +} + +static int msm_isp_cfg_rdi(struct vfe_device *vfe_dev, + struct msm_vfe_input_cfg *input_cfg) +{ + int rc = 0; + + if (vfe_dev->axi_data.src_info[input_cfg->input_src].active) { + pr_err("%s: RAW%d path is active\n", __func__, + input_cfg->input_src - VFE_RAW_0); + return -EINVAL; + } + + vfe_dev->axi_data.src_info[input_cfg->input_src].pixel_clock = + input_cfg->input_pix_clk; + vfe_dev->hw_info->vfe_ops.core_ops.cfg_rdi_reg( + vfe_dev, &input_cfg->d.rdi_cfg, input_cfg->input_src); + return rc; +} + +int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0; + struct msm_vfe_input_cfg *input_cfg = arg; + + switch (input_cfg->input_src) { + case VFE_PIX_0: + rc = msm_isp_cfg_pix(vfe_dev, input_cfg); + break; + case VFE_RAW_0: + case VFE_RAW_1: + case VFE_RAW_2: + rc = msm_isp_cfg_rdi(vfe_dev, input_cfg); + break; + default: + pr_err("%s: Invalid input source\n", __func__); + rc = -EINVAL; + } + return rc; +} + +static int msm_isp_proc_cmd_list_unlocked(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0; + uint32_t count = 0; + struct msm_vfe_cfg_cmd_list *proc_cmd = + (struct msm_vfe_cfg_cmd_list *)arg; + struct msm_vfe_cfg_cmd_list cmd, cmd_next; + struct msm_vfe_cfg_cmd2 cfg_cmd; + + if (!vfe_dev || !arg) { + pr_err("%s:%d failed: vfe_dev %pK arg %pK", __func__, __LINE__, + vfe_dev, arg); + return -EINVAL; + } + + rc = msm_isp_proc_cmd(vfe_dev, &proc_cmd->cfg_cmd); + if (rc < 0) + pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc); + + cmd = *proc_cmd; + + while (cmd.next) { + if (cmd.next_size != sizeof(struct msm_vfe_cfg_cmd_list)) { + pr_err("%s:%d failed: next size %u != expected %zu\n", + __func__, __LINE__, cmd.next_size, + sizeof(struct msm_vfe_cfg_cmd_list)); + break; + } + if (++count >= MAX_ISP_REG_LIST) { + pr_err("%s:%d Error exceeding the max register count:%u\n", + __func__, __LINE__, count); + rc = -EFAULT; + break; + } + if (copy_from_user(&cmd_next, (void __user *)cmd.next, + sizeof(struct msm_vfe_cfg_cmd_list))) { + rc = -EFAULT; + continue; + } + + cfg_cmd = cmd_next.cfg_cmd; + + rc = msm_isp_proc_cmd(vfe_dev, &cfg_cmd); + if (rc < 0) + pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc); + + cmd = cmd_next; + } + return rc; +} + +#ifdef CONFIG_COMPAT +struct msm_vfe_cfg_cmd2_32 { + uint16_t num_cfg; + uint16_t cmd_len; + compat_caddr_t cfg_data; + compat_caddr_t cfg_cmd; +}; + +struct msm_vfe_cfg_cmd_list_32 { + struct msm_vfe_cfg_cmd2_32 cfg_cmd; + compat_caddr_t next; + uint32_t next_size; +}; + +#define VIDIOC_MSM_VFE_REG_CFG_COMPAT \ + _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_vfe_cfg_cmd2_32) +#define VIDIOC_MSM_VFE_REG_LIST_CFG_COMPAT \ + _IOWR('V', BASE_VIDIOC_PRIVATE+14, struct msm_vfe_cfg_cmd_list_32) + +static void msm_isp_compat_to_proc_cmd(struct msm_vfe_cfg_cmd2 *proc_cmd, + struct msm_vfe_cfg_cmd2_32 *proc_cmd_ptr32) +{ + proc_cmd->num_cfg = proc_cmd_ptr32->num_cfg; + proc_cmd->cmd_len = proc_cmd_ptr32->cmd_len; + proc_cmd->cfg_data = compat_ptr(proc_cmd_ptr32->cfg_data); + proc_cmd->cfg_cmd = compat_ptr(proc_cmd_ptr32->cfg_cmd); +} + +static int msm_isp_proc_cmd_list_compat(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0; + uint32_t count = 0; + struct msm_vfe_cfg_cmd_list_32 *proc_cmd = + (struct msm_vfe_cfg_cmd_list_32 *)arg; + struct msm_vfe_cfg_cmd_list_32 cmd, cmd_next; + struct msm_vfe_cfg_cmd2 current_cmd; + + if (!vfe_dev || !arg) { + pr_err("%s:%d failed: vfe_dev %pK arg %pK", __func__, __LINE__, + vfe_dev, arg); + return -EINVAL; + } + msm_isp_compat_to_proc_cmd(¤t_cmd, &proc_cmd->cfg_cmd); + rc = msm_isp_proc_cmd(vfe_dev, ¤t_cmd); + if (rc < 0) + pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc); + + cmd = *proc_cmd; + + while (compat_ptr(cmd.next) != NULL) { + if (cmd.next_size != sizeof(struct msm_vfe_cfg_cmd_list_32)) { + pr_err("%s:%d failed: next size %u != expected %zu\n", + __func__, __LINE__, cmd.next_size, + sizeof(struct msm_vfe_cfg_cmd_list)); + break; + } + if (++count >= MAX_ISP_REG_LIST) { + pr_err("%s:%d Error exceeding the max register count:%u\n", + __func__, __LINE__, count); + rc = -EFAULT; + break; + } + if (copy_from_user(&cmd_next, compat_ptr(cmd.next), + sizeof(struct msm_vfe_cfg_cmd_list_32))) { + rc = -EFAULT; + continue; + } + + msm_isp_compat_to_proc_cmd(¤t_cmd, &cmd_next.cfg_cmd); + rc = msm_isp_proc_cmd(vfe_dev, ¤t_cmd); + if (rc < 0) + pr_err("%s:%d failed: rc %d", __func__, __LINE__, rc); + + cmd = cmd_next; + } + return rc; +} + +static int msm_isp_proc_cmd_list(struct vfe_device *vfe_dev, void *arg) +{ + if (is_compat_task()) + return msm_isp_proc_cmd_list_compat(vfe_dev, arg); + else + return msm_isp_proc_cmd_list_unlocked(vfe_dev, arg); +} +#else /* CONFIG_COMPAT */ +static int msm_isp_proc_cmd_list(struct vfe_device *vfe_dev, void *arg) +{ + return msm_isp_proc_cmd_list_unlocked(vfe_dev, arg); +} +#endif /* CONFIG_COMPAT */ + + +static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + long rc = 0; + struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd); + + if (!vfe_dev || !vfe_dev->vfe_base) { + pr_err("%s:%d failed: invalid params %pK\n", + __func__, __LINE__, vfe_dev); + if (vfe_dev) + pr_err("%s:%d failed %pK\n", __func__, + __LINE__, vfe_dev->vfe_base); + return -EINVAL; + } + + /* use real time mutex for hard real-time ioctls such as + * buffer operations and register updates. + * Use core mutex for other ioctls that could take + * longer time to complete such as start/stop ISP streams + * which blocks until the hardware start/stop streaming + */ + ISP_DBG("%s cmd: %d\n", __func__, _IOC_TYPE(cmd)); + switch (cmd) { + case VIDIOC_MSM_VFE_REG_CFG: { + mutex_lock(&vfe_dev->realtime_mutex); + rc = msm_isp_proc_cmd(vfe_dev, arg); + mutex_unlock(&vfe_dev->realtime_mutex); + break; + } + case VIDIOC_MSM_VFE_REG_LIST_CFG: { + mutex_lock(&vfe_dev->realtime_mutex); + rc = msm_isp_proc_cmd_list(vfe_dev, arg); + mutex_unlock(&vfe_dev->realtime_mutex); + break; + } + case VIDIOC_MSM_ISP_REQUEST_BUF: + case VIDIOC_MSM_ISP_ENQUEUE_BUF: + case VIDIOC_MSM_ISP_RELEASE_BUF: { + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_proc_buf_cmd(vfe_dev->buf_mgr, cmd, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + } + case VIDIOC_MSM_ISP32_REQUEST_STREAM: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_request_axi_stream(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_RELEASE_STREAM: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_release_axi_stream(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_CFG_STREAM: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_cfg_axi_stream(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_AXI_HALT: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_axi_halt(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_AXI_RESET: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_axi_reset(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_AXI_RESTART: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_axi_restart(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_INPUT_CFG: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_cfg_input(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_FETCH_ENG_START: + mutex_lock(&vfe_dev->core_mutex); + rc = vfe_dev->hw_info->vfe_ops.core_ops. + start_fetch_eng(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_REG_UPDATE_CMD: + if (arg) { + enum msm_vfe_input_src frame_src = + *((enum msm_vfe_input_src *)arg); + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, (1 << frame_src)); + vfe_dev->axi_data.src_info[frame_src].last_updt_frm_id = + vfe_dev->axi_data.src_info[frame_src].frame_id; + } + break; + case VIDIOC_MSM_ISP_SET_SRC_STATE: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_set_src_state(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_REQUEST_STATS_STREAM: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_request_stats_stream(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_RELEASE_STATS_STREAM: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_release_stats_stream(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_CFG_STATS_STREAM: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_cfg_stats_stream(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_UPDATE_STATS_STREAM: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_update_stats_stream(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_UPDATE_STREAM: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_update_axi_stream(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case VIDIOC_MSM_ISP_SMMU_ATTACH: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_smmu_attach(vfe_dev->buf_mgr, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case MSM_SD_NOTIFY_FREEZE: + vfe_dev->isp_sof_debug = 0; + break; + case VIDIOC_MSM_ISP_BUF_DONE: + mutex_lock(&vfe_dev->core_mutex); + rc = msm_isp_user_buf_done(vfe_dev, arg); + mutex_unlock(&vfe_dev->core_mutex); + break; + case MSM_SD_SHUTDOWN: + while (vfe_dev->vfe_open_cnt != 0) + msm_isp_close_node(sd, NULL); + break; + + default: + pr_err_ratelimited("%s: Invalid ISP command\n", __func__); + rc = -EINVAL; + } + return rc; +} + + +#ifdef CONFIG_COMPAT +static long msm_isp_ioctl_compat(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd); + long rc = 0; + + if (!vfe_dev || !vfe_dev->vfe_base) { + pr_err("%s:%d failed: invalid params %pK\n", + __func__, __LINE__, vfe_dev); + if (vfe_dev) + pr_err("%s:%d failed %pK\n", __func__, + __LINE__, vfe_dev->vfe_base); + return -EINVAL; + } + + switch (cmd) { + case VIDIOC_MSM_VFE_REG_CFG_COMPAT: { + struct msm_vfe_cfg_cmd2 proc_cmd; + + mutex_lock(&vfe_dev->realtime_mutex); + msm_isp_compat_to_proc_cmd(&proc_cmd, + (struct msm_vfe_cfg_cmd2_32 *) arg); + rc = msm_isp_proc_cmd(vfe_dev, &proc_cmd); + mutex_unlock(&vfe_dev->realtime_mutex); + break; + } + case VIDIOC_MSM_VFE_REG_LIST_CFG_COMPAT: { + mutex_lock(&vfe_dev->realtime_mutex); + rc = msm_isp_proc_cmd_list(vfe_dev, arg); + mutex_unlock(&vfe_dev->realtime_mutex); + break; + } + default: + return msm_isp_ioctl_unlocked(sd, cmd, arg); + } + + return rc; +} + +long msm_isp_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + return msm_isp_ioctl_compat(sd, cmd, arg); +} +#else /* CONFIG_COMPAT */ +long msm_isp_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + return msm_isp_ioctl_unlocked(sd, cmd, arg); +} +#endif /* CONFIG_COMPAT */ + +static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev, + struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd, + uint32_t *cfg_data, uint32_t cmd_len) +{ + if (!vfe_dev || !reg_cfg_cmd) { + pr_err("%s:%d failed: vfe_dev %pK reg_cfg_cmd %pK\n", __func__, + __LINE__, vfe_dev, reg_cfg_cmd); + return -EINVAL; + } + if ((reg_cfg_cmd->cmd_type != VFE_CFG_MASK) && + (!cfg_data || !cmd_len)) { + pr_err("%s:%d failed: cmd type %d cfg_data %pK cmd_len %d\n", + __func__, __LINE__, reg_cfg_cmd->cmd_type, cfg_data, + cmd_len); + return -EINVAL; + } + + /* Validate input parameters */ + switch (reg_cfg_cmd->cmd_type) { + case VFE_WRITE: + case VFE_READ: + case VFE_WRITE_MB: { + if ((reg_cfg_cmd->u.rw_info.reg_offset > + (UINT_MAX - reg_cfg_cmd->u.rw_info.len)) || + ((reg_cfg_cmd->u.rw_info.reg_offset + + reg_cfg_cmd->u.rw_info.len) > + resource_size(vfe_dev->vfe_mem)) || + (reg_cfg_cmd->u.rw_info.reg_offset & 0x3)) { + pr_err("%s:%d reg_offset %d len %d res %d\n", + __func__, __LINE__, + reg_cfg_cmd->u.rw_info.reg_offset, + reg_cfg_cmd->u.rw_info.len, + (uint32_t)resource_size(vfe_dev->vfe_mem)); + return -EINVAL; + } + + if ((reg_cfg_cmd->u.rw_info.cmd_data_offset > + (UINT_MAX - reg_cfg_cmd->u.rw_info.len)) || + ((reg_cfg_cmd->u.rw_info.cmd_data_offset + + reg_cfg_cmd->u.rw_info.len) > cmd_len)) { + pr_err("%s:%d cmd_data_offset %d len %d cmd_len %d\n", + __func__, __LINE__, + reg_cfg_cmd->u.rw_info.cmd_data_offset, + reg_cfg_cmd->u.rw_info.len, cmd_len); + return -EINVAL; + } + break; + } + + case VFE_WRITE_DMI_16BIT: + case VFE_WRITE_DMI_32BIT: + case VFE_WRITE_DMI_64BIT: + case VFE_READ_DMI_16BIT: + case VFE_READ_DMI_32BIT: + case VFE_READ_DMI_64BIT: { + if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT || + reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) { + if ((reg_cfg_cmd->u.dmi_info.hi_tbl_offset <= + reg_cfg_cmd->u.dmi_info.lo_tbl_offset) || + (reg_cfg_cmd->u.dmi_info.hi_tbl_offset - + reg_cfg_cmd->u.dmi_info.lo_tbl_offset != + (sizeof(uint32_t)))) { + pr_err("%s:%d hi %d lo %d\n", + __func__, __LINE__, + reg_cfg_cmd->u.dmi_info.hi_tbl_offset, + reg_cfg_cmd->u.dmi_info.hi_tbl_offset); + return -EINVAL; + } + if (reg_cfg_cmd->u.dmi_info.len <= sizeof(uint32_t)) { + pr_err("%s:%d len %d\n", + __func__, __LINE__, + reg_cfg_cmd->u.dmi_info.len); + return -EINVAL; + } + if (((UINT_MAX - + reg_cfg_cmd->u.dmi_info.hi_tbl_offset) < + (reg_cfg_cmd->u.dmi_info.len - + sizeof(uint32_t))) || + ((reg_cfg_cmd->u.dmi_info.hi_tbl_offset + + reg_cfg_cmd->u.dmi_info.len - + sizeof(uint32_t)) > cmd_len)) { + pr_err("%s:%d hi_tbl_offset %d len %d cmd %d\n", + __func__, __LINE__, + reg_cfg_cmd->u.dmi_info.hi_tbl_offset, + reg_cfg_cmd->u.dmi_info.len, cmd_len); + return -EINVAL; + } + } + if ((reg_cfg_cmd->u.dmi_info.lo_tbl_offset > + (UINT_MAX - reg_cfg_cmd->u.dmi_info.len)) || + ((reg_cfg_cmd->u.dmi_info.lo_tbl_offset + + reg_cfg_cmd->u.dmi_info.len) > cmd_len)) { + pr_err("%s:%d lo_tbl_offset %d len %d cmd_len %d\n", + __func__, __LINE__, + reg_cfg_cmd->u.dmi_info.lo_tbl_offset, + reg_cfg_cmd->u.dmi_info.len, cmd_len); + return -EINVAL; + } + break; + } + + default: + break; + } + + switch (reg_cfg_cmd->cmd_type) { + case VFE_WRITE: { + msm_camera_io_memcpy(vfe_dev->vfe_base + + reg_cfg_cmd->u.rw_info.reg_offset, + cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4, + reg_cfg_cmd->u.rw_info.len); + break; + } + case VFE_WRITE_MB: { + msm_camera_io_memcpy_mb(vfe_dev->vfe_base + + reg_cfg_cmd->u.rw_info.reg_offset, + cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4, + reg_cfg_cmd->u.rw_info.len); + break; + } + case VFE_CFG_MASK: { + uint32_t temp; + + if ((UINT_MAX - sizeof(temp) < + reg_cfg_cmd->u.mask_info.reg_offset) || + (resource_size(vfe_dev->vfe_mem) < + reg_cfg_cmd->u.mask_info.reg_offset + + sizeof(temp)) || + (reg_cfg_cmd->u.mask_info.reg_offset & 0x3)) { + pr_err("%s: VFE_CFG_MASK: Invalid length\n", __func__); + return -EINVAL; + } + temp = msm_camera_io_r(vfe_dev->vfe_base + + reg_cfg_cmd->u.mask_info.reg_offset); + + temp &= ~reg_cfg_cmd->u.mask_info.mask; + temp |= reg_cfg_cmd->u.mask_info.val; + msm_camera_io_w(temp, vfe_dev->vfe_base + + reg_cfg_cmd->u.mask_info.reg_offset); + break; + } + case VFE_WRITE_DMI_16BIT: + case VFE_WRITE_DMI_32BIT: + case VFE_WRITE_DMI_64BIT: { + int i; + uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL; + uint32_t hi_val, lo_val, lo_val1; + + if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT) { + hi_tbl_ptr = cfg_data + + reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4; + } + lo_tbl_ptr = cfg_data + + reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4; + if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_64BIT) + reg_cfg_cmd->u.dmi_info.len = + reg_cfg_cmd->u.dmi_info.len / 2; + for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) { + lo_val = *lo_tbl_ptr++; + if (reg_cfg_cmd->cmd_type == VFE_WRITE_DMI_16BIT) { + lo_val1 = lo_val & 0x0000FFFF; + lo_val = (lo_val & 0xFFFF0000)>>16; + msm_camera_io_w(lo_val1, vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset + 0x4); + } else if (reg_cfg_cmd->cmd_type == + VFE_WRITE_DMI_64BIT) { + lo_tbl_ptr++; + hi_val = *hi_tbl_ptr; + hi_tbl_ptr = hi_tbl_ptr + 2; + msm_camera_io_w(hi_val, vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset); + } + msm_camera_io_w(lo_val, vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset + 0x4); + } + break; + } + case VFE_READ_DMI_16BIT: + case VFE_READ_DMI_32BIT: + case VFE_READ_DMI_64BIT: { + int i; + uint32_t *hi_tbl_ptr = NULL, *lo_tbl_ptr = NULL; + uint32_t hi_val, lo_val, lo_val1; + + if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) { + hi_tbl_ptr = cfg_data + + reg_cfg_cmd->u.dmi_info.hi_tbl_offset/4; + } + + lo_tbl_ptr = cfg_data + + reg_cfg_cmd->u.dmi_info.lo_tbl_offset/4; + + if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) + reg_cfg_cmd->u.dmi_info.len = + reg_cfg_cmd->u.dmi_info.len / 2; + + for (i = 0; i < reg_cfg_cmd->u.dmi_info.len/4; i++) { + lo_val = msm_camera_io_r(vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset + 0x4); + + if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_16BIT) { + lo_val1 = msm_camera_io_r(vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset + 0x4); + lo_val |= lo_val1 << 16; + } + *lo_tbl_ptr++ = lo_val; + if (reg_cfg_cmd->cmd_type == VFE_READ_DMI_64BIT) { + hi_val = msm_camera_io_r(vfe_dev->vfe_base + + vfe_dev->hw_info->dmi_reg_offset); + *hi_tbl_ptr = hi_val; + hi_tbl_ptr += 2; + lo_tbl_ptr++; + } + } + break; + } + case VFE_HW_UPDATE_LOCK: { + uint32_t update_id = + vfe_dev->axi_data.src_info[VFE_PIX_0].last_updt_frm_id; + if (update_id) { + ISP_DBG("%s hw_update_lock fail cur_id %u,last_id %u\n", + __func__, + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id, + update_id); + return -EINVAL; + } + break; + } + case VFE_HW_UPDATE_UNLOCK: { + if (vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id + != *cfg_data) { + ISP_DBG("hw_updt over frm bound,strt_id %u end_id %d\n", + *cfg_data, + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); + } + vfe_dev->axi_data.src_info[VFE_PIX_0].last_updt_frm_id = + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; + break; + } + case VFE_READ: { + int i; + uint32_t *data_ptr = cfg_data + + reg_cfg_cmd->u.rw_info.cmd_data_offset/4; + for (i = 0; i < reg_cfg_cmd->u.rw_info.len/4; i++) { + if ((data_ptr < cfg_data) || + (UINT_MAX / sizeof(*data_ptr) < + (data_ptr - cfg_data)) || + (sizeof(*data_ptr) * (data_ptr - cfg_data) >= + cmd_len)) + return -EINVAL; + *data_ptr++ = msm_camera_io_r(vfe_dev->vfe_base + + reg_cfg_cmd->u.rw_info.reg_offset); + reg_cfg_cmd->u.rw_info.reg_offset += 4; + } + break; + } + case GET_MAX_CLK_RATE: { + int rc = 0; + unsigned long rate; + + if (cmd_len != sizeof(__u32)) { + pr_err("%s:%d failed: invalid cmd len %u exp %zu\n", + __func__, __LINE__, cmd_len, + sizeof(__u32)); + return -EINVAL; + } + rc = msm_isp_get_max_clk_rate(vfe_dev, &rate); + if (rc < 0) { + pr_err("%s:%d failed: rc %d\n", __func__, __LINE__, rc); + return -EINVAL; + } + + *(__u32 *)cfg_data = (__u32)rate; + + break; + } + case GET_ISP_ID: { + uint32_t *isp_id = NULL; + + if (cmd_len < sizeof(uint32_t)) { + pr_err("%s:%d failed: invalid cmd len %u exp %zu\n", + __func__, __LINE__, cmd_len, + sizeof(uint32_t)); + return -EINVAL; + } + + isp_id = (uint32_t *)cfg_data; + *isp_id = vfe_dev->pdev->id; + break; + } + case SET_WM_UB_SIZE: + break; + case SET_UB_POLICY: { + + if (cmd_len < sizeof(vfe_dev->vfe_ub_policy)) { + pr_err("%s:%d failed: invalid cmd len %u exp %zu\n", + __func__, __LINE__, cmd_len, + sizeof(vfe_dev->vfe_ub_policy)); + return -EINVAL; + } + vfe_dev->vfe_ub_policy = *cfg_data; + break; + } + default: + break; + } + return 0; +} + +int msm_isp_proc_cmd(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0, i; + struct msm_vfe_cfg_cmd2 *proc_cmd = arg; + struct msm_vfe_reg_cfg_cmd *reg_cfg_cmd; + uint32_t *cfg_data = NULL; + + if (!proc_cmd->num_cfg) { + pr_err("%s: Passed num_cfg as 0\n", __func__); + return -EINVAL; + } + + reg_cfg_cmd = kzalloc(sizeof(struct msm_vfe_reg_cfg_cmd)* + proc_cmd->num_cfg, GFP_KERNEL); + if (!reg_cfg_cmd) { + rc = -ENOMEM; + goto reg_cfg_failed; + } + + if (copy_from_user(reg_cfg_cmd, + (void __user *)(proc_cmd->cfg_cmd), + sizeof(struct msm_vfe_reg_cfg_cmd) * proc_cmd->num_cfg)) { + rc = -EFAULT; + goto copy_cmd_failed; + } + + if (proc_cmd->cmd_len > 0 && + proc_cmd->cmd_len < UINT16_MAX) { + cfg_data = kzalloc(proc_cmd->cmd_len, GFP_KERNEL); + if (!cfg_data) { + pr_err("%s: cfg_data alloc failed\n", __func__); + rc = -ENOMEM; + goto cfg_data_failed; + } + + if (copy_from_user(cfg_data, + (void __user *)(proc_cmd->cfg_data), + proc_cmd->cmd_len)) { + rc = -EFAULT; + goto copy_cmd_failed; + } + } + + for (i = 0; i < proc_cmd->num_cfg; i++) + rc = msm_isp_send_hw_cmd(vfe_dev, ®_cfg_cmd[i], + cfg_data, proc_cmd->cmd_len); + + if (copy_to_user(proc_cmd->cfg_data, + cfg_data, proc_cmd->cmd_len)) { + rc = -EFAULT; + goto copy_cmd_failed; + } + +copy_cmd_failed: + kfree(cfg_data); +cfg_data_failed: + kfree(reg_cfg_cmd); +reg_cfg_failed: + return rc; +} + +int msm_isp_send_event(struct vfe_device *vfe_dev, + uint32_t event_type, + struct msm_isp32_event_data *event_data) +{ + struct v4l2_event isp_event; + + memset(&isp_event, 0, sizeof(struct v4l2_event)); + isp_event.id = 0; + isp_event.type = event_type; + + memcpy(&isp_event.u.data[0], event_data, + sizeof(struct msm_isp32_event_data)); + v4l2_event_queue(vfe_dev->subdev.sd.devnode, &isp_event); + return 0; +} + +#define CAL_WORD(width, M, N) ((width * M + N - 1) / N) + +int msm_isp_cal_word_per_line(uint32_t output_format, + uint32_t pixel_per_line) +{ + int val = -1; + + switch (output_format) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_QBGGR8: + case V4L2_PIX_FMT_QGBRG8: + case V4L2_PIX_FMT_QGRBG8: + case V4L2_PIX_FMT_QRGGB8: + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_META: + val = CAL_WORD(pixel_per_line, 1, 8); + break; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + val = CAL_WORD(pixel_per_line, 5, 32); + break; + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + val = CAL_WORD(pixel_per_line, 3, 16); + break; + case V4L2_PIX_FMT_SBGGR14: + case V4L2_PIX_FMT_SGBRG14: + case V4L2_PIX_FMT_SGRBG14: + case V4L2_PIX_FMT_SRGGB14: + val = CAL_WORD(pixel_per_line, 7, 32); + break; + case V4L2_PIX_FMT_QBGGR10: + case V4L2_PIX_FMT_QGBRG10: + case V4L2_PIX_FMT_QGRBG10: + case V4L2_PIX_FMT_QRGGB10: + val = CAL_WORD(pixel_per_line, 1, 6); + break; + case V4L2_PIX_FMT_QBGGR12: + case V4L2_PIX_FMT_QGBRG12: + case V4L2_PIX_FMT_QGRBG12: + case V4L2_PIX_FMT_QRGGB12: + val = CAL_WORD(pixel_per_line, 1, 5); + break; + case V4L2_PIX_FMT_QBGGR14: + case V4L2_PIX_FMT_QGBRG14: + case V4L2_PIX_FMT_QGRBG14: + case V4L2_PIX_FMT_QRGGB14: + val = CAL_WORD(pixel_per_line, 1, 4); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV14: + case V4L2_PIX_FMT_NV41: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + val = CAL_WORD(pixel_per_line, 1, 8); + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + val = CAL_WORD(pixel_per_line, 2, 8); + break; + case V4L2_PIX_FMT_P16BGGR10: + case V4L2_PIX_FMT_P16GBRG10: + case V4L2_PIX_FMT_P16GRBG10: + case V4L2_PIX_FMT_P16RGGB10: + val = CAL_WORD(pixel_per_line, 1, 4); + break; + /*TD: Add more image format*/ + default: + msm_isp_print_fourcc_error(__func__, output_format); + break; + } + return val; +} + +enum msm_isp_pack_fmt msm_isp_get_pack_format(uint32_t output_format) +{ + switch (output_format) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + case V4L2_PIX_FMT_SBGGR14: + case V4L2_PIX_FMT_SGBRG14: + case V4L2_PIX_FMT_SGRBG14: + case V4L2_PIX_FMT_SRGGB14: + return MIPI; + case V4L2_PIX_FMT_QBGGR8: + case V4L2_PIX_FMT_QGBRG8: + case V4L2_PIX_FMT_QGRBG8: + case V4L2_PIX_FMT_QRGGB8: + case V4L2_PIX_FMT_QBGGR10: + case V4L2_PIX_FMT_QGBRG10: + case V4L2_PIX_FMT_QGRBG10: + case V4L2_PIX_FMT_QRGGB10: + case V4L2_PIX_FMT_QBGGR12: + case V4L2_PIX_FMT_QGBRG12: + case V4L2_PIX_FMT_QGRBG12: + case V4L2_PIX_FMT_QRGGB12: + case V4L2_PIX_FMT_QBGGR14: + case V4L2_PIX_FMT_QGBRG14: + case V4L2_PIX_FMT_QGRBG14: + case V4L2_PIX_FMT_QRGGB14: + return QCOM; + case V4L2_PIX_FMT_P16BGGR10: + case V4L2_PIX_FMT_P16GBRG10: + case V4L2_PIX_FMT_P16GRBG10: + case V4L2_PIX_FMT_P16RGGB10: + return PLAIN16; + default: + msm_isp_print_fourcc_error(__func__, output_format); + break; + } + return -EINVAL; +} + +int msm_isp_get_bit_per_pixel(uint32_t output_format) +{ + switch (output_format) { + case V4L2_PIX_FMT_Y4: + return 4; + case V4L2_PIX_FMT_Y6: + return 6; + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + case V4L2_PIX_FMT_QBGGR8: + case V4L2_PIX_FMT_QGBRG8: + case V4L2_PIX_FMT_QGRBG8: + case V4L2_PIX_FMT_QRGGB8: + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_META: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV14: + case V4L2_PIX_FMT_NV41: + case V4L2_PIX_FMT_YVU410: + case V4L2_PIX_FMT_YVU420: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YYUV: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_VYUY: + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_YUV411P: + case V4L2_PIX_FMT_Y41P: + case V4L2_PIX_FMT_YUV444: + case V4L2_PIX_FMT_YUV555: + case V4L2_PIX_FMT_YUV565: + case V4L2_PIX_FMT_YUV32: + case V4L2_PIX_FMT_YUV410: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_PAL8: + case V4L2_PIX_FMT_UV8: + case MSM_V4L2_PIX_FMT_META: + return 8; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + case V4L2_PIX_FMT_QBGGR10: + case V4L2_PIX_FMT_QGBRG10: + case V4L2_PIX_FMT_QGRBG10: + case V4L2_PIX_FMT_QRGGB10: + case V4L2_PIX_FMT_Y10: + case V4L2_PIX_FMT_Y10BPACK: + case V4L2_PIX_FMT_P16BGGR10: + case V4L2_PIX_FMT_P16GBRG10: + case V4L2_PIX_FMT_P16GRBG10: + case V4L2_PIX_FMT_P16RGGB10: + return 10; + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + case V4L2_PIX_FMT_QBGGR12: + case V4L2_PIX_FMT_QGBRG12: + case V4L2_PIX_FMT_QGRBG12: + case V4L2_PIX_FMT_QRGGB12: + case V4L2_PIX_FMT_Y12: + return 12; + case V4L2_PIX_FMT_SBGGR14: + case V4L2_PIX_FMT_SGBRG14: + case V4L2_PIX_FMT_SGRBG14: + case V4L2_PIX_FMT_SRGGB14: + case V4L2_PIX_FMT_QBGGR14: + case V4L2_PIX_FMT_QGBRG14: + case V4L2_PIX_FMT_QGRBG14: + case V4L2_PIX_FMT_QRGGB14: + return 14; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + case V4L2_PIX_FMT_Y16: + return 16; + /*TD: Add more image format*/ + default: + msm_isp_print_fourcc_error(__func__, output_format); + pr_err("%s: Invalid output format %x\n", + __func__, output_format); + return -EINVAL; + } +} + +void msm_isp_update_error_frame_count(struct vfe_device *vfe_dev) +{ + struct msm_vfe_error_info *error_info = &vfe_dev->error_info; + + error_info->info_dump_frame_count++; +} + +void msm_isp_process_error_info(struct vfe_device *vfe_dev) +{ + int i; + uint8_t num_stats_type = + vfe_dev->hw_info->stats_hw_info->num_stats_type; + struct msm_vfe_error_info *error_info = &vfe_dev->error_info; + static DEFINE_RATELIMIT_STATE(rs, + DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); + static DEFINE_RATELIMIT_STATE(rs_stats, + DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); + + if (error_info->error_count == 1 || + !(error_info->info_dump_frame_count % 100)) { + vfe_dev->hw_info->vfe_ops.core_ops. + process_error_status(vfe_dev); + error_info->error_mask0 = 0; + error_info->error_mask1 = 0; + error_info->camif_status = 0; + error_info->violation_status = 0; + for (i = 0; i < MAX_NUM_STREAM; i++) { + if (error_info->stream_framedrop_count[i] != 0 && + __ratelimit(&rs)) { + pr_err("%s: Stream[%d]: dropped %d frames\n", + __func__, i, + error_info->stream_framedrop_count[i]); + error_info->stream_framedrop_count[i] = 0; + } + } + for (i = 0; i < num_stats_type; i++) { + if (error_info->stats_framedrop_count[i] != 0 && + __ratelimit(&rs_stats)) { + pr_err("%s: Stats stream[%d]: dropped %d frames\n", + __func__, i, + error_info->stats_framedrop_count[i]); + error_info->stats_framedrop_count[i] = 0; + } + } + } +} + +static inline void msm_isp_update_error_info(struct vfe_device *vfe_dev, + uint32_t error_mask0, uint32_t error_mask1) +{ + vfe_dev->error_info.error_mask0 |= error_mask0; + vfe_dev->error_info.error_mask1 |= error_mask1; + vfe_dev->error_info.error_count++; +} + +static void msm_isp_process_overflow_irq( + struct vfe_device *vfe_dev, + uint32_t *irq_status0, uint32_t *irq_status1) +{ + uint32_t overflow_mask; + + /* if there are no active streams - do not start recovery */ + if (!vfe_dev->axi_data.num_active_stream) + return; + + /*Mask out all other irqs if recovery is started*/ + if (atomic_read(&vfe_dev->error_info.overflow_state) != NO_OVERFLOW) { + uint32_t halt_restart_mask0, halt_restart_mask1; + + vfe_dev->hw_info->vfe_ops.core_ops. + get_halt_restart_mask(&halt_restart_mask0, + &halt_restart_mask1); + *irq_status0 &= halt_restart_mask0; + *irq_status1 &= halt_restart_mask1; + + return; + } + + /*Check if any overflow bit is set*/ + vfe_dev->hw_info->vfe_ops.core_ops. + get_overflow_mask(&overflow_mask); + overflow_mask &= *irq_status1; + + if (overflow_mask) { + struct msm_isp32_event_data error_event; + + if (vfe_dev->reset_pending == 1) { + pr_err("%s:%d failed: overflow %x during reset\n", + __func__, __LINE__, overflow_mask); + /* Clear overflow bits since reset is pending */ + *irq_status1 &= ~overflow_mask; + return; + } + + ISP_DBG("%s: Bus overflow detected: 0x%x, start recovery!\n", + __func__, overflow_mask); + atomic_set(&vfe_dev->error_info.overflow_state, + OVERFLOW_DETECTED); + /*Store current IRQ mask*/ + vfe_dev->hw_info->vfe_ops.core_ops.get_irq_mask(vfe_dev, + &vfe_dev->error_info.overflow_recover_irq_mask0, + &vfe_dev->error_info.overflow_recover_irq_mask1); + + /*Halt the hardware & Clear all other IRQ mask*/ + vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 0); + + /*Stop CAMIF Immediately*/ + vfe_dev->hw_info->vfe_ops.core_ops. + update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY); + + /*Update overflow state*/ + *irq_status0 = 0; + *irq_status1 = 0; + + memset(&error_event, 0, sizeof(error_event)); + error_event.frame_id = + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; + error_event.u.error_info.error_mask = 1 << ISP_WM_BUS_OVERFLOW; + msm_isp_send_event(vfe_dev, + ISP_EVENT_WM_BUS_OVERFLOW, &error_event); + } +} + +void msm_isp_reset_burst_count_and_frame_drop( + struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info) +{ + uint32_t framedrop_period = 0; + + if (stream_info->state != ACTIVE || + stream_info->stream_type != BURST_STREAM) { + return; + } + if (stream_info->num_burst_capture != 0) { + framedrop_period = msm_isp_get_framedrop_period( + stream_info->frame_skip_pattern); + stream_info->burst_frame_count = + stream_info->init_frame_drop + + (stream_info->num_burst_capture - 1) * + framedrop_period + 1; + msm_isp_reset_framedrop(vfe_dev, stream_info); + } +} + +irqreturn_t msm_isp_process_irq(int irq_num, void *data) +{ + unsigned long flags; + struct msm_vfe_tasklet_queue_cmd *queue_cmd; + struct vfe_device *vfe_dev = (struct vfe_device *) data; + uint32_t irq_status0, irq_status1; + uint32_t error_mask0, error_mask1; + + vfe_dev->hw_info->vfe_ops.irq_ops. + read_irq_status(vfe_dev, &irq_status0, &irq_status1); + + if ((irq_status0 == 0) && (irq_status1 == 0)) { + pr_err_ratelimited("%s:VFE%d irq_status0 & 1 are both 0\n", + __func__, vfe_dev->pdev->id); + return IRQ_HANDLED; + } + + msm_isp_process_overflow_irq(vfe_dev, + &irq_status0, &irq_status1); + + vfe_dev->hw_info->vfe_ops.core_ops. + get_error_mask(&error_mask0, &error_mask1); + error_mask0 &= irq_status0; + error_mask1 &= irq_status1; + irq_status0 &= ~error_mask0; + irq_status1 &= ~error_mask1; + if (!vfe_dev->ignore_error && + ((error_mask0 != 0) || (error_mask1 != 0))) + msm_isp_update_error_info(vfe_dev, error_mask0, error_mask1); + + if ((irq_status0 == 0) && (irq_status1 == 0) && + (!(((error_mask0 != 0) || (error_mask1 != 0)) && + vfe_dev->error_info.error_count == 1))) { + ISP_DBG("%s: error_mask0/1 & error_count are set!\n", __func__); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&vfe_dev->tasklet_lock, flags); + queue_cmd = &vfe_dev->tasklet_queue_cmd[vfe_dev->taskletq_idx]; + if (queue_cmd->cmd_used) { + pr_err_ratelimited("%s: Tasklet queue overflow: %d\n", + __func__, vfe_dev->pdev->id); + list_del(&queue_cmd->list); + } else { + atomic_add(1, &vfe_dev->irq_cnt); + } + queue_cmd->vfeInterruptStatus0 = irq_status0; + queue_cmd->vfeInterruptStatus1 = irq_status1; + msm_isp_get_timestamp(&queue_cmd->ts, vfe_dev); + queue_cmd->cmd_used = 1; + vfe_dev->taskletq_idx = + (vfe_dev->taskletq_idx + 1) % MSM_VFE_TASKLETQ_SIZE; + list_add_tail(&queue_cmd->list, &vfe_dev->tasklet_q); + spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags); + tasklet_schedule(&vfe_dev->vfe_tasklet); + return IRQ_HANDLED; +} + +void msm_isp_do_tasklet(unsigned long data) +{ + unsigned long flags; + struct vfe_device *vfe_dev = (struct vfe_device *) data; + struct msm_vfe_irq_ops *irq_ops = &vfe_dev->hw_info->vfe_ops.irq_ops; + struct msm_vfe_tasklet_queue_cmd *queue_cmd; + struct msm_isp_timestamp ts; + uint32_t irq_status0, irq_status1; + + while (atomic_read(&vfe_dev->irq_cnt)) { + spin_lock_irqsave(&vfe_dev->tasklet_lock, flags); + queue_cmd = list_first_entry(&vfe_dev->tasklet_q, + struct msm_vfe_tasklet_queue_cmd, list); + + if (!queue_cmd) { + atomic_set(&vfe_dev->irq_cnt, 0); + spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags); + return; + } + atomic_sub(1, &vfe_dev->irq_cnt); + list_del(&queue_cmd->list); + queue_cmd->cmd_used = 0; + irq_status0 = queue_cmd->vfeInterruptStatus0; + irq_status1 = queue_cmd->vfeInterruptStatus1; + ts = queue_cmd->ts; + spin_unlock_irqrestore(&vfe_dev->tasklet_lock, flags); + ISP_DBG("%s: status0: 0x%x status1: 0x%x\n", + __func__, irq_status0, irq_status1); + irq_ops->process_reset_irq(vfe_dev, + irq_status0, irq_status1); + irq_ops->process_halt_irq(vfe_dev, + irq_status0, irq_status1); + if (atomic_read(&vfe_dev->error_info.overflow_state) + != NO_OVERFLOW) { + pr_err("%s: Recovery in processing, Ignore IRQs!!!\n", + __func__); + continue; + } + msm_isp_process_error_info(vfe_dev); + irq_ops->process_camif_irq(vfe_dev, + irq_status0, irq_status1, &ts); + irq_ops->process_axi_irq(vfe_dev, + irq_status0, irq_status1, &ts); + irq_ops->process_stats_irq(vfe_dev, + irq_status0, irq_status1, &ts); + irq_ops->process_reg_update(vfe_dev, + irq_status0, irq_status1, &ts); + irq_ops->process_epoch_irq(vfe_dev, + irq_status0, irq_status1, &ts); + } +} + +int msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg) +{ + struct msm_vfe_axi_src_state *src_state = arg; + + if (src_state->input_src >= VFE_SRC_MAX) + return -EINVAL; + vfe_dev->axi_data.src_info[src_state->input_src].active = + src_state->src_active; + vfe_dev->axi_data.src_info[src_state->input_src].frame_id = + src_state->src_frame_id; + return 0; +} + +static void msm_vfe_iommu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, int flags, void *token) +{ + struct vfe_device *vfe_dev = NULL; + + if (token) { + vfe_dev = (struct vfe_device *)token; + if (!vfe_dev->buf_mgr || !vfe_dev->buf_mgr->ops) { + pr_err("%s:%d] buf_mgr %pK\n", __func__, + __LINE__, vfe_dev->buf_mgr); + goto end; + } + if (!vfe_dev->buf_mgr->pagefault_debug_disable) { + pr_err("%s:%d] vfe_dev %pK id %d\n", __func__, + __LINE__, vfe_dev, vfe_dev->pdev->id); + vfe_dev->buf_mgr->ops->buf_mgr_debug(vfe_dev->buf_mgr, + iova); + } + } else { + ISP_DBG("%s:%d] no token received: %pK\n", + __func__, __LINE__, token); + goto end; + } +end: + return; +} + +int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd); + long rc = 0; + + ISP_DBG("%s\n", __func__); + + mutex_lock(&vfe_dev->realtime_mutex); + mutex_lock(&vfe_dev->core_mutex); + + if (vfe_dev->vfe_open_cnt++ && vfe_dev->vfe_base) { + mutex_unlock(&vfe_dev->core_mutex); + mutex_unlock(&vfe_dev->realtime_mutex); + return 0; + } + + if (vfe_dev->vfe_base) { + pr_err("%s:%d invalid params cnt %d base %pK\n", __func__, + __LINE__, vfe_dev->vfe_open_cnt, vfe_dev->vfe_base); + vfe_dev->vfe_base = NULL; + } + + vfe_dev->reset_pending = 0; + vfe_dev->isp_sof_debug = 0; + + if (vfe_dev->hw_info->vfe_ops.core_ops.init_hw(vfe_dev) < 0) { + pr_err("%s: init hardware failed\n", __func__); + vfe_dev->vfe_open_cnt--; + mutex_unlock(&vfe_dev->core_mutex); + mutex_unlock(&vfe_dev->realtime_mutex); + return -EBUSY; + } + + memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info)); + atomic_set(&vfe_dev->error_info.overflow_state, NO_OVERFLOW); + + vfe_dev->hw_info->vfe_ops.core_ops.clear_status_reg(vfe_dev); + + rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 1, 1); + if (rc <= 0) { + pr_err("%s: reset timeout\n", __func__); + vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev); + vfe_dev->vfe_open_cnt--; + mutex_unlock(&vfe_dev->core_mutex); + mutex_unlock(&vfe_dev->realtime_mutex); + return -EINVAL; + } + vfe_dev->vfe_hw_version = msm_camera_io_r(vfe_dev->vfe_base); + ISP_DBG("%s: HW Version: 0x%x\n", __func__, vfe_dev->vfe_hw_version); + + vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev); + + vfe_dev->buf_mgr->ops->buf_mgr_init(vfe_dev->buf_mgr, "msm_isp"); + + memset(&vfe_dev->axi_data, 0, sizeof(struct msm_vfe_axi_shared_data)); + memset(&vfe_dev->stats_data, 0, + sizeof(struct msm_vfe_stats_shared_data)); + memset(&vfe_dev->error_info, 0, sizeof(vfe_dev->error_info)); + memset(&vfe_dev->fetch_engine_info, 0, + sizeof(vfe_dev->fetch_engine_info)); + vfe_dev->axi_data.hw_info = vfe_dev->hw_info->axi_hw_info; + vfe_dev->taskletq_idx = 0; + vfe_dev->vt_enable = 0; + vfe_dev->bus_util_factor = 0; + rc = of_property_read_u32(vfe_dev->pdev->dev.of_node, + "bus-util-factor", &vfe_dev->bus_util_factor); + if (rc < 0) + ISP_DBG("%s: Use default bus utilization factor\n", __func__); + + cam_smmu_reg_client_page_fault_handler( + vfe_dev->buf_mgr->iommu_hdl, + msm_vfe_iommu_fault_handler, + NULL, + vfe_dev); + + mutex_unlock(&vfe_dev->core_mutex); + mutex_unlock(&vfe_dev->realtime_mutex); + return 0; +} + +#ifdef CONFIG_MSM_AVTIMER +static void msm_isp_end_avtimer(void) +{ + avcs_core_disable_power_collapse(0); +} +#else +static void msm_isp_end_avtimer(void) +{ + pr_err("AV Timer is not supported\n"); +} +#endif + +int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + long rc = 0; + struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd); + + ISP_DBG("%s E\n", __func__); + mutex_lock(&vfe_dev->realtime_mutex); + mutex_lock(&vfe_dev->core_mutex); + + if (!vfe_dev->vfe_open_cnt) { + pr_err("%s invalid state open cnt %d\n", __func__, + vfe_dev->vfe_open_cnt); + mutex_unlock(&vfe_dev->core_mutex); + mutex_unlock(&vfe_dev->realtime_mutex); + return -EINVAL; + } + + if (--vfe_dev->vfe_open_cnt) { + mutex_unlock(&vfe_dev->core_mutex); + mutex_unlock(&vfe_dev->realtime_mutex); + return 0; + } + + /* Unregister page fault handler */ + cam_smmu_reg_client_page_fault_handler( + vfe_dev->buf_mgr->iommu_hdl, + NULL, NULL, vfe_dev); + + rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1); + if (rc < 0) + pr_err("%s: halt timeout rc=%ld\n", __func__, rc); + + vfe_dev->buf_mgr->ops->buf_mgr_deinit(vfe_dev->buf_mgr); + vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev); + if (vfe_dev->vt_enable) { + msm_isp_end_avtimer(); + vfe_dev->vt_enable = 0; + } + mutex_unlock(&vfe_dev->core_mutex); + mutex_unlock(&vfe_dev->realtime_mutex); + return 0; +} diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.h new file mode 100644 index 000000000000..f2268c3b46a1 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util_32.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MSM_ISP_UTIL_H__ +#define __MSM_ISP_UTIL_H__ + +#include "msm_isp_32.h" +#include + +/* #define CONFIG_MSM_ISP_DBG 1 */ + +#ifdef CONFIG_MSM_ISP_DBG +#define ISP_DBG(fmt, args...) printk(fmt, ##args) +#else +#define ISP_DBG(fmt, args...) pr_debug(fmt, ##args) +#endif + +#define ALT_VECTOR_IDX(x) {x = 3 - x; } + +struct msm_isp_bandwidth_mgr { + uint32_t bus_client; + uint32_t bus_vector_active_idx; + uint32_t use_count; + struct msm_isp_bandwidth_info client_info[MAX_ISP_CLIENT]; +}; + +uint32_t msm_isp_get_framedrop_period( + enum msm_vfe_frame_skip_pattern frame_skip_pattern); +void msm_isp_reset_burst_count_and_frame_drop( + struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info); + +int msm_isp_init_bandwidth_mgr(enum msm_isp_hw_client client); +int msm_isp_update_bandwidth(enum msm_isp_hw_client client, + uint64_t ab, uint64_t ib); +void msm_isp_util_get_bandwidth_stats(struct vfe_device *vfe_dev, + struct msm_isp_statistics *stats); +void msm_isp_util_update_last_overflow_ab_ib(struct vfe_device *vfe_dev); +void msm_isp_util_update_clk_rate(long clock_rate); +void msm_isp_update_req_history(uint32_t client, uint64_t ab, + uint64_t ib, + struct msm_isp_bandwidth_info *client_info, + unsigned long long ts); +void msm_isp_deinit_bandwidth_mgr(enum msm_isp_hw_client client); + +int msm_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); + +int msm_isp_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); + +int msm_isp_proc_cmd(struct vfe_device *vfe_dev, void *arg); +int msm_isp_send_event(struct vfe_device *vfe_dev, + uint32_t type, struct msm_isp32_event_data *event_data); +int msm_isp_cal_word_per_line(uint32_t output_format, + uint32_t pixel_per_line); +int msm_isp_get_bit_per_pixel(uint32_t output_format); +enum msm_isp_pack_fmt msm_isp_get_pack_format(uint32_t output_format); +irqreturn_t msm_isp_process_irq(int irq_num, void *data); +int msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg); +void msm_isp_do_tasklet(unsigned long data); +void msm_isp_update_error_frame_count(struct vfe_device *vfe_dev); +void msm_isp_process_error_info(struct vfe_device *vfe_dev); +int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); +int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh); +long msm_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg); +int msm_isp_get_clk_info(struct vfe_device *vfe_dev, + struct platform_device *pdev, struct msm_cam_clk_info *vfe_clk_info); +void msm_isp_fetch_engine_done_notify(struct vfe_device *vfe_dev, + struct msm_vfe_fetch_engine_info *fetch_engine_info); +void msm_camera_io_dump_2(void __iomem *addr, int size); +void msm_isp_print_fourcc_error(const char *origin, uint32_t fourcc_format); +void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp, + struct vfe_device *vfe_dev); +void msm_isp_get_avtimer_ts(struct msm_isp_timestamp *time_stamp); +int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg); +#endif /* __MSM_ISP_UTIL_H__ */ diff --git a/drivers/media/platform/msm/camera_v2/ispif/Makefile b/drivers/media/platform/msm/camera_v2/ispif/Makefile index 236ec7340c56..d56332d27abb 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/Makefile +++ b/drivers/media/platform/msm/camera_v2/ispif/Makefile @@ -1,4 +1,8 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2 ccflags-y += -Idrivers/media/platform/msm/camera_v2/common ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io +ifeq ($(CONFIG_MSM_ISP_V1),y) +obj-$(CONFIG_MSM_CSID) += msm_ispif_32.o +else obj-$(CONFIG_MSM_CSID) += msm_ispif.o +endif diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.c new file mode 100644 index 000000000000..e9b2a1d80280 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.c @@ -0,0 +1,1581 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_ispif_32.h" +#include "msm.h" +#include "msm_sd.h" +#include "msm_camera_io_util.h" + +#ifdef CONFIG_MSM_ISPIF_V1 +#include "msm_ispif_hwreg_v1.h" +#else +#include "msm_ispif_hwreg_v2.h" +#endif + +#define V4L2_IDENT_ISPIF 50001 +#define MSM_ISPIF_DRV_NAME "msm_ispif" + +#define ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY 0x00 +#define ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY 0x01 +#define ISPIF_INTF_CMD_DISABLE_IMMEDIATELY 0x02 + +#define ISPIF_TIMEOUT_SLEEP_US 1000 +#define ISPIF_TIMEOUT_ALL_US 1000000 + +#undef CDBG +#ifdef CONFIG_MSMB_CAMERA_DEBUG +#define CDBG(fmt, args...) pr_debug(fmt, ##args) +#else +#define CDBG(fmt, args...) do { } while (0) +#endif + +static void msm_ispif_io_dump_reg(struct ispif_device *ispif) +{ + if (!ispif->enb_dump_reg) + return; + + if (!ispif->base) { + pr_err("%s: null pointer for the ispif base\n", __func__); + return; + } + + msm_camera_io_dump(ispif->base, 0x250, 1); +} + + +static inline int msm_ispif_is_intf_valid(uint32_t csid_version, + uint8_t intf_type) +{ + return (((csid_version <= CSID_VERSION_V22 + && intf_type != VFE0) || + (intf_type >= VFE_MAX)) + ? false : true); +} + +static struct msm_cam_clk_info ispif_8626_reset_clk_info[] = { + {"ispif_ahb_clk", NO_SET_RATE}, + {"csi0_src_clk", NO_SET_RATE}, + {"csi0_clk", NO_SET_RATE}, + {"csi0_pix_clk", NO_SET_RATE}, + {"csi0_rdi_clk", NO_SET_RATE}, + {"csi1_src_clk", NO_SET_RATE}, + {"csi1_clk", NO_SET_RATE}, + {"csi1_pix_clk", NO_SET_RATE}, + {"csi1_rdi_clk", NO_SET_RATE}, + {"camss_vfe_vfe0_clk", NO_SET_RATE}, + {"camss_csi_vfe0_clk", NO_SET_RATE}, +}; + +static struct msm_cam_clk_info ispif_8974_ahb_clk_info[ISPIF_CLK_INFO_MAX]; + +static struct msm_cam_clk_info ispif_8974_reset_clk_info[] = { + {"csi0_src_clk", INIT_RATE}, + {"csi0_clk", NO_SET_RATE}, + {"csi0_pix_clk", NO_SET_RATE}, + {"csi0_rdi_clk", NO_SET_RATE}, + {"csi1_src_clk", INIT_RATE}, + {"csi1_clk", NO_SET_RATE}, + {"csi1_pix_clk", NO_SET_RATE}, + {"csi1_rdi_clk", NO_SET_RATE}, + {"csi2_src_clk", INIT_RATE}, + {"csi2_clk", NO_SET_RATE}, + {"csi2_pix_clk", NO_SET_RATE}, + {"csi2_rdi_clk", NO_SET_RATE}, + {"csi3_src_clk", INIT_RATE}, + {"csi3_clk", NO_SET_RATE}, + {"csi3_pix_clk", NO_SET_RATE}, + {"csi3_rdi_clk", NO_SET_RATE}, + {"vfe0_clk_src", INIT_RATE}, + {"camss_vfe_vfe0_clk", NO_SET_RATE}, + {"camss_csi_vfe0_clk", NO_SET_RATE}, + {"vfe1_clk_src", INIT_RATE}, + {"camss_vfe_vfe1_clk", NO_SET_RATE}, + {"camss_csi_vfe1_clk", NO_SET_RATE}, +}; + +static int msm_ispif_reset_hw(struct ispif_device *ispif) +{ + int rc = 0; + long timeout = 0; + struct clk *reset_clk[ARRAY_SIZE(ispif_8974_reset_clk_info)]; + struct clk *reset_clk1[ARRAY_SIZE(ispif_8626_reset_clk_info)]; + + ispif->clk_idx = 0; + + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8974_reset_clk_info, reset_clk, + ARRAY_SIZE(ispif_8974_reset_clk_info), 1); + if (rc < 0) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8626_reset_clk_info, reset_clk1, + ARRAY_SIZE(ispif_8626_reset_clk_info), 1); + if (rc < 0) { + pr_err("%s: cannot enable clock, error = %d", + __func__, rc); + } else { + /* This is set when device is 8x26 */ + ispif->clk_idx = 2; + } + } else { + /* This is set when device is 8974 */ + ispif->clk_idx = 1; + } + + init_completion(&ispif->reset_complete[VFE0]); + if (ispif->hw_num_isps > 1) + init_completion(&ispif->reset_complete[VFE1]); + + /* initiate reset of ISPIF */ + msm_camera_io_w(ISPIF_RST_CMD_MASK, + ispif->base + ISPIF_RST_CMD_ADDR); + if (ispif->hw_num_isps > 1) + msm_camera_io_w(ISPIF_RST_CMD_1_MASK, + ispif->base + ISPIF_RST_CMD_1_ADDR); + + timeout = wait_for_completion_timeout( + &ispif->reset_complete[VFE0], msecs_to_jiffies(500)); + CDBG("%s: VFE0 done\n", __func__); + + if (timeout <= 0) { + pr_err("%s: VFE0 reset wait timeout\n", __func__); + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8974_reset_clk_info, reset_clk, + ARRAY_SIZE(ispif_8974_reset_clk_info), 0); + if (rc < 0) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8626_reset_clk_info, reset_clk1, + ARRAY_SIZE(ispif_8626_reset_clk_info), 0); + if (rc < 0) + pr_err("%s: VFE0 reset wait timeout\n", + __func__); + } + return -ETIMEDOUT; + } + + if (ispif->hw_num_isps > 1) { + timeout = wait_for_completion_timeout( + &ispif->reset_complete[VFE1], + msecs_to_jiffies(500)); + CDBG("%s: VFE1 done\n", __func__); + if (timeout <= 0) { + pr_err("%s: VFE1 reset wait timeout\n", __func__); + msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8974_reset_clk_info, reset_clk, + ARRAY_SIZE(ispif_8974_reset_clk_info), 0); + return -ETIMEDOUT; + } + } + + if (ispif->clk_idx == 1) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8974_reset_clk_info, reset_clk, + ARRAY_SIZE(ispif_8974_reset_clk_info), 0); + if (rc < 0) { + pr_err("%s: cannot disable clock, error = %d", + __func__, rc); + } + } + + if (ispif->clk_idx == 2) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8626_reset_clk_info, reset_clk1, + ARRAY_SIZE(ispif_8626_reset_clk_info), 0); + if (rc < 0) { + pr_err("%s: cannot disable clock, error = %d", + __func__, rc); + } + } + + return rc; +} + +static int msm_ispif_get_ahb_clk_info(struct ispif_device *ispif_dev, + struct platform_device *pdev, + struct msm_cam_clk_info *ahb_clk_info) +{ + uint32_t num_ahb_clk = 0; + int i, count, rc; + uint32_t rates[ISPIF_CLK_INFO_MAX]; + + struct device_node *of_node; + + of_node = pdev->dev.of_node; + + count = of_property_count_strings(of_node, "clock-names"); + + CDBG("count = %d\n", count); + if (count <= 0) { + pr_err("no clocks found in device tree, count=%d", count); + return 0; + } + + if (count > ISPIF_CLK_INFO_MAX) { + pr_err("invalid count=%d, max is %d\n", count, + ISPIF_CLK_INFO_MAX); + return -EINVAL; + } + + rc = of_property_read_u32_array(of_node, "qcom,clock-rates", + rates, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return rc; + } + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, "clock-names", + i, &(ahb_clk_info[num_ahb_clk].clk_name)); + CDBG("clock-names[%d] = %s\n", + i, ahb_clk_info[i].clk_name); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + return rc; + } + if (strnstr(ahb_clk_info[num_ahb_clk].clk_name, "ahb", + sizeof(ahb_clk_info[num_ahb_clk].clk_name))) { + ahb_clk_info[num_ahb_clk].clk_rate = + (rates[i] == 0) ? (long)-1 : rates[i]; + CDBG("clk_rate[%d] = %ld\n", i, + ahb_clk_info[i].clk_rate); + num_ahb_clk++; + } + } + ispif_dev->num_ahb_clk = num_ahb_clk; + return 0; +} + +static int msm_ispif_clk_ahb_enable(struct ispif_device *ispif, int enable) +{ + int rc = 0; + + if (ispif->csid_version < CSID_VERSION_V30) { + /* Older ISPIF versiond don't need ahb clokc */ + return 0; + } + + rc = msm_ispif_get_ahb_clk_info(ispif, ispif->pdev, + ispif_8974_ahb_clk_info); + if (rc < 0) { + pr_err("%s: msm_isp_get_clk_info() failed", __func__); + return -EFAULT; + } + + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8974_ahb_clk_info, ispif->ahb_clk, + ispif->num_ahb_clk, enable); + if (rc < 0) { + pr_err("%s: cannot enable clock, error = %d", + __func__, rc); + } + + return rc; +} + +static int msm_ispif_reset(struct ispif_device *ispif) +{ + int rc = 0; + int i; + + if (WARN_ON(!ispif)) + return -EINVAL; + + memset(ispif->sof_count, 0, sizeof(ispif->sof_count)); + for (i = 0; i < ispif->vfe_info.num_vfe; i++) { + + msm_camera_io_w(1 << PIX0_LINE_BUF_EN_BIT, + ispif->base + ISPIF_VFE_m_CTRL_0(i)); + msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_0(i)); + msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_1(i)); + msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_IRQ_MASK_2(i)); + msm_camera_io_w(0xFFFFFFFF, ispif->base + + ISPIF_VFE_m_IRQ_CLEAR_0(i)); + msm_camera_io_w(0xFFFFFFFF, ispif->base + + ISPIF_VFE_m_IRQ_CLEAR_1(i)); + msm_camera_io_w(0xFFFFFFFF, ispif->base + + ISPIF_VFE_m_IRQ_CLEAR_2(i)); + + msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_INPUT_SEL(i)); + + msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY, + ispif->base + ISPIF_VFE_m_INTF_CMD_0(i)); + msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY, + ispif->base + ISPIF_VFE_m_INTF_CMD_1(i)); + pr_debug("%s: base %pK", __func__, ispif->base); + msm_camera_io_w(0, ispif->base + + ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 0)); + msm_camera_io_w(0, ispif->base + + ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 1)); + msm_camera_io_w(0, ispif->base + + ISPIF_VFE_m_RDI_INTF_n_CID_MASK(i, 0)); + msm_camera_io_w(0, ispif->base + + ISPIF_VFE_m_RDI_INTF_n_CID_MASK(i, 1)); + msm_camera_io_w(0, ispif->base + + ISPIF_VFE_m_RDI_INTF_n_CID_MASK(i, 2)); + + msm_camera_io_w(0, ispif->base + + ISPIF_VFE_m_PIX_INTF_n_CROP(i, 0)); + msm_camera_io_w(0, ispif->base + + ISPIF_VFE_m_PIX_INTF_n_CROP(i, 1)); + } + + msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base + + ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR); + + return rc; +} + +static void msm_ispif_sel_csid_core(struct ispif_device *ispif, + uint8_t intftype, uint8_t csid, uint8_t vfe_intf) +{ + uint32_t data; + + if (WARN_ON(!ispif)) + return; + + if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) { + pr_err("%s: invalid interface type\n", __func__); + return; + } + + data = msm_camera_io_r(ispif->base + ISPIF_VFE_m_INPUT_SEL(vfe_intf)); + switch (intftype) { + case PIX0: + data &= ~(BIT(1) | BIT(0)); + data |= (uint32_t)csid; + break; + case RDI0: + data &= ~(BIT(5) | BIT(4)); + data |= (uint32_t)(csid << 4); + break; + case PIX1: + data &= ~(BIT(9) | BIT(8)); + data |= (uint32_t)(csid << 8); + break; + case RDI1: + data &= ~(BIT(13) | BIT(12)); + data |= (uint32_t)(csid << 12); + break; + case RDI2: + data &= ~(BIT(21) | BIT(20)); + data |= (uint32_t)(csid << 20); + break; + } + + msm_camera_io_w_mb(data, ispif->base + + ISPIF_VFE_m_INPUT_SEL(vfe_intf)); +} + +static void msm_ispif_enable_crop(struct ispif_device *ispif, + uint8_t intftype, uint8_t vfe_intf, uint16_t start_pixel, + uint16_t end_pixel) +{ + uint32_t data; + + if (WARN_ON(!ispif)) + return; + + if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) { + pr_err("%s: invalid interface type\n", __func__); + return; + } + + data = msm_camera_io_r(ispif->base + ISPIF_VFE_m_CTRL_0(vfe_intf)); + data |= (1 << (intftype + 7)); + if (intftype == PIX0) + data |= 1 << PIX0_LINE_BUF_EN_BIT; + msm_camera_io_w(data, + ispif->base + ISPIF_VFE_m_CTRL_0(vfe_intf)); + + if (intftype == PIX0) + msm_camera_io_w_mb(start_pixel | (end_pixel << 16), + ispif->base + ISPIF_VFE_m_PIX_INTF_n_CROP(vfe_intf, 0)); + else if (intftype == PIX1) + msm_camera_io_w_mb(start_pixel | (end_pixel << 16), + ispif->base + ISPIF_VFE_m_PIX_INTF_n_CROP(vfe_intf, 1)); + else { + pr_err("%s: invalid intftype=%d\n", __func__, intftype); + WARN_ON(1); + return; + } +} + +static void msm_ispif_enable_intf_cids(struct ispif_device *ispif, + uint8_t intftype, uint16_t cid_mask, uint8_t vfe_intf, uint8_t enable) +{ + uint32_t intf_addr, data; + + if (WARN_ON((!ispif))) + return; + + if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) { + pr_err("%s: invalid interface type\n", __func__); + return; + } + + switch (intftype) { + case PIX0: + intf_addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe_intf, 0); + break; + case RDI0: + intf_addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe_intf, 0); + break; + case PIX1: + intf_addr = ISPIF_VFE_m_PIX_INTF_n_CID_MASK(vfe_intf, 1); + break; + case RDI1: + intf_addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe_intf, 1); + break; + case RDI2: + intf_addr = ISPIF_VFE_m_RDI_INTF_n_CID_MASK(vfe_intf, 2); + break; + default: + pr_err("%s: invalid intftype=%d\n", __func__, intftype); + WARN_ON(1); + return; + } + + data = msm_camera_io_r(ispif->base + intf_addr); + if (enable) + data |= (uint32_t)cid_mask; + else + data &= ~((uint32_t)cid_mask); + msm_camera_io_w_mb(data, ispif->base + intf_addr); +} + +static int msm_ispif_validate_intf_status(struct ispif_device *ispif, + uint8_t intftype, uint8_t vfe_intf) +{ + int rc = 0; + uint32_t data = 0; + + if (WARN_ON((!ispif))) + return -ENODEV; + + if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) { + pr_err("%s: invalid interface type\n", __func__); + return -EINVAL; + } + + switch (intftype) { + case PIX0: + data = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 0)); + break; + case RDI0: + data = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 0)); + break; + case PIX1: + data = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1)); + break; + case RDI1: + data = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 1)); + break; + case RDI2: + data = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 2)); + break; + } + if ((data & 0xf) != 0xf) + rc = -EBUSY; + return rc; +} + +static void msm_ispif_select_clk_mux(struct ispif_device *ispif, + uint8_t intftype, uint8_t csid, uint8_t vfe_intf) +{ + uint32_t data = 0; + + switch (intftype) { + case PIX0: + data = msm_camera_io_r(ispif->clk_mux_base); + data &= ~(0xf << (vfe_intf * 8)); + data |= (csid << (vfe_intf * 8)); + msm_camera_io_w(data, ispif->clk_mux_base); + break; + + case RDI0: + data = msm_camera_io_r(ispif->clk_mux_base + + ISPIF_RDI_CLK_MUX_SEL_ADDR); + data &= ~(0xf << (vfe_intf * 12)); + data |= (csid << (vfe_intf * 12)); + msm_camera_io_w(data, ispif->clk_mux_base + + ISPIF_RDI_CLK_MUX_SEL_ADDR); + break; + + case PIX1: + data = msm_camera_io_r(ispif->clk_mux_base); + data &= ~(0xf0 << (vfe_intf * 8)); + data |= (csid << (4 + (vfe_intf * 8))); + msm_camera_io_w(data, ispif->clk_mux_base); + break; + + case RDI1: + data = msm_camera_io_r(ispif->clk_mux_base + + ISPIF_RDI_CLK_MUX_SEL_ADDR); + data &= ~(0xf << (4 + (vfe_intf * 12))); + data |= (csid << (4 + (vfe_intf * 12))); + msm_camera_io_w(data, ispif->clk_mux_base + + ISPIF_RDI_CLK_MUX_SEL_ADDR); + break; + + case RDI2: + data = msm_camera_io_r(ispif->clk_mux_base + + ISPIF_RDI_CLK_MUX_SEL_ADDR); + data &= ~(0xf << (8 + (vfe_intf * 12))); + data |= (csid << (8 + (vfe_intf * 12))); + msm_camera_io_w(data, ispif->clk_mux_base + + ISPIF_RDI_CLK_MUX_SEL_ADDR); + break; + } + CDBG("%s intftype %d data %x\n", __func__, intftype, data); + /* ensure clk mux is enabled */ + mb(); +} + +static uint16_t msm_ispif_get_cids_mask_from_cfg( + struct msm_ispif_params_entry *entry) +{ + int i; + uint16_t cids_mask = 0; + + if (WARN_ON(!entry)) { + pr_err("%s: invalid entry", __func__); + return cids_mask; + } + + for (i = 0; i < entry->num_cids && i < MAX_CID_CH_PARAM_ENTRY; i++) + cids_mask |= (1 << entry->cids[i]); + + return cids_mask; +} + +static int msm_ispif_config(struct ispif_device *ispif, + struct msm_ispif_param_data *params) +{ + int rc = 0, i = 0; + uint16_t cid_mask; + enum msm_ispif_intftype intftype; + enum msm_ispif_vfe_intf vfe_intf; + + if (WARN_ON(!ispif) || WARN_ON(!params)) + return -EINVAL; + + if (ispif->ispif_state != ISPIF_POWER_UP) { + pr_err("%s: ispif invalid state %d\n", __func__, + ispif->ispif_state); + rc = -EPERM; + return rc; + } + if (params->num > MAX_PARAM_ENTRIES) { + pr_err("%s: invalid param entries %d\n", __func__, + params->num); + rc = -EINVAL; + return rc; + } + + for (i = 0; i < params->num; i++) { + vfe_intf = params->entries[i].vfe_intf; + if (vfe_intf >= VFE_MAX) { + pr_err("%s: %d invalid i %d vfe_intf %d\n", __func__, + __LINE__, i, vfe_intf); + return -EINVAL; + } + if (!msm_ispif_is_intf_valid(ispif->csid_version, + vfe_intf)) { + pr_err("%s: invalid interface type\n", __func__); + return -EINVAL; + } + msm_camera_io_w(0x0, ispif->base + + ISPIF_VFE_m_IRQ_MASK_0(vfe_intf)); + msm_camera_io_w(0x0, ispif->base + + ISPIF_VFE_m_IRQ_MASK_1(vfe_intf)); + msm_camera_io_w_mb(0x0, ispif->base + + ISPIF_VFE_m_IRQ_MASK_2(vfe_intf)); + } + + for (i = 0; i < params->num; i++) { + intftype = params->entries[i].intftype; + + vfe_intf = params->entries[i].vfe_intf; + + CDBG("%s intftype %x, vfe_intf %d, csid %d\n", __func__, + intftype, vfe_intf, params->entries[i].csid); + + if ((intftype >= INTF_MAX) || + (vfe_intf >= ispif->vfe_info.num_vfe) || + (ispif->csid_version <= CSID_VERSION_V22 && + (vfe_intf > VFE0))) { + pr_err("%s: VFEID %d and CSID version %d mismatch\n", + __func__, vfe_intf, ispif->csid_version); + return -EINVAL; + } + + if (ispif->csid_version >= CSID_VERSION_V30) + msm_ispif_select_clk_mux(ispif, intftype, + params->entries[i].csid, vfe_intf); + + rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf); + if (rc) { + pr_err("%s:validate_intf_status failed, rc = %d\n", + __func__, rc); + return rc; + } + + msm_ispif_sel_csid_core(ispif, intftype, + params->entries[i].csid, vfe_intf); + cid_mask = msm_ispif_get_cids_mask_from_cfg( + ¶ms->entries[i]); + msm_ispif_enable_intf_cids(ispif, intftype, + cid_mask, vfe_intf, 1); + if (params->entries[i].crop_enable) + msm_ispif_enable_crop(ispif, intftype, vfe_intf, + params->entries[i].crop_start_pixel, + params->entries[i].crop_end_pixel); + } + + for (vfe_intf = 0; vfe_intf < 2; vfe_intf++) { + msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base + + ISPIF_VFE_m_IRQ_MASK_0(vfe_intf)); + + msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base + + ISPIF_VFE_m_IRQ_CLEAR_0(vfe_intf)); + + msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base + + ISPIF_VFE_m_IRQ_MASK_1(vfe_intf)); + + msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base + + ISPIF_VFE_m_IRQ_CLEAR_1(vfe_intf)); + + msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base + + ISPIF_VFE_m_IRQ_MASK_2(vfe_intf)); + + msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base + + ISPIF_VFE_m_IRQ_CLEAR_2(vfe_intf)); + } + + msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base + + ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR); + + return rc; +} + +static int msm_ispif_intf_cmd(struct ispif_device *ispif, uint32_t cmd_bits, + struct msm_ispif_param_data *params) +{ + uint8_t vc; + int i, k; + enum msm_ispif_intftype intf_type; + enum msm_ispif_cid cid; + enum msm_ispif_vfe_intf vfe_intf; + + if (WARN_ON(!ispif) || WARN_ON(!params)) + return -EINVAL; + + for (i = 0; i < params->num; i++) { + vfe_intf = params->entries[i].vfe_intf; + if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) { + pr_err("%s: invalid interface type\n", __func__); + return -EINVAL; + } + if (params->entries[i].num_cids > MAX_CID_CH_PARAM_ENTRY) { + pr_err("%s: out of range of cid_num %d\n", + __func__, params->entries[i].num_cids); + return -EINVAL; + } + } + + for (i = 0; i < params->num; i++) { + intf_type = params->entries[i].intftype; + vfe_intf = params->entries[i].vfe_intf; + for (k = 0; k < params->entries[i].num_cids; k++) { + cid = params->entries[i].cids[k]; + vc = cid / 4; + if (intf_type == RDI2) { + /* zero out two bits */ + ispif->applied_intf_cmd[vfe_intf].intf_cmd1 &= + ~(0x3 << (vc * 2 + 8)); + /* set cmd bits */ + ispif->applied_intf_cmd[vfe_intf].intf_cmd1 |= + (cmd_bits << (vc * 2 + 8)); + } else { + /* zero 2 bits */ + ispif->applied_intf_cmd[vfe_intf].intf_cmd &= + ~(0x3 << (vc * 2 + intf_type * 8)); + /* set cmd bits */ + ispif->applied_intf_cmd[vfe_intf].intf_cmd |= + (cmd_bits << (vc * 2 + intf_type * 8)); + } + } + /* cmd for PIX0, PIX1, RDI0, RDI1 */ + if (ispif->applied_intf_cmd[vfe_intf].intf_cmd != 0xFFFFFFFF) + msm_camera_io_w_mb( + ispif->applied_intf_cmd[vfe_intf].intf_cmd, + ispif->base + ISPIF_VFE_m_INTF_CMD_0(vfe_intf)); + + /* cmd for RDI2 */ + if (ispif->applied_intf_cmd[vfe_intf].intf_cmd1 != 0xFFFFFFFF) + msm_camera_io_w_mb( + ispif->applied_intf_cmd[vfe_intf].intf_cmd1, + ispif->base + ISPIF_VFE_m_INTF_CMD_1(vfe_intf)); + } + return 0; +} + +static int msm_ispif_stop_immediately(struct ispif_device *ispif, + struct msm_ispif_param_data *params) +{ + int i, rc = 0; + uint16_t cid_mask = 0; + + if (WARN_ON(!ispif) || WARN_ON(!params)) + return -EINVAL; + + if (ispif->ispif_state != ISPIF_POWER_UP) { + pr_err("%s: ispif invalid state %d\n", __func__, + ispif->ispif_state); + rc = -EPERM; + return rc; + } + + if (params->num > MAX_PARAM_ENTRIES) { + pr_err("%s: invalid param entries %d\n", __func__, + params->num); + rc = -EINVAL; + return rc; + } + msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_DISABLE_IMMEDIATELY, params); + + /* after stop the interface we need to unmask the CID enable bits */ + for (i = 0; i < params->num; i++) { + cid_mask = msm_ispif_get_cids_mask_from_cfg( + ¶ms->entries[i]); + msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype, + cid_mask, params->entries[i].vfe_intf, 0); + } + + return rc; +} + +static int msm_ispif_start_frame_boundary(struct ispif_device *ispif, + struct msm_ispif_param_data *params) +{ + int rc = 0; + + if (ispif->ispif_state != ISPIF_POWER_UP) { + pr_err("%s: ispif invalid state %d\n", __func__, + ispif->ispif_state); + rc = -EPERM; + return rc; + } + if (params->num > MAX_PARAM_ENTRIES) { + pr_err("%s: invalid param entries %d\n", __func__, + params->num); + rc = -EINVAL; + return rc; + } + + msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params); + + return rc; +} + +static int msm_ispif_restart_frame_boundary(struct ispif_device *ispif, + struct msm_ispif_param_data *params) +{ + int rc = 0, i; + long timeout = 0; + uint16_t cid_mask; + enum msm_ispif_intftype intftype; + enum msm_ispif_vfe_intf vfe_intf; + uint32_t vfe_mask = 0; + uint32_t intf_addr; + struct clk *reset_clk[ARRAY_SIZE(ispif_8974_reset_clk_info)]; + struct clk *reset_clk1[ARRAY_SIZE(ispif_8626_reset_clk_info)]; + + ispif->clk_idx = 0; + + if (ispif->ispif_state != ISPIF_POWER_UP) { + pr_err("%s: ispif invalid state %d\n", __func__, + ispif->ispif_state); + rc = -EPERM; + return rc; + } + if (params->num > MAX_PARAM_ENTRIES) { + pr_err("%s: invalid param entries %d\n", __func__, + params->num); + rc = -EINVAL; + return rc; + } + + for (i = 0; i < params->num; i++) { + vfe_intf = params->entries[i].vfe_intf; + if (vfe_intf >= VFE_MAX) { + pr_err("%s: %d invalid i %d vfe_intf %d\n", __func__, + __LINE__, i, vfe_intf); + return -EINVAL; + } + vfe_mask |= (1 << vfe_intf); + } + + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8974_reset_clk_info, reset_clk, + ARRAY_SIZE(ispif_8974_reset_clk_info), 1); + if (rc < 0) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8626_reset_clk_info, reset_clk1, + ARRAY_SIZE(ispif_8626_reset_clk_info), 1); + if (rc < 0) { + pr_err("%s: cannot enable clock, error = %d", + __func__, rc); + } else { + /* This is set when device is 8x26 */ + ispif->clk_idx = 2; + } + } else { + /* This is set when device is 8974 */ + ispif->clk_idx = 1; + } + + if (vfe_mask & (1 << VFE0)) { + init_completion(&ispif->reset_complete[VFE0]); + pr_err("%s Init completion VFE0\n", __func__); + /* initiate reset of ISPIF */ + msm_camera_io_w(0x00001FF9, + ispif->base + ISPIF_RST_CMD_ADDR); + } + if (ispif->hw_num_isps > 1 && (vfe_mask & (1 << VFE1))) { + init_completion(&ispif->reset_complete[VFE1]); + pr_err("%s Init completion VFE1\n", __func__); + msm_camera_io_w(0x00001FF9, + ispif->base + ISPIF_RST_CMD_1_ADDR); + } + + if (vfe_mask & (1 << VFE0)) { + timeout = wait_for_completion_timeout( + &ispif->reset_complete[VFE0], msecs_to_jiffies(500)); + if (timeout <= 0) { + pr_err("%s: VFE0 reset wait timeout\n", __func__); + rc = -ETIMEDOUT; + goto disable_clk; + } + } + + if (ispif->hw_num_isps > 1 && (vfe_mask & (1 << VFE1))) { + timeout = wait_for_completion_timeout( + &ispif->reset_complete[VFE1], + msecs_to_jiffies(500)); + if (timeout <= 0) { + pr_err("%s: VFE1 reset wait timeout\n", __func__); + rc = -ETIMEDOUT; + goto disable_clk; + } + } + + pr_info("%s: ISPIF reset hw done", __func__); + + if (ispif->clk_idx == 1) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8974_reset_clk_info, reset_clk, + ARRAY_SIZE(ispif_8974_reset_clk_info), 0); + if (rc < 0) { + pr_err("%s: cannot disable clock, error = %d", + __func__, rc); + goto end; + } + } + + if (ispif->clk_idx == 2) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8626_reset_clk_info, reset_clk1, + ARRAY_SIZE(ispif_8626_reset_clk_info), 0); + if (rc < 0) { + pr_err("%s: cannot disable clock, error = %d", + __func__, rc); + goto end; + } + } + + for (i = 0; i < params->num; i++) { + intftype = params->entries[i].intftype; + vfe_intf = params->entries[i].vfe_intf; + + switch (params->entries[0].intftype) { + case PIX0: + intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 0); + break; + case RDI0: + intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 0); + break; + case PIX1: + intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1); + break; + case RDI1: + intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 1); + break; + case RDI2: + intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 2); + break; + default: + pr_err("%s: invalid intftype=%d\n", __func__, + params->entries[i].intftype); + rc = -EPERM; + goto end; + } + + msm_ispif_intf_cmd(ispif, + ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params); + } + + for (i = 0; i < params->num; i++) { + intftype = params->entries[i].intftype; + + vfe_intf = params->entries[i].vfe_intf; + + + cid_mask = msm_ispif_get_cids_mask_from_cfg( + ¶ms->entries[i]); + + msm_ispif_enable_intf_cids(ispif, intftype, + cid_mask, vfe_intf, 1); + } + +end: + return rc; +disable_clk: + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8974_reset_clk_info, reset_clk, + ARRAY_SIZE(ispif_8974_reset_clk_info), 0); + if (rc < 0) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, + ispif_8626_reset_clk_info, reset_clk1, + ARRAY_SIZE(ispif_8626_reset_clk_info), 0); + if (rc < 0) + pr_err("%s: cannot enable clock, error = %d", + __func__, rc); + } + + return -ETIMEDOUT; +} + +static int msm_ispif_stop_frame_boundary(struct ispif_device *ispif, + struct msm_ispif_param_data *params) +{ + int i, rc = 0; + uint16_t cid_mask = 0; + uint32_t intf_addr; + enum msm_ispif_vfe_intf vfe_intf; + uint32_t stop_flag = 0; + + if (WARN_ON(!ispif) || WARN_ON(!params)) + return -EINVAL; + + + if (ispif->ispif_state != ISPIF_POWER_UP) { + pr_err("%s: ispif invalid state %d\n", __func__, + ispif->ispif_state); + rc = -EPERM; + return rc; + } + + if (params->num > MAX_PARAM_ENTRIES) { + pr_err("%s: invalid param entries %d\n", __func__, + params->num); + rc = -EINVAL; + return rc; + } + + for (i = 0; i < params->num; i++) { + if (!msm_ispif_is_intf_valid(ispif->csid_version, + params->entries[i].vfe_intf)) { + pr_err("%s: invalid interface type\n", __func__); + rc = -EINVAL; + goto end; + } + } + + msm_ispif_intf_cmd(ispif, + ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY, params); + + for (i = 0; i < params->num; i++) { + cid_mask = + msm_ispif_get_cids_mask_from_cfg(¶ms->entries[i]); + vfe_intf = params->entries[i].vfe_intf; + + switch (params->entries[i].intftype) { + case PIX0: + intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 0); + break; + case RDI0: + intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 0); + break; + case PIX1: + intf_addr = ISPIF_VFE_m_PIX_INTF_n_STATUS(vfe_intf, 1); + break; + case RDI1: + intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 1); + break; + case RDI2: + intf_addr = ISPIF_VFE_m_RDI_INTF_n_STATUS(vfe_intf, 2); + break; + default: + pr_err("%s: invalid intftype=%d\n", __func__, + params->entries[i].intftype); + rc = -EPERM; + goto end; + } + + rc = readl_poll_timeout(ispif->base + intf_addr, stop_flag, + (stop_flag & 0xF) == 0xF, + ISPIF_TIMEOUT_SLEEP_US, + ISPIF_TIMEOUT_ALL_US); + if (rc < 0) + goto end; + + /* disable CIDs in CID_MASK register */ + msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype, + cid_mask, vfe_intf, 0); + } + +end: + return rc; +} + +static void ispif_process_irq(struct ispif_device *ispif, + struct ispif_irq_status *out, enum msm_ispif_vfe_intf vfe_id) +{ + if (WARN_ON(!ispif) || WARN_ON(!out)) + return; + + if (out[vfe_id].ispifIrqStatus0 & + ISPIF_IRQ_STATUS_PIX_SOF_MASK) { + if (ispif->ispif_sof_debug < 5) + pr_err("%s: PIX0 frame id: %u\n", __func__, + ispif->sof_count[vfe_id].sof_cnt[PIX0]); + ispif->sof_count[vfe_id].sof_cnt[PIX0]++; + ispif->ispif_sof_debug++; + } + if (out[vfe_id].ispifIrqStatus0 & + ISPIF_IRQ_STATUS_RDI0_SOF_MASK) { + ispif->sof_count[vfe_id].sof_cnt[RDI0]++; + } + if (out[vfe_id].ispifIrqStatus1 & + ISPIF_IRQ_STATUS_RDI1_SOF_MASK) { + ispif->sof_count[vfe_id].sof_cnt[RDI1]++; + } + if (out[vfe_id].ispifIrqStatus2 & + ISPIF_IRQ_STATUS_RDI2_SOF_MASK) { + ispif->sof_count[vfe_id].sof_cnt[RDI2]++; + } +} + +static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out, + void *data) +{ + struct ispif_device *ispif = (struct ispif_device *)data; + + if (WARN_ON(!ispif) || WARN_ON(!out)) + return; + + out[VFE0].ispifIrqStatus0 = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_IRQ_STATUS_0(VFE0)); + msm_camera_io_w(out[VFE0].ispifIrqStatus0, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(VFE0)); + + out[VFE0].ispifIrqStatus1 = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_IRQ_STATUS_1(VFE0)); + msm_camera_io_w(out[VFE0].ispifIrqStatus1, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(VFE0)); + + out[VFE0].ispifIrqStatus2 = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_IRQ_STATUS_2(VFE0)); + msm_camera_io_w_mb(out[VFE0].ispifIrqStatus2, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(VFE0)); + + if (ispif->vfe_info.num_vfe > 1) { + out[VFE1].ispifIrqStatus0 = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_IRQ_STATUS_0(VFE1)); + msm_camera_io_w(out[VFE1].ispifIrqStatus0, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(VFE1)); + + out[VFE1].ispifIrqStatus1 = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_IRQ_STATUS_1(VFE1)); + msm_camera_io_w(out[VFE1].ispifIrqStatus1, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(VFE1)); + + out[VFE1].ispifIrqStatus2 = msm_camera_io_r(ispif->base + + ISPIF_VFE_m_IRQ_STATUS_2(VFE1)); + msm_camera_io_w_mb(out[VFE1].ispifIrqStatus2, + ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(VFE1)); + } + msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base + + ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR); + + if (out[VFE0].ispifIrqStatus0 & ISPIF_IRQ_STATUS_MASK) { + if (out[VFE0].ispifIrqStatus0 & RESET_DONE_IRQ) + complete(&ispif->reset_complete[VFE0]); + + if (out[VFE0].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) + pr_err("%s: VFE0 pix0 overflow.\n", __func__); + + if (out[VFE0].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ) + pr_err("%s: VFE0 rdi0 overflow.\n", __func__); + + if (out[VFE0].ispifIrqStatus1 & RAW_INTF_1_OVERFLOW_IRQ) + pr_err("%s: VFE0 rdi1 overflow.\n", __func__); + + if (out[VFE0].ispifIrqStatus2 & RAW_INTF_2_OVERFLOW_IRQ) + pr_err("%s: VFE0 rdi2 overflow.\n", __func__); + + ispif_process_irq(ispif, out, VFE0); + } + if (ispif->hw_num_isps > 1) { + if (out[VFE1].ispifIrqStatus0 & RESET_DONE_IRQ) + complete(&ispif->reset_complete[VFE1]); + + if (out[VFE1].ispifIrqStatus0 & PIX_INTF_0_OVERFLOW_IRQ) + pr_err("%s: VFE1 pix0 overflow.\n", __func__); + + if (out[VFE1].ispifIrqStatus0 & RAW_INTF_0_OVERFLOW_IRQ) + pr_err("%s: VFE1 rdi0 overflow.\n", __func__); + + if (out[VFE1].ispifIrqStatus1 & RAW_INTF_1_OVERFLOW_IRQ) + pr_err("%s: VFE1 rdi1 overflow.\n", __func__); + + if (out[VFE1].ispifIrqStatus2 & RAW_INTF_2_OVERFLOW_IRQ) + pr_err("%s: VFE1 rdi2 overflow.\n", __func__); + + ispif_process_irq(ispif, out, VFE1); + } +} + +static irqreturn_t msm_io_ispif_irq(int irq_num, void *data) +{ + struct ispif_irq_status irq[VFE_MAX]; + + msm_ispif_read_irq_status(irq, data); + return IRQ_HANDLED; +} + +static int msm_ispif_set_vfe_info(struct ispif_device *ispif, + struct msm_ispif_vfe_info *vfe_info) +{ + if (!vfe_info || (vfe_info->num_vfe <= 0) || + ((uint32_t)(vfe_info->num_vfe) > ispif->hw_num_isps)) { + pr_err("Invalid VFE info: %pK %d\n", vfe_info, + (vfe_info ? vfe_info->num_vfe:0)); + return -EINVAL; + } + + memcpy(&ispif->vfe_info, vfe_info, sizeof(struct msm_ispif_vfe_info)); + + return 0; +} + +static int msm_ispif_init(struct ispif_device *ispif, + uint32_t csid_version) +{ + int rc = 0; + + if (WARN_ON(!ispif)) { + pr_err("%s: invalid ispif params", __func__); + return -EINVAL; + } + + if (ispif->ispif_state == ISPIF_POWER_UP) { + pr_err("%s: ispif already initted state = %d\n", __func__, + ispif->ispif_state); + rc = -EPERM; + return rc; + } + + /* can we set to zero? */ + ispif->applied_intf_cmd[VFE0].intf_cmd = 0xFFFFFFFF; + ispif->applied_intf_cmd[VFE0].intf_cmd1 = 0xFFFFFFFF; + ispif->applied_intf_cmd[VFE1].intf_cmd = 0xFFFFFFFF; + ispif->applied_intf_cmd[VFE1].intf_cmd1 = 0xFFFFFFFF; + memset(ispif->sof_count, 0, sizeof(ispif->sof_count)); + + ispif->csid_version = csid_version; + + if (ispif->csid_version >= CSID_VERSION_V30) { + if (!ispif->clk_mux_mem || !ispif->clk_mux_io) { + pr_err("%s csi clk mux mem %pK io %pK\n", __func__, + ispif->clk_mux_mem, ispif->clk_mux_io); + rc = -ENOMEM; + return rc; + } + ispif->clk_mux_base = ioremap(ispif->clk_mux_mem->start, + resource_size(ispif->clk_mux_mem)); + if (!ispif->clk_mux_base) { + pr_err("%s: clk_mux_mem ioremap failed\n", __func__); + rc = -ENOMEM; + return rc; + } + } + + ispif->base = ioremap(ispif->mem->start, + resource_size(ispif->mem)); + if (!ispif->base) { + rc = -ENOMEM; + pr_err("%s: nomem\n", __func__); + goto end; + } + rc = request_irq(ispif->irq->start, msm_io_ispif_irq, + IRQF_TRIGGER_RISING, "ispif", ispif); + if (rc) { + pr_err("%s: request_irq error = %d\n", __func__, rc); + goto error_irq; + } + + rc = msm_ispif_clk_ahb_enable(ispif, 1); + if (rc) { + pr_err("%s: ahb_clk enable failed", __func__); + goto error_ahb; + } + + msm_ispif_reset_hw(ispif); + + rc = msm_ispif_reset(ispif); + if (rc == 0) { + ispif->ispif_state = ISPIF_POWER_UP; + CDBG("%s: power up done\n", __func__); + goto end; + } + +error_ahb: + free_irq(ispif->irq->start, ispif); +error_irq: + iounmap(ispif->base); + +end: + return rc; +} + +static void msm_ispif_release(struct ispif_device *ispif) +{ + if (WARN_ON(!ispif)) { + pr_err("%s: invalid ispif params", __func__); + return; + } + + if (!ispif->base) { + pr_err("%s: ispif base is NULL\n", __func__); + return; + } + + if (ispif->ispif_state != ISPIF_POWER_UP) { + pr_err("%s: ispif invalid state %d\n", __func__, + ispif->ispif_state); + return; + } + + /* make sure no streaming going on */ + msm_ispif_reset(ispif); + + msm_ispif_clk_ahb_enable(ispif, 0); + + free_irq(ispif->irq->start, ispif); + + iounmap(ispif->base); + + iounmap(ispif->clk_mux_base); + + ispif->ispif_state = ISPIF_POWER_DOWN; +} + +static long msm_ispif_cmd(struct v4l2_subdev *sd, void *arg) +{ + long rc = 0; + struct ispif_cfg_data *pcdata = (struct ispif_cfg_data *)arg; + struct ispif_device *ispif = + (struct ispif_device *)v4l2_get_subdevdata(sd); + + if (WARN_ON(!sd) || WARN_ON(!pcdata)) + return -EINVAL; + + mutex_lock(&ispif->mutex); + switch (pcdata->cfg_type) { + case ISPIF_ENABLE_REG_DUMP: + ispif->enb_dump_reg = pcdata->reg_dump; /* save dump config */ + break; + case ISPIF_INIT: + rc = msm_ispif_init(ispif, pcdata->csid_version); + msm_ispif_io_dump_reg(ispif); + break; + case ISPIF_CFG: + rc = msm_ispif_config(ispif, &pcdata->params); + msm_ispif_io_dump_reg(ispif); + break; + case ISPIF_START_FRAME_BOUNDARY: + rc = msm_ispif_start_frame_boundary(ispif, &pcdata->params); + msm_ispif_io_dump_reg(ispif); + break; + case ISPIF_RESTART_FRAME_BOUNDARY: + rc = msm_ispif_restart_frame_boundary(ispif, &pcdata->params); + msm_ispif_io_dump_reg(ispif); + break; + + case ISPIF_STOP_FRAME_BOUNDARY: + rc = msm_ispif_stop_frame_boundary(ispif, &pcdata->params); + msm_ispif_io_dump_reg(ispif); + break; + case ISPIF_STOP_IMMEDIATELY: + rc = msm_ispif_stop_immediately(ispif, &pcdata->params); + msm_ispif_io_dump_reg(ispif); + break; + case ISPIF_RELEASE: + msm_ispif_release(ispif); + break; + case ISPIF_SET_VFE_INFO: + rc = msm_ispif_set_vfe_info(ispif, &pcdata->vfe_info); + break; + default: + pr_err("%s: invalid cfg_type\n", __func__); + rc = -EINVAL; + break; + } + mutex_unlock(&ispif->mutex); + return rc; +} +static struct v4l2_file_operations msm_ispif_v4l2_subdev_fops; + +static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct ispif_device *ispif = + (struct ispif_device *)v4l2_get_subdevdata(sd); + + switch (cmd) { + case VIDIOC_MSM_ISPIF_CFG: + return msm_ispif_cmd(sd, arg); + case MSM_SD_NOTIFY_FREEZE: { + ispif->ispif_sof_debug = 0; + return 0; + } + case MSM_SD_SHUTDOWN: { + struct ispif_device *ispif = + (struct ispif_device *)v4l2_get_subdevdata(sd); + if (ispif && ispif->base) { + mutex_lock(&ispif->mutex); + msm_ispif_release(ispif); + mutex_unlock(&ispif->mutex); + } + return 0; + } + default: + pr_err_ratelimited("%s: invalid cmd 0x%x received\n", + __func__, cmd); + return -ENOIOCTLCMD; + } +} + +static long msm_ispif_subdev_do_ioctl( + struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); + + return msm_ispif_subdev_ioctl(sd, cmd, arg); +} + +static long msm_ispif_subdev_fops_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return video_usercopy(file, cmd, arg, msm_ispif_subdev_do_ioctl); +} + +static int ispif_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ispif_device *ispif = v4l2_get_subdevdata(sd); + + mutex_lock(&ispif->mutex); + /* mem remap is done in init when the clock is on */ + ispif->open_cnt++; + mutex_unlock(&ispif->mutex); + return 0; +} + +static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int rc = 0; + struct ispif_device *ispif = v4l2_get_subdevdata(sd); + + if (!ispif) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + mutex_lock(&ispif->mutex); + if (ispif->open_cnt == 0) { + pr_err("%s: Invalid close\n", __func__); + rc = -ENODEV; + goto end; + } + ispif->open_cnt--; + if (ispif->open_cnt == 0) + msm_ispif_release(ispif); +end: + mutex_unlock(&ispif->mutex); + return rc; +} + +static struct v4l2_subdev_core_ops msm_ispif_subdev_core_ops = { + /* .g_chip_ident = &msm_ispif_subdev_g_chip_ident, */ + .ioctl = &msm_ispif_subdev_ioctl, +}; + +static const struct v4l2_subdev_ops msm_ispif_subdev_ops = { + .core = &msm_ispif_subdev_core_ops, +}; + +static const struct v4l2_subdev_internal_ops msm_ispif_internal_ops = { + .open = ispif_open_node, + .close = ispif_close_node, +}; + +static int ispif_probe(struct platform_device *pdev) +{ + int rc; + struct ispif_device *ispif; + + ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL); + if (!ispif) + return -ENOMEM; + + if (pdev->dev.of_node) { + of_property_read_u32((&pdev->dev)->of_node, + "cell-index", &pdev->id); + rc = of_property_read_u32((&pdev->dev)->of_node, + "qcom,num-isps", &ispif->hw_num_isps); + if (rc) + /* backward compatibility */ + ispif->hw_num_isps = 1; + /* not an error condition */ + rc = 0; + } + + mutex_init(&ispif->mutex); + ispif->mem = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "ispif"); + if (!ispif->mem) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto error; + } + ispif->irq = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, "ispif"); + if (!ispif->irq) { + pr_err("%s: no irq resource?\n", __func__); + rc = -ENODEV; + goto error; + } + ispif->io = request_mem_region(ispif->mem->start, + resource_size(ispif->mem), pdev->name); + if (!ispif->io) { + pr_err("%s: no valid mem region\n", __func__); + rc = -EBUSY; + goto error; + } + ispif->clk_mux_mem = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "csi_clk_mux"); + if (ispif->clk_mux_mem) { + ispif->clk_mux_io = request_mem_region( + ispif->clk_mux_mem->start, + resource_size(ispif->clk_mux_mem), + ispif->clk_mux_mem->name); + if (!ispif->clk_mux_io) + pr_err("%s: no valid csi_mux region\n", __func__); + } + + v4l2_subdev_init(&ispif->msm_sd.sd, &msm_ispif_subdev_ops); + ispif->msm_sd.sd.internal_ops = &msm_ispif_internal_ops; + ispif->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + snprintf(ispif->msm_sd.sd.name, + ARRAY_SIZE(ispif->msm_sd.sd.name), MSM_ISPIF_DRV_NAME); + v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif); + + platform_set_drvdata(pdev, &ispif->msm_sd.sd); + + media_entity_pads_init(&ispif->msm_sd.sd.entity, 0, NULL); + ispif->msm_sd.sd.entity.function = MSM_CAMERA_SUBDEV_ISPIF; + ispif->msm_sd.sd.entity.name = pdev->name; + ispif->msm_sd.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x1; + rc = msm_sd_register(&ispif->msm_sd); + if (rc) { + pr_err("%s: msm_sd_register error = %d\n", __func__, rc); + goto error; + } + msm_ispif_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner; + msm_ispif_v4l2_subdev_fops.open = v4l2_subdev_fops.open; + msm_ispif_v4l2_subdev_fops.unlocked_ioctl = msm_ispif_subdev_fops_ioctl; + msm_ispif_v4l2_subdev_fops.release = v4l2_subdev_fops.release; + msm_ispif_v4l2_subdev_fops.poll = v4l2_subdev_fops.poll; +#ifdef CONFIG_COMPAT + msm_ispif_v4l2_subdev_fops.compat_ioctl32 = msm_ispif_subdev_fops_ioctl; +#endif + ispif->msm_sd.sd.devnode->fops = &msm_ispif_v4l2_subdev_fops; + ispif->pdev = pdev; + ispif->ispif_state = ISPIF_POWER_DOWN; + ispif->open_cnt = 0; + return 0; + +error: + mutex_destroy(&ispif->mutex); + kfree(ispif); + return rc; +} + +static const struct of_device_id msm_ispif_dt_match[] = { + {.compatible = "qcom,ispif"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_ispif_dt_match); + +static struct platform_driver ispif_driver = { + .probe = ispif_probe, + .driver = { + .name = MSM_ISPIF_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = msm_ispif_dt_match, + }, +}; + +static int __init msm_ispif_init_module(void) +{ + return platform_driver_register(&ispif_driver); +} + +static void __exit msm_ispif_exit_module(void) +{ + platform_driver_unregister(&ispif_driver); +} + +module_init(msm_ispif_init_module); +module_exit(msm_ispif_exit_module); +MODULE_DESCRIPTION("MSM ISP Interface driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.h b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.h new file mode 100644 index 000000000000..6217fbaa18f6 --- /dev/null +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif_32.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef MSM_ISPIF_H +#define MSM_ISPIF_H + +#include +#include +#include +#include +#include "msm_sd.h" + +#define ISPIF_CLK_INFO_MAX 24 + +struct ispif_irq_status { + uint32_t ispifIrqStatus0; + uint32_t ispifIrqStatus1; + uint32_t ispifIrqStatus2; +}; + +enum msm_ispif_state_t { + ISPIF_POWER_UP, + ISPIF_POWER_DOWN, +}; +struct ispif_sof_count { + uint32_t sof_cnt[INTF_MAX]; +}; + +struct ispif_intf_cmd { + uint32_t intf_cmd; + uint32_t intf_cmd1; +}; + +struct ispif_device { + struct platform_device *pdev; + struct msm_sd_subdev msm_sd; + struct resource *mem; + struct resource *clk_mux_mem; + struct resource *irq; + struct resource *io; + struct resource *clk_mux_io; + void __iomem *base; + void __iomem *clk_mux_base; + struct mutex mutex; + uint8_t start_ack_pending; + uint32_t csid_version; + int enb_dump_reg; + uint32_t open_cnt; + struct ispif_sof_count sof_count[VFE_MAX]; + struct ispif_intf_cmd applied_intf_cmd[VFE_MAX]; + enum msm_ispif_state_t ispif_state; + struct msm_ispif_vfe_info vfe_info; + struct clk *ahb_clk[ISPIF_CLK_INFO_MAX]; + struct completion reset_complete[VFE_MAX]; + uint32_t hw_num_isps; + uint32_t num_ahb_clk; + uint32_t clk_idx; + uint32_t ispif_sof_debug; +}; +#endif diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index 625a0dbeb80f..c045eda41385 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -2077,14 +2077,8 @@ static struct platform_driver msm_actuator_platform_driver = { static int __init msm_actuator_init_module(void) { - int32_t rc = 0; - CDBG("Enter\n"); - rc = platform_driver_register(&msm_actuator_platform_driver); - if (!rc) - return rc; - - CDBG("%s:%d rc %d\n", __func__, __LINE__, rc); + platform_driver_register(&msm_actuator_platform_driver); return i2c_add_driver(&msm_actuator_i2c_driver); } diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c index dfcb73aa5300..d5e79897f73c 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c @@ -323,8 +323,7 @@ static int msm_csid_config(struct csid_device *csid_dev, if (!msm_csid_find_max_clk_rate(csid_dev)) pr_err("msm_csid_find_max_clk_rate failed\n"); - clk_rate = (csid_params->csi_clk > 0) ? - (csid_params->csi_clk) : csid_dev->csid_max_clk; + clk_rate = csid_dev->csid_max_clk; clk_rate = msm_camera_clk_set_rate(&csid_dev->pdev->dev, csid_dev->csid_clk[csid_dev->csid_clk_index], clk_rate); diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c index fa3aceefd32b..5f5667675337 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c @@ -29,7 +29,6 @@ #include "include/msm_csiphy_5_0_hwreg.h" #include "include/msm_csiphy_5_0_1_hwreg.h" #include "include/msm_csiphy_10_0_0_hwreg.h" - #include "cam_hw_ops.h" #define DBG_CSIPHY 0 @@ -1248,9 +1247,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev, return rc; } - clk_rate = (csiphy_params->csiphy_clk > 0) - ? csiphy_params->csiphy_clk : - csiphy_dev->csiphy_max_clk; + clk_rate = csiphy_dev->csiphy_max_clk; clk_rate = msm_camera_clk_set_rate(&csiphy_dev->pdev->dev, csiphy_dev->csiphy_clk[csiphy_dev->csiphy_clk_index], clk_rate); diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c index df22d843e651..3590e15e5d52 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c @@ -20,7 +20,6 @@ #include "msm_sensor.h" #undef CDBG -#define MSM_CAMERA_TZ_I2C_VERBOSE #ifdef CONFIG_MSM_SEC_CCI_DEBUG #define TZ_I2C_FN_RETURN(ret, i2c_fn, ...) \ diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c index 0b0f98a84786..f80de3a0e569 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -10,7 +10,7 @@ * GNU General Public License for more details. */ -#define SENSOR_DRIVER_I2C "i2c_camera" +#define SENSOR_DRIVER_I2C "camera" /* Header file declaration */ #include "msm_sensor.h" #include "msm_sd.h" diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 3679c596c276..ee643b1ae3a0 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -738,7 +738,7 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { (1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI) | (1 << V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS)| (1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP)| - (1UL << V4L2_MPEG_VIDC_EXTRADATA_ENC_FRAME_QP) + (1 << V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO) ), .qmenu = mpeg_video_vidc_extradata, }, diff --git a/include/media/msmb_isp.h b/include/media/msmb_isp.h index 95679cb5ccb6..176dd2e44f07 100644 --- a/include/media/msmb_isp.h +++ b/include/media/msmb_isp.h @@ -28,6 +28,19 @@ struct msm_isp_event_data32 { struct msm_isp_sof_info sof_info; } u; }; + +struct msm_isp32_event_data32 { + struct compat_timeval timestamp; + struct compat_timeval mono_timestamp; + enum msm_vfe_input_src input_intf; + uint32_t frame_id; + union { + struct msm_isp_stats_event stats; + struct msm_isp_buf_event buf_done; + struct msm_isp32_error_info error_info; + } u; +}; + #endif #ifdef CONFIG_MSM_AVTIMER struct avtimer_fptr_t { diff --git a/include/uapi/media/msmb_isp.h b/include/uapi/media/msmb_isp.h index 053fa768cfaf..74a8d9359d34 100644 --- a/include/uapi/media/msmb_isp.h +++ b/include/uapi/media/msmb_isp.h @@ -18,6 +18,7 @@ #define ISP1_BIT (0x10000 << 2) #define ISP_META_CHANNEL_BIT (0x10000 << 3) #define ISP_SCRATCH_BUF_BIT (0x10000 << 4) +#define ISP_PDAF_CHANNEL_BIT (0x10000 << 5) #define ISP_OFFLINE_STATS_BIT (0x10000 << 5) #define ISP_SVHDR_IN_BIT (0x10000 << 6) /* RDI hw stream for SVHDR */ #define ISP_SVHDR_OUT_BIT (0x10000 << 7) /* SVHDR output bufq stream*/ @@ -295,6 +296,11 @@ struct msm_vfe_axi_plane_cfg { uint8_t rdi_cid;/*CID 1-16*/ }; +enum msm_stream_memory_input_t { + MEMORY_INPUT_DISABLED, + MEMORY_INPUT_ENABLED +}; + enum msm_stream_rdi_input_type { MSM_CAMERA_RDI_MIN, MSM_CAMERA_RDI_PDAF, @@ -324,6 +330,29 @@ struct msm_vfe_axi_stream_request_cmd { enum msm_stream_rdi_input_type rdi_input_type; }; +struct msm_vfe32_axi_stream_request_cmd { + uint32_t session_id; + uint32_t stream_id; + uint32_t vt_enable; + uint32_t output_format;/*Planar/RAW/Misc*/ + enum msm_vfe_axi_stream_src stream_src; /*CAMIF/IDEAL/RDIs*/ + struct msm_vfe_axi_plane_cfg plane_cfg[MAX_PLANES_PER_STREAM]; + + uint32_t burst_count; + uint32_t hfr_mode; + uint8_t frame_base; + + uint32_t init_frame_drop; /*MAX 31 Frames*/ + enum msm_vfe_frame_skip_pattern frame_skip_pattern; + uint8_t buf_divert; /* if TRUE no vb2 buf done. */ + /*Return values*/ + uint32_t axi_stream_handle; + uint32_t controllable_output; + uint32_t burst_len; + /* Flag indicating memory input stream */ + enum msm_stream_memory_input_t memory_input; +}; + struct msm_vfe_axi_stream_release_cmd { uint32_t stream_handle; }; @@ -680,7 +709,9 @@ enum msm_isp_event_idx { ISP_PING_PONG_MISMATCH = 12, ISP_REG_UPDATE_MISSING = 13, ISP_BUF_FATAL_ERROR = 14, - ISP_EVENT_MAX = 15 + ISP_EVENT_MAX = 15, + ISP_WM_BUS_OVERFLOW = 16, + ISP_CAMIF_ERROR = 17, }; #define ISP_EVENT_OFFSET 8 @@ -710,6 +741,7 @@ enum msm_isp_event_idx { #define ISP_EVENT_REG_UPDATE_MISSING (ISP_EVENT_BASE + ISP_REG_UPDATE_MISSING) #define ISP_EVENT_BUF_FATAL_ERROR (ISP_EVENT_BASE + ISP_BUF_FATAL_ERROR) #define ISP_EVENT_STREAM_UPDATE_DONE (ISP_STREAM_EVENT_BASE) +#define ISP_EVENT_WM_BUS_OVERFLOW (ISP_EVENT_BASE + ISP_WM_BUS_OVERFLOW) /* The msm_v4l2_event_data structure should match the * v4l2_event.u.data field. @@ -759,6 +791,11 @@ struct msm_isp_error_info { uint32_t stream_id_mask; }; +struct msm_isp32_error_info { + /* 1 << msm_isp_event_idx */ + uint32_t error_mask; +}; + /* This structure reports delta between master and slave */ struct msm_isp_ms_delta_info { uint8_t num_delta_info; @@ -827,6 +864,25 @@ struct msm_isp_event_data { } u; /* union can have max 52 bytes */ }; +struct msm_isp32_event_data { + /*Wall clock except for buffer divert events + *which use monotonic clock + */ + struct timeval timestamp; + /* Monotonic timestamp since bootup */ + struct timeval mono_timestamp; + enum msm_vfe_input_src input_intf; + uint32_t frame_id; + union { + /* Sent for Stats_Done event */ + struct msm_isp_stats_event stats; + /* Sent for Buf_Divert event */ + struct msm_isp_buf_event buf_done; + struct msm_isp32_error_info error_info; + } u; /* union can have max 52 bytes */ + uint32_t is_skip_pproc; +}; + enum msm_vfe_ahb_clk_vote { MSM_ISP_CAMERA_AHB_SVS_VOTE = 1, MSM_ISP_CAMERA_AHB_TURBO_VOTE = 2, @@ -919,6 +975,7 @@ enum msm_isp_ioctl_cmd_code { MSM_ISP_MAP_BUF_START_MULTI_PASS_FE, MSM_ISP_REQUEST_BUF_VER2, MSM_ISP_DUAL_HW_LPM_MODE, + MSM_ISP32_REQUEST_STREAM, }; #define VIDIOC_MSM_VFE_REG_CFG \ @@ -941,6 +998,10 @@ enum msm_isp_ioctl_cmd_code { _IOWR('V', MSM_ISP_REQUEST_STREAM, \ struct msm_vfe_axi_stream_request_cmd) +#define VIDIOC_MSM_ISP32_REQUEST_STREAM \ + _IOWR('V', MSM_ISP32_REQUEST_STREAM, \ + struct msm_vfe32_axi_stream_request_cmd) + #define VIDIOC_MSM_ISP_CFG_STREAM \ _IOWR('V', MSM_ISP_CFG_STREAM, \ struct msm_vfe_axi_stream_cfg_cmd) @@ -1038,6 +1099,8 @@ enum msm_isp_ioctl_cmd_code { #define VIDIOC_MSM_ISP_REQUEST_BUF_VER2 \ _IOWR('V', MSM_ISP_REQUEST_BUF_VER2, struct msm_isp_buf_request_ver2) +#define VIDIOC_MSM_ISP_BUF_DONE \ + _IOWR('V', BASE_VIDIOC_PRIVATE+21, struct msm_isp32_event_data) #define VIDIOC_MSM_ISP_DUAL_HW_LPM_MODE \ _IOWR('V', MSM_ISP_DUAL_HW_LPM_MODE, \