static int snd_atiixp_pcm_open(snd_pcm_substream_t *substream, atiixp_dma_t *dma, int pcm_type) { atiixp_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err; snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL); if (dma->opened) return -EBUSY; dma->substream = substream; runtime->hw = snd_atiixp_pcm_hw; dma->ac97_pcm_type = pcm_type; if (pcm_type >= 0) { runtime->hw.rates = chip->pcms[pcm_type]->rates; snd_pcm_limit_hw_rates(runtime); } else { /* direct SPDIF */ runtime->hw.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; } if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; runtime->private_data = dma; /* enable DMA bits */ spin_lock_irq(&chip->reg_lock); dma->ops->enable_dma(chip, 1); spin_unlock_irq(&chip->reg_lock); dma->opened = 1; return 0; }
static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream) { struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); debug("%s(%p)\n", __func__, substream); substream->runtime->hw = dev->pcm_info; snd_pcm_limit_hw_rates(substream->runtime); return 0; }
static int snd_usb_caiaq_pcm_prepare(struct snd_pcm_substream *substream) { int bytes_per_sample, bpp, ret, i; int index = substream->number; struct snd_usb_caiaqdev *dev = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; debug("%s(%p)\n", __func__, substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dev->period_out_count[index] = BYTES_PER_SAMPLE + 1; dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1; } else { int in_pos = (dev->spec.data_alignment == 2) ? 0 : 2; dev->period_in_count[index] = BYTES_PER_SAMPLE + in_pos; dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE + in_pos; } if (dev->streaming) return 0; /* the first client that opens a stream defines the sample rate * setting for all subsequent calls, until the last client closed. */ for (i=0; i < ARRAY_SIZE(rates); i++) if (runtime->rate == rates[i]) dev->pcm_info.rates = 1 << i; snd_pcm_limit_hw_rates(runtime); bytes_per_sample = BYTES_PER_SAMPLE; if (dev->spec.data_alignment == 2) bytes_per_sample++; bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE) * bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams; if (bpp > MAX_ENDPOINT_SIZE) bpp = MAX_ENDPOINT_SIZE; ret = snd_usb_caiaq_set_audio_params(dev, runtime->rate, runtime->sample_bits, bpp); if (ret) return ret; ret = stream_start(dev); if (ret) return ret; dev->output_running = 0; wait_event_timeout(dev->prepare_wait_queue, dev->output_running, HZ); if (!dev->output_running) { stream_stop(dev); return -EPIPE; } return 0; }
static int pcm_init_hw_params(struct snd_efw *efw, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; unsigned int *pcm_channels; int err; runtime->hw.info = SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS; s = &efw->tx_stream; pcm_channels = efw->pcm_capture_channels; } else { runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; s = &efw->rx_stream; pcm_channels = efw->pcm_playback_channels; } /* limit rates */ runtime->hw.rates = efw->supported_sampling_rate, snd_pcm_limit_hw_rates(runtime); limit_channels(&runtime->hw, pcm_channels); limit_period_and_buffer(&runtime->hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, pcm_channels, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, pcm_channels, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto end; err = amdtp_stream_add_pcm_hw_constraints(s, runtime); end: return err; }
static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream) { int err; struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; runtime->hw = snd_cs5535audio_playback; runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_FRONT_DAC]; snd_pcm_limit_hw_rates(runtime); cs5535au->playback_substream = substream; runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]); if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; return 0; }
static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream) { int err; struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; runtime->hw = snd_cs5535audio_capture; runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC]; snd_pcm_limit_hw_rates(runtime); cs5535au->capture_substream = substream; runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]); if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; olpc_capture_open(cs5535au->ac97); return 0; }
static int pxa3xx_wm9713l_pcm_startup(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; pxa2xx_audio_ops_t *platform_ops; runtime->hw.channels_min = 2; runtime->hw.channels_max = 2; runtime->hw.rates = SOUND_RATES; snd_pcm_limit_hw_rates(runtime); platform_ops = substream->pcm->card->dev->platform_data; if (platform_ops && platform_ops->startup) return platform_ops->startup(substream, platform_ops->priv); else return 0; }
static int pxa2xx_ac97_pcm_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; pxa2xx_audio_ops_t *platform_ops; int r; runtime->hw.channels_min = 2; runtime->hw.channels_max = 2; r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? AC97_RATES_FRONT_DAC : AC97_RATES_ADC; runtime->hw.rates = pxa2xx_ac97_ac97->rates[r]; snd_pcm_limit_hw_rates(runtime); platform_ops = substream->pcm->card->dev->platform_data; if (platform_ops && platform_ops->startup) return platform_ops->startup(substream, platform_ops->priv); else return 0; }
static int limit_channels_and_rates(struct snd_dice *dice, struct snd_pcm_runtime *runtime, enum amdtp_stream_direction dir, unsigned int index, unsigned int size) { struct snd_pcm_hardware *hw = &runtime->hw; struct amdtp_stream *stream; unsigned int rate; __be32 reg; int err; /* * Retrieve current Multi Bit Linear Audio data channel and limit to * it. */ if (dir == AMDTP_IN_STREAM) { stream = &dice->tx_stream[index]; err = snd_dice_transaction_read_tx(dice, size * index + TX_NUMBER_AUDIO, ®, sizeof(reg)); } else { stream = &dice->rx_stream[index]; err = snd_dice_transaction_read_rx(dice, size * index + RX_NUMBER_AUDIO, ®, sizeof(reg)); } if (err < 0) return err; hw->channels_min = hw->channels_max = be32_to_cpu(reg); /* Retrieve current sampling transfer frequency and limit to it. */ err = snd_dice_transaction_get_rate(dice, &rate); if (err < 0) return err; hw->rates = snd_pcm_rate_to_rate_bit(rate); snd_pcm_limit_hw_rates(runtime); return 0; }
/* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls * startup for the cpu DAI, platform, machine and codec DAI. */ static int soc_pcm_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver; int ret = 0; pm_runtime_get_sync(cpu_dai->dev); pm_runtime_get_sync(codec_dai->dev); pm_runtime_get_sync(platform->dev); mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); /* startup the audio subsystem */ if (cpu_dai->driver->ops->startup) { ret = cpu_dai->driver->ops->startup(substream, cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, "can't open interface %s: %d\n", cpu_dai->name, ret); goto out; } } if (platform->driver->ops && platform->driver->ops->open) { ret = platform->driver->ops->open(substream); if (ret < 0) { dev_err(platform->dev, "can't open platform %s: %d\n", platform->name, ret); goto platform_err; } } if (codec_dai->driver->ops->startup) { ret = codec_dai->driver->ops->startup(substream, codec_dai); if (ret < 0) { dev_err(codec_dai->dev, "can't open codec %s: %d\n", codec_dai->name, ret); goto codec_dai_err; } } if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { ret = rtd->dai_link->ops->startup(substream); if (ret < 0) { pr_err("asoc: %s startup failed: %d\n", rtd->dai_link->name, ret); goto machine_err; } } /* Check that the codec and cpu DAIs are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { runtime->hw.rate_min = max(codec_dai_drv->playback.rate_min, cpu_dai_drv->playback.rate_min); runtime->hw.rate_max = min(codec_dai_drv->playback.rate_max, cpu_dai_drv->playback.rate_max); runtime->hw.channels_min = max(codec_dai_drv->playback.channels_min, cpu_dai_drv->playback.channels_min); runtime->hw.channels_max = min(codec_dai_drv->playback.channels_max, cpu_dai_drv->playback.channels_max); runtime->hw.formats = codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats; runtime->hw.rates = codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates; if (codec_dai_drv->playback.rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) runtime->hw.rates |= cpu_dai_drv->playback.rates; if (cpu_dai_drv->playback.rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) runtime->hw.rates |= codec_dai_drv->playback.rates; } else { runtime->hw.rate_min = max(codec_dai_drv->capture.rate_min, cpu_dai_drv->capture.rate_min); runtime->hw.rate_max = min(codec_dai_drv->capture.rate_max, cpu_dai_drv->capture.rate_max); runtime->hw.channels_min = max(codec_dai_drv->capture.channels_min, cpu_dai_drv->capture.channels_min); runtime->hw.channels_max = min(codec_dai_drv->capture.channels_max, cpu_dai_drv->capture.channels_max); runtime->hw.formats = codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats; runtime->hw.rates = codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates; if (codec_dai_drv->capture.rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) runtime->hw.rates |= cpu_dai_drv->capture.rates; if (cpu_dai_drv->capture.rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) runtime->hw.rates |= codec_dai_drv->capture.rates; } ret = -EINVAL; snd_pcm_limit_hw_rates(runtime); if (!runtime->hw.rates) { printk(KERN_ERR "asoc: %s <-> %s No matching rates\n", codec_dai->name, cpu_dai->name); goto config_err; } if (!runtime->hw.formats) { printk(KERN_ERR "asoc: %s <-> %s No matching formats\n", codec_dai->name, cpu_dai->name); goto config_err; } if (!runtime->hw.channels_min || !runtime->hw.channels_max || runtime->hw.channels_min > runtime->hw.channels_max) { printk(KERN_ERR "asoc: %s <-> %s No matching channels\n", codec_dai->name, cpu_dai->name); goto config_err; } soc_pcm_apply_msb(substream, codec_dai); soc_pcm_apply_msb(substream, cpu_dai); /* Symmetry only applies if we've already got an active stream. */ if (cpu_dai->active) { ret = soc_pcm_apply_symmetry(substream, cpu_dai); if (ret != 0) goto config_err; } if (codec_dai->active) { ret = soc_pcm_apply_symmetry(substream, codec_dai); if (ret != 0) goto config_err; } pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name); pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates); pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min, runtime->hw.channels_max); pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min, runtime->hw.rate_max); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { cpu_dai->playback_active++; codec_dai->playback_active++; } else { cpu_dai->capture_active++; codec_dai->capture_active++; } cpu_dai->active++; codec_dai->active++; rtd->codec->active++; mutex_unlock(&rtd->pcm_mutex); return 0; config_err: if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); machine_err: if (codec_dai->driver->ops->shutdown) codec_dai->driver->ops->shutdown(substream, codec_dai); codec_dai_err: if (platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); platform_err: if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); out: mutex_unlock(&rtd->pcm_mutex); pm_runtime_put(platform->dev); pm_runtime_put(codec_dai->dev); pm_runtime_put(cpu_dai->dev); return ret; }
static int firewave_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { static const struct snd_interval all_channels = { .min = 6, .max = 6 }; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); /* 32/44.1 kHz work only with all six channels */ if (snd_interval_max(rate) < 48000) return snd_interval_refine(channels, &all_channels); return 0; } static int firewave_constraints(struct snd_pcm_runtime *runtime) { static unsigned int channels_list[] = { 2, 6 }; static struct snd_pcm_hw_constraint_list channels_list_constraint = { .count = 2, .list = channels_list, }; int err; runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000; runtime->hw.channels_max = 6; err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &channels_list_constraint); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, firewave_rate_constraint, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, firewave_channels_constraint, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; return 0; } static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) { runtime->hw.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; return 0; } static int fwspk_open(struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = AMDTP_OUT_PCM_FORMAT_BITS, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 4 * 1024 * 1024, .period_bytes_min = 1, .period_bytes_max = UINT_MAX, .periods_min = 1, .periods_max = UINT_MAX, }; struct fwspk *fwspk = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; runtime->hw = hardware; err = fwspk->device_info->pcm_constraints(runtime); if (err < 0) return err; err = snd_pcm_limit_hw_rates(runtime); if (err < 0) return err; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5000, UINT_MAX); if (err < 0) return err; err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); if (err < 0) return err; return 0; } static int fwspk_close(struct snd_pcm_substream *substream) { return 0; } static void fwspk_stop_stream(struct fwspk *fwspk) { if (fwspk->stream_running) { amdtp_out_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); fwspk->stream_running = false; } } static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc) { u8 *buf; int err; buf = kmalloc(8, GFP_KERNEL); if (!buf) return -ENOMEM; buf[0] = 0x00; /* AV/C, CONTROL */ buf[1] = 0xff; /* unit */ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ buf[3] = 0x00; /* plug 0 */ buf[4] = 0x90; /* format: audio */ buf[5] = 0x00 | sfc; /* AM824, frequency */ buf[6] = 0xff; /* SYT (not used) */ buf[7] = 0xff; err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8, BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); if (err < 0) goto error; if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) { dev_err(&fwspk->unit->device, "failed to set sample rate\n"); err = -EIO; goto error; } err = 0; error: kfree(buf); return err; } static int fwspk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) goto error; amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); amdtp_out_stream_set_pcm_format(&fwspk->stream, params_format(hw_params)); err = fwspk_set_rate(fwspk, fwspk->stream.sfc); if (err < 0) goto err_buffer; return 0; err_buffer: snd_pcm_lib_free_vmalloc_buffer(substream); error: return err; } static int fwspk_hw_free(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; mutex_lock(&fwspk->mutex); fwspk_stop_stream(fwspk); mutex_unlock(&fwspk->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int fwspk_prepare(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; int err; mutex_lock(&fwspk->mutex); if (amdtp_out_streaming_error(&fwspk->stream)) fwspk_stop_stream(fwspk); if (!fwspk->stream_running) { err = cmp_connection_establish(&fwspk->connection, amdtp_out_stream_get_max_payload(&fwspk->stream)); if (err < 0) goto err_mutex; err = amdtp_out_stream_start(&fwspk->stream, fwspk->connection.resources.channel, fwspk->connection.speed); if (err < 0) goto err_connection; fwspk->stream_running = true; } mutex_unlock(&fwspk->mutex); amdtp_out_stream_pcm_prepare(&fwspk->stream); return 0; err_connection: cmp_connection_break(&fwspk->connection); err_mutex: mutex_unlock(&fwspk->mutex); return err; } static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) { struct fwspk *fwspk = substream->private_data; struct snd_pcm_substream *pcm; switch (cmd) { case SNDRV_PCM_TRIGGER_START: pcm = substream; break; case SNDRV_PCM_TRIGGER_STOP: pcm = NULL; break; default: return -EINVAL; } amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); return 0; } static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) { struct fwspk *fwspk = substream->private_data; return amdtp_out_stream_pcm_pointer(&fwspk->stream); } static int fwspk_create_pcm(struct fwspk *fwspk) { static struct snd_pcm_ops ops = { .open = fwspk_open, .close = fwspk_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = fwspk_hw_params, .hw_free = fwspk_hw_free, .prepare = fwspk_prepare, .trigger = fwspk_trigger, .pointer = fwspk_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); if (err < 0) return err; pcm->private_data = fwspk; strcpy(pcm->name, fwspk->device_info->short_name); fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; fwspk->pcm->ops = &ops; return 0; } enum control_action { CTL_READ, CTL_WRITE }; enum control_attribute { CTL_MIN = 0x02, CTL_MAX = 0x03, CTL_CURRENT = 0x10, }; static int fwspk_mute_command(struct fwspk *fwspk, bool *value, enum control_action action) { u8 *buf; u8 response_ok; int err; buf = kmalloc(11, GFP_KERNEL); if (!buf) return -ENOMEM; if (action == CTL_READ) { buf[0] = 0x01; /* AV/C, STATUS */ response_ok = 0x0c; /* STABLE */ } else { buf[0] = 0x00; /* AV/C, CONTROL */ response_ok = 0x09; /* ACCEPTED */ } buf[1] = 0x08; /* audio unit 0 */ buf[2] = 0xb8; /* FUNCTION BLOCK */ buf[3] = 0x81; /* function block type: feature */ buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ buf[5] = 0x10; /* control attribute: current */ buf[6] = 0x02; /* selector length */ buf[7] = 0x00; /* audio channel number */ buf[8] = 0x01; /* control selector: mute */ buf[9] = 0x01; /* control data length */ if (action == CTL_READ) buf[10] = 0xff; else buf[10] = *value ? 0x70 : 0x60; err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); if (err < 0) goto error; if (err < 11) { dev_err(&fwspk->unit->device, "short FCP response\n"); err = -EIO; goto error; } if (buf[0] != response_ok) { dev_err(&fwspk->unit->device, "mute command failed\n"); err = -EIO; goto error; } if (action == CTL_READ) *value = buf[10] == 0x70; err = 0; error: kfree(buf); return err; }
static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { 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); struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval rates = { .min = UINT_MAX, .max = 0, .integer = 1 }; 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]; if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) continue; if (!snd_interval_test(c, pcm_channels[mode])) continue; rates.min = min(rates.min, rate); rates.max = max(rates.max, rate); } return snd_interval_refine(r, &rates); } static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { 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); struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval channels = { .min = UINT_MAX, .max = 0, .integer = 1 }; 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]; if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) continue; if (!snd_interval_test(r, rate)) continue; channels.min = min(channels.min, pcm_channels[mode]); channels.max = max(channels.max, pcm_channels[mode]); } return snd_interval_refine(c, &channels); } static void limit_channels_and_rates(struct snd_dice *dice, struct snd_pcm_runtime *runtime, unsigned int *pcm_channels) { struct snd_pcm_hardware *hw = &runtime->hw; unsigned int i, rate, mode; hw->channels_min = UINT_MAX; hw->channels_max = 0; for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) continue; hw->rates |= snd_pcm_rate_to_rate_bit(rate); if (pcm_channels[mode] == 0) continue; hw->channels_min = min(hw->channels_min, pcm_channels[mode]); hw->channels_max = max(hw->channels_max, pcm_channels[mode]); } snd_pcm_limit_hw_rates(runtime); } static void limit_period_and_buffer(struct snd_pcm_hardware *hw) { hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ hw->periods_max = UINT_MAX; hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */ /* Just to prevent from allocating much pages. */ hw->period_bytes_max = hw->period_bytes_min * 2048; hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; } static int init_hw_info(struct snd_dice *dice, struct snd_pcm_substream *substream) { 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; 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, 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, substream, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end; err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); end: return err; } 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); if (err < 0) goto end; 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; break; default: internal = true; break; } /* * 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: snd_dice_stream_lock_release(dice); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; snd_dice_stream_lock_release(dice); 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) { mutex_lock(&dice->mutex); dice->substreams_counter++; mutex_unlock(&dice->mutex); } 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) { mutex_lock(&dice->mutex); dice->substreams_counter++; mutex_unlock(&dice->mutex); } amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params)); return snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); } static int capture_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; mutex_lock(&dice->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dice->substreams_counter--; snd_dice_stream_stop_duplex(dice); mutex_unlock(&dice->mutex); 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; mutex_lock(&dice->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dice->substreams_counter--; snd_dice_stream_stop_duplex(dice); mutex_unlock(&dice->mutex); 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; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); 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; int err; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) amdtp_stream_pcm_prepare(&dice->rx_stream); 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; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dice->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); break; default: return -EINVAL; } 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; return amdtp_stream_pcm_pointer(&dice->rx_stream); } 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, .ioctl = snd_pcm_lib_ioctl, .hw_params = playback_hw_params, .hw_free = playback_hw_free, .prepare = playback_prepare, .trigger = playback_trigger, .pointer = playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; unsigned int i, capture, playback; int err; capture = playback = 0; for (i = 0; i < 3; i++) { if (dice->tx_channels[i] > 0) capture = 1; if (dice->rx_channels[i] > 0) playback = 1; } err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); if (err < 0) return err; pcm->private_data = dice; strcpy(pcm->name, dice->card->shortname); if (capture > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); if (playback > 0) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); return 0; }