On Thu, Oct 17, 2013 at 11:22:02AM -0700, Bryan Wu wrote: > On Wed, Oct 16, 2013 at 10:36 AM, Thierry Reding > <thierry.reding@xxxxxxxxx> wrote: [...] > > +int tegra_mipi_calibrate(struct device *device) > > +{ [...] > > + struct platform_device *pdev; > > + unsigned int timeout = 20, i; > > + struct of_phandle_args args; > > + unsigned long value, pads; > > + struct tegra_mipi *mipi; > > + int err; > > + > > + err = of_parse_phandle_with_args(device->of_node, "calibrate", > > + "#calibrate-cells", 0, &args); > > + if (err < 0) > > + return err; > > + > > + pdev = of_find_device_by_node(args.np); > > + if (!pdev) { > > + of_node_put(args.np); > > + return -ENODEV; > > + } > > + > > + of_node_put(args.np); > > + pads = args.args[0]; > > + > > + mipi = platform_get_drvdata(pdev); > > + if (!mipi) { > > + err = -ENODEV; > > + goto out; > > + } > > + > > + err = clk_enable(mipi->clk); > > + if (err < 0) > > + goto out; > > + > > + mutex_lock(&mipi->lock); > > + > > + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0); > > + value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; > > + value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; > > + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0); > > + > > + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2); > > + value &= ~MIPI_CAL_BIAS_PAD_PDVREG; > > + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2); > > + > > + for (i = 0; i < ARRAY_SIZE(modules); i++) { > > + if (pads & BIT(i)) > > + value = MIPI_CAL_CONFIG_SELECT | > > + MIPI_CAL_CONFIG_HSPDOS(0) | > > + MIPI_CAL_CONFIG_HSPUOS(4) | > > + MIPI_CAL_CONFIG_TERMOS(5); > > + else > > + value = 0; > > + > > + tegra_mipi_writel(mipi, value, modules[i].reg); > > + } > > + > > + tegra_mipi_writel(mipi, MIPI_CAL_CTRL_START, MIPI_CAL_CTRL); > > + > > + while (timeout) { > > + value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS); > > + if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 && > > + (value & MIPI_CAL_STATUS_DONE) != 0) > > + break; > > + > > + usleep_range(10, 100); > > + timeout--; > > + } > > + > > + mutex_unlock(&mipi->lock); > > + clk_disable(mipi->clk); > > + > > + if (timeout == 0) > > + err = -ETIMEDOUT; > > + else > > + err = 0; > > + > > +out: > > + platform_device_put(pdev); > > + return err; > > +} > > +EXPORT_SYMBOL_GPL(tegra_mipi_calibrate); > > + > > Hi Thierry, > > Does this driver support T20/T30 or new chip after T114? I guess there > are should be some difference between them. Yes, I think this is currently incompatible with Tegra20 and Tegra30. I have no hardware that can actually make use of this (or on hardware which can, which I suppose would be Cardhu with the camera module) I can't test it because there's no driver yet. I'm not very familiar with the details on how calibration works on earlier Tegra SoCs, but I hope that we can somehow mould it into providing the same interface. I have some vague recollection of some- body mentioning that the registers are not as nicely gathered in one place on earlier SoCs, but I'm not sure. > And is there any guide about when is it correct to call this API > tegra_mipi_calibrate(). From what I understand calibration needs to happen whenever a power cycle has happened. > I think for CSI, we need get all power and clock ready and external > sensor is generating the data. Calibration will be done during CSI > receive the first frame data. For DSI, it might be different. Yes, I think for DSI it's enough to get power ready. I don't think we even need to enable the clock. I wonder how CSI will even receive the first data frame when the pads haven't been calibrated yet? Does our downstream kernel provide any hints? If not we should try to find out internally, but if all else fails I guess we can determine the right time to call this empirically. > And Is there any conflict when CSI/DSI driver both use this API? No. I think it should be safe to use them concurrently because access to the calibration registers is serialized using the mipi->lock mutex. Thierry
Attachment:
pgpiCpQlZEDyv.pgp
Description: PGP signature