From 6393e78183c01a13b371874617e5d230aa93af7c Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Tue, 2 Sep 2014 23:24:59 +0200 Subject: [PATCH 1/3] CEC re-patch --- drivers/mxc/hdmi-cec/mxc_hdmi-cec.c | 467 +++++++++++++--------------- drivers/mxc/hdmi-cec/mxc_hdmi-cec.h | 9 + drivers/video/mxc/mxc_hdmi.c | 3 +- 3 files changed, 235 insertions(+), 244 deletions(-) diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c index e53510f2170a..dc0cbf878076 100644 --- a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c +++ b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c @@ -46,28 +46,15 @@ #include "mxc_hdmi-cec.h" - -#define MAX_MESSAGE_LEN 17 - -#define MESSAGE_TYPE_RECEIVE_SUCCESS 1 -#define MESSAGE_TYPE_NOACK 2 -#define MESSAGE_TYPE_DISCONNECTED 3 -#define MESSAGE_TYPE_CONNECTED 4 -#define MESSAGE_TYPE_SEND_SUCCESS 5 - -#define CEC_TX_INPROGRESS -1 -#define CEC_TX_AVAIL 0 - struct hdmi_cec_priv { int receive_error; int send_error; u8 Logical_address; bool cec_state; + bool write_busy; u8 last_msg[MAX_MESSAGE_LEN]; u8 msg_len; - int tx_answer; - u16 latest_cec_stat; - u8 link_status; + u8 latest_cec_stat; spinlock_t irq_lock; struct delayed_work hdmi_cec_work; struct mutex lock; @@ -80,25 +67,20 @@ struct hdmi_cec_event { struct list_head list; }; - static LIST_HEAD(head); static int hdmi_cec_ready = 0; -static int hdmi_cec_started; static int hdmi_cec_major; static struct class *hdmi_cec_class; static struct hdmi_cec_priv hdmi_cec_data; static u8 open_count; static wait_queue_head_t hdmi_cec_queue; -static wait_queue_head_t tx_cec_queue; - static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data) { struct hdmi_cec_priv *hdmi_cec = data; - u16 cec_stat = 0; + u8 cec_stat = 0; unsigned long flags; - u8 phy_stat0; irqreturn_t ret = IRQ_HANDLED; spin_lock_irqsave(&hdmi_cec->irq_lock, flags); @@ -107,27 +89,19 @@ static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data) cec_stat = hdmi_readb(HDMI_IH_CEC_STAT0); hdmi_writeb(cec_stat, HDMI_IH_CEC_STAT0); - phy_stat0 = hdmi_readb(HDMI_PHY_STAT0) & 0x02; if ((cec_stat & (HDMI_IH_CEC_STAT0_ERROR_INIT | \ HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | \ HDMI_IH_CEC_STAT0_DONE)) == 0) { + + hdmi_cec->latest_cec_stat = 0; ret = IRQ_NONE; - cec_stat = 0; - } - if (hdmi_cec->link_status ^ phy_stat0) { - /* HPD value changed */ - hdmi_cec->link_status = phy_stat0; - if (hdmi_cec->link_status) - cec_stat |= 0x80; /* Connected */ - else - cec_stat |= 0x100; /* Disconnected */ + } else { + pr_debug("HDMI CEC interrupt received\n"); + hdmi_cec->latest_cec_stat = cec_stat; } - pr_debug("HDMI CEC interrupt received\n"); - hdmi_cec->latest_cec_stat = cec_stat ; schedule_delayed_work(&(hdmi_cec->hdmi_cec_work), msecs_to_jiffies(20)); - spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags); return ret; @@ -135,19 +109,58 @@ static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data) void mxc_hdmi_cec_handle(u16 cec_stat) { - u8 val = 0, i = 0; + u8 i = 0; struct hdmi_cec_event *event = NULL; - /*The current transmission is successful (for initiator only).*/ + if (!open_count) return; + /* The current transmission is successful (for initiator only).*/ if (cec_stat & HDMI_IH_CEC_STAT0_DONE) { - hdmi_cec_data.tx_answer = cec_stat; - wake_up(&tx_cec_queue); + + event = vmalloc(sizeof(struct hdmi_cec_event)); + if (NULL == event) { + pr_err("%s: Not enough memory!\n", __func__); + return; + } + memset(event, 0, sizeof(struct hdmi_cec_event)); + + mutex_lock(&hdmi_cec_data.lock); + event->msg_len = min((int)hdmi_cec_data.msg_len, 2); + for (i = 0; i < event->msg_len; i++) + event->msg[i] = hdmi_cec_data.last_msg[i]; + + event->event_type = MESSAGE_TYPE_SEND_SUCCESS; + + list_add_tail(&event->list, &head); + mutex_unlock(&hdmi_cec_data.lock); + + wake_up(&hdmi_cec_queue); } /*EOM is detected so that the received data is ready in the receiver data buffer*/ + if (cec_stat & HDMI_IH_CEC_STAT0_NACK) { + + event = vmalloc(sizeof(struct hdmi_cec_event)); + if (NULL == event) { + pr_err("%s: Not enough memory!\n", __func__); + return; + } + memset(event, 0, sizeof(struct hdmi_cec_event)); + + mutex_lock(&hdmi_cec_data.lock); + event->msg_len = min((int)hdmi_cec_data.msg_len, 2); + for (i = 0; i < event->msg_len; i++) + event->msg[i] = hdmi_cec_data.last_msg[i]; + + event->event_type = MESSAGE_TYPE_NOACK; + + list_add_tail(&event->list, &head); + mutex_unlock(&hdmi_cec_data.lock); + + wake_up(&hdmi_cec_queue); + } + if (cec_stat & HDMI_IH_CEC_STAT0_EOM) { - hdmi_writeb(0x02, HDMI_IH_CEC_STAT0); event = vmalloc(sizeof(struct hdmi_cec_event)); if (NULL == event) { pr_err("%s: Not enough memory!\n", __func__); @@ -170,64 +183,29 @@ void mxc_hdmi_cec_handle(u16 cec_stat) } /*An error is detected on cec line (for initiator only). */ if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_INIT) { - mutex_lock(&hdmi_cec_data.lock); hdmi_cec_data.send_error++; - if (hdmi_cec_data.send_error > 2) { - pr_err("%s:Re-transmission is attempted more than 2 times!\n", __func__); - hdmi_cec_data.send_error = 0; - mutex_unlock(&hdmi_cec_data.lock); - hdmi_cec_data.tx_answer = cec_stat; - wake_up(&tx_cec_queue); - return; - } - for (i = 0; i < hdmi_cec_data.msg_len; i++) - hdmi_writeb(hdmi_cec_data.last_msg[i], HDMI_CEC_TX_DATA0+i); - hdmi_writeb(hdmi_cec_data.msg_len, HDMI_CEC_TX_CNT); - val = hdmi_readb(HDMI_CEC_CTRL); - val |= 0x01; - hdmi_writeb(val, HDMI_CEC_CTRL); - mutex_unlock(&hdmi_cec_data.lock); - } - /*A frame is not acknowledged in a directly addressed message. Or a frame is negatively acknowledged in - a broadcast message (for initiator only).*/ - if (cec_stat & HDMI_IH_CEC_STAT0_NACK) { - hdmi_cec_data.tx_answer = cec_stat; - wake_up(&tx_cec_queue); } /*An error is notified by a follower. Abnormal logic data bit error (for follower).*/ if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_FOLL) { hdmi_cec_data.receive_error++; } - /*HDMI cable connected*/ - if (cec_stat & 0x80) { - pr_info("HDMI link connected\n"); - event = vmalloc(sizeof(struct hdmi_cec_event)); - if (NULL == event) { - pr_err("%s: Not enough memory\n", __func__); - return; - } - memset(event, 0, sizeof(struct hdmi_cec_event)); - event->event_type = MESSAGE_TYPE_CONNECTED; + /* HDMI cable connected / HDMI cable disconnected */ + if (cec_stat & (0x80 | 0x100)) { mutex_lock(&hdmi_cec_data.lock); - list_add_tail(&event->list, &head); + hdmi_cec_data.write_busy = (cec_stat & 0x80) ? false : true; mutex_unlock(&hdmi_cec_data.lock); - wake_up(&hdmi_cec_queue); - } - /*HDMI cable disconnected*/ - if (cec_stat & 0x100) { - pr_info("HDMI link disconnected\n"); event = vmalloc(sizeof(struct hdmi_cec_event)); if (NULL == event) { pr_err("%s: Not enough memory!\n", __func__); return; } memset(event, 0, sizeof(struct hdmi_cec_event)); - event->event_type = MESSAGE_TYPE_DISCONNECTED; - mutex_lock(&hdmi_cec_data.lock); + event->event_type = (cec_stat & 0x80) ? + MESSAGE_TYPE_CONNECTED : MESSAGE_TYPE_DISCONNECTED; list_add_tail(&event->list, &head); - mutex_unlock(&hdmi_cec_data.lock); wake_up(&hdmi_cec_queue); } + return; } EXPORT_SYMBOL(mxc_hdmi_cec_handle); @@ -262,153 +240,100 @@ static int hdmi_cec_open(struct inode *inode, struct file *filp) static ssize_t hdmi_cec_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct hdmi_cec_event *event = NULL; - pr_debug("function : %s\n", __func__); + struct hdmi_cec_priv *hdmi_cec = file->private_data; + int ret = 0; + pr_debug("function : %s\n", __func__); if (!open_count) return -ENODEV; - mutex_lock(&hdmi_cec_data.lock); - if (false == hdmi_cec_data.cec_state) { - mutex_unlock(&hdmi_cec_data.lock); - return -EACCES; - } - if (list_empty(&head)) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&hdmi_cec_data.lock); - return -EAGAIN; - } else { - do { - mutex_unlock(&hdmi_cec_data.lock); - if (wait_event_interruptible(hdmi_cec_queue, (!list_empty(&head)))) - return -ERESTARTSYS; - mutex_lock(&hdmi_cec_data.lock); - } while (list_empty(&head)); + count = min(count, sizeof(struct hdmi_cec_event) - sizeof(struct list_head)); + + do { + unsigned long flags; + struct hdmi_cec_event *event = NULL; + + spin_lock_irqsave(&hdmi_cec->irq_lock, flags); + if (!list_empty(&head)) { + event = list_first_entry(&head, struct hdmi_cec_event, list); + list_del(&event->list); } - } + spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags); - event = list_first_entry(&head, struct hdmi_cec_event, list); - list_del(&event->list); - mutex_unlock(&hdmi_cec_data.lock); - if (copy_to_user(buf, event, - sizeof(struct hdmi_cec_event) - sizeof(struct list_head))) { - vfree(event); - return -EFAULT; - } - vfree(event); - return (sizeof(struct hdmi_cec_event) - sizeof(struct list_head)); + if (event) { + ret = copy_to_user(buf, event, count) ? -EFAULT : count; + vfree(event); + } + else if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + } + else if (wait_event_interruptible(hdmi_cec_queue, (!list_empty(&head)))) { + ret = -ERESTARTSYS; + } + } while(!ret); + + return ret; } static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct hdmi_cec_priv *hdmi_cec = file->private_data; int ret = 0 , i = 0; u8 msg[MAX_MESSAGE_LEN]; - u8 msg_len = 0, val = 0; + u8 val = 0; pr_debug("function : %s\n", __func__); - if (!open_count) return -ENODEV; - mutex_lock(&hdmi_cec_data.lock); - if (false == hdmi_cec_data.cec_state) { - mutex_unlock(&hdmi_cec_data.lock); - return -EACCES; - } - /* Ensure that there is only one writer who is the only listener of tx_cec_queue */ - if (hdmi_cec_data.tx_answer != CEC_TX_AVAIL) { - mutex_unlock(&hdmi_cec_data.lock); - return -EBUSY; - } - mutex_unlock(&hdmi_cec_data.lock); - if (count > MAX_MESSAGE_LEN) - return -EINVAL; - memset(&msg, 0, MAX_MESSAGE_LEN); - ret = copy_from_user(&msg, buf, count); - if (ret) - return -EACCES; - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.send_error = 0; - hdmi_cec_data.tx_answer = CEC_TX_INPROGRESS; - msg_len = count; - hdmi_writeb(msg_len, HDMI_CEC_TX_CNT); - for (i = 0; i < msg_len; i++) - hdmi_writeb(msg[i], HDMI_CEC_TX_DATA0+i); - val = hdmi_readb(HDMI_CEC_CTRL); - val |= 0x01; - hdmi_writeb(val, HDMI_CEC_CTRL); - memcpy(hdmi_cec_data.last_msg, msg, msg_len); - hdmi_cec_data.msg_len = msg_len; - mutex_unlock(&hdmi_cec_data.lock); - - ret = wait_event_interruptible_timeout(tx_cec_queue, hdmi_cec_data.tx_answer != CEC_TX_INPROGRESS, HZ); - - if (ret < 0) { - ret = -ERESTARTSYS; - goto tx_out; - } - - if (hdmi_cec_data.tx_answer & HDMI_IH_CEC_STAT0_DONE) - /* msg correctly sent */ - ret = msg_len; - else - ret = -EIO; - - tx_out: - hdmi_cec_data.tx_answer = CEC_TX_AVAIL; - return ret; -} - -void hdmi_cec_start_device(void) -{ - u8 val; - if (!hdmi_cec_ready || hdmi_cec_started) - return; + if (count > MAX_MESSAGE_LEN) + return -E2BIG; - val = hdmi_readb(HDMI_MC_CLKDIS); - val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; - hdmi_writeb(val, HDMI_MC_CLKDIS); - hdmi_writeb(0x02, HDMI_CEC_CTRL); - /* Force read unlock */ - hdmi_writeb(0x0, HDMI_CEC_LOCK); - val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; - hdmi_writeb(val, HDMI_CEC_POLARITY); - val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ARB_LOST; - hdmi_writeb(val, HDMI_CEC_MASK); - hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); - hdmi_cec_data.link_status = hdmi_readb(HDMI_PHY_STAT0) & 0x02; - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.cec_state = true; - mutex_unlock(&hdmi_cec_data.lock); + memset(&msg, 0, MAX_MESSAGE_LEN); + if (copy_from_user(&msg, buf, count)) + return -EFAULT; - hdmi_cec_started = 1; -} -EXPORT_SYMBOL(hdmi_cec_start_device); + do { + if (file->f_flags & O_NONBLOCK) { + if (hdmi_cec->write_busy) + ret = -EAGAIN; + } else if (wait_event_interruptible(hdmi_cec_queue, (!hdmi_cec->write_busy))) { + ret = -ERESTARTSYS; + } + if (ret) + break; -void hdmi_cec_stop_device(void) -{ - u8 val; + mutex_lock(&hdmi_cec->lock); + hdmi_cec->write_busy = true; - if (!hdmi_cec_ready || !hdmi_cec_started) - return; + hdmi_cec->send_error = 0; + hdmi_writeb(count, HDMI_CEC_TX_CNT); + for (i = 0; i < count; i++) { + hdmi_writeb(msg[i], HDMI_CEC_TX_DATA0+i); + } - hdmi_writeb(0x10, HDMI_CEC_CTRL); - val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST | \ - HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; - hdmi_writeb(val, HDMI_CEC_MASK); - hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); - hdmi_writeb(0x0, HDMI_CEC_POLARITY); - val = hdmi_readb(HDMI_MC_CLKDIS); - val |= HDMI_MC_CLKDIS_CECCLK_DISABLE; - hdmi_writeb(val, HDMI_MC_CLKDIS); - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.cec_state = false; - mutex_unlock(&hdmi_cec_data.lock); + val = hdmi_readb(HDMI_CEC_CTRL); + val |= 0x01; + hdmi_writeb(val, HDMI_CEC_CTRL); + memcpy(hdmi_cec->last_msg, msg, count); + hdmi_cec->msg_len = count; + + for (i = 0; i++ < 300 && (hdmi_readb(HDMI_CEC_CTRL) & 0x01) && !hdmi_cec->send_error; msleep(10)); + if (hdmi_readb(HDMI_CEC_CTRL) & 0x01 || hdmi_cec->send_error) { + hdmi_cec->msg_len = 0; + hdmi_cec->send_error = 0; + hdmi_writeb(0x02, HDMI_CEC_CTRL); + ret = -EIO; + } else { + ret = count; + } + } while(!ret); - hdmi_cec_started = 0; + hdmi_cec->write_busy = false; + mutex_unlock(&hdmi_cec->lock); + return ret; } -EXPORT_SYMBOL(hdmi_cec_stop_device); /*! * @brief IO ctrl function for vpu file operation @@ -418,12 +343,14 @@ EXPORT_SYMBOL(hdmi_cec_stop_device); static long hdmi_cec_ioctl(struct file *filp, u_int cmd, u_long arg) { - int ret = 0, status = 0; - u8 val = 0, msg = 0; + int ret = 0; + u8 val = 0; struct mxc_edid_cfg hdmi_edid_cfg; + pr_debug("function : %s\n", __func__); if (!open_count) return -ENODEV; + switch (cmd) { case HDMICEC_IOC_SETLOGICALADDRESS: mutex_lock(&hdmi_cec_data.lock); @@ -432,63 +359,120 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, pr_err("Trying to set logical address while not started\n"); return -EACCES; } + hdmi_cec_data.Logical_address = (u8)arg; + if (hdmi_cec_data.Logical_address <= 7) { val = 1 << hdmi_cec_data.Logical_address; hdmi_writeb(val, HDMI_CEC_ADDR_L); hdmi_writeb(0, HDMI_CEC_ADDR_H); - } else if (hdmi_cec_data.Logical_address > 7 && hdmi_cec_data.Logical_address <= 15) { + } else if (hdmi_cec_data.Logical_address <= 15) { val = 1 << (hdmi_cec_data.Logical_address - 8); hdmi_writeb(val, HDMI_CEC_ADDR_H); hdmi_writeb(0, HDMI_CEC_ADDR_L); } else ret = -EINVAL; - /*Send Polling message with same source and destination address*/ - if (0 == ret && 15 != hdmi_cec_data.Logical_address) { - msg = (hdmi_cec_data.Logical_address << 4)|hdmi_cec_data.Logical_address; - hdmi_writeb(1, HDMI_CEC_TX_CNT); - hdmi_writeb(msg, HDMI_CEC_TX_DATA0); - val = hdmi_readb(HDMI_CEC_CTRL); - val |= 0x01; - hdmi_writeb(val, HDMI_CEC_CTRL); - } + mutex_unlock(&hdmi_cec_data.lock); break; + case HDMICEC_IOC_STARTDEVICE: - hdmi_cec_start_device(); + val = hdmi_readb(HDMI_MC_CLKDIS); + val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; + hdmi_writeb(val, HDMI_MC_CLKDIS); + + val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | + HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_DONE; + hdmi_writeb(val, HDMI_CEC_POLARITY); + + val = HDMI_IH_CEC_STAT0_WAKEUP | + HDMI_IH_CEC_STAT0_ARB_LOST; + hdmi_writeb(val, HDMI_CEC_MASK); + hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); + + hdmi_writeb(0x02, HDMI_CEC_CTRL); + hdmi_writeb(0x0, HDMI_CEC_LOCK); + + mutex_lock(&hdmi_cec_data.lock); + hdmi_cec_data.cec_state = true; + mutex_unlock(&hdmi_cec_data.lock); break; + case HDMICEC_IOC_STOPDEVICE: - hdmi_cec_stop_device(); + val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | + HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST | + HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | + HDMI_IH_CEC_STAT0_DONE; + hdmi_writeb(val, HDMI_CEC_MASK); + hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); + + hdmi_writeb(0x0, HDMI_CEC_POLARITY); + + hdmi_writeb(0x10, HDMI_CEC_CTRL); + + val = hdmi_readb(HDMI_MC_CLKDIS); + val |= HDMI_MC_CLKDIS_CECCLK_DISABLE; + hdmi_writeb(val, HDMI_MC_CLKDIS); + + mutex_lock(&hdmi_cec_data.lock); + hdmi_cec_data.cec_state = false; + mutex_unlock(&hdmi_cec_data.lock); break; + case HDMICEC_IOC_GETPHYADDRESS: hdmi_get_edid_cfg(&hdmi_edid_cfg); - status = copy_to_user((void __user *)arg, + ret = copy_to_user((void __user *)arg, &hdmi_edid_cfg.physical_address, - 4*sizeof(u8)); - if (status) - ret = -EFAULT; + 4*sizeof(u8))?-EFAULT:0; break; + default: ret = -EINVAL; break; } - return ret; + + return ret; +} + +void hdmi_cec_start_device(void) +{ + hdmi_cec_ready = 1; + hdmi_cec_ioctl(0, HDMICEC_IOC_STARTDEVICE, 0); } +EXPORT_SYMBOL(hdmi_cec_start_device); + +void hdmi_cec_stop_device(void) +{ + hdmi_cec_ioctl(0, HDMICEC_IOC_STOPDEVICE, 0); + hdmi_cec_ready = 0; +} +EXPORT_SYMBOL(hdmi_cec_stop_device); /*! - * @brief Release function for vpu file operation - * @return 0 on success or negative error code on error - */ +* @brief Release function for vpu file operation +* @return 0 on success or negative error code on error +*/ static int hdmi_cec_release(struct inode *inode, struct file *filp) { + if (hdmi_cec_data.cec_state) { + hdmi_cec_stop_device(); + hdmi_cec_ioctl(filp, HDMICEC_IOC_SETLOGICALADDRESS, 15); + } mutex_lock(&hdmi_cec_data.lock); if (open_count) { + if (!(wait_event_timeout(hdmi_cec_queue, !hdmi_cec_data.write_busy, msecs_to_jiffies(500)))) + hdmi_cec_data.write_busy = false; + + while (!list_empty(&head)) { + struct hdmi_cec_event *event; + + event = list_first_entry(&head, struct hdmi_cec_event, list); + list_del(&event->list); + vfree(event); + } open_count = 0; - hdmi_cec_data.cec_state = false; - hdmi_cec_data.Logical_address = 15; } mutex_unlock(&hdmi_cec_data.lock); - return 0; } @@ -500,16 +484,14 @@ static unsigned int hdmi_cec_poll(struct file *file, poll_table *wait) poll_wait(file, &hdmi_cec_queue, wait); - mutex_lock(&hdmi_cec_data.lock); - if (hdmi_cec_data.tx_answer == CEC_TX_AVAIL) - mask = (POLLOUT | POLLWRNORM); + if (!hdmi_cec_data.write_busy) + mask = (POLLOUT | POLLWRNORM); if (!list_empty(&head)) - mask |= (POLLIN | POLLRDNORM); - mutex_unlock(&hdmi_cec_data.lock); + mask |= (POLLIN | POLLRDNORM); + return mask; } - const struct file_operations hdmi_cec_fops = { .owner = THIS_MODULE, .read = hdmi_cec_read, @@ -534,7 +516,7 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) err = -EBUSY; goto out; } - + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (unlikely(res == NULL)) { dev_err(&pdev->dev, "hdmi_cec:No HDMI irq line provided\n"); @@ -555,8 +537,8 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) goto err_out_chrdev; } - temp_class = device_create(hdmi_cec_class, NULL, MKDEV(hdmi_cec_major, 0), - NULL, "mxc_hdmi_cec"); + temp_class = device_create(hdmi_cec_class, NULL, + MKDEV(hdmi_cec_major, 0), NULL, "mxc_hdmi_cec"); if (IS_ERR(temp_class)) { err = PTR_ERR(temp_class); goto err_out_class; @@ -569,13 +551,11 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) } init_waitqueue_head(&hdmi_cec_queue); - init_waitqueue_head(&tx_cec_queue); INIT_LIST_HEAD(&head); mutex_init(&hdmi_cec_data.lock); hdmi_cec_data.Logical_address = 15; - hdmi_cec_data.tx_answer = CEC_TX_AVAIL; platform_set_drvdata(pdev, &hdmi_cec_data); INIT_DELAYED_WORK(&hdmi_cec_data.hdmi_cec_work, mxc_hdmi_cec_worker); @@ -594,14 +574,15 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) static int hdmi_cec_dev_remove(struct platform_device *pdev) { - if (hdmi_cec_data.cec_state) - hdmi_cec_stop_device(); + if (open_count) + hdmi_cec_release(0, 0); + if (hdmi_cec_major > 0) { device_destroy(hdmi_cec_class, MKDEV(hdmi_cec_major, 0)); class_destroy(hdmi_cec_class); unregister_chrdev(hdmi_cec_major, "mxc_hdmi_cec"); hdmi_cec_major = 0; -} + } return 0; } @@ -615,9 +596,9 @@ static struct platform_driver mxc_hdmi_cec_driver = { .probe = hdmi_cec_dev_probe, .remove = hdmi_cec_dev_remove, .driver = { - .name = "mxc_hdmi_cec", + .name = "mxc_hdmi_cec", .of_match_table = imx_hdmi_cec_match, - }, + }, }; module_platform_driver(mxc_hdmi_cec_driver); diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.h b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.h index 4437057f5bce..297f62847005 100644 --- a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.h +++ b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.h @@ -35,4 +35,13 @@ #define HDMICEC_IOC_GETPHYADDRESS \ _IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4]) +#define MAX_MESSAGE_LEN 16 + +#define MESSAGE_TYPE_RECEIVE_SUCCESS 1 +#define MESSAGE_TYPE_NOACK 2 +#define MESSAGE_TYPE_DISCONNECTED 3 +#define MESSAGE_TYPE_CONNECTED 4 +#define MESSAGE_TYPE_SEND_SUCCESS 5 + #endif /* !_HDMICEC_H_ */ + diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 0aac1e036ef4..174342d69afa 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -2074,12 +2074,13 @@ static void hotplug_worker(struct work_struct *work) dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n"); mxc_hdmi_cable_connected(hdmi); + hdmi_set_cable_state(1); + sprintf(event_string, "EVENT=plugin"); kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); #ifdef CONFIG_MXC_HDMI_CEC mxc_hdmi_cec_handle(0x80); #endif - hdmi_set_cable_state(1); } else { /* Plugout event */ dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n"); From 41d607361825f5a9d628cfa8c457114c4f8bce4d Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Tue, 2 Sep 2014 23:24:59 +0200 Subject: [PATCH 2/3] CEC re-patch rm unused varx --- drivers/mfd/mxc-hdmi-core.c | 2 + drivers/mxc/hdmi-cec/mxc_hdmi-cec.c | 165 ++++++++++++++++------------ 2 files changed, 96 insertions(+), 71 deletions(-) diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c index 7300ed61495e..fa97b6d5c3af 100644 --- a/drivers/mfd/mxc-hdmi-core.c +++ b/drivers/mfd/mxc-hdmi-core.c @@ -72,9 +72,11 @@ void hdmi_set_dvi_mode(unsigned int state) { if (state) { mxc_hdmi_abort_stream(); +#ifdef CONFIG_MXC_HDMI_CEC hdmi_cec_stop_device(); } else { hdmi_cec_start_device(); +#ndif } } EXPORT_SYMBOL(hdmi_set_dvi_mode); diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c index dc0cbf878076..7da08a30a9bd 100644 --- a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c +++ b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c @@ -137,7 +137,8 @@ void mxc_hdmi_cec_handle(u16 cec_stat) wake_up(&hdmi_cec_queue); } - /*EOM is detected so that the received data is ready in the receiver data buffer*/ + /*A frame is not acknowledged in a directly addressed message. Or a frame is negatively acknowledged in + a broadcast message (for initiator only).*/ if (cec_stat & HDMI_IH_CEC_STAT0_NACK) { event = vmalloc(sizeof(struct hdmi_cec_event)); @@ -159,7 +160,7 @@ void mxc_hdmi_cec_handle(u16 cec_stat) wake_up(&hdmi_cec_queue); } - + /*EOM is detected so that the received data is ready in the receiver data buffer*/ if (cec_stat & HDMI_IH_CEC_STAT0_EOM) { event = vmalloc(sizeof(struct hdmi_cec_event)); if (NULL == event) { @@ -294,19 +295,14 @@ static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, if (copy_from_user(&msg, buf, count)) return -EFAULT; - do { - if (file->f_flags & O_NONBLOCK) { - if (hdmi_cec->write_busy) - ret = -EAGAIN; - } else if (wait_event_interruptible(hdmi_cec_queue, (!hdmi_cec->write_busy))) { - ret = -ERESTARTSYS; - } - if (ret) - break; - - mutex_lock(&hdmi_cec->lock); - hdmi_cec->write_busy = true; + if (file->f_flags & O_NONBLOCK && hdmi_cec->write_busy) + return -EAGAIN; + else if (wait_event_interruptible(hdmi_cec_queue, (!hdmi_cec->write_busy))) + return -ERESTARTSYS; + mutex_lock(&hdmi_cec->lock); + hdmi_cec->write_busy = true; + do { hdmi_cec->send_error = 0; hdmi_writeb(count, HDMI_CEC_TX_CNT); for (i = 0; i < count; i++) { @@ -335,6 +331,80 @@ static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, return ret; } +static void hdmi_cec_hwenable(void) +{ + u8 val; + + if (!hdmi_cec_ready || hdmi_cec_data.cec_state) + return; + + val = hdmi_readb(HDMI_MC_CLKDIS); + val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; + hdmi_writeb(val, HDMI_MC_CLKDIS); + + val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | + HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_DONE; + hdmi_writeb(val, HDMI_CEC_POLARITY); + + val = HDMI_IH_CEC_STAT0_WAKEUP | + HDMI_IH_CEC_STAT0_ARB_LOST; + hdmi_writeb(val, HDMI_CEC_MASK); + hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); + + hdmi_writeb(0x02, HDMI_CEC_CTRL); + hdmi_writeb(0x0, HDMI_CEC_LOCK); + + mutex_lock(&hdmi_cec_data.lock); + hdmi_cec_data.cec_state = true; + mutex_unlock(&hdmi_cec_data.lock); +} + +static void hdmi_cec_hwdisable(void) +{ + u8 val; + + if(!hdmi_cec_ready || !hdmi_cec_data.cec_state) + return; + + val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | + HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST | + HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | + HDMI_IH_CEC_STAT0_DONE; + hdmi_writeb(val, HDMI_CEC_MASK); + hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); + + hdmi_writeb(0x0, HDMI_CEC_POLARITY); + + hdmi_writeb(0x10, HDMI_CEC_CTRL); + + val = hdmi_readb(HDMI_MC_CLKDIS); + val |= HDMI_MC_CLKDIS_CECCLK_DISABLE; + hdmi_writeb(val, HDMI_MC_CLKDIS); + + mutex_lock(&hdmi_cec_data.lock); + hdmi_cec_data.cec_state = false; + mutex_unlock(&hdmi_cec_data.lock); +} + +static long hdmi_cec_set_address(u_long arg) +{ + u8 val; + + if (arg <= 7) { + val = 1 << (u8)arg; + hdmi_writeb(val, HDMI_CEC_ADDR_L); + hdmi_writeb(0, HDMI_CEC_ADDR_H); + } else if (arg <= 15) { + val = 1 << ((u8)arg - 8); + hdmi_writeb(val, HDMI_CEC_ADDR_H); + hdmi_writeb(0, HDMI_CEC_ADDR_L); + } else + return -EINVAL; + + hdmi_cec_data.Logical_address = (u8)arg; + return 0; +} + /*! * @brief IO ctrl function for vpu file operation * @param cmd IO ctrl command @@ -344,7 +414,6 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, u_long arg) { int ret = 0; - u8 val = 0; struct mxc_edid_cfg hdmi_edid_cfg; pr_debug("function : %s\n", __func__); @@ -360,63 +429,16 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, return -EACCES; } - hdmi_cec_data.Logical_address = (u8)arg; - - if (hdmi_cec_data.Logical_address <= 7) { - val = 1 << hdmi_cec_data.Logical_address; - hdmi_writeb(val, HDMI_CEC_ADDR_L); - hdmi_writeb(0, HDMI_CEC_ADDR_H); - } else if (hdmi_cec_data.Logical_address <= 15) { - val = 1 << (hdmi_cec_data.Logical_address - 8); - hdmi_writeb(val, HDMI_CEC_ADDR_H); - hdmi_writeb(0, HDMI_CEC_ADDR_L); - } else - ret = -EINVAL; - + hdmi_cec_set_address(arg); mutex_unlock(&hdmi_cec_data.lock); break; case HDMICEC_IOC_STARTDEVICE: - val = hdmi_readb(HDMI_MC_CLKDIS); - val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; - hdmi_writeb(val, HDMI_MC_CLKDIS); - - val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | - HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_DONE; - hdmi_writeb(val, HDMI_CEC_POLARITY); - - val = HDMI_IH_CEC_STAT0_WAKEUP | - HDMI_IH_CEC_STAT0_ARB_LOST; - hdmi_writeb(val, HDMI_CEC_MASK); - hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); - - hdmi_writeb(0x02, HDMI_CEC_CTRL); - hdmi_writeb(0x0, HDMI_CEC_LOCK); - - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.cec_state = true; - mutex_unlock(&hdmi_cec_data.lock); + hdmi_cec_hwenable(); break; case HDMICEC_IOC_STOPDEVICE: - val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | - HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST | - HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | - HDMI_IH_CEC_STAT0_DONE; - hdmi_writeb(val, HDMI_CEC_MASK); - hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); - - hdmi_writeb(0x0, HDMI_CEC_POLARITY); - - hdmi_writeb(0x10, HDMI_CEC_CTRL); - - val = hdmi_readb(HDMI_MC_CLKDIS); - val |= HDMI_MC_CLKDIS_CECCLK_DISABLE; - hdmi_writeb(val, HDMI_MC_CLKDIS); - - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.cec_state = false; - mutex_unlock(&hdmi_cec_data.lock); + hdmi_cec_hwdisable(); break; case HDMICEC_IOC_GETPHYADDRESS: @@ -437,13 +459,15 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, void hdmi_cec_start_device(void) { hdmi_cec_ready = 1; - hdmi_cec_ioctl(0, HDMICEC_IOC_STARTDEVICE, 0); + if(open_count) + hdmi_cec_hwenable(); } EXPORT_SYMBOL(hdmi_cec_start_device); void hdmi_cec_stop_device(void) { - hdmi_cec_ioctl(0, HDMICEC_IOC_STOPDEVICE, 0); + if(open_count) + hdmi_cec_hwenable(); hdmi_cec_ready = 0; } EXPORT_SYMBOL(hdmi_cec_stop_device); @@ -454,12 +478,10 @@ EXPORT_SYMBOL(hdmi_cec_stop_device); */ static int hdmi_cec_release(struct inode *inode, struct file *filp) { - if (hdmi_cec_data.cec_state) { - hdmi_cec_stop_device(); - hdmi_cec_ioctl(filp, HDMICEC_IOC_SETLOGICALADDRESS, 15); - } + hdmi_cec_hwdisable(); mutex_lock(&hdmi_cec_data.lock); if (open_count) { + hdmi_cec_set_address(15); if (!(wait_event_timeout(hdmi_cec_queue, !hdmi_cec_data.write_busy, msecs_to_jiffies(500)))) hdmi_cec_data.write_busy = false; @@ -556,6 +578,7 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) mutex_init(&hdmi_cec_data.lock); hdmi_cec_data.Logical_address = 15; + hdmi_cec_data.cec_state = false; platform_set_drvdata(pdev, &hdmi_cec_data); INIT_DELAYED_WORK(&hdmi_cec_data.hdmi_cec_work, mxc_hdmi_cec_worker); From 306d035f44733360bbf7772aaacf03dabc60ceed Mon Sep 17 00:00:00 2001 From: Matus Kral Date: Sun, 23 Nov 2014 17:08:59 +0100 Subject: [PATCH 3/3] hdmi_cec event struct change (plan is to allow shared open mode on cecdev for concurent client access) re-transmitting 5x in case of error indication on the line reworked locking and process to avoid disrupts by hdmi interrupts (or not to cause any) --- drivers/mxc/hdmi-cec/mxc_hdmi-cec.c | 307 ++++++++++++++-------------- drivers/video/mxc/mxc_hdmi.c | 3 +- 2 files changed, 157 insertions(+), 153 deletions(-) diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c index 7da08a30a9bd..f2e0dd895cd8 100644 --- a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c +++ b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c @@ -19,6 +19,8 @@ * @ingroup HDMI */ +#define DEBUG + #include #include #include @@ -58,16 +60,19 @@ struct hdmi_cec_priv { spinlock_t irq_lock; struct delayed_work hdmi_cec_work; struct mutex lock; + struct list_head head; }; struct hdmi_cec_event { - int event_type; - int msg_len; + u8 event_type; + u8 msg_len; u8 msg[MAX_MESSAGE_LEN]; - struct list_head list; }; -static LIST_HEAD(head); +struct hdmi_cec_event_list { + struct hdmi_cec_event data; + struct list_head ptr; +}; static int hdmi_cec_ready = 0; static int hdmi_cec_major; @@ -76,6 +81,7 @@ static struct hdmi_cec_priv hdmi_cec_data; static u8 open_count; static wait_queue_head_t hdmi_cec_queue; +static wait_queue_head_t hdmi_cec_sent; static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data) { struct hdmi_cec_priv *hdmi_cec = data; @@ -83,6 +89,8 @@ static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data) unsigned long flags; irqreturn_t ret = IRQ_HANDLED; + pr_debug("function : %s\n", __func__); + spin_lock_irqsave(&hdmi_cec->irq_lock, flags); hdmi_writeb(0x7f, HDMI_IH_MUTE_CEC_STAT0); @@ -90,15 +98,16 @@ static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data) cec_stat = hdmi_readb(HDMI_IH_CEC_STAT0); hdmi_writeb(cec_stat, HDMI_IH_CEC_STAT0); - if ((cec_stat & (HDMI_IH_CEC_STAT0_ERROR_INIT | \ - HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | \ - HDMI_IH_CEC_STAT0_DONE)) == 0) { + hdmi_cec->latest_cec_stat = cec_stat; + if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_INIT) { + hdmi_cec->send_error++; + + } else if (cec_stat & (HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_DONE)) { + hdmi_cec->send_error = 0; + } else if (!(cec_stat & HDMI_IH_CEC_STAT0_EOM)) { hdmi_cec->latest_cec_stat = 0; ret = IRQ_NONE; - } else { - pr_debug("HDMI CEC interrupt received\n"); - hdmi_cec->latest_cec_stat = cec_stat; } schedule_delayed_work(&(hdmi_cec->hdmi_cec_work), msecs_to_jiffies(20)); @@ -109,113 +118,107 @@ static irqreturn_t mxc_hdmi_cec_isr(int irq, void *data) void mxc_hdmi_cec_handle(u16 cec_stat) { - u8 i = 0; - struct hdmi_cec_event *event = NULL; + struct hdmi_cec_event_list *event = NULL; + unsigned long flags; - if (!open_count) + /* HDMI cable connected / HDMI cable disconnected */ + if (!open_count || !hdmi_cec_ready || !(cec_stat & (0x80 | 0x100))) return; - /* The current transmission is successful (for initiator only).*/ - if (cec_stat & HDMI_IH_CEC_STAT0_DONE) { + pr_debug("function : %s\n", __func__); - event = vmalloc(sizeof(struct hdmi_cec_event)); - if (NULL == event) { - pr_err("%s: Not enough memory!\n", __func__); - return; - } - memset(event, 0, sizeof(struct hdmi_cec_event)); + event = kzalloc(sizeof(struct hdmi_cec_event), GFP_KERNEL); + if (NULL == event) { + pr_err("%s: Not enough memory!\n", __func__); + return; + } + event->data.event_type = (cec_stat & 0x80) ? + MESSAGE_TYPE_CONNECTED : MESSAGE_TYPE_DISCONNECTED; - mutex_lock(&hdmi_cec_data.lock); - event->msg_len = min((int)hdmi_cec_data.msg_len, 2); - for (i = 0; i < event->msg_len; i++) - event->msg[i] = hdmi_cec_data.last_msg[i]; + spin_lock_irqsave(&hdmi_cec_data.irq_lock, flags); + list_add_tail(&event->ptr, &hdmi_cec_data.head); + spin_unlock_irqrestore(&hdmi_cec_data.irq_lock, flags); + wake_up(&hdmi_cec_queue); +} +EXPORT_SYMBOL(mxc_hdmi_cec_handle); - event->event_type = MESSAGE_TYPE_SEND_SUCCESS; +void mxc_hdmi_cec_msg(int event_type, struct hdmi_cec_priv *hdmi_cec) +{ + struct hdmi_cec_event_list *event = NULL; + unsigned long flags; + u8 i; - list_add_tail(&event->list, &head); - mutex_unlock(&hdmi_cec_data.lock); + pr_debug("function : %s\n", __func__); - wake_up(&hdmi_cec_queue); + event = kzalloc(sizeof(struct hdmi_cec_event), GFP_KERNEL); + if (NULL == event) { + pr_err("%s: Not enough memory!\n", __func__); + return; } - /*A frame is not acknowledged in a directly addressed message. Or a frame is negatively acknowledged in - a broadcast message (for initiator only).*/ - if (cec_stat & HDMI_IH_CEC_STAT0_NACK) { + event->data.event_type = event_type; - event = vmalloc(sizeof(struct hdmi_cec_event)); - if (NULL == event) { - pr_err("%s: Not enough memory!\n", __func__); + if (event_type == MESSAGE_TYPE_RECEIVE_SUCCESS) { + event->data.msg_len = hdmi_readb(HDMI_CEC_RX_CNT); + if (!event->data.msg_len) { + pr_err("%s: Bad message size!\n", __func__); return; } - memset(event, 0, sizeof(struct hdmi_cec_event)); + } else + event->data.msg_len = min((int)hdmi_cec->msg_len, 2); - mutex_lock(&hdmi_cec_data.lock); - event->msg_len = min((int)hdmi_cec_data.msg_len, 2); - for (i = 0; i < event->msg_len; i++) - event->msg[i] = hdmi_cec_data.last_msg[i]; + for (i = 0; i < event->data.msg_len; i++) + event->data.msg[i] = (event_type == MESSAGE_TYPE_RECEIVE_SUCCESS) ? + hdmi_readb(HDMI_CEC_RX_DATA0+i) : hdmi_cec->last_msg[i]; - event->event_type = MESSAGE_TYPE_NOACK; + if (event_type & (MESSAGE_TYPE_NOACK | MESSAGE_TYPE_SEND_SUCCESS)) { + wake_up(&hdmi_cec_sent); + mutex_lock(&hdmi_cec->lock); + hdmi_cec->write_busy = false; + mutex_unlock(&hdmi_cec->lock); + } - list_add_tail(&event->list, &head); - mutex_unlock(&hdmi_cec_data.lock); + spin_lock_irqsave(&hdmi_cec->irq_lock, flags); + list_add_tail(&event->ptr, &hdmi_cec->head); + spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags); - wake_up(&hdmi_cec_queue); - } - /*EOM is detected so that the received data is ready in the receiver data buffer*/ - if (cec_stat & HDMI_IH_CEC_STAT0_EOM) { - event = vmalloc(sizeof(struct hdmi_cec_event)); - if (NULL == event) { - pr_err("%s: Not enough memory!\n", __func__); - return; + pr_debug("function : %s write_busy: %d\n", __func__, hdmi_cec->write_busy); + wake_up(&hdmi_cec_queue); +} + +static void mxc_hdmi_cec_worker(struct work_struct *work) +{ + u8 val; + struct delayed_work *delay_work = to_delayed_work(work); + struct hdmi_cec_priv *hdmi_cec = + container_of(delay_work, struct hdmi_cec_priv, hdmi_cec_work); + unsigned long flags; + + pr_debug("function : %s\n", __func__); + if (hdmi_cec->latest_cec_stat && open_count) { + /* The current transmission is successful (for initiator only).*/ + if (hdmi_cec->latest_cec_stat & HDMI_IH_CEC_STAT0_DONE) { + mxc_hdmi_cec_msg(MESSAGE_TYPE_SEND_SUCCESS, hdmi_cec); } - memset(event, 0, sizeof(struct hdmi_cec_event)); - event->msg_len = hdmi_readb(HDMI_CEC_RX_CNT); - if (!event->msg_len) { - pr_err("%s: Invalid CEC message length!\n", __func__); - return; + /*A frame is not acknowledged in a directly addressed message. Or a frame is negatively acknowledged in + a broadcast message (for initiator only).*/ + if (hdmi_cec->latest_cec_stat & HDMI_IH_CEC_STAT0_NACK) { + mxc_hdmi_cec_msg(MESSAGE_TYPE_NOACK, hdmi_cec); } - event->event_type = MESSAGE_TYPE_RECEIVE_SUCCESS; - for (i = 0; i < event->msg_len; i++) - event->msg[i] = hdmi_readb(HDMI_CEC_RX_DATA0+i); - hdmi_writeb(0x0, HDMI_CEC_LOCK); - mutex_lock(&hdmi_cec_data.lock); - list_add_tail(&event->list, &head); - mutex_unlock(&hdmi_cec_data.lock); - wake_up(&hdmi_cec_queue); - } - /*An error is detected on cec line (for initiator only). */ - if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_INIT) { - hdmi_cec_data.send_error++; - } - /*An error is notified by a follower. Abnormal logic data bit error (for follower).*/ - if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_FOLL) { - hdmi_cec_data.receive_error++; - } - /* HDMI cable connected / HDMI cable disconnected */ - if (cec_stat & (0x80 | 0x100)) { - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.write_busy = (cec_stat & 0x80) ? false : true; - mutex_unlock(&hdmi_cec_data.lock); - event = vmalloc(sizeof(struct hdmi_cec_event)); - if (NULL == event) { - pr_err("%s: Not enough memory!\n", __func__); - return; + /*EOM is detected so that the received data is ready in the receiver data buffer*/ + if (hdmi_cec->latest_cec_stat & HDMI_IH_CEC_STAT0_EOM) { + mxc_hdmi_cec_msg(MESSAGE_TYPE_RECEIVE_SUCCESS, hdmi_cec); + hdmi_writeb(0x0, HDMI_CEC_LOCK); + } + /*An error is notified by a follower. Abnormal logic data bit error (for follower).*/ + if (hdmi_cec->latest_cec_stat & HDMI_IH_CEC_STAT0_ERROR_FOLL) { + hdmi_cec->receive_error++; } - memset(event, 0, sizeof(struct hdmi_cec_event)); - event->event_type = (cec_stat & 0x80) ? - MESSAGE_TYPE_CONNECTED : MESSAGE_TYPE_DISCONNECTED; - list_add_tail(&event->list, &head); - wake_up(&hdmi_cec_queue); } - return; -} -EXPORT_SYMBOL(mxc_hdmi_cec_handle); -static void mxc_hdmi_cec_worker(struct work_struct *work) -{ - u8 val; - mxc_hdmi_cec_handle(hdmi_cec_data.latest_cec_stat); val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ARB_LOST; + spin_lock_irqsave(&hdmi_cec->irq_lock, flags); hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); + spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags); } /*! @@ -225,6 +228,8 @@ static void mxc_hdmi_cec_worker(struct work_struct *work) */ static int hdmi_cec_open(struct inode *inode, struct file *filp) { + pr_debug("function : %s\n", __func__); + mutex_lock(&hdmi_cec_data.lock); if (open_count) { mutex_unlock(&hdmi_cec_data.lock); @@ -234,6 +239,7 @@ static int hdmi_cec_open(struct inode *inode, struct file *filp) filp->private_data = (void *)(&hdmi_cec_data); hdmi_cec_data.Logical_address = 15; hdmi_cec_data.cec_state = false; + hdmi_cec_data.write_busy = false; mutex_unlock(&hdmi_cec_data.lock); return 0; } @@ -244,31 +250,31 @@ static ssize_t hdmi_cec_read(struct file *file, char __user *buf, size_t count, struct hdmi_cec_priv *hdmi_cec = file->private_data; int ret = 0; - pr_debug("function : %s\n", __func__); if (!open_count) return -ENODEV; - count = min(count, sizeof(struct hdmi_cec_event) - sizeof(struct list_head)); + pr_debug("function : %s\n", __func__); + count = min(count, sizeof(struct hdmi_cec_event)); do { unsigned long flags; - struct hdmi_cec_event *event = NULL; + struct hdmi_cec_event_list *event = NULL; spin_lock_irqsave(&hdmi_cec->irq_lock, flags); - if (!list_empty(&head)) { - event = list_first_entry(&head, struct hdmi_cec_event, list); - list_del(&event->list); + if (!list_empty(&hdmi_cec->head)) { + event = list_first_entry(&hdmi_cec->head, struct hdmi_cec_event_list, ptr); + list_del(&event->ptr); } spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags); if (event) { - ret = copy_to_user(buf, event, count) ? -EFAULT : count; - vfree(event); + ret = copy_to_user(buf, &event->data, count) ? -EFAULT : count; + kfree(event); } else if (file->f_flags & O_NONBLOCK) { ret = -EAGAIN; } - else if (wait_event_interruptible(hdmi_cec_queue, (!list_empty(&head)))) { + else if (wait_event_interruptible(hdmi_cec_queue, (!list_empty(&hdmi_cec->head)))) { ret = -ERESTARTSYS; } } while(!ret); @@ -283,8 +289,8 @@ static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, int ret = 0 , i = 0; u8 msg[MAX_MESSAGE_LEN]; u8 val = 0; + unsigned long flags; - pr_debug("function : %s\n", __func__); if (!open_count) return -ENODEV; @@ -302,31 +308,34 @@ static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, mutex_lock(&hdmi_cec->lock); hdmi_cec->write_busy = true; + wake_up(&hdmi_cec_queue); + memcpy(hdmi_cec->last_msg, msg, count); + hdmi_cec->msg_len = count; + + pr_debug("function : %s\n", __func__); + do { - hdmi_cec->send_error = 0; hdmi_writeb(count, HDMI_CEC_TX_CNT); for (i = 0; i < count; i++) { hdmi_writeb(msg[i], HDMI_CEC_TX_DATA0+i); } - - val = hdmi_readb(HDMI_CEC_CTRL); - val |= 0x01; + val = (hdmi_cec->send_error ? 0 : 0x02) | 0x01; + spin_lock_irqsave(&hdmi_cec->irq_lock, flags); hdmi_writeb(val, HDMI_CEC_CTRL); - memcpy(hdmi_cec->last_msg, msg, count); - hdmi_cec->msg_len = count; + spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags); - for (i = 0; i++ < 300 && (hdmi_readb(HDMI_CEC_CTRL) & 0x01) && !hdmi_cec->send_error; msleep(10)); - if (hdmi_readb(HDMI_CEC_CTRL) & 0x01 || hdmi_cec->send_error) { + ret = wait_event_timeout(hdmi_cec_sent, !((val = hdmi_readb(HDMI_CEC_CTRL)) & 0x01), msecs_to_jiffies(700)); + if (hdmi_cec->send_error > 5 || !ret) { hdmi_cec->msg_len = 0; - hdmi_cec->send_error = 0; - hdmi_writeb(0x02, HDMI_CEC_CTRL); ret = -EIO; - } else { + hdmi_cec->write_busy = false; + } else if (hdmi_cec->send_error > 0) { + ret = 0; + } else if (ret > 0) { ret = count; } } while(!ret); - hdmi_cec->write_busy = false; mutex_unlock(&hdmi_cec->lock); return ret; } @@ -337,26 +346,24 @@ static void hdmi_cec_hwenable(void) if (!hdmi_cec_ready || hdmi_cec_data.cec_state) return; + hdmi_cec_data.cec_state = true; + + pr_debug("function : %s\n", __func__); val = hdmi_readb(HDMI_MC_CLKDIS); val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; hdmi_writeb(val, HDMI_MC_CLKDIS); val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | - HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_DONE; + HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; hdmi_writeb(val, HDMI_CEC_POLARITY); - val = HDMI_IH_CEC_STAT0_WAKEUP | + val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ARB_LOST; hdmi_writeb(val, HDMI_CEC_MASK); hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); - - hdmi_writeb(0x02, HDMI_CEC_CTRL); hdmi_writeb(0x0, HDMI_CEC_LOCK); - - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.cec_state = true; - mutex_unlock(&hdmi_cec_data.lock); + hdmi_writeb(0x02, HDMI_CEC_CTRL); } static void hdmi_cec_hwdisable(void) @@ -365,7 +372,10 @@ static void hdmi_cec_hwdisable(void) if(!hdmi_cec_ready || !hdmi_cec_data.cec_state) return; + hdmi_cec_data.cec_state = false; + pr_debug("function : %s\n", __func__); + hdmi_writeb(0x10, HDMI_CEC_CTRL); val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_ARB_LOST | HDMI_IH_CEC_STAT0_NACK | HDMI_IH_CEC_STAT0_EOM | @@ -375,21 +385,20 @@ static void hdmi_cec_hwdisable(void) hdmi_writeb(0x0, HDMI_CEC_POLARITY); - hdmi_writeb(0x10, HDMI_CEC_CTRL); - val = hdmi_readb(HDMI_MC_CLKDIS); val |= HDMI_MC_CLKDIS_CECCLK_DISABLE; hdmi_writeb(val, HDMI_MC_CLKDIS); - - mutex_lock(&hdmi_cec_data.lock); - hdmi_cec_data.cec_state = false; - mutex_unlock(&hdmi_cec_data.lock); } static long hdmi_cec_set_address(u_long arg) { u8 val; + if (hdmi_cec_data.Logical_address == (u8)arg) + return 0; + + pr_debug("function : %s\n", __func__); + if (arg <= 7) { val = 1 << (u8)arg; hdmi_writeb(val, HDMI_CEC_ADDR_L); @@ -417,18 +426,13 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, struct mxc_edid_cfg hdmi_edid_cfg; pr_debug("function : %s\n", __func__); + if (!open_count) return -ENODEV; switch (cmd) { case HDMICEC_IOC_SETLOGICALADDRESS: mutex_lock(&hdmi_cec_data.lock); - if (false == hdmi_cec_data.cec_state) { - mutex_unlock(&hdmi_cec_data.lock); - pr_err("Trying to set logical address while not started\n"); - return -EACCES; - } - hdmi_cec_set_address(arg); mutex_unlock(&hdmi_cec_data.lock); break; @@ -458,6 +462,8 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, void hdmi_cec_start_device(void) { + pr_debug("function : %s\n", __func__); + hdmi_cec_ready = 1; if(open_count) hdmi_cec_hwenable(); @@ -466,8 +472,10 @@ EXPORT_SYMBOL(hdmi_cec_start_device); void hdmi_cec_stop_device(void) { + pr_debug("function : %s\n", __func__); + if(open_count) - hdmi_cec_hwenable(); + hdmi_cec_hwdisable(); hdmi_cec_ready = 0; } EXPORT_SYMBOL(hdmi_cec_stop_device); @@ -481,16 +489,16 @@ static int hdmi_cec_release(struct inode *inode, struct file *filp) hdmi_cec_hwdisable(); mutex_lock(&hdmi_cec_data.lock); if (open_count) { - hdmi_cec_set_address(15); if (!(wait_event_timeout(hdmi_cec_queue, !hdmi_cec_data.write_busy, msecs_to_jiffies(500)))) hdmi_cec_data.write_busy = false; + hdmi_cec_set_address(15); - while (!list_empty(&head)) { - struct hdmi_cec_event *event; + while (!list_empty(&hdmi_cec_data.head)) { + struct hdmi_cec_event_list *event = NULL; - event = list_first_entry(&head, struct hdmi_cec_event, list); - list_del(&event->list); - vfree(event); + event = list_first_entry(&hdmi_cec_data.head, struct hdmi_cec_event_list, ptr); + list_del(&event->ptr); + kfree(event); } open_count = 0; } @@ -502,13 +510,12 @@ static unsigned int hdmi_cec_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; - pr_debug("function : %s\n", __func__); - poll_wait(file, &hdmi_cec_queue, wait); + poll_wait(file, &hdmi_cec_sent, wait); if (!hdmi_cec_data.write_busy) mask = (POLLOUT | POLLWRNORM); - if (!list_empty(&head)) + if (!list_empty(&hdmi_cec_data.head)) mask |= (POLLIN | POLLRDNORM); return mask; @@ -573,12 +580,11 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) } init_waitqueue_head(&hdmi_cec_queue); + init_waitqueue_head(&hdmi_cec_sent); - INIT_LIST_HEAD(&head); + INIT_LIST_HEAD(&hdmi_cec_data.head); mutex_init(&hdmi_cec_data.lock); - hdmi_cec_data.Logical_address = 15; - hdmi_cec_data.cec_state = false; platform_set_drvdata(pdev, &hdmi_cec_data); INIT_DELAYED_WORK(&hdmi_cec_data.hdmi_cec_work, mxc_hdmi_cec_worker); @@ -597,9 +603,6 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) static int hdmi_cec_dev_remove(struct platform_device *pdev) { - if (open_count) - hdmi_cec_release(0, 0); - if (hdmi_cec_major > 0) { device_destroy(hdmi_cec_class, MKDEV(hdmi_cec_major, 0)); class_destroy(hdmi_cec_class); diff --git a/drivers/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c index 174342d69afa..233425225606 100644 --- a/drivers/video/mxc/mxc_hdmi.c +++ b/drivers/video/mxc/mxc_hdmi.c @@ -2042,11 +2042,12 @@ static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) clkdis |= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; /* Disable All HDMI clock */ - hdmi_writeb(0xff & clkdis, HDMI_MC_CLKDIS); + hdmi_writeb(0xff, HDMI_MC_CLKDIS); mxc_hdmi_phy_disable(hdmi); hdmi_disable_overflow_interrupts(); + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); hdmi->cable_plugin = false; }