Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.3-rc3 379 lines 10 kB view raw
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 20struct 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 27static 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 47static 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 68void 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 78void 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 87bool 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 94static struct omap_dss_device * 95omap_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 110static 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 143static 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 167static 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 197no_edid: 198 drm_connector_update_edid_property(connector, NULL); 199 return 0; 200} 201 202static 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 242enum 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 262static 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 278done: 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 286static 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 295static 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 300static 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 */ 330struct 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 374fail: 375 if (connector) 376 omap_connector_destroy(connector); 377 378 return NULL; 379}