aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18/cx18-firmware.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2008-04-28 19:24:33 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-04-29 17:41:41 -0400
commit1c1e45d17b663d4749af456ab7c2fc1f36405ef8 (patch)
tree03704d6fd888c4c617baa81a60df0d80815b2607 /drivers/media/video/cx18/cx18-firmware.c
parentd74bee8b4776b5051c650a90f49a2022d46d8588 (diff)
V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip
Many thanks to Steve Toth from Hauppauge and Nattu Dakshinamurthy from Conexant for their support. I am in particular thankful to Hauppauge since without their help this driver would not exist. It should also be noted that Steve did the work to get the DVB part up and running. Thank you! Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Steven Toth <stoth@hauppauge.com> Signed-off-by: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: G. Andrew Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/cx18/cx18-firmware.c')
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c373
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
99struct cx18_apu_rom_seghdr {
100 u32 sync1;
101 u32 sync2;
102 u32 addr;
103 u32 size;
104};
105
106static 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
114retry:
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
155static 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
167retry:
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
227void 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
234void 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
286void 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
331int 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}