Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(619)

Unified Diff: drivers/media/video/samsung/tv20/cec.c

Issue 2036011: V4L/DVB : Add S5PV210 TV out driver support (Closed) Base URL: swsolcc@12.23.106.100:kernel-samsung.git
Patch Set: Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « drivers/media/video/samsung/tv20/cec.h ('k') | drivers/media/video/samsung/tv20/cec_s5pv210.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: drivers/media/video/samsung/tv20/cec.c
diff --git a/drivers/media/video/samsung/tv20/cec.c b/drivers/media/video/samsung/tv20/cec.c
new file mode 100644
index 0000000000000000000000000000000000000000..7a13f2478586043b5dbab7270f3b773e871989ed
--- /dev/null
+++ b/drivers/media/video/samsung/tv20/cec.c
@@ -0,0 +1,405 @@
+/* linux/drivers/media/video/samsung/tv20/cec.c
+*
+* Copyright (c) 2010 Samsung Electronics Co., Ltd.
+* http://www.samsung.com/
+*
+* S5PV210 - cec interface file for Samsung TVOut driver
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+
+#include <linux/io.h>
+#include <asm/mach-types.h>
+#include <linux/uaccess.h>
+
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+
+#include <mach/regs-hdmi.h>
+
+#include "s5p_tv.h"
+#include "cec.h"
+
+#ifdef CECDEBUG
+#define CECIFPRINTK(fmt, args...) \
+ printk(KERN_INFO "\t[CEC_IF] %s: " fmt, __func__ , ## args)
+#else
+#define CECIFPRINTK(fmt, args...)
+#endif
+
+static struct cec_rx_struct cec_rx_struct;
+static struct cec_tx_struct cec_tx_struct;
+
+static bool hdmi_on;
+
+/**
+ * Change CEC Tx state to state
+ * @param state [in] new CEC Tx state.
+ */
+void tv_cec_set_tx_state(enum cec_state state)
+{
+ atomic_set(&cec_tx_struct.state, state);
+}
+
+/**
+ * Change CEC Rx state to @c state.
+ * @param state [in] new CEC Rx state.
+ */
+void tv_cec_set_rx_state(enum cec_state state)
+{
+ atomic_set(&cec_rx_struct.state, state);
+}
+
+
+int s5p_cec_open(struct inode *inode, struct file *file)
+{
+ s5p_tv_clk_gate(true);
+
+ hdmi_on = true;
+
+ tv_cec_reset();
+
+ tv_cec_set_divider();
+
+ tv_cec_threshold();
+
+ tv_cec_unmask_tx_interrupts();
+
+ tv_cec_set_rx_state(STATE_RX);
+ tv_cec_unmask_rx_interrupts();
+ tv_cec_enable_rx();
+
+ return 0;
+}
+
+int s5p_cec_release(struct inode *inode, struct file *file)
+{
+ s5p_tv_clk_gate(false);
+
+ hdmi_on = false;
+
+ tv_cec_mask_tx_interrupts();
+ tv_cec_mask_rx_interrupts();
+
+ return 0;
+}
+
+ssize_t s5p_cec_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ ssize_t retval;
+
+ if (wait_event_interruptible(cec_rx_struct.waitq,
+ atomic_read(&cec_rx_struct.state) == STATE_DONE)) {
+ return -ERESTARTSYS;
+ }
+
+ spin_lock_irq(&cec_rx_struct.lock);
+
+ if (cec_rx_struct.size > count) {
+ spin_unlock_irq(&cec_rx_struct.lock);
+ return -1;
+ }
+
+ if (copy_to_user(buffer, cec_rx_struct.buffer, cec_rx_struct.size)) {
+ spin_unlock_irq(&cec_rx_struct.lock);
+ printk(KERN_ERR " copy_to_user() failed!\n");
+ return -EFAULT;
+ }
+
+ retval = cec_rx_struct.size;
+
+ tv_cec_set_rx_state(STATE_RX);
+ spin_unlock_irq(&cec_rx_struct.lock);
+
+ return retval;
+}
+
+ssize_t s5p_cec_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ char *data;
+
+ /* check data size */
+
+ if (count > CEC_TX_BUFF_SIZE || count == 0)
+ return -1;
+
+ data = kmalloc(count, GFP_KERNEL);
+
+ if (!data) {
+ printk(KERN_ERR " kmalloc() failed!\n");
+ return -1;
+ }
+
+ if (copy_from_user(data, buffer, count)) {
+ printk(KERN_ERR " copy_from_user() failed!\n");
+ kfree(data);
+ return -EFAULT;
+ }
+
+ tv_cec_copy_packet(data, count);
+
+ kfree(data);
+
+ /* wait for interrupt */
+ if (wait_event_interruptible(cec_tx_struct.waitq,
+ atomic_read(&cec_tx_struct.state) != STATE_TX)) {
+ return -ERESTARTSYS;
+ }
+
+ if (atomic_read(&cec_tx_struct.state) == STATE_ERROR)
+ return -1;
+
+
+ return count;
+}
+
+int s5p_cec_ioctl(struct inode *inode, struct file *file,
+ u32 cmd, unsigned long arg)
+{
+ u32 laddr;
+
+ switch (cmd) {
+
+ case CEC_IOC_SETLADDR:
+ CECIFPRINTK("ioctl(CEC_IOC_SETLADDR)\n");
+
+ if (get_user(laddr, (u32 __user *) arg))
+ return -EFAULT;
+
+ CECIFPRINTK("logical address = 0x%02x\n", laddr);
+
+ tv_cec_set_addr(laddr);
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+u32 s5p_cec_poll(struct file *file, poll_table *wait)
+{
+ poll_wait(file, &cec_rx_struct.waitq, wait);
+
+ if (atomic_read(&cec_rx_struct.state) == STATE_DONE)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static const struct file_operations cec_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_cec_open,
+ .release = s5p_cec_release,
+ .read = s5p_cec_read,
+ .write = s5p_cec_write,
+ .ioctl = s5p_cec_ioctl,
+ .poll = s5p_cec_poll,
+};
+
+static struct miscdevice cec_misc_device = {
+ .minor = CEC_MINOR,
+ .name = "CEC",
+ .fops = &cec_fops,
+};
+
+
+/**
+ * @brief CEC interrupt handler
+ *
+ * Handles interrupt requests from CEC hardware. \n
+ * Action depends on current state of CEC hardware.
+ */
+irqreturn_t s5p_cec_irq_handler(int irq, void *dev_id)
+{
+/* u8 flag; */
+ u32 status = 0;
+
+ status = tv_cec_get_status();
+
+ if (status & CEC_STATUS_TX_DONE) {
+ if (status & CEC_STATUS_TX_ERROR) {
+ CECIFPRINTK(" CEC_STATUS_TX_ERROR!\n");
+ tv_cec_set_tx_state(STATE_ERROR);
+ } else {
+ CECIFPRINTK(" CEC_STATUS_TX_DONE!\n");
+ tv_cec_set_tx_state(STATE_DONE);
+ }
+
+ tv_clr_pending_tx();
+
+ wake_up_interruptible(&cec_tx_struct.waitq);
+ }
+
+ if (status & CEC_STATUS_RX_DONE) {
+ if (status & CEC_STATUS_RX_ERROR) {
+ CECIFPRINTK(" CEC_STATUS_RX_ERROR!\n");
+ tv_cec_rx_reset();
+
+ } else {
+ u32 size;
+
+ CECIFPRINTK(" CEC_STATUS_RX_DONE!\n");
+
+ /* copy data from internal buffer */
+ size = status >> 24;
+
+ spin_lock(&cec_rx_struct.lock);
+
+ tv_cec_get_rx_buf(size, cec_rx_struct.buffer);
+
+ cec_rx_struct.size = size;
+
+ tv_cec_set_rx_state(STATE_DONE);
+
+ spin_unlock(&cec_rx_struct.lock);
+
+ tv_cec_enable_rx();
+ }
+
+ /* clear interrupt pending bit */
+ tv_clr_pending_rx();
+
+ wake_up_interruptible(&cec_rx_struct.waitq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __init s5p_cec_probe(struct platform_device *pdev)
+{
+ u8 *buffer;
+ int irq_num;
+ int ret;
+
+ s3c_gpio_cfgpin(S5PV210_GPH1(4), S5P_GPH1_4_HDMI_CEC);
+ s3c_gpio_setpull(S5PV210_GPH1(4), S3C_GPIO_PULL_NONE);
+
+ /* get ioremap addr */
+ tv_cec_probe(pdev);
+
+ if (misc_register(&cec_misc_device)) {
+ printk(KERN_WARNING
+ "Couldn't register device 10, %d.\n", CEC_MINOR);
+ return -EBUSY;
+ }
+
+ irq_num = platform_get_irq(pdev, 0);
+ if (irq_num < 0) {
+ printk(KERN_ERR "failed to get %s irq resource\n", "cec");
+ ret = -ENOENT;
+ return ret;
+ }
+
+ ret = request_irq(irq_num, s5p_cec_irq_handler, IRQF_DISABLED,
+ pdev->name, &pdev->id);
+ if (ret != 0) {
+ printk(KERN_ERR "failed to install %s irq (%d)\n", "cec", ret);
+ return ret;
+ }
+
+ init_waitqueue_head(&cec_rx_struct.waitq);
+ spin_lock_init(&cec_rx_struct.lock);
+ init_waitqueue_head(&cec_tx_struct.waitq);
+
+ buffer = kmalloc(CEC_TX_BUFF_SIZE, GFP_KERNEL);
+
+ if (!buffer) {
+ printk(KERN_ERR " kmalloc() failed!\n");
+ misc_deregister(&cec_misc_device);
+ return -EIO;
+ }
+
+ cec_rx_struct.buffer = buffer;
+
+ cec_rx_struct.size = 0;
+
+ return 0;
+}
+
+static int s5p_cec_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+int s5p_cec_suspend(struct platform_device *dev, pm_message_t state)
+{
+ if (hdmi_on)
+ s5p_tv_clk_gate(false);
+
+ return 0;
+}
+
+int s5p_cec_resume(struct platform_device *dev)
+{
+ if (hdmi_on)
+ s5p_tv_clk_gate(true);
+
+ return 0;
+}
+#else
+#define s5p_cec_suspend NULL
+#define s5p_cec_resume NULL
+#endif
+
+static struct platform_driver s5p_cec_driver = {
+ .probe = s5p_cec_probe,
+ .remove = s5p_cec_remove,
+ .suspend = s5p_cec_suspend,
+ .resume = s5p_cec_resume,
+ .driver = {
+ .name = "s5p-cec",
+ .owner = THIS_MODULE,
+ },
+};
+
+static char banner[] __initdata =
+ "S5PV210 CEC Driver, (c) 2010 Samsung Electronics\n";
+
+int __init s5p_cec_init(void)
+{
+ int ret;
+
+ printk(banner);
+
+ ret = platform_driver_register(&s5p_cec_driver);
+
+ if (ret) {
+ printk(KERN_ERR "Platform Device Register Failed %d\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __exit s5p_cec_exit(void)
+{
+ kfree(cec_rx_struct.buffer);
+
+ platform_driver_unregister(&s5p_cec_driver);
+
+}
+
+module_init(s5p_cec_init);
+module_exit(s5p_cec_exit);
+
+MODULE_AUTHOR("SangPil Moon");
+MODULE_DESCRIPTION("S5PV210 CEC driver");
+MODULE_LICENSE("GPL");
« no previous file with comments | « drivers/media/video/samsung/tv20/cec.h ('k') | drivers/media/video/samsung/tv20/cec_s5pv210.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698