18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA SoC SPDIF Audio Layer 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2015 Andrea Venturi <be17068@iperbole.bo.it> 68c2ecf20Sopenharmony_ci * Copyright 2015 Marcus Cooper <codekipper@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on the Allwinner SDK driver, released under the GPL. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <linux/of_device.h> 198c2ecf20Sopenharmony_ci#include <linux/ioport.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/reset.h> 248c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h> 258c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 268c2ecf20Sopenharmony_ci#include <sound/soc.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_CTL (0x00) 298c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */ 308c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2) 318c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_CTL_GEN BIT(1) 328c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_CTL_RESET BIT(0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_TXCFG (0x04) 358c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31) 368c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_ASS BIT(17) 378c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_NONAUDIO BIT(16) 388c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_TXRATIO(v) ((v) << 4) 398c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_TXRATIO_MASK GENMASK(8, 4) 408c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_FMTRVD GENMASK(3, 2) 418c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_FMT16BIT (0 << 2) 428c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_FMT20BIT (1 << 2) 438c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_FMT24BIT (2 << 2) 448c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_CHSTMODE BIT(1) 458c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_TXEN BIT(0) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_RXCFG (0x08) 488c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCFG_LOCKFLAG BIT(4) 498c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCFG_CHSTSRC BIT(3) 508c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCFG_CHSTCP BIT(1) 518c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCFG_RXEN BIT(0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_TXFIFO (0x0C) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_RXFIFO (0x10) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_FCTL (0x14) 588c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_FIFOSRC BIT(31) 598c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_FTX BIT(17) 608c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_FRX BIT(16) 618c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_TXTL(v) ((v) << 8) 628c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_TXTL_MASK GENMASK(12, 8) 638c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_RXTL(v) ((v) << 3) 648c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_RXTL_MASK GENMASK(7, 3) 658c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_TXIM BIT(2) 668c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_RXOM(v) ((v) << 0) 678c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define SUN50I_H6_SPDIF_FCTL (0x14) 708c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_HUB_EN BIT(31) 718c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_FTX BIT(30) 728c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_FRX BIT(29) 738c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_TXTL(v) ((v) << 12) 748c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_TXTL_MASK GENMASK(19, 12) 758c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_RXTL(v) ((v) << 4) 768c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_RXTL_MASK GENMASK(10, 4) 778c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_TXIM BIT(2) 788c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_RXOM(v) ((v) << 0) 798c2ecf20Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_FSTA (0x18) 828c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FSTA_TXE BIT(14) 838c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FSTA_TXECNTSHT (8) 848c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FSTA_RXA BIT(6) 858c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_FSTA_RXACNTSHT (0) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_INT (0x1C) 888c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_RXLOCKEN BIT(18) 898c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_RXUNLOCKEN BIT(17) 908c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_RXPARERREN BIT(16) 918c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_TXDRQEN BIT(7) 928c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_TXUIEN BIT(6) 938c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_TXOIEN BIT(5) 948c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_TXEIEN BIT(4) 958c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_RXDRQEN BIT(2) 968c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_RXOIEN BIT(1) 978c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_INT_RXAIEN BIT(0) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_ISTA (0x20) 1008c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXLOCKSTA BIT(18) 1018c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXUNLOCKSTA BIT(17) 1028c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXPARERRSTA BIT(16) 1038c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_ISTA_TXUSTA BIT(6) 1048c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_ISTA_TXOSTA BIT(5) 1058c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_ISTA_TXESTA BIT(4) 1068c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXOSTA BIT(1) 1078c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXASTA BIT(0) 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define SUN8I_SPDIF_TXFIFO (0x20) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_TXCNT (0x24) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_RXCNT (0x28) 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_TXCHSTA0 (0x2C) 1168c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CLK(v) ((v) << 28) 1178c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ(v) ((v) << 24) 1188c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ_MASK GENMASK(27, 24) 1198c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CHNUM(v) ((v) << 20) 1208c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CHNUM_MASK GENMASK(23, 20) 1218c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_SRCNUM(v) ((v) << 16) 1228c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CATACOD(v) ((v) << 8) 1238c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_MODE(v) ((v) << 6) 1248c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_EMPHASIS(v) ((v) << 3) 1258c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CP BIT(2) 1268c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_AUDIO BIT(1) 1278c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_PRO BIT(0) 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_TXCHSTA1 (0x30) 1308c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_CGMSA(v) ((v) << 8) 1318c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ(v) ((v) << 4) 1328c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ_MASK GENMASK(7, 4) 1338c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_SAMWORDLEN(v) ((v) << 1) 1348c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_MAXWORDLEN BIT(0) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_RXCHSTA0 (0x34) 1378c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_CLK(v) ((v) << 28) 1388c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_SAMFREQ(v) ((v) << 24) 1398c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_CHNUM(v) ((v) << 20) 1408c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_SRCNUM(v) ((v) << 16) 1418c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_CATACOD(v) ((v) << 8) 1428c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_MODE(v) ((v) << 6) 1438c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_EMPHASIS(v) ((v) << 3) 1448c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_CP BIT(2) 1458c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_AUDIO BIT(1) 1468c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_PRO BIT(0) 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_RXCHSTA1 (0x38) 1498c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA1_CGMSA(v) ((v) << 8) 1508c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA1_ORISAMFREQ(v) ((v) << 4) 1518c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA1_SAMWORDLEN(v) ((v) << 1) 1528c2ecf20Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA1_MAXWORDLEN BIT(0) 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* Defines for Sampling Frequency */ 1558c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_44_1KHZ 0x0 1568c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_NOT_INDICATED 0x1 1578c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_48KHZ 0x2 1588c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_32KHZ 0x3 1598c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_22_05KHZ 0x4 1608c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_24KHZ 0x6 1618c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_88_2KHZ 0x8 1628c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_76_8KHZ 0x9 1638c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_96KHZ 0xa 1648c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_176_4KHZ 0xc 1658c2ecf20Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_192KHZ 0xe 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/** 1688c2ecf20Sopenharmony_ci * struct sun4i_spdif_quirks - Differences between SoC variants. 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * @reg_dac_txdata: TX FIFO offset for DMA config. 1718c2ecf20Sopenharmony_ci * @has_reset: SoC needs reset deasserted. 1728c2ecf20Sopenharmony_ci * @val_fctl_ftx: TX FIFO flush bitmask. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_cistruct sun4i_spdif_quirks { 1758c2ecf20Sopenharmony_ci unsigned int reg_dac_txdata; 1768c2ecf20Sopenharmony_ci bool has_reset; 1778c2ecf20Sopenharmony_ci unsigned int val_fctl_ftx; 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistruct sun4i_spdif_dev { 1818c2ecf20Sopenharmony_ci struct platform_device *pdev; 1828c2ecf20Sopenharmony_ci struct clk *spdif_clk; 1838c2ecf20Sopenharmony_ci struct clk *apb_clk; 1848c2ecf20Sopenharmony_ci struct reset_control *rst; 1858c2ecf20Sopenharmony_ci struct snd_soc_dai_driver cpu_dai_drv; 1868c2ecf20Sopenharmony_ci struct regmap *regmap; 1878c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_params_tx; 1888c2ecf20Sopenharmony_ci const struct sun4i_spdif_quirks *quirks; 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void sun4i_spdif_configure(struct sun4i_spdif_dev *host) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci const struct sun4i_spdif_quirks *quirks = host->quirks; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* soft reset SPDIF */ 1968c2ecf20Sopenharmony_ci regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* flush TX FIFO */ 1998c2ecf20Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, 2008c2ecf20Sopenharmony_ci quirks->val_fctl_ftx, quirks->val_fctl_ftx); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* clear TX counter */ 2038c2ecf20Sopenharmony_ci regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void sun4i_snd_txctrl_on(struct snd_pcm_substream *substream, 2078c2ecf20Sopenharmony_ci struct sun4i_spdif_dev *host) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci if (substream->runtime->channels == 1) 2108c2ecf20Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, 2118c2ecf20Sopenharmony_ci SUN4I_SPDIF_TXCFG_SINGLEMOD, 2128c2ecf20Sopenharmony_ci SUN4I_SPDIF_TXCFG_SINGLEMOD); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* SPDIF TX ENABLE */ 2158c2ecf20Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, 2168c2ecf20Sopenharmony_ci SUN4I_SPDIF_TXCFG_TXEN, SUN4I_SPDIF_TXCFG_TXEN); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* DRQ ENABLE */ 2198c2ecf20Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_INT, 2208c2ecf20Sopenharmony_ci SUN4I_SPDIF_INT_TXDRQEN, SUN4I_SPDIF_INT_TXDRQEN); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* Global enable */ 2238c2ecf20Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL, 2248c2ecf20Sopenharmony_ci SUN4I_SPDIF_CTL_GEN, SUN4I_SPDIF_CTL_GEN); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream, 2288c2ecf20Sopenharmony_ci struct sun4i_spdif_dev *host) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci /* SPDIF TX DISABLE */ 2318c2ecf20Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, 2328c2ecf20Sopenharmony_ci SUN4I_SPDIF_TXCFG_TXEN, 0); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* DRQ DISABLE */ 2358c2ecf20Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_INT, 2368c2ecf20Sopenharmony_ci SUN4I_SPDIF_INT_TXDRQEN, 0); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Global disable */ 2398c2ecf20Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL, 2408c2ecf20Sopenharmony_ci SUN4I_SPDIF_CTL_GEN, 0); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int sun4i_spdif_startup(struct snd_pcm_substream *substream, 2448c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 2478c2ecf20Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) 2508c2ecf20Sopenharmony_ci return -EINVAL; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci sun4i_spdif_configure(host); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, 2588c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 2598c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci int ret = 0; 2628c2ecf20Sopenharmony_ci int fmt; 2638c2ecf20Sopenharmony_ci unsigned long rate = params_rate(params); 2648c2ecf20Sopenharmony_ci u32 mclk_div = 0; 2658c2ecf20Sopenharmony_ci unsigned int mclk = 0; 2668c2ecf20Sopenharmony_ci u32 reg_val; 2678c2ecf20Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); 2688c2ecf20Sopenharmony_ci struct platform_device *pdev = host->pdev; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* Add the PCM and raw data select interface */ 2718c2ecf20Sopenharmony_ci switch (params_channels(params)) { 2728c2ecf20Sopenharmony_ci case 1: /* PCM mode */ 2738c2ecf20Sopenharmony_ci case 2: 2748c2ecf20Sopenharmony_ci fmt = 0; 2758c2ecf20Sopenharmony_ci break; 2768c2ecf20Sopenharmony_ci case 4: /* raw data mode */ 2778c2ecf20Sopenharmony_ci fmt = SUN4I_SPDIF_TXCFG_NONAUDIO; 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci default: 2808c2ecf20Sopenharmony_ci return -EINVAL; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci switch (params_format(params)) { 2848c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 2858c2ecf20Sopenharmony_ci fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT; 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3LE: 2888c2ecf20Sopenharmony_ci fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT; 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 2918c2ecf20Sopenharmony_ci fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT; 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci default: 2948c2ecf20Sopenharmony_ci return -EINVAL; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci switch (rate) { 2988c2ecf20Sopenharmony_ci case 22050: 2998c2ecf20Sopenharmony_ci case 44100: 3008c2ecf20Sopenharmony_ci case 88200: 3018c2ecf20Sopenharmony_ci case 176400: 3028c2ecf20Sopenharmony_ci mclk = 22579200; 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case 24000: 3058c2ecf20Sopenharmony_ci case 32000: 3068c2ecf20Sopenharmony_ci case 48000: 3078c2ecf20Sopenharmony_ci case 96000: 3088c2ecf20Sopenharmony_ci case 192000: 3098c2ecf20Sopenharmony_ci mclk = 24576000; 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci default: 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ret = clk_set_rate(host->spdif_clk, mclk); 3168c2ecf20Sopenharmony_ci if (ret < 0) { 3178c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3188c2ecf20Sopenharmony_ci "Setting SPDIF clock rate for %d Hz failed!\n", mclk); 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, 3238c2ecf20Sopenharmony_ci SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci switch (rate) { 3268c2ecf20Sopenharmony_ci case 22050: 3278c2ecf20Sopenharmony_ci case 24000: 3288c2ecf20Sopenharmony_ci mclk_div = 8; 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci case 32000: 3318c2ecf20Sopenharmony_ci mclk_div = 6; 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case 44100: 3348c2ecf20Sopenharmony_ci case 48000: 3358c2ecf20Sopenharmony_ci mclk_div = 4; 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case 88200: 3388c2ecf20Sopenharmony_ci case 96000: 3398c2ecf20Sopenharmony_ci mclk_div = 2; 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci case 176400: 3428c2ecf20Sopenharmony_ci case 192000: 3438c2ecf20Sopenharmony_ci mclk_div = 1; 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci default: 3468c2ecf20Sopenharmony_ci return -EINVAL; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci reg_val = 0; 3508c2ecf20Sopenharmony_ci reg_val |= SUN4I_SPDIF_TXCFG_ASS; 3518c2ecf20Sopenharmony_ci reg_val |= fmt; /* set non audio and bit depth */ 3528c2ecf20Sopenharmony_ci reg_val |= SUN4I_SPDIF_TXCFG_CHSTMODE; 3538c2ecf20Sopenharmony_ci reg_val |= SUN4I_SPDIF_TXCFG_TXRATIO(mclk_div - 1); 3548c2ecf20Sopenharmony_ci regmap_write(host->regmap, SUN4I_SPDIF_TXCFG, reg_val); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd, 3608c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci int ret = 0; 3638c2ecf20Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) 3668c2ecf20Sopenharmony_ci return -EINVAL; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci switch (cmd) { 3698c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3708c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 3718c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3728c2ecf20Sopenharmony_ci sun4i_snd_txctrl_on(substream, host); 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3768c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 3778c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3788c2ecf20Sopenharmony_ci sun4i_snd_txctrl_off(substream, host); 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci default: 3828c2ecf20Sopenharmony_ci ret = -EINVAL; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci return ret; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL); 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops sun4i_spdif_dai_ops = { 3978c2ecf20Sopenharmony_ci .startup = sun4i_spdif_startup, 3988c2ecf20Sopenharmony_ci .trigger = sun4i_spdif_trigger, 3998c2ecf20Sopenharmony_ci .hw_params = sun4i_spdif_hw_params, 4008c2ecf20Sopenharmony_ci}; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic const struct regmap_config sun4i_spdif_regmap_config = { 4038c2ecf20Sopenharmony_ci .reg_bits = 32, 4048c2ecf20Sopenharmony_ci .reg_stride = 4, 4058c2ecf20Sopenharmony_ci .val_bits = 32, 4068c2ecf20Sopenharmony_ci .max_register = SUN4I_SPDIF_RXCHSTA1, 4078c2ecf20Sopenharmony_ci}; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci#define SUN4I_RATES SNDRV_PCM_RATE_8000_192000 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci#define SUN4I_FORMATS (SNDRV_PCM_FORMAT_S16_LE | \ 4128c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_S20_3LE | \ 4138c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_S24_LE) 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver sun4i_spdif_dai = { 4168c2ecf20Sopenharmony_ci .playback = { 4178c2ecf20Sopenharmony_ci .channels_min = 1, 4188c2ecf20Sopenharmony_ci .channels_max = 2, 4198c2ecf20Sopenharmony_ci .rates = SUN4I_RATES, 4208c2ecf20Sopenharmony_ci .formats = SUN4I_FORMATS, 4218c2ecf20Sopenharmony_ci }, 4228c2ecf20Sopenharmony_ci .probe = sun4i_spdif_soc_dai_probe, 4238c2ecf20Sopenharmony_ci .ops = &sun4i_spdif_dai_ops, 4248c2ecf20Sopenharmony_ci .name = "spdif", 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { 4288c2ecf20Sopenharmony_ci .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, 4298c2ecf20Sopenharmony_ci .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, 4308c2ecf20Sopenharmony_ci}; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { 4338c2ecf20Sopenharmony_ci .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, 4348c2ecf20Sopenharmony_ci .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, 4358c2ecf20Sopenharmony_ci .has_reset = true, 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { 4398c2ecf20Sopenharmony_ci .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, 4408c2ecf20Sopenharmony_ci .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, 4418c2ecf20Sopenharmony_ci .has_reset = true, 4428c2ecf20Sopenharmony_ci}; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { 4458c2ecf20Sopenharmony_ci .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, 4468c2ecf20Sopenharmony_ci .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, 4478c2ecf20Sopenharmony_ci .has_reset = true, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic const struct of_device_id sun4i_spdif_of_match[] = { 4518c2ecf20Sopenharmony_ci { 4528c2ecf20Sopenharmony_ci .compatible = "allwinner,sun4i-a10-spdif", 4538c2ecf20Sopenharmony_ci .data = &sun4i_a10_spdif_quirks, 4548c2ecf20Sopenharmony_ci }, 4558c2ecf20Sopenharmony_ci { 4568c2ecf20Sopenharmony_ci .compatible = "allwinner,sun6i-a31-spdif", 4578c2ecf20Sopenharmony_ci .data = &sun6i_a31_spdif_quirks, 4588c2ecf20Sopenharmony_ci }, 4598c2ecf20Sopenharmony_ci { 4608c2ecf20Sopenharmony_ci .compatible = "allwinner,sun8i-h3-spdif", 4618c2ecf20Sopenharmony_ci .data = &sun8i_h3_spdif_quirks, 4628c2ecf20Sopenharmony_ci }, 4638c2ecf20Sopenharmony_ci { 4648c2ecf20Sopenharmony_ci .compatible = "allwinner,sun50i-h6-spdif", 4658c2ecf20Sopenharmony_ci .data = &sun50i_h6_spdif_quirks, 4668c2ecf20Sopenharmony_ci }, 4678c2ecf20Sopenharmony_ci { /* sentinel */ } 4688c2ecf20Sopenharmony_ci}; 4698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4i_spdif_of_match); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver sun4i_spdif_component = { 4728c2ecf20Sopenharmony_ci .name = "sun4i-spdif", 4738c2ecf20Sopenharmony_ci}; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int sun4i_spdif_runtime_suspend(struct device *dev) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct sun4i_spdif_dev *host = dev_get_drvdata(dev); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci clk_disable_unprepare(host->spdif_clk); 4808c2ecf20Sopenharmony_ci clk_disable_unprepare(host->apb_clk); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int sun4i_spdif_runtime_resume(struct device *dev) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct sun4i_spdif_dev *host = dev_get_drvdata(dev); 4888c2ecf20Sopenharmony_ci int ret; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci ret = clk_prepare_enable(host->spdif_clk); 4918c2ecf20Sopenharmony_ci if (ret) 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci ret = clk_prepare_enable(host->apb_clk); 4948c2ecf20Sopenharmony_ci if (ret) 4958c2ecf20Sopenharmony_ci clk_disable_unprepare(host->spdif_clk); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return ret; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int sun4i_spdif_probe(struct platform_device *pdev) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct sun4i_spdif_dev *host; 5038c2ecf20Sopenharmony_ci struct resource *res; 5048c2ecf20Sopenharmony_ci const struct sun4i_spdif_quirks *quirks; 5058c2ecf20Sopenharmony_ci int ret; 5068c2ecf20Sopenharmony_ci void __iomem *base; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Entered %s\n", __func__); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); 5118c2ecf20Sopenharmony_ci if (!host) 5128c2ecf20Sopenharmony_ci return -ENOMEM; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci host->pdev = pdev; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* Initialize this copy of the CPU DAI driver structure */ 5178c2ecf20Sopenharmony_ci memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai)); 5188c2ecf20Sopenharmony_ci host->cpu_dai_drv.name = dev_name(&pdev->dev); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* Get the addresses */ 5218c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5228c2ecf20Sopenharmony_ci base = devm_ioremap_resource(&pdev->dev, res); 5238c2ecf20Sopenharmony_ci if (IS_ERR(base)) 5248c2ecf20Sopenharmony_ci return PTR_ERR(base); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci quirks = of_device_get_match_data(&pdev->dev); 5278c2ecf20Sopenharmony_ci if (quirks == NULL) { 5288c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); 5298c2ecf20Sopenharmony_ci return -ENODEV; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci host->quirks = quirks; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci host->regmap = devm_regmap_init_mmio(&pdev->dev, base, 5348c2ecf20Sopenharmony_ci &sun4i_spdif_regmap_config); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Clocks */ 5378c2ecf20Sopenharmony_ci host->apb_clk = devm_clk_get(&pdev->dev, "apb"); 5388c2ecf20Sopenharmony_ci if (IS_ERR(host->apb_clk)) { 5398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get a apb clock.\n"); 5408c2ecf20Sopenharmony_ci return PTR_ERR(host->apb_clk); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci host->spdif_clk = devm_clk_get(&pdev->dev, "spdif"); 5448c2ecf20Sopenharmony_ci if (IS_ERR(host->spdif_clk)) { 5458c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get a spdif clock.\n"); 5468c2ecf20Sopenharmony_ci return PTR_ERR(host->spdif_clk); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; 5508c2ecf20Sopenharmony_ci host->dma_params_tx.maxburst = 8; 5518c2ecf20Sopenharmony_ci host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, host); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (quirks->has_reset) { 5568c2ecf20Sopenharmony_ci host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, 5578c2ecf20Sopenharmony_ci NULL); 5588c2ecf20Sopenharmony_ci if (PTR_ERR(host->rst) == -EPROBE_DEFER) { 5598c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 5608c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); 5618c2ecf20Sopenharmony_ci return ret; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci if (!IS_ERR(host->rst)) 5648c2ecf20Sopenharmony_ci reset_control_deassert(host->rst); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 5688c2ecf20Sopenharmony_ci &sun4i_spdif_component, &sun4i_spdif_dai, 1); 5698c2ecf20Sopenharmony_ci if (ret) 5708c2ecf20Sopenharmony_ci return ret; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 5738c2ecf20Sopenharmony_ci if (!pm_runtime_enabled(&pdev->dev)) { 5748c2ecf20Sopenharmony_ci ret = sun4i_spdif_runtime_resume(&pdev->dev); 5758c2ecf20Sopenharmony_ci if (ret) 5768c2ecf20Sopenharmony_ci goto err_unregister; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 5808c2ecf20Sopenharmony_ci if (ret) 5818c2ecf20Sopenharmony_ci goto err_suspend; 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_cierr_suspend: 5848c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 5858c2ecf20Sopenharmony_ci sun4i_spdif_runtime_suspend(&pdev->dev); 5868c2ecf20Sopenharmony_cierr_unregister: 5878c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5888c2ecf20Sopenharmony_ci return ret; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic int sun4i_spdif_remove(struct platform_device *pdev) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5948c2ecf20Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 5958c2ecf20Sopenharmony_ci sun4i_spdif_runtime_suspend(&pdev->dev); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sun4i_spdif_pm = { 6018c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend, 6028c2ecf20Sopenharmony_ci sun4i_spdif_runtime_resume, NULL) 6038c2ecf20Sopenharmony_ci}; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic struct platform_driver sun4i_spdif_driver = { 6068c2ecf20Sopenharmony_ci .driver = { 6078c2ecf20Sopenharmony_ci .name = "sun4i-spdif", 6088c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(sun4i_spdif_of_match), 6098c2ecf20Sopenharmony_ci .pm = &sun4i_spdif_pm, 6108c2ecf20Sopenharmony_ci }, 6118c2ecf20Sopenharmony_ci .probe = sun4i_spdif_probe, 6128c2ecf20Sopenharmony_ci .remove = sun4i_spdif_remove, 6138c2ecf20Sopenharmony_ci}; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cimodule_platform_driver(sun4i_spdif_driver); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marcus Cooper <codekipper@gmail.com>"); 6188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>"); 6198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface"); 6208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6218c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:sun4i-spdif"); 622