1262 lines
30 KiB
C
1262 lines
30 KiB
C
// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
|
|
/*
|
|
* Copyright (C) 2025 Amlogic, Inc. All rights reserved
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/version.h>
|
|
#include <linux/pm_domain.h>
|
|
|
|
#include <linux/media-bus-format.h>
|
|
|
|
#include <media/v4l2-common.h>
|
|
#include <media/v4l2-device.h>
|
|
#include <media/v4l2-subdev.h>
|
|
#include <media/v4l2-fwnode.h>
|
|
#include <media/v4l2-mc.h>
|
|
|
|
#include "c4-isp-common.h"
|
|
#include "hw/c4-isp-hw.h"
|
|
#include "hw/c4-isp-regs.h"
|
|
|
|
#define C4_IPP_RDMA_INIT_SIZE (32 * SZ_1K)
|
|
#define C4_IPP_RDMA_CFG_SIZE (4 * SZ_1K)
|
|
#define C4_FEBE_RDMA_BASE_SIZE (512 * SZ_1K)
|
|
#define C4_FEBE_RDMA_FDN_SIZE (256 * SZ_1K)
|
|
#define C4_FEBE_RDMA_CFG_SIZE (192 * SZ_1K)
|
|
#define C4_DISP_RDMA_BASE_SIZE (8 * SZ_1K)
|
|
#define C4_DISP_RDMA_INIT_SIZE (8 * SZ_1K)
|
|
#define C4_DISP_RDMA_REG_BASE 0x12000
|
|
#define C4_ISP_DISP_REG(reg, id) ((reg) + (id) * 0x100)
|
|
|
|
enum c4_isp_debug_idx {
|
|
ISP_IN_W = 0,
|
|
ISP_IN_H,
|
|
DISP0_W,
|
|
DISP0_H,
|
|
DISP1_W,
|
|
DISP1_H,
|
|
DISP2_W,
|
|
DISP2_H,
|
|
SIZE0_AF,
|
|
SIZE0_AWB,
|
|
SIZE1_AE,
|
|
SIZE1_FLKR,
|
|
SIZE2_POST,
|
|
CSUM_0,
|
|
CSUM_1,
|
|
CSUM_2,
|
|
CSUM_3,
|
|
ISP_DEBUG_NUM
|
|
};
|
|
|
|
static struct c4_isp_thread_info thread_info;
|
|
static struct c4_isp_data_buffer rdma_febe_orig;
|
|
static struct c4_isp_data_buffer rdma_disp_orig;
|
|
static struct c4_ipp_rdma_info rdma_ipp_info;
|
|
|
|
static bool rdma_alloc_en = true;
|
|
module_param(rdma_alloc_en, bool, 0444);
|
|
|
|
static u32 c4_isp_read_field(u32 val, u32 start, u32 len)
|
|
{
|
|
u32 mask = (len == 32) ? ~0U : ((1U << len) - 1);
|
|
|
|
return (val >> start) & mask;
|
|
}
|
|
|
|
static u32 c4_isp_code_to_bpp(u32 code)
|
|
{
|
|
switch (code) {
|
|
case MEDIA_BUS_FMT_SBGGR10_1X10:
|
|
case MEDIA_BUS_FMT_SGBRG10_1X10:
|
|
case MEDIA_BUS_FMT_SGRBG10_1X10:
|
|
case MEDIA_BUS_FMT_SRGGB10_1X10:
|
|
return 10;
|
|
case MEDIA_BUS_FMT_SBGGR12_1X12:
|
|
case MEDIA_BUS_FMT_SGBRG12_1X12:
|
|
case MEDIA_BUS_FMT_SGRBG12_1X12:
|
|
case MEDIA_BUS_FMT_SRGGB12_1X12:
|
|
return 12;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static u32 *c4_isp_proc_status(struct c4_isp_device *isp)
|
|
{
|
|
static u32 isp_debuginfo[ISP_DEBUG_NUM];
|
|
u32 val;
|
|
int i;
|
|
|
|
val = c4_isp_base_hw_read(isp, ISP_TOP_FRM_SIZE);
|
|
isp_debuginfo[ISP_IN_W] = c4_isp_read_field(val, 16, 16);
|
|
isp_debuginfo[ISP_IN_H] = c4_isp_read_field(val, 0, 16);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
static const u32 idwmif_cfg_regs[] = {
|
|
IDWMIF_CFG_0, IDWMIF_CFG_1, IDWMIF_CFG_2
|
|
};
|
|
u32 stream_on;
|
|
|
|
stream_on = c4_isp_read_field(
|
|
c4_isp_base_hw_read(isp, idwmif_cfg_regs[i]), 0, 1);
|
|
if (stream_on) {
|
|
val = c4_isp_disp_hw_read(isp,
|
|
C4_ISP_DISP_REG(DISP_OUT_SIZE, i));
|
|
isp_debuginfo[DISP0_W + 2 * i] =
|
|
c4_isp_read_field(val, 16, 16);
|
|
isp_debuginfo[DISP0_H + 2 * i] =
|
|
c4_isp_read_field(val, 0, 16);
|
|
} else {
|
|
isp_debuginfo[DISP0_W + 2 * i] = 0;
|
|
isp_debuginfo[DISP0_H + 2 * i] = 0;
|
|
}
|
|
}
|
|
|
|
val = c4_isp_base_hw_read(isp, VIU_DMAWR_SIZE0);
|
|
isp_debuginfo[SIZE0_AF] = c4_isp_read_field(val, 0, 16);
|
|
isp_debuginfo[SIZE0_AWB] = c4_isp_read_field(val, 16, 16);
|
|
|
|
val = c4_isp_base_hw_read(isp, VIU_DMAWR_SIZE1);
|
|
isp_debuginfo[SIZE1_AE] = c4_isp_read_field(val, 0, 16);
|
|
isp_debuginfo[SIZE1_FLKR] = c4_isp_read_field(val, 16, 16);
|
|
|
|
val = c4_isp_base_hw_read(isp, VIU_DMAWR_SIZE2);
|
|
isp_debuginfo[SIZE2_POST] = c4_isp_read_field(val, 0, 16);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
isp_debuginfo[CSUM_0 + i] =
|
|
c4_isp_base_hw_read(isp, ISP_CHECKSUM_RO_DAT_0 + i * 4);
|
|
|
|
return isp_debuginfo;
|
|
}
|
|
|
|
static const char *c4_isp_work_mode_str(enum c4_isp_work_mode mode)
|
|
{
|
|
switch (mode) {
|
|
case C4_ISP_RAW2DDR_MODE:
|
|
return "RAW2DDR";
|
|
case C4_ISP_ALLSCAN_MODE:
|
|
return "ALLSCAN";
|
|
case C4_ISP_SCANTILE_MODE:
|
|
return "SCANTILE";
|
|
case C4_ISP_ALLTILE_MODE:
|
|
return "ALLTILE";
|
|
case C4_ISP_OFFTILE_MODE:
|
|
return "OFFTILE";
|
|
case C4_ISP_OFFPOST_MODE:
|
|
return "OFFPOST";
|
|
case C4_ISP_OFFHUGE_MODE:
|
|
return "OFFHUGE";
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
static const char *c4_isp_image_mode_str(enum c4_isp_image_mode mode)
|
|
{
|
|
switch (mode) {
|
|
case C4_ISP_IMAGE_SDR:
|
|
return "SDR";
|
|
case C4_ISP_IMAGE_HDR:
|
|
return "HDR";
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
static int c4_isp_proc_show(struct seq_file *m, void *v)
|
|
{
|
|
struct c4_isp_device *isp = m->private;
|
|
struct c4_isp_core *core = &isp->core;
|
|
u32 *isp_info = c4_isp_proc_status(isp);
|
|
struct v4l2_subdev_format fmt = {
|
|
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
|
|
.pad = C4_ISP_CORE_PAD_SINK_VIDEO,
|
|
};
|
|
u32 width = 0, height = 0, bpp = 0;
|
|
|
|
if (!v4l2_subdev_call_state_active(&core->sd, pad, get_fmt, &fmt)) {
|
|
width = fmt.format.width;
|
|
height = fmt.format.height;
|
|
bpp = c4_isp_code_to_bpp(fmt.format.code);
|
|
}
|
|
|
|
seq_printf(m, "\n****************** ISP MODULE PARAM *******************\n");
|
|
seq_printf(m, " ------- Pubattr Info ------- \n");
|
|
seq_printf(m, "%8s%8s%8s%8s%8s \n", "WndX", "WndY", "snsW", "snsH", "bpp");
|
|
seq_printf(m, "%8d%8d%8d%8d%8d \n\n", 0, 0, width, height, bpp);
|
|
|
|
seq_printf(m, "%12s%12s\n", "work_mode", "wdr_mode");
|
|
seq_printf(m, "%12s%12s\n\n",
|
|
c4_isp_work_mode_str(core->cfg_info.work_mode),
|
|
c4_isp_image_mode_str(core->cfg_info.img_mode));
|
|
|
|
seq_printf(m, " ------- Isp Solution ------- \n");
|
|
seq_printf(m, "%15s%15s \n", "input vsize", "input hsize");
|
|
seq_printf(m, "%15d%15d \n\n",
|
|
isp_info[ISP_IN_H], isp_info[ISP_IN_W]);
|
|
|
|
seq_printf(m, " ------- Disp Solution ------- \n");
|
|
seq_printf(m, "%15s%15s \n", "fmt hsize", "fmt vsize");
|
|
seq_printf(m, "%4s%11d%15d \n", "chn0",
|
|
isp_info[DISP0_W], isp_info[DISP0_H]);
|
|
seq_printf(m, "%4s%11d%15d \n", "chn1",
|
|
isp_info[DISP1_W], isp_info[DISP1_H]);
|
|
seq_printf(m, "%4s%11d%15d \n\n", "chn2",
|
|
isp_info[DISP2_W], isp_info[DISP2_H]);
|
|
|
|
seq_printf(m, " ------- Dma Configuration ------- \n");
|
|
seq_printf(m, "%10s%5s%15s%15s%15s%15s \n",
|
|
"chx_size0-4", "af", "awb", "ae", "flkr", "post");
|
|
seq_printf(m, "%16d%15d%15d%15d%15d \n\n",
|
|
isp_info[SIZE0_AF], isp_info[SIZE0_AWB],
|
|
isp_info[SIZE1_AE], isp_info[SIZE1_FLKR],
|
|
isp_info[SIZE2_POST]);
|
|
|
|
seq_printf(m, " ------- Isp Checksum ------- \n");
|
|
seq_printf(m, " -- ro_checksum_dat_[0-3] -- \n");
|
|
seq_printf(m, "%15s%15s%15s%15s \n",
|
|
"fe_din", "fe_dout", "me_dout", "be_dout");
|
|
seq_printf(m, "%15u%15u%15u%15u \n\n",
|
|
isp_info[CSUM_0], isp_info[CSUM_1],
|
|
isp_info[CSUM_2], isp_info[CSUM_3]);
|
|
seq_printf(m, "******************* PROC END *******************\n\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int c4_isp_debug_open(struct inode *inode, struct file *file)
|
|
{
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
|
return single_open(file, c4_isp_proc_show, pde_data(inode));
|
|
#else
|
|
return single_open(file, c4_isp_proc_show, PDE_DATA(inode));
|
|
#endif
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
|
|
static const struct proc_ops c4_isp_proc_file_ops = {
|
|
.proc_open = c4_isp_debug_open,
|
|
.proc_read = seq_read,
|
|
.proc_lseek = seq_lseek,
|
|
.proc_release = single_release,
|
|
};
|
|
#else
|
|
static const struct file_operations c4_isp_proc_file_ops = {
|
|
.open = c4_isp_debug_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
#endif
|
|
|
|
static int c4_isp_proc_init(struct c4_isp_device *isp)
|
|
{
|
|
char name[16];
|
|
|
|
snprintf(name, sizeof(name), "isp%u", isp->info->id);
|
|
isp->proc_entry = proc_create_data(name, 0644, NULL,
|
|
&c4_isp_proc_file_ops, isp);
|
|
if (!isp->proc_entry) {
|
|
dev_err(isp->dev, "isp%u create proc failed\n", isp->info->id);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void c4_isp_proc_remove(struct c4_isp_device *isp)
|
|
{
|
|
if (isp->proc_entry) {
|
|
proc_remove(isp->proc_entry);
|
|
isp->proc_entry = NULL;
|
|
}
|
|
}
|
|
|
|
static int c4_isp_fill_rdma_buff(struct c4_isp_device *isp, bool febe,
|
|
struct c4_isp_data_buffer *buff)
|
|
{
|
|
u32 count = febe ? C4_ISP_FEBE_RDMA_REG_MAX : C4_ISP_DISP_RDMA_REG_MAX;
|
|
struct c4_isp_rdma_base_reg *rdma_orig = buff->vaddr;
|
|
int ret;
|
|
|
|
ret = pm_runtime_resume_and_get(isp->dev);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to resume and get\n");
|
|
return ret;
|
|
}
|
|
|
|
c4_isp_top_cfg_reset(isp);
|
|
|
|
memset(buff->vaddr, 0xff, buff->size);
|
|
|
|
for (unsigned int reg = 0; reg < count; reg++) {
|
|
if (febe) {
|
|
rdma_orig[reg].addr = reg << 2;
|
|
rdma_orig[reg].value =
|
|
c4_isp_base_hw_read(isp, reg << 2);
|
|
} else {
|
|
rdma_orig[reg].addr = (reg << 2) + C4_DISP_RDMA_REG_BASE;
|
|
rdma_orig[reg].value =
|
|
c4_isp_disp_hw_read(isp, reg << 2);
|
|
}
|
|
}
|
|
|
|
pm_runtime_put(isp->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int c4_isp_rdma_alloc_buff(struct c4_isp_device *isp,
|
|
struct c4_isp_data_buffer *buff, u32 size)
|
|
{
|
|
if (buff->vaddr)
|
|
return 0;
|
|
|
|
buff->size = size;
|
|
buff->vaddr = dma_alloc_attrs(isp->dev, buff->size,
|
|
&buff->dma_addr, GFP_KERNEL, 0);
|
|
if (!buff->vaddr)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void c4_isp_rdma_free_buff(struct c4_isp_device *isp,
|
|
struct c4_isp_data_buffer *buff)
|
|
{
|
|
if (!buff->vaddr)
|
|
return;
|
|
|
|
dma_free_attrs(isp->dev, buff->size, buff->vaddr, buff->dma_addr, 0);
|
|
|
|
buff->vaddr = NULL;
|
|
buff->dma_addr = 0;
|
|
buff->size = 0;
|
|
}
|
|
|
|
static int c4_isp_rdma_alloc_ipp_buffs(struct c4_isp_device *isp)
|
|
{
|
|
struct c4_ipp_rdma_info *ipp_rdma_info = &rdma_ipp_info;
|
|
int ret;
|
|
|
|
if (ipp_rdma_info->rdma_init.vaddr) {
|
|
isp->ipp_rdma_info = ipp_rdma_info;
|
|
return 0;
|
|
}
|
|
|
|
ret = c4_isp_rdma_alloc_buff(isp, &ipp_rdma_info->rdma_init,
|
|
C4_IPP_RDMA_INIT_SIZE);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to alloc ipp_rdma_init buffer\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = c4_isp_rdma_alloc_buff(isp, &ipp_rdma_info->rdma_cfg,
|
|
C4_IPP_RDMA_CFG_SIZE);
|
|
if (ret) {
|
|
c4_isp_rdma_free_buff(isp, &ipp_rdma_info->rdma_init);
|
|
dev_err(isp->dev, "Failed to alloc ipp_rdma_cfg buffer\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
isp->ipp_rdma_info = ipp_rdma_info;
|
|
|
|
dev_info(isp->dev, "Ipp_rdma-(init/cfg): 0x%llx, 0x%llx\n",
|
|
ipp_rdma_info->rdma_init.dma_addr,
|
|
ipp_rdma_info->rdma_cfg.dma_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void c4_isp_rdma_free_ipp_buffs(struct c4_isp_device *isp)
|
|
{
|
|
c4_isp_rdma_free_buff(isp, &isp->ipp_rdma_info->rdma_init);
|
|
c4_isp_rdma_free_buff(isp, &isp->ipp_rdma_info->rdma_cfg);
|
|
}
|
|
|
|
static int c4_isp_rdma_alloc_febe_buffs(struct c4_isp_device *isp)
|
|
{
|
|
struct c4_isp_data_buffer *febe_rdma_orig = &rdma_febe_orig;
|
|
int ret;
|
|
|
|
if (!febe_rdma_orig->vaddr) {
|
|
ret = c4_isp_rdma_alloc_buff(isp, febe_rdma_orig,
|
|
C4_FEBE_RDMA_BASE_SIZE);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to alloc febe_rdma_orig buffer\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
c4_isp_fill_rdma_buff(isp, true, febe_rdma_orig);
|
|
}
|
|
|
|
ret = c4_isp_rdma_alloc_buff(isp, &isp->febe_rdma_base,
|
|
C4_FEBE_RDMA_BASE_SIZE);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to alloc febe_rdma_base buffer\n");
|
|
goto err_febe_rdma_orig_free_buffer;
|
|
}
|
|
|
|
ret = c4_isp_rdma_alloc_buff(isp, &isp->febe_rdma_init,
|
|
C4_FEBE_RDMA_CFG_SIZE);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to alloc febe_rdma_init buffer\n");
|
|
goto err_febe_rdma_base_free_buffer;
|
|
}
|
|
|
|
ret = c4_isp_rdma_alloc_buff(isp, &isp->febe_rdma_cfg,
|
|
C4_FEBE_RDMA_CFG_SIZE);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to alloc febe_rdma_cfg buffer\n");
|
|
goto err_febe_rdma_init_free_buffer;
|
|
}
|
|
|
|
ret = c4_isp_rdma_alloc_buff(isp, &isp->febe_rdma_fdn,
|
|
C4_FEBE_RDMA_FDN_SIZE);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to alloc febe_rdma_fdn buffer\n");
|
|
goto err_febe_rdma_cfg_free_buffer;
|
|
}
|
|
|
|
isp->febe_rdma_orig = febe_rdma_orig;
|
|
|
|
dev_info(isp->dev, "Febe_rdma-(orig/base): 0x%llx, 0x%llx\n",
|
|
isp->febe_rdma_orig->dma_addr, isp->febe_rdma_base.dma_addr);
|
|
dev_info(isp->dev, "Febe_rdma-(fdn/init/cfg): 0x%llx, 0x%llx, 0x%llx\n",
|
|
isp->febe_rdma_fdn.dma_addr, isp->febe_rdma_init.dma_addr,
|
|
isp->febe_rdma_cfg.dma_addr);
|
|
|
|
return 0;
|
|
|
|
err_febe_rdma_cfg_free_buffer:
|
|
c4_isp_rdma_free_buff(isp, &isp->febe_rdma_cfg);
|
|
err_febe_rdma_init_free_buffer:
|
|
c4_isp_rdma_free_buff(isp, &isp->febe_rdma_init);
|
|
err_febe_rdma_base_free_buffer:
|
|
c4_isp_rdma_free_buff(isp, &isp->febe_rdma_base);
|
|
err_febe_rdma_orig_free_buffer:
|
|
c4_isp_rdma_free_buff(isp, febe_rdma_orig);
|
|
return ret;
|
|
}
|
|
|
|
static void c4_isp_rdma_free_febe_buffs(struct c4_isp_device *isp)
|
|
{
|
|
c4_isp_rdma_free_buff(isp, &isp->febe_rdma_fdn);
|
|
c4_isp_rdma_free_buff(isp, &isp->febe_rdma_cfg);
|
|
c4_isp_rdma_free_buff(isp, &isp->febe_rdma_init);
|
|
c4_isp_rdma_free_buff(isp, &isp->febe_rdma_base);
|
|
c4_isp_rdma_free_buff(isp, isp->febe_rdma_orig);
|
|
}
|
|
|
|
static int c4_isp_rdma_alloc_disp_buffs(struct c4_isp_device *isp)
|
|
{
|
|
struct c4_isp_data_buffer *disp_rdma_orig = &rdma_disp_orig;
|
|
int ret;
|
|
|
|
if (!disp_rdma_orig->vaddr) {
|
|
ret = c4_isp_rdma_alloc_buff(isp, disp_rdma_orig,
|
|
C4_DISP_RDMA_BASE_SIZE);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to alloc disp_rdma_orig buffer\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
c4_isp_fill_rdma_buff(isp, false, disp_rdma_orig);
|
|
}
|
|
|
|
ret = c4_isp_rdma_alloc_buff(isp, &isp->disp_rdma_base,
|
|
C4_DISP_RDMA_INIT_SIZE);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to alloc disp_rdma_base buffer\n");
|
|
goto err_disp_rdma_orig_free_buffer;
|
|
}
|
|
|
|
ret = c4_isp_rdma_alloc_buff(isp, &isp->disp_rdma_init,
|
|
C4_DISP_RDMA_INIT_SIZE);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to alloc disp_rdma_init buffer\n");
|
|
goto err_disp_rdma_base_free_buffer;
|
|
}
|
|
|
|
isp->disp_rdma_orig = disp_rdma_orig;
|
|
|
|
dev_info(isp->dev, "Disp_rdma-(orig/base/init): 0x%llx, 0x%llx, 0x%llx\n",
|
|
isp->disp_rdma_orig->dma_addr, isp->disp_rdma_base.dma_addr,
|
|
isp->disp_rdma_init.dma_addr);
|
|
|
|
return 0;
|
|
|
|
err_disp_rdma_base_free_buffer:
|
|
c4_isp_rdma_free_buff(isp, &isp->disp_rdma_base);
|
|
err_disp_rdma_orig_free_buffer:
|
|
c4_isp_rdma_free_buff(isp, disp_rdma_orig);
|
|
return ret;
|
|
}
|
|
|
|
static void c4_isp_rdma_free_disp_buffs(struct c4_isp_device *isp)
|
|
{
|
|
c4_isp_rdma_free_buff(isp, &isp->disp_rdma_init);
|
|
c4_isp_rdma_free_buff(isp, &isp->disp_rdma_base);
|
|
c4_isp_rdma_free_buff(isp, isp->disp_rdma_orig);
|
|
}
|
|
|
|
static int c4_isp_rdma_alloc_buffers(struct c4_isp_device *isp)
|
|
{
|
|
int ret;
|
|
|
|
if (!isp->rdma_alloc_en)
|
|
return 0;
|
|
|
|
ret = c4_isp_rdma_alloc_ipp_buffs(isp);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = c4_isp_rdma_alloc_febe_buffs(isp);
|
|
if (ret)
|
|
goto err_rdma_free_ipp_buffs;
|
|
|
|
ret = c4_isp_rdma_alloc_disp_buffs(isp);
|
|
if (ret)
|
|
goto err_rdma_free_febe_buffs;
|
|
|
|
isp->rdma_inited_cnt = 0;
|
|
|
|
return 0;
|
|
|
|
err_rdma_free_febe_buffs:
|
|
c4_isp_rdma_free_febe_buffs(isp);
|
|
err_rdma_free_ipp_buffs:
|
|
c4_isp_rdma_free_ipp_buffs(isp);
|
|
return ret;
|
|
}
|
|
|
|
static void c4_isp_rdma_free_buffers(struct c4_isp_device *isp)
|
|
{
|
|
if (!isp->rdma_alloc_en)
|
|
return;
|
|
|
|
c4_isp_rdma_free_disp_buffs(isp);
|
|
c4_isp_rdma_free_febe_buffs(isp);
|
|
c4_isp_rdma_free_ipp_buffs(isp);
|
|
}
|
|
|
|
static int c4_isp_rdma_cfg_buffer(struct c4_isp_device *isp)
|
|
{
|
|
int ret;
|
|
|
|
if (isp->core.cfg_info.work_mode == C4_ISP_OFFTILE_MODE ||
|
|
isp->core.cfg_info.work_mode == C4_ISP_OFFPOST_MODE ||
|
|
isp->core.cfg_info.work_mode == C4_ISP_OFFHUGE_MODE ||
|
|
!isp->rdma_en)
|
|
return 0;
|
|
|
|
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_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_cfg_plat_setting(struct c4_isp_device *isp)
|
|
{
|
|
isp->fpga_env = false;
|
|
isp->rdma_alloc_en = rdma_alloc_en;
|
|
isp->rdma_en = rdma_alloc_en;
|
|
isp->thrd_info = &thread_info;
|
|
}
|
|
|
|
/* PM runtime suspend */
|
|
static int c4_isp_runtime_suspend(struct device *dev)
|
|
{
|
|
struct c4_isp_device *isp = dev_get_drvdata(dev);
|
|
|
|
pm_runtime_put_sync(isp->isp_pd);
|
|
pm_runtime_put_sync(isp->opp_pd);
|
|
|
|
clk_bulk_disable_unprepare(isp->info->clock_num, isp->clks);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* PM runtime resume */
|
|
static int c4_isp_runtime_resume(struct device *dev)
|
|
{
|
|
struct c4_isp_device *isp = dev_get_drvdata(dev);
|
|
|
|
pm_runtime_get_sync(isp->isp_pd);
|
|
pm_runtime_get_sync(isp->opp_pd);
|
|
|
|
return clk_bulk_prepare_enable(isp->info->clock_num, isp->clks);
|
|
}
|
|
|
|
static const struct dev_pm_ops c4_isp_pm_ops = {
|
|
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
|
pm_runtime_force_resume)
|
|
RUNTIME_PM_OPS(c4_isp_runtime_suspend,
|
|
c4_isp_runtime_resume, NULL)
|
|
};
|
|
|
|
/* IRQ handler */
|
|
static irqreturn_t c4_isp_fe_rst_irq_handler(int irq, void *dev)
|
|
{
|
|
struct c4_isp_device *isp = dev;
|
|
|
|
if (!isp->streaming || !isp->sending)
|
|
return IRQ_HANDLED;
|
|
|
|
dev_dbg(isp->dev, "FE rst intr\n");
|
|
|
|
c4_isp_core_queue_sof(isp);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t c4_isp_fe_done_irq_handler(int irq, void *dev)
|
|
{
|
|
struct c4_isp_device *isp = dev;
|
|
|
|
if (!isp->streaming || !isp->sending)
|
|
return IRQ_NONE;
|
|
|
|
dev_dbg(isp->dev, "FE done intr\n");
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t c4_isp_be_done_irq_handler(int irq, void *dev)
|
|
{
|
|
struct c4_isp_device *isp = dev;
|
|
|
|
if (!isp->streaming || !isp->sending)
|
|
return IRQ_NONE;
|
|
|
|
dev_dbg(isp->dev, "BE done intr\n");
|
|
|
|
c4_isp_captures_isr(isp, C4_ISP_CAP_DEV_0);
|
|
c4_isp_captures_isr(isp, C4_ISP_CAP_DEV_1);
|
|
c4_isp_captures_isr(isp, C4_ISP_CAP_DEV_2);
|
|
c4_isp_captures_isr(isp, C4_ISP_CAP_DEV_3);
|
|
|
|
c4_isp_core_nr_isr(isp);
|
|
|
|
c4_isp_raw_isr(isp);
|
|
c4_isp_stats_isr(isp);
|
|
c4_isp_params_isr(isp);
|
|
|
|
c4_isp_rdma_cfg_buffer(isp);
|
|
|
|
isp->be_frm_seq++;
|
|
|
|
c4_isp_offline_thread_isr(isp);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t c4_isp_intf_irq_handler(int irq, void *dev)
|
|
{
|
|
struct c4_isp_device *isp = dev;
|
|
|
|
if (!isp->streaming)
|
|
return IRQ_NONE;
|
|
|
|
dev_dbg(isp->dev, "INTF intf\n");
|
|
|
|
c4_ipp_multi_vc_isr(isp);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/* Subdev notifier register */
|
|
static int c4_isp_notify_bound(struct v4l2_async_notifier *notifier,
|
|
struct v4l2_subdev *sd,
|
|
struct v4l2_async_connection *asc)
|
|
{
|
|
struct c4_isp_device *isp =
|
|
container_of(notifier, struct c4_isp_device, notifier);
|
|
struct media_pad *sink =
|
|
&isp->ipp.sd.entity.pads[C4_IPP_PAD_SINK_VIDEO];
|
|
|
|
return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
|
|
MEDIA_LNK_FL_IMMUTABLE);
|
|
}
|
|
|
|
static int c4_isp_notify_complete(struct v4l2_async_notifier *notifier)
|
|
{
|
|
struct c4_isp_device *isp =
|
|
container_of(notifier, struct c4_isp_device, notifier);
|
|
|
|
return v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
|
|
}
|
|
|
|
static const struct v4l2_async_notifier_operations c4_isp_notify_ops = {
|
|
.bound = c4_isp_notify_bound,
|
|
.complete = c4_isp_notify_complete,
|
|
};
|
|
|
|
static int c4_isp_async_nf_register(struct c4_isp_device *isp)
|
|
{
|
|
struct v4l2_async_connection *asc;
|
|
struct fwnode_handle *ep;
|
|
int ret;
|
|
|
|
v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev);
|
|
|
|
ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), 0, 0,
|
|
FWNODE_GRAPH_ENDPOINT_NEXT);
|
|
if (!ep)
|
|
return -ENOTCONN;
|
|
|
|
asc = v4l2_async_nf_add_fwnode_remote(&isp->notifier, ep,
|
|
struct v4l2_async_connection);
|
|
fwnode_handle_put(ep);
|
|
|
|
if (IS_ERR(asc))
|
|
return PTR_ERR(asc);
|
|
|
|
isp->notifier.ops = &c4_isp_notify_ops;
|
|
ret = v4l2_async_nf_register(&isp->notifier);
|
|
if (ret)
|
|
v4l2_async_nf_cleanup(&isp->notifier);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void c4_isp_async_nf_unregister(struct c4_isp_device *isp)
|
|
{
|
|
v4l2_async_nf_unregister(&isp->notifier);
|
|
v4l2_async_nf_cleanup(&isp->notifier);
|
|
}
|
|
|
|
static int c4_isp_media_register(struct c4_isp_device *isp)
|
|
{
|
|
struct media_device *media_dev = &isp->media_dev;
|
|
struct v4l2_device *v4l2_dev = &isp->v4l2_dev;
|
|
int ret;
|
|
|
|
/* Initialize media device */
|
|
strscpy(media_dev->model, C4_ISP_DRIVER_NAME, sizeof(media_dev->model));
|
|
media_dev->dev = isp->dev;
|
|
|
|
media_device_init(media_dev);
|
|
|
|
/* Initialize v4l2 device */
|
|
v4l2_dev->mdev = media_dev;
|
|
strscpy(v4l2_dev->name, C4_ISP_DRIVER_NAME, sizeof(v4l2_dev->name));
|
|
|
|
ret = v4l2_device_register(isp->dev, v4l2_dev);
|
|
if (ret)
|
|
goto err_media_dev_cleanup;
|
|
|
|
ret = media_device_register(&isp->media_dev);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to register media device: %d\n", ret);
|
|
goto err_unreg_v4l2_dev;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_unreg_v4l2_dev:
|
|
v4l2_device_unregister(&isp->v4l2_dev);
|
|
err_media_dev_cleanup:
|
|
media_device_cleanup(media_dev);
|
|
return ret;
|
|
}
|
|
|
|
static void c4_isp_media_unregister(struct c4_isp_device *isp)
|
|
{
|
|
media_device_unregister(&isp->media_dev);
|
|
v4l2_device_unregister(&isp->v4l2_dev);
|
|
media_device_cleanup(&isp->media_dev);
|
|
}
|
|
|
|
static void c4_isp_remove_links(struct c4_isp_device *isp)
|
|
{
|
|
unsigned int i;
|
|
|
|
media_entity_remove_links(&isp->core.sd.entity);
|
|
|
|
for (i = C4_ISP_RSZ_0; i < C4_ISP_RSZ_MAX; i++)
|
|
media_entity_remove_links(&isp->resizers[i].sd.entity);
|
|
|
|
for (i = C4_ISP_RSZ_0; i < C4_ISP_NUM_CAP_DEVS; i++)
|
|
media_entity_remove_links(&isp->caps[i].vdev.entity);
|
|
}
|
|
|
|
static int c4_isp_create_links(struct c4_isp_device *isp)
|
|
{
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
for (i = C4_ISP_RSZ_0; i < C4_ISP_RSZ_MAX; i++) {
|
|
ret = media_create_pad_link(&isp->resizers[i].sd.entity,
|
|
C4_ISP_RSZ_PAD_SOURCE,
|
|
&isp->caps[i].vdev.entity, 0,
|
|
MEDIA_LNK_FL_ENABLED |
|
|
MEDIA_LNK_FL_IMMUTABLE);
|
|
if (ret) {
|
|
dev_err(isp->dev,
|
|
"Failed to link rsz %u and cap %u\n", i, i);
|
|
goto err_remove_links;
|
|
}
|
|
|
|
ret = media_create_pad_link(&isp->core.sd.entity,
|
|
C4_ISP_CORE_PAD_SOURCE_VIDEO_0 + i,
|
|
&isp->resizers[i].sd.entity,
|
|
C4_ISP_RSZ_PAD_SINK,
|
|
MEDIA_LNK_FL_ENABLED);
|
|
if (ret) {
|
|
dev_err(isp->dev,
|
|
"Failed to link core and rsz %u\n", i);
|
|
goto err_remove_links;
|
|
}
|
|
}
|
|
|
|
ret = media_create_pad_link(&isp->core.sd.entity,
|
|
C4_ISP_CORE_PAD_SOURCE_RAW,
|
|
&isp->raw.vdev.entity,
|
|
0, MEDIA_LNK_FL_ENABLED);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to link core and raw\n");
|
|
goto err_remove_links;
|
|
}
|
|
|
|
ret = media_create_pad_link(&isp->core.sd.entity,
|
|
C4_ISP_CORE_PAD_SOURCE_STATS,
|
|
&isp->stats.vdev.entity,
|
|
0, MEDIA_LNK_FL_ENABLED);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to link core and stats\n");
|
|
goto err_remove_links;
|
|
}
|
|
|
|
ret = media_create_pad_link(&isp->params.vdev.entity, 0,
|
|
&isp->core.sd.entity,
|
|
C4_ISP_CORE_PAD_SINK_PARAMS,
|
|
MEDIA_LNK_FL_ENABLED);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to link params and core\n");
|
|
goto err_remove_links;
|
|
}
|
|
|
|
ret = media_create_pad_link(&isp->ipp.sd.entity,
|
|
C4_IPP_PAD_SOURCE_VIDEO_ISP,
|
|
&isp->core.sd.entity,
|
|
C4_ISP_CORE_PAD_SINK_VIDEO, 0);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to link ipp and core\n");
|
|
goto err_remove_links;
|
|
}
|
|
|
|
ret = media_create_pad_link(&isp->imp.vdev.entity, 0,
|
|
&isp->core.sd.entity,
|
|
C4_ISP_CORE_PAD_SINK_VIDEO, 0);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to link imp and core\n");
|
|
goto err_remove_links;
|
|
}
|
|
|
|
for (i = C4_IPP_CAP_DEV_0; i < C4_IPP_NUM_CAP_DEVS; i++) {
|
|
ret = media_create_pad_link(&isp->ipp.sd.entity,
|
|
C4_IPP_PAD_SOURCE_VIDEO_0 + i,
|
|
&isp->ipp_caps[i].vdev.entity,
|
|
0, MEDIA_LNK_FL_ENABLED);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to link ipp and ipp_cap: %u\n", i);
|
|
goto err_remove_links;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_remove_links:
|
|
c4_isp_remove_links(isp);
|
|
return ret;
|
|
}
|
|
|
|
static int c4_isp_videos_register(struct c4_isp_device *isp)
|
|
{
|
|
int ret;
|
|
|
|
ret = c4_isp_captures_register(isp);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = c4_isp_stats_register(isp);
|
|
if (ret)
|
|
goto err_captures_unregister;
|
|
|
|
ret = c4_isp_params_register(isp);
|
|
if (ret)
|
|
goto err_stats_unregister;
|
|
|
|
ret = c4_isp_raw_register(isp);
|
|
if (ret)
|
|
goto err_params_unregister;
|
|
|
|
ret = c4_isp_imp_register(isp);
|
|
if (ret)
|
|
goto err_raw_unregister;
|
|
|
|
ret = c4_ipp_caps_register(isp);
|
|
if (ret)
|
|
goto err_imp_unregister;
|
|
|
|
ret = c4_isp_create_links(isp);
|
|
if (ret)
|
|
goto err_ipp_caps_unregister;
|
|
|
|
return 0;
|
|
|
|
err_ipp_caps_unregister:
|
|
c4_ipp_caps_unregister(isp);
|
|
err_imp_unregister:
|
|
c4_isp_imp_unregister(isp);
|
|
err_raw_unregister:
|
|
c4_isp_raw_unregister(isp);
|
|
err_params_unregister:
|
|
c4_isp_params_unregister(isp);
|
|
err_stats_unregister:
|
|
c4_isp_stats_unregister(isp);
|
|
err_captures_unregister:
|
|
c4_isp_captures_unregister(isp);
|
|
return ret;
|
|
}
|
|
|
|
static void c4_isp_videos_unregister(struct c4_isp_device *isp)
|
|
{
|
|
c4_isp_remove_links(isp);
|
|
c4_ipp_caps_unregister(isp);
|
|
c4_isp_imp_unregister(isp);
|
|
c4_isp_raw_unregister(isp);
|
|
c4_isp_params_unregister(isp);
|
|
c4_isp_stats_unregister(isp);
|
|
c4_isp_captures_unregister(isp);
|
|
}
|
|
|
|
static void __iomem *c4_isp_ioremap_byname(struct c4_isp_device *isp, char *name)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(isp->hw_dev);
|
|
struct resource *res;
|
|
void __iomem *mem;
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
|
|
if (!res) {
|
|
dev_err(isp->dev, "Failed to get %s io resource\n", name);
|
|
return NULL;
|
|
}
|
|
|
|
mem = devm_ioremap(isp->dev, res->start, resource_size(res));
|
|
if (!mem) {
|
|
dev_err(isp->dev, "Failed to ioremap %s resource\n", name);
|
|
return NULL;
|
|
}
|
|
|
|
return mem;
|
|
}
|
|
|
|
static int c4_isp_ioremap_resource(struct c4_isp_device *isp)
|
|
{
|
|
isp->base = c4_isp_ioremap_byname(isp, "base");
|
|
if (!isp->base)
|
|
return -ENOMEM;
|
|
|
|
isp->disp = c4_isp_ioremap_byname(isp, "disp");
|
|
if (!isp->disp)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int c4_isp_get_hw_dev(struct c4_isp_device *isp)
|
|
{
|
|
struct device_node *isp_node;
|
|
struct platform_device *isp_pdev;
|
|
|
|
isp_node = of_parse_phandle(isp->dev->of_node, "isp", 0);
|
|
if (!isp_node)
|
|
return -EINVAL;
|
|
|
|
isp_pdev = of_find_device_by_node(isp_node);
|
|
of_node_put(isp_node);
|
|
if (!isp_pdev) {
|
|
dev_err(isp->dev, "Failed to find isp pdev");
|
|
return -EINVAL;
|
|
}
|
|
|
|
isp->hw_dev = &isp_pdev->dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int c4_isp_request_irq(struct c4_isp_device *isp)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(isp->hw_dev);
|
|
int irq;
|
|
int ret;
|
|
|
|
irq = platform_get_irq_byname(pdev, "fe_rst");
|
|
if (irq < 0) {
|
|
dev_err(isp->dev, "Failed to get fe_rst irq");
|
|
return irq;
|
|
}
|
|
|
|
ret = devm_request_irq(isp->dev, irq, c4_isp_fe_rst_irq_handler,
|
|
IRQF_SHARED, dev_driver_string(isp->dev), isp);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to request fe_rst irq\n");
|
|
return ret;
|
|
}
|
|
|
|
irq = platform_get_irq_byname(pdev, "fe_done");
|
|
if (irq < 0) {
|
|
dev_err(isp->dev, "Failed to get fe_done irq");
|
|
return irq;
|
|
}
|
|
|
|
ret = devm_request_irq(isp->dev, irq, c4_isp_fe_done_irq_handler,
|
|
IRQF_SHARED, dev_driver_string(isp->dev), isp);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to request fe_rst irq\n");
|
|
return ret;
|
|
}
|
|
|
|
irq = platform_get_irq_byname(pdev, "be_done");
|
|
if (irq < 0) {
|
|
dev_err(isp->dev, "Failed to get be_done irq");
|
|
return irq;
|
|
}
|
|
|
|
ret = devm_request_irq(isp->dev, irq, c4_isp_be_done_irq_handler,
|
|
IRQF_SHARED, dev_driver_string(isp->dev), isp);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to request be_done irq\n");
|
|
return ret;
|
|
}
|
|
|
|
irq = platform_get_irq_byname(pdev, "intf");
|
|
if (irq < 0) {
|
|
dev_err(isp->dev, "Failed to get intf_top irq");
|
|
return irq;
|
|
}
|
|
|
|
ret = devm_request_irq(isp->dev, irq, c4_isp_intf_irq_handler,
|
|
IRQF_SHARED, dev_driver_string(isp->dev), isp);
|
|
if (ret) {
|
|
dev_err(isp->dev, "Failed to request intf irq\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int c4_isp_get_clocks(struct c4_isp_device *isp)
|
|
{
|
|
const struct c4_isp_match_info *info = isp->info;
|
|
|
|
for (unsigned int i = 0; i < info->clock_num; i++)
|
|
isp->clks[i].id = info->clocks[i];
|
|
|
|
return devm_clk_bulk_get(isp->dev, info->clock_num, isp->clks);
|
|
}
|
|
|
|
static int c4_isp_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct c4_isp_device *isp;
|
|
int ret;
|
|
|
|
isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
|
|
if (!isp)
|
|
return -ENOMEM;
|
|
|
|
isp->info = of_device_get_match_data(dev);
|
|
isp->dev = dev;
|
|
|
|
ret = c4_isp_get_hw_dev(isp);
|
|
if (ret)
|
|
return ret;
|
|
|
|
isp->isp_pd = dev_pm_domain_attach_by_name(dev, "isp-pd");
|
|
if (IS_ERR_OR_NULL(isp->isp_pd)) {
|
|
ret = IS_ERR(isp->isp_pd) ? PTR_ERR(isp->isp_pd) : -ENODEV;
|
|
return dev_err_probe(dev, ret, "Failed to attach isp-pd\n");
|
|
}
|
|
|
|
isp->opp_pd = dev_pm_domain_attach_by_name(dev, "opp-pd");
|
|
if (IS_ERR_OR_NULL(isp->opp_pd)) {
|
|
ret = IS_ERR(isp->opp_pd) ? PTR_ERR(isp->opp_pd) : -ENODEV;
|
|
return dev_err_probe(dev, ret, "Failed to attach opp-pd\n");
|
|
}
|
|
|
|
ret = c4_isp_ioremap_resource(isp);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = c4_isp_request_irq(isp);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = c4_isp_get_clocks(isp);
|
|
if (ret)
|
|
return dev_err_probe(dev, ret, "Failed to get clocks\n");
|
|
|
|
platform_set_drvdata(pdev, isp);
|
|
|
|
pm_runtime_enable(dev);
|
|
|
|
ret = c4_isp_media_register(isp);
|
|
if (ret)
|
|
goto err_runtime_disable;
|
|
|
|
ret = c4_ipp_register(isp);
|
|
if (ret)
|
|
goto err_media_unregister;
|
|
|
|
ret = c4_isp_core_register(isp);
|
|
if (ret)
|
|
goto err_ipp_unregister;
|
|
|
|
ret = c4_isp_resizers_register(isp);
|
|
if (ret)
|
|
goto err_core_unregister;
|
|
|
|
ret = c4_isp_async_nf_register(isp);
|
|
if (ret)
|
|
goto err_resizers_unregister;
|
|
|
|
ret = c4_isp_videos_register(isp);
|
|
if (ret)
|
|
goto err_nf_unregister;
|
|
|
|
of_reserved_mem_device_init(isp->dev);
|
|
|
|
c4_isp_cfg_plat_setting(isp);
|
|
|
|
ret = c4_isp_rdma_alloc_buffers(isp);
|
|
if (ret)
|
|
goto err_videos_unregister;
|
|
|
|
ret = c4_isp_proc_init(isp);
|
|
if (ret)
|
|
goto err_videos_unregister;
|
|
|
|
dev_info(dev, "isp_probe okk rdma_alloc_en %d rdma_en %d\n",
|
|
isp->rdma_alloc_en, isp->rdma_en);
|
|
|
|
return 0;
|
|
|
|
err_videos_unregister:
|
|
c4_isp_videos_unregister(isp);
|
|
err_nf_unregister:
|
|
c4_isp_async_nf_unregister(isp);
|
|
err_resizers_unregister:
|
|
c4_isp_resizers_unregister(isp);
|
|
err_core_unregister:
|
|
c4_isp_core_unregister(isp);
|
|
err_ipp_unregister:
|
|
c4_ipp_unregister(isp);
|
|
err_media_unregister:
|
|
c4_isp_media_unregister(isp);
|
|
err_runtime_disable:
|
|
pm_runtime_disable(dev);
|
|
return ret;
|
|
};
|
|
|
|
static void c4_isp_remove(struct platform_device *pdev)
|
|
{
|
|
struct c4_isp_device *isp = platform_get_drvdata(pdev);
|
|
|
|
c4_isp_proc_remove(isp);
|
|
c4_isp_rdma_free_buffers(isp);
|
|
of_reserved_mem_device_release(isp->dev);
|
|
c4_isp_videos_unregister(isp);
|
|
c4_isp_async_nf_unregister(isp);
|
|
c4_isp_core_unregister(isp);
|
|
c4_isp_resizers_unregister(isp);
|
|
c4_ipp_unregister(isp);
|
|
c4_isp_media_unregister(isp);
|
|
pm_runtime_disable(isp->dev);
|
|
dev_pm_domain_detach(isp->isp_pd, true);
|
|
dev_pm_domain_detach(isp->opp_pd, true);
|
|
};
|
|
|
|
static const struct c4_isp_match_info isp_info_0 = {
|
|
.id = C4_ISP_VIRT_0,
|
|
.clocks = {"dewarp", "isp"},
|
|
.clock_num = 2,
|
|
};
|
|
|
|
static const struct c4_isp_match_info isp_info_1 = {
|
|
.id = C4_ISP_VIRT_1,
|
|
.clocks = {"dewarp", "isp"},
|
|
.clock_num = 2,
|
|
};
|
|
|
|
static const struct c4_isp_match_info isp_info_2 = {
|
|
.id = C4_ISP_VIRT_2,
|
|
.clocks = {"dewarp", "isp"},
|
|
.clock_num = 2,
|
|
};
|
|
|
|
static const struct c4_isp_match_info isp_info_3 = {
|
|
.id = C4_ISP_VIRT_3,
|
|
.clocks = {"dewarp", "isp"},
|
|
.clock_num = 2,
|
|
};
|
|
|
|
static const struct of_device_id c4_isp_of_match[] = {
|
|
{
|
|
.compatible = "amlogic,isp-virt0",
|
|
.data = &isp_info_0,
|
|
}, {
|
|
.compatible = "amlogic,isp-virt1",
|
|
.data = &isp_info_1,
|
|
}, {
|
|
.compatible = "amlogic,isp-virt2",
|
|
.data = &isp_info_2,
|
|
}, {
|
|
.compatible = "amlogic,isp-virt3",
|
|
.data = &isp_info_3,
|
|
},
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(of, c4_isp_of_match);
|
|
|
|
static struct platform_driver c4_isp_driver = {
|
|
.probe = c4_isp_probe,
|
|
.remove = c4_isp_remove,
|
|
.driver = {
|
|
.name = "c4-isp",
|
|
.of_match_table = c4_isp_of_match,
|
|
.pm = pm_ptr(&c4_isp_pm_ops),
|
|
},
|
|
};
|
|
|
|
module_platform_driver(c4_isp_driver);
|
|
|
|
MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>");
|
|
MODULE_DESCRIPTION("Amlogic C4 ISP5 pipeline");
|
|
MODULE_LICENSE("GPL");
|