return block_ts;
}
+/**
+ * get_reg_width - computes the DMA sample width
+ * @kernel_width: Kernel DMA slave bus width
+ *
+ * converts the DMA kernel slave bus width in the Intel DMA
+ * bus width
+ */
+static int get_reg_width(enum dma_slave_buswidth kernel_width)
+{
+ int reg_width = -1;
+
+ switch (kernel_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ reg_width = 0;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ reg_width = 1;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ reg_width = 2;
+ break;
+ case DMA_SLAVE_BUSWIDTH_UNDEFINED:
+ case DMA_SLAVE_BUSWIDTH_8_BYTES:
+ default:
+ pr_err("ERR_MDMA: get_reg_width unsupported reg width\n");
+ break;
+ }
+ return reg_width;
+}
+
+
/*****************************************************************************
DMAC1 interrupt Functions*/
/**
* dmac1_mask_periphral_intr - mask the periphral interrupt
- * @midc: dma channel for which masking is required
+ * @mid: dma device for which masking is required
*
* Masks the DMA periphral interrupt
* this is valid for DMAC1 family controllers only
* This controller should have periphral mask registers already mapped
*/
-static void dmac1_mask_periphral_intr(struct intel_mid_dma_chan *midc)
+static void dmac1_mask_periphral_intr(struct middma_device *mid)
{
u32 pimr;
- struct middma_device *mid = to_middma_device(midc->chan.device);
if (mid->pimr_mask) {
pimr = readl(mid->mask_reg + LNW_PERIPHRAL_MASK);
static void disable_dma_interrupt(struct intel_mid_dma_chan *midc)
{
/*Check LPE PISR, make sure fwd is disabled*/
- dmac1_mask_periphral_intr(midc);
iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_BLOCK);
iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_TFR);
iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_ERR);
* Load a transaction into the engine. This must be called with midc->lock
* held and bh disabled.
*/
-static void midc_dostart(struct intel_mid_dma_chan *midc,
+static int midc_dostart(struct intel_mid_dma_chan *midc,
struct intel_mid_dma_desc *first)
{
struct middma_device *mid = to_middma_device(midc->chan.device);
/*error*/
pr_err("ERR_MDMA: channel is busy in start\n");
/* The tasklet will hopefully advance the queue... */
- return;
+ return -EBUSY;
}
midc->busy = true;
/*write registers and en*/
first->status = DMA_IN_PROGRESS;
iowrite32(ENABLE_CHANNEL(midc->ch_id), mid->dma_base + DMA_CHAN_EN);
+ return 0;
}
/**
desc->current_lli = 0;
}
spin_unlock_bh(&midc->lock);
- if (callback_txd) {
- pr_debug("MDMA: TXD callback set ... calling\n");
- callback_txd(param_txd);
- }
if (midc->raw_tfr) {
desc->status = DMA_SUCCESS;
- if (desc->lli != NULL) {
+ if (desc->lli != NULL && desc->lli->llp != NULL) {
pci_pool_free(desc->lli_pool, desc->lli,
desc->lli_phys);
pci_pool_destroy(desc->lli_pool);
list_move(&desc->desc_node, &midc->free_list);
midc->busy = false;
}
+ if (callback_txd) {
+ pr_debug("MDMA: TXD callback set ... calling\n");
+ callback_txd(param_txd);
+ }
+
spin_lock_bh(&midc->lock);
+}
+static struct
+intel_mid_dma_desc *midc_first_queued(struct intel_mid_dma_chan *midc)
+{
+ return list_entry(midc->queue.next, struct intel_mid_dma_desc, desc_node);
}
/**
* midc_scan_descriptors - check the descriptors in channel
if (desc->status == DMA_IN_PROGRESS)
midc_descriptor_complete(midc, desc);
}
- return;
+
+ if (!list_empty(&midc->queue)) {
+ pr_debug("MDMA: submitting txn in queue\n");
+ if (0 == midc_dostart(midc, midc_first_queued(midc)))
+ list_splice_init(&midc->queue, &midc->active_list);
+ else
+ pr_warn("Submit failed as ch is busy\n");
}
+ return;
+}
/**
* midc_lli_fill_sg - Helper function to convert
* SG list to Linked List Items.
ret = dma_async_is_complete(cookie, last_complete, last_used);
if (ret != DMA_SUCCESS) {
+ spin_lock_bh(&midc->lock);
midc_scan_descriptors(to_middma_device(chan->device), midc);
+ spin_unlock_bh(&midc->lock);
last_complete = midc->completed;
last_used = chan->cookie;
union intel_mid_dma_cfg_lo cfg_lo;
union intel_mid_dma_cfg_hi cfg_hi;
enum dma_slave_buswidth width;
+ int dst_reg_width = 0;
+ int src_reg_width = 0;
pr_debug("MDMA: Prep for memcpy\n");
BUG_ON(!chan);
cfg_hi.cfgx.dst_per = 3;
if (mids->device_instance == 1)
cfg_hi.cfgx.dst_per = 1;
- } else if (mids->dma_slave.direction == DMA_FROM_DEVICE) {
+ } else if (mids->dma_slave.direction ==
+ DMA_FROM_DEVICE) {
if (mids->device_instance == 0)
cfg_hi.cfgx.src_per = 2;
if (mids->device_instance == 1)
/*calculate CTL_LO*/
ctl_lo.ctl_lo = 0;
ctl_lo.ctlx.int_en = 1;
+
+ dst_reg_width = get_reg_width(mids->dma_slave.dst_addr_width);
+ if (dst_reg_width < 0) {
+ pr_err("ERR_MDMA: Failed to get DST reg width\n");
+ return NULL;
+
+ }
+ ctl_lo.ctlx.dst_tr_width = dst_reg_width;
+
+ src_reg_width = get_reg_width(mids->dma_slave.src_addr_width);
+ if (src_reg_width < 0) {
+ pr_err("ERR_MDMA: Failed to get SRC reg width\n");
+ return NULL;
+ }
+ ctl_lo.ctlx.src_tr_width = src_reg_width;
+
+
+
ctl_lo.ctlx.dst_msize = mids->dma_slave.src_maxburst;
ctl_lo.ctlx.src_msize = mids->dma_slave.dst_maxburst;
- /*
- * Here we need some translation from "enum dma_slave_buswidth"
- * to the format for our dma controller
- * standard intel_mid_dmac's format
- * 1 Byte 0b000
- * 2 Bytes 0b001
- * 4 Bytes 0b010
- */
- ctl_lo.ctlx.dst_tr_width = mids->dma_slave.dst_addr_width / 2;
- ctl_lo.ctlx.src_tr_width = mids->dma_slave.src_addr_width / 2;
-
if (mids->cfg_mode == LNW_DMA_MEM_TO_MEM) {
ctl_lo.ctlx.tt_fc = 0;
ctl_lo.ctlx.sinc = 0;
BUG_ON(!mids);
if (!midc->dma->pimr_mask) {
- /* We can still handle sg list with only one item */
- if (sg_len == 1) {
- txd = intel_mid_dma_prep_memcpy(chan,
- mids->dma_slave.dst_addr,
- mids->dma_slave.src_addr,
- sgl->length,
- flags);
- return txd;
- } else {
- pr_warn("MDMA: SG list is not supported by this controller\n");
- return NULL;
- }
+ pr_debug("MDMA: SG list is not supported by this controller\n");
+ return NULL;
}
pr_debug("MDMA: SG Length = %d, direction = %d, Flags = %#lx\n",
struct middma_device *mid = to_middma_device(chan->device);
struct intel_mid_dma_desc *desc, *_desc;
+ pr_debug("entry:%s\n", __func__);
if (true == midc->busy) {
/*trying to free ch in use!!!!!*/
pr_err("ERR_MDMA: trying to free ch in use\n");
}
- pm_runtime_put(&mid->pdev->dev);
spin_lock_bh(&midc->lock);
midc->descs_allocated = 0;
list_for_each_entry_safe(desc, _desc, &midc->active_list, desc_node) {
/* Disable CH interrupts */
iowrite32(MASK_INTR_REG(midc->ch_id), mid->dma_base + MASK_BLOCK);
iowrite32(MASK_INTR_REG(midc->ch_id), mid->dma_base + MASK_ERR);
+ pm_runtime_put(&mid->pdev->dev);
}
-static int dma_resume(struct device *dev);
-
/**
* intel_mid_dma_alloc_chan_resources - Allocate dma resources
* @chan: chan requiring attention
pm_runtime_get_sync(&mid->pdev->dev);
- if (mid->state == SUSPENDED) {
- if (dma_resume(&mid->pdev->dev)) {
- pr_err("ERR_MDMA: resume failed");
- return -EFAULT;
- }
- }
-
/* ASSERT: channel is idle */
if (test_ch_en(mid->dma_base, midc->ch_id)) {
/*ch is not idle*/
u32 tfr_status, err_status;
int call_tasklet = 0;
+ /*DMA Interrupt*/
+ pr_debug("MDMA:Got an interrupt on irq %d\n", irq);
+ if (!mid) {
+ pr_err("ERR_MDMA:null pointer mid\n");
+ return -EINVAL;
+ }
+
tfr_status = ioread32(mid->dma_base + RAW_TFR);
err_status = ioread32(mid->dma_base + RAW_ERR);
if (!tfr_status && !err_status)
return IRQ_NONE;
- /*DMA Interrupt*/
- pr_debug("MDMA:Got an interrupt on irq %d\n", irq);
pr_debug("MDMA: Status %x, Mask %x\n", tfr_status, mid->intr_mask);
tfr_status &= mid->intr_mask;
if (tfr_status) {
/*need to disable intr*/
- iowrite32((tfr_status << INT_MASK_WE), mid->dma_base + MASK_TFR);
- iowrite32((tfr_status << INT_MASK_WE), mid->dma_base + MASK_BLOCK);
+ iowrite32((tfr_status << INT_MASK_WE),
+ mid->dma_base + MASK_TFR);
+ iowrite32((tfr_status << INT_MASK_WE),
+ mid->dma_base + MASK_BLOCK);
pr_debug("MDMA: Calling tasklet %x\n", tfr_status);
call_tasklet = 1;
}
if (NULL == dma->dma_pool) {
pr_err("ERR_MDMA:pci_pool_create failed\n");
err = -ENOMEM;
+ kfree(dma);
goto err_dma_pool;
}
dma->mask_reg = ioremap(LNW_PERIPHRAL_MASK_BASE,
LNW_PERIPHRAL_MASK_SIZE);
if (dma->mask_reg == NULL) {
- pr_err("ERR_MDMA:Can't map periphral intr space !!\n");
+ pr_err("ERR_MDMA:Cant map periphral intr space !!\n");
return -ENOMEM;
}
} else
free_irq(pdev->irq, dma);
err_irq:
pci_pool_destroy(dma->dma_pool);
+ iounmap(dma->mask_reg);
+ kfree(dma);
err_dma_pool:
pr_err("ERR_MDMA:setup_dma failed: %d\n", err);
return err;
/* Power Management */
/*
-* dma_suspend - PCI suspend function
+* dma_suspend - suspend function
*
-* @pci: PCI device structure
-* @state: PM message
+* @dev: device structure
*
* This function is called by OS when a power event occurs
*/
-static int dma_suspend(struct device *dev)
+int dma_suspend(struct device *dev)
{
int i;
- struct pci_dev *pci = to_pci_dev(dev);
- struct middma_device *device = pci_get_drvdata(pci);
+ struct middma_device *device = dev_get_drvdata(dev);
pr_debug("MDMA: dma_suspend called\n");
for (i = 0; i < device->max_chan; i++) {
if (device->ch[i].in_use)
return -EAGAIN;
}
+ dmac1_mask_periphral_intr(device);
device->state = SUSPENDED;
- pci_set_drvdata(pci, device);
- pci_save_state(pci);
- pci_disable_device(pci);
- pci_set_power_state(pci, PCI_D3hot);
+
return 0;
}
/**
-* dma_resume - PCI resume function
+* dma_resume - resume function
*
-* @pci: PCI device structure
+* @dev: device structure
*
* This function is called by OS when a power event occurs
*/
-static int dma_resume(struct device *dev)
+int dma_resume(struct device *dev)
{
- int ret;
- struct pci_dev *pci = to_pci_dev(dev);
- struct middma_device *device = pci_get_drvdata(pci);
+ struct middma_device *device = dev_get_drvdata(dev);
pr_debug("MDMA: dma_resume called\n");
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- ret = pci_enable_device(pci);
- if (ret) {
- pr_err("MDMA: device can't be enabled for %x\n", pci->device);
- return ret;
- }
device->state = RUNNING;
iowrite32(REG_BIT0, device->dma_base + DMA_CFG);
- pci_set_drvdata(pci, device);
return 0;
}
static int dma_runtime_suspend(struct device *dev)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct middma_device *device = pci_get_drvdata(pci_dev);
-
- device->state = SUSPENDED;
- return 0;
+ return dma_suspend(dev);
}
static int dma_runtime_resume(struct device *dev)
{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct middma_device *device = pci_get_drvdata(pci_dev);
-
- device->state = RUNNING;
- iowrite32(REG_BIT0, device->dma_base + DMA_CFG);
- return 0;
+ return dma_resume(dev);
}
static int dma_runtime_idle(struct device *dev)
{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct middma_device *device = pci_get_drvdata(pdev);
+ struct middma_device *device = dev_get_drvdata(dev);
int i;
for (i = 0; i < device->max_chan; i++) {
* PCI stuff
*/
static struct pci_device_id intel_mid_dma_ids[] = {
- { PCI_VDEVICE(INTEL, INTEL_MID_DMAC1_ID), INFO(2, 6, 4095, 0x200020)},
- { PCI_VDEVICE(INTEL, INTEL_MID_DMAC2_ID), INFO(2, 0, 2047, 0)},
- { PCI_VDEVICE(INTEL, INTEL_MID_GP_DMAC2_ID), INFO(2, 0, 2047, 0)},
- { PCI_VDEVICE(INTEL, INTEL_MFLD_DMAC1_ID), INFO(4, 0, 4095, 0x400040)},
+ { PCI_VDEVICE(INTEL, INTEL_MID_DMAC1_ID),
+ INFO(2, 6, 4095, 0x200020)},
+ { PCI_VDEVICE(INTEL, INTEL_MID_DMAC2_ID),
+ INFO(2, 0, 2047, 0)},
+ { PCI_VDEVICE(INTEL, INTEL_MID_GP_DMAC2_ID),
+ INFO(2, 0, 2047, 0)},
+ { PCI_VDEVICE(INTEL, INTEL_MFLD_DMAC1_ID),
+ INFO(4, 0, 4095, 0x400040)},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, intel_mid_dma_ids);
static const struct dev_pm_ops intel_mid_dma_pm = {
- .suspend = dma_suspend,
- .resume = dma_resume,
- .runtime_suspend = dma_runtime_suspend,
- .runtime_resume = dma_runtime_resume,
- .runtime_idle = dma_runtime_idle,
+ SET_SYSTEM_SLEEP_PM_OPS(dma_suspend,
+ dma_resume)
+ SET_RUNTIME_PM_OPS(dma_runtime_suspend,
+ dma_runtime_resume,
+ dma_runtime_idle)
};
static struct pci_driver intel_mid_dma_pci_driver = {
#define DISABLE_CHANNEL(chan_num) \
(REG_BIT8 << chan_num)
-#define DESCS_PER_CHANNEL 16
+#define DESCS_PER_CHANNEL 128
/*DMA Registers*/
/*registers associated with channel programming*/
#define DMA_REG_SIZE 0x400
return container_of(slave, struct intel_mid_dma_slave, dma_slave);
}
+
+int dma_resume(struct device *dev);
+
#endif /*__INTEL_MID_DMAC_REGS_H__*/
source "drivers/staging/ft1000/Kconfig"
-source "drivers/staging/intel_sst/Kconfig"
-
source "drivers/staging/speakup/Kconfig"
source "drivers/staging/cptm1217/Kconfig"
obj-$(CONFIG_USB_ENESTORAGE) += keucr/
obj-$(CONFIG_BCM_WIMAX) += bcm/
obj-$(CONFIG_FT1000) += ft1000/
-obj-$(CONFIG_SND_INTEL_SST) += intel_sst/
obj-$(CONFIG_SPEAKUP) += speakup/
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/
+++ /dev/null
-config SND_INTEL_SST
- tristate "Intel SST (LPE) Driver"
- depends on X86 && INTEL_SCU_IPC
- default n
- help
- Say Y here to include support for the Intel(R) MID SST DSP driver
- On other PC platforms if you are unsure answer 'N'
+++ /dev/null
-#
-# Makefile for Intel MID Audio drivers
-#
-snd-intel-sst-y := intel_sst.o intel_sst_ipc.o intel_sst_stream.o intel_sst_drv_interface.o intel_sst_dsp.o intel_sst_pvt.o intel_sst_stream_encoded.o intel_sst_app_interface.o
-obj-$(CONFIG_SND_INTEL_SST) += snd-intel-sst.o
+++ /dev/null
-TODO
-----
-
-Get the memrar driver cleaned up and upstream (dependency blocking SST)
-Replace long/short press with two virtual buttons
-Review the printks and kill off any left over ST_ERR: messages
-Review the misc device ioctls for 32/64bit safety and sanity
-Review the misc device ioctls for size safety depending on config and decide
- if space/unused areas should be left
-What the sound folks turn up on full review
-Using the ALSA frameworks properly
-
-
* and middleware.
* This file is shared between the SST and MAD drivers
*/
-#include "intel_sst_ioctl.h"
+#include <sound/intel_sst_ioctl.h>
#include <sound/jack.h>
#define SST_CARD_NAMES "intel_mid_card"
int pb_on, pbhs_on;
int cap_on;
int output_dev_id;
- int lineout_dev_id, line_out_names_cnt;
+ int lineout_dev_id, lineout_names_cnt;
int prev_lineout_dev_id;
bool jack_interrupt_status;
void (*pmic_irq_cb) (void *cb_data, u8 value);
void (*pmic_irq_enable)(void *data);
- int (*pmic_jack_enable) (void);
int (*pmic_get_mic_bias)(void *intelmaddata);
int (*pmic_set_headset_state)(int state);
unsigned int hw_dmic_map[MFLD_MAX_HW_CH];
unsigned int available_dmics;
int (*set_hw_dmic_route) (u8 index);
-
- int gpio_amp;
};
extern void sst_mad_send_jack_report(struct snd_jack *jack,
SST_PLL_AUDIENCE = 0x4,
SST_PLL_VIBRA1 = 0x8,
SST_PLL_VIBRA2 = 0x10,
+ SST_PLL_MSIC = 0x20,
};
int register_sst_card(struct intel_sst_card_ops *card);
} __attribute__ ((packed));
/*IOCTL defined here */
/*SST MMF IOCTLS only */
-#define SNDRV_SST_STREAM_SET_PARAMS _IOR('L', 0x00, \
+#define SNDRV_SST_STREAM_SET_PARAMS _IOWR('L', 0x00, \
struct snd_sst_stream_params *)
#define SNDRV_SST_STREAM_GET_PARAMS _IOWR('L', 0x01, \
struct snd_sst_get_stream_params *)
/* Symmetry requirements */
unsigned int symmetric_rates:1;
+ /* ignore dapm power down delay */
+ unsigned int ignore_pmdown_time:1;
+
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_pcm_runtime *rtd);
if SND_PCI
+config SND_INTEL_SST
+ tristate "Intel SST (LPE) Driver"
+ depends on X86 && INTEL_SCU_IPC
+ default n
+ help
+ Say Y here to include support for the Intel(R) MID SST DSP driver
+ On other PC platforms if you are unsure answer 'N'
+
config SND_AD1889
tristate "Analog Devices AD1889"
select SND_AC97_CODEC
pcxhr/ \
riptide/ \
rme9652/ \
+ sst/ \
trident/ \
ymfpci/ \
vx222/
--- /dev/null
+#
+# Makefile for Intel MID Audio drivers
+#
+snd-intel-sst-objs := intel_sst.o intel_sst_ipc.o intel_sst_stream.o intel_sst_drv_interface.o intel_sst_dsp.o intel_sst_pvt.o intel_sst_stream_encoded.o intel_sst_app_interface.o
+obj-$(CONFIG_SND_INTEL_SST) += snd-intel-sst.o
#include <linux/firmware.h>
#include <linux/miscdevice.h>
#include <linux/pm_runtime.h>
+#include <linux/async.h>
#include <asm/mrst.h>
#include <asm/intel_scu_ipc.h>
-#include "intel_sst.h"
-#include "intel_sst_ioctl.h"
+#include <sound/intel_sst.h>
+#include <sound/intel_sst_ioctl.h>
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
struct intel_sst_drv *sst_drv_ctx;
static struct mutex drv_ctx_lock;
+struct class *sst_class;
/* fops Routines */
static const struct file_operations intel_sst_fops = {
/* 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);
stream->period_elapsed(stream->pcm_substream);
return IRQ_HANDLED;
}
+ pr_err("recieved IPC %x\n", header.full);
if (header.part.large)
size = header.part.data;
if (header.part.msg_id & REPLY_MSG) {
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_LIST_HEAD(&sst_drv_ctx->fw_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);
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;
destroy_workqueue(sst_drv_ctx->process_msg_wq);
destroy_workqueue(sst_drv_ctx->post_msg_wq);
destroy_workqueue(sst_drv_ctx->mad_wq);
- kfree(pci_get_drvdata(pci));
- if (sst_drv_ctx->fw) {
- release_firmware(sst_drv_ctx->fw);
- sst_drv_ctx->fw = NULL;
- }
+ release_firmware(sst_drv_ctx->fw);
+ sst_drv_ctx->fw = NULL;
+ kfree(sst_drv_ctx->fw_in_mem);
+ sst_drv_ctx->fw_in_mem = NULL;
+ kfree(sst_drv_ctx);
sst_drv_ctx = NULL;
pci_release_regions(pci);
pci_disable_device(pci);
static void sst_save_dsp_context(void)
{
struct snd_sst_ctxt_params fw_context;
- unsigned int pvt_id, i;
+ unsigned int pvt_id;
struct ipc_post *msg = NULL;
/*check cpu type*/
if (sst_create_large_msg(&msg))
return;
pvt_id = sst_assign_pvt_id(sst_drv_ctx);
- i = sst_get_block_stream(sst_drv_ctx);
- sst_drv_ctx->alloc_block[i].sst_id = pvt_id;
+ sst_drv_ctx->alloc_block[0].sst_id = pvt_id;
sst_fill_header(&msg->header, IPC_IA_GET_FW_CTXT, 1, pvt_id);
msg->header.part.data = sizeof(fw_context) + sizeof(u32);
fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx);
spin_unlock(&sst_drv_ctx->list_spin_lock);
sst_post_message(&sst_drv_ctx->ipc_post_msg_wq);
/*wait for reply*/
- if (sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i]))
+ if (sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[0]))
pr_debug("err fw context save timeout ...\n");
- sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT;
+ sst_drv_ctx->alloc_block[0].sst_id = BLOCK_UNINIT;
pr_debug("fw context saved ...\n");
return;
}
{
union config_status_reg csr;
- pr_debug("sst: runtime_suspend called\n");
-
- if (sst_drv_ctx->stream_cnt) {
- pr_err("active streams,not able to suspend\n");
- return -EBUSY;
+ 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();
{
u32 csr;
- pr_debug("sst: runtime_resume called\n");
+ 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 - Configuration and Status Register.
*/
csr |= (sst_drv_ctx->csr_value | 0x30000);
+ sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr);
intel_sst_set_pll(true, SST_PLL_AUDIO);
mutex_lock(&sst_drv_ctx->sst_lock);
static int intel_sst_runtime_idle(struct device *dev)
{
pr_debug("runtime_idle called\n");
- if (sst_drv_ctx->stream_cnt == 0 && sst_drv_ctx->am_cnt == 0)
+ if (!sst_drv_ctx->am_cnt && sst_drv_ctx->sst_state != SST_UN_INIT) {
pm_schedule_suspend(dev, SST_SUSPEND_DELAY);
- return -EBUSY;
+ return -EBUSY;
+ } else {
+ return 0;
+ }
}
static const struct dev_pm_ops intel_sst_pm = {
pci_unregister_driver(&driver);
pr_debug("driver unloaded\n");
- if (sst_drv_ctx->fw) {
- release_firmware(sst_drv_ctx->fw);
- sst_drv_ctx->fw = NULL;
- }
sst_drv_ctx = NULL;
return;
}
-module_init(intel_sst_init);
+module_init_async(intel_sst_init);
module_exit(intel_sst_exit);
#include <linux/rar_register.h>
#include "../../../drivers/staging/memrar/memrar.h"
#endif
-#include "intel_sst.h"
-#include "intel_sst_ioctl.h"
+#include <sound/intel_sst.h>
+#include <sound/intel_sst_ioctl.h>
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
int retval = 0;
if (sst_drv_ctx->sst_state == SST_UN_INIT) {
+ sst_drv_ctx->sst_state = SST_START_INIT;
/* FW is not downloaded */
retval = sst_download_fw();
if (retval)
retval = intel_sst_check_device();
if (retval) {
pm_runtime_put(&sst_drv_ctx->pci->dev);
+ sst_drv_ctx->sst_state = SST_UN_INIT;
mutex_unlock(&sst_drv_ctx->stream_lock);
return retval;
}
retval = sst_create_algo_ipc(&algo_params, &msg);
if (retval < 0)
break;
- algo_params.reserved = 1;
if (copy_from_user(msg->mailbox_data + retval,
algo_params.params, algo_params.size)) {
kfree(msg);
{
struct snd_sst_tuning_params params;
struct ipc_post *msg;
+ unsigned long address;
if (copy_from_user(¶ms, (void __user *)arg, sizeof(params)))
return -EFAULT;
+ if (params.size > SST_MAILBOX_SIZE)
+ return -ENOMEM;
pr_debug("Parameter %d, Stream %d, Size %d\n", params.type,
params.str_id, params.size);
if (sst_create_large_msg(&msg))
return -ENOMEM;
+ address = (unsigned long)params.addr;
switch (_IOC_NR(cmd)) {
case _IOC_NR(SNDRV_SST_TUNING_PARAMS):
memcpy(msg->mailbox_data + sizeof(u32), ¶ms, sizeof(params));
/* driver doesn't need to send address, so overwrite addr with data */
if (copy_from_user(msg->mailbox_data + sizeof(u32) + sizeof(params) - sizeof(params.addr),
- (void __user *)(unsigned long)params.addr,
- params.size)) {
+ (void __user *)address, params.size)) {
kfree(msg->mailbox_data);
kfree(msg);
return -EFAULT;
pr_debug("SET_STREAM_PARAMS received!\n");
/* allocated set params only */
retval = sst_set_stream_param(str_id, &str_param);
+ /* Block the call for reply */
+ if (!retval) {
+ int sfreq = 0, word_size = 0, num_channel = 0;
+ sfreq = str_param.sparams.uc.pcm_params.sfreq;
+ word_size = str_param.sparams.uc.pcm_params.pcm_wd_sz;
+ num_channel = str_param.sparams.uc.pcm_params.num_chan;
+ }
}
break;
}
* Common private declarations for SST
*/
+#include <linux/dmaengine.h>
+#include <linux/intel_mid_dma.h>
+
#define SST_DRIVER_VERSION "2.0.04"
#define SST_VERSION_NUM 0x2004
enum sst_states {
SST_FW_LOADED = 1,
SST_FW_RUNNING,
+ SST_START_INIT,
SST_UN_INIT,
SST_ERROR,
SST_SUSPENDED,
#define SST_MMAP_PAGES (640*1024 / PAGE_SIZE)
#define SST_MMAP_STEP (40*1024 / PAGE_SIZE)
+/* FIXME optimze this */
+enum sst_dma_desc {
+ SST_DESC_NULL = 0,
+ SST_DESC_PREPARED,
+ SST_DESC_SUBMITTED,
+ SST_DESC_DONE,
+};
+
+struct sst_firmware_list {
+ unsigned long src;
+ unsigned long dstn;
+ unsigned int len;
+ struct list_head node;
+ struct dma_async_tx_descriptor *desc;
+ enum sst_dma_desc trf_status;
+ bool is_last;
+ wait_queue_t *wait;
+ wait_queue_head_t *queue;
+};
+
+struct sst_dma {
+ struct dma_chan *ch;
+ struct intel_mid_dma_slave slave;
+ struct pci_dev *dmac;
+};
+
+#define PCI_DMAC_ID 0x0830
+#define SST_MAX_DMA_LEN (4095*4)
+#define SST_DMA_DELAY 2000
/***
* struct intel_sst_drv - driver ops
*
void __iomem *mailbox;
void __iomem *iram;
void __iomem *dram;
+ unsigned int iram_base;
+ unsigned int dram_base;
unsigned int shim_phy_add;
struct list_head ipc_dispatch_list;
struct work_struct ipc_post_msg_wq;
struct workqueue_struct *process_msg_wq;
struct workqueue_struct *process_reply_wq;
- struct stream_info streams[MAX_NUM_STREAMS];
+ struct stream_info streams[MAX_NUM_STREAMS+1]; /*str_id 0 is not used*/
struct stream_alloc_block alloc_block[MAX_ACTIVE_STREAM];
struct sst_block tgt_dev_blk, fw_info_blk, ppp_params_blk,
vol_info_blk, mute_info_blk, hs_info_blk;
unsigned int pll_mode;
const struct firmware *fw;
- unsigned int fw_downloaded;
+ struct list_head fw_list;
+ struct sst_dma dma;
+ void *fw_in_mem;
};
extern struct intel_sst_drv *sst_drv_ctx;
ssize_t intel_sst_aio_read(struct kiocb *kiocb, const struct iovec *iov,
unsigned long nr_segs, loff_t offset);
-int sst_load_fw(const struct firmware *fw, void *context);
+int sst_request_fw(void);
+int sst_load_fw(const void *fw_in_mem, void *context);
int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
int sst_spi_mode_enable(void);
int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
{
return readl(addr + offset);
}
+
+static inline void
+sst_set_fw_state_locked(struct intel_sst_drv *sst_drv_ctx, int sst_state)
+{
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_drv_ctx->sst_state = sst_state;
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+}
#endif /* __INTEL_SST_COMMON_H__ */
#include <linux/fs.h>
#include <linux/firmware.h>
#include <linux/pm_runtime.h>
-#include "intel_sst.h"
-#include "intel_sst_ioctl.h"
+#include <sound/intel_sst.h>
+#include <sound/intel_sst_ioctl.h>
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
struct ipc_post *msg = NULL;
int retval = 0;
- pr_debug("sst: restore_fw_context\n");
+ pr_debug("restore_fw_context\n");
/*check cpu type*/
if (sst_drv_ctx->pci_id != SST_MFLD_PCI_ID)
return;
if (!sst_drv_ctx->fw_cntx_size)
return;
/*nothing to restore*/
- pr_debug("sst: restoring context......\n");
+ pr_debug("restoring context......\n");
/*send msg to fw*/
if (sst_create_large_msg(&msg))
return;
- mutex_lock(&sst_drv_ctx->sst_lock);
- sst_drv_ctx->sst_state = SST_FW_CTXT_RESTORE;
- mutex_unlock(&sst_drv_ctx->sst_lock);
+ sst_set_fw_state_locked(sst_drv_ctx, SST_FW_CTXT_RESTORE);
sst_fill_header(&msg->header, IPC_IA_SET_FW_CTXT, 1, 0);
sst_drv_ctx->alloc_block[0].sst_id = FW_DWNL_ID;
sst_drv_ctx->alloc_block[0].ops_block.condition = false;
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[0]);
+ sst_drv_ctx->alloc_block[0].sst_id = BLOCK_UNINIT;
if (retval)
- pr_err("sst: sst_restore_fw_context..timeout!\n");
+ pr_err("sst_restore_fw_context..timeout!\n");
return;
}
{
int retval;
- char name[20];
-
- if (sst_drv_ctx->sst_state != SST_UN_INIT)
- return -EPERM;
-
- /* Reload firmware is not needed for MRST */
- if ( (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) && sst_drv_ctx->fw_downloaded) {
- pr_debug("FW already downloaded, skip for MRST platform\n");
- sst_drv_ctx->sst_state = SST_FW_RUNNING;
- return 0;
- }
-
- snprintf(name, sizeof(name), "%s%04x%s", "fw_sst_",
- sst_drv_ctx->pci_id, ".bin");
+ if (sst_drv_ctx->sst_state != SST_START_INIT)
+ return -EAGAIN;
- pr_debug("Downloading %s FW now...\n", name);
- if (!sst_drv_ctx->fw) {
- retval = request_firmware(&sst_drv_ctx->fw, name,
- &sst_drv_ctx->pci->dev);
- if (retval) {
- pr_err("sst: request fw failed %d\n", retval);
- return retval;
- }
- }
+ retval = sst_request_fw();
+ if (retval)
+ return retval;
sst_drv_ctx->alloc_block[0].sst_id = FW_DWNL_ID;
sst_drv_ctx->alloc_block[0].ops_block.condition = false;
- retval = sst_load_fw(sst_drv_ctx->fw, NULL);
+ retval = sst_load_fw(sst_drv_ctx->fw_in_mem, NULL);
if (retval) {
- release_firmware(sst_drv_ctx->fw);
- sst_drv_ctx->fw = NULL;
goto end_restore;
}
retval = sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[0]);
if (retval) {
pr_err("fw download failed %d\n" , retval);
- release_firmware(sst_drv_ctx->fw);
- sst_drv_ctx->fw = NULL;
- } else
- sst_drv_ctx->fw_downloaded = 1;
+ /* assume FW d/l failed due to timeout*/
+ retval = -EBUSY;
+ }
end_restore:
sst_drv_ctx->alloc_block[0].sst_id = BLOCK_UNINIT;
return retval;
sst_restore_fw_context();
- mutex_lock(&sst_drv_ctx->sst_lock);
- sst_drv_ctx->sst_state = SST_FW_RUNNING;
- mutex_unlock(&sst_drv_ctx->sst_lock);
+ sst_set_fw_state_locked(sst_drv_ctx, SST_FW_RUNNING);
return retval;
}
pr_debug("Stream allocated %d\n", retval);
str_id = retval;
str_info = &sst_drv_ctx->streams[str_id];
- if (str_id == 0)
- str_id = -SST_IPC_ERR_STREAM_ALLOC_FAILED;
/* Block the call for reply */
retval = sst_wait_interruptible_timeout(sst_drv_ctx,
&str_info->ctrl_blk, SST_BLOCK_TIMEOUT);
pr_debug("FW alloc failed retval %d, ret_code %d\n",
retval, str_info->ctrl_blk.ret_code);
str_id = -str_info->ctrl_blk.ret_code; /*return error*/
+ if (str_id == 0)
+ str_id = retval; /*FW timed out*/
*lib_dnld = str_info->ctrl_blk.data;
sst_clean_stream(str_info);
} else
}
}
+static int sst_get_wdsize(struct snd_sst_params *str_param)
+{
+ switch (str_param->codec) {
+ case SST_CODEC_TYPE_PCM:
+ return str_param->sparams.uc.pcm_params.pcm_wd_sz;
+ case SST_CODEC_TYPE_MP3:
+ return str_param->sparams.uc.mp3_params.pcm_wd_sz;
+ case SST_CODEC_TYPE_AAC:
+ return str_param->sparams.uc.aac_params.pcm_wd_sz;
+ case SST_CODEC_TYPE_WMA9:
+ return str_param->sparams.uc.wma_params.pcm_wd_sz;
+ default:
+ return 0;
+ }
+}
+
+
/*
* sst_get_stream - this function prepares for stream allocation
*
retval = sst_get_stream_allocated(str_param, &lib_dnld);
if (retval == -(SST_LIB_ERR_LIB_DNLD_REQUIRED)) {
/* codec download is required */
+ struct snd_sst_alloc_response *response;
+
pr_debug("Codec is required.... trying that\n");
if (lib_dnld == NULL) {
pr_err("lib download null!!! abort\n");
}
i = sst_get_block_stream(sst_drv_ctx);
if (i < 0) {
- pr_err("sst: invalid value for number of stream\n ");
- return -EINVAL;
- }
- pr_debug("alloc block allocated = %d\n", i);
- if (i < 0) {
+ pr_err("invalid value for number of stream\n ");
kfree(lib_dnld);
- return -ENOMEM;
+ return i;
}
+ response = sst_drv_ctx->alloc_block[i].ops_block.data;
+ pr_debug("alloc block allocated = %d\n", i);
+
retval = sst_load_library(lib_dnld, str_param->ops);
kfree(lib_dnld);
pr_debug("codec was downloaded successfully\n");
retval = sst_get_stream_allocated(str_param, &lib_dnld);
- if (retval <= 0)
+ if (retval <= 0) {
+ retval = -EIO;
goto err;
+ }
pr_debug("Alloc done stream id %d\n", retval);
} else {
retval = -EIO;
goto err;
}
- } else if (retval <= 0)
+ } else if (retval <= 0) {
+ retval = -EIO;
goto err;
+ }
/*else
set_port_params(str_param, str_param->ops);*/
return retval;
}
-static void sst_prepare_fw(void)
+void sst_prepare_fw(void)
{
int retval;
+ mutex_lock(&sst_drv_ctx->sst_lock);
if (sst_drv_ctx->sst_state == SST_UN_INIT) {
+ sst_drv_ctx->sst_state = SST_START_INIT;
+ mutex_unlock(&sst_drv_ctx->sst_lock);
/* FW is not downloaded */
- pr_debug("sst: DSP Downloading FW now...\n");
+ pr_debug("DSP Downloading FW now...\n");
retval = sst_download_fw();
if (retval) {
- pr_err("sst: FW download fail %x\n", retval);
- pr_debug("sst: doing rtpm_put\n");
+ pr_err("FW download fail %x\n", retval);
+ pr_debug("doing rtpm_put\n");
+ sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT);
pm_runtime_put(&sst_drv_ctx->pci->dev);
}
+ } else {
+ mutex_unlock(&sst_drv_ctx->sst_lock);
}
}
retval = sst_resume_stream(mad_ops->stream_id);
break;
case SST_SND_DROP:
- pr_debug("SST Debug: in mad_ops drop stream\n");
+ pr_debug("in mad_ops drop stream\n");
retval = sst_drop_stream(mad_ops->stream_id);
break;
case SST_SND_START:
pr_debug("play/capt frames...\n");
break;
case SST_SND_DEVICE_RESUME:
- pr_debug("sst: SST_SND_DEVICE_RESUME\n");
+ pr_debug("SST_SND_DEVICE_RESUME\n");
pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
sst_prepare_fw();
break;
struct stream_info *str_info;
int retval;
- pr_debug("sst: open_pcm, doing rtpm_get\n");
+ pr_debug("open_pcm, doing rtpm_get\n");
pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
+ mutex_lock(&sst_drv_ctx->sst_lock);
if (sst_drv_ctx->sst_state == SST_UN_INIT) {
+ sst_drv_ctx->sst_state = SST_START_INIT;
+ mutex_unlock(&sst_drv_ctx->sst_lock);
/* FW is not downloaded */
pr_debug("DSP Downloading FW now...\n");
retval = sst_download_fw();
if (retval) {
pr_err("FW download fail %x, abort\n", retval);
+ pr_debug("open_pcm, doing rtpm_put\n");
+ sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT);
pm_runtime_put(&sst_drv_ctx->pci->dev);
return retval;
}
send_intial_rx_timeslot();
+ } else {
+ mutex_unlock(&sst_drv_ctx->sst_lock);
}
if (!str_param) {
- pr_debug("sst: open_pcm, doing rtpm_put\n");
+ pr_debug("open_pcm, doing rtpm_put\n");
pm_runtime_put(&sst_drv_ctx->pci->dev);
return -EINVAL;
}
{
struct stream_info *stream;
- pr_debug("sst: stream free called\n");
+ pr_debug("stream free called\n");
if (sst_validate_strid(str_id))
return -EINVAL;
stream = &sst_drv_ctx->streams[str_id];
stream->status = STREAM_UN_INIT;
stream->period_elapsed = NULL;
sst_drv_ctx->stream_cnt--;
- pr_debug("sst: will call runtime put now\n");
+ pr_debug("will call runtime put now\n");
pm_runtime_put(&sst_drv_ctx->pci->dev);
return 0;
}
case SST_SND_START:
case SST_SND_DEVICE_RESUME: {
struct mad_ops_wq *work = kzalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work)
+ return -ENOMEM;
INIT_WORK(&work->wq, sst_process_mad_ops);
work->control_op = cmd;
work->stream_id = *(int *)arg;
break;
}
case SST_SND_DEVICE_RESUME_SYNC:
- pr_debug("sst: SST_SND_DEVICE_RESUME_SYNC\n");
+ pr_debug("SST_SND_DEVICE_RESUME_SYNC\n");
pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
sst_prepare_fw();
break;
case SST_SND_DEVICE_SUSPEND:
- pr_debug("sst: SST_SND_DEVICE_SUSPEND doing rtpm_put\n");
+ pr_debug("SST_SND_DEVICE_SUSPEND doing rtpm_put\n");
pm_runtime_put(&sst_drv_ctx->pci->dev);
break;
case SST_SND_STREAM_INIT: {
*/
int register_sst_card(struct intel_sst_card_ops *card)
{
- pr_debug("sst: driver register card %p\n", sst_drv_ctx);
+ pr_debug("driver register card %p\n", sst_drv_ctx);
if (!sst_drv_ctx) {
pr_err("No SST driver register card reject\n");
return -ENODEV;
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/firmware.h>
-#include "intel_sst.h"
-#include "intel_sst_ioctl.h"
+#include <linux/dmaengine.h>
+#include <linux/intel_mid_dma.h>
+#include <sound/intel_sst.h>
+#include <sound/intel_sst_ioctl.h>
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
* @module: FW module header
*
* Parses modules that need to be placed in SST IRAM and DRAM
+ * and stores them in a list for transfer
* returns error or 0 if module sizes are proper
*/
static int sst_parse_module(struct fw_module_header *module)
{
struct dma_block_info *block;
- u32 count;
- void __iomem *ram;
+ struct sst_firmware_list *node;
+ u32 count, offset;
+ unsigned long ram;
pr_debug("module sign %s size %x blocks %x type %x\n",
module->signature, module->mod_size,
}
switch (block->type) {
case SST_IRAM:
- ram = sst_drv_ctx->iram;
+ ram = sst_drv_ctx->iram_base;
break;
case SST_DRAM:
- ram = sst_drv_ctx->dram;
+ ram = sst_drv_ctx->dram_base;
break;
default:
pr_err("wrong ram type0x%x in block0x%x\n",
block->type, count);
return -EINVAL;
}
- memcpy_toio(ram + block->ram_offset,
- (void *)block + sizeof(*block), block->size);
+ offset = 0;
+ do {
+ node = kmalloc(sizeof(*node), GFP_KERNEL);
+ if (!node) {
+ pr_err("mem alloc failed\n");
+ return -ENOMEM;
+ }
+ node->dstn = ram + block->ram_offset + offset;
+ node->src = virt_to_phys((void *)block + sizeof(*block) + offset);
+ node->len = block->size - offset;
+ node->is_last = false;
+ node->trf_status = SST_DESC_NULL;
+ pr_debug("DMA block src %lx, dstn %lx, size %d, offset %d\n",
+ node->src, node->dstn, node->len, offset);
+ if (node->len > SST_MAX_DMA_LEN) {
+ pr_debug("block size exceeds %d\n", SST_MAX_DMA_LEN);
+ node->len = SST_MAX_DMA_LEN;
+ offset += node->len;
+ } else {
+ offset = 0;
+ pr_debug("Node length less that %d\n", SST_MAX_DMA_LEN);
+ }
+ list_add_tail(&node->node, &sst_drv_ctx->fw_list);
+ } while (offset > 0);
block = (void *)block + sizeof(*block) + block->size;
}
+ if (node)
+ node->is_last = true;
return 0;
}
+static bool chan_filter(struct dma_chan *chan, void *param)
+{
+ struct sst_dma *dma = (struct sst_dma *)param;
+ bool ret = false;
+
+ /* we only need MID_DMAC1 as that can access DSP RAMs*/
+ if (chan->device->dev == &dma->dmac->dev)
+ ret = true;
+
+ return ret;
+}
+
+static int sst_alloc_dma_chan(struct sst_dma *dma)
+{
+ dma_cap_mask_t mask;
+ struct intel_mid_dma_slave *slave = &dma->slave;
+ int retval;
+
+ pr_debug("%s\n", __func__);
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+
+ dma->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DMAC_ID, NULL);
+ if (!dma->dmac) {
+ pr_err("Can't find DMAC %x\n", PCI_DMAC_ID);
+ return -ENODEV;
+ }
+ dma->ch = dma_request_channel(mask, chan_filter, dma);
+
+ slave->dma_slave.direction = DMA_FROM_DEVICE;
+ slave->hs_mode = 0;
+ slave->cfg_mode = LNW_DMA_MEM_TO_MEM;
+ slave->dma_slave.src_addr_width = slave->dma_slave.dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slave->dma_slave.src_maxburst = slave->dma_slave.dst_maxburst =
+ LNW_DMA_MSIZE_16;
+
+ retval = dmaengine_slave_config(dma->ch, &slave->dma_slave);
+ if (retval) {
+ pr_err("unable to set slave config, err %d\n", retval);
+ return -EIO;
+ }
+ return retval;
+}
+
+static void sst_dma_transfer_complete(void *arg)
+{
+ struct sst_firmware_list *block = arg;
+ static unsigned int count;
+
+ pr_debug("%s, dma callback for %d\n", __func__, count);
+ count++;
+
+ pr_debug("current desc status %d\n", block->trf_status);
+ if (block->trf_status != SST_DESC_SUBMITTED)
+ pr_warn("desc status is showing wrongly\n");
+ block->trf_status = SST_DESC_DONE;
+ pr_debug("new desc status %d\n", block->trf_status);
+ if (block->is_last == true) {
+ count = 0;
+ pr_debug("end of list callback, waking up...\n");
+ }
+}
+
+static int
+sst_get_dma_desc(struct list_head *head, struct sst_dma *dma)
+{
+ struct sst_firmware_list *block;
+ enum dma_ctrl_flags flag = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
+ int i = 0;
+
+ pr_debug("%s\n", __func__);
+ list_for_each_entry(block, head, node) {
+ pr_debug("prep for block %d\n", i); i++;
+ block->desc = dma->ch->device->device_prep_dma_memcpy(
+ dma->ch, block->dstn, block->src, block->len, flag);
+ if (block->desc) {
+ block->trf_status = SST_DESC_PREPARED;
+ block->desc->callback = sst_dma_transfer_complete;
+ block->desc->callback_param = block;
+ pr_debug("set block ptrs\n");
+ }
+ }
+ return 0;
+}
/**
* sst_parse_fw_image - parse and load FW
*
* @sst_fw: pointer to audio fw
*
- * This function is called to parse and download the FW image
+ * This function is called to verify and parse the FW image and save the parsed
+ * image in a list for DMA
*/
-static int sst_parse_fw_image(const struct firmware *sst_fw)
+static int sst_parse_fw_image(const void *sst_fw_in_mem, unsigned long size)
{
struct fw_header *header;
u32 count;
int ret_val;
struct fw_module_header *module;
- BUG_ON(!sst_fw);
-
+ pr_debug("%s\n", __func__);
/* Read the header information from the data pointer */
- header = (struct fw_header *)sst_fw->data;
-
+ header = (struct fw_header *)sst_fw_in_mem;
+ pr_debug("header sign=%s size=%x modules=%x fmt=%x size=%x\n",
+ header->signature, header->file_size, header->modules,
+ header->file_format, sizeof(*header));
/* verify FW */
if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
- (sst_fw->size != header->file_size + sizeof(*header))) {
+ (size != header->file_size + sizeof(*header))) {
/* Invalid FW signature */
- pr_err("Invalid FW sign/filesize mismatch\n");
+ pr_err("InvalidFW sign/filesize mismatch\n");
return -EINVAL;
}
- pr_debug("header sign=%s size=%x modules=%x fmt=%x size=%x\n",
- header->signature, header->file_size, header->modules,
- header->file_format, sizeof(*header));
- module = (void *)sst_fw->data + sizeof(*header);
+
+ module = (void *)sst_fw_in_mem + sizeof(*header);
for (count = 0; count < header->modules; count++) {
/* module */
ret_val = sst_parse_module(module);
return 0;
}
+/*
+ * sst_request_fw - requests audio fw from kernel and saves a copy
+ *
+ * This function requests the SST FW from the kernel, parses it and
+ * saves a copy in the driver context
+ */
+int sst_request_fw(void)
+{
+ int retval = 0;
+ char name[20];
+
+ snprintf(name, sizeof(name), "%s%04x%s", "fw_sst_",
+ sst_drv_ctx->pci_id, ".bin");
+
+ pr_debug("Requesting %s FW now...\n", name);
+ retval = request_firmware(&sst_drv_ctx->fw, name,
+ &sst_drv_ctx->pci->dev);
+ if (retval) {
+ pr_err("request fw failed %d\n", retval);
+ return retval;
+ }
+
+ if (!sst_drv_ctx->fw_in_mem) {
+ pr_debug("firmware not in memory\n");
+ sst_drv_ctx->fw_in_mem = kzalloc(sst_drv_ctx->fw->size, GFP_KERNEL);
+ if (!sst_drv_ctx->fw_in_mem) {
+ pr_err("%s unable to allocate memory\n", __func__);
+ retval = -ENOMEM;
+ goto end_release;
+ }
+ memcpy(sst_drv_ctx->fw_in_mem, sst_drv_ctx->fw->data,
+ sst_drv_ctx->fw->size);
+ }
+
+ retval = sst_parse_fw_image(sst_drv_ctx->fw_in_mem,
+ sst_drv_ctx->fw->size);
+ if (retval) {
+ kfree(sst_drv_ctx->fw_in_mem);
+ goto end_release;
+ }
+
+end_release:
+ release_firmware(sst_drv_ctx->fw);
+ sst_drv_ctx->fw = NULL;
+ return retval;
+}
+
+int sst_dma_firmware(struct list_head *head)
+{
+ /* submit the descriptors */
+ struct sst_firmware_list *block;
+ int i = 0;
+ int cnt = 0;
+ pr_debug("%s\n", __func__);
+
+ list_for_each_entry(block, head, node) {
+ pr_debug("submitting desc %d\n", i); i++;
+ pr_debug("before submit desc status %d\n", block->trf_status);
+ block->trf_status = SST_DESC_SUBMITTED;
+ block->desc->tx_submit(block->desc);
+ /* wait for dma completion
+ * since this is memcpy, max block length should get completed
+ * within 2ms, since we are waiting for dma compleetion here and
+ * dont want to yeild, just poll
+ * this is required till multiblock memcpy support API is added
+ * in the dma driver
+ */
+ udelay(SST_DMA_DELAY);
+ pr_debug("after submit desc status %d\n", block->trf_status);
+ cnt = 0;
+ /* wait in loop with counter */
+ while (1) {
+ pr_debug("while desc status %d\n", block->trf_status);
+ if (block->trf_status == SST_DESC_DONE) {
+ pr_debug("desc done %d\n", (i-1));
+ break;
+ }
+ udelay(SST_DMA_DELAY);
+ cnt++;
+ if (cnt >= 10) {
+ pr_err("dma for block %d failed\n", (i-1));
+ break;
+ }
+ }
+ pr_debug("wait done\n");
+ }
+ pr_debug("submitted all desc, waiting now...\n");
+ return 0;
+}
+
+void sst_dma_free_resources(struct list_head *head, struct sst_dma *dma)
+{
+ struct sst_firmware_list *block, *__block;
+
+ pr_debug("entry:%s\n", __func__);
+
+ list_for_each_entry_safe(block, __block, head, node) {
+ list_del(&block->node);
+ kfree(block);
+ }
+ dma_release_channel(dma->ch);
+}
+
/**
* sst_load_fw - function to load FW into DSP
*
* @fw: Pointer to driver loaded FW
* @context: driver context
*
- * This function is called by OS when the FW is loaded into kernel
+ * Transfers the FW to DSP using DMA
*/
-int sst_load_fw(const struct firmware *fw, void *context)
+int sst_load_fw(const void *fw_in_mem, void *context)
{
int ret_val;
pr_debug("load_fw called\n");
- BUG_ON(!fw);
+ BUG_ON(!fw_in_mem);
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID)
ret_val = intel_sst_reset_dsp_mrst();
if (ret_val)
return ret_val;
- ret_val = sst_parse_fw_image(fw);
+ /* get a dmac channel */
+ sst_alloc_dma_chan(&sst_drv_ctx->dma);
+ /* allocate desc for transfer */
+ sst_get_dma_desc(&sst_drv_ctx->fw_list, &sst_drv_ctx->dma);
+ ret_val = sst_dma_firmware(&sst_drv_ctx->fw_list);
if (ret_val)
- return ret_val;
- mutex_lock(&sst_drv_ctx->sst_lock);
- sst_drv_ctx->sst_state = SST_FW_LOADED;
- mutex_unlock(&sst_drv_ctx->sst_lock);
- /* 7. ask scu to reset the bypass bits */
- /* 8.bring sst out of reset */
+ goto free_dma;
+ sst_set_fw_state_locked(sst_drv_ctx, SST_FW_LOADED);
+ /* bring sst out of reset */
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID)
ret_val = sst_start_mrst();
else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID)
ret_val = sst_start_medfield();
if (ret_val)
- return ret_val;
+ goto free_dma;
pr_debug("fw loaded successful!!!\n");
+free_dma:
+ sst_dma_free_resources(&sst_drv_ctx->fw_list, &sst_drv_ctx->dma);
return ret_val;
}
union config_status_reg csr;
struct snd_sst_str_type str_type = {0};
int retval = 0;
+ void *codec_fw;
if (sst_create_large_msg(&msg))
return -ENOMEM;
}
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);
+ sst_set_fw_state_locked(sst_drv_ctx, SST_FW_LOADED);
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.part.bypass = 0x7;
sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full);
- sst_parse_fw_image(fw_lib);
+ codec_fw = kzalloc(fw_lib->size, GFP_KERNEL);
+ if (!codec_fw)
+ return -ENOMEM;
+ memcpy(codec_fw, fw_lib->data, fw_lib->size);
+ retval = sst_parse_fw_image(codec_fw, fw_lib->size);
+ sst_alloc_dma_chan(&sst_drv_ctx->dma);
+ sst_get_dma_desc(&sst_drv_ctx->fw_list, &sst_drv_ctx->dma);
+ retval = sst_dma_firmware(&sst_drv_ctx->fw_list);
+
+ sst_dma_free_resources(&sst_drv_ctx->fw_list, &sst_drv_ctx->dma);
+ kfree(codec_fw);
+ if (retval)
+ return retval;
/* set the FW to running again */
csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR);
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_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT);
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);
+ sst_set_fw_state_locked(sst_drv_ctx, SST_FW_RUNNING);
return 0;
-
}
/* This function is called before downloading the codec/postprocessing
#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/sched.h>
-#include "intel_sst.h"
-#include "intel_sst_ioctl.h"
+#include <sound/intel_sst.h>
+#include <sound/intel_sst_ioctl.h>
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
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);
- return;
}
/**
kfree(msg->mailbox_data);
kfree(msg);
- return;
}
/*
pr_debug("*** FW Init msg came***\n");
if (init->result) {
- mutex_lock(&sst_drv_ctx->sst_lock);
- sst_drv_ctx->sst_state = SST_ERROR;
- mutex_unlock(&sst_drv_ctx->sst_lock);
+ sst_set_fw_state_locked(sst_drv_ctx, SST_ERROR);
pr_debug("FW Init failed, Error %x\n", init->result);
pr_err("FW Init failed, Error %x\n", init->result);
retval = -init->result;
case IPC_SST_GET_PLAY_FRAMES:
if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) {
+ struct stream_info *stream ;
+
if (sst_validate_strid(str_id)) {
pr_err("strid %d invalid\n", str_id);
break;
}
/* call sst_play_frame */
+ stream = &sst_drv_ctx->streams[str_id];
pr_debug("sst_play_frames for %d\n",
msg->header.part.str_id);
mutex_lock(&sst_drv_ctx->streams[str_id].lock);
}
break;
case IPC_IA_ALG_PARAMS: {
- pr_debug("sst:IPC_ALG_PARAMS response %x\n", msg->header.full);
- pr_debug("sst: data value %x\n", msg->header.part.data);
- pr_debug("sst: large value %x\n", msg->header.part.large);
+ pr_debug("IPC_ALG_PARAMS response %x\n", msg->header.full);
+ pr_debug("data value %x\n", msg->header.part.data);
+ pr_debug("large value %x\n", msg->header.part.large);
if (!msg->header.part.large) {
if (!msg->header.part.data) {
- pr_debug("sst: alg set success\n");
+ pr_debug("alg set success\n");
sst_drv_ctx->ppp_params_blk.ret_code = 0;
} else {
- pr_debug("sst: alg set failed\n");
+ pr_debug("alg set failed\n");
sst_drv_ctx->ppp_params_blk.ret_code =
-msg->header.part.data;
}
struct snd_ppp_params *mailbox_params, *get_params;
char *params;
- pr_debug("sst: alg get success\n");
+ pr_debug("alg get success\n");
mailbox_params = (struct snd_ppp_params *)msg->mailbox;
get_params = kzalloc(sizeof(*get_params), GFP_KERNEL);
- if (get_params == NULL) {
- pr_err("sst: out of memory for ALG PARAMS");
+ if (!get_params) {
+ pr_debug("mem alloc failed\n");
break;
}
memcpy_fromio(get_params, mailbox_params,
sizeof(*get_params));
get_params->params = kzalloc(mailbox_params->size,
GFP_KERNEL);
- if (get_params->params == NULL) {
+ if (!get_params->params) {
+ pr_debug("mem alloc failed\n");
kfree(get_params);
- pr_err("sst: out of memory for ALG PARAMS block");
+ pr_err("out of memory for ALG PARAMS block");
break;
}
params = msg->mailbox;
case IPC_IA_TUNING_PARAMS:
case IPC_IA_SET_RUNTIME_PARAMS: {
- pr_debug("sst:IPC_TUNING_PARAMS resp: %x\n", msg->header.full);
+ pr_debug("IPC_TUNING_PARAMS resp: %x\n", msg->header.full);
pr_debug("data value %x\n", msg->header.part.data);
if (msg->header.part.large) {
pr_debug("alg set failed\n");
sst_drv_ctx->ppp_params_blk.condition = true;
wake_up(&sst_drv_ctx->wait_queue);
}
+ break;
}
case IPC_IA_GET_FW_INFO: {
wake_up(&sst_drv_ctx->wait_queue);
}
break;
+
case IPC_IA_PAUSE_STREAM:
case IPC_IA_RESUME_STREAM:
case IPC_IA_SET_STREAM_PARAMS:
break;
}
case IPC_IA_SET_FW_CTXT: {
- int retval;
+ int retval = msg->header.part.data;
if (!msg->header.part.data) {
- pr_debug("sst: Msg IPC_IA_SET_FW_CTXT succedded %x\n",
- msg->header.part.msg_id);
+ pr_debug("Msg IPC_IA_SET_FW_CTXT succedded %x\n",
+ msg->header.part.msg_id);
} else {
- pr_err("sst: Msg %x reply error %x\n",
- msg->header.part.msg_id, msg->header.part.data);
+ pr_err("Msg %x reply error %x\n",
+ msg->header.part.msg_id, msg->header.part.data);
}
- retval = msg->header.part.data;
sst_wake_up_alloc_block(sst_drv_ctx, FW_DWNL_ID, retval, NULL);
break;
}
#include <linux/fs.h>
#include <linux/firmware.h>
#include <linux/sched.h>
-#include "intel_sst.h"
-#include "intel_sst_ioctl.h"
+#include <sound/intel_sst.h>
+#include <sound/intel_sst_ioctl.h>
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
&sst_drv_ctx->hs_info_blk, SST_BLOCK_TIMEOUT);
return retval;
}
-
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
-#include "intel_sst_ioctl.h"
-#include "intel_sst.h"
+#include <sound/intel_sst_ioctl.h>
+#include <sound/intel_sst.h>
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
str_info = &sst_drv_ctx->streams[str_id];
if (resp->str_type.result == SST_LIB_ERR_LIB_DNLD_REQUIRED) {
lib_dnld = kzalloc(sizeof(*lib_dnld), GFP_KERNEL);
- memcpy(lib_dnld, &resp->lib_dnld, sizeof(*lib_dnld));
- } else
+ if (!lib_dnld) {
+ pr_debug("SST DBG: mem alloc failed\n");
+ retval = -ENOMEM;
+ } else {
+ memcpy(lib_dnld, &resp->lib_dnld, sizeof(*lib_dnld));
+ }
+ } else {
lib_dnld = NULL;
+ }
if (str_info->ctrl_blk.on == true) {
str_info->ctrl_blk.on = false;
str_info->ctrl_blk.data = lib_dnld;
str_info->ctrl_blk.ret_code = resp->str_type.result;
pr_debug("SST DEBUG: sst_alloc_stream_response: waking up.\n");
wake_up(&sst_drv_ctx->wait_queue);
+ } else {
+ kfree(lib_dnld);
+ pr_debug("SST DEBUG: sst_alloc_stream_response: ctrl block not on\n");
+ retval = -EIO;
}
return retval;
}
if (str_info->prev == STREAM_UN_INIT)
return -EBADRQC;
if (str_info->ctrl_blk.on == true) {
- pr_err("SST ERR: control path is in use\n");
+ pr_err("control path is in use\n");
return -EINVAL;
}
if (sst_create_short_msg(&msg))
}
} else {
retval = -EBADRQC;
- pr_err("SST ERR: BADQRC for stream\n");
+ pr_err("BADQRC for stream\n");
}
return retval;
}
} else {
retval = -EBADRQC;
- pr_debug("SST ERR:BADQRC for stream, state %x\n",
- str_info->status);
+ pr_debug("BADQRC for stream, state %x\n", str_info->status);
}
return retval;
}
struct stream_info *str_info;
pr_debug("SST DBG:sst_free_stream for %d\n", str_id);
- if (sst_drv_ctx->sst_state == SST_SUSPENDED) {
- pm_runtime_get_sync(&sst_drv_ctx->pci->dev);
- pr_debug("sst: DSP Downloading FW now...\n");
- retval = sst_download_fw();
- if (retval) {
- pr_err("sst: FW download fail %x, abort\n", retval);
- pm_runtime_put(&sst_drv_ctx->pci->dev);
- return retval;
- }
- }
retval = sst_validate_strid(str_id);
if (retval)
return retval;
#include <linux/rar_register.h>
#include "../memrar/memrar.h"
#endif
-#include "intel_sst_ioctl.h"
-#include "intel_sst.h"
+#include <sound/intel_sst_ioctl.h>
+#include <sound/intel_sst.h>
#include "intel_sst_fw_ipc.h"
#include "intel_sst_common.h"
/**
static int sst_target_device_validate(struct snd_sst_target_device *target)
{
int retval = 0;
- int i;
+ int i;
for (i = 0; i < SST_MAX_TARGET_DEVICES; i++) {
if (target->devices[i].device_type == SND_SST_DEVICE_PCM) {
pr_debug("Target Device Select\n");
- if (target->device_route > 2) {
+ if (target->device_route < 0 || target->device_route > 2) {
pr_err("device route is invalid\n");
return -EINVAL;
}
struct ipc_post *msg = NULL;
int retval = 0;
- pr_debug("SST DBG:sst_set_mute:called\n");
+ pr_debug("sst_set_mute:called\n");
if (str_info->decode_ibuf_type == SST_BUF_RAR) {
#ifdef CONFIG_MRST_RAR_HANDLER
return retval;
}
#endif
+
/*This function is used to prepare the kernel input buffers with contents
before sending for decode*/
static int sst_prepare_input_buffers(struct stream_info *str_info,
retval = sst_send_decode_mess(str_id, str_info, &dec_info);
if (retval || dec_info.input_bytes_consumed == 0) {
- pr_err("SST ERR: mess failed or no input consumed\n");
+ pr_err("messg failed or no input consumed\n");
goto finish;
}
input_bytes = dec_info.input_bytes_consumed;
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/slab.h>
-
#include <asm/intel_scu_ipc.h>
#include <asm/intel_mid_gpadc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/jack.h>
-#include "../../../drivers/staging/intel_sst/intel_sst.h"
+#include <sound/intel_sst.h>
#include "sn95031.h"
#define SN95031_RATES (SNDRV_PCM_RATE_8000_96000)
#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+#define SN95031_SW_DBNC 250
struct sn95031_work {
struct delayed_work work;
struct snd_soc_codec *codec;
};
+
+struct sn95031_jack_work {
+ unsigned int intr_id;
+ struct delayed_work work;
+ struct snd_soc_jack *jack;
+};
+
/* codec private data */
struct sn95031_priv {
uint8_t clk_src;
enum sn95031_pll_status pll_state;
- struct sn95031_work oc_work;
+ struct sn95031_jack_work jack_work;
};
-static void *audio_adc_handle;
-static unsigned int sn95031_lp_flag;
+
+void *audio_adc_handle;
+unsigned int sn95031_lp_flag;
/* This Function reads the voltage level from the ADC Driver*/
static unsigned int sn95031_read_voltage(void)
/* Reads the mic bias value */
if (!sn95031_lp_flag)
- /* GPADC MIC BIAS takes around a 1000ms to settle down and
+ /* GPADC MIC BIAS takes around a 50ms to settle down and
* get sampled porperly, reading earlier than this causes to
* read incorrect values */
- msleep(1000);
+ msleep(50);
intel_mid_gpadc_sample(audio_adc_handle, SN95031_ADC_SAMPLE_COUNT,
&mic_bias);
mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000;
/* enables mic bias voltage */
static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
{
- snd_soc_write(codec, SN95031_VAUD, 0x2D);
- snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2));
+ pr_debug("enable mic bias\n");
+ pr_debug("codec %p\n", codec);
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "AMIC1Bias");
+ snd_soc_dapm_sync(&codec->dapm);
}
+/* disables mic bias voltage */
+static void sn95031_disable_mic_bias(struct snd_soc_codec *codec)
+{
+ pr_debug("disable mic bias\n");
+ snd_soc_dapm_disable_pin(&codec->dapm, "AMIC1Bias");
+ snd_soc_dapm_sync(&codec->dapm);
+}
/* reads the ADC registers and gets the mic bias value in mV. */
static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec)
{
unsigned int mic_bias;
- sn95031_enable_mic_bias(codec);
mic_bias = sn95031_read_voltage();
return mic_bias;
}
case SND_SOC_BIAS_STANDBY:
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
pr_debug("vaud_bias power up rail\n");
+ intel_sst_set_pll(true, SST_PLL_MSIC);
/* power up the rail, on in normal and aoac mode */
snd_soc_write(codec, SN95031_VAUD, 0x2D);
msleep(1);
* so 100100b ie 24
*/
snd_soc_write(codec, SN95031_VAUD, 0x24);
+ sn95031_configure_pll(codec, DISABLE_PLL);
+ intel_sst_set_pll(false, SST_PLL_MSIC);
break;
}
{
if (SND_SOC_DAPM_EVENT_ON(event)) {
pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
- /* power up the rail */
- snd_soc_write(w->codec, SN95031_VHSP, 0x2D);
+ /* power up the rail- 1.8v, powersave mode */
+ snd_soc_write(w->codec, SN95031_VHSP, 0xED);
snd_soc_write(w->codec, SN95031_VHSN, 0x2D);
msleep(1);
} else if (SND_SOC_DAPM_EVENT_OFF(event)) {
pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
- snd_soc_write(w->codec, SN95031_VHSP, 0x24);
+ /* First disable VHSN and then followed by VHSP rail.
+ Need to have minimum of 5ms delay between the rail shutdowns
+ to avoid any glitches due to transients. */
snd_soc_write(w->codec, SN95031_VHSN, 0x24);
+ usleep_range(5000, 6000);
+ snd_soc_write(w->codec, SN95031_VHSP, 0x24);
}
return 0;
}
pr_debug("sn95031_dmic12_event\n");
if (SND_SOC_DAPM_EVENT_ON(event)) {
- pr_debug("sn95031_dmic12_eventi ON\n");
+ pr_debug("sn95031_dmic12_event ON\n");
ldo = BIT(5)|BIT(4);
clk_dir = BIT(0);
data_dir = BIT(7);
static const struct soc_enum sn95031_vibra2_brake_enum =
SOC_ENUM_SINGLE(SN95031_VIB2C1, 1, 2, sn95031_vibra_start_text);
+static const char *sn95031_jack_debounce_text[] = {"61us", "122us", "244us",
+ "488us", "976us", "1.952ms",
+ "3.904ms", "7.8125ms", "15.625ms",
+ "31.25ms", "62.5ms", "125ms"};
+static const struct soc_enum sn95031_jack_debounce_enum =
+ SOC_ENUM_SINGLE(SN95031_BTNCTRL1, 4, 12, sn95031_jack_debounce_text);
+
static const struct snd_kcontrol_new sn95031_snd_controls[] = {
SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum),
SOC_ENUM("Mic2Mode Capture Route", sn95031_micmode2_enum),
SOC_ENUM("Vibra2 Off Time", sn95031_vibra2_off_enum),
SOC_ENUM("Vibra2 Start", sn95031_vibra2_start_enum),
SOC_ENUM("Vibra2 Brake", sn95031_vibra2_brake_enum),
+ SOC_ENUM("Jack Debounce Time", sn95031_jack_debounce_enum),
};
/* DAPM widgets */
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("PCM2_IN", "Headset", 0,
SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("PCM1_IN", "Voice-Playback", 0,
+ SND_SOC_DAPM_AIF_IN("PCM1_IN", "Downlink", 0,
SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_OUT("PCM1_Out", "Voice-Capture", 0,
+ SND_SOC_DAPM_AIF_OUT("PCM1_Out", "Uplink", 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0,
sn95031_vhs_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* playback path driver enables */
- SND_SOC_DAPM_PGA("Headset Left Playback",
+ SND_SOC_DAPM_OUT_DRV("Headset Left Playback",
SN95031_DRIVEREN, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Headset Right Playback",
+ SND_SOC_DAPM_OUT_DRV("Headset Right Playback",
SN95031_DRIVEREN, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Speaker Left Playback",
SN95031_DRIVEREN, 2, 0, NULL, 0),
/* Voice Playback path*/
{ "Mode Playback Route", "Voice", "PCM1_IN"},
/* speaker map */
- { "IHFOUTL", NULL, "Speaker Rail"},
- { "IHFOUTR", NULL, "Speaker Rail"},
{ "IHFOUTL", "NULL", "Speaker Left Playback"},
{ "IHFOUTR", "NULL", "Speaker Right Playback"},
{ "Speaker Left Playback", "Music", "Speaker Mux Playback Route"},
static int sn95031_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
+ int mode;
struct snd_soc_codec *codec = codec_dai->codec;
struct sn95031_priv *sn95031_ctx;
sn95031_ctx = snd_soc_codec_get_drvdata(codec_dai->codec);
mutex_unlock(&codec->mutex);
return 0;
}
- sn95031_ctx->clk_src = source;
+ mode = snd_soc_read(codec, SN95031_PCM1C3) >> 7;
+ if (!mode && (!strcmp(codec_dai->name, "SN95031 Voice"))) {
+ sn95031_ctx->clk_src = SN95031_PCM1BCLK;
+ snd_soc_write(codec, SN95031_PCM1C2, 0x04);
+ } else {
+ sn95031_ctx->clk_src = SN95031_PLLIN;
+ snd_soc_write(codec, SN95031_PCM1C2, 0x00);
+ }
if (codec->dapm.bias_level >= SND_SOC_BIAS_PREPARE) {
pr_debug("bias_level is active, enabling pll\n");
sn95031_configure_pll(codec, ENABLE_PLL);
snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate);
snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(6)|BIT(5)|BIT(4),
pcm1fs);
- /* configure the PCM1 in I2S mode */
- snd_soc_write(dai->codec, SN95031_PCM1C2, 0x04);
/* enable pcm 1 */
snd_soc_update_bits(dai->codec, SN95031_PCM1C3, BIT(0), BIT(0));
return 0;
{
.name = "SN95031 Voice",
.playback = {
- .stream_name = "Voice-Playback",
+ .stream_name = "Downlink",
.channels_min = 1,
.channels_max = 2,
.rates = SN95031_RATES,
.formats = SN95031_FORMATS,
},
.capture = {
- .stream_name = "Voice-Capture",
+ .stream_name = "Uplink",
.channels_min = 1,
.channels_max = 2,
.rates = SN95031_RATES,
static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec)
{
- snd_soc_write(codec, SN95031_BTNCTRL1, 0x77);
snd_soc_update_bits(codec, SN95031_BTNCTRL2, BIT(0), BIT(0));
}
static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
{
- int micbias = sn95031_get_mic_bias(mfld_jack->codec);
+ int micbias, jack_type;
- int jack_type = snd_soc_jack_get_type(mfld_jack, micbias);
+ sn95031_enable_mic_bias(mfld_jack->codec);
+ micbias = sn95031_get_mic_bias(mfld_jack->codec);
- pr_debug("jack type detected = %d\n", jack_type);
+ jack_type = snd_soc_jack_get_type(mfld_jack, micbias);
+
+ pr_debug("jack type detected = %d, micbias = %d\n", jack_type, micbias);
if (jack_type == SND_JACK_HEADSET)
sn95031_enable_jack_btn(mfld_jack->codec);
+ else
+ sn95031_disable_mic_bias(mfld_jack->codec);
+
return jack_type;
}
-static void sn95031_jack_report(struct mfld_jack_data *jack_data,
- unsigned int status)
+static void sn95031_jack_report(struct snd_soc_jack *jack, unsigned int status)
{
unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
+ pr_debug("jack reported of type: %d\n", status);
if ((status == SND_JACK_HEADSET) | (status == SND_JACK_HEADPHONE)) {
pr_debug("detected headset or headphone, disabling JACKDET\n");
- snd_soc_update_bits(jack_data->mfld_jack->codec,
- SN95031_ACCDETMASK, BIT(2), BIT(2));
+ snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK,
+ BIT(2), BIT(2));
+
/* if we detected valid headset then disable headset ground.
* Otherwise enable it in else condition
* this is required for jack detect to work well */
- snd_soc_update_bits(jack_data->mfld_jack->codec,
- SN95031_BTNCTRL2, BIT(1), 0);
+ snd_soc_update_bits(jack->codec, SN95031_BTNCTRL2, BIT(1), 0);
} else
- snd_soc_update_bits(jack_data->mfld_jack->codec,
+ snd_soc_update_bits(jack->codec,
SN95031_BTNCTRL2, BIT(1), BIT(1));
-
- snd_soc_jack_report(jack_data->mfld_jack, status, mask);
+ snd_soc_jack_report(jack, status, mask);
#ifdef CONFIG_SWITCH_MID
if (status) {
if (status == SND_JACK_HEADPHONE)
else if (status == SND_JACK_HEADSET)
mid_headset_report(1);
} else {
- mid_headset_report(0);
+ mid_headset_report(0);
}
#endif
/*button pressed and released so we send explicit button release */
if (status & SND_JACK_BTN_0)
- snd_soc_jack_report(jack_data->mfld_jack,
- SND_JACK_HEADSET, mask);
+ snd_soc_jack_report(jack, SND_JACK_HEADSET, mask);
}
-void sn95031_jack_detection(struct mfld_jack_data *jack_data)
+void sn95031_jack_wq(struct work_struct *work)
{
- unsigned int status, voltage;
unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
-
- pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id);
- if (jack_data->intr_id & 0x1) {
- pr_debug("short_push detected\n");
+ struct sn95031_priv *sn95031_ctx =
+ container_of(work, struct sn95031_priv, jack_work.work.work);
+ struct sn95031_jack_work *jack_wq = &sn95031_ctx->jack_work;
+ struct snd_soc_jack *jack = jack_wq->jack;
+ unsigned int voltage, status = 0;
+
+ pr_debug("jack status in wq:%d\n", jack_wq->intr_id);
+ if (jack_wq->intr_id & SN95031_JACK_INSERTED) {
+ status = sn95031_get_headset_state(jack);
+ jack_wq->intr_id &= ~SN95031_JACK_INSERTED;
+ } else if (jack_wq->intr_id & SN95031_JACK_REMOVED) {
+ pr_debug("reporting jack as removed\n");
+ sn95031_disable_jack_btn(jack->codec);
+ snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK, BIT(2), 0);
+ sn95031_disable_mic_bias(jack->codec);
+ jack_wq->intr_id &= ~SN95031_JACK_REMOVED;
+ } else if (jack_wq->intr_id & SN95031_JACK_BTN0) {
if (sn95031_lp_flag) {
- snd_soc_jack_report(jack_data->mfld_jack,
- SND_JACK_HEADSET, mask);
+ snd_soc_jack_report(jack, SND_JACK_HEADSET, mask);
sn95031_lp_flag = 0;
return;
- } else
+ } else {
status = SND_JACK_HEADSET | SND_JACK_BTN_0;
- sn95031_jack_report(jack_data, status);
- }
- if (jack_data->intr_id & 0x2) {
- pr_debug("long_push detected\n");
+ }
+ jack_wq->intr_id &= ~SN95031_JACK_BTN0;
+ } else if (jack_wq->intr_id & SN95031_JACK_BTN1) {
/* we get spurious intterupts if jack key is held down
* so we ignore them untill key is released by
* checking the voltage level */
if (sn95031_lp_flag) {
voltage = sn95031_read_voltage();
if (voltage > 400) {
- snd_soc_jack_report(jack_data->mfld_jack,
+ snd_soc_jack_report(jack,
SND_JACK_HEADSET, mask);
sn95031_lp_flag = 0; /* button released */
}
}
status = SND_JACK_HEADSET | SND_JACK_BTN_1;
sn95031_lp_flag = 1;
- sn95031_jack_report(jack_data, status);
- }
- if (jack_data->intr_id & 0x4) {
- pr_debug("headset or headphones inserted\n");
- status = sn95031_get_headset_state(jack_data->mfld_jack);
- sn95031_jack_report(jack_data, status);
- }
- if (jack_data->intr_id & 0x8) {
- pr_debug("headset or headphones removed, disabling btn and enabling JACKDET\n");
- status = 0;
- sn95031_disable_jack_btn(jack_data->mfld_jack->codec);
- snd_soc_update_bits(jack_data->mfld_jack->codec,
- SN95031_ACCDETMASK, BIT(2), 0);
- sn95031_jack_report(jack_data, status);
+ jack_wq->intr_id &= ~SN95031_JACK_BTN1;
}
+ sn95031_jack_report(jack, status);
}
-EXPORT_SYMBOL_GPL(sn95031_jack_detection);
-static void sn95031_ocvol_int_mask(struct snd_soc_codec *codec, int status)
+static int sn95031_schedule_jack_wq(struct mfld_jack_data *jack_data)
{
- snd_soc_update_bits(codec, SN95031_OCAUDIOMASK, BIT(0), status);
-
-}
-static void sn95031_restore_ihf_vol(struct snd_soc_codec *codec,
- unsigned int vol_addr, int crush_volume)
-{
- u8 ihf_volume;
-
- pr_debug("In %s\n", __func__);
- ihf_volume = SN95031_IHF_VOLUME_MASK & snd_soc_read(codec, vol_addr);
-
- if (!crush_volume)
- ihf_volume -= SN95031_BCU_VOLUME_RECOVERY_3DB;
- else
- ihf_volume -= SN95031_BCU_VOLUME_RECOVERY_6DB;
-
- snd_soc_update_bits(codec, vol_addr,
- SN95031_IHF_VOLUME_MASK, ihf_volume);
-
- ihf_volume = snd_soc_read(codec, vol_addr);
- pr_debug("IHF Volume: %x : %x\n", vol_addr, ihf_volume);
+ int retval = 0;
+ struct sn95031_priv *sn95031 = snd_soc_codec_get_drvdata(
+ jack_data->mfld_jack->codec);
+
+ sn95031->jack_work.jack = jack_data->mfld_jack;
+ retval = schedule_delayed_work(&sn95031->jack_work.work,
+ msecs_to_jiffies(SN95031_SW_DBNC));
+ return retval;
}
-
-static void sn95031_oc_workqueue(struct work_struct *work)
+void sn95031_jack_detection(struct mfld_jack_data *jack_data)
{
- int crush_volume;
- struct sn95031_work *oc_wq =
- container_of(work, struct sn95031_work, work.work);
- struct snd_soc_codec *codec = oc_wq->codec;
-
- pr_debug("In %s\n", __func__);
- if (!codec) {
- pr_debug("codec value null");
- return;
- }
- crush_volume =
- SN95031_BCU_CRUSH_VOL & snd_soc_read(codec, SN95031_IHFRXCTRL);
+ int retval = 0;
+ struct sn95031_priv *sn95031 = snd_soc_codec_get_drvdata(
+ jack_data->mfld_jack->codec);
- sn95031_restore_ihf_vol(codec, SN95031_IHFLVOLCTRL, crush_volume);
- sn95031_restore_ihf_vol(codec, SN95031_IHFRVOLCTRL, crush_volume);
- sn95031_ocvol_int_mask(codec, false);
-
-}
+ pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id);
-void sn95031_oc_handler(struct snd_soc_codec *codec, int oc_interrupt_value)
-{
- int value;
- struct sn95031_priv *sn95031_ctx;
+ if (jack_data->intr_id & SN95031_JACK_INSERTED ||
+ jack_data->intr_id & SN95031_JACK_REMOVED) {
- pr_debug("In %s\n", __func__);
- if (!codec) {
- pr_debug("codec value null\n");
+ retval = sn95031_schedule_jack_wq(jack_data);
+ if (!retval) {
+ pr_debug("jack inserted/removed, intr already queued \n");
+ sn95031->jack_work.intr_id = jack_data->intr_id;
+ } else {
+ sn95031->jack_work.intr_id |= jack_data->intr_id;
+ }
return;
}
- sn95031_ocvol_int_mask(codec, true);
- if (oc_interrupt_value & 0x01) {
- sn95031_ctx = snd_soc_codec_get_drvdata(codec);
-
- value = snd_soc_read(codec, SN95031_BURST_CNTRL);
- if (value & BIT(5))
- pr_debug("TXPACTEN set to'1'<5 percent BCU setting\n");
- schedule_delayed_work(&sn95031_ctx->oc_work.work,
- msecs_to_jiffies(SN95031_BCU_DELAY));
- } else {
- pr_debug("unhandled interrupt: %x..\n", oc_interrupt_value);
- sn95031_ocvol_int_mask(codec, false);
+ if (jack_data->intr_id & SN95031_JACK_BTN0 ||
+ jack_data->intr_id & SN95031_JACK_BTN1) {
+ if (jack_data->mfld_jack->status == SND_JACK_HEADSET) {
+ retval = sn95031_schedule_jack_wq(jack_data);
+ if (!retval) {
+ pr_debug("spurious button press detected\n");
+ sn95031->jack_work.intr_id = jack_data->intr_id;
+ return;
+ } else {
+ sn95031->jack_work.intr_id |= jack_data->intr_id;
+ }
+ pr_debug("BTN_Press detected\n");
+ } else {
+ pr_debug("BTN_press received, but jack is removed\n");
+ }
}
}
-EXPORT_SYMBOL_GPL(sn95031_oc_handler);
+EXPORT_SYMBOL_GPL(sn95031_jack_detection);
/* codec registration */
static int sn95031_codec_probe(struct snd_soc_codec *codec)
{
struct sn95031_priv *sn95031_ctx;
+
pr_debug("codec_probe called\n");
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
sn95031_ctx->clk_src = SN95031_INVALID;
sn95031_ctx->pll_state = PLL_DISABLED;
- INIT_DELAYED_WORK(&sn95031_ctx->oc_work.work, sn95031_oc_workqueue);
- sn95031_ctx->oc_work.codec = codec;
+ INIT_DELAYED_WORK(&sn95031_ctx->jack_work.work, sn95031_jack_wq);
/* PCM1 slot configurations*/
snd_soc_write(codec, SN95031_NOISEMUX, 0x0);
snd_soc_write(codec, SN95031_AUDIOMUX34, 0x32);
/* voice related stuff */
snd_soc_write(codec, SN95031_VOICETXVOL, 0x89);
+ /* debounce time and long press duration */
+ snd_soc_write(codec, SN95031_BTNCTRL1, 0xA7);
/* soft mute ramp time */
snd_soc_write(codec, SN95031_SOFTMUTE, 0x3);
snd_soc_write(codec, SN95031_VHSN, 0x24);
snd_soc_write(codec, SN95031_VHSP, 0x24);
+ /* mask the OCVOLSTSMASK bit, so that driver will not receive
+ * any overcurrent interrupt. Overcurent scenario will be
+ * handled from the application space through alsa_amixer
+ * volume control.
+ */
+ snd_soc_update_bits(codec, SN95031_OCAUDIOMASK, BIT(0), BIT(0));
+
snd_soc_add_controls(codec, sn95031_snd_controls,
ARRAY_SIZE(sn95031_snd_controls));
/*Free the adc handle*/
intel_mid_gpadc_free(audio_adc_handle);
-
+ cancel_delayed_work(&sn95031_ctx->jack_work.work);
kfree(sn95031_ctx);
+
return 0;
}
-
-static struct snd_soc_codec_driver sn95031_codec = {
+struct snd_soc_codec_driver sn95031_codec = {
.probe = sn95031_codec_probe,
.remove = sn95031_codec_remove,
.read = sn95031_read,
#define SN95031_SSR5 0x384
#define SN95031_SSR6 0x385
+#define SN95031_JACK_INSERTED 0x04
+#define SN95031_JACK_REMOVED 0x08
+#define SN95031_JACK_BTN1 0x02
+#define SN95031_JACK_BTN0 0x01
+
/* ADC registers */
#define SN95031_ADC1CNTL1 0x1C0
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/async.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
static struct snd_soc_jack mfld_jack;
+/*Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin mfld_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "AMIC1",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
/* jack detection voltage zones */
static struct snd_soc_jack_zone mfld_zones[] = {
{MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE},
static const struct soc_enum lo_enum =
SOC_ENUM_SINGLE_EXT(4, lo_text);
+static const char *sn95031_pcm1_mode_text[] = {"Slave", "Master"};
+
+static const struct soc_enum SN95031_pcm1_mode_config_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sn95031_pcm1_mode_text),
+ sn95031_pcm1_mode_text);
+
static int headset_get_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
lo_dac = ucontrol->value.integer.value[0];
return 0;
}
+static int sn95031_get_pcm1_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mode;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ mode = snd_soc_read(codec, SN95031_PCM1C3) >> 7;
+ pr_debug("mode: %d\n", mode);
+ ucontrol->value.integer.value[0] = mode;
+ return 0;
+}
+static int sn95031_set_pcm1_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u8 mode = ucontrol->value.integer.value[0];
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (mode) {
+ pr_debug("can we set the master mode settings\n");
+ snd_soc_update_bits(codec, SN95031_PCM1C3,
+ BIT(1)|BIT(2)|BIT(3)|BIT(7), BIT(7)|BIT(1));
+ snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1),
+ BIT(0)|BIT(1));
+ snd_soc_update_bits(codec, SN95031_PCM1C2,
+ BIT(0)|BIT(1)|BIT(2), 0);
+ } else {
+ pr_debug("setting the slave mode settings\n");
+ snd_soc_update_bits(codec, SN95031_PCM1C3, BIT(7), 0);
+ snd_soc_update_bits(codec, SN95031_PCM1C1, BIT(0)|BIT(1), 0);
+ snd_soc_update_bits(codec, SN95031_PCM1C2, BIT(2), BIT(2));
+
+ }
+ return 0;
+}
static const struct snd_kcontrol_new mfld_snd_controls[] = {
SOC_ENUM_EXT("Playback Switch", headset_enum,
headset_get_switch, headset_set_switch),
SOC_ENUM_EXT("Lineout Mux", lo_enum,
lo_get_switch, lo_set_switch),
+ SOC_ENUM_EXT("PCM1 Mode", SN95031_pcm1_mode_config_enum,
+ sn95031_get_pcm1_mode, sn95031_set_pcm1_mode),
};
static const struct snd_soc_dapm_widget mfld_widgets[] = {
/* TODO: add american headset detection post gpiolib support */
}
+static unsigned int async_param;
+static LIST_HEAD(mfld_jack_async_list);
+static void mfld_jack_check_async(void *ptr, async_cookie_t cookie)
+{
+ mfld_jack_check(*(unsigned int *)ptr);
+ return;
+}
+
static int mfld_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_codec *codec = runtime->codec;
snd_soc_dapm_disable_pin(dapm, "DMIC6");
snd_soc_dapm_disable_pin(dapm, "AMIC2");
+ /* Keep the voice call paths active during
+ suspend. Mark the end points ignore_suspend */
snd_soc_dapm_ignore_suspend(dapm, "PCM1_IN");
snd_soc_dapm_ignore_suspend(dapm, "PCM1_Out");
- snd_soc_dapm_ignore_suspend(dapm, "PCM2_IN");
- snd_soc_dapm_ignore_suspend(dapm, "PCM2_Out");
- snd_soc_dapm_ignore_suspend(dapm, "IHFDAC Right");
- snd_soc_dapm_ignore_suspend(dapm, "IHFDAC Left");
- snd_soc_dapm_ignore_suspend(dapm, "HSDAC Right");
- snd_soc_dapm_ignore_suspend(dapm, "HSDAC Left");
-
+ snd_soc_dapm_ignore_suspend(dapm, "EPOUT");
+ snd_soc_dapm_ignore_suspend(dapm, "HPOUTL");
+ snd_soc_dapm_ignore_suspend(dapm, "HPOUTR");
+ snd_soc_dapm_ignore_suspend(dapm, "IHFOUTL");
+ snd_soc_dapm_ignore_suspend(dapm, "IHFOUTR");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC1");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC3");
+ snd_soc_dapm_ignore_suspend(dapm, "DMIC5");
+ snd_soc_dapm_ignore_suspend(dapm, "AMIC1");
snd_soc_dapm_sync(dapm);
/* Headset and button jack detection */
ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",
/* we want to check if anything is inserted at boot,
* so send a fake event to codec and it will read adc
* to find if anything is there or not */
- mfld_jack_check(MFLD_JACK_INSERT);
+ async_param = MFLD_JACK_INSERT;
+ async_schedule_domain(mfld_jack_check_async,
+ &async_param, &mfld_jack_async_list);
return ret_val;
}
.platform_name = "sst-platform",
.init = NULL,
.ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
},
};
{
struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
unsigned long flags;
- u8 oc_int_value = 0, jack_int_value = 0;
+ u8 jack_int_value = 0;
if (mfld_jack.codec == NULL) {
pr_debug("codec NULL returning..");
goto ret;
}
if (mc_drv_ctx->oc_interrupt_status) {
- oc_int_value = mc_drv_ctx->oc_interrupt_status;
+ pr_info("OC int value: %d\n", mc_drv_ctx->oc_interrupt_status);
mc_drv_ctx->oc_interrupt_status = 0;
}
if (mc_drv_ctx->jack_interrupt_status) {
}
spin_unlock_irqrestore(&mc_drv_ctx->lock, flags);
- if (oc_int_value) {
- mc_drv_ctx->codec = mfld_jack.codec;
- sn95031_oc_handler(mc_drv_ctx->codec, oc_int_value);
- }
if (jack_int_value)
mfld_jack_check(jack_int_value);
/* register for interrupt */
ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
snd_mfld_codec_intr_detection,
- IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ pdev->dev.driver->name, mc_drv_ctx);
if (ret_val) {
pr_err("cannot register IRQ\n");
goto unalloc;
pr_debug("snd_soc_register_card failed %d\n", ret_val);
goto freeirq;
}
- platform_set_drvdata(pdev, mc_drv_ctx);
- dev_set_drvdata(&pdev->dev, &snd_soc_card_mfld);
+ platform_set_drvdata(pdev, &snd_soc_card_mfld);
+ snd_soc_card_set_drvdata(&snd_soc_card_mfld, mc_drv_ctx);
pr_debug("successfully exited probe\n");
return ret_val;
static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
{
- struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev);
+ struct snd_soc_card *soc_card = platform_get_drvdata(pdev);
+ struct mfld_mc_private *mc_drv_ctx = snd_soc_card_get_drvdata(soc_card);
pr_debug("snd_mfld_mc_remove called\n");
free_irq(platform_get_irq(pdev, 0), mc_drv_ctx);
- snd_soc_unregister_card(&snd_soc_card_mfld);
kfree(mc_drv_ctx);
+ snd_soc_card_set_drvdata(soc_card, NULL);
+ snd_soc_unregister_card(soc_card);
platform_set_drvdata(pdev, NULL);
return 0;
}
pr_debug("snd_mfld_driver_init called\n");
return platform_driver_register(&snd_mfld_mc_driver);
}
-module_init(snd_mfld_driver_init);
+module_init_async(snd_mfld_driver_init);
static void __exit snd_mfld_driver_exit(void)
{
pr_debug("snd_mfld_driver_exit called\n");
+ async_synchronize_full_domain(&mfld_jack_async_list);
platform_driver_unregister(&snd_mfld_mc_driver);
}
module_exit(snd_mfld_driver_exit);
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include "../../../drivers/staging/intel_sst/intel_sst_ioctl.h"
-#include "../../../drivers/staging/intel_sst/intel_sst.h"
+#include <sound/intel_sst_ioctl.h>
+#include <sound/intel_sst.h>
#include "../codecs/sn95031.h"
#include "sst_platform.h"
},
.capture = {
.channels_min = 1,
- .channels_max = 5,
+ .channels_max = 2,
.rates = SNDRV_PCM_RATE_44100,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.name = "Vibra2-cpu-dai",
.playback = {
.channels_min = SST_MONO,
- .channels_max = SST_STEREO,
+ .channels_max = SST_MONO,
.rates = SNDRV_PCM_RATE_44100,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
struct sst_runtime_stream *stream;
int status;
- if (!substream || !substream->runtime)
+ if (!substream || !substream->runtime) {
+ pr_debug("In %s : Null Substream pointer\n", __func__);
return;
+ }
stream = substream->runtime->private_data;
- if (!stream)
+ if (!stream) {
+ pr_debug("In %s : Null Stream pointer\n", __func__);
return;
+ }
status = sst_get_stream_status(stream);
- if (status != SST_PLATFORM_RUNNING)
+ if (status != SST_PLATFORM_RUNNING) {
+ pr_debug("In %s : Stream Status=%d\n", __func__, status);
return;
+ }
snd_pcm_period_elapsed(substream);
}
}
runtime->private_data = stream;
sst_cpu_ctx->active_nonvoice_cnt++;
+
+ /* Make sure, that the period size is always even */
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
+
return snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
func_exit:
if (!sst_cpu_ctx->active_nonvoice_cnt)
snd_soc_dai_set_tristate(codec_dai, 1);
- /*if all CPU dais are inactive, disable PLL*/
- if (!sst_cpu_ctx->active_voice_cnt && !sst_cpu_ctx->active_nonvoice_cnt)
- snd_soc_dai_set_pll(codec_dai, 0, 0, 0, 0);
return ret_val;
}
static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
{
struct sst_runtime_stream *stream;
- int ret_val = 0;
+ int ret_val = 0, str_id;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai_link *dai_link = rtd->dai_link;
return ret_val;
}
stream = substream->runtime->private_data;
+ str_id = stream->stream_info.str_id;
if (stream->stream_info.str_id)
return ret_val;
static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- int ret, clk_src;
+ int ret;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
- struct snd_soc_dai_link *dai_link = rtd->dai_link;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- if (!strcmp(dai_link->cpu_dai_name, SST_VOICE_DAI))
- clk_src = SN95031_PCM1BCLK;
- else {
- clk_src = SN95031_PLLIN;
- /* Force the data width to 24 bit in MSIC. Post Processing
- algorithms in DSP enabled with 24 bit precision */
- ret = snd_soc_codec_set_params(codec, SNDRV_PCM_FORMAT_S24_LE);
- if (ret < 0) {
- pr_debug("codec set_params returned error\n");
- return ret;
- }
+ /* Force the data width to 24 bit in MSIC. Post Processing
+ algorithms in DSP enabled with 24 bit precision */
+ ret = snd_soc_codec_set_params(codec, SNDRV_PCM_FORMAT_S24_LE);
+ if (ret < 0) {
+ pr_debug("codec set_params returned error\n");
+ return ret;
}
/*last two parameters have to non-zero, otherwise pll gets disabled*/
- snd_soc_dai_set_pll(codec_dai, 0, clk_src, 1, params_rate(params));
+ snd_soc_dai_set_pll(codec_dai, 0, SST_CLK_UNINIT, 1,
+ params_rate(params));
snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
- SST_MIN_BUFFER, SST_MAX_BUFFER);
+ SST_MAX_BUFFER, SST_MAX_BUFFER);
if (retval) {
pr_err("dma buffer allocationf fail\n");
return retval;
#define SST_MAX_RATE 48000
#define SST_MIN_CHANNEL 1
#define SST_MAX_CHANNEL 2
-#define SST_MAX_BUFFER 22050 /*500ms*/
-#define SST_MIN_BUFFER 22050
-#define SST_MIN_PERIOD_BYTES 441 /*10ms */
-#define SST_MAX_PERIOD_BYTES ((SST_MAX_BUFFER) / (SST_MIN_PERIODS))
+#define SST_MAX_BUFFER 88200 /*500ms*/
+#define SST_MIN_PERIOD_BYTES 1764 /*10ms@44.1,16bit,2ch*/
+#define SST_MAX_PERIOD_BYTES 44100 /*250ms*/
#define SST_MIN_PERIODS 2
#define SST_MAX_PERIODS 50
#define SST_FIFO_SIZE 0
#define SST_CARD_NAMES "intel_mid_card"
#define MSIC_VENDOR_ID 3
+#define SST_CLK_UNINIT 0x03
struct sst_runtime_stream {
int stream_status;
cpu_dai->runtime = NULL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- /* start delayed pop wq here for playback streams */
- codec_dai->pop_wait = 1;
- schedule_delayed_work(&rtd->delayed_work,
- msecs_to_jiffies(rtd->pmdown_time));
+ if (unlikely(rtd->dai_link->ignore_pmdown_time)) {
+ /* powered down playback stream now */
+ snd_soc_dapm_stream_event(rtd,
+ codec_dai->driver->playback.stream_name,
+ SND_SOC_DAPM_STREAM_STOP);
+ } else {
+ /* start delayed pop wq here for playback streams */
+ codec_dai->pop_wait = 1;
+ schedule_delayed_work(&rtd->delayed_work,
+ msecs_to_jiffies(rtd->pmdown_time));
+ }
} else {
/* capture streams can be powered down now */
snd_soc_dapm_stream_event(rtd,