// SPDX-License-Identifier: GPL-2.0 /* * camss-vfe-680.c * * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v680 * * Copyright (C) 2025 Linaro Ltd. */ #include #include #include #include #include "camss.h" #include "camss-vfe.h" #define VFE_TOP_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x1c : 0x44) + (n) * 4) #define VFE_TOP_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x24 : 0x34) + (n) * 4) #define VFE_TOP_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x2c : 0x3c) + (n) * 4) #define VFE_IRQ1_SOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(2) : BIT(8)) << ((n) * 2)) #define VFE_IRQ1_EOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(3) : BIT(9)) << ((n) * 2)) #define VFE_TOP_IRQ_CMD(vfe) (vfe_is_lite(vfe) ? 0x38 : 0x30) #define VFE_TOP_IRQ_CMD_GLOBAL_CLEAR BIT(0) #define VFE_TOP_DIAG_CONFIG (vfe_is_lite(vfe) ? 0x40 : 0x50) #define VFE_TOP_DEBUG_11(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xcc) #define VFE_TOP_DEBUG_12(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd0) #define VFE_TOP_DEBUG_13(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd4) #define VFE_BUS_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x218 : 0xc18) + (n) * 4) #define VFE_BUS_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x220 : 0xc20) + (n) * 4) #define VFE_BUS_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x228 : 0xc28) + (n) * 4) #define VFE_BUS_IRQ_GLOBAL_CLEAR(vfe) (vfe_is_lite(vfe) ? 0x230 : 0xc30) #define VFE_BUS_WR_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x264 : 0xc64) #define VFE_BUS_WR_OVERFLOW_STATUS(vfe) (vfe_is_lite(vfe) ? 0x268 : 0xc68) #define VFE_BUS_WR_IMAGE_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x270 : 0xc70) #define VFE_BUS_WRITE_CLIENT_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x400 : 0xe00) + (c) * 0x100) #define VFE_BUS_WRITE_CLIENT_CFG_EN BIT(0) #define VFE_BUS_IMAGE_ADDR(vfe, c) ((vfe_is_lite(vfe) ? 0x404 : 0xe04) + (c) * 0x100) #define VFE_BUS_FRAME_INCR(vfe, c) ((vfe_is_lite(vfe) ? 0x408 : 0xe08) + (c) * 0x100) #define VFE_BUS_IMAGE_CFG0(vfe, c) ((vfe_is_lite(vfe) ? 0x40c : 0xe0c) + (c) * 0x100) #define VFE_BUS_IMAGE_CFG0_DATA(h, s) (((h) << 16) | ((s) >> 4)) #define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF) #define VFE_BUS_IMAGE_CFG1(vfe, c) ((vfe_is_lite(vfe) ? 0x410 : 0xe10) + (c) * 0x100) #define VFE_BUS_IMAGE_CFG2(vfe, c) ((vfe_is_lite(vfe) ? 0x414 : 0xe14) + (c) * 0x100) #define VFE_BUS_PACKER_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x418 : 0xe18) + (c) * 0x100) #define VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x430 : 0xe30) + (c) * 0x100) #define VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x434 : 0xe34) + (c) * 0x100) #define VFE_BUS_FRAMEDROP_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x438 : 0xe38) + (c) * 0x100) #define VFE_BUS_FRAMEDROP_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x43c : 0xe3c) + (c) * 0x100) #define VFE_BUS_MMU_PREFETCH_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x460 : 0xe60) + (c) * 0x100) #define VFE_BUS_MMU_PREFETCH_CFG_EN BIT(0) #define VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, c) ((vfe_is_lite(vfe) ? 0x464 : 0xe64) + (c) * 0x100) #define VFE_BUS_ADDR_STATUS0(vfe, c) ((vfe_is_lite(vfe) ? 0x470 : 0xe70) + (c) * 0x100) /* * TODO: differentiate the port id based on requested type of RDI, BHIST etc * * IFE write master IDs * * VIDEO_FULL_Y 0 * VIDEO_FULL_C 1 * VIDEO_DS_4:1 2 * VIDEO_DS_16:1 3 * DISPLAY_FULL_Y 4 * DISPLAY_FULL_C 5 * DISPLAY_DS_4:1 6 * DISPLAY_DS_16:1 7 * FD_Y 8 * FD_C 9 * PIXEL_RAW 10 * STATS_BE0 11 * STATS_BHIST0 12 * STATS_TINTLESS_BG 13 * STATS_AWB_BG 14 * STATS_AWB_BFW 15 * STATS_BAF 16 * STATS_BHIST 17 * STATS_RS 18 * STATS_IHIST 19 * SPARSE_PD 20 * PDAF_V2.0_PD_DATA 21 * PDAF_V2.0_SAD 22 * LCR 23 * RDI0 24 * RDI1 25 * RDI2 26 * LTM_STATS 27 * * IFE Lite write master IDs * * RDI0 0 * RDI1 1 * RDI2 2 * RDI3 3 * GAMMA 4 * BE 5 */ /* TODO: assign an ENUM in resources and use the provided master * id directly for RDI, STATS, AWB_BG, BHIST. * This macro only works because RDI is all we support right now. */ #define RDI_WM(n) ((vfe_is_lite(vfe) ? 0 : 24) + (n)) static void vfe_global_reset(struct vfe_device *vfe) { /* VFE680 has no global reset, simply report a completion */ complete(&vfe->reset_complete); } /* * 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) { return IRQ_HANDLED; } /* * 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) { /* rely on vfe_disable_output() to stop the VFE */ return 0; } static void vfe_disable_irq(struct vfe_device *vfe) { writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 0)); writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 1)); writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 0)); writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 1)); } static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr, struct vfe_line *line) { u8 wm = RDI_WM(rdi); writel(addr, vfe->base + VFE_BUS_IMAGE_ADDR(vfe, wm)); } static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line) { struct v4l2_pix_format_mplane *pix = &line->video_out.active_fmt.fmt.pix_mp; u32 stride = pix->plane_fmt[0].bytesperline; u32 cfg; u8 wm; cfg = VFE_BUS_IMAGE_CFG0_DATA(pix->height, stride); wm = RDI_WM(rdi); writel(cfg, vfe->base + VFE_BUS_IMAGE_CFG0(vfe, wm)); writel(0, vfe->base + VFE_BUS_IMAGE_CFG1(vfe, wm)); writel(stride, vfe->base + VFE_BUS_IMAGE_CFG2(vfe, wm)); writel(0, vfe->base + VFE_BUS_PACKER_CFG(vfe, wm)); /* Set total frame increment value */ writel(pix->plane_fmt[0].bytesperline * pix->height, vfe->base + VFE_BUS_FRAME_INCR(vfe, wm)); /* MMU */ writel(VFE_BUS_MMU_PREFETCH_CFG_EN, vfe->base + VFE_BUS_MMU_PREFETCH_CFG(vfe, wm)); writel(~0u, vfe->base + VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, wm)); /* no dropped frames, one irq per frame */ writel(1, vfe->base + VFE_BUS_FRAMEDROP_PATTERN(vfe, wm)); writel(0, vfe->base + VFE_BUS_FRAMEDROP_PERIOD(vfe, wm)); writel(1, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, wm)); writel(0, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, wm)); /* We don't process IRQs for VFE in RDI mode at the moment */ vfe_disable_irq(vfe); /* Enable WM */ writel(VFE_BUS_WRITE_CLIENT_CFG_EN, vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm)); dev_dbg(vfe->camss->dev, "RDI%d WM:%d width %d height %d stride %d\n", rdi, wm, pix->width, pix->height, stride); } static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi) { u8 wm = RDI_WM(rdi); writel(0, vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm)); } static const struct camss_video_ops vfe_video_ops_680 = { .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_680; } 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); } const struct vfe_hw_ops vfe_ops_680 = { .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, .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, .reg_update = vfe_reg_update, .reg_update_clear = vfe_reg_update_clear, };