Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpu: host1x: Add MIPI pad calibration support

This driver adds support to perform calibration of the MIPI pads for CSI
and DSI.

Signed-off-by: Thierry Reding <treding@nvidia.com>

+294 -4
+1
drivers/gpu/host1x/Makefile
··· 7 7 channel.o \ 8 8 job.o \ 9 9 debug.o \ 10 + mipi.o \ 10 11 hw/host1x01.o \ 11 12 hw/host1x02.o 12 13
+13 -4
drivers/gpu/host1x/dev.c
··· 210 210 return err; 211 211 212 212 err = platform_driver_register(&tegra_host1x_driver); 213 - if (err < 0) { 214 - host1x_bus_exit(); 215 - return err; 216 - } 213 + if (err < 0) 214 + goto unregister_bus; 215 + 216 + err = platform_driver_register(&tegra_mipi_driver); 217 + if (err < 0) 218 + goto unregister_host1x; 217 219 218 220 return 0; 221 + 222 + unregister_host1x: 223 + platform_driver_unregister(&tegra_host1x_driver); 224 + unregister_bus: 225 + host1x_bus_exit(); 226 + return err; 219 227 } 220 228 module_init(tegra_host1x_init); 221 229 222 230 static void __exit tegra_host1x_exit(void) 223 231 { 232 + platform_driver_unregister(&tegra_mipi_driver); 224 233 platform_driver_unregister(&tegra_host1x_driver); 225 234 host1x_bus_exit(); 226 235 }
+2
drivers/gpu/host1x/dev.h
··· 306 306 host->debug_op->show_mlocks(host, o); 307 307 } 308 308 309 + extern struct platform_driver tegra_mipi_driver; 310 + 309 311 #endif
+272
drivers/gpu/host1x/mipi.c
··· 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/io.h> 26 + #include <linux/of_platform.h> 27 + #include <linux/platform_device.h> 28 + #include <linux/slab.h> 29 + 30 + #define MIPI_CAL_CTRL 0x00 31 + #define MIPI_CAL_CTRL_START (1 << 0) 32 + 33 + #define MIPI_CAL_AUTOCAL_CTRL 0x01 34 + 35 + #define MIPI_CAL_STATUS 0x02 36 + #define MIPI_CAL_STATUS_DONE (1 << 16) 37 + #define MIPI_CAL_STATUS_ACTIVE (1 << 0) 38 + 39 + #define MIPI_CAL_CONFIG_CSIA 0x05 40 + #define MIPI_CAL_CONFIG_CSIB 0x06 41 + #define MIPI_CAL_CONFIG_CSIC 0x07 42 + #define MIPI_CAL_CONFIG_CSID 0x08 43 + #define MIPI_CAL_CONFIG_CSIE 0x09 44 + #define MIPI_CAL_CONFIG_DSIA 0x0e 45 + #define MIPI_CAL_CONFIG_DSIB 0x0f 46 + #define MIPI_CAL_CONFIG_DSIC 0x10 47 + #define MIPI_CAL_CONFIG_DSID 0x11 48 + 49 + #define MIPI_CAL_CONFIG_SELECT (1 << 21) 50 + #define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16) 51 + #define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8) 52 + #define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0) 53 + 54 + #define MIPI_CAL_BIAS_PAD_CFG0 0x16 55 + #define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1) 56 + #define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0) 57 + 58 + #define MIPI_CAL_BIAS_PAD_CFG1 0x17 59 + 60 + #define MIPI_CAL_BIAS_PAD_CFG2 0x18 61 + #define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1) 62 + 63 + static const struct module { 64 + unsigned long reg; 65 + } modules[] = { 66 + { .reg = MIPI_CAL_CONFIG_CSIA }, 67 + { .reg = MIPI_CAL_CONFIG_CSIB }, 68 + { .reg = MIPI_CAL_CONFIG_CSIC }, 69 + { .reg = MIPI_CAL_CONFIG_CSID }, 70 + { .reg = MIPI_CAL_CONFIG_CSIE }, 71 + { .reg = MIPI_CAL_CONFIG_DSIA }, 72 + { .reg = MIPI_CAL_CONFIG_DSIB }, 73 + { .reg = MIPI_CAL_CONFIG_DSIC }, 74 + { .reg = MIPI_CAL_CONFIG_DSID }, 75 + }; 76 + 77 + struct tegra_mipi { 78 + void __iomem *regs; 79 + struct mutex lock; 80 + struct clk *clk; 81 + }; 82 + 83 + struct tegra_mipi_device { 84 + struct platform_device *pdev; 85 + struct tegra_mipi *mipi; 86 + struct device *device; 87 + unsigned long pads; 88 + }; 89 + 90 + static inline unsigned long tegra_mipi_readl(struct tegra_mipi *mipi, 91 + unsigned long reg) 92 + { 93 + return readl(mipi->regs + (reg << 2)); 94 + } 95 + 96 + static inline void tegra_mipi_writel(struct tegra_mipi *mipi, 97 + unsigned long value, unsigned long reg) 98 + { 99 + writel(value, mipi->regs + (reg << 2)); 100 + } 101 + 102 + struct tegra_mipi_device *tegra_mipi_request(struct device *device) 103 + { 104 + struct device_node *np = device->of_node; 105 + struct tegra_mipi_device *dev; 106 + struct of_phandle_args args; 107 + int err; 108 + 109 + err = of_parse_phandle_with_args(np, "nvidia,mipi-calibrate", 110 + "#nvidia,mipi-calibrate-cells", 0, 111 + &args); 112 + if (err < 0) 113 + return ERR_PTR(err); 114 + 115 + dev = kzalloc(sizeof(*dev), GFP_KERNEL); 116 + if (!dev) { 117 + of_node_put(args.np); 118 + err = -ENOMEM; 119 + goto out; 120 + } 121 + 122 + dev->pdev = of_find_device_by_node(args.np); 123 + if (!dev->pdev) { 124 + of_node_put(args.np); 125 + err = -ENODEV; 126 + goto free; 127 + } 128 + 129 + of_node_put(args.np); 130 + 131 + dev->mipi = platform_get_drvdata(dev->pdev); 132 + if (!dev->mipi) { 133 + err = -EPROBE_DEFER; 134 + goto pdev_put; 135 + } 136 + 137 + dev->pads = args.args[0]; 138 + dev->device = device; 139 + 140 + return dev; 141 + 142 + pdev_put: 143 + platform_device_put(dev->pdev); 144 + free: 145 + kfree(dev); 146 + out: 147 + return ERR_PTR(err); 148 + } 149 + EXPORT_SYMBOL(tegra_mipi_request); 150 + 151 + void tegra_mipi_free(struct tegra_mipi_device *device) 152 + { 153 + platform_device_put(device->pdev); 154 + kfree(device); 155 + } 156 + EXPORT_SYMBOL(tegra_mipi_free); 157 + 158 + static int tegra_mipi_wait(struct tegra_mipi *mipi) 159 + { 160 + unsigned long timeout = jiffies + msecs_to_jiffies(250); 161 + unsigned long value; 162 + 163 + while (time_before(jiffies, timeout)) { 164 + value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS); 165 + if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 && 166 + (value & MIPI_CAL_STATUS_DONE) != 0) 167 + return 0; 168 + 169 + usleep_range(10, 50); 170 + } 171 + 172 + return -ETIMEDOUT; 173 + } 174 + 175 + int tegra_mipi_calibrate(struct tegra_mipi_device *device) 176 + { 177 + unsigned long value; 178 + unsigned int i; 179 + int err; 180 + 181 + err = clk_enable(device->mipi->clk); 182 + if (err < 0) 183 + return err; 184 + 185 + mutex_lock(&device->mipi->lock); 186 + 187 + value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0); 188 + value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; 189 + value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; 190 + tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0); 191 + 192 + value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2); 193 + value &= ~MIPI_CAL_BIAS_PAD_PDVREG; 194 + tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2); 195 + 196 + for (i = 0; i < ARRAY_SIZE(modules); i++) { 197 + if (device->pads & BIT(i)) 198 + value = MIPI_CAL_CONFIG_SELECT | 199 + MIPI_CAL_CONFIG_HSPDOS(0) | 200 + MIPI_CAL_CONFIG_HSPUOS(4) | 201 + MIPI_CAL_CONFIG_TERMOS(5); 202 + else 203 + value = 0; 204 + 205 + tegra_mipi_writel(device->mipi, value, modules[i].reg); 206 + } 207 + 208 + tegra_mipi_writel(device->mipi, MIPI_CAL_CTRL_START, MIPI_CAL_CTRL); 209 + 210 + err = tegra_mipi_wait(device->mipi); 211 + 212 + mutex_unlock(&device->mipi->lock); 213 + clk_disable(device->mipi->clk); 214 + 215 + return err; 216 + } 217 + EXPORT_SYMBOL(tegra_mipi_calibrate); 218 + 219 + static int tegra_mipi_probe(struct platform_device *pdev) 220 + { 221 + struct tegra_mipi *mipi; 222 + struct resource *res; 223 + int err; 224 + 225 + mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL); 226 + if (!mipi) 227 + return -ENOMEM; 228 + 229 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 230 + mipi->regs = devm_ioremap_resource(&pdev->dev, res); 231 + if (IS_ERR(mipi->regs)) 232 + return PTR_ERR(mipi->regs); 233 + 234 + mutex_init(&mipi->lock); 235 + 236 + mipi->clk = devm_clk_get(&pdev->dev, NULL); 237 + if (IS_ERR(mipi->clk)) { 238 + dev_err(&pdev->dev, "failed to get clock\n"); 239 + return PTR_ERR(mipi->clk); 240 + } 241 + 242 + err = clk_prepare(mipi->clk); 243 + if (err < 0) 244 + return err; 245 + 246 + platform_set_drvdata(pdev, mipi); 247 + 248 + return 0; 249 + } 250 + 251 + static int tegra_mipi_remove(struct platform_device *pdev) 252 + { 253 + struct tegra_mipi *mipi = platform_get_drvdata(pdev); 254 + 255 + clk_unprepare(mipi->clk); 256 + 257 + return 0; 258 + } 259 + 260 + static struct of_device_id tegra_mipi_of_match[] = { 261 + { .compatible = "nvidia,tegra114-mipi", }, 262 + { }, 263 + }; 264 + 265 + struct platform_driver tegra_mipi_driver = { 266 + .driver = { 267 + .name = "tegra-mipi", 268 + .of_match_table = tegra_mipi_of_match, 269 + }, 270 + .probe = tegra_mipi_probe, 271 + .remove = tegra_mipi_remove, 272 + };
+6
include/linux/host1x.h
··· 281 281 int host1x_client_register(struct host1x_client *client); 282 282 int host1x_client_unregister(struct host1x_client *client); 283 283 284 + struct tegra_mipi_device; 285 + 286 + struct tegra_mipi_device *tegra_mipi_request(struct device *device); 287 + void tegra_mipi_free(struct tegra_mipi_device *device); 288 + int tegra_mipi_calibrate(struct tegra_mipi_device *device); 289 + 284 290 #endif