aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/platform/qcom/camss/Makefile2
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-1.c117
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-7.c241
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-8.c1161
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen1.c742
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen1.h117
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c790
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.h118
-rw-r--r--drivers/media/platform/qcom/camss/camss.c4
9 files changed, 2241 insertions, 1051 deletions
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 63c1b1b2943c..940c0ae3e003 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -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 \
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
index 85b9bcbc7321..636c0515506e 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
@@ -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,
+static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = {
+ .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
+ .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
+ .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,
+ .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,
- .global_reset = vfe_global_reset,
- .halt_request = vfe_halt_request,
.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,
+ .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_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_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_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 = vfe_set_qos,
- .set_ds = vfe_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,
+ .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,
- .violation_read = vfe_violation_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,
};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
index f7e00a2de393..f5ba09a93016 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
@@ -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)
-{
- 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;
-
- 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);
-}
-
-static void vfe48_set_ds(struct vfe_device *vfe)
+static void vfe_violation_read(struct vfe_device *vfe)
{
- u32 val = VFE48_0_BUS_BDG_DS_CFG_0_CFG;
- u32 val16 = VFE48_0_BUS_BDG_DS_CFG_16_CFG;
+ u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
- 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);
+ pr_err_ratelimited("VFE: violation = 0x%08x\n", violation);
}
-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,
+static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_7 = {
+ .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
+ .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
+ .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,
+ .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,
- .wm_enable = vfe48_wm_enable,
+ .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,
+ .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_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_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_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 = vfe48_set_qos,
- .set_ds = vfe48_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,
+ .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,
- .violation_read = vfe_violation_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,
};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
new file mode 100644
index 000000000000..8c3a5d293a1c
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
@@ -0,0 +1,1161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-vfe-4-8.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v4.8
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2021 Linaro Ltd.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "camss.h"
+#include "camss-vfe.h"
+#include "camss-vfe-gen1.h"
+
+#define VFE_0_HW_VERSION 0x000
+
+#define VFE_0_GLOBAL_RESET_CMD 0x018
+#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0)
+#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1)
+#define VFE_0_GLOBAL_RESET_CMD_BUS BIT(2)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG BIT(3)
+#define VFE_0_GLOBAL_RESET_CMD_REGISTER BIT(4)
+#define VFE_0_GLOBAL_RESET_CMD_PM BIT(5)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR BIT(6)
+#define VFE_0_GLOBAL_RESET_CMD_TESTGEN BIT(7)
+#define VFE_0_GLOBAL_RESET_CMD_DSP BIT(8)
+#define VFE_0_GLOBAL_RESET_CMD_IDLE_CGC BIT(9)
+
+#define VFE_0_MODULE_LENS_EN 0x040
+#define VFE_0_MODULE_LENS_EN_DEMUX BIT(2)
+#define VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE BIT(3)
+
+#define VFE_0_MODULE_ZOOM_EN 0x04c
+#define VFE_0_MODULE_ZOOM_EN_SCALE_ENC BIT(1)
+#define VFE_0_MODULE_ZOOM_EN_CROP_ENC BIT(2)
+#define VFE_0_MODULE_ZOOM_EN_REALIGN_BUF BIT(9)
+
+#define VFE_0_CORE_CFG 0x050
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7
+#define VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN BIT(4)
+
+#define VFE_0_IRQ_CMD 0x058
+#define VFE_0_IRQ_CMD_GLOBAL_CLEAR BIT(0)
+
+#define VFE_0_IRQ_MASK_0 0x05c
+#define VFE_0_IRQ_MASK_0_CAMIF_SOF BIT(0)
+#define VFE_0_IRQ_MASK_0_CAMIF_EOF BIT(1)
+#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) BIT((n) + 5)
+#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \
+ ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n))
+#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8)
+#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25)
+#define VFE_0_IRQ_MASK_0_RESET_ACK BIT(31)
+#define VFE_0_IRQ_MASK_1 0x060
+#define VFE_0_IRQ_MASK_1_CAMIF_ERROR BIT(0)
+#define VFE_0_IRQ_MASK_1_VIOLATION BIT(7)
+#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK BIT(8)
+#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9)
+#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) BIT((n) + 29)
+
+#define VFE_0_IRQ_CLEAR_0 0x064
+#define VFE_0_IRQ_CLEAR_1 0x068
+
+#define VFE_0_IRQ_STATUS_0 0x06c
+#define VFE_0_IRQ_STATUS_0_CAMIF_SOF BIT(0)
+#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) BIT((n) + 5)
+#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \
+ ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n))
+#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8)
+#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25)
+#define VFE_0_IRQ_STATUS_0_RESET_ACK BIT(31)
+#define VFE_0_IRQ_STATUS_1 0x070
+#define VFE_0_IRQ_STATUS_1_VIOLATION BIT(7)
+#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK BIT(8)
+#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) BIT((n) + 29)
+
+#define VFE_0_IRQ_COMPOSITE_MASK_0 0x074
+#define VFE_0_VIOLATION_STATUS 0x07c
+
+#define VFE_0_BUS_CMD 0x80
+#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) BIT(x)
+
+#define VFE_0_BUS_CFG 0x084
+
+#define VFE_0_BUS_XBAR_CFG_x(x) (0x90 + 0x4 * ((x) / 2))
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN BIT(2)
+#define VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN BIT(3)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTRA (0x1 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER (0x2 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0x0
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 0xc
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 0xd
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 0xe
+
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x0a0 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x0a4 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x0ac + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x0b4 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT 1
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1f << 2)
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x0b8 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x0bc + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x0c0 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \
+ (0x0c4 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \
+ (0x0c8 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff
+
+#define VFE_0_BUS_PING_PONG_STATUS 0x338
+
+#define VFE_0_BUS_BDG_CMD 0x400
+#define VFE_0_BUS_BDG_CMD_HALT_REQ 1
+
+#define VFE_0_BUS_BDG_QOS_CFG_0 0x404
+#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5
+#define VFE_0_BUS_BDG_QOS_CFG_1 0x408
+#define VFE_0_BUS_BDG_QOS_CFG_2 0x40c
+#define VFE_0_BUS_BDG_QOS_CFG_3 0x410
+#define VFE_0_BUS_BDG_QOS_CFG_3_CFG 0xaa55aaa5
+#define VFE_0_BUS_BDG_QOS_CFG_4 0x414
+#define VFE_0_BUS_BDG_QOS_CFG_4_CFG 0xaa55aa55
+#define VFE_0_BUS_BDG_QOS_CFG_5 0x418
+#define VFE_0_BUS_BDG_QOS_CFG_6 0x41c
+#define VFE_0_BUS_BDG_QOS_CFG_7 0x420
+#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0005aa55
+
+#define VFE_0_BUS_BDG_DS_CFG_0 0x424
+#define VFE_0_BUS_BDG_DS_CFG_0_CFG 0xcccc1111
+#define VFE_0_BUS_BDG_DS_CFG_1 0x428
+#define VFE_0_BUS_BDG_DS_CFG_2 0x42c
+#define VFE_0_BUS_BDG_DS_CFG_3 0x430
+#define VFE_0_BUS_BDG_DS_CFG_4 0x434
+#define VFE_0_BUS_BDG_DS_CFG_5 0x438
+#define VFE_0_BUS_BDG_DS_CFG_6 0x43c
+#define VFE_0_BUS_BDG_DS_CFG_7 0x440
+#define VFE_0_BUS_BDG_DS_CFG_8 0x444
+#define VFE_0_BUS_BDG_DS_CFG_9 0x448
+#define VFE_0_BUS_BDG_DS_CFG_10 0x44c
+#define VFE_0_BUS_BDG_DS_CFG_11 0x450
+#define VFE_0_BUS_BDG_DS_CFG_12 0x454
+#define VFE_0_BUS_BDG_DS_CFG_13 0x458
+#define VFE_0_BUS_BDG_DS_CFG_14 0x45c
+#define VFE_0_BUS_BDG_DS_CFG_15 0x460
+#define VFE_0_BUS_BDG_DS_CFG_16 0x464
+#define VFE_0_BUS_BDG_DS_CFG_16_CFG 0x00000110
+
+#define VFE_0_RDI_CFG_x(x) (0x46c + (0x4 * (x)))
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28)
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4)
+#define VFE_0_RDI_CFG_x_RDI_EN_BIT BIT(2)
+#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3
+
+#define VFE_0_CAMIF_CMD 0x478
+#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0
+#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1
+#define VFE_0_CAMIF_CMD_NO_CHANGE 3
+#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS BIT(2)
+#define VFE_0_CAMIF_CFG 0x47c
+#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN BIT(6)
+#define VFE_0_CAMIF_FRAME_CFG 0x484
+#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x488
+#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x48c
+#define VFE_0_CAMIF_SUBSAMPLE_CFG 0x490
+#define VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN 0x498
+#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x49c
+#define VFE_0_CAMIF_STATUS 0x4a4
+#define VFE_0_CAMIF_STATUS_HALT BIT(31)
+
+#define VFE_0_REG_UPDATE 0x4ac
+#define VFE_0_REG_UPDATE_RDIn(n) BIT(1 + (n))
+#define VFE_0_REG_UPDATE_line_n(n) \
+ ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n))
+
+#define VFE_0_DEMUX_CFG 0x560
+#define VFE_0_DEMUX_CFG_PERIOD 0x3
+#define VFE_0_DEMUX_GAIN_0 0x564
+#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0)
+#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16)
+#define VFE_0_DEMUX_GAIN_1 0x568
+#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0)
+#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16)
+#define VFE_0_DEMUX_EVEN_CFG 0x574
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9
+#define VFE_0_DEMUX_ODD_CFG 0x578
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9
+
+#define VFE_0_SCALE_ENC_Y_CFG 0x91c
+#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x920
+#define VFE_0_SCALE_ENC_Y_H_PHASE 0x924
+#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x934
+#define VFE_0_SCALE_ENC_Y_V_PHASE 0x938
+#define VFE_0_SCALE_ENC_CBCR_CFG 0x948
+#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x94c
+#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x950
+#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x960
+#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x964
+
+#define VFE_0_CROP_ENC_Y_WIDTH 0x974
+#define VFE_0_CROP_ENC_Y_HEIGHT 0x978
+#define VFE_0_CROP_ENC_CBCR_WIDTH 0x97c
+#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x980
+
+#define VFE_0_CLAMP_ENC_MAX_CFG 0x984
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0)
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8)
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16)
+#define VFE_0_CLAMP_ENC_MIN_CFG 0x988
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0)
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8)
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16)
+
+#define VFE_0_REALIGN_BUF_CFG 0xaac
+#define VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL BIT(2)
+#define VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL BIT(3)
+#define VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE BIT(4)
+
+#define VFE_0_BUS_IMAGE_MASTER_CMD 0xcec
+#define VFE_0_BUS_IMAGE_MASTER_n_SHIFT(x) (2 * (x))
+
+#define CAMIF_TIMEOUT_SLEEP_US 1000
+#define CAMIF_TIMEOUT_ALL_US 1000000
+
+#define MSM_VFE_VFE0_UB_SIZE 2047
+#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
+#define MSM_VFE_VFE1_UB_SIZE 1535
+#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
+
+static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev)
+{
+ u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
+
+ dev_err(dev, "VFE HW Version = 0x%08x\n", hw_version);
+}
+
+static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits & ~clr_bits, vfe->base + reg);
+}
+
+static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits | set_bits, vfe->base + reg);
+}
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+ u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_IDLE_CGC |
+ VFE_0_GLOBAL_RESET_CMD_DSP |
+ VFE_0_GLOBAL_RESET_CMD_TESTGEN |
+ VFE_0_GLOBAL_RESET_CMD_BUS_MISR |
+ VFE_0_GLOBAL_RESET_CMD_PM |
+ VFE_0_GLOBAL_RESET_CMD_REGISTER |
+ VFE_0_GLOBAL_RESET_CMD_BUS_BDG |
+ VFE_0_GLOBAL_RESET_CMD_BUS |
+ VFE_0_GLOBAL_RESET_CMD_CAMIF |
+ 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);
+}
+
+static void vfe_halt_request(struct vfe_device *vfe)
+{
+ writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ,
+ vfe->base + VFE_0_BUS_BDG_CMD);
+}
+
+static void vfe_halt_clear(struct vfe_device *vfe)
+{
+ writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
+}
+
+static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ if (enable)
+ vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT);
+ else
+ vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT);
+}
+
+#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
+
+static int vfe_word_per_line_by_pixel(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 int vfe_word_per_line_by_bytes(u32 bytes_per_line)
+{
+ return CALC_WORD(bytes_per_line, 1, 8);
+}
+
+static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane,
+ u16 *width, u16 *height, u16 *bytesperline)
+{
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[0].bytesperline;
+ if (plane == 1)
+ *height /= 2;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[0].bytesperline;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_UYVY:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[plane].bytesperline;
+ break;
+ }
+}
+
+static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm,
+ struct v4l2_pix_format_mplane *pix,
+ u8 plane, u32 enable)
+{
+ u32 reg;
+
+ if (enable) {
+ u16 width = 0, height = 0, bytesperline = 0, wpl;
+
+ vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline);
+
+ wpl = vfe_word_per_line_by_pixel(pix->pixelformat, width);
+
+ reg = height - 1;
+ reg |= ((wpl + 3) / 4 - 1) << 16;
+
+ writel_relaxed(reg, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+
+ wpl = vfe_word_per_line_by_bytes(bytesperline);
+
+ reg = 0x3;
+ reg |= (height - 1) << 2;
+ reg |= ((wpl + 1) / 2) << 16;
+
+ writel_relaxed(reg, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+ } else {
+ writel_relaxed(0, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+ writel_relaxed(0, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+ }
+}
+
+static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per)
+{
+ u32 reg;
+
+ reg = readl_relaxed(vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+
+ reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK);
+
+ reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT)
+ & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK;
+
+ writel_relaxed(reg,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+}
+
+static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm,
+ u32 pattern)
+{
+ writel_relaxed(pattern, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm));
+}
+
+static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm,
+ u16 offset, u16 depth)
+{
+ u32 reg;
+
+ reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) |
+ depth;
+ writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(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();
+}
+
+static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+ writel_relaxed(addr,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm));
+}
+
+static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+ writel_relaxed(addr,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm));
+}
+
+static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm)
+{
+ u32 reg;
+
+ reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS);
+
+ return (reg >> wm) & 0x1;
+}
+
+static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable)
+{
+ if (enable)
+ writel_relaxed(0x101, vfe->base + VFE_0_BUS_CFG);
+ else
+ writel_relaxed(0, vfe->base + VFE_0_BUS_CFG);
+}
+
+static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id)
+{
+ u32 reg;
+
+ reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg);
+
+ reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+ reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) &
+ VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+ switch (id) {
+ case VFE_LINE_RDI0:
+ default:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI1:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI2:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ }
+
+ if (wm % 2 == 1)
+ reg <<= 16;
+
+ vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
+
+static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm)
+{
+ writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm));
+}
+
+static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id)
+{
+ u32 reg;
+
+ reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+ vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+ switch (id) {
+ case VFE_LINE_RDI0:
+ default:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI1:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI2:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ }
+
+ if (wm % 2 == 1)
+ reg <<= 16;
+
+ vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
+
+static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output,
+ u8 enable)
+{
+ struct vfe_line *line = container_of(output, struct vfe_line, output);
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+
+ switch (p) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+
+ if (output->wm_idx[0] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+
+ reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16)
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
+
+ if (output->wm_idx[1] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]),
+ reg);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_UYVY:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN;
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
+
+ if (p == V4L2_PIX_FMT_YUYV || p == V4L2_PIX_FMT_YVYU)
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
+
+ if (output->wm_idx[0] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+ break;
+ default:
+ break;
+ }
+}
+
+static void vfe_set_realign_cfg(struct vfe_device *vfe, struct vfe_line *line,
+ u8 enable)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 val = VFE_0_MODULE_ZOOM_EN_REALIGN_BUF;
+
+ if (p != V4L2_PIX_FMT_YUYV && p != V4L2_PIX_FMT_YVYU &&
+ p != V4L2_PIX_FMT_VYUY && p != V4L2_PIX_FMT_UYVY)
+ return;
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val);
+ return;
+ }
+
+ val = VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE;
+
+ if (p == V4L2_PIX_FMT_UYVY || p == V4L2_PIX_FMT_YUYV)
+ val |= VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL;
+ else
+ val |= VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL;
+
+ writel_relaxed(val, vfe->base + VFE_0_REALIGN_BUF_CFG);
+}
+
+static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
+{
+ vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id),
+ VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK);
+
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id),
+ cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT);
+}
+
+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();
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id);
+}
+
+static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id line_id, u8 enable)
+{
+ u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) |
+ VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+ u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) |
+ VFE_0_IRQ_MASK_1_RDIn_SOF(line_id);
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ }
+}
+
+static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp,
+ enum vfe_line_id line_id, u8 enable)
+{
+ struct vfe_output *output = &vfe->line[line_id].output;
+ unsigned int i;
+ u32 irq_en0;
+ u32 irq_en1;
+ u32 comp_mask = 0;
+
+ irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF;
+ irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF;
+ irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp);
+ irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+ irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR;
+ for (i = 0; i < output->wm_num; i++) {
+ irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(output->wm_idx[i]);
+ comp_mask |= (1 << output->wm_idx[i]) << comp * 8;
+ }
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+ }
+}
+
+static void vfe_enable_irq_common(struct vfe_device *vfe)
+{
+ u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK;
+ u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION |
+ VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK;
+
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+}
+
+static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 val, even_cfg, odd_cfg;
+
+ writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG);
+
+ val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD;
+ writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0);
+
+ val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2;
+ writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1);
+
+ switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ default:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY;
+ break;
+ }
+
+ writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG);
+ writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
+}
+
+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;
+ u32 reg;
+ u16 input, output;
+ u8 interp_reso;
+ u32 phase_mult;
+
+ writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].width - 1;
+ output = line->compose.width - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ output = line->compose.height - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE);
+
+ writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].width - 1;
+ output = line->compose.width / 2 - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ output = line->compose.height - 1;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21)
+ output = line->compose.height / 2 - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE);
+}
+
+static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+ u16 first, last;
+
+ first = line->crop.left;
+ last = line->crop.left + line->crop.width - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH);
+
+ first = line->crop.top;
+ last = line->crop.top + line->crop.height - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT);
+
+ first = line->crop.left / 2;
+ last = line->crop.left / 2 + line->crop.width / 2 - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH);
+
+ first = line->crop.top;
+ last = line->crop.top + line->crop.height - 1;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) {
+ first = line->crop.top / 2;
+ last = line->crop.top / 2 + line->crop.height / 2 - 1;
+ }
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT);
+}
+
+static void vfe_set_clamp_cfg(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 |
+ VFE_0_CLAMP_ENC_MAX_CFG_CH1 |
+ VFE_0_CLAMP_ENC_MAX_CFG_CH2;
+
+ writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG);
+
+ val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 |
+ VFE_0_CLAMP_ENC_MIN_CFG_CH1 |
+ VFE_0_CLAMP_ENC_MIN_CFG_CH2;
+
+ writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG);
+}
+
+static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ /* empty */
+}
+
+static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 val;
+
+ switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ default:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY;
+ break;
+ }
+
+ val |= VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN;
+ writel_relaxed(val, vfe->base + VFE_0_CORE_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1;
+ val |= (line->fmt[MSM_VFE_PAD_SINK].height - 1) << 16;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN);
+
+ val = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val);
+
+ val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG);
+}
+
+static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable)
+{
+ u32 cmd;
+
+ 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)
+ cmd = VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY;
+ else
+ cmd = VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY;
+
+ writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
+}
+
+static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable)
+{
+ u32 val_lens = VFE_0_MODULE_LENS_EN_DEMUX |
+ VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE;
+ u32 val_zoom = VFE_0_MODULE_ZOOM_EN_SCALE_ENC |
+ VFE_0_MODULE_ZOOM_EN_CROP_ENC;
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_MODULE_LENS_EN, val_lens);
+ vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_MODULE_LENS_EN, val_lens);
+ vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom);
+ }
+}
+
+static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev)
+{
+ u32 val;
+ int ret;
+
+ ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS,
+ val,
+ (val & VFE_0_CAMIF_STATUS_HALT),
+ CAMIF_TIMEOUT_SLEEP_US,
+ CAMIF_TIMEOUT_ALL_US);
+ if (ret < 0)
+ dev_err(dev, "%s: camif stop timeout\n", __func__);
+
+ return ret;
+}
+
+/*
+ * vfe_isr - VFE module interrupt handler
+ * @irq: Interrupt line
+ * @dev: VFE device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ struct vfe_device *vfe = dev;
+ u32 value0, value1;
+ int i, j;
+
+ vfe->ops->isr_read(vfe, &value0, &value1);
+
+ dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n",
+ value0, value1);
+
+ if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK)
+ vfe->isr_ops.reset_ack(vfe);
+
+ if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION)
+ vfe->ops->violation_read(vfe);
+
+ 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_num; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
+ vfe->isr_ops.reg_update(vfe, i);
+
+ if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF)
+ vfe->isr_ops.sof(vfe, VFE_LINE_PIX);
+
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
+ if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i))
+ vfe->isr_ops.sof(vfe, i);
+
+ for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) {
+ vfe->isr_ops.comp_done(vfe, i);
+ for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++)
+ if (vfe->wm_output_map[j] == VFE_LINE_PIX)
+ value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j);
+ }
+
+ for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i))
+ vfe->isr_ops.wm_done(vfe, i);
+
+ return IRQ_HANDLED;
+}
+
+static u16 vfe_get_ub_size(u8 vfe_id)
+{
+ /* On VFE4.8 the ub-size is the same on both instances */
+ return MSM_VFE_VFE0_UB_SIZE_RDI;
+}
+
+static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ if (enable)
+ writel_relaxed(2 << VFE_0_BUS_IMAGE_MASTER_n_SHIFT(wm),
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_CMD);
+ else
+ writel_relaxed(1 << VFE_0_BUS_IMAGE_MASTER_n_SHIFT(wm),
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_CMD);
+
+ /* The WM must be enabled before sending other commands */
+ wmb();
+}
+
+static void vfe_set_qos(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG;
+ u32 val3 = VFE_0_BUS_BDG_QOS_CFG_3_CFG;
+ u32 val4 = VFE_0_BUS_BDG_QOS_CFG_4_CFG;
+ u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG;
+
+ 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);
+}
+
+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;
+
+ 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);
+}
+
+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);
+
+ /* Enforce barrier between local & global IRQ clear */
+ 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);
+}
+
+static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_8 = {
+ .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
+ .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
+ .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,
+ .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,
+ .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_8;
+ vfe->video_ops = vfe_video_ops_gen1;
+
+ vfe->line_num = VFE_LINE_NUM_GEN1;
+}
+
+const struct vfe_hw_ops vfe_ops_4_8 = {
+ .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,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
new file mode 100644
index 000000000000..4fd265d01883
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
@@ -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,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.h b/drivers/media/platform/qcom/camss/camss-vfe-gen1.h
new file mode 100644
index 000000000000..6d5f9656562c
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.h
@@ -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 */
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index f50e08c4fd11..e8cf66416004 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -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);
@@ -1332,35 +634,6 @@ exit:
}
/*
- * 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
* @state: vb2 buffer state of the returned buffers
@@ -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;
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
index 5bce6736e4bb..06da0b2918f9 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -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;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 0c679c97da6d..49e25c837bdc 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -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;