Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
  3 *
  4 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
  5 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
  6 *
  7 * This program is free software; you can redistribute it and/or modify
  8 * it under the terms of the GNU General Public License version 2 as
  9 * published by the Free Software Foundation.
 10 */
 11
 12#include <linux/err.h>
 13#include <linux/io.h>
 14#include <linux/kernel.h>
 15#include <linux/mfd/syscon/exynos4-pmu.h>
 16#include <linux/module.h>
 17#include <linux/of.h>
 18#include <linux/of_address.h>
 19#include <linux/phy/phy.h>
 20#include <linux/platform_device.h>
 21#include <linux/regmap.h>
 22#include <linux/spinlock.h>
 23#include <linux/mfd/syscon.h>
 24
 25/* MIPI_PHYn_CONTROL reg. offset (for base address from ioremap): n = 0..1 */
 26#define EXYNOS_MIPI_PHY_CONTROL(n)	((n) * 4)
 27
 28enum exynos_mipi_phy_id {
 29	EXYNOS_MIPI_PHY_ID_CSIS0,
 30	EXYNOS_MIPI_PHY_ID_DSIM0,
 31	EXYNOS_MIPI_PHY_ID_CSIS1,
 32	EXYNOS_MIPI_PHY_ID_DSIM1,
 33	EXYNOS_MIPI_PHYS_NUM
 34};
 35
 36#define is_mipi_dsim_phy_id(id) \
 37	((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
 38
 39struct exynos_mipi_video_phy {
 40	struct video_phy_desc {
 41		struct phy *phy;
 42		unsigned int index;
 43	} phys[EXYNOS_MIPI_PHYS_NUM];
 44	spinlock_t slock;
 45	void __iomem *regs;
 46	struct regmap *regmap;
 47};
 48
 49static int __set_phy_state(struct exynos_mipi_video_phy *state,
 50			enum exynos_mipi_phy_id id, unsigned int on)
 51{
 52	const unsigned int offset = EXYNOS4_MIPI_PHY_CONTROL(id / 2);
 53	void __iomem *addr;
 54	u32 val, reset;
 55
 56	if (is_mipi_dsim_phy_id(id))
 57		reset = EXYNOS4_MIPI_PHY_MRESETN;
 58	else
 59		reset = EXYNOS4_MIPI_PHY_SRESETN;
 60
 61	spin_lock(&state->slock);
 62
 63	if (!IS_ERR(state->regmap)) {
 64		regmap_read(state->regmap, offset, &val);
 65		if (on)
 66			val |= reset;
 67		else
 68			val &= ~reset;
 69		regmap_write(state->regmap, offset, val);
 70		if (on)
 71			val |= EXYNOS4_MIPI_PHY_ENABLE;
 72		else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
 73			val &= ~EXYNOS4_MIPI_PHY_ENABLE;
 74		regmap_write(state->regmap, offset, val);
 75	} else {
 76		addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
 77
 78		val = readl(addr);
 79		if (on)
 80			val |= reset;
 81		else
 82			val &= ~reset;
 83		writel(val, addr);
 84		/* Clear ENABLE bit only if MRESETN, SRESETN bits are not set */
 85		if (on)
 86			val |= EXYNOS4_MIPI_PHY_ENABLE;
 87		else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
 88			val &= ~EXYNOS4_MIPI_PHY_ENABLE;
 89
 90		writel(val, addr);
 91	}
 92
 93	spin_unlock(&state->slock);
 94	return 0;
 95}
 96
 97#define to_mipi_video_phy(desc) \
 98	container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]);
 99
100static int exynos_mipi_video_phy_power_on(struct phy *phy)
101{
102	struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
103	struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
104
105	return __set_phy_state(state, phy_desc->index, 1);
106}
107
108static int exynos_mipi_video_phy_power_off(struct phy *phy)
109{
110	struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
111	struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
112
113	return __set_phy_state(state, phy_desc->index, 0);
114}
115
116static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
117					struct of_phandle_args *args)
118{
119	struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
120
121	if (WARN_ON(args->args[0] >= EXYNOS_MIPI_PHYS_NUM))
122		return ERR_PTR(-ENODEV);
123
124	return state->phys[args->args[0]].phy;
125}
126
127static const struct phy_ops exynos_mipi_video_phy_ops = {
128	.power_on	= exynos_mipi_video_phy_power_on,
129	.power_off	= exynos_mipi_video_phy_power_off,
130	.owner		= THIS_MODULE,
131};
132
133static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
134{
135	struct exynos_mipi_video_phy *state;
136	struct device *dev = &pdev->dev;
137	struct phy_provider *phy_provider;
138	unsigned int i;
139
140	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
141	if (!state)
142		return -ENOMEM;
143
144	state->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
145	if (IS_ERR(state->regmap)) {
146		struct resource *res;
147
148		dev_info(dev, "regmap lookup failed: %ld\n",
149			 PTR_ERR(state->regmap));
150
151		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
152		state->regs = devm_ioremap_resource(dev, res);
153		if (IS_ERR(state->regs))
154			return PTR_ERR(state->regs);
155	}
156
157	dev_set_drvdata(dev, state);
158	spin_lock_init(&state->slock);
159
160	for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
161		struct phy *phy = devm_phy_create(dev, NULL,
162						  &exynos_mipi_video_phy_ops);
163		if (IS_ERR(phy)) {
164			dev_err(dev, "failed to create PHY %d\n", i);
165			return PTR_ERR(phy);
166		}
167
168		state->phys[i].phy = phy;
169		state->phys[i].index = i;
170		phy_set_drvdata(phy, &state->phys[i]);
171	}
172
173	phy_provider = devm_of_phy_provider_register(dev,
174					exynos_mipi_video_phy_xlate);
175
176	return PTR_ERR_OR_ZERO(phy_provider);
177}
178
179static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
180	{ .compatible = "samsung,s5pv210-mipi-video-phy" },
181	{ },
182};
183MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);
184
185static struct platform_driver exynos_mipi_video_phy_driver = {
186	.probe	= exynos_mipi_video_phy_probe,
187	.driver = {
188		.of_match_table	= exynos_mipi_video_phy_of_match,
189		.name  = "exynos-mipi-video-phy",
190	}
191};
192module_platform_driver(exynos_mipi_video_phy_driver);
193
194MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
195MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
196MODULE_LICENSE("GPL v2");