1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #include "dsi.h" |
7 | |
8 | bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi) |
9 | { |
10 | unsigned long host_flags = msm_dsi_host_get_mode_flags(host: msm_dsi->host); |
11 | |
12 | return !(host_flags & MIPI_DSI_MODE_VIDEO); |
13 | } |
14 | |
15 | struct drm_dsc_config *msm_dsi_get_dsc_config(struct msm_dsi *msm_dsi) |
16 | { |
17 | return msm_dsi_host_get_dsc_config(host: msm_dsi->host); |
18 | } |
19 | |
20 | bool msm_dsi_wide_bus_enabled(struct msm_dsi *msm_dsi) |
21 | { |
22 | return msm_dsi_host_is_wide_bus_enabled(host: msm_dsi->host); |
23 | } |
24 | |
25 | static int dsi_get_phy(struct msm_dsi *msm_dsi) |
26 | { |
27 | struct platform_device *pdev = msm_dsi->pdev; |
28 | struct platform_device *phy_pdev; |
29 | struct device_node *phy_node; |
30 | |
31 | phy_node = of_parse_phandle(pdev->dev.of_node, "phys" , 0); |
32 | if (!phy_node) { |
33 | DRM_DEV_ERROR(&pdev->dev, "cannot find phy device\n" ); |
34 | return -ENXIO; |
35 | } |
36 | |
37 | phy_pdev = of_find_device_by_node(np: phy_node); |
38 | if (phy_pdev) { |
39 | msm_dsi->phy = platform_get_drvdata(pdev: phy_pdev); |
40 | msm_dsi->phy_dev = &phy_pdev->dev; |
41 | } |
42 | |
43 | of_node_put(phy_node); |
44 | |
45 | if (!phy_pdev) { |
46 | DRM_DEV_ERROR(&pdev->dev, "%s: phy driver is not ready\n" , __func__); |
47 | return -EPROBE_DEFER; |
48 | } |
49 | if (!msm_dsi->phy) { |
50 | put_device(dev: &phy_pdev->dev); |
51 | DRM_DEV_ERROR(&pdev->dev, "%s: phy driver is not ready\n" , __func__); |
52 | return -EPROBE_DEFER; |
53 | } |
54 | |
55 | return 0; |
56 | } |
57 | |
58 | static void dsi_destroy(struct msm_dsi *msm_dsi) |
59 | { |
60 | if (!msm_dsi) |
61 | return; |
62 | |
63 | msm_dsi_manager_unregister(msm_dsi); |
64 | |
65 | if (msm_dsi->phy_dev) { |
66 | put_device(dev: msm_dsi->phy_dev); |
67 | msm_dsi->phy = NULL; |
68 | msm_dsi->phy_dev = NULL; |
69 | } |
70 | |
71 | if (msm_dsi->host) { |
72 | msm_dsi_host_destroy(host: msm_dsi->host); |
73 | msm_dsi->host = NULL; |
74 | } |
75 | |
76 | platform_set_drvdata(pdev: msm_dsi->pdev, NULL); |
77 | } |
78 | |
79 | static struct msm_dsi *dsi_init(struct platform_device *pdev) |
80 | { |
81 | struct msm_dsi *msm_dsi; |
82 | int ret; |
83 | |
84 | if (!pdev) |
85 | return ERR_PTR(error: -ENXIO); |
86 | |
87 | msm_dsi = devm_kzalloc(dev: &pdev->dev, size: sizeof(*msm_dsi), GFP_KERNEL); |
88 | if (!msm_dsi) |
89 | return ERR_PTR(error: -ENOMEM); |
90 | DBG("dsi probed=%p" , msm_dsi); |
91 | |
92 | msm_dsi->id = -1; |
93 | msm_dsi->pdev = pdev; |
94 | platform_set_drvdata(pdev, data: msm_dsi); |
95 | |
96 | /* Init dsi host */ |
97 | ret = msm_dsi_host_init(msm_dsi); |
98 | if (ret) |
99 | goto destroy_dsi; |
100 | |
101 | /* GET dsi PHY */ |
102 | ret = dsi_get_phy(msm_dsi); |
103 | if (ret) |
104 | goto destroy_dsi; |
105 | |
106 | /* Register to dsi manager */ |
107 | ret = msm_dsi_manager_register(msm_dsi); |
108 | if (ret) |
109 | goto destroy_dsi; |
110 | |
111 | return msm_dsi; |
112 | |
113 | destroy_dsi: |
114 | dsi_destroy(msm_dsi); |
115 | return ERR_PTR(error: ret); |
116 | } |
117 | |
118 | static int dsi_bind(struct device *dev, struct device *master, void *data) |
119 | { |
120 | struct msm_drm_private *priv = dev_get_drvdata(dev: master); |
121 | struct msm_dsi *msm_dsi = dev_get_drvdata(dev); |
122 | |
123 | priv->dsi[msm_dsi->id] = msm_dsi; |
124 | |
125 | return 0; |
126 | } |
127 | |
128 | static void dsi_unbind(struct device *dev, struct device *master, |
129 | void *data) |
130 | { |
131 | struct msm_drm_private *priv = dev_get_drvdata(dev: master); |
132 | struct msm_dsi *msm_dsi = dev_get_drvdata(dev); |
133 | |
134 | msm_dsi_tx_buf_free(mipi_host: msm_dsi->host); |
135 | priv->dsi[msm_dsi->id] = NULL; |
136 | } |
137 | |
138 | static const struct component_ops dsi_ops = { |
139 | .bind = dsi_bind, |
140 | .unbind = dsi_unbind, |
141 | }; |
142 | |
143 | int dsi_dev_attach(struct platform_device *pdev) |
144 | { |
145 | return component_add(&pdev->dev, &dsi_ops); |
146 | } |
147 | |
148 | void dsi_dev_detach(struct platform_device *pdev) |
149 | { |
150 | component_del(&pdev->dev, &dsi_ops); |
151 | } |
152 | |
153 | static int dsi_dev_probe(struct platform_device *pdev) |
154 | { |
155 | struct msm_dsi *msm_dsi; |
156 | |
157 | DBG("" ); |
158 | msm_dsi = dsi_init(pdev); |
159 | if (IS_ERR(ptr: msm_dsi)) { |
160 | /* Don't fail the bind if the dsi port is not connected */ |
161 | if (PTR_ERR(ptr: msm_dsi) == -ENODEV) |
162 | return 0; |
163 | else |
164 | return PTR_ERR(ptr: msm_dsi); |
165 | } |
166 | |
167 | return 0; |
168 | } |
169 | |
170 | static void dsi_dev_remove(struct platform_device *pdev) |
171 | { |
172 | struct msm_dsi *msm_dsi = platform_get_drvdata(pdev); |
173 | |
174 | DBG("" ); |
175 | dsi_destroy(msm_dsi); |
176 | } |
177 | |
178 | static const struct of_device_id dt_match[] = { |
179 | { .compatible = "qcom,mdss-dsi-ctrl" }, |
180 | |
181 | /* Deprecated, don't use */ |
182 | { .compatible = "qcom,dsi-ctrl-6g-qcm2290" }, |
183 | {} |
184 | }; |
185 | |
186 | static const struct dev_pm_ops dsi_pm_ops = { |
187 | SET_RUNTIME_PM_OPS(msm_dsi_runtime_suspend, msm_dsi_runtime_resume, NULL) |
188 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
189 | pm_runtime_force_resume) |
190 | }; |
191 | |
192 | static struct platform_driver dsi_driver = { |
193 | .probe = dsi_dev_probe, |
194 | .remove_new = dsi_dev_remove, |
195 | .driver = { |
196 | .name = "msm_dsi" , |
197 | .of_match_table = dt_match, |
198 | .pm = &dsi_pm_ops, |
199 | }, |
200 | }; |
201 | |
202 | void __init msm_dsi_register(void) |
203 | { |
204 | DBG("" ); |
205 | msm_dsi_phy_driver_register(); |
206 | platform_driver_register(&dsi_driver); |
207 | } |
208 | |
209 | void __exit msm_dsi_unregister(void) |
210 | { |
211 | DBG("" ); |
212 | msm_dsi_phy_driver_unregister(); |
213 | platform_driver_unregister(&dsi_driver); |
214 | } |
215 | |
216 | int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, |
217 | struct drm_encoder *encoder) |
218 | { |
219 | struct drm_bridge *bridge; |
220 | int ret; |
221 | |
222 | msm_dsi->dev = dev; |
223 | |
224 | ret = msm_dsi_host_modeset_init(host: msm_dsi->host, dev); |
225 | if (ret) { |
226 | DRM_DEV_ERROR(dev->dev, "failed to modeset init host: %d\n" , ret); |
227 | return ret; |
228 | } |
229 | |
230 | if (msm_dsi_is_bonded_dsi(msm_dsi) && |
231 | !msm_dsi_is_master_dsi(msm_dsi)) { |
232 | /* |
233 | * Do not return an eror here, |
234 | * Just skip creating encoder/connector for the slave-DSI. |
235 | */ |
236 | return 0; |
237 | } |
238 | |
239 | bridge = msm_dsi_manager_bridge_init(msm_dsi, encoder); |
240 | if (IS_ERR(ptr: bridge)) { |
241 | ret = PTR_ERR(ptr: bridge); |
242 | DRM_DEV_ERROR(dev->dev, "failed to create dsi bridge: %d\n" , ret); |
243 | return ret; |
244 | } |
245 | |
246 | ret = msm_dsi_manager_ext_bridge_init(id: msm_dsi->id, int_bridge: bridge); |
247 | if (ret) { |
248 | DRM_DEV_ERROR(dev->dev, |
249 | "failed to create dsi connector: %d\n" , ret); |
250 | return ret; |
251 | } |
252 | |
253 | return 0; |
254 | } |
255 | |
256 | void msm_dsi_snapshot(struct msm_disp_state *disp_state, struct msm_dsi *msm_dsi) |
257 | { |
258 | msm_dsi_host_snapshot(disp_state, host: msm_dsi->host); |
259 | msm_dsi_phy_snapshot(disp_state, phy: msm_dsi->phy); |
260 | } |
261 | |
262 | |