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