162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ALSA SoC SPDIF Audio Layer 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2015 Andrea Venturi <be17068@iperbole.bo.it> 662306a36Sopenharmony_ci * Copyright 2015 Marcus Cooper <codekipper@gmail.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on the Allwinner SDK driver, released under the GPL. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <linux/of_address.h> 1862306a36Sopenharmony_ci#include <linux/of_device.h> 1962306a36Sopenharmony_ci#include <linux/ioport.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2362306a36Sopenharmony_ci#include <linux/reset.h> 2462306a36Sopenharmony_ci#include <linux/spinlock.h> 2562306a36Sopenharmony_ci#include <sound/asoundef.h> 2662306a36Sopenharmony_ci#include <sound/dmaengine_pcm.h> 2762306a36Sopenharmony_ci#include <sound/pcm_params.h> 2862306a36Sopenharmony_ci#include <sound/soc.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define SUN4I_SPDIF_CTL (0x00) 3162306a36Sopenharmony_ci #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */ 3262306a36Sopenharmony_ci #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2) 3362306a36Sopenharmony_ci #define SUN4I_SPDIF_CTL_GEN BIT(1) 3462306a36Sopenharmony_ci #define SUN4I_SPDIF_CTL_RESET BIT(0) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define SUN4I_SPDIF_TXCFG (0x04) 3762306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31) 3862306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_ASS BIT(17) 3962306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_NONAUDIO BIT(16) 4062306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_TXRATIO(v) ((v) << 4) 4162306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_TXRATIO_MASK GENMASK(8, 4) 4262306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_FMTRVD GENMASK(3, 2) 4362306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_FMT16BIT (0 << 2) 4462306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_FMT20BIT (1 << 2) 4562306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_FMT24BIT (2 << 2) 4662306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_CHSTMODE BIT(1) 4762306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCFG_TXEN BIT(0) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define SUN4I_SPDIF_RXCFG (0x08) 5062306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCFG_LOCKFLAG BIT(4) 5162306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCFG_CHSTSRC BIT(3) 5262306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCFG_CHSTCP BIT(1) 5362306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCFG_RXEN BIT(0) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define SUN4I_SPDIF_TXFIFO (0x0C) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define SUN4I_SPDIF_RXFIFO (0x10) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define SUN4I_SPDIF_FCTL (0x14) 6062306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_FIFOSRC BIT(31) 6162306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_FTX BIT(17) 6262306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_FRX BIT(16) 6362306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_TXTL(v) ((v) << 8) 6462306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_TXTL_MASK GENMASK(12, 8) 6562306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_RXTL(v) ((v) << 3) 6662306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_RXTL_MASK GENMASK(7, 3) 6762306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_TXIM BIT(2) 6862306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_RXOM(v) ((v) << 0) 6962306a36Sopenharmony_ci #define SUN4I_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define SUN50I_H6_SPDIF_FCTL (0x14) 7262306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_HUB_EN BIT(31) 7362306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_FTX BIT(30) 7462306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_FRX BIT(29) 7562306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_TXTL(v) ((v) << 12) 7662306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_TXTL_MASK GENMASK(19, 12) 7762306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_RXTL(v) ((v) << 4) 7862306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_RXTL_MASK GENMASK(10, 4) 7962306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_TXIM BIT(2) 8062306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_RXOM(v) ((v) << 0) 8162306a36Sopenharmony_ci #define SUN50I_H6_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define SUN4I_SPDIF_FSTA (0x18) 8462306a36Sopenharmony_ci #define SUN4I_SPDIF_FSTA_TXE BIT(14) 8562306a36Sopenharmony_ci #define SUN4I_SPDIF_FSTA_TXECNTSHT (8) 8662306a36Sopenharmony_ci #define SUN4I_SPDIF_FSTA_RXA BIT(6) 8762306a36Sopenharmony_ci #define SUN4I_SPDIF_FSTA_RXACNTSHT (0) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define SUN4I_SPDIF_INT (0x1C) 9062306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_RXLOCKEN BIT(18) 9162306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_RXUNLOCKEN BIT(17) 9262306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_RXPARERREN BIT(16) 9362306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_TXDRQEN BIT(7) 9462306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_TXUIEN BIT(6) 9562306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_TXOIEN BIT(5) 9662306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_TXEIEN BIT(4) 9762306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_RXDRQEN BIT(2) 9862306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_RXOIEN BIT(1) 9962306a36Sopenharmony_ci #define SUN4I_SPDIF_INT_RXAIEN BIT(0) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define SUN4I_SPDIF_ISTA (0x20) 10262306a36Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXLOCKSTA BIT(18) 10362306a36Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXUNLOCKSTA BIT(17) 10462306a36Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXPARERRSTA BIT(16) 10562306a36Sopenharmony_ci #define SUN4I_SPDIF_ISTA_TXUSTA BIT(6) 10662306a36Sopenharmony_ci #define SUN4I_SPDIF_ISTA_TXOSTA BIT(5) 10762306a36Sopenharmony_ci #define SUN4I_SPDIF_ISTA_TXESTA BIT(4) 10862306a36Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXOSTA BIT(1) 10962306a36Sopenharmony_ci #define SUN4I_SPDIF_ISTA_RXASTA BIT(0) 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define SUN8I_SPDIF_TXFIFO (0x20) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define SUN4I_SPDIF_TXCNT (0x24) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define SUN4I_SPDIF_RXCNT (0x28) 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define SUN4I_SPDIF_TXCHSTA0 (0x2C) 11862306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CLK(v) ((v) << 28) 11962306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ(v) ((v) << 24) 12062306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ_MASK GENMASK(27, 24) 12162306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CHNUM(v) ((v) << 20) 12262306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CHNUM_MASK GENMASK(23, 20) 12362306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_SRCNUM(v) ((v) << 16) 12462306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CATACOD(v) ((v) << 8) 12562306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_MODE(v) ((v) << 6) 12662306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_EMPHASIS(v) ((v) << 3) 12762306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_CP BIT(2) 12862306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_AUDIO BIT(1) 12962306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA0_PRO BIT(0) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define SUN4I_SPDIF_TXCHSTA1 (0x30) 13262306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_CGMSA(v) ((v) << 8) 13362306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ(v) ((v) << 4) 13462306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ_MASK GENMASK(7, 4) 13562306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_SAMWORDLEN(v) ((v) << 1) 13662306a36Sopenharmony_ci #define SUN4I_SPDIF_TXCHSTA1_MAXWORDLEN BIT(0) 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define SUN4I_SPDIF_RXCHSTA0 (0x34) 13962306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_CLK(v) ((v) << 28) 14062306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_SAMFREQ(v) ((v) << 24) 14162306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_CHNUM(v) ((v) << 20) 14262306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_SRCNUM(v) ((v) << 16) 14362306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_CATACOD(v) ((v) << 8) 14462306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_MODE(v) ((v) << 6) 14562306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_EMPHASIS(v) ((v) << 3) 14662306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_CP BIT(2) 14762306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_AUDIO BIT(1) 14862306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA0_PRO BIT(0) 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#define SUN4I_SPDIF_RXCHSTA1 (0x38) 15162306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA1_CGMSA(v) ((v) << 8) 15262306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA1_ORISAMFREQ(v) ((v) << 4) 15362306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA1_SAMWORDLEN(v) ((v) << 1) 15462306a36Sopenharmony_ci #define SUN4I_SPDIF_RXCHSTA1_MAXWORDLEN BIT(0) 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* Defines for Sampling Frequency */ 15762306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_44_1KHZ 0x0 15862306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_NOT_INDICATED 0x1 15962306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_48KHZ 0x2 16062306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_32KHZ 0x3 16162306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_22_05KHZ 0x4 16262306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_24KHZ 0x6 16362306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_88_2KHZ 0x8 16462306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_76_8KHZ 0x9 16562306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_96KHZ 0xa 16662306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_176_4KHZ 0xc 16762306a36Sopenharmony_ci#define SUN4I_SPDIF_SAMFREQ_192KHZ 0xe 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/** 17062306a36Sopenharmony_ci * struct sun4i_spdif_quirks - Differences between SoC variants. 17162306a36Sopenharmony_ci * 17262306a36Sopenharmony_ci * @reg_dac_txdata: TX FIFO offset for DMA config. 17362306a36Sopenharmony_ci * @has_reset: SoC needs reset deasserted. 17462306a36Sopenharmony_ci * @val_fctl_ftx: TX FIFO flush bitmask. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistruct sun4i_spdif_quirks { 17762306a36Sopenharmony_ci unsigned int reg_dac_txdata; 17862306a36Sopenharmony_ci bool has_reset; 17962306a36Sopenharmony_ci unsigned int val_fctl_ftx; 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistruct sun4i_spdif_dev { 18362306a36Sopenharmony_ci struct platform_device *pdev; 18462306a36Sopenharmony_ci struct clk *spdif_clk; 18562306a36Sopenharmony_ci struct clk *apb_clk; 18662306a36Sopenharmony_ci struct reset_control *rst; 18762306a36Sopenharmony_ci struct snd_soc_dai_driver cpu_dai_drv; 18862306a36Sopenharmony_ci struct regmap *regmap; 18962306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data dma_params_tx; 19062306a36Sopenharmony_ci const struct sun4i_spdif_quirks *quirks; 19162306a36Sopenharmony_ci spinlock_t lock; 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void sun4i_spdif_configure(struct sun4i_spdif_dev *host) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci const struct sun4i_spdif_quirks *quirks = host->quirks; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* soft reset SPDIF */ 19962306a36Sopenharmony_ci regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* flush TX FIFO */ 20262306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, 20362306a36Sopenharmony_ci quirks->val_fctl_ftx, quirks->val_fctl_ftx); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* clear TX counter */ 20662306a36Sopenharmony_ci regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic void sun4i_snd_txctrl_on(struct snd_pcm_substream *substream, 21062306a36Sopenharmony_ci struct sun4i_spdif_dev *host) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci if (substream->runtime->channels == 1) 21362306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, 21462306a36Sopenharmony_ci SUN4I_SPDIF_TXCFG_SINGLEMOD, 21562306a36Sopenharmony_ci SUN4I_SPDIF_TXCFG_SINGLEMOD); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* SPDIF TX ENABLE */ 21862306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, 21962306a36Sopenharmony_ci SUN4I_SPDIF_TXCFG_TXEN, SUN4I_SPDIF_TXCFG_TXEN); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* DRQ ENABLE */ 22262306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_INT, 22362306a36Sopenharmony_ci SUN4I_SPDIF_INT_TXDRQEN, SUN4I_SPDIF_INT_TXDRQEN); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Global enable */ 22662306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL, 22762306a36Sopenharmony_ci SUN4I_SPDIF_CTL_GEN, SUN4I_SPDIF_CTL_GEN); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream, 23162306a36Sopenharmony_ci struct sun4i_spdif_dev *host) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci /* SPDIF TX DISABLE */ 23462306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, 23562306a36Sopenharmony_ci SUN4I_SPDIF_TXCFG_TXEN, 0); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* DRQ DISABLE */ 23862306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_INT, 23962306a36Sopenharmony_ci SUN4I_SPDIF_INT_TXDRQEN, 0); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Global disable */ 24262306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL, 24362306a36Sopenharmony_ci SUN4I_SPDIF_CTL_GEN, 0); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int sun4i_spdif_startup(struct snd_pcm_substream *substream, 24762306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 25062306a36Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci sun4i_spdif_configure(host); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int sun4i_spdif_hw_params(struct snd_pcm_substream *substream, 26162306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 26262306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci int ret = 0; 26562306a36Sopenharmony_ci int fmt; 26662306a36Sopenharmony_ci unsigned long rate = params_rate(params); 26762306a36Sopenharmony_ci u32 mclk_div = 0; 26862306a36Sopenharmony_ci unsigned int mclk = 0; 26962306a36Sopenharmony_ci u32 reg_val; 27062306a36Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); 27162306a36Sopenharmony_ci struct platform_device *pdev = host->pdev; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Add the PCM and raw data select interface */ 27462306a36Sopenharmony_ci switch (params_channels(params)) { 27562306a36Sopenharmony_ci case 1: /* PCM mode */ 27662306a36Sopenharmony_ci case 2: 27762306a36Sopenharmony_ci fmt = 0; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci case 4: /* raw data mode */ 28062306a36Sopenharmony_ci fmt = SUN4I_SPDIF_TXCFG_NONAUDIO; 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci default: 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci switch (params_format(params)) { 28762306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S16_LE: 28862306a36Sopenharmony_ci fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S20_3LE: 29162306a36Sopenharmony_ci fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case SNDRV_PCM_FORMAT_S24_LE: 29462306a36Sopenharmony_ci fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT; 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci default: 29762306a36Sopenharmony_ci return -EINVAL; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci switch (rate) { 30162306a36Sopenharmony_ci case 22050: 30262306a36Sopenharmony_ci case 44100: 30362306a36Sopenharmony_ci case 88200: 30462306a36Sopenharmony_ci case 176400: 30562306a36Sopenharmony_ci mclk = 22579200; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci case 24000: 30862306a36Sopenharmony_ci case 32000: 30962306a36Sopenharmony_ci case 48000: 31062306a36Sopenharmony_ci case 96000: 31162306a36Sopenharmony_ci case 192000: 31262306a36Sopenharmony_ci mclk = 24576000; 31362306a36Sopenharmony_ci break; 31462306a36Sopenharmony_ci default: 31562306a36Sopenharmony_ci return -EINVAL; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ret = clk_set_rate(host->spdif_clk, mclk); 31962306a36Sopenharmony_ci if (ret < 0) { 32062306a36Sopenharmony_ci dev_err(&pdev->dev, 32162306a36Sopenharmony_ci "Setting SPDIF clock rate for %d Hz failed!\n", mclk); 32262306a36Sopenharmony_ci return ret; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL, 32662306a36Sopenharmony_ci SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci switch (rate) { 32962306a36Sopenharmony_ci case 22050: 33062306a36Sopenharmony_ci case 24000: 33162306a36Sopenharmony_ci mclk_div = 8; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case 32000: 33462306a36Sopenharmony_ci mclk_div = 6; 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci case 44100: 33762306a36Sopenharmony_ci case 48000: 33862306a36Sopenharmony_ci mclk_div = 4; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case 88200: 34162306a36Sopenharmony_ci case 96000: 34262306a36Sopenharmony_ci mclk_div = 2; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case 176400: 34562306a36Sopenharmony_ci case 192000: 34662306a36Sopenharmony_ci mclk_div = 1; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci default: 34962306a36Sopenharmony_ci return -EINVAL; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci reg_val = 0; 35362306a36Sopenharmony_ci reg_val |= SUN4I_SPDIF_TXCFG_ASS; 35462306a36Sopenharmony_ci reg_val |= fmt; /* set non audio and bit depth */ 35562306a36Sopenharmony_ci reg_val |= SUN4I_SPDIF_TXCFG_CHSTMODE; 35662306a36Sopenharmony_ci reg_val |= SUN4I_SPDIF_TXCFG_TXRATIO(mclk_div - 1); 35762306a36Sopenharmony_ci regmap_write(host->regmap, SUN4I_SPDIF_TXCFG, reg_val); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd, 36362306a36Sopenharmony_ci struct snd_soc_dai *dai) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci int ret = 0; 36662306a36Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) 36962306a36Sopenharmony_ci return -EINVAL; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci switch (cmd) { 37262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 37362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 37462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 37562306a36Sopenharmony_ci sun4i_snd_txctrl_on(substream, host); 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 37962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 38062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 38162306a36Sopenharmony_ci sun4i_snd_txctrl_off(substream, host); 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci default: 38562306a36Sopenharmony_ci ret = -EINVAL; 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci return ret; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int sun4i_spdif_info(struct snd_kcontrol *kcontrol, 39262306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 39562306a36Sopenharmony_ci uinfo->count = 1; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int sun4i_spdif_get_status_mask(struct snd_kcontrol *kcontrol, 40162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci u8 *status = ucontrol->value.iec958.status; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci status[0] = 0xff; 40662306a36Sopenharmony_ci status[1] = 0xff; 40762306a36Sopenharmony_ci status[2] = 0xff; 40862306a36Sopenharmony_ci status[3] = 0xff; 40962306a36Sopenharmony_ci status[4] = 0xff; 41062306a36Sopenharmony_ci status[5] = 0x03; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int sun4i_spdif_get_status(struct snd_kcontrol *kcontrol, 41662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); 41962306a36Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); 42062306a36Sopenharmony_ci u8 *status = ucontrol->value.iec958.status; 42162306a36Sopenharmony_ci unsigned long flags; 42262306a36Sopenharmony_ci unsigned int reg; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA0, ®); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci status[0] = reg & 0xff; 42962306a36Sopenharmony_ci status[1] = (reg >> 8) & 0xff; 43062306a36Sopenharmony_ci status[2] = (reg >> 16) & 0xff; 43162306a36Sopenharmony_ci status[3] = (reg >> 24) & 0xff; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA1, ®); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci status[4] = reg & 0xff; 43662306a36Sopenharmony_ci status[5] = (reg >> 8) & 0x3; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int sun4i_spdif_set_status(struct snd_kcontrol *kcontrol, 44462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); 44762306a36Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); 44862306a36Sopenharmony_ci u8 *status = ucontrol->value.iec958.status; 44962306a36Sopenharmony_ci unsigned long flags; 45062306a36Sopenharmony_ci unsigned int reg; 45162306a36Sopenharmony_ci bool chg0, chg1; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci reg = (u32)status[3] << 24; 45662306a36Sopenharmony_ci reg |= (u32)status[2] << 16; 45762306a36Sopenharmony_ci reg |= (u32)status[1] << 8; 45862306a36Sopenharmony_ci reg |= (u32)status[0]; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA0, 46162306a36Sopenharmony_ci GENMASK(31,0), reg, &chg0); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci reg = (u32)status[5] << 8; 46462306a36Sopenharmony_ci reg |= (u32)status[4]; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA1, 46762306a36Sopenharmony_ci GENMASK(9,0), reg, &chg1); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci reg = SUN4I_SPDIF_TXCFG_CHSTMODE; 47062306a36Sopenharmony_ci if (status[0] & IEC958_AES0_NONAUDIO) 47162306a36Sopenharmony_ci reg |= SUN4I_SPDIF_TXCFG_NONAUDIO; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, 47462306a36Sopenharmony_ci SUN4I_SPDIF_TXCFG_CHSTMODE | 47562306a36Sopenharmony_ci SUN4I_SPDIF_TXCFG_NONAUDIO, reg); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return chg0 || chg1; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic struct snd_kcontrol_new sun4i_spdif_controls[] = { 48362306a36Sopenharmony_ci { 48462306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 48562306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 48662306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), 48762306a36Sopenharmony_ci .info = sun4i_spdif_info, 48862306a36Sopenharmony_ci .get = sun4i_spdif_get_status_mask 48962306a36Sopenharmony_ci }, 49062306a36Sopenharmony_ci { 49162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 49262306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 49362306a36Sopenharmony_ci .info = sun4i_spdif_info, 49462306a36Sopenharmony_ci .get = sun4i_spdif_get_status, 49562306a36Sopenharmony_ci .put = sun4i_spdif_set_status 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci}; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL); 50462306a36Sopenharmony_ci snd_soc_add_dai_controls(dai, sun4i_spdif_controls, 50562306a36Sopenharmony_ci ARRAY_SIZE(sun4i_spdif_controls)); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic const struct snd_soc_dai_ops sun4i_spdif_dai_ops = { 51162306a36Sopenharmony_ci .probe = sun4i_spdif_soc_dai_probe, 51262306a36Sopenharmony_ci .startup = sun4i_spdif_startup, 51362306a36Sopenharmony_ci .trigger = sun4i_spdif_trigger, 51462306a36Sopenharmony_ci .hw_params = sun4i_spdif_hw_params, 51562306a36Sopenharmony_ci}; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic const struct regmap_config sun4i_spdif_regmap_config = { 51862306a36Sopenharmony_ci .reg_bits = 32, 51962306a36Sopenharmony_ci .reg_stride = 4, 52062306a36Sopenharmony_ci .val_bits = 32, 52162306a36Sopenharmony_ci .max_register = SUN4I_SPDIF_RXCHSTA1, 52262306a36Sopenharmony_ci}; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci#define SUN4I_RATES SNDRV_PCM_RATE_8000_192000 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci#define SUN4I_FORMATS (SNDRV_PCM_FORMAT_S16_LE | \ 52762306a36Sopenharmony_ci SNDRV_PCM_FORMAT_S20_3LE | \ 52862306a36Sopenharmony_ci SNDRV_PCM_FORMAT_S24_LE) 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic struct snd_soc_dai_driver sun4i_spdif_dai = { 53162306a36Sopenharmony_ci .playback = { 53262306a36Sopenharmony_ci .channels_min = 1, 53362306a36Sopenharmony_ci .channels_max = 2, 53462306a36Sopenharmony_ci .rates = SUN4I_RATES, 53562306a36Sopenharmony_ci .formats = SUN4I_FORMATS, 53662306a36Sopenharmony_ci }, 53762306a36Sopenharmony_ci .ops = &sun4i_spdif_dai_ops, 53862306a36Sopenharmony_ci .name = "spdif", 53962306a36Sopenharmony_ci}; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic const struct sun4i_spdif_quirks sun4i_a10_spdif_quirks = { 54262306a36Sopenharmony_ci .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, 54362306a36Sopenharmony_ci .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, 54462306a36Sopenharmony_ci}; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic const struct sun4i_spdif_quirks sun6i_a31_spdif_quirks = { 54762306a36Sopenharmony_ci .reg_dac_txdata = SUN4I_SPDIF_TXFIFO, 54862306a36Sopenharmony_ci .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, 54962306a36Sopenharmony_ci .has_reset = true, 55062306a36Sopenharmony_ci}; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic const struct sun4i_spdif_quirks sun8i_h3_spdif_quirks = { 55362306a36Sopenharmony_ci .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, 55462306a36Sopenharmony_ci .val_fctl_ftx = SUN4I_SPDIF_FCTL_FTX, 55562306a36Sopenharmony_ci .has_reset = true, 55662306a36Sopenharmony_ci}; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic const struct sun4i_spdif_quirks sun50i_h6_spdif_quirks = { 55962306a36Sopenharmony_ci .reg_dac_txdata = SUN8I_SPDIF_TXFIFO, 56062306a36Sopenharmony_ci .val_fctl_ftx = SUN50I_H6_SPDIF_FCTL_FTX, 56162306a36Sopenharmony_ci .has_reset = true, 56262306a36Sopenharmony_ci}; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic const struct of_device_id sun4i_spdif_of_match[] = { 56562306a36Sopenharmony_ci { 56662306a36Sopenharmony_ci .compatible = "allwinner,sun4i-a10-spdif", 56762306a36Sopenharmony_ci .data = &sun4i_a10_spdif_quirks, 56862306a36Sopenharmony_ci }, 56962306a36Sopenharmony_ci { 57062306a36Sopenharmony_ci .compatible = "allwinner,sun6i-a31-spdif", 57162306a36Sopenharmony_ci .data = &sun6i_a31_spdif_quirks, 57262306a36Sopenharmony_ci }, 57362306a36Sopenharmony_ci { 57462306a36Sopenharmony_ci .compatible = "allwinner,sun8i-h3-spdif", 57562306a36Sopenharmony_ci .data = &sun8i_h3_spdif_quirks, 57662306a36Sopenharmony_ci }, 57762306a36Sopenharmony_ci { 57862306a36Sopenharmony_ci .compatible = "allwinner,sun50i-h6-spdif", 57962306a36Sopenharmony_ci .data = &sun50i_h6_spdif_quirks, 58062306a36Sopenharmony_ci }, 58162306a36Sopenharmony_ci { 58262306a36Sopenharmony_ci .compatible = "allwinner,sun50i-h616-spdif", 58362306a36Sopenharmony_ci /* Essentially the same as the H6, but without RX */ 58462306a36Sopenharmony_ci .data = &sun50i_h6_spdif_quirks, 58562306a36Sopenharmony_ci }, 58662306a36Sopenharmony_ci { /* sentinel */ } 58762306a36Sopenharmony_ci}; 58862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4i_spdif_of_match); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic const struct snd_soc_component_driver sun4i_spdif_component = { 59162306a36Sopenharmony_ci .name = "sun4i-spdif", 59262306a36Sopenharmony_ci .legacy_dai_naming = 1, 59362306a36Sopenharmony_ci}; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic int sun4i_spdif_runtime_suspend(struct device *dev) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct sun4i_spdif_dev *host = dev_get_drvdata(dev); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci clk_disable_unprepare(host->spdif_clk); 60062306a36Sopenharmony_ci clk_disable_unprepare(host->apb_clk); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int sun4i_spdif_runtime_resume(struct device *dev) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct sun4i_spdif_dev *host = dev_get_drvdata(dev); 60862306a36Sopenharmony_ci int ret; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci ret = clk_prepare_enable(host->spdif_clk); 61162306a36Sopenharmony_ci if (ret) 61262306a36Sopenharmony_ci return ret; 61362306a36Sopenharmony_ci ret = clk_prepare_enable(host->apb_clk); 61462306a36Sopenharmony_ci if (ret) 61562306a36Sopenharmony_ci clk_disable_unprepare(host->spdif_clk); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return ret; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int sun4i_spdif_probe(struct platform_device *pdev) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct sun4i_spdif_dev *host; 62362306a36Sopenharmony_ci struct resource *res; 62462306a36Sopenharmony_ci const struct sun4i_spdif_quirks *quirks; 62562306a36Sopenharmony_ci int ret; 62662306a36Sopenharmony_ci void __iomem *base; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Entered %s\n", __func__); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); 63162306a36Sopenharmony_ci if (!host) 63262306a36Sopenharmony_ci return -ENOMEM; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci host->pdev = pdev; 63562306a36Sopenharmony_ci spin_lock_init(&host->lock); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* Initialize this copy of the CPU DAI driver structure */ 63862306a36Sopenharmony_ci memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai)); 63962306a36Sopenharmony_ci host->cpu_dai_drv.name = dev_name(&pdev->dev); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Get the addresses */ 64262306a36Sopenharmony_ci base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 64362306a36Sopenharmony_ci if (IS_ERR(base)) 64462306a36Sopenharmony_ci return PTR_ERR(base); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci quirks = of_device_get_match_data(&pdev->dev); 64762306a36Sopenharmony_ci if (quirks == NULL) { 64862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); 64962306a36Sopenharmony_ci return -ENODEV; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci host->quirks = quirks; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci host->regmap = devm_regmap_init_mmio(&pdev->dev, base, 65462306a36Sopenharmony_ci &sun4i_spdif_regmap_config); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* Clocks */ 65762306a36Sopenharmony_ci host->apb_clk = devm_clk_get(&pdev->dev, "apb"); 65862306a36Sopenharmony_ci if (IS_ERR(host->apb_clk)) { 65962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get a apb clock.\n"); 66062306a36Sopenharmony_ci return PTR_ERR(host->apb_clk); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci host->spdif_clk = devm_clk_get(&pdev->dev, "spdif"); 66462306a36Sopenharmony_ci if (IS_ERR(host->spdif_clk)) { 66562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get a spdif clock.\n"); 66662306a36Sopenharmony_ci return PTR_ERR(host->spdif_clk); 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci host->dma_params_tx.addr = res->start + quirks->reg_dac_txdata; 67062306a36Sopenharmony_ci host->dma_params_tx.maxburst = 8; 67162306a36Sopenharmony_ci host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci platform_set_drvdata(pdev, host); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (quirks->has_reset) { 67662306a36Sopenharmony_ci host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, 67762306a36Sopenharmony_ci NULL); 67862306a36Sopenharmony_ci if (PTR_ERR(host->rst) == -EPROBE_DEFER) { 67962306a36Sopenharmony_ci ret = -EPROBE_DEFER; 68062306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); 68162306a36Sopenharmony_ci return ret; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci if (!IS_ERR(host->rst)) 68462306a36Sopenharmony_ci reset_control_deassert(host->rst); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 68862306a36Sopenharmony_ci &sun4i_spdif_component, &sun4i_spdif_dai, 1); 68962306a36Sopenharmony_ci if (ret) 69062306a36Sopenharmony_ci return ret; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 69362306a36Sopenharmony_ci if (!pm_runtime_enabled(&pdev->dev)) { 69462306a36Sopenharmony_ci ret = sun4i_spdif_runtime_resume(&pdev->dev); 69562306a36Sopenharmony_ci if (ret) 69662306a36Sopenharmony_ci goto err_unregister; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 70062306a36Sopenharmony_ci if (ret) 70162306a36Sopenharmony_ci goto err_suspend; 70262306a36Sopenharmony_ci return 0; 70362306a36Sopenharmony_cierr_suspend: 70462306a36Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 70562306a36Sopenharmony_ci sun4i_spdif_runtime_suspend(&pdev->dev); 70662306a36Sopenharmony_cierr_unregister: 70762306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 70862306a36Sopenharmony_ci return ret; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic void sun4i_spdif_remove(struct platform_device *pdev) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 71462306a36Sopenharmony_ci if (!pm_runtime_status_suspended(&pdev->dev)) 71562306a36Sopenharmony_ci sun4i_spdif_runtime_suspend(&pdev->dev); 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic const struct dev_pm_ops sun4i_spdif_pm = { 71962306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend, 72062306a36Sopenharmony_ci sun4i_spdif_runtime_resume, NULL) 72162306a36Sopenharmony_ci}; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic struct platform_driver sun4i_spdif_driver = { 72462306a36Sopenharmony_ci .driver = { 72562306a36Sopenharmony_ci .name = "sun4i-spdif", 72662306a36Sopenharmony_ci .of_match_table = sun4i_spdif_of_match, 72762306a36Sopenharmony_ci .pm = &sun4i_spdif_pm, 72862306a36Sopenharmony_ci }, 72962306a36Sopenharmony_ci .probe = sun4i_spdif_probe, 73062306a36Sopenharmony_ci .remove_new = sun4i_spdif_remove, 73162306a36Sopenharmony_ci}; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cimodule_platform_driver(sun4i_spdif_driver); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ciMODULE_AUTHOR("Marcus Cooper <codekipper@gmail.com>"); 73662306a36Sopenharmony_ciMODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>"); 73762306a36Sopenharmony_ciMODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface"); 73862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 73962306a36Sopenharmony_ciMODULE_ALIAS("platform:sun4i-spdif"); 740