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