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