162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ispccp2.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * TI OMAP3 ISP - CCP2 module
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation
862306a36Sopenharmony_ci * Copyright (C) 2010 Texas Instruments, Inc.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
1162306a36Sopenharmony_ci *	     Sakari Ailus <sakari.ailus@iki.fi>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/device.h>
1662306a36Sopenharmony_ci#include <linux/mm.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci#include <linux/uaccess.h>
2062306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "isp.h"
2362306a36Sopenharmony_ci#include "ispreg.h"
2462306a36Sopenharmony_ci#include "ispccp2.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* Number of LCX channels */
2762306a36Sopenharmony_ci#define CCP2_LCx_CHANS_NUM			3
2862306a36Sopenharmony_ci/* Max/Min size for CCP2 video port */
2962306a36Sopenharmony_ci#define ISPCCP2_DAT_START_MIN			0
3062306a36Sopenharmony_ci#define ISPCCP2_DAT_START_MAX			4095
3162306a36Sopenharmony_ci#define ISPCCP2_DAT_SIZE_MIN			0
3262306a36Sopenharmony_ci#define ISPCCP2_DAT_SIZE_MAX			4095
3362306a36Sopenharmony_ci#define ISPCCP2_VPCLK_FRACDIV			65536
3462306a36Sopenharmony_ci#define ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP	0x12
3562306a36Sopenharmony_ci#define ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP	0x16
3662306a36Sopenharmony_ci/* Max/Min size for CCP2 memory channel */
3762306a36Sopenharmony_ci#define ISPCCP2_LCM_HSIZE_COUNT_MIN		16
3862306a36Sopenharmony_ci#define ISPCCP2_LCM_HSIZE_COUNT_MAX		8191
3962306a36Sopenharmony_ci#define ISPCCP2_LCM_HSIZE_SKIP_MIN		0
4062306a36Sopenharmony_ci#define ISPCCP2_LCM_HSIZE_SKIP_MAX		8191
4162306a36Sopenharmony_ci#define ISPCCP2_LCM_VSIZE_MIN			1
4262306a36Sopenharmony_ci#define ISPCCP2_LCM_VSIZE_MAX			8191
4362306a36Sopenharmony_ci#define ISPCCP2_LCM_HWORDS_MIN			1
4462306a36Sopenharmony_ci#define ISPCCP2_LCM_HWORDS_MAX			4095
4562306a36Sopenharmony_ci#define ISPCCP2_LCM_CTRL_BURST_SIZE_32X		5
4662306a36Sopenharmony_ci#define ISPCCP2_LCM_CTRL_READ_THROTTLE_FULL	0
4762306a36Sopenharmony_ci#define ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10	2
4862306a36Sopenharmony_ci#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8	2
4962306a36Sopenharmony_ci#define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10	3
5062306a36Sopenharmony_ci#define ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10	3
5162306a36Sopenharmony_ci#define ISPCCP2_LCM_CTRL_DST_PORT_VP		0
5262306a36Sopenharmony_ci#define ISPCCP2_LCM_CTRL_DST_PORT_MEM		1
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* Set only the required bits */
5562306a36Sopenharmony_ci#define BIT_SET(var, shift, mask, val)			\
5662306a36Sopenharmony_ci	do {						\
5762306a36Sopenharmony_ci		var = ((var) & ~((mask) << (shift)))	\
5862306a36Sopenharmony_ci			| ((val) << (shift));		\
5962306a36Sopenharmony_ci	} while (0)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * ccp2_print_status - Print current CCP2 module register values.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_ci#define CCP2_PRINT_REGISTER(isp, name)\
6562306a36Sopenharmony_ci	dev_dbg(isp->dev, "###CCP2 " #name "=0x%08x\n", \
6662306a36Sopenharmony_ci		isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_##name))
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void ccp2_print_status(struct isp_ccp2_device *ccp2)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	dev_dbg(isp->dev, "-------------CCP2 Register dump-------------\n");
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, SYSCONFIG);
7562306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, SYSSTATUS);
7662306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LC01_IRQENABLE);
7762306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LC01_IRQSTATUS);
7862306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LC23_IRQENABLE);
7962306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LC23_IRQSTATUS);
8062306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_IRQENABLE);
8162306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_IRQSTATUS);
8262306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, CTRL);
8362306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_CTRL(0));
8462306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_CODE(0));
8562306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_STAT_START(0));
8662306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_STAT_SIZE(0));
8762306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_SOF_ADDR(0));
8862306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_EOF_ADDR(0));
8962306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_DAT_START(0));
9062306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_DAT_SIZE(0));
9162306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_DAT_PING_ADDR(0));
9262306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_DAT_PONG_ADDR(0));
9362306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCx_DAT_OFST(0));
9462306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_CTRL);
9562306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_VSIZE);
9662306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_HSIZE);
9762306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_PREFETCH);
9862306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_SRC_ADDR);
9962306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_SRC_OFST);
10062306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_DST_ADDR);
10162306a36Sopenharmony_ci	CCP2_PRINT_REGISTER(isp, LCM_DST_OFST);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	dev_dbg(isp->dev, "--------------------------------------------\n");
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/*
10762306a36Sopenharmony_ci * ccp2_reset - Reset the CCP2
10862306a36Sopenharmony_ci * @ccp2: pointer to ISP CCP2 device
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_cistatic void ccp2_reset(struct isp_ccp2_device *ccp2)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
11362306a36Sopenharmony_ci	int i = 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* Reset the CSI1/CCP2B and wait for reset to complete */
11662306a36Sopenharmony_ci	isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG,
11762306a36Sopenharmony_ci		    ISPCCP2_SYSCONFIG_SOFT_RESET);
11862306a36Sopenharmony_ci	while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSSTATUS) &
11962306a36Sopenharmony_ci		 ISPCCP2_SYSSTATUS_RESET_DONE)) {
12062306a36Sopenharmony_ci		udelay(10);
12162306a36Sopenharmony_ci		if (i++ > 10) {  /* try read 10 times */
12262306a36Sopenharmony_ci			dev_warn(isp->dev,
12362306a36Sopenharmony_ci				"omap3_isp: timeout waiting for ccp2 reset\n");
12462306a36Sopenharmony_ci			break;
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * ccp2_pwr_cfg - Configure the power mode settings
13162306a36Sopenharmony_ci * @ccp2: pointer to ISP CCP2 device
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_cistatic void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	isp_reg_writel(isp, ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART |
13862306a36Sopenharmony_ci			((isp->revision == ISP_REVISION_15_0 && isp->autoidle) ?
13962306a36Sopenharmony_ci			  ISPCCP2_SYSCONFIG_AUTO_IDLE : 0),
14062306a36Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCP2, ISPCCP2_SYSCONFIG);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * ccp2_if_enable - Enable CCP2 interface.
14562306a36Sopenharmony_ci * @ccp2: pointer to ISP CCP2 device
14662306a36Sopenharmony_ci * @enable: enable/disable flag
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_cistatic int ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
15162306a36Sopenharmony_ci	int ret;
15262306a36Sopenharmony_ci	int i;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (enable && ccp2->vdds_csib) {
15562306a36Sopenharmony_ci		ret = regulator_enable(ccp2->vdds_csib);
15662306a36Sopenharmony_ci		if (ret < 0)
15762306a36Sopenharmony_ci			return ret;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Enable/Disable all the LCx channels */
16162306a36Sopenharmony_ci	for (i = 0; i < CCP2_LCx_CHANS_NUM; i++)
16262306a36Sopenharmony_ci		isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(i),
16362306a36Sopenharmony_ci				ISPCCP2_LCx_CTRL_CHAN_EN,
16462306a36Sopenharmony_ci				enable ? ISPCCP2_LCx_CTRL_CHAN_EN : 0);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Enable/Disable ccp2 interface in ccp2 mode */
16762306a36Sopenharmony_ci	isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL,
16862306a36Sopenharmony_ci			ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN,
16962306a36Sopenharmony_ci			enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (!enable && ccp2->vdds_csib)
17262306a36Sopenharmony_ci		regulator_disable(ccp2->vdds_csib);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/*
17862306a36Sopenharmony_ci * ccp2_mem_enable - Enable CCP2 memory interface.
17962306a36Sopenharmony_ci * @ccp2: pointer to ISP CCP2 device
18062306a36Sopenharmony_ci * @enable: enable/disable flag
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_cistatic void ccp2_mem_enable(struct isp_ccp2_device *ccp2, u8 enable)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (enable)
18762306a36Sopenharmony_ci		ccp2_if_enable(ccp2, 0);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Enable/Disable ccp2 interface in ccp2 mode */
19062306a36Sopenharmony_ci	isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL,
19162306a36Sopenharmony_ci			ISPCCP2_CTRL_MODE, enable ? ISPCCP2_CTRL_MODE : 0);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL,
19462306a36Sopenharmony_ci			ISPCCP2_LCM_CTRL_CHAN_EN,
19562306a36Sopenharmony_ci			enable ? ISPCCP2_LCM_CTRL_CHAN_EN : 0);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/*
19962306a36Sopenharmony_ci * ccp2_phyif_config - Initialize CCP2 phy interface config
20062306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
20162306a36Sopenharmony_ci * @buscfg: CCP2 platform data
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * Configure the CCP2 physical interface module from platform data.
20462306a36Sopenharmony_ci *
20562306a36Sopenharmony_ci * Returns -EIO if strobe is chosen in CSI1 mode, or 0 on success.
20662306a36Sopenharmony_ci */
20762306a36Sopenharmony_cistatic int ccp2_phyif_config(struct isp_ccp2_device *ccp2,
20862306a36Sopenharmony_ci			     const struct isp_ccp2_cfg *buscfg)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
21162306a36Sopenharmony_ci	u32 val;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL) |
21462306a36Sopenharmony_ci			    ISPCCP2_CTRL_MODE;
21562306a36Sopenharmony_ci	/* Data/strobe physical layer */
21662306a36Sopenharmony_ci	BIT_SET(val, ISPCCP2_CTRL_PHY_SEL_SHIFT, ISPCCP2_CTRL_PHY_SEL_MASK,
21762306a36Sopenharmony_ci		buscfg->phy_layer);
21862306a36Sopenharmony_ci	BIT_SET(val, ISPCCP2_CTRL_IO_OUT_SEL_SHIFT,
21962306a36Sopenharmony_ci		ISPCCP2_CTRL_IO_OUT_SEL_MASK, buscfg->ccp2_mode);
22062306a36Sopenharmony_ci	BIT_SET(val, ISPCCP2_CTRL_INV_SHIFT, ISPCCP2_CTRL_INV_MASK,
22162306a36Sopenharmony_ci		buscfg->strobe_clk_pol);
22262306a36Sopenharmony_ci	BIT_SET(val, ISPCCP2_CTRL_VP_CLK_POL_SHIFT,
22362306a36Sopenharmony_ci		ISPCCP2_CTRL_VP_CLK_POL_MASK, buscfg->vp_clk_pol);
22462306a36Sopenharmony_ci	isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
22762306a36Sopenharmony_ci	if (!(val & ISPCCP2_CTRL_MODE)) {
22862306a36Sopenharmony_ci		if (buscfg->ccp2_mode == ISP_CCP2_MODE_CCP2)
22962306a36Sopenharmony_ci			dev_warn(isp->dev, "OMAP3 CCP2 bus not available\n");
23062306a36Sopenharmony_ci		if (buscfg->phy_layer == ISP_CCP2_PHY_DATA_STROBE)
23162306a36Sopenharmony_ci			/* Strobe mode requires CCP2 */
23262306a36Sopenharmony_ci			return -EIO;
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return 0;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/*
23962306a36Sopenharmony_ci * ccp2_vp_config - Initialize CCP2 video port interface.
24062306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
24162306a36Sopenharmony_ci * @vpclk_div: Video port divisor
24262306a36Sopenharmony_ci *
24362306a36Sopenharmony_ci * Configure the CCP2 video port with the given clock divisor. The valid divisor
24462306a36Sopenharmony_ci * values depend on the ISP revision:
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * - revision 1.0 and 2.0	1 to 4
24762306a36Sopenharmony_ci * - revision 15.0		1 to 65536
24862306a36Sopenharmony_ci *
24962306a36Sopenharmony_ci * The exact divisor value used might differ from the requested value, as ISP
25062306a36Sopenharmony_ci * revision 15.0 represent the divisor by 65536 divided by an integer.
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_cistatic void ccp2_vp_config(struct isp_ccp2_device *ccp2,
25362306a36Sopenharmony_ci			   unsigned int vpclk_div)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
25662306a36Sopenharmony_ci	u32 val;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* ISPCCP2_CTRL Video port */
25962306a36Sopenharmony_ci	val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
26062306a36Sopenharmony_ci	val |= ISPCCP2_CTRL_VP_ONLY_EN;	/* Disable the memory write port */
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (isp->revision == ISP_REVISION_15_0) {
26362306a36Sopenharmony_ci		vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 65536);
26462306a36Sopenharmony_ci		vpclk_div = min(ISPCCP2_VPCLK_FRACDIV / vpclk_div, 65535U);
26562306a36Sopenharmony_ci		BIT_SET(val, ISPCCP2_CTRL_VPCLK_DIV_SHIFT,
26662306a36Sopenharmony_ci			ISPCCP2_CTRL_VPCLK_DIV_MASK, vpclk_div);
26762306a36Sopenharmony_ci	} else {
26862306a36Sopenharmony_ci		vpclk_div = clamp_t(unsigned int, vpclk_div, 1, 4);
26962306a36Sopenharmony_ci		BIT_SET(val, ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT,
27062306a36Sopenharmony_ci			ISPCCP2_CTRL_VP_OUT_CTRL_MASK, vpclk_div - 1);
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * ccp2_lcx_config - Initialize CCP2 logical channel interface.
27862306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
27962306a36Sopenharmony_ci * @config: Pointer to ISP LCx config structure.
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci * This will analyze the parameters passed by the interface config
28262306a36Sopenharmony_ci * and configure CSI1/CCP2 logical channel
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_cistatic void ccp2_lcx_config(struct isp_ccp2_device *ccp2,
28662306a36Sopenharmony_ci			    struct isp_interface_lcx_config *config)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
28962306a36Sopenharmony_ci	u32 val, format;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	switch (config->format) {
29262306a36Sopenharmony_ci	case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
29362306a36Sopenharmony_ci		format = ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP;
29462306a36Sopenharmony_ci		break;
29562306a36Sopenharmony_ci	case MEDIA_BUS_FMT_SGRBG10_1X10:
29662306a36Sopenharmony_ci	default:
29762306a36Sopenharmony_ci		format = ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP;	/* RAW10+VP */
29862306a36Sopenharmony_ci		break;
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci	/* ISPCCP2_LCx_CTRL logical channel #0 */
30162306a36Sopenharmony_ci	val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0))
30262306a36Sopenharmony_ci			    | (ISPCCP2_LCx_CTRL_REGION_EN); /* Region */
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (isp->revision == ISP_REVISION_15_0) {
30562306a36Sopenharmony_ci		/* CRC */
30662306a36Sopenharmony_ci		BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0,
30762306a36Sopenharmony_ci			ISPCCP2_LCx_CTRL_CRC_MASK,
30862306a36Sopenharmony_ci			config->crc);
30962306a36Sopenharmony_ci		/* Format = RAW10+VP or RAW8+DPCM10+VP*/
31062306a36Sopenharmony_ci		BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0,
31162306a36Sopenharmony_ci			ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0, format);
31262306a36Sopenharmony_ci	} else {
31362306a36Sopenharmony_ci		BIT_SET(val, ISPCCP2_LCx_CTRL_CRC_SHIFT,
31462306a36Sopenharmony_ci			ISPCCP2_LCx_CTRL_CRC_MASK,
31562306a36Sopenharmony_ci			config->crc);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		BIT_SET(val, ISPCCP2_LCx_CTRL_FORMAT_SHIFT,
31862306a36Sopenharmony_ci			ISPCCP2_LCx_CTRL_FORMAT_MASK, format);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci	isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_CTRL(0));
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* ISPCCP2_DAT_START for logical channel #0 */
32362306a36Sopenharmony_ci	isp_reg_writel(isp, config->data_start << ISPCCP2_LCx_DAT_SHIFT,
32462306a36Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_START(0));
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/* ISPCCP2_DAT_SIZE for logical channel #0 */
32762306a36Sopenharmony_ci	isp_reg_writel(isp, config->data_size << ISPCCP2_LCx_DAT_SHIFT,
32862306a36Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCx_DAT_SIZE(0));
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* Enable error IRQs for logical channel #0 */
33162306a36Sopenharmony_ci	val = ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ |
33262306a36Sopenharmony_ci	      ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ |
33362306a36Sopenharmony_ci	      ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ |
33462306a36Sopenharmony_ci	      ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ |
33562306a36Sopenharmony_ci	      ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ |
33662306a36Sopenharmony_ci	      ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQSTATUS);
33962306a36Sopenharmony_ci	isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LC01_IRQENABLE, val);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/*
34362306a36Sopenharmony_ci * ccp2_if_configure - Configure ccp2 with data from sensor
34462306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
34562306a36Sopenharmony_ci *
34662306a36Sopenharmony_ci * Return 0 on success or a negative error code
34762306a36Sopenharmony_ci */
34862306a36Sopenharmony_cistatic int ccp2_if_configure(struct isp_ccp2_device *ccp2)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
35162306a36Sopenharmony_ci	const struct isp_bus_cfg *buscfg;
35262306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
35362306a36Sopenharmony_ci	struct media_pad *pad;
35462306a36Sopenharmony_ci	struct v4l2_subdev *sensor;
35562306a36Sopenharmony_ci	u32 lines = 0;
35662306a36Sopenharmony_ci	int ret;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	ccp2_pwr_cfg(ccp2);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	pad = media_pad_remote_pad_first(&ccp2->pads[CCP2_PAD_SINK]);
36162306a36Sopenharmony_ci	sensor = media_entity_to_v4l2_subdev(pad->entity);
36262306a36Sopenharmony_ci	buscfg = v4l2_subdev_to_bus_cfg(pipe->external);
36362306a36Sopenharmony_ci	if (WARN_ON(!buscfg))
36462306a36Sopenharmony_ci		return -EPIPE;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	ret = ccp2_phyif_config(ccp2, &buscfg->bus.ccp2);
36762306a36Sopenharmony_ci	if (ret < 0)
36862306a36Sopenharmony_ci		return ret;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	ccp2_vp_config(ccp2, buscfg->bus.ccp2.vpclk_div + 1);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	v4l2_subdev_call(sensor, sensor, g_skip_top_lines, &lines);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	format = &ccp2->formats[CCP2_PAD_SINK];
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	ccp2->if_cfg.data_start = lines;
37762306a36Sopenharmony_ci	ccp2->if_cfg.crc = buscfg->bus.ccp2.crc;
37862306a36Sopenharmony_ci	ccp2->if_cfg.format = format->code;
37962306a36Sopenharmony_ci	ccp2->if_cfg.data_size = format->height;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	ccp2_lcx_config(ccp2, &ccp2->if_cfg);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return 0;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic int ccp2_adjust_bandwidth(struct isp_ccp2_device *ccp2)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
38962306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
39062306a36Sopenharmony_ci	const struct v4l2_mbus_framefmt *ofmt = &ccp2->formats[CCP2_PAD_SOURCE];
39162306a36Sopenharmony_ci	unsigned long l3_ick = pipe->l3_ick;
39262306a36Sopenharmony_ci	struct v4l2_fract *timeperframe;
39362306a36Sopenharmony_ci	unsigned int vpclk_div = 2;
39462306a36Sopenharmony_ci	unsigned int value;
39562306a36Sopenharmony_ci	u64 bound;
39662306a36Sopenharmony_ci	u64 area;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* Compute the minimum clock divisor, based on the pipeline maximum
39962306a36Sopenharmony_ci	 * data rate. This is an absolute lower bound if we don't want SBL
40062306a36Sopenharmony_ci	 * overflows, so round the value up.
40162306a36Sopenharmony_ci	 */
40262306a36Sopenharmony_ci	vpclk_div = max_t(unsigned int, DIV_ROUND_UP(l3_ick, pipe->max_rate),
40362306a36Sopenharmony_ci			  vpclk_div);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* Compute the maximum clock divisor, based on the requested frame rate.
40662306a36Sopenharmony_ci	 * This is a soft lower bound to achieve a frame rate equal or higher
40762306a36Sopenharmony_ci	 * than the requested value, so round the value down.
40862306a36Sopenharmony_ci	 */
40962306a36Sopenharmony_ci	timeperframe = &pipe->max_timeperframe;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (timeperframe->numerator) {
41262306a36Sopenharmony_ci		area = ofmt->width * ofmt->height;
41362306a36Sopenharmony_ci		bound = div_u64(area * timeperframe->denominator,
41462306a36Sopenharmony_ci				timeperframe->numerator);
41562306a36Sopenharmony_ci		value = min_t(u64, bound, l3_ick);
41662306a36Sopenharmony_ci		vpclk_div = max_t(unsigned int, l3_ick / value, vpclk_div);
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	dev_dbg(isp->dev, "%s: minimum clock divisor = %u\n", __func__,
42062306a36Sopenharmony_ci		vpclk_div);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	return vpclk_div;
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci/*
42662306a36Sopenharmony_ci * ccp2_mem_configure - Initialize CCP2 memory input/output interface
42762306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
42862306a36Sopenharmony_ci * @config: Pointer to ISP mem interface config structure
42962306a36Sopenharmony_ci *
43062306a36Sopenharmony_ci * This will analyze the parameters passed by the interface config
43162306a36Sopenharmony_ci * structure, and configure the respective registers for proper
43262306a36Sopenharmony_ci * CSI1/CCP2 memory input.
43362306a36Sopenharmony_ci */
43462306a36Sopenharmony_cistatic void ccp2_mem_configure(struct isp_ccp2_device *ccp2,
43562306a36Sopenharmony_ci			       struct isp_interface_mem_config *config)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
43862306a36Sopenharmony_ci	u32 sink_pixcode = ccp2->formats[CCP2_PAD_SINK].code;
43962306a36Sopenharmony_ci	u32 source_pixcode = ccp2->formats[CCP2_PAD_SOURCE].code;
44062306a36Sopenharmony_ci	unsigned int dpcm_decompress = 0;
44162306a36Sopenharmony_ci	u32 val, hwords;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (sink_pixcode != source_pixcode &&
44462306a36Sopenharmony_ci	    sink_pixcode == MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8)
44562306a36Sopenharmony_ci		dpcm_decompress = 1;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ccp2_pwr_cfg(ccp2);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/* Hsize, Skip */
45062306a36Sopenharmony_ci	isp_reg_writel(isp, ISPCCP2_LCM_HSIZE_SKIP_MIN |
45162306a36Sopenharmony_ci		       (config->hsize_count << ISPCCP2_LCM_HSIZE_SHIFT),
45262306a36Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_HSIZE);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* Vsize, no. of lines */
45562306a36Sopenharmony_ci	isp_reg_writel(isp, config->vsize_count << ISPCCP2_LCM_VSIZE_SHIFT,
45662306a36Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_VSIZE);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (ccp2->video_in.bpl_padding == 0)
45962306a36Sopenharmony_ci		config->src_ofst = 0;
46062306a36Sopenharmony_ci	else
46162306a36Sopenharmony_ci		config->src_ofst = ccp2->video_in.bpl_value;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	isp_reg_writel(isp, config->src_ofst, OMAP3_ISP_IOMEM_CCP2,
46462306a36Sopenharmony_ci		       ISPCCP2_LCM_SRC_OFST);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* Source and Destination formats */
46762306a36Sopenharmony_ci	val = ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 <<
46862306a36Sopenharmony_ci	      ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (dpcm_decompress) {
47162306a36Sopenharmony_ci		/* source format is RAW8 */
47262306a36Sopenharmony_ci		val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 <<
47362306a36Sopenharmony_ci		       ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		/* RAW8 + DPCM10 - simple predictor */
47662306a36Sopenharmony_ci		val |= ISPCCP2_LCM_CTRL_SRC_DPCM_PRED;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		/* enable source DPCM decompression */
47962306a36Sopenharmony_ci		val |= ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 <<
48062306a36Sopenharmony_ci		       ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT;
48162306a36Sopenharmony_ci	} else {
48262306a36Sopenharmony_ci		/* source format is RAW10 */
48362306a36Sopenharmony_ci		val |= ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 <<
48462306a36Sopenharmony_ci		       ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* Burst size to 32x64 */
48862306a36Sopenharmony_ci	val |= ISPCCP2_LCM_CTRL_BURST_SIZE_32X <<
48962306a36Sopenharmony_ci	       ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_CTRL);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* Prefetch setup */
49462306a36Sopenharmony_ci	if (dpcm_decompress)
49562306a36Sopenharmony_ci		hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN +
49662306a36Sopenharmony_ci			  config->hsize_count) >> 3;
49762306a36Sopenharmony_ci	else
49862306a36Sopenharmony_ci		hwords = (ISPCCP2_LCM_HSIZE_SKIP_MIN +
49962306a36Sopenharmony_ci			  config->hsize_count) >> 2;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	isp_reg_writel(isp, hwords << ISPCCP2_LCM_PREFETCH_SHIFT,
50262306a36Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_PREFETCH);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* Video port */
50562306a36Sopenharmony_ci	isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_CTRL,
50662306a36Sopenharmony_ci		    ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE);
50762306a36Sopenharmony_ci	ccp2_vp_config(ccp2, ccp2_adjust_bandwidth(ccp2));
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* Clear LCM interrupts */
51062306a36Sopenharmony_ci	isp_reg_writel(isp, ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ |
51162306a36Sopenharmony_ci		       ISPCCP2_LCM_IRQSTATUS_EOF_IRQ,
51262306a36Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQSTATUS);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* Enable LCM interrupts */
51562306a36Sopenharmony_ci	isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_IRQENABLE,
51662306a36Sopenharmony_ci		    ISPCCP2_LCM_IRQSTATUS_EOF_IRQ |
51762306a36Sopenharmony_ci		    ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/*
52162306a36Sopenharmony_ci * ccp2_set_inaddr - Sets memory address of input frame.
52262306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
52362306a36Sopenharmony_ci * @addr: 32bit memory address aligned on 32byte boundary.
52462306a36Sopenharmony_ci *
52562306a36Sopenharmony_ci * Configures the memory address from which the input frame is to be read.
52662306a36Sopenharmony_ci */
52762306a36Sopenharmony_cistatic void ccp2_set_inaddr(struct isp_ccp2_device *ccp2, u32 addr)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCP2, ISPCCP2_LCM_SRC_ADDR);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
53562306a36Sopenharmony_ci * Interrupt handling
53662306a36Sopenharmony_ci */
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic void ccp2_isr_buffer(struct isp_ccp2_device *ccp2)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
54162306a36Sopenharmony_ci	struct isp_buffer *buffer;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	buffer = omap3isp_video_buffer_next(&ccp2->video_in);
54462306a36Sopenharmony_ci	if (buffer != NULL)
54562306a36Sopenharmony_ci		ccp2_set_inaddr(ccp2, buffer->dma);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	pipe->state |= ISP_PIPELINE_IDLE_INPUT;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (ccp2->state == ISP_PIPELINE_STREAM_SINGLESHOT) {
55062306a36Sopenharmony_ci		if (isp_pipeline_ready(pipe))
55162306a36Sopenharmony_ci			omap3isp_pipeline_set_stream(pipe,
55262306a36Sopenharmony_ci						ISP_PIPELINE_STREAM_SINGLESHOT);
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci/*
55762306a36Sopenharmony_ci * omap3isp_ccp2_isr - Handle ISP CCP2 interrupts
55862306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
55962306a36Sopenharmony_ci *
56062306a36Sopenharmony_ci * This will handle the CCP2 interrupts
56162306a36Sopenharmony_ci */
56262306a36Sopenharmony_civoid omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
56562306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
56662306a36Sopenharmony_ci	static const u32 ISPCCP2_LC01_ERROR =
56762306a36Sopenharmony_ci		ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ |
56862306a36Sopenharmony_ci		ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ |
56962306a36Sopenharmony_ci		ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ |
57062306a36Sopenharmony_ci		ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ |
57162306a36Sopenharmony_ci		ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ |
57262306a36Sopenharmony_ci		ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ;
57362306a36Sopenharmony_ci	u32 lcx_irqstatus, lcm_irqstatus;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* First clear the interrupts */
57662306a36Sopenharmony_ci	lcx_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2,
57762306a36Sopenharmony_ci				      ISPCCP2_LC01_IRQSTATUS);
57862306a36Sopenharmony_ci	isp_reg_writel(isp, lcx_irqstatus, OMAP3_ISP_IOMEM_CCP2,
57962306a36Sopenharmony_ci		       ISPCCP2_LC01_IRQSTATUS);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	lcm_irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCP2,
58262306a36Sopenharmony_ci				      ISPCCP2_LCM_IRQSTATUS);
58362306a36Sopenharmony_ci	isp_reg_writel(isp, lcm_irqstatus, OMAP3_ISP_IOMEM_CCP2,
58462306a36Sopenharmony_ci		       ISPCCP2_LCM_IRQSTATUS);
58562306a36Sopenharmony_ci	/* Errors */
58662306a36Sopenharmony_ci	if (lcx_irqstatus & ISPCCP2_LC01_ERROR) {
58762306a36Sopenharmony_ci		pipe->error = true;
58862306a36Sopenharmony_ci		dev_dbg(isp->dev, "CCP2 err:%x\n", lcx_irqstatus);
58962306a36Sopenharmony_ci		return;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ) {
59362306a36Sopenharmony_ci		pipe->error = true;
59462306a36Sopenharmony_ci		dev_dbg(isp->dev, "CCP2 OCP err:%x\n", lcm_irqstatus);
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping))
59862306a36Sopenharmony_ci		return;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* Handle queued buffers on frame end interrupts */
60162306a36Sopenharmony_ci	if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ)
60262306a36Sopenharmony_ci		ccp2_isr_buffer(ccp2);
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
60662306a36Sopenharmony_ci * V4L2 subdev operations
60762306a36Sopenharmony_ci */
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic const unsigned int ccp2_fmts[] = {
61062306a36Sopenharmony_ci	MEDIA_BUS_FMT_SGRBG10_1X10,
61162306a36Sopenharmony_ci	MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
61262306a36Sopenharmony_ci};
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci/*
61562306a36Sopenharmony_ci * __ccp2_get_format - helper function for getting ccp2 format
61662306a36Sopenharmony_ci * @ccp2  : Pointer to ISP CCP2 device
61762306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
61862306a36Sopenharmony_ci * @pad   : pad number
61962306a36Sopenharmony_ci * @which : wanted subdev format
62062306a36Sopenharmony_ci * return format structure or NULL on error
62162306a36Sopenharmony_ci */
62262306a36Sopenharmony_cistatic struct v4l2_mbus_framefmt *
62362306a36Sopenharmony_ci__ccp2_get_format(struct isp_ccp2_device *ccp2,
62462306a36Sopenharmony_ci		  struct v4l2_subdev_state *sd_state,
62562306a36Sopenharmony_ci		  unsigned int pad, enum v4l2_subdev_format_whence which)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
62862306a36Sopenharmony_ci		return v4l2_subdev_get_try_format(&ccp2->subdev, sd_state,
62962306a36Sopenharmony_ci						  pad);
63062306a36Sopenharmony_ci	else
63162306a36Sopenharmony_ci		return &ccp2->formats[pad];
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci/*
63562306a36Sopenharmony_ci * ccp2_try_format - Handle try format by pad subdev method
63662306a36Sopenharmony_ci * @ccp2  : Pointer to ISP CCP2 device
63762306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
63862306a36Sopenharmony_ci * @pad   : pad num
63962306a36Sopenharmony_ci * @fmt   : pointer to v4l2 mbus format structure
64062306a36Sopenharmony_ci * @which : wanted subdev format
64162306a36Sopenharmony_ci */
64262306a36Sopenharmony_cistatic void ccp2_try_format(struct isp_ccp2_device *ccp2,
64362306a36Sopenharmony_ci			       struct v4l2_subdev_state *sd_state,
64462306a36Sopenharmony_ci			       unsigned int pad,
64562306a36Sopenharmony_ci			       struct v4l2_mbus_framefmt *fmt,
64662306a36Sopenharmony_ci			       enum v4l2_subdev_format_whence which)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	switch (pad) {
65162306a36Sopenharmony_ci	case CCP2_PAD_SINK:
65262306a36Sopenharmony_ci		if (fmt->code != MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8)
65362306a36Sopenharmony_ci			fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci		if (ccp2->input == CCP2_INPUT_SENSOR) {
65662306a36Sopenharmony_ci			fmt->width = clamp_t(u32, fmt->width,
65762306a36Sopenharmony_ci					     ISPCCP2_DAT_START_MIN,
65862306a36Sopenharmony_ci					     ISPCCP2_DAT_START_MAX);
65962306a36Sopenharmony_ci			fmt->height = clamp_t(u32, fmt->height,
66062306a36Sopenharmony_ci					      ISPCCP2_DAT_SIZE_MIN,
66162306a36Sopenharmony_ci					      ISPCCP2_DAT_SIZE_MAX);
66262306a36Sopenharmony_ci		} else if (ccp2->input == CCP2_INPUT_MEMORY) {
66362306a36Sopenharmony_ci			fmt->width = clamp_t(u32, fmt->width,
66462306a36Sopenharmony_ci					     ISPCCP2_LCM_HSIZE_COUNT_MIN,
66562306a36Sopenharmony_ci					     ISPCCP2_LCM_HSIZE_COUNT_MAX);
66662306a36Sopenharmony_ci			fmt->height = clamp_t(u32, fmt->height,
66762306a36Sopenharmony_ci					      ISPCCP2_LCM_VSIZE_MIN,
66862306a36Sopenharmony_ci					      ISPCCP2_LCM_VSIZE_MAX);
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci		break;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	case CCP2_PAD_SOURCE:
67362306a36Sopenharmony_ci		/* Source format - copy sink format and change pixel code
67462306a36Sopenharmony_ci		 * to SGRBG10_1X10 as we don't support CCP2 write to memory.
67562306a36Sopenharmony_ci		 * When CCP2 write to memory feature will be added this
67662306a36Sopenharmony_ci		 * should be changed properly.
67762306a36Sopenharmony_ci		 */
67862306a36Sopenharmony_ci		format = __ccp2_get_format(ccp2, sd_state, CCP2_PAD_SINK,
67962306a36Sopenharmony_ci					   which);
68062306a36Sopenharmony_ci		memcpy(fmt, format, sizeof(*fmt));
68162306a36Sopenharmony_ci		fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
68262306a36Sopenharmony_ci		break;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	fmt->field = V4L2_FIELD_NONE;
68662306a36Sopenharmony_ci	fmt->colorspace = V4L2_COLORSPACE_SRGB;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci/*
69062306a36Sopenharmony_ci * ccp2_enum_mbus_code - Handle pixel format enumeration
69162306a36Sopenharmony_ci * @sd     : pointer to v4l2 subdev structure
69262306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
69362306a36Sopenharmony_ci * @code   : pointer to v4l2_subdev_mbus_code_enum structure
69462306a36Sopenharmony_ci * return -EINVAL or zero on success
69562306a36Sopenharmony_ci */
69662306a36Sopenharmony_cistatic int ccp2_enum_mbus_code(struct v4l2_subdev *sd,
69762306a36Sopenharmony_ci				  struct v4l2_subdev_state *sd_state,
69862306a36Sopenharmony_ci				  struct v4l2_subdev_mbus_code_enum *code)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
70162306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (code->pad == CCP2_PAD_SINK) {
70462306a36Sopenharmony_ci		if (code->index >= ARRAY_SIZE(ccp2_fmts))
70562306a36Sopenharmony_ci			return -EINVAL;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci		code->code = ccp2_fmts[code->index];
70862306a36Sopenharmony_ci	} else {
70962306a36Sopenharmony_ci		if (code->index != 0)
71062306a36Sopenharmony_ci			return -EINVAL;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		format = __ccp2_get_format(ccp2, sd_state, CCP2_PAD_SINK,
71362306a36Sopenharmony_ci					   code->which);
71462306a36Sopenharmony_ci		code->code = format->code;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	return 0;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic int ccp2_enum_frame_size(struct v4l2_subdev *sd,
72162306a36Sopenharmony_ci				   struct v4l2_subdev_state *sd_state,
72262306a36Sopenharmony_ci				   struct v4l2_subdev_frame_size_enum *fse)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
72562306a36Sopenharmony_ci	struct v4l2_mbus_framefmt format;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (fse->index != 0)
72862306a36Sopenharmony_ci		return -EINVAL;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	format.code = fse->code;
73162306a36Sopenharmony_ci	format.width = 1;
73262306a36Sopenharmony_ci	format.height = 1;
73362306a36Sopenharmony_ci	ccp2_try_format(ccp2, sd_state, fse->pad, &format, fse->which);
73462306a36Sopenharmony_ci	fse->min_width = format.width;
73562306a36Sopenharmony_ci	fse->min_height = format.height;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (format.code != fse->code)
73862306a36Sopenharmony_ci		return -EINVAL;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	format.code = fse->code;
74162306a36Sopenharmony_ci	format.width = -1;
74262306a36Sopenharmony_ci	format.height = -1;
74362306a36Sopenharmony_ci	ccp2_try_format(ccp2, sd_state, fse->pad, &format, fse->which);
74462306a36Sopenharmony_ci	fse->max_width = format.width;
74562306a36Sopenharmony_ci	fse->max_height = format.height;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return 0;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci/*
75162306a36Sopenharmony_ci * ccp2_get_format - Handle get format by pads subdev method
75262306a36Sopenharmony_ci * @sd    : pointer to v4l2 subdev structure
75362306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
75462306a36Sopenharmony_ci * @fmt   : pointer to v4l2 subdev format structure
75562306a36Sopenharmony_ci * return -EINVAL or zero on success
75662306a36Sopenharmony_ci */
75762306a36Sopenharmony_cistatic int ccp2_get_format(struct v4l2_subdev *sd,
75862306a36Sopenharmony_ci			   struct v4l2_subdev_state *sd_state,
75962306a36Sopenharmony_ci			   struct v4l2_subdev_format *fmt)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
76262306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	format = __ccp2_get_format(ccp2, sd_state, fmt->pad, fmt->which);
76562306a36Sopenharmony_ci	if (format == NULL)
76662306a36Sopenharmony_ci		return -EINVAL;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	fmt->format = *format;
76962306a36Sopenharmony_ci	return 0;
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci/*
77362306a36Sopenharmony_ci * ccp2_set_format - Handle set format by pads subdev method
77462306a36Sopenharmony_ci * @sd    : pointer to v4l2 subdev structure
77562306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
77662306a36Sopenharmony_ci * @fmt   : pointer to v4l2 subdev format structure
77762306a36Sopenharmony_ci * returns zero
77862306a36Sopenharmony_ci */
77962306a36Sopenharmony_cistatic int ccp2_set_format(struct v4l2_subdev *sd,
78062306a36Sopenharmony_ci			   struct v4l2_subdev_state *sd_state,
78162306a36Sopenharmony_ci			   struct v4l2_subdev_format *fmt)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
78462306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	format = __ccp2_get_format(ccp2, sd_state, fmt->pad, fmt->which);
78762306a36Sopenharmony_ci	if (format == NULL)
78862306a36Sopenharmony_ci		return -EINVAL;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	ccp2_try_format(ccp2, sd_state, fmt->pad, &fmt->format, fmt->which);
79162306a36Sopenharmony_ci	*format = fmt->format;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	/* Propagate the format from sink to source */
79462306a36Sopenharmony_ci	if (fmt->pad == CCP2_PAD_SINK) {
79562306a36Sopenharmony_ci		format = __ccp2_get_format(ccp2, sd_state, CCP2_PAD_SOURCE,
79662306a36Sopenharmony_ci					   fmt->which);
79762306a36Sopenharmony_ci		*format = fmt->format;
79862306a36Sopenharmony_ci		ccp2_try_format(ccp2, sd_state, CCP2_PAD_SOURCE, format,
79962306a36Sopenharmony_ci				fmt->which);
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	return 0;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci/*
80662306a36Sopenharmony_ci * ccp2_init_formats - Initialize formats on all pads
80762306a36Sopenharmony_ci * @sd: ISP CCP2 V4L2 subdevice
80862306a36Sopenharmony_ci * @fh: V4L2 subdev file handle
80962306a36Sopenharmony_ci *
81062306a36Sopenharmony_ci * Initialize all pad formats with default values. If fh is not NULL, try
81162306a36Sopenharmony_ci * formats are initialized on the file handle. Otherwise active formats are
81262306a36Sopenharmony_ci * initialized on the device.
81362306a36Sopenharmony_ci */
81462306a36Sopenharmony_cistatic int ccp2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	struct v4l2_subdev_format format;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	memset(&format, 0, sizeof(format));
81962306a36Sopenharmony_ci	format.pad = CCP2_PAD_SINK;
82062306a36Sopenharmony_ci	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
82162306a36Sopenharmony_ci	format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
82262306a36Sopenharmony_ci	format.format.width = 4096;
82362306a36Sopenharmony_ci	format.format.height = 4096;
82462306a36Sopenharmony_ci	ccp2_set_format(sd, fh ? fh->state : NULL, &format);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	return 0;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci/*
83062306a36Sopenharmony_ci * ccp2_s_stream - Enable/Disable streaming on ccp2 subdev
83162306a36Sopenharmony_ci * @sd    : pointer to v4l2 subdev structure
83262306a36Sopenharmony_ci * @enable: 1 == Enable, 0 == Disable
83362306a36Sopenharmony_ci * return zero
83462306a36Sopenharmony_ci */
83562306a36Sopenharmony_cistatic int ccp2_s_stream(struct v4l2_subdev *sd, int enable)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
83862306a36Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccp2);
83962306a36Sopenharmony_ci	struct device *dev = to_device(ccp2);
84062306a36Sopenharmony_ci	int ret;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	if (ccp2->state == ISP_PIPELINE_STREAM_STOPPED) {
84362306a36Sopenharmony_ci		if (enable == ISP_PIPELINE_STREAM_STOPPED)
84462306a36Sopenharmony_ci			return 0;
84562306a36Sopenharmony_ci		atomic_set(&ccp2->stopping, 0);
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	switch (enable) {
84962306a36Sopenharmony_ci	case ISP_PIPELINE_STREAM_CONTINUOUS:
85062306a36Sopenharmony_ci		if (ccp2->phy) {
85162306a36Sopenharmony_ci			ret = omap3isp_csiphy_acquire(ccp2->phy, &sd->entity);
85262306a36Sopenharmony_ci			if (ret < 0)
85362306a36Sopenharmony_ci				return ret;
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		ccp2_if_configure(ccp2);
85762306a36Sopenharmony_ci		ccp2_print_status(ccp2);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		/* Enable CSI1/CCP2 interface */
86062306a36Sopenharmony_ci		ret = ccp2_if_enable(ccp2, 1);
86162306a36Sopenharmony_ci		if (ret < 0) {
86262306a36Sopenharmony_ci			if (ccp2->phy)
86362306a36Sopenharmony_ci				omap3isp_csiphy_release(ccp2->phy);
86462306a36Sopenharmony_ci			return ret;
86562306a36Sopenharmony_ci		}
86662306a36Sopenharmony_ci		break;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	case ISP_PIPELINE_STREAM_SINGLESHOT:
86962306a36Sopenharmony_ci		if (ccp2->state != ISP_PIPELINE_STREAM_SINGLESHOT) {
87062306a36Sopenharmony_ci			struct v4l2_mbus_framefmt *format;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci			format = &ccp2->formats[CCP2_PAD_SINK];
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci			ccp2->mem_cfg.hsize_count = format->width;
87562306a36Sopenharmony_ci			ccp2->mem_cfg.vsize_count = format->height;
87662306a36Sopenharmony_ci			ccp2->mem_cfg.src_ofst = 0;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci			ccp2_mem_configure(ccp2, &ccp2->mem_cfg);
87962306a36Sopenharmony_ci			omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI1_READ);
88062306a36Sopenharmony_ci			ccp2_print_status(ccp2);
88162306a36Sopenharmony_ci		}
88262306a36Sopenharmony_ci		ccp2_mem_enable(ccp2, 1);
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	case ISP_PIPELINE_STREAM_STOPPED:
88662306a36Sopenharmony_ci		if (omap3isp_module_sync_idle(&sd->entity, &ccp2->wait,
88762306a36Sopenharmony_ci					      &ccp2->stopping))
88862306a36Sopenharmony_ci			dev_dbg(dev, "%s: module stop timeout.\n", sd->name);
88962306a36Sopenharmony_ci		if (ccp2->input == CCP2_INPUT_MEMORY) {
89062306a36Sopenharmony_ci			ccp2_mem_enable(ccp2, 0);
89162306a36Sopenharmony_ci			omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI1_READ);
89262306a36Sopenharmony_ci		} else if (ccp2->input == CCP2_INPUT_SENSOR) {
89362306a36Sopenharmony_ci			/* Disable CSI1/CCP2 interface */
89462306a36Sopenharmony_ci			ccp2_if_enable(ccp2, 0);
89562306a36Sopenharmony_ci			if (ccp2->phy)
89662306a36Sopenharmony_ci				omap3isp_csiphy_release(ccp2->phy);
89762306a36Sopenharmony_ci		}
89862306a36Sopenharmony_ci		break;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	ccp2->state = enable;
90262306a36Sopenharmony_ci	return 0;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci/* subdev video operations */
90662306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops ccp2_sd_video_ops = {
90762306a36Sopenharmony_ci	.s_stream = ccp2_s_stream,
90862306a36Sopenharmony_ci};
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci/* subdev pad operations */
91162306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops ccp2_sd_pad_ops = {
91262306a36Sopenharmony_ci	.enum_mbus_code = ccp2_enum_mbus_code,
91362306a36Sopenharmony_ci	.enum_frame_size = ccp2_enum_frame_size,
91462306a36Sopenharmony_ci	.get_fmt = ccp2_get_format,
91562306a36Sopenharmony_ci	.set_fmt = ccp2_set_format,
91662306a36Sopenharmony_ci};
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/* subdev operations */
91962306a36Sopenharmony_cistatic const struct v4l2_subdev_ops ccp2_sd_ops = {
92062306a36Sopenharmony_ci	.video = &ccp2_sd_video_ops,
92162306a36Sopenharmony_ci	.pad = &ccp2_sd_pad_ops,
92262306a36Sopenharmony_ci};
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci/* subdev internal operations */
92562306a36Sopenharmony_cistatic const struct v4l2_subdev_internal_ops ccp2_sd_internal_ops = {
92662306a36Sopenharmony_ci	.open = ccp2_init_formats,
92762306a36Sopenharmony_ci};
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci/* --------------------------------------------------------------------------
93062306a36Sopenharmony_ci * ISP ccp2 video device node
93162306a36Sopenharmony_ci */
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci/*
93462306a36Sopenharmony_ci * ccp2_video_queue - Queue video buffer.
93562306a36Sopenharmony_ci * @video : Pointer to isp video structure
93662306a36Sopenharmony_ci * @buffer: Pointer to isp_buffer structure
93762306a36Sopenharmony_ci * return -EIO or zero on success
93862306a36Sopenharmony_ci */
93962306a36Sopenharmony_cistatic int ccp2_video_queue(struct isp_video *video, struct isp_buffer *buffer)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	struct isp_ccp2_device *ccp2 = &video->isp->isp_ccp2;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	ccp2_set_inaddr(ccp2, buffer->dma);
94462306a36Sopenharmony_ci	return 0;
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic const struct isp_video_operations ccp2_video_ops = {
94862306a36Sopenharmony_ci	.queue = ccp2_video_queue,
94962306a36Sopenharmony_ci};
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
95262306a36Sopenharmony_ci * Media entity operations
95362306a36Sopenharmony_ci */
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci/*
95662306a36Sopenharmony_ci * ccp2_link_setup - Setup ccp2 connections.
95762306a36Sopenharmony_ci * @entity : Pointer to media entity structure
95862306a36Sopenharmony_ci * @local  : Pointer to local pad array
95962306a36Sopenharmony_ci * @remote : Pointer to remote pad array
96062306a36Sopenharmony_ci * @flags  : Link flags
96162306a36Sopenharmony_ci * return -EINVAL on error or zero on success
96262306a36Sopenharmony_ci */
96362306a36Sopenharmony_cistatic int ccp2_link_setup(struct media_entity *entity,
96462306a36Sopenharmony_ci			   const struct media_pad *local,
96562306a36Sopenharmony_ci			   const struct media_pad *remote, u32 flags)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
96862306a36Sopenharmony_ci	struct isp_ccp2_device *ccp2 = v4l2_get_subdevdata(sd);
96962306a36Sopenharmony_ci	unsigned int index = local->index;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	/* FIXME: this is actually a hack! */
97262306a36Sopenharmony_ci	if (is_media_entity_v4l2_subdev(remote->entity))
97362306a36Sopenharmony_ci		index |= 2 << 16;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	switch (index) {
97662306a36Sopenharmony_ci	case CCP2_PAD_SINK:
97762306a36Sopenharmony_ci		/* read from memory */
97862306a36Sopenharmony_ci		if (flags & MEDIA_LNK_FL_ENABLED) {
97962306a36Sopenharmony_ci			if (ccp2->input == CCP2_INPUT_SENSOR)
98062306a36Sopenharmony_ci				return -EBUSY;
98162306a36Sopenharmony_ci			ccp2->input = CCP2_INPUT_MEMORY;
98262306a36Sopenharmony_ci		} else {
98362306a36Sopenharmony_ci			if (ccp2->input == CCP2_INPUT_MEMORY)
98462306a36Sopenharmony_ci				ccp2->input = CCP2_INPUT_NONE;
98562306a36Sopenharmony_ci		}
98662306a36Sopenharmony_ci		break;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	case CCP2_PAD_SINK | 2 << 16:
98962306a36Sopenharmony_ci		/* read from sensor/phy */
99062306a36Sopenharmony_ci		if (flags & MEDIA_LNK_FL_ENABLED) {
99162306a36Sopenharmony_ci			if (ccp2->input == CCP2_INPUT_MEMORY)
99262306a36Sopenharmony_ci				return -EBUSY;
99362306a36Sopenharmony_ci			ccp2->input = CCP2_INPUT_SENSOR;
99462306a36Sopenharmony_ci		} else {
99562306a36Sopenharmony_ci			if (ccp2->input == CCP2_INPUT_SENSOR)
99662306a36Sopenharmony_ci				ccp2->input = CCP2_INPUT_NONE;
99762306a36Sopenharmony_ci		} break;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	case CCP2_PAD_SOURCE | 2 << 16:
100062306a36Sopenharmony_ci		/* write to video port/ccdc */
100162306a36Sopenharmony_ci		if (flags & MEDIA_LNK_FL_ENABLED)
100262306a36Sopenharmony_ci			ccp2->output = CCP2_OUTPUT_CCDC;
100362306a36Sopenharmony_ci		else
100462306a36Sopenharmony_ci			ccp2->output = CCP2_OUTPUT_NONE;
100562306a36Sopenharmony_ci		break;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	default:
100862306a36Sopenharmony_ci		return -EINVAL;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	return 0;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci/* media operations */
101562306a36Sopenharmony_cistatic const struct media_entity_operations ccp2_media_ops = {
101662306a36Sopenharmony_ci	.link_setup = ccp2_link_setup,
101762306a36Sopenharmony_ci	.link_validate = v4l2_subdev_link_validate,
101862306a36Sopenharmony_ci};
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci/*
102162306a36Sopenharmony_ci * omap3isp_ccp2_unregister_entities - Unregister media entities: subdev
102262306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
102362306a36Sopenharmony_ci */
102462306a36Sopenharmony_civoid omap3isp_ccp2_unregister_entities(struct isp_ccp2_device *ccp2)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	v4l2_device_unregister_subdev(&ccp2->subdev);
102762306a36Sopenharmony_ci	omap3isp_video_unregister(&ccp2->video_in);
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci/*
103162306a36Sopenharmony_ci * omap3isp_ccp2_register_entities - Register the subdev media entity
103262306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
103362306a36Sopenharmony_ci * @vdev: Pointer to v4l device
103462306a36Sopenharmony_ci * return negative error code or zero on success
103562306a36Sopenharmony_ci */
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ciint omap3isp_ccp2_register_entities(struct isp_ccp2_device *ccp2,
103862306a36Sopenharmony_ci				    struct v4l2_device *vdev)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	int ret;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	/* Register the subdev and video nodes. */
104362306a36Sopenharmony_ci	ccp2->subdev.dev = vdev->mdev->dev;
104462306a36Sopenharmony_ci	ret = v4l2_device_register_subdev(vdev, &ccp2->subdev);
104562306a36Sopenharmony_ci	if (ret < 0)
104662306a36Sopenharmony_ci		goto error;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	ret = omap3isp_video_register(&ccp2->video_in, vdev);
104962306a36Sopenharmony_ci	if (ret < 0)
105062306a36Sopenharmony_ci		goto error;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	return 0;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_cierror:
105562306a36Sopenharmony_ci	omap3isp_ccp2_unregister_entities(ccp2);
105662306a36Sopenharmony_ci	return ret;
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
106062306a36Sopenharmony_ci * ISP ccp2 initialisation and cleanup
106162306a36Sopenharmony_ci */
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci/*
106462306a36Sopenharmony_ci * ccp2_init_entities - Initialize ccp2 subdev and media entity.
106562306a36Sopenharmony_ci * @ccp2: Pointer to ISP CCP2 device
106662306a36Sopenharmony_ci * return negative error code or zero on success
106762306a36Sopenharmony_ci */
106862306a36Sopenharmony_cistatic int ccp2_init_entities(struct isp_ccp2_device *ccp2)
106962306a36Sopenharmony_ci{
107062306a36Sopenharmony_ci	struct v4l2_subdev *sd = &ccp2->subdev;
107162306a36Sopenharmony_ci	struct media_pad *pads = ccp2->pads;
107262306a36Sopenharmony_ci	struct media_entity *me = &sd->entity;
107362306a36Sopenharmony_ci	int ret;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	ccp2->input = CCP2_INPUT_NONE;
107662306a36Sopenharmony_ci	ccp2->output = CCP2_OUTPUT_NONE;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	v4l2_subdev_init(sd, &ccp2_sd_ops);
107962306a36Sopenharmony_ci	sd->internal_ops = &ccp2_sd_internal_ops;
108062306a36Sopenharmony_ci	strscpy(sd->name, "OMAP3 ISP CCP2", sizeof(sd->name));
108162306a36Sopenharmony_ci	sd->grp_id = 1 << 16;   /* group ID for isp subdevs */
108262306a36Sopenharmony_ci	v4l2_set_subdevdata(sd, ccp2);
108362306a36Sopenharmony_ci	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	pads[CCP2_PAD_SINK].flags = MEDIA_PAD_FL_SINK
108662306a36Sopenharmony_ci				    | MEDIA_PAD_FL_MUST_CONNECT;
108762306a36Sopenharmony_ci	pads[CCP2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	me->ops = &ccp2_media_ops;
109062306a36Sopenharmony_ci	ret = media_entity_pads_init(me, CCP2_PADS_NUM, pads);
109162306a36Sopenharmony_ci	if (ret < 0)
109262306a36Sopenharmony_ci		return ret;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	ccp2_init_formats(sd, NULL);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	/*
109762306a36Sopenharmony_ci	 * The CCP2 has weird line alignment requirements, possibly caused by
109862306a36Sopenharmony_ci	 * DPCM8 decompression. Line length for data read from memory must be a
109962306a36Sopenharmony_ci	 * multiple of 128 bits (16 bytes) in continuous mode (when no padding
110062306a36Sopenharmony_ci	 * is present at end of lines). Additionally, if padding is used, the
110162306a36Sopenharmony_ci	 * padded line length must be a multiple of 32 bytes. To simplify the
110262306a36Sopenharmony_ci	 * implementation we use a fixed 32 bytes alignment regardless of the
110362306a36Sopenharmony_ci	 * input format and width. If strict 128 bits alignment support is
110462306a36Sopenharmony_ci	 * required ispvideo will need to be made aware of this special dual
110562306a36Sopenharmony_ci	 * alignment requirements.
110662306a36Sopenharmony_ci	 */
110762306a36Sopenharmony_ci	ccp2->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
110862306a36Sopenharmony_ci	ccp2->video_in.bpl_alignment = 32;
110962306a36Sopenharmony_ci	ccp2->video_in.bpl_max = 0xffffffe0;
111062306a36Sopenharmony_ci	ccp2->video_in.isp = to_isp_device(ccp2);
111162306a36Sopenharmony_ci	ccp2->video_in.ops = &ccp2_video_ops;
111262306a36Sopenharmony_ci	ccp2->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	ret = omap3isp_video_init(&ccp2->video_in, "CCP2");
111562306a36Sopenharmony_ci	if (ret < 0)
111662306a36Sopenharmony_ci		goto error;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	return 0;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cierror:
112162306a36Sopenharmony_ci	media_entity_cleanup(&ccp2->subdev.entity);
112262306a36Sopenharmony_ci	return ret;
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci/*
112662306a36Sopenharmony_ci * omap3isp_ccp2_init - CCP2 initialization.
112762306a36Sopenharmony_ci * @isp : Pointer to ISP device
112862306a36Sopenharmony_ci * return negative error code or zero on success
112962306a36Sopenharmony_ci */
113062306a36Sopenharmony_ciint omap3isp_ccp2_init(struct isp_device *isp)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
113362306a36Sopenharmony_ci	int ret;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	init_waitqueue_head(&ccp2->wait);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	/*
113862306a36Sopenharmony_ci	 * On the OMAP34xx the CSI1 receiver is operated in the CSIb IO
113962306a36Sopenharmony_ci	 * complex, which is powered by vdds_csib power rail. Hence the
114062306a36Sopenharmony_ci	 * request for the regulator.
114162306a36Sopenharmony_ci	 *
114262306a36Sopenharmony_ci	 * On the OMAP36xx, the CCP2 uses the CSI PHY1 or PHY2, shared with
114362306a36Sopenharmony_ci	 * the CSI2c or CSI2a receivers. The PHY then needs to be explicitly
114462306a36Sopenharmony_ci	 * configured.
114562306a36Sopenharmony_ci	 *
114662306a36Sopenharmony_ci	 * TODO: Don't hardcode the usage of PHY1 (shared with CSI2c).
114762306a36Sopenharmony_ci	 */
114862306a36Sopenharmony_ci	if (isp->revision == ISP_REVISION_2_0) {
114962306a36Sopenharmony_ci		ccp2->vdds_csib = devm_regulator_get(isp->dev, "vdds_csib");
115062306a36Sopenharmony_ci		if (IS_ERR(ccp2->vdds_csib)) {
115162306a36Sopenharmony_ci			if (PTR_ERR(ccp2->vdds_csib) == -EPROBE_DEFER) {
115262306a36Sopenharmony_ci				dev_dbg(isp->dev,
115362306a36Sopenharmony_ci					"Can't get regulator vdds_csib, deferring probing\n");
115462306a36Sopenharmony_ci				return -EPROBE_DEFER;
115562306a36Sopenharmony_ci			}
115662306a36Sopenharmony_ci			dev_dbg(isp->dev,
115762306a36Sopenharmony_ci				"Could not get regulator vdds_csib\n");
115862306a36Sopenharmony_ci			ccp2->vdds_csib = NULL;
115962306a36Sopenharmony_ci		}
116062306a36Sopenharmony_ci		ccp2->phy = &isp->isp_csiphy2;
116162306a36Sopenharmony_ci	} else if (isp->revision == ISP_REVISION_15_0) {
116262306a36Sopenharmony_ci		ccp2->phy = &isp->isp_csiphy1;
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	ret = ccp2_init_entities(ccp2);
116662306a36Sopenharmony_ci	if (ret < 0)
116762306a36Sopenharmony_ci		return ret;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	ccp2_reset(ccp2);
117062306a36Sopenharmony_ci	return 0;
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci/*
117462306a36Sopenharmony_ci * omap3isp_ccp2_cleanup - CCP2 un-initialization
117562306a36Sopenharmony_ci * @isp : Pointer to ISP device
117662306a36Sopenharmony_ci */
117762306a36Sopenharmony_civoid omap3isp_ccp2_cleanup(struct isp_device *isp)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	struct isp_ccp2_device *ccp2 = &isp->isp_ccp2;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	omap3isp_video_cleanup(&ccp2->video_in);
118262306a36Sopenharmony_ci	media_entity_cleanup(&ccp2->subdev.entity);
118362306a36Sopenharmony_ci}
1184