summary refs log tree commit diff
path: root/arch/arm/mach-ep93xx
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-08-07 17:07:31 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-08-07 17:07:31 -0700
commitfaa38b5e0e092914764cdba9f83d31a3f794d182 (patch)
treeb3e5921bdc36378033b4910eb4f29cb0dfc486e0 /arch/arm/mach-ep93xx
parent78417334b5cb6e1f915b8fdcc4fce3f1a1b4420c (diff)
parent74bf40f0793fed9e01eb6164c2ce63e8c27ca205 (diff)
downloadlinux-faa38b5e0e092914764cdba9f83d31a3f794d182.tar.gz
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (214 commits)
  ALSA: hda - Add pin-fix for HP dc5750
  ALSA: als4000: Fix potentially invalid DMA mode setup
  ALSA: als4000: enable burst mode
  ALSA: hda - Fix initial capsrc selection in patch_alc269()
  ASoC: TWL4030: Capture route runtime DAPM ordering fix
  ALSA: hda - Add PC-beep whitelist for an Intel board
  ALSA: hda - More relax for pending period handling
  ALSA: hda - Define AC_FMT_* constants
  ALSA: hda - Fix beep frequency on IDT 92HD73xx and 92HD71Bxx codecs
  ALSA: hda - Add support for HDMI HBR passthrough
  ALSA: hda - Set Stream Type in Stream Format according to AES0
  ALSA: hda - Fix Thinkpad X300 so SPDIF is not exposed
  ALSA: hda - FIX to not expose SPDIF on Thinkpad X301, since it does not have the ability to use SPDIF
  ASoC: wm9081: fix resource reclaim in wm9081_register error path
  ASoC: wm8978: fix a memory leak if a wm8978_register fail
  ASoC: wm8974: fix a memory leak if another WM8974 is registered
  ASoC: wm8961: fix resource reclaim in wm8961_register error path
  ASoC: wm8955: fix resource reclaim in wm8955_register error path
  ASoC: wm8940: fix a memory leak if wm8940_register return error
  ASoC: wm8904: fix resource reclaim in wm8904_register error path
  ...
Diffstat (limited to 'arch/arm/mach-ep93xx')
-rw-r--r--arch/arm/mach-ep93xx/clock.c67
-rw-r--r--arch/arm/mach-ep93xx/core.c67
-rw-r--r--arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h10
-rw-r--r--arch/arm/mach-ep93xx/include/mach/platform.h3
-rw-r--r--arch/arm/mach-ep93xx/snappercl15.c1
5 files changed, 147 insertions, 1 deletions
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
index 7f3039761d91..8bf3cec98cfa 100644
--- a/arch/arm/mach-ep93xx/clock.c
+++ b/arch/arm/mach-ep93xx/clock.c
@@ -43,7 +43,8 @@ static unsigned long get_uart_rate(struct clk *clk);
 
 static int set_keytchclk_rate(struct clk *clk, unsigned long rate);
 static int set_div_rate(struct clk *clk, unsigned long rate);
-
+static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate);
+static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate);
 
 static struct clk clk_xtali = {
 	.rate		= EP93XX_EXT_CLK_RATE,
@@ -112,6 +113,29 @@ static struct clk clk_video = {
 	.set_rate	= set_div_rate,
 };
 
+static struct clk clk_i2s_mclk = {
+	.sw_locked	= 1,
+	.enable_reg	= EP93XX_SYSCON_I2SCLKDIV,
+	.enable_mask	= EP93XX_SYSCON_CLKDIV_ENABLE,
+	.set_rate	= set_div_rate,
+};
+
+static struct clk clk_i2s_sclk = {
+	.sw_locked	= 1,
+	.parent		= &clk_i2s_mclk,
+	.enable_reg	= EP93XX_SYSCON_I2SCLKDIV,
+	.enable_mask	= EP93XX_SYSCON_I2SCLKDIV_SENA,
+	.set_rate	= set_i2s_sclk_rate,
+};
+
+static struct clk clk_i2s_lrclk = {
+	.sw_locked	= 1,
+	.parent		= &clk_i2s_sclk,
+	.enable_reg	= EP93XX_SYSCON_I2SCLKDIV,
+	.enable_mask	= EP93XX_SYSCON_I2SCLKDIV_SENA,
+	.set_rate	= set_i2s_lrclk_rate,
+};
+
 /* DMA Clocks */
 static struct clk clk_m2p0 = {
 	.parent		= &clk_h,
@@ -191,6 +215,9 @@ static struct clk_lookup clocks[] = {
 	INIT_CK("ep93xx-keypad",	NULL,		&clk_keypad),
 	INIT_CK("ep93xx-fb",		NULL,		&clk_video),
 	INIT_CK("ep93xx-spi.0",		NULL,		&clk_spi),
+	INIT_CK("ep93xx-i2s",		"mclk",		&clk_i2s_mclk),
+	INIT_CK("ep93xx-i2s",		"sclk",		&clk_i2s_sclk),
+	INIT_CK("ep93xx-i2s",		"lrclk",	&clk_i2s_lrclk),
 	INIT_CK(NULL,			"pwm_clk",	&clk_pwm),
 	INIT_CK(NULL,			"m2p0",		&clk_m2p0),
 	INIT_CK(NULL,			"m2p1",		&clk_m2p1),
@@ -401,6 +428,44 @@ static int set_div_rate(struct clk *clk, unsigned long rate)
 	return 0;
 }
 
+static int set_i2s_sclk_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned val = __raw_readl(clk->enable_reg);
+
+	if (rate == clk_i2s_mclk.rate / 2)
+		ep93xx_syscon_swlocked_write(val & ~EP93XX_I2SCLKDIV_SDIV, 
+					     clk->enable_reg);
+	else if (rate == clk_i2s_mclk.rate / 4)
+		ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_SDIV, 
+					     clk->enable_reg);
+	else
+		return -EINVAL;
+
+	clk_i2s_sclk.rate = rate;
+	return 0;
+}
+
+static int set_i2s_lrclk_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned val = __raw_readl(clk->enable_reg) & 
+		~EP93XX_I2SCLKDIV_LRDIV_MASK;
+	
+	if (rate == clk_i2s_sclk.rate / 32)
+		ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV32,
+					     clk->enable_reg);
+	else if (rate == clk_i2s_sclk.rate / 64)
+		ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV64,
+					     clk->enable_reg);
+	else if (rate == clk_i2s_sclk.rate / 128)
+		ep93xx_syscon_swlocked_write(val | EP93XX_I2SCLKDIV_LRDIV128,
+					     clk->enable_reg);
+	else
+		return -EINVAL;
+
+	clk_i2s_lrclk.rate = rate;
+	return 0;
+}
+
 int clk_set_rate(struct clk *clk, unsigned long rate)
 {
 	if (clk->set_rate)
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index 8e37a045188c..4cb55d3902ff 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -758,6 +758,73 @@ void ep93xx_keypad_release_gpio(struct platform_device *pdev)
 }
 EXPORT_SYMBOL(ep93xx_keypad_release_gpio);
 
+/*************************************************************************
+ * EP93xx I2S audio peripheral handling
+ *************************************************************************/
+static struct resource ep93xx_i2s_resource[] = {
+	{
+		.start	= EP93XX_I2S_PHYS_BASE,
+		.end	= EP93XX_I2S_PHYS_BASE + 0x100 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device ep93xx_i2s_device = {
+	.name		= "ep93xx-i2s",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(ep93xx_i2s_resource),
+	.resource	= ep93xx_i2s_resource,
+};
+
+void __init ep93xx_register_i2s(void)
+{
+	platform_device_register(&ep93xx_i2s_device);
+}
+
+#define EP93XX_SYSCON_DEVCFG_I2S_MASK	(EP93XX_SYSCON_DEVCFG_I2SONSSP | \
+					 EP93XX_SYSCON_DEVCFG_I2SONAC97)
+
+#define EP93XX_I2SCLKDIV_MASK		(EP93XX_SYSCON_I2SCLKDIV_ORIDE | \
+					 EP93XX_SYSCON_I2SCLKDIV_SPOL)
+
+int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config)
+{
+	unsigned val;
+
+	/* Sanity check */
+	if (i2s_pins & ~EP93XX_SYSCON_DEVCFG_I2S_MASK)
+		return -EINVAL;
+	if (i2s_config & ~EP93XX_I2SCLKDIV_MASK)
+		return -EINVAL;
+
+	/* Must have only one of I2SONSSP/I2SONAC97 set */
+	if ((i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONSSP) ==
+	    (i2s_pins & EP93XX_SYSCON_DEVCFG_I2SONAC97))
+		return -EINVAL;
+
+	ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2S_MASK);
+	ep93xx_devcfg_set_bits(i2s_pins);
+
+	/*
+	 * This is potentially racy with the clock api for i2s_mclk, sclk and 
+	 * lrclk. Since the i2s driver is the only user of those clocks we
+	 * rely on it to prevent parallel use of this function and the 
+	 * clock api for the i2s clocks.
+	 */
+	val = __raw_readl(EP93XX_SYSCON_I2SCLKDIV);
+	val &= ~EP93XX_I2SCLKDIV_MASK;
+	val |= i2s_config;
+	ep93xx_syscon_swlocked_write(val, EP93XX_SYSCON_I2SCLKDIV);
+
+	return 0;
+}
+EXPORT_SYMBOL(ep93xx_i2s_acquire);
+
+void ep93xx_i2s_release(void)
+{
+	ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2S_MASK);
+}
+EXPORT_SYMBOL(ep93xx_i2s_release);
 
 extern void ep93xx_gpio_init(void);
 
diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
index b1e096f0c2d2..c54b3e56ba63 100644
--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
+++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
@@ -93,6 +93,7 @@
 /* APB peripherals */
 #define EP93XX_TIMER_BASE		EP93XX_APB_IOMEM(0x00010000)
 
+#define EP93XX_I2S_PHYS_BASE		EP93XX_APB_PHYS(0x00020000)
 #define EP93XX_I2S_BASE			EP93XX_APB_IOMEM(0x00020000)
 
 #define EP93XX_SECURITY_BASE		EP93XX_APB_IOMEM(0x00030000)
@@ -194,6 +195,15 @@
 #define EP93XX_SYSCON_CLKDIV_ESEL	(1<<14)
 #define EP93XX_SYSCON_CLKDIV_PSEL	(1<<13)
 #define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT	8
+#define EP93XX_SYSCON_I2SCLKDIV		EP93XX_SYSCON_REG(0x8c)
+#define EP93XX_SYSCON_I2SCLKDIV_SENA	(1<<31)
+#define EP93XX_SYSCON_I2SCLKDIV_ORIDE   (1<<29)
+#define EP93XX_SYSCON_I2SCLKDIV_SPOL	(1<<19)
+#define EP93XX_I2SCLKDIV_SDIV		(1 << 16)
+#define EP93XX_I2SCLKDIV_LRDIV32	(0 << 17)
+#define EP93XX_I2SCLKDIV_LRDIV64	(1 << 17)
+#define EP93XX_I2SCLKDIV_LRDIV128 	(2 << 17)
+#define EP93XX_I2SCLKDIV_LRDIV_MASK 	(3 << 17)
 #define EP93XX_SYSCON_KEYTCHCLKDIV	EP93XX_SYSCON_REG(0x90)
 #define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN	(1<<31)
 #define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV	(1<<16)
diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
index a6c09176334c..3330b36d79e6 100644
--- a/arch/arm/mach-ep93xx/include/mach/platform.h
+++ b/arch/arm/mach-ep93xx/include/mach/platform.h
@@ -58,6 +58,9 @@ void ep93xx_pwm_release_gpio(struct platform_device *pdev);
 void ep93xx_register_keypad(struct ep93xx_keypad_platform_data *data);
 int ep93xx_keypad_acquire_gpio(struct platform_device *pdev);
 void ep93xx_keypad_release_gpio(struct platform_device *pdev);
+void ep93xx_register_i2s(void);
+int ep93xx_i2s_acquire(unsigned i2s_pins, unsigned i2s_config);
+void ep93xx_i2s_release(void);
 
 void ep93xx_init_devices(void);
 extern struct sys_timer ep93xx_timer;
diff --git a/arch/arm/mach-ep93xx/snappercl15.c b/arch/arm/mach-ep93xx/snappercl15.c
index 38deaee40397..a12c89301297 100644
--- a/arch/arm/mach-ep93xx/snappercl15.c
+++ b/arch/arm/mach-ep93xx/snappercl15.c
@@ -157,6 +157,7 @@ static void __init snappercl15_init_machine(void)
 	ep93xx_register_i2c(&snappercl15_i2c_gpio_data, snappercl15_i2c_data,
 			    ARRAY_SIZE(snappercl15_i2c_data));
 	ep93xx_register_fb(&snappercl15_fb_info);
+	ep93xx_register_i2s();
 	platform_device_register(&snappercl15_nand_device);
 }