s3c2412-i2s.c 4.7 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 23 24
/* 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>
#include <linux/clk.h>
#include <linux/kernel.h>
B
Ben Dooks 已提交
25
#include <linux/io.h>
B
Ben Dooks 已提交
26 27 28 29 30 31

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

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

36
#include <plat/audio.h>
37
#include <mach/regs-gpio.h>
38
#include <mach/dma.h>
B
Ben Dooks 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

#include "s3c24xx-pcm.h"
#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"
};

static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_out = {
	.client		= &s3c2412_dma_client_out,
	.channel	= DMACH_I2S_OUT,
	.dma_addr	= S3C2410_PA_IIS + S3C2412_IISTXD,
	.dma_size	= 4,
};

static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = {
	.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 106
		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);


107
static int s3c2412_i2s_probe(struct platform_device *pdev,
108
			     struct snd_soc_dai *dai)
B
Ben Dooks 已提交
109
{
110
	int ret;
B
Ben Dooks 已提交
111

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

114 115 116
	ret = s3c_i2sv2_probe(pdev, dai, &s3c2412_i2s, S3C2410_PA_IIS);
	if (ret)
		return ret;
B
Ben Dooks 已提交
117

118 119
	s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in;
	s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
B
Ben Dooks 已提交
120 121 122

	s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
	if (s3c2412_i2s.iis_cclk == NULL) {
123
		pr_debug("failed to get i2sclk clock\n");
B
Ben Dooks 已提交
124 125 126 127
		iounmap(s3c2412_i2s.regs);
		return -ENODEV;
	}

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

130
	clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
B
Ben Dooks 已提交
131 132
	clk_enable(s3c2412_i2s.iis_cclk);

133
	s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
B
Ben Dooks 已提交
134 135 136 137 138 139 140 141

	/* 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);

142 143 144
	return 0;
}

B
Ben Dooks 已提交
145 146 147 148 149
#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)

150 151 152 153
static struct snd_soc_dai_ops s3c2412_i2s_dai_ops = {
	.set_sysclk	= s3c2412_i2s_set_sysclk,
};

154
struct snd_soc_dai s3c2412_i2s_dai = {
155 156 157
	.name		= "s3c2412-i2s",
	.id		= 0,
	.probe		= s3c2412_i2s_probe,
B
Ben Dooks 已提交
158 159 160 161 162 163 164 165 166 167 168 169
	.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,
	},
170
	.ops = &s3c2412_i2s_dai_ops,
B
Ben Dooks 已提交
171 172 173
};
EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);

174
static int __init s3c2412_i2s_init(void)
M
Mark Brown 已提交
175
{
176
	return  s3c_i2sv2_register_dai(&s3c2412_i2s_dai);
M
Mark Brown 已提交
177 178 179 180 181 182 183 184 185
}
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 已提交
186 187 188 189
/* Module information */
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
MODULE_LICENSE("GPL");