Initial commit
[kernel/linux-3.0.git] / drivers / media / video / exynos / tv / hdmi_cec_ctrl.c
1 /* linux/drivers/media/video/samsung/tvout/hw_if/cec.c
2  *
3  * Copyright (c) 2009 Samsung Electronics
4  *              http://www.samsung.com/
5  *
6  * cec ftn 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/io.h>
14 #include <linux/slab.h>
15 #include <linux/platform_device.h>
16 #include <linux/videodev2.h>
17 #include <linux/videodev2_exynos_camera.h>
18 #include <linux/irqreturn.h>
19 #include <linux/stddef.h>
20
21 #include <mach/regs-clock.h>
22 #include <mach/regs-clock.h>
23 #include <mach/regs-cec.h>
24
25 #include "cec.h"
26
27 #undef tvout_dbg
28
29 #ifdef CONFIG_CEC_DEBUG
30 #define tvout_dbg(fmt, ...)                                     \
31                 printk(KERN_INFO "\t\t[CEC] %s(): " fmt,        \
32                         __func__, ##__VA_ARGS__)
33 #else
34 #define tvout_dbg(fmt, ...)
35 #endif
36
37 #define S5P_HDMI_FIN                    24000000
38 #define CEC_DIV_RATIO                   320000
39
40 #define CEC_MESSAGE_BROADCAST_MASK      0x0F
41 #define CEC_MESSAGE_BROADCAST           0x0F
42 #define CEC_FILTER_THRESHOLD            0x15
43
44 static struct resource  *cec_mem;
45 void __iomem            *cec_base;
46
47 struct cec_rx_struct cec_rx_struct;
48 struct cec_tx_struct cec_tx_struct;
49
50 void s5p_cec_set_divider(void)
51 {
52         u32 div_ratio, reg, div_val;
53
54         div_ratio  = S5P_HDMI_FIN / CEC_DIV_RATIO - 1;
55
56         reg = readl(S5P_HDMI_PHY_CONTROL);
57         reg = (reg & ~(0x3FF << 16)) | (div_ratio << 16);
58
59         writel(reg, S5P_HDMI_PHY_CONTROL);
60
61         div_val = CEC_DIV_RATIO * 0.00005 - 1;
62
63         writeb(0x0, cec_base + S5P_CES_DIVISOR_3);
64         writeb(0x0, cec_base + S5P_CES_DIVISOR_2);
65         writeb(0x0, cec_base + S5P_CES_DIVISOR_1);
66         writeb(div_val, cec_base + S5P_CES_DIVISOR_0);
67 }
68
69 void s5p_cec_enable_rx(void)
70 {
71         u8 reg;
72
73         reg = readb(cec_base + S5P_CES_RX_CTRL);
74         reg |= S5P_CES_RX_CTRL_ENABLE;
75         writeb(reg, cec_base + S5P_CES_RX_CTRL);
76 }
77
78 void s5p_cec_mask_rx_interrupts(void)
79 {
80         u8 reg;
81
82         reg = readb(cec_base + S5P_CES_IRQ_MASK);
83         reg |= S5P_CES_IRQ_RX_DONE;
84         reg |= S5P_CES_IRQ_RX_ERROR;
85         writeb(reg, cec_base + S5P_CES_IRQ_MASK);
86 }
87
88 void s5p_cec_unmask_rx_interrupts(void)
89 {
90         u8 reg;
91
92         reg = readb(cec_base + S5P_CES_IRQ_MASK);
93         reg &= ~S5P_CES_IRQ_RX_DONE;
94         reg &= ~S5P_CES_IRQ_RX_ERROR;
95         writeb(reg, cec_base + S5P_CES_IRQ_MASK);
96 }
97
98 void s5p_cec_mask_tx_interrupts(void)
99 {
100         u8 reg;
101         reg = readb(cec_base + S5P_CES_IRQ_MASK);
102         reg |= S5P_CES_IRQ_TX_DONE;
103         reg |= S5P_CES_IRQ_TX_ERROR;
104         writeb(reg, cec_base + S5P_CES_IRQ_MASK);
105
106 }
107
108 void s5p_cec_unmask_tx_interrupts(void)
109 {
110         u8 reg;
111
112         reg = readb(cec_base + S5P_CES_IRQ_MASK);
113         reg &= ~S5P_CES_IRQ_TX_DONE;
114         reg &= ~S5P_CES_IRQ_TX_ERROR;
115         writeb(reg, cec_base + S5P_CES_IRQ_MASK);
116 }
117
118 void s5p_cec_reset(void)
119 {
120         writeb(S5P_CES_RX_CTRL_RESET, cec_base + S5P_CES_RX_CTRL);
121         writeb(S5P_CES_TX_CTRL_RESET, cec_base + S5P_CES_TX_CTRL);
122 }
123
124 void s5p_cec_tx_reset(void)
125 {
126         writeb(S5P_CES_TX_CTRL_RESET, cec_base + S5P_CES_TX_CTRL);
127 }
128
129 void s5p_cec_rx_reset(void)
130 {
131         writeb(S5P_CES_RX_CTRL_RESET, cec_base + S5P_CES_RX_CTRL);
132 }
133
134 void s5p_cec_threshold(void)
135 {
136         writeb(CEC_FILTER_THRESHOLD, cec_base + S5P_CES_RX_FILTER_TH);
137         writeb(0, cec_base + S5P_CES_RX_FILTER_CTRL);
138 }
139
140 void s5p_cec_set_tx_state(enum cec_state state)
141 {
142         atomic_set(&cec_tx_struct.state, state);
143 }
144
145 void s5p_cec_set_rx_state(enum cec_state state)
146 {
147         atomic_set(&cec_rx_struct.state, state);
148 }
149
150 void s5p_cec_copy_packet(char *data, size_t count)
151 {
152         int i = 0;
153         u8 reg;
154
155         while (i < count) {
156                 writeb(data[i], cec_base + (S5P_CES_TX_BUFF0 + (i * 4)));
157                 i++;
158         }
159
160         writeb(count, cec_base + S5P_CES_TX_BYTES);
161         s5p_cec_set_tx_state(STATE_TX);
162         reg = readb(cec_base + S5P_CES_TX_CTRL);
163         reg |= S5P_CES_TX_CTRL_START;
164
165         if ((data[0] & CEC_MESSAGE_BROADCAST_MASK) == CEC_MESSAGE_BROADCAST)
166                 reg |= S5P_CES_TX_CTRL_BCAST;
167         else
168                 reg &= ~S5P_CES_TX_CTRL_BCAST;
169
170         reg |= 0x50;
171         writeb(reg, cec_base + S5P_CES_TX_CTRL);
172 }
173
174 void s5p_cec_set_addr(u32 addr)
175 {
176         writeb(addr & 0x0F, cec_base + S5P_CES_LOGIC_ADDR);
177 }
178
179 u32 s5p_cec_get_status(void)
180 {
181         u32 status = 0;
182
183         status = readb(cec_base + S5P_CES_STATUS_0);
184         status |= readb(cec_base + S5P_CES_STATUS_1) << 8;
185         status |= readb(cec_base + S5P_CES_STATUS_2) << 16;
186         status |= readb(cec_base + S5P_CES_STATUS_3) << 24;
187
188         tvout_dbg("status = 0x%x!\n", status);
189
190         return status;
191 }
192
193 void s5p_clr_pending_tx(void)
194 {
195         writeb(S5P_CES_IRQ_TX_DONE | S5P_CES_IRQ_TX_ERROR,
196                                         cec_base + S5P_CES_IRQ_CLEAR);
197 }
198
199 void s5p_clr_pending_rx(void)
200 {
201         writeb(S5P_CES_IRQ_RX_DONE | S5P_CES_IRQ_RX_ERROR,
202                                         cec_base + S5P_CES_IRQ_CLEAR);
203 }
204
205 void s5p_cec_get_rx_buf(u32 size, u8 *buffer)
206 {
207         u32 i = 0;
208
209         while (i < size) {
210                 buffer[i] = readb(cec_base + S5P_CES_RX_BUFF0 + (i * 4));
211                 i++;
212         }
213 }
214
215 int __init s5p_cec_mem_probe(struct platform_device *pdev)
216 {
217         struct resource *res;
218         size_t  size;
219         int     ret;
220
221         dev_dbg(&pdev->dev, "%s\n", __func__);
222
223         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
224
225         if (res == NULL) {
226                 dev_err(&pdev->dev,
227                         "failed to get memory region resource for cec\n");
228                 return -ENOENT;
229         }
230
231         size = (res->end - res->start) + 1;
232         cec_mem = request_mem_region(res->start, size, pdev->name);
233
234         if (cec_mem == NULL) {
235                 dev_err(&pdev->dev,
236                         "failed to get memory region for cec\n");
237                 return -ENOENT;
238         }
239
240         cec_base = ioremap(res->start, size);
241
242         if (cec_base == NULL) {
243                 dev_err(&pdev->dev,
244                         "failed to ioremap address region for cec\n");
245                 return -ENOENT;
246         }
247
248         return ret;
249 }
250
251 int __init s5p_cec_mem_release(struct platform_device *pdev)
252 {
253         iounmap(cec_base);
254
255         if (cec_mem != NULL) {
256                 if (release_resource(cec_mem))
257                         dev_err(&pdev->dev,
258                                 "Can't remove tvout drv !!\n");
259
260                 kfree(cec_mem);
261
262                 cec_mem = NULL;
263         }
264
265         return 0;
266 }