]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/kernel-video.git/blob - drivers/gpu/drm/omapdrm/omap_connector.c
drm/omap: fix leak & crash in omap_gem_op_sync()
[android-sdk/kernel-video.git] / drivers / gpu / drm / omapdrm / omap_connector.c
1 /*
2  * drivers/gpu/drm/omapdrm/omap_connector.c
3  *
4  * Copyright (C) 2011 Texas Instruments
5  * Author: Rob Clark <rob@ti.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published by
9  * the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
20 #include "omap_drv.h"
22 #include "drm_crtc.h"
23 #include "drm_crtc_helper.h"
25 #define OMAP_DRM_MODE_FLAG_DATA_RISING  (1 << 24)
26 #define OMAP_DRM_MODE_FLAG_DATA_FALLING (1 << 25)
27 #define OMAP_DRM_MODE_FLAG_SYNC_RISING  (1 << 26)
28 #define OMAP_DRM_MODE_FLAG_SYNC_FALLING (1 << 27)
29 #define OMAP_DRM_MODE_FLAG_PDE          (1 << 28)
30 #define OMAP_DRM_MODE_FLAG_NDE          (1 << 29)
32 /*
33  * connector funcs
34  */
36 #define to_omap_connector(x) container_of(x, struct omap_connector, base)
38 struct omap_connector {
39         struct drm_connector base;
40         struct omap_dss_device *dssdev;
41         struct drm_encoder *encoder;
42         bool hdmi_mode;
43 };
45 bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
46 {
47         struct omap_connector *omap_connector = to_omap_connector(connector);
49         return omap_connector->hdmi_mode;
50 }
52 void copy_timings_omap_to_drm(struct drm_display_mode *mode,
53                 struct omap_video_timings *timings)
54 {
55         mode->clock = timings->pixelclock / 1000;
57         mode->hdisplay = timings->x_res;
58         mode->hsync_start = mode->hdisplay + timings->hfp;
59         mode->hsync_end = mode->hsync_start + timings->hsw;
60         mode->htotal = mode->hsync_end + timings->hbp;
62         mode->vdisplay = timings->y_res;
63         mode->vsync_start = mode->vdisplay + timings->vfp;
64         mode->vsync_end = mode->vsync_start + timings->vsw;
65         mode->vtotal = mode->vsync_end + timings->vbp;
67         mode->flags = 0;
69         if (timings->interlace)
70                 mode->flags |= DRM_MODE_FLAG_INTERLACE;
72         if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
73                 mode->flags |= DRM_MODE_FLAG_PHSYNC;
74         else
75                 mode->flags |= DRM_MODE_FLAG_NHSYNC;
77         if (timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
78                 mode->flags |= DRM_MODE_FLAG_PVSYNC;
79         else
80                 mode->flags |= DRM_MODE_FLAG_NVSYNC;
82         if (timings->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE)
83                 mode->flags |= OMAP_DRM_MODE_FLAG_DATA_RISING;
84         else
85                 mode->flags |= OMAP_DRM_MODE_FLAG_DATA_FALLING;
87         if (timings->sync_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE)
88                 mode->flags |= OMAP_DRM_MODE_FLAG_SYNC_RISING;
89         else
90                 mode->flags |= OMAP_DRM_MODE_FLAG_SYNC_FALLING;
92         if (timings->de_level == OMAPDSS_SIG_ACTIVE_HIGH)
93                 mode->flags |= OMAP_DRM_MODE_FLAG_PDE;
94         else
95                 mode->flags |= OMAP_DRM_MODE_FLAG_NDE;
96 }
98 void copy_timings_drm_to_omap(struct omap_video_timings *timings,
99                 struct drm_display_mode *mode)
101         timings->pixelclock = mode->clock * 1000;
103         timings->x_res = mode->hdisplay;
104         timings->hfp = mode->hsync_start - mode->hdisplay;
105         timings->hsw = mode->hsync_end - mode->hsync_start;
106         timings->hbp = mode->htotal - mode->hsync_end;
108         timings->y_res = mode->vdisplay;
109         timings->vfp = mode->vsync_start - mode->vdisplay;
110         timings->vsw = mode->vsync_end - mode->vsync_start;
111         timings->vbp = mode->vtotal - mode->vsync_end;
113         timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
115         if (mode->flags & DRM_MODE_FLAG_PHSYNC)
116                 timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
117         else
118                 timings->hsync_level = OMAPDSS_SIG_ACTIVE_LOW;
120         if (mode->flags & DRM_MODE_FLAG_PVSYNC)
121                 timings->vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
122         else
123                 timings->vsync_level = OMAPDSS_SIG_ACTIVE_LOW;
125         if (mode->flags & OMAP_DRM_MODE_FLAG_DATA_RISING)
126                 timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
127         else
128                 timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE;
130         if (mode->flags & OMAP_DRM_MODE_FLAG_SYNC_RISING)
131                 timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
132         else
133                 timings->de_level = OMAPDSS_SIG_ACTIVE_LOW;
135         if (mode->flags & OMAP_DRM_MODE_FLAG_PDE)
136                 timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
137         else
138                 timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE;
141 static enum drm_connector_status omap_connector_detect(
142                 struct drm_connector *connector, bool force)
144         struct omap_connector *omap_connector = to_omap_connector(connector);
145         struct omap_dss_device *dssdev = omap_connector->dssdev;
146         struct omap_dss_driver *dssdrv = dssdev->driver;
147         enum drm_connector_status ret;
149         if (dssdrv->detect) {
150                 if (dssdrv->detect(dssdev))
151                         ret = connector_status_connected;
152                 else
153                         ret = connector_status_disconnected;
154         } else if (dssdev->type == OMAP_DISPLAY_TYPE_DPI ||
155                         dssdev->type == OMAP_DISPLAY_TYPE_DBI ||
156                         dssdev->type == OMAP_DISPLAY_TYPE_SDI ||
157                         dssdev->type == OMAP_DISPLAY_TYPE_DSI) {
158                 ret = connector_status_connected;
159         } else {
160                 ret = connector_status_unknown;
161         }
163         VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force);
165         return ret;
168 static void omap_connector_destroy(struct drm_connector *connector)
170         struct omap_connector *omap_connector = to_omap_connector(connector);
171         struct omap_dss_device *dssdev = omap_connector->dssdev;
173         DBG("%s", omap_connector->dssdev->name);
174         drm_sysfs_connector_remove(connector);
175         drm_connector_cleanup(connector);
176         kfree(omap_connector);
178         omap_dss_put_device(dssdev);
181 #define MAX_EDID  512
183 static int omap_connector_get_modes(struct drm_connector *connector)
185         struct omap_connector *omap_connector = to_omap_connector(connector);
186         struct omap_dss_device *dssdev = omap_connector->dssdev;
187         struct omap_dss_driver *dssdrv = dssdev->driver;
188         struct drm_device *dev = connector->dev;
189         int n = 0;
191         DBG("%s", omap_connector->dssdev->name);
193         /* if display exposes EDID, then we parse that in the normal way to
194          * build table of supported modes.. otherwise (ie. fixed resolution
195          * LCD panels) we just return a single mode corresponding to the
196          * currently configured timings:
197          */
198         if (dssdrv->read_edid) {
199                 void *edid = kzalloc(MAX_EDID, GFP_KERNEL);
201                 if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) &&
202                                 drm_edid_is_valid(edid)) {
203                         drm_mode_connector_update_edid_property(
204                                         connector, edid);
205                         n = drm_add_edid_modes(connector, edid);
207                         omap_connector->hdmi_mode =
208                                 drm_detect_hdmi_monitor(edid);
209                 } else {
210                         drm_mode_connector_update_edid_property(
211                                         connector, NULL);
212                 }
214                 kfree(edid);
215         } else {
216                 struct drm_display_mode *mode = drm_mode_create(dev);
217                 struct omap_video_timings timings = {0};
219                 dssdrv->get_timings(dssdev, &timings);
221                 copy_timings_omap_to_drm(mode, &timings);
223                 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
224                 drm_mode_set_name(mode);
225                 drm_mode_probed_add(connector, mode);
227                 n = 1;
228         }
230         return n;
233 static int omap_connector_mode_valid(struct drm_connector *connector,
234                                  struct drm_display_mode *mode)
236         struct omap_connector *omap_connector = to_omap_connector(connector);
237         struct omap_dss_device *dssdev = omap_connector->dssdev;
238         struct omap_dss_driver *dssdrv = dssdev->driver;
239         struct omap_video_timings timings = {0};
240         struct drm_device *dev = connector->dev;
241         struct drm_display_mode *new_mode;
242         int r, ret = MODE_BAD;
244         copy_timings_drm_to_omap(&timings, mode);
245         mode->vrefresh = drm_mode_vrefresh(mode);
247         /*
248          * if the panel driver doesn't have a check_timings, it's most likely
249          * a fixed resolution panel, check if the timings match with the
250          * panel's timings
251          */
252         if (dssdrv->check_timings) {
253                 r = dssdrv->check_timings(dssdev, &timings);
254         } else {
255                 struct omap_video_timings t = {0};
257                 dssdrv->get_timings(dssdev, &t);
259                 if (memcmp(&timings, &t, sizeof(struct omap_video_timings)))
260                         r = -EINVAL;
261                 else
262                         r = 0;
263         }
265         if (!r) {
266                 /* check if vrefresh is still valid */
267                 new_mode = drm_mode_duplicate(dev, mode);
268                 new_mode->clock = timings.pixelclock / 1000;
269                 new_mode->vrefresh = 0;
270                 if (mode->vrefresh == drm_mode_vrefresh(new_mode))
271                         ret = MODE_OK;
272                 drm_mode_destroy(dev, new_mode);
273         }
275         DBG("connector: mode %s: "
276                         "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
277                         (ret == MODE_OK) ? "valid" : "invalid",
278                         mode->base.id, mode->name, mode->vrefresh, mode->clock,
279                         mode->hdisplay, mode->hsync_start,
280                         mode->hsync_end, mode->htotal,
281                         mode->vdisplay, mode->vsync_start,
282                         mode->vsync_end, mode->vtotal, mode->type, mode->flags);
284         return ret;
287 struct drm_encoder *omap_connector_attached_encoder(
288                 struct drm_connector *connector)
290         struct omap_connector *omap_connector = to_omap_connector(connector);
291         return omap_connector->encoder;
294 static const struct drm_connector_funcs omap_connector_funcs = {
295         .dpms = drm_helper_connector_dpms,
296         .detect = omap_connector_detect,
297         .fill_modes = drm_helper_probe_single_connector_modes,
298         .destroy = omap_connector_destroy,
299 };
301 static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
302         .get_modes = omap_connector_get_modes,
303         .mode_valid = omap_connector_mode_valid,
304         .best_encoder = omap_connector_attached_encoder,
305 };
307 /* flush an area of the framebuffer (in case of manual update display that
308  * is not automatically flushed)
309  */
310 void omap_connector_flush(struct drm_connector *connector,
311                 int x, int y, int w, int h)
313         struct omap_connector *omap_connector = to_omap_connector(connector);
315         /* TODO: enable when supported in dss */
316         VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
319 /* initialize connector */
320 struct drm_connector *omap_connector_init(struct drm_device *dev,
321                 int connector_type, struct omap_dss_device *dssdev,
322                 struct drm_encoder *encoder)
324         struct drm_connector *connector = NULL;
325         struct omap_connector *omap_connector;
327         DBG("%s", dssdev->name);
329         omap_dss_get_device(dssdev);
331         omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL);
332         if (!omap_connector)
333                 goto fail;
335         omap_connector->dssdev = dssdev;
336         omap_connector->encoder = encoder;
338         connector = &omap_connector->base;
340         drm_connector_init(dev, connector, &omap_connector_funcs,
341                                 connector_type);
342         drm_connector_helper_add(connector, &omap_connector_helper_funcs);
344 #if 0 /* enable when dss2 supports hotplug */
345         if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD)
346                 connector->polled = 0;
347         else
348 #endif
349                 connector->polled = DRM_CONNECTOR_POLL_CONNECT |
350                                 DRM_CONNECTOR_POLL_DISCONNECT;
352         connector->interlace_allowed = 1;
353         connector->doublescan_allowed = 0;
355         drm_sysfs_connector_add(connector);
357         return connector;
359 fail:
360         if (connector)
361                 omap_connector_destroy(connector);
363         return NULL;