]>
Commit | Line | Data |
---|---|---|
fffa1cca VK |
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 | */ | |
d0f40c50 JP |
27 | |
28 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
29 | ||
fffa1cca VK |
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 <sound/control.h> | |
36 | #include <asm/mrst.h> | |
37 | #include <sound/pcm.h> | |
38 | #include "jack.h" | |
39 | #include <sound/pcm_params.h> | |
40 | #include <sound/initval.h> | |
41 | #include "intel_sst.h" | |
42 | #include "intel_sst_ioctl.h" | |
43 | #include "intelmid_snd_control.h" | |
44 | #include "intelmid.h" | |
45 | ||
46 | MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); | |
47 | MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); | |
48 | MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>"); | |
49 | MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>"); | |
50 | MODULE_DESCRIPTION("Intel MAD Sound card driver"); | |
51 | MODULE_LICENSE("GPL v2"); | |
52 | MODULE_SUPPORTED_DEVICE("{Intel,Intel_MAD}"); | |
53 | ||
54 | ||
55 | static int card_index = SNDRV_DEFAULT_IDX1;/* Index 0-MAX */ | |
56 | static char *card_id = SNDRV_DEFAULT_STR1; /* ID for this card */ | |
57 | ||
58 | module_param(card_index, int, 0444); | |
59 | MODULE_PARM_DESC(card_index, "Index value for INTELMAD soundcard."); | |
60 | module_param(card_id, charp, 0444); | |
61 | MODULE_PARM_DESC(card_id, "ID string for INTELMAD soundcard."); | |
62 | ||
63 | int sst_card_vendor_id; | |
64 | int intelmid_audio_interrupt_enable;/*checkpatch fix*/ | |
65 | ||
66 | /* Data path functionalities */ | |
67 | static struct snd_pcm_hardware snd_intelmad_stream = { | |
68 | .info = (SNDRV_PCM_INFO_INTERLEAVED | | |
69 | SNDRV_PCM_INFO_DOUBLE | | |
70 | SNDRV_PCM_INFO_PAUSE | | |
71 | SNDRV_PCM_INFO_RESUME | | |
72 | SNDRV_PCM_INFO_MMAP| | |
73 | SNDRV_PCM_INFO_MMAP_VALID | | |
74 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
75 | SNDRV_PCM_INFO_SYNC_START), | |
76 | .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | | |
77 | SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | | |
78 | SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), | |
79 | .rates = (SNDRV_PCM_RATE_8000| | |
80 | SNDRV_PCM_RATE_44100 | | |
81 | SNDRV_PCM_RATE_48000), | |
82 | .rate_min = MIN_RATE, | |
83 | ||
84 | .rate_max = MAX_RATE, | |
85 | .channels_min = MIN_CHANNEL, | |
86 | .channels_max = MAX_CHANNEL_AMIC, | |
87 | .buffer_bytes_max = MAX_BUFFER, | |
88 | .period_bytes_min = MIN_PERIOD_BYTES, | |
89 | .period_bytes_max = MAX_PERIOD_BYTES, | |
90 | .periods_min = MIN_PERIODS, | |
91 | .periods_max = MAX_PERIODS, | |
92 | .fifo_size = FIFO_SIZE, | |
93 | }; | |
94 | ||
95 | ||
96 | /** | |
97 | * snd_intelmad_pcm_trigger - stream activities are handled here | |
98 | * | |
99 | * @substream:substream for which the stream function is called | |
100 | * @cmd:the stream commamd that requested from upper layer | |
101 | * | |
102 | * This function is called whenever an a stream activity is invoked | |
103 | */ | |
104 | static int snd_intelmad_pcm_trigger(struct snd_pcm_substream *substream, | |
105 | int cmd) | |
106 | { | |
6f6ffec1 | 107 | int ret_val = 0, str_id; |
fffa1cca VK |
108 | struct snd_intelmad *intelmaddata; |
109 | struct mad_stream_pvt *stream; | |
6f6ffec1 | 110 | struct intel_sst_pcm_control *sst_ops; |
fffa1cca VK |
111 | |
112 | WARN_ON(!substream); | |
113 | ||
114 | intelmaddata = snd_pcm_substream_chip(substream); | |
115 | stream = substream->runtime->private_data; | |
116 | ||
117 | WARN_ON(!intelmaddata->sstdrv_ops); | |
118 | WARN_ON(!intelmaddata->sstdrv_ops->scard_ops); | |
6f6ffec1 VK |
119 | sst_ops = intelmaddata->sstdrv_ops->pcm_control; |
120 | str_id = stream->stream_info.str_id; | |
fffa1cca VK |
121 | |
122 | switch (cmd) { | |
123 | case SNDRV_PCM_TRIGGER_START: | |
d0f40c50 | 124 | pr_debug("Trigger Start\n"); |
6f6ffec1 | 125 | ret_val = sst_ops->device_control(SST_SND_START, &str_id); |
fffa1cca VK |
126 | if (ret_val) |
127 | return ret_val; | |
128 | stream->stream_status = RUNNING; | |
129 | stream->substream = substream; | |
fffa1cca VK |
130 | break; |
131 | case SNDRV_PCM_TRIGGER_STOP: | |
d0f40c50 | 132 | pr_debug("in stop\n"); |
6f6ffec1 | 133 | ret_val = sst_ops->device_control(SST_SND_DROP, &str_id); |
fffa1cca VK |
134 | if (ret_val) |
135 | return ret_val; | |
136 | stream->stream_status = DROPPED; | |
137 | break; | |
138 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
d0f40c50 | 139 | pr_debug("in pause\n"); |
6f6ffec1 | 140 | ret_val = sst_ops->device_control(SST_SND_PAUSE, &str_id); |
fffa1cca VK |
141 | if (ret_val) |
142 | return ret_val; | |
143 | stream->stream_status = PAUSED; | |
144 | break; | |
145 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
d0f40c50 | 146 | pr_debug("in pause release\n"); |
6f6ffec1 | 147 | ret_val = sst_ops->device_control(SST_SND_RESUME, &str_id); |
fffa1cca VK |
148 | if (ret_val) |
149 | return ret_val; | |
150 | stream->stream_status = RUNNING; | |
151 | break; | |
152 | default: | |
153 | return -EINVAL; | |
154 | } | |
155 | return ret_val; | |
156 | } | |
157 | ||
158 | /** | |
159 | * snd_intelmad_pcm_prepare- internal preparation before starting a stream | |
160 | * | |
161 | * @substream: substream for which the function is called | |
162 | * | |
163 | * This function is called when a stream is started for internal preparation. | |
164 | */ | |
165 | static int snd_intelmad_pcm_prepare(struct snd_pcm_substream *substream) | |
166 | { | |
167 | struct mad_stream_pvt *stream; | |
168 | int ret_val = 0; | |
169 | struct snd_intelmad *intelmaddata; | |
170 | ||
d0f40c50 | 171 | pr_debug("pcm_prepare called\n"); |
fffa1cca VK |
172 | |
173 | WARN_ON(!substream); | |
174 | stream = substream->runtime->private_data; | |
175 | intelmaddata = snd_pcm_substream_chip(substream); | |
d0f40c50 | 176 | pr_debug("pb cnt = %d cap cnt = %d\n",\ |
fffa1cca VK |
177 | intelmaddata->playback_cnt, |
178 | intelmaddata->capture_cnt); | |
179 | ||
180 | if (stream->stream_info.str_id) { | |
d0f40c50 | 181 | pr_debug("Prepare called for already set stream\n"); |
6f6ffec1 VK |
182 | ret_val = intelmaddata->sstdrv_ops->pcm_control->device_control( |
183 | SST_SND_DROP, &stream->stream_info.str_id); | |
fffa1cca VK |
184 | return ret_val; |
185 | } | |
186 | ||
187 | ret_val = snd_intelmad_alloc_stream(substream); | |
188 | if (ret_val < 0) | |
189 | return ret_val; | |
190 | stream->dbg_cum_bytes = 0; | |
191 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
192 | intelmaddata->playback_cnt++; | |
193 | else | |
194 | intelmaddata->capture_cnt++; | |
195 | /* return back the stream id */ | |
196 | snprintf(substream->pcm->id, sizeof(substream->pcm->id), | |
197 | "%d", stream->stream_info.str_id); | |
d0f40c50 | 198 | pr_debug("stream id to user = %s\n", |
fffa1cca VK |
199 | substream->pcm->id); |
200 | ||
201 | ret_val = snd_intelmad_init_stream(substream); | |
202 | if (ret_val) | |
203 | return ret_val; | |
204 | substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER; | |
205 | return ret_val; | |
206 | } | |
207 | ||
208 | static int snd_intelmad_hw_params(struct snd_pcm_substream *substream, | |
209 | struct snd_pcm_hw_params *hw_params) | |
210 | { | |
211 | int ret_val; | |
212 | ||
d0f40c50 | 213 | pr_debug("snd_intelmad_hw_params called\n"); |
fffa1cca VK |
214 | ret_val = snd_pcm_lib_malloc_pages(substream, |
215 | params_buffer_bytes(hw_params)); | |
216 | memset(substream->runtime->dma_area, 0, | |
217 | params_buffer_bytes(hw_params)); | |
218 | ||
219 | return ret_val; | |
220 | } | |
221 | ||
222 | static int snd_intelmad_hw_free(struct snd_pcm_substream *substream) | |
223 | { | |
d0f40c50 | 224 | pr_debug("snd_intelmad_hw_free called\n"); |
fffa1cca VK |
225 | return snd_pcm_lib_free_pages(substream); |
226 | } | |
227 | ||
228 | /** | |
229 | * snd_intelmad_pcm_pointer- to send the current buffer pointer processed by hw | |
230 | * | |
231 | * @substream: substream for which the function is called | |
232 | * | |
233 | * This function is called by ALSA framework to get the current hw buffer ptr | |
234 | * when a period is elapsed | |
235 | */ | |
236 | static snd_pcm_uframes_t snd_intelmad_pcm_pointer | |
237 | (struct snd_pcm_substream *substream) | |
238 | { | |
239 | /* struct snd_pcm_runtime *runtime = substream->runtime; */ | |
240 | struct mad_stream_pvt *stream; | |
241 | struct snd_intelmad *intelmaddata; | |
242 | int ret_val; | |
243 | ||
244 | WARN_ON(!substream); | |
245 | ||
246 | intelmaddata = snd_pcm_substream_chip(substream); | |
247 | stream = substream->runtime->private_data; | |
248 | if (stream->stream_status == INIT) | |
249 | return 0; | |
250 | ||
6f6ffec1 VK |
251 | ret_val = intelmaddata->sstdrv_ops->pcm_control->device_control( |
252 | SST_SND_BUFFER_POINTER, &stream->stream_info); | |
fffa1cca | 253 | if (ret_val) { |
d0f40c50 | 254 | pr_err("error code = 0x%x\n", ret_val); |
fffa1cca VK |
255 | return ret_val; |
256 | } | |
d0f40c50 | 257 | pr_debug("samples reported out 0x%llx\n", |
fffa1cca | 258 | stream->stream_info.buffer_ptr); |
d0f40c50 | 259 | pr_debug("Frame bits:: %d period_count :: %d\n", |
fffa1cca VK |
260 | (int)substream->runtime->frame_bits, |
261 | (int)substream->runtime->period_size); | |
262 | ||
263 | return stream->stream_info.buffer_ptr; | |
264 | ||
265 | } | |
266 | ||
267 | /** | |
268 | * snd_intelmad_close- to free parameteres when stream is stopped | |
269 | * | |
270 | * @substream: substream for which the function is called | |
271 | * | |
272 | * This function is called by ALSA framework when stream is stopped | |
273 | */ | |
274 | static int snd_intelmad_close(struct snd_pcm_substream *substream) | |
275 | { | |
276 | struct snd_intelmad *intelmaddata; | |
277 | struct mad_stream_pvt *stream; | |
6f6ffec1 | 278 | int ret_val = 0, str_id; |
fffa1cca VK |
279 | |
280 | WARN_ON(!substream); | |
281 | ||
282 | stream = substream->runtime->private_data; | |
6f6ffec1 | 283 | str_id = stream->stream_info.str_id; |
fffa1cca | 284 | |
6f6ffec1 | 285 | pr_debug("sst: snd_intelmad_close called for %d\n", str_id); |
fffa1cca VK |
286 | intelmaddata = snd_pcm_substream_chip(substream); |
287 | ||
d0f40c50 | 288 | pr_debug("str id = %d\n", stream->stream_info.str_id); |
fffa1cca VK |
289 | if (stream->stream_info.str_id) { |
290 | /* SST API to actually stop/free the stream */ | |
6f6ffec1 | 291 | ret_val = intelmaddata->sstdrv_ops->pcm_control->close(str_id); |
fffa1cca VK |
292 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
293 | intelmaddata->playback_cnt--; | |
294 | else | |
295 | intelmaddata->capture_cnt--; | |
296 | } | |
d0f40c50 | 297 | pr_debug("snd_intelmad_close : pb cnt = %d cap cnt = %d\n", |
fffa1cca VK |
298 | intelmaddata->playback_cnt, intelmaddata->capture_cnt); |
299 | kfree(substream->runtime->private_data); | |
300 | return ret_val; | |
301 | } | |
302 | ||
303 | /** | |
304 | * snd_intelmad_open- to set runtime parameters during stream start | |
305 | * | |
306 | * @substream: substream for which the function is called | |
307 | * @type: audio device type | |
308 | * | |
309 | * This function is called by ALSA framework when stream is started | |
310 | */ | |
311 | static int snd_intelmad_open(struct snd_pcm_substream *substream, | |
312 | enum snd_sst_audio_device_type type) | |
313 | { | |
314 | struct snd_intelmad *intelmaddata; | |
315 | struct snd_pcm_runtime *runtime; | |
316 | struct mad_stream_pvt *stream; | |
317 | ||
318 | WARN_ON(!substream); | |
319 | ||
d0f40c50 | 320 | pr_debug("snd_intelmad_open called\n"); |
fffa1cca VK |
321 | |
322 | intelmaddata = snd_pcm_substream_chip(substream); | |
323 | runtime = substream->runtime; | |
324 | /* set the runtime hw parameter with local snd_pcm_hardware struct */ | |
325 | runtime->hw = snd_intelmad_stream; | |
326 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { | |
327 | runtime->hw = snd_intelmad_stream; | |
328 | runtime->hw.rates = SNDRV_PCM_RATE_48000; | |
329 | runtime->hw.rate_min = MAX_RATE; | |
330 | runtime->hw.formats = (SNDRV_PCM_FMTBIT_S24 | | |
331 | SNDRV_PCM_FMTBIT_U24); | |
332 | if (intelmaddata->sstdrv_ops->scard_ops->input_dev_id == AMIC) | |
333 | runtime->hw.channels_max = MAX_CHANNEL_AMIC; | |
334 | else | |
335 | runtime->hw.channels_max = MAX_CHANNEL_DMIC; | |
336 | ||
337 | } | |
338 | /* setup the internal datastruture stream pointers based on it being | |
339 | playback or capture stream */ | |
340 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); | |
341 | if (!stream) | |
342 | return -ENOMEM; | |
343 | stream->stream_info.str_id = 0; | |
344 | stream->device = type; | |
345 | stream->stream_status = INIT; | |
346 | runtime->private_data = stream; | |
347 | return snd_pcm_hw_constraint_integer(runtime, | |
348 | SNDRV_PCM_HW_PARAM_PERIODS); | |
349 | } | |
350 | ||
351 | static int snd_intelmad_headset_open(struct snd_pcm_substream *substream) | |
352 | { | |
353 | return snd_intelmad_open(substream, SND_SST_DEVICE_HEADSET); | |
354 | } | |
355 | ||
356 | static int snd_intelmad_ihf_open(struct snd_pcm_substream *substream) | |
357 | { | |
358 | return snd_intelmad_open(substream, SND_SST_DEVICE_IHF); | |
359 | } | |
360 | ||
361 | static int snd_intelmad_vibra_open(struct snd_pcm_substream *substream) | |
362 | { | |
363 | return snd_intelmad_open(substream, SND_SST_DEVICE_VIBRA); | |
364 | } | |
365 | ||
366 | static int snd_intelmad_haptic_open(struct snd_pcm_substream *substream) | |
367 | { | |
368 | return snd_intelmad_open(substream, SND_SST_DEVICE_HAPTIC); | |
369 | } | |
370 | ||
371 | static struct snd_pcm_ops snd_intelmad_headset_ops = { | |
372 | .open = snd_intelmad_headset_open, | |
373 | .close = snd_intelmad_close, | |
374 | .ioctl = snd_pcm_lib_ioctl, | |
375 | .hw_params = snd_intelmad_hw_params, | |
376 | .hw_free = snd_intelmad_hw_free, | |
377 | .prepare = snd_intelmad_pcm_prepare, | |
378 | .trigger = snd_intelmad_pcm_trigger, | |
379 | .pointer = snd_intelmad_pcm_pointer, | |
380 | }; | |
381 | ||
382 | static struct snd_pcm_ops snd_intelmad_ihf_ops = { | |
383 | .open = snd_intelmad_ihf_open, | |
384 | .close = snd_intelmad_close, | |
385 | .ioctl = snd_pcm_lib_ioctl, | |
386 | .hw_params = snd_intelmad_hw_params, | |
387 | .hw_free = snd_intelmad_hw_free, | |
388 | .prepare = snd_intelmad_pcm_prepare, | |
389 | .trigger = snd_intelmad_pcm_trigger, | |
390 | .pointer = snd_intelmad_pcm_pointer, | |
391 | }; | |
392 | ||
393 | static struct snd_pcm_ops snd_intelmad_vibra_ops = { | |
394 | .open = snd_intelmad_vibra_open, | |
395 | .close = snd_intelmad_close, | |
396 | .ioctl = snd_pcm_lib_ioctl, | |
397 | .hw_params = snd_intelmad_hw_params, | |
398 | .hw_free = snd_intelmad_hw_free, | |
399 | .prepare = snd_intelmad_pcm_prepare, | |
400 | .trigger = snd_intelmad_pcm_trigger, | |
401 | .pointer = snd_intelmad_pcm_pointer, | |
402 | }; | |
403 | ||
404 | static struct snd_pcm_ops snd_intelmad_haptic_ops = { | |
405 | .open = snd_intelmad_haptic_open, | |
406 | .close = snd_intelmad_close, | |
407 | .ioctl = snd_pcm_lib_ioctl, | |
408 | .hw_params = snd_intelmad_hw_params, | |
409 | .hw_free = snd_intelmad_hw_free, | |
410 | .prepare = snd_intelmad_pcm_prepare, | |
411 | .trigger = snd_intelmad_pcm_trigger, | |
412 | .pointer = snd_intelmad_pcm_pointer, | |
413 | }; | |
414 | ||
415 | static struct snd_pcm_ops snd_intelmad_capture_ops = { | |
416 | .open = snd_intelmad_headset_open, | |
417 | .close = snd_intelmad_close, | |
418 | .ioctl = snd_pcm_lib_ioctl, | |
419 | .hw_params = snd_intelmad_hw_params, | |
420 | .hw_free = snd_intelmad_hw_free, | |
421 | .prepare = snd_intelmad_pcm_prepare, | |
422 | .trigger = snd_intelmad_pcm_trigger, | |
423 | .pointer = snd_intelmad_pcm_pointer, | |
424 | }; | |
425 | ||
426 | ||
427 | /** | |
428 | * snd_intelmad_intr_handler- interrupt handler | |
429 | * | |
430 | * @irq : irq number of the interrupt received | |
431 | * @dev: device context | |
432 | * | |
433 | * This function is called when an interrupt is raised at the sound card | |
434 | */ | |
435 | static irqreturn_t snd_intelmad_intr_handler(int irq, void *dev) | |
436 | { | |
437 | struct snd_intelmad *intelmaddata = | |
438 | (struct snd_intelmad *)dev; | |
439 | u8 intsts; | |
440 | ||
441 | memcpy_fromio(&intsts, | |
442 | ((void *)(intelmaddata->int_base)), | |
443 | sizeof(u8)); | |
444 | intelmaddata->mad_jack_msg.intsts = intsts; | |
445 | intelmaddata->mad_jack_msg.intelmaddata = intelmaddata; | |
446 | ||
447 | queue_work(intelmaddata->mad_jack_wq, &intelmaddata->mad_jack_msg.wq); | |
448 | ||
449 | return IRQ_HANDLED; | |
450 | } | |
451 | ||
452 | void sst_mad_send_jack_report(struct snd_jack *jack, | |
453 | int buttonpressevent , int status) | |
454 | { | |
455 | ||
456 | if (!jack) { | |
d0f40c50 | 457 | pr_debug("MAD error jack empty\n"); |
fffa1cca VK |
458 | |
459 | } else { | |
d0f40c50 JP |
460 | pr_debug("MAD send jack report for = %d!!!\n", status); |
461 | pr_debug("MAD send jack report %d\n", jack->type); | |
fffa1cca VK |
462 | snd_jack_report(jack, status); |
463 | ||
464 | /*button pressed and released */ | |
465 | if (buttonpressevent) | |
466 | snd_jack_report(jack, 0); | |
d0f40c50 | 467 | pr_debug("MAD sending jack report Done !!!\n"); |
fffa1cca VK |
468 | } |
469 | ||
470 | ||
471 | ||
472 | } | |
473 | ||
474 | void sst_mad_jackdetection_fs(u8 intsts , struct snd_intelmad *intelmaddata) | |
475 | { | |
476 | struct snd_jack *jack = NULL; | |
477 | unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; | |
478 | struct sc_reg_access sc_access[] = { | |
479 | {0x187, 0x00, MASK7}, | |
480 | {0x188, 0x10, MASK4}, | |
481 | {0x18b, 0x10, MASK4}, | |
482 | }; | |
483 | ||
484 | struct sc_reg_access sc_access_write[] = { | |
485 | {0x198, 0x00, 0x0}, | |
486 | }; | |
487 | ||
488 | if (intsts & 0x4) { | |
489 | ||
490 | if (!(intelmid_audio_interrupt_enable)) { | |
d0f40c50 | 491 | pr_debug("Audio interrupt enable\n"); |
fffa1cca VK |
492 | sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); |
493 | ||
494 | sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1); | |
495 | intelmid_audio_interrupt_enable = 1; | |
496 | intelmaddata->jack[0].jack_status = 0; | |
497 | intelmaddata->jack[1].jack_status = 0; | |
498 | ||
499 | } | |
500 | /* send headphone detect */ | |
d0f40c50 | 501 | pr_debug("MAD headphone %d\n", intsts & 0x4); |
fffa1cca VK |
502 | jack = &intelmaddata->jack[0].jack; |
503 | present = !(intelmaddata->jack[0].jack_status); | |
504 | intelmaddata->jack[0].jack_status = present; | |
505 | jack_event_flag = 1; | |
506 | ||
507 | } | |
508 | ||
509 | if (intsts & 0x2) { | |
510 | /* send short push */ | |
d0f40c50 | 511 | pr_debug("MAD short push %d\n", intsts & 0x2); |
fffa1cca VK |
512 | jack = &intelmaddata->jack[2].jack; |
513 | present = 1; | |
514 | jack_event_flag = 1; | |
515 | buttonpressflag = 1; | |
516 | } | |
517 | if (intsts & 0x1) { | |
518 | /* send long push */ | |
d0f40c50 | 519 | pr_debug("MAD long push %d\n", intsts & 0x1); |
fffa1cca VK |
520 | jack = &intelmaddata->jack[3].jack; |
521 | present = 1; | |
522 | jack_event_flag = 1; | |
523 | buttonpressflag = 1; | |
524 | } | |
525 | if (intsts & 0x8) { | |
526 | if (!(intelmid_audio_interrupt_enable)) { | |
d0f40c50 | 527 | pr_debug("Audio interrupt enable\n"); |
fffa1cca VK |
528 | sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); |
529 | ||
530 | sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1); | |
531 | intelmid_audio_interrupt_enable = 1; | |
532 | intelmaddata->jack[0].jack_status = 0; | |
533 | intelmaddata->jack[1].jack_status = 0; | |
534 | } | |
535 | /* send headset detect */ | |
d0f40c50 | 536 | pr_debug("MAD headset = %d\n", intsts & 0x8); |
fffa1cca VK |
537 | jack = &intelmaddata->jack[1].jack; |
538 | present = !(intelmaddata->jack[1].jack_status); | |
539 | intelmaddata->jack[1].jack_status = present; | |
540 | jack_event_flag = 1; | |
541 | } | |
542 | ||
543 | if (jack_event_flag) | |
544 | sst_mad_send_jack_report(jack, buttonpressflag, present); | |
545 | } | |
546 | ||
547 | ||
548 | void sst_mad_jackdetection_mx(u8 intsts, struct snd_intelmad *intelmaddata) | |
549 | { | |
550 | u8 value = 0, jack_prev_state = 0; | |
551 | struct snd_jack *jack = NULL; | |
552 | unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; | |
553 | time_t timediff; | |
554 | struct sc_reg_access sc_access_read = {0,}; | |
555 | struct snd_pmic_ops *scard_ops; | |
556 | ||
557 | scard_ops = intelmaddata->sstdrv_ops->scard_ops; | |
558 | ||
d0f40c50 | 559 | pr_debug("previous value: %x\n", intelmaddata->jack_prev_state); |
fffa1cca VK |
560 | |
561 | if (!(intelmid_audio_interrupt_enable)) { | |
d0f40c50 | 562 | pr_debug("Audio interrupt enable\n"); |
fffa1cca VK |
563 | intelmaddata->jack_prev_state = 0xC0; |
564 | intelmid_audio_interrupt_enable = 1; | |
565 | } | |
566 | ||
567 | if (intsts & 0x2) { | |
568 | jack_prev_state = intelmaddata->jack_prev_state; | |
569 | if (intelmaddata->pmic_status == PMIC_INIT) { | |
570 | sc_access_read.reg_addr = 0x201; | |
571 | sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); | |
572 | value = (sc_access_read.value); | |
d0f40c50 | 573 | pr_debug("value returned = 0x%x\n", value); |
fffa1cca VK |
574 | } |
575 | ||
576 | if (jack_prev_state == 0xc0 && value == 0x40) { | |
577 | /*headset detected. */ | |
d0f40c50 | 578 | pr_debug("MAD headset inserted\n"); |
fffa1cca VK |
579 | jack = &intelmaddata->jack[1].jack; |
580 | present = 1; | |
581 | jack_event_flag = 1; | |
582 | intelmaddata->jack[1].jack_status = 1; | |
583 | ||
584 | } | |
585 | ||
586 | if (jack_prev_state == 0xc0 && value == 0x00) { | |
587 | /* headphone detected. */ | |
d0f40c50 | 588 | pr_debug("MAD headphone inserted\n"); |
fffa1cca VK |
589 | jack = &intelmaddata->jack[0].jack; |
590 | present = 1; | |
591 | jack_event_flag = 1; | |
592 | ||
593 | } | |
594 | ||
595 | if (jack_prev_state == 0x40 && value == 0xc0) { | |
596 | /*headset removed*/ | |
d0f40c50 | 597 | pr_debug("Jack headset status %d\n", |
fffa1cca | 598 | intelmaddata->jack[1].jack_status); |
d0f40c50 | 599 | pr_debug("MAD headset removed\n"); |
fffa1cca VK |
600 | jack = &intelmaddata->jack[1].jack; |
601 | present = 0; | |
602 | jack_event_flag = 1; | |
603 | intelmaddata->jack[1].jack_status = 0; | |
604 | } | |
605 | ||
606 | if (jack_prev_state == 0x00 && value == 0xc0) { | |
607 | /* headphone detected. */ | |
d0f40c50 | 608 | pr_debug("Jack headphone status %d\n", |
fffa1cca | 609 | intelmaddata->jack[0].jack_status); |
d0f40c50 | 610 | pr_debug("headphone removed\n"); |
fffa1cca VK |
611 | jack = &intelmaddata->jack[0].jack; |
612 | present = 0; | |
613 | jack_event_flag = 1; | |
614 | } | |
615 | ||
616 | if (jack_prev_state == 0x40 && value == 0x00) { | |
617 | /*button pressed*/ | |
618 | do_gettimeofday(&intelmaddata->jack[1].buttonpressed); | |
d0f40c50 | 619 | pr_debug("MAD button press detected\n"); |
fffa1cca VK |
620 | } |
621 | ||
622 | ||
623 | if (jack_prev_state == 0x00 && value == 0x40) { | |
624 | if (intelmaddata->jack[1].jack_status) { | |
625 | /*button pressed*/ | |
626 | do_gettimeofday( | |
627 | &intelmaddata->jack[1].buttonreleased); | |
628 | /*button pressed */ | |
d0f40c50 | 629 | pr_debug("Button Released detected\n"); |
fffa1cca VK |
630 | timediff = intelmaddata->jack[1]. |
631 | buttonreleased.tv_sec - intelmaddata-> | |
632 | jack[1].buttonpressed.tv_sec; | |
633 | buttonpressflag = 1; | |
634 | if (timediff > 1) { | |
d0f40c50 | 635 | pr_debug("long press detected\n"); |
fffa1cca VK |
636 | /* send headphone detect/undetect */ |
637 | jack = &intelmaddata->jack[3].jack; | |
638 | present = 1; | |
639 | jack_event_flag = 1; | |
83cb1926 | 640 | } else { |
d0f40c50 | 641 | pr_debug("short press detected\n"); |
fffa1cca VK |
642 | /* send headphone detect/undetect */ |
643 | jack = &intelmaddata->jack[2].jack; | |
644 | present = 1; | |
645 | jack_event_flag = 1; | |
646 | } | |
647 | } | |
648 | ||
649 | } | |
83cb1926 | 650 | intelmaddata->jack_prev_state = value; |
fffa1cca | 651 | } |
fffa1cca VK |
652 | if (jack_event_flag) |
653 | sst_mad_send_jack_report(jack, buttonpressflag, present); | |
654 | } | |
655 | ||
656 | ||
657 | void sst_mad_jackdetection_nec(u8 intsts, struct snd_intelmad *intelmaddata) | |
658 | { | |
659 | u8 value = 0; | |
660 | struct snd_jack *jack = NULL; | |
661 | unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; | |
662 | struct sc_reg_access sc_access_read = {0,}; | |
663 | ||
664 | if (intelmaddata->pmic_status == PMIC_INIT) { | |
665 | sc_access_read.reg_addr = 0x132; | |
666 | sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); | |
667 | value = (sc_access_read.value); | |
d0f40c50 | 668 | pr_debug("value returned = 0x%x\n", value); |
fffa1cca VK |
669 | } |
670 | if (intsts & 0x1) { | |
d0f40c50 | 671 | pr_debug("headset detected\n"); |
fffa1cca VK |
672 | /* send headset detect/undetect */ |
673 | jack = &intelmaddata->jack[1].jack; | |
674 | present = (value == 0x1) ? 1 : 0; | |
675 | jack_event_flag = 1; | |
676 | } | |
677 | if (intsts & 0x2) { | |
d0f40c50 | 678 | pr_debug("headphone detected\n"); |
fffa1cca VK |
679 | /* send headphone detect/undetect */ |
680 | jack = &intelmaddata->jack[0].jack; | |
681 | present = (value == 0x2) ? 1 : 0; | |
682 | jack_event_flag = 1; | |
683 | } | |
684 | if (intsts & 0x4) { | |
d0f40c50 | 685 | pr_debug("short push detected\n"); |
fffa1cca VK |
686 | /* send short push */ |
687 | jack = &intelmaddata->jack[2].jack; | |
688 | present = 1; | |
689 | jack_event_flag = 1; | |
690 | buttonpressflag = 1; | |
691 | } | |
692 | if (intsts & 0x8) { | |
d0f40c50 | 693 | pr_debug("long push detected\n"); |
fffa1cca VK |
694 | /* send long push */ |
695 | jack = &intelmaddata->jack[3].jack; | |
696 | present = 1; | |
697 | jack_event_flag = 1; | |
698 | buttonpressflag = 1; | |
699 | } | |
700 | ||
701 | if (jack_event_flag) | |
702 | sst_mad_send_jack_report(jack, buttonpressflag, present); | |
703 | ||
704 | ||
705 | } | |
706 | ||
707 | void sst_process_mad_jack_detection(struct work_struct *work) | |
708 | { | |
709 | u8 intsts; | |
710 | struct mad_jack_msg_wq *mad_jack_detect = | |
711 | container_of(work, struct mad_jack_msg_wq, wq); | |
712 | ||
713 | struct snd_intelmad *intelmaddata = | |
714 | mad_jack_detect->intelmaddata; | |
715 | ||
716 | intsts = mad_jack_detect->intsts; | |
717 | ||
718 | switch (intelmaddata->sstdrv_ops->vendor_id) { | |
719 | case SND_FS: | |
720 | sst_mad_jackdetection_fs(intsts , intelmaddata); | |
721 | break; | |
722 | case SND_MX: | |
723 | sst_mad_jackdetection_mx(intsts , intelmaddata); | |
724 | break; | |
725 | case SND_NC: | |
726 | sst_mad_jackdetection_nec(intsts , intelmaddata); | |
727 | break; | |
728 | } | |
729 | } | |
730 | ||
731 | ||
732 | static int __devinit snd_intelmad_register_irq( | |
733 | struct snd_intelmad *intelmaddata) | |
734 | { | |
735 | int ret_val; | |
736 | u32 regbase = AUDINT_BASE, regsize = 8; | |
737 | char *drv_name; | |
738 | ||
d0f40c50 | 739 | pr_debug("irq reg done, regbase 0x%x, regsize 0x%x\n", |
fffa1cca VK |
740 | regbase, regsize); |
741 | intelmaddata->int_base = ioremap_nocache(regbase, regsize); | |
742 | if (!intelmaddata->int_base) | |
d0f40c50 JP |
743 | pr_err("Mapping of cache failed\n"); |
744 | pr_debug("irq = 0x%x\n", intelmaddata->irq); | |
fffa1cca VK |
745 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) |
746 | drv_name = DRIVER_NAME_MFLD; | |
747 | else | |
748 | drv_name = DRIVER_NAME_MRST; | |
749 | ret_val = request_irq(intelmaddata->irq, | |
750 | snd_intelmad_intr_handler, | |
751 | IRQF_SHARED, drv_name, | |
752 | intelmaddata); | |
753 | if (ret_val) | |
d0f40c50 | 754 | pr_err("cannot register IRQ\n"); |
fffa1cca VK |
755 | return ret_val; |
756 | } | |
757 | ||
758 | static int __devinit snd_intelmad_sst_register( | |
759 | struct snd_intelmad *intelmaddata) | |
760 | { | |
761 | int ret_val = 0; | |
762 | struct snd_pmic_ops *intelmad_vendor_ops[MAX_VENDORS] = { | |
763 | &snd_pmic_ops_fs, | |
764 | &snd_pmic_ops_mx, | |
765 | &snd_pmic_ops_nc, | |
766 | &snd_msic_ops | |
767 | }; | |
768 | ||
769 | struct sc_reg_access vendor_addr = {0x00, 0x00, 0x00}; | |
770 | ||
771 | if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) { | |
772 | ret_val = sst_sc_reg_access(&vendor_addr, PMIC_READ, 1); | |
773 | if (ret_val) | |
774 | return ret_val; | |
775 | sst_card_vendor_id = (vendor_addr.value & (MASK2|MASK1|MASK0)); | |
25985edc | 776 | pr_debug("original n extrated vendor id = 0x%x %d\n", |
fffa1cca VK |
777 | vendor_addr.value, sst_card_vendor_id); |
778 | if (sst_card_vendor_id < 0 || sst_card_vendor_id > 2) { | |
d0f40c50 | 779 | pr_err("vendor card not supported!!\n"); |
fffa1cca VK |
780 | return -EIO; |
781 | } | |
782 | } else | |
783 | sst_card_vendor_id = 0x3; | |
784 | ||
785 | intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES; | |
786 | intelmaddata->sstdrv_ops->vendor_id = sst_card_vendor_id; | |
787 | BUG_ON(!intelmad_vendor_ops[sst_card_vendor_id]); | |
788 | intelmaddata->sstdrv_ops->scard_ops = | |
789 | intelmad_vendor_ops[sst_card_vendor_id]; | |
790 | ||
791 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { | |
792 | intelmaddata->sstdrv_ops->scard_ops->pb_on = 0; | |
793 | intelmaddata->sstdrv_ops->scard_ops->cap_on = 0; | |
794 | intelmaddata->sstdrv_ops->scard_ops->input_dev_id = DMIC; | |
795 | intelmaddata->sstdrv_ops->scard_ops->output_dev_id = | |
796 | STEREO_HEADPHONE; | |
797 | } | |
798 | ||
799 | /* registering with SST driver to get access to SST APIs to use */ | |
800 | ret_val = register_sst_card(intelmaddata->sstdrv_ops); | |
801 | if (ret_val) { | |
d0f40c50 | 802 | pr_err("sst card registration failed\n"); |
fffa1cca VK |
803 | return ret_val; |
804 | } | |
805 | ||
806 | sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id; | |
807 | intelmaddata->pmic_status = PMIC_UNINIT; | |
808 | return ret_val; | |
809 | } | |
810 | ||
811 | /* Driver Init/exit functionalities */ | |
812 | /** | |
813 | * snd_intelmad_pcm_new - to setup pcm for the card | |
814 | * | |
815 | * @card: pointer to the sound card structure | |
816 | * @intelmaddata: pointer to internal context | |
817 | * @pb: playback count for this card | |
818 | * @cap: capture count for this card | |
819 | * @index: device index | |
820 | * | |
821 | * This function is called from probe function to set up pcm params | |
822 | * and functions | |
823 | */ | |
824 | static int __devinit snd_intelmad_pcm_new(struct snd_card *card, | |
825 | struct snd_intelmad *intelmaddata, | |
826 | unsigned int pb, unsigned int cap, unsigned int index) | |
827 | { | |
828 | int ret_val = 0; | |
829 | struct snd_pcm *pcm; | |
830 | char name[32] = INTEL_MAD; | |
831 | struct snd_pcm_ops *pb_ops = NULL, *cap_ops = NULL; | |
832 | ||
d0f40c50 | 833 | pr_debug("called for pb %d, cp %d, idx %d\n", pb, cap, index); |
fffa1cca VK |
834 | ret_val = snd_pcm_new(card, name, index, pb, cap, &pcm); |
835 | if (ret_val) | |
836 | return ret_val; | |
837 | /* setup the ops for playback and capture streams */ | |
838 | switch (index) { | |
839 | case 0: | |
840 | pb_ops = &snd_intelmad_headset_ops; | |
841 | cap_ops = &snd_intelmad_capture_ops; | |
842 | break; | |
843 | case 1: | |
844 | pb_ops = &snd_intelmad_ihf_ops; | |
845 | cap_ops = &snd_intelmad_capture_ops; | |
846 | break; | |
847 | case 2: | |
848 | pb_ops = &snd_intelmad_vibra_ops; | |
849 | cap_ops = &snd_intelmad_capture_ops; | |
850 | break; | |
851 | case 3: | |
852 | pb_ops = &snd_intelmad_haptic_ops; | |
853 | cap_ops = &snd_intelmad_capture_ops; | |
854 | break; | |
855 | } | |
856 | if (pb) | |
857 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, pb_ops); | |
858 | if (cap) | |
859 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, cap_ops); | |
860 | /* setup private data which can be retrieved when required */ | |
861 | pcm->private_data = intelmaddata; | |
862 | pcm->info_flags = 0; | |
863 | strncpy(pcm->name, card->shortname, strlen(card->shortname)); | |
864 | /* allocate dma pages for ALSA stream operations */ | |
865 | snd_pcm_lib_preallocate_pages_for_all(pcm, | |
866 | SNDRV_DMA_TYPE_CONTINUOUS, | |
867 | snd_dma_continuous_data(GFP_KERNEL), | |
868 | MIN_BUFFER, MAX_BUFFER); | |
869 | return ret_val; | |
870 | } | |
871 | ||
872 | static int __devinit snd_intelmad_pcm(struct snd_card *card, | |
873 | struct snd_intelmad *intelmaddata) | |
874 | { | |
875 | int ret_val = 0; | |
876 | ||
877 | WARN_ON(!card); | |
878 | WARN_ON(!intelmaddata); | |
d0f40c50 | 879 | pr_debug("snd_intelmad_pcm called\n"); |
fffa1cca VK |
880 | ret_val = snd_intelmad_pcm_new(card, intelmaddata, 1, 1, 0); |
881 | if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) | |
882 | return ret_val; | |
883 | ret_val = snd_intelmad_pcm_new(card, intelmaddata, 1, 0, 1); | |
884 | if (ret_val) | |
885 | return ret_val; | |
886 | ret_val = snd_intelmad_pcm_new(card, intelmaddata, 1, 0, 2); | |
887 | if (ret_val) | |
888 | return ret_val; | |
889 | return snd_intelmad_pcm_new(card, intelmaddata, 1, 0, 3); | |
890 | } | |
891 | ||
892 | /** | |
893 | * snd_intelmad_jack- to setup jack settings of the card | |
894 | * | |
895 | * @intelmaddata: pointer to internal context | |
896 | * | |
897 | * This function is called send jack events | |
898 | */ | |
899 | static int snd_intelmad_jack(struct snd_intelmad *intelmaddata) | |
900 | { | |
901 | struct snd_jack *jack; | |
902 | int retval; | |
903 | ||
d0f40c50 | 904 | pr_debug("snd_intelmad_jack called\n"); |
fffa1cca VK |
905 | jack = &intelmaddata->jack[0].jack; |
906 | retval = snd_jack_new(intelmaddata->card, "Headphone", | |
907 | SND_JACK_HEADPHONE, &jack); | |
908 | if (retval < 0) | |
909 | return retval; | |
910 | snd_jack_report(jack, 0); | |
911 | ||
912 | jack->private_data = jack; | |
913 | intelmaddata->jack[0].jack = *jack; | |
914 | ||
915 | ||
916 | jack = &intelmaddata->jack[1].jack; | |
917 | retval = snd_jack_new(intelmaddata->card, "Headset", | |
918 | SND_JACK_HEADSET, &jack); | |
919 | if (retval < 0) | |
920 | return retval; | |
921 | ||
922 | ||
923 | ||
924 | jack->private_data = jack; | |
925 | intelmaddata->jack[1].jack = *jack; | |
926 | ||
927 | ||
928 | jack = &intelmaddata->jack[2].jack; | |
929 | retval = snd_jack_new(intelmaddata->card, "Short Press", | |
930 | SND_JACK_HS_SHORT_PRESS, &jack); | |
931 | if (retval < 0) | |
932 | return retval; | |
933 | ||
934 | ||
935 | jack->private_data = jack; | |
936 | intelmaddata->jack[2].jack = *jack; | |
937 | ||
938 | ||
939 | jack = &intelmaddata->jack[3].jack; | |
940 | retval = snd_jack_new(intelmaddata->card, "Long Press", | |
941 | SND_JACK_HS_LONG_PRESS, &jack); | |
942 | if (retval < 0) | |
943 | return retval; | |
944 | ||
945 | ||
946 | jack->private_data = jack; | |
947 | intelmaddata->jack[3].jack = *jack; | |
948 | ||
949 | return retval; | |
950 | } | |
951 | ||
952 | /** | |
953 | * snd_intelmad_mixer- to setup mixer settings of the card | |
954 | * | |
955 | * @intelmaddata: pointer to internal context | |
956 | * | |
957 | * This function is called from probe function to set up mixer controls | |
958 | */ | |
959 | static int __devinit snd_intelmad_mixer(struct snd_intelmad *intelmaddata) | |
960 | { | |
961 | struct snd_card *card; | |
962 | unsigned int idx; | |
963 | int ret_val = 0, max_controls = 0; | |
964 | char *mixername = "IntelMAD Controls"; | |
965 | struct snd_kcontrol_new *controls; | |
966 | ||
967 | WARN_ON(!intelmaddata); | |
968 | ||
969 | card = intelmaddata->card; | |
970 | strncpy(card->mixername, mixername, sizeof(card->mixername)-1); | |
971 | /* add all widget controls and expose the same */ | |
972 | if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { | |
973 | max_controls = MAX_CTRL_MFLD; | |
974 | controls = snd_intelmad_controls_mfld; | |
975 | } else { | |
976 | max_controls = MAX_CTRL_MRST; | |
977 | controls = snd_intelmad_controls_mrst; | |
978 | } | |
979 | for (idx = 0; idx < max_controls; idx++) { | |
980 | ret_val = snd_ctl_add(card, | |
981 | snd_ctl_new1(&controls[idx], | |
982 | intelmaddata)); | |
d0f40c50 | 983 | pr_debug("mixer[idx]=%d added\n", idx); |
fffa1cca | 984 | if (ret_val) { |
d0f40c50 | 985 | pr_err("in adding of control index = %d\n", idx); |
fffa1cca VK |
986 | break; |
987 | } | |
988 | } | |
989 | return ret_val; | |
990 | } | |
991 | ||
992 | static int snd_intelmad_dev_free(struct snd_device *device) | |
993 | { | |
994 | struct snd_intelmad *intelmaddata; | |
995 | ||
996 | WARN_ON(!device); | |
997 | ||
998 | intelmaddata = device->device_data; | |
999 | ||
d0f40c50 | 1000 | pr_debug("snd_intelmad_dev_free called\n"); |
fffa1cca VK |
1001 | snd_card_free(intelmaddata->card); |
1002 | /*genl_unregister_family(&audio_event_genl_family);*/ | |
1003 | unregister_sst_card(intelmaddata->sstdrv_ops); | |
1004 | ||
1005 | /* free allocated memory for internal context */ | |
1006 | destroy_workqueue(intelmaddata->mad_jack_wq); | |
1007 | kfree(intelmaddata->sstdrv_ops); | |
1008 | kfree(intelmaddata); | |
1009 | return 0; | |
1010 | } | |
1011 | ||
1012 | static int __devinit snd_intelmad_create( | |
1013 | struct snd_intelmad *intelmaddata, | |
1014 | struct snd_card *card) | |
1015 | { | |
1016 | int ret_val; | |
1017 | static struct snd_device_ops ops = { | |
1018 | .dev_free = snd_intelmad_dev_free, | |
1019 | }; | |
1020 | ||
1021 | WARN_ON(!intelmaddata); | |
1022 | WARN_ON(!card); | |
1023 | /* ALSA api to register for the device */ | |
1024 | ret_val = snd_device_new(card, SNDRV_DEV_LOWLEVEL, intelmaddata, &ops); | |
1025 | return ret_val; | |
1026 | } | |
1027 | ||
1028 | /** | |
1029 | * snd_intelmad_probe- function registred for init | |
1030 | * @pdev : pointer to the device struture | |
1031 | * This function is called when the device is initialized | |
1032 | */ | |
1033 | int __devinit snd_intelmad_probe(struct platform_device *pdev) | |
1034 | { | |
1035 | struct snd_card *card; | |
1036 | int ret_val; | |
1037 | struct snd_intelmad *intelmaddata; | |
1038 | const struct platform_device_id *id = platform_get_device_id(pdev); | |
1039 | unsigned int cpu_id = (unsigned int)id->driver_data; | |
1040 | ||
d0f40c50 | 1041 | pr_debug("probe for %s cpu_id %d\n", pdev->name, cpu_id); |
fffa1cca | 1042 | if (!strcmp(pdev->name, DRIVER_NAME_MRST)) |
d0f40c50 | 1043 | pr_debug("detected MRST\n"); |
fffa1cca | 1044 | else if (!strcmp(pdev->name, DRIVER_NAME_MFLD)) |
d0f40c50 | 1045 | pr_debug("detected MFLD\n"); |
fffa1cca | 1046 | else { |
d0f40c50 | 1047 | pr_err("detected unknown device abort!!\n"); |
fffa1cca VK |
1048 | return -EIO; |
1049 | } | |
1050 | if ((cpu_id < CPU_CHIP_LINCROFT) || (cpu_id > CPU_CHIP_PENWELL)) { | |
d0f40c50 | 1051 | pr_err("detected unknown cpu_id abort!!\n"); |
fffa1cca VK |
1052 | return -EIO; |
1053 | } | |
1054 | /* allocate memory for saving internal context and working */ | |
1055 | intelmaddata = kzalloc(sizeof(*intelmaddata), GFP_KERNEL); | |
1056 | if (!intelmaddata) { | |
d0f40c50 | 1057 | pr_debug("mem alloctn fail\n"); |
fffa1cca VK |
1058 | return -ENOMEM; |
1059 | } | |
1060 | ||
1061 | /* allocate memory for LPE API set */ | |
1062 | intelmaddata->sstdrv_ops = kzalloc(sizeof(struct intel_sst_card_ops), | |
1063 | GFP_KERNEL); | |
1064 | if (!intelmaddata->sstdrv_ops) { | |
d0f40c50 | 1065 | pr_err("mem allocation for ops fail\n"); |
fffa1cca VK |
1066 | kfree(intelmaddata); |
1067 | return -ENOMEM; | |
1068 | } | |
1069 | ||
1070 | intelmaddata->cpu_id = cpu_id; | |
1071 | /* create a card instance with ALSA framework */ | |
1072 | ret_val = snd_card_create(card_index, card_id, THIS_MODULE, 0, &card); | |
1073 | if (ret_val) { | |
d0f40c50 | 1074 | pr_err("snd_card_create fail\n"); |
fffa1cca VK |
1075 | goto free_allocs; |
1076 | } | |
1077 | ||
1078 | intelmaddata->pdev = pdev; | |
1079 | intelmaddata->irq = platform_get_irq(pdev, 0); | |
1080 | platform_set_drvdata(pdev, intelmaddata); | |
1081 | intelmaddata->card = card; | |
1082 | intelmaddata->card_id = card_id; | |
1083 | intelmaddata->card_index = card_index; | |
1084 | intelmaddata->master_mute = UNMUTE; | |
1085 | intelmaddata->playback_cnt = intelmaddata->capture_cnt = 0; | |
1086 | strncpy(card->driver, INTEL_MAD, strlen(INTEL_MAD)); | |
1087 | strncpy(card->shortname, INTEL_MAD, strlen(INTEL_MAD)); | |
1088 | ||
1089 | intelmaddata->sstdrv_ops->module_name = SST_CARD_NAMES; | |
1090 | /* registering with LPE driver to get access to SST APIs to use */ | |
1091 | ret_val = snd_intelmad_sst_register(intelmaddata); | |
1092 | if (ret_val) { | |
d0f40c50 | 1093 | pr_err("snd_intelmad_sst_register failed\n"); |
fffa1cca VK |
1094 | goto free_allocs; |
1095 | } | |
1096 | ||
1097 | intelmaddata->pmic_status = PMIC_INIT; | |
1098 | ||
1099 | ret_val = snd_intelmad_pcm(card, intelmaddata); | |
1100 | if (ret_val) { | |
d0f40c50 | 1101 | pr_err("snd_intelmad_pcm failed\n"); |
fffa1cca VK |
1102 | goto free_allocs; |
1103 | } | |
1104 | ||
1105 | ret_val = snd_intelmad_mixer(intelmaddata); | |
1106 | if (ret_val) { | |
d0f40c50 | 1107 | pr_err("snd_intelmad_mixer failed\n"); |
fffa1cca VK |
1108 | goto free_allocs; |
1109 | } | |
1110 | ||
1111 | ret_val = snd_intelmad_jack(intelmaddata); | |
1112 | if (ret_val) { | |
d0f40c50 | 1113 | pr_err("snd_intelmad_jack failed\n"); |
fffa1cca VK |
1114 | goto free_allocs; |
1115 | } | |
1116 | ||
1117 | /*create work queue for jack interrupt*/ | |
1118 | INIT_WORK(&intelmaddata->mad_jack_msg.wq, | |
1119 | sst_process_mad_jack_detection); | |
1120 | ||
1121 | intelmaddata->mad_jack_wq = create_workqueue("sst_mad_jack_wq"); | |
1122 | if (!intelmaddata->mad_jack_wq) | |
1123 | goto free_mad_jack_wq; | |
1124 | ||
1125 | ret_val = snd_intelmad_register_irq(intelmaddata); | |
1126 | if (ret_val) { | |
d0f40c50 | 1127 | pr_err("snd_intelmad_register_irq fail\n"); |
fffa1cca VK |
1128 | goto free_allocs; |
1129 | } | |
1130 | ||
1131 | /* internal function call to register device with ALSA */ | |
1132 | ret_val = snd_intelmad_create(intelmaddata, card); | |
1133 | if (ret_val) { | |
d0f40c50 | 1134 | pr_err("snd_intelmad_create failed\n"); |
fffa1cca VK |
1135 | goto free_allocs; |
1136 | } | |
1137 | card->private_data = &intelmaddata; | |
1138 | snd_card_set_dev(card, &pdev->dev); | |
1139 | ret_val = snd_card_register(card); | |
1140 | if (ret_val) { | |
d0f40c50 | 1141 | pr_err("snd_card_register failed\n"); |
fffa1cca VK |
1142 | goto free_allocs; |
1143 | } | |
1144 | ||
d0f40c50 | 1145 | pr_debug("snd_intelmad_probe complete\n"); |
fffa1cca VK |
1146 | return ret_val; |
1147 | ||
1148 | free_mad_jack_wq: | |
1149 | destroy_workqueue(intelmaddata->mad_jack_wq); | |
1150 | free_allocs: | |
d0f40c50 | 1151 | pr_err("probe failed\n"); |
fffa1cca VK |
1152 | snd_card_free(card); |
1153 | kfree(intelmaddata->sstdrv_ops); | |
1154 | kfree(intelmaddata); | |
1155 | return ret_val; | |
1156 | } | |
1157 | ||
1158 | ||
1159 | static int snd_intelmad_remove(struct platform_device *pdev) | |
1160 | { | |
1161 | struct snd_intelmad *intelmaddata = platform_get_drvdata(pdev); | |
1162 | ||
1163 | if (intelmaddata) { | |
1164 | snd_card_free(intelmaddata->card); | |
1165 | unregister_sst_card(intelmaddata->sstdrv_ops); | |
1166 | /* free allocated memory for internal context */ | |
1167 | destroy_workqueue(intelmaddata->mad_jack_wq); | |
1168 | kfree(intelmaddata->sstdrv_ops); | |
1169 | kfree(intelmaddata); | |
1170 | } | |
1171 | return 0; | |
1172 | } | |
1173 | ||
1174 | /********************************************************************* | |
1175 | * Driver initialization and exit | |
1176 | *********************************************************************/ | |
1177 | static const struct platform_device_id snd_intelmad_ids[] = { | |
1178 | {DRIVER_NAME_MRST, CPU_CHIP_LINCROFT}, | |
1179 | {DRIVER_NAME_MFLD, CPU_CHIP_PENWELL}, | |
1180 | {"", 0}, | |
1181 | ||
1182 | }; | |
1183 | ||
1184 | static struct platform_driver snd_intelmad_driver = { | |
1185 | .driver = { | |
1186 | .owner = THIS_MODULE, | |
1187 | .name = "intel_mid_sound_card", | |
1188 | }, | |
1189 | .id_table = snd_intelmad_ids, | |
1190 | .probe = snd_intelmad_probe, | |
1191 | .remove = __devexit_p(snd_intelmad_remove), | |
1192 | }; | |
1193 | ||
1194 | /* | |
1195 | * alsa_card_intelmad_init- driver init function | |
1196 | * | |
1197 | * This function is called when driver module is inserted | |
1198 | */ | |
1199 | static int __init alsa_card_intelmad_init(void) | |
1200 | { | |
d0f40c50 | 1201 | pr_debug("mad_init called\n"); |
fffa1cca VK |
1202 | return platform_driver_register(&snd_intelmad_driver); |
1203 | } | |
1204 | ||
1205 | /** | |
1206 | * alsa_card_intelmad_exit- driver exit function | |
1207 | * | |
1208 | * This function is called when driver module is removed | |
1209 | */ | |
1210 | static void __exit alsa_card_intelmad_exit(void) | |
1211 | { | |
d0f40c50 | 1212 | pr_debug("mad_exit called\n"); |
fffa1cca VK |
1213 | return platform_driver_unregister(&snd_intelmad_driver); |
1214 | } | |
1215 | ||
1216 | module_init(alsa_card_intelmad_init) | |
1217 | module_exit(alsa_card_intelmad_exit) | |
1218 |