s3c2412-i2s.c 5.6 KB
Newer Older
B
Ben Dooks 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* sound/soc/s3c24xx/s3c2412-i2s.c
 *
 * ALSA Soc Audio Layer - S3C2412 I2S driver
 *
 * Copyright (c) 2006 Wolfson Microelectronics PLC.
 *	Graeme Gregory graeme.gregory@wolfsonmicro.com
 *	linux@wolfsonmicro.com
 *
 * Copyright (c) 2007, 2004-2005 Simtec Electronics
 *	http://armlinux.simtec.co.uk/
 *	Ben Dooks <ben@simtec.co.uk>
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
23
#include <linux/gpio.h>
B
Ben Dooks 已提交
24 25
#include <linux/clk.h>
#include <linux/kernel.h>
B
Ben Dooks 已提交
26
#include <linux/io.h>
B
Ben Dooks 已提交
27 28 29 30 31 32

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
33
#include <mach/hardware.h>
B
Ben Dooks 已提交
34

B
Ben Dooks 已提交
35
#include <plat/regs-s3c2412-iis.h>
B
Ben Dooks 已提交
36

37
#include <mach/regs-gpio.h>
38
#include <mach/dma.h>
B
Ben Dooks 已提交
39

40
#include "s3c-dma.h"
B
Ben Dooks 已提交
41 42 43 44 45 46 47 48 49 50 51 52
#include "s3c2412-i2s.h"

#define S3C2412_I2S_DEBUG 0

static struct s3c2410_dma_client s3c2412_dma_client_out = {
	.name		= "I2S PCM Stereo out"
};

static struct s3c2410_dma_client s3c2412_dma_client_in = {
	.name		= "I2S PCM Stereo in"
};

53
static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = {
B
Ben Dooks 已提交
54 55 56 57 58 59
	.client		= &s3c2412_dma_client_out,
	.channel	= DMACH_I2S_OUT,
	.dma_addr	= S3C2410_PA_IIS + S3C2412_IISTXD,
	.dma_size	= 4,
};

60
static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
B
Ben Dooks 已提交
61 62 63 64 65 66
	.client		= &s3c2412_dma_client_in,
	.channel	= DMACH_I2S_IN,
	.dma_addr	= S3C2410_PA_IIS + S3C2412_IISRXD,
	.dma_size	= 4,
};

67
static struct s3c_i2sv2_info s3c2412_i2s;
B
Ben Dooks 已提交
68 69 70 71

/*
 * Set S3C2412 Clock source
 */
72
static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
B
Ben Dooks 已提交
73 74 75 76
				  int clk_id, unsigned int freq, int dir)
{
	u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);

77
	pr_debug("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id,
B
Ben Dooks 已提交
78 79 80 81
	    freq, dir);

	switch (clk_id) {
	case S3C2412_CLKSRC_PCLK:
82
		s3c2412_i2s.master = 1;
B
Ben Dooks 已提交
83 84 85 86
		iismod &= ~S3C2412_IISMOD_MASTER_MASK;
		iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
		break;
	case S3C2412_CLKSRC_I2SCLK:
87
		s3c2412_i2s.master = 0;
B
Ben Dooks 已提交
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
		iismod &= ~S3C2412_IISMOD_MASTER_MASK;
		iismod |= S3C2412_IISMOD_MASTER_EXTERNAL;
		break;
	default:
		return -EINVAL;
	}

	writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
	return 0;
}


struct clk *s3c2412_get_iisclk(void)
{
	return s3c2412_i2s.iis_clk;
}
EXPORT_SYMBOL_GPL(s3c2412_get_iisclk);

106 107 108 109
static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
{
	return cpu_dai->private_data;
}
B
Ben Dooks 已提交
110

111
static int s3c2412_i2s_probe(struct platform_device *pdev,
112
			     struct snd_soc_dai *dai)
B
Ben Dooks 已提交
113
{
114
	int ret;
B
Ben Dooks 已提交
115

116
	pr_debug("Entered %s\n", __func__);
B
Ben Dooks 已提交
117

118 119 120
	ret = s3c_i2sv2_probe(pdev, dai, &s3c2412_i2s, S3C2410_PA_IIS);
	if (ret)
		return ret;
B
Ben Dooks 已提交
121

122 123
	s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in;
	s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
B
Ben Dooks 已提交
124 125 126

	s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
	if (s3c2412_i2s.iis_cclk == NULL) {
127
		pr_err("failed to get i2sclk clock\n");
B
Ben Dooks 已提交
128 129 130 131
		iounmap(s3c2412_i2s.regs);
		return -ENODEV;
	}

132
	/* Set MPLL as the source for IIS CLK */
B
Ben Dooks 已提交
133

134
	clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
B
Ben Dooks 已提交
135 136
	clk_enable(s3c2412_i2s.iis_cclk);

137
	s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
B
Ben Dooks 已提交
138 139 140 141 142 143 144 145

	/* Configure the I2S pins in correct mode */
	s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
	s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
	s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
	s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
	s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);

146 147 148
	return 0;
}

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
				 struct snd_pcm_hw_params *params,
				 struct snd_soc_dai *cpu_dai)
{
	struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
	u32 iismod;

	pr_debug("Entered %s\n", __func__);

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
		cpu_dai->dma_data = i2s->dma_playback;
	else
		cpu_dai->dma_data = i2s->dma_capture;

	iismod = readl(i2s->regs + S3C2412_IISMOD);
	pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S8:
		iismod |= S3C2412_IISMOD_8BIT;
		break;
	case SNDRV_PCM_FORMAT_S16_LE:
		iismod &= ~S3C2412_IISMOD_8BIT;
		break;
	}

	writel(iismod, i2s->regs + S3C2412_IISMOD);
	pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);

	return 0;
}

B
Ben Dooks 已提交
181 182 183 184 185
#define S3C2412_I2S_RATES \
	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
	SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)

186 187
static struct snd_soc_dai_ops s3c2412_i2s_dai_ops = {
	.set_sysclk	= s3c2412_i2s_set_sysclk,
188
	.hw_params	= s3c2412_i2s_hw_params,
189 190
};

191
struct snd_soc_dai s3c2412_i2s_dai = {
192 193 194
	.name		= "s3c2412-i2s",
	.id		= 0,
	.probe		= s3c2412_i2s_probe,
B
Ben Dooks 已提交
195 196 197 198 199 200 201 202 203 204 205 206
	.playback = {
		.channels_min	= 2,
		.channels_max	= 2,
		.rates		= S3C2412_I2S_RATES,
		.formats	= SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
	},
	.capture = {
		.channels_min	= 2,
		.channels_max	= 2,
		.rates		= S3C2412_I2S_RATES,
		.formats	= SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
	},
207
	.ops = &s3c2412_i2s_dai_ops,
B
Ben Dooks 已提交
208 209 210
};
EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);

211
static int __init s3c2412_i2s_init(void)
M
Mark Brown 已提交
212
{
213
	return  s3c_i2sv2_register_dai(&s3c2412_i2s_dai);
M
Mark Brown 已提交
214 215 216 217 218 219 220 221 222
}
module_init(s3c2412_i2s_init);

static void __exit s3c2412_i2s_exit(void)
{
	snd_soc_unregister_dai(&s3c2412_i2s_dai);
}
module_exit(s3c2412_i2s_exit);

B
Ben Dooks 已提交
223 224 225 226
/* Module information */
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
MODULE_LICENSE("GPL");