540 lines · c
1/*2 * Copyright (C) 2013 NVIDIA Corporation3 *4 * Permission to use, copy, modify, distribute, and sell this software and its5 * documentation for any purpose is hereby granted without fee, provided that6 * the above copyright notice appear in all copies and that both that copyright7 * notice and this permission notice appear in supporting documentation, and8 * that the name of the copyright holders not be used in advertising or9 * publicity pertaining to distribution of the software without specific,10 * written prior permission. The copyright holders make no representations11 * about the suitability of this software for any purpose. It is provided "as12 * is" without express or implied warranty.13 *14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE20 * OF THIS SOFTWARE.21 */22 23#include <linux/clk.h>24#include <linux/host1x.h>25#include <linux/io.h>26#include <linux/iopoll.h>27#include <linux/of_platform.h>28#include <linux/platform_device.h>29#include <linux/slab.h>30 31#include "dev.h"32 33#define MIPI_CAL_CTRL 0x0034#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)35#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)36#define MIPI_CAL_CTRL_CLKEN_OVR (1 << 4)37#define MIPI_CAL_CTRL_START (1 << 0)38 39#define MIPI_CAL_AUTOCAL_CTRL 0x0140 41#define MIPI_CAL_STATUS 0x0242#define MIPI_CAL_STATUS_DONE (1 << 16)43#define MIPI_CAL_STATUS_ACTIVE (1 << 0)44 45#define MIPI_CAL_CONFIG_CSIA 0x0546#define MIPI_CAL_CONFIG_CSIB 0x0647#define MIPI_CAL_CONFIG_CSIC 0x0748#define MIPI_CAL_CONFIG_CSID 0x0849#define MIPI_CAL_CONFIG_CSIE 0x0950#define MIPI_CAL_CONFIG_CSIF 0x0a51#define MIPI_CAL_CONFIG_DSIA 0x0e52#define MIPI_CAL_CONFIG_DSIB 0x0f53#define MIPI_CAL_CONFIG_DSIC 0x1054#define MIPI_CAL_CONFIG_DSID 0x1155 56#define MIPI_CAL_CONFIG_DSIA_CLK 0x1957#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a58#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b59#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c60#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c61#define MIPI_CAL_CONFIG_DSID_CLK 0x1d62#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d63 64/* for data and clock lanes */65#define MIPI_CAL_CONFIG_SELECT (1 << 21)66 67/* for data lanes */68#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)69#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)70#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)71 72/* for clock lanes */73#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)74#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)75 76#define MIPI_CAL_BIAS_PAD_CFG0 0x1677#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)78#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)79 80#define MIPI_CAL_BIAS_PAD_CFG1 0x1781#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)82#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)83 84#define MIPI_CAL_BIAS_PAD_CFG2 0x1885#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)86#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)87#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)88 89struct tegra_mipi_pad {90 unsigned long data;91 unsigned long clk;92};93 94struct tegra_mipi_soc {95 bool has_clk_lane;96 const struct tegra_mipi_pad *pads;97 unsigned int num_pads;98 99 bool clock_enable_override;100 bool needs_vclamp_ref;101 102 /* bias pad configuration settings */103 u8 pad_drive_down_ref;104 u8 pad_drive_up_ref;105 106 u8 pad_vclamp_level;107 u8 pad_vauxp_level;108 109 /* calibration settings for data lanes */110 u8 hspdos;111 u8 hspuos;112 u8 termos;113 114 /* calibration settings for clock lanes */115 u8 hsclkpdos;116 u8 hsclkpuos;117};118 119struct tegra_mipi {120 const struct tegra_mipi_soc *soc;121 struct device *dev;122 void __iomem *regs;123 struct mutex lock;124 struct clk *clk;125 126 unsigned long usage_count;127};128 129struct tegra_mipi_device {130 struct platform_device *pdev;131 struct tegra_mipi *mipi;132 struct device *device;133 unsigned long pads;134};135 136static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,137 unsigned long offset)138{139 return readl(mipi->regs + (offset << 2));140}141 142static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,143 unsigned long offset)144{145 writel(value, mipi->regs + (offset << 2));146}147 148static int tegra_mipi_power_up(struct tegra_mipi *mipi)149{150 u32 value;151 int err;152 153 err = clk_enable(mipi->clk);154 if (err < 0)155 return err;156 157 value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);158 value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;159 160 if (mipi->soc->needs_vclamp_ref)161 value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;162 163 tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);164 165 value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);166 value &= ~MIPI_CAL_BIAS_PAD_PDVREG;167 tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);168 169 clk_disable(mipi->clk);170 171 return 0;172}173 174static int tegra_mipi_power_down(struct tegra_mipi *mipi)175{176 u32 value;177 int err;178 179 err = clk_enable(mipi->clk);180 if (err < 0)181 return err;182 183 /*184 * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that185 * supplies the DSI pads. This must be kept enabled until none of the186 * DSI lanes are used anymore.187 */188 value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);189 value |= MIPI_CAL_BIAS_PAD_PDVREG;190 tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);191 192 /*193 * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF194 * control a regulator that supplies current to the pre-driver logic.195 * Powering down this regulator causes DSI to fail, so it must remain196 * powered on until none of the DSI lanes are used anymore.197 */198 value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);199 200 if (mipi->soc->needs_vclamp_ref)201 value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;202 203 value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;204 tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);205 206 return 0;207}208 209struct tegra_mipi_device *tegra_mipi_request(struct device *device,210 struct device_node *np)211{212 struct tegra_mipi_device *dev;213 struct of_phandle_args args;214 int err;215 216 err = of_parse_phandle_with_args(np, "nvidia,mipi-calibrate",217 "#nvidia,mipi-calibrate-cells", 0,218 &args);219 if (err < 0)220 return ERR_PTR(err);221 222 dev = kzalloc(sizeof(*dev), GFP_KERNEL);223 if (!dev) {224 err = -ENOMEM;225 goto out;226 }227 228 dev->pdev = of_find_device_by_node(args.np);229 if (!dev->pdev) {230 err = -ENODEV;231 goto free;232 }233 234 dev->mipi = platform_get_drvdata(dev->pdev);235 if (!dev->mipi) {236 err = -EPROBE_DEFER;237 goto put;238 }239 240 of_node_put(args.np);241 242 dev->pads = args.args[0];243 dev->device = device;244 245 return dev;246 247put:248 platform_device_put(dev->pdev);249free:250 kfree(dev);251out:252 of_node_put(args.np);253 return ERR_PTR(err);254}255EXPORT_SYMBOL(tegra_mipi_request);256 257void tegra_mipi_free(struct tegra_mipi_device *device)258{259 platform_device_put(device->pdev);260 kfree(device);261}262EXPORT_SYMBOL(tegra_mipi_free);263 264int tegra_mipi_enable(struct tegra_mipi_device *dev)265{266 int err = 0;267 268 mutex_lock(&dev->mipi->lock);269 270 if (dev->mipi->usage_count++ == 0)271 err = tegra_mipi_power_up(dev->mipi);272 273 mutex_unlock(&dev->mipi->lock);274 275 return err;276 277}278EXPORT_SYMBOL(tegra_mipi_enable);279 280int tegra_mipi_disable(struct tegra_mipi_device *dev)281{282 int err = 0;283 284 mutex_lock(&dev->mipi->lock);285 286 if (--dev->mipi->usage_count == 0)287 err = tegra_mipi_power_down(dev->mipi);288 289 mutex_unlock(&dev->mipi->lock);290 291 return err;292 293}294EXPORT_SYMBOL(tegra_mipi_disable);295 296int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)297{298 struct tegra_mipi *mipi = device->mipi;299 void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);300 u32 value;301 int err;302 303 err = readl_relaxed_poll_timeout(status_reg, value,304 !(value & MIPI_CAL_STATUS_ACTIVE) &&305 (value & MIPI_CAL_STATUS_DONE), 50,306 250000);307 mutex_unlock(&device->mipi->lock);308 clk_disable(device->mipi->clk);309 310 return err;311}312EXPORT_SYMBOL(tegra_mipi_finish_calibration);313 314int tegra_mipi_start_calibration(struct tegra_mipi_device *device)315{316 const struct tegra_mipi_soc *soc = device->mipi->soc;317 unsigned int i;318 u32 value;319 int err;320 321 err = clk_enable(device->mipi->clk);322 if (err < 0)323 return err;324 325 mutex_lock(&device->mipi->lock);326 327 value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |328 MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);329 tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);330 331 value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);332 value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);333 value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);334 value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);335 value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);336 tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);337 338 for (i = 0; i < soc->num_pads; i++) {339 u32 clk = 0, data = 0;340 341 if (device->pads & BIT(i)) {342 data = MIPI_CAL_CONFIG_SELECT |343 MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |344 MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |345 MIPI_CAL_CONFIG_TERMOS(soc->termos);346 clk = MIPI_CAL_CONFIG_SELECT |347 MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |348 MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);349 }350 351 tegra_mipi_writel(device->mipi, data, soc->pads[i].data);352 353 if (soc->has_clk_lane && soc->pads[i].clk != 0)354 tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);355 }356 357 value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);358 value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);359 value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);360 value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);361 value |= MIPI_CAL_CTRL_PRESCALE(0x2);362 363 if (!soc->clock_enable_override)364 value &= ~MIPI_CAL_CTRL_CLKEN_OVR;365 else366 value |= MIPI_CAL_CTRL_CLKEN_OVR;367 368 tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);369 370 /* clear any pending status bits */371 value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS);372 tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS);373 374 value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);375 value |= MIPI_CAL_CTRL_START;376 tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);377 378 /*379 * Wait for min 72uS to let calibration logic finish calibration380 * sequence codes before waiting for pads idle state to apply the381 * results.382 */383 usleep_range(75, 80);384 385 return 0;386}387EXPORT_SYMBOL(tegra_mipi_start_calibration);388 389static const struct tegra_mipi_pad tegra114_mipi_pads[] = {390 { .data = MIPI_CAL_CONFIG_CSIA },391 { .data = MIPI_CAL_CONFIG_CSIB },392 { .data = MIPI_CAL_CONFIG_CSIC },393 { .data = MIPI_CAL_CONFIG_CSID },394 { .data = MIPI_CAL_CONFIG_CSIE },395 { .data = MIPI_CAL_CONFIG_DSIA },396 { .data = MIPI_CAL_CONFIG_DSIB },397 { .data = MIPI_CAL_CONFIG_DSIC },398 { .data = MIPI_CAL_CONFIG_DSID },399};400 401static const struct tegra_mipi_soc tegra114_mipi_soc = {402 .has_clk_lane = false,403 .pads = tegra114_mipi_pads,404 .num_pads = ARRAY_SIZE(tegra114_mipi_pads),405 .clock_enable_override = true,406 .needs_vclamp_ref = true,407 .pad_drive_down_ref = 0x2,408 .pad_drive_up_ref = 0x0,409 .pad_vclamp_level = 0x0,410 .pad_vauxp_level = 0x0,411 .hspdos = 0x0,412 .hspuos = 0x4,413 .termos = 0x5,414 .hsclkpdos = 0x0,415 .hsclkpuos = 0x4,416};417 418static const struct tegra_mipi_pad tegra124_mipi_pads[] = {419 { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },420 { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },421 { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },422 { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },423 { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },424 { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },425 { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },426};427 428static const struct tegra_mipi_soc tegra124_mipi_soc = {429 .has_clk_lane = true,430 .pads = tegra124_mipi_pads,431 .num_pads = ARRAY_SIZE(tegra124_mipi_pads),432 .clock_enable_override = true,433 .needs_vclamp_ref = true,434 .pad_drive_down_ref = 0x2,435 .pad_drive_up_ref = 0x0,436 .pad_vclamp_level = 0x0,437 .pad_vauxp_level = 0x0,438 .hspdos = 0x0,439 .hspuos = 0x0,440 .termos = 0x0,441 .hsclkpdos = 0x1,442 .hsclkpuos = 0x2,443};444 445static const struct tegra_mipi_soc tegra132_mipi_soc = {446 .has_clk_lane = true,447 .pads = tegra124_mipi_pads,448 .num_pads = ARRAY_SIZE(tegra124_mipi_pads),449 .clock_enable_override = false,450 .needs_vclamp_ref = false,451 .pad_drive_down_ref = 0x0,452 .pad_drive_up_ref = 0x3,453 .pad_vclamp_level = 0x0,454 .pad_vauxp_level = 0x0,455 .hspdos = 0x0,456 .hspuos = 0x0,457 .termos = 0x0,458 .hsclkpdos = 0x3,459 .hsclkpuos = 0x2,460};461 462static const struct tegra_mipi_pad tegra210_mipi_pads[] = {463 { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },464 { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },465 { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },466 { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },467 { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },468 { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },469 { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },470 { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },471 { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },472 { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },473};474 475static const struct tegra_mipi_soc tegra210_mipi_soc = {476 .has_clk_lane = true,477 .pads = tegra210_mipi_pads,478 .num_pads = ARRAY_SIZE(tegra210_mipi_pads),479 .clock_enable_override = true,480 .needs_vclamp_ref = false,481 .pad_drive_down_ref = 0x0,482 .pad_drive_up_ref = 0x3,483 .pad_vclamp_level = 0x1,484 .pad_vauxp_level = 0x1,485 .hspdos = 0x0,486 .hspuos = 0x2,487 .termos = 0x0,488 .hsclkpdos = 0x0,489 .hsclkpuos = 0x2,490};491 492static const struct of_device_id tegra_mipi_of_match[] = {493 { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },494 { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },495 { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },496 { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },497 { },498};499 500static int tegra_mipi_probe(struct platform_device *pdev)501{502 const struct of_device_id *match;503 struct tegra_mipi *mipi;504 505 match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);506 if (!match)507 return -ENODEV;508 509 mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);510 if (!mipi)511 return -ENOMEM;512 513 mipi->soc = match->data;514 mipi->dev = &pdev->dev;515 516 mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);517 if (IS_ERR(mipi->regs))518 return PTR_ERR(mipi->regs);519 520 mutex_init(&mipi->lock);521 522 mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);523 if (IS_ERR(mipi->clk)) {524 dev_err(&pdev->dev, "failed to get clock\n");525 return PTR_ERR(mipi->clk);526 }527 528 platform_set_drvdata(pdev, mipi);529 530 return 0;531}532 533struct platform_driver tegra_mipi_driver = {534 .driver = {535 .name = "tegra-mipi",536 .of_match_table = tegra_mipi_of_match,537 },538 .probe = tegra_mipi_probe,539};540