V4L/DVB (9895): cx18: Refine the firmware load and firmware startup process

Refine the firmware load and firmware startup process.  Significant changes
are to ensure the SCB and IPC area are correct before starting up the firmware,
and letting the CPU firmware start up the APU firmware for us.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index d7baf1b..f50cf21 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -878,6 +878,7 @@
 		goto free_i2c;
 	}
 	cx18_init_memory(cx);
+	cx18_init_scb(cx);
 
 	/* Register IRQ */
 	retval = request_irq(cx->dev->irq, cx18_irq_handler,
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
index 8eac843..e74f76d 100644
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ b/drivers/media/video/cx18/cx18-firmware.c
@@ -332,6 +332,10 @@
 
 int cx18_firmware_init(struct cx18 *cx)
 {
+	u32 fw_entry_addr;
+	int sz, retries;
+	u32 api_args[MAX_MB_ARGUMENTS];
+
 	/* Allow chip to control CLKRUN */
 	cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
 
@@ -341,65 +345,62 @@
 
 	cx18_msleep_timeout(1, 0);
 
+	/* If the CPU is still running */
+	if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
+		CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
+		return -EIO;
+	}
+
 	cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
 	cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
 
-	/* Only if the processor is not running */
-	if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) {
-		u32 fw_entry_addr = 0;
-		int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
-			       cx->enc_mem, cx, &fw_entry_addr);
+	sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
+	if (sz <= 0)
+		return sz;
 
-		if (sz <= 0)
-			return sz;
+	/* The SCB & IPC area *must* be correct before starting the firmwares */
+	cx18_init_scb(cx);
 
-		/* Clear bit0 for APU to start from 0 */
-		cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
+	fw_entry_addr = 0;
+	sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx,
+				&fw_entry_addr);
+	if (sz <= 0)
+		return sz;
 
-		cx18_write_enc(cx, 0xE51FF004, 0);    /* ldr pc, [pc, #-4] */
-		cx18_write_enc(cx, fw_entry_addr, 4);
+	/* Start the CPU. The CPU will take care of the APU for us. */
+	cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
+				  0x00000000, 0x00080008);
 
-		/* Start APU */
-		cx18_write_reg_expect(cx, 0x00010000, CX18_PROC_SOFT_RESET,
-					  0x00000000, 0x00010001);
-		cx18_msleep_timeout(500, 0);
+	/* Wait up to 500 ms for the APU to come out of reset */
+	for (retries = 0;
+	     retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
+	     retries++)
+		cx18_msleep_timeout(10, 0);
 
-		sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
-					cx->enc_mem, cx);
+	cx18_msleep_timeout(200, 0);
 
-		if (sz > 0) {
-			int retries = 0;
-
-			/* start the CPU */
-			cx18_write_reg_expect(cx,
-					      0x00080000, CX18_PROC_SOFT_RESET,
-					      0x00000000, 0x00080008);
-			while (retries++ < 50) { /* Loop for max 500mS */
-				if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET)
-				     & 1) == 0)
-					break;
-				cx18_msleep_timeout(10, 0);
-			}
-			cx18_msleep_timeout(200, 0);
-			if (retries == 51) {
-				CX18_ERR("Could not start the CPU\n");
-				return -EIO;
-			}
-		}
-		if (sz <= 0)
-			return -EIO;
+	if (retries == 50 &&
+	    (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
+		CX18_ERR("Could not start the CPU\n");
+		return -EIO;
 	}
 
 	/*
-	 * The CPU firmware apparently sets up to receive an interrupt for it's
-	 * outgoing IRQ_CPU_TO_EPU_ACK to us (*boggle*).  We get an interrupt
-	 * when it sends us an ack, but by the time we process it, that flag in
-	 * the SW2 status register has been cleared by the CPU firmware.
-	 * We'll prevent that not so useful behavior by clearing the CPU's
-	 * interrupt enables for Ack IRQ's we want to process.
+	 * The CPU had once before set up to receive an interrupt for it's
+	 * outgoing IRQ_CPU_TO_EPU_ACK to us.  If it ever does this, we get an
+	 * interrupt when it sends us an ack, but by the time we process it,
+	 * that flag in the SW2 status register has been cleared by the CPU
+	 * firmware.  We'll prevent that not so useful condition from happening
+	 * by clearing the CPU's interrupt enables for Ack IRQ's we want to
+	 * process.
 	 */
 	cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
 
+	/* Try a benign command to see if the CPU is alive and well */
+	sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
+	if (sz < 0)
+		return sz;
+
 	/* initialize GPIO */
 	cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
 	return 0;
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index d623514..de5e723 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -82,8 +82,9 @@
 	API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS,                    0),
 	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK,			0),
 	API_ENTRY(CPU, CX18_CPU_DE_SET_MDL,			API_FAST),
-	API_ENTRY(CPU, CX18_APU_RESETAI,			API_FAST),
 	API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL,			API_SLOW),
+	API_ENTRY(APU, CX18_APU_RESETAI,			0),
+	API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32,			0),
 	API_ENTRY(0, 0,						0),
 };
 
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c
index ac18bd9..34b4d03 100644
--- a/drivers/media/video/cx18/cx18-scb.c
+++ b/drivers/media/video/cx18/cx18-scb.c
@@ -118,6 +118,5 @@
 	cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
 			&cx->scb->ipc_offset);
 
-	cx18_writel(cx, 1, &cx->scb->hpu_state);
 	cx18_writel(cx, 1, &cx->scb->epu_state);
 }
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 668f968..601f3a2 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -44,6 +44,7 @@
 
 /* All commands for CPU have the following mask set */
 #define CPU_CMD_MASK                        	0x20000000
+#define CPU_CMD_MASK_DEBUG       		(CPU_CMD_MASK | 0x00000000)
 #define CPU_CMD_MASK_ACK                    	(CPU_CMD_MASK | 0x80000000)
 #define CPU_CMD_MASK_CAPTURE                	(CPU_CMD_MASK | 0x00020000)
 #define CPU_CMD_MASK_TS                     	(CPU_CMD_MASK | 0x00040000)
@@ -71,6 +72,11 @@
 	   0/zero/NULL means "I have nothing to say" */
 #define CX18_EPU_DEBUG 				(EPU_CMD_MASK_DEBUG | 0x0003)
 
+/* Reads memory/registers (32-bit)
+   IN[0] - Address
+   OUT[1] - Value */
+#define CX18_CPU_DEBUG_PEEK32			(CPU_CMD_MASK_DEBUG | 0x0003)
+
 /* Description: This command starts streaming with the set channel type
    IN[0] - Task handle. Handle of the task to start
    ReturnCode - One of the ERR_CAPTURE_... */