brintos

brintos / linux-shallow public Read only

0
0
Text · 10.8 KiB · 3800112 Raw
431 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tegra20_spdif.c - Tegra20 SPDIF driver4 *5 * Author: Stephen Warren <swarren@nvidia.com>6 * Copyright (C) 2011-2012 - NVIDIA, Inc.7 */8 9#include <linux/clk.h>10#include <linux/delay.h>11#include <linux/device.h>12#include <linux/io.h>13#include <linux/mod_devicetable.h>14#include <linux/module.h>15#include <linux/platform_device.h>16#include <linux/pm_runtime.h>17#include <linux/regmap.h>18#include <linux/reset.h>19#include <linux/slab.h>20#include <sound/core.h>21#include <sound/pcm.h>22#include <sound/pcm_params.h>23#include <sound/soc.h>24#include <sound/dmaengine_pcm.h>25 26#include "tegra20_spdif.h"27 28static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev)29{30	struct tegra20_spdif *spdif = dev_get_drvdata(dev);31 32	regcache_cache_only(spdif->regmap, true);33 34	clk_disable_unprepare(spdif->clk_spdif_out);35 36	return 0;37}38 39static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev)40{41	struct tegra20_spdif *spdif = dev_get_drvdata(dev);42	int ret;43 44	ret = reset_control_assert(spdif->reset);45	if (ret)46		return ret;47 48	ret = clk_prepare_enable(spdif->clk_spdif_out);49	if (ret) {50		dev_err(dev, "clk_enable failed: %d\n", ret);51		return ret;52	}53 54	usleep_range(10, 100);55 56	ret = reset_control_deassert(spdif->reset);57	if (ret)58		goto disable_clocks;59 60	regcache_cache_only(spdif->regmap, false);61	regcache_mark_dirty(spdif->regmap);62 63	ret = regcache_sync(spdif->regmap);64	if (ret)65		goto disable_clocks;66 67	return 0;68 69disable_clocks:70	clk_disable_unprepare(spdif->clk_spdif_out);71 72	return ret;73}74 75static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,76				   struct snd_pcm_hw_params *params,77				   struct snd_soc_dai *dai)78{79	struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);80	unsigned int mask = 0, val = 0;81	int ret, spdifclock;82	long rate;83 84	mask |= TEGRA20_SPDIF_CTRL_PACK |85		TEGRA20_SPDIF_CTRL_BIT_MODE_MASK;86	switch (params_format(params)) {87	case SNDRV_PCM_FORMAT_S16_LE:88		val |= TEGRA20_SPDIF_CTRL_PACK |89		       TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT;90		break;91	default:92		return -EINVAL;93	}94 95	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val);96 97	/*98	 * FIFO trigger level must be bigger than DMA burst or equal to it,99	 * otherwise data is discarded on overflow.100	 */101	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_DATA_FIFO_CSR,102			   TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK,103			   TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL);104 105	switch (params_rate(params)) {106	case 32000:107		spdifclock = 4096000;108		break;109	case 44100:110		spdifclock = 5644800;111		break;112	case 48000:113		spdifclock = 6144000;114		break;115	case 88200:116		spdifclock = 11289600;117		break;118	case 96000:119		spdifclock = 12288000;120		break;121	case 176400:122		spdifclock = 22579200;123		break;124	case 192000:125		spdifclock = 24576000;126		break;127	default:128		return -EINVAL;129	}130 131	ret = clk_set_rate(spdif->clk_spdif_out, spdifclock);132	if (ret) {133		dev_err(dai->dev, "Can't set SPDIF clock rate: %d\n", ret);134		return ret;135	}136 137	rate = clk_get_rate(spdif->clk_spdif_out);138	if (rate != spdifclock)139		dev_warn_once(dai->dev,140			      "SPDIF clock rate %d doesn't match requested rate %lu\n",141			      spdifclock, rate);142 143	return 0;144}145 146static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif)147{148	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,149			   TEGRA20_SPDIF_CTRL_TX_EN,150			   TEGRA20_SPDIF_CTRL_TX_EN);151}152 153static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif)154{155	regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL,156			   TEGRA20_SPDIF_CTRL_TX_EN, 0);157}158 159static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,160				 struct snd_soc_dai *dai)161{162	struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);163 164	switch (cmd) {165	case SNDRV_PCM_TRIGGER_START:166	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:167	case SNDRV_PCM_TRIGGER_RESUME:168		tegra20_spdif_start_playback(spdif);169		break;170	case SNDRV_PCM_TRIGGER_STOP:171	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:172	case SNDRV_PCM_TRIGGER_SUSPEND:173		tegra20_spdif_stop_playback(spdif);174		break;175	default:176		return -EINVAL;177	}178 179	return 0;180}181 182static int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params,183				      struct snd_pcm_hw_rule *rule)184{185	struct snd_interval *r = hw_param_interval(params, rule->var);186	struct snd_soc_dai *dai = rule->private;187	struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);188	struct clk *parent = clk_get_parent(spdif->clk_spdif_out);189	static const unsigned int rates[] = { 32000, 44100, 48000 };190	unsigned long i, parent_rate, valid_rates = 0;191 192	parent_rate = clk_get_rate(parent);193	if (!parent_rate) {194		dev_err(dai->dev, "Can't get parent clock rate\n");195		return -EINVAL;196	}197 198	for (i = 0; i < ARRAY_SIZE(rates); i++) {199		if (parent_rate % (rates[i] * 128) == 0)200			valid_rates |= BIT(i);201	}202 203	/*204	 * At least one rate must be valid, otherwise the parent clock isn't205	 * audio PLL. Nothing should be filtered in this case.206	 */207	if (!valid_rates)208		valid_rates = BIT(ARRAY_SIZE(rates)) - 1;209 210	return snd_interval_list(r, ARRAY_SIZE(rates), rates, valid_rates);211}212 213static int tegra20_spdif_startup(struct snd_pcm_substream *substream,214				 struct snd_soc_dai *dai)215{216	if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate"))217		return 0;218 219	/*220	 * SPDIF and I2S share audio PLL. HDMI takes audio packets from SPDIF221	 * and audio may not work on some TVs if clock rate isn't precise.222	 *223	 * PLL rate is controlled by I2S side. Filter out audio rates that224	 * don't match PLL rate at the start of stream to allow both SPDIF225	 * and I2S work simultaneously, assuming that PLL rate won't be226	 * changed later on.227	 */228	return snd_pcm_hw_rule_add(substream->runtime, 0,229				   SNDRV_PCM_HW_PARAM_RATE,230				   tegra20_spdif_filter_rates, dai,231				   SNDRV_PCM_HW_PARAM_RATE, -1);232}233 234static int tegra20_spdif_probe(struct snd_soc_dai *dai)235{236	struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);237 238	snd_soc_dai_init_dma_data(dai, &spdif->playback_dma_data, NULL);239 240	return 0;241}242 243static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = {244	.probe = tegra20_spdif_probe,245	.hw_params = tegra20_spdif_hw_params,246	.trigger = tegra20_spdif_trigger,247	.startup = tegra20_spdif_startup,248};249 250static struct snd_soc_dai_driver tegra20_spdif_dai = {251	.name = "tegra20-spdif",252	.playback = {253		.stream_name = "Playback",254		.channels_min = 2,255		.channels_max = 2,256		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |257			 SNDRV_PCM_RATE_48000,258		.formats = SNDRV_PCM_FMTBIT_S16_LE,259	},260	.ops = &tegra20_spdif_dai_ops,261};262 263static const struct snd_soc_component_driver tegra20_spdif_component = {264	.name = "tegra20-spdif",265	.legacy_dai_naming = 1,266};267 268static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg)269{270	switch (reg) {271	case TEGRA20_SPDIF_CTRL:272	case TEGRA20_SPDIF_STATUS:273	case TEGRA20_SPDIF_STROBE_CTRL:274	case TEGRA20_SPDIF_DATA_FIFO_CSR:275	case TEGRA20_SPDIF_DATA_OUT:276	case TEGRA20_SPDIF_DATA_IN:277	case TEGRA20_SPDIF_CH_STA_RX_A:278	case TEGRA20_SPDIF_CH_STA_RX_B:279	case TEGRA20_SPDIF_CH_STA_RX_C:280	case TEGRA20_SPDIF_CH_STA_RX_D:281	case TEGRA20_SPDIF_CH_STA_RX_E:282	case TEGRA20_SPDIF_CH_STA_RX_F:283	case TEGRA20_SPDIF_CH_STA_TX_A:284	case TEGRA20_SPDIF_CH_STA_TX_B:285	case TEGRA20_SPDIF_CH_STA_TX_C:286	case TEGRA20_SPDIF_CH_STA_TX_D:287	case TEGRA20_SPDIF_CH_STA_TX_E:288	case TEGRA20_SPDIF_CH_STA_TX_F:289	case TEGRA20_SPDIF_USR_STA_RX_A:290	case TEGRA20_SPDIF_USR_DAT_TX_A:291		return true;292	default:293		return false;294	}295}296 297static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg)298{299	switch (reg) {300	case TEGRA20_SPDIF_STATUS:301	case TEGRA20_SPDIF_DATA_FIFO_CSR:302	case TEGRA20_SPDIF_DATA_OUT:303	case TEGRA20_SPDIF_DATA_IN:304	case TEGRA20_SPDIF_CH_STA_RX_A:305	case TEGRA20_SPDIF_CH_STA_RX_B:306	case TEGRA20_SPDIF_CH_STA_RX_C:307	case TEGRA20_SPDIF_CH_STA_RX_D:308	case TEGRA20_SPDIF_CH_STA_RX_E:309	case TEGRA20_SPDIF_CH_STA_RX_F:310	case TEGRA20_SPDIF_USR_STA_RX_A:311	case TEGRA20_SPDIF_USR_DAT_TX_A:312		return true;313	default:314		return false;315	}316}317 318static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg)319{320	switch (reg) {321	case TEGRA20_SPDIF_DATA_OUT:322	case TEGRA20_SPDIF_DATA_IN:323	case TEGRA20_SPDIF_USR_STA_RX_A:324	case TEGRA20_SPDIF_USR_DAT_TX_A:325		return true;326	default:327		return false;328	}329}330 331static const struct regmap_config tegra20_spdif_regmap_config = {332	.reg_bits = 32,333	.reg_stride = 4,334	.val_bits = 32,335	.max_register = TEGRA20_SPDIF_USR_DAT_TX_A,336	.writeable_reg = tegra20_spdif_wr_rd_reg,337	.readable_reg = tegra20_spdif_wr_rd_reg,338	.volatile_reg = tegra20_spdif_volatile_reg,339	.precious_reg = tegra20_spdif_precious_reg,340	.cache_type = REGCACHE_FLAT,341};342 343static int tegra20_spdif_platform_probe(struct platform_device *pdev)344{345	struct tegra20_spdif *spdif;346	struct resource *mem;347	void __iomem *regs;348	int ret;349 350	spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),351			     GFP_KERNEL);352	if (!spdif)353		return -ENOMEM;354 355	dev_set_drvdata(&pdev->dev, spdif);356 357	spdif->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);358	if (IS_ERR(spdif->reset)) {359		dev_err(&pdev->dev, "Can't retrieve spdif reset\n");360		return PTR_ERR(spdif->reset);361	}362 363	spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out");364	if (IS_ERR(spdif->clk_spdif_out)) {365		dev_err(&pdev->dev, "Could not retrieve spdif clock\n");366		return PTR_ERR(spdif->clk_spdif_out);367	}368 369	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);370	if (IS_ERR(regs))371		return PTR_ERR(regs);372 373	spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,374					      &tegra20_spdif_regmap_config);375	if (IS_ERR(spdif->regmap)) {376		dev_err(&pdev->dev, "regmap init failed\n");377		return PTR_ERR(spdif->regmap);378	}379 380	spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT;381	spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;382	spdif->playback_dma_data.maxburst = 4;383 384	ret = devm_pm_runtime_enable(&pdev->dev);385	if (ret)386		return ret;387 388	ret = devm_snd_soc_register_component(&pdev->dev,389					      &tegra20_spdif_component,390					      &tegra20_spdif_dai, 1);391	if (ret) {392		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);393		return ret;394	}395 396	ret = devm_tegra_pcm_platform_register(&pdev->dev);397	if (ret) {398		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);399		return ret;400	}401 402	return 0;403}404 405static const struct dev_pm_ops tegra20_spdif_pm_ops = {406	SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,407			   tegra20_spdif_runtime_resume, NULL)408	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,409				pm_runtime_force_resume)410};411 412static const struct of_device_id tegra20_spdif_of_match[] = {413	{ .compatible = "nvidia,tegra20-spdif", },414	{},415};416MODULE_DEVICE_TABLE(of, tegra20_spdif_of_match);417 418static struct platform_driver tegra20_spdif_driver = {419	.driver = {420		.name = "tegra20-spdif",421		.pm = &tegra20_spdif_pm_ops,422		.of_match_table = tegra20_spdif_of_match,423	},424	.probe = tegra20_spdif_platform_probe,425};426module_platform_driver(tegra20_spdif_driver);427 428MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");429MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver");430MODULE_LICENSE("GPL");431