162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * tegra20_spdif.c - Tegra20 SPDIF driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Stephen Warren <swarren@nvidia.com> 662306a36Sopenharmony_ci * Copyright (C) 2011-2012 - NVIDIA, Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of_device.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/regmap.h> 1862306a36Sopenharmony_ci#include <linux/reset.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <sound/core.h> 2162306a36Sopenharmony_ci#include <sound/pcm.h> 2262306a36Sopenharmony_ci#include <sound/pcm_params.h> 2362306a36Sopenharmony_ci#include <sound/soc.h> 2462306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "tegra20_spdif.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct tegra20_spdif *spdif = dev_get_drvdata(dev); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci regcache_cache_only(spdif->regmap, true); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci clk_disable_unprepare(spdif->clk_spdif_out); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct tegra20_spdif *spdif = dev_get_drvdata(dev); 4262306a36Sopenharmony_ci int ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci ret = reset_control_assert(spdif->reset); 4562306a36Sopenharmony_ci if (ret) 4662306a36Sopenharmony_ci return ret; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci ret = clk_prepare_enable(spdif->clk_spdif_out); 4962306a36Sopenharmony_ci if (ret) { 5062306a36Sopenharmony_ci dev_err(dev, "clk_enable failed: %d\n", ret); 5162306a36Sopenharmony_ci return ret; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci usleep_range(10, 100); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci ret = reset_control_deassert(spdif->reset); 5762306a36Sopenharmony_ci if (ret) 5862306a36Sopenharmony_ci goto disable_clocks; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci regcache_cache_only(spdif->regmap, false); 6162306a36Sopenharmony_ci regcache_mark_dirty(spdif->regmap); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci ret = regcache_sync(spdif->regmap); 6462306a36Sopenharmony_ci if (ret) 6562306a36Sopenharmony_ci goto disable_clocks; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cidisable_clocks: 7062306a36Sopenharmony_ci clk_disable_unprepare(spdif->clk_spdif_out); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return ret; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, 7662306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 7762306a36Sopenharmony_ci struct snd_soc_dai *dai) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); 8062306a36Sopenharmony_ci unsigned int mask = 0, val = 0; 8162306a36Sopenharmony_ci int ret, spdifclock; 8262306a36Sopenharmony_ci long rate; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci mask |= TEGRA20_SPDIF_CTRL_PACK | 8562306a36Sopenharmony_ci TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; 8662306a36Sopenharmony_ci switch (params_format(params)) { 8762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 8862306a36Sopenharmony_ci val |= TEGRA20_SPDIF_CTRL_PACK | 8962306a36Sopenharmony_ci TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT; 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci default: 9262306a36Sopenharmony_ci return -EINVAL; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* 9862306a36Sopenharmony_ci * FIFO trigger level must be bigger than DMA burst or equal to it, 9962306a36Sopenharmony_ci * otherwise data is discarded on overflow. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_DATA_FIFO_CSR, 10262306a36Sopenharmony_ci TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK, 10362306a36Sopenharmony_ci TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci switch (params_rate(params)) { 10662306a36Sopenharmony_ci case 32000: 10762306a36Sopenharmony_ci spdifclock = 4096000; 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci case 44100: 11062306a36Sopenharmony_ci spdifclock = 5644800; 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci case 48000: 11362306a36Sopenharmony_ci spdifclock = 6144000; 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case 88200: 11662306a36Sopenharmony_ci spdifclock = 11289600; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci case 96000: 11962306a36Sopenharmony_ci spdifclock = 12288000; 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci case 176400: 12262306a36Sopenharmony_ci spdifclock = 22579200; 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci case 192000: 12562306a36Sopenharmony_ci spdifclock = 24576000; 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci default: 12862306a36Sopenharmony_ci return -EINVAL; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); 13262306a36Sopenharmony_ci if (ret) { 13362306a36Sopenharmony_ci dev_err(dai->dev, "Can't set SPDIF clock rate: %d\n", ret); 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci rate = clk_get_rate(spdif->clk_spdif_out); 13862306a36Sopenharmony_ci if (rate != spdifclock) 13962306a36Sopenharmony_ci dev_warn_once(dai->dev, 14062306a36Sopenharmony_ci "SPDIF clock rate %d doesn't match requested rate %lu\n", 14162306a36Sopenharmony_ci spdifclock, rate); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void tegra20_spdif_start_playback(struct tegra20_spdif *spdif) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, 14962306a36Sopenharmony_ci TEGRA20_SPDIF_CTRL_TX_EN, 15062306a36Sopenharmony_ci TEGRA20_SPDIF_CTRL_TX_EN); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, 15662306a36Sopenharmony_ci TEGRA20_SPDIF_CTRL_TX_EN, 0); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, 16062306a36Sopenharmony_ci struct snd_soc_dai *dai) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci switch (cmd) { 16562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 16662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 16762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 16862306a36Sopenharmony_ci tegra20_spdif_start_playback(spdif); 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 17162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 17262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 17362306a36Sopenharmony_ci tegra20_spdif_stop_playback(spdif); 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci default: 17662306a36Sopenharmony_ci return -EINVAL; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int tegra20_spdif_filter_rates(struct snd_pcm_hw_params *params, 18362306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct snd_interval *r = hw_param_interval(params, rule->var); 18662306a36Sopenharmony_ci struct snd_soc_dai *dai = rule->private; 18762306a36Sopenharmony_ci struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); 18862306a36Sopenharmony_ci struct clk *parent = clk_get_parent(spdif->clk_spdif_out); 18962306a36Sopenharmony_ci static const unsigned int rates[] = { 32000, 44100, 48000 }; 19062306a36Sopenharmony_ci unsigned long i, parent_rate, valid_rates = 0; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci parent_rate = clk_get_rate(parent); 19362306a36Sopenharmony_ci if (!parent_rate) { 19462306a36Sopenharmony_ci dev_err(dai->dev, "Can't get parent clock rate\n"); 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rates); i++) { 19962306a36Sopenharmony_ci if (parent_rate % (rates[i] * 128) == 0) 20062306a36Sopenharmony_ci valid_rates |= BIT(i); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * At least one rate must be valid, otherwise the parent clock isn't 20562306a36Sopenharmony_ci * audio PLL. Nothing should be filtered in this case. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci if (!valid_rates) 20862306a36Sopenharmony_ci valid_rates = BIT(ARRAY_SIZE(rates)) - 1; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return snd_interval_list(r, ARRAY_SIZE(rates), rates, valid_rates); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int tegra20_spdif_startup(struct snd_pcm_substream *substream, 21462306a36Sopenharmony_ci struct snd_soc_dai *dai) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci if (!device_property_read_bool(dai->dev, "nvidia,fixed-parent-rate")) 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* 22062306a36Sopenharmony_ci * SPDIF and I2S share audio PLL. HDMI takes audio packets from SPDIF 22162306a36Sopenharmony_ci * and audio may not work on some TVs if clock rate isn't precise. 22262306a36Sopenharmony_ci * 22362306a36Sopenharmony_ci * PLL rate is controlled by I2S side. Filter out audio rates that 22462306a36Sopenharmony_ci * don't match PLL rate at the start of stream to allow both SPDIF 22562306a36Sopenharmony_ci * and I2S work simultaneously, assuming that PLL rate won't be 22662306a36Sopenharmony_ci * changed later on. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci return snd_pcm_hw_rule_add(substream->runtime, 0, 22962306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 23062306a36Sopenharmony_ci tegra20_spdif_filter_rates, dai, 23162306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int tegra20_spdif_probe(struct snd_soc_dai *dai) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &spdif->playback_dma_data, NULL); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { 24462306a36Sopenharmony_ci .probe = tegra20_spdif_probe, 24562306a36Sopenharmony_ci .hw_params = tegra20_spdif_hw_params, 24662306a36Sopenharmony_ci .trigger = tegra20_spdif_trigger, 24762306a36Sopenharmony_ci .startup = tegra20_spdif_startup, 24862306a36Sopenharmony_ci}; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic struct snd_soc_dai_driver tegra20_spdif_dai = { 25162306a36Sopenharmony_ci .name = "tegra20-spdif", 25262306a36Sopenharmony_ci .playback = { 25362306a36Sopenharmony_ci .stream_name = "Playback", 25462306a36Sopenharmony_ci .channels_min = 2, 25562306a36Sopenharmony_ci .channels_max = 2, 25662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | 25762306a36Sopenharmony_ci SNDRV_PCM_RATE_48000, 25862306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 25962306a36Sopenharmony_ci }, 26062306a36Sopenharmony_ci .ops = &tegra20_spdif_dai_ops, 26162306a36Sopenharmony_ci}; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic const struct snd_soc_component_driver tegra20_spdif_component = { 26462306a36Sopenharmony_ci .name = "tegra20-spdif", 26562306a36Sopenharmony_ci .legacy_dai_naming = 1, 26662306a36Sopenharmony_ci}; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci switch (reg) { 27162306a36Sopenharmony_ci case TEGRA20_SPDIF_CTRL: 27262306a36Sopenharmony_ci case TEGRA20_SPDIF_STATUS: 27362306a36Sopenharmony_ci case TEGRA20_SPDIF_STROBE_CTRL: 27462306a36Sopenharmony_ci case TEGRA20_SPDIF_DATA_FIFO_CSR: 27562306a36Sopenharmony_ci case TEGRA20_SPDIF_DATA_OUT: 27662306a36Sopenharmony_ci case TEGRA20_SPDIF_DATA_IN: 27762306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_A: 27862306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_B: 27962306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_C: 28062306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_D: 28162306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_E: 28262306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_F: 28362306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_A: 28462306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_B: 28562306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_C: 28662306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_D: 28762306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_E: 28862306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_F: 28962306a36Sopenharmony_ci case TEGRA20_SPDIF_USR_STA_RX_A: 29062306a36Sopenharmony_ci case TEGRA20_SPDIF_USR_DAT_TX_A: 29162306a36Sopenharmony_ci return true; 29262306a36Sopenharmony_ci default: 29362306a36Sopenharmony_ci return false; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci switch (reg) { 30062306a36Sopenharmony_ci case TEGRA20_SPDIF_STATUS: 30162306a36Sopenharmony_ci case TEGRA20_SPDIF_DATA_FIFO_CSR: 30262306a36Sopenharmony_ci case TEGRA20_SPDIF_DATA_OUT: 30362306a36Sopenharmony_ci case TEGRA20_SPDIF_DATA_IN: 30462306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_A: 30562306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_B: 30662306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_C: 30762306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_D: 30862306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_E: 30962306a36Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_F: 31062306a36Sopenharmony_ci case TEGRA20_SPDIF_USR_STA_RX_A: 31162306a36Sopenharmony_ci case TEGRA20_SPDIF_USR_DAT_TX_A: 31262306a36Sopenharmony_ci return true; 31362306a36Sopenharmony_ci default: 31462306a36Sopenharmony_ci return false; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci switch (reg) { 32162306a36Sopenharmony_ci case TEGRA20_SPDIF_DATA_OUT: 32262306a36Sopenharmony_ci case TEGRA20_SPDIF_DATA_IN: 32362306a36Sopenharmony_ci case TEGRA20_SPDIF_USR_STA_RX_A: 32462306a36Sopenharmony_ci case TEGRA20_SPDIF_USR_DAT_TX_A: 32562306a36Sopenharmony_ci return true; 32662306a36Sopenharmony_ci default: 32762306a36Sopenharmony_ci return false; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic const struct regmap_config tegra20_spdif_regmap_config = { 33262306a36Sopenharmony_ci .reg_bits = 32, 33362306a36Sopenharmony_ci .reg_stride = 4, 33462306a36Sopenharmony_ci .val_bits = 32, 33562306a36Sopenharmony_ci .max_register = TEGRA20_SPDIF_USR_DAT_TX_A, 33662306a36Sopenharmony_ci .writeable_reg = tegra20_spdif_wr_rd_reg, 33762306a36Sopenharmony_ci .readable_reg = tegra20_spdif_wr_rd_reg, 33862306a36Sopenharmony_ci .volatile_reg = tegra20_spdif_volatile_reg, 33962306a36Sopenharmony_ci .precious_reg = tegra20_spdif_precious_reg, 34062306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 34162306a36Sopenharmony_ci}; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int tegra20_spdif_platform_probe(struct platform_device *pdev) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct tegra20_spdif *spdif; 34662306a36Sopenharmony_ci struct resource *mem; 34762306a36Sopenharmony_ci void __iomem *regs; 34862306a36Sopenharmony_ci int ret; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif), 35162306a36Sopenharmony_ci GFP_KERNEL); 35262306a36Sopenharmony_ci if (!spdif) 35362306a36Sopenharmony_ci return -ENOMEM; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, spdif); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci spdif->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); 35862306a36Sopenharmony_ci if (IS_ERR(spdif->reset)) { 35962306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't retrieve spdif reset\n"); 36062306a36Sopenharmony_ci return PTR_ERR(spdif->reset); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out"); 36462306a36Sopenharmony_ci if (IS_ERR(spdif->clk_spdif_out)) { 36562306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not retrieve spdif clock\n"); 36662306a36Sopenharmony_ci return PTR_ERR(spdif->clk_spdif_out); 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); 37062306a36Sopenharmony_ci if (IS_ERR(regs)) 37162306a36Sopenharmony_ci return PTR_ERR(regs); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, 37462306a36Sopenharmony_ci &tegra20_spdif_regmap_config); 37562306a36Sopenharmony_ci if (IS_ERR(spdif->regmap)) { 37662306a36Sopenharmony_ci dev_err(&pdev->dev, "regmap init failed\n"); 37762306a36Sopenharmony_ci return PTR_ERR(spdif->regmap); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; 38162306a36Sopenharmony_ci spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 38262306a36Sopenharmony_ci spdif->playback_dma_data.maxburst = 4; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ret = devm_pm_runtime_enable(&pdev->dev); 38562306a36Sopenharmony_ci if (ret) 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 38962306a36Sopenharmony_ci &tegra20_spdif_component, 39062306a36Sopenharmony_ci &tegra20_spdif_dai, 1); 39162306a36Sopenharmony_ci if (ret) { 39262306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); 39362306a36Sopenharmony_ci return ret; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = devm_tegra_pcm_platform_register(&pdev->dev); 39762306a36Sopenharmony_ci if (ret) { 39862306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); 39962306a36Sopenharmony_ci return ret; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic const struct dev_pm_ops tegra20_spdif_pm_ops = { 40662306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, 40762306a36Sopenharmony_ci tegra20_spdif_runtime_resume, NULL) 40862306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 40962306a36Sopenharmony_ci pm_runtime_force_resume) 41062306a36Sopenharmony_ci}; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic const struct of_device_id tegra20_spdif_of_match[] = { 41362306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-spdif", }, 41462306a36Sopenharmony_ci {}, 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra20_spdif_of_match); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic struct platform_driver tegra20_spdif_driver = { 41962306a36Sopenharmony_ci .driver = { 42062306a36Sopenharmony_ci .name = "tegra20-spdif", 42162306a36Sopenharmony_ci .pm = &tegra20_spdif_pm_ops, 42262306a36Sopenharmony_ci .of_match_table = tegra20_spdif_of_match, 42362306a36Sopenharmony_ci }, 42462306a36Sopenharmony_ci .probe = tegra20_spdif_platform_probe, 42562306a36Sopenharmony_ci}; 42662306a36Sopenharmony_cimodule_platform_driver(tegra20_spdif_driver); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciMODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 42962306a36Sopenharmony_ciMODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver"); 43062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 431