Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:pzskc383
kernel
linux-2.6.34-moorestown-audio-driver-5.0.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File linux-2.6.34-moorestown-audio-driver-5.0.patch of Package kernel
Index: linux-2.6.33/include/sound/intel_lpe.h =================================================================== --- /dev/null +++ linux-2.6.33/include/sound/intel_lpe.h @@ -0,0 +1,139 @@ +#ifndef __INTEL_SST_H__ +#define __INTEL_SST_H__ +/* + * intel_lpe.h - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corporation + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This driver exposes the audio engine functionalities to the ALSA + * and middleware. + * This file is shared between the SST and MAD drivers + */ + +#define SST_CARD_NAMES "intel_mid_card" + +/* control list Pmic & Lpe */ +/* Input controls */ +enum port_status { + ACTIVATE = 1, + DEACTIVATE, +}; + +/* Card states */ +enum sst_card_states { + SND_CARD_UN_INIT = 0, + SND_CARD_INIT_DONE, +}; + +enum sst_controls { + SST_SND_ALLOC = 0x1000, + SST_SND_PAUSE = 0x1001, + SST_SND_RESUME = 0x1002, + SST_SND_DROP = 0x1003, + SST_SND_FREE = 0x1004, + SST_SND_BUFFER_POINTER = 0x1005, + SST_SND_STREAM_INIT = 0x1006, + SST_SND_START = 0x1007, + SST_SND_STREAM_PROCESS = 0x1008, + SST_MAX_CONTROLS = 0x1008, + SST_CONTROL_BASE = 0x1000, +}; + +struct pcm_stream_info { + int str_id; + void *mad_substream; + void (*period_elapsed) (void *mad_substream); + unsigned long long buffer_ptr; + int sfreq; +}; + +struct stream_buffer { + unsigned long addr; + int length; +}; + + +/* SST debug print statement definition */ + +static inline long sst_get_time(void) +{ + struct timeval t; + do_gettimeofday(&t); + return t.tv_usec; +} + +struct snd_pmic_ops { + int card_status; + int (*set_input_dev) (u8 value); + int (*set_output_dev) (u8 value); + + int (*set_mute) (int dev_id, u8 value); + int (*get_mute) (int dev_id, u8 *value); + + int (*set_vol) (int dev_id, u8 value); + int (*get_vol) (int dev_id, u8 *value); + + int (*init_card) (void); + int (*set_pcm_audio_params) (int sfreq, int word_size); + int (*set_pcm_voice_params) (void); + int (*set_voice_port) (int status); + int (*set_audio_port) (int status); + + int (*power_up_pmic_pb) (void); + int (*power_up_pmic_cp) (void); + int (*power_down_pmic_pb) (void); + int (*power_down_pmic_cp) (void); + int (*power_down_pmic) (void); +}; + +struct intel_sst_card_ops { + char *module_name; + int vendor_id; + int (*control_set) (int control_element, void *value); + int (*send_buffer) (int str_id, struct stream_buffer *mad_buf); + struct snd_pmic_ops *scard_ops; +}; + +/* periphral interrupt interface */ +enum lpe_periphral { + LPE_DMA = 1, + LPE_SSP0, + LPE_SSP1 +}; + +/* modified for generic access */ +struct sc_reg_access { + u16 reg_addr; + u8 value; + u8 mask; +}; +enum sc_reg_access_type { + PMIC_READ = 0, + PMIC_WRITE, + PMIC_READ_MODIFY, +}; + +int register_sst_card(struct intel_sst_card_ops *card); +void unregister_sst_card(struct intel_sst_card_ops *card); +int lpe_mask_periphral_intr(enum lpe_periphral device); +int lpe_unmask_periphral_intr(enum lpe_periphral device); +int lpe_periphral_intr_status(enum lpe_periphral device, int *status); +#endif /* __INTEL_SST_H__ */ Index: linux-2.6.33/sound/pci/sst/intel_sst.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intel_sst.c @@ -0,0 +1,496 @@ +/* + * intel_sst.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This driver exposes the audio engine functionalities to the ALSA + * and middleware. + * + * This file contains all init functions + */ + +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <linux/firmware.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/intel_mid.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" + + +MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); +MODULE_DESCRIPTION("Intel (R) Moorestown Audio Engine Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SST_DRIVER_VERSION); + +struct intel_sst_drv *sst_drv_ctx; + +/* fops Routines */ +static const struct file_operations intel_sst_fops = { + .owner = THIS_MODULE, + .open = intel_sst_open, + .release = intel_sst_release, + .read = intel_sst_read, + .write = intel_sst_write, + .ioctl = intel_sst_ioctl, + .mmap = intel_sst_mmap, + .aio_read = intel_sst_aio_read, + .aio_write = intel_sst_aio_write, +}; + +/** +* intel_sst_interrupt - Interrupt service routine for SST +* @irq: irq number of interrupt +* @dev_id: 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; + + /* Interrupt arrived, check src */ + isr.full = readl(drv->shim + SST_ISRX); + + if (isr.part.busy_interrupt) { + header.full = readl(drv->shim + SST_IPCD); + 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 = readl(drv->shim + SST_IMRX); + imr.part.busy_interrupt = 1; + /* dummy register for shim workaround */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(imr.full, drv->shim + SST_IMRX); + return IRQ_HANDLED; + } else if (isr.part.done_interrupt) { + /* Clear done bit */ + header.full = readl(drv->shim + SST_IPCX); + header.part.done = 0; + /* dummy register for shim workaround */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(header.full, drv->shim + SST_IPCX); + /* write 1 to clear status register */; + isr.part.done_interrupt = 1; + /* dummy register for shim workaround */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(isr.full, drv->shim + SST_ISRX); + queue_work(sst_drv_ctx->post_msg_wq, + &sst_drv_ctx->ipc_post_msg.wq); + return IRQ_HANDLED; + } else + return IRQ_NONE; + +} + + +/* PCI Routines */ + +static struct pci_device_id intel_sst_ids[] = { + { 0x8086, 0x080A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, intel_sst_ids); + + +/* +* 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; + static struct mutex drv_ctx_lock; + mutex_init(&drv_ctx_lock); + + mutex_lock(&drv_ctx_lock); + if (sst_drv_ctx) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Only one sst +\ + handle is supported\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + mutex_unlock(&drv_ctx_lock); + return -EBUSY; + } + + sst_drv_ctx = kzalloc(sizeof(*sst_drv_ctx), GFP_KERNEL); + if (!sst_drv_ctx) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: intel_sst malloc fail\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + mutex_unlock(&drv_ctx_lock); + return -ENOMEM; + } + mutex_unlock(&drv_ctx_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->pb_streams = 0; + sst_drv_ctx->cp_streams = 0; + sst_drv_ctx->am_cnt = 0; + sst_drv_ctx->unique_id = 0; + + 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; + } + mutex_init(&sst_drv_ctx->list_lock); + + for (i = 1; i < MAX_NUM_STREAMS; i++) { + struct stream_info *stream = &sst_drv_ctx->streams[i]; + INIT_LIST_HEAD(&stream->bufs); + mutex_init(&stream->lock); + } + 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; + } + sst_drv_ctx->mmap_len -= (SST_MMAP_STEP * PAGE_SIZE); + if (sst_drv_ctx->mmap_len <= 0) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Couldnt get any +\ + mem...abort!!\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + ret = -ENOMEM; + goto free_process_reply_wq; + } + pr_debug("Failed...trying %d\n", sst_drv_ctx->mmap_len); + } + + /* Init the device */ + ret = pci_enable_device(pci); + if (ret) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: device cant be enabled\n", + get_seconds(), sst_get_time(), __func__ , __LINE__); + 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 = + (unsigned long) 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); + + sst_drv_ctx->sst_state = SST_UN_INIT; + /* 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 with /dev */ + ret = register_chrdev(INTEL_SST_MAJOR, SST_DRV_NAME, &intel_sst_fops); + if (ret) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: couldn't register +\ + device number\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto do_free_irq; + } + + sst_drv_ctx->lpe_stalled = 0; + pr_debug("...successfully done!!!\n"); + return ret; + +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); + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Probe failed with 0x%x \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, ret); + return ret; +} + +/** +* intel_sst_remove - PCI remove function +* @pci: PCI device structure +* +* This function is called by OS when a device is unloaded +* This frees the interrupt etc +*/ +static void __devexit intel_sst_remove(struct pci_dev *pci) +{ + pci_dev_put(sst_drv_ctx->pci); + sst_drv_ctx->sst_state = SST_UN_INIT; + unregister_chrdev(INTEL_SST_MAJOR, SST_DRV_NAME); + free_irq(pci->irq, sst_drv_ctx); + iounmap(sst_drv_ctx->dram); + iounmap(sst_drv_ctx->iram); + iounmap(sst_drv_ctx->mailbox); + iounmap(sst_drv_ctx->shim); + sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; + kfree(sst_drv_ctx->mmap_mem); + destroy_workqueue(sst_drv_ctx->process_reply_wq); + destroy_workqueue(sst_drv_ctx->process_msg_wq); + destroy_workqueue(sst_drv_ctx->post_msg_wq); + destroy_workqueue(sst_drv_ctx->mad_wq); + kfree(sst_drv_ctx); + pci_release_region(pci, 2); + pci_release_region(pci, 3); + pci_release_region(pci, 4); + pci_release_region(pci, 5); + pci_set_drvdata(pci, NULL); +} + +/* Power Management */ + +/** +* intel_sst_suspend - PCI suspend function +* @pci: PCI device structure +* @state: PM message +* +* This function is called by OS when a power event occurs +*/ +static int intel_sst_suspend(struct pci_dev *pci, pm_message_t state) +{ + int i; + pr_debug("intel_sst_suspend called\n"); + + /* Pause all running streams */ + for (i = 1; i < MAX_NUM_STREAMS; i++) { + if (sst_drv_ctx->streams[i].status == STREAM_RUNNING) { + sst_drv_ctx->active_streams[i] = true; + sst_pause_stream(i); + } else + sst_drv_ctx->active_streams[i] = false; + } + + pci_set_drvdata(pci, sst_drv_ctx); + + /* Send msg to FW FIXME */ + + /* Disable everything */ + /* free_irq(pci->irq, sst_drv_ctx); */ + pci_disable_device(pci); + pci_save_state(pci); + pci_set_power_state(pci, pci_choose_state(pci, state)); + return 0; +} + +/** +* intel_sst_resume - PCI resume function +* @pci: PCI device structure +* @state: PM message +* +* This function is called by OS when a power event occurs +*/ +static int intel_sst_resume(struct pci_dev *pci) +{ + int i; + + pr_debug("\nintel_sst_resume called\n"); + sst_drv_ctx = pci_get_drvdata(pci); + if (pci->irq) + pr_debug("irq %d \n", pci->irq); + + pci_set_power_state(pci, PCI_D0); + pci_restore_state(pci); + + /* ret = request_irq(pci->irq, intel_sst_interrupt, + IRQF_SHARED, "intel_sst_engine", sst_drv_ctx); + if (ret) { + return ret; + } */ + + /* Send msg to FW FIXME */ + + /* Start all paused streams */ + for (i = 1; i < MAX_NUM_STREAMS; i++) { + if (sst_drv_ctx->active_streams[i] == true) + sst_resume_stream(i); + } + return 0; +} + + +static struct pci_driver driver = { + .name = SST_DRV_NAME, + .id_table = intel_sst_ids, + .probe = intel_sst_probe, + .remove = __devexit_p(intel_sst_remove), + .suspend = intel_sst_suspend, + .resume = intel_sst_resume, +}; + +/** +* intel_sst_init - Module init function +* +* Registers with PCI +* Registers with /dev +*Init all data strutures +*/ +static int __init intel_sst_init(void) +{ + /* Init all variables, data structure etc....*/ + int ret = 0; + printk(KERN_INFO \ + "Time:%8ld::%8ld >> SST: %s %d INFO: ******** SST DRIVER +\ + loading.. Ver: %s\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, SST_DRIVER_VERSION); + + /* Register with PCI */ + ret = pci_register_driver(&driver); + if (ret) + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: PCI register failed \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + + sst_spi_mode_enable(); + return ret; +} + +/** +* intel_sst_exit - Module exit function +* +* Unregisters with PCI +* Unregisters with /dev +* Frees all data strutures +*/ +static void __exit intel_sst_exit(void) +{ + int i; + + for (i = 0; i < MAX_NUM_STREAMS; i++) + sst_free_stream(i); + + /* Send msg to FW TBD */ + pci_unregister_driver(&driver); + + flush_scheduled_work(); + pr_debug("...unloaded\n"); + return; +} + +module_init(intel_sst_init); +module_exit(intel_sst_exit); Index: linux-2.6.33/sound/pci/sst/intel_sst_dsp.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intel_sst_dsp.c @@ -0,0 +1,678 @@ +/* + * intel_sst_dsp.h - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This driver exposes the audio engine functionalities to the ALSA + * and middleware. + * + * This file contains all dsp controlling functions like firmware download, + * setting/resetting dsp cores, etc + */ +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <linux/firmware.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <asm/ipc_defs.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" + + +MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); +MODULE_DESCRIPTION("Intel (R) Moorestown Audio Engine Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SST_DRIVER_VERSION); + + +/** +* this function writes registers through SCU IPC +*/ +static int sst_scu_ipc_write(u32 addr, u32 value) +{ + int retval = 0, retry = 3; + struct ipc_reg_data ipc_reg = {0}; + + ipc_reg.address = addr; + ipc_reg.data = value; + ipc_reg.ioc = 1; + + while (retry) { + retval = mrst_ipc_write32(&ipc_reg); + if (!retval) + break; + retry--; + /* error */ + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: IPC write failed %x\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + } + return retval; +} + +/** +* this function reads registers through SCU IPC +*/ +static int sst_scu_ipc_read(u32 addr, u32 *value) +{ + int retval = 0, retry = 3; + struct ipc_reg_data ipc_reg = {0}; + + ipc_reg.address = addr; + ipc_reg.data = 0; + ipc_reg.ioc = 1; + + while (retry) { + retval = mrst_ipc_read32(&ipc_reg); + if (!retval) + break; + retry--; + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: IPC read failed %x\n ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + } + *value = ipc_reg.data; + pr_debug("The read value from the mrst_ipc is ::0x%08x\n", *value); + return retval; +} +/** +* Resetting SST DSP +*/ +static int intel_sst_reset_dsp(void) +{ + int retval; + unsigned int value = 0; + + retval = sst_scu_ipc_read(CHIP_REV_REG, &value); + if (retval) + return retval; + + /* A2-CHANGES */ + if (((value & CHIP_REV_ADDR) >> 3) == CHIP_REV_A1) { + sst_drv_ctx->chip_rev_id = CHIP_A1_50; + /* SCU FW Changes*/ + retval = sst_scu_ipc_write(AUD_SHIM_BASE_ADDR, + AUD_SHIM_RATIO_1_1); + } else { + if (DSP_CLOCK_SPEED == CLK_100MHZ) { + sst_drv_ctx->chip_rev_id = CHIP_A2_100; + /* SCU FW Changes*/ + retval = sst_scu_ipc_write(AUD_SHIM_BASE_ADDR, + AUD_SHIM_RATIO); + } else if (DSP_CLOCK_SPEED == CLK_50MHZ) { + sst_drv_ctx->chip_rev_id = CHIP_A2_50; + /* SCU FW Changes*/ + + retval = sst_scu_ipc_write(AUD_SHIM_BASE_ADDR, + AUD_SHIM_RATIO_1_1); + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Invalid +\ + clock speed\n ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EIO; + } + } /*else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Invalid +\ + chip revision Type\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EIO; + }*/ + pr_debug("Chip version is:: %d\n", sst_drv_ctx->chip_rev_id); + return retval; +} + +/** +* Start the SST DSP processor +*/ +static int sst_start(void) +{ + union config_status_reg csr; + int retval; + + csr.full = readl(sst_drv_ctx->shim + SST_CSR); + csr.part.bypass = 0; + retval = sst_scu_ipc_write( + sst_drv_ctx->shim_phy_add + SST_CSR, csr.full); + if (retval != 0) + dev_err(&sst_drv_ctx->pci->dev, + "Time:%8ld::%8ld >> SST: %s %d ERR: scu ipc write +\ + start failed %d ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + csr.full = readl(sst_drv_ctx->shim + SST_CSR); + csr.part.sst_reset = 0; + csr.part.run_stall = 0; + if ((sst_drv_ctx->chip_rev_id == CHIP_A2_50) || + (sst_drv_ctx->chip_rev_id == CHIP_A2_100)) { + csr.part.strb_cntr_rst = 1; + if (sst_drv_ctx->chip_rev_id == CHIP_A2_100) + csr.part.sst_clk = 1; + } + + pr_debug("Setting SST to execute 0x%x \n", csr.full); + + return sst_scu_ipc_write( + sst_drv_ctx->shim_phy_add + SST_CSR, csr.full); +} + +/** +* Parse modules that need to be placed in SST IRAM and DRAM +*/ +static int sst_parse_module(struct fw_module_header *module) +{ + struct dma_block_info *block; + u32 count; + void __iomem *ram; + + pr_debug("module sign=%s sz=0x%x blks=0x%x typ=0x%x ep=0x%x sz=0x%x\n", + module->signature, module->mod_size, + module->blocks, module->type, + module->entry_point, sizeof(*module)); + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->blocks; count++) { + if (block->size <= 0) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: block size invalid\n ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + switch (block->type) { + case SST_IRAM: + ram = sst_drv_ctx->iram; + break; + case SST_DRAM: + ram = sst_drv_ctx->dram; + break; + default: + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR:wrng ramtyp0x%xinblock0x%x\n", \ + get_seconds(), sst_get_time(), __func__, __LINE__, \ + block->type, count); + return -EINVAL; + } + memcpy_toio(ram + block->ram_offset, + (void *)block + sizeof(*block), block->size); + block = (void *)block + sizeof(*block) + block->size; + } + return 0; +} + +/** +* sst_parse_fw_image - FW parse and load +* This function is called to parse and download the FW image +*/ +static int sst_parse_fw_image(const struct firmware *sst_fw) +{ + struct fw_header *header; + u32 count; + int ret_val; + struct fw_module_header *module; + + BUG_ON(!sst_fw); + + /* Read the header information from the data pointer */ + header = (struct fw_header *)sst_fw->data; + + /* verify FW */ + if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) || + (sst_fw->size != header->file_size + sizeof(*header))) { + /* Invalid FW signature */ + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: InvalidFW sgn/filesiz mismtch\n ",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + pr_debug("header sign=%s size=0x%x modules=0x%x fmt=0x%x size=0x%x\n", + header->signature, header->file_size, header->modules, + header->file_format, sizeof(*header)); + module = (void *)sst_fw->data + sizeof(*header); + for (count = 0; count < header->modules; count++) { + /* module */ + ret_val = sst_parse_module(module); + if (ret_val) + return ret_val; + module = (void *)module + sizeof(*module) + module->mod_size ; + } + + pr_debug("done....\n"); + return 0; +} + +/** +* sst_load_fw - function to reset FW +* @fw: Pointer to loaded FW +* This function is called when the FW is loaded +*/ +int sst_load_fw(const struct firmware *fw, void *context) +{ + int ret_val; + + pr_debug("called \n"); + BUG_ON(!fw); + + /* TBD: Checksum, tamper check etc */ + ret_val = intel_sst_reset_dsp(); + if (ret_val) + return ret_val; + /* putting the sst state to init */ + sst_drv_ctx->sst_state = SST_UN_INIT; + + ret_val = sst_parse_fw_image(fw); + if (ret_val) + return ret_val; + + sst_drv_ctx->sst_state = SST_FW_LOADED; + /* 7. ask scu to reset the bypass bits */ + /* 8.bring sst out of reset */ + ret_val = sst_start(); + if (ret_val) + return ret_val; + + pr_debug("...successful!!!\n"); + return ret_val; +} + +/** +* 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, 0); + 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)); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_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; + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Prep codec downloaded failed %d\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + return -EIO; + } + pr_debug("FW responded, ready for download now...\n"); + /* downloading on success */ + sst_drv_ctx->sst_state = SST_FW_LOADED; + csr.full = readl(sst_drv_ctx->shim + SST_CSR); + pr_debug("CSR reg 0x%x \n", csr.full); + csr.part.run_stall = 1; + pr_debug("HALT CSR reg setting to 0x%x \n", csr.full); + retval = sst_scu_ipc_write( + sst_drv_ctx->shim_phy_add + SST_CSR, csr.full); + if (retval) { + /* error */ + sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: IPC failed to Halt SST 0x%x\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + return -EAGAIN; + } + csr.full = readl(sst_drv_ctx->shim + SST_CSR); + csr.part.bypass = 0x7; + pr_debug("Bypass CSR reg setting to 0x%x \n", csr.full); + retval = sst_scu_ipc_write( + sst_drv_ctx->shim_phy_add + SST_CSR, csr.full); + if (retval) { + /* error */ + sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: IPC failed to Bypass SST 0x%x\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + csr.part.bypass = 0x0; + /* bring LPE out of run stall */ + /* send error mesages to FW- TBD FIXME */ + csr.part.run_stall = 0x0; + pr_debug("Bypass CSR reg setting to 0x%x \n", csr.full); + retval = sst_scu_ipc_write(sst_drv_ctx->shim_phy_add + SST_CSR, + csr.full); + if (retval) { + /* prepare to download firmware again + for the next time TBD FIXME + sst_drv_ctx->sst_state = SST_UN_INIT;*/ + } + return -EAGAIN; + } + sst_parse_fw_image(fw_lib); + + /* set the FW to running again */ + csr.full = readl(sst_drv_ctx->shim + SST_CSR); + csr.part.bypass = 0x0; + pr_debug("Bypass CSR reg clearing to 0x%x \n", csr.full); + retval = sst_scu_ipc_write( + sst_drv_ctx->shim_phy_add + SST_CSR, csr.full); + if (retval) { + sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: BypassCSR regclear failed 0x%x\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + /* bring LPE out of run stall */ + /* send error mesages to FW- TBD FIXME */ + csr.part.run_stall = 0x0; + pr_debug("Bypass CSR reg setting to 0x%x \n", csr.full); + retval = sst_scu_ipc_write(sst_drv_ctx->shim_phy_add + SST_CSR, + csr.full); + if (retval) { + /* prepare to download firmware again + for the next time TBD FIXME + sst_drv_ctx->sst_state = SST_UN_INIT;*/ + } + return -EAGAIN; + } + + csr.full = readl(sst_drv_ctx->shim + SST_CSR); + csr.part.run_stall = 0; + pr_debug("Stalll CSR reg clearing to 0x%x \n", csr.full); + retval = sst_scu_ipc_write( + sst_drv_ctx->shim_phy_add + SST_CSR, csr.full); + if (retval) { + sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Stall CSR reg clear failed 0x%x \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + if (retval) { + /* prepare to download firmware again + for the next time TBD FIXME + sst_drv_ctx->sst_state = SST_UN_INIT;*/ + } + return -EAGAIN; + } + /* 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, 0); + 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)); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + pr_debug("Waiting for FW to respond on 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 */ + sst_drv_ctx->sst_state = SST_FW_RUNNING; + /* shouldnt we set it to error state??? TBD */ + sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; + return -EIO; + } + + pr_debug("FW responded sucess on Download complete \n"); + sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; + sst_drv_ctx->sst_state = SST_FW_RUNNING; + return 0; + +} + +/** +* This function is called befoer downloading the codec/postprocessing +* library is set for download to SST DSP +*/ +static int sst_validate_library(const struct firmware *fw_lib, + struct lib_slot_info *slot, + u32 *entry_point) +{ + struct fw_header *header; + struct fw_module_header *module; + struct dma_block_info *block; + unsigned int n_blk, isize = 0, dsize = 0; + int err = 0; + + header = (struct fw_header *)fw_lib->data; + if (header->modules != 1) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Module no mismatch found\n ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + err = -EINVAL; + goto exit; + } + module = (void *)fw_lib->data + sizeof(*header); + *entry_point = module->entry_point; + pr_debug("Module entry point 0x%x \n", *entry_point); + pr_debug("Module Sign %s, Size 0x%x, Blocks 0x%x Type 0x%x \n", + module->signature, module->mod_size, + module->blocks, module->type); + + block = (void *)module + sizeof(*module); + for (n_blk = 0; n_blk < module->blocks; n_blk++) { + switch (block->type) { + case SST_IRAM: + isize += block->size; + break; + case SST_DRAM: + dsize += block->size; + break; + default: + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Invalid blk type for 0x%x\n ",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, n_blk); + err = -EINVAL; + goto exit; + } + block = (void *)block + sizeof(*block) + block->size; + } + if (isize > slot->iram_size || dsize > slot->dram_size) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: library exceeds size allocated \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + err = -EINVAL; + goto exit; + } else + pr_debug("Library is safe for download...\n"); + + pr_debug("iram 0x%x, dram 0x%x, allowed iram 0x%x, allowed dram 0x%x\n", + isize, dsize, slot->iram_size, slot->dram_size); +exit: + return err; + +} + +/** +* This function is called when FW requests for a particular libary download +* This function prepares the library to download +*/ +int sst_load_library(struct snd_sst_lib_download *lib, u8 ops, u32 pvt_id) +{ + char buf[20]; + const char *type, *dir; + int len = 0, error = 0; + u32 entry_point; + const struct firmware *fw_lib; + struct snd_sst_lib_download_info dload_info = {{{0},},}; + + memset(buf, 0, sizeof(buf)); + + pr_debug("Lib Type 0x%x, Slot 0x%x, ops 0x%x \n", + lib->lib_info.lib_type, lib->slot_info.slot_num, ops); + pr_debug("Version 0x%x, name %s, caps 0x%x media type 0x%x \n", + lib->lib_info.lib_version, lib->lib_info.lib_name, + lib->lib_info.lib_caps, lib->lib_info.media_type); + + pr_debug("IRAM Size 0x%x, offset 0x%x, DRAM Size 0x%x, offset 0x%x \n", + lib->slot_info.iram_size, lib->slot_info.iram_offset, + lib->slot_info.dram_size, lib->slot_info.dram_offset); + + switch (lib->lib_info.lib_type) { + case SST_CODEC_TYPE_MP3: + type = "mp3_"; + break; + case SST_CODEC_TYPE_AAC: + type = "aac_"; + break; + case SST_CODEC_TYPE_AACP: + type = "aac_v1_"; + break; + case SST_CODEC_TYPE_eAACP: + type = "aac_v2_"; + break; + case SST_CODEC_TYPE_WMA9: + type = "wma9_"; + break; + default: + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Invalid codec type \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + error = -EINVAL; + goto wake; + } + + if (ops == STREAM_OPS_CAPTURE) + dir = "enc_"; + else + dir = "dec_"; + strncpy(buf, type, strlen(type)); + strncpy(buf + strlen(type), dir, strlen(dir)); + len = strlen(type) + strlen(dir); + len += snprintf(buf + len, sizeof(buf) - len, "%d", + lib->slot_info.slot_num); + len += snprintf(buf + len, sizeof(buf) - len, ".bin"); + + pr_debug("Requesting %s \n", buf); + + error = request_firmware(&fw_lib, buf, &sst_drv_ctx->pci->dev); + if (error) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: library load failed %d \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, error); + goto wake; + } + error = sst_validate_library(fw_lib, &lib->slot_info, &entry_point); + if (error) + goto wake_free; + + lib->mod_entry_pt = entry_point; + memcpy(&dload_info.dload_lib, lib, sizeof(*lib)); + error = sst_download_library(fw_lib, &dload_info); + if (error) + goto wake_free; + + /* lib is downloaded and init send alloc again */ + pr_debug("Library is downloaded now... \n"); +wake_free: + /* sst_wake_up_alloc_block(sst_drv_ctx, pvt_id, error, NULL); */ + release_firmware(fw_lib); +wake: + return error; +} + +/* This Function set the bit banging*/ +int sst_spi_mode_enable() +{ + + void __iomem *logical_ptr_to_bang; + u32 regbase = SPI_MODE_ENABLE_BASE_ADDR, range = 0x38; + u32 data; + u32 mask = 0x400000; + int retval; + int i = 0; + + + logical_ptr_to_bang = ioremap_nocache(regbase, range); + if (!logical_ptr_to_bang) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: SSP0 bit bang -IOREMAP Failed \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -1; + } + + /* spi mode enable */ + iowrite32(0x0000000f, logical_ptr_to_bang); + iowrite32(0x33301dc3, logical_ptr_to_bang + 0x4); + iowrite32(0x02010007, logical_ptr_to_bang + 0x2c); + iowrite32(0x00000000, logical_ptr_to_bang + 0x30); + iowrite32(0x00000000, logical_ptr_to_bang + 0x34); + iowrite32(0x0000008f, logical_ptr_to_bang); + + + retval = sst_scu_ipc_write(0xff12b004, 0x3); + retval = sst_scu_ipc_write(0xff12b000, 0x01070034); + retval = sst_scu_ipc_write(0xff12b004, 0x99); + retval = sst_scu_ipc_write(0xff12b000, 0x01070038); + + data = ioread32(logical_ptr_to_bang+0x8); + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: SSP0 bit bang SSCR val = 0x%08x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, data); + data = data & mask; + while (data == mask) { + retval = sst_scu_ipc_write(0xff12b004, 0x3); + retval = sst_scu_ipc_write(0xff12b000, 0x01070034); + retval = sst_scu_ipc_write(0xff12b004, 0x2); + retval = sst_scu_ipc_write(0xff12b000, 0x01070034); + data = ioread32(logical_ptr_to_bang+0x8); + data = data & mask; + i++; + } + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: SSP0 bit bang while loop counter= %4d \n ",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, i); + retval = sst_scu_ipc_write(0xff12b004, 0x0); + retval = sst_scu_ipc_write(0xff12b000, 0x01070038); + + return retval; +} Index: linux-2.6.33/sound/pci/sst/intel_sst_common.h =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intel_sst_common.h @@ -0,0 +1,510 @@ +#ifndef __INTEL_SST_COMMON_H__ +#define __INTEL_SST_COMMON_H__ +/* + * intel_sst_common.h - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corporation + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Common private declarations for SST + */ +#include <linux/time.h> +#include <linux/intel_mid.h> + +/* #define SND_LOOP_TEST */ + +#define SST_DRIVER_VERSION "0.04.019" +#define SST_VERSION_NUM 0x0419 + +/* driver names */ +#define SST_DRV_NAME "intel_sst_driver" +#define SST_FW_STD_FILENAME "fw_sst.bin" + + +enum sst_states { + SST_FW_LOADED = 1, + SST_FW_RUNNING, + SST_UN_INIT, + SST_ERROR, +}; + +#define MAX_ACTIVE_STREAM 3 +#define MAX_ENC_STREAM 1 +#define MAX_AM_HANDLES 1 +#define ALLOC_TIMEOUT 5000 +/* SST numbers */ +#define SST_BLOCK_TIMEOUT 5000 +#define TARGET_DEV_BLOCK_TIMEOUT 5000 + +/* FIXME */ +#define INTEL_SST_MAJOR 255 +#define BLOCK_UNINIT -1 +/* Chip revision ID */ + +#define CHIP_A1_50 0x01 +#define CHIP_A2_50 0x02 +#define CHIP_A2_100 0x03 + +#define DSP_CLOCK_SPEED 100 /* 50: 50MHz, 100: 100MHz */ + +/* SST register map */ +#define SST_CSR 0x00 +#define SST_PISR 0x08 +#define SST_PIMR 0x10 +#define SST_ISRX 0x18 +#define SST_IMRX 0x28 +#define SST_IPCX 0x38 /* IPC IA-SST */ +#define SST_IPCD 0x40 /* IPC SST-IA */ +#define SST_ISRD 0x20 /* dummy register for shim workaround */ +#define SST_SHIM_SIZE 0X44 + +#define SPI_MODE_ENABLE_BASE_ADDR 0xffae4000 +#define FW_SIGNATURE_SIZE 4 + +/* PMIC and SST hardware states */ +enum sst_mad_states { + SND_MAD_UN_INIT = 0, + SND_MAD_INIT_DONE, +}; + +/* stream states */ +enum sst_stream_states { + STREAM_UN_INIT = 0, /* Freed/Not used stream */ + STREAM_RUNNING = 1, /* Running */ + STREAM_PAUSED = 2, /* Paused stream */ + STREAM_DECODE = 4, /* stream is in decoding only state */ + STREAM_INIT = 5, /* stream init, waiting for data */ +}; + + +enum sst_ram_type{ + SST_IRAM = 1, + SST_DRAM = 2, +}; +/* SST shim registers to structure mapping */ +union config_status_reg { + struct { + u32 rsvd0:1; + u32 sst_reset:1; + u32 hw_rsvd:3; + u32 sst_clk:2; + u32 bypass:3; + u32 run_stall:1; + u32 rsvd1:2; + u32 strb_cntr_rst:1; + u32 rsvd:18; + } part; + u32 full; +}; + +union interrupt_reg { + struct { + u32 done_interrupt:1; + u32 busy_interrupt:1; + u32 rsvd:30; + } part; + u32 full; +}; + +union sst_pisr_reg { + struct { + u32 pssp0:1; + u32 pssp1:1; + u32 rsvd0:3; + u32 dmac:1; + u32 rsvd1:26; + } part; + u32 full; +}; + +union sst_pimr_reg { + struct { + u32 ssp0:1; + u32 ssp1:1; + u32 rsvd0:3; + u32 dmac:1; + u32 rsvd1:10; + u32 ssp0_sc:1; + u32 ssp1_sc:1; + u32 rsvd2:3; + u32 dmac_sc:1; + u32 rsvd3:10; + } part; + u32 full; +}; + + +struct sst_stream_bufs { + struct list_head node; + u32 size; + const char *addr; + u32 data_copied; + bool in_use; + u32 offset; +}; + +struct snd_sst_user_cap_list { + unsigned int iov_index; /* index of iov */ + unsigned long iov_offset; /* offset in iov */ + unsigned long offset; /* offset in kmem */ + unsigned long size; /* size copied */ + struct list_head node; +}; +/* +This structure is used to block a user/fw data call to another +fw/user call +*/ +struct sst_block { + bool condition; /* condition for blocking check */ + int ret_code; /* ret code when block is released */ + void *data; /* data to be appsed for block if any */ + bool on; +}; + +enum snd_sst_buf_type { + SST_BUF_USER_STATIC = 1, + SST_BUF_USER_DYNAMIC, + SST_BUF_MMAP_STATIC, + SST_BUF_MMAP_DYNAMIC, +}; +enum snd_src { + SST_DRV = 1, + MAD_DRV = 2 +}; +/* +structure that holds the stream information +*/ +struct stream_info { + unsigned int status; + unsigned int prev; + u8 codec; + unsigned int sst_id; + unsigned int ops; + struct list_head bufs; + struct mutex lock; /* mutex */ + bool mmapped; + unsigned int sg_index; /* current buf Index */ + unsigned char *cur_ptr; /* Current static bufs */ + struct snd_sst_buf_entry *buf_entry; + struct sst_block data_blk; /* stream ops block */ + struct sst_block ctrl_blk; /* stream control cmd block */ + enum snd_sst_buf_type buf_type; + void *pcm_substream; + void (*period_elapsed) (void *pcm_substream); + unsigned int sfreq; + void *decode_ibuf, *decode_obuf; + unsigned int decode_isize, decode_osize; + unsigned int idecode_alloc; + unsigned int need_draining; + unsigned int str_type; + u32 curr_bytes; + u32 cumm_bytes; + u32 src; /* hack to remove */ +}; + + + +/* +this structure is used for blocking the user's alloc calls to +fw's response to alloc calls +*/ +struct stream_alloc_block { + int sst_id; /* session id of blocked stream */ + struct sst_block ops_block; /* ops block struture */ +}; + +#define SST_FW_SIGN "$SST" +#define SST_FW_LIB_SIGN "$LIB" + +/* FW file headers */ +struct fw_header { + unsigned char signature[FW_SIGNATURE_SIZE]; /* FW signature */ + u32 file_size; /* size of fw minus this header */ + u32 modules; /* # of modules */ + u32 file_format; /* version of header format */ + u32 reserved[4]; +}; + +struct fw_module_header { + unsigned char signature[FW_SIGNATURE_SIZE]; /* module signature */ + u32 mod_size; /* size of module */ + u32 blocks; /* # of blocks */ + u32 type; /* codec type, pp lib */ + u32 entry_point; +}; + +struct dma_block_info { + enum sst_ram_type type; /* IRAM/DRAM */ + u32 size; /* Bytes */ + u32 ram_offset; /* Offset in I/DRAM */ + u32 rsvd; /* Reserved field */ +}; + +struct ioctl_pvt_data { + int str_id; + int pvt_id; +}; + +struct sst_ipc_msg_wq { + union ipc_header header; + char mailbox[SST_MAILBOX_SIZE]; + struct work_struct wq; +}; + +struct mad_ops_wq { + int stream_id; + enum sst_controls control_op; + struct work_struct wq; + +}; + +#define SST_MMAP_PAGES (640*1024 / PAGE_SIZE) +#define SST_MMAP_STEP (40*1024 / PAGE_SIZE) + +/* driver ops */ +struct intel_sst_drv { + bool pmic_state; + int pmic_vendor; + int sst_state; + int chip_rev_id; + void __iomem *shim; + void __iomem *mailbox; + void __iomem *iram; + void __iomem *dram; + unsigned int shim_phy_add; + struct list_head ipc_dispatch_list; + struct work_struct ipc_post_msg_wq; + struct sst_ipc_msg_wq ipc_process_msg; + struct sst_ipc_msg_wq ipc_process_reply; + struct sst_ipc_msg_wq ipc_post_msg; + struct mad_ops_wq mad_ops; + wait_queue_head_t wait_queue; + struct workqueue_struct *mad_wq; + struct workqueue_struct *post_msg_wq; + struct workqueue_struct *process_msg_wq; + struct workqueue_struct *process_reply_wq; + + struct stream_info streams[MAX_NUM_STREAMS]; + struct stream_alloc_block alloc_block[MAX_ACTIVE_STREAM]; + struct sst_block tgt_dev_blk, fw_info_blk, + vol_info_blk, mute_info_blk; + struct mutex list_lock;/* mutex for IPC list locking */ + struct snd_pmic_ops *scard_ops; + struct pci_dev *pci; + int active_streams[MAX_NUM_STREAMS]; + void *mmap_mem; + unsigned int mmap_len; + unsigned int unique_id; + unsigned int stream_cnt; /* total streams */ + unsigned int encoded_cnt; /* enocded streams only */ + unsigned int am_cnt; + unsigned int pb_streams; /* pb streams active */ + unsigned int cp_streams; /* cp streams active */ + unsigned int lpe_stalled; /* LPE is stalled or not */ +}; + +extern struct intel_sst_drv *sst_drv_ctx; + +/* register definitions */ +/*SCU FW Changes*/ +/*#define AUD_CLK_ADDR 0xff11d83c +#define AUD_CLK_DISABLE 0x80008008 +#define AUD_CLK_50MHZ 0x80008301 +#define AUD_CLK_RATIO_1_2 0x80000301 +#define AUD_CLK_RATIO_8008 0x80008008 +#define AUD_CLK_RATIO_8101 0x80008101 +#define AUD_CLK_RATIO_0101 0x80000101 +#define AUD_SYS_ADDR 0xff11d118 +#define AUD_SYS_RESET 0x7ffffcff +#define AUD_SYS_SET 0x7fffffff*/ +#define AUD_SHIM_BASE_ADDR 0xffae8000 +#define AUD_SHIM_RATIO_1_1 0x382 +#define AUD_SHIM_RATIO 0x3a2 +/*SCU FW Changes*/ +/*#define AUD_CLK_200 0xff11d200 +#define AUD_CLK_204 0xff11d204 +#define AUD_INIT_VAL 0x0*/ +#define CHIP_REV_REG 0xff108000 +#define CHIP_REV_ADDR 0x78 +#define CHIP_REV_A1 0x0 +#define CHIP_REV_A2 0x3 +#define CLK_50MHZ 50 +#define CLK_100MHZ 100 + +/* misc definitions */ +#define FW_DWNL_ID 0xFF +#define LOOP1 0x11111111 +#define LOOP2 0x22222222 +#define LOOP3 0x33333333 +#define LOOP4 0x44444444 + +/* NOTE: status will +ve for good cases and -ve for error ones */ +#define MAX_STREAM_FIELD 255 + +int sst_alloc_stream(char *params, unsigned int stream_ops, u8 codec, + unsigned int session_id); +int sst_alloc_stream_response(unsigned int str_id, + struct snd_sst_str_type *type); +int sst_pause_stream(int id); +int sst_resume_stream(int id); +int sst_drop_stream(int id); +int sst_free_stream(int id); +int sst_play_frame(int streamID); +int sst_capture_frame(int streamID); +int sst_set_stream_param(int streamID, struct snd_sst_params *str_param); +int sst_target_device_select(struct snd_sst_target_device *target_device); +int sst_decode(int str_id, struct snd_sst_dbufs *dbufs); +int sst_get_decoded_bytes(int str_id, unsigned long long *bytes); +int sst_get_fw_info(struct snd_sst_fw_info *info); +int sst_get_stream_params(int str_id, + struct snd_sst_get_stream_params *get_params); +int sst_drain_stream(int str_id); +int sst_get_vol(struct snd_sst_vol *set_vol); +int sst_set_vol(struct snd_sst_vol *set_vol); +int sst_set_mute(struct snd_sst_mute *set_mute); + +void sst_post_message(struct work_struct *work); +void sst_process_message(struct work_struct *work); +void sst_process_reply(struct work_struct *work); +void sst_process_mad_ops(struct work_struct *work); +void sst_process_mad_jack_detection(struct work_struct *work); + +int intel_sst_ioctl(struct inode *i_node, struct file *file_ptr, + unsigned int cmd, unsigned long arg); +int intel_sst_open(struct inode *i_node, struct file *file_ptr); +int intel_sst_release(struct inode *i_node, struct file *file_ptr); +int intel_sst_read(struct file *file_ptr, char __user *buf, + size_t count, loff_t *ppos); +int intel_sst_write(struct file *file_ptr, const char __user *buf, + size_t count, loff_t *ppos); +int intel_sst_mmap(struct file *fp, struct vm_area_struct *vma); +ssize_t intel_sst_aio_write(struct kiocb *kiocb, const struct iovec *iov, + unsigned long nr_segs, loff_t offset); +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_load_library(struct snd_sst_lib_download *lib, u8 ops, u32 pvt_id); +int sst_spi_mode_enable(void); +int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx); + +void sst_print_hex(unsigned char *buf, unsigned int size); +int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block); +int sst_wait_interruptible_timeout(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block, int timeout); +int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, + struct stream_alloc_block *block); +int sst_create_large_msg(struct ipc_post **arg); +int sst_create_short_msg(struct ipc_post **arg); +void sst_print_params(struct snd_sst_params *str_params); +void sst_wake_up_alloc_block(struct intel_sst_drv *sst_drv_ctx, + u8 sst_id, int status, void *data); + +/** +* this function is an inline function that sets the headers before +* sending a message +*/ +static inline void sst_fill_header(union ipc_header *header, + int msg, int large, int strID) +{ + header->part.msg_id = msg; + header->part.str_id = strID; + header->part.large = large; + header->part.done = 0; + header->part.busy = 1; + header->part.data = 0; +} + +/** +* this inline function assigns a private id for calls that dont have stream +* context yet +*/ +static inline unsigned int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx) +{ + sst_drv_ctx->unique_id++; + if (sst_drv_ctx->unique_id >= MAX_NUM_STREAMS) + sst_drv_ctx->unique_id = 1; + return sst_drv_ctx->unique_id; +} + +/** +* this function initialzes stream context +*/ +static inline void sst_init_stream(struct stream_info *stream, + int codec, int str_type, int sst_id, int ops) +{ + stream->status = STREAM_INIT; + stream->prev = STREAM_UN_INIT; + stream->codec = codec; + stream->sst_id = sst_id; + stream->str_type = str_type; + stream->ops = ops; + stream->data_blk.on = false; + stream->data_blk.condition = false; + stream->data_blk.ret_code = 0; + stream->data_blk.data = NULL; + stream->ctrl_blk.on = false; + stream->ctrl_blk.condition = false; + stream->ctrl_blk.ret_code = 0; + stream->ctrl_blk.data = NULL; + stream->need_draining = false; + stream->decode_ibuf = NULL; + stream->decode_isize = 0; + stream->mmapped = false; +} + +/** +* this function resets the stream contexts +*/ +static inline void sst_clean_stream(struct stream_info *stream) +{ + struct sst_stream_bufs *bufs = NULL, *_bufs; + stream->status = STREAM_UN_INIT; + stream->prev = STREAM_UN_INIT; + mutex_lock(&stream->lock); + list_for_each_entry_safe(bufs, _bufs, &stream->bufs, node) { + list_del(&bufs->node); + kfree(bufs); + } + mutex_unlock(&stream->lock); + kfree(stream->decode_ibuf); +} + +/** +* this function generates events for OSPM +*/ +static inline int sst_ospm_send_event(int event) +{ + return ospm_generate_netlink_event(AUDIO_SUBSYTEM_ID, event); +} + +/** +* this function validates the stream id +*/ +static inline int sst_validate_strid(int str_id) +{ + if (str_id <= 0 || str_id >= MAX_NUM_STREAMS) { + return -EINVAL; + } else + return 0; +} + +#endif /* __INTEL_SST_COMMON_H__ */ Index: linux-2.6.33/sound/pci/sst/intel_sst_pvt.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intel_sst_pvt.c @@ -0,0 +1,321 @@ +/* + * intel_sst_pvt.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This driver exposes the audio engine functionalities to the ALSA + * and middleware. + * + * This file contains all private functions + */ + +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <linux/firmware.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" + +/** +* this function assigns a block for the calls that dont have stream context yet +* the blocks are used for waiting on Firmware's response for any operation +*/ +int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx) +{ + int i; + + for (i = 0; i < MAX_ACTIVE_STREAM; i++) { + if (sst_drv_ctx->alloc_block[i].sst_id == BLOCK_UNINIT) { + sst_drv_ctx->alloc_block[i].ops_block.condition = false; + sst_drv_ctx->alloc_block[i].ops_block.ret_code = 0; + sst_drv_ctx->alloc_block[i].sst_id = 0; + break; + } + } + if (i == MAX_ACTIVE_STREAM) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: max alloc_stream reached", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + i = -EBUSY; /* active stream limit reached */ + } + return i; +} + +/** +* this function is a debug function that is used to print contents of a buffer +*/ +void sst_print_hex(unsigned char *buf, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < size; i++) { + pr_debug("%02x ", buf[i]); + if ((i != 0) && ((i % 8) == 0)) + pr_debug("\n"); + } +} +/** +* this function waits without a timeout (and is interruptable) for a +* given block event +*/ +int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block) +{ + int retval = 0; + + if (!wait_event_interruptible(sst_drv_ctx->wait_queue, + block->condition)) { + /* event wake */ + if (block->ret_code < 0) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream failed %d\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, block->ret_code); + retval = -EBUSY; + } else { + pr_debug("event up\n"); + retval = 0; + } + } else { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: signal interrupted\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + retval = -EINTR; + } + return retval; + +} + +/** +* this function waits with a timeout value (and is interruptle) on a +* given block event +*/ +int sst_wait_interruptible_timeout( + struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block, int timeout) +{ + int retval = 0; + + pr_debug("waiting....\n"); + if (wait_event_interruptible_timeout(sst_drv_ctx->wait_queue, + block->condition, + msecs_to_jiffies(timeout))) { + if (block->ret_code < 0) + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream failed %d\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, block->ret_code); + else + pr_debug("event up\n"); + retval = block->ret_code; + } else { + block->on = false; + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: timeout occured...\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + /* settign firmware state as uninit so that the + firmware will get redownloaded on next request + this is because firmare not responding for 5 sec + is equalant to some unrecoverable error of FW + sst_drv_ctx->sst_state = SST_UN_INIT;*/ + retval = -EBUSY; + } + return retval; + +} + +/** +* this function waits with on a given block event +*/ +int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, + struct stream_alloc_block *block) +{ + int retval = 0; + + /* NOTE: + Observed that FW processes the alloc msg and replies even + before the alloc thread has finished execution */ + pr_debug("waiting for %x, condition %x \n", block->sst_id, + block->ops_block.condition); + if (wait_event_interruptible_timeout(sst_drv_ctx->wait_queue, + block->ops_block.condition, + msecs_to_jiffies(SST_BLOCK_TIMEOUT))) { + /* event wake */ + pr_debug("Event wake ... %x \n", block->ops_block.condition); + pr_debug("message ret: %d\n", block->ops_block.ret_code); + retval = block->ops_block.ret_code; + } else { + block->ops_block.on = false; + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Wait timed-out %x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, block->ops_block.condition); + /* settign firmware state as uninit so that the + firmware will get redownloaded on next request + this is because firmare not responding for 5 sec + is equalant to some unrecoverable error of FW + sst_drv_ctx->sst_state = SST_UN_INIT;*/ + retval = -EBUSY; + } + return retval; + +} + +/** +* this function allocats structures to send a large message to the firmware +*/ +int sst_create_large_msg(struct ipc_post **arg) +{ + struct ipc_post *msg; + + msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); + if (!msg) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: kzalloc msg failed \n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + + msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); + if (!msg->mailbox_data) { + kfree(msg); + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: kzalloc mailbox_data failed" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + }; + *arg = msg; + return 0; +} + +/** +* this function allocats structures to send a short message to the firmware +*/ +int sst_create_short_msg(struct ipc_post **arg) +{ + struct ipc_post *msg; + + msg = kzalloc(sizeof(*msg), GFP_ATOMIC); + if (!msg) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: kzalloc msg failed \n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + msg->mailbox_data = NULL; + *arg = msg; + return 0; +} + +/** +* this function is a debug funtion to print the stream parameters +*/ +void sst_print_params(struct snd_sst_params *str_params) +{ + switch (str_params->codec) { + case SST_CODEC_TYPE_PCM: + pr_debug("pcm \n"); + pr_debug("chan=%d, sfreq = %d, wd_sz = %d \ + brate = %d framesize = %d \ + samples_per_frame = %d \ + period_cnt = %d\n", + str_params->sparams.uc.pcm_params.num_chan, + str_params->sparams.uc.pcm_params.sfreq, + str_params->sparams.uc.pcm_params.pcm_wd_sz, + str_params->sparams.uc.pcm_params.brate, + str_params->sparams.uc.pcm_params.frame_size, + str_params->sparams.uc.pcm_params.samples_per_frame, + str_params->sparams.uc.pcm_params.period_count); + break; + + case SST_CODEC_TYPE_MP3: + pr_debug("mp3 \n"); + pr_debug("chan=%d, brate=%d, sfreq = %d, wd_sz = %d\n", + str_params->sparams.uc.mp3_params.num_chan, + str_params->sparams.uc.mp3_params.brate, + str_params->sparams.uc.mp3_params.sfreq, + str_params->sparams.uc.mp3_params.pcm_wd_sz); + break; + + case SST_CODEC_TYPE_AAC: + pr_debug("aac \n"); + pr_debug("chan=%d, brate=%d, sfreq = %d, wd_sz = %d,asrate=%d\n", + str_params->sparams. uc.aac_params.num_chan, + str_params->sparams.uc.aac_params.brate, + str_params->sparams.uc.aac_params.sfreq, + str_params->sparams.uc.aac_params.pcm_wd_sz, + str_params->sparams.uc.aac_params.aac_srate); + pr_debug("mpgid=%d profile=%d, aot = %d\n", + str_params->sparams.uc.aac_params.mpg_id, + str_params->sparams.uc.aac_params.aac_profile, + str_params->sparams.uc.aac_params.aot); + break; + case SST_CODEC_TYPE_WMA9: + pr_debug("wma type \n"); + pr_debug("chan=%d, brate=%d, sfreq = %d, wd_sz = %d, tag=%d\n", + str_params->sparams. uc.wma_params.num_chan, + str_params->sparams.uc.wma_params.brate, + str_params->sparams.uc.wma_params.sfreq, + str_params->sparams.uc.wma_params.pcm_wd_sz, + str_params->sparams.uc.wma_params.format_tag); + pr_debug("mask=%d, b align=%d, enc opt =%d, op align =%d\n", + str_params->sparams.uc.wma_params.channel_mask, + str_params->sparams.uc.wma_params.block_align, + str_params->sparams.uc.wma_params.wma_encode_opt, + str_params->sparams.uc.wma_params.op_align); + break; + default: + pr_debug("other codec 0x%x\n", str_params->codec); + } +} + +/** +* this function wakes up a sleeping block event based on the response +*/ +void sst_wake_up_alloc_block(struct intel_sst_drv *sst_drv_ctx, + u8 sst_id, int status, void *data) +{ + int i; + + /* Unblock with retval code */ + for (i = 0; i < MAX_ACTIVE_STREAM; i++) { + if (sst_id == sst_drv_ctx->alloc_block[i].sst_id) { + sst_drv_ctx->alloc_block[i].ops_block.condition = true; + sst_drv_ctx->alloc_block[i].ops_block.ret_code = status; + sst_drv_ctx->alloc_block[i].ops_block.data = data; + pr_debug("wake id %d, sst_id %d condition %x\n", i, + sst_drv_ctx->alloc_block[i].sst_id, + sst_drv_ctx->alloc_block[i].ops_block.condition); + wake_up(&sst_drv_ctx->wait_queue); + break; + } + } +} Index: linux-2.6.33/include/sound/intel_sst_ioctl.h =================================================================== --- /dev/null +++ linux-2.6.33/include/sound/intel_sst_ioctl.h @@ -0,0 +1,364 @@ +#ifndef __INTEL_SST_IOCTL_H__ +#define __INTEL_SST_IOCTL_H__ +/* + * intel_sst_ipc.h - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corporation + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file defines all sst ioctls + */ + +/* codec and post/pre processing related info */ + +enum sst_codec_types { +/* AUDIO/MUSIC CODEC Type Definitions */ + SST_CODEC_TYPE_UNKNOWN = 0, + SST_CODEC_TYPE_PCM, /* Pass through Audio codec */ + SST_CODEC_TYPE_MP3, + SST_CODEC_TYPE_MP24, + SST_CODEC_TYPE_AAC, + SST_CODEC_TYPE_AACP, + SST_CODEC_TYPE_eAACP, + SST_CODEC_TYPE_WMA9, + SST_CODEC_TYPE_WMA10, + SST_CODEC_TYPE_WMA10P, + SST_CODEC_TYPE_RA, + SST_CODEC_TYPE_DDAC3, + SST_CODEC_TYPE_STEREO_TRUE_HD, + SST_CODEC_TYPE_STEREO_HD_PLUS, + + /* VOICE CODEC Type Definitions */ + SST_CODEC_TYPE_VOICE_PCM = 0x21, /* Pass through voice codec */ + SST_CODEC_SRC = 0x64, + SST_CODEC_MIXER = 0x65, + SST_CODEC_DOWN_MIXER = 0x66, + SST_CODEC_VOLUME_CONTROL = 0x67, + SST_CODEC_OEM1 = 0xC8, + SST_CODEC_OEM2 = 0xC9, +}; + +enum snd_sst_stream_ops { + STREAM_OPS_PLAYBACK = 0, /* Decode */ + STREAM_OPS_CAPTURE, /* Encode */ + STREAM_OPS_PLAYBACK_DRM, /* Play Audio/Voice */ + STREAM_OPS_PLAYBACK_ALERT, /* Play Audio/Voice */ + STREAM_OPS_CAPTURE_VOICE_CALL, /* CSV Voice recording */ +}; + +enum stream_type { + STREAM_TYPE_MUSIC = 1, + STREAM_TYPE_VOICE +}; + +/* Firmware Version info */ +struct snd_sst_fw_version { + __u8 build; /* build number*/ + __u8 minor; /* minor number*/ + __u8 major; /* major number*/ + __u8 type; /* build type */ +}; + +/* Port info structure */ +struct snd_sst_port_info { + __u16 port_type; + __u16 reserved; +}; + +/* Mixer info structure */ +struct snd_sst_mix_info { + __u16 max_streams; + __u16 reserved; +}; + +/* PCM Parameters */ +struct snd_pcm_params { + __u16 codec; /* codec type */ + __u8 num_chan; /* 1=Mono, 2=Stereo */ + __u8 pcm_wd_sz; /* 16/24 - bit*/ + __u32 brate; /* Bitrate in bits per second */ + __u32 sfreq; /* Sampling rate in Hz */ + __u16 frame_size; + __u16 samples_per_frame; /* Frame size num samples per frame */ + __u32 period_count; /* period elapsed time count, in samples,*/ +}; + +/* MP3 Music Parameters Message */ +struct snd_mp3_params { + __u16 codec; + __u8 num_chan; /* 1=Mono, 2=Stereo */ + __u8 pcm_wd_sz; /* 16/24 - bit*/ + __u32 brate; /* Use the hard coded value. */ + __u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */ + __u8 crc_check; /* crc_check - disable (0) or enable (1) */ + __u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB*/ + __u16 reserved; /* Unused */ +}; + +#define AAC_BIT_STREAM_ADTS 0 +#define AAC_BIT_STREAM_ADIF 1 +#define AAC_BIT_STREAM_RAW 2 + +/* AAC Music Parameters Message */ +struct snd_aac_params { + __u16 codec; + __u8 num_chan; /* 1=Mono, 2=Stereo*/ + __u8 pcm_wd_sz; /* 16/24 - bit*/ + __u32 brate; + __u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */ + __u32 aac_srate; /* Plain AAC decoder operating sample rate */ + __u8 mpg_id; /* 0=MPEG-2, 1=MPEG-4 */ + __u8 bs_format; /* input bit stream format adts=0, adif=1, raw=2 */ + __u8 aac_profile; /* 0=Main Profile, 1=LC profile, 3=SSR profile */ + __u8 ext_chl; /* No.of external channels */ + __u8 aot; /* Audio object type. 1=Main , 2=LC , 3=SSR, 4=SBR*/ + __u8 op_align; /* output alignment 0=16 bit , 1=MSB, 2= LSB align */ + __u8 brate_type; /* 0=CBR, 1=VBR */ + __u8 crc_check; /* crc check 0= disable, 1=enable */ + __s8 bit_stream_format[8]; /* input bit stream format adts/adif/raw */ + __u8 jstereo; /* Joint stereo Flag */ + __u8 sbr_present; /* 1 = SBR Present, 0 = SBR absent, for RAW */ + __u8 downsample; /* 1 = Downsampling ON, 0 = Downsampling OFF */ + __u8 num_syntc_elems; /* 1- Mono/stereo, 0 - Dual Mono, 0 - for raw */ + __s8 syntc_id[2]; /* 0 for ID_SCE(Dula Mono), -1 for raw */ + __s8 syntc_tag[2]; /* raw - -1 and 0 -16 for rest of the streams */ + __u8 pce_present; /* Flag. 1- present 0 - not present, for RAW */ + __u8 reserved; + __u16 reserved1; + +}; + +/* WMA Music Parameters Message */ +struct snd_wma_params { + __u16 codec; + __u8 num_chan; /* 1=Mono, 2=Stereo */ + __u8 pcm_wd_sz; /* 16/24 - bit*/ + __u32 brate; /* Use the hard coded value. */ + __u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */ + __u32 channel_mask; /* Channel Mask */ + __u16 format_tag; /* Format Tag */ + __u16 block_align; /* packet size */ + __u16 wma_encode_opt;/* Encoder option */ + __u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB */ + __u8 pcm_src; /* input pcm bit width */ +}; + +/* Pre processing param structure */ +struct snd_prp_params { + __u32 reserved; /* No pre-processing defined yet */ +}; + +/* Post processing Capability info structure */ +struct snd_sst_postproc_info { + __u32 src_min; /* Supported SRC Min sampling freq */ + __u32 src_max; /* Supported SRC Max sampling freq */ + __u8 src; /* 0=Not supported, 1=Supported */ + __u8 bass_boost; /* 0=Not Supported, 1=Supported */ + __u8 stereo_widening; /* 0=Not Supported, 1=Supported */ + __u8 volume_control; /* 0=Not Supported, 1=Supported */ + __s16 min_vol; /* Minimum value of Volume in dB */ + __s16 max_vol; /* Maximum value of Volume in dB */ + __u8 mute_control; /* 0=No Mute, 1=Mute */ + __u8 reserved1; + __u16 reserved2; +}; + +/* pre processing Capability info structure */ +struct snd_sst_prp_info { + __s16 min_vol; /* Minimum value of Volume in dB */ + __s16 max_vol; /* Maximum value of Volume in dB */ + __u8 volume_control; /* 0=Not Supported, 1=Supported */ + __u8 reserved1; /* for 32 bit alignment */ + __u16 reserved2; /* for 32 bit alignment */ +} __attribute__ ((packed)); + +/* Firmware capabilities info */ +struct snd_sst_fw_info { + struct snd_sst_fw_version fw_version; /* Firmware version */ + __u8 audio_codecs_supported[8]; /* Codecs supported by FW */ + __u32 recommend_min_duration; /* Min duration for Lowpower Playback */ + __u8 max_pcm_streams_supported; /* Max num of PCM streams supported */ + __u8 max_enc_streams_supported; /* Max number of Encoded streams */ + __u16 reserved; /* 32 bit alignment*/ + struct snd_sst_postproc_info pop_info; /* Post processing capability */ + struct snd_sst_prp_info prp_info; /* pre_processing mod cap info */ + struct snd_sst_port_info port_info[2]; /* Port info */ + struct snd_sst_mix_info mix_info; /* Mixer info */ + __u32 min_input_buf; /* minmum i/p buffer for decode */ +}; + +/* Add the codec parameter structures for new codecs to be supported */ +#define CODEC_PARAM_STRUCTURES \ + struct snd_pcm_params pcm_params; \ + struct snd_mp3_params mp3_params; \ + struct snd_aac_params aac_params; \ + struct snd_wma_params wma_params; + +/* Pre and Post Processing param structures */ +#define PPP_PARAM_STRUCTURES \ + struct snd_prp_params prp_params; + +/* Codec params struture */ +union snd_sst_codec_params { + CODEC_PARAM_STRUCTURES; +}; + +/* Pre-processing params struture */ +union snd_sst_ppp_params{ + PPP_PARAM_STRUCTURES; +}; + +struct snd_sst_stream_params { + union snd_sst_codec_params uc; +} __attribute__ ((packed)); + +struct snd_sst_params { + __u32 result; + __u32 stream_id; + __u8 codec; + __u8 ops; + __u8 stream_type; + struct snd_sst_stream_params sparams; +}; + +/* ioctl related stuff here */ +struct snd_sst_pmic_config { + __u32 sfreq; /* Sampling rate in Hz */ + __u16 num_chan; /* Mono =1 or Stereo =2 */ + __u16 pcm_wd_sz; /* Number of bits per sample */ +} __attribute__ ((packed)); + +struct snd_sst_get_stream_params { + struct snd_sst_params codec_params; + struct snd_sst_pmic_config pcm_params; +}; + +enum snd_sst_target_type { + SND_SST_TARGET_PMIC = 1, + SND_SST_TARGET_OTHER, +}; + +enum snd_sst_port_action { + SND_SST_PORT_PREPARE = 1, + SND_SST_PORT_ACTIVATE, +}; + +/* Target selection per device structure */ +struct snd_sst_slot_info { + __u8 mix_enable; /* Mixer enable or disable */ + __u8 device_type; + __u8 device_instance; /* 0, 1, 2 */ + __u8 target_type; + __u16 slot[2]; + __u8 master; + __u8 action; + __u16 reserved; + struct snd_sst_pmic_config pcm_params; +} __attribute__ ((packed)); + +/* Target device list structure */ +struct snd_sst_target_device { + __u32 device_route; + struct snd_sst_slot_info devices[2]; +} __attribute__ ((packed)); + +struct snd_sst_driver_info { + __u32 version; /* Version of the driver */ + __u32 active_pcm_streams; + __u32 active_enc_streams; + __u32 max_pcm_streams; + __u32 max_enc_streams; + __u32 buf_per_stream; +}; + +struct snd_sst_vol { + __u32 stream_id; + __s32 volume; + __u32 ramp_duration; + __u32 ramp_type; /* Ramp type, default=0 */ +}; + +struct snd_sst_mute { + __u32 stream_id; + __u32 mute; +}; + +enum snd_sst_buff_type { + SST_BUF_USER = 1, + SST_BUF_MMAP, + SST_BUF_RAR, +}; + +struct snd_sst_mmap_buff_entry { + unsigned int offset; + unsigned int size; +}; + +struct snd_sst_mmap_buffs { + unsigned int entries; + enum snd_sst_buff_type type; + struct snd_sst_mmap_buff_entry *buff; +}; + +struct snd_sst_buff_entry { + void *buffer; + unsigned int size; +}; + +struct snd_sst_buffs { + unsigned int entries; + __u8 type; + struct snd_sst_buff_entry *buff_entry; +}; + +struct snd_sst_dbufs { + unsigned long long input_bytes_consumed; + unsigned long long output_bytes_produced; + struct snd_sst_buffs *ibufs; + struct snd_sst_buffs *obufs; +}; + +/*IOCTL defined here */ +/*SST MMF IOCTLS only */ +#define SNDRV_SST_STREAM_SET_PARAMS _IOR('L', 0x00, \ + struct snd_sst_stream_params *) +#define SNDRV_SST_STREAM_GET_PARAMS _IOWR('L', 0x01, \ + struct snd_sst_get_stream_params *) +#define SNDRV_SST_STREAM_GET_TSTAMP _IOWR('L', 0x02, __u64 *) +#define SNDRV_SST_STREAM_DECODE _IOWR('L', 0x03, struct snd_sst_dbufs *) +#define SNDRV_SST_STREAM_BYTES_DECODED _IOWR('L', 0x04, __u64 *) +#define SNDRV_SST_STREAM_START _IO('A', 0x42) +#define SNDRV_SST_STREAM_DROP _IO('A', 0x43) +#define SNDRV_SST_STREAM_DRAIN _IO('A', 0x44) +#define SNDRV_SST_STREAM_PAUSE _IOW('A', 0x45, int) +#define SNDRV_SST_STREAM_RESUME _IO('A', 0x47) +#define SNDRV_SST_MMAP_PLAY _IOW('L', 0x05, struct snd_sst_mmap_buffs *) +#define SNDRV_SST_MMAP_CAPTURE _IOW('L', 0x06, struct snd_sst_mmap_buffs *) +/*SST common ioctls */ +#define SNDRV_SST_DRIVER_INFO _IOR('L', 0x10, struct snd_sst_driver_info *) +#define SNDRV_SST_SET_VOL _IOW('L', 0x11, struct snd_sst_vol *) +#define SNDRV_SST_GET_VOL _IOW('L', 0x12, struct snd_sst_vol *) +#define SNDRV_SST_MUTE _IOW('L', 0x13, struct snd_sst_mute *) +/*AM Ioctly only */ +#define SNDRV_SST_FW_INFO _IOR('L', 0x20, struct snd_sst_fw_info *) +#define SNDRV_SST_SET_TARGET_DEVICE _IOW('L', 0x21, \ + struct snd_sst_target_device *) + +#endif /* __INTEL_SST_IOCTL_H__ */ Index: linux-2.6.33/sound/pci/sst/intel_sst_interface.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intel_sst_interface.c @@ -0,0 +1,2134 @@ +/* + * intel_sst_interface.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corporation + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * Dharageswari R <dharageswari.r@intel.com) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This driver exposes the audio engine functionalities to the ALSA + * and middleware. + * Upper layer interfaces (MAD driver, MMF) to SST driver + */ + +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/fs.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/workqueue.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <linux/ioctl.h> +#ifdef CONFIG_MRST_RAR_HANDLER +#include <linux/rar/rar_register.h> +#include <linux/rar/memrar.h> +#endif +#include <asm/div64.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" + +#define AM_MODULE 1 +#define STREAM_MODULE 0 + +/** +* This function is called when the FW needs to be downloaded to SST DSP engine +*/ +static int sst_download_fw(void) +{ + int retval; + const struct firmware *fw_sst; + + pr_debug("SST Downloading FW now...\n"); + retval = request_firmware(&fw_sst, + SST_FW_STD_FILENAME, + &sst_drv_ctx->pci->dev); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: req fw failed %d \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + return retval; + } + sst_drv_ctx->alloc_block[0].sst_id = FW_DWNL_ID; + retval = sst_load_fw(fw_sst, NULL); + if (retval) + goto end_restore; + + sst_drv_ctx->alloc_block[0].ops_block.condition = false; + retval = sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[0]); + if (retval) + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: fw download failed %d \n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); +end_restore: + release_firmware(fw_sst); + sst_drv_ctx->alloc_block[0].sst_id = BLOCK_UNINIT; + return retval; +} + +/** +* intel_sst_open - opens a handle to driver +* @i_node: inode structure +* @file_ptr:pointer to file +* +* This function is called by OS when a user space component +* tries to get a driver handle. Only one handle at a time +* will be allowed +*/ +int intel_sst_open(struct inode *i_node, struct file *file_ptr) +{ + dev_t device = i_node->i_rdev; + unsigned int retval = 0; + + if (sst_drv_ctx->pmic_state != SND_MAD_INIT_DONE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Sound card not availble \n ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EIO; + } + + if (sst_drv_ctx->sst_state == SST_UN_INIT) { + /* FW is not downloaded */ + retval = sst_download_fw(); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: FW download +\ + failed...abort\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENODEV; + } + } + if (device == MKDEV(INTEL_SST_MAJOR, 0)) { + /* app open */ + if (sst_drv_ctx->encoded_cnt < MAX_ENC_STREAM) { + struct ioctl_pvt_data *data = + kzalloc(sizeof(struct ioctl_pvt_data), + GFP_KERNEL); + if (!data) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR:error +\ + rsrvin data mry\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + sst_drv_ctx->encoded_cnt++; + sst_drv_ctx->stream_cnt++; + data->pvt_id = sst_assign_pvt_id(sst_drv_ctx); + data->str_id = 0; + file_ptr->private_data = (void *)data; + pr_debug("sst id allocated = %d!\n", data->pvt_id); + } else + retval = -EACCES; + } else if (device == MKDEV(INTEL_SST_MAJOR, 1)) { + /* audio manager open */ + if (sst_drv_ctx->am_cnt < MAX_AM_HANDLES) { + sst_drv_ctx->am_cnt++; + pr_debug("AM handle opened...\n"); + } else + retval = -EACCES; + } else + retval = -EINVAL; + return retval; +} + +void free_stream_context(unsigned int str_id) +{ + struct stream_info *stream; + + if (!sst_validate_strid(str_id)) { + /* str_id is valid, so stream is alloacted */ + stream = &sst_drv_ctx->streams[str_id]; + if (stream->ops == STREAM_OPS_PLAYBACK || + stream->ops == STREAM_OPS_PLAYBACK_DRM) { + sst_drv_ctx->pb_streams--; + if (sst_drv_ctx->pb_streams == 0) + sst_drv_ctx->scard_ops->power_down_pmic_pb(); + } else if (stream->ops == STREAM_OPS_CAPTURE) { + sst_drv_ctx->cp_streams--; + if (sst_drv_ctx->cp_streams == 0) + sst_drv_ctx->scard_ops->power_down_pmic_cp(); + } + if (sst_drv_ctx->pb_streams == 0 && + sst_drv_ctx->cp_streams == 0) + sst_drv_ctx->scard_ops->power_down_pmic(); + if (sst_free_stream(str_id)) + sst_clean_stream(&sst_drv_ctx->streams[str_id]); + } +} + +/** +* intel_sst_release - releases a handle to driver +* @i_node: inode structure +* @file_ptr: pointer to file +* +* This function is called by OS when a user space component +* tries to release a driver handle. +*/ +int intel_sst_release(struct inode *i_node, struct file *file_ptr) +{ + dev_t device = i_node->i_rdev; + + pr_debug("Release called \n"); + if (device == MKDEV(INTEL_SST_MAJOR, 0)) { + struct ioctl_pvt_data *data = + (struct ioctl_pvt_data *)file_ptr->private_data; + + /* app close */ + pr_debug("Closing app handle \n"); + sst_drv_ctx->encoded_cnt--; + sst_drv_ctx->stream_cnt--; + free_stream_context(data->str_id); + kfree(data); + } else if (device == MKDEV(INTEL_SST_MAJOR, 1)) { + /* audio manager close */ + pr_debug("AM handle closed \n"); + sst_drv_ctx->am_cnt--; + } + return 0; +} + +/** +* intel_sst_mmap - mmaps a kernel buffer to user space for copying data +* @vma: vm area structure instance +* @file_ptr: pointer to file +* +* This function is called by OS when a user space component +* tries to get mmap memory from driver +*/ +int intel_sst_mmap(struct file *file_ptr, struct vm_area_struct *vma) +{ + int retval, length; + struct ioctl_pvt_data *data = + (struct ioctl_pvt_data *)file_ptr->private_data; + int str_id = data->str_id; + void *mem_area; + + retval = sst_validate_strid(str_id); + if (retval) + return -EINVAL; + + length = vma->vm_end - vma->vm_start; + pr_debug("called for stream %d length 0x%x\n", str_id, length); + + if (length > sst_drv_ctx->mmap_len) + return -ENOMEM; + if (!sst_drv_ctx->mmap_mem) + return -EIO; + + /* round it up to the page bondary */ + /*mem_area = (void *)((((unsigned long)sst_drv_ctx->mmap_mem) + + PAGE_SIZE - 1) & PAGE_MASK);*/ + mem_area = (void *) PAGE_ALIGN((unsigned int) sst_drv_ctx->mmap_mem); + + /* map the whole physically contiguous area in one piece */ + retval = remap_pfn_range(vma, + vma->vm_start, + virt_to_phys((void *)mem_area) >> PAGE_SHIFT, + length, + vma->vm_page_prot); + if (retval) { + sst_drv_ctx->streams[str_id].mmapped = false; + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: mapping +\ + failed %d ",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + retval); + } else + sst_drv_ctx->streams[str_id].mmapped = true; + + pr_debug("mmap ret 0x%x \n", retval); + return retval; +} + +/** +* intel_sst_mmap_play_capture - sets mmap data buffers to play/capture +*/ +static int intel_sst_mmap_play_capture(u32 str_id, + struct snd_sst_mmap_buffs *mmap_buf) +{ + struct sst_stream_bufs *bufs; + int retval, i; + struct stream_info *stream; + struct snd_sst_mmap_buff_entry *buf_entry; + + pr_debug("called for str_id %d \n", str_id); + retval = sst_validate_strid(str_id); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: val failed %d " ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + return -EINVAL; + } + BUG_ON(!mmap_buf); + + stream = &sst_drv_ctx->streams[str_id]; + if (stream->mmapped != true) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream not mapped! ",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EIO; + } + + if (stream->status == STREAM_UN_INIT || + stream->status == STREAM_DECODE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: BAD REQUEST!,+\ + streamstate is %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + stream->status); + return -EBADRQC; + } + stream->curr_bytes = 0; + stream->cumm_bytes = 0; + + pr_debug("new buffers count %d status %d\n", + mmap_buf->entries, stream->status); + buf_entry = mmap_buf->buff; + for (i = 0; i < mmap_buf->entries; i++) { + BUG_ON(!buf_entry); + bufs = kzalloc(sizeof(*bufs), GFP_KERNEL); + if (!bufs) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: mem +\ + allocation failed \n ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + bufs->size = buf_entry->size; + bufs->offset = buf_entry->offset; + bufs->addr = sst_drv_ctx->mmap_mem; + bufs->in_use = false; + buf_entry++; + /* locking here */ + mutex_lock(&stream->lock); + list_add_tail(&bufs->node, &stream->bufs); + mutex_unlock(&stream->lock); + } + + mutex_lock(&stream->lock); + stream->data_blk.condition = false; + stream->data_blk.ret_code = 0; + if (stream->status == STREAM_INIT && + stream->prev != STREAM_UN_INIT && + stream->need_draining != true) { + stream->prev = stream->status; + stream->status = STREAM_RUNNING; + if (stream->ops == STREAM_OPS_PLAYBACK) { + pr_debug("play frames...\n"); + if (sst_play_frame(str_id) < 0) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + play frames failed \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + mutex_unlock(&stream->lock); + return -EIO; + } + } else if (stream->ops == STREAM_OPS_CAPTURE) { + pr_debug("capture frames...\n"); + if (sst_capture_frame(str_id) < 0) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: capture +\ + frames failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + mutex_unlock(&stream->lock); + return -EIO; + } + } + } + mutex_unlock(&stream->lock); + /* Block the call for reply */ + if (!list_empty(&stream->bufs)) { + pr_debug("ioctl waiting...\n"); + stream->data_blk.on = true; + retval = sst_wait_interruptible(sst_drv_ctx, + &stream->data_blk); + } + + if (retval >= 0) + retval = stream->cumm_bytes; + pr_debug("end of play/rec ioctl bytes = %d!!\n", retval); + return retval; +} + +/** +* intel_sst_play_capture - sets user data buffers to play/capture +*/ +static int intel_sst_play_capture(struct stream_info *stream, int str_id) +{ + int retval; + + stream->data_blk.ret_code = 0; + stream->data_blk.on = true; + stream->data_blk.condition = false; + + mutex_lock(&stream->lock); + if (stream->status == STREAM_INIT && stream->prev != STREAM_UN_INIT) { + /* stream is started */ + stream->prev = stream->status; + stream->status = STREAM_RUNNING; + } + + if (stream->status == STREAM_INIT && stream->prev == STREAM_UN_INIT) { + /* stream is not started yet */ + pr_debug("Stream isnt started yet state %d, prev %d \n", + stream->status, stream->prev); + } else if ((stream->status == STREAM_RUNNING || + stream->status == STREAM_PAUSED) && + stream->need_draining != true) { + /* stream is started */ + if (stream->ops == STREAM_OPS_PLAYBACK || + stream->ops == STREAM_OPS_PLAYBACK_DRM) { + if (sst_play_frame(str_id) < 0) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: play frames +\ + failed \n", + get_seconds(), sst_get_time(), __func__ , __LINE__); + mutex_unlock(&stream->lock); + return -EIO; + } + } else if (stream->ops == STREAM_OPS_CAPTURE) { + if (sst_capture_frame(str_id) < 0) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: capture +\ + frames failed \n " ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + mutex_unlock(&stream->lock); + return -EIO; + } + } + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Streamstate %d +\ + invalid,prev %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__,\ + stream->status, stream->prev); + mutex_unlock(&stream->lock); + return -EIO; + } + mutex_unlock(&stream->lock); + /* Block the call for reply */ + pr_debug("write waiting...\n"); + + retval = sst_wait_interruptible(sst_drv_ctx, &stream->data_blk); + if (retval) { + stream->status = STREAM_INIT; + pr_debug("wait returned error...\n"); + } + pr_debug("write returning\n"); + return retval; +} + +/** +* snd_sst_fill_kernel_list - fills kernel list with buffer addresses for +* SST DSP driver to process +*/ +static int snd_sst_fill_kernel_list(struct stream_info *stream, + const struct iovec *iovec, unsigned long nr_segs, + struct list_head *copy_to_list) +{ + struct sst_stream_bufs *stream_bufs; + unsigned long index, data_not_copied, mmap_len; + unsigned char *bufp; + unsigned long size, copied_size; + int retval = 0, add_to_list = 0; + static int sent_offset; + static unsigned long sent_index; + + stream_bufs = kzalloc(sizeof(*stream_bufs), GFP_KERNEL); + if (!stream_bufs) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: memory allocation failed \n " ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + stream_bufs->addr = sst_drv_ctx->mmap_mem; +#ifdef CONFIG_MRST_RAR_HANDLER + if (stream->ops == STREAM_OPS_PLAYBACK_DRM) { + for (index = stream->sg_index; index < nr_segs; index++) { + __u32 rar_handle; + struct sst_stream_bufs *stream_bufs = kzalloc(sizeof(*stream_bufs), GFP_KERNEL); + + stream->sg_index = index; + if (!stream_bufs) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: mry +\ + alocation failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + retval = copy_from_user(&rar_handle, + iovec[index].iov_base, + sizeof(__u32)); + if (retval != 0) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: copy from user +\ + failed\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EIO; + } + stream_bufs->addr = (char *)rar_handle; + pr_debug("rar handle received = 0x%x\n", (__u32)stream_bufs->addr); + stream_bufs->in_use = false; + stream_bufs->size = iovec[0].iov_len; + /* locking here */ + mutex_lock(&stream->lock); + list_add_tail(&stream_bufs->node, &stream->bufs); + mutex_unlock(&stream->lock); + } + stream->sg_index = index; + return retval; + } +#endif + mmap_len = sst_drv_ctx->mmap_len; + stream_bufs->addr = sst_drv_ctx->mmap_mem; + bufp = stream->cur_ptr; + + pr_debug("mmap_len - %lx\n", mmap_len); + copied_size = 0; + + if (!stream->sg_index) + sent_index = sent_offset = 0; + + for (index = stream->sg_index; index < nr_segs; index++) { + stream->sg_index = index; + pr_debug("index - %lx, cur_ptr - %p\n", index, stream->cur_ptr); + pr_debug("base - %p, size - 0x%x\n", iovec[index].iov_base, + iovec[index].iov_len); + pr_debug("bufp - %p\n", bufp); + if (!stream->cur_ptr) + bufp = iovec[index].iov_base; + + size = ((unsigned long)iovec[index].iov_base + + iovec[index].iov_len) - (unsigned long) bufp; + + pr_debug("size - %lx\n", size); + if ((copied_size + size) > mmap_len) + size = mmap_len - copied_size; + + pr_debug("size - %lx\n", size); + + if (stream->ops == STREAM_OPS_PLAYBACK) { + pr_debug("Playback stream copying now....\n"); + data_not_copied = copy_from_user( + (void *)(stream_bufs->addr + copied_size), + bufp, size); + if (data_not_copied > 0) { + /* Clean up the list and return error code */ + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: cpyfrmusr not +\ + coped -%ld",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + data_not_copied); + retval = -EIO; + break; + } + } else if (stream->ops == STREAM_OPS_CAPTURE) { + struct snd_sst_user_cap_list *entry = + kzalloc(sizeof(*entry), GFP_KERNEL); + + if (!entry) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: mem +\ + allocation failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + /* FIXME cleanup prev */ + return -ENOMEM; + } + entry->iov_index = index; + entry->iov_offset = (unsigned long) bufp - + (unsigned long)iovec[index].iov_base; + entry->offset = copied_size; + entry->size = size; + pr_debug("ENTRY:ioindx %d,iooff %ld,koff %ld,ksz %ld \n", + entry->iov_index, entry->iov_offset, + entry->offset, entry->size); + list_add_tail(&entry->node, copy_to_list); + } + + pr_debug("cur_ptr - %lx\n", (unsigned long) stream->cur_ptr); + stream->cur_ptr = bufp + size; + + if (((unsigned long)iovec[index].iov_base + + iovec[index].iov_len) == + (unsigned long)stream->cur_ptr) { + stream->cur_ptr = NULL; + stream->sg_index++; + } + + copied_size += size; + pr_debug("copied_size - %lx\n", copied_size); + if ((copied_size >= mmap_len) || + (stream->sg_index == nr_segs)) { + add_to_list = 1; + } + + if (add_to_list) { + stream_bufs->in_use = false; + stream_bufs->size = copied_size; + /* locking here */ + mutex_lock(&stream->lock); + list_add_tail(&stream_bufs->node, &stream->bufs); + mutex_unlock(&stream->lock); + break; + } + } + return retval; +} + +/** +* snd_sst_copy_userbuf_capture - This function copies the captured data +* returned from SST DSP engine to the user buffers +*/ +static int snd_sst_copy_userbuf_capture(struct stream_info *stream, + const struct iovec *iovec, + struct list_head *copy_to_list) +{ + struct snd_sst_user_cap_list *entry, *_entry; + struct sst_stream_bufs *kbufs = NULL, *_kbufs; + int retval = 0; + unsigned long data_not_copied; + + /* copy sent buffers */ + /* FIXME optimze */ + pr_debug("capture stream copying to user now...\n"); + list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) { + if (kbufs->in_use == true) { + /* copy to user */ + list_for_each_entry_safe(entry, _entry, + copy_to_list, node) { + pr_debug("filling now... \n"); + pr_debug("iindx %d,ioff %ld,koff %ld,ksz %ld \n", + entry->iov_index, entry->iov_offset, + entry->offset, entry->size); + pr_debug("Copying at %p size %lx\n", + iovec[entry->iov_index].iov_base + + entry->iov_offset, + entry->size); + data_not_copied = copy_to_user((void *) + iovec[entry->iov_index].iov_base + + entry->iov_offset, + kbufs->addr + entry->offset, + entry->size); + if (data_not_copied > 0) { + /* Clean up the list and return error */ + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: copy to +\ + user err -%ld\n ",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + data_not_copied); + retval = -EIO; + break; + } + list_del(&entry->node); + kfree(entry); + } + pr_debug("coming out of loop\n"); + } + } + pr_debug("end of cap copy\n"); + return retval; +} + +/* + * snd_sst_userbufs_play_cap - constructs the list from user buffers + * @iovec: pointer to iovec structure + * @nr_segs: number entries in the iovec structure + * @str_id: stream id + * @stream: pointer to stream_info structure + * This function will traverse the user list and copy the data to the kernel + * space buffers. + */ /* FIXME cleanups in this fn no mem leaks due to link list */ +static int snd_sst_userbufs_play_cap(const struct iovec *iovec, + unsigned long nr_segs, unsigned int str_id, + struct stream_info *stream) +{ + int retval; + LIST_HEAD(copy_to_list); + + + retval = snd_sst_fill_kernel_list(stream, iovec, nr_segs, + ©_to_list); + + retval = intel_sst_play_capture(stream, str_id); + if (retval < 0) + return retval; + + if (stream->ops == STREAM_OPS_CAPTURE) { + retval = snd_sst_copy_userbuf_capture(stream, iovec, + ©_to_list); + } + return retval; +} + +/** +* intel_sst_read_write - This function is common function across read/write +* for user buffers called from system calls +*/ +static int intel_sst_read_write(unsigned int str_id, char __user *buf, + size_t count) +{ + int retval; + struct stream_info *stream; + struct iovec iovec; + unsigned long nr_segs; + + retval = sst_validate_strid(str_id); + if (retval) + return -EINVAL; + stream = &sst_drv_ctx->streams[str_id]; + if (stream->mmapped == true) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: user write and +\ + stream is mapped",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EIO; + } + if (!count) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: args +\ + invalid %d",\ + get_seconds(), sst_get_time(), __func__ , __LINE__,\ + retval); + return -EINVAL; + } + stream->curr_bytes = 0; + stream->cumm_bytes = 0; + /* copy user buf details */ + pr_debug("new buffers %p, copy size %d, status %d\n" , + buf, (int) count, (int) stream->status); + + stream->buf_type = SST_BUF_USER_STATIC; + iovec.iov_base = (void *)buf; + iovec.iov_len = count; + nr_segs = 1; + + do { + retval = snd_sst_userbufs_play_cap(&iovec, nr_segs, + str_id, stream); + + if (retval < 0) + break; + + } while (stream->sg_index < nr_segs); + + stream->sg_index = 0; + stream->cur_ptr = NULL; + if (retval >= 0) + retval = stream->cumm_bytes; + pr_debug("end of play/rec bytes = %d!!\n", retval); + return retval; +} + +/* + * intel_sst_write - This function is called when user tries to play out data + * @file_ptr: pointer to file + * @buf: user buffer to be played out + * @count: size of tthe buffer + * @offset: offset to start from + */ +int intel_sst_write(struct file *file_ptr, const char __user *buf, + size_t count, loff_t *offset) +{ + struct ioctl_pvt_data *data = + (struct ioctl_pvt_data *)file_ptr->private_data; + int str_id = data->str_id; + struct stream_info *stream = &sst_drv_ctx->streams[str_id]; + + pr_debug("called for %d\n", str_id); + if (stream->status == STREAM_UN_INIT || + stream->status == STREAM_DECODE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: BAD REQUEST ",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EBADRQC; + } + return intel_sst_read_write(str_id, (char __user *)buf, count); +} + +/* + * intel_sst_aio_write - This function is called when user tries to play out + * multiple data buffers + * @kiocb: pointer to a structure containing file pointer + * @iov: list of user buffer to be played out + * @nr_segs: number of entries + * @offset: offset to start from + */ +ssize_t intel_sst_aio_write(struct kiocb *kiocb, const struct iovec *iov, + unsigned long nr_segs, loff_t offset) +{ + int retval; + struct ioctl_pvt_data *data = + (struct ioctl_pvt_data *)kiocb->ki_filp->private_data; + int str_id = data->str_id; + struct stream_info *stream; + + pr_debug("entry - %ld\n", nr_segs); + + if (is_sync_kiocb(kiocb) == false) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: aio_writ frm +\ + userspace is not alowed\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + + pr_debug("called for str_id %d \n", str_id); + retval = sst_validate_strid(str_id); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: invalid stream id \n ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + stream = &sst_drv_ctx->streams[str_id]; + if (stream->mmapped == true) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: user write & stream is mapped",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EIO; + } + if (stream->status == STREAM_UN_INIT || + stream->status == STREAM_DECODE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: BAD REQUEST",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EBADRQC; + } + stream->curr_bytes = 0; + stream->cumm_bytes = 0; + pr_debug("new segs %ld, offset %d, status %d\n" , + nr_segs, (int) offset, (int) stream->status); + stream->buf_type = SST_BUF_USER_STATIC; + do { + retval = snd_sst_userbufs_play_cap(iov, nr_segs, + str_id, stream); + if (retval < 0) + break; + + } while (stream->sg_index < nr_segs); + + stream->sg_index = 0; + stream->cur_ptr = NULL; + if (retval >= 0) + retval = stream->cumm_bytes; + pr_debug("end of play/rec bytes = %d!!\n", retval); + return retval; +} + +/* + * intel_sst_read - This function is called when user tries to capture data + * @file_ptr: pointer to file + * @buf: user buffer to be filled with captured data + * @count: size of tthe buffer + * @offset: offset to start from + */ +int intel_sst_read(struct file *file_ptr, char __user *buf, + size_t count, loff_t *offset) +{ + struct ioctl_pvt_data *data = + (struct ioctl_pvt_data *)file_ptr->private_data; + int str_id = data->str_id; + struct stream_info *stream = &sst_drv_ctx->streams[str_id]; + + pr_debug("called for %d\n", str_id); + if (stream->status == STREAM_UN_INIT || + stream->status == STREAM_DECODE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: BAD REQUEST!\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EBADRQC; + } + return intel_sst_read_write(str_id, buf, count); +} + +/* + * intel_sst_aio_read - This function is called when user tries to capture out + * multiple data buffers + * @kiocb: pointer to a structure containing file pointer + * @iov: list of user buffer to be filled with captured + * @nr_segs: number of entries + * @offset: offset to start from + */ + +ssize_t intel_sst_aio_read(struct kiocb *kiocb, const struct iovec *iov, + unsigned long nr_segs, loff_t offset) +{ + int retval; + struct ioctl_pvt_data *data = + (struct ioctl_pvt_data *)kiocb->ki_filp->private_data; + int str_id = data->str_id; + struct stream_info *stream; + + pr_debug("entry - %ld\n", nr_segs); + + if (is_sync_kiocb(kiocb) == false) { + pr_debug("aio_read from user space is not allowed\n"); + return -EINVAL; + } + + pr_debug("called for str_id %d \n", str_id); + retval = sst_validate_strid(str_id); + if (retval) + return -EINVAL; + stream = &sst_drv_ctx->streams[str_id]; + if (stream->mmapped == true) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: user write & +\ + stream is mapped!!! ",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EIO; + } + if (stream->status == STREAM_UN_INIT || + stream->status == STREAM_DECODE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: BAD REQUEST!\n ",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EBADRQC; + } + stream->curr_bytes = 0; + stream->cumm_bytes = 0; + + pr_debug("new segs %ld, offset %d, status %d\n" , + nr_segs, (int) offset, (int) stream->status); + stream->buf_type = SST_BUF_USER_STATIC; + do { + retval = snd_sst_userbufs_play_cap(iov, nr_segs, + str_id, stream); + if (retval < 0) + break; + + } while (stream->sg_index < nr_segs); + + stream->sg_index = 0; + stream->cur_ptr = NULL; + if (retval >= 0) + retval = stream->cumm_bytes; + pr_debug("end of play/rec bytes = %d!!\n", retval); + return retval; +} + +/* + * sst_print_stream_params - prints the stream parameters (debug fn) + */ +static void sst_print_stream_params(struct snd_sst_get_stream_params *get_prm) +{ + pr_debug("codec params:result =%d\n", + get_prm->codec_params.result); + pr_debug("codec params:stream = %d\n", + get_prm->codec_params.stream_id); + pr_debug("codec params:codec = %d\n", + get_prm->codec_params.codec); + pr_debug("codec params:ops = %d\n", + get_prm->codec_params.ops); + pr_debug("codec params:stream_type= %d\n", + get_prm->codec_params.stream_type); + pr_debug("pcmparams:sfreq= %d\n", + get_prm->pcm_params.sfreq); + pr_debug("pcmparams:num_chan= %d\n", + get_prm->pcm_params.num_chan); + pr_debug("pcmparams:pcm_wd_sz= %d\n", + get_prm->pcm_params.pcm_wd_sz); + return; +} + +/* + * sst_print_fw_info - prints the firmware information (debug fn) + */ +static void sst_print_fw_info(struct snd_sst_fw_info *fw_info) +{ + pr_debug("build = %d\n", fw_info->fw_version.build); + pr_debug("minor = %d\n", fw_info->fw_version.minor); + pr_debug("major= %d\n", fw_info->fw_version.major); + pr_debug("max pcm = %d\n", fw_info->max_pcm_streams_supported); + pr_debug("max enc = %d\n", fw_info->max_enc_streams_supported); + pr_debug("min input buf = %d\n", fw_info->min_input_buf); + pr_debug("pop:src_min= %d\n", fw_info->pop_info.src_min); + pr_debug("pop:src_max= %d\n", fw_info->pop_info.src_max); + pr_debug("pop:src= %d\n", fw_info->pop_info.src); + pr_debug("pop:bass_boost= %d\n", fw_info->pop_info.bass_boost); + pr_debug("pop:stereo_widening= %d\n", fw_info->pop_info.stereo_widening); + pr_debug("pop:volume_control= %d\n", fw_info->pop_info.volume_control); + pr_debug("pop:min_vol= %d\n", fw_info->pop_info.min_vol); + pr_debug("pop:max_vol= %d\n", fw_info->pop_info.max_vol); + pr_debug("prp:min_vol= %d\n", fw_info->prp_info.min_vol); + pr_debug("prp:max_vol= %d\n", fw_info->prp_info.max_vol); + pr_debug("prp:volume_control= %d\n", fw_info->prp_info.volume_control); + pr_debug("mix:max streams = %d\n", fw_info->mix_info.max_streams); + pr_debug("port0:port_type = %d\n", fw_info->port_info[0].port_type); + pr_debug("port1:port_type = %d\n", fw_info->port_info[1].port_type); + return; +} + +/* + * sst_get_stream_allocated - this function gets a stream allocated with + * the given params + */ +static int sst_get_stream_allocated(struct snd_sst_params *str_param, + u32 block, u32 pvt_id) +{ + int retval; + retval = sst_alloc_stream((char *) &str_param->sparams, str_param->ops, + str_param->codec, pvt_id); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: sst_alloc_stream failed %d \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + return retval; + } + /* Block the call for reply */ + retval = sst_wait_timeout(sst_drv_ctx, + &sst_drv_ctx->alloc_block[block]); + + return retval; +} + +/* + * set_port_params - this function sets the port parameters at Sound card end + */ +static void set_port_params(struct snd_sst_params *str_param, + enum snd_sst_stream_ops ops) +{ + /*int sfreq = str_param->sparams.uc.pcm_params.sfreq; + int word_size = str_param->sparams.uc.pcm_params.pcm_wd_sz; + + pr_debug("sampling frequency = %d wd_size = %d \n", sfreq, word_size); + + if (ops == STREAM_OPS_PLAYBACK || + ops == STREAM_OPS_PLAYBACK_DRM) { + pr_debug("Setting playback path and port settings...\n"); + sst_drv_ctx->scard_ops.set_pcm_audio_params(sfreq, + word_size); + } else if (ops == STREAM_OPS_CAPTURE) { + pr_debug("Setting capture path...\n"); + sst_drv_ctx->scard_ops->set_pcm_audio_params(sfreq, word_size); + }*/ + return; +} + +/* + * sst_get_sfreq - this function returns the frequency of the stream + */ +static int sst_get_sfreq(struct snd_sst_params *str_param) +{ + switch (str_param->codec) { + case SST_CODEC_TYPE_PCM: + return str_param->sparams.uc.pcm_params.sfreq; + case SST_CODEC_TYPE_MP3: + return str_param->sparams.uc.mp3_params.sfreq; + case SST_CODEC_TYPE_AAC: + return str_param->sparams.uc.aac_params.sfreq;; + case SST_CODEC_TYPE_WMA9: + return str_param->sparams.uc.wma_params.sfreq;; + default: + return 0; + } +} + +/* + * sst_get_stream - this function prepares for stream allocation + */ +static int sst_get_stream(struct snd_sst_params *str_param, u32 pvt_id) +{ + int i, retval; + struct stream_info *str_info; + + /* stream is not allocated, we are allocating */ + i = sst_get_block_stream(sst_drv_ctx); + pr_debug("alloc block allocated = %d\n", i); + if (i < 0) + return -ENOMEM; + sst_drv_ctx->alloc_block[i].sst_id = pvt_id; + + /*To check if LPE is in stalled state.*/ + while (sst_drv_ctx->lpe_stalled) + ; + retval = sst_get_stream_allocated(str_param, i, pvt_id); + if (retval == -(SST_LIB_ERR_LIB_DNLD_REQUIRED)) { + /* codec download is required */ + struct snd_sst_alloc_response *response = + sst_drv_ctx->alloc_block[i].ops_block.data; + pr_debug("Codec is required.... trying that\n"); + retval = sst_load_library(&response->lib_dnld, + str_param->ops, pvt_id); + kfree(response); + + if (!retval) { + pr_debug("codec was downloaded sucesfully \n"); + pr_debug("try alloac again\n"); + sst_drv_ctx->alloc_block[i].ops_block.condition = false; + + retval = sst_get_stream_allocated(str_param, i, pvt_id); + + if (retval <= 0) + goto err; + set_port_params(str_param, str_param->ops); + + pr_debug("Allocation done stream id %d \n", retval); + } else { + pr_debug("codec download failed \n"); + retval = -EIO; + goto err; + } + } else if (retval <= 0) + goto err; + else + set_port_params(str_param, str_param->ops); + + /* store sampling freq */ + str_info = &sst_drv_ctx->streams[retval]; + str_info->sfreq = sst_get_sfreq(str_param); + + /* power on the analog, if reqd */ + if (str_param->ops == STREAM_OPS_PLAYBACK || + str_param->ops == STREAM_OPS_PLAYBACK_DRM) { + if (sst_drv_ctx->pb_streams == 0) + sst_drv_ctx->scard_ops->power_up_pmic_pb(); + sst_drv_ctx->pb_streams++; + } else if (str_param->ops == STREAM_OPS_CAPTURE) { + if (sst_drv_ctx->cp_streams == 0) + sst_drv_ctx->scard_ops->power_up_pmic_cp(); + sst_drv_ctx->cp_streams++; + } + +err: + sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; + return retval; +} + +/** +* intel_sst_ioctl - recieves the device ioctl's +* @i_node: inode structure +* @file_ptr: pointer to file +* @cmd: Ioctl cmd +* @arg: data +* +* This function is called by OS when a user space component +* sends an Ioctl to SST driver +*/ +int intel_sst_ioctl(struct inode *i_node, struct file *file_ptr, + unsigned int cmd, unsigned long arg) +{ + int retval = 0; + struct ioctl_pvt_data *data = NULL; + int str_id = 0, minor = 0; + dev_t device = i_node->i_rdev; + + if (device == MKDEV(INTEL_SST_MAJOR, 0)) { + minor = 0; + data = (struct ioctl_pvt_data *) + file_ptr->private_data; + str_id = data->str_id; + } else if (device == MKDEV(INTEL_SST_MAJOR, 1)) + minor = 1; + else + return -EINVAL; + + if (sst_drv_ctx->sst_state != SST_FW_RUNNING) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: SST +\ + Not runng %d\n " ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, sst_drv_ctx->sst_state); + return -EBUSY; + } + + switch (_IOC_NR(cmd)) { + case _IOC_NR(SNDRV_SST_STREAM_PAUSE): + pr_debug("SNDRV_SST_IOCTL_PAUSE recieved for %d!\n", str_id); + if (minor != STREAM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: called for AM +\ + hdle minor%d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + retval = sst_pause_stream(str_id); + break; + + case _IOC_NR(SNDRV_SST_STREAM_RESUME): + pr_debug("SNDRV_SST_IOCTL_RESUME recieved!\n"); + if (minor != STREAM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: caled for +\ + AM hndl minor %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + retval = sst_resume_stream(str_id); + break; + + case _IOC_NR(SNDRV_SST_STREAM_SET_PARAMS): { + struct snd_sst_params *str_param = (struct snd_sst_params *)arg; + + pr_debug("IOCTL_SET_PARAMS recieved!\n"); + if (minor != STREAM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: caled for AM +\ + hndl minor %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + sst_print_params(str_param); + + if (!str_id) { + retval = sst_get_stream(str_param, data->pvt_id); + if (retval > 0) { + struct stream_info *str_info; + sst_drv_ctx->stream_cnt++; + data->str_id = retval; + str_info = &sst_drv_ctx->streams[retval]; + str_info->src = SST_DRV; + retval = copy_to_user(&str_param->stream_id, + &retval, sizeof(__u32)); + } else { + if (retval == -SST_ERR_INVALID_PARAMS) + retval = -EINVAL; + } + } else { + pr_debug("SET_STREAM_PARAMS recieved!\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; + sfreq = str_param->sparams.uc.pcm_params.sfreq; + word_size = str_param->sparams. + uc.pcm_params.pcm_wd_sz; + if (str_param->ops == STREAM_OPS_CAPTURE) { + pr_debug("SST sampling frequency= %d\n", + sfreq); + sst_drv_ctx->scard_ops->\ + set_pcm_audio_params(sfreq, word_size); + } + } + } + break; + } + case _IOC_NR(SNDRV_SST_SET_VOL): { + struct snd_sst_vol *set_vol; + struct snd_sst_vol *rec_vol = (struct snd_sst_vol *)arg; + pr_debug("SNDRV_SST_SET_VOLUME recieved for %d!\n", + rec_vol->stream_id); + if (minor == STREAM_MODULE && rec_vol->stream_id == 0) { + pr_debug("invalid operation!\n"); + retval = -EPERM; + break; + } + set_vol = kzalloc(sizeof(*set_vol), GFP_ATOMIC); + if (!set_vol) { + pr_debug("mem allocation failed\n"); + retval = -ENOMEM; + break; + } + retval = copy_from_user(set_vol, rec_vol, sizeof(*set_vol)); + if (retval) { + pr_debug("copy failed\n"); + retval = -EAGAIN; + break; + } + retval = sst_set_vol(set_vol); + kfree(set_vol); + break; + } + case _IOC_NR(SNDRV_SST_GET_VOL): { + struct snd_sst_vol *rec_vol = (struct snd_sst_vol *)arg; + struct snd_sst_vol get_vol; + pr_debug("IOCTL_GET_VOLUME recieved for stream = %d!\n", + rec_vol->stream_id); + if (minor == STREAM_MODULE && rec_vol->stream_id == 0) { + pr_debug("invalid operation!\n"); + retval = -EPERM; + break; + } + get_vol.stream_id = rec_vol->stream_id; + retval = sst_get_vol(&get_vol); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Get volume failed = %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + retval = -EIO; + break; + } + pr_debug("id = %d\n, vol = %d, ramp_dur = %d, ramp_type=%d\n", + get_vol.stream_id, get_vol.volume, + get_vol.ramp_duration, get_vol.ramp_type); + retval = copy_to_user((struct snd_sst_vol *)arg, + &get_vol, sizeof(get_vol)); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: copy to user failed %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + retval = -EIO; + break; + } + /*sst_print_get_vol_info(str_id, &get_vol);*/ + break; + } + + case _IOC_NR(SNDRV_SST_MUTE): { + struct snd_sst_mute *set_mute; + struct snd_sst_vol *rec_mute = (struct snd_sst_vol *)arg; + pr_debug("SNDRV_SST_SET_VOLUME recieved for %d!\n", + rec_mute->stream_id); + if (minor == STREAM_MODULE && rec_mute->stream_id == 0) { + pr_debug("invalid operation!\n"); + retval = -EPERM; + break; + } + set_mute = kzalloc(sizeof(*set_mute), GFP_ATOMIC); + if (!set_mute) { + pr_debug("mem allocation failed\n"); + retval = -ENOMEM; + break; + } + retval = copy_from_user(set_mute, rec_mute, sizeof(*set_mute)); + if (retval) { + pr_debug("copy failed\n"); + retval = -EAGAIN; + break; + } + retval = sst_set_mute(set_mute); + kfree(set_mute); + break; + } + case _IOC_NR(SNDRV_SST_STREAM_GET_PARAMS): { + struct snd_sst_get_stream_params get_params; + + pr_debug("IOCTL_GET_PARAMS recieved!\n"); + if (minor != 0) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: called for +\ + AM handle minor %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + + retval = sst_get_stream_params(str_id, &get_params); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Get +\ + arams failed = %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + retval = -EIO; + break; + } + retval = copy_to_user((struct snd_sst_get_stream_params *)arg, + &get_params, sizeof(get_params)); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: copy to +\ + user failed %d\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + retval = -EIO; + break; + } + sst_print_stream_params(&get_params); + break; + } + + case _IOC_NR(SNDRV_SST_MMAP_PLAY): + case _IOC_NR(SNDRV_SST_MMAP_CAPTURE): + pr_debug("SNDRV_SST_MMAP_PLAY/CAPTURE recieved!\n"); + if (minor != STREAM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: called for AM +\ + handle minor %d\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + retval = intel_sst_mmap_play_capture(str_id, + (struct snd_sst_mmap_buffs *)arg); + break; + + case _IOC_NR(SNDRV_SST_STREAM_DROP): + pr_debug("SNDRV_SST_IOCTL_DROP recieved!\n"); + if (minor != STREAM_MODULE) { + retval = -EINVAL; + break; + } + retval = sst_drop_stream(str_id); + break; + + case _IOC_NR(SNDRV_SST_STREAM_GET_TSTAMP): { + unsigned long long *ms = (unsigned long long *)arg; + struct snd_sst_tstamp tstamp = {0}; + unsigned long long time, freq, mod; + + pr_debug("SNDRV_SST_STREAM_GET_TSTAMP recieved!\n"); + if (minor != STREAM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: called for AM +\ + handle minor %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + memcpy_fromio(&tstamp, + ((void *)(sst_drv_ctx->mailbox + SST_TIME_STAMP) + +(str_id * sizeof(tstamp))), + sizeof(tstamp)); + time = tstamp.samples_rendered; + pr_debug("samples rendered! = 0x%llx\n", time); + freq = (unsigned long long) tstamp.sampling_frequency; + pr_debug("freq = %llx\n", freq); + time = time * 1000; /* converting it to ms */ + mod = do_div(time, freq); + pr_debug("mod = 0x%llx\n", mod); + pr_debug("msec = 0x%llx\n", time); + retval = copy_to_user(ms, &time, sizeof(*ms)); + if (retval) + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: copy failed = %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + break; + } + + case _IOC_NR(SNDRV_SST_STREAM_START):{ + struct stream_info *stream; + + pr_debug("SNDRV_SST_STREAM_START recieved!\n"); + if (minor != STREAM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: called for AM +\ + handle minor %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + retval = sst_validate_strid(str_id); + if (retval) + break; + stream = &sst_drv_ctx->streams[str_id]; + mutex_lock(&stream->lock); + if (stream->status == STREAM_INIT && + stream->need_draining != true) { + pr_debug("calling play frames...\n"); + stream->prev = stream->status; + stream->status = STREAM_RUNNING; + if (stream->ops == STREAM_OPS_PLAYBACK || + stream->ops == STREAM_OPS_PLAYBACK_DRM) { + retval = sst_play_frame(str_id); +#ifdef CONFIG_MSTWN_POWER_MGMT + sst_ospm_send_event( + OSPM_EVENT_SUBSYS_START_PLAY); +#endif + } else if (stream->ops == STREAM_OPS_CAPTURE) + retval = sst_capture_frame(str_id); + else { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Invalid +\ + ops 0x%x\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + stream->ops); + retval = -EINVAL; + mutex_unlock( + &sst_drv_ctx->streams[str_id].lock); + break; + } + if (retval < 0) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: play/cptur frame fail \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + stream->status = STREAM_INIT; + mutex_unlock( + &sst_drv_ctx->streams[str_id].lock); + break; + } + } else { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Inv strt for +\ + stream%d state0x%x\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id, \ + stream->status); + retval = -EINVAL; + } + mutex_unlock(&sst_drv_ctx->streams[str_id].lock); + break; + } + + case _IOC_NR(SNDRV_SST_SET_TARGET_DEVICE): { + struct snd_sst_target_device *target_device; + + pr_debug("SNDRV_SST_SET_TARGET_PLAYBACK DEVICE recieved!\n"); + target_device = (struct snd_sst_target_device *)arg; + BUG_ON(!target_device); + if (minor != AM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: called for non +\ + AM handle minor %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + retval = sst_target_device_select(target_device); + break; + } + + case _IOC_NR(SNDRV_SST_DRIVER_INFO): { + struct snd_sst_driver_info *info = + (struct snd_sst_driver_info *)arg; + + pr_debug("SNDRV_SST_DRIVER_INFO recived \n"); + info->version = SST_VERSION_NUM; + /* hard coding, shud get sumhow later */ + info->active_pcm_streams = sst_drv_ctx->stream_cnt - + sst_drv_ctx->encoded_cnt; + info->active_enc_streams = sst_drv_ctx->encoded_cnt; + info->max_pcm_streams = MAX_ACTIVE_STREAM - MAX_ENC_STREAM; + info->max_enc_streams = MAX_ENC_STREAM; + info->buf_per_stream = sst_drv_ctx->mmap_len; + break; + } + + case _IOC_NR(SNDRV_SST_STREAM_DECODE): { + struct snd_sst_dbufs *param = + (struct snd_sst_dbufs *)arg, dbufs_local; + int i; + struct snd_sst_buffs ibufs, obufs; + struct snd_sst_buff_entry ibuf_temp[param->ibufs->entries], + obuf_temp[param->obufs->entries]; + + pr_debug("SNDRV_SST_STREAM_DECODE recived \n"); + if (minor != STREAM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: called for AM +\ + handle minor %d\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + if (!param) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: null param passed\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + retval = -EINVAL; + break; + } + + dbufs_local.input_bytes_consumed = param->input_bytes_consumed; + dbufs_local.output_bytes_produced = + param->output_bytes_produced; + dbufs_local.ibufs = &ibufs; + dbufs_local.obufs = &obufs; + dbufs_local.ibufs->entries = param->ibufs->entries; + dbufs_local.ibufs->type = param->ibufs->type; + dbufs_local.obufs->entries = param->obufs->entries; + dbufs_local.obufs->type = param->obufs->type; + + dbufs_local.ibufs->buff_entry = ibuf_temp; + for (i = 0; i < dbufs_local.ibufs->entries; i++) { + ibuf_temp[i].buffer = + param->ibufs->buff_entry[i].buffer; + ibuf_temp[i].size = + param->ibufs->buff_entry[i].size; + } + dbufs_local.obufs->buff_entry = obuf_temp; + for (i = 0; i < dbufs_local.obufs->entries; i++) { + obuf_temp[i].buffer = + param->obufs->buff_entry[i].buffer; + obuf_temp[i].size = + param->obufs->buff_entry[i].size; + } + retval = sst_decode(str_id, &dbufs_local); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: decoding failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + retval = -EAGAIN; + } + retval = copy_to_user(¶m->input_bytes_consumed, + &dbufs_local.input_bytes_consumed, + sizeof(unsigned long long)); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: copy to user failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + retval = -EAGAIN; + break; + } + retval = copy_to_user(¶m->output_bytes_produced, + &dbufs_local.output_bytes_produced, + sizeof(unsigned long long)); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: copy to user failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + retval = -EAGAIN; + break; + } + pr_debug("input_bytes_consumed=%lld\n", + param->input_bytes_consumed); + pr_debug("output_bytes_produced=%lld\n", + param->output_bytes_produced); + pr_debug("ibufs->entries=%d\n", param->ibufs->entries); + pr_debug("input_consumed = %lld, output_produced = %lld \n", + param->input_bytes_consumed, + param->output_bytes_produced); + pr_debug("first ibufs size=%d\n", + param->ibufs->buff_entry[0].size); + pr_debug("first ibufs addr=%p\n", + param->ibufs->buff_entry[0].buffer); + pr_debug("obufs->entries=%d\n", param->obufs->entries); + pr_debug("first obufs size=%d\n", + param->obufs->buff_entry[0].size); + pr_debug("first obufs addr=%p\n", + param->obufs->buff_entry[0].buffer); + break; + } + + case _IOC_NR(SNDRV_SST_STREAM_DRAIN): + pr_debug("SNDRV_SST_STREAM_DRAIN recived \n"); + if (minor != STREAM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: caled for AM +\ + hndle minr %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, minor); + retval = -EINVAL; + break; + } + retval = sst_drain_stream(str_id); + break; + + case _IOC_NR(SNDRV_SST_STREAM_BYTES_DECODED): { + unsigned long long *bytes = (unsigned long long *)arg; + struct snd_sst_tstamp tstamp = {0}; + + pr_debug("SNDRV_SST_STREAM_BYTES_DECODED recieved!\n"); + if (minor != STREAM_MODULE) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: caled for +\ + AM hndle minr %d\n",\ + get_seconds(), sst_get_time(), __func__ , \ + __LINE__, minor); + retval = -EINVAL; + break; + } + memcpy_fromio(&tstamp, + ((void *)(sst_drv_ctx->mailbox + SST_TIME_STAMP) + +(str_id * sizeof(tstamp))), + sizeof(tstamp)); + retval = copy_to_user(bytes, &tstamp.bytes_processed, + sizeof(*bytes)); + pr_debug("bytes processed =%lld\n", tstamp.bytes_processed); + if (retval) + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: copy +\ + failed = %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + break; + } + case _IOC_NR(SNDRV_SST_FW_INFO): { + struct snd_sst_fw_info *fw_info; + + pr_debug("SNDRV_SST_FW_INFO recived \n"); + + fw_info = kzalloc(sizeof(*fw_info), GFP_ATOMIC); + if (!fw_info) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: mem +\ + alocation fail\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + retval = -ENOMEM; + break; + } + retval = sst_get_fw_info(fw_info); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: sst_get_fw_info +\ + fail = %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + kfree(fw_info); + break; + } + retval = copy_to_user((struct snd_sst_dbufs *)arg, + fw_info, sizeof(*fw_info)); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: copy to user failed %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + kfree(fw_info); + retval = -EIO; + break; + } + sst_print_fw_info(fw_info); + kfree(fw_info); + break; + } + default: + pr_debug("IOCTL not supported yet !\n"); + retval = -ENOTTY; + } + pr_debug("...complete ret code = %d\n", retval); + + return retval; +} + +/* + Intelmid driver interface Routines +*/ + +void sst_process_mad_ops(struct work_struct *work) +{ + struct mad_ops_wq *mad_ops = + container_of(work, struct mad_ops_wq, wq); + int retval = 0; + struct stream_info *stream; + + switch (mad_ops->control_op) { + case SST_SND_PAUSE: + retval = sst_pause_stream(mad_ops->stream_id); + break; + case SST_SND_RESUME: + retval = sst_resume_stream(mad_ops->stream_id); + break; + case SST_SND_DROP: + retval = sst_drop_stream(mad_ops->stream_id); + break; + case SST_SND_STREAM_PROCESS: + pr_debug("play/capt frames...\n"); + stream = &sst_drv_ctx->streams[mad_ops->stream_id]; + if (stream->status == STREAM_UN_INIT) + return; + stream->prev = stream->status; + stream->status = STREAM_RUNNING; + stream->data_blk.on = false; + if (stream->ops == STREAM_OPS_PLAYBACK) + retval = sst_play_frame(mad_ops->stream_id); + else if (stream->ops == STREAM_OPS_CAPTURE) + retval = sst_capture_frame(mad_ops->stream_id); + else + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: invalid stream +\ + ops invoked \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + if (retval < 0) + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: play/captur frames +\ + failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + break; + default: + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: wrong +\ + control_ops reported\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + } + return; +} +/** +* sst_control_set - Set Control params +* @control_list: list of controls to be set +* +* This function is called by MID sound card driver to set +* SST/Sound card controls. This is registered with MID driver +*/ +int sst_control_set(int control_element, void *value) +{ + int retval = 0, str_id = 0; + struct stream_info *stream; + + if (sst_drv_ctx->sst_state == SST_UN_INIT) { + /* FW is not downloaded */ + pr_debug("DSP Downloading FW now...\n"); + retval = sst_download_fw(); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: FW download +\ + failed = 0x%x, abort\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, retval); + return retval; + } + } + + switch (control_element) { + case SST_SND_ALLOC: { + struct snd_sst_params *str_param; + int pcm_id = sst_assign_pvt_id(sst_drv_ctx); + struct stream_info *str_info; + + str_param = (struct snd_sst_params *)value; + BUG_ON(!str_param); + sst_print_params(str_param); + retval = sst_get_stream(str_param, pcm_id); + if (retval >= 0) + sst_drv_ctx->stream_cnt++; +#ifdef CONFIG_MSTWN_POWER_MGMT + if (str_param->ops == STREAM_OPS_PLAYBACK || + str_param->ops == STREAM_OPS_PLAYBACK_DRM) + sst_ospm_send_event(OSPM_EVENT_SUBSYS_START_PLAY); +#endif + str_info = &sst_drv_ctx->streams[retval]; + str_info->src = MAD_DRV; + break; + } + + case SST_SND_PAUSE: + case SST_SND_RESUME: + case SST_SND_DROP: + sst_drv_ctx->mad_ops.control_op = control_element; + sst_drv_ctx->mad_ops.stream_id = *(int *)value; + queue_work(sst_drv_ctx->mad_wq, &sst_drv_ctx->mad_ops.wq); + break; + + case SST_SND_FREE: + str_id = *(int *)value; + stream = &sst_drv_ctx->streams[str_id]; + free_stream_context(str_id); + stream->pcm_substream = NULL; + stream->period_elapsed = NULL; + sst_drv_ctx->stream_cnt--; + break; + + case SST_SND_STREAM_INIT: { + struct pcm_stream_info *str_info; + struct stream_info *stream; + + pr_debug("stream init called\n"); + str_info = (struct pcm_stream_info *)value; + str_id = str_info->str_id; + retval = sst_validate_strid(str_id); + if (retval) + break; + + stream = &sst_drv_ctx->streams[str_id]; + pr_debug("setting the period ptrs\n"); + stream->pcm_substream = str_info->mad_substream; + stream->period_elapsed = str_info->period_elapsed; + stream->sfreq = str_info->sfreq; + stream->prev = stream->status; + stream->status = STREAM_INIT; + break; + } + + case SST_SND_BUFFER_POINTER: { + struct pcm_stream_info *stream_info; + struct snd_sst_tstamp fw_tstamp = {0,}; + struct stream_info *stream; + + pr_debug("buffer pointer query\n"); + + stream_info = (struct pcm_stream_info *)value; + str_id = stream_info->str_id; + retval = sst_validate_strid(str_id); + if (retval) + break; + stream = &sst_drv_ctx->streams[str_id]; + + if (!stream->pcm_substream) + break; + memcpy_fromio(&fw_tstamp, + ((void *)(sst_drv_ctx->mailbox + SST_TIME_STAMP) + +(str_id * sizeof(fw_tstamp))), + sizeof(fw_tstamp)); + + pr_debug("strid = %d\n", str_id); + + if (stream->ops == STREAM_OPS_PLAYBACK) + stream_info->buffer_ptr = fw_tstamp.samples_rendered; + else + stream_info->buffer_ptr = fw_tstamp.samples_processed; + pr_debug("samples played = %lld\n", + stream_info->buffer_ptr); + break; + } + + default: + /* Illegal case */ + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: illegal req\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + pr_debug("...complete ret code = %d\n", retval); + + return retval; +} + + +/** +* sst_send_data_to_HW - send data buffers +* @buffer_data: user buffer +* +* This function is called by MID sound card driver to send buffer +* to HW. This is registered with MID driver +*/ +int sst_send_buffer_to_HW(int str_id, struct stream_buffer *mad_buf) +{ + /* recvd a buffer map it to stream */ + /* this is a PCM stream and playback */ + int retval = 0; + bool flag_add = false; + struct sst_stream_bufs *sst_buf = NULL, *_sst_buf; + struct stream_info *stream; + + if (!mad_buf || !mad_buf->addr || !mad_buf->length) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Null Ptr +\ + or buf size = 0\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + + if (sst_drv_ctx->sst_state != SST_FW_RUNNING) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: SST +\ + Not runng: %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + sst_drv_ctx->sst_state); + return -EBUSY; + } + + retval = sst_validate_strid(str_id); + if (retval < 0) + return -EINVAL; + + stream = &sst_drv_ctx->streams[str_id]; + pr_debug("stream status = %d strid=%d\n", stream->status, str_id); + pr_debug("stream codec = %d, prevstate=%d\n", + stream->codec, stream->prev); + if (stream->status == STREAM_UN_INIT) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: BAD REQUEST!\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EBADRQC; + } + pr_debug("received addr=0x%x size = 0x%x\n", + (unsigned int)mad_buf->addr, mad_buf->length); + /* list is not empty */ + list_for_each_entry_safe(sst_buf, _sst_buf, &stream->bufs, node) { + if (sst_buf->in_use == true) + continue; + else if ((int) mad_buf->addr != + (int)sst_buf->addr + sst_buf->size) + continue; + else { + sst_buf->size += mad_buf->length; + flag_add = true; + pr_debug("inc addr = 0x%p, base = 0x%x inc_val = 0x%x\n", + sst_buf->addr, sst_buf->size, mad_buf->length); + break; + } + } + + if (flag_add == false) { + sst_buf = kzalloc(sizeof(*sst_buf), GFP_ATOMIC); + if (!sst_buf) + return -ENOMEM; + sst_buf->size = mad_buf->length; + sst_buf->addr = (void *)mad_buf->addr; + sst_buf->offset = 0; + sst_buf->in_use = false; + /*adding without locking FIXME*/ + list_add_tail(&sst_buf->node, &stream->bufs); + flag_add = true; + pr_debug("entry added addr = 0x%x size = 0x%x\n", + (unsigned int)mad_buf->addr, mad_buf->length); + } + + if (stream->status == STREAM_INIT) { + sst_drv_ctx->mad_ops.control_op = SST_SND_STREAM_PROCESS; + sst_drv_ctx->mad_ops.stream_id = str_id; + queue_work(sst_drv_ctx->mad_wq, &sst_drv_ctx->mad_ops.wq); + } + + return retval; +} + +struct intel_sst_card_ops sst_pmic_ops = { + .control_set = sst_control_set, + .send_buffer = sst_send_buffer_to_HW, +}; + +/** +* register_sst_card- function for sound card to register +* @card: pointer to structure of operations +* This function is called card driver loads and is ready for registration +*/ +int register_sst_card(struct intel_sst_card_ops *card) +{ + + if (!card || !card->module_name) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Null Pointer Passed\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + + if (sst_drv_ctx->pmic_state == SND_MAD_UN_INIT) { + /* register this driver */ + if ((strncmp(SST_CARD_NAMES, card->module_name, + strlen(SST_CARD_NAMES))) == 0) { + sst_drv_ctx->pmic_vendor = card->vendor_id; + sst_drv_ctx->scard_ops = card->scard_ops; + sst_pmic_ops.module_name = card->module_name; + sst_drv_ctx->pmic_state = SND_MAD_INIT_DONE; + card->control_set = sst_pmic_ops.control_set; + card->send_buffer = sst_pmic_ops.send_buffer; + sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT; + /* initialize card to know good state */ + /*sst_drv_ctx->scard_ops->init_card();*/ + return 0; + } else { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: strcmp failed %s \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + card->module_name); + return -EINVAL; + } + + } else { + /* already registered a driver */ + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Repeat for +\ + register..denied\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EBADRQC; + } + return 0; +} +EXPORT_SYMBOL_GPL(register_sst_card); + +/** +* unregister_sst_card- function for sound card to un-register +* @card: pointer to structure of operations +* This function is called when card driver unloads +*/ +void unregister_sst_card(struct intel_sst_card_ops *card) +{ + if (sst_pmic_ops.module_name == card->module_name) { + /* unreg */ + sst_pmic_ops.module_name = ""; + sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; + pr_debug("Unregistered %s\n", card->module_name); + } + return; +} +EXPORT_SYMBOL_GPL(unregister_sst_card); + +/** +* lpe_mask_periphral_intr- function to mask SST DSP peripheral interrupt +* @device: device interrupt that needs masking +*/ +int lpe_mask_periphral_intr(enum lpe_periphral device) +{ + union sst_pimr_reg pimr = {{0},}; + if (!sst_drv_ctx) + return -EIO; + + pimr.full = readl(sst_drv_ctx->shim + SST_PIMR); + + switch (device) { + case LPE_DMA: + pimr.part.dmac_sc = 1; + /* dummy register for shim workaround */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(pimr.full, sst_drv_ctx->shim + SST_PIMR); + break; + + case LPE_SSP0: + break; + + case LPE_SSP1: + break; + + default: + break; + } + return 0; +} +EXPORT_SYMBOL_GPL(lpe_mask_periphral_intr); + +/** +* lpe_unmask_periphral_intr- function to unmask SST DSP peripheral interrupt +* @device: device interrupt that needs unmasking +*/ +int lpe_unmask_periphral_intr(enum lpe_periphral device) +{ + union sst_pimr_reg pimr = {{0},}; + if (!sst_drv_ctx) + return -EIO; + + pimr.full = readl(sst_drv_ctx->shim + SST_PIMR); + + switch (device) { + case LPE_DMA: + pimr.part.dmac_sc = 0; + /* dummy register for shim workaround */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(pimr.full, sst_drv_ctx->shim + SST_PIMR); + break; + + case LPE_SSP0: + break; + + case LPE_SSP1: + break; + + default: + break; + } + return 0; + +} +EXPORT_SYMBOL_GPL(lpe_unmask_periphral_intr); + +/** +* lpe_periphral_intr_status- function returns SST peripheral interrupt status +* @device: device for which the status is enquired +* @status: out parameters with the status of the peripheral device +*/ +int lpe_periphral_intr_status(enum lpe_periphral device, int *status) +{ + union sst_pisr_reg pisr = {{0},}; + if (!sst_drv_ctx) + return -EIO; + + pisr.full = readl(sst_drv_ctx->shim + SST_PISR); + + switch (device) { + case LPE_DMA: + *status = pisr.part.dmac; + break; + + case LPE_SSP0: + break; + + case LPE_SSP1: + break; + + default: + break; + } + return 0; +} +EXPORT_SYMBOL_GPL(lpe_periphral_intr_status); Index: linux-2.6.33/sound/pci/sst/intel_sst_fw_ipc.h =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intel_sst_fw_ipc.h @@ -0,0 +1,398 @@ +#ifndef __INTEL_SST_FW_IPC_H__ +#define __INTEL_SST_FW_IPC_H__ +/* +* intel_sst_fw_ipc.h - Intel SST Driver for audio engine +* +* Copyright (C) 2008-09 Intel Corporation +* Author: Vinod Koul <vinod.koul@intel.com> +* Harsha Priya <priya.harsha@intel.com> +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; version 2 of the License. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +* +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* +* This driver exposes the audio engine functionalities to the ALSA +* and middleware. +* This file has definitions shared between the firmware and driver +*/ + +#define MAX_NUM_STREAMS 4 +#define MAX_DBG_RW_BYTES 80 +#define MAX_NUM_SCATTER_BUFFERS 8 +#define MAX_LOOP_BACK_DWORDS 8 +/* IPC base address and mailbox, timestamp offsets */ +#define SST_MAILBOX_SIZE 0x0400 +#define SST_MAILBOX_SEND 0x0000 +#define SST_MAILBOX_RCV 0x0804 +#define SST_TIME_STAMP 0x1800 +#define SST_RESERVED_OFFSET 0x1840 +#define SST_CHEKPOINT_OFFSET 0x1C00 +#define REPLY_MSG 0x80 + +/* Message ID's for IPC messages */ +/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */ + +/* I2L Firmware/Codec Download msgs */ +#define IPC_IA_PREP_LIB_DNLD 0x01 +#define IPC_IA_LIB_DNLD_CMPLT 0x02 + +#define IPC_IA_SET_PMIC_TYPE 0x03 +#define IPC_IA_GET_FW_VERSION 0x04 +#define IPC_IA_GET_FW_BUILD_INF 0x05 +#define IPC_IA_GET_FW_INFO 0x06 + +/* I2L Codec Config/control msgs */ +#define IPC_IA_SET_CODEC_PARAMS 0x10 +#define IPC_IA_GET_CODEC_PARAMS 0x11 +#define IPC_IA_SET_PPP_PARAMS 0x12 +#define IPC_IA_GET_PPP_PARAMS 0x13 +#define IPC_IA_PLAY_FRAMES 0x14 +#define IPC_IA_CAPT_FRAMES 0x15 +#define IPC_IA_PLAY_VOICE 0x16 +#define IPC_IA_CAPT_VOICE 0x17 +#define IPC_IA_DECODE_FRAMES 0x18 + +/* I2L Stream config/control msgs */ +#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */ +#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */ +#define IPC_IA_SET_STREAM_PARAMS 0x22 +#define IPC_IA_GET_STREAM_PARAMS 0x23 +#define IPC_IA_PAUSE_STREAM 0x24 +#define IPC_IA_RESUME_STREAM 0x25 +#define IPC_IA_DROP_STREAM 0x26 +#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */ +#define IPC_IA_TARGET_DEV_SELECT 0x28 +#define IPC_IA_CONTROL_ROUTING 0x29 + +#define IPC_IA_SET_STREAM_VOL 0x2A /*Vol for stream, pre mixer */ +#define IPC_IA_GET_STREAM_VOL 0x2B +#define IPC_IA_SET_STREAM_MUTE 0x2C +#define IPC_IA_GET_STREAM_MUTE 0x2D +#define IPC_IA_SET_MASTER_VOL 0x2E /* set vol for post mixer */ +#define IPC_IA_GET_MASTER_VOL 0x2F /* Get Volume for post mixer */ +#define IPC_IA_SET_MASTER_MUTE 0x30 /* Set Master Mute post mixer */ +#define IPC_IA_GET_MASTER_MUTE 0x31 /* Get Master Mute; post mixer */ + +/* Debug msgs */ +#define IPC_IA_DBG_MEM_READ 0x40 +#define IPC_IA_DBG_MEM_WRITE 0x41 +#define IPC_IA_DBG_LOOP_BACK 0x42 + +/* L2I Firmware/Codec Download msgs */ +#define IPC_IA_FW_INIT_CMPLT 0x81 +#define IPC_IA_LPE_GETTING_STALLED 0x82 +#define IPC_IA_LPE_UNSTALLED 0x83 + +/* L2I Codec Config/control msgs */ +#define IPC_SST_GET_PLAY_FRAMES 0x90 /* Request IA more data */ +#define IPC_SST_GET_CAPT_FRAMES 0x91 /* Request IA more data */ +#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */ +#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */ +#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */ +#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */ +#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */ +#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */ +#define IPC_IA_TARGET_DEV_CHNGD 0x98 /* error in processing a stream */ + +/* L2S messages */ +#define IPC_SC_DDR_LINK_UP 0xC0 +#define IPC_SC_DDR_LINK_DOWN 0xC1 + +/* L2I Error reporting msgs */ +#define IPC_IA_MEM_ALLOC_FAIL 0xE0 +#define IPC_IA_PROC_ERR 0xE1 /* error in processing a + stream can be used by playback and + capture modules */ + +/* L2I Debug msgs */ +#define IPC_IA_PRINT_STRING 0xF0 + +/* Command Response or Acknowledge message to any IPC message will have + * same message ID and stream ID information which is sent. + * There is no specific Ack message ID. The data field is used as response + * meaning. + */ +enum ackData { + IPC_ACK_SUCCESS = 0, + IPC_ACK_FAILURE +}; + + +enum sst_error_codes { + /* Error code,response to msgId: Description */ + /* Common error codes */ + SST_SUCCESS = 0, /* Success */ + SST_ERR_INVALID_STREAM_ID, /* Invalid stream ID */ + SST_ERR_INVALID_MSG_ID, /* Invalid message ID */ + SST_ERR_INVALID_STREAM_OP, /* Invalid stream operation request */ + SST_ERR_INVALID_PARAMS, /* Invalid params */ + SST_ERR_INVALID_CODEC, /* Invalid codec type */ + SST_ERR_INVALID_MEDIA_TYPE, /* Invalid media type */ + SST_ERR_STREAM_ERR, /* ANY: Stream control or config or + processing error */ + + /* IPC specific error codes */ + SST_IPC_ERR_CALL_BACK_NOT_REGD, /* Call back for msg not regd */ + SST_IPC_ERR_STREAM_NOT_ALLOCATED, /* Stream is not allocated */ + SST_IPC_ERR_STREAM_ALLOC_FAILED, /* ALLOC:Stream alloc failed */ + SST_IPC_ERR_GET_STREAM_FAILED, /* ALLOC:Get stream id failed*/ + SST_ERR_MOD_NOT_AVAIL, /* SET/GET: Mod(AEC/AGC/ALC) not available */ + SST_ERR_MOD_DNLD_RQD, /* SET/GET: Mod(AEC/AGC/ALC) download required */ + SST_ERR_STREAM_STOPPED, /* ANY: Stream is in stopped state */ + SST_ERR_STREAM_IN_USE, /* ANY: Stream is already in use */ + + /* Capture specific error codes */ + SST_CAP_ERR_INCMPLTE_CAPTURE_MSG,/* ANY:Incomplete message */ + SST_CAP_ERR_CAPTURE_FAIL, /* ANY:Capture op failed */ + SST_CAP_ERR_GET_DDR_NEW_SGLIST, + SST_CAP_ERR_UNDER_RUN, /* lack of input data */ + SST_CAP_ERR_OVERFLOW, /* lack of output space */ + + /* Playback specific error codes*/ + SST_PB_ERR_INCMPLTE_PLAY_MSG, /* ANY: Incomplete message */ + SST_PB_ERR_PLAY_FAIL, /* ANY: Playback operation failed */ + SST_PB_ERR_GET_DDR_NEW_SGLIST, + + /* Codec manager specific error codes */ + SST_LIB_ERR_LIB_DNLD_REQUIRED, /* ALLOC: Codec download required */ + SST_LIB_ERR_LIB_NOT_SUPPORTED, /* Library is not supported */ + + /* Library manager specific error codes */ + SST_SCC_ERR_PREP_DNLD_FAILED, /* Failed to prepare for codec download */ + SST_SCC_ERR_LIB_DNLD_RES_FAILED, /* Lib download resume failed */ + /* Scheduler specific error codes */ + SST_SCH_ERR_FAIL, /* REPORT: */ + + /* DMA specific error codes */ + SST_DMA_ERR_NO_CHNL_AVAILABLE, /* DMA Ch not available */ + SST_DMA_ERR_INVALID_INPUT_PARAMS, /* Invalid input params */ + SST_DMA_ERR_CHNL_ALREADY_SUSPENDED, /* Ch is suspended */ + SST_DMA_ERR_CHNL_ALREADY_STARTED, /* Ch already started */ + SST_DMA_ERR_CHNL_NOT_ENABLED, /* Ch not enabled */ + SST_DMA_ERR_TRANSFER_FAILED, /* Transfer failed */ + SST_SSP_ERR_ALREADY_ENABLED, /* REPORT: SSP already enabled */ + SST_SSP_ERR_ALREADY_DISABLED, /* REPORT: SSP already disabled */ + SST_SSP_ERR_NOT_INITIALIZED, + + /* Other error codes */ + SST_ERR_MOD_INIT_FAIL, /* Firmware Module init failed */ + + /* FW init error codes */ + SST_RDR_ERR_IO_DEV_SEL_NOT_ALLOWED, + SST_RDR_ERR_ROUTE_ALREADY_STARTED, + SST_RDR_PREP_CODEC_DNLD_FAILED, + + /* Memory debug error codes */ + SST_ERR_DBG_MEM_READ_FAIL, + SST_ERR_DBG_MEM_WRITE_FAIL, + + /* Decode error codes */ + SST_ERR_DEC_NEED_INPUT_BUF, + +}; + +enum dbg_mem_data_type { + /* Data type of debug read/write */ + DATA_TYPE_U32, + DATA_TYPE_U16, + DATA_TYPE_U8, +}; + +/* CAUTION NOTE: All IPC message body must be multiple of 32 bits.*/ + +/* IPC Header */ +union ipc_header { + struct { + u32 msg_id:8; /* Message ID - Max 256 Message Types */ + u32 str_id:3; /* Undefined for SC communication */ + u32 large:1; /* Large Message if large = 1 */ + u32 reserved:4;/* Reserved for future use */ + u32 data:14; /* Ack/Info for msg, size of msg in Mailbox */ + u32 done:1; /* bit 30 */ + u32 busy:1; /* bit 31 */ + } part; + u32 full; +} __attribute__ ((packed)); + +struct ipc_header_fw_init { + struct snd_sst_fw_version fw_version;/* Firmware version details */ + u16 result; /* Fw init result */ + u8 module_id; /* Module ID in case of error */ + u8 debug_info; /* Debug info from Module ID in case of fail */ +} __attribute__ ((packed)); + +/* Firmware build info */ +struct sst_fw_build_info { + unsigned char date[16]; /* Firmware build date */ + unsigned char time[16]; /* Firmware build time */ +} __attribute__ ((packed)); + +/* Address and size info of a frame buffer in DDR */ +struct sst_address_info { + u32 addr; /* Address at IA */ + u32 size; /* Size of the buffer */ +} __attribute__ ((packed)); + +/* Time stamp */ +struct snd_sst_tstamp { + u64 samples_processed; /* capture - data in DDR */ + u64 samples_rendered; /* playback - data rendered */ + u64 bytes_processed; /* bytes decoded or encoded */ + u32 sampling_frequency; /* eg: 48000, 44100 */ + +}; + +/* Frame info to play or capture */ +struct sst_frame_info { + u16 num_entries; /* number of entries to follow */ + u16 rsrvd; + struct sst_address_info addr[MAX_NUM_SCATTER_BUFFERS]; +} __attribute__ ((packed)); + +/* Frames info for decode */ +struct snd_sst_decode_info { + unsigned long long input_bytes_consumed; + unsigned long long output_bytes_produced; + struct sst_frame_info frames_in; + struct sst_frame_info frames_out; +} __attribute__ ((packed)); +/* SST to IA print debug message*/ +struct ipc_sst_ia_print_params { + u32 string_size; /* Max value is 160 */ + u8 prt_string[160]; /* Null terminated Char string */ +} __attribute__ ((packed)); +/* Voice data message */ +struct snd_sst_voice_data { + u16 num_bytes; /* Number of valid voice data bytes */ + u8 pcm_wd_size; /* 0=8 bit, 1=16 bit 2=32 bit */ + u8 reserved; /* Reserved */ + u8 voice_data_buf[0]; /* Voice data buffer in bytes, little endian */ +} __attribute__ ((packed)); + +/* SST to IA memory read debug message */ +struct ipc_sst_ia_dbg_mem_rw { + u16 num_bytes; /* Maximum of MAX_DBG_RW_BYTES */ + u16 data_type; /* enum: dbg_mem_data_type */ + u32 address; /* Memory address of data memory of data_type */ + u8 rw_bytes[MAX_DBG_RW_BYTES];/* Maximum of 64 bytes can be RW */ +} __attribute__ ((packed)); + +struct ipc_sst_ia_dbg_loop_back { + u16 num_dwords; /* Maximum of MAX_DBG_RW_BYTES */ + u16 increment_val;/* Increments dwords by this value, 0- no increment */ + u32 lpbk_dwords[MAX_LOOP_BACK_DWORDS];/* Maximum of 8 dwords loopback */ +} __attribute__ ((packed)); + +/* Stream type params struture for Alloc stream */ +struct snd_sst_str_type { + u8 codec_type; /* Codec type */ + u8 str_type; /* 1 = voice 2 = music */ + u8 operation; /* Playback or Capture */ + u8 protected_str; /* 0=Non DRM, 1=DRM */ + u8 pvt_id; /* Driver Private ID */ + u8 reserved; /* Reserved */ + u16 result; /* Result used for acknowledgment */ +} __attribute__ ((packed)); + +/* Library info structure */ +struct module_info { + u32 lib_version; + u32 lib_type;/*TBD- KLOCKWORK u8 lib_type;*/ + u32 media_type; + u8 lib_name[12]; + u32 lib_caps; + unsigned char b_date[16]; /* Lib build date */ + unsigned char b_time[16]; /* Lib build time */ +} __attribute__ ((packed)); + +/* Library slot info */ +struct lib_slot_info { + u8 slot_num; /* 1 or 2 */ + u8 reserved1; + u16 reserved2; + u32 iram_size; /* slot size in IRAM */ + u32 dram_size; /* slot size in DRAM */ + u32 iram_offset; /* starting offset of slot in IRAM */ + u32 dram_offset; /* starting offset of slot in DRAM */ +} __attribute__ ((packed)); + +struct snd_sst_lib_download { + struct module_info lib_info; /* library info type, capabilities etc */ + struct lib_slot_info slot_info; /* slot info to be downloaded */ + u32 mod_entry_pt; +}; + +struct snd_sst_lib_download_info { + struct snd_sst_lib_download dload_lib; + u16 result; /* Result used for acknowledgment */ + u8 pvt_id; /* Private ID */ + u8 reserved; /* for alignment */ +}; + +/* Alloc stream params structure */ +struct snd_sst_alloc_params { + struct snd_sst_str_type str_type; + struct snd_sst_stream_params stream_params; +}; + +struct snd_sst_fw_get_stream_params { + struct snd_sst_stream_params codec_params; + struct snd_sst_pmic_config pcm_params; +}; + +/* Alloc stream response message */ +struct snd_sst_alloc_response { + struct snd_sst_str_type str_type; /* Stream type for allocation */ + struct snd_sst_lib_download lib_dnld; /* Valid only for codec dnld */ +}; + +/* Drop response */ +struct snd_sst_drop_response { + u32 result; + u32 bytes; +}; + +/* CSV Voice call routing structure */ +struct snd_sst_control_routing { + u8 control; /* 0=start, 1=Stop */ + u8 reserved[3]; /* Reserved- for 32 bit alignment */ +}; + + + +/* struct ipc_msg_body { + union { + CODEC_PARAM_STRUCTURES; + PPP_PARAM_STRUCTURES; + struct snd_sst_alloc_params alloc_params; + struct snd_sst_alloc_response alloc_response; + struct snd_sst_stream_params stream_params; + struct sst_frame_info frames_info; + struct ipc_sst_ia_print_params print_params; + struct ipc_sst_ia_dbg_mem_rw dbg_mem_rw; + struct ipc_sst_ia_dbg_loop_back loop_back; + struct pmic_pcm_params ssp_params; + } u; +};*/ + + + +struct ipc_post { + struct list_head node; + union ipc_header header; /* driver specific */ + char *mailbox_data; +}; + +#endif /* __INTEL_SST_FW_IPC_H__ */ Index: linux-2.6.33/sound/pci/sst/intel_sst_ipc.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intel_sst_ipc.c @@ -0,0 +1,827 @@ +/* + * intel_sst_ipc.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corporation + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file defines all ipc functions + */ + +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/firmware.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" + +/** +* Debug function to test basic IPC between driver and SST firmware +*/ +static void sst_send_loop_test(int loop_no) +{ + struct ipc_post *msg = NULL; + struct ipc_sst_ia_dbg_loop_back loop_msg; + static int large_num; + + pr_debug("Loop testing %d \n", loop_no); + + if (large_num >= 4) { + pr_debug("Loop testing complete.....\n"); + return; + } + if (loop_no >= 4) { + /* large loop */ + large_num++; + pr_debug("Large msg \n"); + if (sst_create_large_msg(&msg)) + return; + + loop_msg.increment_val = 1; + loop_msg.lpbk_dwords[0] = LOOP1; + loop_msg.lpbk_dwords[1] = LOOP2; + loop_msg.lpbk_dwords[2] = LOOP3; + loop_msg.lpbk_dwords[3] = LOOP4; + loop_msg.num_dwords = 4; + sst_fill_header(&msg->header, IPC_IA_DBG_LOOP_BACK, 1, loop_no); + msg->header.part.data = sizeof(u32) + sizeof(loop_msg); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), + &loop_msg, sizeof(loop_msg)); + } else { + /* short loop */ + pr_debug("Loop Short msg \n"); + if (sst_create_short_msg(&msg)) + return; + sst_fill_header(&msg->header, IPC_IA_DBG_LOOP_BACK, 0, loop_no); + } + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + return; +} + +/** +* this function sends the sound card type to sst dsp engine +*/ +static void sst_send_sound_card_type(void) +{ + struct ipc_post *msg = NULL; + + pr_debug("...called\n"); + + if (sst_create_short_msg(&msg)) + return; + + sst_fill_header(&msg->header, IPC_IA_SET_PMIC_TYPE, 0, 0); + msg->header.part.data = sst_drv_ctx->pmic_vendor; + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + return; +} + +/** +* 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; + imr.full = 0; + + pr_debug("..called \n"); + mutex_lock(&sst_drv_ctx->list_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 */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(imr.full, sst_drv_ctx->shim + SST_IMRX); + mutex_unlock(&sst_drv_ctx->list_lock); + return; + } + + /* check busy bit */ + header.full = readl(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 */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(imr.full, sst_drv_ctx->shim + SST_IMRX); + mutex_unlock(&sst_drv_ctx->list_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 */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(msg->header.full, sst_drv_ctx->shim + SST_IPCX); + mutex_unlock(&sst_drv_ctx->list_lock); + + kfree(msg->mailbox_data); + kfree(msg); + pr_debug("...done\n"); + return; +} + +/** +* this function clears the interrupt register after the interrupt +* bottom half is complete allowing next interrupt to arrive +*/ +static void sst_clear_interrupt(void) +{ + union interrupt_reg isr; + union interrupt_reg imr; + union ipc_header clear_ipc; + + pr_debug("sst clearing interrupt \n"); + imr.full = readl(sst_drv_ctx->shim + SST_IMRX); + isr.full = readl(sst_drv_ctx->shim + SST_ISRX); + /* write 1 to clear */; + isr.part.busy_interrupt = 1; + /* dummy register for shim workaround */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(isr.full, sst_drv_ctx->shim + SST_ISRX); + /* Set IA done bit */ + clear_ipc.full = readl(sst_drv_ctx->shim + SST_IPCD); + clear_ipc.part.busy = 0; + clear_ipc.part.done = 1; + clear_ipc.part.data = IPC_ACK_SUCCESS; + /* dummy register for shim workaround */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(clear_ipc.full, sst_drv_ctx->shim + SST_IPCD); + /* un mask busy interrupt */ + imr.part.busy_interrupt = 0; + /* dummy register for shim workaround */ + writel(0x0, sst_drv_ctx->shim + SST_ISRD); + writel(imr.full, sst_drv_ctx->shim + SST_IMRX); +} + +/** +* sst_process_message - Processes message from SST +* @work: Pointer to work structure +* +* This function is scheduled by ISR +* It take a msg from process_queue and does action based on msg +*/ +void sst_process_message(struct work_struct *work) +{ + struct sst_ipc_msg_wq *msg = + container_of(work, struct sst_ipc_msg_wq, wq); + int str_id = msg->header.part.str_id; + struct stream_info *stream ; + + pr_debug("called \n"); + + /* based on msg in list call respective handler */ + switch (msg->header.part.msg_id) { + case IPC_SST_BUF_UNDER_RUN: + case IPC_SST_BUF_OVER_RUN: + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id); + break; + } + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Buffer under/overrun for %d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.str_id); + pr_debug("Got Underrun & not to send data...ignore\n"); + break; + + case IPC_SST_GET_PLAY_FRAMES: + { + struct stream_info *stream ; + + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, 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); + sst_play_frame(msg->header.part.str_id); + mutex_unlock(&sst_drv_ctx->streams[str_id].lock); + break; + } + + case IPC_SST_PERIOD_ELAPSED: + { + struct snd_sst_tstamp fw_tstamp = {0,}; + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id); + break; + } + stream = &sst_drv_ctx->streams[str_id]; + + pr_debug("Period elapsed \n"); + memcpy_fromio(&fw_tstamp, + ((void *)(sst_drv_ctx->mailbox + SST_TIME_STAMP) + + (str_id * sizeof(fw_tstamp))), + sizeof(fw_tstamp)); + pr_debug("samples played = %lld\n", fw_tstamp.samples_processed); + pr_debug("diff in mesg = %d\n", msg->header.part.data); + sst_clear_interrupt(); + if (stream->period_elapsed) + stream->period_elapsed(stream->pcm_substream); + return; + } + + case IPC_SST_GET_CAPT_FRAMES: + /* call sst_capture_frame */ + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id); + break; + } + stream = &sst_drv_ctx->streams[str_id]; + pr_debug("sst_capture_frames for %d\n", msg->header.part.str_id); + mutex_lock(&stream->lock); + if (stream->mmapped == false && stream->src == SST_DRV) { + pr_debug("waking up block for copy...\n"); + stream->data_blk.ret_code = 0; + stream->data_blk.condition = true; + stream->data_blk.on = false; + wake_up(&sst_drv_ctx->wait_queue); + } else + sst_capture_frame(msg->header.part.str_id); + mutex_unlock(&stream->lock); + break; + + case IPC_IA_PRINT_STRING: + pr_debug("been asked to print something by fw\n"); + /* TBD */ + break; + + case IPC_IA_FW_INIT_CMPLT: { + /* send next data to FW */ + struct ipc_header_fw_init *init = + (struct ipc_header_fw_init *)msg->mailbox; + int major = init->fw_version.major; + int minor = init->fw_version.minor; + int build = init->fw_version.build; + + pr_debug("*** FW Init msg came*** \n"); + if (!init->result) { + sst_drv_ctx->sst_state = SST_FW_RUNNING; + pr_debug("FW Version %x.%x \n", + init->fw_version.major, init->fw_version.minor); + pr_debug("Build No %x Type %x \n", + init->fw_version.build, init->fw_version.type); +#ifdef SND_LOOP_TEST + sst_send_loop_test(0); +#endif + sst_send_sound_card_type(); + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d INFO: ***SST FW VERSION*** +\ + = %02d.%02d.%02d\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, major, \ + minor, build); + } else { + sst_drv_ctx->sst_state = SST_ERROR; + pr_debug("FW Init failed, Error %x\n", init->result); + pr_debug("FW Init failed, Module %x, Debug Info %x \n", + init->module_id, init->debug_info); + } + pr_debug("Waking up... open\n"); + sst_wake_up_alloc_block(sst_drv_ctx, FW_DWNL_ID, 0, NULL); + break; + } + + case IPC_SST_STREAM_PROCESS_FATAL_ERR: + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id); + break; + } + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: codec fatal error %x for +\ + stream %d... \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, msg->header.full, \ + msg->header.part.str_id); + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Dropping the stream \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + sst_drop_stream(msg->header.part.str_id); + break; + + default: + /* Illegal case */ + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Unhandled case msg_id %x +\ + message %x\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, msg->header.full); + } + sst_clear_interrupt(); + return; +} + +/** +* sst_process_reply - Processes reply message from SST +* @work: Pointer to work structure +* +* This function is scheduled by ISR +* It take a reply msg from response_queue and +* does action based on msg +*/ +void sst_process_reply(struct work_struct *work) +{ + struct sst_ipc_msg_wq *msg = + container_of(work, struct sst_ipc_msg_wq, wq); + + int str_id = msg->header.part.str_id; + struct stream_info *str_info; + + pr_debug("called for %x \n", msg->header.full); + switch (msg->header.part.msg_id) { + case IPC_IA_TARGET_DEV_SELECT: + if (!msg->header.part.data) { + pr_debug("Msg succedded %x \n", msg->header.part.msg_id); + sst_drv_ctx->tgt_dev_blk.ret_code = 0; + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x reply +\ + error %x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, msg->header.part.data); + sst_drv_ctx->tgt_dev_blk.ret_code = + -msg->header.part.data; + } + + if (sst_drv_ctx->tgt_dev_blk.on == true) { + sst_drv_ctx->tgt_dev_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + case IPC_IA_GET_FW_INFO: { + struct snd_sst_fw_info *fw_info = + (struct snd_sst_fw_info *)msg->mailbox; + if (msg->header.part.large) { + int major = fw_info->fw_version.major; + int minor = fw_info->fw_version.minor; + int build = fw_info->fw_version.build; + pr_debug("Msg succedded %x \n", msg->header.part.msg_id); + dev_info(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d INFO: ***FW VERSION*** +\ + = %02d.%02d.%02d\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, major, \ + minor, build); + + memcpy_fromio(sst_drv_ctx->fw_info_blk.data, + ((struct snd_sst_fw_info *)(msg->mailbox)), + sizeof(struct snd_sst_fw_info)); + sst_drv_ctx->fw_info_blk.ret_code = 0; + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x reply +\ + error %x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, msg->header.part.data); + sst_drv_ctx->fw_info_blk.ret_code = + -msg->header.part.data; + } + if (sst_drv_ctx->fw_info_blk.on == true) { + pr_debug("Memcopy succedded \n"); + sst_drv_ctx->fw_info_blk.on = false; + sst_drv_ctx->fw_info_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + } + case IPC_IA_SET_STREAM_MUTE: { + if (!msg->header.part.data) { + pr_debug("Msg succedded %x \n", msg->header.part.msg_id); + sst_drv_ctx->mute_info_blk.ret_code = 0; + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x reply +\ + error %x \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, msg->header.part.data); + sst_drv_ctx->mute_info_blk.ret_code = + -msg->header.part.data; + + } + if (sst_drv_ctx->mute_info_blk.on == true) { + sst_drv_ctx->mute_info_blk.on = false; + sst_drv_ctx->mute_info_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + } + case IPC_IA_SET_STREAM_VOL: { + if (!msg->header.part.data) { + pr_debug("Msg succedded %x \n", msg->header.part.msg_id); + sst_drv_ctx->vol_info_blk.ret_code = 0; + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x reply +\ + error %x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, \ + msg->header.part.data); + sst_drv_ctx->vol_info_blk.ret_code = + -msg->header.part.data; + + } + + if (sst_drv_ctx->vol_info_blk.on == true) { + sst_drv_ctx->vol_info_blk.on = false; + sst_drv_ctx->vol_info_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + } + case IPC_IA_GET_STREAM_VOL: + if (msg->header.part.large) { + pr_debug("Large Msg Received Successfully\n"); + pr_debug("Msg succedded %x \n", msg->header.part.msg_id); + memcpy_fromio(sst_drv_ctx->vol_info_blk.data, + (void *) msg->mailbox, + sizeof(struct snd_sst_vol)); + sst_drv_ctx->vol_info_blk.ret_code = 0; + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x +\ + reply error %x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, msg->header.part.data); + sst_drv_ctx->vol_info_blk.ret_code = + -msg->header.part.data; + } + if (sst_drv_ctx->vol_info_blk.on == true) { + sst_drv_ctx->vol_info_blk.on = false; + sst_drv_ctx->vol_info_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + + case IPC_IA_GET_STREAM_PARAMS: + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id); + break; + } + str_info = &sst_drv_ctx->streams[str_id]; + if (msg->header.part.large) { + pr_debug("The Large message for get stream params\n"); + pr_debug("Msg succedded %x \n", msg->header.part.msg_id); + memcpy_fromio(str_info->ctrl_blk.data, + ((void *)(msg->mailbox)), + sizeof(struct snd_sst_fw_get_stream_params)); + str_info->ctrl_blk.ret_code = 0; + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: The message for +\ + get params is not large\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x reply error %x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, msg->header.part.data); + str_info->ctrl_blk.ret_code = + -msg->header.part.data; + } + if (str_info->ctrl_blk.on == true) { + str_info->ctrl_blk.on = false; + str_info->ctrl_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + case IPC_IA_DECODE_FRAMES: { + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id); + break; + } + str_info = &sst_drv_ctx->streams[str_id]; + if (msg->header.part.large) { + pr_debug("Msg succedded %x \n", msg->header.part.msg_id); + memcpy_fromio(str_info->data_blk.data, + ((void *)(msg->mailbox)), + sizeof(struct snd_sst_decode_info)); + str_info->data_blk.ret_code = 0; + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x reply error %x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, msg->header.part.data); + str_info->data_blk.ret_code = + -msg->header.part.data; + } + if (str_info->data_blk.on == true) { + str_info->data_blk.on = false; + str_info->data_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + } + case IPC_IA_DRAIN_STREAM: + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id); + break; + } + str_info = &sst_drv_ctx->streams[str_id]; + if (!msg->header.part.data) { + pr_debug("Msg succedded %x \n", msg->header.part.msg_id); + str_info->ctrl_blk.ret_code = 0; + + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x reply error %x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, msg->header.part.data); + str_info->ctrl_blk.ret_code = -msg->header.part.data; + + } + str_info = &sst_drv_ctx->streams[str_id]; + if (str_info->data_blk.on == true) { + str_info->data_blk.on = false; + str_info->data_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + + case IPC_IA_DROP_STREAM: + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id); + break; + } + str_info = &sst_drv_ctx->streams[str_id]; + if (msg->header.part.large) { + struct snd_sst_drop_response *drop_resp = + (struct snd_sst_drop_response *)msg->mailbox; + + pr_debug("Drop returns with bytes 0x%x \n", + drop_resp->bytes); + + str_info->curr_bytes = drop_resp->bytes; + str_info->ctrl_blk.ret_code = 0; + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x reply error %x \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.part.msg_id, msg->header.part.data); + str_info->ctrl_blk.ret_code = -msg->header.part.data; + } + if (str_info->ctrl_blk.on == true) { + str_info->ctrl_blk.on = false; + str_info->ctrl_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + case IPC_IA_PAUSE_STREAM: + case IPC_IA_RESUME_STREAM: + case IPC_IA_SET_STREAM_PARAMS: + str_info = &sst_drv_ctx->streams[str_id]; + if (!msg->header.part.data) { + pr_debug("Msg succedded %x \n", msg->header.part.msg_id); + str_info->ctrl_blk.ret_code = 0; + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Msg %x +\ + reply error %x \n", get_seconds(), sst_get_time(), __func__ , \ + __LINE__, msg->header.part.msg_id, msg->header.part.data); + str_info->ctrl_blk.ret_code = -msg->header.part.data; + } + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d +\ + invalid\n", get_seconds(), sst_get_time(), __func__ , __LINE__, \ + str_id); + break; + } + + if (str_info->ctrl_blk.on == true) { + str_info->ctrl_blk.on = false; + str_info->ctrl_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + break; + + case IPC_IA_FREE_STREAM: + if (!msg->header.part.data) { + pr_debug("Stream %d freed\n", str_id); + } else { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Free for %d +\ + returned error %x\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__, str_id, msg->header.part.data); + } + break; + case IPC_IA_LPE_GETTING_STALLED: + sst_drv_ctx->lpe_stalled = 1; + break; + case IPC_IA_LPE_UNSTALLED: + sst_drv_ctx->lpe_stalled = 0; + break; + case IPC_IA_ALLOC_STREAM: { + /* map to stream, call play */ + struct snd_sst_alloc_response *resp = + (struct snd_sst_alloc_response *)msg->mailbox; + if (resp->str_type.result) { + /* error case */ + struct snd_sst_alloc_response *lib = NULL; + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: error +\ + alloc stream = %x \n", get_seconds(), sst_get_time(), \ + __func__ , __LINE__, resp->str_type.result); + if (resp->str_type.result == + SST_LIB_ERR_LIB_DNLD_REQUIRED) { + lib = kzalloc(sizeof(*lib), GFP_ATOMIC); + if (!lib) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + mem allocation failed \n", \ + get_seconds(), sst_get_time(), \ + __func__ , __LINE__); + break; + } + memcpy(lib, msg->mailbox, sizeof(*lib)); + /* library needs to be downloaded */ + pr_debug("Codec Download required \n"); + } + sst_wake_up_alloc_block(sst_drv_ctx, + resp->str_type.pvt_id, + (-resp->str_type.result), lib); + break; + } + sst_alloc_stream_response(str_id, &resp->str_type); + break; + } + + case IPC_IA_DBG_LOOP_BACK: + /* Debug loop back msg */ + pr_debug("Loop back came \n"); + if (msg->header.part.data) + pr_debug("Possible error if not large \n"); + pr_debug("Loop ID: %d\n", str_id); + if (msg->header.part.large) { + struct ipc_sst_ia_dbg_loop_back *loop_msg = + (struct ipc_sst_ia_dbg_loop_back *)msg->mailbox; + int i; + pr_debug("Got large loop back: Words %d\n", + loop_msg->num_dwords); + for (i = 0; i < loop_msg->num_dwords; i++) { + pr_debug("Loop Word %d = %d \n", i, + loop_msg->lpbk_dwords[i]); + } + } + sst_send_loop_test((str_id + 1)); + break; + + case IPC_IA_PLAY_FRAMES: + case IPC_IA_CAPT_FRAMES: + if (sst_validate_strid(str_id)) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: stream id %d invalid\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, str_id); + break; + } + pr_debug("Ack for play/capt frames recived \n"); + break; + + case IPC_IA_PREP_LIB_DNLD: { + struct snd_sst_str_type *str_type = + (struct snd_sst_str_type *)msg->mailbox; + pr_debug("Prep Lib download %x\n", msg->header.part.msg_id); + if (str_type->result) + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Error in prep lib +\ + download 0x%x\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + str_type->result); + else + pr_debug("Need to download codec now...\n"); + /* FIXME remove this workaround */ + str_type->result = 0; + sst_wake_up_alloc_block(sst_drv_ctx, str_type->pvt_id, + str_type->result, NULL); + break; + } + + case IPC_IA_LIB_DNLD_CMPLT: { + struct snd_sst_lib_download_info *resp = + (struct snd_sst_lib_download_info *)msg->mailbox; + int retval = resp->result; + + pr_debug("Lib download cmplt %x\n", msg->header.part.msg_id); + if (resp->result) + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: Error in +\ + lib dload %x\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + resp->result); + else { + pr_debug("Codec download complete...\n"); + pr_debug("Downloaded codec Type %d Ver %d Built %s: %s\n", + resp->dload_lib.lib_info.lib_type, + resp->dload_lib.lib_info.lib_version, + resp->dload_lib.lib_info.b_date, + resp->dload_lib.lib_info.b_time); + } + sst_wake_up_alloc_block(sst_drv_ctx, resp->pvt_id, + retval, NULL); + break; + } + + case IPC_IA_GET_FW_VERSION: { + struct ipc_header_fw_init *version = + (struct ipc_header_fw_init *)msg->mailbox; + int major = version->fw_version.major; + int minor = version->fw_version.minor; + int build = version->fw_version.build; + dev_info(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d INFO: ***LOADED SST FW +\ + VERSION*** = %02d.%02d.%02d\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + major, minor, build); + break; + } + case IPC_IA_GET_FW_BUILD_INF: { + struct sst_fw_build_info *build = + (struct sst_fw_build_info *)msg->mailbox; + dev_info(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d INFO: Build date %s +\ + Time %s",\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + build->date, build->time); + break; + } + case IPC_IA_SET_PMIC_TYPE: + break; + default: + /* Illegal case */ + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: process reply :default +\ + case = %x\n" ,\ + get_seconds(), sst_get_time(), __func__ , __LINE__, \ + msg->header.full); + + } + sst_clear_interrupt(); + return; +} Index: linux-2.6.33/sound/pci/sst/intel_sst_stream.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intel_sst_stream.c @@ -0,0 +1,1572 @@ +/* + * intel_sst_stream.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * Dharageswari R <dharageswari.r@intel.com) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains the stream operations of SST driver + */ + +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/uaccess.h> +#include <linux/firmware.h> +#ifdef CONFIG_MSTWN_POWER_MGMT +#include <linux/intel_mid.h> +#endif +#ifdef CONFIG_MRST_RAR_HANDLER +#include <linux/rar/rar_register.h> +#include <linux/rar/memrar.h> +#endif +#include <sound/intel_sst_ioctl.h> +#include <sound/intel_lpe.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" +/** +* sst_alloc_stream - Send msg for a new stream ID +* @params: stream params +* @stream_ops: operation of stream PB/capture +* @codec: codec for stream +* @session_id: pvt_id passed by MMF to distinguish stream +* +* This function is called by any function which wants to start +* a new stream. This also check if a stream exists which is idle +* it initializes idle stream id to this request +*/ +int sst_alloc_stream(char *params, unsigned int stream_ops, + u8 codec, unsigned int session_id) +{ + struct ipc_post *msg = NULL; + struct snd_sst_alloc_params alloc_param = {{0,},}; + + pr_debug("entering sst_alloc_stream \n"); + pr_debug("%d %d %d\n", stream_ops, codec, session_id); + + BUG_ON(!params); + + /* send msg to FW to allocate a stream */ + if (sst_create_large_msg(&msg)) + return -ENOMEM; + + sst_fill_header(&msg->header, IPC_IA_ALLOC_STREAM, 1, 0); + msg->header.part.data = sizeof(alloc_param) + sizeof(u32); + alloc_param.str_type.codec_type = codec; + alloc_param.str_type.str_type = STREAM_TYPE_MUSIC; /* music */ + alloc_param.str_type.operation = stream_ops; + alloc_param.str_type.protected_str = 0; /* non drm */ + alloc_param.str_type.pvt_id = session_id; + memcpy(&alloc_param.stream_params, params, + sizeof(struct snd_sst_stream_params)); + + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), &alloc_param, + sizeof(alloc_param)); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + pr_debug("alloc stream done\n"); + return 0; +} + +/** +* sst_get_stream_params - Send msg to query for stream parameters +* @str_id: stream id for which the parameters are queried for +* @get_params: out parameters to which the parameters are copied to +* +* This function is called when the stream parameters are queiried for +*/ +int sst_get_stream_params(int str_id, + struct snd_sst_get_stream_params *get_params) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + struct snd_sst_fw_get_stream_params *fw_params; + + pr_debug("get_stream for %d\n", str_id); + retval = sst_validate_strid(str_id); + if (retval) + return retval; + + str_info = &sst_drv_ctx->streams[str_id]; + if (str_info->status != STREAM_UN_INIT) { + if (str_info->ctrl_blk.on == true) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: control path is already in use \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + if (sst_create_short_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: message creation failed\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + fw_params = kzalloc(sizeof(*fw_params), GFP_ATOMIC); + if (!fw_params) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: mem allcoation failed\n ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + + sst_fill_header(&msg->header, IPC_IA_GET_STREAM_PARAMS, + 0, str_id); + str_info->ctrl_blk.condition = false; + str_info->ctrl_blk.ret_code = 0; + str_info->ctrl_blk.on = true; + str_info->ctrl_blk.data = (void *) fw_params; + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); + if (retval) { + get_params->codec_params.result = retval; + kfree(fw_params); + return -EIO; + } + memcpy(&get_params->pcm_params, &fw_params->pcm_params, + sizeof(fw_params->pcm_params)); + memcpy(&get_params->codec_params.sparams, + &fw_params->codec_params, + sizeof(fw_params->codec_params)); + get_params->codec_params.result = 0; + get_params->codec_params.stream_id = str_id; + get_params->codec_params.codec = str_info->codec; + get_params->codec_params.ops = str_info->ops; + get_params->codec_params.stream_type = str_info->str_type; + kfree(fw_params); + } else { + pr_debug("Stream is not in the init state\n"); + } + return retval; +} + +/** +* sst_get_fw_info - Send msg to query for firmware configurations +* @info: out param that holds the firmare configurations +* +* This function is called when the firmware configurations are queiried for +*/ +int sst_get_fw_info(struct snd_sst_fw_info *info) +{ + int retval = 0; + struct ipc_post *msg = NULL; + + pr_debug("...called \n"); + + if (sst_create_short_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: +\ + %s %d ERR: message creation failed\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + + sst_fill_header(&msg->header, IPC_IA_GET_FW_INFO, 0, 0); + sst_drv_ctx->fw_info_blk.condition = false; + sst_drv_ctx->fw_info_blk.ret_code = 0; + sst_drv_ctx->fw_info_blk.on = true; + sst_drv_ctx->fw_info_blk.data = info; + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &sst_drv_ctx->fw_info_blk, SST_BLOCK_TIMEOUT); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: +\ + %s %d ERR: error in fw_info = %d\n", get_seconds(),\ + sst_get_time(), __func__ , __LINE__, retval); + retval = -EIO; + } + return retval; +} + + +/** +* sst_alloc_stream_response - process alloc reply +* @str_id: stream id for which the stream has been allocated +* @type the stream parameters that are allocated + +* This function is called by firmware as a response to stream allcoation +* request +*/ +int sst_alloc_stream_response(unsigned int str_id, + struct snd_sst_str_type *type) +{ + int retval = 0, i, valid_str = 0; + struct ipc_post *msg = NULL; + + /* allocation succesfull */ + pr_debug("stream number given = %d \n", str_id); + + for (i = 0; i < MAX_ACTIVE_STREAM; i++) { + if (type->pvt_id == sst_drv_ctx->alloc_block[i].sst_id) { + valid_str = 1; + break; + } + } + if (!valid_str) { + /* this is not valid stream */ + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: +\ + %s %d ERR: Invalid stream allocation detetcted... freeing\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + if (sst_create_short_msg(&msg)) + return -ENOMEM; + sst_fill_header(&msg->header, IPC_IA_FREE_STREAM, 0, str_id); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + return 0; + } + + sst_init_stream(&sst_drv_ctx->streams[str_id], type->codec_type, + type->str_type, type->pvt_id, type->operation); + + pr_debug("stream pvt id = %d \n", type->pvt_id); + + /* Unblock with retval code */ + sst_wake_up_alloc_block(sst_drv_ctx, type->pvt_id, str_id, NULL); + return retval; +} + +/** +* sst_pause_stream - Send msg for a pausing stream +* @str_id: stream ID +* +* This function is called by any function which wants to pause +* an already running stream. +*/ +int sst_pause_stream(int str_id) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + + pr_debug("sst_pause_stream for %d\n", str_id); + retval = sst_validate_strid(str_id); + if (retval) + return retval; + str_info = &sst_drv_ctx->streams[str_id]; + if (str_info->status == STREAM_PAUSED) + return 0; + if (str_info->status == STREAM_RUNNING || + str_info->status == STREAM_INIT) { + if (str_info->prev == STREAM_UN_INIT) + return -EBADRQC; + if (str_info->ctrl_blk.on == true) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: +\ + %s %d ERR: control path is already in use\n ", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + if (sst_create_short_msg(&msg)) + return -ENOMEM; + + sst_fill_header(&msg->header, IPC_IA_PAUSE_STREAM, 0, str_id); + str_info->ctrl_blk.condition = false; + str_info->ctrl_blk.ret_code = 0; + str_info->ctrl_blk.on = true; + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, + &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); + if (retval == 0) { + str_info->prev = str_info->status; + str_info->status = STREAM_PAUSED; +#ifdef CONFIG_MSTWN_POWER_MGMT + sst_ospm_send_event(OSPM_EVENT_SUBSYS_STOP_PLAY); +#endif + } else if (retval == SST_ERR_INVALID_STREAM_ID) { + retval = -EINVAL; + sst_clean_stream(str_info); + } + } else { + retval = -EBADRQC; + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: +\ + %s %d ERR: BADQRC for stream\n ", get_seconds(),\ + sst_get_time(), __func__ , __LINE__); + } + + return retval; +} + +/** +* sst_resume_stream - Send msg for resuming stream +* @str_id: stream ID +* +* This function is called by any function which wants to resume +* an already paused stream. +*/ +int sst_resume_stream(int str_id) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + + pr_debug("sst_resume_stream for %d\n", str_id); + retval = sst_validate_strid(str_id); + if (retval) + return retval; + str_info = &sst_drv_ctx->streams[str_id]; + if (str_info->status == STREAM_RUNNING) + return 0; + if (str_info->status == STREAM_PAUSED) { + if (str_info->ctrl_blk.on == true) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: control path is already in use\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + if (sst_create_short_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: short message mem allocation failed\n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + sst_fill_header(&msg->header, IPC_IA_RESUME_STREAM, 0, str_id); + str_info->ctrl_blk.condition = false; + str_info->ctrl_blk.ret_code = 0; + str_info->ctrl_blk.on = true; + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, + &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); + if (!retval) { + if (str_info->prev == STREAM_RUNNING) + str_info->status = STREAM_RUNNING; + else + str_info->status = STREAM_INIT; + str_info->prev = STREAM_PAUSED; +#ifdef CONFIG_MSTWN_POWER_MGMT + sst_ospm_send_event(OSPM_EVENT_SUBSYS_START_PLAY); +#endif + } else if (retval == -SST_ERR_INVALID_STREAM_ID) { + retval = -EINVAL; + sst_clean_stream(str_info); + } + } else { + retval = -EBADRQC; + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: +\ + %s %d ERR: BADQRC for stream\n", get_seconds(),\ + sst_get_time(), __func__ , __LINE__); + } + + return retval; +} + + +/** +* sst_drop_stream - Send msg for stopping stream +* @str_id: stream ID +* +* This function is called by any function which wants to stop +* a stream. +*/ +int sst_drop_stream(int str_id) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct sst_stream_bufs *bufs = NULL, *_bufs; + struct stream_info *str_info; + + pr_debug("sst_drop_stream for %d\n", str_id); + retval = sst_validate_strid(str_id); + if (retval) + return retval; + str_info = &sst_drv_ctx->streams[str_id]; + + if (str_info->status != STREAM_UN_INIT && + str_info->status != STREAM_DECODE) { + if (str_info->ctrl_blk.on == true) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: control path is already in use\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + if (sst_create_short_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: short message mem allocation +\ + failed\n", get_seconds(), sst_get_time(), __func__ ,\ + __LINE__); + return -ENOMEM; + } + sst_fill_header(&msg->header, IPC_IA_DROP_STREAM, 0, str_id); + str_info->ctrl_blk.condition = false; + str_info->ctrl_blk.ret_code = 0; + str_info->ctrl_blk.on = true; + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, + &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); + if (!retval) { + pr_debug("drop success\n"); + str_info->prev = STREAM_UN_INIT; + str_info->status = STREAM_INIT; + if (str_info->src != MAD_DRV) { + mutex_lock(&str_info->lock); + list_for_each_entry_safe(bufs, _bufs, + &str_info->bufs, node) { + list_del(&bufs->node); + kfree(bufs); + } + mutex_unlock(&str_info->lock); + } + str_info->cumm_bytes += str_info->curr_bytes; +#ifdef CONFIG_MSTWN_POWER_MGMT + sst_ospm_send_event(OSPM_EVENT_SUBSYS_STOP_PLAY); +#endif + } else if (retval == -SST_ERR_INVALID_STREAM_ID) { + retval = -EINVAL; + sst_clean_stream(str_info); + } + if (str_info->data_blk.on == true) { + str_info->data_blk.condition = true; + str_info->data_blk.ret_code = retval; + wake_up(&sst_drv_ctx->wait_queue); + } + } else { + retval = -EBADRQC; + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: +\ + %s %d ERR:BADQRC for stream\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + } + return retval; +} + +/** +* sst_drain_stream - Send msg for draining stream +* @str_id: stream ID +* +* This function is called by any function which wants to drain +* a stream. +*/ +int sst_drain_stream(int str_id) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + + pr_debug("sst_drain_stream for %d\n", str_id); + retval = sst_validate_strid(str_id); + if (retval) + return retval; + str_info = &sst_drv_ctx->streams[str_id]; + + if (str_info->status != STREAM_RUNNING && + str_info->status != STREAM_INIT && + str_info->status != STREAM_PAUSED) { + dev_err(&sst_drv_ctx->pci->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: BADQRC for +\ + stream = %d\n", get_seconds(), sst_get_time(), \ + __func__ , __LINE__, str_info->status); + return -EBADRQC; + } + + if (str_info->status == STREAM_INIT) { + if (sst_create_short_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: short message +\ + mem allocation failed\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -ENOMEM; + } + sst_fill_header(&msg->header, IPC_IA_DRAIN_STREAM, 0, str_id); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + } else + str_info->need_draining = true; + str_info->data_blk.condition = false; + str_info->data_blk.ret_code = 0; + str_info->data_blk.on = true; + retval = sst_wait_interruptible(sst_drv_ctx, &str_info->data_blk); + str_info->need_draining = false; + if (retval == -SST_ERR_INVALID_STREAM_ID) { + retval = -EINVAL; + sst_clean_stream(str_info); + } + return retval; +} + +/** +* sst_free_stream - Frees a stream +* @str_id: stream ID +* +* This function is called by any function which wants to free +* a stream. +*/ +int sst_free_stream(int str_id) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + + pr_debug("sst_free_stream for %d\n", str_id); + + retval = sst_validate_strid(str_id); + if (retval) + return retval; + str_info = &sst_drv_ctx->streams[str_id]; + + if (str_info->status != STREAM_UN_INIT) { + if (sst_create_short_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: short message mem allocation +\ + failed\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -ENOMEM; + } + sst_fill_header(&msg->header, IPC_IA_FREE_STREAM, 0, str_id); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + str_info->prev = str_info->status; + str_info->status = STREAM_UN_INIT; + if (str_info->data_blk.on == true) { + str_info->data_blk.condition = true; + str_info->data_blk.ret_code = 0; + wake_up(&sst_drv_ctx->wait_queue); + } + sst_clean_stream(str_info); + pr_debug("Stream freed\n"); + +#ifdef CONFIG_MSTWN_POWER_MGMT + sst_ospm_send_event(OSPM_EVENT_SUBSYS_STOP_PLAY); +#endif + } else { + retval = -EBADRQC; + pr_debug("BADQRC for stream\n"); + } + + return retval; +} + + +/** +* sst_set_stream_param - Send msg for setting stream parameters +* @id: stream id +* @params: stream params +* +* This function sets stream params during runtime +*/ +int sst_set_stream_param(int str_id, struct snd_sst_params *str_param) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + + BUG_ON(!str_param); + retval = sst_validate_strid(str_id); + if (retval) + return retval; + pr_debug("set_stream for %d\n", str_id); + str_info = &sst_drv_ctx->streams[str_id]; + if (sst_drv_ctx->streams[str_id].status == STREAM_INIT) { + if (str_info->ctrl_blk.on == true) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + control path is already in use\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + if (sst_create_large_msg(&msg)) + return -ENOMEM; + + sst_fill_header(&msg->header, + IPC_IA_SET_STREAM_PARAMS, 1, str_id); + str_info->ctrl_blk.condition = false; + str_info->ctrl_blk.ret_code = 0; + str_info->ctrl_blk.on = true; + msg->header.part.data = sizeof(u32) + + sizeof(str_param->sparams); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), &str_param->sparams, + sizeof(str_param->sparams)); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); + if (retval < 0) { + retval = -EIO; + sst_clean_stream(str_info); + } + } else { + retval = -EBADRQC; + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + BADQRC for stream\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + } + return retval; +} + +/** +* sst_get_vol - This fuction allows to get the premix gain or gain of a stream +* @get_vol: this is an output param through which the volume +* structure is passed back to user +* +* This function is called when the premix gain or stream gain is queried for +*/ +int sst_get_vol(struct snd_sst_vol *get_vol) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct snd_sst_vol *fw_get_vol; + int str_id = get_vol->stream_id; + + pr_debug("get vol called \n"); + + if (sst_create_short_msg(&msg)) + return -ENOMEM; + + sst_fill_header(&msg->header, + IPC_IA_GET_STREAM_VOL, 0, str_id); + sst_drv_ctx->vol_info_blk.condition = false; + sst_drv_ctx->vol_info_blk.ret_code = 0; + sst_drv_ctx->vol_info_blk.on = true; + fw_get_vol = kzalloc(sizeof(*fw_get_vol), GFP_ATOMIC); + if (!fw_get_vol) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + mem allcoation failed\n", get_seconds(), sst_get_time(), \ + __func__ , __LINE__); + return -ENOMEM; + } + sst_drv_ctx->vol_info_blk.data = (void *)fw_get_vol; + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &sst_drv_ctx->vol_info_blk, SST_BLOCK_TIMEOUT); + if (retval) + retval = -EIO; + else { + pr_debug("stream id = %d\n", fw_get_vol->stream_id); + pr_debug("volume = %d\n", fw_get_vol->volume); + pr_debug("ramp dur = %d\n", fw_get_vol->ramp_duration); + pr_debug("ramp_type = %d\n", fw_get_vol->ramp_type); + memcpy(get_vol, fw_get_vol, sizeof(*fw_get_vol)); + } + return retval; +} + +/** +* sst_set_vol - This fuction allows to set the premix gain or gain of a stream +* @set_vol: this holds the volume structure that needs to be set +* +* This function is called when premix gain or stream gain is requested to be set +*/ +int sst_set_vol(struct snd_sst_vol *set_vol) +{ + + int retval = 0; + struct ipc_post *msg = NULL; + + pr_debug("set vol called \n"); + + if (sst_create_large_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + message creation failed\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -ENOMEM; + } + sst_fill_header(&msg->header, IPC_IA_SET_STREAM_VOL, 1, + set_vol->stream_id); + + msg->header.part.data = sizeof(u32) + sizeof(*set_vol); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), set_vol, sizeof(*set_vol)); + sst_drv_ctx->vol_info_blk.condition = false; + sst_drv_ctx->vol_info_blk.ret_code = 0; + sst_drv_ctx->vol_info_blk.on = true; + sst_drv_ctx->vol_info_blk.data = set_vol; + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &sst_drv_ctx->vol_info_blk, SST_BLOCK_TIMEOUT); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + error in set_vol = %d\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__, retval); + retval = -EIO; + } + return retval; +} + +/** +* sst_get_vol - This fuction allows to set premix mute or soft mute of a stream +* @set_mute: this holds the mute structure that needs to be set +* +* This function is called when premix mute or stream mute is requested to be set +*/ +int sst_set_mute(struct snd_sst_mute *set_mute) +{ + + int retval = 0; + struct ipc_post *msg = NULL; + + pr_debug("set mute called \n"); + + if (sst_create_large_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + message creation failed\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -ENOMEM; + } + sst_fill_header(&msg->header, IPC_IA_SET_STREAM_MUTE, 1, + set_mute->stream_id); + sst_drv_ctx->mute_info_blk.condition = false; + sst_drv_ctx->mute_info_blk.ret_code = 0; + sst_drv_ctx->mute_info_blk.on = true; + sst_drv_ctx->mute_info_blk.data = set_mute; + + msg->header.part.data = sizeof(u32) + sizeof(*set_mute); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), set_mute, + sizeof(*set_mute)); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &sst_drv_ctx->mute_info_blk, SST_BLOCK_TIMEOUT); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + error in set_mute = %d\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__, retval); + retval = -EIO; + } + return retval; +} + +/** +* sst_target_device_select - This fuction sets the target device configurations +* @target_device: this parameter holds the configurations to be set +* +* This function is called when the user layer wants to change the target +* device's configurations +*/ + +int sst_target_device_select(struct snd_sst_target_device *target_device) +{ + int retval = 0; + struct ipc_post *msg = NULL; + /* struct snd_sst_slot_info *slot_info = NULL; */ + + pr_debug("Target Device Select\n"); + + if (target_device->device_route < 0 || + target_device->device_route > 2) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + device route is invalid\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -EINVAL; + } + + if (target_device->devices[0].action == SND_SST_PORT_ACTIVATE) { + pr_debug("action - activate\n"); + if (target_device->devices[0].target_type == + SND_SST_TARGET_PMIC) { + if (target_device->device_route == 2) { + pr_debug("activating voice port\n"); + /* sst_drv_ctx->scard_ops->set_voice_port( + ACTIVATE); + sst_drv_ctx->scard_ops.set_audio_port( + DEACTIVATE);*/ + } else if (target_device->device_route == 0) { + pr_debug("activating audio port\n"); + /* sst_drv_ctx->scard_ops.set_voice_port( + DEACTIVATE); + sst_drv_ctx->scard_ops.set_audio_port( + ACTIVATE);*/ + } + } + } + + if (sst_create_large_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + message creation failed\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -ENOMEM; + } + sst_fill_header(&msg->header, IPC_IA_TARGET_DEV_SELECT, + 1, 0); + sst_drv_ctx->tgt_dev_blk.condition = false; + sst_drv_ctx->tgt_dev_blk.ret_code = 0; + sst_drv_ctx->tgt_dev_blk.on = true; + + msg->header.part.data = sizeof(u32) + sizeof(*target_device); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), target_device, + sizeof(*target_device)); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + pr_debug("message sent- waiting\n"); + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &sst_drv_ctx->tgt_dev_blk, TARGET_DEV_BLOCK_TIMEOUT); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + target device ipc failed = 0x%x\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__, retval); + return retval; + } + + if (target_device->devices[0].action == SND_SST_PORT_PREPARE) { + pr_debug("inside action - prepare\n"); + if (target_device->devices[0].target_type == + SND_SST_TARGET_PMIC) { + pr_debug("device type is pmic \n"); + if (target_device->device_route == 0) { + sst_drv_ctx->scard_ops->set_pcm_audio_params( + target_device->devices[0].\ + pcm_params.sfreq, + target_device->devices[0].\ + pcm_params.pcm_wd_sz); + } else if (target_device->device_route == 2) { + pr_debug("setting voice pmic configs\n"); + retval = sst_drv_ctx->scard_ops-> + set_pcm_voice_params(); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: port setting failed\n"\ + , get_seconds(), sst_get_time(), \ + __func__ , __LINE__); + return retval; + } + } + } else { + pr_debug("disabling audio&voice ports\n"); + sst_drv_ctx->scard_ops->set_audio_port( + DEACTIVATE); + sst_drv_ctx->scard_ops->set_voice_port( + DEACTIVATE); + } + } + return retval; +} + +#ifdef CONFIG_MRST_RAR_HANDLER +/** +* This function gets the physical address of the secure memory from the handle +*/ +static inline int sst_get_RAR(struct RAR_buffer *buffers, int count) +{ + int retval = 0, rar_status = 0; + + rar_status = rar_handle_to_bus(buffers, count); + + if (count != rar_status) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + The rar CALL Failed", get_seconds(), sst_get_time(), \ + __func__ , __LINE__); + retval = -EIO; + } + if (buffers->info.type != RAR_TYPE_AUDIO) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + Invalid RAR type\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -EINVAL; + } + return retval; +} +#endif + +/** +* This function creates the scatter gather list to be sent to firmware to +* capture/playback data +*/ +static int sst_create_sg_list(struct stream_info *stream, + struct sst_frame_info *sg_list) +{ + struct sst_stream_bufs *kbufs = NULL; +#ifdef CONFIG_MRST_RAR_HANDLER + struct RAR_buffer rar_buffers; + int retval = 0; +#endif + int i = 0; + + list_for_each_entry(kbufs, &stream->bufs, node) { + if (kbufs->in_use == false) { + if (stream->ops == STREAM_OPS_PLAYBACK_DRM) { + pr_debug("DRM playback handling \n"); +#ifdef CONFIG_MRST_RAR_HANDLER + rar_buffers.info.handle = (__u32)kbufs->addr; + rar_buffers.info.size = kbufs->size; + pr_debug("rar handle = 0x%x", rar_buffers.info.handle); + retval = sst_get_RAR(&rar_buffers, 1); + if (retval) + return retval; + sg_list->addr[i].addr = (__u32)kbufs->addr;; + sg_list->addr[i].size = kbufs->size; + pr_debug("phy addr[%d] 0x%x Size 0x%x\n", i, + sg_list->addr[i].addr, sg_list->addr[i].size); +#endif + } else { + sg_list->addr[i].addr = + virt_to_phys((void *) + kbufs->addr + kbufs->offset); + sg_list->addr[i].size = kbufs->size; + pr_debug("phy addr[%d] 0x%x Size 0x%x\n", i, + sg_list->addr[i].addr, kbufs->size); + } + stream->curr_bytes += sg_list->addr[i].size; + kbufs->in_use = true; + i++; + } + if (i >= MAX_NUM_SCATTER_BUFFERS) + break; + } + + sg_list->num_entries = i; + pr_debug("sg list entries = %d \n", sg_list->num_entries); + return i; +} + +/** +* sst_play_frame - Send msg for sending stream frames +* @str_id: ID of stream +* +* This function is called to send data to be played out +* to the firmware +*/ +int sst_play_frame(int str_id) +{ + int i = 0, retval = 0; + struct ipc_post *msg = NULL; + struct sst_frame_info sg_list = {0}; + struct sst_stream_bufs *kbufs = NULL, *_kbufs; + struct stream_info *stream; + + pr_debug("play frame for %d\n", str_id); + retval = sst_validate_strid(str_id); + if (retval) + return retval; + + stream = &sst_drv_ctx->streams[str_id]; + /* clear prev sent buffers */ + list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) { + if (kbufs->in_use == true) { + list_del(&kbufs->node); + kfree(kbufs); + pr_debug("del node \n"); + } + } + /* update bytes sent */ + stream->cumm_bytes += stream->curr_bytes; + stream->curr_bytes = 0; + if (list_empty(&stream->bufs)) { + /* no user buffer available */ + pr_debug("Null buffer!!!!stream status = %d \n", stream->status); + stream->prev = stream->status; + stream->status = STREAM_INIT; + pr_debug("new stream status = %d \n", stream->status); + if (stream->need_draining == true) { + pr_debug("draining stream \n"); + if (sst_create_short_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d +\ + ERR: short message mem alloc failed\n", \ + get_seconds(), sst_get_time(), \ + __func__ , __LINE__); + return -ENOMEM; + } + sst_fill_header(&msg->header, IPC_IA_DRAIN_STREAM, + 0, str_id); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, + &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + } else if (stream->data_blk.on == true) { + pr_debug("user list is empty.. wake \n"); + /* unblock */ + stream->data_blk.ret_code = 0; + stream->data_blk.condition = true; + stream->data_blk.on = false; + wake_up(&sst_drv_ctx->wait_queue); + } +#ifdef CONFIG_MSTWN_POWER_MGMT + sst_ospm_send_event(OSPM_EVENT_AUDIO_BUF_EMPTY); +#endif + return 0; + } + + /* create list */ + i = sst_create_sg_list(stream, &sg_list); + + /* post msg */ + if (sst_create_large_msg(&msg)) + return -ENOMEM; + + sst_fill_header(&msg->header, IPC_IA_PLAY_FRAMES, 1, str_id); + msg->header.part.data = sizeof(u32) + sizeof(sg_list); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), &sg_list, sizeof(sg_list)); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + return 0; + +} + +/** +* sst_capture_frame - Send msg for sending stream frames +* @str_id: ID of stream +* +* This function is called to capture data from the firmware +*/ +int sst_capture_frame(int str_id) +{ + int i = 0, retval = 0; + struct ipc_post *msg = NULL; + struct sst_frame_info sg_list = {0}; + struct sst_stream_bufs *kbufs = NULL, *_kbufs; + struct stream_info *stream; + + + pr_debug("capture frame for %d\n", str_id); + retval = sst_validate_strid(str_id); + if (retval) + return retval; + + stream = &sst_drv_ctx->streams[str_id]; + /*update bytes sent*/ + /*stream->cumm_bytes += stream->curr_bytes; + stream->curr_bytes = 0;*/ + /* clear prev sent buffers */ + list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) { + if (kbufs->in_use == true) { + list_del(&kbufs->node); + kfree(kbufs); + pr_debug("del node \n"); + } + } + if (list_empty(&stream->bufs)) { + /* no user buffer available */ + pr_debug("Null buffer!!!!stream status = %d \n", stream->status); + stream->prev = stream->status; + stream->status = STREAM_INIT; + pr_debug("new stream status = %d \n", stream->status); + if (stream->data_blk.on == true) { + pr_debug("user list is empty.. wake \n"); + /* unblock */ + stream->data_blk.ret_code = 0; + stream->data_blk.condition = true; + stream->data_blk.on = false; + wake_up(&sst_drv_ctx->wait_queue); + + } +#ifdef CONFIG_MSTWN_POWER_MGMT + sst_ospm_send_event(OSPM_EVENT_AUDIO_BUF_FULL); +#endif + return 0; + } + + /* create new sg list */ + i = sst_create_sg_list(stream, &sg_list); + + /* post msg */ + if (sst_create_large_msg(&msg)) + return -ENOMEM; + + sst_fill_header(&msg->header, IPC_IA_CAPT_FRAMES, 1, str_id); + msg->header.part.data = sizeof(u32) + sizeof(sg_list); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), &sg_list, sizeof(sg_list)); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + + + /*update bytes recevied*/ + stream->cumm_bytes += stream->curr_bytes; + stream->curr_bytes = 0; + + pr_debug("Cum bytes = %d \n", stream->cumm_bytes); + return 0; +} + +/** +* This function is used to calculate the minimum size of input buffers given +*/ +static unsigned int calculate_min_size(struct snd_sst_buffs *bufs) +{ + int i, min_val = bufs->buff_entry[0].size; + for (i = 1 ; i < bufs->entries; i++) { + if (bufs->buff_entry[i].size < min_val) + min_val = bufs->buff_entry[i].size; + } + pr_debug("min_val = %d\n", min_val); + return min_val; +} + +/** +* This function is used to calculate the maximum size of input buffers given +*/ +static unsigned int calculate_max_size(struct snd_sst_buffs *bufs) +{ + int i, max_val = bufs->buff_entry[0].size; + for (i = 1 ; i < bufs->entries; i++) { + if (bufs->buff_entry[i].size > max_val) + max_val = bufs->buff_entry[i].size; + } + pr_debug("max_val = %d\n", max_val); + return max_val; +} + +/** +* This function is used to allocate input and output buffers to be sent to +* the firmware that will take encoded data and return decoded data +*/ +static int sst_allocate_decode_buf(struct stream_info *str_info, + struct snd_sst_dbufs *dbufs, + unsigned int cum_input_given, + unsigned int cum_output_given) +{ + if (str_info->ops == STREAM_OPS_PLAYBACK_DRM) { + if (dbufs->ibufs->entries == dbufs->obufs->entries) + return 0; + else { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + RAR buffer entries do not match \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + } + if (!str_info->decode_ibuf) { + pr_debug("no input buffers, trying full size\n"); + str_info->decode_isize = cum_input_given; + str_info->decode_ibuf = kzalloc(str_info->decode_isize, + GFP_KERNEL); + str_info->idecode_alloc = str_info->decode_isize; + } + if (!str_info->decode_ibuf) { + pr_debug("buff alloaction failed, trying max size\n"); + str_info->decode_isize = calculate_max_size(dbufs->ibufs); + str_info->decode_ibuf = kzalloc(str_info->decode_isize, + GFP_KERNEL); + str_info->idecode_alloc = str_info->decode_isize; + } + if (!str_info->decode_ibuf) { + pr_debug("buff alloaction failed, trying min size\n"); + str_info->decode_isize = calculate_min_size(dbufs->ibufs); + str_info->decode_ibuf = kzalloc(str_info->decode_isize, + GFP_KERNEL); + if (!str_info->decode_ibuf) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + mem allocation failed\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -ENOMEM; + } + str_info->idecode_alloc = str_info->decode_isize; + } + str_info->decode_osize = cum_output_given; + if (str_info->decode_osize > sst_drv_ctx->mmap_len) + str_info->decode_osize = sst_drv_ctx->mmap_len; + pr_debug("input buffers allocated at %x of %d ouput buffer size = %d\n", + (unsigned int) str_info->decode_ibuf, + str_info->idecode_alloc, str_info->decode_osize); + return 0; +} + +/** +* This function is used to send the message to firmware to decode the data +*/ +static int sst_send_decode_mess(int str_id, struct stream_info *str_info, + struct snd_sst_decode_info *dec_info) +{ + struct ipc_post *msg = NULL; + int retval = 0; + + pr_debug("called \n"); + + dec_info->frames_in.addr[0].addr = virt_to_phys((void *) + str_info->decode_ibuf); + dec_info->frames_in.addr[0].size = str_info->decode_isize; + dec_info->frames_out.addr[0].addr = virt_to_phys((void *) + str_info->decode_obuf); + dec_info->frames_out.addr[0].size = str_info->decode_osize; + + dec_info->frames_in.num_entries = 1; + dec_info->frames_out.num_entries = 1; + dec_info->frames_in.rsrvd = 0; + dec_info->frames_out.rsrvd = 0; + dec_info->input_bytes_consumed = 0; + dec_info->output_bytes_produced = 0; + if (sst_create_large_msg(&msg)) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + message creation failed\n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -ENOMEM; + } + + sst_fill_header(&msg->header, IPC_IA_DECODE_FRAMES, 1, str_id); + msg->header.part.data = sizeof(u32) + sizeof(*dec_info); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), dec_info, + sizeof(*dec_info)); + mutex_lock(&sst_drv_ctx->list_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + mutex_unlock(&sst_drv_ctx->list_lock); + str_info->data_blk.condition = false; + str_info->data_blk.ret_code = 0; + str_info->data_blk.on = true; + str_info->data_blk.data = dec_info; + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_interruptible(sst_drv_ctx, &str_info->data_blk); + return retval; +} + +/** +* This function is used to prepare the kernel input buffers with contents before +* sendind for decode +*/ +static int sst_prepare_input_buffers(struct stream_info *str_info, + struct snd_sst_dbufs *dbufs, + int *input_index, int *in_copied) +{ + int i, cpy_size, retval = 0; + + pr_debug("input_index = %d, input entries = %d\n", *input_index, + dbufs->ibufs->entries); + for (i = *input_index; i < dbufs->ibufs->entries; i++) { +#ifdef CONFIG_MRST_RAR_HANDLER + if (str_info->ops == STREAM_OPS_PLAYBACK_DRM) { + struct RAR_buffer rar_buffers; + struct RAR_block_info *info; + retval = copy_from_user(&info, + dbufs->ibufs->buff_entry[i].buffer, + sizeof(struct RAR_block_info)); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d +\ + ERR: copy from user failed\n", get_seconds(),\ + sst_get_time(), __func__, __LINE__); + return -EAGAIN; + } + rar_buffers.info.type = info->type; + rar_buffers.info.size = info->size; + rar_buffers.info.handle = info->handle; + retval = sst_get_RAR(&rar_buffers, 1); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d +\ + ERR: RAR API failed\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return retval; + } + dbufs->ibufs->buff_entry[i].buffer = + (void *) rar_buffers.bus_address; + dbufs->ibufs->buff_entry[i].size = + rar_buffers.info.size; + *input_index = i + 1; + str_info->decode_isize = info->size; + break; + } +#endif + *input_index = i; + pr_debug("inout addr = %p, size = %d\n", + dbufs->ibufs->buff_entry[i].buffer, + dbufs->ibufs->buff_entry[i].size); + pr_debug("decode_isize = %d, in_copied = %d\n", + str_info->decode_isize, *in_copied); + if (dbufs->ibufs->buff_entry[i].size <= + (str_info->decode_isize - *in_copied)) + cpy_size = dbufs->ibufs->buff_entry[i].size; + else + cpy_size = str_info->decode_isize - *in_copied; + + pr_debug("cpy size = %d\n", cpy_size); + if (!dbufs->ibufs->buff_entry[i].buffer) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + input buffer is null\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + pr_debug("Trying copy To: %p, From %p, size %d\n", + str_info->decode_ibuf + *in_copied, + dbufs->ibufs->buff_entry[i].buffer, cpy_size); + + retval = + copy_from_user((void *)(str_info->decode_ibuf + *in_copied), + (void *) dbufs->ibufs->buff_entry[i].buffer, + cpy_size); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + copy from user failed \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -EIO; + } + *in_copied += cpy_size; + dbufs->ibufs->buff_entry[i].size -= cpy_size; + pr_debug("in buff size = %d, in_copied = %d\n", + dbufs->ibufs->buff_entry[i].size, *in_copied); + if (dbufs->ibufs->buff_entry[i].size != 0) { + pr_debug("more input buffers left \n"); + dbufs->ibufs->buff_entry[i].buffer += + cpy_size; + break; + } + if (*in_copied == str_info->decode_isize && + dbufs->ibufs->buff_entry[i].size == 0) { + pr_debug("all input buffers copied\n"); + *input_index = i + 1; + break; + } + } + return retval; +} + +/** +* This function is used to copy the decoded data from kernel buffers to +* the user output buffers with contents after decode +*/ +static int sst_prepare_output_buffers(struct stream_info *str_info, + struct snd_sst_dbufs *dbufs, + int *output_index, int output_size, + int *out_copied) + +{ + int i, cpy_size, retval = 0; + pr_debug("output_index = %d, output entries = %d\n", + *output_index, + dbufs->obufs->entries); + for (i = *output_index; i < dbufs->obufs->entries; i++) { + *output_index = i; + pr_debug("output addr = %p, size = %d\n", + dbufs->obufs->buff_entry[i].buffer, + dbufs->obufs->buff_entry[i].size); + pr_debug("output_size = %d, out_copied = %d\n", + output_size, *out_copied); + if (dbufs->obufs->buff_entry[i].size < + (output_size - *out_copied)) + cpy_size = dbufs->obufs->buff_entry[i].size; + else + cpy_size = output_size - *out_copied; + pr_debug("cpy size = %d\n", cpy_size); + pr_debug("Trying copy To: %p, From %p, size %d\n", + dbufs->obufs->buff_entry[i].buffer, + sst_drv_ctx->mmap_mem + *out_copied, + cpy_size); + retval = copy_to_user(dbufs->obufs->buff_entry[i].buffer, + sst_drv_ctx->mmap_mem + *out_copied, + cpy_size); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + copy to user failed \n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -EIO; + } else + pr_debug("copy to user passed \n"); + *out_copied += cpy_size; + dbufs->obufs->buff_entry[i].size -= cpy_size; + pr_debug("output buff size = %d, out_copied = %d\n", + dbufs->obufs->buff_entry[i].size, *out_copied); + if (dbufs->obufs->buff_entry[i].size != 0) { + *output_index = i; + dbufs->obufs->buff_entry[i].buffer += cpy_size; + break; + } else if (*out_copied == output_size) { + *output_index = i + 1; + break; + } + } + return retval; +} + +/** +* sst_decode - Send msg for decoding frames +* @str_id: ID of stream +* @dbufs - param that holds the user input and output buffers and sizes +* This function is called to decode data from the firmware +*/ +int sst_decode(int str_id, struct snd_sst_dbufs *dbufs) +{ + int retval = 0, i; + unsigned long long total_input = 0 , total_output = 0; + unsigned int cum_input_given = 0 , cum_output_given = 0; + int copy_in_done = false, copy_out_done = false; + int input_index = 0, output_index = 0; + int in_copied, out_copied; + u64 output_size; + struct stream_info *str_info; + struct snd_sst_decode_info dec_info; + + pr_debug("...called \n"); + + retval = sst_validate_strid(str_id); + if (retval) + return retval; + + str_info = &sst_drv_ctx->streams[str_id]; + if (str_info->status != STREAM_INIT) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: invalid +\ + stream state = %d\n", get_seconds(), sst_get_time(), __func__ ,\ + __LINE__, str_info->status); + return -EINVAL; + } + + str_info->prev = str_info->status; + str_info->status = STREAM_DECODE; + for (i = 0; i < dbufs->ibufs->entries; i++) + cum_input_given += dbufs->ibufs->buff_entry[i].size; + for (i = 0; i < dbufs->obufs->entries; i++) + cum_output_given += dbufs->obufs->buff_entry[i].size; + /* input and output buffer allocation */ + retval = sst_allocate_decode_buf(str_info, dbufs, + cum_input_given, cum_output_given); + str_info->decode_isize = str_info->idecode_alloc; + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + mem allocation failed, abort!!!\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + retval = -ENOMEM; + goto finish; + } + while ((copy_out_done == false) && (copy_in_done == false)) { + in_copied = 0; + retval = sst_prepare_input_buffers(str_info, dbufs, &input_index, + &in_copied); + pr_debug("prep inbuf ret %d\n", retval); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + prepare in buffers failed\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + goto finish; + } + if (str_info->ops != STREAM_OPS_PLAYBACK_DRM) + str_info->decode_obuf = sst_drv_ctx->mmap_mem; + else { +#ifdef CONFIG_MRST_RAR_HANDLER + struct RAR_buffer rar_buffers; + struct RAR_block_info *info; + retval = copy_from_user(&info, + dbufs->obufs->buff_entry[output_index].buffer, + sizeof(struct RAR_block_info)); // This is buggy! + rar_buffers.info.type = info->type; + rar_buffers.info.size = info->size; + rar_buffers.info.handle = info->handle; + retval = sst_get_RAR(&rar_buffers, 1); + if (retval) + return retval; + str_info->decode_obuf = (void *)rar_buffers.bus_address; + str_info->decode_osize = rar_buffers.info.size; +#endif + pr_debug("DRM handling\n"); + } + if (str_info->decode_isize > in_copied) { + str_info->decode_isize = in_copied; + pr_debug("input size modified = %d\n", + str_info->decode_isize); + } + + retval = sst_send_decode_mess(str_id, str_info, &dec_info); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d ERR:+\ + sending message failed\n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + goto finish; + } + pr_debug("in_copied = %d, consumed = %lld, produced = %lld\n", + in_copied, + dec_info.input_bytes_consumed, + dec_info.output_bytes_produced); + if (str_info->ops == STREAM_OPS_PLAYBACK_DRM) { + output_index += 1; + if (output_index == dbufs->obufs->entries) { + copy_in_done = true; + pr_debug("all input copy done\n"); + } + total_output += dec_info.output_bytes_produced; + } else { + out_copied = 0; + output_size = dec_info.output_bytes_produced; + retval = sst_prepare_output_buffers(str_info, dbufs, + &output_index, output_size, &out_copied); + if (retval) { + dev_err(&sst_drv_ctx->pci->dev, "Time:%8ld::%8ld >> SST: %s %d +\ + ERR: prepare out buffers failed\n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto finish; + } + if (in_copied != dec_info.input_bytes_consumed) { + pr_debug("input left to be copied = %lld \n", + in_copied - dec_info.input_bytes_consumed); + input_index--; + dbufs->ibufs->buff_entry[input_index].size += + (in_copied - + dec_info.input_bytes_consumed); + dbufs->ibufs->buff_entry[input_index].buffer += + dec_info.input_bytes_consumed; + } + total_output += out_copied; + if (str_info->decode_osize != out_copied) { + str_info->decode_osize -= out_copied; + pr_debug("output size modified = %d\n", + str_info->decode_osize); + } + } + total_input += dec_info.input_bytes_consumed; + if (total_output == cum_output_given) { + copy_out_done = true; + pr_debug("all output copy done\n"); + } + if (total_input == cum_input_given) { + copy_in_done = true; + pr_debug("all input copy done\n"); + } + + pr_debug("copy_out_done = %d, copy_in_done = %d \n", + copy_out_done, copy_in_done); + } + +finish: + dbufs->input_bytes_consumed = total_input; + dbufs->output_bytes_produced = total_output; + str_info->status = str_info->prev; + str_info->prev = STREAM_DECODE; + + return retval; +} Index: linux-2.6.33/sound/pci/sst/intelmid_snd_control.h =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intelmid_snd_control.h @@ -0,0 +1,112 @@ +#ifndef __INTELMID_SND_CTRL_H__ +#define __INTELMID_SND_CTRL_H__ +/* + * intelmid_snd_control.h - Intel Sound card driver for MID + * + * Copyright (C) 2008-09 Intel Corporation + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file defines all snd control functions + */ + +/* +Mask bits +*/ +#define MASK0 0x01 /* 0000 0001 */ +#define MASK1 0x02 /* 0000 0010 */ +#define MASK2 0x04 /* 0000 0100 */ +#define MASK3 0x08 /* 0000 1000 */ +#define MASK4 0x10 /* 0001 0000 */ +#define MASK5 0x20 /* 0010 0000 */ +#define MASK6 0x40 /* 0100 0000 */ +#define MASK7 0x80 /* 1000 0000 */ +/* +value bits +*/ +#define VALUE0 0x01 /* 0000 0001 */ +#define VALUE1 0x02 /* 0000 0010 */ +#define VALUE2 0x04 /* 0000 0100 */ +#define VALUE3 0x08 /* 0000 1000 */ +#define VALUE4 0x10 /* 0001 0000 */ +#define VALUE5 0x20 /* 0010 0000 */ +#define VALUE6 0x40 /* 0100 0000 */ +#define VALUE7 0x80 /* 1000 0000 */ + +#define MUTE 0 /* ALSA Passes 0 for mute */ +#define UNMUTE 1 /* ALSA Passes 1 for unmute */ + +#define MAX_VOL_PMIC_VENDOR0 0x3f /* max vol in dB for stereo & voice DAC */ +#define MIN_VOL_PMIC_VENDOR0 0 /* min vol in dB for stereo & voice DAC */ +/* Head phone volume control */ +#define MAX_HP_VOL_PMIC_VENDOR1 6 /* max volume in dB for HP */ +#define MIN_HP_VOL_PMIC_VENDOR1 (-84) /* min volume in dB for HP */ +#define MAX_HP_VOL_INDX_PMIC_VENDOR1 40 /* Number of HP volume control values */ + +/* Mono Earpiece Volume control */ +#define MAX_EP_VOL_PMIC_VENDOR1 0 /* max volume in dB for EP */ +#define MIN_EP_VOL_PMIC_VENDOR1 (-75) /* min volume in dB for EP */ +#define MAX_EP_VOL_INDX_PMIC_VENDOR1 32 /* Number of EP volume control values */ + +int sst_sc_reg_access(struct sc_reg_access *sc_access, + int type, int num_val); +extern struct snd_pmic_ops snd_pmic_ops_fs; +extern struct snd_pmic_ops snd_pmic_ops_mx; +extern struct snd_pmic_ops snd_pmic_ops_nc; + +/* device */ +enum SND_INPUT_DEVICE { + HS_MIC, + AMIC, + DMIC, + IN_UNDEFINED +}; + +enum SND_OUTPUT_DEVICE { + STEREO_HEADPHONE, + INTERNAL_SPKR, + OUT_UNDEFINED +}; + +enum SND_CARDS { + SND_FS = 0, + SND_MX, + SND_NC, +}; + +enum pmic_controls { + PMIC_SND_HP_MIC_MUTE = 0x0001, + PMIC_SND_AMIC_MUTE = 0x0002, + PMIC_SND_DMIC_MUTE = 0x0003, + PMIC_SND_CAPTURE_VOL = 0x0004, +/* Output controls */ + PMIC_SND_LEFT_PB_VOL = 0x0010, + PMIC_SND_RIGHT_PB_VOL = 0x0011, + PMIC_SND_LEFT_HP_MUTE = 0x0012, + PMIC_SND_RIGHT_HP_MUTE = 0x0013, + PMIC_SND_LEFT_SPEAKER_MUTE = 0x0014, + PMIC_SND_RIGHT_SPEAKER_MUTE = 0x0015, +/* Other controls */ + PMIC_SND_MUTE_ALL = 0x0020, + PMIC_MAX_CONTROLS = 0x0020, +}; + +#endif /* __INTELMID_SND_CTRL_H__ */ + + Index: linux-2.6.33/sound/pci/sst/intelmid_v0_control.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intelmid_v0_control.c @@ -0,0 +1,732 @@ +/* + * intel_sst_v0_control.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corporation + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains the control operations of vendor 1 + */ + +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/workqueue.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" +#include "intelmid_snd_control.h" + + +enum _reg_v1 { + VOICEPORT1 = 0x180, + VOICEPORT2 = 0x181, + AUDIOPORT1 = 0x182, + AUDIOPORT2 = 0x183, + MISCVOICECTRL = 0x184, + MISCAUDCTRL = 0x185, + DMICCTRL1 = 0x186, + DMICCTRL2 = 0x187, + MICCTRL = 0x188, + MICLICTRL1 = 0x189, + MICLICTRL2 = 0x18A, + MICLICTRL3 = 0x18B, + VOICEDACCTRL1 = 0x18C, + STEREOADCCTRL = 0x18D, + AUD15 = 0x18E, + AUD16 = 0x18F, + AUD17 = 0x190, + AUD18 = 0x191, + RMIXOUTSEL = 0x192, + ANALOGLBR = 0x193, + ANALOGLBL = 0x194, + POWERCTRL1 = 0x195, + POWERCTRL2 = 0x196, + HEADSETDETECTINT = 0x197, + HEADSETDETECTINTMASK = 0x198, + TRIMENABLE = 0x199, +}; + +int rev_id = 0x20; + +int fs_init_card(void) +{ + struct sc_reg_access sc_access[] = { + {0x180, 0x00, 0x0}, + {0x181, 0x00, 0x0}, + {0x182, 0xF8, 0x0}, + {0x183, 0x08, 0x0}, + {0x184, 0x00, 0x0}, + {0x185, 0x40, 0x0}, + {0x186, 0x06, 0x0}, + {0x187, 0x80, 0x0}, + {0x188, 0x00, 0x0}, + {0x189, 0x39, 0x0}, + {0x18a, 0x39, 0x0}, + {0x18b, 0x1F, 0x0}, + {0x18c, 0x00, 0x0}, + {0x18d, 0x00, 0x0}, + {0x18e, 0x39, 0x0}, + {0x18f, 0x39, 0x0}, + {0x190, 0x39, 0x0}, + {0x191, 0x11, 0x0}, + {0x192, 0x0E, 0x0}, + {0x193, 0x00, 0x0}, + {0x194, 0x00, 0x0}, + {0x195, 0x06, 0x0}, + {0x196, 0x7F, 0x0}, + {0x197, 0x00, 0x0}, + {0x198, 0x0B, 0x0}, + {0x199, 0x00, 0x0}, + {0x037, 0x3F, 0x0}, + }; + + snd_pmic_ops_fs.card_status = SND_CARD_INIT_DONE; + return sst_sc_reg_access(sc_access, PMIC_WRITE, 27); +} + +int fs_power_up_pb(void) +{ + struct sc_reg_access sc_access[] = { + {POWERCTRL1, 0xC6, 0xC6}, + {POWERCTRL2, 0x30, 0x30}, + }; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + +} + +int fs_power_up_cp(void) +{ + struct sc_reg_access sc_access[] = { + {POWERCTRL2, 0x02, 0x02}, /*NOTE power up A ADC only as*/ + /*as turning on V ADC causes noise*/ + }; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); +} + +int fs_power_down_pb(void) +{ + struct sc_reg_access sc_access[] = { + {POWERCTRL1, 0x00, 0xC6}, + {POWERCTRL2, 0x00, 0x30}, + }; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); +} + +int fs_power_down(void) +{ + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + return retval; +} + +int fs_power_down_cp(void) +{ + struct sc_reg_access sc_access[] = { + {POWERCTRL2, 0x00, 0x03}, + }; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); +} + +int fs_set_pcm_voice_params(void) +{ + struct sc_reg_access sc_access[] = { + {0x180, 0xA0, 0}, + {0x181, 0x04, 0}, + {0x182, 0x0, 0}, + {0x183, 0x0, 0}, + {0x184, 0x18, 0}, + {0x185, 0x40, 0}, + {0x186, 0x06, 0}, + {0x187, 0x0, 0}, + {0x188, 0x10, 0}, + {0x189, 0x39, 0}, + {0x18a, 0x39, 0}, + {0x18b, 0x02, 0}, + {0x18c, 0x0, 0}, + {0x18d, 0x0, 0}, + {0x18e, 0x39, 0}, + {0x18f, 0x0, 0}, + {0x190, 0x0, 0}, + {0x191, 0x20, 0}, + {0x192, 0x20, 0}, + {0x193, 0x0, 0}, + {0x194, 0x0, 0}, + {0x195, 0x6, 0}, + {0x196, 0x25, 0}, + {0x197, 0x0, 0}, + {0x198, 0xF, 0}, + {0x199, 0x0, 0}, + }; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + return sst_sc_reg_access(sc_access, PMIC_WRITE, 26); +} + +int fs_set_audio_port(int status) +{ + struct sc_reg_access sc_access[2]; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + if (status == DEACTIVATE) { + /* Deactivate audio port-tristate and power */ + sc_access[0].value = 0x00; + sc_access[0].mask = MASK6|MASK7; + sc_access[0].reg_addr = AUDIOPORT1; + sc_access[1].value = 0x00; + sc_access[1].mask = MASK4|MASK5; + sc_access[1].reg_addr = POWERCTRL2; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + } else if (status == ACTIVATE) { + /* activate audio port */ + sc_access[0].value = 0xC0; + sc_access[0].mask = MASK6|MASK7; + sc_access[0].reg_addr = AUDIOPORT1; + sc_access[1].value = 0x30; + sc_access[1].mask = MASK4|MASK5; + sc_access[1].reg_addr = POWERCTRL2; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + } else + return -EINVAL; +} + +int fs_set_voice_port(int status) +{ + struct sc_reg_access sc_access[2]; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + if (status == DEACTIVATE) { + /* Deactivate audio port-tristate and power */ + sc_access[0].value = 0x00; + sc_access[0].mask = MASK6|MASK7; + sc_access[0].reg_addr = VOICEPORT1; + sc_access[1].value = 0x00; + sc_access[1].mask = MASK0|MASK1; + sc_access[1].reg_addr = POWERCTRL2; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + } else if (status == ACTIVATE) { + /* activate audio port */ + sc_access[0].value = 0xC0; + sc_access[0].mask = MASK6|MASK7; + sc_access[0].reg_addr = VOICEPORT1; + sc_access[1].value = 0x03; + sc_access[1].mask = MASK0|MASK1; + sc_access[1].reg_addr = POWERCTRL2; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + } else + return -EINVAL; +} + +int fs_set_pcm_audio_params(int sfreq, int word_size) +{ + u8 config1 = 0; + struct sc_reg_access sc_access[2]; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + switch (sfreq) { + case 8000: + config1 = 0x00; + break; + case 11025: + config1 = 0x01; + break; + case 12000: + config1 = 0x02; + break; + case 16000: + config1 = 0x03; + break; + case 22050: + config1 = 0x04; + break; + case 24000: + config1 = 0x05; + break; + case 26000: + config1 = 0x06; + break; + case 32000: + config1 = 0x07; + break; + case 44100: + config1 = 0x08; + break; + case 48000: + config1 = 0x09; + break; + } + + pr_debug("sfreq:%d, Register value = %x\n", sfreq, config1); + /* FIXME 24 bit audio settings */ + if (word_size == 24) { + /* config1 = config1 | 0x10;*/ + sc_access[0].mask = MASK4; + sc_access[0].value = 0x10; + + } + sc_access[0].reg_addr = AUDIOPORT2; + sc_access[0].value = config1; + sc_access[0].mask = MASK0|MASK1|MASK2|MASK3; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); +} + +int fs_set_selected_input_dev(u8 value) +{ + struct sc_reg_access sc_access_dmic[] = { + {MICCTRL, 0x85, 0xf7}, + {MICLICTRL3, 0x00, 0xE0}, + }; + struct sc_reg_access sc_access_mic[] = { + {MICCTRL, 0x10, MASK2|MASK4|MASK5|MASK6|MASK7}, + {MICLICTRL3, 0x00, 0xE0}, + }; + struct sc_reg_access sc_access_hsmic[] = { + {MICCTRL, 0x40, MASK2|MASK4|MASK5|MASK6|MASK7}, + {MICLICTRL3, 0x00, 0xE0}, + }; + + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + + switch (value) { + case AMIC: + pr_debug("Selecting amic not supported in mono cfg\n"); + return sst_sc_reg_access(sc_access_mic, PMIC_READ_MODIFY, 2); + break; + + case HS_MIC: + pr_debug("Selecting hsmic\n"); + return sst_sc_reg_access(sc_access_hsmic, + PMIC_READ_MODIFY, 2); + break; + + case DMIC: + pr_debug("Selecting dmic\n"); + return sst_sc_reg_access(sc_access_dmic, PMIC_READ_MODIFY, 2); + break; + + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + rcvd illegal request \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -EINVAL; + + } +} + +int fs_set_selected_output_dev(u8 value) +{ + struct sc_reg_access sc_access_hp[] = { + {0x191, 0x11, 0x0}, + {0x192, 0x0E, 0x0}, + {0x195, 0x06, 0x0}, + {0x196, 0x7F, 0x0}, + }; + struct sc_reg_access sc_access_is[] = { + {0x191, 0x17, 0xFF}, + {0x192, 0x08, 0xFF}, + {0x195, 0xC0, 0xFF}, + {0x196, 0x12, 0x12}, + }; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + + switch (value) { + case STEREO_HEADPHONE: + pr_debug("Selecting headphone \n"); + return sst_sc_reg_access(sc_access_hp, PMIC_WRITE, 4); + break; + + case INTERNAL_SPKR: + pr_debug("Selecting internal spkr\n"); + return sst_sc_reg_access(sc_access_is, PMIC_READ_MODIFY, 4); + break; + + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + rcvd illegal request \n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -EINVAL; + + } +} + +int fs_set_mute(int dev_id, u8 value) +{ + struct sc_reg_access sc_access[6] = {{0,},}; + int reg_num = 0; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + + pr_debug("dev_id:0x%x value:0x%x\n", dev_id, value); + switch (dev_id) { + case PMIC_SND_DMIC_MUTE: + sc_access[0].reg_addr = MICCTRL; + sc_access[1].reg_addr = MICLICTRL1; + sc_access[2].reg_addr = MICLICTRL2; + sc_access[0].mask = MASK5; + sc_access[1].mask = sc_access[2].mask = MASK6; + if (value == MUTE) { + sc_access[0].value = 0x20; + sc_access[2].value = sc_access[1].value = 0x40; + } else + sc_access[0].value = sc_access[1].value + = sc_access[2].value = 0x0; + reg_num = 3; + break; + case PMIC_SND_HP_MIC_MUTE: + case PMIC_SND_AMIC_MUTE: + sc_access[0].reg_addr = MICLICTRL1; + sc_access[1].reg_addr = MICLICTRL2; + sc_access[0].mask = sc_access[1].mask = MASK6; + if (value == MUTE) + sc_access[0].value = sc_access[1].value = 0x40; + else + sc_access[0].value = sc_access[1].value = 0x0; + reg_num = 2; + break; + case PMIC_SND_LEFT_SPEAKER_MUTE: + case PMIC_SND_LEFT_HP_MUTE: + sc_access[0].reg_addr = AUD16; + sc_access[1].reg_addr = AUD15; + + sc_access[0].mask = sc_access[1].mask = MASK7; + if (value == MUTE) + sc_access[0].value = sc_access[1].value = 0x80; + else + sc_access[0].value = sc_access[1].value = 0x0; + reg_num = 2; + break; + case PMIC_SND_RIGHT_HP_MUTE: + case PMIC_SND_RIGHT_SPEAKER_MUTE: + sc_access[0].reg_addr = AUD17; + sc_access[1].reg_addr = AUD15; + sc_access[0].mask = sc_access[1].mask = MASK7; + if (value == MUTE) + sc_access[0].value = sc_access[1].value = 0x80; + else + sc_access[0].value = sc_access[1].value = 0x0; + reg_num = 2; + break; + case PMIC_SND_MUTE_ALL: + sc_access[0].reg_addr = AUD16; + sc_access[1].reg_addr = AUD17; + sc_access[2].reg_addr = AUD15; + sc_access[3].reg_addr = MICCTRL; + sc_access[4].reg_addr = MICLICTRL1; + sc_access[5].reg_addr = MICLICTRL2; + sc_access[0].mask = sc_access[1].mask = + sc_access[2].mask = MASK7; + sc_access[3].mask = MASK5; + sc_access[4].mask = sc_access[5].mask = MASK6; + + if (value == MUTE) { + sc_access[0].value = + sc_access[1].value = sc_access[2].value = 0x80; + sc_access[3].value = 0x20; + sc_access[4].value = sc_access[5].value = 0x40; + + } else { + sc_access[0].value = sc_access[1].value = + sc_access[2].value = sc_access[3].value = + sc_access[4].value = sc_access[5].value = 0x0; + } + reg_num = 6; + break; + + } + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, reg_num); +} + +int fs_set_vol(int dev_id, u8 value) +{ + struct sc_reg_access sc_access[3] = {{0},}; + int reg_num = 0; + int retval = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + + switch (dev_id) { + case PMIC_SND_LEFT_PB_VOL: + pr_debug("PMIC_SND_LEFT_PB_VOL:%d \n", value); + sc_access[0].value = sc_access[1].value = value; + sc_access[0].reg_addr = AUD16; + sc_access[1].reg_addr = AUD15; + sc_access[0].mask = sc_access[1].mask = + (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); + reg_num = 2; + break; + + case PMIC_SND_RIGHT_PB_VOL: + pr_debug("PMIC_SND_RIGHT_PB_VOL:%d \n", value); + sc_access[0].value = sc_access[1].value = value; + sc_access[0].reg_addr = AUD17; + sc_access[1].reg_addr = AUD15; + sc_access[0].mask = sc_access[1].mask = + (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); + reg_num = 2; + break; + + case PMIC_SND_CAPTURE_VOL: + pr_debug("PMIC_SND_CAPTURE_VOL:%d \n", value); + sc_access[0].reg_addr = MICLICTRL1; + sc_access[1].reg_addr = MICLICTRL2; + sc_access[0].value = sc_access[1].value = value; + sc_access[2].reg_addr = DMICCTRL1; + sc_access[2].value = 0x3F - value; + sc_access[0].mask = sc_access[1].mask = + sc_access[2].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); + reg_num = 3; + break; + + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + rcvd illegal request \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, reg_num); +} + +int fs_get_mute(int dev_id, u8 *value) +{ + struct sc_reg_access sc_access[6] = {{0,},}; + + int retval = 0, temp_value = 0, mask = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + + switch (dev_id) { + + case PMIC_SND_AMIC_MUTE: + case PMIC_SND_HP_MIC_MUTE: + sc_access[0].reg_addr = MICLICTRL1; + mask = MASK6; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + if (sc_access[0].value & mask) + *value = MUTE; + else + *value = UNMUTE; + break; + case PMIC_SND_DMIC_MUTE: + sc_access[0].reg_addr = MICCTRL; + mask = MASK5; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + temp_value = (sc_access[0].value & mask); + if (temp_value == 0) + *value = UNMUTE; + else + *value = MUTE; + break; + + case PMIC_SND_LEFT_HP_MUTE: + case PMIC_SND_LEFT_SPEAKER_MUTE: + sc_access[0].reg_addr = AUD16; + mask = MASK7; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + temp_value = sc_access[0].value & mask; + if (temp_value == 0) + *value = UNMUTE; + else + *value = MUTE; + break; + case PMIC_SND_RIGHT_HP_MUTE: + case PMIC_SND_RIGHT_SPEAKER_MUTE: + sc_access[0].reg_addr = AUD17; + mask = MASK7; + retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); + temp_value = sc_access[0].value & mask; + if (temp_value == 0) + *value = UNMUTE; + else + *value = MUTE; + break; + case PMIC_SND_MUTE_ALL: + sc_access[0].reg_addr = AUD15; + sc_access[1].reg_addr = AUD16; + sc_access[2].reg_addr = AUD17; + sc_access[3].reg_addr = MICCTRL; + sc_access[4].reg_addr = MICLICTRL1; + sc_access[5].reg_addr = MICLICTRL2; + sc_access[0].mask = sc_access[1].mask = + sc_access[2].mask = MASK7; + sc_access[3].mask = MASK5; + sc_access[4].mask = sc_access[5].mask = MASK6; + + retval = sst_sc_reg_access(sc_access, PMIC_READ, 6); + if (((sc_access[0].value & sc_access[0].mask) == + sc_access[0].mask) + && ((sc_access[1].value & sc_access[1].mask) == + sc_access[1].mask) + && ((sc_access[2].value & sc_access[2].mask) == + sc_access[2].mask) + && ((sc_access[3].value & sc_access[3].mask) == + sc_access[3].mask) + && ((sc_access[4].value & sc_access[4].mask) == + sc_access[4].mask) + && ((sc_access[5].value & sc_access[5].mask) == + sc_access[5].mask)) + *value = MUTE; + else + *value = UNMUTE; + break; + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + rcvd illegal request \n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + return -EINVAL; + } + + return retval; +} + +int fs_get_vol(int dev_id, u8 *value) +{ + struct sc_reg_access sc_access = {0,}; + int retval = 0, mask = 0; + + if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) + retval = fs_init_card(); + if (retval) + return retval; + + switch (dev_id) { + case PMIC_SND_CAPTURE_VOL: + pr_debug("PMIC_SND_CAPTURE_VOL\n"); + sc_access.reg_addr = MICLICTRL1; + mask = (MASK5|MASK4|MASK3|MASK2|MASK1|MASK0); + break; + case PMIC_SND_LEFT_PB_VOL: + pr_debug("PMIC_SND_LEFT_PB_VOL\n"); + sc_access.reg_addr = AUD16; + mask = (MASK5|MASK4|MASK3|MASK2|MASK1|MASK0); + break; + case PMIC_SND_RIGHT_PB_VOL: + pr_debug("PMIC_SND_LEFT_PB_VOL\n"); + sc_access.reg_addr = AUD17; + mask = (MASK5|MASK4|MASK3|MASK2|MASK1|MASK0); + break; + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + rcvd illegal request \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + + retval = sst_sc_reg_access(&sc_access, PMIC_READ, 1); + pr_debug("value read = 0x%x\n", sc_access.value); + *value = (sc_access.value) & mask; + pr_debug("value returned = 0x%x\n", *value); + return retval; +} + +struct snd_pmic_ops snd_pmic_ops_fs = { + .set_input_dev = fs_set_selected_input_dev, + .set_output_dev = fs_set_selected_output_dev, + .set_mute = fs_set_mute, + .get_mute = fs_get_mute, + .set_vol = fs_set_vol, + .get_vol = fs_get_vol, + .init_card = fs_init_card, + .set_pcm_audio_params = fs_set_pcm_audio_params, + .set_pcm_voice_params = fs_set_pcm_voice_params, + .set_voice_port = fs_set_voice_port, + .set_audio_port = fs_set_audio_port, + .power_up_pmic_pb = fs_power_up_pb, + .power_up_pmic_cp = fs_power_up_cp, + .power_down_pmic_pb = fs_power_down_pb, + .power_down_pmic_cp = fs_power_down_cp, + .power_down_pmic = fs_power_down, +}; Index: linux-2.6.33/sound/pci/sst/intelmid_v1_control.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intelmid_v1_control.c @@ -0,0 +1,623 @@ +/* + * intel_sst_v1_control.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains the control operations of vendor 2 + */ + +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/workqueue.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" +#include "intelmid_snd_control.h" + +enum _reg_v2 { + + MASTER_CLOCK_PRESCALAR = 0x205, + SET_MASTER_AND_LR_CLK1 = 0x20b, + SET_MASTER_AND_LR_CLK2 = 0x20c, + MASTER_MODE_AND_DATA_DELAY = 0x20d, + DIGITAL_INTERFACE_TO_DAI2 = 0x20e, + CLK_AND_FS1 = 0x208, + CLK_AND_FS2 = 0x209, + DAI2_TO_DAC_HP = 0x210, + HP_OP_SINGLE_ENDED = 0x224, + ENABLE_OPDEV_CTRL = 0x226, + ENABLE_DEV_AND_USE_XTAL = 0x227, + + /* Max audio subsystem (PQ49) MAX 8921 */ + AS_IP_MODE_CTL = 0xF9, + AS_LEFT_SPKR_VOL_CTL = 0xFA, /* Mono Earpiece volume control */ + AS_RIGHT_SPKR_VOL_CTL = 0xFB, + AS_LEFT_HP_VOL_CTL = 0xFC, + AS_RIGHT_HP_VOL_CTL = 0xFD, + AS_OP_MIX_CTL = 0xFE, + AS_CONFIG = 0xFF, + + /* Headphone volume control & mute registers */ + VOL_CTRL_LT = 0x21c, + VOL_CTRL_RT = 0x21d, + +}; + +int mx_init_card(void) +{ + struct sc_reg_access sc_access[] = { + {0x200, 0x80, 0x00}, + {0x201, 0xC0, 0x00}, + {0x202, 0x00, 0x00}, + {0x203, 0x00, 0x00}, + {0x204, 0x02, 0x00}, + {0x205, 0x10, 0x00}, + {0x206, 0x60, 0x00}, + {0x207, 0x00, 0x00}, + {0x208, 0x90, 0x00}, + {0x209, 0x51, 0x00}, + {0x20a, 0x00, 0x00}, + {0x20b, 0x10, 0x00}, + {0x20c, 0x00, 0x00}, + {0x20d, 0x00, 0x00}, + {0x20e, 0x11, 0x00}, + {0x20f, 0x00, 0x00}, + {0x210, 0x84, 0x00}, + {0x211, 0x33, 0x00}, + {0x212, 0x00, 0x00}, + {0x213, 0x00, 0x00}, + {0x214, 0x41, 0x00}, + {0x215, 0x00, 0x00}, + {0x216, 0x00, 0x00}, + {0x217, 0x00, 0x00}, + {0x218, 0x03, 0x00}, + {0x219, 0x03, 0x00}, + {0x21a, 0x00, 0x00}, + {0x21b, 0x00, 0x00}, + {0x21c, 0x09, 0x00}, + {0x21d, 0x09, 0x00}, + {0x21e, 0x00, 0x00}, + {0x21f, 0x00, 0x00}, + {0x220, 0x54, 0x00}, + {0x221, 0x54, 0x00}, + {0x222, 0x50, 0x00}, + {0x223, 0x00, 0x00}, + {0x224, 0x04, 0x00}, + {0x225, 0x80, 0x00}, + {0x226, 0x0F, 0x00}, + {0x227, 0x88, 0x00}, + {0xf9, 0x40, 0x00}, + {0xfa, 0x1F, 0x00}, + {0xfb, 0x00, 0x00}, + {0xfc, 0x00, 0x00}, + {0xfd, 0x00, 0x00}, + {0xfe, 0x00, 0x00}, + {0xff, 0x00, 0x00}, + }; + snd_pmic_ops_mx.card_status = SND_CARD_INIT_DONE; + return sst_sc_reg_access(sc_access, PMIC_WRITE, 47); +} + +int mx_power_up_pb(void) +{ + struct sc_reg_access sc_access[] = { + {0x226, 0x0C, MASK3|MASK2}, + {0x227, 0x88, MASK7|MASK3}, + }; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); +} + +int mx_power_up_cp(void) +{ + struct sc_reg_access sc_access[] = { + {0x226, 0x03, MASK1|MASK0}, + {0x227, 0x88, MASK7|MASK3}, + }; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); +} + +int mx_power_down_pb(void) +{ +/* struct sc_reg_access sc_access[] = { + {0x226, 0x00, MASK3|MASK2}, + }; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); + Turning Off this code, as power off coausing first few frames + to be cutt off... */ + return 0; +} + +int mx_power_down_cp(void) +{ +/* struct sc_reg_access sc_access[] = { + {0x226, 0x00, MASK1|MASK0}, + }; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); +*/ + return 0; +} + +int mx_power_down(void) +{ +/* struct sc_reg_access sc_access[] = { + {0x227, 0x00, MASK7}, + }; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); +*/ return 0; +} + +int mx_set_voice_port(int status) +{ + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + return retval; +} + +int mx_set_audio_port(int status) +{ + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + return retval; +} +int mx_set_pcm_voice_params(void) +{ + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + return retval; +} + +int mx_set_pcm_audio_params(int sfreq, int word_size) +{ + int config1 = 0, config2 = 0; + struct sc_reg_access sc_access[3]; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + + switch (sfreq) { + case 8000: + config1 = 0x10; + config2 = 0x00; + break; + case 11025: + config1 = 0x16; + config2 = 0x0d; + break; + case 12000: + config1 = 0x18; + config2 = 0x00; + break; + case 16000: + config1 = 0x20; + config2 = 0x00; + break; + case 22050: + config1 = 0x2c; + config2 = 0x1a; + break; + case 24000: + config1 = 0x30; + config2 = 0x00; + break; + case 32000: + config1 = 0x40; + config2 = 0x00; + break; + case 44100: + config1 = 0x58; + config2 = 0x33; + break; + case 48000: + config1 = 0x60; + config2 = 0x00; + break; + } + + sc_access[0].reg_addr = 0x206; + sc_access[0].value = config1; + sc_access[1].reg_addr = 0x207; + sc_access[1].value = config2; + + if (word_size == 16) + sc_access[2].value = 0x31; + else if (word_size == 24) + sc_access[2].value = 0x32; + + sc_access[2].reg_addr = 0x209; + return sst_sc_reg_access(sc_access, PMIC_WRITE, 3); +} + +int mx_set_selected_output_dev(u8 dev_id) +{ + struct sc_reg_access sc_access[2]; + int num_reg; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + pr_debug("mx_set_selected_output_dev dev_id:0x%x\n", dev_id); + switch (dev_id) { + case STEREO_HEADPHONE: + sc_access[0].reg_addr = 0x226; + sc_access[0].value = 0x0C; + sc_access[0].mask = 0x3F; + + sc_access[1].reg_addr = 0xFF; + sc_access[1].value = 0x00; + sc_access[1].mask = 0xA0; + num_reg = 2; + break; + case INTERNAL_SPKR: + sc_access[0].reg_addr = 0x226; + sc_access[0].value = 0x3F; + sc_access[0].mask = 0x3F; + + sc_access[1].reg_addr = 0xFF; + sc_access[1].value = 0xA0; + sc_access[1].mask = 0xA0; + num_reg = 2; + break; + } + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); +} + +int mx_set_selected_input_dev(u8 dev_id) +{ + struct sc_reg_access sc_access[2]; + int num_reg = 0; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + pr_debug("mx_set_selected_input_dev dev_id:0x%x\n", dev_id); + + switch (dev_id) { + case AMIC: + sc_access[0].reg_addr = 0x223; + sc_access[0].value = 0x00; + sc_access[0].mask = 0x00; + sst_sc_reg_access(sc_access, PMIC_WRITE, 1); + return 0; + + case HS_MIC: + sc_access[0].reg_addr = 0x223; + sc_access[0].value = 0x00; + sc_access[0].mask = 0x00; + num_reg = 1; + break; + case DMIC: + sc_access[0].reg_addr = 0x223; + sc_access[0].value = 0x30; + sc_access[0].mask = 0x30; + num_reg = 1; + break; + } + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); +} + +int mx_set_mute(int dev_id, u8 value) +{ + struct sc_reg_access sc_access[6]; + int num_reg = 0; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + pr_debug("mx_set_mute dev_id:0x%x , value:%d \n", dev_id, value); + + switch (dev_id) { + case PMIC_SND_DMIC_MUTE: + case PMIC_SND_AMIC_MUTE: + case PMIC_SND_HP_MIC_MUTE: + sc_access[0].reg_addr = 0x220; + sc_access[1].reg_addr = 0x221; + sc_access[2].reg_addr = 0x223; + if (value == MUTE) { + sc_access[0].value = 0x00; + sc_access[1].value = 0x00; + sc_access[2].value = 0x10; + } else { + sc_access[0].value = 0x20; + sc_access[1].value = 0x20; + } + sc_access[0].mask = MASK5|MASK6; + sc_access[1].mask = MASK5|MASK6; + sc_access[2].mask = MASK5|MASK4; + num_reg = 3; + break; + + case PMIC_SND_LEFT_SPEAKER_MUTE: + case PMIC_SND_LEFT_HP_MUTE: + sc_access[0].reg_addr = VOL_CTRL_LT; + if (value == MUTE) + sc_access[0].value = 0x40; + else + sc_access[0].value = 0x00; + sc_access[0].mask = MASK6; + num_reg = 1; + break; + case PMIC_SND_RIGHT_SPEAKER_MUTE: + case PMIC_SND_RIGHT_HP_MUTE: + sc_access[0].reg_addr = VOL_CTRL_RT; + if (value == MUTE) + sc_access[0].value = 0x40; + else + sc_access[0].value = 0x00; + sc_access[0].mask = MASK6; + num_reg = 1; + break; + + case PMIC_SND_MUTE_ALL: + sc_access[0].reg_addr = VOL_CTRL_RT; + sc_access[1].reg_addr = VOL_CTRL_LT; + sc_access[2].reg_addr = 0x220; + sc_access[3].reg_addr = 0x221; + if (value == MUTE) { + sc_access[0].value = sc_access[1].value = 0x40; + sc_access[2].value = 0x00; + sc_access[3].value = 0x00; + } else { + sc_access[0].value = sc_access[1].value = 0x00; + sc_access[2].value = 0x20; + sc_access[3].value = 0x20; + } + sc_access[0].mask = sc_access[1].mask = MASK6; + sc_access[2].mask = MASK5|MASK6; + sc_access[3].mask = MASK5|MASK6; + num_reg = 4; + break; + } + + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); +} + +int mx_set_vol(int dev_id, u8 value) +{ + struct sc_reg_access sc_access[2] = {{0},}; + int num_reg = 0; + int retval = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + pr_debug("mx_set_vol dev_id:0x%x , value:%d \n", dev_id, value); + switch (dev_id) { + case PMIC_SND_CAPTURE_VOL: + sc_access[0].reg_addr = 0x220; + sc_access[1].reg_addr = 0x221; + sc_access[0].value = sc_access[1].value = -value; + sc_access[0].mask = sc_access[1].mask = + (MASK0|MASK1|MASK2|MASK3|MASK4); + num_reg = 2; + break; + case PMIC_SND_LEFT_PB_VOL: + sc_access[0].value = -value; + sc_access[0].reg_addr = VOL_CTRL_LT; + sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); + num_reg = 1; + break; + case PMIC_SND_RIGHT_PB_VOL: + sc_access[0].value = -value; + sc_access[0].reg_addr = VOL_CTRL_RT; + sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); + num_reg = 1; + break; + } + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); +} + +int mx_get_mute(int dev_id, u8 *value) +{ + struct sc_reg_access sc_access[4] = {{0},}; + int retval = 0, num_reg = 0, mask = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + switch (dev_id) { + case PMIC_SND_DMIC_MUTE: + case PMIC_SND_AMIC_MUTE: + case PMIC_SND_HP_MIC_MUTE: + sc_access[0].reg_addr = 0x220; + mask = MASK5|MASK6; + num_reg = 1; + retval = sst_sc_reg_access(sc_access, PMIC_READ, num_reg); + if (retval) + return retval; + *value = sc_access[0].value & mask; + if (*value) + *value = UNMUTE; + else + *value = MUTE; + return retval; + + case PMIC_SND_LEFT_HP_MUTE: + case PMIC_SND_LEFT_SPEAKER_MUTE: + sc_access[0].reg_addr = VOL_CTRL_LT; + num_reg = 1; + mask = MASK6; + break; + case PMIC_SND_RIGHT_HP_MUTE: + case PMIC_SND_RIGHT_SPEAKER_MUTE: + sc_access[0].reg_addr = VOL_CTRL_RT; + num_reg = 1; + mask = MASK6; + break; + case PMIC_SND_MUTE_ALL: + sc_access[0].reg_addr = VOL_CTRL_RT; + sc_access[1].reg_addr = VOL_CTRL_LT; + sc_access[2].reg_addr = 0x220; + sc_access[3].reg_addr = 0x221; + num_reg = 4; + retval = sst_sc_reg_access(sc_access, PMIC_READ, num_reg); + if (((sc_access[0].value & MASK6) == MASK6) && + ((sc_access[1].value & MASK6) == MASK6) && + ((sc_access[2].value & (MASK5|MASK6)) == 0) && + ((sc_access[3].value & (MASK5|MASK6)) == 0)) + *value = MUTE; + else + *value = UNMUTE; + break; + } + retval = sst_sc_reg_access(sc_access, PMIC_READ, num_reg); + if (retval) + return retval; + *value = sc_access[0].value & mask; + if (*value) + *value = MUTE; + else + *value = UNMUTE; + return retval; +} + +int mx_get_vol(int dev_id, u8 *value) +{ + struct sc_reg_access sc_access = {0,}; + int retval = 0, mask = 0, num_reg = 0; + + if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) + retval = mx_init_card(); + if (retval) + return retval; + + switch (dev_id) { + case PMIC_SND_CAPTURE_VOL: + sc_access.reg_addr = 0x220; + mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5; + num_reg = 1; + break; + case PMIC_SND_LEFT_PB_VOL: + sc_access.reg_addr = VOL_CTRL_LT; + mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5; + num_reg = 1; + break; + case PMIC_SND_RIGHT_PB_VOL: + sc_access.reg_addr = VOL_CTRL_RT; + mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5; + num_reg = 1; + break; + } + retval = sst_sc_reg_access(&sc_access, PMIC_READ, num_reg); + if (retval) + return retval; + *value = -(sc_access.value & mask); + pr_debug("value extracted 0x%x\n", *value); + return retval; +} + +struct snd_pmic_ops snd_pmic_ops_mx = { + .set_input_dev = mx_set_selected_input_dev, + .set_output_dev = mx_set_selected_output_dev, + .set_mute = mx_set_mute, + .get_mute = mx_get_mute, + .set_vol = mx_set_vol, + .get_vol = mx_get_vol, + .init_card = mx_init_card, + .set_pcm_audio_params = mx_set_pcm_audio_params, + .set_pcm_voice_params = mx_set_pcm_voice_params, + .set_voice_port = mx_set_voice_port, + .set_audio_port = mx_set_audio_port, + .power_up_pmic_pb = mx_power_up_pb, + .power_up_pmic_cp = mx_power_up_cp, + .power_down_pmic_pb = mx_power_down_pb, + .power_down_pmic_cp = mx_power_down_cp, + .power_down_pmic = mx_power_down, +}; + Index: linux-2.6.33/sound/pci/sst/intelmid_v2_control.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intelmid_v2_control.c @@ -0,0 +1,786 @@ +/* + * intelmid_v2_control.c - Intel Sound card driver for MID + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Vinod Koul <vinod.koul@intel.com> + * Harsha Priya <priya.harsha@intel.com> + * Dharageswari R <dharageswari.r@intel.com) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains the control operations of vendor 3 + */ + +#include <linux/cdev.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/uaccess.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/workqueue.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intel_sst_fw_ipc.h" +#include "intel_sst_common.h" +#include "intelmid_snd_control.h" + +enum reg_v3 { + VAUDIOCNT = 0x51, + VOICEPORT1 = 0x100, + VOICEPORT2 = 0x101, + AUDIOPORT1 = 0x102, + AUDIOPORT2 = 0x103, + ADCSAMPLERATE = 0x104, + DMICCTRL1 = 0x105, + DMICCTRL2 = 0x106, + MICCTRL = 0x107, + MICSELVOL = 0x108, + LILSEL = 0x109, + LIRSEL = 0x10a, + VOICEVOL = 0x10b, + AUDIOLVOL = 0x10c, + AUDIORVOL = 0x10d, + LMUTE = 0x10e, + RMUTE = 0x10f, + POWERCTRL1 = 0x110, + POWERCTRL2 = 0x111, + DRVPOWERCTRL = 0x112, + VREFPLL = 0x113, + PCMBUFCTRL = 0x114, + SOFTMUTE = 0x115, + DTMFPATH = 0x116, + DTMFVOL = 0x117, + DTMFFREQ = 0x118, + DTMFHFREQ = 0x119, + DTMFLFREQ = 0x11a, + DTMFCTRL = 0x11b, + DTMFASON = 0x11c, + DTMFASOFF = 0x11d, + DTMFASINUM = 0x11e, + CLASSDVOL = 0x11f, + VOICEDACAVOL = 0x120, + AUDDACAVOL = 0x121, + LOMUTEVOL = 0x122, + HPLVOL = 0x123, + HPRVOL = 0x124, + MONOVOL = 0x125, + LINEOUTMIXVOL = 0x126, + EPMIXVOL = 0x127, + LINEOUTLSEL = 0x128, + LINEOUTRSEL = 0x129, + EPMIXOUTSEL = 0x12a, + HPLMIXSEL = 0x12b, + HPRMIXSEL = 0x12c, + LOANTIPOP = 0x12d, +}; + + +int nc_init_card(void) +{ + struct sc_reg_access sc_access[] = { + {VAUDIOCNT, 0x27, 0}, + {VOICEPORT1, 0x00, 0}, + {VOICEPORT2, 0x00, 0}, + {AUDIOPORT1, 0x98, 0}, + {AUDIOPORT2, 0x09, 0}, + {AUDIOLVOL, 0x0e, 0}, + {AUDIORVOL, 0x0e, 0}, + {LMUTE, 0x03, 0}, + {RMUTE, 0x03, 0}, + {POWERCTRL1, 0x00, 0}, + {POWERCTRL2, 0x00, 0}, + {DRVPOWERCTRL, 0x00, 0}, + {VREFPLL, 0x0, 0}, + {HPLMIXSEL, 0xee, 0}, + {HPRMIXSEL, 0xf6, 0}, + {PCMBUFCTRL, 0x0, 0}, + {VOICEVOL, 0x0e, 0}, + {HPLVOL, 0x06, 0}, + {HPRVOL, 0x06, 0}, + {MICCTRL, 0x11, 0x00}, + {ADCSAMPLERATE, 0x8B, 0x00}, + {MICSELVOL, 0x5B, 0x00}, + {LILSEL, 0x06, 0}, + {LIRSEL, 0x06, 0}, + {LOANTIPOP, 0x0f, 0}, + }; + snd_pmic_ops_nc.card_status = SND_CARD_INIT_DONE; + sst_sc_reg_access(sc_access, PMIC_WRITE, 25); + pr_debug("init complete!!\n"); + return 0; +} + +int nc_power_up_pb(void) +{ + struct sc_reg_access sc_access[] = { + {VREFPLL, 0x35, 0x35}, + {POWERCTRL1, 0x01, 0x01}, + {POWERCTRL2, 0x0C, 0x0C}, + {VREFPLL, 0x35, 0x35}, + {DRVPOWERCTRL, 0x86, 0x86}, + }; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("powering up pb....\n"); + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 5); +} + +int nc_power_up_cp(void) +{ + struct sc_reg_access sc_access[] = { + {POWERCTRL1, 0xB6, 0xB6}, + {VREFPLL, 0x35, 0x35}, + }; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("powering up cp....\n"); + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + +} +int nc_power_down(void) +{ + int retval = 0; + + struct sc_reg_access sc_access[] = { + {POWERCTRL1, 0x00, 0}, + {POWERCTRL2, 0x00, 0}, + {DRVPOWERCTRL, 0x00, 0}, + {VREFPLL, 0x0, 0}, + }; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("powering dn ....\n"); + return sst_sc_reg_access(sc_access, PMIC_WRITE, 4); +} +int nc_power_down_pb(void) +{ + struct sc_reg_access sc_access[] = { + {POWERCTRL1, 0x00, 0x41}, + {POWERCTRL2, 0x00, 0x0C}, + {DRVPOWERCTRL, 0x00, 0x86}, + }; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("powering dn pb....\n"); + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); +} + +int nc_power_down_cp(void) +{ + struct sc_reg_access sc_access[] = { + {POWERCTRL1, 0x00, 0xB6}, + }; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("powering dn cp....\n"); + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); +} + +int nc_set_pcm_voice_params(void) +{ + struct sc_reg_access sc_access[] = { + {0x051, 0x27, 0}, + {0x100, 0x50, 0}, + {0x101, 0x04, 0}, + {0x102, 0x0B, 0}, + {0x103, 0x08, 0}, + {0x104, 0x83, 0}, + {0x105, 0x40, 0}, + {0x106, 0x47, 0}, + {0x107, 0x50, 0}, + {0x108, 0x52, 0}, + {0x109, 0x46, 0}, + {0x10A, 0x03, 0}, + {0x10B, 0x0E, 0}, + {0x10C, 0x8E, 0}, + {0x10D, 0x8E, 0}, + {0x10E, 0x03, 0}, + {0x10F, 0x03, 0}, + {0x110, 0xF7, 0}, + {0x111, 0x0C, 0}, + {0x112, 0x86, 0}, + {0x113, 0x3E, 0}, + {0x114, 0x13, 0}, + {0x115, 0x00, 0}, + {0x116, 0x00, 0}, + {0x117, 0x00, 0}, + {0x118, 0x12, 0}, + {0x119, 0x10, 0}, + {0x11A, 0x10, 0}, + {0x11B, 0x00, 0}, + {0x11C, 0x64, 0}, + {0x11D, 0x64, 0}, + {0x11E, 0x01, 0}, + {0x11F, 0x00, 0}, + {0x120, 0x06, 0}, + {0x121, 0x06, 0}, + {0x122, 0x1F, 0}, + {0x123, 0x03, 0}, + {0x124, 0x03, 0}, + {0x125, 0x06, 0}, + {0x126, 0x06, 0}, + {0x127, 0x06, 0}, + {0x128, 0xFE, 0}, + {0x129, 0xFE, 0}, + {0x12A, 0xFE, 0}, + {0x12B, 0xDE, 0}, + {0x12C, 0xFE, 0}, + {0x12D, 0x00, 0}, + {0x12F, 0x00, 0}, + {0x131, 0x00, 0}, + }; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + sst_sc_reg_access(sc_access, PMIC_WRITE, 49); + pr_debug("Voice parameters set successfully!!\n"); + return 0; +} + +int nc_set_pcm_audio_params(int sfreq, int word_size) +{ + int config2 = 0; + struct sc_reg_access sc_access; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + switch (sfreq) { + case 8000: + config2 = 0x00; + break; + case 11025: + config2 = 0x01; + break; + case 12000: + config2 = 0x02; + break; + case 16000: + config2 = 0x03; + break; + case 22050: + config2 = 0x04; + break; + case 24000: + config2 = 0x05; + break; + case 32000: + config2 = 0x07; + break; + case 44100: + config2 = 0x08; + break; + case 48000: + config2 = 0x09; + break; + } + sc_access.value = config2; + sc_access.mask = 0x0F; + sc_access.reg_addr = AUDIOPORT2; + sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); + + pr_debug("word_size = %d\n", word_size); + sc_access.reg_addr = AUDIOPORT1; + sc_access.mask = 0x07; + if (word_size == 16) + sc_access.value = 0x0; + else if (word_size == 24) + sc_access.value = 0x13; + return sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); +} + +int nc_set_audio_port(int status) +{ + struct sc_reg_access sc_access[2] = {{0,},}; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + if (status == DEACTIVATE) { + /* Deactivate audio port-tristate and power */ + sc_access[0].value = 0x00; + sc_access[0].mask = MASK4|MASK5; + sc_access[0].reg_addr = AUDIOPORT1; + sc_access[1].value = 0x00; + sc_access[1].mask = MASK0|MASK1; + sc_access[1].reg_addr = 0x110; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + } else if (status == ACTIVATE) { + /* activate audio port */ + sc_access[0].value = 0x98; + sc_access[0].mask = MASK4|MASK5 ; + sc_access[0].reg_addr = AUDIOPORT1; + sc_access[1].value = 0x03; + sc_access[1].mask = MASK0|MASK1; + sc_access[1].reg_addr = 0x110; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + } else + return -EINVAL; + +} + +int nc_set_voice_port(int status) +{ + struct sc_reg_access sc_access[2] = {{0,},}; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + if (status == DEACTIVATE) { + /* Activate Voice port */ + sc_access[0].value = 0x40; + sc_access[0].mask = MASK4; + sc_access[0].reg_addr = VOICEPORT1; + sc_access[1].value = 0x00; + sc_access[1].mask = MASK7|MASK6; + sc_access[1].reg_addr = 0x110; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + /* todo move the playback and capture path from PCM2 -> PCM1 */ + } else if (status == ACTIVATE) { + /* Deactivate voice port */ + sc_access[0].value = 0xd0; + sc_access[0].mask = MASK4; + sc_access[0].reg_addr = VOICEPORT1; + sc_access[1].value = 0xC0; + sc_access[1].mask = MASK7|MASK6; + sc_access[1].reg_addr = 0x110; + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); + } else + return -EINVAL; +} +int nc_set_selected_output_dev(u8 value) +{ + struct sc_reg_access sc_access_HP[] = { + {LMUTE, 0x02, 0x06}, + {RMUTE, 0x02, 0x06} + }; + struct sc_reg_access sc_access_IS[] = { + {LMUTE, 0x04, 0x06}, + {RMUTE, 0x04, 0x06} + }; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("nc set selected output:%d \n", value); + + switch (value) { + case STEREO_HEADPHONE: + return sst_sc_reg_access(sc_access_HP, PMIC_WRITE, 2); + case INTERNAL_SPKR: + return sst_sc_reg_access(sc_access_IS, PMIC_WRITE, 2); + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + rcvd illegal request: %d \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__ , value); + return -EINVAL; + } +} + +int nc_set_mute(int dev_id, u8 value) +{ + struct sc_reg_access sc_access[2]; + u8 mute_val, cap_mute; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("set device id::%d, value %d\n", dev_id, value); + switch (dev_id) { + case PMIC_SND_MUTE_ALL: + pr_debug("PMIC_SND_MUTE_ALL: value::%d \n", value); + if (value == UNMUTE) { + /* unmute the system, set the 7th bit to zero */ + mute_val = cap_mute = 0x00; + } else { + /* MUTE:Set the seventh bit */ + mute_val = 0x80; + cap_mute = 0x40; + } + sc_access[0].reg_addr = AUDIOLVOL; + sc_access[1].reg_addr = AUDIORVOL; + sc_access[0].mask = sc_access[1].mask = MASK7; + sc_access[0].value = sc_access[1].value = mute_val; + if (!sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2)) { + sc_access[0].reg_addr = 0x109; + sc_access[1].reg_addr = 0x10a; + sc_access[0].mask = sc_access[1].mask = MASK6; + sc_access[0].value = sc_access[1].value = cap_mute; + return sst_sc_reg_access(sc_access, + PMIC_READ_MODIFY, 2); + } + case PMIC_SND_HP_MIC_MUTE: + pr_debug("PMIC_SND_HPMIC_MUTE: value::%d\n", value); + if (value == UNMUTE) { + /* unmute the system, set the 6th bit to one */ + sc_access[0].value = 0x00; + } else { + /* mute the system, reset the 6th bit to zero */ + sc_access[0].value = 0x40; + } + sc_access[0].reg_addr = LIRSEL; + sc_access[0].mask = MASK6; + break; + case PMIC_SND_AMIC_MUTE: + pr_debug("PMIC_SND_AMIC_MUTE: value::%d\n", value); + if (value == UNMUTE) { + /* unmute the system, set the 6th bit to one */ + sc_access[0].value = 0x00; + } else { + /* mute the system, reset the 6th bit to zero */ + sc_access[0].value = 0x40; + } + sc_access[0].reg_addr = LILSEL; + sc_access[0].mask = MASK6; + break; + + case PMIC_SND_DMIC_MUTE: + pr_debug("PMIC_SND_INPUT_MUTE_DMIC: value::%d\n", value); + if (value == UNMUTE) { + /* unmute the system, set the 6th bit to one */ + sc_access[0].value = 0x00; + } else { + /* mute the system, reset the 6th bit to zero */ + sc_access[0].value = 0x40; + } + sc_access[0].reg_addr = DMICCTRL1; + sc_access[0].mask = MASK6; + break; + + case PMIC_SND_LEFT_HP_MUTE: + case PMIC_SND_RIGHT_HP_MUTE: + if (value == UNMUTE) + sc_access[0].value = 0x0; + else + sc_access[0].value = 0x04; + if (dev_id == PMIC_SND_LEFT_HP_MUTE) { + sc_access[0].reg_addr = LMUTE; + pr_debug("PMIC_SND_LEFT_HP_MUTE:: value::%d\n", + sc_access[0].value); + } else { + sc_access[0].reg_addr = RMUTE; + pr_debug("PMIC_SND_RIGHT_HP_MUTE:: value::%d\n", + sc_access[0].value); + } + sc_access[0].mask = MASK2; + break; + case PMIC_SND_LEFT_SPEAKER_MUTE: + case PMIC_SND_RIGHT_SPEAKER_MUTE: + if (value == UNMUTE) + sc_access[0].value = 0x00; + else + sc_access[0].value = 0x03; + sc_access[0].reg_addr = LMUTE; + pr_debug("PMIC_SND_SPEAKER_MUTE %d\n", sc_access[0].value); + sc_access[0].mask = MASK1; + break; + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + Invalid Device_id \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -EINVAL; + } + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); + +} + +int nc_set_vol(int dev_id, u8 value) +{ + struct sc_reg_access sc_access; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("set volume:%d\n", dev_id); + switch (dev_id) { + case PMIC_SND_CAPTURE_VOL: + pr_debug("PMIC_SND_CAPTURE_VOL:: value::%d \n", value); + sc_access.value = -value; + sc_access.reg_addr = LILSEL; + sc_access.mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5; + break; + + case PMIC_SND_LEFT_PB_VOL: + pr_debug("PMIC_SND_LEFT_HP_VOL:%d \n", value); + sc_access.value = -value; + sc_access.reg_addr = AUDIOLVOL; + sc_access.mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); + break; + + case PMIC_SND_RIGHT_PB_VOL: + pr_debug("PMIC_SND_RIGHT_HP_VOL: value::%d\n", value); + sc_access.value = -value; + sc_access.reg_addr = AUDIORVOL; + sc_access.mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); + break; + + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + Invalid Device_id \n", get_seconds(),\ + sst_get_time(), __func__ , __LINE__); + return -EINVAL; + + } + /* sst_sc_read_modify(®_adrs, &difference, 1);*/ + return sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); +} + +int nc_set_selected_input_dev(u8 value) +{ + struct sc_reg_access sc_access[3]; + u8 num_val; + int retval = 0; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("nc set selected input:%d \n", value); + + switch (value) { + case AMIC: + pr_debug("Selecting AMIC\n"); + sc_access[0].reg_addr = 0x107; + sc_access[0].value = 0x40; + sc_access[0].mask = MASK6|MASK4; + sc_access[1].reg_addr = 0x110; + sc_access[1].value = 0x38; + sc_access[1].mask = MASK5|MASK4|MASK3|MASK2; + sc_access[2].reg_addr = 0x111; + sc_access[2].value = 0x02; + sc_access[2].mask = MASK1; + num_val = 3; + break; + + case HS_MIC: + pr_debug("Selecting HS_MIC\n"); + sc_access[0].reg_addr = 0x107; + sc_access[0].mask = MASK6|MASK4; + sc_access[0].value = 0x10; + sc_access[1].reg_addr = 0x110; + sc_access[1].value = 0x34; + sc_access[1].mask = MASK5|MASK4|MASK3|MASK2; + sc_access[2].reg_addr = 0x111; + sc_access[2].value = 0x02; + sc_access[2].mask = MASK1; + num_val = 3; + break; + + case DMIC: + pr_debug("DMIC\n"); + sc_access[0].reg_addr = DMICCTRL1; + sc_access[0].value = 0x40; + sc_access[0].mask = MASK6; + num_val = 1; + + break; + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR:+\ + rcvd illegal request: %d \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__ , value); + return -EINVAL; + } + return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_val); +} + +int nc_get_mute(int dev_id, u8 *value) +{ + int retval = 0, mask = 0; + struct sc_reg_access sc_access = {0,}; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + pr_debug("get mute::%d\n", dev_id); + + switch (dev_id) { + case PMIC_SND_MUTE_ALL: + pr_debug("PMIC_SND_INPUT_MASTER_MUTE: value::%d\n", *value); + sc_access.reg_addr = AUDIOLVOL; + sc_access.mask = MASK7; + break; + case PMIC_SND_AMIC_MUTE: + pr_debug("PMIC_SND_INPUT_MUTE_MIC1\n"); + sc_access.reg_addr = LILSEL; + mask = MASK6; + break; + case PMIC_SND_HP_MIC_MUTE: + pr_debug("PMIC_SND_INPUT_MUTE_MIC2\n"); + sc_access.reg_addr = LIRSEL; + mask = MASK6; + break; + case PMIC_SND_LEFT_HP_MUTE: + case PMIC_SND_RIGHT_HP_MUTE: + mask = MASK2; + pr_debug("PMIC_SN_LEFT/RIGHT_HP_MUTE\n"); + if (dev_id == PMIC_SND_RIGHT_HP_MUTE) + sc_access.reg_addr = RMUTE; + else + sc_access.reg_addr = LMUTE; + break; + + case PMIC_SND_LEFT_SPEAKER_MUTE: + pr_debug("PMIC_MONO_EARPIECE_MUTE\n"); + sc_access.reg_addr = RMUTE; + mask = MASK1; + break; + case PMIC_SND_DMIC_MUTE: /* FIXME */ + pr_debug("PMIC_SND_INPUT_MUTE_DMIC\n"); + sc_access.reg_addr = RMUTE; + mask = MASK1; + break; + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + Invalid Device_id \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return -EINVAL; + + } + retval = sst_sc_reg_access(&sc_access, PMIC_READ, 1); + pr_debug("reg value = %d\n", sc_access.value); + if (retval) + return retval; + *value = (sc_access.value) & mask; + pr_debug("masked value = %d\n", *value); + if (*value) + *value = 0; + else + *value = 1; + pr_debug("value returned = 0x%x\n", *value); + return retval; +} + +int nc_get_vol(int dev_id, u8 *value) +{ + int retval = 0, mask = 0; + struct sc_reg_access sc_access = {0,}; + + if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) + retval = nc_init_card(); + if (retval) + return retval; + + switch (dev_id) { + case PMIC_SND_CAPTURE_VOL: + pr_debug("PMIC_SND_INPUT_CAPTURE_VOL\n"); + sc_access.reg_addr = LILSEL; + mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); + break; + + case PMIC_SND_RIGHT_PB_VOL: + pr_debug("GET_VOLUME_PMIC_LEFT_HP_VOL\n"); + sc_access.reg_addr = AUDIOLVOL; + mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); + break; + + case PMIC_SND_LEFT_PB_VOL: + pr_debug("GET_VOLUME_PMIC_RIGHT_HP_VOL\n"); + sc_access.reg_addr = AUDIORVOL; + mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); + break; + + default: + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + Invalid Device_id = %d \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__ , dev_id); + return -EINVAL; + + } + retval = sst_sc_reg_access(&sc_access, PMIC_READ, 1); + pr_debug("value read = 0x%x\n", sc_access.value); + *value = (sc_access.value) & mask; + *value = -*value; + pr_debug("value returned = 0x%x\n", *value); + return retval; +} + +struct snd_pmic_ops snd_pmic_ops_nc = { + .set_input_dev = nc_set_selected_input_dev, + .set_output_dev = nc_set_selected_output_dev, + .set_mute = nc_set_mute, + .get_mute = nc_get_mute, + .set_vol = nc_set_vol, + .get_vol = nc_get_vol, + .init_card = nc_init_card, + .set_pcm_audio_params = nc_set_pcm_audio_params, + .set_pcm_voice_params = nc_set_pcm_voice_params, + .set_voice_port = nc_set_voice_port, + .set_audio_port = nc_set_audio_port, + .power_up_pmic_pb = nc_power_up_pb, + .power_up_pmic_cp = nc_power_up_cp, + .power_down_pmic_pb = nc_power_down_pb, + .power_down_pmic_cp = nc_power_down_cp, + .power_down_pmic = nc_power_down, +}; Index: linux-2.6.33/include/sound/jack.h =================================================================== --- linux-2.6.33.orig/include/sound/jack.h +++ linux-2.6.33/include/sound/jack.h @@ -42,6 +42,8 @@ enum snd_jack_types { SND_JACK_MECHANICAL = 0x0008, /* If detected separately */ SND_JACK_VIDEOOUT = 0x0010, SND_JACK_AVOUT = SND_JACK_LINEOUT | SND_JACK_VIDEOOUT, + SND_JACK_HS_SHORT_PRESS = SND_JACK_HEADSET | 0x0020, + SND_JACK_HS_LONG_PRESS = SND_JACK_HEADSET | 0x0040, }; struct snd_jack { Index: linux-2.6.33/sound/pci/sst/intelmid.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intelmid.c @@ -0,0 +1,1120 @@ +/* + * intelmid.c - Intel Sound card driver for MID + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Harsha Priya <priya.harsha@intel.com> + * Vinod Koul <vinod.koul@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * ALSA driver for Intel MID sound card chipset + */ +#include <linux/spi/spi.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/sched.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/initval.h> + +#include <sound/pcm-indirect.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +/* #include <net/netlink.h> +#include <net/genetlink.h> */ + +#include "intelmid_snd_control.h" +#include "intelmid.h" +#include "intelmid_ctrl.h" + +MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); +MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); +MODULE_DESCRIPTION("Intel MAD Sound card driver"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("{Intel,Intel_MAD}"); + + +static int card_index = SNDRV_DEFAULT_IDX1;/* Index 0-MAX */ +static char *card_id = SNDRV_DEFAULT_STR1; /* ID for this card */ + +module_param(card_index, int, 0444); +MODULE_PARM_DESC(card_index, "Index value for INTELMAD soundcard."); +module_param(card_id, charp, 0444); +MODULE_PARM_DESC(card_id, "ID string for INTELMAD soundcard."); + +int sst_card_vendor_id; +int audio_interrupt_enable = 0; + +/* Data path functionalities */ +static struct snd_pcm_hardware snd_intelmad_stream = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_DOUBLE | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP| + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | + /* SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | */ + SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24), + .rates = (SNDRV_PCM_RATE_8000| + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = MIN_RATE, + + .rate_max = MAX_RATE, + .channels_min = MIN_CHANNEL, + .channels_max = MAX_CHANNEL, + .buffer_bytes_max = MAX_BUFFER, + .period_bytes_min = MIN_PERIOD_BYTES, + .period_bytes_max = MAX_PERIOD_BYTES, + .periods_min = MIN_PERIODS, + .periods_max = MAX_PERIODS, + .fifo_size = FIFO_SIZE, +}; + +static int snd_intelmad_pcm_ack(struct snd_pcm_substream *substream) +{ + struct mad_stream_pvt *stream; + struct snd_pcm_indirect *rec; + + WARN_ON(!substream); + WARN_ON(!substream->runtime); + + stream = substream->runtime->private_data; + WARN_ON(!stream); + + pr_debug("called %d\n", stream->stream_status); + if (stream->stream_status != INIT) { + rec = &stream->pcm_indirect; + if (substream->stream == STREAM_OPS_PLAYBACK) { + pr_debug("calling indirect playback transfer\n"); + snd_pcm_indirect_playback_transfer(substream, rec, + send_buffer_to_sst); + } else if (substream->stream == STREAM_OPS_CAPTURE) { + pr_debug("calling indirect capture transfer\n"); + snd_pcm_indirect_capture_transfer(substream, rec, + send_buffer_to_sst); + } + stream->stream_status = RUNNING; + } + return 0; +} + +/** +* snd_intelmad_pcm_trigger - stream activities are handled here +* @substream:substream for which the stream function is called +*@cmd:the stream commamd thats requested from upper layer +* This function is called whenever an a stream activity is invoked +*/ +static int snd_intelmad_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + int ret_val = 0; + struct snd_intelmad *intelmaddata; + struct mad_stream_pvt *stream; + struct stream_buffer buffer_to_sst; + + pr_debug("called\n"); + + WARN_ON(!substream); + + intelmaddata = snd_pcm_substream_chip(substream); + stream = substream->runtime->private_data; + + WARN_ON(!intelmaddata->sstdrv_ops); + WARN_ON(!intelmaddata->sstdrv_ops->scard_ops); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + stream->substream = substream; + pr_debug("pcm_size=%d\n", snd_pcm_lib_buffer_bytes(substream)); + stream->stream_status = STARTED; + + if (substream->stream == STREAM_OPS_PLAYBACK) + snd_intelmad_pcm_ack(substream); + else if (substream->stream == STREAM_OPS_CAPTURE) { + buffer_to_sst.length = + frames_to_bytes(substream->runtime, + substream->runtime->buffer_size); + buffer_to_sst.addr = (unsigned long) + substream->runtime->dma_area; + ret_val = intelmaddata->sstdrv_ops->send_buffer( + stream->stream_info.str_id, + &buffer_to_sst); + stream->dbg_cum_bytes += + frames_to_bytes(substream->runtime, + substream->runtime->buffer_size); + stream->stream_status = RUNNING; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("in stop\n"); + ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_DROP, + &stream->stream_info.str_id); + if (ret_val) + return ret_val; + stream->stream_status = DROPPED; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("in pause\n"); + ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_PAUSE, + &stream->stream_info.str_id); + if (ret_val) + return ret_val; + stream->stream_status = PAUSED; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("in pause release \n"); + ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_RESUME, + &stream->stream_info.str_id); + if (ret_val) + return ret_val; + stream->stream_status = RUNNING; + break; + default: + return -EINVAL; + } + return ret_val; +} + +/** +* snd_intelmad_pcm_prepare- internal preparation before starting a stream +* @substream: substream for which the function is called +* This function is called when a stream is started for internal preparation. +*/ +static int snd_intelmad_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct mad_stream_pvt *stream; + int ret_val = 0; + struct snd_intelmad *intelmaddata; + + pr_debug("called \n"); + + WARN_ON(!substream); + stream = substream->runtime->private_data; + intelmaddata = snd_pcm_substream_chip(substream); + pr_debug("pb cnt = %d cap cnt = %d\n", intelmaddata->playback_cnt, + intelmaddata->capture_cnt); + + /*if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (intelmaddata->playback_cnt >= PLAYBACK_COUNT) + return -EINVAL; + } else { + if (intelmaddata->capture_cnt >= CAPTURE_COUNT) + return -EINVAL; + }*/ + if (!stream->stream_info.str_id) { + ret_val = snd_intelmad_alloc_stream(substream); + if (ret_val < 0) + return ret_val; + stream->dbg_cum_bytes = 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + intelmaddata->playback_cnt++; + else + intelmaddata->capture_cnt++; + pr_debug("period size = %d \n", + (int)substream->runtime->period_size); + pr_debug("buf size = %d \n", + (int)substream->runtime->buffer_size); + memset(&stream->pcm_indirect, 0, sizeof(stream->pcm_indirect)); + stream->pcm_indirect.hw_buffer_size = + snd_pcm_lib_buffer_bytes(substream); + stream->pcm_indirect.sw_buffer_size = + stream->pcm_indirect.hw_buffer_size; + /* return back the stream id */ + snprintf(substream->pcm->id, sizeof(substream->pcm->id), + "%d", stream->stream_info.str_id); + pr_debug("stream id to user = %s\n", substream->pcm->id); + } + ret_val = snd_intelmad_init_stream(substream); + if (ret_val) + return ret_val; + /* if (substream->stream == STREAM_OPS_CAPTURE) */ + substream->runtime->hw.info = SNDRV_PCM_INFO_BATCH; + return ret_val; +} + +static int snd_intelmad_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int ret_val; + + pr_debug("called\n"); + ret_val = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + memset(substream->runtime->dma_area, 0, + params_buffer_bytes(hw_params)); + return ret_val; +} + +static int snd_intelmad_hw_free(struct snd_pcm_substream *substream) +{ + pr_debug("called\n"); + return snd_pcm_lib_free_pages(substream); +} + +/** +* snd_intelmad_pcm_pointer- to send the current buffer pointer processed by hw +* @substream: substream for which the function is called +* This function is called by ALSA framework to get the current hw buffer ptr +* when a period is elapsed +*/ +static snd_pcm_uframes_t snd_intelmad_pcm_pointer + (struct snd_pcm_substream *substream) +{ + /* struct snd_pcm_runtime *runtime = substream->runtime; */ + struct mad_stream_pvt *stream; + struct snd_intelmad *intelmaddata; + int ret_val; + unsigned long buf_size; + + WARN_ON(!substream); + + intelmaddata = snd_pcm_substream_chip(substream); + stream = substream->runtime->private_data; + if (stream->stream_status == INIT) + return 0; + + ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_BUFFER_POINTER, + &stream->stream_info); + if (ret_val) { + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: error code = 0x%x \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__ , ret_val); + return ret_val; + } + pr_debug("samples reported out 0x%llx \n", + stream->stream_info.buffer_ptr); + pr_debug("Frame bits:: %d period_count :: %d \n", + (int)substream->runtime->frame_bits, + (int)substream->runtime->period_size); + buf_size = frames_to_bytes(substream->runtime, + stream->stream_info.buffer_ptr); + pr_debug(" bytes reported out = 0x%lx\n", buf_size); + if (buf_size > stream->dbg_cum_bytes) + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: excess reported \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + + if (substream->stream == STREAM_OPS_PLAYBACK) + return snd_pcm_indirect_playback_pointer( + substream, &stream->pcm_indirect, buf_size); + else + return snd_pcm_indirect_capture_pointer( + substream, &stream->pcm_indirect, buf_size); +} + +/** +* snd_intelmad_close- to free parameteres when stream is stopped +* @substream: substream for which the function is called +* This function is called by ALSA framework when stream is stopped +*/ +static int snd_intelmad_close(struct snd_pcm_substream *substream) +{ + struct snd_intelmad *intelmaddata; + struct mad_stream_pvt *stream; + int ret_val = 0; + + WARN_ON(!substream); + + stream = substream->runtime->private_data; + + pr_debug("called \n"); + intelmaddata = snd_pcm_substream_chip(substream); + + pr_debug("str id = %d\n", stream->stream_info.str_id); + if (stream->stream_info.str_id) { + /* SST API to actually stop/free the stream */ + ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_FREE, + &stream->stream_info.str_id); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + intelmaddata->playback_cnt--; + else + intelmaddata->capture_cnt--; + } + pr_debug("pb cnt = %d cap cnt = %d\n", intelmaddata->playback_cnt, + intelmaddata->capture_cnt); + kfree(substream->runtime->private_data); + return ret_val; +} + +/** +* snd_intelmad_open- to set runtime parameters during stream start +* @substream: substream for which the function is called +* This function is called by ALSA framework when stream is started +*/ +static int snd_intelmad_open(struct snd_pcm_substream *substream) +{ + struct snd_intelmad *intelmaddata; + struct snd_pcm_runtime *runtime; + struct mad_stream_pvt *stream; + + WARN_ON(!substream); + + pr_debug("called \n"); + + intelmaddata = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + /* set the runtime hw parameter with local snd_pcm_hardware struct */ + runtime->hw = snd_intelmad_stream; + /* setup the internal datastruture stream pointers based on it being + playback or capture stream */ + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + stream->stream_info.str_id = 0; + stream->stream_status = INIT; + runtime->private_data = stream; + return snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +} + +static struct snd_pcm_ops snd_intelmad_playback_ops = { + .open = snd_intelmad_open, + .close = snd_intelmad_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intelmad_hw_params, + .hw_free = snd_intelmad_hw_free, + .prepare = snd_intelmad_pcm_prepare, + .trigger = snd_intelmad_pcm_trigger, + .pointer = snd_intelmad_pcm_pointer, + .ack = snd_intelmad_pcm_ack, +}; + +static struct snd_pcm_ops snd_intelmad_capture_ops = { + .open = snd_intelmad_open, + .close = snd_intelmad_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_intelmad_hw_params, + .hw_free = snd_intelmad_hw_free, + .prepare = snd_intelmad_pcm_prepare, + .trigger = snd_intelmad_pcm_trigger, + .pointer = snd_intelmad_pcm_pointer, + .ack = snd_intelmad_pcm_ack, +}; + + +#ifdef REG_IRQ +/** +* snd_intelmad_intr_handler- interrupt handler +*@irq : irq number of the interrupt received +*@dev: device context +* This function is called when an interrupt is raised at the sound card +*/ +static irqreturn_t snd_intelmad_intr_handler(int irq, void *dev) +{ + struct snd_intelmad *intelmaddata = + (struct snd_intelmad *)dev; + u8 intsts; + + memcpy_fromio(&intsts, + ((void *)(intelmaddata->int_base)), + sizeof(u8)); + intelmaddata->mad_jack_msg.intsts = intsts; + intelmaddata->mad_jack_msg.intelmaddata = intelmaddata; + + queue_work(intelmaddata->mad_jack_wq, &intelmaddata->mad_jack_msg.wq); + + return IRQ_HANDLED; +} + +void sst_process_mad_jack_detection(struct work_struct *work) +{ + u8 intsts, value = 0; + struct snd_jack *jack = NULL; + unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; + struct sc_reg_access sc_access[] = { + {0x187, 0x00, MASK7}, + {0x188, 0x10, MASK4}, + {0x18b, 0x10, MASK4}, + }; + struct sc_reg_access sc_access_read = {0,}; + + struct sc_reg_access sc_access_write[] = { + {0x198, 0x00, 0x0}, + }; + struct mad_jack_msg_wq *mad_jack_detect = + container_of(work, struct mad_jack_msg_wq, wq); + + struct snd_intelmad *intelmaddata = + mad_jack_detect->intelmaddata; + + intsts = mad_jack_detect->intsts; + + switch (intelmaddata->sstdrv_ops->vendor_id) { + case SND_FS: + if (intsts & 0x4) { + + if (!(audio_interrupt_enable)) { + pr_debug("Audio interrupt enable\n"); + sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); + + sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1); + audio_interrupt_enable = 1; + + } + /* send headphone detect */ + pr_debug("MAD headphone = %d!!!\n", intsts & 0x4); + jack = &intelmaddata->jack[0].jack; + present = !(intelmaddata->jack[0].jack_status); + intelmaddata->jack[0].jack_status = present; + jack_event_flag = 1; + + } + + if (intsts & 0x2) { + /* send short push */ + pr_debug("MAD short push = %d!!!\n", intsts & 0x2); + jack = &intelmaddata->jack[2].jack; + present = 1; + jack_event_flag = 1; + buttonpressflag = 1; + } + if (intsts & 0x1) { + /* send long push */ + pr_debug("MAD long push = %d!!!\n", intsts & 0x1); + jack = &intelmaddata->jack[3].jack; + present = 1; + jack_event_flag = 1; + buttonpressflag = 1; + } + if (intsts & 0x8) { + if (!(audio_interrupt_enable)) { + pr_debug("Audio interrupt enable\n"); + sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 4); + audio_interrupt_enable = 1; + } + /* send headset detect */ + pr_debug("MAD headset = %d!!!\n", intsts & 0x8); + jack = &intelmaddata->jack[1].jack; + present = !(intelmaddata->jack[1].jack_status); + intelmaddata->jack[1].jack_status = present; + jack_event_flag = 1; + } + + break; + case SND_MX:/* FIXME */ + if (intsts & 0x2) { + if (intelmaddata->pmic_status == PMIC_INIT) { + sc_access_read.reg_addr = 0x201; + sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); + value = (sc_access_read.value); + pr_debug("value returned = 0x%x\n", value); + } + if (value & 0xc0) { + /*accessory is removed */ + + if (intelmaddata->jack[0].jack_status) { + pr_debug("MAD headset detected\n"); + /* send headphone detect/undetect */ + jack = &intelmaddata->jack[0].jack; + present = 0; + intelmaddata->jack[0].jack_status = present; + } + + if (intelmaddata->jack[1].jack_status) { + pr_debug("MAD headphone detected\n"); + /* send headphone detect/undetect */ + jack = &intelmaddata->jack[1].jack; + present = 0; + intelmaddata->jack[1].jack_status = present; + } + + + } + if (value & 0x00) { + + if (intelmaddata->jack[1].jack_status) { + /*button pressed */ + pr_debug("MAD headphone detected\n"); + /* send headphone detect/undetect */ + jack = &intelmaddata->jack[2].jack; + present = 1; + intelmaddata->jack[2].jack_status = present; + jack_event_flag = 1; + buttonpressflag = 1; + + } else { + pr_debug("MAD headphone detected\n"); + /* send headphone detect/undetect */ + jack = &intelmaddata->jack[0].jack; + present = 1; + intelmaddata->jack[0].jack_status = present; + jack_event_flag = 1; + } + } + if (value & 0x40) { + pr_debug("MAD headset detected\n"); + /* send headphone detect/undetect */ + jack = &intelmaddata->jack[1].jack; + present = 1; + intelmaddata->jack[1].jack_status = present; + jack_event_flag = 1; + } + + } + break; + case SND_NC: + if (intelmaddata->pmic_status == PMIC_INIT) { + sc_access_read.reg_addr = 0x132; + sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); + value = (sc_access_read.value); + pr_debug("value returned = 0x%x\n", value); + } + if (intsts & 0x1) { + pr_debug("MAD headset detected\n"); + /* send headset detect/undetect */ + jack = &intelmaddata->jack[1].jack; + present = (value == 0x1) ? 1 : 0; + jack_event_flag = 1; + } + if (intsts & 0x2) { + pr_debug("MAD headphone detected\n"); + /* send headphone detect/undetect */ + jack = &intelmaddata->jack[0].jack; + present = (value == 0x2) ? 1 : 0; + jack_event_flag = 1; + } + if (intsts & 0x4) { + pr_debug("MAD short push detected\n"); + /* send short push */ + jack = &intelmaddata->jack[2].jack; + present = 1; + jack_event_flag = 1; + buttonpressflag = 1; + } + if (intsts & 0x8) { + pr_debug("MAD long push detected\n"); + /* send long push */ + jack = &intelmaddata->jack[3].jack; + present = 1; + jack_event_flag = 1; + buttonpressflag = 1; + } + break; + } + + if (!jack) { + pr_debug("MAD error jack empty \n"); + + } else { + pr_debug("MAD sending jack report for = %d!!!\n", present); + if (jack) + pr_debug("MAD sending jack report for = %d !!!\n", jack->type); + if (jack_event_flag) + snd_jack_report(jack, present); + if (buttonpressflag) + snd_jack_report(jack, 0); + pr_debug("MAD sending jack report Done !!!\n"); + } +} + +static int __devinit snd_intelmad_register_irq( + struct snd_intelmad *intelmaddata) +{ + int ret_val; + u32 regbase = AUDINT_BASE, regsize = 8; + + pr_debug("irq reg done, now mapping... regbase 0x%x, regsize 0x%x\n", + regbase, regsize); + intelmaddata->int_base = ioremap_nocache(regbase, regsize); + if (!intelmaddata->int_base) + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: Mapping of cache failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + + /* interpret irq field */ + pr_debug("irq = 0x%x\n", intelmaddata->irq); + ret_val = request_irq(intelmaddata->irq, + snd_intelmad_intr_handler, + IRQF_SHARED, DRIVER_NAME, + intelmaddata); + if (ret_val) + dev_err(&intelmaddata->spi->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: cannot register IRQ \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return ret_val; +} + +/*static int __devinit snd_intelmad_register_netlink(void) +{ + int ret_val; + + ret_val = genl_register_family(&audio_event_genl_family); + if (ret_val) { + pr_debug("netlink registration failed\n"); + return ret_val; + } + ret_val = genl_register_mc_group(&audio_event_genl_family, + &audio_event_mcgrp); + if (ret_val) { + pr_debug("netlink group registration failed\n"); + genl_unregister_family(&audio_event_genl_family); + return ret_val; + } + return ret_val; +}*/ +#endif + +static int __devinit snd_intelmad_sst_register( + struct snd_intelmad *intelmaddata) +{ + int ret_val; + struct sc_reg_access pmic_reg = {0,}; + + pmic_reg.reg_addr = 0; + ret_val = sst_sc_reg_access(&pmic_reg, PMIC_READ, 1); + + if (ret_val) + return ret_val; + + sst_card_vendor_id = pmic_reg.value & (MASK2|MASK1|MASK0); + pr_debug("orginal reg n extrated vendor id = 0x%x %d\n", + pmic_reg.value, sst_card_vendor_id); + if (sst_card_vendor_id < 0 || sst_card_vendor_id > 2) { + dev_err(&intelmaddata->spi->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: vendor card not supported!! \n"\ + , get_seconds(), sst_get_time(), __func__ , __LINE__); + return -EIO; + } + intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES; + intelmaddata->sstdrv_ops->vendor_id = sst_card_vendor_id; + intelmaddata->sstdrv_ops->scard_ops = + intelmad_vendor_ops[sst_card_vendor_id]; + + /* registering with SST driver to get access to SST APIs to use */ + ret_val = register_sst_card(intelmaddata->sstdrv_ops); + if (ret_val) { + dev_err(&intelmaddata->spi->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: sst card registration failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + return ret_val; + } + + sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id; + intelmaddata->pmic_status = PMIC_UNINIT; + return ret_val; +} + +/* Driver Init/exit functionalities */ +/** +* snd_intelmad_pcm- to setup pcm for the card +* @card: pointer to the sound card structure +*@intelmaddata: pointer to internal context +* This function is called from probe function to set up pcm params and functions +*/ +static int __devinit snd_intelmad_pcm(struct snd_card *card, + struct snd_intelmad *intelmaddata) +{ + struct snd_pcm *pcm; + int i, ret_val = 0; + char name[32] = INTEL_MAD; + + WARN_ON(!card); + WARN_ON(!intelmaddata); + + for (i = 0; i < MAX_DEVICES; i++) { + ret_val = snd_pcm_new(card, name, i, PLAYBACK_COUNT, + CAPTURE_COUNT, &pcm); + if (ret_val) + break; + /* setup the ops for playback and capture streams */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_intelmad_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_intelmad_capture_ops); + /* setup private data which can be retrieved when required */ + pcm->private_data = intelmaddata; + pcm->info_flags = 0; + strncpy(pcm->name, card->shortname, strlen(card->shortname)); + /* allocate dma pages for ALSA stream operations */ + snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + MIN_BUFFER, MAX_BUFFER); + } + return ret_val; +} + +/** +* snd_intelmad_jack- to setup jack settings of the card +*@intelmaddata: pointer to internal context +* This function is called from probe function to set up mixer controls +*/ +static int __devinit snd_intelmad_jack(struct snd_intelmad *intelmaddata) +{ + struct snd_jack *jack; + int retval; + + pr_debug("called\n"); + jack = &intelmaddata->jack[0].jack; + retval = snd_jack_new(intelmaddata->card, "Headphone", + SND_JACK_HEADPHONE, &jack); + if (retval < 0) + return retval; + snd_jack_report(jack, 0); + + jack->private_data = jack; + intelmaddata->jack[0].jack = *jack; + intelmaddata->jack[0].jack_status = 0; + + + jack = &intelmaddata->jack[1].jack; + retval = snd_jack_new(intelmaddata->card, "Headset", + SND_JACK_HEADSET, &jack); + if (retval < 0) + return retval; + + + snd_jack_report(jack, 0); + + jack->private_data = jack; + intelmaddata->jack[1].jack = *jack; + intelmaddata->jack[1].jack_status = 0; + + + jack = &intelmaddata->jack[2].jack; + retval = snd_jack_new(intelmaddata->card, "Short Press", + SND_JACK_HS_SHORT_PRESS, &jack); + if (retval < 0) + return retval; + + snd_jack_report(jack, 0); + + jack->private_data = jack; + intelmaddata->jack[2].jack = *jack; + intelmaddata->jack[2].jack_status = 0; + + + jack = &intelmaddata->jack[3].jack; + retval = snd_jack_new(intelmaddata->card, "Long Press", + SND_JACK_HS_LONG_PRESS, &jack); + if (retval < 0) + return retval; + + snd_jack_report(jack, 0); + + jack->private_data = jack; + intelmaddata->jack[3].jack = *jack; + intelmaddata->jack[3].jack_status = 0; + + return retval; +} + +/** +* snd_intelmad_mixer- to setup mixer settings of the card +*@intelmaddata: pointer to internal context +* This function is called from probe function to set up mixer controls +*/ +static int __devinit snd_intelmad_mixer(struct snd_intelmad *intelmaddata) +{ + struct snd_card *card; + unsigned int idx; + int ret_val = 0; + char *mixername = "IntelMAD Controls"; + + WARN_ON(!intelmaddata); + + card = intelmaddata->card; + + strncpy(card->mixername, mixername, strlen(mixername)); + /* add all widget controls and expose the same */ + for (idx = 0; idx < MAX_CTRL; idx++) { + ret_val = snd_ctl_add(card, + snd_ctl_new1(&snd_intelmad_controls[idx], + intelmaddata)); + pr_debug("mixer[idx]=%d added \n", idx); + if (ret_val) { + dev_err(&intelmaddata->spi->dev, "Time:%8ld::%8ld >> +\ + SST: %s %d ERR: adding of control failed index = %d \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__ , idx); + break; + } + } + return ret_val; +} + +/** +* snd_intelmad_dev_free- to free device +*@device: pointer to the device +* This function is called when driver module is removed +*/ +static int snd_intelmad_dev_free(struct snd_device *device) +{ + struct snd_intelmad *intelmaddata; + + WARN_ON(!device); + + intelmaddata = device->device_data; + + pr_debug("called\n"); + snd_card_free(intelmaddata->card); + /*genl_unregister_family(&audio_event_genl_family);*/ + unregister_sst_card(intelmaddata->sstdrv_ops); + + /* free allocated memory for internal context */ + destroy_workqueue(intelmaddata->mad_jack_wq); + kfree(intelmaddata->sstdrv_ops); + kfree(intelmaddata); + return 0; +} + +/** +* snd_intelmad_create- called from probe to create a snd device +*@intelmaddata : pointer to the internal context +*@card : pointer to the sound card +* This function is called when driver module is started +*/ +static int __devinit snd_intelmad_create( + struct snd_intelmad *intelmaddata, + struct snd_card *card) +{ + int ret_val; + static struct snd_device_ops ops = { + .dev_free = snd_intelmad_dev_free, + }; + + WARN_ON(!intelmaddata); + WARN_ON(!card); + /* ALSA api to register for the device */ + ret_val = snd_device_new(card, SNDRV_DEV_LOWLEVEL, intelmaddata, &ops); + return ret_val; +} + +/********************************************************************* + * SPI Functions + *********************************************************************/ + + +/** +* snd_intelmad_probe- function registred for init +*@spi : pointer to the spi device context +* This function is called when the device is initialized +*/ +int __devinit snd_intelmad_probe(struct spi_device *spi) +{ + struct snd_card *card; + int ret_val; + struct snd_intelmad *intelmaddata; + + pr_debug("called \n"); + + /* allocate memory for saving internal context and working */ + intelmaddata = kzalloc(sizeof(*intelmaddata), GFP_KERNEL); + if (!intelmaddata) + return -ENOMEM; + + /* allocate memory for LPE API set */ + intelmaddata->sstdrv_ops = kzalloc(sizeof(struct intel_sst_card_ops), + GFP_KERNEL); + if (!intelmaddata->sstdrv_ops) { + dev_err(&intelmaddata->spi->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: mem alloctn fail \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + kfree(intelmaddata); + return -ENOMEM; + } + + /* create a card instance with ALSA framework */ + ret_val = snd_card_create(card_index, card_id, THIS_MODULE, 0, &card); + if (ret_val) { + dev_err(&intelmaddata->spi->dev, \ + "Time:%8ld::%8ld >> SST: %s %d ERR: snd_card_create fail \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto free_allocs; + } + + intelmaddata->spi = spi; + intelmaddata->irq = spi->irq; + dev_set_drvdata(&spi->dev, intelmaddata); + intelmaddata->card = card; + intelmaddata->card_id = card_id; + intelmaddata->card_index = card_index; + intelmaddata->playback_cnt = intelmaddata->capture_cnt = 0; + strncpy(card->driver, INTEL_MAD, strlen(INTEL_MAD)); + strncpy(card->shortname, INTEL_MAD, strlen(INTEL_MAD)); + + + intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES; + /* registering with LPE driver to get access to SST APIs to use */ + ret_val = snd_intelmad_sst_register(intelmaddata); + if (ret_val) { + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR:+\ + snd_intelmad_sst_register failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto free_allocs; + } + + intelmaddata->pmic_status = PMIC_INIT; + + ret_val = snd_intelmad_pcm(card, intelmaddata); + if (ret_val) { + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: snd_intelmad_pcm failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto free_allocs; + } + + ret_val = snd_intelmad_mixer(intelmaddata); + if (ret_val) { + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: snd_intelmad_mixer failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto free_allocs; + } + + ret_val = snd_intelmad_jack(intelmaddata); + if (ret_val) { + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: snd_intelmad_jack failed \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto free_allocs; + } + + /*create work queue for jack interrupt*/ + + INIT_WORK(&intelmaddata->mad_jack_msg.wq, sst_process_mad_jack_detection); + + intelmaddata->mad_jack_wq = create_workqueue("sst_mad_jack_wq"); + if (!intelmaddata->mad_jack_wq) + goto free_mad_jack_wq; + +#ifdef REG_IRQ + ret_val = snd_intelmad_register_irq(intelmaddata); + if (ret_val) { + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR:+\ + snd_intelmad_register_irq fail \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto free_allocs; + } + /*ret_val = snd_intelmad_register_netlink(); + if (ret_val) { + pr_debug("...complete\n"); + return ret_val; + }*/ +#endif + + /* internal function call to register device with ALSA */ + ret_val = snd_intelmad_create(intelmaddata, card); + if (ret_val) { + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + snd_intelmad_create failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto free_allocs; + } + card->private_data = &intelmaddata; + snd_card_set_dev(card, &spi->dev); + ret_val = snd_card_register(card); + if (ret_val) { + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: snd_card_register failed \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__); + goto free_allocs; + } + + pr_debug("...complete\n"); + return ret_val; + +free_mad_jack_wq: + destroy_workqueue(intelmaddata->mad_jack_wq); + +free_allocs: + /* TODO: unregister IRQ */ + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: probe failed \n", \ + get_seconds(), sst_get_time(), __func__ , __LINE__); + /* snd_card_free(card); */ + kfree(intelmaddata->sstdrv_ops); + kfree(intelmaddata); + return ret_val; +} + + +/** +* snd_intelmad_remove- function registred for exit +*@spi : pointer to the spi device context +* This function is called when the device is uninitialized +*/ +static int snd_intelmad_remove(struct spi_device *spi) +{ + struct snd_intelmad *intelmaddata = + dev_get_drvdata(&spi->dev); + /* + * TODO:: de-register interrupt handler + */ + + if (intelmaddata) { + snd_card_free(intelmaddata->card); + /*genl_unregister_family(&audio_event_genl_family);*/ + unregister_sst_card(intelmaddata->sstdrv_ops); + /* free allocated memory for internal context */ + destroy_workqueue(intelmaddata->mad_jack_wq); + kfree(intelmaddata->sstdrv_ops); + kfree(intelmaddata); + } + return 0; +} + +/********************************************************************* + * Driver initialization and exit + *********************************************************************/ + +static struct spi_driver snd_intelmad_driver = { + .driver = { + .name = DRIVER_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = snd_intelmad_probe, + .remove = __devexit_p(snd_intelmad_remove), +}; + +/* +* alsa_card_intelmad_init- driver init function +* This function is called when driver module is inserted +*/ +static int __init alsa_card_intelmad_init(void) +{ + pr_debug("called\n"); + return spi_register_driver(&snd_intelmad_driver); +} + +/** +* alsa_card_intelmad_exit- driver exit function +* This function is called when driver module is removed +*/ +static void __exit alsa_card_intelmad_exit(void) +{ + pr_debug("called\n"); + spi_unregister_driver(&snd_intelmad_driver); +} + +module_init(alsa_card_intelmad_init) +module_exit(alsa_card_intelmad_exit) + Index: linux-2.6.33/sound/pci/sst/intelmid.h =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intelmid.h @@ -0,0 +1,165 @@ +/* + * intelmid.h - Intel Sound card driver for MID + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Harsha Priya <priya.harsha@intel.com> + * Vinod Koul <vinod.koul@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * ALSA driver header for Intel MAD chipset + */ +#ifndef __INTELMID_H +#define __INTELMID_H + +#include <linux/time.h> + +#define DRIVER_NAME "pmic_audio" +#define PMIC_SOUND_IRQ_TYPE_MASK (1 << 15) +#define AUDINT_BASE (0xFFFFEFF8 + (6 * sizeof(u8))) +#define REG_IRQ +/* values #defined */ +/* will differ for different hw - to be taken from config */ +#define MAX_DEVICES 1 +#define MIN_RATE 8000 +#define MAX_RATE 48000 +#define MAX_BUFFER (320*1024) /* TBD for PCM */ +#define MIN_BUFFER (320*1024) +#define MAX_PERIODS (1024*2) +#define MIN_PERIODS 1 +#define MAX_PERIOD_BYTES MAX_BUFFER +#define MIN_PERIOD_BYTES 32 +#define MAX_MUTE 1 +#define MIN_MUTE 0 +#define MONO_CNTL 1 +#define STEREO_CNTL 2 +#define MIN_CHANNEL 1 +#define MAX_CHANNEL 2 +#define FIFO_SIZE 0 /* fifo not being used */ +#define INTEL_MAD "Intel MAD" +#define MAX_CTRL 7 +#define MAX_VENDORS 3 +/* TODO +6 db */ +#define MAX_VOL 64 +/* TODO -57 db */ +#define MIN_VOL 0 +#define PLAYBACK_COUNT 1 +#define CAPTURE_COUNT 1 + +extern int sst_card_vendor_id; + +struct mad_jack { + struct snd_jack jack; + int jack_status; +}; +struct mad_jack_msg_wq { + u8 intsts; + struct snd_intelmad *intelmaddata; + struct work_struct wq; + +}; +struct snd_intelmad { + struct snd_card *card; /* ptr to the card details */ + int card_index;/* card index */ + char *card_id; /* card id */ + struct intel_sst_card_ops *sstdrv_ops;/* ptr to sst driver ops */ + struct spi_device *spi; + int irq; + int pmic_status; + void __iomem *int_base; + int output_sel; + int input_sel; + int master_mute; + struct mad_jack jack[4]; + int playback_cnt; + int capture_cnt; + struct mad_jack_msg_wq mad_jack_msg; + struct workqueue_struct *mad_jack_wq; +}; + +struct snd_control_val { + int playback_vol_max; + int playback_vol_min; + int capture_vol_max; + int capture_vol_min; +}; + +struct mad_stream_pvt { + int stream_status; + int stream_ops; + struct snd_pcm_substream *substream; + struct snd_pcm_indirect pcm_indirect; + struct pcm_stream_info stream_info; + ssize_t dbg_cum_bytes; +}; + +enum mad_drv_status { + INIT = 1, + STARTED, + RUNNING, + PAUSED, + DROPPED, +}; + +enum mad_pmic_status { + PMIC_UNINIT = 1, + PMIC_INIT, +}; +enum _widget_ctrl { + PLAYBACK_VOL = 1 , + PLAYBACK_MUTE, + CAPTURE_VOL, + CAPTURE_MUTE, + OUTPUT_SEL, + INPUT_SEL, + MASTER_MUTE +}; + +/*enum { + AUDIO_GENL_ATTR_UNSPEC = 0, + AUDIO_GENL_ATTR_EVENT, + AUDIO_GENL_ATTR_MAX, +}; +enum { + AUDIO_GENL_CMD_UNSPEC, + AUDIO_GENL_CMD_EVENT, + AUDIO_GENL_CMD_MAX, +}; + +enum eaudio_events { + AUDIO_EVENT_HP_DETECT, + AUDIO_EVENT_HS_DETECT, + AUDIO_EVENT_SHORT_PRESS, + AUDIO_EVENT_LONG_PRESS, + AUDIO_EVENT_COUNT, +}; + +struct audio_genl_event { + u32 orig; + enum eaudio_events event; +};*/ + + +void period_elapsed(void *mad_substream); +int snd_intelmad_alloc_stream(struct snd_pcm_substream *substream); +int snd_intelmad_init_stream(struct snd_pcm_substream *substream); +void send_buffer_to_sst(struct snd_pcm_substream *substream, + struct snd_pcm_indirect *rec, size_t bytes); +int sst_sc_reg_access(struct sc_reg_access *sc_access, + int type, int num_val); + + +#endif /* __INTELMID_H */ Index: linux-2.6.33/sound/pci/sst/intelmid_ctrl.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intelmid_ctrl.c @@ -0,0 +1,542 @@ +/* + * intelmid_ctrl.c - Intel Sound card driver for MID + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Harsha Priya <priya.harsha@intel.com> + * Vinod Koul <vinod.koul@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * ALSA driver handling mixer controls for Intel MAD chipset + */ +#include <linux/spi/spi.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/sched.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/initval.h> +#include <sound/pcm-indirect.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intelmid_snd_control.h" +#include "intelmid.h" + +static char *out_names[] = {"Headphones", + "Internal speakers"}; +static char *in_names[] = {"HS_MIC", + "AMIC", + "DMIC"}; + +struct snd_pmic_ops *intelmad_vendor_ops[MAX_VENDORS] = { + &snd_pmic_ops_fs, + &snd_pmic_ops_mx, + &snd_pmic_ops_nc +}; + +struct snd_control_val intelmad_ctrl_val[MAX_VENDORS] = { + { + .playback_vol_max = 63, + .playback_vol_min = 0, + .capture_vol_max = 63, + .capture_vol_min = 0, + }, + { + .playback_vol_max = 0, + .playback_vol_min = -31, + .capture_vol_max = 0, + .capture_vol_min = -20, + }, + { + .playback_vol_max = 0, + .playback_vol_min = -126, + .capture_vol_max = 0, + .capture_vol_min = -31, + }, +}; + +/* control path functionalities */ + +static inline int snd_intelmad_volume_info(struct snd_ctl_elem_info *uinfo, + int control_type, int max, int min) +{ + WARN_ON(!uinfo); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = control_type; + uinfo->value.integer.min = min; + uinfo->value.integer.max = max; + return 0; +} + +/** +* snd_intelmad_mute_info - provides information about the mute controls +* @kcontrol: pointer to the control +* @uinfo: pointer to the structure where the control's info need +* to be filled +* This function is called when a mixer application requests for control's info +*/ +static int snd_intelmad_mute_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + WARN_ON(!uinfo); + WARN_ON(!kcontrol); + + /* set up the mute as a boolean mono control with min-max values */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = MONO_CNTL; + uinfo->value.integer.min = MIN_MUTE; + uinfo->value.integer.max = MAX_MUTE; + return 0; +} + +/** +* snd_intelmad_capture_volume_info - provides info about the volume control +* @kcontrol: pointer to the control +* @uinfo: pointer to the structure where the control's info need +* to be filled +* This function is called when a mixer application requests for control's info +*/ +static int snd_intelmad_capture_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + snd_intelmad_volume_info(uinfo, MONO_CNTL, + intelmad_ctrl_val[sst_card_vendor_id].capture_vol_max, + intelmad_ctrl_val[sst_card_vendor_id].capture_vol_min); + return 0; +} + +/** +* snd_intelmad_playback_volume_info - provides info about the volume control +* @kcontrol: pointer to the control +* @uinfo: pointer to the structure where the control's info need +* to be filled +* This function is called when a mixer application requests for control's info +*/ +static int snd_intelmad_playback_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + snd_intelmad_volume_info(uinfo, STEREO_CNTL, + intelmad_ctrl_val[sst_card_vendor_id].playback_vol_max, + intelmad_ctrl_val[sst_card_vendor_id].playback_vol_min); + return 0; +} + +/** +* snd_intelmad_device_info - provides information about the devices available +* @kcontrol: pointer to the control +* @uinfo: pointer to the structure where the devices's info need +* to be filled +* This function is called when a mixer application requests for device's info +*/ +static int snd_intelmad_device_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + WARN_ON(!kcontrol); + WARN_ON(!uinfo); + /* setup device select as drop down controls with different values */ + if (kcontrol->id.numid == OUTPUT_SEL) + uinfo->value.enumerated.items = ARRAY_SIZE(out_names); + else + uinfo->value.enumerated.items = ARRAY_SIZE(in_names); + uinfo->count = MONO_CNTL; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = 1; + if (kcontrol->id.numid == OUTPUT_SEL) + strncpy(uinfo->value.enumerated.name, + out_names[uinfo->value.enumerated.item], + strlen(out_names[uinfo->value.enumerated.item])); + else + strncpy(uinfo->value.enumerated.name, + in_names[uinfo->value.enumerated.item], + strlen(in_names[uinfo->value.enumerated.item])); + return 0; +} + +/** +* snd_intelmad_volume_get - gets the current volume for the control +* @kcontrol: pointer to the control +* @uval: pointer to the structure where the control's info need +* to be filled +* This function is called when .get function of a control is invoked from app +*/ +static int snd_intelmad_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uval) +{ + int ret_val = 0, cntl_list[2] = {0,}; + u8 value = 0; + struct snd_intelmad *intelmaddata; + struct snd_pmic_ops *scard_ops; + + pr_debug("called\n"); + + WARN_ON(!uval); + WARN_ON(!kcontrol); + + intelmaddata = kcontrol->private_data; + + WARN_ON(!intelmaddata->sstdrv_ops); + + scard_ops = intelmaddata->sstdrv_ops->scard_ops; + + WARN_ON(!scard_ops); + + switch (kcontrol->id.numid) { + case PLAYBACK_VOL: + cntl_list[0] = PMIC_SND_RIGHT_PB_VOL; + cntl_list[1] = PMIC_SND_LEFT_PB_VOL; + break; + + case CAPTURE_VOL: + cntl_list[0] = PMIC_SND_CAPTURE_VOL; + break; + default: + return -EINVAL; + } + + ret_val = scard_ops->get_vol(cntl_list[0], &value); + uval->value.integer.value[0] = value; + + if (ret_val) + return ret_val; + + if (kcontrol->id.numid == PLAYBACK_VOL) { + ret_val = scard_ops->get_vol(cntl_list[1], &value); + uval->value.integer.value[1] = value; + } + return ret_val; +} + +/** +* snd_intelmad_mute_get - gets the current mute status for the control +* @kcontrol: pointer to the control +* @uval: pointer to the structure where the control's info need +* to be filled +* This function is called when .get function of a control is invoked from app +*/ +static int snd_intelmad_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uval) +{ + + int cntl_list = 0, ret_val = 0; + u8 value = 0; + struct snd_intelmad *intelmaddata; + struct snd_pmic_ops *scard_ops; + + pr_debug("called\n"); + + WARN_ON(!uval); + WARN_ON(!kcontrol); + + intelmaddata = kcontrol->private_data; + + WARN_ON(!intelmaddata->sstdrv_ops); + + scard_ops = intelmaddata->sstdrv_ops->scard_ops; + + WARN_ON(!scard_ops); + + switch (kcontrol->id.numid) { + case PLAYBACK_MUTE: + if (intelmaddata->output_sel == STEREO_HEADPHONE) + cntl_list = PMIC_SND_LEFT_HP_MUTE; + else if (intelmaddata->output_sel == INTERNAL_SPKR) + cntl_list = PMIC_SND_LEFT_SPEAKER_MUTE; + break; + + case CAPTURE_MUTE: + if (intelmaddata->input_sel == DMIC) + cntl_list = PMIC_SND_DMIC_MUTE; + else if (intelmaddata->input_sel == AMIC) + cntl_list = PMIC_SND_AMIC_MUTE; + else if (intelmaddata->input_sel == HS_MIC) + cntl_list = PMIC_SND_HP_MIC_MUTE; + break; + case MASTER_MUTE: + uval->value.integer.value[0] = intelmaddata->master_mute; + return 0; + default: + return -EINVAL; + } + + ret_val = scard_ops->get_mute(cntl_list, &value); + uval->value.integer.value[0] = value; + return ret_val; +} + +/** +* snd_intelmad_volume_set - sets the volume control's info +* @kcontrol: pointer to the control +* @uval: pointer to the structure where the control's info is +* available to be set +* This function is called when .set function of a control is invoked from app +*/ +static int snd_intelmad_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uval) +{ + + int ret_val, cntl_list[2] = {0,}; + struct snd_intelmad *intelmaddata; + struct snd_pmic_ops *scard_ops; + + pr_debug("volume set called:%ld %ld \n", + uval->value.integer.value[0], + uval->value.integer.value[1]); + + WARN_ON(!uval); + WARN_ON(!kcontrol); + + intelmaddata = kcontrol->private_data; + + WARN_ON(!intelmaddata->sstdrv_ops); + + scard_ops = intelmaddata->sstdrv_ops->scard_ops; + + WARN_ON(!scard_ops); + + switch (kcontrol->id.numid) { + case PLAYBACK_VOL: + cntl_list[0] = PMIC_SND_LEFT_PB_VOL; + cntl_list[1] = PMIC_SND_RIGHT_PB_VOL; + break; + + case CAPTURE_VOL: + cntl_list[0] = PMIC_SND_CAPTURE_VOL; + break; + default: + return -EINVAL; + } + + ret_val = scard_ops->set_vol(cntl_list[0], + uval->value.integer.value[0]); + if (ret_val) + return ret_val; + + if (kcontrol->id.numid == PLAYBACK_VOL) + ret_val = scard_ops->set_vol(cntl_list[1], + uval->value.integer.value[1]); + return ret_val; +} + +/** +* snd_intelmad_mute_set - sets the mute control's info +* @kcontrol: pointer to the control +* @uval: pointer to the structure where the control's info is +* available to be set +* This function is called when .set function of a control is invoked from app +*/ +static int snd_intelmad_mute_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uval) +{ + int cntl_list[2] = {0,}, ret_val; + struct snd_intelmad *intelmaddata; + struct snd_pmic_ops *scard_ops; + + pr_debug("called\n"); + + WARN_ON(!uval); + WARN_ON(!kcontrol); + + intelmaddata = kcontrol->private_data; + + WARN_ON(!intelmaddata->sstdrv_ops); + + scard_ops = intelmaddata->sstdrv_ops->scard_ops; + + WARN_ON(!scard_ops); + + kcontrol->private_value = uval->value.integer.value[0]; + + switch (kcontrol->id.numid) { + case PLAYBACK_MUTE: + if (intelmaddata->output_sel == STEREO_HEADPHONE) { + cntl_list[0] = PMIC_SND_LEFT_HP_MUTE; + cntl_list[1] = PMIC_SND_RIGHT_HP_MUTE; + } else if (intelmaddata->output_sel == INTERNAL_SPKR) { + cntl_list[0] = PMIC_SND_LEFT_SPEAKER_MUTE; + cntl_list[1] = PMIC_SND_RIGHT_SPEAKER_MUTE; + } + break; + + case CAPTURE_MUTE:/*based on sel device mute the i/p dev*/ + if (intelmaddata->input_sel == DMIC) + cntl_list[0] = PMIC_SND_DMIC_MUTE; + else if (intelmaddata->input_sel == AMIC) + cntl_list[0] = PMIC_SND_AMIC_MUTE; + else if (intelmaddata->input_sel == HS_MIC) + cntl_list[0] = PMIC_SND_HP_MIC_MUTE; + break; + case MASTER_MUTE: + cntl_list[0] = PMIC_SND_MUTE_ALL; + intelmaddata->master_mute = uval->value.integer.value[0]; + break; + default: + return -EINVAL; + } + + ret_val = scard_ops->set_mute(cntl_list[0], + uval->value.integer.value[0]); + if (ret_val) + return ret_val; + + if (kcontrol->id.numid == PLAYBACK_MUTE) + ret_val = scard_ops->set_mute(cntl_list[1], + uval->value.integer.value[0]); + return ret_val; +} + +/** +* snd_intelmad_device_get - get the device select control's info +* @kcontrol: pointer to the control +* @uval: pointer to the structure where the control's info is +* to be filled +* This function is called when .get function of a control is invoked from app +*/ +static int snd_intelmad_device_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uval) +{ + pr_debug("called\n"); + + WARN_ON(!uval); + WARN_ON(!kcontrol); + + uval->value.enumerated.item[0] = kcontrol->private_value; + return 0; +} + +/** +* snd_intelmad_device_set - set the device select control's info +* @kcontrol: pointer to the control +* @uval: pointer to the structure where the control's info is +* available to be set +* This function is called when .set function of a control is invoked from app +*/ +static int snd_intelmad_device_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uval) +{ + struct snd_intelmad *intelmaddata; + struct snd_pmic_ops *scard_ops; + int ret_val = 0; + + pr_debug("called\n"); + + WARN_ON(!uval); + WARN_ON(!kcontrol); + + intelmaddata = kcontrol->private_data; + + WARN_ON(!intelmaddata->sstdrv_ops); + + scard_ops = intelmaddata->sstdrv_ops->scard_ops; + + WARN_ON(!scard_ops); + + /* store value with driver */ + kcontrol->private_value = uval->value.enumerated.item[0]; + + switch (kcontrol->id.numid) { + case OUTPUT_SEL: + ret_val = scard_ops->set_output_dev( + uval->value.enumerated.item[0]); + intelmaddata->output_sel = uval->value.enumerated.item[0]; + break; + case INPUT_SEL: + ret_val = scard_ops->set_input_dev( + uval->value.enumerated.item[0]); + intelmaddata->input_sel = uval->value.enumerated.item[0]; + break; + default: + return -EINVAL; + } + kcontrol->private_value = uval->value.enumerated.item[0]; + return ret_val; +} + +struct snd_kcontrol_new snd_intelmad_controls[MAX_CTRL] __devinitdata = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_playback_volume_info, + .get = snd_intelmad_volume_get, + .put = snd_intelmad_volume_set, + .private_value = 0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_mute_info, + .get = snd_intelmad_mute_get, + .put = snd_intelmad_mute_set, + .private_value = 0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_capture_volume_info, + .get = snd_intelmad_volume_get, + .put = snd_intelmad_volume_set, + .private_value = 0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_mute_info, + .get = snd_intelmad_mute_get, + .put = snd_intelmad_mute_set, + .private_value = 0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_device_info, + .get = snd_intelmad_device_get, + .put = snd_intelmad_device_set, + .private_value = 0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_device_info, + .get = snd_intelmad_device_get, + .put = snd_intelmad_device_set, + .private_value = 0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_mute_info, + .get = snd_intelmad_mute_get, + .put = snd_intelmad_mute_set, + .private_value = 0, +}, +}; Index: linux-2.6.33/sound/pci/sst/intelmid_ctrl.h =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intelmid_ctrl.h @@ -0,0 +1,32 @@ +/* + * intelmid_ctrl.h - Intel Sound card driver for MID + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Harsha Priya <priya.harsha@intel.com> + * Vinod Koul <vinod.koul@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * ALSA driver header file for handling mixer controls for Intel MAD chipset + */ +#ifndef __INTELMID_CTRL_H +#define __INTELMID_CTRL_H + +extern struct snd_control_val intelmad_ctrl_val[]; +extern struct snd_kcontrol_new snd_intelmad_controls[]; +extern struct snd_pmic_ops *intelmad_vendor_ops[]; + +#endif /*__INTELMID_CTRL_H*/ Index: linux-2.6.33/sound/pci/sst/intelmid_pvt.c =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/intelmid_pvt.c @@ -0,0 +1,349 @@ +/* + * intelmid_pvt.h - Intel Sound card driver for MID + * + * Copyright (C) 2008-09 Intel Corp + * Authors: Harsha Priya <priya.harsha@intel.com> + * Vinod Koul <vinod.koul@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * ALSA driver for Intel MID sound card chipset - holding private functions + */ +#include <linux/spi/spi.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/sched.h> +#include <asm/ipc_defs.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/initval.h> +#include <sound/pcm-indirect.h> +#include <sound/intel_lpe.h> +#include <sound/intel_sst_ioctl.h> +#include "intelmid_snd_control.h" +#include "intelmid.h" + + +/*static unsigned int audio_event_seqnum; +static struct genl_family audio_event_genl_family = { + .id = GENL_ID_GENERATE, + .name = "audio events", + .version = 0x01, + .maxattr = 0, +}; + +static struct genl_multicast_group audio_event_mcgrp = { + .name = "audio_group", +}; +*/ + +void period_elapsed(void *mad_substream) +{ + struct snd_pcm_substream *substream = mad_substream; + struct mad_stream_pvt *stream; + + if (!substream || !substream->runtime) + return; + stream = substream->runtime->private_data; + if (!stream) + return; + + pr_debug("called\n"); + if (stream->stream_status != RUNNING) + return; + pr_debug("calling period elapsed\n"); + snd_pcm_period_elapsed(substream); + return; +} + + +int snd_intelmad_alloc_stream(struct snd_pcm_substream *substream) +{ + struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream); + struct mad_stream_pvt *stream = substream->runtime->private_data; + unsigned int bits_per_sec = (substream->runtime->sample_bits/8) + * (substream->runtime->channels) + * (substream->runtime->rate); + struct snd_sst_stream_params param = {{{0,},},}; + struct snd_sst_params str_params = {0}; + int ret_val; + + /* set codec params and inform SST driver the same */ + + param.uc.pcm_params.codec = SST_CODEC_TYPE_PCM; + param.uc.pcm_params.brate = bits_per_sec; + param.uc.pcm_params.num_chan = (u8) substream->runtime->channels; + param.uc.pcm_params.sfreq = substream->runtime->rate; + param.uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits; + param.uc.pcm_params.frame_size = 0; + param.uc.pcm_params.samples_per_frame = 250; /* FIXME */ + param.uc.pcm_params.period_count = substream->runtime->period_size; + pr_debug("period_count = %d\n", param.uc.pcm_params.period_count); + pr_debug("sfreq= %d, wd_sz = %d\n", param.uc.pcm_params.sfreq, + param.uc.pcm_params.pcm_wd_sz); + + str_params.sparams = param; + str_params.codec = SST_CODEC_TYPE_PCM; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + str_params.ops = STREAM_OPS_PLAYBACK; + else + str_params.ops = STREAM_OPS_CAPTURE; + ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_ALLOC, + &str_params); + pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", + ret_val); + if (ret_val < 0) + return ret_val; + + stream->stream_info.str_id = ret_val; + stream->stream_status = INIT; + stream->stream_info.buffer_ptr = 0; + pr_debug("str id : %d\n", stream->stream_info.str_id); + + return ret_val; +} + +int snd_intelmad_init_stream(struct snd_pcm_substream *substream) +{ + struct mad_stream_pvt *stream = substream->runtime->private_data; + struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream); + int ret_val; + + pr_debug("setting buffer ptr param\n"); + stream->stream_info.period_elapsed = period_elapsed; + stream->stream_info.mad_substream = substream; + stream->stream_info.buffer_ptr = 0; + stream->stream_info.sfreq = substream->runtime->rate; + ret_val = intelmaddata->sstdrv_ops->control_set(SST_SND_STREAM_INIT, + &stream->stream_info); + if (ret_val) + dev_err(&intelmaddata->spi->dev,\ + "Time:%8ld::%8ld >> SST: %s %d ERR: error code = %d \n",\ + get_seconds(), sst_get_time(), __func__ , __LINE__ , ret_val); + return ret_val; + +} + +void send_buffer_to_sst(struct snd_pcm_substream *substream, + struct snd_pcm_indirect *rec, size_t bytes) +{ + struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream); + struct mad_stream_pvt *stream = substream->runtime->private_data; + struct stream_buffer buffer_to_sst = {0,}; + int ret_val; + + /* sends data to SST to be processed */ + stream->dbg_cum_bytes += bytes; + pr_debug("bytes = %d \n", bytes); + pr_debug("cum_bytes = 0x%x, \n", stream->dbg_cum_bytes); + buffer_to_sst.length = bytes; + buffer_to_sst.addr = (unsigned long) substream->runtime->dma_area + + rec->sw_data; + /* SST API to actually send the buffer to be played */ + ret_val = intelmaddata->sstdrv_ops->send_buffer( + stream->stream_info.str_id, + &buffer_to_sst); + pr_debug("send_buffer ret_val = 0x%x \n", ret_val); + return; +} + +/*int snd_intelmad_generate_netlink(u32 orig, enum eaudio_events event) +{ + struct sk_buff *skb = NULL; + struct nlattr *attr = NULL; + struct audio_genl_event *aud_event = NULL; + void *msg_header = NULL; + int size = 0, ret_val = 0; + + + size = nla_total_size(sizeof(struct audio_genl_event)) + \ + nla_total_size(0); + + skb = genlmsg_new(size, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + + msg_header = genlmsg_put(skb, 0, audio_event_seqnum++, + &audio_event_genl_family, 0, + AUDIO_GENL_CMD_EVENT); + if (!msg_header) { + nlmsg_free(skb); + return -ENOMEM; + } + + attr = nla_reserve(skb, AUDIO_GENL_ATTR_EVENT, \ + sizeof(struct audio_genl_event)); + + if (!attr) { + nlmsg_free(skb); + return -EINVAL; + } + + aud_event = nla_data(attr); + if (!aud_event) { + nlmsg_free(skb); + return -EINVAL; + } + + memset(aud_event, 0, sizeof(struct audio_genl_event)); + + aud_event->orig = orig; + aud_event->event = event; + + + ret_val = genlmsg_end(skb, msg_header); + if (ret_val < 0) { + nlmsg_free(skb); + return ret_val; + } + + ret_val = genlmsg_multicast(skb, 0, audio_event_mcgrp.id, GFP_ATOMIC); + + if (ret_val) + printk(KERN_INFO "Failed to send a Genetlink message!\n"); + return 0; +}*/ + + +/** +* Reads/writes/read-modify operations on registers accessed through SCU (sound +* card and few SST DSP regsiters that are not accissible to IA) +*/ +int sst_sc_reg_access(struct sc_reg_access *sc_access, + int type, int num_val) +{ + int i, retval = 0, j = 0, k = 0, count = 0; + struct ipc_pmic_reg_data reg_data; + struct ipc_pmic_mod_reg_data pmic_mod_reg = {0}; + + reg_data.ioc = TRUE; + if (type == PMIC_WRITE) { + do { + int max_retries = 0; + + if (num_val <= 4) + count = num_val; + else + count = 4; +retry_write: + for (i = 0; i < count; i++, j++) { + reg_data.pmic_reg_data[i]. + register_address = sc_access[j].reg_addr; + + reg_data.pmic_reg_data[i].value = + sc_access[j].value; + } + reg_data.num_entries = (u8) count; + retval = ipc_pmic_register_write(®_data, 0); + if (retval == E_NO_INTERRUPT_ON_IOC && + max_retries < 10) { + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: write communcation +\ + needs retry \n", get_seconds(), sst_get_time(), __func__ , __LINE__); + max_retries++; + goto retry_write; + } + if (0 != retval) { + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR:+\ + pmic write failed \n", get_seconds(), +\ + sst_get_time(), __func__ , __LINE__); + return retval; + } + num_val -= count; + } while (num_val > 0); + } else if (type == PMIC_READ) { + do { + int max_retries = 0; + if (num_val <= 4) + count = num_val; + else + count = 4; +retry_read: + for (i = 0; i < count; i++, j++) + reg_data.pmic_reg_data[i].register_address + = sc_access[j].reg_addr; + reg_data.num_entries = count; + retval = ipc_pmic_register_read(®_data); + if (retval == E_NO_INTERRUPT_ON_IOC && + max_retries < 10) { + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d +\ + ERR: read communcation needs retry \n", get_seconds(),\ + sst_get_time(), __func__ , __LINE__); + max_retries++; + goto retry_read; + } + if (0 != retval) { + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d +\ + ERR: pmic read failed \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return retval; + } + + for (i = 0; i < count; i++, k++) + sc_access[k].value = + reg_data.pmic_reg_data[i].value; + num_val -= count; + } while (num_val > 0); + } else { + pmic_mod_reg.ioc = TRUE; + do { + int max_retries = 0; + if (num_val <= 4) + count = num_val; + else + count = 4; +retry_readmod: + for (i = 0; i < count; i++, j++) { + pmic_mod_reg.pmic_mod_reg_data[i]. + register_address = sc_access[j].reg_addr; + pmic_mod_reg.pmic_mod_reg_data[i].value = + sc_access[j].value; + pmic_mod_reg.pmic_mod_reg_data[i].bit_map = + sc_access[j].mask; + } + pmic_mod_reg.num_entries = count; + pr_debug("read_modify called for cnt = %d\n", count); + retval = ipc_pmic_register_read_modify(&pmic_mod_reg); + if (retval == E_NO_INTERRUPT_ON_IOC && + max_retries < 10) { + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR: +\ + read/modify retry \n", get_seconds(), sst_get_time(),\ + __func__ , __LINE__); + max_retries++; + goto retry_readmod; + } + if (0 != retval) { + /* pmic communication fails */ + printk(KERN_ERR "Time:%8ld::%8ld >> SST: %s %d ERR:+\ + pmic read_modify failed \n", get_seconds(), \ + sst_get_time(), __func__ , __LINE__); + return retval; + } + num_val -= count; + } while (num_val > 0); + } + return retval; +} + Index: linux-2.6.33/sound/pci/Kconfig =================================================================== --- linux-2.6.33.orig/sound/pci/Kconfig +++ linux-2.6.33/sound/pci/Kconfig @@ -8,6 +8,22 @@ menuconfig SND_PCI Support for sound devices connected via the PCI bus. if SND_PCI +config SND_INTEL_SST + bool "Moorestown SST (LPE) Driver" + depends on X86 && LNW_IPC + default y + help + Say Y here to include support for the Moorestown SST DSP driver + On other PC platforms if you are unsure answer 'N' + +config SND_INTELMID + bool "Moorestown sound card driver" + select SND_JACK + depends on SND_INTEL_SST && SPI_MRST + default y + help + Say Y here to include support for the Moorestown sound driver. + On other PC platforms if you are unsure answer 'N' config SND_AD1889 tristate "Analog Devices AD1889" Index: linux-2.6.33/sound/pci/Makefile =================================================================== --- linux-2.6.33.orig/sound/pci/Makefile +++ linux-2.6.33/sound/pci/Makefile @@ -77,4 +77,5 @@ obj-$(CONFIG_SND) += \ rme9652/ \ trident/ \ ymfpci/ \ - vx222/ + vx222/ \ + sst/ Index: linux-2.6.33/sound/pci/sst/Makefile =================================================================== --- /dev/null +++ linux-2.6.33/sound/pci/sst/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for Intel MID Audio drivers +# +EXTRA_CFLAGS=-g -DCONFIG_MRST_RAR_HANDLER +snd-intel-sst-objs := intel_sst.o intel_sst_ipc.o intel_sst_stream.o intel_sst_interface.o intel_sst_dsp.o intel_sst_pvt.o +snd-intelmid-objs := intelmid.o intelmid_v0_control.o intelmid_v1_control.o intelmid_v2_control.o intelmid_ctrl.o intelmid_pvt.o +# Toplevel Module Dependency +obj-$(CONFIG_SND_INTEL_SST) += snd-intel-sst.o +obj-$(CONFIG_SND_INTELMID) += snd-intelmid.o
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor