aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
authorSylwester Nawrocki <sylvester.nawrocki@gmail.com>2013-10-16 12:28:10 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-10-16 16:48:08 -0400
commit069d2e26e9d64ba7e04c2c255efdfd1238789ca6 (patch)
tree2c21fb5f712d26a8980dfe3329b42f5f47e853e7 /drivers/phy
parente92aee330837e4911553761490a8fb843f2053a6 (diff)
phy: Add driver for Exynos MIPI CSIS/DSIM DPHYs
Add a PHY provider driver for the Samsung S5P/Exynos SoC MIPI CSI-2 receiver and MIPI DSI transmitter DPHYs. Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig6
-rw-r--r--drivers/phy/Makefile7
-rw-r--r--drivers/phy/phy-exynos-mipi-video.c176
3 files changed, 186 insertions, 3 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index ac239aca77ec..0062d7e60dbe 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -15,6 +15,12 @@ config GENERIC_PHY
15 phy users can obtain reference to the PHY. All the users of this 15 phy users can obtain reference to the PHY. All the users of this
16 framework should select this config. 16 framework should select this config.
17 17
18config PHY_EXYNOS_MIPI_VIDEO
19 tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
20 help
21 Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
22 and EXYNOS SoCs.
23
18config OMAP_USB2 24config OMAP_USB2
19 tristate "OMAP USB2 PHY Driver" 25 tristate "OMAP USB2 PHY Driver"
20 depends on ARCH_OMAP2PLUS 26 depends on ARCH_OMAP2PLUS
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 0dd8a9834548..6344053661f4 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -2,6 +2,7 @@
2# Makefile for the phy drivers. 2# Makefile for the phy drivers.
3# 3#
4 4
5obj-$(CONFIG_GENERIC_PHY) += phy-core.o 5obj-$(CONFIG_GENERIC_PHY) += phy-core.o
6obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o 6obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
7obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o 7obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
8obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c
new file mode 100644
index 000000000000..b73b86ab4697
--- /dev/null
+++ b/drivers/phy/phy-exynos-mipi-video.c
@@ -0,0 +1,176 @@
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/io.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/of_address.h>
17#include <linux/phy/phy.h>
18#include <linux/platform_device.h>
19#include <linux/spinlock.h>
20
21/* MIPI_PHYn_CONTROL register offset: n = 0..1 */
22#define EXYNOS_MIPI_PHY_CONTROL(n) ((n) * 4)
23#define EXYNOS_MIPI_PHY_ENABLE (1 << 0)
24#define EXYNOS_MIPI_PHY_SRESETN (1 << 1)
25#define EXYNOS_MIPI_PHY_MRESETN (1 << 2)
26#define EXYNOS_MIPI_PHY_RESET_MASK (3 << 1)
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 spinlock_t slock;
41 struct video_phy_desc {
42 struct phy *phy;
43 unsigned int index;
44 } phys[EXYNOS_MIPI_PHYS_NUM];
45 void __iomem *regs;
46};
47
48static int __set_phy_state(struct exynos_mipi_video_phy *state,
49 enum exynos_mipi_phy_id id, unsigned int on)
50{
51 void __iomem *addr;
52 u32 reg, reset;
53
54 addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
55
56 if (is_mipi_dsim_phy_id(id))
57 reset = EXYNOS_MIPI_PHY_MRESETN;
58 else
59 reset = EXYNOS_MIPI_PHY_SRESETN;
60
61 spin_lock(&state->slock);
62 reg = readl(addr);
63 if (on)
64 reg |= reset;
65 else
66 reg &= ~reset;
67 writel(reg, addr);
68
69 /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */
70 if (on)
71 reg |= EXYNOS_MIPI_PHY_ENABLE;
72 else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK))
73 reg &= ~EXYNOS_MIPI_PHY_ENABLE;
74
75 writel(reg, addr);
76 spin_unlock(&state->slock);
77 return 0;
78}
79
80#define to_mipi_video_phy(desc) \
81 container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]);
82
83static int exynos_mipi_video_phy_power_on(struct phy *phy)
84{
85 struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
86 struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
87
88 return __set_phy_state(state, phy_desc->index, 1);
89}
90
91static int exynos_mipi_video_phy_power_off(struct phy *phy)
92{
93 struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
94 struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
95
96 return __set_phy_state(state, phy_desc->index, 1);
97}
98
99static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
100 struct of_phandle_args *args)
101{
102 struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
103
104 if (WARN_ON(args->args[0] > EXYNOS_MIPI_PHYS_NUM))
105 return ERR_PTR(-ENODEV);
106
107 return state->phys[args->args[0]].phy;
108}
109
110static struct phy_ops exynos_mipi_video_phy_ops = {
111 .power_on = exynos_mipi_video_phy_power_on,
112 .power_off = exynos_mipi_video_phy_power_off,
113 .owner = THIS_MODULE,
114};
115
116static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
117{
118 struct exynos_mipi_video_phy *state;
119 struct device *dev = &pdev->dev;
120 struct resource *res;
121 struct phy_provider *phy_provider;
122 unsigned int i;
123
124 state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
125 if (!state)
126 return -ENOMEM;
127
128 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
129
130 state->regs = devm_ioremap_resource(dev, res);
131 if (IS_ERR(state->regs))
132 return PTR_ERR(state->regs);
133
134 dev_set_drvdata(dev, state);
135 spin_lock_init(&state->slock);
136
137 phy_provider = devm_of_phy_provider_register(dev,
138 exynos_mipi_video_phy_xlate);
139 if (IS_ERR(phy_provider))
140 return PTR_ERR(phy_provider);
141
142 for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
143 struct phy *phy = devm_phy_create(dev,
144 &exynos_mipi_video_phy_ops, NULL);
145 if (IS_ERR(phy)) {
146 dev_err(dev, "failed to create PHY %d\n", i);
147 return PTR_ERR(phy);
148 }
149
150 state->phys[i].phy = phy;
151 state->phys[i].index = i;
152 phy_set_drvdata(phy, &state->phys[i]);
153 }
154
155 return 0;
156}
157
158static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
159 { .compatible = "samsung,s5pv210-mipi-video-phy" },
160 { },
161};
162MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);
163
164static struct platform_driver exynos_mipi_video_phy_driver = {
165 .probe = exynos_mipi_video_phy_probe,
166 .driver = {
167 .of_match_table = exynos_mipi_video_phy_of_match,
168 .name = "exynos-mipi-video-phy",
169 .owner = THIS_MODULE,
170 }
171};
172module_platform_driver(exynos_mipi_video_phy_driver);
173
174MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
175MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
176MODULE_LICENSE("GPL v2");