18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (c) 2018 BayLibre, SAS. 48c2ecf20Sopenharmony_ci// Author: Jerome Brunet <jbrunet@baylibre.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/clk.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 98c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 108c2ecf20Sopenharmony_ci#include <sound/soc.h> 118c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "axg-tdm.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cienum { 168c2ecf20Sopenharmony_ci TDM_IFACE_PAD, 178c2ecf20Sopenharmony_ci TDM_IFACE_LOOPBACK, 188c2ecf20Sopenharmony_ci}; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic unsigned int axg_tdm_slots_total(u32 *mask) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci unsigned int slots = 0; 238c2ecf20Sopenharmony_ci int i; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci if (!mask) 268c2ecf20Sopenharmony_ci return 0; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci /* Count the total number of slots provided by all 4 lanes */ 298c2ecf20Sopenharmony_ci for (i = 0; i < AXG_TDM_NUM_LANES; i++) 308c2ecf20Sopenharmony_ci slots += hweight32(mask[i]); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci return slots; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciint axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask, 368c2ecf20Sopenharmony_ci u32 *rx_mask, unsigned int slots, 378c2ecf20Sopenharmony_ci unsigned int slot_width) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 408c2ecf20Sopenharmony_ci struct axg_tdm_stream *tx = (struct axg_tdm_stream *) 418c2ecf20Sopenharmony_ci dai->playback_dma_data; 428c2ecf20Sopenharmony_ci struct axg_tdm_stream *rx = (struct axg_tdm_stream *) 438c2ecf20Sopenharmony_ci dai->capture_dma_data; 448c2ecf20Sopenharmony_ci unsigned int tx_slots, rx_slots; 458c2ecf20Sopenharmony_ci unsigned int fmt = 0; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci tx_slots = axg_tdm_slots_total(tx_mask); 488c2ecf20Sopenharmony_ci rx_slots = axg_tdm_slots_total(rx_mask); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* We should at least have a slot for a valid interface */ 518c2ecf20Sopenharmony_ci if (!tx_slots && !rx_slots) { 528c2ecf20Sopenharmony_ci dev_err(dai->dev, "interface has no slot\n"); 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci iface->slots = slots; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci switch (slot_width) { 598c2ecf20Sopenharmony_ci case 0: 608c2ecf20Sopenharmony_ci slot_width = 32; 618c2ecf20Sopenharmony_ci fallthrough; 628c2ecf20Sopenharmony_ci case 32: 638c2ecf20Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S32_LE; 648c2ecf20Sopenharmony_ci fallthrough; 658c2ecf20Sopenharmony_ci case 24: 668c2ecf20Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S24_LE; 678c2ecf20Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S20_LE; 688c2ecf20Sopenharmony_ci fallthrough; 698c2ecf20Sopenharmony_ci case 16: 708c2ecf20Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S16_LE; 718c2ecf20Sopenharmony_ci fallthrough; 728c2ecf20Sopenharmony_ci case 8: 738c2ecf20Sopenharmony_ci fmt |= SNDRV_PCM_FMTBIT_S8; 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci default: 768c2ecf20Sopenharmony_ci dev_err(dai->dev, "unsupported slot width: %d\n", slot_width); 778c2ecf20Sopenharmony_ci return -EINVAL; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci iface->slot_width = slot_width; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Amend the dai driver and let dpcm merge do its job */ 838c2ecf20Sopenharmony_ci if (tx) { 848c2ecf20Sopenharmony_ci tx->mask = tx_mask; 858c2ecf20Sopenharmony_ci dai->driver->playback.channels_max = tx_slots; 868c2ecf20Sopenharmony_ci dai->driver->playback.formats = fmt; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (rx) { 908c2ecf20Sopenharmony_ci rx->mask = rx_mask; 918c2ecf20Sopenharmony_ci dai->driver->capture.channels_max = rx_slots; 928c2ecf20Sopenharmony_ci dai->driver->capture.formats = fmt; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(axg_tdm_set_tdm_slots); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int axg_tdm_iface_set_sysclk(struct snd_soc_dai *dai, int clk_id, 1008c2ecf20Sopenharmony_ci unsigned int freq, int dir) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 1038c2ecf20Sopenharmony_ci int ret = -ENOTSUPP; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (dir == SND_SOC_CLOCK_OUT && clk_id == 0) { 1068c2ecf20Sopenharmony_ci if (!iface->mclk) { 1078c2ecf20Sopenharmony_ci dev_warn(dai->dev, "master clock not provided\n"); 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci ret = clk_set_rate(iface->mclk, freq); 1108c2ecf20Sopenharmony_ci if (!ret) 1118c2ecf20Sopenharmony_ci iface->mclk_rate = freq; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 1238c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 1248c2ecf20Sopenharmony_ci if (!iface->mclk) { 1258c2ecf20Sopenharmony_ci dev_err(dai->dev, "cpu clock master: mclk missing\n"); 1268c2ecf20Sopenharmony_ci return -ENODEV; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFM: 1348c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFS: 1358c2ecf20Sopenharmony_ci dev_err(dai->dev, "only CBS_CFS and CBM_CFM are supported\n"); 1368c2ecf20Sopenharmony_ci fallthrough; 1378c2ecf20Sopenharmony_ci default: 1388c2ecf20Sopenharmony_ci return -EINVAL; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci iface->fmt = fmt; 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int axg_tdm_iface_startup(struct snd_pcm_substream *substream, 1468c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 1498c2ecf20Sopenharmony_ci struct axg_tdm_stream *ts = 1508c2ecf20Sopenharmony_ci snd_soc_dai_get_dma_data(dai, substream); 1518c2ecf20Sopenharmony_ci int ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (!axg_tdm_slots_total(ts->mask)) { 1548c2ecf20Sopenharmony_ci dev_err(dai->dev, "interface has not slots\n"); 1558c2ecf20Sopenharmony_ci return -EINVAL; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Apply component wide rate symmetry */ 1598c2ecf20Sopenharmony_ci if (snd_soc_component_active(dai->component)) { 1608c2ecf20Sopenharmony_ci ret = snd_pcm_hw_constraint_single(substream->runtime, 1618c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 1628c2ecf20Sopenharmony_ci iface->rate); 1638c2ecf20Sopenharmony_ci if (ret < 0) { 1648c2ecf20Sopenharmony_ci dev_err(dai->dev, 1658c2ecf20Sopenharmony_ci "can't set iface rate constraint\n"); 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream, 1748c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1758c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 1788c2ecf20Sopenharmony_ci struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); 1798c2ecf20Sopenharmony_ci unsigned int channels = params_channels(params); 1808c2ecf20Sopenharmony_ci unsigned int width = params_width(params); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Save rate and sample_bits for component symmetry */ 1838c2ecf20Sopenharmony_ci iface->rate = params_rate(params); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Make sure this interface can cope with the stream */ 1868c2ecf20Sopenharmony_ci if (axg_tdm_slots_total(ts->mask) < channels) { 1878c2ecf20Sopenharmony_ci dev_err(dai->dev, "not enough slots for channels\n"); 1888c2ecf20Sopenharmony_ci return -EINVAL; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (iface->slot_width < width) { 1928c2ecf20Sopenharmony_ci dev_err(dai->dev, "incompatible slots width for stream\n"); 1938c2ecf20Sopenharmony_ci return -EINVAL; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Save the parameter for tdmout/tdmin widgets */ 1978c2ecf20Sopenharmony_ci ts->physical_width = params_physical_width(params); 1988c2ecf20Sopenharmony_ci ts->width = params_width(params); 1998c2ecf20Sopenharmony_ci ts->channels = params_channels(params); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int axg_tdm_iface_set_lrclk(struct snd_soc_dai *dai, 2058c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 2088c2ecf20Sopenharmony_ci unsigned int ratio_num; 2098c2ecf20Sopenharmony_ci int ret; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ret = clk_set_rate(iface->lrclk, params_rate(params)); 2128c2ecf20Sopenharmony_ci if (ret) { 2138c2ecf20Sopenharmony_ci dev_err(dai->dev, "setting sample clock failed: %d\n", ret); 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 2188c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 2198c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 2208c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 2218c2ecf20Sopenharmony_ci /* 50% duty cycle ratio */ 2228c2ecf20Sopenharmony_ci ratio_num = 1; 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 2268c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * A zero duty cycle ratio will result in setting the mininum 2298c2ecf20Sopenharmony_ci * ratio possible which, for this clock, is 1 cycle of the 2308c2ecf20Sopenharmony_ci * parent bclk clock high and the rest low, This is exactly 2318c2ecf20Sopenharmony_ci * what we want here. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci ratio_num = 0; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci default: 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = clk_set_duty_cycle(iface->lrclk, ratio_num, 2); 2418c2ecf20Sopenharmony_ci if (ret) { 2428c2ecf20Sopenharmony_ci dev_err(dai->dev, 2438c2ecf20Sopenharmony_ci "setting sample clock duty cycle failed: %d\n", ret); 2448c2ecf20Sopenharmony_ci return ret; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Set sample clock inversion */ 2488c2ecf20Sopenharmony_ci ret = clk_set_phase(iface->lrclk, 2498c2ecf20Sopenharmony_ci axg_tdm_lrclk_invert(iface->fmt) ? 180 : 0); 2508c2ecf20Sopenharmony_ci if (ret) { 2518c2ecf20Sopenharmony_ci dev_err(dai->dev, 2528c2ecf20Sopenharmony_ci "setting sample clock phase failed: %d\n", ret); 2538c2ecf20Sopenharmony_ci return ret; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai, 2608c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 2638c2ecf20Sopenharmony_ci unsigned long srate; 2648c2ecf20Sopenharmony_ci int ret; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci srate = iface->slots * iface->slot_width * params_rate(params); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!iface->mclk_rate) { 2698c2ecf20Sopenharmony_ci /* If no specific mclk is requested, default to bit clock * 4 */ 2708c2ecf20Sopenharmony_ci clk_set_rate(iface->mclk, 4 * srate); 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci /* Check if we can actually get the bit clock from mclk */ 2738c2ecf20Sopenharmony_ci if (iface->mclk_rate % srate) { 2748c2ecf20Sopenharmony_ci dev_err(dai->dev, 2758c2ecf20Sopenharmony_ci "can't derive sclk %lu from mclk %lu\n", 2768c2ecf20Sopenharmony_ci srate, iface->mclk_rate); 2778c2ecf20Sopenharmony_ci return -EINVAL; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ret = clk_set_rate(iface->sclk, srate); 2828c2ecf20Sopenharmony_ci if (ret) { 2838c2ecf20Sopenharmony_ci dev_err(dai->dev, "setting bit clock failed: %d\n", ret); 2848c2ecf20Sopenharmony_ci return ret; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Set the bit clock inversion */ 2888c2ecf20Sopenharmony_ci ret = clk_set_phase(iface->sclk, 2898c2ecf20Sopenharmony_ci axg_tdm_sclk_invert(iface->fmt) ? 0 : 180); 2908c2ecf20Sopenharmony_ci if (ret) { 2918c2ecf20Sopenharmony_ci dev_err(dai->dev, "setting bit clock phase failed: %d\n", ret); 2928c2ecf20Sopenharmony_ci return ret; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return ret; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream, 2998c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 3008c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 3038c2ecf20Sopenharmony_ci int ret; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 3068c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 3078c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 3088c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 3098c2ecf20Sopenharmony_ci if (iface->slots > 2) { 3108c2ecf20Sopenharmony_ci dev_err(dai->dev, "bad slot number for format: %d\n", 3118c2ecf20Sopenharmony_ci iface->slots); 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 3178c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci default: 3218c2ecf20Sopenharmony_ci dev_err(dai->dev, "unsupported dai format\n"); 3228c2ecf20Sopenharmony_ci return -EINVAL; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci ret = axg_tdm_iface_set_stream(substream, params, dai); 3268c2ecf20Sopenharmony_ci if (ret) 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if ((iface->fmt & SND_SOC_DAIFMT_MASTER_MASK) == 3308c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS) { 3318c2ecf20Sopenharmony_ci ret = axg_tdm_iface_set_sclk(dai, params); 3328c2ecf20Sopenharmony_ci if (ret) 3338c2ecf20Sopenharmony_ci return ret; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci ret = axg_tdm_iface_set_lrclk(dai, params); 3368c2ecf20Sopenharmony_ci if (ret) 3378c2ecf20Sopenharmony_ci return ret; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream, 3448c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Stop all attached formatters */ 3498c2ecf20Sopenharmony_ci axg_tdm_stream_stop(ts); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, 3558c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* Force all attached formatters to update */ 3608c2ecf20Sopenharmony_ci return axg_tdm_stream_reset(ts); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci if (dai->capture_dma_data) 3668c2ecf20Sopenharmony_ci axg_tdm_stream_free(dai->capture_dma_data); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (dai->playback_dma_data) 3698c2ecf20Sopenharmony_ci axg_tdm_stream_free(dai->playback_dma_data); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return 0; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (dai->capture_widget) { 3798c2ecf20Sopenharmony_ci dai->capture_dma_data = axg_tdm_stream_alloc(iface); 3808c2ecf20Sopenharmony_ci if (!dai->capture_dma_data) 3818c2ecf20Sopenharmony_ci return -ENOMEM; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (dai->playback_widget) { 3858c2ecf20Sopenharmony_ci dai->playback_dma_data = axg_tdm_stream_alloc(iface); 3868c2ecf20Sopenharmony_ci if (!dai->playback_dma_data) { 3878c2ecf20Sopenharmony_ci axg_tdm_iface_remove_dai(dai); 3888c2ecf20Sopenharmony_ci return -ENOMEM; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops axg_tdm_iface_ops = { 3968c2ecf20Sopenharmony_ci .set_sysclk = axg_tdm_iface_set_sysclk, 3978c2ecf20Sopenharmony_ci .set_fmt = axg_tdm_iface_set_fmt, 3988c2ecf20Sopenharmony_ci .startup = axg_tdm_iface_startup, 3998c2ecf20Sopenharmony_ci .hw_params = axg_tdm_iface_hw_params, 4008c2ecf20Sopenharmony_ci .prepare = axg_tdm_iface_prepare, 4018c2ecf20Sopenharmony_ci .hw_free = axg_tdm_iface_hw_free, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* TDM Backend DAIs */ 4058c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = { 4068c2ecf20Sopenharmony_ci [TDM_IFACE_PAD] = { 4078c2ecf20Sopenharmony_ci .name = "TDM Pad", 4088c2ecf20Sopenharmony_ci .playback = { 4098c2ecf20Sopenharmony_ci .stream_name = "Playback", 4108c2ecf20Sopenharmony_ci .channels_min = 1, 4118c2ecf20Sopenharmony_ci .channels_max = AXG_TDM_CHANNEL_MAX, 4128c2ecf20Sopenharmony_ci .rates = AXG_TDM_RATES, 4138c2ecf20Sopenharmony_ci .formats = AXG_TDM_FORMATS, 4148c2ecf20Sopenharmony_ci }, 4158c2ecf20Sopenharmony_ci .capture = { 4168c2ecf20Sopenharmony_ci .stream_name = "Capture", 4178c2ecf20Sopenharmony_ci .channels_min = 1, 4188c2ecf20Sopenharmony_ci .channels_max = AXG_TDM_CHANNEL_MAX, 4198c2ecf20Sopenharmony_ci .rates = AXG_TDM_RATES, 4208c2ecf20Sopenharmony_ci .formats = AXG_TDM_FORMATS, 4218c2ecf20Sopenharmony_ci }, 4228c2ecf20Sopenharmony_ci .id = TDM_IFACE_PAD, 4238c2ecf20Sopenharmony_ci .ops = &axg_tdm_iface_ops, 4248c2ecf20Sopenharmony_ci .probe = axg_tdm_iface_probe_dai, 4258c2ecf20Sopenharmony_ci .remove = axg_tdm_iface_remove_dai, 4268c2ecf20Sopenharmony_ci }, 4278c2ecf20Sopenharmony_ci [TDM_IFACE_LOOPBACK] = { 4288c2ecf20Sopenharmony_ci .name = "TDM Loopback", 4298c2ecf20Sopenharmony_ci .capture = { 4308c2ecf20Sopenharmony_ci .stream_name = "Loopback", 4318c2ecf20Sopenharmony_ci .channels_min = 1, 4328c2ecf20Sopenharmony_ci .channels_max = AXG_TDM_CHANNEL_MAX, 4338c2ecf20Sopenharmony_ci .rates = AXG_TDM_RATES, 4348c2ecf20Sopenharmony_ci .formats = AXG_TDM_FORMATS, 4358c2ecf20Sopenharmony_ci }, 4368c2ecf20Sopenharmony_ci .id = TDM_IFACE_LOOPBACK, 4378c2ecf20Sopenharmony_ci .ops = &axg_tdm_iface_ops, 4388c2ecf20Sopenharmony_ci .probe = axg_tdm_iface_probe_dai, 4398c2ecf20Sopenharmony_ci .remove = axg_tdm_iface_remove_dai, 4408c2ecf20Sopenharmony_ci }, 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int axg_tdm_iface_set_bias_level(struct snd_soc_component *component, 4448c2ecf20Sopenharmony_ci enum snd_soc_bias_level level) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface = snd_soc_component_get_drvdata(component); 4478c2ecf20Sopenharmony_ci enum snd_soc_bias_level now = 4488c2ecf20Sopenharmony_ci snd_soc_component_get_bias_level(component); 4498c2ecf20Sopenharmony_ci int ret = 0; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci switch (level) { 4528c2ecf20Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 4538c2ecf20Sopenharmony_ci if (now == SND_SOC_BIAS_STANDBY) 4548c2ecf20Sopenharmony_ci ret = clk_prepare_enable(iface->mclk); 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 4588c2ecf20Sopenharmony_ci if (now == SND_SOC_BIAS_PREPARE) 4598c2ecf20Sopenharmony_ci clk_disable_unprepare(iface->mclk); 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci case SND_SOC_BIAS_OFF: 4638c2ecf20Sopenharmony_ci case SND_SOC_BIAS_ON: 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return ret; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget axg_tdm_iface_dapm_widgets[] = { 4718c2ecf20Sopenharmony_ci SND_SOC_DAPM_SIGGEN("Playback Signal"), 4728c2ecf20Sopenharmony_ci}; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route axg_tdm_iface_dapm_routes[] = { 4758c2ecf20Sopenharmony_ci { "Loopback", NULL, "Playback Signal" }, 4768c2ecf20Sopenharmony_ci}; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver axg_tdm_iface_component_drv = { 4798c2ecf20Sopenharmony_ci .dapm_widgets = axg_tdm_iface_dapm_widgets, 4808c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(axg_tdm_iface_dapm_widgets), 4818c2ecf20Sopenharmony_ci .dapm_routes = axg_tdm_iface_dapm_routes, 4828c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(axg_tdm_iface_dapm_routes), 4838c2ecf20Sopenharmony_ci .set_bias_level = axg_tdm_iface_set_bias_level, 4848c2ecf20Sopenharmony_ci}; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic const struct of_device_id axg_tdm_iface_of_match[] = { 4878c2ecf20Sopenharmony_ci { .compatible = "amlogic,axg-tdm-iface", }, 4888c2ecf20Sopenharmony_ci {} 4898c2ecf20Sopenharmony_ci}; 4908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, axg_tdm_iface_of_match); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int axg_tdm_iface_probe(struct platform_device *pdev) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4958c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv; 4968c2ecf20Sopenharmony_ci struct axg_tdm_iface *iface; 4978c2ecf20Sopenharmony_ci int ret, i; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL); 5008c2ecf20Sopenharmony_ci if (!iface) 5018c2ecf20Sopenharmony_ci return -ENOMEM; 5028c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, iface); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * Duplicate dai driver: depending on the slot masks configuration 5068c2ecf20Sopenharmony_ci * We'll change the number of channel provided by DAI stream, so dpcm 5078c2ecf20Sopenharmony_ci * channel merge can be done properly 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ci dai_drv = devm_kcalloc(dev, ARRAY_SIZE(axg_tdm_iface_dai_drv), 5108c2ecf20Sopenharmony_ci sizeof(*dai_drv), GFP_KERNEL); 5118c2ecf20Sopenharmony_ci if (!dai_drv) 5128c2ecf20Sopenharmony_ci return -ENOMEM; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(axg_tdm_iface_dai_drv); i++) 5158c2ecf20Sopenharmony_ci memcpy(&dai_drv[i], &axg_tdm_iface_dai_drv[i], 5168c2ecf20Sopenharmony_ci sizeof(*dai_drv)); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Bit clock provided on the pad */ 5198c2ecf20Sopenharmony_ci iface->sclk = devm_clk_get(dev, "sclk"); 5208c2ecf20Sopenharmony_ci if (IS_ERR(iface->sclk)) { 5218c2ecf20Sopenharmony_ci ret = PTR_ERR(iface->sclk); 5228c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 5238c2ecf20Sopenharmony_ci dev_err(dev, "failed to get sclk: %d\n", ret); 5248c2ecf20Sopenharmony_ci return ret; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Sample clock provided on the pad */ 5288c2ecf20Sopenharmony_ci iface->lrclk = devm_clk_get(dev, "lrclk"); 5298c2ecf20Sopenharmony_ci if (IS_ERR(iface->lrclk)) { 5308c2ecf20Sopenharmony_ci ret = PTR_ERR(iface->lrclk); 5318c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 5328c2ecf20Sopenharmony_ci dev_err(dev, "failed to get lrclk: %d\n", ret); 5338c2ecf20Sopenharmony_ci return ret; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * mclk maybe be missing when the cpu dai is in slave mode and 5388c2ecf20Sopenharmony_ci * the codec does not require it to provide a master clock. 5398c2ecf20Sopenharmony_ci * At this point, ignore the error if mclk is missing. We'll 5408c2ecf20Sopenharmony_ci * throw an error if the cpu dai is master and mclk is missing 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci iface->mclk = devm_clk_get(dev, "mclk"); 5438c2ecf20Sopenharmony_ci if (IS_ERR(iface->mclk)) { 5448c2ecf20Sopenharmony_ci ret = PTR_ERR(iface->mclk); 5458c2ecf20Sopenharmony_ci if (ret == -ENOENT) { 5468c2ecf20Sopenharmony_ci iface->mclk = NULL; 5478c2ecf20Sopenharmony_ci } else { 5488c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 5498c2ecf20Sopenharmony_ci dev_err(dev, "failed to get mclk: %d\n", ret); 5508c2ecf20Sopenharmony_ci return ret; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return devm_snd_soc_register_component(dev, 5558c2ecf20Sopenharmony_ci &axg_tdm_iface_component_drv, dai_drv, 5568c2ecf20Sopenharmony_ci ARRAY_SIZE(axg_tdm_iface_dai_drv)); 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic struct platform_driver axg_tdm_iface_pdrv = { 5608c2ecf20Sopenharmony_ci .probe = axg_tdm_iface_probe, 5618c2ecf20Sopenharmony_ci .driver = { 5628c2ecf20Sopenharmony_ci .name = "axg-tdm-iface", 5638c2ecf20Sopenharmony_ci .of_match_table = axg_tdm_iface_of_match, 5648c2ecf20Sopenharmony_ci }, 5658c2ecf20Sopenharmony_ci}; 5668c2ecf20Sopenharmony_cimodule_platform_driver(axg_tdm_iface_pdrv); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amlogic AXG TDM interface driver"); 5698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 5708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 571