linux/drivers/gpu/host1x/mipi.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013 NVIDIA Corporation
   3 *
   4 * Permission to use, copy, modify, distribute, and sell this software and its
   5 * documentation for any purpose is hereby granted without fee, provided that
   6 * the above copyright notice appear in all copies and that both that copyright
   7 * notice and this permission notice appear in supporting documentation, and
   8 * that the name of the copyright holders not be used in advertising or
   9 * publicity pertaining to distribution of the software without specific,
  10 * written prior permission.  The copyright holders make no representations
  11 * about the suitability of this software for any purpose.  It is provided "as
  12 * 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 NO
  16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  20 * OF THIS SOFTWARE.
  21 */
  22
  23#include <linux/clk.h>
  24#include <linux/delay.h>
  25#include <linux/host1x.h>
  26#include <linux/io.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                   0x00
  34#define MIPI_CAL_CTRL_START             (1 << 0)
  35
  36#define MIPI_CAL_AUTOCAL_CTRL           0x01
  37
  38#define MIPI_CAL_STATUS                 0x02
  39#define MIPI_CAL_STATUS_DONE            (1 << 16)
  40#define MIPI_CAL_STATUS_ACTIVE          (1 <<  0)
  41
  42#define MIPI_CAL_CONFIG_CSIA            0x05
  43#define MIPI_CAL_CONFIG_CSIB            0x06
  44#define MIPI_CAL_CONFIG_CSIC            0x07
  45#define MIPI_CAL_CONFIG_CSID            0x08
  46#define MIPI_CAL_CONFIG_CSIE            0x09
  47#define MIPI_CAL_CONFIG_DSIA            0x0e
  48#define MIPI_CAL_CONFIG_DSIB            0x0f
  49#define MIPI_CAL_CONFIG_DSIC            0x10
  50#define MIPI_CAL_CONFIG_DSID            0x11
  51
  52#define MIPI_CAL_CONFIG_DSIAB_CLK       0x19
  53#define MIPI_CAL_CONFIG_DSICD_CLK       0x1a
  54#define MIPI_CAL_CONFIG_CSIAB_CLK       0x1b
  55#define MIPI_CAL_CONFIG_CSICD_CLK       0x1c
  56#define MIPI_CAL_CONFIG_CSIE_CLK        0x1d
  57
  58/* for data and clock lanes */
  59#define MIPI_CAL_CONFIG_SELECT          (1 << 21)
  60
  61/* for data lanes */
  62#define MIPI_CAL_CONFIG_HSPDOS(x)       (((x) & 0x1f) << 16)
  63#define MIPI_CAL_CONFIG_HSPUOS(x)       (((x) & 0x1f) <<  8)
  64#define MIPI_CAL_CONFIG_TERMOS(x)       (((x) & 0x1f) <<  0)
  65
  66/* for clock lanes */
  67#define MIPI_CAL_CONFIG_HSCLKPDOSD(x)   (((x) & 0x1f) <<  8)
  68#define MIPI_CAL_CONFIG_HSCLKPUOSD(x)   (((x) & 0x1f) <<  0)
  69
  70#define MIPI_CAL_BIAS_PAD_CFG0          0x16
  71#define MIPI_CAL_BIAS_PAD_PDVCLAMP      (1 << 1)
  72#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF  (1 << 0)
  73
  74#define MIPI_CAL_BIAS_PAD_CFG1          0x17
  75#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
  76
  77#define MIPI_CAL_BIAS_PAD_CFG2          0x18
  78#define MIPI_CAL_BIAS_PAD_PDVREG        (1 << 1)
  79
  80struct tegra_mipi_pad {
  81        unsigned long data;
  82        unsigned long clk;
  83};
  84
  85struct tegra_mipi_soc {
  86        bool has_clk_lane;
  87        const struct tegra_mipi_pad *pads;
  88        unsigned int num_pads;
  89};
  90
  91struct tegra_mipi {
  92        const struct tegra_mipi_soc *soc;
  93        void __iomem *regs;
  94        struct mutex lock;
  95        struct clk *clk;
  96};
  97
  98struct tegra_mipi_device {
  99        struct platform_device *pdev;
 100        struct tegra_mipi *mipi;
 101        struct device *device;
 102        unsigned long pads;
 103};
 104
 105static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
 106                                   unsigned long offset)
 107{
 108        return readl(mipi->regs + (offset << 2));
 109}
 110
 111static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
 112                                     unsigned long offset)
 113{
 114        writel(value, mipi->regs + (offset << 2));
 115}
 116
 117struct tegra_mipi_device *tegra_mipi_request(struct device *device)
 118{
 119        struct device_node *np = device->of_node;
 120        struct tegra_mipi_device *dev;
 121        struct of_phandle_args args;
 122        int err;
 123
 124        err = of_parse_phandle_with_args(np, "nvidia,mipi-calibrate",
 125                                         "#nvidia,mipi-calibrate-cells", 0,
 126                                         &args);
 127        if (err < 0)
 128                return ERR_PTR(err);
 129
 130        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 131        if (!dev) {
 132                err = -ENOMEM;
 133                goto out;
 134        }
 135
 136        dev->pdev = of_find_device_by_node(args.np);
 137        if (!dev->pdev) {
 138                err = -ENODEV;
 139                goto free;
 140        }
 141
 142        dev->mipi = platform_get_drvdata(dev->pdev);
 143        if (!dev->mipi) {
 144                err = -EPROBE_DEFER;
 145                goto put;
 146        }
 147
 148        of_node_put(args.np);
 149
 150        dev->pads = args.args[0];
 151        dev->device = device;
 152
 153        return dev;
 154
 155put:
 156        platform_device_put(dev->pdev);
 157free:
 158        kfree(dev);
 159out:
 160        of_node_put(args.np);
 161        return ERR_PTR(err);
 162}
 163EXPORT_SYMBOL(tegra_mipi_request);
 164
 165void tegra_mipi_free(struct tegra_mipi_device *device)
 166{
 167        platform_device_put(device->pdev);
 168        kfree(device);
 169}
 170EXPORT_SYMBOL(tegra_mipi_free);
 171
 172static int tegra_mipi_wait(struct tegra_mipi *mipi)
 173{
 174        unsigned long timeout = jiffies + msecs_to_jiffies(250);
 175        u32 value;
 176
 177        while (time_before(jiffies, timeout)) {
 178                value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
 179                if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 &&
 180                    (value & MIPI_CAL_STATUS_DONE) != 0)
 181                        return 0;
 182
 183                usleep_range(10, 50);
 184        }
 185
 186        return -ETIMEDOUT;
 187}
 188
 189int tegra_mipi_calibrate(struct tegra_mipi_device *device)
 190{
 191        const struct tegra_mipi_soc *soc = device->mipi->soc;
 192        unsigned int i;
 193        u32 value;
 194        int err;
 195
 196        err = clk_enable(device->mipi->clk);
 197        if (err < 0)
 198                return err;
 199
 200        mutex_lock(&device->mipi->lock);
 201
 202        value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0);
 203        value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
 204        value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
 205        tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
 206
 207        tegra_mipi_writel(device->mipi, MIPI_CAL_BIAS_PAD_DRV_DN_REF(2),
 208                          MIPI_CAL_BIAS_PAD_CFG1);
 209
 210        value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
 211        value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
 212        tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
 213
 214        for (i = 0; i < soc->num_pads; i++) {
 215                u32 clk = 0, data = 0;
 216
 217                if (device->pads & BIT(i)) {
 218                        data = MIPI_CAL_CONFIG_SELECT |
 219                               MIPI_CAL_CONFIG_HSPDOS(0) |
 220                               MIPI_CAL_CONFIG_HSPUOS(4) |
 221                               MIPI_CAL_CONFIG_TERMOS(5);
 222                        clk = MIPI_CAL_CONFIG_SELECT |
 223                              MIPI_CAL_CONFIG_HSCLKPDOSD(0) |
 224                              MIPI_CAL_CONFIG_HSCLKPUOSD(4);
 225                }
 226
 227                tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
 228
 229                if (soc->has_clk_lane)
 230                        tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
 231        }
 232
 233        value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
 234        value |= MIPI_CAL_CTRL_START;
 235        tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
 236
 237        err = tegra_mipi_wait(device->mipi);
 238
 239        mutex_unlock(&device->mipi->lock);
 240        clk_disable(device->mipi->clk);
 241
 242        return err;
 243}
 244EXPORT_SYMBOL(tegra_mipi_calibrate);
 245
 246static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
 247        { .data = MIPI_CAL_CONFIG_CSIA },
 248        { .data = MIPI_CAL_CONFIG_CSIB },
 249        { .data = MIPI_CAL_CONFIG_CSIC },
 250        { .data = MIPI_CAL_CONFIG_CSID },
 251        { .data = MIPI_CAL_CONFIG_CSIE },
 252        { .data = MIPI_CAL_CONFIG_DSIA },
 253        { .data = MIPI_CAL_CONFIG_DSIB },
 254        { .data = MIPI_CAL_CONFIG_DSIC },
 255        { .data = MIPI_CAL_CONFIG_DSID },
 256};
 257
 258static const struct tegra_mipi_soc tegra114_mipi_soc = {
 259        .has_clk_lane = false,
 260        .pads = tegra114_mipi_pads,
 261        .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
 262};
 263
 264static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
 265        { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
 266        { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
 267        { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
 268        { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
 269        { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
 270        { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIAB_CLK },
 271        { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIAB_CLK },
 272};
 273
 274static const struct tegra_mipi_soc tegra124_mipi_soc = {
 275        .has_clk_lane = true,
 276        .pads = tegra124_mipi_pads,
 277        .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
 278};
 279
 280static struct of_device_id tegra_mipi_of_match[] = {
 281        { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
 282        { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
 283        { },
 284};
 285
 286static int tegra_mipi_probe(struct platform_device *pdev)
 287{
 288        const struct of_device_id *match;
 289        struct tegra_mipi *mipi;
 290        struct resource *res;
 291        int err;
 292
 293        match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
 294        if (!match)
 295                return -ENODEV;
 296
 297        mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
 298        if (!mipi)
 299                return -ENOMEM;
 300
 301        mipi->soc = match->data;
 302
 303        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 304        mipi->regs = devm_ioremap_resource(&pdev->dev, res);
 305        if (IS_ERR(mipi->regs))
 306                return PTR_ERR(mipi->regs);
 307
 308        mutex_init(&mipi->lock);
 309
 310        mipi->clk = devm_clk_get(&pdev->dev, NULL);
 311        if (IS_ERR(mipi->clk)) {
 312                dev_err(&pdev->dev, "failed to get clock\n");
 313                return PTR_ERR(mipi->clk);
 314        }
 315
 316        err = clk_prepare(mipi->clk);
 317        if (err < 0)
 318                return err;
 319
 320        platform_set_drvdata(pdev, mipi);
 321
 322        return 0;
 323}
 324
 325static int tegra_mipi_remove(struct platform_device *pdev)
 326{
 327        struct tegra_mipi *mipi = platform_get_drvdata(pdev);
 328
 329        clk_unprepare(mipi->clk);
 330
 331        return 0;
 332}
 333
 334struct platform_driver tegra_mipi_driver = {
 335        .driver = {
 336                .name = "tegra-mipi",
 337                .of_match_table = tegra_mipi_of_match,
 338        },
 339        .probe = tegra_mipi_probe,
 340        .remove = tegra_mipi_remove,
 341};
 342
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.