diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-firmware.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-firmware.c | 101 |
1 files changed, 51 insertions, 50 deletions
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index 8eac84314d40..e74f76d47df5 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c | |||
@@ -332,6 +332,10 @@ void cx18_init_memory(struct cx18 *cx) | |||
332 | 332 | ||
333 | int cx18_firmware_init(struct cx18 *cx) | 333 | int cx18_firmware_init(struct cx18 *cx) |
334 | { | 334 | { |
335 | u32 fw_entry_addr; | ||
336 | int sz, retries; | ||
337 | u32 api_args[MAX_MB_ARGUMENTS]; | ||
338 | |||
335 | /* Allow chip to control CLKRUN */ | 339 | /* Allow chip to control CLKRUN */ |
336 | cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); | 340 | cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); |
337 | 341 | ||
@@ -341,65 +345,62 @@ int cx18_firmware_init(struct cx18 *cx) | |||
341 | 345 | ||
342 | cx18_msleep_timeout(1, 0); | 346 | cx18_msleep_timeout(1, 0); |
343 | 347 | ||
348 | /* If the CPU is still running */ | ||
349 | if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) { | ||
350 | CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__); | ||
351 | return -EIO; | ||
352 | } | ||
353 | |||
344 | cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); | 354 | cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); |
345 | cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | 355 | cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); |
346 | 356 | ||
347 | /* Only if the processor is not running */ | 357 | sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx); |
348 | if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) { | 358 | if (sz <= 0) |
349 | u32 fw_entry_addr = 0; | 359 | return sz; |
350 | int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", | 360 | |
351 | cx->enc_mem, cx, &fw_entry_addr); | 361 | /* The SCB & IPC area *must* be correct before starting the firmwares */ |
352 | 362 | cx18_init_scb(cx); | |
353 | if (sz <= 0) | 363 | |
354 | return sz; | 364 | fw_entry_addr = 0; |
355 | 365 | sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx, | |
356 | /* Clear bit0 for APU to start from 0 */ | 366 | &fw_entry_addr); |
357 | cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030); | 367 | if (sz <= 0) |
358 | 368 | return sz; | |
359 | cx18_write_enc(cx, 0xE51FF004, 0); /* ldr pc, [pc, #-4] */ | 369 | |
360 | cx18_write_enc(cx, fw_entry_addr, 4); | 370 | /* Start the CPU. The CPU will take care of the APU for us. */ |
361 | 371 | cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET, | |
362 | /* Start APU */ | 372 | 0x00000000, 0x00080008); |
363 | cx18_write_reg_expect(cx, 0x00010000, CX18_PROC_SOFT_RESET, | 373 | |
364 | 0x00000000, 0x00010001); | 374 | /* Wait up to 500 ms for the APU to come out of reset */ |
365 | cx18_msleep_timeout(500, 0); | 375 | for (retries = 0; |
366 | 376 | retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1; | |
367 | sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", | 377 | retries++) |
368 | cx->enc_mem, cx); | 378 | cx18_msleep_timeout(10, 0); |
369 | 379 | ||
370 | if (sz > 0) { | 380 | cx18_msleep_timeout(200, 0); |
371 | int retries = 0; | 381 | |
372 | 382 | if (retries == 50 && | |
373 | /* start the CPU */ | 383 | (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) { |
374 | cx18_write_reg_expect(cx, | 384 | CX18_ERR("Could not start the CPU\n"); |
375 | 0x00080000, CX18_PROC_SOFT_RESET, | 385 | return -EIO; |
376 | 0x00000000, 0x00080008); | ||
377 | while (retries++ < 50) { /* Loop for max 500mS */ | ||
378 | if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) | ||
379 | & 1) == 0) | ||
380 | break; | ||
381 | cx18_msleep_timeout(10, 0); | ||
382 | } | ||
383 | cx18_msleep_timeout(200, 0); | ||
384 | if (retries == 51) { | ||
385 | CX18_ERR("Could not start the CPU\n"); | ||
386 | return -EIO; | ||
387 | } | ||
388 | } | ||
389 | if (sz <= 0) | ||
390 | return -EIO; | ||
391 | } | 386 | } |
392 | 387 | ||
393 | /* | 388 | /* |
394 | * The CPU firmware apparently sets up to receive an interrupt for it's | 389 | * The CPU had once before set up to receive an interrupt for it's |
395 | * outgoing IRQ_CPU_TO_EPU_ACK to us (*boggle*). We get an interrupt | 390 | * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an |
396 | * when it sends us an ack, but by the time we process it, that flag in | 391 | * interrupt when it sends us an ack, but by the time we process it, |
397 | * the SW2 status register has been cleared by the CPU firmware. | 392 | * that flag in the SW2 status register has been cleared by the CPU |
398 | * We'll prevent that not so useful behavior by clearing the CPU's | 393 | * firmware. We'll prevent that not so useful condition from happening |
399 | * interrupt enables for Ack IRQ's we want to process. | 394 | * by clearing the CPU's interrupt enables for Ack IRQ's we want to |
395 | * process. | ||
400 | */ | 396 | */ |
401 | cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | 397 | cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); |
402 | 398 | ||
399 | /* Try a benign command to see if the CPU is alive and well */ | ||
400 | sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0); | ||
401 | if (sz < 0) | ||
402 | return sz; | ||
403 | |||
403 | /* initialize GPIO */ | 404 | /* initialize GPIO */ |
404 | cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400); | 405 | cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400); |
405 | return 0; | 406 | return 0; |