Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph...
[pandora-kernel.git] / drivers / staging / omapdrm / omap_connector.c
1 /*
2  * drivers/staging/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  */
19
20 #include "omap_drv.h"
21
22 #include "drm_crtc.h"
23 #include "drm_crtc_helper.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 *dssdev;
34 };
35
36 static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
37                 struct omap_video_timings *timings)
38 {
39         mode->clock = timings->pixel_clock;
40
41         mode->hdisplay = timings->x_res;
42         mode->hsync_start = mode->hdisplay + timings->hfp;
43         mode->hsync_end = mode->hsync_start + timings->hsw;
44         mode->htotal = mode->hsync_end + timings->hbp;
45
46         mode->vdisplay = timings->y_res;
47         mode->vsync_start = mode->vdisplay + timings->vfp;
48         mode->vsync_end = mode->vsync_start + timings->vsw;
49         mode->vtotal = mode->vsync_end + timings->vbp;
50
51         mode->flags = 0;
52
53         if (timings->interlace)
54                 mode->flags |= DRM_MODE_FLAG_INTERLACE;
55
56         if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
57                 mode->flags |= DRM_MODE_FLAG_PHSYNC;
58         else
59                 mode->flags |= DRM_MODE_FLAG_NHSYNC;
60
61         if (timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
62                 mode->flags |= DRM_MODE_FLAG_PVSYNC;
63         else
64                 mode->flags |= DRM_MODE_FLAG_NVSYNC;
65 }
66
67 static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
68                 struct drm_display_mode *mode)
69 {
70         timings->pixel_clock = mode->clock;
71
72         timings->x_res = mode->hdisplay;
73         timings->hfp = mode->hsync_start - mode->hdisplay;
74         timings->hsw = mode->hsync_end - mode->hsync_start;
75         timings->hbp = mode->htotal - mode->hsync_end;
76
77         timings->y_res = mode->vdisplay;
78         timings->vfp = mode->vsync_start - mode->vdisplay;
79         timings->vsw = mode->vsync_end - mode->vsync_start;
80         timings->vbp = mode->vtotal - mode->vsync_end;
81
82         timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
83
84         if (mode->flags & DRM_MODE_FLAG_PHSYNC)
85                 timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
86         else
87                 timings->hsync_level = OMAPDSS_SIG_ACTIVE_LOW;
88
89         if (mode->flags & DRM_MODE_FLAG_PVSYNC)
90                 timings->vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
91         else
92                 timings->vsync_level = OMAPDSS_SIG_ACTIVE_LOW;
93
94         timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
95         timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
96         timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
97 }
98
99 static void omap_connector_dpms(struct drm_connector *connector, int mode)
100 {
101         struct omap_connector *omap_connector = to_omap_connector(connector);
102         struct omap_dss_device *dssdev = omap_connector->dssdev;
103         int old_dpms;
104
105         DBG("%s: %d", dssdev->name, mode);
106
107         old_dpms = connector->dpms;
108
109         /* from off to on, do from crtc to connector */
110         if (mode < old_dpms)
111                 drm_helper_connector_dpms(connector, mode);
112
113         if (mode == DRM_MODE_DPMS_ON) {
114                 /* store resume info for suspended displays */
115                 switch (dssdev->state) {
116                 case OMAP_DSS_DISPLAY_SUSPENDED:
117                         dssdev->activate_after_resume = true;
118                         break;
119                 case OMAP_DSS_DISPLAY_DISABLED: {
120                         int ret = dssdev->driver->enable(dssdev);
121                         if (ret) {
122                                 DBG("%s: failed to enable: %d",
123                                                 dssdev->name, ret);
124                                 dssdev->driver->disable(dssdev);
125                         }
126                         break;
127                 }
128                 default:
129                         break;
130                 }
131         } else {
132                 /* TODO */
133         }
134
135         /* from on to off, do from connector to crtc */
136         if (mode > old_dpms)
137                 drm_helper_connector_dpms(connector, mode);
138 }
139
140 enum drm_connector_status omap_connector_detect(
141                 struct drm_connector *connector, bool force)
142 {
143         struct omap_connector *omap_connector = to_omap_connector(connector);
144         struct omap_dss_device *dssdev = omap_connector->dssdev;
145         struct omap_dss_driver *dssdrv = dssdev->driver;
146         enum drm_connector_status ret;
147
148         if (dssdrv->detect) {
149                 if (dssdrv->detect(dssdev))
150                         ret = connector_status_connected;
151                 else
152                         ret = connector_status_disconnected;
153         } else {
154                 ret = connector_status_unknown;
155         }
156
157         VERB("%s: %d (force=%d)", omap_connector->dssdev->name, ret, force);
158
159         return ret;
160 }
161
162 static void omap_connector_destroy(struct drm_connector *connector)
163 {
164         struct omap_connector *omap_connector = to_omap_connector(connector);
165         struct omap_dss_device *dssdev = omap_connector->dssdev;
166
167         dssdev->driver->disable(dssdev);
168
169         DBG("%s", omap_connector->dssdev->name);
170         drm_sysfs_connector_remove(connector);
171         drm_connector_cleanup(connector);
172         kfree(omap_connector);
173
174         omap_dss_put_device(dssdev);
175 }
176
177 #define MAX_EDID  512
178
179 static int omap_connector_get_modes(struct drm_connector *connector)
180 {
181         struct omap_connector *omap_connector = to_omap_connector(connector);
182         struct omap_dss_device *dssdev = omap_connector->dssdev;
183         struct omap_dss_driver *dssdrv = dssdev->driver;
184         struct drm_device *dev = connector->dev;
185         int n = 0;
186
187         DBG("%s", omap_connector->dssdev->name);
188
189         /* if display exposes EDID, then we parse that in the normal way to
190          * build table of supported modes.. otherwise (ie. fixed resolution
191          * LCD panels) we just return a single mode corresponding to the
192          * currently configured timings:
193          */
194         if (dssdrv->read_edid) {
195                 void *edid = kzalloc(MAX_EDID, GFP_KERNEL);
196
197                 if ((dssdrv->read_edid(dssdev, edid, MAX_EDID) > 0) &&
198                                 drm_edid_is_valid(edid)) {
199                         drm_mode_connector_update_edid_property(
200                                         connector, edid);
201                         n = drm_add_edid_modes(connector, edid);
202                 } else {
203                         drm_mode_connector_update_edid_property(
204                                         connector, NULL);
205                 }
206                 kfree(edid);
207         } else {
208                 struct drm_display_mode *mode = drm_mode_create(dev);
209                 struct omap_video_timings timings = {0};
210
211                 dssdrv->get_timings(dssdev, &timings);
212
213                 copy_timings_omap_to_drm(mode, &timings);
214
215                 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
216                 drm_mode_set_name(mode);
217                 drm_mode_probed_add(connector, mode);
218
219                 n = 1;
220         }
221
222         return n;
223 }
224
225 static int omap_connector_mode_valid(struct drm_connector *connector,
226                                  struct drm_display_mode *mode)
227 {
228         struct omap_connector *omap_connector = to_omap_connector(connector);
229         struct omap_dss_device *dssdev = omap_connector->dssdev;
230         struct omap_dss_driver *dssdrv = dssdev->driver;
231         struct omap_video_timings timings = {0};
232         struct drm_device *dev = connector->dev;
233         struct drm_display_mode *new_mode;
234         int ret = MODE_BAD;
235
236         copy_timings_drm_to_omap(&timings, mode);
237         mode->vrefresh = drm_mode_vrefresh(mode);
238
239         if (!dssdrv->check_timings(dssdev, &timings)) {
240                 /* check if vrefresh is still valid */
241                 new_mode = drm_mode_duplicate(dev, mode);
242                 new_mode->clock = timings.pixel_clock;
243                 new_mode->vrefresh = 0;
244                 if (mode->vrefresh == drm_mode_vrefresh(new_mode))
245                         ret = MODE_OK;
246                 drm_mode_destroy(dev, new_mode);
247         }
248
249         DBG("connector: mode %s: "
250                         "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
251                         (ret == MODE_OK) ? "valid" : "invalid",
252                         mode->base.id, mode->name, mode->vrefresh, mode->clock,
253                         mode->hdisplay, mode->hsync_start,
254                         mode->hsync_end, mode->htotal,
255                         mode->vdisplay, mode->vsync_start,
256                         mode->vsync_end, mode->vtotal, mode->type, mode->flags);
257
258         return ret;
259 }
260
261 struct drm_encoder *omap_connector_attached_encoder(
262                 struct drm_connector *connector)
263 {
264         int i;
265         struct omap_connector *omap_connector = to_omap_connector(connector);
266
267         for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
268                 struct drm_mode_object *obj;
269
270                 if (connector->encoder_ids[i] == 0)
271                         break;
272
273                 obj = drm_mode_object_find(connector->dev,
274                                 connector->encoder_ids[i],
275                                 DRM_MODE_OBJECT_ENCODER);
276
277                 if (obj) {
278                         struct drm_encoder *encoder = obj_to_encoder(obj);
279                         struct omap_overlay_manager *mgr =
280                                         omap_encoder_get_manager(encoder);
281                         DBG("%s: found %s", omap_connector->dssdev->name,
282                                         mgr->name);
283                         return encoder;
284                 }
285         }
286
287         DBG("%s: no encoder", omap_connector->dssdev->name);
288
289         return NULL;
290 }
291
292 static const struct drm_connector_funcs omap_connector_funcs = {
293         .dpms = omap_connector_dpms,
294         .detect = omap_connector_detect,
295         .fill_modes = drm_helper_probe_single_connector_modes,
296         .destroy = omap_connector_destroy,
297 };
298
299 static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
300         .get_modes = omap_connector_get_modes,
301         .mode_valid = omap_connector_mode_valid,
302         .best_encoder = omap_connector_attached_encoder,
303 };
304
305 /* called from encoder when mode is set, to propagate settings to the dssdev */
306 void omap_connector_mode_set(struct drm_connector *connector,
307                 struct drm_display_mode *mode)
308 {
309         struct drm_device *dev = connector->dev;
310         struct omap_connector *omap_connector = to_omap_connector(connector);
311         struct omap_dss_device *dssdev = omap_connector->dssdev;
312         struct omap_dss_driver *dssdrv = dssdev->driver;
313         struct omap_video_timings timings = {0};
314
315         copy_timings_drm_to_omap(&timings, mode);
316
317         DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
318                         omap_connector->dssdev->name,
319                         mode->base.id, mode->name, mode->vrefresh, mode->clock,
320                         mode->hdisplay, mode->hsync_start,
321                         mode->hsync_end, mode->htotal,
322                         mode->vdisplay, mode->vsync_start,
323                         mode->vsync_end, mode->vtotal, mode->type, mode->flags);
324
325         if (dssdrv->check_timings(dssdev, &timings)) {
326                 dev_err(dev->dev, "could not set timings\n");
327                 return;
328         }
329
330         dssdrv->set_timings(dssdev, &timings);
331 }
332
333 /* flush an area of the framebuffer (in case of manual update display that
334  * is not automatically flushed)
335  */
336 void omap_connector_flush(struct drm_connector *connector,
337                 int x, int y, int w, int h)
338 {
339         struct omap_connector *omap_connector = to_omap_connector(connector);
340
341         /* TODO: enable when supported in dss */
342         VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
343 }
344
345 /* initialize connector */
346 struct drm_connector *omap_connector_init(struct drm_device *dev,
347                 int connector_type, struct omap_dss_device *dssdev)
348 {
349         struct drm_connector *connector = NULL;
350         struct omap_connector *omap_connector;
351
352         DBG("%s", dssdev->name);
353
354         omap_dss_get_device(dssdev);
355
356         omap_connector = kzalloc(sizeof(struct omap_connector), GFP_KERNEL);
357         if (!omap_connector) {
358                 dev_err(dev->dev, "could not allocate connector\n");
359                 goto fail;
360         }
361
362         omap_connector->dssdev = dssdev;
363         connector = &omap_connector->base;
364
365         drm_connector_init(dev, connector, &omap_connector_funcs,
366                                 connector_type);
367         drm_connector_helper_add(connector, &omap_connector_helper_funcs);
368
369 #if 0 /* enable when dss2 supports hotplug */
370         if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_HPD)
371                 connector->polled = 0;
372         else
373 #endif
374                 connector->polled = DRM_CONNECTOR_POLL_CONNECT |
375                                 DRM_CONNECTOR_POLL_DISCONNECT;
376
377         connector->interlace_allowed = 1;
378         connector->doublescan_allowed = 0;
379
380         drm_sysfs_connector_add(connector);
381
382         return connector;
383
384 fail:
385         if (connector)
386                 omap_connector_destroy(connector);
387
388         return NULL;
389 }