18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// ALSA SoC Audio Layer - Samsung S/PDIF Controller driver 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (c) 2010 Samsung Electronics Co. Ltd 68c2ecf20Sopenharmony_ci// http://www.samsung.com/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <sound/soc.h> 138c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/platform_data/asoc-s3c.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "dma.h" 188c2ecf20Sopenharmony_ci#include "spdif.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Registers */ 218c2ecf20Sopenharmony_ci#define CLKCON 0x00 228c2ecf20Sopenharmony_ci#define CON 0x04 238c2ecf20Sopenharmony_ci#define BSTAS 0x08 248c2ecf20Sopenharmony_ci#define CSTAS 0x0C 258c2ecf20Sopenharmony_ci#define DATA_OUTBUF 0x10 268c2ecf20Sopenharmony_ci#define DCNT 0x14 278c2ecf20Sopenharmony_ci#define BSTAS_S 0x18 288c2ecf20Sopenharmony_ci#define DCNT_S 0x1C 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define CLKCTL_MASK 0x7 318c2ecf20Sopenharmony_ci#define CLKCTL_MCLK_EXT (0x1 << 2) 328c2ecf20Sopenharmony_ci#define CLKCTL_PWR_ON (0x1 << 0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define CON_MASK 0x3ffffff 358c2ecf20Sopenharmony_ci#define CON_FIFO_TH_SHIFT 19 368c2ecf20Sopenharmony_ci#define CON_FIFO_TH_MASK (0x7 << 19) 378c2ecf20Sopenharmony_ci#define CON_USERDATA_23RDBIT (0x1 << 12) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define CON_SW_RESET (0x1 << 5) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define CON_MCLKDIV_MASK (0x3 << 3) 428c2ecf20Sopenharmony_ci#define CON_MCLKDIV_256FS (0x0 << 3) 438c2ecf20Sopenharmony_ci#define CON_MCLKDIV_384FS (0x1 << 3) 448c2ecf20Sopenharmony_ci#define CON_MCLKDIV_512FS (0x2 << 3) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define CON_PCM_MASK (0x3 << 1) 478c2ecf20Sopenharmony_ci#define CON_PCM_16BIT (0x0 << 1) 488c2ecf20Sopenharmony_ci#define CON_PCM_20BIT (0x1 << 1) 498c2ecf20Sopenharmony_ci#define CON_PCM_24BIT (0x2 << 1) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define CON_PCM_DATA (0x1 << 0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define CSTAS_MASK 0x3fffffff 548c2ecf20Sopenharmony_ci#define CSTAS_SAMP_FREQ_MASK (0xF << 24) 558c2ecf20Sopenharmony_ci#define CSTAS_SAMP_FREQ_44 (0x0 << 24) 568c2ecf20Sopenharmony_ci#define CSTAS_SAMP_FREQ_48 (0x2 << 24) 578c2ecf20Sopenharmony_ci#define CSTAS_SAMP_FREQ_32 (0x3 << 24) 588c2ecf20Sopenharmony_ci#define CSTAS_SAMP_FREQ_96 (0xA << 24) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define CSTAS_CATEGORY_MASK (0xFF << 8) 618c2ecf20Sopenharmony_ci#define CSTAS_CATEGORY_CODE_CDP (0x01 << 8) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define CSTAS_NO_COPYRIGHT (0x1 << 2) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/** 668c2ecf20Sopenharmony_ci * struct samsung_spdif_info - Samsung S/PDIF Controller information 678c2ecf20Sopenharmony_ci * @lock: Spin lock for S/PDIF. 688c2ecf20Sopenharmony_ci * @dev: The parent device passed to use from the probe. 698c2ecf20Sopenharmony_ci * @regs: The pointer to the device register block. 708c2ecf20Sopenharmony_ci * @clk_rate: Current clock rate for calcurate ratio. 718c2ecf20Sopenharmony_ci * @pclk: The peri-clock pointer for spdif master operation. 728c2ecf20Sopenharmony_ci * @sclk: The source clock pointer for making sync signals. 738c2ecf20Sopenharmony_ci * @saved_clkcon: Backup clkcon reg. in suspend. 748c2ecf20Sopenharmony_ci * @saved_con: Backup con reg. in suspend. 758c2ecf20Sopenharmony_ci * @saved_cstas: Backup cstas reg. in suspend. 768c2ecf20Sopenharmony_ci * @dma_playback: DMA information for playback channel. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistruct samsung_spdif_info { 798c2ecf20Sopenharmony_ci spinlock_t lock; 808c2ecf20Sopenharmony_ci struct device *dev; 818c2ecf20Sopenharmony_ci void __iomem *regs; 828c2ecf20Sopenharmony_ci unsigned long clk_rate; 838c2ecf20Sopenharmony_ci struct clk *pclk; 848c2ecf20Sopenharmony_ci struct clk *sclk; 858c2ecf20Sopenharmony_ci u32 saved_clkcon; 868c2ecf20Sopenharmony_ci u32 saved_con; 878c2ecf20Sopenharmony_ci u32 saved_cstas; 888c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_playback; 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic struct snd_dmaengine_dai_dma_data spdif_stereo_out; 928c2ecf20Sopenharmony_cistatic struct samsung_spdif_info spdif_info; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline struct samsung_spdif_info 958c2ecf20Sopenharmony_ci*component_to_info(struct snd_soc_component *component) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return snd_soc_component_get_drvdata(component); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic inline struct samsung_spdif_info *to_info(struct snd_soc_dai *cpu_dai) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci return snd_soc_dai_get_drvdata(cpu_dai); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void spdif_snd_txctrl(struct samsung_spdif_info *spdif, int on) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci void __iomem *regs = spdif->regs; 1088c2ecf20Sopenharmony_ci u32 clkcon; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci clkcon = readl(regs + CLKCON) & CLKCTL_MASK; 1138c2ecf20Sopenharmony_ci if (on) 1148c2ecf20Sopenharmony_ci writel(clkcon | CLKCTL_PWR_ON, regs + CLKCON); 1158c2ecf20Sopenharmony_ci else 1168c2ecf20Sopenharmony_ci writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int spdif_set_sysclk(struct snd_soc_dai *cpu_dai, 1208c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct samsung_spdif_info *spdif = to_info(cpu_dai); 1238c2ecf20Sopenharmony_ci u32 clkcon; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci clkcon = readl(spdif->regs + CLKCON); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (clk_id == SND_SOC_SPDIF_INT_MCLK) 1308c2ecf20Sopenharmony_ci clkcon &= ~CLKCTL_MCLK_EXT; 1318c2ecf20Sopenharmony_ci else 1328c2ecf20Sopenharmony_ci clkcon |= CLKCTL_MCLK_EXT; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci writel(clkcon, spdif->regs + CLKCON); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci spdif->clk_rate = freq; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int spdif_trigger(struct snd_pcm_substream *substream, int cmd, 1428c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1458c2ecf20Sopenharmony_ci struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); 1468c2ecf20Sopenharmony_ci unsigned long flags; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci switch (cmd) { 1518c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 1528c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 1538c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1548c2ecf20Sopenharmony_ci spin_lock_irqsave(&spdif->lock, flags); 1558c2ecf20Sopenharmony_ci spdif_snd_txctrl(spdif, 1); 1568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&spdif->lock, flags); 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 1598c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 1608c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 1618c2ecf20Sopenharmony_ci spin_lock_irqsave(&spdif->lock, flags); 1628c2ecf20Sopenharmony_ci spdif_snd_txctrl(spdif, 0); 1638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&spdif->lock, flags); 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci default: 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int spdif_sysclk_ratios[] = { 1738c2ecf20Sopenharmony_ci 512, 384, 256, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int spdif_hw_params(struct snd_pcm_substream *substream, 1778c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1788c2ecf20Sopenharmony_ci struct snd_soc_dai *socdai) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1818c2ecf20Sopenharmony_ci struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); 1828c2ecf20Sopenharmony_ci void __iomem *regs = spdif->regs; 1838c2ecf20Sopenharmony_ci struct snd_dmaengine_dai_dma_data *dma_data; 1848c2ecf20Sopenharmony_ci u32 con, clkcon, cstas; 1858c2ecf20Sopenharmony_ci unsigned long flags; 1868c2ecf20Sopenharmony_ci int i, ratio; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1918c2ecf20Sopenharmony_ci dma_data = spdif->dma_playback; 1928c2ecf20Sopenharmony_ci else { 1938c2ecf20Sopenharmony_ci dev_err(spdif->dev, "Capture is not supported\n"); 1948c2ecf20Sopenharmony_ci return -EINVAL; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci snd_soc_dai_set_dma_data(asoc_rtd_to_cpu(rtd, 0), substream, dma_data); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci spin_lock_irqsave(&spdif->lock, flags); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci con = readl(regs + CON) & CON_MASK; 2028c2ecf20Sopenharmony_ci cstas = readl(regs + CSTAS) & CSTAS_MASK; 2038c2ecf20Sopenharmony_ci clkcon = readl(regs + CLKCON) & CLKCTL_MASK; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci con &= ~CON_FIFO_TH_MASK; 2068c2ecf20Sopenharmony_ci con |= (0x7 << CON_FIFO_TH_SHIFT); 2078c2ecf20Sopenharmony_ci con |= CON_USERDATA_23RDBIT; 2088c2ecf20Sopenharmony_ci con |= CON_PCM_DATA; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci con &= ~CON_PCM_MASK; 2118c2ecf20Sopenharmony_ci switch (params_width(params)) { 2128c2ecf20Sopenharmony_ci case 16: 2138c2ecf20Sopenharmony_ci con |= CON_PCM_16BIT; 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci default: 2168c2ecf20Sopenharmony_ci dev_err(spdif->dev, "Unsupported data size.\n"); 2178c2ecf20Sopenharmony_ci goto err; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ratio = spdif->clk_rate / params_rate(params); 2218c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(spdif_sysclk_ratios); i++) 2228c2ecf20Sopenharmony_ci if (ratio == spdif_sysclk_ratios[i]) 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(spdif_sysclk_ratios)) { 2258c2ecf20Sopenharmony_ci dev_err(spdif->dev, "Invalid clock ratio %ld/%d\n", 2268c2ecf20Sopenharmony_ci spdif->clk_rate, params_rate(params)); 2278c2ecf20Sopenharmony_ci goto err; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci con &= ~CON_MCLKDIV_MASK; 2318c2ecf20Sopenharmony_ci switch (ratio) { 2328c2ecf20Sopenharmony_ci case 256: 2338c2ecf20Sopenharmony_ci con |= CON_MCLKDIV_256FS; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci case 384: 2368c2ecf20Sopenharmony_ci con |= CON_MCLKDIV_384FS; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci case 512: 2398c2ecf20Sopenharmony_ci con |= CON_MCLKDIV_512FS; 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci cstas &= ~CSTAS_SAMP_FREQ_MASK; 2448c2ecf20Sopenharmony_ci switch (params_rate(params)) { 2458c2ecf20Sopenharmony_ci case 44100: 2468c2ecf20Sopenharmony_ci cstas |= CSTAS_SAMP_FREQ_44; 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci case 48000: 2498c2ecf20Sopenharmony_ci cstas |= CSTAS_SAMP_FREQ_48; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci case 32000: 2528c2ecf20Sopenharmony_ci cstas |= CSTAS_SAMP_FREQ_32; 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci case 96000: 2558c2ecf20Sopenharmony_ci cstas |= CSTAS_SAMP_FREQ_96; 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci default: 2588c2ecf20Sopenharmony_ci dev_err(spdif->dev, "Invalid sampling rate %d\n", 2598c2ecf20Sopenharmony_ci params_rate(params)); 2608c2ecf20Sopenharmony_ci goto err; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci cstas &= ~CSTAS_CATEGORY_MASK; 2648c2ecf20Sopenharmony_ci cstas |= CSTAS_CATEGORY_CODE_CDP; 2658c2ecf20Sopenharmony_ci cstas |= CSTAS_NO_COPYRIGHT; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci writel(con, regs + CON); 2688c2ecf20Sopenharmony_ci writel(cstas, regs + CSTAS); 2698c2ecf20Sopenharmony_ci writel(clkcon, regs + CLKCON); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&spdif->lock, flags); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_cierr: 2758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&spdif->lock, flags); 2768c2ecf20Sopenharmony_ci return -EINVAL; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic void spdif_shutdown(struct snd_pcm_substream *substream, 2808c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 2838c2ecf20Sopenharmony_ci struct samsung_spdif_info *spdif = to_info(asoc_rtd_to_cpu(rtd, 0)); 2848c2ecf20Sopenharmony_ci void __iomem *regs = spdif->regs; 2858c2ecf20Sopenharmony_ci u32 con, clkcon; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci con = readl(regs + CON) & CON_MASK; 2908c2ecf20Sopenharmony_ci clkcon = readl(regs + CLKCON) & CLKCTL_MASK; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci writel(con | CON_SW_RESET, regs + CON); 2938c2ecf20Sopenharmony_ci cpu_relax(); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci writel(clkcon & ~CLKCTL_PWR_ON, regs + CLKCON); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2998c2ecf20Sopenharmony_cistatic int spdif_suspend(struct snd_soc_component *component) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct samsung_spdif_info *spdif = component_to_info(component); 3028c2ecf20Sopenharmony_ci u32 con = spdif->saved_con; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci spdif->saved_clkcon = readl(spdif->regs + CLKCON) & CLKCTL_MASK; 3078c2ecf20Sopenharmony_ci spdif->saved_con = readl(spdif->regs + CON) & CON_MASK; 3088c2ecf20Sopenharmony_ci spdif->saved_cstas = readl(spdif->regs + CSTAS) & CSTAS_MASK; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci writel(con | CON_SW_RESET, spdif->regs + CON); 3118c2ecf20Sopenharmony_ci cpu_relax(); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int spdif_resume(struct snd_soc_component *component) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct samsung_spdif_info *spdif = component_to_info(component); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci dev_dbg(spdif->dev, "Entered %s\n", __func__); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci writel(spdif->saved_clkcon, spdif->regs + CLKCON); 3238c2ecf20Sopenharmony_ci writel(spdif->saved_con, spdif->regs + CON); 3248c2ecf20Sopenharmony_ci writel(spdif->saved_cstas, spdif->regs + CSTAS); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci#else 3298c2ecf20Sopenharmony_ci#define spdif_suspend NULL 3308c2ecf20Sopenharmony_ci#define spdif_resume NULL 3318c2ecf20Sopenharmony_ci#endif 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops spdif_dai_ops = { 3348c2ecf20Sopenharmony_ci .set_sysclk = spdif_set_sysclk, 3358c2ecf20Sopenharmony_ci .trigger = spdif_trigger, 3368c2ecf20Sopenharmony_ci .hw_params = spdif_hw_params, 3378c2ecf20Sopenharmony_ci .shutdown = spdif_shutdown, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver samsung_spdif_dai = { 3418c2ecf20Sopenharmony_ci .name = "samsung-spdif", 3428c2ecf20Sopenharmony_ci .playback = { 3438c2ecf20Sopenharmony_ci .stream_name = "S/PDIF Playback", 3448c2ecf20Sopenharmony_ci .channels_min = 2, 3458c2ecf20Sopenharmony_ci .channels_max = 2, 3468c2ecf20Sopenharmony_ci .rates = (SNDRV_PCM_RATE_32000 | 3478c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_44100 | 3488c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_48000 | 3498c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000), 3508c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, }, 3518c2ecf20Sopenharmony_ci .ops = &spdif_dai_ops, 3528c2ecf20Sopenharmony_ci}; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver samsung_spdif_component = { 3558c2ecf20Sopenharmony_ci .name = "samsung-spdif", 3568c2ecf20Sopenharmony_ci .suspend = spdif_suspend, 3578c2ecf20Sopenharmony_ci .resume = spdif_resume, 3588c2ecf20Sopenharmony_ci}; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int spdif_probe(struct platform_device *pdev) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct s3c_audio_pdata *spdif_pdata; 3638c2ecf20Sopenharmony_ci struct resource *mem_res; 3648c2ecf20Sopenharmony_ci struct samsung_spdif_info *spdif; 3658c2ecf20Sopenharmony_ci dma_filter_fn filter; 3668c2ecf20Sopenharmony_ci int ret; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci spdif_pdata = pdev->dev.platform_data; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Entered %s\n", __func__); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3738c2ecf20Sopenharmony_ci if (!mem_res) { 3748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to get register resource.\n"); 3758c2ecf20Sopenharmony_ci return -ENXIO; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (spdif_pdata && spdif_pdata->cfg_gpio 3798c2ecf20Sopenharmony_ci && spdif_pdata->cfg_gpio(pdev)) { 3808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to configure GPIO pins\n"); 3818c2ecf20Sopenharmony_ci return -EINVAL; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci spdif = &spdif_info; 3858c2ecf20Sopenharmony_ci spdif->dev = &pdev->dev; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci spin_lock_init(&spdif->lock); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); 3908c2ecf20Sopenharmony_ci if (IS_ERR(spdif->pclk)) { 3918c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get peri-clock\n"); 3928c2ecf20Sopenharmony_ci ret = -ENOENT; 3938c2ecf20Sopenharmony_ci goto err0; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci ret = clk_prepare_enable(spdif->pclk); 3968c2ecf20Sopenharmony_ci if (ret) 3978c2ecf20Sopenharmony_ci goto err0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); 4008c2ecf20Sopenharmony_ci if (IS_ERR(spdif->sclk)) { 4018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get internal source clock\n"); 4028c2ecf20Sopenharmony_ci ret = -ENOENT; 4038c2ecf20Sopenharmony_ci goto err1; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci ret = clk_prepare_enable(spdif->sclk); 4068c2ecf20Sopenharmony_ci if (ret) 4078c2ecf20Sopenharmony_ci goto err1; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* Request S/PDIF Register's memory region */ 4108c2ecf20Sopenharmony_ci if (!request_mem_region(mem_res->start, 4118c2ecf20Sopenharmony_ci resource_size(mem_res), "samsung-spdif")) { 4128c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to request register region\n"); 4138c2ecf20Sopenharmony_ci ret = -EBUSY; 4148c2ecf20Sopenharmony_ci goto err2; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci spdif->regs = ioremap(mem_res->start, 0x100); 4188c2ecf20Sopenharmony_ci if (spdif->regs == NULL) { 4198c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot ioremap registers\n"); 4208c2ecf20Sopenharmony_ci ret = -ENXIO; 4218c2ecf20Sopenharmony_ci goto err3; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci spdif_stereo_out.addr_width = 2; 4258c2ecf20Sopenharmony_ci spdif_stereo_out.addr = mem_res->start + DATA_OUTBUF; 4268c2ecf20Sopenharmony_ci filter = NULL; 4278c2ecf20Sopenharmony_ci if (spdif_pdata) { 4288c2ecf20Sopenharmony_ci spdif_stereo_out.filter_data = spdif_pdata->dma_playback; 4298c2ecf20Sopenharmony_ci filter = spdif_pdata->dma_filter; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci spdif->dma_playback = &spdif_stereo_out; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ret = samsung_asoc_dma_platform_register(&pdev->dev, filter, 4348c2ecf20Sopenharmony_ci NULL, NULL, NULL); 4358c2ecf20Sopenharmony_ci if (ret) { 4368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register DMA: %d\n", ret); 4378c2ecf20Sopenharmony_ci goto err4; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, spdif); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(&pdev->dev, 4438c2ecf20Sopenharmony_ci &samsung_spdif_component, &samsung_spdif_dai, 1); 4448c2ecf20Sopenharmony_ci if (ret != 0) { 4458c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "fail to register dai\n"); 4468c2ecf20Sopenharmony_ci goto err4; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_cierr4: 4518c2ecf20Sopenharmony_ci iounmap(spdif->regs); 4528c2ecf20Sopenharmony_cierr3: 4538c2ecf20Sopenharmony_ci release_mem_region(mem_res->start, resource_size(mem_res)); 4548c2ecf20Sopenharmony_cierr2: 4558c2ecf20Sopenharmony_ci clk_disable_unprepare(spdif->sclk); 4568c2ecf20Sopenharmony_cierr1: 4578c2ecf20Sopenharmony_ci clk_disable_unprepare(spdif->pclk); 4588c2ecf20Sopenharmony_cierr0: 4598c2ecf20Sopenharmony_ci return ret; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int spdif_remove(struct platform_device *pdev) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci struct samsung_spdif_info *spdif = &spdif_info; 4658c2ecf20Sopenharmony_ci struct resource *mem_res; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci iounmap(spdif->regs); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4708c2ecf20Sopenharmony_ci if (mem_res) 4718c2ecf20Sopenharmony_ci release_mem_region(mem_res->start, resource_size(mem_res)); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci clk_disable_unprepare(spdif->sclk); 4748c2ecf20Sopenharmony_ci clk_disable_unprepare(spdif->pclk); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic struct platform_driver samsung_spdif_driver = { 4808c2ecf20Sopenharmony_ci .probe = spdif_probe, 4818c2ecf20Sopenharmony_ci .remove = spdif_remove, 4828c2ecf20Sopenharmony_ci .driver = { 4838c2ecf20Sopenharmony_ci .name = "samsung-spdif", 4848c2ecf20Sopenharmony_ci }, 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cimodule_platform_driver(samsung_spdif_driver); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ciMODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>"); 4908c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung S/PDIF Controller Driver"); 4918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4928c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:samsung-spdif"); 493