+
+
+static int vi2_mipi_calibration(struct tegra_camera_dev *cam)
+{
+ void __iomem *mipi_cal;
+ struct regmap *regs;
+ struct platform_device *pdev = cam->ndev;
+ struct vb2_buffer *vb = cam->active;
+ struct tegra_camera_buffer *buf = to_tegra_vb(vb);
+ struct soc_camera_device *icd = buf->icd;
+ struct soc_camera_subdev_desc *ssdesc = &icd->sdesc->subdev_desc;
+ struct tegra_camera_platform_data *pdata = ssdesc->drv_priv;
+ int port = pdata->port;
+ u32 val;
+ struct clk *clk_mipi_cal = NULL, *clk_72mhz = NULL;
+ int retry = 500;
+
+ /* TPG mode doesn't need any calibration */
+ if (cam->tpg_mode)
+ return 0;
+
+ /* Get clks for MIPI Calibration */
+ clk_mipi_cal = clk_get_sys("mipi-cal", NULL);
+ if (IS_ERR_OR_NULL(clk_mipi_cal)) {
+ dev_err(&pdev->dev, "cannot get mipi-cal clk.\n");
+ return PTR_ERR(clk_mipi_cal);
+ }
+
+ clk_72mhz = clk_get_sys("clk72mhz", NULL);
+ if (IS_ERR_OR_NULL(clk_72mhz)) {
+ dev_err(&pdev->dev, "cannot get 72MHz clk.\n");
+ return PTR_ERR(clk_72mhz);
+ }
+
+ /* Map registers */
+ mipi_cal = ioremap(MIPI_CAL_BASE, 0x100);
+ if (!mipi_cal)
+ return -ENOMEM;
+
+ regs = devm_regmap_init_mmio(&pdev->dev, mipi_cal, &mipi_cal_config);
+ if (IS_ERR(regs)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ iounmap(mipi_cal);
+ return PTR_ERR(regs);
+ }
+
+ /* Enable MIPI Calibration clocks */
+ if (clk_mipi_cal)
+ clk_prepare_enable(clk_mipi_cal);
+ if (clk_72mhz)
+ clk_prepare_enable(clk_72mhz);
+
+ /* MIPI_CAL_CLKEN_OVR = 1 */
+ regmap_update_bits(regs, MIPI_CAL_CTRL, CLKEN_OVR, CLKEN_OVR);
+
+ /* Clear MIPI CAL status flags */
+ regmap_write(regs, CIL_MIPI_CAL_STATUS, 0xF1F10000);
+ regmap_update_bits(regs, DSIA_MIPI_CAL_CONFIG, SELDSIA, 0);
+ regmap_update_bits(regs, DSIB_MIPI_CAL_CONFIG, SELDSIB, 0);
+ regmap_update_bits(regs, MIPI_BIAS_PAD_CFG0,
+ E_VCLAMP_REF, E_VCLAMP_REF);
+ regmap_update_bits(regs, MIPI_BIAS_PAD_CFG2, PDVREG, 0);
+ regmap_update_bits(regs, CILA_MIPI_CAL_CONFIG, SELA, 0);
+ regmap_update_bits(regs, DSIA_MIPI_CAL_CONFIG_2, CLKSELDSIA, 0);
+ regmap_update_bits(regs, CILB_MIPI_CAL_CONFIG, SELB, 0);
+ regmap_update_bits(regs, DSIB_MIPI_CAL_CONFIG_2, CLKSELDSIB, 0);
+ regmap_update_bits(regs, CILC_MIPI_CAL_CONFIG, SELC, 0);
+ regmap_update_bits(regs, CILC_MIPI_CAL_CONFIG_2, CLKSELC, 0);
+ regmap_update_bits(regs, CILD_MIPI_CAL_CONFIG, SELD, 0);
+ regmap_update_bits(regs, CILD_MIPI_CAL_CONFIG_2, CLKSELD, 0);
+ regmap_update_bits(regs, CILC_MIPI_CAL_CONFIG, SELE, 0);
+ regmap_update_bits(regs, CSIE_MIPI_CAL_CONFIG_2, CLKSELE, 0);
+
+ /* Select the CIL pad for auto calibration */
+ switch (port) {
+ case TEGRA_CAMERA_PORT_CSI_A:
+ regmap_update_bits(regs, CILA_MIPI_CAL_CONFIG, SELA, SELA);
+ regmap_update_bits(regs, DSIA_MIPI_CAL_CONFIG_2, CLKSELDSIA, 0);
+ if (pdata->lanes > 2) {
+ regmap_update_bits(regs, CILB_MIPI_CAL_CONFIG,
+ SELB, SELB);
+ regmap_update_bits(regs, DSIB_MIPI_CAL_CONFIG_2,
+ CLKSELDSIB, 0);
+ }
+ break;
+ case TEGRA_CAMERA_PORT_CSI_B:
+ regmap_update_bits(regs, CILC_MIPI_CAL_CONFIG, SELC, SELC);
+ regmap_update_bits(regs, CILC_MIPI_CAL_CONFIG_2, CLKSELC, 0);
+ if (pdata->lanes > 2) {
+ regmap_update_bits(regs, CILD_MIPI_CAL_CONFIG,
+ SELD, SELD);
+ regmap_update_bits(regs, CILD_MIPI_CAL_CONFIG_2,
+ CLKSELD, 0);
+ }
+ break;
+ case TEGRA_CAMERA_PORT_CSI_C:
+ regmap_update_bits(regs, CILE_MIPI_CAL_CONFIG, SELE, SELE);
+ regmap_update_bits(regs, CSIE_MIPI_CAL_CONFIG_2,
+ CLKSELE, CLKSELE);
+ break;
+ default:
+ dev_err(&pdev->dev, "wrong port %d\n", port);
+ }
+
+ /* Trigger calibration */
+ regmap_update_bits(regs, MIPI_CAL_CTRL, STARTCAL, STARTCAL);
+ while (--retry) {
+ regmap_read(regs, CIL_MIPI_CAL_STATUS, &val);
+ if (val & CAL_DONE)
+ break;
+ usleep_range(200, 300);
+ }
+
+ /* Cleanup: un-select to avoid interference with DSI */
+ regmap_update_bits(regs, CILA_MIPI_CAL_CONFIG, SELA, 0);
+ regmap_update_bits(regs, DSIA_MIPI_CAL_CONFIG_2,
+ CLKSELDSIA, CLKSELDSIA);
+ regmap_update_bits(regs, CILB_MIPI_CAL_CONFIG, SELB, 0);
+ regmap_update_bits(regs, DSIB_MIPI_CAL_CONFIG_2,
+ CLKSELDSIB, CLKSELDSIB);
+ regmap_update_bits(regs, CILC_MIPI_CAL_CONFIG, SELC, 0);
+ regmap_update_bits(regs, CILC_MIPI_CAL_CONFIG_2, CLKSELC, CLKSELC);
+ regmap_update_bits(regs, CILD_MIPI_CAL_CONFIG, SELD, 0);
+ regmap_update_bits(regs, CILD_MIPI_CAL_CONFIG_2, CLKSELD, CLKSELD);
+ regmap_update_bits(regs, CILE_MIPI_CAL_CONFIG, SELE, 0);
+ regmap_update_bits(regs, CSIE_MIPI_CAL_CONFIG_2, CLKSELE, 0);
+
+ /* Disable clocks */
+ if (clk_mipi_cal)
+ clk_disable_unprepare(clk_mipi_cal);
+ if (clk_72mhz)
+ clk_disable_unprepare(clk_72mhz);
+
+ if (!retry) {
+ dev_err(&pdev->dev, "MIPI calibration timeout!\n");
+ return -EBUSY;
+ }
+
+ dev_info(&pdev->dev, "MIPI calibration for CSI is done\n");
+ return 0;
+}
+