1 /* linux/drivers/media/video/samsung/tvout/s5p_cec_ctrl.c
3 * Copyright (c) 2009 Samsung Electronics
4 * http://www.samsung.com/
6 * cec interface file for Samsung TVOut driver
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
13 #include <linux/slab.h>
14 #include <linux/uaccess.h>
15 #include <linux/poll.h>
16 #include <linux/miscdevice.h>
17 #include <linux/clk.h>
18 #include <linux/sched.h>
19 #include <linux/platform_device.h>
20 #include <linux/interrupt.h>
21 #include <plat/tvout.h>
25 #define CEC_IOC_MAGIC 'c'
26 #define CEC_IOC_SETLADDR _IOW(CEC_IOC_MAGIC, 0, unsigned int)
28 #define VERSION "1.0" /* Driver version number */
29 #define CEC_MINOR 243 /* Major 10, Minor 242, /dev/cec */
32 #define CEC_STATUS_TX_RUNNING (1<<0)
33 #define CEC_STATUS_TX_TRANSFERRING (1<<1)
34 #define CEC_STATUS_TX_DONE (1<<2)
35 #define CEC_STATUS_TX_ERROR (1<<3)
36 #define CEC_STATUS_TX_BYTES (0xFF<<8)
37 #define CEC_STATUS_RX_RUNNING (1<<16)
38 #define CEC_STATUS_RX_RECEIVING (1<<17)
39 #define CEC_STATUS_RX_DONE (1<<18)
40 #define CEC_STATUS_RX_ERROR (1<<19)
41 #define CEC_STATUS_RX_BCAST (1<<20)
42 #define CEC_STATUS_RX_BYTES (0xFF<<24)
45 /* CEC Rx buffer size */
46 #define CEC_RX_BUFF_SIZE 16
47 /* CEC Tx buffer size */
48 #define CEC_TX_BUFF_SIZE 16
50 #define TV_CLK_GET_WITH_ERR_CHECK(clk, pdev, clk_name) \
52 clk = clk_get(&pdev->dev, clk_name); \
55 "failed to find clock %s\n", clk_name); \
60 static atomic_t hdmi_on = ATOMIC_INIT(0);
61 static DEFINE_MUTEX(cec_lock);
62 struct clk *hdmi_cec_clk;
64 static int s5p_cec_open(struct inode *inode, struct file *file)
68 mutex_lock(&cec_lock);
69 clk_enable(hdmi_cec_clk);
71 if (atomic_read(&hdmi_on)) {
72 tvout_dbg("do not allow multiple open for tvout cec\n");
80 s5p_cec_set_divider();
84 s5p_cec_unmask_tx_interrupts();
86 s5p_cec_set_rx_state(STATE_RX);
87 s5p_cec_unmask_rx_interrupts();
91 mutex_unlock(&cec_lock);
96 static int s5p_cec_release(struct inode *inode, struct file *file)
100 s5p_cec_mask_tx_interrupts();
101 s5p_cec_mask_rx_interrupts();
103 clk_disable(hdmi_cec_clk);
104 clk_put(hdmi_cec_clk);
109 static ssize_t s5p_cec_read(struct file *file, char __user *buffer,
110 size_t count, loff_t *ppos)
113 unsigned long spin_flags;
115 if (wait_event_interruptible(cec_rx_struct.waitq,
116 atomic_read(&cec_rx_struct.state) == STATE_DONE)) {
119 spin_lock_irqsave(&cec_rx_struct.lock, spin_flags);
121 if (cec_rx_struct.size > count) {
122 spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags);
127 if (copy_to_user(buffer, cec_rx_struct.buffer, cec_rx_struct.size)) {
128 spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags);
129 printk(KERN_ERR " copy_to_user() failed!\n");
134 retval = cec_rx_struct.size;
136 s5p_cec_set_rx_state(STATE_RX);
137 spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags);
142 static ssize_t s5p_cec_write(struct file *file, const char __user *buffer,
143 size_t count, loff_t *ppos)
147 /* check data size */
149 if (count > CEC_TX_BUFF_SIZE || count == 0)
152 data = kmalloc(count, GFP_KERNEL);
155 printk(KERN_ERR " kmalloc() failed!\n");
160 if (copy_from_user(data, buffer, count)) {
161 printk(KERN_ERR " copy_from_user() failed!\n");
167 s5p_cec_copy_packet(data, count);
171 /* wait for interrupt */
172 if (wait_event_interruptible(cec_tx_struct.waitq,
173 atomic_read(&cec_tx_struct.state)
179 if (atomic_read(&cec_tx_struct.state) == STATE_ERROR)
186 static int s5p_cec_ioctl(struct inode *inode, struct file *file, u32 cmd,
189 static long s5p_cec_ioctl(struct file *file, unsigned int cmd,
196 case CEC_IOC_SETLADDR:
197 if (get_user(laddr, (u32 __user *) arg))
200 tvout_dbg("logical address = 0x%02x\n", laddr);
202 s5p_cec_set_addr(laddr);
212 static u32 s5p_cec_poll(struct file *file, poll_table *wait)
214 poll_wait(file, &cec_rx_struct.waitq, wait);
216 if (atomic_read(&cec_rx_struct.state) == STATE_DONE)
217 return POLLIN | POLLRDNORM;
222 static const struct file_operations cec_fops = {
223 .owner = THIS_MODULE,
224 .open = s5p_cec_open,
225 .release = s5p_cec_release,
226 .read = s5p_cec_read,
227 .write = s5p_cec_write,
229 .unlocked_ioctl = s5p_cec_ioctl,
231 .ioctl = s5p_cec_ioctl,
233 .poll = s5p_cec_poll,
236 static struct miscdevice cec_misc_device = {
242 static irqreturn_t s5p_cec_irq_handler(int irq, void *dev_id)
247 status = s5p_cec_get_status();
249 if (status & CEC_STATUS_TX_DONE) {
250 if (status & CEC_STATUS_TX_ERROR) {
251 tvout_dbg(" CEC_STATUS_TX_ERROR!\n");
252 s5p_cec_set_tx_state(STATE_ERROR);
254 tvout_dbg(" CEC_STATUS_TX_DONE!\n");
255 s5p_cec_set_tx_state(STATE_DONE);
258 s5p_clr_pending_tx();
260 wake_up_interruptible(&cec_tx_struct.waitq);
263 if (status & CEC_STATUS_RX_DONE) {
264 if (status & CEC_STATUS_RX_ERROR) {
265 tvout_dbg(" CEC_STATUS_RX_ERROR!\n");
271 tvout_dbg(" CEC_STATUS_RX_DONE!\n");
273 /* copy data from internal buffer */
276 spin_lock(&cec_rx_struct.lock);
278 s5p_cec_get_rx_buf(size, cec_rx_struct.buffer);
280 cec_rx_struct.size = size;
282 s5p_cec_set_rx_state(STATE_DONE);
284 spin_unlock(&cec_rx_struct.lock);
289 /* clear interrupt pending bit */
290 s5p_clr_pending_rx();
292 wake_up_interruptible(&cec_rx_struct.waitq);
298 static int __devinit s5p_cec_probe(struct platform_device *pdev)
300 struct s5p_platform_cec *pdata;
303 struct resource *res;
305 pdata = to_tvout_plat(&pdev->dev);
308 pdata->cfg_gpio(pdev);
311 s5p_cec_mem_probe(pdev);
313 if (misc_register(&cec_misc_device)) {
314 printk(KERN_WARNING " Couldn't register device 10, %d.\n",
321 irq_num = platform_get_irq(pdev, 0);
324 printk(KERN_ERR "failed to get %s irq resource\n", "cec");
331 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
333 dev_err(&pdev->dev, "failed to get irq resource.\n");
338 ret = request_irq(res->start, s5p_cec_irq_handler, IRQF_DISABLED,
339 pdev->name, &pdev->id);
342 printk(KERN_ERR "failed to install %s irq (%d)\n", "cec", ret);
347 init_waitqueue_head(&cec_rx_struct.waitq);
348 spin_lock_init(&cec_rx_struct.lock);
349 init_waitqueue_head(&cec_tx_struct.waitq);
351 buffer = kmalloc(CEC_TX_BUFF_SIZE, GFP_KERNEL);
354 printk(KERN_ERR " kmalloc() failed!\n");
355 misc_deregister(&cec_misc_device);
360 cec_rx_struct.buffer = buffer;
362 cec_rx_struct.size = 0;
363 TV_CLK_GET_WITH_ERR_CHECK(hdmi_cec_clk, pdev, "sclk_cec");
365 dev_info(&pdev->dev, "probe successful\n");
370 static int __devexit s5p_cec_remove(struct platform_device *pdev)
376 static int s5p_cec_suspend(struct platform_device *dev, pm_message_t state)
381 static int s5p_cec_resume(struct platform_device *dev)
386 #define s5p_cec_suspend NULL
387 #define s5p_cec_resume NULL
390 static struct platform_driver s5p_cec_driver = {
391 .probe = s5p_cec_probe,
392 .remove = __devexit_p(s5p_cec_remove),
393 .suspend = s5p_cec_suspend,
394 .resume = s5p_cec_resume,
396 .name = "s5p-tvout-cec",
397 .owner = THIS_MODULE,
401 static char banner[] __initdata =
402 "S5P CEC for Exynos4 Driver, (c) 2009 Samsung Electronics\n";
404 static int __init s5p_cec_init(void)
410 ret = platform_driver_register(&s5p_cec_driver);
413 printk(KERN_ERR "Platform Device Register Failed %d\n", ret);
421 static void __exit s5p_cec_exit(void)
423 kfree(cec_rx_struct.buffer);
425 platform_driver_unregister(&s5p_cec_driver);
428 module_init(s5p_cec_init);
429 module_exit(s5p_cec_exit);
431 MODULE_AUTHOR("SangPil Moon");
432 MODULE_DESCRIPTION("S5P CEC driver");
433 MODULE_LICENSE("GPL");