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