162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * camss-vfe-480.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v480 (SM8250) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2020-2021 Linaro Ltd. 862306a36Sopenharmony_ci * Copyright (C) 2021 Jonathan Marek 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/iopoll.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "camss.h" 1662306a36Sopenharmony_ci#include "camss-vfe.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* VFE 2/3 are lite and have a different register layout */ 1962306a36Sopenharmony_ci#define IS_LITE (vfe->id >= 2 ? 1 : 0) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define VFE_HW_VERSION (0x00) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define VFE_GLOBAL_RESET_CMD (IS_LITE ? 0x0c : 0x1c) 2462306a36Sopenharmony_ci#define GLOBAL_RESET_HW_AND_REG (IS_LITE ? BIT(1) : BIT(0)) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define VFE_REG_UPDATE_CMD (IS_LITE ? 0x20 : 0x34) 2762306a36Sopenharmony_cistatic inline int reg_update_rdi(struct vfe_device *vfe, int n) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return IS_LITE ? BIT(n) : BIT(1 + (n)); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define REG_UPDATE_RDI reg_update_rdi 3362306a36Sopenharmony_ci#define VFE_IRQ_CMD (IS_LITE ? 0x24 : 0x38) 3462306a36Sopenharmony_ci#define IRQ_CMD_GLOBAL_CLEAR BIT(0) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define VFE_IRQ_MASK(n) ((IS_LITE ? 0x28 : 0x3c) + (n) * 4) 3762306a36Sopenharmony_ci#define IRQ_MASK_0_RESET_ACK (IS_LITE ? BIT(17) : BIT(0)) 3862306a36Sopenharmony_ci#define IRQ_MASK_0_BUS_TOP_IRQ (IS_LITE ? BIT(4) : BIT(7)) 3962306a36Sopenharmony_ci#define VFE_IRQ_CLEAR(n) ((IS_LITE ? 0x34 : 0x48) + (n) * 4) 4062306a36Sopenharmony_ci#define VFE_IRQ_STATUS(n) ((IS_LITE ? 0x40 : 0x54) + (n) * 4) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define BUS_REG_BASE (IS_LITE ? 0x1a00 : 0xaa00) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08) 4562306a36Sopenharmony_ci#define WM_CGC_OVERRIDE_ALL (0x3FFFFFF) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define VFE_BUS_WM_TEST_BUS_CTRL (BUS_REG_BASE + 0xdc) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define VFE_BUS_IRQ_MASK(n) (BUS_REG_BASE + 0x18 + (n) * 4) 5062306a36Sopenharmony_cistatic inline int bus_irq_mask_0_rdi_rup(struct vfe_device *vfe, int n) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci return IS_LITE ? BIT(n) : BIT(3 + (n)); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define BUS_IRQ_MASK_0_RDI_RUP bus_irq_mask_0_rdi_rup 5662306a36Sopenharmony_cistatic inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return IS_LITE ? BIT(4 + (n)) : BIT(6 + (n)); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define BUS_IRQ_MASK_0_COMP_DONE bus_irq_mask_0_comp_done 6262306a36Sopenharmony_ci#define VFE_BUS_IRQ_CLEAR(n) (BUS_REG_BASE + 0x20 + (n) * 4) 6362306a36Sopenharmony_ci#define VFE_BUS_IRQ_STATUS(n) (BUS_REG_BASE + 0x28 + (n) * 4) 6462306a36Sopenharmony_ci#define VFE_BUS_IRQ_CLEAR_GLOBAL (BUS_REG_BASE + 0x30) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define VFE_BUS_WM_CFG(n) (BUS_REG_BASE + 0x200 + (n) * 0x100) 6762306a36Sopenharmony_ci#define WM_CFG_EN (0) 6862306a36Sopenharmony_ci#define WM_CFG_MODE (16) 6962306a36Sopenharmony_ci#define MODE_QCOM_PLAIN (0) 7062306a36Sopenharmony_ci#define MODE_MIPI_RAW (1) 7162306a36Sopenharmony_ci#define VFE_BUS_WM_IMAGE_ADDR(n) (BUS_REG_BASE + 0x204 + (n) * 0x100) 7262306a36Sopenharmony_ci#define VFE_BUS_WM_FRAME_INCR(n) (BUS_REG_BASE + 0x208 + (n) * 0x100) 7362306a36Sopenharmony_ci#define VFE_BUS_WM_IMAGE_CFG_0(n) (BUS_REG_BASE + 0x20c + (n) * 0x100) 7462306a36Sopenharmony_ci#define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF) 7562306a36Sopenharmony_ci#define VFE_BUS_WM_IMAGE_CFG_1(n) (BUS_REG_BASE + 0x210 + (n) * 0x100) 7662306a36Sopenharmony_ci#define VFE_BUS_WM_IMAGE_CFG_2(n) (BUS_REG_BASE + 0x214 + (n) * 0x100) 7762306a36Sopenharmony_ci#define VFE_BUS_WM_PACKER_CFG(n) (BUS_REG_BASE + 0x218 + (n) * 0x100) 7862306a36Sopenharmony_ci#define VFE_BUS_WM_HEADER_ADDR(n) (BUS_REG_BASE + 0x220 + (n) * 0x100) 7962306a36Sopenharmony_ci#define VFE_BUS_WM_HEADER_INCR(n) (BUS_REG_BASE + 0x224 + (n) * 0x100) 8062306a36Sopenharmony_ci#define VFE_BUS_WM_HEADER_CFG(n) (BUS_REG_BASE + 0x228 + (n) * 0x100) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(n) (BUS_REG_BASE + 0x230 + (n) * 0x100) 8362306a36Sopenharmony_ci#define VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(n) (BUS_REG_BASE + 0x234 + (n) * 0x100) 8462306a36Sopenharmony_ci#define VFE_BUS_WM_FRAMEDROP_PERIOD(n) (BUS_REG_BASE + 0x238 + (n) * 0x100) 8562306a36Sopenharmony_ci#define VFE_BUS_WM_FRAMEDROP_PATTERN(n) (BUS_REG_BASE + 0x23c + (n) * 0x100) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define VFE_BUS_WM_SYSTEM_CACHE_CFG(n) (BUS_REG_BASE + 0x260 + (n) * 0x100) 8862306a36Sopenharmony_ci#define VFE_BUS_WM_BURST_LIMIT(n) (BUS_REG_BASE + 0x264 + (n) * 0x100) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* for titan 480, each bus client is hardcoded to a specific path 9162306a36Sopenharmony_ci * and each bus client is part of a hardcoded "comp group" 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci#define RDI_WM(n) ((IS_LITE ? 0 : 23) + (n)) 9462306a36Sopenharmony_ci#define RDI_COMP_GROUP(n) ((IS_LITE ? 0 : 11) + (n)) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define MAX_VFE_OUTPUT_LINES 4 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic u32 vfe_hw_version(struct vfe_device *vfe) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci u32 gen = (hw_version >> 28) & 0xF; 10362306a36Sopenharmony_ci u32 rev = (hw_version >> 16) & 0xFFF; 10462306a36Sopenharmony_ci u32 step = hw_version & 0xFFFF; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci dev_dbg(vfe->camss->dev, "VFE HW Version = %u.%u.%u\n", gen, rev, step); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return hw_version; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void vfe_global_reset(struct vfe_device *vfe) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci writel_relaxed(IRQ_MASK_0_RESET_ACK, vfe->base + VFE_IRQ_MASK(0)); 11462306a36Sopenharmony_ci writel_relaxed(GLOBAL_RESET_HW_AND_REG, vfe->base + VFE_GLOBAL_RESET_CMD); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix = 12062306a36Sopenharmony_ci &line->video_out.active_fmt.fmt.pix_mp; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* no clock gating at bus input */ 12562306a36Sopenharmony_ci writel_relaxed(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci writel_relaxed(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci writel_relaxed(pix->plane_fmt[0].bytesperline * pix->height, 13062306a36Sopenharmony_ci vfe->base + VFE_BUS_WM_FRAME_INCR(wm)); 13162306a36Sopenharmony_ci writel_relaxed(0xf, vfe->base + VFE_BUS_WM_BURST_LIMIT(wm)); 13262306a36Sopenharmony_ci writel_relaxed(WM_IMAGE_CFG_0_DEFAULT_WIDTH, 13362306a36Sopenharmony_ci vfe->base + VFE_BUS_WM_IMAGE_CFG_0(wm)); 13462306a36Sopenharmony_ci writel_relaxed(pix->plane_fmt[0].bytesperline, 13562306a36Sopenharmony_ci vfe->base + VFE_BUS_WM_IMAGE_CFG_2(wm)); 13662306a36Sopenharmony_ci writel_relaxed(0, vfe->base + VFE_BUS_WM_PACKER_CFG(wm)); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* no dropped frames, one irq per frame */ 13962306a36Sopenharmony_ci writel_relaxed(0, vfe->base + VFE_BUS_WM_FRAMEDROP_PERIOD(wm)); 14062306a36Sopenharmony_ci writel_relaxed(1, vfe->base + VFE_BUS_WM_FRAMEDROP_PATTERN(wm)); 14162306a36Sopenharmony_ci writel_relaxed(0, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(wm)); 14262306a36Sopenharmony_ci writel_relaxed(1, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(wm)); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci writel_relaxed(1 << WM_CFG_EN | MODE_MIPI_RAW << WM_CFG_MODE, 14562306a36Sopenharmony_ci vfe->base + VFE_BUS_WM_CFG(wm)); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void vfe_wm_stop(struct vfe_device *vfe, u8 wm) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */ 15162306a36Sopenharmony_ci writel_relaxed(0, vfe->base + VFE_BUS_WM_CFG(wm)); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr, 15562306a36Sopenharmony_ci struct vfe_line *line) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */ 15862306a36Sopenharmony_ci writel_relaxed(addr, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm)); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci vfe->reg_update |= REG_UPDATE_RDI(vfe, line_id); 16462306a36Sopenharmony_ci writel_relaxed(vfe->reg_update, vfe->base + VFE_REG_UPDATE_CMD); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic inline void vfe_reg_update_clear(struct vfe_device *vfe, 16862306a36Sopenharmony_ci enum vfe_line_id line_id) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci vfe->reg_update &= ~REG_UPDATE_RDI(vfe, line_id); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void vfe_enable_irq_common(struct vfe_device *vfe) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci /* enable reset ack IRQ and top BUS status IRQ */ 17662306a36Sopenharmony_ci writel_relaxed(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ, 17762306a36Sopenharmony_ci vfe->base + VFE_IRQ_MASK(0)); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void vfe_enable_lines_irq(struct vfe_device *vfe) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int i; 18362306a36Sopenharmony_ci u32 bus_irq_mask = 0; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (i = 0; i < MAX_VFE_OUTPUT_LINES; i++) { 18662306a36Sopenharmony_ci /* Enable IRQ for newly added lines, but also keep already running lines's IRQ */ 18762306a36Sopenharmony_ci if (vfe->line[i].output.state == VFE_OUTPUT_RESERVED || 18862306a36Sopenharmony_ci vfe->line[i].output.state == VFE_OUTPUT_ON) { 18962306a36Sopenharmony_ci bus_irq_mask |= BUS_IRQ_MASK_0_RDI_RUP(vfe, i) 19062306a36Sopenharmony_ci | BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i)); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci writel_relaxed(bus_irq_mask, vfe->base + VFE_BUS_IRQ_MASK(0)); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id); 19862306a36Sopenharmony_cistatic void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* 20162306a36Sopenharmony_ci * vfe_isr - VFE module interrupt handler 20262306a36Sopenharmony_ci * @irq: Interrupt line 20362306a36Sopenharmony_ci * @dev: VFE device 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * Return IRQ_HANDLED on success 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_cistatic irqreturn_t vfe_isr(int irq, void *dev) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct vfe_device *vfe = dev; 21062306a36Sopenharmony_ci u32 status; 21162306a36Sopenharmony_ci int i; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci status = readl_relaxed(vfe->base + VFE_IRQ_STATUS(0)); 21462306a36Sopenharmony_ci writel_relaxed(status, vfe->base + VFE_IRQ_CLEAR(0)); 21562306a36Sopenharmony_ci writel_relaxed(IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_IRQ_CMD); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (status & IRQ_MASK_0_RESET_ACK) 21862306a36Sopenharmony_ci vfe_isr_reset_ack(vfe); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (status & IRQ_MASK_0_BUS_TOP_IRQ) { 22162306a36Sopenharmony_ci u32 status = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(0)); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci writel_relaxed(status, vfe->base + VFE_BUS_IRQ_CLEAR(0)); 22462306a36Sopenharmony_ci writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* Loop through all WMs IRQs */ 22762306a36Sopenharmony_ci for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) { 22862306a36Sopenharmony_ci if (status & BUS_IRQ_MASK_0_RDI_RUP(vfe, i)) 22962306a36Sopenharmony_ci vfe_isr_reg_update(vfe, i); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i))) 23262306a36Sopenharmony_ci vfe_isr_wm_done(vfe, i); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return IRQ_HANDLED; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* 24062306a36Sopenharmony_ci * vfe_halt - Trigger halt on VFE module and wait to complete 24162306a36Sopenharmony_ci * @vfe: VFE device 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_cistatic int vfe_halt(struct vfe_device *vfe) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci /* rely on vfe_disable_output() to stop the VFE */ 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int vfe_get_output(struct vfe_line *line) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 25462306a36Sopenharmony_ci struct vfe_output *output; 25562306a36Sopenharmony_ci unsigned long flags; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci output = &line->output; 26062306a36Sopenharmony_ci if (output->state > VFE_OUTPUT_RESERVED) { 26162306a36Sopenharmony_ci dev_err(vfe->camss->dev, "Output is running\n"); 26262306a36Sopenharmony_ci goto error; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci output->wm_num = 1; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Correspondence between VFE line number and WM number. 26862306a36Sopenharmony_ci * line 0 -> RDI 0, line 1 -> RDI1, line 2 -> RDI2, line 3 -> PIX/RDI3 26962306a36Sopenharmony_ci * Note this 1:1 mapping will not work for PIX streams. 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ci output->wm_idx[0] = line->id; 27262306a36Sopenharmony_ci vfe->wm_output_map[line->id] = line->id; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci output->drop_update_idx = 0; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cierror: 28162306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 28262306a36Sopenharmony_ci output->state = VFE_OUTPUT_OFF; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return -EINVAL; 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int vfe_enable_output(struct vfe_line *line) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 29062306a36Sopenharmony_ci struct vfe_output *output = &line->output; 29162306a36Sopenharmony_ci unsigned long flags; 29262306a36Sopenharmony_ci unsigned int i; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci vfe_reg_update_clear(vfe, line->id); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (output->state > VFE_OUTPUT_RESERVED) { 29962306a36Sopenharmony_ci dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", 30062306a36Sopenharmony_ci output->state); 30162306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 30262306a36Sopenharmony_ci return -EINVAL; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci WARN_ON(output->gen2.active_num); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci output->state = VFE_OUTPUT_ON; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci output->sequence = 0; 31062306a36Sopenharmony_ci output->wait_reg_update = 0; 31162306a36Sopenharmony_ci reinit_completion(&output->reg_update); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci vfe_wm_start(vfe, output->wm_idx[0], line); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 31662306a36Sopenharmony_ci output->buf[i] = vfe_buf_get_pending(output); 31762306a36Sopenharmony_ci if (!output->buf[i]) 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci output->gen2.active_num++; 32062306a36Sopenharmony_ci vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci vfe_reg_update(vfe, line->id); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void vfe_disable_output(struct vfe_line *line) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 33362306a36Sopenharmony_ci struct vfe_output *output = &line->output; 33462306a36Sopenharmony_ci unsigned long flags; 33562306a36Sopenharmony_ci unsigned int i; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 33862306a36Sopenharmony_ci for (i = 0; i < output->wm_num; i++) 33962306a36Sopenharmony_ci vfe_wm_stop(vfe, output->wm_idx[i]); 34062306a36Sopenharmony_ci output->gen2.active_num = 0; 34162306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci vfe_reset(vfe); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/* 34762306a36Sopenharmony_ci * vfe_enable - Enable streaming on VFE line 34862306a36Sopenharmony_ci * @line: VFE line 34962306a36Sopenharmony_ci * 35062306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_cistatic int vfe_enable(struct vfe_line *line) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci mutex_lock(&vfe->stream_lock); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!vfe->stream_count) 36062306a36Sopenharmony_ci vfe_enable_irq_common(vfe); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci vfe->stream_count++; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci vfe_enable_lines_irq(vfe); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci mutex_unlock(&vfe->stream_lock); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ret = vfe_get_output(line); 36962306a36Sopenharmony_ci if (ret < 0) 37062306a36Sopenharmony_ci goto error_get_output; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ret = vfe_enable_output(line); 37362306a36Sopenharmony_ci if (ret < 0) 37462306a36Sopenharmony_ci goto error_enable_output; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci vfe->was_streaming = 1; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cierror_enable_output: 38162306a36Sopenharmony_ci vfe_put_output(line); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cierror_get_output: 38462306a36Sopenharmony_ci mutex_lock(&vfe->stream_lock); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci vfe->stream_count--; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci mutex_unlock(&vfe->stream_lock); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/* 39462306a36Sopenharmony_ci * vfe_disable - Disable streaming on VFE line 39562306a36Sopenharmony_ci * @line: VFE line 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_cistatic int vfe_disable(struct vfe_line *line) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci vfe_disable_output(line); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci vfe_put_output(line); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci mutex_lock(&vfe->stream_lock); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci vfe->stream_count--; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci mutex_unlock(&vfe->stream_lock); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/* 41762306a36Sopenharmony_ci * vfe_isr_reg_update - Process reg update interrupt 41862306a36Sopenharmony_ci * @vfe: VFE Device 41962306a36Sopenharmony_ci * @line_id: VFE line 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_cistatic void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct vfe_output *output; 42462306a36Sopenharmony_ci unsigned long flags; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 42762306a36Sopenharmony_ci vfe_reg_update_clear(vfe, line_id); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci output = &vfe->line[line_id].output; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (output->wait_reg_update) { 43262306a36Sopenharmony_ci output->wait_reg_update = 0; 43362306a36Sopenharmony_ci complete(&output->reg_update); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* 44062306a36Sopenharmony_ci * vfe_isr_wm_done - Process write master done interrupt 44162306a36Sopenharmony_ci * @vfe: VFE Device 44262306a36Sopenharmony_ci * @wm: Write master id 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_cistatic void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]]; 44762306a36Sopenharmony_ci struct camss_buffer *ready_buf; 44862306a36Sopenharmony_ci struct vfe_output *output; 44962306a36Sopenharmony_ci unsigned long flags; 45062306a36Sopenharmony_ci u32 index; 45162306a36Sopenharmony_ci u64 ts = ktime_get_ns(); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (vfe->wm_output_map[wm] == VFE_LINE_NONE) { 45662306a36Sopenharmony_ci dev_err_ratelimited(vfe->camss->dev, 45762306a36Sopenharmony_ci "Received wm done for unmapped index\n"); 45862306a36Sopenharmony_ci goto out_unlock; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci output = &vfe->line[vfe->wm_output_map[wm]].output; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ready_buf = output->buf[0]; 46362306a36Sopenharmony_ci if (!ready_buf) { 46462306a36Sopenharmony_ci dev_err_ratelimited(vfe->camss->dev, 46562306a36Sopenharmony_ci "Missing ready buf %d!\n", output->state); 46662306a36Sopenharmony_ci goto out_unlock; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ready_buf->vb.vb2_buf.timestamp = ts; 47062306a36Sopenharmony_ci ready_buf->vb.sequence = output->sequence++; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci index = 0; 47362306a36Sopenharmony_ci output->buf[0] = output->buf[1]; 47462306a36Sopenharmony_ci if (output->buf[0]) 47562306a36Sopenharmony_ci index = 1; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci output->buf[index] = vfe_buf_get_pending(output); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (output->buf[index]) 48062306a36Sopenharmony_ci vfe_wm_update(vfe, output->wm_idx[0], output->buf[index]->addr[0], line); 48162306a36Sopenharmony_ci else 48262306a36Sopenharmony_ci output->gen2.active_num--; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ciout_unlock: 49162306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/* 49562306a36Sopenharmony_ci * vfe_pm_domain_off - Disable power domains specific to this VFE. 49662306a36Sopenharmony_ci * @vfe: VFE Device 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_cistatic void vfe_pm_domain_off(struct vfe_device *vfe) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct camss *camss = vfe->camss; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (vfe->id >= camss->vfe_num) 50362306a36Sopenharmony_ci return; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci device_link_del(camss->genpd_link[vfe->id]); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/* 50962306a36Sopenharmony_ci * vfe_pm_domain_on - Enable power domains specific to this VFE. 51062306a36Sopenharmony_ci * @vfe: VFE Device 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_cistatic int vfe_pm_domain_on(struct vfe_device *vfe) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct camss *camss = vfe->camss; 51562306a36Sopenharmony_ci enum vfe_line_id id = vfe->id; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (id >= camss->vfe_num) 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id], 52162306a36Sopenharmony_ci DL_FLAG_STATELESS | 52262306a36Sopenharmony_ci DL_FLAG_PM_RUNTIME | 52362306a36Sopenharmony_ci DL_FLAG_RPM_ACTIVE); 52462306a36Sopenharmony_ci if (!camss->genpd_link[id]) 52562306a36Sopenharmony_ci return -EINVAL; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return 0; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci/* 53162306a36Sopenharmony_ci * vfe_queue_buffer - Add empty buffer 53262306a36Sopenharmony_ci * @vid: Video device structure 53362306a36Sopenharmony_ci * @buf: Buffer to be enqueued 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * Add an empty buffer - depending on the current number of buffers it will be 53662306a36Sopenharmony_ci * put in pending buffer queue or directly given to the hardware to be filled. 53762306a36Sopenharmony_ci * 53862306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_cistatic int vfe_queue_buffer(struct camss_video *vid, 54162306a36Sopenharmony_ci struct camss_buffer *buf) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct vfe_line *line = container_of(vid, struct vfe_line, video_out); 54462306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 54562306a36Sopenharmony_ci struct vfe_output *output; 54662306a36Sopenharmony_ci unsigned long flags; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci output = &line->output; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) { 55362306a36Sopenharmony_ci output->buf[output->gen2.active_num++] = buf; 55462306a36Sopenharmony_ci vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line); 55562306a36Sopenharmony_ci } else { 55662306a36Sopenharmony_ci vfe_buf_add_pending(output, buf); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic const struct camss_video_ops vfe_video_ops_480 = { 56562306a36Sopenharmony_ci .queue_buffer = vfe_queue_buffer, 56662306a36Sopenharmony_ci .flush_buffers = vfe_flush_buffers, 56762306a36Sopenharmony_ci}; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic void vfe_subdev_init(struct device *dev, struct vfe_device *vfe) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci vfe->video_ops = vfe_video_ops_480; 57262306a36Sopenharmony_ci vfe->line_num = MAX_VFE_OUTPUT_LINES; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ciconst struct vfe_hw_ops vfe_ops_480 = { 57662306a36Sopenharmony_ci .global_reset = vfe_global_reset, 57762306a36Sopenharmony_ci .hw_version = vfe_hw_version, 57862306a36Sopenharmony_ci .isr = vfe_isr, 57962306a36Sopenharmony_ci .pm_domain_off = vfe_pm_domain_off, 58062306a36Sopenharmony_ci .pm_domain_on = vfe_pm_domain_on, 58162306a36Sopenharmony_ci .subdev_init = vfe_subdev_init, 58262306a36Sopenharmony_ci .vfe_disable = vfe_disable, 58362306a36Sopenharmony_ci .vfe_enable = vfe_enable, 58462306a36Sopenharmony_ci .vfe_halt = vfe_halt, 58562306a36Sopenharmony_ci}; 586