drm/msm: move irq utils to mdp_kms
[deliverable/linux.git] / drivers / gpu / drm / msm / hdmi / hdmi_connector.c
CommitLineData
c8afe684
RC
1/*
2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <linux/gpio.h>
19
dd2da6e3 20#include "msm_kms.h"
c8afe684
RC
21#include "hdmi.h"
22
23struct hdmi_connector {
a3376e3e
RC
24 struct drm_connector base;
25 struct hdmi *hdmi;
c8afe684
RC
26};
27#define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base)
28
29static int gpio_config(struct hdmi *hdmi, bool on)
30{
31 struct drm_device *dev = hdmi->dev;
32 struct hdmi_platform_config *config =
33 hdmi->pdev->dev.platform_data;
34 int ret;
35
36 if (on) {
37 ret = gpio_request(config->ddc_clk_gpio, "HDMI_DDC_CLK");
38 if (ret) {
39 dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
40 "HDMI_DDC_CLK", config->ddc_clk_gpio, ret);
41 goto error1;
42 }
43 ret = gpio_request(config->ddc_data_gpio, "HDMI_DDC_DATA");
44 if (ret) {
45 dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
46 "HDMI_DDC_DATA", config->ddc_data_gpio, ret);
47 goto error2;
48 }
49 ret = gpio_request(config->hpd_gpio, "HDMI_HPD");
50 if (ret) {
51 dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
52 "HDMI_HPD", config->hpd_gpio, ret);
53 goto error3;
54 }
55 if (config->pmic_gpio != -1) {
56 ret = gpio_request(config->pmic_gpio, "PMIC_HDMI_MUX_SEL");
57 if (ret) {
58 dev_err(dev->dev, "'%s'(%d) gpio_request failed: %d\n",
59 "PMIC_HDMI_MUX_SEL", config->pmic_gpio, ret);
60 goto error4;
61 }
62 gpio_set_value_cansleep(config->pmic_gpio, 0);
63 }
64 DBG("gpio on");
65 } else {
66 gpio_free(config->ddc_clk_gpio);
67 gpio_free(config->ddc_data_gpio);
68 gpio_free(config->hpd_gpio);
69
70 if (config->pmic_gpio != -1) {
71 gpio_set_value_cansleep(config->pmic_gpio, 1);
72 gpio_free(config->pmic_gpio);
73 }
74 DBG("gpio off");
75 }
76
77 return 0;
78
79error4:
80 gpio_free(config->hpd_gpio);
81error3:
82 gpio_free(config->ddc_data_gpio);
83error2:
84 gpio_free(config->ddc_clk_gpio);
85error1:
86 return ret;
87}
88
89static int hpd_enable(struct hdmi_connector *hdmi_connector)
90{
a3376e3e
RC
91 struct hdmi *hdmi = hdmi_connector->hdmi;
92 struct drm_device *dev = hdmi_connector->base.dev;
c8afe684
RC
93 struct hdmi_phy *phy = hdmi->phy;
94 uint32_t hpd_ctrl;
95 int ret;
96
97 ret = gpio_config(hdmi, true);
98 if (ret) {
99 dev_err(dev->dev, "failed to configure GPIOs: %d\n", ret);
100 goto fail;
101 }
102
103 ret = clk_prepare_enable(hdmi->clk);
104 if (ret) {
105 dev_err(dev->dev, "failed to enable 'clk': %d\n", ret);
106 goto fail;
107 }
108
109 ret = clk_prepare_enable(hdmi->m_pclk);
110 if (ret) {
111 dev_err(dev->dev, "failed to enable 'm_pclk': %d\n", ret);
112 goto fail;
113 }
114
115 ret = clk_prepare_enable(hdmi->s_pclk);
116 if (ret) {
117 dev_err(dev->dev, "failed to enable 's_pclk': %d\n", ret);
118 goto fail;
119 }
120
121 if (hdmi->mpp0)
122 ret = regulator_enable(hdmi->mpp0);
123 if (!ret)
124 ret = regulator_enable(hdmi->mvs);
125 if (ret) {
126 dev_err(dev->dev, "failed to enable regulators: %d\n", ret);
127 goto fail;
128 }
129
130 hdmi_set_mode(hdmi, false);
131 phy->funcs->reset(phy);
132 hdmi_set_mode(hdmi, true);
133
134 hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
135
136 /* enable HPD events: */
137 hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
138 HDMI_HPD_INT_CTRL_INT_CONNECT |
139 HDMI_HPD_INT_CTRL_INT_EN);
140
141 /* set timeout to 4.1ms (max) for hardware debounce */
142 hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
143 hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
144
145 /* Toggle HPD circuit to trigger HPD sense */
146 hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
147 ~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
148 hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
149 HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
150
151 return 0;
152
153fail:
154 return ret;
155}
156
157static int hdp_disable(struct hdmi_connector *hdmi_connector)
158{
a3376e3e
RC
159 struct hdmi *hdmi = hdmi_connector->hdmi;
160 struct drm_device *dev = hdmi_connector->base.dev;
c8afe684
RC
161 int ret = 0;
162
163 /* Disable HPD interrupt */
164 hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
165
166 hdmi_set_mode(hdmi, false);
167
168 if (hdmi->mpp0)
169 ret = regulator_disable(hdmi->mpp0);
170 if (!ret)
171 ret = regulator_disable(hdmi->mvs);
172 if (ret) {
173 dev_err(dev->dev, "failed to enable regulators: %d\n", ret);
174 goto fail;
175 }
176
177 clk_disable_unprepare(hdmi->clk);
178 clk_disable_unprepare(hdmi->m_pclk);
179 clk_disable_unprepare(hdmi->s_pclk);
180
181 ret = gpio_config(hdmi, false);
182 if (ret) {
183 dev_err(dev->dev, "failed to unconfigure GPIOs: %d\n", ret);
184 goto fail;
185 }
186
187 return 0;
188
189fail:
190 return ret;
191}
192
193void hdmi_connector_irq(struct drm_connector *connector)
194{
a3376e3e
RC
195 struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
196 struct hdmi *hdmi = hdmi_connector->hdmi;
c8afe684
RC
197 uint32_t hpd_int_status, hpd_int_ctrl;
198
199 /* Process HPD: */
200 hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
201 hpd_int_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);
202
203 if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&
204 (hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {
205 bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED);
206
207 DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl);
208
209 /* ack the irq: */
210 hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
211 hpd_int_ctrl | HDMI_HPD_INT_CTRL_INT_ACK);
212
213 drm_helper_hpd_irq_event(connector->dev);
214
215 /* detect disconnect if we are connected or visa versa: */
216 hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
217 if (!detected)
218 hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
219 hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
220 }
221}
222
223static enum drm_connector_status hdmi_connector_detect(
224 struct drm_connector *connector, bool force)
225{
a3376e3e
RC
226 struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
227 struct hdmi *hdmi = hdmi_connector->hdmi;
c8afe684
RC
228 uint32_t hpd_int_status;
229 int retry = 20;
230
231 hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
232
233 /* sense seems to in some cases be momentarily de-asserted, don't
234 * let that trick us into thinking the monitor is gone:
235 */
236 while (retry-- && !(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED)) {
237 mdelay(10);
238 hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
239 DBG("status=%08x", hpd_int_status);
240 }
241
242 return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
243 connector_status_connected : connector_status_disconnected;
244}
245
246static void hdmi_connector_destroy(struct drm_connector *connector)
247{
a3376e3e 248 struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
c8afe684
RC
249
250 hdp_disable(hdmi_connector);
251
252 drm_sysfs_connector_remove(connector);
253 drm_connector_cleanup(connector);
254
a3376e3e 255 hdmi_unreference(hdmi_connector->hdmi);
c8afe684
RC
256
257 kfree(hdmi_connector);
258}
259
260static int hdmi_connector_get_modes(struct drm_connector *connector)
261{
a3376e3e
RC
262 struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
263 struct hdmi *hdmi = hdmi_connector->hdmi;
c8afe684
RC
264 struct edid *edid;
265 uint32_t hdmi_ctrl;
266 int ret = 0;
267
268 hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
269 hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
270
271 edid = drm_get_edid(connector, hdmi->i2c);
272
273 hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
274
275 drm_mode_connector_update_edid_property(connector, edid);
276
277 if (edid) {
278 ret = drm_add_edid_modes(connector, edid);
279 kfree(edid);
280 }
281
282 return ret;
283}
284
285static int hdmi_connector_mode_valid(struct drm_connector *connector,
286 struct drm_display_mode *mode)
287{
a3376e3e 288 struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
c8afe684
RC
289 struct msm_drm_private *priv = connector->dev->dev_private;
290 struct msm_kms *kms = priv->kms;
291 long actual, requested;
292
293 requested = 1000 * mode->clock;
294 actual = kms->funcs->round_pixclk(kms,
a3376e3e 295 requested, hdmi_connector->hdmi->encoder);
c8afe684
RC
296
297 DBG("requested=%ld, actual=%ld", requested, actual);
298
299 if (actual != requested)
300 return MODE_CLOCK_RANGE;
301
302 return 0;
303}
304
a3376e3e
RC
305static struct drm_encoder *
306hdmi_connector_best_encoder(struct drm_connector *connector)
307{
308 struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
309 return hdmi_connector->hdmi->encoder;
310}
311
c8afe684
RC
312static const struct drm_connector_funcs hdmi_connector_funcs = {
313 .dpms = drm_helper_connector_dpms,
314 .detect = hdmi_connector_detect,
315 .fill_modes = drm_helper_probe_single_connector_modes,
316 .destroy = hdmi_connector_destroy,
317};
318
319static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
320 .get_modes = hdmi_connector_get_modes,
321 .mode_valid = hdmi_connector_mode_valid,
a3376e3e 322 .best_encoder = hdmi_connector_best_encoder,
c8afe684
RC
323};
324
325/* initialize connector */
a3376e3e 326struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
c8afe684
RC
327{
328 struct drm_connector *connector = NULL;
329 struct hdmi_connector *hdmi_connector;
330 int ret;
331
332 hdmi_connector = kzalloc(sizeof(*hdmi_connector), GFP_KERNEL);
333 if (!hdmi_connector) {
334 ret = -ENOMEM;
335 goto fail;
336 }
337
a3376e3e
RC
338 hdmi_connector->hdmi = hdmi_reference(hdmi);
339
340 connector = &hdmi_connector->base;
c8afe684 341
a3376e3e 342 drm_connector_init(hdmi->dev, connector, &hdmi_connector_funcs,
c8afe684
RC
343 DRM_MODE_CONNECTOR_HDMIA);
344 drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
345
346 connector->polled = DRM_CONNECTOR_POLL_HPD;
347
348 connector->interlace_allowed = 1;
349 connector->doublescan_allowed = 0;
350
351 drm_sysfs_connector_add(connector);
352
c8afe684
RC
353 ret = hpd_enable(hdmi_connector);
354 if (ret) {
a3376e3e 355 dev_err(hdmi->dev->dev, "failed to enable HPD: %d\n", ret);
c8afe684
RC
356 goto fail;
357 }
358
a3376e3e 359 drm_mode_connector_attach_encoder(connector, hdmi->encoder);
c8afe684
RC
360
361 return connector;
362
363fail:
364 if (connector)
365 hdmi_connector_destroy(connector);
366
367 return ERR_PTR(ret);
368}
This page took 0.061393 seconds and 5 git commands to generate.