brintos

brintos / linux-shallow public Read only

0
0
Text · 14.3 KiB · e51b43d Raw
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