diff options
Diffstat (limited to 'drivers/gpu/drm/msm/dsi/dsi.c')
-rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c new file mode 100644 index 000000000000..28d1f95a90cc --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/dsi.c | |||
@@ -0,0 +1,212 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include "dsi.h" | ||
15 | |||
16 | struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi) | ||
17 | { | ||
18 | if (!msm_dsi || !msm_dsi->panel) | ||
19 | return NULL; | ||
20 | |||
21 | return (msm_dsi->panel_flags & MIPI_DSI_MODE_VIDEO) ? | ||
22 | msm_dsi->encoders[MSM_DSI_VIDEO_ENCODER_ID] : | ||
23 | msm_dsi->encoders[MSM_DSI_CMD_ENCODER_ID]; | ||
24 | } | ||
25 | |||
26 | static void dsi_destroy(struct msm_dsi *msm_dsi) | ||
27 | { | ||
28 | if (!msm_dsi) | ||
29 | return; | ||
30 | |||
31 | msm_dsi_manager_unregister(msm_dsi); | ||
32 | if (msm_dsi->host) { | ||
33 | msm_dsi_host_destroy(msm_dsi->host); | ||
34 | msm_dsi->host = NULL; | ||
35 | } | ||
36 | |||
37 | platform_set_drvdata(msm_dsi->pdev, NULL); | ||
38 | } | ||
39 | |||
40 | static struct msm_dsi *dsi_init(struct platform_device *pdev) | ||
41 | { | ||
42 | struct msm_dsi *msm_dsi = NULL; | ||
43 | int ret; | ||
44 | |||
45 | if (!pdev) { | ||
46 | dev_err(&pdev->dev, "no dsi device\n"); | ||
47 | ret = -ENXIO; | ||
48 | goto fail; | ||
49 | } | ||
50 | |||
51 | msm_dsi = devm_kzalloc(&pdev->dev, sizeof(*msm_dsi), GFP_KERNEL); | ||
52 | if (!msm_dsi) { | ||
53 | ret = -ENOMEM; | ||
54 | goto fail; | ||
55 | } | ||
56 | DBG("dsi probed=%p", msm_dsi); | ||
57 | |||
58 | msm_dsi->pdev = pdev; | ||
59 | platform_set_drvdata(pdev, msm_dsi); | ||
60 | |||
61 | /* Init dsi host */ | ||
62 | ret = msm_dsi_host_init(msm_dsi); | ||
63 | if (ret) | ||
64 | goto fail; | ||
65 | |||
66 | /* Register to dsi manager */ | ||
67 | ret = msm_dsi_manager_register(msm_dsi); | ||
68 | if (ret) | ||
69 | goto fail; | ||
70 | |||
71 | return msm_dsi; | ||
72 | |||
73 | fail: | ||
74 | if (msm_dsi) | ||
75 | dsi_destroy(msm_dsi); | ||
76 | |||
77 | return ERR_PTR(ret); | ||
78 | } | ||
79 | |||
80 | static int dsi_bind(struct device *dev, struct device *master, void *data) | ||
81 | { | ||
82 | struct drm_device *drm = dev_get_drvdata(master); | ||
83 | struct msm_drm_private *priv = drm->dev_private; | ||
84 | struct platform_device *pdev = to_platform_device(dev); | ||
85 | struct msm_dsi *msm_dsi; | ||
86 | |||
87 | DBG(""); | ||
88 | msm_dsi = dsi_init(pdev); | ||
89 | if (IS_ERR(msm_dsi)) | ||
90 | return PTR_ERR(msm_dsi); | ||
91 | |||
92 | priv->dsi[msm_dsi->id] = msm_dsi; | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static void dsi_unbind(struct device *dev, struct device *master, | ||
98 | void *data) | ||
99 | { | ||
100 | struct drm_device *drm = dev_get_drvdata(master); | ||
101 | struct msm_drm_private *priv = drm->dev_private; | ||
102 | struct msm_dsi *msm_dsi = dev_get_drvdata(dev); | ||
103 | int id = msm_dsi->id; | ||
104 | |||
105 | if (priv->dsi[id]) { | ||
106 | dsi_destroy(msm_dsi); | ||
107 | priv->dsi[id] = NULL; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | static const struct component_ops dsi_ops = { | ||
112 | .bind = dsi_bind, | ||
113 | .unbind = dsi_unbind, | ||
114 | }; | ||
115 | |||
116 | static int dsi_dev_probe(struct platform_device *pdev) | ||
117 | { | ||
118 | return component_add(&pdev->dev, &dsi_ops); | ||
119 | } | ||
120 | |||
121 | static int dsi_dev_remove(struct platform_device *pdev) | ||
122 | { | ||
123 | DBG(""); | ||
124 | component_del(&pdev->dev, &dsi_ops); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static const struct of_device_id dt_match[] = { | ||
129 | { .compatible = "qcom,mdss-dsi-ctrl" }, | ||
130 | {} | ||
131 | }; | ||
132 | |||
133 | static struct platform_driver dsi_driver = { | ||
134 | .probe = dsi_dev_probe, | ||
135 | .remove = dsi_dev_remove, | ||
136 | .driver = { | ||
137 | .name = "msm_dsi", | ||
138 | .of_match_table = dt_match, | ||
139 | }, | ||
140 | }; | ||
141 | |||
142 | void __init msm_dsi_register(void) | ||
143 | { | ||
144 | DBG(""); | ||
145 | platform_driver_register(&dsi_driver); | ||
146 | } | ||
147 | |||
148 | void __exit msm_dsi_unregister(void) | ||
149 | { | ||
150 | DBG(""); | ||
151 | platform_driver_unregister(&dsi_driver); | ||
152 | } | ||
153 | |||
154 | int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, | ||
155 | struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]) | ||
156 | { | ||
157 | struct msm_drm_private *priv = dev->dev_private; | ||
158 | int ret, i; | ||
159 | |||
160 | if (WARN_ON(!encoders[MSM_DSI_VIDEO_ENCODER_ID] || | ||
161 | !encoders[MSM_DSI_CMD_ENCODER_ID])) | ||
162 | return -EINVAL; | ||
163 | |||
164 | msm_dsi->dev = dev; | ||
165 | |||
166 | ret = msm_dsi_host_modeset_init(msm_dsi->host, dev); | ||
167 | if (ret) { | ||
168 | dev_err(dev->dev, "failed to modeset init host: %d\n", ret); | ||
169 | goto fail; | ||
170 | } | ||
171 | |||
172 | msm_dsi->bridge = msm_dsi_manager_bridge_init(msm_dsi->id); | ||
173 | if (IS_ERR(msm_dsi->bridge)) { | ||
174 | ret = PTR_ERR(msm_dsi->bridge); | ||
175 | dev_err(dev->dev, "failed to create dsi bridge: %d\n", ret); | ||
176 | msm_dsi->bridge = NULL; | ||
177 | goto fail; | ||
178 | } | ||
179 | |||
180 | msm_dsi->connector = msm_dsi_manager_connector_init(msm_dsi->id); | ||
181 | if (IS_ERR(msm_dsi->connector)) { | ||
182 | ret = PTR_ERR(msm_dsi->connector); | ||
183 | dev_err(dev->dev, "failed to create dsi connector: %d\n", ret); | ||
184 | msm_dsi->connector = NULL; | ||
185 | goto fail; | ||
186 | } | ||
187 | |||
188 | for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) { | ||
189 | encoders[i]->bridge = msm_dsi->bridge; | ||
190 | msm_dsi->encoders[i] = encoders[i]; | ||
191 | } | ||
192 | |||
193 | priv->bridges[priv->num_bridges++] = msm_dsi->bridge; | ||
194 | priv->connectors[priv->num_connectors++] = msm_dsi->connector; | ||
195 | |||
196 | return 0; | ||
197 | fail: | ||
198 | if (msm_dsi) { | ||
199 | /* bridge/connector are normally destroyed by drm: */ | ||
200 | if (msm_dsi->bridge) { | ||
201 | msm_dsi_manager_bridge_destroy(msm_dsi->bridge); | ||
202 | msm_dsi->bridge = NULL; | ||
203 | } | ||
204 | if (msm_dsi->connector) { | ||
205 | msm_dsi->connector->funcs->destroy(msm_dsi->connector); | ||
206 | msm_dsi->connector = NULL; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | return ret; | ||
211 | } | ||
212 | |||