From bbaef2263d8ee6a771b7a0953b46e50a73d50861 Mon Sep 17 00:00:00 2001 From: Zhang Xiaodong Date: Mon, 2 Jul 2012 15:02:41 +0800 Subject: [PATCH] ENGR00215182-2 MXC HDMI CEC: Add basic support for HDMI CEC - 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 --- drivers/mxc/Kconfig | 1 + drivers/mxc/Makefile | 1 + drivers/mxc/hdmi-cec/Kconfig | 14 + drivers/mxc/hdmi-cec/Makefile | 6 + drivers/mxc/hdmi-cec/mxc_hdmi-cec.c | 584 ++++++++++++++++++++++++++++ drivers/mxc/hdmi-cec/mxc_hdmi-cec.h | 37 ++ drivers/video/mxc/mxc_edid.c | 4 + drivers/video/mxc_hdmi.c | 8 +- 8 files changed, 654 insertions(+), 1 deletion(-) create mode 100644 drivers/mxc/hdmi-cec/Kconfig create mode 100644 drivers/mxc/hdmi-cec/Makefile create mode 100644 drivers/mxc/hdmi-cec/mxc_hdmi-cec.c create mode 100644 drivers/mxc/hdmi-cec/mxc_hdmi-cec.h diff --git a/drivers/mxc/Kconfig b/drivers/mxc/Kconfig index 441b655da153..5e2bfc0d1f69 100644 --- a/drivers/mxc/Kconfig +++ b/drivers/mxc/Kconfig @@ -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 diff --git a/drivers/mxc/Makefile b/drivers/mxc/Makefile index fbcd0fee1801..5d44c89eea04 100644 --- a/drivers/mxc/Makefile +++ b/drivers/mxc/Makefile @@ -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 index 000000000000..0afc58ada696 --- /dev/null +++ b/drivers/mxc/hdmi-cec/Kconfig @@ -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 index 000000000000..86cf269879ab --- /dev/null +++ b/drivers/mxc/hdmi-cec/Makefile @@ -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 index 000000000000..011fc33ab17e --- /dev/null +++ b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c @@ -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 +#include +#include /* for struct file_operations */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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 index 000000000000..a95b4cedf0c8 --- /dev/null +++ b/drivers/mxc/hdmi-cec/mxc_hdmi-cec.h @@ -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 /* 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_ */ diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index f86313df7dea..6aec72de5fb6 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -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) && diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index b096a881a7b2..317013149db0 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -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"); -- 2.39.5