162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// soc-util.c -- ALSA SoC Audio Layer utility functions 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright 2009 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 862306a36Sopenharmony_ci// Liam Girdwood <lrg@slimlogic.co.uk> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <linux/math.h> 1362306a36Sopenharmony_ci#include <sound/core.h> 1462306a36Sopenharmony_ci#include <sound/pcm.h> 1562306a36Sopenharmony_ci#include <sound/pcm_params.h> 1662306a36Sopenharmony_ci#include <sound/soc.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciint snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci return sample_size * channels * tdm_slots; 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_calc_frame_size); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciint snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci int sample_size; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci sample_size = snd_pcm_format_width(params_format(params)); 2962306a36Sopenharmony_ci if (sample_size < 0) 3062306a36Sopenharmony_ci return sample_size; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return snd_soc_calc_frame_size(sample_size, params_channels(params), 3362306a36Sopenharmony_ci 1); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_params_to_frame_size); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciint snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci return fs * snd_soc_calc_frame_size(sample_size, channels, tdm_slots); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_calc_bclk); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciint snd_soc_params_to_bclk(struct snd_pcm_hw_params *params) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci int ret; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci ret = snd_soc_params_to_frame_size(params); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (ret > 0) 5062306a36Sopenharmony_ci return ret * params_rate(params); 5162306a36Sopenharmony_ci else 5262306a36Sopenharmony_ci return ret; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_params_to_bclk); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * snd_soc_tdm_params_to_bclk - calculate bclk from params and tdm slot info. 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * Calculate the bclk from the params sample rate, the tdm slot count and the 6062306a36Sopenharmony_ci * tdm slot width. Optionally round-up the slot count to a given multiple. 6162306a36Sopenharmony_ci * Either or both of tdm_width and tdm_slots can be 0. 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * If tdm_width == 0: use params_width() as the slot width. 6462306a36Sopenharmony_ci * If tdm_slots == 0: use params_channels() as the slot count. 6562306a36Sopenharmony_ci * 6662306a36Sopenharmony_ci * If slot_multiple > 1 the slot count (or params_channels() if tdm_slots == 0) 6762306a36Sopenharmony_ci * will be rounded up to a multiple of slot_multiple. This is mainly useful for 6862306a36Sopenharmony_ci * I2S mode, which has a left and right phase so the number of slots is always 6962306a36Sopenharmony_ci * a multiple of 2. 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * If tdm_width == 0 && tdm_slots == 0 && slot_multiple < 2, this is equivalent 7262306a36Sopenharmony_ci * to calling snd_soc_params_to_bclk(). 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * @params: Pointer to struct_pcm_hw_params. 7562306a36Sopenharmony_ci * @tdm_width: Width in bits of the tdm slots. Must be >= 0. 7662306a36Sopenharmony_ci * @tdm_slots: Number of tdm slots per frame. Must be >= 0. 7762306a36Sopenharmony_ci * @slot_multiple: If >1 roundup slot count to a multiple of this value. 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * Return: bclk frequency in Hz, else a negative error code if params format 8062306a36Sopenharmony_ci * is invalid. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ciint snd_soc_tdm_params_to_bclk(struct snd_pcm_hw_params *params, 8362306a36Sopenharmony_ci int tdm_width, int tdm_slots, int slot_multiple) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci if (!tdm_slots) 8662306a36Sopenharmony_ci tdm_slots = params_channels(params); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (slot_multiple > 1) 8962306a36Sopenharmony_ci tdm_slots = roundup(tdm_slots, slot_multiple); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (!tdm_width) { 9262306a36Sopenharmony_ci tdm_width = snd_pcm_format_width(params_format(params)); 9362306a36Sopenharmony_ci if (tdm_width < 0) 9462306a36Sopenharmony_ci return tdm_width; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return snd_soc_calc_bclk(params_rate(params), tdm_width, 1, tdm_slots); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_tdm_params_to_bclk); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic const struct snd_pcm_hardware dummy_dma_hardware = { 10262306a36Sopenharmony_ci /* Random values to keep userspace happy when checking constraints */ 10362306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_INTERLEAVED | 10462306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER, 10562306a36Sopenharmony_ci .buffer_bytes_max = 128*1024, 10662306a36Sopenharmony_ci .period_bytes_min = PAGE_SIZE, 10762306a36Sopenharmony_ci .period_bytes_max = PAGE_SIZE*2, 10862306a36Sopenharmony_ci .periods_min = 2, 10962306a36Sopenharmony_ci .periods_max = 128, 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic const struct snd_soc_component_driver dummy_platform; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int dummy_dma_open(struct snd_soc_component *component, 11662306a36Sopenharmony_ci struct snd_pcm_substream *substream) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 11962306a36Sopenharmony_ci int i; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * If there are other components associated with rtd, we shouldn't 12362306a36Sopenharmony_ci * override their hwparams 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci for_each_rtd_components(rtd, i, component) { 12662306a36Sopenharmony_ci if (component->driver == &dummy_platform) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* BE's dont need dummy params */ 13162306a36Sopenharmony_ci if (!rtd->dai_link->no_pcm) 13262306a36Sopenharmony_ci snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic const struct snd_soc_component_driver dummy_platform = { 13862306a36Sopenharmony_ci .open = dummy_dma_open, 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic const struct snd_soc_component_driver dummy_codec = { 14262306a36Sopenharmony_ci .idle_bias_on = 1, 14362306a36Sopenharmony_ci .use_pmdown_time = 1, 14462306a36Sopenharmony_ci .endianness = 1, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define STUB_RATES SNDRV_PCM_RATE_8000_384000 14862306a36Sopenharmony_ci#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ 14962306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U8 | \ 15062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | \ 15162306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U16_LE | \ 15262306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | \ 15362306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_3LE | \ 15462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U24_LE | \ 15562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S32_LE | \ 15662306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_U32_LE | \ 15762306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * Select these from Sound Card Manually 16162306a36Sopenharmony_ci * SND_SOC_POSSIBLE_DAIFMT_CBP_CFP 16262306a36Sopenharmony_ci * SND_SOC_POSSIBLE_DAIFMT_CBP_CFC 16362306a36Sopenharmony_ci * SND_SOC_POSSIBLE_DAIFMT_CBC_CFP 16462306a36Sopenharmony_ci * SND_SOC_POSSIBLE_DAIFMT_CBC_CFC 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic u64 dummy_dai_formats = 16762306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_I2S | 16862306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | 16962306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_LEFT_J | 17062306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_DSP_A | 17162306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_DSP_B | 17262306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_AC97 | 17362306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_PDM | 17462306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_GATED | 17562306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_CONT | 17662306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_NB_NF | 17762306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_NB_IF | 17862306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_IB_NF | 17962306a36Sopenharmony_ci SND_SOC_POSSIBLE_DAIFMT_IB_IF; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const struct snd_soc_dai_ops dummy_dai_ops = { 18262306a36Sopenharmony_ci .auto_selectable_formats = &dummy_dai_formats, 18362306a36Sopenharmony_ci .num_auto_selectable_formats = 1, 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci * The dummy CODEC is only meant to be used in situations where there is no 18862306a36Sopenharmony_ci * actual hardware. 18962306a36Sopenharmony_ci * 19062306a36Sopenharmony_ci * If there is actual hardware even if it does not have a control bus 19162306a36Sopenharmony_ci * the hardware will still have constraints like supported samplerates, etc. 19262306a36Sopenharmony_ci * which should be modelled. And the data flow graph also should be modelled 19362306a36Sopenharmony_ci * using DAPM. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic struct snd_soc_dai_driver dummy_dai = { 19662306a36Sopenharmony_ci .name = "snd-soc-dummy-dai", 19762306a36Sopenharmony_ci .playback = { 19862306a36Sopenharmony_ci .stream_name = "Playback", 19962306a36Sopenharmony_ci .channels_min = 1, 20062306a36Sopenharmony_ci .channels_max = 384, 20162306a36Sopenharmony_ci .rates = STUB_RATES, 20262306a36Sopenharmony_ci .formats = STUB_FORMATS, 20362306a36Sopenharmony_ci }, 20462306a36Sopenharmony_ci .capture = { 20562306a36Sopenharmony_ci .stream_name = "Capture", 20662306a36Sopenharmony_ci .channels_min = 1, 20762306a36Sopenharmony_ci .channels_max = 384, 20862306a36Sopenharmony_ci .rates = STUB_RATES, 20962306a36Sopenharmony_ci .formats = STUB_FORMATS, 21062306a36Sopenharmony_ci }, 21162306a36Sopenharmony_ci .ops = &dummy_dai_ops, 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ciint snd_soc_dai_is_dummy(struct snd_soc_dai *dai) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci if (dai->driver == &dummy_dai) 21762306a36Sopenharmony_ci return 1; 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dai_is_dummy); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciint snd_soc_component_is_dummy(struct snd_soc_component *component) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci return ((component->driver == &dummy_platform) || 22562306a36Sopenharmony_ci (component->driver == &dummy_codec)); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistruct snd_soc_dai_link_component snd_soc_dummy_dlc = { 22962306a36Sopenharmony_ci .of_node = NULL, 23062306a36Sopenharmony_ci .dai_name = "snd-soc-dummy-dai", 23162306a36Sopenharmony_ci .name = "snd-soc-dummy", 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_dummy_dlc); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int snd_soc_dummy_probe(struct platform_device *pdev) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci int ret; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 24062306a36Sopenharmony_ci &dummy_codec, &dummy_dai, 1); 24162306a36Sopenharmony_ci if (ret < 0) 24262306a36Sopenharmony_ci return ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, &dummy_platform, 24562306a36Sopenharmony_ci NULL, 0); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic struct platform_driver soc_dummy_driver = { 25162306a36Sopenharmony_ci .driver = { 25262306a36Sopenharmony_ci .name = "snd-soc-dummy", 25362306a36Sopenharmony_ci }, 25462306a36Sopenharmony_ci .probe = snd_soc_dummy_probe, 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic struct platform_device *soc_dummy_dev; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciint __init snd_soc_util_init(void) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci soc_dummy_dev = 26462306a36Sopenharmony_ci platform_device_register_simple("snd-soc-dummy", -1, NULL, 0); 26562306a36Sopenharmony_ci if (IS_ERR(soc_dummy_dev)) 26662306a36Sopenharmony_ci return PTR_ERR(soc_dummy_dev); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci ret = platform_driver_register(&soc_dummy_driver); 26962306a36Sopenharmony_ci if (ret != 0) 27062306a36Sopenharmony_ci platform_device_unregister(soc_dummy_dev); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_civoid snd_soc_util_exit(void) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci platform_driver_unregister(&soc_dummy_driver); 27862306a36Sopenharmony_ci platform_device_unregister(soc_dummy_dev); 27962306a36Sopenharmony_ci} 280