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