2 * drivers/gpu/drm/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/>.
20 #include <drm/drm_atomic_helper.h>
21 #include <drm/drm_crtc.h>
22 #include <drm/drm_crtc_helper.h>
30 #define to_omap_connector(x) container_of(x, struct omap_connector, base)
32 struct omap_connector
{
33 struct drm_connector base
;
34 struct omap_dss_device
*dssdev
;
38 bool omap_connector_get_hdmi_mode(struct drm_connector
*connector
)
40 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
42 return omap_connector
->hdmi_mode
;
45 static enum drm_connector_status
omap_connector_detect(
46 struct drm_connector
*connector
, bool force
)
48 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
49 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
50 struct omap_dss_driver
*dssdrv
= dssdev
->driver
;
51 enum drm_connector_status ret
;
54 if (dssdrv
->detect(dssdev
))
55 ret
= connector_status_connected
;
57 ret
= connector_status_disconnected
;
58 } else if (dssdev
->type
== OMAP_DISPLAY_TYPE_DPI
||
59 dssdev
->type
== OMAP_DISPLAY_TYPE_DBI
||
60 dssdev
->type
== OMAP_DISPLAY_TYPE_SDI
||
61 dssdev
->type
== OMAP_DISPLAY_TYPE_DSI
) {
62 ret
= connector_status_connected
;
64 ret
= connector_status_unknown
;
67 VERB("%s: %d (force=%d)", omap_connector
->dssdev
->name
, ret
, force
);
72 static void omap_connector_destroy(struct drm_connector
*connector
)
74 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
75 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
77 DBG("%s", omap_connector
->dssdev
->name
);
78 drm_connector_unregister(connector
);
79 drm_connector_cleanup(connector
);
80 kfree(omap_connector
);
82 omap_dss_put_device(dssdev
);
87 static int omap_connector_get_modes(struct drm_connector
*connector
)
89 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
90 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
91 struct omap_dss_driver
*dssdrv
= dssdev
->driver
;
92 struct drm_device
*dev
= connector
->dev
;
95 DBG("%s", omap_connector
->dssdev
->name
);
97 /* if display exposes EDID, then we parse that in the normal way to
98 * build table of supported modes.. otherwise (ie. fixed resolution
99 * LCD panels) we just return a single mode corresponding to the
100 * currently configured timings:
102 if (dssdrv
->read_edid
) {
103 void *edid
= kzalloc(MAX_EDID
, GFP_KERNEL
);
105 if ((dssdrv
->read_edid(dssdev
, edid
, MAX_EDID
) > 0) &&
106 drm_edid_is_valid(edid
)) {
107 drm_mode_connector_update_edid_property(
109 n
= drm_add_edid_modes(connector
, edid
);
111 omap_connector
->hdmi_mode
=
112 drm_detect_hdmi_monitor(edid
);
114 drm_mode_connector_update_edid_property(
120 struct drm_display_mode
*mode
= drm_mode_create(dev
);
121 struct videomode vm
= {0};
123 dssdrv
->get_timings(dssdev
, &vm
);
125 drm_display_mode_from_videomode(&vm
, mode
);
127 mode
->type
= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
;
128 drm_mode_set_name(mode
);
129 drm_mode_probed_add(connector
, mode
);
137 static int omap_connector_mode_valid(struct drm_connector
*connector
,
138 struct drm_display_mode
*mode
)
140 struct omap_connector
*omap_connector
= to_omap_connector(connector
);
141 struct omap_dss_device
*dssdev
= omap_connector
->dssdev
;
142 struct omap_dss_driver
*dssdrv
= dssdev
->driver
;
143 struct videomode vm
= {0};
144 struct drm_device
*dev
= connector
->dev
;
145 struct drm_display_mode
*new_mode
;
146 int r
, ret
= MODE_BAD
;
148 drm_display_mode_to_videomode(mode
, &vm
);
149 mode
->vrefresh
= drm_mode_vrefresh(mode
);
152 * if the panel driver doesn't have a check_timings, it's most likely
153 * a fixed resolution panel, check if the timings match with the
156 if (dssdrv
->check_timings
) {
157 r
= dssdrv
->check_timings(dssdev
, &vm
);
159 struct videomode t
= {0};
161 dssdrv
->get_timings(dssdev
, &t
);
164 * Ignore the flags, as we don't get them from
165 * drm_display_mode_to_videomode.
169 if (memcmp(&vm
, &t
, sizeof(vm
)))
176 /* check if vrefresh is still valid */
177 new_mode
= drm_mode_duplicate(dev
, mode
);
178 new_mode
->clock
= vm
.pixelclock
/ 1000;
179 new_mode
->vrefresh
= 0;
180 if (mode
->vrefresh
== drm_mode_vrefresh(new_mode
))
182 drm_mode_destroy(dev
, new_mode
);
185 DBG("connector: mode %s: "
186 "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
187 (ret
== MODE_OK
) ? "valid" : "invalid",
188 mode
->base
.id
, mode
->name
, mode
->vrefresh
, mode
->clock
,
189 mode
->hdisplay
, mode
->hsync_start
,
190 mode
->hsync_end
, mode
->htotal
,
191 mode
->vdisplay
, mode
->vsync_start
,
192 mode
->vsync_end
, mode
->vtotal
, mode
->type
, mode
->flags
);
197 static const struct drm_connector_funcs omap_connector_funcs
= {
198 .dpms
= drm_atomic_helper_connector_dpms
,
199 .reset
= drm_atomic_helper_connector_reset
,
200 .detect
= omap_connector_detect
,
201 .fill_modes
= drm_helper_probe_single_connector_modes
,
202 .destroy
= omap_connector_destroy
,
203 .atomic_duplicate_state
= drm_atomic_helper_connector_duplicate_state
,
204 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
207 static const struct drm_connector_helper_funcs omap_connector_helper_funcs
= {
208 .get_modes
= omap_connector_get_modes
,
209 .mode_valid
= omap_connector_mode_valid
,
212 /* initialize connector */
213 struct drm_connector
*omap_connector_init(struct drm_device
*dev
,
214 int connector_type
, struct omap_dss_device
*dssdev
,
215 struct drm_encoder
*encoder
)
217 struct drm_connector
*connector
= NULL
;
218 struct omap_connector
*omap_connector
;
220 DBG("%s", dssdev
->name
);
222 omap_dss_get_device(dssdev
);
224 omap_connector
= kzalloc(sizeof(*omap_connector
), GFP_KERNEL
);
228 omap_connector
->dssdev
= dssdev
;
230 connector
= &omap_connector
->base
;
232 drm_connector_init(dev
, connector
, &omap_connector_funcs
,
234 drm_connector_helper_add(connector
, &omap_connector_helper_funcs
);
236 if (dssdev
->driver
->detect
)
237 connector
->polled
= DRM_CONNECTOR_POLL_CONNECT
|
238 DRM_CONNECTOR_POLL_DISCONNECT
;
240 connector
->polled
= 0;
242 connector
->interlace_allowed
= 1;
243 connector
->doublescan_allowed
= 0;
249 omap_connector_destroy(connector
);