summaryrefslogtreecommitdiffstats
path: root/drivers/media/cec
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/cec')
-rw-r--r--drivers/media/cec/Kconfig15
-rw-r--r--drivers/media/cec/Makefile2
-rw-r--r--drivers/media/cec/cec-adap.c90
-rw-r--r--drivers/media/cec/cec-api.c13
-rw-r--r--drivers/media/cec/cec-core.c9
5 files changed, 83 insertions, 46 deletions
diff --git a/drivers/media/cec/Kconfig b/drivers/media/cec/Kconfig
index f944d93e3167..43428cec3a01 100644
--- a/drivers/media/cec/Kconfig
+++ b/drivers/media/cec/Kconfig
@@ -1,19 +1,6 @@
-config CEC_CORE
- tristate
- depends on MEDIA_CEC_SUPPORT
- default y
-
-config MEDIA_CEC_NOTIFIER
- bool
-
config MEDIA_CEC_RC
bool "HDMI CEC RC integration"
depends on CEC_CORE && RC_CORE
+ depends on CEC_CORE=m || RC_CORE=y
---help---
Pass on CEC remote control messages to the RC framework.
-
-config MEDIA_CEC_DEBUG
- bool "HDMI CEC debugfs interface"
- depends on CEC_CORE && DEBUG_FS
- ---help---
- Turns on the DebugFS interface for CEC devices.
diff --git a/drivers/media/cec/Makefile b/drivers/media/cec/Makefile
index 402a6c62a3e8..eaf408e64669 100644
--- a/drivers/media/cec/Makefile
+++ b/drivers/media/cec/Makefile
@@ -1,6 +1,6 @@
cec-objs := cec-core.o cec-adap.o cec-api.o cec-edid.o
-ifeq ($(CONFIG_MEDIA_CEC_NOTIFIER),y)
+ifeq ($(CONFIG_CEC_NOTIFIER),y)
cec-objs += cec-notifier.o
endif
diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c
index f5fe01c9da8a..bf45977b2823 100644
--- a/drivers/media/cec/cec-adap.c
+++ b/drivers/media/cec/cec-adap.c
@@ -28,6 +28,8 @@
#include <linux/string.h>
#include <linux/types.h>
+#include <drm/drm_edid.h>
+
#include "cec-priv.h"
static void cec_fill_msg_report_features(struct cec_adapter *adap,
@@ -366,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)),
@@ -381,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;
}
@@ -392,7 +398,7 @@ int cec_thread_func(void *_adap)
* happen and is an indication of a faulty CEC adapter
* driver, or the CEC bus is in some weird state.
*/
- dprintk(0, "message %*ph timed out!\n",
+ dprintk(0, "%s: message %*ph timed out!\n", __func__,
adap->transmitting->msg.len,
adap->transmitting->msg.msg);
/* Just give up on this. */
@@ -468,7 +474,7 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
struct cec_msg *msg;
u64 ts = ktime_get_ns();
- dprintk(2, "cec_transmit_done %02x\n", status);
+ dprintk(2, "%s: status %02x\n", __func__, status);
mutex_lock(&adap->lock);
data = adap->transmitting;
if (!data) {
@@ -477,7 +483,8 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
* unplugged while the transmit is ongoing. Ignore this
* transmit in that case.
*/
- dprintk(1, "cec_transmit_done without an ongoing transmit!\n");
+ dprintk(1, "%s was called without an ongoing transmit!\n",
+ __func__);
goto unlock;
}
@@ -504,6 +511,12 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt,
!(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) {
/* Retry this message */
data->attempts--;
+ if (msg->timeout)
+ dprintk(2, "retransmit: %*ph (attempts: %d, wait for 0x%02x)\n",
+ msg->len, msg->msg, data->attempts, msg->reply);
+ else
+ dprintk(2, "retransmit: %*ph (attempts: %d)\n",
+ msg->len, msg->msg, data->attempts);
/* Add the message in front of the transmit queue */
list_add(&data->list, &adap->transmit_queue);
adap->transmit_queue_sz++;
@@ -544,6 +557,32 @@ unlock:
}
EXPORT_SYMBOL_GPL(cec_transmit_done);
+void cec_transmit_attempt_done(struct cec_adapter *adap, u8 status)
+{
+ switch (status) {
+ case CEC_TX_STATUS_OK:
+ cec_transmit_done(adap, status, 0, 0, 0, 0);
+ return;
+ case CEC_TX_STATUS_ARB_LOST:
+ cec_transmit_done(adap, status, 1, 0, 0, 0);
+ return;
+ case CEC_TX_STATUS_NACK:
+ cec_transmit_done(adap, status, 0, 1, 0, 0);
+ return;
+ case CEC_TX_STATUS_LOW_DRIVE:
+ cec_transmit_done(adap, status, 0, 0, 1, 0);
+ return;
+ case CEC_TX_STATUS_ERROR:
+ cec_transmit_done(adap, status, 0, 0, 0, 1);
+ return;
+ default:
+ /* Should never happen */
+ WARN(1, "cec-%s: invalid status 0x%02x\n", adap->name, status);
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(cec_transmit_attempt_done);
+
/*
* Called when waiting for a reply times out.
*/
@@ -647,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;
}
@@ -911,7 +950,7 @@ void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg)
memset(msg->msg + msg->len, 0, sizeof(msg->msg) - msg->len);
mutex_lock(&adap->lock);
- dprintk(2, "cec_received_msg: %*ph\n", msg->len, msg->msg);
+ dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg);
/* Check if this message was for us (directed or broadcast). */
if (!cec_msg_is_broadcast(msg))
@@ -1112,9 +1151,6 @@ static int cec_config_log_addr(struct cec_adapter *adap,
las->log_addr[idx] = log_addr;
las->log_addr_mask |= 1 << log_addr;
adap->phys_addrs[log_addr] = adap->phys_addr;
-
- dprintk(2, "claimed addr %d (%d)\n", log_addr,
- las->primary_device_type[idx]);
return 1;
}
@@ -1126,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;
@@ -1300,7 +1338,7 @@ configured:
/* Report Physical Address */
cec_msg_report_physical_addr(&msg, adap->phys_addr,
las->primary_device_type[i]);
- dprintk(2, "config: la %d pa %x.%x.%x.%x\n",
+ dprintk(1, "config: la %d pa %x.%x.%x.%x\n",
las->log_addr[i],
cec_phys_addr_exp(adap->phys_addr));
cec_transmit_msg_fh(adap, &msg, NULL, false);
@@ -1355,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;
@@ -1364,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)
@@ -1372,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;
@@ -1380,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;
@@ -1404,6 +1444,18 @@ void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
}
EXPORT_SYMBOL_GPL(cec_s_phys_addr);
+void cec_s_phys_addr_from_edid(struct cec_adapter *adap,
+ const struct edid *edid)
+{
+ u16 pa = CEC_PHYS_ADDR_INVALID;
+
+ if (edid && edid->extensions)
+ pa = cec_get_edid_phys_addr((const u8 *)edid,
+ EDID_LENGTH * (edid->extensions + 1), NULL);
+ cec_s_phys_addr(adap, pa, false);
+}
+EXPORT_SYMBOL_GPL(cec_s_phys_addr_from_edid);
+
/*
* Called from either the ioctl or a driver to set the logical addresses.
*
@@ -1534,12 +1586,12 @@ int __cec_s_log_addrs(struct cec_adapter *adap,
if (log_addrs->num_log_addrs == 2) {
if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_AUDIOSYSTEM) |
(1 << CEC_LOG_ADDR_TYPE_TV)))) {
- dprintk(1, "Two LAs is only allowed for audiosystem and TV\n");
+ dprintk(1, "two LAs is only allowed for audiosystem and TV\n");
return -EINVAL;
}
if (!(type_mask & ((1 << CEC_LOG_ADDR_TYPE_PLAYBACK) |
(1 << CEC_LOG_ADDR_TYPE_RECORD)))) {
- dprintk(1, "An audiosystem/TV can only be combined with record or playback\n");
+ dprintk(1, "an audiosystem/TV can only be combined with record or playback\n");
return -EINVAL;
}
}
@@ -1653,7 +1705,7 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
bool from_unregistered = init_laddr == 0xf;
struct cec_msg tx_cec_msg = { };
- dprintk(1, "cec_receive_notify: %*ph\n", msg->len, msg->msg);
+ dprintk(2, "%s: %*ph\n", __func__, msg->len, msg->msg);
/* If this is a CDC-Only device, then ignore any non-CDC messages */
if (cec_is_cdc_only(&adap->log_addrs) &&
@@ -1722,7 +1774,7 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg,
if (!from_unregistered)
adap->phys_addrs[init_laddr] = pa;
- dprintk(1, "Reported physical address %x.%x.%x.%x for logical address %d\n",
+ dprintk(1, "reported physical address %x.%x.%x.%x for logical address %d\n",
cec_phys_addr_exp(pa), init_laddr);
break;
}
@@ -1864,7 +1916,7 @@ void cec_monitor_all_cnt_dec(struct cec_adapter *adap)
WARN_ON(call_op(adap, adap_monitor_all_enable, 0));
}
-#ifdef CONFIG_MEDIA_CEC_DEBUG
+#ifdef CONFIG_DEBUG_FS
/*
* Log the current state of the CEC adapter.
* Very useful for debugging.
diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c
index 0860fb458757..f7eb4c54a354 100644
--- a/drivers/media/cec/cec-api.c
+++ b/drivers/media/cec/cec-api.c
@@ -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;
@@ -271,16 +272,10 @@ static long cec_receive(struct cec_adapter *adap, struct cec_fh *fh,
bool block, struct cec_msg __user *parg)
{
struct cec_msg msg = {};
- long err = 0;
+ long err;
if (copy_from_user(&msg, parg, sizeof(msg)))
return -EFAULT;
- mutex_lock(&adap->lock);
- if (!adap->is_configured && fh->mode_follower < CEC_MODE_MONITOR)
- err = -ENONET;
- mutex_unlock(&adap->lock);
- if (err)
- return err;
err = cec_receive_msg(fh, &msg, block);
if (err)
@@ -521,6 +516,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 +561,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));
}
diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c
index f9ebff90f8eb..b516d599d6c4 100644
--- a/drivers/media/cec/cec-core.c
+++ b/drivers/media/cec/cec-core.c
@@ -187,7 +187,7 @@ static void cec_devnode_unregister(struct cec_devnode *devnode)
put_device(&devnode->dev);
}
-#ifdef CONFIG_MEDIA_CEC_NOTIFIER
+#ifdef CONFIG_CEC_NOTIFIER
static void cec_cec_notify(struct cec_adapter *adap, u16 pa)
{
cec_s_phys_addr(adap, pa, false);
@@ -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;
@@ -323,7 +324,7 @@ int cec_register_adapter(struct cec_adapter *adap,
}
dev_set_drvdata(&adap->devnode.dev, adap);
-#ifdef CONFIG_MEDIA_CEC_DEBUG
+#ifdef CONFIG_DEBUG_FS
if (!top_cec_dir)
return 0;
@@ -355,7 +356,7 @@ void cec_unregister_adapter(struct cec_adapter *adap)
adap->rc = NULL;
#endif
debugfs_remove_recursive(adap->cec_dir);
-#ifdef CONFIG_MEDIA_CEC_NOTIFIER
+#ifdef CONFIG_CEC_NOTIFIER
if (adap->notifier)
cec_notifier_unregister(adap->notifier);
#endif
@@ -395,7 +396,7 @@ static int __init cec_devnode_init(void)
return ret;
}
-#ifdef CONFIG_MEDIA_CEC_DEBUG
+#ifdef CONFIG_DEBUG_FS
top_cec_dir = debugfs_create_dir("cec", NULL);
if (IS_ERR_OR_NULL(top_cec_dir)) {
pr_warn("cec: Failed to create debugfs cec dir\n");