]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/gpu/drm/omapdrm/omap_connector.c
Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / drivers / gpu / drm / omapdrm / omap_connector.c
1 /*
2  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
3  * Author: Rob Clark <rob@ti.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 <drm/drm_atomic_helper.h>
19 #include <drm/drm_crtc.h>
20 #include <drm/drm_panel.h>
21 #include <drm/drm_probe_helper.h>
22
23 #include "omap_drv.h"
24
25 /*
26  * connector funcs
27  */
28
29 #define to_omap_connector(x) container_of(x, struct omap_connector, base)
30
31 struct omap_connector {
32         struct drm_connector base;
33         struct omap_dss_device *output;
34         struct omap_dss_device *hpd;
35         bool hdmi_mode;
36 };
37
38 static void omap_connector_hpd_notify(struct drm_connector *connector,
39                                       enum drm_connector_status status)
40 {
41         struct omap_connector *omap_connector = to_omap_connector(connector);
42         struct omap_dss_device *dssdev;
43
44         if (status != connector_status_disconnected)
45                 return;
46
47         /*
48          * Notify all devics in the pipeline of disconnection. This is required
49          * to let the HDMI encoders reset their internal state related to
50          * connection status, such as the CEC address.
51          */
52         for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) {
53                 if (dssdev->ops && dssdev->ops->hdmi.lost_hotplug)
54                         dssdev->ops->hdmi.lost_hotplug(dssdev);
55         }
56 }
57
58 static void omap_connector_hpd_cb(void *cb_data,
59                                   enum drm_connector_status status)
60 {
61         struct omap_connector *omap_connector = cb_data;
62         struct drm_connector *connector = &omap_connector->base;
63         struct drm_device *dev = connector->dev;
64         enum drm_connector_status old_status;
65
66         mutex_lock(&dev->mode_config.mutex);
67         old_status = connector->status;
68         connector->status = status;
69         mutex_unlock(&dev->mode_config.mutex);
70
71         if (old_status == status)
72                 return;
73
74         omap_connector_hpd_notify(connector, status);
75
76         drm_kms_helper_hotplug_event(dev);
77 }
78
79 void omap_connector_enable_hpd(struct drm_connector *connector)
80 {
81         struct omap_connector *omap_connector = to_omap_connector(connector);
82         struct omap_dss_device *hpd = omap_connector->hpd;
83
84         if (hpd)
85                 hpd->ops->register_hpd_cb(hpd, omap_connector_hpd_cb,
86                                           omap_connector);
87 }
88
89 void omap_connector_disable_hpd(struct drm_connector *connector)
90 {
91         struct omap_connector *omap_connector = to_omap_connector(connector);
92         struct omap_dss_device *hpd = omap_connector->hpd;
93
94         if (hpd)
95                 hpd->ops->unregister_hpd_cb(hpd);
96 }
97
98 bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
99 {
100         struct omap_connector *omap_connector = to_omap_connector(connector);
101
102         return omap_connector->hdmi_mode;
103 }
104
105 static struct omap_dss_device *
106 omap_connector_find_device(struct drm_connector *connector,
107                            enum omap_dss_device_ops_flag op)
108 {
109         struct omap_connector *omap_connector = to_omap_connector(connector);
110         struct omap_dss_device *dssdev = NULL;
111         struct omap_dss_device *d;
112
113         for (d = omap_connector->output; d; d = d->next) {
114                 if (d->ops_flags & op)
115                         dssdev = d;
116         }
117
118         return dssdev;
119 }
120
121 static enum drm_connector_status omap_connector_detect(
122                 struct drm_connector *connector, bool force)
123 {
124         struct omap_dss_device *dssdev;
125         enum drm_connector_status status;
126
127         dssdev = omap_connector_find_device(connector,
128                                             OMAP_DSS_DEVICE_OP_DETECT);
129
130         if (dssdev) {
131                 status = dssdev->ops->detect(dssdev)
132                        ? connector_status_connected
133                        : connector_status_disconnected;
134
135                 omap_connector_hpd_notify(connector, status);
136         } else {
137                 switch (connector->connector_type) {
138                 case DRM_MODE_CONNECTOR_DPI:
139                 case DRM_MODE_CONNECTOR_LVDS:
140                 case DRM_MODE_CONNECTOR_DSI:
141                         status = connector_status_connected;
142                         break;
143                 default:
144                         status = connector_status_unknown;
145                         break;
146                 }
147         }
148
149         VERB("%s: %d (force=%d)", connector->name, status, force);
150
151         return status;
152 }
153
154 static void omap_connector_destroy(struct drm_connector *connector)
155 {
156         struct omap_connector *omap_connector = to_omap_connector(connector);
157
158         DBG("%s", connector->name);
159
160         if (omap_connector->hpd) {
161                 struct omap_dss_device *hpd = omap_connector->hpd;
162
163                 hpd->ops->unregister_hpd_cb(hpd);
164                 omapdss_device_put(hpd);
165                 omap_connector->hpd = NULL;
166         }
167
168         drm_connector_unregister(connector);
169         drm_connector_cleanup(connector);
170
171         omapdss_device_put(omap_connector->output);
172
173         kfree(omap_connector);
174 }
175
176 #define MAX_EDID  512
177
178 static int omap_connector_get_modes_edid(struct drm_connector *connector,
179                                          struct omap_dss_device *dssdev)
180 {
181         struct omap_connector *omap_connector = to_omap_connector(connector);
182         enum drm_connector_status status;
183         void *edid;
184         int n;
185
186         status = omap_connector_detect(connector, false);
187         if (status != connector_status_connected)
188                 goto no_edid;
189
190         edid = kzalloc(MAX_EDID, GFP_KERNEL);
191         if (!edid)
192                 goto no_edid;
193
194         if (dssdev->ops->read_edid(dssdev, edid, MAX_EDID) <= 0 ||
195             !drm_edid_is_valid(edid)) {
196                 kfree(edid);
197                 goto no_edid;
198         }
199
200         drm_connector_update_edid_property(connector, edid);
201         n = drm_add_edid_modes(connector, edid);
202
203         omap_connector->hdmi_mode = drm_detect_hdmi_monitor(edid);
204
205         kfree(edid);
206         return n;
207
208 no_edid:
209         drm_connector_update_edid_property(connector, NULL);
210         return 0;
211 }
212
213 static int omap_connector_get_modes(struct drm_connector *connector)
214 {
215         struct omap_connector *omap_connector = to_omap_connector(connector);
216         struct omap_dss_device *dssdev;
217
218         DBG("%s", connector->name);
219
220         /*
221          * If display exposes EDID, then we parse that in the normal way to
222          * build table of supported modes.
223          */
224         dssdev = omap_connector_find_device(connector,
225                                             OMAP_DSS_DEVICE_OP_EDID);
226         if (dssdev)
227                 return omap_connector_get_modes_edid(connector, dssdev);
228
229         /*
230          * Otherwise if the display pipeline reports modes (e.g. with a fixed
231          * resolution panel or an analog TV output), query it.
232          */
233         dssdev = omap_connector_find_device(connector,
234                                             OMAP_DSS_DEVICE_OP_MODES);
235         if (dssdev)
236                 return dssdev->ops->get_modes(dssdev, connector);
237
238         /*
239          * Otherwise if the display pipeline uses a drm_panel, we delegate the
240          * operation to the panel API.
241          */
242         if (omap_connector->output->panel)
243                 return drm_panel_get_modes(omap_connector->output->panel);
244
245         /*
246          * We can't retrieve modes, which can happen for instance for a DVI or
247          * VGA output with the DDC bus unconnected. The KMS core will add the
248          * default modes.
249          */
250         return 0;
251 }
252
253 enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev,
254                                         const struct drm_display_mode *mode,
255                                         struct drm_display_mode *adjusted_mode)
256 {
257         int ret;
258
259         drm_mode_copy(adjusted_mode, mode);
260
261         for (; dssdev; dssdev = dssdev->next) {
262                 if (!dssdev->ops->check_timings)
263                         continue;
264
265                 ret = dssdev->ops->check_timings(dssdev, adjusted_mode);
266                 if (ret)
267                         return MODE_BAD;
268         }
269
270         return MODE_OK;
271 }
272
273 static enum drm_mode_status omap_connector_mode_valid(struct drm_connector *connector,
274                                  struct drm_display_mode *mode)
275 {
276         struct omap_connector *omap_connector = to_omap_connector(connector);
277         struct drm_display_mode new_mode = { { 0 } };
278         enum drm_mode_status status;
279
280         status = omap_connector_mode_fixup(omap_connector->output, mode,
281                                            &new_mode);
282         if (status != MODE_OK)
283                 goto done;
284
285         /* Check if vrefresh is still valid. */
286         if (drm_mode_vrefresh(mode) != drm_mode_vrefresh(&new_mode))
287                 status = MODE_NOCLOCK;
288
289 done:
290         DBG("connector: mode %s: " DRM_MODE_FMT,
291                         (status == MODE_OK) ? "valid" : "invalid",
292                         DRM_MODE_ARG(mode));
293
294         return status;
295 }
296
297 static const struct drm_connector_funcs omap_connector_funcs = {
298         .reset = drm_atomic_helper_connector_reset,
299         .detect = omap_connector_detect,
300         .fill_modes = drm_helper_probe_single_connector_modes,
301         .destroy = omap_connector_destroy,
302         .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
303         .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
304 };
305
306 static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
307         .get_modes = omap_connector_get_modes,
308         .mode_valid = omap_connector_mode_valid,
309 };
310
311 static int omap_connector_get_type(struct omap_dss_device *output)
312 {
313         struct omap_dss_device *display;
314         enum omap_display_type type;
315
316         display = omapdss_display_get(output);
317         type = display->type;
318         omapdss_device_put(display);
319
320         switch (type) {
321         case OMAP_DISPLAY_TYPE_HDMI:
322                 return DRM_MODE_CONNECTOR_HDMIA;
323         case OMAP_DISPLAY_TYPE_DVI:
324                 return DRM_MODE_CONNECTOR_DVID;
325         case OMAP_DISPLAY_TYPE_DSI:
326                 return DRM_MODE_CONNECTOR_DSI;
327         case OMAP_DISPLAY_TYPE_DPI:
328         case OMAP_DISPLAY_TYPE_DBI:
329                 return DRM_MODE_CONNECTOR_DPI;
330         case OMAP_DISPLAY_TYPE_VENC:
331                 /* TODO: This could also be composite */
332                 return DRM_MODE_CONNECTOR_SVIDEO;
333         case OMAP_DISPLAY_TYPE_SDI:
334                 return DRM_MODE_CONNECTOR_LVDS;
335         default:
336                 return DRM_MODE_CONNECTOR_Unknown;
337         }
338 }
339
340 /* initialize connector */
341 struct drm_connector *omap_connector_init(struct drm_device *dev,
342                                           struct omap_dss_device *output,
343                                           struct drm_encoder *encoder)
344 {
345         struct drm_connector *connector = NULL;
346         struct omap_connector *omap_connector;
347         struct omap_dss_device *dssdev;
348
349         DBG("%s", output->name);
350
351         omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
352         if (!omap_connector)
353                 goto fail;
354
355         omap_connector->output = omapdss_device_get(output);
356
357         connector = &omap_connector->base;
358         connector->interlace_allowed = 1;
359         connector->doublescan_allowed = 0;
360
361         drm_connector_init(dev, connector, &omap_connector_funcs,
362                            omap_connector_get_type(output));
363         drm_connector_helper_add(connector, &omap_connector_helper_funcs);
364
365         /*
366          * Initialize connector status handling. First try to find a device that
367          * supports hot-plug reporting. If it fails, fall back to a device that
368          * support polling. If that fails too, we don't support hot-plug
369          * detection at all.
370          */
371         dssdev = omap_connector_find_device(connector, OMAP_DSS_DEVICE_OP_HPD);
372         if (dssdev) {
373                 omap_connector->hpd = omapdss_device_get(dssdev);
374                 connector->polled = DRM_CONNECTOR_POLL_HPD;
375         } else {
376                 dssdev = omap_connector_find_device(connector,
377                                                     OMAP_DSS_DEVICE_OP_DETECT);
378                 if (dssdev)
379                         connector->polled = DRM_CONNECTOR_POLL_CONNECT |
380                                             DRM_CONNECTOR_POLL_DISCONNECT;
381         }
382
383         return connector;
384
385 fail:
386         if (connector)
387                 omap_connector_destroy(connector);
388
389         return NULL;
390 }