blob: cd33995b0b1ae7f6e445e36fef5f884f77fa3411 [file] [log] [blame]
Rob Clarkcd5351f2011-11-12 12:09:40 -06001/*
Andrew F. Davisbb5cdf82017-12-05 14:29:31 -06002 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
Rob Clarkcd5351f2011-11-12 12:09:40 -06003 * Author: Rob Clark <rob@ti.com>
4 *
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.
8 *
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
12 * more details.
13 *
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/>.
16 */
17
Laurent Pinchart69a12262015-03-05 21:38:16 +020018#include <drm/drm_atomic_helper.h>
Laurent Pinchart2d278f52015-03-05 21:31:37 +020019#include <drm/drm_crtc.h>
20#include <drm/drm_crtc_helper.h>
Rob Clarkcd5351f2011-11-12 12:09:40 -060021
Laurent Pinchart2d278f52015-03-05 21:31:37 +020022#include "omap_drv.h"
Rob Clarkcd5351f2011-11-12 12:09:40 -060023
24/*
25 * connector funcs
26 */
27
28#define to_omap_connector(x) container_of(x, struct omap_connector, base)
29
30struct omap_connector {
31 struct drm_connector base;
32 struct omap_dss_device *dssdev;
Tomi Valkeinen4f930c02014-06-18 14:19:48 +030033 bool hdmi_mode;
Rob Clarkcd5351f2011-11-12 12:09:40 -060034};
35
Peter Ujfalusi3c596802017-06-02 15:26:35 +030036static void omap_connector_hpd_cb(void *cb_data,
37 enum drm_connector_status status)
38{
39 struct omap_connector *omap_connector = cb_data;
40 struct drm_connector *connector = &omap_connector->base;
41 struct drm_device *dev = connector->dev;
42 enum drm_connector_status old_status;
43
44 mutex_lock(&dev->mode_config.mutex);
45 old_status = connector->status;
46 connector->status = status;
47 mutex_unlock(&dev->mode_config.mutex);
48
49 if (old_status != status)
50 drm_kms_helper_hotplug_event(dev);
51}
52
Tomi Valkeinen4f930c02014-06-18 14:19:48 +030053bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
54{
55 struct omap_connector *omap_connector = to_omap_connector(connector);
56
57 return omap_connector->hdmi_mode;
58}
59
Laurent Pinchart949ea2e2018-05-30 18:49:48 +030060static struct omap_dss_device *
61omap_connector_find_device(struct drm_connector *connector,
62 enum omap_dss_device_ops_flag op)
63{
64 struct omap_connector *omap_connector = to_omap_connector(connector);
65 struct omap_dss_device *dssdev;
66
67 for (dssdev = omap_connector->dssdev; dssdev; dssdev = dssdev->src) {
68 if (dssdev->ops_flags & op)
69 return dssdev;
70 }
71
72 return NULL;
73}
74
Rob Clarkf5f94542012-12-04 13:59:12 -060075static enum drm_connector_status omap_connector_detect(
Rob Clarkcd5351f2011-11-12 12:09:40 -060076 struct drm_connector *connector, bool force)
77{
78 struct omap_connector *omap_connector = to_omap_connector(connector);
Laurent Pinchartf2ea5572018-05-28 16:49:36 +030079 struct omap_dss_device *dssdev;
80 enum drm_connector_status status;
Rob Clarkcd5351f2011-11-12 12:09:40 -060081
Laurent Pinchart949ea2e2018-05-30 18:49:48 +030082 dssdev = omap_connector_find_device(connector,
83 OMAP_DSS_DEVICE_OP_DETECT);
Rob Clarkcd5351f2011-11-12 12:09:40 -060084
Laurent Pinchartf2ea5572018-05-28 16:49:36 +030085 if (dssdev) {
86 if (dssdev->ops->detect(dssdev))
87 status = connector_status_connected;
88 else
89 status = connector_status_disconnected;
90 } else {
91 switch (omap_connector->dssdev->type) {
92 case OMAP_DISPLAY_TYPE_DPI:
93 case OMAP_DISPLAY_TYPE_DBI:
94 case OMAP_DISPLAY_TYPE_SDI:
95 case OMAP_DISPLAY_TYPE_DSI:
96 status = connector_status_connected;
97 break;
98 default:
99 status = connector_status_unknown;
100 break;
101 }
102 }
Rob Clarkcd5351f2011-11-12 12:09:40 -0600103
Laurent Pinchartf2ea5572018-05-28 16:49:36 +0300104 VERB("%s: %d (force=%d)", omap_connector->dssdev->name, status, force);
105
106 return status;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600107}
108
109static void omap_connector_destroy(struct drm_connector *connector)
110{
111 struct omap_connector *omap_connector = to_omap_connector(connector);
Laurent Pinchart949ea2e2018-05-30 18:49:48 +0300112 struct omap_dss_device *dssdev;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600113
Rob Clarkcd5351f2011-11-12 12:09:40 -0600114 DBG("%s", omap_connector->dssdev->name);
Laurent Pinchart949ea2e2018-05-30 18:49:48 +0300115
116 if (connector->polled == DRM_CONNECTOR_POLL_HPD) {
117 dssdev = omap_connector_find_device(connector,
118 OMAP_DSS_DEVICE_OP_HPD);
Laurent Pinchart83910ad2018-06-01 19:45:01 +0300119 dssdev->ops->unregister_hpd_cb(dssdev);
Peter Ujfalusi3c596802017-06-02 15:26:35 +0300120 }
Laurent Pinchart949ea2e2018-05-30 18:49:48 +0300121
Thomas Wood34ea3d32014-05-29 16:57:41 +0100122 drm_connector_unregister(connector);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600123 drm_connector_cleanup(connector);
124 kfree(omap_connector);
125
Laurent Pinchart949ea2e2018-05-30 18:49:48 +0300126 omapdss_device_put(omap_connector->dssdev);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600127}
128
129#define MAX_EDID 512
130
131static int omap_connector_get_modes(struct drm_connector *connector)
132{
133 struct omap_connector *omap_connector = to_omap_connector(connector);
134 struct omap_dss_device *dssdev = omap_connector->dssdev;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600135 struct drm_device *dev = connector->dev;
136 int n = 0;
137
138 DBG("%s", omap_connector->dssdev->name);
139
140 /* if display exposes EDID, then we parse that in the normal way to
141 * build table of supported modes.. otherwise (ie. fixed resolution
142 * LCD panels) we just return a single mode corresponding to the
143 * currently configured timings:
144 */
Laurent Pinchart83910ad2018-06-01 19:45:01 +0300145 if (dssdev->ops->read_edid) {
Rob Clarkcd5351f2011-11-12 12:09:40 -0600146 void *edid = kzalloc(MAX_EDID, GFP_KERNEL);
147
Tomi Valkeinen47aaaec2018-05-02 12:11:59 +0300148 if (!edid)
149 return 0;
150
Laurent Pinchart83910ad2018-06-01 19:45:01 +0300151 if ((dssdev->ops->read_edid(dssdev, edid, MAX_EDID) > 0) &&
Rob Clarkcd5351f2011-11-12 12:09:40 -0600152 drm_edid_is_valid(edid)) {
Daniel Vetterc555f022018-07-09 10:40:06 +0200153 drm_connector_update_edid_property(
Rob Clarkcd5351f2011-11-12 12:09:40 -0600154 connector, edid);
155 n = drm_add_edid_modes(connector, edid);
Tomi Valkeinen4f930c02014-06-18 14:19:48 +0300156
157 omap_connector->hdmi_mode =
158 drm_detect_hdmi_monitor(edid);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600159 } else {
Daniel Vetterc555f022018-07-09 10:40:06 +0200160 drm_connector_update_edid_property(
Rob Clarkcd5351f2011-11-12 12:09:40 -0600161 connector, NULL);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600162 }
Tomi Valkeinen4f930c02014-06-18 14:19:48 +0300163
Jani Nikula451023d2012-08-15 09:32:39 +0000164 kfree(edid);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600165 } else {
166 struct drm_display_mode *mode = drm_mode_create(dev);
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300167 struct videomode vm = {0};
Rob Clarkcd5351f2011-11-12 12:09:40 -0600168
Tomi Valkeinen47aaaec2018-05-02 12:11:59 +0300169 if (!mode)
170 return 0;
171
Laurent Pinchart83910ad2018-06-01 19:45:01 +0300172 dssdev->ops->get_timings(dssdev, &vm);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600173
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300174 drm_display_mode_from_videomode(&vm, mode);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600175
176 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
177 drm_mode_set_name(mode);
178 drm_mode_probed_add(connector, mode);
179
Laurent Pinchart83910ad2018-06-01 19:45:01 +0300180 if (dssdev->driver && dssdev->driver->get_size) {
181 dssdev->driver->get_size(dssdev,
Sebastian Reichelb070d6e2017-07-24 19:33:04 +0200182 &connector->display_info.width_mm,
183 &connector->display_info.height_mm);
184 }
185
Rob Clarkcd5351f2011-11-12 12:09:40 -0600186 n = 1;
187 }
188
189 return n;
190}
191
192static int omap_connector_mode_valid(struct drm_connector *connector,
193 struct drm_display_mode *mode)
194{
195 struct omap_connector *omap_connector = to_omap_connector(connector);
196 struct omap_dss_device *dssdev = omap_connector->dssdev;
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300197 struct videomode vm = {0};
Rob Clarkcd5351f2011-11-12 12:09:40 -0600198 struct drm_device *dev = connector->dev;
199 struct drm_display_mode *new_mode;
Archit Tanejabddabbe2013-03-26 19:15:20 +0530200 int r, ret = MODE_BAD;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600201
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300202 drm_display_mode_to_videomode(mode, &vm);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600203 mode->vrefresh = drm_mode_vrefresh(mode);
204
Archit Tanejabddabbe2013-03-26 19:15:20 +0530205 /*
206 * if the panel driver doesn't have a check_timings, it's most likely
207 * a fixed resolution panel, check if the timings match with the
208 * panel's timings
209 */
Laurent Pinchart83910ad2018-06-01 19:45:01 +0300210 if (dssdev->ops->check_timings) {
211 r = dssdev->ops->check_timings(dssdev, &vm);
Archit Tanejabddabbe2013-03-26 19:15:20 +0530212 } else {
Peter Ujfalusi4520ff22016-09-22 14:07:03 +0300213 struct videomode t = {0};
Archit Tanejabddabbe2013-03-26 19:15:20 +0530214
Laurent Pinchart83910ad2018-06-01 19:45:01 +0300215 dssdev->ops->get_timings(dssdev, &t);
Archit Tanejabddabbe2013-03-26 19:15:20 +0530216
Tomi Valkeinen50fa9f02016-11-23 13:24:00 +0200217 /*
218 * Ignore the flags, as we don't get them from
219 * drm_display_mode_to_videomode.
220 */
221 t.flags = 0;
222
Laurent Pinchartd501b122016-12-12 11:57:24 +0200223 if (memcmp(&vm, &t, sizeof(vm)))
Archit Tanejabddabbe2013-03-26 19:15:20 +0530224 r = -EINVAL;
225 else
226 r = 0;
227 }
228
229 if (!r) {
Rob Clarkcd5351f2011-11-12 12:09:40 -0600230 /* check if vrefresh is still valid */
231 new_mode = drm_mode_duplicate(dev, mode);
Tomi Valkeinen47aaaec2018-05-02 12:11:59 +0300232
233 if (!new_mode)
234 return MODE_BAD;
235
Peter Ujfalusida11bbbb2016-09-22 14:07:04 +0300236 new_mode->clock = vm.pixelclock / 1000;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600237 new_mode->vrefresh = 0;
238 if (mode->vrefresh == drm_mode_vrefresh(new_mode))
239 ret = MODE_OK;
240 drm_mode_destroy(dev, new_mode);
241 }
242
243 DBG("connector: mode %s: "
244 "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
245 (ret == MODE_OK) ? "valid" : "invalid",
246 mode->base.id, mode->name, mode->vrefresh, mode->clock,
247 mode->hdisplay, mode->hsync_start,
248 mode->hsync_end, mode->htotal,
249 mode->vdisplay, mode->vsync_start,
250 mode->vsync_end, mode->vtotal, mode->type, mode->flags);
251
252 return ret;
253}
254
Rob Clarkcd5351f2011-11-12 12:09:40 -0600255static const struct drm_connector_funcs omap_connector_funcs = {
Laurent Pinchart69a12262015-03-05 21:38:16 +0200256 .reset = drm_atomic_helper_connector_reset,
Rob Clarkcd5351f2011-11-12 12:09:40 -0600257 .detect = omap_connector_detect,
258 .fill_modes = drm_helper_probe_single_connector_modes,
259 .destroy = omap_connector_destroy,
Laurent Pinchart69a12262015-03-05 21:38:16 +0200260 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
261 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
Rob Clarkcd5351f2011-11-12 12:09:40 -0600262};
263
264static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
265 .get_modes = omap_connector_get_modes,
266 .mode_valid = omap_connector_mode_valid,
Rob Clarkcd5351f2011-11-12 12:09:40 -0600267};
268
Rob Clarkcd5351f2011-11-12 12:09:40 -0600269/* initialize connector */
270struct drm_connector *omap_connector_init(struct drm_device *dev,
Rob Clarkf5f94542012-12-04 13:59:12 -0600271 int connector_type, struct omap_dss_device *dssdev,
272 struct drm_encoder *encoder)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600273{
274 struct drm_connector *connector = NULL;
275 struct omap_connector *omap_connector;
276
277 DBG("%s", dssdev->name);
278
Laurent Pinchartd501b122016-12-12 11:57:24 +0200279 omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
Joe Perches78110bb2013-02-11 09:41:29 -0800280 if (!omap_connector)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600281 goto fail;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600282
Laurent Pinchart949ea2e2018-05-30 18:49:48 +0300283 omap_connector->dssdev = omapdss_device_get(dssdev);
Rob Clarkf5f94542012-12-04 13:59:12 -0600284
Rob Clarkcd5351f2011-11-12 12:09:40 -0600285 connector = &omap_connector->base;
Laurent Pinchart949ea2e2018-05-30 18:49:48 +0300286 connector->interlace_allowed = 1;
287 connector->doublescan_allowed = 0;
Rob Clarkcd5351f2011-11-12 12:09:40 -0600288
289 drm_connector_init(dev, connector, &omap_connector_funcs,
290 connector_type);
291 drm_connector_helper_add(connector, &omap_connector_helper_funcs);
292
Laurent Pinchart949ea2e2018-05-30 18:49:48 +0300293 /*
294 * Initialize connector status handling. First try to find a device that
295 * supports hot-plug reporting. If it fails, fall back to a device that
296 * support polling. If that fails too, we don't support hot-plug
297 * detection at all.
298 */
299 dssdev = omap_connector_find_device(connector, OMAP_DSS_DEVICE_OP_HPD);
300 if (dssdev) {
Laurent Pincharta21a8f32018-05-30 18:51:59 +0300301 dssdev->ops->register_hpd_cb(dssdev, omap_connector_hpd_cb,
302 omap_connector);
303 connector->polled = DRM_CONNECTOR_POLL_HPD;
304 } else {
Laurent Pinchart949ea2e2018-05-30 18:49:48 +0300305 dssdev = omap_connector_find_device(connector,
306 OMAP_DSS_DEVICE_OP_DETECT);
307 if (dssdev)
308 connector->polled = DRM_CONNECTOR_POLL_CONNECT |
309 DRM_CONNECTOR_POLL_DISCONNECT;
310 }
Rob Clarkcd5351f2011-11-12 12:09:40 -0600311
Rob Clarkcd5351f2011-11-12 12:09:40 -0600312 return connector;
313
314fail:
YAMANE Toshiakifdbf20b2012-11-14 19:28:54 +0900315 if (connector)
Rob Clarkcd5351f2011-11-12 12:09:40 -0600316 omap_connector_destroy(connector);
Rob Clarkcd5351f2011-11-12 12:09:40 -0600317
318 return NULL;
319}