18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2013 NVIDIA Corporation
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, distribute, and sell this software and its
58c2ecf20Sopenharmony_ci * documentation for any purpose is hereby granted without fee, provided that
68c2ecf20Sopenharmony_ci * the above copyright notice appear in all copies and that both that copyright
78c2ecf20Sopenharmony_ci * notice and this permission notice appear in supporting documentation, and
88c2ecf20Sopenharmony_ci * that the name of the copyright holders not be used in advertising or
98c2ecf20Sopenharmony_ci * publicity pertaining to distribution of the software without specific,
108c2ecf20Sopenharmony_ci * written prior permission.  The copyright holders make no representations
118c2ecf20Sopenharmony_ci * about the suitability of this software for any purpose.  It is provided "as
128c2ecf20Sopenharmony_ci * is" without express or implied warranty.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
158c2ecf20Sopenharmony_ci * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
168c2ecf20Sopenharmony_ci * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
178c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
188c2ecf20Sopenharmony_ci * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
198c2ecf20Sopenharmony_ci * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
208c2ecf20Sopenharmony_ci * OF THIS SOFTWARE.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/clk.h>
248c2ecf20Sopenharmony_ci#include <linux/host1x.h>
258c2ecf20Sopenharmony_ci#include <linux/io.h>
268c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
278c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
288c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "dev.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define MIPI_CAL_CTRL			0x00
348c2ecf20Sopenharmony_ci#define MIPI_CAL_CTRL_NOISE_FILTER(x)	(((x) & 0xf) << 26)
358c2ecf20Sopenharmony_ci#define MIPI_CAL_CTRL_PRESCALE(x)	(((x) & 0x3) << 24)
368c2ecf20Sopenharmony_ci#define MIPI_CAL_CTRL_CLKEN_OVR		(1 << 4)
378c2ecf20Sopenharmony_ci#define MIPI_CAL_CTRL_START		(1 << 0)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define MIPI_CAL_AUTOCAL_CTRL		0x01
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define MIPI_CAL_STATUS			0x02
428c2ecf20Sopenharmony_ci#define MIPI_CAL_STATUS_DONE		(1 << 16)
438c2ecf20Sopenharmony_ci#define MIPI_CAL_STATUS_ACTIVE		(1 <<  0)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIA		0x05
468c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIB		0x06
478c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIC		0x07
488c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_CSID		0x08
498c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIE		0x09
508c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIF		0x0a
518c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIA		0x0e
528c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIB		0x0f
538c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIC		0x10
548c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_DSID		0x11
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIA_CLK	0x19
578c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIB_CLK	0x1a
588c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIAB_CLK	0x1b
598c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_DSIC_CLK	0x1c
608c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_CSICD_CLK	0x1c
618c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_DSID_CLK	0x1d
628c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_CSIE_CLK	0x1d
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* for data and clock lanes */
658c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_SELECT		(1 << 21)
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* for data lanes */
688c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_HSPDOS(x)	(((x) & 0x1f) << 16)
698c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_HSPUOS(x)	(((x) & 0x1f) <<  8)
708c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_TERMOS(x)	(((x) & 0x1f) <<  0)
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* for clock lanes */
738c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_HSCLKPDOSD(x)	(((x) & 0x1f) <<  8)
748c2ecf20Sopenharmony_ci#define MIPI_CAL_CONFIG_HSCLKPUOSD(x)	(((x) & 0x1f) <<  0)
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_CFG0		0x16
778c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_PDVCLAMP	(1 << 1)
788c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF	(1 << 0)
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_CFG1		0x17
818c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
828c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_CFG2		0x18
858c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_VCLAMP(x)	(((x) & 0x7) << 16)
868c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_VAUXP(x)	(((x) & 0x7) << 4)
878c2ecf20Sopenharmony_ci#define MIPI_CAL_BIAS_PAD_PDVREG	(1 << 1)
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistruct tegra_mipi_pad {
908c2ecf20Sopenharmony_ci	unsigned long data;
918c2ecf20Sopenharmony_ci	unsigned long clk;
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistruct tegra_mipi_soc {
958c2ecf20Sopenharmony_ci	bool has_clk_lane;
968c2ecf20Sopenharmony_ci	const struct tegra_mipi_pad *pads;
978c2ecf20Sopenharmony_ci	unsigned int num_pads;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	bool clock_enable_override;
1008c2ecf20Sopenharmony_ci	bool needs_vclamp_ref;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* bias pad configuration settings */
1038c2ecf20Sopenharmony_ci	u8 pad_drive_down_ref;
1048c2ecf20Sopenharmony_ci	u8 pad_drive_up_ref;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	u8 pad_vclamp_level;
1078c2ecf20Sopenharmony_ci	u8 pad_vauxp_level;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* calibration settings for data lanes */
1108c2ecf20Sopenharmony_ci	u8 hspdos;
1118c2ecf20Sopenharmony_ci	u8 hspuos;
1128c2ecf20Sopenharmony_ci	u8 termos;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* calibration settings for clock lanes */
1158c2ecf20Sopenharmony_ci	u8 hsclkpdos;
1168c2ecf20Sopenharmony_ci	u8 hsclkpuos;
1178c2ecf20Sopenharmony_ci};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistruct tegra_mipi {
1208c2ecf20Sopenharmony_ci	const struct tegra_mipi_soc *soc;
1218c2ecf20Sopenharmony_ci	struct device *dev;
1228c2ecf20Sopenharmony_ci	void __iomem *regs;
1238c2ecf20Sopenharmony_ci	struct mutex lock;
1248c2ecf20Sopenharmony_ci	struct clk *clk;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	unsigned long usage_count;
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistruct tegra_mipi_device {
1308c2ecf20Sopenharmony_ci	struct platform_device *pdev;
1318c2ecf20Sopenharmony_ci	struct tegra_mipi *mipi;
1328c2ecf20Sopenharmony_ci	struct device *device;
1338c2ecf20Sopenharmony_ci	unsigned long pads;
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
1378c2ecf20Sopenharmony_ci				   unsigned long offset)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	return readl(mipi->regs + (offset << 2));
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
1438c2ecf20Sopenharmony_ci				     unsigned long offset)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	writel(value, mipi->regs + (offset << 2));
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int tegra_mipi_power_up(struct tegra_mipi *mipi)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	u32 value;
1518c2ecf20Sopenharmony_ci	int err;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	err = clk_enable(mipi->clk);
1548c2ecf20Sopenharmony_ci	if (err < 0)
1558c2ecf20Sopenharmony_ci		return err;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
1588c2ecf20Sopenharmony_ci	value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (mipi->soc->needs_vclamp_ref)
1618c2ecf20Sopenharmony_ci		value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
1668c2ecf20Sopenharmony_ci	value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
1678c2ecf20Sopenharmony_ci	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	clk_disable(mipi->clk);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return 0;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic int tegra_mipi_power_down(struct tegra_mipi *mipi)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	u32 value;
1778c2ecf20Sopenharmony_ci	int err;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	err = clk_enable(mipi->clk);
1808c2ecf20Sopenharmony_ci	if (err < 0)
1818c2ecf20Sopenharmony_ci		return err;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/*
1848c2ecf20Sopenharmony_ci	 * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
1858c2ecf20Sopenharmony_ci	 * supplies the DSI pads. This must be kept enabled until none of the
1868c2ecf20Sopenharmony_ci	 * DSI lanes are used anymore.
1878c2ecf20Sopenharmony_ci	 */
1888c2ecf20Sopenharmony_ci	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
1898c2ecf20Sopenharmony_ci	value |= MIPI_CAL_BIAS_PAD_PDVREG;
1908c2ecf20Sopenharmony_ci	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/*
1938c2ecf20Sopenharmony_ci	 * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
1948c2ecf20Sopenharmony_ci	 * control a regulator that supplies current to the pre-driver logic.
1958c2ecf20Sopenharmony_ci	 * Powering down this regulator causes DSI to fail, so it must remain
1968c2ecf20Sopenharmony_ci	 * powered on until none of the DSI lanes are used anymore.
1978c2ecf20Sopenharmony_ci	 */
1988c2ecf20Sopenharmony_ci	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (mipi->soc->needs_vclamp_ref)
2018c2ecf20Sopenharmony_ci		value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
2048c2ecf20Sopenharmony_ci	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistruct tegra_mipi_device *tegra_mipi_request(struct device *device,
2108c2ecf20Sopenharmony_ci					     struct device_node *np)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct tegra_mipi_device *dev;
2138c2ecf20Sopenharmony_ci	struct of_phandle_args args;
2148c2ecf20Sopenharmony_ci	int err;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	err = of_parse_phandle_with_args(np, "nvidia,mipi-calibrate",
2178c2ecf20Sopenharmony_ci					 "#nvidia,mipi-calibrate-cells", 0,
2188c2ecf20Sopenharmony_ci					 &args);
2198c2ecf20Sopenharmony_ci	if (err < 0)
2208c2ecf20Sopenharmony_ci		return ERR_PTR(err);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
2238c2ecf20Sopenharmony_ci	if (!dev) {
2248c2ecf20Sopenharmony_ci		err = -ENOMEM;
2258c2ecf20Sopenharmony_ci		goto out;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	dev->pdev = of_find_device_by_node(args.np);
2298c2ecf20Sopenharmony_ci	if (!dev->pdev) {
2308c2ecf20Sopenharmony_ci		err = -ENODEV;
2318c2ecf20Sopenharmony_ci		goto free;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	dev->mipi = platform_get_drvdata(dev->pdev);
2358c2ecf20Sopenharmony_ci	if (!dev->mipi) {
2368c2ecf20Sopenharmony_ci		err = -EPROBE_DEFER;
2378c2ecf20Sopenharmony_ci		goto put;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	of_node_put(args.np);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	dev->pads = args.args[0];
2438c2ecf20Sopenharmony_ci	dev->device = device;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return dev;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ciput:
2488c2ecf20Sopenharmony_ci	platform_device_put(dev->pdev);
2498c2ecf20Sopenharmony_cifree:
2508c2ecf20Sopenharmony_ci	kfree(dev);
2518c2ecf20Sopenharmony_ciout:
2528c2ecf20Sopenharmony_ci	of_node_put(args.np);
2538c2ecf20Sopenharmony_ci	return ERR_PTR(err);
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_request);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_civoid tegra_mipi_free(struct tegra_mipi_device *device)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	platform_device_put(device->pdev);
2608c2ecf20Sopenharmony_ci	kfree(device);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_free);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ciint tegra_mipi_enable(struct tegra_mipi_device *dev)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	int err = 0;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	mutex_lock(&dev->mipi->lock);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (dev->mipi->usage_count++ == 0)
2718c2ecf20Sopenharmony_ci		err = tegra_mipi_power_up(dev->mipi);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	mutex_unlock(&dev->mipi->lock);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return err;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_enable);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ciint tegra_mipi_disable(struct tegra_mipi_device *dev)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	int err = 0;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	mutex_lock(&dev->mipi->lock);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (--dev->mipi->usage_count == 0)
2878c2ecf20Sopenharmony_ci		err = tegra_mipi_power_down(dev->mipi);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	mutex_unlock(&dev->mipi->lock);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return err;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_disable);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ciint tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct tegra_mipi *mipi = device->mipi;
2998c2ecf20Sopenharmony_ci	void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
3008c2ecf20Sopenharmony_ci	u32 value;
3018c2ecf20Sopenharmony_ci	int err;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	err = readl_relaxed_poll_timeout(status_reg, value,
3048c2ecf20Sopenharmony_ci					 !(value & MIPI_CAL_STATUS_ACTIVE) &&
3058c2ecf20Sopenharmony_ci					 (value & MIPI_CAL_STATUS_DONE), 50,
3068c2ecf20Sopenharmony_ci					 250000);
3078c2ecf20Sopenharmony_ci	mutex_unlock(&device->mipi->lock);
3088c2ecf20Sopenharmony_ci	clk_disable(device->mipi->clk);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return err;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_finish_calibration);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ciint tegra_mipi_start_calibration(struct tegra_mipi_device *device)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	const struct tegra_mipi_soc *soc = device->mipi->soc;
3178c2ecf20Sopenharmony_ci	unsigned int i;
3188c2ecf20Sopenharmony_ci	u32 value;
3198c2ecf20Sopenharmony_ci	int err;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	err = clk_enable(device->mipi->clk);
3228c2ecf20Sopenharmony_ci	if (err < 0)
3238c2ecf20Sopenharmony_ci		return err;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	mutex_lock(&device->mipi->lock);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
3288c2ecf20Sopenharmony_ci		MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
3298c2ecf20Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
3328c2ecf20Sopenharmony_ci	value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
3338c2ecf20Sopenharmony_ci	value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
3348c2ecf20Sopenharmony_ci	value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
3358c2ecf20Sopenharmony_ci	value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
3368c2ecf20Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	for (i = 0; i < soc->num_pads; i++) {
3398c2ecf20Sopenharmony_ci		u32 clk = 0, data = 0;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		if (device->pads & BIT(i)) {
3428c2ecf20Sopenharmony_ci			data = MIPI_CAL_CONFIG_SELECT |
3438c2ecf20Sopenharmony_ci			       MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
3448c2ecf20Sopenharmony_ci			       MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
3458c2ecf20Sopenharmony_ci			       MIPI_CAL_CONFIG_TERMOS(soc->termos);
3468c2ecf20Sopenharmony_ci			clk = MIPI_CAL_CONFIG_SELECT |
3478c2ecf20Sopenharmony_ci			      MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
3488c2ecf20Sopenharmony_ci			      MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
3498c2ecf20Sopenharmony_ci		}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		if (soc->has_clk_lane && soc->pads[i].clk != 0)
3548c2ecf20Sopenharmony_ci			tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
3588c2ecf20Sopenharmony_ci	value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
3598c2ecf20Sopenharmony_ci	value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
3608c2ecf20Sopenharmony_ci	value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
3618c2ecf20Sopenharmony_ci	value |= MIPI_CAL_CTRL_PRESCALE(0x2);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (!soc->clock_enable_override)
3648c2ecf20Sopenharmony_ci		value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
3658c2ecf20Sopenharmony_ci	else
3668c2ecf20Sopenharmony_ci		value |= MIPI_CAL_CTRL_CLKEN_OVR;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* clear any pending status bits */
3718c2ecf20Sopenharmony_ci	value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS);
3728c2ecf20Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
3758c2ecf20Sopenharmony_ci	value |= MIPI_CAL_CTRL_START;
3768c2ecf20Sopenharmony_ci	tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/*
3798c2ecf20Sopenharmony_ci	 * Wait for min 72uS to let calibration logic finish calibration
3808c2ecf20Sopenharmony_ci	 * sequence codes before waiting for pads idle state to apply the
3818c2ecf20Sopenharmony_ci	 * results.
3828c2ecf20Sopenharmony_ci	 */
3838c2ecf20Sopenharmony_ci	usleep_range(75, 80);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tegra_mipi_start_calibration);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic const struct tegra_mipi_pad tegra114_mipi_pads[] = {
3908c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIA },
3918c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIB },
3928c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIC },
3938c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSID },
3948c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIE },
3958c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIA },
3968c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIB },
3978c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIC },
3988c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSID },
3998c2ecf20Sopenharmony_ci};
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic const struct tegra_mipi_soc tegra114_mipi_soc = {
4028c2ecf20Sopenharmony_ci	.has_clk_lane = false,
4038c2ecf20Sopenharmony_ci	.pads = tegra114_mipi_pads,
4048c2ecf20Sopenharmony_ci	.num_pads = ARRAY_SIZE(tegra114_mipi_pads),
4058c2ecf20Sopenharmony_ci	.clock_enable_override = true,
4068c2ecf20Sopenharmony_ci	.needs_vclamp_ref = true,
4078c2ecf20Sopenharmony_ci	.pad_drive_down_ref = 0x2,
4088c2ecf20Sopenharmony_ci	.pad_drive_up_ref = 0x0,
4098c2ecf20Sopenharmony_ci	.pad_vclamp_level = 0x0,
4108c2ecf20Sopenharmony_ci	.pad_vauxp_level = 0x0,
4118c2ecf20Sopenharmony_ci	.hspdos = 0x0,
4128c2ecf20Sopenharmony_ci	.hspuos = 0x4,
4138c2ecf20Sopenharmony_ci	.termos = 0x5,
4148c2ecf20Sopenharmony_ci	.hsclkpdos = 0x0,
4158c2ecf20Sopenharmony_ci	.hsclkpuos = 0x4,
4168c2ecf20Sopenharmony_ci};
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic const struct tegra_mipi_pad tegra124_mipi_pads[] = {
4198c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
4208c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
4218c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
4228c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
4238c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK  },
4248c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK  },
4258c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK  },
4268c2ecf20Sopenharmony_ci};
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic const struct tegra_mipi_soc tegra124_mipi_soc = {
4298c2ecf20Sopenharmony_ci	.has_clk_lane = true,
4308c2ecf20Sopenharmony_ci	.pads = tegra124_mipi_pads,
4318c2ecf20Sopenharmony_ci	.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
4328c2ecf20Sopenharmony_ci	.clock_enable_override = true,
4338c2ecf20Sopenharmony_ci	.needs_vclamp_ref = true,
4348c2ecf20Sopenharmony_ci	.pad_drive_down_ref = 0x2,
4358c2ecf20Sopenharmony_ci	.pad_drive_up_ref = 0x0,
4368c2ecf20Sopenharmony_ci	.pad_vclamp_level = 0x0,
4378c2ecf20Sopenharmony_ci	.pad_vauxp_level = 0x0,
4388c2ecf20Sopenharmony_ci	.hspdos = 0x0,
4398c2ecf20Sopenharmony_ci	.hspuos = 0x0,
4408c2ecf20Sopenharmony_ci	.termos = 0x0,
4418c2ecf20Sopenharmony_ci	.hsclkpdos = 0x1,
4428c2ecf20Sopenharmony_ci	.hsclkpuos = 0x2,
4438c2ecf20Sopenharmony_ci};
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic const struct tegra_mipi_soc tegra132_mipi_soc = {
4468c2ecf20Sopenharmony_ci	.has_clk_lane = true,
4478c2ecf20Sopenharmony_ci	.pads = tegra124_mipi_pads,
4488c2ecf20Sopenharmony_ci	.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
4498c2ecf20Sopenharmony_ci	.clock_enable_override = false,
4508c2ecf20Sopenharmony_ci	.needs_vclamp_ref = false,
4518c2ecf20Sopenharmony_ci	.pad_drive_down_ref = 0x0,
4528c2ecf20Sopenharmony_ci	.pad_drive_up_ref = 0x3,
4538c2ecf20Sopenharmony_ci	.pad_vclamp_level = 0x0,
4548c2ecf20Sopenharmony_ci	.pad_vauxp_level = 0x0,
4558c2ecf20Sopenharmony_ci	.hspdos = 0x0,
4568c2ecf20Sopenharmony_ci	.hspuos = 0x0,
4578c2ecf20Sopenharmony_ci	.termos = 0x0,
4588c2ecf20Sopenharmony_ci	.hsclkpdos = 0x3,
4598c2ecf20Sopenharmony_ci	.hsclkpuos = 0x2,
4608c2ecf20Sopenharmony_ci};
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic const struct tegra_mipi_pad tegra210_mipi_pads[] = {
4638c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
4648c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
4658c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
4668c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
4678c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
4688c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
4698c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
4708c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
4718c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
4728c2ecf20Sopenharmony_ci	{ .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
4738c2ecf20Sopenharmony_ci};
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic const struct tegra_mipi_soc tegra210_mipi_soc = {
4768c2ecf20Sopenharmony_ci	.has_clk_lane = true,
4778c2ecf20Sopenharmony_ci	.pads = tegra210_mipi_pads,
4788c2ecf20Sopenharmony_ci	.num_pads = ARRAY_SIZE(tegra210_mipi_pads),
4798c2ecf20Sopenharmony_ci	.clock_enable_override = true,
4808c2ecf20Sopenharmony_ci	.needs_vclamp_ref = false,
4818c2ecf20Sopenharmony_ci	.pad_drive_down_ref = 0x0,
4828c2ecf20Sopenharmony_ci	.pad_drive_up_ref = 0x3,
4838c2ecf20Sopenharmony_ci	.pad_vclamp_level = 0x1,
4848c2ecf20Sopenharmony_ci	.pad_vauxp_level = 0x1,
4858c2ecf20Sopenharmony_ci	.hspdos = 0x0,
4868c2ecf20Sopenharmony_ci	.hspuos = 0x2,
4878c2ecf20Sopenharmony_ci	.termos = 0x0,
4888c2ecf20Sopenharmony_ci	.hsclkpdos = 0x0,
4898c2ecf20Sopenharmony_ci	.hsclkpuos = 0x2,
4908c2ecf20Sopenharmony_ci};
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_mipi_of_match[] = {
4938c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
4948c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
4958c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
4968c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
4978c2ecf20Sopenharmony_ci	{ },
4988c2ecf20Sopenharmony_ci};
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int tegra_mipi_probe(struct platform_device *pdev)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	const struct of_device_id *match;
5038c2ecf20Sopenharmony_ci	struct tegra_mipi *mipi;
5048c2ecf20Sopenharmony_ci	struct resource *res;
5058c2ecf20Sopenharmony_ci	int err;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
5088c2ecf20Sopenharmony_ci	if (!match)
5098c2ecf20Sopenharmony_ci		return -ENODEV;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
5128c2ecf20Sopenharmony_ci	if (!mipi)
5138c2ecf20Sopenharmony_ci		return -ENOMEM;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	mipi->soc = match->data;
5168c2ecf20Sopenharmony_ci	mipi->dev = &pdev->dev;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5198c2ecf20Sopenharmony_ci	mipi->regs = devm_ioremap_resource(&pdev->dev, res);
5208c2ecf20Sopenharmony_ci	if (IS_ERR(mipi->regs))
5218c2ecf20Sopenharmony_ci		return PTR_ERR(mipi->regs);
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	mutex_init(&mipi->lock);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	mipi->clk = devm_clk_get(&pdev->dev, NULL);
5268c2ecf20Sopenharmony_ci	if (IS_ERR(mipi->clk)) {
5278c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to get clock\n");
5288c2ecf20Sopenharmony_ci		return PTR_ERR(mipi->clk);
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	err = clk_prepare(mipi->clk);
5328c2ecf20Sopenharmony_ci	if (err < 0)
5338c2ecf20Sopenharmony_ci		return err;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, mipi);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	return 0;
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic int tegra_mipi_remove(struct platform_device *pdev)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct tegra_mipi *mipi = platform_get_drvdata(pdev);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	clk_unprepare(mipi->clk);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	return 0;
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistruct platform_driver tegra_mipi_driver = {
5508c2ecf20Sopenharmony_ci	.driver = {
5518c2ecf20Sopenharmony_ci		.name = "tegra-mipi",
5528c2ecf20Sopenharmony_ci		.of_match_table = tegra_mipi_of_match,
5538c2ecf20Sopenharmony_ci	},
5548c2ecf20Sopenharmony_ci	.probe = tegra_mipi_probe,
5558c2ecf20Sopenharmony_ci	.remove = tegra_mipi_remove,
5568c2ecf20Sopenharmony_ci};
557