Linux-libre 5.4.47-gnu
[librecmc/linux-libre.git] / drivers / gpu / drm / omapdrm / omap_connector.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
4  * Author: Rob Clark <rob@ti.com>
5  */
6
7 #include <drm/drm_atomic_helper.h>
8 #include <drm/drm_crtc.h>
9 #include <drm/drm_panel.h>
10 #include <drm/drm_probe_helper.h>
11
12 #include "omap_drv.h"
13
14 /*
15  * connector funcs
16  */
17
18 #define to_omap_connector(x) container_of(x, struct omap_connector, base)
19
20 struct omap_connector {
21         struct drm_connector base;
22         struct omap_dss_device *output;
23         struct omap_dss_device *hpd;
24         bool hdmi_mode;
25 };
26
27 static void omap_connector_hpd_notify(struct drm_connector *connector,
28                                       enum drm_connector_status status)
29 {
30         struct omap_connector *omap_connector = to_omap_connector(connector);
31         struct omap_dss_device *dssdev;
32
33         if (status != connector_status_disconnected)
34                 return;
35
36         /*
37          * Notify all devics in the pipeline of disconnection. This is required
38          * to let the HDMI encoders reset their internal state related to
39          * connection status, such as the CEC address.
40          */
41         for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) {
42                 if (dssdev->ops && dssdev->ops->hdmi.lost_hotplug)
43                         dssdev->ops->hdmi.lost_hotplug(dssdev);
44         }
45 }
46
47 static void omap_connector_hpd_cb(void *cb_data,
48                                   enum drm_connector_status status)
49 {
50         struct omap_connector *omap_connector = cb_data;
51         struct drm_connector *connector = &omap_connector->base;
52         struct drm_device *dev = connector->dev;
53         enum drm_connector_status old_status;
54
55         mutex_lock(&dev->mode_config.mutex);
56         old_status = connector->status;
57         connector->status = status;
58         mutex_unlock(&dev->mode_config.mutex);
59
60         if (old_status == status)
61                 return;
62
63         omap_connector_hpd_notify(connector, status);
64
65         drm_kms_helper_hotplug_event(dev);
66 }
67
68 void omap_connector_enable_hpd(struct drm_connector *connector)
69 {
70         struct omap_connector *omap_connector = to_omap_connector(connector);
71         struct omap_dss_device *hpd = omap_connector->hpd;
72
73         if (hpd)
74                 hpd->ops->register_hpd_cb(hpd, omap_connector_hpd_cb,
75                                           omap_connector);
76 }
77
78 void omap_connector_disable_hpd(struct drm_connector *connector)
79 {
80         struct omap_connector *omap_connector = to_omap_connector(connector);
81         struct omap_dss_device *hpd = omap_connector->hpd;
82
83         if (hpd)
84                 hpd->ops->unregister_hpd_cb(hpd);
85 }
86
87 bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
88 {
89         struct omap_connector *omap_connector = to_omap_connector(connector);
90
91         return omap_connector->hdmi_mode;
92 }
93
94 static struct omap_dss_device *
95 omap_connector_find_device(struct drm_connector *connector,
96                            enum omap_dss_device_ops_flag op)
97 {
98         struct omap_connector *omap_connector = to_omap_connector(connector);
99         struct omap_dss_device *dssdev = NULL;
100         struct omap_dss_device *d;
101
102         for (d = omap_connector->output; d; d = d->next) {
103                 if (d->ops_flags & op)
104                         dssdev = d;
105         }
106
107         return dssdev;
108 }
109
110 static enum drm_connector_status omap_connector_detect(
111                 struct drm_connector *connector, bool force)
112 {
113         struct omap_dss_device *dssdev;
114         enum drm_connector_status status;
115
116         dssdev = omap_connector_find_device(connector,
117                                             OMAP_DSS_DEVICE_OP_DETECT);
118
119         if (dssdev) {
120                 status = dssdev->ops->detect(dssdev)
121                        ? connector_status_connected
122                        : connector_status_disconnected;
123
124                 omap_connector_hpd_notify(connector, status);
125         } else {
126                 switch (connector->connector_type) {
127                 case DRM_MODE_CONNECTOR_DPI:
128                 case DRM_MODE_CONNECTOR_LVDS:
129                 case DRM_MODE_CONNECTOR_DSI:
130                         status = connector_status_connected;
131                         break;
132                 default:
133                         status = connector_status_unknown;
134                         break;
135                 }
136         }
137
138         VERB("%s: %d (force=%d)", connector->name, status, force);
139
140         return status;
141 }
142
143 static void omap_connector_destroy(struct drm_connector *connector)
144 {
145         struct omap_connector *omap_connector = to_omap_connector(connector);
146
147         DBG("%s", connector->name);
148
149         if (omap_connector->hpd) {
150                 struct omap_dss_device *hpd = omap_connector->hpd;
151
152                 hpd->ops->unregister_hpd_cb(hpd);
153                 omapdss_device_put(hpd);
154                 omap_connector->hpd = NULL;
155         }
156
157         drm_connector_unregister(connector);
158         drm_connector_cleanup(connector);
159
160         omapdss_device_put(omap_connector->output);
161
162         kfree(omap_connector);
163 }
164
165 #define MAX_EDID  512
166
167 static int omap_connector_get_modes_edid(struct drm_connector *connector,
168                                          struct omap_dss_device *dssdev)
169 {
170         struct omap_connector *omap_connector = to_omap_connector(connector);
171         enum drm_connector_status status;
172         void *edid;
173         int n;
174
175         status = omap_connector_detect(connector, false);
176         if (status != connector_status_connected)
177                 goto no_edid;
178
179         edid = kzalloc(MAX_EDID, GFP_KERNEL);
180         if (!edid)
181                 goto no_edid;
182
183         if (dssdev->ops->read_edid(dssdev, edid, MAX_EDID) <= 0 ||
184             !drm_edid_is_valid(edid)) {
185                 kfree(edid);
186                 goto no_edid;
187         }
188
189         drm_connector_update_edid_property(connector, edid);
190         n = drm_add_edid_modes(connector, edid);
191
192         omap_connector->hdmi_mode = drm_detect_hdmi_monitor(edid);
193
194         kfree(edid);
195         return n;
196
197 no_edid:
198         drm_connector_update_edid_property(connector, NULL);
199         return 0;
200 }
201
202 static int omap_connector_get_modes(struct drm_connector *connector)
203 {
204         struct omap_connector *omap_connector = to_omap_connector(connector);
205         struct omap_dss_device *dssdev;
206
207         DBG("%s", connector->name);
208
209         /*
210          * If display exposes EDID, then we parse that in the normal way to
211          * build table of supported modes.
212          */
213         dssdev = omap_connector_find_device(connector,
214                                             OMAP_DSS_DEVICE_OP_EDID);
215         if (dssdev)
216                 return omap_connector_get_modes_edid(connector, dssdev);
217
218         /*
219          * Otherwise if the display pipeline reports modes (e.g. with a fixed
220          * resolution panel or an analog TV output), query it.
221          */
222         dssdev = omap_connector_find_device(connector,
223                                             OMAP_DSS_DEVICE_OP_MODES);
224         if (dssdev)
225                 return dssdev->ops->get_modes(dssdev, connector);
226
227         /*
228          * Otherwise if the display pipeline uses a drm_panel, we delegate the
229          * operation to the panel API.
230          */
231         if (omap_connector->output->panel)
232                 return drm_panel_get_modes(omap_connector->output->panel);
233
234         /*
235          * We can't retrieve modes, which can happen for instance for a DVI or
236          * VGA output with the DDC bus unconnected. The KMS core will add the
237          * default modes.
238          */
239         return 0;
240 }
241
242 enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev,
243                                         const struct drm_display_mode *mode,
244                                         struct drm_display_mode *adjusted_mode)
245 {
246         int ret;
247
248         drm_mode_copy(adjusted_mode, mode);
249
250         for (; dssdev; dssdev = dssdev->next) {
251                 if (!dssdev->ops->check_timings)
252                         continue;
253
254                 ret = dssdev->ops->check_timings(dssdev, adjusted_mode);
255                 if (ret)
256                         return MODE_BAD;
257         }
258
259         return MODE_OK;
260 }
261
262 static enum drm_mode_status omap_connector_mode_valid(struct drm_connector *connector,
263                                  struct drm_display_mode *mode)
264 {
265         struct omap_connector *omap_connector = to_omap_connector(connector);
266         struct drm_display_mode new_mode = { { 0 } };
267         enum drm_mode_status status;
268
269         status = omap_connector_mode_fixup(omap_connector->output, mode,
270                                            &new_mode);
271         if (status != MODE_OK)
272                 goto done;
273
274         /* Check if vrefresh is still valid. */
275         if (drm_mode_vrefresh(mode) != drm_mode_vrefresh(&new_mode))
276                 status = MODE_NOCLOCK;
277
278 done:
279         DBG("connector: mode %s: " DRM_MODE_FMT,
280                         (status == MODE_OK) ? "valid" : "invalid",
281                         DRM_MODE_ARG(mode));
282
283         return status;
284 }
285
286 static const struct drm_connector_funcs omap_connector_funcs = {
287         .reset = drm_atomic_helper_connector_reset,
288         .detect = omap_connector_detect,
289         .fill_modes = drm_helper_probe_single_connector_modes,
290         .destroy = omap_connector_destroy,
291         .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
292         .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
293 };
294
295 static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
296         .get_modes = omap_connector_get_modes,
297         .mode_valid = omap_connector_mode_valid,
298 };
299
300 static int omap_connector_get_type(struct omap_dss_device *output)
301 {
302         struct omap_dss_device *display;
303         enum omap_display_type type;
304
305         display = omapdss_display_get(output);
306         type = display->type;
307         omapdss_device_put(display);
308
309         switch (type) {
310         case OMAP_DISPLAY_TYPE_HDMI:
311                 return DRM_MODE_CONNECTOR_HDMIA;
312         case OMAP_DISPLAY_TYPE_DVI:
313                 return DRM_MODE_CONNECTOR_DVID;
314         case OMAP_DISPLAY_TYPE_DSI:
315                 return DRM_MODE_CONNECTOR_DSI;
316         case OMAP_DISPLAY_TYPE_DPI:
317         case OMAP_DISPLAY_TYPE_DBI:
318                 return DRM_MODE_CONNECTOR_DPI;
319         case OMAP_DISPLAY_TYPE_VENC:
320                 /* TODO: This could also be composite */
321                 return DRM_MODE_CONNECTOR_SVIDEO;
322         case OMAP_DISPLAY_TYPE_SDI:
323                 return DRM_MODE_CONNECTOR_LVDS;
324         default:
325                 return DRM_MODE_CONNECTOR_Unknown;
326         }
327 }
328
329 /* initialize connector */
330 struct drm_connector *omap_connector_init(struct drm_device *dev,
331                                           struct omap_dss_device *output,
332                                           struct drm_encoder *encoder)
333 {
334         struct drm_connector *connector = NULL;
335         struct omap_connector *omap_connector;
336         struct omap_dss_device *dssdev;
337
338         DBG("%s", output->name);
339
340         omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
341         if (!omap_connector)
342                 goto fail;
343
344         omap_connector->output = omapdss_device_get(output);
345
346         connector = &omap_connector->base;
347         connector->interlace_allowed = 1;
348         connector->doublescan_allowed = 0;
349
350         drm_connector_init(dev, connector, &omap_connector_funcs,
351                            omap_connector_get_type(output));
352         drm_connector_helper_add(connector, &omap_connector_helper_funcs);
353
354         /*
355          * Initialize connector status handling. First try to find a device that
356          * supports hot-plug reporting. If it fails, fall back to a device that
357          * support polling. If that fails too, we don't support hot-plug
358          * detection at all.
359          */
360         dssdev = omap_connector_find_device(connector, OMAP_DSS_DEVICE_OP_HPD);
361         if (dssdev) {
362                 omap_connector->hpd = omapdss_device_get(dssdev);
363                 connector->polled = DRM_CONNECTOR_POLL_HPD;
364         } else {
365                 dssdev = omap_connector_find_device(connector,
366                                                     OMAP_DSS_DEVICE_OP_DETECT);
367                 if (dssdev)
368                         connector->polled = DRM_CONNECTOR_POLL_CONNECT |
369                                             DRM_CONNECTOR_POLL_DISCONNECT;
370         }
371
372         return connector;
373
374 fail:
375         if (connector)
376                 omap_connector_destroy(connector);
377
378         return NULL;
379 }