Commit | Line | Data |
---|---|---|
fffa1cca VK |
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 | ||
d0f40c50 JP |
32 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
33 | ||
fffa1cca VK |
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> | |
964c6975 | 39 | #include <linux/pm_runtime.h> |
fffa1cca VK |
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 | /* Interrupt arrived, check src */ | |
111 | isr.full = sst_shim_read(drv->shim, SST_ISRX); | |
112 | ||
113 | if (isr.part.busy_interrupt) { | |
114 | header.full = sst_shim_read(drv->shim, SST_IPCD); | |
115 | if (header.part.msg_id == IPC_SST_PERIOD_ELAPSED) { | |
116 | sst_clear_interrupt(); | |
117 | str_id = header.part.str_id; | |
118 | stream = &sst_drv_ctx->streams[str_id]; | |
119 | if (stream->period_elapsed) | |
120 | stream->period_elapsed(stream->pcm_substream); | |
121 | return IRQ_HANDLED; | |
122 | } | |
123 | if (header.part.large) | |
124 | size = header.part.data; | |
125 | if (header.part.msg_id & REPLY_MSG) { | |
126 | sst_drv_ctx->ipc_process_msg.header = header; | |
127 | memcpy_fromio(sst_drv_ctx->ipc_process_msg.mailbox, | |
128 | drv->mailbox + SST_MAILBOX_RCV, size); | |
129 | queue_work(sst_drv_ctx->process_msg_wq, | |
130 | &sst_drv_ctx->ipc_process_msg.wq); | |
131 | } else { | |
132 | sst_drv_ctx->ipc_process_reply.header = header; | |
133 | memcpy_fromio(sst_drv_ctx->ipc_process_reply.mailbox, | |
134 | drv->mailbox + SST_MAILBOX_RCV, size); | |
135 | queue_work(sst_drv_ctx->process_reply_wq, | |
136 | &sst_drv_ctx->ipc_process_reply.wq); | |
137 | } | |
138 | /* mask busy inetrrupt */ | |
139 | imr.full = sst_shim_read(drv->shim, SST_IMRX); | |
140 | imr.part.busy_interrupt = 1; | |
141 | sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); | |
142 | return IRQ_HANDLED; | |
143 | } else if (isr.part.done_interrupt) { | |
144 | /* Clear done bit */ | |
145 | header.full = sst_shim_read(drv->shim, SST_IPCX); | |
146 | header.part.done = 0; | |
147 | sst_shim_write(sst_drv_ctx->shim, SST_IPCX, header.full); | |
148 | /* write 1 to clear status register */; | |
149 | isr.part.done_interrupt = 1; | |
150 | /* dummy register for shim workaround */ | |
151 | sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full); | |
152 | queue_work(sst_drv_ctx->post_msg_wq, | |
153 | &sst_drv_ctx->ipc_post_msg.wq); | |
154 | return IRQ_HANDLED; | |
155 | } else | |
156 | return IRQ_NONE; | |
157 | ||
158 | } | |
159 | ||
160 | ||
161 | /* | |
162 | * intel_sst_probe - PCI probe function | |
163 | * | |
164 | * @pci: PCI device structure | |
165 | * @pci_id: PCI device ID structure | |
166 | * | |
167 | * This function is called by OS when a device is found | |
168 | * This enables the device, interrupt etc | |
169 | */ | |
170 | static int __devinit intel_sst_probe(struct pci_dev *pci, | |
171 | const struct pci_device_id *pci_id) | |
172 | { | |
173 | int i, ret = 0; | |
174 | ||
d0f40c50 | 175 | pr_debug("Probe for DID %x\n", pci->device); |
fffa1cca VK |
176 | mutex_lock(&drv_ctx_lock); |
177 | if (sst_drv_ctx) { | |
d0f40c50 | 178 | pr_err("Only one sst handle is supported\n"); |
fffa1cca VK |
179 | mutex_unlock(&drv_ctx_lock); |
180 | return -EBUSY; | |
181 | } | |
182 | ||
183 | sst_drv_ctx = kzalloc(sizeof(*sst_drv_ctx), GFP_KERNEL); | |
184 | if (!sst_drv_ctx) { | |
d0f40c50 | 185 | pr_err("malloc fail\n"); |
fffa1cca VK |
186 | mutex_unlock(&drv_ctx_lock); |
187 | return -ENOMEM; | |
188 | } | |
189 | mutex_unlock(&drv_ctx_lock); | |
190 | ||
191 | sst_drv_ctx->pci_id = pci->device; | |
192 | ||
193 | mutex_init(&sst_drv_ctx->stream_lock); | |
194 | mutex_init(&sst_drv_ctx->sst_lock); | |
195 | sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; | |
196 | ||
197 | sst_drv_ctx->stream_cnt = 0; | |
198 | sst_drv_ctx->encoded_cnt = 0; | |
199 | sst_drv_ctx->am_cnt = 0; | |
200 | sst_drv_ctx->pb_streams = 0; | |
201 | sst_drv_ctx->cp_streams = 0; | |
202 | sst_drv_ctx->unique_id = 0; | |
203 | sst_drv_ctx->pmic_port_instance = SST_DEFAULT_PMIC_PORT; | |
204 | ||
205 | INIT_LIST_HEAD(&sst_drv_ctx->ipc_dispatch_list); | |
206 | INIT_WORK(&sst_drv_ctx->ipc_post_msg.wq, sst_post_message); | |
207 | INIT_WORK(&sst_drv_ctx->ipc_process_msg.wq, sst_process_message); | |
208 | INIT_WORK(&sst_drv_ctx->ipc_process_reply.wq, sst_process_reply); | |
209 | INIT_WORK(&sst_drv_ctx->mad_ops.wq, sst_process_mad_ops); | |
210 | init_waitqueue_head(&sst_drv_ctx->wait_queue); | |
211 | ||
212 | sst_drv_ctx->mad_wq = create_workqueue("sst_mad_wq"); | |
213 | if (!sst_drv_ctx->mad_wq) | |
214 | goto do_free_drv_ctx; | |
215 | sst_drv_ctx->post_msg_wq = create_workqueue("sst_post_msg_wq"); | |
216 | if (!sst_drv_ctx->post_msg_wq) | |
217 | goto free_mad_wq; | |
218 | sst_drv_ctx->process_msg_wq = create_workqueue("sst_process_msg_wqq"); | |
219 | if (!sst_drv_ctx->process_msg_wq) | |
220 | goto free_post_msg_wq; | |
221 | sst_drv_ctx->process_reply_wq = create_workqueue("sst_proces_reply_wq"); | |
222 | if (!sst_drv_ctx->process_reply_wq) | |
223 | goto free_process_msg_wq; | |
224 | ||
225 | for (i = 0; i < MAX_ACTIVE_STREAM; i++) { | |
226 | sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; | |
227 | sst_drv_ctx->alloc_block[i].ops_block.condition = false; | |
228 | } | |
229 | spin_lock_init(&sst_drv_ctx->list_spin_lock); | |
230 | ||
231 | sst_drv_ctx->max_streams = pci_id->driver_data; | |
d0f40c50 | 232 | pr_debug("Got drv data max stream %d\n", |
fffa1cca VK |
233 | sst_drv_ctx->max_streams); |
234 | for (i = 1; i <= sst_drv_ctx->max_streams; i++) { | |
235 | struct stream_info *stream = &sst_drv_ctx->streams[i]; | |
236 | INIT_LIST_HEAD(&stream->bufs); | |
237 | mutex_init(&stream->lock); | |
238 | spin_lock_init(&stream->pcm_lock); | |
239 | } | |
240 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { | |
241 | sst_drv_ctx->mmap_mem = NULL; | |
242 | sst_drv_ctx->mmap_len = SST_MMAP_PAGES * PAGE_SIZE; | |
243 | while (sst_drv_ctx->mmap_len > 0) { | |
244 | sst_drv_ctx->mmap_mem = | |
245 | kzalloc(sst_drv_ctx->mmap_len, GFP_KERNEL); | |
246 | if (sst_drv_ctx->mmap_mem) { | |
d0f40c50 | 247 | pr_debug("Got memory %p size 0x%x\n", |
fffa1cca VK |
248 | sst_drv_ctx->mmap_mem, |
249 | sst_drv_ctx->mmap_len); | |
250 | break; | |
251 | } | |
252 | if (sst_drv_ctx->mmap_len < (SST_MMAP_STEP*PAGE_SIZE)) { | |
d0f40c50 | 253 | pr_err("mem alloc fail...abort!!\n"); |
fffa1cca VK |
254 | ret = -ENOMEM; |
255 | goto free_process_reply_wq; | |
256 | } | |
257 | sst_drv_ctx->mmap_len -= (SST_MMAP_STEP * PAGE_SIZE); | |
d0f40c50 | 258 | pr_debug("mem alloc failed...trying %d\n", |
fffa1cca VK |
259 | sst_drv_ctx->mmap_len); |
260 | } | |
261 | } | |
262 | ||
263 | /* Init the device */ | |
264 | ret = pci_enable_device(pci); | |
265 | if (ret) { | |
25985edc | 266 | pr_err("device can't be enabled\n"); |
fffa1cca VK |
267 | goto do_free_mem; |
268 | } | |
269 | sst_drv_ctx->pci = pci_dev_get(pci); | |
270 | ret = pci_request_regions(pci, SST_DRV_NAME); | |
271 | if (ret) | |
272 | goto do_disable_device; | |
273 | /* map registers */ | |
274 | /* SST Shim */ | |
275 | sst_drv_ctx->shim_phy_add = pci_resource_start(pci, 1); | |
276 | sst_drv_ctx->shim = pci_ioremap_bar(pci, 1); | |
277 | if (!sst_drv_ctx->shim) | |
278 | goto do_release_regions; | |
d0f40c50 | 279 | pr_debug("SST Shim Ptr %p\n", sst_drv_ctx->shim); |
fffa1cca VK |
280 | |
281 | /* Shared SRAM */ | |
282 | sst_drv_ctx->mailbox = pci_ioremap_bar(pci, 2); | |
283 | if (!sst_drv_ctx->mailbox) | |
284 | goto do_unmap_shim; | |
d0f40c50 | 285 | pr_debug("SRAM Ptr %p\n", sst_drv_ctx->mailbox); |
fffa1cca VK |
286 | |
287 | /* IRAM */ | |
288 | sst_drv_ctx->iram = pci_ioremap_bar(pci, 3); | |
289 | if (!sst_drv_ctx->iram) | |
290 | goto do_unmap_sram; | |
d0f40c50 | 291 | pr_debug("IRAM Ptr %p\n", sst_drv_ctx->iram); |
fffa1cca VK |
292 | |
293 | /* DRAM */ | |
294 | sst_drv_ctx->dram = pci_ioremap_bar(pci, 4); | |
295 | if (!sst_drv_ctx->dram) | |
296 | goto do_unmap_iram; | |
d0f40c50 | 297 | pr_debug("DRAM Ptr %p\n", sst_drv_ctx->dram); |
fffa1cca VK |
298 | |
299 | mutex_lock(&sst_drv_ctx->sst_lock); | |
300 | sst_drv_ctx->sst_state = SST_UN_INIT; | |
301 | mutex_unlock(&sst_drv_ctx->sst_lock); | |
302 | /* Register the ISR */ | |
303 | ret = request_irq(pci->irq, intel_sst_interrupt, | |
304 | IRQF_SHARED, SST_DRV_NAME, sst_drv_ctx); | |
305 | if (ret) | |
306 | goto do_unmap_dram; | |
d0f40c50 | 307 | pr_debug("Registered IRQ 0x%x\n", pci->irq); |
fffa1cca | 308 | |
62877913 VK |
309 | /*Register LPE Control as misc driver*/ |
310 | ret = misc_register(&lpe_ctrl); | |
311 | if (ret) { | |
312 | pr_err("couldn't register control device\n"); | |
313 | goto do_free_irq; | |
314 | } | |
315 | ||
fffa1cca VK |
316 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { |
317 | ret = misc_register(&lpe_dev); | |
318 | if (ret) { | |
62877913 VK |
319 | pr_err("couldn't register misc driver\n"); |
320 | goto do_free_misc; | |
321 | } | |
fffa1cca VK |
322 | } |
323 | sst_drv_ctx->lpe_stalled = 0; | |
964c6975 VK |
324 | pm_runtime_set_active(&pci->dev); |
325 | pm_runtime_enable(&pci->dev); | |
326 | pm_runtime_allow(&pci->dev); | |
d0f40c50 | 327 | pr_debug("...successfully done!!!\n"); |
fffa1cca VK |
328 | return ret; |
329 | ||
62877913 VK |
330 | do_free_misc: |
331 | misc_deregister(&lpe_ctrl); | |
fffa1cca VK |
332 | do_free_irq: |
333 | free_irq(pci->irq, sst_drv_ctx); | |
334 | do_unmap_dram: | |
335 | iounmap(sst_drv_ctx->dram); | |
336 | do_unmap_iram: | |
337 | iounmap(sst_drv_ctx->iram); | |
338 | do_unmap_sram: | |
339 | iounmap(sst_drv_ctx->mailbox); | |
340 | do_unmap_shim: | |
341 | iounmap(sst_drv_ctx->shim); | |
342 | do_release_regions: | |
343 | pci_release_regions(pci); | |
344 | do_disable_device: | |
345 | pci_disable_device(pci); | |
346 | do_free_mem: | |
347 | kfree(sst_drv_ctx->mmap_mem); | |
348 | free_process_reply_wq: | |
349 | destroy_workqueue(sst_drv_ctx->process_reply_wq); | |
350 | free_process_msg_wq: | |
351 | destroy_workqueue(sst_drv_ctx->process_msg_wq); | |
352 | free_post_msg_wq: | |
353 | destroy_workqueue(sst_drv_ctx->post_msg_wq); | |
354 | free_mad_wq: | |
355 | destroy_workqueue(sst_drv_ctx->mad_wq); | |
356 | do_free_drv_ctx: | |
357 | kfree(sst_drv_ctx); | |
d0f40c50 | 358 | pr_err("Probe failed with 0x%x\n", ret); |
fffa1cca VK |
359 | return ret; |
360 | } | |
361 | ||
362 | /** | |
363 | * intel_sst_remove - PCI remove function | |
364 | * | |
365 | * @pci: PCI device structure | |
366 | * | |
367 | * This function is called by OS when a device is unloaded | |
368 | * This frees the interrupt etc | |
369 | */ | |
370 | static void __devexit intel_sst_remove(struct pci_dev *pci) | |
371 | { | |
372 | pci_dev_put(sst_drv_ctx->pci); | |
373 | mutex_lock(&sst_drv_ctx->sst_lock); | |
374 | sst_drv_ctx->sst_state = SST_UN_INIT; | |
375 | mutex_unlock(&sst_drv_ctx->sst_lock); | |
62877913 VK |
376 | misc_deregister(&lpe_ctrl); |
377 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) | |
fffa1cca | 378 | misc_deregister(&lpe_dev); |
fffa1cca VK |
379 | free_irq(pci->irq, sst_drv_ctx); |
380 | iounmap(sst_drv_ctx->dram); | |
381 | iounmap(sst_drv_ctx->iram); | |
382 | iounmap(sst_drv_ctx->mailbox); | |
383 | iounmap(sst_drv_ctx->shim); | |
384 | sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; | |
385 | if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) | |
386 | kfree(sst_drv_ctx->mmap_mem); | |
387 | flush_scheduled_work(); | |
388 | destroy_workqueue(sst_drv_ctx->process_reply_wq); | |
389 | destroy_workqueue(sst_drv_ctx->process_msg_wq); | |
390 | destroy_workqueue(sst_drv_ctx->post_msg_wq); | |
391 | destroy_workqueue(sst_drv_ctx->mad_wq); | |
392 | kfree(sst_drv_ctx); | |
393 | pci_release_region(pci, 1); | |
394 | pci_release_region(pci, 2); | |
395 | pci_release_region(pci, 3); | |
396 | pci_release_region(pci, 4); | |
397 | pci_release_region(pci, 5); | |
398 | pci_set_drvdata(pci, NULL); | |
399 | } | |
400 | ||
401 | /* Power Management */ | |
402 | /* | |
403 | * intel_sst_suspend - PCI suspend function | |
404 | * | |
405 | * @pci: PCI device structure | |
406 | * @state: PM message | |
407 | * | |
408 | * This function is called by OS when a power event occurs | |
409 | */ | |
410 | int intel_sst_suspend(struct pci_dev *pci, pm_message_t state) | |
411 | { | |
412 | union config_status_reg csr; | |
413 | ||
d0f40c50 | 414 | pr_debug("intel_sst_suspend called\n"); |
fffa1cca | 415 | |
964c6975 VK |
416 | if (sst_drv_ctx->stream_cnt) { |
417 | pr_err("active streams,not able to suspend\n"); | |
418 | return -EBUSY; | |
419 | } | |
fffa1cca VK |
420 | /*Assert RESET on LPE Processor*/ |
421 | csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); | |
422 | csr.full = csr.full | 0x2; | |
423 | /* Move the SST state to Suspended */ | |
424 | mutex_lock(&sst_drv_ctx->sst_lock); | |
425 | sst_drv_ctx->sst_state = SST_SUSPENDED; | |
426 | sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); | |
427 | mutex_unlock(&sst_drv_ctx->sst_lock); | |
428 | pci_set_drvdata(pci, sst_drv_ctx); | |
429 | pci_save_state(pci); | |
430 | pci_disable_device(pci); | |
431 | pci_set_power_state(pci, PCI_D3hot); | |
432 | return 0; | |
433 | } | |
434 | ||
435 | /** | |
436 | * intel_sst_resume - PCI resume function | |
437 | * | |
438 | * @pci: PCI device structure | |
439 | * | |
440 | * This function is called by OS when a power event occurs | |
441 | */ | |
442 | int intel_sst_resume(struct pci_dev *pci) | |
443 | { | |
444 | int ret = 0; | |
445 | ||
d0f40c50 | 446 | pr_debug("intel_sst_resume called\n"); |
fffa1cca | 447 | if (sst_drv_ctx->sst_state != SST_SUSPENDED) { |
d0f40c50 | 448 | pr_err("SST is not in suspended state\n"); |
964c6975 | 449 | return 0; |
fffa1cca VK |
450 | } |
451 | sst_drv_ctx = pci_get_drvdata(pci); | |
452 | pci_set_power_state(pci, PCI_D0); | |
453 | pci_restore_state(pci); | |
454 | ret = pci_enable_device(pci); | |
455 | if (ret) | |
25985edc | 456 | pr_err("device can't be enabled\n"); |
fffa1cca VK |
457 | |
458 | mutex_lock(&sst_drv_ctx->sst_lock); | |
459 | sst_drv_ctx->sst_state = SST_UN_INIT; | |
460 | mutex_unlock(&sst_drv_ctx->sst_lock); | |
461 | return 0; | |
462 | } | |
463 | ||
964c6975 VK |
464 | static int intel_sst_runtime_suspend(struct device *dev) |
465 | { | |
466 | struct pci_dev *pci_dev = to_pci_dev(dev); | |
467 | pr_debug("runtime_suspend called\n"); | |
468 | return intel_sst_suspend(pci_dev, PMSG_SUSPEND); | |
469 | } | |
470 | ||
471 | static int intel_sst_runtime_resume(struct device *dev) | |
472 | { | |
473 | struct pci_dev *pci_dev = to_pci_dev(dev); | |
474 | pr_debug("runtime_resume called\n"); | |
475 | return intel_sst_resume(pci_dev); | |
476 | } | |
477 | ||
478 | static int intel_sst_runtime_idle(struct device *dev) | |
479 | { | |
480 | pr_debug("runtime_idle called\n"); | |
481 | if (sst_drv_ctx->stream_cnt == 0 && sst_drv_ctx->am_cnt == 0) | |
482 | pm_schedule_suspend(dev, SST_SUSPEND_DELAY); | |
483 | return -EBUSY; | |
484 | } | |
485 | ||
486 | static const struct dev_pm_ops intel_sst_pm = { | |
487 | .runtime_suspend = intel_sst_runtime_suspend, | |
488 | .runtime_resume = intel_sst_runtime_resume, | |
489 | .runtime_idle = intel_sst_runtime_idle, | |
490 | }; | |
491 | ||
fffa1cca VK |
492 | /* PCI Routines */ |
493 | static struct pci_device_id intel_sst_ids[] = { | |
494 | { PCI_VDEVICE(INTEL, SST_MRST_PCI_ID), 3}, | |
495 | { PCI_VDEVICE(INTEL, SST_MFLD_PCI_ID), 6}, | |
496 | { 0, } | |
497 | }; | |
498 | MODULE_DEVICE_TABLE(pci, intel_sst_ids); | |
499 | ||
500 | static struct pci_driver driver = { | |
501 | .name = SST_DRV_NAME, | |
502 | .id_table = intel_sst_ids, | |
503 | .probe = intel_sst_probe, | |
504 | .remove = __devexit_p(intel_sst_remove), | |
505 | #ifdef CONFIG_PM | |
506 | .suspend = intel_sst_suspend, | |
507 | .resume = intel_sst_resume, | |
964c6975 VK |
508 | .driver = { |
509 | .pm = &intel_sst_pm, | |
510 | }, | |
fffa1cca VK |
511 | #endif |
512 | }; | |
513 | ||
514 | /** | |
515 | * intel_sst_init - Module init function | |
516 | * | |
517 | * Registers with PCI | |
518 | * Registers with /dev | |
519 | * Init all data strutures | |
520 | */ | |
521 | static int __init intel_sst_init(void) | |
522 | { | |
523 | /* Init all variables, data structure etc....*/ | |
524 | int ret = 0; | |
d0f40c50 | 525 | pr_debug("INFO: ******** SST DRIVER loading.. Ver: %s\n", |
fffa1cca VK |
526 | SST_DRIVER_VERSION); |
527 | ||
528 | mutex_init(&drv_ctx_lock); | |
529 | /* Register with PCI */ | |
530 | ret = pci_register_driver(&driver); | |
531 | if (ret) | |
d0f40c50 | 532 | pr_err("PCI register failed\n"); |
fffa1cca VK |
533 | return ret; |
534 | } | |
535 | ||
536 | /** | |
537 | * intel_sst_exit - Module exit function | |
538 | * | |
539 | * Unregisters with PCI | |
540 | * Unregisters with /dev | |
541 | * Frees all data strutures | |
542 | */ | |
543 | static void __exit intel_sst_exit(void) | |
544 | { | |
545 | pci_unregister_driver(&driver); | |
546 | ||
d0f40c50 | 547 | pr_debug("driver unloaded\n"); |
fffa1cca VK |
548 | return; |
549 | } | |
550 | ||
551 | module_init(intel_sst_init); | |
552 | module_exit(intel_sst_exit); |