From patchwork Sun May 18 13:36:37 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Takashi Sakamoto X-Patchwork-Id: 4198051 X-Patchwork-Delegate: tiwai@suse.de Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id A1B929F1CD for ; Sun, 18 May 2014 13:43:51 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 89B5920127 for ; Sun, 18 May 2014 13:43:50 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id 051A920274 for ; Sun, 18 May 2014 13:43:49 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 180182654D9; Sun, 18 May 2014 15:43:48 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id 9A3A4264F22; Sun, 18 May 2014 15:37:47 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 0E3B5264F34; Sun, 18 May 2014 15:37:46 +0200 (CEST) Received: from smtp302.phy.lolipop.jp (smtp302.phy.lolipop.jp [210.157.22.85]) by alsa0.perex.cz (Postfix) with ESMTP id BFAF1264F22 for ; Sun, 18 May 2014 15:37:01 +0200 (CEST) Received: from smtp302.phy.lolipop.lan (HELO smtp302.phy.lolipop.jp) (172.17.1.85) (smtp-auth username m12129643-o-takashi, mechanism plain) by smtp302.phy.lolipop.jp (qpsmtpd/0.82) with ESMTPA; Sun, 18 May 2014 22:37:01 +0900 Received: from 127.0.0.1 (127.0.0.1) by smtp302.phy.lolipop.jp (LOLIPOP-Fsecure); Sun, 18 May 2014 22:36:39 +0900 (JST) X-Virus-Status: clean(LOLIPOP-Fsecure) From: Takashi Sakamoto To: clemens@ladisch.de Date: Sun, 18 May 2014 22:36:37 +0900 Message-Id: <1400420198-24312-13-git-send-email-o-takashi@sakamocchi.jp> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1400420198-24312-1-git-send-email-o-takashi@sakamocchi.jp> References: <1400420198-24312-1-git-send-email-o-takashi@sakamocchi.jp> Cc: alsa-devel@alsa-project.org, ffado-devel@lists.sf.net Subject: [alsa-devel] [PATCH 12/13] dice: Add support for PCM capture X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP This commit adds a support for PCM capture. When source of clock is external or opposite PCM substream is already running, available sampling rate is limited at current one. This driver give no way to change source of clock because it can be implemented in user-land. Signed-off-by: Takashi Sakamoto --- sound/firewire/Kconfig | 3 - sound/firewire/dice/dice_pcm.c | 158 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 148 insertions(+), 13 deletions(-) diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 92c92c6..a240a70 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -20,9 +20,6 @@ config SND_DICE Say Y here to include support for many DACs based on the DICE chip family (DICE-II/Jr/Mini) from TC Applied Technologies. - At the moment, this driver supports playback only. If you - want to use devices that support capturing, use FFADO instead. - To compile this driver as a module, choose M here: the module will be called snd-dice. diff --git a/sound/firewire/dice/dice_pcm.c b/sound/firewire/dice/dice_pcm.c index 414c1c6..4c2a5c8 100644 --- a/sound/firewire/dice/dice_pcm.c +++ b/sound/firewire/dice/dice_pcm.c @@ -12,7 +12,8 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_dice *dice = rule->private; + struct snd_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data; const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); @@ -21,7 +22,12 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_interval rates = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + unsigned int i, rate, mode, *pcm_channels; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_channels; + else + pcm_channels = dice->rx_channels; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; @@ -41,7 +47,8 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_dice *dice = rule->private; + struct snd_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data; const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); @@ -50,7 +57,12 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_interval channels = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + unsigned int i, rate, mode, *pcm_channels; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_channels; + else + pcm_channels = dice->rx_channels; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; @@ -109,30 +121,42 @@ static int init_hw_info(struct snd_dice *dice, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; + struct amdtp_stream *stream; + unsigned int *pcm_channels; int err; hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_BLOCK_TRANSFER; - hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; - limit_channels_and_rates(dice, runtime, dice->rx_channels); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + hw->formats = AMDTP_IN_PCM_FORMAT_BITS; + stream = &dice->tx_stream; + pcm_channels = dice->tx_channels; + } else { + hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; + stream = &dice->rx_stream; + pcm_channels = dice->rx_channels; + } + + limit_channels_and_rates(dice, runtime, pcm_channels); limit_period_and_buffer(hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - dice_rate_constraint, dice, + dice_rate_constraint, substream, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - dice_channels_constraint, dice, + dice_channels_constraint, substream, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end; - err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); + err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); end: return err; } @@ -140,6 +164,8 @@ end: static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + unsigned int source, rate; + bool internal; int err; err = snd_dice_stream_lock_try(dice); @@ -149,6 +175,39 @@ static int pcm_open(struct snd_pcm_substream *substream) err = init_hw_info(dice, substream); if (err < 0) goto err_locked; + + err = snd_dice_transaction_get_clock_source(dice, &source); + if (err < 0) + goto err_locked; + switch (source) { + case CLOCK_SOURCE_AES1: + case CLOCK_SOURCE_AES2: + case CLOCK_SOURCE_AES3: + case CLOCK_SOURCE_AES4: + case CLOCK_SOURCE_AES_ANY: + case CLOCK_SOURCE_ADAT: + case CLOCK_SOURCE_TDIF: + case CLOCK_SOURCE_WC: + internal = false; + default: + internal = true; + } + + /* + * When source of clock is not internal or any PCM streams are running, + * available sampling rate is limited at current sampling rate. + */ + if (!internal || + amdtp_stream_pcm_running(&dice->tx_stream) || + amdtp_stream_pcm_running(&dice->rx_stream)) { + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + goto err_locked; + substream->runtime->hw.rate_min = rate; + substream->runtime->hw.rate_max = rate; + } + + snd_pcm_set_sync(substream); end: return err; err_locked: @@ -165,11 +224,28 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; } +static int capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + atomic_inc(&dice->substreams_counter); + + amdtp_stream_set_pcm_format(&dice->tx_stream, + params_format(hw_params)); + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + atomic_inc(&dice->substreams_counter); + amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params)); @@ -177,15 +253,41 @@ static int playback_hw_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); } +static int capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + atomic_dec(&dice->substreams_counter); + + snd_dice_stream_stop_duplex(dice); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + static int playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + atomic_dec(&dice->substreams_counter); + snd_dice_stream_stop_duplex(dice); return snd_pcm_lib_free_vmalloc_buffer(substream); } +static int capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err; + + err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); + if (err >= 0) + amdtp_stream_pcm_prepare(&dice->tx_stream); + + return 0; +} static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; @@ -198,6 +300,23 @@ static int playback_prepare(struct snd_pcm_substream *substream) return err; } +static int capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dice *dice = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&dice->tx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; @@ -216,6 +335,12 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } +static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + return amdtp_stream_pcm_pointer(&dice->tx_stream); +} static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; @@ -225,6 +350,18 @@ static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) int snd_dice_create_pcm(struct snd_dice *dice) { + static struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = capture_hw_params, + .hw_free = capture_hw_free, + .prepare = capture_prepare, + .trigger = capture_trigger, + .pointer = capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; static struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, @@ -240,11 +377,12 @@ int snd_dice_create_pcm(struct snd_dice *dice) struct snd_pcm *pcm; int err; - err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); + err = snd_pcm_new(dice->card, "DICE", 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = dice; strcpy(pcm->name, dice->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); return 0;