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

omap-dmic.c (12200B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * omap-dmic.c  --  OMAP ASoC DMIC DAI driver
      4 *
      5 * Copyright (C) 2010 - 2011 Texas Instruments
      6 *
      7 * Author: David Lambert <dlambert@ti.com>
      8 *	   Misael Lopez Cruz <misael.lopez@ti.com>
      9 *	   Liam Girdwood <lrg@ti.com>
     10 *	   Peter Ujfalusi <peter.ujfalusi@ti.com>
     11 */
     12
     13#include <linux/init.h>
     14#include <linux/module.h>
     15#include <linux/platform_device.h>
     16#include <linux/err.h>
     17#include <linux/clk.h>
     18#include <linux/io.h>
     19#include <linux/slab.h>
     20#include <linux/pm_runtime.h>
     21#include <linux/of_device.h>
     22
     23#include <sound/core.h>
     24#include <sound/pcm.h>
     25#include <sound/pcm_params.h>
     26#include <sound/initval.h>
     27#include <sound/soc.h>
     28#include <sound/dmaengine_pcm.h>
     29
     30#include "omap-dmic.h"
     31#include "sdma-pcm.h"
     32
     33struct omap_dmic {
     34	struct device *dev;
     35	void __iomem *io_base;
     36	struct clk *fclk;
     37	struct pm_qos_request pm_qos_req;
     38	int latency;
     39	int fclk_freq;
     40	int out_freq;
     41	int clk_div;
     42	int sysclk;
     43	int threshold;
     44	u32 ch_enabled;
     45	bool active;
     46	struct mutex mutex;
     47
     48	struct snd_dmaengine_dai_dma_data dma_data;
     49};
     50
     51static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val)
     52{
     53	writel_relaxed(val, dmic->io_base + reg);
     54}
     55
     56static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg)
     57{
     58	return readl_relaxed(dmic->io_base + reg);
     59}
     60
     61static inline void omap_dmic_start(struct omap_dmic *dmic)
     62{
     63	u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
     64
     65	/* Configure DMA controller */
     66	omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_SET_REG,
     67			OMAP_DMIC_DMA_ENABLE);
     68
     69	omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl | dmic->ch_enabled);
     70}
     71
     72static inline void omap_dmic_stop(struct omap_dmic *dmic)
     73{
     74	u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
     75	omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
     76			ctrl & ~OMAP_DMIC_UP_ENABLE_MASK);
     77
     78	/* Disable DMA request generation */
     79	omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_CLR_REG,
     80			OMAP_DMIC_DMA_ENABLE);
     81
     82}
     83
     84static inline int dmic_is_enabled(struct omap_dmic *dmic)
     85{
     86	return omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG) &
     87						OMAP_DMIC_UP_ENABLE_MASK;
     88}
     89
     90static int omap_dmic_dai_startup(struct snd_pcm_substream *substream,
     91				  struct snd_soc_dai *dai)
     92{
     93	struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
     94	int ret = 0;
     95
     96	mutex_lock(&dmic->mutex);
     97
     98	if (!snd_soc_dai_active(dai))
     99		dmic->active = 1;
    100	else
    101		ret = -EBUSY;
    102
    103	mutex_unlock(&dmic->mutex);
    104
    105	return ret;
    106}
    107
    108static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream,
    109				    struct snd_soc_dai *dai)
    110{
    111	struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
    112
    113	mutex_lock(&dmic->mutex);
    114
    115	cpu_latency_qos_remove_request(&dmic->pm_qos_req);
    116
    117	if (!snd_soc_dai_active(dai))
    118		dmic->active = 0;
    119
    120	mutex_unlock(&dmic->mutex);
    121}
    122
    123static int omap_dmic_select_divider(struct omap_dmic *dmic, int sample_rate)
    124{
    125	int divider = -EINVAL;
    126
    127	/*
    128	 * 192KHz rate is only supported with 19.2MHz/3.84MHz clock
    129	 * configuration.
    130	 */
    131	if (sample_rate == 192000) {
    132		if (dmic->fclk_freq == 19200000 && dmic->out_freq == 3840000)
    133			divider = 0x6; /* Divider: 5 (192KHz sampling rate) */
    134		else
    135			dev_err(dmic->dev,
    136				"invalid clock configuration for 192KHz\n");
    137
    138		return divider;
    139	}
    140
    141	switch (dmic->out_freq) {
    142	case 1536000:
    143		if (dmic->fclk_freq != 24576000)
    144			goto div_err;
    145		divider = 0x4; /* Divider: 16 */
    146		break;
    147	case 2400000:
    148		switch (dmic->fclk_freq) {
    149		case 12000000:
    150			divider = 0x5; /* Divider: 5 */
    151			break;
    152		case 19200000:
    153			divider = 0x0; /* Divider: 8 */
    154			break;
    155		case 24000000:
    156			divider = 0x2; /* Divider: 10 */
    157			break;
    158		default:
    159			goto div_err;
    160		}
    161		break;
    162	case 3072000:
    163		if (dmic->fclk_freq != 24576000)
    164			goto div_err;
    165		divider = 0x3; /* Divider: 8 */
    166		break;
    167	case 3840000:
    168		if (dmic->fclk_freq != 19200000)
    169			goto div_err;
    170		divider = 0x1; /* Divider: 5 (96KHz sampling rate) */
    171		break;
    172	default:
    173		dev_err(dmic->dev, "invalid out frequency: %dHz\n",
    174			dmic->out_freq);
    175		break;
    176	}
    177
    178	return divider;
    179
    180div_err:
    181	dev_err(dmic->dev, "invalid out frequency %dHz for %dHz input\n",
    182		dmic->out_freq, dmic->fclk_freq);
    183	return -EINVAL;
    184}
    185
    186static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream,
    187				    struct snd_pcm_hw_params *params,
    188				    struct snd_soc_dai *dai)
    189{
    190	struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
    191	struct snd_dmaengine_dai_dma_data *dma_data;
    192	int channels;
    193
    194	dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params));
    195	if (dmic->clk_div < 0) {
    196		dev_err(dmic->dev, "no valid divider for %dHz from %dHz\n",
    197			dmic->out_freq, dmic->fclk_freq);
    198		return -EINVAL;
    199	}
    200
    201	dmic->ch_enabled = 0;
    202	channels = params_channels(params);
    203	switch (channels) {
    204	case 6:
    205		dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE;
    206		fallthrough;
    207	case 4:
    208		dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE;
    209		fallthrough;
    210	case 2:
    211		dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE;
    212		break;
    213	default:
    214		dev_err(dmic->dev, "invalid number of legacy channels\n");
    215		return -EINVAL;
    216	}
    217
    218	/* packet size is threshold * channels */
    219	dma_data = snd_soc_dai_get_dma_data(dai, substream);
    220	dma_data->maxburst = dmic->threshold * channels;
    221	dmic->latency = (OMAP_DMIC_THRES_MAX - dmic->threshold) * USEC_PER_SEC /
    222			params_rate(params);
    223
    224	return 0;
    225}
    226
    227static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream,
    228				  struct snd_soc_dai *dai)
    229{
    230	struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
    231	u32 ctrl;
    232
    233	if (cpu_latency_qos_request_active(&dmic->pm_qos_req))
    234		cpu_latency_qos_update_request(&dmic->pm_qos_req,
    235					       dmic->latency);
    236
    237	/* Configure uplink threshold */
    238	omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold);
    239
    240	ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
    241
    242	/* Set dmic out format */
    243	ctrl &= ~(OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK);
    244	ctrl |= (OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
    245		 OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
    246
    247	/* Configure dmic clock divider */
    248	ctrl &= ~OMAP_DMIC_CLK_DIV_MASK;
    249	ctrl |= OMAP_DMIC_CLK_DIV(dmic->clk_div);
    250
    251	omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl);
    252
    253	omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
    254			ctrl | OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
    255			OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
    256
    257	return 0;
    258}
    259
    260static int omap_dmic_dai_trigger(struct snd_pcm_substream *substream,
    261				  int cmd, struct snd_soc_dai *dai)
    262{
    263	struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
    264
    265	switch (cmd) {
    266	case SNDRV_PCM_TRIGGER_START:
    267		omap_dmic_start(dmic);
    268		break;
    269	case SNDRV_PCM_TRIGGER_STOP:
    270		omap_dmic_stop(dmic);
    271		break;
    272	default:
    273		break;
    274	}
    275
    276	return 0;
    277}
    278
    279static int omap_dmic_select_fclk(struct omap_dmic *dmic, int clk_id,
    280				 unsigned int freq)
    281{
    282	struct clk *parent_clk, *mux;
    283	char *parent_clk_name;
    284	int ret = 0;
    285
    286	switch (freq) {
    287	case 12000000:
    288	case 19200000:
    289	case 24000000:
    290	case 24576000:
    291		break;
    292	default:
    293		dev_err(dmic->dev, "invalid input frequency: %dHz\n", freq);
    294		dmic->fclk_freq = 0;
    295		return -EINVAL;
    296	}
    297
    298	if (dmic->sysclk == clk_id) {
    299		dmic->fclk_freq = freq;
    300		return 0;
    301	}
    302
    303	/* re-parent not allowed if a stream is ongoing */
    304	if (dmic->active && dmic_is_enabled(dmic)) {
    305		dev_err(dmic->dev, "can't re-parent when DMIC active\n");
    306		return -EBUSY;
    307	}
    308
    309	switch (clk_id) {
    310	case OMAP_DMIC_SYSCLK_PAD_CLKS:
    311		parent_clk_name = "pad_clks_ck";
    312		break;
    313	case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS:
    314		parent_clk_name = "slimbus_clk";
    315		break;
    316	case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS:
    317		parent_clk_name = "dmic_sync_mux_ck";
    318		break;
    319	default:
    320		dev_err(dmic->dev, "fclk clk_id (%d) not supported\n", clk_id);
    321		return -EINVAL;
    322	}
    323
    324	parent_clk = clk_get(dmic->dev, parent_clk_name);
    325	if (IS_ERR(parent_clk)) {
    326		dev_err(dmic->dev, "can't get %s\n", parent_clk_name);
    327		return -ENODEV;
    328	}
    329
    330	mux = clk_get_parent(dmic->fclk);
    331	if (IS_ERR(mux)) {
    332		dev_err(dmic->dev, "can't get fck mux parent\n");
    333		clk_put(parent_clk);
    334		return -ENODEV;
    335	}
    336
    337	mutex_lock(&dmic->mutex);
    338	if (dmic->active) {
    339		/* disable clock while reparenting */
    340		pm_runtime_put_sync(dmic->dev);
    341		ret = clk_set_parent(mux, parent_clk);
    342		pm_runtime_get_sync(dmic->dev);
    343	} else {
    344		ret = clk_set_parent(mux, parent_clk);
    345	}
    346	mutex_unlock(&dmic->mutex);
    347
    348	if (ret < 0) {
    349		dev_err(dmic->dev, "re-parent failed\n");
    350		goto err_busy;
    351	}
    352
    353	dmic->sysclk = clk_id;
    354	dmic->fclk_freq = freq;
    355
    356err_busy:
    357	clk_put(mux);
    358	clk_put(parent_clk);
    359
    360	return ret;
    361}
    362
    363static int omap_dmic_select_outclk(struct omap_dmic *dmic, int clk_id,
    364				    unsigned int freq)
    365{
    366	int ret = 0;
    367
    368	if (clk_id != OMAP_DMIC_ABE_DMIC_CLK) {
    369		dev_err(dmic->dev, "output clk_id (%d) not supported\n",
    370			clk_id);
    371		return -EINVAL;
    372	}
    373
    374	switch (freq) {
    375	case 1536000:
    376	case 2400000:
    377	case 3072000:
    378	case 3840000:
    379		dmic->out_freq = freq;
    380		break;
    381	default:
    382		dev_err(dmic->dev, "invalid out frequency: %dHz\n", freq);
    383		dmic->out_freq = 0;
    384		ret = -EINVAL;
    385	}
    386
    387	return ret;
    388}
    389
    390static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
    391				    unsigned int freq, int dir)
    392{
    393	struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
    394
    395	if (dir == SND_SOC_CLOCK_IN)
    396		return omap_dmic_select_fclk(dmic, clk_id, freq);
    397	else if (dir == SND_SOC_CLOCK_OUT)
    398		return omap_dmic_select_outclk(dmic, clk_id, freq);
    399
    400	dev_err(dmic->dev, "invalid clock direction (%d)\n", dir);
    401	return -EINVAL;
    402}
    403
    404static const struct snd_soc_dai_ops omap_dmic_dai_ops = {
    405	.startup	= omap_dmic_dai_startup,
    406	.shutdown	= omap_dmic_dai_shutdown,
    407	.hw_params	= omap_dmic_dai_hw_params,
    408	.prepare	= omap_dmic_dai_prepare,
    409	.trigger	= omap_dmic_dai_trigger,
    410	.set_sysclk	= omap_dmic_set_dai_sysclk,
    411};
    412
    413static int omap_dmic_probe(struct snd_soc_dai *dai)
    414{
    415	struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
    416
    417	pm_runtime_enable(dmic->dev);
    418
    419	/* Disable lines while request is ongoing */
    420	pm_runtime_get_sync(dmic->dev);
    421	omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, 0x00);
    422	pm_runtime_put_sync(dmic->dev);
    423
    424	/* Configure DMIC threshold value */
    425	dmic->threshold = OMAP_DMIC_THRES_MAX - 3;
    426
    427	snd_soc_dai_init_dma_data(dai, NULL, &dmic->dma_data);
    428
    429	return 0;
    430}
    431
    432static int omap_dmic_remove(struct snd_soc_dai *dai)
    433{
    434	struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
    435
    436	pm_runtime_disable(dmic->dev);
    437
    438	return 0;
    439}
    440
    441static struct snd_soc_dai_driver omap_dmic_dai = {
    442	.name = "omap-dmic",
    443	.probe = omap_dmic_probe,
    444	.remove = omap_dmic_remove,
    445	.capture = {
    446		.channels_min = 2,
    447		.channels_max = 6,
    448		.rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
    449		.formats = SNDRV_PCM_FMTBIT_S32_LE,
    450		.sig_bits = 24,
    451	},
    452	.ops = &omap_dmic_dai_ops,
    453};
    454
    455static const struct snd_soc_component_driver omap_dmic_component = {
    456	.name		= "omap-dmic",
    457};
    458
    459static int asoc_dmic_probe(struct platform_device *pdev)
    460{
    461	struct omap_dmic *dmic;
    462	struct resource *res;
    463	int ret;
    464
    465	dmic = devm_kzalloc(&pdev->dev, sizeof(struct omap_dmic), GFP_KERNEL);
    466	if (!dmic)
    467		return -ENOMEM;
    468
    469	platform_set_drvdata(pdev, dmic);
    470	dmic->dev = &pdev->dev;
    471	dmic->sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS;
    472
    473	mutex_init(&dmic->mutex);
    474
    475	dmic->fclk = devm_clk_get(dmic->dev, "fck");
    476	if (IS_ERR(dmic->fclk)) {
    477		dev_err(dmic->dev, "can't get fck\n");
    478		return -ENODEV;
    479	}
    480
    481	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
    482	if (!res) {
    483		dev_err(dmic->dev, "invalid dma memory resource\n");
    484		return -ENODEV;
    485	}
    486	dmic->dma_data.addr = res->start + OMAP_DMIC_DATA_REG;
    487
    488	dmic->dma_data.filter_data = "up_link";
    489
    490	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
    491	dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
    492	if (IS_ERR(dmic->io_base))
    493		return PTR_ERR(dmic->io_base);
    494
    495
    496	ret = devm_snd_soc_register_component(&pdev->dev,
    497					      &omap_dmic_component,
    498					      &omap_dmic_dai, 1);
    499	if (ret)
    500		return ret;
    501
    502	ret = sdma_pcm_platform_register(&pdev->dev, NULL, "up_link");
    503	if (ret)
    504		return ret;
    505
    506	return 0;
    507}
    508
    509static const struct of_device_id omap_dmic_of_match[] = {
    510	{ .compatible = "ti,omap4-dmic", },
    511	{ }
    512};
    513MODULE_DEVICE_TABLE(of, omap_dmic_of_match);
    514
    515static struct platform_driver asoc_dmic_driver = {
    516	.driver = {
    517		.name = "omap-dmic",
    518		.of_match_table = omap_dmic_of_match,
    519	},
    520	.probe = asoc_dmic_probe,
    521};
    522
    523module_platform_driver(asoc_dmic_driver);
    524
    525MODULE_ALIAS("platform:omap-dmic");
    526MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
    527MODULE_DESCRIPTION("OMAP DMIC ASoC Interface");
    528MODULE_LICENSE("GPL");