All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] ARM: S3C64XX: Add AC97 platform resources
@ 2010-01-26  5:51 ` jassisinghbrar at gmail.com
  0 siblings, 0 replies; 44+ messages in thread
From: jassisinghbrar @ 2010-01-26  5:51 UTC (permalink / raw)
  To: alsa-devel, linux-arm-kernel
  Cc: ben-linux, broonie, lrg, linux-samsung-soc, Jassi Brar

From: Jassi Brar <jassi.brar@samsung.com>

This patch defines the platform device and the resources: IRQ, DMA and MEM,
needed by the AC97 controller driver.

Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
---
 arch/arm/plat-s3c/include/plat/audio.h |    8 +++
 arch/arm/plat-s3c/include/plat/devs.h  |    3 +
 arch/arm/plat-s3c64xx/dev-audio.c      |   78 ++++++++++++++++++++++++++++++++
 3 files changed, 89 insertions(+), 0 deletions(-)

diff --git a/arch/arm/plat-s3c/include/plat/audio.h b/arch/arm/plat-s3c/include/plat/audio.h
index f22d23b..e07c941 100644
--- a/arch/arm/plat-s3c/include/plat/audio.h
+++ b/arch/arm/plat-s3c/include/plat/audio.h
@@ -8,6 +8,14 @@
  * published by the Free Software Foundation.
  */
 
+/* The machine init code calls s3c*_ac97_setup_gpio with
+ * one of these defines in order to select appropriate bank
+ * of GPIO for AC97 pins
+ */
+#define S3C64XX_AC97_GPD  0
+#define S3C64XX_AC97_GPE  1
+extern void s3c64xx_ac97_setup_gpio(int);
+
 /**
  * struct s3c_audio_pdata - common platform data for audio device drivers
  * @cfg_gpio: Callback function to setup mux'ed pins in I2S/PCM/AC97 mode
diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h
index c6f9b73..65317eb 100644
--- a/arch/arm/plat-s3c/include/plat/devs.h
+++ b/arch/arm/plat-s3c/include/plat/devs.h
@@ -35,7 +35,10 @@ extern struct platform_device s3c64xx_device_spi1;
 extern struct platform_device s3c64xx_device_pcm0;
 extern struct platform_device s3c64xx_device_pcm1;
 
+extern struct platform_device s3c64xx_device_ac97;
+
 extern struct platform_device s3c_device_ts;
+
 extern struct platform_device s3c_device_fb;
 extern struct platform_device s3c_device_ohci;
 extern struct platform_device s3c_device_lcd;
diff --git a/arch/arm/plat-s3c64xx/dev-audio.c b/arch/arm/plat-s3c64xx/dev-audio.c
index f6b7bfb..63088b7 100644
--- a/arch/arm/plat-s3c64xx/dev-audio.c
+++ b/arch/arm/plat-s3c64xx/dev-audio.c
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
 
 #include <mach/irqs.h>
 #include <mach/map.h>
@@ -254,3 +255,80 @@ struct platform_device s3c64xx_device_pcm1 = {
 	},
 };
 EXPORT_SYMBOL(s3c64xx_device_pcm1);
+
+/* AC97 Controller platform devices */
+
+static int s3c64xx_ac97_cfg_gpd(struct platform_device *pdev)
+{
+	s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_AC97_BITCLK);
+	s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_AC97_nRESET);
+	s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_AC97_SYNC);
+	s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_AC97_SDI);
+	s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_AC97_SDO);
+
+	return 0;
+}
+
+static int s3c64xx_ac97_cfg_gpe(struct platform_device *pdev)
+{
+	s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_AC97_BITCLK);
+	s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_AC97_nRESET);
+	s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_AC97_SYNC);
+	s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_AC97_SDI);
+	s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_AC97_SDO);
+
+	return 0;
+}
+
+static struct resource s3c64xx_ac97_resource[] = {
+	[0] = {
+		.start = S3C64XX_PA_AC97,
+		.end   = S3C64XX_PA_AC97 + 0x100 - 1,
+		.flags = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start = DMACH_AC97_PCMOUT,
+		.end   = DMACH_AC97_PCMOUT,
+		.flags = IORESOURCE_DMA,
+	},
+	[2] = {
+		.start = DMACH_AC97_PCMIN,
+		.end   = DMACH_AC97_PCMIN,
+		.flags = IORESOURCE_DMA,
+	},
+	[3] = {
+		.start = DMACH_AC97_MICIN,
+		.end   = DMACH_AC97_MICIN,
+		.flags = IORESOURCE_DMA,
+	},
+	[4] = {
+		.start = IRQ_AC97,
+		.end   = IRQ_AC97,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct s3c_audio_pdata s3c_ac97_pdata;
+
+static u64 s3c64xx_ac97_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s3c64xx_device_ac97 = {
+	.name		  = "s3c-ac97",
+	.id		  = -1,
+	.num_resources	  = ARRAY_SIZE(s3c64xx_ac97_resource),
+	.resource	  = s3c64xx_ac97_resource,
+	.dev = {
+		.platform_data = &s3c_ac97_pdata,
+		.dma_mask = &s3c64xx_ac97_dmamask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};
+EXPORT_SYMBOL(s3c64xx_device_ac97);
+
+void __init s3c64xx_ac97_setup_gpio(int num)
+{
+	if (num == S3C64XX_AC97_GPD)
+		s3c_ac97_pdata.cfg_gpio = s3c64xx_ac97_cfg_gpd;
+	else
+		s3c_ac97_pdata.cfg_gpio = s3c64xx_ac97_cfg_gpe;
+}
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 1/3] ARM: S3C64XX: Add AC97 platform resources
@ 2010-01-26  5:51 ` jassisinghbrar at gmail.com
  0 siblings, 0 replies; 44+ messages in thread
From: jassisinghbrar at gmail.com @ 2010-01-26  5:51 UTC (permalink / raw)
  To: linux-arm-kernel

From: Jassi Brar <jassi.brar@samsung.com>

This patch defines the platform device and the resources: IRQ, DMA and MEM,
needed by the AC97 controller driver.

Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
---
 arch/arm/plat-s3c/include/plat/audio.h |    8 +++
 arch/arm/plat-s3c/include/plat/devs.h  |    3 +
 arch/arm/plat-s3c64xx/dev-audio.c      |   78 ++++++++++++++++++++++++++++++++
 3 files changed, 89 insertions(+), 0 deletions(-)

diff --git a/arch/arm/plat-s3c/include/plat/audio.h b/arch/arm/plat-s3c/include/plat/audio.h
index f22d23b..e07c941 100644
--- a/arch/arm/plat-s3c/include/plat/audio.h
+++ b/arch/arm/plat-s3c/include/plat/audio.h
@@ -8,6 +8,14 @@
  * published by the Free Software Foundation.
  */
 
+/* The machine init code calls s3c*_ac97_setup_gpio with
+ * one of these defines in order to select appropriate bank
+ * of GPIO for AC97 pins
+ */
+#define S3C64XX_AC97_GPD  0
+#define S3C64XX_AC97_GPE  1
+extern void s3c64xx_ac97_setup_gpio(int);
+
 /**
  * struct s3c_audio_pdata - common platform data for audio device drivers
  * @cfg_gpio: Callback function to setup mux'ed pins in I2S/PCM/AC97 mode
diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h
index c6f9b73..65317eb 100644
--- a/arch/arm/plat-s3c/include/plat/devs.h
+++ b/arch/arm/plat-s3c/include/plat/devs.h
@@ -35,7 +35,10 @@ extern struct platform_device s3c64xx_device_spi1;
 extern struct platform_device s3c64xx_device_pcm0;
 extern struct platform_device s3c64xx_device_pcm1;
 
+extern struct platform_device s3c64xx_device_ac97;
+
 extern struct platform_device s3c_device_ts;
+
 extern struct platform_device s3c_device_fb;
 extern struct platform_device s3c_device_ohci;
 extern struct platform_device s3c_device_lcd;
diff --git a/arch/arm/plat-s3c64xx/dev-audio.c b/arch/arm/plat-s3c64xx/dev-audio.c
index f6b7bfb..63088b7 100644
--- a/arch/arm/plat-s3c64xx/dev-audio.c
+++ b/arch/arm/plat-s3c64xx/dev-audio.c
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
 
 #include <mach/irqs.h>
 #include <mach/map.h>
@@ -254,3 +255,80 @@ struct platform_device s3c64xx_device_pcm1 = {
 	},
 };
 EXPORT_SYMBOL(s3c64xx_device_pcm1);
+
+/* AC97 Controller platform devices */
+
+static int s3c64xx_ac97_cfg_gpd(struct platform_device *pdev)
+{
+	s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_AC97_BITCLK);
+	s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_AC97_nRESET);
+	s3c_gpio_cfgpin(S3C64XX_GPD(2), S3C64XX_GPD2_AC97_SYNC);
+	s3c_gpio_cfgpin(S3C64XX_GPD(3), S3C64XX_GPD3_AC97_SDI);
+	s3c_gpio_cfgpin(S3C64XX_GPD(4), S3C64XX_GPD4_AC97_SDO);
+
+	return 0;
+}
+
+static int s3c64xx_ac97_cfg_gpe(struct platform_device *pdev)
+{
+	s3c_gpio_cfgpin(S3C64XX_GPE(0), S3C64XX_GPE0_AC97_BITCLK);
+	s3c_gpio_cfgpin(S3C64XX_GPE(1), S3C64XX_GPE1_AC97_nRESET);
+	s3c_gpio_cfgpin(S3C64XX_GPE(2), S3C64XX_GPE2_AC97_SYNC);
+	s3c_gpio_cfgpin(S3C64XX_GPE(3), S3C64XX_GPE3_AC97_SDI);
+	s3c_gpio_cfgpin(S3C64XX_GPE(4), S3C64XX_GPE4_AC97_SDO);
+
+	return 0;
+}
+
+static struct resource s3c64xx_ac97_resource[] = {
+	[0] = {
+		.start = S3C64XX_PA_AC97,
+		.end   = S3C64XX_PA_AC97 + 0x100 - 1,
+		.flags = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start = DMACH_AC97_PCMOUT,
+		.end   = DMACH_AC97_PCMOUT,
+		.flags = IORESOURCE_DMA,
+	},
+	[2] = {
+		.start = DMACH_AC97_PCMIN,
+		.end   = DMACH_AC97_PCMIN,
+		.flags = IORESOURCE_DMA,
+	},
+	[3] = {
+		.start = DMACH_AC97_MICIN,
+		.end   = DMACH_AC97_MICIN,
+		.flags = IORESOURCE_DMA,
+	},
+	[4] = {
+		.start = IRQ_AC97,
+		.end   = IRQ_AC97,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct s3c_audio_pdata s3c_ac97_pdata;
+
+static u64 s3c64xx_ac97_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s3c64xx_device_ac97 = {
+	.name		  = "s3c-ac97",
+	.id		  = -1,
+	.num_resources	  = ARRAY_SIZE(s3c64xx_ac97_resource),
+	.resource	  = s3c64xx_ac97_resource,
+	.dev = {
+		.platform_data = &s3c_ac97_pdata,
+		.dma_mask = &s3c64xx_ac97_dmamask,
+		.coherent_dma_mask = DMA_BIT_MASK(32),
+	},
+};
+EXPORT_SYMBOL(s3c64xx_device_ac97);
+
+void __init s3c64xx_ac97_setup_gpio(int num)
+{
+	if (num == S3C64XX_AC97_GPD)
+		s3c_ac97_pdata.cfg_gpio = s3c64xx_ac97_cfg_gpd;
+	else
+		s3c_ac97_pdata.cfg_gpio = s3c64xx_ac97_cfg_gpe;
+}
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26  5:51 ` jassisinghbrar at gmail.com
@ 2010-01-26  5:51   ` jassisinghbrar at gmail.com
  -1 siblings, 0 replies; 44+ messages in thread
From: jassisinghbrar @ 2010-01-26  5:51 UTC (permalink / raw)
  To: alsa-devel, linux-arm-kernel
  Cc: ben-linux, broonie, lrg, linux-samsung-soc, Jassi Brar

From: Jassi Brar <jassi.brar@samsung.com>

Add the AC97 controller driver for Samsung SoCs that have one.

Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
---
 sound/soc/s3c24xx/Kconfig    |    6 +-
 sound/soc/s3c24xx/Makefile   |    3 +-
 sound/soc/s3c24xx/s3c-ac97.c |  535 ++++++++++++++++++++++++++++++++++++++++++
 sound/soc/s3c24xx/s3c-ac97.h |   23 ++
 4 files changed, 565 insertions(+), 2 deletions(-)
 create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
 create mode 100644 sound/soc/s3c24xx/s3c-ac97.h

diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index b489f1a..ad3690e 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
 	select S3C2410_DMA
 	select AC97_BUS
 	select SND_SOC_AC97_BUS
-	
+
+config SND_S3C_SOC_AC97
+	tristate
+	select SND_SOC_AC97_BUS
+
 config SND_S3C24XX_SOC_NEO1973_WM8753
 	tristate "SoC I2S Audio support for NEO1973 - WM8753"
 	depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index b744657..b7411bd 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
+snd-soc-s3c-ac97-objs := s3c-ac97.o
 snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
 snd-soc-s3c-pcm-objs := s3c-pcm.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
 obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
 obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
@@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
 obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
-
diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
new file mode 100644
index 0000000..acb8f51
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-ac97.c
@@ -0,0 +1,535 @@
+/* sound/soc/s3c24xx/s3c-ac97.c
+ *
+ * ALSA SoC Audio Layer - S3C AC97 Controller driver
+ * 	Evolved from s3c2443-ac97.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co. Ltd
+ * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
+ * 	Credits: Graeme Gregory, Sean Choi
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <sound/soc.h>
+
+#include <plat/regs-ac97.h>
+#include <mach/dma.h>
+#include <plat/audio.h>
+
+#include "s3c-dma.h"
+#include "s3c-ac97.h"
+
+#define AC_CMD_ADDR(x) (x << 16)
+#define AC_CMD_DATA(x) (x & 0xffff)
+
+struct s3c_ac97_info {
+	unsigned           state;
+	struct clk         *ac97_clk;
+	void __iomem	   *regs;
+	struct mutex       lock;
+	struct completion  done;
+};
+static struct s3c_ac97_info s3c_ac97;
+
+static struct s3c2410_dma_client s3c_dma_client_out = {
+	.name = "AC97 PCMOut"
+};
+
+static struct s3c2410_dma_client s3c_dma_client_in = {
+	.name = "AC97 PCMIn"
+};
+
+static struct s3c2410_dma_client s3c_dma_client_micin = {
+	.name = "AC97 MicIn"
+};
+
+static struct s3c_dma_params s3c_ac97_pcm_out = {
+	.client		= &s3c_dma_client_out,
+	.dma_size	= 4,
+};
+
+static struct s3c_dma_params s3c_ac97_pcm_in = {
+	.client		= &s3c_dma_client_in,
+	.dma_size	= 4,
+};
+
+static struct s3c_dma_params s3c_ac97_mic_in = {
+	.client		= &s3c_dma_client_micin,
+	.dma_size	= 4,
+};
+
+static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	writel(S3C_AC97_GLBCTRL_COLDRESET,
+			s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+}
+
+static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+}
+
+static void s3c_ac97_activate(struct snd_ac97 *ac97)
+{
+	u32 ac_glbctrl, stat;
+
+	stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
+	switch (stat) {
+	case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
+		return;
+	case S3C_AC97_GLBSTAT_MAINSTATE_READY:
+	case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
+		break;
+	default:
+		s3c_ac97_cold_reset(ac97);
+		s3c_ac97_warm_reset(ac97);
+		break;
+	}
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	INIT_COMPLETION(s3c_ac97.done);
+
+	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
+		printk(KERN_ERR "AC97: Unable to activate!");
+}
+
+static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
+	unsigned short reg)
+{
+	u32 ac_glbctrl, ac_codec_cmd;
+	u32 stat, addr, data;
+
+	mutex_lock(&s3c_ac97.lock);
+
+	s3c_ac97_activate(ac97);
+
+	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
+	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	INIT_COMPLETION(s3c_ac97.done);
+
+	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
+		printk(KERN_ERR "AC97: Unable to read!");
+
+	stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
+	addr = (stat >> 16) & 0x7f;
+	data = (stat & 0xffff);
+
+	if (addr != reg)
+		printk(KERN_ERR "s3c-ac97: req addr = %02x,"
+				" rep addr = %02x\n", reg, addr);
+
+	mutex_unlock(&s3c_ac97.lock);
+
+	return (unsigned short)data;
+}
+
+static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+	unsigned short val)
+{
+	u32 ac_glbctrl, ac_codec_cmd;
+
+	mutex_lock(&s3c_ac97.lock);
+
+	s3c_ac97_activate(ac97);
+
+	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
+	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	INIT_COMPLETION(s3c_ac97.done);
+
+	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
+		printk(KERN_ERR "AC97: Unable to write!");
+
+	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
+	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	mutex_unlock(&s3c_ac97.lock);
+}
+
+static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
+{
+	u32 ac_glbctrl, ac_glbstat;
+
+	ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
+
+	if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
+
+		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
+		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+		ac_glbctrl |= (1<<30); /* Clear interrupt */
+		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+		complete(&s3c_ac97.done);
+	}
+
+	return IRQ_HANDLED;
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read       = s3c_ac97_read,
+	.write      = s3c_ac97_write,
+	.warm_reset = s3c_ac97_warm_reset,
+	.reset      = s3c_ac97_cold_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		cpu_dai->dma_data = &s3c_ac97_pcm_out;
+	else
+		cpu_dai->dma_data = &s3c_ac97_pcm_in;
+
+	return 0;
+}
+
+static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	u32 ac_glbctrl;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channel = ((struct s3c_dma_params *)
+		  rtd->dai->cpu_dai->dma_data)->channel;
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
+	else
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
+		else
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		break;
+	}
+
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
+	return 0;
+}
+
+static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return -ENODEV;
+	else
+		cpu_dai->dma_data = &s3c_ac97_mic_in;
+
+	return 0;
+}
+
+static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
+				    int cmd, struct snd_soc_dai *dai)
+{
+	u32 ac_glbctrl;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channel = ((struct s3c_dma_params *)
+		  rtd->dai->cpu_dai->dma_data)->channel;
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		break;
+	}
+
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
+	return 0;
+}
+
+#define S3C_AC97_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)
+
+static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
+	.hw_params	= s3c_ac97_hw_params,
+	.trigger	= s3c_ac97_trigger,
+};
+
+static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
+	.hw_params	= s3c_ac97_hw_mic_params,
+	.trigger	= s3c_ac97_mic_trigger,
+};
+
+struct snd_soc_dai s3c_ac97_dai[] = {
+	[S3C_AC97_DAI_PCM] = {
+		.name =	"s3c-ac97",
+		.id = S3C_AC97_DAI_PCM,
+		.ac97_control = 1,
+		.playback = {
+			.stream_name = "AC97 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = S3C_AC97_RATES,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.capture = {
+			.stream_name = "AC97 Capture",
+			/* NOTE: If the codec ouputs just one slot,
+			 * it *seems* our AC97 controller reads the only
+			 * valid slot(if either 3 or 4) for PCM-In.
+			 * For such cases, we record Mono.
+			 */
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = S3C_AC97_RATES,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.ops = &s3c_ac97_dai_ops,
+	},
+	[S3C_AC97_DAI_MIC] = {
+		.name = "s3c-ac97-mic",
+		.id = S3C_AC97_DAI_MIC,
+		.ac97_control = 1,
+		.capture = {
+			.stream_name = "AC97 Mic Capture",
+			.channels_min = 1,
+			/* NOTE: If the codec(like WM9713) can't ouput just
+			 * one slot, it *seems* our AC97 controller reads
+			 * two slots(if one of them is Slot-6) for MIC also.
+			 * For such cases, we record Stereo.
+			 */
+			.channels_max = 2,
+			.rates = S3C_AC97_RATES,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.ops = &s3c_ac97_mic_dai_ops,
+	},
+};
+EXPORT_SYMBOL_GPL(s3c_ac97_dai);
+
+static __devinit int s3c_ac97_probe(struct platform_device *pdev)
+{
+	struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
+	struct s3c_audio_pdata *ac97_pdata;
+	int ret;
+
+	ac97_pdata = pdev->dev.platform_data;
+	if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
+		dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
+		return -EINVAL;
+	}
+
+	/* Check for availability of necessary resource */
+	dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!dmatx_res) {
+		dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
+		return -ENXIO;
+	}
+
+	dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!dmarx_res) {
+		dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
+		return -ENXIO;
+	}
+
+	dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
+	if (!dmamic_res) {
+		dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
+		return -ENXIO;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "Unable to get register resource\n");
+		return -ENXIO;
+	}
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq_res) {
+		dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
+		return -ENXIO;
+	}
+
+	if (!request_mem_region(mem_res->start,
+				resource_size(mem_res), "s3c-ac97")) {
+		dev_err(&pdev->dev, "Unable to request register region\n");
+		return -EBUSY;
+	}
+
+	s3c_ac97_pcm_out.channel = dmatx_res->start;
+	s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
+	s3c_ac97_pcm_in.channel = dmarx_res->start;
+	s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
+	s3c_ac97_mic_in.channel = dmamic_res->start;
+	s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
+
+	init_completion(&s3c_ac97.done);
+	mutex_init(&s3c_ac97.lock);
+
+	s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
+	if (s3c_ac97.regs == NULL) {
+		dev_err(&pdev->dev, "Unable to ioremap register region\n");
+		ret = -ENXIO;
+		goto lb1;
+	}
+
+	s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
+	if (IS_ERR(s3c_ac97.ac97_clk)) {
+		dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
+		ret = -ENODEV;
+		goto lb2;
+	}
+	clk_enable(s3c_ac97.ac97_clk);
+
+	if (ac97_pdata->cfg_gpio(pdev)) {
+		dev_err(&pdev->dev, "Unable to configure gpio\n");
+		ret = -EINVAL;
+		goto lb3;
+	}
+
+	ret = request_irq(irq_res->start, s3c_ac97_irq,
+					IRQF_DISABLED, "AC97", NULL);
+	if (ret < 0) {
+		printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
+		goto lb4;
+	}
+
+	s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
+	s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
+
+	ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
+	if (ret)
+		goto lb5;
+
+	return 0;
+
+lb5:
+	free_irq(irq_res->start, NULL);
+lb4:
+lb3:
+	clk_disable(s3c_ac97.ac97_clk);
+	clk_put(s3c_ac97.ac97_clk);
+lb2:
+	iounmap(s3c_ac97.regs);
+lb1:
+	release_mem_region(mem_res->start, resource_size(mem_res));
+
+	return ret;
+}
+
+static __devexit int s3c_ac97_remove(struct platform_device *pdev)
+{
+	struct resource *mem_res, *irq_res;
+
+	snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (irq_res)
+		free_irq(irq_res->start, NULL);
+
+	clk_disable(s3c_ac97.ac97_clk);
+	clk_put(s3c_ac97.ac97_clk);
+
+	iounmap(s3c_ac97.regs);
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (mem_res)
+		release_mem_region(mem_res->start, resource_size(mem_res));
+
+	return 0;
+}
+
+static struct platform_driver s3c_ac97_driver = {
+	.probe  = s3c_ac97_probe,
+	.remove = s3c_ac97_remove,
+	.driver = {
+		.name = "s3c-ac97",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init s3c_ac97_init(void)
+{
+	return platform_driver_register(&s3c_ac97_driver);
+}
+module_init(s3c_ac97_init);
+
+static void __exit s3c_ac97_exit(void)
+{
+	platform_driver_unregister(&s3c_ac97_driver);
+}
+module_exit(s3c_ac97_exit);
+
+MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
+MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
new file mode 100644
index 0000000..2781983
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-ac97.h
@@ -0,0 +1,23 @@
+/* sound/soc/s3c24xx/s3c-ac97.h
+ *
+ * ALSA SoC Audio Layer - S3C AC97 Controller driver
+ * 	Evolved from s3c2443-ac97.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co. Ltd
+ * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
+ * 	Credits: Graeme Gregory, Sean Choi
+ *
+ * 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.
+ */
+
+#ifndef __S3C_AC97_H_
+#define __S3C_AC97_H_
+
+#define S3C_AC97_DAI_PCM 0
+#define S3C_AC97_DAI_MIC 1
+
+extern struct snd_soc_dai s3c_ac97_dai[];
+
+#endif /* __S3C_AC97_H_ */
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26  5:51   ` jassisinghbrar at gmail.com
  0 siblings, 0 replies; 44+ messages in thread
From: jassisinghbrar at gmail.com @ 2010-01-26  5:51 UTC (permalink / raw)
  To: linux-arm-kernel

From: Jassi Brar <jassi.brar@samsung.com>

Add the AC97 controller driver for Samsung SoCs that have one.

Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
---
 sound/soc/s3c24xx/Kconfig    |    6 +-
 sound/soc/s3c24xx/Makefile   |    3 +-
 sound/soc/s3c24xx/s3c-ac97.c |  535 ++++++++++++++++++++++++++++++++++++++++++
 sound/soc/s3c24xx/s3c-ac97.h |   23 ++
 4 files changed, 565 insertions(+), 2 deletions(-)
 create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
 create mode 100644 sound/soc/s3c24xx/s3c-ac97.h

diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index b489f1a..ad3690e 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
 	select S3C2410_DMA
 	select AC97_BUS
 	select SND_SOC_AC97_BUS
-	
+
+config SND_S3C_SOC_AC97
+	tristate
+	select SND_SOC_AC97_BUS
+
 config SND_S3C24XX_SOC_NEO1973_WM8753
 	tristate "SoC I2S Audio support for NEO1973 - WM8753"
 	depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index b744657..b7411bd 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
+snd-soc-s3c-ac97-objs := s3c-ac97.o
 snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
 snd-soc-s3c-pcm-objs := s3c-pcm.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
 obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
 obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
@@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
 obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
-
diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
new file mode 100644
index 0000000..acb8f51
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-ac97.c
@@ -0,0 +1,535 @@
+/* sound/soc/s3c24xx/s3c-ac97.c
+ *
+ * ALSA SoC Audio Layer - S3C AC97 Controller driver
+ * 	Evolved from s3c2443-ac97.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co. Ltd
+ * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
+ * 	Credits: Graeme Gregory, Sean Choi
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <sound/soc.h>
+
+#include <plat/regs-ac97.h>
+#include <mach/dma.h>
+#include <plat/audio.h>
+
+#include "s3c-dma.h"
+#include "s3c-ac97.h"
+
+#define AC_CMD_ADDR(x) (x << 16)
+#define AC_CMD_DATA(x) (x & 0xffff)
+
+struct s3c_ac97_info {
+	unsigned           state;
+	struct clk         *ac97_clk;
+	void __iomem	   *regs;
+	struct mutex       lock;
+	struct completion  done;
+};
+static struct s3c_ac97_info s3c_ac97;
+
+static struct s3c2410_dma_client s3c_dma_client_out = {
+	.name = "AC97 PCMOut"
+};
+
+static struct s3c2410_dma_client s3c_dma_client_in = {
+	.name = "AC97 PCMIn"
+};
+
+static struct s3c2410_dma_client s3c_dma_client_micin = {
+	.name = "AC97 MicIn"
+};
+
+static struct s3c_dma_params s3c_ac97_pcm_out = {
+	.client		= &s3c_dma_client_out,
+	.dma_size	= 4,
+};
+
+static struct s3c_dma_params s3c_ac97_pcm_in = {
+	.client		= &s3c_dma_client_in,
+	.dma_size	= 4,
+};
+
+static struct s3c_dma_params s3c_ac97_mic_in = {
+	.client		= &s3c_dma_client_micin,
+	.dma_size	= 4,
+};
+
+static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	writel(S3C_AC97_GLBCTRL_COLDRESET,
+			s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+}
+
+static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+}
+
+static void s3c_ac97_activate(struct snd_ac97 *ac97)
+{
+	u32 ac_glbctrl, stat;
+
+	stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
+	switch (stat) {
+	case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
+		return;
+	case S3C_AC97_GLBSTAT_MAINSTATE_READY:
+	case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
+		break;
+	default:
+		s3c_ac97_cold_reset(ac97);
+		s3c_ac97_warm_reset(ac97);
+		break;
+	}
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	INIT_COMPLETION(s3c_ac97.done);
+
+	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
+		printk(KERN_ERR "AC97: Unable to activate!");
+}
+
+static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
+	unsigned short reg)
+{
+	u32 ac_glbctrl, ac_codec_cmd;
+	u32 stat, addr, data;
+
+	mutex_lock(&s3c_ac97.lock);
+
+	s3c_ac97_activate(ac97);
+
+	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
+	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	INIT_COMPLETION(s3c_ac97.done);
+
+	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
+		printk(KERN_ERR "AC97: Unable to read!");
+
+	stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
+	addr = (stat >> 16) & 0x7f;
+	data = (stat & 0xffff);
+
+	if (addr != reg)
+		printk(KERN_ERR "s3c-ac97: req addr = %02x,"
+				" rep addr = %02x\n", reg, addr);
+
+	mutex_unlock(&s3c_ac97.lock);
+
+	return (unsigned short)data;
+}
+
+static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+	unsigned short val)
+{
+	u32 ac_glbctrl, ac_codec_cmd;
+
+	mutex_lock(&s3c_ac97.lock);
+
+	s3c_ac97_activate(ac97);
+
+	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
+	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	INIT_COMPLETION(s3c_ac97.done);
+
+	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
+		printk(KERN_ERR "AC97: Unable to write!");
+
+	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
+	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	mutex_unlock(&s3c_ac97.lock);
+}
+
+static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
+{
+	u32 ac_glbctrl, ac_glbstat;
+
+	ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
+
+	if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
+
+		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
+		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+		ac_glbctrl |= (1<<30); /* Clear interrupt */
+		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+		complete(&s3c_ac97.done);
+	}
+
+	return IRQ_HANDLED;
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read       = s3c_ac97_read,
+	.write      = s3c_ac97_write,
+	.warm_reset = s3c_ac97_warm_reset,
+	.reset      = s3c_ac97_cold_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		cpu_dai->dma_data = &s3c_ac97_pcm_out;
+	else
+		cpu_dai->dma_data = &s3c_ac97_pcm_in;
+
+	return 0;
+}
+
+static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	u32 ac_glbctrl;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channel = ((struct s3c_dma_params *)
+		  rtd->dai->cpu_dai->dma_data)->channel;
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
+	else
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
+		else
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		break;
+	}
+
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
+	return 0;
+}
+
+static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return -ENODEV;
+	else
+		cpu_dai->dma_data = &s3c_ac97_mic_in;
+
+	return 0;
+}
+
+static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
+				    int cmd, struct snd_soc_dai *dai)
+{
+	u32 ac_glbctrl;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channel = ((struct s3c_dma_params *)
+		  rtd->dai->cpu_dai->dma_data)->channel;
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		break;
+	}
+
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
+	return 0;
+}
+
+#define S3C_AC97_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)
+
+static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
+	.hw_params	= s3c_ac97_hw_params,
+	.trigger	= s3c_ac97_trigger,
+};
+
+static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
+	.hw_params	= s3c_ac97_hw_mic_params,
+	.trigger	= s3c_ac97_mic_trigger,
+};
+
+struct snd_soc_dai s3c_ac97_dai[] = {
+	[S3C_AC97_DAI_PCM] = {
+		.name =	"s3c-ac97",
+		.id = S3C_AC97_DAI_PCM,
+		.ac97_control = 1,
+		.playback = {
+			.stream_name = "AC97 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = S3C_AC97_RATES,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.capture = {
+			.stream_name = "AC97 Capture",
+			/* NOTE: If the codec ouputs just one slot,
+			 * it *seems* our AC97 controller reads the only
+			 * valid slot(if either 3 or 4) for PCM-In.
+			 * For such cases, we record Mono.
+			 */
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = S3C_AC97_RATES,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.ops = &s3c_ac97_dai_ops,
+	},
+	[S3C_AC97_DAI_MIC] = {
+		.name = "s3c-ac97-mic",
+		.id = S3C_AC97_DAI_MIC,
+		.ac97_control = 1,
+		.capture = {
+			.stream_name = "AC97 Mic Capture",
+			.channels_min = 1,
+			/* NOTE: If the codec(like WM9713) can't ouput just
+			 * one slot, it *seems* our AC97 controller reads
+			 * two slots(if one of them is Slot-6) for MIC also.
+			 * For such cases, we record Stereo.
+			 */
+			.channels_max = 2,
+			.rates = S3C_AC97_RATES,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.ops = &s3c_ac97_mic_dai_ops,
+	},
+};
+EXPORT_SYMBOL_GPL(s3c_ac97_dai);
+
+static __devinit int s3c_ac97_probe(struct platform_device *pdev)
+{
+	struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
+	struct s3c_audio_pdata *ac97_pdata;
+	int ret;
+
+	ac97_pdata = pdev->dev.platform_data;
+	if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
+		dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
+		return -EINVAL;
+	}
+
+	/* Check for availability of necessary resource */
+	dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!dmatx_res) {
+		dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
+		return -ENXIO;
+	}
+
+	dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!dmarx_res) {
+		dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
+		return -ENXIO;
+	}
+
+	dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
+	if (!dmamic_res) {
+		dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
+		return -ENXIO;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "Unable to get register resource\n");
+		return -ENXIO;
+	}
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq_res) {
+		dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
+		return -ENXIO;
+	}
+
+	if (!request_mem_region(mem_res->start,
+				resource_size(mem_res), "s3c-ac97")) {
+		dev_err(&pdev->dev, "Unable to request register region\n");
+		return -EBUSY;
+	}
+
+	s3c_ac97_pcm_out.channel = dmatx_res->start;
+	s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
+	s3c_ac97_pcm_in.channel = dmarx_res->start;
+	s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
+	s3c_ac97_mic_in.channel = dmamic_res->start;
+	s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
+
+	init_completion(&s3c_ac97.done);
+	mutex_init(&s3c_ac97.lock);
+
+	s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
+	if (s3c_ac97.regs == NULL) {
+		dev_err(&pdev->dev, "Unable to ioremap register region\n");
+		ret = -ENXIO;
+		goto lb1;
+	}
+
+	s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
+	if (IS_ERR(s3c_ac97.ac97_clk)) {
+		dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
+		ret = -ENODEV;
+		goto lb2;
+	}
+	clk_enable(s3c_ac97.ac97_clk);
+
+	if (ac97_pdata->cfg_gpio(pdev)) {
+		dev_err(&pdev->dev, "Unable to configure gpio\n");
+		ret = -EINVAL;
+		goto lb3;
+	}
+
+	ret = request_irq(irq_res->start, s3c_ac97_irq,
+					IRQF_DISABLED, "AC97", NULL);
+	if (ret < 0) {
+		printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
+		goto lb4;
+	}
+
+	s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
+	s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
+
+	ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
+	if (ret)
+		goto lb5;
+
+	return 0;
+
+lb5:
+	free_irq(irq_res->start, NULL);
+lb4:
+lb3:
+	clk_disable(s3c_ac97.ac97_clk);
+	clk_put(s3c_ac97.ac97_clk);
+lb2:
+	iounmap(s3c_ac97.regs);
+lb1:
+	release_mem_region(mem_res->start, resource_size(mem_res));
+
+	return ret;
+}
+
+static __devexit int s3c_ac97_remove(struct platform_device *pdev)
+{
+	struct resource *mem_res, *irq_res;
+
+	snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (irq_res)
+		free_irq(irq_res->start, NULL);
+
+	clk_disable(s3c_ac97.ac97_clk);
+	clk_put(s3c_ac97.ac97_clk);
+
+	iounmap(s3c_ac97.regs);
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (mem_res)
+		release_mem_region(mem_res->start, resource_size(mem_res));
+
+	return 0;
+}
+
+static struct platform_driver s3c_ac97_driver = {
+	.probe  = s3c_ac97_probe,
+	.remove = s3c_ac97_remove,
+	.driver = {
+		.name = "s3c-ac97",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init s3c_ac97_init(void)
+{
+	return platform_driver_register(&s3c_ac97_driver);
+}
+module_init(s3c_ac97_init);
+
+static void __exit s3c_ac97_exit(void)
+{
+	platform_driver_unregister(&s3c_ac97_driver);
+}
+module_exit(s3c_ac97_exit);
+
+MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
+MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
new file mode 100644
index 0000000..2781983
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-ac97.h
@@ -0,0 +1,23 @@
+/* sound/soc/s3c24xx/s3c-ac97.h
+ *
+ * ALSA SoC Audio Layer - S3C AC97 Controller driver
+ * 	Evolved from s3c2443-ac97.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co. Ltd
+ * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
+ * 	Credits: Graeme Gregory, Sean Choi
+ *
+ * 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.
+ */
+
+#ifndef __S3C_AC97_H_
+#define __S3C_AC97_H_
+
+#define S3C_AC97_DAI_PCM 0
+#define S3C_AC97_DAI_MIC 1
+
+extern struct snd_soc_dai s3c_ac97_dai[];
+
+#endif /* __S3C_AC97_H_ */
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 3/3] ARM: SMDK64XX: Enable AC97 device
  2010-01-26  5:51   ` jassisinghbrar at gmail.com
@ 2010-01-26  5:51     ` jassisinghbrar at gmail.com
  -1 siblings, 0 replies; 44+ messages in thread
From: jassisinghbrar @ 2010-01-26  5:51 UTC (permalink / raw)
  To: alsa-devel, linux-arm-kernel
  Cc: ben-linux, broonie, lrg, linux-samsung-soc, Jassi Brar

From: Jassi Brar <jassi.brar@samsung.com>

Implementing basic support for Playback/Capture by SMDK's AC97
controller with WM9713 codec, this patch adds the ALSA machine
driver for AC97 device.

Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
---
 arch/arm/mach-s3c6410/mach-smdk6410.c |    9 ++-
 sound/soc/s3c24xx/Kconfig             |    8 ++
 sound/soc/s3c24xx/Makefile            |    2 +
 sound/soc/s3c24xx/smdk_wm9713.c       |  147 +++++++++++++++++++++++++++++++++
 4 files changed, 165 insertions(+), 1 deletions(-)
 create mode 100644 sound/soc/s3c24xx/smdk_wm9713.c

diff --git a/arch/arm/mach-s3c6410/mach-smdk6410.c b/arch/arm/mach-s3c6410/mach-smdk6410.c
index eba345f..259ebbb 100644
--- a/arch/arm/mach-s3c6410/mach-smdk6410.c
+++ b/arch/arm/mach-s3c6410/mach-smdk6410.c
@@ -55,6 +55,7 @@
 #include <plat/gpio-cfg.h>
 
 #include <plat/s3c6410.h>
+#include <plat/audio.h>
 #include <plat/clock.h>
 #include <plat/devs.h>
 #include <plat/cpu.h>
@@ -253,7 +254,9 @@ static struct platform_device *smdk6410_devices[] __initdata = {
 	&smdk6410_b_pwr_5v,
 #endif
 	&smdk6410_lcd_powerdev,
-
+#ifdef CONFIG_SND_SOC_SMDK_WM9713
+	&s3c64xx_device_ac97,
+#endif
 	&smdk6410_smsc911x,
 };
 
@@ -475,6 +478,10 @@ static void __init smdk6410_machine_init(void)
 	i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
 	i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
 
+#ifdef CONFIG_SND_SOC_SMDK_WM9713
+	s3c64xx_ac97_setup_gpio(S3C64XX_AC97_GPD);
+#endif
+
 	platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
 }
 
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index ad3690e..d1c6f93 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -115,3 +115,11 @@ config SND_S3C24XX_SOC_SIMTEC_HERMES
 	select SND_S3C24XX_SOC_I2S
 	select SND_SOC_TLV320AIC3X
 	select SND_S3C24XX_SOC_SIMTEC
+
+config SND_SOC_SMDK_WM9713
+	tristate "SoC AC97 Audio support for SMDK with WM9713"
+	depends on SND_S3C24XX_SOC && MACH_SMDK6410
+	select SND_SOC_WM9713
+	select SND_S3C_SOC_AC97
+	help
+	  Sat Y if you want to add support for SoC audio on the SMDK.
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index b7411bd..1117678 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -28,6 +28,7 @@ snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
 snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
 snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
 snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o
+snd-soc-smdk-wm9713-objs := smdk_wm9713.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
@@ -39,3 +40,4 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
 obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
+obj-$(CONFIG_SND_SOC_SMDK_WM9713) += snd-soc-smdk-wm9713.o
diff --git a/sound/soc/s3c24xx/smdk_wm9713.c b/sound/soc/s3c24xx/smdk_wm9713.c
new file mode 100644
index 0000000..4ce851e
--- /dev/null
+++ b/sound/soc/s3c24xx/smdk_wm9713.c
@@ -0,0 +1,147 @@
+/*
+ * smdk_wm9713.c  --  SoC audio for SMDK
+ *
+ * Copyright 2010 Samsung Electronics Co. Ltd.
+ * Author: Jaswinder Singh Brar <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 as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm9713.h"
+#include "s3c-dma.h"
+#include "s3c-ac97.h"
+
+static struct snd_soc_card smdk;
+
+/*
+ Playback :-
+	Headphone Playback Switch - On
+	$ amixer cset numid=4 1
+
+	Right Headphone Out Mux - Headphone
+	$ amixer cset numid=92 2
+	Left Headphone Out Mux - Headphone
+	$ amixer cset numid=93 2
+
+	Right HP Mixer PCM Playback Switch - On
+	$ amixer cset numid=75 1
+	Left HP Mixer PCM Playback Switch - On
+	$ amixer cset numid=81 1
+
+ Capture (LineIn):-
+	Right Capture Source - Line
+	$ amixer cset numid=86 2
+	Left Capture Source - Line
+	$ amixer cset numid=87 2
+
+ Capture (MicIn):-
+	Right Capture Source - Mic1
+	$ amixer cset numid=86 0
+	Left Capture Source - Mic1
+	$ amixer cset numid=87 0
+*/
+
+static int smdk_hifi_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	/* CAPTURE - Stereo LineIn
+	 * Currently there is no other way to change ACLink-In slots
+	 * of Wm9713, so we make do with this call.
+	 */
+	/* Slots-3,4*/
+	soc_ac97_ops.write(NULL, 0x5c, 0);
+
+	return 0;
+}
+
+static struct snd_soc_ops smdk_hifi_ops = {
+	.hw_params = smdk_hifi_hw_params,
+};
+
+static int smdk_mic_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	/* CAPTURE - Mono MicIn
+	 * Currently there is no other way to change ACLink-In slots
+	 * of Wm9713, so we make do with this call.
+	 */
+	/* Slots-6,9*/
+	soc_ac97_ops.write(NULL, 0x5c, 2);
+
+	return 0;
+}
+
+static struct snd_soc_ops smdk_mic_ops = {
+	.hw_params = smdk_mic_hw_params,
+};
+
+static struct snd_soc_dai_link smdk_dai[] = {
+	[0] = {
+		.name = "AC97",
+		.stream_name = "AC97 HiFi",
+		.cpu_dai = &s3c_ac97_dai[S3C_AC97_DAI_PCM],
+		.codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
+		.ops = &smdk_hifi_ops,
+	},
+	[1] = {
+		.name = "AC97",
+		.stream_name = "AC97 Mic",
+		.cpu_dai = &s3c_ac97_dai[S3C_AC97_DAI_MIC],
+		.codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
+		.ops = &smdk_mic_ops,
+	},
+};
+
+static struct snd_soc_card smdk = {
+	.name = "SMDK",
+	.platform = &s3c24xx_soc_platform,
+	.dai_link = smdk_dai,
+	.num_links = ARRAY_SIZE(smdk_dai),
+};
+
+static struct snd_soc_device smdk_snd_ac97_devdata = {
+	.card = &smdk,
+	.codec_dev = &soc_codec_dev_wm9713,
+};
+
+static struct platform_device *smdk_snd_ac97_device;
+
+static int __init smdk_init(void)
+{
+	int ret;
+
+	smdk_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+	if (!smdk_snd_ac97_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(smdk_snd_ac97_device,
+			     &smdk_snd_ac97_devdata);
+	smdk_snd_ac97_devdata.dev = &smdk_snd_ac97_device->dev;
+
+	ret = platform_device_add(smdk_snd_ac97_device);
+	if (ret)
+		platform_device_put(smdk_snd_ac97_device);
+
+	return ret;
+}
+
+static void __exit smdk_exit(void)
+{
+	platform_device_unregister(smdk_snd_ac97_device);
+}
+
+module_init(smdk_init);
+module_exit(smdk_exit);
+
+/* Module information */
+MODULE_AUTHOR("Jaswinder Singh Brar, jassi.brar@samsung.com");
+MODULE_DESCRIPTION("ALSA SoC SMDK+WM9713");
+MODULE_LICENSE("GPL");
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH 3/3] ARM: SMDK64XX: Enable AC97 device
@ 2010-01-26  5:51     ` jassisinghbrar at gmail.com
  0 siblings, 0 replies; 44+ messages in thread
From: jassisinghbrar at gmail.com @ 2010-01-26  5:51 UTC (permalink / raw)
  To: linux-arm-kernel

From: Jassi Brar <jassi.brar@samsung.com>

Implementing basic support for Playback/Capture by SMDK's AC97
controller with WM9713 codec, this patch adds the ALSA machine
driver for AC97 device.

Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
---
 arch/arm/mach-s3c6410/mach-smdk6410.c |    9 ++-
 sound/soc/s3c24xx/Kconfig             |    8 ++
 sound/soc/s3c24xx/Makefile            |    2 +
 sound/soc/s3c24xx/smdk_wm9713.c       |  147 +++++++++++++++++++++++++++++++++
 4 files changed, 165 insertions(+), 1 deletions(-)
 create mode 100644 sound/soc/s3c24xx/smdk_wm9713.c

diff --git a/arch/arm/mach-s3c6410/mach-smdk6410.c b/arch/arm/mach-s3c6410/mach-smdk6410.c
index eba345f..259ebbb 100644
--- a/arch/arm/mach-s3c6410/mach-smdk6410.c
+++ b/arch/arm/mach-s3c6410/mach-smdk6410.c
@@ -55,6 +55,7 @@
 #include <plat/gpio-cfg.h>
 
 #include <plat/s3c6410.h>
+#include <plat/audio.h>
 #include <plat/clock.h>
 #include <plat/devs.h>
 #include <plat/cpu.h>
@@ -253,7 +254,9 @@ static struct platform_device *smdk6410_devices[] __initdata = {
 	&smdk6410_b_pwr_5v,
 #endif
 	&smdk6410_lcd_powerdev,
-
+#ifdef CONFIG_SND_SOC_SMDK_WM9713
+	&s3c64xx_device_ac97,
+#endif
 	&smdk6410_smsc911x,
 };
 
@@ -475,6 +478,10 @@ static void __init smdk6410_machine_init(void)
 	i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
 	i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
 
+#ifdef CONFIG_SND_SOC_SMDK_WM9713
+	s3c64xx_ac97_setup_gpio(S3C64XX_AC97_GPD);
+#endif
+
 	platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));
 }
 
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index ad3690e..d1c6f93 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -115,3 +115,11 @@ config SND_S3C24XX_SOC_SIMTEC_HERMES
 	select SND_S3C24XX_SOC_I2S
 	select SND_SOC_TLV320AIC3X
 	select SND_S3C24XX_SOC_SIMTEC
+
+config SND_SOC_SMDK_WM9713
+	tristate "SoC AC97 Audio support for SMDK with WM9713"
+	depends on SND_S3C24XX_SOC && MACH_SMDK6410
+	select SND_SOC_WM9713
+	select SND_S3C_SOC_AC97
+	help
+	  Sat Y if you want to add support for SoC audio on the SMDK.
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index b7411bd..1117678 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -28,6 +28,7 @@ snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
 snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
 snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
 snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o
+snd-soc-smdk-wm9713-objs := smdk_wm9713.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
@@ -39,3 +40,4 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
 obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
+obj-$(CONFIG_SND_SOC_SMDK_WM9713) += snd-soc-smdk-wm9713.o
diff --git a/sound/soc/s3c24xx/smdk_wm9713.c b/sound/soc/s3c24xx/smdk_wm9713.c
new file mode 100644
index 0000000..4ce851e
--- /dev/null
+++ b/sound/soc/s3c24xx/smdk_wm9713.c
@@ -0,0 +1,147 @@
+/*
+ * smdk_wm9713.c  --  SoC audio for SMDK
+ *
+ * Copyright 2010 Samsung Electronics Co. Ltd.
+ * Author: Jaswinder Singh Brar <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 as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm9713.h"
+#include "s3c-dma.h"
+#include "s3c-ac97.h"
+
+static struct snd_soc_card smdk;
+
+/*
+ Playback :-
+	Headphone Playback Switch - On
+	$ amixer cset numid=4 1
+
+	Right Headphone Out Mux - Headphone
+	$ amixer cset numid=92 2
+	Left Headphone Out Mux - Headphone
+	$ amixer cset numid=93 2
+
+	Right HP Mixer PCM Playback Switch - On
+	$ amixer cset numid=75 1
+	Left HP Mixer PCM Playback Switch - On
+	$ amixer cset numid=81 1
+
+ Capture (LineIn):-
+	Right Capture Source - Line
+	$ amixer cset numid=86 2
+	Left Capture Source - Line
+	$ amixer cset numid=87 2
+
+ Capture (MicIn):-
+	Right Capture Source - Mic1
+	$ amixer cset numid=86 0
+	Left Capture Source - Mic1
+	$ amixer cset numid=87 0
+*/
+
+static int smdk_hifi_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	/* CAPTURE - Stereo LineIn
+	 * Currently there is no other way to change ACLink-In slots
+	 * of Wm9713, so we make do with this call.
+	 */
+	/* Slots-3,4*/
+	soc_ac97_ops.write(NULL, 0x5c, 0);
+
+	return 0;
+}
+
+static struct snd_soc_ops smdk_hifi_ops = {
+	.hw_params = smdk_hifi_hw_params,
+};
+
+static int smdk_mic_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	/* CAPTURE - Mono MicIn
+	 * Currently there is no other way to change ACLink-In slots
+	 * of Wm9713, so we make do with this call.
+	 */
+	/* Slots-6,9*/
+	soc_ac97_ops.write(NULL, 0x5c, 2);
+
+	return 0;
+}
+
+static struct snd_soc_ops smdk_mic_ops = {
+	.hw_params = smdk_mic_hw_params,
+};
+
+static struct snd_soc_dai_link smdk_dai[] = {
+	[0] = {
+		.name = "AC97",
+		.stream_name = "AC97 HiFi",
+		.cpu_dai = &s3c_ac97_dai[S3C_AC97_DAI_PCM],
+		.codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
+		.ops = &smdk_hifi_ops,
+	},
+	[1] = {
+		.name = "AC97",
+		.stream_name = "AC97 Mic",
+		.cpu_dai = &s3c_ac97_dai[S3C_AC97_DAI_MIC],
+		.codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
+		.ops = &smdk_mic_ops,
+	},
+};
+
+static struct snd_soc_card smdk = {
+	.name = "SMDK",
+	.platform = &s3c24xx_soc_platform,
+	.dai_link = smdk_dai,
+	.num_links = ARRAY_SIZE(smdk_dai),
+};
+
+static struct snd_soc_device smdk_snd_ac97_devdata = {
+	.card = &smdk,
+	.codec_dev = &soc_codec_dev_wm9713,
+};
+
+static struct platform_device *smdk_snd_ac97_device;
+
+static int __init smdk_init(void)
+{
+	int ret;
+
+	smdk_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+	if (!smdk_snd_ac97_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(smdk_snd_ac97_device,
+			     &smdk_snd_ac97_devdata);
+	smdk_snd_ac97_devdata.dev = &smdk_snd_ac97_device->dev;
+
+	ret = platform_device_add(smdk_snd_ac97_device);
+	if (ret)
+		platform_device_put(smdk_snd_ac97_device);
+
+	return ret;
+}
+
+static void __exit smdk_exit(void)
+{
+	platform_device_unregister(smdk_snd_ac97_device);
+}
+
+module_init(smdk_init);
+module_exit(smdk_exit);
+
+/* Module information */
+MODULE_AUTHOR("Jaswinder Singh Brar, jassi.brar at samsung.com");
+MODULE_DESCRIPTION("ALSA SoC SMDK+WM9713");
+MODULE_LICENSE("GPL");
-- 
1.6.2.5

^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26  5:51   ` jassisinghbrar at gmail.com
@ 2010-01-26  7:32     ` jassi brar
  -1 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26  7:32 UTC (permalink / raw)
  To: alsa-devel, linux-arm-kernel
  Cc: ben-linux, broonie, lrg, linux-samsung-soc, Jassi Brar

On Tue, Jan 26, 2010 at 2:51 PM,  <jassisinghbrar@gmail.com> wrote:
> From: Jassi Brar <jassi.brar@samsung.com>
>
> Add the AC97 controller driver for Samsung SoCs that have one.
>
> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
> ---
>  sound/soc/s3c24xx/Kconfig    |    6 +-
>  sound/soc/s3c24xx/Makefile   |    3 +-
>  sound/soc/s3c24xx/s3c-ac97.c |  535 ++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/s3c24xx/s3c-ac97.h |   23 ++
>  4 files changed, 565 insertions(+), 2 deletions(-)
>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.h
>
> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
> index b489f1a..ad3690e 100644
> --- a/sound/soc/s3c24xx/Kconfig
> +++ b/sound/soc/s3c24xx/Kconfig
> @@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
>        select S3C2410_DMA
>        select AC97_BUS
>        select SND_SOC_AC97_BUS
> -
> +
> +config SND_S3C_SOC_AC97
> +       tristate
> +       select SND_SOC_AC97_BUS
> +
>  config SND_S3C24XX_SOC_NEO1973_WM8753
>        tristate "SoC I2S Audio support for NEO1973 - WM8753"
>        depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
> diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
> index b744657..b7411bd 100644
> --- a/sound/soc/s3c24xx/Makefile
> +++ b/sound/soc/s3c24xx/Makefile
> @@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
> +snd-soc-s3c-ac97-objs := s3c-ac97.o
>  snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
>  snd-soc-s3c-pcm-objs := s3c-pcm.o
>
>  obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
>  obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
> @@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
>  obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
> -
> diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
> new file mode 100644
> index 0000000..acb8f51
> --- /dev/null
> +++ b/sound/soc/s3c24xx/s3c-ac97.c
> @@ -0,0 +1,535 @@
> +/* sound/soc/s3c24xx/s3c-ac97.c
> + *
> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
> + *     Evolved from s3c2443-ac97.c
> + *
> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
> + *     Author: Jaswinder Singh <jassi.brar@samsung.com>
> + *     Credits: Graeme Gregory, Sean Choi
> + *
> + * 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/init.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/clk.h>
> +
> +#include <sound/soc.h>
> +
> +#include <plat/regs-ac97.h>
> +#include <mach/dma.h>
> +#include <plat/audio.h>
> +
> +#include "s3c-dma.h"
> +#include "s3c-ac97.h"
> +
> +#define AC_CMD_ADDR(x) (x << 16)
> +#define AC_CMD_DATA(x) (x & 0xffff)
> +
> +struct s3c_ac97_info {
> +       unsigned           state;
> +       struct clk         *ac97_clk;
> +       void __iomem       *regs;
> +       struct mutex       lock;
> +       struct completion  done;
> +};
> +static struct s3c_ac97_info s3c_ac97;
> +
> +static struct s3c2410_dma_client s3c_dma_client_out = {
> +       .name = "AC97 PCMOut"
> +};
> +
> +static struct s3c2410_dma_client s3c_dma_client_in = {
> +       .name = "AC97 PCMIn"
> +};
> +
> +static struct s3c2410_dma_client s3c_dma_client_micin = {
> +       .name = "AC97 MicIn"
> +};
> +
> +static struct s3c_dma_params s3c_ac97_pcm_out = {
> +       .client         = &s3c_dma_client_out,
> +       .dma_size       = 4,
> +};
> +
> +static struct s3c_dma_params s3c_ac97_pcm_in = {
> +       .client         = &s3c_dma_client_in,
> +       .dma_size       = 4,
> +};
> +
> +static struct s3c_dma_params s3c_ac97_mic_in = {
> +       .client         = &s3c_dma_client_micin,
> +       .dma_size       = 4,
> +};
> +
> +static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
> +{
> +       writel(S3C_AC97_GLBCTRL_COLDRESET,
> +                       s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       msleep(1);
> +
> +       writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       msleep(1);
> +}
> +
> +static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
> +{
> +       writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       msleep(1);
> +
> +       writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       msleep(1);
> +}
> +
> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
> +{
> +       u32 ac_glbctrl, stat;
> +
> +       stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
> +       switch (stat) {
> +       case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
> +               return;
> +       case S3C_AC97_GLBSTAT_MAINSTATE_READY:
> +       case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
> +               break;
> +       default:
> +               s3c_ac97_cold_reset(ac97);
> +               s3c_ac97_warm_reset(ac97);
> +               break;
> +       }
> +
> +       ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> +       writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       msleep(1);
> +
> +       ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
> +       writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       msleep(1);
> +
> +       ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> +       writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +       INIT_COMPLETION(s3c_ac97.done);
> +
> +       if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +               printk(KERN_ERR "AC97: Unable to activate!");
> +}
> +
> +static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
> +       unsigned short reg)
> +{
> +       u32 ac_glbctrl, ac_codec_cmd;
> +       u32 stat, addr, data;
> +
> +       mutex_lock(&s3c_ac97.lock);
> +
> +       s3c_ac97_activate(ac97);
> +
> +       ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +       ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
> +       writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> +       udelay(50);
> +
> +       ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> +       writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +       INIT_COMPLETION(s3c_ac97.done);
> +
> +       if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +               printk(KERN_ERR "AC97: Unable to read!");
> +
> +       stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
> +       addr = (stat >> 16) & 0x7f;
> +       data = (stat & 0xffff);
> +
> +       if (addr != reg)
> +               printk(KERN_ERR "s3c-ac97: req addr = %02x,"
> +                               " rep addr = %02x\n", reg, addr);
> +
> +       mutex_unlock(&s3c_ac97.lock);
> +
> +       return (unsigned short)data;
> +}
> +
> +static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
> +       unsigned short val)
> +{
> +       u32 ac_glbctrl, ac_codec_cmd;
> +
> +       mutex_lock(&s3c_ac97.lock);
> +
> +       s3c_ac97_activate(ac97);
> +
> +       ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +       ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
> +       writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> +       udelay(50);
> +
> +       ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> +       writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +       INIT_COMPLETION(s3c_ac97.done);
> +
> +       if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +               printk(KERN_ERR "AC97: Unable to write!");
> +
> +       ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +       ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
> +       writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> +       mutex_unlock(&s3c_ac97.lock);
> +}
> +
> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
> +{
> +       u32 ac_glbctrl, ac_glbstat;
> +
> +       ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
> +
> +       if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
> +
> +               ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +               ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
> +               writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +               ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +               ac_glbctrl |= (1<<30); /* Clear interrupt */
> +               writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +               complete(&s3c_ac97.done);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +struct snd_ac97_bus_ops soc_ac97_ops = {
> +       .read       = s3c_ac97_read,
> +       .write      = s3c_ac97_write,
> +       .warm_reset = s3c_ac97_warm_reset,
> +       .reset      = s3c_ac97_cold_reset,
> +};
> +EXPORT_SYMBOL_GPL(soc_ac97_ops);
> +
> +static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
> +                                 struct snd_pcm_hw_params *params,
> +                                 struct snd_soc_dai *dai)
> +{
> +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> +
> +       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +               cpu_dai->dma_data = &s3c_ac97_pcm_out;
> +       else
> +               cpu_dai->dma_data = &s3c_ac97_pcm_in;
> +
> +       return 0;
> +}
> +
> +static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
> +                               struct snd_soc_dai *dai)
> +{
> +       u32 ac_glbctrl;
> +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +       int channel = ((struct s3c_dma_params *)
> +                 rtd->dai->cpu_dai->dma_data)->channel;
> +
> +       ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> +               ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
> +       else
> +               ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
> +
> +       switch (cmd) {
> +       case SNDRV_PCM_TRIGGER_START:
> +       case SNDRV_PCM_TRIGGER_RESUME:
> +       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> +                       ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
> +               else
> +                       ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
> +               break;
> +
> +       case SNDRV_PCM_TRIGGER_STOP:
> +       case SNDRV_PCM_TRIGGER_SUSPEND:
> +       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +               break;
> +       }
> +
> +       writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +       s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
> +
> +       return 0;
> +}
> +
> +static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
> +                                     struct snd_pcm_hw_params *params,
> +                                     struct snd_soc_dai *dai)
> +{
> +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> +
> +       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +               return -ENODEV;
> +       else
> +               cpu_dai->dma_data = &s3c_ac97_mic_in;
> +
> +       return 0;
> +}
> +
> +static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
> +                                   int cmd, struct snd_soc_dai *dai)
> +{
> +       u32 ac_glbctrl;
> +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +       int channel = ((struct s3c_dma_params *)
> +                 rtd->dai->cpu_dai->dma_data)->channel;
> +
> +       ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +       ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
> +
> +       switch (cmd) {
> +       case SNDRV_PCM_TRIGGER_START:
> +       case SNDRV_PCM_TRIGGER_RESUME:
> +       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +               ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
> +               break;
> +
> +       case SNDRV_PCM_TRIGGER_STOP:
> +       case SNDRV_PCM_TRIGGER_SUSPEND:
> +       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +               break;
> +       }
> +
> +       writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +       s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
> +
> +       return 0;
> +}
> +
> +#define S3C_AC97_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)
> +
> +static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
> +       .hw_params      = s3c_ac97_hw_params,
> +       .trigger        = s3c_ac97_trigger,
> +};
> +
> +static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
> +       .hw_params      = s3c_ac97_hw_mic_params,
> +       .trigger        = s3c_ac97_mic_trigger,
> +};
> +
> +struct snd_soc_dai s3c_ac97_dai[] = {
> +       [S3C_AC97_DAI_PCM] = {
> +               .name = "s3c-ac97",
> +               .id = S3C_AC97_DAI_PCM,
> +               .ac97_control = 1,
> +               .playback = {
> +                       .stream_name = "AC97 Playback",
> +                       .channels_min = 2,
> +                       .channels_max = 2,
> +                       .rates = S3C_AC97_RATES,
> +                       .formats = SNDRV_PCM_FMTBIT_S16_LE,},
> +               .capture = {
> +                       .stream_name = "AC97 Capture",
> +                       /* NOTE: If the codec ouputs just one slot,
> +                        * it *seems* our AC97 controller reads the only
> +                        * valid slot(if either 3 or 4) for PCM-In.
> +                        * For such cases, we record Mono.
> +                        */
> +                       .channels_min = 1,
  I would like to remove this anomaly and set channels_min = 2

> +                       .channels_max = 2,
> +                       .rates = S3C_AC97_RATES,
> +                       .formats = SNDRV_PCM_FMTBIT_S16_LE,},
> +               .ops = &s3c_ac97_dai_ops,
> +       },
> +       [S3C_AC97_DAI_MIC] = {
> +               .name = "s3c-ac97-mic",
> +               .id = S3C_AC97_DAI_MIC,
> +               .ac97_control = 1,
> +               .capture = {
> +                       .stream_name = "AC97 Mic Capture",
> +                       .channels_min = 1,
> +                       /* NOTE: If the codec(like WM9713) can't ouput just
> +                        * one slot, it *seems* our AC97 controller reads
> +                        * two slots(if one of them is Slot-6) for MIC also.
> +                        * For such cases, we record Stereo.
> +                        */
> +                       .channels_max = 2,
  I would like to remove this anomaly and set channels_max = 1

 And remove the second DAI_LINK in machine driver.

Please continue review and give me other feedback. Thanks.

> +                       .rates = S3C_AC97_RATES,
> +                       .formats = SNDRV_PCM_FMTBIT_S16_LE,},
> +               .ops = &s3c_ac97_mic_dai_ops,
> +       },
> +};
> +EXPORT_SYMBOL_GPL(s3c_ac97_dai);
> +
> +static __devinit int s3c_ac97_probe(struct platform_device *pdev)
> +{
> +       struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
> +       struct s3c_audio_pdata *ac97_pdata;
> +       int ret;
> +
> +       ac97_pdata = pdev->dev.platform_data;
> +       if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
> +               dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Check for availability of necessary resource */
> +       dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
> +       if (!dmatx_res) {
> +               dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
> +               return -ENXIO;
> +       }
> +
> +       dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
> +       if (!dmarx_res) {
> +               dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
> +               return -ENXIO;
> +       }
> +
> +       dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
> +       if (!dmamic_res) {
> +               dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
> +               return -ENXIO;
> +       }
> +
> +       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!mem_res) {
> +               dev_err(&pdev->dev, "Unable to get register resource\n");
> +               return -ENXIO;
> +       }
> +
> +       irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +       if (!irq_res) {
> +               dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
> +               return -ENXIO;
> +       }
> +
> +       if (!request_mem_region(mem_res->start,
> +                               resource_size(mem_res), "s3c-ac97")) {
> +               dev_err(&pdev->dev, "Unable to request register region\n");
> +               return -EBUSY;
> +       }
> +
> +       s3c_ac97_pcm_out.channel = dmatx_res->start;
> +       s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
> +       s3c_ac97_pcm_in.channel = dmarx_res->start;
> +       s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
> +       s3c_ac97_mic_in.channel = dmamic_res->start;
> +       s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
> +
> +       init_completion(&s3c_ac97.done);
> +       mutex_init(&s3c_ac97.lock);
> +
> +       s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
> +       if (s3c_ac97.regs == NULL) {
> +               dev_err(&pdev->dev, "Unable to ioremap register region\n");
> +               ret = -ENXIO;
> +               goto lb1;
> +       }
> +
> +       s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
> +       if (IS_ERR(s3c_ac97.ac97_clk)) {
> +               dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
> +               ret = -ENODEV;
> +               goto lb2;
> +       }
> +       clk_enable(s3c_ac97.ac97_clk);
> +
> +       if (ac97_pdata->cfg_gpio(pdev)) {
> +               dev_err(&pdev->dev, "Unable to configure gpio\n");
> +               ret = -EINVAL;
> +               goto lb3;
> +       }
> +
> +       ret = request_irq(irq_res->start, s3c_ac97_irq,
> +                                       IRQF_DISABLED, "AC97", NULL);
> +       if (ret < 0) {
> +               printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
> +               goto lb4;
> +       }
> +
> +       s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
> +       s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
> +
> +       ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
> +       if (ret)
> +               goto lb5;
> +
> +       return 0;
> +
> +lb5:
> +       free_irq(irq_res->start, NULL);
> +lb4:
> +lb3:
> +       clk_disable(s3c_ac97.ac97_clk);
> +       clk_put(s3c_ac97.ac97_clk);
> +lb2:
> +       iounmap(s3c_ac97.regs);
> +lb1:
> +       release_mem_region(mem_res->start, resource_size(mem_res));
> +
> +       return ret;
> +}
> +
> +static __devexit int s3c_ac97_remove(struct platform_device *pdev)
> +{
> +       struct resource *mem_res, *irq_res;
> +
> +       snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
> +
> +       irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +       if (irq_res)
> +               free_irq(irq_res->start, NULL);
> +
> +       clk_disable(s3c_ac97.ac97_clk);
> +       clk_put(s3c_ac97.ac97_clk);
> +
> +       iounmap(s3c_ac97.regs);
> +
> +       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (mem_res)
> +               release_mem_region(mem_res->start, resource_size(mem_res));
> +
> +       return 0;
> +}
> +
> +static struct platform_driver s3c_ac97_driver = {
> +       .probe  = s3c_ac97_probe,
> +       .remove = s3c_ac97_remove,
> +       .driver = {
> +               .name = "s3c-ac97",
> +               .owner = THIS_MODULE,
> +       },
> +};
> +
> +static int __init s3c_ac97_init(void)
> +{
> +       return platform_driver_register(&s3c_ac97_driver);
> +}
> +module_init(s3c_ac97_init);
> +
> +static void __exit s3c_ac97_exit(void)
> +{
> +       platform_driver_unregister(&s3c_ac97_driver);
> +}
> +module_exit(s3c_ac97_exit);
> +
> +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
> +MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
> new file mode 100644
> index 0000000..2781983
> --- /dev/null
> +++ b/sound/soc/s3c24xx/s3c-ac97.h
> @@ -0,0 +1,23 @@
> +/* sound/soc/s3c24xx/s3c-ac97.h
> + *
> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
> + *     Evolved from s3c2443-ac97.h
> + *
> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
> + *     Author: Jaswinder Singh <jassi.brar@samsung.com>
> + *     Credits: Graeme Gregory, Sean Choi
> + *
> + * 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.
> + */
> +
> +#ifndef __S3C_AC97_H_
> +#define __S3C_AC97_H_
> +
> +#define S3C_AC97_DAI_PCM 0
> +#define S3C_AC97_DAI_MIC 1
> +
> +extern struct snd_soc_dai s3c_ac97_dai[];
> +
> +#endif /* __S3C_AC97_H_ */
> --
> 1.6.2.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26  7:32     ` jassi brar
  0 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26  7:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 2:51 PM,  <jassisinghbrar@gmail.com> wrote:
> From: Jassi Brar <jassi.brar@samsung.com>
>
> Add the AC97 controller driver for Samsung SoCs that have one.
>
> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
> ---
> ?sound/soc/s3c24xx/Kconfig ? ?| ? ?6 +-
> ?sound/soc/s3c24xx/Makefile ? | ? ?3 +-
> ?sound/soc/s3c24xx/s3c-ac97.c | ?535 ++++++++++++++++++++++++++++++++++++++++++
> ?sound/soc/s3c24xx/s3c-ac97.h | ? 23 ++
> ?4 files changed, 565 insertions(+), 2 deletions(-)
> ?create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
> ?create mode 100644 sound/soc/s3c24xx/s3c-ac97.h
>
> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
> index b489f1a..ad3690e 100644
> --- a/sound/soc/s3c24xx/Kconfig
> +++ b/sound/soc/s3c24xx/Kconfig
> @@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
> ? ? ? ?select S3C2410_DMA
> ? ? ? ?select AC97_BUS
> ? ? ? ?select SND_SOC_AC97_BUS
> -
> +
> +config SND_S3C_SOC_AC97
> + ? ? ? tristate
> + ? ? ? select SND_SOC_AC97_BUS
> +
> ?config SND_S3C24XX_SOC_NEO1973_WM8753
> ? ? ? ?tristate "SoC I2S Audio support for NEO1973 - WM8753"
> ? ? ? ?depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
> diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
> index b744657..b7411bd 100644
> --- a/sound/soc/s3c24xx/Makefile
> +++ b/sound/soc/s3c24xx/Makefile
> @@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
> +snd-soc-s3c-ac97-objs := s3c-ac97.o
> ?snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
> ?snd-soc-s3c-pcm-objs := s3c-pcm.o
>
> ?obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
> ?obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
> ?obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
> @@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
> ?obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
> ?obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
> ?obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
> -
> diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
> new file mode 100644
> index 0000000..acb8f51
> --- /dev/null
> +++ b/sound/soc/s3c24xx/s3c-ac97.c
> @@ -0,0 +1,535 @@
> +/* sound/soc/s3c24xx/s3c-ac97.c
> + *
> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
> + * ? ? Evolved from s3c2443-ac97.c
> + *
> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
> + * ? ? Author: Jaswinder Singh <jassi.brar@samsung.com>
> + * ? ? Credits: Graeme Gregory, Sean Choi
> + *
> + * 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/init.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/clk.h>
> +
> +#include <sound/soc.h>
> +
> +#include <plat/regs-ac97.h>
> +#include <mach/dma.h>
> +#include <plat/audio.h>
> +
> +#include "s3c-dma.h"
> +#include "s3c-ac97.h"
> +
> +#define AC_CMD_ADDR(x) (x << 16)
> +#define AC_CMD_DATA(x) (x & 0xffff)
> +
> +struct s3c_ac97_info {
> + ? ? ? unsigned ? ? ? ? ? state;
> + ? ? ? struct clk ? ? ? ? *ac97_clk;
> + ? ? ? void __iomem ? ? ? *regs;
> + ? ? ? struct mutex ? ? ? lock;
> + ? ? ? struct completion ?done;
> +};
> +static struct s3c_ac97_info s3c_ac97;
> +
> +static struct s3c2410_dma_client s3c_dma_client_out = {
> + ? ? ? .name = "AC97 PCMOut"
> +};
> +
> +static struct s3c2410_dma_client s3c_dma_client_in = {
> + ? ? ? .name = "AC97 PCMIn"
> +};
> +
> +static struct s3c2410_dma_client s3c_dma_client_micin = {
> + ? ? ? .name = "AC97 MicIn"
> +};
> +
> +static struct s3c_dma_params s3c_ac97_pcm_out = {
> + ? ? ? .client ? ? ? ? = &s3c_dma_client_out,
> + ? ? ? .dma_size ? ? ? = 4,
> +};
> +
> +static struct s3c_dma_params s3c_ac97_pcm_in = {
> + ? ? ? .client ? ? ? ? = &s3c_dma_client_in,
> + ? ? ? .dma_size ? ? ? = 4,
> +};
> +
> +static struct s3c_dma_params s3c_ac97_mic_in = {
> + ? ? ? .client ? ? ? ? = &s3c_dma_client_micin,
> + ? ? ? .dma_size ? ? ? = 4,
> +};
> +
> +static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
> +{
> + ? ? ? writel(S3C_AC97_GLBCTRL_COLDRESET,
> + ? ? ? ? ? ? ? ? ? ? ? s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? msleep(1);
> +
> + ? ? ? writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? msleep(1);
> +}
> +
> +static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
> +{
> + ? ? ? writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? msleep(1);
> +
> + ? ? ? writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? msleep(1);
> +}
> +
> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
> +{
> + ? ? ? u32 ac_glbctrl, stat;
> +
> + ? ? ? stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
> + ? ? ? switch (stat) {
> + ? ? ? case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
> + ? ? ? ? ? ? ? return;
> + ? ? ? case S3C_AC97_GLBSTAT_MAINSTATE_READY:
> + ? ? ? case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? s3c_ac97_cold_reset(ac97);
> + ? ? ? ? ? ? ? s3c_ac97_warm_reset(ac97);
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> + ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? msleep(1);
> +
> + ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
> + ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? msleep(1);
> +
> + ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> + ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> + ? ? ? INIT_COMPLETION(s3c_ac97.done);
> +
> + ? ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> + ? ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to activate!");
> +}
> +
> +static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
> + ? ? ? unsigned short reg)
> +{
> + ? ? ? u32 ac_glbctrl, ac_codec_cmd;
> + ? ? ? u32 stat, addr, data;
> +
> + ? ? ? mutex_lock(&s3c_ac97.lock);
> +
> + ? ? ? s3c_ac97_activate(ac97);
> +
> + ? ? ? ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> + ? ? ? ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
> + ? ? ? writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> + ? ? ? udelay(50);
> +
> + ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> + ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> + ? ? ? INIT_COMPLETION(s3c_ac97.done);
> +
> + ? ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> + ? ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to read!");
> +
> + ? ? ? stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
> + ? ? ? addr = (stat >> 16) & 0x7f;
> + ? ? ? data = (stat & 0xffff);
> +
> + ? ? ? if (addr != reg)
> + ? ? ? ? ? ? ? printk(KERN_ERR "s3c-ac97: req addr = %02x,"
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? " rep addr = %02x\n", reg, addr);
> +
> + ? ? ? mutex_unlock(&s3c_ac97.lock);
> +
> + ? ? ? return (unsigned short)data;
> +}
> +
> +static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
> + ? ? ? unsigned short val)
> +{
> + ? ? ? u32 ac_glbctrl, ac_codec_cmd;
> +
> + ? ? ? mutex_lock(&s3c_ac97.lock);
> +
> + ? ? ? s3c_ac97_activate(ac97);
> +
> + ? ? ? ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> + ? ? ? ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
> + ? ? ? writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> + ? ? ? udelay(50);
> +
> + ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> + ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> + ? ? ? INIT_COMPLETION(s3c_ac97.done);
> +
> + ? ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> + ? ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to write!");
> +
> + ? ? ? ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> + ? ? ? ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
> + ? ? ? writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> + ? ? ? mutex_unlock(&s3c_ac97.lock);
> +}
> +
> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
> +{
> + ? ? ? u32 ac_glbctrl, ac_glbstat;
> +
> + ? ? ? ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
> +
> + ? ? ? if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
> +
> + ? ? ? ? ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
> + ? ? ? ? ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> + ? ? ? ? ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? ? ? ? ? ac_glbctrl |= (1<<30); /* Clear interrupt */
> + ? ? ? ? ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> + ? ? ? ? ? ? ? complete(&s3c_ac97.done);
> + ? ? ? }
> +
> + ? ? ? return IRQ_HANDLED;
> +}
> +
> +struct snd_ac97_bus_ops soc_ac97_ops = {
> + ? ? ? .read ? ? ? = s3c_ac97_read,
> + ? ? ? .write ? ? ?= s3c_ac97_write,
> + ? ? ? .warm_reset = s3c_ac97_warm_reset,
> + ? ? ? .reset ? ? ?= s3c_ac97_cold_reset,
> +};
> +EXPORT_SYMBOL_GPL(soc_ac97_ops);
> +
> +static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_pcm_hw_params *params,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_soc_dai *dai)
> +{
> + ? ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + ? ? ? struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> +
> + ? ? ? if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> + ? ? ? ? ? ? ? cpu_dai->dma_data = &s3c_ac97_pcm_out;
> + ? ? ? else
> + ? ? ? ? ? ? ? cpu_dai->dma_data = &s3c_ac97_pcm_in;
> +
> + ? ? ? return 0;
> +}
> +
> +static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_soc_dai *dai)
> +{
> + ? ? ? u32 ac_glbctrl;
> + ? ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + ? ? ? int channel = ((struct s3c_dma_params *)
> + ? ? ? ? ? ? ? ? rtd->dai->cpu_dai->dma_data)->channel;
> +
> + ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> + ? ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
> + ? ? ? else
> + ? ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
> +
> + ? ? ? switch (cmd) {
> + ? ? ? case SNDRV_PCM_TRIGGER_START:
> + ? ? ? case SNDRV_PCM_TRIGGER_RESUME:
> + ? ? ? case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + ? ? ? ? ? ? ? if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> + ? ? ? ? ? ? ? ? ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
> + ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
> + ? ? ? ? ? ? ? break;
> +
> + ? ? ? case SNDRV_PCM_TRIGGER_STOP:
> + ? ? ? case SNDRV_PCM_TRIGGER_SUSPEND:
> + ? ? ? case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> + ? ? ? s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
> +
> + ? ? ? return 0;
> +}
> +
> +static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_pcm_hw_params *params,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_soc_dai *dai)
> +{
> + ? ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + ? ? ? struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> +
> + ? ? ? if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> + ? ? ? ? ? ? ? return -ENODEV;
> + ? ? ? else
> + ? ? ? ? ? ? ? cpu_dai->dma_data = &s3c_ac97_mic_in;
> +
> + ? ? ? return 0;
> +}
> +
> +static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int cmd, struct snd_soc_dai *dai)
> +{
> + ? ? ? u32 ac_glbctrl;
> + ? ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + ? ? ? int channel = ((struct s3c_dma_params *)
> + ? ? ? ? ? ? ? ? rtd->dai->cpu_dai->dma_data)->channel;
> +
> + ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> + ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
> +
> + ? ? ? switch (cmd) {
> + ? ? ? case SNDRV_PCM_TRIGGER_START:
> + ? ? ? case SNDRV_PCM_TRIGGER_RESUME:
> + ? ? ? case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + ? ? ? ? ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
> + ? ? ? ? ? ? ? break;
> +
> + ? ? ? case SNDRV_PCM_TRIGGER_STOP:
> + ? ? ? case SNDRV_PCM_TRIGGER_SUSPEND:
> + ? ? ? case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> + ? ? ? s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
> +
> + ? ? ? return 0;
> +}
> +
> +#define S3C_AC97_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)
> +
> +static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
> + ? ? ? .hw_params ? ? ?= s3c_ac97_hw_params,
> + ? ? ? .trigger ? ? ? ?= s3c_ac97_trigger,
> +};
> +
> +static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
> + ? ? ? .hw_params ? ? ?= s3c_ac97_hw_mic_params,
> + ? ? ? .trigger ? ? ? ?= s3c_ac97_mic_trigger,
> +};
> +
> +struct snd_soc_dai s3c_ac97_dai[] = {
> + ? ? ? [S3C_AC97_DAI_PCM] = {
> + ? ? ? ? ? ? ? .name = "s3c-ac97",
> + ? ? ? ? ? ? ? .id = S3C_AC97_DAI_PCM,
> + ? ? ? ? ? ? ? .ac97_control = 1,
> + ? ? ? ? ? ? ? .playback = {
> + ? ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Playback",
> + ? ? ? ? ? ? ? ? ? ? ? .channels_min = 2,
> + ? ? ? ? ? ? ? ? ? ? ? .channels_max = 2,
> + ? ? ? ? ? ? ? ? ? ? ? .rates = S3C_AC97_RATES,
> + ? ? ? ? ? ? ? ? ? ? ? .formats = SNDRV_PCM_FMTBIT_S16_LE,},
> + ? ? ? ? ? ? ? .capture = {
> + ? ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Capture",
> + ? ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec ouputs just one slot,
> + ? ? ? ? ? ? ? ? ? ? ? ?* it *seems* our AC97 controller reads the only
> + ? ? ? ? ? ? ? ? ? ? ? ?* valid slot(if either 3 or 4) for PCM-In.
> + ? ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Mono.
> + ? ? ? ? ? ? ? ? ? ? ? ?*/
> + ? ? ? ? ? ? ? ? ? ? ? .channels_min = 1,
  I would like to remove this anomaly and set channels_min = 2

> + ? ? ? ? ? ? ? ? ? ? ? .channels_max = 2,
> + ? ? ? ? ? ? ? ? ? ? ? .rates = S3C_AC97_RATES,
> + ? ? ? ? ? ? ? ? ? ? ? .formats = SNDRV_PCM_FMTBIT_S16_LE,},
> + ? ? ? ? ? ? ? .ops = &s3c_ac97_dai_ops,
> + ? ? ? },
> + ? ? ? [S3C_AC97_DAI_MIC] = {
> + ? ? ? ? ? ? ? .name = "s3c-ac97-mic",
> + ? ? ? ? ? ? ? .id = S3C_AC97_DAI_MIC,
> + ? ? ? ? ? ? ? .ac97_control = 1,
> + ? ? ? ? ? ? ? .capture = {
> + ? ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Mic Capture",
> + ? ? ? ? ? ? ? ? ? ? ? .channels_min = 1,
> + ? ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec(like WM9713) can't ouput just
> + ? ? ? ? ? ? ? ? ? ? ? ?* one slot, it *seems* our AC97 controller reads
> + ? ? ? ? ? ? ? ? ? ? ? ?* two slots(if one of them is Slot-6) for MIC also.
> + ? ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Stereo.
> + ? ? ? ? ? ? ? ? ? ? ? ?*/
> + ? ? ? ? ? ? ? ? ? ? ? .channels_max = 2,
  I would like to remove this anomaly and set channels_max = 1

 And remove the second DAI_LINK in machine driver.

Please continue review and give me other feedback. Thanks.

> + ? ? ? ? ? ? ? ? ? ? ? .rates = S3C_AC97_RATES,
> + ? ? ? ? ? ? ? ? ? ? ? .formats = SNDRV_PCM_FMTBIT_S16_LE,},
> + ? ? ? ? ? ? ? .ops = &s3c_ac97_mic_dai_ops,
> + ? ? ? },
> +};
> +EXPORT_SYMBOL_GPL(s3c_ac97_dai);
> +
> +static __devinit int s3c_ac97_probe(struct platform_device *pdev)
> +{
> + ? ? ? struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
> + ? ? ? struct s3c_audio_pdata *ac97_pdata;
> + ? ? ? int ret;
> +
> + ? ? ? ac97_pdata = pdev->dev.platform_data;
> + ? ? ? if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? /* Check for availability of necessary resource */
> + ? ? ? dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
> + ? ? ? if (!dmatx_res) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
> + ? ? ? ? ? ? ? return -ENXIO;
> + ? ? ? }
> +
> + ? ? ? dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
> + ? ? ? if (!dmarx_res) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
> + ? ? ? ? ? ? ? return -ENXIO;
> + ? ? ? }
> +
> + ? ? ? dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
> + ? ? ? if (!dmamic_res) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
> + ? ? ? ? ? ? ? return -ENXIO;
> + ? ? ? }
> +
> + ? ? ? mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ? ? ? if (!mem_res) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get register resource\n");
> + ? ? ? ? ? ? ? return -ENXIO;
> + ? ? ? }
> +
> + ? ? ? irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + ? ? ? if (!irq_res) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
> + ? ? ? ? ? ? ? return -ENXIO;
> + ? ? ? }
> +
> + ? ? ? if (!request_mem_region(mem_res->start,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? resource_size(mem_res), "s3c-ac97")) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to request register region\n");
> + ? ? ? ? ? ? ? return -EBUSY;
> + ? ? ? }
> +
> + ? ? ? s3c_ac97_pcm_out.channel = dmatx_res->start;
> + ? ? ? s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
> + ? ? ? s3c_ac97_pcm_in.channel = dmarx_res->start;
> + ? ? ? s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
> + ? ? ? s3c_ac97_mic_in.channel = dmamic_res->start;
> + ? ? ? s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
> +
> + ? ? ? init_completion(&s3c_ac97.done);
> + ? ? ? mutex_init(&s3c_ac97.lock);
> +
> + ? ? ? s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
> + ? ? ? if (s3c_ac97.regs == NULL) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to ioremap register region\n");
> + ? ? ? ? ? ? ? ret = -ENXIO;
> + ? ? ? ? ? ? ? goto lb1;
> + ? ? ? }
> +
> + ? ? ? s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
> + ? ? ? if (IS_ERR(s3c_ac97.ac97_clk)) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
> + ? ? ? ? ? ? ? ret = -ENODEV;
> + ? ? ? ? ? ? ? goto lb2;
> + ? ? ? }
> + ? ? ? clk_enable(s3c_ac97.ac97_clk);
> +
> + ? ? ? if (ac97_pdata->cfg_gpio(pdev)) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to configure gpio\n");
> + ? ? ? ? ? ? ? ret = -EINVAL;
> + ? ? ? ? ? ? ? goto lb3;
> + ? ? ? }
> +
> + ? ? ? ret = request_irq(irq_res->start, s3c_ac97_irq,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IRQF_DISABLED, "AC97", NULL);
> + ? ? ? if (ret < 0) {
> + ? ? ? ? ? ? ? printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
> + ? ? ? ? ? ? ? goto lb4;
> + ? ? ? }
> +
> + ? ? ? s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
> + ? ? ? s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
> +
> + ? ? ? ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? goto lb5;
> +
> + ? ? ? return 0;
> +
> +lb5:
> + ? ? ? free_irq(irq_res->start, NULL);
> +lb4:
> +lb3:
> + ? ? ? clk_disable(s3c_ac97.ac97_clk);
> + ? ? ? clk_put(s3c_ac97.ac97_clk);
> +lb2:
> + ? ? ? iounmap(s3c_ac97.regs);
> +lb1:
> + ? ? ? release_mem_region(mem_res->start, resource_size(mem_res));
> +
> + ? ? ? return ret;
> +}
> +
> +static __devexit int s3c_ac97_remove(struct platform_device *pdev)
> +{
> + ? ? ? struct resource *mem_res, *irq_res;
> +
> + ? ? ? snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
> +
> + ? ? ? irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> + ? ? ? if (irq_res)
> + ? ? ? ? ? ? ? free_irq(irq_res->start, NULL);
> +
> + ? ? ? clk_disable(s3c_ac97.ac97_clk);
> + ? ? ? clk_put(s3c_ac97.ac97_clk);
> +
> + ? ? ? iounmap(s3c_ac97.regs);
> +
> + ? ? ? mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ? ? ? if (mem_res)
> + ? ? ? ? ? ? ? release_mem_region(mem_res->start, resource_size(mem_res));
> +
> + ? ? ? return 0;
> +}
> +
> +static struct platform_driver s3c_ac97_driver = {
> + ? ? ? .probe ?= s3c_ac97_probe,
> + ? ? ? .remove = s3c_ac97_remove,
> + ? ? ? .driver = {
> + ? ? ? ? ? ? ? .name = "s3c-ac97",
> + ? ? ? ? ? ? ? .owner = THIS_MODULE,
> + ? ? ? },
> +};
> +
> +static int __init s3c_ac97_init(void)
> +{
> + ? ? ? return platform_driver_register(&s3c_ac97_driver);
> +}
> +module_init(s3c_ac97_init);
> +
> +static void __exit s3c_ac97_exit(void)
> +{
> + ? ? ? platform_driver_unregister(&s3c_ac97_driver);
> +}
> +module_exit(s3c_ac97_exit);
> +
> +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
> +MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
> new file mode 100644
> index 0000000..2781983
> --- /dev/null
> +++ b/sound/soc/s3c24xx/s3c-ac97.h
> @@ -0,0 +1,23 @@
> +/* sound/soc/s3c24xx/s3c-ac97.h
> + *
> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
> + * ? ? Evolved from s3c2443-ac97.h
> + *
> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
> + * ? ? Author: Jaswinder Singh <jassi.brar@samsung.com>
> + * ? ? Credits: Graeme Gregory, Sean Choi
> + *
> + * 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.
> + */
> +
> +#ifndef __S3C_AC97_H_
> +#define __S3C_AC97_H_
> +
> +#define S3C_AC97_DAI_PCM 0
> +#define S3C_AC97_DAI_MIC 1
> +
> +extern struct snd_soc_dai s3c_ac97_dai[];
> +
> +#endif /* __S3C_AC97_H_ */
> --
> 1.6.2.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 1/3] ARM: S3C64XX: Add AC97 platform resources
  2010-01-26  5:51 ` jassisinghbrar at gmail.com
@ 2010-01-26 10:12   ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:12 UTC (permalink / raw)
  To: jassisinghbrar
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, ben-linux,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 02:51:39PM +0900, jassisinghbrar@gmail.com wrote:
> From: Jassi Brar <jassi.brar@samsung.com>
> 
> This patch defines the platform device and the resources: IRQ, DMA and MEM,
> needed by the AC97 controller driver.
> 
> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>

Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 1/3] ARM: S3C64XX: Add AC97 platform resources
@ 2010-01-26 10:12   ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 02:51:39PM +0900, jassisinghbrar at gmail.com wrote:
> From: Jassi Brar <jassi.brar@samsung.com>
> 
> This patch defines the platform device and the resources: IRQ, DMA and MEM,
> needed by the AC97 controller driver.
> 
> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>

Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 3/3] ARM: SMDK64XX: Enable AC97 device
  2010-01-26  5:51     ` jassisinghbrar at gmail.com
@ 2010-01-26 10:19       ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:19 UTC (permalink / raw)
  To: jassisinghbrar
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, ben-linux,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 02:51:41PM +0900, jassisinghbrar@gmail.com wrote:
> From: Jassi Brar <jassi.brar@samsung.com>
> 
> Implementing basic support for Playback/Capture by SMDK's AC97
> controller with WM9713 codec, this patch adds the ALSA machine
> driver for AC97 device.

Might be better to split this into an arch/arm patch and a sound/soc
patch - they don't appear to depend on each other so it'd ease merging.

> +static int smdk_hifi_hw_params(struct snd_pcm_substream *substream,
> +				struct snd_pcm_hw_params *params)
> +{
> +	/* CAPTURE - Stereo LineIn
> +	 * Currently there is no other way to change ACLink-In slots
> +	 * of Wm9713, so we make do with this call.
> +	 */
> +	/* Slots-3,4*/
> +	soc_ac97_ops.write(NULL, 0x5c, 0);
> +
> +	return 0;
> +}

This one needs a bit more explanation - why are you changing the AC97
slot?  Is this a general problem with the S3C AC97 controller?  It'd
also be better to use the symbolic register name rather than a number.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 3/3] ARM: SMDK64XX: Enable AC97 device
@ 2010-01-26 10:19       ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 02:51:41PM +0900, jassisinghbrar at gmail.com wrote:
> From: Jassi Brar <jassi.brar@samsung.com>
> 
> Implementing basic support for Playback/Capture by SMDK's AC97
> controller with WM9713 codec, this patch adds the ALSA machine
> driver for AC97 device.

Might be better to split this into an arch/arm patch and a sound/soc
patch - they don't appear to depend on each other so it'd ease merging.

> +static int smdk_hifi_hw_params(struct snd_pcm_substream *substream,
> +				struct snd_pcm_hw_params *params)
> +{
> +	/* CAPTURE - Stereo LineIn
> +	 * Currently there is no other way to change ACLink-In slots
> +	 * of Wm9713, so we make do with this call.
> +	 */
> +	/* Slots-3,4*/
> +	soc_ac97_ops.write(NULL, 0x5c, 0);
> +
> +	return 0;
> +}

This one needs a bit more explanation - why are you changing the AC97
slot?  Is this a general problem with the S3C AC97 controller?  It'd
also be better to use the symbolic register name rather than a number.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26  5:51   ` jassisinghbrar at gmail.com
@ 2010-01-26 10:23     ` Ben Dooks
  -1 siblings, 0 replies; 44+ messages in thread
From: Ben Dooks @ 2010-01-26 10:23 UTC (permalink / raw)
  To: jassisinghbrar
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, broonie, ben-linux,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar@gmail.com wrote:
> From: Jassi Brar <jassi.brar@samsung.com>
> 
> Add the AC97 controller driver for Samsung SoCs that have one.
> 
> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
> ---
>  sound/soc/s3c24xx/Kconfig    |    6 +-
>  sound/soc/s3c24xx/Makefile   |    3 +-
>  sound/soc/s3c24xx/s3c-ac97.c |  535 ++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/s3c24xx/s3c-ac97.h |   23 ++
>  4 files changed, 565 insertions(+), 2 deletions(-)
>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.h
> 
> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
> index b489f1a..ad3690e 100644
> --- a/sound/soc/s3c24xx/Kconfig
> +++ b/sound/soc/s3c24xx/Kconfig
> @@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
>  	select S3C2410_DMA
>  	select AC97_BUS
>  	select SND_SOC_AC97_BUS
> -	
> +
> +config SND_S3C_SOC_AC97
> +	tristate
> +	select SND_SOC_AC97_BUS
> +
>  config SND_S3C24XX_SOC_NEO1973_WM8753
>  	tristate "SoC I2S Audio support for NEO1973 - WM8753"
>  	depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
> diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
> index b744657..b7411bd 100644
> --- a/sound/soc/s3c24xx/Makefile
> +++ b/sound/soc/s3c24xx/Makefile
> @@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
> +snd-soc-s3c-ac97-objs := s3c-ac97.o
>  snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
>  snd-soc-s3c-pcm-objs := s3c-pcm.o
>  
>  obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
>  obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
> @@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
>  obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
> -
> diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
> new file mode 100644
> index 0000000..acb8f51
> --- /dev/null
> +++ b/sound/soc/s3c24xx/s3c-ac97.c
> @@ -0,0 +1,535 @@
> +/* sound/soc/s3c24xx/s3c-ac97.c
> + *
> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
> + * 	Evolved from s3c2443-ac97.c
> + *
> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
> + * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
> + * 	Credits: Graeme Gregory, Sean Choi
> + *
> + * 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/init.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/clk.h>
> +
> +#include <sound/soc.h>
> +
> +#include <plat/regs-ac97.h>
> +#include <mach/dma.h>
> +#include <plat/audio.h>
> +
> +#include "s3c-dma.h"
> +#include "s3c-ac97.h"
> +
> +#define AC_CMD_ADDR(x) (x << 16)
> +#define AC_CMD_DATA(x) (x & 0xffff)
> +
> +struct s3c_ac97_info {
> +	unsigned           state;
> +	struct clk         *ac97_clk;
> +	void __iomem	   *regs;
> +	struct mutex       lock;
> +	struct completion  done;
> +};
> +static struct s3c_ac97_info s3c_ac97;
> +
> +static struct s3c2410_dma_client s3c_dma_client_out = {
> +	.name = "AC97 PCMOut"
> +};
> +
> +static struct s3c2410_dma_client s3c_dma_client_in = {
> +	.name = "AC97 PCMIn"
> +};
> +
> +static struct s3c2410_dma_client s3c_dma_client_micin = {
> +	.name = "AC97 MicIn"
> +};
> +
> +static struct s3c_dma_params s3c_ac97_pcm_out = {
> +	.client		= &s3c_dma_client_out,
> +	.dma_size	= 4,
> +};
> +
> +static struct s3c_dma_params s3c_ac97_pcm_in = {
> +	.client		= &s3c_dma_client_in,
> +	.dma_size	= 4,
> +};
> +
> +static struct s3c_dma_params s3c_ac97_mic_in = {
> +	.client		= &s3c_dma_client_micin,
> +	.dma_size	= 4,
> +};
> +
> +static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
> +{
> +	writel(S3C_AC97_GLBCTRL_COLDRESET,
> +			s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +
> +	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +}
> +
> +static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
> +{
> +	writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +
> +	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +}

It would be nice t osee something to convert a 'struct snd_ac97' to a 
'struct s3c_ac97_info' as this is being passed in to most place and
would also help if there is ever >1 block.

> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
> +{
> +	u32 ac_glbctrl, stat;
> +
> +	stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
> +	switch (stat) {
> +	case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
> +		return;
> +	case S3C_AC97_GLBSTAT_MAINSTATE_READY:
> +	case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
> +		break;
> +	default:
> +		s3c_ac97_cold_reset(ac97);
> +		s3c_ac97_warm_reset(ac97);
> +		break;
> +	}
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +
> +	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	INIT_COMPLETION(s3c_ac97.done);
> +
> +	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +		printk(KERN_ERR "AC97: Unable to activate!");
> +}
> +
> +static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
> +	unsigned short reg)
> +{
> +	u32 ac_glbctrl, ac_codec_cmd;
> +	u32 stat, addr, data;
> +
> +	mutex_lock(&s3c_ac97.lock);
> +
> +	s3c_ac97_activate(ac97);
> +
> +	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +	ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
> +	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> +	udelay(50);
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	INIT_COMPLETION(s3c_ac97.done);
> +
> +	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +		printk(KERN_ERR "AC97: Unable to read!");
> +
> +	stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
> +	addr = (stat >> 16) & 0x7f;
> +	data = (stat & 0xffff);
> +
> +	if (addr != reg)
> +		printk(KERN_ERR "s3c-ac97: req addr = %02x,"
> +				" rep addr = %02x\n", reg, addr);
> +
> +	mutex_unlock(&s3c_ac97.lock);
> +
> +	return (unsigned short)data;
> +}
> +
> +static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
> +	unsigned short val)
> +{
> +	u32 ac_glbctrl, ac_codec_cmd;
> +
> +	mutex_lock(&s3c_ac97.lock);
> +
> +	s3c_ac97_activate(ac97);
> +
> +	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +	ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
> +	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> +	udelay(50);
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	INIT_COMPLETION(s3c_ac97.done);
> +
> +	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +		printk(KERN_ERR "AC97: Unable to write!");
> +
> +	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +	ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
> +	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> +	mutex_unlock(&s3c_ac97.lock);
> +}
> +
> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
> +{
> +	u32 ac_glbctrl, ac_glbstat;
> +
> +	ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
> +
> +	if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
> +
> +		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
> +		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +		ac_glbctrl |= (1<<30); /* Clear interrupt */
> +		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +		complete(&s3c_ac97.done);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +struct snd_ac97_bus_ops soc_ac97_ops = {
> +	.read       = s3c_ac97_read,
> +	.write      = s3c_ac97_write,
> +	.warm_reset = s3c_ac97_warm_reset,
> +	.reset      = s3c_ac97_cold_reset,
> +};
> +EXPORT_SYMBOL_GPL(soc_ac97_ops);
> +
> +static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
> +				  struct snd_pcm_hw_params *params,
> +				  struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		cpu_dai->dma_data = &s3c_ac97_pcm_out;
> +	else
> +		cpu_dai->dma_data = &s3c_ac97_pcm_in;
> +
> +	return 0;
> +}
> +
> +static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
> +				struct snd_soc_dai *dai)
> +{
> +	u32 ac_glbctrl;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	int channel = ((struct s3c_dma_params *)
> +		  rtd->dai->cpu_dai->dma_data)->channel;
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> +		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
> +	else
> +		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> +			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
> +		else
> +			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +		break;
> +	}
> +
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
> +
> +	return 0;
> +}
> +
> +static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
> +				      struct snd_pcm_hw_params *params,
> +				      struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		return -ENODEV;
> +	else
> +		cpu_dai->dma_data = &s3c_ac97_mic_in;
> +
> +	return 0;
> +}
> +
> +static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
> +				    int cmd, struct snd_soc_dai *dai)
> +{
> +	u32 ac_glbctrl;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	int channel = ((struct s3c_dma_params *)
> +		  rtd->dai->cpu_dai->dma_data)->channel;
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +		break;
> +	}
> +
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
> +
> +	return 0;
> +}
> +
> +#define S3C_AC97_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)
> +
> +static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
> +	.hw_params	= s3c_ac97_hw_params,
> +	.trigger	= s3c_ac97_trigger,
> +};
> +
> +static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
> +	.hw_params	= s3c_ac97_hw_mic_params,
> +	.trigger	= s3c_ac97_mic_trigger,
> +};
> +
> +struct snd_soc_dai s3c_ac97_dai[] = {
> +	[S3C_AC97_DAI_PCM] = {
> +		.name =	"s3c-ac97",
> +		.id = S3C_AC97_DAI_PCM,
> +		.ac97_control = 1,
> +		.playback = {
> +			.stream_name = "AC97 Playback",
> +			.channels_min = 2,
> +			.channels_max = 2,
> +			.rates = S3C_AC97_RATES,
> +			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
> +		.capture = {
> +			.stream_name = "AC97 Capture",
> +			/* NOTE: If the codec ouputs just one slot,
> +			 * it *seems* our AC97 controller reads the only
> +			 * valid slot(if either 3 or 4) for PCM-In.
> +			 * For such cases, we record Mono.
> +			 */
> +			.channels_min = 1,
> +			.channels_max = 2,
> +			.rates = S3C_AC97_RATES,
> +			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
> +		.ops = &s3c_ac97_dai_ops,
> +	},
> +	[S3C_AC97_DAI_MIC] = {
> +		.name = "s3c-ac97-mic",
> +		.id = S3C_AC97_DAI_MIC,
> +		.ac97_control = 1,
> +		.capture = {
> +			.stream_name = "AC97 Mic Capture",
> +			.channels_min = 1,
> +			/* NOTE: If the codec(like WM9713) can't ouput just
> +			 * one slot, it *seems* our AC97 controller reads
> +			 * two slots(if one of them is Slot-6) for MIC also.
> +			 * For such cases, we record Stereo.
> +			 */
> +			.channels_max = 2,
> +			.rates = S3C_AC97_RATES,
> +			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
> +		.ops = &s3c_ac97_mic_dai_ops,
> +	},
> +};
> +EXPORT_SYMBOL_GPL(s3c_ac97_dai);
> +
> +static __devinit int s3c_ac97_probe(struct platform_device *pdev)
> +{
> +	struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
> +	struct s3c_audio_pdata *ac97_pdata;
> +	int ret;
> +
> +	ac97_pdata = pdev->dev.platform_data;
> +	if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
> +		dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Check for availability of necessary resource */
> +	dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
> +	if (!dmatx_res) {
> +		dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
> +		return -ENXIO;
> +	}
> +
> +	dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
> +	if (!dmarx_res) {
> +		dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
> +		return -ENXIO;
> +	}
> +
> +	dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
> +	if (!dmamic_res) {
> +		dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
> +		return -ENXIO;
> +	}
> +
> +	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!mem_res) {
> +		dev_err(&pdev->dev, "Unable to get register resource\n");
> +		return -ENXIO;
> +	}
> +
> +	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (!irq_res) {
> +		dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
> +		return -ENXIO;
> +	}
> +
> +	if (!request_mem_region(mem_res->start,
> +				resource_size(mem_res), "s3c-ac97")) {
> +		dev_err(&pdev->dev, "Unable to request register region\n");
> +		return -EBUSY;
> +	}
> +
> +	s3c_ac97_pcm_out.channel = dmatx_res->start;
> +	s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
> +	s3c_ac97_pcm_in.channel = dmarx_res->start;
> +	s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
> +	s3c_ac97_mic_in.channel = dmamic_res->start;
> +	s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
> +
> +	init_completion(&s3c_ac97.done);
> +	mutex_init(&s3c_ac97.lock);
> +
> +	s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
> +	if (s3c_ac97.regs == NULL) {
> +		dev_err(&pdev->dev, "Unable to ioremap register region\n");
> +		ret = -ENXIO;
> +		goto lb1;
> +	}
> +
> +	s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
> +	if (IS_ERR(s3c_ac97.ac97_clk)) {
> +		dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
> +		ret = -ENODEV;
> +		goto lb2;
> +	}
> +	clk_enable(s3c_ac97.ac97_clk);
> +
> +	if (ac97_pdata->cfg_gpio(pdev)) {
> +		dev_err(&pdev->dev, "Unable to configure gpio\n");
> +		ret = -EINVAL;
> +		goto lb3;
> +	}
> +
> +	ret = request_irq(irq_res->start, s3c_ac97_irq,
> +					IRQF_DISABLED, "AC97", NULL);
> +	if (ret < 0) {
> +		printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
> +		goto lb4;
> +	}
> +
> +	s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
> +	s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
> +
> +	ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
> +	if (ret)
> +		goto lb5;
> +
> +	return 0;
> +
> +lb5:
> +	free_irq(irq_res->start, NULL);
> +lb4:
> +lb3:
> +	clk_disable(s3c_ac97.ac97_clk);
> +	clk_put(s3c_ac97.ac97_clk);
> +lb2:
> +	iounmap(s3c_ac97.regs);
> +lb1:
> +	release_mem_region(mem_res->start, resource_size(mem_res));
> +
> +	return ret;
> +}
> +
> +static __devexit int s3c_ac97_remove(struct platform_device *pdev)
> +{
> +	struct resource *mem_res, *irq_res;
> +
> +	snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
> +
> +	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (irq_res)
> +		free_irq(irq_res->start, NULL);
> +
> +	clk_disable(s3c_ac97.ac97_clk);
> +	clk_put(s3c_ac97.ac97_clk);
> +
> +	iounmap(s3c_ac97.regs);
> +
> +	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (mem_res)
> +		release_mem_region(mem_res->start, resource_size(mem_res));
> +
> +	return 0;
> +}
> +
> +static struct platform_driver s3c_ac97_driver = {
> +	.probe  = s3c_ac97_probe,
> +	.remove = s3c_ac97_remove,
> +	.driver = {
> +		.name = "s3c-ac97",
> +		.owner = THIS_MODULE,
> +	},
> +};
> +
> +static int __init s3c_ac97_init(void)
> +{
> +	return platform_driver_register(&s3c_ac97_driver);
> +}
> +module_init(s3c_ac97_init);
> +
> +static void __exit s3c_ac97_exit(void)
> +{
> +	platform_driver_unregister(&s3c_ac97_driver);
> +}
> +module_exit(s3c_ac97_exit);
> +
> +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
> +MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
> new file mode 100644
> index 0000000..2781983
> --- /dev/null
> +++ b/sound/soc/s3c24xx/s3c-ac97.h
> @@ -0,0 +1,23 @@
> +/* sound/soc/s3c24xx/s3c-ac97.h
> + *
> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
> + * 	Evolved from s3c2443-ac97.h
> + *
> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
> + * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
> + * 	Credits: Graeme Gregory, Sean Choi
> + *
> + * 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.
> + */
> +
> +#ifndef __S3C_AC97_H_
> +#define __S3C_AC97_H_
> +
> +#define S3C_AC97_DAI_PCM 0
> +#define S3C_AC97_DAI_MIC 1
> +
> +extern struct snd_soc_dai s3c_ac97_dai[];
> +
> +#endif /* __S3C_AC97_H_ */
> -- 
> 1.6.2.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Out of interest, how similar are the two blocks and could the s3c2443
case be elided into this one with some minimal checks for which one is
currently in use?

-- 
Ben

Q:      What's a light-year?
A:      One-third less calories than a regular year.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 10:23     ` Ben Dooks
  0 siblings, 0 replies; 44+ messages in thread
From: Ben Dooks @ 2010-01-26 10:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar at gmail.com wrote:
> From: Jassi Brar <jassi.brar@samsung.com>
> 
> Add the AC97 controller driver for Samsung SoCs that have one.
> 
> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
> ---
>  sound/soc/s3c24xx/Kconfig    |    6 +-
>  sound/soc/s3c24xx/Makefile   |    3 +-
>  sound/soc/s3c24xx/s3c-ac97.c |  535 ++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/s3c24xx/s3c-ac97.h |   23 ++
>  4 files changed, 565 insertions(+), 2 deletions(-)
>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.h
> 
> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
> index b489f1a..ad3690e 100644
> --- a/sound/soc/s3c24xx/Kconfig
> +++ b/sound/soc/s3c24xx/Kconfig
> @@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
>  	select S3C2410_DMA
>  	select AC97_BUS
>  	select SND_SOC_AC97_BUS
> -	
> +
> +config SND_S3C_SOC_AC97
> +	tristate
> +	select SND_SOC_AC97_BUS
> +
>  config SND_S3C24XX_SOC_NEO1973_WM8753
>  	tristate "SoC I2S Audio support for NEO1973 - WM8753"
>  	depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
> diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
> index b744657..b7411bd 100644
> --- a/sound/soc/s3c24xx/Makefile
> +++ b/sound/soc/s3c24xx/Makefile
> @@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
> +snd-soc-s3c-ac97-objs := s3c-ac97.o
>  snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
>  snd-soc-s3c-pcm-objs := s3c-pcm.o
>  
>  obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
>  obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
> @@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
>  obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
> -
> diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
> new file mode 100644
> index 0000000..acb8f51
> --- /dev/null
> +++ b/sound/soc/s3c24xx/s3c-ac97.c
> @@ -0,0 +1,535 @@
> +/* sound/soc/s3c24xx/s3c-ac97.c
> + *
> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
> + * 	Evolved from s3c2443-ac97.c
> + *
> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
> + * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
> + * 	Credits: Graeme Gregory, Sean Choi
> + *
> + * 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/init.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/clk.h>
> +
> +#include <sound/soc.h>
> +
> +#include <plat/regs-ac97.h>
> +#include <mach/dma.h>
> +#include <plat/audio.h>
> +
> +#include "s3c-dma.h"
> +#include "s3c-ac97.h"
> +
> +#define AC_CMD_ADDR(x) (x << 16)
> +#define AC_CMD_DATA(x) (x & 0xffff)
> +
> +struct s3c_ac97_info {
> +	unsigned           state;
> +	struct clk         *ac97_clk;
> +	void __iomem	   *regs;
> +	struct mutex       lock;
> +	struct completion  done;
> +};
> +static struct s3c_ac97_info s3c_ac97;
> +
> +static struct s3c2410_dma_client s3c_dma_client_out = {
> +	.name = "AC97 PCMOut"
> +};
> +
> +static struct s3c2410_dma_client s3c_dma_client_in = {
> +	.name = "AC97 PCMIn"
> +};
> +
> +static struct s3c2410_dma_client s3c_dma_client_micin = {
> +	.name = "AC97 MicIn"
> +};
> +
> +static struct s3c_dma_params s3c_ac97_pcm_out = {
> +	.client		= &s3c_dma_client_out,
> +	.dma_size	= 4,
> +};
> +
> +static struct s3c_dma_params s3c_ac97_pcm_in = {
> +	.client		= &s3c_dma_client_in,
> +	.dma_size	= 4,
> +};
> +
> +static struct s3c_dma_params s3c_ac97_mic_in = {
> +	.client		= &s3c_dma_client_micin,
> +	.dma_size	= 4,
> +};
> +
> +static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
> +{
> +	writel(S3C_AC97_GLBCTRL_COLDRESET,
> +			s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +
> +	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +}
> +
> +static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
> +{
> +	writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +
> +	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +}

It would be nice t osee something to convert a 'struct snd_ac97' to a 
'struct s3c_ac97_info' as this is being passed in to most place and
would also help if there is ever >1 block.

> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
> +{
> +	u32 ac_glbctrl, stat;
> +
> +	stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
> +	switch (stat) {
> +	case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
> +		return;
> +	case S3C_AC97_GLBSTAT_MAINSTATE_READY:
> +	case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
> +		break;
> +	default:
> +		s3c_ac97_cold_reset(ac97);
> +		s3c_ac97_warm_reset(ac97);
> +		break;
> +	}
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +
> +	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	INIT_COMPLETION(s3c_ac97.done);
> +
> +	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +		printk(KERN_ERR "AC97: Unable to activate!");
> +}
> +
> +static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
> +	unsigned short reg)
> +{
> +	u32 ac_glbctrl, ac_codec_cmd;
> +	u32 stat, addr, data;
> +
> +	mutex_lock(&s3c_ac97.lock);
> +
> +	s3c_ac97_activate(ac97);
> +
> +	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +	ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
> +	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> +	udelay(50);
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	INIT_COMPLETION(s3c_ac97.done);
> +
> +	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +		printk(KERN_ERR "AC97: Unable to read!");
> +
> +	stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
> +	addr = (stat >> 16) & 0x7f;
> +	data = (stat & 0xffff);
> +
> +	if (addr != reg)
> +		printk(KERN_ERR "s3c-ac97: req addr = %02x,"
> +				" rep addr = %02x\n", reg, addr);
> +
> +	mutex_unlock(&s3c_ac97.lock);
> +
> +	return (unsigned short)data;
> +}
> +
> +static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
> +	unsigned short val)
> +{
> +	u32 ac_glbctrl, ac_codec_cmd;
> +
> +	mutex_lock(&s3c_ac97.lock);
> +
> +	s3c_ac97_activate(ac97);
> +
> +	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +	ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
> +	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> +	udelay(50);
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	INIT_COMPLETION(s3c_ac97.done);
> +
> +	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +		printk(KERN_ERR "AC97: Unable to write!");
> +
> +	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +	ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
> +	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
> +
> +	mutex_unlock(&s3c_ac97.lock);
> +}
> +
> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
> +{
> +	u32 ac_glbctrl, ac_glbstat;
> +
> +	ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
> +
> +	if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
> +
> +		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
> +		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +		ac_glbctrl |= (1<<30); /* Clear interrupt */
> +		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +		complete(&s3c_ac97.done);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +struct snd_ac97_bus_ops soc_ac97_ops = {
> +	.read       = s3c_ac97_read,
> +	.write      = s3c_ac97_write,
> +	.warm_reset = s3c_ac97_warm_reset,
> +	.reset      = s3c_ac97_cold_reset,
> +};
> +EXPORT_SYMBOL_GPL(soc_ac97_ops);
> +
> +static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
> +				  struct snd_pcm_hw_params *params,
> +				  struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		cpu_dai->dma_data = &s3c_ac97_pcm_out;
> +	else
> +		cpu_dai->dma_data = &s3c_ac97_pcm_in;
> +
> +	return 0;
> +}
> +
> +static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
> +				struct snd_soc_dai *dai)
> +{
> +	u32 ac_glbctrl;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	int channel = ((struct s3c_dma_params *)
> +		  rtd->dai->cpu_dai->dma_data)->channel;
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> +		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
> +	else
> +		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> +			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
> +		else
> +			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +		break;
> +	}
> +
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
> +
> +	return 0;
> +}
> +
> +static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
> +				      struct snd_pcm_hw_params *params,
> +				      struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		return -ENODEV;
> +	else
> +		cpu_dai->dma_data = &s3c_ac97_mic_in;
> +
> +	return 0;
> +}
> +
> +static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
> +				    int cmd, struct snd_soc_dai *dai)
> +{
> +	u32 ac_glbctrl;
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	int channel = ((struct s3c_dma_params *)
> +		  rtd->dai->cpu_dai->dma_data)->channel;
> +
> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +	case SNDRV_PCM_TRIGGER_RESUME:
> +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +		ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
> +		break;
> +
> +	case SNDRV_PCM_TRIGGER_STOP:
> +	case SNDRV_PCM_TRIGGER_SUSPEND:
> +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +		break;
> +	}
> +
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
> +
> +	return 0;
> +}
> +
> +#define S3C_AC97_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)
> +
> +static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
> +	.hw_params	= s3c_ac97_hw_params,
> +	.trigger	= s3c_ac97_trigger,
> +};
> +
> +static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
> +	.hw_params	= s3c_ac97_hw_mic_params,
> +	.trigger	= s3c_ac97_mic_trigger,
> +};
> +
> +struct snd_soc_dai s3c_ac97_dai[] = {
> +	[S3C_AC97_DAI_PCM] = {
> +		.name =	"s3c-ac97",
> +		.id = S3C_AC97_DAI_PCM,
> +		.ac97_control = 1,
> +		.playback = {
> +			.stream_name = "AC97 Playback",
> +			.channels_min = 2,
> +			.channels_max = 2,
> +			.rates = S3C_AC97_RATES,
> +			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
> +		.capture = {
> +			.stream_name = "AC97 Capture",
> +			/* NOTE: If the codec ouputs just one slot,
> +			 * it *seems* our AC97 controller reads the only
> +			 * valid slot(if either 3 or 4) for PCM-In.
> +			 * For such cases, we record Mono.
> +			 */
> +			.channels_min = 1,
> +			.channels_max = 2,
> +			.rates = S3C_AC97_RATES,
> +			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
> +		.ops = &s3c_ac97_dai_ops,
> +	},
> +	[S3C_AC97_DAI_MIC] = {
> +		.name = "s3c-ac97-mic",
> +		.id = S3C_AC97_DAI_MIC,
> +		.ac97_control = 1,
> +		.capture = {
> +			.stream_name = "AC97 Mic Capture",
> +			.channels_min = 1,
> +			/* NOTE: If the codec(like WM9713) can't ouput just
> +			 * one slot, it *seems* our AC97 controller reads
> +			 * two slots(if one of them is Slot-6) for MIC also.
> +			 * For such cases, we record Stereo.
> +			 */
> +			.channels_max = 2,
> +			.rates = S3C_AC97_RATES,
> +			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
> +		.ops = &s3c_ac97_mic_dai_ops,
> +	},
> +};
> +EXPORT_SYMBOL_GPL(s3c_ac97_dai);
> +
> +static __devinit int s3c_ac97_probe(struct platform_device *pdev)
> +{
> +	struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
> +	struct s3c_audio_pdata *ac97_pdata;
> +	int ret;
> +
> +	ac97_pdata = pdev->dev.platform_data;
> +	if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
> +		dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Check for availability of necessary resource */
> +	dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
> +	if (!dmatx_res) {
> +		dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
> +		return -ENXIO;
> +	}
> +
> +	dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
> +	if (!dmarx_res) {
> +		dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
> +		return -ENXIO;
> +	}
> +
> +	dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
> +	if (!dmamic_res) {
> +		dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
> +		return -ENXIO;
> +	}
> +
> +	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!mem_res) {
> +		dev_err(&pdev->dev, "Unable to get register resource\n");
> +		return -ENXIO;
> +	}
> +
> +	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (!irq_res) {
> +		dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
> +		return -ENXIO;
> +	}
> +
> +	if (!request_mem_region(mem_res->start,
> +				resource_size(mem_res), "s3c-ac97")) {
> +		dev_err(&pdev->dev, "Unable to request register region\n");
> +		return -EBUSY;
> +	}
> +
> +	s3c_ac97_pcm_out.channel = dmatx_res->start;
> +	s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
> +	s3c_ac97_pcm_in.channel = dmarx_res->start;
> +	s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
> +	s3c_ac97_mic_in.channel = dmamic_res->start;
> +	s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
> +
> +	init_completion(&s3c_ac97.done);
> +	mutex_init(&s3c_ac97.lock);
> +
> +	s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
> +	if (s3c_ac97.regs == NULL) {
> +		dev_err(&pdev->dev, "Unable to ioremap register region\n");
> +		ret = -ENXIO;
> +		goto lb1;
> +	}
> +
> +	s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
> +	if (IS_ERR(s3c_ac97.ac97_clk)) {
> +		dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
> +		ret = -ENODEV;
> +		goto lb2;
> +	}
> +	clk_enable(s3c_ac97.ac97_clk);
> +
> +	if (ac97_pdata->cfg_gpio(pdev)) {
> +		dev_err(&pdev->dev, "Unable to configure gpio\n");
> +		ret = -EINVAL;
> +		goto lb3;
> +	}
> +
> +	ret = request_irq(irq_res->start, s3c_ac97_irq,
> +					IRQF_DISABLED, "AC97", NULL);
> +	if (ret < 0) {
> +		printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
> +		goto lb4;
> +	}
> +
> +	s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
> +	s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
> +
> +	ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
> +	if (ret)
> +		goto lb5;
> +
> +	return 0;
> +
> +lb5:
> +	free_irq(irq_res->start, NULL);
> +lb4:
> +lb3:
> +	clk_disable(s3c_ac97.ac97_clk);
> +	clk_put(s3c_ac97.ac97_clk);
> +lb2:
> +	iounmap(s3c_ac97.regs);
> +lb1:
> +	release_mem_region(mem_res->start, resource_size(mem_res));
> +
> +	return ret;
> +}
> +
> +static __devexit int s3c_ac97_remove(struct platform_device *pdev)
> +{
> +	struct resource *mem_res, *irq_res;
> +
> +	snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
> +
> +	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (irq_res)
> +		free_irq(irq_res->start, NULL);
> +
> +	clk_disable(s3c_ac97.ac97_clk);
> +	clk_put(s3c_ac97.ac97_clk);
> +
> +	iounmap(s3c_ac97.regs);
> +
> +	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (mem_res)
> +		release_mem_region(mem_res->start, resource_size(mem_res));
> +
> +	return 0;
> +}
> +
> +static struct platform_driver s3c_ac97_driver = {
> +	.probe  = s3c_ac97_probe,
> +	.remove = s3c_ac97_remove,
> +	.driver = {
> +		.name = "s3c-ac97",
> +		.owner = THIS_MODULE,
> +	},
> +};
> +
> +static int __init s3c_ac97_init(void)
> +{
> +	return platform_driver_register(&s3c_ac97_driver);
> +}
> +module_init(s3c_ac97_init);
> +
> +static void __exit s3c_ac97_exit(void)
> +{
> +	platform_driver_unregister(&s3c_ac97_driver);
> +}
> +module_exit(s3c_ac97_exit);
> +
> +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
> +MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
> new file mode 100644
> index 0000000..2781983
> --- /dev/null
> +++ b/sound/soc/s3c24xx/s3c-ac97.h
> @@ -0,0 +1,23 @@
> +/* sound/soc/s3c24xx/s3c-ac97.h
> + *
> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
> + * 	Evolved from s3c2443-ac97.h
> + *
> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
> + * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
> + * 	Credits: Graeme Gregory, Sean Choi
> + *
> + * 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.
> + */
> +
> +#ifndef __S3C_AC97_H_
> +#define __S3C_AC97_H_
> +
> +#define S3C_AC97_DAI_PCM 0
> +#define S3C_AC97_DAI_MIC 1
> +
> +extern struct snd_soc_dai s3c_ac97_dai[];
> +
> +#endif /* __S3C_AC97_H_ */
> -- 
> 1.6.2.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Out of interest, how similar are the two blocks and could the s3c2443
case be elided into this one with some minimal checks for which one is
currently in use?

-- 
Ben

Q:      What's a light-year?
A:      One-third less calories than a regular year.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26  5:51   ` jassisinghbrar at gmail.com
@ 2010-01-26 10:47     ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:47 UTC (permalink / raw)
  To: jassisinghbrar
  Cc: alsa-devel, linux-arm-kernel, ben-linux, lrg, linux-samsung-soc,
	Jassi Brar

On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar@gmail.com wrote:

This looks good overall, just a few smallish issues:

> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
> +{
> +	u32 ac_glbctrl, stat;
> +
> +	stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
> +	switch (stat) {
> +	case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
> +		return;
> +	case S3C_AC97_GLBSTAT_MAINSTATE_READY:
> +	case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
> +		break;
> +	default:
> +		s3c_ac97_cold_reset(ac97);
> +		s3c_ac97_warm_reset(ac97);
> +		break;
> +	}

This automatic cold and warm reset looks a bit fishy - obviously if this
code path ever gets hit in normal operation then it's going to seriously
disrupt things since it'll reset the CODEC registers.  A warm reset by
itself wouldn't be a problem but I'd rather see explicit cold resets in
the callers where that's required.

> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);

This also looks a bit odd, ACLINKON sounds like bringing up the link
which is what a warm reset does.

> +	INIT_COMPLETION(s3c_ac97.done);
> +
> +	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +		printk(KERN_ERR "AC97: Unable to activate!");

This looks racy - the INIT_COMPLETION() happens after all the hardware
configuration which suggests that if you're unlucky the event which
should trigger the completion will have happened before the init.  A
bunch of interrupts arriving at an inconvenient time could trigger this,
for example.

The same idiom appears in the register reads and writes.

> +	if (addr != reg)
> +		printk(KERN_ERR "s3c-ac97: req addr = %02x,"
> +				" rep addr = %02x\n", reg, addr);

Please don't split error messages over multiple lines, it makes grepping
for them harder.

> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
> +{
> +	u32 ac_glbctrl, ac_glbstat;
> +
> +	ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
> +
> +	if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
> +
> +		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
> +		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +		ac_glbctrl |= (1<<30); /* Clear interrupt */
> +		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +		complete(&s3c_ac97.done);
> +	}
> +
> +	return IRQ_HANDLED;
> +}

You should only be returning IRQ_HANDLED if you actually handled an
interrupt here.

> +#define S3C_AC97_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_8000_48000.

> +		.capture = {
> +			.stream_name = "AC97 Capture",
> +			/* NOTE: If the codec ouputs just one slot,
> +			 * it *seems* our AC97 controller reads the only
> +			 * valid slot(if either 3 or 4) for PCM-In.
> +			 * For such cases, we record Mono.
> +			 */

This seems like unsurprising behaviour for an AC97 controller - the slot
validity information is there for just this purpose.  I'd just remove
the comment as a result.

> +		.capture = {
> +			.stream_name = "AC97 Mic Capture",
> +			.channels_min = 1,
> +			/* NOTE: If the codec(like WM9713) can't ouput just
> +			 * one slot, it *seems* our AC97 controller reads
> +			 * two slots(if one of them is Slot-6) for MIC also.
> +			 * For such cases, we record Stereo.
> +			 */

Similarly here.

> +	if (ac97_pdata->cfg_gpio(pdev)) {
> +		dev_err(&pdev->dev, "Unable to configure gpio\n");
> +		ret = -EINVAL;
> +		goto lb3;
> +	}

Should really check for the function being non-NULL here.

> +lb5:
> +	free_irq(irq_res->start, NULL);

Perhaps a better name than 'lb' - err, or fail or something.  lb means
nothing to me at least.

> +	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (irq_res)
> +		free_irq(irq_res->start, NULL);

This should never get called if the resources aren't allocated.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 10:47     ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar at gmail.com wrote:

This looks good overall, just a few smallish issues:

> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
> +{
> +	u32 ac_glbctrl, stat;
> +
> +	stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
> +	switch (stat) {
> +	case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
> +		return;
> +	case S3C_AC97_GLBSTAT_MAINSTATE_READY:
> +	case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
> +		break;
> +	default:
> +		s3c_ac97_cold_reset(ac97);
> +		s3c_ac97_warm_reset(ac97);
> +		break;
> +	}

This automatic cold and warm reset looks a bit fishy - obviously if this
code path ever gets hit in normal operation then it's going to seriously
disrupt things since it'll reset the CODEC registers.  A warm reset by
itself wouldn't be a problem but I'd rather see explicit cold resets in
the callers where that's required.

> +	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> +	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +	msleep(1);

This also looks a bit odd, ACLINKON sounds like bringing up the link
which is what a warm reset does.

> +	INIT_COMPLETION(s3c_ac97.done);
> +
> +	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
> +		printk(KERN_ERR "AC97: Unable to activate!");

This looks racy - the INIT_COMPLETION() happens after all the hardware
configuration which suggests that if you're unlucky the event which
should trigger the completion will have happened before the init.  A
bunch of interrupts arriving at an inconvenient time could trigger this,
for example.

The same idiom appears in the register reads and writes.

> +	if (addr != reg)
> +		printk(KERN_ERR "s3c-ac97: req addr = %02x,"
> +				" rep addr = %02x\n", reg, addr);

Please don't split error messages over multiple lines, it makes grepping
for them harder.

> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
> +{
> +	u32 ac_glbctrl, ac_glbstat;
> +
> +	ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
> +
> +	if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
> +
> +		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
> +		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +		ac_glbctrl |= (1<<30); /* Clear interrupt */
> +		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> +
> +		complete(&s3c_ac97.done);
> +	}
> +
> +	return IRQ_HANDLED;
> +}

You should only be returning IRQ_HANDLED if you actually handled an
interrupt here.

> +#define S3C_AC97_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_8000_48000.

> +		.capture = {
> +			.stream_name = "AC97 Capture",
> +			/* NOTE: If the codec ouputs just one slot,
> +			 * it *seems* our AC97 controller reads the only
> +			 * valid slot(if either 3 or 4) for PCM-In.
> +			 * For such cases, we record Mono.
> +			 */

This seems like unsurprising behaviour for an AC97 controller - the slot
validity information is there for just this purpose.  I'd just remove
the comment as a result.

> +		.capture = {
> +			.stream_name = "AC97 Mic Capture",
> +			.channels_min = 1,
> +			/* NOTE: If the codec(like WM9713) can't ouput just
> +			 * one slot, it *seems* our AC97 controller reads
> +			 * two slots(if one of them is Slot-6) for MIC also.
> +			 * For such cases, we record Stereo.
> +			 */

Similarly here.

> +	if (ac97_pdata->cfg_gpio(pdev)) {
> +		dev_err(&pdev->dev, "Unable to configure gpio\n");
> +		ret = -EINVAL;
> +		goto lb3;
> +	}

Should really check for the function being non-NULL here.

> +lb5:
> +	free_irq(irq_res->start, NULL);

Perhaps a better name than 'lb' - err, or fail or something.  lb means
nothing to me at least.

> +	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (irq_res)
> +		free_irq(irq_res->start, NULL);

This should never get called if the resources aren't allocated.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 3/3] ARM: SMDK64XX: Enable AC97 device
  2010-01-26 10:19       ` Mark Brown
@ 2010-01-26 10:52         ` jassi brar
  -1 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 10:52 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, ben-linux,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 7:19 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 02:51:41PM +0900, jassisinghbrar@gmail.com wrote:
>> From: Jassi Brar <jassi.brar@samsung.com>
>>
>> Implementing basic support for Playback/Capture by SMDK's AC97
>> controller with WM9713 codec, this patch adds the ALSA machine
>> driver for AC97 device.
>
> Might be better to split this into an arch/arm patch and a sound/soc
> patch - they don't appear to depend on each other so it'd ease merging.
ok, will do.

>> +static int smdk_hifi_hw_params(struct snd_pcm_substream *substream,
>> +                             struct snd_pcm_hw_params *params)
>> +{
>> +     /* CAPTURE - Stereo LineIn
>> +      * Currently there is no other way to change ACLink-In slots
>> +      * of Wm9713, so we make do with this call.
>> +      */
>> +     /* Slots-3,4*/
>> +     soc_ac97_ops.write(NULL, 0x5c, 0);
>> +
>> +     return 0;
>> +}
>
> This one needs a bit more explanation - why are you changing the AC97
> slot?  Is this a general problem with the S3C AC97 controller?  It'd
> also be better to use the symbolic register name rather than a number.
It is no problem.
wm9713 can be configured to place data in one of four possible pair of slots
3-4 or 6-9 or 7-8 or 10-11. This call ensures the selected pair is 3-4(standard
and compatible with Samsung AC97 controllers). Though we don't need to
explicitly set this if we drop mic dai_link.
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 3/3] ARM: SMDK64XX: Enable AC97 device
@ 2010-01-26 10:52         ` jassi brar
  0 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 10:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 7:19 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 02:51:41PM +0900, jassisinghbrar at gmail.com wrote:
>> From: Jassi Brar <jassi.brar@samsung.com>
>>
>> Implementing basic support for Playback/Capture by SMDK's AC97
>> controller with WM9713 codec, this patch adds the ALSA machine
>> driver for AC97 device.
>
> Might be better to split this into an arch/arm patch and a sound/soc
> patch - they don't appear to depend on each other so it'd ease merging.
ok, will do.

>> +static int smdk_hifi_hw_params(struct snd_pcm_substream *substream,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_pcm_hw_params *params)
>> +{
>> + ? ? /* CAPTURE - Stereo LineIn
>> + ? ? ?* Currently there is no other way to change ACLink-In slots
>> + ? ? ?* of Wm9713, so we make do with this call.
>> + ? ? ?*/
>> + ? ? /* Slots-3,4*/
>> + ? ? soc_ac97_ops.write(NULL, 0x5c, 0);
>> +
>> + ? ? return 0;
>> +}
>
> This one needs a bit more explanation - why are you changing the AC97
> slot? ?Is this a general problem with the S3C AC97 controller? ?It'd
> also be better to use the symbolic register name rather than a number.
It is no problem.
wm9713 can be configured to place data in one of four possible pair of slots
3-4 or 6-9 or 7-8 or 10-11. This call ensures the selected pair is 3-4(standard
and compatible with Samsung AC97 controllers). Though we don't need to
explicitly set this if we drop mic dai_link.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26  7:32     ` jassi brar
@ 2010-01-26 10:53       ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:53 UTC (permalink / raw)
  To: jassi brar
  Cc: alsa-devel, linux-arm-kernel, ben-linux, lrg, linux-samsung-soc,
	Jassi Brar

On Tue, Jan 26, 2010 at 04:32:45PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 2:51 PM,  <jassisinghbrar@gmail.com> wrote:

Please delete irrelevant context when replying to mails, it's much
easier to find the actual new text in the message that way.

> > +                       /* NOTE: If the codec ouputs just one slot,
> > +                        * it *seems* our AC97 controller reads the only
> > +                        * valid slot(if either 3 or 4) for PCM-In.
> > +                        * For such cases, we record Mono.
> > +                        */
> > +                       .channels_min = 1,

>   I would like to remove this anomaly and set channels_min = 2

Why?

>  And remove the second DAI_LINK in machine driver.

Again, why?

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 10:53       ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 04:32:45PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 2:51 PM,  <jassisinghbrar@gmail.com> wrote:

Please delete irrelevant context when replying to mails, it's much
easier to find the actual new text in the message that way.

> > + ? ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec ouputs just one slot,
> > + ? ? ? ? ? ? ? ? ? ? ? ?* it *seems* our AC97 controller reads the only
> > + ? ? ? ? ? ? ? ? ? ? ? ?* valid slot(if either 3 or 4) for PCM-In.
> > + ? ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Mono.
> > + ? ? ? ? ? ? ? ? ? ? ? ?*/
> > + ? ? ? ? ? ? ? ? ? ? ? .channels_min = 1,

>   I would like to remove this anomaly and set channels_min = 2

Why?

>  And remove the second DAI_LINK in machine driver.

Again, why?

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 3/3] ARM: SMDK64XX: Enable AC97 device
  2010-01-26 10:52         ` jassi brar
@ 2010-01-26 10:59           ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:59 UTC (permalink / raw)
  To: jassi brar
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, ben-linux,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 07:52:45PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 7:19 PM, Mark Brown

> > This one needs a bit more explanation - why are you changing the AC97
> > slot?  Is this a general problem with the S3C AC97 controller?  It'd
> > also be better to use the symbolic register name rather than a number.

> It is no problem.
> wm9713 can be configured to place data in one of four possible pair of slots
> 3-4 or 6-9 or 7-8 or 10-11. This call ensures the selected pair is 3-4(standard
> and compatible with Samsung AC97 controllers). Though we don't need to
> explicitly set this if we drop mic dai_link.

Ah, OK.  In this case dropping the second DAI link is the better option
- the CODEC only presents a single stereo ADC pair on the AC97 bus with
selectable inputs rather than having separate ADC(s) for microphone(s).
This means you can't run both DAIs in parallel anyway.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 3/3] ARM: SMDK64XX: Enable AC97 device
@ 2010-01-26 10:59           ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 10:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 07:52:45PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 7:19 PM, Mark Brown

> > This one needs a bit more explanation - why are you changing the AC97
> > slot? ?Is this a general problem with the S3C AC97 controller? ?It'd
> > also be better to use the symbolic register name rather than a number.

> It is no problem.
> wm9713 can be configured to place data in one of four possible pair of slots
> 3-4 or 6-9 or 7-8 or 10-11. This call ensures the selected pair is 3-4(standard
> and compatible with Samsung AC97 controllers). Though we don't need to
> explicitly set this if we drop mic dai_link.

Ah, OK.  In this case dropping the second DAI link is the better option
- the CODEC only presents a single stereo ADC pair on the AC97 bus with
selectable inputs rather than having separate ADC(s) for microphone(s).
This means you can't run both DAIs in parallel anyway.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 10:23     ` Ben Dooks
@ 2010-01-26 11:03       ` jassi brar
  -1 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 11:03 UTC (permalink / raw)
  To: Ben Dooks
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, broonie,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 7:23 PM, Ben Dooks <ben-linux@fluff.org> wrote:
> On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar@gmail.com wrote:
>> From: Jassi Brar <jassi.brar@samsung.com>
>>
>> Add the AC97 controller driver for Samsung SoCs that have one.
>>
>> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
>> ---
>>  sound/soc/s3c24xx/Kconfig    |    6 +-
>>  sound/soc/s3c24xx/Makefile   |    3 +-
>>  sound/soc/s3c24xx/s3c-ac97.c |  535 ++++++++++++++++++++++++++++++++++++++++++
>>  sound/soc/s3c24xx/s3c-ac97.h |   23 ++
>>  4 files changed, 565 insertions(+), 2 deletions(-)
>>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
>>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.h
>>
>> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
>> index b489f1a..ad3690e 100644
>> --- a/sound/soc/s3c24xx/Kconfig
>> +++ b/sound/soc/s3c24xx/Kconfig
>> @@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
>>       select S3C2410_DMA
>>       select AC97_BUS
>>       select SND_SOC_AC97_BUS
>> -
>> +
>> +config SND_S3C_SOC_AC97
>> +     tristate
>> +     select SND_SOC_AC97_BUS
>> +
>>  config SND_S3C24XX_SOC_NEO1973_WM8753
>>       tristate "SoC I2S Audio support for NEO1973 - WM8753"
>>       depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
>> diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
>> index b744657..b7411bd 100644
>> --- a/sound/soc/s3c24xx/Makefile
>> +++ b/sound/soc/s3c24xx/Makefile
>> @@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
>> +snd-soc-s3c-ac97-objs := s3c-ac97.o
>>  snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
>>  snd-soc-s3c-pcm-objs := s3c-pcm.o
>>
>>  obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
>>  obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
>>  obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
>> @@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
>>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
>>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
>>  obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
>> -
>> diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
>> new file mode 100644
>> index 0000000..acb8f51
>> --- /dev/null
>> +++ b/sound/soc/s3c24xx/s3c-ac97.c
>> @@ -0,0 +1,535 @@
>> +/* sound/soc/s3c24xx/s3c-ac97.c
>> + *
>> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
>> + *   Evolved from s3c2443-ac97.c
>> + *
>> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
>> + *   Author: Jaswinder Singh <jassi.brar@samsung.com>
>> + *   Credits: Graeme Gregory, Sean Choi
>> + *
>> + * 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/init.h>
>> +#include <linux/module.h>
>> +#include <linux/io.h>
>> +#include <linux/delay.h>
>> +#include <linux/clk.h>
>> +
>> +#include <sound/soc.h>
>> +
>> +#include <plat/regs-ac97.h>
>> +#include <mach/dma.h>
>> +#include <plat/audio.h>
>> +
>> +#include "s3c-dma.h"
>> +#include "s3c-ac97.h"
>> +
>> +#define AC_CMD_ADDR(x) (x << 16)
>> +#define AC_CMD_DATA(x) (x & 0xffff)
>> +
>> +struct s3c_ac97_info {
>> +     unsigned           state;
>> +     struct clk         *ac97_clk;
>> +     void __iomem       *regs;
>> +     struct mutex       lock;
>> +     struct completion  done;
>> +};
>> +static struct s3c_ac97_info s3c_ac97;
>> +
>> +static struct s3c2410_dma_client s3c_dma_client_out = {
>> +     .name = "AC97 PCMOut"
>> +};
>> +
>> +static struct s3c2410_dma_client s3c_dma_client_in = {
>> +     .name = "AC97 PCMIn"
>> +};
>> +
>> +static struct s3c2410_dma_client s3c_dma_client_micin = {
>> +     .name = "AC97 MicIn"
>> +};
>> +
>> +static struct s3c_dma_params s3c_ac97_pcm_out = {
>> +     .client         = &s3c_dma_client_out,
>> +     .dma_size       = 4,
>> +};
>> +
>> +static struct s3c_dma_params s3c_ac97_pcm_in = {
>> +     .client         = &s3c_dma_client_in,
>> +     .dma_size       = 4,
>> +};
>> +
>> +static struct s3c_dma_params s3c_ac97_mic_in = {
>> +     .client         = &s3c_dma_client_micin,
>> +     .dma_size       = 4,
>> +};
>> +
>> +static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
>> +{
>> +     writel(S3C_AC97_GLBCTRL_COLDRESET,
>> +                     s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     msleep(1);
>> +
>> +     writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     msleep(1);
>> +}
>> +
>> +static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
>> +{
>> +     writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     msleep(1);
>> +
>> +     writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     msleep(1);
>> +}
>
> It would be nice t osee something to convert a 'struct snd_ac97' to a
> 'struct s3c_ac97_info' as this is being passed in to most place and
> would also help if there is ever >1 block.
ok, will find a way to do that.

>> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
>> +{
>> +     u32 ac_glbctrl, stat;
>> +
>> +     stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
>> +     switch (stat) {
>> +     case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
>> +             return;
>> +     case S3C_AC97_GLBSTAT_MAINSTATE_READY:
>> +     case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
>> +             break;
>> +     default:
>> +             s3c_ac97_cold_reset(ac97);
>> +             s3c_ac97_warm_reset(ac97);
>> +             break;
>> +     }
>> +
>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     msleep(1);
>> +
>> +     ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     msleep(1);
>> +
>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> +     INIT_COMPLETION(s3c_ac97.done);
>> +
>> +     if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>> +             printk(KERN_ERR "AC97: Unable to activate!");
>> +}
>> +
>> +static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
>> +     unsigned short reg)
>> +{
>> +     u32 ac_glbctrl, ac_codec_cmd;
>> +     u32 stat, addr, data;
>> +
>> +     mutex_lock(&s3c_ac97.lock);
>> +
>> +     s3c_ac97_activate(ac97);
>> +
>> +     ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> +     ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
>> +     writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> +
>> +     udelay(50);
>> +
>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> +     INIT_COMPLETION(s3c_ac97.done);
>> +
>> +     if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>> +             printk(KERN_ERR "AC97: Unable to read!");
>> +
>> +     stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
>> +     addr = (stat >> 16) & 0x7f;
>> +     data = (stat & 0xffff);
>> +
>> +     if (addr != reg)
>> +             printk(KERN_ERR "s3c-ac97: req addr = %02x,"
>> +                             " rep addr = %02x\n", reg, addr);
>> +
>> +     mutex_unlock(&s3c_ac97.lock);
>> +
>> +     return (unsigned short)data;
>> +}
>> +
>> +static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
>> +     unsigned short val)
>> +{
>> +     u32 ac_glbctrl, ac_codec_cmd;
>> +
>> +     mutex_lock(&s3c_ac97.lock);
>> +
>> +     s3c_ac97_activate(ac97);
>> +
>> +     ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> +     ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
>> +     writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> +
>> +     udelay(50);
>> +
>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> +     INIT_COMPLETION(s3c_ac97.done);
>> +
>> +     if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>> +             printk(KERN_ERR "AC97: Unable to write!");
>> +
>> +     ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> +     ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
>> +     writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> +
>> +     mutex_unlock(&s3c_ac97.lock);
>> +}
>> +
>> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
>> +{
>> +     u32 ac_glbctrl, ac_glbstat;
>> +
>> +     ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
>> +
>> +     if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
>> +
>> +             ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +             ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
>> +             writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> +             ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +             ac_glbctrl |= (1<<30); /* Clear interrupt */
>> +             writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> +             complete(&s3c_ac97.done);
>> +     }
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +struct snd_ac97_bus_ops soc_ac97_ops = {
>> +     .read       = s3c_ac97_read,
>> +     .write      = s3c_ac97_write,
>> +     .warm_reset = s3c_ac97_warm_reset,
>> +     .reset      = s3c_ac97_cold_reset,
>> +};
>> +EXPORT_SYMBOL_GPL(soc_ac97_ops);
>> +
>> +static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
>> +                               struct snd_pcm_hw_params *params,
>> +                               struct snd_soc_dai *dai)
>> +{
>> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +     struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
>> +
>> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>> +             cpu_dai->dma_data = &s3c_ac97_pcm_out;
>> +     else
>> +             cpu_dai->dma_data = &s3c_ac97_pcm_in;
>> +
>> +     return 0;
>> +}
>> +
>> +static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
>> +                             struct snd_soc_dai *dai)
>> +{
>> +     u32 ac_glbctrl;
>> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +     int channel = ((struct s3c_dma_params *)
>> +               rtd->dai->cpu_dai->dma_data)->channel;
>> +
>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
>> +             ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
>> +     else
>> +             ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
>> +
>> +     switch (cmd) {
>> +     case SNDRV_PCM_TRIGGER_START:
>> +     case SNDRV_PCM_TRIGGER_RESUME:
>> +     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>> +             if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
>> +                     ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
>> +             else
>> +                     ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
>> +             break;
>> +
>> +     case SNDRV_PCM_TRIGGER_STOP:
>> +     case SNDRV_PCM_TRIGGER_SUSPEND:
>> +     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>> +             break;
>> +     }
>> +
>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> +     s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
>> +
>> +     return 0;
>> +}
>> +
>> +static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
>> +                                   struct snd_pcm_hw_params *params,
>> +                                   struct snd_soc_dai *dai)
>> +{
>> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +     struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
>> +
>> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>> +             return -ENODEV;
>> +     else
>> +             cpu_dai->dma_data = &s3c_ac97_mic_in;
>> +
>> +     return 0;
>> +}
>> +
>> +static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
>> +                                 int cmd, struct snd_soc_dai *dai)
>> +{
>> +     u32 ac_glbctrl;
>> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> +     int channel = ((struct s3c_dma_params *)
>> +               rtd->dai->cpu_dai->dma_data)->channel;
>> +
>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
>> +
>> +     switch (cmd) {
>> +     case SNDRV_PCM_TRIGGER_START:
>> +     case SNDRV_PCM_TRIGGER_RESUME:
>> +     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>> +             ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
>> +             break;
>> +
>> +     case SNDRV_PCM_TRIGGER_STOP:
>> +     case SNDRV_PCM_TRIGGER_SUSPEND:
>> +     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>> +             break;
>> +     }
>> +
>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> +     s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
>> +
>> +     return 0;
>> +}
>> +
>> +#define S3C_AC97_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)
>> +
>> +static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
>> +     .hw_params      = s3c_ac97_hw_params,
>> +     .trigger        = s3c_ac97_trigger,
>> +};
>> +
>> +static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
>> +     .hw_params      = s3c_ac97_hw_mic_params,
>> +     .trigger        = s3c_ac97_mic_trigger,
>> +};
>> +
>> +struct snd_soc_dai s3c_ac97_dai[] = {
>> +     [S3C_AC97_DAI_PCM] = {
>> +             .name = "s3c-ac97",
>> +             .id = S3C_AC97_DAI_PCM,
>> +             .ac97_control = 1,
>> +             .playback = {
>> +                     .stream_name = "AC97 Playback",
>> +                     .channels_min = 2,
>> +                     .channels_max = 2,
>> +                     .rates = S3C_AC97_RATES,
>> +                     .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>> +             .capture = {
>> +                     .stream_name = "AC97 Capture",
>> +                     /* NOTE: If the codec ouputs just one slot,
>> +                      * it *seems* our AC97 controller reads the only
>> +                      * valid slot(if either 3 or 4) for PCM-In.
>> +                      * For such cases, we record Mono.
>> +                      */
>> +                     .channels_min = 1,
>> +                     .channels_max = 2,
>> +                     .rates = S3C_AC97_RATES,
>> +                     .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>> +             .ops = &s3c_ac97_dai_ops,
>> +     },
>> +     [S3C_AC97_DAI_MIC] = {
>> +             .name = "s3c-ac97-mic",
>> +             .id = S3C_AC97_DAI_MIC,
>> +             .ac97_control = 1,
>> +             .capture = {
>> +                     .stream_name = "AC97 Mic Capture",
>> +                     .channels_min = 1,
>> +                     /* NOTE: If the codec(like WM9713) can't ouput just
>> +                      * one slot, it *seems* our AC97 controller reads
>> +                      * two slots(if one of them is Slot-6) for MIC also.
>> +                      * For such cases, we record Stereo.
>> +                      */
>> +                     .channels_max = 2,
>> +                     .rates = S3C_AC97_RATES,
>> +                     .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>> +             .ops = &s3c_ac97_mic_dai_ops,
>> +     },
>> +};
>> +EXPORT_SYMBOL_GPL(s3c_ac97_dai);
>> +
>> +static __devinit int s3c_ac97_probe(struct platform_device *pdev)
>> +{
>> +     struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
>> +     struct s3c_audio_pdata *ac97_pdata;
>> +     int ret;
>> +
>> +     ac97_pdata = pdev->dev.platform_data;
>> +     if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
>> +             dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Check for availability of necessary resource */
>> +     dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
>> +     if (!dmatx_res) {
>> +             dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
>> +             return -ENXIO;
>> +     }
>> +
>> +     dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
>> +     if (!dmarx_res) {
>> +             dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
>> +             return -ENXIO;
>> +     }
>> +
>> +     dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
>> +     if (!dmamic_res) {
>> +             dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
>> +             return -ENXIO;
>> +     }
>> +
>> +     mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     if (!mem_res) {
>> +             dev_err(&pdev->dev, "Unable to get register resource\n");
>> +             return -ENXIO;
>> +     }
>> +
>> +     irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> +     if (!irq_res) {
>> +             dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
>> +             return -ENXIO;
>> +     }
>> +
>> +     if (!request_mem_region(mem_res->start,
>> +                             resource_size(mem_res), "s3c-ac97")) {
>> +             dev_err(&pdev->dev, "Unable to request register region\n");
>> +             return -EBUSY;
>> +     }
>> +
>> +     s3c_ac97_pcm_out.channel = dmatx_res->start;
>> +     s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
>> +     s3c_ac97_pcm_in.channel = dmarx_res->start;
>> +     s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
>> +     s3c_ac97_mic_in.channel = dmamic_res->start;
>> +     s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
>> +
>> +     init_completion(&s3c_ac97.done);
>> +     mutex_init(&s3c_ac97.lock);
>> +
>> +     s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
>> +     if (s3c_ac97.regs == NULL) {
>> +             dev_err(&pdev->dev, "Unable to ioremap register region\n");
>> +             ret = -ENXIO;
>> +             goto lb1;
>> +     }
>> +
>> +     s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
>> +     if (IS_ERR(s3c_ac97.ac97_clk)) {
>> +             dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
>> +             ret = -ENODEV;
>> +             goto lb2;
>> +     }
>> +     clk_enable(s3c_ac97.ac97_clk);
>> +
>> +     if (ac97_pdata->cfg_gpio(pdev)) {
>> +             dev_err(&pdev->dev, "Unable to configure gpio\n");
>> +             ret = -EINVAL;
>> +             goto lb3;
>> +     }
>> +
>> +     ret = request_irq(irq_res->start, s3c_ac97_irq,
>> +                                     IRQF_DISABLED, "AC97", NULL);
>> +     if (ret < 0) {
>> +             printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
>> +             goto lb4;
>> +     }
>> +
>> +     s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
>> +     s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
>> +
>> +     ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
>> +     if (ret)
>> +             goto lb5;
>> +
>> +     return 0;
>> +
>> +lb5:
>> +     free_irq(irq_res->start, NULL);
>> +lb4:
>> +lb3:
>> +     clk_disable(s3c_ac97.ac97_clk);
>> +     clk_put(s3c_ac97.ac97_clk);
>> +lb2:
>> +     iounmap(s3c_ac97.regs);
>> +lb1:
>> +     release_mem_region(mem_res->start, resource_size(mem_res));
>> +
>> +     return ret;
>> +}
>> +
>> +static __devexit int s3c_ac97_remove(struct platform_device *pdev)
>> +{
>> +     struct resource *mem_res, *irq_res;
>> +
>> +     snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
>> +
>> +     irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> +     if (irq_res)
>> +             free_irq(irq_res->start, NULL);
>> +
>> +     clk_disable(s3c_ac97.ac97_clk);
>> +     clk_put(s3c_ac97.ac97_clk);
>> +
>> +     iounmap(s3c_ac97.regs);
>> +
>> +     mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     if (mem_res)
>> +             release_mem_region(mem_res->start, resource_size(mem_res));
>> +
>> +     return 0;
>> +}
>> +
>> +static struct platform_driver s3c_ac97_driver = {
>> +     .probe  = s3c_ac97_probe,
>> +     .remove = s3c_ac97_remove,
>> +     .driver = {
>> +             .name = "s3c-ac97",
>> +             .owner = THIS_MODULE,
>> +     },
>> +};
>> +
>> +static int __init s3c_ac97_init(void)
>> +{
>> +     return platform_driver_register(&s3c_ac97_driver);
>> +}
>> +module_init(s3c_ac97_init);
>> +
>> +static void __exit s3c_ac97_exit(void)
>> +{
>> +     platform_driver_unregister(&s3c_ac97_driver);
>> +}
>> +module_exit(s3c_ac97_exit);
>> +
>> +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
>> +MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
>> +MODULE_LICENSE("GPL");
>> diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
>> new file mode 100644
>> index 0000000..2781983
>> --- /dev/null
>> +++ b/sound/soc/s3c24xx/s3c-ac97.h
>> @@ -0,0 +1,23 @@
>> +/* sound/soc/s3c24xx/s3c-ac97.h
>> + *
>> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
>> + *   Evolved from s3c2443-ac97.h
>> + *
>> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
>> + *   Author: Jaswinder Singh <jassi.brar@samsung.com>
>> + *   Credits: Graeme Gregory, Sean Choi
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef __S3C_AC97_H_
>> +#define __S3C_AC97_H_
>> +
>> +#define S3C_AC97_DAI_PCM 0
>> +#define S3C_AC97_DAI_MIC 1
>> +
>> +extern struct snd_soc_dai s3c_ac97_dai[];
>> +
>> +#endif /* __S3C_AC97_H_ */
>> --
>> 1.6.2.5
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
> Out of interest, how similar are the two blocks and could the s3c2443
> case be elided into this one with some minimal checks for which one is
> currently in use?
Same except for a few 'dead' bits in 2443.
The only reason I didn't submit patch to delete s3c2443-ac97.c is that my
 smdk2443 didn't produce any sound. (btw the s3c2443-ac97.c also fails to do so)
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 11:03       ` jassi brar
  0 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 11:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 7:23 PM, Ben Dooks <ben-linux@fluff.org> wrote:
> On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar at gmail.com wrote:
>> From: Jassi Brar <jassi.brar@samsung.com>
>>
>> Add the AC97 controller driver for Samsung SoCs that have one.
>>
>> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
>> ---
>> ?sound/soc/s3c24xx/Kconfig ? ?| ? ?6 +-
>> ?sound/soc/s3c24xx/Makefile ? | ? ?3 +-
>> ?sound/soc/s3c24xx/s3c-ac97.c | ?535 ++++++++++++++++++++++++++++++++++++++++++
>> ?sound/soc/s3c24xx/s3c-ac97.h | ? 23 ++
>> ?4 files changed, 565 insertions(+), 2 deletions(-)
>> ?create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
>> ?create mode 100644 sound/soc/s3c24xx/s3c-ac97.h
>>
>> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
>> index b489f1a..ad3690e 100644
>> --- a/sound/soc/s3c24xx/Kconfig
>> +++ b/sound/soc/s3c24xx/Kconfig
>> @@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
>> ? ? ? select S3C2410_DMA
>> ? ? ? select AC97_BUS
>> ? ? ? select SND_SOC_AC97_BUS
>> -
>> +
>> +config SND_S3C_SOC_AC97
>> + ? ? tristate
>> + ? ? select SND_SOC_AC97_BUS
>> +
>> ?config SND_S3C24XX_SOC_NEO1973_WM8753
>> ? ? ? tristate "SoC I2S Audio support for NEO1973 - WM8753"
>> ? ? ? depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
>> diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
>> index b744657..b7411bd 100644
>> --- a/sound/soc/s3c24xx/Makefile
>> +++ b/sound/soc/s3c24xx/Makefile
>> @@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
>> +snd-soc-s3c-ac97-objs := s3c-ac97.o
>> ?snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
>> ?snd-soc-s3c-pcm-objs := s3c-pcm.o
>>
>> ?obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
>> ?obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
>> ?obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
>> @@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
>> ?obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
>> ?obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
>> ?obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
>> -
>> diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
>> new file mode 100644
>> index 0000000..acb8f51
>> --- /dev/null
>> +++ b/sound/soc/s3c24xx/s3c-ac97.c
>> @@ -0,0 +1,535 @@
>> +/* sound/soc/s3c24xx/s3c-ac97.c
>> + *
>> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
>> + * ? Evolved from s3c2443-ac97.c
>> + *
>> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
>> + * ? Author: Jaswinder Singh <jassi.brar@samsung.com>
>> + * ? Credits: Graeme Gregory, Sean Choi
>> + *
>> + * 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/init.h>
>> +#include <linux/module.h>
>> +#include <linux/io.h>
>> +#include <linux/delay.h>
>> +#include <linux/clk.h>
>> +
>> +#include <sound/soc.h>
>> +
>> +#include <plat/regs-ac97.h>
>> +#include <mach/dma.h>
>> +#include <plat/audio.h>
>> +
>> +#include "s3c-dma.h"
>> +#include "s3c-ac97.h"
>> +
>> +#define AC_CMD_ADDR(x) (x << 16)
>> +#define AC_CMD_DATA(x) (x & 0xffff)
>> +
>> +struct s3c_ac97_info {
>> + ? ? unsigned ? ? ? ? ? state;
>> + ? ? struct clk ? ? ? ? *ac97_clk;
>> + ? ? void __iomem ? ? ? *regs;
>> + ? ? struct mutex ? ? ? lock;
>> + ? ? struct completion ?done;
>> +};
>> +static struct s3c_ac97_info s3c_ac97;
>> +
>> +static struct s3c2410_dma_client s3c_dma_client_out = {
>> + ? ? .name = "AC97 PCMOut"
>> +};
>> +
>> +static struct s3c2410_dma_client s3c_dma_client_in = {
>> + ? ? .name = "AC97 PCMIn"
>> +};
>> +
>> +static struct s3c2410_dma_client s3c_dma_client_micin = {
>> + ? ? .name = "AC97 MicIn"
>> +};
>> +
>> +static struct s3c_dma_params s3c_ac97_pcm_out = {
>> + ? ? .client ? ? ? ? = &s3c_dma_client_out,
>> + ? ? .dma_size ? ? ? = 4,
>> +};
>> +
>> +static struct s3c_dma_params s3c_ac97_pcm_in = {
>> + ? ? .client ? ? ? ? = &s3c_dma_client_in,
>> + ? ? .dma_size ? ? ? = 4,
>> +};
>> +
>> +static struct s3c_dma_params s3c_ac97_mic_in = {
>> + ? ? .client ? ? ? ? = &s3c_dma_client_micin,
>> + ? ? .dma_size ? ? ? = 4,
>> +};
>> +
>> +static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
>> +{
>> + ? ? writel(S3C_AC97_GLBCTRL_COLDRESET,
>> + ? ? ? ? ? ? ? ? ? ? s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? msleep(1);
>> +
>> + ? ? writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? msleep(1);
>> +}
>> +
>> +static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
>> +{
>> + ? ? writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? msleep(1);
>> +
>> + ? ? writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? msleep(1);
>> +}
>
> It would be nice t osee something to convert a 'struct snd_ac97' to a
> 'struct s3c_ac97_info' as this is being passed in to most place and
> would also help if there is ever >1 block.
ok, will find a way to do that.

>> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
>> +{
>> + ? ? u32 ac_glbctrl, stat;
>> +
>> + ? ? stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
>> + ? ? switch (stat) {
>> + ? ? case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
>> + ? ? ? ? ? ? return;
>> + ? ? case S3C_AC97_GLBSTAT_MAINSTATE_READY:
>> + ? ? case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
>> + ? ? ? ? ? ? break;
>> + ? ? default:
>> + ? ? ? ? ? ? s3c_ac97_cold_reset(ac97);
>> + ? ? ? ? ? ? s3c_ac97_warm_reset(ac97);
>> + ? ? ? ? ? ? break;
>> + ? ? }
>> +
>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? msleep(1);
>> +
>> + ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? msleep(1);
>> +
>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> + ? ? INIT_COMPLETION(s3c_ac97.done);
>> +
>> + ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>> + ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to activate!");
>> +}
>> +
>> +static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
>> + ? ? unsigned short reg)
>> +{
>> + ? ? u32 ac_glbctrl, ac_codec_cmd;
>> + ? ? u32 stat, addr, data;
>> +
>> + ? ? mutex_lock(&s3c_ac97.lock);
>> +
>> + ? ? s3c_ac97_activate(ac97);
>> +
>> + ? ? ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> + ? ? ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
>> + ? ? writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> +
>> + ? ? udelay(50);
>> +
>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> + ? ? INIT_COMPLETION(s3c_ac97.done);
>> +
>> + ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>> + ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to read!");
>> +
>> + ? ? stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
>> + ? ? addr = (stat >> 16) & 0x7f;
>> + ? ? data = (stat & 0xffff);
>> +
>> + ? ? if (addr != reg)
>> + ? ? ? ? ? ? printk(KERN_ERR "s3c-ac97: req addr = %02x,"
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? " rep addr = %02x\n", reg, addr);
>> +
>> + ? ? mutex_unlock(&s3c_ac97.lock);
>> +
>> + ? ? return (unsigned short)data;
>> +}
>> +
>> +static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
>> + ? ? unsigned short val)
>> +{
>> + ? ? u32 ac_glbctrl, ac_codec_cmd;
>> +
>> + ? ? mutex_lock(&s3c_ac97.lock);
>> +
>> + ? ? s3c_ac97_activate(ac97);
>> +
>> + ? ? ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> + ? ? ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
>> + ? ? writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> +
>> + ? ? udelay(50);
>> +
>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> + ? ? INIT_COMPLETION(s3c_ac97.done);
>> +
>> + ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>> + ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to write!");
>> +
>> + ? ? ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> + ? ? ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
>> + ? ? writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>> +
>> + ? ? mutex_unlock(&s3c_ac97.lock);
>> +}
>> +
>> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
>> +{
>> + ? ? u32 ac_glbctrl, ac_glbstat;
>> +
>> + ? ? ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
>> +
>> + ? ? if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
>> +
>> + ? ? ? ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
>> + ? ? ? ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> + ? ? ? ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ? ? ? ? ac_glbctrl |= (1<<30); /* Clear interrupt */
>> + ? ? ? ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> + ? ? ? ? ? ? complete(&s3c_ac97.done);
>> + ? ? }
>> +
>> + ? ? return IRQ_HANDLED;
>> +}
>> +
>> +struct snd_ac97_bus_ops soc_ac97_ops = {
>> + ? ? .read ? ? ? = s3c_ac97_read,
>> + ? ? .write ? ? ?= s3c_ac97_write,
>> + ? ? .warm_reset = s3c_ac97_warm_reset,
>> + ? ? .reset ? ? ?= s3c_ac97_cold_reset,
>> +};
>> +EXPORT_SYMBOL_GPL(soc_ac97_ops);
>> +
>> +static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_pcm_hw_params *params,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_soc_dai *dai)
>> +{
>> + ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + ? ? struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
>> +
>> + ? ? if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>> + ? ? ? ? ? ? cpu_dai->dma_data = &s3c_ac97_pcm_out;
>> + ? ? else
>> + ? ? ? ? ? ? cpu_dai->dma_data = &s3c_ac97_pcm_in;
>> +
>> + ? ? return 0;
>> +}
>> +
>> +static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_soc_dai *dai)
>> +{
>> + ? ? u32 ac_glbctrl;
>> + ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + ? ? int channel = ((struct s3c_dma_params *)
>> + ? ? ? ? ? ? ? rtd->dai->cpu_dai->dma_data)->channel;
>> +
>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
>> + ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
>> + ? ? else
>> + ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
>> +
>> + ? ? switch (cmd) {
>> + ? ? case SNDRV_PCM_TRIGGER_START:
>> + ? ? case SNDRV_PCM_TRIGGER_RESUME:
>> + ? ? case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>> + ? ? ? ? ? ? if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
>> + ? ? ? ? ? ? ? ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
>> + ? ? ? ? ? ? else
>> + ? ? ? ? ? ? ? ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
>> + ? ? ? ? ? ? break;
>> +
>> + ? ? case SNDRV_PCM_TRIGGER_STOP:
>> + ? ? case SNDRV_PCM_TRIGGER_SUSPEND:
>> + ? ? case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>> + ? ? ? ? ? ? break;
>> + ? ? }
>> +
>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> + ? ? s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
>> +
>> + ? ? return 0;
>> +}
>> +
>> +static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_pcm_hw_params *params,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_soc_dai *dai)
>> +{
>> + ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + ? ? struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
>> +
>> + ? ? if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>> + ? ? ? ? ? ? return -ENODEV;
>> + ? ? else
>> + ? ? ? ? ? ? cpu_dai->dma_data = &s3c_ac97_mic_in;
>> +
>> + ? ? return 0;
>> +}
>> +
>> +static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int cmd, struct snd_soc_dai *dai)
>> +{
>> + ? ? u32 ac_glbctrl;
>> + ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
>> + ? ? int channel = ((struct s3c_dma_params *)
>> + ? ? ? ? ? ? ? rtd->dai->cpu_dai->dma_data)->channel;
>> +
>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
>> +
>> + ? ? switch (cmd) {
>> + ? ? case SNDRV_PCM_TRIGGER_START:
>> + ? ? case SNDRV_PCM_TRIGGER_RESUME:
>> + ? ? case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>> + ? ? ? ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
>> + ? ? ? ? ? ? break;
>> +
>> + ? ? case SNDRV_PCM_TRIGGER_STOP:
>> + ? ? case SNDRV_PCM_TRIGGER_SUSPEND:
>> + ? ? case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>> + ? ? ? ? ? ? break;
>> + ? ? }
>> +
>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> + ? ? s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
>> +
>> + ? ? return 0;
>> +}
>> +
>> +#define S3C_AC97_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)
>> +
>> +static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
>> + ? ? .hw_params ? ? ?= s3c_ac97_hw_params,
>> + ? ? .trigger ? ? ? ?= s3c_ac97_trigger,
>> +};
>> +
>> +static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
>> + ? ? .hw_params ? ? ?= s3c_ac97_hw_mic_params,
>> + ? ? .trigger ? ? ? ?= s3c_ac97_mic_trigger,
>> +};
>> +
>> +struct snd_soc_dai s3c_ac97_dai[] = {
>> + ? ? [S3C_AC97_DAI_PCM] = {
>> + ? ? ? ? ? ? .name = "s3c-ac97",
>> + ? ? ? ? ? ? .id = S3C_AC97_DAI_PCM,
>> + ? ? ? ? ? ? .ac97_control = 1,
>> + ? ? ? ? ? ? .playback = {
>> + ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Playback",
>> + ? ? ? ? ? ? ? ? ? ? .channels_min = 2,
>> + ? ? ? ? ? ? ? ? ? ? .channels_max = 2,
>> + ? ? ? ? ? ? ? ? ? ? .rates = S3C_AC97_RATES,
>> + ? ? ? ? ? ? ? ? ? ? .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>> + ? ? ? ? ? ? .capture = {
>> + ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Capture",
>> + ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec ouputs just one slot,
>> + ? ? ? ? ? ? ? ? ? ? ?* it *seems* our AC97 controller reads the only
>> + ? ? ? ? ? ? ? ? ? ? ?* valid slot(if either 3 or 4) for PCM-In.
>> + ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Mono.
>> + ? ? ? ? ? ? ? ? ? ? ?*/
>> + ? ? ? ? ? ? ? ? ? ? .channels_min = 1,
>> + ? ? ? ? ? ? ? ? ? ? .channels_max = 2,
>> + ? ? ? ? ? ? ? ? ? ? .rates = S3C_AC97_RATES,
>> + ? ? ? ? ? ? ? ? ? ? .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>> + ? ? ? ? ? ? .ops = &s3c_ac97_dai_ops,
>> + ? ? },
>> + ? ? [S3C_AC97_DAI_MIC] = {
>> + ? ? ? ? ? ? .name = "s3c-ac97-mic",
>> + ? ? ? ? ? ? .id = S3C_AC97_DAI_MIC,
>> + ? ? ? ? ? ? .ac97_control = 1,
>> + ? ? ? ? ? ? .capture = {
>> + ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Mic Capture",
>> + ? ? ? ? ? ? ? ? ? ? .channels_min = 1,
>> + ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec(like WM9713) can't ouput just
>> + ? ? ? ? ? ? ? ? ? ? ?* one slot, it *seems* our AC97 controller reads
>> + ? ? ? ? ? ? ? ? ? ? ?* two slots(if one of them is Slot-6) for MIC also.
>> + ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Stereo.
>> + ? ? ? ? ? ? ? ? ? ? ?*/
>> + ? ? ? ? ? ? ? ? ? ? .channels_max = 2,
>> + ? ? ? ? ? ? ? ? ? ? .rates = S3C_AC97_RATES,
>> + ? ? ? ? ? ? ? ? ? ? .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>> + ? ? ? ? ? ? .ops = &s3c_ac97_mic_dai_ops,
>> + ? ? },
>> +};
>> +EXPORT_SYMBOL_GPL(s3c_ac97_dai);
>> +
>> +static __devinit int s3c_ac97_probe(struct platform_device *pdev)
>> +{
>> + ? ? struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
>> + ? ? struct s3c_audio_pdata *ac97_pdata;
>> + ? ? int ret;
>> +
>> + ? ? ac97_pdata = pdev->dev.platform_data;
>> + ? ? if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> +
>> + ? ? /* Check for availability of necessary resource */
>> + ? ? dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
>> + ? ? if (!dmatx_res) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
>> + ? ? ? ? ? ? return -ENXIO;
>> + ? ? }
>> +
>> + ? ? dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
>> + ? ? if (!dmarx_res) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
>> + ? ? ? ? ? ? return -ENXIO;
>> + ? ? }
>> +
>> + ? ? dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
>> + ? ? if (!dmamic_res) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
>> + ? ? ? ? ? ? return -ENXIO;
>> + ? ? }
>> +
>> + ? ? mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + ? ? if (!mem_res) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get register resource\n");
>> + ? ? ? ? ? ? return -ENXIO;
>> + ? ? }
>> +
>> + ? ? irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> + ? ? if (!irq_res) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
>> + ? ? ? ? ? ? return -ENXIO;
>> + ? ? }
>> +
>> + ? ? if (!request_mem_region(mem_res->start,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? resource_size(mem_res), "s3c-ac97")) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to request register region\n");
>> + ? ? ? ? ? ? return -EBUSY;
>> + ? ? }
>> +
>> + ? ? s3c_ac97_pcm_out.channel = dmatx_res->start;
>> + ? ? s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
>> + ? ? s3c_ac97_pcm_in.channel = dmarx_res->start;
>> + ? ? s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
>> + ? ? s3c_ac97_mic_in.channel = dmamic_res->start;
>> + ? ? s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
>> +
>> + ? ? init_completion(&s3c_ac97.done);
>> + ? ? mutex_init(&s3c_ac97.lock);
>> +
>> + ? ? s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
>> + ? ? if (s3c_ac97.regs == NULL) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to ioremap register region\n");
>> + ? ? ? ? ? ? ret = -ENXIO;
>> + ? ? ? ? ? ? goto lb1;
>> + ? ? }
>> +
>> + ? ? s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
>> + ? ? if (IS_ERR(s3c_ac97.ac97_clk)) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
>> + ? ? ? ? ? ? ret = -ENODEV;
>> + ? ? ? ? ? ? goto lb2;
>> + ? ? }
>> + ? ? clk_enable(s3c_ac97.ac97_clk);
>> +
>> + ? ? if (ac97_pdata->cfg_gpio(pdev)) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to configure gpio\n");
>> + ? ? ? ? ? ? ret = -EINVAL;
>> + ? ? ? ? ? ? goto lb3;
>> + ? ? }
>> +
>> + ? ? ret = request_irq(irq_res->start, s3c_ac97_irq,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IRQF_DISABLED, "AC97", NULL);
>> + ? ? if (ret < 0) {
>> + ? ? ? ? ? ? printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
>> + ? ? ? ? ? ? goto lb4;
>> + ? ? }
>> +
>> + ? ? s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
>> + ? ? s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
>> +
>> + ? ? ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
>> + ? ? if (ret)
>> + ? ? ? ? ? ? goto lb5;
>> +
>> + ? ? return 0;
>> +
>> +lb5:
>> + ? ? free_irq(irq_res->start, NULL);
>> +lb4:
>> +lb3:
>> + ? ? clk_disable(s3c_ac97.ac97_clk);
>> + ? ? clk_put(s3c_ac97.ac97_clk);
>> +lb2:
>> + ? ? iounmap(s3c_ac97.regs);
>> +lb1:
>> + ? ? release_mem_region(mem_res->start, resource_size(mem_res));
>> +
>> + ? ? return ret;
>> +}
>> +
>> +static __devexit int s3c_ac97_remove(struct platform_device *pdev)
>> +{
>> + ? ? struct resource *mem_res, *irq_res;
>> +
>> + ? ? snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
>> +
>> + ? ? irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> + ? ? if (irq_res)
>> + ? ? ? ? ? ? free_irq(irq_res->start, NULL);
>> +
>> + ? ? clk_disable(s3c_ac97.ac97_clk);
>> + ? ? clk_put(s3c_ac97.ac97_clk);
>> +
>> + ? ? iounmap(s3c_ac97.regs);
>> +
>> + ? ? mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + ? ? if (mem_res)
>> + ? ? ? ? ? ? release_mem_region(mem_res->start, resource_size(mem_res));
>> +
>> + ? ? return 0;
>> +}
>> +
>> +static struct platform_driver s3c_ac97_driver = {
>> + ? ? .probe ?= s3c_ac97_probe,
>> + ? ? .remove = s3c_ac97_remove,
>> + ? ? .driver = {
>> + ? ? ? ? ? ? .name = "s3c-ac97",
>> + ? ? ? ? ? ? .owner = THIS_MODULE,
>> + ? ? },
>> +};
>> +
>> +static int __init s3c_ac97_init(void)
>> +{
>> + ? ? return platform_driver_register(&s3c_ac97_driver);
>> +}
>> +module_init(s3c_ac97_init);
>> +
>> +static void __exit s3c_ac97_exit(void)
>> +{
>> + ? ? platform_driver_unregister(&s3c_ac97_driver);
>> +}
>> +module_exit(s3c_ac97_exit);
>> +
>> +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
>> +MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
>> +MODULE_LICENSE("GPL");
>> diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
>> new file mode 100644
>> index 0000000..2781983
>> --- /dev/null
>> +++ b/sound/soc/s3c24xx/s3c-ac97.h
>> @@ -0,0 +1,23 @@
>> +/* sound/soc/s3c24xx/s3c-ac97.h
>> + *
>> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
>> + * ? Evolved from s3c2443-ac97.h
>> + *
>> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
>> + * ? Author: Jaswinder Singh <jassi.brar@samsung.com>
>> + * ? Credits: Graeme Gregory, Sean Choi
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef __S3C_AC97_H_
>> +#define __S3C_AC97_H_
>> +
>> +#define S3C_AC97_DAI_PCM 0
>> +#define S3C_AC97_DAI_MIC 1
>> +
>> +extern struct snd_soc_dai s3c_ac97_dai[];
>> +
>> +#endif /* __S3C_AC97_H_ */
>> --
>> 1.6.2.5
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>
>
> Out of interest, how similar are the two blocks and could the s3c2443
> case be elided into this one with some minimal checks for which one is
> currently in use?
Same except for a few 'dead' bits in 2443.
The only reason I didn't submit patch to delete s3c2443-ac97.c is that my
 smdk2443 didn't produce any sound. (btw the s3c2443-ac97.c also fails to do so)

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 11:03       ` jassi brar
@ 2010-01-26 11:09         ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 11:09 UTC (permalink / raw)
  To: jassi brar
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, Ben Dooks,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 08:03:09PM +0900, jassi brar wrote:

> The only reason I didn't submit patch to delete s3c2443-ac97.c is that my
>  smdk2443 didn't produce any sound. (btw the s3c2443-ac97.c also fails to do so)

If they both work equally well please just go ahead and remove the old
driver.  Keeping the old driver around if it doesn't work is likely to
result in any fixups going into that.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 11:09         ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 11:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 08:03:09PM +0900, jassi brar wrote:

> The only reason I didn't submit patch to delete s3c2443-ac97.c is that my
>  smdk2443 didn't produce any sound. (btw the s3c2443-ac97.c also fails to do so)

If they both work equally well please just go ahead and remove the old
driver.  Keeping the old driver around if it doesn't work is likely to
result in any fixups going into that.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 10:47     ` Mark Brown
@ 2010-01-26 11:17       ` jassi brar
  -1 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 11:17 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, ben-linux,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 7:47 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar@gmail.com wrote:
>
> This looks good overall, just a few smallish issues:
>
>> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
>> +{
>> +     u32 ac_glbctrl, stat;
>> +
>> +     stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
>> +     switch (stat) {
>> +     case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
>> +             return;
>> +     case S3C_AC97_GLBSTAT_MAINSTATE_READY:
>> +     case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
>> +             break;
>> +     default:
>> +             s3c_ac97_cold_reset(ac97);
>> +             s3c_ac97_warm_reset(ac97);
>> +             break;
>> +     }
>
> This automatic cold and warm reset looks a bit fishy - obviously if this
> code path ever gets hit in normal operation then it's going to seriously
> disrupt things since it'll reset the CODEC registers.  A warm reset by
> itself wouldn't be a problem but I'd rather see explicit cold resets in
> the callers where that's required.
Before read/write we need to ensure the link is active. And to reach the
active state we have to do that as suggested by the FSM shown in SoCs' Manual.
Also, this helps not relying on codec/core to perform particular steps
of initializations.

>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +     msleep(1);
>
> This also looks a bit odd, ACLINKON sounds like bringing up the link
> which is what a warm reset does.
As shown in FSM of SoCs manual, this sets the controller state to READY.
Please have a look at any manual's AC97 chapter.

>> +     INIT_COMPLETION(s3c_ac97.done);
>> +
>> +     if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>> +             printk(KERN_ERR "AC97: Unable to activate!");
>
> This looks racy - the INIT_COMPLETION() happens after all the hardware
> configuration which suggests that if you're unlucky the event which
> should trigger the completion will have happened before the init.  A
> bunch of interrupts arriving at an inconvenient time could trigger this,
> for example.
Yes, init needs to be moved early.

> The same idiom appears in the register reads and writes.
>
>> +     if (addr != reg)
>> +             printk(KERN_ERR "s3c-ac97: req addr = %02x,"
>> +                             " rep addr = %02x\n", reg, addr);
>
> Please don't split error messages over multiple lines, it makes grepping
> for them harder.
ok.

>> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
>> +{
>> +     u32 ac_glbctrl, ac_glbstat;
>> +
>> +     ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
>> +
>> +     if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
>> +
>> +             ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +             ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
>> +             writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> +             ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +             ac_glbctrl |= (1<<30); /* Clear interrupt */
>> +             writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> +             complete(&s3c_ac97.done);
>> +     }
>> +
>> +     return IRQ_HANDLED;
>> +}
>
> You should only be returning IRQ_HANDLED if you actually handled an
> interrupt here.
I'd rather move the intr-clearing out of the block.

>> +#define S3C_AC97_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_8000_48000.
yes.

>> +             .capture = {
>> +                     .stream_name = "AC97 Capture",
>> +                     /* NOTE: If the codec ouputs just one slot,
>> +                      * it *seems* our AC97 controller reads the only
>> +                      * valid slot(if either 3 or 4) for PCM-In.
>> +                      * For such cases, we record Mono.
>> +                      */
>
> This seems like unsurprising behaviour for an AC97 controller - the slot
> validity information is there for just this purpose.  I'd just remove
> the comment as a result.
>
>> +             .capture = {
>> +                     .stream_name = "AC97 Mic Capture",
>> +                     .channels_min = 1,
>> +                     /* NOTE: If the codec(like WM9713) can't ouput just
>> +                      * one slot, it *seems* our AC97 controller reads
>> +                      * two slots(if one of them is Slot-6) for MIC also.
>> +                      * For such cases, we record Stereo.
>> +                      */
>
> Similarly here.
>
>> +     if (ac97_pdata->cfg_gpio(pdev)) {
>> +             dev_err(&pdev->dev, "Unable to configure gpio\n");
>> +             ret = -EINVAL;
>> +             goto lb3;
>> +     }
>
> Should really check for the function being non-NULL here.
already checked at the start and probe fails if its NULL

>> +lb5:
>> +     free_irq(irq_res->start, NULL);
>
> Perhaps a better name than 'lb' - err, or fail or something.  lb means
> nothing to me at least.
means label to me :)

>> +     irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> +     if (irq_res)
>> +             free_irq(irq_res->start, NULL);
>
> This should never get called if the resources aren't allocated.
but the static code analyser doesn't understand that.
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 11:17       ` jassi brar
  0 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 11:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 7:47 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar at gmail.com wrote:
>
> This looks good overall, just a few smallish issues:
>
>> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
>> +{
>> + ? ? u32 ac_glbctrl, stat;
>> +
>> + ? ? stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
>> + ? ? switch (stat) {
>> + ? ? case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
>> + ? ? ? ? ? ? return;
>> + ? ? case S3C_AC97_GLBSTAT_MAINSTATE_READY:
>> + ? ? case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
>> + ? ? ? ? ? ? break;
>> + ? ? default:
>> + ? ? ? ? ? ? s3c_ac97_cold_reset(ac97);
>> + ? ? ? ? ? ? s3c_ac97_warm_reset(ac97);
>> + ? ? ? ? ? ? break;
>> + ? ? }
>
> This automatic cold and warm reset looks a bit fishy - obviously if this
> code path ever gets hit in normal operation then it's going to seriously
> disrupt things since it'll reset the CODEC registers. ?A warm reset by
> itself wouldn't be a problem but I'd rather see explicit cold resets in
> the callers where that's required.
Before read/write we need to ensure the link is active. And to reach the
active state we have to do that as suggested by the FSM shown in SoCs' Manual.
Also, this helps not relying on codec/core to perform particular steps
of initializations.

>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? msleep(1);
>
> This also looks a bit odd, ACLINKON sounds like bringing up the link
> which is what a warm reset does.
As shown in FSM of SoCs manual, this sets the controller state to READY.
Please have a look at any manual's AC97 chapter.

>> + ? ? INIT_COMPLETION(s3c_ac97.done);
>> +
>> + ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>> + ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to activate!");
>
> This looks racy - the INIT_COMPLETION() happens after all the hardware
> configuration which suggests that if you're unlucky the event which
> should trigger the completion will have happened before the init. ?A
> bunch of interrupts arriving at an inconvenient time could trigger this,
> for example.
Yes, init needs to be moved early.

> The same idiom appears in the register reads and writes.
>
>> + ? ? if (addr != reg)
>> + ? ? ? ? ? ? printk(KERN_ERR "s3c-ac97: req addr = %02x,"
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? " rep addr = %02x\n", reg, addr);
>
> Please don't split error messages over multiple lines, it makes grepping
> for them harder.
ok.

>> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
>> +{
>> + ? ? u32 ac_glbctrl, ac_glbstat;
>> +
>> + ? ? ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
>> +
>> + ? ? if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
>> +
>> + ? ? ? ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
>> + ? ? ? ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> + ? ? ? ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> + ? ? ? ? ? ? ac_glbctrl |= (1<<30); /* Clear interrupt */
>> + ? ? ? ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> +
>> + ? ? ? ? ? ? complete(&s3c_ac97.done);
>> + ? ? }
>> +
>> + ? ? return IRQ_HANDLED;
>> +}
>
> You should only be returning IRQ_HANDLED if you actually handled an
> interrupt here.
I'd rather move the intr-clearing out of the block.

>> +#define S3C_AC97_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_8000_48000.
yes.

>> + ? ? ? ? ? ? .capture = {
>> + ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Capture",
>> + ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec ouputs just one slot,
>> + ? ? ? ? ? ? ? ? ? ? ?* it *seems* our AC97 controller reads the only
>> + ? ? ? ? ? ? ? ? ? ? ?* valid slot(if either 3 or 4) for PCM-In.
>> + ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Mono.
>> + ? ? ? ? ? ? ? ? ? ? ?*/
>
> This seems like unsurprising behaviour for an AC97 controller - the slot
> validity information is there for just this purpose. ?I'd just remove
> the comment as a result.
>
>> + ? ? ? ? ? ? .capture = {
>> + ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Mic Capture",
>> + ? ? ? ? ? ? ? ? ? ? .channels_min = 1,
>> + ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec(like WM9713) can't ouput just
>> + ? ? ? ? ? ? ? ? ? ? ?* one slot, it *seems* our AC97 controller reads
>> + ? ? ? ? ? ? ? ? ? ? ?* two slots(if one of them is Slot-6) for MIC also.
>> + ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Stereo.
>> + ? ? ? ? ? ? ? ? ? ? ?*/
>
> Similarly here.
>
>> + ? ? if (ac97_pdata->cfg_gpio(pdev)) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to configure gpio\n");
>> + ? ? ? ? ? ? ret = -EINVAL;
>> + ? ? ? ? ? ? goto lb3;
>> + ? ? }
>
> Should really check for the function being non-NULL here.
already checked at the start and probe fails if its NULL

>> +lb5:
>> + ? ? free_irq(irq_res->start, NULL);
>
> Perhaps a better name than 'lb' - err, or fail or something. ?lb means
> nothing to me at least.
means label to me :)

>> + ? ? irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> + ? ? if (irq_res)
>> + ? ? ? ? ? ? free_irq(irq_res->start, NULL);
>
> This should never get called if the resources aren't allocated.
but the static code analyser doesn't understand that.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 10:53       ` Mark Brown
@ 2010-01-26 11:49         ` jassi brar
  -1 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 11:49 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, linux-arm-kernel, ben-linux, lrg, linux-samsung-soc,
	Jassi Brar

On Tue, Jan 26, 2010 at 7:53 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 04:32:45PM +0900, jassi brar wrote:
>> On Tue, Jan 26, 2010 at 2:51 PM,  <jassisinghbrar@gmail.com> wrote:
>
> Please delete irrelevant context when replying to mails, it's much
> easier to find the actual new text in the message that way.
ok

>> > +                       /* NOTE: If the codec ouputs just one slot,
>> > +                        * it *seems* our AC97 controller reads the only
>> > +                        * valid slot(if either 3 or 4) for PCM-In.
>> > +                        * For such cases, we record Mono.
>> > +                        */
>> > +                       .channels_min = 1,
>
>>   I would like to remove this anomaly and set channels_min = 2
>
> Why?
This hypothesis was based on wrong observation that AC97 can record stereo-mic
if two slots are marked valid and one of them is 6th.
I decided to remove it after that observation turned out wrong(testing error).
Even though this seems logical, but I have not verified it so I wanted
to remove.

>>  And remove the second DAI_LINK in machine driver.
>
> Again, why?
The wm9713 doesn't support mono capture and AC97 controller
doesn't support stereo MIC capture. So the second dai link
is improper.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 11:49         ` jassi brar
  0 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 11:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 7:53 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 04:32:45PM +0900, jassi brar wrote:
>> On Tue, Jan 26, 2010 at 2:51 PM, ?<jassisinghbrar@gmail.com> wrote:
>
> Please delete irrelevant context when replying to mails, it's much
> easier to find the actual new text in the message that way.
ok

>> > + ? ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec ouputs just one slot,
>> > + ? ? ? ? ? ? ? ? ? ? ? ?* it *seems* our AC97 controller reads the only
>> > + ? ? ? ? ? ? ? ? ? ? ? ?* valid slot(if either 3 or 4) for PCM-In.
>> > + ? ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Mono.
>> > + ? ? ? ? ? ? ? ? ? ? ? ?*/
>> > + ? ? ? ? ? ? ? ? ? ? ? .channels_min = 1,
>
>> ? I would like to remove this anomaly and set channels_min = 2
>
> Why?
This hypothesis was based on wrong observation that AC97 can record stereo-mic
if two slots are marked valid and one of them is 6th.
I decided to remove it after that observation turned out wrong(testing error).
Even though this seems logical, but I have not verified it so I wanted
to remove.

>> ?And remove the second DAI_LINK in machine driver.
>
> Again, why?
The wm9713 doesn't support mono capture and AC97 controller
doesn't support stereo MIC capture. So the second dai link
is improper.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 11:17       ` jassi brar
@ 2010-01-26 11:52         ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 11:52 UTC (permalink / raw)
  To: jassi brar
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, ben-linux,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 08:17:25PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 7:47 PM, Mark Brown

> >> +     default:
> >> +             s3c_ac97_cold_reset(ac97);
> >> +             s3c_ac97_warm_reset(ac97);
> >> +             break;
> >> +     }

> > This automatic cold and warm reset looks a bit fishy - obviously if this
> > code path ever gets hit in normal operation then it's going to seriously
> > disrupt things since it'll reset the CODEC registers.  A warm reset by
> > itself wouldn't be a problem but I'd rather see explicit cold resets in
> > the callers where that's required.

> Before read/write we need to ensure the link is active. And to reach the
> active state we have to do that as suggested by the FSM shown in SoCs' Manual.

That's not addressing the problem, though - the big issue is not
bringing up the AC97 link, it's the fact that you're doing an
uncontrolled cold reset.  If we hit this code path it'll revert the
device registers to default which will upset the drivers for the CODEC
rather badly.  There should be no possibility of that happening, if it
is happening it's something that callers really should know about.

> Also, this helps not relying on codec/core to perform particular steps
> of initializations.

Equally well if the controller driver diverges from what other drivers
do it's going to lead to interoperability skew for drivers.

For the warm reset function I'd suggest adding something which checks to
see if the link is already active and suppresses the warm reset in that
case.  That will avoid slowing everything down with spurious warm resets
when the link is already runnning and other drivers assume they need to
bring it up - with the way things are structured at present many warm
resets requested by other drivers are likely to be spurious.

Ideally the core would keep track of this and transparently trigger the
warm reset when required, but driver local is fine since that's not
there yet.

> >> +     ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> >> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> >> +     msleep(1);

> > This also looks a bit odd, ACLINKON sounds like bringing up the link
> > which is what a warm reset does.

> As shown in FSM of SoCs manual, this sets the controller state to READY.
> Please have a look at any manual's AC97 chapter.

Right, but why is this being done by this function and not, for example,
by the warm reset?  It's the structure of the code I'm commenting on
rather than the operations that get performed on the hardware.

> >> +lb5:
> >> +     free_irq(irq_res->start, NULL);

> > Perhaps a better name than 'lb' - err, or fail or something.  lb means
> > nothing to me at least.

> means label to me :)

If you'd spelt out label I'd probably have been OK, but in English lb is
normally only an abbreviation for pounds as a unit of weight.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 11:52         ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 11:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 08:17:25PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 7:47 PM, Mark Brown

> >> + ? ? default:
> >> + ? ? ? ? ? ? s3c_ac97_cold_reset(ac97);
> >> + ? ? ? ? ? ? s3c_ac97_warm_reset(ac97);
> >> + ? ? ? ? ? ? break;
> >> + ? ? }

> > This automatic cold and warm reset looks a bit fishy - obviously if this
> > code path ever gets hit in normal operation then it's going to seriously
> > disrupt things since it'll reset the CODEC registers. ?A warm reset by
> > itself wouldn't be a problem but I'd rather see explicit cold resets in
> > the callers where that's required.

> Before read/write we need to ensure the link is active. And to reach the
> active state we have to do that as suggested by the FSM shown in SoCs' Manual.

That's not addressing the problem, though - the big issue is not
bringing up the AC97 link, it's the fact that you're doing an
uncontrolled cold reset.  If we hit this code path it'll revert the
device registers to default which will upset the drivers for the CODEC
rather badly.  There should be no possibility of that happening, if it
is happening it's something that callers really should know about.

> Also, this helps not relying on codec/core to perform particular steps
> of initializations.

Equally well if the controller driver diverges from what other drivers
do it's going to lead to interoperability skew for drivers.

For the warm reset function I'd suggest adding something which checks to
see if the link is already active and suppresses the warm reset in that
case.  That will avoid slowing everything down with spurious warm resets
when the link is already runnning and other drivers assume they need to
bring it up - with the way things are structured at present many warm
resets requested by other drivers are likely to be spurious.

Ideally the core would keep track of this and transparently trigger the
warm reset when required, but driver local is fine since that's not
there yet.

> >> + ? ? ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> >> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> >> + ? ? msleep(1);

> > This also looks a bit odd, ACLINKON sounds like bringing up the link
> > which is what a warm reset does.

> As shown in FSM of SoCs manual, this sets the controller state to READY.
> Please have a look at any manual's AC97 chapter.

Right, but why is this being done by this function and not, for example,
by the warm reset?  It's the structure of the code I'm commenting on
rather than the operations that get performed on the hardware.

> >> +lb5:
> >> + ? ? free_irq(irq_res->start, NULL);

> > Perhaps a better name than 'lb' - err, or fail or something. ?lb means
> > nothing to me at least.

> means label to me :)

If you'd spelt out label I'd probably have been OK, but in English lb is
normally only an abbreviation for pounds as a unit of weight.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 11:09         ` Mark Brown
@ 2010-01-26 11:57           ` jassi brar
  -1 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 11:57 UTC (permalink / raw)
  To: Mark Brown
  Cc: Ben Dooks, alsa-devel, linux-arm-kernel, lrg, linux-samsung-soc,
	Jassi Brar

On Tue, Jan 26, 2010 at 8:09 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 08:03:09PM +0900, jassi brar wrote:
>
>> The only reason I didn't submit patch to delete s3c2443-ac97.c is that my
>>  smdk2443 didn't produce any sound. (btw the s3c2443-ac97.c also fails to do so)
>
> If they both work equally well please just go ahead and remove the old
> driver.  Keeping the old driver around if it doesn't work is likely to
> result in any fixups going into that.
ok, will submit a patch for that too.
Btw, I don't have ln2440sbc and don't know how would it take the change,
though I sincerely think it should be just fine.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 11:57           ` jassi brar
  0 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 11:57 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 8:09 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 08:03:09PM +0900, jassi brar wrote:
>
>> The only reason I didn't submit patch to delete s3c2443-ac97.c is that my
>> ?smdk2443 didn't produce any sound. (btw the s3c2443-ac97.c also fails to do so)
>
> If they both work equally well please just go ahead and remove the old
> driver. ?Keeping the old driver around if it doesn't work is likely to
> result in any fixups going into that.
ok, will submit a patch for that too.
Btw, I don't have ln2440sbc and don't know how would it take the change,
though I sincerely think it should be just fine.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 11:57           ` jassi brar
@ 2010-01-26 12:04             ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 12:04 UTC (permalink / raw)
  To: jassi brar
  Cc: Ben Dooks, alsa-devel, linux-arm-kernel, lrg, linux-samsung-soc,
	Jassi Brar

On Tue, Jan 26, 2010 at 08:57:56PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 8:09 PM, Mark Brown

> > If they both work equally well please just go ahead and remove the old
> > driver.  Keeping the old driver around if it doesn't work is likely to
> > result in any fixups going into that.

> ok, will submit a patch for that too.
> Btw, I don't have ln2440sbc and don't know how would it take the change,
> though I sincerely think it should be just fine.

CC the original author on the patch updating it, if they don't reply
then a build test is fine - I'd also expect it'll work fine (though
thinking about it you'll need to add the platform data for the GPIO
configuration to the 2443 instance of the struct device, I'm not sure if
that was there or not?).

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 12:04             ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 12:04 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 08:57:56PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 8:09 PM, Mark Brown

> > If they both work equally well please just go ahead and remove the old
> > driver. ?Keeping the old driver around if it doesn't work is likely to
> > result in any fixups going into that.

> ok, will submit a patch for that too.
> Btw, I don't have ln2440sbc and don't know how would it take the change,
> though I sincerely think it should be just fine.

CC the original author on the patch updating it, if they don't reply
then a build test is fine - I'd also expect it'll work fine (though
thinking about it you'll need to add the platform data for the GPIO
configuration to the 2443 instance of the struct device, I'm not sure if
that was there or not?).

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 11:52         ` Mark Brown
@ 2010-01-26 12:11           ` jassi brar
  -1 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 12:11 UTC (permalink / raw)
  To: Mark Brown
  Cc: alsa-devel, linux-samsung-soc, Jassi Brar, ben-linux,
	linux-arm-kernel, lrg

On Tue, Jan 26, 2010 at 8:52 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 08:17:25PM +0900, jassi brar wrote:
>> On Tue, Jan 26, 2010 at 7:47 PM, Mark Brown
>
>> >> +     default:
>> >> +             s3c_ac97_cold_reset(ac97);
>> >> +             s3c_ac97_warm_reset(ac97);
>> >> +             break;
>> >> +     }
>
>> > This automatic cold and warm reset looks a bit fishy - obviously if this
>> > code path ever gets hit in normal operation then it's going to seriously
>> > disrupt things since it'll reset the CODEC registers.  A warm reset by
>> > itself wouldn't be a problem but I'd rather see explicit cold resets in
>> > the callers where that's required.
>
>> Before read/write we need to ensure the link is active. And to reach the
>> active state we have to do that as suggested by the FSM shown in SoCs' Manual.
>
> That's not addressing the problem, though - the big issue is not
> bringing up the AC97 link, it's the fact that you're doing an
> uncontrolled cold reset.  If we hit this code path it'll revert the
> device registers to default which will upset the drivers for the CODEC
> rather badly.  There should be no possibility of that happening, if it
> is happening it's something that callers really should know about.
It should never be reached after the link is up and running. Only if something
goes wrong at runtime, would the controller state be changed. And
this is an attempt to recover from that failure. If the upper layer should
know of such failure, then maybe we should not do it.
I have never seen such runtime error though.

>> Also, this helps not relying on codec/core to perform particular steps
>> of initializations.
>
> Equally well if the controller driver diverges from what other drivers
> do it's going to lead to interoperability skew for drivers.
>
> For the warm reset function I'd suggest adding something which checks to
> see if the link is already active and suppresses the warm reset in that
> case.  That will avoid slowing everything down with spurious warm resets
> when the link is already runnning and other drivers assume they need to
> bring it up - with the way things are structured at present many warm
> resets requested by other drivers are likely to be spurious.
ok, i will add the check in warm reset.

> Ideally the core would keep track of this and transparently trigger the
> warm reset when required, but driver local is fine since that's not
> there yet.
>
>> >> +     ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
>> >> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> >> +     msleep(1);
>
>> > This also looks a bit odd, ACLINKON sounds like bringing up the link
>> > which is what a warm reset does.
>
>> As shown in FSM of SoCs manual, this sets the controller state to READY.
>> Please have a look at any manual's AC97 chapter.
>
> Right, but why is this being done by this function and not, for example,
> by the warm reset?  It's the structure of the code I'm commenting on
> rather than the operations that get performed on the hardware.
I assume the ALSA definition of cold/warm reset is same as that of
the AC97 controller. Anything remaining should be done only when necessary.
For ex, the ALSA might want to leave controller after just cold/warm
reset ... as
the SoC manual says it's in low-power mode without the AC-link on.

>> >> +lb5:
>> >> +     free_irq(irq_res->start, NULL);
>
>> > Perhaps a better name than 'lb' - err, or fail or something.  lb means
>> > nothing to me at least.
>
>> means label to me :)
>
> If you'd spelt out label I'd probably have been OK, but in English lb is
> normally only an abbreviation for pounds as a unit of weight.
ok, will rename the labels.
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 12:11           ` jassi brar
  0 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-26 12:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 8:52 PM, Mark Brown
<broonie@opensource.wolfsonmicro.com> wrote:
> On Tue, Jan 26, 2010 at 08:17:25PM +0900, jassi brar wrote:
>> On Tue, Jan 26, 2010 at 7:47 PM, Mark Brown
>
>> >> + ? ? default:
>> >> + ? ? ? ? ? ? s3c_ac97_cold_reset(ac97);
>> >> + ? ? ? ? ? ? s3c_ac97_warm_reset(ac97);
>> >> + ? ? ? ? ? ? break;
>> >> + ? ? }
>
>> > This automatic cold and warm reset looks a bit fishy - obviously if this
>> > code path ever gets hit in normal operation then it's going to seriously
>> > disrupt things since it'll reset the CODEC registers. ?A warm reset by
>> > itself wouldn't be a problem but I'd rather see explicit cold resets in
>> > the callers where that's required.
>
>> Before read/write we need to ensure the link is active. And to reach the
>> active state we have to do that as suggested by the FSM shown in SoCs' Manual.
>
> That's not addressing the problem, though - the big issue is not
> bringing up the AC97 link, it's the fact that you're doing an
> uncontrolled cold reset. ?If we hit this code path it'll revert the
> device registers to default which will upset the drivers for the CODEC
> rather badly. ?There should be no possibility of that happening, if it
> is happening it's something that callers really should know about.
It should never be reached after the link is up and running. Only if something
goes wrong at runtime, would the controller state be changed. And
this is an attempt to recover from that failure. If the upper layer should
know of such failure, then maybe we should not do it.
I have never seen such runtime error though.

>> Also, this helps not relying on codec/core to perform particular steps
>> of initializations.
>
> Equally well if the controller driver diverges from what other drivers
> do it's going to lead to interoperability skew for drivers.
>
> For the warm reset function I'd suggest adding something which checks to
> see if the link is already active and suppresses the warm reset in that
> case. ?That will avoid slowing everything down with spurious warm resets
> when the link is already runnning and other drivers assume they need to
> bring it up - with the way things are structured at present many warm
> resets requested by other drivers are likely to be spurious.
ok, i will add the check in warm reset.

> Ideally the core would keep track of this and transparently trigger the
> warm reset when required, but driver local is fine since that's not
> there yet.
>
>> >> + ? ? ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
>> >> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>> >> + ? ? msleep(1);
>
>> > This also looks a bit odd, ACLINKON sounds like bringing up the link
>> > which is what a warm reset does.
>
>> As shown in FSM of SoCs manual, this sets the controller state to READY.
>> Please have a look at any manual's AC97 chapter.
>
> Right, but why is this being done by this function and not, for example,
> by the warm reset? ?It's the structure of the code I'm commenting on
> rather than the operations that get performed on the hardware.
I assume the ALSA definition of cold/warm reset is same as that of
the AC97 controller. Anything remaining should be done only when necessary.
For ex, the ALSA might want to leave controller after just cold/warm
reset ... as
the SoC manual says it's in low-power mode without the AC-link on.

>> >> +lb5:
>> >> + ? ? free_irq(irq_res->start, NULL);
>
>> > Perhaps a better name than 'lb' - err, or fail or something. ?lb means
>> > nothing to me at least.
>
>> means label to me :)
>
> If you'd spelt out label I'd probably have been OK, but in English lb is
> normally only an abbreviation for pounds as a unit of weight.
ok, will rename the labels.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 12:11           ` jassi brar
@ 2010-01-26 13:00             ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 13:00 UTC (permalink / raw)
  To: jassi brar
  Cc: alsa-devel, linux-arm-kernel, ben-linux, lrg, linux-samsung-soc,
	Jassi Brar

On Tue, Jan 26, 2010 at 09:11:55PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 8:52 PM, Mark Brown

> > That's not addressing the problem, though - the big issue is not
> > bringing up the AC97 link, it's the fact that you're doing an
> > uncontrolled cold reset.  If we hit this code path it'll revert the
> > device registers to default which will upset the drivers for the CODEC
> > rather badly.  There should be no possibility of that happening, if it
> > is happening it's something that callers really should know about.

> It should never be reached after the link is up and running. Only if something
> goes wrong at runtime, would the controller state be changed. And
> this is an attempt to recover from that failure. If the upper layer should
> know of such failure, then maybe we should not do it.
> I have never seen such runtime error though.

Sounds like the code should be changed to log and return an error if
that case is hit?

> 
> >> >> +     ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> >> >> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> >> >> +     msleep(1);

...

> I assume the ALSA definition of cold/warm reset is same as that of
> the AC97 controller. Anything remaining should be done only when necessary.
> For ex, the ALSA might want to leave controller after just cold/warm
> reset ... as
> the SoC manual says it's in low-power mode without the AC-link on.

A warm reset pretty much means "start the AC-link" - it's what you do to
exit low power mode and start clocking the link (which is mastered by
the CODEC), it doesn't have any other effect.  That's a big part of why
this is confusing, I'd expect a warm reset would also do any setup on
the controller side that was needed to cope with the clock restarting.

Due to the way AC97 is structured this stuff is all shared between the
controller and the CODECs on the bus.  It's not particularly pretty from
a software point of view at the minute.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-26 13:00             ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-26 13:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 09:11:55PM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 8:52 PM, Mark Brown

> > That's not addressing the problem, though - the big issue is not
> > bringing up the AC97 link, it's the fact that you're doing an
> > uncontrolled cold reset. ?If we hit this code path it'll revert the
> > device registers to default which will upset the drivers for the CODEC
> > rather badly. ?There should be no possibility of that happening, if it
> > is happening it's something that callers really should know about.

> It should never be reached after the link is up and running. Only if something
> goes wrong at runtime, would the controller state be changed. And
> this is an attempt to recover from that failure. If the upper layer should
> know of such failure, then maybe we should not do it.
> I have never seen such runtime error though.

Sounds like the code should be changed to log and return an error if
that case is hit?

> 
> >> >> + ? ? ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
> >> >> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
> >> >> + ? ? msleep(1);

...

> I assume the ALSA definition of cold/warm reset is same as that of
> the AC97 controller. Anything remaining should be done only when necessary.
> For ex, the ALSA might want to leave controller after just cold/warm
> reset ... as
> the SoC manual says it's in low-power mode without the AC-link on.

A warm reset pretty much means "start the AC-link" - it's what you do to
exit low power mode and start clocking the link (which is mastered by
the CODEC), it doesn't have any other effect.  That's a big part of why
this is confusing, I'd expect a warm reset would also do any setup on
the controller side that was needed to cope with the clock restarting.

Due to the way AC97 is structured this stuff is all shared between the
controller and the CODECs on the bus.  It's not particularly pretty from
a software point of view at the minute.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-26 11:03       ` jassi brar
@ 2010-01-27  2:45         ` jassi brar
  -1 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-27  2:45 UTC (permalink / raw)
  To: Ben Dooks
  Cc: alsa-devel, linux-arm-kernel, broonie, lrg, linux-samsung-soc,
	Jassi Brar

On Tue, Jan 26, 2010 at 8:03 PM, jassi brar <jassisinghbrar@gmail.com> wrote:
> On Tue, Jan 26, 2010 at 7:23 PM, Ben Dooks <ben-linux@fluff.org> wrote:
>> On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar@gmail.com wrote:
>>> From: Jassi Brar <jassi.brar@samsung.com>
>>>
>>> Add the AC97 controller driver for Samsung SoCs that have one.
>>>
>>> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
>>> ---
>>>  sound/soc/s3c24xx/Kconfig    |    6 +-
>>>  sound/soc/s3c24xx/Makefile   |    3 +-
>>>  sound/soc/s3c24xx/s3c-ac97.c |  535 ++++++++++++++++++++++++++++++++++++++++++
>>>  sound/soc/s3c24xx/s3c-ac97.h |   23 ++
>>>  4 files changed, 565 insertions(+), 2 deletions(-)
>>>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
>>>  create mode 100644 sound/soc/s3c24xx/s3c-ac97.h
>>>
>>> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
>>> index b489f1a..ad3690e 100644
>>> --- a/sound/soc/s3c24xx/Kconfig
>>> +++ b/sound/soc/s3c24xx/Kconfig
>>> @@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
>>>       select S3C2410_DMA
>>>       select AC97_BUS
>>>       select SND_SOC_AC97_BUS
>>> -
>>> +
>>> +config SND_S3C_SOC_AC97
>>> +     tristate
>>> +     select SND_SOC_AC97_BUS
>>> +
>>>  config SND_S3C24XX_SOC_NEO1973_WM8753
>>>       tristate "SoC I2S Audio support for NEO1973 - WM8753"
>>>       depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
>>> diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
>>> index b744657..b7411bd 100644
>>> --- a/sound/soc/s3c24xx/Makefile
>>> +++ b/sound/soc/s3c24xx/Makefile
>>> @@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
>>> +snd-soc-s3c-ac97-objs := s3c-ac97.o
>>>  snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
>>>  snd-soc-s3c-pcm-objs := s3c-pcm.o
>>>
>>>  obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
>>>  obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
>>>  obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
>>> @@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
>>>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
>>>  obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
>>>  obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
>>> -
>>> diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
>>> new file mode 100644
>>> index 0000000..acb8f51
>>> --- /dev/null
>>> +++ b/sound/soc/s3c24xx/s3c-ac97.c
>>> @@ -0,0 +1,535 @@
>>> +/* sound/soc/s3c24xx/s3c-ac97.c
>>> + *
>>> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
>>> + *   Evolved from s3c2443-ac97.c
>>> + *
>>> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
>>> + *   Author: Jaswinder Singh <jassi.brar@samsung.com>
>>> + *   Credits: Graeme Gregory, Sean Choi
>>> + *
>>> + * 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/init.h>
>>> +#include <linux/module.h>
>>> +#include <linux/io.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/clk.h>
>>> +
>>> +#include <sound/soc.h>
>>> +
>>> +#include <plat/regs-ac97.h>
>>> +#include <mach/dma.h>
>>> +#include <plat/audio.h>
>>> +
>>> +#include "s3c-dma.h"
>>> +#include "s3c-ac97.h"
>>> +
>>> +#define AC_CMD_ADDR(x) (x << 16)
>>> +#define AC_CMD_DATA(x) (x & 0xffff)
>>> +
>>> +struct s3c_ac97_info {
>>> +     unsigned           state;
>>> +     struct clk         *ac97_clk;
>>> +     void __iomem       *regs;
>>> +     struct mutex       lock;
>>> +     struct completion  done;
>>> +};
>>> +static struct s3c_ac97_info s3c_ac97;
>>> +
>>> +static struct s3c2410_dma_client s3c_dma_client_out = {
>>> +     .name = "AC97 PCMOut"
>>> +};
>>> +
>>> +static struct s3c2410_dma_client s3c_dma_client_in = {
>>> +     .name = "AC97 PCMIn"
>>> +};
>>> +
>>> +static struct s3c2410_dma_client s3c_dma_client_micin = {
>>> +     .name = "AC97 MicIn"
>>> +};
>>> +
>>> +static struct s3c_dma_params s3c_ac97_pcm_out = {
>>> +     .client         = &s3c_dma_client_out,
>>> +     .dma_size       = 4,
>>> +};
>>> +
>>> +static struct s3c_dma_params s3c_ac97_pcm_in = {
>>> +     .client         = &s3c_dma_client_in,
>>> +     .dma_size       = 4,
>>> +};
>>> +
>>> +static struct s3c_dma_params s3c_ac97_mic_in = {
>>> +     .client         = &s3c_dma_client_micin,
>>> +     .dma_size       = 4,
>>> +};
>>> +
>>> +static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
>>> +{
>>> +     writel(S3C_AC97_GLBCTRL_COLDRESET,
>>> +                     s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     msleep(1);
>>> +
>>> +     writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     msleep(1);
>>> +}
>>> +
>>> +static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
>>> +{
>>> +     writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     msleep(1);
>>> +
>>> +     writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     msleep(1);
>>> +}
>>
>> It would be nice t osee something to convert a 'struct snd_ac97' to a
>> 'struct s3c_ac97_info' as this is being passed in to most place and
>> would also help if there is ever >1 block.
> ok, will find a way to do that.
after looking, I found there is no convenient way to extract driver private
data from the snd_ac97 structure. Its possible, but after ugly voodoo
stuff.  Not to forget that no other ASoC ac97 controller driver does
it either.
And since we have just 1 AC97 controller in SoCs till date(no plans of
adding another anytime soon) I think we can live with it?

>>> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
>>> +{
>>> +     u32 ac_glbctrl, stat;
>>> +
>>> +     stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
>>> +     switch (stat) {
>>> +     case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
>>> +             return;
>>> +     case S3C_AC97_GLBSTAT_MAINSTATE_READY:
>>> +     case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
>>> +             break;
>>> +     default:
>>> +             s3c_ac97_cold_reset(ac97);
>>> +             s3c_ac97_warm_reset(ac97);
>>> +             break;
>>> +     }
>>> +
>>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
>>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     msleep(1);
>>> +
>>> +     ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
>>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     msleep(1);
>>> +
>>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> +     INIT_COMPLETION(s3c_ac97.done);
>>> +
>>> +     if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>>> +             printk(KERN_ERR "AC97: Unable to activate!");
>>> +}
>>> +
>>> +static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
>>> +     unsigned short reg)
>>> +{
>>> +     u32 ac_glbctrl, ac_codec_cmd;
>>> +     u32 stat, addr, data;
>>> +
>>> +     mutex_lock(&s3c_ac97.lock);
>>> +
>>> +     s3c_ac97_activate(ac97);
>>> +
>>> +     ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> +     ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
>>> +     writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> +
>>> +     udelay(50);
>>> +
>>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> +     INIT_COMPLETION(s3c_ac97.done);
>>> +
>>> +     if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>>> +             printk(KERN_ERR "AC97: Unable to read!");
>>> +
>>> +     stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
>>> +     addr = (stat >> 16) & 0x7f;
>>> +     data = (stat & 0xffff);
>>> +
>>> +     if (addr != reg)
>>> +             printk(KERN_ERR "s3c-ac97: req addr = %02x,"
>>> +                             " rep addr = %02x\n", reg, addr);
>>> +
>>> +     mutex_unlock(&s3c_ac97.lock);
>>> +
>>> +     return (unsigned short)data;
>>> +}
>>> +
>>> +static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
>>> +     unsigned short val)
>>> +{
>>> +     u32 ac_glbctrl, ac_codec_cmd;
>>> +
>>> +     mutex_lock(&s3c_ac97.lock);
>>> +
>>> +     s3c_ac97_activate(ac97);
>>> +
>>> +     ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> +     ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
>>> +     writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> +
>>> +     udelay(50);
>>> +
>>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> +     INIT_COMPLETION(s3c_ac97.done);
>>> +
>>> +     if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>>> +             printk(KERN_ERR "AC97: Unable to write!");
>>> +
>>> +     ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> +     ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
>>> +     writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> +
>>> +     mutex_unlock(&s3c_ac97.lock);
>>> +}
>>> +
>>> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
>>> +{
>>> +     u32 ac_glbctrl, ac_glbstat;
>>> +
>>> +     ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
>>> +
>>> +     if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
>>> +
>>> +             ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +             ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
>>> +             writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> +             ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +             ac_glbctrl |= (1<<30); /* Clear interrupt */
>>> +             writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> +             complete(&s3c_ac97.done);
>>> +     }
>>> +
>>> +     return IRQ_HANDLED;
>>> +}
>>> +
>>> +struct snd_ac97_bus_ops soc_ac97_ops = {
>>> +     .read       = s3c_ac97_read,
>>> +     .write      = s3c_ac97_write,
>>> +     .warm_reset = s3c_ac97_warm_reset,
>>> +     .reset      = s3c_ac97_cold_reset,
>>> +};
>>> +EXPORT_SYMBOL_GPL(soc_ac97_ops);
>>> +
>>> +static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
>>> +                               struct snd_pcm_hw_params *params,
>>> +                               struct snd_soc_dai *dai)
>>> +{
>>> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>> +     struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
>>> +
>>> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>> +             cpu_dai->dma_data = &s3c_ac97_pcm_out;
>>> +     else
>>> +             cpu_dai->dma_data = &s3c_ac97_pcm_in;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
>>> +                             struct snd_soc_dai *dai)
>>> +{
>>> +     u32 ac_glbctrl;
>>> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>> +     int channel = ((struct s3c_dma_params *)
>>> +               rtd->dai->cpu_dai->dma_data)->channel;
>>> +
>>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
>>> +             ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
>>> +     else
>>> +             ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
>>> +
>>> +     switch (cmd) {
>>> +     case SNDRV_PCM_TRIGGER_START:
>>> +     case SNDRV_PCM_TRIGGER_RESUME:
>>> +     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>>> +             if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
>>> +                     ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
>>> +             else
>>> +                     ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
>>> +             break;
>>> +
>>> +     case SNDRV_PCM_TRIGGER_STOP:
>>> +     case SNDRV_PCM_TRIGGER_SUSPEND:
>>> +     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>>> +             break;
>>> +     }
>>> +
>>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> +     s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
>>> +                                   struct snd_pcm_hw_params *params,
>>> +                                   struct snd_soc_dai *dai)
>>> +{
>>> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>> +     struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
>>> +
>>> +     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>> +             return -ENODEV;
>>> +     else
>>> +             cpu_dai->dma_data = &s3c_ac97_mic_in;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
>>> +                                 int cmd, struct snd_soc_dai *dai)
>>> +{
>>> +     u32 ac_glbctrl;
>>> +     struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>> +     int channel = ((struct s3c_dma_params *)
>>> +               rtd->dai->cpu_dai->dma_data)->channel;
>>> +
>>> +     ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +     ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
>>> +
>>> +     switch (cmd) {
>>> +     case SNDRV_PCM_TRIGGER_START:
>>> +     case SNDRV_PCM_TRIGGER_RESUME:
>>> +     case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>>> +             ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
>>> +             break;
>>> +
>>> +     case SNDRV_PCM_TRIGGER_STOP:
>>> +     case SNDRV_PCM_TRIGGER_SUSPEND:
>>> +     case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>>> +             break;
>>> +     }
>>> +
>>> +     writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> +     s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +#define S3C_AC97_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)
>>> +
>>> +static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
>>> +     .hw_params      = s3c_ac97_hw_params,
>>> +     .trigger        = s3c_ac97_trigger,
>>> +};
>>> +
>>> +static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
>>> +     .hw_params      = s3c_ac97_hw_mic_params,
>>> +     .trigger        = s3c_ac97_mic_trigger,
>>> +};
>>> +
>>> +struct snd_soc_dai s3c_ac97_dai[] = {
>>> +     [S3C_AC97_DAI_PCM] = {
>>> +             .name = "s3c-ac97",
>>> +             .id = S3C_AC97_DAI_PCM,
>>> +             .ac97_control = 1,
>>> +             .playback = {
>>> +                     .stream_name = "AC97 Playback",
>>> +                     .channels_min = 2,
>>> +                     .channels_max = 2,
>>> +                     .rates = S3C_AC97_RATES,
>>> +                     .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>>> +             .capture = {
>>> +                     .stream_name = "AC97 Capture",
>>> +                     /* NOTE: If the codec ouputs just one slot,
>>> +                      * it *seems* our AC97 controller reads the only
>>> +                      * valid slot(if either 3 or 4) for PCM-In.
>>> +                      * For such cases, we record Mono.
>>> +                      */
>>> +                     .channels_min = 1,
>>> +                     .channels_max = 2,
>>> +                     .rates = S3C_AC97_RATES,
>>> +                     .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>>> +             .ops = &s3c_ac97_dai_ops,
>>> +     },
>>> +     [S3C_AC97_DAI_MIC] = {
>>> +             .name = "s3c-ac97-mic",
>>> +             .id = S3C_AC97_DAI_MIC,
>>> +             .ac97_control = 1,
>>> +             .capture = {
>>> +                     .stream_name = "AC97 Mic Capture",
>>> +                     .channels_min = 1,
>>> +                     /* NOTE: If the codec(like WM9713) can't ouput just
>>> +                      * one slot, it *seems* our AC97 controller reads
>>> +                      * two slots(if one of them is Slot-6) for MIC also.
>>> +                      * For such cases, we record Stereo.
>>> +                      */
>>> +                     .channels_max = 2,
>>> +                     .rates = S3C_AC97_RATES,
>>> +                     .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>>> +             .ops = &s3c_ac97_mic_dai_ops,
>>> +     },
>>> +};
>>> +EXPORT_SYMBOL_GPL(s3c_ac97_dai);
>>> +
>>> +static __devinit int s3c_ac97_probe(struct platform_device *pdev)
>>> +{
>>> +     struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
>>> +     struct s3c_audio_pdata *ac97_pdata;
>>> +     int ret;
>>> +
>>> +     ac97_pdata = pdev->dev.platform_data;
>>> +     if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
>>> +             dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     /* Check for availability of necessary resource */
>>> +     dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
>>> +     if (!dmatx_res) {
>>> +             dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
>>> +             return -ENXIO;
>>> +     }
>>> +
>>> +     dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
>>> +     if (!dmarx_res) {
>>> +             dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
>>> +             return -ENXIO;
>>> +     }
>>> +
>>> +     dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
>>> +     if (!dmamic_res) {
>>> +             dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
>>> +             return -ENXIO;
>>> +     }
>>> +
>>> +     mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +     if (!mem_res) {
>>> +             dev_err(&pdev->dev, "Unable to get register resource\n");
>>> +             return -ENXIO;
>>> +     }
>>> +
>>> +     irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>>> +     if (!irq_res) {
>>> +             dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
>>> +             return -ENXIO;
>>> +     }
>>> +
>>> +     if (!request_mem_region(mem_res->start,
>>> +                             resource_size(mem_res), "s3c-ac97")) {
>>> +             dev_err(&pdev->dev, "Unable to request register region\n");
>>> +             return -EBUSY;
>>> +     }
>>> +
>>> +     s3c_ac97_pcm_out.channel = dmatx_res->start;
>>> +     s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
>>> +     s3c_ac97_pcm_in.channel = dmarx_res->start;
>>> +     s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
>>> +     s3c_ac97_mic_in.channel = dmamic_res->start;
>>> +     s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
>>> +
>>> +     init_completion(&s3c_ac97.done);
>>> +     mutex_init(&s3c_ac97.lock);
>>> +
>>> +     s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
>>> +     if (s3c_ac97.regs == NULL) {
>>> +             dev_err(&pdev->dev, "Unable to ioremap register region\n");
>>> +             ret = -ENXIO;
>>> +             goto lb1;
>>> +     }
>>> +
>>> +     s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
>>> +     if (IS_ERR(s3c_ac97.ac97_clk)) {
>>> +             dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
>>> +             ret = -ENODEV;
>>> +             goto lb2;
>>> +     }
>>> +     clk_enable(s3c_ac97.ac97_clk);
>>> +
>>> +     if (ac97_pdata->cfg_gpio(pdev)) {
>>> +             dev_err(&pdev->dev, "Unable to configure gpio\n");
>>> +             ret = -EINVAL;
>>> +             goto lb3;
>>> +     }
>>> +
>>> +     ret = request_irq(irq_res->start, s3c_ac97_irq,
>>> +                                     IRQF_DISABLED, "AC97", NULL);
>>> +     if (ret < 0) {
>>> +             printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
>>> +             goto lb4;
>>> +     }
>>> +
>>> +     s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
>>> +     s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
>>> +
>>> +     ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
>>> +     if (ret)
>>> +             goto lb5;
>>> +
>>> +     return 0;
>>> +
>>> +lb5:
>>> +     free_irq(irq_res->start, NULL);
>>> +lb4:
>>> +lb3:
>>> +     clk_disable(s3c_ac97.ac97_clk);
>>> +     clk_put(s3c_ac97.ac97_clk);
>>> +lb2:
>>> +     iounmap(s3c_ac97.regs);
>>> +lb1:
>>> +     release_mem_region(mem_res->start, resource_size(mem_res));
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static __devexit int s3c_ac97_remove(struct platform_device *pdev)
>>> +{
>>> +     struct resource *mem_res, *irq_res;
>>> +
>>> +     snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
>>> +
>>> +     irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>>> +     if (irq_res)
>>> +             free_irq(irq_res->start, NULL);
>>> +
>>> +     clk_disable(s3c_ac97.ac97_clk);
>>> +     clk_put(s3c_ac97.ac97_clk);
>>> +
>>> +     iounmap(s3c_ac97.regs);
>>> +
>>> +     mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +     if (mem_res)
>>> +             release_mem_region(mem_res->start, resource_size(mem_res));
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static struct platform_driver s3c_ac97_driver = {
>>> +     .probe  = s3c_ac97_probe,
>>> +     .remove = s3c_ac97_remove,
>>> +     .driver = {
>>> +             .name = "s3c-ac97",
>>> +             .owner = THIS_MODULE,
>>> +     },
>>> +};
>>> +
>>> +static int __init s3c_ac97_init(void)
>>> +{
>>> +     return platform_driver_register(&s3c_ac97_driver);
>>> +}
>>> +module_init(s3c_ac97_init);
>>> +
>>> +static void __exit s3c_ac97_exit(void)
>>> +{
>>> +     platform_driver_unregister(&s3c_ac97_driver);
>>> +}
>>> +module_exit(s3c_ac97_exit);
>>> +
>>> +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
>>> +MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
>>> new file mode 100644
>>> index 0000000..2781983
>>> --- /dev/null
>>> +++ b/sound/soc/s3c24xx/s3c-ac97.h
>>> @@ -0,0 +1,23 @@
>>> +/* sound/soc/s3c24xx/s3c-ac97.h
>>> + *
>>> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
>>> + *   Evolved from s3c2443-ac97.h
>>> + *
>>> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
>>> + *   Author: Jaswinder Singh <jassi.brar@samsung.com>
>>> + *   Credits: Graeme Gregory, Sean Choi
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#ifndef __S3C_AC97_H_
>>> +#define __S3C_AC97_H_
>>> +
>>> +#define S3C_AC97_DAI_PCM 0
>>> +#define S3C_AC97_DAI_MIC 1
>>> +
>>> +extern struct snd_soc_dai s3c_ac97_dai[];
>>> +
>>> +#endif /* __S3C_AC97_H_ */
>>> --
>>> 1.6.2.5
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>>
>> Out of interest, how similar are the two blocks and could the s3c2443
>> case be elided into this one with some minimal checks for which one is
>> currently in use?
> Same except for a few 'dead' bits in 2443.
> The only reason I didn't submit patch to delete s3c2443-ac97.c is that my
>  smdk2443 didn't produce any sound. (btw the s3c2443-ac97.c also fails to do so)
>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-27  2:45         ` jassi brar
  0 siblings, 0 replies; 44+ messages in thread
From: jassi brar @ 2010-01-27  2:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Jan 26, 2010 at 8:03 PM, jassi brar <jassisinghbrar@gmail.com> wrote:
> On Tue, Jan 26, 2010 at 7:23 PM, Ben Dooks <ben-linux@fluff.org> wrote:
>> On Tue, Jan 26, 2010 at 02:51:40PM +0900, jassisinghbrar at gmail.com wrote:
>>> From: Jassi Brar <jassi.brar@samsung.com>
>>>
>>> Add the AC97 controller driver for Samsung SoCs that have one.
>>>
>>> Signed-off-by: Jassi Brar <jassi.brar@samsung.com>
>>> ---
>>> ?sound/soc/s3c24xx/Kconfig ? ?| ? ?6 +-
>>> ?sound/soc/s3c24xx/Makefile ? | ? ?3 +-
>>> ?sound/soc/s3c24xx/s3c-ac97.c | ?535 ++++++++++++++++++++++++++++++++++++++++++
>>> ?sound/soc/s3c24xx/s3c-ac97.h | ? 23 ++
>>> ?4 files changed, 565 insertions(+), 2 deletions(-)
>>> ?create mode 100644 sound/soc/s3c24xx/s3c-ac97.c
>>> ?create mode 100644 sound/soc/s3c24xx/s3c-ac97.h
>>>
>>> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
>>> index b489f1a..ad3690e 100644
>>> --- a/sound/soc/s3c24xx/Kconfig
>>> +++ b/sound/soc/s3c24xx/Kconfig
>>> @@ -32,7 +32,11 @@ config SND_S3C2443_SOC_AC97
>>> ? ? ? select S3C2410_DMA
>>> ? ? ? select AC97_BUS
>>> ? ? ? select SND_SOC_AC97_BUS
>>> -
>>> +
>>> +config SND_S3C_SOC_AC97
>>> + ? ? tristate
>>> + ? ? select SND_SOC_AC97_BUS
>>> +
>>> ?config SND_S3C24XX_SOC_NEO1973_WM8753
>>> ? ? ? tristate "SoC I2S Audio support for NEO1973 - WM8753"
>>> ? ? ? depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
>>> diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
>>> index b744657..b7411bd 100644
>>> --- a/sound/soc/s3c24xx/Makefile
>>> +++ b/sound/soc/s3c24xx/Makefile
>>> @@ -4,12 +4,14 @@ 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-s3c2443-ac97-objs := s3c2443-ac97.o
>>> +snd-soc-s3c-ac97-objs := s3c-ac97.o
>>> ?snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
>>> ?snd-soc-s3c-pcm-objs := s3c-pcm.o
>>>
>>> ?obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
>>> ?obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
>>> ?obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.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_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
>>> @@ -37,4 +39,3 @@ obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
>>> ?obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
>>> ?obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
>>> ?obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
>>> -
>>> diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
>>> new file mode 100644
>>> index 0000000..acb8f51
>>> --- /dev/null
>>> +++ b/sound/soc/s3c24xx/s3c-ac97.c
>>> @@ -0,0 +1,535 @@
>>> +/* sound/soc/s3c24xx/s3c-ac97.c
>>> + *
>>> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
>>> + * ? Evolved from s3c2443-ac97.c
>>> + *
>>> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
>>> + * ? Author: Jaswinder Singh <jassi.brar@samsung.com>
>>> + * ? Credits: Graeme Gregory, Sean Choi
>>> + *
>>> + * 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/init.h>
>>> +#include <linux/module.h>
>>> +#include <linux/io.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/clk.h>
>>> +
>>> +#include <sound/soc.h>
>>> +
>>> +#include <plat/regs-ac97.h>
>>> +#include <mach/dma.h>
>>> +#include <plat/audio.h>
>>> +
>>> +#include "s3c-dma.h"
>>> +#include "s3c-ac97.h"
>>> +
>>> +#define AC_CMD_ADDR(x) (x << 16)
>>> +#define AC_CMD_DATA(x) (x & 0xffff)
>>> +
>>> +struct s3c_ac97_info {
>>> + ? ? unsigned ? ? ? ? ? state;
>>> + ? ? struct clk ? ? ? ? *ac97_clk;
>>> + ? ? void __iomem ? ? ? *regs;
>>> + ? ? struct mutex ? ? ? lock;
>>> + ? ? struct completion ?done;
>>> +};
>>> +static struct s3c_ac97_info s3c_ac97;
>>> +
>>> +static struct s3c2410_dma_client s3c_dma_client_out = {
>>> + ? ? .name = "AC97 PCMOut"
>>> +};
>>> +
>>> +static struct s3c2410_dma_client s3c_dma_client_in = {
>>> + ? ? .name = "AC97 PCMIn"
>>> +};
>>> +
>>> +static struct s3c2410_dma_client s3c_dma_client_micin = {
>>> + ? ? .name = "AC97 MicIn"
>>> +};
>>> +
>>> +static struct s3c_dma_params s3c_ac97_pcm_out = {
>>> + ? ? .client ? ? ? ? = &s3c_dma_client_out,
>>> + ? ? .dma_size ? ? ? = 4,
>>> +};
>>> +
>>> +static struct s3c_dma_params s3c_ac97_pcm_in = {
>>> + ? ? .client ? ? ? ? = &s3c_dma_client_in,
>>> + ? ? .dma_size ? ? ? = 4,
>>> +};
>>> +
>>> +static struct s3c_dma_params s3c_ac97_mic_in = {
>>> + ? ? .client ? ? ? ? = &s3c_dma_client_micin,
>>> + ? ? .dma_size ? ? ? = 4,
>>> +};
>>> +
>>> +static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
>>> +{
>>> + ? ? writel(S3C_AC97_GLBCTRL_COLDRESET,
>>> + ? ? ? ? ? ? ? ? ? ? s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? msleep(1);
>>> +
>>> + ? ? writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? msleep(1);
>>> +}
>>> +
>>> +static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
>>> +{
>>> + ? ? writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? msleep(1);
>>> +
>>> + ? ? writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? msleep(1);
>>> +}
>>
>> It would be nice t osee something to convert a 'struct snd_ac97' to a
>> 'struct s3c_ac97_info' as this is being passed in to most place and
>> would also help if there is ever >1 block.
> ok, will find a way to do that.
after looking, I found there is no convenient way to extract driver private
data from the snd_ac97 structure. Its possible, but after ugly voodoo
stuff.  Not to forget that no other ASoC ac97 controller driver does
it either.
And since we have just 1 AC97 controller in SoCs till date(no plans of
adding another anytime soon) I think we can live with it?

>>> +static void s3c_ac97_activate(struct snd_ac97 *ac97)
>>> +{
>>> + ? ? u32 ac_glbctrl, stat;
>>> +
>>> + ? ? stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
>>> + ? ? switch (stat) {
>>> + ? ? case S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE:
>>> + ? ? ? ? ? ? return;
>>> + ? ? case S3C_AC97_GLBSTAT_MAINSTATE_READY:
>>> + ? ? case S3C_AC97_GLBSTAT_MAINSTATE_INIT:
>>> + ? ? ? ? ? ? break;
>>> + ? ? default:
>>> + ? ? ? ? ? ? s3c_ac97_cold_reset(ac97);
>>> + ? ? ? ? ? ? s3c_ac97_warm_reset(ac97);
>>> + ? ? ? ? ? ? break;
>>> + ? ? }
>>> +
>>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
>>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? msleep(1);
>>> +
>>> + ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
>>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? msleep(1);
>>> +
>>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> + ? ? INIT_COMPLETION(s3c_ac97.done);
>>> +
>>> + ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>>> + ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to activate!");
>>> +}
>>> +
>>> +static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
>>> + ? ? unsigned short reg)
>>> +{
>>> + ? ? u32 ac_glbctrl, ac_codec_cmd;
>>> + ? ? u32 stat, addr, data;
>>> +
>>> + ? ? mutex_lock(&s3c_ac97.lock);
>>> +
>>> + ? ? s3c_ac97_activate(ac97);
>>> +
>>> + ? ? ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> + ? ? ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
>>> + ? ? writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> +
>>> + ? ? udelay(50);
>>> +
>>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> + ? ? INIT_COMPLETION(s3c_ac97.done);
>>> +
>>> + ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>>> + ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to read!");
>>> +
>>> + ? ? stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
>>> + ? ? addr = (stat >> 16) & 0x7f;
>>> + ? ? data = (stat & 0xffff);
>>> +
>>> + ? ? if (addr != reg)
>>> + ? ? ? ? ? ? printk(KERN_ERR "s3c-ac97: req addr = %02x,"
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? " rep addr = %02x\n", reg, addr);
>>> +
>>> + ? ? mutex_unlock(&s3c_ac97.lock);
>>> +
>>> + ? ? return (unsigned short)data;
>>> +}
>>> +
>>> +static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
>>> + ? ? unsigned short val)
>>> +{
>>> + ? ? u32 ac_glbctrl, ac_codec_cmd;
>>> +
>>> + ? ? mutex_lock(&s3c_ac97.lock);
>>> +
>>> + ? ? s3c_ac97_activate(ac97);
>>> +
>>> + ? ? ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> + ? ? ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
>>> + ? ? writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> +
>>> + ? ? udelay(50);
>>> +
>>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
>>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> + ? ? INIT_COMPLETION(s3c_ac97.done);
>>> +
>>> + ? ? if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
>>> + ? ? ? ? ? ? printk(KERN_ERR "AC97: Unable to write!");
>>> +
>>> + ? ? ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> + ? ? ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
>>> + ? ? writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
>>> +
>>> + ? ? mutex_unlock(&s3c_ac97.lock);
>>> +}
>>> +
>>> +static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
>>> +{
>>> + ? ? u32 ac_glbctrl, ac_glbstat;
>>> +
>>> + ? ? ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
>>> +
>>> + ? ? if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
>>> +
>>> + ? ? ? ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
>>> + ? ? ? ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> + ? ? ? ? ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? ? ? ? ? ac_glbctrl |= (1<<30); /* Clear interrupt */
>>> + ? ? ? ? ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> + ? ? ? ? ? ? complete(&s3c_ac97.done);
>>> + ? ? }
>>> +
>>> + ? ? return IRQ_HANDLED;
>>> +}
>>> +
>>> +struct snd_ac97_bus_ops soc_ac97_ops = {
>>> + ? ? .read ? ? ? = s3c_ac97_read,
>>> + ? ? .write ? ? ?= s3c_ac97_write,
>>> + ? ? .warm_reset = s3c_ac97_warm_reset,
>>> + ? ? .reset ? ? ?= s3c_ac97_cold_reset,
>>> +};
>>> +EXPORT_SYMBOL_GPL(soc_ac97_ops);
>>> +
>>> +static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_pcm_hw_params *params,
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_soc_dai *dai)
>>> +{
>>> + ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>> + ? ? struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
>>> +
>>> + ? ? if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>> + ? ? ? ? ? ? cpu_dai->dma_data = &s3c_ac97_pcm_out;
>>> + ? ? else
>>> + ? ? ? ? ? ? cpu_dai->dma_data = &s3c_ac97_pcm_in;
>>> +
>>> + ? ? return 0;
>>> +}
>>> +
>>> +static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_soc_dai *dai)
>>> +{
>>> + ? ? u32 ac_glbctrl;
>>> + ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>> + ? ? int channel = ((struct s3c_dma_params *)
>>> + ? ? ? ? ? ? ? rtd->dai->cpu_dai->dma_data)->channel;
>>> +
>>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
>>> + ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
>>> + ? ? else
>>> + ? ? ? ? ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
>>> +
>>> + ? ? switch (cmd) {
>>> + ? ? case SNDRV_PCM_TRIGGER_START:
>>> + ? ? case SNDRV_PCM_TRIGGER_RESUME:
>>> + ? ? case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>>> + ? ? ? ? ? ? if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
>>> + ? ? ? ? ? ? ? ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
>>> + ? ? ? ? ? ? else
>>> + ? ? ? ? ? ? ? ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
>>> + ? ? ? ? ? ? break;
>>> +
>>> + ? ? case SNDRV_PCM_TRIGGER_STOP:
>>> + ? ? case SNDRV_PCM_TRIGGER_SUSPEND:
>>> + ? ? case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>>> + ? ? ? ? ? ? break;
>>> + ? ? }
>>> +
>>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> + ? ? s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
>>> +
>>> + ? ? return 0;
>>> +}
>>> +
>>> +static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_pcm_hw_params *params,
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct snd_soc_dai *dai)
>>> +{
>>> + ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>> + ? ? struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
>>> +
>>> + ? ? if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
>>> + ? ? ? ? ? ? return -ENODEV;
>>> + ? ? else
>>> + ? ? ? ? ? ? cpu_dai->dma_data = &s3c_ac97_mic_in;
>>> +
>>> + ? ? return 0;
>>> +}
>>> +
>>> +static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int cmd, struct snd_soc_dai *dai)
>>> +{
>>> + ? ? u32 ac_glbctrl;
>>> + ? ? struct snd_soc_pcm_runtime *rtd = substream->private_data;
>>> + ? ? int channel = ((struct s3c_dma_params *)
>>> + ? ? ? ? ? ? ? rtd->dai->cpu_dai->dma_data)->channel;
>>> +
>>> + ? ? ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> + ? ? ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
>>> +
>>> + ? ? switch (cmd) {
>>> + ? ? case SNDRV_PCM_TRIGGER_START:
>>> + ? ? case SNDRV_PCM_TRIGGER_RESUME:
>>> + ? ? case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
>>> + ? ? ? ? ? ? ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
>>> + ? ? ? ? ? ? break;
>>> +
>>> + ? ? case SNDRV_PCM_TRIGGER_STOP:
>>> + ? ? case SNDRV_PCM_TRIGGER_SUSPEND:
>>> + ? ? case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>>> + ? ? ? ? ? ? break;
>>> + ? ? }
>>> +
>>> + ? ? writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
>>> +
>>> + ? ? s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
>>> +
>>> + ? ? return 0;
>>> +}
>>> +
>>> +#define S3C_AC97_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)
>>> +
>>> +static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
>>> + ? ? .hw_params ? ? ?= s3c_ac97_hw_params,
>>> + ? ? .trigger ? ? ? ?= s3c_ac97_trigger,
>>> +};
>>> +
>>> +static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
>>> + ? ? .hw_params ? ? ?= s3c_ac97_hw_mic_params,
>>> + ? ? .trigger ? ? ? ?= s3c_ac97_mic_trigger,
>>> +};
>>> +
>>> +struct snd_soc_dai s3c_ac97_dai[] = {
>>> + ? ? [S3C_AC97_DAI_PCM] = {
>>> + ? ? ? ? ? ? .name = "s3c-ac97",
>>> + ? ? ? ? ? ? .id = S3C_AC97_DAI_PCM,
>>> + ? ? ? ? ? ? .ac97_control = 1,
>>> + ? ? ? ? ? ? .playback = {
>>> + ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Playback",
>>> + ? ? ? ? ? ? ? ? ? ? .channels_min = 2,
>>> + ? ? ? ? ? ? ? ? ? ? .channels_max = 2,
>>> + ? ? ? ? ? ? ? ? ? ? .rates = S3C_AC97_RATES,
>>> + ? ? ? ? ? ? ? ? ? ? .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>>> + ? ? ? ? ? ? .capture = {
>>> + ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Capture",
>>> + ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec ouputs just one slot,
>>> + ? ? ? ? ? ? ? ? ? ? ?* it *seems* our AC97 controller reads the only
>>> + ? ? ? ? ? ? ? ? ? ? ?* valid slot(if either 3 or 4) for PCM-In.
>>> + ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Mono.
>>> + ? ? ? ? ? ? ? ? ? ? ?*/
>>> + ? ? ? ? ? ? ? ? ? ? .channels_min = 1,
>>> + ? ? ? ? ? ? ? ? ? ? .channels_max = 2,
>>> + ? ? ? ? ? ? ? ? ? ? .rates = S3C_AC97_RATES,
>>> + ? ? ? ? ? ? ? ? ? ? .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>>> + ? ? ? ? ? ? .ops = &s3c_ac97_dai_ops,
>>> + ? ? },
>>> + ? ? [S3C_AC97_DAI_MIC] = {
>>> + ? ? ? ? ? ? .name = "s3c-ac97-mic",
>>> + ? ? ? ? ? ? .id = S3C_AC97_DAI_MIC,
>>> + ? ? ? ? ? ? .ac97_control = 1,
>>> + ? ? ? ? ? ? .capture = {
>>> + ? ? ? ? ? ? ? ? ? ? .stream_name = "AC97 Mic Capture",
>>> + ? ? ? ? ? ? ? ? ? ? .channels_min = 1,
>>> + ? ? ? ? ? ? ? ? ? ? /* NOTE: If the codec(like WM9713) can't ouput just
>>> + ? ? ? ? ? ? ? ? ? ? ?* one slot, it *seems* our AC97 controller reads
>>> + ? ? ? ? ? ? ? ? ? ? ?* two slots(if one of them is Slot-6) for MIC also.
>>> + ? ? ? ? ? ? ? ? ? ? ?* For such cases, we record Stereo.
>>> + ? ? ? ? ? ? ? ? ? ? ?*/
>>> + ? ? ? ? ? ? ? ? ? ? .channels_max = 2,
>>> + ? ? ? ? ? ? ? ? ? ? .rates = S3C_AC97_RATES,
>>> + ? ? ? ? ? ? ? ? ? ? .formats = SNDRV_PCM_FMTBIT_S16_LE,},
>>> + ? ? ? ? ? ? .ops = &s3c_ac97_mic_dai_ops,
>>> + ? ? },
>>> +};
>>> +EXPORT_SYMBOL_GPL(s3c_ac97_dai);
>>> +
>>> +static __devinit int s3c_ac97_probe(struct platform_device *pdev)
>>> +{
>>> + ? ? struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
>>> + ? ? struct s3c_audio_pdata *ac97_pdata;
>>> + ? ? int ret;
>>> +
>>> + ? ? ac97_pdata = pdev->dev.platform_data;
>>> + ? ? if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
>>> + ? ? ? ? ? ? return -EINVAL;
>>> + ? ? }
>>> +
>>> + ? ? /* Check for availability of necessary resource */
>>> + ? ? dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
>>> + ? ? if (!dmatx_res) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
>>> + ? ? ? ? ? ? return -ENXIO;
>>> + ? ? }
>>> +
>>> + ? ? dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
>>> + ? ? if (!dmarx_res) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
>>> + ? ? ? ? ? ? return -ENXIO;
>>> + ? ? }
>>> +
>>> + ? ? dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
>>> + ? ? if (!dmamic_res) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
>>> + ? ? ? ? ? ? return -ENXIO;
>>> + ? ? }
>>> +
>>> + ? ? mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + ? ? if (!mem_res) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to get register resource\n");
>>> + ? ? ? ? ? ? return -ENXIO;
>>> + ? ? }
>>> +
>>> + ? ? irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>>> + ? ? if (!irq_res) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
>>> + ? ? ? ? ? ? return -ENXIO;
>>> + ? ? }
>>> +
>>> + ? ? if (!request_mem_region(mem_res->start,
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? resource_size(mem_res), "s3c-ac97")) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to request register region\n");
>>> + ? ? ? ? ? ? return -EBUSY;
>>> + ? ? }
>>> +
>>> + ? ? s3c_ac97_pcm_out.channel = dmatx_res->start;
>>> + ? ? s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
>>> + ? ? s3c_ac97_pcm_in.channel = dmarx_res->start;
>>> + ? ? s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
>>> + ? ? s3c_ac97_mic_in.channel = dmamic_res->start;
>>> + ? ? s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
>>> +
>>> + ? ? init_completion(&s3c_ac97.done);
>>> + ? ? mutex_init(&s3c_ac97.lock);
>>> +
>>> + ? ? s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
>>> + ? ? if (s3c_ac97.regs == NULL) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to ioremap register region\n");
>>> + ? ? ? ? ? ? ret = -ENXIO;
>>> + ? ? ? ? ? ? goto lb1;
>>> + ? ? }
>>> +
>>> + ? ? s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
>>> + ? ? if (IS_ERR(s3c_ac97.ac97_clk)) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
>>> + ? ? ? ? ? ? ret = -ENODEV;
>>> + ? ? ? ? ? ? goto lb2;
>>> + ? ? }
>>> + ? ? clk_enable(s3c_ac97.ac97_clk);
>>> +
>>> + ? ? if (ac97_pdata->cfg_gpio(pdev)) {
>>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Unable to configure gpio\n");
>>> + ? ? ? ? ? ? ret = -EINVAL;
>>> + ? ? ? ? ? ? goto lb3;
>>> + ? ? }
>>> +
>>> + ? ? ret = request_irq(irq_res->start, s3c_ac97_irq,
>>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IRQF_DISABLED, "AC97", NULL);
>>> + ? ? if (ret < 0) {
>>> + ? ? ? ? ? ? printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
>>> + ? ? ? ? ? ? goto lb4;
>>> + ? ? }
>>> +
>>> + ? ? s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
>>> + ? ? s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
>>> +
>>> + ? ? ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
>>> + ? ? if (ret)
>>> + ? ? ? ? ? ? goto lb5;
>>> +
>>> + ? ? return 0;
>>> +
>>> +lb5:
>>> + ? ? free_irq(irq_res->start, NULL);
>>> +lb4:
>>> +lb3:
>>> + ? ? clk_disable(s3c_ac97.ac97_clk);
>>> + ? ? clk_put(s3c_ac97.ac97_clk);
>>> +lb2:
>>> + ? ? iounmap(s3c_ac97.regs);
>>> +lb1:
>>> + ? ? release_mem_region(mem_res->start, resource_size(mem_res));
>>> +
>>> + ? ? return ret;
>>> +}
>>> +
>>> +static __devexit int s3c_ac97_remove(struct platform_device *pdev)
>>> +{
>>> + ? ? struct resource *mem_res, *irq_res;
>>> +
>>> + ? ? snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
>>> +
>>> + ? ? irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>>> + ? ? if (irq_res)
>>> + ? ? ? ? ? ? free_irq(irq_res->start, NULL);
>>> +
>>> + ? ? clk_disable(s3c_ac97.ac97_clk);
>>> + ? ? clk_put(s3c_ac97.ac97_clk);
>>> +
>>> + ? ? iounmap(s3c_ac97.regs);
>>> +
>>> + ? ? mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + ? ? if (mem_res)
>>> + ? ? ? ? ? ? release_mem_region(mem_res->start, resource_size(mem_res));
>>> +
>>> + ? ? return 0;
>>> +}
>>> +
>>> +static struct platform_driver s3c_ac97_driver = {
>>> + ? ? .probe ?= s3c_ac97_probe,
>>> + ? ? .remove = s3c_ac97_remove,
>>> + ? ? .driver = {
>>> + ? ? ? ? ? ? .name = "s3c-ac97",
>>> + ? ? ? ? ? ? .owner = THIS_MODULE,
>>> + ? ? },
>>> +};
>>> +
>>> +static int __init s3c_ac97_init(void)
>>> +{
>>> + ? ? return platform_driver_register(&s3c_ac97_driver);
>>> +}
>>> +module_init(s3c_ac97_init);
>>> +
>>> +static void __exit s3c_ac97_exit(void)
>>> +{
>>> + ? ? platform_driver_unregister(&s3c_ac97_driver);
>>> +}
>>> +module_exit(s3c_ac97_exit);
>>> +
>>> +MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
>>> +MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
>>> new file mode 100644
>>> index 0000000..2781983
>>> --- /dev/null
>>> +++ b/sound/soc/s3c24xx/s3c-ac97.h
>>> @@ -0,0 +1,23 @@
>>> +/* sound/soc/s3c24xx/s3c-ac97.h
>>> + *
>>> + * ALSA SoC Audio Layer - S3C AC97 Controller driver
>>> + * ? Evolved from s3c2443-ac97.h
>>> + *
>>> + * Copyright (c) 2010 Samsung Electronics Co. Ltd
>>> + * ? Author: Jaswinder Singh <jassi.brar@samsung.com>
>>> + * ? Credits: Graeme Gregory, Sean Choi
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#ifndef __S3C_AC97_H_
>>> +#define __S3C_AC97_H_
>>> +
>>> +#define S3C_AC97_DAI_PCM 0
>>> +#define S3C_AC97_DAI_MIC 1
>>> +
>>> +extern struct snd_soc_dai s3c_ac97_dai[];
>>> +
>>> +#endif /* __S3C_AC97_H_ */
>>> --
>>> 1.6.2.5
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>>> the body of a message to majordomo at vger.kernel.org
>>> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>>
>>
>> Out of interest, how similar are the two blocks and could the s3c2443
>> case be elided into this one with some minimal checks for which one is
>> currently in use?
> Same except for a few 'dead' bits in 2443.
> The only reason I didn't submit patch to delete s3c2443-ac97.c is that my
> ?smdk2443 didn't produce any sound. (btw the s3c2443-ac97.c also fails to do so)
>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
  2010-01-27  2:45         ` jassi brar
@ 2010-01-27 10:25           ` Mark Brown
  -1 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-27 10:25 UTC (permalink / raw)
  To: jassi brar
  Cc: Ben Dooks, alsa-devel, linux-arm-kernel, lrg, linux-samsung-soc,
	Jassi Brar

On Wed, Jan 27, 2010 at 11:45:24AM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 8:03 PM, jassi brar <jassisinghbrar@gmail.com> wrote:

> >> It would be nice t osee something to convert a 'struct snd_ac97' to a
> >> 'struct s3c_ac97_info' as this is being passed in to most place and
> >> would also help if there is ever >1 block.

> > ok, will find a way to do that.

> after looking, I found there is no convenient way to extract driver private
> data from the snd_ac97 structure. Its possible, but after ugly voodoo
> stuff.  Not to forget that no other ASoC ac97 controller driver does
> it either.
> And since we have just 1 AC97 controller in SoCs till date(no plans of
> adding another anytime soon) I think we can live with it?

I think this is fine.  It's vanishingly unlikely that we'll see CPUs
with multiple AC97 controllers and there are plenty of places in the
core which assume that there will only be a single AC97 bus in the
system.

Please also let me renew my reminder to cut irrelevant text from
replies, it really does help with finding the new text in the mail and
is especially helpful to those of us who read some of our mail on mobile
phones.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH 2/3] ASoC: AC97: S3C: Add controller driver
@ 2010-01-27 10:25           ` Mark Brown
  0 siblings, 0 replies; 44+ messages in thread
From: Mark Brown @ 2010-01-27 10:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jan 27, 2010 at 11:45:24AM +0900, jassi brar wrote:
> On Tue, Jan 26, 2010 at 8:03 PM, jassi brar <jassisinghbrar@gmail.com> wrote:

> >> It would be nice t osee something to convert a 'struct snd_ac97' to a
> >> 'struct s3c_ac97_info' as this is being passed in to most place and
> >> would also help if there is ever >1 block.

> > ok, will find a way to do that.

> after looking, I found there is no convenient way to extract driver private
> data from the snd_ac97 structure. Its possible, but after ugly voodoo
> stuff.  Not to forget that no other ASoC ac97 controller driver does
> it either.
> And since we have just 1 AC97 controller in SoCs till date(no plans of
> adding another anytime soon) I think we can live with it?

I think this is fine.  It's vanishingly unlikely that we'll see CPUs
with multiple AC97 controllers and there are plenty of places in the
core which assume that there will only be a single AC97 bus in the
system.

Please also let me renew my reminder to cut irrelevant text from
replies, it really does help with finding the new text in the mail and
is especially helpful to those of us who read some of our mail on mobile
phones.

^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2010-01-27 10:25 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-01-26  5:51 [PATCH 1/3] ARM: S3C64XX: Add AC97 platform resources jassisinghbrar
2010-01-26  5:51 ` jassisinghbrar at gmail.com
2010-01-26  5:51 ` [PATCH 2/3] ASoC: AC97: S3C: Add controller driver jassisinghbrar
2010-01-26  5:51   ` jassisinghbrar at gmail.com
2010-01-26  5:51   ` [PATCH 3/3] ARM: SMDK64XX: Enable AC97 device jassisinghbrar
2010-01-26  5:51     ` jassisinghbrar at gmail.com
2010-01-26 10:19     ` Mark Brown
2010-01-26 10:19       ` Mark Brown
2010-01-26 10:52       ` jassi brar
2010-01-26 10:52         ` jassi brar
2010-01-26 10:59         ` Mark Brown
2010-01-26 10:59           ` Mark Brown
2010-01-26  7:32   ` [PATCH 2/3] ASoC: AC97: S3C: Add controller driver jassi brar
2010-01-26  7:32     ` jassi brar
2010-01-26 10:53     ` Mark Brown
2010-01-26 10:53       ` Mark Brown
2010-01-26 11:49       ` jassi brar
2010-01-26 11:49         ` jassi brar
2010-01-26 10:23   ` Ben Dooks
2010-01-26 10:23     ` Ben Dooks
2010-01-26 11:03     ` jassi brar
2010-01-26 11:03       ` jassi brar
2010-01-26 11:09       ` Mark Brown
2010-01-26 11:09         ` Mark Brown
2010-01-26 11:57         ` jassi brar
2010-01-26 11:57           ` jassi brar
2010-01-26 12:04           ` Mark Brown
2010-01-26 12:04             ` Mark Brown
2010-01-27  2:45       ` jassi brar
2010-01-27  2:45         ` jassi brar
2010-01-27 10:25         ` Mark Brown
2010-01-27 10:25           ` Mark Brown
2010-01-26 10:47   ` Mark Brown
2010-01-26 10:47     ` Mark Brown
2010-01-26 11:17     ` jassi brar
2010-01-26 11:17       ` jassi brar
2010-01-26 11:52       ` Mark Brown
2010-01-26 11:52         ` Mark Brown
2010-01-26 12:11         ` jassi brar
2010-01-26 12:11           ` jassi brar
2010-01-26 13:00           ` Mark Brown
2010-01-26 13:00             ` Mark Brown
2010-01-26 10:12 ` [PATCH 1/3] ARM: S3C64XX: Add AC97 platform resources Mark Brown
2010-01-26 10:12   ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.