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