[media] cec: add CEC_CAP_NEEDS_HPD
authorHans Verkuil <hans.verkuil@cisco.com>
Wed, 7 Jun 2017 14:46:12 +0000 (11:46 -0300)
committerMauro Carvalho Chehab <mchehab@s-opensource.com>
Tue, 20 Jun 2017 09:54:34 +0000 (06:54 -0300)
Add a new capability CEC_CAP_NEEDS_HPD. If this capability is set
then the hardware can only use CEC if the HDMI Hotplug Detect pin
is high. Such hardware cannot handle the corner case in the CEC specification
where it is possible to transmit messages even if no hotplug signal is
present (needed for some displays that turn off the HPD when in standby,
but still have CEC enabled).

Typically hardware that needs this capability have the HPD wired to the CEC
block, often to a 'power' or 'active' pin.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
drivers/media/cec/cec-adap.c
drivers/media/cec/cec-api.c
drivers/media/cec/cec-core.c
include/media/cec.h
include/uapi/linux/cec.h

index bd76c15ade4f0c7062a28adceed42a4876e8fc3d..bf45977b2823b067076eb1e01f741af8dd424bdb 100644 (file)
@@ -368,6 +368,8 @@ int cec_thread_func(void *_adap)
                         * transmit should be canceled.
                         */
                        err = wait_event_interruptible_timeout(adap->kthread_waitq,
+                               (adap->needs_hpd &&
+                                (!adap->is_configured && !adap->is_configuring)) ||
                                kthread_should_stop() ||
                                (!adap->transmitting &&
                                 !list_empty(&adap->transmit_queue)),
@@ -383,7 +385,9 @@ int cec_thread_func(void *_adap)
 
                mutex_lock(&adap->lock);
 
-               if (kthread_should_stop()) {
+               if ((adap->needs_hpd &&
+                    (!adap->is_configured && !adap->is_configuring)) ||
+                   kthread_should_stop()) {
                        cec_flush(adap);
                        goto unlock;
                }
@@ -682,7 +686,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg,
                return -EINVAL;
        }
        if (!adap->is_configured && !adap->is_configuring) {
-               if (msg->msg[0] != 0xf0) {
+               if (adap->needs_hpd || msg->msg[0] != 0xf0) {
                        dprintk(1, "%s: adapter is unconfigured\n", __func__);
                        return -ENONET;
                }
@@ -1158,7 +1162,9 @@ static int cec_config_log_addr(struct cec_adapter *adap,
  */
 static void cec_adap_unconfigure(struct cec_adapter *adap)
 {
-       WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID));
+       if (!adap->needs_hpd ||
+           adap->phys_addr != CEC_PHYS_ADDR_INVALID)
+               WARN_ON(adap->ops->adap_log_addr(adap, CEC_LOG_ADDR_INVALID));
        adap->log_addrs.log_addr_mask = 0;
        adap->is_configuring = false;
        adap->is_configured = false;
@@ -1387,6 +1393,8 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
        if (phys_addr == adap->phys_addr || adap->devnode.unregistered)
                return;
 
+       dprintk(1, "new physical address %x.%x.%x.%x\n",
+               cec_phys_addr_exp(phys_addr));
        if (phys_addr == CEC_PHYS_ADDR_INVALID ||
            adap->phys_addr != CEC_PHYS_ADDR_INVALID) {
                adap->phys_addr = CEC_PHYS_ADDR_INVALID;
@@ -1396,7 +1404,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
                if (adap->monitor_all_cnt)
                        WARN_ON(call_op(adap, adap_monitor_all_enable, false));
                mutex_lock(&adap->devnode.lock);
-               if (list_empty(&adap->devnode.fhs))
+               if (adap->needs_hpd || list_empty(&adap->devnode.fhs))
                        WARN_ON(adap->ops->adap_enable(adap, false));
                mutex_unlock(&adap->devnode.lock);
                if (phys_addr == CEC_PHYS_ADDR_INVALID)
@@ -1404,7 +1412,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
        }
 
        mutex_lock(&adap->devnode.lock);
-       if (list_empty(&adap->devnode.fhs) &&
+       if ((adap->needs_hpd || list_empty(&adap->devnode.fhs)) &&
            adap->ops->adap_enable(adap, true)) {
                mutex_unlock(&adap->devnode.lock);
                return;
@@ -1412,7 +1420,7 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
 
        if (adap->monitor_all_cnt &&
            call_op(adap, adap_monitor_all_enable, true)) {
-               if (list_empty(&adap->devnode.fhs))
+               if (adap->needs_hpd || list_empty(&adap->devnode.fhs))
                        WARN_ON(adap->ops->adap_enable(adap, false));
                mutex_unlock(&adap->devnode.lock);
                return;
index 0860fb458757df3871ccab290867fcf7c4d5c11e..1359c3977101aaa2eff59050575e46edeeb4214d 100644 (file)
@@ -202,7 +202,8 @@ static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh,
                err = -EPERM;
        else if (adap->is_configuring)
                err = -ENONET;
-       else if (!adap->is_configured && msg.msg[0] != 0xf0)
+       else if (!adap->is_configured &&
+                (adap->needs_hpd || msg.msg[0] != 0xf0))
                err = -ENONET;
        else if (cec_is_busy(adap, fh))
                err = -EBUSY;
@@ -521,6 +522,7 @@ static int cec_open(struct inode *inode, struct file *filp)
 
        mutex_lock(&devnode->lock);
        if (list_empty(&devnode->fhs) &&
+           !adap->needs_hpd &&
            adap->phys_addr == CEC_PHYS_ADDR_INVALID) {
                err = adap->ops->adap_enable(adap, true);
                if (err) {
@@ -565,6 +567,7 @@ static int cec_release(struct inode *inode, struct file *filp)
        mutex_lock(&devnode->lock);
        list_del(&fh->list);
        if (list_empty(&devnode->fhs) &&
+           !adap->needs_hpd &&
            adap->phys_addr == CEC_PHYS_ADDR_INVALID) {
                WARN_ON(adap->ops->adap_enable(adap, false));
        }
index 2f87748ba4fceea377284be6a1c35b6478ca4788..b516d599d6c44c4ddfd1f7b69c653edb6cc70c2c 100644 (file)
@@ -230,6 +230,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
        adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
        adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
        adap->capabilities = caps;
+       adap->needs_hpd = caps & CEC_CAP_NEEDS_HPD;
        adap->available_log_addrs = available_las;
        adap->sequence = 0;
        adap->ops = ops;
index a2e184d1df008f150f9586ac5ce2b8758ddf2bfd..7e32e80b243ed44afcbfe6335aa5aebd6b3597bf 100644 (file)
@@ -164,6 +164,7 @@ struct cec_adapter {
        u8 available_log_addrs;
 
        u16 phys_addr;
+       bool needs_hpd;
        bool is_configuring;
        bool is_configured;
        u32 monitor_all_cnt;
index a0dfe27bc6c7c0d07bf6abca546105b7d94773fb..44579a24f95d637836d528d803a05c2c27323234 100644 (file)
@@ -336,6 +336,8 @@ static inline int cec_is_unconfigured(__u16 log_addr_mask)
 #define CEC_CAP_RC             (1 << 4)
 /* Hardware can monitor all messages, not just directed and broadcast. */
 #define CEC_CAP_MONITOR_ALL    (1 << 5)
+/* Hardware can use CEC only if the HDMI HPD pin is high. */
+#define CEC_CAP_NEEDS_HPD      (1 << 6)
 
 /**
  * struct cec_caps - CEC capabilities structure.