| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2021 MediaTek Inc. |
| * |
| * Author: Frederic Chen <frederic.chen@mediatek.com> |
| * Holmes Chiou <holmes.chiou@mediatek.com> |
| * |
| */ |
| |
| #include "linux/dma-direction.h" |
| #include "linux/dma-mapping.h" |
| #include "linux/gfp_types.h" |
| #include <linux/device.h> |
| #include <linux/freezer.h> |
| #include <linux/pm_runtime.h> |
| #include <linux/remoteproc.h> |
| #include <linux/slab.h> |
| #include <linux/spinlock.h> |
| #include <linux/wait.h> |
| #include <linux/hashtable.h> |
| #include "media/videobuf2-dma-contig.h" |
| #include <media/v4l2-event.h> |
| #include "mtk-hcp.h" |
| #include "mtk_header_desc.h" |
| #include "mtk_imgsys-dev.h" |
| #include "mtk_imgsys-hw.h" |
| #include "mtk_imgsys-vnode_id.h" |
| #include "mtk_imgsys-cmdq.h" |
| #include "mtk_imgsys-module.h" |
| #include "mtk_imgsys-debug.h" |
| |
| static int imgsys_quick_onoff_en; |
| module_param(imgsys_quick_onoff_en, int, 0644); |
| |
| static int imgsys_send(struct platform_device *pdev, enum hcp_id id, |
| void *buf, unsigned int len, int req_fd, |
| unsigned int wait) |
| { |
| int ret; |
| |
| if (wait) |
| ret = mtk_hcp_send(pdev, id, buf, len, req_fd); |
| else |
| ret = mtk_hcp_send_async(pdev, id, buf, len, req_fd); |
| |
| return ret; |
| } |
| |
| int mtk_imgsys_hw_working_buf_pool_init(struct mtk_imgsys_dev *imgsys_dev) |
| { |
| struct mtk_imgsys_hw_working_buf *buf; |
| void *sd; |
| dma_addr_t scp_addr, isp_daddr; |
| size_t size = sizeof(struct singlenode_desc_norm); |
| int i; |
| |
| if (imgsys_dev->working_bufs) |
| return 0; |
| |
| INIT_LIST_HEAD(&imgsys_dev->free_working_bufs.list); |
| spin_lock_init(&imgsys_dev->free_working_bufs.lock); |
| |
| imgsys_dev->working_bufs = kmalloc_array(IMGSYS_WORKING_BUF_NUM, sizeof(*buf), GFP_KERNEL); |
| if (!imgsys_dev->working_bufs) |
| return -ENOMEM; |
| |
| sd = dma_alloc_coherent(imgsys_dev->smem_dev, |
| size * IMGSYS_WORKING_BUF_NUM, |
| &scp_addr, GFP_KERNEL); |
| if (!sd) |
| goto err_alloc; |
| |
| isp_daddr = dma_map_resource(imgsys_dev->dev, |
| scp_addr, |
| size * IMGSYS_WORKING_BUF_NUM, |
| DMA_TO_DEVICE, 0); |
| if (!isp_daddr) |
| goto err_map; |
| |
| for (i = 0; i < IMGSYS_WORKING_BUF_NUM; i++) { |
| buf = &imgsys_dev->working_bufs[i]; |
| |
| buf->sd_norm = sd; |
| buf->isp_daddr = isp_daddr; |
| buf->scp_daddr = scp_addr; |
| |
| sd += size; |
| isp_daddr += size; |
| scp_addr += size; |
| |
| list_add_tail(&buf->list, |
| &imgsys_dev->free_working_bufs.list); |
| } |
| |
| return 0; |
| |
| err_map: |
| dma_free_coherent(imgsys_dev->dev, |
| sizeof(struct singlenode_desc_norm), |
| sd, scp_addr); |
| err_alloc: |
| kfree(imgsys_dev->working_bufs); |
| |
| return -ENOMEM; |
| } |
| |
| void mtk_imgsys_hw_working_buf_pool_release(struct mtk_imgsys_dev *imgsys_dev) |
| { |
| struct mtk_imgsys_hw_working_buf *buf = &imgsys_dev->working_bufs[0]; |
| |
| dma_unmap_resource(imgsys_dev->dev, buf->isp_daddr, |
| sizeof(struct singlenode_desc_norm) * IMGSYS_WORKING_BUF_NUM, |
| DMA_TO_DEVICE, 0); |
| dma_free_coherent(imgsys_dev->dev, |
| sizeof(struct singlenode_desc_norm) * IMGSYS_WORKING_BUF_NUM, |
| buf->sd_norm, buf->scp_daddr); |
| kfree(imgsys_dev->working_bufs); |
| } |
| |
| static void mtk_imgsys_hw_working_buf_free(struct mtk_imgsys_dev *imgsys_dev, |
| struct mtk_imgsys_hw_working_buf *working_buf) |
| { |
| if (!working_buf) |
| return; |
| |
| spin_lock(&imgsys_dev->free_working_bufs.lock); |
| list_add_tail(&working_buf->list, |
| &imgsys_dev->free_working_bufs.list); |
| spin_unlock(&imgsys_dev->free_working_bufs.lock); |
| } |
| |
| static struct mtk_imgsys_hw_working_buf* |
| mtk_imgsys_hw_working_buf_alloc(struct mtk_imgsys_dev *imgsys_dev) |
| { |
| struct mtk_imgsys_hw_working_buf *working_buf; |
| |
| spin_lock(&imgsys_dev->free_working_bufs.lock); |
| if (list_empty(&imgsys_dev->free_working_bufs.list)) { |
| spin_unlock(&imgsys_dev->free_working_bufs.lock); |
| return NULL; |
| } |
| |
| working_buf = list_first_entry(&imgsys_dev->free_working_bufs.list, |
| struct mtk_imgsys_hw_working_buf, |
| list); |
| list_del(&working_buf->list); |
| spin_unlock(&imgsys_dev->free_working_bufs.lock); |
| |
| return working_buf; |
| } |
| |
| static void mtk_imgsys_notify(struct mtk_imgsys_request *req, uint64_t frm_owner) |
| { |
| struct mtk_imgsys_dev *imgsys_dev = req->imgsys_pipe->imgsys_dev; |
| struct mtk_imgsys_pipe *pipe = req->imgsys_pipe; |
| struct req_frameparam *iparam = &req->img_fparam.frameparam; |
| enum vb2_buffer_state vbf_state; |
| |
| if (!mtk_imgsys_pipe_get_running_job(pipe, req->id)) |
| goto out; |
| |
| if (iparam->state != FRAME_STATE_HW_TIMEOUT) |
| vbf_state = VB2_BUF_STATE_DONE; |
| else |
| vbf_state = VB2_BUF_STATE_ERROR; |
| |
| atomic_dec(&imgsys_dev->num_composing); |
| |
| mtk_imgsys_hw_working_buf_free(imgsys_dev, req->working_buf); |
| req->working_buf = NULL; |
| mtk_imgsys_pipe_remove_job(req); |
| if (vbf_state == VB2_BUF_STATE_DONE) |
| mtk_imgsys_pipe_job_finish(req, vbf_state); |
| |
| wake_up(&imgsys_dev->flushing_waitq); |
| dev_dbg(imgsys_dev->dev, |
| "%s:%s:(reqfd-%d) job id(%d), frame_no(%d) finished\n", |
| __func__, pipe->desc->name, req->request_fd, iparam->index, |
| iparam->frame_no); |
| |
| out: |
| media_request_put(&req->req); |
| } |
| |
| static void cmdq_cb_done_worker(struct work_struct *work) |
| { |
| struct mtk_imgsys_pipe *pipe; |
| struct swfrm_info_t *gwfrm_info = NULL; |
| struct gce_cb_work *gwork; |
| struct img_sw_buffer swbuf_data = {0}; |
| |
| gwork = container_of(work, struct gce_cb_work, work); |
| gwfrm_info = gwork->req_sbuf_kva; |
| |
| pipe = gwork->pipe; |
| if (!pipe->streaming) |
| goto out; |
| |
| if (gwfrm_info->user_info[0].ndd_fp[0] != '\0') |
| wait_for_completion(gwfrm_info->ndd_user_complete); |
| |
| /* send to HCP after frame done & del node from list */ |
| swbuf_data.offset = gwfrm_info->req_sbuf_goft; |
| imgsys_send(pipe->imgsys_dev->scp_pdev, HCP_IMGSYS_DEQUE_DONE_ID, |
| &swbuf_data, sizeof(struct img_sw_buffer), |
| gwork->reqfd, 1); |
| |
| out: |
| if (gwfrm_info->user_info[0].ndd_fp[0] != '\0') |
| kfree(gwfrm_info->ndd_user_complete); |
| |
| kfree(gwork); |
| } |
| |
| static void imgsys_mdp_cb_func(struct imgsys_cb_data data, |
| unsigned int subfidx, bool last_task_in_req) |
| { |
| struct mtk_imgsys_pipe *pipe; |
| struct mtk_imgsys_request *req; |
| struct mtk_imgsys_dev *imgsys_dev; |
| struct swfrm_info_t *swfrminfo_cb; |
| struct gce_cb_work *gwork; |
| |
| swfrminfo_cb = data.data; |
| pipe = (struct mtk_imgsys_pipe *)swfrminfo_cb->pipe; |
| if (!pipe->streaming) { |
| pr_info("%s pipe already streamoff\n", __func__); |
| return; |
| } |
| |
| req = (struct mtk_imgsys_request *)(swfrminfo_cb->req); |
| if (!req) { |
| pr_info("%s NULL request Address\n", __func__); |
| return; |
| } |
| |
| imgsys_dev = req->imgsys_pipe->imgsys_dev; |
| dev_dbg(imgsys_dev->dev, "%s:(reqfd-%d)frame_no(%d) +", __func__, |
| req->request_fd, |
| req->img_fparam.frameparam.frame_no); |
| |
| if (swfrminfo_cb->hw_hang >= 0) |
| req->img_fparam.frameparam.state = FRAME_STATE_HW_TIMEOUT; |
| |
| mtk_imgsys_notify(req, swfrminfo_cb->frm_owner); |
| |
| if (swfrminfo_cb->hw_hang < 0) { |
| gwork = kzalloc(sizeof(*gwork), GFP_KERNEL); |
| if (!gwork) |
| return; |
| |
| gwork->reqfd = swfrminfo_cb->request_fd; |
| gwork->req_sbuf_kva = swfrminfo_cb->req_sbuf_kva; |
| gwork->pipe = swfrminfo_cb->pipe; |
| |
| INIT_WORK(&gwork->work, cmdq_cb_done_worker); |
| queue_work(req->imgsys_pipe->imgsys_dev->mdpcb_wq, &gwork->work); |
| } |
| } |
| |
| static void imgsys_runner_func(struct work_struct *work) |
| { |
| struct mtk_imgsys_request *req = mtk_imgsys_runner_work_to_req(work); |
| struct mtk_imgsys_dev *imgsys_dev = req->imgsys_pipe->imgsys_dev; |
| struct swfrm_info_t *frm_info; |
| int ret; |
| |
| frm_info = req->swfrm_info; |
| frm_info->is_sent = true; |
| |
| ret = imgsys_cmdq_sendtask(imgsys_dev, frm_info, imgsys_mdp_cb_func, NULL); |
| if (ret) |
| dev_info(imgsys_dev->dev, |
| "%s: imgsys_cmdq_sendtask fail(%d)\n", __func__, ret); |
| } |
| |
| static void imgsys_scp_handler(void *data, unsigned int len, void *priv) |
| { |
| int job_id; |
| struct mtk_imgsys_pipe *pipe; |
| int pipe_id; |
| struct mtk_imgsys_request *req; |
| struct mtk_imgsys_dev *imgsys_dev = (struct mtk_imgsys_dev *)priv; |
| struct img_sw_buffer *swbuf_data; |
| struct swfrm_info_t *swfrm_info; |
| int i = 0; |
| void *gce_virt; |
| int total_framenum = 0; |
| |
| if (!data) { |
| WARN_ONCE(!data, "%s: failed due to NULL data\n", __func__); |
| return; |
| } |
| |
| if (WARN_ONCE(len != sizeof(struct img_sw_buffer), |
| "%s: len(%d) not match img_sw_buffer\n", __func__, len)) |
| return; |
| |
| swbuf_data = (struct img_sw_buffer *)data; |
| gce_virt = mtk_hcp_get_gce_mem_virt(imgsys_dev->scp_pdev); |
| if (!gce_virt) { |
| pr_info("%s: invalid gce_virt(%p)\n", |
| __func__, gce_virt); |
| return; |
| } |
| |
| swfrm_info = (struct swfrm_info_t *)(gce_virt + (swbuf_data->offset)); |
| if ((int)swbuf_data->offset < 0 || |
| swbuf_data->offset > mtk_hcp_get_gce_mem_size(imgsys_dev->scp_pdev)) { |
| pr_info("%s: invalid swbuf_data->offset(%d), max(%llu)\n", |
| __func__, swbuf_data->offset, |
| (u64)mtk_hcp_get_gce_mem_size(imgsys_dev->scp_pdev)); |
| return; |
| } |
| |
| swfrm_info->req_sbuf_goft = swbuf_data->offset; |
| swfrm_info->req_sbuf_kva = gce_virt + (swbuf_data->offset); |
| |
| job_id = swfrm_info->handle; |
| pipe_id = mtk_imgsys_pipe_get_pipe_from_job_id(job_id); |
| pipe = mtk_imgsys_dev_get_pipe(imgsys_dev, pipe_id); |
| if (!pipe) { |
| dev_info(imgsys_dev->dev, |
| "%s: get invalid img_ipi_frameparam index(%d) from firmware\n", |
| __func__, job_id); |
| return; |
| } |
| |
| req = (struct mtk_imgsys_request *)swfrm_info->req_vaddr; |
| if (!req) { |
| WARN_ONCE(!req, "%s: frame_no(%d) is lost\n", __func__, job_id); |
| return; |
| } |
| |
| if (!req->working_buf) { |
| dev_dbg(imgsys_dev->dev, |
| "%s: (reqfd-%d) composing\n", |
| __func__, req->request_fd); |
| } |
| |
| swfrm_info->is_sent = false; |
| swfrm_info->req = (void *)req; |
| swfrm_info->pipe = (void *)pipe; |
| swfrm_info->cb_frmcnt = 0; |
| swfrm_info->total_taskcnt = 0; |
| swfrm_info->chan_id = 0; |
| swfrm_info->hw_hang = -1; |
| total_framenum = swfrm_info->total_frmnum; |
| |
| if (total_framenum < 0 || total_framenum > TIME_MAX) { |
| dev_info(imgsys_dev->dev, |
| "%s:unexpected total_framenum (%d -> %d), batchnum(%d) MAX (%d/%d)\n", |
| __func__, swfrm_info->total_frmnum, |
| total_framenum, |
| swfrm_info->batchnum, |
| TMAX, TIME_MAX); |
| return; |
| } |
| |
| for (i = 0 ; i < total_framenum ; i++) { |
| swfrm_info->user_info[i].g_swbuf = gce_virt + (swfrm_info->user_info[i].sw_goft); |
| swfrm_info->user_info[i].bw_swbuf = gce_virt + (swfrm_info->user_info[i].sw_bwoft); |
| } |
| |
| req->swfrm_info = swfrm_info; |
| |
| up(&imgsys_dev->sem); |
| |
| if (swfrm_info->user_info[0].ndd_fp[0] != '\0') { |
| swfrm_info->ndd_user_complete = |
| kzalloc(sizeof(*swfrm_info->ndd_user_complete), GFP_KERNEL); |
| init_completion(swfrm_info->ndd_user_complete); |
| imgsys_ndd_swfrm_list_add(imgsys_dev, swfrm_info); |
| } |
| |
| INIT_WORK(&req->runner_work, imgsys_runner_func); |
| queue_work(req->imgsys_pipe->imgsys_dev->runner_wq, |
| &req->runner_work); |
| } |
| |
| static int mtk_imgsys_hw_flush_pipe_jobs(struct mtk_imgsys_pipe *pipe) |
| { |
| struct mtk_imgsys_request *req; |
| struct list_head job_list = LIST_HEAD_INIT(job_list); |
| unsigned long flag; |
| int num; |
| int ret; |
| |
| spin_lock_irqsave(&pipe->running_job_lock, flag); |
| list_splice_init(&pipe->pipe_job_running_list, &job_list); |
| pipe->num_jobs = 0; |
| spin_unlock_irqrestore(&pipe->running_job_lock, flag); |
| |
| ret = wait_event_timeout |
| (pipe->imgsys_dev->flushing_waitq, |
| !(num = atomic_read(&pipe->imgsys_dev->num_composing)), |
| msecs_to_jiffies(1000 / 30 * IMGSYS_WORKING_BUF_NUM * 3)); |
| if (!ret && num) { |
| dev_info(pipe->imgsys_dev->dev, |
| "flushing is aborted, num(%d)\n", num); |
| return -EINVAL; |
| } |
| |
| list_for_each_entry(req, &job_list, list) |
| mtk_imgsys_pipe_job_finish(req, VB2_BUF_STATE_ERROR); |
| |
| dev_dbg(pipe->imgsys_dev->dev, |
| "%s: wakeup num(%d)\n", __func__, num); |
| return 0; |
| } |
| |
| static void module_uninit(struct kref *kref) |
| { |
| struct mtk_imgsys_dev *imgsys_dev; |
| int i; |
| |
| imgsys_dev = container_of(kref, struct mtk_imgsys_dev, init_kref); |
| |
| for (i = 0; i < (imgsys_dev->num_mods); i++) |
| if (imgsys_dev->modules[i].uninit) |
| imgsys_dev->modules[i].uninit(imgsys_dev); |
| } |
| |
| void mtk_imgsys_mod_put(struct mtk_imgsys_dev *imgsys_dev) |
| { |
| struct kref *kref; |
| |
| kref = &imgsys_dev->init_kref; |
| kref_put(kref, module_uninit); |
| } |
| |
| void mtk_imgsys_mod_get(struct mtk_imgsys_dev *imgsys_dev) |
| { |
| struct kref *kref; |
| |
| kref = &imgsys_dev->init_kref; |
| kref_get(kref); |
| } |
| |
| static int mtk_imgsys_hw_connect(struct mtk_imgsys_dev *imgsys_dev) |
| { |
| int ret, i; |
| u32 user_cnt = 0; |
| struct img_init_info info; |
| |
| user_cnt = atomic_read(&imgsys_dev->imgsys_user_cnt); |
| if (user_cnt != 0) |
| dev_info(imgsys_dev->dev, |
| "%s: [ERROR] imgsys user count is not zero(%d)\n", |
| __func__, user_cnt); |
| |
| atomic_set(&imgsys_dev->imgsys_user_cnt, 0); |
| |
| pm_runtime_get_sync(imgsys_dev->dev); |
| /* set default value for hw module */ |
| for (i = 0; i < (imgsys_dev->num_mods); i++) |
| imgsys_dev->modules[i].init(imgsys_dev); |
| kref_init(&imgsys_dev->init_kref); |
| |
| pm_runtime_put_sync(imgsys_dev->dev); |
| if (!imgsys_quick_onoff_en) { |
| pm_runtime_get_sync(imgsys_dev->dev); |
| for (i = 0; i < (imgsys_dev->num_mods); i++) |
| if (imgsys_dev->modules[i].set) |
| imgsys_dev->modules[i].set(imgsys_dev); |
| } |
| |
| ret = mtk_hcp_allocate_working_buffer(imgsys_dev->scp_pdev, 0); |
| if (ret) { |
| dev_err(imgsys_dev->dev, "%s: mtk_hcp_allocate_working_buffer failed %d\n", |
| __func__, ret); |
| return -EBUSY; |
| } |
| |
| /* IMGSYS HW INIT */ |
| memset(&info, 0, sizeof(info)); |
| info.drv_data = (u64)&imgsys_dev; |
| info.header_version = HEADER_VER; |
| info.frameparam_size = sizeof(struct img_ipi_frameparam); |
| info.reg_phys_addr = imgsys_dev->imgsys_resource->start; |
| info.reg_range = resource_size(imgsys_dev->imgsys_resource); |
| |
| mtk_hcp_get_init_info(imgsys_dev->scp_pdev, &info); |
| info.sec_tag = imgsys_dev->imgsys_pipe[0].init_info.sec_tag; |
| info.full_wd = imgsys_dev->imgsys_pipe[0].init_info.sensor.full_wd; |
| info.full_ht = imgsys_dev->imgsys_pipe[0].init_info.sensor.full_ht; |
| info.smvr_mode = 0; |
| |
| ret = imgsys_send(imgsys_dev->scp_pdev, HCP_IMGSYS_INIT_ID, |
| (void *)&info, sizeof(info), 0, 1); |
| |
| if (ret) { |
| dev_err(imgsys_dev->dev, "%s: send SCP_IPI_DIP_FRAME failed %d\n", |
| __func__, ret); |
| return -EBUSY; |
| } |
| |
| /* calling cmdq stream on */ |
| imgsys_cmdq_streamon(imgsys_dev); |
| |
| mtk_hcp_register(imgsys_dev->scp_pdev, HCP_IMGSYS_FRAME_ID, |
| imgsys_scp_handler, "imgsys_scp_handler", imgsys_dev); |
| |
| return 0; |
| } |
| |
| static void mtk_imgsys_hw_disconnect(struct mtk_imgsys_dev *imgsys_dev) |
| { |
| int ret; |
| struct img_init_info info = {0}; |
| u32 user_cnt = 0; |
| |
| imgsys_send(imgsys_dev->scp_pdev, HCP_IMGSYS_DEINIT_ID, |
| (void *)&info, sizeof(info), 0, 1); |
| |
| mtk_hcp_unregister(imgsys_dev->scp_pdev, HCP_DIP_INIT_ID); |
| mtk_hcp_unregister(imgsys_dev->scp_pdev, HCP_DIP_FRAME_ID); |
| |
| /* calling cmdq stream off */ |
| imgsys_cmdq_streamoff(imgsys_dev); |
| |
| /* RELEASE IMGSYS WORKING BUFFER FIRST */ |
| ret = mtk_hcp_release_working_buffer(imgsys_dev->scp_pdev); |
| if (ret) { |
| dev_info(imgsys_dev->dev, |
| "%s: mtk_hcp_release_working_buffer failed(%d)\n", |
| __func__, ret); |
| } |
| |
| if (!imgsys_quick_onoff_en) |
| pm_runtime_put_sync(imgsys_dev->dev); |
| |
| mtk_imgsys_mod_put(imgsys_dev); |
| |
| user_cnt = atomic_read(&imgsys_dev->imgsys_user_cnt); |
| if (user_cnt != 0) |
| dev_info(imgsys_dev->dev, |
| "%s: [ERROR] imgsys user count is not yet return to zero(%d)\n", |
| __func__, user_cnt); |
| } |
| |
| int mtk_imgsys_hw_streamon(struct mtk_imgsys_pipe *pipe) |
| { |
| struct mtk_imgsys_dev *imgsys_dev = pipe->imgsys_dev; |
| int ret; |
| |
| mutex_lock(&imgsys_dev->hw_op_lock); |
| if (!imgsys_dev->stream_cnt) { |
| ret = mtk_imgsys_hw_connect(pipe->imgsys_dev); |
| if (ret) { |
| dev_info(pipe->imgsys_dev->dev, |
| "%s:%s: pipe(%d) connect to dip_hw failed\n", |
| __func__, pipe->desc->name, pipe->desc->id); |
| |
| mutex_unlock(&imgsys_dev->hw_op_lock); |
| |
| return ret; |
| } |
| INIT_LIST_HEAD(&pipe->pipe_job_running_list); |
| } |
| imgsys_dev->stream_cnt++; |
| atomic_set(&imgsys_dev->num_composing, 0); |
| mutex_unlock(&imgsys_dev->hw_op_lock); |
| |
| pipe->streaming = 1; |
| dev_dbg(pipe->imgsys_dev->dev, |
| "%s:%s: started stream, id(%d), stream cnt(%d)\n", |
| __func__, pipe->desc->name, pipe->desc->id, imgsys_dev->stream_cnt); |
| |
| return 0; |
| } |
| |
| int mtk_imgsys_hw_streamoff(struct mtk_imgsys_pipe *pipe) |
| { |
| struct mtk_imgsys_dev *imgsys_dev = pipe->imgsys_dev; |
| int ret; |
| |
| dev_dbg(imgsys_dev->dev, |
| "%s:%s: streamoff, removing all running jobs\n", |
| __func__, pipe->desc->name); |
| |
| pipe->streaming = 0; |
| |
| ret = mtk_imgsys_hw_flush_pipe_jobs(pipe); |
| if (ret) |
| return ret; |
| |
| /* Stop the hardware if there is no streaming pipe */ |
| mutex_lock(&imgsys_dev->hw_op_lock); |
| imgsys_dev->stream_cnt--; |
| if (!imgsys_dev->stream_cnt) |
| mtk_imgsys_hw_disconnect(imgsys_dev); |
| |
| mutex_unlock(&imgsys_dev->hw_op_lock); |
| |
| return 0; |
| } |
| |
| static void imgsys_fill_img_buf(struct mtk_imgsys_dev_buffer *dev_buf, |
| struct header_desc_norm *desc_norm, |
| struct mtk_imgsys_request *req) |
| { |
| struct buf_info *buf_info = &desc_norm->fparams[0][0].bufs[0]; |
| int i; |
| |
| desc_norm->fparams_tnum = 1; |
| |
| buf_info->buf.num_planes = dev_buf->fmt.fmt.pix_mp.num_planes; |
| buf_info->fmt.fmt.pix_mp.width = dev_buf->fmt.fmt.pix_mp.width; |
| buf_info->fmt.fmt.pix_mp.height = dev_buf->fmt.fmt.pix_mp.height; |
| buf_info->fmt.fmt.pix_mp.pixelformat = dev_buf->fmt.fmt.pix_mp.pixelformat; |
| buf_info->crop = dev_buf->crop; |
| buf_info->rotation = dev_buf->rotation; |
| buf_info->hflip = dev_buf->hflip; |
| buf_info->vflip = dev_buf->vflip; |
| buf_info->resizeratio = dev_buf->resize_ratio; |
| |
| for (i = 0; i < buf_info->buf.num_planes; i++) { |
| buf_info->buf.planes[i].m.dma_buf.offset = dev_buf->vbb.planes[i].data_offset; |
| buf_info->buf.planes[i].reserved[0] = dev_buf->isp_daddr[i]; |
| buf_info->buf.planes[i].reserved[1] = dev_buf->vbb.vb2_buf.planes[i].length; |
| buf_info->fmt.fmt.pix_mp.plane_fmt[i].sizeimage = |
| dev_buf->vbb.vb2_buf.planes[i].min_length; |
| buf_info->fmt.fmt.pix_mp.plane_fmt[i].bytesperline = |
| dev_buf->fmt.fmt.pix_mp.plane_fmt[i].bytesperline; |
| } |
| } |
| |
| static void imgsys_fill_meta_buf(struct mtk_imgsys_dev_buffer *dev_buf, |
| struct header_desc_norm *desc_norm, |
| struct mtk_imgsys_request *req) |
| { |
| struct buf_info *buf_info = &desc_norm->fparams[0][0].bufs[0]; |
| |
| memset(&buf_info->buf, 0, sizeof(buf_info->buf)); |
| |
| desc_norm->fparams_tnum = 1; |
| |
| buf_info->buf.num_planes = 1; |
| |
| buf_info->fmt.fmt.pix_mp.width = dev_buf->fmt.fmt.meta.buffersize; |
| buf_info->fmt.fmt.pix_mp.height = 1; |
| buf_info->fmt.fmt.pix_mp.pixelformat = dev_buf->fmt.fmt.meta.dataformat; |
| buf_info->fmt.fmt.pix_mp.plane_fmt[0].sizeimage = dev_buf->fmt.fmt.meta.buffersize; |
| buf_info->fmt.fmt.pix_mp.plane_fmt[0].bytesperline = dev_buf->fmt.fmt.meta.buffersize; |
| buf_info->buf.planes[0].reserved[0] = dev_buf->isp_daddr[0]; |
| buf_info->buf.planes[0].reserved[1] = dev_buf->vbb.vb2_buf.planes[0].length; |
| buf_info->buf.planes[0].m.dma_buf.phyaddr = dev_buf->scp_daddr[0]; |
| } |
| |
| static void imgsys_fill_buf_info(int idx, struct mtk_imgsys_dev_buffer *dev_buf, |
| struct singlenode_desc_norm *sd_norm, |
| struct mtk_imgsys_request *req) |
| { |
| if (!dev_buf) |
| return; |
| |
| if (idx < 0 || idx >= IMG_MAX_HW_DMAS) |
| return; |
| |
| sd_norm->dmas_enable[idx][0] = 1; |
| |
| switch (idx) { |
| case MTK_IMGSYS_VIDEO_NODE_ID_TUNING_OUT: |
| imgsys_fill_meta_buf(dev_buf, &sd_norm->tuning_meta, req); |
| break; |
| case MTK_IMGSYS_VIDEO_NODE_ID_CTRLMETA_OUT: |
| imgsys_fill_meta_buf(dev_buf, &sd_norm->ctrl_meta, req); |
| break; |
| case MTK_IMGSYS_VIDEO_NODE_ID_METAI_OUT: |
| case MTK_IMGSYS_VIDEO_NODE_ID_FEO_CAPTURE: |
| case MTK_IMGSYS_VIDEO_NODE_ID_IMGSTATO_CAPTURE: |
| imgsys_fill_meta_buf(dev_buf, &sd_norm->dmas[idx], req); |
| break; |
| default: |
| imgsys_fill_img_buf(dev_buf, &sd_norm->dmas[idx], req); |
| break; |
| } |
| } |
| |
| static void imgsys_fill_singledev_buf(struct mtk_imgsys_request *req) |
| { |
| struct mtk_imgsys_hw_working_buf *buf = req->working_buf; |
| int i; |
| |
| if (!buf) |
| return; |
| |
| memset(buf->sd_norm, 0, sizeof(*buf->sd_norm)); |
| for (i = 0; i < req->imgsys_pipe->desc->total_queues; i++) |
| imgsys_fill_buf_info(i, req->buf_map[i], buf->sd_norm, req); |
| } |
| |
| static void imgsys_composer_workfunc(struct work_struct *work) |
| { |
| struct mtk_imgsys_request *req = mtk_imgsys_composer_work_to_req(work); |
| struct mtk_imgsys_dev *imgsys_dev = req->imgsys_pipe->imgsys_dev; |
| struct img_ipi_param ipi_param; |
| int ret; |
| |
| imgsys_fill_singledev_buf(req); |
| |
| dev_dbg(imgsys_dev->dev, |
| "%s:(reqfd-%d) to send frame_no(%d)\n", |
| __func__, req->request_fd, |
| req->img_fparam.frameparam.frame_no); |
| media_request_get(&req->req); |
| |
| if (down_interruptible(&imgsys_dev->sem)) |
| return; |
| |
| ipi_param.usage = IMG_IPI_FRAME; |
| ipi_param.req_addr_va = (u64)req; |
| ipi_param.smvr_mode = false; |
| ipi_param.frm_param.handle = req->id; |
| ipi_param.frm_param.offset = 0; |
| ipi_param.frm_param.scp_addr = req->working_buf->scp_daddr; |
| |
| mutex_lock(&imgsys_dev->hw_op_lock); |
| atomic_inc(&imgsys_dev->num_composing); |
| |
| ret = imgsys_send(imgsys_dev->scp_pdev, HCP_DIP_FRAME_ID, |
| &ipi_param, sizeof(ipi_param), |
| req->request_fd, 0); |
| |
| if (ret) { |
| dev_info(imgsys_dev->dev, |
| "frame_no(%d) send SCP_IPI_DIP_FRAME failed %d\n", |
| req->img_fparam.frameparam.frame_no, ret); |
| mtk_imgsys_pipe_remove_job(req); |
| mtk_imgsys_hw_working_buf_free(imgsys_dev, req->working_buf); |
| req->working_buf = NULL; |
| mtk_imgsys_pipe_job_finish(req, VB2_BUF_STATE_ERROR); |
| wake_up(&imgsys_dev->flushing_waitq); |
| } |
| mutex_unlock(&imgsys_dev->hw_op_lock); |
| |
| dev_dbg(imgsys_dev->dev, "%s:(reqfd-%d) sent\n", __func__, |
| req->request_fd); |
| } |
| |
| void mtk_imgsys_hw_enqueue(struct mtk_imgsys_dev *imgsys_dev, |
| struct mtk_imgsys_request *req) |
| { |
| struct req_frameparam *frameparam = &req->img_fparam.frameparam; |
| |
| req->working_buf = mtk_imgsys_hw_working_buf_alloc(imgsys_dev); |
| if (!req->working_buf) { |
| dev_info(imgsys_dev->dev, |
| "%s:req(%p): no free working buffer available\n", |
| req->imgsys_pipe->desc->name, req); |
| return; |
| } |
| |
| frameparam->state = FRAME_STATE_INIT; |
| frameparam->frame_no = |
| atomic_inc_return(&imgsys_dev->imgsys_enqueue_cnt); |
| |
| INIT_WORK(&req->composer_work, imgsys_composer_workfunc); |
| queue_work(req->imgsys_pipe->imgsys_dev->composer_wq, |
| &req->composer_work); |
| } |