static int uni_player_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai); struct uniperif *player = priv->dai_data.uni; int ret; player->substream = substream; player->clk_adj = 0; if (!UNIPERIF_TYPE_IS_TDM(player)) return 0; /* refine hw constraint in tdm mode */ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, sti_uniperiph_fix_tdm_chan, player, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (ret < 0) return ret; return snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, sti_uniperiph_fix_tdm_format, player, SNDRV_PCM_HW_PARAM_FORMAT, -1); }
static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) { struct pxa2xx_pcm_client *client = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct pxa2xx_runtime_data *rtd; int ret; runtime->hw = pxa2xx_pcm_hardware; /* * For mysterious reasons (and despite what the manual says) * playback samples are lost if the DMA count is not a multiple * of the DMA burst size. Let's add a rule to enforce that. */ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, pxa2xx_pcm_hw_rule_mult32, NULL, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1); if (ret) goto out; ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, pxa2xx_pcm_hw_rule_mult32, NULL, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); if (ret) goto out; ret = -ENOMEM; rtd = kmalloc(sizeof(*rtd), GFP_KERNEL); if (!rtd) goto out; rtd->dma_desc_array = dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, &rtd->dma_desc_array_phys, GFP_KERNEL); if (!rtd->dma_desc_array) goto err1; rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? client->playback_params : client->capture_params; ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) goto err2; rtd->dma_ch = ret; runtime->private_data = rtd; ret = client->startup(substream); if (!ret) goto out; pxa_free_dma(rtd->dma_ch); err2: dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, rtd->dma_desc_array, rtd->dma_desc_array_phys); err1: kfree(rtd); out: return ret; }
static int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); if (channels->min > 2) { static const struct snd_interval single_rates = { .min = 1, .max = 48000, }; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); return snd_interval_refine(rate, &single_rates); } return 0; } static int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); if (rate->min > 48000) { static const struct snd_interval double_rate_channels = { .min = 2, .max = 2, }; struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); return snd_interval_refine(channels, &double_rate_channels); } return 0; } /** * snd_ac97_pcm_double_rate_rules - set double rate constraints * @runtime: the runtime of the ac97 front playback pcm * * Installs the hardware constraint rules to prevent using double rates and * more than two channels at the same time. * * Return: Zero if successful, or a negative error code on failure. */ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime) { int err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, double_rate_hw_constraint_rate, 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, double_rate_hw_constraint_channels, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); return err; } EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules);
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_pcm_playback_open(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; pcm_hw_t *chip = snd_pcm_substream_chip(substream); int err = 0; DEBUG_PRINT(("ALSA Core :>>> snd_pcm_playback_open(substream = 0x%08lx)\n",substream)); DEBUG_PRINT((">>> chip = 0x%08lx\n",chip)); snd_pcm_set_sync(substream); #if 0 // No Rule snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, snd_pcm_period_size_rule, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, snd_pcm_period_size_rule, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, snd_pcm_period_size_rule, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); #endif //spin_lock(&chip->lock); chip->current_substream = substream; runtime->hw = chip->hw; // To check in case of decoder is not working than in rawmode of spdif // we will support only 32 mode of data else we can support any if(chip->playback_ops->open_device) err = chip->playback_ops->open_device(substream); // spin_unlock(&chip->lock); DEBUG_PRINT(("ALSA Core :<<< snd_pcm_playback_open(substream = 0x%08lx)\n",substream)); return err; }
static int snd_pmac_pcm_open(struct snd_pmac *chip, struct pmac_stream *rec, struct snd_pcm_substream *subs) { struct snd_pcm_runtime *runtime = subs->runtime; int i; /* look up frequency table and fill bit mask */ runtime->hw.rates = 0; for (i = 0; i < chip->num_freqs; i++) if (chip->freqs_ok & (1 << i)) runtime->hw.rates |= snd_pcm_rate_to_rate_bit(chip->freq_table[i]); /* check for minimum and maximum rates */ for (i = 0; i < chip->num_freqs; i++) { if (chip->freqs_ok & (1 << i)) { runtime->hw.rate_max = chip->freq_table[i]; break; } } for (i = chip->num_freqs - 1; i >= 0; i--) { if (chip->freqs_ok & (1 << i)) { runtime->hw.rate_min = chip->freq_table[i]; break; } } runtime->hw.formats = chip->formats_ok; if (chip->can_capture) { if (! chip->can_duplex) runtime->hw.info |= SNDRV_PCM_INFO_HALF_DUPLEX; runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; } runtime->private_data = rec; rec->substream = subs; #if 0 /* FIXME: still under development.. */ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_pmac_hw_rule_rate, chip, rec->stream, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, snd_pmac_hw_rule_format, chip, rec->stream, -1); #endif runtime->hw.periods_max = rec->cmd.size - 1; /* constraints to fix choppy sound */ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); return 0; }
static int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); if (channels->min > 2) { static const struct snd_interval single_rates = { .min = 1, .max = 48000, }; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); return snd_interval_refine(rate, &single_rates); } return 0; } static int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); if (rate->min > 48000) { static const struct snd_interval double_rate_channels = { .min = 2, .max = 2, }; struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); return snd_interval_refine(channels, &double_rate_channels); } return 0; } int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime) { int err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, double_rate_hw_constraint_rate, 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, double_rate_hw_constraint_channels, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); return err; } EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules);
static int pcm_init_hw_params(struct snd_bebob *bebob, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; struct snd_bebob_stream_formation *formations; 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 = &bebob->tx_stream; formations = bebob->tx_stream_formations; } else { runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; s = &bebob->rx_stream; formations = bebob->rx_stream_formations; } limit_channels_and_rates(&runtime->hw, formations); limit_period_and_buffer(&runtime->hw); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, formations, 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, formations, 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 aess_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; struct omap_abe *abe = snd_soc_platform_get_drvdata(platform); struct snd_soc_dai *dai = rtd->cpu_dai; int ret = 0; mutex_lock(&abe->mutex); dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name); switch (dai->id) { case OMAP_ABE_FRONTEND_DAI_MODEM: break; case OMAP_ABE_FRONTEND_DAI_LP_MEDIA: snd_soc_set_runtime_hwparams(substream, &omap_abe_hardware); ret = snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 1024); break; default: /* * Period size must be aligned with the Audio Engine * processing loop which is 250 us long */ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, omap_abe_hwrule_period_step, NULL, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); break; } if (ret < 0) { dev_err(abe->dev, "failed to set period constraints for DAI %d\n", dai->id); goto out; } omap_abe_pm_runtime_get_sync(abe); if (!abe->active++) { abe->opp.level = 0; ret = abe_pm_restore_context(abe); if (ret) goto out; omap_aess_wakeup(abe->aess); } out: mutex_unlock(&abe->mutex); return ret; }
static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai); int bus_id = mcbsp_data->bus_id; int err = 0; if (!cpu_dai->active) { err = omap_mcbsp_request(bus_id); cpu_dai->active = 1; } /* * OMAP3 McBSP FIFO is word structured. * McBSP2 has 1024 + 256 = 1280 word long buffer, * McBSP1,3,4,5 has 128 word long buffer * This means that the size of the FIFO depends on the sample format. * For example on McBSP3: * 16bit samples: size is 128 * 2 = 256 bytes * 32bit samples: size is 128 * 4 = 512 bytes * It is simpler to place constraint for buffer and period based on * channels. * McBSP3 as example again (16 or 32 bit samples): * 1 channel (mono): size is 128 frames (128 words) * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) */ if (cpu_is_omap34xx() || cpu_is_omap44xx()) { /* * Rule for the buffer size. We should not allow * smaller buffer than the FIFO size to avoid underruns */ #if 0 // FIXME: All BE must support hw_rules and constraints */ snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, omap_mcbsp_hwrule_min_buffersize, mcbsp_data, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); /* Make sure, that the period size is always even */ snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); #endif } return err; }
static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); int err = 0; if (!cpu_dai->active) err = omap_mcbsp_request(mcbsp); /* * OMAP3 McBSP FIFO is word structured. * McBSP2 has 1024 + 256 = 1280 word long buffer, * McBSP1,3,4,5 has 128 word long buffer * This means that the size of the FIFO depends on the sample format. * For example on McBSP3: * 16bit samples: size is 128 * 2 = 256 bytes * 32bit samples: size is 128 * 4 = 512 bytes * It is simpler to place constraint for buffer and period based on * channels. * McBSP3 as example again (16 or 32 bit samples): * 1 channel (mono): size is 128 frames (128 words) * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) */ if (mcbsp->pdata->buffer_size) { /* * Rule for the buffer size. We should not allow * smaller buffer than the FIFO size to avoid underruns. * This applies only for the playback stream. */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, omap_mcbsp_hwrule_min_buffersize, mcbsp, SNDRV_PCM_HW_PARAM_CHANNELS, -1); /* Make sure, that the period size is always even */ snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); } snd_soc_dai_set_dma_data(cpu_dai, substream, &mcbsp->dma_data[substream->stream]); return err; }
static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); struct device *dev = dai->dev; struct snd_pcm_hw_constraint_ratnums *constraints_no_pll; struct snd_ratnum *rats_no_pll; if (IS_ERR(pcm512x->sclk)) { dev_err(dev, "Need SCLK for master mode: %ld\n", PTR_ERR(pcm512x->sclk)); return PTR_ERR(pcm512x->sclk); } if (pcm512x->pll_out) return snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, pcm512x_hw_rule_rate, pcm512x, SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); constraints_no_pll = devm_kzalloc(dev, sizeof(*constraints_no_pll), GFP_KERNEL); if (!constraints_no_pll) return -ENOMEM; constraints_no_pll->nrats = 1; rats_no_pll = devm_kzalloc(dev, sizeof(*rats_no_pll), GFP_KERNEL); if (!rats_no_pll) return -ENOMEM; constraints_no_pll->rats = rats_no_pll; rats_no_pll->num = clk_get_rate(pcm512x->sclk) / 64; rats_no_pll->den_min = 1; rats_no_pll->den_max = 128; rats_no_pll->den_step = 1; return snd_pcm_hw_constraint_ratnums(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, constraints_no_pll); }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(c, formations[i].pcm)) continue; t.min = min(t.min, snd_bebob_rate_table[i]); t.max = max(t.max, snd_bebob_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_bebob_stream_formation *formations = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry is invalid */ if (formations[i].pcm == 0) continue; if (!snd_interval_test(r, snd_bebob_rate_table[i])) continue; t.min = min(t.min, formations[i].pcm); t.max = max(t.max, formations[i].pcm); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, struct snd_bebob_stream_formation *formations) { unsigned int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; hw->rates = 0; for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) { /* entry has no PCM channels */ if (formations[i].pcm == 0) continue; hw->channels_min = min(hw->channels_min, formations[i].pcm); hw->channels_max = max(hw->channels_max, formations[i].pcm); hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]); hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]); hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]); } } static int pcm_init_hw_params(struct snd_bebob *bebob, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; struct snd_bebob_stream_formation *formations; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &bebob->tx_stream; formations = bebob->tx_stream_formations; } else { runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; s = &bebob->rx_stream; formations = bebob->rx_stream_formations; } limit_channels_and_rates(&runtime->hw, formations); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, formations, 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, formations, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto end; err = amdtp_am824_add_pcm_hw_constraints(s, runtime); end: return err; } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; const struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; enum snd_bebob_clock_type src; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) goto end; err = pcm_init_hw_params(bebob, substream); if (err < 0) goto err_locked; err = snd_bebob_stream_get_clock_src(bebob, &src); if (err < 0) goto err_locked; /* * When source of clock is internal or any PCM stream are running, * the available sampling rate is limited at current sampling rate. */ if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { err = spec->get(bebob, &sampling_rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to get sampling rate: %d\n", err); goto err_locked; } substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_bebob_stream_lock_release(bebob); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_lock_release(bebob); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter++; mutex_unlock(&bebob->mutex); } return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); bebob->substreams_counter--; mutex_unlock(&bebob->mutex); } snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->tx_stream); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->rx_stream); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; return amdtp_stream_pcm_ack(&bebob->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; return amdtp_stream_pcm_ack(&bebob->rx_stream); } int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm); if (err < 0) goto end; pcm->private_data = bebob; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); end: return err; }
static int snd_pmac_pcm_open(struct snd_pmac *chip, struct pmac_stream *rec, struct snd_pcm_substream *subs) { struct snd_pcm_runtime *runtime = subs->runtime; int i, j, fflags; static int typical_freqs[] = { 44100, 22050, 11025, 0, }; static int typical_freq_flags[] = { SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_22050, SNDRV_PCM_RATE_11025, 0, }; /* look up frequency table and fill bit mask */ runtime->hw.rates = 0; fflags = chip->freqs_ok; for (i = 0; typical_freqs[i]; i++) { for (j = 0; j < chip->num_freqs; j++) { if ((chip->freqs_ok & (1 << j)) && chip->freq_table[j] == typical_freqs[i]) { runtime->hw.rates |= typical_freq_flags[i]; fflags &= ~(1 << j); break; } } } if (fflags) /* rest */ runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; /* check for minimum and maximum rates */ for (i = 0; i < chip->num_freqs; i++) { if (chip->freqs_ok & (1 << i)) { runtime->hw.rate_max = chip->freq_table[i]; break; } } for (i = chip->num_freqs - 1; i >= 0; i--) { if (chip->freqs_ok & (1 << i)) { runtime->hw.rate_min = chip->freq_table[i]; break; } } runtime->hw.formats = chip->formats_ok; if (chip->can_capture) { if (! chip->can_duplex) runtime->hw.info |= SNDRV_PCM_INFO_HALF_DUPLEX; runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; } runtime->private_data = rec; rec->substream = subs; #if 0 /* FIXME: still under development.. */ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_pmac_hw_rule_rate, chip, rec->stream, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, snd_pmac_hw_rule_format, chip, rec->stream, -1); #endif runtime->hw.periods_max = rec->cmd.size - 1; if (chip->can_duplex) snd_pcm_set_sync(subs); /* constraints to fix choppy sound */ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); return 0; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(c, pcm_channels[mode])) continue; t.min = min(t.min, amdtp_rate_table[i]); t.max = max(t.max, amdtp_rate_table[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { const unsigned int *pcm_channels = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1 }; unsigned int i; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; if (!snd_interval_test(r, amdtp_rate_table[i])) continue; t.min = min(t.min, pcm_channels[mode]); t.max = max(t.max, pcm_channels[mode]); } return snd_interval_refine(c, &t); } static void limit_channels_and_rates(struct snd_pcm_hardware *hw, const unsigned int *pcm_channels) { unsigned int rate, channels; int i; hw->channels_min = UINT_MAX; hw->channels_max = 0; hw->rate_min = UINT_MAX; hw->rate_max = 0; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) { enum snd_ff_stream_mode mode; int err; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) continue; channels = pcm_channels[mode]; if (pcm_channels[mode] == 0) continue; hw->channels_min = min(hw->channels_min, channels); hw->channels_max = max(hw->channels_max, channels); rate = amdtp_rate_table[i]; hw->rates |= snd_pcm_rate_to_rate_bit(rate); hw->rate_min = min(hw->rate_min, rate); hw->rate_max = max(hw->rate_max, rate); } } static int pcm_init_hw_params(struct snd_ff *ff, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct amdtp_stream *s; const unsigned int *pcm_channels; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->tx_stream; pcm_channels = ff->spec->pcm_capture_channels; } else { runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &ff->rx_stream; pcm_channels = ff->spec->pcm_playback_channels; } limit_channels_and_rates(&runtime->hw, pcm_channels); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, (void *)pcm_channels, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; return amdtp_ff_add_pcm_hw_constraints(s, runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; unsigned int rate; enum snd_ff_clock_src src; int i, err; err = snd_ff_stream_lock_try(ff); if (err < 0) return err; err = pcm_init_hw_params(ff, substream); if (err < 0) goto release_lock; err = snd_ff_transaction_get_clock(ff, &rate, &src); if (err < 0) goto release_lock; if (src != SND_FF_CLOCK_SRC_INTERNAL) { for (i = 0; i < CIP_SFC_COUNT; ++i) { if (amdtp_rate_table[i] == rate) break; } /* * The unit is configured at sampling frequency which packet * streaming engine can't support. */ if (i >= CIP_SFC_COUNT) { err = -EIO; goto release_lock; } substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } else { if (amdtp_stream_pcm_running(&ff->rx_stream) || amdtp_stream_pcm_running(&ff->tx_stream)) { rate = amdtp_rate_table[ff->rx_stream.sfc]; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } } snd_pcm_set_sync(substream); return 0; release_lock: snd_ff_stream_lock_release(ff); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; snd_ff_stream_lock_release(ff); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); ff->substreams_counter++; mutex_unlock(&ff->mutex); } return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) ff->substreams_counter--; snd_ff_stream_stop_duplex(ff); mutex_unlock(&ff->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->tx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&ff->mutex); err = snd_ff_stream_start_duplex(ff, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&ff->rx_stream); mutex_unlock(&ff->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ff *ff = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&ff->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&ff->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; return amdtp_stream_pcm_pointer(&ff->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; return amdtp_stream_pcm_ack(&ff->rx_stream); } int snd_ff_create_pcm_devices(struct snd_ff *ff) { static const struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .ack = pcm_capture_ack, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .ack = pcm_playback_ack, .page = snd_pcm_lib_get_vmalloc_page, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = ff; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", ff->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); return 0; }
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 loopback_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct loopback *loopback = substream->private_data; struct loopback_pcm *dpcm; struct loopback_cable *cable; int err = 0; int dev = get_cable_index(substream); mutex_lock(&loopback->cable_lock); dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); if (!dpcm) { err = -ENOMEM; goto unlock; } dpcm->loopback = loopback; dpcm->substream = substream; setup_timer(&dpcm->timer, loopback_timer_function, (unsigned long)dpcm); spin_lock_init(&dpcm->timer_lock); cable = loopback->cables[substream->number][dev]; if (!cable) { cable = kzalloc(sizeof(*cable), GFP_KERNEL); if (!cable) { kfree(dpcm); err = -ENOMEM; goto unlock; } spin_lock_init(&cable->lock); cable->hw = loopback_pcm_hardware; loopback->cables[substream->number][dev] = cable; } dpcm->cable = cable; cable->streams[substream->stream] = dpcm; snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, rule_format, &runtime->hw, SNDRV_PCM_HW_PARAM_FORMAT, -1); if (err < 0) goto unlock; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, rule_rate, &runtime->hw, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto unlock; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, rule_channels, &runtime->hw, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto unlock; runtime->private_data = dpcm; runtime->private_free = loopback_runtime_free; if (get_notify(dpcm)) runtime->hw = loopback_pcm_hardware; else runtime->hw = cable->hw; unlock: mutex_unlock(&loopback->cable_lock); 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; }
static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1, }; unsigned int i; for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (!snd_interval_test(c, snd_dg00x_stream_pcm_channels[i])) continue; t.min = min(t.min, snd_dg00x_stream_rates[i]); t.max = max(t.max, snd_dg00x_stream_rates[i]); } return snd_interval_refine(r, &t); } static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval t = { .min = UINT_MAX, .max = 0, .integer = 1, }; unsigned int i; for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (!snd_interval_test(r, snd_dg00x_stream_rates[i])) continue; t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]); t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]); } return snd_interval_refine(c, &t); } static int pcm_init_hw_params(struct snd_dg00x *dg00x, struct snd_pcm_substream *substream) { static const struct snd_pcm_hardware hardware = { .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, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000, .rate_min = 44100, .rate_max = 96000, .channels_min = 10, .channels_max = 18, .period_bytes_min = 4 * 18, .period_bytes_max = 4 * 18 * 2048, .buffer_bytes_max = 4 * 18 * 2048 * 2, .periods_min = 2, .periods_max = UINT_MAX, }; struct amdtp_stream *s; int err; substream->runtime->hw = hardware; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32; s = &dg00x->tx_stream; } else { substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32; s = &dg00x->rx_stream; } err = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; err = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) return err; return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; enum snd_dg00x_clock clock; bool detect; unsigned int rate; int err; err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) goto end; err = pcm_init_hw_params(dg00x, substream); if (err < 0) goto err_locked; /* Check current clock source. */ err = snd_dg00x_stream_get_clock(dg00x, &clock); if (err < 0) goto err_locked; if (clock != SND_DG00X_CLOCK_INTERNAL) { err = snd_dg00x_stream_check_external_clock(dg00x, &detect); if (err < 0) goto err_locked; if (!detect) { err = -EBUSY; goto err_locked; } } if ((clock != SND_DG00X_CLOCK_INTERNAL) || amdtp_stream_pcm_running(&dg00x->rx_stream) || amdtp_stream_pcm_running(&dg00x->tx_stream)) { err = snd_dg00x_stream_get_external_rate(dg00x, &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_dg00x_stream_lock_release(dg00x); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; snd_dg00x_stream_lock_release(dg00x); return 0; } static int pcm_capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dg00x *dg00x = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dg00x->mutex); dg00x->substreams_counter++; mutex_unlock(&dg00x->mutex); } amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params)); return 0; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dg00x *dg00x = substream->private_data; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dg00x->mutex); dg00x->substreams_counter++; mutex_unlock(&dg00x->mutex); } amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params)); return 0; } static int pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; mutex_lock(&dg00x->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); mutex_unlock(&dg00x->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; mutex_lock(&dg00x->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) dg00x->substreams_counter--; snd_dg00x_stream_stop_duplex(dg00x); mutex_unlock(&dg00x->mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&dg00x->tx_stream); mutex_unlock(&dg00x->mutex); return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); if (err >= 0) { amdtp_stream_pcm_prepare(&dg00x->rx_stream); amdtp_dot_reset(&dg00x->rx_stream); } mutex_unlock(&dg00x->mutex); return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dg00x *dg00x = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dg00x *dg00x = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; return amdtp_stream_pcm_pointer(&dg00x->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; return amdtp_stream_pcm_pointer(&dg00x->rx_stream); } int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) { static const struct snd_pcm_ops capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_capture_hw_params, .hw_free = pcm_capture_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, }; static const struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_playback_hw_params, .hw_free = pcm_playback_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; int err; err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm); if (err < 0) return err; pcm->private_data = dg00x; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", dg00x->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); return 0; }
static int pcm_init_hw_params(struct snd_bebob *bebob, struct snd_pcm_substream *substream) { int err; static const struct snd_pcm_hardware hw = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_FIFO_IN_FRAMES | SNDRV_PCM_INFO_JOINT_DUPLEX | /* for Open Sound System compatibility */ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER, /* set up later */ .rates = 0, .rate_min = UINT_MAX, .rate_max = 0, /* set up later */ .channels_min = UINT_MAX, .channels_max = 0, .buffer_bytes_max = 1024 * 1024 * 1024, .period_bytes_min = 256, .period_bytes_max = 1024 * 1024 * 1024 / 2, .periods_min = 2, .periods_max = 32, .fifo_size = 0, }; substream->runtime->hw = hw; substream->runtime->delay = substream->runtime->hw.fifo_size; /* add rule between channels and sampling rate */ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { prepare_rates(&substream->runtime->hw, bebob->tx_stream_formations); prepare_channels(&substream->runtime->hw, bebob->tx_stream_formations); substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_capture_channels, bebob, SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_capture_rate, bebob, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } else { prepare_rates(&substream->runtime->hw, bebob->rx_stream_formations); prepare_channels(&substream->runtime->hw, bebob->rx_stream_formations); substream->runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_playback_channels, bebob, SNDRV_PCM_HW_PARAM_RATE, -1); snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_playback_rate, bebob, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } /* AM824 in IEC 61883-6 can deliver 24bit data */ err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24); if (err < 0) goto end; /* * AMDTP functionality in firewire-lib require periods to be aligned to * 16 bit, or 24bit inner 32bit. */ err = snd_pcm_hw_constraint_step(substream->runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); if (err < 0) goto end; /* time for period constraint */ err = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 500, UINT_MAX); if (err < 0) goto end; err = 0; end: return err; } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_bebob_rate_spec *spec = bebob->spec->rate; unsigned int sampling_rate; bool internal; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) goto end; err = pcm_init_hw_params(bebob, substream); if (err < 0) goto err_locked; err = snd_bebob_stream_check_internal_clock(bebob, &internal); if (err < 0) goto err_locked; /* * When source of clock is internal or any PCM stream are running, * the available sampling rate is limited at current sampling rate. */ if (!internal || amdtp_stream_pcm_running(&bebob->tx_stream) || amdtp_stream_pcm_running(&bebob->rx_stream)) { err = spec->get(bebob, &sampling_rate); if (err < 0) goto err_locked; substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; } snd_pcm_set_sync(substream); end: return err; err_locked: snd_bebob_stream_lock_release(bebob); return err; } static int pcm_close(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_lock_release(bebob); return 0; } static int pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { return snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); } static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; snd_bebob_stream_stop_duplex(bebob); return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, &bebob->tx_stream, runtime->rate); if (err < 0) goto end; amdtp_stream_set_pcm_format(&bebob->tx_stream, runtime->format); amdtp_stream_pcm_prepare(&bebob->tx_stream); end: return err; } static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; int err; err = snd_bebob_stream_start_duplex(bebob, &bebob->rx_stream, runtime->rate); if (err < 0) goto end; amdtp_stream_set_pcm_format(&bebob->rx_stream, runtime->format); amdtp_stream_pcm_prepare(&bebob->rx_stream); end: return err; } static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->tx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL); break; default: return -EINVAL; } return 0; } static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_bebob *bebob = substream->private_data; switch (cmd) { case SNDRV_PCM_TRIGGER_START: amdtp_stream_pcm_trigger(&bebob->rx_stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL); break; default: return -EINVAL; } return 0; } static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; return amdtp_stream_pcm_pointer(&bebob->rx_stream); } static struct snd_pcm_ops pcm_capture_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_hw_params, .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, .page = snd_pcm_lib_get_vmalloc_page, }; static struct snd_pcm_ops pcm_playback_ops = { .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = pcm_hw_params, .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { struct snd_pcm *pcm; int err; err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm); if (err < 0) goto end; pcm->private_data = bebob; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); end: return err; }
/* * When the bit clock is input, limit the maximum rate according to the * Serial Clock Ratio Considerations section from the SSC documentation: * * The Transmitter and the Receiver can be programmed to operate * with the clock signals provided on either the TK or RK pins. * This allows the SSC to support many slave-mode data transfers. * In this case, the maximum clock speed allowed on the RK pin is: * - Peripheral clock divided by 2 if Receiver Frame Synchro is input * - Peripheral clock divided by 3 if Receiver Frame Synchro is output * In addition, the maximum clock speed allowed on the TK pin is: * - Peripheral clock divided by 6 if Transmit Frame Synchro is input * - Peripheral clock divided by 2 if Transmit Frame Synchro is output * * When the bit clock is output, limit the rate according to the * SSC divider restrictions. */ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct atmel_ssc_info *ssc_p = rule->private; struct ssc_device *ssc = ssc_p->ssc; struct snd_interval *i = hw_param_interval(params, rule->var); struct snd_interval t; struct snd_ratnum r = { .den_min = 1, .den_max = 4095, .den_step = 1, }; unsigned int num = 0, den = 0; int frame_size; int mck_div = 2; int ret; frame_size = snd_soc_params_to_frame_size(params); if (frame_size < 0) return frame_size; switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFS: if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE) && ssc->clk_from_rk_pin) /* Receiver Frame Synchro (i.e. capture) * is output (format is _CFS) and the RK pin * is used for input (format is _CBM_). */ mck_div = 3; break; case SND_SOC_DAIFMT_CBM_CFM: if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK) && !ssc->clk_from_rk_pin) /* Transmit Frame Synchro (i.e. playback) * is input (format is _CFM) and the TK pin * is used for input (format _CBM_ but not * using the RK pin). */ mck_div = 6; break; } switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: r.num = ssc_p->mck_rate / mck_div / frame_size; ret = snd_interval_ratnum(i, 1, &r, &num, &den); if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { params->rate_num = num; params->rate_den = den; } break; case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBM_CFM: t.min = 8000; t.max = ssc_p->mck_rate / mck_div / frame_size; t.openmin = t.openmax = 0; t.integer = 0; ret = snd_interval_refine(i, &t); break; default: ret = -EINVAL; break; } return ret; } /*-------------------------------------------------------------------------*\ * DAI functions \*-------------------------------------------------------------------------*/ /* * Startup. Only that one substream allowed in each direction. */ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; int ret; pr_debug("atmel_ssc_startup: SSC_SR=0x%x\n", ssc_readl(ssc_p->ssc->regs, SR)); /* Enable PMC peripheral clock for this SSC */ pr_debug("atmel_ssc_dai: Starting clock\n"); clk_enable(ssc_p->ssc->clk); ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk); /* Reset the SSC unless initialized to keep it in a clean state */ if (!ssc_p->initialized) ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dir = 0; dir_mask = SSC_DIR_MASK_PLAYBACK; } else { dir = 1; dir_mask = SSC_DIR_MASK_CAPTURE; } ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, atmel_ssc_hw_rule_rate, ssc_p, SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (ret < 0) { dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret); return ret; } dma_params = &ssc_dma_params[pdev->id][dir]; dma_params->ssc = ssc_p->ssc; dma_params->substream = substream; ssc_p->dma_params[dir] = dma_params; snd_soc_dai_set_dma_data(dai, substream, dma_params); spin_lock_irq(&ssc_p->lock); if (ssc_p->dir_mask & dir_mask) { spin_unlock_irq(&ssc_p->lock); return -EBUSY; } ssc_p->dir_mask |= dir_mask; spin_unlock_irq(&ssc_p->lock); return 0; } /* * Shutdown. Clear DMA parameters and shutdown the SSC if there * are no other substreams open. */ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = 0; else dir = 1; dma_params = ssc_p->dma_params[dir]; if (dma_params != NULL) { dma_params->ssc = NULL; dma_params->substream = NULL; ssc_p->dma_params[dir] = NULL; } dir_mask = 1 << dir; spin_lock_irq(&ssc_p->lock); ssc_p->dir_mask &= ~dir_mask; if (!ssc_p->dir_mask) { if (ssc_p->initialized) { free_irq(ssc_p->ssc->irq, ssc_p); ssc_p->initialized = 0; } /* Reset the SSC */ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); /* Clear the SSC dividers */ ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0; } spin_unlock_irq(&ssc_p->lock); /* Shutdown the SSC clock. */ pr_debug("atmel_ssc_dai: Stopping clock\n"); clk_disable(ssc_p->ssc->clk); } /* * Record the DAI format for use in hw_params(). */ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct platform_device *pdev = to_platform_device(cpu_dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; ssc_p->daifmt = fmt; return 0; } /* * Record SSC clock dividers for use in hw_params(). */ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) { struct platform_device *pdev = to_platform_device(cpu_dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; switch (div_id) { case ATMEL_SSC_CMR_DIV: /* * The same master clock divider is used for both * transmit and receive, so if a value has already * been set, it must match this value. */ if (ssc_p->dir_mask != (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE)) ssc_p->cmr_div = div; else if (ssc_p->cmr_div == 0) ssc_p->cmr_div = div; else if (div != ssc_p->cmr_div) return -EBUSY; break; case ATMEL_SSC_TCMR_PERIOD: ssc_p->tcmr_period = div; break; case ATMEL_SSC_RCMR_PERIOD: ssc_p->rcmr_period = div; break; default: return -EINVAL; } return 0; } /* * Configure the SSC. */ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); int id = pdev->id; struct atmel_ssc_info *ssc_p = &ssc_info[id]; struct ssc_device *ssc = ssc_p->ssc; struct atmel_pcm_dma_params *dma_params; int dir, channels, bits; u32 tfmr, rfmr, tcmr, rcmr; int ret; int fslen, fslen_ext; /* * Currently, there is only one set of dma params for * each direction. If more are added, this code will * have to be changed to select the proper set. */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = 0; else dir = 1; dma_params = ssc_p->dma_params[dir]; channels = params_channels(params); /* * Determine sample size in bits and the PDC increment. */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: bits = 8; dma_params->pdc_xfer_size = 1; break; case SNDRV_PCM_FORMAT_S16_LE: bits = 16; dma_params->pdc_xfer_size = 2; break; case SNDRV_PCM_FORMAT_S24_LE: bits = 24; dma_params->pdc_xfer_size = 4; break; case SNDRV_PCM_FORMAT_S32_LE: bits = 32; dma_params->pdc_xfer_size = 4; break; default: printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format"); return -EINVAL; } /* * Compute SSC register settings. */ switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) { case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS: /* * I2S format, SSC provides BCLK and LRC clocks. * * The SSC transmit and receive clocks are generated * from the MCK divider, and the BCLK signal * is output on the SSC TK line. */ if (bits > 16 && !ssc->pdata->has_fslen_ext) { dev_err(dai->dev, "sample size %d is too large for SSC device\n", bits); return -EINVAL; } fslen_ext = (bits - 1) / 16; fslen = (bits - 1) % 16; rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, SSC_CKS_DIV); rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext) | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) | SSC_BF(RFMR_FSLEN, fslen) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_FALLING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) | SSC_BF(TCMR_CKS, SSC_CKS_DIV); tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext) | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) | SSC_BF(TFMR_FSLEN, fslen) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: /* I2S format, CODEC supplies BCLK and LRC clocks. */ rcmr = SSC_BF(RCMR_PERIOD, 0) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(RFMR_FSLEN, 0) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, 0) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_FALLING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(TFMR_FSLEN, 0) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS: /* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */ if (bits > 16 && !ssc->pdata->has_fslen_ext) { dev_err(dai->dev, "sample size %d is too large for SSC device\n", bits); return -EINVAL; } fslen_ext = (bits - 1) / 16; fslen = (bits - 1) % 16; rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_FALLING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext) | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) | SSC_BF(RFMR_FSLEN, fslen) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_FALLING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) | SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext) | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) | SSC_BF(TFMR_FSLEN, fslen) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: /* * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks. * * The SSC transmit and receive clocks are generated from the * MCK divider, and the BCLK signal is output * on the SSC TK line. */ rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) | SSC_BF(RCMR_STTDLY, 1) | SSC_BF(RCMR_START, SSC_START_RISING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, SSC_CKS_DIV); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) | SSC_BF(RFMR_FSLEN, 0) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) | SSC_BF(TCMR_STTDLY, 1) | SSC_BF(TCMR_START, SSC_START_RISING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) | SSC_BF(TCMR_CKS, SSC_CKS_DIV); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) | SSC_BF(TFMR_FSLEN, 0) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM: /* * DSP/PCM Mode A format, CODEC supplies BCLK and LRC clocks. * * Data is transferred on first BCLK after LRC pulse rising * edge.If stereo, the right channel data is contiguous with * the left channel data. */ rcmr = SSC_BF(RCMR_PERIOD, 0) | SSC_BF(RCMR_STTDLY, START_DELAY) | SSC_BF(RCMR_START, SSC_START_RISING_RF) | SSC_BF(RCMR_CKI, SSC_CKI_RISING) | SSC_BF(RCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_PIN : SSC_CKS_CLOCK); rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(RFMR_FSLEN, 0) | SSC_BF(RFMR_DATNB, (channels - 1)) | SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_LOOP, 0) | SSC_BF(RFMR_DATLEN, (bits - 1)); tcmr = SSC_BF(TCMR_PERIOD, 0) | SSC_BF(TCMR_STTDLY, START_DELAY) | SSC_BF(TCMR_START, SSC_START_RISING_RF) | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) | SSC_BF(TCMR_CKO, SSC_CKO_NONE) | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ? SSC_CKS_CLOCK : SSC_CKS_PIN); tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) | SSC_BF(TFMR_FSDEN, 0) | SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) | SSC_BF(TFMR_FSLEN, 0) | SSC_BF(TFMR_DATNB, (channels - 1)) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATDEF, 0) | SSC_BF(TFMR_DATLEN, (bits - 1)); break; default: printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n", ssc_p->daifmt); return -EINVAL; } pr_debug("atmel_ssc_hw_params: " "RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr); if (!ssc_p->initialized) { if (!ssc_p->ssc->pdata->use_dma) { ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0); ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0); ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0); ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0); ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0); ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0); ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0); ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0); } ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0, ssc_p->name, ssc_p); if (ret < 0) { printk(KERN_WARNING "atmel_ssc_dai: request_irq failure\n"); pr_debug("Atmel_ssc_dai: Stoping clock\n"); clk_disable(ssc_p->ssc->clk); return ret; } ssc_p->initialized = 1; } /* set SSC clock mode register */ ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div); /* set receive clock mode and format */ ssc_writel(ssc_p->ssc->regs, RCMR, rcmr); ssc_writel(ssc_p->ssc->regs, RFMR, rfmr); /* set transmit clock mode and format */ ssc_writel(ssc_p->ssc->regs, TCMR, tcmr); ssc_writel(ssc_p->ssc->regs, TFMR, tfmr); pr_debug("atmel_ssc_dai,hw_params: SSC initialized\n"); return 0; } static int atmel_ssc_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = 0; else dir = 1; dma_params = ssc_p->dma_params[dir]; ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error); pr_debug("%s enabled SSC_SR=0x%08x\n", dir ? "receive" : "transmit", ssc_readl(ssc_p->ssc->regs, SR)); return 0; } static int atmel_ssc_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct platform_device *pdev = to_platform_device(dai->dev); struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id]; struct atmel_pcm_dma_params *dma_params; int dir; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = 0; else dir = 1; dma_params = ssc_p->dma_params[dir]; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable); break; default: ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable); break; } return 0; } #ifdef CONFIG_PM static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) { struct atmel_ssc_info *ssc_p; struct platform_device *pdev = to_platform_device(cpu_dai->dev); if (!cpu_dai->active) return 0; ssc_p = &ssc_info[pdev->id]; /* Save the status register before disabling transmit and receive */ ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR); ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS)); /* Save the current interrupt mask, then disable unmasked interrupts */ ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR); ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr); ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR); ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR); ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR); ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR); ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR); return 0; } static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) { struct atmel_ssc_info *ssc_p; struct platform_device *pdev = to_platform_device(cpu_dai->dev); u32 cr; if (!cpu_dai->active) return 0; ssc_p = &ssc_info[pdev->id]; /* restore SSC register settings */ ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr); ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr); ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr); ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr); ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr); /* re-enable interrupts */ ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr); /* Re-enable receive and transmit as appropriate */ cr = 0; cr |= (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0; cr |= (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0; ssc_writel(ssc_p->ssc->regs, CR, cr); return 0; } #else /* CONFIG_PM */ # define atmel_ssc_suspend NULL # define atmel_ssc_resume NULL #endif /* CONFIG_PM */ #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { .startup = atmel_ssc_startup, .shutdown = atmel_ssc_shutdown, .prepare = atmel_ssc_prepare, .trigger = atmel_ssc_trigger, .hw_params = atmel_ssc_hw_params, .set_fmt = atmel_ssc_set_dai_fmt, .set_clkdiv = atmel_ssc_set_dai_clkdiv, }; static struct snd_soc_dai_driver atmel_ssc_dai = { .suspend = atmel_ssc_suspend, .resume = atmel_ssc_resume, .playback = { .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, .rate_max = 384000, .formats = ATMEL_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, .rate_max = 384000, .formats = ATMEL_SSC_FORMATS,}, .ops = &atmel_ssc_dai_ops,