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