162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2018 BayLibre, SAS. 462306a36Sopenharmony_ci// Author: Jerome Brunet <jbrunet@baylibre.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/of_platform.h> 962306a36Sopenharmony_ci#include <sound/pcm_params.h> 1062306a36Sopenharmony_ci#include <sound/soc.h> 1162306a36Sopenharmony_ci#include <sound/soc-dai.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "axg-tdm.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* Maximum bit clock frequency according the datasheets */ 1662306a36Sopenharmony_ci#define MAX_SCLK 100000000 /* Hz */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cienum { 1962306a36Sopenharmony_ci TDM_IFACE_PAD, 2062306a36Sopenharmony_ci TDM_IFACE_LOOPBACK, 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic unsigned int axg_tdm_slots_total(u32 *mask) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned int slots = 0; 2662306a36Sopenharmony_ci int i; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (!mask) 2962306a36Sopenharmony_ci return 0; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* Count the total number of slots provided by all 4 lanes */ 3262306a36Sopenharmony_ci for (i = 0; i < AXG_TDM_NUM_LANES; i++) 3362306a36Sopenharmony_ci slots += hweight32(mask[i]); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return slots; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciint axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask, 3962306a36Sopenharmony_ci u32 *rx_mask, unsigned int slots, 4062306a36Sopenharmony_ci unsigned int slot_width) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 4362306a36Sopenharmony_ci struct axg_tdm_stream *tx = snd_soc_dai_dma_data_get_playback(dai); 4462306a36Sopenharmony_ci struct axg_tdm_stream *rx = snd_soc_dai_dma_data_get_capture(dai); 4562306a36Sopenharmony_ci unsigned int tx_slots, rx_slots; 4662306a36Sopenharmony_ci unsigned int fmt = 0; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci tx_slots = axg_tdm_slots_total(tx_mask); 4962306a36Sopenharmony_ci rx_slots = axg_tdm_slots_total(rx_mask); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* We should at least have a slot for a valid interface */ 5262306a36Sopenharmony_ci if (!tx_slots && !rx_slots) { 5362306a36Sopenharmony_ci dev_err(dai->dev, "interface has no slot\n"); 5462306a36Sopenharmony_ci return -EINVAL; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci iface->slots = slots; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci switch (slot_width) { 6062306a36Sopenharmony_ci case 0: 6162306a36Sopenharmony_ci slot_width = 32; 6262306a36Sopenharmony_ci fallthrough; 6362306a36Sopenharmony_ci case 32: 6462306a36Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S32_LE; 6562306a36Sopenharmony_ci fallthrough; 6662306a36Sopenharmony_ci case 24: 6762306a36Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S24_LE; 6862306a36Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S20_LE; 6962306a36Sopenharmony_ci fallthrough; 7062306a36Sopenharmony_ci case 16: 7162306a36Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S16_LE; 7262306a36Sopenharmony_ci fallthrough; 7362306a36Sopenharmony_ci case 8: 7462306a36Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S8; 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci default: 7762306a36Sopenharmony_ci dev_err(dai->dev, "unsupported slot width: %d\n", slot_width); 7862306a36Sopenharmony_ci return -EINVAL; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci iface->slot_width = slot_width; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Amend the dai driver and let dpcm merge do its job */ 8462306a36Sopenharmony_ci if (tx) { 8562306a36Sopenharmony_ci tx->mask = tx_mask; 8662306a36Sopenharmony_ci dai->driver->playback.channels_max = tx_slots; 8762306a36Sopenharmony_ci dai->driver->playback.formats = fmt; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (rx) { 9162306a36Sopenharmony_ci rx->mask = rx_mask; 9262306a36Sopenharmony_ci dai->driver->capture.channels_max = rx_slots; 9362306a36Sopenharmony_ci dai->driver->capture.formats = fmt; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(axg_tdm_set_tdm_slots); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int axg_tdm_iface_set_sysclk(struct snd_soc_dai *dai, int clk_id, 10162306a36Sopenharmony_ci unsigned int freq, int dir) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 10462306a36Sopenharmony_ci int ret = -ENOTSUPP; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (dir == SND_SOC_CLOCK_OUT && clk_id == 0) { 10762306a36Sopenharmony_ci if (!iface->mclk) { 10862306a36Sopenharmony_ci dev_warn(dai->dev, "master clock not provided\n"); 10962306a36Sopenharmony_ci } else { 11062306a36Sopenharmony_ci ret = clk_set_rate(iface->mclk, freq); 11162306a36Sopenharmony_ci if (!ret) 11262306a36Sopenharmony_ci iface->mclk_rate = freq; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return ret; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { 12462306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FP: 12562306a36Sopenharmony_ci if (!iface->mclk) { 12662306a36Sopenharmony_ci dev_err(dai->dev, "cpu clock master: mclk missing\n"); 12762306a36Sopenharmony_ci return -ENODEV; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FC: 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci case SND_SOC_DAIFMT_BP_FC: 13562306a36Sopenharmony_ci case SND_SOC_DAIFMT_BC_FP: 13662306a36Sopenharmony_ci dev_err(dai->dev, "only CBS_CFS and CBM_CFM are supported\n"); 13762306a36Sopenharmony_ci fallthrough; 13862306a36Sopenharmony_ci default: 13962306a36Sopenharmony_ci return -EINVAL; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci iface->fmt = fmt; 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int axg_tdm_iface_startup(struct snd_pcm_substream *substream, 14762306a36Sopenharmony_ci struct snd_soc_dai *dai) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 15062306a36Sopenharmony_ci struct axg_tdm_stream *ts = 15162306a36Sopenharmony_ci snd_soc_dai_get_dma_data(dai, substream); 15262306a36Sopenharmony_ci int ret; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!axg_tdm_slots_total(ts->mask)) { 15562306a36Sopenharmony_ci dev_err(dai->dev, "interface has not slots\n"); 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (snd_soc_component_active(dai->component)) { 16062306a36Sopenharmony_ci /* Apply component wide rate symmetry */ 16162306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_single(substream->runtime, 16262306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 16362306a36Sopenharmony_ci iface->rate); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci } else { 16662306a36Sopenharmony_ci /* Limit rate according to the slot number and width */ 16762306a36Sopenharmony_ci unsigned int max_rate = 16862306a36Sopenharmony_ci MAX_SCLK / (iface->slots * iface->slot_width); 16962306a36Sopenharmony_ci ret = snd_pcm_hw_constraint_minmax(substream->runtime, 17062306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 17162306a36Sopenharmony_ci 0, max_rate); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (ret < 0) 17562306a36Sopenharmony_ci dev_err(dai->dev, "can't set iface rate constraint\n"); 17662306a36Sopenharmony_ci else 17762306a36Sopenharmony_ci ret = 0; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return ret; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream, 18362306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 18462306a36Sopenharmony_ci struct snd_soc_dai *dai) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 18762306a36Sopenharmony_ci struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); 18862306a36Sopenharmony_ci unsigned int channels = params_channels(params); 18962306a36Sopenharmony_ci unsigned int width = params_width(params); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Save rate and sample_bits for component symmetry */ 19262306a36Sopenharmony_ci iface->rate = params_rate(params); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Make sure this interface can cope with the stream */ 19562306a36Sopenharmony_ci if (axg_tdm_slots_total(ts->mask) < channels) { 19662306a36Sopenharmony_ci dev_err(dai->dev, "not enough slots for channels\n"); 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (iface->slot_width < width) { 20162306a36Sopenharmony_ci dev_err(dai->dev, "incompatible slots width for stream\n"); 20262306a36Sopenharmony_ci return -EINVAL; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Save the parameter for tdmout/tdmin widgets */ 20662306a36Sopenharmony_ci ts->physical_width = params_physical_width(params); 20762306a36Sopenharmony_ci ts->width = params_width(params); 20862306a36Sopenharmony_ci ts->channels = params_channels(params); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int axg_tdm_iface_set_lrclk(struct snd_soc_dai *dai, 21462306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 21762306a36Sopenharmony_ci unsigned int ratio_num; 21862306a36Sopenharmony_ci int ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = clk_set_rate(iface->lrclk, params_rate(params)); 22162306a36Sopenharmony_ci if (ret) { 22262306a36Sopenharmony_ci dev_err(dai->dev, "setting sample clock failed: %d\n", ret); 22362306a36Sopenharmony_ci return ret; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 22762306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 22862306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 22962306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 23062306a36Sopenharmony_ci /* 50% duty cycle ratio */ 23162306a36Sopenharmony_ci ratio_num = 1; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 23562306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 23662306a36Sopenharmony_ci /* 23762306a36Sopenharmony_ci * A zero duty cycle ratio will result in setting the mininum 23862306a36Sopenharmony_ci * ratio possible which, for this clock, is 1 cycle of the 23962306a36Sopenharmony_ci * parent bclk clock high and the rest low, This is exactly 24062306a36Sopenharmony_ci * what we want here. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci ratio_num = 0; 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci default: 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = clk_set_duty_cycle(iface->lrclk, ratio_num, 2); 25062306a36Sopenharmony_ci if (ret) { 25162306a36Sopenharmony_ci dev_err(dai->dev, 25262306a36Sopenharmony_ci "setting sample clock duty cycle failed: %d\n", ret); 25362306a36Sopenharmony_ci return ret; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Set sample clock inversion */ 25762306a36Sopenharmony_ci ret = clk_set_phase(iface->lrclk, 25862306a36Sopenharmony_ci axg_tdm_lrclk_invert(iface->fmt) ? 180 : 0); 25962306a36Sopenharmony_ci if (ret) { 26062306a36Sopenharmony_ci dev_err(dai->dev, 26162306a36Sopenharmony_ci "setting sample clock phase failed: %d\n", ret); 26262306a36Sopenharmony_ci return ret; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai, 26962306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 27262306a36Sopenharmony_ci unsigned long srate; 27362306a36Sopenharmony_ci int ret; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci srate = iface->slots * iface->slot_width * params_rate(params); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (!iface->mclk_rate) { 27862306a36Sopenharmony_ci /* If no specific mclk is requested, default to bit clock * 2 */ 27962306a36Sopenharmony_ci clk_set_rate(iface->mclk, 2 * srate); 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci /* Check if we can actually get the bit clock from mclk */ 28262306a36Sopenharmony_ci if (iface->mclk_rate % srate) { 28362306a36Sopenharmony_ci dev_err(dai->dev, 28462306a36Sopenharmony_ci "can't derive sclk %lu from mclk %lu\n", 28562306a36Sopenharmony_ci srate, iface->mclk_rate); 28662306a36Sopenharmony_ci return -EINVAL; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ret = clk_set_rate(iface->sclk, srate); 29162306a36Sopenharmony_ci if (ret) { 29262306a36Sopenharmony_ci dev_err(dai->dev, "setting bit clock failed: %d\n", ret); 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Set the bit clock inversion */ 29762306a36Sopenharmony_ci ret = clk_set_phase(iface->sclk, 29862306a36Sopenharmony_ci axg_tdm_sclk_invert(iface->fmt) ? 0 : 180); 29962306a36Sopenharmony_ci if (ret) { 30062306a36Sopenharmony_ci dev_err(dai->dev, "setting bit clock phase failed: %d\n", ret); 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return ret; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, 30862306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 30962306a36Sopenharmony_ci struct snd_soc_dai *dai) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 31262306a36Sopenharmony_ci int ret; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 31562306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 31662306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 31762306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 31862306a36Sopenharmony_ci if (iface->slots > 2) { 31962306a36Sopenharmony_ci dev_err(dai->dev, "bad slot number for format: %d\n", 32062306a36Sopenharmony_ci iface->slots); 32162306a36Sopenharmony_ci return -EINVAL; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 32662306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci default: 33062306a36Sopenharmony_ci dev_err(dai->dev, "unsupported dai format\n"); 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ret = axg_tdm_iface_set_stream(substream, params, dai); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if ((iface->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) == 33962306a36Sopenharmony_ci SND_SOC_DAIFMT_BP_FP) { 34062306a36Sopenharmony_ci ret = axg_tdm_iface_set_sclk(dai, params); 34162306a36Sopenharmony_ci if (ret) 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci ret = axg_tdm_iface_set_lrclk(dai, params); 34562306a36Sopenharmony_ci if (ret) 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, 35362306a36Sopenharmony_ci struct snd_soc_dai *dai) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Stop all attached formatters */ 35862306a36Sopenharmony_ci axg_tdm_stream_stop(ts); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, 36462306a36Sopenharmony_ci struct snd_soc_dai *dai) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Force all attached formatters to update */ 36962306a36Sopenharmony_ci return axg_tdm_stream_reset(ts); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci int stream; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci for_each_pcm_streams(stream) { 37762306a36Sopenharmony_ci struct axg_tdm_stream *ts = snd_soc_dai_dma_data_get(dai, stream); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (ts) 38062306a36Sopenharmony_ci axg_tdm_stream_free(ts); 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 38962306a36Sopenharmony_ci int stream; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci for_each_pcm_streams(stream) { 39262306a36Sopenharmony_ci struct axg_tdm_stream *ts; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!snd_soc_dai_get_widget(dai, stream)) 39562306a36Sopenharmony_ci continue; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ts = axg_tdm_stream_alloc(iface); 39862306a36Sopenharmony_ci if (!ts) { 39962306a36Sopenharmony_ci axg_tdm_iface_remove_dai(dai); 40062306a36Sopenharmony_ci return -ENOMEM; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci snd_soc_dai_dma_data_set(dai, stream, ts); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic const struct snd_soc_dai_ops axg_tdm_iface_ops = { 40962306a36Sopenharmony_ci .probe = axg_tdm_iface_probe_dai, 41062306a36Sopenharmony_ci .remove = axg_tdm_iface_remove_dai, 41162306a36Sopenharmony_ci .set_sysclk = axg_tdm_iface_set_sysclk, 41262306a36Sopenharmony_ci .set_fmt = axg_tdm_iface_set_fmt, 41362306a36Sopenharmony_ci .startup = axg_tdm_iface_startup, 41462306a36Sopenharmony_ci .hw_params = axg_tdm_iface_hw_params, 41562306a36Sopenharmony_ci .prepare = axg_tdm_iface_prepare, 41662306a36Sopenharmony_ci .hw_free = axg_tdm_iface_hw_free, 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/* TDM Backend DAIs */ 42062306a36Sopenharmony_cistatic const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = { 42162306a36Sopenharmony_ci [TDM_IFACE_PAD] = { 42262306a36Sopenharmony_ci .name = "TDM Pad", 42362306a36Sopenharmony_ci .playback = { 42462306a36Sopenharmony_ci .stream_name = "Playback", 42562306a36Sopenharmony_ci .channels_min = 1, 42662306a36Sopenharmony_ci .channels_max = AXG_TDM_CHANNEL_MAX, 42762306a36Sopenharmony_ci .rates = AXG_TDM_RATES, 42862306a36Sopenharmony_ci .formats = AXG_TDM_FORMATS, 42962306a36Sopenharmony_ci }, 43062306a36Sopenharmony_ci .capture = { 43162306a36Sopenharmony_ci .stream_name = "Capture", 43262306a36Sopenharmony_ci .channels_min = 1, 43362306a36Sopenharmony_ci .channels_max = AXG_TDM_CHANNEL_MAX, 43462306a36Sopenharmony_ci .rates = AXG_TDM_RATES, 43562306a36Sopenharmony_ci .formats = AXG_TDM_FORMATS, 43662306a36Sopenharmony_ci }, 43762306a36Sopenharmony_ci .id = TDM_IFACE_PAD, 43862306a36Sopenharmony_ci .ops = &axg_tdm_iface_ops, 43962306a36Sopenharmony_ci }, 44062306a36Sopenharmony_ci [TDM_IFACE_LOOPBACK] = { 44162306a36Sopenharmony_ci .name = "TDM Loopback", 44262306a36Sopenharmony_ci .capture = { 44362306a36Sopenharmony_ci .stream_name = "Loopback", 44462306a36Sopenharmony_ci .channels_min = 1, 44562306a36Sopenharmony_ci .channels_max = AXG_TDM_CHANNEL_MAX, 44662306a36Sopenharmony_ci .rates = AXG_TDM_RATES, 44762306a36Sopenharmony_ci .formats = AXG_TDM_FORMATS, 44862306a36Sopenharmony_ci }, 44962306a36Sopenharmony_ci .id = TDM_IFACE_LOOPBACK, 45062306a36Sopenharmony_ci .ops = &axg_tdm_iface_ops, 45162306a36Sopenharmony_ci }, 45262306a36Sopenharmony_ci}; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int axg_tdm_iface_set_bias_level(struct snd_soc_component *component, 45562306a36Sopenharmony_ci enum snd_soc_bias_level level) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_component_get_drvdata(component); 45862306a36Sopenharmony_ci enum snd_soc_bias_level now = 45962306a36Sopenharmony_ci snd_soc_component_get_bias_level(component); 46062306a36Sopenharmony_ci int ret = 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci switch (level) { 46362306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 46462306a36Sopenharmony_ci if (now == SND_SOC_BIAS_STANDBY) 46562306a36Sopenharmony_ci ret = clk_prepare_enable(iface->mclk); 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 46962306a36Sopenharmony_ci if (now == SND_SOC_BIAS_PREPARE) 47062306a36Sopenharmony_ci clk_disable_unprepare(iface->mclk); 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 47462306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return ret; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget axg_tdm_iface_dapm_widgets[] = { 48262306a36Sopenharmony_ci SND_SOC_DAPM_SIGGEN("Playback Signal"), 48362306a36Sopenharmony_ci}; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic const struct snd_soc_dapm_route axg_tdm_iface_dapm_routes[] = { 48662306a36Sopenharmony_ci { "Loopback", NULL, "Playback Signal" }, 48762306a36Sopenharmony_ci}; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic const struct snd_soc_component_driver axg_tdm_iface_component_drv = { 49062306a36Sopenharmony_ci .dapm_widgets = axg_tdm_iface_dapm_widgets, 49162306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(axg_tdm_iface_dapm_widgets), 49262306a36Sopenharmony_ci .dapm_routes = axg_tdm_iface_dapm_routes, 49362306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(axg_tdm_iface_dapm_routes), 49462306a36Sopenharmony_ci .set_bias_level = axg_tdm_iface_set_bias_level, 49562306a36Sopenharmony_ci}; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic const struct of_device_id axg_tdm_iface_of_match[] = { 49862306a36Sopenharmony_ci { .compatible = "amlogic,axg-tdm-iface", }, 49962306a36Sopenharmony_ci {} 50062306a36Sopenharmony_ci}; 50162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, axg_tdm_iface_of_match); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic int axg_tdm_iface_probe(struct platform_device *pdev) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 50662306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv; 50762306a36Sopenharmony_ci struct axg_tdm_iface *iface; 50862306a36Sopenharmony_ci int i; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL); 51162306a36Sopenharmony_ci if (!iface) 51262306a36Sopenharmony_ci return -ENOMEM; 51362306a36Sopenharmony_ci platform_set_drvdata(pdev, iface); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* 51662306a36Sopenharmony_ci * Duplicate dai driver: depending on the slot masks configuration 51762306a36Sopenharmony_ci * We'll change the number of channel provided by DAI stream, so dpcm 51862306a36Sopenharmony_ci * channel merge can be done properly 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_ci dai_drv = devm_kcalloc(dev, ARRAY_SIZE(axg_tdm_iface_dai_drv), 52162306a36Sopenharmony_ci sizeof(*dai_drv), GFP_KERNEL); 52262306a36Sopenharmony_ci if (!dai_drv) 52362306a36Sopenharmony_ci return -ENOMEM; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(axg_tdm_iface_dai_drv); i++) 52662306a36Sopenharmony_ci memcpy(&dai_drv[i], &axg_tdm_iface_dai_drv[i], 52762306a36Sopenharmony_ci sizeof(*dai_drv)); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Bit clock provided on the pad */ 53062306a36Sopenharmony_ci iface->sclk = devm_clk_get(dev, "sclk"); 53162306a36Sopenharmony_ci if (IS_ERR(iface->sclk)) 53262306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(iface->sclk), "failed to get sclk\n"); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Sample clock provided on the pad */ 53562306a36Sopenharmony_ci iface->lrclk = devm_clk_get(dev, "lrclk"); 53662306a36Sopenharmony_ci if (IS_ERR(iface->lrclk)) 53762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(iface->lrclk), "failed to get lrclk\n"); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* 54062306a36Sopenharmony_ci * mclk maybe be missing when the cpu dai is in slave mode and 54162306a36Sopenharmony_ci * the codec does not require it to provide a master clock. 54262306a36Sopenharmony_ci * At this point, ignore the error if mclk is missing. We'll 54362306a36Sopenharmony_ci * throw an error if the cpu dai is master and mclk is missing 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci iface->mclk = devm_clk_get_optional(dev, "mclk"); 54662306a36Sopenharmony_ci if (IS_ERR(iface->mclk)) 54762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(iface->mclk), "failed to get mclk\n"); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return devm_snd_soc_register_component(dev, 55062306a36Sopenharmony_ci &axg_tdm_iface_component_drv, dai_drv, 55162306a36Sopenharmony_ci ARRAY_SIZE(axg_tdm_iface_dai_drv)); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic struct platform_driver axg_tdm_iface_pdrv = { 55562306a36Sopenharmony_ci .probe = axg_tdm_iface_probe, 55662306a36Sopenharmony_ci .driver = { 55762306a36Sopenharmony_ci .name = "axg-tdm-iface", 55862306a36Sopenharmony_ci .of_match_table = axg_tdm_iface_of_match, 55962306a36Sopenharmony_ci }, 56062306a36Sopenharmony_ci}; 56162306a36Sopenharmony_cimodule_platform_driver(axg_tdm_iface_pdrv); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ciMODULE_DESCRIPTION("Amlogic AXG TDM interface driver"); 56462306a36Sopenharmony_ciMODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 56562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 566