From 9e09602b5071ada809017e2fcc6756a2f3ef8826 Mon Sep 17 00:00:00 2001 From: jenkins101 Date: Sun, 27 Apr 2014 14:38:43 +0200 Subject: [PATCH] projects/Cuboxi: added patches from wolfgar and mk1. fixing CEC on Cuboxi --- packages/devel/libcec/package.mk | 2 +- .../libcec/libcec-02-mk1-cec-fix.patch | 214 +++++ .../linux/linux-999.99.22-hdmi-cec.patch | 731 ++++++++++++++++++ 3 files changed, 946 insertions(+), 1 deletion(-) create mode 100644 projects/Cuboxi/patches/libcec/libcec-02-mk1-cec-fix.patch create mode 100644 projects/Cuboxi/patches/linux/linux-999.99.22-hdmi-cec.patch diff --git a/packages/devel/libcec/package.mk b/packages/devel/libcec/package.mk index 99b2036850..f98dbae3af 100644 --- a/packages/devel/libcec/package.mk +++ b/packages/devel/libcec/package.mk @@ -52,7 +52,7 @@ else fi if [ "$XBMCPLAYER_DRIVER" = "libfslvpuwrap" ]; then - PKG_CONFIGURE_OPTS_TARGET="$PKG_CONFIGURE_OPTS_TARGET --disable-imx6" + PKG_CONFIGURE_OPTS_TARGET="$PKG_CONFIGURE_OPTS_TARGET --enable-imx6" else PKG_CONFIGURE_OPTS_TARGET="$PKG_CONFIGURE_OPTS_TARGET --disable-imx6" fi diff --git a/projects/Cuboxi/patches/libcec/libcec-02-mk1-cec-fix.patch b/projects/Cuboxi/patches/libcec/libcec-02-mk1-cec-fix.patch new file mode 100644 index 0000000000..c09e3061c1 --- /dev/null +++ b/projects/Cuboxi/patches/libcec/libcec-02-mk1-cec-fix.patch @@ -0,0 +1,214 @@ +From d821a5f9b03a436950570ee9d7741452248f72e8 Mon Sep 17 00:00:00 2001 +From: wolfgar +Date: Wed, 23 Apr 2014 03:16:04 +0200 +Subject: [PATCH 1/2] Fix physical address retrieval and notify new address + after unplug/plug + +--- + src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp b/src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp +index 54e5662..a226a70 100644 +--- a/src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp ++++ b/src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp +@@ -189,14 +189,18 @@ cec_vendor_id CIMXCECAdapterCommunication::GetVendorId(void) + uint16_t CIMXCECAdapterCommunication::GetPhysicalAddress(void) + { + uint32_t info; ++ uint16_t phy_addr; + + if (m_dev->Ioctl(HDMICEC_IOC_GETPHYADDRESS, &info) != 0) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: HDMICEC_IOC_GETPHYADDRESS failed !", __func__); + return CEC_INVALID_PHYSICAL_ADDRESS; + } ++ /* Rebuild 16 bit raw value from fsl 32 bits value */ ++ phy_addr = ((info & 0x0f) << 12) | (info & 0x0f00) | ++ ((info & 0x0f0000) >> 12) | ((info & 0x0f000000) >> 24); + +- return info; ++ return phy_addr; + } + + +@@ -266,6 +270,13 @@ void *CIMXCECAdapterCommunication::Process(void) + if (!IsStopped()) + m_callback->OnCommandReceived(cmd); + } ++ ++ if (event.event_type == MESSAGE_TYPE_CONNECTED) ++ /* HDMI has just been reconnected - Notify phy address*/ ++ { ++ uint16_t iNewAddress = GetPhysicalAddress(); ++ m_callback->HandlePhysicalAddressChanged(iNewAddress); ++ } + /* We are not interested in other events */ + } /*else { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s: Read returned %d", __func__, ret); +-- +1.7.9.5 + + +From 42c3c07a79b8155635851c2eed9558b221b89047 Mon Sep 17 00:00:00 2001 +From: wolfgar +Date: Sat, 26 Apr 2014 01:48:06 +0200 +Subject: [PATCH 2/2] Grab enhancements from mk01 commit + https://github.com/mk01/libcec/commit/40ac7550fe22a9fed665eec0aec1882498f838d6#diff-f2ea3f151edca2fc91b2f3cea1159c9bR336 + +--- + src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp | 58 ++++++++++++++++---- + src/lib/adapter/IMX/IMXCECAdapterCommunication.h | 9 ++- + 2 files changed, 55 insertions(+), 12 deletions(-) + +diff --git a/src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp b/src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp +index a226a70..1f70989 100644 +--- a/src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp ++++ b/src/lib/adapter/IMX/IMXCECAdapterCommunication.cpp +@@ -88,6 +88,8 @@ CIMXCECAdapterCommunication::CIMXCECAdapterCommunication(IAdapterCommunicationCa + m_iNextMessage = 0; + //m_logicalAddresses.Clear(); + m_logicalAddress = CECDEVICE_UNKNOWN; ++ m_bLogicalAddressRegistered = false; ++ m_bInitialised = false; + m_dev = new CCDevSocket(CEC_IMX_PATH); + } + +@@ -110,10 +112,11 @@ bool CIMXCECAdapterCommunication::Open(uint32_t iTimeoutMs, bool UNUSED(bSkipChe + if (m_dev->Open(iTimeoutMs)) + { + if (!bStartListening || CreateThread()) { +- if (m_dev->Ioctl(HDMICEC_IOC_STARTDEVICE, NULL) != 0) { +- LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: Unable to start device\n", __func__); ++ if (m_dev->Ioctl(HDMICEC_IOC_STARTDEVICE, NULL) == 0) { ++ m_bInitialised = true; ++ return true; + } +- return true; ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: Unable to start device\n", __func__); + } + m_dev->Close(); + } +@@ -125,10 +128,16 @@ bool CIMXCECAdapterCommunication::Open(uint32_t iTimeoutMs, bool UNUSED(bSkipChe + void CIMXCECAdapterCommunication::Close(void) + { + StopThread(0); ++ ++ CLockObject lock(m_mutex); ++ if (!m_bInitialised) { ++ return; ++ } + if (m_dev->Ioctl(HDMICEC_IOC_STOPDEVICE, NULL) != 0) { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: Unable to stop device\n", __func__); + } + m_dev->Close(); ++ m_bInitialised = false; + } + + +@@ -210,31 +219,60 @@ cec_logical_addresses CIMXCECAdapterCommunication::GetLogicalAddresses(void) + addresses.Clear(); + + CLockObject lock(m_mutex); +- if ( m_logicalAddress != CECDEVICE_UNKNOWN) ++ if ((m_logicalAddress & (CECDEVICE_UNKNOWN | CECDEVICE_UNREGISTERED)) == 0) + addresses.Set(m_logicalAddress); + + return addresses; + } + ++void CIMXCECAdapterCommunication::HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress)) ++{ ++ UnregisterLogicalAddress(); ++} + +-bool CIMXCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses) ++bool CIMXCECAdapterCommunication::UnregisterLogicalAddress(void) + { +- int log_addr = addresses.primary; ++ CLockObject lock(m_mutex); ++ if (!m_bLogicalAddressRegistered) ++ return true; ++ ++ if (m_dev->Ioctl(HDMICEC_IOC_SETLOGICALADDRESS, (void *)CECDEVICE_BROADCAST) != 0) ++ { ++ LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: HDMICEC_IOC_SETLOGICALADDRESS failed !", __func__); ++ return false; ++ } + ++ m_logicalAddress = CECDEVICE_UNKNOWN; ++ m_bLogicalAddressRegistered = false; ++ return true; ++} ++ ++bool CIMXCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_address address) ++{ + CLockObject lock(m_mutex); +- if (m_logicalAddress == log_addr) +- return true; + +- if (m_dev->Ioctl(HDMICEC_IOC_SETLOGICALADDRESS, (void *)log_addr) != 0) ++ if (m_logicalAddress == address && m_bLogicalAddressRegistered) ++ { ++ return true; ++ } ++ ++ if (m_dev->Ioctl(HDMICEC_IOC_SETLOGICALADDRESS, (void *)address) != 0) + { + LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: HDMICEC_IOC_SETLOGICALADDRESS failed !", __func__); + return false; + } + +- m_logicalAddress = (cec_logical_address)log_addr; ++ m_logicalAddress = address; ++ m_bLogicalAddressRegistered = true; + return true; + } + ++bool CIMXCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses) ++{ ++ int log_addr = addresses.primary; ++ ++ return RegisterLogicalAddress((cec_logical_address)log_addr); ++} + + void *CIMXCECAdapterCommunication::Process(void) + { +diff --git a/src/lib/adapter/IMX/IMXCECAdapterCommunication.h b/src/lib/adapter/IMX/IMXCECAdapterCommunication.h +index 910dd39..ce5c4cb 100644 +--- a/src/lib/adapter/IMX/IMXCECAdapterCommunication.h ++++ b/src/lib/adapter/IMX/IMXCECAdapterCommunication.h +@@ -85,7 +85,9 @@ namespace CEC + cec_adapter_type GetAdapterType(void) { return ADAPTERTYPE_IMX; } + uint16_t GetAdapterVendorId(void) const { return IMX_ADAPTER_VID; } + uint16_t GetAdapterProductId(void) const { return IMX_ADAPTER_PID; } ++ void HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress)); + void SetActiveSource(bool UNUSED(bSetTo), bool UNUSED(bClientUnregistered)) {} ++ bool RegisterLogicalAddress(const cec_logical_address address); + ///} + + /** @name PLATFORM::CThread implementation */ +@@ -94,7 +96,8 @@ namespace CEC + ///} + + private: +- bool IsInitialised(void) const { return m_dev != 0; }; ++ bool IsInitialised(void) const { return m_bInitialised; }; ++ bool UnregisterLogicalAddress(void); + + std::string m_strError; /**< current error message */ + +@@ -103,7 +106,9 @@ namespace CEC + + PLATFORM::CMutex m_mutex; + PLATFORM::CCDevSocket *m_dev; /**< the device connection */ +- ++ bool m_bLogicalAddressRegistered; ++ bool m_bInitialised; ++ + PLATFORM::CMutex m_messageMutex; + uint32_t m_iNextMessage; + std::map m_messages; +-- +1.7.9.5 + diff --git a/projects/Cuboxi/patches/linux/linux-999.99.22-hdmi-cec.patch b/projects/Cuboxi/patches/linux/linux-999.99.22-hdmi-cec.patch new file mode 100644 index 0000000000..5762554541 --- /dev/null +++ b/projects/Cuboxi/patches/linux/linux-999.99.22-hdmi-cec.patch @@ -0,0 +1,731 @@ +From 74fc2e85c080b0ddc2a4b2565eda97c55300303d Mon Sep 17 00:00:00 2001 +From: wolfgar +Date: Sat, 26 Apr 2014 16:50:32 +0200 +Subject: [PATCH] Merge all CEC driver changes to 3.10 This commit pushes all + changes I did in the 3.0.35 kernel to improve/fix its + behavior with libcec port for imx6 + +It also fixes CEC clock handling in case of FB mode change event +and of HDMI cable disconnection +--- + drivers/mxc/hdmi-cec/mxc_hdmi-cec.c | 327 +++++++++++++++-------------------- + drivers/video/mxc/mxc_hdmi.c | 14 +- + 2 files changed, 147 insertions(+), 194 deletions(-) + +diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c +index d0113ee..3a8aff4 100644 +--- a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c ++++ b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c +@@ -55,6 +55,8 @@ + #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; +@@ -63,7 +65,9 @@ struct hdmi_cec_priv { + bool cec_state; + u8 last_msg[MAX_MESSAGE_LEN]; + u8 msg_len; +- u8 latest_cec_stat; ++ int tx_answer; ++ u16 latest_cec_stat; ++ u8 link_status; + spinlock_t irq_lock; + struct delayed_work hdmi_cec_work; + struct mutex lock; +@@ -76,6 +80,7 @@ struct hdmi_cec_event { + struct list_head list; + }; + ++ + static LIST_HEAD(head); + + static int hdmi_cec_major; +@@ -84,11 +89,14 @@ 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; +- u8 cec_stat = 0; ++ u16 cec_stat = 0; + unsigned long flags; ++ u8 phy_stat0; + + spin_lock_irqsave(&hdmi_cec->irq_lock, flags); + +@@ -96,16 +104,24 @@ 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 (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 */ ++ } + 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_IH_CEC_STAT0_DONE | 0x180)) == 0) { + spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags); + return IRQ_HANDLED; + } +- + pr_debug("HDMI CEC interrupt received\n"); +- hdmi_cec->latest_cec_stat = cec_stat; ++ /* FIXME : there is a race with latest_cec_stat */ ++ hdmi_cec->latest_cec_stat = cec_stat ; + + schedule_delayed_work(&(hdmi_cec->hdmi_cec_work), msecs_to_jiffies(20)); + +@@ -118,115 +134,70 @@ void mxc_hdmi_cec_handle(u16 cec_stat) + { + u8 val = 0, i = 0; + struct hdmi_cec_event *event = NULL; +- +- /* The current transmission is successful (for initiator only). */ ++ /*The current transmission is successful (for initiator only).*/ + if (!open_count) + return; + + if (cec_stat & HDMI_IH_CEC_STAT0_DONE) { +- +- 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_SEND_SUCCESS; +- +- mutex_lock(&hdmi_cec_data.lock); +- list_add_tail(&event->list, &head); +- mutex_unlock(&hdmi_cec_data.lock); +- +- wake_up(&hdmi_cec_queue); ++ hdmi_cec_data.tx_answer = cec_stat; ++ wake_up(&tx_cec_queue); + } +- +- /* EOM is detected so that the received data is ready +- * in the receiver data buffer +- */ ++ /*EOM is detected so that the received data is ready in the receiver data buffer*/ + 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__); + return; + } + 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; + } + 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). */ ++ /*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 > 5) { +- pr_err("%s:Re-transmission is attempted more than 5 times!\n", +- __func__); ++ 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); +- } ++ 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). +- */ ++ /*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)); +- 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_NOACK; +- +- mutex_lock(&hdmi_cec_data.lock); +- list_add_tail(&event->list, &head); +- mutex_unlock(&hdmi_cec_data.lock); +- +- wake_up(&hdmi_cec_queue); ++ 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). +- */ ++ /*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 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__); +@@ -234,16 +205,14 @@ void mxc_hdmi_cec_handle(u16 cec_stat) + } + memset(event, 0, sizeof(struct hdmi_cec_event)); + event->event_type = MESSAGE_TYPE_CONNECTED; +- + mutex_lock(&hdmi_cec_data.lock); + list_add_tail(&event->list, &head); + mutex_unlock(&hdmi_cec_data.lock); +- + wake_up(&hdmi_cec_queue); + } +- +- /* HDMI cable disconnected */ ++ /*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__); +@@ -251,30 +220,24 @@ void mxc_hdmi_cec_handle(u16 cec_stat) + } + memset(event, 0, sizeof(struct hdmi_cec_event)); + event->event_type = MESSAGE_TYPE_DISCONNECTED; +- + mutex_lock(&hdmi_cec_data.lock); + list_add_tail(&event->list, &head); + mutex_unlock(&hdmi_cec_data.lock); +- + 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; ++ val = HDMI_IH_CEC_STAT0_WAKEUP | HDMI_IH_CEC_STAT0_ERROR_FOLL | HDMI_IH_CEC_STAT0_ARB_LOST; + hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0); + } + + /*! +- * @brief open function for vpu file operation ++ * @brief open function for cec file operation + * + * @return 0 on success or negative error code on error + */ +@@ -285,13 +248,11 @@ static int hdmi_cec_open(struct inode *inode, struct file *filp) + mutex_unlock(&hdmi_cec_data.lock); + return -EBUSY; + } +- + open_count = 1; + filp->private_data = (void *)(&hdmi_cec_data); + hdmi_cec_data.Logical_address = 15; + hdmi_cec_data.cec_state = false; + mutex_unlock(&hdmi_cec_data.lock); +- + return 0; + } + +@@ -299,36 +260,40 @@ 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__); ++ + 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; + } +- mutex_unlock(&hdmi_cec_data.lock); + +- /* delete from list */ +- mutex_lock(&hdmi_cec_data.lock); + if (list_empty(&head)) { +- mutex_unlock(&hdmi_cec_data.lock); +- return -EACCES; ++ 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)); ++ } + } ++ + 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))) { ++ sizeof(struct hdmi_cec_event) - sizeof(struct list_head))) { + vfree(event); + return -EFAULT; + } + vfree(event); +- +- return sizeof(struct hdmi_cec_event); ++ return (sizeof(struct hdmi_cec_event) - sizeof(struct list_head)); + } + + static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, +@@ -339,58 +304,77 @@ static ssize_t hdmi_cec_write(struct file *file, const char __user *buf, + u8 msg_len = 0, 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; +- +- mutex_lock(&hdmi_cec_data.lock); +- hdmi_cec_data.send_error = 0; + memset(&msg, 0, MAX_MESSAGE_LEN); + ret = copy_from_user(&msg, buf, count); +- if (ret) { +- ret = -EACCES; +- goto end; +- } +- ++ 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++) { ++ 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); + +- i = 0; +- val = hdmi_readb(HDMI_CEC_CTRL); +- while ((val & 0x01) == 0x1) { +- msleep(50); +- i++; +- if (i > 3) { +- ret = -EIO; +- goto end; +- } +- val = hdmi_readb(HDMI_CEC_CTRL); ++ 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; + } + +-end: +- mutex_unlock(&hdmi_cec_data.lock); ++ 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; + } + ++ ++static void hdmi_stop_device(void) ++{ ++ u8 val; ++ ++ 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); ++} ++ + /*! + * @brief IO ctrl function for vpu file operation + * @param cmd IO ctrl command +@@ -402,93 +386,59 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, + int ret = 0, status = 0; + u8 val = 0, msg = 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); + 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_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 > 7 && 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 { ++ } else + ret = -EINVAL; +- } +- +- /* Send Polling message with same source +- * and destination address +- */ ++ /*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; ++ 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: + val = hdmi_readb(HDMI_MC_CLKDIS); + val &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; + hdmi_writeb(val, HDMI_MC_CLKDIS); +- + hdmi_writeb(0x02, HDMI_CEC_CTRL); +- +- val = HDMI_IH_CEC_STAT0_ERROR_INIT | HDMI_IH_CEC_STAT0_NACK | +- HDMI_IH_CEC_STAT0_EOM | HDMI_IH_CEC_STAT0_DONE; ++ /* 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; ++ 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); + break; +- + case HDMICEC_IOC_STOPDEVICE: +- 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); ++ hdmi_stop_device(); + break; +- + case HDMICEC_IOC_GETPHYADDRESS: + hdmi_get_edid_cfg(&hdmi_edid_cfg); + status = copy_to_user((void __user *)arg, +@@ -497,29 +447,25 @@ static long hdmi_cec_ioctl(struct file *filp, u_int cmd, + if (status) + ret = -EFAULT; + break; +- + default: + ret = -EINVAL; + break; + } +- +- return ret; ++ return ret; + } + + /*! +-* @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) + { + mutex_lock(&hdmi_cec_data.lock); +- + if (open_count) { + open_count = 0; + hdmi_cec_data.cec_state = false; + hdmi_cec_data.Logical_address = 15; + } +- + mutex_unlock(&hdmi_cec_data.lock); + + return 0; +@@ -531,20 +477,18 @@ static unsigned int hdmi_cec_poll(struct file *file, poll_table *wait) + + pr_debug("function : %s\n", __func__); + +- if (!open_count) +- return -ENODEV; +- +- if (false == hdmi_cec_data.cec_state) +- return -EACCES; +- + poll_wait(file, &hdmi_cec_queue, wait); + ++ /* Always writable */ ++ mask = (POLLOUT | POLLWRNORM); ++ mutex_lock(&hdmi_cec_data.lock); + if (!list_empty(&head)) +- mask |= (POLLIN | POLLRDNORM); +- ++ mask |= (POLLIN | POLLRDNORM); ++ mutex_unlock(&hdmi_cec_data.lock); + return mask; + } + ++ + const struct file_operations hdmi_cec_fops = { + .owner = THIS_MODULE, + .read = hdmi_cec_read, +@@ -563,20 +507,18 @@ static int hdmi_cec_dev_probe(struct platform_device *pdev) + struct pinctrl *pinctrl; + int irq = platform_get_irq(pdev, 0); + +- hdmi_cec_major = register_chrdev(hdmi_cec_major, +- "mxc_hdmi_cec", &hdmi_cec_fops); ++ hdmi_cec_major = register_chrdev(hdmi_cec_major, "mxc_hdmi_cec", &hdmi_cec_fops); + if (hdmi_cec_major < 0) { + dev_err(&pdev->dev, "hdmi_cec: unable to get a major for HDMI CEC\n"); + 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"); + goto err_out_chrdev; + } +- + spin_lock_init(&hdmi_cec_data.irq_lock); + + err = devm_request_irq(&pdev->dev, irq, mxc_hdmi_cec_isr, IRQF_SHARED, +@@ -592,8 +534,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; +@@ -606,15 +548,14 @@ 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); + + dev_info(&pdev->dev, "HDMI CEC initialized\n"); +@@ -631,12 +572,14 @@ out: + + static int hdmi_cec_dev_remove(struct platform_device *pdev) + { ++ if (hdmi_cec_data.cec_state) ++ hdmi_stop_device(); + 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; + } + +@@ -650,9 +593,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/video/mxc/mxc_hdmi.c b/drivers/video/mxc/mxc_hdmi.c +index 20c6d70..88d62ce 100644 +--- a/drivers/video/mxc/mxc_hdmi.c ++++ b/drivers/video/mxc/mxc_hdmi.c +@@ -1708,8 +1708,12 @@ static void mxc_hdmi_enable_video_path(struct mxc_hdmi *hdmi) + hdmi_writeb(0x16, HDMI_FC_CH1PREAM); + hdmi_writeb(0x21, HDMI_FC_CH2PREAM); + ++ /* Save CEC clock */ ++ clkdis = hdmi_readb(HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_CECCLK_DISABLE; ++ clkdis |= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; ++ + /* Enable pixel clock and tmds data path */ +- clkdis = 0x7F; ++ clkdis = 0x7F & clkdis; + clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); + +@@ -1990,10 +1994,16 @@ static void mxc_hdmi_power_off(struct mxc_dispdrv_handle *disp) + + static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) + { ++ u8 clkdis; ++ + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + ++ /* Save CEC clock */ ++ clkdis = hdmi_readb(HDMI_MC_CLKDIS) & HDMI_MC_CLKDIS_CECCLK_DISABLE; ++ clkdis |= ~HDMI_MC_CLKDIS_CECCLK_DISABLE; ++ + /* Disable All HDMI clock */ +- hdmi_writeb(0xff, HDMI_MC_CLKDIS); ++ hdmi_writeb(0xff & clkdis, HDMI_MC_CLKDIS); + + mxc_hdmi_phy_disable(hdmi); + +-- +1.7.9.5 +