2 * drivers/staging/omapdrm/omap_connector.c
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
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.
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
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/>.
23 #include "drm_crtc_helper.h"
29 #define to_omap_connector(x) container_of(x, struct omap_connector, base)
31 struct omap_connector
{
32 struct drm_connector base
;
33 struct omap_dss_device
*dssdev
;
36 static inline void copy_timings_omap_to_drm(struct drm_display_mode
*mode
,
37 struct omap_video_timings
*timings
)
39 mode
->clock
= timings
->pixel_clock
;
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
;
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
;
51 /* note: whether or not it is interlaced, +/- h/vsync, etc,
52 * which should be set in the mode flags, is not exposed in
53 * the omap_video_timings struct.. but hdmi driver tracks
54 * those separately so all we have to have to set the mode
55 * is the way to recover these timings values, and the
56 * omap_dss_driver would do the rest.
60 static inline void copy_timings_drm_to_omap(struct omap_video_timings
*timings
,
61 struct drm_display_mode
*mode
)
63 timings
->pixel_clock
= mode
->clock
;
65 timings
->x_res
= mode
->hdisplay
;
66 timings
->hfp
= mode
->hsync_start
- mode
->hdisplay
;
67 timings
->hsw
= mode
->hsync_end
- mode
->hsync_start
;
68 timings
->hbp
= mode
->htotal
- mode
->hsync_end
;
70 timings
->y_res
= mode
->vdisplay
;
71 timings
->vfp
= mode
->vsync_start
- mode
->vdisplay
;
72 timings
->vsw
= mode
->vsync_end
- mode
->vsync_start
;
73 timings
->vbp
= mode
->vtotal
- mode
->vsync_end
;
76 static void omap_connector_dpms(struct drm_connector
*connector
, int mode
)
78 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
79 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
82 DBG("%s: %d", dssdev
->name
, mode
);
84 old_dpms
= connector
->dpms
;
86 /* from off to on, do from crtc to connector */
88 drm_helper_connector_dpms(connector
, mode
);
90 if (mode
== DRM_MODE_DPMS_ON
) {
91 /* store resume info for suspended displays */
92 switch (dssdev
->state
) {
93 case OMAP_DSS_DISPLAY_SUSPENDED
:
94 dssdev
->activate_after_resume
= true;
96 case OMAP_DSS_DISPLAY_DISABLED
: {
97 int ret
= dssdev
->driver
->enable(dssdev
);
99 DBG("%s: failed to enable: %d",
101 dssdev
->driver
->disable(dssdev
);
112 /* from on to off, do from connector to crtc */
114 drm_helper_connector_dpms(connector
, mode
);
117 enum drm_connector_status
omap_connector_detect(
118 struct drm_connector
*connector
, bool force
)
120 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
121 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
122 struct omap_dss_driver
*dssdrv
= dssdev
->driver
;
123 enum drm_connector_status ret
;
125 if (dssdrv
->detect
) {
126 if (dssdrv
->detect(dssdev
)) {
127 ret
= connector_status_connected
;
129 ret
= connector_status_disconnected
;
132 ret
= connector_status_unknown
;
135 VERB("%s: %d (force=%d)", omap_connector
->dssdev
->name
, ret
, force
);
140 static void omap_connector_destroy(struct drm_connector
*connector
)
142 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
143 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
145 dssdev
->driver
->disable(dssdev
);
147 DBG("%s", omap_connector
->dssdev
->name
);
148 drm_sysfs_connector_remove(connector
);
149 drm_connector_cleanup(connector
);
150 kfree(omap_connector
);
152 omap_dss_put_device(dssdev
);
157 static int omap_connector_get_modes(struct drm_connector
*connector
)
159 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
160 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
161 struct omap_dss_driver
*dssdrv
= dssdev
->driver
;
162 struct drm_device
*dev
= connector
->dev
;
165 DBG("%s", omap_connector
->dssdev
->name
);
167 /* if display exposes EDID, then we parse that in the normal way to
168 * build table of supported modes.. otherwise (ie. fixed resolution
169 * LCD panels) we just return a single mode corresponding to the
170 * currently configured timings:
172 if (dssdrv
->read_edid
) {
173 void *edid
= kzalloc(MAX_EDID
, GFP_KERNEL
);
175 if ((dssdrv
->read_edid(dssdev
, edid
, MAX_EDID
) > 0) &&
176 drm_edid_is_valid(edid
)) {
177 drm_mode_connector_update_edid_property(
179 n
= drm_add_edid_modes(connector
, edid
);
181 drm_mode_connector_update_edid_property(
186 struct drm_display_mode
*mode
= drm_mode_create(dev
);
187 struct omap_video_timings timings
;
189 dssdrv
->get_timings(dssdev
, &timings
);
191 copy_timings_omap_to_drm(mode
, &timings
);
193 mode
->type
= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
;
194 drm_mode_set_name(mode
);
195 drm_mode_probed_add(connector
, mode
);
203 static int omap_connector_mode_valid(struct drm_connector
*connector
,
204 struct drm_display_mode
*mode
)
206 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
207 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
208 struct omap_dss_driver
*dssdrv
= dssdev
->driver
;
209 struct omap_video_timings timings
= {0};
210 struct drm_device
*dev
= connector
->dev
;
211 struct drm_display_mode
*new_mode
;
214 copy_timings_drm_to_omap(&timings
, mode
);
215 mode
->vrefresh
= drm_mode_vrefresh(mode
);
217 if (!dssdrv
->check_timings(dssdev
, &timings
)) {
218 /* check if vrefresh is still valid */
219 new_mode
= drm_mode_duplicate(dev
, mode
);
220 new_mode
->clock
= timings
.pixel_clock
;
221 new_mode
->vrefresh
= 0;
222 if (mode
->vrefresh
== drm_mode_vrefresh(new_mode
))
224 drm_mode_destroy(dev
, new_mode
);
227 DBG("connector: mode %s: "
228 "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
229 (ret
== MODE_OK
) ? "valid" : "invalid",
230 mode
->base
.id
, mode
->name
, mode
->vrefresh
, mode
->clock
,
231 mode
->hdisplay
, mode
->hsync_start
,
232 mode
->hsync_end
, mode
->htotal
,
233 mode
->vdisplay
, mode
->vsync_start
,
234 mode
->vsync_end
, mode
->vtotal
, mode
->type
, mode
->flags
);
239 struct drm_encoder
*omap_connector_attached_encoder(
240 struct drm_connector
*connector
)
243 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
245 for (i
= 0; i
< DRM_CONNECTOR_MAX_ENCODER
; i
++) {
246 struct drm_mode_object
*obj
;
248 if (connector
->encoder_ids
[i
] == 0)
251 obj
= drm_mode_object_find(connector
->dev
,
252 connector
->encoder_ids
[i
],
253 DRM_MODE_OBJECT_ENCODER
);
256 struct drm_encoder
*encoder
= obj_to_encoder(obj
);
257 struct omap_overlay_manager
*mgr
=
258 omap_encoder_get_manager(encoder
);
259 DBG("%s: found %s", omap_connector
->dssdev
->name
,
265 DBG("%s: no encoder", omap_connector
->dssdev
->name
);
270 static const struct drm_connector_funcs omap_connector_funcs
= {
271 .dpms
= omap_connector_dpms
,
272 .detect
= omap_connector_detect
,
273 .fill_modes
= drm_helper_probe_single_connector_modes
,
274 .destroy
= omap_connector_destroy
,
277 static const struct drm_connector_helper_funcs omap_connector_helper_funcs
= {
278 .get_modes
= omap_connector_get_modes
,
279 .mode_valid
= omap_connector_mode_valid
,
280 .best_encoder
= omap_connector_attached_encoder
,
283 /* called from encoder when mode is set, to propagate settings to the dssdev */
284 void omap_connector_mode_set(struct drm_connector
*connector
,
285 struct drm_display_mode
*mode
)
287 struct drm_device
*dev
= connector
->dev
;
288 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
289 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
290 struct omap_dss_driver
*dssdrv
= dssdev
->driver
;
291 struct omap_video_timings timings
;
293 copy_timings_drm_to_omap(&timings
, mode
);
295 DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
296 omap_connector
->dssdev
->name
,
297 mode
->base
.id
, mode
->name
, mode
->vrefresh
, mode
->clock
,
298 mode
->hdisplay
, mode
->hsync_start
,
299 mode
->hsync_end
, mode
->htotal
,
300 mode
->vdisplay
, mode
->vsync_start
,
301 mode
->vsync_end
, mode
->vtotal
, mode
->type
, mode
->flags
);
303 if (dssdrv
->check_timings(dssdev
, &timings
)) {
304 dev_err(dev
->dev
, "could not set timings\n");
308 dssdrv
->set_timings(dssdev
, &timings
);
311 /* flush an area of the framebuffer (in case of manual update display that
312 * is not automatically flushed)
314 void omap_connector_flush(struct drm_connector
*connector
,
315 int x
, int y
, int w
, int h
)
317 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
319 /* TODO: enable when supported in dss */
320 VERB("%s: %d,%d, %dx%d", omap_connector
->dssdev
->name
, x
, y
, w
, h
);
323 /* initialize connector */
324 struct drm_connector
*omap_connector_init(struct drm_device
*dev
,
325 int connector_type
, struct omap_dss_device
*dssdev
)
327 struct drm_connector
*connector
= NULL
;
328 struct omap_connector
*omap_connector
;
330 DBG("%s", dssdev
->name
);
332 omap_dss_get_device(dssdev
);
334 omap_connector
= kzalloc(sizeof(struct omap_connector
), GFP_KERNEL
);
335 if (!omap_connector
) {
336 dev_err(dev
->dev
, "could not allocate connector\n");
340 omap_connector
->dssdev
= dssdev
;
341 connector
= &omap_connector
->base
;
343 drm_connector_init(dev
, connector
, &omap_connector_funcs
,
345 drm_connector_helper_add(connector
, &omap_connector_helper_funcs
);
347 #if 0 /* enable when dss2 supports hotplug */
348 if (dssdev
->caps
& OMAP_DSS_DISPLAY_CAP_HPD
)
349 connector
->polled
= 0;
352 connector
->polled
= DRM_CONNECTOR_POLL_CONNECT
|
353 DRM_CONNECTOR_POLL_DISCONNECT
;
355 connector
->interlace_allowed
= 1;
356 connector
->doublescan_allowed
= 0;
358 drm_sysfs_connector_add(connector
);
364 omap_connector_destroy(connector
);