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_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           0x01
  40
  41#define MIPI_CAL_STATUS                 0x02
  42#define MIPI_CAL_STATUS_DONE            (1 << 16)
  43#define MIPI_CAL_STATUS_ACTIVE          (1 <<  0)
  44
  45#define MIPI_CAL_CONFIG_CSIA            0x05
  46#define MIPI_CAL_CONFIG_CSIB            0x06
  47#define MIPI_CAL_CONFIG_CSIC            0x07
  48#define MIPI_CAL_CONFIG_CSID            0x08
  49#define MIPI_CAL_CONFIG_CSIE            0x09
  50#define MIPI_CAL_CONFIG_CSIF            0x0a
  51#define MIPI_CAL_CONFIG_DSIA            0x0e
  52#define MIPI_CAL_CONFIG_DSIB            0x0f
  53#define MIPI_CAL_CONFIG_DSIC            0x10
  54#define MIPI_CAL_CONFIG_DSID            0x11
  55
  56#define MIPI_CAL_CONFIG_DSIA_CLK        0x19
  57#define MIPI_CAL_CONFIG_DSIB_CLK        0x1a
  58#define MIPI_CAL_CONFIG_CSIAB_CLK       0x1b
  59#define MIPI_CAL_CONFIG_DSIC_CLK        0x1c
  60#define MIPI_CAL_CONFIG_CSICD_CLK       0x1c
  61#define MIPI_CAL_CONFIG_DSID_CLK        0x1d
  62#define MIPI_CAL_CONFIG_CSIE_CLK        0x1d
  63
  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          0x16
  77#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          0x17
  81#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          0x18
  85#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 that
 185         * supplies the DSI pads. This must be kept enabled until none of the
 186         * 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_REF
 194         * control a regulator that supplies current to the pre-driver logic.
 195         * Powering down this regulator causes DSI to fail, so it must remain
 196         * 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{
 211        struct device_node *np = device->of_node;
 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        mutex_lock(&dev->mipi->lock);
 246
 247        if (dev->mipi->usage_count++ == 0) {
 248                err = tegra_mipi_power_up(dev->mipi);
 249                if (err < 0) {
 250                        dev_err(dev->mipi->dev,
 251                                "failed to power up MIPI bricks: %d\n",
 252                                err);
 253                        return ERR_PTR(err);
 254                }
 255        }
 256
 257        mutex_unlock(&dev->mipi->lock);
 258
 259        return dev;
 260
 261put:
 262        platform_device_put(dev->pdev);
 263free:
 264        kfree(dev);
 265out:
 266        of_node_put(args.np);
 267        return ERR_PTR(err);
 268}
 269EXPORT_SYMBOL(tegra_mipi_request);
 270
 271void tegra_mipi_free(struct tegra_mipi_device *device)
 272{
 273        int err;
 274
 275        mutex_lock(&device->mipi->lock);
 276
 277        if (--device->mipi->usage_count == 0) {
 278                err = tegra_mipi_power_down(device->mipi);
 279                if (err < 0) {
 280                        /*
 281                         * Not much that can be done here, so an error message
 282                         * will have to do.
 283                         */
 284                        dev_err(device->mipi->dev,
 285                                "failed to power down MIPI bricks: %d\n",
 286                                err);
 287                }
 288        }
 289
 290        mutex_unlock(&device->mipi->lock);
 291
 292        platform_device_put(device->pdev);
 293        kfree(device);
 294}
 295EXPORT_SYMBOL(tegra_mipi_free);
 296
 297static int tegra_mipi_wait(struct tegra_mipi *mipi)
 298{
 299        unsigned long timeout = jiffies + msecs_to_jiffies(250);
 300        u32 value;
 301
 302        while (time_before(jiffies, timeout)) {
 303                value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
 304                if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 &&
 305                    (value & MIPI_CAL_STATUS_DONE) != 0)
 306                        return 0;
 307
 308                usleep_range(10, 50);
 309        }
 310
 311        return -ETIMEDOUT;
 312}
 313
 314int tegra_mipi_calibrate(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        else
 366                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        err = tegra_mipi_wait(device->mipi);
 379
 380        mutex_unlock(&device->mipi->lock);
 381        clk_disable(device->mipi->clk);
 382
 383        return err;
 384}
 385EXPORT_SYMBOL(tegra_mipi_calibrate);
 386
 387static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
 388        { .data = MIPI_CAL_CONFIG_CSIA },
 389        { .data = MIPI_CAL_CONFIG_CSIB },
 390        { .data = MIPI_CAL_CONFIG_CSIC },
 391        { .data = MIPI_CAL_CONFIG_CSID },
 392        { .data = MIPI_CAL_CONFIG_CSIE },
 393        { .data = MIPI_CAL_CONFIG_DSIA },
 394        { .data = MIPI_CAL_CONFIG_DSIB },
 395        { .data = MIPI_CAL_CONFIG_DSIC },
 396        { .data = MIPI_CAL_CONFIG_DSID },
 397};
 398
 399static const struct tegra_mipi_soc tegra114_mipi_soc = {
 400        .has_clk_lane = false,
 401        .pads = tegra114_mipi_pads,
 402        .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
 403        .clock_enable_override = true,
 404        .needs_vclamp_ref = true,
 405        .pad_drive_down_ref = 0x2,
 406        .pad_drive_up_ref = 0x0,
 407        .pad_vclamp_level = 0x0,
 408        .pad_vauxp_level = 0x0,
 409        .hspdos = 0x0,
 410        .hspuos = 0x4,
 411        .termos = 0x5,
 412        .hsclkpdos = 0x0,
 413        .hsclkpuos = 0x4,
 414};
 415
 416static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
 417        { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
 418        { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
 419        { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
 420        { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
 421        { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK  },
 422        { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK  },
 423        { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK  },
 424};
 425
 426static const struct tegra_mipi_soc tegra124_mipi_soc = {
 427        .has_clk_lane = true,
 428        .pads = tegra124_mipi_pads,
 429        .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
 430        .clock_enable_override = true,
 431        .needs_vclamp_ref = true,
 432        .pad_drive_down_ref = 0x2,
 433        .pad_drive_up_ref = 0x0,
 434        .pad_vclamp_level = 0x0,
 435        .pad_vauxp_level = 0x0,
 436        .hspdos = 0x0,
 437        .hspuos = 0x0,
 438        .termos = 0x0,
 439        .hsclkpdos = 0x1,
 440        .hsclkpuos = 0x2,
 441};
 442
 443static const struct tegra_mipi_soc tegra132_mipi_soc = {
 444        .has_clk_lane = true,
 445        .pads = tegra124_mipi_pads,
 446        .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
 447        .clock_enable_override = false,
 448        .needs_vclamp_ref = false,
 449        .pad_drive_down_ref = 0x0,
 450        .pad_drive_up_ref = 0x3,
 451        .pad_vclamp_level = 0x0,
 452        .pad_vauxp_level = 0x0,
 453        .hspdos = 0x0,
 454        .hspuos = 0x0,
 455        .termos = 0x0,
 456        .hsclkpdos = 0x3,
 457        .hsclkpuos = 0x2,
 458};
 459
 460static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
 461        { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
 462        { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
 463        { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
 464        { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
 465        { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
 466        { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
 467        { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
 468        { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
 469        { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
 470        { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
 471};
 472
 473static const struct tegra_mipi_soc tegra210_mipi_soc = {
 474        .has_clk_lane = true,
 475        .pads = tegra210_mipi_pads,
 476        .num_pads = ARRAY_SIZE(tegra210_mipi_pads),
 477        .clock_enable_override = true,
 478        .needs_vclamp_ref = false,
 479        .pad_drive_down_ref = 0x0,
 480        .pad_drive_up_ref = 0x3,
 481        .pad_vclamp_level = 0x1,
 482        .pad_vauxp_level = 0x1,
 483        .hspdos = 0x0,
 484        .hspuos = 0x2,
 485        .termos = 0x0,
 486        .hsclkpdos = 0x0,
 487        .hsclkpuos = 0x2,
 488};
 489
 490static const struct of_device_id tegra_mipi_of_match[] = {
 491        { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
 492        { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
 493        { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
 494        { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
 495        { },
 496};
 497
 498static int tegra_mipi_probe(struct platform_device *pdev)
 499{
 500        const struct of_device_id *match;
 501        struct tegra_mipi *mipi;
 502        struct resource *res;
 503        int err;
 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        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 517        mipi->regs = devm_ioremap_resource(&pdev->dev, res);
 518        if (IS_ERR(mipi->regs))
 519                return PTR_ERR(mipi->regs);
 520
 521        mutex_init(&mipi->lock);
 522
 523        mipi->clk = devm_clk_get(&pdev->dev, NULL);
 524        if (IS_ERR(mipi->clk)) {
 525                dev_err(&pdev->dev, "failed to get clock\n");
 526                return PTR_ERR(mipi->clk);
 527        }
 528
 529        err = clk_prepare(mipi->clk);
 530        if (err < 0)
 531                return err;
 532
 533        platform_set_drvdata(pdev, mipi);
 534
 535        return 0;
 536}
 537
 538static int tegra_mipi_remove(struct platform_device *pdev)
 539{
 540        struct tegra_mipi *mipi = platform_get_drvdata(pdev);
 541
 542        clk_unprepare(mipi->clk);
 543
 544        return 0;
 545}
 546
 547struct platform_driver tegra_mipi_driver = {
 548        .driver = {
 549                .name = "tegra-mipi",
 550                .of_match_table = tegra_mipi_of_match,
 551        },
 552        .probe = tegra_mipi_probe,
 553        .remove = tegra_mipi_remove,
 554};
 555