304 lines
7.4 KiB
C
304 lines
7.4 KiB
C
// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
|
|
/*
|
|
* Copyright (C) 2025 Amlogic, Inc. All rights reserved
|
|
*/
|
|
|
|
#include <linux/cleanup.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-event.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <media/v4l2-mc.h>
|
|
|
|
#include "c4-isp-common.h"
|
|
#include "hw/c4-isp-hw.h"
|
|
#include "hw/c4-isp-regs.h"
|
|
|
|
static void c4_isp_offline_prep_febe_fdn(struct c4_isp_device *isp)
|
|
{
|
|
struct c4_isp_rdma_reg *ipp_rdma_init = isp->ipp_rdma_info->rdma_init.vaddr;
|
|
struct c4_isp_rdma_reg *ipp_rdma_cfg = isp->ipp_rdma_info->rdma_cfg.vaddr;
|
|
struct c4_isp_rdma_reg *febe_rdma_fdn = isp->febe_rdma_fdn.vaddr;
|
|
unsigned int i;
|
|
u32 reg_idx;
|
|
|
|
for (i = 0; i < isp->ipp_rdma_info->rdma_init_cnt; i++) {
|
|
reg_idx = ipp_rdma_init[i].addr >> 2;
|
|
febe_rdma_fdn[reg_idx].value = ipp_rdma_init[i].value;
|
|
}
|
|
|
|
for (i = 0; i < isp->ipp_rdma_info->rdma_cfg_cnt; i++) {
|
|
reg_idx = ipp_rdma_cfg[i].addr >> 2;
|
|
febe_rdma_fdn[reg_idx].value = ipp_rdma_cfg[i].value;
|
|
}
|
|
}
|
|
|
|
static int c4_isp_offline_cfg_febe_phase(struct c4_isp_device *isp,
|
|
struct c4_isp_data_buffer *buff,
|
|
u32 phase_min, u32 phase_max)
|
|
{
|
|
u32 offset = sizeof(struct c4_isp_rdma_reg) * phase_min;;
|
|
u16 reg_cnt = phase_max - phase_min;
|
|
struct c4_isp_data_buffer rdma_buff = { 0 };
|
|
int ret;
|
|
|
|
rdma_buff.dma_addr = buff->dma_addr + offset;
|
|
rdma_buff.vaddr = buff->vaddr + offset;
|
|
|
|
ret = c4_isp_rdma_manual_start(isp, &rdma_buff, reg_cnt);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int c4_isp_offline_cfg_febe_fdn(struct c4_isp_device *isp)
|
|
{
|
|
int ret;
|
|
|
|
ret = c4_isp_offline_cfg_febe_phase(isp, &isp->febe_rdma_fdn,
|
|
C4_ISP_FEBE_RDMA_PHASE_0_MIN,
|
|
C4_ISP_FEBE_RDMA_PHASE_0_MAX);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to cfg isp_rdma_fdn phase_0\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = c4_isp_offline_cfg_febe_phase(isp, &isp->febe_rdma_fdn,
|
|
C4_ISP_FEBE_RDMA_PHASE_1_MIN,
|
|
C4_ISP_FEBE_RDMA_PHASE_1_MAX);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to cfg isp_rdma_fdn phase_1\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = c4_isp_offline_cfg_febe_phase(isp, &isp->febe_rdma_fdn,
|
|
C4_ISP_FEBE_RDMA_PHASE_2_MIN,
|
|
C4_ISP_FEBE_RDMA_PHASE_2_MAX);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to cfg isp_rdma_fdn phase_2\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = c4_isp_offline_cfg_febe_phase(isp, &isp->febe_rdma_fdn,
|
|
C4_ISP_FEBE_RDMA_PHASE_3_MIN,
|
|
C4_ISP_FEBE_RDMA_PHASE_3_MAX);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to cfg isp_rdma_fdn phase_3\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int c4_isp_offline_cfg_rdma(struct c4_isp_device *isp)
|
|
{
|
|
int ret;
|
|
|
|
if (!isp->rdma_en)
|
|
return 0;
|
|
|
|
c4_ipp_rdma_mutex_lock(isp, true);
|
|
c4_ipp_rdma_work_flag(isp, false);
|
|
|
|
c4_isp_offline_prep_febe_fdn(isp);
|
|
|
|
c4_ipp_rdma_mutex_lock(isp, false);
|
|
|
|
c4_ipp_rdma_runtime_mutex_lock(isp, true);
|
|
ret = c4_isp_offline_cfg_febe_fdn(isp);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to manual start isp_rdma_fdn\n");
|
|
goto rdma_runtime_unlock;
|
|
}
|
|
|
|
ret = c4_isp_rdma_manual_start(isp, &isp->febe_rdma_init,
|
|
isp->febe_rdma_init_reg_cnt);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to manual start isp_rdma_init\n");
|
|
goto rdma_runtime_unlock;
|
|
}
|
|
|
|
ret = c4_isp_rdma_manual_start(isp, &isp->febe_rdma_cfg,
|
|
isp->febe_rdma_cfg_reg_cnt);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to manual start isp_rdma_cfg\n");
|
|
goto rdma_runtime_unlock;
|
|
}
|
|
|
|
ret = c4_isp_rdma_manual_start(isp, &isp->disp_rdma_base,
|
|
C4_ISP_DISP_RDMA_REG_MAX);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to manual start disp_rdma_base\n");
|
|
goto rdma_runtime_unlock;
|
|
}
|
|
|
|
ret = c4_isp_rdma_manual_start(isp, &isp->disp_rdma_init,
|
|
isp->disp_rdma_init_reg_cnt);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to manual start disp_rdma_init\n");
|
|
goto rdma_runtime_unlock;
|
|
}
|
|
|
|
rdma_runtime_unlock:
|
|
c4_ipp_rdma_runtime_mutex_lock(isp, false);
|
|
return ret;
|
|
}
|
|
|
|
static void c4_isp_offtile_cfg_buff(struct c4_isp_device *isp,
|
|
struct c4_isp_cap_buffer *buff)
|
|
{
|
|
struct c4_isp_core_cfg_info *cfg_info = &isp->core.cfg_info;
|
|
struct c4_isp_vfcd_params vfcd = { 0 };
|
|
|
|
vfcd.id = C4_ISP_VFCD_0;
|
|
vfcd.luma_body_base_addr = buff->dma_addr[C4_ISP_PLANE_Y];
|
|
c4_isp_vfcd_cfg_rmif_buff(isp, &vfcd);
|
|
|
|
if (cfg_info->img_mode == C4_ISP_IMAGE_HDR) {
|
|
vfcd.id = cfg_info->rawtnr_en ? C4_ISP_VFCD_4 : C4_ISP_VFCD_1;
|
|
vfcd.luma_body_base_addr = buff->dma_addr[C4_ISP_PLANE_UV];
|
|
c4_isp_vfcd_cfg_rmif_buff(isp, &vfcd);
|
|
}
|
|
}
|
|
|
|
static void c4_isp_offpost_cfg_buff(struct c4_isp_device *isp,
|
|
struct c4_isp_cap_buffer *buff)
|
|
{
|
|
struct c4_isp_vfcd_params vfcd = { 0 };
|
|
|
|
vfcd.id = C4_ISP_VFCD_2;
|
|
vfcd.luma_body_base_addr = buff->dma_addr[C4_ISP_PLANE_Y];
|
|
vfcd.chrm_body_base_addr = buff->dma_addr[C4_ISP_PLANE_UV];
|
|
|
|
c4_isp_vfcd_cfg_rmif_buff(isp, &vfcd);
|
|
}
|
|
|
|
static void c4_isp_offline_cfg_buff(struct c4_isp_device *isp,
|
|
struct c4_isp_cap_buffer *buff)
|
|
{
|
|
struct c4_isp_core_cfg_info *cfg_info = &isp->core.cfg_info;
|
|
|
|
if (cfg_info->work_mode == C4_ISP_OFFTILE_MODE ||
|
|
cfg_info->work_mode == C4_ISP_OFFHUGE_MODE)
|
|
c4_isp_offtile_cfg_buff(isp, buff);
|
|
else
|
|
c4_isp_offpost_cfg_buff(isp, buff);
|
|
}
|
|
|
|
static int c4_isp_offline_thread(void *data)
|
|
{
|
|
struct c4_isp_thread_info *thrd_info = data;
|
|
struct c4_isp_cap_buffer *buff;
|
|
struct c4_isp_capture *imp;
|
|
unsigned long time;
|
|
|
|
while (!kthread_should_stop()) {
|
|
scoped_guard(spinlock_irqsave, &thrd_info->buff_lock) {
|
|
buff = list_first_entry_or_null(&thrd_info->pending,
|
|
struct c4_isp_cap_buffer, list);
|
|
if (buff) {
|
|
list_del(&buff->list);
|
|
thrd_info->buff = buff;
|
|
}
|
|
}
|
|
|
|
if (!buff) {
|
|
usleep_range(3000, 4000);
|
|
continue;
|
|
}
|
|
|
|
imp = vb2_get_drv_priv(buff->vb.vb2_buf.vb2_queue);
|
|
|
|
imp->isp->sending = true;
|
|
|
|
c4_isp_offline_cfg_buff(imp->isp, buff);
|
|
|
|
c4_isp_offline_cfg_rdma(imp->isp);
|
|
|
|
/* Hardware write frame reset trigger */
|
|
c4_isp_febe_cfg_frm_rst_trig(imp->isp);
|
|
|
|
/* Hardware write frame start trigger */
|
|
c4_isp_febe_cfg_frm_start_trig(imp->isp);
|
|
|
|
time = wait_for_completion_timeout(&thrd_info->done,
|
|
msecs_to_jiffies(300));
|
|
|
|
buff->vb.sequence = imp->isp->be_frm_seq;
|
|
buff->vb.vb2_buf.timestamp = ktime_get();
|
|
buff->vb.field = V4L2_FIELD_NONE;
|
|
if (!time) {
|
|
dev_err(imp->isp->dev, "Timeout to process\n");
|
|
vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_ERROR);
|
|
} else {
|
|
vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_DONE);
|
|
}
|
|
|
|
thrd_info->buff = NULL;
|
|
imp->isp->sending = false;
|
|
|
|
complete_all(&imp->done);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int c4_isp_offline_thread_start(struct c4_isp_device *isp)
|
|
{
|
|
struct c4_isp_thread_info *thrd_info = isp->thrd_info;
|
|
int ret;
|
|
|
|
if (thrd_info->thread) {
|
|
thrd_info->start_count++;
|
|
return 0;
|
|
}
|
|
|
|
thrd_info->start_count = 0;
|
|
thrd_info->buff = NULL;
|
|
INIT_LIST_HEAD(&thrd_info->pending);
|
|
spin_lock_init(&thrd_info->buff_lock);
|
|
init_completion(&thrd_info->done);
|
|
|
|
thrd_info->thread = kthread_run(c4_isp_offline_thread,
|
|
thrd_info, "isp-offline");
|
|
if (IS_ERR(thrd_info->thread)) {
|
|
ret = PTR_ERR(thrd_info->thread);
|
|
thrd_info->thread = NULL;
|
|
return ret;
|
|
}
|
|
|
|
thrd_info->start_count++;
|
|
|
|
dev_info(isp->dev, "Start ISP offline thread\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void c4_isp_offline_thread_stop(struct c4_isp_device *isp)
|
|
{
|
|
struct c4_isp_thread_info *thrd_info = isp->thrd_info;
|
|
|
|
if (!thrd_info->thread)
|
|
return;
|
|
|
|
thrd_info->start_count--;
|
|
|
|
if (thrd_info->start_count)
|
|
return;
|
|
|
|
kthread_stop(thrd_info->thread);
|
|
thrd_info->thread = NULL;
|
|
|
|
dev_info(isp->dev, "Stop ISP offline thread\n");
|
|
}
|
|
|
|
void c4_isp_offline_thread_isr(struct c4_isp_device *isp)
|
|
{
|
|
if (!isp->thrd_info->thread)
|
|
return;
|
|
|
|
complete(&isp->thrd_info->done);
|
|
}
|