123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- /**
- * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
- *
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
- * @File ctpcm.c
- *
- * @Brief
- * This file contains the definition of the pcm device functions.
- *
- * @Author Liu Chun
- * @Date Apr 2 2008
- *
- */
- #include "ctpcm.h"
- #include "cttimer.h"
- #include <linux/slab.h>
- #include <sound/pcm.h>
- /* Hardware descriptions for playback */
- static const struct snd_pcm_hardware ct_pcm_playback_hw = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE),
- .formats = (SNDRV_PCM_FMTBIT_U8 |
- SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_3LE |
- SNDRV_PCM_FMTBIT_S32_LE |
- SNDRV_PCM_FMTBIT_FLOAT_LE),
- .rates = (SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_8000_192000),
- .rate_min = 8000,
- .rate_max = 192000,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = (64),
- .period_bytes_max = (128*1024),
- .periods_min = 2,
- .periods_max = 1024,
- .fifo_size = 0,
- };
- static const struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = (SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_32000),
- .rate_min = 32000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = (64),
- .period_bytes_max = (128*1024),
- .periods_min = 2,
- .periods_max = 1024,
- .fifo_size = 0,
- };
- /* Hardware descriptions for capture */
- static const struct snd_pcm_hardware ct_pcm_capture_hw = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = (SNDRV_PCM_FMTBIT_U8 |
- SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_3LE |
- SNDRV_PCM_FMTBIT_S32_LE |
- SNDRV_PCM_FMTBIT_FLOAT_LE),
- .rates = (SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_8000_96000),
- .rate_min = 8000,
- .rate_max = 96000,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = (384),
- .period_bytes_max = (64*1024),
- .periods_min = 2,
- .periods_max = 1024,
- .fifo_size = 0,
- };
- static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
- {
- struct ct_atc_pcm *apcm = atc_pcm;
- if (!apcm->substream)
- return;
- snd_pcm_period_elapsed(apcm->substream);
- }
- static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
- {
- struct ct_atc_pcm *apcm = runtime->private_data;
- struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
- atc->pcm_release_resources(atc, apcm);
- ct_timer_instance_free(apcm->timer);
- kfree(apcm);
- runtime->private_data = NULL;
- }
- /* pcm playback operations */
- static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
- {
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct ct_atc_pcm *apcm;
- int err;
- apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
- if (!apcm)
- return -ENOMEM;
- apcm->substream = substream;
- apcm->interrupt = ct_atc_pcm_interrupt;
- if (IEC958 == substream->pcm->device) {
- runtime->hw = ct_spdif_passthru_playback_hw;
- atc->spdif_out_passthru(atc, 1);
- } else {
- runtime->hw = ct_pcm_playback_hw;
- if (FRONT == substream->pcm->device)
- runtime->hw.channels_max = 8;
- }
- err = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0)
- goto free_pcm;
- err = snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- 1024, UINT_MAX);
- if (err < 0)
- goto free_pcm;
- apcm->timer = ct_timer_instance_new(atc->timer, apcm);
- if (!apcm->timer) {
- err = -ENOMEM;
- goto free_pcm;
- }
- runtime->private_data = apcm;
- runtime->private_free = ct_atc_pcm_free_substream;
- return 0;
- free_pcm:
- kfree(apcm);
- return err;
- }
- static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
- {
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- /* TODO: Notify mixer inactive. */
- if (IEC958 == substream->pcm->device)
- atc->spdif_out_passthru(atc, 0);
- /* The ct_atc_pcm object will be freed by runtime->private_free */
- return 0;
- }
- static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
- {
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct ct_atc_pcm *apcm = substream->runtime->private_data;
- int err;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
- /* clear previous resources */
- atc->pcm_release_resources(atc, apcm);
- return err;
- }
- static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
- {
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct ct_atc_pcm *apcm = substream->runtime->private_data;
- /* clear previous resources */
- atc->pcm_release_resources(atc, apcm);
- /* Free snd-allocated pages */
- return snd_pcm_lib_free_pages(substream);
- }
- static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
- {
- int err;
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct ct_atc_pcm *apcm = runtime->private_data;
- if (IEC958 == substream->pcm->device)
- err = atc->spdif_passthru_playback_prepare(atc, apcm);
- else
- err = atc->pcm_playback_prepare(atc, apcm);
- if (err < 0) {
- dev_err(atc->card->dev,
- "Preparing pcm playback failed!!!\n");
- return err;
- }
- return 0;
- }
- static int
- ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
- {
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct ct_atc_pcm *apcm = runtime->private_data;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- atc->pcm_playback_start(atc, apcm);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- atc->pcm_playback_stop(atc, apcm);
- break;
- default:
- break;
- }
- return 0;
- }
- static snd_pcm_uframes_t
- ct_pcm_playback_pointer(struct snd_pcm_substream *substream)
- {
- unsigned long position;
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct ct_atc_pcm *apcm = runtime->private_data;
- /* Read out playback position */
- position = atc->pcm_playback_position(atc, apcm);
- position = bytes_to_frames(runtime, position);
- if (position >= runtime->buffer_size)
- position = 0;
- return position;
- }
- /* pcm capture operations */
- static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
- {
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct ct_atc_pcm *apcm;
- int err;
- apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
- if (!apcm)
- return -ENOMEM;
- apcm->started = 0;
- apcm->substream = substream;
- apcm->interrupt = ct_atc_pcm_interrupt;
- runtime->hw = ct_pcm_capture_hw;
- runtime->hw.rate_max = atc->rsr * atc->msr;
- err = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0)
- goto free_pcm;
- err = snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- 1024, UINT_MAX);
- if (err < 0)
- goto free_pcm;
- apcm->timer = ct_timer_instance_new(atc->timer, apcm);
- if (!apcm->timer) {
- err = -ENOMEM;
- goto free_pcm;
- }
- runtime->private_data = apcm;
- runtime->private_free = ct_atc_pcm_free_substream;
- return 0;
- free_pcm:
- kfree(apcm);
- return err;
- }
- static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
- {
- /* The ct_atc_pcm object will be freed by runtime->private_free */
- /* TODO: Notify mixer inactive. */
- return 0;
- }
- static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
- {
- int err;
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct ct_atc_pcm *apcm = runtime->private_data;
- err = atc->pcm_capture_prepare(atc, apcm);
- if (err < 0) {
- dev_err(atc->card->dev,
- "Preparing pcm capture failed!!!\n");
- return err;
- }
- return 0;
- }
- static int
- ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
- {
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct ct_atc_pcm *apcm = runtime->private_data;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- atc->pcm_capture_start(atc, apcm);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- atc->pcm_capture_stop(atc, apcm);
- break;
- default:
- atc->pcm_capture_stop(atc, apcm);
- break;
- }
- return 0;
- }
- static snd_pcm_uframes_t
- ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
- {
- unsigned long position;
- struct ct_atc *atc = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct ct_atc_pcm *apcm = runtime->private_data;
- /* Read out playback position */
- position = atc->pcm_capture_position(atc, apcm);
- position = bytes_to_frames(runtime, position);
- if (position >= runtime->buffer_size)
- position = 0;
- return position;
- }
- /* PCM operators for playback */
- static const struct snd_pcm_ops ct_pcm_playback_ops = {
- .open = ct_pcm_playback_open,
- .close = ct_pcm_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = ct_pcm_hw_params,
- .hw_free = ct_pcm_hw_free,
- .prepare = ct_pcm_playback_prepare,
- .trigger = ct_pcm_playback_trigger,
- .pointer = ct_pcm_playback_pointer,
- .page = snd_pcm_sgbuf_ops_page,
- };
- /* PCM operators for capture */
- static const struct snd_pcm_ops ct_pcm_capture_ops = {
- .open = ct_pcm_capture_open,
- .close = ct_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = ct_pcm_hw_params,
- .hw_free = ct_pcm_hw_free,
- .prepare = ct_pcm_capture_prepare,
- .trigger = ct_pcm_capture_trigger,
- .pointer = ct_pcm_capture_pointer,
- .page = snd_pcm_sgbuf_ops_page,
- };
- static const struct snd_pcm_chmap_elem surround_map[] = {
- { .channels = 1,
- .map = { SNDRV_CHMAP_MONO } },
- { .channels = 2,
- .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
- { }
- };
- static const struct snd_pcm_chmap_elem clfe_map[] = {
- { .channels = 1,
- .map = { SNDRV_CHMAP_MONO } },
- { .channels = 2,
- .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
- { }
- };
- static const struct snd_pcm_chmap_elem side_map[] = {
- { .channels = 1,
- .map = { SNDRV_CHMAP_MONO } },
- { .channels = 2,
- .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
- { }
- };
- /* Create ALSA pcm device */
- int ct_alsa_pcm_create(struct ct_atc *atc,
- enum CTALSADEVS device,
- const char *device_name)
- {
- struct snd_pcm *pcm;
- const struct snd_pcm_chmap_elem *map;
- int chs;
- int err;
- int playback_count, capture_count;
- playback_count = (IEC958 == device) ? 1 : 256;
- capture_count = (FRONT == device) ? 1 : 0;
- err = snd_pcm_new(atc->card, "ctxfi", device,
- playback_count, capture_count, &pcm);
- if (err < 0) {
- dev_err(atc->card->dev, "snd_pcm_new failed!! Err=%d\n",
- err);
- return err;
- }
- pcm->private_data = atc;
- pcm->info_flags = 0;
- pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strlcpy(pcm->name, device_name, sizeof(pcm->name));
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
- if (FRONT == device)
- snd_pcm_set_ops(pcm,
- SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
- chs = 2;
- switch (device) {
- case FRONT:
- chs = 8;
- map = snd_pcm_std_chmaps;
- break;
- case SURROUND:
- map = surround_map;
- break;
- case CLFE:
- map = clfe_map;
- break;
- case SIDE:
- map = side_map;
- break;
- default:
- map = snd_pcm_std_chmaps;
- break;
- }
- err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs,
- 0, NULL);
- if (err < 0)
- return err;
- #ifdef CONFIG_PM_SLEEP
- atc->pcms[device] = pcm;
- #endif
- return 0;
- }
|