media: camss: Refactor VFE HW version support

In order to support Qualcomm ISP hardware architectures that diverge
from older architectures, the VFE subdevice driver needs to be refactored
to better abstract the different ISP architectures.

Gen1 represents the CAMSS ISP architecture. The ISP architecture developed
after CAMSS, Titan, will be referred to as Gen2.

Signed-off-by: Robert Foss <robert.foss@linaro.org>
Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
Robert Foss 2021-03-16 18:19:15 +01:00 committed by Mauro Carvalho Chehab
parent 9d95baf9fc
commit 633b388f85
9 changed files with 2249 additions and 1059 deletions

View File

@ -10,6 +10,8 @@ qcom-camss-objs += \
camss-ispif.o \
camss-vfe-4-1.o \
camss-vfe-4-7.o \
camss-vfe-4-8.o \
camss-vfe-gen1.o \
camss-vfe.o \
camss-video.o \

View File

@ -14,6 +14,7 @@
#include "camss.h"
#include "camss-vfe.h"
#include "camss-vfe-gen1.h"
#define VFE_0_HW_VERSION 0x000
@ -284,30 +285,6 @@ static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
}
#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
static int vfe_word_per_line(u32 format, u32 pixel_per_line)
{
int val = 0;
switch (format) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
val = CALC_WORD(pixel_per_line, 1, 8);
break;
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
val = CALC_WORD(pixel_per_line, 2, 8);
break;
}
return val;
}
static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane,
u16 *width, u16 *height, u16 *bytesperline)
{
@ -666,20 +643,6 @@ static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
}
static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
{
if (input / output >= 16)
return 0;
if (input / output >= 8)
return 1;
if (input / output >= 4)
return 2;
return 3;
}
static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
{
u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
@ -975,46 +938,62 @@ static irqreturn_t vfe_isr(int irq, void *dev)
return IRQ_HANDLED;
}
const struct vfe_hw_ops vfe_ops_4_1 = {
.hw_version_read = vfe_hw_version_read,
.get_ub_size = vfe_get_ub_size,
.global_reset = vfe_global_reset,
.halt_request = vfe_halt_request,
.halt_clear = vfe_halt_clear,
.wm_enable = vfe_wm_enable,
.wm_frame_based = vfe_wm_frame_based,
.wm_line_based = vfe_wm_line_based,
.wm_set_framedrop_period = vfe_wm_set_framedrop_period,
.wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
.wm_set_ub_cfg = vfe_wm_set_ub_cfg,
.bus_reload_wm = vfe_bus_reload_wm,
.wm_set_ping_addr = vfe_wm_set_ping_addr,
.wm_set_pong_addr = vfe_wm_set_pong_addr,
.wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
.bus_enable_wr_if = vfe_bus_enable_wr_if,
static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = {
.bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
.wm_set_subsample = vfe_wm_set_subsample,
.bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
.set_xbar_cfg = vfe_set_xbar_cfg,
.set_realign_cfg = vfe_set_realign_cfg,
.set_rdi_cid = vfe_set_rdi_cid,
.reg_update = vfe_reg_update,
.reg_update_clear = vfe_reg_update_clear,
.enable_irq_wm_line = vfe_enable_irq_wm_line,
.enable_irq_pix_line = vfe_enable_irq_pix_line,
.bus_enable_wr_if = vfe_bus_enable_wr_if,
.bus_reload_wm = vfe_bus_reload_wm,
.camif_wait_for_stop = vfe_camif_wait_for_stop,
.enable_irq_common = vfe_enable_irq_common,
.set_demux_cfg = vfe_set_demux_cfg,
.set_scale_cfg = vfe_set_scale_cfg,
.set_crop_cfg = vfe_set_crop_cfg,
.set_clamp_cfg = vfe_set_clamp_cfg,
.set_qos = vfe_set_qos,
.set_ds = vfe_set_ds,
.set_cgc_override = vfe_set_cgc_override,
.enable_irq_pix_line = vfe_enable_irq_pix_line,
.enable_irq_wm_line = vfe_enable_irq_wm_line,
.get_ub_size = vfe_get_ub_size,
.halt_clear = vfe_halt_clear,
.halt_request = vfe_halt_request,
.set_camif_cfg = vfe_set_camif_cfg,
.set_camif_cmd = vfe_set_camif_cmd,
.set_cgc_override = vfe_set_cgc_override,
.set_clamp_cfg = vfe_set_clamp_cfg,
.set_crop_cfg = vfe_set_crop_cfg,
.set_demux_cfg = vfe_set_demux_cfg,
.set_ds = vfe_set_ds,
.set_module_cfg = vfe_set_module_cfg,
.camif_wait_for_stop = vfe_camif_wait_for_stop,
.isr_read = vfe_isr_read,
.violation_read = vfe_violation_read,
.isr = vfe_isr,
.set_qos = vfe_set_qos,
.set_rdi_cid = vfe_set_rdi_cid,
.set_realign_cfg = vfe_set_realign_cfg,
.set_scale_cfg = vfe_set_scale_cfg,
.set_xbar_cfg = vfe_set_xbar_cfg,
.wm_enable = vfe_wm_enable,
.wm_frame_based = vfe_wm_frame_based,
.wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
.wm_line_based = vfe_wm_line_based,
.wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
.wm_set_framedrop_period = vfe_wm_set_framedrop_period,
.wm_set_ping_addr = vfe_wm_set_ping_addr,
.wm_set_pong_addr = vfe_wm_set_pong_addr,
.wm_set_subsample = vfe_wm_set_subsample,
.wm_set_ub_cfg = vfe_wm_set_ub_cfg,
};
static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
{
vfe->isr_ops = vfe_isr_ops_gen1;
vfe->ops_gen1 = &vfe_ops_gen1_4_1;
vfe->video_ops = vfe_video_ops_gen1;
vfe->line_num = VFE_LINE_NUM_GEN1;
}
const struct vfe_hw_ops vfe_ops_4_1 = {
.global_reset = vfe_global_reset,
.hw_version_read = vfe_hw_version_read,
.isr_read = vfe_isr_read,
.isr = vfe_isr,
.reg_update_clear = vfe_reg_update_clear,
.reg_update = vfe_reg_update,
.subdev_init = vfe_subdev_init,
.vfe_disable = vfe_gen1_disable,
.vfe_enable = vfe_gen1_enable,
.vfe_halt = vfe_gen1_halt,
.violation_read = vfe_violation_read,
};

View File

@ -14,6 +14,8 @@
#include "camss.h"
#include "camss-vfe.h"
#include "camss-vfe-gen1.h"
#define VFE_0_HW_VERSION 0x000
@ -258,7 +260,7 @@ static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev)
dev_err(dev, "VFE HW Version = 0x%08x\n", hw_version);
}
static u16 vfe47_get_ub_size(u8 vfe_id)
static u16 vfe_get_ub_size(u8 vfe_id)
{
if (vfe_id == 0)
return MSM_VFE_VFE0_UB_SIZE_RDI;
@ -296,6 +298,8 @@ static void vfe_global_reset(struct vfe_device *vfe)
VFE_0_GLOBAL_RESET_CMD_CORE;
writel_relaxed(BIT(31), vfe->base + VFE_0_IRQ_MASK_0);
/* Enforce barrier between IRQ mask setup and global reset */
wmb();
writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD);
}
@ -311,7 +315,7 @@ static void vfe_halt_clear(struct vfe_device *vfe)
writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
}
static void vfe47_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
{
if (enable)
vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
@ -460,8 +464,12 @@ static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm,
static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm)
{
/* Enforce barrier between any outstanding register write */
wmb();
writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD);
/* Use barrier to make sure bus reload is issued before anything else */
wmb();
}
@ -675,8 +683,12 @@ static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
{
vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id);
/* Enforce barrier between line update and commit */
wmb();
writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
/* Make sure register update is issued before further reg writes */
wmb();
}
@ -780,20 +792,6 @@ static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
}
static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
{
if (input / output >= 16)
return 0;
if (input / output >= 8)
return 1;
if (input / output >= 4)
return 2;
return 3;
}
static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
{
u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
@ -895,7 +893,7 @@ static void vfe_set_clamp_cfg(struct vfe_device *vfe)
writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG);
}
static void vfe47_set_qos(struct vfe_device *vfe)
static void vfe_set_qos(struct vfe_device *vfe)
{
u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG;
u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG;
@ -910,7 +908,7 @@ static void vfe47_set_qos(struct vfe_device *vfe)
writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
}
static void vfe47_set_ds(struct vfe_device *vfe)
static void vfe_set_ds(struct vfe_device *vfe)
{
u32 val = VFE_0_BUS_BDG_DS_CFG_0_CFG;
u32 val16 = VFE_0_BUS_BDG_DS_CFG_16_CFG;
@ -994,6 +992,8 @@ static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable)
cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE;
writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
/* Make sure camif command is issued written before it is changed again */
wmb();
if (enable)
@ -1036,24 +1036,7 @@ static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev)
return ret;
}
static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
{
*value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
*value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0);
writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1);
wmb();
writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
}
static void vfe_violation_read(struct vfe_device *vfe)
{
u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
pr_err_ratelimited("VFE: violation = 0x%08x\n", violation);
}
/*
* vfe_isr - VFE module interrupt handler
@ -1082,7 +1065,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK)
vfe->isr_ops.halt_ack(vfe);
for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++)
for (i = VFE_LINE_RDI0; i < vfe->line_num; i++)
if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
vfe->isr_ops.reg_update(vfe, i);
@ -1108,150 +1091,82 @@ static irqreturn_t vfe_isr(int irq, void *dev)
return IRQ_HANDLED;
}
const struct vfe_hw_ops vfe_ops_4_7 = {
.hw_version_read = vfe_hw_version_read,
.get_ub_size = vfe47_get_ub_size,
.global_reset = vfe_global_reset,
.halt_request = vfe_halt_request,
.halt_clear = vfe_halt_clear,
.wm_enable = vfe47_wm_enable,
.wm_frame_based = vfe_wm_frame_based,
.wm_line_based = vfe_wm_line_based,
.wm_set_framedrop_period = vfe_wm_set_framedrop_period,
.wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
.wm_set_ub_cfg = vfe_wm_set_ub_cfg,
.bus_reload_wm = vfe_bus_reload_wm,
.wm_set_ping_addr = vfe_wm_set_ping_addr,
.wm_set_pong_addr = vfe_wm_set_pong_addr,
.wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
.bus_enable_wr_if = vfe_bus_enable_wr_if,
.bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
.wm_set_subsample = vfe_wm_set_subsample,
.bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
.set_xbar_cfg = vfe_set_xbar_cfg,
.set_realign_cfg = vfe_set_realign_cfg,
.set_rdi_cid = vfe_set_rdi_cid,
.reg_update = vfe_reg_update,
.reg_update_clear = vfe_reg_update_clear,
.enable_irq_wm_line = vfe_enable_irq_wm_line,
.enable_irq_pix_line = vfe_enable_irq_pix_line,
.enable_irq_common = vfe_enable_irq_common,
.set_demux_cfg = vfe_set_demux_cfg,
.set_scale_cfg = vfe_set_scale_cfg,
.set_crop_cfg = vfe_set_crop_cfg,
.set_clamp_cfg = vfe_set_clamp_cfg,
.set_qos = vfe47_set_qos,
.set_ds = vfe47_set_ds,
.set_cgc_override = vfe_set_cgc_override,
.set_camif_cfg = vfe_set_camif_cfg,
.set_camif_cmd = vfe_set_camif_cmd,
.set_module_cfg = vfe_set_module_cfg,
.camif_wait_for_stop = vfe_camif_wait_for_stop,
.isr_read = vfe_isr_read,
.violation_read = vfe_violation_read,
.isr = vfe_isr,
};
static u16 vfe48_get_ub_size(u8 vfe_id)
static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
{
/* On VFE4.8 the ub-size is the same on both instances */
return MSM_VFE_VFE0_UB_SIZE_RDI;
}
*value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
*value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
static void vfe48_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
{
if (enable)
writel_relaxed(2 << VFE48_0_BUS_IMAGE_MASTER_n_SHIFT(wm),
vfe->base + VFE48_0_BUS_IMAGE_MASTER_CMD);
else
writel_relaxed(1 << VFE48_0_BUS_IMAGE_MASTER_n_SHIFT(wm),
vfe->base + VFE48_0_BUS_IMAGE_MASTER_CMD);
writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0);
writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1);
/* The WM must be enabled before sending other commands */
/* Enforce barrier between local & global IRQ clear */
wmb();
writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
}
static void vfe48_set_qos(struct vfe_device *vfe)
static void vfe_violation_read(struct vfe_device *vfe)
{
u32 val = VFE48_0_BUS_BDG_QOS_CFG_0_CFG;
u32 val3 = VFE48_0_BUS_BDG_QOS_CFG_3_CFG;
u32 val4 = VFE48_0_BUS_BDG_QOS_CFG_4_CFG;
u32 val7 = VFE48_0_BUS_BDG_QOS_CFG_7_CFG;
u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
writel_relaxed(val3, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
pr_err_ratelimited("VFE: violation = 0x%08x\n", violation);
}
static void vfe48_set_ds(struct vfe_device *vfe)
{
u32 val = VFE48_0_BUS_BDG_DS_CFG_0_CFG;
u32 val16 = VFE48_0_BUS_BDG_DS_CFG_16_CFG;
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_0);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_1);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_2);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_3);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_4);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_5);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_6);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_7);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_8);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_9);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_10);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_11);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_12);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_13);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_14);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_15);
writel_relaxed(val16, vfe->base + VFE_0_BUS_BDG_DS_CFG_16);
}
const struct vfe_hw_ops vfe_ops_4_8 = {
.hw_version_read = vfe_hw_version_read,
.get_ub_size = vfe48_get_ub_size,
.global_reset = vfe_global_reset,
.halt_request = vfe_halt_request,
.halt_clear = vfe_halt_clear,
.wm_enable = vfe48_wm_enable,
.wm_frame_based = vfe_wm_frame_based,
.wm_line_based = vfe_wm_line_based,
.wm_set_framedrop_period = vfe_wm_set_framedrop_period,
.wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
.wm_set_ub_cfg = vfe_wm_set_ub_cfg,
.bus_reload_wm = vfe_bus_reload_wm,
.wm_set_ping_addr = vfe_wm_set_ping_addr,
.wm_set_pong_addr = vfe_wm_set_pong_addr,
.wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
.bus_enable_wr_if = vfe_bus_enable_wr_if,
static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_7 = {
.bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
.wm_set_subsample = vfe_wm_set_subsample,
.bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
.set_xbar_cfg = vfe_set_xbar_cfg,
.set_realign_cfg = vfe_set_realign_cfg,
.set_rdi_cid = vfe_set_rdi_cid,
.reg_update = vfe_reg_update,
.reg_update_clear = vfe_reg_update_clear,
.enable_irq_wm_line = vfe_enable_irq_wm_line,
.enable_irq_pix_line = vfe_enable_irq_pix_line,
.bus_enable_wr_if = vfe_bus_enable_wr_if,
.bus_reload_wm = vfe_bus_reload_wm,
.camif_wait_for_stop = vfe_camif_wait_for_stop,
.enable_irq_common = vfe_enable_irq_common,
.set_demux_cfg = vfe_set_demux_cfg,
.set_scale_cfg = vfe_set_scale_cfg,
.set_crop_cfg = vfe_set_crop_cfg,
.set_clamp_cfg = vfe_set_clamp_cfg,
.set_qos = vfe48_set_qos,
.set_ds = vfe48_set_ds,
.set_cgc_override = vfe_set_cgc_override,
.enable_irq_pix_line = vfe_enable_irq_pix_line,
.enable_irq_wm_line = vfe_enable_irq_wm_line,
.get_ub_size = vfe_get_ub_size,
.halt_clear = vfe_halt_clear,
.halt_request = vfe_halt_request,
.set_camif_cfg = vfe_set_camif_cfg,
.set_camif_cmd = vfe_set_camif_cmd,
.set_cgc_override = vfe_set_cgc_override,
.set_clamp_cfg = vfe_set_clamp_cfg,
.set_crop_cfg = vfe_set_crop_cfg,
.set_demux_cfg = vfe_set_demux_cfg,
.set_ds = vfe_set_ds,
.set_module_cfg = vfe_set_module_cfg,
.camif_wait_for_stop = vfe_camif_wait_for_stop,
.isr_read = vfe_isr_read,
.violation_read = vfe_violation_read,
.isr = vfe_isr,
.set_qos = vfe_set_qos,
.set_rdi_cid = vfe_set_rdi_cid,
.set_realign_cfg = vfe_set_realign_cfg,
.set_scale_cfg = vfe_set_scale_cfg,
.set_xbar_cfg = vfe_set_xbar_cfg,
.wm_enable = vfe_wm_enable,
.wm_frame_based = vfe_wm_frame_based,
.wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
.wm_line_based = vfe_wm_line_based,
.wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
.wm_set_framedrop_period = vfe_wm_set_framedrop_period,
.wm_set_ping_addr = vfe_wm_set_ping_addr,
.wm_set_pong_addr = vfe_wm_set_pong_addr,
.wm_set_subsample = vfe_wm_set_subsample,
.wm_set_ub_cfg = vfe_wm_set_ub_cfg,
};
static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
{
vfe->isr_ops = vfe_isr_ops_gen1;
vfe->ops_gen1 = &vfe_ops_gen1_4_7;
vfe->video_ops = vfe_video_ops_gen1;
vfe->line_num = VFE_LINE_NUM_GEN1;
}
const struct vfe_hw_ops vfe_ops_4_7 = {
.global_reset = vfe_global_reset,
.hw_version_read = vfe_hw_version_read,
.isr_read = vfe_isr_read,
.isr = vfe_isr,
.reg_update_clear = vfe_reg_update_clear,
.reg_update = vfe_reg_update,
.subdev_init = vfe_subdev_init,
.vfe_disable = vfe_gen1_disable,
.vfe_enable = vfe_gen1_enable,
.vfe_halt = vfe_gen1_halt,
.violation_read = vfe_violation_read,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,742 @@
// SPDX-License-Identifier: GPL-2.0
/*
* camss-vfe-gen1.c
*
* Qualcomm MSM Camera Subsystem - VFE Common functionality for Gen 1 versions of hw (4.1, 4.7..)
*
* Copyright (C) 2020 Linaro Ltd.
*/
#include "camss.h"
#include "camss-vfe.h"
#include "camss-vfe-gen1.h"
/* Max number of frame drop updates per frame */
#define VFE_FRAME_DROP_UPDATES 2
#define VFE_NEXT_SOF_MS 500
int vfe_gen1_halt(struct vfe_device *vfe)
{
unsigned long time;
reinit_completion(&vfe->halt_complete);
vfe->ops_gen1->halt_request(vfe);
time = wait_for_completion_timeout(&vfe->halt_complete,
msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
if (!time) {
dev_err(vfe->camss->dev, "VFE halt timeout\n");
return -EIO;
}
return 0;
}
static int vfe_disable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
const struct vfe_hw_ops *ops = vfe->ops;
unsigned long flags;
unsigned long time;
unsigned int i;
spin_lock_irqsave(&vfe->output_lock, flags);
output->gen1.wait_sof = 1;
spin_unlock_irqrestore(&vfe->output_lock, flags);
time = wait_for_completion_timeout(&output->sof, msecs_to_jiffies(VFE_NEXT_SOF_MS));
if (!time)
dev_err(vfe->camss->dev, "VFE sof timeout\n");
spin_lock_irqsave(&vfe->output_lock, flags);
for (i = 0; i < output->wm_num; i++)
vfe->ops_gen1->wm_enable(vfe, output->wm_idx[i], 0);
ops->reg_update(vfe, line->id);
output->wait_reg_update = 1;
spin_unlock_irqrestore(&vfe->output_lock, flags);
time = wait_for_completion_timeout(&output->reg_update, msecs_to_jiffies(VFE_NEXT_SOF_MS));
if (!time)
dev_err(vfe->camss->dev, "VFE reg update timeout\n");
spin_lock_irqsave(&vfe->output_lock, flags);
if (line->id != VFE_LINE_PIX) {
vfe->ops_gen1->wm_frame_based(vfe, output->wm_idx[0], 0);
vfe->ops_gen1->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id);
vfe->ops_gen1->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0);
vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[0], 0);
spin_unlock_irqrestore(&vfe->output_lock, flags);
} else {
for (i = 0; i < output->wm_num; i++) {
vfe->ops_gen1->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[i], 0);
}
vfe->ops_gen1->enable_irq_pix_line(vfe, 0, line->id, 0);
vfe->ops_gen1->set_module_cfg(vfe, 0);
vfe->ops_gen1->set_realign_cfg(vfe, line, 0);
vfe->ops_gen1->set_xbar_cfg(vfe, output, 0);
vfe->ops_gen1->set_camif_cmd(vfe, 0);
spin_unlock_irqrestore(&vfe->output_lock, flags);
vfe->ops_gen1->camif_wait_for_stop(vfe, vfe->camss->dev);
}
return 0;
}
/*
* vfe_gen1_disable - Disable streaming on VFE line
* @line: VFE line
*
* Return 0 on success or a negative error code otherwise
*/
int vfe_gen1_disable(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
vfe_disable_output(line);
vfe_put_output(line);
mutex_lock(&vfe->stream_lock);
if (vfe->stream_count == 1)
vfe->ops_gen1->bus_enable_wr_if(vfe, 0);
vfe->stream_count--;
mutex_unlock(&vfe->stream_lock);
return 0;
}
static void vfe_output_init_addrs(struct vfe_device *vfe,
struct vfe_output *output, u8 sync,
struct vfe_line *line)
{
u32 ping_addr;
u32 pong_addr;
unsigned int i;
output->gen1.active_buf = 0;
for (i = 0; i < output->wm_num; i++) {
if (output->buf[0])
ping_addr = output->buf[0]->addr[i];
else
ping_addr = 0;
if (output->buf[1])
pong_addr = output->buf[1]->addr[i];
else
pong_addr = ping_addr;
vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr);
vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr);
if (sync)
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
}
}
static void vfe_output_frame_drop(struct vfe_device *vfe,
struct vfe_output *output,
u32 drop_pattern)
{
u8 drop_period;
unsigned int i;
/* We need to toggle update period to be valid on next frame */
output->drop_update_idx++;
output->drop_update_idx %= VFE_FRAME_DROP_UPDATES;
drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
for (i = 0; i < output->wm_num; i++) {
vfe->ops_gen1->wm_set_framedrop_period(vfe, output->wm_idx[i], drop_period);
vfe->ops_gen1->wm_set_framedrop_pattern(vfe, output->wm_idx[i], drop_pattern);
}
vfe->ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id);
}
static int vfe_enable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
const struct vfe_hw_ops *ops = vfe->ops;
struct media_entity *sensor;
unsigned long flags;
unsigned int frame_skip = 0;
unsigned int i;
u16 ub_size;
ub_size = vfe->ops_gen1->get_ub_size(vfe->id);
if (!ub_size)
return -EINVAL;
sensor = camss_find_sensor(&line->subdev.entity);
if (sensor) {
struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor);
v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
/* Max frame skip is 29 frames */
if (frame_skip > VFE_FRAME_DROP_VAL - 1)
frame_skip = VFE_FRAME_DROP_VAL - 1;
}
spin_lock_irqsave(&vfe->output_lock, flags);
ops->reg_update_clear(vfe, line->id);
if (output->state != VFE_OUTPUT_RESERVED) {
dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", output->state);
spin_unlock_irqrestore(&vfe->output_lock, flags);
return -EINVAL;
}
output->state = VFE_OUTPUT_IDLE;
output->buf[0] = vfe_buf_get_pending(output);
output->buf[1] = vfe_buf_get_pending(output);
if (!output->buf[0] && output->buf[1]) {
output->buf[0] = output->buf[1];
output->buf[1] = NULL;
}
if (output->buf[0])
output->state = VFE_OUTPUT_SINGLE;
if (output->buf[1])
output->state = VFE_OUTPUT_CONTINUOUS;
switch (output->state) {
case VFE_OUTPUT_SINGLE:
vfe_output_frame_drop(vfe, output, 1 << frame_skip);
break;
case VFE_OUTPUT_CONTINUOUS:
vfe_output_frame_drop(vfe, output, 3 << frame_skip);
break;
default:
vfe_output_frame_drop(vfe, output, 0);
break;
}
output->sequence = 0;
output->gen1.wait_sof = 0;
output->wait_reg_update = 0;
reinit_completion(&output->sof);
reinit_completion(&output->reg_update);
vfe_output_init_addrs(vfe, output, 0, line);
if (line->id != VFE_LINE_PIX) {
vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[0], 1);
vfe->ops_gen1->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1);
vfe->ops_gen1->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id);
vfe->ops_gen1->wm_set_subsample(vfe, output->wm_idx[0]);
vfe->ops_gen1->set_rdi_cid(vfe, line->id, 0);
vfe->ops_gen1->wm_set_ub_cfg(vfe, output->wm_idx[0],
(ub_size + 1) * output->wm_idx[0], ub_size);
vfe->ops_gen1->wm_frame_based(vfe, output->wm_idx[0], 1);
vfe->ops_gen1->wm_enable(vfe, output->wm_idx[0], 1);
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[0]);
} else {
ub_size /= output->wm_num;
for (i = 0; i < output->wm_num; i++) {
vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[i], 1);
vfe->ops_gen1->wm_set_subsample(vfe, output->wm_idx[i]);
vfe->ops_gen1->wm_set_ub_cfg(vfe, output->wm_idx[i],
(ub_size + 1) * output->wm_idx[i], ub_size);
vfe->ops_gen1->wm_line_based(vfe, output->wm_idx[i],
&line->video_out.active_fmt.fmt.pix_mp, i, 1);
vfe->ops_gen1->wm_enable(vfe, output->wm_idx[i], 1);
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
}
vfe->ops_gen1->enable_irq_pix_line(vfe, 0, line->id, 1);
vfe->ops_gen1->set_module_cfg(vfe, 1);
vfe->ops_gen1->set_camif_cfg(vfe, line);
vfe->ops_gen1->set_realign_cfg(vfe, line, 1);
vfe->ops_gen1->set_xbar_cfg(vfe, output, 1);
vfe->ops_gen1->set_demux_cfg(vfe, line);
vfe->ops_gen1->set_scale_cfg(vfe, line);
vfe->ops_gen1->set_crop_cfg(vfe, line);
vfe->ops_gen1->set_clamp_cfg(vfe);
vfe->ops_gen1->set_camif_cmd(vfe, 1);
}
ops->reg_update(vfe, line->id);
spin_unlock_irqrestore(&vfe->output_lock, flags);
return 0;
}
static int vfe_get_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output;
struct v4l2_format *f = &line->video_out.active_fmt;
unsigned long flags;
int i;
int wm_idx;
spin_lock_irqsave(&vfe->output_lock, flags);
output = &line->output;
if (output->state != VFE_OUTPUT_OFF) {
dev_err(vfe->camss->dev, "Output is running\n");
goto error;
}
output->state = VFE_OUTPUT_RESERVED;
output->gen1.active_buf = 0;
switch (f->fmt.pix_mp.pixelformat) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
output->wm_num = 2;
break;
default:
output->wm_num = 1;
break;
}
for (i = 0; i < output->wm_num; i++) {
wm_idx = vfe_reserve_wm(vfe, line->id);
if (wm_idx < 0) {
dev_err(vfe->camss->dev, "Can not reserve wm\n");
goto error_get_wm;
}
output->wm_idx[i] = wm_idx;
}
output->drop_update_idx = 0;
spin_unlock_irqrestore(&vfe->output_lock, flags);
return 0;
error_get_wm:
for (i--; i >= 0; i--)
vfe_release_wm(vfe, output->wm_idx[i]);
output->state = VFE_OUTPUT_OFF;
error:
spin_unlock_irqrestore(&vfe->output_lock, flags);
return -EINVAL;
}
int vfe_gen1_enable(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
int ret;
mutex_lock(&vfe->stream_lock);
if (!vfe->stream_count) {
vfe->ops_gen1->enable_irq_common(vfe);
vfe->ops_gen1->bus_enable_wr_if(vfe, 1);
vfe->ops_gen1->set_qos(vfe);
vfe->ops_gen1->set_ds(vfe);
}
vfe->stream_count++;
mutex_unlock(&vfe->stream_lock);
ret = vfe_get_output(line);
if (ret < 0)
goto error_get_output;
ret = vfe_enable_output(line);
if (ret < 0)
goto error_enable_output;
vfe->was_streaming = 1;
return 0;
error_enable_output:
vfe_put_output(line);
error_get_output:
mutex_lock(&vfe->stream_lock);
if (vfe->stream_count == 1)
vfe->ops_gen1->bus_enable_wr_if(vfe, 0);
vfe->stream_count--;
mutex_unlock(&vfe->stream_lock);
return ret;
}
static void vfe_output_update_ping_addr(struct vfe_device *vfe,
struct vfe_output *output, u8 sync,
struct vfe_line *line)
{
u32 addr;
unsigned int i;
for (i = 0; i < output->wm_num; i++) {
if (output->buf[0])
addr = output->buf[0]->addr[i];
else
addr = 0;
vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], addr);
if (sync)
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
}
}
static void vfe_output_update_pong_addr(struct vfe_device *vfe,
struct vfe_output *output, u8 sync,
struct vfe_line *line)
{
u32 addr;
unsigned int i;
for (i = 0; i < output->wm_num; i++) {
if (output->buf[1])
addr = output->buf[1]->addr[i];
else
addr = 0;
vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], addr);
if (sync)
vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]);
}
}
static void vfe_buf_update_wm_on_next(struct vfe_device *vfe,
struct vfe_output *output)
{
switch (output->state) {
case VFE_OUTPUT_CONTINUOUS:
vfe_output_frame_drop(vfe, output, 3);
break;
case VFE_OUTPUT_SINGLE:
default:
dev_err_ratelimited(vfe->camss->dev,
"Next buf in wrong state! %d\n",
output->state);
break;
}
}
static void vfe_buf_update_wm_on_last(struct vfe_device *vfe,
struct vfe_output *output)
{
switch (output->state) {
case VFE_OUTPUT_CONTINUOUS:
output->state = VFE_OUTPUT_SINGLE;
vfe_output_frame_drop(vfe, output, 1);
break;
case VFE_OUTPUT_SINGLE:
output->state = VFE_OUTPUT_STOPPING;
vfe_output_frame_drop(vfe, output, 0);
break;
default:
dev_err_ratelimited(vfe->camss->dev,
"Last buff in wrong state! %d\n",
output->state);
break;
}
}
static void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
struct vfe_output *output,
struct camss_buffer *new_buf,
struct vfe_line *line)
{
int inactive_idx;
switch (output->state) {
case VFE_OUTPUT_SINGLE:
inactive_idx = !output->gen1.active_buf;
if (!output->buf[inactive_idx]) {
output->buf[inactive_idx] = new_buf;
if (inactive_idx)
vfe_output_update_pong_addr(vfe, output, 0, line);
else
vfe_output_update_ping_addr(vfe, output, 0, line);
vfe_output_frame_drop(vfe, output, 3);
output->state = VFE_OUTPUT_CONTINUOUS;
} else {
vfe_buf_add_pending(output, new_buf);
dev_err_ratelimited(vfe->camss->dev,
"Inactive buffer is busy\n");
}
break;
case VFE_OUTPUT_IDLE:
if (!output->buf[0]) {
output->buf[0] = new_buf;
vfe_output_init_addrs(vfe, output, 1, line);
vfe_output_frame_drop(vfe, output, 1);
output->state = VFE_OUTPUT_SINGLE;
} else {
vfe_buf_add_pending(output, new_buf);
dev_err_ratelimited(vfe->camss->dev,
"Output idle with buffer set!\n");
}
break;
case VFE_OUTPUT_CONTINUOUS:
default:
vfe_buf_add_pending(output, new_buf);
break;
}
}
/*
* vfe_isr_halt_ack - Process halt ack
* @vfe: VFE Device
*/
static void vfe_isr_halt_ack(struct vfe_device *vfe)
{
complete(&vfe->halt_complete);
vfe->ops_gen1->halt_clear(vfe);
}
/*
* vfe_isr_sof - Process start of frame interrupt
* @vfe: VFE Device
* @line_id: VFE line
*/
static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id)
{
struct vfe_output *output;
unsigned long flags;
spin_lock_irqsave(&vfe->output_lock, flags);
output = &vfe->line[line_id].output;
if (output->gen1.wait_sof) {
output->gen1.wait_sof = 0;
complete(&output->sof);
}
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
/*
* vfe_isr_reg_update - Process reg update interrupt
* @vfe: VFE Device
* @line_id: VFE line
*/
static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
{
struct vfe_output *output;
struct vfe_line *line = &vfe->line[line_id];
unsigned long flags;
spin_lock_irqsave(&vfe->output_lock, flags);
vfe->ops->reg_update_clear(vfe, line_id);
output = &line->output;
if (output->wait_reg_update) {
output->wait_reg_update = 0;
complete(&output->reg_update);
spin_unlock_irqrestore(&vfe->output_lock, flags);
return;
}
if (output->state == VFE_OUTPUT_STOPPING) {
/* Release last buffer when hw is idle */
if (output->last_buffer) {
vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
VB2_BUF_STATE_DONE);
output->last_buffer = NULL;
}
output->state = VFE_OUTPUT_IDLE;
/* Buffers received in stopping state are queued in */
/* dma pending queue, start next capture here */
output->buf[0] = vfe_buf_get_pending(output);
output->buf[1] = vfe_buf_get_pending(output);
if (!output->buf[0] && output->buf[1]) {
output->buf[0] = output->buf[1];
output->buf[1] = NULL;
}
if (output->buf[0])
output->state = VFE_OUTPUT_SINGLE;
if (output->buf[1])
output->state = VFE_OUTPUT_CONTINUOUS;
switch (output->state) {
case VFE_OUTPUT_SINGLE:
vfe_output_frame_drop(vfe, output, 2);
break;
case VFE_OUTPUT_CONTINUOUS:
vfe_output_frame_drop(vfe, output, 3);
break;
default:
vfe_output_frame_drop(vfe, output, 0);
break;
}
vfe_output_init_addrs(vfe, output, 1, &vfe->line[line_id]);
}
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
/*
* vfe_isr_wm_done - Process write master done interrupt
* @vfe: VFE Device
* @wm: Write master id
*/
static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
{
struct camss_buffer *ready_buf;
struct vfe_output *output;
dma_addr_t *new_addr;
unsigned long flags;
u32 active_index;
u64 ts = ktime_get_ns();
unsigned int i;
active_index = vfe->ops_gen1->wm_get_ping_pong_status(vfe, wm);
spin_lock_irqsave(&vfe->output_lock, flags);
if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
dev_err_ratelimited(vfe->camss->dev,
"Received wm done for unmapped index\n");
goto out_unlock;
}
output = &vfe->line[vfe->wm_output_map[wm]].output;
if (output->gen1.active_buf == active_index && 0) {
dev_err_ratelimited(vfe->camss->dev,
"Active buffer mismatch!\n");
goto out_unlock;
}
output->gen1.active_buf = active_index;
ready_buf = output->buf[!active_index];
if (!ready_buf) {
dev_err_ratelimited(vfe->camss->dev,
"Missing ready buf %d %d!\n",
!active_index, output->state);
goto out_unlock;
}
ready_buf->vb.vb2_buf.timestamp = ts;
ready_buf->vb.sequence = output->sequence++;
/* Get next buffer */
output->buf[!active_index] = vfe_buf_get_pending(output);
if (!output->buf[!active_index]) {
/* No next buffer - set same address */
new_addr = ready_buf->addr;
vfe_buf_update_wm_on_last(vfe, output);
} else {
new_addr = output->buf[!active_index]->addr;
vfe_buf_update_wm_on_next(vfe, output);
}
if (active_index)
for (i = 0; i < output->wm_num; i++)
vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], new_addr[i]);
else
for (i = 0; i < output->wm_num; i++)
vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], new_addr[i]);
spin_unlock_irqrestore(&vfe->output_lock, flags);
if (output->state == VFE_OUTPUT_STOPPING)
output->last_buffer = ready_buf;
else
vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
return;
out_unlock:
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
/*
* vfe_queue_buffer - Add empty buffer
* @vid: Video device structure
* @buf: Buffer to be enqueued
*
* Add an empty buffer - depending on the current number of buffers it will be
* put in pending buffer queue or directly given to the hardware to be filled.
*
* Return 0 on success or a negative error code otherwise
*/
static int vfe_queue_buffer(struct camss_video *vid, struct camss_buffer *buf)
{
struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output;
unsigned long flags;
output = &line->output;
spin_lock_irqsave(&vfe->output_lock, flags);
vfe_buf_update_wm_on_new(vfe, output, buf, line);
spin_unlock_irqrestore(&vfe->output_lock, flags);
return 0;
}
#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
int vfe_word_per_line(u32 format, u32 width)
{
int val = 0;
switch (format) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
val = CALC_WORD(width, 1, 8);
break;
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
val = CALC_WORD(width, 2, 8);
break;
}
return val;
}
const struct vfe_isr_ops vfe_isr_ops_gen1 = {
.reset_ack = vfe_isr_reset_ack,
.halt_ack = vfe_isr_halt_ack,
.reg_update = vfe_isr_reg_update,
.sof = vfe_isr_sof,
.comp_done = vfe_isr_comp_done,
.wm_done = vfe_isr_wm_done,
};
const struct camss_video_ops vfe_video_ops_gen1 = {
.queue_buffer = vfe_queue_buffer,
.flush_buffers = vfe_flush_buffers,
};

View File

@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* camss-vfe.h
*
* Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module
*
* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
* Copyright (C) 2015-2018 Linaro Ltd.
*/
#ifndef QC_MSM_CAMSS_VFE_GEN1_H
#define QC_MSM_CAMSS_VFE_GEN1_H
#include "camss-vfe.h"
enum vfe_line_id;
struct vfe_device;
struct vfe_line;
struct vfe_output;
struct vfe_hw_ops_gen1 {
void (*bus_connect_wm_to_rdi)(struct vfe_device *vfe, u8 wm, enum vfe_line_id id);
void (*bus_disconnect_wm_from_rdi)(struct vfe_device *vfe, u8 wm, enum vfe_line_id id);
void (*bus_enable_wr_if)(struct vfe_device *vfe, u8 enable);
void (*bus_reload_wm)(struct vfe_device *vfe, u8 wm);
int (*camif_wait_for_stop)(struct vfe_device *vfe, struct device *dev);
void (*enable_irq_common)(struct vfe_device *vfe);
void (*enable_irq_wm_line)(struct vfe_device *vfe, u8 wm, enum vfe_line_id line_id,
u8 enable);
void (*enable_irq_pix_line)(struct vfe_device *vfe, u8 comp, enum vfe_line_id line_id,
u8 enable);
u16 (*get_ub_size)(u8 vfe_id);
void (*halt_clear)(struct vfe_device *vfe);
void (*halt_request)(struct vfe_device *vfe);
void (*set_camif_cfg)(struct vfe_device *vfe, struct vfe_line *line);
void (*set_camif_cmd)(struct vfe_device *vfe, u8 enable);
void (*set_cgc_override)(struct vfe_device *vfe, u8 wm, u8 enable);
void (*set_clamp_cfg)(struct vfe_device *vfe);
void (*set_crop_cfg)(struct vfe_device *vfe, struct vfe_line *line);
void (*set_demux_cfg)(struct vfe_device *vfe, struct vfe_line *line);
void (*set_ds)(struct vfe_device *vfe);
void (*set_module_cfg)(struct vfe_device *vfe, u8 enable);
void (*set_scale_cfg)(struct vfe_device *vfe, struct vfe_line *line);
void (*set_rdi_cid)(struct vfe_device *vfe, enum vfe_line_id id, u8 cid);
void (*set_realign_cfg)(struct vfe_device *vfe, struct vfe_line *line, u8 enable);
void (*set_qos)(struct vfe_device *vfe);
void (*set_xbar_cfg)(struct vfe_device *vfe, struct vfe_output *output, u8 enable);
void (*wm_frame_based)(struct vfe_device *vfe, u8 wm, u8 enable);
void (*wm_line_based)(struct vfe_device *vfe, u32 wm, struct v4l2_pix_format_mplane *pix,
u8 plane, u32 enable);
void (*wm_set_ub_cfg)(struct vfe_device *vfe, u8 wm, u16 offset, u16 depth);
void (*wm_set_subsample)(struct vfe_device *vfe, u8 wm);
void (*wm_set_framedrop_period)(struct vfe_device *vfe, u8 wm, u8 per);
void (*wm_set_framedrop_pattern)(struct vfe_device *vfe, u8 wm, u32 pattern);
void (*wm_set_ping_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
void (*wm_set_pong_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
int (*wm_get_ping_pong_status)(struct vfe_device *vfe, u8 wm);
void (*wm_enable)(struct vfe_device *vfe, u8 wm, u8 enable);
};
/*
* vfe_calc_interp_reso - Calculate interpolation mode
* @input: Input resolution
* @output: Output resolution
*
* Return interpolation mode
*/
static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
{
if (input / output >= 16)
return 0;
if (input / output >= 8)
return 1;
if (input / output >= 4)
return 2;
return 3;
}
/*
* vfe_gen1_disable - Disable streaming on VFE line
* @line: VFE line
*
* Return 0 on success or a negative error code otherwise
*/
int vfe_gen1_disable(struct vfe_line *line);
/*
* vfe_gen1_enable - Enable VFE module
* @line: VFE line
*
* Return 0 on success
*/
int vfe_gen1_enable(struct vfe_line *line);
/*
* vfe_gen1_enable - Halt VFE module
* @vfe: VFE device
*
* Return 0 on success
*/
int vfe_gen1_halt(struct vfe_device *vfe);
/*
* vfe_word_per_line - Calculate number of words per frame width
* @format: V4L2 format
* @width: Frame width
*
* Return number of words per frame width
*/
int vfe_word_per_line(u32 format, u32 width);
extern const struct vfe_isr_ops vfe_isr_ops_gen1;
extern const struct camss_video_ops vfe_video_ops_gen1;
#endif /* QC_MSM_CAMSS_VFE_GEN1_H */

View File

@ -26,22 +26,8 @@
#define MSM_VFE_NAME "msm_vfe"
#define vfe_line_array(ptr_line) \
((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)]))
#define to_vfe(ptr_line) \
container_of(vfe_line_array(ptr_line), struct vfe_device, line)
/* VFE reset timeout */
#define VFE_RESET_TIMEOUT_MS 50
/* VFE halt timeout */
#define VFE_HALT_TIMEOUT_MS 100
/* Max number of frame drop updates per frame */
#define VFE_FRAME_DROP_UPDATES 2
/* Frame drop value. VAL + UPDATES - 1 should not exceed 31 */
#define VFE_FRAME_DROP_VAL 30
#define VFE_NEXT_SOF_MS 500
#define SCALER_RATIO_MAX 16
@ -294,35 +280,11 @@ static int vfe_reset(struct vfe_device *vfe)
return 0;
}
/*
* vfe_halt - Trigger halt on VFE module and wait to complete
* @vfe: VFE device
*
* Return 0 on success or a negative error code otherwise
*/
static int vfe_halt(struct vfe_device *vfe)
{
unsigned long time;
reinit_completion(&vfe->halt_complete);
vfe->ops->halt_request(vfe);
time = wait_for_completion_timeout(&vfe->halt_complete,
msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
if (!time) {
dev_err(vfe->camss->dev, "VFE halt timeout\n");
return -EIO;
}
return 0;
}
static void vfe_init_outputs(struct vfe_device *vfe)
{
int i;
for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
for (i = 0; i < vfe->line_num; i++) {
struct vfe_output *output = &vfe->line[i].output;
output->state = VFE_OUTPUT_OFF;
@ -340,71 +302,7 @@ static void vfe_reset_output_maps(struct vfe_device *vfe)
vfe->wm_output_map[i] = VFE_LINE_NONE;
}
static void vfe_output_init_addrs(struct vfe_device *vfe,
struct vfe_output *output, u8 sync)
{
u32 ping_addr;
u32 pong_addr;
unsigned int i;
output->active_buf = 0;
for (i = 0; i < output->wm_num; i++) {
if (output->buf[0])
ping_addr = output->buf[0]->addr[i];
else
ping_addr = 0;
if (output->buf[1])
pong_addr = output->buf[1]->addr[i];
else
pong_addr = ping_addr;
vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr);
vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr);
if (sync)
vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
}
}
static void vfe_output_update_ping_addr(struct vfe_device *vfe,
struct vfe_output *output, u8 sync)
{
u32 addr;
unsigned int i;
for (i = 0; i < output->wm_num; i++) {
if (output->buf[0])
addr = output->buf[0]->addr[i];
else
addr = 0;
vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], addr);
if (sync)
vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
}
}
static void vfe_output_update_pong_addr(struct vfe_device *vfe,
struct vfe_output *output, u8 sync)
{
u32 addr;
unsigned int i;
for (i = 0; i < output->wm_num; i++) {
if (output->buf[1])
addr = output->buf[1]->addr[i];
else
addr = 0;
vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], addr);
if (sync)
vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
}
}
static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
{
int ret = -EBUSY;
int i;
@ -420,7 +318,7 @@ static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id)
return ret;
}
static int vfe_release_wm(struct vfe_device *vfe, u8 wm)
int vfe_release_wm(struct vfe_device *vfe, u8 wm)
{
if (wm >= ARRAY_SIZE(vfe->wm_output_map))
return -EINVAL;
@ -430,29 +328,7 @@ static int vfe_release_wm(struct vfe_device *vfe, u8 wm)
return 0;
}
static void vfe_output_frame_drop(struct vfe_device *vfe,
struct vfe_output *output,
u32 drop_pattern)
{
u8 drop_period;
unsigned int i;
/* We need to toggle update period to be valid on next frame */
output->drop_update_idx++;
output->drop_update_idx %= VFE_FRAME_DROP_UPDATES;
drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
for (i = 0; i < output->wm_num; i++) {
vfe->ops->wm_set_framedrop_period(vfe, output->wm_idx[i],
drop_period);
vfe->ops->wm_set_framedrop_pattern(vfe, output->wm_idx[i],
drop_pattern);
}
vfe->ops->reg_update(vfe,
container_of(output, struct vfe_line, output)->id);
}
static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
{
struct camss_buffer *buffer = NULL;
@ -466,13 +342,8 @@ static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
return buffer;
}
/*
* vfe_buf_add_pending - Add output buffer to list of pending
* @output: VFE output
* @buffer: Video buffer
*/
static void vfe_buf_add_pending(struct vfe_output *output,
struct camss_buffer *buffer)
void vfe_buf_add_pending(struct vfe_output *output,
struct camss_buffer *buffer)
{
INIT_LIST_HEAD(&buffer->queue);
list_add_tail(&buffer->queue, &output->pending_bufs);
@ -495,149 +366,7 @@ static void vfe_buf_flush_pending(struct vfe_output *output,
}
}
static void vfe_buf_update_wm_on_next(struct vfe_device *vfe,
struct vfe_output *output)
{
switch (output->state) {
case VFE_OUTPUT_CONTINUOUS:
vfe_output_frame_drop(vfe, output, 3);
break;
case VFE_OUTPUT_SINGLE:
default:
dev_err_ratelimited(vfe->camss->dev,
"Next buf in wrong state! %d\n",
output->state);
break;
}
}
static void vfe_buf_update_wm_on_last(struct vfe_device *vfe,
struct vfe_output *output)
{
switch (output->state) {
case VFE_OUTPUT_CONTINUOUS:
output->state = VFE_OUTPUT_SINGLE;
vfe_output_frame_drop(vfe, output, 1);
break;
case VFE_OUTPUT_SINGLE:
output->state = VFE_OUTPUT_STOPPING;
vfe_output_frame_drop(vfe, output, 0);
break;
default:
dev_err_ratelimited(vfe->camss->dev,
"Last buff in wrong state! %d\n",
output->state);
break;
}
}
static void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
struct vfe_output *output,
struct camss_buffer *new_buf)
{
int inactive_idx;
switch (output->state) {
case VFE_OUTPUT_SINGLE:
inactive_idx = !output->active_buf;
if (!output->buf[inactive_idx]) {
output->buf[inactive_idx] = new_buf;
if (inactive_idx)
vfe_output_update_pong_addr(vfe, output, 0);
else
vfe_output_update_ping_addr(vfe, output, 0);
vfe_output_frame_drop(vfe, output, 3);
output->state = VFE_OUTPUT_CONTINUOUS;
} else {
vfe_buf_add_pending(output, new_buf);
dev_err_ratelimited(vfe->camss->dev,
"Inactive buffer is busy\n");
}
break;
case VFE_OUTPUT_IDLE:
if (!output->buf[0]) {
output->buf[0] = new_buf;
vfe_output_init_addrs(vfe, output, 1);
vfe_output_frame_drop(vfe, output, 1);
output->state = VFE_OUTPUT_SINGLE;
} else {
vfe_buf_add_pending(output, new_buf);
dev_err_ratelimited(vfe->camss->dev,
"Output idle with buffer set!\n");
}
break;
case VFE_OUTPUT_CONTINUOUS:
default:
vfe_buf_add_pending(output, new_buf);
break;
}
}
static int vfe_get_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output;
struct v4l2_format *f = &line->video_out.active_fmt;
unsigned long flags;
int i;
int wm_idx;
spin_lock_irqsave(&vfe->output_lock, flags);
output = &line->output;
if (output->state != VFE_OUTPUT_OFF) {
dev_err(vfe->camss->dev, "Output is running\n");
goto error;
}
output->state = VFE_OUTPUT_RESERVED;
output->active_buf = 0;
switch (f->fmt.pix_mp.pixelformat) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
output->wm_num = 2;
break;
default:
output->wm_num = 1;
break;
}
for (i = 0; i < output->wm_num; i++) {
wm_idx = vfe_reserve_wm(vfe, line->id);
if (wm_idx < 0) {
dev_err(vfe->camss->dev, "Can not reserve wm\n");
goto error_get_wm;
}
output->wm_idx[i] = wm_idx;
}
output->drop_update_idx = 0;
spin_unlock_irqrestore(&vfe->output_lock, flags);
return 0;
error_get_wm:
for (i--; i >= 0; i--)
vfe_release_wm(vfe, output->wm_idx[i]);
output->state = VFE_OUTPUT_OFF;
error:
spin_unlock_irqrestore(&vfe->output_lock, flags);
return -EINVAL;
}
static int vfe_put_output(struct vfe_line *line)
int vfe_put_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
@ -655,454 +384,27 @@ static int vfe_put_output(struct vfe_line *line)
return 0;
}
static int vfe_enable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
const struct vfe_hw_ops *ops = vfe->ops;
struct media_entity *sensor;
unsigned long flags;
unsigned int frame_skip = 0;
unsigned int i;
u16 ub_size;
ub_size = ops->get_ub_size(vfe->id);
if (!ub_size)
return -EINVAL;
sensor = camss_find_sensor(&line->subdev.entity);
if (sensor) {
struct v4l2_subdev *subdev =
media_entity_to_v4l2_subdev(sensor);
v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
/* Max frame skip is 29 frames */
if (frame_skip > VFE_FRAME_DROP_VAL - 1)
frame_skip = VFE_FRAME_DROP_VAL - 1;
}
spin_lock_irqsave(&vfe->output_lock, flags);
ops->reg_update_clear(vfe, line->id);
if (output->state != VFE_OUTPUT_RESERVED) {
dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
output->state);
spin_unlock_irqrestore(&vfe->output_lock, flags);
return -EINVAL;
}
output->state = VFE_OUTPUT_IDLE;
output->buf[0] = vfe_buf_get_pending(output);
output->buf[1] = vfe_buf_get_pending(output);
if (!output->buf[0] && output->buf[1]) {
output->buf[0] = output->buf[1];
output->buf[1] = NULL;
}
if (output->buf[0])
output->state = VFE_OUTPUT_SINGLE;
if (output->buf[1])
output->state = VFE_OUTPUT_CONTINUOUS;
switch (output->state) {
case VFE_OUTPUT_SINGLE:
vfe_output_frame_drop(vfe, output, 1 << frame_skip);
break;
case VFE_OUTPUT_CONTINUOUS:
vfe_output_frame_drop(vfe, output, 3 << frame_skip);
break;
default:
vfe_output_frame_drop(vfe, output, 0);
break;
}
output->sequence = 0;
output->wait_sof = 0;
output->wait_reg_update = 0;
reinit_completion(&output->sof);
reinit_completion(&output->reg_update);
vfe_output_init_addrs(vfe, output, 0);
if (line->id != VFE_LINE_PIX) {
ops->set_cgc_override(vfe, output->wm_idx[0], 1);
ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1);
ops->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id);
ops->wm_set_subsample(vfe, output->wm_idx[0]);
ops->set_rdi_cid(vfe, line->id, 0);
ops->wm_set_ub_cfg(vfe, output->wm_idx[0],
(ub_size + 1) * output->wm_idx[0], ub_size);
ops->wm_frame_based(vfe, output->wm_idx[0], 1);
ops->wm_enable(vfe, output->wm_idx[0], 1);
ops->bus_reload_wm(vfe, output->wm_idx[0]);
} else {
ub_size /= output->wm_num;
for (i = 0; i < output->wm_num; i++) {
ops->set_cgc_override(vfe, output->wm_idx[i], 1);
ops->wm_set_subsample(vfe, output->wm_idx[i]);
ops->wm_set_ub_cfg(vfe, output->wm_idx[i],
(ub_size + 1) * output->wm_idx[i],
ub_size);
ops->wm_line_based(vfe, output->wm_idx[i],
&line->video_out.active_fmt.fmt.pix_mp,
i, 1);
ops->wm_enable(vfe, output->wm_idx[i], 1);
ops->bus_reload_wm(vfe, output->wm_idx[i]);
}
ops->enable_irq_pix_line(vfe, 0, line->id, 1);
ops->set_module_cfg(vfe, 1);
ops->set_camif_cfg(vfe, line);
ops->set_realign_cfg(vfe, line, 1);
ops->set_xbar_cfg(vfe, output, 1);
ops->set_demux_cfg(vfe, line);
ops->set_scale_cfg(vfe, line);
ops->set_crop_cfg(vfe, line);
ops->set_clamp_cfg(vfe);
ops->set_camif_cmd(vfe, 1);
}
ops->reg_update(vfe, line->id);
spin_unlock_irqrestore(&vfe->output_lock, flags);
return 0;
}
static int vfe_disable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
const struct vfe_hw_ops *ops = vfe->ops;
unsigned long flags;
unsigned long time;
unsigned int i;
spin_lock_irqsave(&vfe->output_lock, flags);
output->wait_sof = 1;
spin_unlock_irqrestore(&vfe->output_lock, flags);
time = wait_for_completion_timeout(&output->sof,
msecs_to_jiffies(VFE_NEXT_SOF_MS));
if (!time)
dev_err(vfe->camss->dev, "VFE sof timeout\n");
spin_lock_irqsave(&vfe->output_lock, flags);
for (i = 0; i < output->wm_num; i++)
ops->wm_enable(vfe, output->wm_idx[i], 0);
ops->reg_update(vfe, line->id);
output->wait_reg_update = 1;
spin_unlock_irqrestore(&vfe->output_lock, flags);
time = wait_for_completion_timeout(&output->reg_update,
msecs_to_jiffies(VFE_NEXT_SOF_MS));
if (!time)
dev_err(vfe->camss->dev, "VFE reg update timeout\n");
spin_lock_irqsave(&vfe->output_lock, flags);
if (line->id != VFE_LINE_PIX) {
ops->wm_frame_based(vfe, output->wm_idx[0], 0);
ops->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0],
line->id);
ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0);
ops->set_cgc_override(vfe, output->wm_idx[0], 0);
spin_unlock_irqrestore(&vfe->output_lock, flags);
} else {
for (i = 0; i < output->wm_num; i++) {
ops->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
ops->set_cgc_override(vfe, output->wm_idx[i], 0);
}
ops->enable_irq_pix_line(vfe, 0, line->id, 0);
ops->set_module_cfg(vfe, 0);
ops->set_realign_cfg(vfe, line, 0);
ops->set_xbar_cfg(vfe, output, 0);
ops->set_camif_cmd(vfe, 0);
spin_unlock_irqrestore(&vfe->output_lock, flags);
ops->camif_wait_for_stop(vfe, vfe->camss->dev);
}
return 0;
}
/*
* vfe_enable - Enable streaming on VFE line
* @line: VFE line
*
* Return 0 on success or a negative error code otherwise
*/
static int vfe_enable(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
int ret;
mutex_lock(&vfe->stream_lock);
if (!vfe->stream_count) {
vfe->ops->enable_irq_common(vfe);
vfe->ops->bus_enable_wr_if(vfe, 1);
vfe->ops->set_qos(vfe);
vfe->ops->set_ds(vfe);
}
vfe->stream_count++;
mutex_unlock(&vfe->stream_lock);
ret = vfe_get_output(line);
if (ret < 0)
goto error_get_output;
ret = vfe_enable_output(line);
if (ret < 0)
goto error_enable_output;
vfe->was_streaming = 1;
return 0;
error_enable_output:
vfe_put_output(line);
error_get_output:
mutex_lock(&vfe->stream_lock);
if (vfe->stream_count == 1)
vfe->ops->bus_enable_wr_if(vfe, 0);
vfe->stream_count--;
mutex_unlock(&vfe->stream_lock);
return ret;
}
/*
* vfe_disable - Disable streaming on VFE line
* @line: VFE line
*
* Return 0 on success or a negative error code otherwise
*/
static int vfe_disable(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
vfe_disable_output(line);
vfe_put_output(line);
mutex_lock(&vfe->stream_lock);
if (vfe->stream_count == 1)
vfe->ops->bus_enable_wr_if(vfe, 0);
vfe->stream_count--;
mutex_unlock(&vfe->stream_lock);
return 0;
}
/*
* vfe_isr_sof - Process start of frame interrupt
* @vfe: VFE Device
* @line_id: VFE line
*/
static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id)
{
struct vfe_output *output;
unsigned long flags;
spin_lock_irqsave(&vfe->output_lock, flags);
output = &vfe->line[line_id].output;
if (output->wait_sof) {
output->wait_sof = 0;
complete(&output->sof);
}
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
/*
* vfe_isr_reg_update - Process reg update interrupt
* @vfe: VFE Device
* @line_id: VFE line
*/
static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
{
struct vfe_output *output;
unsigned long flags;
spin_lock_irqsave(&vfe->output_lock, flags);
vfe->ops->reg_update_clear(vfe, line_id);
output = &vfe->line[line_id].output;
if (output->wait_reg_update) {
output->wait_reg_update = 0;
complete(&output->reg_update);
spin_unlock_irqrestore(&vfe->output_lock, flags);
return;
}
if (output->state == VFE_OUTPUT_STOPPING) {
/* Release last buffer when hw is idle */
if (output->last_buffer) {
vb2_buffer_done(&output->last_buffer->vb.vb2_buf,
VB2_BUF_STATE_DONE);
output->last_buffer = NULL;
}
output->state = VFE_OUTPUT_IDLE;
/* Buffers received in stopping state are queued in */
/* dma pending queue, start next capture here */
output->buf[0] = vfe_buf_get_pending(output);
output->buf[1] = vfe_buf_get_pending(output);
if (!output->buf[0] && output->buf[1]) {
output->buf[0] = output->buf[1];
output->buf[1] = NULL;
}
if (output->buf[0])
output->state = VFE_OUTPUT_SINGLE;
if (output->buf[1])
output->state = VFE_OUTPUT_CONTINUOUS;
switch (output->state) {
case VFE_OUTPUT_SINGLE:
vfe_output_frame_drop(vfe, output, 2);
break;
case VFE_OUTPUT_CONTINUOUS:
vfe_output_frame_drop(vfe, output, 3);
break;
default:
vfe_output_frame_drop(vfe, output, 0);
break;
}
vfe_output_init_addrs(vfe, output, 1);
}
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
/*
* vfe_isr_wm_done - Process write master done interrupt
* @vfe: VFE Device
* @wm: Write master id
*/
static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
{
struct camss_buffer *ready_buf;
struct vfe_output *output;
dma_addr_t *new_addr;
unsigned long flags;
u32 active_index;
u64 ts = ktime_get_ns();
unsigned int i;
active_index = vfe->ops->wm_get_ping_pong_status(vfe, wm);
spin_lock_irqsave(&vfe->output_lock, flags);
if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
dev_err_ratelimited(vfe->camss->dev,
"Received wm done for unmapped index\n");
goto out_unlock;
}
output = &vfe->line[vfe->wm_output_map[wm]].output;
if (output->active_buf == active_index) {
dev_err_ratelimited(vfe->camss->dev,
"Active buffer mismatch!\n");
goto out_unlock;
}
output->active_buf = active_index;
ready_buf = output->buf[!active_index];
if (!ready_buf) {
dev_err_ratelimited(vfe->camss->dev,
"Missing ready buf %d %d!\n",
!active_index, output->state);
goto out_unlock;
}
ready_buf->vb.vb2_buf.timestamp = ts;
ready_buf->vb.sequence = output->sequence++;
/* Get next buffer */
output->buf[!active_index] = vfe_buf_get_pending(output);
if (!output->buf[!active_index]) {
/* No next buffer - set same address */
new_addr = ready_buf->addr;
vfe_buf_update_wm_on_last(vfe, output);
} else {
new_addr = output->buf[!active_index]->addr;
vfe_buf_update_wm_on_next(vfe, output);
}
if (active_index)
for (i = 0; i < output->wm_num; i++)
vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i],
new_addr[i]);
else
for (i = 0; i < output->wm_num; i++)
vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i],
new_addr[i]);
spin_unlock_irqrestore(&vfe->output_lock, flags);
if (output->state == VFE_OUTPUT_STOPPING)
output->last_buffer = ready_buf;
else
vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
return;
out_unlock:
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
/**
* vfe_isr_comp_done() - Process composite image done interrupt
* @vfe: VFE Device
* @comp: Composite image id
*/
static void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++)
if (vfe->wm_output_map[i] == VFE_LINE_PIX) {
vfe_isr_wm_done(vfe, i);
vfe->isr_ops.wm_done(vfe, i);
break;
}
}
static inline void vfe_isr_reset_ack(struct vfe_device *vfe)
void vfe_isr_reset_ack(struct vfe_device *vfe)
{
complete(&vfe->reset_complete);
}
static inline void vfe_isr_halt_ack(struct vfe_device *vfe)
{
complete(&vfe->halt_complete);
vfe->ops->halt_clear(vfe);
}
/*
* vfe_set_clock_rates - Calculate and set clock rates on VFE module
* @vfe: VFE device
@ -1112,11 +414,11 @@ static inline void vfe_isr_halt_ack(struct vfe_device *vfe)
static int vfe_set_clock_rates(struct vfe_device *vfe)
{
struct device *dev = vfe->camss->dev;
u64 pixel_clock[MSM_VFE_LINE_NUM];
u64 pixel_clock[VFE_LINE_NUM_MAX];
int i, j;
int ret;
for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
&pixel_clock[i]);
if (ret)
@ -1131,7 +433,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
u64 min_rate = 0;
long rate;
for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) {
for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
u32 tmp;
u8 bpp;
@ -1194,11 +496,11 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
*/
static int vfe_check_clock_rates(struct vfe_device *vfe)
{
u64 pixel_clock[MSM_VFE_LINE_NUM];
u64 pixel_clock[VFE_LINE_NUM_MAX];
int i, j;
int ret;
for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
&pixel_clock[i]);
if (ret)
@ -1213,7 +515,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe)
u64 min_rate = 0;
unsigned long rate;
for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) {
for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
u32 tmp;
u8 bpp;
@ -1318,7 +620,7 @@ static void vfe_put(struct vfe_device *vfe)
} else if (vfe->power_count == 1) {
if (vfe->was_streaming) {
vfe->was_streaming = 0;
vfe_halt(vfe);
vfe->ops->vfe_halt(vfe);
}
camss_disable_clocks(vfe->nclocks, vfe->clock);
pm_runtime_put_sync(vfe->camss->dev);
@ -1331,35 +633,6 @@ exit:
mutex_unlock(&vfe->power_lock);
}
/*
* vfe_queue_buffer - Add empty buffer
* @vid: Video device structure
* @buf: Buffer to be enqueued
*
* Add an empty buffer - depending on the current number of buffers it will be
* put in pending buffer queue or directly given to the hardware to be filled.
*
* Return 0 on success or a negative error code otherwise
*/
static int vfe_queue_buffer(struct camss_video *vid,
struct camss_buffer *buf)
{
struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output;
unsigned long flags;
output = &line->output;
spin_lock_irqsave(&vfe->output_lock, flags);
vfe_buf_update_wm_on_new(vfe, output, buf);
spin_unlock_irqrestore(&vfe->output_lock, flags);
return 0;
}
/*
* vfe_flush_buffers - Return all vb2 buffers
* @vid: Video device structure
@ -1370,8 +643,8 @@ static int vfe_queue_buffer(struct camss_video *vid,
*
* Return 0 on success or a negative error code otherwise
*/
static int vfe_flush_buffers(struct camss_video *vid,
enum vb2_buffer_state state)
int vfe_flush_buffers(struct camss_video *vid,
enum vb2_buffer_state state)
{
struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
struct vfe_device *vfe = to_vfe(line);
@ -1442,12 +715,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable)
int ret;
if (enable) {
ret = vfe_enable(line);
ret = vfe->ops->vfe_enable(line);
if (ret < 0)
dev_err(vfe->camss->dev,
"Failed to enable vfe outputs\n");
} else {
ret = vfe_disable(line);
ret = vfe->ops->vfe_disable(line);
if (ret < 0)
dev_err(vfe->camss->dev,
"Failed to disable vfe outputs\n");
@ -1985,13 +1258,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
int i, j;
int ret;
vfe->isr_ops.reset_ack = vfe_isr_reset_ack;
vfe->isr_ops.halt_ack = vfe_isr_halt_ack;
vfe->isr_ops.reg_update = vfe_isr_reg_update;
vfe->isr_ops.sof = vfe_isr_sof;
vfe->isr_ops.comp_done = vfe_isr_comp_done;
vfe->isr_ops.wm_done = vfe_isr_wm_done;
switch (camss->version) {
case CAMSS_8x16:
vfe->ops = &vfe_ops_4_1;
@ -2005,6 +1271,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
default:
return -EINVAL;
}
vfe->ops->subdev_init(dev, vfe);
/* Memory */
@ -2086,7 +1353,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
vfe->id = id;
vfe->reg_update = 0;
for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
struct vfe_line *l = &vfe->line[i];
l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
@ -2209,11 +1476,6 @@ static const struct media_entity_operations vfe_media_ops = {
.link_validate = v4l2_subdev_link_validate,
};
static const struct camss_video_ops camss_vfe_video_ops = {
.queue_buffer = vfe_queue_buffer,
.flush_buffers = vfe_flush_buffers,
};
/*
* msm_vfe_register_entities - Register subdev node for VFE module
* @vfe: VFE device
@ -2236,7 +1498,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
int ret;
int i;
for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
for (i = 0; i < vfe->line_num; i++) {
char name[32];
sd = &vfe->line[i].subdev;
@ -2279,7 +1541,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
goto error_reg_subdev;
}
video_out->ops = &camss_vfe_video_ops;
video_out->ops = &vfe->video_ops;
video_out->bpl_alignment = 8;
video_out->line_based = 0;
if (i == VFE_LINE_PIX) {
@ -2343,7 +1605,7 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe)
mutex_destroy(&vfe->power_lock);
mutex_destroy(&vfe->stream_lock);
for (i = 0; i < ARRAY_SIZE(vfe->line); i++) {
for (i = 0; i < vfe->line_num; i++) {
struct v4l2_subdev *sd = &vfe->line[i].subdev;
struct camss_video *video_out = &vfe->line[i].video_out;

View File

@ -17,15 +17,26 @@
#include <media/v4l2-subdev.h>
#include "camss-video.h"
#include "camss-vfe-gen1.h"
#define MSM_VFE_PAD_SINK 0
#define MSM_VFE_PAD_SRC 1
#define MSM_VFE_PADS_NUM 2
#define MSM_VFE_LINE_NUM 4
#define MSM_VFE_IMAGE_MASTERS_NUM 7
#define MSM_VFE_COMPOSITE_IRQ_NUM 4
/* VFE halt timeout */
#define VFE_HALT_TIMEOUT_MS 100
/* Frame drop value. VAL + UPDATES - 1 should not exceed 31 */
#define VFE_FRAME_DROP_VAL 30
#define vfe_line_array(ptr_line) \
((const struct vfe_line (*)[]) &(ptr_line)[-(ptr_line)->id])
#define to_vfe(ptr_line) \
container_of(vfe_line_array(ptr_line), struct vfe_device, line)
enum vfe_output_state {
VFE_OUTPUT_OFF,
VFE_OUTPUT_RESERVED,
@ -40,23 +51,30 @@ enum vfe_line_id {
VFE_LINE_RDI0 = 0,
VFE_LINE_RDI1 = 1,
VFE_LINE_RDI2 = 2,
VFE_LINE_PIX = 3
VFE_LINE_PIX = 3,
VFE_LINE_NUM_GEN1 = 4,
VFE_LINE_NUM_MAX = 4
};
struct vfe_output {
u8 wm_num;
u8 wm_idx[3];
int active_buf;
struct camss_buffer *buf[2];
struct camss_buffer *last_buffer;
struct list_head pending_bufs;
unsigned int drop_update_idx;
union {
struct {
int active_buf;
int wait_sof;
} gen1;
};
enum vfe_output_state state;
unsigned int sequence;
int wait_sof;
int wait_reg_update;
struct completion sof;
struct completion reg_update;
@ -78,59 +96,19 @@ struct vfe_line {
struct vfe_device;
struct vfe_hw_ops {
void (*hw_version_read)(struct vfe_device *vfe, struct device *dev);
u16 (*get_ub_size)(u8 vfe_id);
void (*enable_irq_common)(struct vfe_device *vfe);
void (*global_reset)(struct vfe_device *vfe);
void (*halt_request)(struct vfe_device *vfe);
void (*halt_clear)(struct vfe_device *vfe);
void (*wm_enable)(struct vfe_device *vfe, u8 wm, u8 enable);
void (*wm_frame_based)(struct vfe_device *vfe, u8 wm, u8 enable);
void (*wm_line_based)(struct vfe_device *vfe, u32 wm,
struct v4l2_pix_format_mplane *pix,
u8 plane, u32 enable);
void (*wm_set_framedrop_period)(struct vfe_device *vfe, u8 wm, u8 per);
void (*wm_set_framedrop_pattern)(struct vfe_device *vfe, u8 wm,
u32 pattern);
void (*wm_set_ub_cfg)(struct vfe_device *vfe, u8 wm, u16 offset,
u16 depth);
void (*bus_reload_wm)(struct vfe_device *vfe, u8 wm);
void (*wm_set_ping_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
void (*wm_set_pong_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
int (*wm_get_ping_pong_status)(struct vfe_device *vfe, u8 wm);
void (*bus_enable_wr_if)(struct vfe_device *vfe, u8 enable);
void (*bus_connect_wm_to_rdi)(struct vfe_device *vfe, u8 wm,
enum vfe_line_id id);
void (*wm_set_subsample)(struct vfe_device *vfe, u8 wm);
void (*bus_disconnect_wm_from_rdi)(struct vfe_device *vfe, u8 wm,
enum vfe_line_id id);
void (*set_xbar_cfg)(struct vfe_device *vfe, struct vfe_output *output,
u8 enable);
void (*set_rdi_cid)(struct vfe_device *vfe, enum vfe_line_id id,
u8 cid);
void (*set_realign_cfg)(struct vfe_device *vfe, struct vfe_line *line,
u8 enable);
void (*hw_version_read)(struct vfe_device *vfe, struct device *dev);
irqreturn_t (*isr)(int irq, void *dev);
void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1);
void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id);
void (*reg_update_clear)(struct vfe_device *vfe,
enum vfe_line_id line_id);
void (*enable_irq_wm_line)(struct vfe_device *vfe, u8 wm,
enum vfe_line_id line_id, u8 enable);
void (*enable_irq_pix_line)(struct vfe_device *vfe, u8 comp,
enum vfe_line_id line_id, u8 enable);
void (*enable_irq_common)(struct vfe_device *vfe);
void (*set_demux_cfg)(struct vfe_device *vfe, struct vfe_line *line);
void (*set_scale_cfg)(struct vfe_device *vfe, struct vfe_line *line);
void (*set_crop_cfg)(struct vfe_device *vfe, struct vfe_line *line);
void (*set_clamp_cfg)(struct vfe_device *vfe);
void (*set_qos)(struct vfe_device *vfe);
void (*set_ds)(struct vfe_device *vfe);
void (*set_cgc_override)(struct vfe_device *vfe, u8 wm, u8 enable);
void (*set_camif_cfg)(struct vfe_device *vfe, struct vfe_line *line);
void (*set_camif_cmd)(struct vfe_device *vfe, u8 enable);
void (*set_module_cfg)(struct vfe_device *vfe, u8 enable);
int (*camif_wait_for_stop)(struct vfe_device *vfe, struct device *dev);
void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1);
void (*subdev_init)(struct device *dev, struct vfe_device *vfe);
int (*vfe_disable)(struct vfe_line *line);
int (*vfe_enable)(struct vfe_line *line);
int (*vfe_halt)(struct vfe_device *vfe);
void (*violation_read)(struct vfe_device *vfe);
irqreturn_t (*isr)(int irq, void *dev);
};
struct vfe_isr_ops {
@ -158,11 +136,14 @@ struct vfe_device {
int stream_count;
spinlock_t output_lock;
enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM];
struct vfe_line line[MSM_VFE_LINE_NUM];
struct vfe_line line[VFE_LINE_NUM_MAX];
u8 line_num;
u32 reg_update;
u8 was_streaming;
const struct vfe_hw_ops *ops;
const struct vfe_hw_ops_gen1 *ops_gen1;
struct vfe_isr_ops isr_ops;
struct camss_video_ops video_ops;
};
struct resources;
@ -178,6 +159,37 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe);
void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id);
void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id);
/*
* vfe_buf_add_pending - Add output buffer to list of pending
* @output: VFE output
* @buffer: Video buffer
*/
void vfe_buf_add_pending(struct vfe_output *output, struct camss_buffer *buffer);
struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output);
/*
* vfe_disable - Disable streaming on VFE line
* @line: VFE line
*
* Return 0 on success or a negative error code otherwise
*/
int vfe_disable(struct vfe_line *line);
int vfe_flush_buffers(struct camss_video *vid, enum vb2_buffer_state state);
/*
* vfe_isr_comp_done - Process composite image done interrupt
* @vfe: VFE Device
* @comp: Composite image id
*/
void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp);
void vfe_isr_reset_ack(struct vfe_device *vfe);
int vfe_put_output(struct vfe_line *line);
int vfe_release_wm(struct vfe_device *vfe, u8 wm);
int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id);
extern const struct vfe_hw_ops vfe_ops_4_1;
extern const struct vfe_hw_ops vfe_ops_4_7;
extern const struct vfe_hw_ops vfe_ops_4_8;

View File

@ -881,7 +881,7 @@ static int camss_register_entities(struct camss *camss)
for (i = 0; i < camss->ispif->line_num; i++)
for (k = 0; k < camss->vfe_num; k++)
for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) {
for (j = 0; j < camss->vfe[k].line_num; j++) {
struct v4l2_subdev *ispif = &camss->ispif->line[i].subdev;
struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev;
@ -902,7 +902,7 @@ static int camss_register_entities(struct camss *camss)
} else {
for (i = 0; i < camss->csid_num; i++)
for (k = 0; k < camss->vfe_num; k++)
for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) {
for (j = 0; j < camss->vfe[k].line_num; j++) {
struct v4l2_subdev *csid = &camss->csid[i].subdev;
struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev;