| .. | .. |
|---|
| 8 | 8 | #include <linux/kernel.h> |
|---|
| 9 | 9 | #include <linux/module.h> |
|---|
| 10 | 10 | #include <linux/slab.h> |
|---|
| 11 | | -#include <drm/drm_dp_helper.h> |
|---|
| 11 | + |
|---|
| 12 | 12 | #include <media/cec.h> |
|---|
| 13 | + |
|---|
| 14 | +#include <drm/drm_connector.h> |
|---|
| 15 | +#include <drm/drm_device.h> |
|---|
| 16 | +#include <drm/drm_dp_helper.h> |
|---|
| 13 | 17 | |
|---|
| 14 | 18 | /* |
|---|
| 15 | 19 | * Unfortunately it turns out that we have a chicken-and-egg situation |
|---|
| 16 | 20 | * here. Quite a few active (mini-)DP-to-HDMI or USB-C-to-HDMI adapters |
|---|
| 17 | 21 | * have a converter chip that supports CEC-Tunneling-over-AUX (usually the |
|---|
| 18 | 22 | * Parade PS176), but they do not wire up the CEC pin, thus making CEC |
|---|
| 19 | | - * useless. |
|---|
| 23 | + * useless. Note that MegaChips 2900-based adapters appear to have good |
|---|
| 24 | + * support for CEC tunneling. Those adapters that I have tested using |
|---|
| 25 | + * this chipset all have the CEC line connected. |
|---|
| 20 | 26 | * |
|---|
| 21 | 27 | * Sadly there is no way for this driver to know this. What happens is |
|---|
| 22 | 28 | * that a /dev/cecX device is created that is isolated and unable to see |
|---|
| .. | .. |
|---|
| 238 | 244 | u8 cec_irq; |
|---|
| 239 | 245 | int ret; |
|---|
| 240 | 246 | |
|---|
| 247 | + /* No transfer function was set, so not a DP connector */ |
|---|
| 248 | + if (!aux->transfer) |
|---|
| 249 | + return; |
|---|
| 250 | + |
|---|
| 241 | 251 | mutex_lock(&aux->cec.lock); |
|---|
| 242 | 252 | if (!aux->cec.adap) |
|---|
| 243 | 253 | goto unlock; |
|---|
| .. | .. |
|---|
| 289 | 299 | */ |
|---|
| 290 | 300 | void drm_dp_cec_set_edid(struct drm_dp_aux *aux, const struct edid *edid) |
|---|
| 291 | 301 | { |
|---|
| 292 | | - u32 cec_caps = CEC_CAP_DEFAULTS | CEC_CAP_NEEDS_HPD; |
|---|
| 302 | + struct drm_connector *connector = aux->cec.connector; |
|---|
| 303 | + u32 cec_caps = CEC_CAP_DEFAULTS | CEC_CAP_NEEDS_HPD | |
|---|
| 304 | + CEC_CAP_CONNECTOR_INFO; |
|---|
| 305 | + struct cec_connector_info conn_info; |
|---|
| 293 | 306 | unsigned int num_las = 1; |
|---|
| 294 | 307 | u8 cap; |
|---|
| 308 | + |
|---|
| 309 | + /* No transfer function was set, so not a DP connector */ |
|---|
| 310 | + if (!aux->transfer) |
|---|
| 311 | + return; |
|---|
| 295 | 312 | |
|---|
| 296 | 313 | #ifndef CONFIG_MEDIA_CEC_RC |
|---|
| 297 | 314 | /* |
|---|
| .. | .. |
|---|
| 334 | 351 | |
|---|
| 335 | 352 | /* Create a new adapter */ |
|---|
| 336 | 353 | aux->cec.adap = cec_allocate_adapter(&drm_dp_cec_adap_ops, |
|---|
| 337 | | - aux, aux->cec.name, cec_caps, |
|---|
| 354 | + aux, connector->name, cec_caps, |
|---|
| 338 | 355 | num_las); |
|---|
| 339 | 356 | if (IS_ERR(aux->cec.adap)) { |
|---|
| 340 | 357 | aux->cec.adap = NULL; |
|---|
| 341 | 358 | goto unlock; |
|---|
| 342 | 359 | } |
|---|
| 343 | | - if (cec_register_adapter(aux->cec.adap, aux->cec.parent)) { |
|---|
| 360 | + |
|---|
| 361 | + cec_fill_conn_info_from_drm(&conn_info, connector); |
|---|
| 362 | + cec_s_conn_info(aux->cec.adap, &conn_info); |
|---|
| 363 | + |
|---|
| 364 | + if (cec_register_adapter(aux->cec.adap, connector->dev->dev)) { |
|---|
| 344 | 365 | cec_delete_adapter(aux->cec.adap); |
|---|
| 345 | 366 | aux->cec.adap = NULL; |
|---|
| 346 | 367 | } else { |
|---|
| .. | .. |
|---|
| 361 | 382 | */ |
|---|
| 362 | 383 | void drm_dp_cec_unset_edid(struct drm_dp_aux *aux) |
|---|
| 363 | 384 | { |
|---|
| 385 | + /* No transfer function was set, so not a DP connector */ |
|---|
| 386 | + if (!aux->transfer) |
|---|
| 387 | + return; |
|---|
| 388 | + |
|---|
| 364 | 389 | cancel_delayed_work_sync(&aux->cec.unregister_work); |
|---|
| 365 | 390 | |
|---|
| 366 | 391 | mutex_lock(&aux->cec.lock); |
|---|
| .. | .. |
|---|
| 392 | 417 | /** |
|---|
| 393 | 418 | * drm_dp_cec_register_connector() - register a new connector |
|---|
| 394 | 419 | * @aux: DisplayPort AUX channel |
|---|
| 395 | | - * @name: name of the CEC device |
|---|
| 396 | | - * @parent: parent device |
|---|
| 420 | + * @connector: drm connector |
|---|
| 397 | 421 | * |
|---|
| 398 | 422 | * A new connector was registered with associated CEC adapter name and |
|---|
| 399 | 423 | * CEC adapter parent device. After registering the name and parent |
|---|
| 400 | 424 | * drm_dp_cec_set_edid() is called to check if the connector supports |
|---|
| 401 | 425 | * CEC and to register a CEC adapter if that is the case. |
|---|
| 402 | 426 | */ |
|---|
| 403 | | -void drm_dp_cec_register_connector(struct drm_dp_aux *aux, const char *name, |
|---|
| 404 | | - struct device *parent) |
|---|
| 427 | +void drm_dp_cec_register_connector(struct drm_dp_aux *aux, |
|---|
| 428 | + struct drm_connector *connector) |
|---|
| 405 | 429 | { |
|---|
| 406 | 430 | WARN_ON(aux->cec.adap); |
|---|
| 407 | | - aux->cec.name = name; |
|---|
| 408 | | - aux->cec.parent = parent; |
|---|
| 431 | + if (WARN_ON(!aux->transfer)) |
|---|
| 432 | + return; |
|---|
| 433 | + aux->cec.connector = connector; |
|---|
| 409 | 434 | INIT_DELAYED_WORK(&aux->cec.unregister_work, |
|---|
| 410 | 435 | drm_dp_cec_unregister_work); |
|---|
| 411 | | - |
|---|
| 412 | | - drm_dp_cec_set_edid(aux, NULL); |
|---|
| 413 | 436 | } |
|---|
| 414 | 437 | EXPORT_SYMBOL(drm_dp_cec_register_connector); |
|---|
| 415 | 438 | |
|---|