18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ispccdc.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * TI OMAP3 ISP - CCDC module
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010 Nokia Corporation
88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Texas Instruments, Inc.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
118c2ecf20Sopenharmony_ci *	     Sakari Ailus <sakari.ailus@iki.fi>
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
198c2ecf20Sopenharmony_ci#include <linux/mm.h>
208c2ecf20Sopenharmony_ci#include <linux/sched.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "isp.h"
258c2ecf20Sopenharmony_ci#include "ispreg.h"
268c2ecf20Sopenharmony_ci#include "ispccdc.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define CCDC_MIN_WIDTH		32
298c2ecf20Sopenharmony_ci#define CCDC_MIN_HEIGHT		32
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt *
328c2ecf20Sopenharmony_ci__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg,
338c2ecf20Sopenharmony_ci		  unsigned int pad, enum v4l2_subdev_format_whence which);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic const unsigned int ccdc_fmts[] = {
368c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_Y8_1X8,
378c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_Y10_1X10,
388c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_Y12_1X12,
398c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SGRBG8_1X8,
408c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SRGGB8_1X8,
418c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SBGGR8_1X8,
428c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SGBRG8_1X8,
438c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SGRBG10_1X10,
448c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SRGGB10_1X10,
458c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SBGGR10_1X10,
468c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SGBRG10_1X10,
478c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SGRBG12_1X12,
488c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SRGGB12_1X12,
498c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SBGGR12_1X12,
508c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_SGBRG12_1X12,
518c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_YUYV8_2X8,
528c2ecf20Sopenharmony_ci	MEDIA_BUS_FMT_UYVY8_2X8,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * ccdc_print_status - Print current CCDC Module register values.
578c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * Also prints other debug information stored in the CCDC module.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_ci#define CCDC_PRINT_REGISTER(isp, name)\
628c2ecf20Sopenharmony_ci	dev_dbg(isp->dev, "###CCDC " #name "=0x%08x\n", \
638c2ecf20Sopenharmony_ci		isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name))
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void ccdc_print_status(struct isp_ccdc_device *ccdc)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	dev_dbg(isp->dev, "-------------CCDC Register dump-------------\n");
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, PCR);
728c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, SYN_MODE);
738c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, HD_VD_WID);
748c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, PIX_LINES);
758c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, HORZ_INFO);
768c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, VERT_START);
778c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, VERT_LINES);
788c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, CULLING);
798c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, HSIZE_OFF);
808c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, SDOFST);
818c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, SDR_ADDR);
828c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, CLAMP);
838c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, DCSUB);
848c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, COLPTN);
858c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, BLKCMP);
868c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, FPC);
878c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, FPC_ADDR);
888c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, VDINT);
898c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, ALAW);
908c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, REC656IF);
918c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, CFG);
928c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, FMTCFG);
938c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, FMT_HORZ);
948c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, FMT_VERT);
958c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, PRGEVEN0);
968c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, PRGEVEN1);
978c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, PRGODD0);
988c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, PRGODD1);
998c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, VP_OUT);
1008c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, LSC_CONFIG);
1018c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, LSC_INITIAL);
1028c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, LSC_TABLE_BASE);
1038c2ecf20Sopenharmony_ci	CCDC_PRINT_REGISTER(isp, LSC_TABLE_OFFSET);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	dev_dbg(isp->dev, "--------------------------------------------\n");
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/*
1098c2ecf20Sopenharmony_ci * omap3isp_ccdc_busy - Get busy state of the CCDC.
1108c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_ciint omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) &
1178c2ecf20Sopenharmony_ci		ISPCCDC_PCR_BUSY;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
1218c2ecf20Sopenharmony_ci * Lens Shading Compensation
1228c2ecf20Sopenharmony_ci */
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/*
1258c2ecf20Sopenharmony_ci * ccdc_lsc_validate_config - Check that LSC configuration is valid.
1268c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
1278c2ecf20Sopenharmony_ci * @lsc_cfg: the LSC configuration to check.
1288c2ecf20Sopenharmony_ci *
1298c2ecf20Sopenharmony_ci * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid.
1308c2ecf20Sopenharmony_ci */
1318c2ecf20Sopenharmony_cistatic int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc,
1328c2ecf20Sopenharmony_ci				    struct omap3isp_ccdc_lsc_config *lsc_cfg)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
1358c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
1368c2ecf20Sopenharmony_ci	unsigned int paxel_width, paxel_height;
1378c2ecf20Sopenharmony_ci	unsigned int paxel_shift_x, paxel_shift_y;
1388c2ecf20Sopenharmony_ci	unsigned int min_width, min_height, min_size;
1398c2ecf20Sopenharmony_ci	unsigned int input_width, input_height;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	paxel_shift_x = lsc_cfg->gain_mode_m;
1428c2ecf20Sopenharmony_ci	paxel_shift_y = lsc_cfg->gain_mode_n;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if ((paxel_shift_x < 2) || (paxel_shift_x > 6) ||
1458c2ecf20Sopenharmony_ci	    (paxel_shift_y < 2) || (paxel_shift_y > 6)) {
1468c2ecf20Sopenharmony_ci		dev_dbg(isp->dev, "CCDC: LSC: Invalid paxel size\n");
1478c2ecf20Sopenharmony_ci		return -EINVAL;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (lsc_cfg->offset & 3) {
1518c2ecf20Sopenharmony_ci		dev_dbg(isp->dev,
1528c2ecf20Sopenharmony_ci			"CCDC: LSC: Offset must be a multiple of 4\n");
1538c2ecf20Sopenharmony_ci		return -EINVAL;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) {
1578c2ecf20Sopenharmony_ci		dev_dbg(isp->dev, "CCDC: LSC: initial_x and y must be even\n");
1588c2ecf20Sopenharmony_ci		return -EINVAL;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	format = __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK,
1628c2ecf20Sopenharmony_ci				   V4L2_SUBDEV_FORMAT_ACTIVE);
1638c2ecf20Sopenharmony_ci	input_width = format->width;
1648c2ecf20Sopenharmony_ci	input_height = format->height;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Calculate minimum bytesize for validation */
1678c2ecf20Sopenharmony_ci	paxel_width = 1 << paxel_shift_x;
1688c2ecf20Sopenharmony_ci	min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1)
1698c2ecf20Sopenharmony_ci		     >> paxel_shift_x) + 1;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	paxel_height = 1 << paxel_shift_y;
1728c2ecf20Sopenharmony_ci	min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1)
1738c2ecf20Sopenharmony_ci		     >> paxel_shift_y) + 1;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	min_size = 4 * min_width * min_height;
1768c2ecf20Sopenharmony_ci	if (min_size > lsc_cfg->size) {
1778c2ecf20Sopenharmony_ci		dev_dbg(isp->dev, "CCDC: LSC: too small table\n");
1788c2ecf20Sopenharmony_ci		return -EINVAL;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci	if (lsc_cfg->offset < (min_width * 4)) {
1818c2ecf20Sopenharmony_ci		dev_dbg(isp->dev, "CCDC: LSC: Offset is too small\n");
1828c2ecf20Sopenharmony_ci		return -EINVAL;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	if ((lsc_cfg->size / lsc_cfg->offset) < min_height) {
1858c2ecf20Sopenharmony_ci		dev_dbg(isp->dev, "CCDC: LSC: Wrong size/offset combination\n");
1868c2ecf20Sopenharmony_ci		return -EINVAL;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/*
1928c2ecf20Sopenharmony_ci * ccdc_lsc_program_table - Program Lens Shading Compensation table address.
1938c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
1948c2ecf20Sopenharmony_ci */
1958c2ecf20Sopenharmony_cistatic void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc,
1968c2ecf20Sopenharmony_ci				   dma_addr_t addr)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	isp_reg_writel(to_isp_device(ccdc), addr,
1998c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/*
2038c2ecf20Sopenharmony_ci * ccdc_lsc_setup_regs - Configures the lens shading compensation module
2048c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_cistatic void ccdc_lsc_setup_regs(struct isp_ccdc_device *ccdc,
2078c2ecf20Sopenharmony_ci				struct omap3isp_ccdc_lsc_config *cfg)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
2108c2ecf20Sopenharmony_ci	int reg;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	isp_reg_writel(isp, cfg->offset, OMAP3_ISP_IOMEM_CCDC,
2138c2ecf20Sopenharmony_ci		       ISPCCDC_LSC_TABLE_OFFSET);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	reg = 0;
2168c2ecf20Sopenharmony_ci	reg |= cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT;
2178c2ecf20Sopenharmony_ci	reg |= cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT;
2188c2ecf20Sopenharmony_ci	reg |= cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT;
2198c2ecf20Sopenharmony_ci	isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	reg = 0;
2228c2ecf20Sopenharmony_ci	reg &= ~ISPCCDC_LSC_INITIAL_X_MASK;
2238c2ecf20Sopenharmony_ci	reg |= cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT;
2248c2ecf20Sopenharmony_ci	reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK;
2258c2ecf20Sopenharmony_ci	reg |= cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT;
2268c2ecf20Sopenharmony_ci	isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC,
2278c2ecf20Sopenharmony_ci		       ISPCCDC_LSC_INITIAL);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int ccdc_lsc_wait_prefetch(struct isp_ccdc_device *ccdc)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
2338c2ecf20Sopenharmony_ci	unsigned int wait;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ,
2368c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* timeout 1 ms */
2398c2ecf20Sopenharmony_ci	for (wait = 0; wait < 1000; wait++) {
2408c2ecf20Sopenharmony_ci		if (isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS) &
2418c2ecf20Sopenharmony_ci				  IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ) {
2428c2ecf20Sopenharmony_ci			isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ,
2438c2ecf20Sopenharmony_ci				       OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
2448c2ecf20Sopenharmony_ci			return 0;
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		rmb();
2488c2ecf20Sopenharmony_ci		udelay(1);
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/*
2558c2ecf20Sopenharmony_ci * __ccdc_lsc_enable - Enables/Disables the Lens Shading Compensation module.
2568c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
2578c2ecf20Sopenharmony_ci * @enable: 0 Disables LSC, 1 Enables LSC.
2588c2ecf20Sopenharmony_ci */
2598c2ecf20Sopenharmony_cistatic int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
2628c2ecf20Sopenharmony_ci	const struct v4l2_mbus_framefmt *format =
2638c2ecf20Sopenharmony_ci		__ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK,
2648c2ecf20Sopenharmony_ci				  V4L2_SUBDEV_FORMAT_ACTIVE);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if ((format->code != MEDIA_BUS_FMT_SGRBG10_1X10) &&
2678c2ecf20Sopenharmony_ci	    (format->code != MEDIA_BUS_FMT_SRGGB10_1X10) &&
2688c2ecf20Sopenharmony_ci	    (format->code != MEDIA_BUS_FMT_SBGGR10_1X10) &&
2698c2ecf20Sopenharmony_ci	    (format->code != MEDIA_BUS_FMT_SGBRG10_1X10))
2708c2ecf20Sopenharmony_ci		return -EINVAL;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (enable)
2738c2ecf20Sopenharmony_ci		omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_LSC_READ);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG,
2768c2ecf20Sopenharmony_ci			ISPCCDC_LSC_ENABLE, enable ? ISPCCDC_LSC_ENABLE : 0);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (enable) {
2798c2ecf20Sopenharmony_ci		if (ccdc_lsc_wait_prefetch(ccdc) < 0) {
2808c2ecf20Sopenharmony_ci			isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC,
2818c2ecf20Sopenharmony_ci				    ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE);
2828c2ecf20Sopenharmony_ci			ccdc->lsc.state = LSC_STATE_STOPPED;
2838c2ecf20Sopenharmony_ci			dev_warn(to_device(ccdc), "LSC prefetch timeout\n");
2848c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci		ccdc->lsc.state = LSC_STATE_RUNNING;
2878c2ecf20Sopenharmony_ci	} else {
2888c2ecf20Sopenharmony_ci		ccdc->lsc.state = LSC_STATE_STOPPING;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return 0;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int ccdc_lsc_busy(struct isp_ccdc_device *ccdc)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG) &
2998c2ecf20Sopenharmony_ci			     ISPCCDC_LSC_BUSY;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/* __ccdc_lsc_configure - Apply a new configuration to the LSC engine
3038c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device
3048c2ecf20Sopenharmony_ci * @req: New configuration request
3058c2ecf20Sopenharmony_ci *
3068c2ecf20Sopenharmony_ci * context: in_interrupt()
3078c2ecf20Sopenharmony_ci */
3088c2ecf20Sopenharmony_cistatic int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc,
3098c2ecf20Sopenharmony_ci				struct ispccdc_lsc_config_req *req)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	if (!req->enable)
3128c2ecf20Sopenharmony_ci		return -EINVAL;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (ccdc_lsc_validate_config(ccdc, &req->config) < 0) {
3158c2ecf20Sopenharmony_ci		dev_dbg(to_device(ccdc), "Discard LSC configuration\n");
3168c2ecf20Sopenharmony_ci		return -EINVAL;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (ccdc_lsc_busy(ccdc))
3208c2ecf20Sopenharmony_ci		return -EBUSY;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	ccdc_lsc_setup_regs(ccdc, &req->config);
3238c2ecf20Sopenharmony_ci	ccdc_lsc_program_table(ccdc, req->table.dma);
3248c2ecf20Sopenharmony_ci	return 0;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci/*
3288c2ecf20Sopenharmony_ci * ccdc_lsc_error_handler - Handle LSC prefetch error scenario.
3298c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
3308c2ecf20Sopenharmony_ci *
3318c2ecf20Sopenharmony_ci * Disables LSC, and defers enablement to shadow registers update time.
3328c2ecf20Sopenharmony_ci */
3338c2ecf20Sopenharmony_cistatic void ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
3368c2ecf20Sopenharmony_ci	/*
3378c2ecf20Sopenharmony_ci	 * From OMAP3 TRM: When this event is pending, the module
3388c2ecf20Sopenharmony_ci	 * goes into transparent mode (output =input). Normal
3398c2ecf20Sopenharmony_ci	 * operation can be resumed at the start of the next frame
3408c2ecf20Sopenharmony_ci	 * after:
3418c2ecf20Sopenharmony_ci	 *  1) Clearing this event
3428c2ecf20Sopenharmony_ci	 *  2) Disabling the LSC module
3438c2ecf20Sopenharmony_ci	 *  3) Enabling it
3448c2ecf20Sopenharmony_ci	 */
3458c2ecf20Sopenharmony_ci	isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG,
3468c2ecf20Sopenharmony_ci		    ISPCCDC_LSC_ENABLE);
3478c2ecf20Sopenharmony_ci	ccdc->lsc.state = LSC_STATE_STOPPED;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
3518c2ecf20Sopenharmony_ci				  struct ispccdc_lsc_config_req *req)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (req == NULL)
3568c2ecf20Sopenharmony_ci		return;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (req->table.addr) {
3598c2ecf20Sopenharmony_ci		sg_free_table(&req->table.sgt);
3608c2ecf20Sopenharmony_ci		dma_free_coherent(isp->dev, req->config.size, req->table.addr,
3618c2ecf20Sopenharmony_ci				  req->table.dma);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	kfree(req);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic void ccdc_lsc_free_queue(struct isp_ccdc_device *ccdc,
3688c2ecf20Sopenharmony_ci				struct list_head *queue)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct ispccdc_lsc_config_req *req, *n;
3718c2ecf20Sopenharmony_ci	unsigned long flags;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
3748c2ecf20Sopenharmony_ci	list_for_each_entry_safe(req, n, queue, list) {
3758c2ecf20Sopenharmony_ci		list_del(&req->list);
3768c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
3778c2ecf20Sopenharmony_ci		ccdc_lsc_free_request(ccdc, req);
3788c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic void ccdc_lsc_free_table_work(struct work_struct *work)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc;
3868c2ecf20Sopenharmony_ci	struct ispccdc_lsc *lsc;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	lsc = container_of(work, struct ispccdc_lsc, table_work);
3898c2ecf20Sopenharmony_ci	ccdc = container_of(lsc, struct isp_ccdc_device, lsc);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	ccdc_lsc_free_queue(ccdc, &lsc->free_queue);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/*
3958c2ecf20Sopenharmony_ci * ccdc_lsc_config - Configure the LSC module from a userspace request
3968c2ecf20Sopenharmony_ci *
3978c2ecf20Sopenharmony_ci * Store the request LSC configuration in the LSC engine request pointer. The
3988c2ecf20Sopenharmony_ci * configuration will be applied to the hardware when the CCDC will be enabled,
3998c2ecf20Sopenharmony_ci * or at the next LSC interrupt if the CCDC is already running.
4008c2ecf20Sopenharmony_ci */
4018c2ecf20Sopenharmony_cistatic int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
4028c2ecf20Sopenharmony_ci			   struct omap3isp_ccdc_update_config *config)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
4058c2ecf20Sopenharmony_ci	struct ispccdc_lsc_config_req *req;
4068c2ecf20Sopenharmony_ci	unsigned long flags;
4078c2ecf20Sopenharmony_ci	u16 update;
4088c2ecf20Sopenharmony_ci	int ret;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	update = config->update &
4118c2ecf20Sopenharmony_ci		 (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC);
4128c2ecf20Sopenharmony_ci	if (!update)
4138c2ecf20Sopenharmony_ci		return 0;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) {
4168c2ecf20Sopenharmony_ci		dev_dbg(to_device(ccdc),
4178c2ecf20Sopenharmony_ci			"%s: Both LSC configuration and table need to be supplied\n",
4188c2ecf20Sopenharmony_ci			__func__);
4198c2ecf20Sopenharmony_ci		return -EINVAL;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
4238c2ecf20Sopenharmony_ci	if (req == NULL)
4248c2ecf20Sopenharmony_ci		return -ENOMEM;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) {
4278c2ecf20Sopenharmony_ci		if (copy_from_user(&req->config, config->lsc_cfg,
4288c2ecf20Sopenharmony_ci				   sizeof(req->config))) {
4298c2ecf20Sopenharmony_ci			ret = -EFAULT;
4308c2ecf20Sopenharmony_ci			goto done;
4318c2ecf20Sopenharmony_ci		}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		req->enable = 1;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci		req->table.addr = dma_alloc_coherent(isp->dev, req->config.size,
4368c2ecf20Sopenharmony_ci						     &req->table.dma,
4378c2ecf20Sopenharmony_ci						     GFP_KERNEL);
4388c2ecf20Sopenharmony_ci		if (req->table.addr == NULL) {
4398c2ecf20Sopenharmony_ci			ret = -ENOMEM;
4408c2ecf20Sopenharmony_ci			goto done;
4418c2ecf20Sopenharmony_ci		}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		ret = dma_get_sgtable(isp->dev, &req->table.sgt,
4448c2ecf20Sopenharmony_ci				      req->table.addr, req->table.dma,
4458c2ecf20Sopenharmony_ci				      req->config.size);
4468c2ecf20Sopenharmony_ci		if (ret < 0)
4478c2ecf20Sopenharmony_ci			goto done;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		dma_sync_sg_for_cpu(isp->dev, req->table.sgt.sgl,
4508c2ecf20Sopenharmony_ci				    req->table.sgt.nents, DMA_TO_DEVICE);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci		if (copy_from_user(req->table.addr, config->lsc,
4538c2ecf20Sopenharmony_ci				   req->config.size)) {
4548c2ecf20Sopenharmony_ci			ret = -EFAULT;
4558c2ecf20Sopenharmony_ci			goto done;
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		dma_sync_sg_for_device(isp->dev, req->table.sgt.sgl,
4598c2ecf20Sopenharmony_ci				       req->table.sgt.nents, DMA_TO_DEVICE);
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
4638c2ecf20Sopenharmony_ci	if (ccdc->lsc.request) {
4648c2ecf20Sopenharmony_ci		list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue);
4658c2ecf20Sopenharmony_ci		schedule_work(&ccdc->lsc.table_work);
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci	ccdc->lsc.request = req;
4688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	ret = 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cidone:
4738c2ecf20Sopenharmony_ci	if (ret < 0)
4748c2ecf20Sopenharmony_ci		ccdc_lsc_free_request(ccdc, req);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	return ret;
4778c2ecf20Sopenharmony_ci}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	unsigned long flags;
4828c2ecf20Sopenharmony_ci	int ret;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
4858c2ecf20Sopenharmony_ci	ret = ccdc->lsc.active != NULL;
4868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return ret;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic int ccdc_lsc_enable(struct isp_ccdc_device *ccdc)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct ispccdc_lsc *lsc = &ccdc->lsc;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	if (lsc->state != LSC_STATE_STOPPED)
4968c2ecf20Sopenharmony_ci		return -EINVAL;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	if (lsc->active) {
4998c2ecf20Sopenharmony_ci		list_add_tail(&lsc->active->list, &lsc->free_queue);
5008c2ecf20Sopenharmony_ci		lsc->active = NULL;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (__ccdc_lsc_configure(ccdc, lsc->request) < 0) {
5048c2ecf20Sopenharmony_ci		omap3isp_sbl_disable(to_isp_device(ccdc),
5058c2ecf20Sopenharmony_ci				OMAP3_ISP_SBL_CCDC_LSC_READ);
5068c2ecf20Sopenharmony_ci		list_add_tail(&lsc->request->list, &lsc->free_queue);
5078c2ecf20Sopenharmony_ci		lsc->request = NULL;
5088c2ecf20Sopenharmony_ci		goto done;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	lsc->active = lsc->request;
5128c2ecf20Sopenharmony_ci	lsc->request = NULL;
5138c2ecf20Sopenharmony_ci	__ccdc_lsc_enable(ccdc, 1);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cidone:
5168c2ecf20Sopenharmony_ci	if (!list_empty(&lsc->free_queue))
5178c2ecf20Sopenharmony_ci		schedule_work(&lsc->table_work);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	return 0;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
5238c2ecf20Sopenharmony_ci * Parameters configuration
5248c2ecf20Sopenharmony_ci */
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci/*
5278c2ecf20Sopenharmony_ci * ccdc_configure_clamp - Configure optical-black or digital clamping
5288c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
5298c2ecf20Sopenharmony_ci *
5308c2ecf20Sopenharmony_ci * The CCDC performs either optical-black or digital clamp. Configure and enable
5318c2ecf20Sopenharmony_ci * the selected clamp method.
5328c2ecf20Sopenharmony_ci */
5338c2ecf20Sopenharmony_cistatic void ccdc_configure_clamp(struct isp_ccdc_device *ccdc)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
5368c2ecf20Sopenharmony_ci	u32 clamp;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (ccdc->obclamp) {
5398c2ecf20Sopenharmony_ci		clamp  = ccdc->clamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT;
5408c2ecf20Sopenharmony_ci		clamp |= ccdc->clamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT;
5418c2ecf20Sopenharmony_ci		clamp |= ccdc->clamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT;
5428c2ecf20Sopenharmony_ci		clamp |= ccdc->clamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT;
5438c2ecf20Sopenharmony_ci		isp_reg_writel(isp, clamp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP);
5448c2ecf20Sopenharmony_ci	} else {
5458c2ecf20Sopenharmony_ci		isp_reg_writel(isp, ccdc->clamp.dcsubval,
5468c2ecf20Sopenharmony_ci			       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB);
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP,
5508c2ecf20Sopenharmony_ci			ISPCCDC_CLAMP_CLAMPEN,
5518c2ecf20Sopenharmony_ci			ccdc->obclamp ? ISPCCDC_CLAMP_CLAMPEN : 0);
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci/*
5558c2ecf20Sopenharmony_ci * ccdc_configure_fpc - Configure Faulty Pixel Correction
5568c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
5578c2ecf20Sopenharmony_ci */
5588c2ecf20Sopenharmony_cistatic void ccdc_configure_fpc(struct isp_ccdc_device *ccdc)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ISPCCDC_FPC_FPCEN);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	if (!ccdc->fpc_en)
5658c2ecf20Sopenharmony_ci		return;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	isp_reg_writel(isp, ccdc->fpc.dma, OMAP3_ISP_IOMEM_CCDC,
5688c2ecf20Sopenharmony_ci		       ISPCCDC_FPC_ADDR);
5698c2ecf20Sopenharmony_ci	/* The FPNUM field must be set before enabling FPC. */
5708c2ecf20Sopenharmony_ci	isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT),
5718c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
5728c2ecf20Sopenharmony_ci	isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) |
5738c2ecf20Sopenharmony_ci		       ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci/*
5778c2ecf20Sopenharmony_ci * ccdc_configure_black_comp - Configure Black Level Compensation.
5788c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
5798c2ecf20Sopenharmony_ci */
5808c2ecf20Sopenharmony_cistatic void ccdc_configure_black_comp(struct isp_ccdc_device *ccdc)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
5838c2ecf20Sopenharmony_ci	u32 blcomp;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	blcomp  = ccdc->blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT;
5868c2ecf20Sopenharmony_ci	blcomp |= ccdc->blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT;
5878c2ecf20Sopenharmony_ci	blcomp |= ccdc->blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT;
5888c2ecf20Sopenharmony_ci	blcomp |= ccdc->blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	isp_reg_writel(isp, blcomp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP);
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci/*
5948c2ecf20Sopenharmony_ci * ccdc_configure_lpf - Configure Low-Pass Filter (LPF).
5958c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
5968c2ecf20Sopenharmony_ci */
5978c2ecf20Sopenharmony_cistatic void ccdc_configure_lpf(struct isp_ccdc_device *ccdc)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE,
6028c2ecf20Sopenharmony_ci			ISPCCDC_SYN_MODE_LPF,
6038c2ecf20Sopenharmony_ci			ccdc->lpf ? ISPCCDC_SYN_MODE_LPF : 0);
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci/*
6078c2ecf20Sopenharmony_ci * ccdc_configure_alaw - Configure A-law compression.
6088c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
6098c2ecf20Sopenharmony_ci */
6108c2ecf20Sopenharmony_cistatic void ccdc_configure_alaw(struct isp_ccdc_device *ccdc)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
6138c2ecf20Sopenharmony_ci	const struct isp_format_info *info;
6148c2ecf20Sopenharmony_ci	u32 alaw = 0;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	info = omap3isp_video_format_info(ccdc->formats[CCDC_PAD_SINK].code);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	switch (info->width) {
6198c2ecf20Sopenharmony_ci	case 8:
6208c2ecf20Sopenharmony_ci		return;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	case 10:
6238c2ecf20Sopenharmony_ci		alaw = ISPCCDC_ALAW_GWDI_9_0;
6248c2ecf20Sopenharmony_ci		break;
6258c2ecf20Sopenharmony_ci	case 11:
6268c2ecf20Sopenharmony_ci		alaw = ISPCCDC_ALAW_GWDI_10_1;
6278c2ecf20Sopenharmony_ci		break;
6288c2ecf20Sopenharmony_ci	case 12:
6298c2ecf20Sopenharmony_ci		alaw = ISPCCDC_ALAW_GWDI_11_2;
6308c2ecf20Sopenharmony_ci		break;
6318c2ecf20Sopenharmony_ci	case 13:
6328c2ecf20Sopenharmony_ci		alaw = ISPCCDC_ALAW_GWDI_12_3;
6338c2ecf20Sopenharmony_ci		break;
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (ccdc->alaw)
6378c2ecf20Sopenharmony_ci		alaw |= ISPCCDC_ALAW_CCDTBL;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	isp_reg_writel(isp, alaw, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW);
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci/*
6438c2ecf20Sopenharmony_ci * ccdc_config_imgattr - Configure sensor image specific attributes.
6448c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
6458c2ecf20Sopenharmony_ci * @colptn: Color pattern of the sensor.
6468c2ecf20Sopenharmony_ci */
6478c2ecf20Sopenharmony_cistatic void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	isp_reg_writel(isp, colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN);
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci/*
6558c2ecf20Sopenharmony_ci * ccdc_config - Set CCDC configuration from userspace
6568c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
6578c2ecf20Sopenharmony_ci * @ccdc_struct: Structure containing CCDC configuration sent from userspace.
6588c2ecf20Sopenharmony_ci *
6598c2ecf20Sopenharmony_ci * Returns 0 if successful, -EINVAL if the pointer to the configuration
6608c2ecf20Sopenharmony_ci * structure is null, or the copy_from_user function fails to copy user space
6618c2ecf20Sopenharmony_ci * memory to kernel space memory.
6628c2ecf20Sopenharmony_ci */
6638c2ecf20Sopenharmony_cistatic int ccdc_config(struct isp_ccdc_device *ccdc,
6648c2ecf20Sopenharmony_ci		       struct omap3isp_ccdc_update_config *ccdc_struct)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
6678c2ecf20Sopenharmony_ci	unsigned long flags;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lock, flags);
6708c2ecf20Sopenharmony_ci	ccdc->shadow_update = 1;
6718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lock, flags);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	if (OMAP3ISP_CCDC_ALAW & ccdc_struct->update) {
6748c2ecf20Sopenharmony_ci		ccdc->alaw = !!(OMAP3ISP_CCDC_ALAW & ccdc_struct->flag);
6758c2ecf20Sopenharmony_ci		ccdc->update |= OMAP3ISP_CCDC_ALAW;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (OMAP3ISP_CCDC_LPF & ccdc_struct->update) {
6798c2ecf20Sopenharmony_ci		ccdc->lpf = !!(OMAP3ISP_CCDC_LPF & ccdc_struct->flag);
6808c2ecf20Sopenharmony_ci		ccdc->update |= OMAP3ISP_CCDC_LPF;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->update) {
6848c2ecf20Sopenharmony_ci		if (copy_from_user(&ccdc->clamp, ccdc_struct->bclamp,
6858c2ecf20Sopenharmony_ci				   sizeof(ccdc->clamp))) {
6868c2ecf20Sopenharmony_ci			ccdc->shadow_update = 0;
6878c2ecf20Sopenharmony_ci			return -EFAULT;
6888c2ecf20Sopenharmony_ci		}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		ccdc->obclamp = !!(OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->flag);
6918c2ecf20Sopenharmony_ci		ccdc->update |= OMAP3ISP_CCDC_BLCLAMP;
6928c2ecf20Sopenharmony_ci	}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	if (OMAP3ISP_CCDC_BCOMP & ccdc_struct->update) {
6958c2ecf20Sopenharmony_ci		if (copy_from_user(&ccdc->blcomp, ccdc_struct->blcomp,
6968c2ecf20Sopenharmony_ci				   sizeof(ccdc->blcomp))) {
6978c2ecf20Sopenharmony_ci			ccdc->shadow_update = 0;
6988c2ecf20Sopenharmony_ci			return -EFAULT;
6998c2ecf20Sopenharmony_ci		}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci		ccdc->update |= OMAP3ISP_CCDC_BCOMP;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	ccdc->shadow_update = 0;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) {
7078c2ecf20Sopenharmony_ci		struct omap3isp_ccdc_fpc fpc;
7088c2ecf20Sopenharmony_ci		struct ispccdc_fpc fpc_old = { .addr = NULL, };
7098c2ecf20Sopenharmony_ci		struct ispccdc_fpc fpc_new;
7108c2ecf20Sopenharmony_ci		u32 size;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
7138c2ecf20Sopenharmony_ci			return -EBUSY;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci		ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		if (ccdc->fpc_en) {
7188c2ecf20Sopenharmony_ci			if (copy_from_user(&fpc, ccdc_struct->fpc, sizeof(fpc)))
7198c2ecf20Sopenharmony_ci				return -EFAULT;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci			size = fpc.fpnum * 4;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci			/*
7248c2ecf20Sopenharmony_ci			 * The table address must be 64-bytes aligned, which is
7258c2ecf20Sopenharmony_ci			 * guaranteed by dma_alloc_coherent().
7268c2ecf20Sopenharmony_ci			 */
7278c2ecf20Sopenharmony_ci			fpc_new.fpnum = fpc.fpnum;
7288c2ecf20Sopenharmony_ci			fpc_new.addr = dma_alloc_coherent(isp->dev, size,
7298c2ecf20Sopenharmony_ci							  &fpc_new.dma,
7308c2ecf20Sopenharmony_ci							  GFP_KERNEL);
7318c2ecf20Sopenharmony_ci			if (fpc_new.addr == NULL)
7328c2ecf20Sopenharmony_ci				return -ENOMEM;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci			if (copy_from_user(fpc_new.addr,
7358c2ecf20Sopenharmony_ci					   (__force void __user *)(long)fpc.fpcaddr,
7368c2ecf20Sopenharmony_ci					   size)) {
7378c2ecf20Sopenharmony_ci				dma_free_coherent(isp->dev, size, fpc_new.addr,
7388c2ecf20Sopenharmony_ci						  fpc_new.dma);
7398c2ecf20Sopenharmony_ci				return -EFAULT;
7408c2ecf20Sopenharmony_ci			}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci			fpc_old = ccdc->fpc;
7438c2ecf20Sopenharmony_ci			ccdc->fpc = fpc_new;
7448c2ecf20Sopenharmony_ci		}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci		ccdc_configure_fpc(ccdc);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		if (fpc_old.addr != NULL)
7498c2ecf20Sopenharmony_ci			dma_free_coherent(isp->dev, fpc_old.fpnum * 4,
7508c2ecf20Sopenharmony_ci					  fpc_old.addr, fpc_old.dma);
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	return ccdc_lsc_config(ccdc, ccdc_struct);
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_cistatic void ccdc_apply_controls(struct isp_ccdc_device *ccdc)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	if (ccdc->update & OMAP3ISP_CCDC_ALAW) {
7598c2ecf20Sopenharmony_ci		ccdc_configure_alaw(ccdc);
7608c2ecf20Sopenharmony_ci		ccdc->update &= ~OMAP3ISP_CCDC_ALAW;
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (ccdc->update & OMAP3ISP_CCDC_LPF) {
7648c2ecf20Sopenharmony_ci		ccdc_configure_lpf(ccdc);
7658c2ecf20Sopenharmony_ci		ccdc->update &= ~OMAP3ISP_CCDC_LPF;
7668c2ecf20Sopenharmony_ci	}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	if (ccdc->update & OMAP3ISP_CCDC_BLCLAMP) {
7698c2ecf20Sopenharmony_ci		ccdc_configure_clamp(ccdc);
7708c2ecf20Sopenharmony_ci		ccdc->update &= ~OMAP3ISP_CCDC_BLCLAMP;
7718c2ecf20Sopenharmony_ci	}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	if (ccdc->update & OMAP3ISP_CCDC_BCOMP) {
7748c2ecf20Sopenharmony_ci		ccdc_configure_black_comp(ccdc);
7758c2ecf20Sopenharmony_ci		ccdc->update &= ~OMAP3ISP_CCDC_BCOMP;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci/*
7808c2ecf20Sopenharmony_ci * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers
7818c2ecf20Sopenharmony_ci * @isp: Pointer to ISP device
7828c2ecf20Sopenharmony_ci */
7838c2ecf20Sopenharmony_civoid omap3isp_ccdc_restore_context(struct isp_device *isp)
7848c2ecf20Sopenharmony_ci{
7858c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	ccdc->update = OMAP3ISP_CCDC_ALAW | OMAP3ISP_CCDC_LPF
7908c2ecf20Sopenharmony_ci		     | OMAP3ISP_CCDC_BLCLAMP | OMAP3ISP_CCDC_BCOMP;
7918c2ecf20Sopenharmony_ci	ccdc_apply_controls(ccdc);
7928c2ecf20Sopenharmony_ci	ccdc_configure_fpc(ccdc);
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
7968c2ecf20Sopenharmony_ci * Format- and pipeline-related configuration helpers
7978c2ecf20Sopenharmony_ci */
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci/*
8008c2ecf20Sopenharmony_ci * ccdc_config_vp - Configure the Video Port.
8018c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
8028c2ecf20Sopenharmony_ci */
8038c2ecf20Sopenharmony_cistatic void ccdc_config_vp(struct isp_ccdc_device *ccdc)
8048c2ecf20Sopenharmony_ci{
8058c2ecf20Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
8068c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
8078c2ecf20Sopenharmony_ci	const struct isp_format_info *info;
8088c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
8098c2ecf20Sopenharmony_ci	unsigned long l3_ick = pipe->l3_ick;
8108c2ecf20Sopenharmony_ci	unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8;
8118c2ecf20Sopenharmony_ci	unsigned int div = 0;
8128c2ecf20Sopenharmony_ci	u32 fmtcfg = ISPCCDC_FMTCFG_VPEN;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (!format->code) {
8178c2ecf20Sopenharmony_ci		/* Disable the video port when the input format isn't supported.
8188c2ecf20Sopenharmony_ci		 * This is indicated by a pixel code set to 0.
8198c2ecf20Sopenharmony_ci		 */
8208c2ecf20Sopenharmony_ci		isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
8218c2ecf20Sopenharmony_ci		return;
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) |
8258c2ecf20Sopenharmony_ci		       (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT),
8268c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ);
8278c2ecf20Sopenharmony_ci	isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
8288c2ecf20Sopenharmony_ci		       ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
8298c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) |
8328c2ecf20Sopenharmony_ci		       (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
8338c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	info = omap3isp_video_format_info(ccdc->formats[CCDC_PAD_SINK].code);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	switch (info->width) {
8388c2ecf20Sopenharmony_ci	case 8:
8398c2ecf20Sopenharmony_ci	case 10:
8408c2ecf20Sopenharmony_ci		fmtcfg |= ISPCCDC_FMTCFG_VPIN_9_0;
8418c2ecf20Sopenharmony_ci		break;
8428c2ecf20Sopenharmony_ci	case 11:
8438c2ecf20Sopenharmony_ci		fmtcfg |= ISPCCDC_FMTCFG_VPIN_10_1;
8448c2ecf20Sopenharmony_ci		break;
8458c2ecf20Sopenharmony_ci	case 12:
8468c2ecf20Sopenharmony_ci		fmtcfg |= ISPCCDC_FMTCFG_VPIN_11_2;
8478c2ecf20Sopenharmony_ci		break;
8488c2ecf20Sopenharmony_ci	case 13:
8498c2ecf20Sopenharmony_ci		fmtcfg |= ISPCCDC_FMTCFG_VPIN_12_3;
8508c2ecf20Sopenharmony_ci		break;
8518c2ecf20Sopenharmony_ci	}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	if (pipe->input)
8548c2ecf20Sopenharmony_ci		div = DIV_ROUND_UP(l3_ick, pipe->max_rate);
8558c2ecf20Sopenharmony_ci	else if (pipe->external_rate)
8568c2ecf20Sopenharmony_ci		div = l3_ick / pipe->external_rate;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	div = clamp(div, 2U, max_div);
8598c2ecf20Sopenharmony_ci	fmtcfg |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	isp_reg_writel(isp, fmtcfg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
8628c2ecf20Sopenharmony_ci}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci/*
8658c2ecf20Sopenharmony_ci * ccdc_config_outlineoffset - Configure memory saving output line offset
8668c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
8678c2ecf20Sopenharmony_ci * @bpl: Number of bytes per line when stored in memory.
8688c2ecf20Sopenharmony_ci * @field: Field order when storing interlaced formats in memory.
8698c2ecf20Sopenharmony_ci *
8708c2ecf20Sopenharmony_ci * Configure the offsets for the line output control:
8718c2ecf20Sopenharmony_ci *
8728c2ecf20Sopenharmony_ci * - The horizontal line offset is defined as the number of bytes between the
8738c2ecf20Sopenharmony_ci *   start of two consecutive lines in memory. Set it to the given bytes per
8748c2ecf20Sopenharmony_ci *   line value.
8758c2ecf20Sopenharmony_ci *
8768c2ecf20Sopenharmony_ci * - The field offset value is defined as the number of lines to offset the
8778c2ecf20Sopenharmony_ci *   start of the field identified by FID = 1. Set it to one.
8788c2ecf20Sopenharmony_ci *
8798c2ecf20Sopenharmony_ci * - The line offset values are defined as the number of lines (as defined by
8808c2ecf20Sopenharmony_ci *   the horizontal line offset) between the start of two consecutive lines for
8818c2ecf20Sopenharmony_ci *   all combinations of odd/even lines in odd/even fields. When interleaving
8828c2ecf20Sopenharmony_ci *   fields set them all to two lines, and to one line otherwise.
8838c2ecf20Sopenharmony_ci */
8848c2ecf20Sopenharmony_cistatic void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc,
8858c2ecf20Sopenharmony_ci				      unsigned int bpl,
8868c2ecf20Sopenharmony_ci				      enum v4l2_field field)
8878c2ecf20Sopenharmony_ci{
8888c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
8898c2ecf20Sopenharmony_ci	u32 sdofst = 0;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	isp_reg_writel(isp, bpl & 0xffff, OMAP3_ISP_IOMEM_CCDC,
8928c2ecf20Sopenharmony_ci		       ISPCCDC_HSIZE_OFF);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	switch (field) {
8958c2ecf20Sopenharmony_ci	case V4L2_FIELD_INTERLACED_TB:
8968c2ecf20Sopenharmony_ci	case V4L2_FIELD_INTERLACED_BT:
8978c2ecf20Sopenharmony_ci		/* When interleaving fields in memory offset field one by one
8988c2ecf20Sopenharmony_ci		 * line and set the line offset to two lines.
8998c2ecf20Sopenharmony_ci		 */
9008c2ecf20Sopenharmony_ci		sdofst |= (1 << ISPCCDC_SDOFST_LOFST0_SHIFT)
9018c2ecf20Sopenharmony_ci		       |  (1 << ISPCCDC_SDOFST_LOFST1_SHIFT)
9028c2ecf20Sopenharmony_ci		       |  (1 << ISPCCDC_SDOFST_LOFST2_SHIFT)
9038c2ecf20Sopenharmony_ci		       |  (1 << ISPCCDC_SDOFST_LOFST3_SHIFT);
9048c2ecf20Sopenharmony_ci		break;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	default:
9078c2ecf20Sopenharmony_ci		/* In all other cases set the line offsets to one line. */
9088c2ecf20Sopenharmony_ci		break;
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	isp_reg_writel(isp, sdofst, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST);
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci/*
9158c2ecf20Sopenharmony_ci * ccdc_set_outaddr - Set memory address to save output image
9168c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
9178c2ecf20Sopenharmony_ci * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary.
9188c2ecf20Sopenharmony_ci *
9198c2ecf20Sopenharmony_ci * Sets the memory address where the output will be saved.
9208c2ecf20Sopenharmony_ci */
9218c2ecf20Sopenharmony_cistatic void ccdc_set_outaddr(struct isp_ccdc_device *ccdc, u32 addr)
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR);
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci/*
9298c2ecf20Sopenharmony_ci * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input
9308c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
9318c2ecf20Sopenharmony_ci * @max_rate: Maximum calculated data rate.
9328c2ecf20Sopenharmony_ci *
9338c2ecf20Sopenharmony_ci * Returns in *max_rate less value between calculated and passed
9348c2ecf20Sopenharmony_ci */
9358c2ecf20Sopenharmony_civoid omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc,
9368c2ecf20Sopenharmony_ci			    unsigned int *max_rate)
9378c2ecf20Sopenharmony_ci{
9388c2ecf20Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
9398c2ecf20Sopenharmony_ci	unsigned int rate;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	if (pipe == NULL)
9428c2ecf20Sopenharmony_ci		return;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	/*
9458c2ecf20Sopenharmony_ci	 * TRM says that for parallel sensors the maximum data rate
9468c2ecf20Sopenharmony_ci	 * should be 90% form L3/2 clock, otherwise just L3/2.
9478c2ecf20Sopenharmony_ci	 */
9488c2ecf20Sopenharmony_ci	if (ccdc->input == CCDC_INPUT_PARALLEL)
9498c2ecf20Sopenharmony_ci		rate = pipe->l3_ick / 2 * 9 / 10;
9508c2ecf20Sopenharmony_ci	else
9518c2ecf20Sopenharmony_ci		rate = pipe->l3_ick / 2;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	*max_rate = min(*max_rate, rate);
9548c2ecf20Sopenharmony_ci}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci/*
9578c2ecf20Sopenharmony_ci * ccdc_config_sync_if - Set CCDC sync interface configuration
9588c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
9598c2ecf20Sopenharmony_ci * @parcfg: Parallel interface platform data (may be NULL)
9608c2ecf20Sopenharmony_ci * @data_size: Data size
9618c2ecf20Sopenharmony_ci */
9628c2ecf20Sopenharmony_cistatic void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
9638c2ecf20Sopenharmony_ci				struct isp_parallel_cfg *parcfg,
9648c2ecf20Sopenharmony_ci				unsigned int data_size)
9658c2ecf20Sopenharmony_ci{
9668c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
9678c2ecf20Sopenharmony_ci	const struct v4l2_mbus_framefmt *format;
9688c2ecf20Sopenharmony_ci	u32 syn_mode = ISPCCDC_SYN_MODE_VDHDEN;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	format = &ccdc->formats[CCDC_PAD_SINK];
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
9738c2ecf20Sopenharmony_ci	    format->code == MEDIA_BUS_FMT_UYVY8_2X8) {
9748c2ecf20Sopenharmony_ci		/* According to the OMAP3 TRM the input mode only affects SYNC
9758c2ecf20Sopenharmony_ci		 * mode, enabling BT.656 mode should take precedence. However,
9768c2ecf20Sopenharmony_ci		 * in practice setting the input mode to YCbCr data on 8 bits
9778c2ecf20Sopenharmony_ci		 * seems to be required in BT.656 mode. In SYNC mode set it to
9788c2ecf20Sopenharmony_ci		 * YCbCr on 16 bits as the bridge is enabled in that case.
9798c2ecf20Sopenharmony_ci		 */
9808c2ecf20Sopenharmony_ci		if (ccdc->bt656)
9818c2ecf20Sopenharmony_ci			syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR8;
9828c2ecf20Sopenharmony_ci		else
9838c2ecf20Sopenharmony_ci			syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16;
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	switch (data_size) {
9878c2ecf20Sopenharmony_ci	case 8:
9888c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8;
9898c2ecf20Sopenharmony_ci		break;
9908c2ecf20Sopenharmony_ci	case 10:
9918c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10;
9928c2ecf20Sopenharmony_ci		break;
9938c2ecf20Sopenharmony_ci	case 11:
9948c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11;
9958c2ecf20Sopenharmony_ci		break;
9968c2ecf20Sopenharmony_ci	case 12:
9978c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12;
9988c2ecf20Sopenharmony_ci		break;
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	if (parcfg && parcfg->data_pol)
10028c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_DATAPOL;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	if (parcfg && parcfg->hs_pol)
10058c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_HDPOL;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	/* The polarity of the vertical sync signal output by the BT.656
10088c2ecf20Sopenharmony_ci	 * decoder is not documented and seems to be active low.
10098c2ecf20Sopenharmony_ci	 */
10108c2ecf20Sopenharmony_ci	if ((parcfg && parcfg->vs_pol) || ccdc->bt656)
10118c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	if (parcfg && parcfg->fld_pol)
10148c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_FLDPOL;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* The CCDC_CFG.Y8POS bit is used in YCbCr8 input mode only. The
10198c2ecf20Sopenharmony_ci	 * hardware seems to ignore it in all other input modes.
10208c2ecf20Sopenharmony_ci	 */
10218c2ecf20Sopenharmony_ci	if (format->code == MEDIA_BUS_FMT_UYVY8_2X8)
10228c2ecf20Sopenharmony_ci		isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
10238c2ecf20Sopenharmony_ci			    ISPCCDC_CFG_Y8POS);
10248c2ecf20Sopenharmony_ci	else
10258c2ecf20Sopenharmony_ci		isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
10268c2ecf20Sopenharmony_ci			    ISPCCDC_CFG_Y8POS);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	/* Enable or disable BT.656 mode, including error correction for the
10298c2ecf20Sopenharmony_ci	 * synchronization codes.
10308c2ecf20Sopenharmony_ci	 */
10318c2ecf20Sopenharmony_ci	if (ccdc->bt656)
10328c2ecf20Sopenharmony_ci		isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
10338c2ecf20Sopenharmony_ci			    ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH);
10348c2ecf20Sopenharmony_ci	else
10358c2ecf20Sopenharmony_ci		isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
10368c2ecf20Sopenharmony_ci			    ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci}
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci/* CCDC formats descriptions */
10418c2ecf20Sopenharmony_cistatic const u32 ccdc_sgrbg_pattern =
10428c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
10438c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
10448c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
10458c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
10468c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
10478c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
10488c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
10498c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
10508c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
10518c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
10528c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
10538c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
10548c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
10558c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
10568c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
10578c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_cistatic const u32 ccdc_srggb_pattern =
10608c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
10618c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
10628c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
10638c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
10648c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
10658c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
10668c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
10678c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
10688c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
10698c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
10708c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
10718c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
10728c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
10738c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
10748c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
10758c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_cistatic const u32 ccdc_sbggr_pattern =
10788c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
10798c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
10808c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
10818c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
10828c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
10838c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
10848c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
10858c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
10868c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
10878c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
10888c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
10898c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
10908c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
10918c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
10928c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
10938c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_cistatic const u32 ccdc_sgbrg_pattern =
10968c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
10978c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
10988c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
10998c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
11008c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
11018c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
11028c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
11038c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
11048c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
11058c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
11068c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gb_G  << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
11078c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_B_Mg  << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
11088c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
11098c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
11108c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_R_Ye  << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
11118c2ecf20Sopenharmony_ci	ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_cistatic void ccdc_configure(struct isp_ccdc_device *ccdc)
11148c2ecf20Sopenharmony_ci{
11158c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
11168c2ecf20Sopenharmony_ci	struct isp_parallel_cfg *parcfg = NULL;
11178c2ecf20Sopenharmony_ci	struct v4l2_subdev *sensor;
11188c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
11198c2ecf20Sopenharmony_ci	const struct v4l2_rect *crop;
11208c2ecf20Sopenharmony_ci	const struct isp_format_info *fmt_info;
11218c2ecf20Sopenharmony_ci	struct v4l2_subdev_format fmt_src;
11228c2ecf20Sopenharmony_ci	unsigned int depth_out;
11238c2ecf20Sopenharmony_ci	unsigned int depth_in = 0;
11248c2ecf20Sopenharmony_ci	struct media_pad *pad;
11258c2ecf20Sopenharmony_ci	unsigned long flags;
11268c2ecf20Sopenharmony_ci	unsigned int bridge;
11278c2ecf20Sopenharmony_ci	unsigned int shift;
11288c2ecf20Sopenharmony_ci	unsigned int nph;
11298c2ecf20Sopenharmony_ci	unsigned int sph;
11308c2ecf20Sopenharmony_ci	u32 syn_mode;
11318c2ecf20Sopenharmony_ci	u32 ccdc_pattern;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	ccdc->bt656 = false;
11348c2ecf20Sopenharmony_ci	ccdc->fields = 0;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	pad = media_entity_remote_pad(&ccdc->pads[CCDC_PAD_SINK]);
11378c2ecf20Sopenharmony_ci	sensor = media_entity_to_v4l2_subdev(pad->entity);
11388c2ecf20Sopenharmony_ci	if (ccdc->input == CCDC_INPUT_PARALLEL) {
11398c2ecf20Sopenharmony_ci		struct v4l2_subdev *sd =
11408c2ecf20Sopenharmony_ci			to_isp_pipeline(&ccdc->subdev.entity)->external;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci		parcfg = &v4l2_subdev_to_bus_cfg(sd)->bus.parallel;
11438c2ecf20Sopenharmony_ci		ccdc->bt656 = parcfg->bt656;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	/* CCDC_PAD_SINK */
11478c2ecf20Sopenharmony_ci	format = &ccdc->formats[CCDC_PAD_SINK];
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	/* Compute the lane shifter shift value and enable the bridge when the
11508c2ecf20Sopenharmony_ci	 * input format is a non-BT.656 YUV variant.
11518c2ecf20Sopenharmony_ci	 */
11528c2ecf20Sopenharmony_ci	fmt_src.pad = pad->index;
11538c2ecf20Sopenharmony_ci	fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
11548c2ecf20Sopenharmony_ci	if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) {
11558c2ecf20Sopenharmony_ci		fmt_info = omap3isp_video_format_info(fmt_src.format.code);
11568c2ecf20Sopenharmony_ci		depth_in = fmt_info->width;
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	fmt_info = omap3isp_video_format_info(format->code);
11608c2ecf20Sopenharmony_ci	depth_out = fmt_info->width;
11618c2ecf20Sopenharmony_ci	shift = depth_in - depth_out;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	if (ccdc->bt656)
11648c2ecf20Sopenharmony_ci		bridge = ISPCTRL_PAR_BRIDGE_DISABLE;
11658c2ecf20Sopenharmony_ci	else if (fmt_info->code == MEDIA_BUS_FMT_YUYV8_2X8)
11668c2ecf20Sopenharmony_ci		bridge = ISPCTRL_PAR_BRIDGE_LENDIAN;
11678c2ecf20Sopenharmony_ci	else if (fmt_info->code == MEDIA_BUS_FMT_UYVY8_2X8)
11688c2ecf20Sopenharmony_ci		bridge = ISPCTRL_PAR_BRIDGE_BENDIAN;
11698c2ecf20Sopenharmony_ci	else
11708c2ecf20Sopenharmony_ci		bridge = ISPCTRL_PAR_BRIDGE_DISABLE;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	omap3isp_configure_bridge(isp, ccdc->input, parcfg, shift, bridge);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	/* Configure the sync interface. */
11758c2ecf20Sopenharmony_ci	ccdc_config_sync_if(ccdc, parcfg, depth_out);
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	/* Use the raw, unprocessed data when writing to memory. The H3A and
11808c2ecf20Sopenharmony_ci	 * histogram modules are still fed with lens shading corrected data.
11818c2ecf20Sopenharmony_ci	 */
11828c2ecf20Sopenharmony_ci	syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	if (ccdc->output & CCDC_OUTPUT_MEMORY)
11858c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_WEN;
11868c2ecf20Sopenharmony_ci	else
11878c2ecf20Sopenharmony_ci		syn_mode &= ~ISPCCDC_SYN_MODE_WEN;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	if (ccdc->output & CCDC_OUTPUT_RESIZER)
11908c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ;
11918c2ecf20Sopenharmony_ci	else
11928c2ecf20Sopenharmony_ci		syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	/* Mosaic filter */
11958c2ecf20Sopenharmony_ci	switch (format->code) {
11968c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_SRGGB10_1X10:
11978c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_SRGGB12_1X12:
11988c2ecf20Sopenharmony_ci		ccdc_pattern = ccdc_srggb_pattern;
11998c2ecf20Sopenharmony_ci		break;
12008c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_SBGGR10_1X10:
12018c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_SBGGR12_1X12:
12028c2ecf20Sopenharmony_ci		ccdc_pattern = ccdc_sbggr_pattern;
12038c2ecf20Sopenharmony_ci		break;
12048c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_SGBRG10_1X10:
12058c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_SGBRG12_1X12:
12068c2ecf20Sopenharmony_ci		ccdc_pattern = ccdc_sgbrg_pattern;
12078c2ecf20Sopenharmony_ci		break;
12088c2ecf20Sopenharmony_ci	default:
12098c2ecf20Sopenharmony_ci		/* Use GRBG */
12108c2ecf20Sopenharmony_ci		ccdc_pattern = ccdc_sgrbg_pattern;
12118c2ecf20Sopenharmony_ci		break;
12128c2ecf20Sopenharmony_ci	}
12138c2ecf20Sopenharmony_ci	ccdc_config_imgattr(ccdc, ccdc_pattern);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	/* Generate VD0 on the last line of the image and VD1 on the
12168c2ecf20Sopenharmony_ci	 * 2/3 height line.
12178c2ecf20Sopenharmony_ci	 */
12188c2ecf20Sopenharmony_ci	isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) |
12198c2ecf20Sopenharmony_ci		       ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT),
12208c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	/* CCDC_PAD_SOURCE_OF */
12238c2ecf20Sopenharmony_ci	format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
12248c2ecf20Sopenharmony_ci	crop = &ccdc->crop;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	/* The horizontal coordinates are expressed in pixel clock cycles. We
12278c2ecf20Sopenharmony_ci	 * need two cycles per pixel in BT.656 mode, and one cycle per pixel in
12288c2ecf20Sopenharmony_ci	 * SYNC mode regardless of the format as the bridge is enabled for YUV
12298c2ecf20Sopenharmony_ci	 * formats in that case.
12308c2ecf20Sopenharmony_ci	 */
12318c2ecf20Sopenharmony_ci	if (ccdc->bt656) {
12328c2ecf20Sopenharmony_ci		sph = crop->left * 2;
12338c2ecf20Sopenharmony_ci		nph = crop->width * 2 - 1;
12348c2ecf20Sopenharmony_ci	} else {
12358c2ecf20Sopenharmony_ci		sph = crop->left;
12368c2ecf20Sopenharmony_ci		nph = crop->width - 1;
12378c2ecf20Sopenharmony_ci	}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	isp_reg_writel(isp, (sph << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
12408c2ecf20Sopenharmony_ci		       (nph << ISPCCDC_HORZ_INFO_NPH_SHIFT),
12418c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO);
12428c2ecf20Sopenharmony_ci	isp_reg_writel(isp, (crop->top << ISPCCDC_VERT_START_SLV0_SHIFT) |
12438c2ecf20Sopenharmony_ci		       (crop->top << ISPCCDC_VERT_START_SLV1_SHIFT),
12448c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
12458c2ecf20Sopenharmony_ci	isp_reg_writel(isp, (crop->height - 1)
12468c2ecf20Sopenharmony_ci			<< ISPCCDC_VERT_LINES_NLV_SHIFT,
12478c2ecf20Sopenharmony_ci		       OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value,
12508c2ecf20Sopenharmony_ci				  format->field);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	/* When interleaving fields enable processing of the field input signal.
12538c2ecf20Sopenharmony_ci	 * This will cause the line output control module to apply the field
12548c2ecf20Sopenharmony_ci	 * offset to field 1.
12558c2ecf20Sopenharmony_ci	 */
12568c2ecf20Sopenharmony_ci	if (ccdc->formats[CCDC_PAD_SINK].field == V4L2_FIELD_ALTERNATE &&
12578c2ecf20Sopenharmony_ci	    (format->field == V4L2_FIELD_INTERLACED_TB ||
12588c2ecf20Sopenharmony_ci	     format->field == V4L2_FIELD_INTERLACED_BT))
12598c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_FLDMODE;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	/* The CCDC outputs data in UYVY order by default. Swap bytes to get
12628c2ecf20Sopenharmony_ci	 * YUYV.
12638c2ecf20Sopenharmony_ci	 */
12648c2ecf20Sopenharmony_ci	if (format->code == MEDIA_BUS_FMT_YUYV8_1X16)
12658c2ecf20Sopenharmony_ci		isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
12668c2ecf20Sopenharmony_ci			    ISPCCDC_CFG_BSWD);
12678c2ecf20Sopenharmony_ci	else
12688c2ecf20Sopenharmony_ci		isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
12698c2ecf20Sopenharmony_ci			    ISPCCDC_CFG_BSWD);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	/* Use PACK8 mode for 1byte per pixel formats. Check for BT.656 mode
12728c2ecf20Sopenharmony_ci	 * explicitly as the driver reports 1X16 instead of 2X8 at the OF pad
12738c2ecf20Sopenharmony_ci	 * for simplicity.
12748c2ecf20Sopenharmony_ci	 */
12758c2ecf20Sopenharmony_ci	if (omap3isp_video_format_info(format->code)->width <= 8 || ccdc->bt656)
12768c2ecf20Sopenharmony_ci		syn_mode |= ISPCCDC_SYN_MODE_PACK8;
12778c2ecf20Sopenharmony_ci	else
12788c2ecf20Sopenharmony_ci		syn_mode &= ~ISPCCDC_SYN_MODE_PACK8;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	/* CCDC_PAD_SOURCE_VP */
12838c2ecf20Sopenharmony_ci	ccdc_config_vp(ccdc);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	/* Lens shading correction. */
12868c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
12878c2ecf20Sopenharmony_ci	if (ccdc->lsc.request == NULL)
12888c2ecf20Sopenharmony_ci		goto unlock;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	WARN_ON(ccdc->lsc.active);
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	/* Get last good LSC configuration. If it is not supported for
12938c2ecf20Sopenharmony_ci	 * the current active resolution discard it.
12948c2ecf20Sopenharmony_ci	 */
12958c2ecf20Sopenharmony_ci	if (ccdc->lsc.active == NULL &&
12968c2ecf20Sopenharmony_ci	    __ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) {
12978c2ecf20Sopenharmony_ci		ccdc->lsc.active = ccdc->lsc.request;
12988c2ecf20Sopenharmony_ci	} else {
12998c2ecf20Sopenharmony_ci		list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue);
13008c2ecf20Sopenharmony_ci		schedule_work(&ccdc->lsc.table_work);
13018c2ecf20Sopenharmony_ci	}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	ccdc->lsc.request = NULL;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ciunlock:
13068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	ccdc_apply_controls(ccdc);
13098c2ecf20Sopenharmony_ci}
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_cistatic void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	/* Avoid restarting the CCDC when streaming is stopping. */
13168c2ecf20Sopenharmony_ci	if (enable && ccdc->stopping & CCDC_STOP_REQUEST)
13178c2ecf20Sopenharmony_ci		return;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR,
13208c2ecf20Sopenharmony_ci			ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0);
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	ccdc->running = enable;
13238c2ecf20Sopenharmony_ci}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_cistatic int ccdc_disable(struct isp_ccdc_device *ccdc)
13268c2ecf20Sopenharmony_ci{
13278c2ecf20Sopenharmony_ci	unsigned long flags;
13288c2ecf20Sopenharmony_ci	int ret = 0;
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lock, flags);
13318c2ecf20Sopenharmony_ci	if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS)
13328c2ecf20Sopenharmony_ci		ccdc->stopping = CCDC_STOP_REQUEST;
13338c2ecf20Sopenharmony_ci	if (!ccdc->running)
13348c2ecf20Sopenharmony_ci		ccdc->stopping = CCDC_STOP_FINISHED;
13358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lock, flags);
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	ret = wait_event_timeout(ccdc->wait,
13388c2ecf20Sopenharmony_ci				 ccdc->stopping == CCDC_STOP_FINISHED,
13398c2ecf20Sopenharmony_ci				 msecs_to_jiffies(2000));
13408c2ecf20Sopenharmony_ci	if (ret == 0) {
13418c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
13428c2ecf20Sopenharmony_ci		dev_warn(to_device(ccdc), "CCDC stop timeout!\n");
13438c2ecf20Sopenharmony_ci	}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	omap3isp_sbl_disable(to_isp_device(ccdc), OMAP3_ISP_SBL_CCDC_LSC_READ);
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	mutex_lock(&ccdc->ioctl_lock);
13488c2ecf20Sopenharmony_ci	ccdc_lsc_free_request(ccdc, ccdc->lsc.request);
13498c2ecf20Sopenharmony_ci	ccdc->lsc.request = ccdc->lsc.active;
13508c2ecf20Sopenharmony_ci	ccdc->lsc.active = NULL;
13518c2ecf20Sopenharmony_ci	cancel_work_sync(&ccdc->lsc.table_work);
13528c2ecf20Sopenharmony_ci	ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
13538c2ecf20Sopenharmony_ci	mutex_unlock(&ccdc->ioctl_lock);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	ccdc->stopping = CCDC_STOP_NOT_REQUESTED;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	return ret > 0 ? 0 : ret;
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_cistatic void ccdc_enable(struct isp_ccdc_device *ccdc)
13618c2ecf20Sopenharmony_ci{
13628c2ecf20Sopenharmony_ci	if (ccdc_lsc_is_configured(ccdc))
13638c2ecf20Sopenharmony_ci		__ccdc_lsc_enable(ccdc, 1);
13648c2ecf20Sopenharmony_ci	__ccdc_enable(ccdc, 1);
13658c2ecf20Sopenharmony_ci}
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
13688c2ecf20Sopenharmony_ci * Interrupt handling
13698c2ecf20Sopenharmony_ci */
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci/*
13728c2ecf20Sopenharmony_ci * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits
13738c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
13748c2ecf20Sopenharmony_ci *
13758c2ecf20Sopenharmony_ci * Returns zero if the CCDC is idle and the image has been written to
13768c2ecf20Sopenharmony_ci * memory, too.
13778c2ecf20Sopenharmony_ci */
13788c2ecf20Sopenharmony_cistatic int ccdc_sbl_busy(struct isp_ccdc_device *ccdc)
13798c2ecf20Sopenharmony_ci{
13808c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	return omap3isp_ccdc_busy(ccdc)
13838c2ecf20Sopenharmony_ci		| (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) &
13848c2ecf20Sopenharmony_ci		   ISPSBL_CCDC_WR_0_DATA_READY)
13858c2ecf20Sopenharmony_ci		| (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) &
13868c2ecf20Sopenharmony_ci		   ISPSBL_CCDC_WR_0_DATA_READY)
13878c2ecf20Sopenharmony_ci		| (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) &
13888c2ecf20Sopenharmony_ci		   ISPSBL_CCDC_WR_0_DATA_READY)
13898c2ecf20Sopenharmony_ci		| (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) &
13908c2ecf20Sopenharmony_ci		   ISPSBL_CCDC_WR_0_DATA_READY);
13918c2ecf20Sopenharmony_ci}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci/*
13948c2ecf20Sopenharmony_ci * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle
13958c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
13968c2ecf20Sopenharmony_ci * @max_wait: Max retry count in us for wait for idle/busy transition.
13978c2ecf20Sopenharmony_ci */
13988c2ecf20Sopenharmony_cistatic int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc,
13998c2ecf20Sopenharmony_ci			      unsigned int max_wait)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	unsigned int wait = 0;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	if (max_wait == 0)
14048c2ecf20Sopenharmony_ci		max_wait = 10000; /* 10 ms */
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	for (wait = 0; wait <= max_wait; wait++) {
14078c2ecf20Sopenharmony_ci		if (!ccdc_sbl_busy(ccdc))
14088c2ecf20Sopenharmony_ci			return 0;
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci		rmb();
14118c2ecf20Sopenharmony_ci		udelay(1);
14128c2ecf20Sopenharmony_ci	}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	return -EBUSY;
14158c2ecf20Sopenharmony_ci}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci/* ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence
14188c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
14198c2ecf20Sopenharmony_ci * @event: Pointing which event trigger handler
14208c2ecf20Sopenharmony_ci *
14218c2ecf20Sopenharmony_ci * Return 1 when the event and stopping request combination is satisfied,
14228c2ecf20Sopenharmony_ci * zero otherwise.
14238c2ecf20Sopenharmony_ci */
14248c2ecf20Sopenharmony_cistatic int ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
14258c2ecf20Sopenharmony_ci{
14268c2ecf20Sopenharmony_ci	int rval = 0;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	switch ((ccdc->stopping & 3) | event) {
14298c2ecf20Sopenharmony_ci	case CCDC_STOP_REQUEST | CCDC_EVENT_VD1:
14308c2ecf20Sopenharmony_ci		if (ccdc->lsc.state != LSC_STATE_STOPPED)
14318c2ecf20Sopenharmony_ci			__ccdc_lsc_enable(ccdc, 0);
14328c2ecf20Sopenharmony_ci		__ccdc_enable(ccdc, 0);
14338c2ecf20Sopenharmony_ci		ccdc->stopping = CCDC_STOP_EXECUTED;
14348c2ecf20Sopenharmony_ci		return 1;
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	case CCDC_STOP_EXECUTED | CCDC_EVENT_VD0:
14378c2ecf20Sopenharmony_ci		ccdc->stopping |= CCDC_STOP_CCDC_FINISHED;
14388c2ecf20Sopenharmony_ci		if (ccdc->lsc.state == LSC_STATE_STOPPED)
14398c2ecf20Sopenharmony_ci			ccdc->stopping |= CCDC_STOP_LSC_FINISHED;
14408c2ecf20Sopenharmony_ci		rval = 1;
14418c2ecf20Sopenharmony_ci		break;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	case CCDC_STOP_EXECUTED | CCDC_EVENT_LSC_DONE:
14448c2ecf20Sopenharmony_ci		ccdc->stopping |= CCDC_STOP_LSC_FINISHED;
14458c2ecf20Sopenharmony_ci		rval = 1;
14468c2ecf20Sopenharmony_ci		break;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	case CCDC_STOP_EXECUTED | CCDC_EVENT_VD1:
14498c2ecf20Sopenharmony_ci		return 1;
14508c2ecf20Sopenharmony_ci	}
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	if (ccdc->stopping == CCDC_STOP_FINISHED) {
14538c2ecf20Sopenharmony_ci		wake_up(&ccdc->wait);
14548c2ecf20Sopenharmony_ci		rval = 1;
14558c2ecf20Sopenharmony_ci	}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	return rval;
14588c2ecf20Sopenharmony_ci}
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_cistatic void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc)
14618c2ecf20Sopenharmony_ci{
14628c2ecf20Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
14638c2ecf20Sopenharmony_ci	struct video_device *vdev = ccdc->subdev.devnode;
14648c2ecf20Sopenharmony_ci	struct v4l2_event event;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	/* Frame number propagation */
14678c2ecf20Sopenharmony_ci	atomic_inc(&pipe->frame_number);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	memset(&event, 0, sizeof(event));
14708c2ecf20Sopenharmony_ci	event.type = V4L2_EVENT_FRAME_SYNC;
14718c2ecf20Sopenharmony_ci	event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number);
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	v4l2_event_queue(vdev, &event);
14748c2ecf20Sopenharmony_ci}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci/*
14778c2ecf20Sopenharmony_ci * ccdc_lsc_isr - Handle LSC events
14788c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
14798c2ecf20Sopenharmony_ci * @events: LSC events
14808c2ecf20Sopenharmony_ci */
14818c2ecf20Sopenharmony_cistatic void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events)
14828c2ecf20Sopenharmony_ci{
14838c2ecf20Sopenharmony_ci	unsigned long flags;
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	if (events & IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ) {
14868c2ecf20Sopenharmony_ci		struct isp_pipeline *pipe =
14878c2ecf20Sopenharmony_ci			to_isp_pipeline(&ccdc->subdev.entity);
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci		ccdc_lsc_error_handler(ccdc);
14908c2ecf20Sopenharmony_ci		pipe->error = true;
14918c2ecf20Sopenharmony_ci		dev_dbg(to_device(ccdc), "lsc prefetch error\n");
14928c2ecf20Sopenharmony_ci	}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ))
14958c2ecf20Sopenharmony_ci		return;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	/* LSC_DONE interrupt occur, there are two cases
14988c2ecf20Sopenharmony_ci	 * 1. stopping for reconfiguration
14998c2ecf20Sopenharmony_ci	 * 2. stopping because of STREAM OFF command
15008c2ecf20Sopenharmony_ci	 */
15018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	if (ccdc->lsc.state == LSC_STATE_STOPPING)
15048c2ecf20Sopenharmony_ci		ccdc->lsc.state = LSC_STATE_STOPPED;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	if (ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE))
15078c2ecf20Sopenharmony_ci		goto done;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (ccdc->lsc.state != LSC_STATE_RECONFIG)
15108c2ecf20Sopenharmony_ci		goto done;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	/* LSC is in STOPPING state, change to the new state */
15138c2ecf20Sopenharmony_ci	ccdc->lsc.state = LSC_STATE_STOPPED;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	/* This is an exception. Start of frame and LSC_DONE interrupt
15168c2ecf20Sopenharmony_ci	 * have been received on the same time. Skip this event and wait
15178c2ecf20Sopenharmony_ci	 * for better times.
15188c2ecf20Sopenharmony_ci	 */
15198c2ecf20Sopenharmony_ci	if (events & IRQ0STATUS_HS_VS_IRQ)
15208c2ecf20Sopenharmony_ci		goto done;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	/* The LSC engine is stopped at this point. Enable it if there's a
15238c2ecf20Sopenharmony_ci	 * pending request.
15248c2ecf20Sopenharmony_ci	 */
15258c2ecf20Sopenharmony_ci	if (ccdc->lsc.request == NULL)
15268c2ecf20Sopenharmony_ci		goto done;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	ccdc_lsc_enable(ccdc);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_cidone:
15318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
15328c2ecf20Sopenharmony_ci}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci/*
15358c2ecf20Sopenharmony_ci * Check whether the CCDC has captured all fields necessary to complete the
15368c2ecf20Sopenharmony_ci * buffer.
15378c2ecf20Sopenharmony_ci */
15388c2ecf20Sopenharmony_cistatic bool ccdc_has_all_fields(struct isp_ccdc_device *ccdc)
15398c2ecf20Sopenharmony_ci{
15408c2ecf20Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
15418c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
15428c2ecf20Sopenharmony_ci	enum v4l2_field of_field = ccdc->formats[CCDC_PAD_SOURCE_OF].field;
15438c2ecf20Sopenharmony_ci	enum v4l2_field field;
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	/* When the input is progressive fields don't matter. */
15468c2ecf20Sopenharmony_ci	if (of_field == V4L2_FIELD_NONE)
15478c2ecf20Sopenharmony_ci		return true;
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	/* Read the current field identifier. */
15508c2ecf20Sopenharmony_ci	field = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE)
15518c2ecf20Sopenharmony_ci	      & ISPCCDC_SYN_MODE_FLDSTAT
15528c2ecf20Sopenharmony_ci	      ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	/* When capturing fields in alternate order just store the current field
15558c2ecf20Sopenharmony_ci	 * identifier in the pipeline.
15568c2ecf20Sopenharmony_ci	 */
15578c2ecf20Sopenharmony_ci	if (of_field == V4L2_FIELD_ALTERNATE) {
15588c2ecf20Sopenharmony_ci		pipe->field = field;
15598c2ecf20Sopenharmony_ci		return true;
15608c2ecf20Sopenharmony_ci	}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	/* The format is interlaced. Make sure we've captured both fields. */
15638c2ecf20Sopenharmony_ci	ccdc->fields |= field == V4L2_FIELD_BOTTOM
15648c2ecf20Sopenharmony_ci		      ? CCDC_FIELD_BOTTOM : CCDC_FIELD_TOP;
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	if (ccdc->fields != CCDC_FIELD_BOTH)
15678c2ecf20Sopenharmony_ci		return false;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	/* Verify that the field just captured corresponds to the last field
15708c2ecf20Sopenharmony_ci	 * needed based on the desired field order.
15718c2ecf20Sopenharmony_ci	 */
15728c2ecf20Sopenharmony_ci	if ((of_field == V4L2_FIELD_INTERLACED_TB && field == V4L2_FIELD_TOP) ||
15738c2ecf20Sopenharmony_ci	    (of_field == V4L2_FIELD_INTERLACED_BT && field == V4L2_FIELD_BOTTOM))
15748c2ecf20Sopenharmony_ci		return false;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	/* The buffer can be completed, reset the fields for the next buffer. */
15778c2ecf20Sopenharmony_ci	ccdc->fields = 0;
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	return true;
15808c2ecf20Sopenharmony_ci}
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_cistatic int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
15838c2ecf20Sopenharmony_ci{
15848c2ecf20Sopenharmony_ci	struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
15858c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
15868c2ecf20Sopenharmony_ci	struct isp_buffer *buffer;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	/* The CCDC generates VD0 interrupts even when disabled (the datasheet
15898c2ecf20Sopenharmony_ci	 * doesn't explicitly state if that's supposed to happen or not, so it
15908c2ecf20Sopenharmony_ci	 * can be considered as a hardware bug or as a feature, but we have to
15918c2ecf20Sopenharmony_ci	 * deal with it anyway). Disabling the CCDC when no buffer is available
15928c2ecf20Sopenharmony_ci	 * would thus not be enough, we need to handle the situation explicitly.
15938c2ecf20Sopenharmony_ci	 */
15948c2ecf20Sopenharmony_ci	if (list_empty(&ccdc->video_out.dmaqueue))
15958c2ecf20Sopenharmony_ci		return 0;
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	/* We're in continuous mode, and memory writes were disabled due to a
15988c2ecf20Sopenharmony_ci	 * buffer underrun. Re-enable them now that we have a buffer. The buffer
15998c2ecf20Sopenharmony_ci	 * address has been set in ccdc_video_queue.
16008c2ecf20Sopenharmony_ci	 */
16018c2ecf20Sopenharmony_ci	if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) {
16028c2ecf20Sopenharmony_ci		ccdc->underrun = 0;
16038c2ecf20Sopenharmony_ci		return 1;
16048c2ecf20Sopenharmony_ci	}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	/* Wait for the CCDC to become idle. */
16078c2ecf20Sopenharmony_ci	if (ccdc_sbl_wait_idle(ccdc, 1000)) {
16088c2ecf20Sopenharmony_ci		dev_info(isp->dev, "CCDC won't become idle!\n");
16098c2ecf20Sopenharmony_ci		media_entity_enum_set(&isp->crashed, &ccdc->subdev.entity);
16108c2ecf20Sopenharmony_ci		omap3isp_pipeline_cancel_stream(pipe);
16118c2ecf20Sopenharmony_ci		return 0;
16128c2ecf20Sopenharmony_ci	}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	/* Don't restart CCDC if we're just about to stop streaming. */
16158c2ecf20Sopenharmony_ci	if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
16168c2ecf20Sopenharmony_ci	    ccdc->stopping & CCDC_STOP_REQUEST)
16178c2ecf20Sopenharmony_ci		return 0;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	if (!ccdc_has_all_fields(ccdc))
16208c2ecf20Sopenharmony_ci		return 1;
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci	buffer = omap3isp_video_buffer_next(&ccdc->video_out);
16238c2ecf20Sopenharmony_ci	if (buffer != NULL)
16248c2ecf20Sopenharmony_ci		ccdc_set_outaddr(ccdc, buffer->dma);
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT &&
16298c2ecf20Sopenharmony_ci	    isp_pipeline_ready(pipe))
16308c2ecf20Sopenharmony_ci		omap3isp_pipeline_set_stream(pipe,
16318c2ecf20Sopenharmony_ci					ISP_PIPELINE_STREAM_SINGLESHOT);
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	return buffer != NULL;
16348c2ecf20Sopenharmony_ci}
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci/*
16378c2ecf20Sopenharmony_ci * ccdc_vd0_isr - Handle VD0 event
16388c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
16398c2ecf20Sopenharmony_ci *
16408c2ecf20Sopenharmony_ci * Executes LSC deferred enablement before next frame starts.
16418c2ecf20Sopenharmony_ci */
16428c2ecf20Sopenharmony_cistatic void ccdc_vd0_isr(struct isp_ccdc_device *ccdc)
16438c2ecf20Sopenharmony_ci{
16448c2ecf20Sopenharmony_ci	unsigned long flags;
16458c2ecf20Sopenharmony_ci	int restart = 0;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	/* In BT.656 mode the CCDC doesn't generate an HS/VS interrupt. We thus
16488c2ecf20Sopenharmony_ci	 * need to increment the frame counter here.
16498c2ecf20Sopenharmony_ci	 */
16508c2ecf20Sopenharmony_ci	if (ccdc->bt656) {
16518c2ecf20Sopenharmony_ci		struct isp_pipeline *pipe =
16528c2ecf20Sopenharmony_ci			to_isp_pipeline(&ccdc->subdev.entity);
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci		atomic_inc(&pipe->frame_number);
16558c2ecf20Sopenharmony_ci	}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	/* Emulate a VD1 interrupt for BT.656 mode, as we can't stop the CCDC in
16588c2ecf20Sopenharmony_ci	 * the VD1 interrupt handler in that mode without risking a CCDC stall
16598c2ecf20Sopenharmony_ci	 * if a short frame is received.
16608c2ecf20Sopenharmony_ci	 */
16618c2ecf20Sopenharmony_ci	if (ccdc->bt656) {
16628c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ccdc->lock, flags);
16638c2ecf20Sopenharmony_ci		if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
16648c2ecf20Sopenharmony_ci		    ccdc->output & CCDC_OUTPUT_MEMORY) {
16658c2ecf20Sopenharmony_ci			if (ccdc->lsc.state != LSC_STATE_STOPPED)
16668c2ecf20Sopenharmony_ci				__ccdc_lsc_enable(ccdc, 0);
16678c2ecf20Sopenharmony_ci			__ccdc_enable(ccdc, 0);
16688c2ecf20Sopenharmony_ci		}
16698c2ecf20Sopenharmony_ci		ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1);
16708c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ccdc->lock, flags);
16718c2ecf20Sopenharmony_ci	}
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lock, flags);
16748c2ecf20Sopenharmony_ci	if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
16758c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ccdc->lock, flags);
16768c2ecf20Sopenharmony_ci		return;
16778c2ecf20Sopenharmony_ci	}
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	if (ccdc->output & CCDC_OUTPUT_MEMORY)
16808c2ecf20Sopenharmony_ci		restart = ccdc_isr_buffer(ccdc);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	if (!ccdc->shadow_update)
16838c2ecf20Sopenharmony_ci		ccdc_apply_controls(ccdc);
16848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lock, flags);
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	if (restart)
16878c2ecf20Sopenharmony_ci		ccdc_enable(ccdc);
16888c2ecf20Sopenharmony_ci}
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci/*
16918c2ecf20Sopenharmony_ci * ccdc_vd1_isr - Handle VD1 event
16928c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
16938c2ecf20Sopenharmony_ci */
16948c2ecf20Sopenharmony_cistatic void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
16958c2ecf20Sopenharmony_ci{
16968c2ecf20Sopenharmony_ci	unsigned long flags;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	/* In BT.656 mode the synchronization signals are generated by the CCDC
16998c2ecf20Sopenharmony_ci	 * from the embedded sync codes. The VD0 and VD1 interrupts are thus
17008c2ecf20Sopenharmony_ci	 * only triggered when the CCDC is enabled, unlike external sync mode
17018c2ecf20Sopenharmony_ci	 * where the line counter runs even when the CCDC is stopped. We can't
17028c2ecf20Sopenharmony_ci	 * disable the CCDC at VD1 time, as no VD0 interrupt would be generated
17038c2ecf20Sopenharmony_ci	 * for a short frame, which would result in the CCDC being stopped and
17048c2ecf20Sopenharmony_ci	 * no VD interrupt generated anymore. The CCDC is stopped from the VD0
17058c2ecf20Sopenharmony_ci	 * interrupt handler instead for BT.656.
17068c2ecf20Sopenharmony_ci	 */
17078c2ecf20Sopenharmony_ci	if (ccdc->bt656)
17088c2ecf20Sopenharmony_ci		return;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci	/*
17138c2ecf20Sopenharmony_ci	 * Depending on the CCDC pipeline state, CCDC stopping should be
17148c2ecf20Sopenharmony_ci	 * handled differently. In SINGLESHOT we emulate an internal CCDC
17158c2ecf20Sopenharmony_ci	 * stopping because the CCDC hw works only in continuous mode.
17168c2ecf20Sopenharmony_ci	 * When CONTINUOUS pipeline state is used and the CCDC writes it's
17178c2ecf20Sopenharmony_ci	 * data to memory the CCDC and LSC are stopped immediately but
17188c2ecf20Sopenharmony_ci	 * without change the CCDC stopping state machine. The CCDC
17198c2ecf20Sopenharmony_ci	 * stopping state machine should be used only when user request
17208c2ecf20Sopenharmony_ci	 * for stopping is received (SINGLESHOT is an exception).
17218c2ecf20Sopenharmony_ci	 */
17228c2ecf20Sopenharmony_ci	switch (ccdc->state) {
17238c2ecf20Sopenharmony_ci	case ISP_PIPELINE_STREAM_SINGLESHOT:
17248c2ecf20Sopenharmony_ci		ccdc->stopping = CCDC_STOP_REQUEST;
17258c2ecf20Sopenharmony_ci		break;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	case ISP_PIPELINE_STREAM_CONTINUOUS:
17288c2ecf20Sopenharmony_ci		if (ccdc->output & CCDC_OUTPUT_MEMORY) {
17298c2ecf20Sopenharmony_ci			if (ccdc->lsc.state != LSC_STATE_STOPPED)
17308c2ecf20Sopenharmony_ci				__ccdc_lsc_enable(ccdc, 0);
17318c2ecf20Sopenharmony_ci			__ccdc_enable(ccdc, 0);
17328c2ecf20Sopenharmony_ci		}
17338c2ecf20Sopenharmony_ci		break;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	case ISP_PIPELINE_STREAM_STOPPED:
17368c2ecf20Sopenharmony_ci		break;
17378c2ecf20Sopenharmony_ci	}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
17408c2ecf20Sopenharmony_ci		goto done;
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	if (ccdc->lsc.request == NULL)
17438c2ecf20Sopenharmony_ci		goto done;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	/*
17468c2ecf20Sopenharmony_ci	 * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ
17478c2ecf20Sopenharmony_ci	 * do the appropriate changes in registers
17488c2ecf20Sopenharmony_ci	 */
17498c2ecf20Sopenharmony_ci	if (ccdc->lsc.state == LSC_STATE_RUNNING) {
17508c2ecf20Sopenharmony_ci		__ccdc_lsc_enable(ccdc, 0);
17518c2ecf20Sopenharmony_ci		ccdc->lsc.state = LSC_STATE_RECONFIG;
17528c2ecf20Sopenharmony_ci		goto done;
17538c2ecf20Sopenharmony_ci	}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	/* LSC has been in STOPPED state, enable it */
17568c2ecf20Sopenharmony_ci	if (ccdc->lsc.state == LSC_STATE_STOPPED)
17578c2ecf20Sopenharmony_ci		ccdc_lsc_enable(ccdc);
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_cidone:
17608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
17618c2ecf20Sopenharmony_ci}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci/*
17648c2ecf20Sopenharmony_ci * omap3isp_ccdc_isr - Configure CCDC during interframe time.
17658c2ecf20Sopenharmony_ci * @ccdc: Pointer to ISP CCDC device.
17668c2ecf20Sopenharmony_ci * @events: CCDC events
17678c2ecf20Sopenharmony_ci */
17688c2ecf20Sopenharmony_ciint omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events)
17698c2ecf20Sopenharmony_ci{
17708c2ecf20Sopenharmony_ci	if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED)
17718c2ecf20Sopenharmony_ci		return 0;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	if (events & IRQ0STATUS_CCDC_VD1_IRQ)
17748c2ecf20Sopenharmony_ci		ccdc_vd1_isr(ccdc);
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	ccdc_lsc_isr(ccdc, events);
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	if (events & IRQ0STATUS_CCDC_VD0_IRQ)
17798c2ecf20Sopenharmony_ci		ccdc_vd0_isr(ccdc);
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	if (events & IRQ0STATUS_HS_VS_IRQ)
17828c2ecf20Sopenharmony_ci		ccdc_hs_vs_isr(ccdc);
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	return 0;
17858c2ecf20Sopenharmony_ci}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
17888c2ecf20Sopenharmony_ci * ISP video operations
17898c2ecf20Sopenharmony_ci */
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_cistatic int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
17928c2ecf20Sopenharmony_ci{
17938c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc;
17948c2ecf20Sopenharmony_ci	unsigned long flags;
17958c2ecf20Sopenharmony_ci	bool restart = false;
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
17988c2ecf20Sopenharmony_ci		return -ENODEV;
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	ccdc_set_outaddr(ccdc, buffer->dma);
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	/* We now have a buffer queued on the output, restart the pipeline
18038c2ecf20Sopenharmony_ci	 * on the next CCDC interrupt if running in continuous mode (or when
18048c2ecf20Sopenharmony_ci	 * starting the stream) in external sync mode, or immediately in BT.656
18058c2ecf20Sopenharmony_ci	 * sync mode as no CCDC interrupt is generated when the CCDC is stopped
18068c2ecf20Sopenharmony_ci	 * in that case.
18078c2ecf20Sopenharmony_ci	 */
18088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ccdc->lock, flags);
18098c2ecf20Sopenharmony_ci	if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && !ccdc->running &&
18108c2ecf20Sopenharmony_ci	    ccdc->bt656)
18118c2ecf20Sopenharmony_ci		restart = true;
18128c2ecf20Sopenharmony_ci	else
18138c2ecf20Sopenharmony_ci		ccdc->underrun = 1;
18148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ccdc->lock, flags);
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	if (restart)
18178c2ecf20Sopenharmony_ci		ccdc_enable(ccdc);
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	return 0;
18208c2ecf20Sopenharmony_ci}
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_cistatic const struct isp_video_operations ccdc_video_ops = {
18238c2ecf20Sopenharmony_ci	.queue = ccdc_video_queue,
18248c2ecf20Sopenharmony_ci};
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
18278c2ecf20Sopenharmony_ci * V4L2 subdev operations
18288c2ecf20Sopenharmony_ci */
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci/*
18318c2ecf20Sopenharmony_ci * ccdc_ioctl - CCDC module private ioctl's
18328c2ecf20Sopenharmony_ci * @sd: ISP CCDC V4L2 subdevice
18338c2ecf20Sopenharmony_ci * @cmd: ioctl command
18348c2ecf20Sopenharmony_ci * @arg: ioctl argument
18358c2ecf20Sopenharmony_ci *
18368c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise.
18378c2ecf20Sopenharmony_ci */
18388c2ecf20Sopenharmony_cistatic long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
18398c2ecf20Sopenharmony_ci{
18408c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
18418c2ecf20Sopenharmony_ci	int ret;
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	switch (cmd) {
18448c2ecf20Sopenharmony_ci	case VIDIOC_OMAP3ISP_CCDC_CFG:
18458c2ecf20Sopenharmony_ci		mutex_lock(&ccdc->ioctl_lock);
18468c2ecf20Sopenharmony_ci		ret = ccdc_config(ccdc, arg);
18478c2ecf20Sopenharmony_ci		mutex_unlock(&ccdc->ioctl_lock);
18488c2ecf20Sopenharmony_ci		break;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	default:
18518c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
18528c2ecf20Sopenharmony_ci	}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	return ret;
18558c2ecf20Sopenharmony_ci}
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_cistatic int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
18588c2ecf20Sopenharmony_ci				struct v4l2_event_subscription *sub)
18598c2ecf20Sopenharmony_ci{
18608c2ecf20Sopenharmony_ci	if (sub->type != V4L2_EVENT_FRAME_SYNC)
18618c2ecf20Sopenharmony_ci		return -EINVAL;
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	/* line number is zero at frame start */
18648c2ecf20Sopenharmony_ci	if (sub->id != 0)
18658c2ecf20Sopenharmony_ci		return -EINVAL;
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS, NULL);
18688c2ecf20Sopenharmony_ci}
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_cistatic int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
18718c2ecf20Sopenharmony_ci				  struct v4l2_event_subscription *sub)
18728c2ecf20Sopenharmony_ci{
18738c2ecf20Sopenharmony_ci	return v4l2_event_unsubscribe(fh, sub);
18748c2ecf20Sopenharmony_ci}
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci/*
18778c2ecf20Sopenharmony_ci * ccdc_set_stream - Enable/Disable streaming on the CCDC module
18788c2ecf20Sopenharmony_ci * @sd: ISP CCDC V4L2 subdevice
18798c2ecf20Sopenharmony_ci * @enable: Enable/disable stream
18808c2ecf20Sopenharmony_ci *
18818c2ecf20Sopenharmony_ci * When writing to memory, the CCDC hardware can't be enabled without a memory
18828c2ecf20Sopenharmony_ci * buffer to write to. As the s_stream operation is called in response to a
18838c2ecf20Sopenharmony_ci * STREAMON call without any buffer queued yet, just update the enabled field
18848c2ecf20Sopenharmony_ci * and return immediately. The CCDC will be enabled in ccdc_isr_buffer().
18858c2ecf20Sopenharmony_ci *
18868c2ecf20Sopenharmony_ci * When not writing to memory enable the CCDC immediately.
18878c2ecf20Sopenharmony_ci */
18888c2ecf20Sopenharmony_cistatic int ccdc_set_stream(struct v4l2_subdev *sd, int enable)
18898c2ecf20Sopenharmony_ci{
18908c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
18918c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
18928c2ecf20Sopenharmony_ci	int ret = 0;
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) {
18958c2ecf20Sopenharmony_ci		if (enable == ISP_PIPELINE_STREAM_STOPPED)
18968c2ecf20Sopenharmony_ci			return 0;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci		omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_CCDC);
18998c2ecf20Sopenharmony_ci		isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
19008c2ecf20Sopenharmony_ci			    ISPCCDC_CFG_VDLC);
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci		ccdc_configure(ccdc);
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci		ccdc_print_status(ccdc);
19058c2ecf20Sopenharmony_ci	}
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	switch (enable) {
19088c2ecf20Sopenharmony_ci	case ISP_PIPELINE_STREAM_CONTINUOUS:
19098c2ecf20Sopenharmony_ci		if (ccdc->output & CCDC_OUTPUT_MEMORY)
19108c2ecf20Sopenharmony_ci			omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci		if (ccdc->underrun || !(ccdc->output & CCDC_OUTPUT_MEMORY))
19138c2ecf20Sopenharmony_ci			ccdc_enable(ccdc);
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci		ccdc->underrun = 0;
19168c2ecf20Sopenharmony_ci		break;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	case ISP_PIPELINE_STREAM_SINGLESHOT:
19198c2ecf20Sopenharmony_ci		if (ccdc->output & CCDC_OUTPUT_MEMORY &&
19208c2ecf20Sopenharmony_ci		    ccdc->state != ISP_PIPELINE_STREAM_SINGLESHOT)
19218c2ecf20Sopenharmony_ci			omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci		ccdc_enable(ccdc);
19248c2ecf20Sopenharmony_ci		break;
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	case ISP_PIPELINE_STREAM_STOPPED:
19278c2ecf20Sopenharmony_ci		ret = ccdc_disable(ccdc);
19288c2ecf20Sopenharmony_ci		if (ccdc->output & CCDC_OUTPUT_MEMORY)
19298c2ecf20Sopenharmony_ci			omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
19308c2ecf20Sopenharmony_ci		omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC);
19318c2ecf20Sopenharmony_ci		ccdc->underrun = 0;
19328c2ecf20Sopenharmony_ci		break;
19338c2ecf20Sopenharmony_ci	}
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	ccdc->state = enable;
19368c2ecf20Sopenharmony_ci	return ret;
19378c2ecf20Sopenharmony_ci}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt *
19408c2ecf20Sopenharmony_ci__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg,
19418c2ecf20Sopenharmony_ci		  unsigned int pad, enum v4l2_subdev_format_whence which)
19428c2ecf20Sopenharmony_ci{
19438c2ecf20Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
19448c2ecf20Sopenharmony_ci		return v4l2_subdev_get_try_format(&ccdc->subdev, cfg, pad);
19458c2ecf20Sopenharmony_ci	else
19468c2ecf20Sopenharmony_ci		return &ccdc->formats[pad];
19478c2ecf20Sopenharmony_ci}
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_cistatic struct v4l2_rect *
19508c2ecf20Sopenharmony_ci__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg,
19518c2ecf20Sopenharmony_ci		enum v4l2_subdev_format_whence which)
19528c2ecf20Sopenharmony_ci{
19538c2ecf20Sopenharmony_ci	if (which == V4L2_SUBDEV_FORMAT_TRY)
19548c2ecf20Sopenharmony_ci		return v4l2_subdev_get_try_crop(&ccdc->subdev, cfg, CCDC_PAD_SOURCE_OF);
19558c2ecf20Sopenharmony_ci	else
19568c2ecf20Sopenharmony_ci		return &ccdc->crop;
19578c2ecf20Sopenharmony_ci}
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci/*
19608c2ecf20Sopenharmony_ci * ccdc_try_format - Try video format on a pad
19618c2ecf20Sopenharmony_ci * @ccdc: ISP CCDC device
19628c2ecf20Sopenharmony_ci * @cfg : V4L2 subdev pad configuration
19638c2ecf20Sopenharmony_ci * @pad: Pad number
19648c2ecf20Sopenharmony_ci * @fmt: Format
19658c2ecf20Sopenharmony_ci */
19668c2ecf20Sopenharmony_cistatic void
19678c2ecf20Sopenharmony_ciccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg,
19688c2ecf20Sopenharmony_ci		unsigned int pad, struct v4l2_mbus_framefmt *fmt,
19698c2ecf20Sopenharmony_ci		enum v4l2_subdev_format_whence which)
19708c2ecf20Sopenharmony_ci{
19718c2ecf20Sopenharmony_ci	const struct isp_format_info *info;
19728c2ecf20Sopenharmony_ci	u32 pixelcode;
19738c2ecf20Sopenharmony_ci	unsigned int width = fmt->width;
19748c2ecf20Sopenharmony_ci	unsigned int height = fmt->height;
19758c2ecf20Sopenharmony_ci	struct v4l2_rect *crop;
19768c2ecf20Sopenharmony_ci	enum v4l2_field field;
19778c2ecf20Sopenharmony_ci	unsigned int i;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	switch (pad) {
19808c2ecf20Sopenharmony_ci	case CCDC_PAD_SINK:
19818c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) {
19828c2ecf20Sopenharmony_ci			if (fmt->code == ccdc_fmts[i])
19838c2ecf20Sopenharmony_ci				break;
19848c2ecf20Sopenharmony_ci		}
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci		/* If not found, use SGRBG10 as default */
19878c2ecf20Sopenharmony_ci		if (i >= ARRAY_SIZE(ccdc_fmts))
19888c2ecf20Sopenharmony_ci			fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci		/* Clamp the input size. */
19918c2ecf20Sopenharmony_ci		fmt->width = clamp_t(u32, width, 32, 4096);
19928c2ecf20Sopenharmony_ci		fmt->height = clamp_t(u32, height, 32, 4096);
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci		/* Default to progressive field order. */
19958c2ecf20Sopenharmony_ci		if (fmt->field == V4L2_FIELD_ANY)
19968c2ecf20Sopenharmony_ci			fmt->field = V4L2_FIELD_NONE;
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci		break;
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci	case CCDC_PAD_SOURCE_OF:
20018c2ecf20Sopenharmony_ci		pixelcode = fmt->code;
20028c2ecf20Sopenharmony_ci		field = fmt->field;
20038c2ecf20Sopenharmony_ci		*fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which);
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci		/* In SYNC mode the bridge converts YUV formats from 2X8 to
20068c2ecf20Sopenharmony_ci		 * 1X16. In BT.656 no such conversion occurs. As we don't know
20078c2ecf20Sopenharmony_ci		 * at this point whether the source will use SYNC or BT.656 mode
20088c2ecf20Sopenharmony_ci		 * let's pretend the conversion always occurs. The CCDC will be
20098c2ecf20Sopenharmony_ci		 * configured to pack bytes in BT.656, hiding the inaccuracy.
20108c2ecf20Sopenharmony_ci		 * In all cases bytes can be swapped.
20118c2ecf20Sopenharmony_ci		 */
20128c2ecf20Sopenharmony_ci		if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
20138c2ecf20Sopenharmony_ci		    fmt->code == MEDIA_BUS_FMT_UYVY8_2X8) {
20148c2ecf20Sopenharmony_ci			/* Use the user requested format if YUV. */
20158c2ecf20Sopenharmony_ci			if (pixelcode == MEDIA_BUS_FMT_YUYV8_2X8 ||
20168c2ecf20Sopenharmony_ci			    pixelcode == MEDIA_BUS_FMT_UYVY8_2X8 ||
20178c2ecf20Sopenharmony_ci			    pixelcode == MEDIA_BUS_FMT_YUYV8_1X16 ||
20188c2ecf20Sopenharmony_ci			    pixelcode == MEDIA_BUS_FMT_UYVY8_1X16)
20198c2ecf20Sopenharmony_ci				fmt->code = pixelcode;
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci			if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8)
20228c2ecf20Sopenharmony_ci				fmt->code = MEDIA_BUS_FMT_YUYV8_1X16;
20238c2ecf20Sopenharmony_ci			else if (fmt->code == MEDIA_BUS_FMT_UYVY8_2X8)
20248c2ecf20Sopenharmony_ci				fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
20258c2ecf20Sopenharmony_ci		}
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci		/* Hardcode the output size to the crop rectangle size. */
20288c2ecf20Sopenharmony_ci		crop = __ccdc_get_crop(ccdc, cfg, which);
20298c2ecf20Sopenharmony_ci		fmt->width = crop->width;
20308c2ecf20Sopenharmony_ci		fmt->height = crop->height;
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci		/* When input format is interlaced with alternating fields the
20338c2ecf20Sopenharmony_ci		 * CCDC can interleave the fields.
20348c2ecf20Sopenharmony_ci		 */
20358c2ecf20Sopenharmony_ci		if (fmt->field == V4L2_FIELD_ALTERNATE &&
20368c2ecf20Sopenharmony_ci		    (field == V4L2_FIELD_INTERLACED_TB ||
20378c2ecf20Sopenharmony_ci		     field == V4L2_FIELD_INTERLACED_BT)) {
20388c2ecf20Sopenharmony_ci			fmt->field = field;
20398c2ecf20Sopenharmony_ci			fmt->height *= 2;
20408c2ecf20Sopenharmony_ci		}
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci		break;
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	case CCDC_PAD_SOURCE_VP:
20458c2ecf20Sopenharmony_ci		*fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which);
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci		/* The video port interface truncates the data to 10 bits. */
20488c2ecf20Sopenharmony_ci		info = omap3isp_video_format_info(fmt->code);
20498c2ecf20Sopenharmony_ci		fmt->code = info->truncated;
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ci		/* YUV formats are not supported by the video port. */
20528c2ecf20Sopenharmony_ci		if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
20538c2ecf20Sopenharmony_ci		    fmt->code == MEDIA_BUS_FMT_UYVY8_2X8)
20548c2ecf20Sopenharmony_ci			fmt->code = 0;
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci		/* The number of lines that can be clocked out from the video
20578c2ecf20Sopenharmony_ci		 * port output must be at least one line less than the number
20588c2ecf20Sopenharmony_ci		 * of input lines.
20598c2ecf20Sopenharmony_ci		 */
20608c2ecf20Sopenharmony_ci		fmt->width = clamp_t(u32, width, 32, fmt->width);
20618c2ecf20Sopenharmony_ci		fmt->height = clamp_t(u32, height, 32, fmt->height - 1);
20628c2ecf20Sopenharmony_ci		break;
20638c2ecf20Sopenharmony_ci	}
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	/* Data is written to memory unpacked, each 10-bit or 12-bit pixel is
20668c2ecf20Sopenharmony_ci	 * stored on 2 bytes.
20678c2ecf20Sopenharmony_ci	 */
20688c2ecf20Sopenharmony_ci	fmt->colorspace = V4L2_COLORSPACE_SRGB;
20698c2ecf20Sopenharmony_ci}
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci/*
20728c2ecf20Sopenharmony_ci * ccdc_try_crop - Validate a crop rectangle
20738c2ecf20Sopenharmony_ci * @ccdc: ISP CCDC device
20748c2ecf20Sopenharmony_ci * @sink: format on the sink pad
20758c2ecf20Sopenharmony_ci * @crop: crop rectangle to be validated
20768c2ecf20Sopenharmony_ci */
20778c2ecf20Sopenharmony_cistatic void ccdc_try_crop(struct isp_ccdc_device *ccdc,
20788c2ecf20Sopenharmony_ci			  const struct v4l2_mbus_framefmt *sink,
20798c2ecf20Sopenharmony_ci			  struct v4l2_rect *crop)
20808c2ecf20Sopenharmony_ci{
20818c2ecf20Sopenharmony_ci	const struct isp_format_info *info;
20828c2ecf20Sopenharmony_ci	unsigned int max_width;
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci	/* For Bayer formats, restrict left/top and width/height to even values
20858c2ecf20Sopenharmony_ci	 * to keep the Bayer pattern.
20868c2ecf20Sopenharmony_ci	 */
20878c2ecf20Sopenharmony_ci	info = omap3isp_video_format_info(sink->code);
20888c2ecf20Sopenharmony_ci	if (info->flavor != MEDIA_BUS_FMT_Y8_1X8) {
20898c2ecf20Sopenharmony_ci		crop->left &= ~1;
20908c2ecf20Sopenharmony_ci		crop->top &= ~1;
20918c2ecf20Sopenharmony_ci	}
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci	crop->left = clamp_t(u32, crop->left, 0, sink->width - CCDC_MIN_WIDTH);
20948c2ecf20Sopenharmony_ci	crop->top = clamp_t(u32, crop->top, 0, sink->height - CCDC_MIN_HEIGHT);
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	/* The data formatter truncates the number of horizontal output pixels
20978c2ecf20Sopenharmony_ci	 * to a multiple of 16. To avoid clipping data, allow callers to request
20988c2ecf20Sopenharmony_ci	 * an output size bigger than the input size up to the nearest multiple
20998c2ecf20Sopenharmony_ci	 * of 16.
21008c2ecf20Sopenharmony_ci	 */
21018c2ecf20Sopenharmony_ci	max_width = (sink->width - crop->left + 15) & ~15;
21028c2ecf20Sopenharmony_ci	crop->width = clamp_t(u32, crop->width, CCDC_MIN_WIDTH, max_width)
21038c2ecf20Sopenharmony_ci		    & ~15;
21048c2ecf20Sopenharmony_ci	crop->height = clamp_t(u32, crop->height, CCDC_MIN_HEIGHT,
21058c2ecf20Sopenharmony_ci			       sink->height - crop->top);
21068c2ecf20Sopenharmony_ci
21078c2ecf20Sopenharmony_ci	/* Odd width/height values don't make sense for Bayer formats. */
21088c2ecf20Sopenharmony_ci	if (info->flavor != MEDIA_BUS_FMT_Y8_1X8) {
21098c2ecf20Sopenharmony_ci		crop->width &= ~1;
21108c2ecf20Sopenharmony_ci		crop->height &= ~1;
21118c2ecf20Sopenharmony_ci	}
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci/*
21158c2ecf20Sopenharmony_ci * ccdc_enum_mbus_code - Handle pixel format enumeration
21168c2ecf20Sopenharmony_ci * @sd     : pointer to v4l2 subdev structure
21178c2ecf20Sopenharmony_ci * @cfg : V4L2 subdev pad configuration
21188c2ecf20Sopenharmony_ci * @code   : pointer to v4l2_subdev_mbus_code_enum structure
21198c2ecf20Sopenharmony_ci * return -EINVAL or zero on success
21208c2ecf20Sopenharmony_ci */
21218c2ecf20Sopenharmony_cistatic int ccdc_enum_mbus_code(struct v4l2_subdev *sd,
21228c2ecf20Sopenharmony_ci			       struct v4l2_subdev_pad_config *cfg,
21238c2ecf20Sopenharmony_ci			       struct v4l2_subdev_mbus_code_enum *code)
21248c2ecf20Sopenharmony_ci{
21258c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
21268c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci	switch (code->pad) {
21298c2ecf20Sopenharmony_ci	case CCDC_PAD_SINK:
21308c2ecf20Sopenharmony_ci		if (code->index >= ARRAY_SIZE(ccdc_fmts))
21318c2ecf20Sopenharmony_ci			return -EINVAL;
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci		code->code = ccdc_fmts[code->index];
21348c2ecf20Sopenharmony_ci		break;
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci	case CCDC_PAD_SOURCE_OF:
21378c2ecf20Sopenharmony_ci		format = __ccdc_get_format(ccdc, cfg, code->pad,
21388c2ecf20Sopenharmony_ci					   code->which);
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci		if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
21418c2ecf20Sopenharmony_ci		    format->code == MEDIA_BUS_FMT_UYVY8_2X8) {
21428c2ecf20Sopenharmony_ci			/* In YUV mode the CCDC can swap bytes. */
21438c2ecf20Sopenharmony_ci			if (code->index == 0)
21448c2ecf20Sopenharmony_ci				code->code = MEDIA_BUS_FMT_YUYV8_1X16;
21458c2ecf20Sopenharmony_ci			else if (code->index == 1)
21468c2ecf20Sopenharmony_ci				code->code = MEDIA_BUS_FMT_UYVY8_1X16;
21478c2ecf20Sopenharmony_ci			else
21488c2ecf20Sopenharmony_ci				return -EINVAL;
21498c2ecf20Sopenharmony_ci		} else {
21508c2ecf20Sopenharmony_ci			/* In raw mode, no configurable format confversion is
21518c2ecf20Sopenharmony_ci			 * available.
21528c2ecf20Sopenharmony_ci			 */
21538c2ecf20Sopenharmony_ci			if (code->index == 0)
21548c2ecf20Sopenharmony_ci				code->code = format->code;
21558c2ecf20Sopenharmony_ci			else
21568c2ecf20Sopenharmony_ci				return -EINVAL;
21578c2ecf20Sopenharmony_ci		}
21588c2ecf20Sopenharmony_ci		break;
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	case CCDC_PAD_SOURCE_VP:
21618c2ecf20Sopenharmony_ci		/* The CCDC supports no configurable format conversion
21628c2ecf20Sopenharmony_ci		 * compatible with the video port. Enumerate a single output
21638c2ecf20Sopenharmony_ci		 * format code.
21648c2ecf20Sopenharmony_ci		 */
21658c2ecf20Sopenharmony_ci		if (code->index != 0)
21668c2ecf20Sopenharmony_ci			return -EINVAL;
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_ci		format = __ccdc_get_format(ccdc, cfg, code->pad,
21698c2ecf20Sopenharmony_ci					   code->which);
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci		/* A pixel code equal to 0 means that the video port doesn't
21728c2ecf20Sopenharmony_ci		 * support the input format. Don't enumerate any pixel code.
21738c2ecf20Sopenharmony_ci		 */
21748c2ecf20Sopenharmony_ci		if (format->code == 0)
21758c2ecf20Sopenharmony_ci			return -EINVAL;
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci		code->code = format->code;
21788c2ecf20Sopenharmony_ci		break;
21798c2ecf20Sopenharmony_ci
21808c2ecf20Sopenharmony_ci	default:
21818c2ecf20Sopenharmony_ci		return -EINVAL;
21828c2ecf20Sopenharmony_ci	}
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	return 0;
21858c2ecf20Sopenharmony_ci}
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_cistatic int ccdc_enum_frame_size(struct v4l2_subdev *sd,
21888c2ecf20Sopenharmony_ci				struct v4l2_subdev_pad_config *cfg,
21898c2ecf20Sopenharmony_ci				struct v4l2_subdev_frame_size_enum *fse)
21908c2ecf20Sopenharmony_ci{
21918c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
21928c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt format;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	if (fse->index != 0)
21958c2ecf20Sopenharmony_ci		return -EINVAL;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	format.code = fse->code;
21988c2ecf20Sopenharmony_ci	format.width = 1;
21998c2ecf20Sopenharmony_ci	format.height = 1;
22008c2ecf20Sopenharmony_ci	ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which);
22018c2ecf20Sopenharmony_ci	fse->min_width = format.width;
22028c2ecf20Sopenharmony_ci	fse->min_height = format.height;
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	if (format.code != fse->code)
22058c2ecf20Sopenharmony_ci		return -EINVAL;
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci	format.code = fse->code;
22088c2ecf20Sopenharmony_ci	format.width = -1;
22098c2ecf20Sopenharmony_ci	format.height = -1;
22108c2ecf20Sopenharmony_ci	ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which);
22118c2ecf20Sopenharmony_ci	fse->max_width = format.width;
22128c2ecf20Sopenharmony_ci	fse->max_height = format.height;
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci	return 0;
22158c2ecf20Sopenharmony_ci}
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci/*
22188c2ecf20Sopenharmony_ci * ccdc_get_selection - Retrieve a selection rectangle on a pad
22198c2ecf20Sopenharmony_ci * @sd: ISP CCDC V4L2 subdevice
22208c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
22218c2ecf20Sopenharmony_ci * @sel: Selection rectangle
22228c2ecf20Sopenharmony_ci *
22238c2ecf20Sopenharmony_ci * The only supported rectangles are the crop rectangles on the output formatter
22248c2ecf20Sopenharmony_ci * source pad.
22258c2ecf20Sopenharmony_ci *
22268c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise.
22278c2ecf20Sopenharmony_ci */
22288c2ecf20Sopenharmony_cistatic int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
22298c2ecf20Sopenharmony_ci			      struct v4l2_subdev_selection *sel)
22308c2ecf20Sopenharmony_ci{
22318c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
22328c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci	if (sel->pad != CCDC_PAD_SOURCE_OF)
22358c2ecf20Sopenharmony_ci		return -EINVAL;
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	switch (sel->target) {
22388c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
22398c2ecf20Sopenharmony_ci		sel->r.left = 0;
22408c2ecf20Sopenharmony_ci		sel->r.top = 0;
22418c2ecf20Sopenharmony_ci		sel->r.width = INT_MAX;
22428c2ecf20Sopenharmony_ci		sel->r.height = INT_MAX;
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci		format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which);
22458c2ecf20Sopenharmony_ci		ccdc_try_crop(ccdc, format, &sel->r);
22468c2ecf20Sopenharmony_ci		break;
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP:
22498c2ecf20Sopenharmony_ci		sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which);
22508c2ecf20Sopenharmony_ci		break;
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	default:
22538c2ecf20Sopenharmony_ci		return -EINVAL;
22548c2ecf20Sopenharmony_ci	}
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_ci	return 0;
22578c2ecf20Sopenharmony_ci}
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci/*
22608c2ecf20Sopenharmony_ci * ccdc_set_selection - Set a selection rectangle on a pad
22618c2ecf20Sopenharmony_ci * @sd: ISP CCDC V4L2 subdevice
22628c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
22638c2ecf20Sopenharmony_ci * @sel: Selection rectangle
22648c2ecf20Sopenharmony_ci *
22658c2ecf20Sopenharmony_ci * The only supported rectangle is the actual crop rectangle on the output
22668c2ecf20Sopenharmony_ci * formatter source pad.
22678c2ecf20Sopenharmony_ci *
22688c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise.
22698c2ecf20Sopenharmony_ci */
22708c2ecf20Sopenharmony_cistatic int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
22718c2ecf20Sopenharmony_ci			      struct v4l2_subdev_selection *sel)
22728c2ecf20Sopenharmony_ci{
22738c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
22748c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	if (sel->target != V4L2_SEL_TGT_CROP ||
22778c2ecf20Sopenharmony_ci	    sel->pad != CCDC_PAD_SOURCE_OF)
22788c2ecf20Sopenharmony_ci		return -EINVAL;
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	/* The crop rectangle can't be changed while streaming. */
22818c2ecf20Sopenharmony_ci	if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
22828c2ecf20Sopenharmony_ci		return -EBUSY;
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_ci	/* Modifying the crop rectangle always changes the format on the source
22858c2ecf20Sopenharmony_ci	 * pad. If the KEEP_CONFIG flag is set, just return the current crop
22868c2ecf20Sopenharmony_ci	 * rectangle.
22878c2ecf20Sopenharmony_ci	 */
22888c2ecf20Sopenharmony_ci	if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
22898c2ecf20Sopenharmony_ci		sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which);
22908c2ecf20Sopenharmony_ci		return 0;
22918c2ecf20Sopenharmony_ci	}
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_ci	format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which);
22948c2ecf20Sopenharmony_ci	ccdc_try_crop(ccdc, format, &sel->r);
22958c2ecf20Sopenharmony_ci	*__ccdc_get_crop(ccdc, cfg, sel->which) = sel->r;
22968c2ecf20Sopenharmony_ci
22978c2ecf20Sopenharmony_ci	/* Update the source format. */
22988c2ecf20Sopenharmony_ci	format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, sel->which);
22998c2ecf20Sopenharmony_ci	ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, sel->which);
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci	return 0;
23028c2ecf20Sopenharmony_ci}
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci/*
23058c2ecf20Sopenharmony_ci * ccdc_get_format - Retrieve the video format on a pad
23068c2ecf20Sopenharmony_ci * @sd : ISP CCDC V4L2 subdevice
23078c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
23088c2ecf20Sopenharmony_ci * @fmt: Format
23098c2ecf20Sopenharmony_ci *
23108c2ecf20Sopenharmony_ci * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
23118c2ecf20Sopenharmony_ci * to the format type.
23128c2ecf20Sopenharmony_ci */
23138c2ecf20Sopenharmony_cistatic int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
23148c2ecf20Sopenharmony_ci			   struct v4l2_subdev_format *fmt)
23158c2ecf20Sopenharmony_ci{
23168c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
23178c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci	format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which);
23208c2ecf20Sopenharmony_ci	if (format == NULL)
23218c2ecf20Sopenharmony_ci		return -EINVAL;
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	fmt->format = *format;
23248c2ecf20Sopenharmony_ci	return 0;
23258c2ecf20Sopenharmony_ci}
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci/*
23288c2ecf20Sopenharmony_ci * ccdc_set_format - Set the video format on a pad
23298c2ecf20Sopenharmony_ci * @sd : ISP CCDC V4L2 subdevice
23308c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration
23318c2ecf20Sopenharmony_ci * @fmt: Format
23328c2ecf20Sopenharmony_ci *
23338c2ecf20Sopenharmony_ci * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
23348c2ecf20Sopenharmony_ci * to the format type.
23358c2ecf20Sopenharmony_ci */
23368c2ecf20Sopenharmony_cistatic int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
23378c2ecf20Sopenharmony_ci			   struct v4l2_subdev_format *fmt)
23388c2ecf20Sopenharmony_ci{
23398c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
23408c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
23418c2ecf20Sopenharmony_ci	struct v4l2_rect *crop;
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which);
23448c2ecf20Sopenharmony_ci	if (format == NULL)
23458c2ecf20Sopenharmony_ci		return -EINVAL;
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	ccdc_try_format(ccdc, cfg, fmt->pad, &fmt->format, fmt->which);
23488c2ecf20Sopenharmony_ci	*format = fmt->format;
23498c2ecf20Sopenharmony_ci
23508c2ecf20Sopenharmony_ci	/* Propagate the format from sink to source */
23518c2ecf20Sopenharmony_ci	if (fmt->pad == CCDC_PAD_SINK) {
23528c2ecf20Sopenharmony_ci		/* Reset the crop rectangle. */
23538c2ecf20Sopenharmony_ci		crop = __ccdc_get_crop(ccdc, cfg, fmt->which);
23548c2ecf20Sopenharmony_ci		crop->left = 0;
23558c2ecf20Sopenharmony_ci		crop->top = 0;
23568c2ecf20Sopenharmony_ci		crop->width = fmt->format.width;
23578c2ecf20Sopenharmony_ci		crop->height = fmt->format.height;
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci		ccdc_try_crop(ccdc, &fmt->format, crop);
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ci		/* Update the source formats. */
23628c2ecf20Sopenharmony_ci		format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF,
23638c2ecf20Sopenharmony_ci					   fmt->which);
23648c2ecf20Sopenharmony_ci		*format = fmt->format;
23658c2ecf20Sopenharmony_ci		ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format,
23668c2ecf20Sopenharmony_ci				fmt->which);
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_ci		format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_VP,
23698c2ecf20Sopenharmony_ci					   fmt->which);
23708c2ecf20Sopenharmony_ci		*format = fmt->format;
23718c2ecf20Sopenharmony_ci		ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, format,
23728c2ecf20Sopenharmony_ci				fmt->which);
23738c2ecf20Sopenharmony_ci	}
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_ci	return 0;
23768c2ecf20Sopenharmony_ci}
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci/*
23798c2ecf20Sopenharmony_ci * Decide whether desired output pixel code can be obtained with
23808c2ecf20Sopenharmony_ci * the lane shifter by shifting the input pixel code.
23818c2ecf20Sopenharmony_ci * @in: input pixelcode to shifter
23828c2ecf20Sopenharmony_ci * @out: output pixelcode from shifter
23838c2ecf20Sopenharmony_ci * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0]
23848c2ecf20Sopenharmony_ci *
23858c2ecf20Sopenharmony_ci * return true if the combination is possible
23868c2ecf20Sopenharmony_ci * return false otherwise
23878c2ecf20Sopenharmony_ci */
23888c2ecf20Sopenharmony_cistatic bool ccdc_is_shiftable(u32 in, u32 out, unsigned int additional_shift)
23898c2ecf20Sopenharmony_ci{
23908c2ecf20Sopenharmony_ci	const struct isp_format_info *in_info, *out_info;
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_ci	if (in == out)
23938c2ecf20Sopenharmony_ci		return true;
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci	in_info = omap3isp_video_format_info(in);
23968c2ecf20Sopenharmony_ci	out_info = omap3isp_video_format_info(out);
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	if ((in_info->flavor == 0) || (out_info->flavor == 0))
23998c2ecf20Sopenharmony_ci		return false;
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_ci	if (in_info->flavor != out_info->flavor)
24028c2ecf20Sopenharmony_ci		return false;
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci	return in_info->width - out_info->width + additional_shift <= 6;
24058c2ecf20Sopenharmony_ci}
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_cistatic int ccdc_link_validate(struct v4l2_subdev *sd,
24088c2ecf20Sopenharmony_ci			      struct media_link *link,
24098c2ecf20Sopenharmony_ci			      struct v4l2_subdev_format *source_fmt,
24108c2ecf20Sopenharmony_ci			      struct v4l2_subdev_format *sink_fmt)
24118c2ecf20Sopenharmony_ci{
24128c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
24138c2ecf20Sopenharmony_ci	unsigned long parallel_shift;
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_ci	/* Check if the two ends match */
24168c2ecf20Sopenharmony_ci	if (source_fmt->format.width != sink_fmt->format.width ||
24178c2ecf20Sopenharmony_ci	    source_fmt->format.height != sink_fmt->format.height)
24188c2ecf20Sopenharmony_ci		return -EPIPE;
24198c2ecf20Sopenharmony_ci
24208c2ecf20Sopenharmony_ci	/* We've got a parallel sensor here. */
24218c2ecf20Sopenharmony_ci	if (ccdc->input == CCDC_INPUT_PARALLEL) {
24228c2ecf20Sopenharmony_ci		struct v4l2_subdev *sd =
24238c2ecf20Sopenharmony_ci			media_entity_to_v4l2_subdev(link->source->entity);
24248c2ecf20Sopenharmony_ci		struct isp_bus_cfg *bus_cfg = v4l2_subdev_to_bus_cfg(sd);
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci		parallel_shift = bus_cfg->bus.parallel.data_lane_shift;
24278c2ecf20Sopenharmony_ci	} else {
24288c2ecf20Sopenharmony_ci		parallel_shift = 0;
24298c2ecf20Sopenharmony_ci	}
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_ci	/* Lane shifter may be used to drop bits on CCDC sink pad */
24328c2ecf20Sopenharmony_ci	if (!ccdc_is_shiftable(source_fmt->format.code,
24338c2ecf20Sopenharmony_ci			       sink_fmt->format.code, parallel_shift))
24348c2ecf20Sopenharmony_ci		return -EPIPE;
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci	return 0;
24378c2ecf20Sopenharmony_ci}
24388c2ecf20Sopenharmony_ci
24398c2ecf20Sopenharmony_ci/*
24408c2ecf20Sopenharmony_ci * ccdc_init_formats - Initialize formats on all pads
24418c2ecf20Sopenharmony_ci * @sd: ISP CCDC V4L2 subdevice
24428c2ecf20Sopenharmony_ci * @fh: V4L2 subdev file handle
24438c2ecf20Sopenharmony_ci *
24448c2ecf20Sopenharmony_ci * Initialize all pad formats with default values. If fh is not NULL, try
24458c2ecf20Sopenharmony_ci * formats are initialized on the file handle. Otherwise active formats are
24468c2ecf20Sopenharmony_ci * initialized on the device.
24478c2ecf20Sopenharmony_ci */
24488c2ecf20Sopenharmony_cistatic int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
24498c2ecf20Sopenharmony_ci{
24508c2ecf20Sopenharmony_ci	struct v4l2_subdev_format format;
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	memset(&format, 0, sizeof(format));
24538c2ecf20Sopenharmony_ci	format.pad = CCDC_PAD_SINK;
24548c2ecf20Sopenharmony_ci	format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
24558c2ecf20Sopenharmony_ci	format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
24568c2ecf20Sopenharmony_ci	format.format.width = 4096;
24578c2ecf20Sopenharmony_ci	format.format.height = 4096;
24588c2ecf20Sopenharmony_ci	ccdc_set_format(sd, fh ? fh->pad : NULL, &format);
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	return 0;
24618c2ecf20Sopenharmony_ci}
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci/* V4L2 subdev core operations */
24648c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = {
24658c2ecf20Sopenharmony_ci	.ioctl = ccdc_ioctl,
24668c2ecf20Sopenharmony_ci	.subscribe_event = ccdc_subscribe_event,
24678c2ecf20Sopenharmony_ci	.unsubscribe_event = ccdc_unsubscribe_event,
24688c2ecf20Sopenharmony_ci};
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_ci/* V4L2 subdev video operations */
24718c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = {
24728c2ecf20Sopenharmony_ci	.s_stream = ccdc_set_stream,
24738c2ecf20Sopenharmony_ci};
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci/* V4L2 subdev pad operations */
24768c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = {
24778c2ecf20Sopenharmony_ci	.enum_mbus_code = ccdc_enum_mbus_code,
24788c2ecf20Sopenharmony_ci	.enum_frame_size = ccdc_enum_frame_size,
24798c2ecf20Sopenharmony_ci	.get_fmt = ccdc_get_format,
24808c2ecf20Sopenharmony_ci	.set_fmt = ccdc_set_format,
24818c2ecf20Sopenharmony_ci	.get_selection = ccdc_get_selection,
24828c2ecf20Sopenharmony_ci	.set_selection = ccdc_set_selection,
24838c2ecf20Sopenharmony_ci	.link_validate = ccdc_link_validate,
24848c2ecf20Sopenharmony_ci};
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci/* V4L2 subdev operations */
24878c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops ccdc_v4l2_ops = {
24888c2ecf20Sopenharmony_ci	.core = &ccdc_v4l2_core_ops,
24898c2ecf20Sopenharmony_ci	.video = &ccdc_v4l2_video_ops,
24908c2ecf20Sopenharmony_ci	.pad = &ccdc_v4l2_pad_ops,
24918c2ecf20Sopenharmony_ci};
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci/* V4L2 subdev internal operations */
24948c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops ccdc_v4l2_internal_ops = {
24958c2ecf20Sopenharmony_ci	.open = ccdc_init_formats,
24968c2ecf20Sopenharmony_ci};
24978c2ecf20Sopenharmony_ci
24988c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
24998c2ecf20Sopenharmony_ci * Media entity operations
25008c2ecf20Sopenharmony_ci */
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci/*
25038c2ecf20Sopenharmony_ci * ccdc_link_setup - Setup CCDC connections
25048c2ecf20Sopenharmony_ci * @entity: CCDC media entity
25058c2ecf20Sopenharmony_ci * @local: Pad at the local end of the link
25068c2ecf20Sopenharmony_ci * @remote: Pad at the remote end of the link
25078c2ecf20Sopenharmony_ci * @flags: Link flags
25088c2ecf20Sopenharmony_ci *
25098c2ecf20Sopenharmony_ci * return -EINVAL or zero on success
25108c2ecf20Sopenharmony_ci */
25118c2ecf20Sopenharmony_cistatic int ccdc_link_setup(struct media_entity *entity,
25128c2ecf20Sopenharmony_ci			   const struct media_pad *local,
25138c2ecf20Sopenharmony_ci			   const struct media_pad *remote, u32 flags)
25148c2ecf20Sopenharmony_ci{
25158c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
25168c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
25178c2ecf20Sopenharmony_ci	struct isp_device *isp = to_isp_device(ccdc);
25188c2ecf20Sopenharmony_ci	unsigned int index = local->index;
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	/* FIXME: this is actually a hack! */
25218c2ecf20Sopenharmony_ci	if (is_media_entity_v4l2_subdev(remote->entity))
25228c2ecf20Sopenharmony_ci		index |= 2 << 16;
25238c2ecf20Sopenharmony_ci
25248c2ecf20Sopenharmony_ci	switch (index) {
25258c2ecf20Sopenharmony_ci	case CCDC_PAD_SINK | 2 << 16:
25268c2ecf20Sopenharmony_ci		/* Read from the sensor (parallel interface), CCP2, CSI2a or
25278c2ecf20Sopenharmony_ci		 * CSI2c.
25288c2ecf20Sopenharmony_ci		 */
25298c2ecf20Sopenharmony_ci		if (!(flags & MEDIA_LNK_FL_ENABLED)) {
25308c2ecf20Sopenharmony_ci			ccdc->input = CCDC_INPUT_NONE;
25318c2ecf20Sopenharmony_ci			break;
25328c2ecf20Sopenharmony_ci		}
25338c2ecf20Sopenharmony_ci
25348c2ecf20Sopenharmony_ci		if (ccdc->input != CCDC_INPUT_NONE)
25358c2ecf20Sopenharmony_ci			return -EBUSY;
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci		if (remote->entity == &isp->isp_ccp2.subdev.entity)
25388c2ecf20Sopenharmony_ci			ccdc->input = CCDC_INPUT_CCP2B;
25398c2ecf20Sopenharmony_ci		else if (remote->entity == &isp->isp_csi2a.subdev.entity)
25408c2ecf20Sopenharmony_ci			ccdc->input = CCDC_INPUT_CSI2A;
25418c2ecf20Sopenharmony_ci		else if (remote->entity == &isp->isp_csi2c.subdev.entity)
25428c2ecf20Sopenharmony_ci			ccdc->input = CCDC_INPUT_CSI2C;
25438c2ecf20Sopenharmony_ci		else
25448c2ecf20Sopenharmony_ci			ccdc->input = CCDC_INPUT_PARALLEL;
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci		break;
25478c2ecf20Sopenharmony_ci
25488c2ecf20Sopenharmony_ci	/*
25498c2ecf20Sopenharmony_ci	 * The ISP core doesn't support pipelines with multiple video outputs.
25508c2ecf20Sopenharmony_ci	 * Revisit this when it will be implemented, and return -EBUSY for now.
25518c2ecf20Sopenharmony_ci	 */
25528c2ecf20Sopenharmony_ci
25538c2ecf20Sopenharmony_ci	case CCDC_PAD_SOURCE_VP | 2 << 16:
25548c2ecf20Sopenharmony_ci		/* Write to preview engine, histogram and H3A. When none of
25558c2ecf20Sopenharmony_ci		 * those links are active, the video port can be disabled.
25568c2ecf20Sopenharmony_ci		 */
25578c2ecf20Sopenharmony_ci		if (flags & MEDIA_LNK_FL_ENABLED) {
25588c2ecf20Sopenharmony_ci			if (ccdc->output & ~CCDC_OUTPUT_PREVIEW)
25598c2ecf20Sopenharmony_ci				return -EBUSY;
25608c2ecf20Sopenharmony_ci			ccdc->output |= CCDC_OUTPUT_PREVIEW;
25618c2ecf20Sopenharmony_ci		} else {
25628c2ecf20Sopenharmony_ci			ccdc->output &= ~CCDC_OUTPUT_PREVIEW;
25638c2ecf20Sopenharmony_ci		}
25648c2ecf20Sopenharmony_ci		break;
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci	case CCDC_PAD_SOURCE_OF:
25678c2ecf20Sopenharmony_ci		/* Write to memory */
25688c2ecf20Sopenharmony_ci		if (flags & MEDIA_LNK_FL_ENABLED) {
25698c2ecf20Sopenharmony_ci			if (ccdc->output & ~CCDC_OUTPUT_MEMORY)
25708c2ecf20Sopenharmony_ci				return -EBUSY;
25718c2ecf20Sopenharmony_ci			ccdc->output |= CCDC_OUTPUT_MEMORY;
25728c2ecf20Sopenharmony_ci		} else {
25738c2ecf20Sopenharmony_ci			ccdc->output &= ~CCDC_OUTPUT_MEMORY;
25748c2ecf20Sopenharmony_ci		}
25758c2ecf20Sopenharmony_ci		break;
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_ci	case CCDC_PAD_SOURCE_OF | 2 << 16:
25788c2ecf20Sopenharmony_ci		/* Write to resizer */
25798c2ecf20Sopenharmony_ci		if (flags & MEDIA_LNK_FL_ENABLED) {
25808c2ecf20Sopenharmony_ci			if (ccdc->output & ~CCDC_OUTPUT_RESIZER)
25818c2ecf20Sopenharmony_ci				return -EBUSY;
25828c2ecf20Sopenharmony_ci			ccdc->output |= CCDC_OUTPUT_RESIZER;
25838c2ecf20Sopenharmony_ci		} else {
25848c2ecf20Sopenharmony_ci			ccdc->output &= ~CCDC_OUTPUT_RESIZER;
25858c2ecf20Sopenharmony_ci		}
25868c2ecf20Sopenharmony_ci		break;
25878c2ecf20Sopenharmony_ci
25888c2ecf20Sopenharmony_ci	default:
25898c2ecf20Sopenharmony_ci		return -EINVAL;
25908c2ecf20Sopenharmony_ci	}
25918c2ecf20Sopenharmony_ci
25928c2ecf20Sopenharmony_ci	return 0;
25938c2ecf20Sopenharmony_ci}
25948c2ecf20Sopenharmony_ci
25958c2ecf20Sopenharmony_ci/* media operations */
25968c2ecf20Sopenharmony_cistatic const struct media_entity_operations ccdc_media_ops = {
25978c2ecf20Sopenharmony_ci	.link_setup = ccdc_link_setup,
25988c2ecf20Sopenharmony_ci	.link_validate = v4l2_subdev_link_validate,
25998c2ecf20Sopenharmony_ci};
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_civoid omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
26028c2ecf20Sopenharmony_ci{
26038c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(&ccdc->subdev);
26048c2ecf20Sopenharmony_ci	omap3isp_video_unregister(&ccdc->video_out);
26058c2ecf20Sopenharmony_ci}
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_ciint omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
26088c2ecf20Sopenharmony_ci	struct v4l2_device *vdev)
26098c2ecf20Sopenharmony_ci{
26108c2ecf20Sopenharmony_ci	int ret;
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	/* Register the subdev and video node. */
26138c2ecf20Sopenharmony_ci	ccdc->subdev.dev = vdev->mdev->dev;
26148c2ecf20Sopenharmony_ci	ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
26158c2ecf20Sopenharmony_ci	if (ret < 0)
26168c2ecf20Sopenharmony_ci		goto error;
26178c2ecf20Sopenharmony_ci
26188c2ecf20Sopenharmony_ci	ret = omap3isp_video_register(&ccdc->video_out, vdev);
26198c2ecf20Sopenharmony_ci	if (ret < 0)
26208c2ecf20Sopenharmony_ci		goto error;
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_ci	return 0;
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_cierror:
26258c2ecf20Sopenharmony_ci	omap3isp_ccdc_unregister_entities(ccdc);
26268c2ecf20Sopenharmony_ci	return ret;
26278c2ecf20Sopenharmony_ci}
26288c2ecf20Sopenharmony_ci
26298c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
26308c2ecf20Sopenharmony_ci * ISP CCDC initialisation and cleanup
26318c2ecf20Sopenharmony_ci */
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_ci/*
26348c2ecf20Sopenharmony_ci * ccdc_init_entities - Initialize V4L2 subdev and media entity
26358c2ecf20Sopenharmony_ci * @ccdc: ISP CCDC module
26368c2ecf20Sopenharmony_ci *
26378c2ecf20Sopenharmony_ci * Return 0 on success and a negative error code on failure.
26388c2ecf20Sopenharmony_ci */
26398c2ecf20Sopenharmony_cistatic int ccdc_init_entities(struct isp_ccdc_device *ccdc)
26408c2ecf20Sopenharmony_ci{
26418c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = &ccdc->subdev;
26428c2ecf20Sopenharmony_ci	struct media_pad *pads = ccdc->pads;
26438c2ecf20Sopenharmony_ci	struct media_entity *me = &sd->entity;
26448c2ecf20Sopenharmony_ci	int ret;
26458c2ecf20Sopenharmony_ci
26468c2ecf20Sopenharmony_ci	ccdc->input = CCDC_INPUT_NONE;
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	v4l2_subdev_init(sd, &ccdc_v4l2_ops);
26498c2ecf20Sopenharmony_ci	sd->internal_ops = &ccdc_v4l2_internal_ops;
26508c2ecf20Sopenharmony_ci	strscpy(sd->name, "OMAP3 ISP CCDC", sizeof(sd->name));
26518c2ecf20Sopenharmony_ci	sd->grp_id = 1 << 16;	/* group ID for isp subdevs */
26528c2ecf20Sopenharmony_ci	v4l2_set_subdevdata(sd, ccdc);
26538c2ecf20Sopenharmony_ci	sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
26548c2ecf20Sopenharmony_ci
26558c2ecf20Sopenharmony_ci	pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK
26568c2ecf20Sopenharmony_ci				    | MEDIA_PAD_FL_MUST_CONNECT;
26578c2ecf20Sopenharmony_ci	pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
26588c2ecf20Sopenharmony_ci	pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE;
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci	me->ops = &ccdc_media_ops;
26618c2ecf20Sopenharmony_ci	ret = media_entity_pads_init(me, CCDC_PADS_NUM, pads);
26628c2ecf20Sopenharmony_ci	if (ret < 0)
26638c2ecf20Sopenharmony_ci		return ret;
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	ccdc_init_formats(sd, NULL);
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci	ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
26688c2ecf20Sopenharmony_ci	ccdc->video_out.ops = &ccdc_video_ops;
26698c2ecf20Sopenharmony_ci	ccdc->video_out.isp = to_isp_device(ccdc);
26708c2ecf20Sopenharmony_ci	ccdc->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
26718c2ecf20Sopenharmony_ci	ccdc->video_out.bpl_alignment = 32;
26728c2ecf20Sopenharmony_ci
26738c2ecf20Sopenharmony_ci	ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
26748c2ecf20Sopenharmony_ci	if (ret < 0)
26758c2ecf20Sopenharmony_ci		goto error;
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_ci	return 0;
26788c2ecf20Sopenharmony_ci
26798c2ecf20Sopenharmony_cierror:
26808c2ecf20Sopenharmony_ci	media_entity_cleanup(me);
26818c2ecf20Sopenharmony_ci	return ret;
26828c2ecf20Sopenharmony_ci}
26838c2ecf20Sopenharmony_ci
26848c2ecf20Sopenharmony_ci/*
26858c2ecf20Sopenharmony_ci * omap3isp_ccdc_init - CCDC module initialization.
26868c2ecf20Sopenharmony_ci * @isp: Device pointer specific to the OMAP3 ISP.
26878c2ecf20Sopenharmony_ci *
26888c2ecf20Sopenharmony_ci * TODO: Get the initialisation values from platform data.
26898c2ecf20Sopenharmony_ci *
26908c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise.
26918c2ecf20Sopenharmony_ci */
26928c2ecf20Sopenharmony_ciint omap3isp_ccdc_init(struct isp_device *isp)
26938c2ecf20Sopenharmony_ci{
26948c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
26958c2ecf20Sopenharmony_ci	int ret;
26968c2ecf20Sopenharmony_ci
26978c2ecf20Sopenharmony_ci	spin_lock_init(&ccdc->lock);
26988c2ecf20Sopenharmony_ci	init_waitqueue_head(&ccdc->wait);
26998c2ecf20Sopenharmony_ci	mutex_init(&ccdc->ioctl_lock);
27008c2ecf20Sopenharmony_ci
27018c2ecf20Sopenharmony_ci	ccdc->stopping = CCDC_STOP_NOT_REQUESTED;
27028c2ecf20Sopenharmony_ci
27038c2ecf20Sopenharmony_ci	INIT_WORK(&ccdc->lsc.table_work, ccdc_lsc_free_table_work);
27048c2ecf20Sopenharmony_ci	ccdc->lsc.state = LSC_STATE_STOPPED;
27058c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ccdc->lsc.free_queue);
27068c2ecf20Sopenharmony_ci	spin_lock_init(&ccdc->lsc.req_lock);
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	ccdc->clamp.oblen = 0;
27098c2ecf20Sopenharmony_ci	ccdc->clamp.dcsubval = 0;
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_ci	ccdc->update = OMAP3ISP_CCDC_BLCLAMP;
27128c2ecf20Sopenharmony_ci	ccdc_apply_controls(ccdc);
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_ci	ret = ccdc_init_entities(ccdc);
27158c2ecf20Sopenharmony_ci	if (ret < 0) {
27168c2ecf20Sopenharmony_ci		mutex_destroy(&ccdc->ioctl_lock);
27178c2ecf20Sopenharmony_ci		return ret;
27188c2ecf20Sopenharmony_ci	}
27198c2ecf20Sopenharmony_ci
27208c2ecf20Sopenharmony_ci	return 0;
27218c2ecf20Sopenharmony_ci}
27228c2ecf20Sopenharmony_ci
27238c2ecf20Sopenharmony_ci/*
27248c2ecf20Sopenharmony_ci * omap3isp_ccdc_cleanup - CCDC module cleanup.
27258c2ecf20Sopenharmony_ci * @isp: Device pointer specific to the OMAP3 ISP.
27268c2ecf20Sopenharmony_ci */
27278c2ecf20Sopenharmony_civoid omap3isp_ccdc_cleanup(struct isp_device *isp)
27288c2ecf20Sopenharmony_ci{
27298c2ecf20Sopenharmony_ci	struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_ci	omap3isp_video_cleanup(&ccdc->video_out);
27328c2ecf20Sopenharmony_ci	media_entity_cleanup(&ccdc->subdev.entity);
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci	/* Free LSC requests. As the CCDC is stopped there's no active request,
27358c2ecf20Sopenharmony_ci	 * so only the pending request and the free queue need to be handled.
27368c2ecf20Sopenharmony_ci	 */
27378c2ecf20Sopenharmony_ci	ccdc_lsc_free_request(ccdc, ccdc->lsc.request);
27388c2ecf20Sopenharmony_ci	cancel_work_sync(&ccdc->lsc.table_work);
27398c2ecf20Sopenharmony_ci	ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_ci	if (ccdc->fpc.addr != NULL)
27428c2ecf20Sopenharmony_ci		dma_free_coherent(isp->dev, ccdc->fpc.fpnum * 4, ccdc->fpc.addr,
27438c2ecf20Sopenharmony_ci				  ccdc->fpc.dma);
27448c2ecf20Sopenharmony_ci
27458c2ecf20Sopenharmony_ci	mutex_destroy(&ccdc->ioctl_lock);
27468c2ecf20Sopenharmony_ci}
2747