2 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
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.
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
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/>.
18 #include <drm/drm_atomic_helper.h>
19 #include <drm/drm_crtc.h>
20 #include <drm/drm_panel.h>
21 #include <drm/drm_probe_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 *output;
34 struct omap_dss_device *hpd;
38 static void omap_connector_hpd_notify(struct drm_connector *connector,
39 enum drm_connector_status status)
41 struct omap_connector *omap_connector = to_omap_connector(connector);
42 struct omap_dss_device *dssdev;
44 if (status != connector_status_disconnected)
48 * Notify all devics in the pipeline of disconnection. This is required
49 * to let the HDMI encoders reset their internal state related to
50 * connection status, such as the CEC address.
52 for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) {
53 if (dssdev->ops && dssdev->ops->hdmi.lost_hotplug)
54 dssdev->ops->hdmi.lost_hotplug(dssdev);
58 static void omap_connector_hpd_cb(void *cb_data,
59 enum drm_connector_status status)
61 struct omap_connector *omap_connector = cb_data;
62 struct drm_connector *connector = &omap_connector->base;
63 struct drm_device *dev = connector->dev;
64 enum drm_connector_status old_status;
66 mutex_lock(&dev->mode_config.mutex);
67 old_status = connector->status;
68 connector->status = status;
69 mutex_unlock(&dev->mode_config.mutex);
71 if (old_status == status)
74 omap_connector_hpd_notify(connector, status);
76 drm_kms_helper_hotplug_event(dev);
79 void omap_connector_enable_hpd(struct drm_connector *connector)
81 struct omap_connector *omap_connector = to_omap_connector(connector);
82 struct omap_dss_device *hpd = omap_connector->hpd;
85 hpd->ops->register_hpd_cb(hpd, omap_connector_hpd_cb,
89 void omap_connector_disable_hpd(struct drm_connector *connector)
91 struct omap_connector *omap_connector = to_omap_connector(connector);
92 struct omap_dss_device *hpd = omap_connector->hpd;
95 hpd->ops->unregister_hpd_cb(hpd);
98 bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
100 struct omap_connector *omap_connector = to_omap_connector(connector);
102 return omap_connector->hdmi_mode;
105 static struct omap_dss_device *
106 omap_connector_find_device(struct drm_connector *connector,
107 enum omap_dss_device_ops_flag op)
109 struct omap_connector *omap_connector = to_omap_connector(connector);
110 struct omap_dss_device *dssdev = NULL;
111 struct omap_dss_device *d;
113 for (d = omap_connector->output; d; d = d->next) {
114 if (d->ops_flags & op)
121 static enum drm_connector_status omap_connector_detect(
122 struct drm_connector *connector, bool force)
124 struct omap_dss_device *dssdev;
125 enum drm_connector_status status;
127 dssdev = omap_connector_find_device(connector,
128 OMAP_DSS_DEVICE_OP_DETECT);
131 status = dssdev->ops->detect(dssdev)
132 ? connector_status_connected
133 : connector_status_disconnected;
135 omap_connector_hpd_notify(connector, status);
137 switch (connector->connector_type) {
138 case DRM_MODE_CONNECTOR_DPI:
139 case DRM_MODE_CONNECTOR_LVDS:
140 case DRM_MODE_CONNECTOR_DSI:
141 status = connector_status_connected;
144 status = connector_status_unknown;
149 VERB("%s: %d (force=%d)", connector->name, status, force);
154 static void omap_connector_destroy(struct drm_connector *connector)
156 struct omap_connector *omap_connector = to_omap_connector(connector);
158 DBG("%s", connector->name);
160 if (omap_connector->hpd) {
161 struct omap_dss_device *hpd = omap_connector->hpd;
163 hpd->ops->unregister_hpd_cb(hpd);
164 omapdss_device_put(hpd);
165 omap_connector->hpd = NULL;
168 drm_connector_unregister(connector);
169 drm_connector_cleanup(connector);
171 omapdss_device_put(omap_connector->output);
173 kfree(omap_connector);
178 static int omap_connector_get_modes_edid(struct drm_connector *connector,
179 struct omap_dss_device *dssdev)
181 struct omap_connector *omap_connector = to_omap_connector(connector);
182 enum drm_connector_status status;
186 status = omap_connector_detect(connector, false);
187 if (status != connector_status_connected)
190 edid = kzalloc(MAX_EDID, GFP_KERNEL);
194 if (dssdev->ops->read_edid(dssdev, edid, MAX_EDID) <= 0 ||
195 !drm_edid_is_valid(edid)) {
200 drm_connector_update_edid_property(connector, edid);
201 n = drm_add_edid_modes(connector, edid);
203 omap_connector->hdmi_mode = drm_detect_hdmi_monitor(edid);
209 drm_connector_update_edid_property(connector, NULL);
213 static int omap_connector_get_modes(struct drm_connector *connector)
215 struct omap_connector *omap_connector = to_omap_connector(connector);
216 struct omap_dss_device *dssdev;
218 DBG("%s", connector->name);
221 * If display exposes EDID, then we parse that in the normal way to
222 * build table of supported modes.
224 dssdev = omap_connector_find_device(connector,
225 OMAP_DSS_DEVICE_OP_EDID);
227 return omap_connector_get_modes_edid(connector, dssdev);
230 * Otherwise if the display pipeline reports modes (e.g. with a fixed
231 * resolution panel or an analog TV output), query it.
233 dssdev = omap_connector_find_device(connector,
234 OMAP_DSS_DEVICE_OP_MODES);
236 return dssdev->ops->get_modes(dssdev, connector);
239 * Otherwise if the display pipeline uses a drm_panel, we delegate the
240 * operation to the panel API.
242 if (omap_connector->output->panel)
243 return drm_panel_get_modes(omap_connector->output->panel);
246 * We can't retrieve modes, which can happen for instance for a DVI or
247 * VGA output with the DDC bus unconnected. The KMS core will add the
253 enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev,
254 const struct drm_display_mode *mode,
255 struct drm_display_mode *adjusted_mode)
259 drm_mode_copy(adjusted_mode, mode);
261 for (; dssdev; dssdev = dssdev->next) {
262 if (!dssdev->ops->check_timings)
265 ret = dssdev->ops->check_timings(dssdev, adjusted_mode);
273 static enum drm_mode_status omap_connector_mode_valid(struct drm_connector *connector,
274 struct drm_display_mode *mode)
276 struct omap_connector *omap_connector = to_omap_connector(connector);
277 struct drm_display_mode new_mode = { { 0 } };
278 enum drm_mode_status status;
280 status = omap_connector_mode_fixup(omap_connector->output, mode,
282 if (status != MODE_OK)
285 /* Check if vrefresh is still valid. */
286 if (drm_mode_vrefresh(mode) != drm_mode_vrefresh(&new_mode))
287 status = MODE_NOCLOCK;
290 DBG("connector: mode %s: " DRM_MODE_FMT,
291 (status == MODE_OK) ? "valid" : "invalid",
297 static const struct drm_connector_funcs omap_connector_funcs = {
298 .reset = drm_atomic_helper_connector_reset,
299 .detect = omap_connector_detect,
300 .fill_modes = drm_helper_probe_single_connector_modes,
301 .destroy = omap_connector_destroy,
302 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
303 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
306 static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
307 .get_modes = omap_connector_get_modes,
308 .mode_valid = omap_connector_mode_valid,
311 static int omap_connector_get_type(struct omap_dss_device *output)
313 struct omap_dss_device *display;
314 enum omap_display_type type;
316 display = omapdss_display_get(output);
317 type = display->type;
318 omapdss_device_put(display);
321 case OMAP_DISPLAY_TYPE_HDMI:
322 return DRM_MODE_CONNECTOR_HDMIA;
323 case OMAP_DISPLAY_TYPE_DVI:
324 return DRM_MODE_CONNECTOR_DVID;
325 case OMAP_DISPLAY_TYPE_DSI:
326 return DRM_MODE_CONNECTOR_DSI;
327 case OMAP_DISPLAY_TYPE_DPI:
328 case OMAP_DISPLAY_TYPE_DBI:
329 return DRM_MODE_CONNECTOR_DPI;
330 case OMAP_DISPLAY_TYPE_VENC:
331 /* TODO: This could also be composite */
332 return DRM_MODE_CONNECTOR_SVIDEO;
333 case OMAP_DISPLAY_TYPE_SDI:
334 return DRM_MODE_CONNECTOR_LVDS;
336 return DRM_MODE_CONNECTOR_Unknown;
340 /* initialize connector */
341 struct drm_connector *omap_connector_init(struct drm_device *dev,
342 struct omap_dss_device *output,
343 struct drm_encoder *encoder)
345 struct drm_connector *connector = NULL;
346 struct omap_connector *omap_connector;
347 struct omap_dss_device *dssdev;
349 DBG("%s", output->name);
351 omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
355 omap_connector->output = omapdss_device_get(output);
357 connector = &omap_connector->base;
358 connector->interlace_allowed = 1;
359 connector->doublescan_allowed = 0;
361 drm_connector_init(dev, connector, &omap_connector_funcs,
362 omap_connector_get_type(output));
363 drm_connector_helper_add(connector, &omap_connector_helper_funcs);
366 * Initialize connector status handling. First try to find a device that
367 * supports hot-plug reporting. If it fails, fall back to a device that
368 * support polling. If that fails too, we don't support hot-plug
371 dssdev = omap_connector_find_device(connector, OMAP_DSS_DEVICE_OP_HPD);
373 omap_connector->hpd = omapdss_device_get(dssdev);
374 connector->polled = DRM_CONNECTOR_POLL_HPD;
376 dssdev = omap_connector_find_device(connector,
377 OMAP_DSS_DEVICE_OP_DETECT);
379 connector->polled = DRM_CONNECTOR_POLL_CONNECT |
380 DRM_CONNECTOR_POLL_DISCONNECT;
387 omap_connector_destroy(connector);