aboutsummaryrefslogtreecommitdiff
path: root/camera/isp/msm_buf_mgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'camera/isp/msm_buf_mgr.c')
-rw-r--r--camera/isp/msm_buf_mgr.c1472
1 files changed, 1472 insertions, 0 deletions
diff --git a/camera/isp/msm_buf_mgr.c b/camera/isp/msm_buf_mgr.c
new file mode 100644
index 00000000..3331f0d4
--- /dev/null
+++ b/camera/isp/msm_buf_mgr.c
@@ -0,0 +1,1472 @@
+/* Copyright (c) 2013-2016, 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 <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <linux/proc_fs.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-core.h>
+
+#include "msm.h"
+#include "msm_buf_mgr.h"
+#include "cam_smmu_api.h"
+
+#undef CDBG
+#define CDBG(fmt, args...) pr_debug(fmt, ##args)
+
+#define BUF_DEBUG_FULL 0
+#define MAX_LIST_COUNT 100
+
+static int msm_buf_check_head_sanity(struct msm_isp_bufq *bufq)
+{
+ int rc = 0;
+ struct list_head *prev = NULL;
+ struct list_head *next = NULL;
+
+ if (!bufq) {
+ pr_err("%s: Error! Invalid bufq\n", __func__);
+ return -EINVAL;
+ }
+
+ prev = bufq->head.prev;
+ next = bufq->head.next;
+
+ if (!prev) {
+ pr_err("%s: Error! bufq->head.prev is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!next) {
+ pr_err("%s: Error! bufq->head.next is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (prev->next != &bufq->head) {
+ pr_err("%s: Error! head prev->next is %p should be %p\n",
+ __func__, prev->next, &bufq->head);
+ return -EINVAL;
+ }
+
+ if (next->prev != &bufq->head) {
+ pr_err("%s: Error! head next->prev is %p should be %p\n",
+ __func__, next->prev, &bufq->head);
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+struct msm_isp_bufq *msm_isp_get_bufq(
+ struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ uint32_t bufq_index = bufq_handle & 0xFF;
+
+ /* bufq_handle cannot be 0 */
+ if ((bufq_handle == 0) ||
+ bufq_index >= BUF_MGR_NUM_BUF_Q ||
+ (bufq_index > buf_mgr->num_buf_q))
+ return NULL;
+
+ bufq = &buf_mgr->bufq[bufq_index];
+ if (bufq->bufq_handle == bufq_handle)
+ return bufq;
+
+ return NULL;
+}
+
+static struct msm_isp_buffer *msm_isp_get_buf_ptr(
+ struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return buf_info;
+ }
+
+ if (bufq->num_bufs <= buf_index) {
+ pr_err("%s: Invalid buf index\n", __func__);
+ return buf_info;
+ }
+ buf_info = &bufq->bufs[buf_index];
+ return buf_info;
+}
+
+static uint32_t msm_isp_get_buf_handle(
+ struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t session_id, uint32_t stream_id)
+{
+ int i;
+ uint32_t embedded_stream_id = 0;
+
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ if (buf_mgr->bufq[i].session_id == session_id &&
+ buf_mgr->bufq[i].stream_id == stream_id)
+ return 0;
+ }
+
+ /* put stream id in handle, if its stats, use FFFF */
+ if (stream_id & (1 << 31))
+ embedded_stream_id = 0xFFFF;
+ else
+ embedded_stream_id = stream_id;
+
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ if (buf_mgr->bufq[i].bufq_handle == 0) {
+ buf_mgr->bufq[i].bufq_handle =
+ embedded_stream_id << 8 | i;
+ return buf_mgr->bufq[i].bufq_handle;
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_free_bufq_handle(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle)
+{
+ struct msm_isp_bufq *bufq =
+ msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq)
+ return -EINVAL;
+
+ /* Set everything except lock to 0 */
+ bufq->bufq_handle = 0;
+ bufq->bufs = 0;
+ bufq->session_id = 0;
+ bufq->stream_id = 0;
+ bufq->num_bufs = 0;
+ bufq->buf_type = 0;
+ INIT_LIST_HEAD(&bufq->head);
+
+ return 0;
+}
+
+static void msm_isp_copy_planes_from_v4l2_buffer(
+ struct msm_isp_qbuf_buffer *qbuf_buf,
+ const struct v4l2_buffer *v4l2_buf)
+{
+ int i;
+ qbuf_buf->num_planes = v4l2_buf->length;
+ for (i = 0; i < qbuf_buf->num_planes; i++) {
+ qbuf_buf->planes[i].addr = v4l2_buf->m.planes[i].m.userptr;
+ qbuf_buf->planes[i].offset = v4l2_buf->m.planes[i].data_offset;
+ qbuf_buf->planes[i].length = v4l2_buf->m.planes[i].length;
+ }
+}
+
+static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer *buf_info,
+ struct msm_isp_qbuf_buffer *qbuf_buf,
+ uint32_t stream_id)
+{
+ int i, rc = -1;
+ int ret;
+ struct msm_isp_buffer_mapped_info *mapped_info;
+ uint32_t accu_length = 0;
+
+ for (i = 0; i < qbuf_buf->num_planes; i++) {
+ mapped_info = &buf_info->mapped_info[i];
+ mapped_info->buf_fd = qbuf_buf->planes[i].addr;
+ ret = cam_smmu_get_phy_addr(buf_mgr->iommu_hdl,
+ mapped_info->buf_fd,
+ CAM_SMMU_MAP_RW,
+ &(mapped_info->paddr),
+ &(mapped_info->len));
+ if (ret) {
+ rc = -EINVAL;
+ pr_err_ratelimited("%s: cannot map address", __func__);
+ goto get_phy_err;
+ }
+
+ mapped_info->paddr += accu_length;
+ accu_length += qbuf_buf->planes[i].length;
+
+ CDBG("%s: plane: %d addr:%lu\n",
+ __func__, i, (unsigned long)mapped_info->paddr);
+
+ }
+ buf_info->num_planes = qbuf_buf->num_planes;
+ return 0;
+get_phy_err:
+ i--;
+
+ return rc;
+}
+
+static void msm_isp_unprepare_v4l2_buf(
+ struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer *buf_info,
+ uint32_t stream_id)
+{
+ int i;
+ struct msm_isp_buffer_mapped_info *mapped_info;
+ struct msm_isp_bufq *bufq = NULL;
+
+ if (!buf_mgr || !buf_info) {
+ pr_err("%s: NULL ptr %p %p\n", __func__,
+ buf_mgr, buf_info);
+ return;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq, stream id %x\n",
+ __func__, stream_id);
+ return;
+ }
+
+ for (i = 0; i < buf_info->num_planes; i++) {
+ mapped_info = &buf_info->mapped_info[i];
+
+ cam_smmu_put_phy_addr(buf_mgr->iommu_hdl, mapped_info->buf_fd);
+ }
+ return;
+}
+
+static int msm_isp_map_buf(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buffer_mapped_info *mapped_info, uint32_t fd)
+{
+ int rc = 0;
+ int ret;
+
+ if (!buf_mgr || !mapped_info) {
+ pr_err_ratelimited("%s: %d] NULL ptr buf_mgr %p mapped_info %p\n",
+ __func__, __LINE__, buf_mgr, mapped_info);
+ return -EINVAL;
+ }
+ ret = cam_smmu_get_phy_addr(buf_mgr->iommu_hdl,
+ fd,
+ CAM_SMMU_MAP_RW,
+ &(mapped_info->paddr),
+ &(mapped_info->len));
+
+ if (ret) {
+ rc = -EINVAL;
+ pr_err_ratelimited("%s: cannot map address", __func__);
+ goto smmu_map_error;
+ }
+ CDBG("%s: addr:%lu\n",
+ __func__, (unsigned long)mapped_info->paddr);
+
+ return rc;
+smmu_map_error:
+ cam_smmu_put_phy_addr(buf_mgr->iommu_hdl,
+ fd);
+ return rc;
+}
+
+static int msm_isp_unmap_buf(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t fd)
+{
+ if (!buf_mgr) {
+ pr_err_ratelimited("%s: %d] NULL ptr buf_mgr\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ cam_smmu_put_phy_addr(buf_mgr->iommu_hdl,
+ fd);
+
+ return 0;
+}
+
+static int msm_isp_buf_prepare(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info, struct vb2_buffer *vb2_buf)
+{
+ int rc = -1;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ struct msm_isp_qbuf_buffer buf;
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr,
+ info->handle, info->buf_idx);
+ if (!buf_info) {
+ pr_err("Invalid buffer prepare\n");
+ return rc;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, buf_info->bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n",
+ __func__);
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ rc = buf_info->state;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+
+ if (buf_info->state != MSM_ISP_BUFFER_STATE_INITIALIZED) {
+ pr_err("%s: Invalid buffer state: %d bufq %x buf-id %d\n",
+ __func__, buf_info->state, bufq->bufq_handle,
+ buf_info->buf_idx);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+
+ if (vb2_buf) {
+ msm_isp_copy_planes_from_v4l2_buffer(&buf, &vb2_buf->v4l2_buf);
+ buf_info->vb2_buf = vb2_buf;
+ } else {
+ buf = info->buffer;
+ }
+
+ rc = msm_isp_prepare_v4l2_buf(buf_mgr, buf_info, &buf, bufq->stream_id);
+ if (rc < 0) {
+ pr_err_ratelimited("%s: Prepare buffer error\n", __func__);
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+}
+
+static int msm_isp_buf_unprepare_all(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t buf_handle)
+{
+ int rc = -1, i;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ bufq = msm_isp_get_bufq(buf_mgr, buf_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ for (i = 0; i < bufq->num_bufs; i++) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, buf_handle, i);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return rc;
+ }
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_UNUSED ||
+ buf_info->state ==
+ MSM_ISP_BUFFER_STATE_INITIALIZED)
+ continue;
+
+ if (MSM_ISP_BUFFER_SRC_HAL == BUF_SRC(bufq->stream_id)) {
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED ||
+ buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED)
+ buf_mgr->vb2_ops->put_buf(buf_info->vb2_buf,
+ bufq->session_id, bufq->stream_id);
+ }
+ msm_isp_unprepare_v4l2_buf(buf_mgr, buf_info, bufq->stream_id);
+ }
+ return 0;
+}
+
+static int msm_isp_get_buf_by_index(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct msm_isp_buffer **buf_info)
+{
+ int rc = -EINVAL;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *temp_buf_info;
+ uint32_t i = 0;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ if (buf_index >= bufq->num_bufs) {
+ pr_err("%s: Invalid buf index: %d max: %d\n", __func__,
+ buf_index, bufq->num_bufs);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+
+ *buf_info = NULL;
+ for (i = 0; bufq->num_bufs; i++) {
+ temp_buf_info = &bufq->bufs[i];
+ if (temp_buf_info && temp_buf_info->buf_idx == buf_index) {
+ *buf_info = temp_buf_info;
+ break;
+ }
+ }
+
+ if (*buf_info) {
+ pr_debug("Found buf in isp buf mgr");
+ rc = 0;
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+}
+
+static int msm_isp_buf_unprepare(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t buf_handle, int32_t buf_idx)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ bufq = msm_isp_get_bufq(buf_mgr, buf_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return -EINVAL;
+ }
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, buf_handle, buf_idx);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return -EINVAL;
+ }
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_UNUSED ||
+ buf_info->state == MSM_ISP_BUFFER_STATE_INITIALIZED)
+ return 0;
+
+ if (MSM_ISP_BUFFER_SRC_HAL == BUF_SRC(bufq->stream_id)) {
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED ||
+ buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED)
+ buf_mgr->vb2_ops->put_buf(buf_info->vb2_buf,
+ bufq->session_id, bufq->stream_id);
+ }
+ msm_isp_unprepare_v4l2_buf(buf_mgr, buf_info, bufq->stream_id);
+
+ return 0;
+}
+
+
+static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
+ uint32_t bufq_handle, struct msm_isp_buffer **buf_info)
+{
+ int rc = -1;
+ unsigned long flags;
+ struct msm_isp_buffer *temp_buf_info = NULL;
+ struct msm_isp_bufq *bufq = NULL;
+ struct vb2_buffer *vb2_buf = NULL;
+
+ if (buf_mgr->open_count == 0) {
+ pr_err_ratelimited("%s: bug mgr open cnt = 0\n",
+ __func__);
+ return 0;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err_ratelimited("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ if (!bufq->bufq_handle) {
+ pr_err_ratelimited("%s: Invalid bufq handle\n", __func__);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+
+ *buf_info = NULL;
+
+ switch (BUF_SRC(bufq->stream_id)) {
+ case MSM_ISP_BUFFER_SRC_NATIVE:
+ list_for_each_entry(temp_buf_info, &bufq->head, list) {
+ if (temp_buf_info->state ==
+ MSM_ISP_BUFFER_STATE_QUEUED) {
+ list_del_init(&temp_buf_info->list);
+ if (msm_buf_check_head_sanity(bufq) < 0) {
+ spin_unlock_irqrestore(
+ &bufq->bufq_lock, flags);
+ WARN(1, "%s buf_handle 0x%x buf_idx %d\n",
+ __func__,
+ bufq->bufq_handle,
+ temp_buf_info->buf_idx);
+ return -EFAULT;
+ }
+ *buf_info = temp_buf_info;
+ break;
+ }
+ }
+ break;
+ case MSM_ISP_BUFFER_SRC_HAL:
+ vb2_buf = buf_mgr->vb2_ops->get_buf(
+ bufq->session_id, bufq->stream_id);
+ if (vb2_buf) {
+ if (vb2_buf->v4l2_buf.index < bufq->num_bufs) {
+ *buf_info = &bufq->bufs[vb2_buf
+ ->v4l2_buf.index];
+ (*buf_info)->vb2_buf = vb2_buf;
+ } else {
+ pr_err("%s: Incorrect buf index %d\n",
+ __func__, vb2_buf->v4l2_buf.index);
+ rc = -EINVAL;
+ }
+ if ((*buf_info) == NULL) {
+ buf_mgr->vb2_ops->put_buf(vb2_buf,
+ bufq->session_id, bufq->stream_id);
+ pr_err("%s: buf index %d not found!\n",
+ __func__, vb2_buf->v4l2_buf.index);
+ rc = -EINVAL;
+
+ }
+ } else {
+ CDBG("%s: No HAL Buffer session_id: %d stream_id: %d\n",
+ __func__, bufq->session_id, bufq->stream_id);
+ rc = -EINVAL;
+ }
+ break;
+ case MSM_ISP_BUFFER_SRC_SCRATCH:
+ /* In scratch buf case we have only on buffer in queue.
+ * We return every time same buffer. */
+ *buf_info = list_entry(bufq->head.next, typeof(**buf_info),
+ list);
+ break;
+ default:
+ pr_err("%s: Incorrect buf source.\n", __func__);
+ rc = -EINVAL;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+ }
+
+ if (!(*buf_info)) {
+ rc = -ENOMEM;
+ } else {
+ (*buf_info)->state = MSM_ISP_BUFFER_STATE_DEQUEUED;
+ rc = 0;
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+}
+
+static int msm_isp_put_buf_unsafe(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index)
+{
+ int rc = -1;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return rc;
+ }
+
+ switch (buf_info->state) {
+ case MSM_ISP_BUFFER_STATE_PREPARED:
+ case MSM_ISP_BUFFER_STATE_DEQUEUED:
+ if (BUF_SRC(bufq->stream_id)) {
+ if (!list_empty(&buf_info->list)) {
+ WARN(1, "%s: buf %x/%x double add\n",
+ __func__, bufq_handle, buf_index);
+ return -EFAULT;
+ }
+ list_add_tail(&buf_info->list, &bufq->head);
+ if (msm_buf_check_head_sanity(bufq) < 0) {
+ WARN(1, "%s buf_handle 0x%x buf_idx %d\n",
+ __func__,
+ bufq->bufq_handle,
+ buf_info->buf_idx);
+ return -EFAULT;
+ }
+ } else {
+ buf_mgr->vb2_ops->put_buf(buf_info->vb2_buf,
+ bufq->session_id, bufq->stream_id);
+ }
+ buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
+ rc = 0;
+ break;
+ case MSM_ISP_BUFFER_STATE_DISPATCHED:
+ buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED;
+ rc = 0;
+ break;
+ case MSM_ISP_BUFFER_STATE_QUEUED:
+ case MSM_ISP_BUFFER_STATE_DIVERTED:
+ default:
+ WARN(1, "%s: bufq 0x%x, buf idx 0x%x, incorrect state = %d",
+ __func__, bufq_handle, buf_index, buf_info->state);
+ return -EFAULT;
+ }
+
+ return rc;
+}
+
+static int msm_isp_put_buf(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index)
+{
+ int rc = -1;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n", __func__);
+ return rc;
+ }
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+
+ rc = msm_isp_put_buf_unsafe(buf_mgr, bufq_handle, buf_index);
+
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+
+ return rc;
+}
+
+static int msm_isp_update_put_buf_cnt_unsafe(
+ struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t id, uint32_t bufq_handle, int32_t buf_index,
+ struct timeval *tv, uint32_t frame_id, uint32_t pingpong_bit)
+{
+ int rc = -1;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ uint8_t *put_buf_mask = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq\n");
+ return rc;
+ }
+
+ put_buf_mask = &bufq->put_buf_mask[pingpong_bit];
+
+ if (buf_index >= 0) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return -EFAULT;
+ }
+ if (buf_info->state != MSM_ISP_BUFFER_STATE_DEQUEUED) {
+ pr_err(
+ "%s: Invalid state, bufq_handle %x stream id %x, state %d\n",
+ __func__, bufq_handle,
+ bufq->stream_id, buf_info->state);
+ return -EFAULT;
+ }
+ if (buf_info->pingpong_bit != pingpong_bit) {
+ pr_err("%s: Pingpong bit mismatch\n", __func__);
+ return -EFAULT;
+ }
+ }
+
+ if (bufq->buf_type != ISP_SHARE_BUF ||
+ (*put_buf_mask == 0)) {
+ if (buf_info)
+ buf_info->frame_id = frame_id;
+ }
+
+ if (bufq->buf_type == ISP_SHARE_BUF &&
+ ((*put_buf_mask & (1 << id)) == 0)) {
+ *put_buf_mask |= (1 << id);
+ if (*put_buf_mask != ISP_SHARE_BUF_MASK) {
+ rc = *put_buf_mask;
+ return 1;
+ }
+ *put_buf_mask = 0;
+ rc = 0;
+ } else if (bufq->buf_type == ISP_SHARE_BUF &&
+ (*put_buf_mask & (1 << id)) != 0) {
+ return -ENOTEMPTY;
+ }
+
+ if (buf_info &&
+ MSM_ISP_BUFFER_SRC_NATIVE == BUF_SRC(bufq->stream_id)) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_DIVERTED;
+ buf_info->tv = tv;
+ }
+ return 0;
+}
+
+static int msm_isp_update_put_buf_cnt(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t id, uint32_t bufq_handle, int32_t buf_index,
+ struct timeval *tv, uint32_t frame_id, uint32_t pingpong_bit)
+{
+ int rc = -1;
+ struct msm_isp_bufq *bufq = NULL;
+ unsigned long flags;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq\n");
+ return rc;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ rc = msm_isp_update_put_buf_cnt_unsafe(buf_mgr, id, bufq_handle,
+ buf_index, tv, frame_id, pingpong_bit);
+ if (-ENOTEMPTY == rc) {
+ pr_err("%s: Error! Uncleared put_buf_mask for pingpong(%d) from vfe %d bufq 0x%x buf_idx %d\n",
+ __func__, pingpong_bit, id, bufq_handle, buf_index);
+ rc = -EFAULT;
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return rc;
+}
+
+static int msm_isp_buf_done(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t buf_index,
+ struct timeval *tv, uint32_t frame_id, uint32_t output_format)
+{
+ int rc = 0;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ enum msm_isp_buffer_state state;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq\n");
+ return -EINVAL;
+ }
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, buf_index);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ state = buf_info->state;
+
+ if (MSM_ISP_BUFFER_SRC_HAL == BUF_SRC(bufq->stream_id)) {
+ if (state == MSM_ISP_BUFFER_STATE_DEQUEUED) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_DISPATCHED;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ buf_mgr->vb2_ops->buf_done(buf_info->vb2_buf,
+ bufq->session_id, bufq->stream_id,
+ frame_id, tv, output_format);
+ } else {
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ }
+ goto done;
+ }
+
+ /*
+ * For native buffer put the diverted buffer back to queue since caller
+ * is not going to send it to CPP, this is error case like
+ * drop_frame/empty_buffer
+ */
+ if (state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ rc = msm_isp_put_buf_unsafe(buf_mgr, buf_info->bufq_handle,
+ buf_info->buf_idx);
+ if (rc < 0)
+ pr_err("%s: Buf put failed\n", __func__);
+ }
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+done:
+ return rc;
+}
+
+static int msm_isp_flush_buf(struct msm_isp_buf_mgr *buf_mgr, uint32_t id,
+ uint32_t bufq_handle, enum msm_isp_buffer_flush_t flush_type,
+ struct timeval *tv, uint32_t frame_id)
+{
+ int rc = 0, i;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+ unsigned long flags;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ for (i = 0; i < bufq->num_bufs; i++) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, bufq_handle, i);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ continue;
+ }
+ if (flush_type == MSM_ISP_BUFFER_FLUSH_DIVERTED &&
+ buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ msm_isp_put_buf_unsafe(buf_mgr,
+ bufq_handle, buf_info->buf_idx);
+ } else if (flush_type == MSM_ISP_BUFFER_FLUSH_ALL) {
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ CDBG("%s: no need to queue Diverted buffer\n",
+ __func__);
+ } else if (buf_info->state ==
+ MSM_ISP_BUFFER_STATE_DEQUEUED) {
+ rc = msm_isp_update_put_buf_cnt_unsafe(buf_mgr,
+ id, bufq_handle, buf_info->buf_idx, tv,
+ frame_id, buf_info->pingpong_bit);
+ if (-ENOTEMPTY == rc) {
+ rc = 0;
+ continue;
+ }
+
+ if (rc == 0) {
+ buf_info->buf_debug.put_state[
+ buf_info->buf_debug.
+ put_state_last]
+ = MSM_ISP_BUFFER_STATE_FLUSH;
+ buf_info->buf_debug.put_state_last ^= 1;
+ buf_info->state =
+ MSM_ISP_BUFFER_STATE_PREPARED;
+ rc = msm_isp_put_buf_unsafe(buf_mgr,
+ bufq_handle, buf_info->buf_idx);
+ if (rc == -EFAULT) {
+ spin_unlock_irqrestore(
+ &bufq->bufq_lock,
+ flags);
+ return rc;
+ }
+ }
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ return 0;
+}
+
+static int msm_isp_buf_enqueue(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info)
+{
+ int rc = 0, buf_state;
+ struct msm_isp_bufq *bufq = NULL;
+ struct msm_isp_buffer *buf_info = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, info->handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq, handle 0x%x, stream id %x num_plane %d\n"
+ , __func__, info->handle, (info->handle >> 8),
+ info->buffer.num_planes);
+ return -EINVAL;
+ }
+
+ buf_state = msm_isp_buf_prepare(buf_mgr, info, NULL);
+ if (buf_state < 0) {
+ pr_err_ratelimited("%s: Buf prepare failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (buf_state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr,
+ info->handle, info->buf_idx);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return -EINVAL;
+ }
+ if (info->dirty_buf) {
+ buf_info->buf_debug.put_state[
+ buf_info->buf_debug.put_state_last]
+ = MSM_ISP_BUFFER_STATE_PUT_BUF;
+ buf_info->buf_debug.put_state_last ^= 1;
+ buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED;
+ rc = msm_isp_put_buf(buf_mgr,
+ info->handle, info->buf_idx);
+ } else {
+ if (BUF_SRC(bufq->stream_id))
+ pr_err("%s: Invalid native buffer state\n",
+ __func__);
+ else {
+ buf_info->buf_debug.put_state[
+ buf_info->buf_debug.put_state_last] =
+ MSM_ISP_BUFFER_STATE_PUT_BUF;
+ buf_info->buf_debug.put_state_last ^= 1;
+ rc = msm_isp_buf_done(buf_mgr,
+ info->handle, info->buf_idx,
+ buf_info->tv, buf_info->frame_id, 0);
+ }
+ }
+ } else {
+ if (MSM_ISP_BUFFER_SRC_HAL != BUF_SRC(bufq->stream_id)) {
+ buf_info = msm_isp_get_buf_ptr(buf_mgr,
+ info->handle, info->buf_idx);
+ if (!buf_info) {
+ pr_err("%s: buf not found\n", __func__);
+ return -EINVAL;
+ }
+
+ buf_info->buf_debug.put_state[
+ buf_info->buf_debug.put_state_last] =
+ MSM_ISP_BUFFER_STATE_PUT_PREPARED;
+ buf_info->buf_debug.put_state_last ^= 1;
+ rc = msm_isp_put_buf(buf_mgr,
+ info->handle, info->buf_idx);
+ if (rc < 0) {
+ pr_err("%s: Buf put failed stream %x\n",
+ __func__, bufq->stream_id);
+ return rc;
+ }
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_buf_dequeue(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_qbuf_info *info)
+{
+ struct msm_isp_buffer *buf_info = NULL;
+ int rc = 0;
+
+ buf_info = msm_isp_get_buf_ptr(buf_mgr, info->handle, info->buf_idx);
+ if (!buf_info) {
+ pr_err("Invalid buffer dequeue\n");
+ return -EINVAL;
+ }
+
+ if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED ||
+ buf_info->state == MSM_ISP_BUFFER_STATE_DIVERTED) {
+ pr_err("%s: Invalid state %d\n", __func__, buf_info->state);
+ return -EINVAL;
+ }
+ msm_isp_buf_unprepare(buf_mgr, info->handle, info->buf_idx);
+
+ buf_info->state = MSM_ISP_BUFFER_STATE_INITIALIZED;
+
+ return rc;
+}
+
+static int msm_isp_get_bufq_handle(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t session_id, uint32_t stream_id)
+{
+ int i;
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ if (buf_mgr->bufq[i].session_id == session_id &&
+ buf_mgr->bufq[i].stream_id == stream_id) {
+ return buf_mgr->bufq[i].bufq_handle;
+ }
+ }
+ pr_err("%s: No match found 0x%x 0x%x\n", __func__,
+ session_id, stream_id);
+ return 0;
+}
+
+static int msm_isp_get_buf_src(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle, uint32_t *buf_src)
+{
+ struct msm_isp_bufq *bufq = NULL;
+
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq\n",
+ __func__);
+ return -EINVAL;
+ }
+ *buf_src = BUF_SRC(bufq->stream_id);
+
+ return 0;
+}
+
+static int msm_isp_request_bufq(struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_isp_buf_request *buf_request)
+{
+ int i;
+ struct msm_isp_bufq *bufq = NULL;
+ CDBG("%s: E\n", __func__);
+
+ if (!buf_request->num_buf || buf_request->num_buf > VB2_MAX_FRAME) {
+ pr_err("Invalid buffer request\n");
+ return -EINVAL;
+ }
+
+ buf_request->handle = msm_isp_get_buf_handle(buf_mgr,
+ buf_request->session_id, buf_request->stream_id);
+ if (!buf_request->handle) {
+ pr_err("Invalid buffer handle\n");
+ return -EINVAL;
+ }
+
+ bufq = msm_isp_get_bufq(buf_mgr, buf_request->handle);
+ if (!bufq) {
+ pr_err("%s: Invalid bufq stream_id %x\n",
+ __func__, buf_request->stream_id);
+
+ return -EINVAL;
+ }
+
+ bufq->bufs = kzalloc(sizeof(struct msm_isp_buffer) *
+ buf_request->num_buf, GFP_KERNEL);
+ if (!bufq->bufs) {
+ pr_err("No free memory for buf info\n");
+ msm_isp_free_bufq_handle(buf_mgr, buf_request->handle);
+ return -ENOMEM;
+ }
+ spin_lock_init(&bufq->bufq_lock);
+ bufq->bufq_handle = buf_request->handle;
+ bufq->session_id = buf_request->session_id;
+ bufq->stream_id = buf_request->stream_id;
+ bufq->num_bufs = buf_request->num_buf;
+ bufq->buf_type = buf_request->buf_type;
+ for (i = 0; i < ISP_NUM_BUF_MASK; i++)
+ bufq->put_buf_mask[i] = 0;
+ INIT_LIST_HEAD(&bufq->head);
+
+ for (i = 0; i < buf_request->num_buf; i++) {
+ bufq->bufs[i].state = MSM_ISP_BUFFER_STATE_INITIALIZED;
+ bufq->bufs[i].buf_debug.put_state[0] =
+ MSM_ISP_BUFFER_STATE_PUT_PREPARED;
+ bufq->bufs[i].buf_debug.put_state[1] =
+ MSM_ISP_BUFFER_STATE_PUT_PREPARED;
+ bufq->bufs[i].buf_debug.put_state_last = 0;
+ bufq->bufs[i].bufq_handle = bufq->bufq_handle;
+ bufq->bufs[i].buf_idx = i;
+ INIT_LIST_HEAD(&bufq->bufs[i].list);
+ }
+
+ return 0;
+}
+
+static int msm_isp_release_bufq(struct msm_isp_buf_mgr *buf_mgr,
+ uint32_t bufq_handle)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ unsigned long flags;
+ bufq = msm_isp_get_bufq(buf_mgr, bufq_handle);
+ if (!bufq) {
+ pr_err("Invalid bufq release\n");
+ return -EINVAL;
+ }
+
+ msm_isp_buf_unprepare_all(buf_mgr, bufq_handle);
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ kfree(bufq->bufs);
+ msm_isp_free_bufq_handle(buf_mgr, bufq_handle);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+
+ return 0;
+}
+
+static void msm_isp_release_all_bufq(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ struct msm_isp_bufq *bufq = NULL;
+ unsigned long flags;
+ int i;
+ for (i = 0; i < buf_mgr->num_buf_q; i++) {
+ bufq = &buf_mgr->bufq[i];
+ if (!bufq->bufq_handle)
+ continue;
+
+ msm_isp_buf_unprepare_all(buf_mgr, bufq->bufq_handle);
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ kfree(bufq->bufs);
+ msm_isp_free_bufq_handle(buf_mgr, bufq->bufq_handle);
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ }
+}
+
+
+/**
+ * msm_isp_buf_put_scratch() - Release scratch buffers
+ * @buf_mgr: The buffer structure for h/w
+ *
+ * Returns 0 on success else error code
+ */
+static int msm_isp_buf_put_scratch(struct msm_isp_buf_mgr *buf_mgr)
+{
+ int rc;
+
+ if (!buf_mgr->scratch_buf_addr)
+ return 0;
+
+ rc = cam_smmu_put_phy_addr_scratch(buf_mgr->iommu_hdl,
+ buf_mgr->scratch_buf_addr);
+ if (rc)
+ pr_err("%s: failed to put scratch buffer to img iommu: %d\n",
+ __func__, rc);
+
+
+ if (!rc)
+ buf_mgr->scratch_buf_addr = 0;
+
+ return rc;
+}
+
+/**
+ * msm_isp_buf_get_scratch() - Create scratch buffers
+ * @buf_mgr: The buffer structure for h/w
+ *
+ * Create and map scratch buffers for all IOMMU's under the buffer
+ * manager.
+ *
+ * Returns 0 on success else error code
+ */
+static int msm_isp_buf_get_scratch(struct msm_isp_buf_mgr *buf_mgr)
+{
+ int rc;
+
+ if (buf_mgr->scratch_buf_addr || !buf_mgr->scratch_buf_range)
+ /* already mapped or not supported */
+ return 0;
+
+ rc = cam_smmu_get_phy_addr_scratch(
+ buf_mgr->iommu_hdl,
+ CAM_SMMU_MAP_RW,
+ &buf_mgr->scratch_buf_addr,
+ buf_mgr->scratch_buf_range,
+ SZ_4K);
+ if (rc) {
+ pr_err("%s: failed to map scratch buffer to img iommu: %d\n",
+ __func__, rc);
+ return rc;
+ }
+ return rc;
+}
+
+int msm_isp_smmu_attach(struct msm_isp_buf_mgr *buf_mgr,
+ void *arg)
+{
+ struct msm_vfe_smmu_attach_cmd *cmd = arg;
+ int rc = 0;
+
+ pr_debug("%s: cmd->security_mode : %d\n", __func__, cmd->security_mode);
+ mutex_lock(&buf_mgr->lock);
+ if (cmd->iommu_attach_mode == IOMMU_ATTACH) {
+ buf_mgr->secure_enable = cmd->security_mode;
+
+ /*
+ * Call hypervisor thru scm call to notify secure or
+ * non-secure mode
+ */
+ if (buf_mgr->attach_ref_cnt == 0) {
+ rc = cam_smmu_ops(buf_mgr->iommu_hdl,
+ CAM_SMMU_ATTACH);
+ if (rc < 0) {
+ pr_err("%s: img smmu attach error, rc :%d\n",
+ __func__, rc);
+ goto err1;
+ }
+ }
+ buf_mgr->attach_ref_cnt++;
+ rc = msm_isp_buf_get_scratch(buf_mgr);
+ if (rc)
+ goto err2;
+ } else {
+ if (buf_mgr->attach_ref_cnt > 0)
+ buf_mgr->attach_ref_cnt--;
+ else
+ pr_err("%s: Error! Invalid ref_cnt %d\n",
+ __func__, buf_mgr->attach_ref_cnt);
+
+ if (buf_mgr->attach_ref_cnt == 0) {
+ rc = msm_isp_buf_put_scratch(buf_mgr);
+ rc |= cam_smmu_ops(buf_mgr->iommu_hdl,
+ CAM_SMMU_DETACH);
+ if (rc < 0) {
+ pr_err("%s: img/stats smmu detach error, rc :%d\n",
+ __func__, rc);
+ }
+ }
+ }
+
+ mutex_unlock(&buf_mgr->lock);
+ return rc;
+
+err2:
+ if (cam_smmu_ops(buf_mgr->iommu_hdl, CAM_SMMU_DETACH))
+ pr_err("%s: img smmu detach error\n", __func__);
+err1:
+ mutex_unlock(&buf_mgr->lock);
+ return rc;
+}
+
+
+static int msm_isp_init_isp_buf_mgr(struct msm_isp_buf_mgr *buf_mgr,
+ const char *ctx_name)
+{
+ int rc = -1;
+ int i = 0;
+ mutex_lock(&buf_mgr->lock);
+ if (buf_mgr->open_count++) {
+ mutex_unlock(&buf_mgr->lock);
+ return 0;
+ }
+
+ CDBG("%s: E\n", __func__);
+ buf_mgr->attach_ref_cnt = 0;
+
+ buf_mgr->num_buf_q = BUF_MGR_NUM_BUF_Q;
+ memset(buf_mgr->bufq, 0, sizeof(buf_mgr->bufq));
+
+ rc = cam_smmu_get_handle("vfe", &buf_mgr->iommu_hdl);
+ if (rc < 0) {
+ pr_err("vfe get handle failed\n");
+ goto get_handle_error;
+ }
+
+ for (i = 0; i < BUF_MGR_NUM_BUF_Q; i++)
+ spin_lock_init(&buf_mgr->bufq[i].bufq_lock);
+
+ buf_mgr->pagefault_debug_disable = 0;
+ buf_mgr->frameId_mismatch_recovery = 0;
+ mutex_unlock(&buf_mgr->lock);
+ return 0;
+
+get_handle_error:
+ mutex_unlock(&buf_mgr->lock);
+ return rc;
+}
+
+static int msm_isp_deinit_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr)
+{
+ mutex_lock(&buf_mgr->lock);
+ if (buf_mgr->open_count > 0)
+ buf_mgr->open_count--;
+
+ if (buf_mgr->open_count) {
+ mutex_unlock(&buf_mgr->lock);
+ return 0;
+ }
+ msm_isp_release_all_bufq(buf_mgr);
+ buf_mgr->num_buf_q = 0;
+ buf_mgr->pagefault_debug_disable = 0;
+
+ msm_isp_buf_put_scratch(buf_mgr);
+ cam_smmu_ops(buf_mgr->iommu_hdl, CAM_SMMU_DETACH);
+ cam_smmu_destroy_handle(buf_mgr->iommu_hdl);
+
+ buf_mgr->attach_ref_cnt = 0;
+ mutex_unlock(&buf_mgr->lock);
+ return 0;
+}
+
+int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_MSM_ISP_REQUEST_BUF: {
+ struct msm_isp_buf_request *buf_req = arg;
+
+ buf_mgr->ops->request_buf(buf_mgr, buf_req);
+ break;
+ }
+ case VIDIOC_MSM_ISP_ENQUEUE_BUF: {
+ struct msm_isp_qbuf_info *qbuf_info = arg;
+
+ buf_mgr->ops->enqueue_buf(buf_mgr, qbuf_info);
+ break;
+ }
+ case VIDIOC_MSM_ISP_DEQUEUE_BUF: {
+ struct msm_isp_qbuf_info *qbuf_info = arg;
+
+ buf_mgr->ops->dequeue_buf(buf_mgr, qbuf_info);
+ break;
+ }
+ case VIDIOC_MSM_ISP_RELEASE_BUF: {
+ struct msm_isp_buf_request *buf_req = arg;
+
+ buf_mgr->ops->release_buf(buf_mgr, buf_req->handle);
+ break;
+ }
+ case VIDIOC_MSM_ISP_UNMAP_BUF: {
+ struct msm_isp_unmap_buf_req *unmap_req = arg;
+
+ buf_mgr->ops->unmap_buf(buf_mgr, unmap_req->fd);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr,
+ unsigned long fault_addr)
+{
+ struct msm_isp_buffer *bufs = NULL;
+ uint32_t i = 0, j = 0, k = 0, rc = 0;
+ char *print_buf = NULL, temp_buf[100];
+ uint32_t start_addr = 0, end_addr = 0, print_buf_size = 2000;
+ int buf_addr_delta = -1;
+ int temp_delta = 0;
+ uint32_t debug_stream_id = 0;
+ uint32_t debug_buf_idx = 0;
+ uint32_t debug_buf_plane = 0;
+ uint32_t debug_start_addr = 0;
+ uint32_t debug_end_addr = 0;
+ uint32_t debug_frame_id = 0;
+ enum msm_isp_buffer_state debug_state;
+ unsigned long flags;
+ struct msm_isp_bufq *bufq = NULL;
+
+ if (!buf_mgr) {
+ pr_err_ratelimited("%s: %d] NULL buf_mgr\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < BUF_MGR_NUM_BUF_Q; i++) {
+ bufq = &buf_mgr->bufq[i];
+
+ spin_lock_irqsave(&bufq->bufq_lock, flags);
+ if (!bufq->bufq_handle) {
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ continue;
+ }
+
+ for (j = 0; j < bufq->num_bufs; j++) {
+ bufs = &bufq->bufs[j];
+ if (!bufs)
+ continue;
+
+ for (k = 0; k < bufs->num_planes; k++) {
+ start_addr = bufs->
+ mapped_info[k].paddr;
+ end_addr = bufs->mapped_info[k].paddr +
+ bufs->mapped_info[k].len - 1;
+ temp_delta = fault_addr - start_addr;
+ if (temp_delta < 0)
+ continue;
+
+ if (buf_addr_delta == -1 ||
+ temp_delta < buf_addr_delta) {
+ buf_addr_delta = temp_delta;
+ debug_stream_id = bufq->stream_id;
+ debug_buf_idx = j;
+ debug_buf_plane = k;
+ debug_start_addr = start_addr;
+ debug_end_addr = end_addr;
+ debug_frame_id = bufs->frame_id;
+ debug_state = bufs->state;
+ }
+ }
+ }
+ start_addr = 0;
+ end_addr = 0;
+ spin_unlock_irqrestore(&bufq->bufq_lock, flags);
+ }
+
+ pr_err("%s: ==== SMMU page fault addr %lx ====\n", __func__,
+ fault_addr);
+ pr_err("%s: nearby stream id %x, frame_id %d\n", __func__,
+ debug_stream_id, debug_frame_id);
+ pr_err("%s: nearby buf index %d, plane %d, state %d\n", __func__,
+ debug_buf_idx, debug_buf_plane, debug_state);
+ pr_err("%s: buf address 0x%x -- 0x%x\n", __func__,
+ debug_start_addr, debug_end_addr);
+
+ if (BUF_DEBUG_FULL) {
+ print_buf = kzalloc(print_buf_size, GFP_ATOMIC);
+ if (!print_buf) {
+ pr_err("%s failed: No memory", __func__);
+ return -ENOMEM;
+ }
+ snprintf(print_buf, print_buf_size, "%s\n", __func__);
+ for (i = 0; i < BUF_MGR_NUM_BUF_Q; i++) {
+ if (i % 2 == 0 && i > 0) {
+ pr_err("%s\n", print_buf);
+ print_buf[0] = 0;
+ }
+ if (buf_mgr->bufq[i].bufq_handle != 0) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "handle %x stream %x num_bufs %d\n",
+ buf_mgr->bufq[i].bufq_handle,
+ buf_mgr->bufq[i].stream_id,
+ buf_mgr->bufq[i].num_bufs);
+ strlcat(print_buf, temp_buf, print_buf_size);
+ for (j = 0; j < buf_mgr->bufq[i].num_bufs;
+ j++) {
+ bufs = &buf_mgr->bufq[i].bufs[j];
+ if (!bufs)
+ break;
+
+ for (k = 0; k < bufs->num_planes; k++) {
+ start_addr = bufs->
+ mapped_info[k].paddr;
+ end_addr = bufs->mapped_info[k].
+ paddr + bufs->
+ mapped_info[k].len;
+ snprintf(temp_buf,
+ sizeof(temp_buf),
+ " buf %d plane %d start_addr %x end_addr %x\n",
+ j, k, start_addr,
+ end_addr);
+ strlcat(print_buf, temp_buf,
+ print_buf_size);
+ }
+ }
+ start_addr = 0;
+ end_addr = 0;
+ }
+ }
+ pr_err("%s\n", print_buf);
+ kfree(print_buf);
+ }
+ return rc;
+}
+
+static struct msm_isp_buf_ops isp_buf_ops = {
+ .request_buf = msm_isp_request_bufq,
+ .enqueue_buf = msm_isp_buf_enqueue,
+ .dequeue_buf = msm_isp_buf_dequeue,
+ .release_buf = msm_isp_release_bufq,
+ .get_bufq_handle = msm_isp_get_bufq_handle,
+ .get_buf_src = msm_isp_get_buf_src,
+ .get_buf = msm_isp_get_buf,
+ .get_buf_by_index = msm_isp_get_buf_by_index,
+ .map_buf = msm_isp_map_buf,
+ .unmap_buf = msm_isp_unmap_buf,
+ .put_buf = msm_isp_put_buf,
+ .flush_buf = msm_isp_flush_buf,
+ .buf_done = msm_isp_buf_done,
+ .buf_mgr_init = msm_isp_init_isp_buf_mgr,
+ .buf_mgr_deinit = msm_isp_deinit_isp_buf_mgr,
+ .buf_mgr_debug = msm_isp_buf_mgr_debug,
+ .get_bufq = msm_isp_get_bufq,
+ .update_put_buf_cnt = msm_isp_update_put_buf_cnt,
+};
+
+int msm_isp_create_isp_buf_mgr(
+ struct msm_isp_buf_mgr *buf_mgr,
+ struct msm_sd_req_vb2_q *vb2_ops,
+ struct device *dev,
+ uint32_t scratch_buf_range)
+{
+ int rc = 0;
+ if (buf_mgr->init_done)
+ return rc;
+
+ buf_mgr->ops = &isp_buf_ops;
+ buf_mgr->vb2_ops = vb2_ops;
+ buf_mgr->open_count = 0;
+ buf_mgr->pagefault_debug_disable = 0;
+ buf_mgr->secure_enable = NON_SECURE_MODE;
+ buf_mgr->attach_state = MSM_ISP_BUF_MGR_DETACH;
+ buf_mgr->scratch_buf_range = scratch_buf_range;
+ mutex_init(&buf_mgr->lock);
+
+ return 0;
+}