diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-firmware.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-firmware.c | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c new file mode 100644 index 000000000000..2694ce350631 --- /dev/null +++ b/drivers/media/video/cx18/cx18-firmware.c | |||
@@ -0,0 +1,373 @@ | |||
1 | /* | ||
2 | * cx18 firmware functions | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
19 | * 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include "cx18-driver.h" | ||
23 | #include "cx18-scb.h" | ||
24 | #include "cx18-irq.h" | ||
25 | #include "cx18-firmware.h" | ||
26 | #include "cx18-cards.h" | ||
27 | #include <linux/firmware.h> | ||
28 | |||
29 | #define CX18_PROC_SOFT_RESET 0xc70010 | ||
30 | #define CX18_DDR_SOFT_RESET 0xc70014 | ||
31 | #define CX18_CLOCK_SELECT1 0xc71000 | ||
32 | #define CX18_CLOCK_SELECT2 0xc71004 | ||
33 | #define CX18_HALF_CLOCK_SELECT1 0xc71008 | ||
34 | #define CX18_HALF_CLOCK_SELECT2 0xc7100C | ||
35 | #define CX18_CLOCK_POLARITY1 0xc71010 | ||
36 | #define CX18_CLOCK_POLARITY2 0xc71014 | ||
37 | #define CX18_ADD_DELAY_ENABLE1 0xc71018 | ||
38 | #define CX18_ADD_DELAY_ENABLE2 0xc7101C | ||
39 | #define CX18_CLOCK_ENABLE1 0xc71020 | ||
40 | #define CX18_CLOCK_ENABLE2 0xc71024 | ||
41 | |||
42 | #define CX18_REG_BUS_TIMEOUT_EN 0xc72024 | ||
43 | |||
44 | #define CX18_AUDIO_ENABLE 0xc72014 | ||
45 | #define CX18_REG_BUS_TIMEOUT_EN 0xc72024 | ||
46 | |||
47 | #define CX18_FAST_CLOCK_PLL_INT 0xc78000 | ||
48 | #define CX18_FAST_CLOCK_PLL_FRAC 0xc78004 | ||
49 | #define CX18_FAST_CLOCK_PLL_POST 0xc78008 | ||
50 | #define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C | ||
51 | #define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010 | ||
52 | |||
53 | #define CX18_SLOW_CLOCK_PLL_INT 0xc78014 | ||
54 | #define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018 | ||
55 | #define CX18_SLOW_CLOCK_PLL_POST 0xc7801C | ||
56 | #define CX18_MPEG_CLOCK_PLL_INT 0xc78040 | ||
57 | #define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044 | ||
58 | #define CX18_MPEG_CLOCK_PLL_POST 0xc78048 | ||
59 | #define CX18_PLL_POWER_DOWN 0xc78088 | ||
60 | #define CX18_SW1_INT_STATUS 0xc73104 | ||
61 | #define CX18_SW1_INT_ENABLE_PCI 0xc7311C | ||
62 | #define CX18_SW2_INT_SET 0xc73140 | ||
63 | #define CX18_SW2_INT_STATUS 0xc73144 | ||
64 | #define CX18_ADEC_CONTROL 0xc78120 | ||
65 | |||
66 | #define CX18_DDR_REQUEST_ENABLE 0xc80000 | ||
67 | #define CX18_DDR_CHIP_CONFIG 0xc80004 | ||
68 | #define CX18_DDR_REFRESH 0xc80008 | ||
69 | #define CX18_DDR_TIMING1 0xc8000C | ||
70 | #define CX18_DDR_TIMING2 0xc80010 | ||
71 | #define CX18_DDR_POWER_REG 0xc8001C | ||
72 | |||
73 | #define CX18_DDR_TUNE_LANE 0xc80048 | ||
74 | #define CX18_DDR_INITIAL_EMRS 0xc80054 | ||
75 | #define CX18_DDR_MB_PER_ROW_7 0xc8009C | ||
76 | #define CX18_DDR_BASE_63_ADDR 0xc804FC | ||
77 | |||
78 | #define CX18_WMB_CLIENT02 0xc90108 | ||
79 | #define CX18_WMB_CLIENT05 0xc90114 | ||
80 | #define CX18_WMB_CLIENT06 0xc90118 | ||
81 | #define CX18_WMB_CLIENT07 0xc9011C | ||
82 | #define CX18_WMB_CLIENT08 0xc90120 | ||
83 | #define CX18_WMB_CLIENT09 0xc90124 | ||
84 | #define CX18_WMB_CLIENT10 0xc90128 | ||
85 | #define CX18_WMB_CLIENT11 0xc9012C | ||
86 | #define CX18_WMB_CLIENT12 0xc90130 | ||
87 | #define CX18_WMB_CLIENT13 0xc90134 | ||
88 | #define CX18_WMB_CLIENT14 0xc90138 | ||
89 | |||
90 | #define CX18_DSP0_INTERRUPT_MASK 0xd0004C | ||
91 | |||
92 | /* Encoder/decoder firmware sizes */ | ||
93 | #define CX18_FW_CPU_SIZE (174716) | ||
94 | #define CX18_FW_APU_SIZE (141200) | ||
95 | |||
96 | #define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */ | ||
97 | #define APU_ROM_SYNC2 0x72646548 /* "rdeH" */ | ||
98 | |||
99 | struct cx18_apu_rom_seghdr { | ||
100 | u32 sync1; | ||
101 | u32 sync2; | ||
102 | u32 addr; | ||
103 | u32 size; | ||
104 | }; | ||
105 | |||
106 | static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx, long size) | ||
107 | { | ||
108 | const struct firmware *fw = NULL; | ||
109 | int retries = 3; | ||
110 | int i, j; | ||
111 | u32 __iomem *dst = (u32 __iomem *)mem; | ||
112 | const u32 *src; | ||
113 | |||
114 | retry: | ||
115 | if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) { | ||
116 | CX18_ERR("Unable to open firmware %s (must be %ld bytes)\n", | ||
117 | fn, size); | ||
118 | CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); | ||
119 | return -ENOMEM; | ||
120 | } | ||
121 | |||
122 | src = (const u32 *)fw->data; | ||
123 | |||
124 | if (fw->size != size) { | ||
125 | /* Due to race conditions in firmware loading (esp. with | ||
126 | udev <0.95) the wrong file was sometimes loaded. So we check | ||
127 | filesizes to see if at least the right-sized file was | ||
128 | loaded. If not, then we retry. */ | ||
129 | CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", | ||
130 | fn, size, fw->size); | ||
131 | release_firmware(fw); | ||
132 | retries--; | ||
133 | goto retry; | ||
134 | } | ||
135 | for (i = 0; i < fw->size; i += 4096) { | ||
136 | setup_page(i); | ||
137 | for (j = i; j < fw->size && j < i + 4096; j += 4) { | ||
138 | /* no need for endianness conversion on the ppc */ | ||
139 | __raw_writel(*src, dst); | ||
140 | if (__raw_readl(dst) != *src) { | ||
141 | CX18_ERR("Mismatch at offset %x\n", i); | ||
142 | release_firmware(fw); | ||
143 | return -EIO; | ||
144 | } | ||
145 | dst++; | ||
146 | src++; | ||
147 | } | ||
148 | } | ||
149 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) | ||
150 | CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); | ||
151 | release_firmware(fw); | ||
152 | return size; | ||
153 | } | ||
154 | |||
155 | static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, long size) | ||
156 | { | ||
157 | const struct firmware *fw = NULL; | ||
158 | int retries = 3; | ||
159 | int i, j; | ||
160 | const u32 *src; | ||
161 | struct cx18_apu_rom_seghdr seghdr; | ||
162 | const u8 *vers; | ||
163 | u32 offset = 0; | ||
164 | u32 apu_version = 0; | ||
165 | int sz; | ||
166 | |||
167 | retry: | ||
168 | if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) { | ||
169 | CX18_ERR("unable to open firmware %s (must be %ld bytes)\n", | ||
170 | fn, size); | ||
171 | CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); | ||
172 | return -ENOMEM; | ||
173 | } | ||
174 | |||
175 | src = (const u32 *)fw->data; | ||
176 | vers = fw->data + sizeof(seghdr); | ||
177 | sz = fw->size; | ||
178 | |||
179 | if (fw->size != size) { | ||
180 | /* Due to race conditions in firmware loading (esp. with | ||
181 | udev <0.95) the wrong file was sometimes loaded. So we check | ||
182 | filesizes to see if at least the right-sized file was | ||
183 | loaded. If not, then we retry. */ | ||
184 | CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", | ||
185 | fn, size, fw->size); | ||
186 | release_firmware(fw); | ||
187 | retries--; | ||
188 | goto retry; | ||
189 | } | ||
190 | apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; | ||
191 | while (offset + sizeof(seghdr) < size) { | ||
192 | /* TODO: byteswapping */ | ||
193 | memcpy(&seghdr, src + offset / 4, sizeof(seghdr)); | ||
194 | offset += sizeof(seghdr); | ||
195 | if (seghdr.sync1 != APU_ROM_SYNC1 || | ||
196 | seghdr.sync2 != APU_ROM_SYNC2) { | ||
197 | offset += seghdr.size; | ||
198 | continue; | ||
199 | } | ||
200 | CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, | ||
201 | seghdr.addr + seghdr.size - 1); | ||
202 | if (offset + seghdr.size > sz) | ||
203 | break; | ||
204 | for (i = 0; i < seghdr.size; i += 4096) { | ||
205 | setup_page(offset + i); | ||
206 | for (j = i; j < seghdr.size && j < i + 4096; j += 4) { | ||
207 | /* no need for endianness conversion on the ppc */ | ||
208 | __raw_writel(src[(offset + j) / 4], dst + seghdr.addr + j); | ||
209 | if (__raw_readl(dst + seghdr.addr + j) != src[(offset + j) / 4]) { | ||
210 | CX18_ERR("Mismatch at offset %x\n", offset + j); | ||
211 | release_firmware(fw); | ||
212 | return -EIO; | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | offset += seghdr.size; | ||
217 | } | ||
218 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) | ||
219 | CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n", | ||
220 | fn, apu_version, fw->size); | ||
221 | release_firmware(fw); | ||
222 | /* Clear bit0 for APU to start from 0 */ | ||
223 | write_reg(read_reg(0xc72030) & ~1, 0xc72030); | ||
224 | return size; | ||
225 | } | ||
226 | |||
227 | void cx18_halt_firmware(struct cx18 *cx) | ||
228 | { | ||
229 | CX18_DEBUG_INFO("Preparing for firmware halt.\n"); | ||
230 | write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ | ||
231 | write_reg(0x00020002, CX18_ADEC_CONTROL); | ||
232 | } | ||
233 | |||
234 | void cx18_init_power(struct cx18 *cx, int lowpwr) | ||
235 | { | ||
236 | /* power-down Spare and AOM PLLs */ | ||
237 | /* power-up fast, slow and mpeg PLLs */ | ||
238 | write_reg(0x00000008, CX18_PLL_POWER_DOWN); | ||
239 | |||
240 | /* ADEC out of sleep */ | ||
241 | write_reg(0x00020000, CX18_ADEC_CONTROL); | ||
242 | |||
243 | /* The fast clock is at 200/245 MHz */ | ||
244 | write_reg(lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); | ||
245 | write_reg(lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC); | ||
246 | |||
247 | write_reg(2, CX18_FAST_CLOCK_PLL_POST); | ||
248 | write_reg(1, CX18_FAST_CLOCK_PLL_PRESCALE); | ||
249 | write_reg(4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); | ||
250 | |||
251 | /* set slow clock to 125/120 MHz */ | ||
252 | write_reg(lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); | ||
253 | write_reg(lowpwr ? 0xEBAF05 : 0x18618A8, CX18_SLOW_CLOCK_PLL_FRAC); | ||
254 | write_reg(4, CX18_SLOW_CLOCK_PLL_POST); | ||
255 | |||
256 | /* mpeg clock pll 54MHz */ | ||
257 | write_reg(0xF, CX18_MPEG_CLOCK_PLL_INT); | ||
258 | write_reg(0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); | ||
259 | write_reg(8, CX18_MPEG_CLOCK_PLL_POST); | ||
260 | |||
261 | /* Defaults */ | ||
262 | /* APU = SC or SC/2 = 125/62.5 */ | ||
263 | /* EPU = SC = 125 */ | ||
264 | /* DDR = FC = 180 */ | ||
265 | /* ENC = SC = 125 */ | ||
266 | /* AI1 = SC = 125 */ | ||
267 | /* VIM2 = disabled */ | ||
268 | /* PCI = FC/2 = 90 */ | ||
269 | /* AI2 = disabled */ | ||
270 | /* DEMUX = disabled */ | ||
271 | /* AO = SC/2 = 62.5 */ | ||
272 | /* SER = 54MHz */ | ||
273 | /* VFC = disabled */ | ||
274 | /* USB = disabled */ | ||
275 | |||
276 | write_reg(lowpwr ? 0xFFFF0020 : 0x00060004, CX18_CLOCK_SELECT1); | ||
277 | write_reg(lowpwr ? 0xFFFF0004 : 0x00060006, CX18_CLOCK_SELECT2); | ||
278 | |||
279 | write_reg(0xFFFF0002, CX18_HALF_CLOCK_SELECT1); | ||
280 | write_reg(0xFFFF0104, CX18_HALF_CLOCK_SELECT2); | ||
281 | |||
282 | write_reg(0xFFFF9026, CX18_CLOCK_ENABLE1); | ||
283 | write_reg(0xFFFF3105, CX18_CLOCK_ENABLE2); | ||
284 | } | ||
285 | |||
286 | void cx18_init_memory(struct cx18 *cx) | ||
287 | { | ||
288 | cx18_msleep_timeout(10, 0); | ||
289 | write_reg(0x10000, CX18_DDR_SOFT_RESET); | ||
290 | cx18_msleep_timeout(10, 0); | ||
291 | |||
292 | write_reg(cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); | ||
293 | |||
294 | cx18_msleep_timeout(10, 0); | ||
295 | |||
296 | write_reg(cx->card->ddr.refresh, CX18_DDR_REFRESH); | ||
297 | write_reg(cx->card->ddr.timing1, CX18_DDR_TIMING1); | ||
298 | write_reg(cx->card->ddr.timing2, CX18_DDR_TIMING2); | ||
299 | |||
300 | cx18_msleep_timeout(10, 0); | ||
301 | |||
302 | /* Initialize DQS pad time */ | ||
303 | write_reg(cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); | ||
304 | write_reg(cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); | ||
305 | |||
306 | cx18_msleep_timeout(10, 0); | ||
307 | |||
308 | write_reg(0x20000, CX18_DDR_SOFT_RESET); | ||
309 | cx18_msleep_timeout(10, 0); | ||
310 | |||
311 | /* use power-down mode when idle */ | ||
312 | write_reg(0x00000010, CX18_DDR_POWER_REG); | ||
313 | |||
314 | write_reg(0x10001, CX18_REG_BUS_TIMEOUT_EN); | ||
315 | |||
316 | write_reg(0x48, CX18_DDR_MB_PER_ROW_7); | ||
317 | write_reg(0xE0000, CX18_DDR_BASE_63_ADDR); | ||
318 | |||
319 | write_reg(0x00000101, CX18_WMB_CLIENT02); /* AO */ | ||
320 | write_reg(0x00000101, CX18_WMB_CLIENT09); /* AI2 */ | ||
321 | write_reg(0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ | ||
322 | write_reg(0x00000101, CX18_WMB_CLIENT06); /* AI1 */ | ||
323 | write_reg(0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ | ||
324 | write_reg(0x00000101, CX18_WMB_CLIENT10); /* ME */ | ||
325 | write_reg(0x00000101, CX18_WMB_CLIENT12); /* ENC */ | ||
326 | write_reg(0x00000101, CX18_WMB_CLIENT13); /* PK */ | ||
327 | write_reg(0x00000101, CX18_WMB_CLIENT11); /* RC */ | ||
328 | write_reg(0x00000101, CX18_WMB_CLIENT14); /* AVO */ | ||
329 | } | ||
330 | |||
331 | int cx18_firmware_init(struct cx18 *cx) | ||
332 | { | ||
333 | /* Allow chip to control CLKRUN */ | ||
334 | write_reg(0x5, CX18_DSP0_INTERRUPT_MASK); | ||
335 | |||
336 | write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ | ||
337 | |||
338 | cx18_msleep_timeout(1, 0); | ||
339 | |||
340 | sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); | ||
341 | sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | ||
342 | |||
343 | /* Only if the processor is not running */ | ||
344 | if (read_reg(CX18_PROC_SOFT_RESET) & 8) { | ||
345 | int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", | ||
346 | cx->enc_mem, cx, CX18_FW_APU_SIZE); | ||
347 | |||
348 | sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", | ||
349 | cx->enc_mem, cx, CX18_FW_CPU_SIZE); | ||
350 | |||
351 | if (sz > 0) { | ||
352 | int retries = 0; | ||
353 | |||
354 | /* start the CPU */ | ||
355 | write_reg(0x00080000, CX18_PROC_SOFT_RESET); | ||
356 | while (retries++ < 50) { /* Loop for max 500mS */ | ||
357 | if ((read_reg(CX18_PROC_SOFT_RESET) & 1) == 0) | ||
358 | break; | ||
359 | cx18_msleep_timeout(10, 0); | ||
360 | } | ||
361 | cx18_msleep_timeout(200, 0); | ||
362 | if (retries == 51) { | ||
363 | CX18_ERR("Could not start the CPU\n"); | ||
364 | return -EIO; | ||
365 | } | ||
366 | } | ||
367 | if (sz <= 0) | ||
368 | return -EIO; | ||
369 | } | ||
370 | /* initialize GPIO */ | ||
371 | write_reg(0x14001400, 0xC78110); | ||
372 | return 0; | ||
373 | } | ||