Skip to content

Commit 706a565

Browse files
Luis Oliveirabala-gunasundar
authored andcommitted
media: platform: dwc: Add DW MIPI DPHY Rx driver
Add of Synopsys MIPI D-PHY in RX mode support. Separated in the implementation are platform dependent probing functions. Signed-off-by: Luis Oliveira <luis.oliveira@synopsys.com> [eugen.hristev@microchip.com: add stop state check : After coming out of reset, the PHY must have the lanes in stop state. Wait for stop state to be active, and if it's not, print out a message.] Signed-off-by: Eugen Hristev <eugen.hristev@microchip.com> [luis.oliveira@synopsys.com: Add platform data support to D-Phy] Signed-off-by: Luis Oliveira <luis.oliveira@synopsys.com>
1 parent 19773aa commit 706a565

8 files changed

Lines changed: 1356 additions & 1 deletion

File tree

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20837,7 +20837,9 @@ S: Maintained
2083720837
T: git git://linuxtv.org/media_tree.git
2083820838
F: drivers/media/platform/dwc
2083920839
F: Documentation/devicetree/bindings/media/snps,dw-csi.yaml
20840+
F: Documentation/devicetree/bindings/phy/snps,dw-dphy-rx.txt
2084020841
F: include/media/dwc/dw-csi-data.h
20842+
F: include/media/dwc/dw-dphy-data.h
2084120843

2084220844
SYNOPSYS DESIGNWARE DMAC DRIVER
2084320845
M: Viresh Kumar <vireshk@kernel.org>

drivers/media/platform/dwc/Kconfig

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0
22
#
33
# Synopsys DWC Platform drivers
4-
# Drivers here are currently for MIPI CSI-2 support
4+
# Drivers here are currently for MIPI CSI-2 and MIPI DPHY support
55

66
config DWC_MIPI_CSI2_HOST
77
tristate "Synopsys DesignWare CSI-2 Host Controller support"
@@ -17,3 +17,25 @@ config DWC_MIPI_CSI2_HOST
1717
If you have a controller with this interface, say Y.
1818

1919
If unsure, say N.
20+
21+
config DWC_MIPI_DPHY_GEN3
22+
tristate "DesignWare platform support using a Gen3 D-PHY"
23+
select GENERIC_PHY
24+
help
25+
Synopsys MIPI D-PHY Generation 3 reference driver. This driver supports
26+
all Generation 3 D-PHYs. Choose Y or M if you have a platform with this
27+
block.
28+
29+
If unsure, say N.
30+
31+
if DWC_MIPI_DPHY_GEN3
32+
33+
config DWC_MIPI_TC_DPHY_GEN3
34+
bool "Platform support using a Synopsys Test Chip"
35+
help
36+
Synopsys Test Chip is for prototyping purposes. This enables extra
37+
features that exist only in prototyping and/or for debug purposes.
38+
39+
If unsure, say N.
40+
41+
endif # DWC_MIPI_DPHY_GEN3

drivers/media/platform/dwc/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@ ifeq ($(CONFIG_DWC_MIPI_TC_DPHY_GEN3),y)
77
dw-csi-objs += dw-csi-sysfs.o
88
endif
99
obj-$(CONFIG_DWC_MIPI_CSI2_HOST) += dw-csi.o
10+
11+
dw-dphy-objs := dw-dphy-plat.o dw-dphy-rx.o
12+
ifeq ($(CONFIG_DWC_MIPI_TC_DPHY_GEN3),y)
13+
dw-dphy-objs += dw-dphy-sysfs.o
14+
endif
15+
obj-$(CONFIG_DWC_MIPI_DPHY_GEN3) += dw-dphy.o
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates.
4+
*
5+
* Synopsys DesignWare MIPI D-PHY controller driver.
6+
* Platform driver
7+
*
8+
* Author: Luis Oliveira <luis.oliveira@synopsys.com>
9+
*/
10+
11+
#include <media/dwc/dw-dphy-data.h>
12+
#include <media/dwc/dw-csi-data.h>
13+
14+
#include "dw-dphy-rx.h"
15+
16+
static struct phy_ops dw_dphy_ops = {
17+
.init = dw_dphy_init,
18+
.reset = dw_dphy_reset,
19+
.power_on = dw_dphy_power_on,
20+
.power_off = dw_dphy_power_off,
21+
.owner = THIS_MODULE,
22+
};
23+
24+
static struct phy_provider *phy_provider;
25+
26+
#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3)
27+
static u8 get_config_8l(struct device *dev, struct dw_dphy_rx *dphy)
28+
{
29+
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
30+
dphy->config_8l = of_get_gpio(dev->of_node, 0);
31+
if (!gpio_is_valid(dphy->config_8l)) {
32+
dev_warn(dev,
33+
"failed to parse 8l config, default is 0\n");
34+
dphy->config_8l = 0;
35+
}
36+
} else {
37+
struct dw_phy_pdata *pdata = dev->platform_data;
38+
39+
dphy->config_8l = pdata->config_8l;
40+
}
41+
return dphy->config_8l;
42+
}
43+
#endif
44+
static int get_resources(struct device *dev, struct dw_dphy_rx *dphy)
45+
{
46+
int ret = 0;
47+
48+
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
49+
if (of_property_read_u32(dev->of_node, "snps,dphy-frequency",
50+
&dphy->dphy_freq)) {
51+
dev_err(dev, "failed to find dphy frequency\n");
52+
ret = -EINVAL;
53+
}
54+
if (of_property_read_u32(dev->of_node, "bus-width",
55+
&dphy->dphy_te_len)) {
56+
dev_err(dev, "failed to find dphy te length\n");
57+
ret = -EINVAL;
58+
}
59+
if (of_property_read_u32(dev->of_node, "snps,phy_type",
60+
&dphy->phy_type)) {
61+
dev_err(dev, "failed to find dphy type\n");
62+
ret = -EINVAL;
63+
}
64+
} else {
65+
struct dw_phy_pdata *pdata = dev->platform_data;
66+
67+
dphy->dphy_freq = pdata->dphy_frequency;
68+
dphy->dphy_te_len = pdata->dphy_te_len;
69+
dphy->dphy_gen = pdata->dphy_gen;
70+
}
71+
dev_set_drvdata(dev, dphy);
72+
73+
return ret;
74+
}
75+
76+
static int phy_register(struct device *dev)
77+
{
78+
int ret = 0;
79+
80+
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
81+
phy_provider = devm_of_phy_provider_register(dev,
82+
dw_dphy_xlate);
83+
if (IS_ERR(phy_provider)) {
84+
dev_err(dev, "error getting phy provider\n");
85+
ret = PTR_ERR(phy_provider);
86+
}
87+
} else {
88+
struct dw_phy_pdata *pdata = dev->platform_data;
89+
struct dw_dphy_rx *dphy = dev_get_drvdata(dev);
90+
91+
ret = phy_create_lookup(dphy->phy,
92+
phys[pdata->id].name,
93+
csis[pdata->id].name);
94+
if (ret)
95+
dev_err(dev, "Failed to create dphy lookup\n");
96+
else
97+
dev_warn(dev,
98+
"Created dphy lookup [%s] --> [%s]\n",
99+
phys[pdata->id].name, csis[pdata->id].name);
100+
}
101+
return ret;
102+
}
103+
104+
static void phy_unregister(struct device *dev)
105+
{
106+
if (!dev->of_node) {
107+
struct dw_dphy_rx *dphy = dev_get_drvdata(dev);
108+
109+
phy_remove_lookup(dphy->phy, "dw-dphy", "dw-csi");
110+
}
111+
}
112+
113+
static int dw_dphy_rx_probe(struct platform_device *pdev)
114+
{
115+
struct device *dev = &pdev->dev;
116+
struct dw_dphy_rx *dphy;
117+
struct resource *res;
118+
119+
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
120+
if (!dphy)
121+
return -ENOMEM;
122+
123+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
124+
dphy->base_address = devm_ioremap(&pdev->dev,
125+
res->start, resource_size(res));
126+
if (IS_ERR(dphy->base_address)) {
127+
dev_err(&pdev->dev, "error requesting base address\n");
128+
return PTR_ERR(dphy->base_address);
129+
}
130+
131+
#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3)
132+
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
133+
134+
dphy->dphy1_if_addr = devm_ioremap_resource(&pdev->dev, res);
135+
if (IS_ERR(dphy->dphy1_if_addr)) {
136+
dev_err(&pdev->dev, "error requesting dphy 1 if regbank\n");
137+
return PTR_ERR(dphy->dphy1_if_addr);
138+
}
139+
140+
dphy->max_lanes =
141+
dw_dphy_if_read_msk(dphy, DPHYID, DPHY_ID_LANE_SUPPORT, 4);
142+
143+
dphy->dphy_gen = dw_dphy_if_read_msk(dphy, DPHYID, DPHY_ID_GEN, 4);
144+
145+
dev_info(&pdev->dev, "DPHY GEN %s with maximum %s lanes\n",
146+
dphy->dphy_gen == GEN3 ? "3" : "2",
147+
dphy->max_lanes == CTRL_8_LANES ? "8" : "4");
148+
149+
if (dphy->max_lanes == CTRL_8_LANES) {
150+
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
151+
dphy->dphy2_if_addr =
152+
devm_ioremap(&pdev->dev,
153+
res->start, resource_size(res));
154+
155+
if (IS_ERR(dphy->dphy2_if_addr)) {
156+
dev_err(&pdev->dev,
157+
"error requesting dphy 2 if regbank\n");
158+
return PTR_ERR(dphy->dphy2_if_addr);
159+
}
160+
dphy->config_8l = get_config_8l(&pdev->dev, dphy);
161+
}
162+
#endif
163+
if (get_resources(dev, dphy)) {
164+
dev_err(dev, "failed to parse PHY resources\n");
165+
return -EINVAL;
166+
}
167+
168+
dphy->phy = devm_phy_create(dev, NULL, &dw_dphy_ops);
169+
if (IS_ERR(dphy->phy)) {
170+
dev_err(dev, "failed to create PHY\n");
171+
return PTR_ERR(dphy->phy);
172+
}
173+
174+
platform_set_drvdata(pdev, dphy);
175+
phy_set_drvdata(dphy->phy, dphy);
176+
177+
if (phy_register(dev)) {
178+
dev_err(dev, "failed to register PHY\n");
179+
return -EINVAL;
180+
}
181+
182+
dphy->lp_time = 1000; /* 1000 ns */
183+
dphy->lanes_config = dw_dphy_setup_config(dphy);
184+
185+
dev_info(dev, "Probing dphy finished\n");
186+
#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3)
187+
dw_dphy_create_capabilities_sysfs(pdev);
188+
#endif
189+
190+
return 0;
191+
}
192+
193+
static int dw_dphy_rx_remove(struct platform_device *pdev)
194+
{
195+
phy_unregister(&pdev->dev);
196+
197+
return 0;
198+
}
199+
200+
#if IS_ENABLED(CONFIG_OF)
201+
static const struct of_device_id dw_dphy_rx_of_match[] = {
202+
{ .compatible = "snps,dw-dphy-rx" },
203+
{},
204+
};
205+
206+
MODULE_DEVICE_TABLE(of, dw_dphy_rx_of_match);
207+
#endif
208+
209+
static struct platform_driver dw_dphy_rx_driver = {
210+
.probe = dw_dphy_rx_probe,
211+
.remove = dw_dphy_rx_remove,
212+
.driver = {
213+
#if IS_ENABLED(CONFIG_OF)
214+
.of_match_table = of_match_ptr(dw_dphy_rx_of_match),
215+
#endif
216+
.name = "dw-dphy",
217+
.owner = THIS_MODULE,
218+
}
219+
};
220+
module_platform_driver(dw_dphy_rx_driver);
221+
222+
MODULE_DESCRIPTION("Synopsys DesignWare MIPI DPHY Rx driver");
223+
MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>");
224+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)