blob: 870fa170f046ab632abe9cd2ee40562f87b69b43 [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"
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020020#include <sound/pcm.h>
21
22/* Hardware descriptions for playback */
23static struct snd_pcm_hardware ct_pcm_playback_hw = {
24 .info = (SNDRV_PCM_INFO_MMAP |
25 SNDRV_PCM_INFO_INTERLEAVED |
26 SNDRV_PCM_INFO_BLOCK_TRANSFER |
27 SNDRV_PCM_INFO_MMAP_VALID |
28 SNDRV_PCM_INFO_PAUSE),
29 .formats = (SNDRV_PCM_FMTBIT_U8 |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020030 SNDRV_PCM_FMTBIT_S16_LE |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020031 SNDRV_PCM_FMTBIT_S24_3LE |
Takashi Iwaid2b9b962009-06-02 14:39:05 +020032 SNDRV_PCM_FMTBIT_S32_LE |
33 SNDRV_PCM_FMTBIT_FLOAT_LE),
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020034 .rates = (SNDRV_PCM_RATE_CONTINUOUS |
35 SNDRV_PCM_RATE_8000_192000),
36 .rate_min = 8000,
37 .rate_max = 192000,
38 .channels_min = 1,
39 .channels_max = 2,
40 .buffer_bytes_max = (128*1024),
41 .period_bytes_min = (64),
42 .period_bytes_max = (128*1024),
Takashi Iwai775ffa12009-06-05 16:12:16 +020043 .periods_min = 2,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020044 .periods_max = 1024,
45 .fifo_size = 0,
46};
47
48static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
49 .info = (SNDRV_PCM_INFO_MMAP |
50 SNDRV_PCM_INFO_INTERLEAVED |
51 SNDRV_PCM_INFO_BLOCK_TRANSFER |
52 SNDRV_PCM_INFO_MMAP_VALID |
53 SNDRV_PCM_INFO_PAUSE),
Takashi Iwaid2b9b962009-06-02 14:39:05 +020054 .formats = SNDRV_PCM_FMTBIT_S16_LE,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020055 .rates = (SNDRV_PCM_RATE_48000 |
56 SNDRV_PCM_RATE_44100 |
57 SNDRV_PCM_RATE_32000),
58 .rate_min = 32000,
59 .rate_max = 48000,
60 .channels_min = 2,
61 .channels_max = 2,
62 .buffer_bytes_max = (128*1024),
63 .period_bytes_min = (64),
64 .period_bytes_max = (128*1024),
Takashi Iwai775ffa12009-06-05 16:12:16 +020065 .periods_min = 2,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020066 .periods_max = 1024,
67 .fifo_size = 0,
68};
69
70/* Hardware descriptions for capture */
71static struct snd_pcm_hardware ct_pcm_capture_hw = {
72 .info = (SNDRV_PCM_INFO_MMAP |
73 SNDRV_PCM_INFO_INTERLEAVED |
74 SNDRV_PCM_INFO_BLOCK_TRANSFER |
75 SNDRV_PCM_INFO_PAUSE |
76 SNDRV_PCM_INFO_MMAP_VALID),
77 .formats = (SNDRV_PCM_FMTBIT_U8 |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020078 SNDRV_PCM_FMTBIT_S16_LE |
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020079 SNDRV_PCM_FMTBIT_S24_3LE |
Takashi Iwaid2b9b962009-06-02 14:39:05 +020080 SNDRV_PCM_FMTBIT_S32_LE |
81 SNDRV_PCM_FMTBIT_FLOAT_LE),
Wai Yew CHAY8cc72362009-05-14 08:05:58 +020082 .rates = (SNDRV_PCM_RATE_CONTINUOUS |
83 SNDRV_PCM_RATE_8000_96000),
84 .rate_min = 8000,
85 .rate_max = 96000,
86 .channels_min = 1,
87 .channels_max = 2,
88 .buffer_bytes_max = (128*1024),
89 .period_bytes_min = (384),
90 .period_bytes_max = (64*1024),
91 .periods_min = 2,
92 .periods_max = 1024,
93 .fifo_size = 0,
94};
95
96static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
97{
98 struct ct_atc_pcm *apcm = atc_pcm;
99
100 if (NULL == apcm->substream)
101 return;
102
103 snd_pcm_period_elapsed(apcm->substream);
104}
105
106static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
107{
108 struct ct_atc_pcm *apcm = runtime->private_data;
109 struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
110
111 atc->pcm_release_resources(atc, apcm);
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200112 ct_timer_instance_free(apcm->timer);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200113 kfree(apcm);
114 runtime->private_data = NULL;
115}
116
117/* pcm playback operations */
118static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
119{
120 struct ct_atc *atc = snd_pcm_substream_chip(substream);
121 struct snd_pcm_runtime *runtime = substream->runtime;
122 struct ct_atc_pcm *apcm;
123 int err;
124
125 apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
126 if (NULL == apcm)
127 return -ENOMEM;
128
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200129 apcm->substream = substream;
130 apcm->interrupt = ct_atc_pcm_interrupt;
131 runtime->private_data = apcm;
132 runtime->private_free = ct_atc_pcm_free_substream;
133 if (IEC958 == substream->pcm->device) {
134 runtime->hw = ct_spdif_passthru_playback_hw;
135 atc->spdif_out_passthru(atc, 1);
136 } else {
137 runtime->hw = ct_pcm_playback_hw;
138 if (FRONT == substream->pcm->device)
139 runtime->hw.channels_max = 8;
140 }
141
142 err = snd_pcm_hw_constraint_integer(runtime,
143 SNDRV_PCM_HW_PARAM_PERIODS);
144 if (err < 0) {
145 kfree(apcm);
146 return err;
147 }
148 err = snd_pcm_hw_constraint_minmax(runtime,
149 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
150 1024, UINT_MAX);
151 if (err < 0) {
152 kfree(apcm);
153 return err;
154 }
155
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200156 apcm->timer = ct_timer_instance_new(atc->timer, apcm);
157 if (!apcm->timer)
158 return -ENOMEM;
159
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200160 return 0;
161}
162
163static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
164{
165 struct ct_atc *atc = snd_pcm_substream_chip(substream);
166
167 /* TODO: Notify mixer inactive. */
168 if (IEC958 == substream->pcm->device)
169 atc->spdif_out_passthru(atc, 0);
170
171 /* The ct_atc_pcm object will be freed by runtime->private_free */
172
173 return 0;
174}
175
176static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
177 struct snd_pcm_hw_params *hw_params)
178{
179 return snd_pcm_lib_malloc_pages(substream,
180 params_buffer_bytes(hw_params));
181}
182
183static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
184{
185 /* Free snd-allocated pages */
186 return snd_pcm_lib_free_pages(substream);
187}
188
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200189
190static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
191{
192 int err;
193 struct ct_atc *atc = snd_pcm_substream_chip(substream);
194 struct snd_pcm_runtime *runtime = substream->runtime;
195 struct ct_atc_pcm *apcm = runtime->private_data;
196
197 if (IEC958 == substream->pcm->device)
198 err = atc->spdif_passthru_playback_prepare(atc, apcm);
199 else
200 err = atc->pcm_playback_prepare(atc, apcm);
201
202 if (err < 0) {
Takashi Iwaib3e0afe2009-05-14 15:19:30 +0200203 printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n");
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200204 return err;
205 }
206
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200207 return 0;
208}
209
210static int
211ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
212{
213 struct ct_atc *atc = snd_pcm_substream_chip(substream);
214 struct snd_pcm_runtime *runtime = substream->runtime;
215 struct ct_atc_pcm *apcm = runtime->private_data;
216
217 switch (cmd) {
218 case SNDRV_PCM_TRIGGER_START:
219 case SNDRV_PCM_TRIGGER_RESUME:
220 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
221 atc->pcm_playback_start(atc, apcm);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200222 break;
223 case SNDRV_PCM_TRIGGER_STOP:
224 case SNDRV_PCM_TRIGGER_SUSPEND:
225 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200226 atc->pcm_playback_stop(atc, apcm);
227 break;
228 default:
229 break;
230 }
231
232 return 0;
233}
234
235static snd_pcm_uframes_t
236ct_pcm_playback_pointer(struct snd_pcm_substream *substream)
237{
238 unsigned long position;
239 struct ct_atc *atc = snd_pcm_substream_chip(substream);
240 struct snd_pcm_runtime *runtime = substream->runtime;
241 struct ct_atc_pcm *apcm = runtime->private_data;
242
243 /* Read out playback position */
244 position = atc->pcm_playback_position(atc, apcm);
245 position = bytes_to_frames(runtime, position);
Takashi Iwaiaf8500b2009-06-08 15:07:46 +0200246 if (position >= runtime->buffer_size)
247 position = 0;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200248 return position;
249}
250
251/* pcm capture operations */
252static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
253{
254 struct ct_atc *atc = snd_pcm_substream_chip(substream);
255 struct snd_pcm_runtime *runtime = substream->runtime;
256 struct ct_atc_pcm *apcm;
257 int err;
258
259 apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
260 if (NULL == apcm)
261 return -ENOMEM;
262
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200263 apcm->started = 0;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200264 apcm->substream = substream;
265 apcm->interrupt = ct_atc_pcm_interrupt;
266 runtime->private_data = apcm;
267 runtime->private_free = ct_atc_pcm_free_substream;
268 runtime->hw = ct_pcm_capture_hw;
269 runtime->hw.rate_max = atc->rsr * atc->msr;
270
271 err = snd_pcm_hw_constraint_integer(runtime,
272 SNDRV_PCM_HW_PARAM_PERIODS);
273 if (err < 0) {
274 kfree(apcm);
275 return err;
276 }
277 err = snd_pcm_hw_constraint_minmax(runtime,
278 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
279 1024, UINT_MAX);
280 if (err < 0) {
281 kfree(apcm);
282 return err;
283 }
284
Takashi Iwaib7bbf872009-06-05 16:11:07 +0200285 apcm->timer = ct_timer_instance_new(atc->timer, apcm);
286 if (!apcm->timer)
287 return -ENOMEM;
288
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200289 return 0;
290}
291
292static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
293{
294 /* The ct_atc_pcm object will be freed by runtime->private_free */
295 /* TODO: Notify mixer inactive. */
296 return 0;
297}
298
299static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
300{
301 int err;
302 struct ct_atc *atc = snd_pcm_substream_chip(substream);
303 struct snd_pcm_runtime *runtime = substream->runtime;
304 struct ct_atc_pcm *apcm = runtime->private_data;
305
306 err = atc->pcm_capture_prepare(atc, apcm);
307 if (err < 0) {
Takashi Iwaib3e0afe2009-05-14 15:19:30 +0200308 printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n");
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200309 return err;
310 }
311
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200312 return 0;
313}
314
315static int
316ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
317{
318 struct ct_atc *atc = snd_pcm_substream_chip(substream);
319 struct snd_pcm_runtime *runtime = substream->runtime;
320 struct ct_atc_pcm *apcm = runtime->private_data;
321
322 switch (cmd) {
323 case SNDRV_PCM_TRIGGER_START:
324 atc->pcm_capture_start(atc, apcm);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200325 break;
326 case SNDRV_PCM_TRIGGER_STOP:
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200327 atc->pcm_capture_stop(atc, apcm);
328 break;
329 default:
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200330 atc->pcm_capture_stop(atc, apcm);
331 break;
332 }
333
334 return 0;
335}
336
337static snd_pcm_uframes_t
338ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
339{
340 unsigned long position;
341 struct ct_atc *atc = snd_pcm_substream_chip(substream);
342 struct snd_pcm_runtime *runtime = substream->runtime;
343 struct ct_atc_pcm *apcm = runtime->private_data;
344
345 /* Read out playback position */
346 position = atc->pcm_capture_position(atc, apcm);
347 position = bytes_to_frames(runtime, position);
Takashi Iwaiaf8500b2009-06-08 15:07:46 +0200348 if (position >= runtime->buffer_size)
349 position = 0;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200350 return position;
351}
352
353/* PCM operators for playback */
354static struct snd_pcm_ops ct_pcm_playback_ops = {
355 .open = ct_pcm_playback_open,
356 .close = ct_pcm_playback_close,
357 .ioctl = snd_pcm_lib_ioctl,
358 .hw_params = ct_pcm_hw_params,
359 .hw_free = ct_pcm_hw_free,
360 .prepare = ct_pcm_playback_prepare,
361 .trigger = ct_pcm_playback_trigger,
362 .pointer = ct_pcm_playback_pointer,
Takashi Iwaic76157d2009-06-02 15:26:19 +0200363 .page = snd_pcm_sgbuf_ops_page,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200364};
365
366/* PCM operators for capture */
367static struct snd_pcm_ops ct_pcm_capture_ops = {
368 .open = ct_pcm_capture_open,
369 .close = ct_pcm_capture_close,
370 .ioctl = snd_pcm_lib_ioctl,
371 .hw_params = ct_pcm_hw_params,
372 .hw_free = ct_pcm_hw_free,
373 .prepare = ct_pcm_capture_prepare,
374 .trigger = ct_pcm_capture_trigger,
375 .pointer = ct_pcm_capture_pointer,
Takashi Iwaic76157d2009-06-02 15:26:19 +0200376 .page = snd_pcm_sgbuf_ops_page,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200377};
378
379/* Create ALSA pcm device */
380int ct_alsa_pcm_create(struct ct_atc *atc,
381 enum CTALSADEVS device,
382 const char *device_name)
383{
384 struct snd_pcm *pcm;
385 int err;
386 int playback_count, capture_count;
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200387
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200388 playback_count = (IEC958 == device) ? 1 : 8;
389 capture_count = (FRONT == device) ? 1 : 0;
Takashi Iwai8372d4982009-06-02 14:27:56 +0200390 err = snd_pcm_new(atc->card, "ctxfi", device,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200391 playback_count, capture_count, &pcm);
392 if (err < 0) {
Takashi Iwaib3e0afe2009-05-14 15:19:30 +0200393 printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err);
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200394 return err;
395 }
396
397 pcm->private_data = atc;
398 pcm->info_flags = 0;
399 pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
Takashi Iwai8372d4982009-06-02 14:27:56 +0200400 strlcpy(pcm->name, device_name, sizeof(pcm->name));
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200401
402 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
403
404 if (FRONT == device)
405 snd_pcm_set_ops(pcm,
406 SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
407
Takashi Iwaic76157d2009-06-02 15:26:19 +0200408 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
Wai Yew CHAY8cc72362009-05-14 08:05:58 +0200409 snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
410
411 return 0;
412}