162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// ALSA SoC Audio Layer - Samsung S/PDIF Controller driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2010 Samsung Electronics Co. Ltd 662306a36Sopenharmony_ci// http://www.samsung.com/ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <sound/soc.h> 1362306a36Sopenharmony_ci#include <sound/pcm_params.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/platform_data/asoc-s3c.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "dma.h" 1862306a36Sopenharmony_ci#include "spdif.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* Registers */ 2162306a36Sopenharmony_ci#define CLKCON 0x00 2262306a36Sopenharmony_ci#define CON 0x04 2362306a36Sopenharmony_ci#define BSTAS 0x08 2462306a36Sopenharmony_ci#define CSTAS 0x0C 2562306a36Sopenharmony_ci#define DATA_OUTBUF 0x10 2662306a36Sopenharmony_ci#define DCNT 0x14 2762306a36Sopenharmony_ci#define BSTAS_S 0x18 2862306a36Sopenharmony_ci#define DCNT_S 0x1C 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define CLKCTL_MASK 0x7 3162306a36Sopenharmony_ci#define CLKCTL_MCLK_EXT (0x1 << 2) 3262306a36Sopenharmony_ci#define CLKCTL_PWR_ON (0x1 << 0) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define CON_MASK 0x3ffffff 3562306a36Sopenharmony_ci#define CON_FIFO_TH_SHIFT 19 3662306a36Sopenharmony_ci#define CON_FIFO_TH_MASK (0x7 << 19) 3762306a36Sopenharmony_ci#define CON_USERDATA_23RDBIT (0x1 << 12) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define CON_SW_RESET (0x1 << 5) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define CON_MCLKDIV_MASK (0x3 << 3) 4262306a36Sopenharmony_ci#define CON_MCLKDIV_256FS (0x0 << 3) 4362306a36Sopenharmony_ci#define CON_MCLKDIV_384FS (0x1 << 3) 4462306a36Sopenharmony_ci#define CON_MCLKDIV_512FS (0x2 << 3) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define CON_PCM_MASK (0x3 << 1) 4762306a36Sopenharmony_ci#define CON_PCM_16BIT (0x0 << 1) 4862306a36Sopenharmony_ci#define CON_PCM_20BIT (0x1 << 1) 4962306a36Sopenharmony_ci#define CON_PCM_24BIT (0x2 << 1) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define CON_PCM_DATA (0x1 << 0) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define CSTAS_MASK 0x3fffffff 5462306a36Sopenharmony_ci#define CSTAS_SAMP_FREQ_MASK (0xF << 24) 5562306a36Sopenharmony_ci#define CSTAS_SAMP_FREQ_44 (0x0 << 24) 5662306a36Sopenharmony_ci#define CSTAS_SAMP_FREQ_48 (0x2 << 24) 5762306a36Sopenharmony_ci#define CSTAS_SAMP_FREQ_32 (0x3 << 24) 5862306a36Sopenharmony_ci#define CSTAS_SAMP_FREQ_96 (0xA << 24) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define CSTAS_CATEGORY_MASK (0xFF << 8) 6162306a36Sopenharmony_ci#define CSTAS_CATEGORY_CODE_CDP (0x01 << 8) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define CSTAS_NO_COPYRIGHT (0x1 << 2) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/** 6662306a36Sopenharmony_ci * struct samsung_spdif_info - Samsung S/PDIF Controller information 6762306a36Sopenharmony_ci * @lock: Spin lock for S/PDIF. 6862306a36Sopenharmony_ci * @dev: The parent device passed to use from the probe. 6962306a36Sopenharmony_ci * @regs: The pointer to the device register block. 7062306a36Sopenharmony_ci * @clk_rate: Current clock rate for calcurate ratio. 7162306a36Sopenharmony_ci * @pclk: The peri-clock pointer for spdif master operation. 7262306a36Sopenharmony_ci * @sclk: The source clock pointer for making sync signals. 7362306a36Sopenharmony_ci * @saved_clkcon: Backup clkcon reg. in suspend. 7462306a36Sopenharmony_ci * @saved_con: Backup con reg. in suspend. 7562306a36Sopenharmony_ci * @saved_cstas: Backup cstas reg. in suspend. 7662306a36Sopenharmony_ci * @dma_playback: DMA information for playback channel. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_cistruct samsung_spdif_info { 7962306a36Sopenharmony_ci spinlock_t lock; 8062306a36Sopenharmony_ci struct device *dev; 8162306a36Sopenharmony_ci void __iomem *regs; 8262306a36Sopenharmony_ci unsigned long clk_rate; 8362306a36Sopenharmony_ci struct clk *pclk; 8462306a36Sopenharmony_ci struct clk *sclk; 8562306a36Sopenharmony_ci u32 saved_clkcon; 8662306a36Sopenharmony_ci u32 saved_con; 8762306a36Sopenharmony_ci u32 saved_cstas; 8862306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_playback; 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data spdif_stereo_out; 9262306a36Sopenharmony_cistatic struct samsung_spdif_info spdif_info; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic inline struct samsung_spdif_info 9562306a36Sopenharmony_ci*component_to_info(struct snd_soc_component *component) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return snd_soc_component_get_drvdata(component); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci return snd_soc_dai_get_drvdata(cpu_dai); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci void __iomem *regs = spdif->regs; 10862306a36Sopenharmony_ci u32 clkcon; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci clkcon = readl(regs + CLKCON) & CLKCTL_MASK; 11362306a36Sopenharmony_ci if (on) 11462306a36Sopenharmony_ci writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON); 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int spdif_set_sysclk(struct snd_soc_dai *cpu_dai, 12062306a36Sopenharmony_ci int clk_id, unsigned int freq, int dir) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct samsung_spdif_info *spdif = to_info(cpu_dai); 12362306a36Sopenharmony_ci u32 clkcon; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci clkcon = readl(spdif->regs + CLKCON); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (clk_id == SND_SOC_SPDIF_INT_MCLK) 13062306a36Sopenharmony_ci clkcon &= ~CLKCTL_MCLK_EXT; 13162306a36Sopenharmony_ci else 13262306a36Sopenharmony_ci clkcon |= CLKCTL_MCLK_EXT; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci writel(clkcon, spdif->regs + CLKCON); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci spdif->clk_rate = freq; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int spdif_trigger(struct snd_pcm_substream *substream, int cmd, 14262306a36Sopenharmony_ci struct snd_soc_dai *dai) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 14562306a36Sopenharmony_ci struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); 14662306a36Sopenharmony_ci unsigned long flags; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci switch (cmd) { 15162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 15262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 15362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 15462306a36Sopenharmony_ci spin_lock_irqsave(&spdif->lock, flags); 15562306a36Sopenharmony_ci spdif_snd_txctrl(spdif, 1); 15662306a36Sopenharmony_ci spin_unlock_irqrestore(&spdif->lock, flags); 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 15962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 16062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 16162306a36Sopenharmony_ci spin_lock_irqsave(&spdif->lock, flags); 16262306a36Sopenharmony_ci spdif_snd_txctrl(spdif, 0); 16362306a36Sopenharmony_ci spin_unlock_irqrestore(&spdif->lock, flags); 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci default: 16662306a36Sopenharmony_ci return -EINVAL; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int spdif_sysclk_ratios[] = { 17362306a36Sopenharmony_ci 512, 384, 256, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int spdif_hw_params(struct snd_pcm_substream *substream, 17762306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 17862306a36Sopenharmony_ci struct snd_soc_dai *socdai) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 18162306a36Sopenharmony_ci struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); 18262306a36Sopenharmony_ci void __iomem *regs = spdif->regs; 18362306a36Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 18462306a36Sopenharmony_ci u32 con, clkcon, cstas; 18562306a36Sopenharmony_ci unsigned long flags; 18662306a36Sopenharmony_ci int i, ratio; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 19162306a36Sopenharmony_ci dma_data = spdif->dma_playback; 19262306a36Sopenharmony_ci else { 19362306a36Sopenharmony_ci dev_err(spdif->dev, "Capture is not supported\n"); 19462306a36Sopenharmony_ci return -EINVAL; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_data); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci spin_lock_irqsave(&spdif->lock, flags); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci con = readl(regs + CON) & CON_MASK; 20262306a36Sopenharmony_ci cstas = readl(regs + CSTAS) & CSTAS_MASK; 20362306a36Sopenharmony_ci clkcon = readl(regs + CLKCON) & CLKCTL_MASK; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci con &= ~CON_FIFO_TH_MASK; 20662306a36Sopenharmony_ci con |= (0x7 << CON_FIFO_TH_SHIFT); 20762306a36Sopenharmony_ci con |= CON_USERDATA_23RDBIT; 20862306a36Sopenharmony_ci con |= CON_PCM_DATA; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci con &= ~CON_PCM_MASK; 21162306a36Sopenharmony_ci switch (params_width(params)) { 21262306a36Sopenharmony_ci case 16: 21362306a36Sopenharmony_ci con |= CON_PCM_16BIT; 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci default: 21662306a36Sopenharmony_ci dev_err(spdif->dev, "Unsupported data size.\n"); 21762306a36Sopenharmony_ci goto err; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ratio = spdif->clk_rate / params_rate(params); 22162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(spdif_sysclk_ratios); i++) 22262306a36Sopenharmony_ci if (ratio == spdif_sysclk_ratios[i]) 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci if (i == ARRAY_SIZE(spdif_sysclk_ratios)) { 22562306a36Sopenharmony_ci dev_err(spdif->dev, "Invalid clock ratio %ld/%d\n", 22662306a36Sopenharmony_ci spdif->clk_rate, params_rate(params)); 22762306a36Sopenharmony_ci goto err; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci con &= ~CON_MCLKDIV_MASK; 23162306a36Sopenharmony_ci switch (ratio) { 23262306a36Sopenharmony_ci case 256: 23362306a36Sopenharmony_ci con |= CON_MCLKDIV_256FS; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case 384: 23662306a36Sopenharmony_ci con |= CON_MCLKDIV_384FS; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case 512: 23962306a36Sopenharmony_ci con |= CON_MCLKDIV_512FS; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci cstas &= ~CSTAS_SAMP_FREQ_MASK; 24462306a36Sopenharmony_ci switch (params_rate(params)) { 24562306a36Sopenharmony_ci case 44100: 24662306a36Sopenharmony_ci cstas |= CSTAS_SAMP_FREQ_44; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case 48000: 24962306a36Sopenharmony_ci cstas |= CSTAS_SAMP_FREQ_48; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci case 32000: 25262306a36Sopenharmony_ci cstas |= CSTAS_SAMP_FREQ_32; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case 96000: 25562306a36Sopenharmony_ci cstas |= CSTAS_SAMP_FREQ_96; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci default: 25862306a36Sopenharmony_ci dev_err(spdif->dev, "Invalid sampling rate %d\n", 25962306a36Sopenharmony_ci params_rate(params)); 26062306a36Sopenharmony_ci goto err; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci cstas &= ~CSTAS_CATEGORY_MASK; 26462306a36Sopenharmony_ci cstas |= CSTAS_CATEGORY_CODE_CDP; 26562306a36Sopenharmony_ci cstas |= CSTAS_NO_COPYRIGHT; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci writel(con, regs + CON); 26862306a36Sopenharmony_ci writel(cstas, regs + CSTAS); 26962306a36Sopenharmony_ci writel(clkcon, regs + CLKCON); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci spin_unlock_irqrestore(&spdif->lock, flags); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_cierr: 27562306a36Sopenharmony_ci spin_unlock_irqrestore(&spdif->lock, flags); 27662306a36Sopenharmony_ci return -EINVAL; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic void spdif_shutdown(struct snd_pcm_substream *substream, 28062306a36Sopenharmony_ci struct snd_soc_dai *dai) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 28362306a36Sopenharmony_ci struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); 28462306a36Sopenharmony_ci void __iomem *regs = spdif->regs; 28562306a36Sopenharmony_ci u32 con, clkcon; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci con = readl(regs + CON) & CON_MASK; 29062306a36Sopenharmony_ci clkcon = readl(regs + CLKCON) & CLKCTL_MASK; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci writel(con | CON_SW_RESET, regs + CON); 29362306a36Sopenharmony_ci cpu_relax(); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci#ifdef CONFIG_PM 29962306a36Sopenharmony_cistatic int spdif_suspend(struct snd_soc_component *component) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct samsung_spdif_info *spdif = component_to_info(component); 30262306a36Sopenharmony_ci u32 con = spdif->saved_con; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci spdif->saved_clkcon = readl(spdif->regs + CLKCON) & CLKCTL_MASK; 30762306a36Sopenharmony_ci spdif->saved_con = readl(spdif->regs + CON) & CON_MASK; 30862306a36Sopenharmony_ci spdif->saved_cstas = readl(spdif->regs + CSTAS) & CSTAS_MASK; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci writel(con | CON_SW_RESET, spdif->regs + CON); 31162306a36Sopenharmony_ci cpu_relax(); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return 0; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int spdif_resume(struct snd_soc_component *component) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct samsung_spdif_info *spdif = component_to_info(component); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci writel(spdif->saved_clkcon, spdif->regs + CLKCON); 32362306a36Sopenharmony_ci writel(spdif->saved_con, spdif->regs + CON); 32462306a36Sopenharmony_ci writel(spdif->saved_cstas, spdif->regs + CSTAS); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci#else 32962306a36Sopenharmony_ci#define spdif_suspend NULL 33062306a36Sopenharmony_ci#define spdif_resume NULL 33162306a36Sopenharmony_ci#endif 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic const struct snd_soc_dai_ops spdif_dai_ops = { 33462306a36Sopenharmony_ci .set_sysclk = spdif_set_sysclk, 33562306a36Sopenharmony_ci .trigger = spdif_trigger, 33662306a36Sopenharmony_ci .hw_params = spdif_hw_params, 33762306a36Sopenharmony_ci .shutdown = spdif_shutdown, 33862306a36Sopenharmony_ci}; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic struct snd_soc_dai_driver samsung_spdif_dai = { 34162306a36Sopenharmony_ci .name = "samsung-spdif", 34262306a36Sopenharmony_ci .playback = { 34362306a36Sopenharmony_ci .stream_name = "S/PDIF Playback", 34462306a36Sopenharmony_ci .channels_min = 2, 34562306a36Sopenharmony_ci .channels_max = 2, 34662306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_32000 | 34762306a36Sopenharmony_ci SNDRV_PCM_RATE_44100 | 34862306a36Sopenharmony_ci SNDRV_PCM_RATE_48000 | 34962306a36Sopenharmony_ci SNDRV_PCM_RATE_96000), 35062306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, }, 35162306a36Sopenharmony_ci .ops = &spdif_dai_ops, 35262306a36Sopenharmony_ci}; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic const struct snd_soc_component_driver samsung_spdif_component = { 35562306a36Sopenharmony_ci .name = "samsung-spdif", 35662306a36Sopenharmony_ci .suspend = spdif_suspend, 35762306a36Sopenharmony_ci .resume = spdif_resume, 35862306a36Sopenharmony_ci .legacy_dai_naming = 1, 35962306a36Sopenharmony_ci}; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int spdif_probe(struct platform_device *pdev) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct s3c_audio_pdata *spdif_pdata; 36462306a36Sopenharmony_ci struct resource *mem_res; 36562306a36Sopenharmony_ci struct samsung_spdif_info *spdif; 36662306a36Sopenharmony_ci dma_filter_fn filter; 36762306a36Sopenharmony_ci int ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci spdif_pdata = pdev->dev.platform_data; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Entered %s\n", __func__); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 37462306a36Sopenharmony_ci if (!mem_res) { 37562306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to get register resource.\n"); 37662306a36Sopenharmony_ci return -ENXIO; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (spdif_pdata && spdif_pdata->cfg_gpio 38062306a36Sopenharmony_ci && spdif_pdata->cfg_gpio(pdev)) { 38162306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to configure GPIO pins\n"); 38262306a36Sopenharmony_ci return -EINVAL; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci spdif = &spdif_info; 38662306a36Sopenharmony_ci spdif->dev = &pdev->dev; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci spin_lock_init(&spdif->lock); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); 39162306a36Sopenharmony_ci if (IS_ERR(spdif->pclk)) { 39262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get peri-clock\n"); 39362306a36Sopenharmony_ci ret = -ENOENT; 39462306a36Sopenharmony_ci goto err0; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci ret = clk_prepare_enable(spdif->pclk); 39762306a36Sopenharmony_ci if (ret) 39862306a36Sopenharmony_ci goto err0; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); 40162306a36Sopenharmony_ci if (IS_ERR(spdif->sclk)) { 40262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get internal source clock\n"); 40362306a36Sopenharmony_ci ret = -ENOENT; 40462306a36Sopenharmony_ci goto err1; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci ret = clk_prepare_enable(spdif->sclk); 40762306a36Sopenharmony_ci if (ret) 40862306a36Sopenharmony_ci goto err1; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Request S/PDIF Register's memory region */ 41162306a36Sopenharmony_ci if (!request_mem_region(mem_res->start, 41262306a36Sopenharmony_ci resource_size(mem_res), "samsung-spdif")) { 41362306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to request register region\n"); 41462306a36Sopenharmony_ci ret = -EBUSY; 41562306a36Sopenharmony_ci goto err2; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci spdif->regs = ioremap(mem_res->start, 0x100); 41962306a36Sopenharmony_ci if (spdif->regs == NULL) { 42062306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot ioremap registers\n"); 42162306a36Sopenharmony_ci ret = -ENXIO; 42262306a36Sopenharmony_ci goto err3; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci spdif_stereo_out.addr_width = 2; 42662306a36Sopenharmony_ci spdif_stereo_out.addr = mem_res->start + DATA_OUTBUF; 42762306a36Sopenharmony_ci filter = NULL; 42862306a36Sopenharmony_ci if (spdif_pdata) { 42962306a36Sopenharmony_ci spdif_stereo_out.filter_data = spdif_pdata->dma_playback; 43062306a36Sopenharmony_ci filter = spdif_pdata->dma_filter; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci spdif->dma_playback = &spdif_stereo_out; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, 43562306a36Sopenharmony_ci NULL, NULL, NULL); 43662306a36Sopenharmony_ci if (ret) { 43762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register DMA: %d\n", ret); 43862306a36Sopenharmony_ci goto err4; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, spdif); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 44462306a36Sopenharmony_ci &samsung_spdif_component, &samsung_spdif_dai, 1); 44562306a36Sopenharmony_ci if (ret != 0) { 44662306a36Sopenharmony_ci dev_err(&pdev->dev, "fail to register dai\n"); 44762306a36Sopenharmony_ci goto err4; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_cierr4: 45262306a36Sopenharmony_ci iounmap(spdif->regs); 45362306a36Sopenharmony_cierr3: 45462306a36Sopenharmony_ci release_mem_region(mem_res->start, resource_size(mem_res)); 45562306a36Sopenharmony_cierr2: 45662306a36Sopenharmony_ci clk_disable_unprepare(spdif->sclk); 45762306a36Sopenharmony_cierr1: 45862306a36Sopenharmony_ci clk_disable_unprepare(spdif->pclk); 45962306a36Sopenharmony_cierr0: 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic void spdif_remove(struct platform_device *pdev) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct samsung_spdif_info *spdif = &spdif_info; 46662306a36Sopenharmony_ci struct resource *mem_res; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci iounmap(spdif->regs); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 47162306a36Sopenharmony_ci release_mem_region(mem_res->start, resource_size(mem_res)); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci clk_disable_unprepare(spdif->sclk); 47462306a36Sopenharmony_ci clk_disable_unprepare(spdif->pclk); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic struct platform_driver samsung_spdif_driver = { 47862306a36Sopenharmony_ci .probe = spdif_probe, 47962306a36Sopenharmony_ci .remove_new = spdif_remove, 48062306a36Sopenharmony_ci .driver = { 48162306a36Sopenharmony_ci .name = "samsung-spdif", 48262306a36Sopenharmony_ci }, 48362306a36Sopenharmony_ci}; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cimodule_platform_driver(samsung_spdif_driver); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ciMODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>"); 48862306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung S/PDIF Controller Driver"); 48962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 49062306a36Sopenharmony_ciMODULE_ALIAS("platform:samsung-spdif"); 491