summaryrefslogtreecommitdiffstats
path: root/drivers/media/cec/core/cec-adap.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil-cisco@xs4all.nl>2022-03-03 13:55:08 +0000
committerMauro Carvalho Chehab <mchehab@kernel.org>2022-04-24 07:37:58 +0100
commit3813c932ed970dd4f413498ccecb03c73c4f1784 (patch)
tree509956bf8e6c674130f45056b9ad256de0fe1085 /drivers/media/cec/core/cec-adap.c
parent82b4737fd001247527405b19045c25bf1622ab6d (diff)
downloadlinux-3813c932ed970dd4f413498ccecb03c73c4f1784.tar.bz2
media: cec: call enable_adap on s_log_addrs
Don't enable/disable the adapter if the first fh is opened or the last fh is closed, instead do this when the adapter is configured or unconfigured, and also when we enter Monitor All or Monitor Pin mode for the first time or we exit the Monitor All/Pin mode for the last time. However, if needs_hpd is true, then do this when the physical address is set or cleared: in that case the adapter typically is powered by the HPD, so it really is disabled when the HPD is low. This case (needs_hpd is true) was already handled in this way, so this wasn't changed. The problem with the old behavior was that if the HPD goes low when no fh is open, and a transmit was in progress, then the adapter would be disabled, typically stopping the transmit immediately which leaves a partial message on the bus, which isn't nice and can confuse some adapters. It makes much more sense to disable it only when the adapter is unconfigured and we're not monitoring the bus, since then you really won't be using it anymore. To keep track of this store a CEC activation count and call adap_enable only when it goes from 0 to 1 or back to 0. Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Diffstat (limited to 'drivers/media/cec/core/cec-adap.c')
-rw-r--r--drivers/media/cec/core/cec-adap.c174
1 files changed, 127 insertions, 47 deletions
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 2e12331c12a9..1a095308f3ab 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -1552,6 +1552,7 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
"ceccfg-%s", adap->name);
if (IS_ERR(adap->kthread_config)) {
adap->kthread_config = NULL;
+ adap->is_configuring = false;
} else if (block) {
mutex_unlock(&adap->lock);
wait_for_completion(&adap->config_completion);
@@ -1559,59 +1560,90 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
}
}
+/*
+ * Helper functions to enable/disable the CEC adapter.
+ *
+ * These functions are called with adap->lock held.
+ */
+static int cec_activate_cnt_inc(struct cec_adapter *adap)
+{
+ int ret;
+
+ if (adap->activate_cnt++)
+ return 0;
+
+ /* serialize adap_enable */
+ mutex_lock(&adap->devnode.lock);
+ adap->last_initiator = 0xff;
+ adap->transmit_in_progress = false;
+ ret = adap->ops->adap_enable(adap, true);
+ if (ret)
+ adap->activate_cnt--;
+ mutex_unlock(&adap->devnode.lock);
+ return ret;
+}
+
+static void cec_activate_cnt_dec(struct cec_adapter *adap)
+{
+ if (WARN_ON(!adap->activate_cnt))
+ return;
+
+ if (--adap->activate_cnt)
+ return;
+
+ /* serialize adap_enable */
+ mutex_lock(&adap->devnode.lock);
+ WARN_ON(adap->ops->adap_enable(adap, false));
+ adap->last_initiator = 0xff;
+ adap->transmit_in_progress = false;
+ mutex_unlock(&adap->devnode.lock);
+}
+
/* Set a new physical address and send an event notifying userspace of this.
*
* This function is called with adap->lock held.
*/
void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block)
{
+ bool becomes_invalid = phys_addr == CEC_PHYS_ADDR_INVALID;
+ bool is_invalid = adap->phys_addr == CEC_PHYS_ADDR_INVALID;
+
if (phys_addr == adap->phys_addr)
return;
- if (phys_addr != CEC_PHYS_ADDR_INVALID && adap->devnode.unregistered)
+ if (!becomes_invalid && 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) {
+ if (becomes_invalid || !is_invalid) {
adap->phys_addr = CEC_PHYS_ADDR_INVALID;
cec_post_state_event(adap);
cec_adap_unconfigure(adap);
- /* Disabling monitor all mode should always succeed */
- if (adap->monitor_all_cnt)
- WARN_ON(call_op(adap, adap_monitor_all_enable, false));
- /* serialize adap_enable */
- mutex_lock(&adap->devnode.lock);
- if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) {
- WARN_ON(adap->ops->adap_enable(adap, false));
- adap->transmit_in_progress = false;
+ if (becomes_invalid && adap->needs_hpd) {
+ /* Disable monitor-all/pin modes if needed */
+ if (adap->monitor_all_cnt)
+ WARN_ON(call_op(adap, adap_monitor_all_enable, false));
+ if (adap->monitor_pin_cnt)
+ WARN_ON(call_op(adap, adap_monitor_pin_enable, false));
+ cec_activate_cnt_dec(adap);
wake_up_interruptible(&adap->kthread_waitq);
}
- mutex_unlock(&adap->devnode.lock);
- if (phys_addr == CEC_PHYS_ADDR_INVALID)
+ if (becomes_invalid)
return;
}
- /* serialize adap_enable */
- mutex_lock(&adap->devnode.lock);
- adap->last_initiator = 0xff;
- adap->transmit_in_progress = false;
-
- if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) {
- if (adap->ops->adap_enable(adap, true)) {
- mutex_unlock(&adap->devnode.lock);
+ if (is_invalid && adap->needs_hpd) {
+ if (cec_activate_cnt_inc(adap))
return;
- }
- }
-
- if (adap->monitor_all_cnt &&
- call_op(adap, adap_monitor_all_enable, true)) {
- if (adap->needs_hpd || list_empty(&adap->devnode.fhs))
- WARN_ON(adap->ops->adap_enable(adap, false));
- mutex_unlock(&adap->devnode.lock);
- return;
+ /*
+ * Re-enable monitor-all/pin modes if needed. We warn, but
+ * continue if this fails as this is not a critical error.
+ */
+ if (adap->monitor_all_cnt)
+ WARN_ON(call_op(adap, adap_monitor_all_enable, true));
+ if (adap->monitor_pin_cnt)
+ WARN_ON(call_op(adap, adap_monitor_pin_enable, true));
}
- mutex_unlock(&adap->devnode.lock);
adap->phys_addr = phys_addr;
cec_post_state_event(adap);
@@ -1676,6 +1708,8 @@ int __cec_s_log_addrs(struct cec_adapter *adap,
return -ENODEV;
if (!log_addrs || log_addrs->num_log_addrs == 0) {
+ if (!adap->is_configuring && !adap->is_configured)
+ return 0;
cec_adap_unconfigure(adap);
adap->log_addrs.num_log_addrs = 0;
for (i = 0; i < CEC_MAX_LOG_ADDRS; i++)
@@ -1683,6 +1717,8 @@ int __cec_s_log_addrs(struct cec_adapter *adap,
adap->log_addrs.osd_name[0] = '\0';
adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;
adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;
+ if (!adap->needs_hpd)
+ cec_activate_cnt_dec(adap);
return 0;
}
@@ -1816,6 +1852,12 @@ int __cec_s_log_addrs(struct cec_adapter *adap,
sizeof(log_addrs->features[i]));
}
+ if (!adap->needs_hpd && !adap->is_configuring && !adap->is_configured) {
+ int ret = cec_activate_cnt_inc(adap);
+
+ if (ret)
+ return ret;
+ }
log_addrs->log_addr_mask = adap->log_addrs.log_addr_mask;
adap->log_addrs = *log_addrs;
if (adap->phys_addr != CEC_PHYS_ADDR_INVALID)
@@ -2119,20 +2161,37 @@ skip_processing:
*/
int cec_monitor_all_cnt_inc(struct cec_adapter *adap)
{
- int ret = 0;
+ int ret;
- if (adap->monitor_all_cnt == 0)
- ret = call_op(adap, adap_monitor_all_enable, 1);
- if (ret == 0)
- adap->monitor_all_cnt++;
+ if (adap->monitor_all_cnt++)
+ return 0;
+
+ if (!adap->needs_hpd) {
+ ret = cec_activate_cnt_inc(adap);
+ if (ret) {
+ adap->monitor_all_cnt--;
+ return ret;
+ }
+ }
+
+ ret = call_op(adap, adap_monitor_all_enable, true);
+ if (ret) {
+ adap->monitor_all_cnt--;
+ if (!adap->needs_hpd)
+ cec_activate_cnt_dec(adap);
+ }
return ret;
}
void cec_monitor_all_cnt_dec(struct cec_adapter *adap)
{
- adap->monitor_all_cnt--;
- if (adap->monitor_all_cnt == 0)
- WARN_ON(call_op(adap, adap_monitor_all_enable, 0));
+ if (WARN_ON(!adap->monitor_all_cnt))
+ return;
+ if (--adap->monitor_all_cnt)
+ return;
+ WARN_ON(call_op(adap, adap_monitor_all_enable, false));
+ if (!adap->needs_hpd)
+ cec_activate_cnt_dec(adap);
}
/*
@@ -2142,20 +2201,37 @@ void cec_monitor_all_cnt_dec(struct cec_adapter *adap)
*/
int cec_monitor_pin_cnt_inc(struct cec_adapter *adap)
{
- int ret = 0;
+ int ret;
- if (adap->monitor_pin_cnt == 0)
- ret = call_op(adap, adap_monitor_pin_enable, 1);
- if (ret == 0)
- adap->monitor_pin_cnt++;
+ if (adap->monitor_pin_cnt++)
+ return 0;
+
+ if (!adap->needs_hpd) {
+ ret = cec_activate_cnt_inc(adap);
+ if (ret) {
+ adap->monitor_pin_cnt--;
+ return ret;
+ }
+ }
+
+ ret = call_op(adap, adap_monitor_pin_enable, true);
+ if (ret) {
+ adap->monitor_pin_cnt--;
+ if (!adap->needs_hpd)
+ cec_activate_cnt_dec(adap);
+ }
return ret;
}
void cec_monitor_pin_cnt_dec(struct cec_adapter *adap)
{
- adap->monitor_pin_cnt--;
- if (adap->monitor_pin_cnt == 0)
- WARN_ON(call_op(adap, adap_monitor_pin_enable, 0));
+ if (WARN_ON(!adap->monitor_pin_cnt))
+ return;
+ if (--adap->monitor_pin_cnt)
+ return;
+ WARN_ON(call_op(adap, adap_monitor_pin_enable, false));
+ if (!adap->needs_hpd)
+ cec_activate_cnt_dec(adap);
}
#ifdef CONFIG_DEBUG_FS
@@ -2169,6 +2245,7 @@ int cec_adap_status(struct seq_file *file, void *priv)
struct cec_data *data;
mutex_lock(&adap->lock);
+ seq_printf(file, "activation count: %u\n", adap->activate_cnt);
seq_printf(file, "configured: %d\n", adap->is_configured);
seq_printf(file, "configuring: %d\n", adap->is_configuring);
seq_printf(file, "phys_addr: %x.%x.%x.%x\n",
@@ -2183,6 +2260,9 @@ int cec_adap_status(struct seq_file *file, void *priv)
if (adap->monitor_all_cnt)
seq_printf(file, "file handles in Monitor All mode: %u\n",
adap->monitor_all_cnt);
+ if (adap->monitor_pin_cnt)
+ seq_printf(file, "file handles in Monitor Pin mode: %u\n",
+ adap->monitor_pin_cnt);
if (adap->tx_timeouts) {
seq_printf(file, "transmit timeouts: %u\n",
adap->tx_timeouts);