]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00215182-2 MXC HDMI CEC: Add basic support for HDMI CEC
authorZhang Xiaodong <B39117@freescale.com>
Mon, 2 Jul 2012 07:02:41 +0000 (15:02 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:34:56 +0000 (08:34 +0200)
- Add MXC HDMI CEC to kconfig and makefile under driver/mxc
- Add initial mxc_hdmi-cec.c file to provide basic HDMI CEC
functionality:
    - Basic HDMI CEC resource initilize functional
    - Support for sending and receiving CEC message via CEC line
    - Report HDMI cable status to CEC lib at userspace.
Signed-off-by: Zhang Xiaodong <B39117@freescale.com>
drivers/mxc/Kconfig
drivers/mxc/Makefile
drivers/mxc/hdmi-cec/Kconfig [new file with mode: 0644]
drivers/mxc/hdmi-cec/Makefile [new file with mode: 0644]
drivers/mxc/hdmi-cec/mxc_hdmi-cec.c [new file with mode: 0644]
drivers/mxc/hdmi-cec/mxc_hdmi-cec.h [new file with mode: 0644]
drivers/video/mxc/mxc_edid.c
drivers/video/mxc_hdmi.c

index 441b655da15370b7fc0a204553a573d93c021889..5e2bfc0d1f69b22b9b03683cb483557c44727185 100644 (file)
@@ -38,6 +38,7 @@ source "drivers/mxc/amd-gpu/Kconfig"
 source "drivers/mxc/gpu-viv/Kconfig"
 source "drivers/mxc/thermal/Kconfig"
 source "drivers/mxc/mipi/Kconfig"
+source "drivers/mxc/hdmi-cec/Kconfig"
 
 endmenu
 
index fbcd0fee1801e583b34a427dbf2126018dac681f..5d44c89eea043cb3381b43025b822fa1e494705d 100644 (file)
@@ -19,3 +19,4 @@ obj-$(CONFIG_MXC_AMD_GPU)              += amd-gpu/
 obj-$(CONFIG_MXC_GPU_VIV)              += gpu-viv/
 obj-$(CONFIG_ANATOP_THERMAL)            += thermal/
 obj-$(CONFIG_MXC_MIPI_CSI2)            += mipi/
+obj-$(CONFIG_MXC_HDMI_CEC)            += hdmi-cec/
diff --git a/drivers/mxc/hdmi-cec/Kconfig b/drivers/mxc/hdmi-cec/Kconfig
new file mode 100644 (file)
index 0000000..0afc58a
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Codec configuration
+#
+
+menu "MXC HDMI CEC (Consumer Electronics Control) support"
+
+config MXC_HDMI_CEC
+         tristate "Support for MXC HDMI CEC (Consumer Electronics Control)"
+         depends on (ARCH_MX6)
+         select MFD_MXC_HDMI
+       ---help---
+         The HDMI CEC device implement low level protocol on i.MX6x platforms.
+
+endmenu
diff --git a/drivers/mxc/hdmi-cec/Makefile b/drivers/mxc/hdmi-cec/Makefile
new file mode 100644 (file)
index 0000000..86cf269
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for the HDMI CEC drivers.
+#
+
+obj-$(CONFIG_MXC_HDMI_CEC)                  += mxc_hdmi-cec.o
+
diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c
new file mode 100644 (file)
index 0000000..011fc33
--- /dev/null
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file mxc_hdmi-cec.c
+ *
+ * @brief HDMI CEC system initialization and file operation implementation
+ *
+ * @ingroup HDMI
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/fs.h>          /* for struct file_operations */
+#include <linux/stat.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/fsl_devices.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/workqueue.h>
+#include <asm/sizes.h>
+
+#include <linux/console.h>
+#include <linux/types.h>
+#include <linux/mfd/mxc-hdmi-core.h>
+
+#include <mach/mxc_edid.h>
+#include <mach/mxc_hdmi.h>
+
+#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
+
+
+struct hdmi_cec_priv {
+       int  receive_error;
+       int  send_error;
+       u8 Logical_address;
+       bool cec_state;
+       u8 last_msg[MAX_MESSAGE_LEN];
+       u8 msg_len;
+       u8 latest_cec_stat;
+       u32 cec_irq;
+       spinlock_t irq_lock;
+       struct delayed_work hdmi_cec_work;
+       struct mutex lock;
+};
+
+struct hdmi_cec_event {
+       int event_type;
+       int msg_len;
+       u8 msg[MAX_MESSAGE_LEN];
+       struct list_head list;
+};
+
+
+static LIST_HEAD(head);
+
+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 irqreturn_t mxc_hdmi_cec_isr(int irq, void *data)
+{
+       struct hdmi_cec_priv *hdmi_cec = data;
+       u8 cec_stat = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hdmi_cec->irq_lock, flags);
+
+       hdmi_writeb(0x7f, HDMI_IH_MUTE_CEC_STAT0);
+
+       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) {
+               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;
+
+       schedule_delayed_work(&(hdmi_cec->hdmi_cec_work), msecs_to_jiffies(20));
+
+       spin_unlock_irqrestore(&hdmi_cec->irq_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+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).*/
+       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:Don't get 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);
+       }
+       /*EOM is detected so that the received data is ready in the receiver data buffer*/
+       else 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:Don't get 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). */
+       else 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__);
+                       hdmi_cec_data.send_error = 0;
+                       mutex_unlock(&hdmi_cec_data.lock);
+                       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).*/
+       else if (cec_stat & HDMI_IH_CEC_STAT0_NACK) {
+               event = vmalloc(sizeof(struct hdmi_cec_event));
+               if (NULL == event) {
+                       pr_err("%s:Don't get 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);
+       }
+       /*An error is notified by a follower. Abnormal logic data bit error (for follower).*/
+       else if (cec_stat & HDMI_IH_CEC_STAT0_ERROR_FOLL)
+               hdmi_cec_data.receive_error++;
+       /*HDMI cable connected*/
+       else if (cec_stat & 0x80) {
+               event = vmalloc(sizeof(struct hdmi_cec_event));
+               if (NULL == event) {
+                       pr_err("%s:Don't get memory!\n", __func__);
+                       return;
+               }
+               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*/
+       else if (cec_stat & 0x100) {
+               event = vmalloc(sizeof(struct hdmi_cec_event));
+               if (NULL == event) {
+                       pr_err("%s:Don't get memory!\n", __func__);
+                       return;
+               }
+               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;
+       hdmi_writeb(val, HDMI_IH_MUTE_CEC_STAT0);
+}
+
+/*!
+ * @brief open function for vpu file operation
+ *
+ * @return  0 on success or negative error code on error
+ */
+static int hdmi_cec_open(struct inode *inode, struct file *filp)
+{
+       mutex_lock(&hdmi_cec_data.lock);
+       if (open_count) {
+               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;
+}
+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;
+       }
+       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);
+}
+static ssize_t hdmi_cec_write(struct file *file, const char __user *buf,
+                            size_t count, loff_t *ppos)
+{
+       int ret = 0 , i = 0;
+       u8 msg[MAX_MESSAGE_LEN];
+       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;
+       }
+       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;
+       }
+       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;
+       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);
+       }
+end:
+       mutex_unlock(&hdmi_cec_data.lock);
+       return ret;
+}
+
+
+/*!
+ * @brief IO ctrl function for vpu file operation
+ * @param cmd IO ctrl command
+ * @return  0 on success or negative error code on error
+ */
+static long hdmi_cec_ioctl(struct file *filp, u_int cmd,
+                    u_long arg)
+{
+       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);
+                       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) {
+                       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:
+               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_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);
+               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);
+               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);
+               break;
+       case HDMICEC_IOC_GETPHYADDRESS:
+               hdmi_get_edid_cfg(&hdmi_edid_cfg);
+               status = copy_to_user((void __user *)arg,
+                                        &hdmi_edid_cfg.physical_address,
+                                        4*sizeof(u8));
+               if (status)
+                       ret = -EFAULT;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+    return ret;
+}
+
+/*!
+ * @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;
+}
+
+static unsigned int hdmi_cec_poll(struct file *file, poll_table *wait)
+{
+       unsigned int mask = 0;
+
+       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);
+
+       if (!list_empty(&head))
+                       mask |= (POLLIN | POLLRDNORM);
+       return mask;
+}
+
+
+const struct file_operations hdmi_cec_fops = {
+       .owner = THIS_MODULE,
+       .read = hdmi_cec_read,
+       .write = hdmi_cec_write,
+       .open = hdmi_cec_open,
+       .unlocked_ioctl = hdmi_cec_ioctl,
+       .release = hdmi_cec_release,
+       .poll = hdmi_cec_poll,
+};
+
+static int hdmi_cec_dev_probe(struct platform_device *pdev)
+{
+       int err = 0;
+       struct device *temp_class;
+       struct resource *res;
+
+       hdmi_cec_major = register_chrdev(hdmi_cec_major, "mxc_hdmi_cec", &hdmi_cec_fops);
+       if (hdmi_cec_major < 0) {
+               pr_err("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)) {
+               pr_err("hdmi_cec:No HDMI irq line provided\n");
+               goto err_out_chrdev;
+       }
+       spin_lock_init(&hdmi_cec_data.irq_lock);
+       hdmi_cec_data.cec_irq = res->start;
+
+       err = request_irq(hdmi_cec_data.cec_irq, mxc_hdmi_cec_isr, IRQF_SHARED,
+                         "mxc_hdmi_cec", &hdmi_cec_data);
+       if (err < 0) {
+               pr_err("hdmi_cec:Unable to request irq: %d\n", err);
+               goto err_out_chrdev;
+       }
+
+       hdmi_cec_class = class_create(THIS_MODULE, "mxc_hdmi_cec");
+       if (IS_ERR(hdmi_cec_class)) {
+               err = PTR_ERR(hdmi_cec_class);
+               goto err_out_chrdev;
+       }
+
+       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;
+       }
+
+       mutex_init(&hdmi_cec_data.lock);
+       hdmi_cec_data.Logical_address = 15;
+       platform_set_drvdata(pdev, &hdmi_cec_data);
+       INIT_DELAYED_WORK(&hdmi_cec_data.hdmi_cec_work, mxc_hdmi_cec_worker);
+       printk(KERN_INFO "HDMI CEC initialized\n");
+       goto out;
+
+err_out_class:
+       device_destroy(hdmi_cec_class, MKDEV(hdmi_cec_major, 0));
+       class_destroy(hdmi_cec_class);
+err_out_chrdev:
+       unregister_chrdev(hdmi_cec_major, "mxc_hdmi_cec");
+out:
+       return err;
+}
+
+static int hdmi_cec_dev_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int hdmi_cec_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       return 0;
+}
+
+static int hdmi_cec_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+#else
+#define        hdmi_cec_suspend        NULL
+#define        hdmi_cec_resume NULL
+#endif                         /* !CONFIG_PM */
+
+/*! Driver definition
+ *
+ */
+static struct platform_driver mxc_hdmi_cec_driver = {
+       .driver = {
+                  .name = "mxc_hdmi_cec",
+                  },
+       .probe = hdmi_cec_dev_probe,
+       .remove = hdmi_cec_dev_remove,
+       .suspend = hdmi_cec_suspend,
+       .resume = hdmi_cec_resume,
+};
+
+static int __init hdmi_cec_init(void)
+{
+       int ret = platform_driver_register(&mxc_hdmi_cec_driver);
+
+       init_waitqueue_head(&hdmi_cec_queue);
+       INIT_LIST_HEAD(&head);
+       return ret;
+}
+
+static void __exit hdmi_cec_exit(void)
+{
+       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_vpu");
+               hdmi_cec_major = 0;
+       }
+
+       platform_driver_unregister(&mxc_hdmi_cec_driver);
+       return;
+}
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Linux HDMI CEC driver for Freescale i.MX/MXC");
+MODULE_LICENSE("GPL");
+
+module_init(hdmi_cec_init);
+module_exit(hdmi_cec_exit);
diff --git a/drivers/mxc/hdmi-cec/mxc_hdmi-cec.h b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.h
new file mode 100644 (file)
index 0000000..a95b4ce
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2005-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef _HDMICEC_H_
+#define _HDMICEC_H_
+#include <linux/ioctl.h>       /* needed for the _IOW etc stuff used later */
+
+
+/*
+ * Ioctl definitions
+ */
+
+/* Use 'k' as magic number */
+#define HDMICEC_IOC_MAGIC  'H'
+/*
+ * S means "Set" through a ptr,
+ * T means "Tell" directly with the argument value
+ * G means "Get": reply by setting through a pointer
+ * Q means "Query": response is on the return value
+ * X means "eXchange": G and S atomically
+ * H means "sHift": T and Q atomically
+ */
+#define HDMICEC_IOC_SETLOGICALADDRESS  _IOW(HDMICEC_IOC_MAGIC,  1, unsigned char)
+#define HDMICEC_IOC_STARTDEVICE        _IO(HDMICEC_IOC_MAGIC,  2)
+#define HDMICEC_IOC_STOPDEVICE _IO(HDMICEC_IOC_MAGIC,  3)
+#define HDMICEC_IOC_GETPHYADDRESS      _IOR(HDMICEC_IOC_MAGIC,  4, unsigned char[4])
+
+#endif                         /* !_HDMICEC_H_ */
index f86313df7deaf00645608caf5ce4f5bbb38b35b5..6aec72de5fb66c8f8fd26034443ed52cb09ddf53 100644 (file)
@@ -284,6 +284,10 @@ int mxc_edid_parse_ext_blk(unsigned char *edid,
                                        IEEE_reg_iden[0] = edid[index+1];
                                        IEEE_reg_iden[1] = edid[index+2];
                                        IEEE_reg_iden[2] = edid[index+3];
+                                       cfg->physical_address[0] = (edid[index+4] & 0xf0) >> 4;
+                                       cfg->physical_address[1] = (edid[index+4] & 0x0f);
+                                       cfg->physical_address[2] = (edid[index+5] & 0xf0) >> 4;
+                                       cfg->physical_address[3] = (edid[index+5] & 0x0f);
                                        deep_color = edid[index+6];
 
                                        if ((IEEE_reg_iden[0] == 0x03) &&
index b096a881a7b2b1b43e66bb2781489951ec2ebab1..317013149db0eda6c01f0feac00854af614f2c07 100644 (file)
@@ -185,7 +185,7 @@ struct i2c_client *hdmi_i2c;
 static bool hdmi_inited;
 
 extern const struct fb_videomode mxc_cea_mode[64];
-
+extern void mxc_hdmi_cec_handle(u16 cec_stat);
 #ifdef DEBUG
 static void dump_fb_videomode(struct fb_videomode *m)
 {
@@ -1753,6 +1753,9 @@ static void hotplug_worker(struct work_struct *work)
 
                        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
 
                } else if (!(phy_int_pol & HDMI_PHY_HPD)) {
                        /* Plugout event */
@@ -1766,6 +1769,9 @@ static void hotplug_worker(struct work_struct *work)
 
                        sprintf(event_string, "EVENT=plugout");
                        kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+#ifdef CONFIG_MXC_HDMI_CEC
+                       mxc_hdmi_cec_handle(0x100);
+#endif
 
                } else
                        dev_dbg(&hdmi->pdev->dev, "EVENT=none?\n");