1*c888c4c8SSvyatoslav Ryhel /* 2*c888c4c8SSvyatoslav Ryhel * Copyright (C) 2013 NVIDIA Corporation 3*c888c4c8SSvyatoslav Ryhel * 4*c888c4c8SSvyatoslav Ryhel * Permission to use, copy, modify, distribute, and sell this software and its 5*c888c4c8SSvyatoslav Ryhel * documentation for any purpose is hereby granted without fee, provided that 6*c888c4c8SSvyatoslav Ryhel * the above copyright notice appear in all copies and that both that copyright 7*c888c4c8SSvyatoslav Ryhel * notice and this permission notice appear in supporting documentation, and 8*c888c4c8SSvyatoslav Ryhel * that the name of the copyright holders not be used in advertising or 9*c888c4c8SSvyatoslav Ryhel * publicity pertaining to distribution of the software without specific, 10*c888c4c8SSvyatoslav Ryhel * written prior permission. The copyright holders make no representations 11*c888c4c8SSvyatoslav Ryhel * about the suitability of this software for any purpose. It is provided "as 12*c888c4c8SSvyatoslav Ryhel * is" without express or implied warranty. 13*c888c4c8SSvyatoslav Ryhel * 14*c888c4c8SSvyatoslav Ryhel * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15*c888c4c8SSvyatoslav Ryhel * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16*c888c4c8SSvyatoslav Ryhel * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17*c888c4c8SSvyatoslav Ryhel * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18*c888c4c8SSvyatoslav Ryhel * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19*c888c4c8SSvyatoslav Ryhel * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 20*c888c4c8SSvyatoslav Ryhel * OF THIS SOFTWARE. 21*c888c4c8SSvyatoslav Ryhel */ 22*c888c4c8SSvyatoslav Ryhel 23*c888c4c8SSvyatoslav Ryhel #include <linux/clk.h> 24*c888c4c8SSvyatoslav Ryhel #include <linux/host1x.h> 25*c888c4c8SSvyatoslav Ryhel #include <linux/io.h> 26*c888c4c8SSvyatoslav Ryhel #include <linux/iopoll.h> 27*c888c4c8SSvyatoslav Ryhel #include <linux/of_platform.h> 28*c888c4c8SSvyatoslav Ryhel #include <linux/platform_device.h> 29*c888c4c8SSvyatoslav Ryhel #include <linux/slab.h> 30*c888c4c8SSvyatoslav Ryhel #include <linux/tegra-mipi-cal.h> 31*c888c4c8SSvyatoslav Ryhel 32*c888c4c8SSvyatoslav Ryhel #include "dev.h" 33*c888c4c8SSvyatoslav Ryhel 34*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL 0x00 35*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26) 36*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24) 37*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL_CLKEN_OVR BIT(4) 38*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL_START BIT(0) 39*c888c4c8SSvyatoslav Ryhel 40*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_AUTOCAL_CTRL 0x01 41*c888c4c8SSvyatoslav Ryhel 42*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_STATUS 0x02 43*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_STATUS_DONE BIT(16) 44*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_STATUS_ACTIVE BIT(0) 45*c888c4c8SSvyatoslav Ryhel 46*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIA 0x05 47*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIB 0x06 48*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIC 0x07 49*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSID 0x08 50*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIE 0x09 51*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIF 0x0a 52*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIA 0x0e 53*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIB 0x0f 54*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIC 0x10 55*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSID 0x11 56*c888c4c8SSvyatoslav Ryhel 57*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIA_CLK 0x19 58*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIB_CLK 0x1a 59*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b 60*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIC_CLK 0x1c 61*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSICD_CLK 0x1c 62*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSID_CLK 0x1d 63*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIE_CLK 0x1d 64*c888c4c8SSvyatoslav Ryhel 65*c888c4c8SSvyatoslav Ryhel /* for data and clock lanes */ 66*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_SELECT BIT(21) 67*c888c4c8SSvyatoslav Ryhel 68*c888c4c8SSvyatoslav Ryhel /* for data lanes */ 69*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16) 70*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8) 71*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0) 72*c888c4c8SSvyatoslav Ryhel 73*c888c4c8SSvyatoslav Ryhel /* for clock lanes */ 74*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8) 75*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0) 76*c888c4c8SSvyatoslav Ryhel 77*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_CFG0 0x16 78*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1) 79*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0) 80*c888c4c8SSvyatoslav Ryhel 81*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_CFG1 0x17 82*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16) 83*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8) 84*c888c4c8SSvyatoslav Ryhel 85*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_CFG2 0x18 86*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16) 87*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4) 88*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_PDVREG BIT(1) 89*c888c4c8SSvyatoslav Ryhel 90*c888c4c8SSvyatoslav Ryhel struct tegra_mipi_pad { 91*c888c4c8SSvyatoslav Ryhel unsigned long data; 92*c888c4c8SSvyatoslav Ryhel unsigned long clk; 93*c888c4c8SSvyatoslav Ryhel }; 94*c888c4c8SSvyatoslav Ryhel 95*c888c4c8SSvyatoslav Ryhel struct tegra_mipi_soc { 96*c888c4c8SSvyatoslav Ryhel bool has_clk_lane; 97*c888c4c8SSvyatoslav Ryhel const struct tegra_mipi_pad *pads; 98*c888c4c8SSvyatoslav Ryhel unsigned int num_pads; 99*c888c4c8SSvyatoslav Ryhel 100*c888c4c8SSvyatoslav Ryhel bool clock_enable_override; 101*c888c4c8SSvyatoslav Ryhel bool needs_vclamp_ref; 102*c888c4c8SSvyatoslav Ryhel 103*c888c4c8SSvyatoslav Ryhel /* bias pad configuration settings */ 104*c888c4c8SSvyatoslav Ryhel u8 pad_drive_down_ref; 105*c888c4c8SSvyatoslav Ryhel u8 pad_drive_up_ref; 106*c888c4c8SSvyatoslav Ryhel 107*c888c4c8SSvyatoslav Ryhel u8 pad_vclamp_level; 108*c888c4c8SSvyatoslav Ryhel u8 pad_vauxp_level; 109*c888c4c8SSvyatoslav Ryhel 110*c888c4c8SSvyatoslav Ryhel /* calibration settings for data lanes */ 111*c888c4c8SSvyatoslav Ryhel u8 hspdos; 112*c888c4c8SSvyatoslav Ryhel u8 hspuos; 113*c888c4c8SSvyatoslav Ryhel u8 termos; 114*c888c4c8SSvyatoslav Ryhel 115*c888c4c8SSvyatoslav Ryhel /* calibration settings for clock lanes */ 116*c888c4c8SSvyatoslav Ryhel u8 hsclkpdos; 117*c888c4c8SSvyatoslav Ryhel u8 hsclkpuos; 118*c888c4c8SSvyatoslav Ryhel }; 119*c888c4c8SSvyatoslav Ryhel 120*c888c4c8SSvyatoslav Ryhel struct tegra_mipi { 121*c888c4c8SSvyatoslav Ryhel const struct tegra_mipi_soc *soc; 122*c888c4c8SSvyatoslav Ryhel struct device *dev; 123*c888c4c8SSvyatoslav Ryhel void __iomem *regs; 124*c888c4c8SSvyatoslav Ryhel struct mutex lock; /* for register access */ 125*c888c4c8SSvyatoslav Ryhel struct clk *clk; 126*c888c4c8SSvyatoslav Ryhel 127*c888c4c8SSvyatoslav Ryhel unsigned long usage_count; 128*c888c4c8SSvyatoslav Ryhel }; 129*c888c4c8SSvyatoslav Ryhel 130*c888c4c8SSvyatoslav Ryhel static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi, 131*c888c4c8SSvyatoslav Ryhel unsigned long offset) 132*c888c4c8SSvyatoslav Ryhel { 133*c888c4c8SSvyatoslav Ryhel return readl(mipi->regs + (offset << 2)); 134*c888c4c8SSvyatoslav Ryhel } 135*c888c4c8SSvyatoslav Ryhel 136*c888c4c8SSvyatoslav Ryhel static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value, 137*c888c4c8SSvyatoslav Ryhel unsigned long offset) 138*c888c4c8SSvyatoslav Ryhel { 139*c888c4c8SSvyatoslav Ryhel writel(value, mipi->regs + (offset << 2)); 140*c888c4c8SSvyatoslav Ryhel } 141*c888c4c8SSvyatoslav Ryhel 142*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_power_up(struct tegra_mipi *mipi) 143*c888c4c8SSvyatoslav Ryhel { 144*c888c4c8SSvyatoslav Ryhel u32 value; 145*c888c4c8SSvyatoslav Ryhel int err; 146*c888c4c8SSvyatoslav Ryhel 147*c888c4c8SSvyatoslav Ryhel err = clk_enable(mipi->clk); 148*c888c4c8SSvyatoslav Ryhel if (err < 0) 149*c888c4c8SSvyatoslav Ryhel return err; 150*c888c4c8SSvyatoslav Ryhel 151*c888c4c8SSvyatoslav Ryhel value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0); 152*c888c4c8SSvyatoslav Ryhel value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; 153*c888c4c8SSvyatoslav Ryhel 154*c888c4c8SSvyatoslav Ryhel if (mipi->soc->needs_vclamp_ref) 155*c888c4c8SSvyatoslav Ryhel value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; 156*c888c4c8SSvyatoslav Ryhel 157*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0); 158*c888c4c8SSvyatoslav Ryhel 159*c888c4c8SSvyatoslav Ryhel value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2); 160*c888c4c8SSvyatoslav Ryhel value &= ~MIPI_CAL_BIAS_PAD_PDVREG; 161*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2); 162*c888c4c8SSvyatoslav Ryhel 163*c888c4c8SSvyatoslav Ryhel clk_disable(mipi->clk); 164*c888c4c8SSvyatoslav Ryhel 165*c888c4c8SSvyatoslav Ryhel return 0; 166*c888c4c8SSvyatoslav Ryhel } 167*c888c4c8SSvyatoslav Ryhel 168*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_power_down(struct tegra_mipi *mipi) 169*c888c4c8SSvyatoslav Ryhel { 170*c888c4c8SSvyatoslav Ryhel u32 value; 171*c888c4c8SSvyatoslav Ryhel int err; 172*c888c4c8SSvyatoslav Ryhel 173*c888c4c8SSvyatoslav Ryhel err = clk_enable(mipi->clk); 174*c888c4c8SSvyatoslav Ryhel if (err < 0) 175*c888c4c8SSvyatoslav Ryhel return err; 176*c888c4c8SSvyatoslav Ryhel 177*c888c4c8SSvyatoslav Ryhel /* 178*c888c4c8SSvyatoslav Ryhel * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that 179*c888c4c8SSvyatoslav Ryhel * supplies the DSI pads. This must be kept enabled until none of the 180*c888c4c8SSvyatoslav Ryhel * DSI lanes are used anymore. 181*c888c4c8SSvyatoslav Ryhel */ 182*c888c4c8SSvyatoslav Ryhel value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2); 183*c888c4c8SSvyatoslav Ryhel value |= MIPI_CAL_BIAS_PAD_PDVREG; 184*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2); 185*c888c4c8SSvyatoslav Ryhel 186*c888c4c8SSvyatoslav Ryhel /* 187*c888c4c8SSvyatoslav Ryhel * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF 188*c888c4c8SSvyatoslav Ryhel * control a regulator that supplies current to the pre-driver logic. 189*c888c4c8SSvyatoslav Ryhel * Powering down this regulator causes DSI to fail, so it must remain 190*c888c4c8SSvyatoslav Ryhel * powered on until none of the DSI lanes are used anymore. 191*c888c4c8SSvyatoslav Ryhel */ 192*c888c4c8SSvyatoslav Ryhel value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0); 193*c888c4c8SSvyatoslav Ryhel 194*c888c4c8SSvyatoslav Ryhel if (mipi->soc->needs_vclamp_ref) 195*c888c4c8SSvyatoslav Ryhel value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; 196*c888c4c8SSvyatoslav Ryhel 197*c888c4c8SSvyatoslav Ryhel value |= MIPI_CAL_BIAS_PAD_PDVCLAMP; 198*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0); 199*c888c4c8SSvyatoslav Ryhel 200*c888c4c8SSvyatoslav Ryhel return 0; 201*c888c4c8SSvyatoslav Ryhel } 202*c888c4c8SSvyatoslav Ryhel 203*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_enable(struct tegra_mipi_device *mipidev) 204*c888c4c8SSvyatoslav Ryhel { 205*c888c4c8SSvyatoslav Ryhel struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev); 206*c888c4c8SSvyatoslav Ryhel int err = 0; 207*c888c4c8SSvyatoslav Ryhel 208*c888c4c8SSvyatoslav Ryhel mutex_lock(&mipi->lock); 209*c888c4c8SSvyatoslav Ryhel 210*c888c4c8SSvyatoslav Ryhel if (mipi->usage_count++ == 0) 211*c888c4c8SSvyatoslav Ryhel err = tegra114_mipi_power_up(mipi); 212*c888c4c8SSvyatoslav Ryhel 213*c888c4c8SSvyatoslav Ryhel mutex_unlock(&mipi->lock); 214*c888c4c8SSvyatoslav Ryhel 215*c888c4c8SSvyatoslav Ryhel return err; 216*c888c4c8SSvyatoslav Ryhel } 217*c888c4c8SSvyatoslav Ryhel 218*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_disable(struct tegra_mipi_device *mipidev) 219*c888c4c8SSvyatoslav Ryhel { 220*c888c4c8SSvyatoslav Ryhel struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev); 221*c888c4c8SSvyatoslav Ryhel int err = 0; 222*c888c4c8SSvyatoslav Ryhel 223*c888c4c8SSvyatoslav Ryhel mutex_lock(&mipi->lock); 224*c888c4c8SSvyatoslav Ryhel 225*c888c4c8SSvyatoslav Ryhel if (--mipi->usage_count == 0) 226*c888c4c8SSvyatoslav Ryhel err = tegra114_mipi_power_down(mipi); 227*c888c4c8SSvyatoslav Ryhel 228*c888c4c8SSvyatoslav Ryhel mutex_unlock(&mipi->lock); 229*c888c4c8SSvyatoslav Ryhel 230*c888c4c8SSvyatoslav Ryhel return err; 231*c888c4c8SSvyatoslav Ryhel } 232*c888c4c8SSvyatoslav Ryhel 233*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_finish_calibration(struct tegra_mipi_device *mipidev) 234*c888c4c8SSvyatoslav Ryhel { 235*c888c4c8SSvyatoslav Ryhel struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev); 236*c888c4c8SSvyatoslav Ryhel void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2); 237*c888c4c8SSvyatoslav Ryhel u32 value; 238*c888c4c8SSvyatoslav Ryhel int err; 239*c888c4c8SSvyatoslav Ryhel 240*c888c4c8SSvyatoslav Ryhel err = readl_relaxed_poll_timeout(status_reg, value, 241*c888c4c8SSvyatoslav Ryhel !(value & MIPI_CAL_STATUS_ACTIVE) && 242*c888c4c8SSvyatoslav Ryhel (value & MIPI_CAL_STATUS_DONE), 50, 243*c888c4c8SSvyatoslav Ryhel 250000); 244*c888c4c8SSvyatoslav Ryhel mutex_unlock(&mipi->lock); 245*c888c4c8SSvyatoslav Ryhel clk_disable(mipi->clk); 246*c888c4c8SSvyatoslav Ryhel 247*c888c4c8SSvyatoslav Ryhel return err; 248*c888c4c8SSvyatoslav Ryhel } 249*c888c4c8SSvyatoslav Ryhel 250*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_start_calibration(struct tegra_mipi_device *mipidev) 251*c888c4c8SSvyatoslav Ryhel { 252*c888c4c8SSvyatoslav Ryhel struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev); 253*c888c4c8SSvyatoslav Ryhel const struct tegra_mipi_soc *soc = mipi->soc; 254*c888c4c8SSvyatoslav Ryhel unsigned int i; 255*c888c4c8SSvyatoslav Ryhel u32 value; 256*c888c4c8SSvyatoslav Ryhel int err; 257*c888c4c8SSvyatoslav Ryhel 258*c888c4c8SSvyatoslav Ryhel err = clk_enable(mipi->clk); 259*c888c4c8SSvyatoslav Ryhel if (err < 0) 260*c888c4c8SSvyatoslav Ryhel return err; 261*c888c4c8SSvyatoslav Ryhel 262*c888c4c8SSvyatoslav Ryhel mutex_lock(&mipi->lock); 263*c888c4c8SSvyatoslav Ryhel 264*c888c4c8SSvyatoslav Ryhel value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) | 265*c888c4c8SSvyatoslav Ryhel MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref); 266*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG1); 267*c888c4c8SSvyatoslav Ryhel 268*c888c4c8SSvyatoslav Ryhel value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2); 269*c888c4c8SSvyatoslav Ryhel value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); 270*c888c4c8SSvyatoslav Ryhel value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); 271*c888c4c8SSvyatoslav Ryhel value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level); 272*c888c4c8SSvyatoslav Ryhel value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level); 273*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2); 274*c888c4c8SSvyatoslav Ryhel 275*c888c4c8SSvyatoslav Ryhel for (i = 0; i < soc->num_pads; i++) { 276*c888c4c8SSvyatoslav Ryhel u32 clk = 0, data = 0; 277*c888c4c8SSvyatoslav Ryhel 278*c888c4c8SSvyatoslav Ryhel if (mipidev->pads & BIT(i)) { 279*c888c4c8SSvyatoslav Ryhel data = MIPI_CAL_CONFIG_SELECT | 280*c888c4c8SSvyatoslav Ryhel MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) | 281*c888c4c8SSvyatoslav Ryhel MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) | 282*c888c4c8SSvyatoslav Ryhel MIPI_CAL_CONFIG_TERMOS(soc->termos); 283*c888c4c8SSvyatoslav Ryhel clk = MIPI_CAL_CONFIG_SELECT | 284*c888c4c8SSvyatoslav Ryhel MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) | 285*c888c4c8SSvyatoslav Ryhel MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos); 286*c888c4c8SSvyatoslav Ryhel } 287*c888c4c8SSvyatoslav Ryhel 288*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, data, soc->pads[i].data); 289*c888c4c8SSvyatoslav Ryhel 290*c888c4c8SSvyatoslav Ryhel if (soc->has_clk_lane && soc->pads[i].clk != 0) 291*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, clk, soc->pads[i].clk); 292*c888c4c8SSvyatoslav Ryhel } 293*c888c4c8SSvyatoslav Ryhel 294*c888c4c8SSvyatoslav Ryhel value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL); 295*c888c4c8SSvyatoslav Ryhel value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf); 296*c888c4c8SSvyatoslav Ryhel value &= ~MIPI_CAL_CTRL_PRESCALE(0x3); 297*c888c4c8SSvyatoslav Ryhel value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa); 298*c888c4c8SSvyatoslav Ryhel value |= MIPI_CAL_CTRL_PRESCALE(0x2); 299*c888c4c8SSvyatoslav Ryhel 300*c888c4c8SSvyatoslav Ryhel if (!soc->clock_enable_override) 301*c888c4c8SSvyatoslav Ryhel value &= ~MIPI_CAL_CTRL_CLKEN_OVR; 302*c888c4c8SSvyatoslav Ryhel else 303*c888c4c8SSvyatoslav Ryhel value |= MIPI_CAL_CTRL_CLKEN_OVR; 304*c888c4c8SSvyatoslav Ryhel 305*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL); 306*c888c4c8SSvyatoslav Ryhel 307*c888c4c8SSvyatoslav Ryhel /* clear any pending status bits */ 308*c888c4c8SSvyatoslav Ryhel value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS); 309*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, value, MIPI_CAL_STATUS); 310*c888c4c8SSvyatoslav Ryhel 311*c888c4c8SSvyatoslav Ryhel value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL); 312*c888c4c8SSvyatoslav Ryhel value |= MIPI_CAL_CTRL_START; 313*c888c4c8SSvyatoslav Ryhel tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL); 314*c888c4c8SSvyatoslav Ryhel 315*c888c4c8SSvyatoslav Ryhel /* 316*c888c4c8SSvyatoslav Ryhel * Wait for min 72uS to let calibration logic finish calibration 317*c888c4c8SSvyatoslav Ryhel * sequence codes before waiting for pads idle state to apply the 318*c888c4c8SSvyatoslav Ryhel * results. 319*c888c4c8SSvyatoslav Ryhel */ 320*c888c4c8SSvyatoslav Ryhel usleep_range(75, 80); 321*c888c4c8SSvyatoslav Ryhel 322*c888c4c8SSvyatoslav Ryhel return 0; 323*c888c4c8SSvyatoslav Ryhel } 324*c888c4c8SSvyatoslav Ryhel 325*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_ops tegra114_mipi_ops = { 326*c888c4c8SSvyatoslav Ryhel .enable = tegra114_mipi_enable, 327*c888c4c8SSvyatoslav Ryhel .disable = tegra114_mipi_disable, 328*c888c4c8SSvyatoslav Ryhel .start_calibration = tegra114_mipi_start_calibration, 329*c888c4c8SSvyatoslav Ryhel .finish_calibration = tegra114_mipi_finish_calibration, 330*c888c4c8SSvyatoslav Ryhel }; 331*c888c4c8SSvyatoslav Ryhel 332*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_pad tegra114_mipi_pads[] = { 333*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIA }, 334*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIB }, 335*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIC }, 336*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSID }, 337*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIE }, 338*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSIA }, 339*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSIB }, 340*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSIC }, 341*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSID }, 342*c888c4c8SSvyatoslav Ryhel }; 343*c888c4c8SSvyatoslav Ryhel 344*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_soc tegra114_mipi_soc = { 345*c888c4c8SSvyatoslav Ryhel .has_clk_lane = false, 346*c888c4c8SSvyatoslav Ryhel .pads = tegra114_mipi_pads, 347*c888c4c8SSvyatoslav Ryhel .num_pads = ARRAY_SIZE(tegra114_mipi_pads), 348*c888c4c8SSvyatoslav Ryhel .clock_enable_override = true, 349*c888c4c8SSvyatoslav Ryhel .needs_vclamp_ref = true, 350*c888c4c8SSvyatoslav Ryhel .pad_drive_down_ref = 0x2, 351*c888c4c8SSvyatoslav Ryhel .pad_drive_up_ref = 0x0, 352*c888c4c8SSvyatoslav Ryhel .pad_vclamp_level = 0x0, 353*c888c4c8SSvyatoslav Ryhel .pad_vauxp_level = 0x0, 354*c888c4c8SSvyatoslav Ryhel .hspdos = 0x0, 355*c888c4c8SSvyatoslav Ryhel .hspuos = 0x4, 356*c888c4c8SSvyatoslav Ryhel .termos = 0x5, 357*c888c4c8SSvyatoslav Ryhel .hsclkpdos = 0x0, 358*c888c4c8SSvyatoslav Ryhel .hsclkpuos = 0x4, 359*c888c4c8SSvyatoslav Ryhel }; 360*c888c4c8SSvyatoslav Ryhel 361*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_pad tegra124_mipi_pads[] = { 362*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK }, 363*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK }, 364*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK }, 365*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK }, 366*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK }, 367*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK }, 368*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK }, 369*c888c4c8SSvyatoslav Ryhel }; 370*c888c4c8SSvyatoslav Ryhel 371*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_soc tegra124_mipi_soc = { 372*c888c4c8SSvyatoslav Ryhel .has_clk_lane = true, 373*c888c4c8SSvyatoslav Ryhel .pads = tegra124_mipi_pads, 374*c888c4c8SSvyatoslav Ryhel .num_pads = ARRAY_SIZE(tegra124_mipi_pads), 375*c888c4c8SSvyatoslav Ryhel .clock_enable_override = true, 376*c888c4c8SSvyatoslav Ryhel .needs_vclamp_ref = true, 377*c888c4c8SSvyatoslav Ryhel .pad_drive_down_ref = 0x2, 378*c888c4c8SSvyatoslav Ryhel .pad_drive_up_ref = 0x0, 379*c888c4c8SSvyatoslav Ryhel .pad_vclamp_level = 0x0, 380*c888c4c8SSvyatoslav Ryhel .pad_vauxp_level = 0x0, 381*c888c4c8SSvyatoslav Ryhel .hspdos = 0x0, 382*c888c4c8SSvyatoslav Ryhel .hspuos = 0x0, 383*c888c4c8SSvyatoslav Ryhel .termos = 0x0, 384*c888c4c8SSvyatoslav Ryhel .hsclkpdos = 0x1, 385*c888c4c8SSvyatoslav Ryhel .hsclkpuos = 0x2, 386*c888c4c8SSvyatoslav Ryhel }; 387*c888c4c8SSvyatoslav Ryhel 388*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_soc tegra132_mipi_soc = { 389*c888c4c8SSvyatoslav Ryhel .has_clk_lane = true, 390*c888c4c8SSvyatoslav Ryhel .pads = tegra124_mipi_pads, 391*c888c4c8SSvyatoslav Ryhel .num_pads = ARRAY_SIZE(tegra124_mipi_pads), 392*c888c4c8SSvyatoslav Ryhel .clock_enable_override = false, 393*c888c4c8SSvyatoslav Ryhel .needs_vclamp_ref = false, 394*c888c4c8SSvyatoslav Ryhel .pad_drive_down_ref = 0x0, 395*c888c4c8SSvyatoslav Ryhel .pad_drive_up_ref = 0x3, 396*c888c4c8SSvyatoslav Ryhel .pad_vclamp_level = 0x0, 397*c888c4c8SSvyatoslav Ryhel .pad_vauxp_level = 0x0, 398*c888c4c8SSvyatoslav Ryhel .hspdos = 0x0, 399*c888c4c8SSvyatoslav Ryhel .hspuos = 0x0, 400*c888c4c8SSvyatoslav Ryhel .termos = 0x0, 401*c888c4c8SSvyatoslav Ryhel .hsclkpdos = 0x3, 402*c888c4c8SSvyatoslav Ryhel .hsclkpuos = 0x2, 403*c888c4c8SSvyatoslav Ryhel }; 404*c888c4c8SSvyatoslav Ryhel 405*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_pad tegra210_mipi_pads[] = { 406*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 }, 407*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 }, 408*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 }, 409*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 }, 410*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 }, 411*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 }, 412*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK }, 413*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK }, 414*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK }, 415*c888c4c8SSvyatoslav Ryhel { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK }, 416*c888c4c8SSvyatoslav Ryhel }; 417*c888c4c8SSvyatoslav Ryhel 418*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_soc tegra210_mipi_soc = { 419*c888c4c8SSvyatoslav Ryhel .has_clk_lane = true, 420*c888c4c8SSvyatoslav Ryhel .pads = tegra210_mipi_pads, 421*c888c4c8SSvyatoslav Ryhel .num_pads = ARRAY_SIZE(tegra210_mipi_pads), 422*c888c4c8SSvyatoslav Ryhel .clock_enable_override = true, 423*c888c4c8SSvyatoslav Ryhel .needs_vclamp_ref = false, 424*c888c4c8SSvyatoslav Ryhel .pad_drive_down_ref = 0x0, 425*c888c4c8SSvyatoslav Ryhel .pad_drive_up_ref = 0x3, 426*c888c4c8SSvyatoslav Ryhel .pad_vclamp_level = 0x1, 427*c888c4c8SSvyatoslav Ryhel .pad_vauxp_level = 0x1, 428*c888c4c8SSvyatoslav Ryhel .hspdos = 0x0, 429*c888c4c8SSvyatoslav Ryhel .hspuos = 0x2, 430*c888c4c8SSvyatoslav Ryhel .termos = 0x0, 431*c888c4c8SSvyatoslav Ryhel .hsclkpdos = 0x0, 432*c888c4c8SSvyatoslav Ryhel .hsclkpuos = 0x2, 433*c888c4c8SSvyatoslav Ryhel }; 434*c888c4c8SSvyatoslav Ryhel 435*c888c4c8SSvyatoslav Ryhel static const struct of_device_id tegra_mipi_of_match[] = { 436*c888c4c8SSvyatoslav Ryhel { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc }, 437*c888c4c8SSvyatoslav Ryhel { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc }, 438*c888c4c8SSvyatoslav Ryhel { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc }, 439*c888c4c8SSvyatoslav Ryhel { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc }, 440*c888c4c8SSvyatoslav Ryhel { }, 441*c888c4c8SSvyatoslav Ryhel }; 442*c888c4c8SSvyatoslav Ryhel 443*c888c4c8SSvyatoslav Ryhel static int tegra_mipi_probe(struct platform_device *pdev) 444*c888c4c8SSvyatoslav Ryhel { 445*c888c4c8SSvyatoslav Ryhel const struct of_device_id *match; 446*c888c4c8SSvyatoslav Ryhel struct tegra_mipi *mipi; 447*c888c4c8SSvyatoslav Ryhel 448*c888c4c8SSvyatoslav Ryhel match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node); 449*c888c4c8SSvyatoslav Ryhel if (!match) 450*c888c4c8SSvyatoslav Ryhel return -ENODEV; 451*c888c4c8SSvyatoslav Ryhel 452*c888c4c8SSvyatoslav Ryhel mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL); 453*c888c4c8SSvyatoslav Ryhel if (!mipi) 454*c888c4c8SSvyatoslav Ryhel return -ENOMEM; 455*c888c4c8SSvyatoslav Ryhel 456*c888c4c8SSvyatoslav Ryhel mipi->soc = match->data; 457*c888c4c8SSvyatoslav Ryhel mipi->dev = &pdev->dev; 458*c888c4c8SSvyatoslav Ryhel 459*c888c4c8SSvyatoslav Ryhel mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 460*c888c4c8SSvyatoslav Ryhel if (IS_ERR(mipi->regs)) 461*c888c4c8SSvyatoslav Ryhel return PTR_ERR(mipi->regs); 462*c888c4c8SSvyatoslav Ryhel 463*c888c4c8SSvyatoslav Ryhel mutex_init(&mipi->lock); 464*c888c4c8SSvyatoslav Ryhel 465*c888c4c8SSvyatoslav Ryhel mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL); 466*c888c4c8SSvyatoslav Ryhel if (IS_ERR(mipi->clk)) { 467*c888c4c8SSvyatoslav Ryhel dev_err(&pdev->dev, "failed to get clock\n"); 468*c888c4c8SSvyatoslav Ryhel return PTR_ERR(mipi->clk); 469*c888c4c8SSvyatoslav Ryhel } 470*c888c4c8SSvyatoslav Ryhel 471*c888c4c8SSvyatoslav Ryhel platform_set_drvdata(pdev, mipi); 472*c888c4c8SSvyatoslav Ryhel 473*c888c4c8SSvyatoslav Ryhel return devm_tegra_mipi_add_provider(&pdev->dev, pdev->dev.of_node, 474*c888c4c8SSvyatoslav Ryhel &tegra114_mipi_ops); 475*c888c4c8SSvyatoslav Ryhel } 476*c888c4c8SSvyatoslav Ryhel 477*c888c4c8SSvyatoslav Ryhel struct platform_driver tegra_mipi_driver = { 478*c888c4c8SSvyatoslav Ryhel .driver = { 479*c888c4c8SSvyatoslav Ryhel .name = "tegra-mipi", 480*c888c4c8SSvyatoslav Ryhel .of_match_table = tegra_mipi_of_match, 481*c888c4c8SSvyatoslav Ryhel }, 482*c888c4c8SSvyatoslav Ryhel .probe = tegra_mipi_probe, 483*c888c4c8SSvyatoslav Ryhel }; 484