diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
| commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
| tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/staging/intel_sst | |
| parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) | |
Diffstat (limited to 'drivers/staging/intel_sst')
25 files changed, 14475 insertions, 0 deletions
diff --git a/drivers/staging/intel_sst/Kconfig b/drivers/staging/intel_sst/Kconfig new file mode 100644 index 00000000000..82391077b38 --- /dev/null +++ b/drivers/staging/intel_sst/Kconfig | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | config SND_INTEL_SST | ||
| 2 | tristate "Intel SST (LPE) Driver" | ||
| 3 | depends on X86 && INTEL_SCU_IPC | ||
| 4 | default n | ||
| 5 | help | ||
| 6 | Say Y here to include support for the Intel(R) MID SST DSP driver | ||
| 7 | On other PC platforms if you are unsure answer 'N' | ||
| 8 | |||
| 9 | config SND_INTELMID | ||
| 10 | tristate "Intel MID sound card driver" | ||
| 11 | depends on SOUND && SND | ||
| 12 | select SND_PCM | ||
| 13 | select SND_SEQUENCER | ||
| 14 | select SND_JACK | ||
| 15 | depends on SND_INTEL_SST | ||
| 16 | default n | ||
| 17 | help | ||
| 18 | Say Y here to include support for the Intel(R) MID sound card driver | ||
| 19 | On other PC platforms if you are unsure answer 'N' | ||
diff --git a/drivers/staging/intel_sst/Makefile b/drivers/staging/intel_sst/Makefile new file mode 100644 index 00000000000..9eb7c158bb6 --- /dev/null +++ b/drivers/staging/intel_sst/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # | ||
| 2 | # Makefile for Intel MID Audio drivers | ||
| 3 | # | ||
| 4 | snd-intel-sst-y := intel_sst.o intel_sst_ipc.o intel_sst_stream.o intel_sst_drv_interface.o intel_sst_dsp.o intel_sst_pvt.o intel_sst_stream_encoded.o intel_sst_app_interface.o | ||
| 5 | snd-intelmid-y := intelmid.o intelmid_msic_control.o intelmid_ctrl.o intelmid_pvt.o intelmid_v0_control.o intelmid_v1_control.o intelmid_v2_control.o | ||
| 6 | obj-$(CONFIG_SND_INTEL_SST) += snd-intel-sst.o | ||
| 7 | obj-$(CONFIG_SND_INTELMID) += snd-intelmid.o | ||
diff --git a/drivers/staging/intel_sst/TODO b/drivers/staging/intel_sst/TODO new file mode 100644 index 00000000000..c733d701109 --- /dev/null +++ b/drivers/staging/intel_sst/TODO | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | TODO | ||
| 2 | ---- | ||
| 3 | |||
| 4 | Get the memrar driver cleaned up and upstream (dependency blocking SST) | ||
| 5 | Replace long/short press with two virtual buttons | ||
| 6 | Review the printks and kill off any left over ST_ERR: messages | ||
| 7 | Review the misc device ioctls for 32/64bit safety and sanity | ||
| 8 | Review the misc device ioctls for size safety depending on config and decide | ||
| 9 | if space/unused areas should be left | ||
| 10 | What the sound folks turn up on full review | ||
| 11 | Using the ALSA frameworks properly | ||
| 12 | |||
| 13 | |||
diff --git a/drivers/staging/intel_sst/intel_sst.c b/drivers/staging/intel_sst/intel_sst.c new file mode 100644 index 00000000000..d892861346f --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst.c | |||
| @@ -0,0 +1,646 @@ | |||
| 1 | /* | ||
| 2 | * intel_sst.c - Intel SST Driver for audio engine | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * | ||
| 26 | * This driver exposes the audio engine functionalities to the ALSA | ||
| 27 | * and middleware. | ||
| 28 | * | ||
| 29 | * This file contains all init functions | ||
| 30 | */ | ||
| 31 | |||
| 32 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 33 | |||
| 34 | #include <linux/pci.h> | ||
| 35 | #include <linux/fs.h> | ||
| 36 | #include <linux/interrupt.h> | ||
| 37 | #include <linux/firmware.h> | ||
| 38 | #include <linux/miscdevice.h> | ||
| 39 | #include <linux/pm_runtime.h> | ||
| 40 | #include <asm/mrst.h> | ||
| 41 | #include "intel_sst.h" | ||
| 42 | #include "intel_sst_ioctl.h" | ||
| 43 | #include "intel_sst_fw_ipc.h" | ||
| 44 | #include "intel_sst_common.h" | ||
| 45 | |||
| 46 | |||
| 47 | MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); | ||
| 48 | MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); | ||
| 49 | MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>"); | ||
| 50 | MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>"); | ||
| 51 | MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver"); | ||
| 52 | MODULE_LICENSE("GPL v2"); | ||
| 53 | MODULE_VERSION(SST_DRIVER_VERSION); | ||
| 54 | |||
| 55 | struct intel_sst_drv *sst_drv_ctx; | ||
| 56 | static struct mutex drv_ctx_lock; | ||
| 57 | struct class *sst_class; | ||
| 58 | |||
| 59 | /* fops Routines */ | ||
| 60 | static const struct file_operations intel_sst_fops = { | ||
| 61 | .owner = THIS_MODULE, | ||
| 62 | .open = intel_sst_open, | ||
| 63 | .release = intel_sst_release, | ||
| 64 | .read = intel_sst_read, | ||
| 65 | .write = intel_sst_write, | ||
| 66 | .unlocked_ioctl = intel_sst_ioctl, | ||
| 67 | .mmap = intel_sst_mmap, | ||
| 68 | .aio_read = intel_sst_aio_read, | ||
| 69 | .aio_write = intel_sst_aio_write, | ||
| 70 | }; | ||
| 71 | static const struct file_operations intel_sst_fops_cntrl = { | ||
| 72 | .owner = THIS_MODULE, | ||
| 73 | .open = intel_sst_open_cntrl, | ||
| 74 | .release = intel_sst_release_cntrl, | ||
| 75 | .unlocked_ioctl = intel_sst_ioctl, | ||
| 76 | }; | ||
| 77 | |||
| 78 | static struct miscdevice lpe_dev = { | ||
| 79 | .minor = MISC_DYNAMIC_MINOR,/* dynamic allocation */ | ||
| 80 | .name = "intel_sst",/* /dev/intel_sst */ | ||
| 81 | .fops = &intel_sst_fops | ||
| 82 | }; | ||
| 83 | |||
| 84 | |||
| 85 | static struct miscdevice lpe_ctrl = { | ||
| 86 | .minor = MISC_DYNAMIC_MINOR,/* dynamic allocation */ | ||
| 87 | .name = "intel_sst_ctrl",/* /dev/intel_sst_ctrl */ | ||
| 88 | .fops = &intel_sst_fops_cntrl | ||
| 89 | }; | ||
| 90 | |||
| 91 | /** | ||
| 92 | * intel_sst_interrupt - Interrupt service routine for SST | ||
| 93 | * | ||
| 94 | * @irq: irq number of interrupt | ||
| 95 | * @context: pointer to device structre | ||
| 96 | * | ||
| 97 | * This function is called by OS when SST device raises | ||
| 98 | * an interrupt. This will be result of write in IPC register | ||
| 99 | * Source can be busy or done interrupt | ||
| 100 | */ | ||
| 101 | static irqreturn_t intel_sst_interrupt(int irq, void *context) | ||
| 102 | { | ||
| 103 | union interrupt_reg isr; | ||
| 104 | union ipc_header header; | ||
| 105 | union interrupt_reg imr; | ||
| 106 | struct intel_sst_drv *drv = (struct intel_sst_drv *) context; | ||
| 107 | unsigned int size = 0, str_id; | ||
| 108 | struct stream_info *stream ; | ||
| 109 | |||
| 110 | /* Do not handle interrupt in suspended state */ | ||
| 111 | if (drv->sst_state == SST_SUSPENDED) | ||
| 112 | return IRQ_NONE; | ||
| 113 | /* Interrupt arrived, check src */ | ||
| 114 | isr.full = sst_shim_read(drv->shim, SST_ISRX); | ||
| 115 | |||
| 116 | if (isr.part.busy_interrupt) { | ||
| 117 | header.full = sst_shim_read(drv->shim, SST_IPCD); | ||
| 118 | if (header.part.msg_id == IPC_SST_PERIOD_ELAPSED) { | ||
| 119 | sst_clear_interrupt(); | ||
| 120 | str_id = header.part.str_id; | ||
| 121 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 122 | if (stream->period_elapsed) | ||
| 123 | stream->period_elapsed(stream->pcm_substream); | ||
| 124 | return IRQ_HANDLED; | ||
| 125 | } | ||
| 126 | if (header.part.large) | ||
| 127 | size = header.part.data; | ||
| 128 | if (header.part.msg_id & REPLY_MSG) { | ||
| 129 | sst_drv_ctx->ipc_process_msg.header = header; | ||
| 130 | memcpy_fromio(sst_drv_ctx->ipc_process_msg.mailbox, | ||
| 131 | drv->mailbox + SST_MAILBOX_RCV, size); | ||
| 132 | queue_work(sst_drv_ctx->process_msg_wq, | ||
| 133 | &sst_drv_ctx->ipc_process_msg.wq); | ||
| 134 | } else { | ||
| 135 | sst_drv_ctx->ipc_process_reply.header = header; | ||
| 136 | memcpy_fromio(sst_drv_ctx->ipc_process_reply.mailbox, | ||
| 137 | drv->mailbox + SST_MAILBOX_RCV, size); | ||
| 138 | queue_work(sst_drv_ctx->process_reply_wq, | ||
| 139 | &sst_drv_ctx->ipc_process_reply.wq); | ||
| 140 | } | ||
| 141 | /* mask busy inetrrupt */ | ||
| 142 | imr.full = sst_shim_read(drv->shim, SST_IMRX); | ||
| 143 | imr.part.busy_interrupt = 1; | ||
| 144 | sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); | ||
| 145 | return IRQ_HANDLED; | ||
| 146 | } else if (isr.part.done_interrupt) { | ||
| 147 | /* Clear done bit */ | ||
| 148 | header.full = sst_shim_read(drv->shim, SST_IPCX); | ||
| 149 | header.part.done = 0; | ||
| 150 | sst_shim_write(sst_drv_ctx->shim, SST_IPCX, header.full); | ||
| 151 | /* write 1 to clear status register */; | ||
| 152 | isr.part.done_interrupt = 1; | ||
| 153 | /* dummy register for shim workaround */ | ||
| 154 | sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full); | ||
| 155 | queue_work(sst_drv_ctx->post_msg_wq, | ||
| 156 | &sst_drv_ctx->ipc_post_msg.wq); | ||
| 157 | return IRQ_HANDLED; | ||
| 158 | } else | ||
| 159 | return IRQ_NONE; | ||
| 160 | |||
| 161 | } | ||
| 162 | |||
| 163 | |||
| 164 | /* | ||
| 165 | * intel_sst_probe - PCI probe function | ||
| 166 | * | ||
| 167 | * @pci: PCI device structure | ||
| 168 | * @pci_id: PCI device ID structure | ||
| 169 | * | ||
| 170 | * This function is called by OS when a device is found | ||
| 171 | * This enables the device, interrupt etc | ||
| 172 | */ | ||
| 173 | static int __devinit intel_sst_probe(struct pci_dev *pci, | ||
| 174 | const struct pci_device_id *pci_id) | ||
| 175 | { | ||
| 176 | int i, ret = 0; | ||
| 177 | |||
| 178 | pr_debug("Probe for DID %x\n", pci->device); | ||
| 179 | mutex_lock(&drv_ctx_lock); | ||
| 180 | if (sst_drv_ctx) { | ||
| 181 | pr_err("Only one sst handle is supported\n"); | ||
| 182 | mutex_unlock(&drv_ctx_lock); | ||
| 183 | return -EBUSY; | ||
| 184 | } | ||
| 185 | |||
| 186 | sst_drv_ctx = kzalloc(sizeof(*sst_drv_ctx), GFP_KERNEL); | ||
| 187 | if (!sst_drv_ctx) { | ||
| 188 | pr_err("malloc fail\n"); | ||
| 189 | mutex_unlock(&drv_ctx_lock); | ||
| 190 | return -ENOMEM; | ||
| 191 | } | ||
| 192 | mutex_unlock(&drv_ctx_lock); | ||
| 193 | |||
| 194 | sst_drv_ctx->pci_id = pci->device; | ||
| 195 | |||
| 196 | mutex_init(&sst_drv_ctx->stream_lock); | ||
| 197 | mutex_init(&sst_drv_ctx->sst_lock); | ||
| 198 | sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; | ||
| 199 | |||
| 200 | sst_drv_ctx->stream_cnt = 0; | ||
| 201 | sst_drv_ctx->encoded_cnt = 0; | ||
| 202 | sst_drv_ctx->am_cnt = 0; | ||
| 203 | sst_drv_ctx->pb_streams = 0; | ||
| 204 | sst_drv_ctx->cp_streams = 0; | ||
| 205 | sst_drv_ctx->unique_id = 0; | ||
| 206 | sst_drv_ctx->pmic_port_instance = SST_DEFAULT_PMIC_PORT; | ||
| 207 | |||
| 208 | INIT_LIST_HEAD(&sst_drv_ctx->ipc_dispatch_list); | ||
| 209 | INIT_WORK(&sst_drv_ctx->ipc_post_msg.wq, sst_post_message); | ||
| 210 | INIT_WORK(&sst_drv_ctx->ipc_process_msg.wq, sst_process_message); | ||
| 211 | INIT_WORK(&sst_drv_ctx->ipc_process_reply.wq, sst_process_reply); | ||
| 212 | INIT_WORK(&sst_drv_ctx->mad_ops.wq, sst_process_mad_ops); | ||
| 213 | init_waitqueue_head(&sst_drv_ctx->wait_queue); | ||
| 214 | |||
| 215 | sst_drv_ctx->mad_wq = create_workqueue("sst_mad_wq"); | ||
| 216 | if (!sst_drv_ctx->mad_wq) | ||
| 217 | goto do_free_drv_ctx; | ||
| 218 | sst_drv_ctx->post_msg_wq = create_workqueue("sst_post_msg_wq"); | ||
| 219 | if (!sst_drv_ctx->post_msg_wq) | ||
| 220 | goto free_mad_wq; | ||
| 221 | sst_drv_ctx->process_msg_wq = create_workqueue("sst_process_msg_wqq"); | ||
| 222 | if (!sst_drv_ctx->process_msg_wq) | ||
| 223 | goto free_post_msg_wq; | ||
| 224 | sst_drv_ctx->process_reply_wq = create_workqueue("sst_proces_reply_wq"); | ||
| 225 | if (!sst_drv_ctx->process_reply_wq) | ||
| 226 | goto free_process_msg_wq; | ||
| 227 | |||
| 228 | for (i = 0; i < MAX_ACTIVE_STREAM; i++) { | ||
| 229 | sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; | ||
| 230 | sst_drv_ctx->alloc_block[i].ops_block.condition = false; | ||
| 231 | } | ||
| 232 | spin_lock_init(&sst_drv_ctx->list_spin_lock); | ||
| 233 | |||
| 234 | sst_drv_ctx->max_streams = pci_id->driver_data; | ||
| 235 | pr_debug("Got drv data max stream %d\n", | ||
| 236 | sst_drv_ctx->max_streams); | ||
| 237 | for (i = 1; i <= sst_drv_ctx->max_streams; i++) { | ||
| 238 | struct stream_info *stream = &sst_drv_ctx->streams[i]; | ||
| 239 | INIT_LIST_HEAD(&stream->bufs); | ||
| 240 | mutex_init(&stream->lock); | ||
| 241 | spin_lock_init(&stream->pcm_lock); | ||
| 242 | } | ||
| 243 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { | ||
| 244 | sst_drv_ctx->mmap_mem = NULL; | ||
| 245 | sst_drv_ctx->mmap_len = SST_MMAP_PAGES * PAGE_SIZE; | ||
| 246 | while (sst_drv_ctx->mmap_len > 0) { | ||
| 247 | sst_drv_ctx->mmap_mem = | ||
| 248 | kzalloc(sst_drv_ctx->mmap_len, GFP_KERNEL); | ||
| 249 | if (sst_drv_ctx->mmap_mem) { | ||
| 250 | pr_debug("Got memory %p size 0x%x\n", | ||
| 251 | sst_drv_ctx->mmap_mem, | ||
| 252 | sst_drv_ctx->mmap_len); | ||
| 253 | break; | ||
| 254 | } | ||
| 255 | if (sst_drv_ctx->mmap_len < (SST_MMAP_STEP*PAGE_SIZE)) { | ||
| 256 | pr_err("mem alloc fail...abort!!\n"); | ||
| 257 | ret = -ENOMEM; | ||
| 258 | goto free_process_reply_wq; | ||
| 259 | } | ||
| 260 | sst_drv_ctx->mmap_len -= (SST_MMAP_STEP * PAGE_SIZE); | ||
| 261 | pr_debug("mem alloc failed...trying %d\n", | ||
| 262 | sst_drv_ctx->mmap_len); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | /* Init the device */ | ||
| 267 | ret = pci_enable_device(pci); | ||
| 268 | if (ret) { | ||
| 269 | pr_err("device can't be enabled\n"); | ||
| 270 | goto do_free_mem; | ||
| 271 | } | ||
| 272 | sst_drv_ctx->pci = pci_dev_get(pci); | ||
| 273 | ret = pci_request_regions(pci, SST_DRV_NAME); | ||
| 274 | if (ret) | ||
| 275 | goto do_disable_device; | ||
| 276 | /* map registers */ | ||
| 277 | /* SST Shim */ | ||
| 278 | sst_drv_ctx->shim_phy_add = pci_resource_start(pci, 1); | ||
| 279 | sst_drv_ctx->shim = pci_ioremap_bar(pci, 1); | ||
| 280 | if (!sst_drv_ctx->shim) | ||
| 281 | goto do_release_regions; | ||
| 282 | pr_debug("SST Shim Ptr %p\n", sst_drv_ctx->shim); | ||
| 283 | |||
| 284 | /* Shared SRAM */ | ||
| 285 | sst_drv_ctx->mailbox = pci_ioremap_bar(pci, 2); | ||
| 286 | if (!sst_drv_ctx->mailbox) | ||
| 287 | goto do_unmap_shim; | ||
| 288 | pr_debug("SRAM Ptr %p\n", sst_drv_ctx->mailbox); | ||
| 289 | |||
| 290 | /* IRAM */ | ||
| 291 | sst_drv_ctx->iram = pci_ioremap_bar(pci, 3); | ||
| 292 | if (!sst_drv_ctx->iram) | ||
| 293 | goto do_unmap_sram; | ||
| 294 | pr_debug("IRAM Ptr %p\n", sst_drv_ctx->iram); | ||
| 295 | |||
| 296 | /* DRAM */ | ||
| 297 | sst_drv_ctx->dram = pci_ioremap_bar(pci, 4); | ||
| 298 | if (!sst_drv_ctx->dram) | ||
| 299 | goto do_unmap_iram; | ||
| 300 | pr_debug("DRAM Ptr %p\n", sst_drv_ctx->dram); | ||
| 301 | |||
| 302 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 303 | sst_drv_ctx->sst_state = SST_UN_INIT; | ||
| 304 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 305 | /* Register the ISR */ | ||
| 306 | ret = request_irq(pci->irq, intel_sst_interrupt, | ||
| 307 | IRQF_SHARED, SST_DRV_NAME, sst_drv_ctx); | ||
| 308 | if (ret) | ||
| 309 | goto do_unmap_dram; | ||
| 310 | pr_debug("Registered IRQ 0x%x\n", pci->irq); | ||
| 311 | |||
| 312 | /*Register LPE Control as misc driver*/ | ||
| 313 | ret = misc_register(&lpe_ctrl); | ||
| 314 | if (ret) { | ||
| 315 | pr_err("couldn't register control device\n"); | ||
| 316 | goto do_free_irq; | ||
| 317 | } | ||
| 318 | |||
| 319 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { | ||
| 320 | ret = misc_register(&lpe_dev); | ||
| 321 | if (ret) { | ||
| 322 | pr_err("couldn't register LPE device\n"); | ||
| 323 | goto do_free_misc; | ||
| 324 | } | ||
| 325 | } else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) { | ||
| 326 | u32 csr; | ||
| 327 | |||
| 328 | /*allocate mem for fw context save during suspend*/ | ||
| 329 | sst_drv_ctx->fw_cntx = kzalloc(FW_CONTEXT_MEM, GFP_KERNEL); | ||
| 330 | if (!sst_drv_ctx->fw_cntx) { | ||
| 331 | ret = -ENOMEM; | ||
| 332 | goto do_free_misc; | ||
| 333 | } | ||
| 334 | /*setting zero as that is valid mem to restore*/ | ||
| 335 | sst_drv_ctx->fw_cntx_size = 0; | ||
| 336 | |||
| 337 | /*set lpe start clock and ram size*/ | ||
| 338 | csr = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 339 | csr |= 0x30060; /*remove the clock ratio after fw fix*/ | ||
| 340 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr); | ||
| 341 | } | ||
| 342 | sst_drv_ctx->lpe_stalled = 0; | ||
| 343 | pci_set_drvdata(pci, sst_drv_ctx); | ||
| 344 | pm_runtime_allow(&pci->dev); | ||
| 345 | pm_runtime_put_noidle(&pci->dev); | ||
| 346 | pr_debug("...successfully done!!!\n"); | ||
| 347 | return ret; | ||
| 348 | |||
| 349 | do_free_misc: | ||
| 350 | misc_deregister(&lpe_ctrl); | ||
| 351 | do_free_irq: | ||
| 352 | free_irq(pci->irq, sst_drv_ctx); | ||
| 353 | do_unmap_dram: | ||
| 354 | iounmap(sst_drv_ctx->dram); | ||
| 355 | do_unmap_iram: | ||
| 356 | iounmap(sst_drv_ctx->iram); | ||
| 357 | do_unmap_sram: | ||
| 358 | iounmap(sst_drv_ctx->mailbox); | ||
| 359 | do_unmap_shim: | ||
| 360 | iounmap(sst_drv_ctx->shim); | ||
| 361 | do_release_regions: | ||
| 362 | pci_release_regions(pci); | ||
| 363 | do_disable_device: | ||
| 364 | pci_disable_device(pci); | ||
| 365 | do_free_mem: | ||
| 366 | kfree(sst_drv_ctx->mmap_mem); | ||
| 367 | free_process_reply_wq: | ||
| 368 | destroy_workqueue(sst_drv_ctx->process_reply_wq); | ||
| 369 | free_process_msg_wq: | ||
| 370 | destroy_workqueue(sst_drv_ctx->process_msg_wq); | ||
| 371 | free_post_msg_wq: | ||
| 372 | destroy_workqueue(sst_drv_ctx->post_msg_wq); | ||
| 373 | free_mad_wq: | ||
| 374 | destroy_workqueue(sst_drv_ctx->mad_wq); | ||
| 375 | do_free_drv_ctx: | ||
| 376 | kfree(sst_drv_ctx); | ||
| 377 | sst_drv_ctx = NULL; | ||
| 378 | pr_err("Probe failed with %d\n", ret); | ||
| 379 | return ret; | ||
| 380 | } | ||
| 381 | |||
| 382 | /** | ||
| 383 | * intel_sst_remove - PCI remove function | ||
| 384 | * | ||
| 385 | * @pci: PCI device structure | ||
| 386 | * | ||
| 387 | * This function is called by OS when a device is unloaded | ||
| 388 | * This frees the interrupt etc | ||
| 389 | */ | ||
| 390 | static void __devexit intel_sst_remove(struct pci_dev *pci) | ||
| 391 | { | ||
| 392 | pm_runtime_get_noresume(&pci->dev); | ||
| 393 | pm_runtime_forbid(&pci->dev); | ||
| 394 | pci_dev_put(sst_drv_ctx->pci); | ||
| 395 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 396 | sst_drv_ctx->sst_state = SST_UN_INIT; | ||
| 397 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 398 | misc_deregister(&lpe_ctrl); | ||
| 399 | free_irq(pci->irq, sst_drv_ctx); | ||
| 400 | iounmap(sst_drv_ctx->dram); | ||
| 401 | iounmap(sst_drv_ctx->iram); | ||
| 402 | iounmap(sst_drv_ctx->mailbox); | ||
| 403 | iounmap(sst_drv_ctx->shim); | ||
| 404 | sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; | ||
| 405 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { | ||
| 406 | misc_deregister(&lpe_dev); | ||
| 407 | kfree(sst_drv_ctx->mmap_mem); | ||
| 408 | } else | ||
| 409 | kfree(sst_drv_ctx->fw_cntx); | ||
| 410 | flush_scheduled_work(); | ||
| 411 | destroy_workqueue(sst_drv_ctx->process_reply_wq); | ||
| 412 | destroy_workqueue(sst_drv_ctx->process_msg_wq); | ||
| 413 | destroy_workqueue(sst_drv_ctx->post_msg_wq); | ||
| 414 | destroy_workqueue(sst_drv_ctx->mad_wq); | ||
| 415 | kfree(pci_get_drvdata(pci)); | ||
| 416 | sst_drv_ctx = NULL; | ||
| 417 | pci_release_regions(pci); | ||
| 418 | pci_disable_device(pci); | ||
| 419 | pci_set_drvdata(pci, NULL); | ||
| 420 | } | ||
| 421 | |||
| 422 | void sst_save_dsp_context(void) | ||
| 423 | { | ||
| 424 | struct snd_sst_ctxt_params fw_context; | ||
| 425 | unsigned int pvt_id, i; | ||
| 426 | struct ipc_post *msg = NULL; | ||
| 427 | |||
| 428 | /*check cpu type*/ | ||
| 429 | if (sst_drv_ctx->pci_id != SST_MFLD_PCI_ID) | ||
| 430 | return; | ||
| 431 | /*not supported for rest*/ | ||
| 432 | if (sst_drv_ctx->sst_state != SST_FW_RUNNING) { | ||
| 433 | pr_debug("fw not running no context save ...\n"); | ||
| 434 | return; | ||
| 435 | } | ||
| 436 | |||
| 437 | /*send msg to fw*/ | ||
| 438 | if (sst_create_large_msg(&msg)) | ||
| 439 | return; | ||
| 440 | pvt_id = sst_assign_pvt_id(sst_drv_ctx); | ||
| 441 | i = sst_get_block_stream(sst_drv_ctx); | ||
| 442 | sst_drv_ctx->alloc_block[i].sst_id = pvt_id; | ||
| 443 | sst_fill_header(&msg->header, IPC_IA_GET_FW_CTXT, 1, pvt_id); | ||
| 444 | msg->header.part.data = sizeof(fw_context) + sizeof(u32); | ||
| 445 | fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx); | ||
| 446 | fw_context.size = FW_CONTEXT_MEM; | ||
| 447 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 448 | memcpy(msg->mailbox_data + sizeof(u32), | ||
| 449 | &fw_context, sizeof(fw_context)); | ||
| 450 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 451 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 452 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 453 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 454 | /*wait for reply*/ | ||
| 455 | if (sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i])) | ||
| 456 | pr_debug("err fw context save timeout ...\n"); | ||
| 457 | sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; | ||
| 458 | pr_debug("fw context saved ...\n"); | ||
| 459 | return; | ||
| 460 | } | ||
| 461 | |||
| 462 | /* Power Management */ | ||
| 463 | /* | ||
| 464 | * intel_sst_suspend - PCI suspend function | ||
| 465 | * | ||
| 466 | * @pci: PCI device structure | ||
| 467 | * @state: PM message | ||
| 468 | * | ||
| 469 | * This function is called by OS when a power event occurs | ||
| 470 | */ | ||
| 471 | int intel_sst_suspend(struct pci_dev *pci, pm_message_t state) | ||
| 472 | { | ||
| 473 | union config_status_reg csr; | ||
| 474 | |||
| 475 | pr_debug("intel_sst_suspend called\n"); | ||
| 476 | |||
| 477 | if (sst_drv_ctx->stream_cnt) { | ||
| 478 | pr_err("active streams,not able to suspend\n"); | ||
| 479 | return -EBUSY; | ||
| 480 | } | ||
| 481 | /*save fw context*/ | ||
| 482 | sst_save_dsp_context(); | ||
| 483 | /*Assert RESET on LPE Processor*/ | ||
| 484 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 485 | csr.full = csr.full | 0x2; | ||
| 486 | /* Move the SST state to Suspended */ | ||
| 487 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 488 | sst_drv_ctx->sst_state = SST_SUSPENDED; | ||
| 489 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 490 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 491 | pci_set_drvdata(pci, sst_drv_ctx); | ||
| 492 | pci_save_state(pci); | ||
| 493 | pci_disable_device(pci); | ||
| 494 | pci_set_power_state(pci, PCI_D3hot); | ||
| 495 | return 0; | ||
| 496 | } | ||
| 497 | |||
| 498 | /** | ||
| 499 | * intel_sst_resume - PCI resume function | ||
| 500 | * | ||
| 501 | * @pci: PCI device structure | ||
| 502 | * | ||
| 503 | * This function is called by OS when a power event occurs | ||
| 504 | */ | ||
| 505 | int intel_sst_resume(struct pci_dev *pci) | ||
| 506 | { | ||
| 507 | int ret = 0; | ||
| 508 | |||
| 509 | pr_debug("intel_sst_resume called\n"); | ||
| 510 | if (sst_drv_ctx->sst_state != SST_SUSPENDED) { | ||
| 511 | pr_err("SST is not in suspended state\n"); | ||
| 512 | return 0; | ||
| 513 | } | ||
| 514 | sst_drv_ctx = pci_get_drvdata(pci); | ||
| 515 | pci_set_power_state(pci, PCI_D0); | ||
| 516 | pci_restore_state(pci); | ||
| 517 | ret = pci_enable_device(pci); | ||
| 518 | if (ret) | ||
| 519 | pr_err("device can't be enabled\n"); | ||
| 520 | |||
| 521 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 522 | sst_drv_ctx->sst_state = SST_UN_INIT; | ||
| 523 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 524 | return 0; | ||
| 525 | } | ||
| 526 | |||
| 527 | /* The runtime_suspend/resume is pretty much similar to the legacy suspend/resume with the noted exception below: | ||
| 528 | * The PCI core takes care of taking the system through D3hot and restoring it back to D0 and so there is | ||
| 529 | * no need to duplicate that here. | ||
| 530 | */ | ||
| 531 | static int intel_sst_runtime_suspend(struct device *dev) | ||
| 532 | { | ||
| 533 | union config_status_reg csr; | ||
| 534 | |||
| 535 | pr_debug("intel_sst_runtime_suspend called\n"); | ||
| 536 | if (sst_drv_ctx->stream_cnt) { | ||
| 537 | pr_err("active streams,not able to suspend\n"); | ||
| 538 | return -EBUSY; | ||
| 539 | } | ||
| 540 | /*save fw context*/ | ||
| 541 | sst_save_dsp_context(); | ||
| 542 | /*Assert RESET on LPE Processor*/ | ||
| 543 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 544 | csr.full = csr.full | 0x2; | ||
| 545 | /* Move the SST state to Suspended */ | ||
| 546 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 547 | sst_drv_ctx->sst_state = SST_SUSPENDED; | ||
| 548 | |||
| 549 | /* Only needed by Medfield */ | ||
| 550 | if (sst_drv_ctx->pci_id != SST_MRST_PCI_ID) | ||
| 551 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 552 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 553 | return 0; | ||
| 554 | } | ||
| 555 | |||
| 556 | static int intel_sst_runtime_resume(struct device *dev) | ||
| 557 | { | ||
| 558 | |||
| 559 | pr_debug("intel_sst_runtime_resume called\n"); | ||
| 560 | if (sst_drv_ctx->sst_state != SST_SUSPENDED) { | ||
| 561 | pr_err("SST is not in suspended state\n"); | ||
| 562 | return 0; | ||
| 563 | } | ||
| 564 | |||
| 565 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 566 | sst_drv_ctx->sst_state = SST_UN_INIT; | ||
| 567 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 568 | return 0; | ||
| 569 | } | ||
| 570 | |||
| 571 | static int intel_sst_runtime_idle(struct device *dev) | ||
| 572 | { | ||
| 573 | pr_debug("runtime_idle called\n"); | ||
| 574 | if (sst_drv_ctx->stream_cnt == 0 && sst_drv_ctx->am_cnt == 0) | ||
| 575 | pm_schedule_suspend(dev, SST_SUSPEND_DELAY); | ||
| 576 | return -EBUSY; | ||
| 577 | } | ||
| 578 | |||
| 579 | static const struct dev_pm_ops intel_sst_pm = { | ||
| 580 | .runtime_suspend = intel_sst_runtime_suspend, | ||
| 581 | .runtime_resume = intel_sst_runtime_resume, | ||
| 582 | .runtime_idle = intel_sst_runtime_idle, | ||
| 583 | }; | ||
| 584 | |||
| 585 | /* PCI Routines */ | ||
| 586 | static struct pci_device_id intel_sst_ids[] = { | ||
| 587 | { PCI_VDEVICE(INTEL, SST_MRST_PCI_ID), 3}, | ||
| 588 | { PCI_VDEVICE(INTEL, SST_MFLD_PCI_ID), 6}, | ||
| 589 | { 0, } | ||
| 590 | }; | ||
| 591 | MODULE_DEVICE_TABLE(pci, intel_sst_ids); | ||
| 592 | |||
| 593 | static struct pci_driver driver = { | ||
| 594 | .name = SST_DRV_NAME, | ||
| 595 | .id_table = intel_sst_ids, | ||
| 596 | .probe = intel_sst_probe, | ||
| 597 | .remove = __devexit_p(intel_sst_remove), | ||
| 598 | #ifdef CONFIG_PM | ||
| 599 | .suspend = intel_sst_suspend, | ||
| 600 | .resume = intel_sst_resume, | ||
| 601 | .driver = { | ||
| 602 | .pm = &intel_sst_pm, | ||
| 603 | }, | ||
| 604 | #endif | ||
| 605 | }; | ||
| 606 | |||
| 607 | /** | ||
| 608 | * intel_sst_init - Module init function | ||
| 609 | * | ||
| 610 | * Registers with PCI | ||
| 611 | * Registers with /dev | ||
| 612 | * Init all data strutures | ||
| 613 | */ | ||
| 614 | static int __init intel_sst_init(void) | ||
| 615 | { | ||
| 616 | /* Init all variables, data structure etc....*/ | ||
| 617 | int ret = 0; | ||
| 618 | pr_debug("INFO: ******** SST DRIVER loading.. Ver: %s\n", | ||
| 619 | SST_DRIVER_VERSION); | ||
| 620 | |||
| 621 | mutex_init(&drv_ctx_lock); | ||
| 622 | /* Register with PCI */ | ||
| 623 | ret = pci_register_driver(&driver); | ||
| 624 | if (ret) | ||
| 625 | pr_err("PCI register failed\n"); | ||
| 626 | return ret; | ||
| 627 | } | ||
| 628 | |||
| 629 | /** | ||
| 630 | * intel_sst_exit - Module exit function | ||
| 631 | * | ||
| 632 | * Unregisters with PCI | ||
| 633 | * Unregisters with /dev | ||
| 634 | * Frees all data strutures | ||
| 635 | */ | ||
| 636 | static void __exit intel_sst_exit(void) | ||
| 637 | { | ||
| 638 | pci_unregister_driver(&driver); | ||
| 639 | |||
| 640 | pr_debug("driver unloaded\n"); | ||
| 641 | sst_drv_ctx = NULL; | ||
| 642 | return; | ||
| 643 | } | ||
| 644 | |||
| 645 | module_init(intel_sst_init); | ||
| 646 | module_exit(intel_sst_exit); | ||
diff --git a/drivers/staging/intel_sst/intel_sst.h b/drivers/staging/intel_sst/intel_sst.h new file mode 100644 index 00000000000..4ad2829105a --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst.h | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | #ifndef __INTEL_SST_H__ | ||
| 2 | #define __INTEL_SST_H__ | ||
| 3 | /* | ||
| 4 | * intel_sst.h - Intel SST Driver for audio engine | ||
| 5 | * | ||
| 6 | * Copyright (C) 2008-10 Intel Corporation | ||
| 7 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 8 | * Harsha Priya <priya.harsha@intel.com> | ||
| 9 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 10 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 11 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License as published by | ||
| 15 | * the Free Software Foundation; version 2 of the License. | ||
| 16 | * | ||
| 17 | * This program is distributed in the hope that it will be useful, but | ||
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 20 | * General Public License for more details. | ||
| 21 | * | ||
| 22 | * You should have received a copy of the GNU General Public License along | ||
| 23 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 24 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 25 | * | ||
| 26 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 27 | * | ||
| 28 | * This driver exposes the audio engine functionalities to the ALSA | ||
| 29 | * and middleware. | ||
| 30 | * This file is shared between the SST and MAD drivers | ||
| 31 | */ | ||
| 32 | #include "intel_sst_ioctl.h" | ||
| 33 | #include <sound/jack.h> | ||
| 34 | |||
| 35 | #define SST_CARD_NAMES "intel_mid_card" | ||
| 36 | |||
| 37 | #define MFLD_MAX_HW_CH 4 | ||
| 38 | /* control list Pmic & Lpe */ | ||
| 39 | /* Input controls */ | ||
| 40 | enum port_status { | ||
| 41 | ACTIVATE = 1, | ||
| 42 | DEACTIVATE, | ||
| 43 | }; | ||
| 44 | |||
| 45 | /* Card states */ | ||
| 46 | enum sst_card_states { | ||
| 47 | SND_CARD_UN_INIT = 0, | ||
| 48 | SND_CARD_INIT_DONE, | ||
| 49 | }; | ||
| 50 | |||
| 51 | enum sst_controls { | ||
| 52 | SST_SND_ALLOC = 0x1000, | ||
| 53 | SST_SND_PAUSE = 0x1001, | ||
| 54 | SST_SND_RESUME = 0x1002, | ||
| 55 | SST_SND_DROP = 0x1003, | ||
| 56 | SST_SND_FREE = 0x1004, | ||
| 57 | SST_SND_BUFFER_POINTER = 0x1005, | ||
| 58 | SST_SND_STREAM_INIT = 0x1006, | ||
| 59 | SST_SND_START = 0x1007, | ||
| 60 | SST_SND_STREAM_PROCESS = 0x1008, | ||
| 61 | SST_MAX_CONTROLS = 0x1008, | ||
| 62 | SST_CONTROL_BASE = 0x1000, | ||
| 63 | SST_ENABLE_RX_TIME_SLOT = 0x1009, | ||
| 64 | }; | ||
| 65 | |||
| 66 | enum SND_CARDS { | ||
| 67 | SND_FS = 0, | ||
| 68 | SND_MX, | ||
| 69 | SND_NC, | ||
| 70 | SND_MSIC | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct pcm_stream_info { | ||
| 74 | int str_id; | ||
| 75 | void *mad_substream; | ||
| 76 | void (*period_elapsed) (void *mad_substream); | ||
| 77 | unsigned long long buffer_ptr; | ||
| 78 | int sfreq; | ||
| 79 | }; | ||
| 80 | |||
| 81 | struct snd_pmic_ops { | ||
| 82 | int card_status; | ||
| 83 | int master_mute; | ||
| 84 | int num_channel; | ||
| 85 | int input_dev_id; | ||
| 86 | int mute_status; | ||
| 87 | struct mutex lock; | ||
| 88 | int pb_on, pbhs_on; | ||
| 89 | int cap_on; | ||
| 90 | int output_dev_id; | ||
| 91 | int lineout_dev_id, line_out_names_cnt; | ||
| 92 | int prev_lineout_dev_id; | ||
| 93 | bool jack_interrupt_status; | ||
| 94 | int (*set_input_dev) (u8 value); | ||
| 95 | int (*set_output_dev) (u8 value); | ||
| 96 | int (*set_lineout_dev) (u8 value); | ||
| 97 | int (*set_mute) (int dev_id, u8 value); | ||
| 98 | int (*get_mute) (int dev_id, u8 *value); | ||
| 99 | |||
| 100 | int (*set_vol) (int dev_id, int value); | ||
| 101 | int (*get_vol) (int dev_id, int *value); | ||
| 102 | |||
| 103 | int (*init_card) (void); | ||
| 104 | int (*set_pcm_audio_params) | ||
| 105 | (int sfreq, int word_size , int num_channel); | ||
| 106 | int (*set_pcm_voice_params) (void); | ||
| 107 | int (*set_voice_port) (int status); | ||
| 108 | int (*set_audio_port) (int status); | ||
| 109 | |||
| 110 | int (*power_up_pmic_pb) (unsigned int port); | ||
| 111 | int (*power_up_pmic_cp) (unsigned int port); | ||
| 112 | int (*power_down_pmic_pb) (unsigned int device); | ||
| 113 | int (*power_down_pmic_cp) (unsigned int device); | ||
| 114 | int (*power_down_pmic) (void); | ||
| 115 | void (*pmic_irq_cb) (void *cb_data, u8 value); | ||
| 116 | void (*pmic_irq_enable)(void *data); | ||
| 117 | int (*pmic_jack_enable) (void); | ||
| 118 | int (*pmic_get_mic_bias)(void *intelmaddata); | ||
| 119 | int (*pmic_set_headset_state)(int state); | ||
| 120 | |||
| 121 | unsigned int hw_dmic_map[MFLD_MAX_HW_CH]; | ||
| 122 | unsigned int available_dmics; | ||
| 123 | int (*set_hw_dmic_route) (u8 index); | ||
| 124 | |||
| 125 | int gpio_amp; | ||
| 126 | }; | ||
| 127 | |||
| 128 | extern void sst_mad_send_jack_report(struct snd_jack *jack, | ||
| 129 | int buttonpressevent, | ||
| 130 | int status); | ||
| 131 | |||
| 132 | |||
| 133 | int intemad_set_headset_state(int state); | ||
| 134 | int intelmad_get_mic_bias(void); | ||
| 135 | |||
| 136 | struct intel_sst_pcm_control { | ||
| 137 | int (*open) (struct snd_sst_params *str_param); | ||
| 138 | int (*device_control) (int cmd, void *arg); | ||
| 139 | int (*close) (unsigned int str_id); | ||
| 140 | }; | ||
| 141 | struct intel_sst_card_ops { | ||
| 142 | char *module_name; | ||
| 143 | unsigned int vendor_id; | ||
| 144 | struct intel_sst_pcm_control *pcm_control; | ||
| 145 | struct snd_pmic_ops *scard_ops; | ||
| 146 | }; | ||
| 147 | |||
| 148 | /* modified for generic access */ | ||
| 149 | struct sc_reg_access { | ||
| 150 | u16 reg_addr; | ||
| 151 | u8 value; | ||
| 152 | u8 mask; | ||
| 153 | }; | ||
| 154 | enum sc_reg_access_type { | ||
| 155 | PMIC_READ = 0, | ||
| 156 | PMIC_WRITE, | ||
| 157 | PMIC_READ_MODIFY, | ||
| 158 | }; | ||
| 159 | |||
| 160 | int register_sst_card(struct intel_sst_card_ops *card); | ||
| 161 | void unregister_sst_card(struct intel_sst_card_ops *card); | ||
| 162 | #endif /* __INTEL_SST_H__ */ | ||
diff --git a/drivers/staging/intel_sst/intel_sst_app_interface.c b/drivers/staging/intel_sst/intel_sst_app_interface.c new file mode 100644 index 00000000000..93b41a284d8 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_app_interface.c | |||
| @@ -0,0 +1,1460 @@ | |||
| 1 | /* | ||
| 2 | * intel_sst_interface.c - Intel SST Driver for audio engine | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * Jeeja KP <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * This driver exposes the audio engine functionalities to the ALSA | ||
| 26 | * and middleware. | ||
| 27 | * Upper layer interfaces (MAD driver, MMF) to SST driver | ||
| 28 | */ | ||
| 29 | |||
| 30 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 31 | |||
| 32 | #include <linux/pci.h> | ||
| 33 | #include <linux/fs.h> | ||
| 34 | #include <linux/uio.h> | ||
| 35 | #include <linux/aio.h> | ||
| 36 | #include <linux/uaccess.h> | ||
| 37 | #include <linux/firmware.h> | ||
| 38 | #include <linux/pm_runtime.h> | ||
| 39 | #include <linux/ioctl.h> | ||
| 40 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 41 | #include <linux/rar_register.h> | ||
| 42 | #include "../../../drivers/staging/memrar/memrar.h" | ||
| 43 | #endif | ||
| 44 | #include "intel_sst.h" | ||
| 45 | #include "intel_sst_ioctl.h" | ||
| 46 | #include "intel_sst_fw_ipc.h" | ||
| 47 | #include "intel_sst_common.h" | ||
| 48 | |||
| 49 | #define AM_MODULE 1 | ||
| 50 | #define STREAM_MODULE 0 | ||
| 51 | |||
| 52 | |||
| 53 | /** | ||
| 54 | * intel_sst_check_device - checks SST device | ||
| 55 | * | ||
| 56 | * This utility function checks the state of SST device and downlaods FW if | ||
| 57 | * not done, or resumes the device if suspended | ||
| 58 | */ | ||
| 59 | |||
| 60 | static int intel_sst_check_device(void) | ||
| 61 | { | ||
| 62 | int retval = 0; | ||
| 63 | if (sst_drv_ctx->pmic_state != SND_MAD_INIT_DONE) { | ||
| 64 | pr_warn("Sound card not available\n"); | ||
| 65 | return -EIO; | ||
| 66 | } | ||
| 67 | if (sst_drv_ctx->sst_state == SST_SUSPENDED) { | ||
| 68 | pr_debug("Resuming from Suspended state\n"); | ||
| 69 | retval = intel_sst_resume(sst_drv_ctx->pci); | ||
| 70 | if (retval) { | ||
| 71 | pr_debug("Resume Failed= %#x,abort\n", retval); | ||
| 72 | return retval; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | if (sst_drv_ctx->sst_state == SST_UN_INIT) { | ||
| 77 | /* FW is not downloaded */ | ||
| 78 | retval = sst_download_fw(); | ||
| 79 | if (retval) | ||
| 80 | return -ENODEV; | ||
| 81 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { | ||
| 82 | retval = sst_drv_ctx->rx_time_slot_status; | ||
| 83 | if (retval != RX_TIMESLOT_UNINIT | ||
| 84 | && sst_drv_ctx->pmic_vendor != SND_NC) | ||
| 85 | sst_enable_rx_timeslot(retval); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * intel_sst_open - opens a handle to driver | ||
| 93 | * | ||
| 94 | * @i_node: inode structure | ||
| 95 | * @file_ptr:pointer to file | ||
| 96 | * | ||
| 97 | * This function is called by OS when a user space component | ||
| 98 | * tries to get a driver handle. Only one handle at a time | ||
| 99 | * will be allowed | ||
| 100 | */ | ||
| 101 | int intel_sst_open(struct inode *i_node, struct file *file_ptr) | ||
| 102 | { | ||
| 103 | unsigned int retval; | ||
| 104 | |||
| 105 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 106 | pm_runtime_get_sync(&sst_drv_ctx->pci->dev); | ||
| 107 | retval = intel_sst_check_device(); | ||
| 108 | if (retval) { | ||
| 109 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 110 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 111 | return retval; | ||
| 112 | } | ||
| 113 | |||
| 114 | if (sst_drv_ctx->encoded_cnt < MAX_ENC_STREAM) { | ||
| 115 | struct ioctl_pvt_data *data = | ||
| 116 | kzalloc(sizeof(struct ioctl_pvt_data), GFP_KERNEL); | ||
| 117 | if (!data) { | ||
| 118 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 119 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 120 | return -ENOMEM; | ||
| 121 | } | ||
| 122 | |||
| 123 | sst_drv_ctx->encoded_cnt++; | ||
| 124 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 125 | data->pvt_id = sst_assign_pvt_id(sst_drv_ctx); | ||
| 126 | data->str_id = 0; | ||
| 127 | file_ptr->private_data = (void *)data; | ||
| 128 | pr_debug("pvt_id handle = %d!\n", data->pvt_id); | ||
| 129 | } else { | ||
| 130 | retval = -EUSERS; | ||
| 131 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 132 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 133 | } | ||
| 134 | return retval; | ||
| 135 | } | ||
| 136 | |||
| 137 | /** | ||
| 138 | * intel_sst_open_cntrl - opens a handle to driver | ||
| 139 | * | ||
| 140 | * @i_node: inode structure | ||
| 141 | * @file_ptr:pointer to file | ||
| 142 | * | ||
| 143 | * This function is called by OS when a user space component | ||
| 144 | * tries to get a driver handle to /dev/intel_sst_control. | ||
| 145 | * Only one handle at a time will be allowed | ||
| 146 | * This is for control operations only | ||
| 147 | */ | ||
| 148 | int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr) | ||
| 149 | { | ||
| 150 | unsigned int retval; | ||
| 151 | |||
| 152 | /* audio manager open */ | ||
| 153 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 154 | pm_runtime_get_sync(&sst_drv_ctx->pci->dev); | ||
| 155 | retval = intel_sst_check_device(); | ||
| 156 | if (retval) { | ||
| 157 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 158 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 159 | return retval; | ||
| 160 | } | ||
| 161 | |||
| 162 | if (sst_drv_ctx->am_cnt < MAX_AM_HANDLES) { | ||
| 163 | sst_drv_ctx->am_cnt++; | ||
| 164 | pr_debug("AM handle opened...\n"); | ||
| 165 | file_ptr->private_data = NULL; | ||
| 166 | } else { | ||
| 167 | retval = -EACCES; | ||
| 168 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 169 | } | ||
| 170 | |||
| 171 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 172 | return retval; | ||
| 173 | } | ||
| 174 | |||
| 175 | /** | ||
| 176 | * intel_sst_release - releases a handle to driver | ||
| 177 | * | ||
| 178 | * @i_node: inode structure | ||
| 179 | * @file_ptr: pointer to file | ||
| 180 | * | ||
| 181 | * This function is called by OS when a user space component | ||
| 182 | * tries to release a driver handle. | ||
| 183 | */ | ||
| 184 | int intel_sst_release(struct inode *i_node, struct file *file_ptr) | ||
| 185 | { | ||
| 186 | struct ioctl_pvt_data *data = file_ptr->private_data; | ||
| 187 | |||
| 188 | pr_debug("Release called, closing app handle\n"); | ||
| 189 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 190 | sst_drv_ctx->encoded_cnt--; | ||
| 191 | sst_drv_ctx->stream_cnt--; | ||
| 192 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 193 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 194 | free_stream_context(data->str_id); | ||
| 195 | kfree(data); | ||
| 196 | return 0; | ||
| 197 | } | ||
| 198 | |||
| 199 | int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr) | ||
| 200 | { | ||
| 201 | /* audio manager close */ | ||
| 202 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 203 | sst_drv_ctx->am_cnt--; | ||
| 204 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 205 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 206 | pr_debug("AM handle closed\n"); | ||
| 207 | return 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * intel_sst_mmap - mmaps a kernel buffer to user space for copying data | ||
| 212 | * | ||
| 213 | * @vma: vm area structure instance | ||
| 214 | * @file_ptr: pointer to file | ||
| 215 | * | ||
| 216 | * This function is called by OS when a user space component | ||
| 217 | * tries to get mmap memory from driver | ||
| 218 | */ | ||
| 219 | int intel_sst_mmap(struct file *file_ptr, struct vm_area_struct *vma) | ||
| 220 | { | ||
| 221 | int retval, length; | ||
| 222 | struct ioctl_pvt_data *data = | ||
| 223 | (struct ioctl_pvt_data *)file_ptr->private_data; | ||
| 224 | int str_id = data->str_id; | ||
| 225 | void *mem_area; | ||
| 226 | |||
| 227 | retval = sst_validate_strid(str_id); | ||
| 228 | if (retval) | ||
| 229 | return -EINVAL; | ||
| 230 | |||
| 231 | length = vma->vm_end - vma->vm_start; | ||
| 232 | pr_debug("called for stream %d length 0x%x\n", str_id, length); | ||
| 233 | |||
| 234 | if (length > sst_drv_ctx->mmap_len) | ||
| 235 | return -ENOMEM; | ||
| 236 | if (!sst_drv_ctx->mmap_mem) | ||
| 237 | return -EIO; | ||
| 238 | |||
| 239 | /* round it up to the page boundary */ | ||
| 240 | /*mem_area = (void *)((((unsigned long)sst_drv_ctx->mmap_mem) | ||
| 241 | + PAGE_SIZE - 1) & PAGE_MASK);*/ | ||
| 242 | mem_area = (void *) PAGE_ALIGN((unsigned int) sst_drv_ctx->mmap_mem); | ||
| 243 | |||
| 244 | /* map the whole physically contiguous area in one piece */ | ||
| 245 | retval = remap_pfn_range(vma, | ||
| 246 | vma->vm_start, | ||
| 247 | virt_to_phys((void *)mem_area) >> PAGE_SHIFT, | ||
| 248 | length, | ||
| 249 | vma->vm_page_prot); | ||
| 250 | if (retval) | ||
| 251 | sst_drv_ctx->streams[str_id].mmapped = false; | ||
| 252 | else | ||
| 253 | sst_drv_ctx->streams[str_id].mmapped = true; | ||
| 254 | |||
| 255 | pr_debug("mmap ret 0x%x\n", retval); | ||
| 256 | return retval; | ||
| 257 | } | ||
| 258 | |||
| 259 | /* sets mmap data buffers to play/capture*/ | ||
| 260 | static int intel_sst_mmap_play_capture(u32 str_id, | ||
| 261 | struct snd_sst_mmap_buffs *mmap_buf) | ||
| 262 | { | ||
| 263 | struct sst_stream_bufs *bufs; | ||
| 264 | int retval, i; | ||
| 265 | struct stream_info *stream; | ||
| 266 | struct snd_sst_mmap_buff_entry *buf_entry; | ||
| 267 | struct snd_sst_mmap_buff_entry *tmp_buf; | ||
| 268 | |||
| 269 | pr_debug("called for str_id %d\n", str_id); | ||
| 270 | retval = sst_validate_strid(str_id); | ||
| 271 | if (retval) | ||
| 272 | return -EINVAL; | ||
| 273 | |||
| 274 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 275 | if (stream->mmapped != true) | ||
| 276 | return -EIO; | ||
| 277 | |||
| 278 | if (stream->status == STREAM_UN_INIT || | ||
| 279 | stream->status == STREAM_DECODE) { | ||
| 280 | return -EBADRQC; | ||
| 281 | } | ||
| 282 | stream->curr_bytes = 0; | ||
| 283 | stream->cumm_bytes = 0; | ||
| 284 | |||
| 285 | tmp_buf = kcalloc(mmap_buf->entries, sizeof(*tmp_buf), GFP_KERNEL); | ||
| 286 | if (!tmp_buf) | ||
| 287 | return -ENOMEM; | ||
| 288 | if (copy_from_user(tmp_buf, (void __user *)mmap_buf->buff, | ||
| 289 | mmap_buf->entries * sizeof(*tmp_buf))) { | ||
| 290 | retval = -EFAULT; | ||
| 291 | goto out_free; | ||
| 292 | } | ||
| 293 | |||
| 294 | pr_debug("new buffers count %d status %d\n", | ||
| 295 | mmap_buf->entries, stream->status); | ||
| 296 | buf_entry = tmp_buf; | ||
| 297 | for (i = 0; i < mmap_buf->entries; i++) { | ||
| 298 | bufs = kzalloc(sizeof(*bufs), GFP_KERNEL); | ||
| 299 | if (!bufs) { | ||
| 300 | retval = -ENOMEM; | ||
| 301 | goto out_free; | ||
| 302 | } | ||
| 303 | bufs->size = buf_entry->size; | ||
| 304 | bufs->offset = buf_entry->offset; | ||
| 305 | bufs->addr = sst_drv_ctx->mmap_mem; | ||
| 306 | bufs->in_use = false; | ||
| 307 | buf_entry++; | ||
| 308 | /* locking here */ | ||
| 309 | mutex_lock(&stream->lock); | ||
| 310 | list_add_tail(&bufs->node, &stream->bufs); | ||
| 311 | mutex_unlock(&stream->lock); | ||
| 312 | } | ||
| 313 | |||
| 314 | mutex_lock(&stream->lock); | ||
| 315 | stream->data_blk.condition = false; | ||
| 316 | stream->data_blk.ret_code = 0; | ||
| 317 | if (stream->status == STREAM_INIT && | ||
| 318 | stream->prev != STREAM_UN_INIT && | ||
| 319 | stream->need_draining != true) { | ||
| 320 | stream->prev = stream->status; | ||
| 321 | stream->status = STREAM_RUNNING; | ||
| 322 | if (stream->ops == STREAM_OPS_PLAYBACK) { | ||
| 323 | if (sst_play_frame(str_id) < 0) { | ||
| 324 | pr_warn("play frames fail\n"); | ||
| 325 | mutex_unlock(&stream->lock); | ||
| 326 | retval = -EIO; | ||
| 327 | goto out_free; | ||
| 328 | } | ||
| 329 | } else if (stream->ops == STREAM_OPS_CAPTURE) { | ||
| 330 | if (sst_capture_frame(str_id) < 0) { | ||
| 331 | pr_warn("capture frame fail\n"); | ||
| 332 | mutex_unlock(&stream->lock); | ||
| 333 | retval = -EIO; | ||
| 334 | goto out_free; | ||
| 335 | } | ||
| 336 | } | ||
| 337 | } | ||
| 338 | mutex_unlock(&stream->lock); | ||
| 339 | /* Block the call for reply */ | ||
| 340 | if (!list_empty(&stream->bufs)) { | ||
| 341 | stream->data_blk.on = true; | ||
| 342 | retval = sst_wait_interruptible(sst_drv_ctx, | ||
| 343 | &stream->data_blk); | ||
| 344 | } | ||
| 345 | |||
| 346 | if (retval >= 0) | ||
| 347 | retval = stream->cumm_bytes; | ||
| 348 | pr_debug("end of play/rec ioctl bytes = %d!!\n", retval); | ||
| 349 | |||
| 350 | out_free: | ||
| 351 | kfree(tmp_buf); | ||
| 352 | return retval; | ||
| 353 | } | ||
| 354 | |||
| 355 | /*sets user data buffers to play/capture*/ | ||
| 356 | static int intel_sst_play_capture(struct stream_info *stream, int str_id) | ||
| 357 | { | ||
| 358 | int retval; | ||
| 359 | |||
| 360 | stream->data_blk.ret_code = 0; | ||
| 361 | stream->data_blk.on = true; | ||
| 362 | stream->data_blk.condition = false; | ||
| 363 | |||
| 364 | mutex_lock(&stream->lock); | ||
| 365 | if (stream->status == STREAM_INIT && stream->prev != STREAM_UN_INIT) { | ||
| 366 | /* stream is started */ | ||
| 367 | stream->prev = stream->status; | ||
| 368 | stream->status = STREAM_RUNNING; | ||
| 369 | } | ||
| 370 | |||
| 371 | if (stream->status == STREAM_INIT && stream->prev == STREAM_UN_INIT) { | ||
| 372 | /* stream is not started yet */ | ||
| 373 | pr_debug("Stream isn't in started state %d, prev %d\n", | ||
| 374 | stream->status, stream->prev); | ||
| 375 | } else if ((stream->status == STREAM_RUNNING || | ||
| 376 | stream->status == STREAM_PAUSED) && | ||
| 377 | stream->need_draining != true) { | ||
| 378 | /* stream is started */ | ||
| 379 | if (stream->ops == STREAM_OPS_PLAYBACK || | ||
| 380 | stream->ops == STREAM_OPS_PLAYBACK_DRM) { | ||
| 381 | if (sst_play_frame(str_id) < 0) { | ||
| 382 | pr_warn("play frames failed\n"); | ||
| 383 | mutex_unlock(&stream->lock); | ||
| 384 | return -EIO; | ||
| 385 | } | ||
| 386 | } else if (stream->ops == STREAM_OPS_CAPTURE) { | ||
| 387 | if (sst_capture_frame(str_id) < 0) { | ||
| 388 | pr_warn("capture frames failed\n"); | ||
| 389 | mutex_unlock(&stream->lock); | ||
| 390 | return -EIO; | ||
| 391 | } | ||
| 392 | } | ||
| 393 | } else { | ||
| 394 | mutex_unlock(&stream->lock); | ||
| 395 | return -EIO; | ||
| 396 | } | ||
| 397 | mutex_unlock(&stream->lock); | ||
| 398 | /* Block the call for reply */ | ||
| 399 | |||
| 400 | retval = sst_wait_interruptible(sst_drv_ctx, &stream->data_blk); | ||
| 401 | if (retval) { | ||
| 402 | stream->status = STREAM_INIT; | ||
| 403 | pr_debug("wait returned error...\n"); | ||
| 404 | } | ||
| 405 | return retval; | ||
| 406 | } | ||
| 407 | |||
| 408 | /* fills kernel list with buffer addresses for SST DSP driver to process*/ | ||
| 409 | static int snd_sst_fill_kernel_list(struct stream_info *stream, | ||
| 410 | const struct iovec *iovec, unsigned long nr_segs, | ||
| 411 | struct list_head *copy_to_list) | ||
| 412 | { | ||
| 413 | struct sst_stream_bufs *stream_bufs; | ||
| 414 | unsigned long index, mmap_len; | ||
| 415 | unsigned char __user *bufp; | ||
| 416 | unsigned long size, copied_size; | ||
| 417 | int retval = 0, add_to_list = 0; | ||
| 418 | static int sent_offset; | ||
| 419 | static unsigned long sent_index; | ||
| 420 | |||
| 421 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 422 | if (stream->ops == STREAM_OPS_PLAYBACK_DRM) { | ||
| 423 | for (index = stream->sg_index; index < nr_segs; index++) { | ||
| 424 | __u32 rar_handle; | ||
| 425 | struct sst_stream_bufs *stream_bufs = | ||
| 426 | kzalloc(sizeof(*stream_bufs), GFP_KERNEL); | ||
| 427 | |||
| 428 | stream->sg_index = index; | ||
| 429 | if (!stream_bufs) | ||
| 430 | return -ENOMEM; | ||
| 431 | if (copy_from_user((void *) &rar_handle, | ||
| 432 | iovec[index].iov_base, | ||
| 433 | sizeof(__u32))) { | ||
| 434 | kfree(stream_bufs); | ||
| 435 | return -EFAULT; | ||
| 436 | } | ||
| 437 | stream_bufs->addr = (char *)rar_handle; | ||
| 438 | stream_bufs->in_use = false; | ||
| 439 | stream_bufs->size = iovec[0].iov_len; | ||
| 440 | /* locking here */ | ||
| 441 | mutex_lock(&stream->lock); | ||
| 442 | list_add_tail(&stream_bufs->node, &stream->bufs); | ||
| 443 | mutex_unlock(&stream->lock); | ||
| 444 | } | ||
| 445 | stream->sg_index = index; | ||
| 446 | return retval; | ||
| 447 | } | ||
| 448 | #endif | ||
| 449 | stream_bufs = kzalloc(sizeof(*stream_bufs), GFP_KERNEL); | ||
| 450 | if (!stream_bufs) | ||
| 451 | return -ENOMEM; | ||
| 452 | stream_bufs->addr = sst_drv_ctx->mmap_mem; | ||
| 453 | mmap_len = sst_drv_ctx->mmap_len; | ||
| 454 | stream_bufs->addr = sst_drv_ctx->mmap_mem; | ||
| 455 | bufp = stream->cur_ptr; | ||
| 456 | |||
| 457 | copied_size = 0; | ||
| 458 | |||
| 459 | if (!stream->sg_index) | ||
| 460 | sent_index = sent_offset = 0; | ||
| 461 | |||
| 462 | for (index = stream->sg_index; index < nr_segs; index++) { | ||
| 463 | stream->sg_index = index; | ||
| 464 | if (!stream->cur_ptr) | ||
| 465 | bufp = iovec[index].iov_base; | ||
| 466 | |||
| 467 | size = ((unsigned long)iovec[index].iov_base | ||
| 468 | + iovec[index].iov_len) - (unsigned long) bufp; | ||
| 469 | |||
| 470 | if ((copied_size + size) > mmap_len) | ||
| 471 | size = mmap_len - copied_size; | ||
| 472 | |||
| 473 | |||
| 474 | if (stream->ops == STREAM_OPS_PLAYBACK) { | ||
| 475 | if (copy_from_user((void *) | ||
| 476 | (stream_bufs->addr + copied_size), | ||
| 477 | bufp, size)) { | ||
| 478 | /* Clean up the list and return error code */ | ||
| 479 | retval = -EFAULT; | ||
| 480 | break; | ||
| 481 | } | ||
| 482 | } else if (stream->ops == STREAM_OPS_CAPTURE) { | ||
| 483 | struct snd_sst_user_cap_list *entry = | ||
| 484 | kzalloc(sizeof(*entry), GFP_KERNEL); | ||
| 485 | |||
| 486 | if (!entry) { | ||
| 487 | kfree(stream_bufs); | ||
| 488 | return -ENOMEM; | ||
| 489 | } | ||
| 490 | entry->iov_index = index; | ||
| 491 | entry->iov_offset = (unsigned long) bufp - | ||
| 492 | (unsigned long)iovec[index].iov_base; | ||
| 493 | entry->offset = copied_size; | ||
| 494 | entry->size = size; | ||
| 495 | list_add_tail(&entry->node, copy_to_list); | ||
| 496 | } | ||
| 497 | |||
| 498 | stream->cur_ptr = bufp + size; | ||
| 499 | |||
| 500 | if (((unsigned long)iovec[index].iov_base | ||
| 501 | + iovec[index].iov_len) < | ||
| 502 | ((unsigned long)iovec[index].iov_base)) { | ||
| 503 | pr_debug("Buffer overflows\n"); | ||
| 504 | kfree(stream_bufs); | ||
| 505 | return -EINVAL; | ||
| 506 | } | ||
| 507 | |||
| 508 | if (((unsigned long)iovec[index].iov_base | ||
| 509 | + iovec[index].iov_len) == | ||
| 510 | (unsigned long)stream->cur_ptr) { | ||
| 511 | stream->cur_ptr = NULL; | ||
| 512 | stream->sg_index++; | ||
| 513 | } | ||
| 514 | |||
| 515 | copied_size += size; | ||
| 516 | pr_debug("copied_size - %lx\n", copied_size); | ||
| 517 | if ((copied_size >= mmap_len) || | ||
| 518 | (stream->sg_index == nr_segs)) { | ||
| 519 | add_to_list = 1; | ||
| 520 | } | ||
| 521 | |||
| 522 | if (add_to_list) { | ||
| 523 | stream_bufs->in_use = false; | ||
| 524 | stream_bufs->size = copied_size; | ||
| 525 | /* locking here */ | ||
| 526 | mutex_lock(&stream->lock); | ||
| 527 | list_add_tail(&stream_bufs->node, &stream->bufs); | ||
| 528 | mutex_unlock(&stream->lock); | ||
| 529 | break; | ||
| 530 | } | ||
| 531 | } | ||
| 532 | return retval; | ||
| 533 | } | ||
| 534 | |||
| 535 | /* This function copies the captured data returned from SST DSP engine | ||
| 536 | * to the user buffers*/ | ||
| 537 | static int snd_sst_copy_userbuf_capture(struct stream_info *stream, | ||
| 538 | const struct iovec *iovec, | ||
| 539 | struct list_head *copy_to_list) | ||
| 540 | { | ||
| 541 | struct snd_sst_user_cap_list *entry, *_entry; | ||
| 542 | struct sst_stream_bufs *kbufs = NULL, *_kbufs; | ||
| 543 | int retval = 0; | ||
| 544 | |||
| 545 | /* copy sent buffers */ | ||
| 546 | pr_debug("capture stream copying to user now...\n"); | ||
| 547 | list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) { | ||
| 548 | if (kbufs->in_use == true) { | ||
| 549 | /* copy to user */ | ||
| 550 | list_for_each_entry_safe(entry, _entry, | ||
| 551 | copy_to_list, node) { | ||
| 552 | if (copy_to_user(iovec[entry->iov_index].iov_base + entry->iov_offset, | ||
| 553 | kbufs->addr + entry->offset, | ||
| 554 | entry->size)) { | ||
| 555 | /* Clean up the list and return error */ | ||
| 556 | retval = -EFAULT; | ||
| 557 | break; | ||
| 558 | } | ||
| 559 | list_del(&entry->node); | ||
| 560 | kfree(entry); | ||
| 561 | } | ||
| 562 | } | ||
| 563 | } | ||
| 564 | pr_debug("end of cap copy\n"); | ||
| 565 | return retval; | ||
| 566 | } | ||
| 567 | |||
| 568 | /* | ||
| 569 | * snd_sst_userbufs_play_cap - constructs the list from user buffers | ||
| 570 | * | ||
| 571 | * @iovec:pointer to iovec structure | ||
| 572 | * @nr_segs:number entries in the iovec structure | ||
| 573 | * @str_id:stream id | ||
| 574 | * @stream:pointer to stream_info structure | ||
| 575 | * | ||
| 576 | * This function will traverse the user list and copy the data to the kernel | ||
| 577 | * space buffers. | ||
| 578 | */ | ||
| 579 | static int snd_sst_userbufs_play_cap(const struct iovec *iovec, | ||
| 580 | unsigned long nr_segs, unsigned int str_id, | ||
| 581 | struct stream_info *stream) | ||
| 582 | { | ||
| 583 | int retval; | ||
| 584 | LIST_HEAD(copy_to_list); | ||
| 585 | |||
| 586 | |||
| 587 | retval = snd_sst_fill_kernel_list(stream, iovec, nr_segs, | ||
| 588 | ©_to_list); | ||
| 589 | |||
| 590 | retval = intel_sst_play_capture(stream, str_id); | ||
| 591 | if (retval < 0) | ||
| 592 | return retval; | ||
| 593 | |||
| 594 | if (stream->ops == STREAM_OPS_CAPTURE) { | ||
| 595 | retval = snd_sst_copy_userbuf_capture(stream, iovec, | ||
| 596 | ©_to_list); | ||
| 597 | } | ||
| 598 | return retval; | ||
| 599 | } | ||
| 600 | |||
| 601 | /* This function is common function across read/write | ||
| 602 | for user buffers called from system calls*/ | ||
| 603 | static int intel_sst_read_write(unsigned int str_id, char __user *buf, | ||
| 604 | size_t count) | ||
| 605 | { | ||
| 606 | int retval; | ||
| 607 | struct stream_info *stream; | ||
| 608 | struct iovec iovec; | ||
| 609 | unsigned long nr_segs; | ||
| 610 | |||
| 611 | retval = sst_validate_strid(str_id); | ||
| 612 | if (retval) | ||
| 613 | return -EINVAL; | ||
| 614 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 615 | if (stream->mmapped == true) { | ||
| 616 | pr_warn("user write and stream is mapped\n"); | ||
| 617 | return -EIO; | ||
| 618 | } | ||
| 619 | if (!count) | ||
| 620 | return -EINVAL; | ||
| 621 | stream->curr_bytes = 0; | ||
| 622 | stream->cumm_bytes = 0; | ||
| 623 | /* copy user buf details */ | ||
| 624 | pr_debug("new buffers %p, copy size %d, status %d\n" , | ||
| 625 | buf, (int) count, (int) stream->status); | ||
| 626 | |||
| 627 | stream->buf_type = SST_BUF_USER_STATIC; | ||
| 628 | iovec.iov_base = buf; | ||
| 629 | iovec.iov_len = count; | ||
| 630 | nr_segs = 1; | ||
| 631 | |||
| 632 | do { | ||
| 633 | retval = snd_sst_userbufs_play_cap( | ||
| 634 | &iovec, nr_segs, str_id, stream); | ||
| 635 | if (retval < 0) | ||
| 636 | break; | ||
| 637 | |||
| 638 | } while (stream->sg_index < nr_segs); | ||
| 639 | |||
| 640 | stream->sg_index = 0; | ||
| 641 | stream->cur_ptr = NULL; | ||
| 642 | if (retval >= 0) | ||
| 643 | retval = stream->cumm_bytes; | ||
| 644 | pr_debug("end of play/rec bytes = %d!!\n", retval); | ||
| 645 | return retval; | ||
| 646 | } | ||
| 647 | |||
| 648 | /*** | ||
| 649 | * intel_sst_write - This function is called when user tries to play out data | ||
| 650 | * | ||
| 651 | * @file_ptr:pointer to file | ||
| 652 | * @buf:user buffer to be played out | ||
| 653 | * @count:size of tthe buffer | ||
| 654 | * @offset:offset to start from | ||
| 655 | * | ||
| 656 | * writes the encoded data into DSP | ||
| 657 | */ | ||
| 658 | int intel_sst_write(struct file *file_ptr, const char __user *buf, | ||
| 659 | size_t count, loff_t *offset) | ||
| 660 | { | ||
| 661 | struct ioctl_pvt_data *data = file_ptr->private_data; | ||
| 662 | int str_id = data->str_id; | ||
| 663 | struct stream_info *stream = &sst_drv_ctx->streams[str_id]; | ||
| 664 | |||
| 665 | pr_debug("called for %d\n", str_id); | ||
| 666 | if (stream->status == STREAM_UN_INIT || | ||
| 667 | stream->status == STREAM_DECODE) { | ||
| 668 | return -EBADRQC; | ||
| 669 | } | ||
| 670 | return intel_sst_read_write(str_id, (char __user *)buf, count); | ||
| 671 | } | ||
| 672 | |||
| 673 | /* | ||
| 674 | * intel_sst_aio_write - write buffers | ||
| 675 | * | ||
| 676 | * @kiocb:pointer to a structure containing file pointer | ||
| 677 | * @iov:list of user buffer to be played out | ||
| 678 | * @nr_segs:number of entries | ||
| 679 | * @offset:offset to start from | ||
| 680 | * | ||
| 681 | * This function is called when user tries to play out multiple data buffers | ||
| 682 | */ | ||
| 683 | ssize_t intel_sst_aio_write(struct kiocb *kiocb, const struct iovec *iov, | ||
| 684 | unsigned long nr_segs, loff_t offset) | ||
| 685 | { | ||
| 686 | int retval; | ||
| 687 | struct ioctl_pvt_data *data = kiocb->ki_filp->private_data; | ||
| 688 | int str_id = data->str_id; | ||
| 689 | struct stream_info *stream; | ||
| 690 | |||
| 691 | pr_debug("entry - %ld\n", nr_segs); | ||
| 692 | |||
| 693 | if (is_sync_kiocb(kiocb) == false) | ||
| 694 | return -EINVAL; | ||
| 695 | |||
| 696 | pr_debug("called for str_id %d\n", str_id); | ||
| 697 | retval = sst_validate_strid(str_id); | ||
| 698 | if (retval) | ||
| 699 | return -EINVAL; | ||
| 700 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 701 | if (stream->mmapped == true) | ||
| 702 | return -EIO; | ||
| 703 | if (stream->status == STREAM_UN_INIT || | ||
| 704 | stream->status == STREAM_DECODE) { | ||
| 705 | return -EBADRQC; | ||
| 706 | } | ||
| 707 | stream->curr_bytes = 0; | ||
| 708 | stream->cumm_bytes = 0; | ||
| 709 | pr_debug("new segs %ld, offset %d, status %d\n" , | ||
| 710 | nr_segs, (int) offset, (int) stream->status); | ||
| 711 | stream->buf_type = SST_BUF_USER_STATIC; | ||
| 712 | do { | ||
| 713 | retval = snd_sst_userbufs_play_cap(iov, nr_segs, | ||
| 714 | str_id, stream); | ||
| 715 | if (retval < 0) | ||
| 716 | break; | ||
| 717 | |||
| 718 | } while (stream->sg_index < nr_segs); | ||
| 719 | |||
| 720 | stream->sg_index = 0; | ||
| 721 | stream->cur_ptr = NULL; | ||
| 722 | if (retval >= 0) | ||
| 723 | retval = stream->cumm_bytes; | ||
| 724 | pr_debug("end of play/rec bytes = %d!!\n", retval); | ||
| 725 | return retval; | ||
| 726 | } | ||
| 727 | |||
| 728 | /* | ||
| 729 | * intel_sst_read - read the encoded data | ||
| 730 | * | ||
| 731 | * @file_ptr: pointer to file | ||
| 732 | * @buf: user buffer to be filled with captured data | ||
| 733 | * @count: size of tthe buffer | ||
| 734 | * @offset: offset to start from | ||
| 735 | * | ||
| 736 | * This function is called when user tries to capture data | ||
| 737 | */ | ||
| 738 | int intel_sst_read(struct file *file_ptr, char __user *buf, | ||
| 739 | size_t count, loff_t *offset) | ||
| 740 | { | ||
| 741 | struct ioctl_pvt_data *data = file_ptr->private_data; | ||
| 742 | int str_id = data->str_id; | ||
| 743 | struct stream_info *stream = &sst_drv_ctx->streams[str_id]; | ||
| 744 | |||
| 745 | pr_debug("called for %d\n", str_id); | ||
| 746 | if (stream->status == STREAM_UN_INIT || | ||
| 747 | stream->status == STREAM_DECODE) | ||
| 748 | return -EBADRQC; | ||
| 749 | return intel_sst_read_write(str_id, buf, count); | ||
| 750 | } | ||
| 751 | |||
| 752 | /* | ||
| 753 | * intel_sst_aio_read - aio read | ||
| 754 | * | ||
| 755 | * @kiocb: pointer to a structure containing file pointer | ||
| 756 | * @iov: list of user buffer to be filled with captured | ||
| 757 | * @nr_segs: number of entries | ||
| 758 | * @offset: offset to start from | ||
| 759 | * | ||
| 760 | * This function is called when user tries to capture out multiple data buffers | ||
| 761 | */ | ||
| 762 | ssize_t intel_sst_aio_read(struct kiocb *kiocb, const struct iovec *iov, | ||
| 763 | unsigned long nr_segs, loff_t offset) | ||
| 764 | { | ||
| 765 | int retval; | ||
| 766 | struct ioctl_pvt_data *data = kiocb->ki_filp->private_data; | ||
| 767 | int str_id = data->str_id; | ||
| 768 | struct stream_info *stream; | ||
| 769 | |||
| 770 | pr_debug("entry - %ld\n", nr_segs); | ||
| 771 | |||
| 772 | if (is_sync_kiocb(kiocb) == false) { | ||
| 773 | pr_debug("aio_read from user space is not allowed\n"); | ||
| 774 | return -EINVAL; | ||
| 775 | } | ||
| 776 | |||
| 777 | pr_debug("called for str_id %d\n", str_id); | ||
| 778 | retval = sst_validate_strid(str_id); | ||
| 779 | if (retval) | ||
| 780 | return -EINVAL; | ||
| 781 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 782 | if (stream->mmapped == true) | ||
| 783 | return -EIO; | ||
| 784 | if (stream->status == STREAM_UN_INIT || | ||
| 785 | stream->status == STREAM_DECODE) | ||
| 786 | return -EBADRQC; | ||
| 787 | stream->curr_bytes = 0; | ||
| 788 | stream->cumm_bytes = 0; | ||
| 789 | |||
| 790 | pr_debug("new segs %ld, offset %d, status %d\n" , | ||
| 791 | nr_segs, (int) offset, (int) stream->status); | ||
| 792 | stream->buf_type = SST_BUF_USER_STATIC; | ||
| 793 | do { | ||
| 794 | retval = snd_sst_userbufs_play_cap(iov, nr_segs, | ||
| 795 | str_id, stream); | ||
| 796 | if (retval < 0) | ||
| 797 | break; | ||
| 798 | |||
| 799 | } while (stream->sg_index < nr_segs); | ||
| 800 | |||
| 801 | stream->sg_index = 0; | ||
| 802 | stream->cur_ptr = NULL; | ||
| 803 | if (retval >= 0) | ||
| 804 | retval = stream->cumm_bytes; | ||
| 805 | pr_debug("end of play/rec bytes = %d!!\n", retval); | ||
| 806 | return retval; | ||
| 807 | } | ||
| 808 | |||
| 809 | /* sst_print_stream_params - prints the stream parameters (debug fn)*/ | ||
| 810 | static void sst_print_stream_params(struct snd_sst_get_stream_params *get_prm) | ||
| 811 | { | ||
| 812 | pr_debug("codec params:result = %d\n", | ||
| 813 | get_prm->codec_params.result); | ||
| 814 | pr_debug("codec params:stream = %d\n", | ||
| 815 | get_prm->codec_params.stream_id); | ||
| 816 | pr_debug("codec params:codec = %d\n", | ||
| 817 | get_prm->codec_params.codec); | ||
| 818 | pr_debug("codec params:ops = %d\n", | ||
| 819 | get_prm->codec_params.ops); | ||
| 820 | pr_debug("codec params:stream_type = %d\n", | ||
| 821 | get_prm->codec_params.stream_type); | ||
| 822 | pr_debug("pcmparams:sfreq = %d\n", | ||
| 823 | get_prm->pcm_params.sfreq); | ||
| 824 | pr_debug("pcmparams:num_chan = %d\n", | ||
| 825 | get_prm->pcm_params.num_chan); | ||
| 826 | pr_debug("pcmparams:pcm_wd_sz = %d\n", | ||
| 827 | get_prm->pcm_params.pcm_wd_sz); | ||
| 828 | return; | ||
| 829 | } | ||
| 830 | |||
| 831 | /** | ||
| 832 | * sst_create_algo_ipc - create ipc msg for algorithm parameters | ||
| 833 | * | ||
| 834 | * @algo_params: Algorithm parameters | ||
| 835 | * @msg: post msg pointer | ||
| 836 | * | ||
| 837 | * This function is called to create ipc msg | ||
| 838 | */ | ||
| 839 | int sst_create_algo_ipc(struct snd_ppp_params *algo_params, | ||
| 840 | struct ipc_post **msg) | ||
| 841 | { | ||
| 842 | if (sst_create_large_msg(msg)) | ||
| 843 | return -ENOMEM; | ||
| 844 | sst_fill_header(&(*msg)->header, | ||
| 845 | IPC_IA_ALG_PARAMS, 1, algo_params->str_id); | ||
| 846 | (*msg)->header.part.data = sizeof(u32) + | ||
| 847 | sizeof(*algo_params) + algo_params->size; | ||
| 848 | memcpy((*msg)->mailbox_data, &(*msg)->header, sizeof(u32)); | ||
| 849 | memcpy((*msg)->mailbox_data + sizeof(u32), | ||
| 850 | algo_params, sizeof(*algo_params)); | ||
| 851 | return 0; | ||
| 852 | } | ||
| 853 | |||
| 854 | /** | ||
| 855 | * sst_send_algo_ipc - send ipc msg for algorithm parameters | ||
| 856 | * | ||
| 857 | * @msg: post msg pointer | ||
| 858 | * | ||
| 859 | * This function is called to send ipc msg | ||
| 860 | */ | ||
| 861 | int sst_send_algo_ipc(struct ipc_post **msg) | ||
| 862 | { | ||
| 863 | sst_drv_ctx->ppp_params_blk.condition = false; | ||
| 864 | sst_drv_ctx->ppp_params_blk.ret_code = 0; | ||
| 865 | sst_drv_ctx->ppp_params_blk.on = true; | ||
| 866 | sst_drv_ctx->ppp_params_blk.data = NULL; | ||
| 867 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 868 | list_add_tail(&(*msg)->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 869 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 870 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 871 | return sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 872 | &sst_drv_ctx->ppp_params_blk, SST_BLOCK_TIMEOUT); | ||
| 873 | } | ||
| 874 | |||
| 875 | /** | ||
| 876 | * intel_sst_ioctl_dsp - receives the device ioctl's | ||
| 877 | * | ||
| 878 | * @cmd:Ioctl cmd | ||
| 879 | * @arg:data | ||
| 880 | * | ||
| 881 | * This function is called when a user space component | ||
| 882 | * sends a DSP Ioctl to SST driver | ||
| 883 | */ | ||
| 884 | long intel_sst_ioctl_dsp(unsigned int cmd, unsigned long arg) | ||
| 885 | { | ||
| 886 | int retval = 0; | ||
| 887 | struct snd_ppp_params algo_params; | ||
| 888 | struct snd_ppp_params *algo_params_copied; | ||
| 889 | struct ipc_post *msg; | ||
| 890 | |||
| 891 | switch (_IOC_NR(cmd)) { | ||
| 892 | case _IOC_NR(SNDRV_SST_SET_ALGO): | ||
| 893 | if (copy_from_user(&algo_params, (void __user *)arg, | ||
| 894 | sizeof(algo_params))) | ||
| 895 | return -EFAULT; | ||
| 896 | if (algo_params.size > SST_MAILBOX_SIZE) | ||
| 897 | return -EMSGSIZE; | ||
| 898 | |||
| 899 | pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", | ||
| 900 | algo_params.algo_id, algo_params.str_id, | ||
| 901 | algo_params.enable, algo_params.size); | ||
| 902 | retval = sst_create_algo_ipc(&algo_params, &msg); | ||
| 903 | if (retval) | ||
| 904 | break; | ||
| 905 | algo_params.reserved = 0; | ||
| 906 | if (copy_from_user(msg->mailbox_data + sizeof(algo_params), | ||
| 907 | algo_params.params, algo_params.size)) | ||
| 908 | return -EFAULT; | ||
| 909 | |||
| 910 | retval = sst_send_algo_ipc(&msg); | ||
| 911 | if (retval) { | ||
| 912 | pr_debug("Error in sst_set_algo = %d\n", retval); | ||
| 913 | retval = -EIO; | ||
| 914 | } | ||
| 915 | break; | ||
| 916 | |||
| 917 | case _IOC_NR(SNDRV_SST_GET_ALGO): | ||
| 918 | if (copy_from_user(&algo_params, (void __user *)arg, | ||
| 919 | sizeof(algo_params))) | ||
| 920 | return -EFAULT; | ||
| 921 | pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", | ||
| 922 | algo_params.algo_id, algo_params.str_id, | ||
| 923 | algo_params.enable, algo_params.size); | ||
| 924 | retval = sst_create_algo_ipc(&algo_params, &msg); | ||
| 925 | if (retval) | ||
| 926 | break; | ||
| 927 | algo_params.reserved = 1; | ||
| 928 | retval = sst_send_algo_ipc(&msg); | ||
| 929 | if (retval) { | ||
| 930 | pr_debug("Error in sst_get_algo = %d\n", retval); | ||
| 931 | retval = -EIO; | ||
| 932 | break; | ||
| 933 | } | ||
| 934 | algo_params_copied = (struct snd_ppp_params *) | ||
| 935 | sst_drv_ctx->ppp_params_blk.data; | ||
| 936 | if (algo_params_copied->size > algo_params.size) { | ||
| 937 | pr_debug("mem insufficient to copy\n"); | ||
| 938 | retval = -EMSGSIZE; | ||
| 939 | goto free_mem; | ||
| 940 | } else { | ||
| 941 | char __user *tmp; | ||
| 942 | |||
| 943 | if (copy_to_user(algo_params.params, | ||
| 944 | algo_params_copied->params, | ||
| 945 | algo_params_copied->size)) { | ||
| 946 | retval = -EFAULT; | ||
| 947 | goto free_mem; | ||
| 948 | } | ||
| 949 | tmp = (char __user *)arg + offsetof( | ||
| 950 | struct snd_ppp_params, size); | ||
| 951 | if (copy_to_user(tmp, &algo_params_copied->size, | ||
| 952 | sizeof(__u32))) { | ||
| 953 | retval = -EFAULT; | ||
| 954 | goto free_mem; | ||
| 955 | } | ||
| 956 | |||
| 957 | } | ||
| 958 | free_mem: | ||
| 959 | kfree(algo_params_copied->params); | ||
| 960 | kfree(algo_params_copied); | ||
| 961 | break; | ||
| 962 | } | ||
| 963 | return retval; | ||
| 964 | } | ||
| 965 | |||
| 966 | |||
| 967 | int sst_ioctl_tuning_params(unsigned long arg) | ||
| 968 | { | ||
| 969 | struct snd_sst_tuning_params params; | ||
| 970 | struct ipc_post *msg; | ||
| 971 | |||
| 972 | if (copy_from_user(¶ms, (void __user *)arg, sizeof(params))) | ||
| 973 | return -EFAULT; | ||
| 974 | if (params.size > SST_MAILBOX_SIZE) | ||
| 975 | return -ENOMEM; | ||
| 976 | pr_debug("Parameter %d, Stream %d, Size %d\n", params.type, | ||
| 977 | params.str_id, params.size); | ||
| 978 | if (sst_create_large_msg(&msg)) | ||
| 979 | return -ENOMEM; | ||
| 980 | |||
| 981 | sst_fill_header(&msg->header, IPC_IA_TUNING_PARAMS, 1, params.str_id); | ||
| 982 | msg->header.part.data = sizeof(u32) + sizeof(params) + params.size; | ||
| 983 | memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); | ||
| 984 | memcpy(msg->mailbox_data + sizeof(u32), ¶ms, sizeof(params)); | ||
| 985 | if (copy_from_user(msg->mailbox_data + sizeof(params), | ||
| 986 | (void __user *)(unsigned long)params.addr, | ||
| 987 | params.size)) { | ||
| 988 | kfree(msg->mailbox_data); | ||
| 989 | kfree(msg); | ||
| 990 | return -EFAULT; | ||
| 991 | } | ||
| 992 | return sst_send_algo_ipc(&msg); | ||
| 993 | } | ||
| 994 | /** | ||
| 995 | * intel_sst_ioctl - receives the device ioctl's | ||
| 996 | * @file_ptr:pointer to file | ||
| 997 | * @cmd:Ioctl cmd | ||
| 998 | * @arg:data | ||
| 999 | * | ||
| 1000 | * This function is called by OS when a user space component | ||
| 1001 | * sends an Ioctl to SST driver | ||
| 1002 | */ | ||
| 1003 | long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg) | ||
| 1004 | { | ||
| 1005 | int retval = 0; | ||
| 1006 | struct ioctl_pvt_data *data = NULL; | ||
| 1007 | int str_id = 0, minor = 0; | ||
| 1008 | |||
| 1009 | data = file_ptr->private_data; | ||
| 1010 | if (data) { | ||
| 1011 | minor = 0; | ||
| 1012 | str_id = data->str_id; | ||
| 1013 | } else | ||
| 1014 | minor = 1; | ||
| 1015 | |||
| 1016 | if (sst_drv_ctx->sst_state != SST_FW_RUNNING) | ||
| 1017 | return -EBUSY; | ||
| 1018 | |||
| 1019 | switch (_IOC_NR(cmd)) { | ||
| 1020 | case _IOC_NR(SNDRV_SST_STREAM_PAUSE): | ||
| 1021 | pr_debug("IOCTL_PAUSE received for %d!\n", str_id); | ||
| 1022 | if (minor != STREAM_MODULE) { | ||
| 1023 | retval = -EBADRQC; | ||
| 1024 | break; | ||
| 1025 | } | ||
| 1026 | retval = sst_pause_stream(str_id); | ||
| 1027 | break; | ||
| 1028 | |||
| 1029 | case _IOC_NR(SNDRV_SST_STREAM_RESUME): | ||
| 1030 | pr_debug("SNDRV_SST_IOCTL_RESUME received!\n"); | ||
| 1031 | if (minor != STREAM_MODULE) { | ||
| 1032 | retval = -EBADRQC; | ||
| 1033 | break; | ||
| 1034 | } | ||
| 1035 | retval = sst_resume_stream(str_id); | ||
| 1036 | break; | ||
| 1037 | |||
| 1038 | case _IOC_NR(SNDRV_SST_STREAM_SET_PARAMS): { | ||
| 1039 | struct snd_sst_params str_param; | ||
| 1040 | |||
| 1041 | pr_debug("IOCTL_SET_PARAMS received!\n"); | ||
| 1042 | if (minor != STREAM_MODULE) { | ||
| 1043 | retval = -EBADRQC; | ||
| 1044 | break; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | if (copy_from_user(&str_param, (void __user *)arg, | ||
| 1048 | sizeof(str_param))) { | ||
| 1049 | retval = -EFAULT; | ||
| 1050 | break; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | if (!str_id) { | ||
| 1054 | |||
| 1055 | retval = sst_get_stream(&str_param); | ||
| 1056 | if (retval > 0) { | ||
| 1057 | struct stream_info *str_info; | ||
| 1058 | char __user *dest; | ||
| 1059 | |||
| 1060 | sst_drv_ctx->stream_cnt++; | ||
| 1061 | data->str_id = retval; | ||
| 1062 | str_info = &sst_drv_ctx->streams[retval]; | ||
| 1063 | str_info->src = SST_DRV; | ||
| 1064 | dest = (char __user *)arg + offsetof(struct snd_sst_params, stream_id); | ||
| 1065 | retval = copy_to_user(dest, &retval, sizeof(__u32)); | ||
| 1066 | if (retval) | ||
| 1067 | retval = -EFAULT; | ||
| 1068 | } else { | ||
| 1069 | if (retval == -SST_ERR_INVALID_PARAMS) | ||
| 1070 | retval = -EINVAL; | ||
| 1071 | } | ||
| 1072 | } else { | ||
| 1073 | pr_debug("SET_STREAM_PARAMS received!\n"); | ||
| 1074 | /* allocated set params only */ | ||
| 1075 | retval = sst_set_stream_param(str_id, &str_param); | ||
| 1076 | /* Block the call for reply */ | ||
| 1077 | if (!retval) { | ||
| 1078 | int sfreq = 0, word_size = 0, num_channel = 0; | ||
| 1079 | sfreq = str_param.sparams.uc.pcm_params.sfreq; | ||
| 1080 | word_size = str_param.sparams.uc.pcm_params.pcm_wd_sz; | ||
| 1081 | num_channel = str_param.sparams.uc.pcm_params.num_chan; | ||
| 1082 | if (str_param.ops == STREAM_OPS_CAPTURE) { | ||
| 1083 | sst_drv_ctx->scard_ops->\ | ||
| 1084 | set_pcm_audio_params(sfreq, | ||
| 1085 | word_size, num_channel); | ||
| 1086 | } | ||
| 1087 | } | ||
| 1088 | } | ||
| 1089 | break; | ||
| 1090 | } | ||
| 1091 | case _IOC_NR(SNDRV_SST_SET_VOL): { | ||
| 1092 | struct snd_sst_vol set_vol; | ||
| 1093 | |||
| 1094 | if (copy_from_user(&set_vol, (void __user *)arg, | ||
| 1095 | sizeof(set_vol))) { | ||
| 1096 | pr_debug("copy failed\n"); | ||
| 1097 | retval = -EFAULT; | ||
| 1098 | break; | ||
| 1099 | } | ||
| 1100 | pr_debug("SET_VOLUME received for %d!\n", | ||
| 1101 | set_vol.stream_id); | ||
| 1102 | if (minor == STREAM_MODULE && set_vol.stream_id == 0) { | ||
| 1103 | pr_debug("invalid operation!\n"); | ||
| 1104 | retval = -EPERM; | ||
| 1105 | break; | ||
| 1106 | } | ||
| 1107 | retval = sst_set_vol(&set_vol); | ||
| 1108 | break; | ||
| 1109 | } | ||
| 1110 | case _IOC_NR(SNDRV_SST_GET_VOL): { | ||
| 1111 | struct snd_sst_vol get_vol; | ||
| 1112 | |||
| 1113 | if (copy_from_user(&get_vol, (void __user *)arg, | ||
| 1114 | sizeof(get_vol))) { | ||
| 1115 | retval = -EFAULT; | ||
| 1116 | break; | ||
| 1117 | } | ||
| 1118 | pr_debug("IOCTL_GET_VOLUME received for stream = %d!\n", | ||
| 1119 | get_vol.stream_id); | ||
| 1120 | if (minor == STREAM_MODULE && get_vol.stream_id == 0) { | ||
| 1121 | pr_debug("invalid operation!\n"); | ||
| 1122 | retval = -EPERM; | ||
| 1123 | break; | ||
| 1124 | } | ||
| 1125 | retval = sst_get_vol(&get_vol); | ||
| 1126 | if (retval) { | ||
| 1127 | retval = -EIO; | ||
| 1128 | break; | ||
| 1129 | } | ||
| 1130 | pr_debug("id:%d\n, vol:%d, ramp_dur:%d, ramp_type:%d\n", | ||
| 1131 | get_vol.stream_id, get_vol.volume, | ||
| 1132 | get_vol.ramp_duration, get_vol.ramp_type); | ||
| 1133 | if (copy_to_user((struct snd_sst_vol __user *)arg, | ||
| 1134 | &get_vol, sizeof(get_vol))) { | ||
| 1135 | retval = -EFAULT; | ||
| 1136 | break; | ||
| 1137 | } | ||
| 1138 | /*sst_print_get_vol_info(str_id, &get_vol);*/ | ||
| 1139 | break; | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | case _IOC_NR(SNDRV_SST_MUTE): { | ||
| 1143 | struct snd_sst_mute set_mute; | ||
| 1144 | |||
| 1145 | if (copy_from_user(&set_mute, (void __user *)arg, | ||
| 1146 | sizeof(set_mute))) { | ||
| 1147 | retval = -EFAULT; | ||
| 1148 | break; | ||
| 1149 | } | ||
| 1150 | pr_debug("SNDRV_SST_SET_VOLUME received for %d!\n", | ||
| 1151 | set_mute.stream_id); | ||
| 1152 | if (minor == STREAM_MODULE && set_mute.stream_id == 0) { | ||
| 1153 | retval = -EPERM; | ||
| 1154 | break; | ||
| 1155 | } | ||
| 1156 | retval = sst_set_mute(&set_mute); | ||
| 1157 | break; | ||
| 1158 | } | ||
| 1159 | case _IOC_NR(SNDRV_SST_STREAM_GET_PARAMS): { | ||
| 1160 | struct snd_sst_get_stream_params get_params; | ||
| 1161 | |||
| 1162 | pr_debug("IOCTL_GET_PARAMS received!\n"); | ||
| 1163 | if (minor != 0) { | ||
| 1164 | retval = -EBADRQC; | ||
| 1165 | break; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | retval = sst_get_stream_params(str_id, &get_params); | ||
| 1169 | if (retval) { | ||
| 1170 | retval = -EIO; | ||
| 1171 | break; | ||
| 1172 | } | ||
| 1173 | if (copy_to_user((struct snd_sst_get_stream_params __user *)arg, | ||
| 1174 | &get_params, sizeof(get_params))) { | ||
| 1175 | retval = -EFAULT; | ||
| 1176 | break; | ||
| 1177 | } | ||
| 1178 | sst_print_stream_params(&get_params); | ||
| 1179 | break; | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | case _IOC_NR(SNDRV_SST_MMAP_PLAY): | ||
| 1183 | case _IOC_NR(SNDRV_SST_MMAP_CAPTURE): { | ||
| 1184 | struct snd_sst_mmap_buffs mmap_buf; | ||
| 1185 | |||
| 1186 | pr_debug("SNDRV_SST_MMAP_PLAY/CAPTURE received!\n"); | ||
| 1187 | if (minor != STREAM_MODULE) { | ||
| 1188 | retval = -EBADRQC; | ||
| 1189 | break; | ||
| 1190 | } | ||
| 1191 | if (copy_from_user(&mmap_buf, (void __user *)arg, | ||
| 1192 | sizeof(mmap_buf))) { | ||
| 1193 | retval = -EFAULT; | ||
| 1194 | break; | ||
| 1195 | } | ||
| 1196 | retval = intel_sst_mmap_play_capture(str_id, &mmap_buf); | ||
| 1197 | break; | ||
| 1198 | } | ||
| 1199 | case _IOC_NR(SNDRV_SST_STREAM_DROP): | ||
| 1200 | pr_debug("SNDRV_SST_IOCTL_DROP received!\n"); | ||
| 1201 | if (minor != STREAM_MODULE) { | ||
| 1202 | retval = -EINVAL; | ||
| 1203 | break; | ||
| 1204 | } | ||
| 1205 | retval = sst_drop_stream(str_id); | ||
| 1206 | break; | ||
| 1207 | |||
| 1208 | case _IOC_NR(SNDRV_SST_STREAM_GET_TSTAMP): { | ||
| 1209 | struct snd_sst_tstamp tstamp = {0}; | ||
| 1210 | unsigned long long time, freq, mod; | ||
| 1211 | |||
| 1212 | pr_debug("SNDRV_SST_STREAM_GET_TSTAMP received!\n"); | ||
| 1213 | if (minor != STREAM_MODULE) { | ||
| 1214 | retval = -EBADRQC; | ||
| 1215 | break; | ||
| 1216 | } | ||
| 1217 | memcpy_fromio(&tstamp, | ||
| 1218 | sst_drv_ctx->mailbox + SST_TIME_STAMP + str_id * sizeof(tstamp), | ||
| 1219 | sizeof(tstamp)); | ||
| 1220 | time = tstamp.samples_rendered; | ||
| 1221 | freq = (unsigned long long) tstamp.sampling_frequency; | ||
| 1222 | time = time * 1000; /* converting it to ms */ | ||
| 1223 | mod = do_div(time, freq); | ||
| 1224 | if (copy_to_user((void __user *)arg, &time, | ||
| 1225 | sizeof(unsigned long long))) | ||
| 1226 | retval = -EFAULT; | ||
| 1227 | break; | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | case _IOC_NR(SNDRV_SST_STREAM_START):{ | ||
| 1231 | struct stream_info *stream; | ||
| 1232 | |||
| 1233 | pr_debug("SNDRV_SST_STREAM_START received!\n"); | ||
| 1234 | if (minor != STREAM_MODULE) { | ||
| 1235 | retval = -EINVAL; | ||
| 1236 | break; | ||
| 1237 | } | ||
| 1238 | retval = sst_validate_strid(str_id); | ||
| 1239 | if (retval) | ||
| 1240 | break; | ||
| 1241 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 1242 | mutex_lock(&stream->lock); | ||
| 1243 | if (stream->status == STREAM_INIT && | ||
| 1244 | stream->need_draining != true) { | ||
| 1245 | stream->prev = stream->status; | ||
| 1246 | stream->status = STREAM_RUNNING; | ||
| 1247 | if (stream->ops == STREAM_OPS_PLAYBACK || | ||
| 1248 | stream->ops == STREAM_OPS_PLAYBACK_DRM) { | ||
| 1249 | retval = sst_play_frame(str_id); | ||
| 1250 | } else if (stream->ops == STREAM_OPS_CAPTURE) | ||
| 1251 | retval = sst_capture_frame(str_id); | ||
| 1252 | else { | ||
| 1253 | retval = -EINVAL; | ||
| 1254 | mutex_unlock(&stream->lock); | ||
| 1255 | break; | ||
| 1256 | } | ||
| 1257 | if (retval < 0) { | ||
| 1258 | stream->status = STREAM_INIT; | ||
| 1259 | mutex_unlock(&stream->lock); | ||
| 1260 | break; | ||
| 1261 | } | ||
| 1262 | } else { | ||
| 1263 | retval = -EINVAL; | ||
| 1264 | } | ||
| 1265 | mutex_unlock(&stream->lock); | ||
| 1266 | break; | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | case _IOC_NR(SNDRV_SST_SET_TARGET_DEVICE): { | ||
| 1270 | struct snd_sst_target_device target_device; | ||
| 1271 | |||
| 1272 | pr_debug("SET_TARGET_DEVICE received!\n"); | ||
| 1273 | if (copy_from_user(&target_device, (void __user *)arg, | ||
| 1274 | sizeof(target_device))) { | ||
| 1275 | retval = -EFAULT; | ||
| 1276 | break; | ||
| 1277 | } | ||
| 1278 | if (minor != AM_MODULE) { | ||
| 1279 | retval = -EBADRQC; | ||
| 1280 | break; | ||
| 1281 | } | ||
| 1282 | retval = sst_target_device_select(&target_device); | ||
| 1283 | break; | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | case _IOC_NR(SNDRV_SST_DRIVER_INFO): { | ||
| 1287 | struct snd_sst_driver_info info; | ||
| 1288 | |||
| 1289 | pr_debug("SNDRV_SST_DRIVER_INFO received\n"); | ||
| 1290 | info.version = SST_VERSION_NUM; | ||
| 1291 | /* hard coding, shud get sumhow later */ | ||
| 1292 | info.active_pcm_streams = sst_drv_ctx->stream_cnt - | ||
| 1293 | sst_drv_ctx->encoded_cnt; | ||
| 1294 | info.active_enc_streams = sst_drv_ctx->encoded_cnt; | ||
| 1295 | info.max_pcm_streams = MAX_ACTIVE_STREAM - MAX_ENC_STREAM; | ||
| 1296 | info.max_enc_streams = MAX_ENC_STREAM; | ||
| 1297 | info.buf_per_stream = sst_drv_ctx->mmap_len; | ||
| 1298 | if (copy_to_user((void __user *)arg, &info, | ||
| 1299 | sizeof(info))) | ||
| 1300 | retval = -EFAULT; | ||
| 1301 | break; | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | case _IOC_NR(SNDRV_SST_STREAM_DECODE): { | ||
| 1305 | struct snd_sst_dbufs param; | ||
| 1306 | struct snd_sst_dbufs dbufs_local; | ||
| 1307 | struct snd_sst_buffs ibufs, obufs; | ||
| 1308 | struct snd_sst_buff_entry *ibuf_tmp, *obuf_tmp; | ||
| 1309 | char __user *dest; | ||
| 1310 | |||
| 1311 | pr_debug("SNDRV_SST_STREAM_DECODE received\n"); | ||
| 1312 | if (minor != STREAM_MODULE) { | ||
| 1313 | retval = -EBADRQC; | ||
| 1314 | break; | ||
| 1315 | } | ||
| 1316 | if (copy_from_user(¶m, (void __user *)arg, | ||
| 1317 | sizeof(param))) { | ||
| 1318 | retval = -EFAULT; | ||
| 1319 | break; | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | dbufs_local.input_bytes_consumed = param.input_bytes_consumed; | ||
| 1323 | dbufs_local.output_bytes_produced = | ||
| 1324 | param.output_bytes_produced; | ||
| 1325 | |||
| 1326 | if (copy_from_user(&ibufs, (void __user *)param.ibufs, sizeof(ibufs))) { | ||
| 1327 | retval = -EFAULT; | ||
| 1328 | break; | ||
| 1329 | } | ||
| 1330 | if (copy_from_user(&obufs, (void __user *)param.obufs, sizeof(obufs))) { | ||
| 1331 | retval = -EFAULT; | ||
| 1332 | break; | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | ibuf_tmp = kcalloc(ibufs.entries, sizeof(*ibuf_tmp), GFP_KERNEL); | ||
| 1336 | obuf_tmp = kcalloc(obufs.entries, sizeof(*obuf_tmp), GFP_KERNEL); | ||
| 1337 | if (!ibuf_tmp || !obuf_tmp) { | ||
| 1338 | retval = -ENOMEM; | ||
| 1339 | goto free_iobufs; | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | if (copy_from_user(ibuf_tmp, (void __user *)ibufs.buff_entry, | ||
| 1343 | ibufs.entries * sizeof(*ibuf_tmp))) { | ||
| 1344 | retval = -EFAULT; | ||
| 1345 | goto free_iobufs; | ||
| 1346 | } | ||
| 1347 | ibufs.buff_entry = ibuf_tmp; | ||
| 1348 | dbufs_local.ibufs = &ibufs; | ||
| 1349 | |||
| 1350 | if (copy_from_user(obuf_tmp, (void __user *)obufs.buff_entry, | ||
| 1351 | obufs.entries * sizeof(*obuf_tmp))) { | ||
| 1352 | retval = -EFAULT; | ||
| 1353 | goto free_iobufs; | ||
| 1354 | } | ||
| 1355 | obufs.buff_entry = obuf_tmp; | ||
| 1356 | dbufs_local.obufs = &obufs; | ||
| 1357 | |||
| 1358 | retval = sst_decode(str_id, &dbufs_local); | ||
| 1359 | if (retval) { | ||
| 1360 | retval = -EAGAIN; | ||
| 1361 | goto free_iobufs; | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | dest = (char __user *)arg + offsetof(struct snd_sst_dbufs, input_bytes_consumed); | ||
| 1365 | if (copy_to_user(dest, | ||
| 1366 | &dbufs_local.input_bytes_consumed, | ||
| 1367 | sizeof(unsigned long long))) { | ||
| 1368 | retval = -EFAULT; | ||
| 1369 | goto free_iobufs; | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | dest = (char __user *)arg + offsetof(struct snd_sst_dbufs, input_bytes_consumed); | ||
| 1373 | if (copy_to_user(dest, | ||
| 1374 | &dbufs_local.output_bytes_produced, | ||
| 1375 | sizeof(unsigned long long))) { | ||
| 1376 | retval = -EFAULT; | ||
| 1377 | goto free_iobufs; | ||
| 1378 | } | ||
| 1379 | free_iobufs: | ||
| 1380 | kfree(ibuf_tmp); | ||
| 1381 | kfree(obuf_tmp); | ||
| 1382 | break; | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | case _IOC_NR(SNDRV_SST_STREAM_DRAIN): | ||
| 1386 | pr_debug("SNDRV_SST_STREAM_DRAIN received\n"); | ||
| 1387 | if (minor != STREAM_MODULE) { | ||
| 1388 | retval = -EINVAL; | ||
| 1389 | break; | ||
| 1390 | } | ||
| 1391 | retval = sst_drain_stream(str_id); | ||
| 1392 | break; | ||
| 1393 | |||
| 1394 | case _IOC_NR(SNDRV_SST_STREAM_BYTES_DECODED): { | ||
| 1395 | unsigned long long __user *bytes = (unsigned long long __user *)arg; | ||
| 1396 | struct snd_sst_tstamp tstamp = {0}; | ||
| 1397 | |||
| 1398 | pr_debug("STREAM_BYTES_DECODED received!\n"); | ||
| 1399 | if (minor != STREAM_MODULE) { | ||
| 1400 | retval = -EINVAL; | ||
| 1401 | break; | ||
| 1402 | } | ||
| 1403 | memcpy_fromio(&tstamp, | ||
| 1404 | sst_drv_ctx->mailbox + SST_TIME_STAMP + str_id * sizeof(tstamp), | ||
| 1405 | sizeof(tstamp)); | ||
| 1406 | if (copy_to_user(bytes, &tstamp.bytes_processed, | ||
| 1407 | sizeof(*bytes))) | ||
| 1408 | retval = -EFAULT; | ||
| 1409 | break; | ||
| 1410 | } | ||
| 1411 | case _IOC_NR(SNDRV_SST_FW_INFO): { | ||
| 1412 | struct snd_sst_fw_info *fw_info; | ||
| 1413 | |||
| 1414 | pr_debug("SNDRV_SST_FW_INFO received\n"); | ||
| 1415 | |||
| 1416 | fw_info = kzalloc(sizeof(*fw_info), GFP_ATOMIC); | ||
| 1417 | if (!fw_info) { | ||
| 1418 | retval = -ENOMEM; | ||
| 1419 | break; | ||
| 1420 | } | ||
| 1421 | retval = sst_get_fw_info(fw_info); | ||
| 1422 | if (retval) { | ||
| 1423 | retval = -EIO; | ||
| 1424 | kfree(fw_info); | ||
| 1425 | break; | ||
| 1426 | } | ||
| 1427 | if (copy_to_user((struct snd_sst_dbufs __user *)arg, | ||
| 1428 | fw_info, sizeof(*fw_info))) { | ||
| 1429 | kfree(fw_info); | ||
| 1430 | retval = -EFAULT; | ||
| 1431 | break; | ||
| 1432 | } | ||
| 1433 | /*sst_print_fw_info(fw_info);*/ | ||
| 1434 | kfree(fw_info); | ||
| 1435 | break; | ||
| 1436 | } | ||
| 1437 | case _IOC_NR(SNDRV_SST_GET_ALGO): | ||
| 1438 | case _IOC_NR(SNDRV_SST_SET_ALGO): | ||
| 1439 | if (minor != AM_MODULE) { | ||
| 1440 | retval = -EBADRQC; | ||
| 1441 | break; | ||
| 1442 | } | ||
| 1443 | retval = intel_sst_ioctl_dsp(cmd, arg); | ||
| 1444 | break; | ||
| 1445 | |||
| 1446 | case _IOC_NR(SNDRV_SST_TUNING_PARAMS): | ||
| 1447 | if (minor != AM_MODULE) { | ||
| 1448 | retval = -EBADRQC; | ||
| 1449 | break; | ||
| 1450 | } | ||
| 1451 | retval = sst_ioctl_tuning_params(arg); | ||
| 1452 | break; | ||
| 1453 | |||
| 1454 | default: | ||
| 1455 | retval = -EINVAL; | ||
| 1456 | } | ||
| 1457 | pr_debug("intel_sst_ioctl:complete ret code = %d\n", retval); | ||
| 1458 | return retval; | ||
| 1459 | } | ||
| 1460 | |||
diff --git a/drivers/staging/intel_sst/intel_sst_common.h b/drivers/staging/intel_sst/intel_sst_common.h new file mode 100644 index 00000000000..870981ba3c9 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_common.h | |||
| @@ -0,0 +1,623 @@ | |||
| 1 | #ifndef __INTEL_SST_COMMON_H__ | ||
| 2 | #define __INTEL_SST_COMMON_H__ | ||
| 3 | /* | ||
| 4 | * intel_sst_common.h - Intel SST Driver for audio engine | ||
| 5 | * | ||
| 6 | * Copyright (C) 2008-10 Intel Corporation | ||
| 7 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 8 | * Harsha Priya <priya.harsha@intel.com> | ||
| 9 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 10 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 11 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License as published by | ||
| 15 | * the Free Software Foundation; version 2 of the License. | ||
| 16 | * | ||
| 17 | * This program is distributed in the hope that it will be useful, but | ||
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 20 | * General Public License for more details. | ||
| 21 | * | ||
| 22 | * You should have received a copy of the GNU General Public License along | ||
| 23 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 24 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 25 | * | ||
| 26 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 27 | * | ||
| 28 | * Common private declarations for SST | ||
| 29 | */ | ||
| 30 | |||
| 31 | #define SST_DRIVER_VERSION "1.2.17" | ||
| 32 | #define SST_VERSION_NUM 0x1217 | ||
| 33 | |||
| 34 | /* driver names */ | ||
| 35 | #define SST_DRV_NAME "intel_sst_driver" | ||
| 36 | #define SST_MRST_PCI_ID 0x080A | ||
| 37 | #define SST_MFLD_PCI_ID 0x082F | ||
| 38 | #define PCI_ID_LENGTH 4 | ||
| 39 | #define SST_SUSPEND_DELAY 2000 | ||
| 40 | #define FW_CONTEXT_MEM (64*1024) | ||
| 41 | |||
| 42 | enum sst_states { | ||
| 43 | SST_FW_LOADED = 1, | ||
| 44 | SST_FW_RUNNING, | ||
| 45 | SST_UN_INIT, | ||
| 46 | SST_ERROR, | ||
| 47 | SST_SUSPENDED | ||
| 48 | }; | ||
| 49 | |||
| 50 | #define MAX_ACTIVE_STREAM 3 | ||
| 51 | #define MAX_ENC_STREAM 1 | ||
| 52 | #define MAX_AM_HANDLES 1 | ||
| 53 | #define ALLOC_TIMEOUT 5000 | ||
| 54 | /* SST numbers */ | ||
| 55 | #define SST_BLOCK_TIMEOUT 5000 | ||
| 56 | #define TARGET_DEV_BLOCK_TIMEOUT 5000 | ||
| 57 | |||
| 58 | #define BLOCK_UNINIT -1 | ||
| 59 | #define RX_TIMESLOT_UNINIT -1 | ||
| 60 | |||
| 61 | /* SST register map */ | ||
| 62 | #define SST_CSR 0x00 | ||
| 63 | #define SST_PISR 0x08 | ||
| 64 | #define SST_PIMR 0x10 | ||
| 65 | #define SST_ISRX 0x18 | ||
| 66 | #define SST_IMRX 0x28 | ||
| 67 | #define SST_IPCX 0x38 /* IPC IA-SST */ | ||
| 68 | #define SST_IPCD 0x40 /* IPC SST-IA */ | ||
| 69 | #define SST_ISRD 0x20 /* dummy register for shim workaround */ | ||
| 70 | #define SST_SHIM_SIZE 0X44 | ||
| 71 | |||
| 72 | #define SPI_MODE_ENABLE_BASE_ADDR 0xffae4000 | ||
| 73 | #define FW_SIGNATURE_SIZE 4 | ||
| 74 | |||
| 75 | /* PMIC and SST hardware states */ | ||
| 76 | enum sst_mad_states { | ||
| 77 | SND_MAD_UN_INIT = 0, | ||
| 78 | SND_MAD_INIT_DONE, | ||
| 79 | }; | ||
| 80 | |||
| 81 | /* stream states */ | ||
| 82 | enum sst_stream_states { | ||
| 83 | STREAM_UN_INIT = 0, /* Freed/Not used stream */ | ||
| 84 | STREAM_RUNNING = 1, /* Running */ | ||
| 85 | STREAM_PAUSED = 2, /* Paused stream */ | ||
| 86 | STREAM_DECODE = 3, /* stream is in decoding only state */ | ||
| 87 | STREAM_INIT = 4, /* stream init, waiting for data */ | ||
| 88 | }; | ||
| 89 | |||
| 90 | |||
| 91 | enum sst_ram_type { | ||
| 92 | SST_IRAM = 1, | ||
| 93 | SST_DRAM = 2, | ||
| 94 | }; | ||
| 95 | /* SST shim registers to structure mapping */ | ||
| 96 | union config_status_reg { | ||
| 97 | struct { | ||
| 98 | u32 mfld_strb:1; | ||
| 99 | u32 sst_reset:1; | ||
| 100 | u32 hw_rsvd:3; | ||
| 101 | u32 sst_clk:2; | ||
| 102 | u32 bypass:3; | ||
| 103 | u32 run_stall:1; | ||
| 104 | u32 rsvd1:2; | ||
| 105 | u32 strb_cntr_rst:1; | ||
| 106 | u32 rsvd:18; | ||
| 107 | } part; | ||
| 108 | u32 full; | ||
| 109 | }; | ||
| 110 | |||
| 111 | union interrupt_reg { | ||
| 112 | struct { | ||
| 113 | u32 done_interrupt:1; | ||
| 114 | u32 busy_interrupt:1; | ||
| 115 | u32 rsvd:30; | ||
| 116 | } part; | ||
| 117 | u32 full; | ||
| 118 | }; | ||
| 119 | |||
| 120 | union sst_pisr_reg { | ||
| 121 | struct { | ||
| 122 | u32 pssp0:1; | ||
| 123 | u32 pssp1:1; | ||
| 124 | u32 rsvd0:3; | ||
| 125 | u32 dmac:1; | ||
| 126 | u32 rsvd1:26; | ||
| 127 | } part; | ||
| 128 | u32 full; | ||
| 129 | }; | ||
| 130 | |||
| 131 | union sst_pimr_reg { | ||
| 132 | struct { | ||
| 133 | u32 ssp0:1; | ||
| 134 | u32 ssp1:1; | ||
| 135 | u32 rsvd0:3; | ||
| 136 | u32 dmac:1; | ||
| 137 | u32 rsvd1:10; | ||
| 138 | u32 ssp0_sc:1; | ||
| 139 | u32 ssp1_sc:1; | ||
| 140 | u32 rsvd2:3; | ||
| 141 | u32 dmac_sc:1; | ||
| 142 | u32 rsvd3:10; | ||
| 143 | } part; | ||
| 144 | u32 full; | ||
| 145 | }; | ||
| 146 | |||
| 147 | |||
| 148 | struct sst_stream_bufs { | ||
| 149 | struct list_head node; | ||
| 150 | u32 size; | ||
| 151 | const char *addr; | ||
| 152 | u32 data_copied; | ||
| 153 | bool in_use; | ||
| 154 | u32 offset; | ||
| 155 | }; | ||
| 156 | |||
| 157 | struct snd_sst_user_cap_list { | ||
| 158 | unsigned int iov_index; /* index of iov */ | ||
| 159 | unsigned long iov_offset; /* offset in iov */ | ||
| 160 | unsigned long offset; /* offset in kmem */ | ||
| 161 | unsigned long size; /* size copied */ | ||
| 162 | struct list_head node; | ||
| 163 | }; | ||
| 164 | /* | ||
| 165 | This structure is used to block a user/fw data call to another | ||
| 166 | fw/user call | ||
| 167 | */ | ||
| 168 | struct sst_block { | ||
| 169 | bool condition; /* condition for blocking check */ | ||
| 170 | int ret_code; /* ret code when block is released */ | ||
| 171 | void *data; /* data to be appsed for block if any */ | ||
| 172 | bool on; | ||
| 173 | }; | ||
| 174 | |||
| 175 | enum snd_sst_buf_type { | ||
| 176 | SST_BUF_USER_STATIC = 1, | ||
| 177 | SST_BUF_USER_DYNAMIC, | ||
| 178 | SST_BUF_MMAP_STATIC, | ||
| 179 | SST_BUF_MMAP_DYNAMIC, | ||
| 180 | }; | ||
| 181 | |||
| 182 | enum snd_src { | ||
| 183 | SST_DRV = 1, | ||
| 184 | MAD_DRV = 2 | ||
| 185 | }; | ||
| 186 | |||
| 187 | /** | ||
| 188 | * struct stream_info - structure that holds the stream information | ||
| 189 | * | ||
| 190 | * @status : stream current state | ||
| 191 | * @prev : stream prev state | ||
| 192 | * @codec : stream codec | ||
| 193 | * @sst_id : stream id | ||
| 194 | * @ops : stream operation pb/cp/drm... | ||
| 195 | * @bufs: stream buffer list | ||
| 196 | * @lock : stream mutex for protecting state | ||
| 197 | * @pcm_lock : spinlock for pcm path only | ||
| 198 | * @mmapped : is stream mmapped | ||
| 199 | * @sg_index : current stream user buffer index | ||
| 200 | * @cur_ptr : stream user buffer pointer | ||
| 201 | * @buf_entry : current user buffer | ||
| 202 | * @data_blk : stream block for data operations | ||
| 203 | * @ctrl_blk : stream block for ctrl operations | ||
| 204 | * @buf_type : stream user buffer type | ||
| 205 | * @pcm_substream : PCM substream | ||
| 206 | * @period_elapsed : PCM period elapsed callback | ||
| 207 | * @sfreq : stream sampling freq | ||
| 208 | * @decode_ibuf : Decoded i/p buffers pointer | ||
| 209 | * @decode_obuf : Decoded o/p buffers pointer | ||
| 210 | * @decode_isize : Decoded i/p buffers size | ||
| 211 | * @decode_osize : Decoded o/p buffers size | ||
| 212 | * @decode_ibuf_type : Decoded i/p buffer type | ||
| 213 | * @decode_obuf_type : Decoded o/p buffer type | ||
| 214 | * @idecode_alloc : Decode alloc index | ||
| 215 | * @need_draining : stream set for drain | ||
| 216 | * @str_type : stream type | ||
| 217 | * @curr_bytes : current bytes decoded | ||
| 218 | * @cumm_bytes : cummulative bytes decoded | ||
| 219 | * @str_type : stream type | ||
| 220 | * @src : stream source | ||
| 221 | * @device : output device type (medfield only) | ||
| 222 | * @pcm_slot : pcm slot value | ||
| 223 | */ | ||
| 224 | struct stream_info { | ||
| 225 | unsigned int status; | ||
| 226 | unsigned int prev; | ||
| 227 | u8 codec; | ||
| 228 | unsigned int sst_id; | ||
| 229 | unsigned int ops; | ||
| 230 | struct list_head bufs; | ||
| 231 | struct mutex lock; /* mutex */ | ||
| 232 | spinlock_t pcm_lock; | ||
| 233 | bool mmapped; | ||
| 234 | unsigned int sg_index; /* current buf Index */ | ||
| 235 | unsigned char __user *cur_ptr; /* Current static bufs */ | ||
| 236 | struct snd_sst_buf_entry __user *buf_entry; | ||
| 237 | struct sst_block data_blk; /* stream ops block */ | ||
| 238 | struct sst_block ctrl_blk; /* stream control cmd block */ | ||
| 239 | enum snd_sst_buf_type buf_type; | ||
| 240 | void *pcm_substream; | ||
| 241 | void (*period_elapsed) (void *pcm_substream); | ||
| 242 | unsigned int sfreq; | ||
| 243 | void *decode_ibuf, *decode_obuf; | ||
| 244 | unsigned int decode_isize, decode_osize; | ||
| 245 | u8 decode_ibuf_type, decode_obuf_type; | ||
| 246 | unsigned int idecode_alloc; | ||
| 247 | unsigned int need_draining; | ||
| 248 | unsigned int str_type; | ||
| 249 | u32 curr_bytes; | ||
| 250 | u32 cumm_bytes; | ||
| 251 | u32 src; | ||
| 252 | enum snd_sst_audio_device_type device; | ||
| 253 | u8 pcm_slot; | ||
| 254 | }; | ||
| 255 | |||
| 256 | /* | ||
| 257 | * struct stream_alloc_bloc - this structure is used for blocking the user's | ||
| 258 | * alloc calls to fw's response to alloc calls | ||
| 259 | * | ||
| 260 | * @sst_id : session id of blocked stream | ||
| 261 | * @ops_block : ops block struture | ||
| 262 | */ | ||
| 263 | struct stream_alloc_block { | ||
| 264 | int sst_id; /* session id of blocked stream */ | ||
| 265 | struct sst_block ops_block; /* ops block struture */ | ||
| 266 | }; | ||
| 267 | |||
| 268 | #define SST_FW_SIGN "$SST" | ||
| 269 | #define SST_FW_LIB_SIGN "$LIB" | ||
| 270 | |||
| 271 | /* | ||
| 272 | * struct fw_header - FW file headers | ||
| 273 | * | ||
| 274 | * @signature : FW signature | ||
| 275 | * @modules : # of modules | ||
| 276 | * @file_format : version of header format | ||
| 277 | * @reserved : reserved fields | ||
| 278 | */ | ||
| 279 | struct fw_header { | ||
| 280 | unsigned char signature[FW_SIGNATURE_SIZE]; /* FW signature */ | ||
| 281 | u32 file_size; /* size of fw minus this header */ | ||
| 282 | u32 modules; /* # of modules */ | ||
| 283 | u32 file_format; /* version of header format */ | ||
| 284 | u32 reserved[4]; | ||
| 285 | }; | ||
| 286 | |||
| 287 | struct fw_module_header { | ||
| 288 | unsigned char signature[FW_SIGNATURE_SIZE]; /* module signature */ | ||
| 289 | u32 mod_size; /* size of module */ | ||
| 290 | u32 blocks; /* # of blocks */ | ||
| 291 | u32 type; /* codec type, pp lib */ | ||
| 292 | u32 entry_point; | ||
| 293 | }; | ||
| 294 | |||
| 295 | struct dma_block_info { | ||
| 296 | enum sst_ram_type type; /* IRAM/DRAM */ | ||
| 297 | u32 size; /* Bytes */ | ||
| 298 | u32 ram_offset; /* Offset in I/DRAM */ | ||
| 299 | u32 rsvd; /* Reserved field */ | ||
| 300 | }; | ||
| 301 | |||
| 302 | struct ioctl_pvt_data { | ||
| 303 | int str_id; | ||
| 304 | int pvt_id; | ||
| 305 | }; | ||
| 306 | |||
| 307 | struct sst_ipc_msg_wq { | ||
| 308 | union ipc_header header; | ||
| 309 | char mailbox[SST_MAILBOX_SIZE]; | ||
| 310 | struct work_struct wq; | ||
| 311 | }; | ||
| 312 | |||
| 313 | struct mad_ops_wq { | ||
| 314 | int stream_id; | ||
| 315 | enum sst_controls control_op; | ||
| 316 | struct work_struct wq; | ||
| 317 | |||
| 318 | }; | ||
| 319 | |||
| 320 | #define SST_MMAP_PAGES (640*1024 / PAGE_SIZE) | ||
| 321 | #define SST_MMAP_STEP (40*1024 / PAGE_SIZE) | ||
| 322 | |||
| 323 | /*** | ||
| 324 | * struct intel_sst_drv - driver ops | ||
| 325 | * | ||
| 326 | * @pmic_state : pmic state | ||
| 327 | * @pmic_vendor : pmic vendor detected | ||
| 328 | * @sst_state : current sst device state | ||
| 329 | * @pci_id : PCI device id loaded | ||
| 330 | * @shim : SST shim pointer | ||
| 331 | * @mailbox : SST mailbox pointer | ||
| 332 | * @iram : SST IRAM pointer | ||
| 333 | * @dram : SST DRAM pointer | ||
| 334 | * @shim_phy_add : SST shim phy addr | ||
| 335 | * @ipc_dispatch_list : ipc messages dispatched | ||
| 336 | * @ipc_post_msg_wq : wq to post IPC messages context | ||
| 337 | * @ipc_process_msg : wq to process msgs from FW context | ||
| 338 | * @ipc_process_reply : wq to process reply from FW context | ||
| 339 | * @ipc_post_msg : wq to post reply from FW context | ||
| 340 | * @mad_ops : MAD driver operations registered | ||
| 341 | * @mad_wq : MAD driver wq | ||
| 342 | * @post_msg_wq : wq to post IPC messages | ||
| 343 | * @process_msg_wq : wq to process msgs from FW | ||
| 344 | * @process_reply_wq : wq to process reply from FW | ||
| 345 | * @streams : sst stream contexts | ||
| 346 | * @alloc_block : block structure for alloc | ||
| 347 | * @tgt_dev_blk : block structure for target device | ||
| 348 | * @fw_info_blk : block structure for fw info block | ||
| 349 | * @vol_info_blk : block structure for vol info block | ||
| 350 | * @mute_info_blk : block structure for mute info block | ||
| 351 | * @hs_info_blk : block structure for hs info block | ||
| 352 | * @list_lock : sst driver list lock (deprecated) | ||
| 353 | * @list_spin_lock : sst driver spin lock block | ||
| 354 | * @scard_ops : sst card ops | ||
| 355 | * @pci : sst pci device struture | ||
| 356 | * @active_streams : sst active streams | ||
| 357 | * @sst_lock : sst device lock | ||
| 358 | * @stream_lock : sst stream lock | ||
| 359 | * @unique_id : sst unique id | ||
| 360 | * @stream_cnt : total sst active stream count | ||
| 361 | * @pb_streams : total active pb streams | ||
| 362 | * @cp_streams : total active cp streams | ||
| 363 | * @lpe_stalled : lpe stall status | ||
| 364 | * @pmic_port_instance : active pmic port instance | ||
| 365 | * @rx_time_slot_status : active rx slot | ||
| 366 | * @lpaudio_start : lpaudio status | ||
| 367 | * @audio_start : audio status | ||
| 368 | * @devt_d : pointer to /dev/lpe node | ||
| 369 | * @devt_c : pointer to /dev/lpe_ctrl node | ||
| 370 | * @max_streams : max streams allowed | ||
| 371 | */ | ||
| 372 | struct intel_sst_drv { | ||
| 373 | bool pmic_state; | ||
| 374 | int pmic_vendor; | ||
| 375 | int sst_state; | ||
| 376 | unsigned int pci_id; | ||
| 377 | void __iomem *shim; | ||
| 378 | void __iomem *mailbox; | ||
| 379 | void __iomem *iram; | ||
| 380 | void __iomem *dram; | ||
| 381 | unsigned int shim_phy_add; | ||
| 382 | struct list_head ipc_dispatch_list; | ||
| 383 | struct work_struct ipc_post_msg_wq; | ||
| 384 | struct sst_ipc_msg_wq ipc_process_msg; | ||
| 385 | struct sst_ipc_msg_wq ipc_process_reply; | ||
| 386 | struct sst_ipc_msg_wq ipc_post_msg; | ||
| 387 | struct mad_ops_wq mad_ops; | ||
| 388 | wait_queue_head_t wait_queue; | ||
| 389 | struct workqueue_struct *mad_wq; | ||
| 390 | struct workqueue_struct *post_msg_wq; | ||
| 391 | struct workqueue_struct *process_msg_wq; | ||
| 392 | struct workqueue_struct *process_reply_wq; | ||
| 393 | |||
| 394 | struct stream_info streams[MAX_NUM_STREAMS]; | ||
| 395 | struct stream_alloc_block alloc_block[MAX_ACTIVE_STREAM]; | ||
| 396 | struct sst_block tgt_dev_blk, fw_info_blk, ppp_params_blk, | ||
| 397 | vol_info_blk, mute_info_blk, hs_info_blk; | ||
| 398 | struct mutex list_lock;/* mutex for IPC list locking */ | ||
| 399 | spinlock_t list_spin_lock; /* mutex for IPC list locking */ | ||
| 400 | struct snd_pmic_ops *scard_ops; | ||
| 401 | struct pci_dev *pci; | ||
| 402 | int active_streams[MAX_NUM_STREAMS]; | ||
| 403 | void *mmap_mem; | ||
| 404 | struct mutex sst_lock; | ||
| 405 | struct mutex stream_lock; | ||
| 406 | unsigned int mmap_len; | ||
| 407 | unsigned int unique_id; | ||
| 408 | unsigned int stream_cnt; /* total streams */ | ||
| 409 | unsigned int encoded_cnt; /* enocded streams only */ | ||
| 410 | unsigned int am_cnt; | ||
| 411 | unsigned int pb_streams; /* pb streams active */ | ||
| 412 | unsigned int cp_streams; /* cp streams active */ | ||
| 413 | unsigned int lpe_stalled; /* LPE is stalled or not */ | ||
| 414 | unsigned int pmic_port_instance; /*pmic port instance*/ | ||
| 415 | int rx_time_slot_status; | ||
| 416 | unsigned int lpaudio_start; | ||
| 417 | /* 1 - LPA stream(MP3 pb) in progress*/ | ||
| 418 | unsigned int audio_start; | ||
| 419 | dev_t devt_d, devt_c; | ||
| 420 | unsigned int max_streams; | ||
| 421 | unsigned int *fw_cntx; | ||
| 422 | unsigned int fw_cntx_size; | ||
| 423 | |||
| 424 | unsigned int fw_downloaded; | ||
| 425 | }; | ||
| 426 | |||
| 427 | extern struct intel_sst_drv *sst_drv_ctx; | ||
| 428 | |||
| 429 | #define CHIP_REV_REG 0xff108000 | ||
| 430 | #define CHIP_REV_ADDR 0x78 | ||
| 431 | |||
| 432 | /* misc definitions */ | ||
| 433 | #define FW_DWNL_ID 0xFF | ||
| 434 | #define LOOP1 0x11111111 | ||
| 435 | #define LOOP2 0x22222222 | ||
| 436 | #define LOOP3 0x33333333 | ||
| 437 | #define LOOP4 0x44444444 | ||
| 438 | |||
| 439 | #define SST_DEFAULT_PMIC_PORT 1 /*audio port*/ | ||
| 440 | /* NOTE: status will have +ve for good cases and -ve for error ones */ | ||
| 441 | #define MAX_STREAM_FIELD 255 | ||
| 442 | |||
| 443 | int sst_alloc_stream(char *params, unsigned int stream_ops, u8 codec, | ||
| 444 | unsigned int session_id); | ||
| 445 | int sst_alloc_stream_response(unsigned int str_id, | ||
| 446 | struct snd_sst_alloc_response *response); | ||
| 447 | int sst_stalled(void); | ||
| 448 | int sst_pause_stream(int id); | ||
| 449 | int sst_resume_stream(int id); | ||
| 450 | int sst_enable_rx_timeslot(int status); | ||
| 451 | int sst_drop_stream(int id); | ||
| 452 | int sst_free_stream(int id); | ||
| 453 | int sst_start_stream(int streamID); | ||
| 454 | int sst_play_frame(int streamID); | ||
| 455 | int sst_pcm_play_frame(int str_id, struct sst_stream_bufs *sst_buf); | ||
| 456 | int sst_capture_frame(int streamID); | ||
| 457 | int sst_set_stream_param(int streamID, struct snd_sst_params *str_param); | ||
| 458 | int sst_target_device_select(struct snd_sst_target_device *target_device); | ||
| 459 | int sst_decode(int str_id, struct snd_sst_dbufs *dbufs); | ||
| 460 | int sst_get_decoded_bytes(int str_id, unsigned long long *bytes); | ||
| 461 | int sst_get_fw_info(struct snd_sst_fw_info *info); | ||
| 462 | int sst_get_stream_params(int str_id, | ||
| 463 | struct snd_sst_get_stream_params *get_params); | ||
| 464 | int sst_get_stream(struct snd_sst_params *str_param); | ||
| 465 | int sst_get_stream_allocated(struct snd_sst_params *str_param, | ||
| 466 | struct snd_sst_lib_download **lib_dnld); | ||
| 467 | int sst_drain_stream(int str_id); | ||
| 468 | int sst_get_vol(struct snd_sst_vol *set_vol); | ||
| 469 | int sst_set_vol(struct snd_sst_vol *set_vol); | ||
| 470 | int sst_set_mute(struct snd_sst_mute *set_mute); | ||
| 471 | |||
| 472 | |||
| 473 | void sst_post_message(struct work_struct *work); | ||
| 474 | void sst_process_message(struct work_struct *work); | ||
| 475 | void sst_process_reply(struct work_struct *work); | ||
| 476 | void sst_process_mad_ops(struct work_struct *work); | ||
| 477 | void sst_process_mad_jack_detection(struct work_struct *work); | ||
| 478 | |||
| 479 | long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd, | ||
| 480 | unsigned long arg); | ||
| 481 | int intel_sst_open(struct inode *i_node, struct file *file_ptr); | ||
| 482 | int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr); | ||
| 483 | int intel_sst_release(struct inode *i_node, struct file *file_ptr); | ||
| 484 | int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr); | ||
| 485 | int intel_sst_read(struct file *file_ptr, char __user *buf, | ||
| 486 | size_t count, loff_t *ppos); | ||
| 487 | int intel_sst_write(struct file *file_ptr, const char __user *buf, | ||
| 488 | size_t count, loff_t *ppos); | ||
| 489 | int intel_sst_mmap(struct file *fp, struct vm_area_struct *vma); | ||
| 490 | ssize_t intel_sst_aio_write(struct kiocb *kiocb, const struct iovec *iov, | ||
| 491 | unsigned long nr_segs, loff_t offset); | ||
| 492 | ssize_t intel_sst_aio_read(struct kiocb *kiocb, const struct iovec *iov, | ||
| 493 | unsigned long nr_segs, loff_t offset); | ||
| 494 | |||
| 495 | int sst_load_fw(const struct firmware *fw, void *context); | ||
| 496 | int sst_load_library(struct snd_sst_lib_download *lib, u8 ops); | ||
| 497 | int sst_spi_mode_enable(void); | ||
| 498 | int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx); | ||
| 499 | |||
| 500 | int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, | ||
| 501 | struct sst_block *block); | ||
| 502 | int sst_wait_interruptible_timeout(struct intel_sst_drv *sst_drv_ctx, | ||
| 503 | struct sst_block *block, int timeout); | ||
| 504 | int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, | ||
| 505 | struct stream_alloc_block *block); | ||
| 506 | int sst_create_large_msg(struct ipc_post **arg); | ||
| 507 | int sst_create_short_msg(struct ipc_post **arg); | ||
| 508 | void sst_wake_up_alloc_block(struct intel_sst_drv *sst_drv_ctx, | ||
| 509 | u8 sst_id, int status, void *data); | ||
| 510 | void sst_clear_interrupt(void); | ||
| 511 | int intel_sst_resume(struct pci_dev *pci); | ||
| 512 | int sst_download_fw(void); | ||
| 513 | void free_stream_context(unsigned int str_id); | ||
| 514 | void sst_clean_stream(struct stream_info *stream); | ||
| 515 | |||
| 516 | /* | ||
| 517 | * sst_fill_header - inline to fill sst header | ||
| 518 | * | ||
| 519 | * @header : ipc header | ||
| 520 | * @msg : IPC message to be sent | ||
| 521 | * @large : is ipc large msg | ||
| 522 | * @str_id : stream id | ||
| 523 | * | ||
| 524 | * this function is an inline function that sets the headers before | ||
| 525 | * sending a message | ||
| 526 | */ | ||
| 527 | static inline void sst_fill_header(union ipc_header *header, | ||
| 528 | int msg, int large, int str_id) | ||
| 529 | { | ||
| 530 | header->part.msg_id = msg; | ||
| 531 | header->part.str_id = str_id; | ||
| 532 | header->part.large = large; | ||
| 533 | header->part.done = 0; | ||
| 534 | header->part.busy = 1; | ||
| 535 | header->part.data = 0; | ||
| 536 | } | ||
| 537 | |||
| 538 | /* | ||
| 539 | * sst_assign_pvt_id - assign a pvt id for stream | ||
| 540 | * | ||
| 541 | * @sst_drv_ctx : driver context | ||
| 542 | * | ||
| 543 | * this inline function assigns a private id for calls that dont have stream | ||
| 544 | * context yet, should be called with lock held | ||
| 545 | */ | ||
| 546 | static inline unsigned int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx) | ||
| 547 | { | ||
| 548 | sst_drv_ctx->unique_id++; | ||
| 549 | if (sst_drv_ctx->unique_id >= MAX_NUM_STREAMS) | ||
| 550 | sst_drv_ctx->unique_id = 1; | ||
| 551 | return sst_drv_ctx->unique_id; | ||
| 552 | } | ||
| 553 | |||
| 554 | /* | ||
| 555 | * sst_init_stream - this function initialzes stream context | ||
| 556 | * | ||
| 557 | * @stream : stream struture | ||
| 558 | * @codec : codec for stream | ||
| 559 | * @sst_id : stream id | ||
| 560 | * @ops : stream operation | ||
| 561 | * @slot : stream pcm slot | ||
| 562 | * @device : device type | ||
| 563 | * | ||
| 564 | * this inline function initialzes stream context for allocated stream | ||
| 565 | */ | ||
| 566 | static inline void sst_init_stream(struct stream_info *stream, | ||
| 567 | int codec, int sst_id, int ops, u8 slot, | ||
| 568 | enum snd_sst_audio_device_type device) | ||
| 569 | { | ||
| 570 | stream->status = STREAM_INIT; | ||
| 571 | stream->prev = STREAM_UN_INIT; | ||
| 572 | stream->codec = codec; | ||
| 573 | stream->sst_id = sst_id; | ||
| 574 | stream->str_type = 0; | ||
| 575 | stream->ops = ops; | ||
| 576 | stream->data_blk.on = false; | ||
| 577 | stream->data_blk.condition = false; | ||
| 578 | stream->data_blk.ret_code = 0; | ||
| 579 | stream->data_blk.data = NULL; | ||
| 580 | stream->ctrl_blk.on = false; | ||
| 581 | stream->ctrl_blk.condition = false; | ||
| 582 | stream->ctrl_blk.ret_code = 0; | ||
| 583 | stream->ctrl_blk.data = NULL; | ||
| 584 | stream->need_draining = false; | ||
| 585 | stream->decode_ibuf = NULL; | ||
| 586 | stream->decode_isize = 0; | ||
| 587 | stream->mmapped = false; | ||
| 588 | stream->pcm_slot = slot; | ||
| 589 | stream->device = device; | ||
| 590 | } | ||
| 591 | |||
| 592 | |||
| 593 | /* | ||
| 594 | * sst_validate_strid - this function validates the stream id | ||
| 595 | * | ||
| 596 | * @str_id : stream id to be validated | ||
| 597 | * | ||
| 598 | * returns 0 if valid stream | ||
| 599 | */ | ||
| 600 | static inline int sst_validate_strid(int str_id) | ||
| 601 | { | ||
| 602 | if (str_id <= 0 || str_id > sst_drv_ctx->max_streams) { | ||
| 603 | pr_err("SST ERR: invalid stream id : %d MAX_STREAMS:%d\n", | ||
| 604 | str_id, sst_drv_ctx->max_streams); | ||
| 605 | return -EINVAL; | ||
| 606 | } else | ||
| 607 | return 0; | ||
| 608 | } | ||
| 609 | |||
| 610 | static inline int sst_shim_write(void __iomem *addr, int offset, int value) | ||
| 611 | { | ||
| 612 | |||
| 613 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) | ||
| 614 | writel(value, addr + SST_ISRD); /*dummy*/ | ||
| 615 | writel(value, addr + offset); | ||
| 616 | return 0; | ||
| 617 | } | ||
| 618 | |||
| 619 | static inline int sst_shim_read(void __iomem *addr, int offset) | ||
| 620 | { | ||
| 621 | return readl(addr + offset); | ||
| 622 | } | ||
| 623 | #endif /* __INTEL_SST_COMMON_H__ */ | ||
diff --git a/drivers/staging/intel_sst/intel_sst_drv_interface.c b/drivers/staging/intel_sst/intel_sst_drv_interface.c new file mode 100644 index 00000000000..69daa1404b6 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_drv_interface.c | |||
| @@ -0,0 +1,563 @@ | |||
| 1 | /* | ||
| 2 | * intel_sst_interface.c - Intel SST Driver for audio engine | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com) | ||
| 8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; version 2 of the License. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, but | ||
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 17 | * General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License along | ||
| 20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 21 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 22 | * | ||
| 23 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 24 | * This driver exposes the audio engine functionalities to the ALSA | ||
| 25 | * and middleware. | ||
| 26 | * Upper layer interfaces (MAD driver, MMF) to SST driver | ||
| 27 | */ | ||
| 28 | |||
| 29 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 30 | |||
| 31 | #include <linux/delay.h> | ||
| 32 | #include <linux/pci.h> | ||
| 33 | #include <linux/fs.h> | ||
| 34 | #include <linux/firmware.h> | ||
| 35 | #include <linux/pm_runtime.h> | ||
| 36 | #include "intel_sst.h" | ||
| 37 | #include "intel_sst_ioctl.h" | ||
| 38 | #include "intel_sst_fw_ipc.h" | ||
| 39 | #include "intel_sst_common.h" | ||
| 40 | |||
| 41 | |||
| 42 | /* | ||
| 43 | * sst_download_fw - download the audio firmware to DSP | ||
| 44 | * | ||
| 45 | * This function is called when the FW needs to be downloaded to SST DSP engine | ||
| 46 | */ | ||
| 47 | int sst_download_fw(void) | ||
| 48 | { | ||
| 49 | int retval; | ||
| 50 | const struct firmware *fw_sst; | ||
| 51 | char name[20]; | ||
| 52 | |||
| 53 | if (sst_drv_ctx->sst_state != SST_UN_INIT) | ||
| 54 | return -EPERM; | ||
| 55 | |||
| 56 | /* Reload firmware is not needed for MRST */ | ||
| 57 | if ( (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) && sst_drv_ctx->fw_downloaded) { | ||
| 58 | pr_debug("FW already downloaded, skip for MRST platform\n"); | ||
| 59 | sst_drv_ctx->sst_state = SST_FW_RUNNING; | ||
| 60 | return 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | snprintf(name, sizeof(name), "%s%04x%s", "fw_sst_", | ||
| 64 | sst_drv_ctx->pci_id, ".bin"); | ||
| 65 | |||
| 66 | pr_debug("Downloading %s FW now...\n", name); | ||
| 67 | retval = request_firmware(&fw_sst, name, &sst_drv_ctx->pci->dev); | ||
| 68 | if (retval) { | ||
| 69 | pr_err("request fw failed %d\n", retval); | ||
| 70 | return retval; | ||
| 71 | } | ||
| 72 | sst_drv_ctx->alloc_block[0].sst_id = FW_DWNL_ID; | ||
| 73 | sst_drv_ctx->alloc_block[0].ops_block.condition = false; | ||
| 74 | retval = sst_load_fw(fw_sst, NULL); | ||
| 75 | if (retval) | ||
| 76 | goto end_restore; | ||
| 77 | |||
| 78 | retval = sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[0]); | ||
| 79 | if (retval) | ||
| 80 | pr_err("fw download failed %d\n" , retval); | ||
| 81 | else | ||
| 82 | sst_drv_ctx->fw_downloaded = 1; | ||
| 83 | |||
| 84 | end_restore: | ||
| 85 | release_firmware(fw_sst); | ||
| 86 | sst_drv_ctx->alloc_block[0].sst_id = BLOCK_UNINIT; | ||
| 87 | return retval; | ||
| 88 | } | ||
| 89 | |||
| 90 | |||
| 91 | /* | ||
| 92 | * sst_stalled - this function checks if the lpe is in stalled state | ||
| 93 | */ | ||
| 94 | int sst_stalled(void) | ||
| 95 | { | ||
| 96 | int retry = 1000; | ||
| 97 | int retval = -1; | ||
| 98 | |||
| 99 | while (retry) { | ||
| 100 | if (!sst_drv_ctx->lpe_stalled) | ||
| 101 | return 0; | ||
| 102 | /*wait for time and re-check*/ | ||
| 103 | msleep(1); | ||
| 104 | |||
| 105 | retry--; | ||
| 106 | } | ||
| 107 | pr_debug("in Stalled State\n"); | ||
| 108 | return retval; | ||
| 109 | } | ||
| 110 | |||
| 111 | void free_stream_context(unsigned int str_id) | ||
| 112 | { | ||
| 113 | struct stream_info *stream; | ||
| 114 | |||
| 115 | if (!sst_validate_strid(str_id)) { | ||
| 116 | /* str_id is valid, so stream is alloacted */ | ||
| 117 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 118 | if (sst_free_stream(str_id)) | ||
| 119 | sst_clean_stream(&sst_drv_ctx->streams[str_id]); | ||
| 120 | if (stream->ops == STREAM_OPS_PLAYBACK || | ||
| 121 | stream->ops == STREAM_OPS_PLAYBACK_DRM) { | ||
| 122 | sst_drv_ctx->pb_streams--; | ||
| 123 | if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) | ||
| 124 | sst_drv_ctx->scard_ops->power_down_pmic_pb( | ||
| 125 | stream->device); | ||
| 126 | else { | ||
| 127 | if (sst_drv_ctx->pb_streams == 0) | ||
| 128 | sst_drv_ctx->scard_ops-> | ||
| 129 | power_down_pmic_pb(stream->device); | ||
| 130 | } | ||
| 131 | } else if (stream->ops == STREAM_OPS_CAPTURE) { | ||
| 132 | sst_drv_ctx->cp_streams--; | ||
| 133 | if (sst_drv_ctx->cp_streams == 0) | ||
| 134 | sst_drv_ctx->scard_ops->power_down_pmic_cp( | ||
| 135 | stream->device); | ||
| 136 | } | ||
| 137 | if (sst_drv_ctx->pb_streams == 0 | ||
| 138 | && sst_drv_ctx->cp_streams == 0) | ||
| 139 | sst_drv_ctx->scard_ops->power_down_pmic(); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | /* | ||
| 144 | * sst_get_stream_allocated - this function gets a stream allocated with | ||
| 145 | * the given params | ||
| 146 | * | ||
| 147 | * @str_param : stream params | ||
| 148 | * @lib_dnld : pointer to pointer of lib downlaod struct | ||
| 149 | * | ||
| 150 | * This creates new stream id for a stream, in case lib is to be downloaded to | ||
| 151 | * DSP, it downloads that | ||
| 152 | */ | ||
| 153 | int sst_get_stream_allocated(struct snd_sst_params *str_param, | ||
| 154 | struct snd_sst_lib_download **lib_dnld) | ||
| 155 | { | ||
| 156 | int retval, str_id; | ||
| 157 | struct stream_info *str_info; | ||
| 158 | |||
| 159 | retval = sst_alloc_stream((char *) &str_param->sparams, str_param->ops, | ||
| 160 | str_param->codec, str_param->device_type); | ||
| 161 | if (retval < 0) { | ||
| 162 | pr_err("sst_alloc_stream failed %d\n", retval); | ||
| 163 | return retval; | ||
| 164 | } | ||
| 165 | pr_debug("Stream allocated %d\n", retval); | ||
| 166 | str_id = retval; | ||
| 167 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 168 | /* Block the call for reply */ | ||
| 169 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 170 | &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); | ||
| 171 | if ((retval != 0) || (str_info->ctrl_blk.ret_code != 0)) { | ||
| 172 | pr_debug("FW alloc failed retval %d, ret_code %d\n", | ||
| 173 | retval, str_info->ctrl_blk.ret_code); | ||
| 174 | str_id = -str_info->ctrl_blk.ret_code; /*return error*/ | ||
| 175 | *lib_dnld = str_info->ctrl_blk.data; | ||
| 176 | sst_clean_stream(str_info); | ||
| 177 | } else | ||
| 178 | pr_debug("FW Stream allocated success\n"); | ||
| 179 | return str_id; /*will ret either error (in above if) or correct str id*/ | ||
| 180 | } | ||
| 181 | |||
| 182 | /* | ||
| 183 | * sst_get_sfreq - this function returns the frequency of the stream | ||
| 184 | * | ||
| 185 | * @str_param : stream params | ||
| 186 | */ | ||
| 187 | static int sst_get_sfreq(struct snd_sst_params *str_param) | ||
| 188 | { | ||
| 189 | switch (str_param->codec) { | ||
| 190 | case SST_CODEC_TYPE_PCM: | ||
| 191 | return 48000; /*str_param->sparams.uc.pcm_params.sfreq;*/ | ||
| 192 | case SST_CODEC_TYPE_MP3: | ||
| 193 | return str_param->sparams.uc.mp3_params.sfreq; | ||
| 194 | case SST_CODEC_TYPE_AAC: | ||
| 195 | return str_param->sparams.uc.aac_params.sfreq; | ||
| 196 | case SST_CODEC_TYPE_WMA9: | ||
| 197 | return str_param->sparams.uc.wma_params.sfreq; | ||
| 198 | default: | ||
| 199 | return 0; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | /* | ||
| 204 | * sst_get_stream - this function prepares for stream allocation | ||
| 205 | * | ||
| 206 | * @str_param : stream param | ||
| 207 | */ | ||
| 208 | int sst_get_stream(struct snd_sst_params *str_param) | ||
| 209 | { | ||
| 210 | int i, retval; | ||
| 211 | struct stream_info *str_info; | ||
| 212 | struct snd_sst_lib_download *lib_dnld; | ||
| 213 | |||
| 214 | /* stream is not allocated, we are allocating */ | ||
| 215 | retval = sst_get_stream_allocated(str_param, &lib_dnld); | ||
| 216 | if (retval == -(SST_LIB_ERR_LIB_DNLD_REQUIRED)) { | ||
| 217 | /* codec download is required */ | ||
| 218 | struct snd_sst_alloc_response *response; | ||
| 219 | |||
| 220 | pr_debug("Codec is required.... trying that\n"); | ||
| 221 | if (lib_dnld == NULL) { | ||
| 222 | pr_err("lib download null!!! abort\n"); | ||
| 223 | return -EIO; | ||
| 224 | } | ||
| 225 | i = sst_get_block_stream(sst_drv_ctx); | ||
| 226 | response = sst_drv_ctx->alloc_block[i].ops_block.data; | ||
| 227 | pr_debug("alloc block allocated = %d\n", i); | ||
| 228 | if (i < 0) { | ||
| 229 | kfree(lib_dnld); | ||
| 230 | return -ENOMEM; | ||
| 231 | } | ||
| 232 | retval = sst_load_library(lib_dnld, str_param->ops); | ||
| 233 | kfree(lib_dnld); | ||
| 234 | |||
| 235 | sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; | ||
| 236 | if (!retval) { | ||
| 237 | pr_debug("codec was downloaded successfully\n"); | ||
| 238 | |||
| 239 | retval = sst_get_stream_allocated(str_param, &lib_dnld); | ||
| 240 | if (retval <= 0) | ||
| 241 | goto err; | ||
| 242 | |||
| 243 | pr_debug("Alloc done stream id %d\n", retval); | ||
| 244 | } else { | ||
| 245 | pr_debug("codec download failed\n"); | ||
| 246 | retval = -EIO; | ||
| 247 | goto err; | ||
| 248 | } | ||
| 249 | } else if (retval <= 0) | ||
| 250 | goto err; | ||
| 251 | /*else | ||
| 252 | set_port_params(str_param, str_param->ops);*/ | ||
| 253 | |||
| 254 | /* store sampling freq */ | ||
| 255 | str_info = &sst_drv_ctx->streams[retval]; | ||
| 256 | str_info->sfreq = sst_get_sfreq(str_param); | ||
| 257 | |||
| 258 | /* power on the analog, if reqd */ | ||
| 259 | if (str_param->ops == STREAM_OPS_PLAYBACK || | ||
| 260 | str_param->ops == STREAM_OPS_PLAYBACK_DRM) { | ||
| 261 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) | ||
| 262 | sst_drv_ctx->scard_ops->power_up_pmic_pb( | ||
| 263 | sst_drv_ctx->pmic_port_instance); | ||
| 264 | else | ||
| 265 | sst_drv_ctx->scard_ops->power_up_pmic_pb( | ||
| 266 | str_info->device); | ||
| 267 | /*Only if the playback is MP3 - Send a message*/ | ||
| 268 | sst_drv_ctx->pb_streams++; | ||
| 269 | } else if (str_param->ops == STREAM_OPS_CAPTURE) { | ||
| 270 | |||
| 271 | sst_drv_ctx->scard_ops->power_up_pmic_cp( | ||
| 272 | sst_drv_ctx->pmic_port_instance); | ||
| 273 | /*Send a messageif not sent already*/ | ||
| 274 | sst_drv_ctx->cp_streams++; | ||
| 275 | } | ||
| 276 | |||
| 277 | err: | ||
| 278 | return retval; | ||
| 279 | } | ||
| 280 | |||
| 281 | void sst_process_mad_ops(struct work_struct *work) | ||
| 282 | { | ||
| 283 | |||
| 284 | struct mad_ops_wq *mad_ops = | ||
| 285 | container_of(work, struct mad_ops_wq, wq); | ||
| 286 | int retval = 0; | ||
| 287 | |||
| 288 | switch (mad_ops->control_op) { | ||
| 289 | case SST_SND_PAUSE: | ||
| 290 | retval = sst_pause_stream(mad_ops->stream_id); | ||
| 291 | break; | ||
| 292 | case SST_SND_RESUME: | ||
| 293 | retval = sst_resume_stream(mad_ops->stream_id); | ||
| 294 | break; | ||
| 295 | case SST_SND_DROP: | ||
| 296 | retval = sst_drop_stream(mad_ops->stream_id); | ||
| 297 | break; | ||
| 298 | case SST_SND_START: | ||
| 299 | pr_debug("SST Debug: start stream\n"); | ||
| 300 | retval = sst_start_stream(mad_ops->stream_id); | ||
| 301 | break; | ||
| 302 | case SST_SND_STREAM_PROCESS: | ||
| 303 | pr_debug("play/capt frames...\n"); | ||
| 304 | break; | ||
| 305 | default: | ||
| 306 | pr_err(" wrong control_ops reported\n"); | ||
| 307 | } | ||
| 308 | return; | ||
| 309 | } | ||
| 310 | |||
| 311 | void send_intial_rx_timeslot(void) | ||
| 312 | { | ||
| 313 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID && | ||
| 314 | sst_drv_ctx->rx_time_slot_status != RX_TIMESLOT_UNINIT | ||
| 315 | && sst_drv_ctx->pmic_vendor != SND_NC) | ||
| 316 | sst_enable_rx_timeslot(sst_drv_ctx->rx_time_slot_status); | ||
| 317 | } | ||
| 318 | |||
| 319 | /* | ||
| 320 | * sst_open_pcm_stream - Open PCM interface | ||
| 321 | * | ||
| 322 | * @str_param: parameters of pcm stream | ||
| 323 | * | ||
| 324 | * This function is called by MID sound card driver to open | ||
| 325 | * a new pcm interface | ||
| 326 | */ | ||
| 327 | int sst_open_pcm_stream(struct snd_sst_params *str_param) | ||
| 328 | { | ||
| 329 | struct stream_info *str_info; | ||
| 330 | int retval; | ||
| 331 | |||
| 332 | pm_runtime_get_sync(&sst_drv_ctx->pci->dev); | ||
| 333 | |||
| 334 | if (sst_drv_ctx->sst_state == SST_SUSPENDED) { | ||
| 335 | /* LPE is suspended, resume it before proceeding*/ | ||
| 336 | pr_debug("Resuming from Suspended state\n"); | ||
| 337 | retval = intel_sst_resume(sst_drv_ctx->pci); | ||
| 338 | if (retval) { | ||
| 339 | pr_err("Resume Failed = %#x, abort\n", retval); | ||
| 340 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 341 | return retval; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | if (sst_drv_ctx->sst_state == SST_UN_INIT) { | ||
| 345 | /* FW is not downloaded */ | ||
| 346 | pr_debug("DSP Downloading FW now...\n"); | ||
| 347 | retval = sst_download_fw(); | ||
| 348 | if (retval) { | ||
| 349 | pr_err("FW download fail %x, abort\n", retval); | ||
| 350 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 351 | return retval; | ||
| 352 | } | ||
| 353 | send_intial_rx_timeslot(); | ||
| 354 | } | ||
| 355 | |||
| 356 | if (!str_param) { | ||
| 357 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 358 | return -EINVAL; | ||
| 359 | } | ||
| 360 | |||
| 361 | retval = sst_get_stream(str_param); | ||
| 362 | if (retval > 0) { | ||
| 363 | sst_drv_ctx->stream_cnt++; | ||
| 364 | str_info = &sst_drv_ctx->streams[retval]; | ||
| 365 | str_info->src = MAD_DRV; | ||
| 366 | } else | ||
| 367 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 368 | |||
| 369 | return retval; | ||
| 370 | } | ||
| 371 | |||
| 372 | /* | ||
| 373 | * sst_close_pcm_stream - Close PCM interface | ||
| 374 | * | ||
| 375 | * @str_id: stream id to be closed | ||
| 376 | * | ||
| 377 | * This function is called by MID sound card driver to close | ||
| 378 | * an existing pcm interface | ||
| 379 | */ | ||
| 380 | int sst_close_pcm_stream(unsigned int str_id) | ||
| 381 | { | ||
| 382 | struct stream_info *stream; | ||
| 383 | |||
| 384 | pr_debug("sst: stream free called\n"); | ||
| 385 | if (sst_validate_strid(str_id)) | ||
| 386 | return -EINVAL; | ||
| 387 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 388 | free_stream_context(str_id); | ||
| 389 | stream->pcm_substream = NULL; | ||
| 390 | stream->status = STREAM_UN_INIT; | ||
| 391 | stream->period_elapsed = NULL; | ||
| 392 | sst_drv_ctx->stream_cnt--; | ||
| 393 | pr_debug("sst: will call runtime put now\n"); | ||
| 394 | pm_runtime_put(&sst_drv_ctx->pci->dev); | ||
| 395 | return 0; | ||
| 396 | } | ||
| 397 | |||
| 398 | /* | ||
| 399 | * sst_device_control - Set Control params | ||
| 400 | * | ||
| 401 | * @cmd: control cmd to be set | ||
| 402 | * @arg: command argument | ||
| 403 | * | ||
| 404 | * This function is called by MID sound card driver to set | ||
| 405 | * SST/Sound card controls for an opened stream. | ||
| 406 | * This is registered with MID driver | ||
| 407 | */ | ||
| 408 | int sst_device_control(int cmd, void *arg) | ||
| 409 | { | ||
| 410 | int retval = 0, str_id = 0; | ||
| 411 | |||
| 412 | switch (cmd) { | ||
| 413 | case SST_SND_PAUSE: | ||
| 414 | case SST_SND_RESUME: | ||
| 415 | case SST_SND_DROP: | ||
| 416 | case SST_SND_START: | ||
| 417 | sst_drv_ctx->mad_ops.control_op = cmd; | ||
| 418 | sst_drv_ctx->mad_ops.stream_id = *(int *)arg; | ||
| 419 | queue_work(sst_drv_ctx->mad_wq, &sst_drv_ctx->mad_ops.wq); | ||
| 420 | break; | ||
| 421 | |||
| 422 | case SST_SND_STREAM_INIT: { | ||
| 423 | struct pcm_stream_info *str_info; | ||
| 424 | struct stream_info *stream; | ||
| 425 | |||
| 426 | pr_debug("stream init called\n"); | ||
| 427 | str_info = (struct pcm_stream_info *)arg; | ||
| 428 | str_id = str_info->str_id; | ||
| 429 | retval = sst_validate_strid(str_id); | ||
| 430 | if (retval) | ||
| 431 | break; | ||
| 432 | |||
| 433 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 434 | pr_debug("setting the period ptrs\n"); | ||
| 435 | stream->pcm_substream = str_info->mad_substream; | ||
| 436 | stream->period_elapsed = str_info->period_elapsed; | ||
| 437 | stream->sfreq = str_info->sfreq; | ||
| 438 | stream->prev = stream->status; | ||
| 439 | stream->status = STREAM_INIT; | ||
| 440 | break; | ||
| 441 | } | ||
| 442 | |||
| 443 | case SST_SND_BUFFER_POINTER: { | ||
| 444 | struct pcm_stream_info *stream_info; | ||
| 445 | struct snd_sst_tstamp fw_tstamp = {0,}; | ||
| 446 | struct stream_info *stream; | ||
| 447 | |||
| 448 | |||
| 449 | stream_info = (struct pcm_stream_info *)arg; | ||
| 450 | str_id = stream_info->str_id; | ||
| 451 | retval = sst_validate_strid(str_id); | ||
| 452 | if (retval) | ||
| 453 | break; | ||
| 454 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 455 | |||
| 456 | if (!stream->pcm_substream) | ||
| 457 | break; | ||
| 458 | memcpy_fromio(&fw_tstamp, | ||
| 459 | ((void *)(sst_drv_ctx->mailbox + SST_TIME_STAMP) | ||
| 460 | +(str_id * sizeof(fw_tstamp))), | ||
| 461 | sizeof(fw_tstamp)); | ||
| 462 | |||
| 463 | pr_debug("Pointer Query on strid = %d ops %d\n", | ||
| 464 | str_id, stream->ops); | ||
| 465 | |||
| 466 | if (stream->ops == STREAM_OPS_PLAYBACK) | ||
| 467 | stream_info->buffer_ptr = fw_tstamp.samples_rendered; | ||
| 468 | else | ||
| 469 | stream_info->buffer_ptr = fw_tstamp.samples_processed; | ||
| 470 | pr_debug("Samples rendered = %llu, buffer ptr %llu\n", | ||
| 471 | fw_tstamp.samples_rendered, stream_info->buffer_ptr); | ||
| 472 | break; | ||
| 473 | } | ||
| 474 | case SST_ENABLE_RX_TIME_SLOT: { | ||
| 475 | int status = *(int *)arg; | ||
| 476 | sst_drv_ctx->rx_time_slot_status = status ; | ||
| 477 | sst_enable_rx_timeslot(status); | ||
| 478 | break; | ||
| 479 | } | ||
| 480 | default: | ||
| 481 | /* Illegal case */ | ||
| 482 | pr_warn("illegal req\n"); | ||
| 483 | return -EINVAL; | ||
| 484 | } | ||
| 485 | |||
| 486 | return retval; | ||
| 487 | } | ||
| 488 | |||
| 489 | |||
| 490 | struct intel_sst_pcm_control pcm_ops = { | ||
| 491 | .open = sst_open_pcm_stream, | ||
| 492 | .device_control = sst_device_control, | ||
| 493 | .close = sst_close_pcm_stream, | ||
| 494 | }; | ||
| 495 | |||
| 496 | struct intel_sst_card_ops sst_pmic_ops = { | ||
| 497 | .pcm_control = &pcm_ops, | ||
| 498 | }; | ||
| 499 | |||
| 500 | /* | ||
| 501 | * register_sst_card - function for sound card to register | ||
| 502 | * | ||
| 503 | * @card: pointer to structure of operations | ||
| 504 | * | ||
| 505 | * This function is called card driver loads and is ready for registration | ||
| 506 | */ | ||
| 507 | int register_sst_card(struct intel_sst_card_ops *card) | ||
| 508 | { | ||
| 509 | if (!sst_drv_ctx) { | ||
| 510 | pr_err("No SST driver register card reject\n"); | ||
| 511 | return -ENODEV; | ||
| 512 | } | ||
| 513 | |||
| 514 | if (!card || !card->module_name) { | ||
| 515 | pr_err("Null Pointer Passed\n"); | ||
| 516 | return -EINVAL; | ||
| 517 | } | ||
| 518 | if (sst_drv_ctx->pmic_state == SND_MAD_UN_INIT) { | ||
| 519 | /* register this driver */ | ||
| 520 | if ((strncmp(SST_CARD_NAMES, card->module_name, | ||
| 521 | strlen(SST_CARD_NAMES))) == 0) { | ||
| 522 | sst_drv_ctx->pmic_vendor = card->vendor_id; | ||
| 523 | sst_drv_ctx->scard_ops = card->scard_ops; | ||
| 524 | sst_pmic_ops.module_name = card->module_name; | ||
| 525 | sst_drv_ctx->pmic_state = SND_MAD_INIT_DONE; | ||
| 526 | sst_drv_ctx->rx_time_slot_status = 0; /*default AMIC*/ | ||
| 527 | card->pcm_control = sst_pmic_ops.pcm_control; | ||
| 528 | return 0; | ||
| 529 | } else { | ||
| 530 | pr_err("strcmp fail %s\n", card->module_name); | ||
| 531 | return -EINVAL; | ||
| 532 | } | ||
| 533 | |||
| 534 | } else { | ||
| 535 | /* already registered a driver */ | ||
| 536 | pr_err("Repeat for registration..denied\n"); | ||
| 537 | return -EBADRQC; | ||
| 538 | } | ||
| 539 | /* The ASoC code doesn't set scard_ops */ | ||
| 540 | if (sst_drv_ctx->scard_ops) | ||
| 541 | sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT; | ||
| 542 | return 0; | ||
| 543 | } | ||
| 544 | EXPORT_SYMBOL_GPL(register_sst_card); | ||
| 545 | |||
| 546 | /* | ||
| 547 | * unregister_sst_card- function for sound card to un-register | ||
| 548 | * | ||
| 549 | * @card: pointer to structure of operations | ||
| 550 | * | ||
| 551 | * This function is called when card driver unloads | ||
| 552 | */ | ||
| 553 | void unregister_sst_card(struct intel_sst_card_ops *card) | ||
| 554 | { | ||
| 555 | if (sst_pmic_ops.pcm_control == card->pcm_control) { | ||
| 556 | /* unreg */ | ||
| 557 | sst_pmic_ops.module_name = ""; | ||
| 558 | sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; | ||
| 559 | pr_debug("Unregistered %s\n", card->module_name); | ||
| 560 | } | ||
| 561 | return; | ||
| 562 | } | ||
| 563 | EXPORT_SYMBOL_GPL(unregister_sst_card); | ||
diff --git a/drivers/staging/intel_sst/intel_sst_dsp.c b/drivers/staging/intel_sst/intel_sst_dsp.c new file mode 100644 index 00000000000..a89e1ade847 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_dsp.c | |||
| @@ -0,0 +1,496 @@ | |||
| 1 | /* | ||
| 2 | * intel_sst_dsp.c - Intel SST Driver for audio engine | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * | ||
| 26 | * This driver exposes the audio engine functionalities to the ALSA | ||
| 27 | * and middleware. | ||
| 28 | * | ||
| 29 | * This file contains all dsp controlling functions like firmware download, | ||
| 30 | * setting/resetting dsp cores, etc | ||
| 31 | */ | ||
| 32 | |||
| 33 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 34 | |||
| 35 | #include <linux/pci.h> | ||
| 36 | #include <linux/fs.h> | ||
| 37 | #include <linux/firmware.h> | ||
| 38 | #include "intel_sst.h" | ||
| 39 | #include "intel_sst_ioctl.h" | ||
| 40 | #include "intel_sst_fw_ipc.h" | ||
| 41 | #include "intel_sst_common.h" | ||
| 42 | |||
| 43 | |||
| 44 | /** | ||
| 45 | * intel_sst_reset_dsp_mrst - Resetting SST DSP | ||
| 46 | * | ||
| 47 | * This resets DSP in case of MRST platfroms | ||
| 48 | */ | ||
| 49 | static int intel_sst_reset_dsp_mrst(void) | ||
| 50 | { | ||
| 51 | union config_status_reg csr; | ||
| 52 | |||
| 53 | pr_debug("Resetting the DSP in mrst\n"); | ||
| 54 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 55 | csr.full |= 0x382; | ||
| 56 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 57 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 58 | csr.part.strb_cntr_rst = 0; | ||
| 59 | csr.part.run_stall = 0x1; | ||
| 60 | csr.part.bypass = 0x7; | ||
| 61 | csr.part.sst_reset = 0x1; | ||
| 62 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | /** | ||
| 67 | * intel_sst_reset_dsp_medfield - Resetting SST DSP | ||
| 68 | * | ||
| 69 | * This resets DSP in case of Medfield platfroms | ||
| 70 | */ | ||
| 71 | static int intel_sst_reset_dsp_medfield(void) | ||
| 72 | { | ||
| 73 | union config_status_reg csr; | ||
| 74 | |||
| 75 | pr_debug("Resetting the DSP in medfield\n"); | ||
| 76 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 77 | csr.full |= 0x382; | ||
| 78 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 79 | |||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * sst_start_mrst - Start the SST DSP processor | ||
| 85 | * | ||
| 86 | * This starts the DSP in MRST platfroms | ||
| 87 | */ | ||
| 88 | static int sst_start_mrst(void) | ||
| 89 | { | ||
| 90 | union config_status_reg csr; | ||
| 91 | |||
| 92 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 93 | csr.part.bypass = 0; | ||
| 94 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 95 | csr.part.run_stall = 0; | ||
| 96 | csr.part.sst_reset = 0; | ||
| 97 | csr.part.strb_cntr_rst = 1; | ||
| 98 | pr_debug("Setting SST to execute_mrst 0x%x\n", csr.full); | ||
| 99 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 100 | |||
| 101 | return 0; | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * sst_start_medfield - Start the SST DSP processor | ||
| 106 | * | ||
| 107 | * This starts the DSP in MRST platfroms | ||
| 108 | */ | ||
| 109 | static int sst_start_medfield(void) | ||
| 110 | { | ||
| 111 | union config_status_reg csr; | ||
| 112 | |||
| 113 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 114 | csr.part.bypass = 0; | ||
| 115 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 116 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 117 | csr.part.mfld_strb = 1; | ||
| 118 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 119 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 120 | csr.part.run_stall = 0; | ||
| 121 | csr.part.sst_reset = 0; | ||
| 122 | pr_debug("Starting the DSP_medfld %x\n", csr.full); | ||
| 123 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 124 | pr_debug("Starting the DSP_medfld\n"); | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * sst_parse_module - Parse audio FW modules | ||
| 131 | * | ||
| 132 | * @module: FW module header | ||
| 133 | * | ||
| 134 | * Parses modules that need to be placed in SST IRAM and DRAM | ||
| 135 | * returns error or 0 if module sizes are proper | ||
| 136 | */ | ||
| 137 | static int sst_parse_module(struct fw_module_header *module) | ||
| 138 | { | ||
| 139 | struct dma_block_info *block; | ||
| 140 | u32 count; | ||
| 141 | void __iomem *ram; | ||
| 142 | |||
| 143 | pr_debug("module sign %s size %x blocks %x type %x\n", | ||
| 144 | module->signature, module->mod_size, | ||
| 145 | module->blocks, module->type); | ||
| 146 | pr_debug("module entrypoint 0x%x\n", module->entry_point); | ||
| 147 | |||
| 148 | block = (void *)module + sizeof(*module); | ||
| 149 | |||
| 150 | for (count = 0; count < module->blocks; count++) { | ||
| 151 | if (block->size <= 0) { | ||
| 152 | pr_err("block size invalid\n"); | ||
| 153 | return -EINVAL; | ||
| 154 | } | ||
| 155 | switch (block->type) { | ||
| 156 | case SST_IRAM: | ||
| 157 | ram = sst_drv_ctx->iram; | ||
| 158 | break; | ||
| 159 | case SST_DRAM: | ||
| 160 | ram = sst_drv_ctx->dram; | ||
| 161 | break; | ||
| 162 | default: | ||
| 163 | pr_err("wrong ram type0x%x in block0x%x\n", | ||
| 164 | block->type, count); | ||
| 165 | return -EINVAL; | ||
| 166 | } | ||
| 167 | memcpy_toio(ram + block->ram_offset, | ||
| 168 | (void *)block + sizeof(*block), block->size); | ||
| 169 | block = (void *)block + sizeof(*block) + block->size; | ||
| 170 | } | ||
| 171 | return 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * sst_parse_fw_image - parse and load FW | ||
| 176 | * | ||
| 177 | * @sst_fw: pointer to audio fw | ||
| 178 | * | ||
| 179 | * This function is called to parse and download the FW image | ||
| 180 | */ | ||
| 181 | static int sst_parse_fw_image(const struct firmware *sst_fw) | ||
| 182 | { | ||
| 183 | struct fw_header *header; | ||
| 184 | u32 count; | ||
| 185 | int ret_val; | ||
| 186 | struct fw_module_header *module; | ||
| 187 | |||
| 188 | BUG_ON(!sst_fw); | ||
| 189 | |||
| 190 | /* Read the header information from the data pointer */ | ||
| 191 | header = (struct fw_header *)sst_fw->data; | ||
| 192 | |||
| 193 | /* verify FW */ | ||
| 194 | if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) || | ||
| 195 | (sst_fw->size != header->file_size + sizeof(*header))) { | ||
| 196 | /* Invalid FW signature */ | ||
| 197 | pr_err("Invalid FW sign/filesize mismatch\n"); | ||
| 198 | return -EINVAL; | ||
| 199 | } | ||
| 200 | pr_debug("header sign=%s size=%x modules=%x fmt=%x size=%x\n", | ||
| 201 | header->signature, header->file_size, header->modules, | ||
| 202 | header->file_format, sizeof(*header)); | ||
| 203 | module = (void *)sst_fw->data + sizeof(*header); | ||
| 204 | for (count = 0; count < header->modules; count++) { | ||
| 205 | /* module */ | ||
| 206 | ret_val = sst_parse_module(module); | ||
| 207 | if (ret_val) | ||
| 208 | return ret_val; | ||
| 209 | module = (void *)module + sizeof(*module) + module->mod_size ; | ||
| 210 | } | ||
| 211 | |||
| 212 | return 0; | ||
| 213 | } | ||
| 214 | |||
| 215 | /** | ||
| 216 | * sst_load_fw - function to load FW into DSP | ||
| 217 | * | ||
| 218 | * @fw: Pointer to driver loaded FW | ||
| 219 | * @context: driver context | ||
| 220 | * | ||
| 221 | * This function is called by OS when the FW is loaded into kernel | ||
| 222 | */ | ||
| 223 | int sst_load_fw(const struct firmware *fw, void *context) | ||
| 224 | { | ||
| 225 | int ret_val; | ||
| 226 | |||
| 227 | pr_debug("load_fw called\n"); | ||
| 228 | BUG_ON(!fw); | ||
| 229 | |||
| 230 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) | ||
| 231 | ret_val = intel_sst_reset_dsp_mrst(); | ||
| 232 | else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) | ||
| 233 | ret_val = intel_sst_reset_dsp_medfield(); | ||
| 234 | if (ret_val) | ||
| 235 | return ret_val; | ||
| 236 | |||
| 237 | ret_val = sst_parse_fw_image(fw); | ||
| 238 | if (ret_val) | ||
| 239 | return ret_val; | ||
| 240 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 241 | sst_drv_ctx->sst_state = SST_FW_LOADED; | ||
| 242 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 243 | /* 7. ask scu to reset the bypass bits */ | ||
| 244 | /* 8.bring sst out of reset */ | ||
| 245 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) | ||
| 246 | ret_val = sst_start_mrst(); | ||
| 247 | else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) | ||
| 248 | ret_val = sst_start_medfield(); | ||
| 249 | if (ret_val) | ||
| 250 | return ret_val; | ||
| 251 | |||
| 252 | pr_debug("fw loaded successful!!!\n"); | ||
| 253 | return ret_val; | ||
| 254 | } | ||
| 255 | |||
| 256 | /*This function is called when any codec/post processing library | ||
| 257 | needs to be downloaded*/ | ||
| 258 | static int sst_download_library(const struct firmware *fw_lib, | ||
| 259 | struct snd_sst_lib_download_info *lib) | ||
| 260 | { | ||
| 261 | /* send IPC message and wait */ | ||
| 262 | int i; | ||
| 263 | u8 pvt_id; | ||
| 264 | struct ipc_post *msg = NULL; | ||
| 265 | union config_status_reg csr; | ||
| 266 | struct snd_sst_str_type str_type = {0}; | ||
| 267 | int retval = 0; | ||
| 268 | |||
| 269 | if (sst_create_large_msg(&msg)) | ||
| 270 | return -ENOMEM; | ||
| 271 | |||
| 272 | pvt_id = sst_assign_pvt_id(sst_drv_ctx); | ||
| 273 | i = sst_get_block_stream(sst_drv_ctx); | ||
| 274 | pr_debug("alloc block allocated = %d, pvt_id %d\n", i, pvt_id); | ||
| 275 | if (i < 0) { | ||
| 276 | kfree(msg); | ||
| 277 | return -ENOMEM; | ||
| 278 | } | ||
| 279 | sst_drv_ctx->alloc_block[i].sst_id = pvt_id; | ||
| 280 | sst_fill_header(&msg->header, IPC_IA_PREP_LIB_DNLD, 1, pvt_id); | ||
| 281 | msg->header.part.data = sizeof(u32) + sizeof(str_type); | ||
| 282 | str_type.codec_type = lib->dload_lib.lib_info.lib_type; | ||
| 283 | /*str_type.pvt_id = pvt_id;*/ | ||
| 284 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 285 | memcpy(msg->mailbox_data + sizeof(u32), &str_type, sizeof(str_type)); | ||
| 286 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 287 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 288 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 289 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 290 | retval = sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i]); | ||
| 291 | if (retval) { | ||
| 292 | /* error */ | ||
| 293 | sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; | ||
| 294 | pr_err("Prep codec downloaded failed %d\n", | ||
| 295 | retval); | ||
| 296 | return -EIO; | ||
| 297 | } | ||
| 298 | pr_debug("FW responded, ready for download now...\n"); | ||
| 299 | /* downloading on success */ | ||
| 300 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 301 | sst_drv_ctx->sst_state = SST_FW_LOADED; | ||
| 302 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 303 | csr.full = readl(sst_drv_ctx->shim + SST_CSR); | ||
| 304 | csr.part.run_stall = 1; | ||
| 305 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 306 | |||
| 307 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 308 | csr.part.bypass = 0x7; | ||
| 309 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 310 | |||
| 311 | sst_parse_fw_image(fw_lib); | ||
| 312 | |||
| 313 | /* set the FW to running again */ | ||
| 314 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 315 | csr.part.bypass = 0x0; | ||
| 316 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 317 | |||
| 318 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | ||
| 319 | csr.part.run_stall = 0; | ||
| 320 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | ||
| 321 | |||
| 322 | /* send download complete and wait */ | ||
| 323 | if (sst_create_large_msg(&msg)) { | ||
| 324 | sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; | ||
| 325 | return -ENOMEM; | ||
| 326 | } | ||
| 327 | |||
| 328 | sst_fill_header(&msg->header, IPC_IA_LIB_DNLD_CMPLT, 1, pvt_id); | ||
| 329 | sst_drv_ctx->alloc_block[i].sst_id = pvt_id; | ||
| 330 | msg->header.part.data = sizeof(u32) + sizeof(*lib); | ||
| 331 | lib->pvt_id = pvt_id; | ||
| 332 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 333 | memcpy(msg->mailbox_data + sizeof(u32), lib, sizeof(*lib)); | ||
| 334 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 335 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 336 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 337 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 338 | pr_debug("Waiting for FW response Download complete\n"); | ||
| 339 | sst_drv_ctx->alloc_block[i].ops_block.condition = false; | ||
| 340 | retval = sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i]); | ||
| 341 | if (retval) { | ||
| 342 | /* error */ | ||
| 343 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 344 | sst_drv_ctx->sst_state = SST_UN_INIT; | ||
| 345 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 346 | sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; | ||
| 347 | return -EIO; | ||
| 348 | } | ||
| 349 | |||
| 350 | pr_debug("FW success on Download complete\n"); | ||
| 351 | sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; | ||
| 352 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 353 | sst_drv_ctx->sst_state = SST_FW_RUNNING; | ||
| 354 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 355 | return 0; | ||
| 356 | |||
| 357 | } | ||
| 358 | |||
| 359 | /* This function is called before downloading the codec/postprocessing | ||
| 360 | library is set for download to SST DSP*/ | ||
| 361 | static int sst_validate_library(const struct firmware *fw_lib, | ||
| 362 | struct lib_slot_info *slot, | ||
| 363 | u32 *entry_point) | ||
| 364 | { | ||
| 365 | struct fw_header *header; | ||
| 366 | struct fw_module_header *module; | ||
| 367 | struct dma_block_info *block; | ||
| 368 | unsigned int n_blk, isize = 0, dsize = 0; | ||
| 369 | int err = 0; | ||
| 370 | |||
| 371 | header = (struct fw_header *)fw_lib->data; | ||
| 372 | if (header->modules != 1) { | ||
| 373 | pr_err("Module no mismatch found\n"); | ||
| 374 | err = -EINVAL; | ||
| 375 | goto exit; | ||
| 376 | } | ||
| 377 | module = (void *)fw_lib->data + sizeof(*header); | ||
| 378 | *entry_point = module->entry_point; | ||
| 379 | pr_debug("Module entry point 0x%x\n", *entry_point); | ||
| 380 | pr_debug("Module Sign %s, Size 0x%x, Blocks 0x%x Type 0x%x\n", | ||
| 381 | module->signature, module->mod_size, | ||
| 382 | module->blocks, module->type); | ||
| 383 | |||
| 384 | block = (void *)module + sizeof(*module); | ||
| 385 | for (n_blk = 0; n_blk < module->blocks; n_blk++) { | ||
| 386 | switch (block->type) { | ||
| 387 | case SST_IRAM: | ||
| 388 | isize += block->size; | ||
| 389 | break; | ||
| 390 | case SST_DRAM: | ||
| 391 | dsize += block->size; | ||
| 392 | break; | ||
| 393 | default: | ||
| 394 | pr_err("Invalid block type for 0x%x\n", n_blk); | ||
| 395 | err = -EINVAL; | ||
| 396 | goto exit; | ||
| 397 | } | ||
| 398 | block = (void *)block + sizeof(*block) + block->size; | ||
| 399 | } | ||
| 400 | if (isize > slot->iram_size || dsize > slot->dram_size) { | ||
| 401 | pr_err("library exceeds size allocated\n"); | ||
| 402 | err = -EINVAL; | ||
| 403 | goto exit; | ||
| 404 | } else | ||
| 405 | pr_debug("Library is safe for download...\n"); | ||
| 406 | |||
| 407 | pr_debug("iram 0x%x, dram 0x%x, iram 0x%x, dram 0x%x\n", | ||
| 408 | isize, dsize, slot->iram_size, slot->dram_size); | ||
| 409 | exit: | ||
| 410 | return err; | ||
| 411 | |||
| 412 | } | ||
| 413 | |||
| 414 | /* This function is called when FW requests for a particular library download | ||
| 415 | This function prepares the library to download*/ | ||
| 416 | int sst_load_library(struct snd_sst_lib_download *lib, u8 ops) | ||
| 417 | { | ||
| 418 | char buf[20]; | ||
| 419 | const char *type, *dir; | ||
| 420 | int len = 0, error = 0; | ||
| 421 | u32 entry_point; | ||
| 422 | const struct firmware *fw_lib; | ||
| 423 | struct snd_sst_lib_download_info dload_info = {{{0},},}; | ||
| 424 | |||
| 425 | memset(buf, 0, sizeof(buf)); | ||
| 426 | |||
| 427 | pr_debug("Lib Type 0x%x, Slot 0x%x, ops 0x%x\n", | ||
| 428 | lib->lib_info.lib_type, lib->slot_info.slot_num, ops); | ||
| 429 | pr_debug("Version 0x%x, name %s, caps 0x%x media type 0x%x\n", | ||
| 430 | lib->lib_info.lib_version, lib->lib_info.lib_name, | ||
| 431 | lib->lib_info.lib_caps, lib->lib_info.media_type); | ||
| 432 | |||
| 433 | pr_debug("IRAM Size 0x%x, offset 0x%x\n", | ||
| 434 | lib->slot_info.iram_size, lib->slot_info.iram_offset); | ||
| 435 | pr_debug("DRAM Size 0x%x, offset 0x%x\n", | ||
| 436 | lib->slot_info.dram_size, lib->slot_info.dram_offset); | ||
| 437 | |||
| 438 | switch (lib->lib_info.lib_type) { | ||
| 439 | case SST_CODEC_TYPE_MP3: | ||
| 440 | type = "mp3_"; | ||
| 441 | break; | ||
| 442 | case SST_CODEC_TYPE_AAC: | ||
| 443 | type = "aac_"; | ||
| 444 | break; | ||
| 445 | case SST_CODEC_TYPE_AACP: | ||
| 446 | type = "aac_v1_"; | ||
| 447 | break; | ||
| 448 | case SST_CODEC_TYPE_eAACP: | ||
| 449 | type = "aac_v2_"; | ||
| 450 | break; | ||
| 451 | case SST_CODEC_TYPE_WMA9: | ||
| 452 | type = "wma9_"; | ||
| 453 | break; | ||
| 454 | default: | ||
| 455 | pr_err("Invalid codec type\n"); | ||
| 456 | error = -EINVAL; | ||
| 457 | goto wake; | ||
| 458 | } | ||
| 459 | |||
| 460 | if (ops == STREAM_OPS_CAPTURE) | ||
| 461 | dir = "enc_"; | ||
| 462 | else | ||
| 463 | dir = "dec_"; | ||
| 464 | len = strlen(type) + strlen(dir); | ||
| 465 | strncpy(buf, type, sizeof(buf)-1); | ||
| 466 | strncpy(buf + strlen(type), dir, sizeof(buf)-strlen(type)-1); | ||
| 467 | len += snprintf(buf + len, sizeof(buf) - len, "%d", | ||
| 468 | lib->slot_info.slot_num); | ||
| 469 | len += snprintf(buf + len, sizeof(buf) - len, ".bin"); | ||
| 470 | |||
| 471 | pr_debug("Requesting %s\n", buf); | ||
| 472 | |||
| 473 | error = request_firmware(&fw_lib, buf, &sst_drv_ctx->pci->dev); | ||
| 474 | if (error) { | ||
| 475 | pr_err("library load failed %d\n", error); | ||
| 476 | goto wake; | ||
| 477 | } | ||
| 478 | error = sst_validate_library(fw_lib, &lib->slot_info, &entry_point); | ||
| 479 | if (error) | ||
| 480 | goto wake_free; | ||
| 481 | |||
| 482 | lib->mod_entry_pt = entry_point; | ||
| 483 | memcpy(&dload_info.dload_lib, lib, sizeof(*lib)); | ||
| 484 | error = sst_download_library(fw_lib, &dload_info); | ||
| 485 | if (error) | ||
| 486 | goto wake_free; | ||
| 487 | |||
| 488 | /* lib is downloaded and init send alloc again */ | ||
| 489 | pr_debug("Library is downloaded now...\n"); | ||
| 490 | wake_free: | ||
| 491 | /* sst_wake_up_alloc_block(sst_drv_ctx, pvt_id, error, NULL); */ | ||
| 492 | release_firmware(fw_lib); | ||
| 493 | wake: | ||
| 494 | return error; | ||
| 495 | } | ||
| 496 | |||
diff --git a/drivers/staging/intel_sst/intel_sst_fw_ipc.h b/drivers/staging/intel_sst/intel_sst_fw_ipc.h new file mode 100644 index 00000000000..5d0cc56aaef --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_fw_ipc.h | |||
| @@ -0,0 +1,416 @@ | |||
| 1 | #ifndef __INTEL_SST_FW_IPC_H__ | ||
| 2 | #define __INTEL_SST_FW_IPC_H__ | ||
| 3 | /* | ||
| 4 | * intel_sst_fw_ipc.h - Intel SST Driver for audio engine | ||
| 5 | * | ||
| 6 | * Copyright (C) 2008-10 Intel Corporation | ||
| 7 | * Author: Vinod Koul <vinod.koul@intel.com> | ||
| 8 | * Harsha Priya <priya.harsha@intel.com> | ||
| 9 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 10 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 11 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License as published by | ||
| 15 | * the Free Software Foundation; version 2 of the License. | ||
| 16 | * | ||
| 17 | * This program is distributed in the hope that it will be useful, but | ||
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 20 | * General Public License for more details. | ||
| 21 | * | ||
| 22 | * You should have received a copy of the GNU General Public License along | ||
| 23 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 24 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 25 | * | ||
| 26 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 27 | * | ||
| 28 | * This driver exposes the audio engine functionalities to the ALSA | ||
| 29 | * and middleware. | ||
| 30 | * This file has definitions shared between the firmware and driver | ||
| 31 | */ | ||
| 32 | |||
| 33 | #define MAX_NUM_STREAMS_MRST 3 | ||
| 34 | #define MAX_NUM_STREAMS_MFLD 6 | ||
| 35 | #define MAX_NUM_STREAMS 6 | ||
| 36 | #define MAX_DBG_RW_BYTES 80 | ||
| 37 | #define MAX_NUM_SCATTER_BUFFERS 8 | ||
| 38 | #define MAX_LOOP_BACK_DWORDS 8 | ||
| 39 | /* IPC base address and mailbox, timestamp offsets */ | ||
| 40 | #define SST_MAILBOX_SIZE 0x0400 | ||
| 41 | #define SST_MAILBOX_SEND 0x0000 | ||
| 42 | #define SST_MAILBOX_RCV 0x0804 | ||
| 43 | #define SST_TIME_STAMP 0x1800 | ||
| 44 | #define SST_RESERVED_OFFSET 0x1A00 | ||
| 45 | #define SST_CHEKPOINT_OFFSET 0x1C00 | ||
| 46 | #define REPLY_MSG 0x80 | ||
| 47 | |||
| 48 | /* Message ID's for IPC messages */ | ||
| 49 | /* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */ | ||
| 50 | |||
| 51 | /* I2L Firmware/Codec Download msgs */ | ||
| 52 | #define IPC_IA_PREP_LIB_DNLD 0x01 | ||
| 53 | #define IPC_IA_LIB_DNLD_CMPLT 0x02 | ||
| 54 | |||
| 55 | #define IPC_IA_SET_PMIC_TYPE 0x03 | ||
| 56 | #define IPC_IA_GET_FW_VERSION 0x04 | ||
| 57 | #define IPC_IA_GET_FW_BUILD_INF 0x05 | ||
| 58 | #define IPC_IA_GET_FW_INFO 0x06 | ||
| 59 | #define IPC_IA_GET_FW_CTXT 0x07 | ||
| 60 | #define IPC_IA_SET_FW_CTXT 0x08 | ||
| 61 | |||
| 62 | /* I2L Codec Config/control msgs */ | ||
| 63 | #define IPC_IA_SET_CODEC_PARAMS 0x10 | ||
| 64 | #define IPC_IA_GET_CODEC_PARAMS 0x11 | ||
| 65 | #define IPC_IA_SET_PPP_PARAMS 0x12 | ||
| 66 | #define IPC_IA_GET_PPP_PARAMS 0x13 | ||
| 67 | #define IPC_IA_PLAY_FRAMES 0x14 | ||
| 68 | #define IPC_IA_CAPT_FRAMES 0x15 | ||
| 69 | #define IPC_IA_PLAY_VOICE 0x16 | ||
| 70 | #define IPC_IA_CAPT_VOICE 0x17 | ||
| 71 | #define IPC_IA_DECODE_FRAMES 0x18 | ||
| 72 | |||
| 73 | #define IPC_IA_ALG_PARAMS 0x1A | ||
| 74 | #define IPC_IA_TUNING_PARAMS 0x1B | ||
| 75 | |||
| 76 | /* I2L Stream config/control msgs */ | ||
| 77 | #define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */ | ||
| 78 | #define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */ | ||
| 79 | #define IPC_IA_SET_STREAM_PARAMS 0x22 | ||
| 80 | #define IPC_IA_GET_STREAM_PARAMS 0x23 | ||
| 81 | #define IPC_IA_PAUSE_STREAM 0x24 | ||
| 82 | #define IPC_IA_RESUME_STREAM 0x25 | ||
| 83 | #define IPC_IA_DROP_STREAM 0x26 | ||
| 84 | #define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */ | ||
| 85 | #define IPC_IA_TARGET_DEV_SELECT 0x28 | ||
| 86 | #define IPC_IA_CONTROL_ROUTING 0x29 | ||
| 87 | |||
| 88 | #define IPC_IA_SET_STREAM_VOL 0x2A /*Vol for stream, pre mixer */ | ||
| 89 | #define IPC_IA_GET_STREAM_VOL 0x2B | ||
| 90 | #define IPC_IA_SET_STREAM_MUTE 0x2C | ||
| 91 | #define IPC_IA_GET_STREAM_MUTE 0x2D | ||
| 92 | #define IPC_IA_ENABLE_RX_TIME_SLOT 0x2E /* Enable Rx time slot 0 or 1 */ | ||
| 93 | |||
| 94 | #define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */ | ||
| 95 | |||
| 96 | /* Debug msgs */ | ||
| 97 | #define IPC_IA_DBG_MEM_READ 0x40 | ||
| 98 | #define IPC_IA_DBG_MEM_WRITE 0x41 | ||
| 99 | #define IPC_IA_DBG_LOOP_BACK 0x42 | ||
| 100 | |||
| 101 | /* L2I Firmware/Codec Download msgs */ | ||
| 102 | #define IPC_IA_FW_INIT_CMPLT 0x81 | ||
| 103 | #define IPC_IA_LPE_GETTING_STALLED 0x82 | ||
| 104 | #define IPC_IA_LPE_UNSTALLED 0x83 | ||
| 105 | |||
| 106 | /* L2I Codec Config/control msgs */ | ||
| 107 | #define IPC_SST_GET_PLAY_FRAMES 0x90 /* Request IA more data */ | ||
| 108 | #define IPC_SST_GET_CAPT_FRAMES 0x91 /* Request IA more data */ | ||
| 109 | #define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */ | ||
| 110 | #define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */ | ||
| 111 | #define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */ | ||
| 112 | #define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */ | ||
| 113 | #define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */ | ||
| 114 | #define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */ | ||
| 115 | #define IPC_IA_TARGET_DEV_CHNGD 0x98 /* error in processing a stream */ | ||
| 116 | |||
| 117 | #define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */ | ||
| 118 | /* L2S messages */ | ||
| 119 | #define IPC_SC_DDR_LINK_UP 0xC0 | ||
| 120 | #define IPC_SC_DDR_LINK_DOWN 0xC1 | ||
| 121 | #define IPC_SC_SET_LPECLK_REQ 0xC2 | ||
| 122 | #define IPC_SC_SSP_BIT_BANG 0xC3 | ||
| 123 | |||
| 124 | /* L2I Error reporting msgs */ | ||
| 125 | #define IPC_IA_MEM_ALLOC_FAIL 0xE0 | ||
| 126 | #define IPC_IA_PROC_ERR 0xE1 /* error in processing a | ||
| 127 | stream can be used by playback and | ||
| 128 | capture modules */ | ||
| 129 | |||
| 130 | /* L2I Debug msgs */ | ||
| 131 | #define IPC_IA_PRINT_STRING 0xF0 | ||
| 132 | |||
| 133 | |||
| 134 | |||
| 135 | /* Command Response or Acknowledge message to any IPC message will have | ||
| 136 | * same message ID and stream ID information which is sent. | ||
| 137 | * There is no specific Ack message ID. The data field is used as response | ||
| 138 | * meaning. | ||
| 139 | */ | ||
| 140 | enum ackData { | ||
| 141 | IPC_ACK_SUCCESS = 0, | ||
| 142 | IPC_ACK_FAILURE | ||
| 143 | }; | ||
| 144 | |||
| 145 | |||
| 146 | enum sst_error_codes { | ||
| 147 | /* Error code,response to msgId: Description */ | ||
| 148 | /* Common error codes */ | ||
| 149 | SST_SUCCESS = 0, /* Success */ | ||
| 150 | SST_ERR_INVALID_STREAM_ID = 1, | ||
| 151 | SST_ERR_INVALID_MSG_ID = 2, | ||
| 152 | SST_ERR_INVALID_STREAM_OP = 3, | ||
| 153 | SST_ERR_INVALID_PARAMS = 4, | ||
| 154 | SST_ERR_INVALID_CODEC = 5, | ||
| 155 | SST_ERR_INVALID_MEDIA_TYPE = 6, | ||
| 156 | SST_ERR_STREAM_ERR = 7, | ||
| 157 | |||
| 158 | /* IPC specific error codes */ | ||
| 159 | SST_IPC_ERR_CALL_BACK_NOT_REGD = 8, | ||
| 160 | SST_IPC_ERR_STREAM_NOT_ALLOCATED = 9, | ||
| 161 | SST_IPC_ERR_STREAM_ALLOC_FAILED = 10, | ||
| 162 | SST_IPC_ERR_GET_STREAM_FAILED = 11, | ||
| 163 | SST_ERR_MOD_NOT_AVAIL = 12, | ||
| 164 | SST_ERR_MOD_DNLD_RQD = 13, | ||
| 165 | SST_ERR_STREAM_STOPPED = 14, | ||
| 166 | SST_ERR_STREAM_IN_USE = 15, | ||
| 167 | |||
| 168 | /* Capture specific error codes */ | ||
| 169 | SST_CAP_ERR_INCMPLTE_CAPTURE_MSG = 16, | ||
| 170 | SST_CAP_ERR_CAPTURE_FAIL = 17, | ||
| 171 | SST_CAP_ERR_GET_DDR_NEW_SGLIST = 18, | ||
| 172 | SST_CAP_ERR_UNDER_RUN = 19, | ||
| 173 | SST_CAP_ERR_OVERFLOW = 20, | ||
| 174 | |||
| 175 | /* Playback specific error codes*/ | ||
| 176 | SST_PB_ERR_INCMPLTE_PLAY_MSG = 21, | ||
| 177 | SST_PB_ERR_PLAY_FAIL = 22, | ||
| 178 | SST_PB_ERR_GET_DDR_NEW_SGLIST = 23, | ||
| 179 | |||
| 180 | /* Codec manager specific error codes */ | ||
| 181 | SST_LIB_ERR_LIB_DNLD_REQUIRED = 24, | ||
| 182 | SST_LIB_ERR_LIB_NOT_SUPPORTED = 25, | ||
| 183 | |||
| 184 | /* Library manager specific error codes */ | ||
| 185 | SST_SCC_ERR_PREP_DNLD_FAILED = 26, | ||
| 186 | SST_SCC_ERR_LIB_DNLD_RES_FAILED = 27, | ||
| 187 | /* Scheduler specific error codes */ | ||
| 188 | SST_SCH_ERR_FAIL = 28, | ||
| 189 | |||
| 190 | /* DMA specific error codes */ | ||
| 191 | SST_DMA_ERR_NO_CHNL_AVAILABLE = 29, | ||
| 192 | SST_DMA_ERR_INVALID_INPUT_PARAMS = 30, | ||
| 193 | SST_DMA_ERR_CHNL_ALREADY_SUSPENDED = 31, | ||
| 194 | SST_DMA_ERR_CHNL_ALREADY_STARTED = 32, | ||
| 195 | SST_DMA_ERR_CHNL_NOT_ENABLED = 33, | ||
| 196 | SST_DMA_ERR_TRANSFER_FAILED = 34, | ||
| 197 | |||
| 198 | SST_SSP_ERR_ALREADY_ENABLED = 35, | ||
| 199 | SST_SSP_ERR_ALREADY_DISABLED = 36, | ||
| 200 | SST_SSP_ERR_NOT_INITIALIZED = 37, | ||
| 201 | SST_SSP_ERR_SRAM_NO_DMA_DATA = 38, | ||
| 202 | |||
| 203 | /* Other error codes */ | ||
| 204 | SST_ERR_MOD_INIT_FAIL = 39, | ||
| 205 | |||
| 206 | /* FW init error codes */ | ||
| 207 | SST_RDR_ERR_IO_DEV_SEL_NOT_ALLOWED = 40, | ||
| 208 | SST_RDR_ERR_ROUTE_ALREADY_STARTED = 41, | ||
| 209 | SST_RDR_ERR_IO_DEV_SEL_FAILED = 42, | ||
| 210 | SST_RDR_PREP_CODEC_DNLD_FAILED = 43, | ||
| 211 | |||
| 212 | /* Memory debug error codes */ | ||
| 213 | SST_ERR_DBG_MEM_READ_FAIL = 44, | ||
| 214 | SST_ERR_DBG_MEM_WRITE_FAIL = 45, | ||
| 215 | SST_ERR_INSUFFICIENT_INPUT_SG_LIST = 46, | ||
| 216 | SST_ERR_INSUFFICIENT_OUTPUT_SG_LIST = 47, | ||
| 217 | |||
| 218 | SST_ERR_BUFFER_NOT_AVAILABLE = 48, | ||
| 219 | SST_ERR_BUFFER_NOT_ALLOCATED = 49, | ||
| 220 | SST_ERR_INVALID_REGION_TYPE = 50, | ||
| 221 | SST_ERR_NULL_PTR = 51, | ||
| 222 | SST_ERR_INVALID_BUFFER_SIZE = 52, | ||
| 223 | SST_ERR_INVALID_BUFFER_INDEX = 53, | ||
| 224 | |||
| 225 | /*IIPC specific error codes */ | ||
| 226 | SST_IIPC_QUEUE_FULL = 54, | ||
| 227 | SST_IIPC_ERR_MSG_SND_FAILED = 55, | ||
| 228 | SST_PB_ERR_UNDERRUN_OCCURED = 56, | ||
| 229 | SST_RDR_INSUFFICIENT_MIXER_BUFFER = 57, | ||
| 230 | SST_INVALID_TIME_SLOTS = 58, | ||
| 231 | }; | ||
| 232 | |||
| 233 | enum dbg_mem_data_type { | ||
| 234 | /* Data type of debug read/write */ | ||
| 235 | DATA_TYPE_U32, | ||
| 236 | DATA_TYPE_U16, | ||
| 237 | DATA_TYPE_U8, | ||
| 238 | }; | ||
| 239 | |||
| 240 | /* CAUTION NOTE: All IPC message body must be multiple of 32 bits.*/ | ||
| 241 | |||
| 242 | /* IPC Header */ | ||
| 243 | union ipc_header { | ||
| 244 | struct { | ||
| 245 | u32 msg_id:8; /* Message ID - Max 256 Message Types */ | ||
| 246 | u32 str_id:5; | ||
| 247 | u32 large:1; /* Large Message if large = 1 */ | ||
| 248 | u32 reserved:2; /* Reserved for future use */ | ||
| 249 | u32 data:14; /* Ack/Info for msg, size of msg in Mailbox */ | ||
| 250 | u32 done:1; /* bit 30 */ | ||
| 251 | u32 busy:1; /* bit 31 */ | ||
| 252 | } part; | ||
| 253 | u32 full; | ||
| 254 | } __attribute__ ((packed)); | ||
| 255 | |||
| 256 | /* Firmware build info */ | ||
| 257 | struct sst_fw_build_info { | ||
| 258 | unsigned char date[16]; /* Firmware build date */ | ||
| 259 | unsigned char time[16]; /* Firmware build time */ | ||
| 260 | } __attribute__ ((packed)); | ||
| 261 | |||
| 262 | struct ipc_header_fw_init { | ||
| 263 | struct snd_sst_fw_version fw_version;/* Firmware version details */ | ||
| 264 | struct sst_fw_build_info build_info; | ||
| 265 | u16 result; /* Fw init result */ | ||
| 266 | u8 module_id; /* Module ID in case of error */ | ||
| 267 | u8 debug_info; /* Debug info from Module ID in case of fail */ | ||
| 268 | } __attribute__ ((packed)); | ||
| 269 | |||
| 270 | /* Address and size info of a frame buffer in DDR */ | ||
| 271 | struct sst_address_info { | ||
| 272 | u32 addr; /* Address at IA */ | ||
| 273 | u32 size; /* Size of the buffer */ | ||
| 274 | } __attribute__ ((packed)); | ||
| 275 | |||
| 276 | /* Time stamp */ | ||
| 277 | struct snd_sst_tstamp { | ||
| 278 | u64 samples_processed;/* capture - data in DDR */ | ||
| 279 | u64 samples_rendered;/* playback - data rendered */ | ||
| 280 | u64 bytes_processed;/* bytes decoded or encoded */ | ||
| 281 | u32 sampling_frequency;/* eg: 48000, 44100 */ | ||
| 282 | u32 dma_base_address;/* DMA base address */ | ||
| 283 | u16 dma_channel_no;/* DMA Channel used for the data transfer*/ | ||
| 284 | u16 reserved;/* 32 bit alignment */ | ||
| 285 | }; | ||
| 286 | |||
| 287 | /* Frame info to play or capture */ | ||
| 288 | struct sst_frame_info { | ||
| 289 | u16 num_entries; /* number of entries to follow */ | ||
| 290 | u16 rsrvd; | ||
| 291 | struct sst_address_info addr[MAX_NUM_SCATTER_BUFFERS]; | ||
| 292 | } __attribute__ ((packed)); | ||
| 293 | |||
| 294 | /* Frames info for decode */ | ||
| 295 | struct snd_sst_decode_info { | ||
| 296 | unsigned long long input_bytes_consumed; | ||
| 297 | unsigned long long output_bytes_produced; | ||
| 298 | struct sst_frame_info frames_in; | ||
| 299 | struct sst_frame_info frames_out; | ||
| 300 | } __attribute__ ((packed)); | ||
| 301 | |||
| 302 | /* SST to IA print debug message*/ | ||
| 303 | struct ipc_sst_ia_print_params { | ||
| 304 | u32 string_size;/* Max value is 160 */ | ||
| 305 | u8 prt_string[160];/* Null terminated Char string */ | ||
| 306 | } __attribute__ ((packed)); | ||
| 307 | |||
| 308 | /* Voice data message */ | ||
| 309 | struct snd_sst_voice_data { | ||
| 310 | u16 num_bytes;/* Number of valid voice data bytes */ | ||
| 311 | u8 pcm_wd_size;/* 0=8 bit, 1=16 bit 2=32 bit */ | ||
| 312 | u8 reserved;/* Reserved */ | ||
| 313 | u8 voice_data_buf[0];/* Voice data buffer in bytes, little endian */ | ||
| 314 | } __attribute__ ((packed)); | ||
| 315 | |||
| 316 | /* SST to IA memory read debug message */ | ||
| 317 | struct ipc_sst_ia_dbg_mem_rw { | ||
| 318 | u16 num_bytes;/* Maximum of MAX_DBG_RW_BYTES */ | ||
| 319 | u16 data_type;/* enum: dbg_mem_data_type */ | ||
| 320 | u32 address; /* Memory address of data memory of data_type */ | ||
| 321 | u8 rw_bytes[MAX_DBG_RW_BYTES];/* Maximum of 64 bytes can be RW */ | ||
| 322 | } __attribute__ ((packed)); | ||
| 323 | |||
| 324 | struct ipc_sst_ia_dbg_loop_back { | ||
| 325 | u16 num_dwords; /* Maximum of MAX_DBG_RW_BYTES */ | ||
| 326 | u16 increment_val;/* Increments dwords by this value, 0- no increment */ | ||
| 327 | u32 lpbk_dwords[MAX_LOOP_BACK_DWORDS];/* Maximum of 8 dwords loopback */ | ||
| 328 | } __attribute__ ((packed)); | ||
| 329 | |||
| 330 | /* Stream type params struture for Alloc stream */ | ||
| 331 | struct snd_sst_str_type { | ||
| 332 | u8 codec_type; /* Codec type */ | ||
| 333 | u8 str_type; /* 1 = voice 2 = music */ | ||
| 334 | u8 operation; /* Playback or Capture */ | ||
| 335 | u8 protected_str; /* 0=Non DRM, 1=DRM */ | ||
| 336 | u8 time_slots; | ||
| 337 | u8 reserved; /* Reserved */ | ||
| 338 | u16 result; /* Result used for acknowledgment */ | ||
| 339 | } __attribute__ ((packed)); | ||
| 340 | |||
| 341 | /* Library info structure */ | ||
| 342 | struct module_info { | ||
| 343 | u32 lib_version; | ||
| 344 | u32 lib_type;/*TBD- KLOCKWORK u8 lib_type;*/ | ||
| 345 | u32 media_type; | ||
| 346 | u8 lib_name[12]; | ||
| 347 | u32 lib_caps; | ||
| 348 | unsigned char b_date[16]; /* Lib build date */ | ||
| 349 | unsigned char b_time[16]; /* Lib build time */ | ||
| 350 | } __attribute__ ((packed)); | ||
| 351 | |||
| 352 | /* Library slot info */ | ||
| 353 | struct lib_slot_info { | ||
| 354 | u8 slot_num; /* 1 or 2 */ | ||
| 355 | u8 reserved1; | ||
| 356 | u16 reserved2; | ||
| 357 | u32 iram_size; /* slot size in IRAM */ | ||
| 358 | u32 dram_size; /* slot size in DRAM */ | ||
| 359 | u32 iram_offset; /* starting offset of slot in IRAM */ | ||
| 360 | u32 dram_offset; /* starting offset of slot in DRAM */ | ||
| 361 | } __attribute__ ((packed)); | ||
| 362 | |||
| 363 | struct snd_sst_lib_download { | ||
| 364 | struct module_info lib_info; /* library info type, capabilities etc */ | ||
| 365 | struct lib_slot_info slot_info; /* slot info to be downloaded */ | ||
| 366 | u32 mod_entry_pt; | ||
| 367 | }; | ||
| 368 | |||
| 369 | struct snd_sst_lib_download_info { | ||
| 370 | struct snd_sst_lib_download dload_lib; | ||
| 371 | u16 result; /* Result used for acknowledgment */ | ||
| 372 | u8 pvt_id; /* Private ID */ | ||
| 373 | u8 reserved; /* for alignment */ | ||
| 374 | }; | ||
| 375 | |||
| 376 | /* Alloc stream params structure */ | ||
| 377 | struct snd_sst_alloc_params { | ||
| 378 | struct snd_sst_str_type str_type; | ||
| 379 | struct snd_sst_stream_params stream_params; | ||
| 380 | }; | ||
| 381 | |||
| 382 | struct snd_sst_fw_get_stream_params { | ||
| 383 | struct snd_sst_stream_params codec_params; | ||
| 384 | struct snd_sst_pmic_config pcm_params; | ||
| 385 | }; | ||
| 386 | |||
| 387 | /* Alloc stream response message */ | ||
| 388 | struct snd_sst_alloc_response { | ||
| 389 | struct snd_sst_str_type str_type; /* Stream type for allocation */ | ||
| 390 | struct snd_sst_lib_download lib_dnld; /* Valid only for codec dnld */ | ||
| 391 | }; | ||
| 392 | |||
| 393 | /* Drop response */ | ||
| 394 | struct snd_sst_drop_response { | ||
| 395 | u32 result; | ||
| 396 | u32 bytes; | ||
| 397 | }; | ||
| 398 | |||
| 399 | /* CSV Voice call routing structure */ | ||
| 400 | struct snd_sst_control_routing { | ||
| 401 | u8 control; /* 0=start, 1=Stop */ | ||
| 402 | u8 reserved[3]; /* Reserved- for 32 bit alignment */ | ||
| 403 | }; | ||
| 404 | |||
| 405 | |||
| 406 | struct ipc_post { | ||
| 407 | struct list_head node; | ||
| 408 | union ipc_header header; /* driver specific */ | ||
| 409 | char *mailbox_data; | ||
| 410 | }; | ||
| 411 | |||
| 412 | struct snd_sst_ctxt_params { | ||
| 413 | u32 address; /* Physical Address in DDR where the context is stored */ | ||
| 414 | u32 size; /* size of the context */ | ||
| 415 | }; | ||
| 416 | #endif /* __INTEL_SST_FW_IPC_H__ */ | ||
diff --git a/drivers/staging/intel_sst/intel_sst_ioctl.h b/drivers/staging/intel_sst/intel_sst_ioctl.h new file mode 100644 index 00000000000..5da5ee092c6 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_ioctl.h | |||
| @@ -0,0 +1,440 @@ | |||
| 1 | #ifndef __INTEL_SST_IOCTL_H__ | ||
| 2 | #define __INTEL_SST_IOCTL_H__ | ||
| 3 | /* | ||
| 4 | * intel_sst_ioctl.h - Intel SST Driver for audio engine | ||
| 5 | * | ||
| 6 | * Copyright (C) 2008-10 Intel Corporation | ||
| 7 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 8 | * Harsha Priya <priya.harsha@intel.com> | ||
| 9 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 10 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 11 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License as published by | ||
| 15 | * the Free Software Foundation; version 2 of the License. | ||
| 16 | * | ||
| 17 | * This program is distributed in the hope that it will be useful, but | ||
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 20 | * General Public License for more details. | ||
| 21 | * | ||
| 22 | * You should have received a copy of the GNU General Public License along | ||
| 23 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 24 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 25 | * | ||
| 26 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 27 | * | ||
| 28 | * This file defines all sst ioctls | ||
| 29 | */ | ||
| 30 | |||
| 31 | /* codec and post/pre processing related info */ | ||
| 32 | |||
| 33 | #include <linux/types.h> | ||
| 34 | |||
| 35 | enum sst_codec_types { | ||
| 36 | /* AUDIO/MUSIC CODEC Type Definitions */ | ||
| 37 | SST_CODEC_TYPE_UNKNOWN = 0, | ||
| 38 | SST_CODEC_TYPE_PCM, /* Pass through Audio codec */ | ||
| 39 | SST_CODEC_TYPE_MP3, | ||
| 40 | SST_CODEC_TYPE_MP24, | ||
| 41 | SST_CODEC_TYPE_AAC, | ||
| 42 | SST_CODEC_TYPE_AACP, | ||
| 43 | SST_CODEC_TYPE_eAACP, | ||
| 44 | SST_CODEC_TYPE_WMA9, | ||
| 45 | SST_CODEC_TYPE_WMA10, | ||
| 46 | SST_CODEC_TYPE_WMA10P, | ||
| 47 | SST_CODEC_TYPE_RA, | ||
| 48 | SST_CODEC_TYPE_DDAC3, | ||
| 49 | SST_CODEC_TYPE_STEREO_TRUE_HD, | ||
| 50 | SST_CODEC_TYPE_STEREO_HD_PLUS, | ||
| 51 | |||
| 52 | /* VOICE CODEC Type Definitions */ | ||
| 53 | SST_CODEC_TYPE_VOICE_PCM = 0x21, /* Pass through voice codec */ | ||
| 54 | }; | ||
| 55 | |||
| 56 | enum sst_algo_types { | ||
| 57 | SST_CODEC_SRC = 0x64, | ||
| 58 | SST_CODEC_MIXER = 0x65, | ||
| 59 | SST_CODEC_DOWN_MIXER = 0x66, | ||
| 60 | SST_CODEC_VOLUME_CONTROL = 0x67, | ||
| 61 | SST_CODEC_OEM1 = 0xC8, | ||
| 62 | SST_CODEC_OEM2 = 0xC9, | ||
| 63 | }; | ||
| 64 | |||
| 65 | enum snd_sst_stream_ops { | ||
| 66 | STREAM_OPS_PLAYBACK = 0, /* Decode */ | ||
| 67 | STREAM_OPS_CAPTURE, /* Encode */ | ||
| 68 | STREAM_OPS_PLAYBACK_DRM, /* Play Audio/Voice */ | ||
| 69 | STREAM_OPS_PLAYBACK_ALERT, /* Play Audio/Voice */ | ||
| 70 | STREAM_OPS_CAPTURE_VOICE_CALL, /* CSV Voice recording */ | ||
| 71 | }; | ||
| 72 | |||
| 73 | enum stream_mode { | ||
| 74 | SST_STREAM_MODE_NONE = 0, | ||
| 75 | SST_STREAM_MODE_DNR = 1, | ||
| 76 | SST_STREAM_MODE_FNF = 2, | ||
| 77 | SST_STREAM_MODE_CAPTURE = 3 | ||
| 78 | }; | ||
| 79 | |||
| 80 | enum stream_type { | ||
| 81 | SST_STREAM_TYPE_NONE = 0, | ||
| 82 | SST_STREAM_TYPE_MUSIC = 1, | ||
| 83 | SST_STREAM_TYPE_NORMAL = 2, | ||
| 84 | SST_STREAM_TYPE_LONG_PB = 3, | ||
| 85 | SST_STREAM_TYPE_LOW_LATENCY = 4, | ||
| 86 | }; | ||
| 87 | |||
| 88 | enum snd_sst_audio_device_type { | ||
| 89 | SND_SST_DEVICE_HEADSET = 1, | ||
| 90 | SND_SST_DEVICE_IHF, | ||
| 91 | SND_SST_DEVICE_VIBRA, | ||
| 92 | SND_SST_DEVICE_HAPTIC, | ||
| 93 | SND_SST_DEVICE_CAPTURE, | ||
| 94 | }; | ||
| 95 | |||
| 96 | /* Firmware Version info */ | ||
| 97 | struct snd_sst_fw_version { | ||
| 98 | __u8 build; /* build number*/ | ||
| 99 | __u8 minor; /* minor number*/ | ||
| 100 | __u8 major; /* major number*/ | ||
| 101 | __u8 type; /* build type */ | ||
| 102 | }; | ||
| 103 | |||
| 104 | /* Port info structure */ | ||
| 105 | struct snd_sst_port_info { | ||
| 106 | __u16 port_type; | ||
| 107 | __u16 reserved; | ||
| 108 | }; | ||
| 109 | |||
| 110 | /* Mixer info structure */ | ||
| 111 | struct snd_sst_mix_info { | ||
| 112 | __u16 max_streams; | ||
| 113 | __u16 reserved; | ||
| 114 | }; | ||
| 115 | |||
| 116 | /* PCM Parameters */ | ||
| 117 | struct snd_pcm_params { | ||
| 118 | __u16 codec; /* codec type */ | ||
| 119 | __u8 num_chan; /* 1=Mono, 2=Stereo */ | ||
| 120 | __u8 pcm_wd_sz; /* 16/24 - bit*/ | ||
| 121 | __u32 reserved; /* Bitrate in bits per second */ | ||
| 122 | __u32 sfreq; /* Sampling rate in Hz */ | ||
| 123 | __u32 ring_buffer_size; | ||
| 124 | __u32 period_count; /* period elapsed in samples*/ | ||
| 125 | __u32 ring_buffer_addr; | ||
| 126 | }; | ||
| 127 | |||
| 128 | /* MP3 Music Parameters Message */ | ||
| 129 | struct snd_mp3_params { | ||
| 130 | __u16 codec; | ||
| 131 | __u8 num_chan; /* 1=Mono, 2=Stereo */ | ||
| 132 | __u8 pcm_wd_sz; /* 16/24 - bit*/ | ||
| 133 | __u32 brate; /* Use the hard coded value. */ | ||
| 134 | __u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */ | ||
| 135 | __u8 crc_check; /* crc_check - disable (0) or enable (1) */ | ||
| 136 | __u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB*/ | ||
| 137 | __u16 reserved; /* Unused */ | ||
| 138 | }; | ||
| 139 | |||
| 140 | #define AAC_BIT_STREAM_ADTS 0 | ||
| 141 | #define AAC_BIT_STREAM_ADIF 1 | ||
| 142 | #define AAC_BIT_STREAM_RAW 2 | ||
| 143 | |||
| 144 | /* AAC Music Parameters Message */ | ||
| 145 | struct snd_aac_params { | ||
| 146 | __u16 codec; | ||
| 147 | __u8 num_chan; /* 1=Mono, 2=Stereo*/ | ||
| 148 | __u8 pcm_wd_sz; /* 16/24 - bit*/ | ||
| 149 | __u32 brate; | ||
| 150 | __u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */ | ||
| 151 | __u32 aac_srate; /* Plain AAC decoder operating sample rate */ | ||
| 152 | __u8 mpg_id; /* 0=MPEG-2, 1=MPEG-4 */ | ||
| 153 | __u8 bs_format; /* input bit stream format adts=0, adif=1, raw=2 */ | ||
| 154 | __u8 aac_profile; /* 0=Main Profile, 1=LC profile, 3=SSR profile */ | ||
| 155 | __u8 ext_chl; /* No.of external channels */ | ||
| 156 | __u8 aot; /* Audio object type. 1=Main , 2=LC , 3=SSR, 4=SBR*/ | ||
| 157 | __u8 op_align; /* output alignment 0=16 bit , 1=MSB, 2= LSB align */ | ||
| 158 | __u8 brate_type; /* 0=CBR, 1=VBR */ | ||
| 159 | __u8 crc_check; /* crc check 0= disable, 1=enable */ | ||
| 160 | __s8 bit_stream_format[8]; /* input bit stream format adts/adif/raw */ | ||
| 161 | __u8 jstereo; /* Joint stereo Flag */ | ||
| 162 | __u8 sbr_present; /* 1 = SBR Present, 0 = SBR absent, for RAW */ | ||
| 163 | __u8 downsample; /* 1 = Downsampling ON, 0 = Downsampling OFF */ | ||
| 164 | __u8 num_syntc_elems; /* 1- Mono/stereo, 0 - Dual Mono, 0 - for raw */ | ||
| 165 | __s8 syntc_id[2]; /* 0 for ID_SCE(Dula Mono), -1 for raw */ | ||
| 166 | __s8 syntc_tag[2]; /* raw - -1 and 0 -16 for rest of the streams */ | ||
| 167 | __u8 pce_present; /* Flag. 1- present 0 - not present, for RAW */ | ||
| 168 | __u8 sbr_type; /* sbr_type: 0-plain aac, 1-aac-v1, 2-aac-v2 */ | ||
| 169 | __u8 outchmode; /*0- mono, 1-stereo, 2-dual mono 3-Parametric stereo */ | ||
| 170 | __u8 ps_present; | ||
| 171 | }; | ||
| 172 | |||
| 173 | /* WMA Music Parameters Message */ | ||
| 174 | struct snd_wma_params { | ||
| 175 | __u16 codec; | ||
| 176 | __u8 num_chan; /* 1=Mono, 2=Stereo */ | ||
| 177 | __u8 pcm_wd_sz; /* 16/24 - bit*/ | ||
| 178 | __u32 brate; /* Use the hard coded value. */ | ||
| 179 | __u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */ | ||
| 180 | __u32 channel_mask; /* Channel Mask */ | ||
| 181 | __u16 format_tag; /* Format Tag */ | ||
| 182 | __u16 block_align; /* packet size */ | ||
| 183 | __u16 wma_encode_opt;/* Encoder option */ | ||
| 184 | __u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB */ | ||
| 185 | __u8 pcm_src; /* input pcm bit width */ | ||
| 186 | }; | ||
| 187 | |||
| 188 | /* Pre processing param structure */ | ||
| 189 | struct snd_prp_params { | ||
| 190 | __u32 reserved; /* No pre-processing defined yet */ | ||
| 191 | }; | ||
| 192 | |||
| 193 | /* Pre and post processing params structure */ | ||
| 194 | struct snd_ppp_params { | ||
| 195 | __u8 algo_id;/* Post/Pre processing algorithm ID */ | ||
| 196 | __u8 str_id; /*Only 5 bits used 0 - 31 are valid*/ | ||
| 197 | __u8 enable; /* 0= disable, 1= enable*/ | ||
| 198 | __u8 reserved; | ||
| 199 | __u32 size; /*Size of parameters for all blocks*/ | ||
| 200 | void *params; | ||
| 201 | } __attribute__ ((packed)); | ||
| 202 | |||
| 203 | struct snd_sst_postproc_info { | ||
| 204 | __u32 src_min; /* Supported SRC Min sampling freq */ | ||
| 205 | __u32 src_max; /* Supported SRC Max sampling freq */ | ||
| 206 | __u8 src; /* 0=Not supported, 1=Supported */ | ||
| 207 | __u8 bass_boost; /* 0=Not Supported, 1=Supported */ | ||
| 208 | __u8 stereo_widening; /* 0=Not Supported, 1=Supported */ | ||
| 209 | __u8 volume_control; /* 0=Not Supported, 1=Supported */ | ||
| 210 | __s16 min_vol; /* Minimum value of Volume in dB */ | ||
| 211 | __s16 max_vol; /* Maximum value of Volume in dB */ | ||
| 212 | __u8 mute_control; /* 0=No Mute, 1=Mute */ | ||
| 213 | __u8 reserved1; | ||
| 214 | __u16 reserved2; | ||
| 215 | }; | ||
| 216 | |||
| 217 | /* pre processing Capability info structure */ | ||
| 218 | struct snd_sst_prp_info { | ||
| 219 | __s16 min_vol; /* Minimum value of Volume in dB */ | ||
| 220 | __s16 max_vol; /* Maximum value of Volume in dB */ | ||
| 221 | __u8 volume_control; /* 0=Not Supported, 1=Supported */ | ||
| 222 | __u8 reserved1; /* for 32 bit alignment */ | ||
| 223 | __u16 reserved2; /* for 32 bit alignment */ | ||
| 224 | } __attribute__ ((packed)); | ||
| 225 | |||
| 226 | /*Pre / Post processing algorithms support*/ | ||
| 227 | struct snd_sst_ppp_info { | ||
| 228 | __u32 src:1; /* 0=Not supported, 1=Supported */ | ||
| 229 | __u32 mixer:1; /* 0=Not supported, 1=Supported */ | ||
| 230 | __u32 volume_control:1; /* 0=Not Supported, 1=Supported */ | ||
| 231 | __u32 mute_control:1; /* 0=Not Supported, 1=Supported */ | ||
| 232 | __u32 anc:1; /* 0=Not Supported, 1=Supported */ | ||
| 233 | __u32 side_tone:1; /* 0=Not Supported, 1=Supported */ | ||
| 234 | __u32 dc_removal:1; /* 0=Not Supported, 1=Supported */ | ||
| 235 | __u32 equalizer:1; /* 0=Not Supported, 1=Supported */ | ||
| 236 | __u32 spkr_prot:1; /* 0=Not Supported, 1=Supported */ | ||
| 237 | __u32 bass_boost:1; /* 0=Not Supported, 1=Supported */ | ||
| 238 | __u32 stereo_widening:1;/* 0=Not Supported, 1=Supported */ | ||
| 239 | __u32 rsvd1:21; | ||
| 240 | __u32 rsvd2; | ||
| 241 | }; | ||
| 242 | |||
| 243 | /* Firmware capabilities info */ | ||
| 244 | struct snd_sst_fw_info { | ||
| 245 | struct snd_sst_fw_version fw_version; /* Firmware version */ | ||
| 246 | __u8 audio_codecs_supported[8]; /* Codecs supported by FW */ | ||
| 247 | __u32 recommend_min_duration; /* Min duration for Lowpower Playback */ | ||
| 248 | __u8 max_pcm_streams_supported; /* Max num of PCM streams supported */ | ||
| 249 | __u8 max_enc_streams_supported; /* Max number of Encoded streams */ | ||
| 250 | __u16 reserved; /* 32 bit alignment*/ | ||
| 251 | struct snd_sst_ppp_info ppp_info; /* pre_processing mod cap info */ | ||
| 252 | struct snd_sst_postproc_info pop_info; /* Post processing cap info*/ | ||
| 253 | struct snd_sst_port_info port_info[3]; /* Port info */ | ||
| 254 | struct snd_sst_mix_info mix_info;/* Mixer info */ | ||
| 255 | __u32 min_input_buf; /* minmum i/p buffer for decode */ | ||
| 256 | }; | ||
| 257 | |||
| 258 | /* Codec params struture */ | ||
| 259 | union snd_sst_codec_params { | ||
| 260 | struct snd_pcm_params pcm_params; | ||
| 261 | struct snd_mp3_params mp3_params; | ||
| 262 | struct snd_aac_params aac_params; | ||
| 263 | struct snd_wma_params wma_params; | ||
| 264 | }; | ||
| 265 | |||
| 266 | |||
| 267 | struct snd_sst_stream_params { | ||
| 268 | union snd_sst_codec_params uc; | ||
| 269 | } __attribute__ ((packed)); | ||
| 270 | |||
| 271 | struct snd_sst_params { | ||
| 272 | __u32 result; | ||
| 273 | __u32 stream_id; | ||
| 274 | __u8 codec; | ||
| 275 | __u8 ops; | ||
| 276 | __u8 stream_type; | ||
| 277 | __u8 device_type; | ||
| 278 | struct snd_sst_stream_params sparams; | ||
| 279 | }; | ||
| 280 | |||
| 281 | struct snd_sst_vol { | ||
| 282 | __u32 stream_id; | ||
| 283 | __s32 volume; | ||
| 284 | __u32 ramp_duration; | ||
| 285 | __u32 ramp_type; /* Ramp type, default=0 */ | ||
| 286 | }; | ||
| 287 | |||
| 288 | struct snd_sst_mute { | ||
| 289 | __u32 stream_id; | ||
| 290 | __u32 mute; | ||
| 291 | }; | ||
| 292 | |||
| 293 | /* ioctl related stuff here */ | ||
| 294 | struct snd_sst_pmic_config { | ||
| 295 | __u32 sfreq; /* Sampling rate in Hz */ | ||
| 296 | __u16 num_chan; /* Mono =1 or Stereo =2 */ | ||
| 297 | __u16 pcm_wd_sz; /* Number of bits per sample */ | ||
| 298 | } __attribute__ ((packed)); | ||
| 299 | |||
| 300 | struct snd_sst_get_stream_params { | ||
| 301 | struct snd_sst_params codec_params; | ||
| 302 | struct snd_sst_pmic_config pcm_params; | ||
| 303 | }; | ||
| 304 | |||
| 305 | enum snd_sst_target_type { | ||
| 306 | SND_SST_TARGET_PMIC = 1, | ||
| 307 | SND_SST_TARGET_LPE, | ||
| 308 | SND_SST_TARGET_MODEM, | ||
| 309 | SND_SST_TARGET_BT, | ||
| 310 | SND_SST_TARGET_FM, | ||
| 311 | SND_SST_TARGET_NONE, | ||
| 312 | }; | ||
| 313 | |||
| 314 | enum snd_sst_device_type { | ||
| 315 | SND_SST_DEVICE_SSP = 1, | ||
| 316 | SND_SST_DEVICE_PCM, | ||
| 317 | SND_SST_DEVICE_OTHER, | ||
| 318 | }; | ||
| 319 | |||
| 320 | enum snd_sst_device_mode { | ||
| 321 | |||
| 322 | SND_SST_DEV_MODE_PCM_MODE1 = 1, /*(16-bit word, bit-length frame sync)*/ | ||
| 323 | SND_SST_DEV_MODE_PCM_MODE2, | ||
| 324 | SND_SST_DEV_MODE_PCM_MODE3, | ||
| 325 | SND_SST_DEV_MODE_PCM_MODE4_RIGHT_JUSTIFIED, | ||
| 326 | SND_SST_DEV_MODE_PCM_MODE4_LEFT_JUSTIFIED, | ||
| 327 | SND_SST_DEV_MODE_PCM_MODE4_I2S, /*(I2S mode, 16-bit words)*/ | ||
| 328 | SND_SST_DEV_MODE_PCM_MODE5, | ||
| 329 | SND_SST_DEV_MODE_PCM_MODE6, | ||
| 330 | }; | ||
| 331 | |||
| 332 | enum snd_sst_port_action { | ||
| 333 | SND_SST_PORT_PREPARE = 1, | ||
| 334 | SND_SST_PORT_ACTIVATE, | ||
| 335 | }; | ||
| 336 | |||
| 337 | /* Target selection per device structure */ | ||
| 338 | struct snd_sst_slot_info { | ||
| 339 | __u8 mix_enable; /* Mixer enable or disable */ | ||
| 340 | __u8 device_type; | ||
| 341 | __u8 device_instance; /* 0, 1, 2 */ | ||
| 342 | __u8 target_device; | ||
| 343 | __u16 target_sink; | ||
| 344 | __u8 slot[2]; | ||
| 345 | __u8 master; | ||
| 346 | __u8 action; | ||
| 347 | __u8 device_mode; | ||
| 348 | __u8 reserved; | ||
| 349 | struct snd_sst_pmic_config pcm_params; | ||
| 350 | } __attribute__ ((packed)); | ||
| 351 | |||
| 352 | #define SST_MAX_TARGET_DEVICES 3 | ||
| 353 | /* Target device list structure */ | ||
| 354 | struct snd_sst_target_device { | ||
| 355 | __u32 device_route; | ||
| 356 | struct snd_sst_slot_info devices[SST_MAX_TARGET_DEVICES]; | ||
| 357 | } __attribute__ ((packed)); | ||
| 358 | |||
| 359 | struct snd_sst_driver_info { | ||
| 360 | __u32 version; /* Version of the driver */ | ||
| 361 | __u32 active_pcm_streams; | ||
| 362 | __u32 active_enc_streams; | ||
| 363 | __u32 max_pcm_streams; | ||
| 364 | __u32 max_enc_streams; | ||
| 365 | __u32 buf_per_stream; | ||
| 366 | }; | ||
| 367 | |||
| 368 | enum snd_sst_buff_type { | ||
| 369 | SST_BUF_USER = 1, | ||
| 370 | SST_BUF_MMAP, | ||
| 371 | SST_BUF_RAR, | ||
| 372 | }; | ||
| 373 | |||
| 374 | struct snd_sst_mmap_buff_entry { | ||
| 375 | unsigned int offset; | ||
| 376 | unsigned int size; | ||
| 377 | }; | ||
| 378 | |||
| 379 | struct snd_sst_mmap_buffs { | ||
| 380 | unsigned int entries; | ||
| 381 | enum snd_sst_buff_type type; | ||
| 382 | struct snd_sst_mmap_buff_entry *buff; | ||
| 383 | }; | ||
| 384 | |||
| 385 | struct snd_sst_buff_entry { | ||
| 386 | void *buffer; | ||
| 387 | unsigned int size; | ||
| 388 | }; | ||
| 389 | |||
| 390 | struct snd_sst_buffs { | ||
| 391 | unsigned int entries; | ||
| 392 | __u8 type; | ||
| 393 | struct snd_sst_buff_entry *buff_entry; | ||
| 394 | }; | ||
| 395 | |||
| 396 | struct snd_sst_dbufs { | ||
| 397 | unsigned long long input_bytes_consumed; | ||
| 398 | unsigned long long output_bytes_produced; | ||
| 399 | struct snd_sst_buffs *ibufs; | ||
| 400 | struct snd_sst_buffs *obufs; | ||
| 401 | }; | ||
| 402 | |||
| 403 | struct snd_sst_tuning_params { | ||
| 404 | __u8 type; | ||
| 405 | __u8 str_id; | ||
| 406 | __u8 size; | ||
| 407 | __u8 rsvd; | ||
| 408 | __aligned_u64 addr; | ||
| 409 | } __attribute__ ((packed)); | ||
| 410 | /*IOCTL defined here */ | ||
| 411 | /*SST MMF IOCTLS only */ | ||
| 412 | #define SNDRV_SST_STREAM_SET_PARAMS _IOR('L', 0x00, \ | ||
| 413 | struct snd_sst_stream_params *) | ||
| 414 | #define SNDRV_SST_STREAM_GET_PARAMS _IOWR('L', 0x01, \ | ||
| 415 | struct snd_sst_get_stream_params *) | ||
| 416 | #define SNDRV_SST_STREAM_GET_TSTAMP _IOWR('L', 0x02, __u64 *) | ||
| 417 | #define SNDRV_SST_STREAM_DECODE _IOWR('L', 0x03, struct snd_sst_dbufs *) | ||
| 418 | #define SNDRV_SST_STREAM_BYTES_DECODED _IOWR('L', 0x04, __u64 *) | ||
| 419 | #define SNDRV_SST_STREAM_START _IO('A', 0x42) | ||
| 420 | #define SNDRV_SST_STREAM_DROP _IO('A', 0x43) | ||
| 421 | #define SNDRV_SST_STREAM_DRAIN _IO('A', 0x44) | ||
| 422 | #define SNDRV_SST_STREAM_PAUSE _IOW('A', 0x45, int) | ||
| 423 | #define SNDRV_SST_STREAM_RESUME _IO('A', 0x47) | ||
| 424 | #define SNDRV_SST_MMAP_PLAY _IOW('L', 0x05, struct snd_sst_mmap_buffs *) | ||
| 425 | #define SNDRV_SST_MMAP_CAPTURE _IOW('L', 0x06, struct snd_sst_mmap_buffs *) | ||
| 426 | /*SST common ioctls */ | ||
| 427 | #define SNDRV_SST_DRIVER_INFO _IOR('L', 0x10, struct snd_sst_driver_info *) | ||
| 428 | #define SNDRV_SST_SET_VOL _IOW('L', 0x11, struct snd_sst_vol *) | ||
| 429 | #define SNDRV_SST_GET_VOL _IOW('L', 0x12, struct snd_sst_vol *) | ||
| 430 | #define SNDRV_SST_MUTE _IOW('L', 0x13, struct snd_sst_mute *) | ||
| 431 | /*AM Ioctly only */ | ||
| 432 | #define SNDRV_SST_FW_INFO _IOR('L', 0x20, struct snd_sst_fw_info *) | ||
| 433 | #define SNDRV_SST_SET_TARGET_DEVICE _IOW('L', 0x21, \ | ||
| 434 | struct snd_sst_target_device *) | ||
| 435 | /*DSP Ioctls on /dev/intel_sst_ctrl only*/ | ||
| 436 | #define SNDRV_SST_SET_ALGO _IOW('L', 0x30, struct snd_ppp_params *) | ||
| 437 | #define SNDRV_SST_GET_ALGO _IOWR('L', 0x31, struct snd_ppp_params *) | ||
| 438 | #define SNDRV_SST_TUNING_PARAMS _IOW('L', 0x32, struct snd_sst_tuning_params *) | ||
| 439 | |||
| 440 | #endif /* __INTEL_SST_IOCTL_H__ */ | ||
diff --git a/drivers/staging/intel_sst/intel_sst_ipc.c b/drivers/staging/intel_sst/intel_sst_ipc.c new file mode 100644 index 00000000000..5c3444f6ab4 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_ipc.c | |||
| @@ -0,0 +1,774 @@ | |||
| 1 | /* | ||
| 2 | * intel_sst_ipc.c - Intel SST Driver for audio engine | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corporation | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * | ||
| 26 | * This file defines all ipc functions | ||
| 27 | */ | ||
| 28 | |||
| 29 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 30 | |||
| 31 | #include <linux/pci.h> | ||
| 32 | #include <linux/firmware.h> | ||
| 33 | #include <linux/sched.h> | ||
| 34 | #include "intel_sst.h" | ||
| 35 | #include "intel_sst_ioctl.h" | ||
| 36 | #include "intel_sst_fw_ipc.h" | ||
| 37 | #include "intel_sst_common.h" | ||
| 38 | |||
| 39 | /* | ||
| 40 | * sst_send_sound_card_type - send sound card type | ||
| 41 | * | ||
| 42 | * this function sends the sound card type to sst dsp engine | ||
| 43 | */ | ||
| 44 | static void sst_send_sound_card_type(void) | ||
| 45 | { | ||
| 46 | struct ipc_post *msg = NULL; | ||
| 47 | |||
| 48 | if (sst_create_short_msg(&msg)) | ||
| 49 | return; | ||
| 50 | |||
| 51 | sst_fill_header(&msg->header, IPC_IA_SET_PMIC_TYPE, 0, 0); | ||
| 52 | msg->header.part.data = sst_drv_ctx->pmic_vendor; | ||
| 53 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 54 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 55 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 56 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | |||
| 60 | /** | ||
| 61 | * sst_post_message - Posts message to SST | ||
| 62 | * | ||
| 63 | * @work: Pointer to work structure | ||
| 64 | * | ||
| 65 | * This function is called by any component in driver which | ||
| 66 | * wants to send an IPC message. This will post message only if | ||
| 67 | * busy bit is free | ||
| 68 | */ | ||
| 69 | void sst_post_message(struct work_struct *work) | ||
| 70 | { | ||
| 71 | struct ipc_post *msg; | ||
| 72 | union ipc_header header; | ||
| 73 | union interrupt_reg imr; | ||
| 74 | int retval = 0; | ||
| 75 | imr.full = 0; | ||
| 76 | |||
| 77 | /*To check if LPE is in stalled state.*/ | ||
| 78 | retval = sst_stalled(); | ||
| 79 | if (retval < 0) { | ||
| 80 | pr_err("in stalled state\n"); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | pr_debug("post message called\n"); | ||
| 84 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 85 | |||
| 86 | /* check list */ | ||
| 87 | if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) { | ||
| 88 | /* list is empty, mask imr */ | ||
| 89 | pr_debug("Empty msg queue... masking\n"); | ||
| 90 | imr.full = readl(sst_drv_ctx->shim + SST_IMRX); | ||
| 91 | imr.part.done_interrupt = 1; | ||
| 92 | /* dummy register for shim workaround */ | ||
| 93 | sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); | ||
| 94 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 95 | return; | ||
| 96 | } | ||
| 97 | |||
| 98 | /* check busy bit */ | ||
| 99 | header.full = sst_shim_read(sst_drv_ctx->shim, SST_IPCX); | ||
| 100 | if (header.part.busy) { | ||
| 101 | /* busy, unmask */ | ||
| 102 | pr_debug("Busy not free... unmasking\n"); | ||
| 103 | imr.full = readl(sst_drv_ctx->shim + SST_IMRX); | ||
| 104 | imr.part.done_interrupt = 0; | ||
| 105 | /* dummy register for shim workaround */ | ||
| 106 | sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); | ||
| 107 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 108 | return; | ||
| 109 | } | ||
| 110 | /* copy msg from list */ | ||
| 111 | msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next, | ||
| 112 | struct ipc_post, node); | ||
| 113 | list_del(&msg->node); | ||
| 114 | pr_debug("Post message: header = %x\n", msg->header.full); | ||
| 115 | pr_debug("size: = %x\n", msg->header.part.data); | ||
| 116 | if (msg->header.part.large) | ||
| 117 | memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, | ||
| 118 | msg->mailbox_data, msg->header.part.data); | ||
| 119 | /* dummy register for shim workaround */ | ||
| 120 | |||
| 121 | sst_shim_write(sst_drv_ctx->shim, SST_IPCX, msg->header.full); | ||
| 122 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 123 | |||
| 124 | kfree(msg->mailbox_data); | ||
| 125 | kfree(msg); | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | |||
| 129 | /* | ||
| 130 | * sst_clear_interrupt - clear the SST FW interrupt | ||
| 131 | * | ||
| 132 | * This function clears the interrupt register after the interrupt | ||
| 133 | * bottom half is complete allowing next interrupt to arrive | ||
| 134 | */ | ||
| 135 | void sst_clear_interrupt(void) | ||
| 136 | { | ||
| 137 | union interrupt_reg isr; | ||
| 138 | union interrupt_reg imr; | ||
| 139 | union ipc_header clear_ipc; | ||
| 140 | |||
| 141 | imr.full = sst_shim_read(sst_drv_ctx->shim, SST_IMRX); | ||
| 142 | isr.full = sst_shim_read(sst_drv_ctx->shim, SST_ISRX); | ||
| 143 | /* write 1 to clear */; | ||
| 144 | isr.part.busy_interrupt = 1; | ||
| 145 | sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full); | ||
| 146 | /* Set IA done bit */ | ||
| 147 | clear_ipc.full = sst_shim_read(sst_drv_ctx->shim, SST_IPCD); | ||
| 148 | clear_ipc.part.busy = 0; | ||
| 149 | clear_ipc.part.done = 1; | ||
| 150 | clear_ipc.part.data = IPC_ACK_SUCCESS; | ||
| 151 | sst_shim_write(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full); | ||
| 152 | /* un mask busy interrupt */ | ||
| 153 | imr.part.busy_interrupt = 0; | ||
| 154 | sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); | ||
| 155 | } | ||
| 156 | |||
| 157 | void sst_restore_fw_context(void) | ||
| 158 | { | ||
| 159 | struct snd_sst_ctxt_params fw_context; | ||
| 160 | struct ipc_post *msg = NULL; | ||
| 161 | |||
| 162 | pr_debug("restore_fw_context\n"); | ||
| 163 | /*check cpu type*/ | ||
| 164 | if (sst_drv_ctx->pci_id != SST_MFLD_PCI_ID) | ||
| 165 | return; | ||
| 166 | /*not supported for rest*/ | ||
| 167 | if (!sst_drv_ctx->fw_cntx_size) | ||
| 168 | return; | ||
| 169 | /*nothing to restore*/ | ||
| 170 | pr_debug("restoring context......\n"); | ||
| 171 | /*send msg to fw*/ | ||
| 172 | if (sst_create_large_msg(&msg)) | ||
| 173 | return; | ||
| 174 | |||
| 175 | sst_fill_header(&msg->header, IPC_IA_SET_FW_CTXT, 1, 0); | ||
| 176 | msg->header.part.data = sizeof(fw_context) + sizeof(u32); | ||
| 177 | fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx); | ||
| 178 | fw_context.size = sst_drv_ctx->fw_cntx_size; | ||
| 179 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 180 | memcpy(msg->mailbox_data + sizeof(u32), | ||
| 181 | &fw_context, sizeof(fw_context)); | ||
| 182 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 183 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 184 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 185 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 186 | return; | ||
| 187 | } | ||
| 188 | /* | ||
| 189 | * process_fw_init - process the FW init msg | ||
| 190 | * | ||
| 191 | * @msg: IPC message from FW | ||
| 192 | * | ||
| 193 | * This function processes the FW init msg from FW | ||
| 194 | * marks FW state and prints debug info of loaded FW | ||
| 195 | */ | ||
| 196 | int process_fw_init(struct sst_ipc_msg_wq *msg) | ||
| 197 | { | ||
| 198 | struct ipc_header_fw_init *init = | ||
| 199 | (struct ipc_header_fw_init *)msg->mailbox; | ||
| 200 | int retval = 0; | ||
| 201 | |||
| 202 | pr_debug("*** FW Init msg came***\n"); | ||
| 203 | if (init->result) { | ||
| 204 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 205 | sst_drv_ctx->sst_state = SST_ERROR; | ||
| 206 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 207 | pr_debug("FW Init failed, Error %x\n", init->result); | ||
| 208 | pr_err("FW Init failed, Error %x\n", init->result); | ||
| 209 | retval = -init->result; | ||
| 210 | return retval; | ||
| 211 | } | ||
| 212 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) | ||
| 213 | sst_send_sound_card_type(); | ||
| 214 | mutex_lock(&sst_drv_ctx->sst_lock); | ||
| 215 | sst_drv_ctx->sst_state = SST_FW_RUNNING; | ||
| 216 | sst_drv_ctx->lpe_stalled = 0; | ||
| 217 | mutex_unlock(&sst_drv_ctx->sst_lock); | ||
| 218 | pr_debug("FW Version %02x.%02x.%02x\n", init->fw_version.major, | ||
| 219 | init->fw_version.minor, init->fw_version.build); | ||
| 220 | pr_debug("Build Type %x\n", init->fw_version.type); | ||
| 221 | pr_debug(" Build date %s Time %s\n", | ||
| 222 | init->build_info.date, init->build_info.time); | ||
| 223 | sst_wake_up_alloc_block(sst_drv_ctx, FW_DWNL_ID, retval, NULL); | ||
| 224 | sst_restore_fw_context(); | ||
| 225 | return retval; | ||
| 226 | } | ||
| 227 | /** | ||
| 228 | * sst_process_message - Processes message from SST | ||
| 229 | * | ||
| 230 | * @work: Pointer to work structure | ||
| 231 | * | ||
| 232 | * This function is scheduled by ISR | ||
| 233 | * It take a msg from process_queue and does action based on msg | ||
| 234 | */ | ||
| 235 | void sst_process_message(struct work_struct *work) | ||
| 236 | { | ||
| 237 | struct sst_ipc_msg_wq *msg = | ||
| 238 | container_of(work, struct sst_ipc_msg_wq, wq); | ||
| 239 | int str_id = msg->header.part.str_id; | ||
| 240 | |||
| 241 | pr_debug("IPC process for %x\n", msg->header.full); | ||
| 242 | |||
| 243 | /* based on msg in list call respective handler */ | ||
| 244 | switch (msg->header.part.msg_id) { | ||
| 245 | case IPC_SST_BUF_UNDER_RUN: | ||
| 246 | case IPC_SST_BUF_OVER_RUN: | ||
| 247 | if (sst_validate_strid(str_id)) { | ||
| 248 | pr_err("stream id %d invalid\n", str_id); | ||
| 249 | break; | ||
| 250 | } | ||
| 251 | pr_err("Buffer under/overrun for %d\n", | ||
| 252 | msg->header.part.str_id); | ||
| 253 | pr_err("Got Underrun & not to send data...ignore\n"); | ||
| 254 | break; | ||
| 255 | |||
| 256 | case IPC_SST_GET_PLAY_FRAMES: | ||
| 257 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { | ||
| 258 | struct stream_info *stream ; | ||
| 259 | |||
| 260 | if (sst_validate_strid(str_id)) { | ||
| 261 | pr_err("strid %d invalid\n", str_id); | ||
| 262 | break; | ||
| 263 | } | ||
| 264 | /* call sst_play_frame */ | ||
| 265 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 266 | pr_debug("sst_play_frames for %d\n", | ||
| 267 | msg->header.part.str_id); | ||
| 268 | mutex_lock(&sst_drv_ctx->streams[str_id].lock); | ||
| 269 | sst_play_frame(msg->header.part.str_id); | ||
| 270 | mutex_unlock(&sst_drv_ctx->streams[str_id].lock); | ||
| 271 | break; | ||
| 272 | } else | ||
| 273 | pr_err("sst_play_frames for Penwell!!\n"); | ||
| 274 | |||
| 275 | case IPC_SST_GET_CAPT_FRAMES: | ||
| 276 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { | ||
| 277 | struct stream_info *stream; | ||
| 278 | /* call sst_capture_frame */ | ||
| 279 | if (sst_validate_strid(str_id)) { | ||
| 280 | pr_err("str id %d invalid\n", str_id); | ||
| 281 | break; | ||
| 282 | } | ||
| 283 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 284 | pr_debug("sst_capture_frames for %d\n", | ||
| 285 | msg->header.part.str_id); | ||
| 286 | mutex_lock(&stream->lock); | ||
| 287 | if (stream->mmapped == false && | ||
| 288 | stream->src == SST_DRV) { | ||
| 289 | pr_debug("waking up block for copy.\n"); | ||
| 290 | stream->data_blk.ret_code = 0; | ||
| 291 | stream->data_blk.condition = true; | ||
| 292 | stream->data_blk.on = false; | ||
| 293 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 294 | } else | ||
| 295 | sst_capture_frame(msg->header.part.str_id); | ||
| 296 | mutex_unlock(&stream->lock); | ||
| 297 | } else | ||
| 298 | pr_err("sst_play_frames for Penwell!!\n"); | ||
| 299 | break; | ||
| 300 | |||
| 301 | case IPC_IA_PRINT_STRING: | ||
| 302 | pr_debug("been asked to print something by fw\n"); | ||
| 303 | /* TBD */ | ||
| 304 | break; | ||
| 305 | |||
| 306 | case IPC_IA_FW_INIT_CMPLT: { | ||
| 307 | /* send next data to FW */ | ||
| 308 | process_fw_init(msg); | ||
| 309 | break; | ||
| 310 | } | ||
| 311 | |||
| 312 | case IPC_SST_STREAM_PROCESS_FATAL_ERR: | ||
| 313 | if (sst_validate_strid(str_id)) { | ||
| 314 | pr_err("stream id %d invalid\n", str_id); | ||
| 315 | break; | ||
| 316 | } | ||
| 317 | pr_err("codec fatal error %x stream %d...\n", | ||
| 318 | msg->header.full, msg->header.part.str_id); | ||
| 319 | pr_err("Dropping the stream\n"); | ||
| 320 | sst_drop_stream(msg->header.part.str_id); | ||
| 321 | break; | ||
| 322 | case IPC_IA_LPE_GETTING_STALLED: | ||
| 323 | sst_drv_ctx->lpe_stalled = 1; | ||
| 324 | break; | ||
| 325 | case IPC_IA_LPE_UNSTALLED: | ||
| 326 | sst_drv_ctx->lpe_stalled = 0; | ||
| 327 | break; | ||
| 328 | default: | ||
| 329 | /* Illegal case */ | ||
| 330 | pr_err("Unhandled msg %x header %x\n", | ||
| 331 | msg->header.part.msg_id, msg->header.full); | ||
| 332 | } | ||
| 333 | sst_clear_interrupt(); | ||
| 334 | return; | ||
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * sst_process_reply - Processes reply message from SST | ||
| 339 | * | ||
| 340 | * @work: Pointer to work structure | ||
| 341 | * | ||
| 342 | * This function is scheduled by ISR | ||
| 343 | * It take a reply msg from response_queue and | ||
| 344 | * does action based on msg | ||
| 345 | */ | ||
| 346 | void sst_process_reply(struct work_struct *work) | ||
| 347 | { | ||
| 348 | struct sst_ipc_msg_wq *msg = | ||
| 349 | container_of(work, struct sst_ipc_msg_wq, wq); | ||
| 350 | |||
| 351 | int str_id = msg->header.part.str_id; | ||
| 352 | struct stream_info *str_info; | ||
| 353 | |||
| 354 | switch (msg->header.part.msg_id) { | ||
| 355 | case IPC_IA_TARGET_DEV_SELECT: | ||
| 356 | if (!msg->header.part.data) { | ||
| 357 | sst_drv_ctx->tgt_dev_blk.ret_code = 0; | ||
| 358 | } else { | ||
| 359 | pr_err(" Msg %x reply error %x\n", | ||
| 360 | msg->header.part.msg_id, msg->header.part.data); | ||
| 361 | sst_drv_ctx->tgt_dev_blk.ret_code = | ||
| 362 | -msg->header.part.data; | ||
| 363 | } | ||
| 364 | |||
| 365 | if (sst_drv_ctx->tgt_dev_blk.on == true) { | ||
| 366 | sst_drv_ctx->tgt_dev_blk.condition = true; | ||
| 367 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 368 | } | ||
| 369 | break; | ||
| 370 | case IPC_IA_ALG_PARAMS: { | ||
| 371 | pr_debug("sst:IPC_ALG_PARAMS response %x\n", msg->header.full); | ||
| 372 | pr_debug("sst: data value %x\n", msg->header.part.data); | ||
| 373 | pr_debug("sst: large value %x\n", msg->header.part.large); | ||
| 374 | |||
| 375 | if (!msg->header.part.large) { | ||
| 376 | if (!msg->header.part.data) { | ||
| 377 | pr_debug("sst: alg set success\n"); | ||
| 378 | sst_drv_ctx->ppp_params_blk.ret_code = 0; | ||
| 379 | } else { | ||
| 380 | pr_debug("sst: alg set failed\n"); | ||
| 381 | sst_drv_ctx->ppp_params_blk.ret_code = | ||
| 382 | -msg->header.part.data; | ||
| 383 | } | ||
| 384 | |||
| 385 | } else if (msg->header.part.data) { | ||
| 386 | struct snd_ppp_params *mailbox_params, *get_params; | ||
| 387 | char *params; | ||
| 388 | |||
| 389 | pr_debug("sst: alg get success\n"); | ||
| 390 | mailbox_params = (struct snd_ppp_params *)msg->mailbox; | ||
| 391 | get_params = kzalloc(sizeof(*get_params), GFP_KERNEL); | ||
| 392 | if (get_params == NULL) { | ||
| 393 | pr_err("sst: out of memory for ALG PARAMS"); | ||
| 394 | break; | ||
| 395 | } | ||
| 396 | memcpy_fromio(get_params, mailbox_params, | ||
| 397 | sizeof(*get_params)); | ||
| 398 | get_params->params = kzalloc(mailbox_params->size, | ||
| 399 | GFP_KERNEL); | ||
| 400 | if (get_params->params == NULL) { | ||
| 401 | kfree(get_params); | ||
| 402 | pr_err("sst: out of memory for ALG PARAMS block"); | ||
| 403 | break; | ||
| 404 | } | ||
| 405 | params = msg->mailbox; | ||
| 406 | params = params + sizeof(*mailbox_params) - sizeof(u32); | ||
| 407 | memcpy_fromio(get_params->params, params, | ||
| 408 | get_params->size); | ||
| 409 | sst_drv_ctx->ppp_params_blk.ret_code = 0; | ||
| 410 | sst_drv_ctx->ppp_params_blk.data = get_params; | ||
| 411 | } | ||
| 412 | |||
| 413 | if (sst_drv_ctx->ppp_params_blk.on == true) { | ||
| 414 | sst_drv_ctx->ppp_params_blk.condition = true; | ||
| 415 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 416 | } | ||
| 417 | break; | ||
| 418 | } | ||
| 419 | |||
| 420 | case IPC_IA_TUNING_PARAMS: { | ||
| 421 | pr_debug("sst:IPC_TUNING_PARAMS resp: %x\n", msg->header.full); | ||
| 422 | pr_debug("data value %x\n", msg->header.part.data); | ||
| 423 | if (msg->header.part.large) { | ||
| 424 | pr_debug("alg set failed\n"); | ||
| 425 | sst_drv_ctx->ppp_params_blk.ret_code = | ||
| 426 | -msg->header.part.data; | ||
| 427 | } else { | ||
| 428 | pr_debug("alg set success\n"); | ||
| 429 | sst_drv_ctx->ppp_params_blk.ret_code = 0; | ||
| 430 | } | ||
| 431 | if (sst_drv_ctx->ppp_params_blk.on == true) { | ||
| 432 | sst_drv_ctx->ppp_params_blk.condition = true; | ||
| 433 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 437 | case IPC_IA_GET_FW_INFO: { | ||
| 438 | struct snd_sst_fw_info *fw_info = | ||
| 439 | (struct snd_sst_fw_info *)msg->mailbox; | ||
| 440 | if (msg->header.part.large) { | ||
| 441 | int major = fw_info->fw_version.major; | ||
| 442 | int minor = fw_info->fw_version.minor; | ||
| 443 | int build = fw_info->fw_version.build; | ||
| 444 | pr_debug("Msg succeeded %x\n", | ||
| 445 | msg->header.part.msg_id); | ||
| 446 | pr_debug("INFO: ***FW*** = %02d.%02d.%02d\n", | ||
| 447 | major, minor, build); | ||
| 448 | memcpy_fromio(sst_drv_ctx->fw_info_blk.data, | ||
| 449 | ((struct snd_sst_fw_info *)(msg->mailbox)), | ||
| 450 | sizeof(struct snd_sst_fw_info)); | ||
| 451 | sst_drv_ctx->fw_info_blk.ret_code = 0; | ||
| 452 | } else { | ||
| 453 | pr_err(" Msg %x reply error %x\n", | ||
| 454 | msg->header.part.msg_id, msg->header.part.data); | ||
| 455 | sst_drv_ctx->fw_info_blk.ret_code = | ||
| 456 | -msg->header.part.data; | ||
| 457 | } | ||
| 458 | if (sst_drv_ctx->fw_info_blk.on == true) { | ||
| 459 | pr_debug("Memcopy succeeded\n"); | ||
| 460 | sst_drv_ctx->fw_info_blk.on = false; | ||
| 461 | sst_drv_ctx->fw_info_blk.condition = true; | ||
| 462 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 463 | } | ||
| 464 | break; | ||
| 465 | } | ||
| 466 | case IPC_IA_SET_STREAM_MUTE: | ||
| 467 | if (!msg->header.part.data) { | ||
| 468 | pr_debug("Msg succeeded %x\n", | ||
| 469 | msg->header.part.msg_id); | ||
| 470 | sst_drv_ctx->mute_info_blk.ret_code = 0; | ||
| 471 | } else { | ||
| 472 | pr_err(" Msg %x reply error %x\n", | ||
| 473 | msg->header.part.msg_id, msg->header.part.data); | ||
| 474 | sst_drv_ctx->mute_info_blk.ret_code = | ||
| 475 | -msg->header.part.data; | ||
| 476 | |||
| 477 | } | ||
| 478 | if (sst_drv_ctx->mute_info_blk.on == true) { | ||
| 479 | sst_drv_ctx->mute_info_blk.on = false; | ||
| 480 | sst_drv_ctx->mute_info_blk.condition = true; | ||
| 481 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 482 | } | ||
| 483 | break; | ||
| 484 | case IPC_IA_SET_STREAM_VOL: | ||
| 485 | if (!msg->header.part.data) { | ||
| 486 | pr_debug("Msg succeeded %x\n", | ||
| 487 | msg->header.part.msg_id); | ||
| 488 | sst_drv_ctx->vol_info_blk.ret_code = 0; | ||
| 489 | } else { | ||
| 490 | pr_err(" Msg %x reply error %x\n", | ||
| 491 | msg->header.part.msg_id, | ||
| 492 | msg->header.part.data); | ||
| 493 | sst_drv_ctx->vol_info_blk.ret_code = | ||
| 494 | -msg->header.part.data; | ||
| 495 | |||
| 496 | } | ||
| 497 | |||
| 498 | if (sst_drv_ctx->vol_info_blk.on == true) { | ||
| 499 | sst_drv_ctx->vol_info_blk.on = false; | ||
| 500 | sst_drv_ctx->vol_info_blk.condition = true; | ||
| 501 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 502 | } | ||
| 503 | break; | ||
| 504 | case IPC_IA_GET_STREAM_VOL: | ||
| 505 | if (msg->header.part.large) { | ||
| 506 | pr_debug("Large Msg Received Successfully\n"); | ||
| 507 | pr_debug("Msg succeeded %x\n", | ||
| 508 | msg->header.part.msg_id); | ||
| 509 | memcpy_fromio(sst_drv_ctx->vol_info_blk.data, | ||
| 510 | (void *) msg->mailbox, | ||
| 511 | sizeof(struct snd_sst_vol)); | ||
| 512 | sst_drv_ctx->vol_info_blk.ret_code = 0; | ||
| 513 | } else { | ||
| 514 | pr_err("Msg %x reply error %x\n", | ||
| 515 | msg->header.part.msg_id, msg->header.part.data); | ||
| 516 | sst_drv_ctx->vol_info_blk.ret_code = | ||
| 517 | -msg->header.part.data; | ||
| 518 | } | ||
| 519 | if (sst_drv_ctx->vol_info_blk.on == true) { | ||
| 520 | sst_drv_ctx->vol_info_blk.on = false; | ||
| 521 | sst_drv_ctx->vol_info_blk.condition = true; | ||
| 522 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 523 | } | ||
| 524 | break; | ||
| 525 | |||
| 526 | case IPC_IA_GET_STREAM_PARAMS: | ||
| 527 | if (sst_validate_strid(str_id)) { | ||
| 528 | pr_err("stream id %d invalid\n", str_id); | ||
| 529 | break; | ||
| 530 | } | ||
| 531 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 532 | if (msg->header.part.large) { | ||
| 533 | pr_debug("Get stream large success\n"); | ||
| 534 | memcpy_fromio(str_info->ctrl_blk.data, | ||
| 535 | ((void *)(msg->mailbox)), | ||
| 536 | sizeof(struct snd_sst_fw_get_stream_params)); | ||
| 537 | str_info->ctrl_blk.ret_code = 0; | ||
| 538 | } else { | ||
| 539 | pr_err("Msg %x reply error %x\n", | ||
| 540 | msg->header.part.msg_id, msg->header.part.data); | ||
| 541 | str_info->ctrl_blk.ret_code = -msg->header.part.data; | ||
| 542 | } | ||
| 543 | if (str_info->ctrl_blk.on == true) { | ||
| 544 | str_info->ctrl_blk.on = false; | ||
| 545 | str_info->ctrl_blk.condition = true; | ||
| 546 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 547 | } | ||
| 548 | break; | ||
| 549 | case IPC_IA_DECODE_FRAMES: | ||
| 550 | if (sst_validate_strid(str_id)) { | ||
| 551 | pr_err("stream id %d invalid\n", str_id); | ||
| 552 | break; | ||
| 553 | } | ||
| 554 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 555 | if (msg->header.part.large) { | ||
| 556 | pr_debug("Msg succeeded %x\n", | ||
| 557 | msg->header.part.msg_id); | ||
| 558 | memcpy_fromio(str_info->data_blk.data, | ||
| 559 | ((void *)(msg->mailbox)), | ||
| 560 | sizeof(struct snd_sst_decode_info)); | ||
| 561 | str_info->data_blk.ret_code = 0; | ||
| 562 | } else { | ||
| 563 | pr_err("Msg %x reply error %x\n", | ||
| 564 | msg->header.part.msg_id, msg->header.part.data); | ||
| 565 | str_info->data_blk.ret_code = -msg->header.part.data; | ||
| 566 | } | ||
| 567 | if (str_info->data_blk.on == true) { | ||
| 568 | str_info->data_blk.on = false; | ||
| 569 | str_info->data_blk.condition = true; | ||
| 570 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 571 | } | ||
| 572 | break; | ||
| 573 | case IPC_IA_DRAIN_STREAM: | ||
| 574 | if (sst_validate_strid(str_id)) { | ||
| 575 | pr_err("stream id %d invalid\n", str_id); | ||
| 576 | break; | ||
| 577 | } | ||
| 578 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 579 | if (!msg->header.part.data) { | ||
| 580 | pr_debug("Msg succeeded %x\n", | ||
| 581 | msg->header.part.msg_id); | ||
| 582 | str_info->ctrl_blk.ret_code = 0; | ||
| 583 | |||
| 584 | } else { | ||
| 585 | pr_err(" Msg %x reply error %x\n", | ||
| 586 | msg->header.part.msg_id, msg->header.part.data); | ||
| 587 | str_info->ctrl_blk.ret_code = -msg->header.part.data; | ||
| 588 | |||
| 589 | } | ||
| 590 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 591 | if (str_info->data_blk.on == true) { | ||
| 592 | str_info->data_blk.on = false; | ||
| 593 | str_info->data_blk.condition = true; | ||
| 594 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 595 | } | ||
| 596 | break; | ||
| 597 | |||
| 598 | case IPC_IA_DROP_STREAM: | ||
| 599 | if (sst_validate_strid(str_id)) { | ||
| 600 | pr_err("str id %d invalid\n", str_id); | ||
| 601 | break; | ||
| 602 | } | ||
| 603 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 604 | if (msg->header.part.large) { | ||
| 605 | struct snd_sst_drop_response *drop_resp = | ||
| 606 | (struct snd_sst_drop_response *)msg->mailbox; | ||
| 607 | |||
| 608 | pr_debug("Drop ret bytes %x\n", drop_resp->bytes); | ||
| 609 | |||
| 610 | str_info->curr_bytes = drop_resp->bytes; | ||
| 611 | str_info->ctrl_blk.ret_code = 0; | ||
| 612 | } else { | ||
| 613 | pr_err(" Msg %x reply error %x\n", | ||
| 614 | msg->header.part.msg_id, msg->header.part.data); | ||
| 615 | str_info->ctrl_blk.ret_code = -msg->header.part.data; | ||
| 616 | } | ||
| 617 | if (str_info->ctrl_blk.on == true) { | ||
| 618 | str_info->ctrl_blk.on = false; | ||
| 619 | str_info->ctrl_blk.condition = true; | ||
| 620 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 621 | } | ||
| 622 | break; | ||
| 623 | case IPC_IA_ENABLE_RX_TIME_SLOT: | ||
| 624 | if (!msg->header.part.data) { | ||
| 625 | pr_debug("RX_TIME_SLOT success\n"); | ||
| 626 | sst_drv_ctx->hs_info_blk.ret_code = 0; | ||
| 627 | } else { | ||
| 628 | pr_err(" Msg %x reply error %x\n", | ||
| 629 | msg->header.part.msg_id, | ||
| 630 | msg->header.part.data); | ||
| 631 | sst_drv_ctx->hs_info_blk.ret_code = | ||
| 632 | -msg->header.part.data; | ||
| 633 | } | ||
| 634 | if (sst_drv_ctx->hs_info_blk.on == true) { | ||
| 635 | sst_drv_ctx->hs_info_blk.on = false; | ||
| 636 | sst_drv_ctx->hs_info_blk.condition = true; | ||
| 637 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 638 | } | ||
| 639 | break; | ||
| 640 | case IPC_IA_PAUSE_STREAM: | ||
| 641 | case IPC_IA_RESUME_STREAM: | ||
| 642 | case IPC_IA_SET_STREAM_PARAMS: | ||
| 643 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 644 | if (!msg->header.part.data) { | ||
| 645 | pr_debug("Msg succeeded %x\n", | ||
| 646 | msg->header.part.msg_id); | ||
| 647 | str_info->ctrl_blk.ret_code = 0; | ||
| 648 | } else { | ||
| 649 | pr_err(" Msg %x reply error %x\n", | ||
| 650 | msg->header.part.msg_id, | ||
| 651 | msg->header.part.data); | ||
| 652 | str_info->ctrl_blk.ret_code = -msg->header.part.data; | ||
| 653 | } | ||
| 654 | if (sst_validate_strid(str_id)) { | ||
| 655 | pr_err(" stream id %d invalid\n", str_id); | ||
| 656 | break; | ||
| 657 | } | ||
| 658 | |||
| 659 | if (str_info->ctrl_blk.on == true) { | ||
| 660 | str_info->ctrl_blk.on = false; | ||
| 661 | str_info->ctrl_blk.condition = true; | ||
| 662 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 663 | } | ||
| 664 | break; | ||
| 665 | |||
| 666 | case IPC_IA_FREE_STREAM: | ||
| 667 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 668 | if (!msg->header.part.data) { | ||
| 669 | pr_debug("Stream %d freed\n", str_id); | ||
| 670 | } else { | ||
| 671 | pr_err("Free for %d ret error %x\n", | ||
| 672 | str_id, msg->header.part.data); | ||
| 673 | } | ||
| 674 | if (str_info->ctrl_blk.on == true) { | ||
| 675 | str_info->ctrl_blk.on = false; | ||
| 676 | str_info->ctrl_blk.condition = true; | ||
| 677 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 678 | } | ||
| 679 | break; | ||
| 680 | case IPC_IA_ALLOC_STREAM: { | ||
| 681 | /* map to stream, call play */ | ||
| 682 | struct snd_sst_alloc_response *resp = | ||
| 683 | (struct snd_sst_alloc_response *)msg->mailbox; | ||
| 684 | if (resp->str_type.result) | ||
| 685 | pr_err("error alloc stream = %x\n", | ||
| 686 | resp->str_type.result); | ||
| 687 | sst_alloc_stream_response(str_id, resp); | ||
| 688 | break; | ||
| 689 | } | ||
| 690 | |||
| 691 | case IPC_IA_PLAY_FRAMES: | ||
| 692 | case IPC_IA_CAPT_FRAMES: | ||
| 693 | if (sst_validate_strid(str_id)) { | ||
| 694 | pr_err("stream id %d invalid\n", str_id); | ||
| 695 | break; | ||
| 696 | } | ||
| 697 | pr_debug("Ack for play/capt frames received\n"); | ||
| 698 | break; | ||
| 699 | |||
| 700 | case IPC_IA_PREP_LIB_DNLD: { | ||
| 701 | struct snd_sst_str_type *str_type = | ||
| 702 | (struct snd_sst_str_type *)msg->mailbox; | ||
| 703 | pr_debug("Prep Lib download %x\n", | ||
| 704 | msg->header.part.msg_id); | ||
| 705 | if (str_type->result) | ||
| 706 | pr_err("Prep lib download %x\n", str_type->result); | ||
| 707 | else | ||
| 708 | pr_debug("Can download codec now...\n"); | ||
| 709 | sst_wake_up_alloc_block(sst_drv_ctx, str_id, | ||
| 710 | str_type->result, NULL); | ||
| 711 | break; | ||
| 712 | } | ||
| 713 | |||
| 714 | case IPC_IA_LIB_DNLD_CMPLT: { | ||
| 715 | struct snd_sst_lib_download_info *resp = | ||
| 716 | (struct snd_sst_lib_download_info *)msg->mailbox; | ||
| 717 | int retval = resp->result; | ||
| 718 | |||
| 719 | pr_debug("Lib downloaded %x\n", msg->header.part.msg_id); | ||
| 720 | if (resp->result) { | ||
| 721 | pr_err("err in lib dload %x\n", resp->result); | ||
| 722 | } else { | ||
| 723 | pr_debug("Codec download complete...\n"); | ||
| 724 | pr_debug("codec Type %d Ver %d Built %s: %s\n", | ||
| 725 | resp->dload_lib.lib_info.lib_type, | ||
| 726 | resp->dload_lib.lib_info.lib_version, | ||
| 727 | resp->dload_lib.lib_info.b_date, | ||
| 728 | resp->dload_lib.lib_info.b_time); | ||
| 729 | } | ||
| 730 | sst_wake_up_alloc_block(sst_drv_ctx, str_id, | ||
| 731 | retval, NULL); | ||
| 732 | break; | ||
| 733 | } | ||
| 734 | |||
| 735 | case IPC_IA_GET_FW_VERSION: { | ||
| 736 | struct ipc_header_fw_init *version = | ||
| 737 | (struct ipc_header_fw_init *)msg->mailbox; | ||
| 738 | int major = version->fw_version.major; | ||
| 739 | int minor = version->fw_version.minor; | ||
| 740 | int build = version->fw_version.build; | ||
| 741 | dev_info(&sst_drv_ctx->pci->dev, | ||
| 742 | "INFO: ***LOADED SST FW VERSION*** = %02d.%02d.%02d\n", | ||
| 743 | major, minor, build); | ||
| 744 | break; | ||
| 745 | } | ||
| 746 | case IPC_IA_GET_FW_BUILD_INF: { | ||
| 747 | struct sst_fw_build_info *build = | ||
| 748 | (struct sst_fw_build_info *)msg->mailbox; | ||
| 749 | pr_debug("Build date:%sTime:%s", build->date, build->time); | ||
| 750 | break; | ||
| 751 | } | ||
| 752 | case IPC_IA_SET_PMIC_TYPE: | ||
| 753 | break; | ||
| 754 | case IPC_IA_START_STREAM: | ||
| 755 | pr_debug("reply for START STREAM %x\n", msg->header.full); | ||
| 756 | break; | ||
| 757 | |||
| 758 | case IPC_IA_GET_FW_CTXT: | ||
| 759 | pr_debug("reply for get fw ctxt %x\n", msg->header.full); | ||
| 760 | if (msg->header.part.data) | ||
| 761 | sst_drv_ctx->fw_cntx_size = 0; | ||
| 762 | else | ||
| 763 | sst_drv_ctx->fw_cntx_size = *sst_drv_ctx->fw_cntx; | ||
| 764 | pr_debug("fw copied data %x\n", sst_drv_ctx->fw_cntx_size); | ||
| 765 | sst_wake_up_alloc_block( | ||
| 766 | sst_drv_ctx, str_id, msg->header.part.data, NULL); | ||
| 767 | break; | ||
| 768 | default: | ||
| 769 | /* Illegal case */ | ||
| 770 | pr_err("process reply:default = %x\n", msg->header.full); | ||
| 771 | } | ||
| 772 | sst_clear_interrupt(); | ||
| 773 | return; | ||
| 774 | } | ||
diff --git a/drivers/staging/intel_sst/intel_sst_pvt.c b/drivers/staging/intel_sst/intel_sst_pvt.c new file mode 100644 index 00000000000..e034bea56f1 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_pvt.c | |||
| @@ -0,0 +1,313 @@ | |||
| 1 | /* | ||
| 2 | * intel_sst_pvt.c - Intel SST Driver for audio engine | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * | ||
| 26 | * This driver exposes the audio engine functionalities to the ALSA | ||
| 27 | * and middleware. | ||
| 28 | * | ||
| 29 | * This file contains all private functions | ||
| 30 | */ | ||
| 31 | |||
| 32 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 33 | |||
| 34 | #include <linux/pci.h> | ||
| 35 | #include <linux/fs.h> | ||
| 36 | #include <linux/firmware.h> | ||
| 37 | #include <linux/sched.h> | ||
| 38 | #include "intel_sst.h" | ||
| 39 | #include "intel_sst_ioctl.h" | ||
| 40 | #include "intel_sst_fw_ipc.h" | ||
| 41 | #include "intel_sst_common.h" | ||
| 42 | |||
| 43 | /* | ||
| 44 | * sst_get_block_stream - get a new block stream | ||
| 45 | * | ||
| 46 | * @sst_drv_ctx: Driver context structure | ||
| 47 | * | ||
| 48 | * This function assigns a block for the calls that dont have stream context yet | ||
| 49 | * the blocks are used for waiting on Firmware's response for any operation | ||
| 50 | * Should be called with stream lock held | ||
| 51 | */ | ||
| 52 | int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx) | ||
| 53 | { | ||
| 54 | int i; | ||
| 55 | |||
| 56 | for (i = 0; i < MAX_ACTIVE_STREAM; i++) { | ||
| 57 | if (sst_drv_ctx->alloc_block[i].sst_id == BLOCK_UNINIT) { | ||
| 58 | sst_drv_ctx->alloc_block[i].ops_block.condition = false; | ||
| 59 | sst_drv_ctx->alloc_block[i].ops_block.ret_code = 0; | ||
| 60 | sst_drv_ctx->alloc_block[i].sst_id = 0; | ||
| 61 | break; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | if (i == MAX_ACTIVE_STREAM) { | ||
| 65 | pr_err("max alloc_stream reached\n"); | ||
| 66 | i = -EBUSY; /* active stream limit reached */ | ||
| 67 | } | ||
| 68 | return i; | ||
| 69 | } | ||
| 70 | |||
| 71 | /* | ||
| 72 | * sst_wait_interruptible - wait on event | ||
| 73 | * | ||
| 74 | * @sst_drv_ctx: Driver context | ||
| 75 | * @block: Driver block to wait on | ||
| 76 | * | ||
| 77 | * This function waits without a timeout (and is interruptable) for a | ||
| 78 | * given block event | ||
| 79 | */ | ||
| 80 | int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, | ||
| 81 | struct sst_block *block) | ||
| 82 | { | ||
| 83 | int retval = 0; | ||
| 84 | |||
| 85 | if (!wait_event_interruptible(sst_drv_ctx->wait_queue, | ||
| 86 | block->condition)) { | ||
| 87 | /* event wake */ | ||
| 88 | if (block->ret_code < 0) { | ||
| 89 | pr_err("stream failed %d\n", block->ret_code); | ||
| 90 | retval = -EBUSY; | ||
| 91 | } else { | ||
| 92 | pr_debug("event up\n"); | ||
| 93 | retval = 0; | ||
| 94 | } | ||
| 95 | } else { | ||
| 96 | pr_err("signal interrupted\n"); | ||
| 97 | retval = -EINTR; | ||
| 98 | } | ||
| 99 | return retval; | ||
| 100 | |||
| 101 | } | ||
| 102 | |||
| 103 | |||
| 104 | /* | ||
| 105 | * sst_wait_interruptible_timeout - wait on event interruptable | ||
| 106 | * | ||
| 107 | * @sst_drv_ctx: Driver context | ||
| 108 | * @block: Driver block to wait on | ||
| 109 | * @timeout: time for wait on | ||
| 110 | * | ||
| 111 | * This function waits with a timeout value (and is interruptible) on a | ||
| 112 | * given block event | ||
| 113 | */ | ||
| 114 | int sst_wait_interruptible_timeout( | ||
| 115 | struct intel_sst_drv *sst_drv_ctx, | ||
| 116 | struct sst_block *block, int timeout) | ||
| 117 | { | ||
| 118 | int retval = 0; | ||
| 119 | |||
| 120 | pr_debug("sst_wait_interruptible_timeout - waiting....\n"); | ||
| 121 | if (wait_event_interruptible_timeout(sst_drv_ctx->wait_queue, | ||
| 122 | block->condition, | ||
| 123 | msecs_to_jiffies(timeout))) { | ||
| 124 | if (block->ret_code < 0) | ||
| 125 | pr_err("stream failed %d\n", block->ret_code); | ||
| 126 | else | ||
| 127 | pr_debug("event up\n"); | ||
| 128 | retval = block->ret_code; | ||
| 129 | } else { | ||
| 130 | block->on = false; | ||
| 131 | pr_err("timeout occurred...\n"); | ||
| 132 | /*setting firmware state as uninit so that the | ||
| 133 | firmware will get re-downloaded on next request | ||
| 134 | this is because firmare not responding for 5 sec | ||
| 135 | is equalant to some unrecoverable error of FW | ||
| 136 | sst_drv_ctx->sst_state = SST_UN_INIT;*/ | ||
| 137 | retval = -EBUSY; | ||
| 138 | } | ||
| 139 | return retval; | ||
| 140 | |||
| 141 | } | ||
| 142 | |||
| 143 | |||
| 144 | /* | ||
| 145 | * sst_wait_timeout - wait on event for timeout | ||
| 146 | * | ||
| 147 | * @sst_drv_ctx: Driver context | ||
| 148 | * @block: Driver block to wait on | ||
| 149 | * | ||
| 150 | * This function waits with a timeout value (and is not interruptible) on a | ||
| 151 | * given block event | ||
| 152 | */ | ||
| 153 | int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, | ||
| 154 | struct stream_alloc_block *block) | ||
| 155 | { | ||
| 156 | int retval = 0; | ||
| 157 | |||
| 158 | /* NOTE: | ||
| 159 | Observed that FW processes the alloc msg and replies even | ||
| 160 | before the alloc thread has finished execution */ | ||
| 161 | pr_debug("waiting for %x, condition %x\n", | ||
| 162 | block->sst_id, block->ops_block.condition); | ||
| 163 | if (wait_event_interruptible_timeout(sst_drv_ctx->wait_queue, | ||
| 164 | block->ops_block.condition, | ||
| 165 | msecs_to_jiffies(SST_BLOCK_TIMEOUT))) { | ||
| 166 | /* event wake */ | ||
| 167 | pr_debug("Event wake %x\n", block->ops_block.condition); | ||
| 168 | pr_debug("message ret: %d\n", block->ops_block.ret_code); | ||
| 169 | retval = block->ops_block.ret_code; | ||
| 170 | } else { | ||
| 171 | block->ops_block.on = false; | ||
| 172 | pr_err("Wait timed-out %x\n", block->ops_block.condition); | ||
| 173 | /* settign firmware state as uninit so that the | ||
| 174 | firmware will get redownloaded on next request | ||
| 175 | this is because firmare not responding for 5 sec | ||
| 176 | is equalant to some unrecoverable error of FW | ||
| 177 | sst_drv_ctx->sst_state = SST_UN_INIT;*/ | ||
| 178 | retval = -EBUSY; | ||
| 179 | } | ||
| 180 | return retval; | ||
| 181 | |||
| 182 | } | ||
| 183 | |||
| 184 | /* | ||
| 185 | * sst_create_large_msg - create a large IPC message | ||
| 186 | * | ||
| 187 | * @arg: ipc message | ||
| 188 | * | ||
| 189 | * this function allocates structures to send a large message to the firmware | ||
| 190 | */ | ||
| 191 | int sst_create_large_msg(struct ipc_post **arg) | ||
| 192 | { | ||
| 193 | struct ipc_post *msg; | ||
| 194 | |||
| 195 | msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); | ||
| 196 | if (!msg) { | ||
| 197 | pr_err("kzalloc msg failed\n"); | ||
| 198 | return -ENOMEM; | ||
| 199 | } | ||
| 200 | |||
| 201 | msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); | ||
| 202 | if (!msg->mailbox_data) { | ||
| 203 | kfree(msg); | ||
| 204 | pr_err("kzalloc mailbox_data failed"); | ||
| 205 | return -ENOMEM; | ||
| 206 | } | ||
| 207 | *arg = msg; | ||
| 208 | return 0; | ||
| 209 | } | ||
| 210 | |||
| 211 | /* | ||
| 212 | * sst_create_short_msg - create a short IPC message | ||
| 213 | * | ||
| 214 | * @arg: ipc message | ||
| 215 | * | ||
| 216 | * this function allocates structures to send a short message to the firmware | ||
| 217 | */ | ||
| 218 | int sst_create_short_msg(struct ipc_post **arg) | ||
| 219 | { | ||
| 220 | struct ipc_post *msg; | ||
| 221 | |||
| 222 | msg = kzalloc(sizeof(*msg), GFP_ATOMIC); | ||
| 223 | if (!msg) { | ||
| 224 | pr_err("kzalloc msg failed\n"); | ||
| 225 | return -ENOMEM; | ||
| 226 | } | ||
| 227 | msg->mailbox_data = NULL; | ||
| 228 | *arg = msg; | ||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | /* | ||
| 233 | * sst_clean_stream - clean the stream context | ||
| 234 | * | ||
| 235 | * @stream: stream structure | ||
| 236 | * | ||
| 237 | * this function resets the stream contexts | ||
| 238 | * should be called in free | ||
| 239 | */ | ||
| 240 | void sst_clean_stream(struct stream_info *stream) | ||
| 241 | { | ||
| 242 | struct sst_stream_bufs *bufs = NULL, *_bufs; | ||
| 243 | stream->status = STREAM_UN_INIT; | ||
| 244 | stream->prev = STREAM_UN_INIT; | ||
| 245 | mutex_lock(&stream->lock); | ||
| 246 | list_for_each_entry_safe(bufs, _bufs, &stream->bufs, node) { | ||
| 247 | list_del(&bufs->node); | ||
| 248 | kfree(bufs); | ||
| 249 | } | ||
| 250 | mutex_unlock(&stream->lock); | ||
| 251 | |||
| 252 | if (stream->ops != STREAM_OPS_PLAYBACK_DRM) | ||
| 253 | kfree(stream->decode_ibuf); | ||
| 254 | } | ||
| 255 | |||
| 256 | /* | ||
| 257 | * sst_wake_up_alloc_block - wake up waiting block | ||
| 258 | * | ||
| 259 | * @sst_drv_ctx: Driver context | ||
| 260 | * @sst_id: stream id | ||
| 261 | * @status: status of wakeup | ||
| 262 | * @data: data pointer of wakeup | ||
| 263 | * | ||
| 264 | * This function wakes up a sleeping block event based on the response | ||
| 265 | */ | ||
| 266 | void sst_wake_up_alloc_block(struct intel_sst_drv *sst_drv_ctx, | ||
| 267 | u8 sst_id, int status, void *data) | ||
| 268 | { | ||
| 269 | int i; | ||
| 270 | |||
| 271 | /* Unblock with retval code */ | ||
| 272 | for (i = 0; i < MAX_ACTIVE_STREAM; i++) { | ||
| 273 | if (sst_id == sst_drv_ctx->alloc_block[i].sst_id) { | ||
| 274 | sst_drv_ctx->alloc_block[i].ops_block.condition = true; | ||
| 275 | sst_drv_ctx->alloc_block[i].ops_block.ret_code = status; | ||
| 276 | sst_drv_ctx->alloc_block[i].ops_block.data = data; | ||
| 277 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 278 | break; | ||
| 279 | } | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | /* | ||
| 284 | * sst_enable_rx_timeslot - Send msg to query for stream parameters | ||
| 285 | * @status: rx timeslot to be enabled | ||
| 286 | * | ||
| 287 | * This function is called when the RX timeslot is required to be enabled | ||
| 288 | */ | ||
| 289 | int sst_enable_rx_timeslot(int status) | ||
| 290 | { | ||
| 291 | int retval = 0; | ||
| 292 | struct ipc_post *msg = NULL; | ||
| 293 | |||
| 294 | if (sst_create_short_msg(&msg)) { | ||
| 295 | pr_err("mem allocation failed\n"); | ||
| 296 | return -ENOMEM; | ||
| 297 | } | ||
| 298 | pr_debug("ipc message sending: ENABLE_RX_TIME_SLOT\n"); | ||
| 299 | sst_fill_header(&msg->header, IPC_IA_ENABLE_RX_TIME_SLOT, 0, 0); | ||
| 300 | msg->header.part.data = status; | ||
| 301 | sst_drv_ctx->hs_info_blk.condition = false; | ||
| 302 | sst_drv_ctx->hs_info_blk.ret_code = 0; | ||
| 303 | sst_drv_ctx->hs_info_blk.on = true; | ||
| 304 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 305 | list_add_tail(&msg->node, | ||
| 306 | &sst_drv_ctx->ipc_dispatch_list); | ||
| 307 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 308 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 309 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 310 | &sst_drv_ctx->hs_info_blk, SST_BLOCK_TIMEOUT); | ||
| 311 | return retval; | ||
| 312 | } | ||
| 313 | |||
diff --git a/drivers/staging/intel_sst/intel_sst_stream.c b/drivers/staging/intel_sst/intel_sst_stream.c new file mode 100644 index 00000000000..be4565e74f8 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_stream.c | |||
| @@ -0,0 +1,583 @@ | |||
| 1 | /* | ||
| 2 | * intel_sst_stream.c - Intel SST Driver for audio engine | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * | ||
| 26 | * This file contains the stream operations of SST driver | ||
| 27 | */ | ||
| 28 | |||
| 29 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 30 | |||
| 31 | #include <linux/pci.h> | ||
| 32 | #include <linux/firmware.h> | ||
| 33 | #include <linux/sched.h> | ||
| 34 | #include <linux/delay.h> | ||
| 35 | #include "intel_sst_ioctl.h" | ||
| 36 | #include "intel_sst.h" | ||
| 37 | #include "intel_sst_fw_ipc.h" | ||
| 38 | #include "intel_sst_common.h" | ||
| 39 | |||
| 40 | /* | ||
| 41 | * sst_check_device_type - Check the medfield device type | ||
| 42 | * | ||
| 43 | * @device: Device to be checked | ||
| 44 | * @num_ch: Number of channels queried | ||
| 45 | * @pcm_slot: slot to be enabled for this device | ||
| 46 | * | ||
| 47 | * This checks the deivce against the map and calculates pcm_slot value | ||
| 48 | */ | ||
| 49 | int sst_check_device_type(u32 device, u32 num_chan, u32 *pcm_slot) | ||
| 50 | { | ||
| 51 | if (device >= MAX_NUM_STREAMS_MFLD) { | ||
| 52 | pr_debug("device type invalid %d\n", device); | ||
| 53 | return -EINVAL; | ||
| 54 | } | ||
| 55 | if (sst_drv_ctx->streams[device].status == STREAM_UN_INIT) { | ||
| 56 | if (device == SND_SST_DEVICE_VIBRA && num_chan == 1) | ||
| 57 | *pcm_slot = 0x10; | ||
| 58 | else if (device == SND_SST_DEVICE_HAPTIC && num_chan == 1) | ||
| 59 | *pcm_slot = 0x20; | ||
| 60 | else if (device == SND_SST_DEVICE_IHF && num_chan == 1) | ||
| 61 | *pcm_slot = 0x04; | ||
| 62 | else if (device == SND_SST_DEVICE_IHF && num_chan == 2) | ||
| 63 | *pcm_slot = 0x0C; | ||
| 64 | else if (device == SND_SST_DEVICE_HEADSET && num_chan == 1) | ||
| 65 | *pcm_slot = 0x01; | ||
| 66 | else if (device == SND_SST_DEVICE_HEADSET && num_chan == 2) | ||
| 67 | *pcm_slot = 0x03; | ||
| 68 | else if (device == SND_SST_DEVICE_CAPTURE && num_chan == 1) | ||
| 69 | *pcm_slot = 0x01; | ||
| 70 | else if (device == SND_SST_DEVICE_CAPTURE && num_chan == 2) | ||
| 71 | *pcm_slot = 0x03; | ||
| 72 | else if (device == SND_SST_DEVICE_CAPTURE && num_chan == 3) | ||
| 73 | *pcm_slot = 0x07; | ||
| 74 | else if (device == SND_SST_DEVICE_CAPTURE && num_chan == 4) | ||
| 75 | *pcm_slot = 0x0F; | ||
| 76 | else if (device == SND_SST_DEVICE_CAPTURE && num_chan > 4) | ||
| 77 | *pcm_slot = 0x1F; | ||
| 78 | else { | ||
| 79 | pr_debug("No condition satisfied.. ret err\n"); | ||
| 80 | return -EINVAL; | ||
| 81 | } | ||
| 82 | } else { | ||
| 83 | pr_debug("this stream state is not uni-init, is %d\n", | ||
| 84 | sst_drv_ctx->streams[device].status); | ||
| 85 | return -EBADRQC; | ||
| 86 | } | ||
| 87 | pr_debug("returning slot %x\n", *pcm_slot); | ||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | /** | ||
| 91 | * get_mrst_stream_id - gets a new stream id for use | ||
| 92 | * | ||
| 93 | * This functions searches the current streams and allocated an empty stream | ||
| 94 | * lock stream_lock required to be held before calling this | ||
| 95 | */ | ||
| 96 | static unsigned int get_mrst_stream_id(void) | ||
| 97 | { | ||
| 98 | int i; | ||
| 99 | |||
| 100 | for (i = 1; i <= MAX_NUM_STREAMS_MRST; i++) { | ||
| 101 | if (sst_drv_ctx->streams[i].status == STREAM_UN_INIT) | ||
| 102 | return i; | ||
| 103 | } | ||
| 104 | pr_debug("Didn't find empty stream for mrst\n"); | ||
| 105 | return -EBUSY; | ||
| 106 | } | ||
| 107 | |||
| 108 | /** | ||
| 109 | * sst_alloc_stream - Send msg for a new stream ID | ||
| 110 | * | ||
| 111 | * @params: stream params | ||
| 112 | * @stream_ops: operation of stream PB/capture | ||
| 113 | * @codec: codec for stream | ||
| 114 | * @device: device stream to be allocated for | ||
| 115 | * | ||
| 116 | * This function is called by any function which wants to start | ||
| 117 | * a new stream. This also check if a stream exists which is idle | ||
| 118 | * it initializes idle stream id to this request | ||
| 119 | */ | ||
| 120 | int sst_alloc_stream(char *params, unsigned int stream_ops, | ||
| 121 | u8 codec, unsigned int device) | ||
| 122 | { | ||
| 123 | struct ipc_post *msg = NULL; | ||
| 124 | struct snd_sst_alloc_params alloc_param; | ||
| 125 | unsigned int pcm_slot = 0, num_ch; | ||
| 126 | int str_id; | ||
| 127 | struct snd_sst_stream_params *sparams; | ||
| 128 | struct stream_info *str_info; | ||
| 129 | |||
| 130 | pr_debug("SST DBG:entering sst_alloc_stream\n"); | ||
| 131 | pr_debug("SST DBG:%d %d %d\n", stream_ops, codec, device); | ||
| 132 | |||
| 133 | BUG_ON(!params); | ||
| 134 | sparams = (struct snd_sst_stream_params *)params; | ||
| 135 | num_ch = sparams->uc.pcm_params.num_chan; | ||
| 136 | /*check the device type*/ | ||
| 137 | if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) { | ||
| 138 | if (sst_check_device_type(device, num_ch, &pcm_slot)) | ||
| 139 | return -EINVAL; | ||
| 140 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 141 | str_id = device; | ||
| 142 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 143 | pr_debug("SST_DBG: slot %x\n", pcm_slot); | ||
| 144 | } else { | ||
| 145 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 146 | str_id = get_mrst_stream_id(); | ||
| 147 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 148 | if (str_id <= 0) | ||
| 149 | return -EBUSY; | ||
| 150 | } | ||
| 151 | /*allocate device type context*/ | ||
| 152 | sst_init_stream(&sst_drv_ctx->streams[str_id], codec, | ||
| 153 | str_id, stream_ops, pcm_slot, device); | ||
| 154 | /* send msg to FW to allocate a stream */ | ||
| 155 | if (sst_create_large_msg(&msg)) | ||
| 156 | return -ENOMEM; | ||
| 157 | |||
| 158 | sst_fill_header(&msg->header, IPC_IA_ALLOC_STREAM, 1, str_id); | ||
| 159 | msg->header.part.data = sizeof(alloc_param) + sizeof(u32); | ||
| 160 | alloc_param.str_type.codec_type = codec; | ||
| 161 | alloc_param.str_type.str_type = SST_STREAM_TYPE_MUSIC; | ||
| 162 | alloc_param.str_type.operation = stream_ops; | ||
| 163 | alloc_param.str_type.protected_str = 0; /* non drm */ | ||
| 164 | alloc_param.str_type.time_slots = pcm_slot; | ||
| 165 | alloc_param.str_type.result = alloc_param.str_type.reserved = 0; | ||
| 166 | memcpy(&alloc_param.stream_params, params, | ||
| 167 | sizeof(struct snd_sst_stream_params)); | ||
| 168 | |||
| 169 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 170 | memcpy(msg->mailbox_data + sizeof(u32), &alloc_param, | ||
| 171 | sizeof(alloc_param)); | ||
| 172 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 173 | str_info->ctrl_blk.condition = false; | ||
| 174 | str_info->ctrl_blk.ret_code = 0; | ||
| 175 | str_info->ctrl_blk.on = true; | ||
| 176 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 177 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 178 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 179 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 180 | pr_debug("SST DBG:alloc stream done\n"); | ||
| 181 | return str_id; | ||
| 182 | } | ||
| 183 | |||
| 184 | |||
| 185 | /* | ||
| 186 | * sst_alloc_stream_response - process alloc reply | ||
| 187 | * | ||
| 188 | * @str_id: stream id for which the stream has been allocated | ||
| 189 | * @resp the stream response from firware | ||
| 190 | * | ||
| 191 | * This function is called by firmware as a response to stream allcoation | ||
| 192 | * request | ||
| 193 | */ | ||
| 194 | int sst_alloc_stream_response(unsigned int str_id, | ||
| 195 | struct snd_sst_alloc_response *resp) | ||
| 196 | { | ||
| 197 | int retval = 0; | ||
| 198 | struct stream_info *str_info; | ||
| 199 | struct snd_sst_lib_download *lib_dnld; | ||
| 200 | |||
| 201 | pr_debug("SST DEBUG: stream number given = %d\n", str_id); | ||
| 202 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 203 | if (resp->str_type.result == SST_LIB_ERR_LIB_DNLD_REQUIRED) { | ||
| 204 | lib_dnld = kzalloc(sizeof(*lib_dnld), GFP_KERNEL); | ||
| 205 | memcpy(lib_dnld, &resp->lib_dnld, sizeof(*lib_dnld)); | ||
| 206 | } else | ||
| 207 | lib_dnld = NULL; | ||
| 208 | if (str_info->ctrl_blk.on == true) { | ||
| 209 | str_info->ctrl_blk.on = false; | ||
| 210 | str_info->ctrl_blk.data = lib_dnld; | ||
| 211 | str_info->ctrl_blk.condition = true; | ||
| 212 | str_info->ctrl_blk.ret_code = resp->str_type.result; | ||
| 213 | pr_debug("SST DEBUG: sst_alloc_stream_response: waking up.\n"); | ||
| 214 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 215 | } | ||
| 216 | return retval; | ||
| 217 | } | ||
| 218 | |||
| 219 | |||
| 220 | /** | ||
| 221 | * sst_get_fw_info - Send msg to query for firmware configurations | ||
| 222 | * @info: out param that holds the firmare configurations | ||
| 223 | * | ||
| 224 | * This function is called when the firmware configurations are queiried for | ||
| 225 | */ | ||
| 226 | int sst_get_fw_info(struct snd_sst_fw_info *info) | ||
| 227 | { | ||
| 228 | int retval = 0; | ||
| 229 | struct ipc_post *msg = NULL; | ||
| 230 | |||
| 231 | pr_debug("SST DBG:sst_get_fw_info called\n"); | ||
| 232 | |||
| 233 | if (sst_create_short_msg(&msg)) { | ||
| 234 | pr_err("SST ERR: message creation failed\n"); | ||
| 235 | return -ENOMEM; | ||
| 236 | } | ||
| 237 | |||
| 238 | sst_fill_header(&msg->header, IPC_IA_GET_FW_INFO, 0, 0); | ||
| 239 | sst_drv_ctx->fw_info_blk.condition = false; | ||
| 240 | sst_drv_ctx->fw_info_blk.ret_code = 0; | ||
| 241 | sst_drv_ctx->fw_info_blk.on = true; | ||
| 242 | sst_drv_ctx->fw_info_blk.data = info; | ||
| 243 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 244 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 245 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 246 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 247 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 248 | &sst_drv_ctx->fw_info_blk, SST_BLOCK_TIMEOUT); | ||
| 249 | if (retval) { | ||
| 250 | pr_err("SST ERR: error in fw_info = %d\n", retval); | ||
| 251 | retval = -EIO; | ||
| 252 | } | ||
| 253 | return retval; | ||
| 254 | } | ||
| 255 | |||
| 256 | |||
| 257 | /** | ||
| 258 | * sst_pause_stream - Send msg for a pausing stream | ||
| 259 | * @str_id: stream ID | ||
| 260 | * | ||
| 261 | * This function is called by any function which wants to pause | ||
| 262 | * an already running stream. | ||
| 263 | */ | ||
| 264 | int sst_start_stream(int str_id) | ||
| 265 | { | ||
| 266 | int retval = 0; | ||
| 267 | struct ipc_post *msg = NULL; | ||
| 268 | struct stream_info *str_info; | ||
| 269 | |||
| 270 | pr_debug("sst_start_stream for %d\n", str_id); | ||
| 271 | retval = sst_validate_strid(str_id); | ||
| 272 | if (retval) | ||
| 273 | return retval; | ||
| 274 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 275 | if (str_info->status != STREAM_INIT) | ||
| 276 | return -EBADRQC; | ||
| 277 | if (sst_create_short_msg(&msg)) | ||
| 278 | return -ENOMEM; | ||
| 279 | |||
| 280 | sst_fill_header(&msg->header, IPC_IA_START_STREAM, 0, str_id); | ||
| 281 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 282 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 283 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 284 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 285 | return retval; | ||
| 286 | } | ||
| 287 | |||
| 288 | /* | ||
| 289 | * sst_pause_stream - Send msg for a pausing stream | ||
| 290 | * @str_id: stream ID | ||
| 291 | * | ||
| 292 | * This function is called by any function which wants to pause | ||
| 293 | * an already running stream. | ||
| 294 | */ | ||
| 295 | int sst_pause_stream(int str_id) | ||
| 296 | { | ||
| 297 | int retval = 0; | ||
| 298 | struct ipc_post *msg = NULL; | ||
| 299 | struct stream_info *str_info; | ||
| 300 | |||
| 301 | pr_debug("SST DBG:sst_pause_stream for %d\n", str_id); | ||
| 302 | retval = sst_validate_strid(str_id); | ||
| 303 | if (retval) | ||
| 304 | return retval; | ||
| 305 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 306 | if (str_info->status == STREAM_PAUSED) | ||
| 307 | return 0; | ||
| 308 | if (str_info->status == STREAM_RUNNING || | ||
| 309 | str_info->status == STREAM_INIT) { | ||
| 310 | if (str_info->prev == STREAM_UN_INIT) | ||
| 311 | return -EBADRQC; | ||
| 312 | if (str_info->ctrl_blk.on == true) { | ||
| 313 | pr_err("SST ERR: control path is in use\n"); | ||
| 314 | return -EINVAL; | ||
| 315 | } | ||
| 316 | if (sst_create_short_msg(&msg)) | ||
| 317 | return -ENOMEM; | ||
| 318 | |||
| 319 | sst_fill_header(&msg->header, IPC_IA_PAUSE_STREAM, 0, str_id); | ||
| 320 | str_info->ctrl_blk.condition = false; | ||
| 321 | str_info->ctrl_blk.ret_code = 0; | ||
| 322 | str_info->ctrl_blk.on = true; | ||
| 323 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 324 | list_add_tail(&msg->node, | ||
| 325 | &sst_drv_ctx->ipc_dispatch_list); | ||
| 326 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 327 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 328 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 329 | &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); | ||
| 330 | if (retval == 0) { | ||
| 331 | str_info->prev = str_info->status; | ||
| 332 | str_info->status = STREAM_PAUSED; | ||
| 333 | } else if (retval == SST_ERR_INVALID_STREAM_ID) { | ||
| 334 | retval = -EINVAL; | ||
| 335 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 336 | sst_clean_stream(str_info); | ||
| 337 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 338 | } | ||
| 339 | } else { | ||
| 340 | retval = -EBADRQC; | ||
| 341 | pr_err("SST ERR: BADQRC for stream\n"); | ||
| 342 | } | ||
| 343 | |||
| 344 | return retval; | ||
| 345 | } | ||
| 346 | |||
| 347 | /** | ||
| 348 | * sst_resume_stream - Send msg for resuming stream | ||
| 349 | * @str_id: stream ID | ||
| 350 | * | ||
| 351 | * This function is called by any function which wants to resume | ||
| 352 | * an already paused stream. | ||
| 353 | */ | ||
| 354 | int sst_resume_stream(int str_id) | ||
| 355 | { | ||
| 356 | int retval = 0; | ||
| 357 | struct ipc_post *msg = NULL; | ||
| 358 | struct stream_info *str_info; | ||
| 359 | |||
| 360 | pr_debug("SST DBG:sst_resume_stream for %d\n", str_id); | ||
| 361 | retval = sst_validate_strid(str_id); | ||
| 362 | if (retval) | ||
| 363 | return retval; | ||
| 364 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 365 | if (str_info->status == STREAM_RUNNING) | ||
| 366 | return 0; | ||
| 367 | if (str_info->status == STREAM_PAUSED) { | ||
| 368 | if (str_info->ctrl_blk.on == true) { | ||
| 369 | pr_err("SST ERR: control path in use\n"); | ||
| 370 | return -EINVAL; | ||
| 371 | } | ||
| 372 | if (sst_create_short_msg(&msg)) { | ||
| 373 | pr_err("SST ERR: mem allocation failed\n"); | ||
| 374 | return -ENOMEM; | ||
| 375 | } | ||
| 376 | sst_fill_header(&msg->header, IPC_IA_RESUME_STREAM, 0, str_id); | ||
| 377 | str_info->ctrl_blk.condition = false; | ||
| 378 | str_info->ctrl_blk.ret_code = 0; | ||
| 379 | str_info->ctrl_blk.on = true; | ||
| 380 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 381 | list_add_tail(&msg->node, | ||
| 382 | &sst_drv_ctx->ipc_dispatch_list); | ||
| 383 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 384 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 385 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 386 | &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); | ||
| 387 | if (!retval) { | ||
| 388 | if (str_info->prev == STREAM_RUNNING) | ||
| 389 | str_info->status = STREAM_RUNNING; | ||
| 390 | else | ||
| 391 | str_info->status = STREAM_INIT; | ||
| 392 | str_info->prev = STREAM_PAUSED; | ||
| 393 | } else if (retval == -SST_ERR_INVALID_STREAM_ID) { | ||
| 394 | retval = -EINVAL; | ||
| 395 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 396 | sst_clean_stream(str_info); | ||
| 397 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 398 | } | ||
| 399 | } else { | ||
| 400 | retval = -EBADRQC; | ||
| 401 | pr_err("SST ERR: BADQRC for stream\n"); | ||
| 402 | } | ||
| 403 | |||
| 404 | return retval; | ||
| 405 | } | ||
| 406 | |||
| 407 | |||
| 408 | /** | ||
| 409 | * sst_drop_stream - Send msg for stopping stream | ||
| 410 | * @str_id: stream ID | ||
| 411 | * | ||
| 412 | * This function is called by any function which wants to stop | ||
| 413 | * a stream. | ||
| 414 | */ | ||
| 415 | int sst_drop_stream(int str_id) | ||
| 416 | { | ||
| 417 | int retval = 0; | ||
| 418 | struct ipc_post *msg = NULL; | ||
| 419 | struct sst_stream_bufs *bufs = NULL, *_bufs; | ||
| 420 | struct stream_info *str_info; | ||
| 421 | |||
| 422 | pr_debug("SST DBG:sst_drop_stream for %d\n", str_id); | ||
| 423 | retval = sst_validate_strid(str_id); | ||
| 424 | if (retval) | ||
| 425 | return retval; | ||
| 426 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 427 | |||
| 428 | if (str_info->status != STREAM_UN_INIT && | ||
| 429 | str_info->status != STREAM_DECODE) { | ||
| 430 | if (str_info->ctrl_blk.on == true) { | ||
| 431 | pr_err("SST ERR: control path in use\n"); | ||
| 432 | return -EINVAL; | ||
| 433 | } | ||
| 434 | if (sst_create_short_msg(&msg)) { | ||
| 435 | pr_err("SST ERR: mem allocation failed\n"); | ||
| 436 | return -ENOMEM; | ||
| 437 | } | ||
| 438 | sst_fill_header(&msg->header, IPC_IA_DROP_STREAM, 0, str_id); | ||
| 439 | str_info->ctrl_blk.condition = false; | ||
| 440 | str_info->ctrl_blk.ret_code = 0; | ||
| 441 | str_info->ctrl_blk.on = true; | ||
| 442 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 443 | list_add_tail(&msg->node, | ||
| 444 | &sst_drv_ctx->ipc_dispatch_list); | ||
| 445 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 446 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 447 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 448 | &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); | ||
| 449 | if (!retval) { | ||
| 450 | pr_debug("SST DBG:drop success\n"); | ||
| 451 | str_info->prev = STREAM_UN_INIT; | ||
| 452 | str_info->status = STREAM_INIT; | ||
| 453 | if (str_info->src != MAD_DRV) { | ||
| 454 | mutex_lock(&str_info->lock); | ||
| 455 | list_for_each_entry_safe(bufs, _bufs, | ||
| 456 | &str_info->bufs, node) { | ||
| 457 | list_del(&bufs->node); | ||
| 458 | kfree(bufs); | ||
| 459 | } | ||
| 460 | mutex_unlock(&str_info->lock); | ||
| 461 | } | ||
| 462 | str_info->cumm_bytes += str_info->curr_bytes; | ||
| 463 | } else if (retval == -SST_ERR_INVALID_STREAM_ID) { | ||
| 464 | retval = -EINVAL; | ||
| 465 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 466 | sst_clean_stream(str_info); | ||
| 467 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 468 | } | ||
| 469 | if (str_info->data_blk.on == true) { | ||
| 470 | str_info->data_blk.condition = true; | ||
| 471 | str_info->data_blk.ret_code = retval; | ||
| 472 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 473 | } | ||
| 474 | } else { | ||
| 475 | retval = -EBADRQC; | ||
| 476 | pr_err("SST ERR: BADQRC for stream\n"); | ||
| 477 | } | ||
| 478 | return retval; | ||
| 479 | } | ||
| 480 | |||
| 481 | /** | ||
| 482 | * sst_drain_stream - Send msg for draining stream | ||
| 483 | * @str_id: stream ID | ||
| 484 | * | ||
| 485 | * This function is called by any function which wants to drain | ||
| 486 | * a stream. | ||
| 487 | */ | ||
| 488 | int sst_drain_stream(int str_id) | ||
| 489 | { | ||
| 490 | int retval = 0; | ||
| 491 | struct ipc_post *msg = NULL; | ||
| 492 | struct stream_info *str_info; | ||
| 493 | |||
| 494 | pr_debug("SST DBG:sst_drain_stream for %d\n", str_id); | ||
| 495 | retval = sst_validate_strid(str_id); | ||
| 496 | if (retval) | ||
| 497 | return retval; | ||
| 498 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 499 | |||
| 500 | if (str_info->status != STREAM_RUNNING && | ||
| 501 | str_info->status != STREAM_INIT && | ||
| 502 | str_info->status != STREAM_PAUSED) { | ||
| 503 | pr_err("SST ERR: BADQRC for stream = %d\n", | ||
| 504 | str_info->status); | ||
| 505 | return -EBADRQC; | ||
| 506 | } | ||
| 507 | |||
| 508 | if (str_info->status == STREAM_INIT) { | ||
| 509 | if (sst_create_short_msg(&msg)) { | ||
| 510 | pr_err("SST ERR: mem allocation failed\n"); | ||
| 511 | return -ENOMEM; | ||
| 512 | } | ||
| 513 | sst_fill_header(&msg->header, IPC_IA_DRAIN_STREAM, 0, str_id); | ||
| 514 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 515 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 516 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 517 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 518 | } else | ||
| 519 | str_info->need_draining = true; | ||
| 520 | str_info->data_blk.condition = false; | ||
| 521 | str_info->data_blk.ret_code = 0; | ||
| 522 | str_info->data_blk.on = true; | ||
| 523 | retval = sst_wait_interruptible(sst_drv_ctx, &str_info->data_blk); | ||
| 524 | str_info->need_draining = false; | ||
| 525 | return retval; | ||
| 526 | } | ||
| 527 | |||
| 528 | /** | ||
| 529 | * sst_free_stream - Frees a stream | ||
| 530 | * @str_id: stream ID | ||
| 531 | * | ||
| 532 | * This function is called by any function which wants to free | ||
| 533 | * a stream. | ||
| 534 | */ | ||
| 535 | int sst_free_stream(int str_id) | ||
| 536 | { | ||
| 537 | int retval = 0; | ||
| 538 | struct ipc_post *msg = NULL; | ||
| 539 | struct stream_info *str_info; | ||
| 540 | |||
| 541 | pr_debug("SST DBG:sst_free_stream for %d\n", str_id); | ||
| 542 | |||
| 543 | retval = sst_validate_strid(str_id); | ||
| 544 | if (retval) | ||
| 545 | return retval; | ||
| 546 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 547 | |||
| 548 | if (str_info->status != STREAM_UN_INIT) { | ||
| 549 | if (sst_create_short_msg(&msg)) { | ||
| 550 | pr_err("SST ERR: mem allocation failed\n"); | ||
| 551 | return -ENOMEM; | ||
| 552 | } | ||
| 553 | sst_fill_header(&msg->header, IPC_IA_FREE_STREAM, 0, str_id); | ||
| 554 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 555 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 556 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 557 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 558 | str_info->prev = str_info->status; | ||
| 559 | str_info->status = STREAM_UN_INIT; | ||
| 560 | if (str_info->data_blk.on == true) { | ||
| 561 | str_info->data_blk.condition = true; | ||
| 562 | str_info->data_blk.ret_code = 0; | ||
| 563 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 564 | } | ||
| 565 | str_info->data_blk.on = true; | ||
| 566 | str_info->data_blk.condition = false; | ||
| 567 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 568 | &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); | ||
| 569 | pr_debug("wait for free returned %d\n", retval); | ||
| 570 | msleep(100); | ||
| 571 | mutex_lock(&sst_drv_ctx->stream_lock); | ||
| 572 | sst_clean_stream(str_info); | ||
| 573 | mutex_unlock(&sst_drv_ctx->stream_lock); | ||
| 574 | pr_debug("SST DBG:Stream freed\n"); | ||
| 575 | } else { | ||
| 576 | retval = -EBADRQC; | ||
| 577 | pr_debug("SST DBG:BADQRC for stream\n"); | ||
| 578 | } | ||
| 579 | |||
| 580 | return retval; | ||
| 581 | } | ||
| 582 | |||
| 583 | |||
diff --git a/drivers/staging/intel_sst/intel_sst_stream_encoded.c b/drivers/staging/intel_sst/intel_sst_stream_encoded.c new file mode 100644 index 00000000000..2be58c5cba0 --- /dev/null +++ b/drivers/staging/intel_sst/intel_sst_stream_encoded.c | |||
| @@ -0,0 +1,1273 @@ | |||
| 1 | /* | ||
| 2 | * intel_sst_stream.c - Intel SST Driver for audio engine | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * | ||
| 26 | * This file contains the stream operations of SST driver | ||
| 27 | */ | ||
| 28 | |||
| 29 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 30 | |||
| 31 | #include <linux/pci.h> | ||
| 32 | #include <linux/syscalls.h> | ||
| 33 | #include <linux/firmware.h> | ||
| 34 | #include <linux/sched.h> | ||
| 35 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 36 | #include <linux/rar_register.h> | ||
| 37 | #include "../memrar/memrar.h" | ||
| 38 | #endif | ||
| 39 | #include "intel_sst_ioctl.h" | ||
| 40 | #include "intel_sst.h" | ||
| 41 | #include "intel_sst_fw_ipc.h" | ||
| 42 | #include "intel_sst_common.h" | ||
| 43 | /** | ||
| 44 | * sst_get_stream_params - Send msg to query for stream parameters | ||
| 45 | * @str_id: stream id for which the parameters are queried for | ||
| 46 | * @get_params: out parameters to which the parameters are copied to | ||
| 47 | * | ||
| 48 | * This function is called when the stream parameters are queiried for | ||
| 49 | */ | ||
| 50 | int sst_get_stream_params(int str_id, | ||
| 51 | struct snd_sst_get_stream_params *get_params) | ||
| 52 | { | ||
| 53 | int retval = 0; | ||
| 54 | struct ipc_post *msg = NULL; | ||
| 55 | struct stream_info *str_info; | ||
| 56 | struct snd_sst_fw_get_stream_params *fw_params; | ||
| 57 | |||
| 58 | pr_debug("get_stream for %d\n", str_id); | ||
| 59 | retval = sst_validate_strid(str_id); | ||
| 60 | if (retval) | ||
| 61 | return retval; | ||
| 62 | |||
| 63 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 64 | if (str_info->status != STREAM_UN_INIT) { | ||
| 65 | if (str_info->ctrl_blk.on == true) { | ||
| 66 | pr_err("control path in use\n"); | ||
| 67 | return -EINVAL; | ||
| 68 | } | ||
| 69 | if (sst_create_short_msg(&msg)) { | ||
| 70 | pr_err("message creation failed\n"); | ||
| 71 | return -ENOMEM; | ||
| 72 | } | ||
| 73 | fw_params = kzalloc(sizeof(*fw_params), GFP_ATOMIC); | ||
| 74 | if (!fw_params) { | ||
| 75 | pr_err("mem allocation failed\n"); | ||
| 76 | kfree(msg); | ||
| 77 | return -ENOMEM; | ||
| 78 | } | ||
| 79 | |||
| 80 | sst_fill_header(&msg->header, IPC_IA_GET_STREAM_PARAMS, | ||
| 81 | 0, str_id); | ||
| 82 | str_info->ctrl_blk.condition = false; | ||
| 83 | str_info->ctrl_blk.ret_code = 0; | ||
| 84 | str_info->ctrl_blk.on = true; | ||
| 85 | str_info->ctrl_blk.data = (void *) fw_params; | ||
| 86 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 87 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 88 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 89 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 90 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 91 | &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); | ||
| 92 | if (retval) { | ||
| 93 | get_params->codec_params.result = retval; | ||
| 94 | kfree(fw_params); | ||
| 95 | return -EIO; | ||
| 96 | } | ||
| 97 | memcpy(&get_params->pcm_params, &fw_params->pcm_params, | ||
| 98 | sizeof(fw_params->pcm_params)); | ||
| 99 | memcpy(&get_params->codec_params.sparams, | ||
| 100 | &fw_params->codec_params, | ||
| 101 | sizeof(fw_params->codec_params)); | ||
| 102 | get_params->codec_params.result = 0; | ||
| 103 | get_params->codec_params.stream_id = str_id; | ||
| 104 | get_params->codec_params.codec = str_info->codec; | ||
| 105 | get_params->codec_params.ops = str_info->ops; | ||
| 106 | get_params->codec_params.stream_type = str_info->str_type; | ||
| 107 | kfree(fw_params); | ||
| 108 | } else { | ||
| 109 | pr_debug("Stream is not in the init state\n"); | ||
| 110 | } | ||
| 111 | return retval; | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * sst_set_stream_param - Send msg for setting stream parameters | ||
| 116 | * | ||
| 117 | * @str_id: stream id | ||
| 118 | * @str_param: stream params | ||
| 119 | * | ||
| 120 | * This function sets stream params during runtime | ||
| 121 | */ | ||
| 122 | int sst_set_stream_param(int str_id, struct snd_sst_params *str_param) | ||
| 123 | { | ||
| 124 | int retval = 0; | ||
| 125 | struct ipc_post *msg = NULL; | ||
| 126 | struct stream_info *str_info; | ||
| 127 | |||
| 128 | BUG_ON(!str_param); | ||
| 129 | if (sst_drv_ctx->streams[str_id].ops != str_param->ops) { | ||
| 130 | pr_err("Invalid operation\n"); | ||
| 131 | return -EINVAL; | ||
| 132 | } | ||
| 133 | retval = sst_validate_strid(str_id); | ||
| 134 | if (retval) | ||
| 135 | return retval; | ||
| 136 | pr_debug("set_stream for %d\n", str_id); | ||
| 137 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 138 | if (sst_drv_ctx->streams[str_id].status == STREAM_INIT) { | ||
| 139 | if (str_info->ctrl_blk.on == true) { | ||
| 140 | pr_err("control path in use\n"); | ||
| 141 | return -EAGAIN; | ||
| 142 | } | ||
| 143 | if (sst_create_large_msg(&msg)) | ||
| 144 | return -ENOMEM; | ||
| 145 | |||
| 146 | sst_fill_header(&msg->header, | ||
| 147 | IPC_IA_SET_STREAM_PARAMS, 1, str_id); | ||
| 148 | str_info->ctrl_blk.condition = false; | ||
| 149 | str_info->ctrl_blk.ret_code = 0; | ||
| 150 | str_info->ctrl_blk.on = true; | ||
| 151 | msg->header.part.data = sizeof(u32) + | ||
| 152 | sizeof(str_param->sparams); | ||
| 153 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 154 | memcpy(msg->mailbox_data + sizeof(u32), &str_param->sparams, | ||
| 155 | sizeof(str_param->sparams)); | ||
| 156 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 157 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 158 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 159 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 160 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 161 | &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); | ||
| 162 | if (retval < 0) { | ||
| 163 | retval = -EIO; | ||
| 164 | sst_clean_stream(str_info); | ||
| 165 | } | ||
| 166 | } else { | ||
| 167 | retval = -EBADRQC; | ||
| 168 | pr_err("BADQRC for stream\n"); | ||
| 169 | } | ||
| 170 | return retval; | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * sst_get_vol - This function allows to get the premix gain or gain of a stream | ||
| 175 | * | ||
| 176 | * @get_vol: this is an output param through which the volume | ||
| 177 | * structure is passed back to user | ||
| 178 | * | ||
| 179 | * This function is called when the premix gain or stream gain is queried for | ||
| 180 | */ | ||
| 181 | int sst_get_vol(struct snd_sst_vol *get_vol) | ||
| 182 | { | ||
| 183 | int retval = 0; | ||
| 184 | struct ipc_post *msg = NULL; | ||
| 185 | struct snd_sst_vol *fw_get_vol; | ||
| 186 | int str_id = get_vol->stream_id; | ||
| 187 | |||
| 188 | pr_debug("get vol called\n"); | ||
| 189 | |||
| 190 | if (sst_create_short_msg(&msg)) | ||
| 191 | return -ENOMEM; | ||
| 192 | |||
| 193 | sst_fill_header(&msg->header, | ||
| 194 | IPC_IA_GET_STREAM_VOL, 0, str_id); | ||
| 195 | sst_drv_ctx->vol_info_blk.condition = false; | ||
| 196 | sst_drv_ctx->vol_info_blk.ret_code = 0; | ||
| 197 | sst_drv_ctx->vol_info_blk.on = true; | ||
| 198 | fw_get_vol = kzalloc(sizeof(*fw_get_vol), GFP_ATOMIC); | ||
| 199 | if (!fw_get_vol) { | ||
| 200 | pr_err("mem allocation failed\n"); | ||
| 201 | kfree(msg); | ||
| 202 | return -ENOMEM; | ||
| 203 | } | ||
| 204 | sst_drv_ctx->vol_info_blk.data = (void *)fw_get_vol; | ||
| 205 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 206 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 207 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 208 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 209 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 210 | &sst_drv_ctx->vol_info_blk, SST_BLOCK_TIMEOUT); | ||
| 211 | if (retval) | ||
| 212 | retval = -EIO; | ||
| 213 | else { | ||
| 214 | pr_debug("stream id %d\n", fw_get_vol->stream_id); | ||
| 215 | pr_debug("volume %d\n", fw_get_vol->volume); | ||
| 216 | pr_debug("ramp duration %d\n", fw_get_vol->ramp_duration); | ||
| 217 | pr_debug("ramp_type %d\n", fw_get_vol->ramp_type); | ||
| 218 | memcpy(get_vol, fw_get_vol, sizeof(*fw_get_vol)); | ||
| 219 | } | ||
| 220 | return retval; | ||
| 221 | } | ||
| 222 | |||
| 223 | /** | ||
| 224 | * sst_set_vol - This function allows to set the premix gain or gain of a stream | ||
| 225 | * | ||
| 226 | * @set_vol: this holds the volume structure that needs to be set | ||
| 227 | * | ||
| 228 | * This function is called when premix gain or stream gain is requested to be set | ||
| 229 | */ | ||
| 230 | int sst_set_vol(struct snd_sst_vol *set_vol) | ||
| 231 | { | ||
| 232 | |||
| 233 | int retval = 0; | ||
| 234 | struct ipc_post *msg = NULL; | ||
| 235 | |||
| 236 | pr_debug("set vol called\n"); | ||
| 237 | |||
| 238 | if (sst_create_large_msg(&msg)) { | ||
| 239 | pr_err("message creation failed\n"); | ||
| 240 | return -ENOMEM; | ||
| 241 | } | ||
| 242 | sst_fill_header(&msg->header, IPC_IA_SET_STREAM_VOL, 1, | ||
| 243 | set_vol->stream_id); | ||
| 244 | |||
| 245 | msg->header.part.data = sizeof(u32) + sizeof(*set_vol); | ||
| 246 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 247 | memcpy(msg->mailbox_data + sizeof(u32), set_vol, sizeof(*set_vol)); | ||
| 248 | sst_drv_ctx->vol_info_blk.condition = false; | ||
| 249 | sst_drv_ctx->vol_info_blk.ret_code = 0; | ||
| 250 | sst_drv_ctx->vol_info_blk.on = true; | ||
| 251 | sst_drv_ctx->vol_info_blk.data = set_vol; | ||
| 252 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 253 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 254 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 255 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 256 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 257 | &sst_drv_ctx->vol_info_blk, SST_BLOCK_TIMEOUT); | ||
| 258 | if (retval) { | ||
| 259 | pr_err("error in set_vol = %d\n", retval); | ||
| 260 | retval = -EIO; | ||
| 261 | } | ||
| 262 | return retval; | ||
| 263 | } | ||
| 264 | |||
| 265 | /** | ||
| 266 | * sst_set_mute - This function sets premix mute or soft mute of a stream | ||
| 267 | * | ||
| 268 | * @set_mute: this holds the mute structure that needs to be set | ||
| 269 | * | ||
| 270 | * This function is called when premix mute or stream mute requested to be set | ||
| 271 | */ | ||
| 272 | int sst_set_mute(struct snd_sst_mute *set_mute) | ||
| 273 | { | ||
| 274 | |||
| 275 | int retval = 0; | ||
| 276 | struct ipc_post *msg = NULL; | ||
| 277 | |||
| 278 | pr_debug("set mute called\n"); | ||
| 279 | |||
| 280 | if (sst_create_large_msg(&msg)) { | ||
| 281 | pr_err("message creation failed\n"); | ||
| 282 | return -ENOMEM; | ||
| 283 | } | ||
| 284 | sst_fill_header(&msg->header, IPC_IA_SET_STREAM_MUTE, 1, | ||
| 285 | set_mute->stream_id); | ||
| 286 | sst_drv_ctx->mute_info_blk.condition = false; | ||
| 287 | sst_drv_ctx->mute_info_blk.ret_code = 0; | ||
| 288 | sst_drv_ctx->mute_info_blk.on = true; | ||
| 289 | sst_drv_ctx->mute_info_blk.data = set_mute; | ||
| 290 | |||
| 291 | msg->header.part.data = sizeof(u32) + sizeof(*set_mute); | ||
| 292 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 293 | memcpy(msg->mailbox_data + sizeof(u32), set_mute, | ||
| 294 | sizeof(*set_mute)); | ||
| 295 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 296 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 297 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 298 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 299 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 300 | &sst_drv_ctx->mute_info_blk, SST_BLOCK_TIMEOUT); | ||
| 301 | if (retval) { | ||
| 302 | pr_err("error in set_mute = %d\n", retval); | ||
| 303 | retval = -EIO; | ||
| 304 | } | ||
| 305 | return retval; | ||
| 306 | } | ||
| 307 | |||
| 308 | int sst_prepare_target(struct snd_sst_slot_info *slot) | ||
| 309 | { | ||
| 310 | if (slot->target_device == SND_SST_TARGET_PMIC | ||
| 311 | && slot->device_instance == 1) { | ||
| 312 | /*music mode*/ | ||
| 313 | if (sst_drv_ctx->pmic_port_instance == 0) | ||
| 314 | sst_drv_ctx->scard_ops->set_voice_port( | ||
| 315 | DEACTIVATE); | ||
| 316 | } else if ((slot->target_device == SND_SST_TARGET_PMIC || | ||
| 317 | slot->target_device == SND_SST_TARGET_MODEM) && | ||
| 318 | slot->device_instance == 0) { | ||
| 319 | /*voip mode where pcm0 is active*/ | ||
| 320 | if (sst_drv_ctx->pmic_port_instance == 1) | ||
| 321 | sst_drv_ctx->scard_ops->set_audio_port( | ||
| 322 | DEACTIVATE); | ||
| 323 | } | ||
| 324 | return 0; | ||
| 325 | } | ||
| 326 | |||
| 327 | int sst_activate_target(struct snd_sst_slot_info *slot) | ||
| 328 | { | ||
| 329 | if (slot->target_device == SND_SST_TARGET_PMIC && | ||
| 330 | slot->device_instance == 1) { | ||
| 331 | /*music mode*/ | ||
| 332 | sst_drv_ctx->pmic_port_instance = 1; | ||
| 333 | sst_drv_ctx->scard_ops->set_audio_port(ACTIVATE); | ||
| 334 | sst_drv_ctx->scard_ops->set_pcm_audio_params( | ||
| 335 | slot->pcm_params.sfreq, | ||
| 336 | slot->pcm_params.pcm_wd_sz, | ||
| 337 | slot->pcm_params.num_chan); | ||
| 338 | if (sst_drv_ctx->pb_streams) | ||
| 339 | sst_drv_ctx->scard_ops->power_up_pmic_pb(1); | ||
| 340 | if (sst_drv_ctx->cp_streams) | ||
| 341 | sst_drv_ctx->scard_ops->power_up_pmic_cp(1); | ||
| 342 | } else if ((slot->target_device == SND_SST_TARGET_PMIC || | ||
| 343 | slot->target_device == SND_SST_TARGET_MODEM) && | ||
| 344 | slot->device_instance == 0) { | ||
| 345 | /*voip mode where pcm0 is active*/ | ||
| 346 | sst_drv_ctx->pmic_port_instance = 0; | ||
| 347 | sst_drv_ctx->scard_ops->set_voice_port( | ||
| 348 | ACTIVATE); | ||
| 349 | sst_drv_ctx->scard_ops->power_up_pmic_pb(0); | ||
| 350 | /*sst_drv_ctx->scard_ops->power_up_pmic_cp(0);*/ | ||
| 351 | } | ||
| 352 | return 0; | ||
| 353 | } | ||
| 354 | |||
| 355 | int sst_parse_target(struct snd_sst_slot_info *slot) | ||
| 356 | { | ||
| 357 | int retval = 0; | ||
| 358 | |||
| 359 | if (slot->action == SND_SST_PORT_ACTIVATE && | ||
| 360 | slot->device_type == SND_SST_DEVICE_PCM) { | ||
| 361 | retval = sst_activate_target(slot); | ||
| 362 | if (retval) | ||
| 363 | pr_err("SST_Activate_target_fail\n"); | ||
| 364 | else | ||
| 365 | pr_err("SST_Activate_target_pass\n"); | ||
| 366 | } else if (slot->action == SND_SST_PORT_PREPARE && | ||
| 367 | slot->device_type == SND_SST_DEVICE_PCM) { | ||
| 368 | retval = sst_prepare_target(slot); | ||
| 369 | if (retval) | ||
| 370 | pr_err("SST_prepare_target_fail\n"); | ||
| 371 | else | ||
| 372 | pr_err("SST_prepare_target_pass\n"); | ||
| 373 | } else { | ||
| 374 | pr_err("slot_action : %d, device_type: %d\n", | ||
| 375 | slot->action, slot->device_type); | ||
| 376 | } | ||
| 377 | return retval; | ||
| 378 | } | ||
| 379 | |||
| 380 | int sst_send_target(struct snd_sst_target_device *target) | ||
| 381 | { | ||
| 382 | int retval; | ||
| 383 | struct ipc_post *msg; | ||
| 384 | |||
| 385 | if (sst_create_large_msg(&msg)) { | ||
| 386 | pr_err("message creation failed\n"); | ||
| 387 | return -ENOMEM; | ||
| 388 | } | ||
| 389 | sst_fill_header(&msg->header, IPC_IA_TARGET_DEV_SELECT, 1, 0); | ||
| 390 | sst_drv_ctx->tgt_dev_blk.condition = false; | ||
| 391 | sst_drv_ctx->tgt_dev_blk.ret_code = 0; | ||
| 392 | sst_drv_ctx->tgt_dev_blk.on = true; | ||
| 393 | |||
| 394 | msg->header.part.data = sizeof(u32) + sizeof(*target); | ||
| 395 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 396 | memcpy(msg->mailbox_data + sizeof(u32), target, | ||
| 397 | sizeof(*target)); | ||
| 398 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 399 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 400 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 401 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 402 | pr_debug("message sent- waiting\n"); | ||
| 403 | retval = sst_wait_interruptible_timeout(sst_drv_ctx, | ||
| 404 | &sst_drv_ctx->tgt_dev_blk, TARGET_DEV_BLOCK_TIMEOUT); | ||
| 405 | if (retval) | ||
| 406 | pr_err("target device ipc failed = 0x%x\n", retval); | ||
| 407 | return retval; | ||
| 408 | |||
| 409 | } | ||
| 410 | |||
| 411 | int sst_target_device_validate(struct snd_sst_target_device *target) | ||
| 412 | { | ||
| 413 | int retval = 0; | ||
| 414 | int i; | ||
| 415 | |||
| 416 | for (i = 0; i < SST_MAX_TARGET_DEVICES; i++) { | ||
| 417 | if (target->devices[i].device_type == SND_SST_DEVICE_PCM) { | ||
| 418 | /*pcm device, check params*/ | ||
| 419 | if (target->devices[i].device_instance == 1) { | ||
| 420 | if ((target->devices[i].device_mode != | ||
| 421 | SND_SST_DEV_MODE_PCM_MODE4_I2S) && | ||
| 422 | (target->devices[i].device_mode != | ||
| 423 | SND_SST_DEV_MODE_PCM_MODE4_RIGHT_JUSTIFIED) | ||
| 424 | && (target->devices[i].device_mode != | ||
| 425 | SND_SST_DEV_MODE_PCM_MODE1)) | ||
| 426 | goto err; | ||
| 427 | } else if (target->devices[i].device_instance == 0) { | ||
| 428 | if ((target->devices[i].device_mode != | ||
| 429 | SND_SST_DEV_MODE_PCM_MODE2) | ||
| 430 | && (target->devices[i].device_mode != | ||
| 431 | SND_SST_DEV_MODE_PCM_MODE4_I2S) | ||
| 432 | && (target->devices[i].device_mode != | ||
| 433 | SND_SST_DEV_MODE_PCM_MODE1)) | ||
| 434 | goto err; | ||
| 435 | if (target->devices[i].pcm_params.sfreq != 8000 | ||
| 436 | || target->devices[i].pcm_params.num_chan != 1 | ||
| 437 | || target->devices[i].pcm_params.pcm_wd_sz != | ||
| 438 | 16) | ||
| 439 | goto err; | ||
| 440 | } else { | ||
| 441 | err: | ||
| 442 | pr_err("i/p params incorrect\n"); | ||
| 443 | return -EINVAL; | ||
| 444 | } | ||
| 445 | } | ||
| 446 | } | ||
| 447 | return retval; | ||
| 448 | } | ||
| 449 | |||
| 450 | /** | ||
| 451 | * sst_target_device_select - This function sets the target device configurations | ||
| 452 | * | ||
| 453 | * @target: this parameter holds the configurations to be set | ||
| 454 | * | ||
| 455 | * This function is called when the user layer wants to change the target | ||
| 456 | * device's configurations | ||
| 457 | */ | ||
| 458 | |||
| 459 | int sst_target_device_select(struct snd_sst_target_device *target) | ||
| 460 | { | ||
| 461 | int retval, i, prepare_count = 0; | ||
| 462 | |||
| 463 | pr_debug("Target Device Select\n"); | ||
| 464 | |||
| 465 | if (target->device_route < 0 || target->device_route > 2) { | ||
| 466 | pr_err("device route is invalid\n"); | ||
| 467 | return -EINVAL; | ||
| 468 | } | ||
| 469 | |||
| 470 | if (target->device_route != 0) { | ||
| 471 | pr_err("Unsupported config\n"); | ||
| 472 | return -EIO; | ||
| 473 | } | ||
| 474 | retval = sst_target_device_validate(target); | ||
| 475 | if (retval) | ||
| 476 | return retval; | ||
| 477 | |||
| 478 | retval = sst_send_target(target); | ||
| 479 | if (retval) | ||
| 480 | return retval; | ||
| 481 | for (i = 0; i < SST_MAX_TARGET_DEVICES; i++) { | ||
| 482 | if (target->devices[i].action == SND_SST_PORT_ACTIVATE) { | ||
| 483 | pr_debug("activate called in %d\n", i); | ||
| 484 | retval = sst_parse_target(&target->devices[i]); | ||
| 485 | if (retval) | ||
| 486 | return retval; | ||
| 487 | } else if (target->devices[i].action == SND_SST_PORT_PREPARE) { | ||
| 488 | pr_debug("PREPARE in %d, Forwarding\n", i); | ||
| 489 | retval = sst_parse_target(&target->devices[i]); | ||
| 490 | if (retval) { | ||
| 491 | pr_err("Parse Target fail %d\n", retval); | ||
| 492 | return retval; | ||
| 493 | } | ||
| 494 | pr_debug("Parse Target successful %d\n", retval); | ||
| 495 | if (target->devices[i].device_type == | ||
| 496 | SND_SST_DEVICE_PCM) | ||
| 497 | prepare_count++; | ||
| 498 | } | ||
| 499 | } | ||
| 500 | if (target->devices[0].action == SND_SST_PORT_PREPARE && | ||
| 501 | prepare_count == 0) | ||
| 502 | sst_drv_ctx->scard_ops->power_down_pmic(); | ||
| 503 | |||
| 504 | return retval; | ||
| 505 | } | ||
| 506 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 507 | /*This function gets the physical address of the secure memory from the handle*/ | ||
| 508 | static inline int sst_get_RAR(struct RAR_buffer *buffers, int count) | ||
| 509 | { | ||
| 510 | int retval = 0, rar_status = 0; | ||
| 511 | |||
| 512 | rar_status = rar_handle_to_bus(buffers, count); | ||
| 513 | |||
| 514 | if (count != rar_status) { | ||
| 515 | pr_err("The rar CALL Failed"); | ||
| 516 | retval = -EIO; | ||
| 517 | } | ||
| 518 | if (buffers->info.type != RAR_TYPE_AUDIO) { | ||
| 519 | pr_err("Invalid RAR type\n"); | ||
| 520 | return -EINVAL; | ||
| 521 | } | ||
| 522 | return retval; | ||
| 523 | } | ||
| 524 | |||
| 525 | #endif | ||
| 526 | |||
| 527 | /* This function creates the scatter gather list to be sent to firmware to | ||
| 528 | capture/playback data*/ | ||
| 529 | static int sst_create_sg_list(struct stream_info *stream, | ||
| 530 | struct sst_frame_info *sg_list) | ||
| 531 | { | ||
| 532 | struct sst_stream_bufs *kbufs = NULL; | ||
| 533 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 534 | struct RAR_buffer rar_buffers; | ||
| 535 | int retval = 0; | ||
| 536 | #endif | ||
| 537 | int i = 0; | ||
| 538 | list_for_each_entry(kbufs, &stream->bufs, node) { | ||
| 539 | if (kbufs->in_use == false) { | ||
| 540 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 541 | if (stream->ops == STREAM_OPS_PLAYBACK_DRM) { | ||
| 542 | pr_debug("DRM playback handling\n"); | ||
| 543 | rar_buffers.info.handle = (__u32)kbufs->addr; | ||
| 544 | rar_buffers.info.size = kbufs->size; | ||
| 545 | pr_debug("rar handle 0x%x size=0x%x\n", | ||
| 546 | rar_buffers.info.handle, | ||
| 547 | rar_buffers.info.size); | ||
| 548 | retval = sst_get_RAR(&rar_buffers, 1); | ||
| 549 | |||
| 550 | if (retval) | ||
| 551 | return retval; | ||
| 552 | sg_list->addr[i].addr = rar_buffers.bus_address; | ||
| 553 | /* rar_buffers.info.size; */ | ||
| 554 | sg_list->addr[i].size = (__u32)kbufs->size; | ||
| 555 | pr_debug("phyaddr[%d] 0x%x Size:0x%x\n" | ||
| 556 | , i, sg_list->addr[i].addr, | ||
| 557 | sg_list->addr[i].size); | ||
| 558 | } | ||
| 559 | #endif | ||
| 560 | if (stream->ops != STREAM_OPS_PLAYBACK_DRM) { | ||
| 561 | sg_list->addr[i].addr = | ||
| 562 | virt_to_phys((void *) | ||
| 563 | kbufs->addr + kbufs->offset); | ||
| 564 | sg_list->addr[i].size = kbufs->size; | ||
| 565 | pr_debug("phyaddr[%d]:0x%x Size:0x%x\n" | ||
| 566 | , i , sg_list->addr[i].addr, kbufs->size); | ||
| 567 | } | ||
| 568 | stream->curr_bytes += sg_list->addr[i].size; | ||
| 569 | kbufs->in_use = true; | ||
| 570 | i++; | ||
| 571 | } | ||
| 572 | if (i >= MAX_NUM_SCATTER_BUFFERS) | ||
| 573 | break; | ||
| 574 | } | ||
| 575 | |||
| 576 | sg_list->num_entries = i; | ||
| 577 | pr_debug("sg list entries = %d\n", sg_list->num_entries); | ||
| 578 | return i; | ||
| 579 | } | ||
| 580 | |||
| 581 | |||
| 582 | /** | ||
| 583 | * sst_play_frame - Send msg for sending stream frames | ||
| 584 | * | ||
| 585 | * @str_id: ID of stream | ||
| 586 | * | ||
| 587 | * This function is called to send data to be played out | ||
| 588 | * to the firmware | ||
| 589 | */ | ||
| 590 | int sst_play_frame(int str_id) | ||
| 591 | { | ||
| 592 | int i = 0, retval = 0; | ||
| 593 | struct ipc_post *msg = NULL; | ||
| 594 | struct sst_frame_info sg_list = {0}; | ||
| 595 | struct sst_stream_bufs *kbufs = NULL, *_kbufs; | ||
| 596 | struct stream_info *stream; | ||
| 597 | |||
| 598 | pr_debug("play frame for %d\n", str_id); | ||
| 599 | retval = sst_validate_strid(str_id); | ||
| 600 | if (retval) | ||
| 601 | return retval; | ||
| 602 | |||
| 603 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 604 | /* clear prev sent buffers */ | ||
| 605 | list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) { | ||
| 606 | if (kbufs->in_use == true) { | ||
| 607 | spin_lock(&stream->pcm_lock); | ||
| 608 | list_del(&kbufs->node); | ||
| 609 | spin_unlock(&stream->pcm_lock); | ||
| 610 | kfree(kbufs); | ||
| 611 | } | ||
| 612 | } | ||
| 613 | /* update bytes sent */ | ||
| 614 | stream->cumm_bytes += stream->curr_bytes; | ||
| 615 | stream->curr_bytes = 0; | ||
| 616 | if (list_empty(&stream->bufs)) { | ||
| 617 | /* no user buffer available */ | ||
| 618 | pr_debug("Null buffer stream status %d\n", stream->status); | ||
| 619 | stream->prev = stream->status; | ||
| 620 | stream->status = STREAM_INIT; | ||
| 621 | pr_debug("new stream status = %d\n", stream->status); | ||
| 622 | if (stream->need_draining == true) { | ||
| 623 | pr_debug("draining stream\n"); | ||
| 624 | if (sst_create_short_msg(&msg)) { | ||
| 625 | pr_err("mem allocation failed\n"); | ||
| 626 | return -ENOMEM; | ||
| 627 | } | ||
| 628 | sst_fill_header(&msg->header, IPC_IA_DRAIN_STREAM, | ||
| 629 | 0, str_id); | ||
| 630 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 631 | list_add_tail(&msg->node, | ||
| 632 | &sst_drv_ctx->ipc_dispatch_list); | ||
| 633 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 634 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 635 | } else if (stream->data_blk.on == true) { | ||
| 636 | pr_debug("user list empty.. wake\n"); | ||
| 637 | /* unblock */ | ||
| 638 | stream->data_blk.ret_code = 0; | ||
| 639 | stream->data_blk.condition = true; | ||
| 640 | stream->data_blk.on = false; | ||
| 641 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 642 | } | ||
| 643 | return 0; | ||
| 644 | } | ||
| 645 | |||
| 646 | /* create list */ | ||
| 647 | i = sst_create_sg_list(stream, &sg_list); | ||
| 648 | |||
| 649 | /* post msg */ | ||
| 650 | if (sst_create_large_msg(&msg)) | ||
| 651 | return -ENOMEM; | ||
| 652 | |||
| 653 | sst_fill_header(&msg->header, IPC_IA_PLAY_FRAMES, 1, str_id); | ||
| 654 | msg->header.part.data = sizeof(u32) + sizeof(sg_list); | ||
| 655 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 656 | memcpy(msg->mailbox_data + sizeof(u32), &sg_list, sizeof(sg_list)); | ||
| 657 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 658 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 659 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 660 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 661 | return 0; | ||
| 662 | |||
| 663 | } | ||
| 664 | |||
| 665 | /** | ||
| 666 | * sst_capture_frame - Send msg for sending stream frames | ||
| 667 | * | ||
| 668 | * @str_id: ID of stream | ||
| 669 | * | ||
| 670 | * This function is called to capture data from the firmware | ||
| 671 | */ | ||
| 672 | int sst_capture_frame(int str_id) | ||
| 673 | { | ||
| 674 | int i = 0, retval = 0; | ||
| 675 | struct ipc_post *msg = NULL; | ||
| 676 | struct sst_frame_info sg_list = {0}; | ||
| 677 | struct sst_stream_bufs *kbufs = NULL, *_kbufs; | ||
| 678 | struct stream_info *stream; | ||
| 679 | |||
| 680 | |||
| 681 | pr_debug("capture frame for %d\n", str_id); | ||
| 682 | retval = sst_validate_strid(str_id); | ||
| 683 | if (retval) | ||
| 684 | return retval; | ||
| 685 | stream = &sst_drv_ctx->streams[str_id]; | ||
| 686 | /* clear prev sent buffers */ | ||
| 687 | list_for_each_entry_safe(kbufs, _kbufs, &stream->bufs, node) { | ||
| 688 | if (kbufs->in_use == true) { | ||
| 689 | list_del(&kbufs->node); | ||
| 690 | kfree(kbufs); | ||
| 691 | pr_debug("del node\n"); | ||
| 692 | } | ||
| 693 | } | ||
| 694 | if (list_empty(&stream->bufs)) { | ||
| 695 | /* no user buffer available */ | ||
| 696 | pr_debug("Null buffer!!!!stream status %d\n", | ||
| 697 | stream->status); | ||
| 698 | stream->prev = stream->status; | ||
| 699 | stream->status = STREAM_INIT; | ||
| 700 | pr_debug("new stream status = %d\n", | ||
| 701 | stream->status); | ||
| 702 | if (stream->data_blk.on == true) { | ||
| 703 | pr_debug("user list empty.. wake\n"); | ||
| 704 | /* unblock */ | ||
| 705 | stream->data_blk.ret_code = 0; | ||
| 706 | stream->data_blk.condition = true; | ||
| 707 | stream->data_blk.on = false; | ||
| 708 | wake_up(&sst_drv_ctx->wait_queue); | ||
| 709 | |||
| 710 | } | ||
| 711 | return 0; | ||
| 712 | } | ||
| 713 | /* create new sg list */ | ||
| 714 | i = sst_create_sg_list(stream, &sg_list); | ||
| 715 | |||
| 716 | /* post msg */ | ||
| 717 | if (sst_create_large_msg(&msg)) | ||
| 718 | return -ENOMEM; | ||
| 719 | |||
| 720 | sst_fill_header(&msg->header, IPC_IA_CAPT_FRAMES, 1, str_id); | ||
| 721 | msg->header.part.data = sizeof(u32) + sizeof(sg_list); | ||
| 722 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 723 | memcpy(msg->mailbox_data + sizeof(u32), &sg_list, sizeof(sg_list)); | ||
| 724 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 725 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 726 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 727 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 728 | |||
| 729 | |||
| 730 | /*update bytes recevied*/ | ||
| 731 | stream->cumm_bytes += stream->curr_bytes; | ||
| 732 | stream->curr_bytes = 0; | ||
| 733 | |||
| 734 | pr_debug("Cum bytes = %d\n", stream->cumm_bytes); | ||
| 735 | return 0; | ||
| 736 | } | ||
| 737 | |||
| 738 | /*This function is used to calculate the minimum size of input buffers given*/ | ||
| 739 | static unsigned int calculate_min_size(struct snd_sst_buffs *bufs) | ||
| 740 | { | ||
| 741 | int i, min_val = bufs->buff_entry[0].size; | ||
| 742 | for (i = 1 ; i < bufs->entries; i++) { | ||
| 743 | if (bufs->buff_entry[i].size < min_val) | ||
| 744 | min_val = bufs->buff_entry[i].size; | ||
| 745 | } | ||
| 746 | pr_debug("min_val = %d\n", min_val); | ||
| 747 | return min_val; | ||
| 748 | } | ||
| 749 | |||
| 750 | static unsigned int calculate_max_size(struct snd_sst_buffs *bufs) | ||
| 751 | { | ||
| 752 | int i, max_val = bufs->buff_entry[0].size; | ||
| 753 | for (i = 1 ; i < bufs->entries; i++) { | ||
| 754 | if (bufs->buff_entry[i].size > max_val) | ||
| 755 | max_val = bufs->buff_entry[i].size; | ||
| 756 | } | ||
| 757 | pr_debug("max_val = %d\n", max_val); | ||
| 758 | return max_val; | ||
| 759 | } | ||
| 760 | |||
| 761 | /*This function is used to allocate input and output buffers to be sent to | ||
| 762 | the firmware that will take encoded data and return decoded data*/ | ||
| 763 | static int sst_allocate_decode_buf(struct stream_info *str_info, | ||
| 764 | struct snd_sst_dbufs *dbufs, | ||
| 765 | unsigned int cum_input_given, | ||
| 766 | unsigned int cum_output_given) | ||
| 767 | { | ||
| 768 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 769 | if (str_info->ops == STREAM_OPS_PLAYBACK_DRM) { | ||
| 770 | |||
| 771 | if (dbufs->ibufs->type == SST_BUF_RAR && | ||
| 772 | dbufs->obufs->type == SST_BUF_RAR) { | ||
| 773 | if (dbufs->ibufs->entries == dbufs->obufs->entries) | ||
| 774 | return 0; | ||
| 775 | else { | ||
| 776 | pr_err("RAR entries dont match\n"); | ||
| 777 | return -EINVAL; | ||
| 778 | } | ||
| 779 | } else | ||
| 780 | str_info->decode_osize = cum_output_given; | ||
| 781 | return 0; | ||
| 782 | |||
| 783 | } | ||
| 784 | #endif | ||
| 785 | if (!str_info->decode_ibuf) { | ||
| 786 | pr_debug("no i/p buffers, trying full size\n"); | ||
| 787 | str_info->decode_isize = cum_input_given; | ||
| 788 | str_info->decode_ibuf = kzalloc(str_info->decode_isize, | ||
| 789 | GFP_KERNEL); | ||
| 790 | str_info->idecode_alloc = str_info->decode_isize; | ||
| 791 | } | ||
| 792 | if (!str_info->decode_ibuf) { | ||
| 793 | pr_debug("buff alloc failed, try max size\n"); | ||
| 794 | str_info->decode_isize = calculate_max_size(dbufs->ibufs); | ||
| 795 | str_info->decode_ibuf = kzalloc( | ||
| 796 | str_info->decode_isize, GFP_KERNEL); | ||
| 797 | str_info->idecode_alloc = str_info->decode_isize; | ||
| 798 | } | ||
| 799 | if (!str_info->decode_ibuf) { | ||
| 800 | pr_debug("buff alloc failed, try min size\n"); | ||
| 801 | str_info->decode_isize = calculate_min_size(dbufs->ibufs); | ||
| 802 | str_info->decode_ibuf = kzalloc(str_info->decode_isize, | ||
| 803 | GFP_KERNEL); | ||
| 804 | if (!str_info->decode_ibuf) { | ||
| 805 | pr_err("mem allocation failed\n"); | ||
| 806 | return -ENOMEM; | ||
| 807 | } | ||
| 808 | str_info->idecode_alloc = str_info->decode_isize; | ||
| 809 | } | ||
| 810 | str_info->decode_osize = cum_output_given; | ||
| 811 | if (str_info->decode_osize > sst_drv_ctx->mmap_len) | ||
| 812 | str_info->decode_osize = sst_drv_ctx->mmap_len; | ||
| 813 | return 0; | ||
| 814 | } | ||
| 815 | |||
| 816 | /*This function is used to send the message to firmware to decode the data*/ | ||
| 817 | static int sst_send_decode_mess(int str_id, struct stream_info *str_info, | ||
| 818 | struct snd_sst_decode_info *dec_info) | ||
| 819 | { | ||
| 820 | struct ipc_post *msg = NULL; | ||
| 821 | int retval = 0; | ||
| 822 | |||
| 823 | pr_debug("SST DBG:sst_set_mute:called\n"); | ||
| 824 | |||
| 825 | if (str_info->decode_ibuf_type == SST_BUF_RAR) { | ||
| 826 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 827 | dec_info->frames_in.addr[0].addr = | ||
| 828 | (unsigned long)str_info->decode_ibuf; | ||
| 829 | dec_info->frames_in.addr[0].size = | ||
| 830 | str_info->decode_isize; | ||
| 831 | #endif | ||
| 832 | |||
| 833 | } else { | ||
| 834 | dec_info->frames_in.addr[0].addr = virt_to_phys((void *) | ||
| 835 | str_info->decode_ibuf); | ||
| 836 | dec_info->frames_in.addr[0].size = str_info->decode_isize; | ||
| 837 | } | ||
| 838 | |||
| 839 | |||
| 840 | if (str_info->decode_obuf_type == SST_BUF_RAR) { | ||
| 841 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 842 | dec_info->frames_out.addr[0].addr = | ||
| 843 | (unsigned long)str_info->decode_obuf; | ||
| 844 | dec_info->frames_out.addr[0].size = str_info->decode_osize; | ||
| 845 | #endif | ||
| 846 | |||
| 847 | } else { | ||
| 848 | dec_info->frames_out.addr[0].addr = virt_to_phys((void *) | ||
| 849 | str_info->decode_obuf) ; | ||
| 850 | dec_info->frames_out.addr[0].size = str_info->decode_osize; | ||
| 851 | } | ||
| 852 | |||
| 853 | dec_info->frames_in.num_entries = 1; | ||
| 854 | dec_info->frames_out.num_entries = 1; | ||
| 855 | dec_info->frames_in.rsrvd = 0; | ||
| 856 | dec_info->frames_out.rsrvd = 0; | ||
| 857 | dec_info->input_bytes_consumed = 0; | ||
| 858 | dec_info->output_bytes_produced = 0; | ||
| 859 | if (sst_create_large_msg(&msg)) { | ||
| 860 | pr_err("message creation failed\n"); | ||
| 861 | return -ENOMEM; | ||
| 862 | } | ||
| 863 | |||
| 864 | sst_fill_header(&msg->header, IPC_IA_DECODE_FRAMES, 1, str_id); | ||
| 865 | msg->header.part.data = sizeof(u32) + sizeof(*dec_info); | ||
| 866 | memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); | ||
| 867 | memcpy(msg->mailbox_data + sizeof(u32), dec_info, | ||
| 868 | sizeof(*dec_info)); | ||
| 869 | spin_lock(&sst_drv_ctx->list_spin_lock); | ||
| 870 | list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); | ||
| 871 | spin_unlock(&sst_drv_ctx->list_spin_lock); | ||
| 872 | str_info->data_blk.condition = false; | ||
| 873 | str_info->data_blk.ret_code = 0; | ||
| 874 | str_info->data_blk.on = true; | ||
| 875 | str_info->data_blk.data = dec_info; | ||
| 876 | sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); | ||
| 877 | retval = sst_wait_interruptible(sst_drv_ctx, &str_info->data_blk); | ||
| 878 | return retval; | ||
| 879 | } | ||
| 880 | |||
| 881 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 882 | static int sst_prepare_input_buffers_rar(struct stream_info *str_info, | ||
| 883 | struct snd_sst_dbufs *dbufs, | ||
| 884 | int *input_index, int *in_copied, | ||
| 885 | int *input_index_valid_size, int *new_entry_flag) | ||
| 886 | { | ||
| 887 | int retval = 0, i; | ||
| 888 | |||
| 889 | if (str_info->ops == STREAM_OPS_PLAYBACK_DRM) { | ||
| 890 | struct RAR_buffer rar_buffers; | ||
| 891 | __u32 info; | ||
| 892 | retval = copy_from_user((void *) &info, | ||
| 893 | dbufs->ibufs->buff_entry[i].buffer, | ||
| 894 | sizeof(__u32)); | ||
| 895 | if (retval) { | ||
| 896 | pr_err("cpy from user fail\n"); | ||
| 897 | return -EAGAIN; | ||
| 898 | } | ||
| 899 | rar_buffers.info.type = dbufs->ibufs->type; | ||
| 900 | rar_buffers.info.size = dbufs->ibufs->buff_entry[i].size; | ||
| 901 | rar_buffers.info.handle = info; | ||
| 902 | pr_debug("rar in DnR(input buffer function)=0x%x size=0x%x", | ||
| 903 | rar_buffers.info.handle, | ||
| 904 | rar_buffers.info.size); | ||
| 905 | retval = sst_get_RAR(&rar_buffers, 1); | ||
| 906 | if (retval) { | ||
| 907 | pr_debug("SST ERR: RAR API failed\n"); | ||
| 908 | return retval; | ||
| 909 | } | ||
| 910 | str_info->decode_ibuf = | ||
| 911 | (void *) ((unsigned long) rar_buffers.bus_address); | ||
| 912 | pr_debug("RAR buf addr in DnR (input buffer function)0x%lu", | ||
| 913 | (unsigned long) str_info->decode_ibuf); | ||
| 914 | pr_debug("rar in DnR decode function/output b_add rar =0x%lu", | ||
| 915 | (unsigned long) rar_buffers.bus_address); | ||
| 916 | *input_index = i + 1; | ||
| 917 | str_info->decode_isize = dbufs->ibufs->buff_entry[i].size; | ||
| 918 | str_info->decode_ibuf_type = dbufs->ibufs->type; | ||
| 919 | *in_copied = str_info->decode_isize; | ||
| 920 | } | ||
| 921 | return retval; | ||
| 922 | } | ||
| 923 | #endif | ||
| 924 | /*This function is used to prepare the kernel input buffers with contents | ||
| 925 | before sending for decode*/ | ||
| 926 | static int sst_prepare_input_buffers(struct stream_info *str_info, | ||
| 927 | struct snd_sst_dbufs *dbufs, | ||
| 928 | int *input_index, int *in_copied, | ||
| 929 | int *input_index_valid_size, int *new_entry_flag) | ||
| 930 | { | ||
| 931 | int i, cpy_size, retval = 0; | ||
| 932 | |||
| 933 | pr_debug("input_index = %d, input entries = %d\n", | ||
| 934 | *input_index, dbufs->ibufs->entries); | ||
| 935 | for (i = *input_index; i < dbufs->ibufs->entries; i++) { | ||
| 936 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 937 | retval = sst_prepare_input_buffers_rar(str_info, | ||
| 938 | dbufs, input_index, in_copied, | ||
| 939 | input_index_valid_size, new_entry_flag); | ||
| 940 | if (retval) { | ||
| 941 | pr_err("In prepare input buffers for RAR\n"); | ||
| 942 | return -EIO; | ||
| 943 | } | ||
| 944 | #endif | ||
| 945 | *input_index = i; | ||
| 946 | if (*input_index_valid_size == 0) | ||
| 947 | *input_index_valid_size = | ||
| 948 | dbufs->ibufs->buff_entry[i].size; | ||
| 949 | pr_debug("inout addr = %p, size = %d\n", | ||
| 950 | dbufs->ibufs->buff_entry[i].buffer, | ||
| 951 | *input_index_valid_size); | ||
| 952 | pr_debug("decode_isize = %d, in_copied %d\n", | ||
| 953 | str_info->decode_isize, *in_copied); | ||
| 954 | if (*input_index_valid_size <= | ||
| 955 | (str_info->decode_isize - *in_copied)) | ||
| 956 | cpy_size = *input_index_valid_size; | ||
| 957 | else | ||
| 958 | cpy_size = str_info->decode_isize - *in_copied; | ||
| 959 | |||
| 960 | pr_debug("cpy size = %d\n", cpy_size); | ||
| 961 | if (!dbufs->ibufs->buff_entry[i].buffer) { | ||
| 962 | pr_err("i/p buffer is null\n"); | ||
| 963 | return -EINVAL; | ||
| 964 | } | ||
| 965 | pr_debug("Try copy To %p, From %p, size %d\n", | ||
| 966 | str_info->decode_ibuf + *in_copied, | ||
| 967 | dbufs->ibufs->buff_entry[i].buffer, cpy_size); | ||
| 968 | |||
| 969 | retval = | ||
| 970 | copy_from_user((void *)(str_info->decode_ibuf + *in_copied), | ||
| 971 | (void *) dbufs->ibufs->buff_entry[i].buffer, | ||
| 972 | cpy_size); | ||
| 973 | if (retval) { | ||
| 974 | pr_err("copy from user failed\n"); | ||
| 975 | return -EIO; | ||
| 976 | } | ||
| 977 | *in_copied += cpy_size; | ||
| 978 | *input_index_valid_size -= cpy_size; | ||
| 979 | pr_debug("in buff size = %d, in_copied = %d\n", | ||
| 980 | *input_index_valid_size, *in_copied); | ||
| 981 | if (*input_index_valid_size != 0) { | ||
| 982 | pr_debug("more input buffers left\n"); | ||
| 983 | dbufs->ibufs->buff_entry[i].buffer += cpy_size; | ||
| 984 | break; | ||
| 985 | } | ||
| 986 | if (*in_copied == str_info->decode_isize && | ||
| 987 | *input_index_valid_size == 0 && | ||
| 988 | (i+1) <= dbufs->ibufs->entries) { | ||
| 989 | pr_debug("all input buffers copied\n"); | ||
| 990 | *new_entry_flag = true; | ||
| 991 | *input_index = i + 1; | ||
| 992 | break; | ||
| 993 | } | ||
| 994 | } | ||
| 995 | return retval; | ||
| 996 | } | ||
| 997 | |||
| 998 | /* This function is used to copy the decoded data from kernel buffers to | ||
| 999 | the user output buffers with contents after decode*/ | ||
| 1000 | static int sst_prepare_output_buffers(struct stream_info *str_info, | ||
| 1001 | struct snd_sst_dbufs *dbufs, | ||
| 1002 | int *output_index, int output_size, | ||
| 1003 | int *out_copied) | ||
| 1004 | |||
| 1005 | { | ||
| 1006 | int i, cpy_size, retval = 0; | ||
| 1007 | pr_debug("output_index = %d, output entries = %d\n", | ||
| 1008 | *output_index, | ||
| 1009 | dbufs->obufs->entries); | ||
| 1010 | for (i = *output_index; i < dbufs->obufs->entries; i++) { | ||
| 1011 | *output_index = i; | ||
| 1012 | pr_debug("output addr = %p, size = %d\n", | ||
| 1013 | dbufs->obufs->buff_entry[i].buffer, | ||
| 1014 | dbufs->obufs->buff_entry[i].size); | ||
| 1015 | pr_debug("output_size = %d, out_copied = %d\n", | ||
| 1016 | output_size, *out_copied); | ||
| 1017 | if (dbufs->obufs->buff_entry[i].size < | ||
| 1018 | (output_size - *out_copied)) | ||
| 1019 | cpy_size = dbufs->obufs->buff_entry[i].size; | ||
| 1020 | else | ||
| 1021 | cpy_size = output_size - *out_copied; | ||
| 1022 | pr_debug("cpy size = %d\n", cpy_size); | ||
| 1023 | pr_debug("Try copy To: %p, From %p, size %d\n", | ||
| 1024 | dbufs->obufs->buff_entry[i].buffer, | ||
| 1025 | sst_drv_ctx->mmap_mem + *out_copied, | ||
| 1026 | cpy_size); | ||
| 1027 | retval = copy_to_user(dbufs->obufs->buff_entry[i].buffer, | ||
| 1028 | sst_drv_ctx->mmap_mem + *out_copied, | ||
| 1029 | cpy_size); | ||
| 1030 | if (retval) { | ||
| 1031 | pr_err("copy to user failed\n"); | ||
| 1032 | return -EIO; | ||
| 1033 | } else | ||
| 1034 | pr_debug("copy to user passed\n"); | ||
| 1035 | *out_copied += cpy_size; | ||
| 1036 | dbufs->obufs->buff_entry[i].size -= cpy_size; | ||
| 1037 | pr_debug("o/p buff size %d, out_copied %d\n", | ||
| 1038 | dbufs->obufs->buff_entry[i].size, *out_copied); | ||
| 1039 | if (dbufs->obufs->buff_entry[i].size != 0) { | ||
| 1040 | *output_index = i; | ||
| 1041 | dbufs->obufs->buff_entry[i].buffer += cpy_size; | ||
| 1042 | break; | ||
| 1043 | } else if (*out_copied == output_size) { | ||
| 1044 | *output_index = i + 1; | ||
| 1045 | break; | ||
| 1046 | } | ||
| 1047 | } | ||
| 1048 | return retval; | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | /** | ||
| 1052 | * sst_decode - Send msg for decoding frames | ||
| 1053 | * | ||
| 1054 | * @str_id: ID of stream | ||
| 1055 | * @dbufs: param that holds the user input and output buffers and size | ||
| 1056 | * | ||
| 1057 | * This function is called to decode data from the firmware | ||
| 1058 | */ | ||
| 1059 | int sst_decode(int str_id, struct snd_sst_dbufs *dbufs) | ||
| 1060 | { | ||
| 1061 | int retval = 0, i; | ||
| 1062 | unsigned long long total_input = 0 , total_output = 0; | ||
| 1063 | unsigned int cum_input_given = 0 , cum_output_given = 0; | ||
| 1064 | int copy_in_done = false, copy_out_done = false; | ||
| 1065 | int input_index = 0, output_index = 0; | ||
| 1066 | int input_index_valid_size = 0; | ||
| 1067 | int in_copied, out_copied; | ||
| 1068 | int new_entry_flag; | ||
| 1069 | u64 output_size; | ||
| 1070 | struct stream_info *str_info; | ||
| 1071 | struct snd_sst_decode_info dec_info; | ||
| 1072 | unsigned long long input_bytes, output_bytes; | ||
| 1073 | |||
| 1074 | sst_drv_ctx->scard_ops->power_down_pmic(); | ||
| 1075 | pr_debug("Powering_down_PMIC...\n"); | ||
| 1076 | |||
| 1077 | retval = sst_validate_strid(str_id); | ||
| 1078 | if (retval) | ||
| 1079 | return retval; | ||
| 1080 | |||
| 1081 | str_info = &sst_drv_ctx->streams[str_id]; | ||
| 1082 | if (str_info->status != STREAM_INIT) { | ||
| 1083 | pr_err("invalid stream state = %d\n", | ||
| 1084 | str_info->status); | ||
| 1085 | return -EINVAL; | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | str_info->prev = str_info->status; | ||
| 1089 | str_info->status = STREAM_DECODE; | ||
| 1090 | |||
| 1091 | for (i = 0; i < dbufs->ibufs->entries; i++) | ||
| 1092 | cum_input_given += dbufs->ibufs->buff_entry[i].size; | ||
| 1093 | for (i = 0; i < dbufs->obufs->entries; i++) | ||
| 1094 | cum_output_given += dbufs->obufs->buff_entry[i].size; | ||
| 1095 | |||
| 1096 | /* input and output buffer allocation */ | ||
| 1097 | retval = sst_allocate_decode_buf(str_info, dbufs, | ||
| 1098 | cum_input_given, cum_output_given); | ||
| 1099 | if (retval) { | ||
| 1100 | pr_err("mem allocation failed, abort!!!\n"); | ||
| 1101 | retval = -ENOMEM; | ||
| 1102 | goto finish; | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | str_info->decode_isize = str_info->idecode_alloc; | ||
| 1106 | str_info->decode_ibuf_type = dbufs->ibufs->type; | ||
| 1107 | str_info->decode_obuf_type = dbufs->obufs->type; | ||
| 1108 | |||
| 1109 | while ((copy_out_done == false) && (copy_in_done == false)) { | ||
| 1110 | in_copied = 0; | ||
| 1111 | new_entry_flag = false; | ||
| 1112 | retval = sst_prepare_input_buffers(str_info,\ | ||
| 1113 | dbufs, &input_index, &in_copied, | ||
| 1114 | &input_index_valid_size, &new_entry_flag); | ||
| 1115 | if (retval) { | ||
| 1116 | pr_err("prepare in buffers failed\n"); | ||
| 1117 | goto finish; | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | if (str_info->ops != STREAM_OPS_PLAYBACK_DRM) | ||
| 1121 | str_info->decode_obuf = sst_drv_ctx->mmap_mem; | ||
| 1122 | |||
| 1123 | #ifdef CONFIG_MRST_RAR_HANDLER | ||
| 1124 | else { | ||
| 1125 | if (dbufs->obufs->type == SST_BUF_RAR) { | ||
| 1126 | struct RAR_buffer rar_buffers; | ||
| 1127 | __u32 info; | ||
| 1128 | |||
| 1129 | pr_debug("DRM"); | ||
| 1130 | retval = copy_from_user((void *) &info, | ||
| 1131 | dbufs->obufs-> | ||
| 1132 | buff_entry[output_index].buffer, | ||
| 1133 | sizeof(__u32)); | ||
| 1134 | |||
| 1135 | rar_buffers.info.size = dbufs->obufs-> | ||
| 1136 | buff_entry[output_index].size; | ||
| 1137 | rar_buffers.info.handle = info; | ||
| 1138 | retval = sst_get_RAR(&rar_buffers, 1); | ||
| 1139 | if (retval) | ||
| 1140 | return retval; | ||
| 1141 | |||
| 1142 | str_info->decode_obuf = (void *)((unsigned long) | ||
| 1143 | rar_buffers.bus_address); | ||
| 1144 | str_info->decode_osize = dbufs->obufs-> | ||
| 1145 | buff_entry[output_index].size; | ||
| 1146 | str_info->decode_obuf_type = dbufs->obufs->type; | ||
| 1147 | pr_debug("DRM handling\n"); | ||
| 1148 | pr_debug("o/p_add=0x%lu Size=0x%x\n", | ||
| 1149 | (unsigned long) str_info->decode_obuf, | ||
| 1150 | str_info->decode_osize); | ||
| 1151 | } else { | ||
| 1152 | str_info->decode_obuf = sst_drv_ctx->mmap_mem; | ||
| 1153 | str_info->decode_osize = dbufs->obufs-> | ||
| 1154 | buff_entry[output_index].size; | ||
| 1155 | |||
| 1156 | } | ||
| 1157 | } | ||
| 1158 | #endif | ||
| 1159 | if (str_info->ops != STREAM_OPS_PLAYBACK_DRM) { | ||
| 1160 | if (str_info->decode_isize > in_copied) { | ||
| 1161 | str_info->decode_isize = in_copied; | ||
| 1162 | pr_debug("i/p size = %d\n", | ||
| 1163 | str_info->decode_isize); | ||
| 1164 | } | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | |||
| 1168 | retval = sst_send_decode_mess(str_id, str_info, &dec_info); | ||
| 1169 | if (retval || dec_info.input_bytes_consumed == 0) { | ||
| 1170 | pr_err("SST ERR: mess failed or no input consumed\n"); | ||
| 1171 | goto finish; | ||
| 1172 | } | ||
| 1173 | input_bytes = dec_info.input_bytes_consumed; | ||
| 1174 | output_bytes = dec_info.output_bytes_produced; | ||
| 1175 | |||
| 1176 | pr_debug("in_copied=%d, con=%lld, prod=%lld\n", | ||
| 1177 | in_copied, input_bytes, output_bytes); | ||
| 1178 | if (dbufs->obufs->type == SST_BUF_RAR) { | ||
| 1179 | output_index += 1; | ||
| 1180 | if (output_index == dbufs->obufs->entries) { | ||
| 1181 | copy_in_done = true; | ||
| 1182 | pr_debug("all i/p cpy done\n"); | ||
| 1183 | } | ||
| 1184 | total_output += output_bytes; | ||
| 1185 | } else { | ||
| 1186 | out_copied = 0; | ||
| 1187 | output_size = output_bytes; | ||
| 1188 | retval = sst_prepare_output_buffers(str_info, dbufs, | ||
| 1189 | &output_index, output_size, &out_copied); | ||
| 1190 | if (retval) { | ||
| 1191 | pr_err("prep out buff fail\n"); | ||
| 1192 | goto finish; | ||
| 1193 | } | ||
| 1194 | if (str_info->ops != STREAM_OPS_PLAYBACK_DRM) { | ||
| 1195 | if (in_copied != input_bytes) { | ||
| 1196 | int bytes_left = in_copied - | ||
| 1197 | input_bytes; | ||
| 1198 | pr_debug("bytes %d\n", | ||
| 1199 | bytes_left); | ||
| 1200 | if (new_entry_flag == true) | ||
| 1201 | input_index--; | ||
| 1202 | while (bytes_left) { | ||
| 1203 | struct snd_sst_buffs *ibufs; | ||
| 1204 | struct snd_sst_buff_entry | ||
| 1205 | *buff_entry; | ||
| 1206 | unsigned int size_sent; | ||
| 1207 | |||
| 1208 | ibufs = dbufs->ibufs; | ||
| 1209 | buff_entry = | ||
| 1210 | &ibufs->buff_entry[input_index]; | ||
| 1211 | size_sent = buff_entry->size -\ | ||
| 1212 | input_index_valid_size; | ||
| 1213 | if (bytes_left == size_sent) { | ||
| 1214 | bytes_left = 0; | ||
| 1215 | } else if (bytes_left < | ||
| 1216 | size_sent) { | ||
| 1217 | buff_entry->buffer += | ||
| 1218 | (size_sent - | ||
| 1219 | bytes_left); | ||
| 1220 | buff_entry->size -= | ||
| 1221 | (size_sent - | ||
| 1222 | bytes_left); | ||
| 1223 | bytes_left = 0; | ||
| 1224 | } else { | ||
| 1225 | bytes_left -= size_sent; | ||
| 1226 | input_index--; | ||
| 1227 | input_index_valid_size = | ||
| 1228 | 0; | ||
| 1229 | } | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | } | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | total_output += out_copied; | ||
| 1236 | if (str_info->decode_osize != out_copied) { | ||
| 1237 | str_info->decode_osize -= out_copied; | ||
| 1238 | pr_debug("output size modified = %d\n", | ||
| 1239 | str_info->decode_osize); | ||
| 1240 | } | ||
| 1241 | } | ||
| 1242 | total_input += input_bytes; | ||
| 1243 | |||
| 1244 | if (str_info->ops == STREAM_OPS_PLAYBACK_DRM) { | ||
| 1245 | if (total_input == cum_input_given) | ||
| 1246 | copy_in_done = true; | ||
| 1247 | copy_out_done = true; | ||
| 1248 | |||
| 1249 | } else { | ||
| 1250 | if (total_output == cum_output_given) { | ||
| 1251 | copy_out_done = true; | ||
| 1252 | pr_debug("all o/p cpy done\n"); | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | if (total_input == cum_input_given) { | ||
| 1256 | copy_in_done = true; | ||
| 1257 | pr_debug("all i/p cpy done\n"); | ||
| 1258 | } | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | pr_debug("copy_out = %d, copy_in = %d\n", | ||
| 1262 | copy_out_done, copy_in_done); | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | finish: | ||
| 1266 | dbufs->input_bytes_consumed = total_input; | ||
| 1267 | dbufs->output_bytes_produced = total_output; | ||
| 1268 | str_info->status = str_info->prev; | ||
| 1269 | str_info->prev = STREAM_DECODE; | ||
| 1270 | kfree(str_info->decode_ibuf); | ||
| 1271 | str_info->decode_ibuf = NULL; | ||
| 1272 | return retval; | ||
| 1273 | } | ||
diff --git a/drivers/staging/intel_sst/intelmid.c b/drivers/staging/intel_sst/intelmid.c new file mode 100644 index 00000000000..25656ad2802 --- /dev/null +++ b/drivers/staging/intel_sst/intelmid.c | |||
| @@ -0,0 +1,1021 @@ | |||
| 1 | /* | ||
| 2 | * intelmid.c - Intel Sound card driver for MID | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Harsha Priya <priya.harsha@intel.com> | ||
| 6 | * Vinod Koul <vinod.koul@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * ALSA driver for Intel MID sound card chipset | ||
| 26 | */ | ||
| 27 | |||
| 28 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 29 | |||
| 30 | #include <linux/slab.h> | ||
| 31 | #include <linux/io.h> | ||
| 32 | #include <linux/platform_device.h> | ||
| 33 | #include <linux/interrupt.h> | ||
| 34 | #include <linux/sched.h> | ||
| 35 | #include <linux/firmware.h> | ||
| 36 | #include <linux/input.h> | ||
| 37 | #include <sound/control.h> | ||
| 38 | #include <asm/mrst.h> | ||
| 39 | #include <sound/pcm.h> | ||
| 40 | #include <sound/jack.h> | ||
| 41 | #include <sound/pcm_params.h> | ||
| 42 | #include <sound/initval.h> | ||
| 43 | #include <linux/gpio.h> | ||
| 44 | #include "intel_sst.h" | ||
| 45 | #include "intel_sst_ioctl.h" | ||
| 46 | #include "intel_sst_fw_ipc.h" | ||
| 47 | #include "intel_sst_common.h" | ||
| 48 | #include "intelmid_snd_control.h" | ||
| 49 | #include "intelmid_adc_control.h" | ||
| 50 | #include "intelmid.h" | ||
| 51 | |||
| 52 | MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); | ||
| 53 | MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); | ||
| 54 | MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>"); | ||
| 55 | MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>"); | ||
| 56 | MODULE_DESCRIPTION("Intel MAD Sound card driver"); | ||
| 57 | MODULE_LICENSE("GPL v2"); | ||
| 58 | MODULE_SUPPORTED_DEVICE("{Intel,Intel_MAD}"); | ||
| 59 | |||
| 60 | |||
| 61 | static int card_index = SNDRV_DEFAULT_IDX1;/* Index 0-MAX */ | ||
| 62 | static char *card_id = SNDRV_DEFAULT_STR1; /* ID for this card */ | ||
| 63 | |||
| 64 | module_param(card_index, int, 0444); | ||
| 65 | MODULE_PARM_DESC(card_index, "Index value for INTELMAD soundcard."); | ||
| 66 | module_param(card_id, charp, 0444); | ||
| 67 | MODULE_PARM_DESC(card_id, "ID string for INTELMAD soundcard."); | ||
| 68 | |||
| 69 | int sst_card_vendor_id; | ||
| 70 | int intelmid_audio_interrupt_enable;/*checkpatch fix*/ | ||
| 71 | struct snd_intelmad *intelmad_drv; | ||
| 72 | |||
| 73 | #define INFO(_cpu_id, _irq_cache, _size) \ | ||
| 74 | ((kernel_ulong_t)&(struct snd_intelmad_probe_info) { \ | ||
| 75 | .cpu_id = (_cpu_id), \ | ||
| 76 | .irq_cache = (_irq_cache), \ | ||
| 77 | .size = (_size), \ | ||
| 78 | }) | ||
| 79 | /* Data path functionalities */ | ||
| 80 | static struct snd_pcm_hardware snd_intelmad_stream = { | ||
| 81 | .info = (SNDRV_PCM_INFO_INTERLEAVED | | ||
| 82 | SNDRV_PCM_INFO_DOUBLE | | ||
| 83 | SNDRV_PCM_INFO_PAUSE | | ||
| 84 | SNDRV_PCM_INFO_RESUME | | ||
| 85 | SNDRV_PCM_INFO_MMAP| | ||
| 86 | SNDRV_PCM_INFO_MMAP_VALID | | ||
| 87 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
| 88 | SNDRV_PCM_INFO_SYNC_START), | ||
| 89 | .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | | ||
| 90 | SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | | ||
| 91 | SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), | ||
| 92 | .rates = (SNDRV_PCM_RATE_8000| | ||
| 93 | SNDRV_PCM_RATE_44100 | | ||
| 94 | SNDRV_PCM_RATE_48000), | ||
| 95 | .rate_min = MIN_RATE, | ||
| 96 | |||
| 97 | .rate_max = MAX_RATE, | ||
| 98 | .channels_min = MIN_CHANNEL, | ||
| 99 | .channels_max = MAX_CHANNEL_AMIC, | ||
| 100 | .buffer_bytes_max = MAX_BUFFER, | ||
| 101 | .period_bytes_min = MIN_PERIOD_BYTES, | ||
| 102 | .period_bytes_max = MAX_PERIOD_BYTES, | ||
| 103 | .periods_min = MIN_PERIODS, | ||
| 104 | .periods_max = MAX_PERIODS, | ||
| 105 | .fifo_size = FIFO_SIZE, | ||
| 106 | }; | ||
| 107 | |||
| 108 | |||
| 109 | /** | ||
| 110 | * snd_intelmad_pcm_trigger - stream activities are handled here | ||
| 111 | * | ||
| 112 | * @substream:substream for which the stream function is called | ||
| 113 | * @cmd:the stream commamd that requested from upper layer | ||
| 114 | * | ||
| 115 | * This function is called whenever an a stream activity is invoked | ||
| 116 | */ | ||
| 117 | static int snd_intelmad_pcm_trigger(struct snd_pcm_substream *substream, | ||
| 118 | int cmd) | ||
| 119 | { | ||
| 120 | int ret_val = 0, str_id; | ||
| 121 | struct snd_intelmad *intelmaddata; | ||
| 122 | struct mad_stream_pvt *stream; | ||
| 123 | struct intel_sst_pcm_control *sst_ops; | ||
| 124 | |||
| 125 | WARN_ON(!substream); | ||
| 126 | |||
| 127 | intelmaddata = snd_pcm_substream_chip(substream); | ||
| 128 | stream = substream->runtime->private_data; | ||
| 129 | |||
| 130 | WARN_ON(!intelmaddata->sstdrv_ops); | ||
| 131 | WARN_ON(!intelmaddata->sstdrv_ops->scard_ops); | ||
| 132 | sst_ops = intelmaddata->sstdrv_ops->pcm_control; | ||
| 133 | str_id = stream->stream_info.str_id; | ||
| 134 | |||
| 135 | switch (cmd) { | ||
| 136 | case SNDRV_PCM_TRIGGER_START: | ||
| 137 | pr_debug("Trigger Start\n"); | ||
| 138 | ret_val = sst_ops->device_control(SST_SND_START, &str_id); | ||
| 139 | if (ret_val) | ||
| 140 | return ret_val; | ||
| 141 | stream->stream_status = RUNNING; | ||
| 142 | stream->substream = substream; | ||
| 143 | break; | ||
| 144 | case SNDRV_PCM_TRIGGER_STOP: | ||
| 145 | pr_debug("in stop\n"); | ||
| 146 | ret_val = sst_ops->device_control(SST_SND_DROP, &str_id); | ||
| 147 | if (ret_val) | ||
| 148 | return ret_val; | ||
| 149 | stream->stream_status = DROPPED; | ||
| 150 | break; | ||
| 151 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 152 | pr_debug("in pause\n"); | ||
| 153 | ret_val = sst_ops->device_control(SST_SND_PAUSE, &str_id); | ||
| 154 | if (ret_val) | ||
| 155 | return ret_val; | ||
| 156 | stream->stream_status = PAUSED; | ||
| 157 | break; | ||
| 158 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 159 | pr_debug("in pause release\n"); | ||
| 160 | ret_val = sst_ops->device_control(SST_SND_RESUME, &str_id); | ||
| 161 | if (ret_val) | ||
| 162 | return ret_val; | ||
| 163 | stream->stream_status = RUNNING; | ||
| 164 | break; | ||
| 165 | default: | ||
| 166 | return -EINVAL; | ||
| 167 | } | ||
| 168 | return ret_val; | ||
| 169 | } | ||
| 170 | |||
| 171 | /** | ||
| 172 | * snd_intelmad_pcm_prepare- internal preparation before starting a stream | ||
| 173 | * | ||
| 174 | * @substream: substream for which the function is called | ||
| 175 | * | ||
| 176 | * This function is called when a stream is started for internal preparation. | ||
| 177 | */ | ||
| 178 | static int snd_intelmad_pcm_prepare(struct snd_pcm_substream *substream) | ||
| 179 | { | ||
| 180 | struct mad_stream_pvt *stream; | ||
| 181 | int ret_val = 0; | ||
| 182 | struct snd_intelmad *intelmaddata; | ||
| 183 | |||
| 184 | pr_debug("pcm_prepare called\n"); | ||
| 185 | |||
| 186 | WARN_ON(!substream); | ||
| 187 | stream = substream->runtime->private_data; | ||
| 188 | intelmaddata = snd_pcm_substream_chip(substream); | ||
| 189 | pr_debug("pb cnt = %d cap cnt = %d\n",\ | ||
| 190 | intelmaddata->playback_cnt, | ||
| 191 | intelmaddata->capture_cnt); | ||
| 192 | |||
| 193 | if (stream->stream_info.str_id) { | ||
| 194 | pr_debug("Prepare called for already set stream\n"); | ||
| 195 | ret_val = intelmaddata->sstdrv_ops->pcm_control->device_control( | ||
| 196 | SST_SND_DROP, &stream->stream_info.str_id); | ||
| 197 | return ret_val; | ||
| 198 | } | ||
| 199 | |||
| 200 | ret_val = snd_intelmad_alloc_stream(substream); | ||
| 201 | if (ret_val < 0) | ||
| 202 | return ret_val; | ||
| 203 | stream->dbg_cum_bytes = 0; | ||
| 204 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 205 | intelmaddata->playback_cnt++; | ||
| 206 | else | ||
| 207 | intelmaddata->capture_cnt++; | ||
| 208 | /* return back the stream id */ | ||
| 209 | snprintf(substream->pcm->id, sizeof(substream->pcm->id), | ||
| 210 | "%d", stream->stream_info.str_id); | ||
| 211 | pr_debug("stream id to user = %s\n", | ||
| 212 | substream->pcm->id); | ||
| 213 | |||
| 214 | ret_val = snd_intelmad_init_stream(substream); | ||
| 215 | if (ret_val) | ||
| 216 | return ret_val; | ||
| 217 | substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER; | ||
| 218 | return ret_val; | ||
| 219 | } | ||
| 220 | |||
| 221 | static int snd_intelmad_hw_params(struct snd_pcm_substream *substream, | ||
| 222 | struct snd_pcm_hw_params *hw_params) | ||
| 223 | { | ||
| 224 | int ret_val; | ||
| 225 | |||
| 226 | pr_debug("snd_intelmad_hw_params called\n"); | ||
| 227 | ret_val = snd_pcm_lib_malloc_pages(substream, | ||
| 228 | params_buffer_bytes(hw_params)); | ||
| 229 | memset(substream->runtime->dma_area, 0, | ||
| 230 | params_buffer_bytes(hw_params)); | ||
| 231 | |||
| 232 | return ret_val; | ||
| 233 | } | ||
| 234 | |||
| 235 | static int snd_intelmad_hw_free(struct snd_pcm_substream *substream) | ||
| 236 | { | ||
| 237 | pr_debug("snd_intelmad_hw_free called\n"); | ||
| 238 | return snd_pcm_lib_free_pages(substream); | ||
| 239 | } | ||
| 240 | |||
| 241 | /** | ||
| 242 | * snd_intelmad_pcm_pointer- to send the current buffer pointer processed by hw | ||
| 243 | * | ||
| 244 | * @substream: substream for which the function is called | ||
| 245 | * | ||
| 246 | * This function is called by ALSA framework to get the current hw buffer ptr | ||
| 247 | * when a period is elapsed | ||
| 248 | */ | ||
| 249 | static snd_pcm_uframes_t snd_intelmad_pcm_pointer | ||
| 250 | (struct snd_pcm_substream *substream) | ||
| 251 | { | ||
| 252 | /* struct snd_pcm_runtime *runtime = substream->runtime; */ | ||
| 253 | struct mad_stream_pvt *stream; | ||
| 254 | struct snd_intelmad *intelmaddata; | ||
| 255 | int ret_val; | ||
| 256 | |||
| 257 | WARN_ON(!substream); | ||
| 258 | |||
| 259 | intelmaddata = snd_pcm_substream_chip(substream); | ||
| 260 | stream = substream->runtime->private_data; | ||
| 261 | if (stream->stream_status == INIT) | ||
| 262 | return 0; | ||
| 263 | |||
| 264 | ret_val = intelmaddata->sstdrv_ops->pcm_control->device_control( | ||
| 265 | SST_SND_BUFFER_POINTER, &stream->stream_info); | ||
| 266 | if (ret_val) { | ||
| 267 | pr_err("error code = 0x%x\n", ret_val); | ||
| 268 | return ret_val; | ||
| 269 | } | ||
| 270 | pr_debug("samples reported out 0x%llx\n", | ||
| 271 | stream->stream_info.buffer_ptr); | ||
| 272 | pr_debug("Frame bits:: %d period_count :: %d\n", | ||
| 273 | (int)substream->runtime->frame_bits, | ||
| 274 | (int)substream->runtime->period_size); | ||
| 275 | |||
| 276 | return stream->stream_info.buffer_ptr; | ||
| 277 | |||
| 278 | } | ||
| 279 | |||
| 280 | /** | ||
| 281 | * snd_intelmad_close- to free parameteres when stream is stopped | ||
| 282 | * | ||
| 283 | * @substream: substream for which the function is called | ||
| 284 | * | ||
| 285 | * This function is called by ALSA framework when stream is stopped | ||
| 286 | */ | ||
| 287 | static int snd_intelmad_close(struct snd_pcm_substream *substream) | ||
| 288 | { | ||
| 289 | struct snd_intelmad *intelmaddata; | ||
| 290 | struct mad_stream_pvt *stream; | ||
| 291 | int ret_val = 0, str_id; | ||
| 292 | |||
| 293 | WARN_ON(!substream); | ||
| 294 | |||
| 295 | stream = substream->runtime->private_data; | ||
| 296 | str_id = stream->stream_info.str_id; | ||
| 297 | |||
| 298 | pr_debug("sst: snd_intelmad_close called for %d\n", str_id); | ||
| 299 | intelmaddata = snd_pcm_substream_chip(substream); | ||
| 300 | |||
| 301 | pr_debug("str id = %d\n", stream->stream_info.str_id); | ||
| 302 | if (stream->stream_info.str_id) { | ||
| 303 | /* SST API to actually stop/free the stream */ | ||
| 304 | ret_val = intelmaddata->sstdrv_ops->pcm_control->close(str_id); | ||
| 305 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
| 306 | intelmaddata->playback_cnt--; | ||
| 307 | else | ||
| 308 | intelmaddata->capture_cnt--; | ||
| 309 | } | ||
| 310 | pr_debug("snd_intelmad_close : pb cnt = %d cap cnt = %d\n", | ||
| 311 | intelmaddata->playback_cnt, intelmaddata->capture_cnt); | ||
| 312 | kfree(substream->runtime->private_data); | ||
| 313 | return ret_val; | ||
| 314 | } | ||
| 315 | |||
| 316 | /** | ||
| 317 | * snd_intelmad_open- to set runtime parameters during stream start | ||
| 318 | * | ||
| 319 | * @substream: substream for which the function is called | ||
| 320 | * @type: audio device type | ||
| 321 | * | ||
| 322 | * This function is called by ALSA framework when stream is started | ||
| 323 | */ | ||
| 324 | static int snd_intelmad_open(struct snd_pcm_substream *substream, | ||
| 325 | enum snd_sst_audio_device_type type) | ||
| 326 | { | ||
| 327 | struct snd_intelmad *intelmaddata; | ||
| 328 | struct snd_pcm_runtime *runtime; | ||
| 329 | struct mad_stream_pvt *stream; | ||
| 330 | |||
| 331 | WARN_ON(!substream); | ||
| 332 | |||
| 333 | pr_debug("snd_intelmad_open called\n"); | ||
| 334 | |||
| 335 | intelmaddata = snd_pcm_substream_chip(substream); | ||
| 336 | runtime = substream->runtime; | ||
| 337 | /* set the runtime hw parameter with local snd_pcm_hardware struct */ | ||
| 338 | runtime->hw = snd_intelmad_stream; | ||
| 339 | if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) { | ||
| 340 | /* | ||
| 341 | * MRST firmware currently denies stereo recording requests. | ||
| 342 | */ | ||
| 343 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
| 344 | runtime->hw.formats = (SNDRV_PCM_FMTBIT_S16 | | ||
| 345 | SNDRV_PCM_FMTBIT_U16); | ||
| 346 | runtime->hw.channels_max = 1; | ||
| 347 | } | ||
| 348 | } | ||
| 349 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { | ||
| 350 | runtime->hw = snd_intelmad_stream; | ||
| 351 | runtime->hw.rates = SNDRV_PCM_RATE_48000; | ||
| 352 | runtime->hw.rate_min = MAX_RATE; | ||
| 353 | runtime->hw.formats = (SNDRV_PCM_FMTBIT_S24 | | ||
| 354 | SNDRV_PCM_FMTBIT_U24); | ||
| 355 | if (intelmaddata->sstdrv_ops->scard_ops->input_dev_id == AMIC) | ||
| 356 | runtime->hw.channels_max = MAX_CHANNEL_AMIC; | ||
| 357 | else | ||
| 358 | runtime->hw.channels_max = MAX_CHANNEL_DMIC; | ||
| 359 | |||
| 360 | } | ||
| 361 | /* setup the internal datastruture stream pointers based on it being | ||
| 362 | playback or capture stream */ | ||
| 363 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); | ||
| 364 | if (!stream) | ||
| 365 | return -ENOMEM; | ||
| 366 | stream->stream_info.str_id = 0; | ||
| 367 | stream->device = type; | ||
| 368 | stream->stream_status = INIT; | ||
| 369 | runtime->private_data = stream; | ||
| 370 | return snd_pcm_hw_constraint_integer(runtime, | ||
| 371 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
| 372 | } | ||
| 373 | |||
| 374 | static int snd_intelmad_headset_open(struct snd_pcm_substream *substream) | ||
| 375 | { | ||
| 376 | return snd_intelmad_open(substream, SND_SST_DEVICE_HEADSET); | ||
| 377 | } | ||
| 378 | |||
| 379 | static int snd_intelmad_ihf_open(struct snd_pcm_substream *substream) | ||
| 380 | { | ||
| 381 | return snd_intelmad_open(substream, SND_SST_DEVICE_IHF); | ||
| 382 | } | ||
| 383 | |||
| 384 | static int snd_intelmad_vibra_open(struct snd_pcm_substream *substream) | ||
| 385 | { | ||
| 386 | return snd_intelmad_open(substream, SND_SST_DEVICE_VIBRA); | ||
| 387 | } | ||
| 388 | |||
| 389 | static int snd_intelmad_haptic_open(struct snd_pcm_substream *substream) | ||
| 390 | { | ||
| 391 | return snd_intelmad_open(substream, SND_SST_DEVICE_HAPTIC); | ||
| 392 | } | ||
| 393 | |||
| 394 | static struct snd_pcm_ops snd_intelmad_headset_ops = { | ||
| 395 | .open = snd_intelmad_headset_open, | ||
| 396 | .close = snd_intelmad_close, | ||
| 397 | .ioctl = snd_pcm_lib_ioctl, | ||
| 398 | .hw_params = snd_intelmad_hw_params, | ||
| 399 | .hw_free = snd_intelmad_hw_free, | ||
| 400 | .prepare = snd_intelmad_pcm_prepare, | ||
| 401 | .trigger = snd_intelmad_pcm_trigger, | ||
| 402 | .pointer = snd_intelmad_pcm_pointer, | ||
| 403 | }; | ||
| 404 | |||
| 405 | static struct snd_pcm_ops snd_intelmad_ihf_ops = { | ||
| 406 | .open = snd_intelmad_ihf_open, | ||
| 407 | .close = snd_intelmad_close, | ||
| 408 | .ioctl = snd_pcm_lib_ioctl, | ||
| 409 | .hw_params = snd_intelmad_hw_params, | ||
| 410 | .hw_free = snd_intelmad_hw_free, | ||
| 411 | .prepare = snd_intelmad_pcm_prepare, | ||
| 412 | .trigger = snd_intelmad_pcm_trigger, | ||
| 413 | .pointer = snd_intelmad_pcm_pointer, | ||
| 414 | }; | ||
| 415 | |||
| 416 | static struct snd_pcm_ops snd_intelmad_vibra_ops = { | ||
| 417 | .open = snd_intelmad_vibra_open, | ||
| 418 | .close = snd_intelmad_close, | ||
| 419 | .ioctl = snd_pcm_lib_ioctl, | ||
| 420 | .hw_params = snd_intelmad_hw_params, | ||
| 421 | .hw_free = snd_intelmad_hw_free, | ||
| 422 | .prepare = snd_intelmad_pcm_prepare, | ||
| 423 | .trigger = snd_intelmad_pcm_trigger, | ||
| 424 | .pointer = snd_intelmad_pcm_pointer, | ||
| 425 | }; | ||
| 426 | |||
| 427 | static struct snd_pcm_ops snd_intelmad_haptic_ops = { | ||
| 428 | .open = snd_intelmad_haptic_open, | ||
| 429 | .close = snd_intelmad_close, | ||
| 430 | .ioctl = snd_pcm_lib_ioctl, | ||
| 431 | .hw_params = snd_intelmad_hw_params, | ||
| 432 | .hw_free = snd_intelmad_hw_free, | ||
| 433 | .prepare = snd_intelmad_pcm_prepare, | ||
| 434 | .trigger = snd_intelmad_pcm_trigger, | ||
| 435 | .pointer = snd_intelmad_pcm_pointer, | ||
| 436 | }; | ||
| 437 | |||
| 438 | static struct snd_pcm_ops snd_intelmad_capture_ops = { | ||
| 439 | .open = snd_intelmad_headset_open, | ||
| 440 | .close = snd_intelmad_close, | ||
| 441 | .ioctl = snd_pcm_lib_ioctl, | ||
| 442 | .hw_params = snd_intelmad_hw_params, | ||
| 443 | .hw_free = snd_intelmad_hw_free, | ||
| 444 | .prepare = snd_intelmad_pcm_prepare, | ||
| 445 | .trigger = snd_intelmad_pcm_trigger, | ||
| 446 | .pointer = snd_intelmad_pcm_pointer, | ||
| 447 | }; | ||
| 448 | |||
| 449 | int intelmad_get_mic_bias(void) | ||
| 450 | { | ||
| 451 | struct snd_pmic_ops *pmic_ops; | ||
| 452 | |||
| 453 | if (!intelmad_drv || !intelmad_drv->sstdrv_ops) | ||
| 454 | return -ENODEV; | ||
| 455 | pmic_ops = intelmad_drv->sstdrv_ops->scard_ops; | ||
| 456 | if (pmic_ops && pmic_ops->pmic_get_mic_bias) | ||
| 457 | return pmic_ops->pmic_get_mic_bias(intelmad_drv); | ||
| 458 | else | ||
| 459 | return -ENODEV; | ||
| 460 | } | ||
| 461 | EXPORT_SYMBOL_GPL(intelmad_get_mic_bias); | ||
| 462 | |||
| 463 | int intelmad_set_headset_state(int state) | ||
| 464 | { | ||
| 465 | struct snd_pmic_ops *pmic_ops; | ||
| 466 | |||
| 467 | if (!intelmad_drv || !intelmad_drv->sstdrv_ops) | ||
| 468 | return -ENODEV; | ||
| 469 | pmic_ops = intelmad_drv->sstdrv_ops->scard_ops; | ||
| 470 | if (pmic_ops && pmic_ops->pmic_set_headset_state) | ||
| 471 | return pmic_ops->pmic_set_headset_state(state); | ||
| 472 | else | ||
| 473 | return -ENODEV; | ||
| 474 | } | ||
| 475 | EXPORT_SYMBOL_GPL(intelmad_set_headset_state); | ||
| 476 | |||
| 477 | void sst_process_mad_jack_detection(struct work_struct *work) | ||
| 478 | { | ||
| 479 | u8 interrupt_status; | ||
| 480 | struct mad_jack_msg_wq *mad_jack_detect = | ||
| 481 | container_of(work, struct mad_jack_msg_wq, wq); | ||
| 482 | |||
| 483 | struct snd_intelmad *intelmaddata = | ||
| 484 | mad_jack_detect->intelmaddata; | ||
| 485 | |||
| 486 | if (!intelmaddata) | ||
| 487 | return; | ||
| 488 | |||
| 489 | interrupt_status = mad_jack_detect->intsts; | ||
| 490 | if (intelmaddata->sstdrv_ops && intelmaddata->sstdrv_ops->scard_ops | ||
| 491 | && intelmaddata->sstdrv_ops->scard_ops->pmic_irq_cb) { | ||
| 492 | intelmaddata->sstdrv_ops->scard_ops->pmic_irq_cb( | ||
| 493 | (void *)intelmaddata, interrupt_status); | ||
| 494 | intelmaddata->sstdrv_ops->scard_ops->pmic_jack_enable(); | ||
| 495 | } | ||
| 496 | kfree(mad_jack_detect); | ||
| 497 | } | ||
| 498 | /** | ||
| 499 | * snd_intelmad_intr_handler- interrupt handler | ||
| 500 | * | ||
| 501 | * @irq : irq number of the interrupt received | ||
| 502 | * @dev: device context | ||
| 503 | * | ||
| 504 | * This function is called when an interrupt is raised at the sound card | ||
| 505 | */ | ||
| 506 | static irqreturn_t snd_intelmad_intr_handler(int irq, void *dev) | ||
| 507 | { | ||
| 508 | struct snd_intelmad *intelmaddata = | ||
| 509 | (struct snd_intelmad *)dev; | ||
| 510 | u8 interrupt_status; | ||
| 511 | struct mad_jack_msg_wq *mad_jack_msg; | ||
| 512 | memcpy_fromio(&interrupt_status, | ||
| 513 | ((void *)(intelmaddata->int_base)), | ||
| 514 | sizeof(u8)); | ||
| 515 | |||
| 516 | mad_jack_msg = kzalloc(sizeof(*mad_jack_msg), GFP_ATOMIC); | ||
| 517 | mad_jack_msg->intsts = interrupt_status; | ||
| 518 | mad_jack_msg->intelmaddata = intelmaddata; | ||
| 519 | INIT_WORK(&mad_jack_msg->wq, sst_process_mad_jack_detection); | ||
| 520 | queue_work(intelmaddata->mad_jack_wq, &mad_jack_msg->wq); | ||
| 521 | |||
| 522 | return IRQ_HANDLED; | ||
| 523 | } | ||
| 524 | |||
| 525 | void sst_mad_send_jack_report(struct snd_jack *jack, | ||
| 526 | int buttonpressevent , int status) | ||
| 527 | { | ||
| 528 | |||
| 529 | if (!jack) { | ||
| 530 | pr_debug("MAD error jack empty\n"); | ||
| 531 | |||
| 532 | } else { | ||
| 533 | snd_jack_report(jack, status); | ||
| 534 | /* button pressed and released */ | ||
| 535 | if (buttonpressevent) | ||
| 536 | snd_jack_report(jack, 0); | ||
| 537 | pr_debug("MAD sending jack report Done !!!\n"); | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | static int __devinit snd_intelmad_register_irq( | ||
| 542 | struct snd_intelmad *intelmaddata, unsigned int regbase, | ||
| 543 | unsigned int regsize) | ||
| 544 | { | ||
| 545 | int ret_val; | ||
| 546 | char *drv_name; | ||
| 547 | |||
| 548 | pr_debug("irq reg regbase 0x%x, regsize 0x%x\n", | ||
| 549 | regbase, regsize); | ||
| 550 | intelmaddata->int_base = ioremap_nocache(regbase, regsize); | ||
| 551 | if (!intelmaddata->int_base) | ||
| 552 | pr_err("Mapping of cache failed\n"); | ||
| 553 | pr_debug("irq = 0x%x\n", intelmaddata->irq); | ||
| 554 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) | ||
| 555 | drv_name = DRIVER_NAME_MFLD; | ||
| 556 | else | ||
| 557 | drv_name = DRIVER_NAME_MRST; | ||
| 558 | ret_val = request_irq(intelmaddata->irq, | ||
| 559 | snd_intelmad_intr_handler, | ||
| 560 | IRQF_SHARED, drv_name, | ||
| 561 | intelmaddata); | ||
| 562 | if (ret_val) | ||
| 563 | pr_err("cannot register IRQ\n"); | ||
| 564 | return ret_val; | ||
| 565 | } | ||
| 566 | |||
| 567 | static int __devinit snd_intelmad_sst_register( | ||
| 568 | struct snd_intelmad *intelmaddata) | ||
| 569 | { | ||
| 570 | int ret_val = 0; | ||
| 571 | struct snd_pmic_ops *intelmad_vendor_ops[MAX_VENDORS] = { | ||
| 572 | &snd_pmic_ops_fs, | ||
| 573 | &snd_pmic_ops_mx, | ||
| 574 | &snd_pmic_ops_nc, | ||
| 575 | &snd_msic_ops | ||
| 576 | }; | ||
| 577 | |||
| 578 | struct sc_reg_access vendor_addr = {0x00, 0x00, 0x00}; | ||
| 579 | |||
| 580 | if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) { | ||
| 581 | ret_val = sst_sc_reg_access(&vendor_addr, PMIC_READ, 1); | ||
| 582 | if (ret_val) | ||
| 583 | return ret_val; | ||
| 584 | sst_card_vendor_id = (vendor_addr.value & (MASK2|MASK1|MASK0)); | ||
| 585 | pr_debug("original n extrated vendor id = 0x%x %d\n", | ||
| 586 | vendor_addr.value, sst_card_vendor_id); | ||
| 587 | if (sst_card_vendor_id < 0 || sst_card_vendor_id > 2) { | ||
| 588 | pr_err("vendor card not supported!!\n"); | ||
| 589 | return -EIO; | ||
| 590 | } | ||
| 591 | } else | ||
| 592 | sst_card_vendor_id = 0x3; | ||
| 593 | |||
| 594 | intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES; | ||
| 595 | intelmaddata->sstdrv_ops->vendor_id = sst_card_vendor_id; | ||
| 596 | BUG_ON(!intelmad_vendor_ops[sst_card_vendor_id]); | ||
| 597 | intelmaddata->sstdrv_ops->scard_ops = | ||
| 598 | intelmad_vendor_ops[sst_card_vendor_id]; | ||
| 599 | |||
| 600 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { | ||
| 601 | intelmaddata->sstdrv_ops->scard_ops->pb_on = 0; | ||
| 602 | intelmaddata->sstdrv_ops->scard_ops->cap_on = 0; | ||
| 603 | intelmaddata->sstdrv_ops->scard_ops->input_dev_id = DMIC; | ||
| 604 | intelmaddata->sstdrv_ops->scard_ops->output_dev_id = | ||
| 605 | STEREO_HEADPHONE; | ||
| 606 | intelmaddata->sstdrv_ops->scard_ops->lineout_dev_id = NONE; | ||
| 607 | } | ||
| 608 | |||
| 609 | /* registering with SST driver to get access to SST APIs to use */ | ||
| 610 | ret_val = register_sst_card(intelmaddata->sstdrv_ops); | ||
| 611 | if (ret_val) { | ||
| 612 | pr_err("sst card registration failed\n"); | ||
| 613 | return ret_val; | ||
| 614 | } | ||
| 615 | sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id; | ||
| 616 | intelmaddata->pmic_status = PMIC_UNINIT; | ||
| 617 | return ret_val; | ||
| 618 | } | ||
| 619 | |||
| 620 | static void snd_intelmad_page_free(struct snd_pcm *pcm) | ||
| 621 | { | ||
| 622 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
| 623 | } | ||
| 624 | /* Driver Init/exit functionalities */ | ||
| 625 | /** | ||
| 626 | * snd_intelmad_pcm_new - to setup pcm for the card | ||
| 627 | * | ||
| 628 | * @card: pointer to the sound card structure | ||
| 629 | * @intelmaddata: pointer to internal context | ||
| 630 | * @pb: playback count for this card | ||
| 631 | * @cap: capture count for this card | ||
| 632 | * @index: device index | ||
| 633 | * | ||
| 634 | * This function is called from probe function to set up pcm params | ||
| 635 | * and functions | ||
| 636 | */ | ||
| 637 | static int __devinit snd_intelmad_pcm_new(struct snd_card *card, | ||
| 638 | struct snd_intelmad *intelmaddata, | ||
| 639 | unsigned int pb, unsigned int cap, unsigned int index) | ||
| 640 | { | ||
| 641 | int ret_val = 0; | ||
| 642 | struct snd_pcm *pcm; | ||
| 643 | char name[32] = INTEL_MAD; | ||
| 644 | struct snd_pcm_ops *pb_ops = NULL, *cap_ops = NULL; | ||
| 645 | |||
| 646 | pr_debug("called for pb %d, cp %d, idx %d\n", pb, cap, index); | ||
| 647 | ret_val = snd_pcm_new(card, name, index, pb, cap, &pcm); | ||
| 648 | if (ret_val) | ||
| 649 | return ret_val; | ||
| 650 | /* setup the ops for playback and capture streams */ | ||
| 651 | switch (index) { | ||
| 652 | case 0: | ||
| 653 | pb_ops = &snd_intelmad_headset_ops; | ||
| 654 | cap_ops = &snd_intelmad_capture_ops; | ||
| 655 | break; | ||
| 656 | case 1: | ||
| 657 | pb_ops = &snd_intelmad_ihf_ops; | ||
| 658 | cap_ops = &snd_intelmad_capture_ops; | ||
| 659 | break; | ||
| 660 | case 2: | ||
| 661 | pb_ops = &snd_intelmad_vibra_ops; | ||
| 662 | cap_ops = &snd_intelmad_capture_ops; | ||
| 663 | break; | ||
| 664 | case 3: | ||
| 665 | pb_ops = &snd_intelmad_haptic_ops; | ||
| 666 | cap_ops = &snd_intelmad_capture_ops; | ||
| 667 | break; | ||
| 668 | } | ||
| 669 | if (pb) | ||
| 670 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, pb_ops); | ||
| 671 | if (cap) | ||
| 672 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, cap_ops); | ||
| 673 | /* setup private data which can be retrieved when required */ | ||
| 674 | pcm->private_data = intelmaddata; | ||
| 675 | pcm->private_free = snd_intelmad_page_free; | ||
| 676 | pcm->info_flags = 0; | ||
| 677 | strncpy(pcm->name, card->shortname, strlen(card->shortname)); | ||
| 678 | /* allocate dma pages for ALSA stream operations */ | ||
| 679 | snd_pcm_lib_preallocate_pages_for_all(pcm, | ||
| 680 | SNDRV_DMA_TYPE_CONTINUOUS, | ||
| 681 | snd_dma_continuous_data(GFP_KERNEL), | ||
| 682 | MIN_BUFFER, MAX_BUFFER); | ||
| 683 | return ret_val; | ||
| 684 | } | ||
| 685 | |||
| 686 | static int __devinit snd_intelmad_pcm(struct snd_card *card, | ||
| 687 | struct snd_intelmad *intelmaddata) | ||
| 688 | { | ||
| 689 | int ret_val = 0; | ||
| 690 | |||
| 691 | WARN_ON(!card); | ||
| 692 | WARN_ON(!intelmaddata); | ||
| 693 | pr_debug("snd_intelmad_pcm called\n"); | ||
| 694 | ret_val = snd_intelmad_pcm_new(card, intelmaddata, 1, 1, 0); | ||
| 695 | if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) | ||
| 696 | return ret_val; | ||
| 697 | ret_val = snd_intelmad_pcm_new(card, intelmaddata, 1, 0, 1); | ||
| 698 | if (ret_val) | ||
| 699 | return ret_val; | ||
| 700 | ret_val = snd_intelmad_pcm_new(card, intelmaddata, 1, 0, 2); | ||
| 701 | if (ret_val) | ||
| 702 | return ret_val; | ||
| 703 | return snd_intelmad_pcm_new(card, intelmaddata, 1, 0, 3); | ||
| 704 | } | ||
| 705 | |||
| 706 | /** | ||
| 707 | * snd_intelmad_jack- to setup jack settings of the card | ||
| 708 | * | ||
| 709 | * @intelmaddata: pointer to internal context | ||
| 710 | * | ||
| 711 | * This function is called send jack events | ||
| 712 | */ | ||
| 713 | static int snd_intelmad_jack(struct snd_intelmad *intelmaddata) | ||
| 714 | { | ||
| 715 | struct snd_jack *jack; | ||
| 716 | int retval; | ||
| 717 | |||
| 718 | pr_debug("snd_intelmad_jack called\n"); | ||
| 719 | jack = &intelmaddata->jack[0].jack; | ||
| 720 | snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PHONE); | ||
| 721 | retval = snd_jack_new(intelmaddata->card, "Intel(R) MID Audio Jack", | ||
| 722 | SND_JACK_HEADPHONE | SND_JACK_HEADSET | | ||
| 723 | SW_JACK_PHYSICAL_INSERT | SND_JACK_BTN_0 | ||
| 724 | | SND_JACK_BTN_1, &jack); | ||
| 725 | pr_debug("snd_intelmad_jack called\n"); | ||
| 726 | if (retval < 0) | ||
| 727 | return retval; | ||
| 728 | snd_jack_report(jack, 0); | ||
| 729 | |||
| 730 | jack->private_data = jack; | ||
| 731 | intelmaddata->jack[0].jack = *jack; | ||
| 732 | |||
| 733 | return retval; | ||
| 734 | } | ||
| 735 | |||
| 736 | /** | ||
| 737 | * snd_intelmad_mixer- to setup mixer settings of the card | ||
| 738 | * | ||
| 739 | * @intelmaddata: pointer to internal context | ||
| 740 | * | ||
| 741 | * This function is called from probe function to set up mixer controls | ||
| 742 | */ | ||
| 743 | static int __devinit snd_intelmad_mixer(struct snd_intelmad *intelmaddata) | ||
| 744 | { | ||
| 745 | struct snd_card *card; | ||
| 746 | unsigned int idx; | ||
| 747 | int ret_val = 0, max_controls = 0; | ||
| 748 | char *mixername = "IntelMAD Controls"; | ||
| 749 | struct snd_kcontrol_new *controls; | ||
| 750 | |||
| 751 | WARN_ON(!intelmaddata); | ||
| 752 | |||
| 753 | card = intelmaddata->card; | ||
| 754 | strncpy(card->mixername, mixername, sizeof(card->mixername)-1); | ||
| 755 | /* add all widget controls and expose the same */ | ||
| 756 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { | ||
| 757 | max_controls = MAX_CTRL_MFLD; | ||
| 758 | controls = snd_intelmad_controls_mfld; | ||
| 759 | } else { | ||
| 760 | max_controls = MAX_CTRL_MRST; | ||
| 761 | controls = snd_intelmad_controls_mrst; | ||
| 762 | } | ||
| 763 | for (idx = 0; idx < max_controls; idx++) { | ||
| 764 | ret_val = snd_ctl_add(card, | ||
| 765 | snd_ctl_new1(&controls[idx], | ||
| 766 | intelmaddata)); | ||
| 767 | pr_debug("mixer[idx]=%d added\n", idx); | ||
| 768 | if (ret_val) { | ||
| 769 | pr_err("in adding of control index = %d\n", idx); | ||
| 770 | break; | ||
| 771 | } | ||
| 772 | } | ||
| 773 | return ret_val; | ||
| 774 | } | ||
| 775 | |||
| 776 | static int snd_intelmad_dev_free(struct snd_device *device) | ||
| 777 | { | ||
| 778 | struct snd_intelmad *intelmaddata; | ||
| 779 | |||
| 780 | WARN_ON(!device); | ||
| 781 | |||
| 782 | intelmaddata = device->device_data; | ||
| 783 | |||
| 784 | pr_debug("snd_intelmad_dev_free called\n"); | ||
| 785 | unregister_sst_card(intelmaddata->sstdrv_ops); | ||
| 786 | |||
| 787 | /* free allocated memory for internal context */ | ||
| 788 | destroy_workqueue(intelmaddata->mad_jack_wq); | ||
| 789 | device->device_data = NULL; | ||
| 790 | kfree(intelmaddata->sstdrv_ops); | ||
| 791 | kfree(intelmaddata); | ||
| 792 | |||
| 793 | return 0; | ||
| 794 | } | ||
| 795 | |||
| 796 | static int __devinit snd_intelmad_create( | ||
| 797 | struct snd_intelmad *intelmaddata, | ||
| 798 | struct snd_card *card) | ||
| 799 | { | ||
| 800 | int ret_val; | ||
| 801 | static struct snd_device_ops ops = { | ||
| 802 | .dev_free = snd_intelmad_dev_free, | ||
| 803 | }; | ||
| 804 | |||
| 805 | WARN_ON(!intelmaddata); | ||
| 806 | WARN_ON(!card); | ||
| 807 | /* ALSA api to register for the device */ | ||
| 808 | ret_val = snd_device_new(card, SNDRV_DEV_LOWLEVEL, intelmaddata, &ops); | ||
| 809 | return ret_val; | ||
| 810 | } | ||
| 811 | |||
| 812 | /** | ||
| 813 | * snd_intelmad_probe- function registred for init | ||
| 814 | * @pdev : pointer to the device struture | ||
| 815 | * This function is called when the device is initialized | ||
| 816 | */ | ||
| 817 | int __devinit snd_intelmad_probe(struct platform_device *pdev) | ||
| 818 | { | ||
| 819 | struct snd_card *card; | ||
| 820 | int ret_val; | ||
| 821 | struct snd_intelmad *intelmaddata; | ||
| 822 | const struct platform_device_id *id = platform_get_device_id(pdev); | ||
| 823 | struct snd_intelmad_probe_info *info = (void *)id->driver_data; | ||
| 824 | |||
| 825 | pr_debug("probe for %s cpu_id %d\n", pdev->name, info->cpu_id); | ||
| 826 | pr_debug("rq_chache %x of size %x\n", info->irq_cache, info->size); | ||
| 827 | if (!strcmp(pdev->name, DRIVER_NAME_MRST)) | ||
| 828 | pr_debug("detected MRST\n"); | ||
| 829 | else if (!strcmp(pdev->name, DRIVER_NAME_MFLD)) | ||
| 830 | pr_debug("detected MFLD\n"); | ||
| 831 | else { | ||
| 832 | pr_err("detected unknown device abort!!\n"); | ||
| 833 | return -EIO; | ||
| 834 | } | ||
| 835 | if ((info->cpu_id < CPU_CHIP_LINCROFT) || | ||
| 836 | (info->cpu_id > CPU_CHIP_PENWELL)) { | ||
| 837 | pr_err("detected unknown cpu_id abort!!\n"); | ||
| 838 | return -EIO; | ||
| 839 | } | ||
| 840 | /* allocate memory for saving internal context and working */ | ||
| 841 | intelmaddata = kzalloc(sizeof(*intelmaddata), GFP_KERNEL); | ||
| 842 | if (!intelmaddata) { | ||
| 843 | pr_debug("mem alloctn fail\n"); | ||
| 844 | return -ENOMEM; | ||
| 845 | } | ||
| 846 | intelmad_drv = intelmaddata; | ||
| 847 | |||
| 848 | /* allocate memory for LPE API set */ | ||
| 849 | intelmaddata->sstdrv_ops = kzalloc(sizeof(struct intel_sst_card_ops), | ||
| 850 | GFP_KERNEL); | ||
| 851 | if (!intelmaddata->sstdrv_ops) { | ||
| 852 | pr_err("mem allocation for ops fail\n"); | ||
| 853 | kfree(intelmaddata); | ||
| 854 | return -ENOMEM; | ||
| 855 | } | ||
| 856 | |||
| 857 | intelmaddata->cpu_id = info->cpu_id; | ||
| 858 | /* create a card instance with ALSA framework */ | ||
| 859 | ret_val = snd_card_create(card_index, card_id, THIS_MODULE, 0, &card); | ||
| 860 | if (ret_val) { | ||
| 861 | pr_err("snd_card_create fail\n"); | ||
| 862 | goto free_allocs; | ||
| 863 | } | ||
| 864 | |||
| 865 | intelmaddata->pdev = pdev; | ||
| 866 | intelmaddata->irq = platform_get_irq(pdev, 0); | ||
| 867 | platform_set_drvdata(pdev, intelmaddata); | ||
| 868 | intelmaddata->card = card; | ||
| 869 | intelmaddata->card_id = card_id; | ||
| 870 | intelmaddata->card_index = card_index; | ||
| 871 | intelmaddata->master_mute = UNMUTE; | ||
| 872 | intelmaddata->playback_cnt = intelmaddata->capture_cnt = 0; | ||
| 873 | strncpy(card->driver, INTEL_MAD, strlen(INTEL_MAD)); | ||
| 874 | strncpy(card->shortname, INTEL_MAD, strlen(INTEL_MAD)); | ||
| 875 | |||
| 876 | intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES; | ||
| 877 | /* registering with LPE driver to get access to SST APIs to use */ | ||
| 878 | ret_val = snd_intelmad_sst_register(intelmaddata); | ||
| 879 | if (ret_val) { | ||
| 880 | pr_err("snd_intelmad_sst_register failed\n"); | ||
| 881 | goto set_null_data; | ||
| 882 | } | ||
| 883 | |||
| 884 | intelmaddata->pmic_status = PMIC_INIT; | ||
| 885 | |||
| 886 | ret_val = snd_intelmad_pcm(card, intelmaddata); | ||
| 887 | if (ret_val) { | ||
| 888 | pr_err("snd_intelmad_pcm failed\n"); | ||
| 889 | goto free_sst; | ||
| 890 | } | ||
| 891 | |||
| 892 | ret_val = snd_intelmad_mixer(intelmaddata); | ||
| 893 | if (ret_val) { | ||
| 894 | pr_err("snd_intelmad_mixer failed\n"); | ||
| 895 | goto free_card; | ||
| 896 | } | ||
| 897 | |||
| 898 | ret_val = snd_intelmad_jack(intelmaddata); | ||
| 899 | if (ret_val) { | ||
| 900 | pr_err("snd_intelmad_jack failed\n"); | ||
| 901 | goto free_card; | ||
| 902 | } | ||
| 903 | intelmaddata->adc_address = mid_initialize_adc(); | ||
| 904 | |||
| 905 | /*create work queue for jack interrupt*/ | ||
| 906 | INIT_WORK(&intelmaddata->mad_jack_msg.wq, | ||
| 907 | sst_process_mad_jack_detection); | ||
| 908 | |||
| 909 | intelmaddata->mad_jack_wq = create_workqueue("sst_mad_jack_wq"); | ||
| 910 | if (!intelmaddata->mad_jack_wq) | ||
| 911 | goto free_card; | ||
| 912 | |||
| 913 | ret_val = snd_intelmad_register_irq(intelmaddata, | ||
| 914 | info->irq_cache, info->size); | ||
| 915 | if (ret_val) { | ||
| 916 | pr_err("snd_intelmad_register_irq fail\n"); | ||
| 917 | goto free_mad_jack_wq; | ||
| 918 | } | ||
| 919 | |||
| 920 | /* internal function call to register device with ALSA */ | ||
| 921 | ret_val = snd_intelmad_create(intelmaddata, card); | ||
| 922 | if (ret_val) { | ||
| 923 | pr_err("snd_intelmad_create failed\n"); | ||
| 924 | goto set_pvt_data; | ||
| 925 | } | ||
| 926 | card->private_data = &intelmaddata; | ||
| 927 | snd_card_set_dev(card, &pdev->dev); | ||
| 928 | ret_val = snd_card_register(card); | ||
| 929 | if (ret_val) { | ||
| 930 | pr_err("snd_card_register failed\n"); | ||
| 931 | goto set_pvt_data; | ||
| 932 | } | ||
| 933 | if (pdev->dev.platform_data) { | ||
| 934 | int gpio_amp = *(int *)pdev->dev.platform_data; | ||
| 935 | if (gpio_request_one(gpio_amp, GPIOF_OUT_INIT_LOW, "amp power")) | ||
| 936 | gpio_amp = 0; | ||
| 937 | intelmaddata->sstdrv_ops->scard_ops->gpio_amp = gpio_amp; | ||
| 938 | } | ||
| 939 | |||
| 940 | pr_debug("snd_intelmad_probe complete\n"); | ||
| 941 | return ret_val; | ||
| 942 | |||
| 943 | set_pvt_data: | ||
| 944 | card->private_data = NULL; | ||
| 945 | free_mad_jack_wq: | ||
| 946 | destroy_workqueue(intelmaddata->mad_jack_wq); | ||
| 947 | free_card: | ||
| 948 | snd_card_free(intelmaddata->card); | ||
| 949 | free_sst: | ||
| 950 | unregister_sst_card(intelmaddata->sstdrv_ops); | ||
| 951 | set_null_data: | ||
| 952 | platform_set_drvdata(pdev, NULL); | ||
| 953 | free_allocs: | ||
| 954 | pr_err("probe failed\n"); | ||
| 955 | snd_card_free(card); | ||
| 956 | kfree(intelmaddata->sstdrv_ops); | ||
| 957 | kfree(intelmaddata); | ||
| 958 | return ret_val; | ||
| 959 | } | ||
| 960 | |||
| 961 | |||
| 962 | static int snd_intelmad_remove(struct platform_device *pdev) | ||
| 963 | { | ||
| 964 | struct snd_intelmad *intelmaddata = platform_get_drvdata(pdev); | ||
| 965 | |||
| 966 | if (intelmaddata) { | ||
| 967 | if (intelmaddata->sstdrv_ops->scard_ops->gpio_amp) | ||
| 968 | gpio_free(intelmaddata->sstdrv_ops->scard_ops->gpio_amp); | ||
| 969 | free_irq(intelmaddata->irq, intelmaddata); | ||
| 970 | snd_card_free(intelmaddata->card); | ||
| 971 | } | ||
| 972 | intelmad_drv = NULL; | ||
| 973 | platform_set_drvdata(pdev, NULL); | ||
| 974 | return 0; | ||
| 975 | } | ||
| 976 | |||
| 977 | /********************************************************************* | ||
| 978 | * Driver initialization and exit | ||
| 979 | *********************************************************************/ | ||
| 980 | static const struct platform_device_id snd_intelmad_ids[] = { | ||
| 981 | {DRIVER_NAME_MRST, INFO(CPU_CHIP_LINCROFT, AUDINT_BASE, 1)}, | ||
| 982 | {DRIVER_NAME_MFLD, INFO(CPU_CHIP_PENWELL, 0xFFFF7FCD, 1)}, | ||
| 983 | {"", 0}, | ||
| 984 | |||
| 985 | }; | ||
| 986 | |||
| 987 | static struct platform_driver snd_intelmad_driver = { | ||
| 988 | .driver = { | ||
| 989 | .owner = THIS_MODULE, | ||
| 990 | .name = "intel_mid_sound_card", | ||
| 991 | }, | ||
| 992 | .id_table = snd_intelmad_ids, | ||
| 993 | .probe = snd_intelmad_probe, | ||
| 994 | .remove = __devexit_p(snd_intelmad_remove), | ||
| 995 | }; | ||
| 996 | |||
| 997 | /* | ||
| 998 | * alsa_card_intelmad_init- driver init function | ||
| 999 | * | ||
| 1000 | * This function is called when driver module is inserted | ||
| 1001 | */ | ||
| 1002 | static int __init alsa_card_intelmad_init(void) | ||
| 1003 | { | ||
| 1004 | pr_debug("mad_init called\n"); | ||
| 1005 | return platform_driver_register(&snd_intelmad_driver); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | /** | ||
| 1009 | * alsa_card_intelmad_exit- driver exit function | ||
| 1010 | * | ||
| 1011 | * This function is called when driver module is removed | ||
| 1012 | */ | ||
| 1013 | static void __exit alsa_card_intelmad_exit(void) | ||
| 1014 | { | ||
| 1015 | pr_debug("mad_exit called\n"); | ||
| 1016 | return platform_driver_unregister(&snd_intelmad_driver); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | module_init(alsa_card_intelmad_init) | ||
| 1020 | module_exit(alsa_card_intelmad_exit) | ||
| 1021 | |||
diff --git a/drivers/staging/intel_sst/intelmid.h b/drivers/staging/intel_sst/intelmid.h new file mode 100644 index 00000000000..14a7ba078b7 --- /dev/null +++ b/drivers/staging/intel_sst/intelmid.h | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | /* | ||
| 2 | * intelmid.h - Intel Sound card driver for MID | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Harsha Priya <priya.harsha@intel.com> | ||
| 6 | * Vinod Koul <vinod.koul@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * ALSA driver header for Intel MAD chipset | ||
| 26 | */ | ||
| 27 | #ifndef __INTELMID_H | ||
| 28 | #define __INTELMID_H | ||
| 29 | |||
| 30 | #include <linux/time.h> | ||
| 31 | #include <sound/jack.h> | ||
| 32 | |||
| 33 | #define DRIVER_NAME_MFLD "msic_audio" | ||
| 34 | #define DRIVER_NAME_MRST "pmic_audio" | ||
| 35 | #define DRIVER_NAME "intelmid_audio" | ||
| 36 | #define PMIC_SOUND_IRQ_TYPE_MASK (1 << 15) | ||
| 37 | #define AUDINT_BASE (0xFFFFEFF8 + (6 * sizeof(u8))) | ||
| 38 | #define REG_IRQ | ||
| 39 | /* values #defined */ | ||
| 40 | /* will differ for different hw - to be taken from config */ | ||
| 41 | #define MAX_DEVICES 1 | ||
| 42 | #define MIN_RATE 8000 | ||
| 43 | #define MAX_RATE 48000 | ||
| 44 | #define MAX_BUFFER (800*1024) /* for PCM */ | ||
| 45 | #define MIN_BUFFER (800*1024) | ||
| 46 | #define MAX_PERIODS (1024*2) | ||
| 47 | #define MIN_PERIODS 2 | ||
| 48 | #define MAX_PERIOD_BYTES MAX_BUFFER | ||
| 49 | #define MIN_PERIOD_BYTES 32 | ||
| 50 | /*#define MIN_PERIOD_BYTES 160*/ | ||
| 51 | #define MAX_MUTE 1 | ||
| 52 | #define MIN_MUTE 0 | ||
| 53 | #define MONO_CNTL 1 | ||
| 54 | #define STEREO_CNTL 2 | ||
| 55 | #define MIN_CHANNEL 1 | ||
| 56 | #define MAX_CHANNEL_AMIC 2 | ||
| 57 | #define MAX_CHANNEL_DMIC 5 | ||
| 58 | #define FIFO_SIZE 0 /* fifo not being used */ | ||
| 59 | #define INTEL_MAD "Intel MAD" | ||
| 60 | #define MAX_CTRL_MRST 8 | ||
| 61 | #define MAX_CTRL_MFLD 7 | ||
| 62 | #define MAX_CTRL 8 | ||
| 63 | #define MAX_VENDORS 4 | ||
| 64 | /* TODO +6 db */ | ||
| 65 | #define MAX_VOL 64 | ||
| 66 | /* TODO -57 db */ | ||
| 67 | #define MIN_VOL 0 | ||
| 68 | #define PLAYBACK_COUNT 1 | ||
| 69 | #define CAPTURE_COUNT 1 | ||
| 70 | #define ADC_ONE_LSB_MULTIPLIER 2346 | ||
| 71 | |||
| 72 | #define MID_JACK_HS_LONG_PRESS SND_JACK_BTN_0 | ||
| 73 | #define MID_JACK_HS_SHORT_PRESS SND_JACK_BTN_1 | ||
| 74 | |||
| 75 | extern int sst_card_vendor_id; | ||
| 76 | |||
| 77 | struct mad_jack { | ||
| 78 | struct snd_jack jack; | ||
| 79 | int jack_status; | ||
| 80 | int jack_dev_state; | ||
| 81 | struct timeval buttonpressed; | ||
| 82 | struct timeval buttonreleased; | ||
| 83 | }; | ||
| 84 | |||
| 85 | struct mad_jack_msg_wq { | ||
| 86 | u8 intsts; | ||
| 87 | struct snd_intelmad *intelmaddata; | ||
| 88 | struct work_struct wq; | ||
| 89 | |||
| 90 | }; | ||
| 91 | |||
| 92 | struct snd_intelmad_probe_info { | ||
| 93 | unsigned int cpu_id; | ||
| 94 | unsigned int irq_cache; | ||
| 95 | unsigned int size; | ||
| 96 | }; | ||
| 97 | |||
| 98 | /** | ||
| 99 | * struct snd_intelmad - intelmad driver structure | ||
| 100 | * | ||
| 101 | * @card: ptr to the card details | ||
| 102 | * @card_index: sound card index | ||
| 103 | * @card_id: sound card id detected | ||
| 104 | * @sstdrv_ops: ptr to sst driver ops | ||
| 105 | * @pdev: ptr to platform device | ||
| 106 | * @irq: interrupt number detected | ||
| 107 | * @pmic_status: Device status of sound card | ||
| 108 | * @int_base: ptr to MMIO interrupt region | ||
| 109 | * @output_sel: device selected as o/p | ||
| 110 | * @input_sel: device selected as i/p | ||
| 111 | * @master_mute: master mute status | ||
| 112 | * @jack: jack status | ||
| 113 | * @playback_cnt: active pb streams | ||
| 114 | * @capture_cnt: active cp streams | ||
| 115 | * @mad_jack_msg: wq struct for jack interrupt processing | ||
| 116 | * @mad_jack_wq: wq for jack interrupt processing | ||
| 117 | * @jack_prev_state: Previos state of jack detected | ||
| 118 | * @cpu_id: current cpu id loaded for | ||
| 119 | */ | ||
| 120 | struct snd_intelmad { | ||
| 121 | struct snd_card *card; /* ptr to the card details */ | ||
| 122 | int card_index;/* card index */ | ||
| 123 | char *card_id; /* card id */ | ||
| 124 | struct intel_sst_card_ops *sstdrv_ops;/* ptr to sst driver ops */ | ||
| 125 | struct platform_device *pdev; | ||
| 126 | int irq; | ||
| 127 | int pmic_status; | ||
| 128 | void __iomem *int_base; | ||
| 129 | int output_sel; | ||
| 130 | int input_sel; | ||
| 131 | int lineout_sel; | ||
| 132 | int master_mute; | ||
| 133 | struct mad_jack jack[4]; | ||
| 134 | int playback_cnt; | ||
| 135 | int capture_cnt; | ||
| 136 | u16 adc_address; | ||
| 137 | struct mad_jack_msg_wq mad_jack_msg; | ||
| 138 | struct workqueue_struct *mad_jack_wq; | ||
| 139 | u8 jack_prev_state; | ||
| 140 | unsigned int cpu_id; | ||
| 141 | }; | ||
| 142 | |||
| 143 | struct snd_control_val { | ||
| 144 | int playback_vol_max; | ||
| 145 | int playback_vol_min; | ||
| 146 | int capture_vol_max; | ||
| 147 | int capture_vol_min; | ||
| 148 | int master_vol_max; | ||
| 149 | int master_vol_min; | ||
| 150 | }; | ||
| 151 | |||
| 152 | struct mad_stream_pvt { | ||
| 153 | int stream_status; | ||
| 154 | int stream_ops; | ||
| 155 | struct snd_pcm_substream *substream; | ||
| 156 | struct pcm_stream_info stream_info; | ||
| 157 | ssize_t dbg_cum_bytes; | ||
| 158 | enum snd_sst_device_type device; | ||
| 159 | }; | ||
| 160 | |||
| 161 | enum mad_drv_status { | ||
| 162 | INIT = 1, | ||
| 163 | STARTED, | ||
| 164 | RUNNING, | ||
| 165 | PAUSED, | ||
| 166 | DROPPED, | ||
| 167 | }; | ||
| 168 | |||
| 169 | enum mad_pmic_status { | ||
| 170 | PMIC_UNINIT = 1, | ||
| 171 | PMIC_INIT, | ||
| 172 | }; | ||
| 173 | enum _widget_ctrl { | ||
| 174 | OUTPUT_SEL = 1, | ||
| 175 | INPUT_SEL, | ||
| 176 | PLAYBACK_VOL, | ||
| 177 | PLAYBACK_MUTE, | ||
| 178 | CAPTURE_VOL, | ||
| 179 | CAPTURE_MUTE, | ||
| 180 | MASTER_VOL, | ||
| 181 | MASTER_MUTE | ||
| 182 | }; | ||
| 183 | enum _widget_ctrl_mfld { | ||
| 184 | LINEOUT_SEL_MFLD = 3, | ||
| 185 | }; | ||
| 186 | enum hw_chs { | ||
| 187 | HW_CH0 = 0, | ||
| 188 | HW_CH1, | ||
| 189 | HW_CH2, | ||
| 190 | HW_CH3 | ||
| 191 | }; | ||
| 192 | |||
| 193 | void period_elapsed(void *mad_substream); | ||
| 194 | int snd_intelmad_alloc_stream(struct snd_pcm_substream *substream); | ||
| 195 | int snd_intelmad_init_stream(struct snd_pcm_substream *substream); | ||
| 196 | |||
| 197 | int sst_sc_reg_access(struct sc_reg_access *sc_access, | ||
| 198 | int type, int num_val); | ||
| 199 | #define CPU_CHIP_LINCROFT 1 /* System running lincroft */ | ||
| 200 | #define CPU_CHIP_PENWELL 2 /* System running penwell */ | ||
| 201 | |||
| 202 | extern struct snd_control_val intelmad_ctrl_val[]; | ||
| 203 | extern struct snd_kcontrol_new snd_intelmad_controls_mrst[]; | ||
| 204 | extern struct snd_kcontrol_new snd_intelmad_controls_mfld[]; | ||
| 205 | extern struct snd_pmic_ops *intelmad_vendor_ops[]; | ||
| 206 | void sst_mad_send_jack_report(struct snd_jack *jack, | ||
| 207 | int buttonpressevent , int status); | ||
| 208 | |||
| 209 | #endif /* __INTELMID_H */ | ||
diff --git a/drivers/staging/intel_sst/intelmid_adc_control.h b/drivers/staging/intel_sst/intelmid_adc_control.h new file mode 100644 index 00000000000..65d5c398876 --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_adc_control.h | |||
| @@ -0,0 +1,193 @@ | |||
| 1 | #ifndef __INTELMID_ADC_CONTROL_H__ | ||
| 2 | #define __INTELMID_ADC_CONTROL_H_ | ||
| 3 | /* | ||
| 4 | * intelmid_adc_control.h - Intel SST Driver for audio engine | ||
| 5 | * | ||
| 6 | * Copyright (C) 2008-10 Intel Corporation | ||
| 7 | * Authors: R Durgadadoss <r.durgadoss@intel.com> | ||
| 8 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * | ||
| 26 | * Common private ADC declarations for SST | ||
| 27 | */ | ||
| 28 | |||
| 29 | |||
| 30 | #define MSIC_ADC1CNTL1 0x1C0 | ||
| 31 | #define MSIC_ADC_ENBL 0x10 | ||
| 32 | #define MSIC_ADC_START 0x08 | ||
| 33 | |||
| 34 | #define MSIC_ADC1CNTL3 0x1C2 | ||
| 35 | #define MSIC_ADCTHERM_ENBL 0x04 | ||
| 36 | #define MSIC_ADCRRDATA_ENBL 0x05 | ||
| 37 | |||
| 38 | #define MSIC_STOPBIT_MASK 16 | ||
| 39 | #define MSIC_ADCTHERM_MASK 4 | ||
| 40 | |||
| 41 | #define ADC_CHANLS_MAX 15 /* Number of ADC channels */ | ||
| 42 | #define ADC_LOOP_MAX (ADC_CHANLS_MAX - 1) | ||
| 43 | |||
| 44 | /* ADC channel code values */ | ||
| 45 | #define AUDIO_DETECT_CODE 0x06 | ||
| 46 | |||
| 47 | /* ADC base addresses */ | ||
| 48 | #define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ | ||
| 49 | #define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ | ||
| 50 | |||
| 51 | |||
| 52 | /** | ||
| 53 | * configure_adc - enables/disables the ADC for conversion | ||
| 54 | * @val: zero: disables the ADC non-zero:enables the ADC | ||
| 55 | * | ||
| 56 | * Enable/Disable the ADC depending on the argument | ||
| 57 | * | ||
| 58 | * Can sleep | ||
| 59 | */ | ||
| 60 | static inline int configure_adc(int val) | ||
| 61 | { | ||
| 62 | int ret; | ||
| 63 | struct sc_reg_access sc_access = {0,}; | ||
| 64 | |||
| 65 | |||
| 66 | sc_access.reg_addr = MSIC_ADC1CNTL1; | ||
| 67 | ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); | ||
| 68 | if (ret) | ||
| 69 | return ret; | ||
| 70 | |||
| 71 | if (val) | ||
| 72 | /* Enable and start the ADC */ | ||
| 73 | sc_access.value |= (MSIC_ADC_ENBL | MSIC_ADC_START); | ||
| 74 | else | ||
| 75 | /* Just stop the ADC */ | ||
| 76 | sc_access.value &= (~MSIC_ADC_START); | ||
| 77 | sc_access.reg_addr = MSIC_ADC1CNTL1; | ||
| 78 | return sst_sc_reg_access(&sc_access, PMIC_WRITE, 1); | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * reset_stopbit - sets the stop bit to 0 on the given channel | ||
| 83 | * @addr: address of the channel | ||
| 84 | * | ||
| 85 | * Can sleep | ||
| 86 | */ | ||
| 87 | static inline int reset_stopbit(uint16_t addr) | ||
| 88 | { | ||
| 89 | int ret; | ||
| 90 | struct sc_reg_access sc_access = {0,}; | ||
| 91 | sc_access.reg_addr = addr; | ||
| 92 | ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); | ||
| 93 | if (ret) | ||
| 94 | return ret; | ||
| 95 | /* Set the stop bit to zero */ | ||
| 96 | sc_access.reg_addr = addr; | ||
| 97 | sc_access.value = (sc_access.value) & 0xEF; | ||
| 98 | return sst_sc_reg_access(&sc_access, PMIC_WRITE, 1); | ||
| 99 | } | ||
| 100 | |||
| 101 | /** | ||
| 102 | * find_free_channel - finds an empty channel for conversion | ||
| 103 | * | ||
| 104 | * If the ADC is not enabled then start using 0th channel | ||
| 105 | * itself. Otherwise find an empty channel by looking for a | ||
| 106 | * channel in which the stopbit is set to 1. returns the index | ||
| 107 | * of the first free channel if succeeds or an error code. | ||
| 108 | * | ||
| 109 | * Context: can sleep | ||
| 110 | * | ||
| 111 | */ | ||
| 112 | static inline int find_free_channel(void) | ||
| 113 | { | ||
| 114 | int ret; | ||
| 115 | int i; | ||
| 116 | |||
| 117 | struct sc_reg_access sc_access = {0,}; | ||
| 118 | |||
| 119 | /* check whether ADC is enabled */ | ||
| 120 | sc_access.reg_addr = MSIC_ADC1CNTL1; | ||
| 121 | ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); | ||
| 122 | if (ret) | ||
| 123 | return ret; | ||
| 124 | |||
| 125 | if ((sc_access.value & MSIC_ADC_ENBL) == 0) | ||
| 126 | return 0; | ||
| 127 | |||
| 128 | /* ADC is already enabled; Looking for an empty channel */ | ||
| 129 | for (i = 0; i < ADC_CHANLS_MAX; i++) { | ||
| 130 | |||
| 131 | sc_access.reg_addr = ADC_CHNL_START_ADDR + i; | ||
| 132 | ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); | ||
| 133 | if (ret) | ||
| 134 | return ret; | ||
| 135 | |||
| 136 | if (sc_access.value & MSIC_STOPBIT_MASK) { | ||
| 137 | ret = i; | ||
| 138 | break; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret; | ||
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * mid_initialize_adc - initializing the ADC | ||
| 146 | * @dev: our device structure | ||
| 147 | * | ||
| 148 | * Initialize the ADC for reading thermistor values. Can sleep. | ||
| 149 | */ | ||
| 150 | static inline int mid_initialize_adc(void) | ||
| 151 | { | ||
| 152 | int base_addr, chnl_addr; | ||
| 153 | int ret; | ||
| 154 | static int channel_index; | ||
| 155 | struct sc_reg_access sc_access = {0,}; | ||
| 156 | |||
| 157 | /* Index of the first channel in which the stop bit is set */ | ||
| 158 | channel_index = find_free_channel(); | ||
| 159 | if (channel_index < 0) { | ||
| 160 | pr_err("No free ADC channels"); | ||
| 161 | return channel_index; | ||
| 162 | } | ||
| 163 | |||
| 164 | base_addr = ADC_CHNL_START_ADDR + channel_index; | ||
| 165 | |||
| 166 | if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) { | ||
| 167 | /* Reset stop bit for channels other than 0 and 12 */ | ||
| 168 | ret = reset_stopbit(base_addr); | ||
| 169 | if (ret) | ||
| 170 | return ret; | ||
| 171 | |||
| 172 | /* Index of the first free channel */ | ||
| 173 | base_addr++; | ||
| 174 | channel_index++; | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Since this is the last channel, set the stop bit | ||
| 178 | to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ | ||
| 179 | sc_access.reg_addr = base_addr; | ||
| 180 | sc_access.value = AUDIO_DETECT_CODE | 0x10; | ||
| 181 | ret = sst_sc_reg_access(&sc_access, PMIC_WRITE, 1); | ||
| 182 | if (ret) { | ||
| 183 | pr_err("unable to enable ADC"); | ||
| 184 | return ret; | ||
| 185 | } | ||
| 186 | |||
| 187 | chnl_addr = ADC_DATA_START_ADDR + 2 * channel_index; | ||
| 188 | pr_debug("mid_initialize : %x", chnl_addr); | ||
| 189 | configure_adc(1); | ||
| 190 | return chnl_addr; | ||
| 191 | } | ||
| 192 | #endif | ||
| 193 | |||
diff --git a/drivers/staging/intel_sst/intelmid_ctrl.c b/drivers/staging/intel_sst/intelmid_ctrl.c new file mode 100644 index 00000000000..19ec474b362 --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_ctrl.c | |||
| @@ -0,0 +1,921 @@ | |||
| 1 | /* | ||
| 2 | * intelmid_ctrl.c - Intel Sound card driver for MID | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Harsha Priya <priya.harsha@intel.com> | ||
| 6 | * Vinod Koul <vinod.koul@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * ALSA driver handling mixer controls for Intel MAD chipset | ||
| 26 | */ | ||
| 27 | |||
| 28 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 29 | |||
| 30 | #include <sound/core.h> | ||
| 31 | #include <sound/control.h> | ||
| 32 | #include "intel_sst.h" | ||
| 33 | #include "intel_sst_ioctl.h" | ||
| 34 | #include "intelmid_snd_control.h" | ||
| 35 | #include "intelmid.h" | ||
| 36 | |||
| 37 | #define HW_CH_BASE 4 | ||
| 38 | |||
| 39 | |||
| 40 | #define HW_CH_0 "Hw1" | ||
| 41 | #define HW_CH_1 "Hw2" | ||
| 42 | #define HW_CH_2 "Hw3" | ||
| 43 | #define HW_CH_3 "Hw4" | ||
| 44 | |||
| 45 | static char *router_dmics[] = { "DMIC1", | ||
| 46 | "DMIC2", | ||
| 47 | "DMIC3", | ||
| 48 | "DMIC4", | ||
| 49 | "DMIC5", | ||
| 50 | "DMIC6" | ||
| 51 | }; | ||
| 52 | |||
| 53 | static char *out_names_mrst[] = {"Headphones", | ||
| 54 | "Internal speakers"}; | ||
| 55 | static char *in_names_mrst[] = {"AMIC", | ||
| 56 | "DMIC", | ||
| 57 | "HS_MIC"}; | ||
| 58 | static char *line_out_names_mfld[] = {"Headset", | ||
| 59 | "IHF ", | ||
| 60 | "Vibra1 ", | ||
| 61 | "Vibra2 ", | ||
| 62 | "NONE "}; | ||
| 63 | static char *out_names_mfld[] = {"Headset ", | ||
| 64 | "EarPiece "}; | ||
| 65 | static char *in_names_mfld[] = {"AMIC", | ||
| 66 | "DMIC"}; | ||
| 67 | |||
| 68 | struct snd_control_val intelmad_ctrl_val[MAX_VENDORS] = { | ||
| 69 | { | ||
| 70 | .playback_vol_max = 63, | ||
| 71 | .playback_vol_min = 0, | ||
| 72 | .capture_vol_max = 63, | ||
| 73 | .capture_vol_min = 0, | ||
| 74 | }, | ||
| 75 | { | ||
| 76 | .playback_vol_max = 0, | ||
| 77 | .playback_vol_min = -31, | ||
| 78 | .capture_vol_max = 0, | ||
| 79 | .capture_vol_min = -20, | ||
| 80 | }, | ||
| 81 | { | ||
| 82 | .playback_vol_max = 0, | ||
| 83 | .playback_vol_min = -31, | ||
| 84 | .capture_vol_max = 0, | ||
| 85 | .capture_vol_min = -31, | ||
| 86 | .master_vol_max = 0, | ||
| 87 | .master_vol_min = -126, | ||
| 88 | }, | ||
| 89 | }; | ||
| 90 | |||
| 91 | /* control path functionalities */ | ||
| 92 | |||
| 93 | static inline int snd_intelmad_volume_info(struct snd_ctl_elem_info *uinfo, | ||
| 94 | int control_type, int max, int min) | ||
| 95 | { | ||
| 96 | WARN_ON(!uinfo); | ||
| 97 | |||
| 98 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
| 99 | uinfo->count = control_type; | ||
| 100 | uinfo->value.integer.min = min; | ||
| 101 | uinfo->value.integer.max = max; | ||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * snd_intelmad_mute_info - provides information about the mute controls | ||
| 107 | * | ||
| 108 | * @kcontrol: pointer to the control | ||
| 109 | * @uinfo: pointer to the structure where the control's info need | ||
| 110 | * to be filled | ||
| 111 | * | ||
| 112 | * This function is called when a mixer application requests for control's info | ||
| 113 | */ | ||
| 114 | static int snd_intelmad_mute_info(struct snd_kcontrol *kcontrol, | ||
| 115 | struct snd_ctl_elem_info *uinfo) | ||
| 116 | { | ||
| 117 | WARN_ON(!uinfo); | ||
| 118 | WARN_ON(!kcontrol); | ||
| 119 | |||
| 120 | /* set up the mute as a boolean mono control with min-max values */ | ||
| 121 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
| 122 | uinfo->count = MONO_CNTL; | ||
| 123 | uinfo->value.integer.min = MIN_MUTE; | ||
| 124 | uinfo->value.integer.max = MAX_MUTE; | ||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | |||
| 128 | /** | ||
| 129 | * snd_intelmad_capture_volume_info - provides info about the volume control | ||
| 130 | * | ||
| 131 | * @kcontrol: pointer to the control | ||
| 132 | * @uinfo: pointer to the structure where the control's info need | ||
| 133 | * to be filled | ||
| 134 | * | ||
| 135 | * This function is called when a mixer application requests for control's info | ||
| 136 | */ | ||
| 137 | static int snd_intelmad_capture_volume_info(struct snd_kcontrol *kcontrol, | ||
| 138 | struct snd_ctl_elem_info *uinfo) | ||
| 139 | { | ||
| 140 | snd_intelmad_volume_info(uinfo, MONO_CNTL, | ||
| 141 | intelmad_ctrl_val[sst_card_vendor_id].capture_vol_max, | ||
| 142 | intelmad_ctrl_val[sst_card_vendor_id].capture_vol_min); | ||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | /** | ||
| 147 | * snd_intelmad_playback_volume_info - provides info about the volume control | ||
| 148 | * | ||
| 149 | * @kcontrol: pointer to the control | ||
| 150 | * @uinfo: pointer to the structure where the control's info need | ||
| 151 | * to be filled | ||
| 152 | * | ||
| 153 | * This function is called when a mixer application requests for control's info | ||
| 154 | */ | ||
| 155 | static int snd_intelmad_playback_volume_info(struct snd_kcontrol *kcontrol, | ||
| 156 | struct snd_ctl_elem_info *uinfo) | ||
| 157 | { | ||
| 158 | snd_intelmad_volume_info(uinfo, STEREO_CNTL, | ||
| 159 | intelmad_ctrl_val[sst_card_vendor_id].playback_vol_max, | ||
| 160 | intelmad_ctrl_val[sst_card_vendor_id].playback_vol_min); | ||
| 161 | return 0; | ||
| 162 | } | ||
| 163 | |||
| 164 | static int snd_intelmad_master_volume_info(struct snd_kcontrol *kcontrol, | ||
| 165 | struct snd_ctl_elem_info *uinfo) | ||
| 166 | { | ||
| 167 | snd_intelmad_volume_info(uinfo, STEREO_CNTL, | ||
| 168 | intelmad_ctrl_val[sst_card_vendor_id].master_vol_max, | ||
| 169 | intelmad_ctrl_val[sst_card_vendor_id].master_vol_min); | ||
| 170 | return 0; | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * snd_intelmad_device_info_mrst - provides information about the devices available | ||
| 175 | * | ||
| 176 | * @kcontrol: pointer to the control | ||
| 177 | * @uinfo: pointer to the structure where the devices's info need | ||
| 178 | * to be filled | ||
| 179 | * | ||
| 180 | * This function is called when a mixer application requests for device's info | ||
| 181 | */ | ||
| 182 | static int snd_intelmad_device_info_mrst(struct snd_kcontrol *kcontrol, | ||
| 183 | struct snd_ctl_elem_info *uinfo) | ||
| 184 | { | ||
| 185 | |||
| 186 | WARN_ON(!kcontrol); | ||
| 187 | WARN_ON(!uinfo); | ||
| 188 | |||
| 189 | /* setup device select as drop down controls with different values */ | ||
| 190 | if (kcontrol->id.numid == OUTPUT_SEL) | ||
| 191 | uinfo->value.enumerated.items = ARRAY_SIZE(out_names_mrst); | ||
| 192 | else | ||
| 193 | uinfo->value.enumerated.items = ARRAY_SIZE(in_names_mrst); | ||
| 194 | uinfo->count = MONO_CNTL; | ||
| 195 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
| 196 | |||
| 197 | if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) | ||
| 198 | uinfo->value.enumerated.item = 1; | ||
| 199 | if (kcontrol->id.numid == OUTPUT_SEL) | ||
| 200 | strncpy(uinfo->value.enumerated.name, | ||
| 201 | out_names_mrst[uinfo->value.enumerated.item], | ||
| 202 | sizeof(uinfo->value.enumerated.name)-1); | ||
| 203 | else | ||
| 204 | strncpy(uinfo->value.enumerated.name, | ||
| 205 | in_names_mrst[uinfo->value.enumerated.item], | ||
| 206 | sizeof(uinfo->value.enumerated.name)-1); | ||
| 207 | return 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | static int snd_intelmad_device_info_mfld(struct snd_kcontrol *kcontrol, | ||
| 211 | struct snd_ctl_elem_info *uinfo) | ||
| 212 | { | ||
| 213 | struct snd_pmic_ops *scard_ops; | ||
| 214 | struct snd_intelmad *intelmaddata; | ||
| 215 | |||
| 216 | WARN_ON(!kcontrol); | ||
| 217 | WARN_ON(!uinfo); | ||
| 218 | |||
| 219 | intelmaddata = kcontrol->private_data; | ||
| 220 | |||
| 221 | WARN_ON(!intelmaddata->sstdrv_ops); | ||
| 222 | |||
| 223 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 224 | /* setup device select as drop down controls with different values */ | ||
| 225 | if (kcontrol->id.numid == OUTPUT_SEL) | ||
| 226 | uinfo->value.enumerated.items = ARRAY_SIZE(out_names_mfld); | ||
| 227 | else if (kcontrol->id.numid == INPUT_SEL) | ||
| 228 | uinfo->value.enumerated.items = ARRAY_SIZE(in_names_mfld); | ||
| 229 | else if (kcontrol->id.numid == LINEOUT_SEL_MFLD) { | ||
| 230 | uinfo->value.enumerated.items = ARRAY_SIZE(line_out_names_mfld); | ||
| 231 | scard_ops->line_out_names_cnt = uinfo->value.enumerated.items; | ||
| 232 | } else | ||
| 233 | return -EINVAL; | ||
| 234 | uinfo->count = MONO_CNTL; | ||
| 235 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
| 236 | |||
| 237 | if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) | ||
| 238 | uinfo->value.enumerated.item = 1; | ||
| 239 | if (kcontrol->id.numid == OUTPUT_SEL) | ||
| 240 | strncpy(uinfo->value.enumerated.name, | ||
| 241 | out_names_mfld[uinfo->value.enumerated.item], | ||
| 242 | sizeof(uinfo->value.enumerated.name)-1); | ||
| 243 | else if (kcontrol->id.numid == INPUT_SEL) | ||
| 244 | strncpy(uinfo->value.enumerated.name, | ||
| 245 | in_names_mfld[uinfo->value.enumerated.item], | ||
| 246 | sizeof(uinfo->value.enumerated.name)-1); | ||
| 247 | else if (kcontrol->id.numid == LINEOUT_SEL_MFLD) | ||
| 248 | strncpy(uinfo->value.enumerated.name, | ||
| 249 | line_out_names_mfld[uinfo->value.enumerated.item], | ||
| 250 | sizeof(uinfo->value.enumerated.name)-1); | ||
| 251 | else | ||
| 252 | return -EINVAL; | ||
| 253 | return 0; | ||
| 254 | } | ||
| 255 | |||
| 256 | /** | ||
| 257 | * snd_intelmad_volume_get - gets the current volume for the control | ||
| 258 | * | ||
| 259 | * @kcontrol: pointer to the control | ||
| 260 | * @uval: pointer to the structure where the control's info need | ||
| 261 | * to be filled | ||
| 262 | * | ||
| 263 | * This function is called when .get function of a control is invoked from app | ||
| 264 | */ | ||
| 265 | static int snd_intelmad_volume_get(struct snd_kcontrol *kcontrol, | ||
| 266 | struct snd_ctl_elem_value *uval) | ||
| 267 | { | ||
| 268 | int ret_val = 0, cntl_list[2] = {0,}; | ||
| 269 | int value = 0; | ||
| 270 | struct snd_intelmad *intelmaddata; | ||
| 271 | struct snd_pmic_ops *scard_ops; | ||
| 272 | |||
| 273 | pr_debug("snd_intelmad_volume_get called\n"); | ||
| 274 | |||
| 275 | WARN_ON(!uval); | ||
| 276 | WARN_ON(!kcontrol); | ||
| 277 | |||
| 278 | intelmaddata = kcontrol->private_data; | ||
| 279 | |||
| 280 | WARN_ON(!intelmaddata->sstdrv_ops); | ||
| 281 | |||
| 282 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 283 | |||
| 284 | WARN_ON(!scard_ops); | ||
| 285 | |||
| 286 | switch (kcontrol->id.numid) { | ||
| 287 | case PLAYBACK_VOL: | ||
| 288 | cntl_list[0] = PMIC_SND_RIGHT_PB_VOL; | ||
| 289 | cntl_list[1] = PMIC_SND_LEFT_PB_VOL; | ||
| 290 | break; | ||
| 291 | |||
| 292 | case CAPTURE_VOL: | ||
| 293 | cntl_list[0] = PMIC_SND_CAPTURE_VOL; | ||
| 294 | break; | ||
| 295 | |||
| 296 | case MASTER_VOL: | ||
| 297 | cntl_list[0] = PMIC_SND_RIGHT_MASTER_VOL; | ||
| 298 | cntl_list[1] = PMIC_SND_LEFT_MASTER_VOL; | ||
| 299 | break; | ||
| 300 | default: | ||
| 301 | return -EINVAL; | ||
| 302 | } | ||
| 303 | |||
| 304 | ret_val = scard_ops->get_vol(cntl_list[0], &value); | ||
| 305 | uval->value.integer.value[0] = value; | ||
| 306 | |||
| 307 | if (ret_val) | ||
| 308 | return ret_val; | ||
| 309 | |||
| 310 | if (kcontrol->id.numid == PLAYBACK_VOL || | ||
| 311 | kcontrol->id.numid == MASTER_VOL) { | ||
| 312 | ret_val = scard_ops->get_vol(cntl_list[1], &value); | ||
| 313 | uval->value.integer.value[1] = value; | ||
| 314 | } | ||
| 315 | return ret_val; | ||
| 316 | } | ||
| 317 | |||
| 318 | /** | ||
| 319 | * snd_intelmad_mute_get - gets the current mute status for the control | ||
| 320 | * | ||
| 321 | * @kcontrol: pointer to the control | ||
| 322 | * @uval: pointer to the structure where the control's info need | ||
| 323 | * to be filled | ||
| 324 | * | ||
| 325 | * This function is called when .get function of a control is invoked from app | ||
| 326 | */ | ||
| 327 | static int snd_intelmad_mute_get(struct snd_kcontrol *kcontrol, | ||
| 328 | struct snd_ctl_elem_value *uval) | ||
| 329 | { | ||
| 330 | |||
| 331 | int cntl_list = 0, ret_val = 0; | ||
| 332 | u8 value = 0; | ||
| 333 | struct snd_intelmad *intelmaddata; | ||
| 334 | struct snd_pmic_ops *scard_ops; | ||
| 335 | |||
| 336 | pr_debug("Mute_get called\n"); | ||
| 337 | |||
| 338 | WARN_ON(!uval); | ||
| 339 | WARN_ON(!kcontrol); | ||
| 340 | |||
| 341 | intelmaddata = kcontrol->private_data; | ||
| 342 | |||
| 343 | WARN_ON(!intelmaddata->sstdrv_ops); | ||
| 344 | |||
| 345 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 346 | |||
| 347 | WARN_ON(!scard_ops); | ||
| 348 | |||
| 349 | switch (kcontrol->id.numid) { | ||
| 350 | case PLAYBACK_MUTE: | ||
| 351 | if (intelmaddata->output_sel == STEREO_HEADPHONE) | ||
| 352 | cntl_list = PMIC_SND_LEFT_HP_MUTE; | ||
| 353 | else if ((intelmaddata->output_sel == INTERNAL_SPKR) || | ||
| 354 | (intelmaddata->output_sel == MONO_EARPIECE)) | ||
| 355 | cntl_list = PMIC_SND_LEFT_SPEAKER_MUTE; | ||
| 356 | break; | ||
| 357 | |||
| 358 | case CAPTURE_MUTE: | ||
| 359 | if (intelmaddata->input_sel == DMIC) | ||
| 360 | cntl_list = PMIC_SND_DMIC_MUTE; | ||
| 361 | else if (intelmaddata->input_sel == AMIC) | ||
| 362 | cntl_list = PMIC_SND_AMIC_MUTE; | ||
| 363 | else if (intelmaddata->input_sel == HS_MIC) | ||
| 364 | cntl_list = PMIC_SND_HP_MIC_MUTE; | ||
| 365 | break; | ||
| 366 | case MASTER_MUTE: | ||
| 367 | uval->value.integer.value[0] = intelmaddata->master_mute; | ||
| 368 | return 0; | ||
| 369 | default: | ||
| 370 | return -EINVAL; | ||
| 371 | } | ||
| 372 | |||
| 373 | ret_val = scard_ops->get_mute(cntl_list, &value); | ||
| 374 | uval->value.integer.value[0] = value; | ||
| 375 | return ret_val; | ||
| 376 | } | ||
| 377 | |||
| 378 | /** | ||
| 379 | * snd_intelmad_volume_set - sets the volume control's info | ||
| 380 | * | ||
| 381 | * @kcontrol: pointer to the control | ||
| 382 | * @uval: pointer to the structure where the control's info is | ||
| 383 | * available to be set | ||
| 384 | * | ||
| 385 | * This function is called when .set function of a control is invoked from app | ||
| 386 | */ | ||
| 387 | static int snd_intelmad_volume_set(struct snd_kcontrol *kcontrol, | ||
| 388 | struct snd_ctl_elem_value *uval) | ||
| 389 | { | ||
| 390 | |||
| 391 | int ret_val, cntl_list[2] = {0,}; | ||
| 392 | struct snd_intelmad *intelmaddata; | ||
| 393 | struct snd_pmic_ops *scard_ops; | ||
| 394 | |||
| 395 | pr_debug("volume set called:%ld %ld\n", | ||
| 396 | uval->value.integer.value[0], | ||
| 397 | uval->value.integer.value[1]); | ||
| 398 | |||
| 399 | WARN_ON(!uval); | ||
| 400 | WARN_ON(!kcontrol); | ||
| 401 | |||
| 402 | intelmaddata = kcontrol->private_data; | ||
| 403 | |||
| 404 | WARN_ON(!intelmaddata->sstdrv_ops); | ||
| 405 | |||
| 406 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 407 | |||
| 408 | WARN_ON(!scard_ops); | ||
| 409 | |||
| 410 | switch (kcontrol->id.numid) { | ||
| 411 | case PLAYBACK_VOL: | ||
| 412 | cntl_list[0] = PMIC_SND_LEFT_PB_VOL; | ||
| 413 | cntl_list[1] = PMIC_SND_RIGHT_PB_VOL; | ||
| 414 | break; | ||
| 415 | |||
| 416 | case CAPTURE_VOL: | ||
| 417 | cntl_list[0] = PMIC_SND_CAPTURE_VOL; | ||
| 418 | break; | ||
| 419 | |||
| 420 | case MASTER_VOL: | ||
| 421 | cntl_list[0] = PMIC_SND_LEFT_MASTER_VOL; | ||
| 422 | cntl_list[1] = PMIC_SND_RIGHT_MASTER_VOL; | ||
| 423 | break; | ||
| 424 | |||
| 425 | default: | ||
| 426 | return -EINVAL; | ||
| 427 | } | ||
| 428 | |||
| 429 | ret_val = scard_ops->set_vol(cntl_list[0], | ||
| 430 | uval->value.integer.value[0]); | ||
| 431 | if (ret_val) | ||
| 432 | return ret_val; | ||
| 433 | |||
| 434 | if (kcontrol->id.numid == PLAYBACK_VOL || | ||
| 435 | kcontrol->id.numid == MASTER_VOL) | ||
| 436 | ret_val = scard_ops->set_vol(cntl_list[1], | ||
| 437 | uval->value.integer.value[1]); | ||
| 438 | return ret_val; | ||
| 439 | } | ||
| 440 | |||
| 441 | /** | ||
| 442 | * snd_intelmad_mute_set - sets the mute control's info | ||
| 443 | * | ||
| 444 | * @kcontrol: pointer to the control | ||
| 445 | * @uval: pointer to the structure where the control's info is | ||
| 446 | * available to be set | ||
| 447 | * | ||
| 448 | * This function is called when .set function of a control is invoked from app | ||
| 449 | */ | ||
| 450 | static int snd_intelmad_mute_set(struct snd_kcontrol *kcontrol, | ||
| 451 | struct snd_ctl_elem_value *uval) | ||
| 452 | { | ||
| 453 | int cntl_list[2] = {0,}, ret_val; | ||
| 454 | struct snd_intelmad *intelmaddata; | ||
| 455 | struct snd_pmic_ops *scard_ops; | ||
| 456 | |||
| 457 | pr_debug("snd_intelmad_mute_set called\n"); | ||
| 458 | |||
| 459 | WARN_ON(!uval); | ||
| 460 | WARN_ON(!kcontrol); | ||
| 461 | |||
| 462 | intelmaddata = kcontrol->private_data; | ||
| 463 | |||
| 464 | WARN_ON(!intelmaddata->sstdrv_ops); | ||
| 465 | |||
| 466 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 467 | |||
| 468 | WARN_ON(!scard_ops); | ||
| 469 | |||
| 470 | kcontrol->private_value = uval->value.integer.value[0]; | ||
| 471 | |||
| 472 | switch (kcontrol->id.numid) { | ||
| 473 | case PLAYBACK_MUTE: | ||
| 474 | if (intelmaddata->output_sel == STEREO_HEADPHONE) { | ||
| 475 | cntl_list[0] = PMIC_SND_LEFT_HP_MUTE; | ||
| 476 | cntl_list[1] = PMIC_SND_RIGHT_HP_MUTE; | ||
| 477 | } else if ((intelmaddata->output_sel == INTERNAL_SPKR) || | ||
| 478 | (intelmaddata->output_sel == MONO_EARPIECE)) { | ||
| 479 | cntl_list[0] = PMIC_SND_LEFT_SPEAKER_MUTE; | ||
| 480 | cntl_list[1] = PMIC_SND_RIGHT_SPEAKER_MUTE; | ||
| 481 | } | ||
| 482 | break; | ||
| 483 | |||
| 484 | case CAPTURE_MUTE:/*based on sel device mute the i/p dev*/ | ||
| 485 | if (intelmaddata->input_sel == DMIC) | ||
| 486 | cntl_list[0] = PMIC_SND_DMIC_MUTE; | ||
| 487 | else if (intelmaddata->input_sel == AMIC) | ||
| 488 | cntl_list[0] = PMIC_SND_AMIC_MUTE; | ||
| 489 | else if (intelmaddata->input_sel == HS_MIC) | ||
| 490 | cntl_list[0] = PMIC_SND_HP_MIC_MUTE; | ||
| 491 | break; | ||
| 492 | case MASTER_MUTE: | ||
| 493 | cntl_list[0] = PMIC_SND_MUTE_ALL; | ||
| 494 | intelmaddata->master_mute = uval->value.integer.value[0]; | ||
| 495 | break; | ||
| 496 | default: | ||
| 497 | return -EINVAL; | ||
| 498 | } | ||
| 499 | |||
| 500 | ret_val = scard_ops->set_mute(cntl_list[0], | ||
| 501 | uval->value.integer.value[0]); | ||
| 502 | if (ret_val) | ||
| 503 | return ret_val; | ||
| 504 | |||
| 505 | if (kcontrol->id.numid == PLAYBACK_MUTE) | ||
| 506 | ret_val = scard_ops->set_mute(cntl_list[1], | ||
| 507 | uval->value.integer.value[0]); | ||
| 508 | return ret_val; | ||
| 509 | } | ||
| 510 | |||
| 511 | /** | ||
| 512 | * snd_intelmad_device_get - get the device select control's info | ||
| 513 | * | ||
| 514 | * @kcontrol: pointer to the control | ||
| 515 | * @uval: pointer to the structure where the control's info is | ||
| 516 | * to be filled | ||
| 517 | * | ||
| 518 | * This function is called when .get function of a control is invoked from app | ||
| 519 | */ | ||
| 520 | static int snd_intelmad_device_get(struct snd_kcontrol *kcontrol, | ||
| 521 | struct snd_ctl_elem_value *uval) | ||
| 522 | { | ||
| 523 | struct snd_intelmad *intelmaddata; | ||
| 524 | struct snd_pmic_ops *scard_ops; | ||
| 525 | pr_debug("device_get called\n"); | ||
| 526 | |||
| 527 | WARN_ON(!uval); | ||
| 528 | WARN_ON(!kcontrol); | ||
| 529 | |||
| 530 | intelmaddata = kcontrol->private_data; | ||
| 531 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 532 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { | ||
| 533 | if (kcontrol->id.numid == OUTPUT_SEL) | ||
| 534 | uval->value.enumerated.item[0] = | ||
| 535 | scard_ops->output_dev_id; | ||
| 536 | else if (kcontrol->id.numid == INPUT_SEL) | ||
| 537 | uval->value.enumerated.item[0] = | ||
| 538 | scard_ops->input_dev_id; | ||
| 539 | else if (kcontrol->id.numid == LINEOUT_SEL_MFLD) | ||
| 540 | uval->value.enumerated.item[0] = | ||
| 541 | scard_ops->lineout_dev_id; | ||
| 542 | else | ||
| 543 | return -EINVAL; | ||
| 544 | } else if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) { | ||
| 545 | if (kcontrol->id.numid == OUTPUT_SEL) | ||
| 546 | /* There is a mismatch here. | ||
| 547 | * ALSA expects 1 for internal speaker. | ||
| 548 | * But internally, we may give 2 for internal speaker. | ||
| 549 | */ | ||
| 550 | if (scard_ops->output_dev_id == MONO_EARPIECE || | ||
| 551 | scard_ops->output_dev_id == INTERNAL_SPKR) | ||
| 552 | uval->value.enumerated.item[0] = MONO_EARPIECE; | ||
| 553 | else if (scard_ops->output_dev_id == STEREO_HEADPHONE) | ||
| 554 | uval->value.enumerated.item[0] = | ||
| 555 | STEREO_HEADPHONE; | ||
| 556 | else | ||
| 557 | return -EINVAL; | ||
| 558 | else if (kcontrol->id.numid == INPUT_SEL) | ||
| 559 | uval->value.enumerated.item[0] = | ||
| 560 | scard_ops->input_dev_id; | ||
| 561 | else | ||
| 562 | return -EINVAL; | ||
| 563 | } else | ||
| 564 | uval->value.enumerated.item[0] = kcontrol->private_value; | ||
| 565 | return 0; | ||
| 566 | } | ||
| 567 | |||
| 568 | /** | ||
| 569 | * snd_intelmad_device_set - set the device select control's info | ||
| 570 | * | ||
| 571 | * @kcontrol: pointer to the control | ||
| 572 | * @uval: pointer to the structure where the control's info is | ||
| 573 | * available to be set | ||
| 574 | * | ||
| 575 | * This function is called when .set function of a control is invoked from app | ||
| 576 | */ | ||
| 577 | static int snd_intelmad_device_set(struct snd_kcontrol *kcontrol, | ||
| 578 | struct snd_ctl_elem_value *uval) | ||
| 579 | { | ||
| 580 | struct snd_intelmad *intelmaddata; | ||
| 581 | struct snd_pmic_ops *scard_ops; | ||
| 582 | int ret_val = 0, vendor, status; | ||
| 583 | struct intel_sst_pcm_control *pcm_control; | ||
| 584 | |||
| 585 | pr_debug("snd_intelmad_device_set called\n"); | ||
| 586 | |||
| 587 | WARN_ON(!uval); | ||
| 588 | WARN_ON(!kcontrol); | ||
| 589 | status = -1; | ||
| 590 | |||
| 591 | intelmaddata = kcontrol->private_data; | ||
| 592 | |||
| 593 | WARN_ON(!intelmaddata->sstdrv_ops); | ||
| 594 | |||
| 595 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 596 | |||
| 597 | WARN_ON(!scard_ops); | ||
| 598 | |||
| 599 | /* store value with driver */ | ||
| 600 | kcontrol->private_value = uval->value.enumerated.item[0]; | ||
| 601 | |||
| 602 | switch (kcontrol->id.numid) { | ||
| 603 | case OUTPUT_SEL: | ||
| 604 | ret_val = scard_ops->set_output_dev( | ||
| 605 | uval->value.enumerated.item[0]); | ||
| 606 | intelmaddata->output_sel = uval->value.enumerated.item[0]; | ||
| 607 | break; | ||
| 608 | case INPUT_SEL: | ||
| 609 | vendor = intelmaddata->sstdrv_ops->vendor_id; | ||
| 610 | if ((vendor == SND_MX) || (vendor == SND_FS)) { | ||
| 611 | pcm_control = intelmaddata->sstdrv_ops->pcm_control; | ||
| 612 | if (uval->value.enumerated.item[0] == HS_MIC) | ||
| 613 | status = 1; | ||
| 614 | else | ||
| 615 | status = 0; | ||
| 616 | pcm_control->device_control( | ||
| 617 | SST_ENABLE_RX_TIME_SLOT, &status); | ||
| 618 | } | ||
| 619 | ret_val = scard_ops->set_input_dev( | ||
| 620 | uval->value.enumerated.item[0]); | ||
| 621 | intelmaddata->input_sel = uval->value.enumerated.item[0]; | ||
| 622 | break; | ||
| 623 | case LINEOUT_SEL_MFLD: | ||
| 624 | ret_val = scard_ops->set_lineout_dev( | ||
| 625 | uval->value.enumerated.item[0]); | ||
| 626 | intelmaddata->lineout_sel = uval->value.enumerated.item[0]; | ||
| 627 | break; | ||
| 628 | default: | ||
| 629 | return -EINVAL; | ||
| 630 | } | ||
| 631 | kcontrol->private_value = uval->value.enumerated.item[0]; | ||
| 632 | return ret_val; | ||
| 633 | } | ||
| 634 | |||
| 635 | static int snd_intelmad_device_dmic_get(struct snd_kcontrol *kcontrol, | ||
| 636 | struct snd_ctl_elem_value *uval) | ||
| 637 | { | ||
| 638 | struct snd_intelmad *intelmaddata; | ||
| 639 | struct snd_pmic_ops *scard_ops; | ||
| 640 | |||
| 641 | WARN_ON(!uval); | ||
| 642 | WARN_ON(!kcontrol); | ||
| 643 | |||
| 644 | intelmaddata = kcontrol->private_data; | ||
| 645 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 646 | |||
| 647 | if (scard_ops->input_dev_id != DMIC) { | ||
| 648 | pr_debug("input dev = 0x%x\n", scard_ops->input_dev_id); | ||
| 649 | return 0; | ||
| 650 | } | ||
| 651 | |||
| 652 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) | ||
| 653 | uval->value.enumerated.item[0] = kcontrol->private_value; | ||
| 654 | else | ||
| 655 | pr_debug(" CPU id = 0x%xis invalid.\n", | ||
| 656 | intelmaddata->cpu_id); | ||
| 657 | return 0; | ||
| 658 | } | ||
| 659 | |||
| 660 | void msic_set_bit(u8 index, unsigned int *available_dmics) | ||
| 661 | { | ||
| 662 | *available_dmics |= (1 << index); | ||
| 663 | } | ||
| 664 | |||
| 665 | void msic_clear_bit(u8 index, unsigned int *available_dmics) | ||
| 666 | { | ||
| 667 | *available_dmics &= ~(1 << index); | ||
| 668 | } | ||
| 669 | |||
| 670 | int msic_is_set_bit(u8 index, unsigned int *available_dmics) | ||
| 671 | { | ||
| 672 | int ret_val; | ||
| 673 | |||
| 674 | ret_val = (*available_dmics & (1 << index)); | ||
| 675 | return ret_val; | ||
| 676 | } | ||
| 677 | |||
| 678 | static int snd_intelmad_device_dmic_set(struct snd_kcontrol *kcontrol, | ||
| 679 | struct snd_ctl_elem_value *uval) | ||
| 680 | { | ||
| 681 | struct snd_intelmad *intelmaddata; | ||
| 682 | struct snd_pmic_ops *scard_ops; | ||
| 683 | int i, dmic_index; | ||
| 684 | unsigned int available_dmics; | ||
| 685 | int jump_count; | ||
| 686 | int max_dmics = ARRAY_SIZE(router_dmics); | ||
| 687 | |||
| 688 | WARN_ON(!uval); | ||
| 689 | WARN_ON(!kcontrol); | ||
| 690 | |||
| 691 | intelmaddata = kcontrol->private_data; | ||
| 692 | WARN_ON(!intelmaddata->sstdrv_ops); | ||
| 693 | |||
| 694 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 695 | WARN_ON(!scard_ops); | ||
| 696 | |||
| 697 | if (scard_ops->input_dev_id != DMIC) { | ||
| 698 | pr_debug("input dev = 0x%x\n", scard_ops->input_dev_id); | ||
| 699 | return 0; | ||
| 700 | } | ||
| 701 | |||
| 702 | available_dmics = scard_ops->available_dmics; | ||
| 703 | |||
| 704 | if (kcontrol->private_value > uval->value.enumerated.item[0]) { | ||
| 705 | pr_debug("jump count -1.\n"); | ||
| 706 | jump_count = -1; | ||
| 707 | } else { | ||
| 708 | pr_debug("jump count 1.\n"); | ||
| 709 | jump_count = 1; | ||
| 710 | } | ||
| 711 | |||
| 712 | dmic_index = uval->value.enumerated.item[0]; | ||
| 713 | pr_debug("set function. dmic_index = %d, avl_dmic = 0x%x\n", | ||
| 714 | dmic_index, available_dmics); | ||
| 715 | for (i = 0; i < max_dmics; i++) { | ||
| 716 | pr_debug("set function. loop index = 0x%x. dmic_index = 0x%x\n", | ||
| 717 | i, dmic_index); | ||
| 718 | if (!msic_is_set_bit(dmic_index, &available_dmics)) { | ||
| 719 | msic_clear_bit(kcontrol->private_value, | ||
| 720 | &available_dmics); | ||
| 721 | msic_set_bit(dmic_index, &available_dmics); | ||
| 722 | kcontrol->private_value = dmic_index; | ||
| 723 | scard_ops->available_dmics = available_dmics; | ||
| 724 | scard_ops->hw_dmic_map[kcontrol->id.numid-HW_CH_BASE] = | ||
| 725 | kcontrol->private_value; | ||
| 726 | scard_ops->set_hw_dmic_route | ||
| 727 | (kcontrol->id.numid-HW_CH_BASE); | ||
| 728 | return 0; | ||
| 729 | } | ||
| 730 | |||
| 731 | dmic_index += jump_count; | ||
| 732 | |||
| 733 | if (dmic_index > (max_dmics - 1) && jump_count == 1) { | ||
| 734 | pr_debug("Resettingthe dmic index to 0.\n"); | ||
| 735 | dmic_index = 0; | ||
| 736 | } else if (dmic_index == -1 && jump_count == -1) { | ||
| 737 | pr_debug("Resetting the dmic index to 5.\n"); | ||
| 738 | dmic_index = max_dmics - 1; | ||
| 739 | } | ||
| 740 | } | ||
| 741 | |||
| 742 | return -EINVAL; | ||
| 743 | } | ||
| 744 | |||
| 745 | static int snd_intelmad_device_dmic_info_mfld(struct snd_kcontrol *kcontrol, | ||
| 746 | struct snd_ctl_elem_info *uinfo) | ||
| 747 | { | ||
| 748 | struct snd_intelmad *intelmaddata; | ||
| 749 | struct snd_pmic_ops *scard_ops; | ||
| 750 | |||
| 751 | uinfo->count = MONO_CNTL; | ||
| 752 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
| 753 | uinfo->value.enumerated.items = ARRAY_SIZE(router_dmics); | ||
| 754 | |||
| 755 | intelmaddata = kcontrol->private_data; | ||
| 756 | WARN_ON(!intelmaddata->sstdrv_ops); | ||
| 757 | |||
| 758 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | ||
| 759 | WARN_ON(!scard_ops); | ||
| 760 | |||
| 761 | if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) | ||
| 762 | uinfo->value.enumerated.item = | ||
| 763 | uinfo->value.enumerated.items - 1; | ||
| 764 | |||
| 765 | strncpy(uinfo->value.enumerated.name, | ||
| 766 | router_dmics[uinfo->value.enumerated.item], | ||
| 767 | sizeof(uinfo->value.enumerated.name)-1); | ||
| 768 | |||
| 769 | |||
| 770 | msic_set_bit(kcontrol->private_value, &scard_ops->available_dmics); | ||
| 771 | pr_debug("info function. avl_dmic = 0x%x", | ||
| 772 | scard_ops->available_dmics); | ||
| 773 | |||
| 774 | scard_ops->hw_dmic_map[kcontrol->id.numid-HW_CH_BASE] = | ||
| 775 | kcontrol->private_value; | ||
| 776 | |||
| 777 | return 0; | ||
| 778 | } | ||
| 779 | |||
| 780 | struct snd_kcontrol_new snd_intelmad_controls_mrst[MAX_CTRL] __devinitdata = { | ||
| 781 | { | ||
| 782 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 783 | .name = "PCM Playback Source", | ||
| 784 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 785 | .info = snd_intelmad_device_info_mrst, | ||
| 786 | .get = snd_intelmad_device_get, | ||
| 787 | .put = snd_intelmad_device_set, | ||
| 788 | .private_value = 0, | ||
| 789 | }, | ||
| 790 | { | ||
| 791 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 792 | .name = "PCM Capture Source", | ||
| 793 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 794 | .info = snd_intelmad_device_info_mrst, | ||
| 795 | .get = snd_intelmad_device_get, | ||
| 796 | .put = snd_intelmad_device_set, | ||
| 797 | .private_value = 0, | ||
| 798 | }, | ||
| 799 | { | ||
| 800 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 801 | .name = "PCM Playback Volume", | ||
| 802 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 803 | .info = snd_intelmad_playback_volume_info, | ||
| 804 | .get = snd_intelmad_volume_get, | ||
| 805 | .put = snd_intelmad_volume_set, | ||
| 806 | .private_value = 0, | ||
| 807 | }, | ||
| 808 | { | ||
| 809 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 810 | .name = "PCM Playback Switch", | ||
| 811 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 812 | .info = snd_intelmad_mute_info, | ||
| 813 | .get = snd_intelmad_mute_get, | ||
| 814 | .put = snd_intelmad_mute_set, | ||
| 815 | .private_value = 0, | ||
| 816 | }, | ||
| 817 | { | ||
| 818 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 819 | .name = "PCM Capture Volume", | ||
| 820 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 821 | .info = snd_intelmad_capture_volume_info, | ||
| 822 | .get = snd_intelmad_volume_get, | ||
| 823 | .put = snd_intelmad_volume_set, | ||
| 824 | .private_value = 0, | ||
| 825 | }, | ||
| 826 | { | ||
| 827 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 828 | .name = "PCM Capture Switch", | ||
| 829 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 830 | .info = snd_intelmad_mute_info, | ||
| 831 | .get = snd_intelmad_mute_get, | ||
| 832 | .put = snd_intelmad_mute_set, | ||
| 833 | .private_value = 0, | ||
| 834 | }, | ||
| 835 | { | ||
| 836 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 837 | .name = "Master Playback Volume", | ||
| 838 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 839 | .info = snd_intelmad_master_volume_info, | ||
| 840 | .get = snd_intelmad_volume_get, | ||
| 841 | .put = snd_intelmad_volume_set, | ||
| 842 | .private_value = 0, | ||
| 843 | }, | ||
| 844 | { | ||
| 845 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 846 | .name = "Master Playback Switch", | ||
| 847 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 848 | .info = snd_intelmad_mute_info, | ||
| 849 | .get = snd_intelmad_mute_get, | ||
| 850 | .put = snd_intelmad_mute_set, | ||
| 851 | .private_value = 0, | ||
| 852 | }, | ||
| 853 | }; | ||
| 854 | |||
| 855 | struct snd_kcontrol_new | ||
| 856 | snd_intelmad_controls_mfld[MAX_CTRL_MFLD] __devinitdata = { | ||
| 857 | { | ||
| 858 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 859 | .name = "PCM Playback Source", | ||
| 860 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 861 | .info = snd_intelmad_device_info_mfld, | ||
| 862 | .get = snd_intelmad_device_get, | ||
| 863 | .put = snd_intelmad_device_set, | ||
| 864 | .private_value = 0, | ||
| 865 | }, | ||
| 866 | { | ||
| 867 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 868 | .name = "PCM Capture Source", | ||
| 869 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 870 | .info = snd_intelmad_device_info_mfld, | ||
| 871 | .get = snd_intelmad_device_get, | ||
| 872 | .put = snd_intelmad_device_set, | ||
| 873 | .private_value = 0, | ||
| 874 | }, | ||
| 875 | { | ||
| 876 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 877 | .name = "Line out", | ||
| 878 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 879 | .info = snd_intelmad_device_info_mfld, | ||
| 880 | .get = snd_intelmad_device_get, | ||
| 881 | .put = snd_intelmad_device_set, | ||
| 882 | .private_value = 0, | ||
| 883 | }, | ||
| 884 | { | ||
| 885 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 886 | .name = HW_CH_0, | ||
| 887 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 888 | .info = snd_intelmad_device_dmic_info_mfld, | ||
| 889 | .get = snd_intelmad_device_dmic_get, | ||
| 890 | .put = snd_intelmad_device_dmic_set, | ||
| 891 | .private_value = 0 | ||
| 892 | }, | ||
| 893 | { | ||
| 894 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 895 | .name = HW_CH_1, | ||
| 896 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 897 | .info = snd_intelmad_device_dmic_info_mfld, | ||
| 898 | .get = snd_intelmad_device_dmic_get, | ||
| 899 | .put = snd_intelmad_device_dmic_set, | ||
| 900 | .private_value = 1 | ||
| 901 | }, | ||
| 902 | { | ||
| 903 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 904 | .name = HW_CH_2, | ||
| 905 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 906 | .info = snd_intelmad_device_dmic_info_mfld, | ||
| 907 | .get = snd_intelmad_device_dmic_get, | ||
| 908 | .put = snd_intelmad_device_dmic_set, | ||
| 909 | .private_value = 2 | ||
| 910 | }, | ||
| 911 | { | ||
| 912 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
| 913 | .name = HW_CH_3, | ||
| 914 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
| 915 | .info = snd_intelmad_device_dmic_info_mfld, | ||
| 916 | .get = snd_intelmad_device_dmic_get, | ||
| 917 | .put = snd_intelmad_device_dmic_set, | ||
| 918 | .private_value = 3 | ||
| 919 | } | ||
| 920 | }; | ||
| 921 | |||
diff --git a/drivers/staging/intel_sst/intelmid_msic_control.c b/drivers/staging/intel_sst/intelmid_msic_control.c new file mode 100644 index 00000000000..70cdb169781 --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_msic_control.c | |||
| @@ -0,0 +1,1047 @@ | |||
| 1 | /* | ||
| 2 | * intelmid_vm_control.c - Intel Sound card driver for MID | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Intel Corp | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * | ||
| 7 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; version 2 of the License. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but | ||
| 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 16 | * General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along | ||
| 19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 20 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 21 | * | ||
| 22 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 23 | * | ||
| 24 | * This file contains the control operations of msic vendors | ||
| 25 | */ | ||
| 26 | |||
| 27 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 28 | |||
| 29 | #include <linux/pci.h> | ||
| 30 | #include <linux/file.h> | ||
| 31 | #include <linux/delay.h> | ||
| 32 | #include <sound/control.h> | ||
| 33 | #include "intel_sst.h" | ||
| 34 | #include <linux/input.h> | ||
| 35 | #include "intelmid_snd_control.h" | ||
| 36 | #include "intelmid.h" | ||
| 37 | |||
| 38 | #define AUDIOMUX12 0x24c | ||
| 39 | #define AUDIOMUX34 0x24d | ||
| 40 | |||
| 41 | static int msic_init_card(void) | ||
| 42 | { | ||
| 43 | struct sc_reg_access sc_access[] = { | ||
| 44 | /* dmic configuration */ | ||
| 45 | {0x241, 0x85, 0}, | ||
| 46 | {0x242, 0x02, 0}, | ||
| 47 | /* audio paths config */ | ||
| 48 | {0x24C, 0x10, 0}, | ||
| 49 | {0x24D, 0x32, 0}, | ||
| 50 | /* PCM2 interface slots */ | ||
| 51 | /* preconfigured slots for 0-5 both tx, rx */ | ||
| 52 | {0x272, 0x10, 0}, | ||
| 53 | {0x273, 0x32, 0}, | ||
| 54 | {0x274, 0xFF, 0}, | ||
| 55 | {0x275, 0x10, 0}, | ||
| 56 | {0x276, 0x32, 0}, | ||
| 57 | {0x277, 0x54, 0}, | ||
| 58 | /*Sinc5 decimator*/ | ||
| 59 | {0x24E, 0x28, 0}, | ||
| 60 | /*TI vibra w/a settings*/ | ||
| 61 | {0x384, 0x80, 0}, | ||
| 62 | {0x385, 0x80, 0}, | ||
| 63 | {0x267, 0x00, 0}, | ||
| 64 | {0x261, 0x00, 0}, | ||
| 65 | /* pcm port setting */ | ||
| 66 | {0x278, 0x00, 0}, | ||
| 67 | {0x27B, 0x01, 0}, | ||
| 68 | {0x27C, 0x0a, 0}, | ||
| 69 | /* Set vol HSLRVOLCTRL, IHFVOL */ | ||
| 70 | {0x259, 0x08, 0}, | ||
| 71 | {0x25A, 0x08, 0}, | ||
| 72 | {0x25B, 0x08, 0}, | ||
| 73 | {0x25C, 0x08, 0}, | ||
| 74 | /* HSEPRXCTRL Enable the headset left and right FIR filters */ | ||
| 75 | {0x250, 0x30, 0}, | ||
| 76 | /* HSMIXER */ | ||
| 77 | {0x256, 0x11, 0}, | ||
| 78 | /* amic configuration */ | ||
| 79 | {0x249, 0x01, 0x0}, | ||
| 80 | {0x24A, 0x01, 0x0}, | ||
| 81 | /* unmask ocaudio/accdet interrupts */ | ||
| 82 | {0x1d, 0x00, 0x00}, | ||
| 83 | {0x1e, 0x00, 0x00}, | ||
| 84 | }; | ||
| 85 | snd_msic_ops.card_status = SND_CARD_INIT_DONE; | ||
| 86 | sst_sc_reg_access(sc_access, PMIC_WRITE, 28); | ||
| 87 | snd_msic_ops.pb_on = 0; | ||
| 88 | snd_msic_ops.pbhs_on = 0; | ||
| 89 | snd_msic_ops.cap_on = 0; | ||
| 90 | snd_msic_ops.input_dev_id = DMIC; /*def dev*/ | ||
| 91 | snd_msic_ops.output_dev_id = STEREO_HEADPHONE; | ||
| 92 | snd_msic_ops.jack_interrupt_status = false; | ||
| 93 | pr_debug("msic init complete!!\n"); | ||
| 94 | return 0; | ||
| 95 | } | ||
| 96 | static int msic_line_out_restore(u8 value) | ||
| 97 | { | ||
| 98 | struct sc_reg_access hs_drv_en[] = { | ||
| 99 | {0x25d, 0x03, 0x03}, | ||
| 100 | }; | ||
| 101 | struct sc_reg_access ep_drv_en[] = { | ||
| 102 | {0x25d, 0x40, 0x40}, | ||
| 103 | }; | ||
| 104 | struct sc_reg_access ihf_drv_en[] = { | ||
| 105 | {0x25d, 0x0c, 0x0c}, | ||
| 106 | }; | ||
| 107 | struct sc_reg_access vib1_drv_en[] = { | ||
| 108 | {0x25d, 0x10, 0x10}, | ||
| 109 | }; | ||
| 110 | struct sc_reg_access vib2_drv_en[] = { | ||
| 111 | {0x25d, 0x20, 0x20}, | ||
| 112 | }; | ||
| 113 | struct sc_reg_access pmode_enable[] = { | ||
| 114 | {0x381, 0x10, 0x10}, | ||
| 115 | }; | ||
| 116 | int retval = 0; | ||
| 117 | |||
| 118 | pr_debug("msic_lineout_restore_lineout_dev:%d\n", value); | ||
| 119 | |||
| 120 | switch (value) { | ||
| 121 | case HEADSET: | ||
| 122 | pr_debug("Selecting Lineout-HEADSET-restore\n"); | ||
| 123 | if (snd_msic_ops.output_dev_id == STEREO_HEADPHONE) | ||
| 124 | retval = sst_sc_reg_access(hs_drv_en, | ||
| 125 | PMIC_READ_MODIFY, 1); | ||
| 126 | else | ||
| 127 | retval = sst_sc_reg_access(ep_drv_en, | ||
| 128 | PMIC_READ_MODIFY, 1); | ||
| 129 | break; | ||
| 130 | case IHF: | ||
| 131 | pr_debug("Selecting Lineout-IHF-restore\n"); | ||
| 132 | retval = sst_sc_reg_access(ihf_drv_en, PMIC_READ_MODIFY, 1); | ||
| 133 | if (retval) | ||
| 134 | return retval; | ||
| 135 | retval = sst_sc_reg_access(pmode_enable, PMIC_READ_MODIFY, 1); | ||
| 136 | break; | ||
| 137 | case VIBRA1: | ||
| 138 | pr_debug("Selecting Lineout-Vibra1-restore\n"); | ||
| 139 | retval = sst_sc_reg_access(vib1_drv_en, PMIC_READ_MODIFY, 1); | ||
| 140 | break; | ||
| 141 | case VIBRA2: | ||
| 142 | pr_debug("Selecting Lineout-VIBRA2-restore\n"); | ||
| 143 | retval = sst_sc_reg_access(vib2_drv_en, PMIC_READ_MODIFY, 1); | ||
| 144 | break; | ||
| 145 | case NONE: | ||
| 146 | pr_debug("Selecting Lineout-NONE-restore\n"); | ||
| 147 | break; | ||
| 148 | default: | ||
| 149 | return -EINVAL; | ||
| 150 | } | ||
| 151 | return retval; | ||
| 152 | } | ||
| 153 | static int msic_get_lineout_prvstate(void) | ||
| 154 | { | ||
| 155 | struct sc_reg_access hs_ihf_drv[2] = { | ||
| 156 | {0x257, 0x0, 0x0}, | ||
| 157 | {0x25d, 0x0, 0x0}, | ||
| 158 | }; | ||
| 159 | struct sc_reg_access vib1drv[2] = { | ||
| 160 | {0x264, 0x0, 0x0}, | ||
| 161 | {0x25D, 0x0, 0x0}, | ||
| 162 | }; | ||
| 163 | struct sc_reg_access vib2drv[2] = { | ||
| 164 | {0x26A, 0x0, 0x0}, | ||
| 165 | {0x25D, 0x0, 0x0}, | ||
| 166 | }; | ||
| 167 | int retval = 0, drv_en, dac_en, dev_id, mask; | ||
| 168 | for (dev_id = 0; dev_id < snd_msic_ops.line_out_names_cnt; dev_id++) { | ||
| 169 | switch (dev_id) { | ||
| 170 | case HEADSET: | ||
| 171 | pr_debug("msic_get_lineout_prvs_state: HEADSET\n"); | ||
| 172 | sst_sc_reg_access(hs_ihf_drv, PMIC_READ, 2); | ||
| 173 | |||
| 174 | mask = (MASK0|MASK1); | ||
| 175 | dac_en = (hs_ihf_drv[0].value) & mask; | ||
| 176 | |||
| 177 | mask = ((MASK0|MASK1)|MASK6); | ||
| 178 | drv_en = (hs_ihf_drv[1].value) & mask; | ||
| 179 | |||
| 180 | if (dac_en && (!drv_en)) { | ||
| 181 | snd_msic_ops.prev_lineout_dev_id = HEADSET; | ||
| 182 | return retval; | ||
| 183 | } | ||
| 184 | break; | ||
| 185 | case IHF: | ||
| 186 | pr_debug("msic_get_lineout_prvstate: IHF\n"); | ||
| 187 | sst_sc_reg_access(hs_ihf_drv, PMIC_READ, 2); | ||
| 188 | |||
| 189 | mask = (MASK2 | MASK3); | ||
| 190 | dac_en = (hs_ihf_drv[0].value) & mask; | ||
| 191 | |||
| 192 | mask = (MASK2 | MASK3); | ||
| 193 | drv_en = (hs_ihf_drv[1].value) & mask; | ||
| 194 | |||
| 195 | if (dac_en && (!drv_en)) { | ||
| 196 | snd_msic_ops.prev_lineout_dev_id = IHF; | ||
| 197 | return retval; | ||
| 198 | } | ||
| 199 | break; | ||
| 200 | case VIBRA1: | ||
| 201 | pr_debug("msic_get_lineout_prvstate: vibra1\n"); | ||
| 202 | sst_sc_reg_access(vib1drv, PMIC_READ, 2); | ||
| 203 | |||
| 204 | mask = MASK1; | ||
| 205 | dac_en = (vib1drv[0].value) & mask; | ||
| 206 | |||
| 207 | mask = MASK4; | ||
| 208 | drv_en = (vib1drv[1].value) & mask; | ||
| 209 | |||
| 210 | if (dac_en && (!drv_en)) { | ||
| 211 | snd_msic_ops.prev_lineout_dev_id = VIBRA1; | ||
| 212 | return retval; | ||
| 213 | } | ||
| 214 | break; | ||
| 215 | case VIBRA2: | ||
| 216 | pr_debug("msic_get_lineout_prvstate: vibra2\n"); | ||
| 217 | sst_sc_reg_access(vib2drv, PMIC_READ, 2); | ||
| 218 | |||
| 219 | mask = MASK1; | ||
| 220 | dac_en = (vib2drv[0].value) & mask; | ||
| 221 | |||
| 222 | mask = MASK5; | ||
| 223 | drv_en = ((vib2drv[1].value) & mask); | ||
| 224 | |||
| 225 | if (dac_en && (!drv_en)) { | ||
| 226 | snd_msic_ops.prev_lineout_dev_id = VIBRA2; | ||
| 227 | return retval; | ||
| 228 | } | ||
| 229 | break; | ||
| 230 | case NONE: | ||
| 231 | pr_debug("msic_get_lineout_prvstate: NONE\n"); | ||
| 232 | snd_msic_ops.prev_lineout_dev_id = NONE; | ||
| 233 | return retval; | ||
| 234 | default: | ||
| 235 | pr_debug("Invalid device id\n"); | ||
| 236 | snd_msic_ops.prev_lineout_dev_id = NONE; | ||
| 237 | return -EINVAL; | ||
| 238 | } | ||
| 239 | } | ||
| 240 | return retval; | ||
| 241 | } | ||
| 242 | static int msic_set_selected_lineout_dev(u8 value) | ||
| 243 | { | ||
| 244 | struct sc_reg_access lout_hs[] = { | ||
| 245 | {0x25e, 0x33, 0xFF}, | ||
| 246 | {0x25d, 0x0, 0x43}, | ||
| 247 | }; | ||
| 248 | struct sc_reg_access lout_ihf[] = { | ||
| 249 | {0x25e, 0x55, 0xff}, | ||
| 250 | {0x25d, 0x0, 0x0c}, | ||
| 251 | }; | ||
| 252 | struct sc_reg_access lout_vibra1[] = { | ||
| 253 | |||
| 254 | {0x25e, 0x61, 0xff}, | ||
| 255 | {0x25d, 0x0, 0x10}, | ||
| 256 | }; | ||
| 257 | struct sc_reg_access lout_vibra2[] = { | ||
| 258 | |||
| 259 | {0x25e, 0x16, 0xff}, | ||
| 260 | {0x25d, 0x0, 0x20}, | ||
| 261 | }; | ||
| 262 | struct sc_reg_access lout_def[] = { | ||
| 263 | {0x25e, 0x66, 0x0}, | ||
| 264 | }; | ||
| 265 | struct sc_reg_access pmode_disable[] = { | ||
| 266 | {0x381, 0x00, 0x10}, | ||
| 267 | }; | ||
| 268 | struct sc_reg_access pmode_enable[] = { | ||
| 269 | {0x381, 0x10, 0x10}, | ||
| 270 | }; | ||
| 271 | int retval = 0; | ||
| 272 | |||
| 273 | pr_debug("msic_set_selected_lineout_dev:%d\n", value); | ||
| 274 | msic_get_lineout_prvstate(); | ||
| 275 | msic_line_out_restore(snd_msic_ops.prev_lineout_dev_id); | ||
| 276 | snd_msic_ops.lineout_dev_id = value; | ||
| 277 | |||
| 278 | switch (value) { | ||
| 279 | case HEADSET: | ||
| 280 | pr_debug("Selecting Lineout-HEADSET\n"); | ||
| 281 | if (snd_msic_ops.pb_on) | ||
| 282 | retval = sst_sc_reg_access(lout_hs, | ||
| 283 | PMIC_READ_MODIFY, 2); | ||
| 284 | if (retval) | ||
| 285 | return retval; | ||
| 286 | retval = sst_sc_reg_access(pmode_disable, | ||
| 287 | PMIC_READ_MODIFY, 1); | ||
| 288 | break; | ||
| 289 | case IHF: | ||
| 290 | pr_debug("Selecting Lineout-IHF\n"); | ||
| 291 | if (snd_msic_ops.pb_on) | ||
| 292 | retval = sst_sc_reg_access(lout_ihf, | ||
| 293 | PMIC_READ_MODIFY, 2); | ||
| 294 | if (retval) | ||
| 295 | return retval; | ||
| 296 | retval = sst_sc_reg_access(pmode_enable, | ||
| 297 | PMIC_READ_MODIFY, 1); | ||
| 298 | break; | ||
| 299 | case VIBRA1: | ||
| 300 | pr_debug("Selecting Lineout-Vibra1\n"); | ||
| 301 | if (snd_msic_ops.pb_on) | ||
| 302 | retval = sst_sc_reg_access(lout_vibra1, | ||
| 303 | PMIC_READ_MODIFY, 2); | ||
| 304 | if (retval) | ||
| 305 | return retval; | ||
| 306 | retval = sst_sc_reg_access(pmode_disable, | ||
| 307 | PMIC_READ_MODIFY, 1); | ||
| 308 | break; | ||
| 309 | case VIBRA2: | ||
| 310 | pr_debug("Selecting Lineout-VIBRA2\n"); | ||
| 311 | if (snd_msic_ops.pb_on) | ||
| 312 | retval = sst_sc_reg_access(lout_vibra2, | ||
| 313 | PMIC_READ_MODIFY, 2); | ||
| 314 | if (retval) | ||
| 315 | return retval; | ||
| 316 | retval = sst_sc_reg_access(pmode_disable, | ||
| 317 | PMIC_READ_MODIFY, 1); | ||
| 318 | break; | ||
| 319 | case NONE: | ||
| 320 | pr_debug("Selecting Lineout-NONE\n"); | ||
| 321 | retval = sst_sc_reg_access(lout_def, | ||
| 322 | PMIC_WRITE, 1); | ||
| 323 | if (retval) | ||
| 324 | return retval; | ||
| 325 | retval = sst_sc_reg_access(pmode_disable, | ||
| 326 | PMIC_READ_MODIFY, 1); | ||
| 327 | break; | ||
| 328 | default: | ||
| 329 | return -EINVAL; | ||
| 330 | } | ||
| 331 | return retval; | ||
| 332 | } | ||
| 333 | |||
| 334 | |||
| 335 | static int msic_power_up_pb(unsigned int device) | ||
| 336 | { | ||
| 337 | struct sc_reg_access vaud[] = { | ||
| 338 | /* turn on the audio power supplies */ | ||
| 339 | {0x0DB, 0x07, 0}, | ||
| 340 | }; | ||
| 341 | struct sc_reg_access pll[] = { | ||
| 342 | /* turn on PLL */ | ||
| 343 | {0x240, 0x20, 0}, | ||
| 344 | }; | ||
| 345 | struct sc_reg_access vhs[] = { | ||
| 346 | /* VHSP */ | ||
| 347 | {0x0DC, 0x3D, 0}, | ||
| 348 | /* VHSN */ | ||
| 349 | {0x0DD, 0x3F, 0}, | ||
| 350 | }; | ||
| 351 | struct sc_reg_access hsdac[] = { | ||
| 352 | {0x382, 0x40, 0x40}, | ||
| 353 | /* disable driver */ | ||
| 354 | {0x25D, 0x0, 0x43}, | ||
| 355 | /* DAC CONFIG ; both HP, LP on */ | ||
| 356 | {0x257, 0x03, 0x03}, | ||
| 357 | }; | ||
| 358 | struct sc_reg_access hs_filter[] = { | ||
| 359 | /* HSEPRXCTRL Enable the headset left and right FIR filters */ | ||
| 360 | {0x250, 0x30, 0}, | ||
| 361 | /* HSMIXER */ | ||
| 362 | {0x256, 0x11, 0}, | ||
| 363 | }; | ||
| 364 | struct sc_reg_access hs_enable[] = { | ||
| 365 | /* enable driver */ | ||
| 366 | {0x25D, 0x3, 0x3}, | ||
| 367 | {0x26C, 0x0, 0x2}, | ||
| 368 | /* unmute the headset */ | ||
| 369 | { 0x259, 0x80, 0x80}, | ||
| 370 | { 0x25A, 0x80, 0x80}, | ||
| 371 | }; | ||
| 372 | struct sc_reg_access vihf[] = { | ||
| 373 | /* VIHF ON */ | ||
| 374 | {0x0C9, 0x27, 0x00}, | ||
| 375 | }; | ||
| 376 | struct sc_reg_access ihf_filter[] = { | ||
| 377 | /* disable driver */ | ||
| 378 | {0x25D, 0x00, 0x0C}, | ||
| 379 | /*Filer DAC enable*/ | ||
| 380 | {0x251, 0x03, 0x03}, | ||
| 381 | {0x257, 0x0C, 0x0C}, | ||
| 382 | }; | ||
| 383 | struct sc_reg_access ihf_en[] = { | ||
| 384 | /*enable drv*/ | ||
| 385 | {0x25D, 0x0C, 0x0c}, | ||
| 386 | }; | ||
| 387 | struct sc_reg_access ihf_unmute[] = { | ||
| 388 | /*unmute headset*/ | ||
| 389 | {0x25B, 0x80, 0x80}, | ||
| 390 | {0x25C, 0x80, 0x80}, | ||
| 391 | }; | ||
| 392 | struct sc_reg_access epdac[] = { | ||
| 393 | /* disable driver */ | ||
| 394 | {0x25D, 0x0, 0x43}, | ||
| 395 | /* DAC CONFIG ; both HP, LP on */ | ||
| 396 | {0x257, 0x03, 0x03}, | ||
| 397 | }; | ||
| 398 | struct sc_reg_access ep_enable[] = { | ||
| 399 | /* enable driver */ | ||
| 400 | {0x25D, 0x40, 0x40}, | ||
| 401 | /* unmute the headset */ | ||
| 402 | { 0x259, 0x80, 0x80}, | ||
| 403 | { 0x25A, 0x80, 0x80}, | ||
| 404 | }; | ||
| 405 | struct sc_reg_access vib1_en[] = { | ||
| 406 | /* enable driver, ADC */ | ||
| 407 | {0x25D, 0x10, 0x10}, | ||
| 408 | {0x264, 0x02, 0x82}, | ||
| 409 | }; | ||
| 410 | struct sc_reg_access vib2_en[] = { | ||
| 411 | /* enable driver, ADC */ | ||
| 412 | {0x25D, 0x20, 0x20}, | ||
| 413 | {0x26A, 0x02, 0x82}, | ||
| 414 | }; | ||
| 415 | struct sc_reg_access pcm2_en[] = { | ||
| 416 | /* enable pcm 2 */ | ||
| 417 | {0x27C, 0x1, 0x1}, | ||
| 418 | }; | ||
| 419 | int retval = 0; | ||
| 420 | |||
| 421 | if (snd_msic_ops.card_status == SND_CARD_UN_INIT) { | ||
| 422 | retval = msic_init_card(); | ||
| 423 | if (retval) | ||
| 424 | return retval; | ||
| 425 | } | ||
| 426 | |||
| 427 | pr_debug("powering up pb.... Device %d\n", device); | ||
| 428 | sst_sc_reg_access(vaud, PMIC_WRITE, 1); | ||
| 429 | msleep(1); | ||
| 430 | sst_sc_reg_access(pll, PMIC_WRITE, 1); | ||
| 431 | msleep(1); | ||
| 432 | switch (device) { | ||
| 433 | case SND_SST_DEVICE_HEADSET: | ||
| 434 | snd_msic_ops.pb_on = 1; | ||
| 435 | snd_msic_ops.pbhs_on = 1; | ||
| 436 | if (snd_msic_ops.output_dev_id == STEREO_HEADPHONE) { | ||
| 437 | sst_sc_reg_access(vhs, PMIC_WRITE, 2); | ||
| 438 | sst_sc_reg_access(hsdac, PMIC_READ_MODIFY, 3); | ||
| 439 | sst_sc_reg_access(hs_filter, PMIC_WRITE, 2); | ||
| 440 | sst_sc_reg_access(hs_enable, PMIC_READ_MODIFY, 4); | ||
| 441 | } else { | ||
| 442 | sst_sc_reg_access(epdac, PMIC_READ_MODIFY, 2); | ||
| 443 | sst_sc_reg_access(hs_filter, PMIC_WRITE, 2); | ||
| 444 | sst_sc_reg_access(ep_enable, PMIC_READ_MODIFY, 3); | ||
| 445 | } | ||
| 446 | if (snd_msic_ops.lineout_dev_id == HEADSET) | ||
| 447 | msic_set_selected_lineout_dev(HEADSET); | ||
| 448 | break; | ||
| 449 | case SND_SST_DEVICE_IHF: | ||
| 450 | snd_msic_ops.pb_on = 1; | ||
| 451 | sst_sc_reg_access(vihf, PMIC_WRITE, 1); | ||
| 452 | sst_sc_reg_access(ihf_filter, PMIC_READ_MODIFY, 3); | ||
| 453 | sst_sc_reg_access(ihf_en, PMIC_READ_MODIFY, 1); | ||
| 454 | sst_sc_reg_access(ihf_unmute, PMIC_READ_MODIFY, 2); | ||
| 455 | if (snd_msic_ops.lineout_dev_id == IHF) | ||
| 456 | msic_set_selected_lineout_dev(IHF); | ||
| 457 | break; | ||
| 458 | |||
| 459 | case SND_SST_DEVICE_VIBRA: | ||
| 460 | snd_msic_ops.pb_on = 1; | ||
| 461 | sst_sc_reg_access(vib1_en, PMIC_READ_MODIFY, 2); | ||
| 462 | if (snd_msic_ops.lineout_dev_id == VIBRA1) | ||
| 463 | msic_set_selected_lineout_dev(VIBRA1); | ||
| 464 | break; | ||
| 465 | |||
| 466 | case SND_SST_DEVICE_HAPTIC: | ||
| 467 | snd_msic_ops.pb_on = 1; | ||
| 468 | sst_sc_reg_access(vib2_en, PMIC_READ_MODIFY, 2); | ||
| 469 | if (snd_msic_ops.lineout_dev_id == VIBRA2) | ||
| 470 | msic_set_selected_lineout_dev(VIBRA2); | ||
| 471 | break; | ||
| 472 | |||
| 473 | default: | ||
| 474 | pr_warn("Wrong Device %d, selected %d\n", | ||
| 475 | device, snd_msic_ops.output_dev_id); | ||
| 476 | } | ||
| 477 | return sst_sc_reg_access(pcm2_en, PMIC_READ_MODIFY, 1); | ||
| 478 | } | ||
| 479 | |||
| 480 | static int msic_power_up_cp(unsigned int device) | ||
| 481 | { | ||
| 482 | struct sc_reg_access vaud[] = { | ||
| 483 | /* turn on the audio power supplies */ | ||
| 484 | {0x0DB, 0x07, 0}, | ||
| 485 | }; | ||
| 486 | struct sc_reg_access pll[] = { | ||
| 487 | /* turn on PLL */ | ||
| 488 | {0x240, 0x20, 0}, | ||
| 489 | }; | ||
| 490 | struct sc_reg_access dmic_bias[] = { | ||
| 491 | /* Turn on AMIC supply */ | ||
| 492 | {0x247, 0xA0, 0xA0}, | ||
| 493 | }; | ||
| 494 | struct sc_reg_access dmic[] = { | ||
| 495 | /* mic demux enable */ | ||
| 496 | {0x245, 0x3F, 0x3F}, | ||
| 497 | {0x246, 0x07, 0x07}, | ||
| 498 | |||
| 499 | }; | ||
| 500 | struct sc_reg_access amic_bias[] = { | ||
| 501 | /* Turn on AMIC supply */ | ||
| 502 | {0x247, 0xFC, 0xFC}, | ||
| 503 | }; | ||
| 504 | struct sc_reg_access amic[] = { | ||
| 505 | /*MIC EN*/ | ||
| 506 | {0x249, 0x01, 0x01}, | ||
| 507 | {0x24A, 0x01, 0x01}, | ||
| 508 | /*ADC EN*/ | ||
| 509 | {0x248, 0x05, 0x0F}, | ||
| 510 | |||
| 511 | }; | ||
| 512 | struct sc_reg_access pcm2[] = { | ||
| 513 | /* enable pcm 2 */ | ||
| 514 | {0x27C, 0x1, 0x1}, | ||
| 515 | }; | ||
| 516 | struct sc_reg_access tx_on[] = { | ||
| 517 | /*wait for mic to stabalize before turning on audio channels*/ | ||
| 518 | {0x24F, 0x3C, 0x0}, | ||
| 519 | }; | ||
| 520 | int retval = 0; | ||
| 521 | |||
| 522 | if (snd_msic_ops.card_status == SND_CARD_UN_INIT) { | ||
| 523 | retval = msic_init_card(); | ||
| 524 | if (retval) | ||
| 525 | return retval; | ||
| 526 | } | ||
| 527 | |||
| 528 | pr_debug("powering up cp....%d\n", snd_msic_ops.input_dev_id); | ||
| 529 | sst_sc_reg_access(vaud, PMIC_WRITE, 1); | ||
| 530 | msleep(500);/*FIXME need optimzed value here*/ | ||
| 531 | sst_sc_reg_access(pll, PMIC_WRITE, 1); | ||
| 532 | msleep(1); | ||
| 533 | snd_msic_ops.cap_on = 1; | ||
| 534 | if (snd_msic_ops.input_dev_id == AMIC) { | ||
| 535 | sst_sc_reg_access(amic_bias, PMIC_READ_MODIFY, 1); | ||
| 536 | msleep(1); | ||
| 537 | sst_sc_reg_access(amic, PMIC_READ_MODIFY, 3); | ||
| 538 | } else { | ||
| 539 | sst_sc_reg_access(dmic_bias, PMIC_READ_MODIFY, 1); | ||
| 540 | msleep(1); | ||
| 541 | sst_sc_reg_access(dmic, PMIC_READ_MODIFY, 2); | ||
| 542 | } | ||
| 543 | msleep(1); | ||
| 544 | sst_sc_reg_access(tx_on, PMIC_WRITE, 1); | ||
| 545 | return sst_sc_reg_access(pcm2, PMIC_READ_MODIFY, 1); | ||
| 546 | } | ||
| 547 | |||
| 548 | static int msic_power_down(void) | ||
| 549 | { | ||
| 550 | struct sc_reg_access power_dn[] = { | ||
| 551 | /* VHSP */ | ||
| 552 | {0x0DC, 0xC4, 0}, | ||
| 553 | /* VHSN */ | ||
| 554 | {0x0DD, 0x04, 0}, | ||
| 555 | /* VIHF */ | ||
| 556 | {0x0C9, 0x24, 0}, | ||
| 557 | }; | ||
| 558 | struct sc_reg_access pll[] = { | ||
| 559 | /* turn off PLL*/ | ||
| 560 | {0x240, 0x00, 0x0}, | ||
| 561 | }; | ||
| 562 | struct sc_reg_access vaud[] = { | ||
| 563 | /* turn off VAUD*/ | ||
| 564 | {0x0DB, 0x04, 0}, | ||
| 565 | }; | ||
| 566 | |||
| 567 | pr_debug("powering dn msic\n"); | ||
| 568 | snd_msic_ops.pbhs_on = 0; | ||
| 569 | snd_msic_ops.pb_on = 0; | ||
| 570 | snd_msic_ops.cap_on = 0; | ||
| 571 | sst_sc_reg_access(power_dn, PMIC_WRITE, 3); | ||
| 572 | msleep(1); | ||
| 573 | sst_sc_reg_access(pll, PMIC_WRITE, 1); | ||
| 574 | msleep(1); | ||
| 575 | sst_sc_reg_access(vaud, PMIC_WRITE, 1); | ||
| 576 | return 0; | ||
| 577 | } | ||
| 578 | |||
| 579 | static int msic_power_down_pb(unsigned int device) | ||
| 580 | { | ||
| 581 | struct sc_reg_access drv_enable[] = { | ||
| 582 | {0x25D, 0x00, 0x00}, | ||
| 583 | }; | ||
| 584 | struct sc_reg_access hs_mute[] = { | ||
| 585 | {0x259, 0x80, 0x80}, | ||
| 586 | {0x25A, 0x80, 0x80}, | ||
| 587 | {0x26C, 0x02, 0x02}, | ||
| 588 | }; | ||
| 589 | struct sc_reg_access hs_off[] = { | ||
| 590 | {0x257, 0x00, 0x03}, | ||
| 591 | {0x250, 0x00, 0x30}, | ||
| 592 | {0x382, 0x00, 0x40}, | ||
| 593 | }; | ||
| 594 | struct sc_reg_access ihf_mute[] = { | ||
| 595 | {0x25B, 0x80, 0x80}, | ||
| 596 | {0x25C, 0x80, 0x80}, | ||
| 597 | }; | ||
| 598 | struct sc_reg_access ihf_off[] = { | ||
| 599 | {0x257, 0x00, 0x0C}, | ||
| 600 | {0x251, 0x00, 0x03}, | ||
| 601 | }; | ||
| 602 | struct sc_reg_access vib1_off[] = { | ||
| 603 | {0x264, 0x00, 0x82}, | ||
| 604 | }; | ||
| 605 | struct sc_reg_access vib2_off[] = { | ||
| 606 | {0x26A, 0x00, 0x82}, | ||
| 607 | }; | ||
| 608 | struct sc_reg_access lout_off[] = { | ||
| 609 | {0x25e, 0x66, 0x00}, | ||
| 610 | }; | ||
| 611 | struct sc_reg_access pmode_disable[] = { | ||
| 612 | {0x381, 0x00, 0x10}, | ||
| 613 | }; | ||
| 614 | |||
| 615 | |||
| 616 | |||
| 617 | pr_debug("powering dn pb for device %d\n", device); | ||
| 618 | switch (device) { | ||
| 619 | case SND_SST_DEVICE_HEADSET: | ||
| 620 | snd_msic_ops.pbhs_on = 0; | ||
| 621 | sst_sc_reg_access(hs_mute, PMIC_READ_MODIFY, 3); | ||
| 622 | drv_enable[0].mask = 0x43; | ||
| 623 | sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1); | ||
| 624 | sst_sc_reg_access(hs_off, PMIC_READ_MODIFY, 3); | ||
| 625 | if (snd_msic_ops.lineout_dev_id == HEADSET) | ||
| 626 | sst_sc_reg_access(lout_off, PMIC_WRITE, 1); | ||
| 627 | break; | ||
| 628 | |||
| 629 | case SND_SST_DEVICE_IHF: | ||
| 630 | sst_sc_reg_access(ihf_mute, PMIC_READ_MODIFY, 2); | ||
| 631 | drv_enable[0].mask = 0x0C; | ||
| 632 | sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1); | ||
| 633 | sst_sc_reg_access(ihf_off, PMIC_READ_MODIFY, 2); | ||
| 634 | if (snd_msic_ops.lineout_dev_id == IHF) { | ||
| 635 | sst_sc_reg_access(lout_off, PMIC_WRITE, 1); | ||
| 636 | sst_sc_reg_access(pmode_disable, PMIC_READ_MODIFY, 1); | ||
| 637 | } | ||
| 638 | break; | ||
| 639 | |||
| 640 | case SND_SST_DEVICE_VIBRA: | ||
| 641 | sst_sc_reg_access(vib1_off, PMIC_READ_MODIFY, 1); | ||
| 642 | drv_enable[0].mask = 0x10; | ||
| 643 | sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1); | ||
| 644 | if (snd_msic_ops.lineout_dev_id == VIBRA1) | ||
| 645 | sst_sc_reg_access(lout_off, PMIC_WRITE, 1); | ||
| 646 | break; | ||
| 647 | |||
| 648 | case SND_SST_DEVICE_HAPTIC: | ||
| 649 | sst_sc_reg_access(vib2_off, PMIC_READ_MODIFY, 1); | ||
| 650 | drv_enable[0].mask = 0x20; | ||
| 651 | sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1); | ||
| 652 | if (snd_msic_ops.lineout_dev_id == VIBRA2) | ||
| 653 | sst_sc_reg_access(lout_off, PMIC_WRITE, 1); | ||
| 654 | break; | ||
| 655 | } | ||
| 656 | return 0; | ||
| 657 | } | ||
| 658 | |||
| 659 | static int msic_power_down_cp(unsigned int device) | ||
| 660 | { | ||
| 661 | struct sc_reg_access dmic[] = { | ||
| 662 | {0x247, 0x00, 0xA0}, | ||
| 663 | {0x245, 0x00, 0x38}, | ||
| 664 | {0x246, 0x00, 0x07}, | ||
| 665 | }; | ||
| 666 | struct sc_reg_access amic[] = { | ||
| 667 | {0x248, 0x00, 0x05}, | ||
| 668 | {0x249, 0x00, 0x01}, | ||
| 669 | {0x24A, 0x00, 0x01}, | ||
| 670 | {0x247, 0x00, 0xA3}, | ||
| 671 | }; | ||
| 672 | struct sc_reg_access tx_off[] = { | ||
| 673 | {0x24F, 0x00, 0x3C}, | ||
| 674 | }; | ||
| 675 | |||
| 676 | pr_debug("powering dn cp....\n"); | ||
| 677 | snd_msic_ops.cap_on = 0; | ||
| 678 | sst_sc_reg_access(tx_off, PMIC_READ_MODIFY, 1); | ||
| 679 | if (snd_msic_ops.input_dev_id == DMIC) | ||
| 680 | sst_sc_reg_access(dmic, PMIC_READ_MODIFY, 3); | ||
| 681 | else | ||
| 682 | sst_sc_reg_access(amic, PMIC_READ_MODIFY, 4); | ||
| 683 | return 0; | ||
| 684 | } | ||
| 685 | |||
| 686 | static int msic_set_selected_output_dev(u8 value) | ||
| 687 | { | ||
| 688 | int retval = 0; | ||
| 689 | |||
| 690 | pr_debug("msic set selected output:%d\n", value); | ||
| 691 | snd_msic_ops.output_dev_id = value; | ||
| 692 | if (snd_msic_ops.pbhs_on) | ||
| 693 | msic_power_up_pb(SND_SST_DEVICE_HEADSET); | ||
| 694 | return retval; | ||
| 695 | } | ||
| 696 | |||
| 697 | static int msic_set_selected_input_dev(u8 value) | ||
| 698 | { | ||
| 699 | |||
| 700 | struct sc_reg_access sc_access_dmic[] = { | ||
| 701 | {0x24C, 0x10, 0x0}, | ||
| 702 | }; | ||
| 703 | struct sc_reg_access sc_access_amic[] = { | ||
| 704 | {0x24C, 0x76, 0x0}, | ||
| 705 | |||
| 706 | }; | ||
| 707 | int retval = 0; | ||
| 708 | |||
| 709 | pr_debug("msic_set_selected_input_dev:%d\n", value); | ||
| 710 | snd_msic_ops.input_dev_id = value; | ||
| 711 | switch (value) { | ||
| 712 | case AMIC: | ||
| 713 | pr_debug("Selecting AMIC1\n"); | ||
| 714 | retval = sst_sc_reg_access(sc_access_amic, PMIC_WRITE, 1); | ||
| 715 | break; | ||
| 716 | case DMIC: | ||
| 717 | pr_debug("Selecting DMIC1\n"); | ||
| 718 | retval = sst_sc_reg_access(sc_access_dmic, PMIC_WRITE, 1); | ||
| 719 | break; | ||
| 720 | default: | ||
| 721 | return -EINVAL; | ||
| 722 | |||
| 723 | } | ||
| 724 | if (snd_msic_ops.cap_on) | ||
| 725 | retval = msic_power_up_cp(SND_SST_DEVICE_CAPTURE); | ||
| 726 | return retval; | ||
| 727 | } | ||
| 728 | |||
| 729 | static int msic_set_hw_dmic_route(u8 hw_ch_index) | ||
| 730 | { | ||
| 731 | struct sc_reg_access sc_access_router; | ||
| 732 | int retval = -EINVAL; | ||
| 733 | |||
| 734 | switch (hw_ch_index) { | ||
| 735 | case HW_CH0: | ||
| 736 | sc_access_router.reg_addr = AUDIOMUX12; | ||
| 737 | sc_access_router.value = snd_msic_ops.hw_dmic_map[0]; | ||
| 738 | sc_access_router.mask = (MASK2 | MASK1 | MASK0); | ||
| 739 | pr_debug("hw_ch0. value = 0x%x\n", | ||
| 740 | sc_access_router.value); | ||
| 741 | retval = sst_sc_reg_access(&sc_access_router, | ||
| 742 | PMIC_READ_MODIFY, 1); | ||
| 743 | break; | ||
| 744 | |||
| 745 | case HW_CH1: | ||
| 746 | sc_access_router.reg_addr = AUDIOMUX12; | ||
| 747 | sc_access_router.value = (snd_msic_ops.hw_dmic_map[1]) << 4; | ||
| 748 | sc_access_router.mask = (MASK6 | MASK5 | MASK4); | ||
| 749 | pr_debug("### hw_ch1. value = 0x%x\n", | ||
| 750 | sc_access_router.value); | ||
| 751 | retval = sst_sc_reg_access(&sc_access_router, | ||
| 752 | PMIC_READ_MODIFY, 1); | ||
| 753 | break; | ||
| 754 | |||
| 755 | case HW_CH2: | ||
| 756 | sc_access_router.reg_addr = AUDIOMUX34; | ||
| 757 | sc_access_router.value = snd_msic_ops.hw_dmic_map[2]; | ||
| 758 | sc_access_router.mask = (MASK2 | MASK1 | MASK0); | ||
| 759 | pr_debug("hw_ch2. value = 0x%x\n", | ||
| 760 | sc_access_router.value); | ||
| 761 | retval = sst_sc_reg_access(&sc_access_router, | ||
| 762 | PMIC_READ_MODIFY, 1); | ||
| 763 | break; | ||
| 764 | |||
| 765 | case HW_CH3: | ||
| 766 | sc_access_router.reg_addr = AUDIOMUX34; | ||
| 767 | sc_access_router.value = (snd_msic_ops.hw_dmic_map[3]) << 4; | ||
| 768 | sc_access_router.mask = (MASK6 | MASK5 | MASK4); | ||
| 769 | pr_debug("hw_ch3. value = 0x%x\n", | ||
| 770 | sc_access_router.value); | ||
| 771 | retval = sst_sc_reg_access(&sc_access_router, | ||
| 772 | PMIC_READ_MODIFY, 1); | ||
| 773 | break; | ||
| 774 | } | ||
| 775 | |||
| 776 | return retval; | ||
| 777 | } | ||
| 778 | |||
| 779 | |||
| 780 | static int msic_set_pcm_voice_params(void) | ||
| 781 | { | ||
| 782 | return 0; | ||
| 783 | } | ||
| 784 | |||
| 785 | static int msic_set_pcm_audio_params(int sfreq, int word_size, int num_channel) | ||
| 786 | { | ||
| 787 | return 0; | ||
| 788 | } | ||
| 789 | |||
| 790 | static int msic_set_audio_port(int status) | ||
| 791 | { | ||
| 792 | return 0; | ||
| 793 | } | ||
| 794 | |||
| 795 | static int msic_set_voice_port(int status) | ||
| 796 | { | ||
| 797 | return 0; | ||
| 798 | } | ||
| 799 | |||
| 800 | static int msic_set_mute(int dev_id, u8 value) | ||
| 801 | { | ||
| 802 | return 0; | ||
| 803 | } | ||
| 804 | |||
| 805 | static int msic_set_vol(int dev_id, int value) | ||
| 806 | { | ||
| 807 | return 0; | ||
| 808 | } | ||
| 809 | |||
| 810 | static int msic_get_mute(int dev_id, u8 *value) | ||
| 811 | { | ||
| 812 | return 0; | ||
| 813 | } | ||
| 814 | |||
| 815 | static int msic_get_vol(int dev_id, int *value) | ||
| 816 | { | ||
| 817 | return 0; | ||
| 818 | } | ||
| 819 | |||
| 820 | static int msic_set_headset_state(int state) | ||
| 821 | { | ||
| 822 | struct sc_reg_access hs_enable[] = { | ||
| 823 | {0x25D, 0x03, 0x03}, | ||
| 824 | }; | ||
| 825 | |||
| 826 | if (state) | ||
| 827 | /*enable*/ | ||
| 828 | sst_sc_reg_access(hs_enable, PMIC_READ_MODIFY, 1); | ||
| 829 | else { | ||
| 830 | hs_enable[0].value = 0; | ||
| 831 | sst_sc_reg_access(hs_enable, PMIC_READ_MODIFY, 1); | ||
| 832 | } | ||
| 833 | return 0; | ||
| 834 | } | ||
| 835 | |||
| 836 | static int msic_enable_mic_bias(void) | ||
| 837 | { | ||
| 838 | struct sc_reg_access jack_interrupt_reg[] = { | ||
| 839 | {0x0DB, 0x07, 0x00}, | ||
| 840 | |||
| 841 | }; | ||
| 842 | struct sc_reg_access jack_bias_reg[] = { | ||
| 843 | {0x247, 0x0C, 0x0C}, | ||
| 844 | }; | ||
| 845 | |||
| 846 | sst_sc_reg_access(jack_interrupt_reg, PMIC_WRITE, 1); | ||
| 847 | sst_sc_reg_access(jack_bias_reg, PMIC_READ_MODIFY, 1); | ||
| 848 | return 0; | ||
| 849 | } | ||
| 850 | |||
| 851 | static int msic_disable_mic_bias(void) | ||
| 852 | { | ||
| 853 | if (snd_msic_ops.jack_interrupt_status == true) | ||
| 854 | return 0; | ||
| 855 | if (!(snd_msic_ops.pb_on || snd_msic_ops.cap_on)) | ||
| 856 | msic_power_down(); | ||
| 857 | return 0; | ||
| 858 | } | ||
| 859 | |||
| 860 | static int msic_disable_jack_btn(void) | ||
| 861 | { | ||
| 862 | struct sc_reg_access btn_disable[] = { | ||
| 863 | {0x26C, 0x00, 0x01} | ||
| 864 | }; | ||
| 865 | |||
| 866 | if (!(snd_msic_ops.pb_on || snd_msic_ops.cap_on)) | ||
| 867 | msic_power_down(); | ||
| 868 | snd_msic_ops.jack_interrupt_status = false; | ||
| 869 | return sst_sc_reg_access(btn_disable, PMIC_READ_MODIFY, 1); | ||
| 870 | } | ||
| 871 | |||
| 872 | static int msic_enable_jack_btn(void) | ||
| 873 | { | ||
| 874 | struct sc_reg_access btn_enable[] = { | ||
| 875 | {0x26b, 0x77, 0x00}, | ||
| 876 | {0x26C, 0x01, 0x00}, | ||
| 877 | }; | ||
| 878 | return sst_sc_reg_access(btn_enable, PMIC_WRITE, 2); | ||
| 879 | } | ||
| 880 | static int msic_convert_adc_to_mvolt(unsigned int mic_bias) | ||
| 881 | { | ||
| 882 | return (ADC_ONE_LSB_MULTIPLIER * mic_bias) / 1000; | ||
| 883 | } | ||
| 884 | int msic_get_headset_state(int mic_bias) | ||
| 885 | { | ||
| 886 | struct sc_reg_access msic_hs_toggle[] = { | ||
| 887 | {0x070, 0x00, 0x01}, | ||
| 888 | }; | ||
| 889 | if (mic_bias >= 0 && mic_bias < 400) { | ||
| 890 | |||
| 891 | pr_debug("Detected Headphone!!!\n"); | ||
| 892 | sst_sc_reg_access(msic_hs_toggle, PMIC_READ_MODIFY, 1); | ||
| 893 | |||
| 894 | } else if (mic_bias > 400 && mic_bias < 650) { | ||
| 895 | |||
| 896 | pr_debug("Detected American headset\n"); | ||
| 897 | msic_hs_toggle[0].value = 0x01; | ||
| 898 | sst_sc_reg_access(msic_hs_toggle, PMIC_READ_MODIFY, 1); | ||
| 899 | |||
| 900 | } else if (mic_bias >= 650 && mic_bias < 2000) { | ||
| 901 | |||
| 902 | pr_debug("Detected Headset!!!\n"); | ||
| 903 | sst_sc_reg_access(msic_hs_toggle, PMIC_READ_MODIFY, 1); | ||
| 904 | /*power on jack and btn*/ | ||
| 905 | snd_msic_ops.jack_interrupt_status = true; | ||
| 906 | msic_enable_jack_btn(); | ||
| 907 | msic_enable_mic_bias(); | ||
| 908 | return SND_JACK_HEADSET; | ||
| 909 | |||
| 910 | } else | ||
| 911 | pr_debug("Detected Open Cable!!!\n"); | ||
| 912 | |||
| 913 | return SND_JACK_HEADPHONE; | ||
| 914 | } | ||
| 915 | |||
| 916 | static int msic_get_mic_bias(void *arg) | ||
| 917 | { | ||
| 918 | struct snd_intelmad *intelmad_drv = (struct snd_intelmad *)arg; | ||
| 919 | u16 adc_adr = intelmad_drv->adc_address; | ||
| 920 | u16 adc_val; | ||
| 921 | int ret; | ||
| 922 | struct sc_reg_access adc_ctrl3[2] = { | ||
| 923 | {0x1C2, 0x05, 0x0}, | ||
| 924 | }; | ||
| 925 | |||
| 926 | struct sc_reg_access audio_adc_reg1 = {0,}; | ||
| 927 | struct sc_reg_access audio_adc_reg2 = {0,}; | ||
| 928 | |||
| 929 | msic_enable_mic_bias(); | ||
| 930 | /* Enable the msic for conversion before reading */ | ||
| 931 | ret = sst_sc_reg_access(adc_ctrl3, PMIC_WRITE, 1); | ||
| 932 | if (ret) | ||
| 933 | return ret; | ||
| 934 | adc_ctrl3[0].value = 0x04; | ||
| 935 | /* Re-toggle the RRDATARD bit */ | ||
| 936 | ret = sst_sc_reg_access(adc_ctrl3, PMIC_WRITE, 1); | ||
| 937 | if (ret) | ||
| 938 | return ret; | ||
| 939 | |||
| 940 | audio_adc_reg1.reg_addr = adc_adr; | ||
| 941 | /* Read the higher bits of data */ | ||
| 942 | msleep(1000); | ||
| 943 | ret = sst_sc_reg_access(&audio_adc_reg1, PMIC_READ, 1); | ||
| 944 | if (ret) | ||
| 945 | return ret; | ||
| 946 | pr_debug("adc read value %x", audio_adc_reg1.value); | ||
| 947 | |||
| 948 | /* Shift bits to accomodate the lower two data bits */ | ||
| 949 | adc_val = (audio_adc_reg1.value << 2); | ||
| 950 | adc_adr++; | ||
| 951 | audio_adc_reg2. reg_addr = adc_adr; | ||
| 952 | ret = sst_sc_reg_access(&audio_adc_reg2, PMIC_READ, 1); | ||
| 953 | if (ret) | ||
| 954 | return ret; | ||
| 955 | pr_debug("adc read value %x", audio_adc_reg2.value); | ||
| 956 | |||
| 957 | /* Adding lower two bits to the higher bits */ | ||
| 958 | audio_adc_reg2.value &= 03; | ||
| 959 | adc_val += audio_adc_reg2.value; | ||
| 960 | |||
| 961 | pr_debug("ADC value 0x%x", adc_val); | ||
| 962 | msic_disable_mic_bias(); | ||
| 963 | return adc_val; | ||
| 964 | } | ||
| 965 | |||
| 966 | static void msic_pmic_irq_cb(void *cb_data, u8 intsts) | ||
| 967 | { | ||
| 968 | struct mad_jack *mjack = NULL; | ||
| 969 | unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; | ||
| 970 | struct snd_intelmad *intelmaddata = cb_data; | ||
| 971 | int retval = 0; | ||
| 972 | |||
| 973 | pr_debug("value returned = 0x%x\n", intsts); | ||
| 974 | |||
| 975 | if (snd_msic_ops.card_status == SND_CARD_UN_INIT) { | ||
| 976 | retval = msic_init_card(); | ||
| 977 | if (retval) | ||
| 978 | return; | ||
| 979 | } | ||
| 980 | |||
| 981 | mjack = &intelmaddata->jack[0]; | ||
| 982 | if (intsts & 0x1) { | ||
| 983 | pr_debug("MAD short_push detected\n"); | ||
| 984 | present = SND_JACK_BTN_0; | ||
| 985 | jack_event_flag = buttonpressflag = 1; | ||
| 986 | mjack->jack.type = SND_JACK_BTN_0; | ||
| 987 | mjack->jack.key[0] = BTN_0 ; | ||
| 988 | } | ||
| 989 | |||
| 990 | if (intsts & 0x2) { | ||
| 991 | pr_debug(":MAD long_push detected\n"); | ||
| 992 | jack_event_flag = buttonpressflag = 1; | ||
| 993 | mjack->jack.type = present = SND_JACK_BTN_1; | ||
| 994 | mjack->jack.key[1] = BTN_1; | ||
| 995 | } | ||
| 996 | |||
| 997 | if (intsts & 0x4) { | ||
| 998 | unsigned int mic_bias; | ||
| 999 | jack_event_flag = 1; | ||
| 1000 | buttonpressflag = 0; | ||
| 1001 | mic_bias = msic_get_mic_bias(intelmaddata); | ||
| 1002 | pr_debug("mic_bias = %d\n", mic_bias); | ||
| 1003 | mic_bias = msic_convert_adc_to_mvolt(mic_bias); | ||
| 1004 | pr_debug("mic_bias after conversion = %d mV\n", mic_bias); | ||
| 1005 | mjack->jack_dev_state = msic_get_headset_state(mic_bias); | ||
| 1006 | mjack->jack.type = present = mjack->jack_dev_state; | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | if (intsts & 0x8) { | ||
| 1010 | mjack->jack.type = mjack->jack_dev_state; | ||
| 1011 | present = 0; | ||
| 1012 | jack_event_flag = 1; | ||
| 1013 | buttonpressflag = 0; | ||
| 1014 | msic_disable_jack_btn(); | ||
| 1015 | msic_disable_mic_bias(); | ||
| 1016 | } | ||
| 1017 | if (jack_event_flag) | ||
| 1018 | sst_mad_send_jack_report(&mjack->jack, | ||
| 1019 | buttonpressflag, present); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | |||
| 1023 | |||
| 1024 | struct snd_pmic_ops snd_msic_ops = { | ||
| 1025 | .set_input_dev = msic_set_selected_input_dev, | ||
| 1026 | .set_output_dev = msic_set_selected_output_dev, | ||
| 1027 | .set_lineout_dev = msic_set_selected_lineout_dev, | ||
| 1028 | .set_hw_dmic_route = msic_set_hw_dmic_route, | ||
| 1029 | .set_mute = msic_set_mute, | ||
| 1030 | .get_mute = msic_get_mute, | ||
| 1031 | .set_vol = msic_set_vol, | ||
| 1032 | .get_vol = msic_get_vol, | ||
| 1033 | .init_card = msic_init_card, | ||
| 1034 | .set_pcm_audio_params = msic_set_pcm_audio_params, | ||
| 1035 | .set_pcm_voice_params = msic_set_pcm_voice_params, | ||
| 1036 | .set_voice_port = msic_set_voice_port, | ||
| 1037 | .set_audio_port = msic_set_audio_port, | ||
| 1038 | .power_up_pmic_pb = msic_power_up_pb, | ||
| 1039 | .power_up_pmic_cp = msic_power_up_cp, | ||
| 1040 | .power_down_pmic_pb = msic_power_down_pb, | ||
| 1041 | .power_down_pmic_cp = msic_power_down_cp, | ||
| 1042 | .power_down_pmic = msic_power_down, | ||
| 1043 | .pmic_irq_cb = msic_pmic_irq_cb, | ||
| 1044 | .pmic_jack_enable = msic_enable_mic_bias, | ||
| 1045 | .pmic_get_mic_bias = msic_get_mic_bias, | ||
| 1046 | .pmic_set_headset_state = msic_set_headset_state, | ||
| 1047 | }; | ||
diff --git a/drivers/staging/intel_sst/intelmid_pvt.c b/drivers/staging/intel_sst/intelmid_pvt.c new file mode 100644 index 00000000000..90e0e64c0ab --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_pvt.c | |||
| @@ -0,0 +1,173 @@ | |||
| 1 | /* | ||
| 2 | * intelmid_pvt.h - Intel Sound card driver for MID | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Harsha Priya <priya.harsha@intel.com> | ||
| 6 | * Vinod Koul <vinod.koul@intel.com> | ||
| 7 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; version 2 of the License. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, but | ||
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 17 | * General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License along | ||
| 20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 21 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 22 | * | ||
| 23 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 24 | * ALSA driver for Intel MID sound card chipset - holding private functions | ||
| 25 | */ | ||
| 26 | |||
| 27 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 28 | |||
| 29 | #include <linux/io.h> | ||
| 30 | #include <asm/intel_scu_ipc.h> | ||
| 31 | #include <sound/core.h> | ||
| 32 | #include <sound/control.h> | ||
| 33 | #include <sound/pcm.h> | ||
| 34 | #include "intel_sst.h" | ||
| 35 | #include "intel_sst_ioctl.h" | ||
| 36 | #include "intelmid_snd_control.h" | ||
| 37 | #include "intelmid.h" | ||
| 38 | |||
| 39 | |||
| 40 | void period_elapsed(void *mad_substream) | ||
| 41 | { | ||
| 42 | struct snd_pcm_substream *substream = mad_substream; | ||
| 43 | struct mad_stream_pvt *stream; | ||
| 44 | |||
| 45 | |||
| 46 | |||
| 47 | if (!substream || !substream->runtime) | ||
| 48 | return; | ||
| 49 | stream = substream->runtime->private_data; | ||
| 50 | if (!stream) | ||
| 51 | return; | ||
| 52 | |||
| 53 | if (stream->stream_status != RUNNING) | ||
| 54 | return; | ||
| 55 | pr_debug("calling period elapsed\n"); | ||
| 56 | snd_pcm_period_elapsed(substream); | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | |||
| 60 | |||
| 61 | int snd_intelmad_alloc_stream(struct snd_pcm_substream *substream) | ||
| 62 | { | ||
| 63 | struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream); | ||
| 64 | struct mad_stream_pvt *stream = substream->runtime->private_data; | ||
| 65 | struct snd_sst_stream_params param = {{{0,},},}; | ||
| 66 | struct snd_sst_params str_params = {0}; | ||
| 67 | int ret_val; | ||
| 68 | |||
| 69 | /* set codec params and inform SST driver the same */ | ||
| 70 | |||
| 71 | param.uc.pcm_params.codec = SST_CODEC_TYPE_PCM; | ||
| 72 | param.uc.pcm_params.num_chan = (u8) substream->runtime->channels; | ||
| 73 | param.uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits; | ||
| 74 | param.uc.pcm_params.reserved = 0; | ||
| 75 | param.uc.pcm_params.sfreq = substream->runtime->rate; | ||
| 76 | param.uc.pcm_params.ring_buffer_size = | ||
| 77 | snd_pcm_lib_buffer_bytes(substream); | ||
| 78 | param.uc.pcm_params.period_count = substream->runtime->period_size; | ||
| 79 | param.uc.pcm_params.ring_buffer_addr = | ||
| 80 | virt_to_phys(substream->runtime->dma_area); | ||
| 81 | pr_debug("period_cnt = %d\n", param.uc.pcm_params.period_count); | ||
| 82 | pr_debug("sfreq= %d, wd_sz = %d\n", | ||
| 83 | param.uc.pcm_params.sfreq, param.uc.pcm_params.pcm_wd_sz); | ||
| 84 | |||
| 85 | str_params.sparams = param; | ||
| 86 | str_params.codec = SST_CODEC_TYPE_PCM; | ||
| 87 | |||
| 88 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 89 | str_params.ops = STREAM_OPS_PLAYBACK; | ||
| 90 | pr_debug("Playbck stream,Device %d\n", stream->device); | ||
| 91 | } else { | ||
| 92 | str_params.ops = STREAM_OPS_CAPTURE; | ||
| 93 | stream->device = SND_SST_DEVICE_CAPTURE; | ||
| 94 | pr_debug("Capture stream,Device %d\n", stream->device); | ||
| 95 | } | ||
| 96 | str_params.device_type = stream->device; | ||
| 97 | ret_val = intelmaddata->sstdrv_ops->pcm_control->open(&str_params); | ||
| 98 | pr_debug("sst: SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val); | ||
| 99 | if (ret_val < 0) | ||
| 100 | return ret_val; | ||
| 101 | |||
| 102 | stream->stream_info.str_id = ret_val; | ||
| 103 | stream->stream_status = INIT; | ||
| 104 | stream->stream_info.buffer_ptr = 0; | ||
| 105 | pr_debug("str id : %d\n", stream->stream_info.str_id); | ||
| 106 | |||
| 107 | return ret_val; | ||
| 108 | } | ||
| 109 | |||
| 110 | int snd_intelmad_init_stream(struct snd_pcm_substream *substream) | ||
| 111 | { | ||
| 112 | struct mad_stream_pvt *stream = substream->runtime->private_data; | ||
| 113 | struct snd_intelmad *intelmaddata = snd_pcm_substream_chip(substream); | ||
| 114 | int ret_val; | ||
| 115 | |||
| 116 | pr_debug("setting buffer ptr param\n"); | ||
| 117 | stream->stream_info.period_elapsed = period_elapsed; | ||
| 118 | stream->stream_info.mad_substream = substream; | ||
| 119 | stream->stream_info.buffer_ptr = 0; | ||
| 120 | stream->stream_info.sfreq = substream->runtime->rate; | ||
| 121 | ret_val = intelmaddata->sstdrv_ops->pcm_control->device_control( | ||
| 122 | SST_SND_STREAM_INIT, &stream->stream_info); | ||
| 123 | if (ret_val) | ||
| 124 | pr_err("control_set ret error %d\n", ret_val); | ||
| 125 | return ret_val; | ||
| 126 | |||
| 127 | } | ||
| 128 | |||
| 129 | |||
| 130 | /** | ||
| 131 | * sst_sc_reg_access - IPC read/write wrapper | ||
| 132 | * | ||
| 133 | * @sc_access: array of data, addresses and mask | ||
| 134 | * @type: operation type | ||
| 135 | * @num_val: number of reg to opertae on | ||
| 136 | * | ||
| 137 | * Reads/writes/read-modify operations on registers accessed through SCU (sound | ||
| 138 | * card and few SST DSP regsisters that are not accissible to IA) | ||
| 139 | */ | ||
| 140 | int sst_sc_reg_access(struct sc_reg_access *sc_access, | ||
| 141 | int type, int num_val) | ||
| 142 | { | ||
| 143 | int i, retval = 0; | ||
| 144 | if (type == PMIC_WRITE) { | ||
| 145 | for (i = 0; i < num_val; i++) { | ||
| 146 | retval = intel_scu_ipc_iowrite8(sc_access[i].reg_addr, | ||
| 147 | sc_access[i].value); | ||
| 148 | if (retval) | ||
| 149 | goto err; | ||
| 150 | } | ||
| 151 | } else if (type == PMIC_READ) { | ||
| 152 | for (i = 0; i < num_val; i++) { | ||
| 153 | retval = intel_scu_ipc_ioread8(sc_access[i].reg_addr, | ||
| 154 | &(sc_access[i].value)); | ||
| 155 | if (retval) | ||
| 156 | goto err; | ||
| 157 | } | ||
| 158 | } else { | ||
| 159 | for (i = 0; i < num_val; i++) { | ||
| 160 | retval = intel_scu_ipc_update_register( | ||
| 161 | sc_access[i].reg_addr, sc_access[i].value, | ||
| 162 | sc_access[i].mask); | ||
| 163 | if (retval) | ||
| 164 | goto err; | ||
| 165 | } | ||
| 166 | } | ||
| 167 | return 0; | ||
| 168 | err: | ||
| 169 | pr_err("IPC failed for cmd %d, %d\n", retval, type); | ||
| 170 | pr_err("reg:0x%2x addr:0x%2x\n", | ||
| 171 | sc_access[i].reg_addr, sc_access[i].value); | ||
| 172 | return retval; | ||
| 173 | } | ||
diff --git a/drivers/staging/intel_sst/intelmid_snd_control.h b/drivers/staging/intel_sst/intelmid_snd_control.h new file mode 100644 index 00000000000..06ad3a10099 --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_snd_control.h | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | #ifndef __INTELMID_SND_CTRL_H__ | ||
| 2 | #define __INTELMID_SND_CTRL_H__ | ||
| 3 | /* | ||
| 4 | * intelmid_snd_control.h - Intel Sound card driver for MID | ||
| 5 | * | ||
| 6 | * Copyright (C) 2008-10 Intel Corporation | ||
| 7 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 8 | * Harsha Priya <priya.harsha@intel.com> | ||
| 9 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 10 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 11 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License as published by | ||
| 15 | * the Free Software Foundation; version 2 of the License. | ||
| 16 | * | ||
| 17 | * This program is distributed in the hope that it will be useful, but | ||
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 20 | * General Public License for more details. | ||
| 21 | * | ||
| 22 | * You should have received a copy of the GNU General Public License along | ||
| 23 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 24 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 25 | * | ||
| 26 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 27 | * | ||
| 28 | * This file defines all snd control functions | ||
| 29 | */ | ||
| 30 | |||
| 31 | /* | ||
| 32 | Mask bits | ||
| 33 | */ | ||
| 34 | #define MASK0 0x01 /* 0000 0001 */ | ||
| 35 | #define MASK1 0x02 /* 0000 0010 */ | ||
| 36 | #define MASK2 0x04 /* 0000 0100 */ | ||
| 37 | #define MASK3 0x08 /* 0000 1000 */ | ||
| 38 | #define MASK4 0x10 /* 0001 0000 */ | ||
| 39 | #define MASK5 0x20 /* 0010 0000 */ | ||
| 40 | #define MASK6 0x40 /* 0100 0000 */ | ||
| 41 | #define MASK7 0x80 /* 1000 0000 */ | ||
| 42 | /* | ||
| 43 | value bits | ||
| 44 | */ | ||
| 45 | #define VALUE0 0x01 /* 0000 0001 */ | ||
| 46 | #define VALUE1 0x02 /* 0000 0010 */ | ||
| 47 | #define VALUE2 0x04 /* 0000 0100 */ | ||
| 48 | #define VALUE3 0x08 /* 0000 1000 */ | ||
| 49 | #define VALUE4 0x10 /* 0001 0000 */ | ||
| 50 | #define VALUE5 0x20 /* 0010 0000 */ | ||
| 51 | #define VALUE6 0x40 /* 0100 0000 */ | ||
| 52 | #define VALUE7 0x80 /* 1000 0000 */ | ||
| 53 | |||
| 54 | #define MUTE 0 /* ALSA Passes 0 for mute */ | ||
| 55 | #define UNMUTE 1 /* ALSA Passes 1 for unmute */ | ||
| 56 | |||
| 57 | #define MAX_VOL_PMIC_VENDOR0 0x3f /* max vol in dB for stereo & voice DAC */ | ||
| 58 | #define MIN_VOL_PMIC_VENDOR0 0 /* min vol in dB for stereo & voice DAC */ | ||
| 59 | /* Head phone volume control */ | ||
| 60 | #define MAX_HP_VOL_PMIC_VENDOR1 6 /* max volume in dB for HP */ | ||
| 61 | #define MIN_HP_VOL_PMIC_VENDOR1 (-84) /* min volume in dB for HP */ | ||
| 62 | #define MAX_HP_VOL_INDX_PMIC_VENDOR1 40 /* Number of HP volume control values */ | ||
| 63 | |||
| 64 | /* Mono Earpiece Volume control */ | ||
| 65 | #define MAX_EP_VOL_PMIC_VENDOR1 0 /* max volume in dB for EP */ | ||
| 66 | #define MIN_EP_VOL_PMIC_VENDOR1 (-75) /* min volume in dB for EP */ | ||
| 67 | #define MAX_EP_VOL_INDX_PMIC_VENDOR1 32 /* Number of EP volume control values */ | ||
| 68 | |||
| 69 | int sst_sc_reg_access(struct sc_reg_access *sc_access, | ||
| 70 | int type, int num_val); | ||
| 71 | extern struct snd_pmic_ops snd_pmic_ops_fs; | ||
| 72 | extern struct snd_pmic_ops snd_pmic_ops_mx; | ||
| 73 | extern struct snd_pmic_ops snd_pmic_ops_nc; | ||
| 74 | extern struct snd_pmic_ops snd_msic_ops; | ||
| 75 | |||
| 76 | /* device */ | ||
| 77 | enum SND_INPUT_DEVICE { | ||
| 78 | AMIC, | ||
| 79 | DMIC, | ||
| 80 | HS_MIC, | ||
| 81 | IN_UNDEFINED | ||
| 82 | }; | ||
| 83 | enum SND_LINE_OUT_DEVICE { | ||
| 84 | HEADSET, | ||
| 85 | IHF, | ||
| 86 | VIBRA1, | ||
| 87 | VIBRA2, | ||
| 88 | NONE, | ||
| 89 | }; | ||
| 90 | |||
| 91 | enum SND_OUTPUT_DEVICE { | ||
| 92 | STEREO_HEADPHONE, | ||
| 93 | MONO_EARPIECE, | ||
| 94 | |||
| 95 | INTERNAL_SPKR, | ||
| 96 | RECEIVER, | ||
| 97 | OUT_UNDEFINED | ||
| 98 | }; | ||
| 99 | |||
| 100 | enum pmic_controls { | ||
| 101 | PMIC_SND_HP_MIC_MUTE = 0x0001, | ||
| 102 | PMIC_SND_AMIC_MUTE = 0x0002, | ||
| 103 | PMIC_SND_DMIC_MUTE = 0x0003, | ||
| 104 | PMIC_SND_CAPTURE_VOL = 0x0004, | ||
| 105 | /* Output controls */ | ||
| 106 | PMIC_SND_LEFT_PB_VOL = 0x0010, | ||
| 107 | PMIC_SND_RIGHT_PB_VOL = 0x0011, | ||
| 108 | PMIC_SND_LEFT_HP_MUTE = 0x0012, | ||
| 109 | PMIC_SND_RIGHT_HP_MUTE = 0x0013, | ||
| 110 | PMIC_SND_LEFT_SPEAKER_MUTE = 0x0014, | ||
| 111 | PMIC_SND_RIGHT_SPEAKER_MUTE = 0x0015, | ||
| 112 | PMIC_SND_RECEIVER_VOL = 0x0016, | ||
| 113 | PMIC_SND_RECEIVER_MUTE = 0x0017, | ||
| 114 | PMIC_SND_LEFT_MASTER_VOL = 0x0018, | ||
| 115 | PMIC_SND_RIGHT_MASTER_VOL = 0x0019, | ||
| 116 | /* Other controls */ | ||
| 117 | PMIC_SND_MUTE_ALL = 0x0020, | ||
| 118 | PMIC_MAX_CONTROLS = 0x0020, | ||
| 119 | }; | ||
| 120 | |||
| 121 | #endif /* __INTELMID_SND_CTRL_H__ */ | ||
| 122 | |||
| 123 | |||
diff --git a/drivers/staging/intel_sst/intelmid_v0_control.c b/drivers/staging/intel_sst/intelmid_v0_control.c new file mode 100644 index 00000000000..b8dfdb9bc1a --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_v0_control.c | |||
| @@ -0,0 +1,866 @@ | |||
| 1 | /* | ||
| 2 | * intel_sst_v0_control.c - Intel SST Driver for audio engine | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corporation | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 8 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * | ||
| 26 | * This file contains the control operations of vendor 1 | ||
| 27 | */ | ||
| 28 | |||
| 29 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 30 | |||
| 31 | #include <linux/pci.h> | ||
| 32 | #include <linux/file.h> | ||
| 33 | #include <sound/control.h> | ||
| 34 | #include "intel_sst.h" | ||
| 35 | #include "intelmid_snd_control.h" | ||
| 36 | #include "intelmid.h" | ||
| 37 | |||
| 38 | enum _reg_v1 { | ||
| 39 | VOICEPORT1 = 0x180, | ||
| 40 | VOICEPORT2 = 0x181, | ||
| 41 | AUDIOPORT1 = 0x182, | ||
| 42 | AUDIOPORT2 = 0x183, | ||
| 43 | MISCVOICECTRL = 0x184, | ||
| 44 | MISCAUDCTRL = 0x185, | ||
| 45 | DMICCTRL1 = 0x186, | ||
| 46 | AUDIOBIAS = 0x187, | ||
| 47 | MICCTRL = 0x188, | ||
| 48 | MICLICTRL1 = 0x189, | ||
| 49 | MICLICTRL2 = 0x18A, | ||
| 50 | MICLICTRL3 = 0x18B, | ||
| 51 | VOICEDACCTRL1 = 0x18C, | ||
| 52 | STEREOADCCTRL = 0x18D, | ||
| 53 | AUD15 = 0x18E, | ||
| 54 | AUD16 = 0x18F, | ||
| 55 | AUD17 = 0x190, | ||
| 56 | AUD18 = 0x191, | ||
| 57 | RMIXOUTSEL = 0x192, | ||
| 58 | ANALOGLBR = 0x193, | ||
| 59 | ANALOGLBL = 0x194, | ||
| 60 | POWERCTRL1 = 0x195, | ||
| 61 | POWERCTRL2 = 0x196, | ||
| 62 | HEADSETDETECTINT = 0x197, | ||
| 63 | HEADSETDETECTINTMASK = 0x198, | ||
| 64 | TRIMENABLE = 0x199, | ||
| 65 | }; | ||
| 66 | |||
| 67 | int rev_id = 0x20; | ||
| 68 | static bool jack_det_enabled; | ||
| 69 | |||
| 70 | /**** | ||
| 71 | * fs_init_card - initialize the sound card | ||
| 72 | * | ||
| 73 | * This initializes the audio paths to know values in case of this sound card | ||
| 74 | */ | ||
| 75 | static int fs_init_card(void) | ||
| 76 | { | ||
| 77 | struct sc_reg_access sc_access[] = { | ||
| 78 | {0x180, 0x00, 0x0}, | ||
| 79 | {0x181, 0x00, 0x0}, | ||
| 80 | {0x182, 0xF8, 0x0}, | ||
| 81 | {0x183, 0x08, 0x0}, | ||
| 82 | {0x184, 0x00, 0x0}, | ||
| 83 | {0x185, 0x40, 0x0}, | ||
| 84 | {0x186, 0x06, 0x0}, | ||
| 85 | {0x187, 0x80, 0x0}, | ||
| 86 | {0x188, 0x40, 0x0}, | ||
| 87 | {0x189, 0x39, 0x0}, | ||
| 88 | {0x18a, 0x39, 0x0}, | ||
| 89 | {0x18b, 0x1F, 0x0}, | ||
| 90 | {0x18c, 0x00, 0x0}, | ||
| 91 | {0x18d, 0x00, 0x0}, | ||
| 92 | {0x18e, 0x39, 0x0}, | ||
| 93 | {0x18f, 0x39, 0x0}, | ||
| 94 | {0x190, 0x39, 0x0}, | ||
| 95 | {0x191, 0x11, 0x0}, | ||
| 96 | {0x192, 0x0E, 0x0}, | ||
| 97 | {0x193, 0x00, 0x0}, | ||
| 98 | {0x194, 0x00, 0x0}, | ||
| 99 | {0x195, 0x00, 0x0}, | ||
| 100 | {0x196, 0x7C, 0x0}, | ||
| 101 | {0x197, 0x00, 0x0}, | ||
| 102 | {0x198, 0x0B, 0x0}, | ||
| 103 | {0x199, 0x00, 0x0}, | ||
| 104 | {0x037, 0x3F, 0x0}, | ||
| 105 | }; | ||
| 106 | |||
| 107 | snd_pmic_ops_fs.card_status = SND_CARD_INIT_DONE; | ||
| 108 | snd_pmic_ops_fs.master_mute = UNMUTE; | ||
| 109 | snd_pmic_ops_fs.mute_status = UNMUTE; | ||
| 110 | snd_pmic_ops_fs.num_channel = 2; | ||
| 111 | return sst_sc_reg_access(sc_access, PMIC_WRITE, 27); | ||
| 112 | } | ||
| 113 | |||
| 114 | static int fs_enable_audiodac(int value) | ||
| 115 | { | ||
| 116 | struct sc_reg_access sc_access[3]; | ||
| 117 | sc_access[0].reg_addr = AUD16; | ||
| 118 | sc_access[1].reg_addr = AUD17; | ||
| 119 | sc_access[2].reg_addr = AUD15; | ||
| 120 | sc_access[0].mask = sc_access[1].mask = sc_access[2].mask = MASK7; | ||
| 121 | |||
| 122 | if (snd_pmic_ops_fs.mute_status == MUTE) | ||
| 123 | return 0; | ||
| 124 | if (value == MUTE) { | ||
| 125 | sc_access[0].value = sc_access[1].value = | ||
| 126 | sc_access[2].value = 0x80; | ||
| 127 | |||
| 128 | } else { | ||
| 129 | sc_access[0].value = sc_access[1].value = | ||
| 130 | sc_access[2].value = 0x0; | ||
| 131 | } | ||
| 132 | if (snd_pmic_ops_fs.num_channel == 1) | ||
| 133 | sc_access[1].value = sc_access[2].value = 0x80; | ||
| 134 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); | ||
| 135 | |||
| 136 | } | ||
| 137 | |||
| 138 | static int fs_power_up_pb(unsigned int port) | ||
| 139 | { | ||
| 140 | struct sc_reg_access sc_access[] = { | ||
| 141 | {AUDIOBIAS, 0x00, MASK7}, | ||
| 142 | {POWERCTRL1, 0xC6, 0xC6}, | ||
| 143 | {POWERCTRL2, 0x30, 0x30}, | ||
| 144 | |||
| 145 | }; | ||
| 146 | int retval = 0; | ||
| 147 | |||
| 148 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 149 | retval = fs_init_card(); | ||
| 150 | if (retval) | ||
| 151 | return retval; | ||
| 152 | retval = fs_enable_audiodac(MUTE); | ||
| 153 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); | ||
| 154 | |||
| 155 | if (retval) | ||
| 156 | return retval; | ||
| 157 | |||
| 158 | pr_debug("in fs power up pb\n"); | ||
| 159 | return fs_enable_audiodac(UNMUTE); | ||
| 160 | } | ||
| 161 | |||
| 162 | static int fs_power_down_pb(unsigned int device) | ||
| 163 | { | ||
| 164 | struct sc_reg_access sc_access[] = { | ||
| 165 | {POWERCTRL1, 0x00, 0xC6}, | ||
| 166 | {POWERCTRL2, 0x00, 0x30}, | ||
| 167 | }; | ||
| 168 | int retval = 0; | ||
| 169 | |||
| 170 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 171 | retval = fs_init_card(); | ||
| 172 | if (retval) | ||
| 173 | return retval; | ||
| 174 | retval = fs_enable_audiodac(MUTE); | ||
| 175 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 176 | |||
| 177 | if (retval) | ||
| 178 | return retval; | ||
| 179 | |||
| 180 | pr_debug("in fsl power down pb\n"); | ||
| 181 | return fs_enable_audiodac(UNMUTE); | ||
| 182 | } | ||
| 183 | |||
| 184 | static int fs_power_up_cp(unsigned int port) | ||
| 185 | { | ||
| 186 | struct sc_reg_access sc_access[] = { | ||
| 187 | {POWERCTRL2, 0x32, 0x32}, /*NOTE power up A ADC only as*/ | ||
| 188 | {AUDIOBIAS, 0x00, MASK7}, | ||
| 189 | /*as turning on V ADC causes noise*/ | ||
| 190 | }; | ||
| 191 | int retval = 0; | ||
| 192 | |||
| 193 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 194 | retval = fs_init_card(); | ||
| 195 | if (retval) | ||
| 196 | return retval; | ||
| 197 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 198 | } | ||
| 199 | |||
| 200 | static int fs_power_down_cp(unsigned int device) | ||
| 201 | { | ||
| 202 | struct sc_reg_access sc_access[] = { | ||
| 203 | {POWERCTRL2, 0x00, 0x03}, | ||
| 204 | }; | ||
| 205 | int retval = 0; | ||
| 206 | |||
| 207 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 208 | retval = fs_init_card(); | ||
| 209 | if (retval) | ||
| 210 | return retval; | ||
| 211 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 212 | } | ||
| 213 | |||
| 214 | static int fs_power_down(void) | ||
| 215 | { | ||
| 216 | int retval = 0; | ||
| 217 | struct sc_reg_access sc_access[] = { | ||
| 218 | {AUDIOBIAS, MASK7, MASK7}, | ||
| 219 | }; | ||
| 220 | |||
| 221 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 222 | retval = fs_init_card(); | ||
| 223 | if (retval) | ||
| 224 | return retval; | ||
| 225 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 226 | } | ||
| 227 | |||
| 228 | static int fs_set_pcm_voice_params(void) | ||
| 229 | { | ||
| 230 | struct sc_reg_access sc_access[] = { | ||
| 231 | {0x180, 0xA0, 0}, | ||
| 232 | {0x181, 0x04, 0}, | ||
| 233 | {0x182, 0x0, 0}, | ||
| 234 | {0x183, 0x0, 0}, | ||
| 235 | {0x184, 0x18, 0}, | ||
| 236 | {0x185, 0x40, 0}, | ||
| 237 | {0x186, 0x06, 0}, | ||
| 238 | {0x187, 0x0, 0}, | ||
| 239 | {0x188, 0x10, 0}, | ||
| 240 | {0x189, 0x39, 0}, | ||
| 241 | {0x18a, 0x39, 0}, | ||
| 242 | {0x18b, 0x02, 0}, | ||
| 243 | {0x18c, 0x0, 0}, | ||
| 244 | {0x18d, 0x0, 0}, | ||
| 245 | {0x18e, 0x39, 0}, | ||
| 246 | {0x18f, 0x0, 0}, | ||
| 247 | {0x190, 0x0, 0}, | ||
| 248 | {0x191, 0x20, 0}, | ||
| 249 | {0x192, 0x20, 0}, | ||
| 250 | {0x193, 0x0, 0}, | ||
| 251 | {0x194, 0x0, 0}, | ||
| 252 | {0x195, 0x06, 0}, | ||
| 253 | {0x196, 0x25, 0}, | ||
| 254 | {0x197, 0x0, 0}, | ||
| 255 | {0x198, 0xF, 0}, | ||
| 256 | {0x199, 0x0, 0}, | ||
| 257 | }; | ||
| 258 | int retval = 0; | ||
| 259 | |||
| 260 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 261 | retval = fs_init_card(); | ||
| 262 | if (retval) | ||
| 263 | return retval; | ||
| 264 | return sst_sc_reg_access(sc_access, PMIC_WRITE, 26); | ||
| 265 | } | ||
| 266 | |||
| 267 | static int fs_set_audio_port(int status) | ||
| 268 | { | ||
| 269 | struct sc_reg_access sc_access[2]; | ||
| 270 | int retval = 0; | ||
| 271 | |||
| 272 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 273 | retval = fs_init_card(); | ||
| 274 | if (retval) | ||
| 275 | return retval; | ||
| 276 | if (status == DEACTIVATE) { | ||
| 277 | /* Deactivate audio port-tristate and power */ | ||
| 278 | sc_access[0].value = 0x00; | ||
| 279 | sc_access[0].mask = MASK6|MASK7; | ||
| 280 | sc_access[0].reg_addr = AUDIOPORT1; | ||
| 281 | sc_access[1].value = 0x00; | ||
| 282 | sc_access[1].mask = MASK4|MASK5; | ||
| 283 | sc_access[1].reg_addr = POWERCTRL2; | ||
| 284 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 285 | } else if (status == ACTIVATE) { | ||
| 286 | /* activate audio port */ | ||
| 287 | sc_access[0].value = 0xC0; | ||
| 288 | sc_access[0].mask = MASK6|MASK7; | ||
| 289 | sc_access[0].reg_addr = AUDIOPORT1; | ||
| 290 | sc_access[1].value = 0x30; | ||
| 291 | sc_access[1].mask = MASK4|MASK5; | ||
| 292 | sc_access[1].reg_addr = POWERCTRL2; | ||
| 293 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 294 | } else | ||
| 295 | return -EINVAL; | ||
| 296 | } | ||
| 297 | |||
| 298 | static int fs_set_voice_port(int status) | ||
| 299 | { | ||
| 300 | struct sc_reg_access sc_access[2]; | ||
| 301 | int retval = 0; | ||
| 302 | |||
| 303 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 304 | retval = fs_init_card(); | ||
| 305 | if (retval) | ||
| 306 | return retval; | ||
| 307 | if (status == DEACTIVATE) { | ||
| 308 | /* Deactivate audio port-tristate and power */ | ||
| 309 | sc_access[0].value = 0x00; | ||
| 310 | sc_access[0].mask = MASK6|MASK7; | ||
| 311 | sc_access[0].reg_addr = VOICEPORT1; | ||
| 312 | sc_access[1].value = 0x00; | ||
| 313 | sc_access[1].mask = MASK0|MASK1; | ||
| 314 | sc_access[1].reg_addr = POWERCTRL2; | ||
| 315 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 316 | } else if (status == ACTIVATE) { | ||
| 317 | /* activate audio port */ | ||
| 318 | sc_access[0].value = 0xC0; | ||
| 319 | sc_access[0].mask = MASK6|MASK7; | ||
| 320 | sc_access[0].reg_addr = VOICEPORT1; | ||
| 321 | sc_access[1].value = 0x03; | ||
| 322 | sc_access[1].mask = MASK0|MASK1; | ||
| 323 | sc_access[1].reg_addr = POWERCTRL2; | ||
| 324 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 325 | } else | ||
| 326 | return -EINVAL; | ||
| 327 | } | ||
| 328 | |||
| 329 | static int fs_set_pcm_audio_params(int sfreq, int word_size, int num_channel) | ||
| 330 | { | ||
| 331 | u8 config1 = 0; | ||
| 332 | struct sc_reg_access sc_access[4]; | ||
| 333 | int retval = 0, num_value = 0; | ||
| 334 | |||
| 335 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 336 | retval = fs_init_card(); | ||
| 337 | if (retval) | ||
| 338 | return retval; | ||
| 339 | switch (sfreq) { | ||
| 340 | case 8000: | ||
| 341 | config1 = 0x00; | ||
| 342 | break; | ||
| 343 | case 11025: | ||
| 344 | config1 = 0x01; | ||
| 345 | break; | ||
| 346 | case 12000: | ||
| 347 | config1 = 0x02; | ||
| 348 | break; | ||
| 349 | case 16000: | ||
| 350 | config1 = 0x03; | ||
| 351 | break; | ||
| 352 | case 22050: | ||
| 353 | config1 = 0x04; | ||
| 354 | break; | ||
| 355 | case 24000: | ||
| 356 | config1 = 0x05; | ||
| 357 | break; | ||
| 358 | case 26000: | ||
| 359 | config1 = 0x06; | ||
| 360 | break; | ||
| 361 | case 32000: | ||
| 362 | config1 = 0x07; | ||
| 363 | break; | ||
| 364 | case 44100: | ||
| 365 | config1 = 0x08; | ||
| 366 | break; | ||
| 367 | case 48000: | ||
| 368 | config1 = 0x09; | ||
| 369 | break; | ||
| 370 | } | ||
| 371 | snd_pmic_ops_fs.num_channel = num_channel; | ||
| 372 | if (snd_pmic_ops_fs.num_channel == 1) { | ||
| 373 | sc_access[0].reg_addr = AUD17; | ||
| 374 | sc_access[1].reg_addr = AUD15; | ||
| 375 | sc_access[0].mask = sc_access[1].mask = MASK7; | ||
| 376 | sc_access[0].value = sc_access[1].value = 0x80; | ||
| 377 | sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 378 | |||
| 379 | } else { | ||
| 380 | sc_access[0].reg_addr = AUD17; | ||
| 381 | sc_access[1].reg_addr = AUD15; | ||
| 382 | sc_access[0].mask = sc_access[1].mask = MASK7; | ||
| 383 | sc_access[0].value = sc_access[1].value = 0x00; | ||
| 384 | sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 385 | |||
| 386 | } | ||
| 387 | pr_debug("sfreq:%d,Register value = %x\n", sfreq, config1); | ||
| 388 | |||
| 389 | if (word_size == 24) { | ||
| 390 | sc_access[0].reg_addr = AUDIOPORT1; | ||
| 391 | sc_access[0].mask = MASK0|MASK1|MASK2|MASK3; | ||
| 392 | sc_access[0].value = 0xFB; | ||
| 393 | |||
| 394 | |||
| 395 | sc_access[1].reg_addr = AUDIOPORT2; | ||
| 396 | sc_access[1].value = config1 | 0x10; | ||
| 397 | sc_access[1].mask = MASK0 | MASK1 | MASK2 | MASK3 | ||
| 398 | | MASK4 | MASK5 | MASK6; | ||
| 399 | |||
| 400 | sc_access[2].reg_addr = MISCAUDCTRL; | ||
| 401 | sc_access[2].value = 0x02; | ||
| 402 | sc_access[2].mask = 0x02; | ||
| 403 | |||
| 404 | num_value = 3 ; | ||
| 405 | |||
| 406 | } else { | ||
| 407 | |||
| 408 | sc_access[0].reg_addr = AUDIOPORT2; | ||
| 409 | sc_access[0].value = config1; | ||
| 410 | sc_access[0].mask = MASK0|MASK1|MASK2|MASK3; | ||
| 411 | |||
| 412 | sc_access[1].reg_addr = MISCAUDCTRL; | ||
| 413 | sc_access[1].value = 0x00; | ||
| 414 | sc_access[1].mask = 0x02; | ||
| 415 | num_value = 2; | ||
| 416 | } | ||
| 417 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_value); | ||
| 418 | |||
| 419 | } | ||
| 420 | |||
| 421 | static int fs_set_selected_input_dev(u8 value) | ||
| 422 | { | ||
| 423 | struct sc_reg_access sc_access_dmic[] = { | ||
| 424 | {MICCTRL, 0x81, 0xf7}, | ||
| 425 | {MICLICTRL3, 0x00, 0xE0}, | ||
| 426 | }; | ||
| 427 | struct sc_reg_access sc_access_mic[] = { | ||
| 428 | {MICCTRL, 0x40, MASK2|MASK4|MASK5|MASK6|MASK7}, | ||
| 429 | {MICLICTRL3, 0x00, 0xE0}, | ||
| 430 | }; | ||
| 431 | struct sc_reg_access sc_access_hsmic[] = { | ||
| 432 | {MICCTRL, 0x10, MASK2|MASK4|MASK5|MASK6|MASK7}, | ||
| 433 | {MICLICTRL3, 0x00, 0xE0}, | ||
| 434 | }; | ||
| 435 | |||
| 436 | int retval = 0; | ||
| 437 | |||
| 438 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 439 | retval = fs_init_card(); | ||
| 440 | if (retval) | ||
| 441 | return retval; | ||
| 442 | |||
| 443 | switch (value) { | ||
| 444 | case AMIC: | ||
| 445 | pr_debug("Selecting amic not supported in mono cfg\n"); | ||
| 446 | return sst_sc_reg_access(sc_access_mic, PMIC_READ_MODIFY, 2); | ||
| 447 | break; | ||
| 448 | |||
| 449 | case HS_MIC: | ||
| 450 | pr_debug("Selecting hsmic\n"); | ||
| 451 | return sst_sc_reg_access(sc_access_hsmic, | ||
| 452 | PMIC_READ_MODIFY, 2); | ||
| 453 | break; | ||
| 454 | |||
| 455 | case DMIC: | ||
| 456 | pr_debug("Selecting dmic\n"); | ||
| 457 | return sst_sc_reg_access(sc_access_dmic, PMIC_READ_MODIFY, 2); | ||
| 458 | break; | ||
| 459 | |||
| 460 | default: | ||
| 461 | return -EINVAL; | ||
| 462 | |||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | static int fs_set_selected_output_dev(u8 value) | ||
| 467 | { | ||
| 468 | struct sc_reg_access sc_access_hp[] = { | ||
| 469 | {0x191, 0x11, 0x0}, | ||
| 470 | {0x192, 0x0E, 0x0}, | ||
| 471 | }; | ||
| 472 | struct sc_reg_access sc_access_is[] = { | ||
| 473 | {0x191, 0x17, 0xFF}, | ||
| 474 | {0x192, 0x08, 0xFF}, | ||
| 475 | }; | ||
| 476 | int retval = 0; | ||
| 477 | |||
| 478 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 479 | retval = fs_init_card(); | ||
| 480 | if (retval) | ||
| 481 | return retval; | ||
| 482 | |||
| 483 | switch (value) { | ||
| 484 | case STEREO_HEADPHONE: | ||
| 485 | pr_debug("SST DBG:Selecting headphone\n"); | ||
| 486 | return sst_sc_reg_access(sc_access_hp, PMIC_WRITE, 2); | ||
| 487 | break; | ||
| 488 | case MONO_EARPIECE: | ||
| 489 | case INTERNAL_SPKR: | ||
| 490 | pr_debug("SST DBG:Selecting internal spkr\n"); | ||
| 491 | return sst_sc_reg_access(sc_access_is, PMIC_READ_MODIFY, 2); | ||
| 492 | break; | ||
| 493 | |||
| 494 | default: | ||
| 495 | return -EINVAL; | ||
| 496 | |||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | static int fs_set_mute(int dev_id, u8 value) | ||
| 501 | { | ||
| 502 | struct sc_reg_access sc_access[6] = {{0,},}; | ||
| 503 | int reg_num = 0; | ||
| 504 | int retval = 0; | ||
| 505 | |||
| 506 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 507 | retval = fs_init_card(); | ||
| 508 | if (retval) | ||
| 509 | return retval; | ||
| 510 | |||
| 511 | |||
| 512 | pr_debug("dev_id:0x%x value:0x%x\n", dev_id, value); | ||
| 513 | switch (dev_id) { | ||
| 514 | case PMIC_SND_DMIC_MUTE: | ||
| 515 | sc_access[0].reg_addr = MICCTRL; | ||
| 516 | sc_access[1].reg_addr = MICLICTRL1; | ||
| 517 | sc_access[2].reg_addr = MICLICTRL2; | ||
| 518 | sc_access[0].mask = MASK5; | ||
| 519 | sc_access[1].mask = sc_access[2].mask = MASK6; | ||
| 520 | if (value == MUTE) { | ||
| 521 | sc_access[0].value = 0x20; | ||
| 522 | sc_access[2].value = sc_access[1].value = 0x40; | ||
| 523 | } else | ||
| 524 | sc_access[0].value = sc_access[1].value | ||
| 525 | = sc_access[2].value = 0x0; | ||
| 526 | reg_num = 3; | ||
| 527 | break; | ||
| 528 | case PMIC_SND_HP_MIC_MUTE: | ||
| 529 | case PMIC_SND_AMIC_MUTE: | ||
| 530 | sc_access[0].reg_addr = MICLICTRL1; | ||
| 531 | sc_access[1].reg_addr = MICLICTRL2; | ||
| 532 | sc_access[0].mask = sc_access[1].mask = MASK6; | ||
| 533 | if (value == MUTE) | ||
| 534 | sc_access[0].value = sc_access[1].value = 0x40; | ||
| 535 | else | ||
| 536 | sc_access[0].value = sc_access[1].value = 0x0; | ||
| 537 | reg_num = 2; | ||
| 538 | break; | ||
| 539 | case PMIC_SND_LEFT_SPEAKER_MUTE: | ||
| 540 | case PMIC_SND_LEFT_HP_MUTE: | ||
| 541 | sc_access[0].reg_addr = AUD16; | ||
| 542 | sc_access[1].reg_addr = AUD15; | ||
| 543 | |||
| 544 | sc_access[0].mask = sc_access[1].mask = MASK7; | ||
| 545 | if (value == MUTE) | ||
| 546 | sc_access[0].value = sc_access[1].value = 0x80; | ||
| 547 | else | ||
| 548 | sc_access[0].value = sc_access[1].value = 0x0; | ||
| 549 | reg_num = 2; | ||
| 550 | snd_pmic_ops_fs.mute_status = value; | ||
| 551 | break; | ||
| 552 | case PMIC_SND_RIGHT_HP_MUTE: | ||
| 553 | case PMIC_SND_RIGHT_SPEAKER_MUTE: | ||
| 554 | sc_access[0].reg_addr = AUD17; | ||
| 555 | sc_access[1].reg_addr = AUD15; | ||
| 556 | sc_access[0].mask = sc_access[1].mask = MASK7; | ||
| 557 | if (value == MUTE) | ||
| 558 | sc_access[0].value = sc_access[1].value = 0x80; | ||
| 559 | else | ||
| 560 | sc_access[0].value = sc_access[1].value = 0x0; | ||
| 561 | snd_pmic_ops_fs.mute_status = value; | ||
| 562 | if (snd_pmic_ops_fs.num_channel == 1) | ||
| 563 | sc_access[0].value = sc_access[1].value = 0x80; | ||
| 564 | reg_num = 2; | ||
| 565 | break; | ||
| 566 | case PMIC_SND_MUTE_ALL: | ||
| 567 | sc_access[0].reg_addr = AUD16; | ||
| 568 | sc_access[1].reg_addr = AUD17; | ||
| 569 | sc_access[2].reg_addr = AUD15; | ||
| 570 | sc_access[3].reg_addr = MICCTRL; | ||
| 571 | sc_access[4].reg_addr = MICLICTRL1; | ||
| 572 | sc_access[5].reg_addr = MICLICTRL2; | ||
| 573 | sc_access[0].mask = sc_access[1].mask = | ||
| 574 | sc_access[2].mask = MASK7; | ||
| 575 | sc_access[3].mask = MASK5; | ||
| 576 | sc_access[4].mask = sc_access[5].mask = MASK6; | ||
| 577 | |||
| 578 | if (value == MUTE) { | ||
| 579 | sc_access[0].value = | ||
| 580 | sc_access[1].value = sc_access[2].value = 0x80; | ||
| 581 | sc_access[3].value = 0x20; | ||
| 582 | sc_access[4].value = sc_access[5].value = 0x40; | ||
| 583 | |||
| 584 | } else { | ||
| 585 | sc_access[0].value = sc_access[1].value = | ||
| 586 | sc_access[2].value = sc_access[3].value = | ||
| 587 | sc_access[4].value = sc_access[5].value = 0x0; | ||
| 588 | } | ||
| 589 | if (snd_pmic_ops_fs.num_channel == 1) | ||
| 590 | sc_access[1].value = sc_access[2].value = 0x80; | ||
| 591 | reg_num = 6; | ||
| 592 | snd_pmic_ops_fs.mute_status = value; | ||
| 593 | snd_pmic_ops_fs.master_mute = value; | ||
| 594 | break; | ||
| 595 | |||
| 596 | } | ||
| 597 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, reg_num); | ||
| 598 | } | ||
| 599 | |||
| 600 | static int fs_set_vol(int dev_id, int value) | ||
| 601 | { | ||
| 602 | struct sc_reg_access sc_acces, sc_access[4] = {{0},}; | ||
| 603 | int reg_num = 0; | ||
| 604 | int retval = 0; | ||
| 605 | |||
| 606 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 607 | retval = fs_init_card(); | ||
| 608 | if (retval) | ||
| 609 | return retval; | ||
| 610 | |||
| 611 | switch (dev_id) { | ||
| 612 | case PMIC_SND_LEFT_PB_VOL: | ||
| 613 | pr_debug("PMIC_SND_LEFT_PB_VOL:%d\n", value); | ||
| 614 | sc_access[0].value = sc_access[1].value = value; | ||
| 615 | sc_access[0].reg_addr = AUD16; | ||
| 616 | sc_access[1].reg_addr = AUD15; | ||
| 617 | sc_access[0].mask = sc_access[1].mask = | ||
| 618 | (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); | ||
| 619 | reg_num = 2; | ||
| 620 | break; | ||
| 621 | |||
| 622 | case PMIC_SND_RIGHT_PB_VOL: | ||
| 623 | pr_debug("PMIC_SND_RIGHT_PB_VOL:%d\n", value); | ||
| 624 | sc_access[0].value = sc_access[1].value = value; | ||
| 625 | sc_access[0].reg_addr = AUD17; | ||
| 626 | sc_access[1].reg_addr = AUD15; | ||
| 627 | sc_access[0].mask = sc_access[1].mask = | ||
| 628 | (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); | ||
| 629 | if (snd_pmic_ops_fs.num_channel == 1) { | ||
| 630 | sc_access[0].value = sc_access[1].value = 0x80; | ||
| 631 | sc_access[0].mask = sc_access[1].mask = MASK7; | ||
| 632 | } | ||
| 633 | reg_num = 2; | ||
| 634 | break; | ||
| 635 | case PMIC_SND_CAPTURE_VOL: | ||
| 636 | pr_debug("PMIC_SND_CAPTURE_VOL:%d\n", value); | ||
| 637 | sc_access[0].reg_addr = MICLICTRL1; | ||
| 638 | sc_access[1].reg_addr = MICLICTRL2; | ||
| 639 | sc_access[2].reg_addr = DMICCTRL1; | ||
| 640 | sc_access[2].value = value; | ||
| 641 | sc_access[0].value = sc_access[1].value = value; | ||
| 642 | sc_acces.reg_addr = MICLICTRL3; | ||
| 643 | sc_acces.value = value; | ||
| 644 | sc_acces.mask = (MASK0|MASK1|MASK2|MASK3|MASK5|MASK6|MASK7); | ||
| 645 | retval = sst_sc_reg_access(&sc_acces, PMIC_READ_MODIFY, 1); | ||
| 646 | sc_access[0].mask = sc_access[1].mask = | ||
| 647 | sc_access[2].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); | ||
| 648 | reg_num = 3; | ||
| 649 | break; | ||
| 650 | |||
| 651 | default: | ||
| 652 | return -EINVAL; | ||
| 653 | } | ||
| 654 | |||
| 655 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, reg_num); | ||
| 656 | } | ||
| 657 | |||
| 658 | static int fs_get_mute(int dev_id, u8 *value) | ||
| 659 | { | ||
| 660 | struct sc_reg_access sc_access[6] = {{0,},}; | ||
| 661 | |||
| 662 | int retval = 0, temp_value = 0, mask = 0; | ||
| 663 | |||
| 664 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 665 | retval = fs_init_card(); | ||
| 666 | if (retval) | ||
| 667 | return retval; | ||
| 668 | |||
| 669 | switch (dev_id) { | ||
| 670 | |||
| 671 | case PMIC_SND_AMIC_MUTE: | ||
| 672 | case PMIC_SND_HP_MIC_MUTE: | ||
| 673 | sc_access[0].reg_addr = MICLICTRL1; | ||
| 674 | mask = MASK6; | ||
| 675 | retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); | ||
| 676 | if (sc_access[0].value & mask) | ||
| 677 | *value = MUTE; | ||
| 678 | else | ||
| 679 | *value = UNMUTE; | ||
| 680 | break; | ||
| 681 | case PMIC_SND_DMIC_MUTE: | ||
| 682 | sc_access[0].reg_addr = MICCTRL; | ||
| 683 | mask = MASK5; | ||
| 684 | retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); | ||
| 685 | temp_value = (sc_access[0].value & mask); | ||
| 686 | if (temp_value == 0) | ||
| 687 | *value = UNMUTE; | ||
| 688 | else | ||
| 689 | *value = MUTE; | ||
| 690 | break; | ||
| 691 | |||
| 692 | case PMIC_SND_LEFT_HP_MUTE: | ||
| 693 | case PMIC_SND_LEFT_SPEAKER_MUTE: | ||
| 694 | sc_access[0].reg_addr = AUD16; | ||
| 695 | mask = MASK7; | ||
| 696 | retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); | ||
| 697 | temp_value = sc_access[0].value & mask; | ||
| 698 | if (temp_value == 0) | ||
| 699 | *value = UNMUTE; | ||
| 700 | else | ||
| 701 | *value = MUTE; | ||
| 702 | break; | ||
| 703 | case PMIC_SND_RIGHT_HP_MUTE: | ||
| 704 | case PMIC_SND_RIGHT_SPEAKER_MUTE: | ||
| 705 | sc_access[0].reg_addr = AUD17; | ||
| 706 | mask = MASK7; | ||
| 707 | retval = sst_sc_reg_access(sc_access, PMIC_READ, 1); | ||
| 708 | temp_value = sc_access[0].value & mask; | ||
| 709 | if (temp_value == 0) | ||
| 710 | *value = UNMUTE; | ||
| 711 | else | ||
| 712 | *value = MUTE; | ||
| 713 | break; | ||
| 714 | default: | ||
| 715 | return -EINVAL; | ||
| 716 | } | ||
| 717 | |||
| 718 | return retval; | ||
| 719 | } | ||
| 720 | |||
| 721 | static int fs_get_vol(int dev_id, int *value) | ||
| 722 | { | ||
| 723 | struct sc_reg_access sc_access = {0,}; | ||
| 724 | int retval = 0, mask = 0; | ||
| 725 | |||
| 726 | if (snd_pmic_ops_fs.card_status == SND_CARD_UN_INIT) | ||
| 727 | retval = fs_init_card(); | ||
| 728 | if (retval) | ||
| 729 | return retval; | ||
| 730 | |||
| 731 | switch (dev_id) { | ||
| 732 | case PMIC_SND_CAPTURE_VOL: | ||
| 733 | pr_debug("PMIC_SND_CAPTURE_VOL\n"); | ||
| 734 | sc_access.reg_addr = MICLICTRL1; | ||
| 735 | mask = (MASK5|MASK4|MASK3|MASK2|MASK1|MASK0); | ||
| 736 | break; | ||
| 737 | case PMIC_SND_LEFT_PB_VOL: | ||
| 738 | pr_debug("PMIC_SND_LEFT_PB_VOL\n"); | ||
| 739 | sc_access.reg_addr = AUD16; | ||
| 740 | mask = (MASK5|MASK4|MASK3|MASK2|MASK1|MASK0); | ||
| 741 | break; | ||
| 742 | case PMIC_SND_RIGHT_PB_VOL: | ||
| 743 | pr_debug("PMIC_SND_RT_PB_VOL\n"); | ||
| 744 | sc_access.reg_addr = AUD17; | ||
| 745 | mask = (MASK5|MASK4|MASK3|MASK2|MASK1|MASK0); | ||
| 746 | break; | ||
| 747 | default: | ||
| 748 | return -EINVAL; | ||
| 749 | } | ||
| 750 | |||
| 751 | retval = sst_sc_reg_access(&sc_access, PMIC_READ, 1); | ||
| 752 | pr_debug("value read = 0x%x\n", sc_access.value); | ||
| 753 | *value = (int) (sc_access.value & mask); | ||
| 754 | pr_debug("value returned = 0x%x\n", *value); | ||
| 755 | return retval; | ||
| 756 | } | ||
| 757 | |||
| 758 | static void fs_pmic_irq_enable(void *data) | ||
| 759 | { | ||
| 760 | struct snd_intelmad *intelmaddata = data; | ||
| 761 | struct sc_reg_access sc_access[] = { | ||
| 762 | {0x187, 0x00, MASK7}, | ||
| 763 | {0x188, 0x10, MASK4}, | ||
| 764 | {0x18b, 0x10, MASK4}, | ||
| 765 | }; | ||
| 766 | |||
| 767 | struct sc_reg_access sc_access_write[] = { | ||
| 768 | {0x198, 0x00, 0x0}, | ||
| 769 | }; | ||
| 770 | pr_debug("Audio interrupt enable\n"); | ||
| 771 | sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); | ||
| 772 | sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1); | ||
| 773 | |||
| 774 | intelmaddata->jack[0].jack_status = 0; | ||
| 775 | /*intelmaddata->jack[1].jack_status = 0;*/ | ||
| 776 | |||
| 777 | jack_det_enabled = true; | ||
| 778 | return; | ||
| 779 | } | ||
| 780 | |||
| 781 | static void fs_pmic_irq_cb(void *cb_data, u8 value) | ||
| 782 | { | ||
| 783 | struct mad_jack *mjack = NULL; | ||
| 784 | struct snd_intelmad *intelmaddata = cb_data; | ||
| 785 | unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; | ||
| 786 | |||
| 787 | mjack = &intelmaddata->jack[0]; | ||
| 788 | |||
| 789 | if (value & 0x4) { | ||
| 790 | if (!jack_det_enabled) | ||
| 791 | fs_pmic_irq_enable(intelmaddata); | ||
| 792 | |||
| 793 | /* send headphone detect */ | ||
| 794 | pr_debug(":MAD headphone %d\n", value & 0x4); | ||
| 795 | present = !(mjack->jack_status); | ||
| 796 | mjack->jack_status = present; | ||
| 797 | jack_event_flag = 1; | ||
| 798 | mjack->jack.type = SND_JACK_HEADPHONE; | ||
| 799 | } | ||
| 800 | |||
| 801 | if (value & 0x2) { | ||
| 802 | /* send short push */ | ||
| 803 | pr_debug(":MAD short push %d\n", value & 0x2); | ||
| 804 | present = 1; | ||
| 805 | jack_event_flag = 1; | ||
| 806 | buttonpressflag = 1; | ||
| 807 | mjack->jack.type = MID_JACK_HS_SHORT_PRESS; | ||
| 808 | } | ||
| 809 | |||
| 810 | if (value & 0x1) { | ||
| 811 | /* send long push */ | ||
| 812 | pr_debug(":MAD long push %d\n", value & 0x1); | ||
| 813 | present = 1; | ||
| 814 | jack_event_flag = 1; | ||
| 815 | buttonpressflag = 1; | ||
| 816 | mjack->jack.type = MID_JACK_HS_LONG_PRESS; | ||
| 817 | } | ||
| 818 | |||
| 819 | if (value & 0x8) { | ||
| 820 | if (!jack_det_enabled) | ||
| 821 | fs_pmic_irq_enable(intelmaddata); | ||
| 822 | /* send headset detect */ | ||
| 823 | pr_debug(":MAD headset = %d\n", value & 0x8); | ||
| 824 | present = !(mjack->jack_status); | ||
| 825 | mjack->jack_status = present; | ||
| 826 | jack_event_flag = 1; | ||
| 827 | mjack->jack.type = SND_JACK_HEADSET; | ||
| 828 | } | ||
| 829 | |||
| 830 | |||
| 831 | if (jack_event_flag) | ||
| 832 | sst_mad_send_jack_report(&mjack->jack, | ||
| 833 | buttonpressflag, present); | ||
| 834 | |||
| 835 | return; | ||
| 836 | } | ||
| 837 | static int fs_jack_enable(void) | ||
| 838 | { | ||
| 839 | return 0; | ||
| 840 | } | ||
| 841 | |||
| 842 | struct snd_pmic_ops snd_pmic_ops_fs = { | ||
| 843 | .set_input_dev = fs_set_selected_input_dev, | ||
| 844 | .set_output_dev = fs_set_selected_output_dev, | ||
| 845 | .set_mute = fs_set_mute, | ||
| 846 | .get_mute = fs_get_mute, | ||
| 847 | .set_vol = fs_set_vol, | ||
| 848 | .get_vol = fs_get_vol, | ||
| 849 | .init_card = fs_init_card, | ||
| 850 | .set_pcm_audio_params = fs_set_pcm_audio_params, | ||
| 851 | .set_pcm_voice_params = fs_set_pcm_voice_params, | ||
| 852 | .set_voice_port = fs_set_voice_port, | ||
| 853 | .set_audio_port = fs_set_audio_port, | ||
| 854 | .power_up_pmic_pb = fs_power_up_pb, | ||
| 855 | .power_up_pmic_cp = fs_power_up_cp, | ||
| 856 | .power_down_pmic_pb = fs_power_down_pb, | ||
| 857 | .power_down_pmic_cp = fs_power_down_cp, | ||
| 858 | .power_down_pmic = fs_power_down, | ||
| 859 | .pmic_irq_cb = fs_pmic_irq_cb, | ||
| 860 | /* | ||
| 861 | * Jack detection enabling | ||
| 862 | * need be delayed till first IRQ happen. | ||
| 863 | */ | ||
| 864 | .pmic_irq_enable = NULL, | ||
| 865 | .pmic_jack_enable = fs_jack_enable, | ||
| 866 | }; | ||
diff --git a/drivers/staging/intel_sst/intelmid_v1_control.c b/drivers/staging/intel_sst/intelmid_v1_control.c new file mode 100644 index 00000000000..9d00728d8de --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_v1_control.c | |||
| @@ -0,0 +1,978 @@ | |||
| 1 | /* intel_sst_v1_control.c - Intel SST Driver for audio engine | ||
| 2 | * | ||
| 3 | * Copyright (C) 2008-10 Intel Corp | ||
| 4 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 5 | * Harsha Priya <priya.harsha@intel.com> | ||
| 6 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 7 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify | ||
| 11 | * it under the terms of the GNU General Public License as published by | ||
| 12 | * the Free Software Foundation; version 2 of the License. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, but | ||
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 17 | * General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License along | ||
| 20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 21 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 22 | * | ||
| 23 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 24 | * | ||
| 25 | * This file contains the control operations of vendor 2 | ||
| 26 | */ | ||
| 27 | |||
| 28 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 29 | |||
| 30 | #include <linux/pci.h> | ||
| 31 | #include <linux/delay.h> | ||
| 32 | #include <linux/file.h> | ||
| 33 | #include <asm/mrst.h> | ||
| 34 | #include <sound/pcm.h> | ||
| 35 | #include <sound/pcm_params.h> | ||
| 36 | #include <sound/control.h> | ||
| 37 | #include <sound/initval.h> | ||
| 38 | #include "intel_sst.h" | ||
| 39 | #include "intel_sst_ioctl.h" | ||
| 40 | #include "intelmid.h" | ||
| 41 | #include "intelmid_snd_control.h" | ||
| 42 | |||
| 43 | #include <linux/gpio.h> | ||
| 44 | #define KOSKI_VOICE_CODEC_ENABLE 46 | ||
| 45 | |||
| 46 | enum _reg_v2 { | ||
| 47 | |||
| 48 | MASTER_CLOCK_PRESCALAR = 0x205, | ||
| 49 | SET_MASTER_AND_LR_CLK1 = 0x20b, | ||
| 50 | SET_MASTER_AND_LR_CLK2 = 0x20c, | ||
| 51 | MASTER_MODE_AND_DATA_DELAY = 0x20d, | ||
| 52 | DIGITAL_INTERFACE_TO_DAI2 = 0x20e, | ||
| 53 | CLK_AND_FS1 = 0x208, | ||
| 54 | CLK_AND_FS2 = 0x209, | ||
| 55 | DAI2_TO_DAC_HP = 0x210, | ||
| 56 | HP_OP_SINGLE_ENDED = 0x224, | ||
| 57 | ENABLE_OPDEV_CTRL = 0x226, | ||
| 58 | ENABLE_DEV_AND_USE_XTAL = 0x227, | ||
| 59 | |||
| 60 | /* Max audio subsystem (PQ49) MAX 8921 */ | ||
| 61 | AS_IP_MODE_CTL = 0xF9, | ||
| 62 | AS_LEFT_SPKR_VOL_CTL = 0xFA, /* Mono Earpiece volume control */ | ||
| 63 | AS_RIGHT_SPKR_VOL_CTL = 0xFB, | ||
| 64 | AS_LEFT_HP_VOL_CTL = 0xFC, | ||
| 65 | AS_RIGHT_HP_VOL_CTL = 0xFD, | ||
| 66 | AS_OP_MIX_CTL = 0xFE, | ||
| 67 | AS_CONFIG = 0xFF, | ||
| 68 | |||
| 69 | /* Headphone volume control & mute registers */ | ||
| 70 | VOL_CTRL_LT = 0x21c, | ||
| 71 | VOL_CTRL_RT = 0x21d, | ||
| 72 | |||
| 73 | }; | ||
| 74 | /** | ||
| 75 | * mx_init_card - initialize the sound card | ||
| 76 | * | ||
| 77 | * This initializes the audio paths to know values in case of this sound card | ||
| 78 | */ | ||
| 79 | static int mx_init_card(void) | ||
| 80 | { | ||
| 81 | struct sc_reg_access sc_access[] = { | ||
| 82 | {0x200, 0x80, 0x00}, | ||
| 83 | {0x201, 0xC0, 0x00}, | ||
| 84 | {0x202, 0x00, 0x00}, | ||
| 85 | {0x203, 0x00, 0x00}, | ||
| 86 | {0x204, 0x02, 0x00}, | ||
| 87 | {0x205, 0x10, 0x00}, | ||
| 88 | {0x206, 0x60, 0x00}, | ||
| 89 | {0x207, 0x00, 0x00}, | ||
| 90 | {0x208, 0x90, 0x00}, | ||
| 91 | {0x209, 0x51, 0x00}, | ||
| 92 | {0x20a, 0x00, 0x00}, | ||
| 93 | {0x20b, 0x10, 0x00}, | ||
| 94 | {0x20c, 0x00, 0x00}, | ||
| 95 | {0x20d, 0x00, 0x00}, | ||
| 96 | {0x20e, 0x21, 0x00}, | ||
| 97 | {0x20f, 0x00, 0x00}, | ||
| 98 | {0x210, 0x84, 0x00}, | ||
| 99 | {0x211, 0xB3, 0x00}, | ||
| 100 | {0x212, 0x00, 0x00}, | ||
| 101 | {0x213, 0x00, 0x00}, | ||
| 102 | {0x214, 0x41, 0x00}, | ||
| 103 | {0x215, 0x00, 0x00}, | ||
| 104 | {0x216, 0x00, 0x00}, | ||
| 105 | {0x217, 0x00, 0x00}, | ||
| 106 | {0x218, 0x03, 0x00}, | ||
| 107 | {0x219, 0x03, 0x00}, | ||
| 108 | {0x21a, 0x00, 0x00}, | ||
| 109 | {0x21b, 0x00, 0x00}, | ||
| 110 | {0x21c, 0x00, 0x00}, | ||
| 111 | {0x21d, 0x00, 0x00}, | ||
| 112 | {0x21e, 0x00, 0x00}, | ||
| 113 | {0x21f, 0x00, 0x00}, | ||
| 114 | {0x220, 0x20, 0x00}, | ||
| 115 | {0x221, 0x20, 0x00}, | ||
| 116 | {0x222, 0x51, 0x00}, | ||
| 117 | {0x223, 0x20, 0x00}, | ||
| 118 | {0x224, 0x04, 0x00}, | ||
| 119 | {0x225, 0x80, 0x00}, | ||
| 120 | {0x226, 0x0F, 0x00}, | ||
| 121 | {0x227, 0x08, 0x00}, | ||
| 122 | {0xf9, 0x40, 0x00}, | ||
| 123 | {0xfa, 0x1f, 0x00}, | ||
| 124 | {0xfb, 0x1f, 0x00}, | ||
| 125 | {0xfc, 0x1f, 0x00}, | ||
| 126 | {0xfd, 0x1f, 0x00}, | ||
| 127 | {0xfe, 0x00, 0x00}, | ||
| 128 | {0xff, 0x0c, 0x00}, | ||
| 129 | }; | ||
| 130 | snd_pmic_ops_mx.card_status = SND_CARD_INIT_DONE; | ||
| 131 | snd_pmic_ops_mx.num_channel = 2; | ||
| 132 | snd_pmic_ops_mx.master_mute = UNMUTE; | ||
| 133 | snd_pmic_ops_mx.mute_status = UNMUTE; | ||
| 134 | return sst_sc_reg_access(sc_access, PMIC_WRITE, 47); | ||
| 135 | } | ||
| 136 | |||
| 137 | static int mx_enable_audiodac(int value) | ||
| 138 | { | ||
| 139 | struct sc_reg_access sc_access[3]; | ||
| 140 | int mute_val = 0; | ||
| 141 | int mute_val1 = 0; | ||
| 142 | int retval = 0; | ||
| 143 | |||
| 144 | sc_access[0].reg_addr = AS_LEFT_HP_VOL_CTL; | ||
| 145 | sc_access[1].reg_addr = AS_RIGHT_HP_VOL_CTL; | ||
| 146 | |||
| 147 | if (value == UNMUTE) { | ||
| 148 | mute_val = 0x1F; | ||
| 149 | mute_val1 = 0x00; | ||
| 150 | } else { | ||
| 151 | mute_val = 0x00; | ||
| 152 | mute_val1 = 0x40; | ||
| 153 | } | ||
| 154 | sc_access[0].mask = sc_access[1].mask = MASK0|MASK1|MASK2|MASK3|MASK4; | ||
| 155 | sc_access[0].value = sc_access[1].value = (u8)mute_val; | ||
| 156 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 157 | if (retval) | ||
| 158 | return retval; | ||
| 159 | pr_debug("mute status = %d\n", snd_pmic_ops_mx.mute_status); | ||
| 160 | if (snd_pmic_ops_mx.mute_status == MUTE || | ||
| 161 | snd_pmic_ops_mx.master_mute == MUTE) | ||
| 162 | return retval; | ||
| 163 | |||
| 164 | sc_access[0].reg_addr = VOL_CTRL_LT; | ||
| 165 | sc_access[1].reg_addr = VOL_CTRL_RT; | ||
| 166 | sc_access[0].mask = sc_access[1].mask = MASK6; | ||
| 167 | sc_access[0].value = sc_access[1].value = mute_val1; | ||
| 168 | if (snd_pmic_ops_mx.num_channel == 1) | ||
| 169 | sc_access[1].value = 0x40; | ||
| 170 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 171 | } | ||
| 172 | |||
| 173 | static int mx_power_up_pb(unsigned int port) | ||
| 174 | { | ||
| 175 | |||
| 176 | int retval = 0; | ||
| 177 | struct sc_reg_access sc_access[3]; | ||
| 178 | |||
| 179 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 180 | retval = mx_init_card(); | ||
| 181 | if (retval) | ||
| 182 | return retval; | ||
| 183 | } | ||
| 184 | retval = mx_enable_audiodac(MUTE); | ||
| 185 | if (retval) | ||
| 186 | return retval; | ||
| 187 | |||
| 188 | msleep(10); | ||
| 189 | |||
| 190 | sc_access[0].reg_addr = AS_CONFIG; | ||
| 191 | sc_access[0].mask = MASK7; | ||
| 192 | sc_access[0].value = 0x80; | ||
| 193 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 194 | if (retval) | ||
| 195 | return retval; | ||
| 196 | |||
| 197 | sc_access[0].reg_addr = ENABLE_OPDEV_CTRL; | ||
| 198 | sc_access[0].mask = 0xff; | ||
| 199 | sc_access[0].value = 0x3C; | ||
| 200 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 201 | if (retval) | ||
| 202 | return retval; | ||
| 203 | |||
| 204 | sc_access[0].reg_addr = ENABLE_DEV_AND_USE_XTAL; | ||
| 205 | sc_access[0].mask = 0x80; | ||
| 206 | sc_access[0].value = 0x80; | ||
| 207 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 208 | if (retval) | ||
| 209 | return retval; | ||
| 210 | |||
| 211 | return mx_enable_audiodac(UNMUTE); | ||
| 212 | } | ||
| 213 | |||
| 214 | static int mx_power_down_pb(unsigned int device) | ||
| 215 | { | ||
| 216 | struct sc_reg_access sc_access[3]; | ||
| 217 | int retval = 0; | ||
| 218 | |||
| 219 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 220 | retval = mx_init_card(); | ||
| 221 | if (retval) | ||
| 222 | return retval; | ||
| 223 | } | ||
| 224 | |||
| 225 | retval = mx_enable_audiodac(MUTE); | ||
| 226 | if (retval) | ||
| 227 | return retval; | ||
| 228 | |||
| 229 | sc_access[0].reg_addr = ENABLE_OPDEV_CTRL; | ||
| 230 | sc_access[0].mask = MASK3|MASK2; | ||
| 231 | sc_access[0].value = 0x00; | ||
| 232 | |||
| 233 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 234 | if (retval) | ||
| 235 | return retval; | ||
| 236 | |||
| 237 | return mx_enable_audiodac(UNMUTE); | ||
| 238 | } | ||
| 239 | |||
| 240 | static int mx_power_up_cp(unsigned int port) | ||
| 241 | { | ||
| 242 | int retval = 0; | ||
| 243 | struct sc_reg_access sc_access[] = { | ||
| 244 | {ENABLE_DEV_AND_USE_XTAL, 0x80, MASK7}, | ||
| 245 | {ENABLE_OPDEV_CTRL, 0x3, 0x3}, | ||
| 246 | }; | ||
| 247 | |||
| 248 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 249 | retval = mx_init_card(); | ||
| 250 | if (retval) | ||
| 251 | return retval; | ||
| 252 | } | ||
| 253 | |||
| 254 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 255 | } | ||
| 256 | |||
| 257 | static int mx_power_down_cp(unsigned int device) | ||
| 258 | { | ||
| 259 | struct sc_reg_access sc_access[] = { | ||
| 260 | {ENABLE_OPDEV_CTRL, 0x00, MASK1|MASK0}, | ||
| 261 | }; | ||
| 262 | int retval = 0; | ||
| 263 | |||
| 264 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 265 | retval = mx_init_card(); | ||
| 266 | if (retval) | ||
| 267 | return retval; | ||
| 268 | } | ||
| 269 | |||
| 270 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 271 | } | ||
| 272 | |||
| 273 | static int mx_power_down(void) | ||
| 274 | { | ||
| 275 | int retval = 0; | ||
| 276 | struct sc_reg_access sc_access[3]; | ||
| 277 | |||
| 278 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 279 | retval = mx_init_card(); | ||
| 280 | if (retval) | ||
| 281 | return retval; | ||
| 282 | } | ||
| 283 | |||
| 284 | retval = mx_enable_audiodac(MUTE); | ||
| 285 | if (retval) | ||
| 286 | return retval; | ||
| 287 | |||
| 288 | sc_access[0].reg_addr = AS_CONFIG; | ||
| 289 | sc_access[0].mask = MASK7; | ||
| 290 | sc_access[0].value = 0x00; | ||
| 291 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 292 | if (retval) | ||
| 293 | return retval; | ||
| 294 | |||
| 295 | sc_access[0].reg_addr = ENABLE_DEV_AND_USE_XTAL; | ||
| 296 | sc_access[0].mask = MASK7; | ||
| 297 | sc_access[0].value = 0x00; | ||
| 298 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 299 | if (retval) | ||
| 300 | return retval; | ||
| 301 | |||
| 302 | sc_access[0].reg_addr = ENABLE_OPDEV_CTRL; | ||
| 303 | sc_access[0].mask = MASK3|MASK2; | ||
| 304 | sc_access[0].value = 0x00; | ||
| 305 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 306 | if (retval) | ||
| 307 | return retval; | ||
| 308 | |||
| 309 | return mx_enable_audiodac(UNMUTE); | ||
| 310 | } | ||
| 311 | |||
| 312 | static int mx_set_pcm_voice_params(void) | ||
| 313 | { | ||
| 314 | int retval = 0; | ||
| 315 | struct sc_reg_access sc_access[] = { | ||
| 316 | {0x200, 0x80, 0x00}, | ||
| 317 | {0x201, 0xC0, 0x00}, | ||
| 318 | {0x202, 0x00, 0x00}, | ||
| 319 | {0x203, 0x00, 0x00}, | ||
| 320 | {0x204, 0x0e, 0x00}, | ||
| 321 | {0x205, 0x20, 0x00}, | ||
| 322 | {0x206, 0x8f, 0x00}, | ||
| 323 | {0x207, 0x21, 0x00}, | ||
| 324 | {0x208, 0x18, 0x00}, | ||
| 325 | {0x209, 0x32, 0x00}, | ||
| 326 | {0x20a, 0x00, 0x00}, | ||
| 327 | {0x20b, 0x5A, 0x00}, | ||
| 328 | {0x20c, 0xBE, 0x00},/* 0x00 -> 0xBE Koski */ | ||
| 329 | {0x20d, 0x00, 0x00}, /* DAI2 'off' */ | ||
| 330 | {0x20e, 0x40, 0x00}, | ||
| 331 | {0x20f, 0x00, 0x00}, | ||
| 332 | {0x210, 0x84, 0x00}, | ||
| 333 | {0x211, 0x33, 0x00}, /* Voice filter */ | ||
| 334 | {0x212, 0x00, 0x00}, | ||
| 335 | {0x213, 0x00, 0x00}, | ||
| 336 | {0x214, 0x41, 0x00}, | ||
| 337 | {0x215, 0x00, 0x00}, | ||
| 338 | {0x216, 0x00, 0x00}, | ||
| 339 | {0x217, 0x20, 0x00}, | ||
| 340 | {0x218, 0x00, 0x00}, | ||
| 341 | {0x219, 0x00, 0x00}, | ||
| 342 | {0x21a, 0x40, 0x00}, | ||
| 343 | {0x21b, 0x40, 0x00}, | ||
| 344 | {0x21c, 0x09, 0x00}, | ||
| 345 | {0x21d, 0x09, 0x00}, | ||
| 346 | {0x21e, 0x00, 0x00}, | ||
| 347 | {0x21f, 0x00, 0x00}, | ||
| 348 | {0x220, 0x00, 0x00}, /* Microphone configurations */ | ||
| 349 | {0x221, 0x00, 0x00}, /* Microphone configurations */ | ||
| 350 | {0x222, 0x50, 0x00}, /* Microphone configurations */ | ||
| 351 | {0x223, 0x21, 0x00}, /* Microphone configurations */ | ||
| 352 | {0x224, 0x00, 0x00}, | ||
| 353 | {0x225, 0x80, 0x00}, | ||
| 354 | {0xf9, 0x40, 0x00}, | ||
| 355 | {0xfa, 0x19, 0x00}, | ||
| 356 | {0xfb, 0x19, 0x00}, | ||
| 357 | {0xfc, 0x12, 0x00}, | ||
| 358 | {0xfd, 0x12, 0x00}, | ||
| 359 | {0xfe, 0x00, 0x00}, | ||
| 360 | }; | ||
| 361 | |||
| 362 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 363 | retval = mx_init_card(); | ||
| 364 | if (retval) | ||
| 365 | return retval; | ||
| 366 | } | ||
| 367 | pr_debug("SST DBG:mx_set_pcm_voice_params called\n"); | ||
| 368 | return sst_sc_reg_access(sc_access, PMIC_WRITE, 44); | ||
| 369 | } | ||
| 370 | |||
| 371 | static int mx_set_pcm_audio_params(int sfreq, int word_size, int num_channel) | ||
| 372 | { | ||
| 373 | int retval = 0; | ||
| 374 | |||
| 375 | int config1 = 0, config2 = 0, filter = 0xB3; | ||
| 376 | struct sc_reg_access sc_access[5]; | ||
| 377 | |||
| 378 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 379 | retval = mx_init_card(); | ||
| 380 | if (retval) | ||
| 381 | return retval; | ||
| 382 | } | ||
| 383 | |||
| 384 | switch (sfreq) { | ||
| 385 | case 8000: | ||
| 386 | config1 = 0x10; | ||
| 387 | config2 = 0x00; | ||
| 388 | filter = 0x33; | ||
| 389 | break; | ||
| 390 | case 11025: | ||
| 391 | config1 = 0x16; | ||
| 392 | config2 = 0x0d; | ||
| 393 | break; | ||
| 394 | case 12000: | ||
| 395 | config1 = 0x18; | ||
| 396 | config2 = 0x00; | ||
| 397 | break; | ||
| 398 | case 16000: | ||
| 399 | config1 = 0x20; | ||
| 400 | config2 = 0x00; | ||
| 401 | break; | ||
| 402 | case 22050: | ||
| 403 | config1 = 0x2c; | ||
| 404 | config2 = 0x1a; | ||
| 405 | break; | ||
| 406 | case 24000: | ||
| 407 | config1 = 0x30; | ||
| 408 | config2 = 0x00; | ||
| 409 | break; | ||
| 410 | case 32000: | ||
| 411 | config1 = 0x40; | ||
| 412 | config2 = 0x00; | ||
| 413 | break; | ||
| 414 | case 44100: | ||
| 415 | config1 = 0x58; | ||
| 416 | config2 = 0x33; | ||
| 417 | break; | ||
| 418 | case 48000: | ||
| 419 | config1 = 0x60; | ||
| 420 | config2 = 0x00; | ||
| 421 | break; | ||
| 422 | } | ||
| 423 | snd_pmic_ops_mx.num_channel = num_channel; | ||
| 424 | /*mute the right channel if MONO*/ | ||
| 425 | if (snd_pmic_ops_mx.num_channel == 1) { | ||
| 426 | sc_access[0].reg_addr = VOL_CTRL_RT; | ||
| 427 | sc_access[0].value = 0x40; | ||
| 428 | sc_access[0].mask = MASK6; | ||
| 429 | |||
| 430 | sc_access[1].reg_addr = 0x224; | ||
| 431 | sc_access[1].value = 0x05; | ||
| 432 | sc_access[1].mask = MASK0|MASK1|MASK2; | ||
| 433 | |||
| 434 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 435 | if (retval) | ||
| 436 | return retval; | ||
| 437 | } else { | ||
| 438 | sc_access[0].reg_addr = VOL_CTRL_RT; | ||
| 439 | sc_access[0].value = 0x00; | ||
| 440 | sc_access[0].mask = MASK6; | ||
| 441 | |||
| 442 | sc_access[1].reg_addr = 0x224; | ||
| 443 | sc_access[1].value = 0x04; | ||
| 444 | sc_access[1].mask = MASK0|MASK1|MASK2; | ||
| 445 | |||
| 446 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 447 | if (retval) | ||
| 448 | return retval; | ||
| 449 | } | ||
| 450 | sc_access[0].reg_addr = 0x206; | ||
| 451 | sc_access[0].value = config1; | ||
| 452 | sc_access[1].reg_addr = 0x207; | ||
| 453 | sc_access[1].value = config2; | ||
| 454 | |||
| 455 | if (word_size == 16) { | ||
| 456 | sc_access[2].value = 0x51; | ||
| 457 | sc_access[3].value = 0x31; | ||
| 458 | } else if (word_size == 24) { | ||
| 459 | sc_access[2].value = 0x52; | ||
| 460 | sc_access[3].value = 0x92; | ||
| 461 | } | ||
| 462 | |||
| 463 | sc_access[2].reg_addr = 0x209; | ||
| 464 | sc_access[3].reg_addr = 0x20e; | ||
| 465 | |||
| 466 | sc_access[4].reg_addr = 0x211; | ||
| 467 | sc_access[4].value = filter; | ||
| 468 | |||
| 469 | return sst_sc_reg_access(sc_access, PMIC_WRITE, 5); | ||
| 470 | } | ||
| 471 | |||
| 472 | static int mx_set_selected_output_dev(u8 dev_id) | ||
| 473 | { | ||
| 474 | struct sc_reg_access sc_access[2]; | ||
| 475 | int num_reg = 0; | ||
| 476 | int retval = 0; | ||
| 477 | |||
| 478 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 479 | retval = mx_init_card(); | ||
| 480 | if (retval) | ||
| 481 | return retval; | ||
| 482 | } | ||
| 483 | |||
| 484 | pr_debug("mx_set_selected_output_dev dev_id:0x%x\n", dev_id); | ||
| 485 | snd_pmic_ops_mx.output_dev_id = dev_id; | ||
| 486 | switch (dev_id) { | ||
| 487 | case STEREO_HEADPHONE: | ||
| 488 | sc_access[0].reg_addr = 0xFF; | ||
| 489 | sc_access[0].value = 0x8C; | ||
| 490 | sc_access[0].mask = | ||
| 491 | MASK2|MASK3|MASK5|MASK6|MASK4; | ||
| 492 | |||
| 493 | num_reg = 1; | ||
| 494 | break; | ||
| 495 | case MONO_EARPIECE: | ||
| 496 | case INTERNAL_SPKR: | ||
| 497 | sc_access[0].reg_addr = 0xFF; | ||
| 498 | sc_access[0].value = 0xb0; | ||
| 499 | sc_access[0].mask = MASK2|MASK3|MASK5|MASK6|MASK4; | ||
| 500 | |||
| 501 | num_reg = 1; | ||
| 502 | break; | ||
| 503 | case RECEIVER: | ||
| 504 | pr_debug("RECEIVER Koski selected\n"); | ||
| 505 | |||
| 506 | /* configuration - AS enable, receiver enable */ | ||
| 507 | sc_access[0].reg_addr = 0xFF; | ||
| 508 | sc_access[0].value = 0x81; | ||
| 509 | sc_access[0].mask = 0xff; | ||
| 510 | |||
| 511 | num_reg = 1; | ||
| 512 | break; | ||
| 513 | default: | ||
| 514 | pr_err("Not a valid output dev\n"); | ||
| 515 | return 0; | ||
| 516 | } | ||
| 517 | return sst_sc_reg_access(sc_access, PMIC_WRITE, num_reg); | ||
| 518 | } | ||
| 519 | |||
| 520 | |||
| 521 | static int mx_set_voice_port(int status) | ||
| 522 | { | ||
| 523 | int retval = 0; | ||
| 524 | |||
| 525 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 526 | retval = mx_init_card(); | ||
| 527 | if (retval) | ||
| 528 | return retval; | ||
| 529 | } | ||
| 530 | if (status == ACTIVATE) | ||
| 531 | retval = mx_set_pcm_voice_params(); | ||
| 532 | |||
| 533 | return retval; | ||
| 534 | } | ||
| 535 | |||
| 536 | static int mx_set_audio_port(int status) | ||
| 537 | { | ||
| 538 | return 0; | ||
| 539 | } | ||
| 540 | |||
| 541 | static int mx_set_selected_input_dev(u8 dev_id) | ||
| 542 | { | ||
| 543 | struct sc_reg_access sc_access[2]; | ||
| 544 | int num_reg = 0; | ||
| 545 | int retval = 0; | ||
| 546 | |||
| 547 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 548 | retval = mx_init_card(); | ||
| 549 | if (retval) | ||
| 550 | return retval; | ||
| 551 | } | ||
| 552 | snd_pmic_ops_mx.input_dev_id = dev_id; | ||
| 553 | pr_debug("mx_set_selected_input_dev dev_id:0x%x\n", dev_id); | ||
| 554 | |||
| 555 | switch (dev_id) { | ||
| 556 | case AMIC: | ||
| 557 | sc_access[0].reg_addr = 0x223; | ||
| 558 | sc_access[0].value = 0x00; | ||
| 559 | sc_access[0].mask = MASK7|MASK6|MASK5|MASK4|MASK0; | ||
| 560 | sc_access[1].reg_addr = 0x222; | ||
| 561 | sc_access[1].value = 0x50; | ||
| 562 | sc_access[1].mask = MASK7|MASK6|MASK5|MASK4; | ||
| 563 | num_reg = 2; | ||
| 564 | break; | ||
| 565 | |||
| 566 | case HS_MIC: | ||
| 567 | sc_access[0].reg_addr = 0x223; | ||
| 568 | sc_access[0].value = 0x20; | ||
| 569 | sc_access[0].mask = MASK7|MASK6|MASK5|MASK4|MASK0; | ||
| 570 | sc_access[1].reg_addr = 0x222; | ||
| 571 | sc_access[1].value = 0x51; | ||
| 572 | sc_access[1].mask = MASK7|MASK6|MASK5|MASK4; | ||
| 573 | num_reg = 2; | ||
| 574 | break; | ||
| 575 | case DMIC: | ||
| 576 | sc_access[1].reg_addr = 0x222; | ||
| 577 | sc_access[1].value = 0x00; | ||
| 578 | sc_access[1].mask = MASK7|MASK6|MASK5|MASK4|MASK0; | ||
| 579 | sc_access[0].reg_addr = 0x223; | ||
| 580 | sc_access[0].value = 0x20; | ||
| 581 | sc_access[0].mask = MASK7|MASK6|MASK5|MASK4|MASK0; | ||
| 582 | num_reg = 2; | ||
| 583 | break; | ||
| 584 | } | ||
| 585 | return sst_sc_reg_access(sc_access, PMIC_WRITE, num_reg); | ||
| 586 | } | ||
| 587 | |||
| 588 | static int mx_set_mute(int dev_id, u8 value) | ||
| 589 | { | ||
| 590 | struct sc_reg_access sc_access[5]; | ||
| 591 | int num_reg = 0; | ||
| 592 | int retval = 0; | ||
| 593 | |||
| 594 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 595 | retval = mx_init_card(); | ||
| 596 | if (retval) | ||
| 597 | return retval; | ||
| 598 | } | ||
| 599 | |||
| 600 | |||
| 601 | pr_debug("set_mute dev_id:0x%x , value:%d\n", dev_id, value); | ||
| 602 | |||
| 603 | switch (dev_id) { | ||
| 604 | case PMIC_SND_DMIC_MUTE: | ||
| 605 | case PMIC_SND_AMIC_MUTE: | ||
| 606 | case PMIC_SND_HP_MIC_MUTE: | ||
| 607 | sc_access[0].reg_addr = 0x220; | ||
| 608 | sc_access[1].reg_addr = 0x221; | ||
| 609 | sc_access[2].reg_addr = 0x223; | ||
| 610 | if (value == MUTE) { | ||
| 611 | sc_access[0].value = 0x00; | ||
| 612 | sc_access[1].value = 0x00; | ||
| 613 | if (snd_pmic_ops_mx.input_dev_id == DMIC) | ||
| 614 | sc_access[2].value = 0x00; | ||
| 615 | else | ||
| 616 | sc_access[2].value = 0x20; | ||
| 617 | } else { | ||
| 618 | sc_access[0].value = 0x20; | ||
| 619 | sc_access[1].value = 0x20; | ||
| 620 | if (snd_pmic_ops_mx.input_dev_id == DMIC) | ||
| 621 | sc_access[2].value = 0x20; | ||
| 622 | else | ||
| 623 | sc_access[2].value = 0x00; | ||
| 624 | } | ||
| 625 | sc_access[0].mask = MASK5|MASK6; | ||
| 626 | sc_access[1].mask = MASK5|MASK6; | ||
| 627 | sc_access[2].mask = MASK5|MASK6; | ||
| 628 | num_reg = 3; | ||
| 629 | break; | ||
| 630 | case PMIC_SND_LEFT_SPEAKER_MUTE: | ||
| 631 | case PMIC_SND_LEFT_HP_MUTE: | ||
| 632 | sc_access[0].reg_addr = VOL_CTRL_LT; | ||
| 633 | if (value == MUTE) | ||
| 634 | sc_access[0].value = 0x40; | ||
| 635 | else | ||
| 636 | sc_access[0].value = 0x00; | ||
| 637 | sc_access[0].mask = MASK6; | ||
| 638 | num_reg = 1; | ||
| 639 | snd_pmic_ops_mx.mute_status = value; | ||
| 640 | break; | ||
| 641 | case PMIC_SND_RIGHT_SPEAKER_MUTE: | ||
| 642 | case PMIC_SND_RIGHT_HP_MUTE: | ||
| 643 | sc_access[0].reg_addr = VOL_CTRL_RT; | ||
| 644 | if (snd_pmic_ops_mx.num_channel == 1) | ||
| 645 | value = MUTE; | ||
| 646 | if (value == MUTE) | ||
| 647 | sc_access[0].value = 0x40; | ||
| 648 | else | ||
| 649 | sc_access[0].value = 0x00; | ||
| 650 | sc_access[0].mask = MASK6; | ||
| 651 | num_reg = 1; | ||
| 652 | snd_pmic_ops_mx.mute_status = value; | ||
| 653 | break; | ||
| 654 | case PMIC_SND_MUTE_ALL: | ||
| 655 | sc_access[0].reg_addr = VOL_CTRL_RT; | ||
| 656 | sc_access[1].reg_addr = VOL_CTRL_LT; | ||
| 657 | sc_access[2].reg_addr = 0x220; | ||
| 658 | sc_access[3].reg_addr = 0x221; | ||
| 659 | sc_access[4].reg_addr = 0x223; | ||
| 660 | snd_pmic_ops_mx.master_mute = value; | ||
| 661 | if (value == MUTE) { | ||
| 662 | sc_access[0].value = sc_access[1].value = 0x40; | ||
| 663 | sc_access[2].value = 0x00; | ||
| 664 | sc_access[3].value = 0x00; | ||
| 665 | if (snd_pmic_ops_mx.input_dev_id == DMIC) | ||
| 666 | sc_access[4].value = 0x00; | ||
| 667 | else | ||
| 668 | sc_access[4].value = 0x20; | ||
| 669 | |||
| 670 | } else { | ||
| 671 | sc_access[0].value = sc_access[1].value = 0x00; | ||
| 672 | sc_access[2].value = sc_access[3].value = 0x20; | ||
| 673 | sc_access[4].value = 0x20; | ||
| 674 | if (snd_pmic_ops_mx.input_dev_id == DMIC) | ||
| 675 | sc_access[4].value = 0x20; | ||
| 676 | else | ||
| 677 | sc_access[4].value = 0x00; | ||
| 678 | |||
| 679 | |||
| 680 | } | ||
| 681 | if (snd_pmic_ops_mx.num_channel == 1) | ||
| 682 | sc_access[0].value = 0x40; | ||
| 683 | sc_access[0].mask = sc_access[1].mask = MASK6; | ||
| 684 | sc_access[2].mask = MASK5|MASK6; | ||
| 685 | sc_access[3].mask = MASK5|MASK6|MASK2|MASK4; | ||
| 686 | sc_access[4].mask = MASK5|MASK6|MASK4; | ||
| 687 | |||
| 688 | num_reg = 5; | ||
| 689 | break; | ||
| 690 | case PMIC_SND_RECEIVER_MUTE: | ||
| 691 | sc_access[0].reg_addr = VOL_CTRL_RT; | ||
| 692 | if (value == MUTE) | ||
| 693 | sc_access[0].value = 0x40; | ||
| 694 | else | ||
| 695 | sc_access[0].value = 0x00; | ||
| 696 | sc_access[0].mask = MASK6; | ||
| 697 | num_reg = 1; | ||
| 698 | break; | ||
| 699 | } | ||
| 700 | |||
| 701 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); | ||
| 702 | } | ||
| 703 | |||
| 704 | static int mx_set_vol(int dev_id, int value) | ||
| 705 | { | ||
| 706 | struct sc_reg_access sc_access[2] = {{0},}; | ||
| 707 | int num_reg = 0; | ||
| 708 | int retval = 0; | ||
| 709 | |||
| 710 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 711 | retval = mx_init_card(); | ||
| 712 | if (retval) | ||
| 713 | return retval; | ||
| 714 | } | ||
| 715 | pr_debug("set_vol dev_id:0x%x ,value:%d\n", dev_id, value); | ||
| 716 | switch (dev_id) { | ||
| 717 | case PMIC_SND_RECEIVER_VOL: | ||
| 718 | return 0; | ||
| 719 | break; | ||
| 720 | case PMIC_SND_CAPTURE_VOL: | ||
| 721 | sc_access[0].reg_addr = 0x220; | ||
| 722 | sc_access[1].reg_addr = 0x221; | ||
| 723 | sc_access[0].value = sc_access[1].value = -value; | ||
| 724 | sc_access[0].mask = sc_access[1].mask = | ||
| 725 | (MASK0|MASK1|MASK2|MASK3|MASK4); | ||
| 726 | num_reg = 2; | ||
| 727 | break; | ||
| 728 | case PMIC_SND_LEFT_PB_VOL: | ||
| 729 | sc_access[0].value = -value; | ||
| 730 | sc_access[0].reg_addr = VOL_CTRL_LT; | ||
| 731 | sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); | ||
| 732 | num_reg = 1; | ||
| 733 | break; | ||
| 734 | case PMIC_SND_RIGHT_PB_VOL: | ||
| 735 | sc_access[0].value = -value; | ||
| 736 | sc_access[0].reg_addr = VOL_CTRL_RT; | ||
| 737 | sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); | ||
| 738 | if (snd_pmic_ops_mx.num_channel == 1) { | ||
| 739 | sc_access[0].value = 0x40; | ||
| 740 | sc_access[0].mask = MASK6; | ||
| 741 | sc_access[0].reg_addr = VOL_CTRL_RT; | ||
| 742 | } | ||
| 743 | num_reg = 1; | ||
| 744 | break; | ||
| 745 | } | ||
| 746 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_reg); | ||
| 747 | } | ||
| 748 | |||
| 749 | static int mx_get_mute(int dev_id, u8 *value) | ||
| 750 | { | ||
| 751 | struct sc_reg_access sc_access[4] = {{0},}; | ||
| 752 | int retval = 0, num_reg = 0, mask = 0; | ||
| 753 | |||
| 754 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 755 | retval = mx_init_card(); | ||
| 756 | if (retval) | ||
| 757 | return retval; | ||
| 758 | } | ||
| 759 | switch (dev_id) { | ||
| 760 | case PMIC_SND_DMIC_MUTE: | ||
| 761 | case PMIC_SND_AMIC_MUTE: | ||
| 762 | case PMIC_SND_HP_MIC_MUTE: | ||
| 763 | sc_access[0].reg_addr = 0x220; | ||
| 764 | mask = MASK5|MASK6; | ||
| 765 | num_reg = 1; | ||
| 766 | retval = sst_sc_reg_access(sc_access, PMIC_READ, num_reg); | ||
| 767 | if (retval) | ||
| 768 | return retval; | ||
| 769 | *value = sc_access[0].value & mask; | ||
| 770 | if (*value) | ||
| 771 | *value = UNMUTE; | ||
| 772 | else | ||
| 773 | *value = MUTE; | ||
| 774 | return retval; | ||
| 775 | case PMIC_SND_LEFT_HP_MUTE: | ||
| 776 | case PMIC_SND_LEFT_SPEAKER_MUTE: | ||
| 777 | sc_access[0].reg_addr = VOL_CTRL_LT; | ||
| 778 | num_reg = 1; | ||
| 779 | mask = MASK6; | ||
| 780 | break; | ||
| 781 | case PMIC_SND_RIGHT_HP_MUTE: | ||
| 782 | case PMIC_SND_RIGHT_SPEAKER_MUTE: | ||
| 783 | sc_access[0].reg_addr = VOL_CTRL_RT; | ||
| 784 | num_reg = 1; | ||
| 785 | mask = MASK6; | ||
| 786 | break; | ||
| 787 | } | ||
| 788 | retval = sst_sc_reg_access(sc_access, PMIC_READ, num_reg); | ||
| 789 | if (retval) | ||
| 790 | return retval; | ||
| 791 | *value = sc_access[0].value & mask; | ||
| 792 | if (*value) | ||
| 793 | *value = MUTE; | ||
| 794 | else | ||
| 795 | *value = UNMUTE; | ||
| 796 | return retval; | ||
| 797 | } | ||
| 798 | |||
| 799 | static int mx_get_vol(int dev_id, int *value) | ||
| 800 | { | ||
| 801 | struct sc_reg_access sc_access = {0,}; | ||
| 802 | int retval = 0, mask = 0, num_reg = 0; | ||
| 803 | |||
| 804 | if (snd_pmic_ops_mx.card_status == SND_CARD_UN_INIT) { | ||
| 805 | retval = mx_init_card(); | ||
| 806 | if (retval) | ||
| 807 | return retval; | ||
| 808 | } | ||
| 809 | switch (dev_id) { | ||
| 810 | case PMIC_SND_CAPTURE_VOL: | ||
| 811 | sc_access.reg_addr = 0x220; | ||
| 812 | mask = MASK0|MASK1|MASK2|MASK3|MASK4; | ||
| 813 | num_reg = 1; | ||
| 814 | break; | ||
| 815 | case PMIC_SND_LEFT_PB_VOL: | ||
| 816 | sc_access.reg_addr = VOL_CTRL_LT; | ||
| 817 | mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5; | ||
| 818 | num_reg = 1; | ||
| 819 | break; | ||
| 820 | case PMIC_SND_RIGHT_PB_VOL: | ||
| 821 | sc_access.reg_addr = VOL_CTRL_RT; | ||
| 822 | mask = MASK0|MASK1|MASK2|MASK3|MASK4|MASK5; | ||
| 823 | num_reg = 1; | ||
| 824 | break; | ||
| 825 | } | ||
| 826 | retval = sst_sc_reg_access(&sc_access, PMIC_READ, num_reg); | ||
| 827 | if (retval) | ||
| 828 | return retval; | ||
| 829 | *value = -(sc_access.value & mask); | ||
| 830 | pr_debug("get volume value extracted %d\n", *value); | ||
| 831 | return retval; | ||
| 832 | } | ||
| 833 | |||
| 834 | static u8 mx_get_jack_status(void) | ||
| 835 | { | ||
| 836 | struct sc_reg_access sc_access_read = {0,}; | ||
| 837 | |||
| 838 | sc_access_read.reg_addr = 0x201; | ||
| 839 | sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); | ||
| 840 | pr_debug("value returned = 0x%x\n", sc_access_read.value); | ||
| 841 | return sc_access_read.value; | ||
| 842 | } | ||
| 843 | |||
| 844 | static void mx_pmic_irq_enable(void *data) | ||
| 845 | { | ||
| 846 | struct snd_intelmad *intelmaddata = data; | ||
| 847 | |||
| 848 | intelmaddata->jack_prev_state = 0xc0; | ||
| 849 | return; | ||
| 850 | } | ||
| 851 | |||
| 852 | static void mx_pmic_irq_cb(void *cb_data, u8 intsts) | ||
| 853 | { | ||
| 854 | u8 jack_cur_status, jack_prev_state = 0; | ||
| 855 | struct mad_jack *mjack = NULL; | ||
| 856 | unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; | ||
| 857 | time_t timediff; | ||
| 858 | struct snd_intelmad *intelmaddata = cb_data; | ||
| 859 | |||
| 860 | mjack = &intelmaddata->jack[0]; | ||
| 861 | if (intsts & 0x2) { | ||
| 862 | jack_cur_status = mx_get_jack_status(); | ||
| 863 | jack_prev_state = intelmaddata->jack_prev_state; | ||
| 864 | if ((jack_prev_state == 0xc0) && (jack_cur_status == 0x40)) { | ||
| 865 | /*headset insert detected. */ | ||
| 866 | pr_debug("MAD headset inserted\n"); | ||
| 867 | present = 1; | ||
| 868 | jack_event_flag = 1; | ||
| 869 | mjack->jack_status = 1; | ||
| 870 | mjack->jack.type = SND_JACK_HEADSET; | ||
| 871 | } | ||
| 872 | |||
| 873 | if ((jack_prev_state == 0xc0) && (jack_cur_status == 0x00)) { | ||
| 874 | /* headphone insert detected. */ | ||
| 875 | pr_debug("MAD headphone inserted\n"); | ||
| 876 | present = 1; | ||
| 877 | jack_event_flag = 1; | ||
| 878 | mjack->jack.type = SND_JACK_HEADPHONE; | ||
| 879 | } | ||
| 880 | |||
| 881 | if ((jack_prev_state == 0x40) && (jack_cur_status == 0xc0)) { | ||
| 882 | /* headset remove detected. */ | ||
| 883 | pr_debug("MAD headset removed\n"); | ||
| 884 | |||
| 885 | present = 0; | ||
| 886 | jack_event_flag = 1; | ||
| 887 | mjack->jack_status = 0; | ||
| 888 | mjack->jack.type = SND_JACK_HEADSET; | ||
| 889 | } | ||
| 890 | |||
| 891 | if ((jack_prev_state == 0x00) && (jack_cur_status == 0xc0)) { | ||
| 892 | /* headphone remove detected. */ | ||
| 893 | pr_debug("MAD headphone removed\n"); | ||
| 894 | present = 0; | ||
| 895 | jack_event_flag = 1; | ||
| 896 | mjack->jack.type = SND_JACK_HEADPHONE; | ||
| 897 | } | ||
| 898 | |||
| 899 | if ((jack_prev_state == 0x40) && (jack_cur_status == 0x00)) { | ||
| 900 | /* button pressed */ | ||
| 901 | do_gettimeofday(&mjack->buttonpressed); | ||
| 902 | pr_debug("MAD button press detected\n"); | ||
| 903 | } | ||
| 904 | |||
| 905 | if ((jack_prev_state == 0x00) && (jack_cur_status == 0x40)) { | ||
| 906 | if (mjack->jack_status) { | ||
| 907 | /*button pressed */ | ||
| 908 | do_gettimeofday( | ||
| 909 | &mjack->buttonreleased); | ||
| 910 | /*button pressed */ | ||
| 911 | pr_debug("MAD Button Released detected\n"); | ||
| 912 | timediff = mjack->buttonreleased.tv_sec - | ||
| 913 | mjack->buttonpressed.tv_sec; | ||
| 914 | buttonpressflag = 1; | ||
| 915 | |||
| 916 | if (timediff > 1) { | ||
| 917 | pr_debug("MAD long press dtd\n"); | ||
| 918 | /* send headphone detect/undetect */ | ||
| 919 | present = 1; | ||
| 920 | jack_event_flag = 1; | ||
| 921 | mjack->jack.type = | ||
| 922 | MID_JACK_HS_LONG_PRESS; | ||
| 923 | } else { | ||
| 924 | pr_debug("MAD short press dtd\n"); | ||
| 925 | /* send headphone detect/undetect */ | ||
| 926 | present = 1; | ||
| 927 | jack_event_flag = 1; | ||
| 928 | mjack->jack.type = | ||
| 929 | MID_JACK_HS_SHORT_PRESS; | ||
| 930 | } | ||
| 931 | } else { | ||
| 932 | /***workaround for maxim | ||
| 933 | hw issue,0x00 t 0x40 is not | ||
| 934 | a valid transiton for Headset insertion */ | ||
| 935 | /*headset insert detected. */ | ||
| 936 | pr_debug("MAD headset inserted\n"); | ||
| 937 | present = 1; | ||
| 938 | jack_event_flag = 1; | ||
| 939 | mjack->jack_status = 1; | ||
| 940 | mjack->jack.type = SND_JACK_HEADSET; | ||
| 941 | } | ||
| 942 | } | ||
| 943 | intelmaddata->jack_prev_state = jack_cur_status; | ||
| 944 | pr_debug("mx_pmic_irq_cb prv_state= 0x%x\n", | ||
| 945 | intelmaddata->jack_prev_state); | ||
| 946 | } | ||
| 947 | |||
| 948 | if (jack_event_flag) | ||
| 949 | sst_mad_send_jack_report(&mjack->jack, | ||
| 950 | buttonpressflag, present); | ||
| 951 | } | ||
| 952 | static int mx_jack_enable(void) | ||
| 953 | { | ||
| 954 | return 0; | ||
| 955 | } | ||
| 956 | |||
| 957 | struct snd_pmic_ops snd_pmic_ops_mx = { | ||
| 958 | .set_input_dev = mx_set_selected_input_dev, | ||
| 959 | .set_output_dev = mx_set_selected_output_dev, | ||
| 960 | .set_mute = mx_set_mute, | ||
| 961 | .get_mute = mx_get_mute, | ||
| 962 | .set_vol = mx_set_vol, | ||
| 963 | .get_vol = mx_get_vol, | ||
| 964 | .init_card = mx_init_card, | ||
| 965 | .set_pcm_audio_params = mx_set_pcm_audio_params, | ||
| 966 | .set_pcm_voice_params = mx_set_pcm_voice_params, | ||
| 967 | .set_voice_port = mx_set_voice_port, | ||
| 968 | .set_audio_port = mx_set_audio_port, | ||
| 969 | .power_up_pmic_pb = mx_power_up_pb, | ||
| 970 | .power_up_pmic_cp = mx_power_up_cp, | ||
| 971 | .power_down_pmic_pb = mx_power_down_pb, | ||
| 972 | .power_down_pmic_cp = mx_power_down_cp, | ||
| 973 | .power_down_pmic = mx_power_down, | ||
| 974 | .pmic_irq_cb = mx_pmic_irq_cb, | ||
| 975 | .pmic_irq_enable = mx_pmic_irq_enable, | ||
| 976 | .pmic_jack_enable = mx_jack_enable, | ||
| 977 | }; | ||
| 978 | |||
diff --git a/drivers/staging/intel_sst/intelmid_v2_control.c b/drivers/staging/intel_sst/intelmid_v2_control.c new file mode 100644 index 00000000000..46ab55eb809 --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_v2_control.c | |||
| @@ -0,0 +1,1156 @@ | |||
| 1 | /* | ||
| 2 | * intelmid_v2_control.c - Intel Sound card driver for MID | ||
| 3 | * | ||
| 4 | * Copyright (C) 2008-10 Intel Corp | ||
| 5 | * Authors: Vinod Koul <vinod.koul@intel.com> | ||
| 6 | * Harsha Priya <priya.harsha@intel.com> | ||
| 7 | * KP Jeeja <jeeja.kp@intel.com> | ||
| 8 | * Dharageswari R <dharageswari.r@intel.com> | ||
| 9 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 10 | * | ||
| 11 | * This program is free software; you can redistribute it and/or modify | ||
| 12 | * it under the terms of the GNU General Public License as published by | ||
| 13 | * the Free Software Foundation; version 2 of the License. | ||
| 14 | * | ||
| 15 | * This program is distributed in the hope that it will be useful, but | ||
| 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 18 | * General Public License for more details. | ||
| 19 | * | ||
| 20 | * You should have received a copy of the GNU General Public License along | ||
| 21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 22 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
| 23 | * | ||
| 24 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 25 | * | ||
| 26 | * This file contains the control operations of vendor 3 | ||
| 27 | */ | ||
| 28 | |||
| 29 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 30 | |||
| 31 | #include <linux/gpio.h> | ||
| 32 | #include <linux/pci.h> | ||
| 33 | #include <linux/delay.h> | ||
| 34 | #include <linux/file.h> | ||
| 35 | #include <sound/control.h> | ||
| 36 | #include "intel_sst.h" | ||
| 37 | #include "intelmid_snd_control.h" | ||
| 38 | #include "intelmid.h" | ||
| 39 | |||
| 40 | enum reg_v3 { | ||
| 41 | VAUDIOCNT = 0x51, | ||
| 42 | VOICEPORT1 = 0x100, | ||
| 43 | VOICEPORT2 = 0x101, | ||
| 44 | AUDIOPORT1 = 0x102, | ||
| 45 | AUDIOPORT2 = 0x103, | ||
| 46 | ADCSAMPLERATE = 0x104, | ||
| 47 | DMICCTRL1 = 0x105, | ||
| 48 | DMICCTRL2 = 0x106, | ||
| 49 | MICCTRL = 0x107, | ||
| 50 | MICSELVOL = 0x108, | ||
| 51 | LILSEL = 0x109, | ||
| 52 | LIRSEL = 0x10a, | ||
| 53 | VOICEVOL = 0x10b, | ||
| 54 | AUDIOLVOL = 0x10c, | ||
| 55 | AUDIORVOL = 0x10d, | ||
| 56 | LMUTE = 0x10e, | ||
| 57 | RMUTE = 0x10f, | ||
| 58 | POWERCTRL1 = 0x110, | ||
| 59 | POWERCTRL2 = 0x111, | ||
| 60 | DRVPOWERCTRL = 0x112, | ||
| 61 | VREFPLL = 0x113, | ||
| 62 | PCMBUFCTRL = 0x114, | ||
| 63 | SOFTMUTE = 0x115, | ||
| 64 | DTMFPATH = 0x116, | ||
| 65 | DTMFVOL = 0x117, | ||
| 66 | DTMFFREQ = 0x118, | ||
| 67 | DTMFHFREQ = 0x119, | ||
| 68 | DTMFLFREQ = 0x11a, | ||
| 69 | DTMFCTRL = 0x11b, | ||
| 70 | DTMFASON = 0x11c, | ||
| 71 | DTMFASOFF = 0x11d, | ||
| 72 | DTMFASINUM = 0x11e, | ||
| 73 | CLASSDVOL = 0x11f, | ||
| 74 | VOICEDACAVOL = 0x120, | ||
| 75 | AUDDACAVOL = 0x121, | ||
| 76 | LOMUTEVOL = 0x122, | ||
| 77 | HPLVOL = 0x123, | ||
| 78 | HPRVOL = 0x124, | ||
| 79 | MONOVOL = 0x125, | ||
| 80 | LINEOUTMIXVOL = 0x126, | ||
| 81 | EPMIXVOL = 0x127, | ||
| 82 | LINEOUTLSEL = 0x128, | ||
| 83 | LINEOUTRSEL = 0x129, | ||
| 84 | EPMIXOUTSEL = 0x12a, | ||
| 85 | HPLMIXSEL = 0x12b, | ||
| 86 | HPRMIXSEL = 0x12c, | ||
| 87 | LOANTIPOP = 0x12d, | ||
| 88 | AUXDBNC = 0x12f, | ||
| 89 | }; | ||
| 90 | |||
| 91 | static void nc_set_amp_power(int power) | ||
| 92 | { | ||
| 93 | if (snd_pmic_ops_nc.gpio_amp) | ||
| 94 | gpio_set_value(snd_pmic_ops_nc.gpio_amp, power); | ||
| 95 | } | ||
| 96 | |||
| 97 | /**** | ||
| 98 | * nc_init_card - initialize the sound card | ||
| 99 | * | ||
| 100 | * This initializes the audio paths to know values in case of this sound card | ||
| 101 | */ | ||
| 102 | static int nc_init_card(void) | ||
| 103 | { | ||
| 104 | struct sc_reg_access sc_access[] = { | ||
| 105 | {VAUDIOCNT, 0x25, 0}, | ||
| 106 | {VOICEPORT1, 0x00, 0}, | ||
| 107 | {VOICEPORT2, 0x00, 0}, | ||
| 108 | {AUDIOPORT1, 0x98, 0}, | ||
| 109 | {AUDIOPORT2, 0x09, 0}, | ||
| 110 | {AUDIOLVOL, 0x00, 0}, | ||
| 111 | {AUDIORVOL, 0x00, 0}, | ||
| 112 | {LMUTE, 0x03, 0}, | ||
| 113 | {RMUTE, 0x03, 0}, | ||
| 114 | {POWERCTRL1, 0x00, 0}, | ||
| 115 | {POWERCTRL2, 0x00, 0}, | ||
| 116 | {DRVPOWERCTRL, 0x00, 0}, | ||
| 117 | {VREFPLL, 0x10, 0}, | ||
| 118 | {HPLMIXSEL, 0xee, 0}, | ||
| 119 | {HPRMIXSEL, 0xf6, 0}, | ||
| 120 | {PCMBUFCTRL, 0x0, 0}, | ||
| 121 | {VOICEVOL, 0x0e, 0}, | ||
| 122 | {HPLVOL, 0x06, 0}, | ||
| 123 | {HPRVOL, 0x06, 0}, | ||
| 124 | {MICCTRL, 0x51, 0x00}, | ||
| 125 | {ADCSAMPLERATE, 0x8B, 0x00}, | ||
| 126 | {MICSELVOL, 0x5B, 0x00}, | ||
| 127 | {LILSEL, 0x06, 0}, | ||
| 128 | {LIRSEL, 0x46, 0}, | ||
| 129 | {LOANTIPOP, 0x00, 0}, | ||
| 130 | {DMICCTRL1, 0x40, 0}, | ||
| 131 | {AUXDBNC, 0xff, 0}, | ||
| 132 | }; | ||
| 133 | snd_pmic_ops_nc.card_status = SND_CARD_INIT_DONE; | ||
| 134 | snd_pmic_ops_nc.master_mute = UNMUTE; | ||
| 135 | snd_pmic_ops_nc.mute_status = UNMUTE; | ||
| 136 | sst_sc_reg_access(sc_access, PMIC_WRITE, 27); | ||
| 137 | mutex_init(&snd_pmic_ops_nc.lock); | ||
| 138 | pr_debug("init complete!!\n"); | ||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | static int nc_enable_audiodac(int value) | ||
| 143 | { | ||
| 144 | struct sc_reg_access sc_access[3]; | ||
| 145 | int mute_val = 0; | ||
| 146 | |||
| 147 | if (snd_pmic_ops_nc.mute_status == MUTE) | ||
| 148 | return 0; | ||
| 149 | |||
| 150 | if (((snd_pmic_ops_nc.output_dev_id == MONO_EARPIECE) || | ||
| 151 | (snd_pmic_ops_nc.output_dev_id == INTERNAL_SPKR)) && | ||
| 152 | (value == UNMUTE)) | ||
| 153 | return 0; | ||
| 154 | if (value == UNMUTE) { | ||
| 155 | /* unmute the system, set the 7th bit to zero */ | ||
| 156 | mute_val = 0x00; | ||
| 157 | } else { | ||
| 158 | /* MUTE:Set the seventh bit */ | ||
| 159 | mute_val = 0x04; | ||
| 160 | |||
| 161 | } | ||
| 162 | sc_access[0].reg_addr = LMUTE; | ||
| 163 | sc_access[1].reg_addr = RMUTE; | ||
| 164 | sc_access[0].mask = sc_access[1].mask = MASK2; | ||
| 165 | sc_access[0].value = sc_access[1].value = mute_val; | ||
| 166 | |||
| 167 | if (snd_pmic_ops_nc.num_channel == 1) | ||
| 168 | sc_access[1].value = 0x04; | ||
| 169 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 170 | |||
| 171 | } | ||
| 172 | |||
| 173 | static int nc_power_up_pb(unsigned int port) | ||
| 174 | { | ||
| 175 | struct sc_reg_access sc_access[7]; | ||
| 176 | int retval = 0; | ||
| 177 | |||
| 178 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 179 | retval = nc_init_card(); | ||
| 180 | if (retval) | ||
| 181 | return retval; | ||
| 182 | if (port == 0xFF) | ||
| 183 | return 0; | ||
| 184 | mutex_lock(&snd_pmic_ops_nc.lock); | ||
| 185 | nc_enable_audiodac(MUTE); | ||
| 186 | msleep(30); | ||
| 187 | |||
| 188 | pr_debug("powering up pb....\n"); | ||
| 189 | |||
| 190 | sc_access[0].reg_addr = VAUDIOCNT; | ||
| 191 | sc_access[0].value = 0x27; | ||
| 192 | sc_access[0].mask = 0x27; | ||
| 193 | sc_access[1].reg_addr = VREFPLL; | ||
| 194 | if (port == 0) { | ||
| 195 | sc_access[1].value = 0x3A; | ||
| 196 | sc_access[1].mask = 0x3A; | ||
| 197 | } else if (port == 1) { | ||
| 198 | sc_access[1].value = 0x35; | ||
| 199 | sc_access[1].mask = 0x35; | ||
| 200 | } | ||
| 201 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 202 | |||
| 203 | |||
| 204 | |||
| 205 | sc_access[0].reg_addr = POWERCTRL1; | ||
| 206 | if (port == 0) { | ||
| 207 | sc_access[0].value = 0x40; | ||
| 208 | sc_access[0].mask = 0x40; | ||
| 209 | } else if (port == 1) { | ||
| 210 | sc_access[0].value = 0x01; | ||
| 211 | sc_access[0].mask = 0x01; | ||
| 212 | } | ||
| 213 | sc_access[1].reg_addr = POWERCTRL2; | ||
| 214 | sc_access[1].value = 0x0C; | ||
| 215 | sc_access[1].mask = 0x0C; | ||
| 216 | |||
| 217 | sc_access[2].reg_addr = DRVPOWERCTRL; | ||
| 218 | sc_access[2].value = 0x86; | ||
| 219 | sc_access[2].mask = 0x86; | ||
| 220 | |||
| 221 | sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); | ||
| 222 | |||
| 223 | msleep(30); | ||
| 224 | |||
| 225 | snd_pmic_ops_nc.pb_on = 1; | ||
| 226 | |||
| 227 | /* | ||
| 228 | * There is a mismatch between Playback Sources and the enumerated | ||
| 229 | * values of output sources. This mismatch causes ALSA upper to send | ||
| 230 | * Item 1 for Internal Speaker, but the expected enumeration is 2! For | ||
| 231 | * now, treat MONO_EARPIECE and INTERNAL_SPKR identically and power up | ||
| 232 | * the needed resources | ||
| 233 | */ | ||
| 234 | if (snd_pmic_ops_nc.output_dev_id == MONO_EARPIECE || | ||
| 235 | snd_pmic_ops_nc.output_dev_id == INTERNAL_SPKR) | ||
| 236 | nc_set_amp_power(1); | ||
| 237 | nc_enable_audiodac(UNMUTE); | ||
| 238 | mutex_unlock(&snd_pmic_ops_nc.lock); | ||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int nc_power_up_cp(unsigned int port) | ||
| 243 | { | ||
| 244 | struct sc_reg_access sc_access[5]; | ||
| 245 | int retval = 0; | ||
| 246 | |||
| 247 | |||
| 248 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 249 | retval = nc_init_card(); | ||
| 250 | if (retval) | ||
| 251 | return retval; | ||
| 252 | |||
| 253 | |||
| 254 | pr_debug("powering up cp....\n"); | ||
| 255 | |||
| 256 | if (port == 0xFF) | ||
| 257 | return 0; | ||
| 258 | sc_access[0].reg_addr = VAUDIOCNT; | ||
| 259 | sc_access[0].value = 0x27; | ||
| 260 | sc_access[0].mask = 0x27; | ||
| 261 | sc_access[1].reg_addr = VREFPLL; | ||
| 262 | if (port == 0) { | ||
| 263 | sc_access[1].value = 0x3E; | ||
| 264 | sc_access[1].mask = 0x3E; | ||
| 265 | } else if (port == 1) { | ||
| 266 | sc_access[1].value = 0x35; | ||
| 267 | sc_access[1].mask = 0x35; | ||
| 268 | } | ||
| 269 | |||
| 270 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 271 | |||
| 272 | |||
| 273 | sc_access[0].reg_addr = POWERCTRL1; | ||
| 274 | if (port == 0) { | ||
| 275 | sc_access[0].value = 0xB4; | ||
| 276 | sc_access[0].mask = 0xB4; | ||
| 277 | } else if (port == 1) { | ||
| 278 | sc_access[0].value = 0xBF; | ||
| 279 | sc_access[0].mask = 0xBF; | ||
| 280 | } | ||
| 281 | sc_access[1].reg_addr = POWERCTRL2; | ||
| 282 | if (port == 0) { | ||
| 283 | sc_access[1].value = 0x0C; | ||
| 284 | sc_access[1].mask = 0x0C; | ||
| 285 | } else if (port == 1) { | ||
| 286 | sc_access[1].value = 0x02; | ||
| 287 | sc_access[1].mask = 0x02; | ||
| 288 | } | ||
| 289 | |||
| 290 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 291 | |||
| 292 | } | ||
| 293 | |||
| 294 | static int nc_power_down(void) | ||
| 295 | { | ||
| 296 | int retval = 0; | ||
| 297 | struct sc_reg_access sc_access[5]; | ||
| 298 | |||
| 299 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 300 | retval = nc_init_card(); | ||
| 301 | if (retval) | ||
| 302 | return retval; | ||
| 303 | nc_enable_audiodac(MUTE); | ||
| 304 | |||
| 305 | |||
| 306 | pr_debug("powering dn nc_power_down ....\n"); | ||
| 307 | |||
| 308 | if (snd_pmic_ops_nc.output_dev_id == MONO_EARPIECE || | ||
| 309 | snd_pmic_ops_nc.output_dev_id == INTERNAL_SPKR) | ||
| 310 | nc_set_amp_power(0); | ||
| 311 | |||
| 312 | msleep(30); | ||
| 313 | |||
| 314 | sc_access[0].reg_addr = DRVPOWERCTRL; | ||
| 315 | sc_access[0].value = 0x00; | ||
| 316 | sc_access[0].mask = 0x00; | ||
| 317 | |||
| 318 | sst_sc_reg_access(sc_access, PMIC_WRITE, 1); | ||
| 319 | |||
| 320 | sc_access[0].reg_addr = POWERCTRL1; | ||
| 321 | sc_access[0].value = 0x00; | ||
| 322 | sc_access[0].mask = 0x00; | ||
| 323 | |||
| 324 | sc_access[1].reg_addr = POWERCTRL2; | ||
| 325 | sc_access[1].value = 0x00; | ||
| 326 | sc_access[1].mask = 0x00; | ||
| 327 | |||
| 328 | |||
| 329 | |||
| 330 | sst_sc_reg_access(sc_access, PMIC_WRITE, 2); | ||
| 331 | |||
| 332 | msleep(30); | ||
| 333 | sc_access[0].reg_addr = VREFPLL; | ||
| 334 | sc_access[0].value = 0x10; | ||
| 335 | sc_access[0].mask = 0x10; | ||
| 336 | |||
| 337 | sc_access[1].reg_addr = VAUDIOCNT; | ||
| 338 | sc_access[1].value = 0x25; | ||
| 339 | sc_access[1].mask = 0x25; | ||
| 340 | |||
| 341 | |||
| 342 | retval = sst_sc_reg_access(sc_access, PMIC_WRITE, 2); | ||
| 343 | |||
| 344 | msleep(30); | ||
| 345 | return nc_enable_audiodac(UNMUTE); | ||
| 346 | } | ||
| 347 | |||
| 348 | static int nc_power_down_pb(unsigned int device) | ||
| 349 | { | ||
| 350 | |||
| 351 | int retval = 0; | ||
| 352 | struct sc_reg_access sc_access[5]; | ||
| 353 | |||
| 354 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 355 | retval = nc_init_card(); | ||
| 356 | if (retval) | ||
| 357 | return retval; | ||
| 358 | |||
| 359 | pr_debug("powering dn pb....\n"); | ||
| 360 | mutex_lock(&snd_pmic_ops_nc.lock); | ||
| 361 | nc_enable_audiodac(MUTE); | ||
| 362 | |||
| 363 | |||
| 364 | msleep(30); | ||
| 365 | |||
| 366 | |||
| 367 | sc_access[0].reg_addr = DRVPOWERCTRL; | ||
| 368 | sc_access[0].value = 0x00; | ||
| 369 | sc_access[0].mask = 0x00; | ||
| 370 | |||
| 371 | sst_sc_reg_access(sc_access, PMIC_WRITE, 1); | ||
| 372 | |||
| 373 | msleep(30); | ||
| 374 | |||
| 375 | sc_access[0].reg_addr = POWERCTRL1; | ||
| 376 | sc_access[0].value = 0x00; | ||
| 377 | sc_access[0].mask = 0x41; | ||
| 378 | |||
| 379 | sc_access[1].reg_addr = POWERCTRL2; | ||
| 380 | sc_access[1].value = 0x00; | ||
| 381 | sc_access[1].mask = 0x0C; | ||
| 382 | |||
| 383 | sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); | ||
| 384 | |||
| 385 | msleep(30); | ||
| 386 | |||
| 387 | snd_pmic_ops_nc.pb_on = 0; | ||
| 388 | |||
| 389 | nc_enable_audiodac(UNMUTE); | ||
| 390 | mutex_unlock(&snd_pmic_ops_nc.lock); | ||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | |||
| 394 | static int nc_power_down_cp(unsigned int device) | ||
| 395 | { | ||
| 396 | struct sc_reg_access sc_access[] = { | ||
| 397 | {POWERCTRL1, 0x00, 0xBE}, | ||
| 398 | {POWERCTRL2, 0x00, 0x02}, | ||
| 399 | }; | ||
| 400 | int retval = 0; | ||
| 401 | |||
| 402 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 403 | retval = nc_init_card(); | ||
| 404 | if (retval) | ||
| 405 | return retval; | ||
| 406 | |||
| 407 | pr_debug("powering dn cp....\n"); | ||
| 408 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 409 | } | ||
| 410 | |||
| 411 | static int nc_set_pcm_voice_params(void) | ||
| 412 | { | ||
| 413 | struct sc_reg_access sc_access[] = { | ||
| 414 | {0x100, 0xD5, 0}, | ||
| 415 | {0x101, 0x08, 0}, | ||
| 416 | {0x104, 0x03, 0}, | ||
| 417 | {0x107, 0x10, 0}, | ||
| 418 | {0x10B, 0x0E, 0}, | ||
| 419 | {0x10E, 0x03, 0}, | ||
| 420 | {0x10F, 0x03, 0}, | ||
| 421 | {0x114, 0x13, 0}, | ||
| 422 | {0x115, 0x00, 0}, | ||
| 423 | {0x128, 0xFE, 0}, | ||
| 424 | {0x129, 0xFE, 0}, | ||
| 425 | {0x12A, 0xFE, 0}, | ||
| 426 | {0x12B, 0xDE, 0}, | ||
| 427 | {0x12C, 0xDE, 0}, | ||
| 428 | }; | ||
| 429 | int retval = 0; | ||
| 430 | |||
| 431 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 432 | retval = nc_init_card(); | ||
| 433 | if (retval) | ||
| 434 | return retval; | ||
| 435 | |||
| 436 | sst_sc_reg_access(sc_access, PMIC_WRITE, 14); | ||
| 437 | pr_debug("Voice parameters set successfully!!\n"); | ||
| 438 | return 0; | ||
| 439 | } | ||
| 440 | |||
| 441 | |||
| 442 | static int nc_set_pcm_audio_params(int sfreq, int word_size, int num_channel) | ||
| 443 | { | ||
| 444 | int config2 = 0; | ||
| 445 | struct sc_reg_access sc_access; | ||
| 446 | int retval = 0; | ||
| 447 | |||
| 448 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 449 | retval = nc_init_card(); | ||
| 450 | if (retval) | ||
| 451 | return retval; | ||
| 452 | |||
| 453 | switch (sfreq) { | ||
| 454 | case 8000: | ||
| 455 | config2 = 0x00; | ||
| 456 | break; | ||
| 457 | case 11025: | ||
| 458 | config2 = 0x01; | ||
| 459 | break; | ||
| 460 | case 12000: | ||
| 461 | config2 = 0x02; | ||
| 462 | break; | ||
| 463 | case 16000: | ||
| 464 | config2 = 0x03; | ||
| 465 | break; | ||
| 466 | case 22050: | ||
| 467 | config2 = 0x04; | ||
| 468 | break; | ||
| 469 | case 24000: | ||
| 470 | config2 = 0x05; | ||
| 471 | break; | ||
| 472 | case 32000: | ||
| 473 | config2 = 0x07; | ||
| 474 | break; | ||
| 475 | case 44100: | ||
| 476 | config2 = 0x08; | ||
| 477 | break; | ||
| 478 | case 48000: | ||
| 479 | config2 = 0x09; | ||
| 480 | break; | ||
| 481 | } | ||
| 482 | |||
| 483 | snd_pmic_ops_nc.num_channel = num_channel; | ||
| 484 | if (snd_pmic_ops_nc.num_channel == 1) { | ||
| 485 | |||
| 486 | sc_access.value = 0x07; | ||
| 487 | sc_access.reg_addr = RMUTE; | ||
| 488 | pr_debug("RIGHT_HP_MUTE value%d\n", sc_access.value); | ||
| 489 | sc_access.mask = MASK2; | ||
| 490 | sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); | ||
| 491 | } else { | ||
| 492 | sc_access.value = 0x00; | ||
| 493 | sc_access.reg_addr = RMUTE; | ||
| 494 | pr_debug("RIGHT_HP_MUTE value %d\n", sc_access.value); | ||
| 495 | sc_access.mask = MASK2; | ||
| 496 | sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); | ||
| 497 | |||
| 498 | |||
| 499 | } | ||
| 500 | |||
| 501 | pr_debug("word_size = %d\n", word_size); | ||
| 502 | |||
| 503 | if (word_size == 24) { | ||
| 504 | sc_access.reg_addr = AUDIOPORT2; | ||
| 505 | sc_access.value = config2 | 0x10; | ||
| 506 | sc_access.mask = 0x1F; | ||
| 507 | } else { | ||
| 508 | sc_access.value = config2; | ||
| 509 | sc_access.mask = 0x1F; | ||
| 510 | sc_access.reg_addr = AUDIOPORT2; | ||
| 511 | } | ||
| 512 | sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); | ||
| 513 | |||
| 514 | pr_debug("word_size = %d\n", word_size); | ||
| 515 | sc_access.reg_addr = AUDIOPORT1; | ||
| 516 | sc_access.mask = MASK5|MASK4|MASK1|MASK0; | ||
| 517 | if (word_size == 16) | ||
| 518 | sc_access.value = 0x98; | ||
| 519 | else if (word_size == 24) | ||
| 520 | sc_access.value = 0xAB; | ||
| 521 | |||
| 522 | return sst_sc_reg_access(&sc_access, PMIC_READ_MODIFY, 1); | ||
| 523 | |||
| 524 | |||
| 525 | |||
| 526 | } | ||
| 527 | |||
| 528 | static int nc_set_selected_output_dev(u8 value) | ||
| 529 | { | ||
| 530 | struct sc_reg_access sc_access_HP[] = { | ||
| 531 | {LMUTE, 0x02, 0x06}, | ||
| 532 | {RMUTE, 0x02, 0x06}, | ||
| 533 | {DRVPOWERCTRL, 0x06, 0x06}, | ||
| 534 | }; | ||
| 535 | struct sc_reg_access sc_access_IS[] = { | ||
| 536 | {LMUTE, 0x04, 0x06}, | ||
| 537 | {RMUTE, 0x04, 0x06}, | ||
| 538 | {DRVPOWERCTRL, 0x00, 0x06}, | ||
| 539 | }; | ||
| 540 | int retval = 0; | ||
| 541 | |||
| 542 | snd_pmic_ops_nc.output_dev_id = value; | ||
| 543 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 544 | retval = nc_init_card(); | ||
| 545 | if (retval) | ||
| 546 | return retval; | ||
| 547 | pr_debug("nc set selected output:%d\n", value); | ||
| 548 | mutex_lock(&snd_pmic_ops_nc.lock); | ||
| 549 | switch (value) { | ||
| 550 | case STEREO_HEADPHONE: | ||
| 551 | if (snd_pmic_ops_nc.pb_on) | ||
| 552 | sst_sc_reg_access(sc_access_HP+2, PMIC_WRITE, 1); | ||
| 553 | retval = sst_sc_reg_access(sc_access_HP, PMIC_WRITE, 2); | ||
| 554 | nc_set_amp_power(0); | ||
| 555 | break; | ||
| 556 | case MONO_EARPIECE: | ||
| 557 | case INTERNAL_SPKR: | ||
| 558 | retval = sst_sc_reg_access(sc_access_IS, PMIC_WRITE, 3); | ||
| 559 | if (snd_pmic_ops_nc.pb_on) | ||
| 560 | nc_set_amp_power(1); | ||
| 561 | break; | ||
| 562 | default: | ||
| 563 | pr_err("rcvd illegal request: %d\n", value); | ||
| 564 | mutex_unlock(&snd_pmic_ops_nc.lock); | ||
| 565 | return -EINVAL; | ||
| 566 | } | ||
| 567 | mutex_unlock(&snd_pmic_ops_nc.lock); | ||
| 568 | return retval; | ||
| 569 | } | ||
| 570 | |||
| 571 | static int nc_audio_init(void) | ||
| 572 | { | ||
| 573 | struct sc_reg_access sc_acces, sc_access[] = { | ||
| 574 | {0x100, 0x00, 0}, | ||
| 575 | {0x101, 0x00, 0}, | ||
| 576 | {0x104, 0x8B, 0}, | ||
| 577 | {0x107, 0x11, 0}, | ||
| 578 | {0x10B, 0x0E, 0}, | ||
| 579 | {0x114, 0x00, 0}, | ||
| 580 | {0x115, 0x00, 0}, | ||
| 581 | {0x128, 0x00, 0}, | ||
| 582 | {0x129, 0x00, 0}, | ||
| 583 | {0x12A, 0x00, 0}, | ||
| 584 | {0x12B, 0xee, 0}, | ||
| 585 | {0x12C, 0xf6, 0}, | ||
| 586 | }; | ||
| 587 | |||
| 588 | sst_sc_reg_access(sc_access, PMIC_WRITE, 12); | ||
| 589 | pr_debug("Audio Init successfully!!\n"); | ||
| 590 | |||
| 591 | /*set output device */ | ||
| 592 | nc_set_selected_output_dev(snd_pmic_ops_nc.output_dev_id); | ||
| 593 | |||
| 594 | if (snd_pmic_ops_nc.num_channel == 1) { | ||
| 595 | sc_acces.value = 0x07; | ||
| 596 | sc_acces.reg_addr = RMUTE; | ||
| 597 | pr_debug("RIGHT_HP_MUTE value%d\n", sc_acces.value); | ||
| 598 | sc_acces.mask = MASK2; | ||
| 599 | sst_sc_reg_access(&sc_acces, PMIC_READ_MODIFY, 1); | ||
| 600 | } else { | ||
| 601 | sc_acces.value = 0x00; | ||
| 602 | sc_acces.reg_addr = RMUTE; | ||
| 603 | pr_debug("RIGHT_HP_MUTE value%d\n", sc_acces.value); | ||
| 604 | sc_acces.mask = MASK2; | ||
| 605 | sst_sc_reg_access(&sc_acces, PMIC_READ_MODIFY, 1); | ||
| 606 | } | ||
| 607 | |||
| 608 | return 0; | ||
| 609 | } | ||
| 610 | |||
| 611 | static int nc_set_audio_port(int status) | ||
| 612 | { | ||
| 613 | struct sc_reg_access sc_access[2] = {{0,},}; | ||
| 614 | int retval = 0; | ||
| 615 | |||
| 616 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 617 | retval = nc_init_card(); | ||
| 618 | if (retval) | ||
| 619 | return retval; | ||
| 620 | |||
| 621 | if (status == DEACTIVATE) { | ||
| 622 | /* Deactivate audio port-tristate and power */ | ||
| 623 | sc_access[0].value = 0x00; | ||
| 624 | sc_access[0].mask = MASK4|MASK5; | ||
| 625 | sc_access[0].reg_addr = AUDIOPORT1; | ||
| 626 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 627 | } else if (status == ACTIVATE) { | ||
| 628 | /* activate audio port */ | ||
| 629 | nc_audio_init(); | ||
| 630 | sc_access[0].value = 0x10; | ||
| 631 | sc_access[0].mask = MASK4|MASK5 ; | ||
| 632 | sc_access[0].reg_addr = AUDIOPORT1; | ||
| 633 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 634 | } else | ||
| 635 | return -EINVAL; | ||
| 636 | |||
| 637 | } | ||
| 638 | |||
| 639 | static int nc_set_voice_port(int status) | ||
| 640 | { | ||
| 641 | struct sc_reg_access sc_access[2] = {{0,},}; | ||
| 642 | int retval = 0; | ||
| 643 | |||
| 644 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 645 | retval = nc_init_card(); | ||
| 646 | if (retval) | ||
| 647 | return retval; | ||
| 648 | |||
| 649 | if (status == DEACTIVATE) { | ||
| 650 | /* Activate Voice port */ | ||
| 651 | sc_access[0].value = 0x00; | ||
| 652 | sc_access[0].mask = MASK4; | ||
| 653 | sc_access[0].reg_addr = VOICEPORT1; | ||
| 654 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 655 | } else if (status == ACTIVATE) { | ||
| 656 | /* Deactivate voice port */ | ||
| 657 | nc_set_pcm_voice_params(); | ||
| 658 | sc_access[0].value = 0x10; | ||
| 659 | sc_access[0].mask = MASK4; | ||
| 660 | sc_access[0].reg_addr = VOICEPORT1; | ||
| 661 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 662 | } else | ||
| 663 | return -EINVAL; | ||
| 664 | } | ||
| 665 | |||
| 666 | static int nc_set_mute(int dev_id, u8 value) | ||
| 667 | { | ||
| 668 | struct sc_reg_access sc_access[3]; | ||
| 669 | u8 mute_val, cap_mute; | ||
| 670 | int retval = 0; | ||
| 671 | |||
| 672 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 673 | retval = nc_init_card(); | ||
| 674 | if (retval) | ||
| 675 | return retval; | ||
| 676 | |||
| 677 | pr_debug("set device id::%d, value %d\n", dev_id, value); | ||
| 678 | |||
| 679 | switch (dev_id) { | ||
| 680 | case PMIC_SND_MUTE_ALL: | ||
| 681 | pr_debug("PMIC_SND_MUTE_ALL value %d\n", value); | ||
| 682 | snd_pmic_ops_nc.mute_status = value; | ||
| 683 | snd_pmic_ops_nc.master_mute = value; | ||
| 684 | if (value == UNMUTE) { | ||
| 685 | /* unmute the system, set the 7th bit to zero */ | ||
| 686 | mute_val = cap_mute = 0x00; | ||
| 687 | } else { | ||
| 688 | /* MUTE:Set the seventh bit */ | ||
| 689 | mute_val = 0x80; | ||
| 690 | cap_mute = 0x40; | ||
| 691 | } | ||
| 692 | sc_access[0].reg_addr = AUDIOLVOL; | ||
| 693 | sc_access[1].reg_addr = AUDIORVOL; | ||
| 694 | sc_access[0].mask = sc_access[1].mask = MASK7; | ||
| 695 | sc_access[0].value = sc_access[1].value = mute_val; | ||
| 696 | if (snd_pmic_ops_nc.num_channel == 1) | ||
| 697 | sc_access[1].value = 0x80; | ||
| 698 | if (!sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2)) { | ||
| 699 | sc_access[0].reg_addr = 0x109; | ||
| 700 | sc_access[1].reg_addr = 0x10a; | ||
| 701 | sc_access[2].reg_addr = 0x105; | ||
| 702 | sc_access[0].mask = sc_access[1].mask = | ||
| 703 | sc_access[2].mask = MASK6; | ||
| 704 | sc_access[0].value = sc_access[1].value = | ||
| 705 | sc_access[2].value = cap_mute; | ||
| 706 | |||
| 707 | if ((snd_pmic_ops_nc.input_dev_id == AMIC) || | ||
| 708 | (snd_pmic_ops_nc.input_dev_id == DMIC)) | ||
| 709 | sc_access[1].value = 0x40; | ||
| 710 | if (snd_pmic_ops_nc.input_dev_id == HS_MIC) | ||
| 711 | sc_access[0].value = 0x40; | ||
| 712 | retval = sst_sc_reg_access(sc_access, | ||
| 713 | PMIC_READ_MODIFY, 3); | ||
| 714 | } | ||
| 715 | break; | ||
| 716 | case PMIC_SND_HP_MIC_MUTE: | ||
| 717 | pr_debug("PMIC_SND_HPMIC_MUTE value %d\n", value); | ||
| 718 | if (value == UNMUTE) { | ||
| 719 | /* unmute the system, set the 6th bit to one */ | ||
| 720 | sc_access[0].value = 0x00; | ||
| 721 | } else { | ||
| 722 | /* mute the system, reset the 6th bit to zero */ | ||
| 723 | sc_access[0].value = 0x40; | ||
| 724 | } | ||
| 725 | sc_access[0].reg_addr = LIRSEL; | ||
| 726 | sc_access[0].mask = MASK6; | ||
| 727 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 728 | break; | ||
| 729 | case PMIC_SND_AMIC_MUTE: | ||
| 730 | pr_debug("PMIC_SND_AMIC_MUTE value %d\n", value); | ||
| 731 | if (value == UNMUTE) { | ||
| 732 | /* unmute the system, set the 6th bit to one */ | ||
| 733 | sc_access[0].value = 0x00; | ||
| 734 | } else { | ||
| 735 | /* mute the system, reset the 6th bit to zero */ | ||
| 736 | sc_access[0].value = 0x40; | ||
| 737 | } | ||
| 738 | sc_access[0].reg_addr = LILSEL; | ||
| 739 | sc_access[0].mask = MASK6; | ||
| 740 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 741 | break; | ||
| 742 | |||
| 743 | case PMIC_SND_DMIC_MUTE: | ||
| 744 | pr_debug("INPUT_MUTE_DMIC value%d\n", value); | ||
| 745 | if (value == UNMUTE) { | ||
| 746 | /* unmute the system, set the 6th bit to one */ | ||
| 747 | sc_access[1].value = 0x00; | ||
| 748 | sc_access[0].value = 0x00; | ||
| 749 | } else { | ||
| 750 | /* mute the system, reset the 6th bit to zero */ | ||
| 751 | sc_access[1].value = 0x40; | ||
| 752 | sc_access[0].value = 0x40; | ||
| 753 | } | ||
| 754 | sc_access[0].reg_addr = DMICCTRL1; | ||
| 755 | sc_access[0].mask = MASK6; | ||
| 756 | sc_access[1].reg_addr = LILSEL; | ||
| 757 | sc_access[1].mask = MASK6; | ||
| 758 | retval = sst_sc_reg_access(sc_access, | ||
| 759 | PMIC_READ_MODIFY, 2); | ||
| 760 | break; | ||
| 761 | |||
| 762 | case PMIC_SND_LEFT_HP_MUTE: | ||
| 763 | case PMIC_SND_RIGHT_HP_MUTE: | ||
| 764 | snd_pmic_ops_nc.mute_status = value; | ||
| 765 | if (value == UNMUTE) | ||
| 766 | sc_access[0].value = 0x0; | ||
| 767 | else | ||
| 768 | sc_access[0].value = 0x04; | ||
| 769 | |||
| 770 | if (dev_id == PMIC_SND_LEFT_HP_MUTE) { | ||
| 771 | sc_access[0].reg_addr = LMUTE; | ||
| 772 | pr_debug("LEFT_HP_MUTE value %d\n", | ||
| 773 | sc_access[0].value); | ||
| 774 | } else { | ||
| 775 | if (snd_pmic_ops_nc.num_channel == 1) | ||
| 776 | sc_access[0].value = 0x04; | ||
| 777 | sc_access[0].reg_addr = RMUTE; | ||
| 778 | pr_debug("RIGHT_HP_MUTE value %d\n", | ||
| 779 | sc_access[0].value); | ||
| 780 | } | ||
| 781 | sc_access[0].mask = MASK2; | ||
| 782 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 783 | break; | ||
| 784 | case PMIC_SND_LEFT_SPEAKER_MUTE: | ||
| 785 | case PMIC_SND_RIGHT_SPEAKER_MUTE: | ||
| 786 | if (value == UNMUTE) | ||
| 787 | sc_access[0].value = 0x00; | ||
| 788 | else | ||
| 789 | sc_access[0].value = 0x03; | ||
| 790 | sc_access[0].reg_addr = LMUTE; | ||
| 791 | pr_debug("SPEAKER_MUTE %d\n", sc_access[0].value); | ||
| 792 | sc_access[0].mask = MASK1; | ||
| 793 | retval = sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 1); | ||
| 794 | break; | ||
| 795 | default: | ||
| 796 | return -EINVAL; | ||
| 797 | } | ||
| 798 | return retval ; | ||
| 799 | |||
| 800 | } | ||
| 801 | |||
| 802 | static int nc_set_vol(int dev_id, int value) | ||
| 803 | { | ||
| 804 | struct sc_reg_access sc_access[3]; | ||
| 805 | int retval = 0, entries = 0; | ||
| 806 | |||
| 807 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 808 | retval = nc_init_card(); | ||
| 809 | if (retval) | ||
| 810 | return retval; | ||
| 811 | |||
| 812 | pr_debug("set volume:%d\n", dev_id); | ||
| 813 | switch (dev_id) { | ||
| 814 | case PMIC_SND_CAPTURE_VOL: | ||
| 815 | pr_debug("PMIC_SND_CAPTURE_VOL:value::%d\n", value); | ||
| 816 | sc_access[0].value = sc_access[1].value = | ||
| 817 | sc_access[2].value = -value; | ||
| 818 | sc_access[0].mask = sc_access[1].mask = sc_access[2].mask = | ||
| 819 | (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); | ||
| 820 | sc_access[0].reg_addr = 0x10a; | ||
| 821 | sc_access[1].reg_addr = 0x109; | ||
| 822 | sc_access[2].reg_addr = 0x105; | ||
| 823 | entries = 3; | ||
| 824 | break; | ||
| 825 | |||
| 826 | case PMIC_SND_LEFT_PB_VOL: | ||
| 827 | pr_debug("PMIC_SND_LEFT_HP_VOL %d\n", value); | ||
| 828 | sc_access[0].value = -value; | ||
| 829 | sc_access[0].reg_addr = HPLVOL; | ||
| 830 | sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4); | ||
| 831 | entries = 1; | ||
| 832 | break; | ||
| 833 | |||
| 834 | case PMIC_SND_RIGHT_PB_VOL: | ||
| 835 | pr_debug("PMIC_SND_RIGHT_HP_VOL value %d\n", value); | ||
| 836 | if (snd_pmic_ops_nc.num_channel == 1) { | ||
| 837 | sc_access[0].value = 0x04; | ||
| 838 | sc_access[0].reg_addr = RMUTE; | ||
| 839 | sc_access[0].mask = MASK2; | ||
| 840 | } else { | ||
| 841 | sc_access[0].value = -value; | ||
| 842 | sc_access[0].reg_addr = HPRVOL; | ||
| 843 | sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4); | ||
| 844 | } | ||
| 845 | entries = 1; | ||
| 846 | break; | ||
| 847 | |||
| 848 | case PMIC_SND_LEFT_MASTER_VOL: | ||
| 849 | pr_debug("PMIC_SND_LEFT_MASTER_VOL value %d\n", value); | ||
| 850 | sc_access[0].value = -value; | ||
| 851 | sc_access[0].reg_addr = AUDIOLVOL; | ||
| 852 | sc_access[0].mask = | ||
| 853 | (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); | ||
| 854 | entries = 1; | ||
| 855 | break; | ||
| 856 | |||
| 857 | case PMIC_SND_RIGHT_MASTER_VOL: | ||
| 858 | pr_debug("PMIC_SND_RIGHT_MASTER_VOL value %d\n", value); | ||
| 859 | sc_access[0].value = -value; | ||
| 860 | sc_access[0].reg_addr = AUDIORVOL; | ||
| 861 | sc_access[0].mask = | ||
| 862 | (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); | ||
| 863 | entries = 1; | ||
| 864 | break; | ||
| 865 | |||
| 866 | default: | ||
| 867 | return -EINVAL; | ||
| 868 | |||
| 869 | } | ||
| 870 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, entries); | ||
| 871 | } | ||
| 872 | |||
| 873 | static int nc_set_selected_input_dev(u8 value) | ||
| 874 | { | ||
| 875 | struct sc_reg_access sc_access[6]; | ||
| 876 | u8 num_val; | ||
| 877 | int retval = 0; | ||
| 878 | |||
| 879 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 880 | retval = nc_init_card(); | ||
| 881 | if (retval) | ||
| 882 | return retval; | ||
| 883 | snd_pmic_ops_nc.input_dev_id = value; | ||
| 884 | |||
| 885 | pr_debug("nc set selected input:%d\n", value); | ||
| 886 | |||
| 887 | switch (value) { | ||
| 888 | case AMIC: | ||
| 889 | pr_debug("Selecting AMIC\n"); | ||
| 890 | sc_access[0].reg_addr = 0x107; | ||
| 891 | sc_access[0].value = 0x40; | ||
| 892 | sc_access[0].mask = MASK6|MASK3|MASK1|MASK0; | ||
| 893 | sc_access[1].reg_addr = 0x10a; | ||
| 894 | sc_access[1].value = 0x40; | ||
| 895 | sc_access[1].mask = MASK6; | ||
| 896 | sc_access[2].reg_addr = 0x109; | ||
| 897 | sc_access[2].value = 0x00; | ||
| 898 | sc_access[2].mask = MASK6; | ||
| 899 | sc_access[3].reg_addr = 0x105; | ||
| 900 | sc_access[3].value = 0x40; | ||
| 901 | sc_access[3].mask = MASK6; | ||
| 902 | num_val = 4; | ||
| 903 | break; | ||
| 904 | |||
| 905 | case HS_MIC: | ||
| 906 | pr_debug("Selecting HS_MIC\n"); | ||
| 907 | sc_access[0].reg_addr = MICCTRL; | ||
| 908 | sc_access[0].mask = MASK6|MASK3|MASK1|MASK0; | ||
| 909 | sc_access[0].value = 0x00; | ||
| 910 | sc_access[1].reg_addr = 0x109; | ||
| 911 | sc_access[1].mask = MASK6; | ||
| 912 | sc_access[1].value = 0x40; | ||
| 913 | sc_access[2].reg_addr = 0x10a; | ||
| 914 | sc_access[2].mask = MASK6; | ||
| 915 | sc_access[2].value = 0x00; | ||
| 916 | sc_access[3].reg_addr = 0x105; | ||
| 917 | sc_access[3].value = 0x40; | ||
| 918 | sc_access[3].mask = MASK6; | ||
| 919 | sc_access[4].reg_addr = ADCSAMPLERATE; | ||
| 920 | sc_access[4].mask = MASK7|MASK6|MASK5|MASK4|MASK3; | ||
| 921 | sc_access[4].value = 0xc8; | ||
| 922 | num_val = 5; | ||
| 923 | break; | ||
| 924 | |||
| 925 | case DMIC: | ||
| 926 | pr_debug("DMIC\n"); | ||
| 927 | sc_access[0].reg_addr = MICCTRL; | ||
| 928 | sc_access[0].mask = MASK6|MASK3|MASK1|MASK0; | ||
| 929 | sc_access[0].value = 0x0B; | ||
| 930 | sc_access[1].reg_addr = 0x105; | ||
| 931 | sc_access[1].value = 0x80; | ||
| 932 | sc_access[1].mask = MASK7|MASK6; | ||
| 933 | sc_access[2].reg_addr = 0x10a; | ||
| 934 | sc_access[2].value = 0x40; | ||
| 935 | sc_access[2].mask = MASK6; | ||
| 936 | sc_access[3].reg_addr = LILSEL; | ||
| 937 | sc_access[3].mask = MASK6; | ||
| 938 | sc_access[3].value = 0x00; | ||
| 939 | sc_access[4].reg_addr = ADCSAMPLERATE; | ||
| 940 | sc_access[4].mask = MASK7|MASK6|MASK5|MASK4|MASK3; | ||
| 941 | sc_access[4].value = 0x33; | ||
| 942 | num_val = 5; | ||
| 943 | break; | ||
| 944 | default: | ||
| 945 | return -EINVAL; | ||
| 946 | } | ||
| 947 | return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, num_val); | ||
| 948 | } | ||
| 949 | |||
| 950 | static int nc_get_mute(int dev_id, u8 *value) | ||
| 951 | { | ||
| 952 | int retval = 0, mask = 0; | ||
| 953 | struct sc_reg_access sc_access = {0,}; | ||
| 954 | |||
| 955 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 956 | retval = nc_init_card(); | ||
| 957 | if (retval) | ||
| 958 | return retval; | ||
| 959 | |||
| 960 | pr_debug("get mute::%d\n", dev_id); | ||
| 961 | |||
| 962 | switch (dev_id) { | ||
| 963 | case PMIC_SND_AMIC_MUTE: | ||
| 964 | pr_debug("PMIC_SND_INPUT_MUTE_MIC1\n"); | ||
| 965 | sc_access.reg_addr = LILSEL; | ||
| 966 | mask = MASK6; | ||
| 967 | break; | ||
| 968 | case PMIC_SND_HP_MIC_MUTE: | ||
| 969 | pr_debug("PMIC_SND_INPUT_MUTE_MIC2\n"); | ||
| 970 | sc_access.reg_addr = LIRSEL; | ||
| 971 | mask = MASK6; | ||
| 972 | break; | ||
| 973 | case PMIC_SND_LEFT_HP_MUTE: | ||
| 974 | case PMIC_SND_RIGHT_HP_MUTE: | ||
| 975 | mask = MASK2; | ||
| 976 | pr_debug("PMIC_SN_LEFT/RIGHT_HP_MUTE\n"); | ||
| 977 | if (dev_id == PMIC_SND_RIGHT_HP_MUTE) | ||
| 978 | sc_access.reg_addr = RMUTE; | ||
| 979 | else | ||
| 980 | sc_access.reg_addr = LMUTE; | ||
| 981 | break; | ||
| 982 | |||
| 983 | case PMIC_SND_LEFT_SPEAKER_MUTE: | ||
| 984 | pr_debug("PMIC_MONO_EARPIECE_MUTE\n"); | ||
| 985 | sc_access.reg_addr = RMUTE; | ||
| 986 | mask = MASK1; | ||
| 987 | break; | ||
| 988 | case PMIC_SND_DMIC_MUTE: | ||
| 989 | pr_debug("PMIC_SND_INPUT_MUTE_DMIC\n"); | ||
| 990 | sc_access.reg_addr = 0x105; | ||
| 991 | mask = MASK6; | ||
| 992 | break; | ||
| 993 | default: | ||
| 994 | return -EINVAL; | ||
| 995 | |||
| 996 | } | ||
| 997 | retval = sst_sc_reg_access(&sc_access, PMIC_READ, 1); | ||
| 998 | pr_debug("reg value = %d\n", sc_access.value); | ||
| 999 | if (retval) | ||
| 1000 | return retval; | ||
| 1001 | *value = (sc_access.value) & mask; | ||
| 1002 | pr_debug("masked value = %d\n", *value); | ||
| 1003 | if (*value) | ||
| 1004 | *value = 0; | ||
| 1005 | else | ||
| 1006 | *value = 1; | ||
| 1007 | pr_debug("value returned = 0x%x\n", *value); | ||
| 1008 | return retval; | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | static int nc_get_vol(int dev_id, int *value) | ||
| 1012 | { | ||
| 1013 | int retval = 0, mask = 0; | ||
| 1014 | struct sc_reg_access sc_access = {0,}; | ||
| 1015 | |||
| 1016 | if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) | ||
| 1017 | retval = nc_init_card(); | ||
| 1018 | if (retval) | ||
| 1019 | return retval; | ||
| 1020 | |||
| 1021 | switch (dev_id) { | ||
| 1022 | case PMIC_SND_CAPTURE_VOL: | ||
| 1023 | pr_debug("PMIC_SND_INPUT_CAPTURE_VOL\n"); | ||
| 1024 | sc_access.reg_addr = LILSEL; | ||
| 1025 | mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); | ||
| 1026 | break; | ||
| 1027 | |||
| 1028 | case PMIC_SND_LEFT_MASTER_VOL: | ||
| 1029 | pr_debug("GET_VOLUME_PMIC_LEFT_MASTER_VOL\n"); | ||
| 1030 | sc_access.reg_addr = AUDIOLVOL; | ||
| 1031 | mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); | ||
| 1032 | break; | ||
| 1033 | |||
| 1034 | case PMIC_SND_RIGHT_MASTER_VOL: | ||
| 1035 | pr_debug("GET_VOLUME_PMIC_RIGHT_MASTER_VOL\n"); | ||
| 1036 | sc_access.reg_addr = AUDIORVOL; | ||
| 1037 | mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); | ||
| 1038 | break; | ||
| 1039 | |||
| 1040 | case PMIC_SND_RIGHT_PB_VOL: | ||
| 1041 | pr_debug("GET_VOLUME_PMIC_RIGHT_HP_VOL\n"); | ||
| 1042 | sc_access.reg_addr = HPRVOL; | ||
| 1043 | mask = (MASK0|MASK1|MASK2|MASK3|MASK4); | ||
| 1044 | break; | ||
| 1045 | |||
| 1046 | case PMIC_SND_LEFT_PB_VOL: | ||
| 1047 | pr_debug("GET_VOLUME_PMIC_LEFT_HP_VOL\n"); | ||
| 1048 | sc_access.reg_addr = HPLVOL; | ||
| 1049 | mask = (MASK0|MASK1|MASK2|MASK3|MASK4); | ||
| 1050 | break; | ||
| 1051 | |||
| 1052 | default: | ||
| 1053 | return -EINVAL; | ||
| 1054 | |||
| 1055 | } | ||
| 1056 | retval = sst_sc_reg_access(&sc_access, PMIC_READ, 1); | ||
| 1057 | pr_debug("value read = 0x%x\n", sc_access.value); | ||
| 1058 | *value = -((sc_access.value) & mask); | ||
| 1059 | pr_debug("get vol value returned = %d\n", *value); | ||
| 1060 | return retval; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | static void hp_automute(enum snd_jack_types type, int present) | ||
| 1064 | { | ||
| 1065 | u8 in = DMIC; | ||
| 1066 | u8 out = INTERNAL_SPKR; | ||
| 1067 | if (present) { | ||
| 1068 | if (type == SND_JACK_HEADSET) | ||
| 1069 | in = HS_MIC; | ||
| 1070 | out = STEREO_HEADPHONE; | ||
| 1071 | } | ||
| 1072 | nc_set_selected_input_dev(in); | ||
| 1073 | nc_set_selected_output_dev(out); | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | static void nc_pmic_irq_cb(void *cb_data, u8 intsts) | ||
| 1077 | { | ||
| 1078 | u8 value = 0; | ||
| 1079 | struct mad_jack *mjack = NULL; | ||
| 1080 | unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; | ||
| 1081 | struct snd_intelmad *intelmaddata = cb_data; | ||
| 1082 | struct sc_reg_access sc_access_read = {0,}; | ||
| 1083 | |||
| 1084 | sc_access_read.reg_addr = 0x132; | ||
| 1085 | sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); | ||
| 1086 | value = (sc_access_read.value); | ||
| 1087 | pr_debug("value returned = 0x%x\n", value); | ||
| 1088 | |||
| 1089 | mjack = &intelmaddata->jack[0]; | ||
| 1090 | if (intsts & 0x1) { | ||
| 1091 | pr_debug("SST DBG:MAD headset detected\n"); | ||
| 1092 | /* send headset detect/undetect */ | ||
| 1093 | present = (value == 0x1) ? 3 : 0; | ||
| 1094 | jack_event_flag = 1; | ||
| 1095 | mjack->jack.type = SND_JACK_HEADSET; | ||
| 1096 | hp_automute(SND_JACK_HEADSET, present); | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | if (intsts & 0x2) { | ||
| 1100 | pr_debug(":MAD headphone detected\n"); | ||
| 1101 | /* send headphone detect/undetect */ | ||
| 1102 | present = (value == 0x2) ? 1 : 0; | ||
| 1103 | jack_event_flag = 1; | ||
| 1104 | mjack->jack.type = SND_JACK_HEADPHONE; | ||
| 1105 | hp_automute(SND_JACK_HEADPHONE, present); | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | if (intsts & 0x4) { | ||
| 1109 | pr_debug("MAD short push detected\n"); | ||
| 1110 | /* send short push */ | ||
| 1111 | present = 1; | ||
| 1112 | jack_event_flag = 1; | ||
| 1113 | buttonpressflag = 1; | ||
| 1114 | mjack->jack.type = MID_JACK_HS_SHORT_PRESS; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | if (intsts & 0x8) { | ||
| 1118 | pr_debug(":MAD long push detected\n"); | ||
| 1119 | /* send long push */ | ||
| 1120 | present = 1; | ||
| 1121 | jack_event_flag = 1; | ||
| 1122 | buttonpressflag = 1; | ||
| 1123 | mjack->jack.type = MID_JACK_HS_LONG_PRESS; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | if (jack_event_flag) | ||
| 1127 | sst_mad_send_jack_report(&mjack->jack, | ||
| 1128 | buttonpressflag, present); | ||
| 1129 | } | ||
| 1130 | static int nc_jack_enable(void) | ||
| 1131 | { | ||
| 1132 | return 0; | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | struct snd_pmic_ops snd_pmic_ops_nc = { | ||
| 1136 | .input_dev_id = DMIC, | ||
| 1137 | .output_dev_id = INTERNAL_SPKR, | ||
| 1138 | .set_input_dev = nc_set_selected_input_dev, | ||
| 1139 | .set_output_dev = nc_set_selected_output_dev, | ||
| 1140 | .set_mute = nc_set_mute, | ||
| 1141 | .get_mute = nc_get_mute, | ||
| 1142 | .set_vol = nc_set_vol, | ||
| 1143 | .get_vol = nc_get_vol, | ||
| 1144 | .init_card = nc_init_card, | ||
| 1145 | .set_pcm_audio_params = nc_set_pcm_audio_params, | ||
| 1146 | .set_pcm_voice_params = nc_set_pcm_voice_params, | ||
| 1147 | .set_voice_port = nc_set_voice_port, | ||
| 1148 | .set_audio_port = nc_set_audio_port, | ||
| 1149 | .power_up_pmic_pb = nc_power_up_pb, | ||
| 1150 | .power_up_pmic_cp = nc_power_up_cp, | ||
| 1151 | .power_down_pmic_pb = nc_power_down_pb, | ||
| 1152 | .power_down_pmic_cp = nc_power_down_cp, | ||
| 1153 | .power_down_pmic = nc_power_down, | ||
| 1154 | .pmic_irq_cb = nc_pmic_irq_cb, | ||
| 1155 | .pmic_jack_enable = nc_jack_enable, | ||
| 1156 | }; | ||
