media: qcom: camss: Add support for VFE 780

Add support for VFE found on SM8550 (Titan 780). This implementation is
based on the titan 480 implementation. It supports the normal and lite
VFE.

Co-developed-by: Yongsheng Li <quic_yon@quicinc.com>
Signed-off-by: Yongsheng Li <quic_yon@quicinc.com>
Reviewed-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Signed-off-by: Depeng Shao <quic_depengs@quicinc.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
This commit is contained in:
Depeng Shao 2025-01-13 10:01:33 +05:30 committed by Hans Verkuil
parent d96fe1808d
commit 39e3f5bc0a
5 changed files with 284 additions and 0 deletions

View file

@ -17,6 +17,7 @@ qcom-camss-objs += \
camss-vfe-4-8.o \
camss-vfe-17x.o \
camss-vfe-480.o \
camss-vfe-780.o \
camss-vfe-gen1.o \
camss-vfe.o \
camss-video.o \

View file

@ -0,0 +1,159 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v780 (SM8550)
*
* Copyright (c) 2024 Qualcomm Technologies, Inc.
*/
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include "camss.h"
#include "camss-vfe.h"
#define BUS_REG_BASE (vfe_is_lite(vfe) ? 0x200 : 0xC00)
#define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08)
#define WM_CGC_OVERRIDE_ALL (0x7FFFFFF)
#define VFE_BUS_WM_TEST_BUS_CTRL (BUS_REG_BASE + 0xDC)
#define VFE_BUS_WM_CFG(n) (BUS_REG_BASE + 0x200 + (n) * 0x100)
#define WM_CFG_EN BIT(0)
#define WM_VIR_FRM_EN BIT(1)
#define WM_CFG_MODE BIT(16)
#define VFE_BUS_WM_IMAGE_ADDR(n) (BUS_REG_BASE + 0x204 + (n) * 0x100)
#define VFE_BUS_WM_FRAME_INCR(n) (BUS_REG_BASE + 0x208 + (n) * 0x100)
#define VFE_BUS_WM_IMAGE_CFG_0(n) (BUS_REG_BASE + 0x20c + (n) * 0x100)
#define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF)
#define VFE_BUS_WM_IMAGE_CFG_2(n) (BUS_REG_BASE + 0x214 + (n) * 0x100)
#define WM_IMAGE_CFG_2_DEFAULT_STRIDE (0xFFFF)
#define VFE_BUS_WM_PACKER_CFG(n) (BUS_REG_BASE + 0x218 + (n) * 0x100)
#define VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(n) (BUS_REG_BASE + 0x230 + (n) * 0x100)
#define VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(n) (BUS_REG_BASE + 0x234 + (n) * 0x100)
#define VFE_BUS_WM_FRAMEDROP_PERIOD(n) (BUS_REG_BASE + 0x238 + (n) * 0x100)
#define VFE_BUS_WM_FRAMEDROP_PATTERN(n) (BUS_REG_BASE + 0x23c + (n) * 0x100)
#define VFE_BUS_WM_MMU_PREFETCH_CFG(n) (BUS_REG_BASE + 0x260 + (n) * 0x100)
#define VFE_BUS_WM_MMU_PREFETCH_MAX_OFFSET(n) (BUS_REG_BASE + 0x264 + (n) * 0x100)
/*
* Bus client mapping:
*
* Full VFE:
* 23 = RDI0, 24 = RDI1, 25 = RDI2
*
* VFE LITE:
* 0 = RDI0, 1 = RDI1, 2 = RDI3, 4 = RDI4
*/
#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0x0 : 0x17) + (n))
static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line)
{
struct v4l2_pix_format_mplane *pix =
&line->video_out.active_fmt.fmt.pix_mp;
wm = RDI_WM(wm);
/* no clock gating at bus input */
writel(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE);
writel(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL);
writel(ALIGN(pix->plane_fmt[0].bytesperline, 16) * pix->height >> 8,
vfe->base + VFE_BUS_WM_FRAME_INCR(wm));
writel((WM_IMAGE_CFG_0_DEFAULT_WIDTH & 0xFFFF),
vfe->base + VFE_BUS_WM_IMAGE_CFG_0(wm));
writel(WM_IMAGE_CFG_2_DEFAULT_STRIDE,
vfe->base + VFE_BUS_WM_IMAGE_CFG_2(wm));
writel(0, vfe->base + VFE_BUS_WM_PACKER_CFG(wm));
/* no dropped frames, one irq per frame */
writel(0, vfe->base + VFE_BUS_WM_FRAMEDROP_PERIOD(wm));
writel(1, vfe->base + VFE_BUS_WM_FRAMEDROP_PATTERN(wm));
writel(0, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(wm));
writel(1, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(wm));
writel(1, vfe->base + VFE_BUS_WM_MMU_PREFETCH_CFG(wm));
writel(0xFFFFFFFF, vfe->base + VFE_BUS_WM_MMU_PREFETCH_MAX_OFFSET(wm));
writel(WM_CFG_EN | WM_CFG_MODE, vfe->base + VFE_BUS_WM_CFG(wm));
}
static void vfe_wm_stop(struct vfe_device *vfe, u8 wm)
{
wm = RDI_WM(wm);
writel(0, vfe->base + VFE_BUS_WM_CFG(wm));
}
static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr,
struct vfe_line *line)
{
wm = RDI_WM(wm);
writel((addr >> 8) & 0xFFFFFFFF, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm));
dev_dbg(vfe->camss->dev, "wm:%d, image buf addr:0x%x\n",
wm, addr);
}
static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
{
int port_id = line_id;
camss_reg_update(vfe->camss, vfe->id, port_id, false);
}
static inline void vfe_reg_update_clear(struct vfe_device *vfe,
enum vfe_line_id line_id)
{
int port_id = line_id;
camss_reg_update(vfe->camss, vfe->id, port_id, true);
}
static const struct camss_video_ops vfe_video_ops_780 = {
.queue_buffer = vfe_queue_buffer_v2,
.flush_buffers = vfe_flush_buffers,
};
static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
{
vfe->video_ops = vfe_video_ops_780;
}
static void vfe_global_reset(struct vfe_device *vfe)
{
vfe_isr_reset_ack(vfe);
}
static irqreturn_t vfe_isr(int irq, void *dev)
{
/* nop */
return IRQ_HANDLED;
}
static int vfe_halt(struct vfe_device *vfe)
{
/* rely on vfe_disable_output() to stop the VFE */
return 0;
}
const struct vfe_hw_ops vfe_ops_780 = {
.global_reset = vfe_global_reset,
.hw_version = vfe_hw_version,
.isr = vfe_isr,
.pm_domain_off = vfe_pm_domain_off,
.pm_domain_on = vfe_pm_domain_on,
.reg_update = vfe_reg_update,
.reg_update_clear = vfe_reg_update_clear,
.subdev_init = vfe_subdev_init,
.vfe_disable = vfe_disable,
.vfe_enable = vfe_enable_v2,
.vfe_halt = vfe_halt,
.vfe_wm_start = vfe_wm_start,
.vfe_wm_stop = vfe_wm_stop,
.vfe_buf_done = vfe_buf_done,
.vfe_wm_update = vfe_wm_update,
};

View file

@ -345,6 +345,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
case CAMSS_8250:
case CAMSS_8280XP:
case CAMSS_845:
case CAMSS_8550:
switch (sink_code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
{
@ -1970,6 +1971,7 @@ static int vfe_bpl_align(struct vfe_device *vfe)
case CAMSS_8250:
case CAMSS_8280XP:
case CAMSS_845:
case CAMSS_8550:
ret = 16;
break;
default:

View file

@ -243,6 +243,7 @@ extern const struct vfe_hw_ops vfe_ops_4_7;
extern const struct vfe_hw_ops vfe_ops_4_8;
extern const struct vfe_hw_ops vfe_ops_170;
extern const struct vfe_hw_ops vfe_ops_480;
extern const struct vfe_hw_ops vfe_ops_780;
int vfe_get(struct vfe_device *vfe);
void vfe_put(struct vfe_device *vfe);

View file

@ -2132,6 +2132,125 @@ static const struct camss_subdev_resources csid_res_8550[] = {
}
};
static const struct camss_subdev_resources vfe_res_8550[] = {
/* VFE0 */
{
.regulators = {},
.clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe0_fast_ahb",
"vfe0", "cpas_vfe0", "camnoc_axi" },
.clock_rate = { { 0 },
{ 80000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 },
{ 466000000, 594000000, 675000000, 785000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 } },
.reg = { "vfe0" },
.interrupt = { "vfe0" },
.vfe = {
.line_num = 3,
.is_lite = false,
.has_pd = true,
.pd_name = "ife0",
.hw_ops = &vfe_ops_780,
.formats_rdi = &vfe_formats_rdi_845,
.formats_pix = &vfe_formats_pix_845
}
},
/* VFE1 */
{
.regulators = {},
.clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe1_fast_ahb",
"vfe1", "cpas_vfe1", "camnoc_axi" },
.clock_rate = { { 0 },
{ 80000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 },
{ 466000000, 594000000, 675000000, 785000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 } },
.reg = { "vfe1" },
.interrupt = { "vfe1" },
.vfe = {
.line_num = 3,
.is_lite = false,
.has_pd = true,
.pd_name = "ife1",
.hw_ops = &vfe_ops_780,
.formats_rdi = &vfe_formats_rdi_845,
.formats_pix = &vfe_formats_pix_845
}
},
/* VFE2 */
{
.regulators = {},
.clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe2_fast_ahb",
"vfe2", "cpas_vfe2", "camnoc_axi" },
.clock_rate = { { 0 },
{ 80000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 },
{ 466000000, 594000000, 675000000, 785000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 } },
.reg = { "vfe2" },
.interrupt = { "vfe2" },
.vfe = {
.line_num = 3,
.is_lite = false,
.has_pd = true,
.pd_name = "ife2",
.hw_ops = &vfe_ops_780,
.formats_rdi = &vfe_formats_rdi_845,
.formats_pix = &vfe_formats_pix_845
}
},
/* VFE3 lite */
{
.regulators = {},
.clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb",
"vfe_lite", "cpas_ife_lite", "camnoc_axi" },
.clock_rate = { { 0 },
{ 80000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 },
{ 400000000, 480000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 } },
.reg = { "vfe_lite0" },
.interrupt = { "vfe_lite0" },
.vfe = {
.line_num = 4,
.is_lite = true,
.hw_ops = &vfe_ops_780,
.formats_rdi = &vfe_formats_rdi_845,
.formats_pix = &vfe_formats_pix_845
}
},
/* VFE4 lite */
{
.regulators = {},
.clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb",
"vfe_lite", "cpas_ife_lite", "camnoc_axi" },
.clock_rate = { { 0 },
{ 80000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 },
{ 400000000, 480000000 },
{ 300000000, 400000000 },
{ 300000000, 400000000 } },
.reg = { "vfe_lite1" },
.interrupt = { "vfe_lite1" },
.vfe = {
.line_num = 4,
.is_lite = true,
.hw_ops = &vfe_ops_780,
.formats_rdi = &vfe_formats_rdi_845,
.formats_pix = &vfe_formats_pix_845
}
},
};
static const struct resources_icc icc_res_sm8550[] = {
{
.name = "ahb",
@ -3186,11 +3305,13 @@ static const struct camss_resources sm8550_resources = {
.pd_name = "top",
.csiphy_res = csiphy_res_8550,
.csid_res = csid_res_8550,
.vfe_res = vfe_res_8550,
.csid_wrapper_res = &csid_wrapper_res_sm8550,
.icc_res = icc_res_sm8550,
.icc_path_num = ARRAY_SIZE(icc_res_sm8550),
.csiphy_num = ARRAY_SIZE(csiphy_res_8550),
.csid_num = ARRAY_SIZE(csid_res_8550),
.vfe_num = ARRAY_SIZE(vfe_res_8550),
.link_entities = camss_link_entities
};