diff options
Diffstat (limited to 'sound/soc/s3c24xx')
-rw-r--r-- | sound/soc/s3c24xx/Kconfig | 12 | ||||
-rw-r--r-- | sound/soc/s3c24xx/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/s3c24xx/jive_wm8750.c | 7 | ||||
-rw-r--r-- | sound/soc/s3c24xx/neo1973_gta02_wm8753.c | 8 | ||||
-rw-r--r-- | sound/soc/s3c24xx/regs-i2s-v2.h | 115 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c-i2s-v2.c | 205 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c-i2s-v2.h | 15 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c2412-i2s.c | 77 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c2412-i2s.h | 6 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c64xx-i2s-v4.c | 209 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c64xx-i2s.c | 69 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c64xx-i2s.h | 18 | ||||
-rw-r--r-- | sound/soc/s3c24xx/smdk64xx_wm8580.c | 6 |
13 files changed, 533 insertions, 216 deletions
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index 15fe57e5a232..2a7cc222d098 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -24,6 +24,11 @@ config SND_S3C64XX_SOC_I2S select SND_S3C_I2SV2_SOC select S3C64XX_DMA +config SND_S3C64XX_SOC_I2S_V4 + tristate + select SND_S3C_I2SV2_SOC + select S3C64XX_DMA + config SND_S3C_SOC_PCM tristate @@ -59,12 +64,11 @@ config SND_S3C24XX_SOC_JIVE_WM8750 config SND_S3C64XX_SOC_WM8580 tristate "SoC I2S Audio support for WM8580 on SMDK64XX" - depends on SND_S3C24XX_SOC && (MACH_SMDK6400 || MACH_SMDK6410) - depends on BROKEN + depends on SND_S3C24XX_SOC && MACH_SMDK6410 select SND_SOC_WM8580 - select SND_S3C64XX_SOC_I2S + select SND_S3C64XX_SOC_I2S_V4 help - Sat Y if you want to add support for SoC audio on the SMDK64XX. + Say Y if you want to add support for SoC audio on the SMDK6410. config SND_S3C24XX_SOC_SMDK2443_WM9710 tristate "SoC AC97 Audio support for SMDK2443 - WM9710" diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index df071a376fa2..81d8dc503f87 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -4,6 +4,7 @@ snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o snd-soc-s3c-ac97-objs := s3c-ac97.o +snd-soc-s3c64xx-i2s-v4-objs := s3c64xx-i2s-v4.o snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o snd-soc-s3c-pcm-objs := s3c-pcm.o @@ -12,6 +13,7 @@ obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o obj-$(CONFIG_SND_S3C_SOC_AC97) += snd-soc-s3c-ac97.o obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o +obj-$(CONFIG_SND_S3C64XX_SOC_I2S_V4) += snd-soc-s3c64xx-i2s-v4.o obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o obj-$(CONFIG_SND_S3C_SOC_PCM) += snd-soc-s3c-pcm.o diff --git a/sound/soc/s3c24xx/jive_wm8750.c b/sound/soc/s3c24xx/jive_wm8750.c index 59dc2c6b56d9..8c108b121c10 100644 --- a/sound/soc/s3c24xx/jive_wm8750.c +++ b/sound/soc/s3c24xx/jive_wm8750.c @@ -70,7 +70,7 @@ static int jive_hw_params(struct snd_pcm_substream *substream, } s3c_i2sv2_iis_calc_rate(&div, NULL, params_rate(params), - s3c2412_get_iisclk()); + s3c_i2sv2_get_clock(cpu_dai)); /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | @@ -152,15 +152,10 @@ static struct snd_soc_card snd_soc_machine_jive = { .num_links = 1, }; -/* jive audio private data */ -static struct wm8750_setup_data jive_wm8750_setup = { -}; - /* jive audio subsystem */ static struct snd_soc_device jive_snd_devdata = { .card = &snd_soc_machine_jive, .codec_dev = &soc_codec_dev_wm8750, - .codec_data = &jive_wm8750_setup, }; static struct platform_device *jive_snd_device; diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c index dea83d30a5c9..209c25994c7e 100644 --- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c @@ -362,6 +362,14 @@ static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec) snd_soc_dapm_disable_pin(codec, "Handset Mic"); snd_soc_dapm_disable_pin(codec, "Handset Spk"); + /* allow audio paths from the GSM modem to run during suspend */ + snd_soc_dapm_ignore_suspend(codec, "Stereo Out"); + snd_soc_dapm_ignore_suspend(codec, "GSM Line Out"); + snd_soc_dapm_ignore_suspend(codec, "GSM Line In"); + snd_soc_dapm_ignore_suspend(codec, "Headset Mic"); + snd_soc_dapm_ignore_suspend(codec, "Handset Mic"); + snd_soc_dapm_ignore_suspend(codec, "Handset Spk"); + snd_soc_dapm_sync(codec); return 0; diff --git a/sound/soc/s3c24xx/regs-i2s-v2.h b/sound/soc/s3c24xx/regs-i2s-v2.h new file mode 100644 index 000000000000..5e5e5680580b --- /dev/null +++ b/sound/soc/s3c24xx/regs-i2s-v2.h @@ -0,0 +1,115 @@ +/* linux/include/asm-arm/plat-s3c24xx/regs-s3c2412-iis.h + * + * Copyright 2007 Simtec Electronics <linux@simtec.co.uk> + * http://armlinux.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 version 2 as + * published by the Free Software Foundation. + * + * S3C2412 IIS register definition +*/ + +#ifndef __ASM_ARCH_REGS_S3C2412_IIS_H +#define __ASM_ARCH_REGS_S3C2412_IIS_H + +#define S3C2412_IISCON (0x00) +#define S3C2412_IISMOD (0x04) +#define S3C2412_IISFIC (0x08) +#define S3C2412_IISPSR (0x0C) +#define S3C2412_IISTXD (0x10) +#define S3C2412_IISRXD (0x14) + +#define S5PC1XX_IISFICS 0x18 +#define S5PC1XX_IISTXDS 0x1C + +#define S5PC1XX_IISCON_SW_RST (1 << 31) +#define S5PC1XX_IISCON_FRXOFSTATUS (1 << 26) +#define S5PC1XX_IISCON_FRXORINTEN (1 << 25) +#define S5PC1XX_IISCON_FTXSURSTAT (1 << 24) +#define S5PC1XX_IISCON_FTXSURINTEN (1 << 23) +#define S5PC1XX_IISCON_TXSDMAPAUSE (1 << 20) +#define S5PC1XX_IISCON_TXSDMACTIVE (1 << 18) + +#define S3C64XX_IISCON_FTXURSTATUS (1 << 17) +#define S3C64XX_IISCON_FTXURINTEN (1 << 16) +#define S3C64XX_IISCON_TXFIFO2_EMPTY (1 << 15) +#define S3C64XX_IISCON_TXFIFO1_EMPTY (1 << 14) +#define S3C64XX_IISCON_TXFIFO2_FULL (1 << 13) +#define S3C64XX_IISCON_TXFIFO1_FULL (1 << 12) + +#define S3C2412_IISCON_LRINDEX (1 << 11) +#define S3C2412_IISCON_TXFIFO_EMPTY (1 << 10) +#define S3C2412_IISCON_RXFIFO_EMPTY (1 << 9) +#define S3C2412_IISCON_TXFIFO_FULL (1 << 8) +#define S3C2412_IISCON_RXFIFO_FULL (1 << 7) +#define S3C2412_IISCON_TXDMA_PAUSE (1 << 6) +#define S3C2412_IISCON_RXDMA_PAUSE (1 << 5) +#define S3C2412_IISCON_TXCH_PAUSE (1 << 4) +#define S3C2412_IISCON_RXCH_PAUSE (1 << 3) +#define S3C2412_IISCON_TXDMA_ACTIVE (1 << 2) +#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1) +#define S3C2412_IISCON_IIS_ACTIVE (1 << 0) + +#define S5PC1XX_IISMOD_OPCLK_CDCLK_OUT (0 << 30) +#define S5PC1XX_IISMOD_OPCLK_CDCLK_IN (1 << 30) +#define S5PC1XX_IISMOD_OPCLK_BCLK_OUT (2 << 30) +#define S5PC1XX_IISMOD_OPCLK_PCLK (3 << 30) +#define S5PC1XX_IISMOD_OPCLK_MASK (3 << 30) +#define S5PC1XX_IISMOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */ +#define S5PC1XX_IISMOD_BLCS_MASK 0x3 +#define S5PC1XX_IISMOD_BLCS_SHIFT 26 +#define S5PC1XX_IISMOD_BLCP_MASK 0x3 +#define S5PC1XX_IISMOD_BLCP_SHIFT 24 + +#define S3C64XX_IISMOD_C2DD_HHALF (1 << 21) /* Discard Higher-half */ +#define S3C64XX_IISMOD_C2DD_LHALF (1 << 20) /* Discard Lower-half */ +#define S3C64XX_IISMOD_C1DD_HHALF (1 << 19) +#define S3C64XX_IISMOD_C1DD_LHALF (1 << 18) +#define S3C64XX_IISMOD_DC2_EN (1 << 17) +#define S3C64XX_IISMOD_DC1_EN (1 << 16) +#define S3C64XX_IISMOD_BLC_16BIT (0 << 13) +#define S3C64XX_IISMOD_BLC_8BIT (1 << 13) +#define S3C64XX_IISMOD_BLC_24BIT (2 << 13) +#define S3C64XX_IISMOD_BLC_MASK (3 << 13) + +#define S3C2412_IISMOD_IMS_SYSMUX (1 << 10) +#define S3C2412_IISMOD_SLAVE (1 << 11) +#define S3C2412_IISMOD_MODE_TXONLY (0 << 8) +#define S3C2412_IISMOD_MODE_RXONLY (1 << 8) +#define S3C2412_IISMOD_MODE_TXRX (2 << 8) +#define S3C2412_IISMOD_MODE_MASK (3 << 8) +#define S3C2412_IISMOD_LR_LLOW (0 << 7) +#define S3C2412_IISMOD_LR_RLOW (1 << 7) +#define S3C2412_IISMOD_SDF_IIS (0 << 5) +#define S3C2412_IISMOD_SDF_MSB (1 << 5) +#define S3C2412_IISMOD_SDF_LSB (2 << 5) +#define S3C2412_IISMOD_SDF_MASK (3 << 5) +#define S3C2412_IISMOD_RCLK_256FS (0 << 3) +#define S3C2412_IISMOD_RCLK_512FS (1 << 3) +#define S3C2412_IISMOD_RCLK_384FS (2 << 3) +#define S3C2412_IISMOD_RCLK_768FS (3 << 3) +#define S3C2412_IISMOD_RCLK_MASK (3 << 3) +#define S3C2412_IISMOD_BCLK_32FS (0 << 1) +#define S3C2412_IISMOD_BCLK_48FS (1 << 1) +#define S3C2412_IISMOD_BCLK_16FS (2 << 1) +#define S3C2412_IISMOD_BCLK_24FS (3 << 1) +#define S3C2412_IISMOD_BCLK_MASK (3 << 1) +#define S3C2412_IISMOD_8BIT (1 << 0) + +#define S3C64XX_IISMOD_CDCLKCON (1 << 12) + +#define S3C2412_IISPSR_PSREN (1 << 15) + +#define S3C64XX_IISFIC_TX2COUNT(x) (((x) >> 24) & 0xf) +#define S3C64XX_IISFIC_TX1COUNT(x) (((x) >> 16) & 0xf) + +#define S3C2412_IISFIC_TXFLUSH (1 << 15) +#define S3C2412_IISFIC_RXFLUSH (1 << 7) +#define S3C2412_IISFIC_TXCOUNT(x) (((x) >> 8) & 0xf) +#define S3C2412_IISFIC_RXCOUNT(x) (((x) >> 0) & 0xf) + +#define S5PC1XX_IISFICS_TXFLUSH (1 << 15) +#define S5PC1XX_IISFICS_TXCOUNT(x) (((x) >> 8) & 0x7f) + +#endif /* __ASM_ARCH_REGS_S3C2412_IIS_H */ diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index 88515946b6c0..13311c8cf965 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -16,24 +16,17 @@ * 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> #include <linux/io.h> -#include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> -#include <sound/initval.h> #include <sound/soc.h> -#include <plat/regs-s3c2412-iis.h> - #include <mach/dma.h> +#include "regs-i2s-v2.h" #include "s3c-i2s-v2.h" #include "s3c-dma.h" @@ -272,35 +265,14 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, iismod = readl(i2s->regs + S3C2412_IISMOD); pr_debug("hw_params r: IISMOD: %x \n", iismod); -#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) -#define IISMOD_MASTER_MASK S3C2412_IISMOD_MASTER_MASK -#define IISMOD_SLAVE S3C2412_IISMOD_SLAVE -#define IISMOD_MASTER S3C2412_IISMOD_MASTER_INTERNAL -#endif - -#if defined(CONFIG_PLAT_S3C64XX) -/* From Rev1.1 datasheet, we have two master and two slave modes: - * IMS[11:10]: - * 00 = master mode, fed from PCLK - * 01 = master mode, fed from CLKAUDIO - * 10 = slave mode, using PCLK - * 11 = slave mode, using I2SCLK - */ -#define IISMOD_MASTER_MASK (1 << 11) -#define IISMOD_SLAVE (1 << 11) -#define IISMOD_MASTER (0 << 11) -#endif - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: i2s->master = 0; - iismod &= ~IISMOD_MASTER_MASK; - iismod |= IISMOD_SLAVE; + iismod |= S3C2412_IISMOD_SLAVE; break; case SND_SOC_DAIFMT_CBS_CFS: i2s->master = 1; - iismod &= ~IISMOD_MASTER_MASK; - iismod |= IISMOD_MASTER; + iismod &= ~S3C2412_IISMOD_SLAVE; break; default: pr_err("unknwon master/slave format\n"); @@ -332,7 +304,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, return 0; } -static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, +static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *socdai) { @@ -355,37 +327,67 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, iismod = readl(i2s->regs + S3C2412_IISMOD); pr_debug("%s: r: IISMOD: %x\n", __func__, iismod); -#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) + iismod &= ~S3C64XX_IISMOD_BLC_MASK; + /* Sample size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: - iismod |= S3C2412_IISMOD_8BIT; + iismod |= S3C64XX_IISMOD_BLC_8BIT; break; case SNDRV_PCM_FORMAT_S16_LE: - iismod &= ~S3C2412_IISMOD_8BIT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iismod |= S3C64XX_IISMOD_BLC_24BIT; break; } -#endif -#ifdef CONFIG_PLAT_S3C64XX - iismod &= ~(S3C64XX_IISMOD_BLC_MASK | S3C2412_IISMOD_BCLK_MASK); - /* Sample size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: - /* 8 bit sample, 16fs BCLK */ - iismod |= (S3C64XX_IISMOD_BLC_8BIT | S3C2412_IISMOD_BCLK_16FS); + writel(iismod, i2s->regs + S3C2412_IISMOD); + pr_debug("%s: w: IISMOD: %x\n", __func__, iismod); + + return 0; +} + +static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct s3c_i2sv2_info *i2s = to_info(cpu_dai); + u32 iismod = readl(i2s->regs + S3C2412_IISMOD); + + pr_debug("Entered %s\n", __func__); + pr_debug("%s r: IISMOD: %x\n", __func__, iismod); + + switch (clk_id) { + case S3C_I2SV2_CLKSRC_PCLK: + iismod &= ~S3C2412_IISMOD_IMS_SYSMUX; break; - case SNDRV_PCM_FORMAT_S16_LE: - /* 16 bit sample, 32fs BCLK */ + + case S3C_I2SV2_CLKSRC_AUDIOBUS: + iismod |= S3C2412_IISMOD_IMS_SYSMUX; break; - case SNDRV_PCM_FORMAT_S24_LE: - /* 24 bit sample, 48fs BCLK */ - iismod |= (S3C64XX_IISMOD_BLC_24BIT | S3C2412_IISMOD_BCLK_48FS); + + case S3C_I2SV2_CLKSRC_CDCLK: + /* Error if controller doesn't have the CDCLKCON bit */ + if (!(i2s->feature & S3C_FEATURE_CDCLKCON)) + return -EINVAL; + + switch (dir) { + case SND_SOC_CLOCK_IN: + iismod |= S3C64XX_IISMOD_CDCLKCON; + break; + case SND_SOC_CLOCK_OUT: + iismod &= ~S3C64XX_IISMOD_CDCLKCON; + break; + default: + return -EINVAL; + } break; + + default: + return -EINVAL; } -#endif writel(iismod, i2s->regs + S3C2412_IISMOD); - pr_debug("%s: w: IISMOD: %x\n", __func__, iismod); + pr_debug("%s w: IISMOD: %x\n", __func__, iismod); + return 0; } @@ -472,29 +474,25 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, switch (div_id) { case S3C_I2SV2_DIV_BCLK: - if (div > 3) { - /* convert value to bit field */ - - switch (div) { - case 16: - div = S3C2412_IISMOD_BCLK_16FS; - break; + switch (div) { + case 16: + div = S3C2412_IISMOD_BCLK_16FS; + break; - case 32: - div = S3C2412_IISMOD_BCLK_32FS; - break; + case 32: + div = S3C2412_IISMOD_BCLK_32FS; + break; - case 24: - div = S3C2412_IISMOD_BCLK_24FS; - break; + case 24: + div = S3C2412_IISMOD_BCLK_24FS; + break; - case 48: - div = S3C2412_IISMOD_BCLK_48FS; - break; + case 48: + div = S3C2412_IISMOD_BCLK_48FS; + break; - default: - return -EINVAL; - } + default: + return -EINVAL; } reg = readl(i2s->regs + S3C2412_IISMOD); @@ -505,29 +503,25 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, break; case S3C_I2SV2_DIV_RCLK: - if (div > 3) { - /* convert value to bit field */ - - switch (div) { - case 256: - div = S3C2412_IISMOD_RCLK_256FS; - break; + switch (div) { + case 256: + div = S3C2412_IISMOD_RCLK_256FS; + break; - case 384: - div = S3C2412_IISMOD_RCLK_384FS; - break; + case 384: + div = S3C2412_IISMOD_RCLK_384FS; + break; - case 512: - div = S3C2412_IISMOD_RCLK_512FS; - break; + case 512: + div = S3C2412_IISMOD_RCLK_512FS; + break; - case 768: - div = S3C2412_IISMOD_RCLK_768FS; - break; + case 768: + div = S3C2412_IISMOD_RCLK_768FS; + break; - default: - return -EINVAL; - } + default: + return -EINVAL; } reg = readl(i2s->regs + S3C2412_IISMOD); @@ -553,6 +547,33 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, return 0; } +static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct s3c_i2sv2_info *i2s = to_info(dai); + u32 reg = readl(i2s->regs + S3C2412_IISFIC); + snd_pcm_sframes_t delay; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + delay = S3C2412_IISFIC_TXCOUNT(reg); + else + delay = S3C2412_IISFIC_RXCOUNT(reg); + + return delay; +} + +struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai) +{ + struct s3c_i2sv2_info *i2s = to_info(cpu_dai); + u32 iismod = readl(i2s->regs + S3C2412_IISMOD); + + if (iismod & S3C2412_IISMOD_IMS_SYSMUX) + return i2s->iis_cclk; + else + return i2s->iis_pclk; +} +EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock); + /* default table of all avaialable root fs divisors */ static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 }; @@ -735,9 +756,15 @@ int s3c_i2sv2_register_dai(struct snd_soc_dai *dai) struct snd_soc_dai_ops *ops = dai->ops; ops->trigger = s3c2412_i2s_trigger; - ops->hw_params = s3c2412_i2s_hw_params; + if (!ops->hw_params) + ops->hw_params = s3c_i2sv2_hw_params; ops->set_fmt = s3c2412_i2s_set_fmt; ops->set_clkdiv = s3c2412_i2s_set_clkdiv; + ops->set_sysclk = s3c_i2sv2_set_sysclk; + + /* Allow overriding by (for example) IISv4 */ + if (!ops->delay) + ops->delay = s3c2412_i2s_delay; dai->suspend = s3c2412_i2s_suspend; dai->resume = s3c2412_i2s_resume; diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.h b/sound/soc/s3c24xx/s3c-i2s-v2.h index ecf8eaaed1db..766f43a13d8b 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.h +++ b/sound/soc/s3c24xx/s3c-i2s-v2.h @@ -25,10 +25,20 @@ #define S3C_I2SV2_DIV_RCLK (2) #define S3C_I2SV2_DIV_PRESCALER (3) +#define S3C_I2SV2_CLKSRC_PCLK 0 +#define S3C_I2SV2_CLKSRC_AUDIOBUS 1 +#define S3C_I2SV2_CLKSRC_CDCLK 2 + +/* Set this flag for I2S controllers that have the bit IISMOD[12] + * bridge/break RCLK signal and external Xi2sCDCLK pin. + */ +#define S3C_FEATURE_CDCLKCON (1 << 0) + /** * struct s3c_i2sv2_info - S3C I2S-V2 information * @dev: The parent device passed to use from the probe. * @regs: The pointer to the device registe block. + * @feature: Set of bit-flags indicating features of the controller. * @master: True if the I2S core is the I2S bit clock master. * @dma_playback: DMA information for playback channel. * @dma_capture: DMA information for capture channel. @@ -43,9 +53,10 @@ struct s3c_i2sv2_info { struct device *dev; void __iomem *regs; + u32 feature; + struct clk *iis_pclk; struct clk *iis_cclk; - struct clk *iis_clk; unsigned char master; @@ -57,6 +68,8 @@ struct s3c_i2sv2_info { u32 suspend_iispsr; }; +extern struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai); + struct s3c_i2sv2_rate_calc { unsigned int clk_div; /* for prescaler */ unsigned int fs_div; /* for root frame clock */ diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index 359e59346ba2..709adef9d043 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -32,12 +32,11 @@ #include <sound/soc.h> #include <mach/hardware.h> -#include <plat/regs-s3c2412-iis.h> - #include <mach/regs-gpio.h> #include <mach/dma.h> #include "s3c-dma.h" +#include "regs-i2s-v2.h" #include "s3c2412-i2s.h" #define S3C2412_I2S_DEBUG 0 @@ -66,44 +65,11 @@ static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = { static struct s3c_i2sv2_info s3c2412_i2s; -/* - * Set S3C2412 Clock source - */ -static int s3c2412_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) +static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) { - u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); - - pr_debug("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id, - freq, dir); - - switch (clk_id) { - case S3C2412_CLKSRC_PCLK: - s3c2412_i2s.master = 1; - iismod &= ~S3C2412_IISMOD_MASTER_MASK; - iismod |= S3C2412_IISMOD_MASTER_INTERNAL; - break; - case S3C2412_CLKSRC_I2SCLK: - s3c2412_i2s.master = 0; - iismod &= ~S3C2412_IISMOD_MASTER_MASK; - iismod |= S3C2412_IISMOD_MASTER_EXTERNAL; - break; - default: - return -EINVAL; - } - - writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); - return 0; + return cpu_dai->private_data; } - -struct clk *s3c2412_get_iisclk(void) -{ - return s3c2412_i2s.iis_clk; -} -EXPORT_SYMBOL_GPL(s3c2412_get_iisclk); - - static int s3c2412_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { @@ -142,13 +108,48 @@ static int s3c2412_i2s_probe(struct platform_device *pdev, return 0; } +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); + struct s3c_dma_params *dma_data; + u32 iismod; + + pr_debug("Entered %s\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data = i2s->dma_playback; + else + dma_data = i2s->dma_capture; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); + + 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; +} + #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) static struct snd_soc_dai_ops s3c2412_i2s_dai_ops = { - .set_sysclk = s3c2412_i2s_set_sysclk, + .hw_params = s3c2412_i2s_hw_params, }; struct snd_soc_dai s3c2412_i2s_dai = { diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h index 92848e54be16..0b5686b4d5c3 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.h +++ b/sound/soc/s3c24xx/s3c2412-i2s.h @@ -21,10 +21,8 @@ #define S3C2412_DIV_RCLK S3C_I2SV2_DIV_RCLK #define S3C2412_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER -#define S3C2412_CLKSRC_PCLK (0) -#define S3C2412_CLKSRC_I2SCLK (1) - -extern struct clk *s3c2412_get_iisclk(void); +#define S3C2412_CLKSRC_PCLK S3C_I2SV2_CLKSRC_PCLK +#define S3C2412_CLKSRC_I2SCLK S3C_I2SV2_CLKSRC_AUDIOBUS extern struct snd_soc_dai s3c2412_i2s_dai; diff --git a/sound/soc/s3c24xx/s3c64xx-i2s-v4.c b/sound/soc/s3c24xx/s3c64xx-i2s-v4.c new file mode 100644 index 000000000000..06db130030a1 --- /dev/null +++ b/sound/soc/s3c24xx/s3c64xx-i2s-v4.c @@ -0,0 +1,209 @@ +/* sound/soc/s3c24xx/s3c64xx-i2s-v4.c + * + * ALSA SoC Audio Layer - S3C64XX I2Sv4 driver + * Copyright (c) 2010 Samsung Electronics Co. Ltd + * Author: Jaswinder Singh <jassi.brar@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/io.h> + +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include <mach/gpio-bank-c.h> +#include <mach/gpio-bank-h.h> +#include <plat/gpio-cfg.h> + +#include <mach/map.h> +#include <mach/dma.h> + +#include "s3c-dma.h" +#include "regs-i2s-v2.h" +#include "s3c64xx-i2s.h" + +static struct s3c2410_dma_client s3c64xx_dma_client_out = { + .name = "I2Sv4 PCM Stereo out" +}; + +static struct s3c2410_dma_client s3c64xx_dma_client_in = { + .name = "I2Sv4 PCM Stereo in" +}; + +static struct s3c_dma_params s3c64xx_i2sv4_pcm_stereo_out; +static struct s3c_dma_params s3c64xx_i2sv4_pcm_stereo_in; +static struct s3c_i2sv2_info s3c64xx_i2sv4; + +struct snd_soc_dai s3c64xx_i2s_v4_dai; +EXPORT_SYMBOL_GPL(s3c64xx_i2s_v4_dai); + +static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) +{ + return cpu_dai->private_data; +} + +static int s3c64xx_i2sv4_probe(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + /* configure GPIO for i2s port */ + s3c_gpio_cfgpin(S3C64XX_GPC(4), S3C64XX_GPC4_I2S_V40_DO0); + s3c_gpio_cfgpin(S3C64XX_GPC(5), S3C64XX_GPC5_I2S_V40_DO1); + s3c_gpio_cfgpin(S3C64XX_GPC(7), S3C64XX_GPC7_I2S_V40_DO2); + s3c_gpio_cfgpin(S3C64XX_GPH(6), S3C64XX_GPH6_I2S_V40_BCLK); + s3c_gpio_cfgpin(S3C64XX_GPH(7), S3C64XX_GPH7_I2S_V40_CDCLK); + s3c_gpio_cfgpin(S3C64XX_GPH(8), S3C64XX_GPH8_I2S_V40_LRCLK); + s3c_gpio_cfgpin(S3C64XX_GPH(9), S3C64XX_GPH9_I2S_V40_DI); + + return 0; +} + +static int s3c_i2sv4_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); + struct s3c_dma_params *dma_data; + u32 iismod; + + dev_dbg(cpu_dai->dev, "Entered %s\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data = i2s->dma_playback; + else + dma_data = i2s->dma_capture; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); + + iismod = readl(i2s->regs + S3C2412_IISMOD); + dev_dbg(cpu_dai->dev, "%s: r: IISMOD: %x\n", __func__, iismod); + + iismod &= ~S3C64XX_IISMOD_BLC_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + iismod |= S3C64XX_IISMOD_BLC_8BIT; + break; + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S24_LE: + iismod |= S3C64XX_IISMOD_BLC_24BIT; + break; + } + + writel(iismod, i2s->regs + S3C2412_IISMOD); + dev_dbg(cpu_dai->dev, "%s: w: IISMOD: %x\n", __func__, iismod); + + return 0; +} + +static struct snd_soc_dai_ops s3c64xx_i2sv4_dai_ops = { + .hw_params = s3c_i2sv4_hw_params, +}; + +static __devinit int s3c64xx_i2sv4_dev_probe(struct platform_device *pdev) +{ + struct s3c_i2sv2_info *i2s; + struct snd_soc_dai *dai; + int ret; + + i2s = &s3c64xx_i2sv4; + dai = &s3c64xx_i2s_v4_dai; + + if (dai->dev) { + dev_dbg(dai->dev, "%s: \ + I2Sv4 instance already registered!\n", __func__); + return -EBUSY; + } + + dai->dev = &pdev->dev; + dai->name = "s3c64xx-i2s-v4"; + dai->id = 0; + dai->symmetric_rates = 1; + dai->playback.channels_min = 2; + dai->playback.channels_max = 2; + dai->playback.rates = S3C64XX_I2S_RATES; + dai->playback.formats = S3C64XX_I2S_FMTS; + dai->capture.channels_min = 2; + dai->capture.channels_max = 2; + dai->capture.rates = S3C64XX_I2S_RATES; + dai->capture.formats = S3C64XX_I2S_FMTS; + dai->probe = s3c64xx_i2sv4_probe; + dai->ops = &s3c64xx_i2sv4_dai_ops; + + i2s->feature |= S3C_FEATURE_CDCLKCON; + + i2s->dma_capture = &s3c64xx_i2sv4_pcm_stereo_in; + i2s->dma_playback = &s3c64xx_i2sv4_pcm_stereo_out; + + i2s->dma_capture->channel = DMACH_HSI_I2SV40_RX; + i2s->dma_capture->dma_addr = S3C64XX_PA_IISV4 + S3C2412_IISRXD; + i2s->dma_playback->channel = DMACH_HSI_I2SV40_TX; + i2s->dma_playback->dma_addr = S3C64XX_PA_IISV4 + S3C2412_IISTXD; + + i2s->dma_capture->client = &s3c64xx_dma_client_in; + i2s->dma_capture->dma_size = 4; + i2s->dma_playback->client = &s3c64xx_dma_client_out; + i2s->dma_playback->dma_size = 4; + + i2s->iis_cclk = clk_get(&pdev->dev, "audio-bus"); + if (IS_ERR(i2s->iis_cclk)) { + dev_err(&pdev->dev, "failed to get audio-bus\n"); + ret = PTR_ERR(i2s->iis_cclk); + goto err; + } + + clk_enable(i2s->iis_cclk); + + ret = s3c_i2sv2_probe(pdev, dai, i2s, 0); + if (ret) + goto err_clk; + + ret = s3c_i2sv2_register_dai(dai); + if (ret != 0) + goto err_i2sv2; + + return 0; + +err_i2sv2: + /* Not implemented for I2Sv2 core yet */ +err_clk: + clk_put(i2s->iis_cclk); +err: + return ret; +} + +static __devexit int s3c64xx_i2sv4_dev_remove(struct platform_device *pdev) +{ + dev_err(&pdev->dev, "Device removal not yet supported\n"); + return 0; +} + +static struct platform_driver s3c64xx_i2sv4_driver = { + .probe = s3c64xx_i2sv4_dev_probe, + .remove = s3c64xx_i2sv4_dev_remove, + .driver = { + .name = "s3c64xx-iis-v4", + .owner = THIS_MODULE, + }, +}; + +static int __init s3c64xx_i2sv4_init(void) +{ + return platform_driver_register(&s3c64xx_i2sv4_driver); +} +module_init(s3c64xx_i2sv4_init); + +static void __exit s3c64xx_i2sv4_exit(void) +{ + platform_driver_unregister(&s3c64xx_i2sv4_driver); +} +module_exit(s3c64xx_i2sv4_exit); + +/* Module information */ +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>"); +MODULE_DESCRIPTION("S3C64XX I2Sv4 SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index a72c251401ac..1d85cb85a7d2 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -12,16 +12,12 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> #include <linux/clk.h> #include <linux/gpio.h> #include <linux/io.h> #include <sound/soc.h> -#include <plat/regs-s3c2412-iis.h> #include <mach/gpio-bank-d.h> #include <mach/gpio-bank-e.h> #include <plat/gpio-cfg.h> @@ -30,6 +26,7 @@ #include <mach/dma.h> #include "s3c-dma.h" +#include "regs-i2s-v2.h" #include "s3c64xx-i2s.h" /* The value should be set to maximum of the total number @@ -57,55 +54,6 @@ static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) return cpu_dai->private_data; } -static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) -{ - struct s3c_i2sv2_info *i2s = to_info(cpu_dai); - u32 iismod = readl(i2s->regs + S3C2412_IISMOD); - - switch (clk_id) { - case S3C64XX_CLKSRC_PCLK: - iismod &= ~S3C64XX_IISMOD_IMS_SYSMUX; - break; - - case S3C64XX_CLKSRC_MUX: - iismod |= S3C64XX_IISMOD_IMS_SYSMUX; - break; - - case S3C64XX_CLKSRC_CDCLK: - switch (dir) { - case SND_SOC_CLOCK_IN: - iismod |= S3C64XX_IISMOD_CDCLKCON; - break; - case SND_SOC_CLOCK_OUT: - iismod &= ~S3C64XX_IISMOD_CDCLKCON; - break; - default: - return -EINVAL; - } - break; - - default: - return -EINVAL; - } - - writel(iismod, i2s->regs + S3C2412_IISMOD); - - return 0; -} - -struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai) -{ - struct s3c_i2sv2_info *i2s = to_info(dai); - u32 iismod = readl(i2s->regs + S3C2412_IISMOD); - - if (iismod & S3C64XX_IISMOD_IMS_SYSMUX) - return i2s->iis_cclk; - else - return i2s->iis_pclk; -} -EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock); - static int s3c64xx_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { @@ -130,18 +78,7 @@ static int s3c64xx_i2s_probe(struct platform_device *pdev, } -#define S3C64XX_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) - -#define S3C64XX_I2S_FMTS \ - (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE) - -static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops = { - .set_sysclk = s3c64xx_i2s_set_sysclk, -}; +static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops; static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev) { @@ -171,6 +108,8 @@ static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev) dai->probe = s3c64xx_i2s_probe; dai->ops = &s3c64xx_i2s_dai_ops; + i2s->feature |= S3C_FEATURE_CDCLKCON; + i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id]; i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id]; diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h index abe7253b55fc..7a40f43d1d51 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.h +++ b/sound/soc/s3c24xx/s3c64xx-i2s.h @@ -23,12 +23,20 @@ struct clk; #define S3C64XX_DIV_RCLK S3C_I2SV2_DIV_RCLK #define S3C64XX_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER -#define S3C64XX_CLKSRC_PCLK (0) -#define S3C64XX_CLKSRC_MUX (1) -#define S3C64XX_CLKSRC_CDCLK (2) +#define S3C64XX_CLKSRC_PCLK S3C_I2SV2_CLKSRC_PCLK +#define S3C64XX_CLKSRC_MUX S3C_I2SV2_CLKSRC_AUDIOBUS +#define S3C64XX_CLKSRC_CDCLK S3C_I2SV2_CLKSRC_CDCLK -extern struct snd_soc_dai s3c64xx_i2s_dai[]; +#define S3C64XX_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) + +#define S3C64XX_I2S_FMTS \ + (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE) -extern struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai); +extern struct snd_soc_dai s3c64xx_i2s_dai[]; +extern struct snd_soc_dai s3c64xx_i2s_v4_dai; #endif /* __SND_SOC_S3C24XX_S3C64XX_I2S_H */ diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c index efe4901213a3..07e8e51d10d6 100644 --- a/sound/soc/s3c24xx/smdk64xx_wm8580.c +++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c @@ -22,8 +22,6 @@ #include "s3c-dma.h" #include "s3c64xx-i2s.h" -#define S3C64XX_I2S_V4 2 - /* SMDK64XX has a 12MHZ crystal attached to WM8580 */ #define SMDK64XX_WM8580_FREQ 12000000 @@ -215,7 +213,7 @@ static struct snd_soc_dai_link smdk64xx_dai[] = { { /* Primary Playback i/f */ .name = "WM8580 PAIF RX", .stream_name = "Playback", - .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4], + .cpu_dai = &s3c64xx_i2s_v4_dai, .codec_dai = &wm8580_dai[WM8580_DAI_PAIFRX], .init = smdk64xx_wm8580_init_paifrx, .ops = &smdk64xx_ops, @@ -223,7 +221,7 @@ static struct snd_soc_dai_link smdk64xx_dai[] = { { /* Primary Capture i/f */ .name = "WM8580 PAIF TX", .stream_name = "Capture", - .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4], + .cpu_dai = &s3c64xx_i2s_v4_dai, .codec_dai = &wm8580_dai[WM8580_DAI_PAIFTX], .init = smdk64xx_wm8580_init_paiftx, .ops = &smdk64xx_ops, |