]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/msm/hdmi/hdmi_connector.c
drm/msm: convert to drm_bridge
[karo-tx-linux.git] / drivers / gpu / drm / msm / hdmi / hdmi_connector.c
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
20 #include "hdmi.h"
21
22 struct hdmi_connector {
23         struct drm_connector base;
24         struct hdmi *hdmi;
25 };
26 #define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base)
27
28 static 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
78 error4:
79         gpio_free(config->hpd_gpio);
80 error3:
81         gpio_free(config->ddc_data_gpio);
82 error2:
83         gpio_free(config->ddc_clk_gpio);
84 error1:
85         return ret;
86 }
87
88 static int hpd_enable(struct hdmi_connector *hdmi_connector)
89 {
90         struct hdmi *hdmi = hdmi_connector->hdmi;
91         struct drm_device *dev = hdmi_connector->base.dev;
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
152 fail:
153         return ret;
154 }
155
156 static int hdp_disable(struct hdmi_connector *hdmi_connector)
157 {
158         struct hdmi *hdmi = hdmi_connector->hdmi;
159         struct drm_device *dev = hdmi_connector->base.dev;
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
188 fail:
189         return ret;
190 }
191
192 void hdmi_connector_irq(struct drm_connector *connector)
193 {
194         struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
195         struct hdmi *hdmi = hdmi_connector->hdmi;
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
222 static enum drm_connector_status hdmi_connector_detect(
223                 struct drm_connector *connector, bool force)
224 {
225         struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
226         struct hdmi *hdmi = hdmi_connector->hdmi;
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
245 static void hdmi_connector_destroy(struct drm_connector *connector)
246 {
247         struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
248
249         hdp_disable(hdmi_connector);
250
251         drm_sysfs_connector_remove(connector);
252         drm_connector_cleanup(connector);
253
254         hdmi_unreference(hdmi_connector->hdmi);
255
256         kfree(hdmi_connector);
257 }
258
259 static int hdmi_connector_get_modes(struct drm_connector *connector)
260 {
261         struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
262         struct hdmi *hdmi = hdmi_connector->hdmi;
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
284 static int hdmi_connector_mode_valid(struct drm_connector *connector,
285                                  struct drm_display_mode *mode)
286 {
287         struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
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,
294                         requested, hdmi_connector->hdmi->encoder);
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
304 static struct drm_encoder *
305 hdmi_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
311 static 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
318 static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
319         .get_modes = hdmi_connector_get_modes,
320         .mode_valid = hdmi_connector_mode_valid,
321         .best_encoder = hdmi_connector_best_encoder,
322 };
323
324 /* initialize connector */
325 struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
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
337         hdmi_connector->hdmi = hdmi_reference(hdmi);
338
339         connector = &hdmi_connector->base;
340
341         drm_connector_init(hdmi->dev, connector, &hdmi_connector_funcs,
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
352         ret = hpd_enable(hdmi_connector);
353         if (ret) {
354                 dev_err(hdmi->dev->dev, "failed to enable HPD: %d\n", ret);
355                 goto fail;
356         }
357
358         drm_mode_connector_attach_encoder(connector, hdmi->encoder);
359
360         return connector;
361
362 fail:
363         if (connector)
364                 hdmi_connector_destroy(connector);
365
366         return ERR_PTR(ret);
367 }