cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

axg-tdm-interface.c (13898B)


      1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
      2//
      3// Copyright (c) 2018 BayLibre, SAS.
      4// Author: Jerome Brunet <jbrunet@baylibre.com>
      5
      6#include <linux/clk.h>
      7#include <linux/module.h>
      8#include <linux/of_platform.h>
      9#include <sound/pcm_params.h>
     10#include <sound/soc.h>
     11#include <sound/soc-dai.h>
     12
     13#include "axg-tdm.h"
     14
     15enum {
     16	TDM_IFACE_PAD,
     17	TDM_IFACE_LOOPBACK,
     18};
     19
     20static unsigned int axg_tdm_slots_total(u32 *mask)
     21{
     22	unsigned int slots = 0;
     23	int i;
     24
     25	if (!mask)
     26		return 0;
     27
     28	/* Count the total number of slots provided by all 4 lanes */
     29	for (i = 0; i < AXG_TDM_NUM_LANES; i++)
     30		slots += hweight32(mask[i]);
     31
     32	return slots;
     33}
     34
     35int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask,
     36			  u32 *rx_mask, unsigned int slots,
     37			  unsigned int slot_width)
     38{
     39	struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
     40	struct axg_tdm_stream *tx = (struct axg_tdm_stream *)
     41		dai->playback_dma_data;
     42	struct axg_tdm_stream *rx = (struct axg_tdm_stream *)
     43		dai->capture_dma_data;
     44	unsigned int tx_slots, rx_slots;
     45	unsigned int fmt = 0;
     46
     47	tx_slots = axg_tdm_slots_total(tx_mask);
     48	rx_slots = axg_tdm_slots_total(rx_mask);
     49
     50	/* We should at least have a slot for a valid interface */
     51	if (!tx_slots && !rx_slots) {
     52		dev_err(dai->dev, "interface has no slot\n");
     53		return -EINVAL;
     54	}
     55
     56	iface->slots = slots;
     57
     58	switch (slot_width) {
     59	case 0:
     60		slot_width = 32;
     61		fallthrough;
     62	case 32:
     63		fmt |= SNDRV_PCM_FMTBIT_S32_LE;
     64		fallthrough;
     65	case 24:
     66		fmt |= SNDRV_PCM_FMTBIT_S24_LE;
     67		fmt |= SNDRV_PCM_FMTBIT_S20_LE;
     68		fallthrough;
     69	case 16:
     70		fmt |= SNDRV_PCM_FMTBIT_S16_LE;
     71		fallthrough;
     72	case 8:
     73		fmt |= SNDRV_PCM_FMTBIT_S8;
     74		break;
     75	default:
     76		dev_err(dai->dev, "unsupported slot width: %d\n", slot_width);
     77		return -EINVAL;
     78	}
     79
     80	iface->slot_width = slot_width;
     81
     82	/* Amend the dai driver and let dpcm merge do its job */
     83	if (tx) {
     84		tx->mask = tx_mask;
     85		dai->driver->playback.channels_max = tx_slots;
     86		dai->driver->playback.formats = fmt;
     87	}
     88
     89	if (rx) {
     90		rx->mask = rx_mask;
     91		dai->driver->capture.channels_max = rx_slots;
     92		dai->driver->capture.formats = fmt;
     93	}
     94
     95	return 0;
     96}
     97EXPORT_SYMBOL_GPL(axg_tdm_set_tdm_slots);
     98
     99static int axg_tdm_iface_set_sysclk(struct snd_soc_dai *dai, int clk_id,
    100				    unsigned int freq, int dir)
    101{
    102	struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
    103	int ret = -ENOTSUPP;
    104
    105	if (dir == SND_SOC_CLOCK_OUT && clk_id == 0) {
    106		if (!iface->mclk) {
    107			dev_warn(dai->dev, "master clock not provided\n");
    108		} else {
    109			ret = clk_set_rate(iface->mclk, freq);
    110			if (!ret)
    111				iface->mclk_rate = freq;
    112		}
    113	}
    114
    115	return ret;
    116}
    117
    118static int axg_tdm_iface_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
    119{
    120	struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
    121
    122	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
    123	case SND_SOC_DAIFMT_CBS_CFS:
    124		if (!iface->mclk) {
    125			dev_err(dai->dev, "cpu clock master: mclk missing\n");
    126			return -ENODEV;
    127		}
    128		break;
    129
    130	case SND_SOC_DAIFMT_CBM_CFM:
    131		break;
    132
    133	case SND_SOC_DAIFMT_CBS_CFM:
    134	case SND_SOC_DAIFMT_CBM_CFS:
    135		dev_err(dai->dev, "only CBS_CFS and CBM_CFM are supported\n");
    136		fallthrough;
    137	default:
    138		return -EINVAL;
    139	}
    140
    141	iface->fmt = fmt;
    142	return 0;
    143}
    144
    145static int axg_tdm_iface_startup(struct snd_pcm_substream *substream,
    146				 struct snd_soc_dai *dai)
    147{
    148	struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
    149	struct axg_tdm_stream *ts =
    150		snd_soc_dai_get_dma_data(dai, substream);
    151	int ret;
    152
    153	if (!axg_tdm_slots_total(ts->mask)) {
    154		dev_err(dai->dev, "interface has not slots\n");
    155		return -EINVAL;
    156	}
    157
    158	/* Apply component wide rate symmetry */
    159	if (snd_soc_component_active(dai->component)) {
    160		ret = snd_pcm_hw_constraint_single(substream->runtime,
    161						   SNDRV_PCM_HW_PARAM_RATE,
    162						   iface->rate);
    163		if (ret < 0) {
    164			dev_err(dai->dev,
    165				"can't set iface rate constraint\n");
    166			return ret;
    167		}
    168	}
    169
    170	return 0;
    171}
    172
    173static int axg_tdm_iface_set_stream(struct snd_pcm_substream *substream,
    174				    struct snd_pcm_hw_params *params,
    175				    struct snd_soc_dai *dai)
    176{
    177	struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
    178	struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
    179	unsigned int channels = params_channels(params);
    180	unsigned int width = params_width(params);
    181
    182	/* Save rate and sample_bits for component symmetry */
    183	iface->rate = params_rate(params);
    184
    185	/* Make sure this interface can cope with the stream */
    186	if (axg_tdm_slots_total(ts->mask) < channels) {
    187		dev_err(dai->dev, "not enough slots for channels\n");
    188		return -EINVAL;
    189	}
    190
    191	if (iface->slot_width < width) {
    192		dev_err(dai->dev, "incompatible slots width for stream\n");
    193		return -EINVAL;
    194	}
    195
    196	/* Save the parameter for tdmout/tdmin widgets */
    197	ts->physical_width = params_physical_width(params);
    198	ts->width = params_width(params);
    199	ts->channels = params_channels(params);
    200
    201	return 0;
    202}
    203
    204static int axg_tdm_iface_set_lrclk(struct snd_soc_dai *dai,
    205				   struct snd_pcm_hw_params *params)
    206{
    207	struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
    208	unsigned int ratio_num;
    209	int ret;
    210
    211	ret = clk_set_rate(iface->lrclk, params_rate(params));
    212	if (ret) {
    213		dev_err(dai->dev, "setting sample clock failed: %d\n", ret);
    214		return ret;
    215	}
    216
    217	switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    218	case SND_SOC_DAIFMT_I2S:
    219	case SND_SOC_DAIFMT_LEFT_J:
    220	case SND_SOC_DAIFMT_RIGHT_J:
    221		/* 50% duty cycle ratio */
    222		ratio_num = 1;
    223		break;
    224
    225	case SND_SOC_DAIFMT_DSP_A:
    226	case SND_SOC_DAIFMT_DSP_B:
    227		/*
    228		 * A zero duty cycle ratio will result in setting the mininum
    229		 * ratio possible which, for this clock, is 1 cycle of the
    230		 * parent bclk clock high and the rest low, This is exactly
    231		 * what we want here.
    232		 */
    233		ratio_num = 0;
    234		break;
    235
    236	default:
    237		return -EINVAL;
    238	}
    239
    240	ret = clk_set_duty_cycle(iface->lrclk, ratio_num, 2);
    241	if (ret) {
    242		dev_err(dai->dev,
    243			"setting sample clock duty cycle failed: %d\n", ret);
    244		return ret;
    245	}
    246
    247	/* Set sample clock inversion */
    248	ret = clk_set_phase(iface->lrclk,
    249			    axg_tdm_lrclk_invert(iface->fmt) ? 180 : 0);
    250	if (ret) {
    251		dev_err(dai->dev,
    252			"setting sample clock phase failed: %d\n", ret);
    253		return ret;
    254	}
    255
    256	return 0;
    257}
    258
    259static int axg_tdm_iface_set_sclk(struct snd_soc_dai *dai,
    260				  struct snd_pcm_hw_params *params)
    261{
    262	struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
    263	unsigned long srate;
    264	int ret;
    265
    266	srate = iface->slots * iface->slot_width * params_rate(params);
    267
    268	if (!iface->mclk_rate) {
    269		/* If no specific mclk is requested, default to bit clock * 4 */
    270		clk_set_rate(iface->mclk, 4 * srate);
    271	} else {
    272		/* Check if we can actually get the bit clock from mclk */
    273		if (iface->mclk_rate % srate) {
    274			dev_err(dai->dev,
    275				"can't derive sclk %lu from mclk %lu\n",
    276				srate, iface->mclk_rate);
    277			return -EINVAL;
    278		}
    279	}
    280
    281	ret = clk_set_rate(iface->sclk, srate);
    282	if (ret) {
    283		dev_err(dai->dev, "setting bit clock failed: %d\n", ret);
    284		return ret;
    285	}
    286
    287	/* Set the bit clock inversion */
    288	ret = clk_set_phase(iface->sclk,
    289			    axg_tdm_sclk_invert(iface->fmt) ? 0 : 180);
    290	if (ret) {
    291		dev_err(dai->dev, "setting bit clock phase failed: %d\n", ret);
    292		return ret;
    293	}
    294
    295	return ret;
    296}
    297
    298static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream,
    299				   struct snd_pcm_hw_params *params,
    300				   struct snd_soc_dai *dai)
    301{
    302	struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
    303	int ret;
    304
    305	switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
    306	case SND_SOC_DAIFMT_I2S:
    307	case SND_SOC_DAIFMT_LEFT_J:
    308	case SND_SOC_DAIFMT_RIGHT_J:
    309		if (iface->slots > 2) {
    310			dev_err(dai->dev, "bad slot number for format: %d\n",
    311				iface->slots);
    312			return -EINVAL;
    313		}
    314		break;
    315
    316	case SND_SOC_DAIFMT_DSP_A:
    317	case SND_SOC_DAIFMT_DSP_B:
    318		break;
    319
    320	default:
    321		dev_err(dai->dev, "unsupported dai format\n");
    322		return -EINVAL;
    323	}
    324
    325	ret = axg_tdm_iface_set_stream(substream, params, dai);
    326	if (ret)
    327		return ret;
    328
    329	if ((iface->fmt & SND_SOC_DAIFMT_MASTER_MASK) ==
    330	    SND_SOC_DAIFMT_CBS_CFS) {
    331		ret = axg_tdm_iface_set_sclk(dai, params);
    332		if (ret)
    333			return ret;
    334
    335		ret = axg_tdm_iface_set_lrclk(dai, params);
    336		if (ret)
    337			return ret;
    338	}
    339
    340	return 0;
    341}
    342
    343static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream,
    344				 struct snd_soc_dai *dai)
    345{
    346	struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
    347
    348	/* Stop all attached formatters */
    349	axg_tdm_stream_stop(ts);
    350
    351	return 0;
    352}
    353
    354static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream,
    355				 struct snd_soc_dai *dai)
    356{
    357	struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
    358
    359	/* Force all attached formatters to update */
    360	return axg_tdm_stream_reset(ts);
    361}
    362
    363static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai)
    364{
    365	if (dai->capture_dma_data)
    366		axg_tdm_stream_free(dai->capture_dma_data);
    367
    368	if (dai->playback_dma_data)
    369		axg_tdm_stream_free(dai->playback_dma_data);
    370
    371	return 0;
    372}
    373
    374static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai)
    375{
    376	struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
    377
    378	if (dai->capture_widget) {
    379		dai->capture_dma_data = axg_tdm_stream_alloc(iface);
    380		if (!dai->capture_dma_data)
    381			return -ENOMEM;
    382	}
    383
    384	if (dai->playback_widget) {
    385		dai->playback_dma_data = axg_tdm_stream_alloc(iface);
    386		if (!dai->playback_dma_data) {
    387			axg_tdm_iface_remove_dai(dai);
    388			return -ENOMEM;
    389		}
    390	}
    391
    392	return 0;
    393}
    394
    395static const struct snd_soc_dai_ops axg_tdm_iface_ops = {
    396	.set_sysclk	= axg_tdm_iface_set_sysclk,
    397	.set_fmt	= axg_tdm_iface_set_fmt,
    398	.startup	= axg_tdm_iface_startup,
    399	.hw_params	= axg_tdm_iface_hw_params,
    400	.prepare	= axg_tdm_iface_prepare,
    401	.hw_free	= axg_tdm_iface_hw_free,
    402};
    403
    404/* TDM Backend DAIs */
    405static const struct snd_soc_dai_driver axg_tdm_iface_dai_drv[] = {
    406	[TDM_IFACE_PAD] = {
    407		.name = "TDM Pad",
    408		.playback = {
    409			.stream_name	= "Playback",
    410			.channels_min	= 1,
    411			.channels_max	= AXG_TDM_CHANNEL_MAX,
    412			.rates		= AXG_TDM_RATES,
    413			.formats	= AXG_TDM_FORMATS,
    414		},
    415		.capture = {
    416			.stream_name	= "Capture",
    417			.channels_min	= 1,
    418			.channels_max	= AXG_TDM_CHANNEL_MAX,
    419			.rates		= AXG_TDM_RATES,
    420			.formats	= AXG_TDM_FORMATS,
    421		},
    422		.id = TDM_IFACE_PAD,
    423		.ops = &axg_tdm_iface_ops,
    424		.probe = axg_tdm_iface_probe_dai,
    425		.remove = axg_tdm_iface_remove_dai,
    426	},
    427	[TDM_IFACE_LOOPBACK] = {
    428		.name = "TDM Loopback",
    429		.capture = {
    430			.stream_name	= "Loopback",
    431			.channels_min	= 1,
    432			.channels_max	= AXG_TDM_CHANNEL_MAX,
    433			.rates		= AXG_TDM_RATES,
    434			.formats	= AXG_TDM_FORMATS,
    435		},
    436		.id = TDM_IFACE_LOOPBACK,
    437		.ops = &axg_tdm_iface_ops,
    438		.probe = axg_tdm_iface_probe_dai,
    439		.remove = axg_tdm_iface_remove_dai,
    440	},
    441};
    442
    443static int axg_tdm_iface_set_bias_level(struct snd_soc_component *component,
    444					enum snd_soc_bias_level level)
    445{
    446	struct axg_tdm_iface *iface = snd_soc_component_get_drvdata(component);
    447	enum snd_soc_bias_level now =
    448		snd_soc_component_get_bias_level(component);
    449	int ret = 0;
    450
    451	switch (level) {
    452	case SND_SOC_BIAS_PREPARE:
    453		if (now == SND_SOC_BIAS_STANDBY)
    454			ret = clk_prepare_enable(iface->mclk);
    455		break;
    456
    457	case SND_SOC_BIAS_STANDBY:
    458		if (now == SND_SOC_BIAS_PREPARE)
    459			clk_disable_unprepare(iface->mclk);
    460		break;
    461
    462	case SND_SOC_BIAS_OFF:
    463	case SND_SOC_BIAS_ON:
    464		break;
    465	}
    466
    467	return ret;
    468}
    469
    470static const struct snd_soc_dapm_widget axg_tdm_iface_dapm_widgets[] = {
    471	SND_SOC_DAPM_SIGGEN("Playback Signal"),
    472};
    473
    474static const struct snd_soc_dapm_route axg_tdm_iface_dapm_routes[] = {
    475	{ "Loopback", NULL, "Playback Signal" },
    476};
    477
    478static const struct snd_soc_component_driver axg_tdm_iface_component_drv = {
    479	.dapm_widgets		= axg_tdm_iface_dapm_widgets,
    480	.num_dapm_widgets	= ARRAY_SIZE(axg_tdm_iface_dapm_widgets),
    481	.dapm_routes		= axg_tdm_iface_dapm_routes,
    482	.num_dapm_routes	= ARRAY_SIZE(axg_tdm_iface_dapm_routes),
    483	.set_bias_level		= axg_tdm_iface_set_bias_level,
    484};
    485
    486static const struct of_device_id axg_tdm_iface_of_match[] = {
    487	{ .compatible = "amlogic,axg-tdm-iface", },
    488	{}
    489};
    490MODULE_DEVICE_TABLE(of, axg_tdm_iface_of_match);
    491
    492static int axg_tdm_iface_probe(struct platform_device *pdev)
    493{
    494	struct device *dev = &pdev->dev;
    495	struct snd_soc_dai_driver *dai_drv;
    496	struct axg_tdm_iface *iface;
    497	int ret, i;
    498
    499	iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL);
    500	if (!iface)
    501		return -ENOMEM;
    502	platform_set_drvdata(pdev, iface);
    503
    504	/*
    505	 * Duplicate dai driver: depending on the slot masks configuration
    506	 * We'll change the number of channel provided by DAI stream, so dpcm
    507	 * channel merge can be done properly
    508	 */
    509	dai_drv = devm_kcalloc(dev, ARRAY_SIZE(axg_tdm_iface_dai_drv),
    510			       sizeof(*dai_drv), GFP_KERNEL);
    511	if (!dai_drv)
    512		return -ENOMEM;
    513
    514	for (i = 0; i < ARRAY_SIZE(axg_tdm_iface_dai_drv); i++)
    515		memcpy(&dai_drv[i], &axg_tdm_iface_dai_drv[i],
    516		       sizeof(*dai_drv));
    517
    518	/* Bit clock provided on the pad */
    519	iface->sclk = devm_clk_get(dev, "sclk");
    520	if (IS_ERR(iface->sclk))
    521		return dev_err_probe(dev, PTR_ERR(iface->sclk), "failed to get sclk\n");
    522
    523	/* Sample clock provided on the pad */
    524	iface->lrclk = devm_clk_get(dev, "lrclk");
    525	if (IS_ERR(iface->lrclk))
    526		return dev_err_probe(dev, PTR_ERR(iface->lrclk), "failed to get lrclk\n");
    527
    528	/*
    529	 * mclk maybe be missing when the cpu dai is in slave mode and
    530	 * the codec does not require it to provide a master clock.
    531	 * At this point, ignore the error if mclk is missing. We'll
    532	 * throw an error if the cpu dai is master and mclk is missing
    533	 */
    534	iface->mclk = devm_clk_get(dev, "mclk");
    535	if (IS_ERR(iface->mclk)) {
    536		ret = PTR_ERR(iface->mclk);
    537		if (ret == -ENOENT)
    538			iface->mclk = NULL;
    539		else
    540			return dev_err_probe(dev, ret, "failed to get mclk\n");
    541	}
    542
    543	return devm_snd_soc_register_component(dev,
    544					&axg_tdm_iface_component_drv, dai_drv,
    545					ARRAY_SIZE(axg_tdm_iface_dai_drv));
    546}
    547
    548static struct platform_driver axg_tdm_iface_pdrv = {
    549	.probe = axg_tdm_iface_probe,
    550	.driver = {
    551		.name = "axg-tdm-iface",
    552		.of_match_table = axg_tdm_iface_of_match,
    553	},
    554};
    555module_platform_driver(axg_tdm_iface_pdrv);
    556
    557MODULE_DESCRIPTION("Amlogic AXG TDM interface driver");
    558MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
    559MODULE_LICENSE("GPL v2");