Пример #1
0
/**
* sst_post_message - Posts message to SST
*
* @work: Pointer to work structure
*
* This function is called by any component in driver which
* wants to send an IPC message. This will post message only if
* busy bit is free
*/
void sst_post_message(struct work_struct *work)
{
	struct ipc_post *msg;
	union ipc_header header;
	union  interrupt_reg imr;
	int retval = 0;
	imr.full = 0;

	/*To check if LPE is in stalled state.*/
	retval = sst_stalled();
	if (retval < 0) {
		pr_err("in stalled state\n");
		return;
	}
	pr_debug("post message called\n");
	spin_lock(&sst_drv_ctx->list_spin_lock);

	/* check list */
	if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
		/* list is empty, mask imr */
		pr_debug("Empty msg queue... masking\n");
		imr.full = readl(sst_drv_ctx->shim + SST_IMRX);
		imr.part.done_interrupt = 1;
		/* dummy register for shim workaround */
		sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
		spin_unlock(&sst_drv_ctx->list_spin_lock);
		return;
	}

	/* check busy bit */
	header.full = sst_shim_read(sst_drv_ctx->shim, SST_IPCX);
	if (header.part.busy) {
		/* busy, unmask */
		pr_debug("Busy not free... unmasking\n");
		imr.full = readl(sst_drv_ctx->shim + SST_IMRX);
		imr.part.done_interrupt = 0;
		/* dummy register for shim workaround */
		sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
		spin_unlock(&sst_drv_ctx->list_spin_lock);
		return;
	}
	/* copy msg from list */
	msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
			struct ipc_post, node);
	list_del(&msg->node);
	pr_debug("Post message: header = %x\n", msg->header.full);
	pr_debug("size: = %x\n", msg->header.part.data);
	if (msg->header.part.large)
		memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
			msg->mailbox_data, msg->header.part.data);
	/* dummy register for shim workaround */

	sst_shim_write(sst_drv_ctx->shim, SST_IPCX, msg->header.full);
	spin_unlock(&sst_drv_ctx->list_spin_lock);

	kfree(msg->mailbox_data);
	kfree(msg);
	return;
}
/**
 * sst_start_medfield - Start the SST DSP processor
 *
 * This starts the DSP in MRST platfroms
 */
static int sst_start_medfield(void)
{
    union config_status_reg csr;

    csr.full = 0x04830062;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
    csr.full = 0x04830063;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
    csr.full = 0x04830061;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
    pr_debug("Starting the DSP_medfld\n");

    return 0;
}
/**
 * sst_start_mrst - Start the SST DSP processor
 *
 * This starts the DSP in MRST platfroms
 */
static int sst_start_mrst(void)
{
    union config_status_reg csr;

    csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
    csr.part.bypass = 0;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
    csr.part.run_stall = 0;
    csr.part.sst_reset = 0;
    csr.part.strb_cntr_rst = 1;
    pr_debug("Setting SST to execute_mrst 0x%x\n", csr.full);
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);

    return 0;
}
Пример #4
0
/*
 * The runtime_suspend/resume is pretty much similar to the legacy
 * suspend/resume with the noted exception below: The PCI core takes care of
 * taking the system through D3hot and restoring it back to D0 and so there is
 * no need to duplicate that here.
 */
static int intel_sst_runtime_suspend(struct device *dev)
{
    union config_status_reg csr;

    pr_debug("runtime_suspend called\n");
    if (sst_drv_ctx->sst_state == SST_SUSPENDED) {
        pr_err("System already in Suspended state");
        return 0;
    }
    /*save fw context*/
    sst_save_dsp_context();
    /*Assert RESET on LPE Processor*/
    csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
    sst_drv_ctx->csr_value = csr.full;
    csr.full = csr.full | 0x2;

    /* Move the SST state to Suspended */
    mutex_lock(&sst_drv_ctx->sst_lock);
    sst_drv_ctx->sst_state = SST_SUSPENDED;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
    mutex_unlock(&sst_drv_ctx->sst_lock);
    if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID)
        vibra_pwm_configure(false);

    flush_workqueue(sst_drv_ctx->post_msg_wq);
    flush_workqueue(sst_drv_ctx->process_msg_wq);
    flush_workqueue(sst_drv_ctx->process_reply_wq);
    return 0;
}
Пример #5
0
static int sst_cdev_ack(struct device *dev, unsigned int str_id,
		unsigned long bytes)
{
	struct stream_info *stream;
	struct snd_sst_tstamp fw_tstamp = {0,};
	int offset;
	void __iomem *addr;
	struct intel_sst_drv *ctx = dev_get_drvdata(dev);

	stream = get_stream_info(ctx, str_id);
	if (!stream)
		return -EINVAL;

	/* update bytes sent */
	stream->cumm_bytes += bytes;
	dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);

	memcpy_fromio(&fw_tstamp,
		((void *)(ctx->mailbox + ctx->tstamp)
		+(str_id * sizeof(fw_tstamp))),
		sizeof(fw_tstamp));

	fw_tstamp.bytes_copied = stream->cumm_bytes;
	dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
			fw_tstamp.bytes_copied, bytes);

	addr =  ((void *)(ctx->mailbox + ctx->tstamp)) +
			(str_id * sizeof(fw_tstamp));
	offset =  offsetof(struct snd_sst_tstamp, bytes_copied);
	sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
	return 0;
}
Пример #6
0
/*
* intel_sst_suspend - PCI suspend function
*
* @pci: PCI device structure
* @state: PM message
*
* This function is called by OS when a power event occurs
*/
int intel_sst_suspend(struct pci_dev *pci, pm_message_t state)
{
	union config_status_reg csr;

	pr_debug("intel_sst_suspend called\n");

	if (sst_drv_ctx->stream_cnt) {
		pr_err("active streams,not able to suspend\n");
		return -EBUSY;
	}
	/*save fw context*/
	sst_save_dsp_context();
	/*Assert RESET on LPE Processor*/
	csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
	csr.full = csr.full | 0x2;
	/* Move the SST state to Suspended */
	mutex_lock(&sst_drv_ctx->sst_lock);
	sst_drv_ctx->sst_state = SST_SUSPENDED;
	sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
	mutex_unlock(&sst_drv_ctx->sst_lock);
	pci_set_drvdata(pci, sst_drv_ctx);
	pci_save_state(pci);
	pci_disable_device(pci);
	pci_set_power_state(pci, PCI_D3hot);
	return 0;
}
/**
 * intel_sst_reset_dsp_mrst - Resetting SST DSP
 *
 * This resets DSP in case of MRST platfroms
 */
static int intel_sst_reset_dsp_mrst(void)
{
    union config_status_reg csr;

    pr_debug("Resetting the DSP in mrst\n");
    csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
    csr.full |= 0x382;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
    csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
    csr.part.strb_cntr_rst = 0;
    csr.part.run_stall = 0x1;
    csr.part.bypass = 0x7;
    csr.part.sst_reset = 0x1;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
    return 0;
}
/**
 * intel_sst_reset_dsp_medfield - Resetting SST DSP
 *
 * This resets DSP in case of Medfield platfroms
 */
static int intel_sst_reset_dsp_medfield(void)
{
    union config_status_reg csr;

    pr_debug("Resetting the DSP in medfield\n");
    csr.full = 0x048303E2;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);

    return 0;
}
Пример #9
0
/**
 * sst_start_medfield - Start the SST DSP processor
 *
 * This starts the DSP in MRST platfroms
 */
static int sst_start_medfield(void)
{
	union config_status_reg csr;

	csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
	csr.part.bypass = 0;
	sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
	csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
	csr.part.mfld_strb = 1;
	sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
	csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
	csr.part.run_stall = 0;
	csr.part.sst_reset = 0;
	pr_debug("Starting the DSP_medfld %x\n", csr.full);
	sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
	pr_debug("Starting the DSP_medfld\n");

	return 0;
}
Пример #10
0
/*
 * sst_clear_interrupt - clear the SST FW interrupt
 *
 * This function clears the interrupt register after the interrupt
 * bottom half is complete allowing next interrupt to arrive
 */
void sst_clear_ipcx_interrupt(void)
{
	union interrupt_reg isr;
	union interrupt_reg imr;
	uint32_t clear_ipc;

	imr.full = sst_shim_read(sst_drv_ctx->shim, SST_IMRX);
	isr.full = sst_shim_read(sst_drv_ctx->shim, SST_ISRX);
	/*  write 1 to clear  */
	isr.part.ipcx = 1;
	sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full);
	/* Set IA done bit */
	clear_ipc = sst_shim_read(sst_drv_ctx->shim, SST_IPCX);
	soc_ipc_set_busy(&clear_ipc, 0);
	soc_ipc_set_done(&clear_ipc, 0);
	sst_shim_write(sst_drv_ctx->shim, SST_IPCX, clear_ipc);
	/* un mask busy interrupt */
	imr.full &= ~(0x3);
	sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
}
Пример #11
0
/*
 * sst_clear_interrupt - clear the SST FW interrupt
 *
 * This function clears the interrupt register after the interrupt
 * bottom half is complete allowing next interrupt to arrive
 */
void sst_clear_interrupt(void)
{
	union interrupt_reg isr;
	union interrupt_reg imr;
	union ipc_header clear_ipc;

	imr.full = sst_shim_read(sst_drv_ctx->shim, SST_IMRX);
	isr.full = sst_shim_read(sst_drv_ctx->shim, SST_ISRX);
	/*  write 1 to clear  */;
	isr.part.busy_interrupt = 1;
	sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full);
	/* Set IA done bit */
	clear_ipc.full = sst_shim_read(sst_drv_ctx->shim, SST_IPCD);
	clear_ipc.part.busy = 0;
	clear_ipc.part.done = 1;
	clear_ipc.part.data = IPC_ACK_SUCCESS;
	sst_shim_write(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
	/* un mask busy interrupt */
	imr.part.busy_interrupt = 0;
	sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
}
Пример #12
0
/**
* intel_sst_interrupt - Interrupt service routine for SST
*
* @irq:	irq number of interrupt
* @context: pointer to device structre
*
* This function is called by OS when SST device raises
* an interrupt. This will be result of write in IPC register
* Source can be busy or done interrupt
*/
static irqreturn_t intel_sst_interrupt(int irq, void *context)
{
    union interrupt_reg isr, imr;
    union ipc_header header;
    irqreturn_t retval = IRQ_NONE;

    struct intel_sst_drv *drv = (struct intel_sst_drv *) context;

    /* Do not handle interrupt in suspended state */
    if (drv->sst_state == SST_SUSPENDED)
        return IRQ_NONE;

    /* Interrupt arrived, check src */
    isr.full = sst_shim_read(drv->shim, SST_ISRX);
    if (isr.part.done_interrupt) {
        /* Clear done bit */
        spin_lock(&sst_drv_ctx->ipc_spin_lock);
        header.full = sst_shim_read(drv->shim, SST_IPCX);
        header.part.done = 0;
        sst_shim_write(sst_drv_ctx->shim, SST_IPCX, header.full);
        /* write 1 to clear status register */;
        isr.part.done_interrupt = 1;
        sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full);
        spin_unlock(&sst_drv_ctx->ipc_spin_lock);
        queue_work(sst_drv_ctx->post_msg_wq,
                   &sst_drv_ctx->ipc_post_msg.wq);
        retval = IRQ_HANDLED;
    }
    if (isr.part.busy_interrupt) {
        /* mask busy interrupt */
        spin_lock(&sst_drv_ctx->ipc_spin_lock);
        imr.full = sst_shim_read(drv->shim, SST_IMRX);
        imr.part.busy_interrupt = 1;
        sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
        spin_unlock(&sst_drv_ctx->ipc_spin_lock);
        retval = IRQ_WAKE_THREAD;
    }
    return retval;
}
Пример #13
0
/*
 * sst_clear_interrupt - clear the SST FW interrupt
 *
 * This function clears the interrupt register after the interrupt
 * bottom half is complete allowing next interrupt to arrive
 */
void sst_clear_ipcd_interrupt(void)
{
	union interrupt_reg isr;
	union interrupt_reg imr;
	uint32_t clear_ipc;

	imr.full = sst_shim_read(sst_drv_ctx->shim, SST_IMRX);
	isr.full = sst_shim_read(sst_drv_ctx->shim, SST_ISRX);
	/*  write 1 to clear  */ ;
	isr.part.ipcd = 1;
	sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full);
	/* Set IA done bit */
	clear_ipc = sst_shim_read(sst_drv_ctx->shim, SST_IPCD);
	soc_ipc_set_busy(&clear_ipc, 0);
	/* There is no return message from IA to DSP
	 * In general all DSP to IA message are one way message */
	soc_ipc_set_done(&clear_ipc, 0);
	sst_shim_write(sst_drv_ctx->shim, SST_IPCD, clear_ipc);

	/* un mask busy interrupt */
	imr.full &= ~(0x3);
	sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
}
Пример #14
0
/* The runtime_suspend/resume is pretty much similar to the legacy suspend/resume with the noted exception below:
 * The PCI core takes care of taking the system through D3hot and restoring it back to D0 and so there is
 * no need to duplicate that here.
 */
static int intel_sst_runtime_suspend(struct device *dev)
{
	union config_status_reg csr;

	pr_debug("intel_sst_runtime_suspend called\n");
	if (sst_drv_ctx->stream_cnt) {
		pr_err("active streams,not able to suspend\n");
		return -EBUSY;
	}
	/*save fw context*/
	sst_save_dsp_context();
	/*Assert RESET on LPE Processor*/
	csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
	csr.full = csr.full | 0x2;
	/* Move the SST state to Suspended */
	mutex_lock(&sst_drv_ctx->sst_lock);
	sst_drv_ctx->sst_state = SST_SUSPENDED;

	/* Only needed by Medfield */
	if (sst_drv_ctx->pci_id != SST_MRST_PCI_ID)
		sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
	mutex_unlock(&sst_drv_ctx->sst_lock);
	return 0;
}
Пример #15
0
static int intel_sst_runtime_resume(struct device *dev)
{
    u32 csr;

    pr_debug("runtime_resume called\n");
    if (sst_drv_ctx->sst_state != SST_SUSPENDED) {
        pr_err("SST is not in suspended state\n");
        return 0;
    }
    csr = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
    /*
     * To restore the csr_value after S0ix and S3 states.
     * The value 0x30000 is to enable LPE dram high and low addresses.
     * Reference:
     * Penwell Audio Voice Module HAS 1.61 Section - 13.12.1 -
     * CSR - Configuration and Status Register.
     */
    csr |= (sst_drv_ctx->csr_value | 0x30000);
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr);

    /* GPIO_PIN 12,13,74,75 needs to be configured in
     * ALT_FUNC_2 mode for SSP3 IOs
     */
    if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) {
        lnw_gpio_set_alt(CLV_I2S_3_CLK_GPIO_PIN, LNW_ALT_2);
        lnw_gpio_set_alt(CLV_I2S_3_FS_GPIO_PIN, LNW_ALT_2);
        lnw_gpio_set_alt(CLV_I2S_3_TXD_GPIO_PIN, LNW_ALT_2);
        lnw_gpio_set_alt(CLV_I2S_3_RXD_GPIO_PIN, LNW_ALT_2);
        lnw_gpio_set_alt(CLV_VIBRA_PWM_GPIO_PIN, LNW_ALT_2);

        vibra_pwm_configure(true);
    }

    sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT);
    return 0;
}
Пример #16
0
/*
* intel_sst_probe - PCI probe function
*
* @pci:	PCI device structure
* @pci_id: PCI device ID structure
*
* This function is called by OS when a device is found
* This enables the device, interrupt etc
*/
static int __devinit intel_sst_probe(struct pci_dev *pci,
			const struct pci_device_id *pci_id)
{
	int i, ret = 0;

	pr_debug("Probe for DID %x\n", pci->device);
	mutex_lock(&drv_ctx_lock);
	if (sst_drv_ctx) {
		pr_err("Only one sst handle is supported\n");
		mutex_unlock(&drv_ctx_lock);
		return -EBUSY;
	}

	sst_drv_ctx = kzalloc(sizeof(*sst_drv_ctx), GFP_KERNEL);
	if (!sst_drv_ctx) {
		pr_err("malloc fail\n");
		mutex_unlock(&drv_ctx_lock);
		return -ENOMEM;
	}
	mutex_unlock(&drv_ctx_lock);

	sst_drv_ctx->pci_id = pci->device;

	mutex_init(&sst_drv_ctx->stream_lock);
	mutex_init(&sst_drv_ctx->sst_lock);
	sst_drv_ctx->pmic_state = SND_MAD_UN_INIT;

	sst_drv_ctx->stream_cnt = 0;
	sst_drv_ctx->encoded_cnt = 0;
	sst_drv_ctx->am_cnt = 0;
	sst_drv_ctx->pb_streams = 0;
	sst_drv_ctx->cp_streams = 0;
	sst_drv_ctx->unique_id = 0;
	sst_drv_ctx->pmic_port_instance = SST_DEFAULT_PMIC_PORT;

	INIT_LIST_HEAD(&sst_drv_ctx->ipc_dispatch_list);
	INIT_WORK(&sst_drv_ctx->ipc_post_msg.wq, sst_post_message);
	INIT_WORK(&sst_drv_ctx->ipc_process_msg.wq, sst_process_message);
	INIT_WORK(&sst_drv_ctx->ipc_process_reply.wq, sst_process_reply);
	INIT_WORK(&sst_drv_ctx->mad_ops.wq, sst_process_mad_ops);
	init_waitqueue_head(&sst_drv_ctx->wait_queue);

	sst_drv_ctx->mad_wq = create_workqueue("sst_mad_wq");
	if (!sst_drv_ctx->mad_wq)
		goto do_free_drv_ctx;
	sst_drv_ctx->post_msg_wq = create_workqueue("sst_post_msg_wq");
	if (!sst_drv_ctx->post_msg_wq)
		goto free_mad_wq;
	sst_drv_ctx->process_msg_wq = create_workqueue("sst_process_msg_wqq");
	if (!sst_drv_ctx->process_msg_wq)
		goto free_post_msg_wq;
	sst_drv_ctx->process_reply_wq = create_workqueue("sst_proces_reply_wq");
	if (!sst_drv_ctx->process_reply_wq)
		goto free_process_msg_wq;

	for (i = 0; i < MAX_ACTIVE_STREAM; i++) {
		sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
		sst_drv_ctx->alloc_block[i].ops_block.condition = false;
	}
	spin_lock_init(&sst_drv_ctx->list_spin_lock);

	sst_drv_ctx->max_streams = pci_id->driver_data;
	pr_debug("Got drv data max stream %d\n",
				sst_drv_ctx->max_streams);
	for (i = 1; i <= sst_drv_ctx->max_streams; i++) {
		struct stream_info *stream = &sst_drv_ctx->streams[i];
		INIT_LIST_HEAD(&stream->bufs);
		mutex_init(&stream->lock);
		spin_lock_init(&stream->pcm_lock);
	}
	if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
		sst_drv_ctx->mmap_mem = NULL;
		sst_drv_ctx->mmap_len = SST_MMAP_PAGES * PAGE_SIZE;
		while (sst_drv_ctx->mmap_len > 0) {
			sst_drv_ctx->mmap_mem =
				kzalloc(sst_drv_ctx->mmap_len, GFP_KERNEL);
			if (sst_drv_ctx->mmap_mem) {
				pr_debug("Got memory %p size 0x%x\n",
					sst_drv_ctx->mmap_mem,
					sst_drv_ctx->mmap_len);
				break;
			}
			if (sst_drv_ctx->mmap_len < (SST_MMAP_STEP*PAGE_SIZE)) {
				pr_err("mem alloc fail...abort!!\n");
				ret = -ENOMEM;
				goto free_process_reply_wq;
			}
			sst_drv_ctx->mmap_len -= (SST_MMAP_STEP * PAGE_SIZE);
			pr_debug("mem alloc failed...trying %d\n",
						sst_drv_ctx->mmap_len);
		}
	}

	/* Init the device */
	ret = pci_enable_device(pci);
	if (ret) {
		pr_err("device can't be enabled\n");
		goto do_free_mem;
	}
	sst_drv_ctx->pci = pci_dev_get(pci);
	ret = pci_request_regions(pci, SST_DRV_NAME);
	if (ret)
		goto do_disable_device;
	/* map registers */
	/* SST Shim */
	sst_drv_ctx->shim_phy_add = pci_resource_start(pci, 1);
	sst_drv_ctx->shim = pci_ioremap_bar(pci, 1);
	if (!sst_drv_ctx->shim)
		goto do_release_regions;
	pr_debug("SST Shim Ptr %p\n", sst_drv_ctx->shim);

	/* Shared SRAM */
	sst_drv_ctx->mailbox = pci_ioremap_bar(pci, 2);
	if (!sst_drv_ctx->mailbox)
		goto do_unmap_shim;
	pr_debug("SRAM Ptr %p\n", sst_drv_ctx->mailbox);

	/* IRAM */
	sst_drv_ctx->iram = pci_ioremap_bar(pci, 3);
	if (!sst_drv_ctx->iram)
		goto do_unmap_sram;
	pr_debug("IRAM Ptr %p\n", sst_drv_ctx->iram);

	/* DRAM */
	sst_drv_ctx->dram = pci_ioremap_bar(pci, 4);
	if (!sst_drv_ctx->dram)
		goto do_unmap_iram;
	pr_debug("DRAM Ptr %p\n", sst_drv_ctx->dram);

	mutex_lock(&sst_drv_ctx->sst_lock);
	sst_drv_ctx->sst_state = SST_UN_INIT;
	mutex_unlock(&sst_drv_ctx->sst_lock);
	/* Register the ISR */
	ret = request_irq(pci->irq, intel_sst_interrupt,
		IRQF_SHARED, SST_DRV_NAME, sst_drv_ctx);
	if (ret)
		goto do_unmap_dram;
	pr_debug("Registered IRQ 0x%x\n", pci->irq);

	/*Register LPE Control as misc driver*/
	ret = misc_register(&lpe_ctrl);
	if (ret) {
		pr_err("couldn't register control device\n");
		goto do_free_irq;
	}

	if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
		ret = misc_register(&lpe_dev);
		if (ret) {
			pr_err("couldn't register LPE device\n");
			goto do_free_misc;
 		}
	} else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) {
		u32 csr;

		/*allocate mem for fw context save during suspend*/
		sst_drv_ctx->fw_cntx = kzalloc(FW_CONTEXT_MEM, GFP_KERNEL);
		if (!sst_drv_ctx->fw_cntx) {
			ret = -ENOMEM;
			goto do_free_misc;
		}
		/*setting zero as that is valid mem to restore*/
		sst_drv_ctx->fw_cntx_size = 0;

		/*set lpe start clock and ram size*/
		csr = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
		csr |= 0x30060; /*remove the clock ratio after fw fix*/
		sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr);
	}
	sst_drv_ctx->lpe_stalled = 0;
	pci_set_drvdata(pci, sst_drv_ctx);
	pm_runtime_allow(&pci->dev);
	pm_runtime_put_noidle(&pci->dev);
	pr_debug("...successfully done!!!\n");
	return ret;

do_free_misc:
	misc_deregister(&lpe_ctrl);
do_free_irq:
	free_irq(pci->irq, sst_drv_ctx);
do_unmap_dram:
	iounmap(sst_drv_ctx->dram);
do_unmap_iram:
	iounmap(sst_drv_ctx->iram);
do_unmap_sram:
	iounmap(sst_drv_ctx->mailbox);
do_unmap_shim:
	iounmap(sst_drv_ctx->shim);
do_release_regions:
	pci_release_regions(pci);
do_disable_device:
	pci_disable_device(pci);
do_free_mem:
	kfree(sst_drv_ctx->mmap_mem);
free_process_reply_wq:
	destroy_workqueue(sst_drv_ctx->process_reply_wq);
free_process_msg_wq:
	destroy_workqueue(sst_drv_ctx->process_msg_wq);
free_post_msg_wq:
	destroy_workqueue(sst_drv_ctx->post_msg_wq);
free_mad_wq:
	destroy_workqueue(sst_drv_ctx->mad_wq);
do_free_drv_ctx:
	kfree(sst_drv_ctx);
	sst_drv_ctx = NULL;
	pr_err("Probe failed with %d\n", ret);
	return ret;
}
Пример #17
0
/**
* intel_sst_interrupt - Interrupt service routine for SST
*
* @irq:	irq number of interrupt
* @context: pointer to device structre
*
* This function is called by OS when SST device raises
* an interrupt. This will be result of write in IPC register
* Source can be busy or done interrupt
*/
static irqreturn_t intel_sst_interrupt(int irq, void *context)
{
	union interrupt_reg isr;
	union ipc_header header;
	union interrupt_reg imr;
	struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
	unsigned int size = 0, str_id;
	struct stream_info *stream ;

	/* Do not handle interrupt in suspended state */
	if (drv->sst_state == SST_SUSPENDED)
		return IRQ_NONE;
	/* Interrupt arrived, check src */
	isr.full = sst_shim_read(drv->shim, SST_ISRX);

	if (isr.part.busy_interrupt) {
		header.full = sst_shim_read(drv->shim, SST_IPCD);
		if (header.part.msg_id == IPC_SST_PERIOD_ELAPSED) {
			sst_clear_interrupt();
			str_id = header.part.str_id;
			stream = &sst_drv_ctx->streams[str_id];
			if (stream->period_elapsed)
				stream->period_elapsed(stream->pcm_substream);
			return IRQ_HANDLED;
		}
		if (header.part.large)
			size = header.part.data;
		if (header.part.msg_id & REPLY_MSG) {
			sst_drv_ctx->ipc_process_msg.header = header;
			memcpy_fromio(sst_drv_ctx->ipc_process_msg.mailbox,
				drv->mailbox + SST_MAILBOX_RCV, size);
			queue_work(sst_drv_ctx->process_msg_wq,
					&sst_drv_ctx->ipc_process_msg.wq);
		} else {
			sst_drv_ctx->ipc_process_reply.header = header;
			memcpy_fromio(sst_drv_ctx->ipc_process_reply.mailbox,
				drv->mailbox + SST_MAILBOX_RCV, size);
			queue_work(sst_drv_ctx->process_reply_wq,
					&sst_drv_ctx->ipc_process_reply.wq);
		}
		/* mask busy inetrrupt */
		imr.full = sst_shim_read(drv->shim, SST_IMRX);
		imr.part.busy_interrupt = 1;
		sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full);
		return IRQ_HANDLED;
	} else if (isr.part.done_interrupt) {
		/* Clear done bit */
		header.full = sst_shim_read(drv->shim, SST_IPCX);
		header.part.done = 0;
		sst_shim_write(sst_drv_ctx->shim, SST_IPCX, header.full);
		/* write 1 to clear status register */;
		isr.part.done_interrupt = 1;
		/* dummy register for shim workaround */
		sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full);
		queue_work(sst_drv_ctx->post_msg_wq,
			&sst_drv_ctx->ipc_post_msg.wq);
		return IRQ_HANDLED;
	} else
		return IRQ_NONE;

}
/*This function is called when any codec/post processing library
 needs to be downloaded*/
static int sst_download_library(const struct firmware *fw_lib,
                                struct snd_sst_lib_download_info *lib)
{
    /* send IPC message and wait */
    int i;
    u8 pvt_id;
    struct ipc_post *msg = NULL;
    union config_status_reg csr;
    struct snd_sst_str_type str_type = {0};
    int retval = 0;

    if (sst_create_large_msg(&msg))
        return -ENOMEM;

    pvt_id = sst_assign_pvt_id(sst_drv_ctx);
    i = sst_get_block_stream(sst_drv_ctx);
    pr_debug("alloc block allocated = %d, pvt_id %d\n", i, pvt_id);
    if (i < 0) {
        kfree(msg);
        return -ENOMEM;
    }
    sst_drv_ctx->alloc_block[i].sst_id = pvt_id;
    sst_fill_header(&msg->header, IPC_IA_PREP_LIB_DNLD, 1, pvt_id);
    msg->header.part.data = sizeof(u32) + sizeof(str_type);
    str_type.codec_type = lib->dload_lib.lib_info.lib_type;
    /*str_type.pvt_id = pvt_id;*/
    memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
    memcpy(msg->mailbox_data + sizeof(u32), &str_type, sizeof(str_type));
    spin_lock(&sst_drv_ctx->list_spin_lock);
    list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list);
    spin_unlock(&sst_drv_ctx->list_spin_lock);
    sst_post_message(&sst_drv_ctx->ipc_post_msg_wq);
    retval = sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i]);
    if (retval) {
        /* error */
        sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
        pr_err("Prep codec downloaded failed %d\n",
               retval);
        return -EIO;
    }
    pr_debug("FW responded, ready for download now...\n");
    /* downloading on success */
    mutex_lock(&sst_drv_ctx->sst_lock);
    sst_drv_ctx->sst_state = SST_FW_LOADED;
    mutex_unlock(&sst_drv_ctx->sst_lock);
    csr.full = readl(sst_drv_ctx->shim + SST_CSR);
    csr.part.run_stall = 1;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);

    csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
    csr.part.bypass = 0x7;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);

    sst_parse_fw_image(fw_lib);

    /* set the FW to running again */
    csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
    csr.part.bypass = 0x0;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);

    csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
    csr.part.run_stall = 0;
    sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);

    /* send download complete and wait */
    if (sst_create_large_msg(&msg)) {
        sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
        return -ENOMEM;
    }

    sst_fill_header(&msg->header, IPC_IA_LIB_DNLD_CMPLT, 1, pvt_id);
    sst_drv_ctx->alloc_block[i].sst_id = pvt_id;
    msg->header.part.data = sizeof(u32) + sizeof(*lib);
    lib->pvt_id = pvt_id;
    memcpy(msg->mailbox_data, &msg->header, sizeof(u32));
    memcpy(msg->mailbox_data + sizeof(u32), lib, sizeof(*lib));
    spin_lock(&sst_drv_ctx->list_spin_lock);
    list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list);
    spin_unlock(&sst_drv_ctx->list_spin_lock);
    sst_post_message(&sst_drv_ctx->ipc_post_msg_wq);
    pr_debug("Waiting for FW response Download complete\n");
    sst_drv_ctx->alloc_block[i].ops_block.condition = false;
    retval = sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i]);
    if (retval) {
        /* error */
        mutex_lock(&sst_drv_ctx->sst_lock);
        sst_drv_ctx->sst_state = SST_UN_INIT;
        mutex_unlock(&sst_drv_ctx->sst_lock);
        sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
        return -EIO;
    }

    pr_debug("FW success on Download complete\n");
    sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
    mutex_lock(&sst_drv_ctx->sst_lock);
    sst_drv_ctx->sst_state = SST_FW_RUNNING;
    mutex_unlock(&sst_drv_ctx->sst_lock);
    return 0;

}
Пример #19
0
/*
* intel_sst_probe - PCI probe function
*
* @pci:	PCI device structure
* @pci_id: PCI device ID structure
*
* This function is called by OS when a device is found
* This enables the device, interrupt etc
*/
static int __devinit intel_sst_probe(struct pci_dev *pci,
                                     const struct pci_device_id *pci_id)
{
    int i, ret = 0;

    pr_debug("Probe for DID %x\n", pci->device);
    mutex_lock(&drv_ctx_lock);
    if (sst_drv_ctx) {
        pr_err("Only one sst handle is supported\n");
        mutex_unlock(&drv_ctx_lock);
        return -EBUSY;
    }

    sst_drv_ctx = kzalloc(sizeof(*sst_drv_ctx), GFP_KERNEL);
    if (!sst_drv_ctx) {
        pr_err("malloc fail\n");
        mutex_unlock(&drv_ctx_lock);
        return -ENOMEM;
    }
    mutex_unlock(&drv_ctx_lock);

    sst_drv_ctx->pci_id = pci->device;

    mutex_init(&sst_drv_ctx->stream_lock);
    mutex_init(&sst_drv_ctx->sst_lock);
    mutex_init(&sst_drv_ctx->mixer_ctrl_lock);

    sst_drv_ctx->stream_cnt = 0;
    sst_drv_ctx->encoded_cnt = 0;
    sst_drv_ctx->am_cnt = 0;
    sst_drv_ctx->pb_streams = 0;
    sst_drv_ctx->cp_streams = 0;
    sst_drv_ctx->unique_id = 0;
    sst_drv_ctx->pmic_port_instance = SST_DEFAULT_PMIC_PORT;
    sst_drv_ctx->fw = NULL;
    sst_drv_ctx->fw_in_mem = NULL;

    INIT_LIST_HEAD(&sst_drv_ctx->ipc_dispatch_list);
    INIT_WORK(&sst_drv_ctx->ipc_post_msg.wq, sst_post_message);
    INIT_WORK(&sst_drv_ctx->ipc_process_msg.wq, sst_process_message);
    INIT_WORK(&sst_drv_ctx->ipc_process_reply.wq, sst_process_reply);
    init_waitqueue_head(&sst_drv_ctx->wait_queue);

    sst_drv_ctx->mad_wq = create_singlethread_workqueue("sst_mad_wq");
    if (!sst_drv_ctx->mad_wq)
        goto do_free_drv_ctx;
    sst_drv_ctx->post_msg_wq = create_workqueue("sst_post_msg_wq");
    if (!sst_drv_ctx->post_msg_wq)
        goto free_mad_wq;
    sst_drv_ctx->process_msg_wq = create_workqueue("sst_process_msg_wqq");
    if (!sst_drv_ctx->process_msg_wq)
        goto free_post_msg_wq;
    sst_drv_ctx->process_reply_wq = create_workqueue("sst_proces_reply_wq");
    if (!sst_drv_ctx->process_reply_wq)
        goto free_process_msg_wq;

    for (i = 0; i < MAX_ACTIVE_STREAM; i++) {
        sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
        sst_drv_ctx->alloc_block[i].ops_block.condition = false;
    }
    spin_lock_init(&sst_drv_ctx->ipc_spin_lock);

    sst_drv_ctx->max_streams = pci_id->driver_data;
    pr_debug("Got drv data max stream %d\n",
             sst_drv_ctx->max_streams);
    for (i = 1; i <= sst_drv_ctx->max_streams; i++) {
        struct stream_info *stream = &sst_drv_ctx->streams[i];
        INIT_LIST_HEAD(&stream->bufs);
        mutex_init(&stream->lock);
        spin_lock_init(&stream->pcm_lock);
    }
    if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
        sst_drv_ctx->mmap_mem = NULL;
        sst_drv_ctx->mmap_len = SST_MMAP_PAGES * PAGE_SIZE;
        while (sst_drv_ctx->mmap_len > 0) {
            sst_drv_ctx->mmap_mem =
                kzalloc(sst_drv_ctx->mmap_len, GFP_KERNEL);
            if (sst_drv_ctx->mmap_mem) {
                pr_debug("Got memory %p size 0x%x\n",
                         sst_drv_ctx->mmap_mem,
                         sst_drv_ctx->mmap_len);
                break;
            }
            if (sst_drv_ctx->mmap_len < (SST_MMAP_STEP*PAGE_SIZE)) {
                pr_err("mem alloc fail...abort!!\n");
                ret = -ENOMEM;
                goto free_process_reply_wq;
            }
            sst_drv_ctx->mmap_len -= (SST_MMAP_STEP * PAGE_SIZE);
            pr_debug("mem alloc failed...trying %d\n",
                     sst_drv_ctx->mmap_len);
        }
    }
    if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) {
        sst_drv_ctx->device_input_mixer = SST_STREAM_DEVICE_IHF
                                          | SST_INPUT_STREAM_PCM;
    }

    /* Init the device */
    ret = pci_enable_device(pci);
    if (ret) {
        pr_err("device can't be enabled\n");
        goto do_free_mem;
    }
    sst_drv_ctx->pci = pci_dev_get(pci);
    ret = pci_request_regions(pci, SST_DRV_NAME);
    if (ret)
        goto do_disable_device;
    /* map registers */
    /* SST Shim */
    sst_drv_ctx->shim_phy_add = pci_resource_start(pci, 1);
    sst_drv_ctx->shim = pci_ioremap_bar(pci, 1);
    if (!sst_drv_ctx->shim)
        goto do_release_regions;
    pr_debug("SST Shim Ptr %p\n", sst_drv_ctx->shim);

    /* Shared SRAM */
    sst_drv_ctx->mailbox = pci_ioremap_bar(pci, 2);
    if (!sst_drv_ctx->mailbox)
        goto do_unmap_shim;
    pr_debug("SRAM Ptr %p\n", sst_drv_ctx->mailbox);

    /* IRAM */
    sst_drv_ctx->iram_base = pci_resource_start(pci, 3);
    sst_drv_ctx->iram = pci_ioremap_bar(pci, 3);
    if (!sst_drv_ctx->iram)
        goto do_unmap_sram;
    pr_debug("IRAM Ptr %p\n", sst_drv_ctx->iram);

    /* DRAM */
    sst_drv_ctx->dram_base = pci_resource_start(pci, 4);
    sst_drv_ctx->dram = pci_ioremap_bar(pci, 4);
    if (!sst_drv_ctx->dram)
        goto do_unmap_iram;
    pr_debug("DRAM Ptr %p\n", sst_drv_ctx->dram);

    sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT);
    /* Register the ISR */
    ret = request_threaded_irq(pci->irq, intel_sst_interrupt,
                               intel_sst_irq_thread, IRQF_SHARED, SST_DRV_NAME,
                               sst_drv_ctx);
    if (ret)
        goto do_unmap_dram;
    pr_debug("Registered IRQ 0x%x\n", pci->irq);

    /*Register LPE Control as misc driver*/
    ret = misc_register(&lpe_ctrl);
    if (ret) {
        pr_err("couldn't register control device\n");
        goto do_free_irq;
    }

    if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
        ret = misc_register(&lpe_dev);
        if (ret) {
            pr_err("couldn't register LPE device\n");
            goto do_free_misc;
        }
    } else if ((sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) ||
               (sst_drv_ctx->pci_id == SST_CLV_PCI_ID)) {
        u32 csr;
        u32 csr2;
        u32 clkctl;

        /*allocate mem for fw context save during suspend*/
        sst_drv_ctx->fw_cntx = kzalloc(FW_CONTEXT_MEM, GFP_KERNEL);
        if (!sst_drv_ctx->fw_cntx) {
            ret = -ENOMEM;
            goto do_free_misc;
        }
        /*setting zero as that is valid mem to restore*/
        sst_drv_ctx->fw_cntx_size = 0;

        /*set lpe start clock and ram size*/
        csr = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
        csr |= 0x30000;
        /*make sure clksel set to OSC for SSP0,1 (default)*/
        csr &= 0xFFFFFFF3;
        sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr);

        /*set clock output enable for SSP0,1,3*/
        clkctl = sst_shim_read(sst_drv_ctx->shim, SST_CLKCTL);
        if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID)
            clkctl |= (0x7 << 16);
        else
            clkctl |= ((1<<16)|(1<<17));
        sst_shim_write(sst_drv_ctx->shim, SST_CLKCTL, clkctl);

        /* set SSP0 & SSP1 disable DMA Finish*/
        csr2 = sst_shim_read(sst_drv_ctx->shim, SST_CSR2);
        /*set SSP3 disable DMA finsh for SSSP3 */
        csr2 |= BIT(1)|BIT(2);
        sst_shim_write(sst_drv_ctx->shim, SST_CSR2, csr2);
    }

    /* GPIO_PIN 12,13,74,75 needs to be configured in
     * ALT_FUNC_2 mode for SSP3 IOs
     */
    if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) {
        lnw_gpio_set_alt(CLV_I2S_3_CLK_GPIO_PIN, LNW_ALT_2);
        lnw_gpio_set_alt(CLV_I2S_3_FS_GPIO_PIN, LNW_ALT_2);
        lnw_gpio_set_alt(CLV_I2S_3_TXD_GPIO_PIN, LNW_ALT_2);
        lnw_gpio_set_alt(CLV_I2S_3_RXD_GPIO_PIN, LNW_ALT_2);
        lnw_gpio_set_alt(CLV_VIBRA_PWM_GPIO_PIN, LNW_ALT_2);

        vibra_pwm_configure(true);
    }

    sst_drv_ctx->lpe_stalled = 0;
    pci_set_drvdata(pci, sst_drv_ctx);
    pm_runtime_allow(&pci->dev);
    pm_runtime_put_noidle(&pci->dev);
    register_sst(&pci->dev);

    sst_drv_ctx->qos = kzalloc(sizeof(struct pm_qos_request_list), GFP_KERNEL);
    if (!sst_drv_ctx->qos)
        goto do_free_misc;
    pm_qos_add_request(sst_drv_ctx->qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);

    pr_info("%s successfully done!\n", __func__);
    return ret;

do_free_misc:
    misc_deregister(&lpe_ctrl);
do_free_irq:
    free_irq(pci->irq, sst_drv_ctx);
do_unmap_dram:
    iounmap(sst_drv_ctx->dram);
do_unmap_iram:
    iounmap(sst_drv_ctx->iram);
do_unmap_sram:
    iounmap(sst_drv_ctx->mailbox);
do_unmap_shim:
    iounmap(sst_drv_ctx->shim);
do_release_regions:
    pci_release_regions(pci);
do_disable_device:
    pci_disable_device(pci);
do_free_mem:
    kfree(sst_drv_ctx->mmap_mem);
free_process_reply_wq:
    destroy_workqueue(sst_drv_ctx->process_reply_wq);
free_process_msg_wq:
    destroy_workqueue(sst_drv_ctx->process_msg_wq);
free_post_msg_wq:
    destroy_workqueue(sst_drv_ctx->post_msg_wq);
free_mad_wq:
    destroy_workqueue(sst_drv_ctx->mad_wq);
do_free_drv_ctx:
    kfree(sst_drv_ctx);
    sst_drv_ctx = NULL;
    pr_err("Probe failed with %d\n", ret);
    return ret;
}
Пример #20
0
/**
 * sst_post_message - Posts message to SST
 * @msg: IPC message to be posted
 *
 * This function is called by any component in driver which
 * wants to send an IPC message. This will post message only if
 * busy bit is free
 *
 * The caller must ensure that this is single threaded.
 */
enum soc_result sst_post_message(struct ipc_post *msg)
{
	uint32_t header;

	pr_debug("post message called, ID = %x\n",
		 soc_ipc_get_msg_id(msg->header));

	/* check busy bit */
	header = sst_shim_read(sst_drv_ctx->shim, SST_IPCX);
	if (soc_ipc_get_busy(header) || soc_ipc_get_done(header)) {
		/* busy, unmask */
		pr_err("Busy not free...\n");
		return SOC_ERROR_NO_RESOURCES;
	}
	if (soc_ipc_get_size(msg->header) > SOC_AUDIO_MAILBOX_SIZE_IPCX_SEND) {
		pr_err("mailbox data size is large %d\n",
		       soc_ipc_get_size(msg->header));
		return SOC_ERROR_INVALID_PARAMETER;
	}
	if (SOC_IPC_IA_CONFIG_PIPE == soc_ipc_get_msg_id(msg->header)) {
		struct audio_htod_pipe_conf_t conf_msg;
		struct ipc_config_params *ipc_config_msg;
		struct soc_audio_pipeline *pipe;
		/* For sending config message realign all the pointers */
		ipc_config_msg = (struct ipc_config_params *)
		&(msg->mailbox_data[0]);
		conf_msg.ctx = ipc_config_msg->ctx;
		pipe = ipc_config_msg->pipe;
		conf_msg.num_stages = pipe->num_stages;
		if (SOC_AUDIO_MAIN_PIPE == pipe->type) {
			/* Post processing pipeline: in SRAM */
			conf_msg.psm_stages =
				(struct soc_audio_pipeline_stage *)
				(SOC_AUDIO_DSP_MAILBOX_ADDR
				+ ((void *)&pipe->stages[0] -
				sst_drv_ctx->mailbox));
		} else {
			/* Other pipelines: in DSP DRAM */
			conf_msg.psm_stages =
			(struct soc_audio_pipeline_stage *)
			    (SOC_AUDIO_DSP_DRAM_ADDR +
			    SOC_AUDIO_DRAM_PIPELINE_OFFSET +
			    ((void *)&pipe->stages[0] -
			     (void *)dram_pipelines_shadow));
		}
		soc_ipc_set_size(&msg->header, sizeof(conf_msg));
		memcpy_toio(sst_drv_ctx->mailbox +
			    SOC_AUDIO_MAILBOX_SEND_OFFSET, &conf_msg,
			    soc_ipc_get_size(msg->header));

		soc_audio_config_preprocess(pipe, (void *)conf_msg.psm_stages);

		if (SOC_AUDIO_MAIN_PIPE != pipe->type) {
			pr_debug("sst_post_message: pipe_shadow"
					 "%p psm_stages %p\n",
					 dram_pipelines_shadow,
					 conf_msg.psm_stages);
			sst_pause_dsp();
			memcpy_toio(sst_drv_ctx->dram +
				    SOC_AUDIO_DRAM_PIPELINE_OFFSET +
				    ((void *)pipe -
				     (void *)dram_pipelines_shadow),
				    (void *)pipe, SOC_AUDIO_DEC_PIPE_SIZE);
			sst_resume_dsp();
		}
		sst_shim_write(sst_drv_ctx->shim, SST_IPCX, msg->header);

	} else {
		memcpy_toio(sst_drv_ctx->mailbox +
			    SOC_AUDIO_MAILBOX_SEND_OFFSET, msg->mailbox_data,
			    soc_ipc_get_size(msg->header));
		sst_shim_write(sst_drv_ctx->shim, SST_IPCX, msg->header);
	}

	return SOC_SUCCESS;
}