162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * TI Camera Access Layer (CAL) - CAMERARX
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2015-2020 Texas Instruments Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors:
862306a36Sopenharmony_ci *	Benoit Parrot <bparrot@ti.com>
962306a36Sopenharmony_ci *	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/of_graph.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/regmap.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
2262306a36Sopenharmony_ci#include <media/v4l2-fwnode.h>
2362306a36Sopenharmony_ci#include <media/v4l2-subdev.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "cal.h"
2662306a36Sopenharmony_ci#include "cal_regs.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* ------------------------------------------------------------------
2962306a36Sopenharmony_ci *	I/O Register Accessors
3062306a36Sopenharmony_ci * ------------------------------------------------------------------
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic inline u32 camerarx_read(struct cal_camerarx *phy, u32 offset)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	return ioread32(phy->base + offset);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	iowrite32(val, phy->base + offset);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* ------------------------------------------------------------------
4462306a36Sopenharmony_ci *	CAMERARX Management
4562306a36Sopenharmony_ci * ------------------------------------------------------------------
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2;
5162306a36Sopenharmony_ci	u32 num_lanes = mipi_csi2->num_data_lanes;
5262306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
5362306a36Sopenharmony_ci	struct v4l2_subdev_state *state;
5462306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *fmt;
5562306a36Sopenharmony_ci	u32 bpp;
5662306a36Sopenharmony_ci	s64 freq;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	state = v4l2_subdev_get_locked_active_state(&phy->subdev);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	fmt = v4l2_subdev_get_pad_format(&phy->subdev, state, CAL_CAMERARX_PAD_SINK);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	fmtinfo = cal_format_by_code(fmt->code);
6362306a36Sopenharmony_ci	if (!fmtinfo)
6462306a36Sopenharmony_ci		return -EINVAL;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	bpp = fmtinfo->bpp;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	freq = v4l2_get_link_freq(phy->source->ctrl_handler, bpp, 2 * num_lanes);
6962306a36Sopenharmony_ci	if (freq < 0) {
7062306a36Sopenharmony_ci		phy_err(phy, "failed to get link freq for subdev '%s'\n",
7162306a36Sopenharmony_ci			phy->source->name);
7262306a36Sopenharmony_ci		return freq;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	phy_dbg(3, phy, "Source Link Freq: %llu\n", freq);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return freq;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic void cal_camerarx_lane_config(struct cal_camerarx *phy)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance));
8362306a36Sopenharmony_ci	u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;
8462306a36Sopenharmony_ci	u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;
8562306a36Sopenharmony_ci	struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 =
8662306a36Sopenharmony_ci		&phy->endpoint.bus.mipi_csi2;
8762306a36Sopenharmony_ci	int lane;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	cal_set_field(&val, mipi_csi2->clock_lane + 1, lane_mask);
9062306a36Sopenharmony_ci	cal_set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask);
9162306a36Sopenharmony_ci	for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) {
9262306a36Sopenharmony_ci		/*
9362306a36Sopenharmony_ci		 * Every lane are one nibble apart starting with the
9462306a36Sopenharmony_ci		 * clock followed by the data lanes so shift masks by 4.
9562306a36Sopenharmony_ci		 */
9662306a36Sopenharmony_ci		lane_mask <<= 4;
9762306a36Sopenharmony_ci		polarity_mask <<= 4;
9862306a36Sopenharmony_ci		cal_set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask);
9962306a36Sopenharmony_ci		cal_set_field(&val, mipi_csi2->lane_polarities[lane + 1],
10062306a36Sopenharmony_ci			      polarity_mask);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	cal_write(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), val);
10462306a36Sopenharmony_ci	phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n",
10562306a36Sopenharmony_ci		phy->instance, val);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic void cal_camerarx_enable(struct cal_camerarx *phy)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	u32 num_lanes = phy->cal->data->camerarx[phy->instance].num_lanes;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	regmap_field_write(phy->fields[F_CAMMODE], 0);
11362306a36Sopenharmony_ci	/* Always enable all lanes at the phy control level */
11462306a36Sopenharmony_ci	regmap_field_write(phy->fields[F_LANEENABLE], (1 << num_lanes) - 1);
11562306a36Sopenharmony_ci	/* F_CSI_MODE is not present on every architecture */
11662306a36Sopenharmony_ci	if (phy->fields[F_CSI_MODE])
11762306a36Sopenharmony_ci		regmap_field_write(phy->fields[F_CSI_MODE], 1);
11862306a36Sopenharmony_ci	regmap_field_write(phy->fields[F_CTRLCLKEN], 1);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_civoid cal_camerarx_disable(struct cal_camerarx *phy)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	regmap_field_write(phy->fields[F_CTRLCLKEN], 0);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * TCLK values are OK at their reset values
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_ci#define TCLK_TERM	0
13062306a36Sopenharmony_ci#define TCLK_MISS	1
13162306a36Sopenharmony_ci#define TCLK_SETTLE	14
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic void cal_camerarx_config(struct cal_camerarx *phy, s64 link_freq)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	unsigned int reg0, reg1;
13662306a36Sopenharmony_ci	unsigned int ths_term, ths_settle;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* DPHY timing configuration */
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */
14162306a36Sopenharmony_ci	ths_term = div_s64(20 * link_freq, 1000 * 1000 * 1000);
14262306a36Sopenharmony_ci	phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */
14562306a36Sopenharmony_ci	ths_settle = div_s64(105 * link_freq, 1000 * 1000 * 1000) + 4;
14662306a36Sopenharmony_ci	phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0);
14962306a36Sopenharmony_ci	cal_set_field(&reg0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE,
15062306a36Sopenharmony_ci		      CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK);
15162306a36Sopenharmony_ci	cal_set_field(&reg0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK);
15262306a36Sopenharmony_ci	cal_set_field(&reg0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	phy_dbg(1, phy, "CSI2_%d_REG0 = 0x%08x\n", phy->instance, reg0);
15562306a36Sopenharmony_ci	camerarx_write(phy, CAL_CSI2_PHY_REG0, reg0);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	reg1 = camerarx_read(phy, CAL_CSI2_PHY_REG1);
15862306a36Sopenharmony_ci	cal_set_field(&reg1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK);
15962306a36Sopenharmony_ci	cal_set_field(&reg1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK);
16062306a36Sopenharmony_ci	cal_set_field(&reg1, TCLK_MISS,
16162306a36Sopenharmony_ci		      CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK);
16262306a36Sopenharmony_ci	cal_set_field(&reg1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	phy_dbg(1, phy, "CSI2_%d_REG1 = 0x%08x\n", phy->instance, reg1);
16562306a36Sopenharmony_ci	camerarx_write(phy, CAL_CSI2_PHY_REG1, reg1);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void cal_camerarx_power(struct cal_camerarx *phy, bool enable)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	u32 target_state;
17162306a36Sopenharmony_ci	unsigned int i;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON :
17462306a36Sopenharmony_ci		       CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
17762306a36Sopenharmony_ci			target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
18062306a36Sopenharmony_ci		u32 current_state;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		current_state = cal_read_field(phy->cal,
18362306a36Sopenharmony_ci					       CAL_CSI2_COMPLEXIO_CFG(phy->instance),
18462306a36Sopenharmony_ci					       CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci		if (current_state == target_state)
18762306a36Sopenharmony_ci			break;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		usleep_range(1000, 1100);
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (i == 10)
19362306a36Sopenharmony_ci		phy_err(phy, "Failed to power %s complexio\n",
19462306a36Sopenharmony_ci			enable ? "up" : "down");
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void cal_camerarx_wait_reset(struct cal_camerarx *phy)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	unsigned long timeout;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(750);
20262306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
20362306a36Sopenharmony_ci		if (cal_read_field(phy->cal,
20462306a36Sopenharmony_ci				   CAL_CSI2_COMPLEXIO_CFG(phy->instance),
20562306a36Sopenharmony_ci				   CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) ==
20662306a36Sopenharmony_ci		    CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED)
20762306a36Sopenharmony_ci			break;
20862306a36Sopenharmony_ci		usleep_range(500, 5000);
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (cal_read_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
21262306a36Sopenharmony_ci			   CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) !=
21362306a36Sopenharmony_ci			   CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED)
21462306a36Sopenharmony_ci		phy_err(phy, "Timeout waiting for Complex IO reset done\n");
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic void cal_camerarx_wait_stop_state(struct cal_camerarx *phy)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	unsigned long timeout;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(750);
22262306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
22362306a36Sopenharmony_ci		if (cal_read_field(phy->cal,
22462306a36Sopenharmony_ci				   CAL_CSI2_TIMING(phy->instance),
22562306a36Sopenharmony_ci				   CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0)
22662306a36Sopenharmony_ci			break;
22762306a36Sopenharmony_ci		usleep_range(500, 5000);
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (cal_read_field(phy->cal, CAL_CSI2_TIMING(phy->instance),
23162306a36Sopenharmony_ci			   CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0)
23262306a36Sopenharmony_ci		phy_err(phy, "Timeout waiting for stop state\n");
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void cal_camerarx_enable_irqs(struct cal_camerarx *phy)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	const u32 cio_err_mask =
23862306a36Sopenharmony_ci		CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK |
23962306a36Sopenharmony_ci		CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK |
24062306a36Sopenharmony_ci		CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK |
24162306a36Sopenharmony_ci		CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK;
24262306a36Sopenharmony_ci	const u32 vc_err_mask =
24362306a36Sopenharmony_ci		CAL_CSI2_VC_IRQ_CS_IRQ_MASK(0) |
24462306a36Sopenharmony_ci		CAL_CSI2_VC_IRQ_CS_IRQ_MASK(1) |
24562306a36Sopenharmony_ci		CAL_CSI2_VC_IRQ_CS_IRQ_MASK(2) |
24662306a36Sopenharmony_ci		CAL_CSI2_VC_IRQ_CS_IRQ_MASK(3) |
24762306a36Sopenharmony_ci		CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(0) |
24862306a36Sopenharmony_ci		CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(1) |
24962306a36Sopenharmony_ci		CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(2) |
25062306a36Sopenharmony_ci		CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(3);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Enable CIO & VC error IRQs. */
25362306a36Sopenharmony_ci	cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0),
25462306a36Sopenharmony_ci		  CAL_HL_IRQ_CIO_MASK(phy->instance) |
25562306a36Sopenharmony_ci		  CAL_HL_IRQ_VC_MASK(phy->instance));
25662306a36Sopenharmony_ci	cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance),
25762306a36Sopenharmony_ci		  cio_err_mask);
25862306a36Sopenharmony_ci	cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance),
25962306a36Sopenharmony_ci		  vc_err_mask);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic void cal_camerarx_disable_irqs(struct cal_camerarx *phy)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	/* Disable CIO error irqs */
26562306a36Sopenharmony_ci	cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0),
26662306a36Sopenharmony_ci		  CAL_HL_IRQ_CIO_MASK(phy->instance) |
26762306a36Sopenharmony_ci		  CAL_HL_IRQ_VC_MASK(phy->instance));
26862306a36Sopenharmony_ci	cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0);
26962306a36Sopenharmony_ci	cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance), 0);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void cal_camerarx_ppi_enable(struct cal_camerarx *phy)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
27562306a36Sopenharmony_ci			1, CAL_CSI2_PPI_CTRL_ECC_EN_MASK);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
27862306a36Sopenharmony_ci			1, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic void cal_camerarx_ppi_disable(struct cal_camerarx *phy)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance),
28462306a36Sopenharmony_ci			0, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int cal_camerarx_start(struct cal_camerarx *phy)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	s64 link_freq;
29062306a36Sopenharmony_ci	u32 sscounter;
29162306a36Sopenharmony_ci	u32 val;
29262306a36Sopenharmony_ci	int ret;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (phy->enable_count > 0) {
29562306a36Sopenharmony_ci		phy->enable_count++;
29662306a36Sopenharmony_ci		return 0;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	link_freq = cal_camerarx_get_ext_link_freq(phy);
30062306a36Sopenharmony_ci	if (link_freq < 0)
30162306a36Sopenharmony_ci		return link_freq;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	ret = v4l2_subdev_call(phy->source, core, s_power, 1);
30462306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
30562306a36Sopenharmony_ci		phy_err(phy, "power on failed in subdev\n");
30662306a36Sopenharmony_ci		return ret;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	cal_camerarx_enable_irqs(phy);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/*
31262306a36Sopenharmony_ci	 * CSI-2 PHY Link Initialization Sequence, according to the DRA74xP /
31362306a36Sopenharmony_ci	 * DRA75xP / DRA76xP / DRA77xP TRM. The DRA71x / DRA72x and the AM65x /
31462306a36Sopenharmony_ci	 * DRA80xM TRMs have a slightly simplified sequence.
31562306a36Sopenharmony_ci	 */
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/*
31862306a36Sopenharmony_ci	 * 1. Configure all CSI-2 low level protocol registers to be ready to
31962306a36Sopenharmony_ci	 *    receive signals/data from the CSI-2 PHY.
32062306a36Sopenharmony_ci	 *
32162306a36Sopenharmony_ci	 *    i.-v. Configure the lanes position and polarity.
32262306a36Sopenharmony_ci	 */
32362306a36Sopenharmony_ci	cal_camerarx_lane_config(phy);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/*
32662306a36Sopenharmony_ci	 *    vi.-vii. Configure D-PHY mode, enable the required lanes and
32762306a36Sopenharmony_ci	 *             enable the CAMERARX clock.
32862306a36Sopenharmony_ci	 */
32962306a36Sopenharmony_ci	cal_camerarx_enable(phy);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/*
33262306a36Sopenharmony_ci	 * 2. CSI PHY and link initialization sequence.
33362306a36Sopenharmony_ci	 *
33462306a36Sopenharmony_ci	 *    a. Deassert the CSI-2 PHY reset. Do not wait for reset completion
33562306a36Sopenharmony_ci	 *       at this point, as it requires the external source to send the
33662306a36Sopenharmony_ci	 *       CSI-2 HS clock.
33762306a36Sopenharmony_ci	 */
33862306a36Sopenharmony_ci	cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
33962306a36Sopenharmony_ci			CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
34062306a36Sopenharmony_ci			CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
34162306a36Sopenharmony_ci	phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n",
34262306a36Sopenharmony_ci		phy->instance,
34362306a36Sopenharmony_ci		cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)));
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* Dummy read to allow SCP reset to complete. */
34662306a36Sopenharmony_ci	camerarx_read(phy, CAL_CSI2_PHY_REG0);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* Program the PHY timing parameters. */
34962306a36Sopenharmony_ci	cal_camerarx_config(phy, link_freq);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/*
35262306a36Sopenharmony_ci	 *    b. Assert the FORCERXMODE signal.
35362306a36Sopenharmony_ci	 *
35462306a36Sopenharmony_ci	 * The stop-state-counter is based on fclk cycles, and we always use
35562306a36Sopenharmony_ci	 * the x16 and x4 settings, so stop-state-timeout =
35662306a36Sopenharmony_ci	 * fclk-cycle * 16 * 4 * counter.
35762306a36Sopenharmony_ci	 *
35862306a36Sopenharmony_ci	 * Stop-state-timeout must be more than 100us as per CSI-2 spec, so we
35962306a36Sopenharmony_ci	 * calculate a timeout that's 100us (rounding up).
36062306a36Sopenharmony_ci	 */
36162306a36Sopenharmony_ci	sscounter = DIV_ROUND_UP(clk_get_rate(phy->cal->fclk), 10000 *  16 * 4);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	val = cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance));
36462306a36Sopenharmony_ci	cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK);
36562306a36Sopenharmony_ci	cal_set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK);
36662306a36Sopenharmony_ci	cal_set_field(&val, sscounter,
36762306a36Sopenharmony_ci		      CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK);
36862306a36Sopenharmony_ci	cal_write(phy->cal, CAL_CSI2_TIMING(phy->instance), val);
36962306a36Sopenharmony_ci	phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n",
37062306a36Sopenharmony_ci		phy->instance,
37162306a36Sopenharmony_ci		cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)));
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* Assert the FORCERXMODE signal. */
37462306a36Sopenharmony_ci	cal_write_field(phy->cal, CAL_CSI2_TIMING(phy->instance),
37562306a36Sopenharmony_ci			1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK);
37662306a36Sopenharmony_ci	phy_dbg(3, phy, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n",
37762306a36Sopenharmony_ci		phy->instance,
37862306a36Sopenharmony_ci		cal_read(phy->cal, CAL_CSI2_TIMING(phy->instance)));
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/*
38162306a36Sopenharmony_ci	 * c. Connect pull-down on CSI-2 PHY link (using pad control).
38262306a36Sopenharmony_ci	 *
38362306a36Sopenharmony_ci	 * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not
38462306a36Sopenharmony_ci	 * implemented.
38562306a36Sopenharmony_ci	 */
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/*
38862306a36Sopenharmony_ci	 * d. Power up the CSI-2 PHY.
38962306a36Sopenharmony_ci	 * e. Check whether the state status reaches the ON state.
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	cal_camerarx_power(phy, true);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/*
39462306a36Sopenharmony_ci	 * Start the source to enable the CSI-2 HS clock. We can now wait for
39562306a36Sopenharmony_ci	 * CSI-2 PHY reset to complete.
39662306a36Sopenharmony_ci	 */
39762306a36Sopenharmony_ci	ret = v4l2_subdev_call(phy->source, video, s_stream, 1);
39862306a36Sopenharmony_ci	if (ret) {
39962306a36Sopenharmony_ci		v4l2_subdev_call(phy->source, core, s_power, 0);
40062306a36Sopenharmony_ci		cal_camerarx_disable_irqs(phy);
40162306a36Sopenharmony_ci		phy_err(phy, "stream on failed in subdev\n");
40262306a36Sopenharmony_ci		return ret;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	cal_camerarx_wait_reset(phy);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* f. Wait for STOPSTATE=1 for all enabled lane modules. */
40862306a36Sopenharmony_ci	cal_camerarx_wait_stop_state(phy);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	phy_dbg(1, phy, "CSI2_%u_REG1 = 0x%08x (bits 31-28 should be set)\n",
41162306a36Sopenharmony_ci		phy->instance, camerarx_read(phy, CAL_CSI2_PHY_REG1));
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/*
41462306a36Sopenharmony_ci	 * g. Disable pull-down on CSI-2 PHY link (using pad control).
41562306a36Sopenharmony_ci	 *
41662306a36Sopenharmony_ci	 * This is not required on DRA71x, DRA72x, AM65x and DRA80xM. Not
41762306a36Sopenharmony_ci	 * implemented.
41862306a36Sopenharmony_ci	 */
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* Finally, enable the PHY Protocol Interface (PPI). */
42162306a36Sopenharmony_ci	cal_camerarx_ppi_enable(phy);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	phy->enable_count++;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return 0;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void cal_camerarx_stop(struct cal_camerarx *phy)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	int ret;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (--phy->enable_count > 0)
43362306a36Sopenharmony_ci		return;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	cal_camerarx_ppi_disable(phy);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	cal_camerarx_disable_irqs(phy);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	cal_camerarx_power(phy, false);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/* Assert Complex IO Reset */
44262306a36Sopenharmony_ci	cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance),
44362306a36Sopenharmony_ci			CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL,
44462306a36Sopenharmony_ci			CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset\n",
44762306a36Sopenharmony_ci		phy->instance,
44862306a36Sopenharmony_ci		cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)));
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* Disable the phy */
45162306a36Sopenharmony_ci	cal_camerarx_disable(phy);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (v4l2_subdev_call(phy->source, video, s_stream, 0))
45462306a36Sopenharmony_ci		phy_err(phy, "stream off failed in subdev\n");
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	ret = v4l2_subdev_call(phy->source, core, s_power, 0);
45762306a36Sopenharmony_ci	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
45862306a36Sopenharmony_ci		phy_err(phy, "power off failed in subdev\n");
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/*
46262306a36Sopenharmony_ci *   Errata i913: CSI2 LDO Needs to be disabled when module is powered on
46362306a36Sopenharmony_ci *
46462306a36Sopenharmony_ci *   Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2
46562306a36Sopenharmony_ci *   LDOs on the device are disabled if CSI-2 module is powered on
46662306a36Sopenharmony_ci *   (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304
46762306a36Sopenharmony_ci *   | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high
46862306a36Sopenharmony_ci *   current draw on the module supply in active mode.
46962306a36Sopenharmony_ci *
47062306a36Sopenharmony_ci *   Errata does not apply when CSI-2 module is powered off
47162306a36Sopenharmony_ci *   (0x4845 B304 | 0x4845 B384 [28:27] = 0x0).
47262306a36Sopenharmony_ci *
47362306a36Sopenharmony_ci * SW Workaround:
47462306a36Sopenharmony_ci *	Set the following register bits to disable the LDO,
47562306a36Sopenharmony_ci *	which is essentially CSI2 REG10 bit 6:
47662306a36Sopenharmony_ci *
47762306a36Sopenharmony_ci *		Core 0:  0x4845 B828 = 0x0000 0040
47862306a36Sopenharmony_ci *		Core 1:  0x4845 B928 = 0x0000 0040
47962306a36Sopenharmony_ci */
48062306a36Sopenharmony_civoid cal_camerarx_i913_errata(struct cal_camerarx *phy)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	u32 reg10 = camerarx_read(phy, CAL_CSI2_PHY_REG10);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	cal_set_field(&reg10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	phy_dbg(1, phy, "CSI2_%d_REG10 = 0x%08x\n", phy->instance, reg10);
48762306a36Sopenharmony_ci	camerarx_write(phy, CAL_CSI2_PHY_REG10, reg10);
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int cal_camerarx_regmap_init(struct cal_dev *cal,
49162306a36Sopenharmony_ci				    struct cal_camerarx *phy)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	const struct cal_camerarx_data *phy_data;
49462306a36Sopenharmony_ci	unsigned int i;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (!cal->data)
49762306a36Sopenharmony_ci		return -EINVAL;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	phy_data = &cal->data->camerarx[phy->instance];
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	for (i = 0; i < F_MAX_FIELDS; i++) {
50262306a36Sopenharmony_ci		struct reg_field field = {
50362306a36Sopenharmony_ci			.reg = cal->syscon_camerrx_offset,
50462306a36Sopenharmony_ci			.lsb = phy_data->fields[i].lsb,
50562306a36Sopenharmony_ci			.msb = phy_data->fields[i].msb,
50662306a36Sopenharmony_ci		};
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		/*
50962306a36Sopenharmony_ci		 * Here we update the reg offset with the
51062306a36Sopenharmony_ci		 * value found in DT
51162306a36Sopenharmony_ci		 */
51262306a36Sopenharmony_ci		phy->fields[i] = devm_regmap_field_alloc(cal->dev,
51362306a36Sopenharmony_ci							 cal->syscon_camerrx,
51462306a36Sopenharmony_ci							 field);
51562306a36Sopenharmony_ci		if (IS_ERR(phy->fields[i])) {
51662306a36Sopenharmony_ci			cal_err(cal, "Unable to allocate regmap fields\n");
51762306a36Sopenharmony_ci			return PTR_ERR(phy->fields[i]);
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int cal_camerarx_parse_dt(struct cal_camerarx *phy)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint;
52762306a36Sopenharmony_ci	char data_lanes[V4L2_MBUS_CSI2_MAX_DATA_LANES * 2];
52862306a36Sopenharmony_ci	struct device_node *ep_node;
52962306a36Sopenharmony_ci	unsigned int i;
53062306a36Sopenharmony_ci	int ret;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/*
53362306a36Sopenharmony_ci	 * Find the endpoint node for the port corresponding to the PHY
53462306a36Sopenharmony_ci	 * instance, and parse its CSI-2-related properties.
53562306a36Sopenharmony_ci	 */
53662306a36Sopenharmony_ci	ep_node = of_graph_get_endpoint_by_regs(phy->cal->dev->of_node,
53762306a36Sopenharmony_ci						phy->instance, 0);
53862306a36Sopenharmony_ci	if (!ep_node) {
53962306a36Sopenharmony_ci		/*
54062306a36Sopenharmony_ci		 * The endpoint is not mandatory, not all PHY instances need to
54162306a36Sopenharmony_ci		 * be connected in DT.
54262306a36Sopenharmony_ci		 */
54362306a36Sopenharmony_ci		phy_dbg(3, phy, "Port has no endpoint\n");
54462306a36Sopenharmony_ci		return 0;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	endpoint->bus_type = V4L2_MBUS_CSI2_DPHY;
54862306a36Sopenharmony_ci	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint);
54962306a36Sopenharmony_ci	if (ret < 0) {
55062306a36Sopenharmony_ci		phy_err(phy, "Failed to parse endpoint\n");
55162306a36Sopenharmony_ci		goto done;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	for (i = 0; i < endpoint->bus.mipi_csi2.num_data_lanes; i++) {
55562306a36Sopenharmony_ci		unsigned int lane = endpoint->bus.mipi_csi2.data_lanes[i];
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		if (lane > 4) {
55862306a36Sopenharmony_ci			phy_err(phy, "Invalid position %u for data lane %u\n",
55962306a36Sopenharmony_ci				lane, i);
56062306a36Sopenharmony_ci			ret = -EINVAL;
56162306a36Sopenharmony_ci			goto done;
56262306a36Sopenharmony_ci		}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		data_lanes[i*2] = '0' + lane;
56562306a36Sopenharmony_ci		data_lanes[i*2+1] = ' ';
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	data_lanes[i*2-1] = '\0';
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	phy_dbg(3, phy,
57162306a36Sopenharmony_ci		"CSI-2 bus: clock lane <%u>, data lanes <%s>, flags 0x%08x\n",
57262306a36Sopenharmony_ci		endpoint->bus.mipi_csi2.clock_lane, data_lanes,
57362306a36Sopenharmony_ci		endpoint->bus.mipi_csi2.flags);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* Retrieve the connected device and store it for later use. */
57662306a36Sopenharmony_ci	phy->source_ep_node = of_graph_get_remote_endpoint(ep_node);
57762306a36Sopenharmony_ci	phy->source_node = of_graph_get_port_parent(phy->source_ep_node);
57862306a36Sopenharmony_ci	if (!phy->source_node) {
57962306a36Sopenharmony_ci		phy_dbg(3, phy, "Can't get remote parent\n");
58062306a36Sopenharmony_ci		of_node_put(phy->source_ep_node);
58162306a36Sopenharmony_ci		ret = -EINVAL;
58262306a36Sopenharmony_ci		goto done;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	phy_dbg(1, phy, "Found connected device %pOFn\n", phy->source_node);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cidone:
58862306a36Sopenharmony_ci	of_node_put(ep_node);
58962306a36Sopenharmony_ci	return ret;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci/* ------------------------------------------------------------------
59362306a36Sopenharmony_ci *	V4L2 Subdev Operations
59462306a36Sopenharmony_ci * ------------------------------------------------------------------
59562306a36Sopenharmony_ci */
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	return container_of(sd, struct cal_camerarx, subdev);
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct cal_camerarx *phy = to_cal_camerarx(sd);
60562306a36Sopenharmony_ci	struct v4l2_subdev_state *state;
60662306a36Sopenharmony_ci	int ret = 0;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	state = v4l2_subdev_lock_and_get_active_state(sd);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (enable)
61162306a36Sopenharmony_ci		ret = cal_camerarx_start(phy);
61262306a36Sopenharmony_ci	else
61362306a36Sopenharmony_ci		cal_camerarx_stop(phy);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	v4l2_subdev_unlock_state(state);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	return ret;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
62162306a36Sopenharmony_ci					  struct v4l2_subdev_state *state,
62262306a36Sopenharmony_ci					  struct v4l2_subdev_mbus_code_enum *code)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct cal_camerarx *phy = to_cal_camerarx(sd);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* No transcoding, source and sink codes must match. */
62762306a36Sopenharmony_ci	if (cal_rx_pad_is_source(code->pad)) {
62862306a36Sopenharmony_ci		struct v4l2_mbus_framefmt *fmt;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		if (code->index > 0)
63162306a36Sopenharmony_ci			return -EINVAL;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci		fmt = v4l2_subdev_get_pad_format(&phy->subdev, state,
63462306a36Sopenharmony_ci						 CAL_CAMERARX_PAD_SINK);
63562306a36Sopenharmony_ci		code->code = fmt->code;
63662306a36Sopenharmony_ci	} else {
63762306a36Sopenharmony_ci		if (code->index >= cal_num_formats)
63862306a36Sopenharmony_ci			return -EINVAL;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		code->code = cal_formats[code->index].code;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return 0;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd,
64762306a36Sopenharmony_ci					   struct v4l2_subdev_state *state,
64862306a36Sopenharmony_ci					   struct v4l2_subdev_frame_size_enum *fse)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (fse->index > 0)
65362306a36Sopenharmony_ci		return -EINVAL;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	/* No transcoding, source and sink formats must match. */
65662306a36Sopenharmony_ci	if (cal_rx_pad_is_source(fse->pad)) {
65762306a36Sopenharmony_ci		struct v4l2_mbus_framefmt *fmt;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		fmt = v4l2_subdev_get_pad_format(sd, state,
66062306a36Sopenharmony_ci						 CAL_CAMERARX_PAD_SINK);
66162306a36Sopenharmony_ci		if (fse->code != fmt->code)
66262306a36Sopenharmony_ci			return -EINVAL;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		fse->min_width = fmt->width;
66562306a36Sopenharmony_ci		fse->max_width = fmt->width;
66662306a36Sopenharmony_ci		fse->min_height = fmt->height;
66762306a36Sopenharmony_ci		fse->max_height = fmt->height;
66862306a36Sopenharmony_ci	} else {
66962306a36Sopenharmony_ci		fmtinfo = cal_format_by_code(fse->code);
67062306a36Sopenharmony_ci		if (!fmtinfo)
67162306a36Sopenharmony_ci			return -EINVAL;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);
67462306a36Sopenharmony_ci		fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8);
67562306a36Sopenharmony_ci		fse->min_height = CAL_MIN_HEIGHT_LINES;
67662306a36Sopenharmony_ci		fse->max_height = CAL_MAX_HEIGHT_LINES;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd,
68362306a36Sopenharmony_ci				   struct v4l2_subdev_state *state,
68462306a36Sopenharmony_ci				   struct v4l2_subdev_format *format)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	const struct cal_format_info *fmtinfo;
68762306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *fmt;
68862306a36Sopenharmony_ci	unsigned int bpp;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/* No transcoding, source and sink formats must match. */
69162306a36Sopenharmony_ci	if (cal_rx_pad_is_source(format->pad))
69262306a36Sopenharmony_ci		return v4l2_subdev_get_fmt(sd, state, format);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/*
69562306a36Sopenharmony_ci	 * Default to the first format if the requested media bus code isn't
69662306a36Sopenharmony_ci	 * supported.
69762306a36Sopenharmony_ci	 */
69862306a36Sopenharmony_ci	fmtinfo = cal_format_by_code(format->format.code);
69962306a36Sopenharmony_ci	if (!fmtinfo)
70062306a36Sopenharmony_ci		fmtinfo = &cal_formats[0];
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	/* Clamp the size, update the code. The colorspace is accepted as-is. */
70362306a36Sopenharmony_ci	bpp = ALIGN(fmtinfo->bpp, 8);
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	format->format.width = clamp_t(unsigned int, format->format.width,
70662306a36Sopenharmony_ci				       CAL_MIN_WIDTH_BYTES * 8 / bpp,
70762306a36Sopenharmony_ci				       CAL_MAX_WIDTH_BYTES * 8 / bpp);
70862306a36Sopenharmony_ci	format->format.height = clamp_t(unsigned int, format->format.height,
70962306a36Sopenharmony_ci					CAL_MIN_HEIGHT_LINES,
71062306a36Sopenharmony_ci					CAL_MAX_HEIGHT_LINES);
71162306a36Sopenharmony_ci	format->format.code = fmtinfo->code;
71262306a36Sopenharmony_ci	format->format.field = V4L2_FIELD_NONE;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/* Store the format and propagate it to the source pad. */
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	fmt = v4l2_subdev_get_pad_format(sd, state, CAL_CAMERARX_PAD_SINK);
71762306a36Sopenharmony_ci	*fmt = format->format;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	fmt = v4l2_subdev_get_pad_format(sd, state,
72062306a36Sopenharmony_ci					 CAL_CAMERARX_PAD_FIRST_SOURCE);
72162306a36Sopenharmony_ci	*fmt = format->format;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	return 0;
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic int cal_camerarx_sd_init_cfg(struct v4l2_subdev *sd,
72762306a36Sopenharmony_ci				    struct v4l2_subdev_state *state)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct v4l2_subdev_format format = {
73062306a36Sopenharmony_ci		.which = state ? V4L2_SUBDEV_FORMAT_TRY
73162306a36Sopenharmony_ci		: V4L2_SUBDEV_FORMAT_ACTIVE,
73262306a36Sopenharmony_ci		.pad = CAL_CAMERARX_PAD_SINK,
73362306a36Sopenharmony_ci		.format = {
73462306a36Sopenharmony_ci			.width = 640,
73562306a36Sopenharmony_ci			.height = 480,
73662306a36Sopenharmony_ci			.code = MEDIA_BUS_FMT_UYVY8_1X16,
73762306a36Sopenharmony_ci			.field = V4L2_FIELD_NONE,
73862306a36Sopenharmony_ci			.colorspace = V4L2_COLORSPACE_SRGB,
73962306a36Sopenharmony_ci			.ycbcr_enc = V4L2_YCBCR_ENC_601,
74062306a36Sopenharmony_ci			.quantization = V4L2_QUANTIZATION_LIM_RANGE,
74162306a36Sopenharmony_ci			.xfer_func = V4L2_XFER_FUNC_SRGB,
74262306a36Sopenharmony_ci		},
74362306a36Sopenharmony_ci	};
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	return cal_camerarx_sd_set_fmt(sd, state, &format);
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic int cal_camerarx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
74962306a36Sopenharmony_ci				       struct v4l2_mbus_frame_desc *fd)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct cal_camerarx *phy = to_cal_camerarx(sd);
75262306a36Sopenharmony_ci	struct v4l2_mbus_frame_desc remote_desc;
75362306a36Sopenharmony_ci	const struct media_pad *remote_pad;
75462306a36Sopenharmony_ci	int ret;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]);
75762306a36Sopenharmony_ci	if (!remote_pad)
75862306a36Sopenharmony_ci		return -EPIPE;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	ret = v4l2_subdev_call(phy->source, pad, get_frame_desc,
76162306a36Sopenharmony_ci			       remote_pad->index, &remote_desc);
76262306a36Sopenharmony_ci	if (ret)
76362306a36Sopenharmony_ci		return ret;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
76662306a36Sopenharmony_ci		cal_err(phy->cal,
76762306a36Sopenharmony_ci			"Frame descriptor does not describe CSI-2 link");
76862306a36Sopenharmony_ci		return -EINVAL;
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (remote_desc.num_entries > 1)
77262306a36Sopenharmony_ci		cal_err(phy->cal,
77362306a36Sopenharmony_ci			"Multiple streams not supported in remote frame descriptor, using the first one\n");
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
77662306a36Sopenharmony_ci	fd->num_entries = 1;
77762306a36Sopenharmony_ci	fd->entry[0] = remote_desc.entry[0];
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	return 0;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops cal_camerarx_video_ops = {
78362306a36Sopenharmony_ci	.s_stream = cal_camerarx_sd_s_stream,
78462306a36Sopenharmony_ci};
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = {
78762306a36Sopenharmony_ci	.init_cfg = cal_camerarx_sd_init_cfg,
78862306a36Sopenharmony_ci	.enum_mbus_code = cal_camerarx_sd_enum_mbus_code,
78962306a36Sopenharmony_ci	.enum_frame_size = cal_camerarx_sd_enum_frame_size,
79062306a36Sopenharmony_ci	.get_fmt = v4l2_subdev_get_fmt,
79162306a36Sopenharmony_ci	.set_fmt = cal_camerarx_sd_set_fmt,
79262306a36Sopenharmony_ci	.get_frame_desc = cal_camerarx_get_frame_desc,
79362306a36Sopenharmony_ci};
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic const struct v4l2_subdev_ops cal_camerarx_subdev_ops = {
79662306a36Sopenharmony_ci	.video = &cal_camerarx_video_ops,
79762306a36Sopenharmony_ci	.pad = &cal_camerarx_pad_ops,
79862306a36Sopenharmony_ci};
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic struct media_entity_operations cal_camerarx_media_ops = {
80162306a36Sopenharmony_ci	.link_validate = v4l2_subdev_link_validate,
80262306a36Sopenharmony_ci};
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci/* ------------------------------------------------------------------
80562306a36Sopenharmony_ci *	Create and Destroy
80662306a36Sopenharmony_ci * ------------------------------------------------------------------
80762306a36Sopenharmony_ci */
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistruct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
81062306a36Sopenharmony_ci					 unsigned int instance)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(cal->dev);
81362306a36Sopenharmony_ci	struct cal_camerarx *phy;
81462306a36Sopenharmony_ci	struct v4l2_subdev *sd;
81562306a36Sopenharmony_ci	unsigned int i;
81662306a36Sopenharmony_ci	int ret;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	phy = devm_kzalloc(cal->dev, sizeof(*phy), GFP_KERNEL);
81962306a36Sopenharmony_ci	if (!phy)
82062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	phy->cal = cal;
82362306a36Sopenharmony_ci	phy->instance = instance;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	spin_lock_init(&phy->vc_lock);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
82862306a36Sopenharmony_ci						(instance == 0) ?
82962306a36Sopenharmony_ci						"cal_rx_core0" :
83062306a36Sopenharmony_ci						"cal_rx_core1");
83162306a36Sopenharmony_ci	phy->base = devm_ioremap_resource(cal->dev, phy->res);
83262306a36Sopenharmony_ci	if (IS_ERR(phy->base)) {
83362306a36Sopenharmony_ci		cal_err(cal, "failed to ioremap\n");
83462306a36Sopenharmony_ci		return ERR_CAST(phy->base);
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	cal_dbg(1, cal, "ioresource %s at %pa - %pa\n",
83862306a36Sopenharmony_ci		phy->res->name, &phy->res->start, &phy->res->end);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	ret = cal_camerarx_regmap_init(cal, phy);
84162306a36Sopenharmony_ci	if (ret)
84262306a36Sopenharmony_ci		return ERR_PTR(ret);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	ret = cal_camerarx_parse_dt(phy);
84562306a36Sopenharmony_ci	if (ret)
84662306a36Sopenharmony_ci		return ERR_PTR(ret);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/* Initialize the V4L2 subdev and media entity. */
84962306a36Sopenharmony_ci	sd = &phy->subdev;
85062306a36Sopenharmony_ci	v4l2_subdev_init(sd, &cal_camerarx_subdev_ops);
85162306a36Sopenharmony_ci	sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
85262306a36Sopenharmony_ci	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
85362306a36Sopenharmony_ci	snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance);
85462306a36Sopenharmony_ci	sd->dev = cal->dev;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
85762306a36Sopenharmony_ci	for (i = CAL_CAMERARX_PAD_FIRST_SOURCE; i < CAL_CAMERARX_NUM_PADS; ++i)
85862306a36Sopenharmony_ci		phy->pads[i].flags = MEDIA_PAD_FL_SOURCE;
85962306a36Sopenharmony_ci	sd->entity.ops = &cal_camerarx_media_ops;
86062306a36Sopenharmony_ci	ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads),
86162306a36Sopenharmony_ci				     phy->pads);
86262306a36Sopenharmony_ci	if (ret)
86362306a36Sopenharmony_ci		goto err_node_put;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	ret = v4l2_subdev_init_finalize(sd);
86662306a36Sopenharmony_ci	if (ret)
86762306a36Sopenharmony_ci		goto err_entity_cleanup;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd);
87062306a36Sopenharmony_ci	if (ret)
87162306a36Sopenharmony_ci		goto err_free_state;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	return phy;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cierr_free_state:
87662306a36Sopenharmony_ci	v4l2_subdev_cleanup(sd);
87762306a36Sopenharmony_cierr_entity_cleanup:
87862306a36Sopenharmony_ci	media_entity_cleanup(&phy->subdev.entity);
87962306a36Sopenharmony_cierr_node_put:
88062306a36Sopenharmony_ci	of_node_put(phy->source_ep_node);
88162306a36Sopenharmony_ci	of_node_put(phy->source_node);
88262306a36Sopenharmony_ci	return ERR_PTR(ret);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_civoid cal_camerarx_destroy(struct cal_camerarx *phy)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	if (!phy)
88862306a36Sopenharmony_ci		return;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	v4l2_device_unregister_subdev(&phy->subdev);
89162306a36Sopenharmony_ci	v4l2_subdev_cleanup(&phy->subdev);
89262306a36Sopenharmony_ci	media_entity_cleanup(&phy->subdev.entity);
89362306a36Sopenharmony_ci	of_node_put(phy->source_ep_node);
89462306a36Sopenharmony_ci	of_node_put(phy->source_node);
89562306a36Sopenharmony_ci}
896