linux:alsa

diff -ruN linux-2.6.29.4/sound/core/oss/pcm_oss.c linux-new/sound/core/oss/pcm_oss.c
--- linux-2.6.29.4/sound/core/oss/pcm_oss.c	2009-05-19 01:52:34.000000000 +0200
+++ linux-new/sound/core/oss/pcm_oss.c	2009-03-04 14:20:18.000000000 +0100
@@ -2241,7 +2241,11 @@
 	runtime = substream->runtime;
 	runtime->oss.params = 1;
 	runtime->oss.trigger = 1;
+#ifdef CONFIG_SND_AT91_SOC_EBD9261_WM8731	
+	runtime->oss.rate = 44100;
+#else
 	runtime->oss.rate = 8000;
+#endif
 	mutex_init(&runtime->oss.params_lock);
 	switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
 	case SNDRV_MINOR_OSS_PCM_8:
@@ -2253,7 +2257,11 @@
 	default:
 		runtime->oss.format = AFMT_MU_LAW;
 	}
+#ifdef CONFIG_SND_AT91_SOC_EBD9261_WM8731	
+	runtime->oss.channels = 2;
+#else
 	runtime->oss.channels = 1;
+#endif	
 	runtime->oss.fragshift = 0;
 	runtime->oss.maxfrags = 0;
 	runtime->oss.subdivision = 0;
diff -ruN linux-2.6.29.4/sound/soc/atmel/ebd9261_wm8731.c linux-new/sound/soc/atmel/ebd9261_wm8731.c
--- linux-2.6.29.4/sound/soc/atmel/ebd9261_wm8731.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-new/sound/soc/atmel/ebd9261_wm8731.c	2009-03-14 16:29:56.000000000 +0100
@@ -0,0 +1,440 @@
+/*
+ * ebd9261_wm8731  --  SoC audio for ebd9261-based
+ * 			ATMEL AT91SAM9261 board.
+ *
+ *  Copyright (C) 2005 SAN People
+ *  Copyright (C) 2008 Atmel
+ *
+ * Author:Marcin Bis <marcin@bis.org.pl>
+ *
+ * Based on at91sam9g20ek.c by: Sedji Gaouaou
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#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 <linux/i2c.h>
+
+#include <linux/atmel-ssc.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 <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include "../codecs/wm8731.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+// codec wm9731 xtal frequency
+#define MCLK_RATE 11289600
+
+static struct clk *mclk;
+
+static int at91ebd9261_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;
+	int ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
+		MCLK_RATE, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		clk_disable(mclk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void at91ebd9261_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+	dev_dbg(rtd->socdev->dev, "shutdown");
+}
+
+static int at91ebd9261_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;
+	struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
+	struct ssc_device *ssc = ssc_p->ssc;
+	int ret;
+
+	unsigned int rate;
+	int cmr_div, period;
+
+	if (ssc == NULL) {
+		printk(KERN_INFO "at91ebd9261_hw_params: ssc is NULL!\n");
+		return -EINVAL;
+	}
+
+	/* TODO */
+	/* maybe select codec master or slave */
+
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+		 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * The SSC clock dividers depend on the sample rate.  The CMR.DIV
+	 * field divides the system master clock MCK to drive the SSC TK
+	 * signal which provides the codec BCLK.  The TCMR.PERIOD and
+	 * RCMR.PERIOD fields further divide the BCLK signal to drive
+	 * the SSC TF and RF signals which provide the codec DACLRC and
+	 * ADCLRC clocks.
+	 *
+	 * The dividers were determined through trial and error, where a
+	 * CMR.DIV value is chosen such that the resulting BCLK value is
+	 * divisible, or almost divisible, by (2 * sample rate), and then
+	 * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
+	 */
+	rate = params_rate(params);
+
+	switch (rate) {
+	case 8000:
+		cmr_div = 41;	/* BCLK = 99.3MHz/(2*41) = 1.209MHz */
+		period = 74;	/* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
+		break;
+	case 11025:
+		cmr_div = 44;	/* BCLK = 99.3MHz/(2*44) = 1.108MHz */
+		period = 49;	/* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
+		break;
+	case 16000:
+		cmr_div = 47;	/* BCLK = 99.3MHz/(2*47) = 1.055MHz */
+		period = 32;	/* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
+		break;
+	case 22050:
+		cmr_div = 38;	/* BCLK = 99.3MHz/(2*38) = 1.278MHz */
+		period = 28;	/* LRC = BCLK/(2*(28+1)) = 22049Hz */
+		break;
+	case 32000:
+		cmr_div = 49;	/* BCLK = 99.3MHz/(2*49)  = 1.007MHz */
+		period = 15;	/* LRC = BCLK/(2*(15+1))  = 31486,742Hz */
+		break;
+	case 44100:
+		cmr_div = 21;	/* BCLK = 99.3/(2*21)    = 2.293MHz */
+		period = 25;	/* LRC = BCLK/(2*(25+1)) = 44098Hz */
+		break;
+	case 48000:
+		cmr_div = 24;	/* BCLK = 99.3MHz/(2*24) = 2.015MHz */
+		period = 20;	/* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
+		break;
+	case 88200:
+		cmr_div = 21;	/* BCLK = 99.3MHz/(2*21) = 2.293MHz */
+		period = 12;	/* LRC = BCLK/(2*(12+1)) = 88196Hz */
+		break;
+	case 96000:
+		cmr_div = 17;	/* BCLK = 99.3MHz/(2*17) = 2.891MHz */
+		period = 14;	/* LRC = BCLK/(2*(14+1)) = 96376Hz */
+		break;
+	default:
+		printk(KERN_WARNING "unsupported rate %d"
+				" on at91ebd9261 board\n", rate);
+		return -EINVAL;
+	}
+
+#if 0		/* codec is master, disable ssc clock provide */
+	/* set the MCK divider for BCLK */
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
+	if (ret < 0)
+		return ret;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* set the BCLK divider for DACLRC */
+		ret = snd_soc_dai_set_clkdiv(cpu_dai,
+						ATMEL_SSC_TCMR_PERIOD, period);
+	} else {
+		/* set the BCLK divider for ADCLRC */
+		ret = snd_soc_dai_set_clkdiv(cpu_dai,
+						ATMEL_SSC_RCMR_PERIOD, period);
+	}
+	if (ret < 0)
+		return ret;
+
+#endif
+	return 0;
+}
+
+static struct snd_soc_ops at91ebd9261_ops = {
+	.startup = at91ebd9261_startup,
+	.hw_params = at91ebd9261_hw_params,
+	.shutdown = at91ebd9261_shutdown,
+};
+
+static int at91ebd9261_set_bias_level(struct snd_soc_card *card,
+					enum snd_soc_bias_level level)
+{
+#ifdef EBD9261_PROVIDE_MCLK_FOR_CODEC
+
+	static int mclk_on;
+	int ret = 0;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		if (!mclk_on)
+			ret = clk_enable(mclk);
+		if (ret == 0)
+			mclk_on = 1;
+		break;
+
+	case SND_SOC_BIAS_OFF:
+	case SND_SOC_BIAS_STANDBY:
+		if (mclk_on)
+			clk_disable(mclk);
+		mclk_on = 0;
+		break;
+	}
+
+	return ret;
+#else
+	return 0;
+#endif	
+}
+
+static const struct snd_soc_dapm_widget at91ebd9261_dapm_widgets[] = {
+	SND_SOC_DAPM_MIC("Int Mic", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+
+	/* speaker connected to LHPOUT */
+	{"Ext Spk", NULL, "LHPOUT"},
+
+	/* mic is connected to Mic Jack, with WM8731 Mic Bias */
+	{"MICIN", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Int Mic"},
+};
+
+/*
+ * Logic for a wm8731 as connected on a at91ebd9261 board.
+ */
+static int at91ebd9261_wm8731_init(struct snd_soc_codec *codec)
+{
+	printk(KERN_DEBUG
+			"at91ebd9261_wm8731 "
+			": at91ebd9261_wm8731_init() called\n");
+
+	/* Add specific widgets */
+	snd_soc_dapm_new_controls(codec, at91ebd9261_dapm_widgets,
+				  ARRAY_SIZE(at91ebd9261_dapm_widgets));
+	/* Set up specific audio path interconnects */
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	/* not connected */
+	snd_soc_dapm_nc_pin(codec, "RLINEIN");
+	snd_soc_dapm_nc_pin(codec, "LLINEIN");
+
+	/* always connected */
+	snd_soc_dapm_enable_pin(codec, "Int Mic");
+	snd_soc_dapm_enable_pin(codec, "Ext Spk");
+
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+static struct snd_soc_dai_link at91ebd9261_dai = {
+	.name = "WM8731",
+	.stream_name = "WM8731 PCM",
+	.cpu_dai = &atmel_ssc_dai[1],	// TODO 0 ?
+	.codec_dai = &wm8731_dai,
+	.init = at91ebd9261_wm8731_init,
+	.ops = &at91ebd9261_ops,
+};
+
+static struct snd_soc_card snd_soc_at91ebd9261 = {
+	.name = "AT91EBD9261",
+	.platform = &atmel_soc_platform,
+	.dai_link = &at91ebd9261_dai,
+	.num_links = 1,
+	.set_bias_level = at91ebd9261_set_bias_level,
+};
+
+/*
+ * FIXME: This is a temporary bodge to avoid cross-tree merge issues.
+ * New drivers should register the wm8731 I2C device in the machine
+ * setup code (under arch/arm for ARM systems).
+ */
+static int wm8731_i2c_register(void)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = 0x1a;
+	strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(0);
+	if (!adapter) {
+		printk(KERN_ERR "can't get i2c adapter 0\n");
+		return -ENODEV;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		printk(KERN_ERR "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_device at91ebd9261_snd_devdata = {
+	.card = &snd_soc_at91ebd9261,
+	.codec_dev = &soc_codec_dev_wm8731,
+};
+
+static struct platform_device *at91ebd9261_snd_device;
+
+static int __init at91ebd9261_init(void)
+{
+	struct atmel_ssc_info *ssc_p = at91ebd9261_dai.cpu_dai->private_data;
+	struct ssc_device *ssc = NULL;
+	struct clk *pllb;
+	int ret;
+
+/* TODO */
+/* machine type check
+	if (!machine_is_at91ebd9261())	
+		return -ENODEV;
+*/
+
+#ifdef EBD9261_PROVIDE_MCLK_FOR_CODEC
+	/*
+	 * Codec MCLK is supplied by PCK0 - set it up.
+	 */
+	mclk = clk_get(NULL, "pck0");
+	if (IS_ERR(mclk)) {
+		printk(KERN_ERR "ASoC: Failed to get MCLK\n");
+		ret = PTR_ERR(mclk);
+		goto err;
+	}
+
+	pllb = clk_get(NULL, "pllb");
+	if (IS_ERR(mclk)) {
+		printk(KERN_ERR "ASoC: Failed to get PLLB\n");
+		ret = PTR_ERR(mclk);
+		goto err_mclk;
+	}
+	ret = clk_set_parent(mclk, pllb);
+	clk_put(pllb);
+	if (ret != 0) {
+		printk(KERN_ERR "ASoC: Failed to set MCLK parent\n");
+		goto err_mclk;
+	}
+
+	clk_set_rate(mclk, MCLK_RATE);
+#endif
+
+	/*
+	 * Request SSC device
+	 */
+	ssc = ssc_request(1);
+	if (IS_ERR(ssc)) {
+		printk(KERN_ERR "ASoC: Failed to request SSC 1\n");
+		ret = PTR_ERR(ssc);
+		ssc = NULL;
+		goto err_ssc1;
+	}
+	ssc_p->ssc = ssc;
+
+	ret = wm8731_i2c_register();
+	if (ret != 0)
+		goto err_ssc;
+
+	at91ebd9261_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!at91ebd9261_snd_device) {
+		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+		ret = -ENOMEM;
+	}
+
+	platform_set_drvdata(at91ebd9261_snd_device,
+			&at91ebd9261_snd_devdata);
+	at91ebd9261_snd_devdata.dev = &at91ebd9261_snd_device->dev;
+
+	ret = platform_device_add(at91ebd9261_snd_device);
+	if (ret) {
+		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+		platform_device_put(at91ebd9261_snd_device);
+	}
+
+	return ret;
+
+err_ssc:
+	ssc_free(ssc);
+err_ssc1:
+	ssc_p->ssc = NULL;
+err_mclk:
+#ifdef EBD9261_PROVIDE_MCLK_FOR_CODEC
+	clk_put(mclk);
+	mclk = NULL;
+#endif
+err:
+	return ret;
+}
+
+static void __exit at91ebd9261_exit(void)
+{
+	struct atmel_ssc_info *ssc_p = at91ebd9261_dai.cpu_dai->private_data;
+	struct ssc_device *ssc;
+
+	if (ssc_p != NULL) {
+		ssc = ssc_p->ssc;
+		if (ssc != NULL)
+			ssc_free(ssc);
+		ssc_p->ssc = NULL;
+	}
+
+	platform_device_unregister(at91ebd9261_snd_device);
+	at91ebd9261_snd_device = NULL;
+	clk_put(mclk);
+	mclk = NULL;
+}
+
+module_init(at91ebd9261_init);
+module_exit(at91ebd9261_exit);
+
+/* Module information */
+MODULE_AUTHOR("Marcin Bis <marcin@bis.org.pl>");
+MODULE_DESCRIPTION("ALSA SoC AT91EBD9261_WM8731");
+MODULE_LICENSE("GPL");
diff -ruN linux-2.6.29.4/sound/soc/atmel/Kconfig linux-new/sound/soc/atmel/Kconfig
--- linux-2.6.29.4/sound/soc/atmel/Kconfig	2009-05-19 01:52:34.000000000 +0200
+++ linux-new/sound/soc/atmel/Kconfig	2009-03-04 14:20:18.000000000 +0100
@@ -41,3 +41,12 @@
           and FRAME signals on the PlayPaq.  Unless you want to play
           with the AT32 as the SSC master, you probably want to say N here,
           as this will give you better sound quality.
+
+config SND_AT91_SOC_EBD9261_WM8731
+	tristate "SoC Audio support for WM8731-based At91ebd9261 evaluation board"
+	depends on ATMEL_SSC && ARCH_AT91SAM9261 && SND_ATMEL_SOC
+	select SND_ATMEL_SOC_SSC
+	select SND_SOC_WM8731
+	help
+	  Say Y if you want to add support for SoC audio on WM8731-based
+	  AT91ebd9261 evaluation board.
diff -ruN linux-2.6.29.4/sound/soc/atmel/Makefile linux-new/sound/soc/atmel/Makefile
--- linux-2.6.29.4/sound/soc/atmel/Makefile	2009-05-19 01:52:34.000000000 +0200
+++ linux-new/sound/soc/atmel/Makefile	2009-03-04 14:20:18.000000000 +0100
@@ -7,9 +7,11 @@
 
 # AT91 Machine Support
 snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
+snd-soc-ebd9261-wm8731-objs := ebd9261_wm8731.o
 
 # AT32 Machine Support
 snd-soc-playpaq-objs := playpaq_wm8510.o
 
 obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
+obj-$(CONFIG_SND_AT91_SOC_EBD9261_WM8731) += snd-soc-ebd9261-wm8731.o
 obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o