Lines Matching +full:no +full:- +full:hpd
1 // SPDX-License-Identifier: GPL-2.0
3 * DisplayPort CEC-Tunneling-over-AUX support
20 * Unfortunately it turns out that we have a chicken-and-egg situation
21 * here. Quite a few active (mini-)DP-to-HDMI or USB-C-to-HDMI adapters
22 * have a converter chip that supports CEC-Tunneling-over-AUX (usually the
24 * useless. Note that MegaChips 2900-based adapters appear to have good
28 * Sadly there is no way for this driver to know this. What happens is
34 * was never supported by any OS. So there was no easy way of testing it,
35 * and no incentive to correctly wire up the CEC pin.
42 * https://hverkuil.home.xs4all.nl/cec-status.txt
48 * As far as I can see there is no mechanism defined in the DisplayPort
57 * These functions take care of supporting the CEC-Tunneling-over-AUX
58 * feature of DisplayPort-to-HDMI adapters.
62 * When the EDID is unset because the HPD went low, then the CEC DPCD registers
63 * typically can no longer be read (true for a DP-to-HDMI adapter since it is
64 * powered by the HPD). However, some displays toggle the HPD off and on for a
69 * actually unregistered. Only if the HPD does not return within that time will
76 * soon as the HPD disappears.
78 * The default is one second to prevent short HPD glitches from unregistering
82 * registers remain available even if the HPD goes low since it is not powered
83 * by the HPD. In that case the CEC adapter will never be unregistered during
91 "CEC unregister delay in seconds, 0: no delay, >= 1000: never unregister");
112 la_mask |= adap->log_addrs.log_addr_mask | (1 << addr); in drm_dp_cec_adap_log_addr()
123 unsigned int retries = min(5, attempts - 1); in drm_dp_cec_adap_transmit()
127 msg->msg, msg->len); in drm_dp_cec_adap_transmit()
132 (msg->len - 1) | (retries << 4) | in drm_dp_cec_adap_transmit()
144 if (!(adap->capabilities & CEC_CAP_MONITOR_ALL)) in drm_dp_cec_adap_monitor_all_enable()
168 (int)sizeof(id->oui), id->oui); in drm_dp_cec_adap_status()
170 (int)strnlen(id->device_id, sizeof(id->device_id)), in drm_dp_cec_adap_status()
171 id->device_id); in drm_dp_cec_adap_status()
172 seq_printf(file, "HW Rev: %d.%d\n", id->hw_rev >> 4, id->hw_rev & 0xf); in drm_dp_cec_adap_status()
178 id->sw_major_rev, id->sw_minor_rev, in drm_dp_cec_adap_status()
179 id->sw_major_rev, id->sw_minor_rev); in drm_dp_cec_adap_status()
192 struct cec_adapter *adap = aux->cec.adap; in drm_dp_cec_received()
215 struct cec_adapter *adap = aux->cec.adap; in drm_dp_cec_handle_irq()
237 * drm_dp_cec_irq() - handle CEC interrupt, if any
240 * Should be called when handling an IRQ_HPD request. If CEC-tunneling-over-AUX
248 /* No transfer function was set, so not a DP connector */ in drm_dp_cec_irq()
249 if (!aux->transfer) in drm_dp_cec_irq()
252 mutex_lock(&aux->cec.lock); in drm_dp_cec_irq()
253 if (!aux->cec.adap) in drm_dp_cec_irq()
264 mutex_unlock(&aux->cec.lock); in drm_dp_cec_irq()
281 * Called if the HPD was low for more than drm_dp_cec_unregister_delay
289 mutex_lock(&aux->cec.lock); in drm_dp_cec_unregister_work()
290 cec_unregister_adapter(aux->cec.adap); in drm_dp_cec_unregister_work()
291 aux->cec.adap = NULL; in drm_dp_cec_unregister_work()
292 mutex_unlock(&aux->cec.lock); in drm_dp_cec_unregister_work()
296 * A new EDID is set. If there is no CEC adapter, then create one. If
303 struct drm_connector *connector = aux->cec.connector; in drm_dp_cec_attach()
310 /* No transfer function was set, so not a DP connector */ in drm_dp_cec_attach()
311 if (!aux->transfer) in drm_dp_cec_attach()
314 cancel_delayed_work_sync(&aux->cec.unregister_work); in drm_dp_cec_attach()
316 mutex_lock(&aux->cec.lock); in drm_dp_cec_attach()
319 cec_unregister_adapter(aux->cec.adap); in drm_dp_cec_attach()
320 aux->cec.adap = NULL; in drm_dp_cec_attach()
329 if (aux->cec.adap) { in drm_dp_cec_attach()
331 if ((aux->cec.adap->capabilities & CEC_CAP_MONITOR_ALL) == in drm_dp_cec_attach()
333 aux->cec.adap->available_log_addrs == num_las) { in drm_dp_cec_attach()
335 cec_s_phys_addr(aux->cec.adap, source_physical_address, false); in drm_dp_cec_attach()
342 cec_unregister_adapter(aux->cec.adap); in drm_dp_cec_attach()
346 aux->cec.adap = cec_allocate_adapter(&drm_dp_cec_adap_ops, in drm_dp_cec_attach()
347 aux, connector->name, cec_caps, in drm_dp_cec_attach()
349 if (IS_ERR(aux->cec.adap)) { in drm_dp_cec_attach()
350 aux->cec.adap = NULL; in drm_dp_cec_attach()
355 cec_s_conn_info(aux->cec.adap, &conn_info); in drm_dp_cec_attach()
357 if (cec_register_adapter(aux->cec.adap, connector->dev->dev)) { in drm_dp_cec_attach()
358 cec_delete_adapter(aux->cec.adap); in drm_dp_cec_attach()
359 aux->cec.adap = NULL; in drm_dp_cec_attach()
366 cec_s_phys_addr(aux->cec.adap, source_physical_address, false); in drm_dp_cec_attach()
369 mutex_unlock(&aux->cec.lock); in drm_dp_cec_attach()
375 * connector->display_info.source_physical_address if possible.
381 if (edid && edid->extensions) in drm_dp_cec_set_edid()
383 EDID_LENGTH * (edid->extensions + 1), NULL); in drm_dp_cec_set_edid()
390 * The EDID disappeared (likely because of the HPD going down).
394 /* No transfer function was set, so not a DP connector */ in drm_dp_cec_unset_edid()
395 if (!aux->transfer) in drm_dp_cec_unset_edid()
398 cancel_delayed_work_sync(&aux->cec.unregister_work); in drm_dp_cec_unset_edid()
400 mutex_lock(&aux->cec.lock); in drm_dp_cec_unset_edid()
401 if (!aux->cec.adap) in drm_dp_cec_unset_edid()
404 cec_phys_addr_invalidate(aux->cec.adap); in drm_dp_cec_unset_edid()
415 * seconds. This to debounce short HPD off-and-on cycles from in drm_dp_cec_unset_edid()
418 schedule_delayed_work(&aux->cec.unregister_work, in drm_dp_cec_unset_edid()
422 mutex_unlock(&aux->cec.lock); in drm_dp_cec_unset_edid()
427 * drm_dp_cec_register_connector() - register a new connector
439 WARN_ON(aux->cec.adap); in drm_dp_cec_register_connector()
440 if (WARN_ON(!aux->transfer)) in drm_dp_cec_register_connector()
442 aux->cec.connector = connector; in drm_dp_cec_register_connector()
443 INIT_DELAYED_WORK(&aux->cec.unregister_work, in drm_dp_cec_register_connector()
449 * drm_dp_cec_unregister_connector() - unregister the CEC adapter, if any
454 if (!aux->cec.adap) in drm_dp_cec_unregister_connector()
456 cancel_delayed_work_sync(&aux->cec.unregister_work); in drm_dp_cec_unregister_connector()
457 cec_unregister_adapter(aux->cec.adap); in drm_dp_cec_unregister_connector()
458 aux->cec.adap = NULL; in drm_dp_cec_unregister_connector()