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