diff options
author | Hai Li <hali@codeaurora.org> | 2015-01-07 18:47:44 -0500 |
---|---|---|
committer | Rob Clark <robdclark@gmail.com> | 2015-02-01 15:30:37 -0500 |
commit | ab5b0107ccf3821a6837b0f2819270d6fa0b278f (patch) | |
tree | 87422469e4f1afaac11592f967ce246163cf7bf3 /drivers/gpu/drm/msm/edp/edp.c | |
parent | b1b1c74e36b369dceaa0ea98ac543e8c13c720ee (diff) |
drm/msm: Initial add eDP support in msm drm driver (v5)
This change adds a new eDP connector in msm drm driver. With this
change, eDP panel can work with msm platform under drm framework.
v1: Initial change
v2: Address Rob's comments
Use generated header file for register definitions
Change to devm_* APIs
v3: Address Thierry's comments and rebase on top of atomic changes
Remove edp_bridge_mode_fixup
Remove backlight control code and rely on pwm-backlight
Remove continuous splash screen support for now
Change to gpiod_* APIs
v4: Fix kbuild test issue
Signed-off-by: Hai Li <hali@codeaurora.org>
[robclark: v5: rebase on drm_bridge changes in drm-next]
Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu/drm/msm/edp/edp.c')
-rw-r--r-- | drivers/gpu/drm/msm/edp/edp.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/edp/edp.c b/drivers/gpu/drm/msm/edp/edp.c new file mode 100644 index 000000000000..0940e84b2821 --- /dev/null +++ b/drivers/gpu/drm/msm/edp/edp.c | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014-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 <linux/of_irq.h> | ||
15 | #include "edp.h" | ||
16 | |||
17 | static irqreturn_t edp_irq(int irq, void *dev_id) | ||
18 | { | ||
19 | struct msm_edp *edp = dev_id; | ||
20 | |||
21 | /* Process eDP irq */ | ||
22 | return msm_edp_ctrl_irq(edp->ctrl); | ||
23 | } | ||
24 | |||
25 | static void edp_destroy(struct platform_device *pdev) | ||
26 | { | ||
27 | struct msm_edp *edp = platform_get_drvdata(pdev); | ||
28 | |||
29 | if (!edp) | ||
30 | return; | ||
31 | |||
32 | if (edp->ctrl) { | ||
33 | msm_edp_ctrl_destroy(edp->ctrl); | ||
34 | edp->ctrl = NULL; | ||
35 | } | ||
36 | |||
37 | platform_set_drvdata(pdev, NULL); | ||
38 | } | ||
39 | |||
40 | /* construct eDP at bind/probe time, grab all the resources. */ | ||
41 | static struct msm_edp *edp_init(struct platform_device *pdev) | ||
42 | { | ||
43 | struct msm_edp *edp = NULL; | ||
44 | int ret; | ||
45 | |||
46 | if (!pdev) { | ||
47 | pr_err("no eDP device\n"); | ||
48 | ret = -ENXIO; | ||
49 | goto fail; | ||
50 | } | ||
51 | |||
52 | edp = devm_kzalloc(&pdev->dev, sizeof(*edp), GFP_KERNEL); | ||
53 | if (!edp) { | ||
54 | ret = -ENOMEM; | ||
55 | goto fail; | ||
56 | } | ||
57 | DBG("eDP probed=%p", edp); | ||
58 | |||
59 | edp->pdev = pdev; | ||
60 | platform_set_drvdata(pdev, edp); | ||
61 | |||
62 | ret = msm_edp_ctrl_init(edp); | ||
63 | if (ret) | ||
64 | goto fail; | ||
65 | |||
66 | return edp; | ||
67 | |||
68 | fail: | ||
69 | if (edp) | ||
70 | edp_destroy(pdev); | ||
71 | |||
72 | return ERR_PTR(ret); | ||
73 | } | ||
74 | |||
75 | static int edp_bind(struct device *dev, struct device *master, void *data) | ||
76 | { | ||
77 | struct drm_device *drm = dev_get_drvdata(master); | ||
78 | struct msm_drm_private *priv = drm->dev_private; | ||
79 | struct msm_edp *edp; | ||
80 | |||
81 | DBG(""); | ||
82 | edp = edp_init(to_platform_device(dev)); | ||
83 | if (IS_ERR(edp)) | ||
84 | return PTR_ERR(edp); | ||
85 | priv->edp = edp; | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static void edp_unbind(struct device *dev, struct device *master, void *data) | ||
91 | { | ||
92 | struct drm_device *drm = dev_get_drvdata(master); | ||
93 | struct msm_drm_private *priv = drm->dev_private; | ||
94 | |||
95 | DBG(""); | ||
96 | if (priv->edp) { | ||
97 | edp_destroy(to_platform_device(dev)); | ||
98 | priv->edp = NULL; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | static const struct component_ops edp_ops = { | ||
103 | .bind = edp_bind, | ||
104 | .unbind = edp_unbind, | ||
105 | }; | ||
106 | |||
107 | static int edp_dev_probe(struct platform_device *pdev) | ||
108 | { | ||
109 | DBG(""); | ||
110 | return component_add(&pdev->dev, &edp_ops); | ||
111 | } | ||
112 | |||
113 | static int edp_dev_remove(struct platform_device *pdev) | ||
114 | { | ||
115 | DBG(""); | ||
116 | component_del(&pdev->dev, &edp_ops); | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static const struct of_device_id dt_match[] = { | ||
121 | { .compatible = "qcom,mdss-edp" }, | ||
122 | {} | ||
123 | }; | ||
124 | |||
125 | static struct platform_driver edp_driver = { | ||
126 | .probe = edp_dev_probe, | ||
127 | .remove = edp_dev_remove, | ||
128 | .driver = { | ||
129 | .name = "msm_edp", | ||
130 | .of_match_table = dt_match, | ||
131 | }, | ||
132 | }; | ||
133 | |||
134 | void __init msm_edp_register(void) | ||
135 | { | ||
136 | DBG(""); | ||
137 | platform_driver_register(&edp_driver); | ||
138 | } | ||
139 | |||
140 | void __exit msm_edp_unregister(void) | ||
141 | { | ||
142 | DBG(""); | ||
143 | platform_driver_unregister(&edp_driver); | ||
144 | } | ||
145 | |||
146 | /* Second part of initialization, the drm/kms level modeset_init */ | ||
147 | int msm_edp_modeset_init(struct msm_edp *edp, struct drm_device *dev, | ||
148 | struct drm_encoder *encoder) | ||
149 | { | ||
150 | struct platform_device *pdev = edp->pdev; | ||
151 | struct msm_drm_private *priv = dev->dev_private; | ||
152 | int ret; | ||
153 | |||
154 | edp->encoder = encoder; | ||
155 | edp->dev = dev; | ||
156 | |||
157 | edp->bridge = msm_edp_bridge_init(edp); | ||
158 | if (IS_ERR(edp->bridge)) { | ||
159 | ret = PTR_ERR(edp->bridge); | ||
160 | dev_err(dev->dev, "failed to create eDP bridge: %d\n", ret); | ||
161 | edp->bridge = NULL; | ||
162 | goto fail; | ||
163 | } | ||
164 | |||
165 | edp->connector = msm_edp_connector_init(edp); | ||
166 | if (IS_ERR(edp->connector)) { | ||
167 | ret = PTR_ERR(edp->connector); | ||
168 | dev_err(dev->dev, "failed to create eDP connector: %d\n", ret); | ||
169 | edp->connector = NULL; | ||
170 | goto fail; | ||
171 | } | ||
172 | |||
173 | edp->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); | ||
174 | if (edp->irq < 0) { | ||
175 | ret = edp->irq; | ||
176 | dev_err(dev->dev, "failed to get IRQ: %d\n", ret); | ||
177 | goto fail; | ||
178 | } | ||
179 | |||
180 | ret = devm_request_irq(&pdev->dev, edp->irq, | ||
181 | edp_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, | ||
182 | "edp_isr", edp); | ||
183 | if (ret < 0) { | ||
184 | dev_err(dev->dev, "failed to request IRQ%u: %d\n", | ||
185 | edp->irq, ret); | ||
186 | goto fail; | ||
187 | } | ||
188 | |||
189 | encoder->bridge = edp->bridge; | ||
190 | |||
191 | priv->bridges[priv->num_bridges++] = edp->bridge; | ||
192 | priv->connectors[priv->num_connectors++] = edp->connector; | ||
193 | |||
194 | return 0; | ||
195 | |||
196 | fail: | ||
197 | /* bridge/connector are normally destroyed by drm */ | ||
198 | if (edp->bridge) { | ||
199 | edp_bridge_destroy(edp->bridge); | ||
200 | edp->bridge = NULL; | ||
201 | } | ||
202 | if (edp->connector) { | ||
203 | edp->connector->funcs->destroy(edp->connector); | ||
204 | edp->connector = NULL; | ||
205 | } | ||
206 | |||
207 | return ret; | ||
208 | } | ||