// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright (C) 2025 Amlogic, Inc. All rights reserved */ #include #include #include #include #include #include #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); ret = c4_isp_offline_cfg_febe_fdn(isp); if (ret) { dev_err(isp->dev, "Failed to manual start isp_rdma_fdn\n"); return ret; } 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"); return ret; } 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"); return ret; } 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"); return ret; } 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"); return ret; } return 0; } 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) 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)); if (!time) dev_err(imp->isp->dev, "Timeout to process\n"); buff->vb.sequence = imp->isp->be_frm_seq; buff->vb.vb2_buf.timestamp = ktime_get(); buff->vb.field = V4L2_FIELD_NONE; 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); }