diff --git a/include/sound/soc.h b/include/sound/soc.h index 2314103985d4..f10f48fad5d5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -970,30 +970,6 @@ struct snd_soc_dai_link { enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */ - /* Keep DAI active over suspend */ - unsigned int ignore_suspend:1; - - /* Symmetry requirements */ - unsigned int symmetric_rates:1; - unsigned int symmetric_channels:1; - unsigned int symmetric_samplebits:1; - - /* Mark this pcm with non atomic ops */ - bool nonatomic; - - /* Do not create a PCM for this DAI link (Backend link) */ - unsigned int no_pcm:1; - - /* This DAI link can route to other DAI links at runtime (Frontend)*/ - unsigned int dynamic:1; - - /* DPCM capture and Playback support */ - unsigned int dpcm_capture:1; - unsigned int dpcm_playback:1; - - /* pmdown_time is ignored at stop */ - unsigned int ignore_pmdown_time:1; - /* codec/machine specific init - e.g. add machine controls */ int (*init)(struct snd_soc_pcm_runtime *rtd); @@ -1008,6 +984,33 @@ struct snd_soc_dai_link { /* For unidirectional dai links */ bool playback_only; bool capture_only; + + /* Mark this pcm with non atomic ops */ + bool nonatomic; + + /* Keep DAI active over suspend */ + unsigned int ignore_suspend:1; + + /* Symmetry requirements */ + unsigned int symmetric_rates:1; + unsigned int symmetric_channels:1; + unsigned int symmetric_samplebits:1; + + /* Do not create a PCM for this DAI link (Backend link) */ + unsigned int no_pcm:1; + + /* This DAI link can route to other DAI links at runtime (Frontend)*/ + unsigned int dynamic:1; + + /* DPCM capture and Playback support */ + unsigned int dpcm_capture:1; + unsigned int dpcm_playback:1; + + /* DPCM used FE & BE merged format */ + unsigned int dpcm_merged_format:1; + + /* pmdown_time is ignored at stop */ + unsigned int ignore_pmdown_time:1; }; struct snd_soc_codec_conf { diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 23c91fa65ab8..d79349434a9a 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -107,6 +107,7 @@ struct davinci_mcasp { #endif struct davinci_mcasp_ruledata ruledata[2]; + struct snd_pcm_hw_constraint_list chconstr[2]; }; static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset, @@ -915,15 +916,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, * the machine driver, we need to calculate the ratio. */ if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { - int channels = params_channels(params); + int slots = mcasp->tdm_slots; int rate = params_rate(params); int sbits = params_width(params); int ppm, div; - if (channels > mcasp->tdm_slots) - channels = mcasp->tdm_slots; - - div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*channels, + div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots, &ppm); if (ppm) dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", @@ -1024,31 +1022,36 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval *ri = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); int sbits = params_width(params); - int channels = params_channels(params); - unsigned int list[ARRAY_SIZE(davinci_mcasp_dai_rates)]; - int i, count = 0; + int slots = rd->mcasp->tdm_slots; + struct snd_interval range; + int i; - if (channels > rd->mcasp->tdm_slots) - channels = rd->mcasp->tdm_slots; + snd_interval_any(&range); + range.empty = 1; for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) { - if (ri->min <= davinci_mcasp_dai_rates[i] && - ri->max >= davinci_mcasp_dai_rates[i]) { - uint bclk_freq = sbits*channels* + if (snd_interval_test(ri, davinci_mcasp_dai_rates[i])) { + uint bclk_freq = sbits*slots* davinci_mcasp_dai_rates[i]; int ppm; davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); - if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) - list[count++] = davinci_mcasp_dai_rates[i]; + if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { + if (range.empty) { + range.min = davinci_mcasp_dai_rates[i]; + range.empty = 0; + } + range.max = davinci_mcasp_dai_rates[i]; + } } } - dev_dbg(rd->mcasp->dev, - "%d frequencies (%d-%d) for %d sbits and %d channels\n", - count, ri->min, ri->max, sbits, channels); - return snd_interval_list(hw_param_interval(params, rule->var), - count, list, 0); + dev_dbg(rd->mcasp->dev, + "Frequencies %d-%d -> %d-%d for %d sbits and %d tdm slots\n", + ri->min, ri->max, range.min, range.max, sbits, slots); + + return snd_interval_refine(hw_param_interval(params, rule->var), + &range); } static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, @@ -1058,17 +1061,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); struct snd_mask nfmt; int rate = params_rate(params); - int channels = params_channels(params); + int slots = rd->mcasp->tdm_slots; int i, count = 0; snd_mask_none(&nfmt); - if (channels > rd->mcasp->tdm_slots) - channels = rd->mcasp->tdm_slots; - for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { if (snd_mask_test(fmt, i)) { - uint bclk_freq = snd_pcm_format_width(i)*channels*rate; + uint bclk_freq = snd_pcm_format_width(i)*slots*rate; int ppm; davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); @@ -1079,51 +1079,12 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, } } dev_dbg(rd->mcasp->dev, - "%d possible sample format for %d Hz and %d channels\n", - count, rate, channels); + "%d possible sample format for %d Hz and %d tdm slots\n", + count, rate, slots); return snd_mask_refine(fmt, &nfmt); } -static int davinci_mcasp_hw_rule_channels(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct davinci_mcasp_ruledata *rd = rule->private; - struct snd_interval *ci = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - int sbits = params_width(params); - int rate = params_rate(params); - int max_chan_per_wire = rd->mcasp->tdm_slots < ci->max ? - rd->mcasp->tdm_slots : ci->max; - unsigned int list[ci->max - ci->min + 1]; - int c1, c, count = 0; - - for (c1 = ci->min; c1 <= max_chan_per_wire; c1++) { - uint bclk_freq = c1*sbits*rate; - int ppm; - - davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); - if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { - /* If we can use all tdm_slots, we can put any - amount of channels to remaining wires as - long as they fit in. */ - if (c1 == rd->mcasp->tdm_slots) { - for (c = c1; c <= rd->serializers*c1 && - c <= ci->max; c++) - list[count++] = c; - } else { - list[count++] = c1; - } - } - } - dev_dbg(rd->mcasp->dev, - "%d possible channel counts (%d-%d) for %d Hz and %d sbits\n", - count, ci->min, ci->max, rate, sbits); - - return snd_interval_list(hw_param_interval(params, rule->var), - count, list, 0); -} - static int davinci_mcasp_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { @@ -1167,6 +1128,11 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_CHANNELS, 2, max_channels); + if (mcasp->chconstr[substream->stream].count) + snd_pcm_hw_constraint_list(substream->runtime, + 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &mcasp->chconstr[substream->stream]); + /* * If we rely on implicit BCLK divider setting we should * set constraints based on what we can provide. @@ -1180,24 +1146,14 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, SNDRV_PCM_HW_PARAM_RATE, davinci_mcasp_hw_rule_rate, ruledata, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + SNDRV_PCM_HW_PARAM_FORMAT, -1); if (ret) return ret; ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, davinci_mcasp_hw_rule_format, ruledata, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (ret) - return ret; - ret = snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - davinci_mcasp_hw_rule_channels, - ruledata, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_FORMAT, -1); + SNDRV_PCM_HW_PARAM_RATE, -1); if (ret) return ret; } @@ -1556,6 +1512,59 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( return pdata; } +/* All serializers must have equal number of channels */ +static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, + struct snd_pcm_hw_constraint_list *cl, + int serializers) +{ + unsigned int *list; + int i, count = 0; + + if (serializers <= 1) + return 0; + + list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) * + (mcasp->tdm_slots + serializers - 2), + GFP_KERNEL); + if (!list) + return -ENOMEM; + + for (i = 2; i <= mcasp->tdm_slots; i++) + list[count++] = i; + + for (i = 2; i <= serializers; i++) + list[count++] = i*mcasp->tdm_slots; + + cl->count = count; + cl->list = list; + + return 0; +} + + +static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp) +{ + int rx_serializers = 0, tx_serializers = 0, ret, i; + + for (i = 0; i < mcasp->num_serializer; i++) + if (mcasp->serial_dir[i] == TX_MODE) + tx_serializers++; + else if (mcasp->serial_dir[i] == RX_MODE) + rx_serializers++; + + ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ + SNDRV_PCM_STREAM_PLAYBACK], + tx_serializers); + if (ret) + return ret; + + ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ + SNDRV_PCM_STREAM_CAPTURE], + rx_serializers); + + return ret; +} + static int davinci_mcasp_probe(struct platform_device *pdev) { struct snd_dmaengine_dai_dma_data *dma_data; @@ -1739,6 +1748,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE; } + ret = davinci_mcasp_init_ch_constraints(mcasp); + if (ret) + goto err; + dev_set_drvdata(&pdev->dev, mcasp); mcasp_reparent_fck(pdev); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 35fe58f4fa86..256b9c91aa94 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1485,30 +1485,67 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) } static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime, - struct snd_soc_pcm_stream *stream) + struct snd_soc_pcm_stream *stream, + u64 formats) { runtime->hw.rate_min = stream->rate_min; runtime->hw.rate_max = stream->rate_max; runtime->hw.channels_min = stream->channels_min; runtime->hw.channels_max = stream->channels_max; if (runtime->hw.formats) - runtime->hw.formats &= stream->formats; + runtime->hw.formats &= formats & stream->formats; else - runtime->hw.formats = stream->formats; + runtime->hw.formats = formats & stream->formats; runtime->hw.rates = stream->rates; } +static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + struct snd_soc_dpcm *dpcm; + u64 formats = ULLONG_MAX; + int stream = substream->stream; + + if (!fe->dai_link->dpcm_merged_format) + return formats; + + /* + * It returns merged BE codec format + * if FE want to use it (= dpcm_merged_format) + */ + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai_driver *codec_dai_drv; + struct snd_soc_pcm_stream *codec_stream; + int i; + + for (i = 0; i < be->num_codecs; i++) { + codec_dai_drv = be->codec_dais[i]->driver; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + codec_stream = &codec_dai_drv->playback; + else + codec_stream = &codec_dai_drv->capture; + + formats &= codec_stream->formats; + } + } + + return formats; +} + static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver; + u64 format = dpcm_runtime_base_format(substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback); + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback, format); else - dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture); + dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture, format); } static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);