14edc8eaeSJonathan Marek // SPDX-License-Identifier: GPL-2.0
24edc8eaeSJonathan Marek /*
34edc8eaeSJonathan Marek * camss-vfe-480.c
44edc8eaeSJonathan Marek *
54edc8eaeSJonathan Marek * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v480 (SM8250)
64edc8eaeSJonathan Marek *
74edc8eaeSJonathan Marek * Copyright (C) 2020-2021 Linaro Ltd.
84edc8eaeSJonathan Marek * Copyright (C) 2021 Jonathan Marek
94edc8eaeSJonathan Marek */
104edc8eaeSJonathan Marek
114edc8eaeSJonathan Marek #include <linux/interrupt.h>
124edc8eaeSJonathan Marek #include <linux/io.h>
134edc8eaeSJonathan Marek #include <linux/iopoll.h>
144edc8eaeSJonathan Marek
154edc8eaeSJonathan Marek #include "camss.h"
164edc8eaeSJonathan Marek #include "camss-vfe.h"
174edc8eaeSJonathan Marek
184edc8eaeSJonathan Marek /* VFE 2/3 are lite and have a different register layout */
194edc8eaeSJonathan Marek #define IS_LITE (vfe->id >= 2 ? 1 : 0)
204edc8eaeSJonathan Marek
214edc8eaeSJonathan Marek #define VFE_HW_VERSION (0x00)
224edc8eaeSJonathan Marek
234edc8eaeSJonathan Marek #define VFE_GLOBAL_RESET_CMD (IS_LITE ? 0x0c : 0x1c)
244edc8eaeSJonathan Marek #define GLOBAL_RESET_HW_AND_REG (IS_LITE ? BIT(1) : BIT(0))
254edc8eaeSJonathan Marek
264edc8eaeSJonathan Marek #define VFE_REG_UPDATE_CMD (IS_LITE ? 0x20 : 0x34)
reg_update_rdi(struct vfe_device * vfe,int n)274edc8eaeSJonathan Marek static inline int reg_update_rdi(struct vfe_device *vfe, int n)
284edc8eaeSJonathan Marek {
294edc8eaeSJonathan Marek return IS_LITE ? BIT(n) : BIT(1 + (n));
304edc8eaeSJonathan Marek }
314edc8eaeSJonathan Marek
324edc8eaeSJonathan Marek #define REG_UPDATE_RDI reg_update_rdi
334edc8eaeSJonathan Marek #define VFE_IRQ_CMD (IS_LITE ? 0x24 : 0x38)
344edc8eaeSJonathan Marek #define IRQ_CMD_GLOBAL_CLEAR BIT(0)
354edc8eaeSJonathan Marek
364edc8eaeSJonathan Marek #define VFE_IRQ_MASK(n) ((IS_LITE ? 0x28 : 0x3c) + (n) * 4)
374edc8eaeSJonathan Marek #define IRQ_MASK_0_RESET_ACK (IS_LITE ? BIT(17) : BIT(0))
384edc8eaeSJonathan Marek #define IRQ_MASK_0_BUS_TOP_IRQ (IS_LITE ? BIT(4) : BIT(7))
394edc8eaeSJonathan Marek #define VFE_IRQ_CLEAR(n) ((IS_LITE ? 0x34 : 0x48) + (n) * 4)
404edc8eaeSJonathan Marek #define VFE_IRQ_STATUS(n) ((IS_LITE ? 0x40 : 0x54) + (n) * 4)
414edc8eaeSJonathan Marek
424edc8eaeSJonathan Marek #define BUS_REG_BASE (IS_LITE ? 0x1a00 : 0xaa00)
434edc8eaeSJonathan Marek
444edc8eaeSJonathan Marek #define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08)
454edc8eaeSJonathan Marek #define WM_CGC_OVERRIDE_ALL (0x3FFFFFF)
464edc8eaeSJonathan Marek
474edc8eaeSJonathan Marek #define VFE_BUS_WM_TEST_BUS_CTRL (BUS_REG_BASE + 0xdc)
484edc8eaeSJonathan Marek
494edc8eaeSJonathan Marek #define VFE_BUS_IRQ_MASK(n) (BUS_REG_BASE + 0x18 + (n) * 4)
bus_irq_mask_0_rdi_rup(struct vfe_device * vfe,int n)504edc8eaeSJonathan Marek static inline int bus_irq_mask_0_rdi_rup(struct vfe_device *vfe, int n)
514edc8eaeSJonathan Marek {
524edc8eaeSJonathan Marek return IS_LITE ? BIT(n) : BIT(3 + (n));
534edc8eaeSJonathan Marek }
544edc8eaeSJonathan Marek
554edc8eaeSJonathan Marek #define BUS_IRQ_MASK_0_RDI_RUP bus_irq_mask_0_rdi_rup
bus_irq_mask_0_comp_done(struct vfe_device * vfe,int n)564edc8eaeSJonathan Marek static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n)
574edc8eaeSJonathan Marek {
584edc8eaeSJonathan Marek return IS_LITE ? BIT(4 + (n)) : BIT(6 + (n));
594edc8eaeSJonathan Marek }
604edc8eaeSJonathan Marek
614edc8eaeSJonathan Marek #define BUS_IRQ_MASK_0_COMP_DONE bus_irq_mask_0_comp_done
624edc8eaeSJonathan Marek #define VFE_BUS_IRQ_CLEAR(n) (BUS_REG_BASE + 0x20 + (n) * 4)
634edc8eaeSJonathan Marek #define VFE_BUS_IRQ_STATUS(n) (BUS_REG_BASE + 0x28 + (n) * 4)
644edc8eaeSJonathan Marek #define VFE_BUS_IRQ_CLEAR_GLOBAL (BUS_REG_BASE + 0x30)
654edc8eaeSJonathan Marek
664edc8eaeSJonathan Marek #define VFE_BUS_WM_CFG(n) (BUS_REG_BASE + 0x200 + (n) * 0x100)
674edc8eaeSJonathan Marek #define WM_CFG_EN (0)
684edc8eaeSJonathan Marek #define WM_CFG_MODE (16)
694edc8eaeSJonathan Marek #define MODE_QCOM_PLAIN (0)
704edc8eaeSJonathan Marek #define MODE_MIPI_RAW (1)
714edc8eaeSJonathan Marek #define VFE_BUS_WM_IMAGE_ADDR(n) (BUS_REG_BASE + 0x204 + (n) * 0x100)
724edc8eaeSJonathan Marek #define VFE_BUS_WM_FRAME_INCR(n) (BUS_REG_BASE + 0x208 + (n) * 0x100)
734edc8eaeSJonathan Marek #define VFE_BUS_WM_IMAGE_CFG_0(n) (BUS_REG_BASE + 0x20c + (n) * 0x100)
744edc8eaeSJonathan Marek #define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF)
754edc8eaeSJonathan Marek #define VFE_BUS_WM_IMAGE_CFG_1(n) (BUS_REG_BASE + 0x210 + (n) * 0x100)
764edc8eaeSJonathan Marek #define VFE_BUS_WM_IMAGE_CFG_2(n) (BUS_REG_BASE + 0x214 + (n) * 0x100)
774edc8eaeSJonathan Marek #define VFE_BUS_WM_PACKER_CFG(n) (BUS_REG_BASE + 0x218 + (n) * 0x100)
784edc8eaeSJonathan Marek #define VFE_BUS_WM_HEADER_ADDR(n) (BUS_REG_BASE + 0x220 + (n) * 0x100)
794edc8eaeSJonathan Marek #define VFE_BUS_WM_HEADER_INCR(n) (BUS_REG_BASE + 0x224 + (n) * 0x100)
804edc8eaeSJonathan Marek #define VFE_BUS_WM_HEADER_CFG(n) (BUS_REG_BASE + 0x228 + (n) * 0x100)
814edc8eaeSJonathan Marek
824edc8eaeSJonathan Marek #define VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(n) (BUS_REG_BASE + 0x230 + (n) * 0x100)
834edc8eaeSJonathan Marek #define VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(n) (BUS_REG_BASE + 0x234 + (n) * 0x100)
844edc8eaeSJonathan Marek #define VFE_BUS_WM_FRAMEDROP_PERIOD(n) (BUS_REG_BASE + 0x238 + (n) * 0x100)
854edc8eaeSJonathan Marek #define VFE_BUS_WM_FRAMEDROP_PATTERN(n) (BUS_REG_BASE + 0x23c + (n) * 0x100)
864edc8eaeSJonathan Marek
874edc8eaeSJonathan Marek #define VFE_BUS_WM_SYSTEM_CACHE_CFG(n) (BUS_REG_BASE + 0x260 + (n) * 0x100)
884edc8eaeSJonathan Marek #define VFE_BUS_WM_BURST_LIMIT(n) (BUS_REG_BASE + 0x264 + (n) * 0x100)
894edc8eaeSJonathan Marek
904edc8eaeSJonathan Marek /* for titan 480, each bus client is hardcoded to a specific path
914edc8eaeSJonathan Marek * and each bus client is part of a hardcoded "comp group"
924edc8eaeSJonathan Marek */
934edc8eaeSJonathan Marek #define RDI_WM(n) ((IS_LITE ? 0 : 23) + (n))
944edc8eaeSJonathan Marek #define RDI_COMP_GROUP(n) ((IS_LITE ? 0 : 11) + (n))
954edc8eaeSJonathan Marek
961c4abf02SMilen Mitkov #define MAX_VFE_OUTPUT_LINES 4
971c4abf02SMilen Mitkov
vfe_hw_version(struct vfe_device * vfe)984edc8eaeSJonathan Marek static u32 vfe_hw_version(struct vfe_device *vfe)
994edc8eaeSJonathan Marek {
1004edc8eaeSJonathan Marek u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
1014edc8eaeSJonathan Marek
1024edc8eaeSJonathan Marek u32 gen = (hw_version >> 28) & 0xF;
1034edc8eaeSJonathan Marek u32 rev = (hw_version >> 16) & 0xFFF;
1044edc8eaeSJonathan Marek u32 step = hw_version & 0xFFFF;
1054edc8eaeSJonathan Marek
1064edc8eaeSJonathan Marek dev_dbg(vfe->camss->dev, "VFE HW Version = %u.%u.%u\n", gen, rev, step);
1074edc8eaeSJonathan Marek
1084edc8eaeSJonathan Marek return hw_version;
1094edc8eaeSJonathan Marek }
1104edc8eaeSJonathan Marek
vfe_global_reset(struct vfe_device * vfe)1114edc8eaeSJonathan Marek static void vfe_global_reset(struct vfe_device *vfe)
1124edc8eaeSJonathan Marek {
1134edc8eaeSJonathan Marek writel_relaxed(IRQ_MASK_0_RESET_ACK, vfe->base + VFE_IRQ_MASK(0));
1144edc8eaeSJonathan Marek writel_relaxed(GLOBAL_RESET_HW_AND_REG, vfe->base + VFE_GLOBAL_RESET_CMD);
1154edc8eaeSJonathan Marek }
1164edc8eaeSJonathan Marek
vfe_wm_start(struct vfe_device * vfe,u8 wm,struct vfe_line * line)1174edc8eaeSJonathan Marek static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line)
1184edc8eaeSJonathan Marek {
1194edc8eaeSJonathan Marek struct v4l2_pix_format_mplane *pix =
1204edc8eaeSJonathan Marek &line->video_out.active_fmt.fmt.pix_mp;
1214edc8eaeSJonathan Marek
1224edc8eaeSJonathan Marek wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */
1234edc8eaeSJonathan Marek
1244edc8eaeSJonathan Marek /* no clock gating at bus input */
1254edc8eaeSJonathan Marek writel_relaxed(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE);
1264edc8eaeSJonathan Marek
1274edc8eaeSJonathan Marek writel_relaxed(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL);
1284edc8eaeSJonathan Marek
1294edc8eaeSJonathan Marek writel_relaxed(pix->plane_fmt[0].bytesperline * pix->height,
1304edc8eaeSJonathan Marek vfe->base + VFE_BUS_WM_FRAME_INCR(wm));
1314edc8eaeSJonathan Marek writel_relaxed(0xf, vfe->base + VFE_BUS_WM_BURST_LIMIT(wm));
1324edc8eaeSJonathan Marek writel_relaxed(WM_IMAGE_CFG_0_DEFAULT_WIDTH,
1334edc8eaeSJonathan Marek vfe->base + VFE_BUS_WM_IMAGE_CFG_0(wm));
1344edc8eaeSJonathan Marek writel_relaxed(pix->plane_fmt[0].bytesperline,
1354edc8eaeSJonathan Marek vfe->base + VFE_BUS_WM_IMAGE_CFG_2(wm));
1364edc8eaeSJonathan Marek writel_relaxed(0, vfe->base + VFE_BUS_WM_PACKER_CFG(wm));
1374edc8eaeSJonathan Marek
1384edc8eaeSJonathan Marek /* no dropped frames, one irq per frame */
1394edc8eaeSJonathan Marek writel_relaxed(0, vfe->base + VFE_BUS_WM_FRAMEDROP_PERIOD(wm));
1404edc8eaeSJonathan Marek writel_relaxed(1, vfe->base + VFE_BUS_WM_FRAMEDROP_PATTERN(wm));
1414edc8eaeSJonathan Marek writel_relaxed(0, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(wm));
1424edc8eaeSJonathan Marek writel_relaxed(1, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(wm));
1434edc8eaeSJonathan Marek
1444edc8eaeSJonathan Marek writel_relaxed(1 << WM_CFG_EN | MODE_MIPI_RAW << WM_CFG_MODE,
1454edc8eaeSJonathan Marek vfe->base + VFE_BUS_WM_CFG(wm));
1464edc8eaeSJonathan Marek }
1474edc8eaeSJonathan Marek
vfe_wm_stop(struct vfe_device * vfe,u8 wm)1484edc8eaeSJonathan Marek static void vfe_wm_stop(struct vfe_device *vfe, u8 wm)
1494edc8eaeSJonathan Marek {
1504edc8eaeSJonathan Marek wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */
1514edc8eaeSJonathan Marek writel_relaxed(0, vfe->base + VFE_BUS_WM_CFG(wm));
1524edc8eaeSJonathan Marek }
1534edc8eaeSJonathan Marek
vfe_wm_update(struct vfe_device * vfe,u8 wm,u32 addr,struct vfe_line * line)1544edc8eaeSJonathan Marek static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr,
1554edc8eaeSJonathan Marek struct vfe_line *line)
1564edc8eaeSJonathan Marek {
1574edc8eaeSJonathan Marek wm = RDI_WM(wm); /* map to actual WM used (from wm=RDI index) */
1584edc8eaeSJonathan Marek writel_relaxed(addr, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm));
1594edc8eaeSJonathan Marek }
1604edc8eaeSJonathan Marek
vfe_reg_update(struct vfe_device * vfe,enum vfe_line_id line_id)1614edc8eaeSJonathan Marek static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
1624edc8eaeSJonathan Marek {
1634edc8eaeSJonathan Marek vfe->reg_update |= REG_UPDATE_RDI(vfe, line_id);
1644edc8eaeSJonathan Marek writel_relaxed(vfe->reg_update, vfe->base + VFE_REG_UPDATE_CMD);
1654edc8eaeSJonathan Marek }
1664edc8eaeSJonathan Marek
vfe_reg_update_clear(struct vfe_device * vfe,enum vfe_line_id line_id)1674edc8eaeSJonathan Marek static inline void vfe_reg_update_clear(struct vfe_device *vfe,
1684edc8eaeSJonathan Marek enum vfe_line_id line_id)
1694edc8eaeSJonathan Marek {
1704edc8eaeSJonathan Marek vfe->reg_update &= ~REG_UPDATE_RDI(vfe, line_id);
1714edc8eaeSJonathan Marek }
1724edc8eaeSJonathan Marek
vfe_enable_irq_common(struct vfe_device * vfe)1734edc8eaeSJonathan Marek static void vfe_enable_irq_common(struct vfe_device *vfe)
1744edc8eaeSJonathan Marek {
1751c4abf02SMilen Mitkov /* enable reset ack IRQ and top BUS status IRQ */
1764edc8eaeSJonathan Marek writel_relaxed(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ,
1774edc8eaeSJonathan Marek vfe->base + VFE_IRQ_MASK(0));
1781c4abf02SMilen Mitkov }
1791c4abf02SMilen Mitkov
vfe_enable_lines_irq(struct vfe_device * vfe)1801c4abf02SMilen Mitkov static void vfe_enable_lines_irq(struct vfe_device *vfe)
1811c4abf02SMilen Mitkov {
1821c4abf02SMilen Mitkov int i;
1831c4abf02SMilen Mitkov u32 bus_irq_mask = 0;
1841c4abf02SMilen Mitkov
1851c4abf02SMilen Mitkov for (i = 0; i < MAX_VFE_OUTPUT_LINES; i++) {
1861c4abf02SMilen Mitkov /* Enable IRQ for newly added lines, but also keep already running lines's IRQ */
1871c4abf02SMilen Mitkov if (vfe->line[i].output.state == VFE_OUTPUT_RESERVED ||
1881c4abf02SMilen Mitkov vfe->line[i].output.state == VFE_OUTPUT_ON) {
1891c4abf02SMilen Mitkov bus_irq_mask |= BUS_IRQ_MASK_0_RDI_RUP(vfe, i)
1901c4abf02SMilen Mitkov | BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i));
1911c4abf02SMilen Mitkov }
1921c4abf02SMilen Mitkov }
1931c4abf02SMilen Mitkov
1941c4abf02SMilen Mitkov writel_relaxed(bus_irq_mask, vfe->base + VFE_BUS_IRQ_MASK(0));
1954edc8eaeSJonathan Marek }
1964edc8eaeSJonathan Marek
1974edc8eaeSJonathan Marek static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id);
1984edc8eaeSJonathan Marek static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm);
1994edc8eaeSJonathan Marek
2004edc8eaeSJonathan Marek /*
2014edc8eaeSJonathan Marek * vfe_isr - VFE module interrupt handler
2024edc8eaeSJonathan Marek * @irq: Interrupt line
2034edc8eaeSJonathan Marek * @dev: VFE device
2044edc8eaeSJonathan Marek *
2054edc8eaeSJonathan Marek * Return IRQ_HANDLED on success
2064edc8eaeSJonathan Marek */
vfe_isr(int irq,void * dev)2074edc8eaeSJonathan Marek static irqreturn_t vfe_isr(int irq, void *dev)
2084edc8eaeSJonathan Marek {
2094edc8eaeSJonathan Marek struct vfe_device *vfe = dev;
2104edc8eaeSJonathan Marek u32 status;
2111c4abf02SMilen Mitkov int i;
2124edc8eaeSJonathan Marek
2134edc8eaeSJonathan Marek status = readl_relaxed(vfe->base + VFE_IRQ_STATUS(0));
2144edc8eaeSJonathan Marek writel_relaxed(status, vfe->base + VFE_IRQ_CLEAR(0));
2154edc8eaeSJonathan Marek writel_relaxed(IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_IRQ_CMD);
2164edc8eaeSJonathan Marek
2174edc8eaeSJonathan Marek if (status & IRQ_MASK_0_RESET_ACK)
2184edc8eaeSJonathan Marek vfe_isr_reset_ack(vfe);
2194edc8eaeSJonathan Marek
2204edc8eaeSJonathan Marek if (status & IRQ_MASK_0_BUS_TOP_IRQ) {
2214edc8eaeSJonathan Marek u32 status = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(0));
2224edc8eaeSJonathan Marek
2234edc8eaeSJonathan Marek writel_relaxed(status, vfe->base + VFE_BUS_IRQ_CLEAR(0));
2244edc8eaeSJonathan Marek writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL);
2254edc8eaeSJonathan Marek
2261c4abf02SMilen Mitkov /* Loop through all WMs IRQs */
2271c4abf02SMilen Mitkov for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++) {
2281c4abf02SMilen Mitkov if (status & BUS_IRQ_MASK_0_RDI_RUP(vfe, i))
2291c4abf02SMilen Mitkov vfe_isr_reg_update(vfe, i);
2304edc8eaeSJonathan Marek
2311c4abf02SMilen Mitkov if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i)))
2321c4abf02SMilen Mitkov vfe_isr_wm_done(vfe, i);
2331c4abf02SMilen Mitkov }
2344edc8eaeSJonathan Marek }
2354edc8eaeSJonathan Marek
2364edc8eaeSJonathan Marek return IRQ_HANDLED;
2374edc8eaeSJonathan Marek }
2384edc8eaeSJonathan Marek
2394edc8eaeSJonathan Marek /*
2404edc8eaeSJonathan Marek * vfe_halt - Trigger halt on VFE module and wait to complete
2414edc8eaeSJonathan Marek * @vfe: VFE device
2424edc8eaeSJonathan Marek *
2434edc8eaeSJonathan Marek * Return 0 on success or a negative error code otherwise
2444edc8eaeSJonathan Marek */
vfe_halt(struct vfe_device * vfe)2454edc8eaeSJonathan Marek static int vfe_halt(struct vfe_device *vfe)
2464edc8eaeSJonathan Marek {
2474edc8eaeSJonathan Marek /* rely on vfe_disable_output() to stop the VFE */
2484edc8eaeSJonathan Marek return 0;
2494edc8eaeSJonathan Marek }
2504edc8eaeSJonathan Marek
vfe_get_output(struct vfe_line * line)2514edc8eaeSJonathan Marek static int vfe_get_output(struct vfe_line *line)
2524edc8eaeSJonathan Marek {
2534edc8eaeSJonathan Marek struct vfe_device *vfe = to_vfe(line);
2544edc8eaeSJonathan Marek struct vfe_output *output;
2554edc8eaeSJonathan Marek unsigned long flags;
2564edc8eaeSJonathan Marek
2574edc8eaeSJonathan Marek spin_lock_irqsave(&vfe->output_lock, flags);
2584edc8eaeSJonathan Marek
2594edc8eaeSJonathan Marek output = &line->output;
2608ce158c1SMilen Mitkov if (output->state > VFE_OUTPUT_RESERVED) {
2614edc8eaeSJonathan Marek dev_err(vfe->camss->dev, "Output is running\n");
2624edc8eaeSJonathan Marek goto error;
2634edc8eaeSJonathan Marek }
2644edc8eaeSJonathan Marek
2654edc8eaeSJonathan Marek output->wm_num = 1;
2664edc8eaeSJonathan Marek
2671c4abf02SMilen Mitkov /* Correspondence between VFE line number and WM number.
2681c4abf02SMilen Mitkov * line 0 -> RDI 0, line 1 -> RDI1, line 2 -> RDI2, line 3 -> PIX/RDI3
2691c4abf02SMilen Mitkov * Note this 1:1 mapping will not work for PIX streams.
2701c4abf02SMilen Mitkov */
2711c4abf02SMilen Mitkov output->wm_idx[0] = line->id;
2721c4abf02SMilen Mitkov vfe->wm_output_map[line->id] = line->id;
2734edc8eaeSJonathan Marek
2744edc8eaeSJonathan Marek output->drop_update_idx = 0;
2754edc8eaeSJonathan Marek
2764edc8eaeSJonathan Marek spin_unlock_irqrestore(&vfe->output_lock, flags);
2774edc8eaeSJonathan Marek
2784edc8eaeSJonathan Marek return 0;
2794edc8eaeSJonathan Marek
2804edc8eaeSJonathan Marek error:
2814edc8eaeSJonathan Marek spin_unlock_irqrestore(&vfe->output_lock, flags);
2821c4abf02SMilen Mitkov output->state = VFE_OUTPUT_OFF;
2834edc8eaeSJonathan Marek
2844edc8eaeSJonathan Marek return -EINVAL;
2854edc8eaeSJonathan Marek }
2864edc8eaeSJonathan Marek
vfe_enable_output(struct vfe_line * line)2874edc8eaeSJonathan Marek static int vfe_enable_output(struct vfe_line *line)
2884edc8eaeSJonathan Marek {
2894edc8eaeSJonathan Marek struct vfe_device *vfe = to_vfe(line);
2904edc8eaeSJonathan Marek struct vfe_output *output = &line->output;
2914edc8eaeSJonathan Marek unsigned long flags;
2924edc8eaeSJonathan Marek unsigned int i;
2934edc8eaeSJonathan Marek
2944edc8eaeSJonathan Marek spin_lock_irqsave(&vfe->output_lock, flags);
2954edc8eaeSJonathan Marek
2964edc8eaeSJonathan Marek vfe_reg_update_clear(vfe, line->id);
2974edc8eaeSJonathan Marek
2988ce158c1SMilen Mitkov if (output->state > VFE_OUTPUT_RESERVED) {
2994edc8eaeSJonathan Marek dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
3004edc8eaeSJonathan Marek output->state);
3014edc8eaeSJonathan Marek spin_unlock_irqrestore(&vfe->output_lock, flags);
3024edc8eaeSJonathan Marek return -EINVAL;
3034edc8eaeSJonathan Marek }
3044edc8eaeSJonathan Marek
3054edc8eaeSJonathan Marek WARN_ON(output->gen2.active_num);
3064edc8eaeSJonathan Marek
3074edc8eaeSJonathan Marek output->state = VFE_OUTPUT_ON;
3084edc8eaeSJonathan Marek
3094edc8eaeSJonathan Marek output->sequence = 0;
3104edc8eaeSJonathan Marek output->wait_reg_update = 0;
3114edc8eaeSJonathan Marek reinit_completion(&output->reg_update);
3124edc8eaeSJonathan Marek
3134edc8eaeSJonathan Marek vfe_wm_start(vfe, output->wm_idx[0], line);
3144edc8eaeSJonathan Marek
3154edc8eaeSJonathan Marek for (i = 0; i < 2; i++) {
3164edc8eaeSJonathan Marek output->buf[i] = vfe_buf_get_pending(output);
3174edc8eaeSJonathan Marek if (!output->buf[i])
3184edc8eaeSJonathan Marek break;
3194edc8eaeSJonathan Marek output->gen2.active_num++;
3204edc8eaeSJonathan Marek vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line);
3214edc8eaeSJonathan Marek }
3224edc8eaeSJonathan Marek
3234edc8eaeSJonathan Marek vfe_reg_update(vfe, line->id);
3244edc8eaeSJonathan Marek
3254edc8eaeSJonathan Marek spin_unlock_irqrestore(&vfe->output_lock, flags);
3264edc8eaeSJonathan Marek
3274edc8eaeSJonathan Marek return 0;
3284edc8eaeSJonathan Marek }
3294edc8eaeSJonathan Marek
vfe_disable_output(struct vfe_line * line)330*4dac469bSBryan O'Donoghue static void vfe_disable_output(struct vfe_line *line)
3314edc8eaeSJonathan Marek {
3324edc8eaeSJonathan Marek struct vfe_device *vfe = to_vfe(line);
3334edc8eaeSJonathan Marek struct vfe_output *output = &line->output;
3344edc8eaeSJonathan Marek unsigned long flags;
3354edc8eaeSJonathan Marek unsigned int i;
3364edc8eaeSJonathan Marek
3374edc8eaeSJonathan Marek spin_lock_irqsave(&vfe->output_lock, flags);
3384edc8eaeSJonathan Marek for (i = 0; i < output->wm_num; i++)
3394edc8eaeSJonathan Marek vfe_wm_stop(vfe, output->wm_idx[i]);
340*4dac469bSBryan O'Donoghue output->gen2.active_num = 0;
3414edc8eaeSJonathan Marek spin_unlock_irqrestore(&vfe->output_lock, flags);
3424edc8eaeSJonathan Marek
343*4dac469bSBryan O'Donoghue vfe_reset(vfe);
3444edc8eaeSJonathan Marek }
3454edc8eaeSJonathan Marek
3464edc8eaeSJonathan Marek /*
3474edc8eaeSJonathan Marek * vfe_enable - Enable streaming on VFE line
3484edc8eaeSJonathan Marek * @line: VFE line
3494edc8eaeSJonathan Marek *
3504edc8eaeSJonathan Marek * Return 0 on success or a negative error code otherwise
3514edc8eaeSJonathan Marek */
vfe_enable(struct vfe_line * line)3524edc8eaeSJonathan Marek static int vfe_enable(struct vfe_line *line)
3534edc8eaeSJonathan Marek {
3544edc8eaeSJonathan Marek struct vfe_device *vfe = to_vfe(line);
3554edc8eaeSJonathan Marek int ret;
3564edc8eaeSJonathan Marek
3574edc8eaeSJonathan Marek mutex_lock(&vfe->stream_lock);
3584edc8eaeSJonathan Marek
3594edc8eaeSJonathan Marek if (!vfe->stream_count)
3604edc8eaeSJonathan Marek vfe_enable_irq_common(vfe);
3614edc8eaeSJonathan Marek
3624edc8eaeSJonathan Marek vfe->stream_count++;
3634edc8eaeSJonathan Marek
3641c4abf02SMilen Mitkov vfe_enable_lines_irq(vfe);
3651c4abf02SMilen Mitkov
3664edc8eaeSJonathan Marek mutex_unlock(&vfe->stream_lock);
3674edc8eaeSJonathan Marek
3684edc8eaeSJonathan Marek ret = vfe_get_output(line);
3694edc8eaeSJonathan Marek if (ret < 0)
3704edc8eaeSJonathan Marek goto error_get_output;
3714edc8eaeSJonathan Marek
3724edc8eaeSJonathan Marek ret = vfe_enable_output(line);
3734edc8eaeSJonathan Marek if (ret < 0)
3744edc8eaeSJonathan Marek goto error_enable_output;
3754edc8eaeSJonathan Marek
3764edc8eaeSJonathan Marek vfe->was_streaming = 1;
3774edc8eaeSJonathan Marek
3784edc8eaeSJonathan Marek return 0;
3794edc8eaeSJonathan Marek
3804edc8eaeSJonathan Marek error_enable_output:
3814edc8eaeSJonathan Marek vfe_put_output(line);
3824edc8eaeSJonathan Marek
3834edc8eaeSJonathan Marek error_get_output:
3844edc8eaeSJonathan Marek mutex_lock(&vfe->stream_lock);
3854edc8eaeSJonathan Marek
3864edc8eaeSJonathan Marek vfe->stream_count--;
3874edc8eaeSJonathan Marek
3884edc8eaeSJonathan Marek mutex_unlock(&vfe->stream_lock);
3894edc8eaeSJonathan Marek
3904edc8eaeSJonathan Marek return ret;
3914edc8eaeSJonathan Marek }
3924edc8eaeSJonathan Marek
3934edc8eaeSJonathan Marek /*
3944edc8eaeSJonathan Marek * vfe_disable - Disable streaming on VFE line
3954edc8eaeSJonathan Marek * @line: VFE line
3964edc8eaeSJonathan Marek *
3974edc8eaeSJonathan Marek * Return 0 on success or a negative error code otherwise
3984edc8eaeSJonathan Marek */
vfe_disable(struct vfe_line * line)3994edc8eaeSJonathan Marek static int vfe_disable(struct vfe_line *line)
4004edc8eaeSJonathan Marek {
4014edc8eaeSJonathan Marek struct vfe_device *vfe = to_vfe(line);
4024edc8eaeSJonathan Marek
4034edc8eaeSJonathan Marek vfe_disable_output(line);
4044edc8eaeSJonathan Marek
4054edc8eaeSJonathan Marek vfe_put_output(line);
4064edc8eaeSJonathan Marek
4074edc8eaeSJonathan Marek mutex_lock(&vfe->stream_lock);
4084edc8eaeSJonathan Marek
4094edc8eaeSJonathan Marek vfe->stream_count--;
4104edc8eaeSJonathan Marek
4114edc8eaeSJonathan Marek mutex_unlock(&vfe->stream_lock);
4124edc8eaeSJonathan Marek
4134edc8eaeSJonathan Marek return 0;
4144edc8eaeSJonathan Marek }
4154edc8eaeSJonathan Marek
4164edc8eaeSJonathan Marek /*
4174edc8eaeSJonathan Marek * vfe_isr_reg_update - Process reg update interrupt
4184edc8eaeSJonathan Marek * @vfe: VFE Device
4194edc8eaeSJonathan Marek * @line_id: VFE line
4204edc8eaeSJonathan Marek */
vfe_isr_reg_update(struct vfe_device * vfe,enum vfe_line_id line_id)4214edc8eaeSJonathan Marek static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
4224edc8eaeSJonathan Marek {
4234edc8eaeSJonathan Marek struct vfe_output *output;
4244edc8eaeSJonathan Marek unsigned long flags;
4254edc8eaeSJonathan Marek
4264edc8eaeSJonathan Marek spin_lock_irqsave(&vfe->output_lock, flags);
4274edc8eaeSJonathan Marek vfe_reg_update_clear(vfe, line_id);
4284edc8eaeSJonathan Marek
4294edc8eaeSJonathan Marek output = &vfe->line[line_id].output;
4304edc8eaeSJonathan Marek
4314edc8eaeSJonathan Marek if (output->wait_reg_update) {
4324edc8eaeSJonathan Marek output->wait_reg_update = 0;
4334edc8eaeSJonathan Marek complete(&output->reg_update);
4344edc8eaeSJonathan Marek }
4354edc8eaeSJonathan Marek
4364edc8eaeSJonathan Marek spin_unlock_irqrestore(&vfe->output_lock, flags);
4374edc8eaeSJonathan Marek }
4384edc8eaeSJonathan Marek
4394edc8eaeSJonathan Marek /*
4404edc8eaeSJonathan Marek * vfe_isr_wm_done - Process write master done interrupt
4414edc8eaeSJonathan Marek * @vfe: VFE Device
4424edc8eaeSJonathan Marek * @wm: Write master id
4434edc8eaeSJonathan Marek */
vfe_isr_wm_done(struct vfe_device * vfe,u8 wm)4444edc8eaeSJonathan Marek static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
4454edc8eaeSJonathan Marek {
4464edc8eaeSJonathan Marek struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]];
4474edc8eaeSJonathan Marek struct camss_buffer *ready_buf;
4484edc8eaeSJonathan Marek struct vfe_output *output;
4494edc8eaeSJonathan Marek unsigned long flags;
4504edc8eaeSJonathan Marek u32 index;
4514edc8eaeSJonathan Marek u64 ts = ktime_get_ns();
4524edc8eaeSJonathan Marek
4534edc8eaeSJonathan Marek spin_lock_irqsave(&vfe->output_lock, flags);
4544edc8eaeSJonathan Marek
4554edc8eaeSJonathan Marek if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
4564edc8eaeSJonathan Marek dev_err_ratelimited(vfe->camss->dev,
4574edc8eaeSJonathan Marek "Received wm done for unmapped index\n");
4584edc8eaeSJonathan Marek goto out_unlock;
4594edc8eaeSJonathan Marek }
4604edc8eaeSJonathan Marek output = &vfe->line[vfe->wm_output_map[wm]].output;
4614edc8eaeSJonathan Marek
4624edc8eaeSJonathan Marek ready_buf = output->buf[0];
4634edc8eaeSJonathan Marek if (!ready_buf) {
4644edc8eaeSJonathan Marek dev_err_ratelimited(vfe->camss->dev,
4654edc8eaeSJonathan Marek "Missing ready buf %d!\n", output->state);
4664edc8eaeSJonathan Marek goto out_unlock;
4674edc8eaeSJonathan Marek }
4684edc8eaeSJonathan Marek
4694edc8eaeSJonathan Marek ready_buf->vb.vb2_buf.timestamp = ts;
4704edc8eaeSJonathan Marek ready_buf->vb.sequence = output->sequence++;
4714edc8eaeSJonathan Marek
4724edc8eaeSJonathan Marek index = 0;
4734edc8eaeSJonathan Marek output->buf[0] = output->buf[1];
4744edc8eaeSJonathan Marek if (output->buf[0])
4754edc8eaeSJonathan Marek index = 1;
4764edc8eaeSJonathan Marek
4774edc8eaeSJonathan Marek output->buf[index] = vfe_buf_get_pending(output);
4784edc8eaeSJonathan Marek
4794edc8eaeSJonathan Marek if (output->buf[index])
4804edc8eaeSJonathan Marek vfe_wm_update(vfe, output->wm_idx[0], output->buf[index]->addr[0], line);
4814edc8eaeSJonathan Marek else
4824edc8eaeSJonathan Marek output->gen2.active_num--;
4834edc8eaeSJonathan Marek
4844edc8eaeSJonathan Marek spin_unlock_irqrestore(&vfe->output_lock, flags);
4854edc8eaeSJonathan Marek
4864edc8eaeSJonathan Marek vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
4874edc8eaeSJonathan Marek
4884edc8eaeSJonathan Marek return;
4894edc8eaeSJonathan Marek
4904edc8eaeSJonathan Marek out_unlock:
4914edc8eaeSJonathan Marek spin_unlock_irqrestore(&vfe->output_lock, flags);
4924edc8eaeSJonathan Marek }
4934edc8eaeSJonathan Marek
4944edc8eaeSJonathan Marek /*
4954edc8eaeSJonathan Marek * vfe_pm_domain_off - Disable power domains specific to this VFE.
4964edc8eaeSJonathan Marek * @vfe: VFE Device
4974edc8eaeSJonathan Marek */
vfe_pm_domain_off(struct vfe_device * vfe)4984edc8eaeSJonathan Marek static void vfe_pm_domain_off(struct vfe_device *vfe)
4994edc8eaeSJonathan Marek {
50046cc0317SVladimir Zapolskiy struct camss *camss = vfe->camss;
50146cc0317SVladimir Zapolskiy
50246cc0317SVladimir Zapolskiy if (vfe->id >= camss->vfe_num)
50346cc0317SVladimir Zapolskiy return;
50446cc0317SVladimir Zapolskiy
50546cc0317SVladimir Zapolskiy device_link_del(camss->genpd_link[vfe->id]);
5064edc8eaeSJonathan Marek }
5074edc8eaeSJonathan Marek
5084edc8eaeSJonathan Marek /*
5094edc8eaeSJonathan Marek * vfe_pm_domain_on - Enable power domains specific to this VFE.
5104edc8eaeSJonathan Marek * @vfe: VFE Device
5114edc8eaeSJonathan Marek */
vfe_pm_domain_on(struct vfe_device * vfe)5124edc8eaeSJonathan Marek static int vfe_pm_domain_on(struct vfe_device *vfe)
5134edc8eaeSJonathan Marek {
51446cc0317SVladimir Zapolskiy struct camss *camss = vfe->camss;
51546cc0317SVladimir Zapolskiy enum vfe_line_id id = vfe->id;
51646cc0317SVladimir Zapolskiy
51746cc0317SVladimir Zapolskiy if (id >= camss->vfe_num)
51846cc0317SVladimir Zapolskiy return 0;
51946cc0317SVladimir Zapolskiy
52046cc0317SVladimir Zapolskiy camss->genpd_link[id] = device_link_add(camss->dev, camss->genpd[id],
52146cc0317SVladimir Zapolskiy DL_FLAG_STATELESS |
52246cc0317SVladimir Zapolskiy DL_FLAG_PM_RUNTIME |
52346cc0317SVladimir Zapolskiy DL_FLAG_RPM_ACTIVE);
52446cc0317SVladimir Zapolskiy if (!camss->genpd_link[id])
52546cc0317SVladimir Zapolskiy return -EINVAL;
52646cc0317SVladimir Zapolskiy
5274edc8eaeSJonathan Marek return 0;
5284edc8eaeSJonathan Marek }
5294edc8eaeSJonathan Marek
5304edc8eaeSJonathan Marek /*
5314edc8eaeSJonathan Marek * vfe_queue_buffer - Add empty buffer
5324edc8eaeSJonathan Marek * @vid: Video device structure
5334edc8eaeSJonathan Marek * @buf: Buffer to be enqueued
5344edc8eaeSJonathan Marek *
5354edc8eaeSJonathan Marek * Add an empty buffer - depending on the current number of buffers it will be
5364edc8eaeSJonathan Marek * put in pending buffer queue or directly given to the hardware to be filled.
5374edc8eaeSJonathan Marek *
5384edc8eaeSJonathan Marek * Return 0 on success or a negative error code otherwise
5394edc8eaeSJonathan Marek */
vfe_queue_buffer(struct camss_video * vid,struct camss_buffer * buf)5404edc8eaeSJonathan Marek static int vfe_queue_buffer(struct camss_video *vid,
5414edc8eaeSJonathan Marek struct camss_buffer *buf)
5424edc8eaeSJonathan Marek {
5434edc8eaeSJonathan Marek struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
5444edc8eaeSJonathan Marek struct vfe_device *vfe = to_vfe(line);
5454edc8eaeSJonathan Marek struct vfe_output *output;
5464edc8eaeSJonathan Marek unsigned long flags;
5474edc8eaeSJonathan Marek
5484edc8eaeSJonathan Marek output = &line->output;
5494edc8eaeSJonathan Marek
5504edc8eaeSJonathan Marek spin_lock_irqsave(&vfe->output_lock, flags);
5514edc8eaeSJonathan Marek
5524edc8eaeSJonathan Marek if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) {
5534edc8eaeSJonathan Marek output->buf[output->gen2.active_num++] = buf;
5544edc8eaeSJonathan Marek vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line);
5554edc8eaeSJonathan Marek } else {
5564edc8eaeSJonathan Marek vfe_buf_add_pending(output, buf);
5574edc8eaeSJonathan Marek }
5584edc8eaeSJonathan Marek
5594edc8eaeSJonathan Marek spin_unlock_irqrestore(&vfe->output_lock, flags);
5604edc8eaeSJonathan Marek
5614edc8eaeSJonathan Marek return 0;
5624edc8eaeSJonathan Marek }
5634edc8eaeSJonathan Marek
5644edc8eaeSJonathan Marek static const struct camss_video_ops vfe_video_ops_480 = {
5654edc8eaeSJonathan Marek .queue_buffer = vfe_queue_buffer,
5664edc8eaeSJonathan Marek .flush_buffers = vfe_flush_buffers,
5674edc8eaeSJonathan Marek };
5684edc8eaeSJonathan Marek
vfe_subdev_init(struct device * dev,struct vfe_device * vfe)5694edc8eaeSJonathan Marek static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
5704edc8eaeSJonathan Marek {
5714edc8eaeSJonathan Marek vfe->video_ops = vfe_video_ops_480;
5721c4abf02SMilen Mitkov vfe->line_num = MAX_VFE_OUTPUT_LINES;
5734edc8eaeSJonathan Marek }
5744edc8eaeSJonathan Marek
5754edc8eaeSJonathan Marek const struct vfe_hw_ops vfe_ops_480 = {
5764edc8eaeSJonathan Marek .global_reset = vfe_global_reset,
5774edc8eaeSJonathan Marek .hw_version = vfe_hw_version,
5784edc8eaeSJonathan Marek .isr = vfe_isr,
5794edc8eaeSJonathan Marek .pm_domain_off = vfe_pm_domain_off,
5804edc8eaeSJonathan Marek .pm_domain_on = vfe_pm_domain_on,
5814edc8eaeSJonathan Marek .subdev_init = vfe_subdev_init,
5824edc8eaeSJonathan Marek .vfe_disable = vfe_disable,
5834edc8eaeSJonathan Marek .vfe_enable = vfe_enable,
5844edc8eaeSJonathan Marek .vfe_halt = vfe_halt,
5854edc8eaeSJonathan Marek };
586