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.0-rc5 408 lines 12 kB view raw
1/* 2 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 3 * 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 18#include <drm/drm_atomic_helper.h> 19#include <drm/drm_crtc.h> 20#include <drm/drm_crtc_helper.h> 21 22#include "omap_drv.h" 23 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 *output; 33 struct omap_dss_device *display; 34 struct omap_dss_device *hpd; 35 bool hdmi_mode; 36}; 37 38static void omap_connector_hpd_notify(struct drm_connector *connector, 39 struct omap_dss_device *src, 40 enum drm_connector_status status) 41{ 42 if (status == connector_status_disconnected) { 43 /* 44 * If the source is an HDMI encoder, notify it of disconnection. 45 * This is required to let the HDMI encoder reset any internal 46 * state related to connection status, such as the CEC address. 47 */ 48 if (src && src->type == OMAP_DISPLAY_TYPE_HDMI && 49 src->ops->hdmi.lost_hotplug) 50 src->ops->hdmi.lost_hotplug(src); 51 } 52} 53 54static void omap_connector_hpd_cb(void *cb_data, 55 enum drm_connector_status status) 56{ 57 struct omap_connector *omap_connector = cb_data; 58 struct drm_connector *connector = &omap_connector->base; 59 struct drm_device *dev = connector->dev; 60 enum drm_connector_status old_status; 61 62 mutex_lock(&dev->mode_config.mutex); 63 old_status = connector->status; 64 connector->status = status; 65 mutex_unlock(&dev->mode_config.mutex); 66 67 if (old_status == status) 68 return; 69 70 omap_connector_hpd_notify(connector, omap_connector->hpd, status); 71 72 drm_kms_helper_hotplug_event(dev); 73} 74 75void omap_connector_enable_hpd(struct drm_connector *connector) 76{ 77 struct omap_connector *omap_connector = to_omap_connector(connector); 78 struct omap_dss_device *hpd = omap_connector->hpd; 79 80 if (hpd) 81 hpd->ops->register_hpd_cb(hpd, omap_connector_hpd_cb, 82 omap_connector); 83} 84 85void omap_connector_disable_hpd(struct drm_connector *connector) 86{ 87 struct omap_connector *omap_connector = to_omap_connector(connector); 88 struct omap_dss_device *hpd = omap_connector->hpd; 89 90 if (hpd) 91 hpd->ops->unregister_hpd_cb(hpd); 92} 93 94bool omap_connector_get_hdmi_mode(struct drm_connector *connector) 95{ 96 struct omap_connector *omap_connector = to_omap_connector(connector); 97 98 return omap_connector->hdmi_mode; 99} 100 101static struct omap_dss_device * 102omap_connector_find_device(struct drm_connector *connector, 103 enum omap_dss_device_ops_flag op) 104{ 105 struct omap_connector *omap_connector = to_omap_connector(connector); 106 struct omap_dss_device *dssdev; 107 108 for (dssdev = omap_connector->display; dssdev; dssdev = dssdev->src) { 109 if (dssdev->ops_flags & op) 110 return dssdev; 111 } 112 113 return NULL; 114} 115 116static enum drm_connector_status omap_connector_detect( 117 struct drm_connector *connector, bool force) 118{ 119 struct omap_connector *omap_connector = to_omap_connector(connector); 120 struct omap_dss_device *dssdev; 121 enum drm_connector_status status; 122 123 dssdev = omap_connector_find_device(connector, 124 OMAP_DSS_DEVICE_OP_DETECT); 125 126 if (dssdev) { 127 status = dssdev->ops->detect(dssdev) 128 ? connector_status_connected 129 : connector_status_disconnected; 130 131 omap_connector_hpd_notify(connector, dssdev->src, status); 132 } else { 133 switch (omap_connector->display->type) { 134 case OMAP_DISPLAY_TYPE_DPI: 135 case OMAP_DISPLAY_TYPE_DBI: 136 case OMAP_DISPLAY_TYPE_SDI: 137 case OMAP_DISPLAY_TYPE_DSI: 138 status = connector_status_connected; 139 break; 140 default: 141 status = connector_status_unknown; 142 break; 143 } 144 } 145 146 VERB("%s: %d (force=%d)", omap_connector->display->name, status, force); 147 148 return status; 149} 150 151static void omap_connector_destroy(struct drm_connector *connector) 152{ 153 struct omap_connector *omap_connector = to_omap_connector(connector); 154 155 DBG("%s", omap_connector->display->name); 156 157 if (omap_connector->hpd) { 158 struct omap_dss_device *hpd = omap_connector->hpd; 159 160 hpd->ops->unregister_hpd_cb(hpd); 161 omapdss_device_put(hpd); 162 omap_connector->hpd = NULL; 163 } 164 165 drm_connector_unregister(connector); 166 drm_connector_cleanup(connector); 167 168 omapdss_device_put(omap_connector->output); 169 omapdss_device_put(omap_connector->display); 170 171 kfree(omap_connector); 172} 173 174#define MAX_EDID 512 175 176static int omap_connector_get_modes_edid(struct drm_connector *connector, 177 struct omap_dss_device *dssdev) 178{ 179 struct omap_connector *omap_connector = to_omap_connector(connector); 180 enum drm_connector_status status; 181 void *edid; 182 int n; 183 184 status = omap_connector_detect(connector, false); 185 if (status != connector_status_connected) 186 goto no_edid; 187 188 edid = kzalloc(MAX_EDID, GFP_KERNEL); 189 if (!edid) 190 goto no_edid; 191 192 if (dssdev->ops->read_edid(dssdev, edid, MAX_EDID) <= 0 || 193 !drm_edid_is_valid(edid)) { 194 kfree(edid); 195 goto no_edid; 196 } 197 198 drm_connector_update_edid_property(connector, edid); 199 n = drm_add_edid_modes(connector, edid); 200 201 omap_connector->hdmi_mode = drm_detect_hdmi_monitor(edid); 202 203 kfree(edid); 204 return n; 205 206no_edid: 207 drm_connector_update_edid_property(connector, NULL); 208 return 0; 209} 210 211static int omap_connector_get_modes(struct drm_connector *connector) 212{ 213 struct omap_connector *omap_connector = to_omap_connector(connector); 214 struct omap_dss_device *dssdev; 215 struct drm_display_mode *mode; 216 struct videomode vm = {0}; 217 218 DBG("%s", omap_connector->display->name); 219 220 /* 221 * If display exposes EDID, then we parse that in the normal way to 222 * build table of supported modes. 223 */ 224 dssdev = omap_connector_find_device(connector, 225 OMAP_DSS_DEVICE_OP_EDID); 226 if (dssdev) 227 return omap_connector_get_modes_edid(connector, dssdev); 228 229 /* 230 * Otherwise we have either a fixed resolution panel or an output that 231 * doesn't support modes discovery (e.g. DVI or VGA with the DDC bus 232 * unconnected, or analog TV). Start by querying the size. 233 */ 234 dssdev = omap_connector->display; 235 if (dssdev->driver && dssdev->driver->get_size) 236 dssdev->driver->get_size(dssdev, 237 &connector->display_info.width_mm, 238 &connector->display_info.height_mm); 239 240 /* 241 * Iterate over the pipeline to find the first device that can provide 242 * timing information. If we can't find any, we just let the KMS core 243 * add the default modes. 244 */ 245 for (dssdev = omap_connector->display; dssdev; dssdev = dssdev->src) { 246 if (dssdev->ops->get_timings) 247 break; 248 } 249 if (!dssdev) 250 return 0; 251 252 /* Add a single mode corresponding to the fixed panel timings. */ 253 mode = drm_mode_create(connector->dev); 254 if (!mode) 255 return 0; 256 257 dssdev->ops->get_timings(dssdev, &vm); 258 259 drm_display_mode_from_videomode(&vm, mode); 260 261 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; 262 drm_mode_set_name(mode); 263 drm_mode_probed_add(connector, mode); 264 265 return 1; 266} 267 268static int omap_connector_mode_valid(struct drm_connector *connector, 269 struct drm_display_mode *mode) 270{ 271 struct omap_connector *omap_connector = to_omap_connector(connector); 272 enum omap_channel channel = omap_connector->output->dispc_channel; 273 struct omap_drm_private *priv = connector->dev->dev_private; 274 struct omap_dss_device *dssdev; 275 struct videomode vm = {0}; 276 struct drm_device *dev = connector->dev; 277 struct drm_display_mode *new_mode; 278 int r, ret = MODE_BAD; 279 280 drm_display_mode_to_videomode(mode, &vm); 281 mode->vrefresh = drm_mode_vrefresh(mode); 282 283 r = priv->dispc_ops->mgr_check_timings(priv->dispc, channel, &vm); 284 if (r) 285 goto done; 286 287 for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) { 288 if (!dssdev->ops->check_timings) 289 continue; 290 291 r = dssdev->ops->check_timings(dssdev, &vm); 292 if (r) 293 goto done; 294 } 295 296 /* check if vrefresh is still valid */ 297 new_mode = drm_mode_duplicate(dev, mode); 298 if (!new_mode) 299 return MODE_BAD; 300 301 new_mode->clock = vm.pixelclock / 1000; 302 new_mode->vrefresh = 0; 303 if (mode->vrefresh == drm_mode_vrefresh(new_mode)) 304 ret = MODE_OK; 305 drm_mode_destroy(dev, new_mode); 306 307done: 308 DBG("connector: mode %s: " 309 "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", 310 (ret == MODE_OK) ? "valid" : "invalid", 311 mode->base.id, mode->name, mode->vrefresh, mode->clock, 312 mode->hdisplay, mode->hsync_start, 313 mode->hsync_end, mode->htotal, 314 mode->vdisplay, mode->vsync_start, 315 mode->vsync_end, mode->vtotal, mode->type, mode->flags); 316 317 return ret; 318} 319 320static const struct drm_connector_funcs omap_connector_funcs = { 321 .reset = drm_atomic_helper_connector_reset, 322 .detect = omap_connector_detect, 323 .fill_modes = drm_helper_probe_single_connector_modes, 324 .destroy = omap_connector_destroy, 325 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 326 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 327}; 328 329static const struct drm_connector_helper_funcs omap_connector_helper_funcs = { 330 .get_modes = omap_connector_get_modes, 331 .mode_valid = omap_connector_mode_valid, 332}; 333 334static int omap_connector_get_type(struct omap_dss_device *display) 335{ 336 switch (display->type) { 337 case OMAP_DISPLAY_TYPE_HDMI: 338 return DRM_MODE_CONNECTOR_HDMIA; 339 case OMAP_DISPLAY_TYPE_DVI: 340 return DRM_MODE_CONNECTOR_DVID; 341 case OMAP_DISPLAY_TYPE_DSI: 342 return DRM_MODE_CONNECTOR_DSI; 343 case OMAP_DISPLAY_TYPE_DPI: 344 case OMAP_DISPLAY_TYPE_DBI: 345 return DRM_MODE_CONNECTOR_DPI; 346 case OMAP_DISPLAY_TYPE_VENC: 347 /* TODO: This could also be composite */ 348 return DRM_MODE_CONNECTOR_SVIDEO; 349 case OMAP_DISPLAY_TYPE_SDI: 350 return DRM_MODE_CONNECTOR_LVDS; 351 default: 352 return DRM_MODE_CONNECTOR_Unknown; 353 } 354} 355 356/* initialize connector */ 357struct drm_connector *omap_connector_init(struct drm_device *dev, 358 struct omap_dss_device *output, 359 struct omap_dss_device *display, 360 struct drm_encoder *encoder) 361{ 362 struct drm_connector *connector = NULL; 363 struct omap_connector *omap_connector; 364 struct omap_dss_device *dssdev; 365 366 DBG("%s", display->name); 367 368 omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL); 369 if (!omap_connector) 370 goto fail; 371 372 omap_connector->output = omapdss_device_get(output); 373 omap_connector->display = omapdss_device_get(display); 374 375 connector = &omap_connector->base; 376 connector->interlace_allowed = 1; 377 connector->doublescan_allowed = 0; 378 379 drm_connector_init(dev, connector, &omap_connector_funcs, 380 omap_connector_get_type(display)); 381 drm_connector_helper_add(connector, &omap_connector_helper_funcs); 382 383 /* 384 * Initialize connector status handling. First try to find a device that 385 * supports hot-plug reporting. If it fails, fall back to a device that 386 * support polling. If that fails too, we don't support hot-plug 387 * detection at all. 388 */ 389 dssdev = omap_connector_find_device(connector, OMAP_DSS_DEVICE_OP_HPD); 390 if (dssdev) { 391 omap_connector->hpd = omapdss_device_get(dssdev); 392 connector->polled = DRM_CONNECTOR_POLL_HPD; 393 } else { 394 dssdev = omap_connector_find_device(connector, 395 OMAP_DSS_DEVICE_OP_DETECT); 396 if (dssdev) 397 connector->polled = DRM_CONNECTOR_POLL_CONNECT | 398 DRM_CONNECTOR_POLL_DISCONNECT; 399 } 400 401 return connector; 402 403fail: 404 if (connector) 405 omap_connector_destroy(connector); 406 407 return NULL; 408}