diff --git a/Documentation/devicetree/bindings/media/video/msm-vfe.txt b/Documentation/devicetree/bindings/media/video/msm-vfe.txt index 7a70cacf6df..f02f35e0401 100644 --- a/Documentation/devicetree/bindings/media/video/msm-vfe.txt +++ b/Documentation/devicetree/bindings/media/video/msm-vfe.txt @@ -4,6 +4,7 @@ Required properties: - cell-index: vfe hardware core index - compatible : - "qcom,vfe" + - "qcom,vfe40" - reg : offset and length of the register set for the device for the vfe operating in compatible mode. - reg-names : should specify relevant names to each reg property defined. diff --git a/drivers/media/video/msmb/Makefile b/drivers/media/video/msmb/Makefile index 860dcdb2750..39861285a5d 100644 --- a/drivers/media/video/msmb/Makefile +++ b/drivers/media/video/msmb/Makefile @@ -10,3 +10,5 @@ obj-$(CONFIG_MSMB_CAMERA) += msm.o obj-$(CONFIG_MSMB_CAMERA) += camera/ obj-$(CONFIG_MSMB_CAMERA) += msm_vb2/ obj-$(CONFIG_MSMB_CAMERA) += sensor/ +obj-$(CONFIG_MSMB_CAMERA) += isp/ +obj-$(CONFIG_MSMB_CAMERA) += ispif/ diff --git a/drivers/media/video/msmb/isp/Makefile b/drivers/media/video/msmb/isp/Makefile new file mode 100644 index 00000000000..e517798ef79 --- /dev/null +++ b/drivers/media/video/msmb/isp/Makefile @@ -0,0 +1,4 @@ +ccflags-y += -Idrivers/media/video/msmb +ccflags-y += -Idrivers/media/video/msmb/sensor/io +obj-$(CONFIG_MSMB_CAMERA) += msm_isp.o msm_buf_mgr.o msm_isp_util.o msm_isp_axi_util.o msm_isp_stats_util.o +obj-$(CONFIG_MSMB_CAMERA) += msm_isp40.o diff --git a/drivers/media/video/msmb/isp/msm_buf_mgr.c b/drivers/media/video/msmb/isp/msm_buf_mgr.c new file mode 100644 index 00000000000..aab97d7ca2c --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_buf_mgr.c @@ -0,0 +1,604 @@ +/* Copyright (c) 2013, 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 +#include +#include +#include + +#include + +#include "msm.h" +#include "msm_buf_mgr.h" + +/*#define CONFIG_MSM_ISP_DBG*/ +#undef CDBG +#ifdef CONFIG_MSM_ISP_DBG +#define CDBG(fmt, args...) pr_err(fmt, ##args) +#else +#define CDBG(fmt, args...) do { } while (0) +#endif + +static 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; + if (bufq_index > buf_mgr->num_buf_q) + return bufq; + + 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) +{ + int i; + if ((buf_mgr->buf_handle_cnt << 8) == 0) + buf_mgr->buf_handle_cnt++; + + for (i = 0; i < buf_mgr->num_buf_q; i++) { + if (buf_mgr->bufq[i].bufq_handle == 0) { + memset(&buf_mgr->bufq[i], + 0, sizeof(struct msm_isp_bufq)); + buf_mgr->bufq[i].bufq_handle = + (++buf_mgr->buf_handle_cnt) << 8 | i; + return buf_mgr->bufq[i].bufq_handle; + } + } + return 0; +} + +static int msm_isp_free_buf_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; + memset(bufq, 0, sizeof(struct msm_isp_bufq)); + return 0; +} + +static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr, + struct msm_isp_buffer *buf_info, + struct v4l2_buffer *v4l2_buf) +{ + int i, rc = -1; + struct msm_isp_buffer_mapped_info *mapped_info; + for (i = 0; i < v4l2_buf->length; i++) { + mapped_info = &buf_info->mapped_info[i]; + mapped_info->handle = + ion_import_dma_buf(buf_mgr->client, + v4l2_buf->m.planes[i].m.userptr); + if (IS_ERR_OR_NULL(mapped_info->handle)) { + pr_err("%s: buf has null/error ION handle %p\n", + __func__, mapped_info->handle); + goto ion_map_error; + } + if (ion_map_iommu(buf_mgr->client, mapped_info->handle, + buf_mgr->iommu_domain_num, 0, SZ_4K, + 0, &(mapped_info->paddr), + &(mapped_info->len), 0, 0) < 0) { + rc = -EINVAL; + pr_err("%s: cannot map address", __func__); + ion_free(buf_mgr->client, mapped_info->handle); + goto ion_map_error; + } + mapped_info->paddr += v4l2_buf->m.planes[i].data_offset; + CDBG("%s: plane: %d addr:%lu\n", + __func__, i, mapped_info->paddr); + } + buf_info->num_planes = v4l2_buf->length; + return 0; +ion_map_error: + for (--i; i >= 0; i--) { + mapped_info = &buf_info->mapped_info[i]; + ion_unmap_iommu(buf_mgr->client, mapped_info->handle, + buf_mgr->iommu_domain_num, 0); + ion_free(buf_mgr->client, mapped_info->handle); + } + return rc; +} + +static void msm_isp_unprepare_v4l2_buf( + struct msm_isp_buf_mgr *buf_mgr, + struct msm_isp_buffer *buf_info) +{ + int i; + struct msm_isp_buffer_mapped_info *mapped_info; + for (i = 0; i < buf_info->num_planes; i++) { + mapped_info = &buf_info->mapped_info[i]; + ion_unmap_iommu(buf_mgr->client, mapped_info->handle, + buf_mgr->iommu_domain_num, 0); + ion_free(buf_mgr->client, mapped_info->handle); + } + return; +} + +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; + struct msm_isp_buffer *buf_info = NULL; + struct v4l2_buffer *buf = NULL; + struct v4l2_plane *plane = NULL; + + 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; + } + + if (buf_info->state == MSM_ISP_BUFFER_STATE_UNUSED || + buf_info->state != MSM_ISP_BUFFER_STATE_INITIALIZED) { + pr_err("%s: Invalid buffer state: %d\n", + __func__, buf_info->state); + return rc; + } + + if (vb2_buf) { + buf = &vb2_buf->v4l2_buf; + buf_info->vb2_buf = vb2_buf; + } else { + buf = &info->buffer; + plane = + kzalloc(sizeof(struct v4l2_plane) * buf->length, + GFP_KERNEL); + if (!plane) { + pr_err("%s: Cannot alloc plane: %d\n", + __func__, buf_info->state); + return rc; + } + if (copy_from_user(plane, + (void __user *)(buf->m.planes), + sizeof(struct v4l2_plane) * buf->length)) { + kfree(plane); + return rc; + } + buf->m.planes = plane; + } + + rc = msm_isp_prepare_v4l2_buf(buf_mgr, buf_info, buf); + if (rc < 0) { + pr_err("%s: Prepare buffer error\n", __func__); + kfree(plane); + return rc; + } + buf_info->state = MSM_ISP_BUFFER_STATE_PREPARED; + kfree(plane); + return rc; +} + +static int msm_isp_buf_unprepare(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->state == MSM_ISP_BUFFER_STATE_UNUSED || + buf_info->state == + MSM_ISP_BUFFER_STATE_INITIALIZED) + continue; + msm_isp_unprepare_v4l2_buf(buf_mgr, buf_info); + } + return 0; +} + +static int msm_isp_get_buf(struct msm_isp_buf_mgr *buf_mgr, + uint32_t bufq_handle, struct msm_isp_buffer **buf_info) +{ + int rc = -1; + struct msm_isp_buffer *temp_buf_info; + struct msm_isp_bufq *bufq = NULL; + struct vb2_buffer *vb2_buf = NULL; + bufq = msm_isp_get_bufq(buf_mgr, bufq_handle); + if (!bufq) { + pr_err("%s: Invalid bufq\n", __func__); + return rc; + } + + *buf_info = NULL; + if (BUF_SRC(bufq->stream_id)) { + list_for_each_entry(temp_buf_info, &bufq->head, list) { + if (temp_buf_info->state == + MSM_ISP_BUFFER_STATE_QUEUED) { + /* found one buf */ + list_del_init(&temp_buf_info->list); + *buf_info = temp_buf_info; + break; + } + } + } else { + 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); + return -EINVAL; + } + } + } + + if (!(*buf_info)) { + pr_err("%s: No free buffer\n", __func__); + return rc; + } + + (*buf_info)->state = MSM_ISP_BUFFER_STATE_DEQUEUED; + return 0; +} + +static int msm_isp_put_buf(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: + case MSM_ISP_BUFFER_STATE_DISPATCHED: + if (BUF_SRC(bufq->stream_id)) + list_add_tail(&buf_info->list, &bufq->head); + else + buf_mgr->vb2_ops->put_buf(buf_info->vb2_buf); + buf_info->state = MSM_ISP_BUFFER_STATE_QUEUED; + rc = 0; + break; + default: + pr_err("%s: incorrect state = %d", + __func__, buf_info->state); + break; + } + + 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) +{ + 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("Invalid bufq\n"); + 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; + } + + if (buf_info->state == MSM_ISP_BUFFER_STATE_DEQUEUED) { + buf_info->state = MSM_ISP_BUFFER_STATE_DISPATCHED; + if (!(BUF_SRC(bufq->stream_id))) { + buf_info->vb2_buf->v4l2_buf.timestamp = *tv; + buf_info->vb2_buf->v4l2_buf.sequence = frame_id; + buf_mgr->vb2_ops->buf_done(buf_info->vb2_buf); + } + } + + return 0; +} + +static int msm_isp_buf_enqueue(struct msm_isp_buf_mgr *buf_mgr, + struct msm_isp_qbuf_info *info) +{ + int rc; + struct msm_isp_bufq *bufq = NULL; + rc = msm_isp_buf_prepare(buf_mgr, info, NULL); + if (rc < 0) { + pr_err("%s: Buf prepare failed\n", __func__); + return rc; + } + + bufq = msm_isp_get_bufq(buf_mgr, info->handle); + if (BUF_SRC(bufq->stream_id)) { + rc = msm_isp_put_buf(buf_mgr, info->handle, info->buf_idx); + if (rc < 0) { + pr_err("%s: Buf put failed\n", __func__); + return rc; + } + } + + 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; + } + } + return 0; +} + +static int msm_isp_request_bufq(struct msm_isp_buf_mgr *buf_mgr, + struct msm_isp_buf_request *buf_request) +{ + int rc = -1, i; + struct msm_isp_bufq *bufq = NULL; + CDBG("%s: E\n", __func__); + + if (!buf_request->num_buf) { + pr_err("Invalid buffer request\n"); + return rc; + } + + buf_request->handle = msm_isp_get_buf_handle(buf_mgr); + if (!buf_request->handle) { + pr_err("Invalid buffer handle\n"); + return rc; + } + + bufq = msm_isp_get_bufq(buf_mgr, buf_request->handle); + if (!bufq) { + pr_err("Invalid buffer queue\n"); + return rc; + } + + 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_buf_handle(buf_mgr, buf_request->handle); + return rc; + } + + 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; + 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].bufq_handle = bufq->bufq_handle; + bufq->bufs[i].buf_idx = i; + } + + 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; + int rc = -1; + bufq = msm_isp_get_bufq(buf_mgr, bufq_handle); + if (!bufq) { + pr_err("Invalid bufq release\n"); + return rc; + } + + msm_isp_buf_unprepare(buf_mgr, bufq_handle); + + kfree(bufq->bufs); + msm_isp_free_buf_handle(buf_mgr, bufq_handle); + return 0; +} + +static void msm_isp_release_all_bufq( + struct msm_isp_buf_mgr *buf_mgr) +{ + struct msm_isp_bufq *bufq = NULL; + 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(buf_mgr, bufq->bufq_handle); + kfree(bufq->bufs); + msm_isp_free_buf_handle(buf_mgr, bufq->bufq_handle); + } +} + +static int msm_isp_attach_ctx(struct msm_isp_buf_mgr *buf_mgr, + struct device *iommu_ctx) +{ + int rc; + rc = iommu_attach_device(buf_mgr->iommu_domain, iommu_ctx); + if (rc) { + pr_err("%s: Iommu attach error\n", __func__); + return -EINVAL; + } + return 0; +} + +static void msm_isp_detach_ctx(struct msm_isp_buf_mgr *buf_mgr, + struct device *iommu_ctx) +{ + iommu_detach_device(buf_mgr->iommu_domain, iommu_ctx); +} + +static int msm_isp_init_isp_buf_mgr( + struct msm_isp_buf_mgr *buf_mgr, + const char *ctx_name, uint16_t num_buf_q) +{ + int rc = -1; + if (!num_buf_q) { + pr_err("Invalid buffer queue number\n"); + return rc; + } + + CDBG("%s: E\n", __func__); + buf_mgr->num_buf_q = num_buf_q; + buf_mgr->bufq = + kzalloc(sizeof(struct msm_isp_bufq) * num_buf_q, + GFP_KERNEL); + if (!buf_mgr->bufq) { + pr_err("Bufq malloc error\n"); + goto bufq_error; + } + buf_mgr->client = msm_ion_client_create(-1, ctx_name); + buf_mgr->buf_handle_cnt = 0; + + return 0; +bufq_error: + return rc; +} + +static int msm_isp_deinit_isp_buf_mgr( + struct msm_isp_buf_mgr *buf_mgr) +{ + msm_isp_release_all_bufq(buf_mgr); + ion_client_destroy(buf_mgr->client); + kfree(buf_mgr->bufq); + buf_mgr->num_buf_q = 0; + 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_RELEASE_BUF: { + struct msm_isp_buf_request *buf_req = arg; + buf_mgr->ops->release_buf(buf_mgr, buf_req->handle); + break; + } + } + return 0; +} + +static struct msm_isp_buf_ops isp_buf_ops = { + .request_buf = msm_isp_request_bufq, + .enqueue_buf = msm_isp_buf_enqueue, + .release_buf = msm_isp_release_bufq, + .get_bufq_handle = msm_isp_get_bufq_handle, + .get_buf = msm_isp_get_buf, + .put_buf = msm_isp_put_buf, + .buf_done = msm_isp_buf_done, + .attach_ctx = msm_isp_attach_ctx, + .detach_ctx = msm_isp_detach_ctx, + .buf_mgr_init = msm_isp_init_isp_buf_mgr, + .buf_mgr_deinit = msm_isp_deinit_isp_buf_mgr, +}; + +int msm_isp_create_isp_buf_mgr( + struct msm_isp_buf_mgr *buf_mgr, + struct msm_sd_req_vb2_q *vb2_ops, + struct msm_iova_layout *iova_layout) +{ + int rc = 0; + if (buf_mgr->init_done) + return rc; + + buf_mgr->iommu_domain_num = msm_register_domain(iova_layout); + if (buf_mgr->iommu_domain_num < 0) { + pr_err("%s: Invalid iommu domain number\n", __func__); + rc = -1; + goto iommu_domain_error; + } + + buf_mgr->iommu_domain = msm_get_iommu_domain( + buf_mgr->iommu_domain_num); + if (!buf_mgr->iommu_domain) { + pr_err("%s: Invalid iommu domain\n", __func__); + rc = -1; + goto iommu_domain_error; + } + + buf_mgr->ops = &isp_buf_ops; + buf_mgr->vb2_ops = vb2_ops; + buf_mgr->init_done = 1; + buf_mgr->ref_count = 0; + return 0; +iommu_domain_error: + return rc; +} diff --git a/drivers/media/video/msmb/isp/msm_buf_mgr.h b/drivers/media/video/msmb/isp/msm_buf_mgr.h new file mode 100644 index 00000000000..a44b5eca2fc --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_buf_mgr.h @@ -0,0 +1,123 @@ +/* Copyright (c) 2013, 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_BUF_H_ +#define _MSM_ISP_BUF_H_ + +#include +#include +#include "msm_sd.h" + +#define BUF_SRC_SHIFT 16 +/*Buffer source can be from userspace / HAL*/ +#define BUF_SRC(id) (id >> BUF_SRC_SHIFT) + +struct msm_isp_buf_mgr; + +enum msm_isp_buffer_state { + MSM_ISP_BUFFER_STATE_UNUSED, /* not used */ + MSM_ISP_BUFFER_STATE_INITIALIZED, /* REQBUF done */ + MSM_ISP_BUFFER_STATE_PREPARED, /* BUF mapped */ + MSM_ISP_BUFFER_STATE_QUEUED, /* buf queued */ + MSM_ISP_BUFFER_STATE_DEQUEUED, /* in use in VFE */ + MSM_ISP_BUFFER_STATE_DISPATCHED, /* sent to userspace */ +}; + +struct msm_isp_buffer_mapped_info { + unsigned long len; + unsigned long paddr; + struct ion_handle *handle; +}; + +struct msm_isp_buffer { + /*Common Data structure*/ + int num_planes; + struct msm_isp_buffer_mapped_info mapped_info[VIDEO_MAX_PLANES]; + int buf_idx; + uint32_t bufq_handle; + + /*Native buffer*/ + struct list_head list; + enum msm_isp_buffer_state state; + + /*Vb2 buffer data*/ + struct vb2_buffer *vb2_buf; + +}; + +struct msm_isp_bufq { + uint32_t session_id; + uint32_t stream_id; + uint32_t num_bufs; + uint32_t bufq_handle; + struct msm_isp_buffer *bufs; + + /*Native buffer queue*/ + struct list_head head; +}; + +struct msm_isp_buf_ops { + int (*request_buf) (struct msm_isp_buf_mgr *buf_mgr, + struct msm_isp_buf_request *buf_request); + + int (*enqueue_buf) (struct msm_isp_buf_mgr *buf_mgr, + struct msm_isp_qbuf_info *info); + + int (*release_buf) (struct msm_isp_buf_mgr *buf_mgr, + uint32_t bufq_handle); + + int (*get_bufq_handle) (struct msm_isp_buf_mgr *buf_mgr, + uint32_t session_id, uint32_t stream_id); + + int (*get_buf) (struct msm_isp_buf_mgr *buf_mgr, + uint32_t bufq_handle, struct msm_isp_buffer **buf_info); + + int (*put_buf) (struct msm_isp_buf_mgr *buf_mgr, + uint32_t bufq_handle, uint32_t buf_index); + + int (*buf_done) (struct msm_isp_buf_mgr *buf_mgr, + uint32_t bufq_handle, uint32_t buf_index, + struct timeval *tv, uint32_t frame_id); + int (*attach_ctx) (struct msm_isp_buf_mgr *buf_mgr, + struct device *iommu_ctx); + void (*detach_ctx) (struct msm_isp_buf_mgr *buf_mgr, + struct device *iommu_ctx); + int (*buf_mgr_init) (struct msm_isp_buf_mgr *buf_mgr, + const char *ctx_name, uint16_t num_buf_q); + int (*buf_mgr_deinit) (struct msm_isp_buf_mgr *buf_mgr); +}; + +struct msm_isp_buf_mgr { + int init_done; + uint32_t ref_count; + spinlock_t lock; + uint16_t num_buf_q; + struct msm_isp_bufq *bufq; + + struct ion_client *client; + struct msm_isp_buf_ops *ops; + uint32_t buf_handle_cnt; + + struct msm_sd_req_vb2_q *vb2_ops; + + /*IOMMU specific*/ + int iommu_domain_num; + struct iommu_domain *iommu_domain; +}; + +int msm_isp_create_isp_buf_mgr(struct msm_isp_buf_mgr *buf_mgr, + struct msm_sd_req_vb2_q *vb2_ops, struct msm_iova_layout *iova_layout); + +int msm_isp_proc_buf_cmd(struct msm_isp_buf_mgr *buf_mgr, + unsigned int cmd, void *arg); + +#endif /* _MSM_ISP_BUF_H_ */ diff --git a/drivers/media/video/msmb/isp/msm_isp.c b/drivers/media/video/msmb/isp/msm_isp.c new file mode 100644 index 00000000000..ab492ab2d0f --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp.c @@ -0,0 +1,168 @@ +/* Copyright (c) 2013, 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 + +#include "msm_isp.h" +#include "msm_isp_util.h" +#include "msm_isp_axi_util.h" +#include "msm_isp_stats_util.h" +#include "msm_sd.h" +#include "msm_isp40.h" + +static struct msm_sd_req_vb2_q vfe_vb2_ops; + +static const struct of_device_id msm_vfe_dt_match[] = { + { + .compatible = "qcom,vfe40", + .data = &vfe40_hw_info, + }, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_vfe_dt_match); + +static const struct platform_device_id msm_vfe_dev_id[] = { + {"msm_vfe32"}, + {} +}; + +static struct msm_isp_buf_mgr vfe_buf_mgr; + +static int __devinit 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; + + struct msm_iova_partition vfe_partition = { + .start = SZ_128K, + .size = SZ_2G - SZ_128K, + }; + struct msm_iova_layout vfe_layout = { + .partitions = &vfe_partition, + .npartitions = 1, + .client_name = "vfe", + .domain_flags = 0, + }; + + vfe_dev = kzalloc(sizeof(struct vfe_device), GFP_KERNEL); + if (!vfe_dev) { + pr_err("%s: no enough memory\n", __func__); + return -ENOMEM; + } + + 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); + vfe_dev->hw_info = + (struct msm_vfe_hardware_info *) match_dev->data; + } else { + vfe_dev->hw_info = platform_get_drvdata(pdev); + } + + if (!vfe_dev->hw_info) { + pr_err("%s: No vfe hardware info\n", __func__); + return -EINVAL; + } + 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__); + kfree(vfe_dev); + return -ENOMEM; + } + + 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->mutex); + spin_lock_init(&vfe_dev->tasklet_lock); + spin_lock_init(&vfe_dev->shared_data_lock); + media_entity_init(&vfe_dev->subdev.sd.entity, 0, NULL, 0); + vfe_dev->subdev.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + vfe_dev->subdev.sd.entity.group_id = MSM_CAMERA_SUBDEV_VFE; + vfe_dev->subdev.sd.entity.name = pdev->name; + rc = msm_sd_register(&vfe_dev->subdev); + if (rc != 0) { + pr_err("%s: msm_sd_register error = %d\n", __func__, rc); + kfree(vfe_dev); + goto end; + } + + 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, &vfe_layout); + if (rc < 0) { + pr_err("%s: Unable to create buffer manager\n", __func__); + kfree(vfe_dev); + return -EINVAL; + } + vfe_dev->vfe_open_cnt = 0; +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/video/msmb/isp/msm_isp.h b/drivers/media/video/msmb/isp/msm_isp.h new file mode 100644 index 00000000000..c320a1a2160 --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp.h @@ -0,0 +1,347 @@ +/* Copyright (c) 2013, 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 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_SUB(a) ((a > 0) ? a-1 : 0) + +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_camif_update_state { + NO_UPDATE, + ENABLE_CAMIF, + DISABLE_CAMIF, + DISABLE_CAMIF_IMMEDIATELY +}; + +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); + 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); + void (*process_axi_irq) (struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct timeval *tv); + void (*process_error_irq) (struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1); + void (*process_stats_irq) (struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct timeval *tv); +}; + +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); + + 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_request_cmd *stream_cfg_cmd, + 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_request_cmd *stream_cfg_cmd, + 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_rdi_reg) (struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, + 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, unsigned long 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); + long (*halt) (struct vfe_device *vfe_dev); +}; + +struct msm_vfe_core_ops { + void (*epoch_irq) (struct vfe_device *vfe_dev, + uint32_t epoch_line0, uint32_t epoch_line1); + void (*reg_update) (struct vfe_device *vfe_dev, uint32_t update_mask); + long (*reset_hw) (struct vfe_device *vfe_dev); + int (*init_hw) (struct vfe_device *vfe_dev); + void (*init_hw_reg) (struct vfe_device *vfe_dev); + void (*release_hw) (struct vfe_device *vfe_dev); + void (*cfg_camif) (struct vfe_device *vfe_dev, + struct msm_vfe_pix_cfg *pix_cfg); + void (*update_camif_state) (struct vfe_device *vfe_dev, + enum msm_isp_camif_update_state update_state); + int (*get_platform_data) (struct vfe_device *vfe_dev); +}; +struct msm_vfe_stats_ops { + 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_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_request_cmd *stream_cfg_cmd); + 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 (*stats_enable) (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); + + 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); +}; + +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 { + struct msm_vfe_ops vfe_ops; + struct msm_vfe_axi_hardware_info *axi_hw_info; + struct v4l2_subdev_internal_ops *subdev_internal_ops; + struct v4l2_subdev_ops *subdev_ops; +}; + +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; +}; + +enum msm_vfe_axi_state { + AVALIABLE, + INACTIVE, + ACTIVE, + PAUSE, + START_PENDING, + STOP_PENDING, + STOPPING, + PAUSE_PENDING, +}; + +#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]; + uint8_t rdi[MAX_PLANES_PER_STREAM]; + uint8_t rdi_master[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 stream_handle; + uint8_t buf_divert; + enum msm_vfe_axi_stream_type stream_type; + + uint32_t framedrop_pattern; + uint32_t init_frame_drop; + uint32_t burst_frame_count;/*number of sof before burst stop*/ + uint8_t auto_trigger_stop; + uint8_t framedrop_update; +}; + +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; + uint8_t pix_stream_count; + uint8_t raw_stream_count; + enum msm_vfe_inputmux input_mux; +}; + + +struct msm_vfe_stats_stream { + uint32_t frame_id; + uint8_t enable; + enum msm_isp_stats_type stats_type; + 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 stream_handle; + uint32_t framedrop_pattern; +}; + +struct msm_vfe_stats_composite_info { + uint32_t stream_handle; + uint32_t stream_composite_mask; +}; + +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]; + 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 free_rdi[MAX_NUM_RDI]; + uint8_t free_rdi_master[MAX_NUM_RDI][MAX_NUM_RDI_MASTER]; + uint8_t num_used_rdi; + uint8_t num_active_stream; + struct msm_vfe_axi_composite_info + composite_info[MAX_NUM_COMPOSITE_MASK]; + uint8_t num_used_composite_mask; + uint32_t stream_update; + struct msm_vfe_src_info src_info[VFE_SRC_MAX]; + uint16_t stream_handle_cnt; + unsigned long event_mask; +}; + +struct msm_vfe_stats_shared_data { + struct msm_vfe_stats_stream stream_info[MSM_ISP_STATS_MAX]; + struct msm_vfe_stats_composite_info + comp_info[MAX_NUM_STATS_COMP_MASK]; + 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; + uint32_t vfeInterruptStatus1; + struct timeval tv; + uint8_t cmd_used; +}; + +#define MSM_VFE_TASKLETQ_SIZE 200 + +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; + + struct regulator *fs_vfe; + struct clk *vfe_clk[7]; + + uint32_t bus_perf_client; + + struct completion reset_complete; + struct completion halt_complete; + struct completion stream_config_complete; + struct mutex 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]; + + 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_isp_buf_mgr *buf_mgr; + int dump_reg; + uint32_t vfe_open_cnt; +}; + +#endif diff --git a/drivers/media/video/msmb/isp/msm_isp32.c b/drivers/media/video/msmb/isp/msm_isp32.c new file mode 100644 index 00000000000..d7b62d1c408 --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp32.c @@ -0,0 +1,822 @@ +/* Copyright (c) 2013, 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 "msm_isp32.h" +#include "msm_isp_util.h" +#include "msm_isp_axi_util.h" +#include "msm_isp.h" +#include "msm.h" + +#define VFE32_BURST_LEN 4 +#define VFE32_EQUAL_SLICE_UB 117 +#define VFE32_WM_BASE(idx) (0x4C + 0x18 * idx) +#define VFE32_RDI_BASE(idx) (0x734 + 0x4 * idx) +#define VFE32_RDI_MN_BASE(m) (0x734 + 0x4 * m/3) +#define VFE32_RDI_MN_SEL_SHIFT(m) (4*(m%3) + 4) +#define VFE32_RDI_MN_FB_SHIFT(m) ((m%3) + 16) +#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 >> wm) & 0x1))) + +/*Temporary use fixed bus vectors in VFE */ +static struct msm_bus_vectors msm_vfe32_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors msm_vfe32_preview_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1027648000, + .ib = 1105920000, + }, +}; + +static struct msm_bus_paths msm_vfe32_bus_client_config[] = { + { + ARRAY_SIZE(msm_vfe32_init_vectors), + msm_vfe32_init_vectors, + }, + { + ARRAY_SIZE(msm_vfe32_preview_vectors), + msm_vfe32_preview_vectors, + }, +}; + +static struct msm_bus_scale_pdata msm_vfe32_bus_client_pdata = { + msm_vfe32_bus_client_config, + ARRAY_SIZE(msm_vfe32_bus_client_config), + .name = "msm_camera_vfe", +}; + +static struct msm_cam_clk_info msm_vfe32_clk_info[] = { + {"vfe_clk", 228570000}, + {"vfe_pclk", -1}, + {"csi_vfe_clk", -1}, +}; + +static int msm_vfe32_init_hardware(struct vfe_device *vfe_dev) +{ + int rc = -1; + + vfe_dev->bus_perf_client = + msm_bus_scale_register_client(&msm_vfe32_bus_client_pdata); + if (!vfe_dev->bus_perf_client) { + pr_err("%s: Registration Failed!\n", __func__); + vfe_dev->bus_perf_client = 0; + goto bus_scale_register_failed; + } + msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 1); + + if (vfe_dev->fs_vfe) { + rc = regulator_enable(vfe_dev->fs_vfe); + if (rc) { + pr_err("%s: Regulator enable failed\n", __func__); + goto fs_failed; + } + } + + rc = msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info, + vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 1); + if (rc < 0) + goto clk_enable_failed; + + vfe_dev->vfe_base = ioremap(vfe_dev->vfe_mem->start, + resource_size(vfe_dev->vfe_mem)); + if (!vfe_dev->vfe_base) { + rc = -ENOMEM; + pr_err("%s: vfe ioremap failed\n", __func__); + goto vfe_remap_failed; + } + + rc = request_irq(vfe_dev->vfe_irq->start, msm_isp_process_irq, + IRQF_TRIGGER_RISING, "vfe", vfe_dev); + if (rc < 0) { + pr_err("%s: irq request failed\n", __func__); + goto irq_req_failed; + } + + return rc; +irq_req_failed: + iounmap(vfe_dev->vfe_base); +vfe_remap_failed: + msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info, + vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 0); +clk_enable_failed: + regulator_disable(vfe_dev->fs_vfe); +fs_failed: + msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0); + msm_bus_scale_unregister_client(vfe_dev->bus_perf_client); +bus_scale_register_failed: + return rc; +} + +static void msm_vfe32_release_hardware(struct vfe_device *vfe_dev) +{ + free_irq(vfe_dev->vfe_irq->start, vfe_dev); + tasklet_kill(&vfe_dev->vfe_tasklet); + iounmap(vfe_dev->vfe_base); + msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe32_clk_info, + vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe32_clk_info), 0); + regulator_disable(vfe_dev->fs_vfe); + msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0); + msm_bus_scale_unregister_client(vfe_dev->bus_perf_client); +} + +static void msm_vfe32_init_hardware_reg(struct vfe_device *vfe_dev) +{ + /* CGC_OVERRIDE */ + msm_camera_io_w(0x07FFFFFF, vfe_dev->vfe_base + 0xC); + /* BUS_CFG */ + msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x3C); + msm_camera_io_w(0x00000025, vfe_dev->vfe_base + 0x1C); + msm_camera_io_w_mb(0x1DFFFFFF, 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); +} + +static void msm_vfe32_process_reset_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1) +{ + if (irq_status1 & (1 << 23)) + 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)) + complete(&vfe_dev->halt_complete); +} + +static void msm_vfe32_process_camif_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1) +{ + if (!(irq_status0 & 0x1F)) + return; + + if (irq_status0 & (1 << 0)) { + ISP_DBG("%s: PIX0 frame id: %lu\n", __func__, + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); + msm_isp_update_framedrop_count(vfe_dev); + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id++; + } +} + +static void msm_vfe32_process_violation_irq(struct vfe_device *vfe_dev) +{ + uint32_t violation_status; + violation_status = msm_camera_io_r(vfe_dev->vfe_base + 0x48); + if (!violation_status) + return; + + if (violation_status & (1 << 0)) + pr_err("%s: black violation\n", __func__); + if (violation_status & (1 << 1)) + pr_err("%s: rolloff violation\n", __func__); + if (violation_status & (1 << 2)) + pr_err("%s: demux violation\n", __func__); + if (violation_status & (1 << 3)) + pr_err("%s: demosaic violation\n", __func__); + if (violation_status & (1 << 4)) + pr_err("%s: crop violation\n", __func__); + if (violation_status & (1 << 5)) + pr_err("%s: scale violation\n", __func__); + if (violation_status & (1 << 6)) + pr_err("%s: wb violation\n", __func__); + if (violation_status & (1 << 7)) + pr_err("%s: clf violation\n", __func__); + if (violation_status & (1 << 8)) + pr_err("%s: matrix violation\n", __func__); + if (violation_status & (1 << 9)) + pr_err("%s: rgb lut violation\n", __func__); + if (violation_status & (1 << 10)) + pr_err("%s: la violation\n", __func__); + if (violation_status & (1 << 11)) + pr_err("%s: chroma enhance violation\n", __func__); + if (violation_status & (1 << 12)) + pr_err("%s: chroma supress mce violation\n", __func__); + if (violation_status & (1 << 13)) + pr_err("%s: skin enhance violation\n", __func__); + if (violation_status & (1 << 14)) + pr_err("%s: asf violation\n", __func__); + if (violation_status & (1 << 15)) + pr_err("%s: scale y violation\n", __func__); + if (violation_status & (1 << 16)) + pr_err("%s: scale cbcr violation\n", __func__); + if (violation_status & (1 << 17)) + pr_err("%s: chroma subsample violation\n", __func__); + if (violation_status & (1 << 18)) + pr_err("%s: framedrop enc y violation\n", __func__); + if (violation_status & (1 << 19)) + pr_err("%s: framedrop enc cbcr violation\n", __func__); + if (violation_status & (1 << 20)) + pr_err("%s: framedrop view y violation\n", __func__); + if (violation_status & (1 << 21)) + pr_err("%s: framedrop view cbcr violation\n", __func__); + if (violation_status & (1 << 22)) + pr_err("%s: realign buf y violation\n", __func__); + if (violation_status & (1 << 23)) + pr_err("%s: realign buf cb violation\n", __func__); + if (violation_status & (1 << 24)) + pr_err("%s: realign buf cr violation\n", __func__); +} + +static void msm_vfe32_process_error_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1) +{ + uint32_t camif_status; + if (!(irq_status1 & 0x7FFFFF)) + return; + + if (irq_status1 & (1 << 0)) { + camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x204); + pr_err("%s: camif error status: 0x%x\n", + __func__, camif_status); + } + if (irq_status1 & (1 << 1)) + pr_err("%s: stats bhist overwrite\n", __func__); + if (irq_status1 & (1 << 2)) + pr_err("%s: stats cs overwrite\n", __func__); + if (irq_status1 & (1 << 3)) + pr_err("%s: stats ihist overwrite\n", __func__); + if (irq_status1 & (1 << 4)) + pr_err("%s: realign buf y overflow\n", __func__); + if (irq_status1 & (1 << 5)) + pr_err("%s: realign buf cb overflow\n", __func__); + if (irq_status1 & (1 << 6)) + pr_err("%s: realign buf cr overflow\n", __func__); + if (irq_status1 & (1 << 7)) { + pr_err("%s: violation\n", __func__); + msm_vfe32_process_violation_irq(vfe_dev); + } + if (irq_status1 & (1 << 8)) + pr_err("%s: image master 0 bus overflow\n", __func__); + if (irq_status1 & (1 << 9)) + pr_err("%s: image master 1 bus overflow\n", __func__); + if (irq_status1 & (1 << 10)) + pr_err("%s: image master 2 bus overflow\n", __func__); + if (irq_status1 & (1 << 11)) + pr_err("%s: image master 3 bus overflow\n", __func__); + if (irq_status1 & (1 << 12)) + pr_err("%s: image master 4 bus overflow\n", __func__); + if (irq_status1 & (1 << 13)) + pr_err("%s: image master 5 bus overflow\n", __func__); + if (irq_status1 & (1 << 14)) + pr_err("%s: image master 6 bus overflow\n", __func__); + if (irq_status1 & (1 << 15)) + pr_err("%s: status ae/bg bus overflow\n", __func__); + if (irq_status1 & (1 << 16)) + pr_err("%s: status af/bf bus overflow\n", __func__); + if (irq_status1 & (1 << 17)) + pr_err("%s: status awb bus overflow\n", __func__); + if (irq_status1 & (1 << 18)) + pr_err("%s: status rs bus overflow\n", __func__); + if (irq_status1 & (1 << 19)) + pr_err("%s: status cs bus overflow\n", __func__); + if (irq_status1 & (1 << 20)) + pr_err("%s: status ihist bus overflow\n", __func__); + if (irq_status1 & (1 << 21)) + pr_err("%s: status skin bhist bus overflow\n", __func__); + if (irq_status1 & (1 << 22)) + pr_err("%s: axi error\n", __func__); +} + +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); + msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x24); + msm_camera_io_w(*irq_status1, vfe_dev->vfe_base + 0x28); + msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x18); +} + +static void msm_vfe32_process_reg_update(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1) +{ + if (!(irq_status0 & 0x20) && !(irq_status1 & 0x1C000000)) + return; + + if (vfe_dev->axi_data.stream_update) + msm_isp_axi_stream_update(vfe_dev); + + msm_isp_update_framedrop_reg(vfe_dev); + + return; +} + +static void msm_vfe32_reg_update( + struct vfe_device *vfe_dev, uint32_t update_mask) +{ + msm_camera_io_w_mb(update_mask, vfe_dev->vfe_base + 0x260); +} + +static long msm_vfe32_reset_hardware(struct vfe_device *vfe_dev) +{ + init_completion(&vfe_dev->reset_complete); + msm_camera_io_w_mb(0x3FF, vfe_dev->vfe_base + 0x4); + return wait_for_completion_interruptible_timeout( + &vfe_dev->reset_complete, msecs_to_jiffies(50)); +} + +static void msm_vfe32_axi_reload_wm( + struct vfe_device *vfe_dev, uint32_t reload_mask) +{ + msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x38); +} + +static void msm_vfe32_axi_enable_wm(struct vfe_device *vfe_dev, + uint8_t wm_idx, uint8_t enable) +{ + if (enable) + msm_camera_io_w_mb(0x1, + vfe_dev->vfe_base + VFE32_WM_BASE(wm_idx)); + else + msm_camera_io_w_mb(0x0, + 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; + 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); + comp_mask &= ~(0x7F << (comp_mask_index * 8)); + comp_mask |= (axi_data->composite_info[comp_mask_index]. + stream_composite_mask << (comp_mask_index * 8)); + msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x34); + + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); + irq_mask |= 1 << (comp_mask_index + 21); + msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C); +} + +static void msm_vfe32_axi_clear_comp_mask(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + 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); + comp_mask &= ~(0x7F << (comp_mask_index * 8)); + msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x34); + + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); + irq_mask &= ~(1 << (comp_mask_index + 21)); + msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C); +} + +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; + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); + irq_mask |= 1 << (stream_info->wm[0] + 6); + msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x1C); +} + +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; + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x1C); + irq_mask &= ~(1 << (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 = 0; + + if (stream_info->init_frame_drop == 0) + framedrop_pattern = stream_info->framedrop_pattern; + + if (stream_info->stream_type == BURST_STREAM && + stream_info->burst_frame_count == 0) + framedrop_pattern = 0; + + if (stream_info->stream_src == PIX_ENCODER) { + msm_camera_io_w(0x1F, vfe_dev->vfe_base + 0x504); + msm_camera_io_w(0x1F, 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(0x1F, vfe_dev->vfe_base + 0x514); + msm_camera_io_w(0x1F, 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_dev->vfe_base + 0x260); +} + +static void msm_vfe32_clear_framedrop(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + if (stream_info->stream_src == PIX_ENCODER) { + msm_camera_io_w(0, vfe_dev->vfe_base + 0x50C); + msm_camera_io_w(0, vfe_dev->vfe_base + 0x510); + } else if (stream_info->stream_src == PIX_VIEWFINDER) { + msm_camera_io_w(0, vfe_dev->vfe_base + 0x51C); + msm_camera_io_w(0, vfe_dev->vfe_base + 0x520); + } +} + +static void msm_vfe32_cfg_camif(struct vfe_device *vfe_dev, + struct msm_vfe_pix_cfg *pix_cfg) +{ + uint16_t first_pixel, last_pixel, first_line, last_line; + struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg; + uint32_t val; + + first_pixel = camif_cfg->left_crop; + last_pixel = camif_cfg->pixels_per_line - + camif_cfg->left_crop - + camif_cfg->right_crop; + first_line = camif_cfg->left_crop; + last_line = camif_cfg->lines_per_frame - + camif_cfg->top_crop - + camif_cfg->bottom_crop; + + msm_camera_io_w(pix_cfg->input_mux << 16 | pix_cfg->pixel_pattern, + vfe_dev->vfe_base + 0x14); + + msm_camera_io_w(camif_cfg->lines_per_frame << 16 | + camif_cfg->pixels_per_line, + vfe_dev->vfe_base + 0x1EC); + + msm_camera_io_w(ISP_SUB(first_pixel) << 16 | ISP_SUB(last_pixel), + vfe_dev->vfe_base + 0x1F0); + + msm_camera_io_w(ISP_SUB(first_line) << 16 | ISP_SUB(last_line), + vfe_dev->vfe_base + 0x1F4); + + val = msm_camera_io_r(vfe_dev->vfe_base + 0x6FC); + val &= 0xFFFFFFFC; + val |= camif_cfg->camif_input; + msm_camera_io_w(val, vfe_dev->vfe_base + 0x6FC); +} + +static void msm_vfe32_update_camif_state( + struct vfe_device *vfe_dev, + enum msm_isp_camif_update_state update_state) +{ + uint32_t val; + bool bus_en, vfe_en; + if (update_state == NO_UPDATE) + return; + + val = msm_camera_io_r(vfe_dev->vfe_base + 0x1E4); + if (update_state == ENABLE_CAMIF) { + 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].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(0x1, vfe_dev->vfe_base + 0x1E0); + vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1; + } else if (update_state == DISABLE_CAMIF) { + msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x1E0); + vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0; + } +} + +static void msm_vfe32_axi_cfg_wm_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, + uint8_t plane_idx) +{ + uint32_t val; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->axi_stream_handle & 0xFF)]; + uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]); + + /*WR_IMAGE_SIZE*/ + val = + ((msm_isp_cal_word_per_line(stream_cfg_cmd->output_format, + stream_cfg_cmd->plane_cfg[ + plane_idx].output_width)+1)/2 - 1) << 16 | + (stream_cfg_cmd->plane_cfg[plane_idx].output_height - 1); + msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x10); + + /*WR_BUFFER_CFG*/ + val = + msm_isp_cal_word_per_line(stream_cfg_cmd->output_format, + stream_cfg_cmd->plane_cfg[plane_idx].output_width) << 16 | + (stream_cfg_cmd->plane_cfg[ + plane_idx].output_scan_lines - 1) << 4 | + VFE32_BURST_LEN >> 2; + msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14); + return; +} + +static void msm_vfe32_axi_clear_wm_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx) +{ + uint32_t val = 0; + uint32_t wm_base = VFE32_WM_BASE(stream_info->wm[plane_idx]); + /*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_rdi_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, + uint8_t plane_idx) +{ + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)]; + struct msm_vfe_axi_plane_cfg *plane_cfg = + &stream_cfg_cmd->plane_cfg[plane_idx]; + uint8_t rdi = stream_info->rdi[plane_idx]; + uint8_t rdi_master = stream_info->rdi_master[plane_idx]; + uint32_t rdi_reg_cfg; + + rdi_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_RDI_BASE(rdi)); + rdi_reg_cfg = (rdi_reg_cfg & 0xFFFFFFF) | rdi_master << 28; + msm_camera_io_w(rdi_reg_cfg, vfe_dev->vfe_base + VFE32_RDI_BASE(rdi)); + + rdi_reg_cfg = msm_camera_io_r( + vfe_dev->vfe_base + VFE32_RDI_MN_BASE(rdi_master)); + rdi_reg_cfg &= ~((0xF << VFE32_RDI_MN_SEL_SHIFT(rdi_master)) | + (0x1 << VFE32_RDI_MN_FB_SHIFT(rdi_master))); + rdi_reg_cfg |= (plane_cfg->rdi_cid << + VFE32_RDI_MN_SEL_SHIFT(rdi_master) | + (stream_cfg_cmd->frame_base << + VFE32_RDI_MN_FB_SHIFT(rdi_master))); + msm_camera_io_w(rdi_reg_cfg, + vfe_dev->vfe_base + + VFE32_RDI_MN_BASE(rdi_master)); +} + +static void msm_vfe32_axi_cfg_wm_xbar_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, + uint8_t plane_idx) +{ + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)]; + struct msm_vfe_axi_plane_cfg *plane_cfg = + &stream_cfg_cmd->plane_cfg[plane_idx]; + uint8_t wm = stream_info->wm[plane_idx]; + uint32_t xbar_cfg = 0; + uint32_t xbar_reg_cfg = 0; + + switch (stream_cfg_cmd->stream_src) { + case PIX_ENCODER: + case PIX_VIEWFINDER: { + if (plane_cfg->output_plane_format + != CRCB_PLANE) { + /*SINGLE_STREAM_SEL*/ + xbar_cfg |= plane_cfg->output_plane_format << 5; + } else { + switch (stream_cfg_cmd->output_format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + xbar_cfg |= 0x3 << 3; /*PAIR_STREAM_SWAP_CTRL*/ + break; + } + xbar_cfg |= 0x1 << 1; /*PAIR_STREAM_EN*/ + } + if (stream_cfg_cmd->stream_src == PIX_VIEWFINDER) + xbar_cfg |= 0x1; /*VIEW_STREAM_EN*/ + break; + } + case CAMIF_RAW: + xbar_cfg = 0x60; + break; + case IDEAL_RAW: + xbar_cfg = 0x80; + break; + case RDI: + if (stream_info->rdi[plane_idx] == 0) + xbar_cfg = 0xA0; + else if (stream_info->rdi[plane_idx] == 1) + xbar_cfg = 0xC0; + else if (stream_info->rdi[plane_idx] == 2) + xbar_cfg = 0xE0; + break; + default: + pr_err("%s: Invalid stream src\n", __func__); + } + xbar_reg_cfg = msm_camera_io_r(vfe_dev->vfe_base + VFE32_XBAR_BASE(wm)); + 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) +{ + 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)); + xbar_reg_cfg &= ~(0xFF << VFE32_XBAR_SHIFT(wm)); + msm_camera_io_w(xbar_reg_cfg, vfe_dev->vfe_base + VFE32_XBAR_BASE(wm)); +} + +static void msm_vfe32_cfg_axi_ub(struct vfe_device *vfe_dev) +{ + int i; + uint32_t ub_offset = 0; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + 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_update_ping_pong_addr(struct vfe_device *vfe_dev, + uint8_t wm_idx, uint32_t pingpong_status, unsigned long paddr) +{ + msm_camera_io_w(paddr, vfe_dev->vfe_base + + VFE32_PING_PONG_BASE(wm_idx, pingpong_status)); +} + +static long msm_vfe32_axi_halt(struct vfe_device *vfe_dev) +{ + uint32_t halt_mask; + halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x20); + halt_mask |= (1 << 24); + msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x20); + init_completion(&vfe_dev->halt_complete); + /*TD: Need to fix crashes with this*/ + /*msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x1D8);*/ + return wait_for_completion_interruptible_timeout( + &vfe_dev->halt_complete, msecs_to_jiffies(500)); +} + +static uint32_t msm_vfe32_get_wm_mask( + uint32_t irq_status0, uint32_t irq_status1) +{ + return (irq_status0 >> 6) & 0x7F; +} + +static uint32_t msm_vfe32_get_comp_mask( + uint32_t irq_status0, uint32_t irq_status1) +{ + return (irq_status0 >> 21) & 0x7; +} + +static uint32_t msm_vfe32_get_pingpong_status(struct vfe_device *vfe_dev) +{ + return msm_camera_io_r(vfe_dev->vfe_base + 0x180); +} + +static int msm_vfe32_get_platform_data(struct vfe_device *vfe_dev) +{ + int rc = 0; + vfe_dev->vfe_mem = platform_get_resource_byname(vfe_dev->pdev, + IORESOURCE_MEM, "vfe"); + if (!vfe_dev->vfe_mem) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto vfe_no_resource; + } + + vfe_dev->vfe_irq = platform_get_resource_byname(vfe_dev->pdev, + IORESOURCE_IRQ, "vfe"); + if (!vfe_dev->vfe_irq) { + pr_err("%s: no irq resource?\n", __func__); + rc = -ENODEV; + goto vfe_no_resource; + } + + vfe_dev->fs_vfe = regulator_get(&vfe_dev->pdev->dev, "vdd"); + if (IS_ERR(vfe_dev->fs_vfe)) { + pr_err("%s: Regulator get failed %ld\n", __func__, + PTR_ERR(vfe_dev->fs_vfe)); + vfe_dev->fs_vfe = NULL; + rc = -ENODEV; + goto vfe_no_resource; + } + + vfe_dev->iommu_ctx[0] = msm_iommu_get_ctx("vfe_imgwr"); + if (!vfe_dev->iommu_ctx[0]) { + pr_err("%s: no iommux ctx resource?\n", __func__); + rc = -ENODEV; + goto vfe_no_resource; + } + + vfe_dev->iommu_ctx[1] = msm_iommu_get_ctx("vfe_misc"); + if (!vfe_dev->iommu_ctx[1]) { + pr_err("%s: no iommux ctx resource?\n", __func__); + rc = -ENODEV; + goto vfe_no_resource; + } + vfe_dev->num_ctx = 2; + +vfe_no_resource: + return rc; +} + +struct msm_vfe_axi_hardware_info msm_vfe32_axi_hw_info = { + .num_wm = 7, + .num_comp_mask = 3, + .num_rdi = 3, + .num_rdi_master = 3, +}; + +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 = { + .vfe_ops = { + .irq_ops = { + .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_reset_irq = msm_vfe32_process_reset_irq, + .process_error_irq = msm_vfe32_process_error_irq, + .process_reg_update = msm_vfe32_process_reg_update, + .process_axi_irq = msm_isp_process_axi_irq, + }, + .axi_ops = { + .reload_wm = msm_vfe32_axi_reload_wm, + .enable_wm = msm_vfe32_axi_enable_wm, + .cfg_comp_mask = msm_vfe32_axi_cfg_comp_mask, + .clear_comp_mask = msm_vfe32_axi_clear_comp_mask, + .cfg_wm_irq_mask = msm_vfe32_axi_cfg_wm_irq_mask, + .clear_wm_irq_mask = msm_vfe32_axi_clear_wm_irq_mask, + .cfg_framedrop = msm_vfe32_cfg_framedrop, + .clear_framedrop = msm_vfe32_clear_framedrop, + .cfg_wm_reg = msm_vfe32_axi_cfg_wm_reg, + .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_rdi_reg = msm_vfe32_axi_cfg_rdi_reg, + .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, + }, + .core_ops = { + .reg_update = msm_vfe32_reg_update, + .cfg_camif = msm_vfe32_cfg_camif, + .update_camif_state = msm_vfe32_update_camif_state, + .reset_hw = msm_vfe32_reset_hardware, + .init_hw = msm_vfe32_init_hardware, + .init_hw_reg = msm_vfe32_init_hardware_reg, + .release_hw = msm_vfe32_release_hardware, + .get_platform_data = msm_vfe32_get_platform_data, + }, + }, + .axi_hw_info = &msm_vfe32_axi_hw_info, + .subdev_ops = &msm_vfe32_subdev_ops, + .subdev_internal_ops = &msm_vfe32_internal_ops, +}; +EXPORT_SYMBOL(vfe32_hw_info); diff --git a/drivers/media/video/msmb/isp/msm_isp32.h b/drivers/media/video/msmb/isp/msm_isp32.h new file mode 100644 index 00000000000..05350486271 --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp32.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2013, 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_ISP32_H__ +#define __MSM_ISP32_H__ + +extern struct msm_vfe_hardware_info vfe32_hw_info; +#endif /* __MSM_ISP32_H__ */ diff --git a/drivers/media/video/msmb/isp/msm_isp40.c b/drivers/media/video/msmb/isp/msm_isp40.c new file mode 100644 index 00000000000..78d82c04aed --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp40.c @@ -0,0 +1,1128 @@ +/* Copyright (c) 2013, 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_isp40.h" +#include "msm_isp_util.h" +#include "msm_isp_axi_util.h" +#include "msm_isp_stats_util.h" +#include "msm_isp.h" +#include "msm.h" +#include "msm_camera_io_util.h" + +/*#define CONFIG_MSM_ISP_DBG*/ +#undef CDBG +#ifdef CONFIG_MSM_ISP_DBG +#define CDBG(fmt, args...) pr_err(fmt, ##args) +#else +#define CDBG(fmt, args...) do { } while (0) +#endif + +#define VFE40_BURST_LEN 4 +#define VFE40_EQUAL_SLICE_UB 117 +#define VFE40_WM_BASE(idx) (0x6C + 0x24 * idx) +#define VFE40_RDI_BASE(idx) (0x2E8 + 0x4 * idx) +#define VFE40_RDI_MN_BASE(m) (0x2E8 + 0x4 * m/3) +#define VFE40_RDI_MN_SEL_SHIFT(m) (4*(m%3) + 4) +#define VFE40_RDI_MN_FB_SHIFT(m) ((m%3) + 16) +#define VFE40_XBAR_BASE(idx) (0x58 + 0x4 * (idx / 2)) +#define VFE40_XBAR_SHIFT(idx) ((idx%2) ? 16 : 0) +#define VFE40_PING_PONG_BASE(wm, ping_pong) \ + (VFE40_WM_BASE(wm) + 0x4 * (1 + (~(ping_pong >> wm) & 0x1))) + +#define VFE40_VBIF_CLKON 0x4 +#define VFE40_VBIF_IN_RD_LIM_CONF0 0xB0 +#define VFE40_VBIF_IN_RD_LIM_CONF1 0xB4 +#define VFE40_VBIF_IN_RD_LIM_CONF2 0xB8 +#define VFE40_VBIF_IN_WR_LIM_CONF0 0xC0 +#define VFE40_VBIF_IN_WR_LIM_CONF1 0xC4 +#define VFE40_VBIF_IN_WR_LIM_CONF2 0xC8 +#define VFE40_VBIF_OUT_RD_LIM_CONF0 0xD0 +#define VFE40_VBIF_OUT_WR_LIM_CONF0 0xD4 +#define VFE40_VBIF_DDR_OUT_MAX_BURST 0xD8 +#define VFE40_VBIF_ARB_CTL 0xF0 +#define VFE40_VBIF_DDR_ARB_CONF0 0xF4 +#define VFE40_VBIF_DDR_ARB_CONF1 0xF8 +#define VFE40_VBIF_ROUND_ROBIN_QOS_ARB 0x124 +#define VFE40_VBIF_OUT_AXI_AOOO_EN 0x178 +#define VFE40_VBIF_OUT_AXI_AOOO 0x17C + +/*Temporary use fixed bus vectors in VFE */ +static struct msm_bus_vectors msm_vfe40_init_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 0, + .ib = 0, + }, +}; + +static struct msm_bus_vectors msm_vfe40_preview_vectors[] = { + { + .src = MSM_BUS_MASTER_VFE, + .dst = MSM_BUS_SLAVE_EBI_CH0, + .ab = 1027648000, + .ib = 1105920000, + }, +}; + +static struct msm_bus_paths msm_vfe40_bus_client_config[] = { + { + ARRAY_SIZE(msm_vfe40_init_vectors), + msm_vfe40_init_vectors, + }, + { + ARRAY_SIZE(msm_vfe40_preview_vectors), + msm_vfe40_preview_vectors, + }, +}; + +static struct msm_bus_scale_pdata msm_vfe40_bus_client_pdata = { + msm_vfe40_bus_client_config, + ARRAY_SIZE(msm_vfe40_bus_client_config), + .name = "msm_camera_vfe", +}; + +static struct msm_cam_clk_info msm_vfe40_clk_info[] = { + {"camss_top_ahb_clk", -1}, + {"vfe_clk_src", 266670000}, + {"camss_vfe_vfe_clk", -1}, + {"camss_csi_vfe_clk", -1}, + {"iface_clk", -1}, + {"bus_clk", -1}, + {"alt_bus_clk", -1}, +}; + +static void msm_vfe40_init_vbif_parms( + void __iomem *vfe_vbif_base) +{ + msm_camera_io_w_mb(0x1, + vfe_vbif_base + VFE40_VBIF_CLKON); + msm_camera_io_w_mb(0x1, + vfe_vbif_base + VFE40_VBIF_ROUND_ROBIN_QOS_ARB); + msm_camera_io_w_mb(0xFFFF, + vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO_EN); + msm_camera_io_w_mb(0xFFFFFFFF, + vfe_vbif_base + VFE40_VBIF_OUT_AXI_AOOO); + + msm_camera_io_w_mb(0x10101010, + vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF0); + msm_camera_io_w_mb(0x10101010, + vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF1); + msm_camera_io_w_mb(0x10101010, + vfe_vbif_base + VFE40_VBIF_IN_RD_LIM_CONF2); + msm_camera_io_w_mb(0x10101010, + vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF0); + msm_camera_io_w_mb(0x10101010, + vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF1); + msm_camera_io_w_mb(0x10101010, + vfe_vbif_base + VFE40_VBIF_IN_WR_LIM_CONF2); + msm_camera_io_w_mb(0x00001010, + vfe_vbif_base + VFE40_VBIF_OUT_RD_LIM_CONF0); + msm_camera_io_w_mb(0x00001010, + vfe_vbif_base + VFE40_VBIF_OUT_WR_LIM_CONF0); + msm_camera_io_w_mb(0x00000707, + vfe_vbif_base + VFE40_VBIF_DDR_OUT_MAX_BURST); + msm_camera_io_w_mb(0x00000030, + vfe_vbif_base + VFE40_VBIF_ARB_CTL); + msm_camera_io_w_mb(0x04210842, + vfe_vbif_base + VFE40_VBIF_DDR_ARB_CONF0); + msm_camera_io_w_mb(0x04210842, + vfe_vbif_base + VFE40_VBIF_DDR_ARB_CONF1); +} + +static int msm_vfe40_init_hardware(struct vfe_device *vfe_dev) +{ + int rc = -1; + + vfe_dev->bus_perf_client = + msm_bus_scale_register_client(&msm_vfe40_bus_client_pdata); + if (!vfe_dev->bus_perf_client) { + pr_err("%s: Registration Failed!\n", __func__); + vfe_dev->bus_perf_client = 0; + goto bus_scale_register_failed; + } + msm_bus_scale_client_update_request( + vfe_dev->bus_perf_client, 1); + + if (vfe_dev->fs_vfe) { + rc = regulator_enable(vfe_dev->fs_vfe); + if (rc) { + pr_err("%s: Regulator enable failed\n", __func__); + goto fs_failed; + } + } + + rc = msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe40_clk_info, + vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe40_clk_info), 1); + if (rc < 0) + goto clk_enable_failed; + + vfe_dev->vfe_base = ioremap(vfe_dev->vfe_mem->start, + resource_size(vfe_dev->vfe_mem)); + if (!vfe_dev->vfe_base) { + rc = -ENOMEM; + pr_err("%s: vfe ioremap failed\n", __func__); + goto vfe_remap_failed; + } + + vfe_dev->vfe_vbif_base = ioremap(vfe_dev->vfe_vbif_mem->start, + resource_size(vfe_dev->vfe_vbif_mem)); + if (!vfe_dev->vfe_vbif_base) { + rc = -ENOMEM; + pr_err("%s: vfe ioremap failed\n", __func__); + goto vbif_remap_failed; + } + + rc = request_irq(vfe_dev->vfe_irq->start, msm_isp_process_irq, + IRQF_TRIGGER_RISING, "vfe", vfe_dev); + if (rc < 0) { + pr_err("%s: irq request failed\n", __func__); + goto irq_req_failed; + } + + msm_vfe40_init_vbif_parms(vfe_dev->vfe_vbif_base); + return rc; +irq_req_failed: + iounmap(vfe_dev->vfe_vbif_base); +vbif_remap_failed: + iounmap(vfe_dev->vfe_base); +vfe_remap_failed: + msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe40_clk_info, + vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe40_clk_info), 0); +clk_enable_failed: + regulator_disable(vfe_dev->fs_vfe); +fs_failed: + msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0); + msm_bus_scale_unregister_client(vfe_dev->bus_perf_client); +bus_scale_register_failed: + return rc; +} + +static void msm_vfe40_release_hardware(struct vfe_device *vfe_dev) +{ + free_irq(vfe_dev->vfe_irq->start, vfe_dev); + tasklet_kill(&vfe_dev->vfe_tasklet); + iounmap(vfe_dev->vfe_vbif_base); + iounmap(vfe_dev->vfe_base); + msm_cam_clk_enable(&vfe_dev->pdev->dev, msm_vfe40_clk_info, + vfe_dev->vfe_clk, ARRAY_SIZE(msm_vfe40_clk_info), 0); + regulator_disable(vfe_dev->fs_vfe); + msm_bus_scale_client_update_request(vfe_dev->bus_perf_client, 0); + msm_bus_scale_unregister_client(vfe_dev->bus_perf_client); +} + +static void msm_vfe40_init_hardware_reg(struct vfe_device *vfe_dev) +{ + /* CGC_OVERRIDE */ + msm_camera_io_w(0x3FFFFFFF, vfe_dev->vfe_base + 0x14); + /* BUS_CFG */ + msm_camera_io_w(0x00000001, vfe_dev->vfe_base + 0x50); + msm_camera_io_w(0x800000F3, vfe_dev->vfe_base + 0x28); + msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x2C); + msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x30); + msm_camera_io_w_mb(0xFEFFFFFF, vfe_dev->vfe_base + 0x34); +} + +static void msm_vfe40_process_reset_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1) +{ + if (irq_status0 & (1 << 31)) + complete(&vfe_dev->reset_complete); +} + +static void msm_vfe40_process_halt_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1) +{ + if (irq_status1 & (1 << 8)) + complete(&vfe_dev->halt_complete); +} + +static void msm_vfe40_process_camif_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1) +{ + if (!(irq_status0 & 0xF)) + return; + + if (vfe_dev->hw_info->vfe_ops.core_ops.epoch_irq) { + if (irq_status0 & (1 << 2)) { + ISP_DBG("%s: EPOCH0 IRQ, PIX0_frameid = 0x%lu\n", + __func__, + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); + msm_isp_new_frame_notify(vfe_dev, VFE_PIX_0); + } + } else { + if (irq_status0 & (1 << 0)) { + ISP_DBG("%s: SOF: PIX0 frame id: %lu\n", __func__, + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); + msm_isp_new_frame_notify(vfe_dev, VFE_PIX_0); + } + } +} + +static void msm_vfe40_process_violation_irq( + struct vfe_device *vfe_dev) +{ + uint32_t violation_status; + violation_status = msm_camera_io_r(vfe_dev->vfe_base + 0x48); + if (!violation_status) + return; + + if (violation_status & (1 << 0)) + pr_err("%s: camif violation\n", __func__); + if (violation_status & (1 << 1)) + pr_err("%s: black violation\n", __func__); + if (violation_status & (1 << 2)) + pr_err("%s: rolloff violation\n", __func__); + if (violation_status & (1 << 3)) + pr_err("%s: demux violation\n", __func__); + if (violation_status & (1 << 4)) + pr_err("%s: demosaic violation\n", __func__); + if (violation_status & (1 << 5)) + pr_err("%s: wb violation\n", __func__); + if (violation_status & (1 << 6)) + pr_err("%s: clf violation\n", __func__); + if (violation_status & (1 << 7)) + pr_err("%s: color correct violation\n", __func__); + if (violation_status & (1 << 8)) + pr_err("%s: rgb lut violation\n", __func__); + if (violation_status & (1 << 9)) + pr_err("%s: la violation\n", __func__); + if (violation_status & (1 << 10)) + pr_err("%s: chroma enhance violation\n", __func__); + if (violation_status & (1 << 11)) + pr_err("%s: chroma supress mce violation\n", __func__); + if (violation_status & (1 << 12)) + pr_err("%s: skin enhance violation\n", __func__); + if (violation_status & (1 << 13)) + pr_err("%s: color tranform enc violation\n", __func__); + if (violation_status & (1 << 14)) + pr_err("%s: color tranform view violation\n", __func__); + if (violation_status & (1 << 15)) + pr_err("%s: scale enc y violation\n", __func__); + if (violation_status & (1 << 16)) + pr_err("%s: scale enc cbcr violation\n", __func__); + if (violation_status & (1 << 17)) + pr_err("%s: scale view y violation\n", __func__); + if (violation_status & (1 << 18)) + pr_err("%s: scale view cbcr violation\n", __func__); + if (violation_status & (1 << 19)) + pr_err("%s: asf enc violation\n", __func__); + if (violation_status & (1 << 20)) + pr_err("%s: asf view violation\n", __func__); + if (violation_status & (1 << 21)) + pr_err("%s: crop enc y violation\n", __func__); + if (violation_status & (1 << 22)) + pr_err("%s: crop enc cbcr violation\n", __func__); + if (violation_status & (1 << 23)) + pr_err("%s: crop view y violation\n", __func__); + if (violation_status & (1 << 24)) + pr_err("%s: crop view cbcr violation\n", __func__); + if (violation_status & (1 << 25)) + pr_err("%s: realign buf y violation\n", __func__); + if (violation_status & (1 << 26)) + pr_err("%s: realign buf cb violation\n", __func__); + if (violation_status & (1 << 27)) + pr_err("%s: realign buf cr violation\n", __func__); +} + +static void msm_vfe40_process_error_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1) +{ + uint32_t camif_status; + if (!(irq_status1 & 0x00FFFEFF)) + return; + + if (irq_status1 & (1 << 0)) { + camif_status = msm_camera_io_r(vfe_dev->vfe_base + 0x31C); + pr_err("%s: camif error status: 0x%x\n", + __func__, camif_status); + } + if (irq_status1 & (1 << 1)) + pr_err("%s: stats bhist overwrite\n", __func__); + if (irq_status1 & (1 << 2)) + pr_err("%s: stats cs overwrite\n", __func__); + if (irq_status1 & (1 << 3)) + pr_err("%s: stats ihist overwrite\n", __func__); + if (irq_status1 & (1 << 4)) + pr_err("%s: realign buf y overflow\n", __func__); + if (irq_status1 & (1 << 5)) + pr_err("%s: realign buf cb overflow\n", __func__); + if (irq_status1 & (1 << 6)) + pr_err("%s: realign buf cr overflow\n", __func__); + if (irq_status1 & (1 << 7)) { + pr_err("%s: violation\n", __func__); + msm_vfe40_process_violation_irq(vfe_dev); + } + if (irq_status1 & (1 << 9)) + pr_err("%s: image master 0 bus overflow\n", __func__); + if (irq_status1 & (1 << 10)) + pr_err("%s: image master 1 bus overflow\n", __func__); + if (irq_status1 & (1 << 11)) + pr_err("%s: image master 2 bus overflow\n", __func__); + if (irq_status1 & (1 << 12)) + pr_err("%s: image master 3 bus overflow\n", __func__); + if (irq_status1 & (1 << 13)) + pr_err("%s: image master 4 bus overflow\n", __func__); + if (irq_status1 & (1 << 14)) + pr_err("%s: image master 5 bus overflow\n", __func__); + if (irq_status1 & (1 << 15)) + pr_err("%s: image master 6 bus overflow\n", __func__); + if (irq_status1 & (1 << 16)) + pr_err("%s: status be bus overflow\n", __func__); + if (irq_status1 & (1 << 17)) + pr_err("%s: status bg bus overflow\n", __func__); + if (irq_status1 & (1 << 18)) + pr_err("%s: status bf bus overflow\n", __func__); + if (irq_status1 & (1 << 19)) + pr_err("%s: status awb bus overflow\n", __func__); + if (irq_status1 & (1 << 20)) + pr_err("%s: status rs bus overflow\n", __func__); + if (irq_status1 & (1 << 21)) + pr_err("%s: status cs bus overflow\n", __func__); + if (irq_status1 & (1 << 22)) + pr_err("%s: status ihist bus overflow\n", __func__); + if (irq_status1 & (1 << 23)) + pr_err("%s: status skin bhist bus overflow\n", __func__); +} + +static void msm_vfe40_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 + 0x38); + *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x3C); + msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x30); + msm_camera_io_w(*irq_status1, vfe_dev->vfe_base + 0x34); + msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x24); +} + +static void msm_vfe40_process_reg_update(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1) +{ + uint32_t update_mask = 0xF; + + if (!(irq_status0 & 0xF0)) + return; + + if (vfe_dev->axi_data.stream_update) + msm_isp_axi_stream_update(vfe_dev); + msm_isp_update_framedrop_reg(vfe_dev); + + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, update_mask); + return; +} + +static void msm_vfe40_epoch_irq_enb(struct vfe_device *vfe_dev, + uint32_t epoch_line0, uint32_t epoch_line1) +{ + uint32_t irq_mask = 0; + uint32_t epoch_val = 0; + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28); + if (epoch_line0 > 0) { + irq_mask |= 0x4; + epoch_val |= (epoch_line0 - 1) << 16; + } else { + irq_mask &= ~0x4; + epoch_val &= 0xFFFF; + } + if (epoch_line1 > 0) { + irq_mask |= 0x8; + epoch_val |= epoch_line1 - 1; + } else { + irq_mask &= ~0x8; + epoch_val &= 0xFFFF0000; + } + msm_camera_io_w_mb(epoch_val, vfe_dev->vfe_base + 0x318); + msm_camera_io_w_mb(irq_mask, vfe_dev->vfe_base + 0x28); +} + +static void msm_vfe40_reg_update( + struct vfe_device *vfe_dev, uint32_t update_mask) +{ + msm_camera_io_w_mb(update_mask, vfe_dev->vfe_base + 0x378); +} + +static long msm_vfe40_reset_hardware(struct vfe_device *vfe_dev) +{ + init_completion(&vfe_dev->reset_complete); + msm_camera_io_w_mb(0x1FF, vfe_dev->vfe_base + 0xC); + return wait_for_completion_interruptible_timeout( + &vfe_dev->reset_complete, msecs_to_jiffies(50)); +} + +static void msm_vfe40_axi_reload_wm( + struct vfe_device *vfe_dev, uint32_t reload_mask) +{ + msm_camera_io_w_mb(reload_mask, vfe_dev->vfe_base + 0x4C); +} + +static void msm_vfe40_axi_enable_wm(struct vfe_device *vfe_dev, + uint8_t wm_idx, uint8_t enable) +{ + if (enable) + msm_camera_io_w_mb(0x1, + vfe_dev->vfe_base + VFE40_WM_BASE(wm_idx)); + else + msm_camera_io_w_mb(0x0, + vfe_dev->vfe_base + VFE40_WM_BASE(wm_idx)); +} + +static void msm_vfe40_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; + 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 + 0x40); + comp_mask &= ~(0x7F << (comp_mask_index * 8)); + comp_mask |= (axi_data->composite_info[comp_mask_index]. + stream_composite_mask << (comp_mask_index * 8)); + msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); + + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28); + irq_mask |= 1 << (comp_mask_index + 25); + msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28); +} + +static void msm_vfe40_axi_clear_comp_mask(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + 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 + 0x40); + comp_mask &= ~(0x7F << (comp_mask_index * 8)); + msm_camera_io_w(comp_mask, vfe_dev->vfe_base + 0x40); + + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28); + irq_mask &= ~(1 << (comp_mask_index + 25)); + msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28); +} + +static void msm_vfe40_axi_cfg_wm_irq_mask(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + uint32_t irq_mask; + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28); + irq_mask |= 1 << (stream_info->wm[0] + 8); + msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28); +} + +static void msm_vfe40_axi_clear_wm_irq_mask(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + uint32_t irq_mask; + irq_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x28); + irq_mask &= ~(1 << (stream_info->wm[0] + 8)); + msm_camera_io_w(irq_mask, vfe_dev->vfe_base + 0x28); +} + +static void msm_vfe40_cfg_framedrop(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + uint32_t i; + uint32_t framedrop_pattern = 0; + + if (stream_info->init_frame_drop == 0) + framedrop_pattern = stream_info->framedrop_pattern; + + if (stream_info->stream_type == BURST_STREAM && + stream_info->burst_frame_count == 0) + framedrop_pattern = 0; + + for (i = 0; i < stream_info->num_planes; i++) + msm_camera_io_w(framedrop_pattern, vfe_dev->vfe_base + + VFE40_WM_BASE(stream_info->wm[i]) + 0x1C); + + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x378); +} + +static void msm_vfe40_clear_framedrop(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info) +{ + uint32_t i; + for (i = 0; i < stream_info->num_planes; i++) + msm_camera_io_w(0, vfe_dev->vfe_base + + VFE40_WM_BASE(stream_info->wm[i]) + 0x1C); +} + +static void msm_vfe40_cfg_camif(struct vfe_device *vfe_dev, + struct msm_vfe_pix_cfg *pix_cfg) +{ + uint16_t first_pixel, last_pixel, first_line, last_line; + struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg; + uint32_t val; + + first_pixel = camif_cfg->first_pixel; + last_pixel = camif_cfg->last_pixel; + first_line = camif_cfg->first_line; + last_line = camif_cfg->last_line; + + msm_camera_io_w(pix_cfg->input_mux << 16 | pix_cfg->pixel_pattern, + vfe_dev->vfe_base + 0x1C); + + msm_camera_io_w(camif_cfg->lines_per_frame << 16 | + camif_cfg->pixels_per_line, vfe_dev->vfe_base + 0x300); + + msm_camera_io_w(first_pixel << 16 | last_pixel, + vfe_dev->vfe_base + 0x304); + + msm_camera_io_w(first_line << 16 | last_line, + vfe_dev->vfe_base + 0x308); + + msm_camera_io_w(0xFFFFFFFF, vfe_dev->vfe_base + 0x314); + + val = msm_camera_io_r(vfe_dev->vfe_base + 0x2E8); + val |= camif_cfg->camif_input; + msm_camera_io_w(val, vfe_dev->vfe_base + 0x2E8); + + vfe_dev->hw_info->vfe_ops.core_ops.epoch_irq(vfe_dev, + camif_cfg->epoch_line0, camif_cfg->epoch_line1); + + switch (pix_cfg->input_mux) { + case CAMIF: + val = 0x01; + msm_camera_io_w(val, vfe_dev->vfe_base + 0x2F4); + break; + case TESTGEN: + val = 0x01; + msm_camera_io_w(val, vfe_dev->vfe_base + 0x93C); + break; + case EXTERNAL_READ: + default: + pr_err("%s: not supported input_mux %d\n", + __func__, pix_cfg->input_mux); + break; + } +} + +static void msm_vfe40_update_camif_state(struct vfe_device *vfe_dev, + enum msm_isp_camif_update_state update_state) +{ + uint32_t val; + bool bus_en, vfe_en; + if (update_state == NO_UPDATE) + return; + + val = msm_camera_io_r(vfe_dev->vfe_base + 0x2F8); + if (update_state == ENABLE_CAMIF) { + 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].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 + 0x2F8); + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2F4); + vfe_dev->axi_data.src_info[VFE_PIX_0].active = 1; + } else if (update_state == DISABLE_CAMIF) { + msm_camera_io_w_mb(0x0, vfe_dev->vfe_base + 0x2F4); + vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0; + } else if (update_state == DISABLE_CAMIF_IMMEDIATELY) { + msm_camera_io_w_mb(0x2, vfe_dev->vfe_base + 0x2F4); + vfe_dev->axi_data.src_info[VFE_PIX_0].active = 0; + } +} + +static void msm_vfe40_axi_cfg_wm_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, + uint8_t plane_idx) +{ + uint32_t val; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->axi_stream_handle & 0xFF)]; + uint32_t wm_base = VFE40_WM_BASE(stream_info->wm[plane_idx]); + + /*WR_ADDR_CFG*/ + msm_camera_io_w(0x7C, vfe_dev->vfe_base + wm_base + 0xC); + + /*WR_IMAGE_SIZE*/ + val = + ((msm_isp_cal_word_per_line(stream_cfg_cmd->output_format, + stream_cfg_cmd->plane_cfg[plane_idx]. + output_width)+1)/2 - 1) << 16 | + (stream_cfg_cmd->plane_cfg[plane_idx]. + output_height - 1); + msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14); + + /*WR_BUFFER_CFG*/ + val = + msm_isp_cal_word_per_line(stream_cfg_cmd->output_format, + stream_cfg_cmd->plane_cfg[ + plane_idx].output_width) << 16 | + (stream_cfg_cmd->plane_cfg[ + plane_idx].output_scan_lines - 1) << 4 | + VFE40_BURST_LEN >> 2; + msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18); + + /*WR_IRQ_SUBSAMPLE_PATTERN*/ + msm_camera_io_w(0xFFFFFFFF, + vfe_dev->vfe_base + wm_base + 0x20); + /* TD: Add IRQ subsample pattern */ + return; +} + +static void msm_vfe40_axi_clear_wm_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, uint8_t plane_idx) +{ + uint32_t val = 0; + uint32_t wm_base = VFE40_WM_BASE(stream_info->wm[plane_idx]); + /*WR_ADDR_CFG*/ + msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0xC); + /*WR_IMAGE_SIZE*/ + msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x14); + /*WR_BUFFER_CFG*/ + msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x18); + /*WR_IRQ_SUBSAMPLE_PATTERN*/ + msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x20); + return; +} + +static void msm_vfe40_axi_cfg_rdi_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, + uint8_t plane_idx) +{ + struct msm_vfe_axi_shared_data *axi_data = + &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->axi_stream_handle & 0xFF)]; + struct msm_vfe_axi_plane_cfg *plane_cfg = + &stream_cfg_cmd->plane_cfg[plane_idx]; + uint8_t rdi = stream_info->rdi[plane_idx]; + uint8_t rdi_master = stream_info->rdi_master[plane_idx]; + uint32_t rdi_reg_cfg; + + rdi_reg_cfg = msm_camera_io_r( + vfe_dev->vfe_base + VFE40_RDI_BASE(rdi)); + rdi_reg_cfg = (rdi_reg_cfg & 0xFFFFFFF) | rdi_master << 28; + msm_camera_io_w( + rdi_reg_cfg, vfe_dev->vfe_base + VFE40_RDI_BASE(rdi)); + + rdi_reg_cfg = msm_camera_io_r( + vfe_dev->vfe_base + VFE40_RDI_MN_BASE(rdi_master)); + rdi_reg_cfg &= ~((0xF << VFE40_RDI_MN_SEL_SHIFT(rdi_master)) | + (0x1 << VFE40_RDI_MN_FB_SHIFT(rdi_master))); + rdi_reg_cfg |= + (plane_cfg->rdi_cid << VFE40_RDI_MN_SEL_SHIFT(rdi_master) | + (stream_cfg_cmd->frame_base << + VFE40_RDI_MN_FB_SHIFT(rdi_master))); + msm_camera_io_w(rdi_reg_cfg, + vfe_dev->vfe_base + VFE40_RDI_MN_BASE(rdi_master)); +} + +static void msm_vfe40_axi_cfg_wm_xbar_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, + uint8_t plane_idx) +{ + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->axi_stream_handle & 0xFF)]; + struct msm_vfe_axi_plane_cfg *plane_cfg = + &stream_cfg_cmd->plane_cfg[plane_idx]; + uint8_t wm = stream_info->wm[plane_idx]; + uint32_t xbar_cfg = 0; + uint32_t xbar_reg_cfg = 0; + + switch (stream_cfg_cmd->stream_src) { + case PIX_ENCODER: + case PIX_VIEWFINDER: { + if (plane_cfg->output_plane_format + != CRCB_PLANE) { + /*SINGLE_STREAM_SEL*/ + xbar_cfg |= plane_cfg->output_plane_format << 8; + } else { + switch (stream_cfg_cmd->output_format) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + xbar_cfg |= 0x3 << 4; /*PAIR_STREAM_SWAP_CTRL*/ + break; + } + xbar_cfg |= 0x1 << 1; /*PAIR_STREAM_EN*/ + } + if (stream_cfg_cmd->stream_src == PIX_VIEWFINDER) + xbar_cfg |= 0x1; /*VIEW_STREAM_EN*/ + break; + } + case CAMIF_RAW: + xbar_cfg = 0x300; + break; + case IDEAL_RAW: + xbar_cfg = 0x400; + break; + case RDI: + if (stream_info->rdi[plane_idx] == 0) + xbar_cfg = 0x500; + else if (stream_info->rdi[plane_idx] == 1) + xbar_cfg = 0x600; + else if (stream_info->rdi[plane_idx] == 2) + xbar_cfg = 0x700; + break; + default: + pr_err("%s: Invalid stream src\n", __func__); + break; + } + xbar_reg_cfg = + msm_camera_io_r(vfe_dev->vfe_base + VFE40_XBAR_BASE(wm)); + xbar_reg_cfg &= ~(0xFFFF << VFE40_XBAR_SHIFT(wm)); + xbar_reg_cfg |= (xbar_cfg << VFE40_XBAR_SHIFT(wm)); + msm_camera_io_w(xbar_reg_cfg, + vfe_dev->vfe_base + VFE40_XBAR_BASE(wm)); + return; +} + +static void msm_vfe40_axi_clear_wm_xbar_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, uint8_t 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 + VFE40_XBAR_BASE(wm)); + xbar_reg_cfg &= ~(0xFFFF << VFE40_XBAR_SHIFT(wm)); + msm_camera_io_w(xbar_reg_cfg, + vfe_dev->vfe_base + VFE40_XBAR_BASE(wm)); +} + +#define MSM_ISP40_TOTAL_WM_UB 819 + +static void msm_vfe40_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; + uint8_t num_used_wms = 0; + uint32_t prop_size = 0; + uint32_t wm_ub_size; + uint32_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_ISP40_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 = + (axi_data->wm_image_size[i] * + prop_size)/total_image_size; + wm_ub_size = axi_data->hw_info->min_wm_ub + delta; + msm_camera_io_w(ub_offset << 16 | (wm_ub_size - 1), + vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10); + ub_offset += wm_ub_size; + } else + msm_camera_io_w(0, + vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10); + } +} + +static void msm_vfe40_cfg_axi_ub_equal_slicing( + struct vfe_device *vfe_dev) +{ + int i; + uint32_t ub_offset = 0; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + msm_camera_io_w(ub_offset << 16 | (VFE40_EQUAL_SLICE_UB - 1), + vfe_dev->vfe_base + VFE40_WM_BASE(i) + 0x10); + ub_offset += VFE40_EQUAL_SLICE_UB; + } +} + +static void msm_vfe40_cfg_axi_ub(struct vfe_device *vfe_dev) +{ + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + + if (axi_data->wm_ub_cfg_policy == MSM_WM_UB_EQUAL_SLICING) + msm_vfe40_cfg_axi_ub_equal_slicing(vfe_dev); + else + msm_vfe40_cfg_axi_ub_equal_default(vfe_dev); +} + +static void msm_vfe40_update_ping_pong_addr( + struct vfe_device *vfe_dev, + uint8_t wm_idx, uint32_t pingpong_status, unsigned long paddr) +{ + msm_camera_io_w(paddr, vfe_dev->vfe_base + + VFE40_PING_PONG_BASE(wm_idx, pingpong_status)); +} + +static long msm_vfe40_axi_halt(struct vfe_device *vfe_dev) +{ + uint32_t halt_mask; + halt_mask = msm_camera_io_r(vfe_dev->vfe_base + 0x2C); + halt_mask |= (1 << 8); + msm_camera_io_w_mb(halt_mask, vfe_dev->vfe_base + 0x2C); + init_completion(&vfe_dev->halt_complete); + msm_camera_io_w_mb(0x1, vfe_dev->vfe_base + 0x2C0); + return wait_for_completion_interruptible_timeout( + &vfe_dev->halt_complete, msecs_to_jiffies(500)); +} + +static uint32_t msm_vfe40_get_wm_mask( + uint32_t irq_status0, uint32_t irq_status1) +{ + return (irq_status0 >> 8) & 0x7F; +} + +static uint32_t msm_vfe40_get_comp_mask( + uint32_t irq_status0, uint32_t irq_status1) +{ + return (irq_status0 >> 25) & 0xF; +} + +static uint32_t msm_vfe40_get_pingpong_status( + struct vfe_device *vfe_dev) +{ + 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 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_wm_irq_mask( + struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info) +{ + +} + +static void msm_vfe40_stats_clear_wm_irq_mask( + struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info) +{ + +} + +static void msm_vfe40_stats_cfg_wm_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream_request_cmd *stream_cfg_cmd) +{ + +} + +static void msm_vfe40_stats_clear_wm_reg( + struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream *stream_info) +{ + +} + +static void msm_vfe40_stats_cfg_ub(struct vfe_device *vfe_dev) +{ +} + +static void msm_vfe40_stats_enable(struct vfe_device *vfe_dev, + uint32_t stats_mask, uint8_t enable) +{ + +} + +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) +{ +} + +static uint32_t msm_vfe40_stats_get_wm_mask( + uint32_t irq_status0, uint32_t irq_status1) +{ + return (irq_status0 >> 16) & 0xFF; +} + +static uint32_t msm_vfe40_stats_get_comp_mask( + uint32_t irq_status0, uint32_t irq_status1) +{ + return (irq_status0 >> 29) & 0x2; +} + +static uint32_t msm_vfe40_stats_get_frame_id( + struct vfe_device *vfe_dev) +{ + return vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; +} +static int msm_vfe40_get_platform_data(struct vfe_device *vfe_dev) +{ + int rc = 0; + vfe_dev->vfe_mem = platform_get_resource_byname(vfe_dev->pdev, + IORESOURCE_MEM, "vfe"); + if (!vfe_dev->vfe_mem) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto vfe_no_resource; + } + + vfe_dev->vfe_vbif_mem = platform_get_resource_byname( + vfe_dev->pdev, + IORESOURCE_MEM, "vfe_vbif"); + if (!vfe_dev->vfe_vbif_mem) { + pr_err("%s: no mem resource?\n", __func__); + rc = -ENODEV; + goto vfe_no_resource; + } + + vfe_dev->vfe_irq = platform_get_resource_byname(vfe_dev->pdev, + IORESOURCE_IRQ, "vfe"); + if (!vfe_dev->vfe_irq) { + pr_err("%s: no irq resource?\n", __func__); + rc = -ENODEV; + goto vfe_no_resource; + } + + vfe_dev->fs_vfe = regulator_get(&vfe_dev->pdev->dev, "vdd"); + if (IS_ERR(vfe_dev->fs_vfe)) { + pr_err("%s: Regulator get failed %ld\n", __func__, + PTR_ERR(vfe_dev->fs_vfe)); + vfe_dev->fs_vfe = NULL; + rc = -ENODEV; + goto vfe_no_resource; + } + + if (vfe_dev->pdev->id == 0) + vfe_dev->iommu_ctx = msm_iommu_get_ctx("vfe0"); + else if (vfe_dev->pdev->id == 1) + vfe_dev->iommu_ctx = msm_iommu_get_ctx("vfe1"); + if (!vfe_dev->iommu_ctx) { + pr_err("%s: no irq resource?\n", __func__); + rc = -ENODEV; + goto vfe_no_resource; + } + +vfe_no_resource: + return rc; +} + +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 v4l2_subdev_core_ops msm_vfe40_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_vfe40_subdev_ops = { + .core = &msm_vfe40_subdev_core_ops, +}; + +static struct v4l2_subdev_internal_ops msm_vfe40_internal_ops = { + .open = msm_isp_open_node, + .close = msm_isp_close_node, +}; + +struct msm_vfe_hardware_info vfe40_hw_info = { + .vfe_ops = { + .irq_ops = { + .read_irq_status = msm_vfe40_read_irq_status, + .process_camif_irq = msm_vfe40_process_camif_irq, + .process_reset_irq = msm_vfe40_process_reset_irq, + .process_halt_irq = msm_vfe40_process_halt_irq, + .process_reset_irq = msm_vfe40_process_reset_irq, + .process_error_irq = msm_vfe40_process_error_irq, + .process_reg_update = msm_vfe40_process_reg_update, + .process_axi_irq = msm_isp_process_axi_irq, + .process_stats_irq = msm_isp_process_stats_irq, + }, + .axi_ops = { + .reload_wm = msm_vfe40_axi_reload_wm, + .enable_wm = msm_vfe40_axi_enable_wm, + .cfg_comp_mask = msm_vfe40_axi_cfg_comp_mask, + .clear_comp_mask = msm_vfe40_axi_clear_comp_mask, + .cfg_wm_irq_mask = msm_vfe40_axi_cfg_wm_irq_mask, + .clear_wm_irq_mask = msm_vfe40_axi_clear_wm_irq_mask, + .cfg_framedrop = msm_vfe40_cfg_framedrop, + .clear_framedrop = msm_vfe40_clear_framedrop, + .cfg_wm_reg = msm_vfe40_axi_cfg_wm_reg, + .clear_wm_reg = msm_vfe40_axi_clear_wm_reg, + .cfg_wm_xbar_reg = msm_vfe40_axi_cfg_wm_xbar_reg, + .clear_wm_xbar_reg = msm_vfe40_axi_clear_wm_xbar_reg, + .cfg_rdi_reg = msm_vfe40_axi_cfg_rdi_reg, + .cfg_ub = msm_vfe40_cfg_axi_ub, + .update_ping_pong_addr = + msm_vfe40_update_ping_pong_addr, + .get_comp_mask = msm_vfe40_get_comp_mask, + .get_wm_mask = msm_vfe40_get_wm_mask, + .get_pingpong_status = msm_vfe40_get_pingpong_status, + .halt = msm_vfe40_axi_halt, + }, + .core_ops = { + .epoch_irq = msm_vfe40_epoch_irq_enb, + .reg_update = msm_vfe40_reg_update, + .cfg_camif = msm_vfe40_cfg_camif, + .update_camif_state = msm_vfe40_update_camif_state, + .reset_hw = msm_vfe40_reset_hardware, + .init_hw = msm_vfe40_init_hardware, + .init_hw_reg = msm_vfe40_init_hardware_reg, + .release_hw = msm_vfe40_release_hardware, + .get_platform_data = msm_vfe40_get_platform_data, + }, + .stats_ops = { + .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, + .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, + }, + }, + .axi_hw_info = &msm_vfe40_axi_hw_info, + .subdev_ops = &msm_vfe40_subdev_ops, + .subdev_internal_ops = &msm_vfe40_internal_ops, +}; +EXPORT_SYMBOL(vfe40_hw_info); diff --git a/drivers/media/video/msmb/isp/msm_isp40.h b/drivers/media/video/msmb/isp/msm_isp40.h new file mode 100644 index 00000000000..e9b151816a3 --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp40.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2013, 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_ISP40_H__ +#define __MSM_ISP40_H__ + +extern struct msm_vfe_hardware_info vfe40_hw_info; +#endif /* __MSM_ISP40_H__ */ diff --git a/drivers/media/video/msmb/isp/msm_isp_axi_util.c b/drivers/media/video/msmb/isp/msm_isp_axi_util.c new file mode 100644 index 00000000000..fe55b400901 --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp_axi_util.c @@ -0,0 +1,968 @@ +/* Copyright (c) 2013, 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 "msm_isp_util.h" +#include "msm_isp_axi_util.h" + +int msm_isp_axi_create_stream( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd) +{ + int i, rc = -1; + for (i = 0; i < MAX_NUM_STREAM; i++) { + if (axi_data->stream_info[i].state == AVALIABLE) + break; + } + + if (i == MAX_NUM_STREAM) { + pr_err("%s: No free stream\n", __func__); + return rc; + } + + 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; + + 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; + + 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 != AVALIABLE) + axi_data->stream_info[stream_idx].state = AVALIABLE; + 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_vfe_axi_stream_request_cmd *stream_cfg_cmd) +{ + int rc = -1; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->axi_stream_handle & 0xFF)]; + + switch (stream_cfg_cmd->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: + stream_info->num_planes = 1; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + stream_info->num_planes = 2; + break; + /*TD: Add more image format*/ + default: + pr_err("%s: Invalid output format\n", __func__); + 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->stream_src == RDI) { + if (axi_data->hw_info->num_rdi - + axi_data->num_used_rdi < stream_info->num_planes) { + pr_err("%s: No free RDI\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; + } + + return 0; +} + +static uint32_t msm_isp_axi_get_plane_size( + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd, int plane_idx) +{ + uint32_t size = 0; + struct msm_vfe_axi_plane_cfg *plane_cfg = stream_cfg_cmd->plane_cfg; + switch (stream_cfg_cmd->output_format) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + 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: + /* 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: + /* TODO: fix me */ + 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 / 2; + break; + /*TD: Add more image format*/ + default: + pr_err("%s: Invalid output format\n", __func__); + break; + } + return size; +} + +void msm_isp_axi_reserve_wm(struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd) +{ + int i, j; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->axi_stream_handle & 0xFF)]; + + 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_cfg_cmd->axi_stream_handle; + axi_data->wm_image_size[j] = + msm_isp_axi_get_plane_size( + stream_cfg_cmd, 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--; + } +} + +void msm_isp_axi_reserve_rdi(struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd) +{ + int i, j; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)]; + + for (i = 0; i < stream_info->num_planes; i++) { + uint8_t csid = stream_cfg_cmd->plane_cfg[i].csid_src; + + for (j = 0; j < axi_data->hw_info->num_rdi; j++) { + if (!axi_data->free_rdi[j]) { + axi_data->free_rdi[j] = 1; + axi_data->num_used_rdi++; + break; + } + } + stream_info->rdi[i] = j; + + for (j = 0; j < axi_data->hw_info->num_rdi; j++) { + if (!axi_data->free_rdi_master[csid][j]) { + axi_data->free_rdi_master[csid][j] = 1; + axi_data->num_used_rdi++; + break; + } + } + stream_info->rdi_master[i] = + csid * axi_data->hw_info->num_rdi_master + j; + } + return; +} + +void msm_isp_axi_reserve_comp_mask( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd) +{ + int i; + uint8_t comp_mask = 0; + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->axi_stream_handle & 0xFF)]; + 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_cfg_cmd->axi_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; + return; +} + +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--; +} + +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, j; + uint8_t src_state; + 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; + + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + stream_info = &axi_data->stream_info[ + (stream_cfg_cmd->stream_handle[i] & 0xFF)]; + if (stream_info->state != valid_state) { + pr_err("%s: Invalid stream state\n", __func__); + rc = -EINVAL; + break; + } + /* + * For RDI stream, if multiple RDIs are used + * check if all the RDI srcs are in the same state, on/off + */ + if (stream_info->stream_src == RDI) { + src_state = axi_data->src_info[ + stream_info->rdi[0]+1].active; + for (j = 0; j < stream_info->num_planes; j++) { + if (src_state != + axi_data->src_info[ + stream_info->rdi[j]+1].active) { + pr_err("%s: RDI stream has inconsistent state\n", + __func__); + rc = -EINVAL; + break; + } + } + } + + if (stream_cfg_cmd->cmd == START_STREAM) { + 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 has no valid buffer queue\n", + __func__); + rc = -EINVAL; + break; + } + } + } + return rc; +} + +void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev) +{ + 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->framedrop_update) { + if (stream_info->init_frame_drop == 0) { + stream_info->framedrop_update = 0; + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_framedrop(vfe_dev, stream_info); + } + } + if (stream_info->stream_type == BURST_STREAM) { + if (stream_info->burst_frame_count == 0 && + stream_info->state == ACTIVE) { + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_framedrop(vfe_dev, stream_info); + if (stream_info->stream_src == RDI) { + uint32_t wm_reload_mask = 0, + reg_update_mask = 0; + stream_info->state = STOP_PENDING; + msm_isp_axi_stream_enable_cfg( + vfe_dev, stream_info, + &wm_reload_mask, + ®_update_mask); + } + } + } + } +} + +void msm_isp_update_framedrop_count( + struct vfe_device *vfe_dev) +{ + 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->framedrop_update) { + stream_info->init_frame_drop--; + if (stream_info->init_frame_drop == 1) { + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, 0xF); + } + } + if (stream_info->stream_type == BURST_STREAM) { + stream_info->burst_frame_count--; + if (stream_info->burst_frame_count == 1) { + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, 0xF); + } else if (stream_info->burst_frame_count == 0) { + if (stream_info->stream_src != RDI) { + vfe_dev->hw_info->vfe_ops.core_ops. + update_camif_state(vfe_dev, + DISABLE_CAMIF); + pr_err("%s: pending burst_cnt = %d, disable camif\n", + __func__, + stream_info->burst_frame_count); + } + } + } + } +} + +void msm_isp_new_frame_notify(struct vfe_device *vfe_dev, + enum msm_vfe_input_src frame_src) { + switch (frame_src) { + case VFE_PIX_0: + ISP_DBG("%s: PIX0 frame id: %lu\n", __func__, + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id); + msm_isp_update_framedrop_count(vfe_dev); + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id++; + if (vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id == 0) + vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id = 1; + break; + case VFE_RAW_0: + case VFE_RAW_1: + case VFE_RAW_2: + break; + default: + pr_err("%s: invalid frame src %d received\n", + __func__, frame_src); + break; + } +} +void msm_isp_calculate_framedrop( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd) +{ + struct msm_vfe_axi_stream *stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->axi_stream_handle & 0xFF)]; + uint32_t framedrop_period = 1; + + switch (stream_cfg_cmd->frame_skip_pattern) { + case NO_SKIP: + stream_info->framedrop_pattern = VFE_NO_DROP; + framedrop_period = 1; + break; + case EVERY_2FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_2FRAME; + framedrop_period = 2; + break; + case EVERY_4FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_4FRAME; + framedrop_period = 4; + break; + case EVERY_8FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_8FRAME; + framedrop_period = 8; + break; + case EVERY_16FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_16FRAME; + framedrop_period = 16; + break; + case EVERY_32FRAME: + stream_info->framedrop_pattern = VFE_DROP_EVERY_32FRAME; + framedrop_period = 32; + break; + default: + stream_info->framedrop_pattern = VFE_NO_DROP; + framedrop_period = 1; + break; + } + + 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->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; + } +} + +int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0, i; + struct msm_vfe_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__); + msm_isp_axi_destroy_stream(&vfe_dev->axi_data, + (stream_cfg_cmd->axi_stream_handle & 0xFF)); + return rc; + } + + msm_isp_axi_reserve_wm(&vfe_dev->axi_data, stream_cfg_cmd); + if (stream_cfg_cmd->stream_src == RDI) + msm_isp_axi_reserve_rdi(&vfe_dev->axi_data, stream_cfg_cmd); + + stream_info = + &vfe_dev->axi_data. + stream_info[(stream_cfg_cmd->axi_stream_handle & 0xFF)]; + + msm_isp_calculate_framedrop(&vfe_dev->axi_data, stream_cfg_cmd); + vfe_dev->hw_info->vfe_ops.axi_ops.cfg_framedrop(vfe_dev, stream_info); + + if (stream_info->num_planes > 1) { + msm_isp_axi_reserve_comp_mask( + &vfe_dev->axi_data, stream_cfg_cmd); + 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_cfg_cmd, i); + + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_wm_xbar_reg(vfe_dev, stream_cfg_cmd, i); + + if (stream_cfg_cmd->stream_src == RDI) + vfe_dev->hw_info->vfe_ops.axi_ops. + cfg_rdi_reg(vfe_dev, stream_cfg_cmd, 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 = + &axi_data->stream_info[ + (stream_release_cmd->stream_handle & 0xFF)]; + struct msm_vfe_axi_stream_cfg_cmd stream_cfg; + + if (stream_info->state == AVALIABLE) { + 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) { + msm_isp_axi_free_comp_mask(&vfe_dev->axi_data, stream_info); + vfe_dev->hw_info->vfe_ops.axi_ops. + clear_comp_mask(vfe_dev, 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, + (stream_release_cmd->stream_handle & 0xFF)); + + return rc; +} + +void msm_isp_axi_stream_enable_cfg( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, + uint32_t *wm_reload_mask, uint32_t *reg_update_mask) +{ + 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++) { + /*TD: Frame base command*/ + if (stream_info->state == START_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); + + *wm_reload_mask |= (1 << stream_info->wm[i]); + if (stream_info->stream_src == RDI) + *reg_update_mask |= (1 << stream_info->rdi[i]); + } + if (stream_info->state == START_PENDING) { + axi_data->num_active_stream++; + stream_info->state = ACTIVE; + } else { + axi_data->num_active_stream--; + stream_info->state = INACTIVE; + } +} + +void msm_isp_axi_stream_update(struct vfe_device *vfe_dev) +{ + int i; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + uint32_t wm_reload_mask = 0x0, reg_update_mask = 0x1; + int send_update_complete = 0; + 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) { + msm_isp_axi_stream_enable_cfg( + vfe_dev, &axi_data->stream_info[i], + &wm_reload_mask, ®_update_mask); + if (axi_data->stream_info[i].state == STOP_PENDING) + axi_data->stream_info[i].state = STOPPING; + } else if (axi_data->stream_info[i].state == STOPPING) { + send_update_complete = 1; + axi_data->stream_info[i].state = INACTIVE; + } + } + /*Reload AXI*/ + vfe_dev->hw_info->vfe_ops.axi_ops. + reload_wm(vfe_dev, wm_reload_mask); + /*Reg update per src*/ + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, reg_update_mask); + if (send_update_complete) { + ISP_DBG("%s: send update complete\n", __func__); + vfe_dev->axi_data.stream_update = 0; + 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, + struct timeval *tv) +{ + int i, rc = -1; + struct msm_isp_buffer *buf; + struct msm_isp_event_data buf_event; + uint32_t pingpong_bit = 0; + uint32_t bufq_handle = stream_info->bufq_handle; + + pingpong_bit = (~(pingpong_status >> stream_info->wm[0]) & 0x1); + rc = vfe_dev->buf_mgr->ops->get_buf( + vfe_dev->buf_mgr, bufq_handle, &buf); + if (rc < 0) { + if (stream_info->stream_type == BURST_STREAM && + stream_info->burst_frame_count <= 1) { + rc = 0; + if (pingpong_bit) + buf = stream_info->buf[0]; + else + buf = stream_info->buf[1]; + } else { + pr_err("%s: No free buffer, stream_type = %d, burst_cnt = %d\n", + __func__, stream_info->stream_type, + stream_info->burst_frame_count); + 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++) { + if (pingpong_bit != + (~(pingpong_status >> stream_info->wm[i]) & 0x1)) { + pr_warn("%s: Write master ping pong mismatch. Status: 0x%x\n", + __func__, pingpong_status); + } + } + 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); + + if (stream_info->buf[pingpong_bit]) { + if (stream_info->buf_divert) { + buf_event.frame_id = stream_info->frame_id; + buf_event.timestamp = *tv; + 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 = + stream_info->buf[pingpong_bit]->buf_idx; + msm_isp_send_event( + vfe_dev, ISP_EVENT_BUF_DIVERT, &buf_event); + } else { + vfe_dev->buf_mgr->ops->buf_done(vfe_dev->buf_mgr, + stream_info->buf[pingpong_bit]->bufq_handle, + stream_info->buf[pingpong_bit]->buf_idx, + tv, stream_info->frame_id); + } + } + + 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; +} + +enum msm_isp_camif_update_state + 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; + uint8_t cur_pix_count = axi_data->src_info[VFE_PIX_0]. + pix_stream_count; + uint8_t cur_raw_count = axi_data->src_info[VFE_PIX_0]. + raw_stream_count; + uint8_t pix_stream_cnt = 0; + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->stream_handle[i] & 0xFF)]; + if (stream_info->stream_src != RDI) + pix_stream_cnt++; + if (stream_info->stream_src == PIX_ENCODER || + stream_info->stream_src == PIX_VIEWFINDER) { + 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 || + stream_info->stream_src == IDEAL_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--; + } + } + if (pix_stream_cnt) { + if ((cur_pix_count + cur_raw_count == 0) && + (axi_data->src_info[VFE_PIX_0]. + pix_stream_count + + axi_data->src_info[VFE_PIX_0]. + raw_stream_count != 0)) { + return ENABLE_CAMIF; + } + + if ((cur_pix_count + cur_raw_count != 0) && + (axi_data->src_info[VFE_PIX_0]. + pix_stream_count + + axi_data->src_info[VFE_PIX_0]. + raw_stream_count == 0)) { + return DISABLE_CAMIF; + } + } + + return NO_UPDATE; +} + +void msm_camera_io_dump_2(void __iomem *addr, int size) +{ + char line_str[128], *p_str; + int i; + u32 *p = (u32 *) addr; + u32 data; + ISP_DBG("%s: %p %d\n", __func__, addr, size); + line_str[0] = '\0'; + p_str = line_str; + for (i = 0; i < size/4; i++) { + if (i % 4 == 0) { + snprintf(p_str, 12, "%08x: ", (u32) p); + p_str += 10; + } + data = readl_relaxed(p++); + snprintf(p_str, 12, "%08x ", data); + p_str += 9; + if ((i + 1) % 4 == 0) { + ISP_DBG("%s\n", line_str); + line_str[0] = '\0'; + p_str = line_str; + } + } + if (line_str[0] != '\0') + ISP_DBG("%s\n", line_str); +} + +int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0, i; + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd = arg; + uint32_t wm_reload_mask = 0x0, reg_update_mask = 0x1; + struct msm_vfe_axi_stream *stream_info; + struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; + uint8_t src_state; + enum msm_isp_camif_update_state camif_update; + uint8_t wait_for_complete = 0; + 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_update_camif_output_count(vfe_dev, stream_cfg_cmd); + + if (camif_update == DISABLE_CAMIF) + vfe_dev->hw_info->vfe_ops.core_ops. + update_camif_state(vfe_dev, DISABLE_CAMIF_IMMEDIATELY); + + /* + * Stream start either immediately or at reg update + * Depends on whether the stream src is active + * If source is on, start and stop have to be done during reg update + * If source is off, start can happen immediately or during reg update + * stop has to be done immediately. + */ + for (i = 0; i < stream_cfg_cmd->num_streams; i++) { + stream_info = + &axi_data->stream_info[ + (stream_cfg_cmd->stream_handle[i] & 0xFF)]; + + + if (stream_info->stream_src == RDI) + src_state = + axi_data->src_info[ + stream_info->rdi[0]+1].active; + else + src_state = axi_data->src_info[0].active; + + stream_info->state = (stream_cfg_cmd->cmd == START_STREAM) ? + START_PENDING : STOP_PENDING; + + if (stream_cfg_cmd->cmd == START_STREAM) { + /*Set address for both PING & PONG register */ + rc = msm_isp_cfg_ping_pong_address(vfe_dev, + stream_info, VFE_PING_FLAG, NULL); + rc = msm_isp_cfg_ping_pong_address(vfe_dev, + stream_info, VFE_PONG_FLAG, NULL); + } + if (src_state && camif_update != DISABLE_CAMIF) { + /*On the fly stream start/stop */ + wait_for_complete = 1; + reg_update_mask = 0xF; /*TD: Maybe set this per src*/ + } 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, + &wm_reload_mask, ®_update_mask); + } + } + if (!wait_for_complete) { + /*Reload AXI*/ + vfe_dev->hw_info->vfe_ops.axi_ops. + reload_wm(vfe_dev, wm_reload_mask); + /*Reg update per src*/ + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, reg_update_mask); + + if (camif_update == ENABLE_CAMIF) + vfe_dev->hw_info->vfe_ops.core_ops. + update_camif_state(vfe_dev, camif_update); + } else { + unsigned long flags; + spin_lock_irqsave(&vfe_dev->shared_data_lock, flags); + init_completion(&vfe_dev->stream_config_complete); + axi_data->stream_update = 1; + spin_unlock_irqrestore(&vfe_dev->shared_data_lock, flags); + /*Reload AXI*/ + vfe_dev->hw_info->vfe_ops.axi_ops. + reload_wm(vfe_dev, wm_reload_mask); + /*Reg update per src*/ + vfe_dev->hw_info->vfe_ops.core_ops. + reg_update(vfe_dev, reg_update_mask); + rc = wait_for_completion_interruptible_timeout( + &vfe_dev->stream_config_complete, + msecs_to_jiffies(500)); + if (rc == 0) { + pr_err("%s: wait timeout\n", __func__); + rc = -1; + } + } + return rc; +} + +void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct timeval *tv) +{ + int i; + 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; + + ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0); + 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; + + 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++) { + comp_info = &axi_data->composite_info[i]; + if (comp_mask & (1 << i)) { + if (!comp_info->stream_handle) { + pr_err("%s: Invalid handle for composite irq\n", + __func__); + } else { + stream_idx = comp_info->stream_handle & 0xFF; + stream_info = + &axi_data->stream_info[stream_idx]; + ISP_DBG("%s: stream%d frame id: 0x%x\n", + __func__, + stream_idx, stream_info->frame_id); + stream_info->frame_id++; + msm_isp_cfg_ping_pong_address(vfe_dev, + stream_info, pingpong_status, tv); + } + } + wm_mask &= ~(comp_info->stream_composite_mask); + } + + for (i = 0; i < axi_data->hw_info->num_wm; i++) { + if (wm_mask & (1 << i)) { + if (!axi_data->free_wm[i]) { + pr_err("%s: Invalid handle for wm irq\n", + __func__); + continue; + } + stream_idx = axi_data->free_wm[i] & 0xFF; + stream_info = &axi_data->stream_info[stream_idx]; + stream_info->frame_id++; + msm_isp_cfg_ping_pong_address(vfe_dev, + stream_info, pingpong_status, tv); + } + } + return; +} diff --git a/drivers/media/video/msmb/isp/msm_isp_axi_util.h b/drivers/media/video/msmb/isp/msm_isp_axi_util.h new file mode 100644 index 00000000000..4847c06316c --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp_axi_util.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2013, 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.h" + +int msm_isp_axi_create_stream( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_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_vfe_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_request_cmd *stream_cfg_cmd); + +void msm_isp_axi_reserve_rdi( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd); + +void msm_isp_axi_reserve_comp_mask( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd); + +int msm_isp_axi_check_stream_state( + struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd); + +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); + +void msm_isp_axi_stream_enable_cfg(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, + uint32_t *wm_reload_mask, uint32_t *reg_update_mask); + +void msm_isp_axi_stream_update(struct vfe_device *vfe_dev); + +int msm_isp_cfg_ping_pong_address(struct vfe_device *vfe_dev, + struct msm_vfe_axi_stream *stream_info, uint32_t pingpong_status, + struct timeval *tv); + +void msm_isp_update_framedrop_reg(struct vfe_device *vfe_dev); +void msm_isp_update_framedrop_count(struct vfe_device *vfe_dev); +void msm_isp_new_frame_notify(struct vfe_device *vfe_dev, + enum msm_vfe_input_src frame_src); +void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct timeval *tv); +#endif /* __MSM_ISP_AXI_UTIL_H__ */ diff --git a/drivers/media/video/msmb/isp/msm_isp_stats_util.c b/drivers/media/video/msmb/isp/msm_isp_stats_util.c new file mode 100644 index 00000000000..15f4c23e26e --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp_stats_util.c @@ -0,0 +1,69 @@ +/* Copyright (c) 2013, 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 "msm_isp_util.h" +#include "msm_isp_stats_util.h" + +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); + 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. + get_wm_mask(irq_status0, irq_status1); + if (!(stats_comp_mask || stats_mask)) + return; + frame_id = vfe_dev->hw_info->vfe_ops.stats_ops.get_frame_id(vfe_dev); + /* TD: process comp/non comp stats */ +} + +int msm_isp_request_stats_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0; + /*To Do*/ + return rc; +} + +int msm_isp_release_stats_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0; + struct msm_vfe_stats_stream_release_cmd *stream_release_cmd = arg; + struct msm_vfe_stats_shared_data *stats_data = &vfe_dev->stats_data; + struct msm_vfe_stats_stream *stream_info = + &stats_data->stream_info[ + (stream_release_cmd->stream_handle & 0xFF)]; + + if (stream_info == NULL) + rc = -1; + return rc; +} + +int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg) +{ + int rc = 0; + uint32_t stats_mask = 0; + uint8_t enable = 0; + uint32_t pingpong_status = 0; + struct msm_isp_buffer *buf = NULL; + enum msm_isp_stats_type stats_type = MSM_ISP_STATS_BE; + vfe_dev->hw_info->vfe_ops.stats_ops. + stats_enable(vfe_dev, stats_mask, enable); + vfe_dev->hw_info->vfe_ops.stats_ops. + update_ping_pong_addr(vfe_dev, stats_type, + pingpong_status, buf->mapped_info[0].paddr); + return rc; +} diff --git a/drivers/media/video/msmb/isp/msm_isp_stats_util.h b/drivers/media/video/msmb/isp/msm_isp_stats_util.h new file mode 100644 index 00000000000..4feb653afd5 --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp_stats_util.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2013, 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.h" + +void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, + uint32_t irq_status0, uint32_t irq_status1, + struct timeval *tv); +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); +#endif /* __MSM_ISP_STATS_UTIL_H__ */ diff --git a/drivers/media/video/msmb/isp/msm_isp_util.c b/drivers/media/video/msmb/isp/msm_isp_util.c new file mode 100644 index 00000000000..ee3e50efe20 --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp_util.c @@ -0,0 +1,462 @@ +/* Copyright (c) 2013, 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 "msm.h" +#include "msm_isp_util.h" +#include "msm_isp_axi_util.h" +#include "msm_isp_stats_util.h" +#include "msm_camera_io_util.h" + +#define MAX_ISP_V4l2_EVENTS 100 + +void msm_isp_gettimeofday(struct timeval *tv) +{ + struct timespec ts; + + ktime_get_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->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); + 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; +} + +int msm_isp_cfg_pix(struct vfe_device *vfe_dev, + struct msm_vfe_pix_cfg *pix_cfg) +{ + int rc = 0; + /*TD Validate config info + * should check if all streams are off */ + + vfe_dev->axi_data.src_info[VFE_PIX_0].input_mux = pix_cfg->input_mux; + + vfe_dev->hw_info->vfe_ops.core_ops.cfg_camif(vfe_dev, pix_cfg); + 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: + msm_isp_cfg_pix(vfe_dev, &input_cfg->d.pix_cfg); + break; + case VFE_RAW_0: + case VFE_RAW_1: + case VFE_RAW_2: + case VFE_SRC_MAX: + break; + } + return rc; +} + +long msm_isp_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd); + + mutex_lock(&vfe_dev->mutex); + ISP_DBG("%s cmd: %d\n", __func__, cmd); + + switch (cmd) { + case VIDIOC_MSM_VFE_REG_CFG: { + msm_isp_proc_cmd(vfe_dev, arg); + break; + } + case VIDIOC_MSM_ISP_REQUEST_BUF: + case VIDIOC_MSM_ISP_ENQUEUE_BUF: + case VIDIOC_MSM_ISP_RELEASE_BUF: { + msm_isp_proc_buf_cmd(vfe_dev->buf_mgr, cmd, arg); + break; + } + case VIDIOC_MSM_ISP_REQUEST_STREAM: + msm_isp_request_axi_stream(vfe_dev, arg); + break; + case VIDIOC_MSM_ISP_RELEASE_STREAM: + msm_isp_release_axi_stream(vfe_dev, arg); + break; + case VIDIOC_MSM_ISP_CFG_STREAM: + msm_isp_cfg_axi_stream(vfe_dev, arg); + break; + case VIDIOC_MSM_ISP_INPUT_CFG: + msm_isp_cfg_input(vfe_dev, arg); + break; + case VIDIOC_MSM_ISP_SET_SRC_STATE: + msm_isp_set_src_state(vfe_dev, arg); + break; + case VIDIOC_MSM_ISP_REQUEST_STATS_STREAM: + msm_isp_request_stats_stream(vfe_dev, arg); + break; + case VIDIOC_MSM_ISP_RELEASE_STATS_STREAM: + msm_isp_release_stats_stream(vfe_dev, arg); + break; + case VIDIOC_MSM_ISP_CFG_STATS_STREAM: + msm_isp_cfg_stats_stream(vfe_dev, arg); + break; + } + + mutex_unlock(&vfe_dev->mutex); + return 0; +} + +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) +{ + switch (reg_cfg_cmd->cmd_type) { + case VFE_WRITE: { + if (resource_size(vfe_dev->vfe_mem) < + (reg_cfg_cmd->reg_offset + reg_cfg_cmd->len)) { + pr_err("%s: Invalid length\n", __func__); + return -EINVAL; + } + msm_camera_io_memcpy(vfe_dev->vfe_base + + reg_cfg_cmd->reg_offset, + cfg_data + reg_cfg_cmd->cmd_data/4, reg_cfg_cmd->len); + break; + } + case VFE_WRITE_MB: { + uint32_t *data_ptr = cfg_data + reg_cfg_cmd->cmd_data/4; + msm_camera_io_w_mb(*data_ptr, vfe_dev->vfe_base + + reg_cfg_cmd->reg_offset); + break; + } + case VFE_WRITE_MASK: { + uint32_t temp; + temp = msm_camera_io_r(vfe_dev->vfe_base + + reg_cfg_cmd->reg_offset); + temp |= reg_cfg_cmd->cmd_data; + msm_camera_io_w(temp, vfe_dev->vfe_base + + reg_cfg_cmd->reg_offset); + break; + } + case VFE_CLEAR_MASK: { + uint32_t temp; + temp = msm_camera_io_r(vfe_dev->vfe_base + + reg_cfg_cmd->reg_offset); + temp &= ~reg_cfg_cmd->cmd_data; + msm_camera_io_w(temp, vfe_dev->vfe_base + + reg_cfg_cmd->reg_offset); + break; + } + case VFE_WRITE_AUTO_INCREMENT: { + int i; + uint32_t *data_ptr = cfg_data + reg_cfg_cmd->cmd_data/4; + for (i = 0; i < reg_cfg_cmd->len/4; i++) + msm_camera_io_w(*data_ptr++, + vfe_dev->vfe_base + reg_cfg_cmd->reg_offset); + break; + } + case VFE_READ: { + int i; + uint32_t *data_ptr = cfg_data + reg_cfg_cmd->cmd_data/4; + for (i = 0; i < reg_cfg_cmd->len/4; i++) + *data_ptr++ = msm_camera_io_r( + vfe_dev->vfe_base + reg_cfg_cmd->reg_offset++); + 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; + + reg_cfg_cmd = kzalloc(sizeof(struct msm_vfe_reg_cfg_cmd)* + proc_cmd->num_cfg, GFP_KERNEL); + if (!reg_cfg_cmd) { + pr_err("%s: reg_cfg alloc failed\n", __func__); + rc = -ENOMEM; + goto reg_cfg_failed; + } + + 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(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 (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++) + msm_isp_send_hw_cmd(vfe_dev, ®_cfg_cmd[i], cfg_data); + + 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_isp_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_isp_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: + 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, 1, 6); + 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, 1, 5); + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + val = CAL_WORD(pixel_per_line, 1, 8); + break; + /*TD: Add more image format*/ + default: + pr_err("%s: Invalid output format\n", __func__); + break; + } + return val; +} + +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; + + vfe_dev->hw_info->vfe_ops.irq_ops. + read_irq_status(vfe_dev, &irq_status0, &irq_status1); + if ((irq_status0 == 0) && (irq_status1 == 0)) { + ISP_DBG("%s: irq_status0 & 1 are both 0!\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("%s: Tasklet queue overflow\n", __func__); + 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_gettimeofday(&queue_cmd->tv); + 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 timeval tv; + 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; + tv = queue_cmd->tv; + 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); + irq_ops->process_camif_irq(vfe_dev, + irq_status0, irq_status1); + irq_ops->process_error_irq(vfe_dev, + irq_status0, irq_status1); + irq_ops->process_axi_irq(vfe_dev, + irq_status0, irq_status1, &tv); + irq_ops->process_stats_irq(vfe_dev, + irq_status0, irq_status1, &tv); + irq_ops->process_reg_update(vfe_dev, irq_status0, irq_status1); + } +} + +void msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg) +{ + struct msm_vfe_axi_src_state *src_state = arg; + vfe_dev->axi_data.src_info[src_state->input_src].active = + src_state->src_active; +} + +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; + ISP_DBG("%s\n", __func__); + + mutex_lock(&vfe_dev->mutex); + if (vfe_dev->vfe_open_cnt == 1) { + pr_err("VFE already open\n"); + mutex_unlock(&vfe_dev->mutex); + return -ENODEV; + } + + if (vfe_dev->hw_info->vfe_ops.core_ops.init_hw(vfe_dev) < 0) { + pr_err("%s: init hardware failed\n", __func__); + mutex_unlock(&vfe_dev->mutex); + return -EBUSY; + } + + rc = vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev); + if (rc <= 0) { + pr_err("%s: reset timeout\n", __func__); + mutex_unlock(&vfe_dev->mutex); + return -EINVAL; + } + vfe_dev->hw_info->vfe_ops.core_ops.init_hw_reg(vfe_dev); + + vfe_dev->buf_mgr->ops->attach_ctx(vfe_dev->buf_mgr, vfe_dev->iommu_ctx); + 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)); + vfe_dev->axi_data.hw_info = vfe_dev->hw_info->axi_hw_info; + + ISP_DBG("%s: HW Version: 0x%x\n", + __func__, msm_camera_io_r(vfe_dev->vfe_base)); + + vfe_dev->vfe_open_cnt++; + vfe_dev->taskletq_idx = 0; + mutex_unlock(&vfe_dev->mutex); + return 0; +} + +int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + long rc; + struct vfe_device *vfe_dev = v4l2_get_subdevdata(sd); + ISP_DBG("%s\n", __func__); + mutex_lock(&vfe_dev->mutex); + if (vfe_dev->vfe_open_cnt == 0) { + pr_err("%s: Invalid close\n", __func__); + mutex_unlock(&vfe_dev->mutex); + return -ENODEV; + } + + rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev); + if (rc <= 0) + pr_err("%s: halt timeout\n", __func__); + + vfe_dev->buf_mgr->ops->buf_mgr_deinit(vfe_dev->buf_mgr); + vfe_dev->buf_mgr->ops->detach_ctx(vfe_dev->buf_mgr, vfe_dev->iommu_ctx); + vfe_dev->hw_info->vfe_ops.core_ops.release_hw(vfe_dev); + + vfe_dev->vfe_open_cnt--; + mutex_unlock(&vfe_dev->mutex); + return 0; +} diff --git a/drivers/media/video/msmb/isp/msm_isp_util.h b/drivers/media/video/msmb/isp/msm_isp_util.h new file mode 100644 index 00000000000..729c8b534f7 --- /dev/null +++ b/drivers/media/video/msmb/isp/msm_isp_util.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2013, 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.h" + +/* #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 + +void msm_isp_gettimeofday(struct timeval *tv); + +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_isp_event_data *event_data); +int msm_isp_cal_word_per_line(uint32_t output_format, + uint32_t pixel_per_line); +irqreturn_t msm_isp_process_irq(int irq_num, void *data); +void msm_isp_set_src_state(struct vfe_device *vfe_dev, void *arg); +void msm_isp_do_tasklet(unsigned long data); + +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); +#endif /* __MSM_ISP_UTIL_H__ */ diff --git a/drivers/media/video/msmb/ispif/Makefile b/drivers/media/video/msmb/ispif/Makefile new file mode 100644 index 00000000000..908cc28561a --- /dev/null +++ b/drivers/media/video/msmb/ispif/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -Idrivers/media/video/msmb +ccflags-y += -Idrivers/media/video/msmb/sensor/io +obj-$(CONFIG_MSM_CSID) += msm_ispif.o diff --git a/drivers/media/video/msmb/ispif/msm_ispif.c b/drivers/media/video/msmb/ispif/msm_ispif.c new file mode 100644 index 00000000000..ffb926390f3 --- /dev/null +++ b/drivers/media/video/msmb/ispif/msm_ispif.c @@ -0,0 +1,954 @@ +/* Copyright (c) 2013, 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 "msm_ispif.h" +#include "msm.h" +#include "msm_ispif_hwreg.h" +#include "msm_sd.h" +#include "msm_camera_io_util.h" + +#define V4L2_IDENT_ISPIF 50001 +#define MSM_ISPIF_DRV_NAME "msm_ispif" +#define DUMP_BUFF_SIZE_128 128 + +#define ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY 0x01 +#define ISPIF_INTF_CMD_DISABLE_FRAME_BOUNDARY 0x00 +#define ISPIF_INTF_CMD_DISABLE_IMMEDIATELY 0x02 + +#define CONFIG_MSMB_CAMERA_DEBUG +#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(void __iomem *addr, int size) +{ + char line_str[DUMP_BUFF_SIZE_128], *p_str; + int i; + u32 *p = (u32 *) addr; + u32 data; + + + CDBG("%s: %p %d\n", __func__, addr, size); + line_str[0] = '\0'; + p_str = line_str; + for (i = 0; i < size/4; i++) { + if (i % 4 == 0) { + snprintf(p_str, 12, "%08x: ", (u32) p); + p_str += 10; + } + data = readl_relaxed(p++); + snprintf(p_str, 12, "%08x ", data); + p_str += 9; + if ((i + 1) % 4 == 0) { + CDBG("%s\n", line_str); + line_str[0] = '\0'; + p_str = line_str; + } + } + if (line_str[0] != '\0') + CDBG("%s\n", line_str); +} + +static void msm_ispif_io_dump_reg(struct ispif_device *ispif) +{ + int size; + + if (!ispif->enb_dump_reg) + return; + size = 0x250; + msm_ispif_io_dump(ispif->base+0x100, size); +} + +static int msm_ispif_intf_reset(struct ispif_device *ispif, + struct msm_ispif_param_data *params) +{ + + int i, rc = 0; + enum msm_ispif_intftype intf_type; + uint32_t data = (0x1 << STROBED_RST_EN); + + for (i = 0; i < params->num; i++) { + intf_type = params->entries[i].intftype; + ispif->sof_count[params->vfe_intf].sof_cnt[intf_type] = 0; + switch (intf_type) { + case PIX0: + data |= (0x1 << PIX_0_VFE_RST_STB) | + (0x1 << PIX_0_CSID_RST_STB); + break; + case RDI0: + data |= (0x1 << RDI_0_VFE_RST_STB) | + (0x1 << RDI_0_CSID_RST_STB); + break; + case PIX1: + data |= (0x1 << PIX_1_VFE_RST_STB) | + (0x1 << PIX_1_CSID_RST_STB); + break; + case RDI1: + data |= (0x1 << RDI_1_VFE_RST_STB) | + (0x1 << RDI_1_CSID_RST_STB); + break; + case RDI2: + data |= (0x1 << RDI_2_VFE_RST_STB) | + (0x1 << RDI_2_CSID_RST_STB); + break; + default: + rc = -EINVAL; + break; + } + } + if (data > 0x1) { + unsigned long jiffes = msecs_to_jiffies(500); + long lrc = 0; + if (params->vfe_intf == VFE0) + msm_camera_io_w(data, ispif->base + ISPIF_RST_CMD_ADDR); + else + msm_camera_io_w(data, ispif->base + + ISPIF_RST_CMD_1_ADDR); + lrc = wait_for_completion_interruptible_timeout( + &ispif->reset_complete, jiffes); + if (lrc < 0 || !lrc) { + pr_err("%s: wait timeout ret = %ld\n", __func__, lrc); + rc = -EIO; + } + } + return rc; +} + +static int msm_ispif_reset(struct ispif_device *ispif) +{ + int rc = 0; + unsigned long jiffes = msecs_to_jiffies(500); + long lrc = 0; + + memset(ispif->sof_count, 0, sizeof(ispif->sof_count)); + msm_camera_io_w(ISPIF_RST_CMD_MASK, ispif->base + ISPIF_RST_CMD_ADDR); + if (ispif->csid_version == CSID_VERSION_V3) + msm_camera_io_w_mb(ISPIF_RST_CMD_1_MASK, ispif->base + + ISPIF_RST_CMD_1_ADDR); + CDBG("%s: Sending reset\n", __func__); + lrc = wait_for_completion_interruptible_timeout( + &ispif->reset_complete, jiffes); + if (lrc < 0 || !lrc) { + pr_err("%s: wait timeout ret = %ld\n", __func__, lrc); + rc = -EIO; + } + CDBG("%s: reset returned\n", __func__); + return rc; +} + +static int msm_ispif_subdev_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + BUG_ON(!chip); + chip->ident = V4L2_IDENT_ISPIF; + chip->revision = 0; + return 0; +} + +static void msm_ispif_sel_csid_core(struct ispif_device *ispif, + uint8_t intftype, uint8_t csid, uint8_t vfe_intf) +{ + int rc = 0; + uint32_t data = 0; + + if (ispif->csid_version <= CSID_VERSION_V2) { + if (ispif->ispif_clk[intftype] == NULL) { + CDBG("%s: ispif NULL clk\n", __func__); + return; + } + rc = clk_set_rate(ispif->ispif_clk[intftype], csid); + if (rc < 0) + pr_err("%s: clk_set_rate failed %d\n", __func__, rc); + return; + } + data = msm_camera_io_r(ispif->base + ISPIF_INPUT_SEL_ADDR + + (0x200 * vfe_intf)); + switch (intftype) { + case PIX0: + data &= ~(0x3); /* clear old setting */ + data |= csid; /* add new setting */ + break; + case RDI0: + data &= ~(0x3 << 4); /* clear old setting */ + data |= (csid << 4); /* add new setting */ + break; + case PIX1: + data &= ~(0x3 << 8); /* clear old setting */ + data |= (csid << 8); /* add new setting */ + break; + case RDI1: + data &= ~(0x3 << 12); /* clear old setting */ + data |= (csid << 12); /* add new setting */ + break; + case RDI2: + data &= ~(0x3 << 20); /* clear old setting */ + data |= (csid << 20); /* add new setting */ + break; + } + if (data) { + msm_camera_io_w_mb(data, ispif->base + ISPIF_INPUT_SEL_ADDR + + (0x200 * vfe_intf)); + } +} + +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 data = 0; + + switch (intftype) { + case PIX0: + data = msm_camera_io_r(ispif->base + + ISPIF_PIX_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + if (enable) + data |= cid_mask; /* add new config */ + else + data &= ~cid_mask; /* remove CID bit */ + msm_camera_io_w_mb(data, ispif->base + + ISPIF_PIX_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + break; + case RDI0: + data = msm_camera_io_r(ispif->base + + ISPIF_RDI_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + if (enable) + data |= cid_mask; /* add new config */ + else + data &= ~cid_mask; /* remove CID bit */ + msm_camera_io_w_mb(data, ispif->base + + ISPIF_RDI_0_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + break; + case PIX1: + data = msm_camera_io_r(ispif->base + + ISPIF_PIX_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + if (enable) + data |= cid_mask; /* add new config */ + else + data &= ~cid_mask; /* remove CID bit */ + msm_camera_io_w_mb(data, ispif->base + + ISPIF_PIX_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + break; + case RDI1: + data = msm_camera_io_r(ispif->base + + ISPIF_RDI_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + if (enable) + data |= cid_mask; /* add new config */ + else + data &= ~cid_mask; /* remove CID bit */ + msm_camera_io_w_mb(data, ispif->base + + ISPIF_RDI_1_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + break; + case RDI2: + data = msm_camera_io_r(ispif->base + + ISPIF_RDI_2_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + if (enable) + data |= cid_mask; /* add new config */ + else + data &= ~cid_mask; /* remove CID bit */ + msm_camera_io_w_mb(data, ispif->base + + ISPIF_RDI_2_INTF_CID_MASK_ADDR + (0x200 * vfe_intf)); + break; + } +} + +static int32_t msm_ispif_validate_intf_status(struct ispif_device *ispif, + uint8_t intftype, uint8_t vfe_intf) +{ + int32_t rc = 0; + uint32_t data = 0; + switch (intftype) { + case PIX0: + data = msm_camera_io_r(ispif->base + + ISPIF_PIX_0_STATUS_ADDR + (0x200 * vfe_intf)); + break; + case RDI0: + data = msm_camera_io_r(ispif->base + + ISPIF_RDI_0_STATUS_ADDR + (0x200 * vfe_intf)); + break; + case PIX1: + data = msm_camera_io_r(ispif->base + + ISPIF_PIX_1_STATUS_ADDR + (0x200 * vfe_intf)); + break; + case RDI1: + data = msm_camera_io_r(ispif->base + + ISPIF_RDI_1_STATUS_ADDR + (0x200 * vfe_intf)); + break; + case RDI2: + data = msm_camera_io_r(ispif->base + + ISPIF_RDI_2_STATUS_ADDR + (0x200 * vfe_intf)); + break; + } + if ((data & 0xf) != 0xf) + rc = -EBUSY; + return rc; +} + +static uint16_t msm_ispif_get_cids_mask_from_cfg( + struct msm_ispif_params_entry *entry) +{ + int i; + uint16_t cids_mask = 0; + + for (i = 0; i < entry->num_cids; 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; + enum msm_ispif_intftype intftype; + enum msm_ispif_vfe_intf vfe_intf = params->vfe_intf; + uint16_t cid_mask; + + msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_ADDR); + msm_camera_io_w(0x00000000, ispif->base + ISPIF_IRQ_MASK_1_ADDR); + msm_camera_io_w_mb(0x00000000, ispif->base + ISPIF_IRQ_MASK_2_ADDR); + for (i = 0; i < params->num; i++) { + intftype = params->entries[i].intftype; + vfe_intf = params->vfe_intf; + CDBG("%s intftype %x, vfe_intf %d, csid %d\n", __func__, + intftype, vfe_intf, params->entries[i].csid); + if ((intftype >= INTF_MAX) || + (ispif->csid_version <= CSID_VERSION_V2 && + vfe_intf > VFE0) || + (ispif->csid_version == CSID_VERSION_V3 && + vfe_intf >= VFE_MAX)) { + pr_err("%s: VFEID %d and CSID version %d mismatch\n", + __func__, vfe_intf, ispif->csid_version); + return -EINVAL; + } + rc = msm_ispif_validate_intf_status(ispif, intftype, vfe_intf); + if (rc < 0) { + 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); + } + + msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base + + ISPIF_IRQ_MASK_ADDR); + + msm_camera_io_w(ISPIF_IRQ_STATUS_MASK, ispif->base + + ISPIF_IRQ_CLEAR_ADDR); + + msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base + + ISPIF_IRQ_MASK_1_ADDR); + + msm_camera_io_w(ISPIF_IRQ_STATUS_1_MASK, ispif->base + + ISPIF_IRQ_CLEAR_1_ADDR); + + msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base + + ISPIF_IRQ_MASK_2_ADDR); + + msm_camera_io_w(ISPIF_IRQ_STATUS_2_MASK, ispif->base + + ISPIF_IRQ_CLEAR_2_ADDR); + + msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base + + ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR); + return rc; +} + +static void msm_ispif_intf_cmd(struct ispif_device *ispif, + uint32_t cmd_bits, + struct msm_ispif_param_data *params) +{ + uint8_t vc = 0; + int i, k; + enum msm_ispif_intftype intf_type; + enum msm_ispif_cid cid; + enum msm_ispif_vfe_intf vfe_intf = params->vfe_intf; + + for (i = 0; i < params->num; i++) { + intf_type = params->entries[i].intftype; + 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)); + ispif->applied_intf_cmd[vfe_intf].intf_cmd1 |= + (cmd_bits << (vc * 2 + 8)); /* set cmd bits */ + } else { + /* zero 2 bits */ + ispif->applied_intf_cmd[vfe_intf].intf_cmd &= + ~(0x3 << (vc * 2 + vfe_intf * 8)); + /* set cmd bits */ + ispif->applied_intf_cmd[vfe_intf].intf_cmd |= + (cmd_bits << (vc * 2 + vfe_intf * 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_INTF_CMD_ADDR + + (0x200 * 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_INTF_CMD_1_ADDR + + (0x200 * vfe_intf)); +} + +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; + + 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->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; + + rc = msm_ispif_intf_reset(ispif, params); + msm_ispif_intf_cmd(ispif, ISPIF_INTF_CMD_ENABLE_FRAME_BOUNDARY, params); + return rc; +} + +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; + + 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]); + switch (params->entries[i].intftype) { + case PIX0: + while ((msm_camera_io_r(ispif->base + + ISPIF_PIX_0_STATUS_ADDR + + (0x200 * params->vfe_intf)) & 0xf) != 0xf) { + CDBG("Wait for pix0 Idle\n"); + } + break; + case RDI0: + while ((msm_camera_io_r(ispif->base + + ISPIF_RDI_0_STATUS_ADDR + + (0x200 * params->vfe_intf)) & 0xf) != 0xf) { + CDBG("Wait for rdi0 Idle\n"); + } + break; + case PIX1: + while ((msm_camera_io_r(ispif->base + + ISPIF_PIX_1_STATUS_ADDR + + (0x200 * params->vfe_intf)) & 0xf) != 0xf) { + CDBG("Wait for pix1 Idle\n"); + } + break; + case RDI1: + while ((msm_camera_io_r(ispif->base + + ISPIF_RDI_1_STATUS_ADDR + + (0x200 * params->vfe_intf)) & 0xf) != 0xf) { + CDBG("Wait for rdi1 Idle\n"); + } + break; + case RDI2: + while ((msm_camera_io_r(ispif->base + + ISPIF_RDI_2_STATUS_ADDR + + (0x200 * params->vfe_intf)) & 0xf) != 0xf) { + CDBG("Wait for rdi2 Idle\n"); + } + break; + default: + break; + } + /* disable CIDs in CID_MASK register */ + msm_ispif_enable_intf_cids(ispif, params->entries[i].intftype, + cid_mask, params->vfe_intf, 0); + } + return rc; +} + +static void ispif_process_irq(struct ispif_device *ispif, + struct ispif_irq_status *out, enum msm_ispif_vfe_intf vfe_id) +{ + if (out[vfe_id].ispifIrqStatus0 & + ISPIF_IRQ_STATUS_PIX_SOF_MASK) { + ispif->sof_count[vfe_id].sof_cnt[PIX0]++; + } + 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]++; + } + return; +} + +static inline void msm_ispif_read_irq_status(struct ispif_irq_status *out, + void *data) +{ + struct ispif_device *ispif = (struct ispif_device *)data; + + out[VFE0].ispifIrqStatus0 = msm_camera_io_r(ispif->base + + ISPIF_IRQ_STATUS_ADDR); + out[VFE0].ispifIrqStatus1 = msm_camera_io_r(ispif->base + + ISPIF_IRQ_STATUS_1_ADDR); + out[VFE0].ispifIrqStatus2 = msm_camera_io_r(ispif->base + + ISPIF_IRQ_STATUS_2_ADDR); + msm_camera_io_w(out[VFE0].ispifIrqStatus0, + ispif->base + ISPIF_IRQ_CLEAR_ADDR); + msm_camera_io_w(out[VFE0].ispifIrqStatus1, + ispif->base + ISPIF_IRQ_CLEAR_1_ADDR); + msm_camera_io_w_mb(out[VFE0].ispifIrqStatus2, + ispif->base + ISPIF_IRQ_CLEAR_2_ADDR); + + if (out[VFE0].ispifIrqStatus0 & ISPIF_IRQ_STATUS_MASK) { + if (out[VFE0].ispifIrqStatus0 & (0x1 << + RESET_DONE_IRQ)) + complete(&ispif->reset_complete); + if (out[VFE0].ispifIrqStatus0 & (0x1 << + PIX_INTF_0_OVERFLOW_IRQ)) + pr_err("%s: VFE0 pix0 overflow.\n", __func__); + if (out[VFE0].ispifIrqStatus0 & (0x1 << + RAW_INTF_0_OVERFLOW_IRQ)) + pr_err("%s: VFE0 rdi0 overflow.\n", __func__); + if (out[VFE0].ispifIrqStatus1 & (0x1 << + RAW_INTF_1_OVERFLOW_IRQ)) + pr_err("%s: VFE0 rdi1 overflow.\n", __func__); + if (out[VFE0].ispifIrqStatus2 & (0x1 << + RAW_INTF_2_OVERFLOW_IRQ)) + pr_err("%s: VFE0 rdi2 overflow.\n", __func__); + if ((out[VFE0].ispifIrqStatus0 & + ISPIF_IRQ_STATUS_SOF_MASK) || + (out[VFE0].ispifIrqStatus1 & + ISPIF_IRQ_STATUS_SOF_MASK) || + (out[VFE0].ispifIrqStatus2 & + ISPIF_IRQ_STATUS_RDI2_SOF_MASK)) + ispif_process_irq(ispif, out, VFE0); + } + if (ispif->csid_version == CSID_VERSION_V3) { + out[VFE1].ispifIrqStatus0 = msm_camera_io_r(ispif->base + + ISPIF_IRQ_STATUS_ADDR + 0x200); + msm_camera_io_w(out[VFE1].ispifIrqStatus0, + ispif->base + ISPIF_IRQ_CLEAR_ADDR + 0x200); + out[VFE1].ispifIrqStatus1 = msm_camera_io_r(ispif->base + + ISPIF_IRQ_STATUS_1_ADDR + 0x200); + msm_camera_io_w(out[VFE1].ispifIrqStatus1, + ispif->base + ISPIF_IRQ_CLEAR_1_ADDR + 0x200); + out[VFE1].ispifIrqStatus2 = msm_camera_io_r(ispif->base + + ISPIF_IRQ_STATUS_2_ADDR + 0x200); + msm_camera_io_w_mb(out[VFE1].ispifIrqStatus2, + ispif->base + ISPIF_IRQ_CLEAR_2_ADDR + 0x200); + if (out[VFE1].ispifIrqStatus0 & (0x1 << + PIX_INTF_0_OVERFLOW_IRQ)) + pr_err("%s: VFE1 pix0 overflow.\n", __func__); + if (out[VFE1].ispifIrqStatus0 & (0x1 << + RAW_INTF_0_OVERFLOW_IRQ)) + pr_err("%s: VFE1 rdi0 overflow.\n", __func__); + if (out[VFE1].ispifIrqStatus1 & (0x1 << + RAW_INTF_1_OVERFLOW_IRQ)) + pr_err("%s: VFE1 rdi1 overflow.\n", __func__); + if (out[VFE1].ispifIrqStatus2 & (0x1 << + RAW_INTF_2_OVERFLOW_IRQ)) + pr_err("%s: VFE1 rdi2 overflow.\n", __func__); + if ((out[VFE1].ispifIrqStatus0 & ISPIF_IRQ_STATUS_SOF_MASK) || + (out[VFE1].ispifIrqStatus1 & + ISPIF_IRQ_STATUS_SOF_MASK) || + (out[VFE1].ispifIrqStatus2 & + ISPIF_IRQ_STATUS_RDI2_SOF_MASK)) + ispif_process_irq(ispif, out, VFE1); + } + msm_camera_io_w_mb(ISPIF_IRQ_GLOBAL_CLEAR_CMD, ispif->base + + ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR); +} + +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 struct msm_cam_clk_info ispif_8960_clk_info[] = { + {"csi_pix_clk", 0}, + {"csi_rdi_clk", 0}, + {"csi_pix1_clk", 0}, + {"csi_rdi1_clk", 0}, + {"csi_rdi2_clk", 0}, +}; +static struct msm_cam_clk_info ispif_8974_clk_info[] = { + {"camss_vfe_vfe_clk", -1}, + {"camss_csi_vfe_clk", -1}, + {"camss_vfe_vfe_clk1", -1}, + {"camss_csi_vfe_clk1", -1}, +}; + +static int msm_ispif_init(struct ispif_device *ispif, + uint32_t csid_version) +{ + int rc = 0; + + if (ispif->ispif_state == ISPIF_POWER_UP) { + CDBG("%s: ispif already initted state = %d\n", __func__, + ispif->ispif_state); + rc = -EAGAIN; + 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_V2) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info, + ispif->ispif_clk, 2, 1); + if (rc < 0) { + pr_err("%s: cannot enable clock, error = %d\n", + __func__, rc); + goto end; + } + } else if (ispif->csid_version == CSID_VERSION_V2) { + rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info, + ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 1); + if (rc < 0) { + pr_err("%s: cannot enable clock, error = %d\n", + __func__, rc); + goto end; + } + } else { + rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_clk_info, + ispif->ispif_clk, ARRAY_SIZE(ispif_8974_clk_info), 1); + if (rc < 0) { + pr_err("%s: cannot enable clock, error = %d\n", + __func__, rc); + goto end; + } + } + ispif->base = ioremap(ispif->mem->start, + resource_size(ispif->mem)); + if (!ispif->base) { + rc = -ENOMEM; + pr_err("%s: nomem\n", __func__); + goto error_clk; + } + rc = request_irq(ispif->irq->start, msm_io_ispif_irq, + IRQF_TRIGGER_RISING, "ispif", ispif); + init_completion(&ispif->reset_complete); + if (rc < 0) { + pr_err("%s: request_irq error = %d\n", __func__, rc); + goto error_irq; + } + rc = msm_ispif_reset(ispif); + if (rc == 0) { + ispif->ispif_state = ISPIF_POWER_UP; + CDBG("%s: power up done\n", __func__); + goto end; + } + free_irq(ispif->irq->start, ispif); +error_irq: + iounmap(ispif->base); +error_clk: + if (ispif->csid_version < CSID_VERSION_V2) { + msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info, + ispif->ispif_clk, 2, 0); + } else if (ispif->csid_version == CSID_VERSION_V2) { + msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info, + ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 0); + } +end: + return rc; +} + +static void msm_ispif_release(struct ispif_device *ispif) +{ + 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); + free_irq(ispif->irq->start, ispif); + iounmap(ispif->base); + if (ispif->csid_version < CSID_VERSION_V2) { + msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info, + ispif->ispif_clk, 2, 0); + } else if (ispif->csid_version == CSID_VERSION_V2) { + msm_cam_clk_enable(&ispif->pdev->dev, ispif_8960_clk_info, + ispif->ispif_clk, ARRAY_SIZE(ispif_8960_clk_info), 0); + } + ispif->ispif_state = ISPIF_POWER_DOWN; +} + +static int msm_ispif_clk_enable(struct ispif_device *ispif, + uint32_t csid_version, int enable) +{ + int rc = 0; + + if (csid_version != CSID_VERSION_V3) + goto end; + rc = msm_cam_clk_enable(&ispif->pdev->dev, ispif_8974_clk_info, + ispif->ispif_clk, ARRAY_SIZE(ispif_8974_clk_info), enable); + if (rc < 0) + pr_err("%s: cannot enable clock, error = %d\n", __func__, rc); +end: + return rc; +} + +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); + mutex_lock(&ispif->mutex); + switch (pcdata->cfg_type) { + case ISPIF_CLK_ENABLE: + rc = msm_ispif_clk_enable(ispif, pcdata->csid_version, 1); + break; + case ISPIF_CLK_DISABLE: + rc = msm_ispif_clk_enable(ispif, pcdata->csid_version, 0); + break; + case ISPIF_ENABLE_REG_DUMP: + ispif->enb_dump_reg = pcdata->reg_dump; /* save dump config */ + break; + case ISPIF_INIT: + /* need to move back to CDBG */ + 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_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; + default: + break; + } + mutex_unlock(&ispif->mutex); + return rc; +} + +static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case VIDIOC_MSM_ISPIF_CFG: + return msm_ispif_cmd(sd, arg); + default: + pr_err("%s: invalid cmd received\n", __func__); + return -ENOIOCTLCMD; + } +} + +static int ispif_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ispif_device *ispif = v4l2_get_subdevdata(sd); + int rc = 0; + + mutex_lock(&ispif->mutex); + if (ispif->open_cnt > 0) { + CDBG("%s: dev already open\n", __func__); + goto end; + } + /* mem remap is done in init when the clock is on */ + ispif->open_cnt++; +end: + mutex_unlock(&ispif->mutex); + return rc; +} + +static int ispif_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ispif_device *ispif = v4l2_get_subdevdata(sd); + int rc = 0; + + CDBG("%s\n", __func__); + mutex_lock(&ispif->mutex); + if (ispif->open_cnt == 0) { + pr_err("Invalid close\n"); + 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 __devinit ispif_probe(struct platform_device *pdev) +{ + int rc = 0; + struct ispif_device *ispif; + + CDBG("%s\n", __func__); + ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL); + if (!ispif) { + pr_err("%s: no enough memory\n", __func__); + return -ENOMEM; + } + + 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"); + v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif); + platform_set_drvdata(pdev, &ispif->msm_sd.sd); + mutex_init(&ispif->mutex); + ispif->pdev = pdev; + media_entity_init(&ispif->msm_sd.sd.entity, 0, NULL, 0); + ispif->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + ispif->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ISPIF; + ispif->msm_sd.sd.entity.name = pdev->name; + rc = msm_sd_register(&ispif->msm_sd); + if (rc != 0) { + pr_err("%s: msm_sd_register error = %d\n", __func__, rc); + goto error; + } + if (pdev->dev.of_node) + of_property_read_u32((&pdev->dev)->of_node, + "cell-index", &pdev->id); + + 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->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/video/msmb/ispif/msm_ispif.h b/drivers/media/video/msmb/ispif/msm_ispif.h new file mode 100644 index 00000000000..c4418c131fe --- /dev/null +++ b/drivers/media/video/msmb/ispif/msm_ispif.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2013, 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" + +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 *irq; + struct resource *io; + void __iomem *base; + struct mutex mutex; + uint8_t start_ack_pending; + struct completion reset_complete; + 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 clk *ispif_clk[INTF_MAX]; +}; +#endif diff --git a/drivers/media/video/msmb/ispif/msm_ispif_hwreg.h b/drivers/media/video/msmb/ispif/msm_ispif_hwreg.h new file mode 100644 index 00000000000..16575aed054 --- /dev/null +++ b/drivers/media/video/msmb/ispif/msm_ispif_hwreg.h @@ -0,0 +1,102 @@ +/* Copyright (c) 2013, 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_HWREG_H +#define MSM_ISPIF_HWREG_H + + +/* ISPIF registers */ + +#define ISPIF_RST_CMD_ADDR 0x08 +#define ISPIF_RST_CMD_1_ADDR 0x0C +#define ISPIF_INTF_CMD_ADDR 0x248 +#define ISPIF_INTF_CMD_1_ADDR 0x24C +#define ISPIF_CTRL_ADDR 0x08 +#define ISPIF_INPUT_SEL_ADDR 0x244 +#define ISPIF_PIX_0_INTF_CID_MASK_ADDR 0x254 +#define ISPIF_RDI_0_INTF_CID_MASK_ADDR 0x264 +#define ISPIF_PIX_1_INTF_CID_MASK_ADDR 0x258 +#define ISPIF_RDI_1_INTF_CID_MASK_ADDR 0x268 +#define ISPIF_RDI_2_INTF_CID_MASK_ADDR 0x26C +#define ISPIF_PIX_0_STATUS_ADDR 0x2C0 +#define ISPIF_RDI_0_STATUS_ADDR 0x2D0 +#define ISPIF_PIX_1_STATUS_ADDR 0x2C4 +#define ISPIF_RDI_1_STATUS_ADDR 0x2D4 +#define ISPIF_RDI_2_STATUS_ADDR 0x2D8 +#define ISPIF_IRQ_MASK_ADDR 0x208 +#define ISPIF_IRQ_CLEAR_ADDR 0x230 +#define ISPIF_IRQ_STATUS_ADDR 0x21C +#define ISPIF_IRQ_MASK_1_ADDR 0x20C +#define ISPIF_IRQ_CLEAR_1_ADDR 0x234 +#define ISPIF_IRQ_STATUS_1_ADDR 0x220 +#define ISPIF_IRQ_MASK_2_ADDR 0x210 +#define ISPIF_IRQ_CLEAR_2_ADDR 0x238 +#define ISPIF_IRQ_STATUS_2_ADDR 0x224 +#define ISPIF_IRQ_GLOBAL_CLEAR_CMD_ADDR 0x1C + +/* new */ +#define ISPIF_VFE_m_CTRL_0_ADDR 0x200 +#define ISPIF_VFE_m_IRQ_MASK_0 0x208 +#define ISPIF_VFE_m_IRQ_MASK_1 0x20C +#define ISPIF_VFE_m_IRQ_MASK_2 0x210 +#define ISPIF_VFE_m_IRQ_STATUS_0 0x21C +#define ISPIF_VFE_m_IRQ_STATUS_1 0x220 +#define ISPIF_VFE_m_IRQ_STATUS_2 0x224 +#define ISPIF_VFE_m_IRQ_CLEAR_0 0x230 +#define ISPIF_VFE_m_IRQ_CLEAR_1 0x234 +#define ISPIF_VFE_m_IRQ_CLEAR_2 0x238 + +/*ISPIF RESET BITS*/ + +#define VFE_CLK_DOMAIN_RST 31 +#define RDI_CLK_DOMAIN_RST 26 +#define RDI_1_CLK_DOMAIN_RST 27 +#define RDI_2_CLK_DOMAIN_RST 28 +#define PIX_CLK_DOMAIN_RST 29 +#define PIX_1_CLK_DOMAIN_RST 30 +#define AHB_CLK_DOMAIN_RST 25 +#define RDI_2_VFE_RST_STB 12 +#define RDI_2_CSID_RST_STB 11 +#define RDI_1_VFE_RST_STB 10 +#define RDI_1_CSID_RST_STB 9 +#define RDI_0_VFE_RST_STB 8 +#define RDI_0_CSID_RST_STB 7 +#define PIX_1_VFE_RST_STB 6 +#define PIX_1_CSID_RST_STB 5 +#define PIX_0_VFE_RST_STB 4 +#define PIX_0_CSID_RST_STB 3 +#define SW_REG_RST_STB 2 +#define MISC_LOGIC_RST_STB 1 +#define STROBED_RST_EN 0 + +#define ISPIF_RST_CMD_MASK 0xFE0F1FFF +#define ISPIF_RST_CMD_1_MASK 0xFC0F1FF9 + +#define PIX_INTF_0_OVERFLOW_IRQ 12 +#define RAW_INTF_0_OVERFLOW_IRQ 25 +#define RAW_INTF_1_OVERFLOW_IRQ 25 +#define RAW_INTF_2_OVERFLOW_IRQ 12 +#define RESET_DONE_IRQ 27 + +#define ISPIF_IRQ_STATUS_MASK 0x0A493249 +#define ISPIF_IRQ_STATUS_1_MASK 0x02493249 +#define ISPIF_IRQ_STATUS_2_MASK 0x00001249 + +#define ISPIF_IRQ_STATUS_PIX_SOF_MASK 0x249 +#define ISPIF_IRQ_STATUS_RDI0_SOF_MASK 0x492000 +#define ISPIF_IRQ_STATUS_RDI1_SOF_MASK 0x492000 +#define ISPIF_IRQ_STATUS_RDI2_SOF_MASK 0x249 + +#define ISPIF_IRQ_STATUS_SOF_MASK 0x492249 +#define ISPIF_IRQ_GLOBAL_CLEAR_CMD 0x1 + +#endif diff --git a/include/media/Kbuild b/include/media/Kbuild index 2b91f88af4e..685e3f15ce1 100644 --- a/include/media/Kbuild +++ b/include/media/Kbuild @@ -9,3 +9,5 @@ header-y += msm_mercury.h header-y += msm_media_info.h header-y += msmb_camera.h header-y += msm_cam_sensor.h +header-y += msmb_isp.h +header-y += msmb_ispif.h diff --git a/include/media/msmb_isp.h b/include/media/msmb_isp.h new file mode 100644 index 00000000000..01276bd25e0 --- /dev/null +++ b/include/media/msmb_isp.h @@ -0,0 +1,320 @@ +#ifndef __MSMB_ISP__ +#define __MSMB_ISP__ + +#include + +#define MAX_PLANES_PER_STREAM 3 +#define MAX_NUM_STREAM 7 + +#define ISP_VERSION_40 40 +#define ISP_VERSION_32 32 + + +enum ISP_START_PIXEL_PATTERN { + ISP_BAYER_RGRGRG, + ISP_BAYER_GRGRGR, + ISP_BAYER_BGBGBG, + ISP_BAYER_GBGBGB, + ISP_YUV_YCbYCr, + ISP_YUV_YCrYCb, + ISP_YUV_CbYCrY, + ISP_YUV_CrYCbY, + ISP_PIX_PATTERN_MAX +}; + +enum msm_vfe_plane_fmt { + Y_PLANE, + CB_PLANE, + CR_PLANE, + CRCB_PLANE, + CBCR_PLANE, + VFE_PLANE_FMT_MAX +}; + +enum msm_vfe_input_src { + VFE_PIX_0, + VFE_RAW_0, + VFE_RAW_1, + VFE_RAW_2, + VFE_SRC_MAX, +}; + +enum msm_vfe_axi_stream_src { + PIX_ENCODER, + PIX_VIEWFINDER, + CAMIF_RAW, + IDEAL_RAW, + RDI, + VFE_AXI_SRC_MAX +}; + +enum msm_vfe_frame_skip_pattern { + NO_SKIP, + EVERY_2FRAME, + EVERY_4FRAME, + EVERY_8FRAME, + EVERY_16FRAME, + EVERY_32FRAME, + MAX_SKIP, +}; + +enum msm_vfe_camif_input { + CAMIF_DISABLED, + CAMIF_PAD_REG_INPUT, + CAMIF_MIDDI_INPUT, + CAMIF_MIPI_INPUT, +}; + +struct msm_vfe_camif_cfg { + uint32_t lines_per_frame; + uint32_t pixels_per_line; + uint32_t first_pixel; + uint32_t last_pixel; + uint32_t first_line; + uint32_t last_line; + uint32_t epoch_line0; + uint32_t epoch_line1; + enum msm_vfe_camif_input camif_input; +}; + +enum msm_vfe_inputmux { + CAMIF, + TESTGEN, + EXTERNAL_READ, +}; + +struct msm_vfe_pix_cfg { + struct msm_vfe_camif_cfg camif_cfg; + enum msm_vfe_inputmux input_mux; + enum ISP_START_PIXEL_PATTERN pixel_pattern; +}; + +struct msm_vfe_input_cfg { + union { + struct msm_vfe_pix_cfg pix_cfg; + } d; + enum msm_vfe_input_src input_src; + +}; + +struct msm_vfe_axi_plane_cfg { + uint32_t output_width; /*Include padding*/ + uint32_t output_height; + uint32_t output_stride; + uint32_t output_scan_lines; + uint32_t output_plane_format; /*Y/Cb/Cr/CbCr*/ + + uint8_t csid_src; /*RDI 0-2*/ + uint8_t rdi_cid;/*CID 1-16*/ +}; + +struct msm_vfe_axi_stream_request_cmd { + uint32_t session_id; + uint32_t stream_id; + 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; +}; + +struct msm_vfe_axi_stream_release_cmd { + uint32_t stream_handle; +}; + +enum msm_vfe_axi_stream_cmd { + STOP_STREAM, + START_STREAM, +}; + +struct msm_vfe_axi_stream_cfg_cmd { + uint8_t num_streams; + uint32_t stream_handle[MAX_NUM_STREAM]; + enum msm_vfe_axi_stream_cmd cmd; +}; + +enum msm_isp_stats_type { + MSM_ISP_STATS_AEC, /* legacy based AEC */ + MSM_ISP_STATS_AF, /* legacy based AF */ + MSM_ISP_STATS_AWB, /* legacy based AWB */ + MSM_ISP_STATS_RS, /* legacy based RS */ + MSM_ISP_STATS_CS, /* legacy based CS */ + MSM_ISP_STATS_IHIST, /* legacy based HIST */ + MSM_ISP_STATS_SKIN, /* legacy based SKIN */ + MSM_ISP_STATS_BG, /* Bayer Grids */ + MSM_ISP_STATS_BF, /* Bayer Focus */ + MSM_ISP_STATS_BE, /* Bayer Exposure*/ + MSM_ISP_STATS_BHIST, /* Bayer Hist */ + MSM_ISP_STATS_MAX /* MAX */ +}; + +struct msm_vfe_stats_stream_request_cmd { + uint32_t session_id; + uint32_t stream_id; + enum msm_isp_stats_type stats_type; + uint32_t stream_handle; +}; +struct msm_vfe_stats_stream_release_cmd { + uint32_t stream_handle; +}; +struct msm_vfe_stats_stream_cfg_cmd { + uint8_t num_streams; + uint32_t stream_handle[MSM_ISP_STATS_MAX]; + uint8_t enable; +}; +enum msm_vfe_reg_cfg_type { + VFE_WRITE, + VFE_WRITE_MB, + VFE_READ, + VFE_WRITE_MASK, + VFE_CLEAR_MASK, + VFE_WRITE_AUTO_INCREMENT, +}; + +struct msm_vfe_cfg_cmd2 { + uint16_t num_cfg; + uint16_t cmd_len; + void __user *cfg_data; + void __user *cfg_cmd; +}; + +struct msm_vfe_reg_cfg_cmd { + uint32_t reg_offset; + uint32_t cmd_data; + uint32_t len; + enum msm_vfe_reg_cfg_type cmd_type; +}; + +struct msm_isp_buf_request { + uint32_t session_id; + uint32_t stream_id; + uint8_t num_buf; + uint32_t handle; +}; + +struct msm_isp_qbuf_info { + uint32_t handle; + int buf_idx; + /*Only used for prepare buffer*/ + struct v4l2_buffer buffer; +}; + +struct msm_vfe_axi_src_state { + enum msm_vfe_input_src input_src; + uint32_t src_active; +}; + +enum msm_isp_event_idx { + ISP_REG_UPDATE = 0, + ISP_START_ACK = 1, + ISP_STOP_ACK = 2, + ISP_IRQ_VIOLATION = 3, + ISP_WM_BUS_OVERFLOW = 4, + ISP_STATS_OVERFLOW = 5, + ISP_CAMIF_ERROR = 6, + ISP_STATS_NOTIFY = 7, + ISP_SOF = 8, + ISP_EOF = 9, + ISP_BUF_DIVERT = 10, + ISP_EVENT_MAX = 11 +}; + +#define ISP_EVENT_BASE (V4L2_EVENT_PRIVATE_START + 1) +#define ISP_EVENT_REG_UPDATE (ISP_EVENT_BASE + ISP_REG_UPDATE) +#define ISP_EVENT_START_ACK (ISP_EVENT_BASE + ISP_START_ACK) +#define ISP_EVENT_STOP_ACK (ISP_EVENT_BASE + ISP_STOP_ACK) +#define ISP_EVENT_IRQ_VIOLATION (ISP_EVENT_BASE + ISP_IRQ_VIOLATION) +#define ISP_EVENT_WM_BUS_OVERFLOW (ISP_EVENT_BASE + ISP_WM_BUS_OVERFLOW) +#define ISP_EVENT_STATS_OVERFLOW (ISP_EVENT_BASE + ISP_STATS_OVERFLOW) +#define ISP_EVENT_CAMIF_ERROR (ISP_EVENT_BASE + ISP_CAMIF_ERROR) +#define ISP_EVENT_STATS_NOTIFY (ISP_EVENT_BASE + ISP_STATS_NOTIFY) +#define ISP_EVENT_SOF (ISP_EVENT_BASE + ISP_SOF) +#define ISP_EVENT_EOF (ISP_EVENT_BASE + ISP_EOF) +#define ISP_EVENT_BUF_DIVERT (ISP_EVENT_BASE + ISP_BUF_DIVERT) + + +/* The msm_v4l2_event_data structure should match the + * v4l2_event.u.data field. + * should not exceed 64 bytes */ + +struct msm_isp_buf_event { + uint32_t session_id; + uint32_t stream_id; + uint32_t handle; + int8_t buf_idx; +}; +struct msm_isp_stats_event { + uint32_t stats_mask; /* 4 bytes */ + uint8_t stats_buf_idxs[MSM_ISP_STATS_MAX]; /* 11 bytes */ +}; + +struct msm_isp_stream_ack { + uint32_t session_id; + uint32_t stream_id; + uint32_t handle; +}; + +struct msm_isp_event_data { + struct timeval timestamp; + /* if pix is a src frame_id is from camif */ + uint32_t frame_id; + union { + /* START_ACK, STOP_ACK */ + struct msm_isp_stream_ack stream_ack; + /* REG_UPDATE_TRIGGER, bus over flow */ + enum msm_vfe_input_src input_src; + /* stats notify */ + struct msm_isp_stats_event stats; + /* IRQ_VIOLATION, STATS_OVER_FLOW, WM_OVER_FLOW */ + uint32_t irq_status_mask; + struct msm_isp_buf_event buf_done; + } u; /* union can have max 52 bytes */ +}; + +#define VIDIOC_MSM_VFE_REG_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_vfe_cfg_cmd2) + +#define VIDIOC_MSM_ISP_REQUEST_BUF \ + _IOWR('V', BASE_VIDIOC_PRIVATE+1, struct msm_isp_buf_request) + +#define VIDIOC_MSM_ISP_ENQUEUE_BUF \ + _IOWR('V', BASE_VIDIOC_PRIVATE+2, struct msm_isp_qbuf_info) + +#define VIDIOC_MSM_ISP_RELEASE_BUF \ + _IOWR('V', BASE_VIDIOC_PRIVATE+3, struct msm_isp_buf_request) + +#define VIDIOC_MSM_ISP_REQUEST_STREAM \ + _IOWR('V', BASE_VIDIOC_PRIVATE+4, struct msm_vfe_axi_stream_request_cmd) + +#define VIDIOC_MSM_ISP_CFG_STREAM \ + _IOWR('V', BASE_VIDIOC_PRIVATE+5, struct msm_vfe_axi_stream_cfg_cmd) + +#define VIDIOC_MSM_ISP_RELEASE_STREAM \ + _IOWR('V', BASE_VIDIOC_PRIVATE+6, struct msm_vfe_axi_stream_release_cmd) + +#define VIDIOC_MSM_ISP_INPUT_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE+7, struct msm_vfe_input_cfg) + +#define VIDIOC_MSM_ISP_SET_SRC_STATE \ + _IOWR('V', BASE_VIDIOC_PRIVATE+8, struct msm_vfe_axi_src_state) + +#define VIDIOC_MSM_ISP_REQUEST_STATS_STREAM \ + _IOWR('V', BASE_VIDIOC_PRIVATE+9, \ + struct msm_vfe_stats_stream_request_cmd) + +#define VIDIOC_MSM_ISP_CFG_STATS_STREAM \ + _IOWR('V', BASE_VIDIOC_PRIVATE+10, struct msm_vfe_stats_stream_cfg_cmd) + +#define VIDIOC_MSM_ISP_RELEASE_STATS_STREAM \ + _IOWR('V', BASE_VIDIOC_PRIVATE+11, \ + struct msm_vfe_stats_stream_release_cmd) + +#endif /* __MSMB_ISP__ */ diff --git a/include/media/msmb_ispif.h b/include/media/msmb_ispif.h new file mode 100644 index 00000000000..fc27ef6c2b9 --- /dev/null +++ b/include/media/msmb_ispif.h @@ -0,0 +1,103 @@ +#ifndef MSM_CAM_ISPIF_H +#define MSM_CAM_ISPIF_H + +#define CSID_VERSION_V2 0x02000011 +#define CSID_VERSION_V3 0x30000000 + +enum msm_ispif_vfe_intf { + VFE0, + VFE1, + VFE_MAX +}; +#define VFE0_MASK (1 << VFE0) +#define VFE1_MASK (1 << VFE1) + +enum msm_ispif_intftype { + PIX0, + RDI0, + PIX1, + RDI1, + RDI2, + INTF_MAX +}; +#define PIX0_MASK (1 << PIX0) +#define PIX1_MASK (1 << PIX1) +#define RDI0_MASK (1 << RDI0) +#define RDI1_MASK (1 << RDI1) +#define RDI2_MASK (1 << RDI2) + + +enum msm_ispif_vc { + VC0, + VC1, + VC2, + VC3, + VC_MAX +}; + +enum msm_ispif_cid { + CID0, + CID1, + CID2, + CID3, + CID4, + CID5, + CID6, + CID7, + CID8, + CID9, + CID10, + CID11, + CID12, + CID13, + CID14, + CID15, + CID_MAX +}; + +enum msm_ispif_csid { + CSID0, + CSID1, + CSID2, + CSID3, + CSID_MAX +}; + +struct msm_ispif_params_entry { + enum msm_ispif_intftype intftype; + int num_cids; + enum msm_ispif_cid cids[3]; + enum msm_ispif_csid csid; +}; + +struct msm_ispif_param_data { + enum msm_ispif_vfe_intf vfe_intf; + uint32_t num; + struct msm_ispif_params_entry entries[INTF_MAX]; +}; + +enum ispif_cfg_type_t { + ISPIF_CLK_ENABLE, + ISPIF_CLK_DISABLE, + ISPIF_INIT, + ISPIF_CFG, + ISPIF_START_FRAME_BOUNDARY, + ISPIF_STOP_FRAME_BOUNDARY, + ISPIF_STOP_IMMEDIATELY, + ISPIF_RELEASE, + ISPIF_ENABLE_REG_DUMP, +}; + +struct ispif_cfg_data { + enum ispif_cfg_type_t cfg_type; + union { + int reg_dump; /* ISPIF_ENABLE_REG_DUMP */ + uint32_t csid_version; /* ISPIF_INIT */ + struct msm_ispif_param_data params; /* CFG, START, STOP */ + }; +}; + +#define VIDIOC_MSM_ISPIF_CFG \ + _IOWR('V', BASE_VIDIOC_PRIVATE, struct ispif_cfg_data) + +#endif /* MSM_CAM_ISPIF_H */