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