Example #1
0
int cx18_firmware_init(struct cx18 *cx)
{
    /* Allow chip to control CLKRUN */
    write_reg(0x5, CX18_DSP0_INTERRUPT_MASK);

    write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */

    cx18_msleep_timeout(1, 0);

    sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
    sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);

    /* Only if the processor is not running */
    if (read_reg(CX18_PROC_SOFT_RESET) & 8) {
        int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
                   cx->enc_mem, cx, CX18_FW_APU_SIZE);

        sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
                    cx->enc_mem, cx, CX18_FW_CPU_SIZE);

        if (sz > 0) {
            int retries = 0;

            /* start the CPU */
            write_reg(0x00080000, CX18_PROC_SOFT_RESET);
            while (retries++ < 50) { /* Loop for max 500mS */
                if ((read_reg(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;
    }
    /* initialize GPIO */
    write_reg(0x14001400, 0xC78110);
    return 0;
}
void cx18_unmute(struct cx18 *cx)
{
	if (atomic_read(&cx->ana_capturing)) {
		cx18_msleep_timeout(100, 0);
		cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
				cx18_find_handle(cx), 12);
		cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
				cx18_find_handle(cx), 0);
	}
	CX18_DEBUG_INFO("Unmute\n");
}
Example #3
0
void cx18_unmute(struct cx18 *cx)
{
    u32 h;
    if (atomic_read(&cx->ana_capturing)) {
        h = cx18_find_handle(cx);
        if (h != CX18_INVALID_TASK_HANDLE) {
            cx18_msleep_timeout(100, 0);
            cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, h, 12);
            cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 0);
        } else
            CX18_ERR("Can't find valid task handle for unmute\n");
    }
    CX18_DEBUG_INFO("Unmute\n");
}
void cx18_init_memory(struct cx18 *cx)
{
	cx18_msleep_timeout(10, 0);
	cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
				  0x00000000, 0x00010001);
	cx18_msleep_timeout(10, 0);

	cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);

	cx18_msleep_timeout(10, 0);

	cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH);
	cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1);
	cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2);

	cx18_msleep_timeout(10, 0);

	/* Initialize DQS pad time */
	cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
	cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);

	cx18_msleep_timeout(10, 0);

	cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
				  0x00000000, 0x00020002);
	cx18_msleep_timeout(10, 0);

	/* use power-down mode when idle */
	cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);

	cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
				  0x00000001, 0x00010001);

	cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
	cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);

	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02);  /* AO */
	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09);  /* AI2 */
	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05);  /* VIM1 */
	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06);  /* AI1 */
	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07);  /* 3D comb */
	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10);  /* ME */
	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12);  /* ENC */
	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13);  /* PK */
	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11);  /* RC */
	cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14);  /* AVO */
}
static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
{
	const struct cx18_api_info *info = find_api_info(cmd);
	u32 state, irq, req, ack, err;
	struct cx18_mailbox __iomem *mb;
	u32 __iomem *xpu_state;
	wait_queue_head_t *waitq;
	struct mutex *mb_lock;
	long int timeout, ret;
	int i;
	char argstr[MAX_MB_ARGUMENTS*11+1];

	if (info == NULL) {
		CX18_WARN("unknown cmd %x\n", cmd);
		return -EINVAL;
	}

	if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */
		if (cmd == CX18_CPU_DE_SET_MDL) {
			if (cx18_debug & CX18_DBGFLG_HIGHVOL)
				CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n",
						info->name, cmd,
						u32arr2hex(data, args, argstr));
		} else
			CX18_DEBUG_API("%s\tcmd %#010x args%s\n",
				       info->name, cmd,
				       u32arr2hex(data, args, argstr));
	}

	switch (info->rpu) {
	case APU:
		waitq = &cx->mb_apu_waitq;
		mb_lock = &cx->epu2apu_mb_lock;
		irq = IRQ_EPU_TO_APU;
		mb = &cx->scb->epu2apu_mb;
		xpu_state = &cx->scb->apu_state;
		break;
	case CPU:
		waitq = &cx->mb_cpu_waitq;
		mb_lock = &cx->epu2cpu_mb_lock;
		irq = IRQ_EPU_TO_CPU;
		mb = &cx->scb->epu2cpu_mb;
		xpu_state = &cx->scb->cpu_state;
		break;
	default:
		CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu);
		return -EINVAL;
	}

	mutex_lock(mb_lock);
	/*
	 * Wait for an in-use mailbox to complete
	 *
	 * If the XPU is responding with Ack's, the mailbox shouldn't be in
	 * a busy state, since we serialize access to it on our end.
	 *
	 * If the wait for ack after sending a previous command was interrupted
	 * by a signal, we may get here and find a busy mailbox.  After waiting,
	 * mark it "not busy" from our end, if the XPU hasn't ack'ed it still.
	 */
	state = cx18_readl(cx, xpu_state);
	req = cx18_readl(cx, &mb->request);
	timeout = msecs_to_jiffies(10);
	ret = wait_event_timeout(*waitq,
				 (ack = cx18_readl(cx, &mb->ack)) == req,
				 timeout);
	if (req != ack) {
		/* waited long enough, make the mbox "not busy" from our end */
		cx18_writel(cx, req, &mb->ack);
		CX18_ERR("mbox was found stuck busy when setting up for %s; "
			 "clearing busy and trying to proceed\n", info->name);
	} else if (ret != timeout)
		CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n",
			       jiffies_to_msecs(timeout-ret));

	/* Build the outgoing mailbox */
	req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1;

	cx18_writel(cx, cmd, &mb->cmd);
	for (i = 0; i < args; i++)
		cx18_writel(cx, data[i], &mb->args[i]);
	cx18_writel(cx, 0, &mb->error);
	cx18_writel(cx, req, &mb->request);
	cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */

	/*
	 * Notify the XPU and wait for it to send an Ack back
	 */
	timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20);

	CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n",
			  irq, info->name);
	cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq);

	ret = wait_event_timeout(
		       *waitq,
		       cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request),
		       timeout);

	if (ret == 0) {
		/* Timed out */
		mutex_unlock(mb_lock);
		CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU "
				"acknowledgement\n",
				info->name, jiffies_to_msecs(timeout));
		return -EINVAL;
	}

	if (ret != timeout)
		CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n",
				  jiffies_to_msecs(timeout-ret), info->name);

	/* Collect data returned by the XPU */
	for (i = 0; i < MAX_MB_ARGUMENTS; i++)
		data[i] = cx18_readl(cx, &mb->args[i]);
	err = cx18_readl(cx, &mb->error);
	mutex_unlock(mb_lock);

	/*
	 * Wait for XPU to perform extra actions for the caller in some cases.
	 * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers
	 * back in a burst shortly thereafter
	 */
	if (info->flags & API_SLOW)
		cx18_msleep_timeout(300, 0);

	if (err)
		CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
				info->name);
	return err ? -EIO : 0;
}
Example #6
0
static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
{
	const struct cx18_api_info *info = find_api_info(cmd);
	u32 state = 0, irq = 0, req, oldreq, err;
	struct cx18_mailbox __iomem *mb;
	wait_queue_head_t *waitq;
	int timeout = 100;
	int cnt = 0;
	int sig = 0;
	int i;

	if (info == NULL) {
		CX18_WARN("unknown cmd %x\n", cmd);
		return -EINVAL;
	}

	if (cmd == CX18_CPU_DE_SET_MDL)
		CX18_DEBUG_HI_API("%s\n", info->name);
	else
		CX18_DEBUG_API("%s\n", info->name);
	cx18_setup_page(cx, SCB_OFFSET);
	mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);

	if (mb == NULL) {
		CX18_ERR("mb %s busy\n", info->name);
		return -EBUSY;
	}

	oldreq = req - 1;
	cx18_writel(cx, cmd, &mb->cmd);
	for (i = 0; i < args; i++)
		cx18_writel(cx, data[i], &mb->args[i]);
	cx18_writel(cx, 0, &mb->error);
	cx18_writel(cx, req, &mb->request);

	switch (info->rpu) {
	case APU: waitq = &cx->mb_apu_waitq; break;
	case CPU: waitq = &cx->mb_cpu_waitq; break;
	case EPU: waitq = &cx->mb_epu_waitq; break;
	case HPU: waitq = &cx->mb_hpu_waitq; break;
	default: return -EINVAL;
	}
	if (info->flags & API_FAST)
		timeout /= 2;
	cx18_write_reg(cx, irq, SW1_INT_SET);

	while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request)
	       && cnt < 660) {
		if (cnt > 200 && !in_atomic())
			sig = cx18_msleep_timeout(10, 1);
		cnt++;
	}
	if (sig)
		return -EINTR;
	if (cnt == 660) {
		cx18_writel(cx, oldreq, &mb->request);
		CX18_ERR("mb %s failed\n", info->name);
		return -EINVAL;
	}
	for (i = 0; i < MAX_MB_ARGUMENTS; i++)
		data[i] = cx18_readl(cx, &mb->args[i]);
	err = cx18_readl(cx, &mb->error);
	if (!in_atomic() && (info->flags & API_SLOW))
		cx18_msleep_timeout(300, 0);
	if (err)
		CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
				info->name);
	return err ? -EIO : 0;
}
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);

	/* Stop the firmware */
	cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
				  0x0000000F, 0x000F000F);

	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);

	sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
	if (sz <= 0)
		return sz;

	/* The SCB & IPC area *must* be correct before starting the firmwares */
	cx18_init_scb(cx);

	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;

	/* 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);

	/* 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);

	cx18_msleep_timeout(200, 0);

	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 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;
}