diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-05-25 04:09:16 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-05-25 04:09:16 +0300 |
commit | 827060261cf3c7b79ee7185d5aa61c851beb9403 (patch) | |
tree | ea33be6609b189d31d1e56c09c92c967078eb3f8 /drivers/media/cec/core | |
parent | 268db333b561c77dee3feb6831806412293b4a7e (diff) | |
parent | 340ce50f75a6bdfe6d1850ca49ef37a8e2765dd1 (diff) | |
download | linux-827060261cf3c7b79ee7185d5aa61c851beb9403.tar.xz |
Merge tag 'media/v5.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab:
- dvb-usb drivers entries got reworked to avoid usage of magic numbers
to refer to data position inside tables
- vcodec driver has gained support for MT8186 and for vp8 and vp9
stateless codecs
- hantro has gained support for Hantro G1 on RK366x
- Added more h264 levels on coda960
- ccs gained support for MIPI CSI-2 28 bits per pixel raw data type
- venus driver gained support for Qualcomm custom compressed pixel
formats
- lots of driver fixes and updates
* tag 'media/v5.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits)
media: hantro: Enable HOLD_CAPTURE_BUF for H.264
media: hantro: Add H.264 field decoding support
media: hantro: h264: Make dpb entry management more robust
media: hantro: Stop using H.264 parameter pic_num
media: rkvdec: Enable capture buffer holding for H264
media: rkvdec-h264: Add field decoding support
media: rkvdec: Ensure decoded resolution fit coded resolution
media: rkvdec: h264: Fix reference frame_num wrap for second field
media: rkvdec: h264: Validate and use pic width and height in mbs
media: rkvdec: Move H264 SPS validation in rkvdec-h264
media: rkvdec: h264: Fix bit depth wrap in pps packet
media: rkvdec: h264: Fix dpb_valid implementation
media: rkvdec: Stop overclocking the decoder
media: v4l2: Reorder field reflist
media: h264: Sort p/b reflist using frame_num
media: v4l2: Trace calculated p/b0/b1 initial reflist
media: h264: Store all fields into the unordered list
media: h264: Store current picture fields
media: h264: Increase reference lists size to 32
media: h264: Use v4l2_h264_reference for reflist
...
Diffstat (limited to 'drivers/media/cec/core')
-rw-r--r-- | drivers/media/cec/core/cec-adap.c | 294 | ||||
-rw-r--r-- | drivers/media/cec/core/cec-api.c | 24 | ||||
-rw-r--r-- | drivers/media/cec/core/cec-core.c | 18 | ||||
-rw-r--r-- | drivers/media/cec/core/cec-pin-priv.h | 11 | ||||
-rw-r--r-- | drivers/media/cec/core/cec-pin.c | 72 | ||||
-rw-r--r-- | drivers/media/cec/core/cec-priv.h | 10 |
6 files changed, 262 insertions, 167 deletions
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 2e12331c12a9..8bf91b5a7d0e 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -27,27 +27,6 @@ static void cec_fill_msg_report_features(struct cec_adapter *adap, struct cec_msg *msg, unsigned int la_idx); -/* - * 400 ms is the time it takes for one 16 byte message to be - * transferred and 5 is the maximum number of retries. Add - * another 100 ms as a margin. So if the transmit doesn't - * finish before that time something is really wrong and we - * have to time out. - * - * This is a sign that something it really wrong and a warning - * will be issued. - */ -#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100) - -#define call_op(adap, op, arg...) \ - (adap->ops->op ? adap->ops->op(adap, ## arg) : 0) - -#define call_void_op(adap, op, arg...) \ - do { \ - if (adap->ops->op) \ - adap->ops->op(adap, ## arg); \ - } while (0) - static int cec_log_addr2idx(const struct cec_adapter *adap, u8 log_addr) { int i; @@ -366,38 +345,48 @@ static void cec_data_completed(struct cec_data *data) /* * A pending CEC transmit needs to be cancelled, either because the CEC * adapter is disabled or the transmit takes an impossibly long time to - * finish. + * finish, or the reply timed out. * * This function is called with adap->lock held. */ -static void cec_data_cancel(struct cec_data *data, u8 tx_status) +static void cec_data_cancel(struct cec_data *data, u8 tx_status, u8 rx_status) { + struct cec_adapter *adap = data->adap; + /* * It's either the current transmit, or it is a pending * transmit. Take the appropriate action to clear it. */ - if (data->adap->transmitting == data) { - data->adap->transmitting = NULL; + if (adap->transmitting == data) { + adap->transmitting = NULL; } else { list_del_init(&data->list); if (!(data->msg.tx_status & CEC_TX_STATUS_OK)) - if (!WARN_ON(!data->adap->transmit_queue_sz)) - data->adap->transmit_queue_sz--; + if (!WARN_ON(!adap->transmit_queue_sz)) + adap->transmit_queue_sz--; } if (data->msg.tx_status & CEC_TX_STATUS_OK) { data->msg.rx_ts = ktime_get_ns(); - data->msg.rx_status = CEC_RX_STATUS_ABORTED; + data->msg.rx_status = rx_status; + if (!data->blocking) + data->msg.tx_status = 0; } else { data->msg.tx_ts = ktime_get_ns(); data->msg.tx_status |= tx_status | CEC_TX_STATUS_MAX_RETRIES; data->msg.tx_error_cnt++; data->attempts = 0; + if (!data->blocking) + data->msg.rx_status = 0; } /* Queue transmitted message for monitoring purposes */ - cec_queue_msg_monitor(data->adap, &data->msg, 1); + cec_queue_msg_monitor(adap, &data->msg, 1); + + if (!data->blocking && data->msg.sequence) + /* Allow drivers to process the message first */ + call_op(adap, received, &data->msg); cec_data_completed(data); } @@ -418,15 +407,15 @@ static void cec_flush(struct cec_adapter *adap) while (!list_empty(&adap->transmit_queue)) { data = list_first_entry(&adap->transmit_queue, struct cec_data, list); - cec_data_cancel(data, CEC_TX_STATUS_ABORTED); + cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0); } if (adap->transmitting) - cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED); + adap->transmit_in_progress_aborted = true; /* Cancel the pending timeout work. */ list_for_each_entry_safe(data, n, &adap->wait_queue, list) { if (cancel_delayed_work(&data->work)) - cec_data_cancel(data, CEC_TX_STATUS_OK); + cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_ABORTED); /* * If cancel_delayed_work returned false, then * the cec_wait_timeout function is running, @@ -482,7 +471,7 @@ int cec_thread_func(void *_adap) kthread_should_stop() || (!adap->transmit_in_progress && !list_empty(&adap->transmit_queue)), - msecs_to_jiffies(CEC_XFER_TIMEOUT_MS)); + msecs_to_jiffies(adap->xfer_timeout_ms)); timeout = err == 0; } else { /* Otherwise we just wait for something to happen. */ @@ -508,7 +497,8 @@ int cec_thread_func(void *_adap) * adapter driver, or the CEC bus is in some weird * state. On rare occasions it can happen if there is * so much traffic on the bus that the adapter was - * unable to transmit for CEC_XFER_TIMEOUT_MS (2.1s). + * unable to transmit for xfer_timeout_ms (2.1s by + * default). */ if (adap->transmitting) { pr_warn("cec-%s: message %*ph timed out\n", adap->name, @@ -516,7 +506,7 @@ int cec_thread_func(void *_adap) adap->transmitting->msg.msg); /* Just give up on this. */ cec_data_cancel(adap->transmitting, - CEC_TX_STATUS_TIMEOUT); + CEC_TX_STATUS_TIMEOUT, 0); } else { pr_warn("cec-%s: transmit timed out\n", adap->name); } @@ -572,10 +562,11 @@ int cec_thread_func(void *_adap) if (data->attempts == 0) data->attempts = attempts; + adap->transmit_in_progress_aborted = false; /* Tell the adapter to transmit, cancel on error */ - if (adap->ops->adap_transmit(adap, data->attempts, - signal_free_time, &data->msg)) - cec_data_cancel(data, CEC_TX_STATUS_ABORTED); + if (call_op(adap, adap_transmit, data->attempts, + signal_free_time, &data->msg)) + cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0); else adap->transmit_in_progress = true; @@ -599,6 +590,8 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, struct cec_msg *msg; unsigned int attempts_made = arb_lost_cnt + nack_cnt + low_drive_cnt + error_cnt; + bool done = status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK); + bool aborted = adap->transmit_in_progress_aborted; dprintk(2, "%s: status 0x%02x\n", __func__, status); if (attempts_made < 1) @@ -619,6 +612,7 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, goto wake_thread; } adap->transmit_in_progress = false; + adap->transmit_in_progress_aborted = false; msg = &data->msg; @@ -639,8 +633,7 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, * the hardware didn't signal that it retried itself (by setting * CEC_TX_STATUS_MAX_RETRIES), then we will retry ourselves. */ - if (data->attempts > attempts_made && - !(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) { + if (!aborted && data->attempts > attempts_made && !done) { /* Retry this message */ data->attempts -= attempts_made; if (msg->timeout) @@ -655,6 +648,8 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, goto wake_thread; } + if (aborted && !done) + status |= CEC_TX_STATUS_ABORTED; data->attempts = 0; /* Always set CEC_TX_STATUS_MAX_RETRIES on error */ @@ -733,9 +728,7 @@ static void cec_wait_timeout(struct work_struct *work) /* Mark the message as timed out */ list_del_init(&data->list); - data->msg.rx_ts = ktime_get_ns(); - data->msg.rx_status = CEC_RX_STATUS_TIMEOUT; - cec_data_completed(data); + cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_TIMEOUT); unlock: mutex_unlock(&adap->lock); } @@ -921,8 +914,12 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, mutex_lock(&adap->lock); /* Cancel the transmit if it was interrupted */ - if (!data->completed) - cec_data_cancel(data, CEC_TX_STATUS_ABORTED); + if (!data->completed) { + if (data->msg.tx_status & CEC_TX_STATUS_OK) + cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_ABORTED); + else + cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0); + } /* The transmit completed (possibly with an error) */ *msg = data->msg; @@ -1278,17 +1275,22 @@ static int cec_config_log_addr(struct cec_adapter *adap, * While trying to poll the physical address was reset * and the adapter was unconfigured, so bail out. */ - if (!adap->is_configuring) + if (adap->phys_addr == CEC_PHYS_ADDR_INVALID) + return -EINTR; + + /* Also bail out if the PA changed while configuring. */ + if (adap->must_reconfigure) return -EINTR; if (err) return err; /* - * The message was aborted due to a disconnect or + * The message was aborted or timed out due to a disconnect or * unconfigure, just bail out. */ - if (msg.tx_status & CEC_TX_STATUS_ABORTED) + if (msg.tx_status & + (CEC_TX_STATUS_ABORTED | CEC_TX_STATUS_TIMEOUT)) return -EINTR; if (msg.tx_status & CEC_TX_STATUS_OK) return 0; @@ -1314,7 +1316,7 @@ static int cec_config_log_addr(struct cec_adapter *adap, * Message not acknowledged, so this logical * address is free to use. */ - err = adap->ops->adap_log_addr(adap, log_addr); + err = call_op(adap, adap_log_addr, log_addr); if (err) return err; @@ -1331,15 +1333,14 @@ static int cec_config_log_addr(struct cec_adapter *adap, */ static void cec_adap_unconfigure(struct cec_adapter *adap) { - if (!adap->needs_hpd || - adap->phys_addr != CEC_PHYS_ADDR_INVALID) - 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(call_op(adap, adap_log_addr, CEC_LOG_ADDR_INVALID)); adap->log_addrs.log_addr_mask = 0; - adap->is_configuring = false; adap->is_configured = false; cec_flush(adap); wake_up_interruptible(&adap->kthread_waitq); cec_post_state_event(adap); + call_void_op(adap, adap_configured, false); } /* @@ -1408,6 +1409,7 @@ static int cec_config_thread_func(void *arg) if (las->log_addr_type[0] == CEC_LOG_ADDR_TYPE_UNREGISTERED) goto configured; +reconfigure: for (i = 0; i < las->num_log_addrs; i++) { unsigned int type = las->log_addr_type[i]; const u8 *la_list; @@ -1430,6 +1432,13 @@ static int cec_config_thread_func(void *arg) last_la = la_list[0]; err = cec_config_log_addr(adap, i, last_la); + + if (adap->must_reconfigure) { + adap->must_reconfigure = false; + las->log_addr_mask = 0; + goto reconfigure; + } + if (err > 0) /* Reused last LA */ continue; @@ -1475,6 +1484,7 @@ configured: las->log_addr[i] = CEC_LOG_ADDR_INVALID; adap->is_configured = true; adap->is_configuring = false; + adap->must_reconfigure = false; cec_post_state_event(adap); /* @@ -1521,15 +1531,18 @@ configured: adap->kthread_config = NULL; complete(&adap->config_completion); mutex_unlock(&adap->lock); + call_void_op(adap, adap_configured, true); return 0; unconfigure: for (i = 0; i < las->num_log_addrs; i++) las->log_addr[i] = CEC_LOG_ADDR_INVALID; cec_adap_unconfigure(adap); + adap->is_configuring = false; + adap->must_reconfigure = false; adap->kthread_config = NULL; - mutex_unlock(&adap->lock); complete(&adap->config_completion); + mutex_unlock(&adap->lock); return 0; } @@ -1552,6 +1565,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,63 +1573,96 @@ static void cec_claim_log_addrs(struct cec_adapter *adap, bool block) } } +/* + * Helper function to enable/disable the CEC adapter. + * + * This function is called with adap->lock held. + */ +static int cec_adap_enable(struct cec_adapter *adap) +{ + bool enable; + int ret = 0; + + enable = adap->monitor_all_cnt || adap->monitor_pin_cnt || + adap->log_addrs.num_log_addrs; + if (adap->needs_hpd) + enable = enable && adap->phys_addr != CEC_PHYS_ADDR_INVALID; + + if (enable == adap->is_enabled) + return 0; + + /* serialize adap_enable */ + mutex_lock(&adap->devnode.lock); + if (enable) { + adap->last_initiator = 0xff; + adap->transmit_in_progress = false; + ret = adap->ops->adap_enable(adap, true); + if (!ret) { + /* + * 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)); + } + } else { + /* Disable monitor-all/pin modes if needed (needs_hpd == 1) */ + 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)); + WARN_ON(adap->ops->adap_enable(adap, false)); + adap->last_initiator = 0xff; + adap->transmit_in_progress = false; + adap->transmit_in_progress_aborted = false; + if (adap->transmitting) + cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED, 0); + } + if (!ret) + adap->is_enabled = enable; + wake_up_interruptible(&adap->kthread_waitq); + mutex_unlock(&adap->devnode.lock); + return ret; +} + /* 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; - wake_up_interruptible(&adap->kthread_waitq); - } - mutex_unlock(&adap->devnode.lock); - if (phys_addr == CEC_PHYS_ADDR_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 (becomes_invalid) { + cec_adap_enable(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; - } - mutex_unlock(&adap->devnode.lock); - adap->phys_addr = phys_addr; + if (is_invalid) + cec_adap_enable(adap); + cec_post_state_event(adap); - if (adap->log_addrs.num_log_addrs) + if (!adap->log_addrs.num_log_addrs) + return; + if (adap->is_configuring) + adap->must_reconfigure = true; + else cec_claim_log_addrs(adap, block); } @@ -1670,19 +1717,24 @@ int __cec_s_log_addrs(struct cec_adapter *adap, struct cec_log_addrs *log_addrs, bool block) { u16 type_mask = 0; + int err; int i; if (adap->devnode.unregistered) return -ENODEV; if (!log_addrs || log_addrs->num_log_addrs == 0) { - cec_adap_unconfigure(adap); + if (!adap->log_addrs.num_log_addrs) + return 0; + if (adap->is_configuring || adap->is_configured) + cec_adap_unconfigure(adap); adap->log_addrs.num_log_addrs = 0; for (i = 0; i < CEC_MAX_LOG_ADDRS; i++) adap->log_addrs.log_addr[i] = CEC_LOG_ADDR_INVALID; 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; + cec_adap_enable(adap); return 0; } @@ -1818,9 +1870,10 @@ int __cec_s_log_addrs(struct cec_adapter *adap, log_addrs->log_addr_mask = adap->log_addrs.log_addr_mask; adap->log_addrs = *log_addrs; - if (adap->phys_addr != CEC_PHYS_ADDR_INVALID) + err = cec_adap_enable(adap); + if (!err && adap->phys_addr != CEC_PHYS_ADDR_INVALID) cec_claim_log_addrs(adap, block); - return 0; + return err; } int cec_s_log_addrs(struct cec_adapter *adap, @@ -1922,11 +1975,10 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, msg->msg[1] != CEC_MSG_CDC_MESSAGE) return 0; - if (adap->ops->received) { - /* Allow drivers to process the message first */ - if (adap->ops->received(adap, msg) != -ENOMSG) - return 0; - } + /* Allow drivers to process the message first */ + if (adap->ops->received && !adap->devnode.unregistered && + adap->ops->received(adap, msg) != -ENOMSG) + return 0; /* * REPORT_PHYSICAL_ADDR, CEC_MSG_USER_CONTROL_PRESSED and @@ -2119,20 +2171,25 @@ skip_processing: */ int cec_monitor_all_cnt_inc(struct cec_adapter *adap) { - int ret = 0; + int ret; + + if (adap->monitor_all_cnt++) + return 0; - if (adap->monitor_all_cnt == 0) - ret = call_op(adap, adap_monitor_all_enable, 1); - if (ret == 0) - adap->monitor_all_cnt++; + ret = cec_adap_enable(adap); + if (ret) + adap->monitor_all_cnt--; 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)); + cec_adap_enable(adap); } /* @@ -2142,20 +2199,25 @@ 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++) + return 0; - if (adap->monitor_pin_cnt == 0) - ret = call_op(adap, adap_monitor_pin_enable, 1); - if (ret == 0) - adap->monitor_pin_cnt++; + ret = cec_adap_enable(adap); + if (ret) + adap->monitor_pin_cnt--; 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)); + cec_adap_enable(adap); } #ifdef CONFIG_DEBUG_FS @@ -2169,6 +2231,7 @@ int cec_adap_status(struct seq_file *file, void *priv) struct cec_data *data; mutex_lock(&adap->lock); + seq_printf(file, "enabled: %d\n", adap->is_enabled); 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 +2246,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); diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c index d72ad48c9898..67dc79ef1705 100644 --- a/drivers/media/cec/core/cec-api.c +++ b/drivers/media/cec/core/cec-api.c @@ -586,18 +586,6 @@ static int cec_open(struct inode *inode, struct file *filp) return err; } - /* serialize adap_enable */ - 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) { - mutex_unlock(&devnode->lock); - kfree(fh); - return err; - } - } filp->private_data = fh; /* Queue up initial state events */ @@ -607,7 +595,8 @@ static int cec_open(struct inode *inode, struct file *filp) adap->conn_info.type != CEC_CONNECTOR_TYPE_NO_CONNECTOR; cec_queue_event_fh(fh, &ev, 0); #ifdef CONFIG_CEC_PIN - if (adap->pin && adap->pin->ops->read_hpd) { + if (adap->pin && adap->pin->ops->read_hpd && + !adap->devnode.unregistered) { err = adap->pin->ops->read_hpd(adap); if (err >= 0) { ev.event = err ? CEC_EVENT_PIN_HPD_HIGH : @@ -615,7 +604,8 @@ static int cec_open(struct inode *inode, struct file *filp) cec_queue_event_fh(fh, &ev, 0); } } - if (adap->pin && adap->pin->ops->read_5v) { + if (adap->pin && adap->pin->ops->read_5v && + !adap->devnode.unregistered) { err = adap->pin->ops->read_5v(adap); if (err >= 0) { ev.event = err ? CEC_EVENT_PIN_5V_HIGH : @@ -625,6 +615,7 @@ static int cec_open(struct inode *inode, struct file *filp) } #endif + mutex_lock(&devnode->lock); mutex_lock(&devnode->lock_fhs); list_add(&fh->list, &devnode->fhs); mutex_unlock(&devnode->lock_fhs); @@ -656,15 +647,10 @@ static int cec_release(struct inode *inode, struct file *filp) cec_monitor_all_cnt_dec(adap); mutex_unlock(&adap->lock); - /* serialize adap_enable */ mutex_lock(&devnode->lock); mutex_lock(&devnode->lock_fhs); list_del(&fh->list); mutex_unlock(&devnode->lock_fhs); - if (cec_is_registered(adap) && list_empty(&devnode->fhs) && - !adap->needs_hpd && adap->phys_addr == CEC_PHYS_ADDR_INVALID) { - WARN_ON(adap->ops->adap_enable(adap, false)); - } mutex_unlock(&devnode->lock); /* Unhook pending transmits from this filehandle. */ diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index a3ab6a43fb14..af358e901b5f 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -20,6 +20,18 @@ #define CEC_NUM_DEVICES 256 #define CEC_NAME "cec" +/* + * 400 ms is the time it takes for one 16 byte message to be + * transferred and 5 is the maximum number of retries. Add + * another 100 ms as a margin. So if the transmit doesn't + * finish before that time something is really wrong and we + * have to time out. + * + * This is a sign that something it really wrong and a warning + * will be issued. + */ +#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100) + int cec_debug; module_param_named(debug, cec_debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); @@ -204,7 +216,7 @@ static ssize_t cec_error_inj_write(struct file *file, line = strsep(&p, "\n"); if (!*line || *line == '#') continue; - if (!adap->ops->error_inj_parse_line(adap, line)) { + if (!call_op(adap, error_inj_parse_line, line)) { kfree(buf); return -EINVAL; } @@ -217,7 +229,7 @@ static int cec_error_inj_show(struct seq_file *sf, void *unused) { struct cec_adapter *adap = sf->private; - return adap->ops->error_inj_show(adap, sf); + return call_op(adap, error_inj_show, sf); } static int cec_error_inj_open(struct inode *inode, struct file *file) @@ -331,6 +343,8 @@ int cec_register_adapter(struct cec_adapter *adap, adap->owner = parent->driver->owner; adap->devnode.dev.parent = parent; + if (!adap->xfer_timeout_ms) + adap->xfer_timeout_ms = CEC_XFER_TIMEOUT_MS; #ifdef CONFIG_MEDIA_CEC_RC if (adap->capabilities & CEC_CAP_RC) { diff --git a/drivers/media/cec/core/cec-pin-priv.h b/drivers/media/cec/core/cec-pin-priv.h index 7bad5a0b7cb7..8eb5819e6ccb 100644 --- a/drivers/media/cec/core/cec-pin-priv.h +++ b/drivers/media/cec/core/cec-pin-priv.h @@ -12,6 +12,17 @@ #include <linux/atomic.h> #include <media/cec-pin.h> +#define call_pin_op(pin, op, arg...) \ + ((pin && pin->ops->op && !pin->adap->devnode.unregistered) ? \ + pin->ops->op(pin->adap, ## arg) : 0) + +#define call_void_pin_op(pin, op, arg...) \ + do { \ + if (pin && pin->ops->op && \ + !pin->adap->devnode.unregistered) \ + pin->ops->op(pin->adap, ## arg); \ + } while (0) + enum cec_pin_state { /* CEC is off */ CEC_ST_OFF, diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c index 21f0f749713e..68353c5dc501 100644 --- a/drivers/media/cec/core/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c @@ -135,7 +135,7 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force) static bool cec_pin_read(struct cec_pin *pin) { - bool v = pin->ops->read(pin->adap); + bool v = call_pin_op(pin, read); cec_pin_update(pin, v, false); return v; @@ -143,13 +143,13 @@ static bool cec_pin_read(struct cec_pin *pin) static void cec_pin_low(struct cec_pin *pin) { - pin->ops->low(pin->adap); + call_void_pin_op(pin, low); cec_pin_update(pin, false, false); } static bool cec_pin_high(struct cec_pin *pin) { - pin->ops->high(pin->adap); + call_void_pin_op(pin, high); return cec_pin_read(pin); } @@ -1037,11 +1037,14 @@ static int cec_pin_thread_func(void *_adap) for (;;) { wait_event_interruptible(pin->kthread_waitq, - kthread_should_stop() || - pin->work_rx_msg.len || - pin->work_tx_status || - atomic_read(&pin->work_irq_change) || - atomic_read(&pin->work_pin_num_events)); + kthread_should_stop() || + pin->work_rx_msg.len || + pin->work_tx_status || + atomic_read(&pin->work_irq_change) || + atomic_read(&pin->work_pin_num_events)); + + if (kthread_should_stop()) + break; if (pin->work_rx_msg.len) { struct cec_msg *msg = &pin->work_rx_msg; @@ -1086,10 +1089,12 @@ static int cec_pin_thread_func(void *_adap) CEC_PIN_IRQ_UNCHANGED)) { case CEC_PIN_IRQ_DISABLE: if (irq_enabled) { - pin->ops->disable_irq(adap); + call_void_pin_op(pin, disable_irq); irq_enabled = false; } cec_pin_high(pin); + if (pin->state == CEC_ST_OFF) + break; cec_pin_to_idle(pin); hrtimer_start(&pin->timer, ns_to_ktime(0), HRTIMER_MODE_REL); @@ -1097,7 +1102,7 @@ static int cec_pin_thread_func(void *_adap) case CEC_PIN_IRQ_ENABLE: if (irq_enabled) break; - pin->enable_irq_failed = !pin->ops->enable_irq(adap); + pin->enable_irq_failed = !call_pin_op(pin, enable_irq); if (pin->enable_irq_failed) { cec_pin_to_idle(pin); hrtimer_start(&pin->timer, ns_to_ktime(0), @@ -1109,15 +1114,7 @@ static int cec_pin_thread_func(void *_adap) default: break; } - if (kthread_should_stop()) - break; } - if (pin->ops->disable_irq && irq_enabled) - pin->ops->disable_irq(adap); - hrtimer_cancel(&pin->timer); - cec_pin_read(pin); - cec_pin_to_idle(pin); - pin->state = CEC_ST_OFF; return 0; } @@ -1126,24 +1123,32 @@ static int cec_pin_adap_enable(struct cec_adapter *adap, bool enable) struct cec_pin *pin = adap->pin; if (enable) { - atomic_set(&pin->work_pin_num_events, 0); - pin->work_pin_events_rd = pin->work_pin_events_wr = 0; - pin->work_pin_events_dropped = false; cec_pin_read(pin); cec_pin_to_idle(pin); pin->tx_msg.len = 0; pin->timer_ts = ns_to_ktime(0); atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_UNCHANGED); - pin->kthread = kthread_run(cec_pin_thread_func, adap, - "cec-pin"); - if (IS_ERR(pin->kthread)) { - pr_err("cec-pin: kernel_thread() failed\n"); - return PTR_ERR(pin->kthread); + if (!pin->kthread) { + pin->kthread = kthread_run(cec_pin_thread_func, adap, + "cec-pin"); + if (IS_ERR(pin->kthread)) { + int err = PTR_ERR(pin->kthread); + + pr_err("cec-pin: kernel_thread() failed\n"); + pin->kthread = NULL; + return err; + } } hrtimer_start(&pin->timer, ns_to_ktime(0), HRTIMER_MODE_REL); - } else { - kthread_stop(pin->kthread); + } else if (pin->kthread) { + hrtimer_cancel(&pin->timer); + cec_pin_high(pin); + cec_pin_to_idle(pin); + pin->state = CEC_ST_OFF; + pin->work_tx_status = 0; + atomic_set(&pin->work_irq_change, CEC_PIN_IRQ_DISABLE); + wake_up_interruptible(&pin->kthread_waitq); } return 0; } @@ -1207,7 +1212,7 @@ static void cec_pin_adap_status(struct cec_adapter *adap, seq_printf(file, "state: %s\n", states[pin->state].name); seq_printf(file, "tx_bit: %d\n", pin->tx_bit); seq_printf(file, "rx_bit: %d\n", pin->rx_bit); - seq_printf(file, "cec pin: %d\n", pin->ops->read(adap)); + seq_printf(file, "cec pin: %d\n", call_pin_op(pin, read)); seq_printf(file, "cec pin events dropped: %u\n", pin->work_pin_events_dropped_cnt); seq_printf(file, "irq failed: %d\n", pin->enable_irq_failed); @@ -1260,8 +1265,7 @@ static void cec_pin_adap_status(struct cec_adapter *adap, pin->rx_data_bit_too_long_cnt = 0; pin->rx_low_drive_cnt = 0; pin->tx_low_drive_cnt = 0; - if (pin->ops->status) - pin->ops->status(adap, file); + call_void_pin_op(pin, status, file); } static int cec_pin_adap_monitor_all_enable(struct cec_adapter *adap, @@ -1277,6 +1281,9 @@ static void cec_pin_adap_free(struct cec_adapter *adap) { struct cec_pin *pin = adap->pin; + if (pin->kthread) + kthread_stop(pin->kthread); + pin->kthread = NULL; if (pin->ops->free) pin->ops->free(adap); adap->pin = NULL; @@ -1287,7 +1294,7 @@ static int cec_pin_received(struct cec_adapter *adap, struct cec_msg *msg) { struct cec_pin *pin = adap->pin; - if (pin->ops->received) + if (pin->ops->received && !adap->devnode.unregistered) return pin->ops->received(adap, msg); return -ENOMSG; } @@ -1327,6 +1334,7 @@ struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops, return ERR_PTR(-ENOMEM); pin->ops = pin_ops; hrtimer_init(&pin->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + atomic_set(&pin->work_pin_num_events, 0); pin->timer.function = cec_pin_timer; init_waitqueue_head(&pin->kthread_waitq); pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT; diff --git a/drivers/media/cec/core/cec-priv.h b/drivers/media/cec/core/cec-priv.h index 9bbd05053d42..b78df931aa74 100644 --- a/drivers/media/cec/core/cec-priv.h +++ b/drivers/media/cec/core/cec-priv.h @@ -17,6 +17,16 @@ pr_info("cec-%s: " fmt, adap->name, ## arg); \ } while (0) +#define call_op(adap, op, arg...) \ + ((adap->ops->op && !adap->devnode.unregistered) ? \ + adap->ops->op(adap, ## arg) : 0) + +#define call_void_op(adap, op, arg...) \ + do { \ + if (adap->ops->op && !adap->devnode.unregistered) \ + adap->ops->op(adap, ## arg); \ + } while (0) + /* devnode to cec_adapter */ #define to_cec_adapter(node) container_of(node, struct cec_adapter, devnode) |