xref: /linux/drivers/gpu/drm/msm/dsi/dsi.c (revision 98659487b845c05b6bed85d881713545db674c7c)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a689554bSHai Li /*
3a689554bSHai Li  * Copyright (c) 2015, The Linux Foundation. All rights reserved.
4a689554bSHai Li  */
5a689554bSHai Li 
6a689554bSHai Li #include "dsi.h"
7a689554bSHai Li 
8a689554bSHai Li struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi)
9a689554bSHai Li {
106f054ec5SArchit Taneja 	if (!msm_dsi || !msm_dsi_device_connected(msm_dsi))
11a689554bSHai Li 		return NULL;
12a689554bSHai Li 
1397e00119SArchit Taneja 	return msm_dsi->encoder;
14a689554bSHai Li }
15a689554bSHai Li 
16ec31abf6SHai Li static int dsi_get_phy(struct msm_dsi *msm_dsi)
17ec31abf6SHai Li {
18ec31abf6SHai Li 	struct platform_device *pdev = msm_dsi->pdev;
19ec31abf6SHai Li 	struct platform_device *phy_pdev;
20ec31abf6SHai Li 	struct device_node *phy_node;
21ec31abf6SHai Li 
2269696ea0SArchit Taneja 	phy_node = of_parse_phandle(pdev->dev.of_node, "phys", 0);
23ec31abf6SHai Li 	if (!phy_node) {
246a41da17SMamta Shukla 		DRM_DEV_ERROR(&pdev->dev, "cannot find phy device\n");
25ec31abf6SHai Li 		return -ENXIO;
26ec31abf6SHai Li 	}
27ec31abf6SHai Li 
28ec31abf6SHai Li 	phy_pdev = of_find_device_by_node(phy_node);
29ec31abf6SHai Li 	if (phy_pdev)
30ec31abf6SHai Li 		msm_dsi->phy = platform_get_drvdata(phy_pdev);
31ec31abf6SHai Li 
32ec31abf6SHai Li 	of_node_put(phy_node);
33ec31abf6SHai Li 
34ec31abf6SHai Li 	if (!phy_pdev || !msm_dsi->phy) {
356a41da17SMamta Shukla 		DRM_DEV_ERROR(&pdev->dev, "%s: phy driver is not ready\n", __func__);
36ec31abf6SHai Li 		return -EPROBE_DEFER;
37ec31abf6SHai Li 	}
38ec31abf6SHai Li 
39ec31abf6SHai Li 	msm_dsi->phy_dev = get_device(&phy_pdev->dev);
40ec31abf6SHai Li 
41ec31abf6SHai Li 	return 0;
42ec31abf6SHai Li }
43ec31abf6SHai Li 
44a689554bSHai Li static void dsi_destroy(struct msm_dsi *msm_dsi)
45a689554bSHai Li {
46a689554bSHai Li 	if (!msm_dsi)
47a689554bSHai Li 		return;
48a689554bSHai Li 
49a689554bSHai Li 	msm_dsi_manager_unregister(msm_dsi);
509d32c498SHai Li 
51ec31abf6SHai Li 	if (msm_dsi->phy_dev) {
52ec31abf6SHai Li 		put_device(msm_dsi->phy_dev);
539d32c498SHai Li 		msm_dsi->phy = NULL;
54ec31abf6SHai Li 		msm_dsi->phy_dev = NULL;
559d32c498SHai Li 	}
569d32c498SHai Li 
57a689554bSHai Li 	if (msm_dsi->host) {
58a689554bSHai Li 		msm_dsi_host_destroy(msm_dsi->host);
59a689554bSHai Li 		msm_dsi->host = NULL;
60a689554bSHai Li 	}
61a689554bSHai Li 
62a689554bSHai Li 	platform_set_drvdata(msm_dsi->pdev, NULL);
63a689554bSHai Li }
64a689554bSHai Li 
65a689554bSHai Li static struct msm_dsi *dsi_init(struct platform_device *pdev)
66a689554bSHai Li {
67da882cd1SMarkus Elfring 	struct msm_dsi *msm_dsi;
68a689554bSHai Li 	int ret;
69a689554bSHai Li 
70da882cd1SMarkus Elfring 	if (!pdev)
71da882cd1SMarkus Elfring 		return ERR_PTR(-ENXIO);
72a689554bSHai Li 
73a689554bSHai Li 	msm_dsi = devm_kzalloc(&pdev->dev, sizeof(*msm_dsi), GFP_KERNEL);
74da882cd1SMarkus Elfring 	if (!msm_dsi)
75da882cd1SMarkus Elfring 		return ERR_PTR(-ENOMEM);
76a689554bSHai Li 	DBG("dsi probed=%p", msm_dsi);
77a689554bSHai Li 
78aea24171SSean Paul 	msm_dsi->id = -1;
79a689554bSHai Li 	msm_dsi->pdev = pdev;
80a689554bSHai Li 	platform_set_drvdata(pdev, msm_dsi);
81a689554bSHai Li 
82a689554bSHai Li 	/* Init dsi host */
83a689554bSHai Li 	ret = msm_dsi_host_init(msm_dsi);
84a689554bSHai Li 	if (ret)
85da882cd1SMarkus Elfring 		goto destroy_dsi;
86a689554bSHai Li 
87ec31abf6SHai Li 	/* GET dsi PHY */
88ec31abf6SHai Li 	ret = dsi_get_phy(msm_dsi);
89ec31abf6SHai Li 	if (ret)
90da882cd1SMarkus Elfring 		goto destroy_dsi;
919d32c498SHai Li 
92a689554bSHai Li 	/* Register to dsi manager */
93a689554bSHai Li 	ret = msm_dsi_manager_register(msm_dsi);
94a689554bSHai Li 	if (ret)
95da882cd1SMarkus Elfring 		goto destroy_dsi;
96a689554bSHai Li 
97a689554bSHai Li 	return msm_dsi;
98a689554bSHai Li 
99da882cd1SMarkus Elfring destroy_dsi:
100a689554bSHai Li 	dsi_destroy(msm_dsi);
101a689554bSHai Li 	return ERR_PTR(ret);
102a689554bSHai Li }
103a689554bSHai Li 
104a689554bSHai Li static int dsi_bind(struct device *dev, struct device *master, void *data)
105a689554bSHai Li {
106a689554bSHai Li 	struct drm_device *drm = dev_get_drvdata(master);
107a689554bSHai Li 	struct msm_drm_private *priv = drm->dev_private;
108a689554bSHai Li 	struct platform_device *pdev = to_platform_device(dev);
109a689554bSHai Li 	struct msm_dsi *msm_dsi;
110a689554bSHai Li 
111a689554bSHai Li 	DBG("");
112a689554bSHai Li 	msm_dsi = dsi_init(pdev);
1139888495aSSean Paul 	if (IS_ERR(msm_dsi)) {
1149888495aSSean Paul 		/* Don't fail the bind if the dsi port is not connected */
1159888495aSSean Paul 		if (PTR_ERR(msm_dsi) == -ENODEV)
1169888495aSSean Paul 			return 0;
1179888495aSSean Paul 		else
118a689554bSHai Li 			return PTR_ERR(msm_dsi);
1199888495aSSean Paul 	}
120a689554bSHai Li 
121a689554bSHai Li 	priv->dsi[msm_dsi->id] = msm_dsi;
122a689554bSHai Li 
123a689554bSHai Li 	return 0;
124a689554bSHai Li }
125a689554bSHai Li 
126a689554bSHai Li static void dsi_unbind(struct device *dev, struct device *master,
127a689554bSHai Li 		void *data)
128a689554bSHai Li {
129a689554bSHai Li 	struct drm_device *drm = dev_get_drvdata(master);
130a689554bSHai Li 	struct msm_drm_private *priv = drm->dev_private;
131a689554bSHai Li 	struct msm_dsi *msm_dsi = dev_get_drvdata(dev);
132a689554bSHai Li 	int id = msm_dsi->id;
133a689554bSHai Li 
134a689554bSHai Li 	if (priv->dsi[id]) {
135a689554bSHai Li 		dsi_destroy(msm_dsi);
136a689554bSHai Li 		priv->dsi[id] = NULL;
137a689554bSHai Li 	}
138a689554bSHai Li }
139a689554bSHai Li 
140a689554bSHai Li static const struct component_ops dsi_ops = {
141a689554bSHai Li 	.bind   = dsi_bind,
142a689554bSHai Li 	.unbind = dsi_unbind,
143a689554bSHai Li };
144a689554bSHai Li 
145a689554bSHai Li static int dsi_dev_probe(struct platform_device *pdev)
146a689554bSHai Li {
147a689554bSHai Li 	return component_add(&pdev->dev, &dsi_ops);
148a689554bSHai Li }
149a689554bSHai Li 
150a689554bSHai Li static int dsi_dev_remove(struct platform_device *pdev)
151a689554bSHai Li {
152a689554bSHai Li 	DBG("");
153a689554bSHai Li 	component_del(&pdev->dev, &dsi_ops);
154a689554bSHai Li 	return 0;
155a689554bSHai Li }
156a689554bSHai Li 
157a689554bSHai Li static const struct of_device_id dt_match[] = {
158a689554bSHai Li 	{ .compatible = "qcom,mdss-dsi-ctrl" },
159a689554bSHai Li 	{}
160a689554bSHai Li };
161a689554bSHai Li 
162f54ca1a0SArchit Taneja static const struct dev_pm_ops dsi_pm_ops = {
163f54ca1a0SArchit Taneja 	SET_RUNTIME_PM_OPS(msm_dsi_runtime_suspend, msm_dsi_runtime_resume, NULL)
164ca8199f1SKalyan Thota 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
165ca8199f1SKalyan Thota 				pm_runtime_force_resume)
166f54ca1a0SArchit Taneja };
167f54ca1a0SArchit Taneja 
168a689554bSHai Li static struct platform_driver dsi_driver = {
169a689554bSHai Li 	.probe = dsi_dev_probe,
170a689554bSHai Li 	.remove = dsi_dev_remove,
171a689554bSHai Li 	.driver = {
172a689554bSHai Li 		.name = "msm_dsi",
173a689554bSHai Li 		.of_match_table = dt_match,
174f54ca1a0SArchit Taneja 		.pm = &dsi_pm_ops,
175a689554bSHai Li 	},
176a689554bSHai Li };
177a689554bSHai Li 
178a689554bSHai Li void __init msm_dsi_register(void)
179a689554bSHai Li {
180a689554bSHai Li 	DBG("");
181ec31abf6SHai Li 	msm_dsi_phy_driver_register();
182a689554bSHai Li 	platform_driver_register(&dsi_driver);
183a689554bSHai Li }
184a689554bSHai Li 
185a689554bSHai Li void __exit msm_dsi_unregister(void)
186a689554bSHai Li {
187a689554bSHai Li 	DBG("");
188ec31abf6SHai Li 	msm_dsi_phy_driver_unregister();
189a689554bSHai Li 	platform_driver_unregister(&dsi_driver);
190a689554bSHai Li }
191a689554bSHai Li 
192a689554bSHai Li int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev,
19397e00119SArchit Taneja 			 struct drm_encoder *encoder)
194a689554bSHai Li {
19552749d60SGustavo A. R. Silva 	struct msm_drm_private *priv;
196c118e290SArchit Taneja 	struct drm_bridge *ext_bridge;
19797e00119SArchit Taneja 	int ret;
198a689554bSHai Li 
1993f0689e6SLloyd Atkinson 	if (WARN_ON(!encoder) || WARN_ON(!msm_dsi) || WARN_ON(!dev))
200a689554bSHai Li 		return -EINVAL;
201a689554bSHai Li 
20252749d60SGustavo A. R. Silva 	priv = dev->dev_private;
203a689554bSHai Li 	msm_dsi->dev = dev;
204a689554bSHai Li 
205a689554bSHai Li 	ret = msm_dsi_host_modeset_init(msm_dsi->host, dev);
206a689554bSHai Li 	if (ret) {
2076a41da17SMamta Shukla 		DRM_DEV_ERROR(dev->dev, "failed to modeset init host: %d\n", ret);
208a689554bSHai Li 		goto fail;
209a689554bSHai Li 	}
210a689554bSHai Li 
2118b03ad30SChandan Uddaraju 	if (!msm_dsi_manager_validate_current_config(msm_dsi->id))
2128b03ad30SChandan Uddaraju 		goto fail;
2138b03ad30SChandan Uddaraju 
21497e00119SArchit Taneja 	msm_dsi->encoder = encoder;
2150bb70b82SArchit Taneja 
216a689554bSHai Li 	msm_dsi->bridge = msm_dsi_manager_bridge_init(msm_dsi->id);
217a689554bSHai Li 	if (IS_ERR(msm_dsi->bridge)) {
218a689554bSHai Li 		ret = PTR_ERR(msm_dsi->bridge);
2196a41da17SMamta Shukla 		DRM_DEV_ERROR(dev->dev, "failed to create dsi bridge: %d\n", ret);
220a689554bSHai Li 		msm_dsi->bridge = NULL;
221a689554bSHai Li 		goto fail;
222a689554bSHai Li 	}
223a689554bSHai Li 
224c118e290SArchit Taneja 	/*
225c118e290SArchit Taneja 	 * check if the dsi encoder output is connected to a panel or an
226c118e290SArchit Taneja 	 * external bridge. We create a connector only if we're connected to a
227c118e290SArchit Taneja 	 * drm_panel device. When we're connected to an external bridge, we
228c118e290SArchit Taneja 	 * assume that the drm_bridge driver will create the connector itself.
229c118e290SArchit Taneja 	 */
230c118e290SArchit Taneja 	ext_bridge = msm_dsi_host_get_bridge(msm_dsi->host);
231c118e290SArchit Taneja 
232c118e290SArchit Taneja 	if (ext_bridge)
233c118e290SArchit Taneja 		msm_dsi->connector =
234c118e290SArchit Taneja 			msm_dsi_manager_ext_bridge_init(msm_dsi->id);
235c118e290SArchit Taneja 	else
236c118e290SArchit Taneja 		msm_dsi->connector =
237c118e290SArchit Taneja 			msm_dsi_manager_connector_init(msm_dsi->id);
238c118e290SArchit Taneja 
239a689554bSHai Li 	if (IS_ERR(msm_dsi->connector)) {
240a689554bSHai Li 		ret = PTR_ERR(msm_dsi->connector);
2416a41da17SMamta Shukla 		DRM_DEV_ERROR(dev->dev,
242c118e290SArchit Taneja 			"failed to create dsi connector: %d\n", ret);
243a689554bSHai Li 		msm_dsi->connector = NULL;
244a689554bSHai Li 		goto fail;
245a689554bSHai Li 	}
246a689554bSHai Li 
24703436e3eSSean Paul 	msm_dsi_manager_setup_encoder(msm_dsi->id);
24803436e3eSSean Paul 
249a689554bSHai Li 	priv->bridges[priv->num_bridges++]       = msm_dsi->bridge;
250a689554bSHai Li 	priv->connectors[priv->num_connectors++] = msm_dsi->connector;
251a689554bSHai Li 
252a689554bSHai Li 	return 0;
253a689554bSHai Li fail:
254a689554bSHai Li 	/* bridge/connector are normally destroyed by drm: */
255a689554bSHai Li 	if (msm_dsi->bridge) {
256a689554bSHai Li 		msm_dsi_manager_bridge_destroy(msm_dsi->bridge);
257a689554bSHai Li 		msm_dsi->bridge = NULL;
258a689554bSHai Li 	}
259c118e290SArchit Taneja 
260c118e290SArchit Taneja 	/* don't destroy connector if we didn't make it */
261c118e290SArchit Taneja 	if (msm_dsi->connector && !msm_dsi->external_bridge)
262a689554bSHai Li 		msm_dsi->connector->funcs->destroy(msm_dsi->connector);
263c118e290SArchit Taneja 
264a689554bSHai Li 	msm_dsi->connector = NULL;
265a689554bSHai Li 
266a689554bSHai Li 	return ret;
267a689554bSHai Li }
268a689554bSHai Li 
269*98659487SAbhinav Kumar 
270