Commit 333ef6bd authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

media: cec: add CEC_EVENT_PIN_HPD_LOW/HIGH events

Add support for two new low-level events: PIN_HPD_LOW and PIN_HPD_HIGH.

This is specifically meant for use with the upcoming cec-gpio driver
and makes it possible to trace when the HPD pin changes. Some HDMI
sinks do strange things with the HPD and this makes it easy to debug
this.

Note that this also moves the initialization of a devnode mutex and
list to the allocate_adapter function: if the HPD is high, then as
soon as the HPD interrupt is created an interrupt occurs and
cec_queue_pin_hpd_event() is called which requires that the devnode
mutex and list are initialized.
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@s-opensource.com>
parent da634f62
...@@ -86,7 +86,7 @@ void cec_queue_event_fh(struct cec_fh *fh, ...@@ -86,7 +86,7 @@ void cec_queue_event_fh(struct cec_fh *fh,
const struct cec_event *new_ev, u64 ts) const struct cec_event *new_ev, u64 ts)
{ {
static const u8 max_events[CEC_NUM_EVENTS] = { static const u8 max_events[CEC_NUM_EVENTS] = {
1, 1, 64, 64, 1, 1, 64, 64, 8, 8,
}; };
struct cec_event_entry *entry; struct cec_event_entry *entry;
unsigned int ev_idx = new_ev->event - 1; unsigned int ev_idx = new_ev->event - 1;
...@@ -170,6 +170,22 @@ void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, ktime_t ts) ...@@ -170,6 +170,22 @@ void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, ktime_t ts)
} }
EXPORT_SYMBOL_GPL(cec_queue_pin_cec_event); EXPORT_SYMBOL_GPL(cec_queue_pin_cec_event);
/* Notify userspace that the HPD pin changed state at the given time. */
void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts)
{
struct cec_event ev = {
.event = is_high ? CEC_EVENT_PIN_HPD_HIGH :
CEC_EVENT_PIN_HPD_LOW,
};
struct cec_fh *fh;
mutex_lock(&adap->devnode.lock);
list_for_each_entry(fh, &adap->devnode.fhs, list)
cec_queue_event_fh(fh, &ev, ktime_to_ns(ts));
mutex_unlock(&adap->devnode.lock);
}
EXPORT_SYMBOL_GPL(cec_queue_pin_hpd_event);
/* /*
* Queue a new message for this filehandle. * Queue a new message for this filehandle.
* *
......
...@@ -529,7 +529,7 @@ static int cec_open(struct inode *inode, struct file *filp) ...@@ -529,7 +529,7 @@ static int cec_open(struct inode *inode, struct file *filp)
* Initial events that are automatically sent when the cec device is * Initial events that are automatically sent when the cec device is
* opened. * opened.
*/ */
struct cec_event ev_state = { struct cec_event ev = {
.event = CEC_EVENT_STATE_CHANGE, .event = CEC_EVENT_STATE_CHANGE,
.flags = CEC_EVENT_FL_INITIAL_STATE, .flags = CEC_EVENT_FL_INITIAL_STATE,
}; };
...@@ -569,9 +569,19 @@ static int cec_open(struct inode *inode, struct file *filp) ...@@ -569,9 +569,19 @@ static int cec_open(struct inode *inode, struct file *filp)
filp->private_data = fh; filp->private_data = fh;
/* Queue up initial state events */ /* Queue up initial state events */
ev_state.state_change.phys_addr = adap->phys_addr; ev.state_change.phys_addr = adap->phys_addr;
ev_state.state_change.log_addr_mask = adap->log_addrs.log_addr_mask; ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask;
cec_queue_event_fh(fh, &ev_state, 0); cec_queue_event_fh(fh, &ev, 0);
#ifdef CONFIG_CEC_PIN
if (adap->pin && adap->pin->ops->read_hpd) {
err = adap->pin->ops->read_hpd(adap);
if (err >= 0) {
ev.event = err ? CEC_EVENT_PIN_HPD_HIGH :
CEC_EVENT_PIN_HPD_LOW;
cec_queue_event_fh(fh, &ev, 0);
}
}
#endif
list_add(&fh->list, &devnode->fhs); list_add(&fh->list, &devnode->fhs);
mutex_unlock(&devnode->lock); mutex_unlock(&devnode->lock);
......
...@@ -112,10 +112,6 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode, ...@@ -112,10 +112,6 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode,
int minor; int minor;
int ret; int ret;
/* Initialization */
INIT_LIST_HEAD(&devnode->fhs);
mutex_init(&devnode->lock);
/* Part 1: Find a free minor number */ /* Part 1: Find a free minor number */
mutex_lock(&cec_devnode_lock); mutex_lock(&cec_devnode_lock);
minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0); minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0);
...@@ -242,6 +238,10 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, ...@@ -242,6 +238,10 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
INIT_LIST_HEAD(&adap->wait_queue); INIT_LIST_HEAD(&adap->wait_queue);
init_waitqueue_head(&adap->kthread_waitq); init_waitqueue_head(&adap->kthread_waitq);
/* adap->devnode initialization */
INIT_LIST_HEAD(&adap->devnode.fhs);
mutex_init(&adap->devnode.lock);
adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name); adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name);
if (IS_ERR(adap->kthread)) { if (IS_ERR(adap->kthread)) {
pr_err("cec-%s: kernel_thread() failed\n", name); pr_err("cec-%s: kernel_thread() failed\n", name);
......
...@@ -97,6 +97,9 @@ enum cec_pin_state { ...@@ -97,6 +97,9 @@ enum cec_pin_state {
* @free: optional. Free any allocated resources. Called when the * @free: optional. Free any allocated resources. Called when the
* adapter is deleted. * adapter is deleted.
* @status: optional, log status information. * @status: optional, log status information.
* @read_hpd: read the HPD pin. Return true if high, false if low or
* an error if negative. If NULL or -ENOTTY is returned,
* then this is not supported.
* *
* These operations are used by the cec pin framework to manipulate * These operations are used by the cec pin framework to manipulate
* the CEC pin. * the CEC pin.
...@@ -109,6 +112,7 @@ struct cec_pin_ops { ...@@ -109,6 +112,7 @@ struct cec_pin_ops {
void (*disable_irq)(struct cec_adapter *adap); void (*disable_irq)(struct cec_adapter *adap);
void (*free)(struct cec_adapter *adap); void (*free)(struct cec_adapter *adap);
void (*status)(struct cec_adapter *adap, struct seq_file *file); void (*status)(struct cec_adapter *adap, struct seq_file *file);
int (*read_hpd)(struct cec_adapter *adap);
}; };
#define CEC_NUM_PIN_EVENTS 128 #define CEC_NUM_PIN_EVENTS 128
......
...@@ -91,7 +91,7 @@ struct cec_event_entry { ...@@ -91,7 +91,7 @@ struct cec_event_entry {
}; };
#define CEC_NUM_CORE_EVENTS 2 #define CEC_NUM_CORE_EVENTS 2
#define CEC_NUM_EVENTS CEC_EVENT_PIN_CEC_HIGH #define CEC_NUM_EVENTS CEC_EVENT_PIN_HPD_HIGH
struct cec_fh { struct cec_fh {
struct list_head list; struct list_head list;
...@@ -296,6 +296,16 @@ static inline void cec_received_msg(struct cec_adapter *adap, ...@@ -296,6 +296,16 @@ static inline void cec_received_msg(struct cec_adapter *adap,
void cec_queue_pin_cec_event(struct cec_adapter *adap, void cec_queue_pin_cec_event(struct cec_adapter *adap,
bool is_high, ktime_t ts); bool is_high, ktime_t ts);
/**
* cec_queue_pin_hpd_event() - queue a pin event with a given timestamp.
*
* @adap: pointer to the cec adapter
* @is_high: when true the HPD pin is high, otherwise it is low
* @ts: the timestamp for this event
*
*/
void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts);
/** /**
* cec_get_edid_phys_addr() - find and return the physical address * cec_get_edid_phys_addr() - find and return the physical address
* *
......
...@@ -410,6 +410,8 @@ struct cec_log_addrs { ...@@ -410,6 +410,8 @@ struct cec_log_addrs {
#define CEC_EVENT_LOST_MSGS 2 #define CEC_EVENT_LOST_MSGS 2
#define CEC_EVENT_PIN_CEC_LOW 3 #define CEC_EVENT_PIN_CEC_LOW 3
#define CEC_EVENT_PIN_CEC_HIGH 4 #define CEC_EVENT_PIN_CEC_HIGH 4
#define CEC_EVENT_PIN_HPD_LOW 5
#define CEC_EVENT_PIN_HPD_HIGH 6
#define CEC_EVENT_FL_INITIAL_STATE (1 << 0) #define CEC_EVENT_FL_INITIAL_STATE (1 << 0)
#define CEC_EVENT_FL_DROPPED_EVENTS (1 << 1) #define CEC_EVENT_FL_DROPPED_EVENTS (1 << 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment