162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (C) 2013 NVIDIA Corporation
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission to use, copy, modify, distribute, and sell this software and its
562306a36Sopenharmony_ci * documentation for any purpose is hereby granted without fee, provided that
662306a36Sopenharmony_ci * the above copyright notice appear in all copies and that both that copyright
762306a36Sopenharmony_ci * notice and this permission notice appear in supporting documentation, and
862306a36Sopenharmony_ci * that the name of the copyright holders not be used in advertising or
962306a36Sopenharmony_ci * publicity pertaining to distribution of the software without specific,
1062306a36Sopenharmony_ci * written prior permission.  The copyright holders make no representations
1162306a36Sopenharmony_ci * about the suitability of this software for any purpose.  It is provided "as
1262306a36Sopenharmony_ci * is" without express or implied warranty.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1562306a36Sopenharmony_ci * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1662306a36Sopenharmony_ci * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1762306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1862306a36Sopenharmony_ci * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1962306a36Sopenharmony_ci * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
2062306a36Sopenharmony_ci * OF THIS SOFTWARE.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/clk.h>
2462306a36Sopenharmony_ci#include <linux/host1x.h>
2562306a36Sopenharmony_ci#include <linux/io.h>
2662306a36Sopenharmony_ci#include <linux/iopoll.h>
2762306a36Sopenharmony_ci#include <linux/of_platform.h>
2862306a36Sopenharmony_ci#include <linux/platform_device.h>
2962306a36Sopenharmony_ci#include <linux/slab.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "dev.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define MIPI_CAL_CTRL			0x00
3462306a36Sopenharmony_ci#define MIPI_CAL_CTRL_NOISE_FILTER(x)	(((x) & 0xf) << 26)
3562306a36Sopenharmony_ci#define MIPI_CAL_CTRL_PRESCALE(x)	(((x) & 0x3) << 24)
3662306a36Sopenharmony_ci#define MIPI_CAL_CTRL_CLKEN_OVR		(1 << 4)
3762306a36Sopenharmony_ci#define MIPI_CAL_CTRL_START		(1 << 0)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define MIPI_CAL_AUTOCAL_CTRL		0x01
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MIPI_CAL_STATUS			0x02
4262306a36Sopenharmony_ci#define MIPI_CAL_STATUS_DONE		(1 << 16)
4362306a36Sopenharmony_ci#define MIPI_CAL_STATUS_ACTIVE		(1 <<  0)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIA		0x05
4662306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIB		0x06
4762306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIC		0x07
4862306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_CSID		0x08
4962306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIE		0x09
5062306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIF		0x0a
5162306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIA		0x0e
5262306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIB		0x0f
5362306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIC		0x10
5462306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_DSID		0x11
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIA_CLK	0x19
5762306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIB_CLK	0x1a
5862306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIAB_CLK	0x1b
5962306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIC_CLK	0x1c
6062306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_CSICD_CLK	0x1c
6162306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_DSID_CLK	0x1d
6262306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIE_CLK	0x1d
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* for data and clock lanes */
6562306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_SELECT		(1 << 21)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* for data lanes */
6862306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_HSPDOS(x)	(((x) & 0x1f) << 16)
6962306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_HSPUOS(x)	(((x) & 0x1f) <<  8)
7062306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_TERMOS(x)	(((x) & 0x1f) <<  0)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* for clock lanes */
7362306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_HSCLKPDOSD(x)	(((x) & 0x1f) <<  8)
7462306a36Sopenharmony_ci#define MIPI_CAL_CONFIG_HSCLKPUOSD(x)	(((x) & 0x1f) <<  0)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_CFG0		0x16
7762306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_PDVCLAMP	(1 << 1)
7862306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF	(1 << 0)
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_CFG1		0x17
8162306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
8262306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_CFG2		0x18
8562306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_VCLAMP(x)	(((x) & 0x7) << 16)
8662306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_VAUXP(x)	(((x) & 0x7) << 4)
8762306a36Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_PDVREG	(1 << 1)
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistruct tegra_mipi_pad {
9062306a36Sopenharmony_ci	unsigned long data;
9162306a36Sopenharmony_ci	unsigned long clk;
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct tegra_mipi_soc {
9562306a36Sopenharmony_ci	bool has_clk_lane;
9662306a36Sopenharmony_ci	const struct tegra_mipi_pad *pads;
9762306a36Sopenharmony_ci	unsigned int num_pads;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	bool clock_enable_override;
10062306a36Sopenharmony_ci	bool needs_vclamp_ref;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* bias pad configuration settings */
10362306a36Sopenharmony_ci	u8 pad_drive_down_ref;
10462306a36Sopenharmony_ci	u8 pad_drive_up_ref;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	u8 pad_vclamp_level;
10762306a36Sopenharmony_ci	u8 pad_vauxp_level;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* calibration settings for data lanes */
11062306a36Sopenharmony_ci	u8 hspdos;
11162306a36Sopenharmony_ci	u8 hspuos;
11262306a36Sopenharmony_ci	u8 termos;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* calibration settings for clock lanes */
11562306a36Sopenharmony_ci	u8 hsclkpdos;
11662306a36Sopenharmony_ci	u8 hsclkpuos;
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistruct tegra_mipi {
12062306a36Sopenharmony_ci	const struct tegra_mipi_soc *soc;
12162306a36Sopenharmony_ci	struct device *dev;
12262306a36Sopenharmony_ci	void __iomem *regs;
12362306a36Sopenharmony_ci	struct mutex lock;
12462306a36Sopenharmony_ci	struct clk *clk;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	unsigned long usage_count;
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistruct tegra_mipi_device {
13062306a36Sopenharmony_ci	struct platform_device *pdev;
13162306a36Sopenharmony_ci	struct tegra_mipi *mipi;
13262306a36Sopenharmony_ci	struct device *device;
13362306a36Sopenharmony_ci	unsigned long pads;
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
13762306a36Sopenharmony_ci				   unsigned long offset)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	return readl(mipi->regs + (offset << 2));
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
14362306a36Sopenharmony_ci				     unsigned long offset)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	writel(value, mipi->regs + (offset << 2));
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int tegra_mipi_power_up(struct tegra_mipi *mipi)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	u32 value;
15162306a36Sopenharmony_ci	int err;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	err = clk_enable(mipi->clk);
15462306a36Sopenharmony_ci	if (err < 0)
15562306a36Sopenharmony_ci		return err;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
15862306a36Sopenharmony_ci	value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (mipi->soc->needs_vclamp_ref)
16162306a36Sopenharmony_ci		value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
16662306a36Sopenharmony_ci	value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
16762306a36Sopenharmony_ci	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	clk_disable(mipi->clk);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int tegra_mipi_power_down(struct tegra_mipi *mipi)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	u32 value;
17762306a36Sopenharmony_ci	int err;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	err = clk_enable(mipi->clk);
18062306a36Sopenharmony_ci	if (err < 0)
18162306a36Sopenharmony_ci		return err;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
18562306a36Sopenharmony_ci	 * supplies the DSI pads. This must be kept enabled until none of the
18662306a36Sopenharmony_ci	 * DSI lanes are used anymore.
18762306a36Sopenharmony_ci	 */
18862306a36Sopenharmony_ci	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
18962306a36Sopenharmony_ci	value |= MIPI_CAL_BIAS_PAD_PDVREG;
19062306a36Sopenharmony_ci	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/*
19362306a36Sopenharmony_ci	 * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
19462306a36Sopenharmony_ci	 * control a regulator that supplies current to the pre-driver logic.
19562306a36Sopenharmony_ci	 * Powering down this regulator causes DSI to fail, so it must remain
19662306a36Sopenharmony_ci	 * powered on until none of the DSI lanes are used anymore.
19762306a36Sopenharmony_ci	 */
19862306a36Sopenharmony_ci	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (mipi->soc->needs_vclamp_ref)
20162306a36Sopenharmony_ci		value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
20462306a36Sopenharmony_ci	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistruct tegra_mipi_device *tegra_mipi_request(struct device *device,
21062306a36Sopenharmony_ci					     struct device_node *np)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct tegra_mipi_device *dev;
21362306a36Sopenharmony_ci	struct of_phandle_args args;
21462306a36Sopenharmony_ci	int err;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	err = of_parse_phandle_with_args(np, "nvidia,mipi-calibrate",
21762306a36Sopenharmony_ci					 "#nvidia,mipi-calibrate-cells", 0,
21862306a36Sopenharmony_ci					 &args);
21962306a36Sopenharmony_ci	if (err < 0)
22062306a36Sopenharmony_ci		return ERR_PTR(err);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
22362306a36Sopenharmony_ci	if (!dev) {
22462306a36Sopenharmony_ci		err = -ENOMEM;
22562306a36Sopenharmony_ci		goto out;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	dev->pdev = of_find_device_by_node(args.np);
22962306a36Sopenharmony_ci	if (!dev->pdev) {
23062306a36Sopenharmony_ci		err = -ENODEV;
23162306a36Sopenharmony_ci		goto free;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	dev->mipi = platform_get_drvdata(dev->pdev);
23562306a36Sopenharmony_ci	if (!dev->mipi) {
23662306a36Sopenharmony_ci		err = -EPROBE_DEFER;
23762306a36Sopenharmony_ci		goto put;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	of_node_put(args.np);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	dev->pads = args.args[0];
24362306a36Sopenharmony_ci	dev->device = device;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return dev;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ciput:
24862306a36Sopenharmony_ci	platform_device_put(dev->pdev);
24962306a36Sopenharmony_cifree:
25062306a36Sopenharmony_ci	kfree(dev);
25162306a36Sopenharmony_ciout:
25262306a36Sopenharmony_ci	of_node_put(args.np);
25362306a36Sopenharmony_ci	return ERR_PTR(err);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_request);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_civoid tegra_mipi_free(struct tegra_mipi_device *device)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	platform_device_put(device->pdev);
26062306a36Sopenharmony_ci	kfree(device);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_free);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ciint tegra_mipi_enable(struct tegra_mipi_device *dev)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	int err = 0;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	mutex_lock(&dev->mipi->lock);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (dev->mipi->usage_count++ == 0)
27162306a36Sopenharmony_ci		err = tegra_mipi_power_up(dev->mipi);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	mutex_unlock(&dev->mipi->lock);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return err;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_enable);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ciint tegra_mipi_disable(struct tegra_mipi_device *dev)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	int err = 0;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	mutex_lock(&dev->mipi->lock);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (--dev->mipi->usage_count == 0)
28762306a36Sopenharmony_ci		err = tegra_mipi_power_down(dev->mipi);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	mutex_unlock(&dev->mipi->lock);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return err;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_disable);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ciint tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct tegra_mipi *mipi = device->mipi;
29962306a36Sopenharmony_ci	void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
30062306a36Sopenharmony_ci	u32 value;
30162306a36Sopenharmony_ci	int err;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	err = readl_relaxed_poll_timeout(status_reg, value,
30462306a36Sopenharmony_ci					 !(value & MIPI_CAL_STATUS_ACTIVE) &&
30562306a36Sopenharmony_ci					 (value & MIPI_CAL_STATUS_DONE), 50,
30662306a36Sopenharmony_ci					 250000);
30762306a36Sopenharmony_ci	mutex_unlock(&device->mipi->lock);
30862306a36Sopenharmony_ci	clk_disable(device->mipi->clk);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return err;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_finish_calibration);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ciint tegra_mipi_start_calibration(struct tegra_mipi_device *device)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	const struct tegra_mipi_soc *soc = device->mipi->soc;
31762306a36Sopenharmony_ci	unsigned int i;
31862306a36Sopenharmony_ci	u32 value;
31962306a36Sopenharmony_ci	int err;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	err = clk_enable(device->mipi->clk);
32262306a36Sopenharmony_ci	if (err < 0)
32362306a36Sopenharmony_ci		return err;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	mutex_lock(&device->mipi->lock);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
32862306a36Sopenharmony_ci		MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
32962306a36Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
33262306a36Sopenharmony_ci	value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
33362306a36Sopenharmony_ci	value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
33462306a36Sopenharmony_ci	value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
33562306a36Sopenharmony_ci	value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
33662306a36Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	for (i = 0; i < soc->num_pads; i++) {
33962306a36Sopenharmony_ci		u32 clk = 0, data = 0;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		if (device->pads & BIT(i)) {
34262306a36Sopenharmony_ci			data = MIPI_CAL_CONFIG_SELECT |
34362306a36Sopenharmony_ci			       MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
34462306a36Sopenharmony_ci			       MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
34562306a36Sopenharmony_ci			       MIPI_CAL_CONFIG_TERMOS(soc->termos);
34662306a36Sopenharmony_ci			clk = MIPI_CAL_CONFIG_SELECT |
34762306a36Sopenharmony_ci			      MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
34862306a36Sopenharmony_ci			      MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
34962306a36Sopenharmony_ci		}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		if (soc->has_clk_lane && soc->pads[i].clk != 0)
35462306a36Sopenharmony_ci			tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
35862306a36Sopenharmony_ci	value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
35962306a36Sopenharmony_ci	value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
36062306a36Sopenharmony_ci	value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
36162306a36Sopenharmony_ci	value |= MIPI_CAL_CTRL_PRESCALE(0x2);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (!soc->clock_enable_override)
36462306a36Sopenharmony_ci		value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
36562306a36Sopenharmony_ci	else
36662306a36Sopenharmony_ci		value |= MIPI_CAL_CTRL_CLKEN_OVR;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* clear any pending status bits */
37162306a36Sopenharmony_ci	value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS);
37262306a36Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
37562306a36Sopenharmony_ci	value |= MIPI_CAL_CTRL_START;
37662306a36Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/*
37962306a36Sopenharmony_ci	 * Wait for min 72uS to let calibration logic finish calibration
38062306a36Sopenharmony_ci	 * sequence codes before waiting for pads idle state to apply the
38162306a36Sopenharmony_ci	 * results.
38262306a36Sopenharmony_ci	 */
38362306a36Sopenharmony_ci	usleep_range(75, 80);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return 0;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_start_calibration);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic const struct tegra_mipi_pad tegra114_mipi_pads[] = {
39062306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIA },
39162306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIB },
39262306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIC },
39362306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSID },
39462306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIE },
39562306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIA },
39662306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIB },
39762306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIC },
39862306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSID },
39962306a36Sopenharmony_ci};
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic const struct tegra_mipi_soc tegra114_mipi_soc = {
40262306a36Sopenharmony_ci	.has_clk_lane = false,
40362306a36Sopenharmony_ci	.pads = tegra114_mipi_pads,
40462306a36Sopenharmony_ci	.num_pads = ARRAY_SIZE(tegra114_mipi_pads),
40562306a36Sopenharmony_ci	.clock_enable_override = true,
40662306a36Sopenharmony_ci	.needs_vclamp_ref = true,
40762306a36Sopenharmony_ci	.pad_drive_down_ref = 0x2,
40862306a36Sopenharmony_ci	.pad_drive_up_ref = 0x0,
40962306a36Sopenharmony_ci	.pad_vclamp_level = 0x0,
41062306a36Sopenharmony_ci	.pad_vauxp_level = 0x0,
41162306a36Sopenharmony_ci	.hspdos = 0x0,
41262306a36Sopenharmony_ci	.hspuos = 0x4,
41362306a36Sopenharmony_ci	.termos = 0x5,
41462306a36Sopenharmony_ci	.hsclkpdos = 0x0,
41562306a36Sopenharmony_ci	.hsclkpuos = 0x4,
41662306a36Sopenharmony_ci};
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic const struct tegra_mipi_pad tegra124_mipi_pads[] = {
41962306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
42062306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
42162306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
42262306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
42362306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK  },
42462306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK  },
42562306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK  },
42662306a36Sopenharmony_ci};
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic const struct tegra_mipi_soc tegra124_mipi_soc = {
42962306a36Sopenharmony_ci	.has_clk_lane = true,
43062306a36Sopenharmony_ci	.pads = tegra124_mipi_pads,
43162306a36Sopenharmony_ci	.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
43262306a36Sopenharmony_ci	.clock_enable_override = true,
43362306a36Sopenharmony_ci	.needs_vclamp_ref = true,
43462306a36Sopenharmony_ci	.pad_drive_down_ref = 0x2,
43562306a36Sopenharmony_ci	.pad_drive_up_ref = 0x0,
43662306a36Sopenharmony_ci	.pad_vclamp_level = 0x0,
43762306a36Sopenharmony_ci	.pad_vauxp_level = 0x0,
43862306a36Sopenharmony_ci	.hspdos = 0x0,
43962306a36Sopenharmony_ci	.hspuos = 0x0,
44062306a36Sopenharmony_ci	.termos = 0x0,
44162306a36Sopenharmony_ci	.hsclkpdos = 0x1,
44262306a36Sopenharmony_ci	.hsclkpuos = 0x2,
44362306a36Sopenharmony_ci};
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic const struct tegra_mipi_soc tegra132_mipi_soc = {
44662306a36Sopenharmony_ci	.has_clk_lane = true,
44762306a36Sopenharmony_ci	.pads = tegra124_mipi_pads,
44862306a36Sopenharmony_ci	.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
44962306a36Sopenharmony_ci	.clock_enable_override = false,
45062306a36Sopenharmony_ci	.needs_vclamp_ref = false,
45162306a36Sopenharmony_ci	.pad_drive_down_ref = 0x0,
45262306a36Sopenharmony_ci	.pad_drive_up_ref = 0x3,
45362306a36Sopenharmony_ci	.pad_vclamp_level = 0x0,
45462306a36Sopenharmony_ci	.pad_vauxp_level = 0x0,
45562306a36Sopenharmony_ci	.hspdos = 0x0,
45662306a36Sopenharmony_ci	.hspuos = 0x0,
45762306a36Sopenharmony_ci	.termos = 0x0,
45862306a36Sopenharmony_ci	.hsclkpdos = 0x3,
45962306a36Sopenharmony_ci	.hsclkpuos = 0x2,
46062306a36Sopenharmony_ci};
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic const struct tegra_mipi_pad tegra210_mipi_pads[] = {
46362306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
46462306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
46562306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
46662306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
46762306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
46862306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
46962306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
47062306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
47162306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
47262306a36Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
47362306a36Sopenharmony_ci};
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic const struct tegra_mipi_soc tegra210_mipi_soc = {
47662306a36Sopenharmony_ci	.has_clk_lane = true,
47762306a36Sopenharmony_ci	.pads = tegra210_mipi_pads,
47862306a36Sopenharmony_ci	.num_pads = ARRAY_SIZE(tegra210_mipi_pads),
47962306a36Sopenharmony_ci	.clock_enable_override = true,
48062306a36Sopenharmony_ci	.needs_vclamp_ref = false,
48162306a36Sopenharmony_ci	.pad_drive_down_ref = 0x0,
48262306a36Sopenharmony_ci	.pad_drive_up_ref = 0x3,
48362306a36Sopenharmony_ci	.pad_vclamp_level = 0x1,
48462306a36Sopenharmony_ci	.pad_vauxp_level = 0x1,
48562306a36Sopenharmony_ci	.hspdos = 0x0,
48662306a36Sopenharmony_ci	.hspuos = 0x2,
48762306a36Sopenharmony_ci	.termos = 0x0,
48862306a36Sopenharmony_ci	.hsclkpdos = 0x0,
48962306a36Sopenharmony_ci	.hsclkpuos = 0x2,
49062306a36Sopenharmony_ci};
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic const struct of_device_id tegra_mipi_of_match[] = {
49362306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
49462306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
49562306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
49662306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
49762306a36Sopenharmony_ci	{ },
49862306a36Sopenharmony_ci};
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic int tegra_mipi_probe(struct platform_device *pdev)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	const struct of_device_id *match;
50362306a36Sopenharmony_ci	struct tegra_mipi *mipi;
50462306a36Sopenharmony_ci	int err;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
50762306a36Sopenharmony_ci	if (!match)
50862306a36Sopenharmony_ci		return -ENODEV;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
51162306a36Sopenharmony_ci	if (!mipi)
51262306a36Sopenharmony_ci		return -ENOMEM;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	mipi->soc = match->data;
51562306a36Sopenharmony_ci	mipi->dev = &pdev->dev;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
51862306a36Sopenharmony_ci	if (IS_ERR(mipi->regs))
51962306a36Sopenharmony_ci		return PTR_ERR(mipi->regs);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	mutex_init(&mipi->lock);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	mipi->clk = devm_clk_get(&pdev->dev, NULL);
52462306a36Sopenharmony_ci	if (IS_ERR(mipi->clk)) {
52562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get clock\n");
52662306a36Sopenharmony_ci		return PTR_ERR(mipi->clk);
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	err = clk_prepare(mipi->clk);
53062306a36Sopenharmony_ci	if (err < 0)
53162306a36Sopenharmony_ci		return err;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	platform_set_drvdata(pdev, mipi);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic int tegra_mipi_remove(struct platform_device *pdev)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct tegra_mipi *mipi = platform_get_drvdata(pdev);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	clk_unprepare(mipi->clk);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return 0;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistruct platform_driver tegra_mipi_driver = {
54862306a36Sopenharmony_ci	.driver = {
54962306a36Sopenharmony_ci		.name = "tegra-mipi",
55062306a36Sopenharmony_ci		.of_match_table = tegra_mipi_of_match,
55162306a36Sopenharmony_ci	},
55262306a36Sopenharmony_ci	.probe = tegra_mipi_probe,
55362306a36Sopenharmony_ci	.remove = tegra_mipi_remove,
55462306a36Sopenharmony_ci};
555