blob: 85ab43e89212c6724c1e70f3cad3de33e9816935 [file] [log] [blame]
Wai Yew CHAY8cc72362009-05-14 08:05:58 +02001/**
2 * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
3 *
4 * This source file is released under GPL v2 license (no other versions).
5 * See the COPYING file included in the main directory of this source
6 * distribution for the license terms and conditions.
7 *
8 * @File ctpcm.c
9 *
10 * @Brief
11 * This file contains the definition of the pcm device functions.
12 *
13 * @Author Liu Chun
14 * @Date Apr 2 2008
15 *
16 */
17
18#include "ctpcm.h"
Takashi Iwaib7bbf872009-06-05 16:11:07 +020019#include "cttimer.h"
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090020#include <linux/slab.h>
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020021#include <sound/pcm.h>
22
23/* Hardware descriptions for playback */
24static struct snd_pcm_hardware ct_pcm_playback_hw = {
25 .info = (SNDRV_PCM_INFO_MMAP |
26 SNDRV_PCM_INFO_INTERLEAVED |
27 SNDRV_PCM_INFO_BLOCK_TRANSFER |
28 SNDRV_PCM_INFO_MMAP_VALID |
29 SNDRV_PCM_INFO_PAUSE),
30 .formats = (SNDRV_PCM_FMTBIT_U8 |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020031 SNDRV_PCM_FMTBIT_S16_LE |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020032 SNDRV_PCM_FMTBIT_S24_3LE |
Takashi Iwaid2b9b962009-06-02 14:39:05 +020033 SNDRV_PCM_FMTBIT_S32_LE |
34 SNDRV_PCM_FMTBIT_FLOAT_LE),
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020035 .rates = (SNDRV_PCM_RATE_CONTINUOUS |
36 SNDRV_PCM_RATE_8000_192000),
37 .rate_min = 8000,
38 .rate_max = 192000,
39 .channels_min = 1,
40 .channels_max = 2,
41 .buffer_bytes_max = (128*1024),
42 .period_bytes_min = (64),
43 .period_bytes_max = (128*1024),
Takashi Iwai775ffa12009-06-05 16:12:16 +020044 .periods_min = 2,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020045 .periods_max = 1024,
46 .fifo_size = 0,
47};
48
49static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
50 .info = (SNDRV_PCM_INFO_MMAP |
51 SNDRV_PCM_INFO_INTERLEAVED |
52 SNDRV_PCM_INFO_BLOCK_TRANSFER |
53 SNDRV_PCM_INFO_MMAP_VALID |
54 SNDRV_PCM_INFO_PAUSE),
Takashi Iwaid2b9b962009-06-02 14:39:05 +020055 .formats = SNDRV_PCM_FMTBIT_S16_LE,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020056 .rates = (SNDRV_PCM_RATE_48000 |
57 SNDRV_PCM_RATE_44100 |
58 SNDRV_PCM_RATE_32000),
59 .rate_min = 32000,
60 .rate_max = 48000,
61 .channels_min = 2,
62 .channels_max = 2,
63 .buffer_bytes_max = (128*1024),
64 .period_bytes_min = (64),
65 .period_bytes_max = (128*1024),
Takashi Iwai775ffa12009-06-05 16:12:16 +020066 .periods_min = 2,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020067 .periods_max = 1024,
68 .fifo_size = 0,
69};
70
71/* Hardware descriptions for capture */
72static struct snd_pcm_hardware ct_pcm_capture_hw = {
73 .info = (SNDRV_PCM_INFO_MMAP |
74 SNDRV_PCM_INFO_INTERLEAVED |
75 SNDRV_PCM_INFO_BLOCK_TRANSFER |
76 SNDRV_PCM_INFO_PAUSE |
77 SNDRV_PCM_INFO_MMAP_VALID),
78 .formats = (SNDRV_PCM_FMTBIT_U8 |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020079 SNDRV_PCM_FMTBIT_S16_LE |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020080 SNDRV_PCM_FMTBIT_S24_3LE |
Takashi Iwaid2b9b962009-06-02 14:39:05 +020081 SNDRV_PCM_FMTBIT_S32_LE |
82 SNDRV_PCM_FMTBIT_FLOAT_LE),
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020083 .rates = (SNDRV_PCM_RATE_CONTINUOUS |
84 SNDRV_PCM_RATE_8000_96000),
85 .rate_min = 8000,
86 .rate_max = 96000,
87 .channels_min = 1,
88 .channels_max = 2,
89 .buffer_bytes_max = (128*1024),
90 .period_bytes_min = (384),
91 .period_bytes_max = (64*1024),
92 .periods_min = 2,
93 .periods_max = 1024,
94 .fifo_size = 0,
95};
96
97static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
98{
99 struct ct_atc_pcm *apcm = atc_pcm;
100
Takashi Iwai35ebf6e2009-07-22 17:12:34 +0200101 if (!apcm->substream)
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200102 return;
103
104 snd_pcm_period_elapsed(apcm->substream);
105}
106
107static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
108{
109 struct ct_atc_pcm *apcm = runtime->private_data;
110 struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
111
112 atc->pcm_release_resources(atc, apcm);
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200113 ct_timer_instance_free(apcm->timer);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200114 kfree(apcm);
115 runtime->private_data = NULL;
116}
117
118/* pcm playback operations */
119static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
120{
121 struct ct_atc *atc = snd_pcm_substream_chip(substream);
122 struct snd_pcm_runtime *runtime = substream->runtime;
123 struct ct_atc_pcm *apcm;
124 int err;
125
126 apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
Takashi Iwai35ebf6e2009-07-22 17:12:34 +0200127 if (!apcm)
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200128 return -ENOMEM;
129
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200130 apcm->substream = substream;
131 apcm->interrupt = ct_atc_pcm_interrupt;
132 runtime->private_data = apcm;
133 runtime->private_free = ct_atc_pcm_free_substream;
134 if (IEC958 == substream->pcm->device) {
135 runtime->hw = ct_spdif_passthru_playback_hw;
136 atc->spdif_out_passthru(atc, 1);
137 } else {
138 runtime->hw = ct_pcm_playback_hw;
139 if (FRONT == substream->pcm->device)
140 runtime->hw.channels_max = 8;
141 }
142
143 err = snd_pcm_hw_constraint_integer(runtime,
144 SNDRV_PCM_HW_PARAM_PERIODS);
145 if (err < 0) {
146 kfree(apcm);
147 return err;
148 }
149 err = snd_pcm_hw_constraint_minmax(runtime,
150 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
151 1024, UINT_MAX);
152 if (err < 0) {
153 kfree(apcm);
154 return err;
155 }
156
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200157 apcm->timer = ct_timer_instance_new(atc->timer, apcm);
158 if (!apcm->timer)
159 return -ENOMEM;
160
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200161 return 0;
162}
163
164static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
165{
166 struct ct_atc *atc = snd_pcm_substream_chip(substream);
167
168 /* TODO: Notify mixer inactive. */
169 if (IEC958 == substream->pcm->device)
170 atc->spdif_out_passthru(atc, 0);
171
172 /* The ct_atc_pcm object will be freed by runtime->private_free */
173
174 return 0;
175}
176
177static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
178 struct snd_pcm_hw_params *hw_params)
179{
Takashi Iwaia5990dc2009-06-09 08:19:02 +0200180 struct ct_atc *atc = snd_pcm_substream_chip(substream);
181 struct ct_atc_pcm *apcm = substream->runtime->private_data;
182 int err;
183
184 err = snd_pcm_lib_malloc_pages(substream,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200185 params_buffer_bytes(hw_params));
Takashi Iwaia5990dc2009-06-09 08:19:02 +0200186 if (err < 0)
187 return err;
188 /* clear previous resources */
189 atc->pcm_release_resources(atc, apcm);
190 return err;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200191}
192
193static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
194{
Takashi Iwaia5990dc2009-06-09 08:19:02 +0200195 struct ct_atc *atc = snd_pcm_substream_chip(substream);
196 struct ct_atc_pcm *apcm = substream->runtime->private_data;
197
198 /* clear previous resources */
199 atc->pcm_release_resources(atc, apcm);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200200 /* Free snd-allocated pages */
201 return snd_pcm_lib_free_pages(substream);
202}
203
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200204
205static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
206{
207 int err;
208 struct ct_atc *atc = snd_pcm_substream_chip(substream);
209 struct snd_pcm_runtime *runtime = substream->runtime;
210 struct ct_atc_pcm *apcm = runtime->private_data;
211
212 if (IEC958 == substream->pcm->device)
213 err = atc->spdif_passthru_playback_prepare(atc, apcm);
214 else
215 err = atc->pcm_playback_prepare(atc, apcm);
216
217 if (err < 0) {
Takashi Iwaib3e0afe2009-05-14 15:19:30 +0200218 printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n");
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200219 return err;
220 }
221
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200222 return 0;
223}
224
225static int
226ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
227{
228 struct ct_atc *atc = snd_pcm_substream_chip(substream);
229 struct snd_pcm_runtime *runtime = substream->runtime;
230 struct ct_atc_pcm *apcm = runtime->private_data;
231
232 switch (cmd) {
233 case SNDRV_PCM_TRIGGER_START:
234 case SNDRV_PCM_TRIGGER_RESUME:
235 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
236 atc->pcm_playback_start(atc, apcm);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200237 break;
238 case SNDRV_PCM_TRIGGER_STOP:
239 case SNDRV_PCM_TRIGGER_SUSPEND:
240 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200241 atc->pcm_playback_stop(atc, apcm);
242 break;
243 default:
244 break;
245 }
246
247 return 0;
248}
249
250static snd_pcm_uframes_t
251ct_pcm_playback_pointer(struct snd_pcm_substream *substream)
252{
253 unsigned long position;
254 struct ct_atc *atc = snd_pcm_substream_chip(substream);
255 struct snd_pcm_runtime *runtime = substream->runtime;
256 struct ct_atc_pcm *apcm = runtime->private_data;
257
258 /* Read out playback position */
259 position = atc->pcm_playback_position(atc, apcm);
260 position = bytes_to_frames(runtime, position);
Takashi Iwaiaf8500b2009-06-08 15:07:46 +0200261 if (position >= runtime->buffer_size)
262 position = 0;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200263 return position;
264}
265
266/* pcm capture operations */
267static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
268{
269 struct ct_atc *atc = snd_pcm_substream_chip(substream);
270 struct snd_pcm_runtime *runtime = substream->runtime;
271 struct ct_atc_pcm *apcm;
272 int err;
273
274 apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
Takashi Iwai35ebf6e2009-07-22 17:12:34 +0200275 if (!apcm)
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200276 return -ENOMEM;
277
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200278 apcm->started = 0;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200279 apcm->substream = substream;
280 apcm->interrupt = ct_atc_pcm_interrupt;
281 runtime->private_data = apcm;
282 runtime->private_free = ct_atc_pcm_free_substream;
283 runtime->hw = ct_pcm_capture_hw;
284 runtime->hw.rate_max = atc->rsr * atc->msr;
285
286 err = snd_pcm_hw_constraint_integer(runtime,
287 SNDRV_PCM_HW_PARAM_PERIODS);
288 if (err < 0) {
289 kfree(apcm);
290 return err;
291 }
292 err = snd_pcm_hw_constraint_minmax(runtime,
293 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
294 1024, UINT_MAX);
295 if (err < 0) {
296 kfree(apcm);
297 return err;
298 }
299
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200300 apcm->timer = ct_timer_instance_new(atc->timer, apcm);
301 if (!apcm->timer)
302 return -ENOMEM;
303
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200304 return 0;
305}
306
307static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
308{
309 /* The ct_atc_pcm object will be freed by runtime->private_free */
310 /* TODO: Notify mixer inactive. */
311 return 0;
312}
313
314static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
315{
316 int err;
317 struct ct_atc *atc = snd_pcm_substream_chip(substream);
318 struct snd_pcm_runtime *runtime = substream->runtime;
319 struct ct_atc_pcm *apcm = runtime->private_data;
320
321 err = atc->pcm_capture_prepare(atc, apcm);
322 if (err < 0) {
Takashi Iwaib3e0afe2009-05-14 15:19:30 +0200323 printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n");
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200324 return err;
325 }
326
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200327 return 0;
328}
329
330static int
331ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
332{
333 struct ct_atc *atc = snd_pcm_substream_chip(substream);
334 struct snd_pcm_runtime *runtime = substream->runtime;
335 struct ct_atc_pcm *apcm = runtime->private_data;
336
337 switch (cmd) {
338 case SNDRV_PCM_TRIGGER_START:
339 atc->pcm_capture_start(atc, apcm);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200340 break;
341 case SNDRV_PCM_TRIGGER_STOP:
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200342 atc->pcm_capture_stop(atc, apcm);
343 break;
344 default:
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200345 atc->pcm_capture_stop(atc, apcm);
346 break;
347 }
348
349 return 0;
350}
351
352static snd_pcm_uframes_t
353ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
354{
355 unsigned long position;
356 struct ct_atc *atc = snd_pcm_substream_chip(substream);
357 struct snd_pcm_runtime *runtime = substream->runtime;
358 struct ct_atc_pcm *apcm = runtime->private_data;
359
360 /* Read out playback position */
361 position = atc->pcm_capture_position(atc, apcm);
362 position = bytes_to_frames(runtime, position);
Takashi Iwaiaf8500b2009-06-08 15:07:46 +0200363 if (position >= runtime->buffer_size)
364 position = 0;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200365 return position;
366}
367
368/* PCM operators for playback */
369static struct snd_pcm_ops ct_pcm_playback_ops = {
370 .open = ct_pcm_playback_open,
371 .close = ct_pcm_playback_close,
372 .ioctl = snd_pcm_lib_ioctl,
373 .hw_params = ct_pcm_hw_params,
374 .hw_free = ct_pcm_hw_free,
375 .prepare = ct_pcm_playback_prepare,
376 .trigger = ct_pcm_playback_trigger,
377 .pointer = ct_pcm_playback_pointer,
Takashi Iwaic76157d2009-06-02 15:26:19 +0200378 .page = snd_pcm_sgbuf_ops_page,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200379};
380
381/* PCM operators for capture */
382static struct snd_pcm_ops ct_pcm_capture_ops = {
383 .open = ct_pcm_capture_open,
384 .close = ct_pcm_capture_close,
385 .ioctl = snd_pcm_lib_ioctl,
386 .hw_params = ct_pcm_hw_params,
387 .hw_free = ct_pcm_hw_free,
388 .prepare = ct_pcm_capture_prepare,
389 .trigger = ct_pcm_capture_trigger,
390 .pointer = ct_pcm_capture_pointer,
Takashi Iwaic76157d2009-06-02 15:26:19 +0200391 .page = snd_pcm_sgbuf_ops_page,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200392};
393
394/* Create ALSA pcm device */
395int ct_alsa_pcm_create(struct ct_atc *atc,
396 enum CTALSADEVS device,
397 const char *device_name)
398{
399 struct snd_pcm *pcm;
400 int err;
401 int playback_count, capture_count;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200402
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200403 playback_count = (IEC958 == device) ? 1 : 8;
404 capture_count = (FRONT == device) ? 1 : 0;
Takashi Iwai8372d4982009-06-02 14:27:56 +0200405 err = snd_pcm_new(atc->card, "ctxfi", device,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200406 playback_count, capture_count, &pcm);
407 if (err < 0) {
Takashi Iwaib3e0afe2009-05-14 15:19:30 +0200408 printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200409 return err;
410 }
411
412 pcm->private_data = atc;
413 pcm->info_flags = 0;
414 pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
Takashi Iwai8372d4982009-06-02 14:27:56 +0200415 strlcpy(pcm->name, device_name, sizeof(pcm->name));
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200416
417 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
418
419 if (FRONT == device)
420 snd_pcm_set_ops(pcm,
421 SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
422
Takashi Iwaic76157d2009-06-02 15:26:19 +0200423 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200424 snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
425
Wai Yew CHAY29959a02009-06-22 14:52:34 +0200426#ifdef CONFIG_PM
427 atc->pcms[device] = pcm;
428#endif
429
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200430 return 0;
431}