Convert existing MIPI code to use operation function pointers, a necessary
step for supporting Tegra20/Tegra30 SoCs. All common MIPI configuration
that is SoC-independent remains in mipi.c, while all SoC-specific code is
moved to tegra114-mipi.c (The naming matches the first SoC generation with
a dedicated calibration block). Shared structures and function calls are
placed into tegra-mipi-cal.h.
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Acked-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/gpu/drm/tegra/dsi.c | 1 +
drivers/gpu/host1x/Makefile | 1 +
drivers/gpu/host1x/mipi.c | 525 +++---------------------
drivers/gpu/host1x/tegra114-mipi.c | 483 ++++++++++++++++++++++
drivers/staging/media/tegra-video/csi.c | 1 +
include/linux/host1x.h | 10 -
include/linux/tegra-mipi-cal.h | 57 +++
7 files changed, 599 insertions(+), 479 deletions(-)
create mode 100644 drivers/gpu/host1x/tegra114-mipi.c
create mode 100644 include/linux/tegra-mipi-cal.h
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index b2046f6ea515..502f0cd0ce53 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -14,6 +14,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
+#include <linux/tegra-mipi-cal.h>
#include <video/mipi_display.h>
diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile
index ee5286ffe08d..fead483af0b4 100644
--- a/drivers/gpu/host1x/Makefile
+++ b/drivers/gpu/host1x/Makefile
@@ -9,6 +9,7 @@ host1x-y = \
job.o \
debug.o \
mipi.o \
+ tegra114-mipi.o \
fence.o \
hw/host1x01.o \
hw/host1x02.o \
diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c
index e51b43dd15a3..8d32072e2244 100644
--- a/drivers/gpu/host1x/mipi.c
+++ b/drivers/gpu/host1x/mipi.c
@@ -1,215 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 NVIDIA Corporation
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that copyright
- * notice and this permission notice appear in supporting documentation, and
- * that the name of the copyright holders not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission. The copyright holders make no representations
- * about the suitability of this software for any purpose. It is provided "as
- * is" without express or implied warranty.
- *
- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
- * OF THIS SOFTWARE.
+ * Copyright (C) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
*/
#include <linux/clk.h>
-#include <linux/host1x.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/tegra-mipi-cal.h>
-#include "dev.h"
+/* only need to support one provider */
+static struct {
+ struct device_node *np;
+ const struct tegra_mipi_ops *ops;
+} provider;
-#define MIPI_CAL_CTRL 0x00
-#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
-#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
-#define MIPI_CAL_CTRL_CLKEN_OVR (1 << 4)
-#define MIPI_CAL_CTRL_START (1 << 0)
-
-#define MIPI_CAL_AUTOCAL_CTRL 0x01
-
-#define MIPI_CAL_STATUS 0x02
-#define MIPI_CAL_STATUS_DONE (1 << 16)
-#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
-
-#define MIPI_CAL_CONFIG_CSIA 0x05
-#define MIPI_CAL_CONFIG_CSIB 0x06
-#define MIPI_CAL_CONFIG_CSIC 0x07
-#define MIPI_CAL_CONFIG_CSID 0x08
-#define MIPI_CAL_CONFIG_CSIE 0x09
-#define MIPI_CAL_CONFIG_CSIF 0x0a
-#define MIPI_CAL_CONFIG_DSIA 0x0e
-#define MIPI_CAL_CONFIG_DSIB 0x0f
-#define MIPI_CAL_CONFIG_DSIC 0x10
-#define MIPI_CAL_CONFIG_DSID 0x11
-
-#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
-#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
-#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
-#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
-#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
-#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
-#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
-
-/* for data and clock lanes */
-#define MIPI_CAL_CONFIG_SELECT (1 << 21)
-
-/* for data lanes */
-#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
-#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
-#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
-
-/* for clock lanes */
-#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
-#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
-
-#define MIPI_CAL_BIAS_PAD_CFG0 0x16
-#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
-#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
-
-#define MIPI_CAL_BIAS_PAD_CFG1 0x17
-#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
-#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
-
-#define MIPI_CAL_BIAS_PAD_CFG2 0x18
-#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
-#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
-#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
-
-struct tegra_mipi_pad {
- unsigned long data;
- unsigned long clk;
-};
-
-struct tegra_mipi_soc {
- bool has_clk_lane;
- const struct tegra_mipi_pad *pads;
- unsigned int num_pads;
-
- bool clock_enable_override;
- bool needs_vclamp_ref;
-
- /* bias pad configuration settings */
- u8 pad_drive_down_ref;
- u8 pad_drive_up_ref;
-
- u8 pad_vclamp_level;
- u8 pad_vauxp_level;
-
- /* calibration settings for data lanes */
- u8 hspdos;
- u8 hspuos;
- u8 termos;
-
- /* calibration settings for clock lanes */
- u8 hsclkpdos;
- u8 hsclkpuos;
-};
-
-struct tegra_mipi {
- const struct tegra_mipi_soc *soc;
- struct device *dev;
- void __iomem *regs;
- struct mutex lock;
- struct clk *clk;
-
- unsigned long usage_count;
-};
-
-struct tegra_mipi_device {
- struct platform_device *pdev;
- struct tegra_mipi *mipi;
- struct device *device;
- unsigned long pads;
-};
-
-static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
- unsigned long offset)
+int tegra_mipi_enable(struct tegra_mipi_device *device)
{
- return readl(mipi->regs + (offset << 2));
-}
+ if (device->ops->enable)
+ return device->ops->enable(device);
-static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
- unsigned long offset)
-{
- writel(value, mipi->regs + (offset << 2));
+ return 0;
}
+EXPORT_SYMBOL(tegra_mipi_enable);
-static int tegra_mipi_power_up(struct tegra_mipi *mipi)
+int tegra_mipi_disable(struct tegra_mipi_device *device)
{
- u32 value;
- int err;
-
- err = clk_enable(mipi->clk);
- if (err < 0)
- return err;
-
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
- value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
-
- if (mipi->soc->needs_vclamp_ref)
- 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);
-
- clk_disable(mipi->clk);
+ if (device->ops->disable)
+ return device->ops->disable(device);
return 0;
}
+EXPORT_SYMBOL(tegra_mipi_disable);
-static int tegra_mipi_power_down(struct tegra_mipi *mipi)
+int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
{
- u32 value;
- int err;
+ if (device->ops->start_calibration)
+ return device->ops->start_calibration(device);
- err = clk_enable(mipi->clk);
- if (err < 0)
- return err;
-
- /*
- * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
- * supplies the DSI pads. This must be kept enabled until none of the
- * DSI lanes are used anymore.
- */
- 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);
-
- /*
- * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
- * control a regulator that supplies current to the pre-driver logic.
- * Powering down this regulator causes DSI to fail, so it must remain
- * powered on until none of the DSI lanes are used anymore.
- */
- value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
-
- if (mipi->soc->needs_vclamp_ref)
- value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+ return 0;
+}
+EXPORT_SYMBOL(tegra_mipi_start_calibration);
- value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
- tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
+{
+ if (device->ops->finish_calibration)
+ return device->ops->finish_calibration(device);
return 0;
}
+EXPORT_SYMBOL(tegra_mipi_finish_calibration);
struct tegra_mipi_device *tegra_mipi_request(struct device *device,
struct device_node *np)
{
- struct tegra_mipi_device *dev;
+ struct tegra_mipi_device *mipidev;
struct of_phandle_args args;
int err;
@@ -219,321 +69,58 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device,
if (err < 0)
return ERR_PTR(err);
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
+ if (provider.np != args.np)
+ return ERR_PTR(-ENODEV);
+
+ mipidev = kzalloc(sizeof(*mipidev), GFP_KERNEL);
+ if (!mipidev) {
err = -ENOMEM;
goto out;
}
- dev->pdev = of_find_device_by_node(args.np);
- if (!dev->pdev) {
+ mipidev->pdev = of_find_device_by_node(args.np);
+ if (!mipidev->pdev) {
err = -ENODEV;
goto free;
}
- dev->mipi = platform_get_drvdata(dev->pdev);
- if (!dev->mipi) {
- err = -EPROBE_DEFER;
- goto put;
- }
-
of_node_put(args.np);
- dev->pads = args.args[0];
- dev->device = device;
+ mipidev->ops = provider.ops;
+ mipidev->pads = args.args[0];
- return dev;
+ return mipidev;
-put:
- platform_device_put(dev->pdev);
free:
- kfree(dev);
+ kfree(mipidev);
out:
of_node_put(args.np);
return ERR_PTR(err);
}
EXPORT_SYMBOL(tegra_mipi_request);
-void tegra_mipi_free(struct tegra_mipi_device *device)
+void tegra_mipi_free(struct tegra_mipi_device *mipidev)
{
- platform_device_put(device->pdev);
- kfree(device);
+ platform_device_put(mipidev->pdev);
+ kfree(mipidev);
}
EXPORT_SYMBOL(tegra_mipi_free);
-int tegra_mipi_enable(struct tegra_mipi_device *dev)
+static void tegra_mipi_remove_provider(void *data)
{
- int err = 0;
-
- mutex_lock(&dev->mipi->lock);
-
- if (dev->mipi->usage_count++ == 0)
- err = tegra_mipi_power_up(dev->mipi);
-
- mutex_unlock(&dev->mipi->lock);
-
- return err;
-
+ provider.np = NULL;
+ provider.ops = NULL;
}
-EXPORT_SYMBOL(tegra_mipi_enable);
-int tegra_mipi_disable(struct tegra_mipi_device *dev)
+int devm_tegra_mipi_add_provider(struct device *device, struct device_node *np,
+ const struct tegra_mipi_ops *ops)
{
- int err = 0;
-
- mutex_lock(&dev->mipi->lock);
-
- if (--dev->mipi->usage_count == 0)
- err = tegra_mipi_power_down(dev->mipi);
+ if (provider.np)
+ return -EBUSY;
- mutex_unlock(&dev->mipi->lock);
+ provider.np = np;
+ provider.ops = ops;
- return err;
-
-}
-EXPORT_SYMBOL(tegra_mipi_disable);
-
-int tegra_mipi_finish_calibration(struct tegra_mipi_device *device)
-{
- struct tegra_mipi *mipi = device->mipi;
- void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
- u32 value;
- int err;
-
- err = readl_relaxed_poll_timeout(status_reg, value,
- !(value & MIPI_CAL_STATUS_ACTIVE) &&
- (value & MIPI_CAL_STATUS_DONE), 50,
- 250000);
- mutex_unlock(&device->mipi->lock);
- clk_disable(device->mipi->clk);
-
- return err;
-}
-EXPORT_SYMBOL(tegra_mipi_finish_calibration);
-
-int tegra_mipi_start_calibration(struct tegra_mipi_device *device)
-{
- const struct tegra_mipi_soc *soc = device->mipi->soc;
- unsigned int i;
- u32 value;
- int err;
-
- err = clk_enable(device->mipi->clk);
- if (err < 0)
- return err;
-
- mutex_lock(&device->mipi->lock);
-
- value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
- MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
-
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
- value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
- value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
- value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
- value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
-
- for (i = 0; i < soc->num_pads; i++) {
- u32 clk = 0, data = 0;
-
- if (device->pads & BIT(i)) {
- data = MIPI_CAL_CONFIG_SELECT |
- MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
- MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
- MIPI_CAL_CONFIG_TERMOS(soc->termos);
- clk = MIPI_CAL_CONFIG_SELECT |
- MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
- MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
- }
-
- tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
-
- if (soc->has_clk_lane && soc->pads[i].clk != 0)
- tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
- }
-
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
- value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
- value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
- value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
- value |= MIPI_CAL_CTRL_PRESCALE(0x2);
-
- if (!soc->clock_enable_override)
- value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
- else
- value |= MIPI_CAL_CTRL_CLKEN_OVR;
-
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
-
- /* clear any pending status bits */
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_STATUS);
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_STATUS);
-
- value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
- value |= MIPI_CAL_CTRL_START;
- tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
-
- /*
- * Wait for min 72uS to let calibration logic finish calibration
- * sequence codes before waiting for pads idle state to apply the
- * results.
- */
- usleep_range(75, 80);
-
- return 0;
-}
-EXPORT_SYMBOL(tegra_mipi_start_calibration);
-
-static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
- { .data = MIPI_CAL_CONFIG_CSIA },
- { .data = MIPI_CAL_CONFIG_CSIB },
- { .data = MIPI_CAL_CONFIG_CSIC },
- { .data = MIPI_CAL_CONFIG_CSID },
- { .data = MIPI_CAL_CONFIG_CSIE },
- { .data = MIPI_CAL_CONFIG_DSIA },
- { .data = MIPI_CAL_CONFIG_DSIB },
- { .data = MIPI_CAL_CONFIG_DSIC },
- { .data = MIPI_CAL_CONFIG_DSID },
-};
-
-static const struct tegra_mipi_soc tegra114_mipi_soc = {
- .has_clk_lane = false,
- .pads = tegra114_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
- .clock_enable_override = true,
- .needs_vclamp_ref = true,
- .pad_drive_down_ref = 0x2,
- .pad_drive_up_ref = 0x0,
- .pad_vclamp_level = 0x0,
- .pad_vauxp_level = 0x0,
- .hspdos = 0x0,
- .hspuos = 0x4,
- .termos = 0x5,
- .hsclkpdos = 0x0,
- .hsclkpuos = 0x4,
-};
-
-static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
- { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
- { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
- { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
- { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
- { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
- { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
- { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
-};
-
-static const struct tegra_mipi_soc tegra124_mipi_soc = {
- .has_clk_lane = true,
- .pads = tegra124_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
- .clock_enable_override = true,
- .needs_vclamp_ref = true,
- .pad_drive_down_ref = 0x2,
- .pad_drive_up_ref = 0x0,
- .pad_vclamp_level = 0x0,
- .pad_vauxp_level = 0x0,
- .hspdos = 0x0,
- .hspuos = 0x0,
- .termos = 0x0,
- .hsclkpdos = 0x1,
- .hsclkpuos = 0x2,
-};
-
-static const struct tegra_mipi_soc tegra132_mipi_soc = {
- .has_clk_lane = true,
- .pads = tegra124_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
- .clock_enable_override = false,
- .needs_vclamp_ref = false,
- .pad_drive_down_ref = 0x0,
- .pad_drive_up_ref = 0x3,
- .pad_vclamp_level = 0x0,
- .pad_vauxp_level = 0x0,
- .hspdos = 0x0,
- .hspuos = 0x0,
- .termos = 0x0,
- .hsclkpdos = 0x3,
- .hsclkpuos = 0x2,
-};
-
-static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
- { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
- { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
- { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
- { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
- { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
-};
-
-static const struct tegra_mipi_soc tegra210_mipi_soc = {
- .has_clk_lane = true,
- .pads = tegra210_mipi_pads,
- .num_pads = ARRAY_SIZE(tegra210_mipi_pads),
- .clock_enable_override = true,
- .needs_vclamp_ref = false,
- .pad_drive_down_ref = 0x0,
- .pad_drive_up_ref = 0x3,
- .pad_vclamp_level = 0x1,
- .pad_vauxp_level = 0x1,
- .hspdos = 0x0,
- .hspuos = 0x2,
- .termos = 0x0,
- .hsclkpdos = 0x0,
- .hsclkpuos = 0x2,
-};
-
-static const struct of_device_id tegra_mipi_of_match[] = {
- { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
- { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
- { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
- { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
- { },
-};
-
-static int tegra_mipi_probe(struct platform_device *pdev)
-{
- const struct of_device_id *match;
- struct tegra_mipi *mipi;
-
- match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
- if (!match)
- return -ENODEV;
-
- mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
- if (!mipi)
- return -ENOMEM;
-
- mipi->soc = match->data;
- mipi->dev = &pdev->dev;
-
- mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
- if (IS_ERR(mipi->regs))
- return PTR_ERR(mipi->regs);
-
- mutex_init(&mipi->lock);
-
- mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
- if (IS_ERR(mipi->clk)) {
- dev_err(&pdev->dev, "failed to get clock\n");
- return PTR_ERR(mipi->clk);
- }
-
- platform_set_drvdata(pdev, mipi);
-
- return 0;
+ return devm_add_action_or_reset(device, tegra_mipi_remove_provider, NULL);
}
-
-struct platform_driver tegra_mipi_driver = {
- .driver = {
- .name = "tegra-mipi",
- .of_match_table = tegra_mipi_of_match,
- },
- .probe = tegra_mipi_probe,
-};
+EXPORT_SYMBOL(devm_tegra_mipi_add_provider);
diff --git a/drivers/gpu/host1x/tegra114-mipi.c b/drivers/gpu/host1x/tegra114-mipi.c
new file mode 100644
index 000000000000..c084a09784d1
--- /dev/null
+++ b/drivers/gpu/host1x/tegra114-mipi.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/host1x.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/tegra-mipi-cal.h>
+
+#include "dev.h"
+
+#define MIPI_CAL_CTRL 0x00
+#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
+#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
+#define MIPI_CAL_CTRL_CLKEN_OVR BIT(4)
+#define MIPI_CAL_CTRL_START BIT(0)
+
+#define MIPI_CAL_AUTOCAL_CTRL 0x01
+
+#define MIPI_CAL_STATUS 0x02
+#define MIPI_CAL_STATUS_DONE BIT(16)
+#define MIPI_CAL_STATUS_ACTIVE BIT(0)
+
+#define MIPI_CAL_CONFIG_CSIA 0x05
+#define MIPI_CAL_CONFIG_CSIB 0x06
+#define MIPI_CAL_CONFIG_CSIC 0x07
+#define MIPI_CAL_CONFIG_CSID 0x08
+#define MIPI_CAL_CONFIG_CSIE 0x09
+#define MIPI_CAL_CONFIG_CSIF 0x0a
+#define MIPI_CAL_CONFIG_DSIA 0x0e
+#define MIPI_CAL_CONFIG_DSIB 0x0f
+#define MIPI_CAL_CONFIG_DSIC 0x10
+#define MIPI_CAL_CONFIG_DSID 0x11
+
+#define MIPI_CAL_CONFIG_DSIA_CLK 0x19
+#define MIPI_CAL_CONFIG_DSIB_CLK 0x1a
+#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
+#define MIPI_CAL_CONFIG_DSIC_CLK 0x1c
+#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
+#define MIPI_CAL_CONFIG_DSID_CLK 0x1d
+#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
+
+/* for data and clock lanes */
+#define MIPI_CAL_CONFIG_SELECT BIT(21)
+
+/* for data lanes */
+#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
+#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
+
+/* for clock lanes */
+#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
+#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
+
+#define MIPI_CAL_BIAS_PAD_CFG0 0x16
+#define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1)
+#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0)
+
+#define MIPI_CAL_BIAS_PAD_CFG1 0x17
+#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
+#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
+
+#define MIPI_CAL_BIAS_PAD_CFG2 0x18
+#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
+#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
+#define MIPI_CAL_BIAS_PAD_PDVREG BIT(1)
+
+struct tegra_mipi_pad {
+ unsigned long data;
+ unsigned long clk;
+};
+
+struct tegra_mipi_soc {
+ bool has_clk_lane;
+ const struct tegra_mipi_pad *pads;
+ unsigned int num_pads;
+
+ bool clock_enable_override;
+ bool needs_vclamp_ref;
+
+ /* bias pad configuration settings */
+ u8 pad_drive_down_ref;
+ u8 pad_drive_up_ref;
+
+ u8 pad_vclamp_level;
+ u8 pad_vauxp_level;
+
+ /* calibration settings for data lanes */
+ u8 hspdos;
+ u8 hspuos;
+ u8 termos;
+
+ /* calibration settings for clock lanes */
+ u8 hsclkpdos;
+ u8 hsclkpuos;
+};
+
+struct tegra_mipi {
+ const struct tegra_mipi_soc *soc;
+ struct device *dev;
+ void __iomem *regs;
+ struct mutex lock; /* for register access */
+ struct clk *clk;
+
+ unsigned long usage_count;
+};
+
+static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
+ unsigned long offset)
+{
+ return readl(mipi->regs + (offset << 2));
+}
+
+static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
+ unsigned long offset)
+{
+ writel(value, mipi->regs + (offset << 2));
+}
+
+static int tegra114_mipi_power_up(struct tegra_mipi *mipi)
+{
+ u32 value;
+ int err;
+
+ err = clk_enable(mipi->clk);
+ if (err < 0)
+ return err;
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
+ value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
+
+ if (mipi->soc->needs_vclamp_ref)
+ 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);
+
+ clk_disable(mipi->clk);
+
+ return 0;
+}
+
+static int tegra114_mipi_power_down(struct tegra_mipi *mipi)
+{
+ u32 value;
+ int err;
+
+ err = clk_enable(mipi->clk);
+ if (err < 0)
+ return err;
+
+ /*
+ * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
+ * supplies the DSI pads. This must be kept enabled until none of the
+ * DSI lanes are used anymore.
+ */
+ 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);
+
+ /*
+ * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
+ * control a regulator that supplies current to the pre-driver logic.
+ * Powering down this regulator causes DSI to fail, so it must remain
+ * powered on until none of the DSI lanes are used anymore.
+ */
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
+
+ if (mipi->soc->needs_vclamp_ref)
+ value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+
+ value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+
+ return 0;
+}
+
+static int tegra114_mipi_enable(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ int err = 0;
+
+ mutex_lock(&mipi->lock);
+
+ if (mipi->usage_count++ == 0)
+ err = tegra114_mipi_power_up(mipi);
+
+ mutex_unlock(&mipi->lock);
+
+ return err;
+}
+
+static int tegra114_mipi_disable(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ int err = 0;
+
+ mutex_lock(&mipi->lock);
+
+ if (--mipi->usage_count == 0)
+ err = tegra114_mipi_power_down(mipi);
+
+ mutex_unlock(&mipi->lock);
+
+ return err;
+}
+
+static int tegra114_mipi_finish_calibration(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
+ u32 value;
+ int err;
+
+ err = readl_relaxed_poll_timeout(status_reg, value,
+ !(value & MIPI_CAL_STATUS_ACTIVE) &&
+ (value & MIPI_CAL_STATUS_DONE), 50,
+ 250000);
+ mutex_unlock(&mipi->lock);
+ clk_disable(mipi->clk);
+
+ return err;
+}
+
+static int tegra114_mipi_start_calibration(struct tegra_mipi_device *mipidev)
+{
+ struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
+ const struct tegra_mipi_soc *soc = mipi->soc;
+ unsigned int i;
+ u32 value;
+ int err;
+
+ err = clk_enable(mipi->clk);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&mipi->lock);
+
+ value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
+ MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
+ value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
+ value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
+ value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
+ value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
+ tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
+
+ for (i = 0; i < soc->num_pads; i++) {
+ u32 clk = 0, data = 0;
+
+ if (mipidev->pads & BIT(i)) {
+ data = MIPI_CAL_CONFIG_SELECT |
+ MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
+ MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
+ MIPI_CAL_CONFIG_TERMOS(soc->termos);
+ clk = MIPI_CAL_CONFIG_SELECT |
+ MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
+ MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
+ }
+
+ tegra_mipi_writel(mipi, data, soc->pads[i].data);
+
+ if (soc->has_clk_lane && soc->pads[i].clk != 0)
+ tegra_mipi_writel(mipi, clk, soc->pads[i].clk);
+ }
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
+ value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
+ value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
+ value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
+ value |= MIPI_CAL_CTRL_PRESCALE(0x2);
+
+ if (!soc->clock_enable_override)
+ value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
+ else
+ value |= MIPI_CAL_CTRL_CLKEN_OVR;
+
+ tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
+
+ /* clear any pending status bits */
+ value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
+ tegra_mipi_writel(mipi, value, MIPI_CAL_STATUS);
+
+ value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
+ value |= MIPI_CAL_CTRL_START;
+ tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
+
+ /*
+ * Wait for min 72uS to let calibration logic finish calibration
+ * sequence codes before waiting for pads idle state to apply the
+ * results.
+ */
+ usleep_range(75, 80);
+
+ return 0;
+}
+
+static const struct tegra_mipi_ops tegra114_mipi_ops = {
+ .enable = tegra114_mipi_enable,
+ .disable = tegra114_mipi_disable,
+ .start_calibration = tegra114_mipi_start_calibration,
+ .finish_calibration = tegra114_mipi_finish_calibration,
+};
+
+static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA },
+ { .data = MIPI_CAL_CONFIG_CSIB },
+ { .data = MIPI_CAL_CONFIG_CSIC },
+ { .data = MIPI_CAL_CONFIG_CSID },
+ { .data = MIPI_CAL_CONFIG_CSIE },
+ { .data = MIPI_CAL_CONFIG_DSIA },
+ { .data = MIPI_CAL_CONFIG_DSIB },
+ { .data = MIPI_CAL_CONFIG_DSIC },
+ { .data = MIPI_CAL_CONFIG_DSID },
+};
+
+static const struct tegra_mipi_soc tegra114_mipi_soc = {
+ .has_clk_lane = false,
+ .pads = tegra114_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
+ .clock_enable_override = true,
+ .needs_vclamp_ref = true,
+ .pad_drive_down_ref = 0x2,
+ .pad_drive_up_ref = 0x0,
+ .pad_vclamp_level = 0x0,
+ .pad_vauxp_level = 0x0,
+ .hspdos = 0x0,
+ .hspuos = 0x4,
+ .termos = 0x5,
+ .hsclkpdos = 0x0,
+ .hsclkpuos = 0x4,
+};
+
+static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
+ { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
+ { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
+};
+
+static const struct tegra_mipi_soc tegra124_mipi_soc = {
+ .has_clk_lane = true,
+ .pads = tegra124_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
+ .clock_enable_override = true,
+ .needs_vclamp_ref = true,
+ .pad_drive_down_ref = 0x2,
+ .pad_drive_up_ref = 0x0,
+ .pad_vclamp_level = 0x0,
+ .pad_vauxp_level = 0x0,
+ .hspdos = 0x0,
+ .hspuos = 0x0,
+ .termos = 0x0,
+ .hsclkpdos = 0x1,
+ .hsclkpuos = 0x2,
+};
+
+static const struct tegra_mipi_soc tegra132_mipi_soc = {
+ .has_clk_lane = true,
+ .pads = tegra124_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
+ .clock_enable_override = false,
+ .needs_vclamp_ref = false,
+ .pad_drive_down_ref = 0x0,
+ .pad_drive_up_ref = 0x3,
+ .pad_vclamp_level = 0x0,
+ .pad_vauxp_level = 0x0,
+ .hspdos = 0x0,
+ .hspuos = 0x0,
+ .termos = 0x0,
+ .hsclkpdos = 0x3,
+ .hsclkpuos = 0x2,
+};
+
+static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
+ { .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
+ { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
+ { .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
+ { .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
+};
+
+static const struct tegra_mipi_soc tegra210_mipi_soc = {
+ .has_clk_lane = true,
+ .pads = tegra210_mipi_pads,
+ .num_pads = ARRAY_SIZE(tegra210_mipi_pads),
+ .clock_enable_override = true,
+ .needs_vclamp_ref = false,
+ .pad_drive_down_ref = 0x0,
+ .pad_drive_up_ref = 0x3,
+ .pad_vclamp_level = 0x1,
+ .pad_vauxp_level = 0x1,
+ .hspdos = 0x0,
+ .hspuos = 0x2,
+ .termos = 0x0,
+ .hsclkpdos = 0x0,
+ .hsclkpuos = 0x2,
+};
+
+static const struct of_device_id tegra_mipi_of_match[] = {
+ { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
+ { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
+ { .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
+ { .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
+ { },
+};
+
+static int tegra_mipi_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct tegra_mipi *mipi;
+
+ match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
+ if (!match)
+ return -ENODEV;
+
+ mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
+ if (!mipi)
+ return -ENOMEM;
+
+ mipi->soc = match->data;
+ mipi->dev = &pdev->dev;
+
+ mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(mipi->regs))
+ return PTR_ERR(mipi->regs);
+
+ mutex_init(&mipi->lock);
+
+ mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
+ if (IS_ERR(mipi->clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(mipi->clk);
+ }
+
+ platform_set_drvdata(pdev, mipi);
+
+ return devm_tegra_mipi_add_provider(&pdev->dev, pdev->dev.of_node,
+ &tegra114_mipi_ops);
+}
+
+struct platform_driver tegra_mipi_driver = {
+ .driver = {
+ .name = "tegra-mipi",
+ .of_match_table = tegra_mipi_of_match,
+ },
+ .probe = tegra_mipi_probe,
+};
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index 74c92db1032f..9e3bd6109781 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -12,6 +12,7 @@
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/tegra-mipi-cal.h>
#include <media/v4l2-fwnode.h>
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 9fa9c30a34e6..b1c6514859d3 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -453,16 +453,6 @@ void host1x_client_unregister(struct host1x_client *client);
int host1x_client_suspend(struct host1x_client *client);
int host1x_client_resume(struct host1x_client *client);
-struct tegra_mipi_device;
-
-struct tegra_mipi_device *tegra_mipi_request(struct device *device,
- struct device_node *np);
-void tegra_mipi_free(struct tegra_mipi_device *device);
-int tegra_mipi_enable(struct tegra_mipi_device *device);
-int tegra_mipi_disable(struct tegra_mipi_device *device);
-int tegra_mipi_start_calibration(struct tegra_mipi_device *device);
-int tegra_mipi_finish_calibration(struct tegra_mipi_device *device);
-
/* host1x memory contexts */
struct host1x_memory_context {
diff --git a/include/linux/tegra-mipi-cal.h b/include/linux/tegra-mipi-cal.h
new file mode 100644
index 000000000000..2a540b50f65d
--- /dev/null
+++ b/include/linux/tegra-mipi-cal.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __TEGRA_MIPI_CAL_H_
+#define __TEGRA_MIPI_CAL_H_
+
+struct tegra_mipi_device {
+ const struct tegra_mipi_ops *ops;
+ struct platform_device *pdev;
+ unsigned long pads;
+};
+
+/**
+ * Operations for Tegra MIPI calibration device
+ */
+struct tegra_mipi_ops {
+ /**
+ * @enable:
+ *
+ * Enable MIPI calibration device
+ */
+ int (*enable)(struct tegra_mipi_device *device);
+
+ /**
+ * @disable:
+ *
+ * Disable MIPI calibration device
+ */
+ int (*disable)(struct tegra_mipi_device *device);
+
+ /**
+ * @start_calibration:
+ *
+ * Start MIPI calibration
+ */
+ int (*start_calibration)(struct tegra_mipi_device *device);
+
+ /**
+ * @finish_calibration:
+ *
+ * Finish MIPI calibration
+ */
+ int (*finish_calibration)(struct tegra_mipi_device *device);
+};
+
+int devm_tegra_mipi_add_provider(struct device *device, struct device_node *np,
+ const struct tegra_mipi_ops *ops);
+
+struct tegra_mipi_device *tegra_mipi_request(struct device *device,
+ struct device_node *np);
+void tegra_mipi_free(struct tegra_mipi_device *device);
+
+int tegra_mipi_enable(struct tegra_mipi_device *device);
+int tegra_mipi_disable(struct tegra_mipi_device *device);
+int tegra_mipi_start_calibration(struct tegra_mipi_device *device);
+int tegra_mipi_finish_calibration(struct tegra_mipi_device *device);
+
+#endif /* __TEGRA_MIPI_CAL_H_ */
--
2.48.1
On Wed, Oct 22, 2025 at 05:20:37PM +0300, Svyatoslav Ryhel wrote: > Convert existing MIPI code to use operation function pointers, a necessary > step for supporting Tegra20/Tegra30 SoCs. All common MIPI configuration > that is SoC-independent remains in mipi.c, while all SoC-specific code is > moved to tegra114-mipi.c (The naming matches the first SoC generation with > a dedicated calibration block). Shared structures and function calls are > placed into tegra-mipi-cal.h. > > Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com> > Acked-by: Mikko Perttunen <mperttunen@nvidia.com> > --- > drivers/gpu/drm/tegra/dsi.c | 1 + > drivers/gpu/host1x/Makefile | 1 + > drivers/gpu/host1x/mipi.c | 525 +++--------------------- > drivers/gpu/host1x/tegra114-mipi.c | 483 ++++++++++++++++++++++ > drivers/staging/media/tegra-video/csi.c | 1 + > include/linux/host1x.h | 10 - > include/linux/tegra-mipi-cal.h | 57 +++ > 7 files changed, 599 insertions(+), 479 deletions(-) > create mode 100644 drivers/gpu/host1x/tegra114-mipi.c > create mode 100644 include/linux/tegra-mipi-cal.h Not sure if I missed this earlier, but I don't understand why the code was moved around like this. tegra114-mipi.c now contains the code for all of Tegra114, Tegra124, Tegra132 and Tegra210, so the name doesn't make any more sense than the old name. Furthermore, moving the header file contents now also means that we have a cross-dependency within the series that makes this more difficult to merge. Obviously that's something we can make work, /if/ there's a need, but from what I can tell there's really no benefit to this extra churn. I also don't fully understand the benefit of converting the code into operation function pointers if we always use the same function pointers for all generations. Effectively this adds boilerplate and an extra indirection for no benefit at all. Splitting the "SoC specific" parts from the generic parts also now needlessly exports a symbol for absolutely no reason. Both files are linked into the same driver/module, there's no need for an exported symbol. The only slight bit of information that might justify this is the hint in this commit message that Tegra20/Tegra30 requires this. But I don't see patches for this anywhere, making it impossible to review this change. Looking at other parts of the series, and given it's spread across a large number of trees with compile-time dependencies between them, I think it would be better to split this up differently. I think it could be three series in total: one for the clock changes needed for this, another with all of the MIPI changes (in which case it would make sense to include the Tegra20/Tegra30 bits as well to actually show why the rework in this patch is needed) and a final one with the staging/media bits that make use if this all. Well, maybe also a fourth series that adds the DT changes needed to make it all work. I think if you avoid splitting the MIPI prototypes into tegra-mipi-cal.h you should be able to get rid of the cross-dependency. Clock changes should already be independent. Also, I think it's probably fine if we keep all of the MIPI driver code in the one file that it's currently in. This removes the need for the odd exported symbol. It's not a great deal of code, and I doubt that Tegra20/Tegra30 support would add significantly to it. Thierry
© 2016 - 2025 Red Hat, Inc.