diff options
Diffstat (limited to 'drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c')
-rw-r--r-- | drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c new file mode 100644 index 00000000000..5f4da80051b --- /dev/null +++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c | |||
@@ -0,0 +1,343 @@ | |||
1 | /* | ||
2 | * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c | ||
3 | * | ||
4 | * Copyright (c) 2010 Samsung Electronics Co., Ltd. | ||
5 | * http://www.samsung.com/ | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/delay.h> | ||
14 | #include <linux/err.h> | ||
15 | #include <linux/firmware.h> | ||
16 | #include <linux/jiffies.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include "regs-mfc.h" | ||
19 | #include "s5p_mfc_cmd.h" | ||
20 | #include "s5p_mfc_common.h" | ||
21 | #include "s5p_mfc_debug.h" | ||
22 | #include "s5p_mfc_intr.h" | ||
23 | #include "s5p_mfc_pm.h" | ||
24 | |||
25 | static void *s5p_mfc_bitproc_buf; | ||
26 | static size_t s5p_mfc_bitproc_phys; | ||
27 | static unsigned char *s5p_mfc_bitproc_virt; | ||
28 | |||
29 | /* Allocate and load firmware */ | ||
30 | int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) | ||
31 | { | ||
32 | struct firmware *fw_blob; | ||
33 | size_t bank2_base_phys; | ||
34 | void *b_base; | ||
35 | int err; | ||
36 | |||
37 | /* Firmare has to be present as a separate file or compiled | ||
38 | * into kernel. */ | ||
39 | mfc_debug_enter(); | ||
40 | err = request_firmware((const struct firmware **)&fw_blob, | ||
41 | "s5pc110-mfc.fw", dev->v4l2_dev.dev); | ||
42 | if (err != 0) { | ||
43 | mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); | ||
44 | return -EINVAL; | ||
45 | } | ||
46 | dev->fw_size = ALIGN(fw_blob->size, FIRMWARE_ALIGN); | ||
47 | if (s5p_mfc_bitproc_buf) { | ||
48 | mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n"); | ||
49 | release_firmware(fw_blob); | ||
50 | return -ENOMEM; | ||
51 | } | ||
52 | s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc( | ||
53 | dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size); | ||
54 | if (IS_ERR(s5p_mfc_bitproc_buf)) { | ||
55 | s5p_mfc_bitproc_buf = 0; | ||
56 | mfc_err("Allocating bitprocessor buffer failed\n"); | ||
57 | release_firmware(fw_blob); | ||
58 | return -ENOMEM; | ||
59 | } | ||
60 | s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie( | ||
61 | dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf); | ||
62 | if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) { | ||
63 | mfc_err("The base memory for bank 1 is not aligned to 128KB\n"); | ||
64 | vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); | ||
65 | s5p_mfc_bitproc_phys = 0; | ||
66 | s5p_mfc_bitproc_buf = 0; | ||
67 | release_firmware(fw_blob); | ||
68 | return -EIO; | ||
69 | } | ||
70 | s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf); | ||
71 | if (!s5p_mfc_bitproc_virt) { | ||
72 | mfc_err("Bitprocessor memory remap failed\n"); | ||
73 | vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); | ||
74 | s5p_mfc_bitproc_phys = 0; | ||
75 | s5p_mfc_bitproc_buf = 0; | ||
76 | release_firmware(fw_blob); | ||
77 | return -EIO; | ||
78 | } | ||
79 | dev->bank1 = s5p_mfc_bitproc_phys; | ||
80 | b_base = vb2_dma_contig_memops.alloc( | ||
81 | dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], 1 << MFC_BANK2_ALIGN_ORDER); | ||
82 | if (IS_ERR(b_base)) { | ||
83 | vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); | ||
84 | s5p_mfc_bitproc_phys = 0; | ||
85 | s5p_mfc_bitproc_buf = 0; | ||
86 | mfc_err("Allocating bank2 base failed\n"); | ||
87 | release_firmware(fw_blob); | ||
88 | return -ENOMEM; | ||
89 | } | ||
90 | bank2_base_phys = s5p_mfc_mem_cookie( | ||
91 | dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base); | ||
92 | vb2_dma_contig_memops.put(b_base); | ||
93 | if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) { | ||
94 | mfc_err("The base memory for bank 2 is not aligned to 128KB\n"); | ||
95 | vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); | ||
96 | s5p_mfc_bitproc_phys = 0; | ||
97 | s5p_mfc_bitproc_buf = 0; | ||
98 | release_firmware(fw_blob); | ||
99 | return -EIO; | ||
100 | } | ||
101 | dev->bank2 = bank2_base_phys; | ||
102 | memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); | ||
103 | wmb(); | ||
104 | release_firmware(fw_blob); | ||
105 | mfc_debug_leave(); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | /* Reload firmware to MFC */ | ||
110 | int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) | ||
111 | { | ||
112 | struct firmware *fw_blob; | ||
113 | int err; | ||
114 | |||
115 | /* Firmare has to be present as a separate file or compiled | ||
116 | * into kernel. */ | ||
117 | mfc_debug_enter(); | ||
118 | err = request_firmware((const struct firmware **)&fw_blob, | ||
119 | "s5pc110-mfc.fw", dev->v4l2_dev.dev); | ||
120 | if (err != 0) { | ||
121 | mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); | ||
122 | return -EINVAL; | ||
123 | } | ||
124 | if (fw_blob->size > dev->fw_size) { | ||
125 | mfc_err("MFC firmware is too big to be loaded\n"); | ||
126 | release_firmware(fw_blob); | ||
127 | return -ENOMEM; | ||
128 | } | ||
129 | if (s5p_mfc_bitproc_buf == 0 || s5p_mfc_bitproc_phys == 0) { | ||
130 | mfc_err("MFC firmware is not allocated or was not mapped correctly\n"); | ||
131 | release_firmware(fw_blob); | ||
132 | return -EINVAL; | ||
133 | } | ||
134 | memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); | ||
135 | wmb(); | ||
136 | release_firmware(fw_blob); | ||
137 | mfc_debug_leave(); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | /* Release firmware memory */ | ||
142 | int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev) | ||
143 | { | ||
144 | /* Before calling this function one has to make sure | ||
145 | * that MFC is no longer processing */ | ||
146 | if (!s5p_mfc_bitproc_buf) | ||
147 | return -EINVAL; | ||
148 | vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); | ||
149 | s5p_mfc_bitproc_virt = 0; | ||
150 | s5p_mfc_bitproc_phys = 0; | ||
151 | s5p_mfc_bitproc_buf = 0; | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | /* Reset the device */ | ||
156 | int s5p_mfc_reset(struct s5p_mfc_dev *dev) | ||
157 | { | ||
158 | unsigned int mc_status; | ||
159 | unsigned long timeout; | ||
160 | |||
161 | mfc_debug_enter(); | ||
162 | /* Stop procedure */ | ||
163 | /* reset RISC */ | ||
164 | mfc_write(dev, 0x3f6, S5P_FIMV_SW_RESET); | ||
165 | /* All reset except for MC */ | ||
166 | mfc_write(dev, 0x3e2, S5P_FIMV_SW_RESET); | ||
167 | mdelay(10); | ||
168 | |||
169 | timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT); | ||
170 | /* Check MC status */ | ||
171 | do { | ||
172 | if (time_after(jiffies, timeout)) { | ||
173 | mfc_err("Timeout while resetting MFC\n"); | ||
174 | return -EIO; | ||
175 | } | ||
176 | |||
177 | mc_status = mfc_read(dev, S5P_FIMV_MC_STATUS); | ||
178 | |||
179 | } while (mc_status & 0x3); | ||
180 | |||
181 | mfc_write(dev, 0x0, S5P_FIMV_SW_RESET); | ||
182 | mfc_write(dev, 0x3fe, S5P_FIMV_SW_RESET); | ||
183 | mfc_debug_leave(); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev) | ||
188 | { | ||
189 | mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A); | ||
190 | mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B); | ||
191 | mfc_debug(2, "Bank1: %08x, Bank2: %08x\n", dev->bank1, dev->bank2); | ||
192 | } | ||
193 | |||
194 | static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev) | ||
195 | { | ||
196 | mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH0_INST_ID); | ||
197 | mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH1_INST_ID); | ||
198 | mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD); | ||
199 | mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD); | ||
200 | } | ||
201 | |||
202 | /* Initialize hardware */ | ||
203 | int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) | ||
204 | { | ||
205 | unsigned int ver; | ||
206 | int ret; | ||
207 | |||
208 | mfc_debug_enter(); | ||
209 | if (!s5p_mfc_bitproc_buf) | ||
210 | return -EINVAL; | ||
211 | |||
212 | /* 0. MFC reset */ | ||
213 | mfc_debug(2, "MFC reset..\n"); | ||
214 | s5p_mfc_clock_on(); | ||
215 | ret = s5p_mfc_reset(dev); | ||
216 | if (ret) { | ||
217 | mfc_err("Failed to reset MFC - timeout\n"); | ||
218 | return ret; | ||
219 | } | ||
220 | mfc_debug(2, "Done MFC reset..\n"); | ||
221 | /* 1. Set DRAM base Addr */ | ||
222 | s5p_mfc_init_memctrl(dev); | ||
223 | /* 2. Initialize registers of channel I/F */ | ||
224 | s5p_mfc_clear_cmds(dev); | ||
225 | /* 3. Release reset signal to the RISC */ | ||
226 | s5p_mfc_clean_dev_int_flags(dev); | ||
227 | mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET); | ||
228 | mfc_debug(2, "Will now wait for completion of firmware transfer\n"); | ||
229 | if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) { | ||
230 | mfc_err("Failed to load firmware\n"); | ||
231 | s5p_mfc_reset(dev); | ||
232 | s5p_mfc_clock_off(); | ||
233 | return -EIO; | ||
234 | } | ||
235 | s5p_mfc_clean_dev_int_flags(dev); | ||
236 | /* 4. Initialize firmware */ | ||
237 | ret = s5p_mfc_sys_init_cmd(dev); | ||
238 | if (ret) { | ||
239 | mfc_err("Failed to send command to MFC - timeout\n"); | ||
240 | s5p_mfc_reset(dev); | ||
241 | s5p_mfc_clock_off(); | ||
242 | return ret; | ||
243 | } | ||
244 | mfc_debug(2, "Ok, now will write a command to init the system\n"); | ||
245 | if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET)) { | ||
246 | mfc_err("Failed to load firmware\n"); | ||
247 | s5p_mfc_reset(dev); | ||
248 | s5p_mfc_clock_off(); | ||
249 | return -EIO; | ||
250 | } | ||
251 | dev->int_cond = 0; | ||
252 | if (dev->int_err != 0 || dev->int_type != | ||
253 | S5P_FIMV_R2H_CMD_SYS_INIT_RET) { | ||
254 | /* Failure. */ | ||
255 | mfc_err("Failed to init firmware - error: %d int: %d\n", | ||
256 | dev->int_err, dev->int_type); | ||
257 | s5p_mfc_reset(dev); | ||
258 | s5p_mfc_clock_off(); | ||
259 | return -EIO; | ||
260 | } | ||
261 | ver = mfc_read(dev, S5P_FIMV_FW_VERSION); | ||
262 | mfc_debug(2, "MFC F/W version : %02xyy, %02xmm, %02xdd\n", | ||
263 | (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF); | ||
264 | s5p_mfc_clock_off(); | ||
265 | mfc_debug_leave(); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | |||
270 | int s5p_mfc_sleep(struct s5p_mfc_dev *dev) | ||
271 | { | ||
272 | int ret; | ||
273 | |||
274 | mfc_debug_enter(); | ||
275 | s5p_mfc_clock_on(); | ||
276 | s5p_mfc_clean_dev_int_flags(dev); | ||
277 | ret = s5p_mfc_sleep_cmd(dev); | ||
278 | if (ret) { | ||
279 | mfc_err("Failed to send command to MFC - timeout\n"); | ||
280 | return ret; | ||
281 | } | ||
282 | if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SLEEP_RET)) { | ||
283 | mfc_err("Failed to sleep\n"); | ||
284 | return -EIO; | ||
285 | } | ||
286 | s5p_mfc_clock_off(); | ||
287 | dev->int_cond = 0; | ||
288 | if (dev->int_err != 0 || dev->int_type != | ||
289 | S5P_FIMV_R2H_CMD_SLEEP_RET) { | ||
290 | /* Failure. */ | ||
291 | mfc_err("Failed to sleep - error: %d int: %d\n", dev->int_err, | ||
292 | dev->int_type); | ||
293 | return -EIO; | ||
294 | } | ||
295 | mfc_debug_leave(); | ||
296 | return ret; | ||
297 | } | ||
298 | |||
299 | int s5p_mfc_wakeup(struct s5p_mfc_dev *dev) | ||
300 | { | ||
301 | int ret; | ||
302 | |||
303 | mfc_debug_enter(); | ||
304 | /* 0. MFC reset */ | ||
305 | mfc_debug(2, "MFC reset..\n"); | ||
306 | s5p_mfc_clock_on(); | ||
307 | ret = s5p_mfc_reset(dev); | ||
308 | if (ret) { | ||
309 | mfc_err("Failed to reset MFC - timeout\n"); | ||
310 | return ret; | ||
311 | } | ||
312 | mfc_debug(2, "Done MFC reset..\n"); | ||
313 | /* 1. Set DRAM base Addr */ | ||
314 | s5p_mfc_init_memctrl(dev); | ||
315 | /* 2. Initialize registers of channel I/F */ | ||
316 | s5p_mfc_clear_cmds(dev); | ||
317 | s5p_mfc_clean_dev_int_flags(dev); | ||
318 | /* 3. Initialize firmware */ | ||
319 | ret = s5p_mfc_wakeup_cmd(dev); | ||
320 | if (ret) { | ||
321 | mfc_err("Failed to send command to MFC - timeout\n"); | ||
322 | return ret; | ||
323 | } | ||
324 | /* 4. Release reset signal to the RISC */ | ||
325 | mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET); | ||
326 | mfc_debug(2, "Ok, now will write a command to wakeup the system\n"); | ||
327 | if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_WAKEUP_RET)) { | ||
328 | mfc_err("Failed to load firmware\n"); | ||
329 | return -EIO; | ||
330 | } | ||
331 | s5p_mfc_clock_off(); | ||
332 | dev->int_cond = 0; | ||
333 | if (dev->int_err != 0 || dev->int_type != | ||
334 | S5P_FIMV_R2H_CMD_WAKEUP_RET) { | ||
335 | /* Failure. */ | ||
336 | mfc_err("Failed to wakeup - error: %d int: %d\n", dev->int_err, | ||
337 | dev->int_type); | ||
338 | return -EIO; | ||
339 | } | ||
340 | mfc_debug_leave(); | ||
341 | return 0; | ||
342 | } | ||
343 | |||