From 3454e2b2ea2d538632455a9b7590a8478a2fa723 Mon Sep 17 00:00:00 2001 From: Kevin Chan Date: Thu, 17 Jan 2013 19:18:57 -0800 Subject: [PATCH] msm: camera: Add support for ISP statistics collection Add suppport to configure statistics modules Add support for statistics buffer handling Handle statistics module interrupt and generate event to notify userspace. Change-Id: I58f610f9509d259dcae7cb55e35ce7e3c4ab03d4 Signed-off-by: Kevin Chan [smuckle@codeaurora.org: backported from msm-3.7] Signed-off-by: Steve Muckle --- .../platform/msm/camera_v2/isp/msm_isp.h | 87 ++-- .../platform/msm/camera_v2/isp/msm_isp40.c | 204 ++++++--- .../msm/camera_v2/isp/msm_isp_axi_util.c | 2 - .../msm/camera_v2/isp/msm_isp_stats_util.c | 400 +++++++++++------- .../msm/camera_v2/isp/msm_isp_stats_util.h | 2 + .../platform/msm/camera_v2/isp/msm_isp_util.c | 5 + include/media/msmb_isp.h | 21 +- 7 files changed, 445 insertions(+), 276 deletions(-) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index d8e6f190a8a..243dac7140c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -34,6 +34,9 @@ #define MAX_INIT_FRAME_DROP 31 #define ISP_SUB(a) ((a > 0) ? a-1 : 0) +#define VFE_PING_FLAG 0xFFFFFFFF +#define VFE_PONG_FLAG 0x0 + struct vfe_device; struct msm_vfe_axi_stream; struct msm_vfe_stats_stream; @@ -133,14 +136,12 @@ struct msm_vfe_core_ops { int (*get_platform_data) (struct vfe_device *vfe_dev); }; struct msm_vfe_stats_ops { + int (*get_stats_idx) (enum msm_isp_stats_type stats_type); 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, - struct msm_vfe_stats_stream *stream_info); - void (*clear_comp_mask) (struct vfe_device *vfe_dev, - struct msm_vfe_stats_stream *stream_info); + void (*cfg_comp_mask) (struct vfe_device *vfe_dev); 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, @@ -153,20 +154,17 @@ struct msm_vfe_stats_ops { void (*cfg_ub) (struct vfe_device *vfe_dev); - void (*stats_enable) (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, - enum msm_isp_stats_type stats_type, uint32_t pingpong_status, - unsigned long paddr); + struct msm_vfe_stats_stream *stream_info, + uint32_t pingpong_status, unsigned long 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); - uint32_t (*get_active_pingpong_idx) (uint32_t pingpong_status, - enum msm_isp_stats_type stats_type); - }; struct msm_vfe_ops { @@ -179,6 +177,7 @@ struct msm_vfe_ops { struct msm_vfe_hardware_info { 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; }; @@ -189,7 +188,6 @@ struct msm_vfe_axi_hardware_info { uint8_t num_rdi_master; uint8_t num_comp_mask; uint32_t min_wm_ub; - uint8_t num_stats_comp_mask; }; enum msm_vfe_axi_state { @@ -243,6 +241,7 @@ struct msm_vfe_axi_composite_info { uint32_t stream_handle; uint32_t stream_composite_mask; }; + struct msm_vfe_src_info { unsigned long frame_id; uint8_t active; @@ -251,40 +250,12 @@ struct msm_vfe_src_info { enum msm_vfe_inputmux input_mux; }; -enum msm_vfe_stats_state { - STATE_AVALIABLE, - STATE_INACTIVE, - STATE_ACTIVE, - STATE_START_PENDING, - STATE_STOP_PENDING, - STATE_STOPPING, -}; -struct msm_vfe_stats_stream { - uint32_t frame_id; - uint8_t enable; - enum msm_isp_stats_type stats_type; - int8_t comp_mask_index; - struct msm_isp_buffer *buf[2]; - uint32_t session_id; - uint32_t stream_id; - uint32_t bufq_handle; - uint32_t stream_handle; - uint32_t framedrop_pattern; - uint8_t comp_flag; - uint8_t comp_idx; - enum msm_vfe_stats_state state; -}; - -struct msm_vfe_stats_composite_info { - uint32_t stats_mask; - uint8_t comp_flag; -}; - 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[MAX_NUM_STREAM]; @@ -305,14 +276,44 @@ struct msm_vfe_axi_shared_data { unsigned long event_mask; }; +struct msm_vfe_stats_hardware_info { + uint32_t stats_capability_mask; + uint32_t stats_ping_pong_offset; + uint8_t num_stats_type; + uint8_t num_stats_comp_mask; +}; + +enum msm_vfe_stats_state { + STATS_AVALIABLE, + STATS_INACTIVE, + STATS_ACTIVE, + STATS_START_PENDING, + STATS_STOP_PENDING, + STATS_STOPPING, +}; + +struct msm_vfe_stats_stream { + uint32_t session_id; + uint32_t stream_id; + uint32_t stream_handle; + enum msm_isp_stats_type stats_type; + enum msm_vfe_stats_state state; + uint32_t framedrop_pattern; + uint32_t irq_subsample_pattern; + + 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]; - struct msm_vfe_stats_composite_info - composite_info[MAX_NUM_STATS_COMP_MASK]; + enum msm_vfe_stats_pipeline_policy stats_pipeline_policy; + uint32_t comp_framedrop_pattern; + uint32_t comp_irq_subsample_pattern; uint8_t num_active_stream; - uint8_t num_used_composite_mask; uint16_t stream_handle_cnt; }; + struct msm_vfe_tasklet_queue_cmd { struct list_head list; uint32_t vfeInterruptStatus0; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index d4b7b847d7a..943908caee0 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -30,6 +30,7 @@ #endif #define VFE40_BURST_LEN 4 +#define VFE40_UB_SIZE 1024 #define VFE40_EQUAL_SLICE_UB 117 #define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx) #define VFE40_RDI_BASE(idx) (0x2E8 + 0x4 * idx) @@ -41,6 +42,13 @@ #define VFE40_PING_PONG_BASE(wm, ping_pong) \ (VFE40_WM_BASE(wm) + 0x4 * (1 + (~(ping_pong >> wm) & 0x1))) +#define VFE40_NUM_STATS_TYPE 8 +#define VFE40_STATS_PING_PONG_OFFSET 8 +#define VFE40_STATS_BASE(idx) (0x168 + 0x18 * idx) +#define VFE40_STATS_PING_PONG_BASE(idx, ping_pong) \ + (VFE40_STATS_BASE(idx) + 0x4 * \ + (~(ping_pong >> (idx + VFE40_STATS_PING_PONG_OFFSET)) & 0x1)) + #define VFE40_VBIF_CLKON 0x4 #define VFE40_VBIF_IN_RD_LIM_CONF0 0xB0 #define VFE40_VBIF_IN_RD_LIM_CONF1 0xB4 @@ -907,64 +915,160 @@ static uint32_t msm_vfe40_get_pingpong_status( return msm_camera_io_r(vfe_dev->vfe_base + 0x268); } -static void msm_vfe40_stats_cfg_comp_mask( - struct vfe_device *vfe_dev, - struct msm_vfe_stats_stream *stream_info) +static int msm_vfe40_get_stats_idx(enum msm_isp_stats_type stats_type) { - + switch (stats_type) { + case MSM_ISP_STATS_BE: + return 0; + case MSM_ISP_STATS_BG: + return 1; + case MSM_ISP_STATS_BF: + return 2; + case MSM_ISP_STATS_AWB: + return 3; + case MSM_ISP_STATS_RS: + return 4; + case MSM_ISP_STATS_CS: + return 5; + case MSM_ISP_STATS_IHIST: + return 6; + case MSM_ISP_STATS_BHIST: + return 7; + default: + pr_err("%s: Invalid stats type\n", __func__); + return -EINVAL; + } } -static void msm_vfe40_stats_clear_comp_mask( - struct vfe_device *vfe_dev, - struct msm_vfe_stats_stream *stream_info) +static void msm_vfe40_stats_cfg_comp_mask(struct vfe_device *vfe_dev) { - + if (vfe_dev->stats_data.stats_pipeline_policy == STATS_COMP_ALL) + msm_camera_io_w(0x00FF0000, vfe_dev->vfe_base + 0x44); + else + msm_camera_io_w(0x00000000, vfe_dev->vfe_base + 0x44); } static void msm_vfe40_stats_cfg_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - + uint32_t irq_mask; + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28); + irq_mask |= 1 << (STATS_IDX(stream_info->stream_handle) + 16); + msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28); } static void msm_vfe40_stats_clear_wm_irq_mask( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { - + uint32_t irq_mask; + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28); + irq_mask &= ~(1 << (STATS_IDX(stream_info->stream_handle) + 16)); + msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28); } static void msm_vfe40_stats_cfg_wm_reg( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { + int stats_idx = STATS_IDX(stream_info->stream_handle); + uint32_t stats_base = VFE40_STATS_BASE(stats_idx); + /*WR_ADDR_CFG*/ + msm_camera_io_w(0x7C, vfe_dev->vfe_base + stats_base + 0x8); + /*WR_IRQ_FRAMEDROP_PATTERN*/ + msm_camera_io_w(0xFFFFFFFF, + vfe_dev->vfe_base + stats_base + 0x10); + /*WR_IRQ_SUBSAMPLE_PATTERN*/ + msm_camera_io_w(0xFFFFFFFF, + vfe_dev->vfe_base + stats_base + 0x14); } static void msm_vfe40_stats_clear_wm_reg( struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info) { + uint32_t val = 0; + int stats_idx = STATS_IDX(stream_info->stream_handle); + uint32_t stats_base = VFE40_STATS_BASE(stats_idx); + /*WR_ADDR_CFG*/ + msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x8); + /*WR_IRQ_FRAMEDROP_PATTERN*/ + msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x10); + /*WR_IRQ_SUBSAMPLE_PATTERN*/ + msm_camera_io_w(val, vfe_dev->vfe_base + stats_base + 0x14); } static void msm_vfe40_stats_cfg_ub(struct vfe_device *vfe_dev) { + int i; + uint32_t ub_offset = VFE40_UB_SIZE; + uint32_t ub_size[VFE40_NUM_STATS_TYPE] = { + 64, /*MSM_ISP_STATS_BE*/ + 64, /*MSM_ISP_STATS_BG*/ + 64, /*MSM_ISP_STATS_BF*/ + 16, /*MSM_ISP_STATS_AWB*/ + 8, /*MSM_ISP_STATS_RS*/ + 16, /*MSM_ISP_STATS_CS*/ + 16, /*MSM_ISP_STATS_IHIST*/ + 16, /*MSM_ISP_STATS_BHIST*/ + }; + + for (i = 0; i < VFE40_NUM_STATS_TYPE; i++) { + ub_offset -= ub_size[i]; + msm_camera_io_w(VFE40_BURST_LEN << 30 | + ub_offset << 16 | (ub_size[i] - 1), + vfe_dev->vfe_base + VFE40_STATS_BASE(i) + 0xC); + } } -static void msm_vfe40_stats_enable(struct vfe_device *vfe_dev, +static void msm_vfe40_stats_enable_module(struct vfe_device *vfe_dev, uint32_t stats_mask, uint8_t enable) { + int i; + uint32_t module_cfg, module_cfg_mask = 0; + for (i = 0; i < VFE40_NUM_STATS_TYPE; i++) { + if ((stats_mask >> i) & 0x1) { + switch (i) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + module_cfg_mask |= 1 << (5 + i); + break; + case 6: + module_cfg_mask |= 1 << 15; + break; + case 7: + module_cfg_mask |= 1 << 18; + break; + default: + pr_err("%s: Invalid stats mask\n", __func__); + return; + } + } + } + + module_cfg = msm_camera_io_r(vfe_dev->vfe_base + 0x18); + if (enable) + module_cfg |= module_cfg_mask; + else + module_cfg &= ~module_cfg_mask; + msm_camera_io_w(module_cfg, vfe_dev->vfe_base + 0x18); } static void msm_vfe40_stats_update_ping_pong_addr( - struct vfe_device *vfe_dev, - enum msm_isp_stats_type stats_type, - uint32_t pingpong_status, - unsigned long paddr) + struct vfe_device *vfe_dev, struct msm_vfe_stats_stream *stream_info, + uint32_t pingpong_status, unsigned long paddr) { + int stats_idx = STATS_IDX(stream_info->stream_handle); + msm_camera_io_w(paddr, vfe_dev->vfe_base + + VFE40_STATS_PING_PONG_BASE(stats_idx, pingpong_status)); } static uint32_t msm_vfe40_stats_get_wm_mask( @@ -976,7 +1080,7 @@ static uint32_t msm_vfe40_stats_get_wm_mask( static uint32_t msm_vfe40_stats_get_comp_mask( uint32_t irq_status0, uint32_t irq_status1) { - return (irq_status0 >> 29) & 0x2; + return (irq_status0 >> 29) & 0x3; } static uint32_t msm_vfe40_stats_get_frame_id( @@ -985,55 +1089,6 @@ static uint32_t msm_vfe40_stats_get_frame_id( return vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; } -static uint32_t msm_vfe40_stats_get_active_pingpong_idx( - uint32_t pingpong_status, - enum msm_isp_stats_type stats_type) -{ - int pingpong_bit = 0; - switch (stats_type) { - case MSM_ISP_STATS_AWB: - if (pingpong_status & (1 << 11)) - pingpong_bit = 1; /* pong is active */ - break; - case MSM_ISP_STATS_RS: - if (pingpong_status & (1 << 12)) - pingpong_bit = 1; /* pong is active */ - break; - case MSM_ISP_STATS_CS: - if (pingpong_status & (1 << 13)) - pingpong_bit = 1; /* pong is active */ - break; - case MSM_ISP_STATS_IHIST: - if (pingpong_status & (1 << 14)) - pingpong_bit = 1; /* pong is active */ - break; - case MSM_ISP_STATS_BG: - if (pingpong_status & (1 << 9)) - pingpong_bit = 1; /* pong is active */ - break; - case MSM_ISP_STATS_BF: - if (pingpong_status & (1 << 10)) - pingpong_bit = 1; /* pong is active */ - break; - case MSM_ISP_STATS_BE: - if (pingpong_status & (1 << 8)) - pingpong_bit = 1; /* pong is active */ - break; - case MSM_ISP_STATS_BHIST: - if (pingpong_status & (1 << 15)) - pingpong_bit = 1; /* pong is active */ - break; - case MSM_ISP_STATS_SKIN: - case MSM_ISP_STATS_AEC: - case MSM_ISP_STATS_AF: - default: - pr_err("%s: not supported stats type = %d\n", - __func__, stats_type); - return -EINVAL; - } - return pingpong_bit; -} - static int msm_vfe40_get_platform_data(struct vfe_device *vfe_dev) { int rc = 0; @@ -1085,12 +1140,22 @@ vfe_no_resource: return rc; } -struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = { +static struct msm_vfe_axi_hardware_info msm_vfe40_axi_hw_info = { .num_wm = 7, .num_comp_mask = 4, .num_rdi = 3, .num_rdi_master = 3, .min_wm_ub = 64, +}; + +static struct msm_vfe_stats_hardware_info msm_vfe40_stats_hw_info = { + .stats_capability_mask = + 1 << MSM_ISP_STATS_BE | 1 << MSM_ISP_STATS_BF | + 1 << MSM_ISP_STATS_BG | 1 << MSM_ISP_STATS_BHIST | + 1 << MSM_ISP_STATS_AWB | 1 << MSM_ISP_STATS_IHIST | + 1 << MSM_ISP_STATS_RS | 1 << MSM_ISP_STATS_CS, + .stats_ping_pong_offset = VFE40_STATS_PING_PONG_OFFSET, + .num_stats_type = VFE40_NUM_STATS_TYPE, .num_stats_comp_mask = 2, }; @@ -1156,25 +1221,24 @@ struct msm_vfe_hardware_info vfe40_hw_info = { .get_platform_data = msm_vfe40_get_platform_data, }, .stats_ops = { + .get_stats_idx = msm_vfe40_get_stats_idx, .cfg_comp_mask = msm_vfe40_stats_cfg_comp_mask, - .clear_comp_mask = msm_vfe40_stats_clear_comp_mask, .cfg_wm_irq_mask = msm_vfe40_stats_cfg_wm_irq_mask, .clear_wm_irq_mask = msm_vfe40_stats_clear_wm_irq_mask, .cfg_wm_reg = msm_vfe40_stats_cfg_wm_reg, .clear_wm_reg = msm_vfe40_stats_clear_wm_reg, .cfg_ub = msm_vfe40_stats_cfg_ub, - .stats_enable = msm_vfe40_stats_enable, + .enable_module = msm_vfe40_stats_enable_module, .update_ping_pong_addr = msm_vfe40_stats_update_ping_pong_addr, .get_comp_mask = msm_vfe40_stats_get_comp_mask, .get_wm_mask = msm_vfe40_stats_get_wm_mask, .get_frame_id = msm_vfe40_stats_get_frame_id, .get_pingpong_status = msm_vfe40_get_pingpong_status, - .get_active_pingpong_idx = - msm_vfe40_stats_get_active_pingpong_idx, }, }, .axi_hw_info = &msm_vfe40_axi_hw_info, + .stats_hw_info = &msm_vfe40_stats_hw_info, .subdev_ops = &msm_vfe40_subdev_ops, .subdev_internal_ops = &msm_vfe40_internal_ops, }; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 1b96568cb99..2dc8e53dab6 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -644,8 +644,6 @@ void msm_isp_axi_stream_update(struct vfe_device *vfe_dev) complete(&vfe_dev->stream_config_complete); } } -#define VFE_PING_FLAG 0xFFFFFFFF -#define VFE_PONG_FLAG 0x0 int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index 86a4a3dafca..94d80fbadd8 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -13,251 +13,331 @@ #include #include "msm_isp_util.h" #include "msm_isp_stats_util.h" -#define VFE_PING_ACTIVE_FLAG 0xFFFFFFFF -#define VFE_PONG_ACTIVE_FLAG 0x0 -int msm_isp_stats_cfg_ping_pong_address(struct vfe_device *vfe_dev, +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 *out_buf) + struct msm_isp_buffer **done_buf) { int rc = -1; struct msm_isp_buffer *buf; - int active_bit = 0; - int buf_idx; + uint32_t pingpong_bit = 0; uint32_t bufq_handle = stream_info->bufq_handle; + uint32_t stats_pingpong_offset = + STATS_IDX(stream_info->stream_handle) + + vfe_dev->hw_info->stats_hw_info->stats_ping_pong_offset; - out_buf = NULL; - active_bit = vfe_dev->hw_info->vfe_ops.stats_ops. - get_active_pingpong_idx(pingpong_status, - stream_info->stats_type); - buf_idx = active_bit^0x1; - + pingpong_bit = (~(pingpong_status >> stats_pingpong_offset) & 0x1); rc = vfe_dev->buf_mgr->ops->get_buf( vfe_dev->buf_mgr, bufq_handle, &buf); if (rc < 0) { - pr_err("%s: No free buffer, stats_type = %d\n", - __func__, stream_info->stats_type); + pr_err("%s: No free buffer, stats_stream = %d\n", + __func__, stream_info->stream_handle); return rc; } - vfe_dev->hw_info->vfe_ops.stats_ops.update_ping_pong_addr( - vfe_dev, stream_info->stats_type, - pingpong_status, buf->mapped_info[buf_idx].paddr); - if (stream_info->buf[buf_idx]) - out_buf = stream_info->buf[buf_idx]; - stream_info->buf[buf_idx] = buf; + 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); + + 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 timeval *tv) { - uint32_t frame_id; - uint32_t stats_comp_mask = 0, stats_mask = 0; - ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0); + int i; + struct msm_isp_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 stats_comp_mask = 0, stats_irq_mask = 0; stats_comp_mask = vfe_dev->hw_info->vfe_ops.stats_ops. get_comp_mask(irq_status0, irq_status1); - stats_mask = vfe_dev->hw_info->vfe_ops.stats_ops. + stats_irq_mask = vfe_dev->hw_info->vfe_ops.stats_ops. get_wm_mask(irq_status0, irq_status1); - if (!(stats_comp_mask || stats_mask)) + if (!(stats_comp_mask || stats_irq_mask)) return; - frame_id = vfe_dev->hw_info->vfe_ops.stats_ops.get_frame_id(vfe_dev); - /* TD: process comp/non comp stats */ -} + ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0); -static int msm_isp_stats_reserve_comp_mask( - struct vfe_device *vfe_dev, - struct msm_vfe_stats_shared_data *stats_data, - struct msm_vfe_stats_stream *stream_info) -{ - int i; - uint8_t num_stats_comp; - int8_t comp_idx = -1; - if (stream_info->comp_flag == 0) - return 0; + if (vfe_dev->stats_data.stats_pipeline_policy == STATS_COMP_ALL) { + if (!stats_comp_mask) + return; + stats_irq_mask = 0xFFFFFFFF; + } - num_stats_comp = vfe_dev->axi_data.hw_info->num_stats_comp_mask; - for (i = 0; i < num_stats_comp; i++) { - if (!stats_data->composite_info[i].stats_mask == 0 && - comp_idx < 0) - comp_idx = i; - if (stats_data->composite_info[i].comp_flag == - stream_info->comp_flag) { - comp_idx = i; - break; + memset(&buf_event, 0, sizeof(struct msm_isp_event_data)); + 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; + 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) { + stats_event->stats_mask |= 1 << stream_info->stats_type; + stats_event->stats_buf_idxs[stream_info->stats_type] = + done_buf->buf_idx; } } - if (comp_idx < 0) { - pr_err("%s: no more stats comp idx\n", __func__); - return -EACCES; - } - stats_data->composite_info[comp_idx].stats_mask |= - (1 << stream_info->stats_type); - if (stats_data->composite_info[comp_idx].comp_flag == 0) - stats_data->composite_info[comp_idx].comp_flag = - stream_info->comp_flag; - stream_info->comp_idx = comp_idx; - return 0; + + buf_event.timestamp = *tv; + msm_isp_send_event(vfe_dev, ISP_EVENT_STATS_NOTIFY, &buf_event); } -static int msm_isp_stats_unreserve_comp_mask( - struct vfe_device *vfe_dev, - struct msm_vfe_stats_shared_data *stats_data, - struct msm_vfe_stats_stream *stream_info) +int msm_isp_stats_create_stream(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream_request_cmd *stream_req_cmd) { - uint8_t comp_idx = stream_info->comp_idx; + 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 (stream_info->comp_flag == 0) - return 0; - stats_data->composite_info[comp_idx].stats_mask &= - ~(1 << stream_info->stats_type); - if (stats_data->composite_info[comp_idx].stats_mask == 0) - memset(&stats_data->composite_info[comp_idx], 0, - sizeof(struct msm_vfe_stats_composite_info)); + 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); + + stream_info = &stats_data->stream_info[stats_idx]; + if (stream_info->state != STATS_AVALIABLE) { + pr_err("%s: Stats already requested\n", __func__); + return rc; + } + + if (stats_data->stats_pipeline_policy != STATS_COMP_ALL) { + 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; + } + } else { + if (stats_data->comp_framedrop_pattern >= MAX_SKIP) { + pr_err("%s: Invalid comp framedrop pattern\n", + __func__); + return rc; + } + + if (stats_data->comp_irq_subsample_pattern >= MAX_SKIP) { + pr_err("%s: Invalid comp irq subsample pattern\n", + __func__); + return rc; + } + stream_req_cmd->framedrop_pattern = + vfe_dev->stats_data.comp_framedrop_pattern; + stream_req_cmd->irq_subsample_pattern = + vfe_dev->stats_data.comp_irq_subsample_pattern; + } + + stream_info->session_id = stream_req_cmd->session_id; + stream_info->stream_id = stream_req_cmd->stream_id; + stream_info->stats_type = stream_req_cmd->stats_type; + stream_info->framedrop_pattern = stream_req_cmd->framedrop_pattern; + 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 = 0; - struct msm_vfe_stats_stream_request_cmd *stream_cfg_cmd = arg; + 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 stats_idx; - if (stream_cfg_cmd->stats_type < MSM_ISP_STATS_AEC || - stream_cfg_cmd->stats_type >= MSM_ISP_STATS_MAX) { - pr_err("%s: invalid stats type %d received\n", - __func__, stream_cfg_cmd->stats_type); - return -EINVAL; - } - stream_info = &stats_data->stream_info[stream_cfg_cmd->stats_type]; - if (stream_info->stream_handle != 0) { - pr_err("%s: stats type %d has been used already\n", - __func__, stream_cfg_cmd->stats_type); - return -EBUSY; + 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_data->stream_handle_cnt++; - if (stats_data->stream_handle_cnt == 0) - stats_data->stream_handle_cnt++; - stream_info->stream_handle = - stats_data->stream_handle_cnt << 16 | - stream_cfg_cmd->stats_type; - stream_info->enable = 0; - stream_info->stats_type = stream_cfg_cmd->stats_type; - stream_info->comp_flag = stream_cfg_cmd->comp_flag; - stream_info->session_id = stream_cfg_cmd->session_id; - stream_info->stream_id = stream_cfg_cmd->stream_id; - stream_info->framedrop_pattern = stream_cfg_cmd->framedrop_pattern; - stream_cfg_cmd->stream_handle = stream_info->stream_handle; - msm_isp_stats_reserve_comp_mask(vfe_dev, stats_data, stream_info); - if (stream_info->comp_flag) - vfe_dev->hw_info->vfe_ops.stats_ops. - cfg_comp_mask(vfe_dev, stream_info); - else + stats_idx = STATS_IDX(stream_req_cmd->stream_handle); + stream_info = &stats_data->stream_info[stats_idx]; + + switch (stream_info->framedrop_pattern) { + case NO_SKIP: + stream_info->framedrop_pattern = VFE_NO_DROP; + break; + case EVERY_2FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_2FRAME; + break; + case EVERY_4FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_4FRAME; + break; + case EVERY_8FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_8FRAME; + break; + case EVERY_16FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_16FRAME; + break; + case EVERY_32FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_32FRAME; + break; + default: + stream_info->framedrop_pattern = VFE_NO_DROP; + break; + } + + if (stats_data->stats_pipeline_policy == STATS_COMP_NONE) vfe_dev->hw_info->vfe_ops.stats_ops. cfg_wm_irq_mask(vfe_dev, stream_info); - vfe_dev->hw_info->vfe_ops.stats_ops. - cfg_wm_reg(vfe_dev, stream_info); + + 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 = 0; + 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 = - &stats_data->stream_info[ - (stream_release_cmd->stream_handle & 0xFF)]; + &stats_data->stream_info[stats_idx]; - if (stream_info == NULL || - stream_info->stream_handle != - stream_release_cmd->stream_handle) { - pr_err("%s: handle mismatch(0x%x, 0x%x\n", - __func__, stream_info->stream_handle, - stream_release_cmd->stream_handle); - rc = -EINVAL; + if (stream_info->state == STATS_AVALIABLE) { + 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->enable) { - struct msm_vfe_stats_stream_cfg_cmd stop_cmd; - memset(&stop_cmd, 0, sizeof(stop_cmd)); - stop_cmd.num_streams = 1; - stop_cmd.stream_handle[0] = stream_release_cmd->stream_handle; - stop_cmd.enable = 0; - rc = msm_isp_cfg_stats_stream(vfe_dev, (void *)&stop_cmd); - if (rc < 0) { - pr_err("%s: cannot stop stats type %d\n", - __func__, stream_info->stats_type); - return -EPERM; - } - } - if (stream_info->bufq_handle) { - vfe_dev->buf_mgr->ops->release_buf(vfe_dev->buf_mgr, - stream_info->bufq_handle); - stream_info->bufq_handle = 0; - } - vfe_dev->hw_info->vfe_ops.stats_ops. - clear_wm_reg(vfe_dev, stream_info); - if (stream_info->comp_flag) { - msm_isp_stats_unreserve_comp_mask(vfe_dev, - stats_data, stream_info); + + if (stats_data->stats_pipeline_policy == STATS_COMP_NONE) vfe_dev->hw_info->vfe_ops.stats_ops. - clear_comp_mask(vfe_dev, stream_info); - } else { - 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_framedrop(vfe_dev, stream_info); + 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 rc; + return 0; } int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg) { int i, rc = 0; - uint32_t pingpong_status = 0; - struct msm_isp_buffer *buf = NULL; struct msm_vfe_stats_stream_cfg_cmd *stream_cfg_cmd = arg; struct msm_vfe_stats_stream *stream_info; struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; int idx; uint32_t stats_mask = 0; - uint8_t enable = stream_cfg_cmd->enable; + + if (stats_data->num_active_stream == 0) + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { - idx = stream_cfg_cmd->stream_handle[i] & 0xF; + idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); 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", + pr_err("%s: Invalid stream handle: 0x%x received\n", __func__, stream_cfg_cmd->stream_handle[i]); continue; } - if (enable) { + + if (stream_cfg_cmd->enable) { 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 stats type = %d\n", + pr_err("%s: no buf configured for stream: 0x%x\n", __func__, - stream_info->stats_type); + stream_info->stream_handle); return -EINVAL; } + + msm_isp_stats_cfg_ping_pong_address(vfe_dev, + stream_info, VFE_PING_FLAG, NULL); + msm_isp_stats_cfg_ping_pong_address(vfe_dev, + stream_info, VFE_PONG_FLAG, NULL); + stream_info->state = STATS_START_PENDING; + stats_data->num_active_stream++; + } else { + stream_info->state = STATS_STOP_PENDING; + stats_data->num_active_stream--; } - /* config ping address */ - pingpong_status = VFE_PONG_ACTIVE_FLAG; - stats_mask |= (1 << stream_info->stats_type); - msm_isp_stats_cfg_ping_pong_address(vfe_dev, - stream_info, pingpong_status, buf); - pingpong_status = VFE_PING_ACTIVE_FLAG; - msm_isp_stats_cfg_ping_pong_address(vfe_dev, - stream_info, pingpong_status, buf); + stats_mask |= 1 << idx; } vfe_dev->hw_info->vfe_ops.stats_ops. - stats_enable(vfe_dev, stats_mask, enable); + enable_module(vfe_dev, stats_mask, stream_cfg_cmd->enable); return rc; } + +int msm_isp_cfg_stats_comp_policy(struct vfe_device *vfe_dev, void *arg) +{ + int rc = -1; + struct msm_vfe_stats_comp_policy_cfg *policy_cfg_cmd = arg; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + + if (stats_data->num_active_stream != 0) { + pr_err("%s: Cannot update policy when there are active streams\n", + __func__); + return rc; + } + + if (policy_cfg_cmd->stats_pipeline_policy >= MAX_STATS_POLICY) { + pr_err("%s: Invalid stats composite policy\n", __func__); + return rc; + } + + if (policy_cfg_cmd->comp_framedrop_pattern >= MAX_SKIP) { + pr_err("%s: Invalid comp framedrop pattern\n", __func__); + return rc; + } + + if (policy_cfg_cmd->comp_irq_subsample_pattern >= MAX_SKIP) { + pr_err("%s: Invalid comp irq subsample pattern\n", __func__); + return rc; + } + + stats_data->stats_pipeline_policy = + policy_cfg_cmd->stats_pipeline_policy; + stats_data->comp_framedrop_pattern = + policy_cfg_cmd->comp_framedrop_pattern; + stats_data->comp_irq_subsample_pattern = + policy_cfg_cmd->comp_irq_subsample_pattern; + + vfe_dev->hw_info->vfe_ops.stats_ops.cfg_comp_mask(vfe_dev); + + return 0; +} + diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h index 4feb653afd5..2f51b7bcb43 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h @@ -13,6 +13,7 @@ #define __MSM_ISP_STATS_UTIL_H__ #include "msm_isp.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, @@ -20,4 +21,5 @@ void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, int msm_isp_cfg_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); +int msm_isp_cfg_stats_comp_policy(struct vfe_device *vfe_dev, void *arg); #endif /* __MSM_ISP_STATS_UTIL_H__ */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 2b641f348c5..27fda986d6c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -142,6 +142,9 @@ long msm_isp_ioctl(struct v4l2_subdev *sd, case VIDIOC_MSM_ISP_CFG_STATS_STREAM: msm_isp_cfg_stats_stream(vfe_dev, arg); break; + case VIDIOC_MSM_ISP_CFG_STATS_COMP_POLICY: + msm_isp_cfg_stats_comp_policy(vfe_dev, arg); + break; } mutex_unlock(&vfe_dev->mutex); @@ -425,6 +428,8 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) vfe_dev->buf_mgr->ops->buf_mgr_init(vfe_dev->buf_mgr, "msm_isp", 14); 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)); vfe_dev->axi_data.hw_info = vfe_dev->hw_info->axi_hw_info; ISP_DBG("%s: HW Version: 0x%x\n", diff --git a/include/media/msmb_isp.h b/include/media/msmb_isp.h index 4e92f70cbfb..7c622f6528c 100644 --- a/include/media/msmb_isp.h +++ b/include/media/msmb_isp.h @@ -142,6 +142,12 @@ struct msm_vfe_axi_stream_cfg_cmd { enum msm_vfe_axi_stream_cmd cmd; }; +enum msm_vfe_stats_pipeline_policy { + STATS_COMP_ALL, + STATS_COMP_NONE, + MAX_STATS_POLICY, +}; + enum msm_isp_stats_type { MSM_ISP_STATS_AEC, /* legacy based AEC */ MSM_ISP_STATS_AF, /* legacy based AF */ @@ -161,10 +167,12 @@ struct msm_vfe_stats_stream_request_cmd { uint32_t session_id; uint32_t stream_id; enum msm_isp_stats_type stats_type; - uint8_t comp_flag; uint32_t framedrop_pattern; + uint32_t irq_subsample_pattern; uint32_t stream_handle; + uint8_t comp_flag; }; + struct msm_vfe_stats_stream_release_cmd { uint32_t stream_handle; }; @@ -173,6 +181,13 @@ struct msm_vfe_stats_stream_cfg_cmd { uint32_t stream_handle[MSM_ISP_STATS_MAX]; uint8_t enable; }; + +struct msm_vfe_stats_comp_policy_cfg { + enum msm_vfe_stats_pipeline_policy stats_pipeline_policy; + uint32_t comp_framedrop_pattern; + uint32_t comp_irq_subsample_pattern; +}; + enum msm_vfe_reg_cfg_type { VFE_WRITE, VFE_WRITE_MB, @@ -320,4 +335,8 @@ struct msm_isp_event_data { _IOWR('V', BASE_VIDIOC_PRIVATE+11, \ struct msm_vfe_stats_stream_release_cmd) +#define VIDIOC_MSM_ISP_CFG_STATS_COMP_POLICY \ + _IOWR('V', BASE_VIDIOC_PRIVATE+12, \ + struct msm_vfe_stats_comp_policy_cfg) + #endif /* __MSMB_ISP__ */