18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tegra20_spdif.c - Tegra20 SPDIF driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Stephen Warren <swarren@nvidia.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2011-2012 - NVIDIA, Inc. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <sound/core.h> 188c2ecf20Sopenharmony_ci#include <sound/pcm.h> 198c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 208c2ecf20Sopenharmony_ci#include <sound/soc.h> 218c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "tegra20_spdif.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRV_NAME "tegra20-spdif" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int tegra20_spdif_runtime_suspend(struct device *dev) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct tegra20_spdif *spdif = dev_get_drvdata(dev); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci clk_disable_unprepare(spdif->clk_spdif_out); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci return 0; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int tegra20_spdif_runtime_resume(struct device *dev) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct tegra20_spdif *spdif = dev_get_drvdata(dev); 398c2ecf20Sopenharmony_ci int ret; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci ret = clk_prepare_enable(spdif->clk_spdif_out); 428c2ecf20Sopenharmony_ci if (ret) { 438c2ecf20Sopenharmony_ci dev_err(dev, "clk_enable failed: %d\n", ret); 448c2ecf20Sopenharmony_ci return ret; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, 518c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 528c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct device *dev = dai->dev; 558c2ecf20Sopenharmony_ci struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); 568c2ecf20Sopenharmony_ci unsigned int mask = 0, val = 0; 578c2ecf20Sopenharmony_ci int ret, spdifclock; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci mask |= TEGRA20_SPDIF_CTRL_PACK | 608c2ecf20Sopenharmony_ci TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; 618c2ecf20Sopenharmony_ci switch (params_format(params)) { 628c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 638c2ecf20Sopenharmony_ci val |= TEGRA20_SPDIF_CTRL_PACK | 648c2ecf20Sopenharmony_ci TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT; 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci default: 678c2ecf20Sopenharmony_ci return -EINVAL; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci switch (params_rate(params)) { 738c2ecf20Sopenharmony_ci case 32000: 748c2ecf20Sopenharmony_ci spdifclock = 4096000; 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci case 44100: 778c2ecf20Sopenharmony_ci spdifclock = 5644800; 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci case 48000: 808c2ecf20Sopenharmony_ci spdifclock = 6144000; 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci case 88200: 838c2ecf20Sopenharmony_ci spdifclock = 11289600; 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci case 96000: 868c2ecf20Sopenharmony_ci spdifclock = 12288000; 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci case 176400: 898c2ecf20Sopenharmony_ci spdifclock = 22579200; 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci case 192000: 928c2ecf20Sopenharmony_ci spdifclock = 24576000; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci default: 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); 998c2ecf20Sopenharmony_ci if (ret) { 1008c2ecf20Sopenharmony_ci dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); 1018c2ecf20Sopenharmony_ci return ret; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void tegra20_spdif_start_playback(struct tegra20_spdif *spdif) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, 1108c2ecf20Sopenharmony_ci TEGRA20_SPDIF_CTRL_TX_EN, 1118c2ecf20Sopenharmony_ci TEGRA20_SPDIF_CTRL_TX_EN); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, 1178c2ecf20Sopenharmony_ci TEGRA20_SPDIF_CTRL_TX_EN, 0); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, 1218c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci switch (cmd) { 1268c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 1278c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1288c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 1298c2ecf20Sopenharmony_ci tegra20_spdif_start_playback(spdif); 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 1328c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 1338c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 1348c2ecf20Sopenharmony_ci tegra20_spdif_stop_playback(spdif); 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci default: 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int tegra20_spdif_probe(struct snd_soc_dai *dai) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci dai->capture_dma_data = NULL; 1488c2ecf20Sopenharmony_ci dai->playback_dma_data = &spdif->playback_dma_data; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { 1548c2ecf20Sopenharmony_ci .hw_params = tegra20_spdif_hw_params, 1558c2ecf20Sopenharmony_ci .trigger = tegra20_spdif_trigger, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver tegra20_spdif_dai = { 1598c2ecf20Sopenharmony_ci .name = DRV_NAME, 1608c2ecf20Sopenharmony_ci .probe = tegra20_spdif_probe, 1618c2ecf20Sopenharmony_ci .playback = { 1628c2ecf20Sopenharmony_ci .stream_name = "Playback", 1638c2ecf20Sopenharmony_ci .channels_min = 2, 1648c2ecf20Sopenharmony_ci .channels_max = 2, 1658c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | 1668c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000, 1678c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 1688c2ecf20Sopenharmony_ci }, 1698c2ecf20Sopenharmony_ci .ops = &tegra20_spdif_dai_ops, 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver tegra20_spdif_component = { 1738c2ecf20Sopenharmony_ci .name = DRV_NAME, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci switch (reg) { 1798c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CTRL: 1808c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_STATUS: 1818c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_STROBE_CTRL: 1828c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_DATA_FIFO_CSR: 1838c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_DATA_OUT: 1848c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_DATA_IN: 1858c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_A: 1868c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_B: 1878c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_C: 1888c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_D: 1898c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_E: 1908c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_F: 1918c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_A: 1928c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_B: 1938c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_C: 1948c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_D: 1958c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_E: 1968c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_TX_F: 1978c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_USR_STA_RX_A: 1988c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_USR_DAT_TX_A: 1998c2ecf20Sopenharmony_ci return true; 2008c2ecf20Sopenharmony_ci default: 2018c2ecf20Sopenharmony_ci return false; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci switch (reg) { 2088c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_STATUS: 2098c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_DATA_FIFO_CSR: 2108c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_DATA_OUT: 2118c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_DATA_IN: 2128c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_A: 2138c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_B: 2148c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_C: 2158c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_D: 2168c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_E: 2178c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_CH_STA_RX_F: 2188c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_USR_STA_RX_A: 2198c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_USR_DAT_TX_A: 2208c2ecf20Sopenharmony_ci return true; 2218c2ecf20Sopenharmony_ci default: 2228c2ecf20Sopenharmony_ci return false; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci switch (reg) { 2298c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_DATA_OUT: 2308c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_DATA_IN: 2318c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_USR_STA_RX_A: 2328c2ecf20Sopenharmony_ci case TEGRA20_SPDIF_USR_DAT_TX_A: 2338c2ecf20Sopenharmony_ci return true; 2348c2ecf20Sopenharmony_ci default: 2358c2ecf20Sopenharmony_ci return false; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct regmap_config tegra20_spdif_regmap_config = { 2408c2ecf20Sopenharmony_ci .reg_bits = 32, 2418c2ecf20Sopenharmony_ci .reg_stride = 4, 2428c2ecf20Sopenharmony_ci .val_bits = 32, 2438c2ecf20Sopenharmony_ci .max_register = TEGRA20_SPDIF_USR_DAT_TX_A, 2448c2ecf20Sopenharmony_ci .writeable_reg = tegra20_spdif_wr_rd_reg, 2458c2ecf20Sopenharmony_ci .readable_reg = tegra20_spdif_wr_rd_reg, 2468c2ecf20Sopenharmony_ci .volatile_reg = tegra20_spdif_volatile_reg, 2478c2ecf20Sopenharmony_ci .precious_reg = tegra20_spdif_precious_reg, 2488c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int tegra20_spdif_platform_probe(struct platform_device *pdev) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct tegra20_spdif *spdif; 2548c2ecf20Sopenharmony_ci struct resource *mem, *dmareq; 2558c2ecf20Sopenharmony_ci void __iomem *regs; 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif), 2598c2ecf20Sopenharmony_ci GFP_KERNEL); 2608c2ecf20Sopenharmony_ci if (!spdif) 2618c2ecf20Sopenharmony_ci return -ENOMEM; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, spdif); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out"); 2668c2ecf20Sopenharmony_ci if (IS_ERR(spdif->clk_spdif_out)) { 2678c2ecf20Sopenharmony_ci pr_err("Can't retrieve spdif clock\n"); 2688c2ecf20Sopenharmony_ci ret = PTR_ERR(spdif->clk_spdif_out); 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2738c2ecf20Sopenharmony_ci regs = devm_ioremap_resource(&pdev->dev, mem); 2748c2ecf20Sopenharmony_ci if (IS_ERR(regs)) 2758c2ecf20Sopenharmony_ci return PTR_ERR(regs); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); 2788c2ecf20Sopenharmony_ci if (!dmareq) { 2798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No DMA resource\n"); 2808c2ecf20Sopenharmony_ci return -ENODEV; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, 2848c2ecf20Sopenharmony_ci &tegra20_spdif_regmap_config); 2858c2ecf20Sopenharmony_ci if (IS_ERR(spdif->regmap)) { 2868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "regmap init failed\n"); 2878c2ecf20Sopenharmony_ci ret = PTR_ERR(spdif->regmap); 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; 2928c2ecf20Sopenharmony_ci spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 2938c2ecf20Sopenharmony_ci spdif->playback_dma_data.maxburst = 4; 2948c2ecf20Sopenharmony_ci spdif->playback_dma_data.slave_id = dmareq->start; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 2978c2ecf20Sopenharmony_ci if (!pm_runtime_enabled(&pdev->dev)) { 2988c2ecf20Sopenharmony_ci ret = tegra20_spdif_runtime_resume(&pdev->dev); 2998c2ecf20Sopenharmony_ci if (ret) 3008c2ecf20Sopenharmony_ci goto err_pm_disable; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component, 3048c2ecf20Sopenharmony_ci &tegra20_spdif_dai, 1); 3058c2ecf20Sopenharmony_ci if (ret) { 3068c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); 3078c2ecf20Sopenharmony_ci ret = -ENOMEM; 3088c2ecf20Sopenharmony_ci goto err_suspend; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci ret = tegra_pcm_platform_register(&pdev->dev); 3128c2ecf20Sopenharmony_ci if (ret) { 3138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); 3148c2ecf20Sopenharmony_ci goto err_unregister_component; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cierr_unregister_component: 3208c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 3218c2ecf20Sopenharmony_cierr_suspend: 3228c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 3238c2ecf20Sopenharmony_ci tegra20_spdif_runtime_suspend(&pdev->dev); 3248c2ecf20Sopenharmony_cierr_pm_disable: 3258c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int tegra20_spdif_platform_remove(struct platform_device *pdev) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 3338c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 3348c2ecf20Sopenharmony_ci tegra20_spdif_runtime_suspend(&pdev->dev); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci tegra_pcm_platform_unregister(&pdev->dev); 3378c2ecf20Sopenharmony_ci snd_soc_unregister_component(&pdev->dev); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tegra20_spdif_pm_ops = { 3438c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, 3448c2ecf20Sopenharmony_ci tegra20_spdif_runtime_resume, NULL) 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic struct platform_driver tegra20_spdif_driver = { 3488c2ecf20Sopenharmony_ci .driver = { 3498c2ecf20Sopenharmony_ci .name = DRV_NAME, 3508c2ecf20Sopenharmony_ci .pm = &tegra20_spdif_pm_ops, 3518c2ecf20Sopenharmony_ci }, 3528c2ecf20Sopenharmony_ci .probe = tegra20_spdif_platform_probe, 3538c2ecf20Sopenharmony_ci .remove = tegra20_spdif_platform_remove, 3548c2ecf20Sopenharmony_ci}; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cimodule_platform_driver(tegra20_spdif_driver); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 3598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver"); 3608c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3618c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 362