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

tegra20_ac97.c (11756B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * tegra20_ac97.c - Tegra20 AC97 platform driver
      4 *
      5 * Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
      6 *
      7 * Partly based on code copyright/by:
      8 *
      9 * Copyright (c) 2011,2012 Toradex Inc.
     10 */
     11
     12#include <linux/clk.h>
     13#include <linux/delay.h>
     14#include <linux/device.h>
     15#include <linux/gpio.h>
     16#include <linux/io.h>
     17#include <linux/jiffies.h>
     18#include <linux/module.h>
     19#include <linux/of.h>
     20#include <linux/of_gpio.h>
     21#include <linux/platform_device.h>
     22#include <linux/pm_runtime.h>
     23#include <linux/regmap.h>
     24#include <linux/reset.h>
     25#include <linux/slab.h>
     26#include <sound/core.h>
     27#include <sound/pcm.h>
     28#include <sound/pcm_params.h>
     29#include <sound/soc.h>
     30#include <sound/dmaengine_pcm.h>
     31
     32#include "tegra20_ac97.h"
     33
     34#define DRV_NAME "tegra20-ac97"
     35
     36static struct tegra20_ac97 *workdata;
     37
     38static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97)
     39{
     40	u32 readback;
     41	unsigned long timeout;
     42
     43	/* reset line is not driven by DAC pad group, have to toggle GPIO */
     44	gpio_set_value(workdata->reset_gpio, 0);
     45	udelay(2);
     46
     47	gpio_set_value(workdata->reset_gpio, 1);
     48	udelay(2);
     49
     50	timeout = jiffies + msecs_to_jiffies(100);
     51
     52	do {
     53		regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
     54		if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
     55			break;
     56		usleep_range(1000, 2000);
     57	} while (!time_after(jiffies, timeout));
     58}
     59
     60static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97)
     61{
     62	u32 readback;
     63	unsigned long timeout;
     64
     65	/*
     66	 * although sync line is driven by the DAC pad group warm reset using
     67	 * the controller cmd is not working, have to toggle sync line
     68	 * manually.
     69	 */
     70	gpio_request(workdata->sync_gpio, "codec-sync");
     71
     72	gpio_direction_output(workdata->sync_gpio, 1);
     73
     74	udelay(2);
     75	gpio_set_value(workdata->sync_gpio, 0);
     76	udelay(2);
     77	gpio_free(workdata->sync_gpio);
     78
     79	timeout = jiffies + msecs_to_jiffies(100);
     80
     81	do {
     82		regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
     83		if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
     84			break;
     85		usleep_range(1000, 2000);
     86	} while (!time_after(jiffies, timeout));
     87}
     88
     89static unsigned short tegra20_ac97_codec_read(struct snd_ac97 *ac97_snd,
     90					      unsigned short reg)
     91{
     92	u32 readback;
     93	unsigned long timeout;
     94
     95	regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
     96		     (((reg | 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
     97		      TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
     98		     TEGRA20_AC97_CMD_BUSY);
     99
    100	timeout = jiffies + msecs_to_jiffies(100);
    101
    102	do {
    103		regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
    104		if (readback & TEGRA20_AC97_STATUS1_STA_VALID1)
    105			break;
    106		usleep_range(1000, 2000);
    107	} while (!time_after(jiffies, timeout));
    108
    109	return ((readback & TEGRA20_AC97_STATUS1_STA_DATA1_MASK) >>
    110		TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT);
    111}
    112
    113static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd,
    114				     unsigned short reg, unsigned short val)
    115{
    116	u32 readback;
    117	unsigned long timeout;
    118
    119	regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
    120		     ((reg << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
    121		      TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
    122		     ((val << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) &
    123		      TEGRA20_AC97_CMD_CMD_DATA_MASK) |
    124		     TEGRA20_AC97_CMD_BUSY);
    125
    126	timeout = jiffies + msecs_to_jiffies(100);
    127
    128	do {
    129		regmap_read(workdata->regmap, TEGRA20_AC97_CMD, &readback);
    130		if (!(readback & TEGRA20_AC97_CMD_BUSY))
    131			break;
    132		usleep_range(1000, 2000);
    133	} while (!time_after(jiffies, timeout));
    134}
    135
    136static struct snd_ac97_bus_ops tegra20_ac97_ops = {
    137	.read		= tegra20_ac97_codec_read,
    138	.write		= tegra20_ac97_codec_write,
    139	.reset		= tegra20_ac97_codec_reset,
    140	.warm_reset	= tegra20_ac97_codec_warm_reset,
    141};
    142
    143static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97)
    144{
    145	regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
    146			   TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN,
    147			   TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN);
    148
    149	regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
    150			   TEGRA20_AC97_CTRL_PCM_DAC_EN |
    151			   TEGRA20_AC97_CTRL_STM_EN,
    152			   TEGRA20_AC97_CTRL_PCM_DAC_EN |
    153			   TEGRA20_AC97_CTRL_STM_EN);
    154}
    155
    156static inline void tegra20_ac97_stop_playback(struct tegra20_ac97 *ac97)
    157{
    158	regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
    159			   TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 0);
    160
    161	regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
    162			   TEGRA20_AC97_CTRL_PCM_DAC_EN, 0);
    163}
    164
    165static inline void tegra20_ac97_start_capture(struct tegra20_ac97 *ac97)
    166{
    167	regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
    168			   TEGRA20_AC97_FIFO_SCR_REC_FULL_EN,
    169			   TEGRA20_AC97_FIFO_SCR_REC_FULL_EN);
    170}
    171
    172static inline void tegra20_ac97_stop_capture(struct tegra20_ac97 *ac97)
    173{
    174	regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
    175			   TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 0);
    176}
    177
    178static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
    179				struct snd_soc_dai *dai)
    180{
    181	struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
    182
    183	switch (cmd) {
    184	case SNDRV_PCM_TRIGGER_START:
    185	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    186	case SNDRV_PCM_TRIGGER_RESUME:
    187		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    188			tegra20_ac97_start_playback(ac97);
    189		else
    190			tegra20_ac97_start_capture(ac97);
    191		break;
    192	case SNDRV_PCM_TRIGGER_STOP:
    193	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    194	case SNDRV_PCM_TRIGGER_SUSPEND:
    195		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
    196			tegra20_ac97_stop_playback(ac97);
    197		else
    198			tegra20_ac97_stop_capture(ac97);
    199		break;
    200	default:
    201		return -EINVAL;
    202	}
    203
    204	return 0;
    205}
    206
    207static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = {
    208	.trigger	= tegra20_ac97_trigger,
    209};
    210
    211static int tegra20_ac97_probe(struct snd_soc_dai *dai)
    212{
    213	struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
    214
    215	dai->capture_dma_data = &ac97->capture_dma_data;
    216	dai->playback_dma_data = &ac97->playback_dma_data;
    217
    218	return 0;
    219}
    220
    221static struct snd_soc_dai_driver tegra20_ac97_dai = {
    222	.name = "tegra-ac97-pcm",
    223	.probe = tegra20_ac97_probe,
    224	.playback = {
    225		.stream_name = "PCM Playback",
    226		.channels_min = 2,
    227		.channels_max = 2,
    228		.rates = SNDRV_PCM_RATE_8000_48000,
    229		.formats = SNDRV_PCM_FMTBIT_S16_LE,
    230	},
    231	.capture = {
    232		.stream_name = "PCM Capture",
    233		.channels_min = 2,
    234		.channels_max = 2,
    235		.rates = SNDRV_PCM_RATE_8000_48000,
    236		.formats = SNDRV_PCM_FMTBIT_S16_LE,
    237	},
    238	.ops = &tegra20_ac97_dai_ops,
    239};
    240
    241static const struct snd_soc_component_driver tegra20_ac97_component = {
    242	.name		= DRV_NAME,
    243};
    244
    245static bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg)
    246{
    247	switch (reg) {
    248	case TEGRA20_AC97_CTRL:
    249	case TEGRA20_AC97_CMD:
    250	case TEGRA20_AC97_STATUS1:
    251	case TEGRA20_AC97_FIFO1_SCR:
    252	case TEGRA20_AC97_FIFO_TX1:
    253	case TEGRA20_AC97_FIFO_RX1:
    254		return true;
    255	default:
    256		break;
    257	}
    258
    259	return false;
    260}
    261
    262static bool tegra20_ac97_volatile_reg(struct device *dev, unsigned int reg)
    263{
    264	switch (reg) {
    265	case TEGRA20_AC97_STATUS1:
    266	case TEGRA20_AC97_FIFO1_SCR:
    267	case TEGRA20_AC97_FIFO_TX1:
    268	case TEGRA20_AC97_FIFO_RX1:
    269		return true;
    270	default:
    271		break;
    272	}
    273
    274	return false;
    275}
    276
    277static bool tegra20_ac97_precious_reg(struct device *dev, unsigned int reg)
    278{
    279	switch (reg) {
    280	case TEGRA20_AC97_FIFO_TX1:
    281	case TEGRA20_AC97_FIFO_RX1:
    282		return true;
    283	default:
    284		break;
    285	}
    286
    287	return false;
    288}
    289
    290static const struct regmap_config tegra20_ac97_regmap_config = {
    291	.reg_bits = 32,
    292	.reg_stride = 4,
    293	.val_bits = 32,
    294	.max_register = TEGRA20_AC97_FIFO_RX1,
    295	.writeable_reg = tegra20_ac97_wr_rd_reg,
    296	.readable_reg = tegra20_ac97_wr_rd_reg,
    297	.volatile_reg = tegra20_ac97_volatile_reg,
    298	.precious_reg = tegra20_ac97_precious_reg,
    299	.cache_type = REGCACHE_FLAT,
    300};
    301
    302static int tegra20_ac97_platform_probe(struct platform_device *pdev)
    303{
    304	struct tegra20_ac97 *ac97;
    305	struct resource *mem;
    306	void __iomem *regs;
    307	int ret = 0;
    308
    309	ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97),
    310			    GFP_KERNEL);
    311	if (!ac97) {
    312		ret = -ENOMEM;
    313		goto err;
    314	}
    315	dev_set_drvdata(&pdev->dev, ac97);
    316
    317	ac97->reset = devm_reset_control_get_exclusive(&pdev->dev, "ac97");
    318	if (IS_ERR(ac97->reset)) {
    319		dev_err(&pdev->dev, "Can't retrieve ac97 reset\n");
    320		return PTR_ERR(ac97->reset);
    321	}
    322
    323	ac97->clk_ac97 = devm_clk_get(&pdev->dev, NULL);
    324	if (IS_ERR(ac97->clk_ac97)) {
    325		dev_err(&pdev->dev, "Can't retrieve ac97 clock\n");
    326		ret = PTR_ERR(ac97->clk_ac97);
    327		goto err;
    328	}
    329
    330	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    331	regs = devm_ioremap_resource(&pdev->dev, mem);
    332	if (IS_ERR(regs)) {
    333		ret = PTR_ERR(regs);
    334		goto err_clk_put;
    335	}
    336
    337	ac97->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
    338					    &tegra20_ac97_regmap_config);
    339	if (IS_ERR(ac97->regmap)) {
    340		dev_err(&pdev->dev, "regmap init failed\n");
    341		ret = PTR_ERR(ac97->regmap);
    342		goto err_clk_put;
    343	}
    344
    345	ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node,
    346					     "nvidia,codec-reset-gpio", 0);
    347	if (gpio_is_valid(ac97->reset_gpio)) {
    348		ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio,
    349					    GPIOF_OUT_INIT_HIGH, "codec-reset");
    350		if (ret) {
    351			dev_err(&pdev->dev, "could not get codec-reset GPIO\n");
    352			goto err_clk_put;
    353		}
    354	} else {
    355		dev_err(&pdev->dev, "no codec-reset GPIO supplied\n");
    356		goto err_clk_put;
    357	}
    358
    359	ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node,
    360					    "nvidia,codec-sync-gpio", 0);
    361	if (!gpio_is_valid(ac97->sync_gpio)) {
    362		dev_err(&pdev->dev, "no codec-sync GPIO supplied\n");
    363		goto err_clk_put;
    364	}
    365
    366	ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1;
    367	ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    368	ac97->capture_dma_data.maxburst = 4;
    369
    370	ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1;
    371	ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
    372	ac97->playback_dma_data.maxburst = 4;
    373
    374	ret = reset_control_assert(ac97->reset);
    375	if (ret) {
    376		dev_err(&pdev->dev, "Failed to assert AC'97 reset: %d\n", ret);
    377		goto err_clk_put;
    378	}
    379
    380	ret = clk_prepare_enable(ac97->clk_ac97);
    381	if (ret) {
    382		dev_err(&pdev->dev, "clk_enable failed: %d\n", ret);
    383		goto err_clk_put;
    384	}
    385
    386	usleep_range(10, 100);
    387
    388	ret = reset_control_deassert(ac97->reset);
    389	if (ret) {
    390		dev_err(&pdev->dev, "Failed to deassert AC'97 reset: %d\n", ret);
    391		goto err_clk_disable_unprepare;
    392	}
    393
    394	ret = snd_soc_set_ac97_ops(&tegra20_ac97_ops);
    395	if (ret) {
    396		dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret);
    397		goto err_clk_disable_unprepare;
    398	}
    399
    400	ret = snd_soc_register_component(&pdev->dev, &tegra20_ac97_component,
    401					 &tegra20_ac97_dai, 1);
    402	if (ret) {
    403		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
    404		ret = -ENOMEM;
    405		goto err_clk_disable_unprepare;
    406	}
    407
    408	ret = tegra_pcm_platform_register(&pdev->dev);
    409	if (ret) {
    410		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
    411		goto err_unregister_component;
    412	}
    413
    414	/* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
    415	workdata = ac97;
    416
    417	return 0;
    418
    419err_unregister_component:
    420	snd_soc_unregister_component(&pdev->dev);
    421err_clk_disable_unprepare:
    422	clk_disable_unprepare(ac97->clk_ac97);
    423err_clk_put:
    424err:
    425	snd_soc_set_ac97_ops(NULL);
    426	return ret;
    427}
    428
    429static int tegra20_ac97_platform_remove(struct platform_device *pdev)
    430{
    431	struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev);
    432
    433	tegra_pcm_platform_unregister(&pdev->dev);
    434	snd_soc_unregister_component(&pdev->dev);
    435
    436	clk_disable_unprepare(ac97->clk_ac97);
    437
    438	snd_soc_set_ac97_ops(NULL);
    439
    440	return 0;
    441}
    442
    443static const struct of_device_id tegra20_ac97_of_match[] = {
    444	{ .compatible = "nvidia,tegra20-ac97", },
    445	{},
    446};
    447
    448static struct platform_driver tegra20_ac97_driver = {
    449	.driver = {
    450		.name = DRV_NAME,
    451		.of_match_table = tegra20_ac97_of_match,
    452	},
    453	.probe = tegra20_ac97_platform_probe,
    454	.remove = tegra20_ac97_platform_remove,
    455};
    456module_platform_driver(tegra20_ac97_driver);
    457
    458MODULE_AUTHOR("Lucas Stach");
    459MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver");
    460MODULE_LICENSE("GPL v2");
    461MODULE_ALIAS("platform:" DRV_NAME);
    462MODULE_DEVICE_TABLE(of, tegra20_ac97_of_match);