浏览代码

Merge branch 'topic/asoc' into to-push

Takashi Iwai 16 年之前
父节点
当前提交
5c8261e44e
共有 100 个文件被更改,包括 8909 次插入3968 次删除
  1. 4 4
      Documentation/sound/alsa/soc/machine.txt
  2. 1 1
      MAINTAINERS
  3. 13 0
      arch/arm/mach-pxa/include/mach/palmasoc.h
  4. 34 4
      include/linux/mfd/wm8350/audio.h
  5. 18 0
      include/sound/l3.h
  6. 14 0
      include/sound/s3c24xx_uda134x.h
  7. 231 0
      include/sound/soc-dai.h
  8. 0 2
      include/sound/soc-dapm.h
  9. 36 170
      include/sound/soc.h
  10. 26 0
      include/sound/uda134x.h
  11. 6 7
      sound/soc/Kconfig
  12. 10 2
      sound/soc/Makefile
  13. 0 34
      sound/soc/at32/Kconfig
  14. 0 11
      sound/soc/at32/Makefile
  15. 0 492
      sound/soc/at32/at32-pcm.c
  16. 0 79
      sound/soc/at32/at32-pcm.h
  17. 0 849
      sound/soc/at32/at32-ssc.c
  18. 0 59
      sound/soc/at32/at32-ssc.h
  19. 0 10
      sound/soc/at91/Kconfig
  20. 0 6
      sound/soc/at91/Makefile
  21. 0 434
      sound/soc/at91/at91-pcm.c
  22. 0 72
      sound/soc/at91/at91-pcm.h
  23. 0 791
      sound/soc/at91/at91-ssc.c
  24. 0 27
      sound/soc/at91/at91-ssc.h
  25. 43 0
      sound/soc/atmel/Kconfig
  26. 15 0
      sound/soc/atmel/Makefile
  27. 494 0
      sound/soc/atmel/atmel-pcm.c
  28. 86 0
      sound/soc/atmel/atmel-pcm.h
  29. 790 0
      sound/soc/atmel/atmel_ssc_dai.c
  30. 121 0
      sound/soc/atmel/atmel_ssc_dai.h
  31. 5 6
      sound/soc/atmel/playpaq_wm8510.c
  32. 328 0
      sound/soc/atmel/sam9g20_wm8731.c
  33. 2 1
      sound/soc/au1x/dbdma2.c
  34. 8 8
      sound/soc/au1x/psc-ac97.c
  35. 8 10
      sound/soc/au1x/psc-i2s.c
  36. 2 2
      sound/soc/au1x/sample-ac97.c
  37. 15 7
      sound/soc/blackfin/Kconfig
  38. 62 51
      sound/soc/blackfin/bf5xx-ac97-pcm.c
  39. 110 68
      sound/soc/blackfin/bf5xx-ac97.c
  40. 30 5
      sound/soc/blackfin/bf5xx-ac97.h
  41. 4 4
      sound/soc/blackfin/bf5xx-ad1980.c
  42. 5 5
      sound/soc/blackfin/bf5xx-ad73311.c
  43. 12 0
      sound/soc/blackfin/bf5xx-i2s-pcm.c
  44. 21 10
      sound/soc/blackfin/bf5xx-i2s.c
  45. 1 1
      sound/soc/blackfin/bf5xx-sport.h
  46. 7 7
      sound/soc/blackfin/bf5xx-ssm2602.c
  47. 49 20
      sound/soc/codecs/Kconfig
  48. 12 0
      sound/soc/codecs/Makefile
  49. 4 3
      sound/soc/codecs/ac97.c
  50. 18 6
      sound/soc/codecs/ad1980.c
  51. 13 5
      sound/soc/codecs/ad73311.c
  52. 15 4
      sound/soc/codecs/ak4535.c
  53. 32 6
      sound/soc/codecs/cs4270.c
  54. 91 0
      sound/soc/codecs/l3.c
  55. 212 0
      sound/soc/codecs/pcm3008.c
  56. 25 0
      sound/soc/codecs/pcm3008.h
  57. 46 11
      sound/soc/codecs/ssm2602.c
  58. 206 56
      sound/soc/codecs/tlv320aic23.c
  59. 15 7
      sound/soc/codecs/tlv320aic26.c
  60. 126 40
      sound/soc/codecs/tlv320aic3x.c
  61. 59 1
      sound/soc/codecs/tlv320aic3x.h
  62. 1317 0
      sound/soc/codecs/twl4030.c
  63. 219 0
      sound/soc/codecs/twl4030.h
  64. 668 0
      sound/soc/codecs/uda134x.c
  65. 36 0
      sound/soc/codecs/uda134x.h
  66. 19 10
      sound/soc/codecs/uda1380.c
  67. 1583 0
      sound/soc/codecs/wm8350.c
  68. 20 0
      sound/soc/codecs/wm8350.h
  69. 15 4
      sound/soc/codecs/wm8510.c
  70. 70 64
      sound/soc/codecs/wm8580.c
  71. 1 0
      sound/soc/codecs/wm8580.h
  72. 585 0
      sound/soc/codecs/wm8728.c
  73. 30 0
      sound/soc/codecs/wm8728.h
  74. 19 6
      sound/soc/codecs/wm8731.c
  75. 15 4
      sound/soc/codecs/wm8750.c
  76. 24 15
      sound/soc/codecs/wm8753.c
  77. 107 155
      sound/soc/codecs/wm8900.c
  78. 0 6
      sound/soc/codecs/wm8900.h
  79. 125 143
      sound/soc/codecs/wm8903.c
  80. 0 5
      sound/soc/codecs/wm8903.h
  81. 15 4
      sound/soc/codecs/wm8971.c
  82. 34 9
      sound/soc/codecs/wm8990.c
  83. 2 2
      sound/soc/codecs/wm8990.h
  84. 7 5
      sound/soc/codecs/wm9712.c
  85. 20 26
      sound/soc/codecs/wm9713.c
  86. 10 0
      sound/soc/davinci/Kconfig
  87. 2 0
      sound/soc/davinci/Makefile
  88. 7 7
      sound/soc/davinci/davinci-evm.c
  89. 194 63
      sound/soc/davinci/davinci-i2s.c
  90. 18 12
      sound/soc/davinci/davinci-pcm.c
  91. 157 0
      sound/soc/davinci/davinci-sffsdr.c
  92. 1 1
      sound/soc/fsl/Kconfig
  93. 13 1
      sound/soc/fsl/fsl_dma.c
  94. 18 6
      sound/soc/fsl/fsl_ssi.c
  95. 14 8
      sound/soc/fsl/mpc5200_psc_i2s.c
  96. 4 4
      sound/soc/fsl/mpc8610_hpcd.c
  97. 6 6
      sound/soc/fsl/soc-of-simple.c
  98. 34 1
      sound/soc/omap/Kconfig
  99. 8 0
      sound/soc/omap/Makefile
  100. 7 3
      sound/soc/omap/n810.c

+ 4 - 4
Documentation/sound/alsa/soc/machine.txt

@@ -9,7 +9,7 @@ the audio subsystem with the kernel as a platform device and is represented by
 the following struct:-
 the following struct:-
 
 
 /* SoC machine */
 /* SoC machine */
-struct snd_soc_machine {
+struct snd_soc_card {
 	char *name;
 	char *name;
 
 
 	int (*probe)(struct platform_device *pdev);
 	int (*probe)(struct platform_device *pdev);
@@ -67,10 +67,10 @@ static struct snd_soc_dai_link corgi_dai = {
 	.ops = &corgi_ops,
 	.ops = &corgi_ops,
 };
 };
 
 
-struct snd_soc_machine then sets up the machine with it's DAIs. e.g.
+struct snd_soc_card then sets up the machine with it's DAIs. e.g.
 
 
 /* corgi audio machine driver */
 /* corgi audio machine driver */
-static struct snd_soc_machine snd_soc_machine_corgi = {
+static struct snd_soc_card snd_soc_corgi = {
 	.name = "Corgi",
 	.name = "Corgi",
 	.dai_link = &corgi_dai,
 	.dai_link = &corgi_dai,
 	.num_links = 1,
 	.num_links = 1,
@@ -90,7 +90,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = {
 
 
 /* corgi audio subsystem */
 /* corgi audio subsystem */
 static struct snd_soc_device corgi_snd_devdata = {
 static struct snd_soc_device corgi_snd_devdata = {
-	.machine = &snd_soc_machine_corgi,
+	.machine = &snd_soc_corgi,
 	.platform = &pxa2xx_soc_platform,
 	.platform = &pxa2xx_soc_platform,
 	.codec_dev = &soc_codec_dev_wm8731,
 	.codec_dev = &soc_codec_dev_wm8731,
 	.codec_data = &corgi_wm8731_setup,
 	.codec_data = &corgi_wm8731_setup,

+ 1 - 1
MAINTAINERS

@@ -3977,7 +3977,7 @@ M:	tiwai@suse.de
 L:	alsa-devel@alsa-project.org (subscribers-only)
 L:	alsa-devel@alsa-project.org (subscribers-only)
 S:	Maintained
 S:	Maintained
 
 
-SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT
+SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
 P:	Liam Girdwood
 P:	Liam Girdwood
 M:	lrg@slimlogic.co.uk
 M:	lrg@slimlogic.co.uk
 P:	Mark Brown
 P:	Mark Brown

+ 13 - 0
arch/arm/mach-pxa/include/mach/palmasoc.h

@@ -0,0 +1,13 @@
+#ifndef _INCLUDE_PALMASOC_H_
+#define _INCLUDE_PALMASOC_H_
+struct palm27x_asoc_info {
+	int	jack_gpio;
+};
+
+#ifdef CONFIG_SND_PXA2XX_SOC_PALM27X
+void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data);
+#else
+static inline void palm27x_asoc_set_pdata(struct palm27x_asoc_info *data) {}
+#endif
+
+#endif

+ 34 - 4
include/linux/mfd/wm8350/audio.h

@@ -1,7 +1,7 @@
 /*
 /*
  * audio.h  --  Audio Driver for Wolfson WM8350 PMIC
  * audio.h  --  Audio Driver for Wolfson WM8350 PMIC
  *
  *
- * Copyright 2007 Wolfson Microelectronics PLC
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC
  *
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  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
  *  under  the terms of  the GNU General  Public License as published by the
@@ -70,9 +70,9 @@
 #define WM8350_CODEC_ISEL_0_5                   3	/* x0.5 */
 #define WM8350_CODEC_ISEL_0_5                   3	/* x0.5 */
 
 
 #define WM8350_VMID_OFF                         0
 #define WM8350_VMID_OFF                         0
-#define WM8350_VMID_500K                        1
-#define WM8350_VMID_100K                        2
-#define WM8350_VMID_10K                         3
+#define WM8350_VMID_300K                        1
+#define WM8350_VMID_50K                         2
+#define WM8350_VMID_5K                          3
 
 
 /*
 /*
  * R40 (0x28) - Clock Control 1
  * R40 (0x28) - Clock Control 1
@@ -591,8 +591,38 @@
 #define WM8350_IRQ_CODEC_MICSCD			41
 #define WM8350_IRQ_CODEC_MICSCD			41
 #define WM8350_IRQ_CODEC_MICD			42
 #define WM8350_IRQ_CODEC_MICD			42
 
 
+/*
+ * WM8350 Platform data.
+ *
+ * This must be initialised per platform for best audio performance.
+ * Please see WM8350 datasheet for information.
+ */
+struct wm8350_audio_platform_data {
+	int vmid_discharge_msecs;	/* VMID --> OFF discharge time */
+	int drain_msecs;	/* OFF drain time */
+	int cap_discharge_msecs;	/* Cap ON (from OFF) discharge time */
+	int vmid_charge_msecs;	/* vmid power up time */
+	u32 vmid_s_curve:2;	/* vmid enable s curve speed */
+	u32 dis_out4:2;		/* out4 discharge speed */
+	u32 dis_out3:2;		/* out3 discharge speed */
+	u32 dis_out2:2;		/* out2 discharge speed */
+	u32 dis_out1:2;		/* out1 discharge speed */
+	u32 vroi_out4:1;	/* out4 tie off */
+	u32 vroi_out3:1;	/* out3 tie off */
+	u32 vroi_out2:1;	/* out2 tie off */
+	u32 vroi_out1:1;	/* out1 tie off */
+	u32 vroi_enable:1;	/* enable tie off */
+	u32 codec_current_on:2;	/* current level ON */
+	u32 codec_current_standby:2;	/* current level STANDBY */
+	u32 codec_current_charge:2;	/* codec current @ vmid charge */
+};
+
+struct snd_soc_codec;
+
 struct wm8350_codec {
 struct wm8350_codec {
 	struct platform_device *pdev;
 	struct platform_device *pdev;
+	struct snd_soc_codec *codec;
+	struct wm8350_audio_platform_data *platform_data;
 };
 };
 
 
 #endif
 #endif

+ 18 - 0
include/sound/l3.h

@@ -0,0 +1,18 @@
+#ifndef _L3_H_
+#define _L3_H_ 1
+
+struct l3_pins {
+	void (*setdat)(int);
+	void (*setclk)(int);
+	void (*setmode)(int);
+	int data_hold;
+	int data_setup;
+	int clock_high;
+	int mode_hold;
+	int mode;
+	int mode_setup;
+};
+
+int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len);
+
+#endif

+ 14 - 0
include/sound/s3c24xx_uda134x.h

@@ -0,0 +1,14 @@
+#ifndef _S3C24XX_UDA134X_H_
+#define _S3C24XX_UDA134X_H_ 1
+
+#include <sound/uda134x.h>
+
+struct s3c24xx_uda134x_platform_data {
+	int l3_clk;
+	int l3_mode;
+	int l3_data;
+	void (*power) (int);
+	int model;
+};
+
+#endif

+ 231 - 0
include/sound/soc-dai.h

@@ -0,0 +1,231 @@
+/*
+ * linux/sound/soc-dai.h -- ALSA SoC Layer
+ *
+ * Copyright:	2005-2008 Wolfson Microelectronics. PLC.
+ *
+ * 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.
+ *
+ * Digital Audio Interface (DAI) API.
+ */
+
+#ifndef __LINUX_SND_SOC_DAI_H
+#define __LINUX_SND_SOC_DAI_H
+
+
+#include <linux/list.h>
+
+struct snd_pcm_substream;
+
+/*
+ * DAI hardware audio formats.
+ *
+ * Describes the physical PCM data formating and clocking. Add new formats
+ * to the end.
+ */
+#define SND_SOC_DAIFMT_I2S		0 /* I2S mode */
+#define SND_SOC_DAIFMT_RIGHT_J		1 /* Right Justified mode */
+#define SND_SOC_DAIFMT_LEFT_J		2 /* Left Justified mode */
+#define SND_SOC_DAIFMT_DSP_A		3 /* L data msb after FRM LRC */
+#define SND_SOC_DAIFMT_DSP_B		4 /* L data msb during FRM LRC */
+#define SND_SOC_DAIFMT_AC97		5 /* AC97 */
+
+/* left and right justified also known as MSB and LSB respectively */
+#define SND_SOC_DAIFMT_MSB		SND_SOC_DAIFMT_LEFT_J
+#define SND_SOC_DAIFMT_LSB		SND_SOC_DAIFMT_RIGHT_J
+
+/*
+ * DAI Clock gating.
+ *
+ * DAI bit clocks can be be gated (disabled) when not the DAI is not
+ * sending or receiving PCM data in a frame. This can be used to save power.
+ */
+#define SND_SOC_DAIFMT_CONT		(0 << 4) /* continuous clock */
+#define SND_SOC_DAIFMT_GATED		(1 << 4) /* clock is gated */
+
+/*
+ * DAI Left/Right Clocks.
+ *
+ * Specifies whether the DAI can support different samples for similtanious
+ * playback and capture. This usually requires a seperate physical frame
+ * clock for playback and capture.
+ */
+#define SND_SOC_DAIFMT_SYNC		(0 << 5) /* Tx FRM = Rx FRM */
+#define SND_SOC_DAIFMT_ASYNC		(1 << 5) /* Tx FRM ~ Rx FRM */
+
+/*
+ * TDM
+ *
+ * Time Division Multiplexing. Allows PCM data to be multplexed with other
+ * data on the DAI.
+ */
+#define SND_SOC_DAIFMT_TDM		(1 << 6)
+
+/*
+ * DAI hardware signal inversions.
+ *
+ * Specifies whether the DAI can also support inverted clocks for the specified
+ * format.
+ */
+#define SND_SOC_DAIFMT_NB_NF		(0 << 8) /* normal bit clock + frame */
+#define SND_SOC_DAIFMT_NB_IF		(1 << 8) /* normal bclk + inv frm */
+#define SND_SOC_DAIFMT_IB_NF		(2 << 8) /* invert bclk + nor frm */
+#define SND_SOC_DAIFMT_IB_IF		(3 << 8) /* invert bclk + frm */
+
+/*
+ * DAI hardware clock masters.
+ *
+ * This is wrt the codec, the inverse is true for the interface
+ * i.e. if the codec is clk and frm master then the interface is
+ * clk and frame slave.
+ */
+#define SND_SOC_DAIFMT_CBM_CFM		(0 << 12) /* codec clk & frm master */
+#define SND_SOC_DAIFMT_CBS_CFM		(1 << 12) /* codec clk slave & frm master */
+#define SND_SOC_DAIFMT_CBM_CFS		(2 << 12) /* codec clk master & frame slave */
+#define SND_SOC_DAIFMT_CBS_CFS		(3 << 12) /* codec clk & frm slave */
+
+#define SND_SOC_DAIFMT_FORMAT_MASK	0x000f
+#define SND_SOC_DAIFMT_CLOCK_MASK	0x00f0
+#define SND_SOC_DAIFMT_INV_MASK		0x0f00
+#define SND_SOC_DAIFMT_MASTER_MASK	0xf000
+
+/*
+ * Master Clock Directions
+ */
+#define SND_SOC_CLOCK_IN		0
+#define SND_SOC_CLOCK_OUT		1
+
+struct snd_soc_dai_ops;
+struct snd_soc_dai;
+struct snd_ac97_bus_ops;
+
+/* Digital Audio Interface registration */
+int snd_soc_register_dai(struct snd_soc_dai *dai);
+void snd_soc_unregister_dai(struct snd_soc_dai *dai);
+int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count);
+void snd_soc_unregister_dais(struct snd_soc_dai *dai, size_t count);
+
+/* Digital Audio Interface clocking API.*/
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+	unsigned int freq, int dir);
+
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+	int div_id, int div);
+
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
+	int pll_id, unsigned int freq_in, unsigned int freq_out);
+
+/* Digital Audio interface formatting */
+int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
+
+int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int mask, int slots);
+
+int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
+
+/* Digital Audio Interface mute */
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
+
+/*
+ * Digital Audio Interface.
+ *
+ * Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
+ * operations an capabilities. Codec and platfom drivers will register a this
+ * structure for every DAI they have.
+ *
+ * This structure covers the clocking, formating and ALSA operations for each
+ * interface a
+ */
+struct snd_soc_dai_ops {
+	/*
+	 * DAI clocking configuration, all optional.
+	 * Called by soc_card drivers, normally in their hw_params.
+	 */
+	int (*set_sysclk)(struct snd_soc_dai *dai,
+		int clk_id, unsigned int freq, int dir);
+	int (*set_pll)(struct snd_soc_dai *dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out);
+	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
+
+	/*
+	 * DAI format configuration
+	 * Called by soc_card drivers, normally in their hw_params.
+	 */
+	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
+	int (*set_tdm_slot)(struct snd_soc_dai *dai,
+		unsigned int mask, int slots);
+	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
+
+	/*
+	 * DAI digital mute - optional.
+	 * Called by soc-core to minimise any pops.
+	 */
+	int (*digital_mute)(struct snd_soc_dai *dai, int mute);
+
+	/*
+	 * ALSA PCM audio operations - all optional.
+	 * Called by soc-core during audio PCM operations.
+	 */
+	int (*startup)(struct snd_pcm_substream *,
+		struct snd_soc_dai *);
+	void (*shutdown)(struct snd_pcm_substream *,
+		struct snd_soc_dai *);
+	int (*hw_params)(struct snd_pcm_substream *,
+		struct snd_pcm_hw_params *, struct snd_soc_dai *);
+	int (*hw_free)(struct snd_pcm_substream *,
+		struct snd_soc_dai *);
+	int (*prepare)(struct snd_pcm_substream *,
+		struct snd_soc_dai *);
+	int (*trigger)(struct snd_pcm_substream *, int,
+		struct snd_soc_dai *);
+};
+
+/*
+ * Digital Audio Interface runtime data.
+ *
+ * Holds runtime data for a DAI.
+ */
+struct snd_soc_dai {
+	/* DAI description */
+	char *name;
+	unsigned int id;
+	int ac97_control;
+
+	struct device *dev;
+
+	/* DAI callbacks */
+	int (*probe)(struct platform_device *pdev,
+		     struct snd_soc_dai *dai);
+	void (*remove)(struct platform_device *pdev,
+		       struct snd_soc_dai *dai);
+	int (*suspend)(struct snd_soc_dai *dai);
+	int (*resume)(struct snd_soc_dai *dai);
+
+	/* ops */
+	struct snd_soc_dai_ops ops;
+
+	/* DAI capabilities */
+	struct snd_soc_pcm_stream capture;
+	struct snd_soc_pcm_stream playback;
+
+	/* DAI runtime info */
+	struct snd_pcm_runtime *runtime;
+	struct snd_soc_codec *codec;
+	unsigned int active;
+	unsigned char pop_wait:1;
+	void *dma_data;
+
+	/* DAI private data */
+	void *private_data;
+
+	/* parent codec/platform */
+	union {
+		struct snd_soc_codec *codec;
+		struct snd_soc_platform *platform;
+	};
+
+	struct list_head list;
+};
+
+#endif

+ 0 - 2
include/sound/soc-dapm.h

@@ -221,8 +221,6 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
 	int num);
 	int num);
 
 
 /* dapm path setup */
 /* dapm path setup */
-int  __deprecated snd_soc_dapm_connect_input(struct snd_soc_codec *codec,
-	const char *sink_name, const char *control_name, const char *src_name);
 int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
 int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec);
 void snd_soc_dapm_free(struct snd_soc_device *socdev);
 void snd_soc_dapm_free(struct snd_soc_device *socdev);
 int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
 int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,

+ 36 - 170
include/sound/soc.h

@@ -21,8 +21,6 @@
 #include <sound/control.h>
 #include <sound/control.h>
 #include <sound/ac97_codec.h>
 #include <sound/ac97_codec.h>
 
 
-#define SND_SOC_VERSION "0.13.2"
-
 /*
 /*
  * Convenience kcontrol builders
  * Convenience kcontrol builders
  */
  */
@@ -145,105 +143,31 @@ enum snd_soc_bias_level {
 	SND_SOC_BIAS_OFF,
 	SND_SOC_BIAS_OFF,
 };
 };
 
 
-/*
- * Digital Audio Interface (DAI) types
- */
-#define SND_SOC_DAI_AC97	0x1
-#define SND_SOC_DAI_I2S		0x2
-#define SND_SOC_DAI_PCM		0x4
-#define SND_SOC_DAI_AC97_BUS	0x8	/* for custom i.e. non ac97_codec.c */
-
-/*
- * DAI hardware audio formats
- */
-#define SND_SOC_DAIFMT_I2S		0	/* I2S mode */
-#define SND_SOC_DAIFMT_RIGHT_J	1	/* Right justified mode */
-#define SND_SOC_DAIFMT_LEFT_J	2	/* Left Justified mode */
-#define SND_SOC_DAIFMT_DSP_A	3	/* L data msb after FRM or LRC */
-#define SND_SOC_DAIFMT_DSP_B	4	/* L data msb during FRM or LRC */
-#define SND_SOC_DAIFMT_AC97		5	/* AC97 */
-
-#define SND_SOC_DAIFMT_MSB 	SND_SOC_DAIFMT_LEFT_J
-#define SND_SOC_DAIFMT_LSB	SND_SOC_DAIFMT_RIGHT_J
-
-/*
- * DAI Gating
- */
-#define SND_SOC_DAIFMT_CONT			(0 << 4)	/* continuous clock */
-#define SND_SOC_DAIFMT_GATED		(1 << 4)	/* clock is gated when not Tx/Rx */
-
-/*
- * DAI Sync
- * Synchronous LR (Left Right) clocks and Frame signals.
- */
-#define SND_SOC_DAIFMT_SYNC		(0 << 5)	/* Tx FRM = Rx FRM */
-#define SND_SOC_DAIFMT_ASYNC		(1 << 5)	/* Tx FRM ~ Rx FRM */
-
-/*
- * TDM
- */
-#define SND_SOC_DAIFMT_TDM		(1 << 6)
-
-/*
- * DAI hardware signal inversions
- */
-#define SND_SOC_DAIFMT_NB_NF		(0 << 8)	/* normal bclk + frm */
-#define SND_SOC_DAIFMT_NB_IF		(1 << 8)	/* normal bclk + inv frm */
-#define SND_SOC_DAIFMT_IB_NF		(2 << 8)	/* invert bclk + nor frm */
-#define SND_SOC_DAIFMT_IB_IF		(3 << 8)	/* invert bclk + frm */
-
-/*
- * DAI hardware clock masters
- * This is wrt the codec, the inverse is true for the interface
- * i.e. if the codec is clk and frm master then the interface is
- * clk and frame slave.
- */
-#define SND_SOC_DAIFMT_CBM_CFM	(0 << 12) /* codec clk & frm master */
-#define SND_SOC_DAIFMT_CBS_CFM	(1 << 12) /* codec clk slave & frm master */
-#define SND_SOC_DAIFMT_CBM_CFS	(2 << 12) /* codec clk master & frame slave */
-#define SND_SOC_DAIFMT_CBS_CFS	(3 << 12) /* codec clk & frm slave */
-
-#define SND_SOC_DAIFMT_FORMAT_MASK		0x000f
-#define SND_SOC_DAIFMT_CLOCK_MASK		0x00f0
-#define SND_SOC_DAIFMT_INV_MASK			0x0f00
-#define SND_SOC_DAIFMT_MASTER_MASK		0xf000
-
-
-/*
- * Master Clock Directions
- */
-#define SND_SOC_CLOCK_IN	0
-#define SND_SOC_CLOCK_OUT	1
-
-/*
- * AC97 codec ID's bitmask
- */
-#define SND_SOC_DAI_AC97_ID0	(1 << 0)
-#define SND_SOC_DAI_AC97_ID1	(1 << 1)
-#define SND_SOC_DAI_AC97_ID2	(1 << 2)
-#define SND_SOC_DAI_AC97_ID3	(1 << 3)
-
 struct snd_soc_device;
 struct snd_soc_device;
 struct snd_soc_pcm_stream;
 struct snd_soc_pcm_stream;
 struct snd_soc_ops;
 struct snd_soc_ops;
 struct snd_soc_dai_mode;
 struct snd_soc_dai_mode;
 struct snd_soc_pcm_runtime;
 struct snd_soc_pcm_runtime;
 struct snd_soc_dai;
 struct snd_soc_dai;
+struct snd_soc_platform;
 struct snd_soc_codec;
 struct snd_soc_codec;
-struct snd_soc_machine_config;
 struct soc_enum;
 struct soc_enum;
 struct snd_soc_ac97_ops;
 struct snd_soc_ac97_ops;
-struct snd_soc_clock_info;
 
 
 typedef int (*hw_write_t)(void *,const char* ,int);
 typedef int (*hw_write_t)(void *,const char* ,int);
 typedef int (*hw_read_t)(void *,char* ,int);
 typedef int (*hw_read_t)(void *,char* ,int);
 
 
 extern struct snd_ac97_bus_ops soc_ac97_ops;
 extern struct snd_ac97_bus_ops soc_ac97_ops;
 
 
+int snd_soc_register_platform(struct snd_soc_platform *platform);
+void snd_soc_unregister_platform(struct snd_soc_platform *platform);
+int snd_soc_register_codec(struct snd_soc_codec *codec);
+void snd_soc_unregister_codec(struct snd_soc_codec *codec);
+
 /* pcm <-> DAI connect */
 /* pcm <-> DAI connect */
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
-int snd_soc_register_card(struct snd_soc_device *socdev);
+int snd_soc_init_card(struct snd_soc_device *socdev);
 
 
 /* set runtime hw params */
 /* set runtime hw params */
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
@@ -263,27 +187,6 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
 	struct snd_ac97_bus_ops *ops, int num);
 	struct snd_ac97_bus_ops *ops, int num);
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
 
 
-/* Digital Audio Interface clocking API.*/
-int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
-	unsigned int freq, int dir);
-
-int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
-	int div_id, int div);
-
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-	int pll_id, unsigned int freq_in, unsigned int freq_out);
-
-/* Digital Audio interface formatting */
-int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
-
-int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
-	unsigned int mask, int slots);
-
-int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
-
-/* Digital Audio Interface mute */
-int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
-
 /*
 /*
  *Controls
  *Controls
  */
  */
@@ -341,66 +244,14 @@ struct snd_soc_ops {
 	int (*trigger)(struct snd_pcm_substream *, int);
 	int (*trigger)(struct snd_pcm_substream *, int);
 };
 };
 
 
-/* ASoC DAI ops */
-struct snd_soc_dai_ops {
-	/* DAI clocking configuration */
-	int (*set_sysclk)(struct snd_soc_dai *dai,
-		int clk_id, unsigned int freq, int dir);
-	int (*set_pll)(struct snd_soc_dai *dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out);
-	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
-
-	/* DAI format configuration */
-	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
-	int (*set_tdm_slot)(struct snd_soc_dai *dai,
-		unsigned int mask, int slots);
-	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
-
-	/* digital mute */
-	int (*digital_mute)(struct snd_soc_dai *dai, int mute);
-};
-
-/* SoC  DAI (Digital Audio Interface) */
-struct snd_soc_dai {
-	/* DAI description */
-	char *name;
-	unsigned int id;
-	unsigned char type;
-
-	/* DAI callbacks */
-	int (*probe)(struct platform_device *pdev,
-		     struct snd_soc_dai *dai);
-	void (*remove)(struct platform_device *pdev,
-		       struct snd_soc_dai *dai);
-	int (*suspend)(struct platform_device *pdev,
-		struct snd_soc_dai *dai);
-	int (*resume)(struct platform_device *pdev,
-		struct snd_soc_dai *dai);
-
-	/* ops */
-	struct snd_soc_ops ops;
-	struct snd_soc_dai_ops dai_ops;
-
-	/* DAI capabilities */
-	struct snd_soc_pcm_stream capture;
-	struct snd_soc_pcm_stream playback;
-
-	/* DAI runtime info */
-	struct snd_pcm_runtime *runtime;
-	struct snd_soc_codec *codec;
-	unsigned int active;
-	unsigned char pop_wait:1;
-	void *dma_data;
-
-	/* DAI private data */
-	void *private_data;
-};
-
 /* SoC Audio Codec */
 /* SoC Audio Codec */
 struct snd_soc_codec {
 struct snd_soc_codec {
 	char *name;
 	char *name;
 	struct module *owner;
 	struct module *owner;
 	struct mutex mutex;
 	struct mutex mutex;
+	struct device *dev;
+
+	struct list_head list;
 
 
 	/* callbacks */
 	/* callbacks */
 	int (*set_bias_level)(struct snd_soc_codec *,
 	int (*set_bias_level)(struct snd_soc_codec *,
@@ -426,6 +277,7 @@ struct snd_soc_codec {
 	short reg_cache_step;
 	short reg_cache_step;
 
 
 	/* dapm */
 	/* dapm */
+	u32 pop_time;
 	struct list_head dapm_widgets;
 	struct list_head dapm_widgets;
 	struct list_head dapm_paths;
 	struct list_head dapm_paths;
 	enum snd_soc_bias_level bias_level;
 	enum snd_soc_bias_level bias_level;
@@ -435,6 +287,11 @@ struct snd_soc_codec {
 	/* codec DAI's */
 	/* codec DAI's */
 	struct snd_soc_dai *dai;
 	struct snd_soc_dai *dai;
 	unsigned int num_dai;
 	unsigned int num_dai;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_reg;
+	struct dentry *debugfs_pop_time;
+#endif
 };
 };
 
 
 /* codec device */
 /* codec device */
@@ -448,13 +305,12 @@ struct snd_soc_codec_device {
 /* SoC platform interface */
 /* SoC platform interface */
 struct snd_soc_platform {
 struct snd_soc_platform {
 	char *name;
 	char *name;
+	struct list_head list;
 
 
 	int (*probe)(struct platform_device *pdev);
 	int (*probe)(struct platform_device *pdev);
 	int (*remove)(struct platform_device *pdev);
 	int (*remove)(struct platform_device *pdev);
-	int (*suspend)(struct platform_device *pdev,
-		struct snd_soc_dai *dai);
-	int (*resume)(struct platform_device *pdev,
-		struct snd_soc_dai *dai);
+	int (*suspend)(struct snd_soc_dai *dai);
+	int (*resume)(struct snd_soc_dai *dai);
 
 
 	/* pcm creation and destruction */
 	/* pcm creation and destruction */
 	int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
 	int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
@@ -484,9 +340,14 @@ struct snd_soc_dai_link  {
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm;
 };
 };
 
 
-/* SoC machine */
-struct snd_soc_machine {
+/* SoC card */
+struct snd_soc_card {
 	char *name;
 	char *name;
+	struct device *dev;
+
+	struct list_head list;
+
+	int instantiated;
 
 
 	int (*probe)(struct platform_device *pdev);
 	int (*probe)(struct platform_device *pdev);
 	int (*remove)(struct platform_device *pdev);
 	int (*remove)(struct platform_device *pdev);
@@ -499,23 +360,26 @@ struct snd_soc_machine {
 	int (*resume_post)(struct platform_device *pdev);
 	int (*resume_post)(struct platform_device *pdev);
 
 
 	/* callbacks */
 	/* callbacks */
-	int (*set_bias_level)(struct snd_soc_machine *,
+	int (*set_bias_level)(struct snd_soc_card *,
 			      enum snd_soc_bias_level level);
 			      enum snd_soc_bias_level level);
 
 
 	/* CPU <--> Codec DAI links  */
 	/* CPU <--> Codec DAI links  */
 	struct snd_soc_dai_link *dai_link;
 	struct snd_soc_dai_link *dai_link;
 	int num_links;
 	int num_links;
+
+	struct snd_soc_device *socdev;
+
+	struct snd_soc_platform *platform;
+	struct delayed_work delayed_work;
+	struct work_struct deferred_resume_work;
 };
 };
 
 
 /* SoC Device - the audio subsystem */
 /* SoC Device - the audio subsystem */
 struct snd_soc_device {
 struct snd_soc_device {
 	struct device *dev;
 	struct device *dev;
-	struct snd_soc_machine *machine;
-	struct snd_soc_platform *platform;
+	struct snd_soc_card *card;
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec_device *codec_dev;
 	struct snd_soc_codec_device *codec_dev;
-	struct delayed_work delayed_work;
-	struct work_struct deferred_resume_work;
 	void *codec_data;
 	void *codec_data;
 };
 };
 
 
@@ -542,4 +406,6 @@ struct soc_enum {
 	void *dapm;
 	void *dapm;
 };
 };
 
 
+#include <sound/soc-dai.h>
+
 #endif
 #endif

+ 26 - 0
include/sound/uda134x.h

@@ -0,0 +1,26 @@
+/*
+ * uda134x.h  --  UDA134x ALSA SoC Codec driver
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * 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 _UDA134X_H
+#define _UDA134X_H
+
+#include <sound/l3.h>
+
+struct uda134x_platform_data {
+	struct l3_pins l3;
+	void (*power) (int);
+	int model;
+#define UDA134X_UDA1340 1
+#define UDA134X_UDA1341 2
+#define UDA134X_UDA1344 3
+};
+
+#endif /* _UDA134X_H */

+ 6 - 7
sound/soc/Kconfig

@@ -22,17 +22,16 @@ if SND_SOC
 config SND_SOC_AC97_BUS
 config SND_SOC_AC97_BUS
 	bool
 	bool
 
 
-# All the supported Soc's
-source "sound/soc/at32/Kconfig"
-source "sound/soc/at91/Kconfig"
+# All the supported SoCs
+source "sound/soc/atmel/Kconfig"
 source "sound/soc/au1x/Kconfig"
 source "sound/soc/au1x/Kconfig"
+source "sound/soc/blackfin/Kconfig"
+source "sound/soc/davinci/Kconfig"
+source "sound/soc/fsl/Kconfig"
+source "sound/soc/omap/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/sh/Kconfig"
 source "sound/soc/sh/Kconfig"
-source "sound/soc/fsl/Kconfig"
-source "sound/soc/davinci/Kconfig"
-source "sound/soc/omap/Kconfig"
-source "sound/soc/blackfin/Kconfig"
 
 
 # Supported codecs
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
 source "sound/soc/codecs/Kconfig"

+ 10 - 2
sound/soc/Makefile

@@ -1,5 +1,13 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o
 snd-soc-core-objs := soc-core.o soc-dapm.o
 
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
-obj-$(CONFIG_SND_SOC)	+= codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/
-obj-$(CONFIG_SND_SOC)	+= omap/ au1x/ blackfin/
+obj-$(CONFIG_SND_SOC)	+= codecs/
+obj-$(CONFIG_SND_SOC)	+= atmel/
+obj-$(CONFIG_SND_SOC)	+= au1x/
+obj-$(CONFIG_SND_SOC)	+= blackfin/
+obj-$(CONFIG_SND_SOC)	+= davinci/
+obj-$(CONFIG_SND_SOC)	+= fsl/
+obj-$(CONFIG_SND_SOC)	+= omap/
+obj-$(CONFIG_SND_SOC)	+= pxa/
+obj-$(CONFIG_SND_SOC)	+= s3c24xx/
+obj-$(CONFIG_SND_SOC)	+= sh/

+ 0 - 34
sound/soc/at32/Kconfig

@@ -1,34 +0,0 @@
-config SND_AT32_SOC
-        tristate "SoC Audio for the Atmel AT32 System-on-a-Chip"
-        depends on AVR32 && SND_SOC
-        help
-          Say Y or M if you want to add support for codecs attached to 
-          the AT32 SSC interface.  You will also need to
-          to select the audio interfaces to support below.
-
-
-config SND_AT32_SOC_SSC
-        tristate
-
-
-
-config SND_AT32_SOC_PLAYPAQ
-        tristate "SoC Audio support for PlayPaq with WM8510"
-        depends on SND_AT32_SOC && BOARD_PLAYPAQ
-        select SND_AT32_SOC_SSC
-        select SND_SOC_WM8510
-        help
-          Say Y or M here if you want to add support for SoC audio
-          on the LRS PlayPaq.
-
-
-
-config SND_AT32_SOC_PLAYPAQ_SLAVE
-        bool "Run CODEC on PlayPaq in slave mode"
-        depends on SND_AT32_SOC_PLAYPAQ
-        default n
-        help
-          Say Y if you want to run with the AT32 SSC generating the BCLK
-          and FRAME signals on the PlayPaq.  Unless you want to play
-          with the AT32 as the SSC master, you probably want to say N here,
-          as this will give you better sound quality.

+ 0 - 11
sound/soc/at32/Makefile

@@ -1,11 +0,0 @@
-# AT32 Platform Support
-snd-soc-at32-objs := at32-pcm.o
-snd-soc-at32-ssc-objs := at32-ssc.o
-
-obj-$(CONFIG_SND_AT32_SOC) += snd-soc-at32.o
-obj-$(CONFIG_SND_AT32_SOC_SSC) += snd-soc-at32-ssc.o
-
-# AT32 Machine Support
-snd-soc-playpaq-objs := playpaq_wm8510.o
-
-obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o

+ 0 - 492
sound/soc/at32/at32-pcm.c

@@ -1,492 +0,0 @@
-/* sound/soc/at32/at32-pcm.c
- * ASoC PCM interface for Atmel AT32 SoC
- *
- * Copyright (C) 2008 Long Range Systems
- *    Geoffrey Wossum <gwossum@acm.org>
- *
- * 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.
- *
- * Note that this is basically a port of the sound/soc/at91-pcm.c to
- * the AVR32 kernel.  Thanks to Frank Mandarino for that code.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/atmel_pdc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "at32-pcm.h"
-
-
-
-/*--------------------------------------------------------------------------*\
- * Hardware definition
-\*--------------------------------------------------------------------------*/
-/* TODO: These values were taken from the AT91 platform driver, check
- *	 them against real values for AT32
- */
-static const struct snd_pcm_hardware at32_pcm_hardware = {
-	.info = (SNDRV_PCM_INFO_MMAP |
-		 SNDRV_PCM_INFO_MMAP_VALID |
-		 SNDRV_PCM_INFO_INTERLEAVED |
-		 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-		 SNDRV_PCM_INFO_PAUSE),
-
-	.formats = SNDRV_PCM_FMTBIT_S16,
-	.period_bytes_min = 32,
-	.period_bytes_max = 8192,	/* 512 frames * 16 bytes / frame */
-	.periods_min = 2,
-	.periods_max = 1024,
-	.buffer_bytes_max = 32 * 1024,
-};
-
-
-
-/*--------------------------------------------------------------------------*\
- * Data types
-\*--------------------------------------------------------------------------*/
-struct at32_runtime_data {
-	struct at32_pcm_dma_params *params;
-	dma_addr_t dma_buffer;	/* physical address of DMA buffer */
-	dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
-	size_t period_size;
-
-	dma_addr_t period_ptr;	/* physical address of next period */
-	int periods;		/* period index of period_ptr */
-
-	/* Save PDC registers (for power management) */
-	u32 pdc_xpr_save;
-	u32 pdc_xcr_save;
-	u32 pdc_xnpr_save;
-	u32 pdc_xncr_save;
-};
-
-
-
-/*--------------------------------------------------------------------------*\
- * Helper functions
-\*--------------------------------------------------------------------------*/
-static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
-	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
-	struct snd_dma_buffer *dmabuf = &substream->dma_buffer;
-	size_t size = at32_pcm_hardware.buffer_bytes_max;
-
-	dmabuf->dev.type = SNDRV_DMA_TYPE_DEV;
-	dmabuf->dev.dev = pcm->card->dev;
-	dmabuf->private_data = NULL;
-	dmabuf->area = dma_alloc_coherent(pcm->card->dev, size,
-					  &dmabuf->addr, GFP_KERNEL);
-	pr_debug("at32_pcm: preallocate_dma_buffer: "
-		 "area=%p, addr=%p, size=%ld\n",
-		 (void *)dmabuf->area, (void *)dmabuf->addr, size);
-
-	if (!dmabuf->area)
-		return -ENOMEM;
-
-	dmabuf->bytes = size;
-	return 0;
-}
-
-
-
-/*--------------------------------------------------------------------------*\
- * ISR
-\*--------------------------------------------------------------------------*/
-static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *rtd = substream->runtime;
-	struct at32_runtime_data *prtd = rtd->private_data;
-	struct at32_pcm_dma_params *params = prtd->params;
-	static int count;
-
-	count++;
-	if (ssc_sr & params->mask->ssc_endbuf) {
-		pr_warning("at32-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
-			   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
-			   "underrun" : "overrun", params->name, ssc_sr, count);
-
-		/* re-start the PDC */
-		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-			   params->mask->pdc_disable);
-		prtd->period_ptr += prtd->period_size;
-		if (prtd->period_ptr >= prtd->dma_buffer_end)
-			prtd->period_ptr = prtd->dma_buffer;
-
-
-		ssc_writex(params->ssc->regs, params->pdc->xpr,
-			   prtd->period_ptr);
-		ssc_writex(params->ssc->regs, params->pdc->xcr,
-			   prtd->period_size / params->pdc_xfer_size);
-		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-			   params->mask->pdc_enable);
-	}
-
-
-	if (ssc_sr & params->mask->ssc_endx) {
-		/* Load the PDC next pointer and counter registers */
-		prtd->period_ptr += prtd->period_size;
-		if (prtd->period_ptr >= prtd->dma_buffer_end)
-			prtd->period_ptr = prtd->dma_buffer;
-		ssc_writex(params->ssc->regs, params->pdc->xnpr,
-			   prtd->period_ptr);
-		ssc_writex(params->ssc->regs, params->pdc->xncr,
-			   prtd->period_size / params->pdc_xfer_size);
-	}
-
-
-	snd_pcm_period_elapsed(substream);
-}
-
-
-
-/*--------------------------------------------------------------------------*\
- * PCM operations
-\*--------------------------------------------------------------------------*/
-static int at32_pcm_hw_params(struct snd_pcm_substream *substream,
-			      struct snd_pcm_hw_params *params)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct at32_runtime_data *prtd = runtime->private_data;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
-	/* this may get called several times by oss emulation
-	 * with different params
-	 */
-	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-	runtime->dma_bytes = params_buffer_bytes(params);
-
-	prtd->params = rtd->dai->cpu_dai->dma_data;
-	prtd->params->dma_intr_handler = at32_pcm_dma_irq;
-
-	prtd->dma_buffer = runtime->dma_addr;
-	prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
-	prtd->period_size = params_period_bytes(params);
-
-	pr_debug("hw_params: DMA for %s initialized "
-		 "(dma_bytes=%ld, period_size=%ld)\n",
-		 prtd->params->name, runtime->dma_bytes, prtd->period_size);
-
-	return 0;
-}
-
-
-
-static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-	struct at32_runtime_data *prtd = substream->runtime->private_data;
-	struct at32_pcm_dma_params *params = prtd->params;
-
-	if (params != NULL) {
-		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
-			   params->mask->pdc_disable);
-		prtd->params->dma_intr_handler = NULL;
-	}
-
-	return 0;
-}
-
-
-
-static int at32_pcm_prepare(struct snd_pcm_substream *substream)
-{
-	struct at32_runtime_data *prtd = substream->runtime->private_data;
-	struct at32_pcm_dma_params *params = prtd->params;
-
-	ssc_writex(params->ssc->regs, SSC_IDR,
-		   params->mask->ssc_endx | params->mask->ssc_endbuf);
-	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-		   params->mask->pdc_disable);
-
-	return 0;
-}
-
-
-static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct snd_pcm_runtime *rtd = substream->runtime;
-	struct at32_runtime_data *prtd = rtd->private_data;
-	struct at32_pcm_dma_params *params = prtd->params;
-	int ret = 0;
-
-	pr_debug("at32_pcm_trigger: buffer_size = %ld, "
-		 "dma_area = %p, dma_bytes = %ld\n",
-		 rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		prtd->period_ptr = prtd->dma_buffer;
-
-		ssc_writex(params->ssc->regs, params->pdc->xpr,
-			   prtd->period_ptr);
-		ssc_writex(params->ssc->regs, params->pdc->xcr,
-			   prtd->period_size / params->pdc_xfer_size);
-
-		prtd->period_ptr += prtd->period_size;
-		ssc_writex(params->ssc->regs, params->pdc->xnpr,
-			   prtd->period_ptr);
-		ssc_writex(params->ssc->regs, params->pdc->xncr,
-			   prtd->period_size / params->pdc_xfer_size);
-
-		pr_debug("trigger: period_ptr=%lx, xpr=%x, "
-			 "xcr=%d, xnpr=%x, xncr=%d\n",
-			 (unsigned long)prtd->period_ptr,
-			 ssc_readx(params->ssc->regs, params->pdc->xpr),
-			 ssc_readx(params->ssc->regs, params->pdc->xcr),
-			 ssc_readx(params->ssc->regs, params->pdc->xnpr),
-			 ssc_readx(params->ssc->regs, params->pdc->xncr));
-
-		ssc_writex(params->ssc->regs, SSC_IER,
-			   params->mask->ssc_endx | params->mask->ssc_endbuf);
-		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
-			   params->mask->pdc_enable);
-
-		pr_debug("sr=%x, imr=%x\n",
-			 ssc_readx(params->ssc->regs, SSC_SR),
-			 ssc_readx(params->ssc->regs, SSC_IER));
-		break;		/* SNDRV_PCM_TRIGGER_START */
-
-
-
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-			   params->mask->pdc_disable);
-		break;
-
-
-	case SNDRV_PCM_TRIGGER_RESUME:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-			   params->mask->pdc_enable);
-		break;
-
-	default:
-		ret = -EINVAL;
-	}
-
-	return ret;
-}
-
-
-
-static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct at32_runtime_data *prtd = runtime->private_data;
-	struct at32_pcm_dma_params *params = prtd->params;
-	dma_addr_t ptr;
-	snd_pcm_uframes_t x;
-
-	ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
-	x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
-
-	if (x == runtime->buffer_size)
-		x = 0;
-
-	return x;
-}
-
-
-
-static int at32_pcm_open(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct at32_runtime_data *prtd;
-	int ret = 0;
-
-	snd_soc_set_runtime_hwparams(substream, &at32_pcm_hardware);
-
-	/* ensure that buffer size is a multiple of period size */
-	ret = snd_pcm_hw_constraint_integer(runtime,
-					    SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		goto out;
-
-	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
-	if (prtd == NULL) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	runtime->private_data = prtd;
-
-
-out:
-	return ret;
-}
-
-
-
-static int at32_pcm_close(struct snd_pcm_substream *substream)
-{
-	struct at32_runtime_data *prtd = substream->runtime->private_data;
-
-	kfree(prtd);
-	return 0;
-}
-
-
-static int at32_pcm_mmap(struct snd_pcm_substream *substream,
-			 struct vm_area_struct *vma)
-{
-	return remap_pfn_range(vma, vma->vm_start,
-			       substream->dma_buffer.addr >> PAGE_SHIFT,
-			       vma->vm_end - vma->vm_start, vma->vm_page_prot);
-}
-
-
-
-static struct snd_pcm_ops at32_pcm_ops = {
-	.open = at32_pcm_open,
-	.close = at32_pcm_close,
-	.ioctl = snd_pcm_lib_ioctl,
-	.hw_params = at32_pcm_hw_params,
-	.hw_free = at32_pcm_hw_free,
-	.prepare = at32_pcm_prepare,
-	.trigger = at32_pcm_trigger,
-	.pointer = at32_pcm_pointer,
-	.mmap = at32_pcm_mmap,
-};
-
-
-
-/*--------------------------------------------------------------------------*\
- * ASoC platform driver
-\*--------------------------------------------------------------------------*/
-static u64 at32_pcm_dmamask = 0xffffffff;
-
-static int at32_pcm_new(struct snd_card *card,
-			struct snd_soc_dai *dai,
-			struct snd_pcm *pcm)
-{
-	int ret = 0;
-
-	if (!card->dev->dma_mask)
-		card->dev->dma_mask = &at32_pcm_dmamask;
-	if (!card->dev->coherent_dma_mask)
-		card->dev->coherent_dma_mask = 0xffffffff;
-
-	if (dai->playback.channels_min) {
-		ret = at32_pcm_preallocate_dma_buffer(
-			  pcm, SNDRV_PCM_STREAM_PLAYBACK);
-		if (ret)
-			goto out;
-	}
-
-	if (dai->capture.channels_min) {
-		pr_debug("at32-pcm: Allocating PCM capture DMA buffer\n");
-		ret = at32_pcm_preallocate_dma_buffer(
-			  pcm, SNDRV_PCM_STREAM_CAPTURE);
-		if (ret)
-			goto out;
-	}
-
-
-out:
-	return ret;
-}
-
-
-
-static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
-	struct snd_pcm_substream *substream;
-	struct snd_dma_buffer *buf;
-	int stream;
-
-	for (stream = 0; stream < 2; stream++) {
-		substream = pcm->streams[stream].substream;
-		if (substream == NULL)
-			continue;
-
-		buf = &substream->dma_buffer;
-		if (!buf->area)
-			continue;
-		dma_free_coherent(pcm->card->dev, buf->bytes,
-				  buf->area, buf->addr);
-		buf->area = NULL;
-	}
-}
-
-
-
-#ifdef CONFIG_PM
-static int at32_pcm_suspend(struct platform_device *pdev,
-			    struct snd_soc_dai *dai)
-{
-	struct snd_pcm_runtime *runtime = dai->runtime;
-	struct at32_runtime_data *prtd;
-	struct at32_pcm_dma_params *params;
-
-	if (runtime == NULL)
-		return 0;
-	prtd = runtime->private_data;
-	params = prtd->params;
-
-	/* Disable the PDC and save the PDC registers */
-	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-		   params->mask->pdc_disable);
-
-	prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
-	prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
-	prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
-	prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
-
-	return 0;
-}
-
-
-
-static int at32_pcm_resume(struct platform_device *pdev,
-			   struct snd_soc_dai *dai)
-{
-	struct snd_pcm_runtime *runtime = dai->runtime;
-	struct at32_runtime_data *prtd;
-	struct at32_pcm_dma_params *params;
-
-	if (runtime == NULL)
-		return 0;
-	prtd = runtime->private_data;
-	params = prtd->params;
-
-	/* Restore the PDC registers and enable the PDC */
-	ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
-	ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
-	ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
-	ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
-
-	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable);
-	return 0;
-}
-#else /* CONFIG_PM */
-#  define at32_pcm_suspend	NULL
-#  define at32_pcm_resume	NULL
-#endif /* CONFIG_PM */
-
-
-
-struct snd_soc_platform at32_soc_platform = {
-	.name = "at32-audio",
-	.pcm_ops = &at32_pcm_ops,
-	.pcm_new = at32_pcm_new,
-	.pcm_free = at32_pcm_free_dma_buffers,
-	.suspend = at32_pcm_suspend,
-	.resume = at32_pcm_resume,
-};
-EXPORT_SYMBOL_GPL(at32_soc_platform);
-
-
-
-MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
-MODULE_DESCRIPTION("Atmel AT32 PCM module");
-MODULE_LICENSE("GPL");

+ 0 - 79
sound/soc/at32/at32-pcm.h

@@ -1,79 +0,0 @@
-/* sound/soc/at32/at32-pcm.h
- * ASoC PCM interface for Atmel AT32 SoC
- *
- * Copyright (C) 2008 Long Range Systems
- *    Geoffrey Wossum <gwossum@acm.org>
- *
- * 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 __SOUND_SOC_AT32_AT32_PCM_H
-#define __SOUND_SOC_AT32_AT32_PCM_H __FILE__
-
-#include <linux/atmel-ssc.h>
-
-
-/*
- * Registers and status bits that are required by the PCM driver
- * TODO: Is ptcr really used?
- */
-struct at32_pdc_regs {
-	u32 xpr;		/* PDC RX/TX pointer */
-	u32 xcr;		/* PDC RX/TX counter */
-	u32 xnpr;		/* PDC next RX/TX pointer */
-	u32 xncr;		/* PDC next RX/TX counter */
-	u32 ptcr;		/* PDC transfer control */
-};
-
-
-
-/*
- * SSC mask info
- */
-struct at32_ssc_mask {
-	u32 ssc_enable;		/* SSC RX/TX enable */
-	u32 ssc_disable;	/* SSC RX/TX disable */
-	u32 ssc_endx;		/* SSC ENDTX or ENDRX */
-	u32 ssc_endbuf;		/* SSC TXBUFF or RXBUFF */
-	u32 pdc_enable;		/* PDC RX/TX enable */
-	u32 pdc_disable;	/* PDC RX/TX disable */
-};
-
-
-
-/*
- * This structure, shared between the PCM driver and the interface,
- * contains all information required by the PCM driver to perform the
- * PDC DMA operation.  All fields except dma_intr_handler() are initialized
- * by the interface.  The dms_intr_handler() pointer is set by the PCM
- * driver and called by the interface SSC interrupt handler if it is
- * non-NULL.
- */
-struct at32_pcm_dma_params {
-	char *name;		/* stream identifier */
-	int pdc_xfer_size;	/* PDC counter increment in bytes */
-	struct ssc_device *ssc;	/* SSC device for stream */
-	struct at32_pdc_regs *pdc;	/* PDC register info */
-	struct at32_ssc_mask *mask;	/* SSC mask info */
-	struct snd_pcm_substream *substream;
-	void (*dma_intr_handler) (u32, struct snd_pcm_substream *);
-};
-
-
-
-/*
- * The AT32 ASoC platform driver
- */
-extern struct snd_soc_platform at32_soc_platform;
-
-
-
-/*
- * SSC register access (since ssc_writel() / ssc_readl() require literal name)
- */
-#define ssc_readx(base, reg)            (__raw_readl((base) + (reg)))
-#define ssc_writex(base, reg, value)    __raw_writel((value), (base) + (reg))
-
-#endif /* __SOUND_SOC_AT32_AT32_PCM_H */

+ 0 - 849
sound/soc/at32/at32-ssc.c

@@ -1,849 +0,0 @@
-/* sound/soc/at32/at32-ssc.c
- * ASoC platform driver for AT32 using SSC as DAI
- *
- * Copyright (C) 2008 Long Range Systems
- *    Geoffrey Wossum <gwossum@acm.org>
- *
- * 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.
- *
- * Note that this is basically a port of the sound/soc/at91-ssc.c to
- * the AVR32 kernel.  Thanks to Frank Mandarino for that code.
- */
-
-/* #define DEBUG */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/atmel_pdc.h>
-#include <linux/atmel-ssc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include "at32-pcm.h"
-#include "at32-ssc.h"
-
-
-
-/*-------------------------------------------------------------------------*\
- * Constants
-\*-------------------------------------------------------------------------*/
-#define NUM_SSC_DEVICES		3
-
-/*
- * SSC direction masks
- */
-#define SSC_DIR_MASK_UNUSED	0
-#define SSC_DIR_MASK_PLAYBACK	1
-#define SSC_DIR_MASK_CAPTURE	2
-
-/*
- * SSC register values that Atmel left out of <linux/atmel-ssc.h>.  These
- * are expected to be used with SSC_BF
- */
-/* START bit field values */
-#define SSC_START_CONTINUOUS	0
-#define SSC_START_TX_RX		1
-#define SSC_START_LOW_RF	2
-#define SSC_START_HIGH_RF	3
-#define SSC_START_FALLING_RF	4
-#define SSC_START_RISING_RF	5
-#define SSC_START_LEVEL_RF	6
-#define SSC_START_EDGE_RF	7
-#define SSS_START_COMPARE_0	8
-
-/* CKI bit field values */
-#define SSC_CKI_FALLING		0
-#define SSC_CKI_RISING		1
-
-/* CKO bit field values */
-#define SSC_CKO_NONE		0
-#define SSC_CKO_CONTINUOUS	1
-#define SSC_CKO_TRANSFER	2
-
-/* CKS bit field values */
-#define SSC_CKS_DIV		0
-#define SSC_CKS_CLOCK		1
-#define SSC_CKS_PIN		2
-
-/* FSEDGE bit field values */
-#define SSC_FSEDGE_POSITIVE	0
-#define SSC_FSEDGE_NEGATIVE	1
-
-/* FSOS bit field values */
-#define SSC_FSOS_NONE		0
-#define SSC_FSOS_NEGATIVE	1
-#define SSC_FSOS_POSITIVE	2
-#define SSC_FSOS_LOW		3
-#define SSC_FSOS_HIGH		4
-#define SSC_FSOS_TOGGLE		5
-
-#define START_DELAY		1
-
-
-
-/*-------------------------------------------------------------------------*\
- * Module data
-\*-------------------------------------------------------------------------*/
-/*
- * SSC PDC registered required by the PCM DMA engine
- */
-static struct at32_pdc_regs pdc_tx_reg = {
-	.xpr = SSC_PDC_TPR,
-	.xcr = SSC_PDC_TCR,
-	.xnpr = SSC_PDC_TNPR,
-	.xncr = SSC_PDC_TNCR,
-};
-
-
-
-static struct at32_pdc_regs pdc_rx_reg = {
-	.xpr = SSC_PDC_RPR,
-	.xcr = SSC_PDC_RCR,
-	.xnpr = SSC_PDC_RNPR,
-	.xncr = SSC_PDC_RNCR,
-};
-
-
-
-/*
- * SSC and PDC status bits for transmit and receive
- */
-static struct at32_ssc_mask ssc_tx_mask = {
-	.ssc_enable = SSC_BIT(CR_TXEN),
-	.ssc_disable = SSC_BIT(CR_TXDIS),
-	.ssc_endx = SSC_BIT(SR_ENDTX),
-	.ssc_endbuf = SSC_BIT(SR_TXBUFE),
-	.pdc_enable = SSC_BIT(PDC_PTCR_TXTEN),
-	.pdc_disable = SSC_BIT(PDC_PTCR_TXTDIS),
-};
-
-
-
-static struct at32_ssc_mask ssc_rx_mask = {
-	.ssc_enable = SSC_BIT(CR_RXEN),
-	.ssc_disable = SSC_BIT(CR_RXDIS),
-	.ssc_endx = SSC_BIT(SR_ENDRX),
-	.ssc_endbuf = SSC_BIT(SR_RXBUFF),
-	.pdc_enable = SSC_BIT(PDC_PTCR_RXTEN),
-	.pdc_disable = SSC_BIT(PDC_PTCR_RXTDIS),
-};
-
-
-
-/*
- * DMA parameters for each SSC
- */
-static struct at32_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
-	{
-	 {
-	  .name = "SSC0 PCM out",
-	  .pdc = &pdc_tx_reg,
-	  .mask = &ssc_tx_mask,
-	  },
-	 {
-	  .name = "SSC0 PCM in",
-	  .pdc = &pdc_rx_reg,
-	  .mask = &ssc_rx_mask,
-	  },
-	 },
-	{
-	 {
-	  .name = "SSC1 PCM out",
-	  .pdc = &pdc_tx_reg,
-	  .mask = &ssc_tx_mask,
-	  },
-	 {
-	  .name = "SSC1 PCM in",
-	  .pdc = &pdc_rx_reg,
-	  .mask = &ssc_rx_mask,
-	  },
-	 },
-	{
-	 {
-	  .name = "SSC2 PCM out",
-	  .pdc = &pdc_tx_reg,
-	  .mask = &ssc_tx_mask,
-	  },
-	 {
-	  .name = "SSC2 PCM in",
-	  .pdc = &pdc_rx_reg,
-	  .mask = &ssc_rx_mask,
-	  },
-	 },
-};
-
-
-
-static struct at32_ssc_info ssc_info[NUM_SSC_DEVICES] = {
-	{
-	 .name = "ssc0",
-	 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
-	 .dir_mask = SSC_DIR_MASK_UNUSED,
-	 .initialized = 0,
-	 },
-	{
-	 .name = "ssc1",
-	 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
-	 .dir_mask = SSC_DIR_MASK_UNUSED,
-	 .initialized = 0,
-	 },
-	{
-	 .name = "ssc2",
-	 .lock = __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
-	 .dir_mask = SSC_DIR_MASK_UNUSED,
-	 .initialized = 0,
-	 },
-};
-
-
-
-
-/*-------------------------------------------------------------------------*\
- * ISR
-\*-------------------------------------------------------------------------*/
-/*
- * SSC interrupt handler.  Passes PDC interrupts to the DMA interrupt
- * handler in the PCM driver.
- */
-static irqreturn_t at32_ssc_interrupt(int irq, void *dev_id)
-{
-	struct at32_ssc_info *ssc_p = dev_id;
-	struct at32_pcm_dma_params *dma_params;
-	u32 ssc_sr;
-	u32 ssc_substream_mask;
-	int i;
-
-	ssc_sr = (ssc_readl(ssc_p->ssc->regs, SR) &
-		  ssc_readl(ssc_p->ssc->regs, IMR));
-
-	/*
-	 * Loop through substreams attached to this SSC.  If a DMA-related
-	 * interrupt occured on that substream, call the DMA interrupt
-	 * handler function, if one has been registered in the dma_param
-	 * structure by the PCM driver.
-	 */
-	for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
-		dma_params = ssc_p->dma_params[i];
-
-		if ((dma_params != NULL) &&
-		    (dma_params->dma_intr_handler != NULL)) {
-			ssc_substream_mask = (dma_params->mask->ssc_endx |
-					      dma_params->mask->ssc_endbuf);
-			if (ssc_sr & ssc_substream_mask) {
-				dma_params->dma_intr_handler(ssc_sr,
-							     dma_params->
-							     substream);
-			}
-		}
-	}
-
-
-	return IRQ_HANDLED;
-}
-
-/*-------------------------------------------------------------------------*\
- * DAI functions
-\*-------------------------------------------------------------------------*/
-/*
- * Startup.  Only that one substream allowed in each direction.
- */
-static int at32_ssc_startup(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-	int dir_mask;
-
-	dir_mask = ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-		    SSC_DIR_MASK_PLAYBACK : SSC_DIR_MASK_CAPTURE);
-
-	spin_lock_irq(&ssc_p->lock);
-	if (ssc_p->dir_mask & dir_mask) {
-		spin_unlock_irq(&ssc_p->lock);
-		return -EBUSY;
-	}
-	ssc_p->dir_mask |= dir_mask;
-	spin_unlock_irq(&ssc_p->lock);
-
-	return 0;
-}
-
-
-
-/*
- * Shutdown.  Clear DMA parameters and shutdown the SSC if there
- * are no other substreams open.
- */
-static void at32_ssc_shutdown(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-	struct at32_pcm_dma_params *dma_params;
-	int dir_mask;
-
-	dma_params = ssc_p->dma_params[substream->stream];
-
-	if (dma_params != NULL) {
-		ssc_writel(dma_params->ssc->regs, CR,
-			   dma_params->mask->ssc_disable);
-		pr_debug("%s disabled SSC_SR=0x%08x\n",
-			 (substream->stream ? "receiver" : "transmit"),
-			 ssc_readl(ssc_p->ssc->regs, SR));
-
-		dma_params->ssc = NULL;
-		dma_params->substream = NULL;
-		ssc_p->dma_params[substream->stream] = NULL;
-	}
-
-
-	dir_mask = 1 << substream->stream;
-	spin_lock_irq(&ssc_p->lock);
-	ssc_p->dir_mask &= ~dir_mask;
-	if (!ssc_p->dir_mask) {
-		/* Shutdown the SSC clock */
-		pr_debug("at32-ssc: Stopping user %d clock\n",
-			 ssc_p->ssc->user);
-		clk_disable(ssc_p->ssc->clk);
-
-		if (ssc_p->initialized) {
-			free_irq(ssc_p->ssc->irq, ssc_p);
-			ssc_p->initialized = 0;
-		}
-
-		/* Reset the SSC */
-		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
-
-		/* clear the SSC dividers */
-		ssc_p->cmr_div = 0;
-		ssc_p->tcmr_period = 0;
-		ssc_p->rcmr_period = 0;
-	}
-	spin_unlock_irq(&ssc_p->lock);
-}
-
-
-
-/*
- * Set the SSC system clock rate
- */
-static int at32_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
-				   int clk_id, unsigned int freq, int dir)
-{
-	/* TODO: What the heck do I do here? */
-	return 0;
-}
-
-
-
-/*
- * Record DAI format for use by hw_params()
- */
-static int at32_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
-				unsigned int fmt)
-{
-	struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
-	ssc_p->daifmt = fmt;
-	return 0;
-}
-
-
-
-/*
- * Record SSC clock dividers for use in hw_params()
- */
-static int at32_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
-				   int div_id, int div)
-{
-	struct at32_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
-	switch (div_id) {
-	case AT32_SSC_CMR_DIV:
-		/*
-		 * The same master clock divider is used for both
-		 * transmit and receive, so if a value has already
-		 * been set, it must match this value
-		 */
-		if (ssc_p->cmr_div == 0)
-			ssc_p->cmr_div = div;
-		else if (div != ssc_p->cmr_div)
-			return -EBUSY;
-		break;
-
-	case AT32_SSC_TCMR_PERIOD:
-		ssc_p->tcmr_period = div;
-		break;
-
-	case AT32_SSC_RCMR_PERIOD:
-		ssc_p->rcmr_period = div;
-		break;
-
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-
-
-/*
- * Configure the SSC
- */
-static int at32_ssc_hw_params(struct snd_pcm_substream *substream,
-			      struct snd_pcm_hw_params *params)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	int id = rtd->dai->cpu_dai->id;
-	struct at32_ssc_info *ssc_p = &ssc_info[id];
-	struct at32_pcm_dma_params *dma_params;
-	int channels, bits;
-	u32 tfmr, rfmr, tcmr, rcmr;
-	int start_event;
-	int ret;
-
-
-	/*
-	 * Currently, there is only one set of dma_params for each direction.
-	 * If more are added, this code will have to be changed to select
-	 * the proper set
-	 */
-	dma_params = &ssc_dma_params[id][substream->stream];
-	dma_params->ssc = ssc_p->ssc;
-	dma_params->substream = substream;
-
-	ssc_p->dma_params[substream->stream] = dma_params;
-
-
-	/*
-	 * The cpu_dai->dma_data field is only used to communicate the
-	 * appropriate DMA parameters to the PCM driver's hw_params()
-	 * function.  It should not be used for other purposes as it
-	 * is common to all substreams.
-	 */
-	rtd->dai->cpu_dai->dma_data = dma_params;
-
-	channels = params_channels(params);
-
-
-	/*
-	 * Determine sample size in bits and the PDC increment
-	 */
-	switch (params_format(params)) {
-	case SNDRV_PCM_FORMAT_S8:
-		bits = 8;
-		dma_params->pdc_xfer_size = 1;
-		break;
-
-	case SNDRV_PCM_FORMAT_S16:
-		bits = 16;
-		dma_params->pdc_xfer_size = 2;
-		break;
-
-	case SNDRV_PCM_FORMAT_S24:
-		bits = 24;
-		dma_params->pdc_xfer_size = 4;
-		break;
-
-	case SNDRV_PCM_FORMAT_S32:
-		bits = 32;
-		dma_params->pdc_xfer_size = 4;
-		break;
-
-	default:
-		pr_warning("at32-ssc: Unsupported PCM format %d",
-			   params_format(params));
-		return -EINVAL;
-	}
-	pr_debug("at32-ssc: bits = %d, pdc_xfer_size = %d, channels = %d\n",
-		 bits, dma_params->pdc_xfer_size, channels);
-
-
-	/*
-	 * The SSC only supports up to 16-bit samples in I2S format, due
-	 * to the size of the Frame Mode Register FSLEN field.
-	 */
-	if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
-		if (bits > 16) {
-			pr_warning("at32-ssc: "
-				   "sample size %d is too large for I2S\n",
-				   bits);
-			return -EINVAL;
-		}
-
-
-	/*
-	 * Compute the SSC register settings
-	 */
-	switch (ssc_p->daifmt & (SND_SOC_DAIFMT_FORMAT_MASK |
-				 SND_SOC_DAIFMT_MASTER_MASK)) {
-	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
-		/*
-		 * I2S format, SSC provides BCLK and LRS clocks.
-		 *
-		 * The SSC transmit and receive clocks are generated from the
-		 * MCK divider, and the BCLK signal is output on the SSC TK line
-		 */
-		pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME master\n");
-		rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
-			SSC_BF(RCMR_STTDLY, START_DELAY) |
-			SSC_BF(RCMR_START, SSC_START_FALLING_RF) |
-			SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
-			SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
-			SSC_BF(RCMR_CKS, SSC_CKS_DIV));
-
-		rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-			SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) |
-			SSC_BF(RFMR_FSLEN, bits - 1) |
-			SSC_BF(RFMR_DATNB, channels - 1) |
-			SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
-
-		tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
-			SSC_BF(TCMR_STTDLY, START_DELAY) |
-			SSC_BF(TCMR_START, SSC_START_FALLING_RF) |
-			SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
-			SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
-			SSC_BF(TCMR_CKS, SSC_CKS_DIV));
-
-		tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-			SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) |
-			SSC_BF(TFMR_FSLEN, bits - 1) |
-			SSC_BF(TFMR_DATNB, channels - 1) | SSC_BIT(TFMR_MSBF) |
-			SSC_BF(TFMR_DATLEN, bits - 1));
-		break;
-
-
-	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
-		/*
-		 * I2S format, CODEC supplies BCLK and LRC clock.
-		 *
-		 * The SSC transmit clock is obtained from the BCLK signal
-		 * on the TK line, and the SSC receive clock is generated from
-		 * the transmit clock.
-		 *
-		 * For single channel data, one sample is transferred on the
-		 * falling edge of the LRC clock.  For two channel data, one
-		 * sample is transferred on both edges of the LRC clock.
-		 */
-		pr_debug("at32-ssc: SSC mode is I2S BCLK / FRAME slave\n");
-		start_event = ((channels == 1) ?
-			       SSC_START_FALLING_RF : SSC_START_EDGE_RF);
-
-		rcmr = (SSC_BF(RCMR_STTDLY, START_DELAY) |
-			SSC_BF(RCMR_START, start_event) |
-			SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
-			SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
-			SSC_BF(RCMR_CKS, SSC_CKS_CLOCK));
-
-		rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-			SSC_BF(RFMR_FSOS, SSC_FSOS_NONE) |
-			SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
-
-		tcmr = (SSC_BF(TCMR_STTDLY, START_DELAY) |
-			SSC_BF(TCMR_START, start_event) |
-			SSC_BF(TCMR_CKI, SSC_CKI_FALLING) |
-			SSC_BF(TCMR_CKO, SSC_CKO_NONE) |
-			SSC_BF(TCMR_CKS, SSC_CKS_PIN));
-
-		tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-			SSC_BF(TFMR_FSOS, SSC_FSOS_NONE) |
-			SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
-		break;
-
-
-	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
-		/*
-		 * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
-		 *
-		 * The SSC transmit and receive clocks are generated from the
-		 * MCK divider, and the BCLK signal is output on the SSC TK line
-		 */
-		pr_debug("at32-ssc: SSC mode is DSP A BCLK / FRAME master\n");
-		rcmr = (SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period) |
-			SSC_BF(RCMR_STTDLY, 1) |
-			SSC_BF(RCMR_START, SSC_START_RISING_RF) |
-			SSC_BF(RCMR_CKI, SSC_CKI_RISING) |
-			SSC_BF(RCMR_CKO, SSC_CKO_NONE) |
-			SSC_BF(RCMR_CKS, SSC_CKS_DIV));
-
-		rfmr = (SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-			SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE) |
-			SSC_BF(RFMR_DATNB, channels - 1) |
-			SSC_BIT(RFMR_MSBF) | SSC_BF(RFMR_DATLEN, bits - 1));
-
-		tcmr = (SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period) |
-			SSC_BF(TCMR_STTDLY, 1) |
-			SSC_BF(TCMR_START, SSC_START_RISING_RF) |
-			SSC_BF(TCMR_CKI, SSC_CKI_RISING) |
-			SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS) |
-			SSC_BF(TCMR_CKS, SSC_CKS_DIV));
-
-		tfmr = (SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE) |
-			SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE) |
-			SSC_BF(TFMR_DATNB, channels - 1) |
-			SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATLEN, bits - 1));
-		break;
-
-
-	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
-	default:
-		pr_warning("at32-ssc: unsupported DAI format 0x%x\n",
-			   ssc_p->daifmt);
-		return -EINVAL;
-		break;
-	}
-	pr_debug("at32-ssc: RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
-		 rcmr, rfmr, tcmr, tfmr);
-
-
-	if (!ssc_p->initialized) {
-		/* enable peripheral clock */
-		pr_debug("at32-ssc: Starting clock\n");
-		clk_enable(ssc_p->ssc->clk);
-
-		/* Reset the SSC and its PDC registers */
-		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
-
-		ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
-		ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
-		ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
-		ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
-
-		ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
-		ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
-		ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
-		ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
-
-		ret = request_irq(ssc_p->ssc->irq, at32_ssc_interrupt, 0,
-				  ssc_p->name, ssc_p);
-		if (ret < 0) {
-			pr_warning("at32-ssc: request irq failed (%d)\n", ret);
-			pr_debug("at32-ssc: Stopping clock\n");
-			clk_disable(ssc_p->ssc->clk);
-			return ret;
-		}
-
-		ssc_p->initialized = 1;
-	}
-
-	/* Set SSC clock mode register */
-	ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
-
-	/* set receive clock mode and format */
-	ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
-	ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
-
-	/* set transmit clock mode and format */
-	ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
-	ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
-
-	pr_debug("at32-ssc: SSC initialized\n");
-	return 0;
-}
-
-
-
-static int at32_ssc_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct at32_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-	struct at32_pcm_dma_params *dma_params;
-
-	dma_params = ssc_p->dma_params[substream->stream];
-
-	ssc_writel(dma_params->ssc->regs, CR, dma_params->mask->ssc_enable);
-
-	return 0;
-}
-
-
-
-#ifdef CONFIG_PM
-static int at32_ssc_suspend(struct platform_device *pdev,
-			    struct snd_soc_dai *cpu_dai)
-{
-	struct at32_ssc_info *ssc_p;
-
-	if (!cpu_dai->active)
-		return 0;
-
-	ssc_p = &ssc_info[cpu_dai->id];
-
-	/* Save the status register before disabling transmit and receive */
-	ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
-	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
-
-	/* Save the current interrupt mask, then disable unmasked interrupts */
-	ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
-	ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
-
-	ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
-	ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
-	ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
-	ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
-	ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
-
-	return 0;
-}
-
-
-
-static int at32_ssc_resume(struct platform_device *pdev,
-			   struct snd_soc_dai *cpu_dai)
-{
-	struct at32_ssc_info *ssc_p;
-	u32 cr;
-
-	if (!cpu_dai->active)
-		return 0;
-
-	ssc_p = &ssc_info[cpu_dai->id];
-
-	/* restore SSC register settings */
-	ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
-	ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
-	ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
-	ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
-	ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
-
-	/* re-enable interrupts */
-	ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
-
-	/* Re-enable recieve and transmit as appropriate */
-	cr = 0;
-	cr |=
-	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
-	cr |=
-	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
-	ssc_writel(ssc_p->ssc->regs, CR, cr);
-
-	return 0;
-}
-#else /* CONFIG_PM */
-#  define at32_ssc_suspend	NULL
-#  define at32_ssc_resume	NULL
-#endif /* CONFIG_PM */
-
-
-#define AT32_SSC_RATES \
-    (SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-     SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-     SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-
-
-#define AT32_SSC_FORMATS \
-    (SNDRV_PCM_FMTBIT_S8  | SNDRV_PCM_FMTBIT_S16 | \
-     SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32)
-
-
-struct snd_soc_dai at32_ssc_dai[NUM_SSC_DEVICES] = {
-	{
-	 .name = "at32-ssc0",
-	 .id = 0,
-	 .type = SND_SOC_DAI_PCM,
-	 .suspend = at32_ssc_suspend,
-	 .resume = at32_ssc_resume,
-	 .playback = {
-		      .channels_min = 1,
-		      .channels_max = 2,
-		      .rates = AT32_SSC_RATES,
-		      .formats = AT32_SSC_FORMATS,
-		      },
-	 .capture = {
-		     .channels_min = 1,
-		     .channels_max = 2,
-		     .rates = AT32_SSC_RATES,
-		     .formats = AT32_SSC_FORMATS,
-		     },
-	 .ops = {
-		 .startup = at32_ssc_startup,
-		 .shutdown = at32_ssc_shutdown,
-		 .prepare = at32_ssc_prepare,
-		 .hw_params = at32_ssc_hw_params,
-		 },
-	 .dai_ops = {
-		     .set_sysclk = at32_ssc_set_dai_sysclk,
-		     .set_fmt = at32_ssc_set_dai_fmt,
-		     .set_clkdiv = at32_ssc_set_dai_clkdiv,
-		     },
-	 .private_data = &ssc_info[0],
-	 },
-	{
-	 .name = "at32-ssc1",
-	 .id = 1,
-	 .type = SND_SOC_DAI_PCM,
-	 .suspend = at32_ssc_suspend,
-	 .resume = at32_ssc_resume,
-	 .playback = {
-		      .channels_min = 1,
-		      .channels_max = 2,
-		      .rates = AT32_SSC_RATES,
-		      .formats = AT32_SSC_FORMATS,
-		      },
-	 .capture = {
-		     .channels_min = 1,
-		     .channels_max = 2,
-		     .rates = AT32_SSC_RATES,
-		     .formats = AT32_SSC_FORMATS,
-		     },
-	 .ops = {
-		 .startup = at32_ssc_startup,
-		 .shutdown = at32_ssc_shutdown,
-		 .prepare = at32_ssc_prepare,
-		 .hw_params = at32_ssc_hw_params,
-		 },
-	 .dai_ops = {
-		     .set_sysclk = at32_ssc_set_dai_sysclk,
-		     .set_fmt = at32_ssc_set_dai_fmt,
-		     .set_clkdiv = at32_ssc_set_dai_clkdiv,
-		     },
-	 .private_data = &ssc_info[1],
-	 },
-	{
-	 .name = "at32-ssc2",
-	 .id = 2,
-	 .type = SND_SOC_DAI_PCM,
-	 .suspend = at32_ssc_suspend,
-	 .resume = at32_ssc_resume,
-	 .playback = {
-		      .channels_min = 1,
-		      .channels_max = 2,
-		      .rates = AT32_SSC_RATES,
-		      .formats = AT32_SSC_FORMATS,
-		      },
-	 .capture = {
-		     .channels_min = 1,
-		     .channels_max = 2,
-		     .rates = AT32_SSC_RATES,
-		     .formats = AT32_SSC_FORMATS,
-		     },
-	 .ops = {
-		 .startup = at32_ssc_startup,
-		 .shutdown = at32_ssc_shutdown,
-		 .prepare = at32_ssc_prepare,
-		 .hw_params = at32_ssc_hw_params,
-		 },
-	 .dai_ops = {
-		     .set_sysclk = at32_ssc_set_dai_sysclk,
-		     .set_fmt = at32_ssc_set_dai_fmt,
-		     .set_clkdiv = at32_ssc_set_dai_clkdiv,
-		     },
-	 .private_data = &ssc_info[2],
-	 },
-};
-EXPORT_SYMBOL_GPL(at32_ssc_dai);
-
-
-MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
-MODULE_DESCRIPTION("AT32 SSC ASoC Interface");
-MODULE_LICENSE("GPL");

+ 0 - 59
sound/soc/at32/at32-ssc.h

@@ -1,59 +0,0 @@
-/* sound/soc/at32/at32-ssc.h
- * ASoC SSC interface for Atmel AT32 SoC
- *
- * Copyright (C) 2008 Long Range Systems
- *    Geoffrey Wossum <gwossum@acm.org>
- *
- * 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 __SOUND_SOC_AT32_AT32_SSC_H
-#define __SOUND_SOC_AT32_AT32_SSC_H __FILE__
-
-#include <linux/types.h>
-#include <linux/atmel-ssc.h>
-
-#include "at32-pcm.h"
-
-
-
-struct at32_ssc_state {
-	u32 ssc_cmr;
-	u32 ssc_rcmr;
-	u32 ssc_rfmr;
-	u32 ssc_tcmr;
-	u32 ssc_tfmr;
-	u32 ssc_sr;
-	u32 ssc_imr;
-};
-
-
-
-struct at32_ssc_info {
-	char *name;
-	struct ssc_device *ssc;
-	spinlock_t lock;	/* lock for dir_mask */
-	unsigned short dir_mask;	/* 0=unused, 1=playback, 2=capture */
-	unsigned short initialized;	/* true if SSC has been initialized */
-	unsigned short daifmt;
-	unsigned short cmr_div;
-	unsigned short tcmr_period;
-	unsigned short rcmr_period;
-	struct at32_pcm_dma_params *dma_params[2];
-	struct at32_ssc_state ssc_state;
-};
-
-
-/* SSC divider ids */
-#define AT32_SSC_CMR_DIV        0	/* MCK divider for BCLK */
-#define AT32_SSC_TCMR_PERIOD    1	/* BCLK divider for transmit FS */
-#define AT32_SSC_RCMR_PERIOD    2	/* BCLK divider for receive FS */
-
-
-extern struct snd_soc_dai at32_ssc_dai[];
-
-
-
-#endif /* __SOUND_SOC_AT32_AT32_SSC_H */

+ 0 - 10
sound/soc/at91/Kconfig

@@ -1,10 +0,0 @@
-config SND_AT91_SOC
-	tristate "SoC Audio for the Atmel AT91 System-on-Chip"
-	depends on ARCH_AT91
-	help
-	  Say Y or M if you want to add support for codecs attached to
-	  the AT91 SSC interface. You will also need
-	  to select the audio interfaces to support below.
-
-config SND_AT91_SOC_SSC
-	tristate

+ 0 - 6
sound/soc/at91/Makefile

@@ -1,6 +0,0 @@
-# AT91 Platform Support
-snd-soc-at91-objs := at91-pcm.o
-snd-soc-at91-ssc-objs := at91-ssc.o
-
-obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o
-obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o

+ 0 - 434
sound/soc/at91/at91-pcm.c

@@ -1,434 +0,0 @@
-/*
- * at91-pcm.c -- ALSA PCM interface for the Atmel AT91 SoC
- *
- * Author:	Frank Mandarino <fmandarino@endrelia.com>
- *		Endrelia Technologies Inc.
- * Created:	Mar 3, 2006
- *
- * Based on pxa2xx-pcm.c by:
- *
- * Author:	Nicolas Pitre
- * Created:	Nov 30, 2004
- * Copyright:	(C) 2004 MontaVista Software, Inc.
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/atmel_pdc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <mach/hardware.h>
-#include <mach/at91_ssc.h>
-
-#include "at91-pcm.h"
-
-#if 0
-#define	DBG(x...)	printk(KERN_INFO "at91-pcm: " x)
-#else
-#define	DBG(x...)
-#endif
-
-static const struct snd_pcm_hardware at91_pcm_hardware = {
-	.info			= SNDRV_PCM_INFO_MMAP |
-				  SNDRV_PCM_INFO_MMAP_VALID |
-				  SNDRV_PCM_INFO_INTERLEAVED |
-				  SNDRV_PCM_INFO_PAUSE,
-	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
-	.period_bytes_min	= 32,
-	.period_bytes_max	= 8192,
-	.periods_min		= 2,
-	.periods_max		= 1024,
-	.buffer_bytes_max	= 32 * 1024,
-};
-
-struct at91_runtime_data {
-	struct at91_pcm_dma_params *params;
-	dma_addr_t dma_buffer;			/* physical address of dma buffer */
-	dma_addr_t dma_buffer_end;		/* first address beyond DMA buffer */
-	size_t period_size;
-	dma_addr_t period_ptr;			/* physical address of next period */
-	u32 pdc_xpr_save;			/* PDC register save */
-	u32 pdc_xcr_save;
-	u32 pdc_xnpr_save;
-	u32 pdc_xncr_save;
-};
-
-static void at91_pcm_dma_irq(u32 ssc_sr,
-	struct snd_pcm_substream *substream)
-{
-	struct at91_runtime_data *prtd = substream->runtime->private_data;
-	struct at91_pcm_dma_params *params = prtd->params;
-	static int count = 0;
-
-	count++;
-
-	if (ssc_sr & params->mask->ssc_endbuf) {
-
-		printk(KERN_WARNING
-			"at91-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
-			substream->stream == SNDRV_PCM_STREAM_PLAYBACK
-				? "underrun" : "overrun",
-			params->name, ssc_sr, count);
-
-		/* re-start the PDC */
-		at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-
-		prtd->period_ptr += prtd->period_size;
-		if (prtd->period_ptr >= prtd->dma_buffer_end) {
-			prtd->period_ptr = prtd->dma_buffer;
-		}
-
-		at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
-		at91_ssc_write(params->ssc_base + params->pdc->xcr,
-				prtd->period_size / params->pdc_xfer_size);
-
-		at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
-	}
-
-	if (ssc_sr & params->mask->ssc_endx) {
-
-		/* Load the PDC next pointer and counter registers */
-		prtd->period_ptr += prtd->period_size;
-		if (prtd->period_ptr >= prtd->dma_buffer_end) {
-			prtd->period_ptr = prtd->dma_buffer;
-		}
-		at91_ssc_write(params->ssc_base + params->pdc->xnpr,
-			       prtd->period_ptr);
-		at91_ssc_write(params->ssc_base + params->pdc->xncr,
-				prtd->period_size / params->pdc_xfer_size);
-	}
-
-	snd_pcm_period_elapsed(substream);
-}
-
-static int at91_pcm_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct at91_runtime_data *prtd = runtime->private_data;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
-	/* this may get called several times by oss emulation
-	 * with different params */
-
-	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-	runtime->dma_bytes = params_buffer_bytes(params);
-
-	prtd->params = rtd->dai->cpu_dai->dma_data;
-	prtd->params->dma_intr_handler = at91_pcm_dma_irq;
-
-	prtd->dma_buffer = runtime->dma_addr;
-	prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
-	prtd->period_size = params_period_bytes(params);
-
-	DBG("hw_params: DMA for %s initialized (dma_bytes=%d, period_size=%d)\n",
-		prtd->params->name, runtime->dma_bytes, prtd->period_size);
-	return 0;
-}
-
-static int at91_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-	struct at91_runtime_data *prtd = substream->runtime->private_data;
-	struct at91_pcm_dma_params *params = prtd->params;
-
-	if (params != NULL) {
-		at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-		prtd->params->dma_intr_handler = NULL;
-	}
-
-	return 0;
-}
-
-static int at91_pcm_prepare(struct snd_pcm_substream *substream)
-{
-	struct at91_runtime_data *prtd = substream->runtime->private_data;
-	struct at91_pcm_dma_params *params = prtd->params;
-
-	at91_ssc_write(params->ssc_base + AT91_SSC_IDR,
-			params->mask->ssc_endx | params->mask->ssc_endbuf);
-
-	at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-	return 0;
-}
-
-static int at91_pcm_trigger(struct snd_pcm_substream *substream,
-	int cmd)
-{
-	struct at91_runtime_data *prtd = substream->runtime->private_data;
-	struct at91_pcm_dma_params *params = prtd->params;
-	int ret = 0;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		prtd->period_ptr = prtd->dma_buffer;
-
-		at91_ssc_write(params->ssc_base + params->pdc->xpr, prtd->period_ptr);
-		at91_ssc_write(params->ssc_base + params->pdc->xcr,
-				prtd->period_size / params->pdc_xfer_size);
-
-		prtd->period_ptr += prtd->period_size;
-		at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->period_ptr);
-		at91_ssc_write(params->ssc_base + params->pdc->xncr,
-				prtd->period_size / params->pdc_xfer_size);
-
-		DBG("trigger: period_ptr=%lx, xpr=%lx, xcr=%ld, xnpr=%lx, xncr=%ld\n",
-			(unsigned long) prtd->period_ptr,
-			at91_ssc_read(params->ssc_base + params->pdc->xpr),
-			at91_ssc_read(params->ssc_base + params->pdc->xcr),
-			at91_ssc_read(params->ssc_base + params->pdc->xnpr),
-			at91_ssc_read(params->ssc_base + params->pdc->xncr));
-
-		at91_ssc_write(params->ssc_base + AT91_SSC_IER,
-			params->mask->ssc_endx | params->mask->ssc_endbuf);
-
-		at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR,
-			params->mask->pdc_enable);
-
-		DBG("sr=%lx imr=%lx\n",
-		    at91_ssc_read(params->ssc_base + AT91_SSC_SR),
-		    at91_ssc_read(params->ssc_base + AT91_SSC_IMR));
-		break;
-
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-		break;
-
-	case SNDRV_PCM_TRIGGER_RESUME:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
-		break;
-
-	default:
-		ret = -EINVAL;
-	}
-
-	return ret;
-}
-
-static snd_pcm_uframes_t at91_pcm_pointer(
-	struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct at91_runtime_data *prtd = runtime->private_data;
-	struct at91_pcm_dma_params *params = prtd->params;
-	dma_addr_t ptr;
-	snd_pcm_uframes_t x;
-
-	ptr = (dma_addr_t) at91_ssc_read(params->ssc_base + params->pdc->xpr);
-	x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
-
-	if (x == runtime->buffer_size)
-		x = 0;
-	return x;
-}
-
-static int at91_pcm_open(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct at91_runtime_data *prtd;
-	int ret = 0;
-
-	snd_soc_set_runtime_hwparams(substream, &at91_pcm_hardware);
-
-	/* ensure that buffer size is a multiple of period size */
-	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		goto out;
-
-	prtd = kzalloc(sizeof(struct at91_runtime_data), GFP_KERNEL);
-	if (prtd == NULL) {
-		ret = -ENOMEM;
-		goto out;
-	}
-	runtime->private_data = prtd;
-
- out:
-	return ret;
-}
-
-static int at91_pcm_close(struct snd_pcm_substream *substream)
-{
-	struct at91_runtime_data *prtd = substream->runtime->private_data;
-
-	kfree(prtd);
-	return 0;
-}
-
-static int at91_pcm_mmap(struct snd_pcm_substream *substream,
-	struct vm_area_struct *vma)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-
-	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
-				     runtime->dma_area,
-				     runtime->dma_addr,
-				     runtime->dma_bytes);
-}
-
-struct snd_pcm_ops at91_pcm_ops = {
-	.open		= at91_pcm_open,
-	.close		= at91_pcm_close,
-	.ioctl		= snd_pcm_lib_ioctl,
-	.hw_params	= at91_pcm_hw_params,
-	.hw_free	= at91_pcm_hw_free,
-	.prepare	= at91_pcm_prepare,
-	.trigger	= at91_pcm_trigger,
-	.pointer	= at91_pcm_pointer,
-	.mmap		= at91_pcm_mmap,
-};
-
-static int at91_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
-	int stream)
-{
-	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
-	struct snd_dma_buffer *buf = &substream->dma_buffer;
-	size_t size = at91_pcm_hardware.buffer_bytes_max;
-
-	buf->dev.type = SNDRV_DMA_TYPE_DEV;
-	buf->dev.dev = pcm->card->dev;
-	buf->private_data = NULL;
-	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
-					   &buf->addr, GFP_KERNEL);
-
-	DBG("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
-		(void *) buf->area,
-		(void *) buf->addr,
-		size);
-
-	if (!buf->area)
-		return -ENOMEM;
-
-	buf->bytes = size;
-	return 0;
-}
-
-static u64 at91_pcm_dmamask = 0xffffffff;
-
-static int at91_pcm_new(struct snd_card *card,
-	struct snd_soc_dai *dai, struct snd_pcm *pcm)
-{
-	int ret = 0;
-
-	if (!card->dev->dma_mask)
-		card->dev->dma_mask = &at91_pcm_dmamask;
-	if (!card->dev->coherent_dma_mask)
-		card->dev->coherent_dma_mask = 0xffffffff;
-
-	if (dai->playback.channels_min) {
-		ret = at91_pcm_preallocate_dma_buffer(pcm,
-			SNDRV_PCM_STREAM_PLAYBACK);
-		if (ret)
-			goto out;
-	}
-
-	if (dai->capture.channels_min) {
-		ret = at91_pcm_preallocate_dma_buffer(pcm,
-			SNDRV_PCM_STREAM_CAPTURE);
-		if (ret)
-			goto out;
-	}
- out:
-	return ret;
-}
-
-static void at91_pcm_free_dma_buffers(struct snd_pcm *pcm)
-{
-	struct snd_pcm_substream *substream;
-	struct snd_dma_buffer *buf;
-	int stream;
-
-	for (stream = 0; stream < 2; stream++) {
-		substream = pcm->streams[stream].substream;
-		if (!substream)
-			continue;
-
-		buf = &substream->dma_buffer;
-		if (!buf->area)
-			continue;
-
-		dma_free_writecombine(pcm->card->dev, buf->bytes,
-				      buf->area, buf->addr);
-		buf->area = NULL;
-	}
-}
-
-#ifdef CONFIG_PM
-static int at91_pcm_suspend(struct platform_device *pdev,
-	struct snd_soc_dai *dai)
-{
-	struct snd_pcm_runtime *runtime = dai->runtime;
-	struct at91_runtime_data *prtd;
-	struct at91_pcm_dma_params *params;
-
-	if (!runtime)
-		return 0;
-
-	prtd = runtime->private_data;
-	params = prtd->params;
-
-	/* disable the PDC and save the PDC registers */
-
-	at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_disable);
-
-	prtd->pdc_xpr_save  = at91_ssc_read(params->ssc_base + params->pdc->xpr);
-	prtd->pdc_xcr_save  = at91_ssc_read(params->ssc_base + params->pdc->xcr);
-	prtd->pdc_xnpr_save = at91_ssc_read(params->ssc_base + params->pdc->xnpr);
-	prtd->pdc_xncr_save = at91_ssc_read(params->ssc_base + params->pdc->xncr);
-
-	return 0;
-}
-
-static int at91_pcm_resume(struct platform_device *pdev,
-	struct snd_soc_dai *dai)
-{
-	struct snd_pcm_runtime *runtime = dai->runtime;
-	struct at91_runtime_data *prtd;
-	struct at91_pcm_dma_params *params;
-
-	if (!runtime)
-		return 0;
-
-	prtd = runtime->private_data;
-	params = prtd->params;
-
-	/* restore the PDC registers and enable the PDC */
-	at91_ssc_write(params->ssc_base + params->pdc->xpr,  prtd->pdc_xpr_save);
-	at91_ssc_write(params->ssc_base + params->pdc->xcr,  prtd->pdc_xcr_save);
-	at91_ssc_write(params->ssc_base + params->pdc->xnpr, prtd->pdc_xnpr_save);
-	at91_ssc_write(params->ssc_base + params->pdc->xncr, prtd->pdc_xncr_save);
-
-	at91_ssc_write(params->ssc_base + ATMEL_PDC_PTCR, params->mask->pdc_enable);
-	return 0;
-}
-#else
-#define at91_pcm_suspend	NULL
-#define at91_pcm_resume		NULL
-#endif
-
-struct snd_soc_platform at91_soc_platform = {
-	.name		= "at91-audio",
-	.pcm_ops 	= &at91_pcm_ops,
-	.pcm_new	= at91_pcm_new,
-	.pcm_free	= at91_pcm_free_dma_buffers,
-	.suspend	= at91_pcm_suspend,
-	.resume		= at91_pcm_resume,
-};
-
-EXPORT_SYMBOL_GPL(at91_soc_platform);
-
-MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>");
-MODULE_DESCRIPTION("Atmel AT91 PCM module");
-MODULE_LICENSE("GPL");

+ 0 - 72
sound/soc/at91/at91-pcm.h

@@ -1,72 +0,0 @@
-/*
- * at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC
- *
- * Author:	Frank Mandarino <fmandarino@endrelia.com>
- *		Endrelia Technologies Inc.
- * Created:	Mar 3, 2006
- *
- * Based on pxa2xx-pcm.h by:
- *
- * Author:	Nicolas Pitre
- * Created:	Nov 30, 2004
- * Copyright:	MontaVista Software, Inc.
- *
- * 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 _AT91_PCM_H
-#define _AT91_PCM_H
-
-#include <mach/hardware.h>
-
-struct at91_ssc_periph {
-	void __iomem	*base;
-	u32		pid;
-};
-
-/*
- * Registers and status bits that are required by the PCM driver.
- */
-struct at91_pdc_regs {
-	unsigned int	xpr;		/* PDC recv/trans pointer */
-	unsigned int	xcr;		/* PDC recv/trans counter */
-	unsigned int	xnpr;		/* PDC next recv/trans pointer */
-	unsigned int	xncr;		/* PDC next recv/trans counter */
-	unsigned int	ptcr;		/* PDC transfer control */
-};
-
-struct at91_ssc_mask {
-	u32	ssc_enable;		/* SSC recv/trans enable */
-	u32	ssc_disable;		/* SSC recv/trans disable */
-	u32	ssc_endx;		/* SSC ENDTX or ENDRX */
-	u32	ssc_endbuf;		/* SSC TXBUFE or RXBUFF */
-	u32	pdc_enable;		/* PDC recv/trans enable */
-	u32	pdc_disable;		/* PDC recv/trans disable */
-};
-
-/*
- * This structure, shared between the PCM driver and the interface,
- * contains all information required by the PCM driver to perform the
- * PDC DMA operation.  All fields except dma_intr_handler() are initialized
- * by the interface.  The dms_intr_handler() pointer is set by the PCM
- * driver and called by the interface SSC interrupt handler if it is
- * non-NULL.
- */
-struct at91_pcm_dma_params {
-	char *name;			/* stream identifier */
-	int pdc_xfer_size;		/* PDC counter increment in bytes */
-	void __iomem *ssc_base;		/* SSC base address */
-	struct at91_pdc_regs *pdc; /* PDC receive or transmit registers */
-	struct at91_ssc_mask *mask;/* SSC & PDC status bits */
-	struct snd_pcm_substream *substream;
-	void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
-};
-
-extern struct snd_soc_platform at91_soc_platform;
-
-#define at91_ssc_read(a)	((unsigned long) __raw_readl(a))
-#define at91_ssc_write(a,v)	__raw_writel((v),(a))
-
-#endif /* _AT91_PCM_H */

+ 0 - 791
sound/soc/at91/at91-ssc.c

@@ -1,791 +0,0 @@
-/*
- * at91-ssc.c  --  ALSA SoC AT91 SSC Audio Layer Platform driver
- *
- * Author: Frank Mandarino <fmandarino@endrelia.com>
- *         Endrelia Technologies Inc.
- *
- * Based on pxa2xx Platform drivers by
- * Liam Girdwood <lrg@slimlogic.co.uk>
- *
- *  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/init.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/atmel_pdc.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include <mach/hardware.h>
-#include <mach/at91_pmc.h>
-#include <mach/at91_ssc.h>
-
-#include "at91-pcm.h"
-#include "at91-ssc.h"
-
-#if 0
-#define	DBG(x...)	printk(KERN_DEBUG "at91-ssc:" x)
-#else
-#define	DBG(x...)
-#endif
-
-#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
-#define NUM_SSC_DEVICES		1
-#else
-#define NUM_SSC_DEVICES		3
-#endif
-
-
-/*
- * SSC PDC registers required by the PCM DMA engine.
- */
-static struct at91_pdc_regs pdc_tx_reg = {
-	.xpr		= ATMEL_PDC_TPR,
-	.xcr		= ATMEL_PDC_TCR,
-	.xnpr		= ATMEL_PDC_TNPR,
-	.xncr		= ATMEL_PDC_TNCR,
-};
-
-static struct at91_pdc_regs pdc_rx_reg = {
-	.xpr		= ATMEL_PDC_RPR,
-	.xcr		= ATMEL_PDC_RCR,
-	.xnpr		= ATMEL_PDC_RNPR,
-	.xncr		= ATMEL_PDC_RNCR,
-};
-
-/*
- * SSC & PDC status bits for transmit and receive.
- */
-static struct at91_ssc_mask ssc_tx_mask = {
-	.ssc_enable	= AT91_SSC_TXEN,
-	.ssc_disable	= AT91_SSC_TXDIS,
-	.ssc_endx	= AT91_SSC_ENDTX,
-	.ssc_endbuf	= AT91_SSC_TXBUFE,
-	.pdc_enable	= ATMEL_PDC_TXTEN,
-	.pdc_disable	= ATMEL_PDC_TXTDIS,
-};
-
-static struct at91_ssc_mask ssc_rx_mask = {
-	.ssc_enable	= AT91_SSC_RXEN,
-	.ssc_disable	= AT91_SSC_RXDIS,
-	.ssc_endx	= AT91_SSC_ENDRX,
-	.ssc_endbuf	= AT91_SSC_RXBUFF,
-	.pdc_enable	= ATMEL_PDC_RXTEN,
-	.pdc_disable	= ATMEL_PDC_RXTDIS,
-};
-
-
-/*
- * DMA parameters.
- */
-static struct at91_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
-	{{
-	.name		= "SSC0 PCM out",
-	.pdc		= &pdc_tx_reg,
-	.mask		= &ssc_tx_mask,
-	},
-	{
-	.name		= "SSC0 PCM in",
-	.pdc		= &pdc_rx_reg,
-	.mask		= &ssc_rx_mask,
-	}},
-#if NUM_SSC_DEVICES == 3
-	{{
-	.name		= "SSC1 PCM out",
-	.pdc		= &pdc_tx_reg,
-	.mask		= &ssc_tx_mask,
-	},
-	{
-	.name		= "SSC1 PCM in",
-	.pdc		= &pdc_rx_reg,
-	.mask		= &ssc_rx_mask,
-	}},
-	{{
-	.name		= "SSC2 PCM out",
-	.pdc		= &pdc_tx_reg,
-	.mask		= &ssc_tx_mask,
-	},
-	{
-	.name		= "SSC2 PCM in",
-	.pdc		= &pdc_rx_reg,
-	.mask		= &ssc_rx_mask,
-	}},
-#endif
-};
-
-struct at91_ssc_state {
-	u32	ssc_cmr;
-	u32	ssc_rcmr;
-	u32	ssc_rfmr;
-	u32	ssc_tcmr;
-	u32	ssc_tfmr;
-	u32	ssc_sr;
-	u32	ssc_imr;
-};
-
-static struct at91_ssc_info {
-	char		*name;
-	struct at91_ssc_periph ssc;
-	spinlock_t 	lock;		/* lock for dir_mask */
-	unsigned short	dir_mask;	/* 0=unused, 1=playback, 2=capture */
-	unsigned short	initialized;	/* 1=SSC has been initialized */
-	unsigned short	daifmt;
-	unsigned short	cmr_div;
-	unsigned short	tcmr_period;
-	unsigned short	rcmr_period;
-	struct at91_pcm_dma_params *dma_params[2];
-	struct at91_ssc_state ssc_state;
-
-} ssc_info[NUM_SSC_DEVICES] = {
-	{
-	.name		= "ssc0",
-	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
-	.dir_mask	= 0,
-	.initialized	= 0,
-	},
-#if NUM_SSC_DEVICES == 3
-	{
-	.name		= "ssc1",
-	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
-	.dir_mask	= 0,
-	.initialized	= 0,
-	},
-	{
-	.name		= "ssc2",
-	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
-	.dir_mask	= 0,
-	.initialized	= 0,
-	},
-#endif
-};
-
-static unsigned int at91_ssc_sysclk;
-
-/*
- * SSC interrupt handler.  Passes PDC interrupts to the DMA
- * interrupt handler in the PCM driver.
- */
-static irqreturn_t at91_ssc_interrupt(int irq, void *dev_id)
-{
-	struct at91_ssc_info *ssc_p = dev_id;
-	struct at91_pcm_dma_params *dma_params;
-	u32 ssc_sr;
-	int i;
-
-	ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR)
-			& at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
-
-	/*
-	 * Loop through the substreams attached to this SSC.  If
-	 * a DMA-related interrupt occurred on that substream, call
-	 * the DMA interrupt handler function, if one has been
-	 * registered in the dma_params structure by the PCM driver.
-	 */
-	for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
-		dma_params = ssc_p->dma_params[i];
-
-		if (dma_params != NULL && dma_params->dma_intr_handler != NULL &&
-			(ssc_sr &
-			(dma_params->mask->ssc_endx | dma_params->mask->ssc_endbuf)))
-
-			dma_params->dma_intr_handler(ssc_sr, dma_params->substream);
-	}
-
-	return IRQ_HANDLED;
-}
-
-/*
- * Startup.  Only that one substream allowed in each direction.
- */
-static int at91_ssc_startup(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-	int dir_mask;
-
-	DBG("ssc_startup: SSC_SR=0x%08lx\n",
-			at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
-	dir_mask = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0x1 : 0x2;
-
-	spin_lock_irq(&ssc_p->lock);
-	if (ssc_p->dir_mask & dir_mask) {
-		spin_unlock_irq(&ssc_p->lock);
-		return -EBUSY;
-	}
-	ssc_p->dir_mask |= dir_mask;
-	spin_unlock_irq(&ssc_p->lock);
-
-	return 0;
-}
-
-/*
- * Shutdown.  Clear DMA parameters and shutdown the SSC if there
- * are no other substreams open.
- */
-static void at91_ssc_shutdown(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-	struct at91_pcm_dma_params *dma_params;
-	int dir, dir_mask;
-
-	dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
-	dma_params = ssc_p->dma_params[dir];
-
-	if (dma_params != NULL) {
-		at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
-				dma_params->mask->ssc_disable);
-		DBG("%s disabled SSC_SR=0x%08lx\n", (dir ? "receive" : "transmit"),
-			at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR));
-
-		dma_params->ssc_base = NULL;
-		dma_params->substream = NULL;
-		ssc_p->dma_params[dir] = NULL;
-	}
-
-	dir_mask = 1 << dir;
-
-	spin_lock_irq(&ssc_p->lock);
-	ssc_p->dir_mask &= ~dir_mask;
-	if (!ssc_p->dir_mask) {
-		/* Shutdown the SSC clock. */
-		DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
-		at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid);
-
-		if (ssc_p->initialized) {
-			free_irq(ssc_p->ssc.pid, ssc_p);
-			ssc_p->initialized = 0;
-		}
-
-		/* Reset the SSC */
-		at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
-
-		/* Clear the SSC dividers */
-		ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
-	}
-	spin_unlock_irq(&ssc_p->lock);
-}
-
-/*
- * Record the SSC system clock rate.
- */
-static int at91_ssc_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
-		int clk_id, unsigned int freq, int dir)
-{
-	/*
-	 * The only clock supplied to the SSC is the AT91 master clock,
-	 * which is only used if the SSC is generating BCLK and/or
-	 * LRC clocks.
-	 */
-	switch (clk_id) {
-	case AT91_SYSCLK_MCK:
-		at91_ssc_sysclk = freq;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-/*
- * Record the DAI format for use in hw_params().
- */
-static int at91_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
-		unsigned int fmt)
-{
-	struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
-	ssc_p->daifmt = fmt;
-	return 0;
-}
-
-/*
- * Record SSC clock dividers for use in hw_params().
- */
-static int at91_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
-	int div_id, int div)
-{
-	struct at91_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
-
-	switch (div_id) {
-	case AT91SSC_CMR_DIV:
-		/*
-		 * The same master clock divider is used for both
-		 * transmit and receive, so if a value has already
-		 * been set, it must match this value.
-		 */
-		if (ssc_p->cmr_div == 0)
-			ssc_p->cmr_div = div;
-		else
-			if (div != ssc_p->cmr_div)
-				return -EBUSY;
-		break;
-
-	case AT91SSC_TCMR_PERIOD:
-		ssc_p->tcmr_period = div;
-		break;
-
-	case AT91SSC_RCMR_PERIOD:
-		ssc_p->rcmr_period = div;
-		break;
-
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-/*
- * Configure the SSC.
- */
-static int at91_ssc_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	int id = rtd->dai->cpu_dai->id;
-	struct at91_ssc_info *ssc_p = &ssc_info[id];
-	struct at91_pcm_dma_params *dma_params;
-	int dir, channels, bits;
-	u32 tfmr, rfmr, tcmr, rcmr;
-	int start_event;
-	int ret;
-
-	/*
-	 * Currently, there is only one set of dma params for
-	 * each direction.  If more are added, this code will
-	 * have to be changed to select the proper set.
-	 */
-	dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
-
-	dma_params = &ssc_dma_params[id][dir];
-	dma_params->ssc_base = ssc_p->ssc.base;
-	dma_params->substream = substream;
-
-	ssc_p->dma_params[dir] = dma_params;
-
-	/*
-	 * The cpu_dai->dma_data field is only used to communicate the
-	 * appropriate DMA parameters to the pcm driver hw_params()
-	 * function.  It should not be used for other purposes
-	 * as it is common to all substreams.
-	 */
-	rtd->dai->cpu_dai->dma_data = dma_params;
-
-	channels = params_channels(params);
-
-	/*
-	 * Determine sample size in bits and the PDC increment.
-	 */
-	switch(params_format(params)) {
-	case SNDRV_PCM_FORMAT_S8:
-		bits = 8;
-		dma_params->pdc_xfer_size = 1;
-		break;
-	case SNDRV_PCM_FORMAT_S16_LE:
-		bits = 16;
-		dma_params->pdc_xfer_size = 2;
-		break;
-	case SNDRV_PCM_FORMAT_S24_LE:
-		bits = 24;
-		dma_params->pdc_xfer_size = 4;
-		break;
-	case SNDRV_PCM_FORMAT_S32_LE:
-		bits = 32;
-		dma_params->pdc_xfer_size = 4;
-		break;
-	default:
-		printk(KERN_WARNING "at91-ssc: unsupported PCM format\n");
-		return -EINVAL;
-	}
-
-	/*
-	 * The SSC only supports up to 16-bit samples in I2S format, due
-	 * to the size of the Frame Mode Register FSLEN field.
-	 */
-	if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
-		&& bits > 16) {
-		printk(KERN_WARNING
-			"at91-ssc: sample size %d is too large for I2S\n", bits);
-		return -EINVAL;
-	}
-
-	/*
-	 * Compute SSC register settings.
-	 */
-	switch (ssc_p->daifmt
-		& (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
-
-	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
-		/*
-		 * I2S format, SSC provides BCLK and LRC clocks.
-		 *
-		 * The SSC transmit and receive clocks are generated from the
-		 * MCK divider, and the BCLK signal is output on the SSC TK line.
-		 */
-		rcmr =	  (( ssc_p->rcmr_period		<< 24) & AT91_SSC_PERIOD)
-			| (( 1				<< 16) & AT91_SSC_STTDLY)
-			| (( AT91_SSC_START_FALLING_RF	     ) & AT91_SSC_START)
-			| (( AT91_SSC_CK_RISING		     ) & AT91_SSC_CKI)
-			| (( AT91_SSC_CKO_NONE		     ) & AT91_SSC_CKO)
-			| (( AT91_SSC_CKS_DIV		     ) & AT91_SSC_CKS);
-
-		rfmr =	  (( AT91_SSC_FSEDGE_POSITIVE	     ) & AT91_SSC_FSEDGE)
-			| (( AT91_SSC_FSOS_NEGATIVE	     ) & AT91_SSC_FSOS)
-			| (((bits - 1)			<< 16) & AT91_SSC_FSLEN)
-			| (((channels - 1)		<<  8) & AT91_SSC_DATNB)
-			| (( 1				<<  7) & AT91_SSC_MSBF)
-			| (( 0				<<  5) & AT91_SSC_LOOP)
-			| (((bits - 1)			<<  0) & AT91_SSC_DATALEN);
-
-		tcmr =	  (( ssc_p->tcmr_period		<< 24) & AT91_SSC_PERIOD)
-			| (( 1				<< 16) & AT91_SSC_STTDLY)
-			| (( AT91_SSC_START_FALLING_RF       ) & AT91_SSC_START)
-			| (( AT91_SSC_CKI_FALLING	     ) & AT91_SSC_CKI)
-			| (( AT91_SSC_CKO_CONTINUOUS	     ) & AT91_SSC_CKO)
-			| (( AT91_SSC_CKS_DIV		     ) & AT91_SSC_CKS);
-
-		tfmr =	  (( AT91_SSC_FSEDGE_POSITIVE	     ) & AT91_SSC_FSEDGE)
-			| (( 0				<< 23) & AT91_SSC_FSDEN)
-			| (( AT91_SSC_FSOS_NEGATIVE	     ) & AT91_SSC_FSOS)
-			| (((bits - 1)			<< 16) & AT91_SSC_FSLEN)
-			| (((channels - 1)		<<  8) & AT91_SSC_DATNB)
-			| (( 1				<<  7) & AT91_SSC_MSBF)
-			| (( 0				<<  5) & AT91_SSC_DATDEF)
-			| (((bits - 1)			<<  0) & AT91_SSC_DATALEN);
-		break;
-
-	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
-		/*
-		 * I2S format, CODEC supplies BCLK and LRC clocks.
-		 *
-		 * The SSC transmit clock is obtained from the BCLK signal on
-		 * on the TK line, and the SSC receive clock is generated from the
-		 * transmit clock.
-		 *
-		 * For single channel data, one sample is transferred on the falling
-		 * edge of the LRC clock.  For two channel data, one sample is
-		 * transferred on both edges of the LRC clock.
-		 */
-		start_event = channels == 1
-				? AT91_SSC_START_FALLING_RF
-				: AT91_SSC_START_EDGE_RF;
-
-		rcmr =	  (( 0				<< 24) & AT91_SSC_PERIOD)
-			| (( 1				<< 16) & AT91_SSC_STTDLY)
-			| (( start_event		     ) & AT91_SSC_START)
-			| (( AT91_SSC_CK_RISING		     ) & AT91_SSC_CKI)
-			| (( AT91_SSC_CKO_NONE		     ) & AT91_SSC_CKO)
-			| (( AT91_SSC_CKS_CLOCK		     ) & AT91_SSC_CKS);
-
-		rfmr =	  (( AT91_SSC_FSEDGE_POSITIVE	     ) & AT91_SSC_FSEDGE)
-			| (( AT91_SSC_FSOS_NONE		     ) & AT91_SSC_FSOS)
-			| (( 0				<< 16) & AT91_SSC_FSLEN)
-			| (( 0				<<  8) & AT91_SSC_DATNB)
-			| (( 1				<<  7) & AT91_SSC_MSBF)
-			| (( 0				<<  5) & AT91_SSC_LOOP)
-			| (((bits - 1)			<<  0) & AT91_SSC_DATALEN);
-
-		tcmr =	  (( 0				<< 24) & AT91_SSC_PERIOD)
-			| (( 1				<< 16) & AT91_SSC_STTDLY)
-			| (( start_event		     ) & AT91_SSC_START)
-			| (( AT91_SSC_CKI_FALLING	     ) & AT91_SSC_CKI)
-			| (( AT91_SSC_CKO_NONE		     ) & AT91_SSC_CKO)
-			| (( AT91_SSC_CKS_PIN		     ) & AT91_SSC_CKS);
-
-		tfmr =	  (( AT91_SSC_FSEDGE_POSITIVE	     ) & AT91_SSC_FSEDGE)
-			| (( 0				<< 23) & AT91_SSC_FSDEN)
-			| (( AT91_SSC_FSOS_NONE		     ) & AT91_SSC_FSOS)
-			| (( 0				<< 16) & AT91_SSC_FSLEN)
-			| (( 0				<<  8) & AT91_SSC_DATNB)
-			| (( 1				<<  7) & AT91_SSC_MSBF)
-			| (( 0				<<  5) & AT91_SSC_DATDEF)
-			| (((bits - 1)			<<  0) & AT91_SSC_DATALEN);
-		break;
-
-	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
-		/*
-		 * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
-		 *
-		 * The SSC transmit and receive clocks are generated from the
-		 * MCK divider, and the BCLK signal is output on the SSC TK line.
-		 */
-		rcmr =	  (( ssc_p->rcmr_period		<< 24) & AT91_SSC_PERIOD)
-			| (( 1				<< 16) & AT91_SSC_STTDLY)
-			| (( AT91_SSC_START_RISING_RF	     ) & AT91_SSC_START)
-			| (( AT91_SSC_CK_RISING		     ) & AT91_SSC_CKI)
-			| (( AT91_SSC_CKO_NONE		     ) & AT91_SSC_CKO)
-			| (( AT91_SSC_CKS_DIV		     ) & AT91_SSC_CKS);
-
-		rfmr =	  (( AT91_SSC_FSEDGE_POSITIVE	     ) & AT91_SSC_FSEDGE)
-			| (( AT91_SSC_FSOS_POSITIVE	     ) & AT91_SSC_FSOS)
-			| (( 0				<< 16) & AT91_SSC_FSLEN)
-			| (((channels - 1)		<<  8) & AT91_SSC_DATNB)
-			| (( 1				<<  7) & AT91_SSC_MSBF)
-			| (( 0				<<  5) & AT91_SSC_LOOP)
-			| (((bits - 1)			<<  0) & AT91_SSC_DATALEN);
-
-		tcmr =	  (( ssc_p->tcmr_period		<< 24) & AT91_SSC_PERIOD)
-			| (( 1				<< 16) & AT91_SSC_STTDLY)
-			| (( AT91_SSC_START_RISING_RF        ) & AT91_SSC_START)
-			| (( AT91_SSC_CK_RISING		     ) & AT91_SSC_CKI)
-			| (( AT91_SSC_CKO_CONTINUOUS	     ) & AT91_SSC_CKO)
-			| (( AT91_SSC_CKS_DIV		     ) & AT91_SSC_CKS);
-
-		tfmr =	  (( AT91_SSC_FSEDGE_POSITIVE	     ) & AT91_SSC_FSEDGE)
-			| (( 0				<< 23) & AT91_SSC_FSDEN)
-			| (( AT91_SSC_FSOS_POSITIVE	     ) & AT91_SSC_FSOS)
-			| (( 0				<< 16) & AT91_SSC_FSLEN)
-			| (((channels - 1)		<<  8) & AT91_SSC_DATNB)
-			| (( 1				<<  7) & AT91_SSC_MSBF)
-			| (( 0				<<  5) & AT91_SSC_DATDEF)
-			| (((bits - 1)			<<  0) & AT91_SSC_DATALEN);
-
-
-
-			break;
-
-	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
-	default:
-		printk(KERN_WARNING "at91-ssc: unsupported DAI format 0x%x.\n",
-			ssc_p->daifmt);
-		return -EINVAL;
-		break;
-	}
-	DBG("RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n", rcmr, rfmr, tcmr, tfmr);
-
-	if (!ssc_p->initialized) {
-
-		/* Enable PMC peripheral clock for this SSC */
-		DBG("Starting pid %d clock\n", ssc_p->ssc.pid);
-		at91_sys_write(AT91_PMC_PCER, 1<<ssc_p->ssc.pid);
-
-		/* Reset the SSC and its PDC registers */
-		at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR, AT91_SSC_SWRST);
-
-		at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RPR, 0);
-		at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RCR, 0);
-		at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNPR, 0);
-		at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_RNCR, 0);
-		at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TPR, 0);
-		at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TCR, 0);
-		at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNPR, 0);
-		at91_ssc_write(ssc_p->ssc.base + ATMEL_PDC_TNCR, 0);
-
-		if ((ret = request_irq(ssc_p->ssc.pid, at91_ssc_interrupt,
-					0, ssc_p->name, ssc_p)) < 0) {
-			printk(KERN_WARNING "at91-ssc: request_irq failure\n");
-
-			DBG("Stopping pid %d clock\n", ssc_p->ssc.pid);
-			at91_sys_write(AT91_PMC_PCDR, 1<<ssc_p->ssc.pid);
-			return ret;
-		}
-
-		ssc_p->initialized = 1;
-	}
-
-	/* set SSC clock mode register */
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR, ssc_p->cmr_div);
-
-	/* set receive clock mode and format */
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, rcmr);
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, rfmr);
-
-	/* set transmit clock mode and format */
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, tcmr);
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, tfmr);
-
-	DBG("hw_params: SSC initialized\n");
-	return 0;
-}
-
-
-static int at91_ssc_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct at91_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
-	struct at91_pcm_dma_params *dma_params;
-	int dir;
-
-	dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
-	dma_params = ssc_p->dma_params[dir];
-
-	at91_ssc_write(dma_params->ssc_base + AT91_SSC_CR,
-			dma_params->mask->ssc_enable);
-
-	DBG("%s enabled SSC_SR=0x%08lx\n", dir ? "receive" : "transmit",
-		at91_ssc_read(dma_params->ssc_base + AT91_SSC_SR));
-	return 0;
-}
-
-
-#ifdef CONFIG_PM
-static int at91_ssc_suspend(struct platform_device *pdev,
-	struct snd_soc_dai *cpu_dai)
-{
-	struct at91_ssc_info *ssc_p;
-
-	if(!cpu_dai->active)
-		return 0;
-
-	ssc_p = &ssc_info[cpu_dai->id];
-
-	/* Save the status register before disabling transmit and receive. */
-	ssc_p->ssc_state.ssc_sr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_SR);
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
-			AT91_SSC_TXDIS | AT91_SSC_RXDIS);
-
-	/* Save the current interrupt mask, then disable unmasked interrupts. */
-	ssc_p->ssc_state.ssc_imr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_IMR);
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IDR, ssc_p->ssc_state.ssc_imr);
-
-	ssc_p->ssc_state.ssc_cmr  = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_CMR);
-	ssc_p->ssc_state.ssc_rcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RCMR);
-	ssc_p->ssc_state.ssc_rfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_RFMR);
-	ssc_p->ssc_state.ssc_tcmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TCMR);
-	ssc_p->ssc_state.ssc_tfmr = at91_ssc_read(ssc_p->ssc.base + AT91_SSC_TFMR);
-
-	return 0;
-}
-
-static int at91_ssc_resume(struct platform_device *pdev,
-	struct snd_soc_dai *cpu_dai)
-{
-	struct at91_ssc_info *ssc_p;
-
-	if(!cpu_dai->active)
-		return 0;
-
-	ssc_p = &ssc_info[cpu_dai->id];
-
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TFMR, ssc_p->ssc_state.ssc_tfmr);
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_TCMR, ssc_p->ssc_state.ssc_tcmr);
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RFMR, ssc_p->ssc_state.ssc_rfmr);
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_RCMR, ssc_p->ssc_state.ssc_rcmr);
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CMR,  ssc_p->ssc_state.ssc_cmr);
-
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_IER,  ssc_p->ssc_state.ssc_imr);
-
-	at91_ssc_write(ssc_p->ssc.base + AT91_SSC_CR,
-		((ssc_p->ssc_state.ssc_sr & AT91_SSC_RXENA) ? AT91_SSC_RXEN : 0) |
-		((ssc_p->ssc_state.ssc_sr & AT91_SSC_TXENA) ? AT91_SSC_TXEN : 0));
-
-	return 0;
-}
-
-#else
-#define at91_ssc_suspend	NULL
-#define at91_ssc_resume		NULL
-#endif
-
-#define AT91_SSC_RATES (SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 |\
-			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
-			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
-			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
-			SNDRV_PCM_RATE_96000)
-
-#define AT91_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
-			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-
-struct snd_soc_dai at91_ssc_dai[NUM_SSC_DEVICES] = {
-	{	.name = "at91-ssc0",
-		.id = 0,
-		.type = SND_SOC_DAI_PCM,
-		.suspend = at91_ssc_suspend,
-		.resume = at91_ssc_resume,
-		.playback = {
-			.channels_min = 1,
-			.channels_max = 2,
-			.rates = AT91_SSC_RATES,
-			.formats = AT91_SSC_FORMATS,},
-		.capture = {
-			.channels_min = 1,
-			.channels_max = 2,
-			.rates = AT91_SSC_RATES,
-			.formats = AT91_SSC_FORMATS,},
-		.ops = {
-			.startup = at91_ssc_startup,
-			.shutdown = at91_ssc_shutdown,
-			.prepare = at91_ssc_prepare,
-			.hw_params = at91_ssc_hw_params,},
-		.dai_ops = {
-			.set_sysclk = at91_ssc_set_dai_sysclk,
-			.set_fmt = at91_ssc_set_dai_fmt,
-			.set_clkdiv = at91_ssc_set_dai_clkdiv,},
-		.private_data = &ssc_info[0].ssc,
-	},
-#if NUM_SSC_DEVICES == 3
-	{	.name = "at91-ssc1",
-		.id = 1,
-		.type = SND_SOC_DAI_PCM,
-		.suspend = at91_ssc_suspend,
-		.resume = at91_ssc_resume,
-		.playback = {
-			.channels_min = 1,
-			.channels_max = 2,
-			.rates = AT91_SSC_RATES,
-			.formats = AT91_SSC_FORMATS,},
-		.capture = {
-			.channels_min = 1,
-			.channels_max = 2,
-			.rates = AT91_SSC_RATES,
-			.formats = AT91_SSC_FORMATS,},
-		.ops = {
-			.startup = at91_ssc_startup,
-			.shutdown = at91_ssc_shutdown,
-			.prepare = at91_ssc_prepare,
-			.hw_params = at91_ssc_hw_params,},
-		.dai_ops = {
-			.set_sysclk = at91_ssc_set_dai_sysclk,
-			.set_fmt = at91_ssc_set_dai_fmt,
-			.set_clkdiv = at91_ssc_set_dai_clkdiv,},
-		.private_data = &ssc_info[1].ssc,
-	},
-	{	.name = "at91-ssc2",
-		.id = 2,
-		.type = SND_SOC_DAI_PCM,
-		.suspend = at91_ssc_suspend,
-		.resume = at91_ssc_resume,
-		.playback = {
-			.channels_min = 1,
-			.channels_max = 2,
-			.rates = AT91_SSC_RATES,
-			.formats = AT91_SSC_FORMATS,},
-		.capture = {
-			.channels_min = 1,
-			.channels_max = 2,
-			.rates = AT91_SSC_RATES,
-			.formats = AT91_SSC_FORMATS,},
-		.ops = {
-			.startup = at91_ssc_startup,
-			.shutdown = at91_ssc_shutdown,
-			.prepare = at91_ssc_prepare,
-			.hw_params = at91_ssc_hw_params,},
-		.dai_ops = {
-			.set_sysclk = at91_ssc_set_dai_sysclk,
-			.set_fmt = at91_ssc_set_dai_fmt,
-			.set_clkdiv = at91_ssc_set_dai_clkdiv,},
-		.private_data = &ssc_info[2].ssc,
-	},
-#endif
-};
-
-EXPORT_SYMBOL_GPL(at91_ssc_dai);
-
-/* Module information */
-MODULE_AUTHOR("Frank Mandarino, fmandarino@endrelia.com, www.endrelia.com");
-MODULE_DESCRIPTION("AT91 SSC ASoC Interface");
-MODULE_LICENSE("GPL");

+ 0 - 27
sound/soc/at91/at91-ssc.h

@@ -1,27 +0,0 @@
-/*
- * at91-ssc.h - ALSA SSC interface for the Atmel AT91 SoC
- *
- * Author:	Frank Mandarino <fmandarino@endrelia.com>
- *		Endrelia Technologies Inc.
- * Created:	Jan 9, 2007
- *
- * 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 _AT91_SSC_H
-#define _AT91_SSC_H
-
-/* SSC system clock ids */
-#define AT91_SYSCLK_MCK		0 /* SSC uses AT91 MCK as system clock */
-
-/* SSC divider ids */
-#define AT91SSC_CMR_DIV		0 /* MCK divider for BCLK */
-#define AT91SSC_TCMR_PERIOD	1 /* BCLK divider for transmit FS */
-#define AT91SSC_RCMR_PERIOD	2 /* BCLK divider for receive FS */
-
-extern struct snd_soc_dai at91_ssc_dai[];
-
-#endif /* _AT91_SSC_H */
-

+ 43 - 0
sound/soc/atmel/Kconfig

@@ -0,0 +1,43 @@
+config SND_ATMEL_SOC
+	tristate "SoC Audio for the Atmel System-on-Chip"
+	depends on ARCH_AT91 || AVR32
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the ATMEL SSC interface. You will also need
+	  to select the audio interfaces to support below.
+
+config SND_ATMEL_SOC_SSC
+	tristate
+	depends on SND_ATMEL_SOC
+	help
+	  Say Y or M if you want to add support for codecs the
+	  ATMEL SSC interface. You will also needs to select the individual
+	  machine drivers to support below.
+
+config SND_AT91_SOC_SAM9G20_WM8731
+	tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
+	depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC
+	select SND_ATMEL_SOC_SSC
+	select SND_SOC_WM8731
+	help
+	  Say Y if you want to add support for SoC audio on WM8731-based
+	  AT91sam9g20 evaluation board.
+
+config SND_AT32_SOC_PLAYPAQ
+        tristate "SoC Audio support for PlayPaq with WM8510"
+        depends on SND_ATMEL_SOC && BOARD_PLAYPAQ
+        select SND_ATMEL_SOC_SSC
+        select SND_SOC_WM8510
+        help
+          Say Y or M here if you want to add support for SoC audio
+          on the LRS PlayPaq.
+
+config SND_AT32_SOC_PLAYPAQ_SLAVE
+        bool "Run CODEC on PlayPaq in slave mode"
+        depends on SND_AT32_SOC_PLAYPAQ
+        default n
+        help
+          Say Y if you want to run with the AT32 SSC generating the BCLK
+          and FRAME signals on the PlayPaq.  Unless you want to play
+          with the AT32 as the SSC master, you probably want to say N here,
+          as this will give you better sound quality.

+ 15 - 0
sound/soc/atmel/Makefile

@@ -0,0 +1,15 @@
+# AT91 Platform Support
+snd-soc-atmel-pcm-objs := atmel-pcm.o
+snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
+
+obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o
+obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
+
+# AT91 Machine Support
+snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
+
+# AT32 Machine Support
+snd-soc-playpaq-objs := playpaq_wm8510.o
+
+obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
+obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o

+ 494 - 0
sound/soc/atmel/atmel-pcm.c

@@ -0,0 +1,494 @@
+/*
+ * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC.
+ *
+ *  Copyright (C) 2005 SAN People
+ *  Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on at91-pcm. by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ *
+ * Based on pxa2xx-pcm.c by:
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Nov 30, 2004
+ * Copyright:	(C) 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/hardware.h>
+
+#include "atmel-pcm.h"
+
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+/* TODO: These values were taken from the AT91 platform driver, check
+ *	 them against real values for AT32
+ */
+static const struct snd_pcm_hardware atmel_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_PAUSE,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.period_bytes_min	= 32,
+	.period_bytes_max	= 8192,
+	.periods_min		= 2,
+	.periods_max		= 1024,
+	.buffer_bytes_max	= 32 * 1024,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * Data types
+\*--------------------------------------------------------------------------*/
+struct atmel_runtime_data {
+	struct atmel_pcm_dma_params *params;
+	dma_addr_t dma_buffer;		/* physical address of dma buffer */
+	dma_addr_t dma_buffer_end;	/* first address beyond DMA buffer */
+	size_t period_size;
+
+	dma_addr_t period_ptr;		/* physical address of next period */
+	int periods;			/* period index of period_ptr */
+
+	/* PDC register save */
+	u32 pdc_xpr_save;
+	u32 pdc_xcr_save;
+	u32 pdc_xnpr_save;
+	u32 pdc_xncr_save;
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * Helper functions
+\*--------------------------------------------------------------------------*/
+static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
+	int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = atmel_pcm_hardware.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+					  &buf->addr, GFP_KERNEL);
+	pr_debug("atmel-pcm:"
+		"preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
+		(void *) buf->area,
+		(void *) buf->addr,
+		size);
+
+	if (!buf->area)
+		return -ENOMEM;
+
+	buf->bytes = size;
+	return 0;
+}
+/*--------------------------------------------------------------------------*\
+ * ISR
+\*--------------------------------------------------------------------------*/
+static void atmel_pcm_dma_irq(u32 ssc_sr,
+	struct snd_pcm_substream *substream)
+{
+	struct atmel_runtime_data *prtd = substream->runtime->private_data;
+	struct atmel_pcm_dma_params *params = prtd->params;
+	static int count;
+
+	count++;
+
+	if (ssc_sr & params->mask->ssc_endbuf) {
+		pr_warning("atmel-pcm: buffer %s on %s"
+				" (SSC_SR=%#x, count=%d)\n",
+				substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+				? "underrun" : "overrun",
+				params->name, ssc_sr, count);
+
+		/* re-start the PDC */
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_disable);
+		prtd->period_ptr += prtd->period_size;
+		if (prtd->period_ptr >= prtd->dma_buffer_end)
+			prtd->period_ptr = prtd->dma_buffer;
+
+		ssc_writex(params->ssc->regs, params->pdc->xpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xcr,
+			   prtd->period_size / params->pdc_xfer_size);
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_enable);
+	}
+
+	if (ssc_sr & params->mask->ssc_endx) {
+		/* Load the PDC next pointer and counter registers */
+		prtd->period_ptr += prtd->period_size;
+		if (prtd->period_ptr >= prtd->dma_buffer_end)
+			prtd->period_ptr = prtd->dma_buffer;
+
+		ssc_writex(params->ssc->regs, params->pdc->xnpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xncr,
+			   prtd->period_size / params->pdc_xfer_size);
+	}
+
+	snd_pcm_period_elapsed(substream);
+}
+
+
+/*--------------------------------------------------------------------------*\
+ * PCM operations
+\*--------------------------------------------------------------------------*/
+static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct atmel_runtime_data *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	/* this may get called several times by oss emulation
+	 * with different params */
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(params);
+
+	prtd->params = rtd->dai->cpu_dai->dma_data;
+	prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
+
+	prtd->dma_buffer = runtime->dma_addr;
+	prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
+	prtd->period_size = params_period_bytes(params);
+
+	pr_debug("atmel-pcm: "
+		"hw_params: DMA for %s initialized "
+		"(dma_bytes=%u, period_size=%u)\n",
+		prtd->params->name,
+		runtime->dma_bytes,
+		prtd->period_size);
+	return 0;
+}
+
+static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct atmel_runtime_data *prtd = substream->runtime->private_data;
+	struct atmel_pcm_dma_params *params = prtd->params;
+
+	if (params != NULL) {
+		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+			   params->mask->pdc_disable);
+		prtd->params->dma_intr_handler = NULL;
+	}
+
+	return 0;
+}
+
+static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct atmel_runtime_data *prtd = substream->runtime->private_data;
+	struct atmel_pcm_dma_params *params = prtd->params;
+
+	ssc_writex(params->ssc->regs, SSC_IDR,
+		   params->mask->ssc_endx | params->mask->ssc_endbuf);
+	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+		   params->mask->pdc_disable);
+	return 0;
+}
+
+static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
+	int cmd)
+{
+	struct snd_pcm_runtime *rtd = substream->runtime;
+	struct atmel_runtime_data *prtd = rtd->private_data;
+	struct atmel_pcm_dma_params *params = prtd->params;
+	int ret = 0;
+
+	pr_debug("atmel-pcm:buffer_size = %ld,"
+		"dma_area = %p, dma_bytes = %u\n",
+		rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		prtd->period_ptr = prtd->dma_buffer;
+
+		ssc_writex(params->ssc->regs, params->pdc->xpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xcr,
+			   prtd->period_size / params->pdc_xfer_size);
+
+		prtd->period_ptr += prtd->period_size;
+		ssc_writex(params->ssc->regs, params->pdc->xnpr,
+			   prtd->period_ptr);
+		ssc_writex(params->ssc->regs, params->pdc->xncr,
+			   prtd->period_size / params->pdc_xfer_size);
+
+		pr_debug("atmel-pcm: trigger: "
+			"period_ptr=%lx, xpr=%u, "
+			"xcr=%u, xnpr=%u, xncr=%u\n",
+			(unsigned long)prtd->period_ptr,
+			ssc_readx(params->ssc->regs, params->pdc->xpr),
+			ssc_readx(params->ssc->regs, params->pdc->xcr),
+			ssc_readx(params->ssc->regs, params->pdc->xnpr),
+			ssc_readx(params->ssc->regs, params->pdc->xncr));
+
+		ssc_writex(params->ssc->regs, SSC_IER,
+			   params->mask->ssc_endx | params->mask->ssc_endbuf);
+		ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+			   params->mask->pdc_enable);
+
+		pr_debug("sr=%u imr=%u\n",
+			ssc_readx(params->ssc->regs, SSC_SR),
+			ssc_readx(params->ssc->regs, SSC_IER));
+		break;		/* SNDRV_PCM_TRIGGER_START */
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_disable);
+		break;
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+			   params->mask->pdc_enable);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t atmel_pcm_pointer(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct atmel_runtime_data *prtd = runtime->private_data;
+	struct atmel_pcm_dma_params *params = prtd->params;
+	dma_addr_t ptr;
+	snd_pcm_uframes_t x;
+
+	ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
+	x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
+
+	if (x == runtime->buffer_size)
+		x = 0;
+
+	return x;
+}
+
+static int atmel_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct atmel_runtime_data *prtd;
+	int ret = 0;
+
+	snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
+
+	/* ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+						SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		goto out;
+
+	prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
+	if (prtd == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	runtime->private_data = prtd;
+
+ out:
+	return ret;
+}
+
+static int atmel_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct atmel_runtime_data *prtd = substream->runtime->private_data;
+
+	kfree(prtd);
+	return 0;
+}
+
+static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
+	struct vm_area_struct *vma)
+{
+	return remap_pfn_range(vma, vma->vm_start,
+		       substream->dma_buffer.addr >> PAGE_SHIFT,
+		       vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+struct snd_pcm_ops atmel_pcm_ops = {
+	.open		= atmel_pcm_open,
+	.close		= atmel_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= atmel_pcm_hw_params,
+	.hw_free	= atmel_pcm_hw_free,
+	.prepare	= atmel_pcm_prepare,
+	.trigger	= atmel_pcm_trigger,
+	.pointer	= atmel_pcm_pointer,
+	.mmap		= atmel_pcm_mmap,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * ASoC platform driver
+\*--------------------------------------------------------------------------*/
+static u64 atmel_pcm_dmamask = 0xffffffff;
+
+static int atmel_pcm_new(struct snd_card *card,
+	struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &atmel_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (dai->playback.channels_min) {
+		ret = atmel_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (dai->capture.channels_min) {
+		pr_debug("at32-pcm:"
+				"Allocating PCM capture DMA buffer\n");
+		ret = atmel_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+ out:
+	return ret;
+}
+
+static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		dma_free_coherent(pcm->card->dev, buf->bytes,
+				  buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+
+#ifdef CONFIG_PM
+static int atmel_pcm_suspend(struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = dai->runtime;
+	struct atmel_runtime_data *prtd;
+	struct atmel_pcm_dma_params *params;
+
+	if (!runtime)
+		return 0;
+
+	prtd = runtime->private_data;
+	params = prtd->params;
+
+	/* disable the PDC and save the PDC registers */
+
+	ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
+
+	prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
+	prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
+	prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
+	prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
+
+	return 0;
+}
+
+static int atmel_pcm_resume(struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = dai->runtime;
+	struct atmel_runtime_data *prtd;
+	struct atmel_pcm_dma_params *params;
+
+	if (!runtime)
+		return 0;
+
+	prtd = runtime->private_data;
+	params = prtd->params;
+
+	/* restore the PDC registers and enable the PDC */
+	ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
+	ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
+	ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
+	ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
+
+	ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
+	return 0;
+}
+#else
+#define atmel_pcm_suspend	NULL
+#define atmel_pcm_resume	NULL
+#endif
+
+struct snd_soc_platform atmel_soc_platform = {
+	.name		= "atmel-audio",
+	.pcm_ops 	= &atmel_pcm_ops,
+	.pcm_new	= atmel_pcm_new,
+	.pcm_free	= atmel_pcm_free_dma_buffers,
+	.suspend	= atmel_pcm_suspend,
+	.resume		= atmel_pcm_resume,
+};
+EXPORT_SYMBOL_GPL(atmel_soc_platform);
+
+static int __init atmel_pcm_modinit(void)
+{
+	return snd_soc_register_platform(&atmel_soc_platform);
+}
+module_init(atmel_pcm_modinit);
+
+static void __exit atmel_pcm_modexit(void)
+{
+	snd_soc_unregister_platform(&atmel_soc_platform);
+}
+module_exit(atmel_pcm_modexit);
+
+MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
+MODULE_DESCRIPTION("Atmel PCM module");
+MODULE_LICENSE("GPL");

+ 86 - 0
sound/soc/atmel/atmel-pcm.h

@@ -0,0 +1,86 @@
+/*
+ * at91-pcm.h - ALSA PCM interface for the Atmel AT91 SoC.
+ *
+ *  Copyright (C) 2005 SAN People
+ *  Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on at91-pcm. by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ *
+ * Based on pxa2xx-pcm.c by:
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Nov 30, 2004
+ * Copyright:	(C) 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _ATMEL_PCM_H
+#define _ATMEL_PCM_H
+
+#include <linux/atmel-ssc.h>
+
+/*
+ * Registers and status bits that are required by the PCM driver.
+ */
+struct atmel_pdc_regs {
+	unsigned int	xpr;		/* PDC recv/trans pointer */
+	unsigned int	xcr;		/* PDC recv/trans counter */
+	unsigned int	xnpr;		/* PDC next recv/trans pointer */
+	unsigned int	xncr;		/* PDC next recv/trans counter */
+	unsigned int	ptcr;		/* PDC transfer control */
+};
+
+struct atmel_ssc_mask {
+	u32	ssc_enable;		/* SSC recv/trans enable */
+	u32	ssc_disable;		/* SSC recv/trans disable */
+	u32	ssc_endx;		/* SSC ENDTX or ENDRX */
+	u32	ssc_endbuf;		/* SSC TXBUFE or RXBUFF */
+	u32	pdc_enable;		/* PDC recv/trans enable */
+	u32	pdc_disable;		/* PDC recv/trans disable */
+};
+
+/*
+ * This structure, shared between the PCM driver and the interface,
+ * contains all information required by the PCM driver to perform the
+ * PDC DMA operation.  All fields except dma_intr_handler() are initialized
+ * by the interface.  The dms_intr_handler() pointer is set by the PCM
+ * driver and called by the interface SSC interrupt handler if it is
+ * non-NULL.
+ */
+struct atmel_pcm_dma_params {
+	char *name;			/* stream identifier */
+	int pdc_xfer_size;		/* PDC counter increment in bytes */
+	struct ssc_device *ssc;		/* SSC device for stream */
+	struct atmel_pdc_regs *pdc;	/* PDC receive or transmit registers */
+	struct atmel_ssc_mask *mask;	/* SSC & PDC status bits */
+	struct snd_pcm_substream *substream;
+	void (*dma_intr_handler)(u32, struct snd_pcm_substream *);
+};
+
+extern struct snd_soc_platform atmel_soc_platform;
+
+
+/*
+ * SSC register access (since ssc_writel() / ssc_readl() require literal name)
+ */
+#define ssc_readx(base, reg)            (__raw_readl((base) + (reg)))
+#define ssc_writex(base, reg, value)    __raw_writel((value), (base) + (reg))
+
+#endif /* _ATMEL_PCM_H */

+ 790 - 0
sound/soc/atmel/atmel_ssc_dai.c

@@ -0,0 +1,790 @@
+/*
+ * atmel_ssc_dai.c  --  ALSA SoC ATMEL SSC Audio Layer Platform driver
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Author: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *         ATMEL CORP.
+ *
+ * Based on at91-ssc.c by
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Based on pxa2xx Platform drivers by
+ * Liam Girdwood <liam.girdwood@wolfsonmicro.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/atmel_pdc.h>
+
+#include <linux/atmel-ssc.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <mach/hardware.h>
+
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+
+#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
+#define NUM_SSC_DEVICES		1
+#else
+#define NUM_SSC_DEVICES		3
+#endif
+
+/*
+ * SSC PDC registers required by the PCM DMA engine.
+ */
+static struct atmel_pdc_regs pdc_tx_reg = {
+	.xpr		= ATMEL_PDC_TPR,
+	.xcr		= ATMEL_PDC_TCR,
+	.xnpr		= ATMEL_PDC_TNPR,
+	.xncr		= ATMEL_PDC_TNCR,
+};
+
+static struct atmel_pdc_regs pdc_rx_reg = {
+	.xpr		= ATMEL_PDC_RPR,
+	.xcr		= ATMEL_PDC_RCR,
+	.xnpr		= ATMEL_PDC_RNPR,
+	.xncr		= ATMEL_PDC_RNCR,
+};
+
+/*
+ * SSC & PDC status bits for transmit and receive.
+ */
+static struct atmel_ssc_mask ssc_tx_mask = {
+	.ssc_enable	= SSC_BIT(CR_TXEN),
+	.ssc_disable	= SSC_BIT(CR_TXDIS),
+	.ssc_endx	= SSC_BIT(SR_ENDTX),
+	.ssc_endbuf	= SSC_BIT(SR_TXBUFE),
+	.pdc_enable	= ATMEL_PDC_TXTEN,
+	.pdc_disable	= ATMEL_PDC_TXTDIS,
+};
+
+static struct atmel_ssc_mask ssc_rx_mask = {
+	.ssc_enable	= SSC_BIT(CR_RXEN),
+	.ssc_disable	= SSC_BIT(CR_RXDIS),
+	.ssc_endx	= SSC_BIT(SR_ENDRX),
+	.ssc_endbuf	= SSC_BIT(SR_RXBUFF),
+	.pdc_enable	= ATMEL_PDC_RXTEN,
+	.pdc_disable	= ATMEL_PDC_RXTDIS,
+};
+
+
+/*
+ * DMA parameters.
+ */
+static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
+	{{
+	.name		= "SSC0 PCM out",
+	.pdc		= &pdc_tx_reg,
+	.mask		= &ssc_tx_mask,
+	},
+	{
+	.name		= "SSC0 PCM in",
+	.pdc		= &pdc_rx_reg,
+	.mask		= &ssc_rx_mask,
+	} },
+#if NUM_SSC_DEVICES == 3
+	{{
+	.name		= "SSC1 PCM out",
+	.pdc		= &pdc_tx_reg,
+	.mask		= &ssc_tx_mask,
+	},
+	{
+	.name		= "SSC1 PCM in",
+	.pdc		= &pdc_rx_reg,
+	.mask		= &ssc_rx_mask,
+	} },
+	{{
+	.name		= "SSC2 PCM out",
+	.pdc		= &pdc_tx_reg,
+	.mask		= &ssc_tx_mask,
+	},
+	{
+	.name		= "SSC2 PCM in",
+	.pdc		= &pdc_rx_reg,
+	.mask		= &ssc_rx_mask,
+	} },
+#endif
+};
+
+
+static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
+	{
+	.name		= "ssc0",
+	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[0].lock),
+	.dir_mask	= SSC_DIR_MASK_UNUSED,
+	.initialized	= 0,
+	},
+#if NUM_SSC_DEVICES == 3
+	{
+	.name		= "ssc1",
+	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
+	.dir_mask	= SSC_DIR_MASK_UNUSED,
+	.initialized	= 0,
+	},
+	{
+	.name		= "ssc2",
+	.lock		= __SPIN_LOCK_UNLOCKED(ssc_info[2].lock),
+	.dir_mask	= SSC_DIR_MASK_UNUSED,
+	.initialized	= 0,
+	},
+#endif
+};
+
+
+/*
+ * SSC interrupt handler.  Passes PDC interrupts to the DMA
+ * interrupt handler in the PCM driver.
+ */
+static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
+{
+	struct atmel_ssc_info *ssc_p = dev_id;
+	struct atmel_pcm_dma_params *dma_params;
+	u32 ssc_sr;
+	u32 ssc_substream_mask;
+	int i;
+
+	ssc_sr = (unsigned long)ssc_readl(ssc_p->ssc->regs, SR)
+			& (unsigned long)ssc_readl(ssc_p->ssc->regs, IMR);
+
+	/*
+	 * Loop through the substreams attached to this SSC.  If
+	 * a DMA-related interrupt occurred on that substream, call
+	 * the DMA interrupt handler function, if one has been
+	 * registered in the dma_params structure by the PCM driver.
+	 */
+	for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
+		dma_params = ssc_p->dma_params[i];
+
+		if ((dma_params != NULL) &&
+			(dma_params->dma_intr_handler != NULL)) {
+			ssc_substream_mask = (dma_params->mask->ssc_endx |
+					dma_params->mask->ssc_endbuf);
+			if (ssc_sr & ssc_substream_mask) {
+				dma_params->dma_intr_handler(ssc_sr,
+						dma_params->
+						substream);
+			}
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+/*-------------------------------------------------------------------------*\
+ * DAI functions
+\*-------------------------------------------------------------------------*/
+/*
+ * Startup.  Only that one substream allowed in each direction.
+ */
+static int atmel_ssc_startup(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+	int dir_mask;
+
+	pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
+		ssc_readl(ssc_p->ssc->regs, SR));
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir_mask = SSC_DIR_MASK_PLAYBACK;
+	else
+		dir_mask = SSC_DIR_MASK_CAPTURE;
+
+	spin_lock_irq(&ssc_p->lock);
+	if (ssc_p->dir_mask & dir_mask) {
+		spin_unlock_irq(&ssc_p->lock);
+		return -EBUSY;
+	}
+	ssc_p->dir_mask |= dir_mask;
+	spin_unlock_irq(&ssc_p->lock);
+
+	return 0;
+}
+
+/*
+ * Shutdown.  Clear DMA parameters and shutdown the SSC if there
+ * are no other substreams open.
+ */
+static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+	struct atmel_pcm_dma_params *dma_params;
+	int dir, dir_mask;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = 0;
+	else
+		dir = 1;
+
+	dma_params = ssc_p->dma_params[dir];
+
+	if (dma_params != NULL) {
+		ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
+		pr_debug("atmel_ssc_shutdown: %s disabled SSC_SR=0x%08x\n",
+			(dir ? "receive" : "transmit"),
+			ssc_readl(ssc_p->ssc->regs, SR));
+
+		dma_params->ssc = NULL;
+		dma_params->substream = NULL;
+		ssc_p->dma_params[dir] = NULL;
+	}
+
+	dir_mask = 1 << dir;
+
+	spin_lock_irq(&ssc_p->lock);
+	ssc_p->dir_mask &= ~dir_mask;
+	if (!ssc_p->dir_mask) {
+		if (ssc_p->initialized) {
+			/* Shutdown the SSC clock. */
+			pr_debug("atmel_ssc_dau: Stopping clock\n");
+			clk_disable(ssc_p->ssc->clk);
+
+			free_irq(ssc_p->ssc->irq, ssc_p);
+			ssc_p->initialized = 0;
+		}
+
+		/* Reset the SSC */
+		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+		/* Clear the SSC dividers */
+		ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
+	}
+	spin_unlock_irq(&ssc_p->lock);
+}
+
+
+/*
+ * Record the DAI format for use in hw_params().
+ */
+static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+		unsigned int fmt)
+{
+	struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+	ssc_p->daifmt = fmt;
+	return 0;
+}
+
+/*
+ * Record SSC clock dividers for use in hw_params().
+ */
+static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+	int div_id, int div)
+{
+	struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+
+	switch (div_id) {
+	case ATMEL_SSC_CMR_DIV:
+		/*
+		 * The same master clock divider is used for both
+		 * transmit and receive, so if a value has already
+		 * been set, it must match this value.
+		 */
+		if (ssc_p->cmr_div == 0)
+			ssc_p->cmr_div = div;
+		else
+			if (div != ssc_p->cmr_div)
+				return -EBUSY;
+		break;
+
+	case ATMEL_SSC_TCMR_PERIOD:
+		ssc_p->tcmr_period = div;
+		break;
+
+	case ATMEL_SSC_RCMR_PERIOD:
+		ssc_p->rcmr_period = div;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Configure the SSC.
+ */
+static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	int id = rtd->dai->cpu_dai->id;
+	struct atmel_ssc_info *ssc_p = &ssc_info[id];
+	struct atmel_pcm_dma_params *dma_params;
+	int dir, channels, bits;
+	u32 tfmr, rfmr, tcmr, rcmr;
+	int start_event;
+	int ret;
+
+	/*
+	 * Currently, there is only one set of dma params for
+	 * each direction.  If more are added, this code will
+	 * have to be changed to select the proper set.
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = 0;
+	else
+		dir = 1;
+
+	dma_params = &ssc_dma_params[id][dir];
+	dma_params->ssc = ssc_p->ssc;
+	dma_params->substream = substream;
+
+	ssc_p->dma_params[dir] = dma_params;
+
+	/*
+	 * The cpu_dai->dma_data field is only used to communicate the
+	 * appropriate DMA parameters to the pcm driver hw_params()
+	 * function.  It should not be used for other purposes
+	 * as it is common to all substreams.
+	 */
+	rtd->dai->cpu_dai->dma_data = dma_params;
+
+	channels = params_channels(params);
+
+	/*
+	 * Determine sample size in bits and the PDC increment.
+	 */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		bits = 8;
+		dma_params->pdc_xfer_size = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bits = 16;
+		dma_params->pdc_xfer_size = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bits = 24;
+		dma_params->pdc_xfer_size = 4;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bits = 32;
+		dma_params->pdc_xfer_size = 4;
+		break;
+	default:
+		printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format");
+		return -EINVAL;
+	}
+
+	/*
+	 * The SSC only supports up to 16-bit samples in I2S format, due
+	 * to the size of the Frame Mode Register FSLEN field.
+	 */
+	if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
+		&& bits > 16) {
+		printk(KERN_WARNING
+				"atmel_ssc_dai: sample size %d"
+				"is too large for I2S\n", bits);
+		return -EINVAL;
+	}
+
+	/*
+	 * Compute SSC register settings.
+	 */
+	switch (ssc_p->daifmt
+		& (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+		/*
+		 * I2S format, SSC provides BCLK and LRC clocks.
+		 *
+		 * The SSC transmit and receive clocks are generated
+		 * from the MCK divider, and the BCLK signal
+		 * is output on the SSC TK line.
+		 */
+		rcmr =	  SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
+			| SSC_BF(RCMR_STTDLY, START_DELAY)
+			| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
+			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+			| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
+
+		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+			| SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
+			| SSC_BF(RFMR_FSLEN, (bits - 1))
+			| SSC_BF(RFMR_DATNB, (channels - 1))
+			| SSC_BIT(RFMR_MSBF)
+			| SSC_BF(RFMR_LOOP, 0)
+			| SSC_BF(RFMR_DATLEN, (bits - 1));
+
+		tcmr =	  SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
+			| SSC_BF(TCMR_STTDLY, START_DELAY)
+			| SSC_BF(TCMR_START, SSC_START_FALLING_RF)
+			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
+			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
+			| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
+
+		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+			| SSC_BF(TFMR_FSDEN, 0)
+			| SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
+			| SSC_BF(TFMR_FSLEN, (bits - 1))
+			| SSC_BF(TFMR_DATNB, (channels - 1))
+			| SSC_BIT(TFMR_MSBF)
+			| SSC_BF(TFMR_DATDEF, 0)
+			| SSC_BF(TFMR_DATLEN, (bits - 1));
+		break;
+
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+		/*
+		 * I2S format, CODEC supplies BCLK and LRC clocks.
+		 *
+		 * The SSC transmit clock is obtained from the BCLK signal on
+		 * on the TK line, and the SSC receive clock is
+		 * generated from the transmit clock.
+		 *
+		 *  For single channel data, one sample is transferred
+		 * on the falling edge of the LRC clock.
+		 * For two channel data, one sample is
+		 * transferred on both edges of the LRC clock.
+		 */
+		start_event = ((channels == 1)
+				? SSC_START_FALLING_RF
+				: SSC_START_EDGE_RF);
+
+		rcmr =	  SSC_BF(RCMR_PERIOD, 0)
+			| SSC_BF(RCMR_STTDLY, START_DELAY)
+			| SSC_BF(RCMR_START, start_event)
+			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+			| SSC_BF(RCMR_CKS, SSC_CKS_CLOCK);
+
+		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+			| SSC_BF(RFMR_FSOS, SSC_FSOS_NONE)
+			| SSC_BF(RFMR_FSLEN, 0)
+			| SSC_BF(RFMR_DATNB, 0)
+			| SSC_BIT(RFMR_MSBF)
+			| SSC_BF(RFMR_LOOP, 0)
+			| SSC_BF(RFMR_DATLEN, (bits - 1));
+
+		tcmr =	  SSC_BF(TCMR_PERIOD, 0)
+			| SSC_BF(TCMR_STTDLY, START_DELAY)
+			| SSC_BF(TCMR_START, start_event)
+			| SSC_BF(TCMR_CKI, SSC_CKI_FALLING)
+			| SSC_BF(TCMR_CKO, SSC_CKO_NONE)
+			| SSC_BF(TCMR_CKS, SSC_CKS_PIN);
+
+		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+			| SSC_BF(TFMR_FSDEN, 0)
+			| SSC_BF(TFMR_FSOS, SSC_FSOS_NONE)
+			| SSC_BF(TFMR_FSLEN, 0)
+			| SSC_BF(TFMR_DATNB, 0)
+			| SSC_BIT(TFMR_MSBF)
+			| SSC_BF(TFMR_DATDEF, 0)
+			| SSC_BF(TFMR_DATLEN, (bits - 1));
+		break;
+
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS:
+		/*
+		 * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks.
+		 *
+		 * The SSC transmit and receive clocks are generated from the
+		 * MCK divider, and the BCLK signal is output
+		 * on the SSC TK line.
+		 */
+		rcmr =	  SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
+			| SSC_BF(RCMR_STTDLY, 1)
+			| SSC_BF(RCMR_START, SSC_START_RISING_RF)
+			| SSC_BF(RCMR_CKI, SSC_CKI_RISING)
+			| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
+			| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
+
+		rfmr =	  SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+			| SSC_BF(RFMR_FSOS, SSC_FSOS_POSITIVE)
+			| SSC_BF(RFMR_FSLEN, 0)
+			| SSC_BF(RFMR_DATNB, (channels - 1))
+			| SSC_BIT(RFMR_MSBF)
+			| SSC_BF(RFMR_LOOP, 0)
+			| SSC_BF(RFMR_DATLEN, (bits - 1));
+
+		tcmr =	  SSC_BF(TCMR_PERIOD, ssc_p->tcmr_period)
+			| SSC_BF(TCMR_STTDLY, 1)
+			| SSC_BF(TCMR_START, SSC_START_RISING_RF)
+			| SSC_BF(TCMR_CKI, SSC_CKI_RISING)
+			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
+			| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
+
+		tfmr =	  SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+			| SSC_BF(TFMR_FSDEN, 0)
+			| SSC_BF(TFMR_FSOS, SSC_FSOS_POSITIVE)
+			| SSC_BF(TFMR_FSLEN, 0)
+			| SSC_BF(TFMR_DATNB, (channels - 1))
+			| SSC_BIT(TFMR_MSBF)
+			| SSC_BF(TFMR_DATDEF, 0)
+			| SSC_BF(TFMR_DATLEN, (bits - 1));
+		break;
+
+	case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM:
+	default:
+		printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x\n",
+			ssc_p->daifmt);
+		return -EINVAL;
+		break;
+	}
+	pr_debug("atmel_ssc_hw_params: "
+			"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x\n",
+			rcmr, rfmr, tcmr, tfmr);
+
+	if (!ssc_p->initialized) {
+
+		/* Enable PMC peripheral clock for this SSC */
+		pr_debug("atmel_ssc_dai: Starting clock\n");
+		clk_enable(ssc_p->ssc->clk);
+
+		/* Reset the SSC and its PDC registers */
+		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
+
+		ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
+
+		ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
+		ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
+
+		ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0,
+				ssc_p->name, ssc_p);
+		if (ret < 0) {
+			printk(KERN_WARNING
+					"atmel_ssc_dai: request_irq failure\n");
+			pr_debug("Atmel_ssc_dai: Stoping clock\n");
+			clk_disable(ssc_p->ssc->clk);
+			return ret;
+		}
+
+		ssc_p->initialized = 1;
+	}
+
+	/* set SSC clock mode register */
+	ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->cmr_div);
+
+	/* set receive clock mode and format */
+	ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
+	ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
+
+	/* set transmit clock mode and format */
+	ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
+	ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
+
+	pr_debug("atmel_ssc_dai,hw_params: SSC initialized\n");
+	return 0;
+}
+
+
+static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct atmel_ssc_info *ssc_p = &ssc_info[rtd->dai->cpu_dai->id];
+	struct atmel_pcm_dma_params *dma_params;
+	int dir;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = 0;
+	else
+		dir = 1;
+
+	dma_params = ssc_p->dma_params[dir];
+
+	ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
+
+	pr_debug("%s enabled SSC_SR=0x%08x\n",
+			dir ? "receive" : "transmit",
+			ssc_readl(ssc_p->ssc->regs, SR));
+	return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
+{
+	struct atmel_ssc_info *ssc_p;
+
+	if (!cpu_dai->active)
+		return 0;
+
+	ssc_p = &ssc_info[cpu_dai->id];
+
+	/* Save the status register before disabling transmit and receive */
+	ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
+	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
+
+	/* Save the current interrupt mask, then disable unmasked interrupts */
+	ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
+	ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
+
+	ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
+	ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
+	ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
+	ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
+	ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
+
+	return 0;
+}
+
+
+
+static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
+{
+	struct atmel_ssc_info *ssc_p;
+	u32 cr;
+
+	if (!cpu_dai->active)
+		return 0;
+
+	ssc_p = &ssc_info[cpu_dai->id];
+
+	/* restore SSC register settings */
+	ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
+	ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
+	ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
+	ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
+	ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
+
+	/* re-enable interrupts */
+	ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
+
+	/* Re-enable recieve and transmit as appropriate */
+	cr = 0;
+	cr |=
+	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
+	cr |=
+	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
+	ssc_writel(ssc_p->ssc->regs, CR, cr);
+
+	return 0;
+}
+#else /* CONFIG_PM */
+#  define atmel_ssc_suspend	NULL
+#  define atmel_ssc_resume	NULL
+#endif /* CONFIG_PM */
+
+
+#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
+			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
+	{	.name = "atmel-ssc0",
+		.id = 0,
+		.suspend = atmel_ssc_suspend,
+		.resume = atmel_ssc_resume,
+		.playback = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ATMEL_SSC_RATES,
+			.formats = ATMEL_SSC_FORMATS,},
+		.capture = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ATMEL_SSC_RATES,
+			.formats = ATMEL_SSC_FORMATS,},
+		.ops = {
+			.startup = atmel_ssc_startup,
+			.shutdown = atmel_ssc_shutdown,
+			.prepare = atmel_ssc_prepare,
+			.hw_params = atmel_ssc_hw_params,
+			.set_fmt = atmel_ssc_set_dai_fmt,
+			.set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+		.private_data = &ssc_info[0],
+	},
+#if NUM_SSC_DEVICES == 3
+	{	.name = "atmel-ssc1",
+		.id = 1,
+		.suspend = atmel_ssc_suspend,
+		.resume = atmel_ssc_resume,
+		.playback = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ATMEL_SSC_RATES,
+			.formats = ATMEL_SSC_FORMATS,},
+		.capture = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ATMEL_SSC_RATES,
+			.formats = ATMEL_SSC_FORMATS,},
+		.ops = {
+			.startup = atmel_ssc_startup,
+			.shutdown = atmel_ssc_shutdown,
+			.prepare = atmel_ssc_prepare,
+			.hw_params = atmel_ssc_hw_params,
+			.set_fmt = atmel_ssc_set_dai_fmt,
+			.set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+		.private_data = &ssc_info[1],
+	},
+	{	.name = "atmel-ssc2",
+		.id = 2,
+		.suspend = atmel_ssc_suspend,
+		.resume = atmel_ssc_resume,
+		.playback = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ATMEL_SSC_RATES,
+			.formats = ATMEL_SSC_FORMATS,},
+		.capture = {
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ATMEL_SSC_RATES,
+			.formats = ATMEL_SSC_FORMATS,},
+		.ops = {
+			.startup = atmel_ssc_startup,
+			.shutdown = atmel_ssc_shutdown,
+			.prepare = atmel_ssc_prepare,
+			.hw_params = atmel_ssc_hw_params,
+			.set_fmt = atmel_ssc_set_dai_fmt,
+			.set_clkdiv = atmel_ssc_set_dai_clkdiv,},
+		.private_data = &ssc_info[2],
+	},
+#endif
+};
+EXPORT_SYMBOL_GPL(atmel_ssc_dai);
+
+static int __init atmel_ssc_modinit(void)
+{
+	return snd_soc_register_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
+}
+module_init(atmel_ssc_modinit);
+
+static void __exit atmel_ssc_modexit(void)
+{
+	snd_soc_unregister_dais(atmel_ssc_dai, ARRAY_SIZE(atmel_ssc_dai));
+}
+module_exit(atmel_ssc_modexit);
+
+/* Module information */
+MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
+MODULE_DESCRIPTION("ATMEL SSC ASoC Interface");
+MODULE_LICENSE("GPL");

+ 121 - 0
sound/soc/atmel/atmel_ssc_dai.h

@@ -0,0 +1,121 @@
+/*
+ * atmel_ssc_dai.h - ALSA SSC interface for the Atmel  SoC
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2008 Atmel
+ *
+ * Author: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *         ATMEL CORP.
+ *
+ * Based on at91-ssc.c by
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Based on pxa2xx Platform drivers by
+ * Liam Girdwood <liam.girdwood@wolfsonmicro.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _ATMEL_SSC_DAI_H
+#define _ATMEL_SSC_DAI_H
+
+#include <linux/types.h>
+#include <linux/atmel-ssc.h>
+
+#include "atmel-pcm.h"
+
+/* SSC system clock ids */
+#define ATMEL_SYSCLK_MCK	0 /* SSC uses AT91 MCK as system clock */
+
+/* SSC divider ids */
+#define ATMEL_SSC_CMR_DIV	0 /* MCK divider for BCLK */
+#define ATMEL_SSC_TCMR_PERIOD	1 /* BCLK divider for transmit FS */
+#define ATMEL_SSC_RCMR_PERIOD	2 /* BCLK divider for receive FS */
+/*
+ * SSC direction masks
+ */
+#define SSC_DIR_MASK_UNUSED	0
+#define SSC_DIR_MASK_PLAYBACK	1
+#define SSC_DIR_MASK_CAPTURE	2
+
+/*
+ * SSC register values that Atmel left out of <linux/atmel-ssc.h>.  These
+ * are expected to be used with SSC_BF
+ */
+/* START bit field values */
+#define SSC_START_CONTINUOUS	0
+#define SSC_START_TX_RX		1
+#define SSC_START_LOW_RF	2
+#define SSC_START_HIGH_RF	3
+#define SSC_START_FALLING_RF	4
+#define SSC_START_RISING_RF	5
+#define SSC_START_LEVEL_RF	6
+#define SSC_START_EDGE_RF	7
+#define SSS_START_COMPARE_0	8
+
+/* CKI bit field values */
+#define SSC_CKI_FALLING		0
+#define SSC_CKI_RISING		1
+
+/* CKO bit field values */
+#define SSC_CKO_NONE		0
+#define SSC_CKO_CONTINUOUS	1
+#define SSC_CKO_TRANSFER	2
+
+/* CKS bit field values */
+#define SSC_CKS_DIV		0
+#define SSC_CKS_CLOCK		1
+#define SSC_CKS_PIN		2
+
+/* FSEDGE bit field values */
+#define SSC_FSEDGE_POSITIVE	0
+#define SSC_FSEDGE_NEGATIVE	1
+
+/* FSOS bit field values */
+#define SSC_FSOS_NONE		0
+#define SSC_FSOS_NEGATIVE	1
+#define SSC_FSOS_POSITIVE	2
+#define SSC_FSOS_LOW		3
+#define SSC_FSOS_HIGH		4
+#define SSC_FSOS_TOGGLE		5
+
+#define START_DELAY		1
+
+struct atmel_ssc_state {
+	u32 ssc_cmr;
+	u32 ssc_rcmr;
+	u32 ssc_rfmr;
+	u32 ssc_tcmr;
+	u32 ssc_tfmr;
+	u32 ssc_sr;
+	u32 ssc_imr;
+};
+
+
+struct atmel_ssc_info {
+	char *name;
+	struct ssc_device *ssc;
+	spinlock_t lock;	/* lock for dir_mask */
+	unsigned short dir_mask;	/* 0=unused, 1=playback, 2=capture */
+	unsigned short initialized;	/* true if SSC has been initialized */
+	unsigned short daifmt;
+	unsigned short cmr_div;
+	unsigned short tcmr_period;
+	unsigned short rcmr_period;
+	struct atmel_pcm_dma_params *dma_params[2];
+	struct atmel_ssc_state ssc_state;
+};
+extern struct snd_soc_dai atmel_ssc_dai[];
+
+#endif /* _AT91_SSC_DAI_H */

+ 5 - 6
sound/soc/at32/playpaq_wm8510.c → sound/soc/atmel/playpaq_wm8510.c

@@ -22,7 +22,6 @@
 
 
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/moduleparam.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/clk.h>
 #include <linux/clk.h>
@@ -40,8 +39,8 @@
 #include <mach/portmux.h>
 #include <mach/portmux.h>
 
 
 #include "../codecs/wm8510.h"
 #include "../codecs/wm8510.h"
-#include "at32-pcm.h"
-#include "at32-ssc.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
 
 
 
 
 /*-------------------------------------------------------------------------*\
 /*-------------------------------------------------------------------------*\
@@ -362,8 +361,9 @@ static struct snd_soc_dai_link playpaq_wm8510_dai = {
 
 
 
 
 
 
-static struct snd_soc_machine snd_soc_machine_playpaq = {
+static struct snd_soc_card snd_soc_playpaq = {
 	.name = "LRS_PlayPaq_WM8510",
 	.name = "LRS_PlayPaq_WM8510",
+	.platform = &at32_soc_platform,
 	.dai_link = &playpaq_wm8510_dai,
 	.dai_link = &playpaq_wm8510_dai,
 	.num_links = 1,
 	.num_links = 1,
 };
 };
@@ -378,8 +378,7 @@ static struct wm8510_setup_data playpaq_wm8510_setup = {
 
 
 
 
 static struct snd_soc_device playpaq_wm8510_snd_devdata = {
 static struct snd_soc_device playpaq_wm8510_snd_devdata = {
-	.machine = &snd_soc_machine_playpaq,
-	.platform = &at32_soc_platform,
+	.card = &snd_soc_playpaq,
 	.codec_dev = &soc_codec_dev_wm8510,
 	.codec_dev = &soc_codec_dev_wm8510,
 	.codec_data = &playpaq_wm8510_setup,
 	.codec_data = &playpaq_wm8510_setup,
 };
 };

+ 328 - 0
sound/soc/atmel/sam9g20_wm8731.c

@@ -0,0 +1,328 @@
+/*
+ * sam9g20_wm8731  --  SoC audio for AT91SAM9G20-based
+ * 			ATMEL AT91SAM9G20ek board.
+ *
+ *  Copyright (C) 2005 SAN People
+ *  Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on ati_b1_wm8731.c by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ * Based on corgi.c by:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+#include "../codecs/wm8731.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+
+static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	int ret;
+
+	/* codec system clock is supplied by PCK0, set to 12MHz */
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
+		12000000, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+	dev_dbg(rtd->socdev->dev, "shutdown");
+}
+
+static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
+	struct ssc_device *ssc = ssc_p->ssc;
+	int ret;
+
+	unsigned int rate;
+	int cmr_div, period;
+
+	if (ssc == NULL) {
+		printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
+		return -EINVAL;
+	}
+
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * The SSC clock dividers depend on the sample rate.  The CMR.DIV
+	 * field divides the system master clock MCK to drive the SSC TK
+	 * signal which provides the codec BCLK.  The TCMR.PERIOD and
+	 * RCMR.PERIOD fields further divide the BCLK signal to drive
+	 * the SSC TF and RF signals which provide the codec DACLRC and
+	 * ADCLRC clocks.
+	 *
+	 * The dividers were determined through trial and error, where a
+	 * CMR.DIV value is chosen such that the resulting BCLK value is
+	 * divisible, or almost divisible, by (2 * sample rate), and then
+	 * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
+	 */
+	rate = params_rate(params);
+
+	switch (rate) {
+	case 8000:
+		cmr_div = 55;	/* BCLK = 133MHz/(2*55) = 1.209MHz */
+		period = 74;	/* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
+		break;
+	case 11025:
+		cmr_div = 67;	/* BCLK = 133MHz/(2*60) = 1.108MHz */
+		period = 45;	/* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
+		break;
+	case 16000:
+		cmr_div = 63;	/* BCLK = 133MHz/(2*63) = 1.055MHz */
+		period = 32;	/* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
+		break;
+	case 22050:
+		cmr_div = 52;	/* BCLK = 133MHz/(2*52) = 1.278MHz */
+		period = 28;	/* LRC = BCLK/(2*(28+1)) = 22049Hz */
+		break;
+	case 32000:
+		cmr_div = 66;	/* BCLK = 133MHz/(2*66) = 1.007MHz */
+		period = 15;	/* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
+		break;
+	case 44100:
+		cmr_div = 29;	/* BCLK = 133MHz/(2*29) = 2.293MHz */
+		period = 25;	/* LRC = BCLK/(2*(25+1)) = 44098Hz */
+		break;
+	case 48000:
+		cmr_div = 33;	/* BCLK = 133MHz/(2*33) = 2.015MHz */
+		period = 20;	/* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
+		break;
+	case 88200:
+		cmr_div = 29;	/* BCLK = 133MHz/(2*29) = 2.293MHz */
+		period = 12;	/* LRC = BCLK/(2*(12+1)) = 88196Hz */
+		break;
+	case 96000:
+		cmr_div = 23;	/* BCLK = 133MHz/(2*23) = 2.891MHz */
+		period = 14;	/* LRC = BCLK/(2*(14+1)) = 96376Hz */
+		break;
+	default:
+		printk(KERN_WARNING "unsupported rate %d"
+				" on at91sam9g20ek board\n", rate);
+		return -EINVAL;
+	}
+
+	/* set the MCK divider for BCLK */
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
+	if (ret < 0)
+		return ret;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* set the BCLK divider for DACLRC */
+		ret = snd_soc_dai_set_clkdiv(cpu_dai,
+						ATMEL_SSC_TCMR_PERIOD, period);
+	} else {
+		/* set the BCLK divider for ADCLRC */
+		ret = snd_soc_dai_set_clkdiv(cpu_dai,
+						ATMEL_SSC_RCMR_PERIOD, period);
+	}
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops at91sam9g20ek_ops = {
+	.startup = at91sam9g20ek_startup,
+	.hw_params = at91sam9g20ek_hw_params,
+	.shutdown = at91sam9g20ek_shutdown,
+};
+
+
+static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
+	SND_SOC_DAPM_MIC("Int Mic", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+
+	/* speaker connected to LHPOUT */
+	{"Ext Spk", NULL, "LHPOUT"},
+
+	/* mic is connected to Mic Jack, with WM8731 Mic Bias */
+	{"MICIN", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Int Mic"},
+};
+
+/*
+ * Logic for a wm8731 as connected on a at91sam9g20ek board.
+ */
+static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
+{
+	printk(KERN_DEBUG
+			"at91sam9g20ek_wm8731 "
+			": at91sam9g20ek_wm8731_init() called\n");
+
+	/* Add specific widgets */
+	snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
+				  ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
+	/* Set up specific audio path interconnects */
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	/* not connected */
+	snd_soc_dapm_disable_pin(codec, "RLINEIN");
+	snd_soc_dapm_disable_pin(codec, "LLINEIN");
+
+	/* always connected */
+	snd_soc_dapm_enable_pin(codec, "Int Mic");
+	snd_soc_dapm_enable_pin(codec, "Ext Spk");
+
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+static struct snd_soc_dai_link at91sam9g20ek_dai = {
+	.name = "WM8731",
+	.stream_name = "WM8731 PCM",
+	.cpu_dai = &atmel_ssc_dai[0],
+	.codec_dai = &wm8731_dai,
+	.init = at91sam9g20ek_wm8731_init,
+	.ops = &at91sam9g20ek_ops,
+};
+
+static struct snd_soc_card snd_soc_at91sam9g20ek = {
+	.name = "WM8731",
+	.platform = &atmel_soc_platform,
+	.dai_link = &at91sam9g20ek_dai,
+	.num_links = 1,
+};
+
+static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = {
+	.i2c_bus = 0,
+	.i2c_address = 0x1b,
+};
+
+static struct snd_soc_device at91sam9g20ek_snd_devdata = {
+	.card = &snd_soc_at91sam9g20ek,
+	.codec_dev = &soc_codec_dev_wm8731,
+	.codec_data = &at91sam9g20ek_wm8731_setup,
+};
+
+static struct platform_device *at91sam9g20ek_snd_device;
+
+static int __init at91sam9g20ek_init(void)
+{
+	struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
+	struct ssc_device *ssc = NULL;
+	int ret;
+
+	/*
+	 * Request SSC device
+	 */
+	ssc = ssc_request(0);
+	if (IS_ERR(ssc)) {
+		ret = PTR_ERR(ssc);
+		ssc = NULL;
+		goto err_ssc;
+	}
+	ssc_p->ssc = ssc;
+
+	at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!at91sam9g20ek_snd_device) {
+		printk(KERN_DEBUG
+				"platform device allocation failed\n");
+		ret = -ENOMEM;
+	}
+
+	platform_set_drvdata(at91sam9g20ek_snd_device,
+			&at91sam9g20ek_snd_devdata);
+	at91sam9g20ek_snd_devdata.dev = &at91sam9g20ek_snd_device->dev;
+
+	ret = platform_device_add(at91sam9g20ek_snd_device);
+	if (ret) {
+		printk(KERN_DEBUG
+				"platform device allocation failed\n");
+		platform_device_put(at91sam9g20ek_snd_device);
+	}
+
+	return ret;
+
+err_ssc:
+	return ret;
+}
+
+static void __exit at91sam9g20ek_exit(void)
+{
+	struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
+	struct ssc_device *ssc;
+
+	if (ssc_p != NULL) {
+		ssc = ssc_p->ssc;
+		if (ssc != NULL)
+			ssc_free(ssc);
+		ssc_p->ssc = NULL;
+	}
+
+	platform_device_unregister(at91sam9g20ek_snd_device);
+	at91sam9g20ek_snd_device = NULL;
+}
+
+module_init(at91sam9g20ek_init);
+module_exit(at91sam9g20ek_exit);
+
+/* Module information */
+MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
+MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
+MODULE_LICENSE("GPL");

+ 2 - 1
sound/soc/au1x/dbdma2.c

@@ -406,11 +406,12 @@ static int __init au1xpsc_audio_dbdma_init(void)
 {
 {
 	au1xpsc_audio_pcmdma[PCM_TX] = NULL;
 	au1xpsc_audio_pcmdma[PCM_TX] = NULL;
 	au1xpsc_audio_pcmdma[PCM_RX] = NULL;
 	au1xpsc_audio_pcmdma[PCM_RX] = NULL;
-	return 0;
+	return snd_soc_register_platform(&au1xpsc_soc_platform);
 }
 }
 
 
 static void __exit au1xpsc_audio_dbdma_exit(void)
 static void __exit au1xpsc_audio_dbdma_exit(void)
 {
 {
+	snd_soc_unregister_platform(&au1xpsc_soc_platform);
 }
 }
 
 
 module_init(au1xpsc_audio_dbdma_init);
 module_init(au1xpsc_audio_dbdma_init);

+ 8 - 8
sound/soc/au1x/psc-ac97.c

@@ -160,7 +160,8 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 
 static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
 static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
-				  struct snd_pcm_hw_params *params)
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
 {
 {
 	/* FIXME */
 	/* FIXME */
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
@@ -210,7 +211,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
 }
 }
 
 
 static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
 static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
-				int cmd)
+				int cmd, struct snd_soc_dai *dai)
 {
 {
 	/* FIXME */
 	/* FIXME */
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
@@ -313,8 +314,7 @@ static void au1xpsc_ac97_remove(struct platform_device *pdev,
 	au1xpsc_ac97_workdata = NULL;
 	au1xpsc_ac97_workdata = NULL;
 }
 }
 
 
-static int au1xpsc_ac97_suspend(struct platform_device *pdev,
-				struct snd_soc_dai *dai)
+static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai)
 {
 {
 	/* save interesting registers and disable PSC */
 	/* save interesting registers and disable PSC */
 	au1xpsc_ac97_workdata->pm[0] =
 	au1xpsc_ac97_workdata->pm[0] =
@@ -328,8 +328,7 @@ static int au1xpsc_ac97_suspend(struct platform_device *pdev,
 	return 0;
 	return 0;
 }
 }
 
 
-static int au1xpsc_ac97_resume(struct platform_device *pdev,
-			       struct snd_soc_dai *dai)
+static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
 {
 {
 	/* restore PSC clock config */
 	/* restore PSC clock config */
 	au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
 	au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
@@ -345,7 +344,7 @@ static int au1xpsc_ac97_resume(struct platform_device *pdev,
 
 
 struct snd_soc_dai au1xpsc_ac97_dai = {
 struct snd_soc_dai au1xpsc_ac97_dai = {
 	.name			= "au1xpsc_ac97",
 	.name			= "au1xpsc_ac97",
-	.type			= SND_SOC_DAI_AC97,
+	.ac97_control		= 1,
 	.probe			= au1xpsc_ac97_probe,
 	.probe			= au1xpsc_ac97_probe,
 	.remove			= au1xpsc_ac97_remove,
 	.remove			= au1xpsc_ac97_remove,
 	.suspend		= au1xpsc_ac97_suspend,
 	.suspend		= au1xpsc_ac97_suspend,
@@ -372,11 +371,12 @@ EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
 static int __init au1xpsc_ac97_init(void)
 static int __init au1xpsc_ac97_init(void)
 {
 {
 	au1xpsc_ac97_workdata = NULL;
 	au1xpsc_ac97_workdata = NULL;
-	return 0;
+	return snd_soc_register_dai(&au1xpsc_ac97_dai);
 }
 }
 
 
 static void __exit au1xpsc_ac97_exit(void)
 static void __exit au1xpsc_ac97_exit(void)
 {
 {
+	snd_soc_unregister_dai(&au1xpsc_ac97_dai);
 }
 }
 
 
 module_init(au1xpsc_ac97_init);
 module_init(au1xpsc_ac97_init);

+ 8 - 10
sound/soc/au1x/psc-i2s.c

@@ -116,7 +116,8 @@ out:
 }
 }
 
 
 static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
 static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream,
-				 struct snd_pcm_hw_params *params)
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
 {
 {
 	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
 	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
 
 
@@ -240,7 +241,8 @@ static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
 	return 0;
 	return 0;
 }
 }
 
 
-static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			       struct snd_soc_dai *dai)
 {
 {
 	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
 	struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata;
 	int ret, stype = SUBSTREAM_TYPE(substream);
 	int ret, stype = SUBSTREAM_TYPE(substream);
@@ -337,8 +339,7 @@ static void au1xpsc_i2s_remove(struct platform_device *pdev,
 	au1xpsc_i2s_workdata = NULL;
 	au1xpsc_i2s_workdata = NULL;
 }
 }
 
 
-static int au1xpsc_i2s_suspend(struct platform_device *pdev,
-			       struct snd_soc_dai *cpu_dai)
+static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai)
 {
 {
 	/* save interesting register and disable PSC */
 	/* save interesting register and disable PSC */
 	au1xpsc_i2s_workdata->pm[0] =
 	au1xpsc_i2s_workdata->pm[0] =
@@ -352,8 +353,7 @@ static int au1xpsc_i2s_suspend(struct platform_device *pdev,
 	return 0;
 	return 0;
 }
 }
 
 
-static int au1xpsc_i2s_resume(struct platform_device *pdev,
-			      struct snd_soc_dai *cpu_dai)
+static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
 {
 {
 	/* select I2S mode and PSC clock */
 	/* select I2S mode and PSC clock */
 	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
 	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
@@ -369,7 +369,6 @@ static int au1xpsc_i2s_resume(struct platform_device *pdev,
 
 
 struct snd_soc_dai au1xpsc_i2s_dai = {
 struct snd_soc_dai au1xpsc_i2s_dai = {
 	.name			= "au1xpsc_i2s",
 	.name			= "au1xpsc_i2s",
-	.type			= SND_SOC_DAI_I2S,
 	.probe			= au1xpsc_i2s_probe,
 	.probe			= au1xpsc_i2s_probe,
 	.remove			= au1xpsc_i2s_remove,
 	.remove			= au1xpsc_i2s_remove,
 	.suspend		= au1xpsc_i2s_suspend,
 	.suspend		= au1xpsc_i2s_suspend,
@@ -389,8 +388,6 @@ struct snd_soc_dai au1xpsc_i2s_dai = {
 	.ops = {
 	.ops = {
 		.trigger	= au1xpsc_i2s_trigger,
 		.trigger	= au1xpsc_i2s_trigger,
 		.hw_params	= au1xpsc_i2s_hw_params,
 		.hw_params	= au1xpsc_i2s_hw_params,
-	},
-	.dai_ops = {
 		.set_fmt	= au1xpsc_i2s_set_fmt,
 		.set_fmt	= au1xpsc_i2s_set_fmt,
 	},
 	},
 };
 };
@@ -399,11 +396,12 @@ EXPORT_SYMBOL(au1xpsc_i2s_dai);
 static int __init au1xpsc_i2s_init(void)
 static int __init au1xpsc_i2s_init(void)
 {
 {
 	au1xpsc_i2s_workdata = NULL;
 	au1xpsc_i2s_workdata = NULL;
-	return 0;
+	return snd_soc_register_dai(&au1xpsc_i2s_dai);
 }
 }
 
 
 static void __exit au1xpsc_i2s_exit(void)
 static void __exit au1xpsc_i2s_exit(void)
 {
 {
+	snd_soc_unregister_dai(&au1xpsc_i2s_dai);
 }
 }
 
 
 module_init(au1xpsc_i2s_init);
 module_init(au1xpsc_i2s_init);

+ 2 - 2
sound/soc/au1x/sample-ac97.c

@@ -42,14 +42,14 @@ static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
 	.ops		= NULL,
 	.ops		= NULL,
 };
 };
 
 
-static struct snd_soc_machine au1xpsc_sample_ac97_machine = {
+static struct snd_soc_card au1xpsc_sample_ac97_machine = {
 	.name		= "Au1xxx PSC AC97 Audio",
 	.name		= "Au1xxx PSC AC97 Audio",
 	.dai_link	= &au1xpsc_sample_ac97_dai,
 	.dai_link	= &au1xpsc_sample_ac97_dai,
 	.num_links	= 1,
 	.num_links	= 1,
 };
 };
 
 
 static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
 static struct snd_soc_device au1xpsc_sample_ac97_devdata = {
-	.machine	= &au1xpsc_sample_ac97_machine,
+	.card		= &au1xpsc_sample_ac97_machine,
 	.platform	= &au1xpsc_soc_platform, /* see dbdma2.c */
 	.platform	= &au1xpsc_soc_platform, /* see dbdma2.c */
 	.codec_dev	= &soc_codec_dev_ac97,
 	.codec_dev	= &soc_codec_dev_ac97,
 };
 };

+ 15 - 7
sound/soc/blackfin/Kconfig

@@ -1,6 +1,6 @@
 config SND_BF5XX_I2S
 config SND_BF5XX_I2S
 	tristate "SoC I2S Audio for the ADI BF5xx chip"
 	tristate "SoC I2S Audio for the ADI BF5xx chip"
-	depends on BLACKFIN && SND_SOC
+	depends on BLACKFIN
 	help
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  Say Y or M if you want to add support for codecs attached to
 	  the Blackfin SPORT (synchronous serial ports) interface in I2S
 	  the Blackfin SPORT (synchronous serial ports) interface in I2S
@@ -13,7 +13,6 @@ config SND_BF5XX_SOC_SSM2602
 	select SND_BF5XX_SOC_I2S
 	select SND_BF5XX_SOC_I2S
 	select SND_SOC_SSM2602
 	select SND_SOC_SSM2602
 	select I2C
 	select I2C
-	select I2C_BLACKFIN_TWI
 	help
 	help
 	  Say Y if you want to add support for SoC audio on BF527-EZKIT.
 	  Say Y if you want to add support for SoC audio on BF527-EZKIT.
 
 
@@ -35,7 +34,7 @@ config SND_BFIN_AD73311_SE
 
 
 config SND_BF5XX_AC97
 config SND_BF5XX_AC97
 	tristate "SoC AC97 Audio for the ADI BF5xx chip"
 	tristate "SoC AC97 Audio for the ADI BF5xx chip"
-	depends on BLACKFIN && SND_SOC
+	depends on BLACKFIN
 	help
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  Say Y or M if you want to add support for codecs attached to
 	  the Blackfin SPORT (synchronous serial ports) interface in slot 16
 	  the Blackfin SPORT (synchronous serial ports) interface in slot 16
@@ -47,7 +46,7 @@ config SND_BF5XX_AC97
 	  properly with this driver. This driver is known to work with the
 	  properly with this driver. This driver is known to work with the
 	  Analog Devices line of AC97 codecs.
 	  Analog Devices line of AC97 codecs.
 
 
-config SND_MMAP_SUPPORT
+config SND_BF5XX_MMAP_SUPPORT
 	bool "Enable MMAP Support"
 	bool "Enable MMAP Support"
 	depends on SND_BF5XX_AC97
 	depends on SND_BF5XX_AC97
 	default y
 	default y
@@ -55,9 +54,17 @@ config SND_MMAP_SUPPORT
 	  Say y if you want AC97 driver to support mmap mode.
 	  Say y if you want AC97 driver to support mmap mode.
 	  We introduce an intermediate buffer to simulate mmap.
 	  We introduce an intermediate buffer to simulate mmap.
 
 
+config SND_BF5XX_MULTICHAN_SUPPORT
+	bool "Enable Multichannel Support"
+	depends on SND_BF5XX_AC97
+	default n
+	help
+	  Say y if you want AC97 driver to support up to 5.1 channel audio.
+	  this mode will consume much more memory for DMA.
+
 config SND_BF5XX_SOC_SPORT
 config SND_BF5XX_SOC_SPORT
 	tristate
 	tristate
-	
+
 config SND_BF5XX_SOC_I2S
 config SND_BF5XX_SOC_I2S
 	tristate
 	tristate
 	select SND_BF5XX_SOC_SPORT
 	select SND_BF5XX_SOC_SPORT
@@ -80,7 +87,7 @@ config SND_BF5XX_SPORT_NUM
 	int "Set a SPORT for Sound chip"
 	int "Set a SPORT for Sound chip"
 	depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
 	depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
 	range 0 3 if BF54x
 	range 0 3 if BF54x
-	range 0 1 if (BF53x || BF561)
+	range 0 1 if !BF54x
 	default 0
 	default 0
 	help
 	help
 	  Set the correct SPORT for sound chip.
 	  Set the correct SPORT for sound chip.
@@ -90,12 +97,13 @@ config SND_BF5XX_HAVE_COLD_RESET
 	depends on SND_BF5XX_AC97
 	depends on SND_BF5XX_AC97
 	default y if BFIN548_EZKIT
 	default y if BFIN548_EZKIT
 	default n if !BFIN548_EZKIT
 	default n if !BFIN548_EZKIT
-	
+
 config SND_BF5XX_RESET_GPIO_NUM
 config SND_BF5XX_RESET_GPIO_NUM
 	int "Set a GPIO for cold reset"
 	int "Set a GPIO for cold reset"
 	depends on SND_BF5XX_HAVE_COLD_RESET
 	depends on SND_BF5XX_HAVE_COLD_RESET
 	range 0 159
 	range 0 159
 	default 19 if BFIN548_EZKIT
 	default 19 if BFIN548_EZKIT
 	default 5 if BFIN537_STAMP
 	default 5 if BFIN537_STAMP
+	default 0
 	help
 	help
 	  Set the correct GPIO for RESET the sound chip.
 	  Set the correct GPIO for RESET the sound chip.

+ 62 - 51
sound/soc/blackfin/bf5xx-ac97-pcm.c

@@ -43,24 +43,34 @@
 #include "bf5xx-ac97.h"
 #include "bf5xx-ac97.h"
 #include "bf5xx-sport.h"
 #include "bf5xx-sport.h"
 
 
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+static unsigned int ac97_chan_mask[] = {
+	SP_FL, /* Mono */
+	SP_STEREO, /* Stereo */
+	SP_2DOT1, /* 2.1*/
+	SP_QUAD,/*Quadraquic*/
+	SP_FL | SP_FR | SP_FC | SP_SL | SP_SR,/*5 channels */
+	SP_5DOT1, /* 5.1 */
+};
+
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
 static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
 	 snd_pcm_uframes_t count)
 	 snd_pcm_uframes_t count)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct sport_device *sport = runtime->private_data;
 	struct sport_device *sport = runtime->private_data;
+	unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		bf5xx_pcm_to_ac97(
-			(struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos,
-			(__u32 *)runtime->dma_area + sport->tx_pos, count);
+		bf5xx_pcm_to_ac97((struct ac97_frame *)sport->tx_dma_buf +
+		sport->tx_pos, (__u16 *)runtime->dma_area + sport->tx_pos *
+		runtime->channels, count, chan_mask);
 		sport->tx_pos += runtime->period_size;
 		sport->tx_pos += runtime->period_size;
 		if (sport->tx_pos >= runtime->buffer_size)
 		if (sport->tx_pos >= runtime->buffer_size)
 			sport->tx_pos %= runtime->buffer_size;
 			sport->tx_pos %= runtime->buffer_size;
 		sport->tx_delay_pos = sport->tx_pos;
 		sport->tx_delay_pos = sport->tx_pos;
 	} else {
 	} else {
-		bf5xx_ac97_to_pcm(
-			(struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
-			(__u32 *)runtime->dma_area + sport->rx_pos, count);
+		bf5xx_ac97_to_pcm((struct ac97_frame *)sport->rx_dma_buf +
+		sport->rx_pos, (__u16 *)runtime->dma_area + sport->rx_pos *
+		runtime->channels, count);
 		sport->rx_pos += runtime->period_size;
 		sport->rx_pos += runtime->period_size;
 		if (sport->rx_pos >= runtime->buffer_size)
 		if (sport->rx_pos >= runtime->buffer_size)
 			sport->rx_pos %= runtime->buffer_size;
 			sport->rx_pos %= runtime->buffer_size;
@@ -71,7 +81,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
 static void bf5xx_dma_irq(void *data)
 static void bf5xx_dma_irq(void *data)
 {
 {
 	struct snd_pcm_substream *pcm = data;
 	struct snd_pcm_substream *pcm = data;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 	struct snd_pcm_runtime *runtime = pcm->runtime;
 	struct snd_pcm_runtime *runtime = pcm->runtime;
 	struct sport_device *sport = runtime->private_data;
 	struct sport_device *sport = runtime->private_data;
 	bf5xx_mmap_copy(pcm, runtime->period_size);
 	bf5xx_mmap_copy(pcm, runtime->period_size);
@@ -90,17 +100,14 @@ static void bf5xx_dma_irq(void *data)
  * The total rx/tx buffer is for ac97 frame to hold all pcm data
  * The total rx/tx buffer is for ac97 frame to hold all pcm data
  * is  0x20000 * sizeof(struct ac97_frame) / 4.
  * is  0x20000 * sizeof(struct ac97_frame) / 4.
  */
  */
-#ifdef CONFIG_SND_MMAP_SUPPORT
 static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
 static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
 	.info			= SNDRV_PCM_INFO_INTERLEAVED |
 	.info			= SNDRV_PCM_INFO_INTERLEAVED |
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 				   SNDRV_PCM_INFO_MMAP |
 				   SNDRV_PCM_INFO_MMAP |
 				   SNDRV_PCM_INFO_MMAP_VALID |
 				   SNDRV_PCM_INFO_MMAP_VALID |
-				   SNDRV_PCM_INFO_BLOCK_TRANSFER,
-#else
-static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
-	.info			= SNDRV_PCM_INFO_INTERLEAVED |
-				  SNDRV_PCM_INFO_BLOCK_TRANSFER,
 #endif
 #endif
+				   SNDRV_PCM_INFO_BLOCK_TRANSFER,
+
 	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
 	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
 	.period_bytes_min	= 32,
 	.period_bytes_min	= 32,
 	.period_bytes_max	= 0x10000,
 	.period_bytes_max	= 0x10000,
@@ -123,10 +130,20 @@ static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
 
 
 static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
 static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
 {
 {
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
 
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-	memset(runtime->dma_area, 0, runtime->buffer_size);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		sport->once = 0;
+		if (runtime->dma_area)
+			memset(runtime->dma_area, 0, runtime->buffer_size);
+		memset(sport->tx_dma_buf, 0, runtime->buffer_size *
+			sizeof(struct ac97_frame));
+	} else
+		memset(sport->rx_dma_buf, 0, runtime->buffer_size *
+			sizeof(struct ac97_frame));
+#endif
 	snd_pcm_lib_free_pages(substream);
 	snd_pcm_lib_free_pages(substream);
 	return 0;
 	return 0;
 }
 }
@@ -139,7 +156,7 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
 	/* An intermediate buffer is introduced for implementing mmap for
 	/* An intermediate buffer is introduced for implementing mmap for
 	 * SPORT working in TMD mode(include AC97).
 	 * SPORT working in TMD mode(include AC97).
 	 */
 	 */
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
 		sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
 		sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
 		sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
@@ -173,24 +190,24 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 	switch (cmd) {
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_START:
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 			bf5xx_mmap_copy(substream, runtime->period_size);
 			bf5xx_mmap_copy(substream, runtime->period_size);
-			snd_pcm_period_elapsed(substream);
 			sport->tx_delay_pos = 0;
 			sport->tx_delay_pos = 0;
+#endif
 			sport_tx_start(sport);
 			sport_tx_start(sport);
-		}
-		else
+		} else
 			sport_rx_start(sport);
 			sport_rx_start(sport);
 		break;
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 			sport->tx_pos = 0;
 			sport->tx_pos = 0;
 #endif
 #endif
 			sport_tx_stop(sport);
 			sport_tx_stop(sport);
 		} else {
 		} else {
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 			sport->rx_pos = 0;
 			sport->rx_pos = 0;
 #endif
 #endif
 			sport_rx_stop(sport);
 			sport_rx_stop(sport);
@@ -208,7 +225,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
 	struct sport_device *sport = runtime->private_data;
 	struct sport_device *sport = runtime->private_data;
 	unsigned int curr;
 	unsigned int curr;
 
 
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		curr = sport->tx_delay_pos;
 		curr = sport->tx_delay_pos;
 	else
 	else
@@ -249,22 +266,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
 	return ret;
 	return ret;
 }
 }
 
 
-static int bf5xx_pcm_close(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct sport_device *sport = runtime->private_data;
-
-	pr_debug("%s enter\n", __func__);
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		sport->once = 0;
-		memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
-	} else
-		memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
-
-	return 0;
-}
-
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
 static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
 	struct vm_area_struct *vma)
 	struct vm_area_struct *vma)
 {
 {
@@ -281,32 +283,29 @@ static	int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
 		    void __user *buf, snd_pcm_uframes_t count)
 		    void __user *buf, snd_pcm_uframes_t count)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-
+	unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
 	pr_debug("%s copy pos:0x%lx count:0x%lx\n",
 	pr_debug("%s copy pos:0x%lx count:0x%lx\n",
 			substream->stream ? "Capture" : "Playback", pos, count);
 			substream->stream ? "Capture" : "Playback", pos, count);
 
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		bf5xx_pcm_to_ac97(
-				(struct ac97_frame *)runtime->dma_area + pos,
-				buf, count);
+		bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos,
+			(__u16 *)buf, count, chan_mask);
 	else
 	else
-		bf5xx_ac97_to_pcm(
-				(struct ac97_frame *)runtime->dma_area + pos,
-				buf, count);
+		bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos,
+			(__u16 *)buf, count);
 	return 0;
 	return 0;
 }
 }
 #endif
 #endif
 
 
 struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
 struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
 	.open		= bf5xx_pcm_open,
 	.open		= bf5xx_pcm_open,
-	.close		= bf5xx_pcm_close,
 	.ioctl		= snd_pcm_lib_ioctl,
 	.ioctl		= snd_pcm_lib_ioctl,
 	.hw_params	= bf5xx_pcm_hw_params,
 	.hw_params	= bf5xx_pcm_hw_params,
 	.hw_free	= bf5xx_pcm_hw_free,
 	.hw_free	= bf5xx_pcm_hw_free,
 	.prepare	= bf5xx_pcm_prepare,
 	.prepare	= bf5xx_pcm_prepare,
 	.trigger	= bf5xx_pcm_trigger,
 	.trigger	= bf5xx_pcm_trigger,
 	.pointer	= bf5xx_pcm_pointer,
 	.pointer	= bf5xx_pcm_pointer,
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 	.mmap		= bf5xx_pcm_mmap,
 	.mmap		= bf5xx_pcm_mmap,
 #else
 #else
 	.copy		= bf5xx_pcm_copy,
 	.copy		= bf5xx_pcm_copy,
@@ -344,7 +343,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
  * Need to allocate local buffer when enable
  * Need to allocate local buffer when enable
  * MMAP for SPORT working in TMD mode (include AC97).
  * MMAP for SPORT working in TMD mode (include AC97).
  */
  */
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		if (!sport_handle->tx_dma_buf) {
 		if (!sport_handle->tx_dma_buf) {
 			sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
 			sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
@@ -381,7 +380,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 	struct snd_pcm_substream *substream;
 	struct snd_pcm_substream *substream;
 	struct snd_dma_buffer *buf;
 	struct snd_dma_buffer *buf;
 	int stream;
 	int stream;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 	size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
 	size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
 		sizeof(struct ac97_frame) / 4;
 		sizeof(struct ac97_frame) / 4;
 #endif
 #endif
@@ -395,7 +394,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 			continue;
 			continue;
 		dma_free_coherent(NULL, buf->bytes, buf->area, 0);
 		dma_free_coherent(NULL, buf->bytes, buf->area, 0);
 		buf->area = NULL;
 		buf->area = NULL;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		if (sport_handle->tx_dma_buf)
 		if (sport_handle->tx_dma_buf)
 			dma_free_coherent(NULL, size, \
 			dma_free_coherent(NULL, size, \
@@ -452,6 +451,18 @@ struct snd_soc_platform bf5xx_ac97_soc_platform = {
 };
 };
 EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
 EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
 
 
+static int __init bfin_ac97_init(void)
+{
+	return snd_soc_register_platform(&bf5xx_ac97_soc_platform);
+}
+module_init(bfin_ac97_init);
+
+static void __exit bfin_ac97_exit(void)
+{
+	snd_soc_unregister_platform(&bf5xx_ac97_soc_platform);
+}
+module_exit(bfin_ac97_exit);
+
 MODULE_AUTHOR("Cliff Cai");
 MODULE_AUTHOR("Cliff Cai");
 MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
 MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 110 - 68
sound/soc/blackfin/bf5xx-ac97.c

@@ -54,71 +54,103 @@
 static int *cmd_count;
 static int *cmd_count;
 static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
 static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
 
 
-#if defined(CONFIG_BF54x)
+static u16 sport_req[][7] = {
+		PIN_REQ_SPORT_0,
+#ifdef PIN_REQ_SPORT_1
+		PIN_REQ_SPORT_1,
+#endif
+#ifdef PIN_REQ_SPORT_2
+		PIN_REQ_SPORT_2,
+#endif
+#ifdef PIN_REQ_SPORT_3
+		PIN_REQ_SPORT_3,
+#endif
+	};
+
 static struct sport_param sport_params[4] = {
 static struct sport_param sport_params[4] = {
 	{
 	{
 		.dma_rx_chan	= CH_SPORT0_RX,
 		.dma_rx_chan	= CH_SPORT0_RX,
 		.dma_tx_chan	= CH_SPORT0_TX,
 		.dma_tx_chan	= CH_SPORT0_TX,
-		.err_irq	= IRQ_SPORT0_ERR,
+		.err_irq	= IRQ_SPORT0_ERROR,
 		.regs		= (struct sport_register *)SPORT0_TCR1,
 		.regs		= (struct sport_register *)SPORT0_TCR1,
 	},
 	},
+#ifdef PIN_REQ_SPORT_1
 	{
 	{
 		.dma_rx_chan	= CH_SPORT1_RX,
 		.dma_rx_chan	= CH_SPORT1_RX,
 		.dma_tx_chan	= CH_SPORT1_TX,
 		.dma_tx_chan	= CH_SPORT1_TX,
-		.err_irq	= IRQ_SPORT1_ERR,
+		.err_irq	= IRQ_SPORT1_ERROR,
 		.regs		= (struct sport_register *)SPORT1_TCR1,
 		.regs		= (struct sport_register *)SPORT1_TCR1,
 	},
 	},
+#endif
+#ifdef PIN_REQ_SPORT_2
 	{
 	{
 		.dma_rx_chan	= CH_SPORT2_RX,
 		.dma_rx_chan	= CH_SPORT2_RX,
 		.dma_tx_chan	= CH_SPORT2_TX,
 		.dma_tx_chan	= CH_SPORT2_TX,
-		.err_irq	= IRQ_SPORT2_ERR,
+		.err_irq	= IRQ_SPORT2_ERROR,
 		.regs		= (struct sport_register *)SPORT2_TCR1,
 		.regs		= (struct sport_register *)SPORT2_TCR1,
 	},
 	},
+#endif
+#ifdef PIN_REQ_SPORT_3
 	{
 	{
 		.dma_rx_chan	= CH_SPORT3_RX,
 		.dma_rx_chan	= CH_SPORT3_RX,
 		.dma_tx_chan	= CH_SPORT3_TX,
 		.dma_tx_chan	= CH_SPORT3_TX,
-		.err_irq	= IRQ_SPORT3_ERR,
+		.err_irq	= IRQ_SPORT3_ERROR,
 		.regs		= (struct sport_register *)SPORT3_TCR1,
 		.regs		= (struct sport_register *)SPORT3_TCR1,
 	}
 	}
-};
-#else
-static struct sport_param sport_params[2] = {
-	{
-		.dma_rx_chan	= CH_SPORT0_RX,
-		.dma_tx_chan	= CH_SPORT0_TX,
-		.err_irq	= IRQ_SPORT0_ERROR,
-		.regs		= (struct sport_register *)SPORT0_TCR1,
-	},
-	{
-		.dma_rx_chan	= CH_SPORT1_RX,
-		.dma_tx_chan	= CH_SPORT1_TX,
-		.err_irq	= IRQ_SPORT1_ERROR,
-		.regs		= (struct sport_register *)SPORT1_TCR1,
-	}
-};
 #endif
 #endif
+};
 
 
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
-		size_t count)
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src,
+		size_t count, unsigned int chan_mask)
 {
 {
 	while (count--) {
 	while (count--) {
-		dst->ac97_tag = TAG_VALID | TAG_PCM;
-		(dst++)->ac97_pcm = *src++;
+		dst->ac97_tag = TAG_VALID;
+		if (chan_mask & SP_FL) {
+			dst->ac97_pcm_r = *src++;
+			dst->ac97_tag |= TAG_PCM_RIGHT;
+		}
+		if (chan_mask & SP_FR) {
+			dst->ac97_pcm_l = *src++;
+			dst->ac97_tag |= TAG_PCM_LEFT;
+
+		}
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+		if (chan_mask & SP_SR) {
+			dst->ac97_sl = *src++;
+			dst->ac97_tag |= TAG_PCM_SL;
+		}
+		if (chan_mask & SP_SL) {
+			dst->ac97_sr = *src++;
+			dst->ac97_tag |= TAG_PCM_SR;
+		}
+		if (chan_mask & SP_LFE) {
+			dst->ac97_lfe = *src++;
+			dst->ac97_tag |= TAG_PCM_LFE;
+		}
+		if (chan_mask & SP_FC) {
+			dst->ac97_center = *src++;
+			dst->ac97_tag |= TAG_PCM_CENTER;
+		}
+#endif
+		dst++;
 	}
 	}
 }
 }
 EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
 EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
 
 
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst,
 		size_t count)
 		size_t count)
 {
 {
-	while (count--)
-		*(dst++) = (src++)->ac97_pcm;
+	while (count--) {
+		*(dst++) = src->ac97_pcm_l;
+		*(dst++) = src->ac97_pcm_r;
+		src++;
+	}
 }
 }
 EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
 EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
 
 
 static unsigned int sport_tx_curr_frag(struct sport_device *sport)
 static unsigned int sport_tx_curr_frag(struct sport_device *sport)
 {
 {
-	return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \
+	return sport->tx_curr_frag = sport_curr_offset_tx(sport) /
 			sport->tx_fragsize;
 			sport->tx_fragsize;
 }
 }
 
 
@@ -130,7 +162,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
 
 
 	sport_incfrag(sport, &nextfrag, 1);
 	sport_incfrag(sport, &nextfrag, 1);
 
 
-	nextwrite = (struct ac97_frame *)(sport->tx_buf + \
+	nextwrite = (struct ac97_frame *)(sport->tx_buf +
 			nextfrag * sport->tx_fragsize);
 			nextfrag * sport->tx_fragsize);
 	pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
 	pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
 		sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
 		sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
@@ -237,8 +269,7 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
-static int bf5xx_ac97_suspend(struct platform_device *pdev,
-	struct snd_soc_dai *dai)
+static int bf5xx_ac97_suspend(struct snd_soc_dai *dai)
 {
 {
 	struct sport_device *sport =
 	struct sport_device *sport =
 		(struct sport_device *)dai->private_data;
 		(struct sport_device *)dai->private_data;
@@ -253,8 +284,7 @@ static int bf5xx_ac97_suspend(struct platform_device *pdev,
 	return 0;
 	return 0;
 }
 }
 
 
-static int bf5xx_ac97_resume(struct platform_device *pdev,
-	struct snd_soc_dai *dai)
+static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
 {
 {
 	int ret;
 	int ret;
 	struct sport_device *sport =
 	struct sport_device *sport =
@@ -297,20 +327,15 @@ static int bf5xx_ac97_resume(struct platform_device *pdev,
 static int bf5xx_ac97_probe(struct platform_device *pdev,
 static int bf5xx_ac97_probe(struct platform_device *pdev,
 			    struct snd_soc_dai *dai)
 			    struct snd_soc_dai *dai)
 {
 {
-	int ret;
-#if defined(CONFIG_BF54x)
-	u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1,
-				 PIN_REQ_SPORT_2, PIN_REQ_SPORT_3};
-#else
-	u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1};
-#endif
+	int ret = 0;
 	cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
 	cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
 	if (cmd_count == NULL)
 	if (cmd_count == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
 	if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
 		pr_err("Requesting Peripherals failed\n");
 		pr_err("Requesting Peripherals failed\n");
-		return -EFAULT;
+		ret =  -EFAULT;
+		goto peripheral_err;
 		}
 		}
 
 
 #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
@@ -318,54 +343,54 @@ static int bf5xx_ac97_probe(struct platform_device *pdev,
 	if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
 	if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
 		pr_err("Failed to request GPIO_%d for reset\n",
 		pr_err("Failed to request GPIO_%d for reset\n",
 				CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 				CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-		peripheral_free_list(&sport_req[sport_num][0]);
-		return -1;
+		ret =  -1;
+		goto gpio_err;
 	}
 	}
 	gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
 	gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
 #endif
 #endif
 	sport_handle = sport_init(&sport_params[sport_num], 2, \
 	sport_handle = sport_init(&sport_params[sport_num], 2, \
 			sizeof(struct ac97_frame), NULL);
 			sizeof(struct ac97_frame), NULL);
 	if (!sport_handle) {
 	if (!sport_handle) {
-		peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
-		return -ENODEV;
+		ret = -ENODEV;
+		goto sport_err;
 	}
 	}
 	/*SPORT works in TDM mode to simulate AC97 transfers*/
 	/*SPORT works in TDM mode to simulate AC97 transfers*/
 	ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
 	ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
 	if (ret) {
 	if (ret) {
 		pr_err("SPORT is busy!\n");
 		pr_err("SPORT is busy!\n");
-		kfree(sport_handle);
-		peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
-		return -EBUSY;
+		ret = -EBUSY;
+		goto sport_config_err;
 	}
 	}
 
 
 	ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
 	ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
 	if (ret) {
 	if (ret) {
 		pr_err("SPORT is busy!\n");
 		pr_err("SPORT is busy!\n");
-		kfree(sport_handle);
-		peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
-		return -EBUSY;
+		ret = -EBUSY;
+		goto sport_config_err;
 	}
 	}
 
 
 	ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
 	ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
 	if (ret) {
 	if (ret) {
 		pr_err("SPORT is busy!\n");
 		pr_err("SPORT is busy!\n");
-		kfree(sport_handle);
-		peripheral_free_list(&sport_req[sport_num][0]);
-#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
-		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
-#endif
-		return -EBUSY;
+		ret = -EBUSY;
+		goto sport_config_err;
 	}
 	}
+
 	return 0;
 	return 0;
+
+sport_config_err:
+	kfree(sport_handle);
+sport_err:
+#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
+	gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
+#endif
+gpio_err:
+	peripheral_free_list(&sport_req[sport_num][0]);
+peripheral_err:
+	free_page((unsigned long)cmd_count);
+	cmd_count = NULL;
+
+	return ret;
 }
 }
 
 
 static void bf5xx_ac97_remove(struct platform_device *pdev,
 static void bf5xx_ac97_remove(struct platform_device *pdev,
@@ -373,6 +398,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
 {
 {
 	free_page((unsigned long)cmd_count);
 	free_page((unsigned long)cmd_count);
 	cmd_count = NULL;
 	cmd_count = NULL;
+	peripheral_free_list(&sport_req[sport_num][0]);
 #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
 	gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 	gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 #endif
 #endif
@@ -381,7 +407,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
 struct snd_soc_dai bfin_ac97_dai = {
 struct snd_soc_dai bfin_ac97_dai = {
 	.name = "bf5xx-ac97",
 	.name = "bf5xx-ac97",
 	.id = 0,
 	.id = 0,
-	.type = SND_SOC_DAI_AC97,
+	.ac97_control = 1,
 	.probe = bf5xx_ac97_probe,
 	.probe = bf5xx_ac97_probe,
 	.remove = bf5xx_ac97_remove,
 	.remove = bf5xx_ac97_remove,
 	.suspend = bf5xx_ac97_suspend,
 	.suspend = bf5xx_ac97_suspend,
@@ -389,7 +415,11 @@ struct snd_soc_dai bfin_ac97_dai = {
 	.playback = {
 	.playback = {
 		.stream_name = "AC97 Playback",
 		.stream_name = "AC97 Playback",
 		.channels_min = 2,
 		.channels_min = 2,
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+		.channels_max = 6,
+#else
 		.channels_max = 2,
 		.channels_max = 2,
+#endif
 		.rates = SNDRV_PCM_RATE_48000,
 		.rates = SNDRV_PCM_RATE_48000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
 		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
 	.capture = {
 	.capture = {
@@ -401,6 +431,18 @@ struct snd_soc_dai bfin_ac97_dai = {
 };
 };
 EXPORT_SYMBOL_GPL(bfin_ac97_dai);
 EXPORT_SYMBOL_GPL(bfin_ac97_dai);
 
 
+static int __init bfin_ac97_init(void)
+{
+	return snd_soc_register_dai(&bfin_ac97_dai);
+}
+module_init(bfin_ac97_init);
+
+static void __exit bfin_ac97_exit(void)
+{
+	snd_soc_unregister_dai(&bfin_ac97_dai);
+}
+module_exit(bfin_ac97_exit);
+
 MODULE_AUTHOR("Roy Huang");
 MODULE_AUTHOR("Roy Huang");
 MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
 MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 30 - 5
sound/soc/blackfin/bf5xx-ac97.h

@@ -16,21 +16,46 @@ struct ac97_frame {
 	u16 ac97_tag;		/* slot 0 */
 	u16 ac97_tag;		/* slot 0 */
 	u16 ac97_addr;		/* slot 1 */
 	u16 ac97_addr;		/* slot 1 */
 	u16 ac97_data;		/* slot 2 */
 	u16 ac97_data;		/* slot 2 */
-	u32 ac97_pcm;		/* slot 3 and 4: left and right pcm data */
+	u16 ac97_pcm_l;		/*slot 3:front left*/
+	u16 ac97_pcm_r;		/*slot 4:front left*/
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+	u16 ac97_mdm_l1;
+	u16 ac97_center;	/*slot 6:center*/
+	u16 ac97_sl;		/*slot 7:surround left*/
+	u16 ac97_sr;		/*slot 8:surround right*/
+	u16 ac97_lfe;		/*slot 9:lfe*/
+#endif
 } __attribute__ ((packed));
 } __attribute__ ((packed));
 
 
+/* Speaker location */
+#define SP_FL		0x0001
+#define SP_FR		0x0010
+#define SP_FC		0x0002
+#define SP_LFE		0x0020
+#define SP_SL		0x0004
+#define SP_SR		0x0040
+
+#define SP_STEREO	(SP_FL | SP_FR)
+#define SP_2DOT1	(SP_FL | SP_FR | SP_LFE)
+#define SP_QUAD		(SP_FL | SP_FR | SP_SL | SP_SR)
+#define SP_5DOT1	(SP_FL | SP_FR | SP_FC | SP_LFE | SP_SL | SP_SR)
+
 #define TAG_VALID		0x8000
 #define TAG_VALID		0x8000
 #define TAG_CMD			0x6000
 #define TAG_CMD			0x6000
 #define TAG_PCM_LEFT		0x1000
 #define TAG_PCM_LEFT		0x1000
 #define TAG_PCM_RIGHT		0x0800
 #define TAG_PCM_RIGHT		0x0800
-#define TAG_PCM			(TAG_PCM_LEFT | TAG_PCM_RIGHT)
+#define TAG_PCM_MDM_L1		0x0400
+#define TAG_PCM_CENTER		0x0200
+#define TAG_PCM_SL		0x0100
+#define TAG_PCM_SR		0x0080
+#define TAG_PCM_LFE		0x0040
 
 
 extern struct snd_soc_dai bfin_ac97_dai;
 extern struct snd_soc_dai bfin_ac97_dai;
 
 
-void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
-		size_t count);
+void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, \
+		size_t count, unsigned int chan_mask);
 
 
-void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
+void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst, \
 		size_t count);
 		size_t count);
 
 
 #endif
 #endif

+ 4 - 4
sound/soc/blackfin/bf5xx-ad1980.c

@@ -43,7 +43,7 @@
 #include "bf5xx-ac97-pcm.h"
 #include "bf5xx-ac97-pcm.h"
 #include "bf5xx-ac97.h"
 #include "bf5xx-ac97.h"
 
 
-static struct snd_soc_machine bf5xx_board;
+static struct snd_soc_card bf5xx_board;
 
 
 static int bf5xx_board_startup(struct snd_pcm_substream *substream)
 static int bf5xx_board_startup(struct snd_pcm_substream *substream)
 {
 {
@@ -67,15 +67,15 @@ static struct snd_soc_dai_link bf5xx_board_dai = {
 	.ops = &bf5xx_board_ops,
 	.ops = &bf5xx_board_ops,
 };
 };
 
 
-static struct snd_soc_machine bf5xx_board = {
+static struct snd_soc_card bf5xx_board = {
 	.name = "bf5xx-board",
 	.name = "bf5xx-board",
+	.platform = &bf5xx_ac97_soc_platform,
 	.dai_link = &bf5xx_board_dai,
 	.dai_link = &bf5xx_board_dai,
 	.num_links = 1,
 	.num_links = 1,
 };
 };
 
 
 static struct snd_soc_device bf5xx_board_snd_devdata = {
 static struct snd_soc_device bf5xx_board_snd_devdata = {
-	.machine = &bf5xx_board,
-	.platform = &bf5xx_ac97_soc_platform,
+	.card = &bf5xx_board,
 	.codec_dev = &soc_codec_dev_ad1980,
 	.codec_dev = &soc_codec_dev_ad1980,
 };
 };
 
 

+ 5 - 5
sound/soc/blackfin/bf5xx-ad73311.c

@@ -65,7 +65,7 @@
 
 
 #define GPIO_SE CONFIG_SND_BFIN_AD73311_SE
 #define GPIO_SE CONFIG_SND_BFIN_AD73311_SE
 
 
-static struct snd_soc_machine bf5xx_ad73311;
+static struct snd_soc_card bf5xx_ad73311;
 
 
 static int snd_ad73311_startup(void)
 static int snd_ad73311_startup(void)
 {
 {
@@ -168,7 +168,7 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
 		params_format(params));
 		params_format(params));
 
 
 	/* set cpu DAI configuration */
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
@@ -190,16 +190,16 @@ static struct snd_soc_dai_link bf5xx_ad73311_dai = {
 	.ops = &bf5xx_ad73311_ops,
 	.ops = &bf5xx_ad73311_ops,
 };
 };
 
 
-static struct snd_soc_machine bf5xx_ad73311 = {
+static struct snd_soc_card bf5xx_ad73311 = {
 	.name = "bf5xx_ad73311",
 	.name = "bf5xx_ad73311",
+	.platform = &bf5xx_i2s_soc_platform,
 	.probe = bf5xx_probe,
 	.probe = bf5xx_probe,
 	.dai_link = &bf5xx_ad73311_dai,
 	.dai_link = &bf5xx_ad73311_dai,
 	.num_links = 1,
 	.num_links = 1,
 };
 };
 
 
 static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
 static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
-	.machine = &bf5xx_ad73311,
-	.platform = &bf5xx_i2s_soc_platform,
+	.card = &bf5xx_ad73311,
 	.codec_dev = &soc_codec_dev_ad73311,
 	.codec_dev = &soc_codec_dev_ad73311,
 };
 };
 
 

+ 12 - 0
sound/soc/blackfin/bf5xx-i2s-pcm.c

@@ -283,6 +283,18 @@ struct snd_soc_platform bf5xx_i2s_soc_platform = {
 };
 };
 EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform);
 EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform);
 
 
+static int __init bfin_i2s_init(void)
+{
+	return snd_soc_register_platform(&bf5xx_i2s_soc_platform);
+}
+module_init(bfin_i2s_init);
+
+static void __exit bfin_i2s_exit(void)
+{
+	snd_soc_unregister_platform(&bf5xx_i2s_soc_platform);
+}
+module_exit(bfin_i2s_exit);
+
 MODULE_AUTHOR("Cliff Cai");
 MODULE_AUTHOR("Cliff Cai");
 MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
 MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 21 - 10
sound/soc/blackfin/bf5xx-i2s.c

@@ -132,7 +132,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 	return ret;
 	return ret;
 }
 }
 
 
-static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
+static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
 {
 {
 	pr_debug("%s enter\n", __func__);
 	pr_debug("%s enter\n", __func__);
 
 
@@ -142,7 +143,8 @@ static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
 }
 }
 
 
 static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
 static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
-				struct snd_pcm_hw_params *params)
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
 {
 {
 	int ret = 0;
 	int ret = 0;
 
 
@@ -193,7 +195,8 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 	return 0;
 }
 }
 
 
-static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream)
+static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
 {
 {
 	pr_debug("%s enter\n", __func__);
 	pr_debug("%s enter\n", __func__);
 	bf5xx_i2s.counter--;
 	bf5xx_i2s.counter--;
@@ -219,16 +222,14 @@ static int bf5xx_i2s_probe(struct platform_device *pdev,
 	return 0;
 	return 0;
 }
 }
 
 
-static void bf5xx_i2s_remove(struct platform_device *pdev,
-			   struct snd_soc_dai *dai)
+static void bf5xx_i2s_remove(struct snd_soc_dai *dai)
 {
 {
 	pr_debug("%s enter\n", __func__);
 	pr_debug("%s enter\n", __func__);
 	peripheral_free_list(&sport_req[sport_num][0]);
 	peripheral_free_list(&sport_req[sport_num][0]);
 }
 }
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
-static int bf5xx_i2s_suspend(struct platform_device *dev,
-			     struct snd_soc_dai *dai)
+static int bf5xx_i2s_suspend(struct snd_soc_dai *dai)
 {
 {
 	struct sport_device *sport =
 	struct sport_device *sport =
 		(struct sport_device *)dai->private_data;
 		(struct sport_device *)dai->private_data;
@@ -289,7 +290,6 @@ static int bf5xx_i2s_resume(struct platform_device *pdev,
 struct snd_soc_dai bf5xx_i2s_dai = {
 struct snd_soc_dai bf5xx_i2s_dai = {
 	.name = "bf5xx-i2s",
 	.name = "bf5xx-i2s",
 	.id = 0,
 	.id = 0,
-	.type = SND_SOC_DAI_I2S,
 	.probe = bf5xx_i2s_probe,
 	.probe = bf5xx_i2s_probe,
 	.remove = bf5xx_i2s_remove,
 	.remove = bf5xx_i2s_remove,
 	.suspend = bf5xx_i2s_suspend,
 	.suspend = bf5xx_i2s_suspend,
@@ -307,13 +307,24 @@ struct snd_soc_dai bf5xx_i2s_dai = {
 	.ops = {
 	.ops = {
 		.startup   = bf5xx_i2s_startup,
 		.startup   = bf5xx_i2s_startup,
 		.shutdown  = bf5xx_i2s_shutdown,
 		.shutdown  = bf5xx_i2s_shutdown,
-		.hw_params = bf5xx_i2s_hw_params,},
-	.dai_ops = {
+		.hw_params = bf5xx_i2s_hw_params,
 		.set_fmt = bf5xx_i2s_set_dai_fmt,
 		.set_fmt = bf5xx_i2s_set_dai_fmt,
 	},
 	},
 };
 };
 EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
 EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
 
 
+static int __init bfin_i2s_init(void)
+{
+	return snd_soc_register_dai(&bf5xx_i2s_dai);
+}
+module_init(bfin_i2s_init);
+
+static void __exit bfin_i2s_exit(void)
+{
+	snd_soc_unregister_dai(&bf5xx_i2s_dai);
+}
+module_exit(bfin_i2s_exit);
+
 /* Module information */
 /* Module information */
 MODULE_AUTHOR("Cliff Cai");
 MODULE_AUTHOR("Cliff Cai");
 MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
 MODULE_DESCRIPTION("I2S driver for ADI Blackfin");

+ 1 - 1
sound/soc/blackfin/bf5xx-sport.h

@@ -116,7 +116,7 @@ struct sport_device {
 	void *err_data;
 	void *err_data;
 	unsigned char *tx_dma_buf;
 	unsigned char *tx_dma_buf;
 	unsigned char *rx_dma_buf;
 	unsigned char *rx_dma_buf;
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#ifdef CONFIG_SND_BF5XX_MMAP_SUPPORT
 	dma_addr_t tx_dma_phy;
 	dma_addr_t tx_dma_phy;
 	dma_addr_t rx_dma_phy;
 	dma_addr_t rx_dma_phy;
 	int tx_pos;/*pcm sample count*/
 	int tx_pos;/*pcm sample count*/

+ 7 - 7
sound/soc/blackfin/bf5xx-ssm2602.c

@@ -44,7 +44,7 @@
 #include "bf5xx-i2s-pcm.h"
 #include "bf5xx-i2s-pcm.h"
 #include "bf5xx-i2s.h"
 #include "bf5xx-i2s.h"
 
 
-static struct snd_soc_machine bf5xx_ssm2602;
+static struct snd_soc_card bf5xx_ssm2602;
 
 
 static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
 static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
 {
 {
@@ -92,17 +92,17 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
 	 */
 	 */
 
 
 	/* set codec DAI configuration */
 	/* set codec DAI configuration */
-	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 	/* set cpu DAI configuration */
 	/* set cpu DAI configuration */
-	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
-	ret = codec_dai->dai_ops.set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
+	ret = snd_soc_dai_set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
 		SND_SOC_CLOCK_IN);
 		SND_SOC_CLOCK_IN);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
@@ -135,15 +135,15 @@ static struct ssm2602_setup_data bf5xx_ssm2602_setup = {
 	.i2c_address = 0x1b,
 	.i2c_address = 0x1b,
 };
 };
 
 
-static struct snd_soc_machine bf5xx_ssm2602 = {
+static struct snd_soc_card bf5xx_ssm2602 = {
 	.name = "bf5xx_ssm2602",
 	.name = "bf5xx_ssm2602",
+	.platform = &bf5xx_i2s_soc_platform,
 	.dai_link = &bf5xx_ssm2602_dai,
 	.dai_link = &bf5xx_ssm2602_dai,
 	.num_links = 1,
 	.num_links = 1,
 };
 };
 
 
 static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
 static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
-	.machine = &bf5xx_ssm2602,
-	.platform = &bf5xx_i2s_soc_platform,
+	.card = &bf5xx_ssm2602,
 	.codec_dev = &soc_codec_dev_ssm2602,
 	.codec_dev = &soc_codec_dev_ssm2602,
 	.codec_data = &bf5xx_ssm2602_setup,
 	.codec_data = &bf5xx_ssm2602_setup,
 };
 };

+ 49 - 20
sound/soc/codecs/Kconfig

@@ -1,31 +1,40 @@
 config SND_SOC_ALL_CODECS
 config SND_SOC_ALL_CODECS
 	tristate "Build all ASoC CODEC drivers"
 	tristate "Build all ASoC CODEC drivers"
-	depends on I2C
-	select SPI
-	select SPI_MASTER
-	select SND_SOC_AD73311
-	select SND_SOC_AK4535
-	select SND_SOC_CS4270
-	select SND_SOC_SSM2602
-	select SND_SOC_TLV320AIC23
-	select SND_SOC_TLV320AIC26
-	select SND_SOC_TLV320AIC3X
-	select SND_SOC_UDA1380
-	select SND_SOC_WM8510
-	select SND_SOC_WM8580
-	select SND_SOC_WM8731
-	select SND_SOC_WM8750
-	select SND_SOC_WM8753
-	select SND_SOC_WM8900
-	select SND_SOC_WM8903
-	select SND_SOC_WM8971
-	select SND_SOC_WM8990
+	select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
+	select SND_SOC_AD1980 if SND_SOC_AC97_BUS
+	select SND_SOC_AD73311 if I2C
+	select SND_SOC_AK4535 if I2C
+	select SND_SOC_CS4270 if I2C
+	select SND_SOC_PCM3008
+	select SND_SOC_SSM2602 if I2C
+	select SND_SOC_TLV320AIC23 if I2C
+	select SND_SOC_TLV320AIC26 if SPI_MASTER
+	select SND_SOC_TLV320AIC3X if I2C
+	select SND_SOC_TWL4030 if TWL4030_CORE
+	select SND_SOC_UDA134X
+	select SND_SOC_UDA1380 if I2C
+	select SND_SOC_WM8350 if MFD_WM8350
+	select SND_SOC_WM8510 if (I2C || SPI_MASTER)
+	select SND_SOC_WM8580 if I2C
+	select SND_SOC_WM8728 if (I2C || SPI_MASTER)
+	select SND_SOC_WM8731 if (I2C || SPI_MASTER)
+	select SND_SOC_WM8750 if (I2C || SPI_MASTER)
+	select SND_SOC_WM8753 if (I2C || SPI_MASTER)
+	select SND_SOC_WM8900 if I2C
+	select SND_SOC_WM8903 if I2C
+	select SND_SOC_WM8971 if I2C
+	select SND_SOC_WM8990 if I2C
+	select SND_SOC_WM9712 if SND_SOC_AC97_BUS
+	select SND_SOC_WM9713 if SND_SOC_AC97_BUS
         help
         help
           Normally ASoC codec drivers are only built if a machine driver which
           Normally ASoC codec drivers are only built if a machine driver which
           uses them is also built since they are only usable with a machine
           uses them is also built since they are only usable with a machine
           driver.  Selecting this option will allow these drivers to be built
           driver.  Selecting this option will allow these drivers to be built
           without an explicit machine driver for test and development purposes.
           without an explicit machine driver for test and development purposes.
 
 
+	  Support for the bus types used to access the codecs to be built must
+	  be selected separately.
+
           If unsure select "N".
           If unsure select "N".
 
 
 
 
@@ -60,6 +69,12 @@ config SND_SOC_CS4270_VD33_ERRATA
 	bool
 	bool
 	depends on SND_SOC_CS4270
 	depends on SND_SOC_CS4270
 
 
+config SND_SOC_L3
+       tristate
+
+config SND_SOC_PCM3008
+       tristate
+
 config SND_SOC_SSM2602
 config SND_SOC_SSM2602
 	tristate
 	tristate
 
 
@@ -75,15 +90,29 @@ config SND_SOC_TLV320AIC3X
 	tristate
 	tristate
 	depends on I2C
 	depends on I2C
 
 
+config SND_SOC_TWL4030
+	tristate
+	depends on TWL4030_CORE
+
+config SND_SOC_UDA134X
+       tristate
+       select SND_SOC_L3
+
 config SND_SOC_UDA1380
 config SND_SOC_UDA1380
         tristate
         tristate
 
 
+config SND_SOC_WM8350
+	tristate
+
 config SND_SOC_WM8510
 config SND_SOC_WM8510
 	tristate
 	tristate
 
 
 config SND_SOC_WM8580
 config SND_SOC_WM8580
 	tristate
 	tristate
 
 
+config SND_SOC_WM8728
+	tristate
+
 config SND_SOC_WM8731
 config SND_SOC_WM8731
 	tristate
 	tristate
 
 

+ 12 - 0
sound/soc/codecs/Makefile

@@ -3,13 +3,19 @@ snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cs4270-objs := cs4270.o
+snd-soc-l3-objs := l3.o
+snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-twl4030-objs := twl4030.o
+snd-soc-uda134x-objs := uda134x.o
 snd-soc-uda1380-objs := uda1380.o
 snd-soc-uda1380-objs := uda1380.o
+snd-soc-wm8350-objs := wm8350.o
 snd-soc-wm8510-objs := wm8510.o
 snd-soc-wm8510-objs := wm8510.o
 snd-soc-wm8580-objs := wm8580.o
 snd-soc-wm8580-objs := wm8580.o
+snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
 snd-soc-wm8753-objs := wm8753.o
@@ -25,13 +31,19 @@ obj-$(CONFIG_SND_SOC_AD1980)	+= snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
+obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)	+= snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)	+= snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TWL4030)	+= snd-soc-twl4030.o
+obj-$(CONFIG_SND_SOC_UDA134X)	+= snd-soc-uda134x.o
 obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
 obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WM8350)	+= snd-soc-wm8350.o
 obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o
 obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o
 obj-$(CONFIG_SND_SOC_WM8580)	+= snd-soc-wm8580.o
 obj-$(CONFIG_SND_SOC_WM8580)	+= snd-soc-wm8580.o
+obj-$(CONFIG_SND_SOC_WM8728)	+= snd-soc-wm8728.o
 obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o
 obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o

+ 4 - 3
sound/soc/codecs/ac97.c

@@ -24,7 +24,8 @@
 
 
 #define AC97_VERSION "0.6"
 #define AC97_VERSION "0.6"
 
 
-static int ac97_prepare(struct snd_pcm_substream *substream)
+static int ac97_prepare(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -42,7 +43,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
 
 
 struct snd_soc_dai ac97_dai = {
 struct snd_soc_dai ac97_dai = {
 	.name = "AC97 HiFi",
 	.name = "AC97 HiFi",
-	.type = SND_SOC_DAI_AC97,
+	.ac97_control = 1,
 	.playback = {
 	.playback = {
 		.stream_name = "AC97 Playback",
 		.stream_name = "AC97 Playback",
 		.channels_min = 1,
 		.channels_min = 1,
@@ -113,7 +114,7 @@ static int ac97_soc_probe(struct platform_device *pdev)
 	if (ret < 0)
 	if (ret < 0)
 		goto bus_err;
 		goto bus_err;
 
 
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0)
 	if (ret < 0)
 		goto bus_err;
 		goto bus_err;
 	return 0;
 	return 0;

+ 18 - 6
sound/soc/codecs/ad1980.c

@@ -85,6 +85,9 @@ SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0),
 SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
 SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
 SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
 SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
 
 
+SOC_DOUBLE("Center/LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE("Center/LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 7, 1, 1),
+
 SOC_ENUM("Capture Source", ad1980_cap_src),
 SOC_ENUM("Capture Source", ad1980_cap_src),
 
 
 SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
 SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
@@ -142,10 +145,11 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 
 
 struct snd_soc_dai ad1980_dai = {
 struct snd_soc_dai ad1980_dai = {
 	.name = "AC97",
 	.name = "AC97",
+	.ac97_control = 1,
 	.playback = {
 	.playback = {
 		.stream_name = "Playback",
 		.stream_name = "Playback",
 		.channels_min = 2,
 		.channels_min = 2,
-		.channels_max = 2,
+		.channels_max = 6,
 		.rates = SNDRV_PCM_RATE_48000,
 		.rates = SNDRV_PCM_RATE_48000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
 		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
 	.capture = {
 	.capture = {
@@ -192,6 +196,7 @@ static int ad1980_soc_probe(struct platform_device *pdev)
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec *codec;
 	int ret = 0;
 	int ret = 0;
 	u16 vendor_id2;
 	u16 vendor_id2;
+	u16 ext_status;
 
 
 	printk(KERN_INFO "AD1980 SoC Audio Codec\n");
 	printk(KERN_INFO "AD1980 SoC Audio Codec\n");
 
 
@@ -234,7 +239,7 @@ static int ad1980_soc_probe(struct platform_device *pdev)
 
 
 	ret = ad1980_reset(codec, 0);
 	ret = ad1980_reset(codec, 0);
 	if (ret < 0) {
 	if (ret < 0) {
-		printk(KERN_ERR "AC97 link error\n");
+		printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
 		goto reset_err;
 		goto reset_err;
 	}
 	}
 
 
@@ -253,12 +258,19 @@ static int ad1980_soc_probe(struct platform_device *pdev)
 				"supported\n");
 				"supported\n");
 	}
 	}
 
 
-	ac97_write(codec, AC97_MASTER, 0x0000); /* unmute line out volume */
-	ac97_write(codec, AC97_PCM, 0x0000);	/* unmute PCM out volume */
-	ac97_write(codec, AC97_REC_GAIN, 0x0000);/* unmute record volume */
+	/* unmute captures and playbacks volume */
+	ac97_write(codec, AC97_MASTER, 0x0000);
+	ac97_write(codec, AC97_PCM, 0x0000);
+	ac97_write(codec, AC97_REC_GAIN, 0x0000);
+	ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
+	ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
+
+	/*power on LFE/CENTER/Surround DACs*/
+	ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
+	ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
 
 
 	ad1980_add_controls(codec);
 	ad1980_add_controls(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "ad1980: failed to register card\n");
 		printk(KERN_ERR "ad1980: failed to register card\n");
 		goto reset_err;
 		goto reset_err;

+ 13 - 5
sound/soc/codecs/ad73311.c

@@ -8,14 +8,10 @@
  *  under  the terms of  the GNU General  Public License as published by the
  *  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
  *  Free Software Foundation;  either version 2 of the  License, or (at your
  *  option) any later version.
  *  option) any later version.
- *
- *  Revision history
- *    25th Sep 2008   Initial version.
  */
  */
 
 
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include <linux/device.h>
 #include <sound/core.h>
 #include <sound/core.h>
@@ -68,7 +64,7 @@ static int ad73311_soc_probe(struct platform_device *pdev)
 		goto pcm_err;
 		goto pcm_err;
 	}
 	}
 
 
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "ad73311: failed to register card\n");
 		printk(KERN_ERR "ad73311: failed to register card\n");
 		goto register_err;
 		goto register_err;
@@ -102,6 +98,18 @@ struct snd_soc_codec_device soc_codec_dev_ad73311 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311);
 EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311);
 
 
+static int __init ad73311_init(void)
+{
+	return snd_soc_register_dai(&ad73311_dai);
+}
+module_init(ad73311_init);
+
+static void __exit ad73311_exit(void)
+{
+	snd_soc_unregister_dai(&ad73311_dai);
+}
+module_exit(ad73311_exit);
+
 MODULE_DESCRIPTION("ASoC ad73311 driver");
 MODULE_DESCRIPTION("ASoC ad73311 driver");
 MODULE_AUTHOR("Cliff Cai ");
 MODULE_AUTHOR("Cliff Cai ");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 15 - 4
sound/soc/codecs/ak4535.c

@@ -339,7 +339,8 @@ static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 }
 }
 
 
 static int ak4535_hw_params(struct snd_pcm_substream *substream,
 static int ak4535_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -451,8 +452,6 @@ struct snd_soc_dai ak4535_dai = {
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
 	.ops = {
 	.ops = {
 		.hw_params = ak4535_hw_params,
 		.hw_params = ak4535_hw_params,
-	},
-	.dai_ops = {
 		.set_fmt = ak4535_set_dai_fmt,
 		.set_fmt = ak4535_set_dai_fmt,
 		.digital_mute = ak4535_mute,
 		.digital_mute = ak4535_mute,
 		.set_sysclk = ak4535_set_dai_sysclk,
 		.set_sysclk = ak4535_set_dai_sysclk,
@@ -513,7 +512,7 @@ static int ak4535_init(struct snd_soc_device *socdev)
 
 
 	ak4535_add_controls(codec);
 	ak4535_add_controls(codec);
 	ak4535_add_widgets(codec);
 	ak4535_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "ak4535: failed to register card\n");
 		printk(KERN_ERR "ak4535: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -689,6 +688,18 @@ struct snd_soc_codec_device soc_codec_dev_ak4535 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
 EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
 
 
+static int __init ak4535_modinit(void)
+{
+	return snd_soc_register_dai(&ak4535_dai);
+}
+module_init(ak4535_modinit);
+
+static void __exit ak4535_exit(void)
+{
+	snd_soc_unregister_dai(&ak4535_dai);
+}
+module_exit(ak4535_exit);
+
 MODULE_DESCRIPTION("Soc AK4535 driver");
 MODULE_DESCRIPTION("Soc AK4535 driver");
 MODULE_AUTHOR("Richard Purdie");
 MODULE_AUTHOR("Richard Purdie");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 32 - 6
sound/soc/codecs/cs4270.c

@@ -360,13 +360,14 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
 /*
 /*
  * Program the CS4270 with the given hardware parameters.
  * Program the CS4270 with the given hardware parameters.
  *
  *
- * The .dai_ops functions are used to provide board-specific data, like
+ * The .ops functions are used to provide board-specific data, like
  * input frequencies, to this driver.  This function takes that information,
  * input frequencies, to this driver.  This function takes that information,
  * combines it with the hardware parameters provided, and programs the
  * combines it with the hardware parameters provided, and programs the
  * hardware accordingly.
  * hardware accordingly.
  */
  */
 static int cs4270_hw_params(struct snd_pcm_substream *substream,
 static int cs4270_hw_params(struct snd_pcm_substream *substream,
-			    struct snd_pcm_hw_params *params)
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -450,6 +451,19 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
 		return ret;
 		return ret;
 	}
 	}
 
 
+	/* Disable automatic volume control.  It's enabled by default, and
+	 * it causes volume change commands to be delayed, sometimes until
+	 * after playback has started.
+	 */
+
+	reg = cs4270_read_reg_cache(codec, CS4270_TRANS);
+	reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
+	ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
+	if (ret < 0) {
+		printk(KERN_ERR "I2C write failed\n");
+		return ret;
+	}
+
 	/* Thaw and power-up the codec */
 	/* Thaw and power-up the codec */
 
 
 	ret = snd_soc_write(codec, CS4270_PWRCTL, 0);
 	ret = snd_soc_write(codec, CS4270_PWRCTL, 0);
@@ -697,10 +711,10 @@ static int cs4270_probe(struct platform_device *pdev)
 	if (codec->control_data) {
 	if (codec->control_data) {
 		/* Initialize codec ops */
 		/* Initialize codec ops */
 		cs4270_dai.ops.hw_params = cs4270_hw_params;
 		cs4270_dai.ops.hw_params = cs4270_hw_params;
-		cs4270_dai.dai_ops.set_sysclk = cs4270_set_dai_sysclk;
-		cs4270_dai.dai_ops.set_fmt = cs4270_set_dai_fmt;
+		cs4270_dai.ops.set_sysclk = cs4270_set_dai_sysclk;
+		cs4270_dai.ops.set_fmt = cs4270_set_dai_fmt;
 #ifdef CONFIG_SND_SOC_CS4270_HWMUTE
 #ifdef CONFIG_SND_SOC_CS4270_HWMUTE
-		cs4270_dai.dai_ops.digital_mute = cs4270_mute;
+		cs4270_dai.ops.digital_mute = cs4270_mute;
 #endif
 #endif
 	} else
 	} else
 		printk(KERN_INFO "cs4270: no I2C device found, "
 		printk(KERN_INFO "cs4270: no I2C device found, "
@@ -709,7 +723,7 @@ static int cs4270_probe(struct platform_device *pdev)
 	printk(KERN_INFO "cs4270: I2C disabled, using stand-alone mode\n");
 	printk(KERN_INFO "cs4270: I2C disabled, using stand-alone mode\n");
 #endif
 #endif
 
 
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "cs4270: failed to register card\n");
 		printk(KERN_ERR "cs4270: failed to register card\n");
 		goto error_del_driver;
 		goto error_del_driver;
@@ -760,6 +774,18 @@ struct snd_soc_codec_device soc_codec_device_cs4270 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
 EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
 
 
+static int __init cs4270_init(void)
+{
+	return snd_soc_register_dai(&cs4270_dai);
+}
+module_init(cs4270_init);
+
+static void __exit cs4270_exit(void)
+{
+	snd_soc_unregister_dai(&cs4270_dai);
+}
+module_exit(cs4270_exit);
+
 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
 MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver");
 MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 91 - 0
sound/soc/codecs/l3.c

@@ -0,0 +1,91 @@
+/*
+ * L3 code
+ *
+ *  Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org>
+ *
+ * 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.
+ *
+ *
+ * based on:
+ *
+ * L3 bus algorithm module.
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include <sound/l3.h>
+
+/*
+ * Send one byte of data to the chip.  Data is latched into the chip on
+ * the rising edge of the clock.
+ */
+static void sendbyte(struct l3_pins *adap, unsigned int byte)
+{
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		adap->setclk(0);
+		udelay(adap->data_hold);
+		adap->setdat(byte & 1);
+		udelay(adap->data_setup);
+		adap->setclk(1);
+		udelay(adap->clock_high);
+		byte >>= 1;
+	}
+}
+
+/*
+ * Send a set of bytes to the chip.  We need to pulse the MODE line
+ * between each byte, but never at the start nor at the end of the
+ * transfer.
+ */
+static void sendbytes(struct l3_pins *adap, const u8 *buf,
+		      int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (i) {
+			udelay(adap->mode_hold);
+			adap->setmode(0);
+			udelay(adap->mode);
+		}
+		adap->setmode(1);
+		udelay(adap->mode_setup);
+		sendbyte(adap, buf[i]);
+	}
+}
+
+int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
+{
+	adap->setclk(1);
+	adap->setdat(1);
+	adap->setmode(1);
+	udelay(adap->mode);
+
+	adap->setmode(0);
+	udelay(adap->mode_setup);
+	sendbyte(adap, addr);
+	udelay(adap->mode_hold);
+
+	sendbytes(adap, data, len);
+
+	adap->setclk(1);
+	adap->setdat(1);
+	adap->setmode(0);
+
+	return len;
+}
+EXPORT_SYMBOL_GPL(l3_write);
+
+MODULE_DESCRIPTION("L3 bit-banging driver");
+MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");

+ 212 - 0
sound/soc/codecs/pcm3008.c

@@ -0,0 +1,212 @@
+/*
+ * ALSA Soc PCM3008 codec support
+ *
+ * Author:	Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * Based on AC97 Soc codec, original copyright follow:
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ *
+ *  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.
+ *
+ * Generic PCM3008 support.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "pcm3008.h"
+
+#define PCM3008_VERSION "0.2"
+
+#define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |	\
+		       SNDRV_PCM_RATE_48000)
+
+struct snd_soc_dai pcm3008_dai = {
+	.name = "PCM3008 HiFi",
+	.playback = {
+		.stream_name = "PCM3008 Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = PCM3008_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "PCM3008 Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = PCM3008_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+};
+EXPORT_SYMBOL_GPL(pcm3008_dai);
+
+static void pcm3008_gpio_free(struct pcm3008_setup_data *setup)
+{
+	gpio_free(setup->dem0_pin);
+	gpio_free(setup->dem1_pin);
+	gpio_free(setup->pdad_pin);
+	gpio_free(setup->pdda_pin);
+}
+
+static int pcm3008_soc_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct pcm3008_setup_data *setup = socdev->codec_data;
+	int ret = 0;
+
+	printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
+
+	socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (!socdev->codec)
+		return -ENOMEM;
+
+	codec = socdev->codec;
+	mutex_init(&codec->mutex);
+
+	codec->name = "PCM3008";
+	codec->owner = THIS_MODULE;
+	codec->dai = &pcm3008_dai;
+	codec->num_dai = 1;
+	codec->write = NULL;
+	codec->read = NULL;
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	/* Register PCMs. */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "pcm3008: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* Register Card. */
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "pcm3008: failed to register card\n");
+		goto card_err;
+	}
+
+	/* DEM1  DEM0  DE-EMPHASIS_MODE
+	 * Low   Low   De-emphasis 44.1 kHz ON
+	 * Low   High  De-emphasis OFF
+	 * High  Low   De-emphasis 48 kHz ON
+	 * High  High  De-emphasis 32 kHz ON
+	 */
+
+	/* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
+	ret = gpio_request(setup->dem0_pin, "codec_dem0");
+	if (ret == 0)
+		ret = gpio_direction_output(setup->dem0_pin, 1);
+	if (ret != 0)
+		goto gpio_err;
+
+	/* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
+	ret = gpio_request(setup->dem1_pin, "codec_dem1");
+	if (ret == 0)
+		ret = gpio_direction_output(setup->dem1_pin, 0);
+	if (ret != 0)
+		goto gpio_err;
+
+	/* Configure PDAD GPIO. */
+	ret = gpio_request(setup->pdad_pin, "codec_pdad");
+	if (ret == 0)
+		ret = gpio_direction_output(setup->pdad_pin, 1);
+	if (ret != 0)
+		goto gpio_err;
+
+	/* Configure PDDA GPIO. */
+	ret = gpio_request(setup->pdda_pin, "codec_pdda");
+	if (ret == 0)
+		ret = gpio_direction_output(setup->pdda_pin, 1);
+	if (ret != 0)
+		goto gpio_err;
+
+	return ret;
+
+gpio_err:
+	pcm3008_gpio_free(setup);
+card_err:
+	snd_soc_free_pcms(socdev);
+pcm_err:
+	kfree(socdev->codec);
+
+	return ret;
+}
+
+static int pcm3008_soc_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	struct pcm3008_setup_data *setup = socdev->codec_data;
+
+	if (!codec)
+		return 0;
+
+	pcm3008_gpio_free(setup);
+	snd_soc_free_pcms(socdev);
+	kfree(socdev->codec);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcm3008_soc_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct pcm3008_setup_data *setup = socdev->codec_data;
+
+	gpio_set_value(setup->pdad_pin, 0);
+	gpio_set_value(setup->pdda_pin, 0);
+
+	return 0;
+}
+
+static int pcm3008_soc_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct pcm3008_setup_data *setup = socdev->codec_data;
+
+	gpio_set_value(setup->pdad_pin, 1);
+	gpio_set_value(setup->pdda_pin, 1);
+
+	return 0;
+}
+#else
+#define pcm3008_soc_suspend NULL
+#define pcm3008_soc_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_pcm3008 = {
+	.probe = 	pcm3008_soc_probe,
+	.remove = 	pcm3008_soc_remove,
+	.suspend =	pcm3008_soc_suspend,
+	.resume =	pcm3008_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_pcm3008);
+
+static int __init pcm3008_init(void)
+{
+	return snd_soc_register_dai(&pcm3008_dai);
+}
+module_init(pcm3008_init);
+
+static void __exit pcm3008_exit(void)
+{
+	snd_soc_unregister_dai(&pcm3008_dai);
+}
+module_exit(pcm3008_exit);
+
+MODULE_DESCRIPTION("Soc PCM3008 driver");
+MODULE_AUTHOR("Hugo Villeneuve");
+MODULE_LICENSE("GPL");

+ 25 - 0
sound/soc/codecs/pcm3008.h

@@ -0,0 +1,25 @@
+/*
+ * PCM3008 ALSA SoC Layer
+ *
+ * Author:	Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * 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 __LINUX_SND_SOC_PCM3008_H
+#define __LINUX_SND_SOC_PCM3008_H
+
+struct pcm3008_setup_data {
+	unsigned dem0_pin;
+	unsigned dem1_pin;
+	unsigned pdad_pin;
+	unsigned pdda_pin;
+};
+
+extern struct snd_soc_codec_device soc_codec_dev_pcm3008;
+extern struct snd_soc_dai pcm3008_dai;
+
+#endif

+ 46 - 11
sound/soc/codecs/ssm2602.c

@@ -285,16 +285,23 @@ static inline int get_coeff(int mclk, int rate)
 }
 }
 
 
 static int ssm2602_hw_params(struct snd_pcm_substream *substream,
 static int ssm2602_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
 {
 {
 	u16 srate;
 	u16 srate;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct ssm2602_priv *ssm2602 = codec->private_data;
 	struct ssm2602_priv *ssm2602 = codec->private_data;
+	struct i2c_client *i2c = codec->control_data;
 	u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
 	u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
 	int i = get_coeff(ssm2602->sysclk, params_rate(params));
 	int i = get_coeff(ssm2602->sysclk, params_rate(params));
 
 
+	if (substream == ssm2602->slave_substream) {
+		dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
+		return 0;
+	}
+
 	/*no match is found*/
 	/*no match is found*/
 	if (i == ARRAY_SIZE(coeff_div))
 	if (i == ARRAY_SIZE(coeff_div))
 		return -EINVAL;
 		return -EINVAL;
@@ -324,19 +331,26 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 	return 0;
 }
 }
 
 
-static int ssm2602_startup(struct snd_pcm_substream *substream)
+static int ssm2602_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct ssm2602_priv *ssm2602 = codec->private_data;
 	struct ssm2602_priv *ssm2602 = codec->private_data;
+	struct i2c_client *i2c = codec->control_data;
 	struct snd_pcm_runtime *master_runtime;
 	struct snd_pcm_runtime *master_runtime;
 
 
 	/* The DAI has shared clocks so if we already have a playback or
 	/* The DAI has shared clocks so if we already have a playback or
 	 * capture going then constrain this substream to match it.
 	 * capture going then constrain this substream to match it.
+	 * TODO: the ssm2602 allows pairs of non-matching PB/REC rates
 	 */
 	 */
 	if (ssm2602->master_substream) {
 	if (ssm2602->master_substream) {
 		master_runtime = ssm2602->master_substream->runtime;
 		master_runtime = ssm2602->master_substream->runtime;
+		dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
+			master_runtime->sample_bits,
+			master_runtime->rate);
+
 		snd_pcm_hw_constraint_minmax(substream->runtime,
 		snd_pcm_hw_constraint_minmax(substream->runtime,
 					     SNDRV_PCM_HW_PARAM_RATE,
 					     SNDRV_PCM_HW_PARAM_RATE,
 					     master_runtime->rate,
 					     master_runtime->rate,
@@ -354,7 +368,8 @@ static int ssm2602_startup(struct snd_pcm_substream *substream)
 	return 0;
 	return 0;
 }
 }
 
 
-static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
+static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -365,14 +380,21 @@ static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
 	return 0;
 	return 0;
 }
 }
 
 
-static void ssm2602_shutdown(struct snd_pcm_substream *substream)
+static void ssm2602_shutdown(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_codec *codec = socdev->codec;
+	struct ssm2602_priv *ssm2602 = codec->private_data;
 	/* deactivate */
 	/* deactivate */
 	if (!codec->active)
 	if (!codec->active)
 		ssm2602_write(codec, SSM2602_ACTIVE, 0);
 		ssm2602_write(codec, SSM2602_ACTIVE, 0);
+
+	if (ssm2602->master_substream == substream)
+		ssm2602->master_substream = ssm2602->slave_substream;
+
+	ssm2602->slave_substream = NULL;
 }
 }
 
 
 static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
 static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
@@ -432,10 +454,10 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		iface |= 0x0001;
 		iface |= 0x0001;
 		break;
 		break;
 	case SND_SOC_DAIFMT_DSP_A:
 	case SND_SOC_DAIFMT_DSP_A:
-		iface |= 0x0003;
+		iface |= 0x0013;
 		break;
 		break;
 	case SND_SOC_DAIFMT_DSP_B:
 	case SND_SOC_DAIFMT_DSP_B:
-		iface |= 0x0013;
+		iface |= 0x0003;
 		break;
 		break;
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
@@ -496,6 +518,9 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
 		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
 		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
 		SNDRV_PCM_RATE_96000)
 		SNDRV_PCM_RATE_96000)
 
 
+#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+		SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
 struct snd_soc_dai ssm2602_dai = {
 struct snd_soc_dai ssm2602_dai = {
 	.name = "SSM2602",
 	.name = "SSM2602",
 	.playback = {
 	.playback = {
@@ -503,20 +528,18 @@ struct snd_soc_dai ssm2602_dai = {
 		.channels_min = 2,
 		.channels_min = 2,
 		.channels_max = 2,
 		.channels_max = 2,
 		.rates = SSM2602_RATES,
 		.rates = SSM2602_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S32_LE,},
+		.formats = SSM2602_FORMATS,},
 	.capture = {
 	.capture = {
 		.stream_name = "Capture",
 		.stream_name = "Capture",
 		.channels_min = 2,
 		.channels_min = 2,
 		.channels_max = 2,
 		.channels_max = 2,
 		.rates = SSM2602_RATES,
 		.rates = SSM2602_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S32_LE,},
+		.formats = SSM2602_FORMATS,},
 	.ops = {
 	.ops = {
 		.startup = ssm2602_startup,
 		.startup = ssm2602_startup,
 		.prepare = ssm2602_pcm_prepare,
 		.prepare = ssm2602_pcm_prepare,
 		.hw_params = ssm2602_hw_params,
 		.hw_params = ssm2602_hw_params,
 		.shutdown = ssm2602_shutdown,
 		.shutdown = ssm2602_shutdown,
-	},
-	.dai_ops = {
 		.digital_mute = ssm2602_mute,
 		.digital_mute = ssm2602_mute,
 		.set_sysclk = ssm2602_set_dai_sysclk,
 		.set_sysclk = ssm2602_set_dai_sysclk,
 		.set_fmt = ssm2602_set_dai_fmt,
 		.set_fmt = ssm2602_set_dai_fmt,
@@ -601,7 +624,7 @@ static int ssm2602_init(struct snd_soc_device *socdev)
 
 
 	ssm2602_add_controls(codec);
 	ssm2602_add_controls(codec);
 	ssm2602_add_widgets(codec);
 	ssm2602_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		pr_err("ssm2602: failed to register card\n");
 		pr_err("ssm2602: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -770,6 +793,18 @@ struct snd_soc_codec_device soc_codec_dev_ssm2602 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602);
 EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602);
 
 
+static int __init ssm2602_modinit(void)
+{
+	return snd_soc_register_dai(&ssm2602_dai);
+}
+module_init(ssm2602_modinit);
+
+static void __exit ssm2602_exit(void)
+{
+	snd_soc_unregister_dai(&ssm2602_dai);
+}
+module_exit(ssm2602_exit);
+
 MODULE_DESCRIPTION("ASoC ssm2602 driver");
 MODULE_DESCRIPTION("ASoC ssm2602 driver");
 MODULE_AUTHOR("Cliff Cai");
 MODULE_AUTHOR("Cliff Cai");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 206 - 56
sound/soc/codecs/tlv320aic23.c

@@ -37,12 +37,6 @@
 
 
 #define AIC23_VERSION "0.1"
 #define AIC23_VERSION "0.1"
 
 
-struct tlv320aic23_srate_reg_info {
-	u32 sample_rate;
-	u8 control;		/* SR3, SR2, SR1, SR0 and BOSR */
-	u8 divider;		/* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */
-};
-
 /*
 /*
  * AIC23 register cache
  * AIC23 register cache
  */
  */
@@ -261,20 +255,156 @@ static const struct snd_soc_dapm_route intercon[] = {
 
 
 };
 };
 
 
-/* tlv320aic23 related */
-static const struct tlv320aic23_srate_reg_info srate_reg_info[] = {
-	{4000, 0x06, 1},	/*  4000 */
-	{8000, 0x06, 0},	/*  8000 */
-	{16000, 0x0C, 1},	/* 16000 */
-	{22050, 0x11, 1},	/* 22050 */
-	{24000, 0x00, 1},	/* 24000 */
-	{32000, 0x0C, 0},	/* 32000 */
-	{44100, 0x11, 0},	/* 44100 */
-	{48000, 0x00, 0},	/* 48000 */
-	{88200, 0x1F, 0},	/* 88200 */
-	{96000, 0x0E, 0},	/* 96000 */
+/* AIC23 driver data */
+struct aic23 {
+	struct snd_soc_codec codec;
+	int mclk;
+	int requested_adc;
+	int requested_dac;
+};
+
+/*
+ * Common Crystals used
+ * 11.2896 Mhz /128 = *88.2k  /192 = 58.8k
+ * 12.0000 Mhz /125 = *96k    /136 = 88.235K
+ * 12.2880 Mhz /128 = *96k    /192 = 64k
+ * 16.9344 Mhz /128 = 132.3k /192 = *88.2k
+ * 18.4320 Mhz /128 = 144k   /192 = *96k
+ */
+
+/*
+ * Normal BOSR 0-256/2 = 128, 1-384/2 = 192
+ * USB BOSR 0-250/2 = 125, 1-272/2 = 136
+ */
+static const int bosr_usb_divisor_table[] = {
+	128, 125, 192, 136
+};
+#define LOWER_GROUP ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<6) | (1<<7))
+#define UPPER_GROUP ((1<<8) | (1<<9) | (1<<10) | (1<<11)        | (1<<15))
+static const unsigned short sr_valid_mask[] = {
+	LOWER_GROUP|UPPER_GROUP,	/* Normal, bosr - 0*/
+	LOWER_GROUP|UPPER_GROUP,	/* Normal, bosr - 1*/
+	LOWER_GROUP,			/* Usb, bosr - 0*/
+	UPPER_GROUP,			/* Usb, bosr - 1*/
+};
+/*
+ * Every divisor is a factor of 11*12
+ */
+#define SR_MULT (11*12)
+#define A(x) (x) ? (SR_MULT/x) : 0
+static const unsigned char sr_adc_mult_table[] = {
+	A(2), A(2), A(12), A(12),  A(0), A(0), A(3), A(1),
+	A(2), A(2), A(11), A(11),  A(0), A(0), A(0), A(1)
+};
+static const unsigned char sr_dac_mult_table[] = {
+	A(2), A(12), A(2), A(12),  A(0), A(0), A(3), A(1),
+	A(2), A(11), A(2), A(11),  A(0), A(0), A(0), A(1)
 };
 };
 
 
+static unsigned get_score(int adc, int adc_l, int adc_h, int need_adc,
+		int dac, int dac_l, int dac_h, int need_dac)
+{
+	if ((adc >= adc_l) && (adc <= adc_h) &&
+			(dac >= dac_l) && (dac <= dac_h)) {
+		int diff_adc = need_adc - adc;
+		int diff_dac = need_dac - dac;
+		return abs(diff_adc) + abs(diff_dac);
+	}
+	return UINT_MAX;
+}
+
+static int find_rate(int mclk, u32 need_adc, u32 need_dac)
+{
+	int i, j;
+	int best_i = -1;
+	int best_j = -1;
+	int best_div = 0;
+	unsigned best_score = UINT_MAX;
+	int adc_l, adc_h, dac_l, dac_h;
+
+	need_adc *= SR_MULT;
+	need_dac *= SR_MULT;
+	/*
+	 * rates given are +/- 1/32
+	 */
+	adc_l = need_adc - (need_adc >> 5);
+	adc_h = need_adc + (need_adc >> 5);
+	dac_l = need_dac - (need_dac >> 5);
+	dac_h = need_dac + (need_dac >> 5);
+	for (i = 0; i < ARRAY_SIZE(bosr_usb_divisor_table); i++) {
+		int base = mclk / bosr_usb_divisor_table[i];
+		int mask = sr_valid_mask[i];
+		for (j = 0; j < ARRAY_SIZE(sr_adc_mult_table);
+				j++, mask >>= 1) {
+			int adc;
+			int dac;
+			int score;
+			if ((mask & 1) == 0)
+				continue;
+			adc = base * sr_adc_mult_table[j];
+			dac = base * sr_dac_mult_table[j];
+			score = get_score(adc, adc_l, adc_h, need_adc,
+					dac, dac_l, dac_h, need_dac);
+			if (best_score > score) {
+				best_score = score;
+				best_i = i;
+				best_j = j;
+				best_div = 0;
+			}
+			score = get_score((adc >> 1), adc_l, adc_h, need_adc,
+					(dac >> 1), dac_l, dac_h, need_dac);
+			/* prefer to have a /2 */
+			if ((score != UINT_MAX) && (best_score >= score)) {
+				best_score = score;
+				best_i = i;
+				best_j = j;
+				best_div = 1;
+			}
+		}
+	}
+	return (best_j << 2) | best_i | (best_div << TLV320AIC23_CLKIN_SHIFT);
+}
+
+#ifdef DEBUG
+static void get_current_sample_rates(struct snd_soc_codec *codec, int mclk,
+		u32 *sample_rate_adc, u32 *sample_rate_dac)
+{
+	int src = tlv320aic23_read_reg_cache(codec, TLV320AIC23_SRATE);
+	int sr = (src >> 2) & 0x0f;
+	int val = (mclk / bosr_usb_divisor_table[src & 3]);
+	int adc = (val * sr_adc_mult_table[sr]) / SR_MULT;
+	int dac = (val * sr_dac_mult_table[sr]) / SR_MULT;
+	if (src & TLV320AIC23_CLKIN_HALF) {
+		adc >>= 1;
+		dac >>= 1;
+	}
+	*sample_rate_adc = adc;
+	*sample_rate_dac = dac;
+}
+#endif
+
+static int set_sample_rate_control(struct snd_soc_codec *codec, int mclk,
+		u32 sample_rate_adc, u32 sample_rate_dac)
+{
+	/* Search for the right sample rate */
+	int data = find_rate(mclk, sample_rate_adc, sample_rate_dac);
+	if (data < 0) {
+		printk(KERN_ERR "%s:Invalid rate %u,%u requested\n",
+				__func__, sample_rate_adc, sample_rate_dac);
+		return -EINVAL;
+	}
+	tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
+#ifdef DEBUG
+	{
+		u32 adc, dac;
+		get_current_sample_rates(codec, mclk, &adc, &dac);
+		printk(KERN_DEBUG "actual samplerate = %u,%u reg=%x\n",
+			adc, dac, data);
+	}
+#endif
+	return 0;
+}
+
 static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
 static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
 {
 {
 	snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
 	snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
@@ -288,32 +418,36 @@ static int tlv320aic23_add_widgets(struct snd_soc_codec *codec)
 }
 }
 
 
 static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
 static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
-				 struct snd_pcm_hw_params *params)
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_codec *codec = socdev->codec;
-	u16 iface_reg, data;
-	u8 count = 0;
+	u16 iface_reg;
+	int ret;
+	struct aic23 *aic23 = container_of(codec, struct aic23, codec);
+	u32 sample_rate_adc = aic23->requested_adc;
+	u32 sample_rate_dac = aic23->requested_dac;
+	u32 sample_rate = params_rate(params);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		aic23->requested_dac = sample_rate_dac = sample_rate;
+		if (!sample_rate_adc)
+			sample_rate_adc = sample_rate;
+	} else {
+		aic23->requested_adc = sample_rate_adc = sample_rate;
+		if (!sample_rate_dac)
+			sample_rate_dac = sample_rate;
+	}
+	ret = set_sample_rate_control(codec, aic23->mclk, sample_rate_adc,
+			sample_rate_dac);
+	if (ret < 0)
+		return ret;
 
 
 	iface_reg =
 	iface_reg =
 	    tlv320aic23_read_reg_cache(codec,
 	    tlv320aic23_read_reg_cache(codec,
 				       TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
 				       TLV320AIC23_DIGT_FMT) & ~(0x03 << 2);
-
-	/* Search for the right sample rate */
-	/* Verify what happens if the rate is not supported
-	 * now it goes to 96Khz */
-	while ((srate_reg_info[count].sample_rate != params_rate(params)) &&
-	       (count < ARRAY_SIZE(srate_reg_info))) {
-		count++;
-	}
-
-	data =  (srate_reg_info[count].divider << TLV320AIC23_CLKIN_SHIFT) |
-		(srate_reg_info[count]. control << TLV320AIC23_BOSR_SHIFT) |
-		TLV320AIC23_USB_CLK_ON;
-
-	tlv320aic23_write(codec, TLV320AIC23_SRATE, data);
-
 	switch (params_format(params)) {
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
 	case SNDRV_PCM_FORMAT_S16_LE:
 		break;
 		break;
@@ -332,7 +466,8 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 	return 0;
 }
 }
 
 
-static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
+static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -344,17 +479,23 @@ static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
 	return 0;
 	return 0;
 }
 }
 
 
-static void tlv320aic23_shutdown(struct snd_pcm_substream *substream)
+static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_codec *codec = socdev->codec;
+	struct aic23 *aic23 = container_of(codec, struct aic23, codec);
 
 
 	/* deactivate */
 	/* deactivate */
 	if (!codec->active) {
 	if (!codec->active) {
 		udelay(50);
 		udelay(50);
 		tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
 		tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
 	}
 	}
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		aic23->requested_dac = 0;
+	else
+		aic23->requested_adc = 0;
 }
 }
 
 
 static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
 static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute)
@@ -400,7 +541,7 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai,
 	case SND_SOC_DAIFMT_I2S:
 	case SND_SOC_DAIFMT_I2S:
 		iface_reg |= TLV320AIC23_FOR_I2S;
 		iface_reg |= TLV320AIC23_FOR_I2S;
 		break;
 		break;
-	case SND_SOC_DAIFMT_DSP_A:
+	case SND_SOC_DAIFMT_DSP_B:
 		iface_reg |= TLV320AIC23_FOR_DSP;
 		iface_reg |= TLV320AIC23_FOR_DSP;
 		break;
 		break;
 	case SND_SOC_DAIFMT_RIGHT_J:
 	case SND_SOC_DAIFMT_RIGHT_J:
@@ -422,12 +563,9 @@ static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 				      int clk_id, unsigned int freq, int dir)
 				      int clk_id, unsigned int freq, int dir)
 {
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct snd_soc_codec *codec = codec_dai->codec;
-
-	switch (freq) {
-	case 12000000:
-		return 0;
-	}
-	return -EINVAL;
+	struct aic23 *aic23 = container_of(codec, struct aic23, codec);
+	aic23->mclk = freq;
+	return 0;
 }
 }
 
 
 static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
 static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
@@ -478,12 +616,10 @@ struct snd_soc_dai tlv320aic23_dai = {
 		.prepare = tlv320aic23_pcm_prepare,
 		.prepare = tlv320aic23_pcm_prepare,
 		.hw_params = tlv320aic23_hw_params,
 		.hw_params = tlv320aic23_hw_params,
 		.shutdown = tlv320aic23_shutdown,
 		.shutdown = tlv320aic23_shutdown,
-		},
-	.dai_ops = {
-		    .digital_mute = tlv320aic23_mute,
-		    .set_fmt = tlv320aic23_set_dai_fmt,
-		    .set_sysclk = tlv320aic23_set_dai_sysclk,
-		    }
+		.digital_mute = tlv320aic23_mute,
+		.set_fmt = tlv320aic23_set_dai_fmt,
+		.set_sysclk = tlv320aic23_set_dai_sysclk,
+	}
 };
 };
 EXPORT_SYMBOL_GPL(tlv320aic23_dai);
 EXPORT_SYMBOL_GPL(tlv320aic23_dai);
 
 
@@ -584,7 +720,7 @@ static int tlv320aic23_init(struct snd_soc_device *socdev)
 
 
 	tlv320aic23_add_controls(codec);
 	tlv320aic23_add_controls(codec);
 	tlv320aic23_add_widgets(codec);
 	tlv320aic23_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "tlv320aic23: failed to register card\n");
 		printk(KERN_ERR "tlv320aic23: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -659,14 +795,15 @@ static int tlv320aic23_probe(struct platform_device *pdev)
 {
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec *codec;
+	struct aic23 *aic23;
 	int ret = 0;
 	int ret = 0;
 
 
 	printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
 	printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION);
 
 
-	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-	if (codec == NULL)
+	aic23 = kzalloc(sizeof(struct aic23), GFP_KERNEL);
+	if (aic23 == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
-
+	codec = &aic23->codec;
 	socdev->codec = codec;
 	socdev->codec = codec;
 	mutex_init(&codec->mutex);
 	mutex_init(&codec->mutex);
 	INIT_LIST_HEAD(&codec->dapm_widgets);
 	INIT_LIST_HEAD(&codec->dapm_widgets);
@@ -687,6 +824,7 @@ static int tlv320aic23_remove(struct platform_device *pdev)
 {
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_codec *codec = socdev->codec;
+	struct aic23 *aic23 = container_of(codec, struct aic23, codec);
 
 
 	if (codec->control_data)
 	if (codec->control_data)
 		tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
 		tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
@@ -697,7 +835,7 @@ static int tlv320aic23_remove(struct platform_device *pdev)
 	i2c_del_driver(&tlv320aic23_i2c_driver);
 	i2c_del_driver(&tlv320aic23_i2c_driver);
 #endif
 #endif
 	kfree(codec->reg_cache);
 	kfree(codec->reg_cache);
-	kfree(codec);
+	kfree(aic23);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -709,6 +847,18 @@ struct snd_soc_codec_device soc_codec_dev_tlv320aic23 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23);
 EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23);
 
 
+static int __init tlv320aic23_modinit(void)
+{
+	return snd_soc_register_dai(&tlv320aic23_dai);
+}
+module_init(tlv320aic23_modinit);
+
+static void __exit tlv320aic23_exit(void)
+{
+	snd_soc_unregister_dai(&tlv320aic23_dai);
+}
+module_exit(tlv320aic23_exit);
+
 MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
 MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver");
 MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
 MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 15 - 7
sound/soc/codecs/tlv320aic26.c

@@ -125,7 +125,8 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
  * Digital Audio Interface Operations
  * Digital Audio Interface Operations
  */
  */
 static int aic26_hw_params(struct snd_pcm_substream *substream,
 static int aic26_hw_params(struct snd_pcm_substream *substream,
-			   struct snd_pcm_hw_params *params)
+			   struct snd_pcm_hw_params *params,
+			   struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -287,8 +288,6 @@ struct snd_soc_dai aic26_dai = {
 	},
 	},
 	.ops = {
 	.ops = {
 		.hw_params = aic26_hw_params,
 		.hw_params = aic26_hw_params,
-	},
-	.dai_ops = {
 		.digital_mute = aic26_mute,
 		.digital_mute = aic26_mute,
 		.set_sysclk = aic26_set_sysclk,
 		.set_sysclk = aic26_set_sysclk,
 		.set_fmt = aic26_set_fmt,
 		.set_fmt = aic26_set_fmt,
@@ -360,7 +359,7 @@ static int aic26_probe(struct platform_device *pdev)
 
 
 	/* CODEC is setup, we can register the card now */
 	/* CODEC is setup, we can register the card now */
 	dev_dbg(&pdev->dev, "Registering card\n");
 	dev_dbg(&pdev->dev, "Registering card\n");
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(&pdev->dev, "aic26: failed to register card\n");
 		dev_err(&pdev->dev, "aic26: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -427,7 +426,7 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
 static int aic26_spi_probe(struct spi_device *spi)
 static int aic26_spi_probe(struct spi_device *spi)
 {
 {
 	struct aic26 *aic26;
 	struct aic26 *aic26;
-	int rc, i, reg;
+	int ret, i, reg;
 
 
 	dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
 	dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n");
 
 
@@ -457,6 +456,14 @@ static int aic26_spi_probe(struct spi_device *spi)
 	aic26->codec.reg_cache_size = AIC26_NUM_REGS;
 	aic26->codec.reg_cache_size = AIC26_NUM_REGS;
 	aic26->codec.reg_cache = aic26->reg_cache;
 	aic26->codec.reg_cache = aic26->reg_cache;
 
 
+	aic26_dai.dev = &spi->dev;
+	ret = snd_soc_register_dai(&aic26_dai);
+	if (ret != 0) {
+		dev_err(&spi->dev, "Failed to register DAI: %d\n", ret);
+		kfree(aic26);
+		return ret;
+	}
+
 	/* Reset the codec to power on defaults */
 	/* Reset the codec to power on defaults */
 	aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00);
 	aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00);
 
 
@@ -475,8 +482,8 @@ static int aic26_spi_probe(struct spi_device *spi)
 
 
 	/* Register the sysfs files for debugging */
 	/* Register the sysfs files for debugging */
 	/* Create SysFS files */
 	/* Create SysFS files */
-	rc = device_create_file(&spi->dev, &dev_attr_keyclick);
-	if (rc)
+	ret = device_create_file(&spi->dev, &dev_attr_keyclick);
+	if (ret)
 		dev_info(&spi->dev, "error creating sysfs files\n");
 		dev_info(&spi->dev, "error creating sysfs files\n");
 
 
 #if defined(CONFIG_SND_SOC_OF_SIMPLE)
 #if defined(CONFIG_SND_SOC_OF_SIMPLE)
@@ -493,6 +500,7 @@ static int aic26_spi_remove(struct spi_device *spi)
 {
 {
 	struct aic26 *aic26 = dev_get_drvdata(&spi->dev);
 	struct aic26 *aic26 = dev_get_drvdata(&spi->dev);
 
 
+	snd_soc_unregister_dai(&aic26_dai);
 	kfree(aic26);
 	kfree(aic26);
 
 
 	return 0;
 	return 0;

+ 126 - 40
sound/soc/codecs/tlv320aic3x.c

@@ -253,11 +253,17 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
 
 
 	SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL,
 	SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL,
 		     DACR1_2_RLOPM_VOL, 0, 0x7f, 1),
 		     DACR1_2_RLOPM_VOL, 0, 0x7f, 1),
-	SOC_DOUBLE_R("Line DAC Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3,
-		     0x01, 0),
-	SOC_DOUBLE_R("Line PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
-		     PGAR_2_RLOPM_VOL, 0, 0x7f, 1),
-	SOC_DOUBLE_R("Line Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
+	SOC_SINGLE("LineL Playback Switch", LLOPM_CTRL, 3, 0x01, 0),
+	SOC_SINGLE("LineR Playback Switch", RLOPM_CTRL, 3, 0x01, 0),
+	SOC_DOUBLE_R("LineL DAC Playback Volume", DACL1_2_LLOPM_VOL,
+		     DACR1_2_LLOPM_VOL, 0, 0x7f, 1),
+	SOC_SINGLE("LineL Left PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
+		     0, 0x7f, 1),
+	SOC_SINGLE("LineR Right PGA Bypass Playback Volume", PGAR_2_RLOPM_VOL,
+		     0, 0x7f, 1),
+	SOC_DOUBLE_R("LineL Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
+		     LINE2R_2_LLOPM_VOL, 0, 0x7f, 1),
+	SOC_DOUBLE_R("LineR Line2 Bypass Playback Volume", LINE2L_2_RLOPM_VOL,
 		     LINE2R_2_RLOPM_VOL, 0, 0x7f, 1),
 		     LINE2R_2_RLOPM_VOL, 0, 0x7f, 1),
 
 
 	SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL,
 	SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL,
@@ -272,8 +278,12 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
 		     DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
 		     DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
 	SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
 	SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
 		     0x01, 0),
 		     0x01, 0),
-	SOC_DOUBLE_R("HP PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
+	SOC_DOUBLE_R("HP Right PGA Bypass Playback Volume", PGAR_2_HPLOUT_VOL,
 		     PGAR_2_HPROUT_VOL, 0, 0x7f, 1),
 		     PGAR_2_HPROUT_VOL, 0, 0x7f, 1),
+	SOC_SINGLE("HPL PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
+		     0, 0x7f, 1),
+	SOC_SINGLE("HPR PGA Bypass Playback Volume", PGAL_2_HPROUT_VOL,
+		     0, 0x7f, 1),
 	SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL,
 	SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL,
 		     LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
 		     LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
 
 
@@ -281,8 +291,10 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
 		     DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
 		     DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
 	SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
 	SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
 		     0x01, 0),
 		     0x01, 0),
-	SOC_DOUBLE_R("HPCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
-		     PGAR_2_HPRCOM_VOL, 0, 0x7f, 1),
+	SOC_SINGLE("HPLCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
+		     0, 0x7f, 1),
+	SOC_SINGLE("HPRCOM PGA Bypass Playback Volume", PGAL_2_HPRCOM_VOL,
+		     0, 0x7f, 1),
 	SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL,
 	SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL,
 		     LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
 		     LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
 
 
@@ -333,7 +345,8 @@ SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]);
 
 
 /* Left DAC_L1 Mixer */
 /* Left DAC_L1 Mixer */
 static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
 static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
-	SOC_DAPM_SINGLE("Line Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineL Switch", DACL1_2_LLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineR Switch", DACL1_2_RLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0),
@@ -341,7 +354,8 @@ static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
 
 
 /* Right DAC_R1 Mixer */
 /* Right DAC_R1 Mixer */
 static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
 static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
-	SOC_DAPM_SINGLE("Line Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineL Switch", DACR1_2_LLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineR Switch", DACR1_2_RLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0),
@@ -350,14 +364,18 @@ static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
 /* Left PGA Mixer */
 /* Left PGA Mixer */
 static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = {
 static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = {
 	SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1),
+	SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_LADC_CTRL, 3, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1),
+	SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_LADC_CTRL, 0, 1, 1),
 };
 };
 
 
 /* Right PGA Mixer */
 /* Right PGA Mixer */
 static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
 static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
 	SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1),
+	SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_RADC_CTRL, 3, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1),
+	SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
 	SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
 };
 };
 
 
@@ -379,34 +397,42 @@ SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]);
 
 
 /* Left PGA Bypass Mixer */
 /* Left PGA Bypass Mixer */
 static const struct snd_kcontrol_new aic3x_left_pga_bp_mixer_controls[] = {
 static const struct snd_kcontrol_new aic3x_left_pga_bp_mixer_controls[] = {
-	SOC_DAPM_SINGLE("Line Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineL Switch", PGAL_2_LLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineR Switch", PGAL_2_RLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0),
-	SOC_DAPM_SINGLE("HP Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
-	SOC_DAPM_SINGLE("HPCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPL Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPR Switch", PGAL_2_HPROUT_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPLCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPRCOM Switch", PGAL_2_HPRCOM_VOL, 7, 1, 0),
 };
 };
 
 
 /* Right PGA Bypass Mixer */
 /* Right PGA Bypass Mixer */
 static const struct snd_kcontrol_new aic3x_right_pga_bp_mixer_controls[] = {
 static const struct snd_kcontrol_new aic3x_right_pga_bp_mixer_controls[] = {
-	SOC_DAPM_SINGLE("Line Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineL Switch", PGAR_2_LLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineR Switch", PGAR_2_RLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0),
-	SOC_DAPM_SINGLE("HP Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
-	SOC_DAPM_SINGLE("HPCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPL Switch", PGAR_2_HPLOUT_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPR Switch", PGAR_2_HPROUT_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPLCOM Switch", PGAR_2_HPLCOM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPRCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0),
 };
 };
 
 
 /* Left Line2 Bypass Mixer */
 /* Left Line2 Bypass Mixer */
 static const struct snd_kcontrol_new aic3x_left_line2_bp_mixer_controls[] = {
 static const struct snd_kcontrol_new aic3x_left_line2_bp_mixer_controls[] = {
-	SOC_DAPM_SINGLE("Line Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineL Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineR Switch", LINE2L_2_RLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HP Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HP Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0),
-	SOC_DAPM_SINGLE("HPCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPLCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0),
 };
 };
 
 
 /* Right Line2 Bypass Mixer */
 /* Right Line2 Bypass Mixer */
 static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = {
 static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = {
-	SOC_DAPM_SINGLE("Line Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineL Switch", LINE2R_2_LLOPM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("LineR Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("Mono Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HP Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0),
 	SOC_DAPM_SINGLE("HP Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0),
-	SOC_DAPM_SINGLE("HPCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
+	SOC_DAPM_SINGLE("HPRCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0),
 };
 };
 
 
 static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
 static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
@@ -439,22 +465,26 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
 	/* Mono Output */
 	/* Mono Output */
 	SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0),
 
 
-	/* Left Inputs to Left ADC */
+	/* Inputs to Left ADC */
 	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),
 	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0),
 	SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
 	SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
 			   &aic3x_left_pga_mixer_controls[0],
 			   &aic3x_left_pga_mixer_controls[0],
 			   ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
 			   ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
 	SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
 	SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
 			 &aic3x_left_line1_mux_controls),
 			 &aic3x_left_line1_mux_controls),
+	SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0,
+			 &aic3x_left_line1_mux_controls),
 	SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
 	SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0,
 			 &aic3x_left_line2_mux_controls),
 			 &aic3x_left_line2_mux_controls),
 
 
-	/* Right Inputs to Right ADC */
+	/* Inputs to Right ADC */
 	SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
 	SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
 			 LINE1R_2_RADC_CTRL, 2, 0),
 			 LINE1R_2_RADC_CTRL, 2, 0),
 	SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
 	SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
 			   &aic3x_right_pga_mixer_controls[0],
 			   &aic3x_right_pga_mixer_controls[0],
 			   ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
 			   ARRAY_SIZE(aic3x_right_pga_mixer_controls)),
+	SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0,
+			 &aic3x_right_line1_mux_controls),
 	SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
 	SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0,
 			 &aic3x_right_line1_mux_controls),
 			 &aic3x_right_line1_mux_controls),
 	SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
 	SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
@@ -531,7 +561,8 @@ static const struct snd_soc_dapm_route intercon[] = {
 	{"Left DAC Mux", "DAC_L2", "Left DAC"},
 	{"Left DAC Mux", "DAC_L2", "Left DAC"},
 	{"Left DAC Mux", "DAC_L3", "Left DAC"},
 	{"Left DAC Mux", "DAC_L3", "Left DAC"},
 
 
-	{"Left DAC_L1 Mixer", "Line Switch", "Left DAC Mux"},
+	{"Left DAC_L1 Mixer", "LineL Switch", "Left DAC Mux"},
+	{"Left DAC_L1 Mixer", "LineR Switch", "Left DAC Mux"},
 	{"Left DAC_L1 Mixer", "Mono Switch", "Left DAC Mux"},
 	{"Left DAC_L1 Mixer", "Mono Switch", "Left DAC Mux"},
 	{"Left DAC_L1 Mixer", "HP Switch", "Left DAC Mux"},
 	{"Left DAC_L1 Mixer", "HP Switch", "Left DAC Mux"},
 	{"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"},
 	{"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"},
@@ -557,7 +588,8 @@ static const struct snd_soc_dapm_route intercon[] = {
 	{"Right DAC Mux", "DAC_R2", "Right DAC"},
 	{"Right DAC Mux", "DAC_R2", "Right DAC"},
 	{"Right DAC Mux", "DAC_R3", "Right DAC"},
 	{"Right DAC Mux", "DAC_R3", "Right DAC"},
 
 
-	{"Right DAC_R1 Mixer", "Line Switch", "Right DAC Mux"},
+	{"Right DAC_R1 Mixer", "LineL Switch", "Right DAC Mux"},
+	{"Right DAC_R1 Mixer", "LineR Switch", "Right DAC Mux"},
 	{"Right DAC_R1 Mixer", "Mono Switch", "Right DAC Mux"},
 	{"Right DAC_R1 Mixer", "Mono Switch", "Right DAC Mux"},
 	{"Right DAC_R1 Mixer", "HP Switch", "Right DAC Mux"},
 	{"Right DAC_R1 Mixer", "HP Switch", "Right DAC Mux"},
 	{"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"},
 	{"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"},
@@ -592,8 +624,10 @@ static const struct snd_soc_dapm_route intercon[] = {
 	{"Left Line2L Mux", "differential", "LINE2L"},
 	{"Left Line2L Mux", "differential", "LINE2L"},
 
 
 	{"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"},
 	{"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"},
+	{"Left PGA Mixer", "Line1R Switch", "Left Line1R Mux"},
 	{"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"},
 	{"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"},
 	{"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
 	{"Left PGA Mixer", "Mic3L Switch", "MIC3L"},
+	{"Left PGA Mixer", "Mic3R Switch", "MIC3R"},
 
 
 	{"Left ADC", NULL, "Left PGA Mixer"},
 	{"Left ADC", NULL, "Left PGA Mixer"},
 	{"Left ADC", NULL, "GPIO1 dmic modclk"},
 	{"Left ADC", NULL, "GPIO1 dmic modclk"},
@@ -605,18 +639,23 @@ static const struct snd_soc_dapm_route intercon[] = {
 	{"Right Line2R Mux", "single-ended", "LINE2R"},
 	{"Right Line2R Mux", "single-ended", "LINE2R"},
 	{"Right Line2R Mux", "differential", "LINE2R"},
 	{"Right Line2R Mux", "differential", "LINE2R"},
 
 
+	{"Right PGA Mixer", "Line1L Switch", "Right Line1L Mux"},
 	{"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"},
 	{"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"},
 	{"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"},
 	{"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"},
+	{"Right PGA Mixer", "Mic3L Switch", "MIC3L"},
 	{"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
 	{"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
 
 
 	{"Right ADC", NULL, "Right PGA Mixer"},
 	{"Right ADC", NULL, "Right PGA Mixer"},
 	{"Right ADC", NULL, "GPIO1 dmic modclk"},
 	{"Right ADC", NULL, "GPIO1 dmic modclk"},
 
 
 	/* Left PGA Bypass */
 	/* Left PGA Bypass */
-	{"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"},
+	{"Left PGA Bypass Mixer", "LineL Switch", "Left PGA Mixer"},
+	{"Left PGA Bypass Mixer", "LineR Switch", "Left PGA Mixer"},
 	{"Left PGA Bypass Mixer", "Mono Switch", "Left PGA Mixer"},
 	{"Left PGA Bypass Mixer", "Mono Switch", "Left PGA Mixer"},
-	{"Left PGA Bypass Mixer", "HP Switch", "Left PGA Mixer"},
-	{"Left PGA Bypass Mixer", "HPCOM Switch", "Left PGA Mixer"},
+	{"Left PGA Bypass Mixer", "HPL Switch", "Left PGA Mixer"},
+	{"Left PGA Bypass Mixer", "HPR Switch", "Left PGA Mixer"},
+	{"Left PGA Bypass Mixer", "HPLCOM Switch", "Left PGA Mixer"},
+	{"Left PGA Bypass Mixer", "HPRCOM Switch", "Left PGA Mixer"},
 
 
 	{"Left HPCOM Mux", "differential of HPLOUT", "Left PGA Bypass Mixer"},
 	{"Left HPCOM Mux", "differential of HPLOUT", "Left PGA Bypass Mixer"},
 	{"Left HPCOM Mux", "constant VCM", "Left PGA Bypass Mixer"},
 	{"Left HPCOM Mux", "constant VCM", "Left PGA Bypass Mixer"},
@@ -627,10 +666,13 @@ static const struct snd_soc_dapm_route intercon[] = {
 	{"Left HP Out", NULL, "Left PGA Bypass Mixer"},
 	{"Left HP Out", NULL, "Left PGA Bypass Mixer"},
 
 
 	/* Right PGA Bypass */
 	/* Right PGA Bypass */
-	{"Right PGA Bypass Mixer", "Line Switch", "Right PGA Mixer"},
+	{"Right PGA Bypass Mixer", "LineL Switch", "Right PGA Mixer"},
+	{"Right PGA Bypass Mixer", "LineR Switch", "Right PGA Mixer"},
 	{"Right PGA Bypass Mixer", "Mono Switch", "Right PGA Mixer"},
 	{"Right PGA Bypass Mixer", "Mono Switch", "Right PGA Mixer"},
-	{"Right PGA Bypass Mixer", "HP Switch", "Right PGA Mixer"},
-	{"Right PGA Bypass Mixer", "HPCOM Switch", "Right PGA Mixer"},
+	{"Right PGA Bypass Mixer", "HPL Switch", "Right PGA Mixer"},
+	{"Right PGA Bypass Mixer", "HPR Switch", "Right PGA Mixer"},
+	{"Right PGA Bypass Mixer", "HPLCOM Switch", "Right PGA Mixer"},
+	{"Right PGA Bypass Mixer", "HPRCOM Switch", "Right PGA Mixer"},
 
 
 	{"Right HPCOM Mux", "differential of HPROUT", "Right PGA Bypass Mixer"},
 	{"Right HPCOM Mux", "differential of HPROUT", "Right PGA Bypass Mixer"},
 	{"Right HPCOM Mux", "constant VCM", "Right PGA Bypass Mixer"},
 	{"Right HPCOM Mux", "constant VCM", "Right PGA Bypass Mixer"},
@@ -643,10 +685,11 @@ static const struct snd_soc_dapm_route intercon[] = {
 	{"Right HP Out", NULL, "Right PGA Bypass Mixer"},
 	{"Right HP Out", NULL, "Right PGA Bypass Mixer"},
 
 
 	/* Left Line2 Bypass */
 	/* Left Line2 Bypass */
-	{"Left Line2 Bypass Mixer", "Line Switch", "Left Line2L Mux"},
+	{"Left Line2 Bypass Mixer", "LineL Switch", "Left Line2L Mux"},
+	{"Left Line2 Bypass Mixer", "LineR Switch", "Left Line2L Mux"},
 	{"Left Line2 Bypass Mixer", "Mono Switch", "Left Line2L Mux"},
 	{"Left Line2 Bypass Mixer", "Mono Switch", "Left Line2L Mux"},
 	{"Left Line2 Bypass Mixer", "HP Switch", "Left Line2L Mux"},
 	{"Left Line2 Bypass Mixer", "HP Switch", "Left Line2L Mux"},
-	{"Left Line2 Bypass Mixer", "HPCOM Switch", "Left Line2L Mux"},
+	{"Left Line2 Bypass Mixer", "HPLCOM Switch", "Left Line2L Mux"},
 
 
 	{"Left HPCOM Mux", "differential of HPLOUT", "Left Line2 Bypass Mixer"},
 	{"Left HPCOM Mux", "differential of HPLOUT", "Left Line2 Bypass Mixer"},
 	{"Left HPCOM Mux", "constant VCM", "Left Line2 Bypass Mixer"},
 	{"Left HPCOM Mux", "constant VCM", "Left Line2 Bypass Mixer"},
@@ -657,10 +700,11 @@ static const struct snd_soc_dapm_route intercon[] = {
 	{"Left HP Out", NULL, "Left Line2 Bypass Mixer"},
 	{"Left HP Out", NULL, "Left Line2 Bypass Mixer"},
 
 
 	/* Right Line2 Bypass */
 	/* Right Line2 Bypass */
-	{"Right Line2 Bypass Mixer", "Line Switch", "Right Line2R Mux"},
+	{"Right Line2 Bypass Mixer", "LineL Switch", "Right Line2R Mux"},
+	{"Right Line2 Bypass Mixer", "LineR Switch", "Right Line2R Mux"},
 	{"Right Line2 Bypass Mixer", "Mono Switch", "Right Line2R Mux"},
 	{"Right Line2 Bypass Mixer", "Mono Switch", "Right Line2R Mux"},
 	{"Right Line2 Bypass Mixer", "HP Switch", "Right Line2R Mux"},
 	{"Right Line2 Bypass Mixer", "HP Switch", "Right Line2R Mux"},
-	{"Right Line2 Bypass Mixer", "HPCOM Switch", "Right Line2R Mux"},
+	{"Right Line2 Bypass Mixer", "HPRCOM Switch", "Right Line2R Mux"},
 
 
 	{"Right HPCOM Mux", "differential of HPROUT", "Right Line2 Bypass Mixer"},
 	{"Right HPCOM Mux", "differential of HPROUT", "Right Line2 Bypass Mixer"},
 	{"Right HPCOM Mux", "constant VCM", "Right Line2 Bypass Mixer"},
 	{"Right HPCOM Mux", "constant VCM", "Right Line2 Bypass Mixer"},
@@ -694,7 +738,8 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
 }
 }
 
 
 static int aic3x_hw_params(struct snd_pcm_substream *substream,
 static int aic3x_hw_params(struct snd_pcm_substream *substream,
-			   struct snd_pcm_hw_params *params)
+			   struct snd_pcm_hw_params *params,
+			   struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -846,6 +891,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct aic3x_priv *aic3x = codec->private_data;
 	struct aic3x_priv *aic3x = codec->private_data;
 	u8 iface_areg, iface_breg;
 	u8 iface_areg, iface_breg;
+	int delay = 0;
 
 
 	iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
 	iface_areg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
 	iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
 	iface_breg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
@@ -871,6 +917,8 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		       SND_SOC_DAIFMT_INV_MASK)) {
 		       SND_SOC_DAIFMT_INV_MASK)) {
 	case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
 	case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
 		break;
 		break;
+	case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
+		delay = 1;
 	case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
 	case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
 		iface_breg |= (0x01 << 6);
 		iface_breg |= (0x01 << 6);
 		break;
 		break;
@@ -887,6 +935,7 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 	/* set iface */
 	/* set iface */
 	aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
 	aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
 	aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
 	aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
+	aic3x_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -981,14 +1030,41 @@ int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio)
 }
 }
 EXPORT_SYMBOL_GPL(aic3x_get_gpio);
 EXPORT_SYMBOL_GPL(aic3x_get_gpio);
 
 
+void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
+				 int headset_debounce, int button_debounce)
+{
+	u8 val;
+
+	val = ((detect & AIC3X_HEADSET_DETECT_MASK)
+		<< AIC3X_HEADSET_DETECT_SHIFT) |
+	      ((headset_debounce & AIC3X_HEADSET_DEBOUNCE_MASK)
+		<< AIC3X_HEADSET_DEBOUNCE_SHIFT) |
+	      ((button_debounce & AIC3X_BUTTON_DEBOUNCE_MASK)
+		<< AIC3X_BUTTON_DEBOUNCE_SHIFT);
+
+	if (detect & AIC3X_HEADSET_DETECT_MASK)
+		val |= AIC3X_HEADSET_DETECT_ENABLED;
+
+	aic3x_write(codec, AIC3X_HEADSET_DETECT_CTRL_A, val);
+}
+EXPORT_SYMBOL_GPL(aic3x_set_headset_detection);
+
 int aic3x_headset_detected(struct snd_soc_codec *codec)
 int aic3x_headset_detected(struct snd_soc_codec *codec)
 {
 {
 	u8 val;
 	u8 val;
-	aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val);
-	return (val >> 2) & 1;
+	aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val);
+	return (val >> 4) & 1;
 }
 }
 EXPORT_SYMBOL_GPL(aic3x_headset_detected);
 EXPORT_SYMBOL_GPL(aic3x_headset_detected);
 
 
+int aic3x_button_pressed(struct snd_soc_codec *codec)
+{
+	u8 val;
+	aic3x_read(codec, AIC3X_HEADSET_DETECT_CTRL_B, &val);
+	return (val >> 5) & 1;
+}
+EXPORT_SYMBOL_GPL(aic3x_button_pressed);
+
 #define AIC3X_RATES	SNDRV_PCM_RATE_8000_96000
 #define AIC3X_RATES	SNDRV_PCM_RATE_8000_96000
 #define AIC3X_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
 #define AIC3X_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
 			 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
 			 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -1009,8 +1085,6 @@ struct snd_soc_dai aic3x_dai = {
 		.formats = AIC3X_FORMATS,},
 		.formats = AIC3X_FORMATS,},
 	.ops = {
 	.ops = {
 		.hw_params = aic3x_hw_params,
 		.hw_params = aic3x_hw_params,
-	},
-	.dai_ops = {
 		.digital_mute = aic3x_mute,
 		.digital_mute = aic3x_mute,
 		.set_sysclk = aic3x_set_dai_sysclk,
 		.set_sysclk = aic3x_set_dai_sysclk,
 		.set_fmt = aic3x_set_dai_fmt,
 		.set_fmt = aic3x_set_dai_fmt,
@@ -1152,7 +1226,7 @@ static int aic3x_init(struct snd_soc_device *socdev)
 
 
 	aic3x_add_controls(codec);
 	aic3x_add_controls(codec);
 	aic3x_add_widgets(codec);
 	aic3x_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "aic3x: failed to register card\n");
 		printk(KERN_ERR "aic3x: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -1341,6 +1415,18 @@ struct snd_soc_codec_device soc_codec_dev_aic3x = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x);
 EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x);
 
 
+static int __init aic3x_modinit(void)
+{
+	return snd_soc_register_dai(&aic3x_dai);
+}
+module_init(aic3x_modinit);
+
+static void __exit aic3x_exit(void)
+{
+	snd_soc_unregister_dai(&aic3x_dai);
+}
+module_exit(aic3x_exit);
+
 MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
 MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver");
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 59 - 1
sound/soc/codecs/tlv320aic3x.h

@@ -35,11 +35,15 @@
 #define AIC3X_ASD_INTF_CTRLA		8
 #define AIC3X_ASD_INTF_CTRLA		8
 /* Audio serial data interface control register B */
 /* Audio serial data interface control register B */
 #define AIC3X_ASD_INTF_CTRLB		9
 #define AIC3X_ASD_INTF_CTRLB		9
+/* Audio serial data interface control register C */
+#define AIC3X_ASD_INTF_CTRLC		10
 /* Audio overflow status and PLL R value programming register */
 /* Audio overflow status and PLL R value programming register */
 #define AIC3X_OVRF_STATUS_AND_PLLR_REG	11
 #define AIC3X_OVRF_STATUS_AND_PLLR_REG	11
 /* Audio codec digital filter control register */
 /* Audio codec digital filter control register */
 #define AIC3X_CODEC_DFILT_CTRL		12
 #define AIC3X_CODEC_DFILT_CTRL		12
-
+/* Headset/button press detection register */
+#define AIC3X_HEADSET_DETECT_CTRL_A	13
+#define AIC3X_HEADSET_DETECT_CTRL_B	14
 /* ADC PGA Gain control registers */
 /* ADC PGA Gain control registers */
 #define LADC_VOL			15
 #define LADC_VOL			15
 #define RADC_VOL			16
 #define RADC_VOL			16
@@ -48,7 +52,9 @@
 #define MIC3LR_2_RADC_CTRL		18
 #define MIC3LR_2_RADC_CTRL		18
 /* Line1 Input control registers */
 /* Line1 Input control registers */
 #define LINE1L_2_LADC_CTRL		19
 #define LINE1L_2_LADC_CTRL		19
+#define LINE1R_2_LADC_CTRL		21
 #define LINE1R_2_RADC_CTRL		22
 #define LINE1R_2_RADC_CTRL		22
+#define LINE1L_2_RADC_CTRL		24
 /* Line2 Input control registers */
 /* Line2 Input control registers */
 #define LINE2L_2_LADC_CTRL		20
 #define LINE2L_2_LADC_CTRL		20
 #define LINE2R_2_RADC_CTRL		23
 #define LINE2R_2_RADC_CTRL		23
@@ -79,6 +85,8 @@
 #define LINE2L_2_HPLOUT_VOL		45
 #define LINE2L_2_HPLOUT_VOL		45
 #define LINE2R_2_HPROUT_VOL		62
 #define LINE2R_2_HPROUT_VOL		62
 #define PGAL_2_HPLOUT_VOL		46
 #define PGAL_2_HPLOUT_VOL		46
+#define PGAL_2_HPROUT_VOL		60
+#define PGAR_2_HPLOUT_VOL		49
 #define PGAR_2_HPROUT_VOL		63
 #define PGAR_2_HPROUT_VOL		63
 #define DACL1_2_HPLOUT_VOL		47
 #define DACL1_2_HPLOUT_VOL		47
 #define DACR1_2_HPROUT_VOL		64
 #define DACR1_2_HPROUT_VOL		64
@@ -88,6 +96,8 @@
 #define LINE2L_2_HPLCOM_VOL		52
 #define LINE2L_2_HPLCOM_VOL		52
 #define LINE2R_2_HPRCOM_VOL		69
 #define LINE2R_2_HPRCOM_VOL		69
 #define PGAL_2_HPLCOM_VOL		53
 #define PGAL_2_HPLCOM_VOL		53
+#define PGAR_2_HPLCOM_VOL		56
+#define PGAL_2_HPRCOM_VOL		67
 #define PGAR_2_HPRCOM_VOL		70
 #define PGAR_2_HPRCOM_VOL		70
 #define DACL1_2_HPLCOM_VOL		54
 #define DACL1_2_HPLCOM_VOL		54
 #define DACR1_2_HPRCOM_VOL		71
 #define DACR1_2_HPRCOM_VOL		71
@@ -103,11 +113,17 @@
 #define MONOLOPM_CTRL			79
 #define MONOLOPM_CTRL			79
 /* Line Output Plus/Minus control registers */
 /* Line Output Plus/Minus control registers */
 #define LINE2L_2_LLOPM_VOL		80
 #define LINE2L_2_LLOPM_VOL		80
+#define LINE2L_2_RLOPM_VOL		87
+#define LINE2R_2_LLOPM_VOL		83
 #define LINE2R_2_RLOPM_VOL		90
 #define LINE2R_2_RLOPM_VOL		90
 #define PGAL_2_LLOPM_VOL		81
 #define PGAL_2_LLOPM_VOL		81
+#define PGAL_2_RLOPM_VOL		88
+#define PGAR_2_LLOPM_VOL		84
 #define PGAR_2_RLOPM_VOL		91
 #define PGAR_2_RLOPM_VOL		91
 #define DACL1_2_LLOPM_VOL		82
 #define DACL1_2_LLOPM_VOL		82
+#define DACL1_2_RLOPM_VOL		89
 #define DACR1_2_RLOPM_VOL		92
 #define DACR1_2_RLOPM_VOL		92
+#define DACR1_2_LLOPM_VOL		85
 #define LLOPM_CTRL			86
 #define LLOPM_CTRL			86
 #define RLOPM_CTRL			93
 #define RLOPM_CTRL			93
 /* GPIO/IRQ registers */
 /* GPIO/IRQ registers */
@@ -221,7 +237,49 @@ enum {
 
 
 void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
 void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
 int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
 int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
+
+/* headset detection / button API */
+
+/* The AIC3x supports detection of stereo headsets (GND + left + right signal)
+ * and cellular headsets (GND + speaker output + microphone input).
+ * It is recommended to enable MIC bias for this function to work properly.
+ * For more information, please refer to the datasheet. */
+enum {
+	AIC3X_HEADSET_DETECT_OFF	= 0,
+	AIC3X_HEADSET_DETECT_STEREO	= 1,
+	AIC3X_HEADSET_DETECT_CELLULAR   = 2,
+	AIC3X_HEADSET_DETECT_BOTH	= 3
+};
+
+enum {
+	AIC3X_HEADSET_DEBOUNCE_16MS	= 0,
+	AIC3X_HEADSET_DEBOUNCE_32MS	= 1,
+	AIC3X_HEADSET_DEBOUNCE_64MS	= 2,
+	AIC3X_HEADSET_DEBOUNCE_128MS	= 3,
+	AIC3X_HEADSET_DEBOUNCE_256MS	= 4,
+	AIC3X_HEADSET_DEBOUNCE_512MS	= 5
+};
+
+enum {
+	AIC3X_BUTTON_DEBOUNCE_0MS	= 0,
+	AIC3X_BUTTON_DEBOUNCE_8MS	= 1,
+	AIC3X_BUTTON_DEBOUNCE_16MS	= 2,
+	AIC3X_BUTTON_DEBOUNCE_32MS	= 3
+};
+
+#define AIC3X_HEADSET_DETECT_ENABLED	0x80
+#define AIC3X_HEADSET_DETECT_SHIFT	5
+#define AIC3X_HEADSET_DETECT_MASK	3
+#define AIC3X_HEADSET_DEBOUNCE_SHIFT	2
+#define AIC3X_HEADSET_DEBOUNCE_MASK	7
+#define AIC3X_BUTTON_DEBOUNCE_SHIFT 	0
+#define AIC3X_BUTTON_DEBOUNCE_MASK	3
+
+/* see the enums above for valid parameters to this function */
+void aic3x_set_headset_detection(struct snd_soc_codec *codec, int detect,
+				 int headset_debounce, int button_debounce);
 int aic3x_headset_detected(struct snd_soc_codec *codec);
 int aic3x_headset_detected(struct snd_soc_codec *codec);
+int aic3x_button_pressed(struct snd_soc_codec *codec);
 
 
 struct aic3x_setup_data {
 struct aic3x_setup_data {
 	int i2c_bus;
 	int i2c_bus;

+ 1317 - 0
sound/soc/codecs/twl4030.c

@@ -0,0 +1,1317 @@
+/*
+ * ALSA SoC TWL4030 codec driver
+ *
+ * Author:      Steve Sakoman, <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "twl4030.h"
+
+/*
+ * twl4030 register cache & default register settings
+ */
+static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
+	0x00, /* this register not used		*/
+	0x93, /* REG_CODEC_MODE		(0x1)	*/
+	0xc3, /* REG_OPTION		(0x2)	*/
+	0x00, /* REG_UNKNOWN		(0x3)	*/
+	0x00, /* REG_MICBIAS_CTL	(0x4)	*/
+	0x20, /* REG_ANAMICL		(0x5)	*/
+	0x00, /* REG_ANAMICR		(0x6)	*/
+	0x00, /* REG_AVADC_CTL		(0x7)	*/
+	0x00, /* REG_ADCMICSEL		(0x8)	*/
+	0x00, /* REG_DIGMIXING		(0x9)	*/
+	0x0c, /* REG_ATXL1PGA		(0xA)	*/
+	0x0c, /* REG_ATXR1PGA		(0xB)	*/
+	0x00, /* REG_AVTXL2PGA		(0xC)	*/
+	0x00, /* REG_AVTXR2PGA		(0xD)	*/
+	0x01, /* REG_AUDIO_IF		(0xE)	*/
+	0x00, /* REG_VOICE_IF		(0xF)	*/
+	0x00, /* REG_ARXR1PGA		(0x10)	*/
+	0x00, /* REG_ARXL1PGA		(0x11)	*/
+	0x6c, /* REG_ARXR2PGA		(0x12)	*/
+	0x6c, /* REG_ARXL2PGA		(0x13)	*/
+	0x00, /* REG_VRXPGA		(0x14)	*/
+	0x00, /* REG_VSTPGA		(0x15)	*/
+	0x00, /* REG_VRX2ARXPGA		(0x16)	*/
+	0x0c, /* REG_AVDAC_CTL		(0x17)	*/
+	0x00, /* REG_ARX2VTXPGA		(0x18)	*/
+	0x00, /* REG_ARXL1_APGA_CTL	(0x19)	*/
+	0x00, /* REG_ARXR1_APGA_CTL	(0x1A)	*/
+	0x4b, /* REG_ARXL2_APGA_CTL	(0x1B)	*/
+	0x4b, /* REG_ARXR2_APGA_CTL	(0x1C)	*/
+	0x00, /* REG_ATX2ARXPGA		(0x1D)	*/
+	0x00, /* REG_BT_IF		(0x1E)	*/
+	0x00, /* REG_BTPGA		(0x1F)	*/
+	0x00, /* REG_BTSTPGA		(0x20)	*/
+	0x00, /* REG_EAR_CTL		(0x21)	*/
+	0x24, /* REG_HS_SEL		(0x22)	*/
+	0x0a, /* REG_HS_GAIN_SET	(0x23)	*/
+	0x00, /* REG_HS_POPN_SET	(0x24)	*/
+	0x00, /* REG_PREDL_CTL		(0x25)	*/
+	0x00, /* REG_PREDR_CTL		(0x26)	*/
+	0x00, /* REG_PRECKL_CTL		(0x27)	*/
+	0x00, /* REG_PRECKR_CTL		(0x28)	*/
+	0x00, /* REG_HFL_CTL		(0x29)	*/
+	0x00, /* REG_HFR_CTL		(0x2A)	*/
+	0x00, /* REG_ALC_CTL		(0x2B)	*/
+	0x00, /* REG_ALC_SET1		(0x2C)	*/
+	0x00, /* REG_ALC_SET2		(0x2D)	*/
+	0x00, /* REG_BOOST_CTL		(0x2E)	*/
+	0x00, /* REG_SOFTVOL_CTL	(0x2F)	*/
+	0x00, /* REG_DTMF_FREQSEL	(0x30)	*/
+	0x00, /* REG_DTMF_TONEXT1H	(0x31)	*/
+	0x00, /* REG_DTMF_TONEXT1L	(0x32)	*/
+	0x00, /* REG_DTMF_TONEXT2H	(0x33)	*/
+	0x00, /* REG_DTMF_TONEXT2L	(0x34)	*/
+	0x00, /* REG_DTMF_TONOFF	(0x35)	*/
+	0x00, /* REG_DTMF_WANONOFF	(0x36)	*/
+	0x00, /* REG_I2S_RX_SCRAMBLE_H	(0x37)	*/
+	0x00, /* REG_I2S_RX_SCRAMBLE_M	(0x38)	*/
+	0x00, /* REG_I2S_RX_SCRAMBLE_L	(0x39)	*/
+	0x16, /* REG_APLL_CTL		(0x3A)	*/
+	0x00, /* REG_DTMF_CTL		(0x3B)	*/
+	0x00, /* REG_DTMF_PGA_CTL2	(0x3C)	*/
+	0x00, /* REG_DTMF_PGA_CTL1	(0x3D)	*/
+	0x00, /* REG_MISC_SET_1		(0x3E)	*/
+	0x00, /* REG_PCMBTMUX		(0x3F)	*/
+	0x00, /* not used		(0x40)	*/
+	0x00, /* not used		(0x41)	*/
+	0x00, /* not used		(0x42)	*/
+	0x00, /* REG_RX_PATH_SEL	(0x43)	*/
+	0x00, /* REG_VDL_APGA_CTL	(0x44)	*/
+	0x00, /* REG_VIBRA_CTL		(0x45)	*/
+	0x00, /* REG_VIBRA_SET		(0x46)	*/
+	0x00, /* REG_VIBRA_PWM_SET	(0x47)	*/
+	0x00, /* REG_ANAMIC_GAIN	(0x48)	*/
+	0x00, /* REG_MISC_SET_2		(0x49)	*/
+};
+
+/*
+ * read twl4030 register cache
+ */
+static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u8 *cache = codec->reg_cache;
+
+	return cache[reg];
+}
+
+/*
+ * write twl4030 register cache
+ */
+static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec,
+						u8 reg, u8 value)
+{
+	u8 *cache = codec->reg_cache;
+
+	if (reg >= TWL4030_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the twl4030 register space
+ */
+static int twl4030_write(struct snd_soc_codec *codec,
+			unsigned int reg, unsigned int value)
+{
+	twl4030_write_reg_cache(codec, reg, value);
+	return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
+}
+
+static void twl4030_clear_codecpdz(struct snd_soc_codec *codec)
+{
+	u8 mode;
+
+	mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
+	twl4030_write(codec, TWL4030_REG_CODEC_MODE,
+		mode & ~TWL4030_CODECPDZ);
+
+	/* REVISIT: this delay is present in TI sample drivers */
+	/* but there seems to be no TRM requirement for it     */
+	udelay(10);
+}
+
+static void twl4030_set_codecpdz(struct snd_soc_codec *codec)
+{
+	u8 mode;
+
+	mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
+	twl4030_write(codec, TWL4030_REG_CODEC_MODE,
+		mode | TWL4030_CODECPDZ);
+
+	/* REVISIT: this delay is present in TI sample drivers */
+	/* but there seems to be no TRM requirement for it     */
+	udelay(10);
+}
+
+static void twl4030_init_chip(struct snd_soc_codec *codec)
+{
+	int i;
+
+	/* clear CODECPDZ prior to setting register defaults */
+	twl4030_clear_codecpdz(codec);
+
+	/* set all audio section registers to reasonable defaults */
+	for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
+		twl4030_write(codec, i,	twl4030_reg[i]);
+
+}
+
+/* Earpiece */
+static const char *twl4030_earpiece_texts[] =
+		{"Off", "DACL1", "DACL2", "Invalid", "DACR1"};
+
+static const struct soc_enum twl4030_earpiece_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1,
+			ARRAY_SIZE(twl4030_earpiece_texts),
+			twl4030_earpiece_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_earpiece_control =
+SOC_DAPM_ENUM("Route", twl4030_earpiece_enum);
+
+/* PreDrive Left */
+static const char *twl4030_predrivel_texts[] =
+		{"Off", "DACL1", "DACL2", "Invalid", "DACR2"};
+
+static const struct soc_enum twl4030_predrivel_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1,
+			ARRAY_SIZE(twl4030_predrivel_texts),
+			twl4030_predrivel_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_predrivel_control =
+SOC_DAPM_ENUM("Route", twl4030_predrivel_enum);
+
+/* PreDrive Right */
+static const char *twl4030_predriver_texts[] =
+		{"Off", "DACR1", "DACR2", "Invalid", "DACL2"};
+
+static const struct soc_enum twl4030_predriver_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1,
+			ARRAY_SIZE(twl4030_predriver_texts),
+			twl4030_predriver_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_predriver_control =
+SOC_DAPM_ENUM("Route", twl4030_predriver_enum);
+
+/* Headset Left */
+static const char *twl4030_hsol_texts[] =
+		{"Off", "DACL1", "DACL2"};
+
+static const struct soc_enum twl4030_hsol_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 1,
+			ARRAY_SIZE(twl4030_hsol_texts),
+			twl4030_hsol_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_hsol_control =
+SOC_DAPM_ENUM("Route", twl4030_hsol_enum);
+
+/* Headset Right */
+static const char *twl4030_hsor_texts[] =
+		{"Off", "DACR1", "DACR2"};
+
+static const struct soc_enum twl4030_hsor_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 4,
+			ARRAY_SIZE(twl4030_hsor_texts),
+			twl4030_hsor_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_hsor_control =
+SOC_DAPM_ENUM("Route", twl4030_hsor_enum);
+
+/* Carkit Left */
+static const char *twl4030_carkitl_texts[] =
+		{"Off", "DACL1", "DACL2"};
+
+static const struct soc_enum twl4030_carkitl_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 1,
+			ARRAY_SIZE(twl4030_carkitl_texts),
+			twl4030_carkitl_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_carkitl_control =
+SOC_DAPM_ENUM("Route", twl4030_carkitl_enum);
+
+/* Carkit Right */
+static const char *twl4030_carkitr_texts[] =
+		{"Off", "DACR1", "DACR2"};
+
+static const struct soc_enum twl4030_carkitr_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 1,
+			ARRAY_SIZE(twl4030_carkitr_texts),
+			twl4030_carkitr_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_carkitr_control =
+SOC_DAPM_ENUM("Route", twl4030_carkitr_enum);
+
+/* Handsfree Left */
+static const char *twl4030_handsfreel_texts[] =
+		{"Voice", "DACL1", "DACL2", "DACR2"};
+
+static const struct soc_enum twl4030_handsfreel_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0,
+			ARRAY_SIZE(twl4030_handsfreel_texts),
+			twl4030_handsfreel_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control =
+SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum);
+
+/* Handsfree Right */
+static const char *twl4030_handsfreer_texts[] =
+		{"Voice", "DACR1", "DACR2", "DACL2"};
+
+static const struct soc_enum twl4030_handsfreer_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0,
+			ARRAY_SIZE(twl4030_handsfreer_texts),
+			twl4030_handsfreer_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
+SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	int ret = 0;
+	int val;
+
+	switch (e->reg) {
+	case TWL4030_REG_PREDL_CTL:
+	case TWL4030_REG_PREDR_CTL:
+	case TWL4030_REG_EAR_CTL:
+		val = w->value >> e->shift_l;
+		if (val == 3) {
+			printk(KERN_WARNING
+			"Invalid MUX setting for register 0x%02x (%d)\n",
+			      e->reg, val);
+			ret = -1;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int handsfree_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value;
+	unsigned char hs_ctl;
+
+	hs_ctl = twl4030_read_reg_cache(w->codec, e->reg);
+
+	if (hs_ctl & TWL4030_HF_CTL_REF_EN) {
+		hs_ctl |= TWL4030_HF_CTL_RAMP_EN;
+		twl4030_write(w->codec, e->reg, hs_ctl);
+		hs_ctl |= TWL4030_HF_CTL_LOOP_EN;
+		twl4030_write(w->codec, e->reg, hs_ctl);
+		hs_ctl |= TWL4030_HF_CTL_HB_EN;
+		twl4030_write(w->codec, e->reg, hs_ctl);
+	} else {
+		hs_ctl &= ~(TWL4030_HF_CTL_RAMP_EN | TWL4030_HF_CTL_LOOP_EN
+				| TWL4030_HF_CTL_HB_EN);
+		twl4030_write(w->codec, e->reg, hs_ctl);
+	}
+
+	return 0;
+}
+
+/*
+ * Some of the gain controls in TWL (mostly those which are associated with
+ * the outputs) are implemented in an interesting way:
+ * 0x0 : Power down (mute)
+ * 0x1 : 6dB
+ * 0x2 : 0 dB
+ * 0x3 : -6 dB
+ * Inverting not going to help with these.
+ * Custom volsw and volsw_2r get/put functions to handle these gain bits.
+ */
+#define SOC_DOUBLE_TLV_TWL4030(xname, xreg, shift_left, shift_right, xmax,\
+			       xinvert, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_get_volsw_twl4030, \
+	.put = snd_soc_put_volsw_twl4030, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .shift = shift_left, .rshift = shift_right,\
+		 .max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_R_TLV_TWL4030(xname, reg_left, reg_right, xshift, xmax,\
+				 xinvert, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw_2r, \
+	.get = snd_soc_get_volsw_r2_twl4030,\
+	.put = snd_soc_put_volsw_r2_twl4030, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+		 .rshift = xshift, .max = xmax, .invert = xinvert} }
+#define SOC_SINGLE_TLV_TWL4030(xname, xreg, xshift, xmax, xinvert, tlv_array) \
+	SOC_DOUBLE_TLV_TWL4030(xname, xreg, xshift, xshift, xmax, \
+			       xinvert, tlv_array)
+
+static int snd_soc_get_volsw_twl4030(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	int mask = (1 << fls(max)) - 1;
+
+	ucontrol->value.integer.value[0] =
+		(snd_soc_read(codec, reg) >> shift) & mask;
+	if (ucontrol->value.integer.value[0])
+		ucontrol->value.integer.value[0] =
+			max + 1 - ucontrol->value.integer.value[0];
+
+	if (shift != rshift) {
+		ucontrol->value.integer.value[1] =
+			(snd_soc_read(codec, reg) >> rshift) & mask;
+		if (ucontrol->value.integer.value[1])
+			ucontrol->value.integer.value[1] =
+				max + 1 - ucontrol->value.integer.value[1];
+	}
+
+	return 0;
+}
+
+static int snd_soc_put_volsw_twl4030(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int rshift = mc->rshift;
+	int max = mc->max;
+	int mask = (1 << fls(max)) - 1;
+	unsigned short val, val2, val_mask;
+
+	val = (ucontrol->value.integer.value[0] & mask);
+
+	val_mask = mask << shift;
+	if (val)
+		val = max + 1 - val;
+	val = val << shift;
+	if (shift != rshift) {
+		val2 = (ucontrol->value.integer.value[1] & mask);
+		val_mask |= mask << rshift;
+		if (val2)
+			val2 = max + 1 - val2;
+		val |= val2 << rshift;
+	}
+	return snd_soc_update_bits(codec, reg, val_mask, val);
+}
+
+static int snd_soc_get_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	int max = mc->max;
+	int mask = (1<<fls(max))-1;
+
+	ucontrol->value.integer.value[0] =
+		(snd_soc_read(codec, reg) >> shift) & mask;
+	ucontrol->value.integer.value[1] =
+		(snd_soc_read(codec, reg2) >> shift) & mask;
+
+	if (ucontrol->value.integer.value[0])
+		ucontrol->value.integer.value[0] =
+			max + 1 - ucontrol->value.integer.value[0];
+	if (ucontrol->value.integer.value[1])
+		ucontrol->value.integer.value[1] =
+			max + 1 - ucontrol->value.integer.value[1];
+
+	return 0;
+}
+
+static int snd_soc_put_volsw_r2_twl4030(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	int max = mc->max;
+	int mask = (1 << fls(max)) - 1;
+	int err;
+	unsigned short val, val2, val_mask;
+
+	val_mask = mask << shift;
+	val = (ucontrol->value.integer.value[0] & mask);
+	val2 = (ucontrol->value.integer.value[1] & mask);
+
+	if (val)
+		val = max + 1 - val;
+	if (val2)
+		val2 = max + 1 - val2;
+
+	val = val << shift;
+	val2 = val2 << shift;
+
+	err = snd_soc_update_bits(codec, reg, val_mask, val);
+	if (err < 0)
+		return err;
+
+	err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+	return err;
+}
+
+static int twl4030_get_left_input(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = kcontrol->private_data;
+	u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+	int result = 0;
+
+	/* one bit must be set a time */
+	reg &= TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
+			| TWL4030_MAINMIC_EN;
+	if (reg != 0) {
+		result++;
+		while ((reg & 1) == 0) {
+			result++;
+			reg >>= 1;
+		}
+	}
+
+	ucontrol->value.integer.value[0] = result;
+	return 0;
+}
+
+static int twl4030_put_left_input(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+	u8 anamicl, micbias, avadc_ctl;
+
+	anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+	anamicl &= ~(TWL4030_CKMIC_EN | TWL4030_AUXL_EN | TWL4030_HSMIC_EN
+			| TWL4030_MAINMIC_EN);
+	micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
+	micbias &= ~(TWL4030_HSMICBIAS_EN | TWL4030_MICBIAS1_EN);
+	avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
+
+	switch (value) {
+	case 1:
+		anamicl |= TWL4030_MAINMIC_EN;
+		micbias |= TWL4030_MICBIAS1_EN;
+		break;
+	case 2:
+		anamicl |= TWL4030_HSMIC_EN;
+		micbias |= TWL4030_HSMICBIAS_EN;
+		break;
+	case 3:
+		anamicl |= TWL4030_AUXL_EN;
+		break;
+	case 4:
+		anamicl |= TWL4030_CKMIC_EN;
+		break;
+	default:
+		break;
+	}
+
+	/* If some input is selected, enable amp and ADC */
+	if (value != 0) {
+		anamicl |= TWL4030_MICAMPL_EN;
+		avadc_ctl |= TWL4030_ADCL_EN;
+	} else {
+		anamicl &= ~TWL4030_MICAMPL_EN;
+		avadc_ctl &= ~TWL4030_ADCL_EN;
+	}
+
+	twl4030_write(codec, TWL4030_REG_ANAMICL, anamicl);
+	twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
+	twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
+
+	return 1;
+}
+
+static int twl4030_get_right_input(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = kcontrol->private_data;
+	u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
+	int value = 0;
+
+	reg &= TWL4030_SUBMIC_EN|TWL4030_AUXR_EN;
+	switch (reg) {
+	case TWL4030_SUBMIC_EN:
+		value = 1;
+		break;
+	case TWL4030_AUXR_EN:
+		value = 2;
+		break;
+	default:
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = value;
+	return 0;
+}
+
+static int twl4030_put_right_input(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+	u8 anamicr, micbias, avadc_ctl;
+
+	anamicr = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICR);
+	anamicr &= ~(TWL4030_SUBMIC_EN|TWL4030_AUXR_EN);
+	micbias = twl4030_read_reg_cache(codec, TWL4030_REG_MICBIAS_CTL);
+	micbias &= ~TWL4030_MICBIAS2_EN;
+	avadc_ctl = twl4030_read_reg_cache(codec, TWL4030_REG_AVADC_CTL);
+
+	switch (value) {
+	case 1:
+		anamicr |= TWL4030_SUBMIC_EN;
+		micbias |= TWL4030_MICBIAS2_EN;
+		break;
+	case 2:
+		anamicr |= TWL4030_AUXR_EN;
+		break;
+	default:
+		break;
+	}
+
+	if (value != 0) {
+		anamicr |= TWL4030_MICAMPR_EN;
+		avadc_ctl |= TWL4030_ADCR_EN;
+	} else {
+		anamicr &= ~TWL4030_MICAMPR_EN;
+		avadc_ctl &= ~TWL4030_ADCR_EN;
+	}
+
+	twl4030_write(codec, TWL4030_REG_ANAMICR, anamicr);
+	twl4030_write(codec, TWL4030_REG_MICBIAS_CTL, micbias);
+	twl4030_write(codec, TWL4030_REG_AVADC_CTL, avadc_ctl);
+
+	return 1;
+}
+
+static const char *twl4030_left_in_sel[] = {
+	"None",
+	"Main Mic",
+	"Headset Mic",
+	"Line In",
+	"Carkit Mic",
+};
+
+static const char *twl4030_right_in_sel[] = {
+	"None",
+	"Sub Mic",
+	"Line In",
+};
+
+static const struct soc_enum twl4030_left_input_mux =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_left_in_sel),
+		twl4030_left_in_sel);
+
+static const struct soc_enum twl4030_right_input_mux =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl4030_right_in_sel),
+		twl4030_right_in_sel);
+
+/*
+ * FGAIN volume control:
+ * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
+ */
+static DECLARE_TLV_DB_SCALE(digital_fine_tlv, -6300, 100, 1);
+
+/*
+ * CGAIN volume control:
+ * 0 dB to 12 dB in 6 dB steps
+ * value 2 and 3 means 12 dB
+ */
+static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0);
+
+/*
+ * Analog playback gain
+ * -24 dB to 12 dB in 2 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(analog_tlv, -2400, 200, 0);
+
+/*
+ * Gain controls tied to outputs
+ * -6 dB to 6 dB in 6 dB steps (mute instead of -12)
+ */
+static DECLARE_TLV_DB_SCALE(output_tvl, -1200, 600, 1);
+
+/*
+ * Capture gain after the ADCs
+ * from 0 dB to 31 dB in 1 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
+
+/*
+ * Gain control for input amplifiers
+ * 0 dB to 30 dB in 6 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new twl4030_snd_controls[] = {
+	/* Common playback gain controls */
+	SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
+		TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
+		0, 0x3f, 0, digital_fine_tlv),
+	SOC_DOUBLE_R_TLV("DAC2 Digital Fine Playback Volume",
+		TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+		0, 0x3f, 0, digital_fine_tlv),
+
+	SOC_DOUBLE_R_TLV("DAC1 Digital Coarse Playback Volume",
+		TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
+		6, 0x2, 0, digital_coarse_tlv),
+	SOC_DOUBLE_R_TLV("DAC2 Digital Coarse Playback Volume",
+		TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+		6, 0x2, 0, digital_coarse_tlv),
+
+	SOC_DOUBLE_R_TLV("DAC1 Analog Playback Volume",
+		TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
+		3, 0x12, 1, analog_tlv),
+	SOC_DOUBLE_R_TLV("DAC2 Analog Playback Volume",
+		TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
+		3, 0x12, 1, analog_tlv),
+	SOC_DOUBLE_R("DAC1 Analog Playback Switch",
+		TWL4030_REG_ARXL1_APGA_CTL, TWL4030_REG_ARXR1_APGA_CTL,
+		1, 1, 0),
+	SOC_DOUBLE_R("DAC2 Analog Playback Switch",
+		TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
+		1, 1, 0),
+
+	/* Separate output gain controls */
+	SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume",
+		TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL,
+		4, 3, 0, output_tvl),
+
+	SOC_DOUBLE_TLV_TWL4030("Headset Playback Volume",
+		TWL4030_REG_HS_GAIN_SET, 0, 2, 3, 0, output_tvl),
+
+	SOC_DOUBLE_R_TLV_TWL4030("Carkit Playback Volume",
+		TWL4030_REG_PRECKL_CTL, TWL4030_REG_PRECKR_CTL,
+		4, 3, 0, output_tvl),
+
+	SOC_SINGLE_TLV_TWL4030("Earpiece Playback Volume",
+		TWL4030_REG_EAR_CTL, 4, 3, 0, output_tvl),
+
+	/* Common capture gain controls */
+	SOC_DOUBLE_R_TLV("Capture Volume",
+		TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
+		0, 0x1f, 0, digital_capture_tlv),
+
+	SOC_DOUBLE_TLV("Input Boost Volume", TWL4030_REG_ANAMIC_GAIN,
+		0, 3, 5, 0, input_gain_tlv),
+
+	/* Input source controls */
+	SOC_ENUM_EXT("Left Input Source", twl4030_left_input_mux,
+		twl4030_get_left_input, twl4030_put_left_input),
+	SOC_ENUM_EXT("Right Input Source", twl4030_right_input_mux,
+		twl4030_get_right_input, twl4030_put_right_input),
+};
+
+/* add non dapm controls */
+static int twl4030_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				  snd_soc_cnew(&twl4030_snd_controls[i],
+						codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("INL"),
+	SND_SOC_DAPM_INPUT("INR"),
+
+	SND_SOC_DAPM_OUTPUT("OUTL"),
+	SND_SOC_DAPM_OUTPUT("OUTR"),
+	SND_SOC_DAPM_OUTPUT("EARPIECE"),
+	SND_SOC_DAPM_OUTPUT("PREDRIVEL"),
+	SND_SOC_DAPM_OUTPUT("PREDRIVER"),
+	SND_SOC_DAPM_OUTPUT("HSOL"),
+	SND_SOC_DAPM_OUTPUT("HSOR"),
+	SND_SOC_DAPM_OUTPUT("CARKITL"),
+	SND_SOC_DAPM_OUTPUT("CARKITR"),
+	SND_SOC_DAPM_OUTPUT("HFL"),
+	SND_SOC_DAPM_OUTPUT("HFR"),
+
+	/* DACs */
+	SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback",
+			TWL4030_REG_AVDAC_CTL, 0, 0),
+	SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback",
+			TWL4030_REG_AVDAC_CTL, 1, 0),
+	SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback",
+			TWL4030_REG_AVDAC_CTL, 2, 0),
+	SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback",
+			TWL4030_REG_AVDAC_CTL, 3, 0),
+
+	/* Analog PGAs */
+	SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL,
+			0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("ARXL1_APGA", TWL4030_REG_ARXL1_APGA_CTL,
+			0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("ARXR2_APGA", TWL4030_REG_ARXR2_APGA_CTL,
+			0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL,
+			0, 0, NULL, 0),
+
+	/* Output MUX controls */
+	/* Earpiece */
+	SND_SOC_DAPM_MUX_E("Earpiece Mux", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_earpiece_control, outmixer_event,
+		SND_SOC_DAPM_PRE_REG),
+	/* PreDrivL/R */
+	SND_SOC_DAPM_MUX_E("PredriveL Mux", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_predrivel_control, outmixer_event,
+		SND_SOC_DAPM_PRE_REG),
+	SND_SOC_DAPM_MUX_E("PredriveR Mux", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_predriver_control, outmixer_event,
+		SND_SOC_DAPM_PRE_REG),
+	/* HeadsetL/R */
+	SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_hsol_control),
+	SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_hsor_control),
+	/* CarkitL/R */
+	SND_SOC_DAPM_MUX("CarkitL Mux", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_carkitl_control),
+	SND_SOC_DAPM_MUX("CarkitR Mux", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_carkitr_control),
+	/* HandsfreeL/R */
+	SND_SOC_DAPM_MUX_E("HandsfreeL Mux", TWL4030_REG_HFL_CTL, 5, 0,
+		&twl4030_dapm_handsfreel_control, handsfree_event,
+		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("HandsfreeR Mux", TWL4030_REG_HFR_CTL, 5, 0,
+		&twl4030_dapm_handsfreer_control, handsfree_event,
+		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{"ARXL1_APGA", NULL, "DAC Left1"},
+	{"ARXR1_APGA", NULL, "DAC Right1"},
+	{"ARXL2_APGA", NULL, "DAC Left2"},
+	{"ARXR2_APGA", NULL, "DAC Right2"},
+
+	/* Internal playback routings */
+	/* Earpiece */
+	{"Earpiece Mux", "DACL1", "ARXL1_APGA"},
+	{"Earpiece Mux", "DACL2", "ARXL2_APGA"},
+	{"Earpiece Mux", "DACR1", "ARXR1_APGA"},
+	/* PreDrivL */
+	{"PredriveL Mux", "DACL1", "ARXL1_APGA"},
+	{"PredriveL Mux", "DACL2", "ARXL2_APGA"},
+	{"PredriveL Mux", "DACR2", "ARXR2_APGA"},
+	/* PreDrivR */
+	{"PredriveR Mux", "DACR1", "ARXR1_APGA"},
+	{"PredriveR Mux", "DACR2", "ARXR2_APGA"},
+	{"PredriveR Mux", "DACL2", "ARXL2_APGA"},
+	/* HeadsetL */
+	{"HeadsetL Mux", "DACL1", "ARXL1_APGA"},
+	{"HeadsetL Mux", "DACL2", "ARXL2_APGA"},
+	/* HeadsetR */
+	{"HeadsetR Mux", "DACR1", "ARXR1_APGA"},
+	{"HeadsetR Mux", "DACR2", "ARXR2_APGA"},
+	/* CarkitL */
+	{"CarkitL Mux", "DACL1", "ARXL1_APGA"},
+	{"CarkitL Mux", "DACL2", "ARXL2_APGA"},
+	/* CarkitR */
+	{"CarkitR Mux", "DACR1", "ARXR1_APGA"},
+	{"CarkitR Mux", "DACR2", "ARXR2_APGA"},
+	/* HandsfreeL */
+	{"HandsfreeL Mux", "DACL1", "ARXL1_APGA"},
+	{"HandsfreeL Mux", "DACL2", "ARXL2_APGA"},
+	{"HandsfreeL Mux", "DACR2", "ARXR2_APGA"},
+	/* HandsfreeR */
+	{"HandsfreeR Mux", "DACR1", "ARXR1_APGA"},
+	{"HandsfreeR Mux", "DACR2", "ARXR2_APGA"},
+	{"HandsfreeR Mux", "DACL2", "ARXL2_APGA"},
+
+	/* outputs */
+	{"OUTL", NULL, "ARXL2_APGA"},
+	{"OUTR", NULL, "ARXR2_APGA"},
+	{"EARPIECE", NULL, "Earpiece Mux"},
+	{"PREDRIVEL", NULL, "PredriveL Mux"},
+	{"PREDRIVER", NULL, "PredriveR Mux"},
+	{"HSOL", NULL, "HeadsetL Mux"},
+	{"HSOR", NULL, "HeadsetR Mux"},
+	{"CARKITL", NULL, "CarkitL Mux"},
+	{"CARKITR", NULL, "CarkitR Mux"},
+	{"HFL", NULL, "HandsfreeL Mux"},
+	{"HFR", NULL, "HandsfreeR Mux"},
+
+	/* inputs */
+	{"ADCL", NULL, "INL"},
+	{"ADCR", NULL, "INR"},
+};
+
+static int twl4030_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, twl4030_dapm_widgets,
+				 ARRAY_SIZE(twl4030_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static void twl4030_power_up(struct snd_soc_codec *codec)
+{
+	u8 anamicl, regmisc1, byte, popn;
+	int i = 0;
+
+	/* set CODECPDZ to turn on codec */
+	twl4030_set_codecpdz(codec);
+
+	/* initiate offset cancellation */
+	anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
+	twl4030_write(codec, TWL4030_REG_ANAMICL,
+		anamicl | TWL4030_CNCL_OFFSET_START);
+
+	/* wait for offset cancellation to complete */
+	do {
+		/* this takes a little while, so don't slam i2c */
+		udelay(2000);
+		twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
+				    TWL4030_REG_ANAMICL);
+	} while ((i++ < 100) &&
+		 ((byte & TWL4030_CNCL_OFFSET_START) ==
+		  TWL4030_CNCL_OFFSET_START));
+
+	/* anti-pop when changing analog gain */
+	regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
+	twl4030_write(codec, TWL4030_REG_MISC_SET_1,
+		regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
+
+	/* toggle CODECPDZ as per TRM */
+	twl4030_clear_codecpdz(codec);
+	twl4030_set_codecpdz(codec);
+
+	/* program anti-pop with bias ramp delay */
+	popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+	popn &= TWL4030_RAMP_DELAY;
+	popn |=	TWL4030_RAMP_DELAY_645MS;
+	twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+	popn |=	TWL4030_VMID_EN;
+	twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+
+	/* enable anti-pop ramp */
+	popn |= TWL4030_RAMP_EN;
+	twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+}
+
+static void twl4030_power_down(struct snd_soc_codec *codec)
+{
+	u8 popn;
+
+	/* disable anti-pop ramp */
+	popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+	popn &= ~TWL4030_RAMP_EN;
+	twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+
+	/* disable bias out */
+	popn &= ~TWL4030_VMID_EN;
+	twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
+
+	/* power down */
+	twl4030_clear_codecpdz(codec);
+}
+
+static int twl4030_set_bias_level(struct snd_soc_codec *codec,
+				  enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		twl4030_power_up(codec);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		/* TODO: develop a twl4030_prepare function */
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* TODO: develop a twl4030_standby function */
+		twl4030_power_down(codec);
+		break;
+	case SND_SOC_BIAS_OFF:
+		twl4030_power_down(codec);
+		break;
+	}
+	codec->bias_level = level;
+
+	return 0;
+}
+
+static int twl4030_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_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	u8 mode, old_mode, format, old_format;
+
+
+	/* bit rate */
+	old_mode = twl4030_read_reg_cache(codec,
+			TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
+	mode = old_mode & ~TWL4030_APLL_RATE;
+
+	switch (params_rate(params)) {
+	case 8000:
+		mode |= TWL4030_APLL_RATE_8000;
+		break;
+	case 11025:
+		mode |= TWL4030_APLL_RATE_11025;
+		break;
+	case 12000:
+		mode |= TWL4030_APLL_RATE_12000;
+		break;
+	case 16000:
+		mode |= TWL4030_APLL_RATE_16000;
+		break;
+	case 22050:
+		mode |= TWL4030_APLL_RATE_22050;
+		break;
+	case 24000:
+		mode |= TWL4030_APLL_RATE_24000;
+		break;
+	case 32000:
+		mode |= TWL4030_APLL_RATE_32000;
+		break;
+	case 44100:
+		mode |= TWL4030_APLL_RATE_44100;
+		break;
+	case 48000:
+		mode |= TWL4030_APLL_RATE_48000;
+		break;
+	default:
+		printk(KERN_ERR "TWL4030 hw params: unknown rate %d\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	if (mode != old_mode) {
+		/* change rate and set CODECPDZ */
+		twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+		twl4030_set_codecpdz(codec);
+	}
+
+	/* sample size */
+	old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+	format = old_format;
+	format &= ~TWL4030_DATA_WIDTH;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		format |= TWL4030_DATA_WIDTH_16S_16W;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		format |= TWL4030_DATA_WIDTH_32S_24W;
+		break;
+	default:
+		printk(KERN_ERR "TWL4030 hw params: unknown format %d\n",
+			params_format(params));
+		return -EINVAL;
+	}
+
+	if (format != old_format) {
+
+		/* clear CODECPDZ before changing format (codec requirement) */
+		twl4030_clear_codecpdz(codec);
+
+		/* change format */
+		twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+
+		/* set CODECPDZ afterwards */
+		twl4030_set_codecpdz(codec);
+	}
+	return 0;
+}
+
+static int twl4030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 infreq;
+
+	switch (freq) {
+	case 19200000:
+		infreq = TWL4030_APLL_INFREQ_19200KHZ;
+		break;
+	case 26000000:
+		infreq = TWL4030_APLL_INFREQ_26000KHZ;
+		break;
+	case 38400000:
+		infreq = TWL4030_APLL_INFREQ_38400KHZ;
+		break;
+	default:
+		printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
+			freq);
+		return -EINVAL;
+	}
+
+	infreq |= TWL4030_APLL_EN;
+	twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+
+	return 0;
+}
+
+static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			     unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 old_format, format;
+
+	/* get format */
+	old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+	format = old_format;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		format &= ~(TWL4030_AIF_SLAVE_EN);
+		format &= ~(TWL4030_CLK256FS_EN);
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		format |= TWL4030_AIF_SLAVE_EN;
+		format |= TWL4030_CLK256FS_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	format &= ~TWL4030_AIF_FORMAT;
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		format |= TWL4030_AIF_FORMAT_CODEC;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (format != old_format) {
+
+		/* clear CODECPDZ before changing format (codec requirement) */
+		twl4030_clear_codecpdz(codec);
+
+		/* change format */
+		twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
+
+		/* set CODECPDZ afterwards */
+		twl4030_set_codecpdz(codec);
+	}
+
+	return 0;
+}
+
+#define TWL4030_RATES	 (SNDRV_PCM_RATE_8000_48000)
+#define TWL4030_FORMATS	 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
+
+struct snd_soc_dai twl4030_dai = {
+	.name = "twl4030",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = TWL4030_RATES,
+		.formats = TWL4030_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = TWL4030_RATES,
+		.formats = TWL4030_FORMATS,},
+	.ops = {
+		.hw_params = twl4030_hw_params,
+		.set_sysclk = twl4030_set_dai_sysclk,
+		.set_fmt = twl4030_set_dai_fmt,
+	}
+};
+EXPORT_SYMBOL_GPL(twl4030_dai);
+
+static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int twl4030_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	twl4030_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+/*
+ * initialize the driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+
+static int twl4030_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+
+	printk(KERN_INFO "TWL4030 Audio Codec init \n");
+
+	codec->name = "twl4030";
+	codec->owner = THIS_MODULE;
+	codec->read = twl4030_read_reg_cache;
+	codec->write = twl4030_write;
+	codec->set_bias_level = twl4030_set_bias_level;
+	codec->dai = &twl4030_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = sizeof(twl4030_reg);
+	codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
+					GFP_KERNEL);
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "twl4030: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	twl4030_init_chip(codec);
+
+	/* power on device */
+	twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	twl4030_add_controls(codec);
+	twl4030_add_widgets(codec);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "twl4030: failed to register card\n");
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+static struct snd_soc_device *twl4030_socdev;
+
+static int twl4030_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	twl4030_socdev = socdev;
+	twl4030_init(socdev);
+
+	return 0;
+}
+
+static int twl4030_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	printk(KERN_INFO "TWL4030 Audio Codec remove\n");
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_twl4030 = {
+	.probe = twl4030_probe,
+	.remove = twl4030_remove,
+	.suspend = twl4030_suspend,
+	.resume = twl4030_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
+
+static int __init twl4030_modinit(void)
+{
+	return snd_soc_register_dai(&twl4030_dai);
+}
+module_init(twl4030_modinit);
+
+static void __exit twl4030_exit(void)
+{
+	snd_soc_unregister_dai(&twl4030_dai);
+}
+module_exit(twl4030_exit);
+
+MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
+MODULE_AUTHOR("Steve Sakoman");
+MODULE_LICENSE("GPL");

+ 219 - 0
sound/soc/codecs/twl4030.h

@@ -0,0 +1,219 @@
+/*
+ * ALSA SoC TWL4030 codec driver
+ *
+ * Author: Steve Sakoman <steve@sakoman.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TWL4030_AUDIO_H__
+#define __TWL4030_AUDIO_H__
+
+#define TWL4030_REG_CODEC_MODE		0x1
+#define TWL4030_REG_OPTION		0x2
+#define TWL4030_REG_UNKNOWN		0x3
+#define TWL4030_REG_MICBIAS_CTL		0x4
+#define TWL4030_REG_ANAMICL		0x5
+#define TWL4030_REG_ANAMICR		0x6
+#define TWL4030_REG_AVADC_CTL		0x7
+#define TWL4030_REG_ADCMICSEL		0x8
+#define TWL4030_REG_DIGMIXING		0x9
+#define TWL4030_REG_ATXL1PGA		0xA
+#define TWL4030_REG_ATXR1PGA		0xB
+#define TWL4030_REG_AVTXL2PGA		0xC
+#define TWL4030_REG_AVTXR2PGA		0xD
+#define TWL4030_REG_AUDIO_IF		0xE
+#define TWL4030_REG_VOICE_IF		0xF
+#define TWL4030_REG_ARXR1PGA		0x10
+#define TWL4030_REG_ARXL1PGA		0x11
+#define TWL4030_REG_ARXR2PGA		0x12
+#define TWL4030_REG_ARXL2PGA		0x13
+#define TWL4030_REG_VRXPGA		0x14
+#define TWL4030_REG_VSTPGA		0x15
+#define TWL4030_REG_VRX2ARXPGA		0x16
+#define TWL4030_REG_AVDAC_CTL		0x17
+#define TWL4030_REG_ARX2VTXPGA		0x18
+#define TWL4030_REG_ARXL1_APGA_CTL	0x19
+#define TWL4030_REG_ARXR1_APGA_CTL	0x1A
+#define TWL4030_REG_ARXL2_APGA_CTL	0x1B
+#define TWL4030_REG_ARXR2_APGA_CTL	0x1C
+#define TWL4030_REG_ATX2ARXPGA		0x1D
+#define TWL4030_REG_BT_IF		0x1E
+#define TWL4030_REG_BTPGA		0x1F
+#define TWL4030_REG_BTSTPGA		0x20
+#define TWL4030_REG_EAR_CTL		0x21
+#define TWL4030_REG_HS_SEL		0x22
+#define TWL4030_REG_HS_GAIN_SET		0x23
+#define TWL4030_REG_HS_POPN_SET		0x24
+#define TWL4030_REG_PREDL_CTL		0x25
+#define TWL4030_REG_PREDR_CTL		0x26
+#define TWL4030_REG_PRECKL_CTL		0x27
+#define TWL4030_REG_PRECKR_CTL		0x28
+#define TWL4030_REG_HFL_CTL		0x29
+#define TWL4030_REG_HFR_CTL		0x2A
+#define TWL4030_REG_ALC_CTL		0x2B
+#define TWL4030_REG_ALC_SET1		0x2C
+#define TWL4030_REG_ALC_SET2		0x2D
+#define TWL4030_REG_BOOST_CTL		0x2E
+#define TWL4030_REG_SOFTVOL_CTL		0x2F
+#define TWL4030_REG_DTMF_FREQSEL	0x30
+#define TWL4030_REG_DTMF_TONEXT1H	0x31
+#define TWL4030_REG_DTMF_TONEXT1L	0x32
+#define TWL4030_REG_DTMF_TONEXT2H	0x33
+#define TWL4030_REG_DTMF_TONEXT2L	0x34
+#define TWL4030_REG_DTMF_TONOFF		0x35
+#define TWL4030_REG_DTMF_WANONOFF	0x36
+#define TWL4030_REG_I2S_RX_SCRAMBLE_H	0x37
+#define TWL4030_REG_I2S_RX_SCRAMBLE_M	0x38
+#define TWL4030_REG_I2S_RX_SCRAMBLE_L	0x39
+#define TWL4030_REG_APLL_CTL		0x3A
+#define TWL4030_REG_DTMF_CTL		0x3B
+#define TWL4030_REG_DTMF_PGA_CTL2	0x3C
+#define TWL4030_REG_DTMF_PGA_CTL1	0x3D
+#define TWL4030_REG_MISC_SET_1		0x3E
+#define TWL4030_REG_PCMBTMUX		0x3F
+#define TWL4030_REG_RX_PATH_SEL		0x43
+#define TWL4030_REG_VDL_APGA_CTL	0x44
+#define TWL4030_REG_VIBRA_CTL		0x45
+#define TWL4030_REG_VIBRA_SET		0x46
+#define TWL4030_REG_VIBRA_PWM_SET	0x47
+#define TWL4030_REG_ANAMIC_GAIN		0x48
+#define TWL4030_REG_MISC_SET_2		0x49
+
+#define TWL4030_CACHEREGNUM	(TWL4030_REG_MISC_SET_2 + 1)
+
+/* Bitfield Definitions */
+
+/* TWL4030_CODEC_MODE (0x01) Fields */
+
+#define TWL4030_APLL_RATE		0xF0
+#define TWL4030_APLL_RATE_8000		0x00
+#define TWL4030_APLL_RATE_11025		0x10
+#define TWL4030_APLL_RATE_12000		0x20
+#define TWL4030_APLL_RATE_16000		0x40
+#define TWL4030_APLL_RATE_22050		0x50
+#define TWL4030_APLL_RATE_24000		0x60
+#define TWL4030_APLL_RATE_32000		0x80
+#define TWL4030_APLL_RATE_44100		0x90
+#define TWL4030_APLL_RATE_48000		0xA0
+#define TWL4030_SEL_16K			0x04
+#define TWL4030_CODECPDZ		0x02
+#define TWL4030_OPT_MODE		0x01
+
+/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
+
+#define TWL4030_MICBIAS2_CTL		0x40
+#define TWL4030_MICBIAS1_CTL		0x20
+#define TWL4030_HSMICBIAS_EN		0x04
+#define TWL4030_MICBIAS2_EN		0x02
+#define TWL4030_MICBIAS1_EN		0x01
+
+/* ANAMICL (0x05) Fields */
+
+#define TWL4030_CNCL_OFFSET_START	0x80
+#define TWL4030_OFFSET_CNCL_SEL		0x60
+#define TWL4030_OFFSET_CNCL_SEL_ARX1	0x00
+#define TWL4030_OFFSET_CNCL_SEL_ARX2	0x20
+#define TWL4030_OFFSET_CNCL_SEL_VRX	0x40
+#define TWL4030_OFFSET_CNCL_SEL_ALL	0x60
+#define TWL4030_MICAMPL_EN		0x10
+#define TWL4030_CKMIC_EN		0x08
+#define TWL4030_AUXL_EN			0x04
+#define TWL4030_HSMIC_EN		0x02
+#define TWL4030_MAINMIC_EN		0x01
+
+/* ANAMICR (0x06) Fields */
+
+#define TWL4030_MICAMPR_EN		0x10
+#define TWL4030_AUXR_EN			0x04
+#define TWL4030_SUBMIC_EN		0x01
+
+/* AVADC_CTL (0x07) Fields */
+
+#define TWL4030_ADCL_EN			0x08
+#define TWL4030_AVADC_CLK_PRIORITY	0x04
+#define TWL4030_ADCR_EN			0x02
+
+/* AUDIO_IF (0x0E) Fields */
+
+#define TWL4030_AIF_SLAVE_EN		0x80
+#define TWL4030_DATA_WIDTH		0x60
+#define TWL4030_DATA_WIDTH_16S_16W	0x00
+#define TWL4030_DATA_WIDTH_32S_16W	0x40
+#define TWL4030_DATA_WIDTH_32S_24W	0x60
+#define TWL4030_AIF_FORMAT		0x18
+#define TWL4030_AIF_FORMAT_CODEC	0x00
+#define TWL4030_AIF_FORMAT_LEFT		0x08
+#define TWL4030_AIF_FORMAT_RIGHT	0x10
+#define TWL4030_AIF_FORMAT_TDM		0x18
+#define TWL4030_AIF_TRI_EN		0x04
+#define TWL4030_CLK256FS_EN		0x02
+#define TWL4030_AIF_EN			0x01
+
+/* HS_GAIN_SET (0x23) Fields */
+
+#define TWL4030_HSR_GAIN		0x0C
+#define TWL4030_HSR_GAIN_PWR_DOWN	0x00
+#define TWL4030_HSR_GAIN_PLUS_6DB	0x04
+#define TWL4030_HSR_GAIN_0DB		0x08
+#define TWL4030_HSR_GAIN_MINUS_6DB	0x0C
+#define TWL4030_HSL_GAIN		0x03
+#define TWL4030_HSL_GAIN_PWR_DOWN	0x00
+#define TWL4030_HSL_GAIN_PLUS_6DB	0x01
+#define TWL4030_HSL_GAIN_0DB		0x02
+#define TWL4030_HSL_GAIN_MINUS_6DB	0x03
+
+/* HS_POPN_SET (0x24) Fields */
+
+#define TWL4030_VMID_EN			0x40
+#define	TWL4030_EXTMUTE			0x20
+#define TWL4030_RAMP_DELAY		0x1C
+#define TWL4030_RAMP_DELAY_20MS		0x00
+#define TWL4030_RAMP_DELAY_40MS		0x04
+#define TWL4030_RAMP_DELAY_81MS		0x08
+#define TWL4030_RAMP_DELAY_161MS	0x0C
+#define TWL4030_RAMP_DELAY_323MS	0x10
+#define TWL4030_RAMP_DELAY_645MS	0x14
+#define TWL4030_RAMP_DELAY_1291MS	0x18
+#define TWL4030_RAMP_DELAY_2581MS	0x1C
+#define TWL4030_RAMP_EN			0x02
+
+/* HFL_CTL (0x29, 0x2A) Fields */
+#define TWL4030_HF_CTL_HB_EN		0x04
+#define TWL4030_HF_CTL_LOOP_EN		0x08
+#define TWL4030_HF_CTL_RAMP_EN		0x10
+#define TWL4030_HF_CTL_REF_EN		0x20
+
+/* APLL_CTL (0x3A) Fields */
+
+#define TWL4030_APLL_EN			0x10
+#define TWL4030_APLL_INFREQ		0x0F
+#define TWL4030_APLL_INFREQ_19200KHZ	0x05
+#define TWL4030_APLL_INFREQ_26000KHZ	0x06
+#define TWL4030_APLL_INFREQ_38400KHZ	0x0F
+
+/* REG_MISC_SET_1 (0x3E) Fields */
+
+#define TWL4030_CLK64_EN		0x80
+#define TWL4030_SCRAMBLE_EN		0x40
+#define TWL4030_FMLOOP_EN		0x20
+#define TWL4030_SMOOTH_ANAVOL_EN	0x02
+#define TWL4030_DIGMIC_LR_SWAP_EN	0x01
+
+extern struct snd_soc_dai twl4030_dai;
+extern struct snd_soc_codec_device soc_codec_dev_twl4030;
+
+#endif	/* End of __TWL4030_AUDIO_H__ */

+ 668 - 0
sound/soc/codecs/uda134x.c

@@ -0,0 +1,668 @@
+/*
+ * uda134x.c  --  UDA134X ALSA SoC Codec driver
+ *
+ * Modifications by Christian Pellegrin <chripell@evolware.org>
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
+ *
+ * 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/module.h>
+#include <linux/delay.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include <sound/uda134x.h>
+#include <sound/l3.h>
+
+#include "uda134x.h"
+
+
+#define POWER_OFF_ON_STANDBY 1
+/*
+  ALSA SOC usually puts the device in standby mode when it's not used
+  for sometime. If you define POWER_OFF_ON_STANDBY the driver will
+  turn off the ADC/DAC when this callback is invoked and turn it back
+  on when needed. Unfortunately this will result in a very light bump
+  (it can be audible only with good earphones). If this bothers you
+  just comment this line, you will have slightly higher power
+  consumption . Please note that sending the L3 command for ADC is
+  enough to make the bump, so it doesn't make difference if you
+  completely take off power from the codec.
+ */
+
+#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
+#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+		SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
+
+struct uda134x_priv {
+	int sysclk;
+	int dai_fmt;
+
+	struct snd_pcm_substream *master_substream;
+	struct snd_pcm_substream *slave_substream;
+};
+
+/* In-data addresses are hard-coded into the reg-cache values */
+static const char uda134x_reg[UDA134X_REGS_NUM] = {
+	/* Extended address registers */
+	0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+	/* Status, data regs */
+	0x00, 0x83, 0x00, 0x40, 0x80, 0x00,
+};
+
+/*
+ * The codec has no support for reading its registers except for peak level...
+ */
+static inline unsigned int uda134x_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u8 *cache = codec->reg_cache;
+
+	if (reg >= UDA134X_REGS_NUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * Write the register cache
+ */
+static inline void uda134x_write_reg_cache(struct snd_soc_codec *codec,
+	u8 reg, unsigned int value)
+{
+	u8 *cache = codec->reg_cache;
+
+	if (reg >= UDA134X_REGS_NUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * Write to the uda134x registers
+ *
+ */
+static int uda134x_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	int ret;
+	u8 addr;
+	u8 data = value;
+	struct uda134x_platform_data *pd = codec->control_data;
+
+	pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);
+
+	if (reg >= UDA134X_REGS_NUM) {
+		printk(KERN_ERR "%s unkown register: reg: %d",
+		       __func__, reg);
+		return -EINVAL;
+	}
+
+	uda134x_write_reg_cache(codec, reg, value);
+
+	switch (reg) {
+	case UDA134X_STATUS0:
+	case UDA134X_STATUS1:
+		addr = UDA134X_STATUS_ADDR;
+		break;
+	case UDA134X_DATA000:
+	case UDA134X_DATA001:
+	case UDA134X_DATA010:
+		addr = UDA134X_DATA0_ADDR;
+		break;
+	case UDA134X_DATA1:
+		addr = UDA134X_DATA1_ADDR;
+		break;
+	default:
+		/* It's an extended address register */
+		addr =  (reg | UDA134X_EXTADDR_PREFIX);
+
+		ret = l3_write(&pd->l3,
+			       UDA134X_DATA0_ADDR, &addr, 1);
+		if (ret != 1)
+			return -EIO;
+
+		addr = UDA134X_DATA0_ADDR;
+		data = (value | UDA134X_EXTDATA_PREFIX);
+		break;
+	}
+
+	ret = l3_write(&pd->l3,
+		       addr, &data, 1);
+	if (ret != 1)
+		return -EIO;
+
+	return 0;
+}
+
+static inline void uda134x_reset(struct snd_soc_codec *codec)
+{
+	u8 reset_reg = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
+	uda134x_write(codec, UDA134X_STATUS0, reset_reg | (1<<6));
+	msleep(1);
+	uda134x_write(codec, UDA134X_STATUS0, reset_reg & ~(1<<6));
+}
+
+static int uda134x_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u8 mute_reg = uda134x_read_reg_cache(codec, UDA134X_DATA010);
+
+	pr_debug("%s mute: %d\n", __func__, mute);
+
+	if (mute)
+		mute_reg |= (1<<2);
+	else
+		mute_reg &= ~(1<<2);
+
+	uda134x_write(codec, UDA134X_DATA010, mute_reg & ~(1<<2));
+
+	return 0;
+}
+
+static int uda134x_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct uda134x_priv *uda134x = codec->private_data;
+	struct snd_pcm_runtime *master_runtime;
+
+	if (uda134x->master_substream) {
+		master_runtime = uda134x->master_substream->runtime;
+
+		pr_debug("%s constraining to %d bits at %d\n", __func__,
+			 master_runtime->sample_bits,
+			 master_runtime->rate);
+
+		snd_pcm_hw_constraint_minmax(substream->runtime,
+					     SNDRV_PCM_HW_PARAM_RATE,
+					     master_runtime->rate,
+					     master_runtime->rate);
+
+		snd_pcm_hw_constraint_minmax(substream->runtime,
+					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+					     master_runtime->sample_bits,
+					     master_runtime->sample_bits);
+
+		uda134x->slave_substream = substream;
+	} else
+		uda134x->master_substream = substream;
+
+	return 0;
+}
+
+static void uda134x_shutdown(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct uda134x_priv *uda134x = codec->private_data;
+
+	if (uda134x->master_substream == substream)
+		uda134x->master_substream = uda134x->slave_substream;
+
+	uda134x->slave_substream = NULL;
+}
+
+static int uda134x_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_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct uda134x_priv *uda134x = codec->private_data;
+	u8 hw_params;
+
+	if (substream == uda134x->slave_substream) {
+		pr_debug("%s ignoring hw_params for slave substream\n",
+			 __func__);
+		return 0;
+	}
+
+	hw_params = uda134x_read_reg_cache(codec, UDA134X_STATUS0);
+	hw_params &= STATUS0_SYSCLK_MASK;
+	hw_params &= STATUS0_DAIFMT_MASK;
+
+	pr_debug("%s sysclk: %d, rate:%d\n", __func__,
+		 uda134x->sysclk, params_rate(params));
+
+	/* set SYSCLK / fs ratio */
+	switch (uda134x->sysclk / params_rate(params)) {
+	case 512:
+		break;
+	case 384:
+		hw_params |= (1<<4);
+		break;
+	case 256:
+		hw_params |= (1<<5);
+		break;
+	default:
+		printk(KERN_ERR "%s unsupported fs\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__,
+		 uda134x->dai_fmt, params_format(params));
+
+	/* set DAI format and word length */
+	switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			hw_params |= (1<<1);
+			break;
+		case SNDRV_PCM_FORMAT_S18_3LE:
+			hw_params |= (1<<2);
+			break;
+		case SNDRV_PCM_FORMAT_S20_3LE:
+			hw_params |= ((1<<2) | (1<<1));
+			break;
+		default:
+			printk(KERN_ERR "%s unsupported format (right)\n",
+			       __func__);
+			return -EINVAL;
+		}
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		hw_params |= (1<<3);
+		break;
+	default:
+		printk(KERN_ERR "%s unsupported format\n", __func__);
+		return -EINVAL;
+	}
+
+	uda134x_write(codec, UDA134X_STATUS0, hw_params);
+
+	return 0;
+}
+
+static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+				  int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct uda134x_priv *uda134x = codec->private_data;
+
+	pr_debug("%s clk_id: %d, freq: %d, dir: %d\n", __func__,
+		 clk_id, freq, dir);
+
+	/* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
+	   because the codec is slave. Of course limitations of the clock
+	   master (the IIS controller) apply.
+	   We'll error out on set_hw_params if it's not OK */
+	if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
+		uda134x->sysclk = freq;
+		return 0;
+	}
+
+	printk(KERN_ERR "%s unsupported sysclk\n", __func__);
+	return -EINVAL;
+}
+
+static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			       unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct uda134x_priv *uda134x = codec->private_data;
+
+	pr_debug("%s fmt: %08X\n", __func__, fmt);
+
+	/* codec supports only full slave mode */
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+		printk(KERN_ERR "%s unsupported slave mode\n", __func__);
+		return -EINVAL;
+	}
+
+	/* no support for clock inversion */
+	if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
+		printk(KERN_ERR "%s unsupported clock inversion\n", __func__);
+		return -EINVAL;
+	}
+
+	/* We can't setup DAI format here as it depends on the word bit num */
+	/* so let's just store the value for later */
+	uda134x->dai_fmt = fmt;
+
+	return 0;
+}
+
+static int uda134x_set_bias_level(struct snd_soc_codec *codec,
+				  enum snd_soc_bias_level level)
+{
+	u8 reg;
+	struct uda134x_platform_data *pd = codec->control_data;
+	int i;
+	u8 *cache = codec->reg_cache;
+
+	pr_debug("%s bias level %d\n", __func__, level);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		/* ADC, DAC on */
+		reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
+		uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		/* power on */
+		if (pd->power) {
+			pd->power(1);
+			/* Sync reg_cache with the hardware */
+			for (i = 0; i < ARRAY_SIZE(uda134x_reg); i++)
+				codec->write(codec, i, *cache++);
+		}
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* ADC, DAC power off */
+		reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
+		uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
+		break;
+	case SND_SOC_BIAS_OFF:
+		/* power off */
+		if (pd->power)
+			pd->power(0);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1",
+					    "Minimum2", "Maximum"};
+static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *uda134x_mixmode[] = {"Differential", "Analog1",
+					"Analog2", "Both"};
+
+static const struct soc_enum uda134x_mixer_enum[] = {
+SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
+SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
+SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
+};
+
+static const struct snd_kcontrol_new uda1341_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0),
+SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1),
+SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1),
+
+SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0),
+SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0),
+
+SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
+SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
+
+SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),
+
+SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0),
+SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1),
+SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0),
+
+SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0),
+SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0),
+SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0),
+SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0),
+SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0),
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new uda1340_snd_controls[] = {
+SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
+
+SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
+SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
+
+SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
+SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
+
+SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
+};
+
+static int uda134x_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i, n;
+	const struct snd_kcontrol_new *ctrls;
+	struct uda134x_platform_data *pd = codec->control_data;
+
+	switch (pd->model) {
+	case UDA134X_UDA1340:
+	case UDA134X_UDA1344:
+		n = ARRAY_SIZE(uda1340_snd_controls);
+		ctrls = uda1340_snd_controls;
+		break;
+	case UDA134X_UDA1341:
+		n = ARRAY_SIZE(uda1341_snd_controls);
+		ctrls = uda1341_snd_controls;
+		break;
+	default:
+		printk(KERN_ERR "%s unkown codec type: %d",
+		       __func__, pd->model);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < n; i++) {
+		err = snd_ctl_add(codec->card,
+				  snd_soc_cnew(&ctrls[i],
+					       codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+struct snd_soc_dai uda134x_dai = {
+	.name = "UDA134X",
+	/* playback capabilities */
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA134X_RATES,
+		.formats = UDA134X_FORMATS,
+	},
+	/* capture capabilities */
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = UDA134X_RATES,
+		.formats = UDA134X_FORMATS,
+	},
+	/* pcm operations */
+	.ops = {
+		.startup = uda134x_startup,
+		.shutdown = uda134x_shutdown,
+		.hw_params = uda134x_hw_params,
+		.digital_mute = uda134x_mute,
+		.set_sysclk = uda134x_set_dai_sysclk,
+		.set_fmt = uda134x_set_dai_fmt,
+	}
+};
+EXPORT_SYMBOL(uda134x_dai);
+
+
+static int uda134x_soc_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct uda134x_priv *uda134x;
+	void *codec_setup_data = socdev->codec_data;
+	int ret = -ENOMEM;
+	struct uda134x_platform_data *pd;
+
+	printk(KERN_INFO "UDA134X SoC Audio Codec\n");
+
+	if (!codec_setup_data) {
+		printk(KERN_ERR "UDA134X SoC codec: "
+		       "missing L3 bitbang function\n");
+		return -ENODEV;
+	}
+
+	pd = codec_setup_data;
+	switch (pd->model) {
+	case UDA134X_UDA1340:
+	case UDA134X_UDA1341:
+	case UDA134X_UDA1344:
+		break;
+	default:
+		printk(KERN_ERR "UDA134X SoC codec: "
+		       "unsupported model %d\n",
+			pd->model);
+		return -EINVAL;
+	}
+
+	socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (socdev->codec == NULL)
+		return ret;
+
+	codec = socdev->codec;
+
+	uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
+	if (uda134x == NULL)
+		goto priv_err;
+	codec->private_data = uda134x;
+
+	codec->reg_cache = kmemdup(uda134x_reg, sizeof(uda134x_reg),
+				   GFP_KERNEL);
+	if (codec->reg_cache == NULL)
+		goto reg_err;
+
+	mutex_init(&codec->mutex);
+
+	codec->reg_cache_size = sizeof(uda134x_reg);
+	codec->reg_cache_step = 1;
+
+	codec->name = "UDA134X";
+	codec->owner = THIS_MODULE;
+	codec->dai = &uda134x_dai;
+	codec->num_dai = 1;
+	codec->read = uda134x_read_reg_cache;
+	codec->write = uda134x_write;
+#ifdef POWER_OFF_ON_STANDBY
+	codec->set_bias_level = uda134x_set_bias_level;
+#endif
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->control_data = codec_setup_data;
+
+	if (pd->power)
+		pd->power(1);
+
+	uda134x_reset(codec);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "UDA134X: failed to register pcms\n");
+		goto pcm_err;
+	}
+
+	ret = uda134x_add_controls(codec);
+	if (ret < 0) {
+		printk(KERN_ERR "UDA134X: failed to register controls\n");
+		goto pcm_err;
+	}
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "UDA134X: failed to register card\n");
+		goto card_err;
+	}
+
+	return 0;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+reg_err:
+	kfree(codec->private_data);
+priv_err:
+	kfree(codec);
+	return ret;
+}
+
+/* power down chip */
+static int uda134x_soc_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	kfree(codec->private_data);
+	kfree(codec->reg_cache);
+	kfree(codec);
+
+	return 0;
+}
+
+#if defined(CONFIG_PM)
+static int uda134x_soc_suspend(struct platform_device *pdev,
+						pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int uda134x_soc_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
+	uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
+	return 0;
+}
+#else
+#define uda134x_soc_suspend NULL
+#define uda134x_soc_resume NULL
+#endif /* CONFIG_PM */
+
+struct snd_soc_codec_device soc_codec_dev_uda134x = {
+	.probe =        uda134x_soc_probe,
+	.remove =       uda134x_soc_remove,
+	.suspend =      uda134x_soc_suspend,
+	.resume =       uda134x_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda134x);
+
+static int __init uda134x_init(void)
+{
+	return snd_soc_register_dai(&uda134x_dai);
+}
+module_init(uda134x_init);
+
+static void __exit uda134x_exit(void)
+{
+	snd_soc_unregister_dai(&uda134x_dai);
+}
+module_exit(uda134x_exit);
+
+MODULE_DESCRIPTION("UDA134X ALSA soc codec driver");
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");

+ 36 - 0
sound/soc/codecs/uda134x.h

@@ -0,0 +1,36 @@
+#ifndef _UDA134X_CODEC_H
+#define _UDA134X_CODEC_H
+
+#define UDA134X_L3ADDR	5
+#define UDA134X_DATA0_ADDR	((UDA134X_L3ADDR << 2) | 0)
+#define UDA134X_DATA1_ADDR	((UDA134X_L3ADDR << 2) | 1)
+#define UDA134X_STATUS_ADDR	((UDA134X_L3ADDR << 2) | 2)
+
+#define UDA134X_EXTADDR_PREFIX	0xC0
+#define UDA134X_EXTDATA_PREFIX	0xE0
+
+/* UDA134X registers */
+#define UDA134X_EA000	0
+#define UDA134X_EA001	1
+#define UDA134X_EA010	2
+#define UDA134X_EA011	3
+#define UDA134X_EA100	4
+#define UDA134X_EA101	5
+#define UDA134X_EA110	6
+#define UDA134X_EA111	7
+#define UDA134X_STATUS0 8
+#define UDA134X_STATUS1 9
+#define UDA134X_DATA000 10
+#define UDA134X_DATA001 11
+#define UDA134X_DATA010 12
+#define UDA134X_DATA1	13
+
+#define UDA134X_REGS_NUM 14
+
+#define STATUS0_DAIFMT_MASK (~(7<<1))
+#define STATUS0_SYSCLK_MASK (~(3<<4))
+
+extern struct snd_soc_dai uda134x_dai;
+extern struct snd_soc_codec_device soc_codec_dev_uda134x;
+
+#endif

+ 19 - 10
sound/soc/codecs/uda1380.c

@@ -407,7 +407,8 @@ static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
  * when the DAI is being clocked by the CPU DAI. It's up to the
  * when the DAI is being clocked by the CPU DAI. It's up to the
  * machine and cpu DAI driver to do this before we are called.
  * machine and cpu DAI driver to do this before we are called.
  */
  */
-static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
+static int uda1380_pcm_prepare(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -439,7 +440,8 @@ static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
 }
 }
 
 
 static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
 static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -477,7 +479,8 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 	return 0;
 }
 }
 
 
-static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
+static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -560,8 +563,6 @@ struct snd_soc_dai uda1380_dai[] = {
 		.hw_params = uda1380_pcm_hw_params,
 		.hw_params = uda1380_pcm_hw_params,
 		.shutdown = uda1380_pcm_shutdown,
 		.shutdown = uda1380_pcm_shutdown,
 		.prepare = uda1380_pcm_prepare,
 		.prepare = uda1380_pcm_prepare,
-	},
-	.dai_ops = {
 		.digital_mute = uda1380_mute,
 		.digital_mute = uda1380_mute,
 		.set_fmt = uda1380_set_dai_fmt,
 		.set_fmt = uda1380_set_dai_fmt,
 	},
 	},
@@ -579,8 +580,6 @@ struct snd_soc_dai uda1380_dai[] = {
 		.hw_params = uda1380_pcm_hw_params,
 		.hw_params = uda1380_pcm_hw_params,
 		.shutdown = uda1380_pcm_shutdown,
 		.shutdown = uda1380_pcm_shutdown,
 		.prepare = uda1380_pcm_prepare,
 		.prepare = uda1380_pcm_prepare,
-	},
-	.dai_ops = {
 		.digital_mute = uda1380_mute,
 		.digital_mute = uda1380_mute,
 		.set_fmt = uda1380_set_dai_fmt,
 		.set_fmt = uda1380_set_dai_fmt,
 	},
 	},
@@ -598,8 +597,6 @@ struct snd_soc_dai uda1380_dai[] = {
 		.hw_params = uda1380_pcm_hw_params,
 		.hw_params = uda1380_pcm_hw_params,
 		.shutdown = uda1380_pcm_shutdown,
 		.shutdown = uda1380_pcm_shutdown,
 		.prepare = uda1380_pcm_prepare,
 		.prepare = uda1380_pcm_prepare,
-	},
-	.dai_ops = {
 		.set_fmt = uda1380_set_dai_fmt,
 		.set_fmt = uda1380_set_dai_fmt,
 	},
 	},
 },
 },
@@ -680,7 +677,7 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
 	/* uda1380 init */
 	/* uda1380 init */
 	uda1380_add_controls(codec);
 	uda1380_add_controls(codec);
 	uda1380_add_widgets(codec);
 	uda1380_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		pr_err("uda1380: failed to register card\n");
 		pr_err("uda1380: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -844,6 +841,18 @@ struct snd_soc_codec_device soc_codec_dev_uda1380 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
 EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
 
 
+static int __init uda1380_modinit(void)
+{
+	return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+}
+module_init(uda1380_modinit);
+
+static void __exit uda1380_exit(void)
+{
+	snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+}
+module_exit(uda1380_exit);
+
 MODULE_AUTHOR("Giorgio Padrin");
 MODULE_AUTHOR("Giorgio Padrin");
 MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
 MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 1583 - 0
sound/soc/codecs/wm8350.c

@@ -0,0 +1,1583 @@
+/*
+ * wm8350.c -- WM8350 ALSA SoC audio driver
+ *
+ * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <lg@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm8350/audio.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8350.h"
+
+#define WM8350_OUTn_0dB 0x39
+
+#define WM8350_RAMP_NONE	0
+#define WM8350_RAMP_UP		1
+#define WM8350_RAMP_DOWN	2
+
+/* We only include the analogue supplies here; the digital supplies
+ * need to be available well before this driver can be probed.
+ */
+static const char *supply_names[] = {
+	"AVDD",
+	"HPVDD",
+};
+
+struct wm8350_output {
+	u16 active;
+	u16 left_vol;
+	u16 right_vol;
+	u16 ramp;
+	u16 mute;
+};
+
+struct wm8350_data {
+	struct snd_soc_codec codec;
+	struct wm8350_output out1;
+	struct wm8350_output out2;
+	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
+};
+
+static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec,
+					    unsigned int reg)
+{
+	struct wm8350 *wm8350 = codec->control_data;
+	return wm8350->reg_cache[reg];
+}
+
+static unsigned int wm8350_codec_read(struct snd_soc_codec *codec,
+				      unsigned int reg)
+{
+	struct wm8350 *wm8350 = codec->control_data;
+	return wm8350_reg_read(wm8350, reg);
+}
+
+static int wm8350_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+			      unsigned int value)
+{
+	struct wm8350 *wm8350 = codec->control_data;
+	return wm8350_reg_write(wm8350, reg, value);
+}
+
+/*
+ * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec)
+{
+	struct wm8350_data *wm8350_data = codec->private_data;
+	struct wm8350_output *out1 = &wm8350_data->out1;
+	struct wm8350 *wm8350 = codec->control_data;
+	int left_complete = 0, right_complete = 0;
+	u16 reg, val;
+
+	/* left channel */
+	reg = wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME);
+	val = (reg & WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+
+	if (out1->ramp == WM8350_RAMP_UP) {
+		/* ramp step up */
+		if (val < out1->left_vol) {
+			val++;
+			reg &= ~WM8350_OUT1L_VOL_MASK;
+			wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+					 reg | (val << WM8350_OUT1L_VOL_SHIFT));
+		} else
+			left_complete = 1;
+	} else if (out1->ramp == WM8350_RAMP_DOWN) {
+		/* ramp step down */
+		if (val > 0) {
+			val--;
+			reg &= ~WM8350_OUT1L_VOL_MASK;
+			wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME,
+					 reg | (val << WM8350_OUT1L_VOL_SHIFT));
+		} else
+			left_complete = 1;
+	} else
+		return 1;
+
+	/* right channel */
+	reg = wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME);
+	val = (reg & WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+	if (out1->ramp == WM8350_RAMP_UP) {
+		/* ramp step up */
+		if (val < out1->right_vol) {
+			val++;
+			reg &= ~WM8350_OUT1R_VOL_MASK;
+			wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+					 reg | (val << WM8350_OUT1R_VOL_SHIFT));
+		} else
+			right_complete = 1;
+	} else if (out1->ramp == WM8350_RAMP_DOWN) {
+		/* ramp step down */
+		if (val > 0) {
+			val--;
+			reg &= ~WM8350_OUT1R_VOL_MASK;
+			wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME,
+					 reg | (val << WM8350_OUT1R_VOL_SHIFT));
+		} else
+			right_complete = 1;
+	}
+
+	/* only hit the update bit if either volume has changed this step */
+	if (!left_complete || !right_complete)
+		wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME, WM8350_OUT1_VU);
+
+	return left_complete & right_complete;
+}
+
+/*
+ * Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown.
+ */
+static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec)
+{
+	struct wm8350_data *wm8350_data = codec->private_data;
+	struct wm8350_output *out2 = &wm8350_data->out2;
+	struct wm8350 *wm8350 = codec->control_data;
+	int left_complete = 0, right_complete = 0;
+	u16 reg, val;
+
+	/* left channel */
+	reg = wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME);
+	val = (reg & WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+	if (out2->ramp == WM8350_RAMP_UP) {
+		/* ramp step up */
+		if (val < out2->left_vol) {
+			val++;
+			reg &= ~WM8350_OUT2L_VOL_MASK;
+			wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+					 reg | (val << WM8350_OUT1L_VOL_SHIFT));
+		} else
+			left_complete = 1;
+	} else if (out2->ramp == WM8350_RAMP_DOWN) {
+		/* ramp step down */
+		if (val > 0) {
+			val--;
+			reg &= ~WM8350_OUT2L_VOL_MASK;
+			wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME,
+					 reg | (val << WM8350_OUT1L_VOL_SHIFT));
+		} else
+			left_complete = 1;
+	} else
+		return 1;
+
+	/* right channel */
+	reg = wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME);
+	val = (reg & WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+	if (out2->ramp == WM8350_RAMP_UP) {
+		/* ramp step up */
+		if (val < out2->right_vol) {
+			val++;
+			reg &= ~WM8350_OUT2R_VOL_MASK;
+			wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+					 reg | (val << WM8350_OUT1R_VOL_SHIFT));
+		} else
+			right_complete = 1;
+	} else if (out2->ramp == WM8350_RAMP_DOWN) {
+		/* ramp step down */
+		if (val > 0) {
+			val--;
+			reg &= ~WM8350_OUT2R_VOL_MASK;
+			wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME,
+					 reg | (val << WM8350_OUT1R_VOL_SHIFT));
+		} else
+			right_complete = 1;
+	}
+
+	/* only hit the update bit if either volume has changed this step */
+	if (!left_complete || !right_complete)
+		wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME, WM8350_OUT2_VU);
+
+	return left_complete & right_complete;
+}
+
+/*
+ * This work ramps both output PGAs at stream start/stop time to
+ * minimise pop associated with DAPM power switching.
+ * It's best to enable Zero Cross when ramping occurs to minimise any
+ * zipper noises.
+ */
+static void wm8350_pga_work(struct work_struct *work)
+{
+	struct snd_soc_codec *codec =
+	    container_of(work, struct snd_soc_codec, delayed_work.work);
+	struct wm8350_data *wm8350_data = codec->private_data;
+	struct wm8350_output *out1 = &wm8350_data->out1,
+	    *out2 = &wm8350_data->out2;
+	int i, out1_complete, out2_complete;
+
+	/* do we need to ramp at all ? */
+	if (out1->ramp == WM8350_RAMP_NONE && out2->ramp == WM8350_RAMP_NONE)
+		return;
+
+	/* PGA volumes have 6 bits of resolution to ramp */
+	for (i = 0; i <= 63; i++) {
+		out1_complete = 1, out2_complete = 1;
+		if (out1->ramp != WM8350_RAMP_NONE)
+			out1_complete = wm8350_out1_ramp_step(codec);
+		if (out2->ramp != WM8350_RAMP_NONE)
+			out2_complete = wm8350_out2_ramp_step(codec);
+
+		/* ramp finished ? */
+		if (out1_complete && out2_complete)
+			break;
+
+		/* we need to delay longer on the up ramp */
+		if (out1->ramp == WM8350_RAMP_UP ||
+		    out2->ramp == WM8350_RAMP_UP) {
+			/* delay is longer over 0dB as increases are larger */
+			if (i >= WM8350_OUTn_0dB)
+				schedule_timeout_interruptible(msecs_to_jiffies
+							       (2));
+			else
+				schedule_timeout_interruptible(msecs_to_jiffies
+							       (1));
+		} else
+			udelay(50);	/* doesn't matter if we delay longer */
+	}
+
+	out1->ramp = WM8350_RAMP_NONE;
+	out2->ramp = WM8350_RAMP_NONE;
+}
+
+/*
+ * WM8350 Controls
+ */
+
+static int pga_event(struct snd_soc_dapm_widget *w,
+		     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wm8350_data *wm8350_data = codec->private_data;
+	struct wm8350_output *out;
+
+	switch (w->shift) {
+	case 0:
+	case 1:
+		out = &wm8350_data->out1;
+		break;
+	case 2:
+	case 3:
+		out = &wm8350_data->out2;
+		break;
+
+	default:
+		BUG();
+		return -1;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		out->ramp = WM8350_RAMP_UP;
+		out->active = 1;
+
+		if (!delayed_work_pending(&codec->delayed_work))
+			schedule_delayed_work(&codec->delayed_work,
+					      msecs_to_jiffies(1));
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		out->ramp = WM8350_RAMP_DOWN;
+		out->active = 0;
+
+		if (!delayed_work_pending(&codec->delayed_work))
+			schedule_delayed_work(&codec->delayed_work,
+					      msecs_to_jiffies(1));
+		break;
+	}
+
+	return 0;
+}
+
+static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8350_data *wm8350_priv = codec->private_data;
+	struct wm8350_output *out = NULL;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int ret;
+	unsigned int reg = mc->reg;
+	u16 val;
+
+	/* For OUT1 and OUT2 we shadow the values and only actually write
+	 * them out when active in order to ensure the amplifier comes on
+	 * as quietly as possible. */
+	switch (reg) {
+	case WM8350_LOUT1_VOLUME:
+		out = &wm8350_priv->out1;
+		break;
+	case WM8350_LOUT2_VOLUME:
+		out = &wm8350_priv->out2;
+		break;
+	default:
+		break;
+	}
+
+	if (out) {
+		out->left_vol = ucontrol->value.integer.value[0];
+		out->right_vol = ucontrol->value.integer.value[1];
+		if (!out->active)
+			return 1;
+	}
+
+	ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+	if (ret < 0)
+		return ret;
+
+	/* now hit the volume update bits (always bit 8) */
+	val = wm8350_codec_read(codec, reg);
+	wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU);
+	return 1;
+}
+
+static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8350_data *wm8350_priv = codec->private_data;
+	struct wm8350_output *out1 = &wm8350_priv->out1;
+	struct wm8350_output *out2 = &wm8350_priv->out2;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+
+	/* If these are cached registers use the cache */
+	switch (reg) {
+	case WM8350_LOUT1_VOLUME:
+		ucontrol->value.integer.value[0] = out1->left_vol;
+		ucontrol->value.integer.value[1] = out1->right_vol;
+		return 0;
+
+	case WM8350_LOUT2_VOLUME:
+		ucontrol->value.integer.value[0] = out2->left_vol;
+		ucontrol->value.integer.value[1] = out2->right_vol;
+		return 0;
+
+	default:
+		break;
+	}
+
+	return snd_soc_get_volsw_2r(kcontrol, ucontrol);
+}
+
+/* double control with volume update */
+#define SOC_WM8350_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, \
+				xinvert, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+		SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+		SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw_2r, \
+	.get = wm8350_get_volsw_2r, .put = wm8350_put_volsw_2r_vu, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+		 .rshift = xshift, .max = xmax, .invert = xinvert}, }
+
+static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
+static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
+static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
+static const char *wm8350_dacfilter[] = { "Normal", "Sloping" };
+static const char *wm8350_adcfilter[] = { "None", "High Pass" };
+static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
+static const char *wm8350_lr[] = { "Left", "Right" };
+
+static const struct soc_enum wm8350_enum[] = {
+	SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 4, 4, wm8350_deemp),
+	SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
+	SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
+	SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
+	SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 12, 2, wm8350_dacfilter),
+	SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
+	SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
+	SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
+	SOC_ENUM_SINGLE(WM8350_INPUT_MIXER_VOLUME, 15, 2, wm8350_lr),
+};
+
+static DECLARE_TLV_DB_LINEAR(pre_amp_tlv, -1200, 3525);
+static DECLARE_TLV_DB_LINEAR(out_pga_tlv, -5700, 600);
+static DECLARE_TLV_DB_SCALE(dac_pcm_tlv, -7163, 36, 1);
+static DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -12700, 50, 1);
+static DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 1);
+
+static const unsigned int capture_sd_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	0, 12, TLV_DB_SCALE_ITEM(-3600, 300, 1),
+	13, 15, TLV_DB_SCALE_ITEM(0, 0, 0),
+};
+
+static const struct snd_kcontrol_new wm8350_snd_controls[] = {
+	SOC_ENUM("Playback Deemphasis", wm8350_enum[0]),
+	SOC_ENUM("Playback DAC Inversion", wm8350_enum[1]),
+	SOC_WM8350_DOUBLE_R_TLV("Playback PCM Volume",
+				WM8350_DAC_DIGITAL_VOLUME_L,
+				WM8350_DAC_DIGITAL_VOLUME_R,
+				0, 255, 0, dac_pcm_tlv),
+	SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
+	SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
+	SOC_ENUM("Playback PCM Filter", wm8350_enum[4]),
+	SOC_ENUM("Capture PCM Filter", wm8350_enum[5]),
+	SOC_ENUM("Capture PCM HP Filter", wm8350_enum[6]),
+	SOC_ENUM("Capture ADC Inversion", wm8350_enum[7]),
+	SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume",
+				WM8350_ADC_DIGITAL_VOLUME_L,
+				WM8350_ADC_DIGITAL_VOLUME_R,
+				0, 255, 0, adc_pcm_tlv),
+	SOC_DOUBLE_TLV("Capture Sidetone Volume",
+		       WM8350_ADC_DIVIDER,
+		       8, 4, 15, 1, capture_sd_tlv),
+	SOC_WM8350_DOUBLE_R_TLV("Capture Volume",
+				WM8350_LEFT_INPUT_VOLUME,
+				WM8350_RIGHT_INPUT_VOLUME,
+				2, 63, 0, pre_amp_tlv),
+	SOC_DOUBLE_R("Capture ZC Switch",
+		     WM8350_LEFT_INPUT_VOLUME,
+		     WM8350_RIGHT_INPUT_VOLUME, 13, 1, 0),
+	SOC_SINGLE_TLV("Left Input Left Sidetone Volume",
+		       WM8350_OUTPUT_LEFT_MIXER_VOLUME, 1, 7, 0, out_mix_tlv),
+	SOC_SINGLE_TLV("Left Input Right Sidetone Volume",
+		       WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+		       5, 7, 0, out_mix_tlv),
+	SOC_SINGLE_TLV("Left Input Bypass Volume",
+		       WM8350_OUTPUT_LEFT_MIXER_VOLUME,
+		       9, 7, 0, out_mix_tlv),
+	SOC_SINGLE_TLV("Right Input Left Sidetone Volume",
+		       WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+		       1, 7, 0, out_mix_tlv),
+	SOC_SINGLE_TLV("Right Input Right Sidetone Volume",
+		       WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+		       5, 7, 0, out_mix_tlv),
+	SOC_SINGLE_TLV("Right Input Bypass Volume",
+		       WM8350_OUTPUT_RIGHT_MIXER_VOLUME,
+		       13, 7, 0, out_mix_tlv),
+	SOC_SINGLE("Left Input Mixer +20dB Switch",
+		   WM8350_INPUT_MIXER_VOLUME_L, 0, 1, 0),
+	SOC_SINGLE("Right Input Mixer +20dB Switch",
+		   WM8350_INPUT_MIXER_VOLUME_R, 0, 1, 0),
+	SOC_SINGLE_TLV("Out4 Capture Volume",
+		       WM8350_INPUT_MIXER_VOLUME,
+		       1, 7, 0, out_mix_tlv),
+	SOC_WM8350_DOUBLE_R_TLV("Out1 Playback Volume",
+				WM8350_LOUT1_VOLUME,
+				WM8350_ROUT1_VOLUME,
+				2, 63, 0, out_pga_tlv),
+	SOC_DOUBLE_R("Out1 Playback ZC Switch",
+		     WM8350_LOUT1_VOLUME,
+		     WM8350_ROUT1_VOLUME, 13, 1, 0),
+	SOC_WM8350_DOUBLE_R_TLV("Out2 Playback Volume",
+				WM8350_LOUT2_VOLUME,
+				WM8350_ROUT2_VOLUME,
+				2, 63, 0, out_pga_tlv),
+	SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8350_LOUT2_VOLUME,
+		     WM8350_ROUT2_VOLUME, 13, 1, 0),
+	SOC_SINGLE("Out2 Right Invert Switch", WM8350_ROUT2_VOLUME, 10, 1, 0),
+	SOC_SINGLE_TLV("Out2 Beep Volume", WM8350_BEEP_VOLUME,
+		       5, 7, 0, out_mix_tlv),
+
+	SOC_DOUBLE_R("Out1 Playback Switch",
+		     WM8350_LOUT1_VOLUME,
+		     WM8350_ROUT1_VOLUME,
+		     14, 1, 1),
+	SOC_DOUBLE_R("Out2 Playback Switch",
+		     WM8350_LOUT2_VOLUME,
+		     WM8350_ROUT2_VOLUME,
+		     14, 1, 1),
+};
+
+/*
+ * DAPM Controls
+ */
+
+/* Left Playback Mixer */
+static const struct snd_kcontrol_new wm8350_left_play_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Playback Switch",
+			WM8350_LEFT_MIXER_CONTROL, 11, 1, 0),
+	SOC_DAPM_SINGLE("Left Bypass Switch",
+			WM8350_LEFT_MIXER_CONTROL, 2, 1, 0),
+	SOC_DAPM_SINGLE("Right Playback Switch",
+			WM8350_LEFT_MIXER_CONTROL, 12, 1, 0),
+	SOC_DAPM_SINGLE("Left Sidetone Switch",
+			WM8350_LEFT_MIXER_CONTROL, 0, 1, 0),
+	SOC_DAPM_SINGLE("Right Sidetone Switch",
+			WM8350_LEFT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Right Playback Mixer */
+static const struct snd_kcontrol_new wm8350_right_play_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Playback Switch",
+			WM8350_RIGHT_MIXER_CONTROL, 12, 1, 0),
+	SOC_DAPM_SINGLE("Right Bypass Switch",
+			WM8350_RIGHT_MIXER_CONTROL, 3, 1, 0),
+	SOC_DAPM_SINGLE("Left Playback Switch",
+			WM8350_RIGHT_MIXER_CONTROL, 11, 1, 0),
+	SOC_DAPM_SINGLE("Left Sidetone Switch",
+			WM8350_RIGHT_MIXER_CONTROL, 0, 1, 0),
+	SOC_DAPM_SINGLE("Right Sidetone Switch",
+			WM8350_RIGHT_MIXER_CONTROL, 1, 1, 0),
+};
+
+/* Out4 Mixer */
+static const struct snd_kcontrol_new wm8350_out4_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Right Playback Switch",
+			WM8350_OUT4_MIXER_CONTROL, 12, 1, 0),
+	SOC_DAPM_SINGLE("Left Playback Switch",
+			WM8350_OUT4_MIXER_CONTROL, 11, 1, 0),
+	SOC_DAPM_SINGLE("Right Capture Switch",
+			WM8350_OUT4_MIXER_CONTROL, 9, 1, 0),
+	SOC_DAPM_SINGLE("Out3 Playback Switch",
+			WM8350_OUT4_MIXER_CONTROL, 2, 1, 0),
+	SOC_DAPM_SINGLE("Right Mixer Switch",
+			WM8350_OUT4_MIXER_CONTROL, 1, 1, 0),
+	SOC_DAPM_SINGLE("Left Mixer Switch",
+			WM8350_OUT4_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Out3 Mixer */
+static const struct snd_kcontrol_new wm8350_out3_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Left Playback Switch",
+			WM8350_OUT3_MIXER_CONTROL, 11, 1, 0),
+	SOC_DAPM_SINGLE("Left Capture Switch",
+			WM8350_OUT3_MIXER_CONTROL, 8, 1, 0),
+	SOC_DAPM_SINGLE("Out4 Playback Switch",
+			WM8350_OUT3_MIXER_CONTROL, 3, 1, 0),
+	SOC_DAPM_SINGLE("Left Mixer Switch",
+			WM8350_OUT3_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* Left Input Mixer */
+static const struct snd_kcontrol_new wm8350_left_capt_mixer_controls[] = {
+	SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+			    WM8350_INPUT_MIXER_VOLUME_L, 1, 7, 0, out_mix_tlv),
+	SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+			    WM8350_INPUT_MIXER_VOLUME_L, 9, 7, 0, out_mix_tlv),
+	SOC_DAPM_SINGLE("PGA Capture Switch",
+			WM8350_LEFT_INPUT_VOLUME, 14, 1, 0),
+};
+
+/* Right Input Mixer */
+static const struct snd_kcontrol_new wm8350_right_capt_mixer_controls[] = {
+	SOC_DAPM_SINGLE_TLV("L2 Capture Volume",
+			    WM8350_INPUT_MIXER_VOLUME_R, 5, 7, 0, out_mix_tlv),
+	SOC_DAPM_SINGLE_TLV("L3 Capture Volume",
+			    WM8350_INPUT_MIXER_VOLUME_R, 13, 7, 0, out_mix_tlv),
+	SOC_DAPM_SINGLE("PGA Capture Switch",
+			WM8350_RIGHT_INPUT_VOLUME, 14, 1, 0),
+};
+
+/* Left Mic Mixer */
+static const struct snd_kcontrol_new wm8350_left_mic_mixer_controls[] = {
+	SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 1, 1, 0),
+	SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 0, 1, 0),
+	SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 2, 1, 0),
+};
+
+/* Right Mic Mixer */
+static const struct snd_kcontrol_new wm8350_right_mic_mixer_controls[] = {
+	SOC_DAPM_SINGLE("INN Capture Switch", WM8350_INPUT_CONTROL, 9, 1, 0),
+	SOC_DAPM_SINGLE("INP Capture Switch", WM8350_INPUT_CONTROL, 8, 1, 0),
+	SOC_DAPM_SINGLE("IN2 Capture Switch", WM8350_INPUT_CONTROL, 10, 1, 0),
+};
+
+/* Beep Switch */
+static const struct snd_kcontrol_new wm8350_beep_switch_controls =
+SOC_DAPM_SINGLE("Switch", WM8350_BEEP_VOLUME, 15, 1, 1);
+
+/* Out4 Capture Mux */
+static const struct snd_kcontrol_new wm8350_out4_capture_controls =
+SOC_DAPM_ENUM("Route", wm8350_enum[8]);
+
+static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = {
+
+	SND_SOC_DAPM_PGA("IN3R PGA", WM8350_POWER_MGMT_2, 11, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("IN3L PGA", WM8350_POWER_MGMT_2, 10, 0, NULL, 0),
+	SND_SOC_DAPM_PGA_E("Right Out2 PGA", WM8350_POWER_MGMT_3, 3, 0, NULL,
+			   0, pga_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_PGA_E("Left Out2 PGA", WM8350_POWER_MGMT_3, 2, 0, NULL, 0,
+			   pga_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_PGA_E("Right Out1 PGA", WM8350_POWER_MGMT_3, 1, 0, NULL,
+			   0, pga_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_PGA_E("Left Out1 PGA", WM8350_POWER_MGMT_3, 0, 0, NULL, 0,
+			   pga_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_MIXER("Right Capture Mixer", WM8350_POWER_MGMT_2,
+			   7, 0, &wm8350_right_capt_mixer_controls[0],
+			   ARRAY_SIZE(wm8350_right_capt_mixer_controls)),
+
+	SND_SOC_DAPM_MIXER("Left Capture Mixer", WM8350_POWER_MGMT_2,
+			   6, 0, &wm8350_left_capt_mixer_controls[0],
+			   ARRAY_SIZE(wm8350_left_capt_mixer_controls)),
+
+	SND_SOC_DAPM_MIXER("Out4 Mixer", WM8350_POWER_MGMT_2, 5, 0,
+			   &wm8350_out4_mixer_controls[0],
+			   ARRAY_SIZE(wm8350_out4_mixer_controls)),
+
+	SND_SOC_DAPM_MIXER("Out3 Mixer", WM8350_POWER_MGMT_2, 4, 0,
+			   &wm8350_out3_mixer_controls[0],
+			   ARRAY_SIZE(wm8350_out3_mixer_controls)),
+
+	SND_SOC_DAPM_MIXER("Right Playback Mixer", WM8350_POWER_MGMT_2, 1, 0,
+			   &wm8350_right_play_mixer_controls[0],
+			   ARRAY_SIZE(wm8350_right_play_mixer_controls)),
+
+	SND_SOC_DAPM_MIXER("Left Playback Mixer", WM8350_POWER_MGMT_2, 0, 0,
+			   &wm8350_left_play_mixer_controls[0],
+			   ARRAY_SIZE(wm8350_left_play_mixer_controls)),
+
+	SND_SOC_DAPM_MIXER("Left Mic Mixer", WM8350_POWER_MGMT_2, 8, 0,
+			   &wm8350_left_mic_mixer_controls[0],
+			   ARRAY_SIZE(wm8350_left_mic_mixer_controls)),
+
+	SND_SOC_DAPM_MIXER("Right Mic Mixer", WM8350_POWER_MGMT_2, 9, 0,
+			   &wm8350_right_mic_mixer_controls[0],
+			   ARRAY_SIZE(wm8350_right_mic_mixer_controls)),
+
+	/* virtual mixer for Beep and Out2R */
+	SND_SOC_DAPM_MIXER("Out2 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_SWITCH("Beep", WM8350_POWER_MGMT_3, 7, 0,
+			    &wm8350_beep_switch_controls),
+
+	SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
+			 WM8350_POWER_MGMT_4, 3, 0),
+	SND_SOC_DAPM_ADC("Left ADC", "Left Capture",
+			 WM8350_POWER_MGMT_4, 2, 0),
+	SND_SOC_DAPM_DAC("Right DAC", "Right Playback",
+			 WM8350_POWER_MGMT_4, 5, 0),
+	SND_SOC_DAPM_DAC("Left DAC", "Left Playback",
+			 WM8350_POWER_MGMT_4, 4, 0),
+
+	SND_SOC_DAPM_MICBIAS("Mic Bias", WM8350_POWER_MGMT_1, 4, 0),
+
+	SND_SOC_DAPM_MUX("Out4 Capture Channel", SND_SOC_NOPM, 0, 0,
+			 &wm8350_out4_capture_controls),
+
+	SND_SOC_DAPM_OUTPUT("OUT1R"),
+	SND_SOC_DAPM_OUTPUT("OUT1L"),
+	SND_SOC_DAPM_OUTPUT("OUT2R"),
+	SND_SOC_DAPM_OUTPUT("OUT2L"),
+	SND_SOC_DAPM_OUTPUT("OUT3"),
+	SND_SOC_DAPM_OUTPUT("OUT4"),
+
+	SND_SOC_DAPM_INPUT("IN1RN"),
+	SND_SOC_DAPM_INPUT("IN1RP"),
+	SND_SOC_DAPM_INPUT("IN2R"),
+	SND_SOC_DAPM_INPUT("IN1LP"),
+	SND_SOC_DAPM_INPUT("IN1LN"),
+	SND_SOC_DAPM_INPUT("IN2L"),
+	SND_SOC_DAPM_INPUT("IN3R"),
+	SND_SOC_DAPM_INPUT("IN3L"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+	/* left playback mixer */
+	{"Left Playback Mixer", "Playback Switch", "Left DAC"},
+	{"Left Playback Mixer", "Left Bypass Switch", "IN3L PGA"},
+	{"Left Playback Mixer", "Right Playback Switch", "Right DAC"},
+	{"Left Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+	{"Left Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+	/* right playback mixer */
+	{"Right Playback Mixer", "Playback Switch", "Right DAC"},
+	{"Right Playback Mixer", "Right Bypass Switch", "IN3R PGA"},
+	{"Right Playback Mixer", "Left Playback Switch", "Left DAC"},
+	{"Right Playback Mixer", "Left Sidetone Switch", "Left Mic Mixer"},
+	{"Right Playback Mixer", "Right Sidetone Switch", "Right Mic Mixer"},
+
+	/* out4 playback mixer */
+	{"Out4 Mixer", "Right Playback Switch", "Right DAC"},
+	{"Out4 Mixer", "Left Playback Switch", "Left DAC"},
+	{"Out4 Mixer", "Right Capture Switch", "Right Capture Mixer"},
+	{"Out4 Mixer", "Out3 Playback Switch", "Out3 Mixer"},
+	{"Out4 Mixer", "Right Mixer Switch", "Right Playback Mixer"},
+	{"Out4 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+	{"OUT4", NULL, "Out4 Mixer"},
+
+	/* out3 playback mixer */
+	{"Out3 Mixer", "Left Playback Switch", "Left DAC"},
+	{"Out3 Mixer", "Left Capture Switch", "Left Capture Mixer"},
+	{"Out3 Mixer", "Left Mixer Switch", "Left Playback Mixer"},
+	{"Out3 Mixer", "Out4 Playback Switch", "Out4 Mixer"},
+	{"OUT3", NULL, "Out3 Mixer"},
+
+	/* out2 */
+	{"Right Out2 PGA", NULL, "Right Playback Mixer"},
+	{"Left Out2 PGA", NULL, "Left Playback Mixer"},
+	{"OUT2L", NULL, "Left Out2 PGA"},
+	{"OUT2R", NULL, "Right Out2 PGA"},
+
+	/* out1 */
+	{"Right Out1 PGA", NULL, "Right Playback Mixer"},
+	{"Left Out1 PGA", NULL, "Left Playback Mixer"},
+	{"OUT1L", NULL, "Left Out1 PGA"},
+	{"OUT1R", NULL, "Right Out1 PGA"},
+
+	/* ADCs */
+	{"Left ADC", NULL, "Left Capture Mixer"},
+	{"Right ADC", NULL, "Right Capture Mixer"},
+
+	/* Left capture mixer */
+	{"Left Capture Mixer", "L2 Capture Volume", "IN2L"},
+	{"Left Capture Mixer", "L3 Capture Volume", "IN3L PGA"},
+	{"Left Capture Mixer", "PGA Capture Switch", "Left Mic Mixer"},
+	{"Left Capture Mixer", NULL, "Out4 Capture Channel"},
+
+	/* Right capture mixer */
+	{"Right Capture Mixer", "L2 Capture Volume", "IN2R"},
+	{"Right Capture Mixer", "L3 Capture Volume", "IN3R PGA"},
+	{"Right Capture Mixer", "PGA Capture Switch", "Right Mic Mixer"},
+	{"Right Capture Mixer", NULL, "Out4 Capture Channel"},
+
+	/* L3 Inputs */
+	{"IN3L PGA", NULL, "IN3L"},
+	{"IN3R PGA", NULL, "IN3R"},
+
+	/* Left Mic mixer */
+	{"Left Mic Mixer", "INN Capture Switch", "IN1LN"},
+	{"Left Mic Mixer", "INP Capture Switch", "IN1LP"},
+	{"Left Mic Mixer", "IN2 Capture Switch", "IN2L"},
+
+	/* Right Mic mixer */
+	{"Right Mic Mixer", "INN Capture Switch", "IN1RN"},
+	{"Right Mic Mixer", "INP Capture Switch", "IN1RP"},
+	{"Right Mic Mixer", "IN2 Capture Switch", "IN2R"},
+
+	/* out 4 capture */
+	{"Out4 Capture Channel", NULL, "Out4 Mixer"},
+
+	/* Beep */
+	{"Beep", NULL, "IN3R PGA"},
+};
+
+static int wm8350_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8350_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				  snd_soc_cnew(&wm8350_snd_controls[i],
+					       codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int wm8350_add_widgets(struct snd_soc_codec *codec)
+{
+	int ret;
+
+	ret = snd_soc_dapm_new_controls(codec,
+					wm8350_dapm_widgets,
+					ARRAY_SIZE(wm8350_dapm_widgets));
+	if (ret != 0) {
+		dev_err(codec->dev, "dapm control register failed\n");
+		return ret;
+	}
+
+	/* set up audio paths */
+	ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+	if (ret != 0) {
+		dev_err(codec->dev, "DAPM route register failed\n");
+		return ret;
+	}
+
+	return snd_soc_dapm_new_widgets(codec);
+}
+
+static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+				 int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8350 *wm8350 = codec->control_data;
+	u16 fll_4;
+
+	switch (clk_id) {
+	case WM8350_MCLK_SEL_MCLK:
+		wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+				  WM8350_MCLK_SEL);
+		break;
+	case WM8350_MCLK_SEL_PLL_MCLK:
+	case WM8350_MCLK_SEL_PLL_DAC:
+	case WM8350_MCLK_SEL_PLL_ADC:
+	case WM8350_MCLK_SEL_PLL_32K:
+		wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1,
+				WM8350_MCLK_SEL);
+		fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+		    ~WM8350_FLL_CLK_SRC_MASK;
+		wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id);
+		break;
+	}
+
+	/* MCLK direction */
+	if (dir == WM8350_MCLK_DIR_OUT)
+		wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+				WM8350_MCLK_DIR);
+	else
+		wm8350_clear_bits(wm8350, WM8350_CLOCK_CONTROL_2,
+				  WM8350_MCLK_DIR);
+
+	return 0;
+}
+
+static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 val;
+
+	switch (div_id) {
+	case WM8350_ADC_CLKDIV:
+		val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) &
+		    ~WM8350_ADC_CLKDIV_MASK;
+		wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div);
+		break;
+	case WM8350_DAC_CLKDIV:
+		val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) &
+		    ~WM8350_DAC_CLKDIV_MASK;
+		wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div);
+		break;
+	case WM8350_BCLK_CLKDIV:
+		val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+		    ~WM8350_BCLK_DIV_MASK;
+		wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+		break;
+	case WM8350_OPCLK_CLKDIV:
+		val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+		    ~WM8350_OPCLK_DIV_MASK;
+		wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+		break;
+	case WM8350_SYS_CLKDIV:
+		val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) &
+		    ~WM8350_MCLK_DIV_MASK;
+		wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div);
+		break;
+	case WM8350_DACLR_CLKDIV:
+		val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+		    ~WM8350_DACLRC_RATE_MASK;
+		wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div);
+		break;
+	case WM8350_ADCLR_CLKDIV:
+		val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+		    ~WM8350_ADCLRC_RATE_MASK;
+		wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+	    ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK);
+	u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) &
+	    ~WM8350_BCLK_MSTR;
+	u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) &
+	    ~WM8350_DACLRC_ENA;
+	u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) &
+	    ~WM8350_ADCLRC_ENA;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		master |= WM8350_BCLK_MSTR;
+		dac_lrc |= WM8350_DACLRC_ENA;
+		adc_lrc |= WM8350_ADCLRC_ENA;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 0x2 << 8;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x1 << 8;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x3 << 8;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= 0x3 << 8;	/* lg not sure which mode */
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= WM8350_AIF_LRCLK_INV | WM8350_AIF_BCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= WM8350_AIF_BCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= WM8350_AIF_LRCLK_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+	wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master);
+	wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc);
+	wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc);
+	return 0;
+}
+
+static int wm8350_pcm_trigger(struct snd_pcm_substream *substream,
+			      int cmd, struct snd_soc_dai *codec_dai)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	int master = wm8350_codec_cache_read(codec, WM8350_AI_DAC_CONTROL) &
+	    WM8350_BCLK_MSTR;
+	int enabled = 0;
+
+	/* Check that the DACs or ADCs are enabled since they are
+	 * required for LRC in master mode. The DACs or ADCs need a
+	 * valid audio path i.e. pin -> ADC or DAC -> pin before
+	 * the LRC will be enabled in master mode. */
+	if (!master && cmd != SNDRV_PCM_TRIGGER_START)
+		return 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
+		    (WM8350_ADCR_ENA | WM8350_ADCL_ENA);
+	} else {
+		enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) &
+		    (WM8350_DACR_ENA | WM8350_DACL_ENA);
+	}
+
+	if (!enabled) {
+		dev_err(codec->dev,
+		       "%s: invalid audio path - no clocks available\n",
+		       __func__);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *codec_dai)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
+	    ~WM8350_AIF_WL_MASK;
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x1 << 10;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x2 << 10;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= 0x3 << 10;
+		break;
+	}
+
+	wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+	return 0;
+}
+
+static int wm8350_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8350 *wm8350 = codec->control_data;
+
+	if (mute)
+		wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+	else
+		wm8350_clear_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+	return 0;
+}
+
+/* FLL divisors */
+struct _fll_div {
+	int div;		/* FLL_OUTDIV */
+	int n;
+	int k;
+	int ratio;		/* FLL_FRATIO */
+};
+
+/* The size in bits of the fll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
+			      unsigned int output)
+{
+	u64 Kpart;
+	unsigned int t1, t2, K, Nmod;
+
+	if (output >= 2815250 && output <= 3125000)
+		fll_div->div = 0x4;
+	else if (output >= 5625000 && output <= 6250000)
+		fll_div->div = 0x3;
+	else if (output >= 11250000 && output <= 12500000)
+		fll_div->div = 0x2;
+	else if (output >= 22500000 && output <= 25000000)
+		fll_div->div = 0x1;
+	else {
+		printk(KERN_ERR "wm8350: fll freq %d out of range\n", output);
+		return -EINVAL;
+	}
+
+	if (input > 48000)
+		fll_div->ratio = 1;
+	else
+		fll_div->ratio = 8;
+
+	t1 = output * (1 << (fll_div->div + 1));
+	t2 = input * fll_div->ratio;
+
+	fll_div->n = t1 / t2;
+	Nmod = t1 % t2;
+
+	if (Nmod) {
+		Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+		do_div(Kpart, t2);
+		K = Kpart & 0xFFFFFFFF;
+
+		/* Check if we need to round */
+		if ((K % 10) >= 5)
+			K += 5;
+
+		/* Move down to proper range now rounding is done */
+		K /= 10;
+		fll_div->k = K;
+	} else
+		fll_div->k = 0;
+
+	return 0;
+}
+
+static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
+			  int pll_id, unsigned int freq_in,
+			  unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8350 *wm8350 = codec->control_data;
+	struct _fll_div fll_div;
+	int ret = 0;
+	u16 fll_1, fll_4;
+
+	/* power down FLL - we need to do this for reconfiguration */
+	wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+			  WM8350_FLL_ENA | WM8350_FLL_OSC_ENA);
+
+	if (freq_out == 0 || freq_in == 0)
+		return ret;
+
+	ret = fll_factors(&fll_div, freq_in, freq_out);
+	if (ret < 0)
+		return ret;
+	dev_dbg(wm8350->dev,
+		"FLL in %d FLL out %d N 0x%x K 0x%x div %d ratio %d",
+		freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div,
+		fll_div.ratio);
+
+	/* set up N.K & dividers */
+	fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) &
+	    ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000);
+	wm8350_codec_write(codec, WM8350_FLL_CONTROL_1,
+			   fll_1 | (fll_div.div << 8) | 0x50);
+	wm8350_codec_write(codec, WM8350_FLL_CONTROL_2,
+			   (fll_div.ratio << 11) | (fll_div.
+						    n & WM8350_FLL_N_MASK));
+	wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k);
+	fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) &
+	    ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF);
+	wm8350_codec_write(codec, WM8350_FLL_CONTROL_4,
+			   fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) |
+			   (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0));
+
+	/* power FLL on */
+	wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_OSC_ENA);
+	wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_FLL_ENA);
+
+	return 0;
+}
+
+static int wm8350_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	struct wm8350 *wm8350 = codec->control_data;
+	struct wm8350_data *priv = codec->private_data;
+	struct wm8350_audio_platform_data *platform =
+		wm8350->codec.platform_data;
+	u16 pm1;
+	int ret;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+		    ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+		wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+				 pm1 | WM8350_VMID_50K |
+				 platform->codec_current_on << 14);
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1);
+		pm1 &= ~WM8350_VMID_MASK;
+		wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+				 pm1 | WM8350_VMID_50K);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies),
+						    priv->supplies);
+			if (ret != 0)
+				return ret;
+
+			/* Enable the system clock */
+			wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4,
+					WM8350_SYSCLK_ENA);
+
+			/* mute DAC & outputs */
+			wm8350_set_bits(wm8350, WM8350_DAC_MUTE,
+					WM8350_DAC_MUTE_ENA);
+
+			/* discharge cap memory */
+			wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+					 platform->dis_out1 |
+					 (platform->dis_out2 << 2) |
+					 (platform->dis_out3 << 4) |
+					 (platform->dis_out4 << 6));
+
+			/* wait for discharge */
+			schedule_timeout_interruptible(msecs_to_jiffies
+						       (platform->
+							cap_discharge_msecs));
+
+			/* enable antipop */
+			wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+					 (platform->vmid_s_curve << 8));
+
+			/* ramp up vmid */
+			wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+					 (platform->
+					  codec_current_charge << 14) |
+					 WM8350_VMID_5K | WM8350_VMIDEN |
+					 WM8350_VBUFEN);
+
+			/* wait for vmid */
+			schedule_timeout_interruptible(msecs_to_jiffies
+						       (platform->
+							vmid_charge_msecs));
+
+			/* turn on vmid 300k  */
+			pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+			    ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+			pm1 |= WM8350_VMID_300K |
+				(platform->codec_current_standby << 14);
+			wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+					 pm1);
+
+
+			/* enable analogue bias */
+			pm1 |= WM8350_BIASEN;
+			wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+			/* disable antipop */
+			wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+		} else {
+			/* turn on vmid 300k and reduce current */
+			pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+			    ~(WM8350_VMID_MASK | WM8350_CODEC_ISEL_MASK);
+			wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+					 pm1 | WM8350_VMID_300K |
+					 (platform->
+					  codec_current_standby << 14));
+
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+
+		/* mute DAC & enable outputs */
+		wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA);
+
+		wm8350_set_bits(wm8350, WM8350_POWER_MGMT_3,
+				WM8350_OUT1L_ENA | WM8350_OUT1R_ENA |
+				WM8350_OUT2L_ENA | WM8350_OUT2R_ENA);
+
+		/* enable anti pop S curve */
+		wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+				 (platform->vmid_s_curve << 8));
+
+		/* turn off vmid  */
+		pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+		    ~WM8350_VMIDEN;
+		wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+		/* wait */
+		schedule_timeout_interruptible(msecs_to_jiffies
+					       (platform->
+						vmid_discharge_msecs));
+
+		wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL,
+				 (platform->vmid_s_curve << 8) |
+				 platform->dis_out1 |
+				 (platform->dis_out2 << 2) |
+				 (platform->dis_out3 << 4) |
+				 (platform->dis_out4 << 6));
+
+		/* turn off VBuf and drain */
+		pm1 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_1) &
+		    ~(WM8350_VBUFEN | WM8350_VMID_MASK);
+		wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1,
+				 pm1 | WM8350_OUTPUT_DRAIN_EN);
+
+		/* wait */
+		schedule_timeout_interruptible(msecs_to_jiffies
+					       (platform->drain_msecs));
+
+		pm1 &= ~WM8350_BIASEN;
+		wm8350_reg_write(wm8350, WM8350_POWER_MGMT_1, pm1);
+
+		/* disable anti-pop */
+		wm8350_reg_write(wm8350, WM8350_ANTI_POP_CONTROL, 0);
+
+		wm8350_clear_bits(wm8350, WM8350_LOUT1_VOLUME,
+				  WM8350_OUT1L_ENA);
+		wm8350_clear_bits(wm8350, WM8350_ROUT1_VOLUME,
+				  WM8350_OUT1R_ENA);
+		wm8350_clear_bits(wm8350, WM8350_LOUT2_VOLUME,
+				  WM8350_OUT2L_ENA);
+		wm8350_clear_bits(wm8350, WM8350_ROUT2_VOLUME,
+				  WM8350_OUT2R_ENA);
+
+		/* disable clock gen */
+		wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4,
+				  WM8350_SYSCLK_ENA);
+
+		regulator_bulk_disable(ARRAY_SIZE(priv->supplies),
+				       priv->supplies);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+static int wm8350_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8350_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+		wm8350_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+	return 0;
+}
+
+static struct snd_soc_codec *wm8350_codec;
+
+static int wm8350_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct wm8350 *wm8350;
+	struct wm8350_data *priv;
+	int ret;
+	struct wm8350_output *out1;
+	struct wm8350_output *out2;
+
+	BUG_ON(!wm8350_codec);
+
+	socdev->codec = wm8350_codec;
+	codec = socdev->codec;
+	wm8350 = codec->control_data;
+	priv = codec->private_data;
+
+	/* Enable the codec */
+	wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+	/* Enable robust clocking mode in ADC */
+	wm8350_codec_write(codec, WM8350_SECURITY, 0xa7);
+	wm8350_codec_write(codec, 0xde, 0x13);
+	wm8350_codec_write(codec, WM8350_SECURITY, 0);
+
+	/* read OUT1 & OUT2 volumes */
+	out1 = &priv->out1;
+	out2 = &priv->out2;
+	out1->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT1_VOLUME) &
+			  WM8350_OUT1L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+	out1->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT1_VOLUME) &
+			   WM8350_OUT1R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+	out2->left_vol = (wm8350_reg_read(wm8350, WM8350_LOUT2_VOLUME) &
+			  WM8350_OUT2L_VOL_MASK) >> WM8350_OUT1L_VOL_SHIFT;
+	out2->right_vol = (wm8350_reg_read(wm8350, WM8350_ROUT2_VOLUME) &
+			   WM8350_OUT2R_VOL_MASK) >> WM8350_OUT1R_VOL_SHIFT;
+	wm8350_reg_write(wm8350, WM8350_LOUT1_VOLUME, 0);
+	wm8350_reg_write(wm8350, WM8350_ROUT1_VOLUME, 0);
+	wm8350_reg_write(wm8350, WM8350_LOUT2_VOLUME, 0);
+	wm8350_reg_write(wm8350, WM8350_ROUT2_VOLUME, 0);
+
+	/* Latch VU bits & mute */
+	wm8350_set_bits(wm8350, WM8350_LOUT1_VOLUME,
+			WM8350_OUT1_VU | WM8350_OUT1L_MUTE);
+	wm8350_set_bits(wm8350, WM8350_LOUT2_VOLUME,
+			WM8350_OUT2_VU | WM8350_OUT2L_MUTE);
+	wm8350_set_bits(wm8350, WM8350_ROUT1_VOLUME,
+			WM8350_OUT1_VU | WM8350_OUT1R_MUTE);
+	wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME,
+			WM8350_OUT2_VU | WM8350_OUT2R_MUTE);
+
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to create pcms\n");
+		return ret;
+	}
+
+	wm8350_add_controls(codec);
+	wm8350_add_widgets(codec);
+
+	wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register card\n");
+		goto card_err;
+	}
+
+	return 0;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+	return ret;
+}
+
+static int wm8350_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+	struct wm8350 *wm8350 = codec->control_data;
+	int ret;
+
+	/* cancel any work waiting to be queued. */
+	ret = cancel_delayed_work(&codec->delayed_work);
+
+	/* if there was any work waiting then we run it now and
+	 * wait for its completion */
+	if (ret) {
+		schedule_delayed_work(&codec->delayed_work, 0);
+		flush_scheduled_work();
+	}
+
+	wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+	return 0;
+}
+
+#define WM8350_RATES (SNDRV_PCM_RATE_8000_96000)
+
+#define WM8350_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+			SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8350_dai = {
+	.name = "WM8350",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8350_RATES,
+		.formats = WM8350_FORMATS,
+	},
+	.capture = {
+		 .stream_name = "Capture",
+		 .channels_min = 1,
+		 .channels_max = 2,
+		 .rates = WM8350_RATES,
+		 .formats = WM8350_FORMATS,
+	 },
+	.ops = {
+		 .hw_params = wm8350_pcm_hw_params,
+		 .digital_mute = wm8350_mute,
+		 .trigger = wm8350_pcm_trigger,
+		 .set_fmt = wm8350_set_dai_fmt,
+		 .set_sysclk = wm8350_set_dai_sysclk,
+		 .set_pll = wm8350_set_fll,
+		 .set_clkdiv = wm8350_set_clkdiv,
+	 },
+};
+EXPORT_SYMBOL_GPL(wm8350_dai);
+
+struct snd_soc_codec_device soc_codec_dev_wm8350 = {
+	.probe = 	wm8350_probe,
+	.remove = 	wm8350_remove,
+	.suspend = 	wm8350_suspend,
+	.resume =	wm8350_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8350);
+
+static int wm8350_codec_probe(struct platform_device *pdev)
+{
+	struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+	struct wm8350_data *priv;
+	struct snd_soc_codec *codec;
+	int ret, i;
+
+	if (wm8350->codec.platform_data == NULL) {
+		dev_err(&pdev->dev, "No audio platform data supplied\n");
+		return -EINVAL;
+	}
+
+	priv = kzalloc(sizeof(struct wm8350_data), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+		priv->supplies[i].supply = supply_names[i];
+
+	ret = regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies),
+				 priv->supplies);
+	if (ret != 0)
+		goto err_priv;
+
+	codec = &priv->codec;
+	wm8350->codec.codec = codec;
+
+	wm8350_dai.dev = &pdev->dev;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+	codec->dev = &pdev->dev;
+	codec->name = "WM8350";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8350_codec_read;
+	codec->write = wm8350_codec_write;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8350_set_bias_level;
+	codec->dai = &wm8350_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8350_MAX_REGISTER;
+	codec->private_data = priv;
+	codec->control_data = wm8350;
+
+	/* Put the codec into reset if it wasn't already */
+	wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
+
+	INIT_DELAYED_WORK(&codec->delayed_work, wm8350_pga_work);
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0)
+		goto err_supply;
+
+	wm8350_codec = codec;
+
+	ret = snd_soc_register_dai(&wm8350_dai);
+	if (ret != 0)
+		goto err_codec;
+	return 0;
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err_supply:
+	regulator_bulk_free(ARRAY_SIZE(priv->supplies), priv->supplies);
+err_priv:
+	kfree(priv);
+	wm8350_codec = NULL;
+	return ret;
+}
+
+static int __devexit wm8350_codec_remove(struct platform_device *pdev)
+{
+	struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = wm8350->codec.codec;
+	struct wm8350_data *priv = codec->private_data;
+
+	snd_soc_unregister_dai(&wm8350_dai);
+	snd_soc_unregister_codec(codec);
+	regulator_bulk_free(ARRAY_SIZE(priv->supplies), priv->supplies);
+	kfree(priv);
+	wm8350_codec = NULL;
+	return 0;
+}
+
+static struct platform_driver wm8350_codec_driver = {
+	.driver = {
+		   .name = "wm8350-codec",
+		   .owner = THIS_MODULE,
+		   },
+	.probe = wm8350_codec_probe,
+	.remove = __devexit_p(wm8350_codec_remove),
+};
+
+static __init int wm8350_init(void)
+{
+	return platform_driver_register(&wm8350_codec_driver);
+}
+module_init(wm8350_init);
+
+static __exit void wm8350_exit(void)
+{
+	platform_driver_unregister(&wm8350_codec_driver);
+}
+module_exit(wm8350_exit);
+
+MODULE_DESCRIPTION("ASoC WM8350 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-codec");

+ 20 - 0
sound/soc/codecs/wm8350.h

@@ -0,0 +1,20 @@
+/*
+ * wm8350.h - WM8903 audio codec interface
+ *
+ * Copyright 2008 Wolfson Microelectronics PLC.
+ *
+ *  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.
+ */
+
+#ifndef _WM8350_H
+#define _WM8350_H
+
+#include <sound/soc.h>
+
+extern struct snd_soc_dai wm8350_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8350;
+
+#endif

+ 15 - 4
sound/soc/codecs/wm8510.c

@@ -463,7 +463,8 @@ static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
 }
 }
 
 
 static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
 static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -585,8 +586,6 @@ struct snd_soc_dai wm8510_dai = {
 		.formats = WM8510_FORMATS,},
 		.formats = WM8510_FORMATS,},
 	.ops = {
 	.ops = {
 		.hw_params = wm8510_pcm_hw_params,
 		.hw_params = wm8510_pcm_hw_params,
-	},
-	.dai_ops = {
 		.digital_mute = wm8510_mute,
 		.digital_mute = wm8510_mute,
 		.set_fmt = wm8510_set_dai_fmt,
 		.set_fmt = wm8510_set_dai_fmt,
 		.set_clkdiv = wm8510_set_dai_clkdiv,
 		.set_clkdiv = wm8510_set_dai_clkdiv,
@@ -659,7 +658,7 @@ static int wm8510_init(struct snd_soc_device *socdev)
 	wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	wm8510_add_controls(codec);
 	wm8510_add_controls(codec);
 	wm8510_add_widgets(codec);
 	wm8510_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "wm8510: failed to register card\n");
 		printk(KERN_ERR "wm8510: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -890,6 +889,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8510 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
 
 
+static int __init wm8510_modinit(void)
+{
+	return snd_soc_register_dai(&wm8510_dai);
+}
+module_init(wm8510_modinit);
+
+static void __exit wm8510_exit(void)
+{
+	snd_soc_unregister_dai(&wm8510_dai);
+}
+module_exit(wm8510_exit);
+
 MODULE_DESCRIPTION("ASoC WM8510 driver");
 MODULE_DESCRIPTION("ASoC WM8510 driver");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 70 - 64
sound/soc/codecs/wm8580.c

@@ -548,13 +548,13 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
  * Set PCM DAI bit size and sample rate.
  * Set PCM DAI bit size and sample rate.
  */
  */
 static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
 static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai_link *dai = rtd->dai;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_codec *codec = socdev->codec;
-	u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->codec_dai->id);
+	u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id);
 
 
 	paifb &= ~WM8580_AIF_LENGTH_MASK;
 	paifb &= ~WM8580_AIF_LENGTH_MASK;
 	/* bit size */
 	/* bit size */
@@ -574,7 +574,7 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb);
+	wm8580_write(codec, WM8580_PAIF3 + dai->id, paifb);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -798,8 +798,6 @@ struct snd_soc_dai wm8580_dai[] = {
 		},
 		},
 		.ops = {
 		.ops = {
 			 .hw_params = wm8580_paif_hw_params,
 			 .hw_params = wm8580_paif_hw_params,
-		 },
-		.dai_ops = {
 			 .set_fmt = wm8580_set_paif_dai_fmt,
 			 .set_fmt = wm8580_set_paif_dai_fmt,
 			 .set_clkdiv = wm8580_set_dai_clkdiv,
 			 .set_clkdiv = wm8580_set_dai_clkdiv,
 			 .set_pll = wm8580_set_dai_pll,
 			 .set_pll = wm8580_set_dai_pll,
@@ -818,8 +816,6 @@ struct snd_soc_dai wm8580_dai[] = {
 		},
 		},
 		.ops = {
 		.ops = {
 			 .hw_params = wm8580_paif_hw_params,
 			 .hw_params = wm8580_paif_hw_params,
-		 },
-		.dai_ops = {
 			 .set_fmt = wm8580_set_paif_dai_fmt,
 			 .set_fmt = wm8580_set_paif_dai_fmt,
 			 .set_clkdiv = wm8580_set_dai_clkdiv,
 			 .set_clkdiv = wm8580_set_dai_clkdiv,
 			 .set_pll = wm8580_set_dai_pll,
 			 .set_pll = wm8580_set_dai_pll,
@@ -873,7 +869,7 @@ static int wm8580_init(struct snd_soc_device *socdev)
 	wm8580_add_controls(codec);
 	wm8580_add_controls(codec);
 	wm8580_add_widgets(codec);
 	wm8580_add_widgets(codec);
 
 
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "wm8580: failed to register card\n");
 		printk(KERN_ERR "wm8580: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -900,85 +896,85 @@ static struct snd_soc_device *wm8580_socdev;
  *    low  = 0x1a
  *    low  = 0x1a
  *    high = 0x1b
  *    high = 0x1b
  */
  */
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
 
 
-static struct i2c_driver wm8580_i2c_driver;
-static struct i2c_client client_template;
-
-static int wm8580_codec_probe(struct i2c_adapter *adap, int addr, int kind)
+static int wm8580_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
 {
 {
 	struct snd_soc_device *socdev = wm8580_socdev;
 	struct snd_soc_device *socdev = wm8580_socdev;
-	struct wm8580_setup_data *setup = socdev->codec_data;
 	struct snd_soc_codec *codec = socdev->codec;
 	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
 	int ret;
 	int ret;
 
 
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (i2c == NULL) {
-		kfree(codec);
-		return -ENOMEM;
-	}
 	i2c_set_clientdata(i2c, codec);
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 	codec->control_data = i2c;
 
 
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		dev_err(&i2c->dev, "failed to attach codec at addr %x\n", addr);
-		goto err;
-	}
-
 	ret = wm8580_init(socdev);
 	ret = wm8580_init(socdev);
-	if (ret < 0) {
+	if (ret < 0)
 		dev_err(&i2c->dev, "failed to initialise WM8580\n");
 		dev_err(&i2c->dev, "failed to initialise WM8580\n");
-		goto err;
-	}
-
-	return ret;
-
-err:
-	kfree(codec);
-	kfree(i2c);
 	return ret;
 	return ret;
 }
 }
 
 
-static int wm8580_i2c_detach(struct i2c_client *client)
+static int wm8580_i2c_remove(struct i2c_client *client)
 {
 {
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
 	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
 	kfree(codec->reg_cache);
 	kfree(codec->reg_cache);
-	kfree(client);
 	return 0;
 	return 0;
 }
 }
 
 
-static int wm8580_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, wm8580_codec_probe);
-}
+static const struct i2c_device_id wm8580_i2c_id[] = {
+	{ "wm8580", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
 
 
-/* corgi i2c codec control layer */
 static struct i2c_driver wm8580_i2c_driver = {
 static struct i2c_driver wm8580_i2c_driver = {
 	.driver = {
 	.driver = {
 		.name = "WM8580 I2C Codec",
 		.name = "WM8580 I2C Codec",
 		.owner = THIS_MODULE,
 		.owner = THIS_MODULE,
 	},
 	},
-	.attach_adapter = wm8580_i2c_attach,
-	.detach_client =  wm8580_i2c_detach,
-	.command =        NULL,
+	.probe =    wm8580_i2c_probe,
+	.remove =   wm8580_i2c_remove,
+	.id_table = wm8580_i2c_id,
 };
 };
 
 
-static struct i2c_client client_template = {
-	.name =   "WM8580",
-	.driver = &wm8580_i2c_driver,
-};
+static int wm8580_add_i2c_device(struct platform_device *pdev,
+				 const struct wm8580_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&wm8580_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "wm8580", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&wm8580_i2c_driver);
+	return -ENODEV;
+}
 #endif
 #endif
 
 
 static int wm8580_probe(struct platform_device *pdev)
 static int wm8580_probe(struct platform_device *pdev)
@@ -1011,11 +1007,8 @@ static int wm8580_probe(struct platform_device *pdev)
 
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
 	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
 		codec->hw_write = (hw_write_t)i2c_master_send;
 		codec->hw_write = (hw_write_t)i2c_master_send;
-		ret = i2c_add_driver(&wm8580_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+		ret = wm8580_add_i2c_device(pdev, setup);
 	}
 	}
 #else
 #else
 		/* Add other interfaces here */
 		/* Add other interfaces here */
@@ -1034,6 +1027,7 @@ static int wm8580_remove(struct platform_device *pdev)
 	snd_soc_free_pcms(socdev);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 	snd_soc_dapm_free(socdev);
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
 	i2c_del_driver(&wm8580_i2c_driver);
 	i2c_del_driver(&wm8580_i2c_driver);
 #endif
 #endif
 	kfree(codec->private_data);
 	kfree(codec->private_data);
@@ -1048,6 +1042,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8580 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
 
 
+static int __init wm8580_modinit(void)
+{
+	return snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+}
+module_init(wm8580_modinit);
+
+static void __exit wm8580_exit(void)
+{
+	snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
+}
+module_exit(wm8580_exit);
+
 MODULE_DESCRIPTION("ASoC WM8580 driver");
 MODULE_DESCRIPTION("ASoC WM8580 driver");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 1 - 0
sound/soc/codecs/wm8580.h

@@ -29,6 +29,7 @@
 #define WM8580_CLKSRC_NONE 5
 #define WM8580_CLKSRC_NONE 5
 
 
 struct wm8580_setup_data {
 struct wm8580_setup_data {
+	int i2c_bus;
 	unsigned short i2c_address;
 	unsigned short i2c_address;
 };
 };
 
 

+ 585 - 0
sound/soc/codecs/wm8728.c

@@ -0,0 +1,585 @@
+/*
+ * wm8728.c  --  WM8728 ALSA SoC Audio driver
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8728.h"
+
+struct snd_soc_codec_device soc_codec_dev_wm8728;
+
+/*
+ * We can't read the WM8728 register space so we cache them instead.
+ * Note that the defaults here aren't the physical defaults, we latch
+ * the volume update bits, mute the output and enable infinite zero
+ * detect.
+ */
+static const u16 wm8728_reg_defaults[] = {
+	0x1ff,
+	0x1ff,
+	0x001,
+	0x100,
+};
+
+static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
+	return cache[reg];
+}
+
+static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
+	cache[reg] = value;
+}
+
+/*
+ * write to the WM8728 register space
+ */
+static int wm8728_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	/* data is
+	 *   D15..D9 WM8728 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8728_write_reg_cache(codec, reg, value);
+
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new wm8728_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
+		 0, 255, 0, wm8728_tlv),
+
+SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
+};
+
+static int wm8728_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8728_snd_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				  snd_soc_cnew(&wm8728_snd_controls[i],
+						codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/*
+ * DAPM controls.
+ */
+static const struct snd_soc_dapm_widget wm8728_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{"VOUTL", NULL, "DAC"},
+	{"VOUTR", NULL, "DAC"},
+};
+
+static int wm8728_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8728_dapm_widgets,
+				  ARRAY_SIZE(wm8728_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	snd_soc_dapm_new_widgets(codec);
+
+	return 0;
+}
+
+static int wm8728_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+
+	if (mute)
+		wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
+	else
+		wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
+
+	return 0;
+}
+
+static int wm8728_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_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+
+	dac &= ~0x18;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		dac |= 0x10;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dac |= 0x08;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8728_write(codec, WM8728_DACCTL, dac);
+
+	return 0;
+}
+
+static int wm8728_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
+
+	/* Currently only I2S is supported by the driver, though the
+	 * hardware is more flexible.
+	 */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* The hardware only support full slave mode */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		iface &= ~0x22;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |=  0x20;
+		iface &= ~0x02;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x02;
+		iface &= ~0x20;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= 0x22;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8728_write(codec, WM8728_IFCTL, iface);
+	return 0;
+}
+
+static int wm8728_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 reg;
+	int i;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Power everything up... */
+			reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+			wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
+
+			/* ..then sync in the register cache. */
+			for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
+				wm8728_write(codec, i,
+					     wm8728_read_reg_cache(codec, i));
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+		wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8728_RATES (SNDRV_PCM_RATE_8000_192000)
+
+#define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai wm8728_dai = {
+	.name = "WM8728",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8728_RATES,
+		.formats = WM8728_FORMATS,
+	},
+	.ops = {
+		 .hw_params = wm8728_hw_params,
+		 .digital_mute = wm8728_mute,
+		 .set_fmt = wm8728_set_dai_fmt,
+	}
+};
+EXPORT_SYMBOL_GPL(wm8728_dai);
+
+static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm8728_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	wm8728_set_bias_level(codec, codec->suspend_bias_level);
+
+	return 0;
+}
+
+/*
+ * initialise the WM8728 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int wm8728_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret = 0;
+
+	codec->name = "WM8728";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8728_read_reg_cache;
+	codec->write = wm8728_write;
+	codec->set_bias_level = wm8728_set_bias_level;
+	codec->dai = &wm8728_dai;
+	codec->num_dai = 1;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->reg_cache_size = ARRAY_SIZE(wm8728_reg_defaults);
+	codec->reg_cache = kmemdup(wm8728_reg_defaults,
+				   sizeof(wm8728_reg_defaults),
+				   GFP_KERNEL);
+	if (codec->reg_cache == NULL)
+		return -ENOMEM;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8728: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	/* power on device */
+	wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	wm8728_add_controls(codec);
+	wm8728_add_widgets(codec);
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8728: failed to register card\n");
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+static struct snd_soc_device *wm8728_socdev;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+/*
+ * WM8728 2 wire address is determined by GPIO5
+ * state during powerup.
+ *    low  = 0x1a
+ *    high = 0x1b
+ */
+
+static int wm8728_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct snd_soc_device *socdev = wm8728_socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret;
+
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	ret = wm8728_init(socdev);
+	if (ret < 0)
+		pr_err("failed to initialise WM8728\n");
+
+	return ret;
+}
+
+static int wm8728_i2c_remove(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
+	kfree(codec->reg_cache);
+	return 0;
+}
+
+static const struct i2c_device_id wm8728_i2c_id[] = {
+	{ "wm8728", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
+
+static struct i2c_driver wm8728_i2c_driver = {
+	.driver = {
+		.name = "WM8728 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8728_i2c_probe,
+	.remove =   wm8728_i2c_remove,
+	.id_table = wm8728_i2c_id,
+};
+
+static int wm8728_add_i2c_device(struct platform_device *pdev,
+				 const struct wm8728_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&wm8728_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->i2c_address;
+	strlcpy(info.type, "wm8728", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&wm8728_i2c_driver);
+	return -ENODEV;
+}
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8728_spi_probe(struct spi_device *spi)
+{
+	struct snd_soc_device *socdev = wm8728_socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret;
+
+	codec->control_data = spi;
+
+	ret = wm8728_init(socdev);
+	if (ret < 0)
+		dev_err(&spi->dev, "failed to initialise WM8728\n");
+
+	return ret;
+}
+
+static int __devexit wm8728_spi_remove(struct spi_device *spi)
+{
+	return 0;
+}
+
+static struct spi_driver wm8728_spi_driver = {
+	.driver = {
+		.name	= "wm8728",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= wm8728_spi_probe,
+	.remove		= __devexit_p(wm8728_spi_remove),
+};
+
+static int wm8728_spi_write(struct spi_device *spi, const char *data, int len)
+{
+	struct spi_transfer t;
+	struct spi_message m;
+	u8 msg[2];
+
+	if (len <= 0)
+		return 0;
+
+	msg[0] = data[0];
+	msg[1] = data[1];
+
+	spi_message_init(&m);
+	memset(&t, 0, (sizeof t));
+
+	t.tx_buf = &msg[0];
+	t.len = len;
+
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	return len;
+}
+#endif /* CONFIG_SPI_MASTER */
+
+static int wm8728_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct wm8728_setup_data *setup;
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	setup = socdev->codec_data;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	wm8728_socdev = socdev;
+	ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->i2c_address) {
+		codec->hw_write = (hw_write_t)i2c_master_send;
+		ret = wm8728_add_i2c_device(pdev, setup);
+	}
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	if (setup->spi) {
+		codec->hw_write = (hw_write_t)wm8728_spi_write;
+		ret = spi_register_driver(&wm8728_spi_driver);
+		if (ret != 0)
+			printk(KERN_ERR "can't add spi driver");
+	}
+#endif
+
+	if (ret != 0)
+		kfree(codec);
+
+	return ret;
+}
+
+/* power down chip */
+static int wm8728_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (codec->control_data)
+		wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_unregister_device(codec->control_data);
+	i2c_del_driver(&wm8728_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&wm8728_spi_driver);
+#endif
+	kfree(codec);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8728 = {
+	.probe = 	wm8728_probe,
+	.remove = 	wm8728_remove,
+	.suspend = 	wm8728_suspend,
+	.resume =	wm8728_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8728);
+
+static int __init wm8728_modinit(void)
+{
+	return snd_soc_register_dai(&wm8728_dai);
+}
+module_init(wm8728_modinit);
+
+static void __exit wm8728_exit(void)
+{
+	snd_soc_unregister_dai(&wm8728_dai);
+}
+module_exit(wm8728_exit);
+
+MODULE_DESCRIPTION("ASoC WM8728 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");

+ 30 - 0
sound/soc/codecs/wm8728.h

@@ -0,0 +1,30 @@
+/*
+ * wm8728.h  --  WM8728 ASoC codec driver
+ *
+ * Copyright 2008 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8728_H
+#define _WM8728_H
+
+#define WM8728_DACLVOL   0x00
+#define WM8728_DACRVOL   0x01
+#define WM8728_DACCTL    0x02
+#define WM8728_IFCTL     0x03
+
+struct wm8728_setup_data {
+	int            spi;
+	int            i2c_bus;
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8728_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8728;
+
+#endif

+ 19 - 6
sound/soc/codecs/wm8731.c

@@ -264,7 +264,8 @@ static inline int get_coeff(int mclk, int rate)
 }
 }
 
 
 static int wm8731_hw_params(struct snd_pcm_substream *substream,
 static int wm8731_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -293,7 +294,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 	return 0;
 }
 }
 
 
-static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
+static int wm8731_pcm_prepare(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -305,7 +307,8 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
 	return 0;
 	return 0;
 }
 }
 
 
-static void wm8731_shutdown(struct snd_pcm_substream *substream)
+static void wm8731_shutdown(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -461,8 +464,6 @@ struct snd_soc_dai wm8731_dai = {
 		.prepare = wm8731_pcm_prepare,
 		.prepare = wm8731_pcm_prepare,
 		.hw_params = wm8731_hw_params,
 		.hw_params = wm8731_hw_params,
 		.shutdown = wm8731_shutdown,
 		.shutdown = wm8731_shutdown,
-	},
-	.dai_ops = {
 		.digital_mute = wm8731_mute,
 		.digital_mute = wm8731_mute,
 		.set_sysclk = wm8731_set_dai_sysclk,
 		.set_sysclk = wm8731_set_dai_sysclk,
 		.set_fmt = wm8731_set_dai_fmt,
 		.set_fmt = wm8731_set_dai_fmt,
@@ -544,7 +545,7 @@ static int wm8731_init(struct snd_soc_device *socdev)
 
 
 	wm8731_add_controls(codec);
 	wm8731_add_controls(codec);
 	wm8731_add_widgets(codec);
 	wm8731_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "wm8731: failed to register card\n");
 		printk(KERN_ERR "wm8731: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -792,6 +793,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8731 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
 
 
+static int __init wm8731_modinit(void)
+{
+	return snd_soc_register_dai(&wm8731_dai);
+}
+module_init(wm8731_modinit);
+
+static void __exit wm8731_exit(void)
+{
+	snd_soc_unregister_dai(&wm8731_dai);
+}
+module_exit(wm8731_exit);
+
 MODULE_DESCRIPTION("ASoC WM8731 driver");
 MODULE_DESCRIPTION("ASoC WM8731 driver");
 MODULE_AUTHOR("Richard Purdie");
 MODULE_AUTHOR("Richard Purdie");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 15 - 4
sound/soc/codecs/wm8750.c

@@ -614,7 +614,8 @@ static int wm8750_set_dai_fmt(struct snd_soc_dai *codec_dai,
 }
 }
 
 
 static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
 static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -709,8 +710,6 @@ struct snd_soc_dai wm8750_dai = {
 		.formats = WM8750_FORMATS,},
 		.formats = WM8750_FORMATS,},
 	.ops = {
 	.ops = {
 		.hw_params = wm8750_pcm_hw_params,
 		.hw_params = wm8750_pcm_hw_params,
-	},
-	.dai_ops = {
 		.digital_mute = wm8750_mute,
 		.digital_mute = wm8750_mute,
 		.set_fmt = wm8750_set_dai_fmt,
 		.set_fmt = wm8750_set_dai_fmt,
 		.set_sysclk = wm8750_set_dai_sysclk,
 		.set_sysclk = wm8750_set_dai_sysclk,
@@ -819,7 +818,7 @@ static int wm8750_init(struct snd_soc_device *socdev)
 
 
 	wm8750_add_controls(codec);
 	wm8750_add_controls(codec);
 	wm8750_add_widgets(codec);
 	wm8750_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "wm8750: failed to register card\n");
 		printk(KERN_ERR "wm8750: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -1086,6 +1085,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8750 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8750);
 
 
+static int __init wm8750_modinit(void)
+{
+	return snd_soc_register_dai(&wm8750_dai);
+}
+module_init(wm8750_modinit);
+
+static void __exit wm8750_exit(void)
+{
+	snd_soc_unregister_dai(&wm8750_dai);
+}
+module_exit(wm8750_exit);
+
 MODULE_DESCRIPTION("ASoC WM8750 driver");
 MODULE_DESCRIPTION("ASoC WM8750 driver");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 24 - 15
sound/soc/codecs/wm8753.c

@@ -922,7 +922,8 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
  * Set PCM DAI bit size and sample rate.
  * Set PCM DAI bit size and sample rate.
  */
  */
 static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
 static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -1155,7 +1156,8 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
  * Set PCM DAI bit size and sample rate.
  * Set PCM DAI bit size and sample rate.
  */
  */
 static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
 static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -1323,16 +1325,15 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
 		.channels_min = 1,
 		.channels_min = 1,
 		.channels_max = 2,
 		.channels_max = 2,
 		.rates = WM8753_RATES,
 		.rates = WM8753_RATES,
-		.formats = WM8753_FORMATS,},
+		.formats = WM8753_FORMATS},
 	.capture = { /* dummy for fast DAI switching */
 	.capture = { /* dummy for fast DAI switching */
 		.stream_name = "Capture",
 		.stream_name = "Capture",
 		.channels_min = 1,
 		.channels_min = 1,
 		.channels_max = 2,
 		.channels_max = 2,
 		.rates = WM8753_RATES,
 		.rates = WM8753_RATES,
-		.formats = WM8753_FORMATS,},
+		.formats = WM8753_FORMATS},
 	.ops = {
 	.ops = {
-		.hw_params = wm8753_i2s_hw_params,},
-	.dai_ops = {
+		.hw_params = wm8753_i2s_hw_params,
 		.digital_mute = wm8753_mute,
 		.digital_mute = wm8753_mute,
 		.set_fmt = wm8753_mode1h_set_dai_fmt,
 		.set_fmt = wm8753_mode1h_set_dai_fmt,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1356,8 +1357,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
 		.rates = WM8753_RATES,
 		.rates = WM8753_RATES,
 		.formats = WM8753_FORMATS,},
 		.formats = WM8753_FORMATS,},
 	.ops = {
 	.ops = {
-		.hw_params = wm8753_pcm_hw_params,},
-	.dai_ops = {
+		.hw_params = wm8753_pcm_hw_params,
 		.digital_mute = wm8753_mute,
 		.digital_mute = wm8753_mute,
 		.set_fmt = wm8753_mode1v_set_dai_fmt,
 		.set_fmt = wm8753_mode1v_set_dai_fmt,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1385,8 +1385,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
 		.rates = WM8753_RATES,
 		.rates = WM8753_RATES,
 		.formats = WM8753_FORMATS,},
 		.formats = WM8753_FORMATS,},
 	.ops = {
 	.ops = {
-		.hw_params = wm8753_pcm_hw_params,},
-	.dai_ops = {
+		.hw_params = wm8753_pcm_hw_params,
 		.digital_mute = wm8753_mute,
 		.digital_mute = wm8753_mute,
 		.set_fmt = wm8753_mode2_set_dai_fmt,
 		.set_fmt = wm8753_mode2_set_dai_fmt,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1410,8 +1409,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
 		.rates = WM8753_RATES,
 		.rates = WM8753_RATES,
 		.formats = WM8753_FORMATS,},
 		.formats = WM8753_FORMATS,},
 	.ops = {
 	.ops = {
-		.hw_params = wm8753_i2s_hw_params,},
-	.dai_ops = {
+		.hw_params = wm8753_i2s_hw_params,
 		.digital_mute = wm8753_mute,
 		.digital_mute = wm8753_mute,
 		.set_fmt = wm8753_mode3_4_set_dai_fmt,
 		.set_fmt = wm8753_mode3_4_set_dai_fmt,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1439,8 +1437,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
 		.rates = WM8753_RATES,
 		.rates = WM8753_RATES,
 		.formats = WM8753_FORMATS,},
 		.formats = WM8753_FORMATS,},
 	.ops = {
 	.ops = {
-		.hw_params = wm8753_i2s_hw_params,},
-	.dai_ops = {
+		.hw_params = wm8753_i2s_hw_params,
 		.digital_mute = wm8753_mute,
 		.digital_mute = wm8753_mute,
 		.set_fmt = wm8753_mode3_4_set_dai_fmt,
 		.set_fmt = wm8753_mode3_4_set_dai_fmt,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
 		.set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1608,7 +1605,7 @@ static int wm8753_init(struct snd_soc_device *socdev)
 
 
 	wm8753_add_controls(codec);
 	wm8753_add_controls(codec);
 	wm8753_add_widgets(codec);
 	wm8753_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "wm8753: failed to register card\n");
 		printk(KERN_ERR "wm8753: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -1877,6 +1874,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8753 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
 
 
+static int __init wm8753_modinit(void)
+{
+	return snd_soc_register_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
+}
+module_init(wm8753_modinit);
+
+static void __exit wm8753_exit(void)
+{
+	snd_soc_unregister_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
+}
+module_exit(wm8753_exit);
+
 MODULE_DESCRIPTION("ASoC WM8753 driver");
 MODULE_DESCRIPTION("ASoC WM8753 driver");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 107 - 155
sound/soc/codecs/wm8900.c

@@ -138,6 +138,10 @@
 struct snd_soc_codec_device soc_codec_dev_wm8900;
 struct snd_soc_codec_device soc_codec_dev_wm8900;
 
 
 struct wm8900_priv {
 struct wm8900_priv {
+	struct snd_soc_codec codec;
+
+	u16 reg_cache[WM8900_MAXREG];
+
 	u32 fll_in; /* FLL input frequency */
 	u32 fll_in; /* FLL input frequency */
 	u32 fll_out; /* FLL output frequency */
 	u32 fll_out; /* FLL output frequency */
 };
 };
@@ -727,7 +731,8 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec)
 }
 }
 
 
 static int wm8900_hw_params(struct snd_pcm_substream *substream,
 static int wm8900_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -1117,8 +1122,6 @@ struct snd_soc_dai wm8900_dai = {
 	 },
 	 },
 	.ops = {
 	.ops = {
 		.hw_params = wm8900_hw_params,
 		.hw_params = wm8900_hw_params,
-	 },
-	.dai_ops = {
 		 .set_clkdiv = wm8900_set_dai_clkdiv,
 		 .set_clkdiv = wm8900_set_dai_clkdiv,
 		 .set_pll = wm8900_set_dai_pll,
 		 .set_pll = wm8900_set_dai_pll,
 		 .set_fmt = wm8900_set_dai_fmt,
 		 .set_fmt = wm8900_set_dai_fmt,
@@ -1283,16 +1286,28 @@ static int wm8900_resume(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-/*
- * initialise the WM8900 driver
- * register the mixer and dsp interfaces with the kernel
- */
-static int wm8900_init(struct snd_soc_device *socdev)
+static struct snd_soc_codec *wm8900_codec;
+
+static int wm8900_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
 {
 {
-	struct snd_soc_codec *codec = socdev->codec;
-	int ret = 0;
+	struct wm8900_priv *wm8900;
+	struct snd_soc_codec *codec;
 	unsigned int reg;
 	unsigned int reg;
-	struct i2c_client *i2c_client = socdev->codec->control_data;
+	int ret;
+
+	wm8900 = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
+	if (wm8900 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8900->codec;
+	codec->private_data = wm8900;
+	codec->reg_cache = &wm8900->reg_cache[0];
+	codec->reg_cache_size = WM8900_MAXREG;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
 
 
 	codec->name = "WM8900";
 	codec->name = "WM8900";
 	codec->owner = THIS_MODULE;
 	codec->owner = THIS_MODULE;
@@ -1300,33 +1315,28 @@ static int wm8900_init(struct snd_soc_device *socdev)
 	codec->write = wm8900_write;
 	codec->write = wm8900_write;
 	codec->dai = &wm8900_dai;
 	codec->dai = &wm8900_dai;
 	codec->num_dai = 1;
 	codec->num_dai = 1;
-	codec->reg_cache_size = WM8900_MAXREG;
-	codec->reg_cache = kmemdup(wm8900_reg_defaults,
-				   sizeof(wm8900_reg_defaults), GFP_KERNEL);
-
-	if (codec->reg_cache == NULL)
-		return -ENOMEM;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+	codec->control_data = i2c;
+	codec->set_bias_level = wm8900_set_bias_level;
+	codec->dev = &i2c->dev;
 
 
 	reg = wm8900_read(codec, WM8900_REG_ID);
 	reg = wm8900_read(codec, WM8900_REG_ID);
 	if (reg != 0x8900) {
 	if (reg != 0x8900) {
-		dev_err(&i2c_client->dev, "Device is not a WM8900 - ID %x\n",
-			reg);
-		return -ENODEV;
-	}
-
-	codec->private_data = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL);
-	if (codec->private_data == NULL) {
-		ret = -ENOMEM;
-		goto priv_err;
+		dev_err(&i2c->dev, "Device is not a WM8900 - ID %x\n", reg);
+		ret = -ENODEV;
+		goto err;
 	}
 	}
 
 
 	/* Read back from the chip */
 	/* Read back from the chip */
 	reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
 	reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
 	reg = (reg >> 12) & 0xf;
 	reg = (reg >> 12) & 0xf;
-	dev_info(&i2c_client->dev, "WM8900 revision %d\n", reg);
+	dev_info(&i2c->dev, "WM8900 revision %d\n", reg);
 
 
 	wm8900_reset(codec);
 	wm8900_reset(codec);
 
 
+	/* Turn the chip on */
+	wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
 	/* Latch the volume update bits */
 	/* Latch the volume update bits */
 	wm8900_write(codec, WM8900_REG_LINVOL,
 	wm8900_write(codec, WM8900_REG_LINVOL,
 		     wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
 		     wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
@@ -1352,160 +1362,98 @@ static int wm8900_init(struct snd_soc_device *socdev)
 	/* Set the DAC and mixer output bias */
 	/* Set the DAC and mixer output bias */
 	wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
 	wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
 
 
-	/* Register pcms */
-	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-	if (ret < 0) {
-		dev_err(&i2c_client->dev, "Failed to register new PCMs\n");
-		goto pcm_err;
-	}
-
-	/* Turn the chip on */
-	codec->bias_level = SND_SOC_BIAS_OFF;
-	wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	wm8900_add_controls(codec);
-	wm8900_add_widgets(codec);
-
-	ret = snd_soc_register_card(socdev);
-	if (ret < 0) {
-		dev_err(&i2c_client->dev, "Failed to register card\n");
-		goto card_err;
-	}
-	return ret;
-
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
-pcm_err:
-	kfree(codec->reg_cache);
-priv_err:
-	kfree(codec->private_data);
-	return ret;
-}
+	wm8900_dai.dev = &i2c->dev;
 
 
-static struct snd_soc_device *wm8900_socdev;
+	wm8900_codec = codec;
 
 
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-
-static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-
-/* Magic definition of all other variables and things */
-I2C_CLIENT_INSMOD;
-
-static struct i2c_driver wm8900_i2c_driver;
-static struct i2c_client client_template;
-
-/* If the i2c layer weren't so broken, we could pass this kind of data
-   around */
-static int wm8900_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-{
-	struct snd_soc_device *socdev = wm8900_socdev;
-	struct wm8900_setup_data *setup = socdev->codec_data;
-	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c;
-	int ret;
-
-	if (addr != setup->i2c_address)
-		return -ENODEV;
-
-	dev_err(&adap->dev, "Probe on %x\n", addr);
-
-	client_template.adapter = adap;
-	client_template.addr = addr;
-
-	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
-	if (i2c == NULL) {
-		kfree(codec);
-		return -ENOMEM;
-	}
-	i2c_set_clientdata(i2c, codec);
-	codec->control_data = i2c;
-
-	ret = i2c_attach_client(i2c);
-	if (ret < 0) {
-		dev_err(&adap->dev,
-			"failed to attach codec at addr %x\n", addr);
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
 		goto err;
 		goto err;
 	}
 	}
 
 
-	ret = wm8900_init(socdev);
-	if (ret < 0) {
-		dev_err(&adap->dev, "failed to initialise WM8900\n");
-		goto err;
+	ret = snd_soc_register_dai(&wm8900_dai);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);
+		goto err_codec;
 	}
 	}
+
 	return ret;
 	return ret;
 
 
+err_codec:
+	snd_soc_unregister_codec(codec);
 err:
 err:
-	kfree(codec);
-	kfree(i2c);
+	kfree(wm8900);
+	wm8900_codec = NULL;
 	return ret;
 	return ret;
 }
 }
 
 
-static int wm8900_i2c_detach(struct i2c_client *client)
+static int wm8900_i2c_remove(struct i2c_client *client)
 {
 {
-	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	i2c_detach_client(client);
-	kfree(codec->reg_cache);
-	kfree(client);
+	snd_soc_unregister_dai(&wm8900_dai);
+	snd_soc_unregister_codec(wm8900_codec);
+
+	wm8900_set_bias_level(wm8900_codec, SND_SOC_BIAS_OFF);
+
+	wm8900_dai.dev = NULL;
+	kfree(wm8900_codec->private_data);
+	wm8900_codec = NULL;
+
 	return 0;
 	return 0;
 }
 }
 
 
-static int wm8900_i2c_attach(struct i2c_adapter *adap)
-{
-	return i2c_probe(adap, &addr_data, wm8900_codec_probe);
-}
+static const struct i2c_device_id wm8900_i2c_id[] = {
+	{ "wm8900", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id);
 
 
-/* corgi i2c codec control layer */
 static struct i2c_driver wm8900_i2c_driver = {
 static struct i2c_driver wm8900_i2c_driver = {
 	.driver = {
 	.driver = {
-		.name = "WM8900 I2C codec",
+		.name = "WM8900",
 		.owner = THIS_MODULE,
 		.owner = THIS_MODULE,
 	},
 	},
-	.attach_adapter = wm8900_i2c_attach,
-	.detach_client =  wm8900_i2c_detach,
-	.command =        NULL,
-};
-
-static struct i2c_client client_template = {
-	.name =   "WM8900",
-	.driver = &wm8900_i2c_driver,
+	.probe = wm8900_i2c_probe,
+	.remove = wm8900_i2c_remove,
+	.id_table = wm8900_i2c_id,
 };
 };
-#endif
 
 
 static int wm8900_probe(struct platform_device *pdev)
 static int wm8900_probe(struct platform_device *pdev)
 {
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct wm8900_setup_data *setup;
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec *codec;
 	int ret = 0;
 	int ret = 0;
 
 
-	dev_info(&pdev->dev, "WM8900 Audio Codec\n");
-
-	setup = socdev->codec_data;
-	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-	if (codec == NULL)
-		return -ENOMEM;
-
-	mutex_init(&codec->mutex);
-	INIT_LIST_HEAD(&codec->dapm_widgets);
-	INIT_LIST_HEAD(&codec->dapm_paths);
+	if (!wm8900_codec) {
+		dev_err(&pdev->dev, "I2C client not yet instantiated\n");
+		return -ENODEV;
+	}
 
 
+	codec = wm8900_codec;
 	socdev->codec = codec;
 	socdev->codec = codec;
 
 
-	codec->set_bias_level = wm8900_set_bias_level;
+	/* Register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to register new PCMs\n");
+		goto pcm_err;
+	}
 
 
-	wm8900_socdev = socdev;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-	if (setup->i2c_address) {
-		normal_i2c[0] = setup->i2c_address;
-		codec->hw_write = (hw_write_t)i2c_master_send;
-		ret = i2c_add_driver(&wm8900_i2c_driver);
-		if (ret != 0)
-			printk(KERN_ERR "can't add i2c driver");
+	wm8900_add_controls(codec);
+	wm8900_add_widgets(codec);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Failed to register card\n");
+		goto card_err;
 	}
 	}
-#else
-#error Non-I2C interfaces not yet supported
-#endif
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1513,17 +1461,9 @@ static int wm8900_probe(struct platform_device *pdev)
 static int wm8900_remove(struct platform_device *pdev)
 static int wm8900_remove(struct platform_device *pdev)
 {
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_codec *codec = socdev->codec;
-
-	if (codec->control_data)
-		wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 
 	snd_soc_free_pcms(socdev);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 	snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-	i2c_del_driver(&wm8900_i2c_driver);
-#endif
-	kfree(codec);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1536,6 +1476,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8900 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900);
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900);
 
 
+static int __init wm8900_modinit(void)
+{
+	return i2c_add_driver(&wm8900_i2c_driver);
+}
+module_init(wm8900_modinit);
+
+static void __exit wm8900_exit(void)
+{
+	i2c_del_driver(&wm8900_i2c_driver);
+}
+module_exit(wm8900_exit);
+
 MODULE_DESCRIPTION("ASoC WM8900 driver");
 MODULE_DESCRIPTION("ASoC WM8900 driver");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 0 - 6
sound/soc/codecs/wm8900.h

@@ -52,12 +52,6 @@
 #define WM8900_DAC_CLKDIV_5_5 0x14
 #define WM8900_DAC_CLKDIV_5_5 0x14
 #define WM8900_DAC_CLKDIV_6   0x18
 #define WM8900_DAC_CLKDIV_6   0x18
 
 
-#define WM8900_
-
-struct wm8900_setup_data {
-	unsigned short i2c_address;
-};
-
 extern struct snd_soc_dai wm8900_dai;
 extern struct snd_soc_dai wm8900_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8900;
 extern struct snd_soc_codec_device soc_codec_dev_wm8900;
 
 

+ 125 - 143
sound/soc/codecs/wm8903.c

@@ -33,19 +33,6 @@
 
 
 #include "wm8903.h"
 #include "wm8903.h"
 
 
-struct wm8903_priv {
-	int sysclk;
-
-	/* Reference counts */
-	int charge_pump_users;
-	int class_w_users;
-	int playback_active;
-	int capture_active;
-
-	struct snd_pcm_substream *master_substream;
-	struct snd_pcm_substream *slave_substream;
-};
-
 /* Register defaults at reset */
 /* Register defaults at reset */
 static u16 wm8903_reg_defaults[] = {
 static u16 wm8903_reg_defaults[] = {
 	0x8903,     /* R0   - SW Reset and ID */
 	0x8903,     /* R0   - SW Reset and ID */
@@ -223,6 +210,23 @@ static u16 wm8903_reg_defaults[] = {
 	0x0000,     /* R172 - Analogue Output Bias 0 */
 	0x0000,     /* R172 - Analogue Output Bias 0 */
 };
 };
 
 
+struct wm8903_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[ARRAY_SIZE(wm8903_reg_defaults)];
+
+	int sysclk;
+
+	/* Reference counts */
+	int charge_pump_users;
+	int class_w_users;
+	int playback_active;
+	int capture_active;
+
+	struct snd_pcm_substream *master_substream;
+	struct snd_pcm_substream *slave_substream;
+};
+
+
 static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec,
 static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec,
 						 unsigned int reg)
 						 unsigned int reg)
 {
 {
@@ -360,6 +364,8 @@ static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
 static void wm8903_reset(struct snd_soc_codec *codec)
 static void wm8903_reset(struct snd_soc_codec *codec)
 {
 {
 	wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0);
 	wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0);
+	memcpy(codec->reg_cache, wm8903_reg_defaults,
+	       sizeof(wm8903_reg_defaults));
 }
 }
 
 
 #define WM8903_OUTPUT_SHORT 0x8
 #define WM8903_OUTPUT_SHORT 0x8
@@ -392,6 +398,7 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
 		break;
 		break;
 	default:
 	default:
 		BUG();
 		BUG();
+		return -EINVAL;  /* Spurious warning from some compilers */
 	}
 	}
 
 
 	switch (w->shift) {
 	switch (w->shift) {
@@ -403,6 +410,7 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
 		break;
 		break;
 	default:
 	default:
 		BUG();
 		BUG();
+		return -EINVAL;  /* Spurious warning from some compilers */
 	}
 	}
 
 
 	if (event & SND_SOC_DAPM_PRE_PMU) {
 	if (event & SND_SOC_DAPM_PRE_PMU) {
@@ -773,14 +781,14 @@ static const struct snd_kcontrol_new left_output_mixer[] = {
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
 SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
 SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
-SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 0, 1, 0),
 };
 };
 
 
 static const struct snd_kcontrol_new right_output_mixer[] = {
 static const struct snd_kcontrol_new right_output_mixer[] = {
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0),
 SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
 SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
-SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0),
+SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 0, 1, 0),
 };
 };
 
 
 static const struct snd_kcontrol_new left_speaker_mixer[] = {
 static const struct snd_kcontrol_new left_speaker_mixer[] = {
@@ -788,7 +796,7 @@ SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0),
 SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0),
 SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0),
 SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0,
 SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0,
-		1, 1, 0),
+		0, 1, 0),
 };
 };
 
 
 static const struct snd_kcontrol_new right_speaker_mixer[] = {
 static const struct snd_kcontrol_new right_speaker_mixer[] = {
@@ -797,7 +805,7 @@ SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0),
 SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
 SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
 		1, 1, 0),
 		1, 1, 0),
 SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
 SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0,
-		1, 1, 0),
+		0, 1, 0),
 };
 };
 
 
 static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = {
 static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = {
@@ -989,6 +997,9 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
 
 
 	case SND_SOC_BIAS_STANDBY:
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			wm8903_write(codec, WM8903_CLOCK_RATES_2,
+				     WM8903_CLK_SYS_ENA);
+
 			wm8903_run_sequence(codec, 0);
 			wm8903_run_sequence(codec, 0);
 			wm8903_sync_reg_cache(codec, codec->reg_cache);
 			wm8903_sync_reg_cache(codec, codec->reg_cache);
 
 
@@ -1019,6 +1030,9 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
 
 
 	case SND_SOC_BIAS_OFF:
 	case SND_SOC_BIAS_OFF:
 		wm8903_run_sequence(codec, 32);
 		wm8903_run_sequence(codec, 32);
+		reg = wm8903_read(codec, WM8903_CLOCK_RATES_2);
+		reg &= ~WM8903_CLK_SYS_ENA;
+		wm8903_write(codec, WM8903_CLOCK_RATES_2, reg);
 		break;
 		break;
 	}
 	}
 
 
@@ -1257,7 +1271,8 @@ static struct {
 	{ 0,      0 },
 	{ 0,      0 },
 };
 };
 
 
-static int wm8903_startup(struct snd_pcm_substream *substream)
+static int wm8903_startup(struct snd_pcm_substream *substream,
+			  struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -1298,7 +1313,8 @@ static int wm8903_startup(struct snd_pcm_substream *substream)
 	return 0;
 	return 0;
 }
 }
 
 
-static void wm8903_shutdown(struct snd_pcm_substream *substream)
+static void wm8903_shutdown(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -1317,7 +1333,8 @@ static void wm8903_shutdown(struct snd_pcm_substream *substream)
 }
 }
 
 
 static int wm8903_hw_params(struct snd_pcm_substream *substream,
 static int wm8903_hw_params(struct snd_pcm_substream *substream,
-			    struct snd_pcm_hw_params *params)
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -1515,8 +1532,6 @@ struct snd_soc_dai wm8903_dai = {
 		 .startup = wm8903_startup,
 		 .startup = wm8903_startup,
 		 .shutdown = wm8903_shutdown,
 		 .shutdown = wm8903_shutdown,
 		 .hw_params = wm8903_hw_params,
 		 .hw_params = wm8903_hw_params,
-	},
-	.dai_ops = {
 		 .digital_mute = wm8903_digital_mute,
 		 .digital_mute = wm8903_digital_mute,
 		 .set_fmt = wm8903_set_dai_fmt,
 		 .set_fmt = wm8903_set_dai_fmt,
 		 .set_sysclk = wm8903_set_dai_sysclk
 		 .set_sysclk = wm8903_set_dai_sysclk
@@ -1560,39 +1575,48 @@ static int wm8903_resume(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-/*
- * initialise the WM8903 driver
- * register the mixer and dsp interfaces with the kernel
- */
-static int wm8903_init(struct snd_soc_device *socdev)
+static struct snd_soc_codec *wm8903_codec;
+
+static int wm8903_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
 {
 {
-	struct snd_soc_codec *codec = socdev->codec;
-	struct i2c_client *i2c = codec->control_data;
-	int ret = 0;
+	struct wm8903_priv *wm8903;
+	struct snd_soc_codec *codec;
+	int ret;
 	u16 val;
 	u16 val;
 
 
-	val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
-	if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
-		dev_err(&i2c->dev,
-			"Device with ID register %x is not a WM8903\n", val);
-		return -ENODEV;
-	}
+	wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
+	if (wm8903 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8903->codec;
 
 
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->dev = &i2c->dev;
 	codec->name = "WM8903";
 	codec->name = "WM8903";
 	codec->owner = THIS_MODULE;
 	codec->owner = THIS_MODULE;
 	codec->read = wm8903_read;
 	codec->read = wm8903_read;
 	codec->write = wm8903_write;
 	codec->write = wm8903_write;
+	codec->hw_write = (hw_write_t)i2c_master_send;
 	codec->bias_level = SND_SOC_BIAS_OFF;
 	codec->bias_level = SND_SOC_BIAS_OFF;
 	codec->set_bias_level = wm8903_set_bias_level;
 	codec->set_bias_level = wm8903_set_bias_level;
 	codec->dai = &wm8903_dai;
 	codec->dai = &wm8903_dai;
 	codec->num_dai = 1;
 	codec->num_dai = 1;
-	codec->reg_cache_size = ARRAY_SIZE(wm8903_reg_defaults);
-	codec->reg_cache = kmemdup(wm8903_reg_defaults,
-				   sizeof(wm8903_reg_defaults),
-				   GFP_KERNEL);
-	if (codec->reg_cache == NULL) {
-		dev_err(&i2c->dev, "Failed to allocate register cache\n");
-		return -ENOMEM;
+	codec->reg_cache_size = ARRAY_SIZE(wm8903->reg_cache);
+	codec->reg_cache = &wm8903->reg_cache[0];
+	codec->private_data = wm8903;
+
+	i2c_set_clientdata(i2c, codec);
+	codec->control_data = i2c;
+
+	val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
+	if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
+		dev_err(&i2c->dev,
+			"Device with ID register %x is not a WM8903\n", val);
+		return -ENODEV;
 	}
 	}
 
 
 	val = wm8903_read(codec, WM8903_REVISION_NUMBER);
 	val = wm8903_read(codec, WM8903_REVISION_NUMBER);
@@ -1601,16 +1625,6 @@ static int wm8903_init(struct snd_soc_device *socdev)
 
 
 	wm8903_reset(codec);
 	wm8903_reset(codec);
 
 
-	/* register pcms */
-	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-	if (ret < 0) {
-		dev_err(&i2c->dev, "failed to create pcms\n");
-		goto pcm_err;
-	}
-
-	/* SYSCLK is required for pretty much anything */
-	wm8903_write(codec, WM8903_CLOCK_RATES_2, WM8903_CLK_SYS_ENA);
-
 	/* power on device */
 	/* power on device */
 	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 
@@ -1645,47 +1659,45 @@ static int wm8903_init(struct snd_soc_device *socdev)
 	val |= WM8903_DAC_MUTEMODE;
 	val |= WM8903_DAC_MUTEMODE;
 	wm8903_write(codec, WM8903_DAC_DIGITAL_1, val);
 	wm8903_write(codec, WM8903_DAC_DIGITAL_1, val);
 
 
-	wm8903_add_controls(codec);
-	wm8903_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
-	if (ret < 0) {
-		dev_err(&i2c->dev, "wm8903: failed to register card\n");
-		goto card_err;
+	wm8903_dai.dev = &i2c->dev;
+	wm8903_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_dai(&wm8903_dai);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to register DAI: %d\n", ret);
+		goto err_codec;
 	}
 	}
 
 
 	return ret;
 	return ret;
 
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
-pcm_err:
-	kfree(codec->reg_cache);
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	wm8903_codec = NULL;
+	kfree(wm8903);
 	return ret;
 	return ret;
 }
 }
 
 
-static struct snd_soc_device *wm8903_socdev;
-
-static int wm8903_i2c_probe(struct i2c_client *i2c,
-			    const struct i2c_device_id *id)
+static int wm8903_i2c_remove(struct i2c_client *client)
 {
 {
-	struct snd_soc_device *socdev = wm8903_socdev;
-	struct snd_soc_codec *codec = socdev->codec;
-	int ret;
+	struct snd_soc_codec *codec = i2c_get_clientdata(client);
 
 
-	i2c_set_clientdata(i2c, codec);
-	codec->control_data = i2c;
+	snd_soc_unregister_dai(&wm8903_dai);
+	snd_soc_unregister_codec(codec);
 
 
-	ret = wm8903_init(socdev);
-	if (ret < 0)
-		dev_err(&i2c->dev, "Device initialisation failed\n");
+	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
 
-	return ret;
-}
+	kfree(codec->private_data);
+
+	wm8903_codec = NULL;
+	wm8903_dai.dev = NULL;
 
 
-static int wm8903_i2c_remove(struct i2c_client *client)
-{
-	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	kfree(codec->reg_cache);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1709,75 +1721,37 @@ static struct i2c_driver wm8903_i2c_driver = {
 static int wm8903_probe(struct platform_device *pdev)
 static int wm8903_probe(struct platform_device *pdev)
 {
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct wm8903_setup_data *setup;
-	struct snd_soc_codec *codec;
-	struct wm8903_priv *wm8903;
-	struct i2c_board_info board_info;
-	struct i2c_adapter *adapter;
-	struct i2c_client *i2c_client;
 	int ret = 0;
 	int ret = 0;
 
 
-	setup = socdev->codec_data;
-
-	if (!setup->i2c_address) {
-		dev_err(&pdev->dev, "No codec address provided\n");
-		return -ENODEV;
+	if (!wm8903_codec) {
+		dev_err(&pdev->dev, "I2C device not yet probed\n");
+		goto err;
 	}
 	}
 
 
-	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-	if (codec == NULL)
-		return -ENOMEM;
+	socdev->codec = wm8903_codec;
 
 
-	wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL);
-	if (wm8903 == NULL) {
-		ret = -ENOMEM;
-		goto err_codec;
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to create pcms\n");
+		goto err;
 	}
 	}
 
 
-	codec->private_data = wm8903;
-	socdev->codec = codec;
-	mutex_init(&codec->mutex);
-	INIT_LIST_HEAD(&codec->dapm_widgets);
-	INIT_LIST_HEAD(&codec->dapm_paths);
-
-	wm8903_socdev = socdev;
+	wm8903_add_controls(socdev->codec);
+	wm8903_add_widgets(socdev->codec);
 
 
-	codec->hw_write = (hw_write_t)i2c_master_send;
-	ret = i2c_add_driver(&wm8903_i2c_driver);
-	if (ret != 0) {
-		dev_err(&pdev->dev, "can't add i2c driver\n");
-		goto err_priv;
-	} else {
-		memset(&board_info, 0, sizeof(board_info));
-		strlcpy(board_info.type, "wm8903", I2C_NAME_SIZE);
-		board_info.addr = setup->i2c_address;
-
-		adapter = i2c_get_adapter(setup->i2c_bus);
-		if (!adapter) {
-			dev_err(&pdev->dev, "Can't get I2C bus %d\n",
-				setup->i2c_bus);
-			ret = -ENODEV;
-			goto err_adapter;
-		}
-
-		i2c_client = i2c_new_device(adapter, &board_info);
-		i2c_put_adapter(adapter);
-		if (i2c_client == NULL) {
-			dev_err(&pdev->dev,
-				"I2C driver registration failed\n");
-			ret = -ENODEV;
-			goto err_adapter;
-		}
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "wm8903: failed to register card\n");
+		goto card_err;
 	}
 	}
 
 
 	return ret;
 	return ret;
 
 
-err_adapter:
-	i2c_del_driver(&wm8903_i2c_driver);
-err_priv:
-	kfree(codec->private_data);
-err_codec:
-	kfree(codec);
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+err:
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1792,10 +1766,6 @@ static int wm8903_remove(struct platform_device *pdev)
 
 
 	snd_soc_free_pcms(socdev);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 	snd_soc_dapm_free(socdev);
-	i2c_unregister_device(socdev->codec->control_data);
-	i2c_del_driver(&wm8903_i2c_driver);
-	kfree(codec->private_data);
-	kfree(codec);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1808,6 +1778,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8903 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903);
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903);
 
 
+static int __init wm8903_modinit(void)
+{
+	return i2c_add_driver(&wm8903_i2c_driver);
+}
+module_init(wm8903_modinit);
+
+static void __exit wm8903_exit(void)
+{
+	i2c_del_driver(&wm8903_i2c_driver);
+}
+module_exit(wm8903_exit);
+
 MODULE_DESCRIPTION("ASoC WM8903 driver");
 MODULE_DESCRIPTION("ASoC WM8903 driver");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 0 - 5
sound/soc/codecs/wm8903.h

@@ -18,11 +18,6 @@
 extern struct snd_soc_dai wm8903_dai;
 extern struct snd_soc_dai wm8903_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8903;
 extern struct snd_soc_codec_device soc_codec_dev_wm8903;
 
 
-struct wm8903_setup_data {
-	int i2c_bus;
-	int i2c_address;
-};
-
 #define WM8903_MCLK_DIV_2 1
 #define WM8903_MCLK_DIV_2 1
 #define WM8903_CLK_SYS    2
 #define WM8903_CLK_SYS    2
 #define WM8903_BCLK       3
 #define WM8903_BCLK       3

+ 15 - 4
sound/soc/codecs/wm8971.c

@@ -541,7 +541,8 @@ static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai,
 }
 }
 
 
 static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
 static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -634,8 +635,6 @@ struct snd_soc_dai wm8971_dai = {
 		.formats = WM8971_FORMATS,},
 		.formats = WM8971_FORMATS,},
 	.ops = {
 	.ops = {
 		.hw_params = wm8971_pcm_hw_params,
 		.hw_params = wm8971_pcm_hw_params,
-	},
-	.dai_ops = {
 		.digital_mute = wm8971_mute,
 		.digital_mute = wm8971_mute,
 		.set_fmt = wm8971_set_dai_fmt,
 		.set_fmt = wm8971_set_dai_fmt,
 		.set_sysclk = wm8971_set_dai_sysclk,
 		.set_sysclk = wm8971_set_dai_sysclk,
@@ -748,7 +747,7 @@ static int wm8971_init(struct snd_soc_device *socdev)
 
 
 	wm8971_add_controls(codec);
 	wm8971_add_controls(codec);
 	wm8971_add_widgets(codec);
 	wm8971_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "wm8971: failed to register card\n");
 		printk(KERN_ERR "wm8971: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -936,6 +935,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8971 = {
 
 
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
 
 
+static int __init wm8971_modinit(void)
+{
+	return snd_soc_register_dai(&wm8971_dai);
+}
+module_init(wm8971_modinit);
+
+static void __exit wm8971_exit(void)
+{
+	snd_soc_unregister_dai(&wm8971_dai);
+}
+module_exit(wm8971_exit);
+
 MODULE_DESCRIPTION("ASoC WM8971 driver");
 MODULE_DESCRIPTION("ASoC WM8971 driver");
 MODULE_AUTHOR("Lab126");
 MODULE_AUTHOR("Lab126");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 34 - 9
sound/soc/codecs/wm8990.c

@@ -106,6 +106,7 @@ static const u16 wm8990_reg[] = {
 	0x0008,     /* R60 - PLL1 */
 	0x0008,     /* R60 - PLL1 */
 	0x0031,     /* R61 - PLL2 */
 	0x0031,     /* R61 - PLL2 */
 	0x0026,     /* R62 - PLL3 */
 	0x0026,     /* R62 - PLL3 */
+	0x0000,	    /* R63 - Driver internal */
 };
 };
 
 
 /*
 /*
@@ -126,10 +127,9 @@ static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
 	unsigned int reg, unsigned int value)
 	unsigned int reg, unsigned int value)
 {
 {
 	u16 *cache = codec->reg_cache;
 	u16 *cache = codec->reg_cache;
-	BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
 
 
-	/* Reset register is uncached */
-	if (reg == 0)
+	/* Reset register and reserved registers are uncached */
+	if (reg == 0 || reg > ARRAY_SIZE(wm8990_reg) - 1)
 		return;
 		return;
 
 
 	cache[reg] = value;
 	cache[reg] = value;
@@ -1172,7 +1172,8 @@ static int wm8990_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
  * Set PCM DAI bit size and sample rate.
  * Set PCM DAI bit size and sample rate.
  */
  */
 static int wm8990_hw_params(struct snd_pcm_substream *substream,
 static int wm8990_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_device *socdev = rtd->socdev;
@@ -1222,8 +1223,14 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
 	switch (level) {
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 	case SND_SOC_BIAS_ON:
 		break;
 		break;
+
 	case SND_SOC_BIAS_PREPARE:
 	case SND_SOC_BIAS_PREPARE:
+		/* VMID=2*50k */
+		val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+			~WM8990_VMID_MODE_MASK;
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x2);
 		break;
 		break;
+
 	case SND_SOC_BIAS_STANDBY:
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
 			/* Enable all output discharge bits */
 			/* Enable all output discharge bits */
@@ -1272,10 +1279,17 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
 
 
 			/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
 			/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
 			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
 			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
-		} else {
-			/* ON -> standby */
 
 
+			/* Enable workaround for ADC clocking issue. */
+			wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0x2);
+			wm8990_write(codec, WM8990_EXT_CTL1, 0xa003);
+			wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0);
 		}
 		}
+
+		/* VMID=2*250k */
+		val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+			~WM8990_VMID_MODE_MASK;
+		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x4);
 		break;
 		break;
 
 
 	case SND_SOC_BIAS_OFF:
 	case SND_SOC_BIAS_OFF:
@@ -1349,8 +1363,7 @@ struct snd_soc_dai wm8990_dai = {
 		.rates = WM8990_RATES,
 		.rates = WM8990_RATES,
 		.formats = WM8990_FORMATS,},
 		.formats = WM8990_FORMATS,},
 	.ops = {
 	.ops = {
-		.hw_params = wm8990_hw_params,},
-	.dai_ops = {
+		.hw_params = wm8990_hw_params,
 		.digital_mute = wm8990_mute,
 		.digital_mute = wm8990_mute,
 		.set_fmt = wm8990_set_dai_fmt,
 		.set_fmt = wm8990_set_dai_fmt,
 		.set_clkdiv = wm8990_set_dai_clkdiv,
 		.set_clkdiv = wm8990_set_dai_clkdiv,
@@ -1449,7 +1462,7 @@ static int wm8990_init(struct snd_soc_device *socdev)
 
 
 	wm8990_add_controls(codec);
 	wm8990_add_controls(codec);
 	wm8990_add_widgets(codec);
 	wm8990_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "wm8990: failed to register card\n");
 		printk(KERN_ERR "wm8990: failed to register card\n");
 		goto card_err;
 		goto card_err;
@@ -1630,6 +1643,18 @@ struct snd_soc_codec_device soc_codec_dev_wm8990 = {
 };
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8990);
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8990);
 
 
+static int __init wm8990_modinit(void)
+{
+	return snd_soc_register_dai(&wm8990_dai);
+}
+module_init(wm8990_modinit);
+
+static void __exit wm8990_exit(void)
+{
+	snd_soc_unregister_dai(&wm8990_dai);
+}
+module_exit(wm8990_exit);
+
 MODULE_DESCRIPTION("ASoC WM8990 driver");
 MODULE_DESCRIPTION("ASoC WM8990 driver");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 2 - 2
sound/soc/codecs/wm8990.h

@@ -80,8 +80,8 @@
 #define WM8990_PLL3                             0x3E
 #define WM8990_PLL3                             0x3E
 #define WM8990_INTDRIVBITS			0x3F
 #define WM8990_INTDRIVBITS			0x3F
 
 
-#define WM8990_REGISTER_COUNT                   60
-#define WM8990_MAX_REGISTER                     0x3F
+#define WM8990_EXT_ACCESS_ENA			0x75
+#define WM8990_EXT_CTL1				0x7a
 
 
 /*
 /*
  * Field Definitions.
  * Field Definitions.

+ 7 - 5
sound/soc/codecs/wm9712.c

@@ -487,7 +487,8 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 	return 0;
 	return 0;
 }
 }
 
 
-static int ac97_prepare(struct snd_pcm_substream *substream)
+static int ac97_prepare(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -507,7 +508,8 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
 	return ac97_write(codec, reg, runtime->rate);
 	return ac97_write(codec, reg, runtime->rate);
 }
 }
 
 
-static int ac97_aux_prepare(struct snd_pcm_substream *substream)
+static int ac97_aux_prepare(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -533,7 +535,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
 struct snd_soc_dai wm9712_dai[] = {
 struct snd_soc_dai wm9712_dai[] = {
 {
 {
 	.name = "AC97 HiFi",
 	.name = "AC97 HiFi",
-	.type = SND_SOC_DAI_AC97_BUS,
+	.ac97_control = 1,
 	.playback = {
 	.playback = {
 		.stream_name = "HiFi Playback",
 		.stream_name = "HiFi Playback",
 		.channels_min = 1,
 		.channels_min = 1,
@@ -688,7 +690,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
 
 
 	ret = wm9712_reset(codec, 0);
 	ret = wm9712_reset(codec, 0);
 	if (ret < 0) {
 	if (ret < 0) {
-		printk(KERN_ERR "AC97 link error\n");
+		printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
 		goto reset_err;
 		goto reset_err;
 	}
 	}
 
 
@@ -698,7 +700,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
 	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	wm9712_add_controls(codec);
 	wm9712_add_controls(codec);
 	wm9712_add_widgets(codec);
 	wm9712_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_ERR "wm9712: failed to register card\n");
 		printk(KERN_ERR "wm9712: failed to register card\n");
 		goto reset_err;
 		goto reset_err;

+ 20 - 26
sound/soc/codecs/wm9713.c

@@ -928,11 +928,10 @@ static int wm9713_set_dai_fmt(struct snd_soc_dai *codec_dai,
 }
 }
 
 
 static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
 static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *params)
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
 {
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_device *socdev = rtd->socdev;
-	struct snd_soc_codec *codec = socdev->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
 	u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
 
 
 	switch (params_format(params)) {
 	switch (params_format(params)) {
@@ -954,11 +953,10 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 	return 0;
 }
 }
 
 
-static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
+static void wm9713_voiceshutdown(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
 {
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_device *socdev = rtd->socdev;
-	struct snd_soc_codec *codec = socdev->codec;
+	struct snd_soc_codec *codec = dai->codec;
 	u16 status;
 	u16 status;
 
 
 	/* Gracefully shut down the voice interface. */
 	/* Gracefully shut down the voice interface. */
@@ -969,12 +967,11 @@ static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)
 	ac97_write(codec, AC97_EXTENDED_MID, status);
 	ac97_write(codec, AC97_EXTENDED_MID, status);
 }
 }
 
 
-static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
+static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
 {
 {
+	struct snd_soc_codec *codec = dai->codec;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_device *socdev = rtd->socdev;
-	struct snd_soc_codec *codec = socdev->codec;
 	int reg;
 	int reg;
 	u16 vra;
 	u16 vra;
 
 
@@ -989,12 +986,11 @@ static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
 	return ac97_write(codec, reg, runtime->rate);
 	return ac97_write(codec, reg, runtime->rate);
 }
 }
 
 
-static int ac97_aux_prepare(struct snd_pcm_substream *substream)
+static int ac97_aux_prepare(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
 {
 {
+	struct snd_soc_codec *codec = dai->codec;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_device *socdev = rtd->socdev;
-	struct snd_soc_codec *codec = socdev->codec;
 	u16 vra, xsle;
 	u16 vra, xsle;
 
 
 	vra = ac97_read(codec, AC97_EXTENDED_STATUS);
 	vra = ac97_read(codec, AC97_EXTENDED_STATUS);
@@ -1028,7 +1024,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
 struct snd_soc_dai wm9713_dai[] = {
 struct snd_soc_dai wm9713_dai[] = {
 {
 {
 	.name = "AC97 HiFi",
 	.name = "AC97 HiFi",
-	.type = SND_SOC_DAI_AC97_BUS,
+	.ac97_control = 1,
 	.playback = {
 	.playback = {
 		.stream_name = "HiFi Playback",
 		.stream_name = "HiFi Playback",
 		.channels_min = 1,
 		.channels_min = 1,
@@ -1042,8 +1038,7 @@ struct snd_soc_dai wm9713_dai[] = {
 		.rates = WM9713_RATES,
 		.rates = WM9713_RATES,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
 	.ops = {
 	.ops = {
-		.prepare = ac97_hifi_prepare,},
-	.dai_ops = {
+		.prepare = ac97_hifi_prepare,
 		.set_clkdiv = wm9713_set_dai_clkdiv,
 		.set_clkdiv = wm9713_set_dai_clkdiv,
 		.set_pll = wm9713_set_dai_pll,},
 		.set_pll = wm9713_set_dai_pll,},
 	},
 	},
@@ -1056,8 +1051,7 @@ struct snd_soc_dai wm9713_dai[] = {
 		.rates = WM9713_RATES,
 		.rates = WM9713_RATES,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
 	.ops = {
 	.ops = {
-		.prepare = ac97_aux_prepare,},
-	.dai_ops = {
+		.prepare = ac97_aux_prepare,
 		.set_clkdiv = wm9713_set_dai_clkdiv,
 		.set_clkdiv = wm9713_set_dai_clkdiv,
 		.set_pll = wm9713_set_dai_pll,},
 		.set_pll = wm9713_set_dai_pll,},
 	},
 	},
@@ -1077,8 +1071,7 @@ struct snd_soc_dai wm9713_dai[] = {
 		.formats = WM9713_PCM_FORMATS,},
 		.formats = WM9713_PCM_FORMATS,},
 	.ops = {
 	.ops = {
 		.hw_params = wm9713_pcm_hw_params,
 		.hw_params = wm9713_pcm_hw_params,
-		.shutdown = wm9713_voiceshutdown,},
-	.dai_ops = {
+		.shutdown = wm9713_voiceshutdown,
 		.set_clkdiv = wm9713_set_dai_clkdiv,
 		.set_clkdiv = wm9713_set_dai_clkdiv,
 		.set_pll = wm9713_set_dai_pll,
 		.set_pll = wm9713_set_dai_pll,
 		.set_fmt = wm9713_set_dai_fmt,
 		.set_fmt = wm9713_set_dai_fmt,
@@ -1097,6 +1090,8 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
 	}
 	}
 
 
 	soc_ac97_ops.reset(codec->ac97);
 	soc_ac97_ops.reset(codec->ac97);
+	if (soc_ac97_ops.warm_reset)
+		soc_ac97_ops.warm_reset(codec->ac97);
 	if (ac97_read(codec, 0) != wm9713_reg[0])
 	if (ac97_read(codec, 0) != wm9713_reg[0])
 		return -EIO;
 		return -EIO;
 	return 0;
 	return 0;
@@ -1240,7 +1235,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
 	wm9713_reset(codec, 0);
 	wm9713_reset(codec, 0);
 	ret = wm9713_reset(codec, 1);
 	ret = wm9713_reset(codec, 1);
 	if (ret < 0) {
 	if (ret < 0) {
-		printk(KERN_ERR "AC97 link error\n");
+		printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
 		goto reset_err;
 		goto reset_err;
 	}
 	}
 
 
@@ -1252,7 +1247,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
 
 
 	wm9713_add_controls(codec);
 	wm9713_add_controls(codec);
 	wm9713_add_widgets(codec);
 	wm9713_add_widgets(codec);
-	ret = snd_soc_register_card(socdev);
+	ret = snd_soc_init_card(socdev);
 	if (ret < 0)
 	if (ret < 0)
 		goto reset_err;
 		goto reset_err;
 	return 0;
 	return 0;
@@ -1288,7 +1283,6 @@ static int wm9713_soc_remove(struct platform_device *pdev)
 	snd_soc_free_ac97_codec(codec);
 	snd_soc_free_ac97_codec(codec);
 	kfree(codec->private_data);
 	kfree(codec->private_data);
 	kfree(codec->reg_cache);
 	kfree(codec->reg_cache);
-	kfree(codec->dai);
 	kfree(codec);
 	kfree(codec);
 	return 0;
 	return 0;
 }
 }

+ 10 - 0
sound/soc/davinci/Kconfig

@@ -17,3 +17,13 @@ config SND_DAVINCI_SOC_EVM
 	help
 	help
 	  Say Y if you want to add support for SoC audio on TI
 	  Say Y if you want to add support for SoC audio on TI
 	  DaVinci EVM platform.
 	  DaVinci EVM platform.
+
+config SND_DAVINCI_SOC_SFFSDR
+	tristate "SoC Audio support for SFFSDR"
+	depends on SND_DAVINCI_SOC && MACH_DAVINCI_SFFSDR
+	select SND_DAVINCI_SOC_I2S
+	select SND_SOC_PCM3008
+	select SFFSDR_FPGA
+	help
+	  Say Y if you want to add support for SoC audio on
+	  Lyrtech SFFSDR board.

+ 2 - 0
sound/soc/davinci/Makefile

@@ -7,5 +7,7 @@ obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
 
 
 # DAVINCI Machine Support
 # DAVINCI Machine Support
 snd-soc-evm-objs := davinci-evm.o
 snd-soc-evm-objs := davinci-evm.o
+snd-soc-sffsdr-objs := davinci-sffsdr.o
 
 
 obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
 obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o

+ 7 - 7
sound/soc/davinci/davinci-evm.c

@@ -28,6 +28,8 @@
 
 
 #define EVM_CODEC_CLOCK 22579200
 #define EVM_CODEC_CLOCK 22579200
 
 
+#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
+		SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
 static int evm_hw_params(struct snd_pcm_substream *substream,
 static int evm_hw_params(struct snd_pcm_substream *substream,
 			 struct snd_pcm_hw_params *params)
 			 struct snd_pcm_hw_params *params)
 {
 {
@@ -37,14 +39,12 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
 	int ret = 0;
 	int ret = 0;
 
 
 	/* set codec DAI configuration */
 	/* set codec DAI configuration */
-	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-					 SND_SOC_DAIFMT_CBM_CFM);
+	ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
 	/* set cpu DAI configuration */
 	/* set cpu DAI configuration */
-	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
-				       SND_SOC_DAIFMT_IB_NF);
+	ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -128,8 +128,9 @@ static struct snd_soc_dai_link evm_dai = {
 };
 };
 
 
 /* davinci-evm audio machine driver */
 /* davinci-evm audio machine driver */
-static struct snd_soc_machine snd_soc_machine_evm = {
+static struct snd_soc_card snd_soc_card_evm = {
 	.name = "DaVinci EVM",
 	.name = "DaVinci EVM",
+	.platform = &davinci_soc_platform,
 	.dai_link = &evm_dai,
 	.dai_link = &evm_dai,
 	.num_links = 1,
 	.num_links = 1,
 };
 };
@@ -142,8 +143,7 @@ static struct aic3x_setup_data evm_aic3x_setup = {
 
 
 /* evm audio subsystem */
 /* evm audio subsystem */
 static struct snd_soc_device evm_snd_devdata = {
 static struct snd_soc_device evm_snd_devdata = {
-	.machine = &snd_soc_machine_evm,
-	.platform = &davinci_soc_platform,
+	.card = &snd_soc_card_evm,
 	.codec_dev = &soc_codec_dev_aic3x,
 	.codec_dev = &soc_codec_dev_aic3x,
 	.codec_data = &evm_aic3x_setup,
 	.codec_data = &evm_aic3x_setup,
 };
 };

+ 194 - 63
sound/soc/davinci/davinci-i2s.c

@@ -59,6 +59,7 @@
 #define DAVINCI_MCBSP_PCR_CLKXP		(1 << 1)
 #define DAVINCI_MCBSP_PCR_CLKXP		(1 << 1)
 #define DAVINCI_MCBSP_PCR_FSRP		(1 << 2)
 #define DAVINCI_MCBSP_PCR_FSRP		(1 << 2)
 #define DAVINCI_MCBSP_PCR_FSXP		(1 << 3)
 #define DAVINCI_MCBSP_PCR_FSXP		(1 << 3)
+#define DAVINCI_MCBSP_PCR_SCLKME	(1 << 7)
 #define DAVINCI_MCBSP_PCR_CLKRM		(1 << 8)
 #define DAVINCI_MCBSP_PCR_CLKRM		(1 << 8)
 #define DAVINCI_MCBSP_PCR_CLKXM		(1 << 9)
 #define DAVINCI_MCBSP_PCR_CLKXM		(1 << 9)
 #define DAVINCI_MCBSP_PCR_FSRM		(1 << 10)
 #define DAVINCI_MCBSP_PCR_FSRM		(1 << 10)
@@ -110,16 +111,59 @@ static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
 	struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_platform *platform = socdev->card->platform;
 	u32 w;
 	u32 w;
+	int ret;
 
 
 	/* Start the sample generator and enable transmitter/receiver */
 	/* Start the sample generator and enable transmitter/receiver */
 	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
 	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
 	MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
 	MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* Stop the DMA to avoid data loss */
+		/* while the transmitter is out of reset to handle XSYNCERR */
+		if (platform->pcm_ops->trigger) {
+			ret = platform->pcm_ops->trigger(substream,
+				SNDRV_PCM_TRIGGER_STOP);
+			if (ret < 0)
+				printk(KERN_DEBUG "Playback DMA stop failed\n");
+		}
+
+		/* Enable the transmitter */
+		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
 		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
 		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
-	else
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+		/* wait for any unexpected frame sync error to occur */
+		udelay(100);
+
+		/* Disable the transmitter to clear any outstanding XSYNCERR */
+		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+		/* Restart the DMA */
+		if (platform->pcm_ops->trigger) {
+			ret = platform->pcm_ops->trigger(substream,
+				SNDRV_PCM_TRIGGER_START);
+			if (ret < 0)
+				printk(KERN_DEBUG "Playback DMA start failed\n");
+		}
+		/* Enable the transmitter */
+		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+
+	} else {
+
+		/* Enable the reciever */
+		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
 		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
 		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+	}
+
 
 
 	/* Start frame sync */
 	/* Start frame sync */
 	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
 	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
@@ -144,7 +188,8 @@ static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
 	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
 	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
 }
 }
 
 
-static int davinci_i2s_startup(struct snd_pcm_substream *substream)
+static int davinci_i2s_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -155,61 +200,138 @@ static int davinci_i2s_startup(struct snd_pcm_substream *substream)
 	return 0;
 	return 0;
 }
 }
 
 
+#define DEFAULT_BITPERSAMPLE	16
+
 static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 				   unsigned int fmt)
 				   unsigned int fmt)
 {
 {
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
-	u32 w;
+	unsigned int pcr;
+	unsigned int srgr;
+	unsigned int rcr;
+	unsigned int xcr;
+	srgr = DAVINCI_MCBSP_SRGR_FSGM |
+		DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
+		DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
 
 
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 	case SND_SOC_DAIFMT_CBS_CFS:
 	case SND_SOC_DAIFMT_CBS_CFS:
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG,
-					DAVINCI_MCBSP_PCR_FSXM |
-					DAVINCI_MCBSP_PCR_FSRM |
-					DAVINCI_MCBSP_PCR_CLKXM |
-					DAVINCI_MCBSP_PCR_CLKRM);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG,
-					DAVINCI_MCBSP_SRGR_FSGM);
+		/* cpu is master */
+		pcr = DAVINCI_MCBSP_PCR_FSXM |
+			DAVINCI_MCBSP_PCR_FSRM |
+			DAVINCI_MCBSP_PCR_CLKXM |
+			DAVINCI_MCBSP_PCR_CLKRM;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		/* McBSP CLKR pin is the input for the Sample Rate Generator.
+		 * McBSP FSR and FSX are driven by the Sample Rate Generator. */
+		pcr = DAVINCI_MCBSP_PCR_SCLKME |
+			DAVINCI_MCBSP_PCR_FSXM |
+			DAVINCI_MCBSP_PCR_FSRM;
 		break;
 		break;
 	case SND_SOC_DAIFMT_CBM_CFM:
 	case SND_SOC_DAIFMT_CBM_CFM:
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, 0);
+		/* codec is master */
+		pcr = 0;
 		break;
 		break;
 	default:
 	default:
+		printk(KERN_ERR "%s:bad master\n", __func__);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-	case SND_SOC_DAIFMT_IB_NF:
-		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
-		MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
-			       DAVINCI_MCBSP_PCR_CLKRP, 1);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+	rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1);
+	xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1);
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_B:
 		break;
 		break;
-	case SND_SOC_DAIFMT_NB_IF:
-		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
-		MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_FSXP |
-			       DAVINCI_MCBSP_PCR_FSRP, 1);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+	case SND_SOC_DAIFMT_I2S:
+		/* Davinci doesn't support TRUE I2S, but some codecs will have
+		 * the left and right channels contiguous. This allows
+		 * dsp_a mode to be used with an inverted normal frame clk.
+		 * If your codec is master and does not have contiguous
+		 * channels, then you will have sound on only one channel.
+		 * Try using a different mode, or codec as slave.
+		 *
+		 * The TLV320AIC33 is an example of a codec where this works.
+		 * It has a variable bit clock frequency allowing it to have
+		 * valid data on every bit clock.
+		 *
+		 * The TLV320AIC23 is an example of a codec where this does not
+		 * work. It has a fixed bit clock frequency with progressively
+		 * more empty bit clock slots between channels as the sample
+		 * rate is lowered.
+		 */
+		fmt ^= SND_SOC_DAIFMT_NB_IF;
+	case SND_SOC_DAIFMT_DSP_A:
+		rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
+		xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+		break;
+	default:
+		printk(KERN_ERR "%s:bad format\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		/* CLKRP Receive clock polarity,
+		 *	1 - sampled on rising edge of CLKR
+		 *	valid on rising edge
+		 * CLKXP Transmit clock polarity,
+		 *	1 - clocked on falling edge of CLKX
+		 *	valid on rising edge
+		 * FSRP  Receive frame sync pol, 0 - active high
+		 * FSXP  Transmit frame sync pol, 0 - active high
+		 */
+		pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP);
 		break;
 		break;
 	case SND_SOC_DAIFMT_IB_IF:
 	case SND_SOC_DAIFMT_IB_IF:
-		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_PCR_REG);
-		MOD_REG_BIT(w, DAVINCI_MCBSP_PCR_CLKXP |
-			       DAVINCI_MCBSP_PCR_CLKRP |
-			       DAVINCI_MCBSP_PCR_FSXP |
-			       DAVINCI_MCBSP_PCR_FSRP, 1);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, w);
+		/* CLKRP Receive clock polarity,
+		 *	0 - sampled on falling edge of CLKR
+		 *	valid on falling edge
+		 * CLKXP Transmit clock polarity,
+		 *	0 - clocked on rising edge of CLKX
+		 *	valid on falling edge
+		 * FSRP  Receive frame sync pol, 1 - active low
+		 * FSXP  Transmit frame sync pol, 1 - active low
+		 */
+		pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
 		break;
 		break;
-	case SND_SOC_DAIFMT_NB_NF:
+	case SND_SOC_DAIFMT_NB_IF:
+		/* CLKRP Receive clock polarity,
+		 *	1 - sampled on rising edge of CLKR
+		 *	valid on rising edge
+		 * CLKXP Transmit clock polarity,
+		 *	1 - clocked on falling edge of CLKX
+		 *	valid on rising edge
+		 * FSRP  Receive frame sync pol, 1 - active low
+		 * FSXP  Transmit frame sync pol, 1 - active low
+		 */
+		pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
+			DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		/* CLKRP Receive clock polarity,
+		 *	0 - sampled on falling edge of CLKR
+		 *	valid on falling edge
+		 * CLKXP Transmit clock polarity,
+		 *	0 - clocked on rising edge of CLKX
+		 *	valid on falling edge
+		 * FSRP  Receive frame sync pol, 0 - active high
+		 * FSXP  Transmit frame sync pol, 0 - active high
+		 */
 		break;
 		break;
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
-
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
 	return 0;
 	return 0;
 }
 }
 
 
 static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
 static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
-				 struct snd_pcm_hw_params *params)
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
 	struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
@@ -219,25 +341,20 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
 	u32 w;
 	u32 w;
 
 
 	/* general line settings */
 	/* general line settings */
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
-				DAVINCI_MCBSP_SPCR_RINTM(3) |
-				DAVINCI_MCBSP_SPCR_XINTM(3) |
-				DAVINCI_MCBSP_SPCR_FREE);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG,
-				DAVINCI_MCBSP_RCR_RFRLEN1(1) |
-				DAVINCI_MCBSP_RCR_RDATDLY(1));
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG,
-				DAVINCI_MCBSP_XCR_XFRLEN1(1) |
-				DAVINCI_MCBSP_XCR_XDATDLY(1) |
-				DAVINCI_MCBSP_XCR_XFIG);
+	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+	} else {
+		w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+	}
 
 
 	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
 	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
-	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
+	w = DAVINCI_MCBSP_SRGR_FSGM;
 	MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
 	MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
 
 
 	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
 	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
-	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
 	MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
 	MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
 	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
 	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
 
 
@@ -260,20 +377,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
-	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
-	MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
-		       DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
+		MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
+			       DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
 
 
-	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
-	MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
-		       DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
+	} else {
+		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
+		MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
+			       DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
 
 
+	}
 	return 0;
 	return 0;
 }
 }
 
 
-static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			       struct snd_soc_dai *dai)
 {
 {
 	int ret = 0;
 	int ret = 0;
 
 
@@ -299,8 +420,8 @@ static int davinci_i2s_probe(struct platform_device *pdev,
 			     struct snd_soc_dai *dai)
 			     struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_machine *machine = socdev->machine;
-	struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+	struct snd_soc_card *card = socdev->card;
+	struct snd_soc_dai *cpu_dai = card->dai_link[pdev->id].cpu_dai;
 	struct davinci_mcbsp_dev *dev;
 	struct davinci_mcbsp_dev *dev;
 	struct resource *mem, *ioarea;
 	struct resource *mem, *ioarea;
 	struct evm_snd_platform_data *pdata;
 	struct evm_snd_platform_data *pdata;
@@ -361,8 +482,8 @@ static void davinci_i2s_remove(struct platform_device *pdev,
 			       struct snd_soc_dai *dai)
 			       struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_machine *machine = socdev->machine;
-	struct snd_soc_dai *cpu_dai = machine->dai_link[pdev->id].cpu_dai;
+	struct snd_soc_card *card = socdev->card;
+	struct snd_soc_dai *cpu_dai = card->dai_link[pdev->id].cpu_dai;
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
 	struct resource *mem;
 	struct resource *mem;
 
 
@@ -381,7 +502,6 @@ static void davinci_i2s_remove(struct platform_device *pdev,
 struct snd_soc_dai davinci_i2s_dai = {
 struct snd_soc_dai davinci_i2s_dai = {
 	.name = "davinci-i2s",
 	.name = "davinci-i2s",
 	.id = 0,
 	.id = 0,
-	.type = SND_SOC_DAI_I2S,
 	.probe = davinci_i2s_probe,
 	.probe = davinci_i2s_probe,
 	.remove = davinci_i2s_remove,
 	.remove = davinci_i2s_remove,
 	.playback = {
 	.playback = {
@@ -397,13 +517,24 @@ struct snd_soc_dai davinci_i2s_dai = {
 	.ops = {
 	.ops = {
 		.startup = davinci_i2s_startup,
 		.startup = davinci_i2s_startup,
 		.trigger = davinci_i2s_trigger,
 		.trigger = davinci_i2s_trigger,
-		.hw_params = davinci_i2s_hw_params,},
-	.dai_ops = {
+		.hw_params = davinci_i2s_hw_params,
 		.set_fmt = davinci_i2s_set_dai_fmt,
 		.set_fmt = davinci_i2s_set_dai_fmt,
 	},
 	},
 };
 };
 EXPORT_SYMBOL_GPL(davinci_i2s_dai);
 EXPORT_SYMBOL_GPL(davinci_i2s_dai);
 
 
+static int __init davinci_i2s_init(void)
+{
+	return snd_soc_register_dai(&davinci_i2s_dai);
+}
+module_init(davinci_i2s_init);
+
+static void __exit davinci_i2s_exit(void)
+{
+	snd_soc_unregister_dai(&davinci_i2s_dai);
+}
+module_exit(davinci_i2s_exit);
+
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
 MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 18 - 12
sound/soc/davinci/davinci-pcm.c

@@ -14,6 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
+#include <linux/kernel.h>
 
 
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm.h>
@@ -24,13 +25,6 @@
 
 
 #include "davinci-pcm.h"
 #include "davinci-pcm.h"
 
 
-#define DAVINCI_PCM_DEBUG 0
-#if DAVINCI_PCM_DEBUG
-#define DPRINTK(x...) printk(KERN_DEBUG x)
-#else
-#define DPRINTK(x...)
-#endif
-
 static struct snd_pcm_hardware davinci_pcm_hardware = {
 static struct snd_pcm_hardware davinci_pcm_hardware = {
 	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
 	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
 		 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
 		 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
@@ -78,8 +72,8 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
 	dma_offset = prtd->period * period_size;
 	dma_offset = prtd->period * period_size;
 	dma_pos = runtime->dma_addr + dma_offset;
 	dma_pos = runtime->dma_addr + dma_offset;
 
 
-	DPRINTK("audio_set_dma_params_play channel = %d dma_ptr = %x "
-		"period_size=%x\n", lch, dma_pos, period_size);
+	pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
+		"dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
 
 
 	data_type = prtd->params->data_type;
 	data_type = prtd->params->data_type;
 	count = period_size / data_type;
 	count = period_size / data_type;
@@ -112,7 +106,7 @@ static void davinci_pcm_dma_irq(int lch, u16 ch_status, void *data)
 	struct snd_pcm_substream *substream = data;
 	struct snd_pcm_substream *substream = data;
 	struct davinci_runtime_data *prtd = substream->runtime->private_data;
 	struct davinci_runtime_data *prtd = substream->runtime->private_data;
 
 
-	DPRINTK("lch=%d, status=0x%x\n", lch, ch_status);
+	pr_debug("davinci_pcm: lch=%d, status=0x%x\n", lch, ch_status);
 
 
 	if (unlikely(ch_status != DMA_COMPLETE))
 	if (unlikely(ch_status != DMA_COMPLETE))
 		return;
 		return;
@@ -316,8 +310,8 @@ static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
 	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
 					   &buf->addr, GFP_KERNEL);
 					   &buf->addr, GFP_KERNEL);
 
 
-	DPRINTK("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
-		(void *) buf->area, (void *) buf->addr, size);
+	pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, "
+		"size=%d\n", (void *) buf->area, (void *) buf->addr, size);
 
 
 	if (!buf->area)
 	if (!buf->area)
 		return -ENOMEM;
 		return -ENOMEM;
@@ -384,6 +378,18 @@ struct snd_soc_platform davinci_soc_platform = {
 };
 };
 EXPORT_SYMBOL_GPL(davinci_soc_platform);
 EXPORT_SYMBOL_GPL(davinci_soc_platform);
 
 
+static int __init davinci_soc_platform_init(void)
+{
+	return snd_soc_register_platform(&davinci_soc_platform);
+}
+module_init(davinci_soc_platform_init);
+
+static void __exit davinci_soc_platform_exit(void)
+{
+	snd_soc_unregister_platform(&davinci_soc_platform);
+}
+module_exit(davinci_soc_platform_exit);
+
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_AUTHOR("Vladimir Barinov");
 MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
 MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 157 - 0
sound/soc/davinci/davinci-sffsdr.c

@@ -0,0 +1,157 @@
+/*
+ * ASoC driver for Lyrtech SFFSDR board.
+ *
+ * Author:	Hugo Villeneuve
+ * Copyright (C) 2008 Lyrtech inc
+ *
+ * Based on ASoC driver for TI DAVINCI EVM platform, original copyright follow:
+ * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/dma.h>
+#include <asm/plat-sffsdr/sffsdr-fpga.h>
+
+#include <mach/mcbsp.h>
+#include <mach/edma.h>
+
+#include "../codecs/pcm3008.h"
+#include "davinci-pcm.h"
+#include "davinci-i2s.h"
+
+static int sffsdr_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;
+	int fs;
+	int ret = 0;
+
+	/* Set cpu DAI configuration:
+	 * CLKX and CLKR are the inputs for the Sample Rate Generator.
+	 * FSX and FSR are outputs, driven by the sample Rate Generator. */
+	ret = snd_soc_dai_set_fmt(cpu_dai,
+				  SND_SOC_DAIFMT_RIGHT_J |
+				  SND_SOC_DAIFMT_CBM_CFS |
+				  SND_SOC_DAIFMT_IB_NF);
+	if (ret < 0)
+		return ret;
+
+	/* Fsref can be 32000, 44100 or 48000. */
+	fs = params_rate(params);
+
+	pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs);
+
+	return sffsdr_fpga_set_codec_fs(fs);
+}
+
+static struct snd_soc_ops sffsdr_ops = {
+	.hw_params = sffsdr_hw_params,
+};
+
+/* davinci-sffsdr digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sffsdr_dai = {
+	.name = "PCM3008", /* Codec name */
+	.stream_name = "PCM3008 HiFi",
+	.cpu_dai = &davinci_i2s_dai,
+	.codec_dai = &pcm3008_dai,
+	.ops = &sffsdr_ops,
+};
+
+/* davinci-sffsdr audio machine driver */
+static struct snd_soc_card snd_soc_sffsdr = {
+	.name = "DaVinci SFFSDR",
+	.platform = &davinci_soc_platform,
+	.dai_link = &sffsdr_dai,
+	.num_links = 1,
+};
+
+/* sffsdr audio private data */
+static struct pcm3008_setup_data sffsdr_pcm3008_setup = {
+	.dem0_pin = GPIO(45),
+	.dem1_pin = GPIO(46),
+	.pdad_pin = GPIO(47),
+	.pdda_pin = GPIO(38),
+};
+
+/* sffsdr audio subsystem */
+static struct snd_soc_device sffsdr_snd_devdata = {
+	.card = &snd_soc_sffsdr,
+	.codec_dev = &soc_codec_dev_pcm3008,
+	.codec_data = &sffsdr_pcm3008_setup,
+};
+
+static struct resource sffsdr_snd_resources[] = {
+	{
+		.start = DAVINCI_MCBSP_BASE,
+		.end = DAVINCI_MCBSP_BASE + SZ_8K - 1,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+static struct evm_snd_platform_data sffsdr_snd_data = {
+	.tx_dma_ch	= DAVINCI_DMA_MCBSP_TX,
+	.rx_dma_ch	= DAVINCI_DMA_MCBSP_RX,
+};
+
+static struct platform_device *sffsdr_snd_device;
+
+static int __init sffsdr_init(void)
+{
+	int ret;
+
+	sffsdr_snd_device = platform_device_alloc("soc-audio", 0);
+	if (!sffsdr_snd_device) {
+		printk(KERN_ERR "platform device allocation failed\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(sffsdr_snd_device, &sffsdr_snd_devdata);
+	sffsdr_snd_devdata.dev = &sffsdr_snd_device->dev;
+	sffsdr_snd_device->dev.platform_data = &sffsdr_snd_data;
+
+	ret = platform_device_add_resources(sffsdr_snd_device,
+					    sffsdr_snd_resources,
+					    ARRAY_SIZE(sffsdr_snd_resources));
+	if (ret) {
+		printk(KERN_ERR "platform device add ressources failed\n");
+		goto error;
+	}
+
+	ret = platform_device_add(sffsdr_snd_device);
+	if (ret)
+		goto error;
+
+	return ret;
+
+error:
+	platform_device_put(sffsdr_snd_device);
+	return ret;
+}
+
+static void __exit sffsdr_exit(void)
+{
+	platform_device_unregister(sffsdr_snd_device);
+}
+
+module_init(sffsdr_init);
+module_exit(sffsdr_exit);
+
+MODULE_AUTHOR("Hugo Villeneuve");
+MODULE_DESCRIPTION("Lyrtech SFFSDR ASoC driver");
+MODULE_LICENSE("GPL");

+ 1 - 1
sound/soc/fsl/Kconfig

@@ -20,7 +20,7 @@ config SND_SOC_MPC8610_HPCD
 
 
 config SND_SOC_MPC5200_I2S
 config SND_SOC_MPC5200_I2S
 	tristate "Freescale MPC5200 PSC in I2S mode driver"
 	tristate "Freescale MPC5200 PSC in I2S mode driver"
-	depends on SND_SOC && PPC_MPC52xx && PPC_BESTCOMM
+	depends on PPC_MPC52xx && PPC_BESTCOMM
 	select SND_SOC_OF_SIMPLE
 	select SND_SOC_OF_SIMPLE
 	select PPC_BESTCOMM_GEN_BD
 	select PPC_BESTCOMM_GEN_BD
 	help
 	help

+ 13 - 1
sound/soc/fsl/fsl_dma.c

@@ -284,7 +284,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
  * fsl_dma_new: initialize this PCM driver.
  * fsl_dma_new: initialize this PCM driver.
  *
  *
  * This function is called when the codec driver calls snd_soc_new_pcms(),
  * This function is called when the codec driver calls snd_soc_new_pcms(),
- * once for each .dai_link in the machine driver's snd_soc_machine
+ * once for each .dai_link in the machine driver's snd_soc_card
  * structure.
  * structure.
  */
  */
 static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
 static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
@@ -853,6 +853,18 @@ int fsl_dma_configure(struct fsl_dma_info *dma_info)
 }
 }
 EXPORT_SYMBOL_GPL(fsl_dma_configure);
 EXPORT_SYMBOL_GPL(fsl_dma_configure);
 
 
+static int __init fsl_soc_platform_init(void)
+{
+	return snd_soc_register_platform(&fsl_soc_platform);
+}
+module_init(fsl_soc_platform_init);
+
+static void __exit fsl_soc_platform_exit(void)
+{
+	snd_soc_unregister_platform(&fsl_soc_platform);
+}
+module_exit(fsl_soc_platform_exit);
+
 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
 MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module");
 MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 18 - 6
sound/soc/fsl/fsl_ssi.c

@@ -266,7 +266,8 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
  * If this is the first stream open, then grab the IRQ and program most of
  * If this is the first stream open, then grab the IRQ and program most of
  * the SSI registers.
  * the SSI registers.
  */
  */
-static int fsl_ssi_startup(struct snd_pcm_substream *substream)
+static int fsl_ssi_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
 	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
@@ -411,7 +412,8 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream)
  * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
  * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
  * clock master.
  * clock master.
  */
  */
-static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
+static int fsl_ssi_prepare(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -441,7 +443,8 @@ static int fsl_ssi_prepare(struct snd_pcm_substream *substream)
  * The DMA channel is in external master start and pause mode, which
  * The DMA channel is in external master start and pause mode, which
  * means the SSI completely controls the flow of data.
  * means the SSI completely controls the flow of data.
  */
  */
-static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
+static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+			   struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
 	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
@@ -490,7 +493,8 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
  *
  *
  * Shutdown the SSI if there are no other substreams open.
  * Shutdown the SSI if there are no other substreams open.
  */
  */
-static void fsl_ssi_shutdown(struct snd_pcm_substream *substream)
+static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
 	struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
@@ -578,8 +582,6 @@ static struct snd_soc_dai fsl_ssi_dai_template = {
 		.prepare = fsl_ssi_prepare,
 		.prepare = fsl_ssi_prepare,
 		.shutdown = fsl_ssi_shutdown,
 		.shutdown = fsl_ssi_shutdown,
 		.trigger = fsl_ssi_trigger,
 		.trigger = fsl_ssi_trigger,
-	},
-	.dai_ops = {
 		.set_sysclk = fsl_ssi_set_sysclk,
 		.set_sysclk = fsl_ssi_set_sysclk,
 		.set_fmt = fsl_ssi_set_fmt,
 		.set_fmt = fsl_ssi_set_fmt,
 	},
 	},
@@ -671,6 +673,14 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
 	fsl_ssi_dai->private_data = ssi_private;
 	fsl_ssi_dai->private_data = ssi_private;
 	fsl_ssi_dai->name = ssi_private->name;
 	fsl_ssi_dai->name = ssi_private->name;
 	fsl_ssi_dai->id = ssi_info->id;
 	fsl_ssi_dai->id = ssi_info->id;
+	fsl_ssi_dai->dev = ssi_info->dev;
+
+	ret = snd_soc_register_dai(fsl_ssi_dai);
+	if (ret != 0) {
+		dev_err(ssi_info->dev, "failed to register DAI: %d\n", ret);
+		kfree(fsl_ssi_dai);
+		return NULL;
+	}
 
 
 	return fsl_ssi_dai;
 	return fsl_ssi_dai;
 }
 }
@@ -688,6 +698,8 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
 
 
 	device_remove_file(ssi_private->dev, &ssi_private->dev_attr);
 	device_remove_file(ssi_private->dev, &ssi_private->dev_attr);
 
 
+	snd_soc_unregister_dai(&ssi_private->cpu_dai);
+
 	kfree(ssi_private);
 	kfree(ssi_private);
 }
 }
 EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
 EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);

+ 14 - 8
sound/soc/fsl/mpc5200_psc_i2s.c

@@ -187,7 +187,8 @@ static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
  * If this is the first stream open, then grab the IRQ and program most of
  * If this is the first stream open, then grab the IRQ and program most of
  * the PSC registers.
  * the PSC registers.
  */
  */
-static int psc_i2s_startup(struct snd_pcm_substream *substream)
+static int psc_i2s_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
 	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -220,7 +221,8 @@ static int psc_i2s_startup(struct snd_pcm_substream *substream)
 }
 }
 
 
 static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
 static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
-				 struct snd_pcm_hw_params *params)
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
 	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -256,7 +258,8 @@ static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 	return 0;
 }
 }
 
 
-static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
+static int psc_i2s_hw_free(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
 {
 {
 	snd_pcm_set_runtime_buffer(substream, NULL);
 	snd_pcm_set_runtime_buffer(substream, NULL);
 	return 0;
 	return 0;
@@ -268,7 +271,8 @@ static int psc_i2s_hw_free(struct snd_pcm_substream *substream)
  * This function is called by ALSA to start, stop, pause, and resume the DMA
  * This function is called by ALSA to start, stop, pause, and resume the DMA
  * transfer of data.
  * transfer of data.
  */
  */
-static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			   struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
 	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -383,7 +387,8 @@ static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
  *
  *
  * Shutdown the PSC if there are no other substreams open.
  * Shutdown the PSC if there are no other substreams open.
  */
  */
-static void psc_i2s_shutdown(struct snd_pcm_substream *substream)
+static void psc_i2s_shutdown(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
 	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
@@ -464,7 +469,6 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
  * psc_i2s_dai_template: template CPU Digital Audio Interface
  * psc_i2s_dai_template: template CPU Digital Audio Interface
  */
  */
 static struct snd_soc_dai psc_i2s_dai_template = {
 static struct snd_soc_dai psc_i2s_dai_template = {
-	.type = SND_SOC_DAI_I2S,
 	.playback = {
 	.playback = {
 		.channels_min = 2,
 		.channels_min = 2,
 		.channels_max = 2,
 		.channels_max = 2,
@@ -483,8 +487,6 @@ static struct snd_soc_dai psc_i2s_dai_template = {
 		.hw_free = psc_i2s_hw_free,
 		.hw_free = psc_i2s_hw_free,
 		.shutdown = psc_i2s_shutdown,
 		.shutdown = psc_i2s_shutdown,
 		.trigger = psc_i2s_trigger,
 		.trigger = psc_i2s_trigger,
-	},
-	.dai_ops = {
 		.set_sysclk = psc_i2s_set_sysclk,
 		.set_sysclk = psc_i2s_set_sysclk,
 		.set_fmt = psc_i2s_set_fmt,
 		.set_fmt = psc_i2s_set_fmt,
 	},
 	},
@@ -826,6 +828,8 @@ static int __devinit psc_i2s_of_probe(struct of_device *op,
 	if (rc)
 	if (rc)
 		dev_info(psc_i2s->dev, "error creating sysfs files\n");
 		dev_info(psc_i2s->dev, "error creating sysfs files\n");
 
 
+	snd_soc_register_platform(&psc_i2s_pcm_soc_platform);
+
 	/* Tell the ASoC OF helpers about it */
 	/* Tell the ASoC OF helpers about it */
 	of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
 	of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
 				     &psc_i2s->dai);
 				     &psc_i2s->dai);
@@ -839,6 +843,8 @@ static int __devexit psc_i2s_of_remove(struct of_device *op)
 
 
 	dev_dbg(&op->dev, "psc_i2s_remove()\n");
 	dev_dbg(&op->dev, "psc_i2s_remove()\n");
 
 
+	snd_soc_unregister_platform(&psc_i2s_pcm_soc_platform);
+
 	bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task);
 	bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task);
 	bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task);
 	bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task);
 
 

+ 4 - 4
sound/soc/fsl/mpc8610_hpcd.c

@@ -29,7 +29,7 @@
 struct mpc8610_hpcd_data {
 struct mpc8610_hpcd_data {
 	struct snd_soc_device sound_devdata;
 	struct snd_soc_device sound_devdata;
 	struct snd_soc_dai_link dai;
 	struct snd_soc_dai_link dai;
-	struct snd_soc_machine machine;
+	struct snd_soc_card machine;
 	unsigned int dai_format;
 	unsigned int dai_format;
 	unsigned int codec_clk_direction;
 	unsigned int codec_clk_direction;
 	unsigned int cpu_clk_direction;
 	unsigned int cpu_clk_direction;
@@ -185,7 +185,7 @@ static struct snd_soc_ops mpc8610_hpcd_ops = {
 /**
 /**
  * mpc8610_hpcd_machine: ASoC machine data
  * mpc8610_hpcd_machine: ASoC machine data
  */
  */
-static struct snd_soc_machine mpc8610_hpcd_machine = {
+static struct snd_soc_card mpc8610_hpcd_machine = {
 	.probe = mpc8610_hpcd_machine_probe,
 	.probe = mpc8610_hpcd_machine_probe,
 	.remove = mpc8610_hpcd_machine_remove,
 	.remove = mpc8610_hpcd_machine_remove,
 	.name = "MPC8610 HPCD",
 	.name = "MPC8610 HPCD",
@@ -465,9 +465,9 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
 		goto error;
 		goto error;
 	}
 	}
 
 
-	machine_data->sound_devdata.machine = &mpc8610_hpcd_machine;
+	machine_data->sound_devdata.card = &mpc8610_hpcd_machine;
 	machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270;
 	machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270;
-	machine_data->sound_devdata.platform = &fsl_soc_platform;
+	machine_data->machine.platform = &fsl_soc_platform;
 
 
 	sound_device->dev.platform_data = machine_data;
 	sound_device->dev.platform_data = machine_data;
 
 

+ 6 - 6
sound/soc/fsl/soc-of-simple.c

@@ -31,7 +31,7 @@ struct of_snd_soc_device {
 	int id;
 	int id;
 	struct list_head list;
 	struct list_head list;
 	struct snd_soc_device device;
 	struct snd_soc_device device;
-	struct snd_soc_machine machine;
+	struct snd_soc_card card;
 	struct snd_soc_dai_link dai_link;
 	struct snd_soc_dai_link dai_link;
 	struct platform_device *pdev;
 	struct platform_device *pdev;
 	struct device_node *platform_node;
 	struct device_node *platform_node;
@@ -58,9 +58,9 @@ of_snd_soc_get_device(struct device_node *codec_node)
 	/* Initialize the structure and add it to the global list */
 	/* Initialize the structure and add it to the global list */
 	of_soc->codec_node = codec_node;
 	of_soc->codec_node = codec_node;
 	of_soc->id = of_snd_soc_next_index++;
 	of_soc->id = of_snd_soc_next_index++;
-	of_soc->machine.dai_link = &of_soc->dai_link;
-	of_soc->machine.num_links = 1;
-	of_soc->device.machine = &of_soc->machine;
+	of_soc->card.dai_link = &of_soc->dai_link;
+	of_soc->card.num_links = 1;
+	of_soc->device.card = &of_soc->card;
 	of_soc->dai_link.ops = &of_snd_soc_ops;
 	of_soc->dai_link.ops = &of_snd_soc_ops;
 	list_add(&of_soc->list, &of_snd_soc_device_list);
 	list_add(&of_soc->list, &of_snd_soc_device_list);
 
 
@@ -158,8 +158,8 @@ int of_snd_soc_register_platform(struct snd_soc_platform *platform,
 
 
 	of_soc->platform_node = node;
 	of_soc->platform_node = node;
 	of_soc->dai_link.cpu_dai = cpu_dai;
 	of_soc->dai_link.cpu_dai = cpu_dai;
-	of_soc->device.platform = platform;
-	of_soc->machine.name = of_soc->dai_link.cpu_dai->name;
+	of_soc->card.platform = platform;
+	of_soc->card.name = of_soc->dai_link.cpu_dai->name;
 
 
 	/* Now try to register the SoC device */
 	/* Now try to register the SoC device */
 	of_snd_soc_register_device(of_soc);
 	of_snd_soc_register_device(of_soc);

+ 34 - 1
sound/soc/omap/Kconfig

@@ -1,6 +1,6 @@
 config SND_OMAP_SOC
 config SND_OMAP_SOC
 	tristate "SoC Audio for the Texas Instruments OMAP chips"
 	tristate "SoC Audio for the Texas Instruments OMAP chips"
-	depends on ARCH_OMAP && SND_SOC
+	depends on ARCH_OMAP
 
 
 config SND_OMAP_SOC_MCBSP
 config SND_OMAP_SOC_MCBSP
 	tristate
 	tristate
@@ -21,3 +21,36 @@ config SND_OMAP_SOC_OSK5912
 	select SND_SOC_TLV320AIC23
 	select SND_SOC_TLV320AIC23
 	help
 	help
 	  Say Y if you want to add support for SoC audio on osk5912.
 	  Say Y if you want to add support for SoC audio on osk5912.
+
+config SND_OMAP_SOC_OVERO
+	tristate "SoC Audio support for Gumstix Overo"
+	depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OVERO
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TWL4030
+	help
+	  Say Y if you want to add support for SoC audio on the Gumstix Overo.
+
+config SND_OMAP_SOC_OMAP2EVM
+	tristate "SoC Audio support for OMAP2EVM board"
+	depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP2EVM
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TWL4030
+	help
+	  Say Y if you want to add support for SoC audio on the omap2evm board.
+
+config SND_OMAP_SOC_SDP3430
+	tristate "SoC Audio support for Texas Instruments SDP3430"
+	depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TWL4030
+	help
+	  Say Y if you want to add support for SoC audio on Texas Instruments
+	  SDP3430.
+
+config SND_OMAP_SOC_OMAP3_PANDORA
+	tristate "SoC Audio support for OMAP3 Pandora"
+	depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_PANDORA
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TWL4030
+	help
+	  Say Y if you want to add support for SoC audio on the OMAP3 Pandora.

+ 8 - 0
sound/soc/omap/Makefile

@@ -8,6 +8,14 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
 # OMAP Machine Support
 # OMAP Machine Support
 snd-soc-n810-objs := n810.o
 snd-soc-n810-objs := n810.o
 snd-soc-osk5912-objs := osk5912.o
 snd-soc-osk5912-objs := osk5912.o
+snd-soc-overo-objs := overo.o
+snd-soc-omap2evm-objs := omap2evm.o
+snd-soc-sdp3430-objs := sdp3430.o
+snd-soc-omap3pandora-objs := omap3pandora.o
 
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
+obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
+obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
+obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
+obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o

+ 7 - 3
sound/soc/omap/n810.c

@@ -70,9 +70,13 @@ static void n810_ext_control(struct snd_soc_codec *codec)
 
 
 static int n810_startup(struct snd_pcm_substream *substream)
 static int n810_startup(struct snd_pcm_substream *substream)
 {
 {
+	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_codec *codec = rtd->socdev->codec;
 	struct snd_soc_codec *codec = rtd->socdev->codec;
 
 
+	snd_pcm_hw_constraint_minmax(runtime,
+				     SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+
 	n810_ext_control(codec);
 	n810_ext_control(codec);
 	return clk_enable(sys_clkout2);
 	return clk_enable(sys_clkout2);
 }
 }
@@ -282,8 +286,9 @@ static struct snd_soc_dai_link n810_dai = {
 };
 };
 
 
 /* Audio machine driver */
 /* Audio machine driver */
-static struct snd_soc_machine snd_soc_machine_n810 = {
+static struct snd_soc_card snd_soc_n810 = {
 	.name = "N810",
 	.name = "N810",
+	.platform = &omap_soc_platform,
 	.dai_link = &n810_dai,
 	.dai_link = &n810_dai,
 	.num_links = 1,
 	.num_links = 1,
 };
 };
@@ -298,8 +303,7 @@ static struct aic3x_setup_data n810_aic33_setup = {
 
 
 /* Audio subsystem */
 /* Audio subsystem */
 static struct snd_soc_device n810_snd_devdata = {
 static struct snd_soc_device n810_snd_devdata = {
-	.machine = &snd_soc_machine_n810,
-	.platform = &omap_soc_platform,
+	.card = &snd_soc_n810,
 	.codec_dev = &soc_codec_dev_aic3x,
 	.codec_dev = &soc_codec_dev_aic3x,
 	.codec_data = &n810_aic33_setup,
 	.codec_data = &n810_aic33_setup,
 };
 };

部分文件因为文件数量过多而无法显示