Initial commit
[kernel/linux-3.0.git] / drivers / media / video / exynos / tv / hdmi_cec.c
1 /* linux/drivers/media/video/samsung/tvout/s5p_cec_ctrl.c
2  *
3  * Copyright (c) 2009 Samsung Electronics
4  *              http://www.samsung.com/
5  *
6  * cec interface file for Samsung TVOut driver
7  *
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.
11 */
12
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>
22
23 #include "cec.h"
24
25 #define CEC_IOC_MAGIC        'c'
26 #define CEC_IOC_SETLADDR     _IOW(CEC_IOC_MAGIC, 0, unsigned int)
27
28 #define VERSION   "1.0" /* Driver version number */
29 #define CEC_MINOR 243   /* Major 10, Minor 242, /dev/cec */
30
31
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)
43
44
45 /* CEC Rx buffer size */
46 #define CEC_RX_BUFF_SIZE            16
47 /* CEC Tx buffer size */
48 #define CEC_TX_BUFF_SIZE            16
49
50 #define TV_CLK_GET_WITH_ERR_CHECK(clk, pdev, clk_name)                  \
51                 do {                                                    \
52                         clk = clk_get(&pdev->dev, clk_name);            \
53                         if (IS_ERR(clk)) {                              \
54                                 printk(KERN_ERR                         \
55                                 "failed to find clock %s\n", clk_name); \
56                                 return -ENOENT;                         \
57                         }                                               \
58                 } while (0);
59
60 static atomic_t hdmi_on = ATOMIC_INIT(0);
61 static DEFINE_MUTEX(cec_lock);
62 struct clk *hdmi_cec_clk;
63
64 static int s5p_cec_open(struct inode *inode, struct file *file)
65 {
66         int ret = 0;
67
68         mutex_lock(&cec_lock);
69         clk_enable(hdmi_cec_clk);
70
71         if (atomic_read(&hdmi_on)) {
72                 tvout_dbg("do not allow multiple open for tvout cec\n");
73                 ret = -EBUSY;
74                 goto err_multi_open;
75         } else
76                 atomic_inc(&hdmi_on);
77
78         s5p_cec_reset();
79
80         s5p_cec_set_divider();
81
82         s5p_cec_threshold();
83
84         s5p_cec_unmask_tx_interrupts();
85
86         s5p_cec_set_rx_state(STATE_RX);
87         s5p_cec_unmask_rx_interrupts();
88         s5p_cec_enable_rx();
89
90 err_multi_open:
91         mutex_unlock(&cec_lock);
92
93         return ret;
94 }
95
96 static int s5p_cec_release(struct inode *inode, struct file *file)
97 {
98         atomic_dec(&hdmi_on);
99
100         s5p_cec_mask_tx_interrupts();
101         s5p_cec_mask_rx_interrupts();
102
103         clk_disable(hdmi_cec_clk);
104         clk_put(hdmi_cec_clk);
105
106         return 0;
107 }
108
109 static ssize_t s5p_cec_read(struct file *file, char __user *buffer,
110                         size_t count, loff_t *ppos)
111 {
112         ssize_t retval;
113         unsigned long spin_flags;
114
115         if (wait_event_interruptible(cec_rx_struct.waitq,
116                         atomic_read(&cec_rx_struct.state) == STATE_DONE)) {
117                 return -ERESTARTSYS;
118         }
119         spin_lock_irqsave(&cec_rx_struct.lock, spin_flags);
120
121         if (cec_rx_struct.size > count) {
122                 spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags);
123
124                 return -1;
125         }
126
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");
130
131                 return -EFAULT;
132         }
133
134         retval = cec_rx_struct.size;
135
136         s5p_cec_set_rx_state(STATE_RX);
137         spin_unlock_irqrestore(&cec_rx_struct.lock, spin_flags);
138
139         return retval;
140 }
141
142 static ssize_t s5p_cec_write(struct file *file, const char __user *buffer,
143                         size_t count, loff_t *ppos)
144 {
145         char *data;
146
147         /* check data size */
148
149         if (count > CEC_TX_BUFF_SIZE || count == 0)
150                 return -1;
151
152         data = kmalloc(count, GFP_KERNEL);
153
154         if (!data) {
155                 printk(KERN_ERR " kmalloc() failed!\n");
156
157                 return -1;
158         }
159
160         if (copy_from_user(data, buffer, count)) {
161                 printk(KERN_ERR " copy_from_user() failed!\n");
162                 kfree(data);
163
164                 return -EFAULT;
165         }
166
167         s5p_cec_copy_packet(data, count);
168
169         kfree(data);
170
171         /* wait for interrupt */
172         if (wait_event_interruptible(cec_tx_struct.waitq,
173                 atomic_read(&cec_tx_struct.state)
174                 != STATE_TX)) {
175
176                 return -ERESTARTSYS;
177         }
178
179         if (atomic_read(&cec_tx_struct.state) == STATE_ERROR)
180                 return -1;
181
182         return count;
183 }
184
185 #if 0
186 static int s5p_cec_ioctl(struct inode *inode, struct file *file, u32 cmd,
187                         unsigned long arg)
188 #else
189 static long s5p_cec_ioctl(struct file *file, unsigned int cmd,
190                                                 unsigned long arg)
191 #endif
192 {
193         u32 laddr;
194
195         switch (cmd) {
196         case CEC_IOC_SETLADDR:
197                 if (get_user(laddr, (u32 __user *) arg))
198                         return -EFAULT;
199
200                 tvout_dbg("logical address = 0x%02x\n", laddr);
201
202                 s5p_cec_set_addr(laddr);
203                 break;
204
205         default:
206                 return -EINVAL;
207         }
208
209         return 0;
210 }
211
212 static u32 s5p_cec_poll(struct file *file, poll_table *wait)
213 {
214         poll_wait(file, &cec_rx_struct.waitq, wait);
215
216         if (atomic_read(&cec_rx_struct.state) == STATE_DONE)
217                 return POLLIN | POLLRDNORM;
218
219         return 0;
220 }
221
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,
228 #if 1
229         .unlocked_ioctl = s5p_cec_ioctl,
230 #else
231         .ioctl   = s5p_cec_ioctl,
232 #endif
233         .poll    = s5p_cec_poll,
234 };
235
236 static struct miscdevice cec_misc_device = {
237         .minor = CEC_MINOR,
238         .name  = "CEC",
239         .fops  = &cec_fops,
240 };
241
242 static irqreturn_t s5p_cec_irq_handler(int irq, void *dev_id)
243 {
244
245         u32 status = 0;
246
247         status = s5p_cec_get_status();
248
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);
253                 } else {
254                         tvout_dbg(" CEC_STATUS_TX_DONE!\n");
255                         s5p_cec_set_tx_state(STATE_DONE);
256                 }
257
258                 s5p_clr_pending_tx();
259
260                 wake_up_interruptible(&cec_tx_struct.waitq);
261         }
262
263         if (status & CEC_STATUS_RX_DONE) {
264                 if (status & CEC_STATUS_RX_ERROR) {
265                         tvout_dbg(" CEC_STATUS_RX_ERROR!\n");
266                         s5p_cec_rx_reset();
267
268                 } else {
269                         u32 size;
270
271                         tvout_dbg(" CEC_STATUS_RX_DONE!\n");
272
273                         /* copy data from internal buffer */
274                         size = status >> 24;
275
276                         spin_lock(&cec_rx_struct.lock);
277
278                         s5p_cec_get_rx_buf(size, cec_rx_struct.buffer);
279
280                         cec_rx_struct.size = size;
281
282                         s5p_cec_set_rx_state(STATE_DONE);
283
284                         spin_unlock(&cec_rx_struct.lock);
285
286                         s5p_cec_enable_rx();
287                 }
288
289                 /* clear interrupt pending bit */
290                 s5p_clr_pending_rx();
291
292                 wake_up_interruptible(&cec_rx_struct.waitq);
293         }
294
295         return IRQ_HANDLED;
296 }
297
298 static int __devinit s5p_cec_probe(struct platform_device *pdev)
299 {
300         struct s5p_platform_cec *pdata;
301         u8 *buffer;
302         int ret;
303         struct resource *res;
304
305         pdata = to_tvout_plat(&pdev->dev);
306
307         if (pdata->cfg_gpio)
308                 pdata->cfg_gpio(pdev);
309
310
311         s5p_cec_mem_probe(pdev);
312
313         if (misc_register(&cec_misc_device)) {
314                 printk(KERN_WARNING " Couldn't register device 10, %d.\n",
315                         CEC_MINOR);
316
317                 return -EBUSY;
318         }
319
320 #if 0
321         irq_num = platform_get_irq(pdev, 0);
322
323         if (irq_num < 0) {
324                 printk(KERN_ERR  "failed to get %s irq resource\n", "cec");
325                 ret = -ENOENT;
326
327                 return ret;
328         }
329 #endif
330
331         res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
332         if (res == NULL) {
333                 dev_err(&pdev->dev, "failed to get irq resource.\n");
334                 ret = -ENOENT;
335                 return ret;
336         }
337
338         ret = request_irq(res->start, s5p_cec_irq_handler, IRQF_DISABLED,
339                 pdev->name, &pdev->id);
340
341         if (ret != 0) {
342                 printk(KERN_ERR  "failed to install %s irq (%d)\n", "cec", ret);
343
344                 return ret;
345         }
346
347         init_waitqueue_head(&cec_rx_struct.waitq);
348         spin_lock_init(&cec_rx_struct.lock);
349         init_waitqueue_head(&cec_tx_struct.waitq);
350
351         buffer = kmalloc(CEC_TX_BUFF_SIZE, GFP_KERNEL);
352
353         if (!buffer) {
354                 printk(KERN_ERR " kmalloc() failed!\n");
355                 misc_deregister(&cec_misc_device);
356
357                 return -EIO;
358         }
359
360         cec_rx_struct.buffer = buffer;
361
362         cec_rx_struct.size   = 0;
363         TV_CLK_GET_WITH_ERR_CHECK(hdmi_cec_clk, pdev, "sclk_cec");
364
365         dev_info(&pdev->dev, "probe successful\n");
366
367         return 0;
368 }
369
370 static int __devexit s5p_cec_remove(struct platform_device *pdev)
371 {
372         return 0;
373 }
374
375 #ifdef CONFIG_PM
376 static int s5p_cec_suspend(struct platform_device *dev, pm_message_t state)
377 {
378         return 0;
379 }
380
381 static int s5p_cec_resume(struct platform_device *dev)
382 {
383         return 0;
384 }
385 #else
386 #define s5p_cec_suspend NULL
387 #define s5p_cec_resume NULL
388 #endif
389
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,
395         .driver         = {
396                 .name   = "s5p-tvout-cec",
397                 .owner  = THIS_MODULE,
398         },
399 };
400
401 static char banner[] __initdata =
402         "S5P CEC for Exynos4 Driver, (c) 2009 Samsung Electronics\n";
403
404 static int __init s5p_cec_init(void)
405 {
406         int ret;
407
408         printk(banner);
409
410         ret = platform_driver_register(&s5p_cec_driver);
411
412         if (ret) {
413                 printk(KERN_ERR "Platform Device Register Failed %d\n", ret);
414
415                 return -1;
416         }
417
418         return 0;
419 }
420
421 static void __exit s5p_cec_exit(void)
422 {
423         kfree(cec_rx_struct.buffer);
424
425         platform_driver_unregister(&s5p_cec_driver);
426 }
427
428 module_init(s5p_cec_init);
429 module_exit(s5p_cec_exit);
430
431 MODULE_AUTHOR("SangPil Moon");
432 MODULE_DESCRIPTION("S5P CEC driver");
433 MODULE_LICENSE("GPL");