diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wm8731.c | 4 | ||||
-rw-r--r-- | sound/soc/s3c24xx/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/s3c24xx/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c-i2s-v2.c | 19 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c24xx-pcm.c | 17 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c64xx-i2s.c | 44 | ||||
-rw-r--r-- | sound/soc/s3c24xx/s3c64xx-i2s.h | 3 | ||||
-rw-r--r-- | sound/soc/s3c24xx/smdk6410-wm8731.c | 227 |
8 files changed, 312 insertions, 12 deletions
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index efbd4449bd3..6edd3447f5a 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -73,6 +73,8 @@ static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec, u16 *cache = codec->reg_cache; if (reg >= WM8731_CACHEREGNUM) return; + + printk(KERN_INFO "%s: reg %d, val %04x\n", __func__, reg, value); cache[reg] = value; } @@ -84,6 +86,8 @@ static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg, { u8 data[2]; + printk(KERN_INFO "%s: reg %d val %04x\n", __func__, reg, value); + /* data is * D15..D9 WM8731 register offset * D8...D0 register data diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index 11dcdfa663f..1ed87482b42 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -84,3 +84,11 @@ config SND_S3C24XX_SOC_S3C24XX_UDA134X depends on SND_S3C24XX_SOC select SND_S3C24XX_SOC_I2S select SND_SOC_UDA134X + +config SND_S3C64XX_SOC_SMDK6410_WM8731 + tristate "SoC I2S Audio support for WM8731 added to an SMDK6410" + depends on SND_S3C24XX_SOC + select SND_S3C64XX_SOC_I2S + select SND_SOC_WM8731 + help + Support for an WM8731 add-on board on I2S channel 0 on an SMDK6410 diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index c1ff0e4bcde..08bd5874633 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -21,6 +21,7 @@ snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o snd-soc-om-gta03-wm8753-objs := om_gta03_wm8753.o +snd-soc-smdk6410-wm8731-objs := smdk6410-wm8731.o obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o @@ -29,3 +30,4 @@ obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o obj-$(CONFIG_SND_S3C24XX_SOC_OM_GTA03_WM8753) += snd-soc-om-gta03-wm8753.o +obj-$(CONFIG_SND_S3C64XX_SOC_SMDK6410_WM8731) += snd-soc-smdk6410-wm8731.o diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c index 1dda7c85622..8a57f77d9a2 100644 --- a/sound/soc/s3c24xx/s3c-i2s-v2.c +++ b/sound/soc/s3c24xx/s3c-i2s-v2.c @@ -257,6 +257,25 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai, iismod = readl(i2s->regs + S3C2412_IISMOD); DBG("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_CPU_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 (0x0) +#endif + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: i2s->master = 0; diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index e797e862724..7458f00dfab 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c @@ -82,11 +82,19 @@ static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream) { struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; dma_addr_t pos = prtd->dma_pos; + unsigned int limit; int ret; DBG("Entered %s\n", __func__); - while (prtd->dma_loaded < prtd->dma_limit) { + if (s3c_dma_has_circular()) { + limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; + } else + limit = prtd->dma_limit; + + printk(KERN_INFO "%s: loaded %d, limit %d\n", __func__, prtd->dma_loaded, limit); + + while (prtd->dma_loaded < limit) { unsigned long len = prtd->dma_period; DBG("dma_loaded: %d\n", prtd->dma_loaded); @@ -130,7 +138,7 @@ static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, snd_pcm_period_elapsed(substream); spin_lock(&prtd->lock); - if (prtd->state & ST_RUNNING) { + if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { prtd->dma_loaded--; s3c24xx_pcm_enqueue(substream); } @@ -171,6 +179,11 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, DBG(KERN_ERR "failed to get dma channel: %d\n", ret); return ret; } + + /* use the circular buffering if we have it available. */ + if (s3c_dma_has_circular()) + s3c2410_dma_setflags(prtd->params->channel, + S3C2410_DMAF_CIRCULAR); } s3c2410_dma_set_buffdone_fn(prtd->params->channel, diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c index 2109b21ef9f..3334c747cc1 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.c +++ b/sound/soc/s3c24xx/s3c64xx-i2s.c @@ -49,11 +49,13 @@ static struct s3c2410_dma_client s3c64xx_dma_client_in = { static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { [0] = { + .channel = DMACH_I2S0_OUT, .client = &s3c64xx_dma_client_out, .dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISTXD, .dma_size = 4, }, [1] = { + .channel = DMACH_I2S1_OUT, .client = &s3c64xx_dma_client_out, .dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISTXD, .dma_size = 4, @@ -62,11 +64,13 @@ static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_out[2] = { static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_in[2] = { [0] = { + .channel = DMACH_I2S0_IN, .client = &s3c64xx_dma_client_in, .dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISRXD, .dma_size = 4, }, [1] = { + .channel = DMACH_I2S1_IN, .client = &s3c64xx_dma_client_in, .dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISRXD, .dma_size = 4, @@ -75,16 +79,33 @@ static struct s3c24xx_pcm_dma_params s3c64xx_i2s_pcm_stereo_in[2] = { static struct s3c_i2sv2_info s3c64xx_i2s[2]; -static int s3c64xx_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) { - /* TODO */ - return 0; + return cpu_dai->private_data; } -static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) +static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) { - return cpu_dai->private_data; + 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; + + default: + return -EINVAL; + } + + writel(iismod, i2s->regs + S3C2412_IISMOD); + + return 0; } unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *dai) @@ -95,6 +116,11 @@ unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *dai) } EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clockrate); +static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai) +{ + return cpu_dai->private_data; +} + static int s3c64xx_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { @@ -119,9 +145,9 @@ static int s3c64xx_i2s_probe(struct platform_device *pdev, i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id]; i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id]; - i2s->iis_cclk = clk_get(dev, "i2sclk"); - if (i2s->iis_cclk == NULL) { - dev_err(dev, "failed to get i2sclk"); + i2s->iis_cclk = clk_get(dev, "audio-bus"); + if (IS_ERR(i2s->iis_cclk)) { + dev_err(dev, "failed to get audio-bus"); iounmap(i2s->regs); return -ENODEV; } diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h index add574607ce..b7ffe3c38b6 100644 --- a/sound/soc/s3c24xx/s3c64xx-i2s.h +++ b/sound/soc/s3c24xx/s3c64xx-i2s.h @@ -21,7 +21,8 @@ #define S3C64XX_DIV_RCLK S3C_I2SV2_DIV_RCLK #define S3C64XX_DIV_PRESCALER S3C_I2SV2_DIV_PRESCALER -/* todo - define clock sources */ +#define S3C64XX_CLKSRC_PCLK (0) +#define S3C64XX_CLKSRC_MUX (1) extern struct snd_soc_dai s3c64xx_i2s_dai; diff --git a/sound/soc/s3c24xx/smdk6410-wm8731.c b/sound/soc/s3c24xx/smdk6410-wm8731.c new file mode 100644 index 00000000000..e11d5d48a5c --- /dev/null +++ b/sound/soc/s3c24xx/smdk6410-wm8731.c @@ -0,0 +1,227 @@ + + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <asm/mach-types.h> + +#include "../codecs/wm8731.h" +#include "s3c64xx-i2s.h" + +static struct platform_device *socdev; + + + +static void wm_shutdown(struct snd_pcm_substream *substream) +{ + printk(KERN_INFO "%s: substream %p\n", __func__, substream); +} + +static int wm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int fmt; + int ret; + + printk(KERN_INFO "%s: (%p,%p)\n", __func__, substream, params); + printk(KERN_INFO "%s: dai: cpu %p, codec %p\n", __func__, cpu_dai, codec_dai); + + //fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; + fmt = SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM; + fmt |= SND_SOC_DAIFMT_I2S; + + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) + return ret; + + if (fmt == (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM)) { + unsigned long iis_clkrate; + + ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_MUX, 0, + SND_SOC_CLOCK_OUT); + if (ret < 0) { + printk(KERN_ERR "%s: cpu set_sysclk err\n", __func__); + return ret; + } + + /* set prescaler division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C64XX_DIV_PRESCALER, 1); + if (ret < 0) { + printk(KERN_ERR "%s: codec clkdiv err\n", __func__); + return ret; + } + + iis_clkrate = s3c64xx_i2s_get_clockrate(cpu_dai) / 2; + printk(KERN_INFO "%s: clockrate %ld\n", __func__, iis_clkrate); + + iis_clkrate = 12000000; //tmphack// + + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, + iis_clkrate, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "%s: codec sysclk err\n", __func__); + return ret; + } + + } else { + /* TODO */ + BUG(); + } + + return 0; +} + +static int wm_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_MUX, 0, + SND_SOC_CLOCK_OUT); + if (ret < 0) { + printk(KERN_ERR "%s: cpu set_sysclk err\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C64XX_DIV_PRESCALER, 1); + if (ret < 0) { + printk(KERN_ERR "%s: cpu set_clkdiv err\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, + 12000000, SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "%s: codec sysclk err\n", __func__); + return ret; + } + + return 0; +} + +static struct snd_soc_ops wm_ops = { + .startup = wm_startup, + .hw_params = wm_hw_params, + .shutdown = wm_shutdown, +}; + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_INPUT("Line In"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* headphone connected to LHPOUT1, RHPOUT1 */ + {"Headphone Jack", NULL, "LHPOUT"}, + {"Headphone Jack", NULL, "RHPOUT"}, + + {"Line Out", NULL, "LOUT" }, + {"Line Out", NULL, "ROUT" }, + + {"LLINEIN", NULL, "Line In" }, + {"RLINEIN", NULL, "Line In" }, +}; + +static int wm_init(struct snd_soc_codec *codec) +{ + printk(KERN_DEBUG "%s: codec %p\n", __func__, codec); + + snd_soc_dapm_new_controls(codec, widgets, ARRAY_SIZE(widgets)); + snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + + snd_soc_dapm_sync(codec); + + return 0; +} + +#include "s3c24xx-pcm.h" + +static struct snd_soc_dai_link wm_dai_link = { + .name = "WM8731", + .stream_name = "WM8731", + .cpu_dai = &s3c64xx_i2s_dai, + .codec_dai = &wm8731_dai, + .init = wm_init, + .ops = &wm_ops, +}; + +static struct snd_soc_card wm_card = { + .name = "SMDK6410-WM8731", + .dai_link = &wm_dai_link, + .platform = &s3c24xx_soc_platform, + .num_links = 1, +}; + +struct wm8731_setup_data wm_setup = { + .i2c_bus = 0, + .i2c_address = 0x1a, +}; + +static struct snd_soc_device wm_snd_devdata = { + .card = &wm_card, + .codec_dev = &soc_codec_dev_wm8731, + .codec_data = &wm_setup, +}; + +static int __init smdk6410_wm8731_init(void) +{ + int ret; + + printk(KERN_INFO "%s: welcome\n", __func__); + + if (!machine_is_smdk6410()) { + printk(KERN_INFO "%s: for SMDK6410s\n", __func__); + return -ENOENT; + } + + socdev = platform_device_alloc("soc-audio", 0); + if (!socdev) { + printk(KERN_ERR "%s: no device\n", __func__); + return -ENOMEM; + } + + platform_set_drvdata(socdev, &wm_snd_devdata); + + wm_snd_devdata.dev = &socdev->dev; + + ret = platform_device_add(socdev); + if (ret) { + printk(KERN_ERR "%s: failed to add\n", __func__); + goto err_dev; + } + + printk(KERN_INFO "%s: succesfull\n", __func__); + return 0; + +err_dev: + platform_device_put(socdev); + return ret; +} + +module_init(smdk6410_wm8731_init); +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_LICENSE("GPL"); |