]> pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'upstream' into topic/asoc
authorTakashi Iwai <tiwai@suse.de>
Mon, 1 Dec 2008 17:02:17 +0000 (18:02 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 1 Dec 2008 17:02:17 +0000 (18:02 +0100)
126 files changed:
Documentation/sound/alsa/soc/machine.txt
MAINTAINERS
arch/arm/mach-pxa/include/mach/palmasoc.h [new file with mode: 0644]
include/sound/l3.h [new file with mode: 0644]
include/sound/s3c24xx_uda134x.h [new file with mode: 0644]
include/sound/soc-dai.h [new file with mode: 0644]
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/uda134x.h [new file with mode: 0644]
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/at32/Kconfig [deleted file]
sound/soc/at32/Makefile [deleted file]
sound/soc/at32/at32-pcm.h [deleted file]
sound/soc/at32/at32-ssc.c [deleted file]
sound/soc/at32/at32-ssc.h [deleted file]
sound/soc/at91/Kconfig [deleted file]
sound/soc/at91/Makefile [deleted file]
sound/soc/at91/at91-pcm.c [deleted file]
sound/soc/at91/at91-pcm.h [deleted file]
sound/soc/at91/at91-ssc.c [deleted file]
sound/soc/at91/at91-ssc.h [deleted file]
sound/soc/atmel/Kconfig [new file with mode: 0644]
sound/soc/atmel/Makefile [new file with mode: 0644]
sound/soc/atmel/atmel-pcm.c [moved from sound/soc/at32/at32-pcm.c with 50% similarity]
sound/soc/atmel/atmel-pcm.h [new file with mode: 0644]
sound/soc/atmel/atmel_ssc_dai.c [new file with mode: 0644]
sound/soc/atmel/atmel_ssc_dai.h [new file with mode: 0644]
sound/soc/atmel/playpaq_wm8510.c [moved from sound/soc/at32/playpaq_wm8510.c with 98% similarity]
sound/soc/atmel/sam9g20_wm8731.c [new file with mode: 0644]
sound/soc/au1x/psc-ac97.c
sound/soc/au1x/psc-i2s.c
sound/soc/au1x/sample-ac97.c
sound/soc/blackfin/Kconfig
sound/soc/blackfin/bf5xx-ac97-pcm.c
sound/soc/blackfin/bf5xx-ac97.c
sound/soc/blackfin/bf5xx-ac97.h
sound/soc/blackfin/bf5xx-ad1980.c
sound/soc/blackfin/bf5xx-ad73311.c
sound/soc/blackfin/bf5xx-i2s.c
sound/soc/blackfin/bf5xx-sport.h
sound/soc/blackfin/bf5xx-ssm2602.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ac97.c
sound/soc/codecs/ad1980.c
sound/soc/codecs/ad73311.c
sound/soc/codecs/ak4535.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/l3.c [new file with mode: 0644]
sound/soc/codecs/pcm3008.c [new file with mode: 0644]
sound/soc/codecs/pcm3008.h [new file with mode: 0644]
sound/soc/codecs/ssm2602.c
sound/soc/codecs/tlv320aic23.c
sound/soc/codecs/tlv320aic26.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tlv320aic3x.h
sound/soc/codecs/twl4030.c [new file with mode: 0644]
sound/soc/codecs/twl4030.h [new file with mode: 0644]
sound/soc/codecs/uda134x.c [new file with mode: 0644]
sound/soc/codecs/uda134x.h [new file with mode: 0644]
sound/soc/codecs/uda1380.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8580.h
sound/soc/codecs/wm8728.c [new file with mode: 0644]
sound/soc/codecs/wm8728.h [new file with mode: 0644]
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8750.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8900.h
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8971.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8990.h
sound/soc/codecs/wm9712.c
sound/soc/codecs/wm9713.c
sound/soc/davinci/Kconfig
sound/soc/davinci/Makefile
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-sffsdr.c [new file with mode: 0644]
sound/soc/fsl/fsl_dma.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/mpc5200_psc_i2s.c
sound/soc/fsl/mpc8610_hpcd.c
sound/soc/fsl/soc-of-simple.c
sound/soc/omap/Kconfig
sound/soc/omap/Makefile
sound/soc/omap/n810.c
sound/soc/omap/omap-mcbsp.c
sound/soc/omap/omap2evm.c [new file with mode: 0644]
sound/soc/omap/omap3beagle.c [new file with mode: 0644]
sound/soc/omap/osk5912.c
sound/soc/omap/overo.c [new file with mode: 0644]
sound/soc/omap/sdp3430.c [new file with mode: 0644]
sound/soc/pxa/Kconfig
sound/soc/pxa/Makefile
sound/soc/pxa/corgi.c
sound/soc/pxa/e800_wm9712.c
sound/soc/pxa/em-x270.c
sound/soc/pxa/palm27x.c [new file with mode: 0644]
sound/soc/pxa/poodle.c
sound/soc/pxa/pxa-ssp.c [new file with mode: 0644]
sound/soc/pxa/pxa-ssp.h [new file with mode: 0644]
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/pxa/pxa2xx-i2s.c
sound/soc/pxa/pxa2xx-pcm.c
sound/soc/pxa/spitz.c
sound/soc/pxa/tosa.c
sound/soc/pxa/zylonite.c [new file with mode: 0644]
sound/soc/s3c24xx/Kconfig
sound/soc/s3c24xx/Makefile
sound/soc/s3c24xx/ln2440sbc_alc650.c
sound/soc/s3c24xx/neo1973_wm8753.c
sound/soc/s3c24xx/s3c2412-i2s.c
sound/soc/s3c24xx/s3c2443-ac97.c
sound/soc/s3c24xx/s3c24xx-i2s.c
sound/soc/s3c24xx/s3c24xx_uda134x.c [new file with mode: 0644]
sound/soc/s3c24xx/smdk2443_wm9710.c
sound/soc/sh/hac.c
sound/soc/sh/sh7760-ac97.c
sound/soc/sh/ssi.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index f370e7db86af6a970de14433523af4b1ca71d390..bab7711ce96311470a3eddb9a9500772b02a777e 100644 (file)
@@ -9,7 +9,7 @@ the audio subsystem with the kernel as a platform device and is represented by
 the following struct:-
 
 /* SoC machine */
-struct snd_soc_machine {
+struct snd_soc_card {
        char *name;
 
        int (*probe)(struct platform_device *pdev);
@@ -67,10 +67,10 @@ static struct snd_soc_dai_link corgi_dai = {
        .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 */
-static struct snd_soc_machine snd_soc_machine_corgi = {
+static struct snd_soc_card snd_soc_corgi = {
        .name = "Corgi",
        .dai_link = &corgi_dai,
        .num_links = 1,
@@ -90,7 +90,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = {
 
 /* corgi audio subsystem */
 static struct snd_soc_device corgi_snd_devdata = {
-       .machine = &snd_soc_machine_corgi,
+       .machine = &snd_soc_corgi,
        .platform = &pxa2xx_soc_platform,
        .codec_dev = &soc_codec_dev_wm8731,
        .codec_data = &corgi_wm8731_setup,
index 618c1ef4a397502749b2d08a49e80806e99825d1..6df20eaf041866e8f305faad6c462893522d52fd 100644 (file)
@@ -3967,7 +3967,7 @@ M:        tiwai@suse.de
 L:     alsa-devel@alsa-project.org (subscribers-only)
 S:     Maintained
 
-SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT
+SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
 P:     Liam Girdwood
 M:     lrg@slimlogic.co.uk
 P:     Mark Brown
diff --git a/arch/arm/mach-pxa/include/mach/palmasoc.h b/arch/arm/mach-pxa/include/mach/palmasoc.h
new file mode 100644 (file)
index 0000000..6c4b1f7
--- /dev/null
@@ -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
diff --git a/include/sound/l3.h b/include/sound/l3.h
new file mode 100644 (file)
index 0000000..423a08f
--- /dev/null
@@ -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
diff --git a/include/sound/s3c24xx_uda134x.h b/include/sound/s3c24xx_uda134x.h
new file mode 100644 (file)
index 0000000..33df4cb
--- /dev/null
@@ -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
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
new file mode 100644 (file)
index 0000000..a01a24b
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * 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 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;
+
+       /* 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_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
index ca699a3017f39563fc8f6b1d4dc36841e724f125..7ee2f70ca42e9009038d02b60e147603ad919d13 100644 (file)
@@ -221,8 +221,6 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
        int num);
 
 /* 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);
 void snd_soc_dapm_free(struct snd_soc_device *socdev);
 int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
index 5e0189876afda7a9d523ae705a113cce1be2c69d..444f9c211379d726a761015ff69494f0026c2f70 100644 (file)
@@ -21,8 +21,6 @@
 #include <sound/control.h>
 #include <sound/ac97_codec.h>
 
-#define SND_SOC_VERSION "0.13.2"
-
 /*
  * Convenience kcontrol builders
  */
@@ -145,84 +143,6 @@ enum snd_soc_bias_level {
        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_pcm_stream;
 struct snd_soc_ops;
@@ -230,7 +150,6 @@ struct snd_soc_dai_mode;
 struct snd_soc_pcm_runtime;
 struct snd_soc_dai;
 struct snd_soc_codec;
-struct snd_soc_machine_config;
 struct soc_enum;
 struct snd_soc_ac97_ops;
 struct snd_soc_clock_info;
@@ -263,27 +182,6 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
        struct snd_ac97_bus_ops *ops, int num);
 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
  */
@@ -341,61 +239,6 @@ struct snd_soc_ops {
        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 */
 struct snd_soc_codec {
        char *name;
@@ -426,6 +269,7 @@ struct snd_soc_codec {
        short reg_cache_step;
 
        /* dapm */
+       u32 pop_time;
        struct list_head dapm_widgets;
        struct list_head dapm_paths;
        enum snd_soc_bias_level bias_level;
@@ -484,8 +328,8 @@ struct snd_soc_dai_link  {
        struct snd_pcm *pcm;
 };
 
-/* SoC machine */
-struct snd_soc_machine {
+/* SoC card */
+struct snd_soc_card {
        char *name;
 
        int (*probe)(struct platform_device *pdev);
@@ -499,7 +343,7 @@ struct snd_soc_machine {
        int (*resume_post)(struct platform_device *pdev);
 
        /* callbacks */
-       int (*set_bias_level)(struct snd_soc_machine *,
+       int (*set_bias_level)(struct snd_soc_card *,
                              enum snd_soc_bias_level level);
 
        /* CPU <--> Codec DAI links  */
@@ -510,13 +354,16 @@ struct snd_soc_machine {
 /* SoC Device - the audio subsystem */
 struct snd_soc_device {
        struct device *dev;
-       struct snd_soc_machine *machine;
+       struct snd_soc_card *card;
        struct snd_soc_platform *platform;
        struct snd_soc_codec *codec;
        struct snd_soc_codec_device *codec_dev;
        struct delayed_work delayed_work;
        struct work_struct deferred_resume_work;
        void *codec_data;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry   *debugfs_root;
+#endif
 };
 
 /* runtime channel data */
@@ -542,4 +389,6 @@ struct soc_enum {
        void *dapm;
 };
 
+#include <sound/soc-dai.h>
+
 #endif
diff --git a/include/sound/uda134x.h b/include/sound/uda134x.h
new file mode 100644 (file)
index 0000000..475ef8b
--- /dev/null
@@ -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 */
index 4dfda6674bec98507ee354a5c7a84f9606a9ab37..615ebf0b76e754c96cde95d2a22acdaf63b44676 100644 (file)
@@ -23,8 +23,7 @@ config SND_SOC_AC97_BUS
        bool
 
 # All the supported Soc's
-source "sound/soc/at32/Kconfig"
-source "sound/soc/at91/Kconfig"
+source "sound/soc/atmel/Kconfig"
 source "sound/soc/au1x/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
index d849349f2c6607ddb5a0da9a1097eae024b22848..4d475c3ceb9142720126aa02ebefef4c8ed1957b 100644 (file)
@@ -1,5 +1,5 @@
 snd-soc-core-objs := soc-core.o soc-dapm.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)  += codecs/ atmel/ pxa/ s3c24xx/ sh/ fsl/ davinci/
 obj-$(CONFIG_SND_SOC)  += omap/ au1x/ blackfin/
diff --git a/sound/soc/at32/Kconfig b/sound/soc/at32/Kconfig
deleted file mode 100644 (file)
index b0765e8..0000000
+++ /dev/null
@@ -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.
diff --git a/sound/soc/at32/Makefile b/sound/soc/at32/Makefile
deleted file mode 100644 (file)
index c03e55e..0000000
+++ /dev/null
@@ -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
diff --git a/sound/soc/at32/at32-pcm.h b/sound/soc/at32/at32-pcm.h
deleted file mode 100644 (file)
index 2a52430..0000000
+++ /dev/null
@@ -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 */
diff --git a/sound/soc/at32/at32-ssc.c b/sound/soc/at32/at32-ssc.c
deleted file mode 100644 (file)
index 4ef6492..0000000
+++ /dev/null
@@ -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");
diff --git a/sound/soc/at32/at32-ssc.h b/sound/soc/at32/at32-ssc.h
deleted file mode 100644 (file)
index 3c052db..0000000
+++ /dev/null
@@ -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 */
diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig
deleted file mode 100644 (file)
index 85a8832..0000000
+++ /dev/null
@@ -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
diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile
deleted file mode 100644 (file)
index b817f11..0000000
+++ /dev/null
@@ -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
diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c
deleted file mode 100644 (file)
index 7ab48bd..0000000
+++ /dev/null
@@ -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");
diff --git a/sound/soc/at91/at91-pcm.h b/sound/soc/at91/at91-pcm.h
deleted file mode 100644 (file)
index e5aada2..0000000
+++ /dev/null
@@ -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 */
diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c
deleted file mode 100644 (file)
index 1b61cc4..0000000
+++ /dev/null
@@ -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");
diff --git a/sound/soc/at91/at91-ssc.h b/sound/soc/at91/at91-ssc.h
deleted file mode 100644 (file)
index 6b7bf38..0000000
+++ /dev/null
@@ -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 */
-
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
new file mode 100644 (file)
index 0000000..a608d70
--- /dev/null
@@ -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.
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
new file mode 100644 (file)
index 0000000..f54a7cc
--- /dev/null
@@ -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
similarity index 50%
rename from sound/soc/at32/at32-pcm.c
rename to sound/soc/atmel/atmel-pcm.c
index c83584f989a9bea6223e611acce0ea37f8f2b51c..394412fb396f67414cd0e8abd89032f2e6ffbdec 100644 (file)
@@ -1,15 +1,34 @@
-/* sound/soc/at32/at32-pcm.c
- * ASoC PCM interface for Atmel AT32 SoC
+/*
+ * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC.
  *
- * Copyright (C) 2008 Long Range Systems
- *    Geoffrey Wossum <gwossum@acm.org>
+ *  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 version 2 as
- * published by the Free Software Foundation.
+ * 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.
  *
- * 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.
+ * 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/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 "at32-pcm.h"
+#include <mach/hardware.h>
 
+#include "atmel-pcm.h"
 
 
 /*--------------------------------------------------------------------------*\
 /* 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,
+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 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 */
+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 */
+       dma_addr_t period_ptr;          /* physical address of next period */
+       int periods;                    /* period index of period_ptr */
 
-       /* Save PDC registers (for power management) */
+       /* PDC register save */
        u32 pdc_xpr_save;
        u32 pdc_xcr_save;
        u32 pdc_xnpr_save;
@@ -71,49 +89,51 @@ struct at32_runtime_data {
 };
 
 
-
 /*--------------------------------------------------------------------------*\
  * Helper functions
 \*--------------------------------------------------------------------------*/
-static int at32_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+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 *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)
+       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;
 
-       dmabuf->bytes = size;
+       buf->bytes = size;
        return 0;
 }
-
-
-
 /*--------------------------------------------------------------------------*\
  * ISR
 \*--------------------------------------------------------------------------*/
-static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
+static void atmel_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;
+       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("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);
+               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,
@@ -122,7 +142,6 @@ static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
                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,
@@ -131,60 +150,58 @@ static void at32_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream)
                           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)
+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 at32_runtime_data *prtd = runtime->private_data;
+       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
-        */
+        * 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->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("hw_params: DMA for %s initialized "
-                "(dma_bytes=%ld, period_size=%ld)\n",
-                prtd->params->name, runtime->dma_bytes, prtd->period_size);
-
+       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 at32_pcm_hw_free(struct snd_pcm_substream *substream)
+static int atmel_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;
+       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,
@@ -195,32 +212,29 @@ static int at32_pcm_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
-
-
-static int at32_pcm_prepare(struct snd_pcm_substream *substream)
+static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
 {
-       struct at32_runtime_data *prtd = substream->runtime->private_data;
-       struct at32_pcm_dma_params *params = prtd->params;
+       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 at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int atmel_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;
+       struct atmel_runtime_data *prtd = rtd->private_data;
+       struct atmel_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);
+       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:
@@ -237,26 +251,25 @@ static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                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));
+               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=%x, imr=%x\n",
-                        ssc_readx(params->ssc->regs, SSC_SR),
-                        ssc_readx(params->ssc->regs, SSC_IER));
+               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:
@@ -264,7 +277,6 @@ static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                           params->mask->pdc_disable);
                break;
 
-
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
@@ -278,13 +290,12 @@ static int at32_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        return ret;
 }
 
-
-
-static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t atmel_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;
+       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;
 
@@ -297,108 +308,95 @@ static snd_pcm_uframes_t at32_pcm_pointer(struct snd_pcm_substream *substream)
        return x;
 }
 
-
-
-static int at32_pcm_open(struct snd_pcm_substream *substream)
+static int atmel_pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct at32_runtime_data *prtd;
+       struct atmel_runtime_data *prtd;
        int ret = 0;
 
-       snd_soc_set_runtime_hwparams(substream, &at32_pcm_hardware);
+       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);
+                                               SNDRV_PCM_HW_PARAM_PERIODS);
        if (ret < 0)
                goto out;
 
-       prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+       prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
        if (prtd == NULL) {
                ret = -ENOMEM;
                goto out;
        }
        runtime->private_data = prtd;
 
-
-out:
+ out:
        return ret;
 }
 
-
-
-static int at32_pcm_close(struct snd_pcm_substream *substream)
+static int atmel_pcm_close(struct snd_pcm_substream *substream)
 {
-       struct at32_runtime_data *prtd = substream->runtime->private_data;
+       struct atmel_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)
+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);
+                      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,
+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 at32_pcm_dmamask = 0xffffffff;
+static u64 atmel_pcm_dmamask = 0xffffffff;
 
-static int at32_pcm_new(struct snd_card *card,
-                       struct snd_soc_dai *dai,
-                       struct snd_pcm *pcm)
+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 = &at32_pcm_dmamask;
+               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 = at32_pcm_preallocate_dma_buffer(
-                         pcm, SNDRV_PCM_STREAM_PLAYBACK);
+               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 = at32_pcm_preallocate_dma_buffer(
-                         pcm, SNDRV_PCM_STREAM_CAPTURE);
+               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:
+ out:
        return ret;
 }
 
-
-
-static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
+static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
 {
        struct snd_pcm_substream *substream;
        struct snd_dma_buffer *buf;
@@ -406,7 +404,7 @@ static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
        for (stream = 0; stream < 2; stream++) {
                substream = pcm->streams[stream].substream;
-               if (substream == NULL)
+               if (!substream)
                        continue;
 
                buf = &substream->dma_buffer;
@@ -418,24 +416,23 @@ static void at32_pcm_free_dma_buffers(struct snd_pcm *pcm)
        }
 }
 
-
-
 #ifdef CONFIG_PM
-static int at32_pcm_suspend(struct platform_device *pdev,
-                           struct snd_soc_dai *dai)
+static int atmel_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;
+       struct atmel_runtime_data *prtd;
+       struct atmel_pcm_dma_params *params;
 
-       if (runtime == NULL)
+       if (!runtime)
                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);
+       /* 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);
@@ -445,48 +442,43 @@ static int at32_pcm_suspend(struct platform_device *pdev,
        return 0;
 }
 
-
-
-static int at32_pcm_resume(struct platform_device *pdev,
-                          struct snd_soc_dai *dai)
+static int atmel_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;
+       struct atmel_runtime_data *prtd;
+       struct atmel_pcm_dma_params *params;
 
-       if (runtime == NULL)
+       if (!runtime)
                return 0;
+
        prtd = runtime->private_data;
        params = prtd->params;
 
-       /* Restore the PDC registers and enable the PDC */
+       /* 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);
+       ssc_writel(params->ssc->regs, 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,
+#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(at32_soc_platform);
-
-
+EXPORT_SYMBOL_GPL(atmel_soc_platform);
 
-MODULE_AUTHOR("Geoffrey Wossum <gwossum@acm.org>");
-MODULE_DESCRIPTION("Atmel AT32 PCM module");
+MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
+MODULE_DESCRIPTION("Atmel PCM module");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
new file mode 100644 (file)
index 0000000..ec9b282
--- /dev/null
@@ -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 */
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
new file mode 100644 (file)
index 0000000..0bb18df
--- /dev/null
@@ -0,0 +1,780 @@
+/*
+ * 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 platform_device *pdev,
+                           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 platform_device *pdev,
+                          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);
+
+/* Module information */
+MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
+MODULE_DESCRIPTION("ATMEL SSC ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h
new file mode 100644 (file)
index 0000000..a828746
--- /dev/null
@@ -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 */
similarity index 98%
rename from sound/soc/at32/playpaq_wm8510.c
rename to sound/soc/atmel/playpaq_wm8510.c
index b1966e4dfcd3e7b2035c15f47b5842d2438d253e..d40b5a52a8d28169dc9375985894696913bada6b 100644 (file)
@@ -22,7 +22,6 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/clk.h>
@@ -40,8 +39,8 @@
 #include <mach/portmux.h>
 
 #include "../codecs/wm8510.h"
-#include "at32-pcm.h"
-#include "at32-ssc.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
 
 
 /*-------------------------------------------------------------------------*\
@@ -362,7 +361,7 @@ 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",
        .dai_link = &playpaq_wm8510_dai,
        .num_links = 1,
@@ -378,7 +377,7 @@ static struct wm8510_setup_data playpaq_wm8510_setup = {
 
 
 static struct snd_soc_device playpaq_wm8510_snd_devdata = {
-       .machine = &snd_soc_machine_playpaq,
+       .card = &snd_soc_playpaq,
        .platform = &at32_soc_platform,
        .codec_dev = &soc_codec_dev_wm8510,
        .codec_data = &playpaq_wm8510_setup,
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
new file mode 100644 (file)
index 0000000..fdc1d02
--- /dev/null
@@ -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",
+       .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,
+       .platform = &atmel_soc_platform,
+       .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");
index 57facbad68254cc4b21ecb189bb3ad161afb495e..a0bcfeaf5f868e1fc582a13a940f3a82004f76cb 100644 (file)
@@ -160,7 +160,8 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 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 */
        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,
-                               int cmd)
+                               int cmd, struct snd_soc_dai *dai)
 {
        /* FIXME */
        struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
@@ -345,7 +346,7 @@ static int au1xpsc_ac97_resume(struct platform_device *pdev,
 
 struct snd_soc_dai au1xpsc_ac97_dai = {
        .name                   = "au1xpsc_ac97",
-       .type                   = SND_SOC_DAI_AC97,
+       .ac97_control           = 1,
        .probe                  = au1xpsc_ac97_probe,
        .remove                 = au1xpsc_ac97_remove,
        .suspend                = au1xpsc_ac97_suspend,
index 9384702c7ebd7c09f0df6d4e25f9161ac854cc37..f4217e70a7871b41328d88ee6a1814cd1189f3c1 100644 (file)
@@ -116,7 +116,8 @@ out:
 }
 
 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;
 
@@ -240,7 +241,8 @@ static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype)
        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;
        int ret, stype = SUBSTREAM_TYPE(substream);
@@ -369,7 +371,6 @@ static int au1xpsc_i2s_resume(struct platform_device *pdev,
 
 struct snd_soc_dai au1xpsc_i2s_dai = {
        .name                   = "au1xpsc_i2s",
-       .type                   = SND_SOC_DAI_I2S,
        .probe                  = au1xpsc_i2s_probe,
        .remove                 = au1xpsc_i2s_remove,
        .suspend                = au1xpsc_i2s_suspend,
@@ -389,8 +390,6 @@ struct snd_soc_dai au1xpsc_i2s_dai = {
        .ops = {
                .trigger        = au1xpsc_i2s_trigger,
                .hw_params      = au1xpsc_i2s_hw_params,
-       },
-       .dai_ops = {
                .set_fmt        = au1xpsc_i2s_set_fmt,
        },
 };
index f75ae7f62c3d4c157ec1b51729b972e173464474..27683eb7905e305f800211dcfa21718e73a4c5c9 100644 (file)
@@ -42,14 +42,14 @@ static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = {
        .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",
        .dai_link       = &au1xpsc_sample_ac97_dai,
        .num_links      = 1,
 };
 
 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 */
        .codec_dev      = &soc_codec_dev_ac97,
 };
index dc006206f622d043d4257162e71466f3b77bf0fb..e162cbbd3f3b4e4e5da80df9326a946357b4970a 100644 (file)
@@ -13,7 +13,6 @@ config SND_BF5XX_SOC_SSM2602
        select SND_BF5XX_SOC_I2S
        select SND_SOC_SSM2602
        select I2C
-       select I2C_BLACKFIN_TWI
        help
          Say Y if you want to add support for SoC audio on BF527-EZKIT.
 
@@ -47,7 +46,7 @@ config SND_BF5XX_AC97
          properly with this driver. This driver is known to work with the
          Analog Devices line of AC97 codecs.
 
-config SND_MMAP_SUPPORT
+config SND_BF5XX_MMAP_SUPPORT
        bool "Enable MMAP Support"
        depends on SND_BF5XX_AC97
        default y
@@ -55,9 +54,17 @@ config SND_MMAP_SUPPORT
          Say y if you want AC97 driver to support mmap mode.
          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
        tristate
-       
+
 config SND_BF5XX_SOC_I2S
        tristate
        select SND_BF5XX_SOC_SPORT
@@ -80,7 +87,7 @@ config SND_BF5XX_SPORT_NUM
        int "Set a SPORT for Sound chip"
        depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
        range 0 3 if BF54x
-       range 0 1 if (BF53x || BF561)
+       range 0 1 if !BF54x
        default 0
        help
          Set the correct SPORT for sound chip.
@@ -90,12 +97,13 @@ config SND_BF5XX_HAVE_COLD_RESET
        depends on SND_BF5XX_AC97
        default y if BFIN548_EZKIT
        default n if !BFIN548_EZKIT
-       
+
 config SND_BF5XX_RESET_GPIO_NUM
        int "Set a GPIO for cold reset"
        depends on SND_BF5XX_HAVE_COLD_RESET
        range 0 159
        default 19 if BFIN548_EZKIT
        default 5 if BFIN537_STAMP
+       default 0
        help
          Set the correct GPIO for RESET the sound chip.
index 25e50d2ea1ec321d4140295f6d6ce587d79f0a9a..d3d51bcb4569cac2a4fd69f3b7f18efd01db4e6c 100644 (file)
 #include "bf5xx-ac97.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,
         snd_pcm_uframes_t count)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct sport_device *sport = runtime->private_data;
+       unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1];
        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;
                if (sport->tx_pos >= runtime->buffer_size)
                        sport->tx_pos %= runtime->buffer_size;
                sport->tx_delay_pos = sport->tx_pos;
        } 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;
                if (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)
 {
        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 sport_device *sport = runtime->private_data;
        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
  * is  0x20000 * sizeof(struct ac97_frame) / 4.
  */
-#ifdef CONFIG_SND_MMAP_SUPPORT
 static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
                                   SNDRV_PCM_INFO_MMAP |
                                   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
+                                  SNDRV_PCM_INFO_BLOCK_TRANSFER,
+
        .formats                = SNDRV_PCM_FMTBIT_S16_LE,
        .period_bytes_min       = 32,
        .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)
 {
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        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);
        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
         * 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) {
                sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
                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) {
        case SNDRV_PCM_TRIGGER_START:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
                        bf5xx_mmap_copy(substream, runtime->period_size);
-                       snd_pcm_period_elapsed(substream);
                        sport->tx_delay_pos = 0;
+#endif
                        sport_tx_start(sport);
-               }
-               else
+               } else
                        sport_rx_start(sport);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
                        sport->tx_pos = 0;
 #endif
                        sport_tx_stop(sport);
                } else {
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
                        sport->rx_pos = 0;
 #endif
                        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;
        unsigned int curr;
 
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                curr = sport->tx_delay_pos;
        else
@@ -249,22 +266,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
        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,
        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)
 {
        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",
                        substream->stream ? "Capture" : "Playback", pos, count);
 
        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
-               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;
 }
 #endif
 
 struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
        .open           = bf5xx_pcm_open,
-       .close          = bf5xx_pcm_close,
        .ioctl          = snd_pcm_lib_ioctl,
        .hw_params      = bf5xx_pcm_hw_params,
        .hw_free        = bf5xx_pcm_hw_free,
        .prepare        = bf5xx_pcm_prepare,
        .trigger        = bf5xx_pcm_trigger,
        .pointer        = bf5xx_pcm_pointer,
-#ifdef CONFIG_SND_MMAP_SUPPORT
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        .mmap           = bf5xx_pcm_mmap,
 #else
        .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
  * 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 (!sport_handle->tx_dma_buf) {
                        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_dma_buffer *buf;
        int stream;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
                sizeof(struct ac97_frame) / 4;
 #endif
@@ -395,7 +394,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
                        continue;
                dma_free_coherent(NULL, buf->bytes, buf->area, 0);
                buf->area = NULL;
-#if defined(CONFIG_SND_MMAP_SUPPORT)
+#if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                if (sport_handle->tx_dma_buf)
                        dma_free_coherent(NULL, size, \
index 5e5aafb6485f0bce67bf211a24bc475db69f686b..709bdf08e398f2fa57c6147f7323acc036fb0e09 100644 (file)
 static int *cmd_count;
 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] = {
        {
                .dma_rx_chan    = CH_SPORT0_RX,
                .dma_tx_chan    = CH_SPORT0_TX,
-               .err_irq        = IRQ_SPORT0_ERR,
+               .err_irq        = IRQ_SPORT0_ERROR,
                .regs           = (struct sport_register *)SPORT0_TCR1,
        },
+#ifdef PIN_REQ_SPORT_1
        {
                .dma_rx_chan    = CH_SPORT1_RX,
                .dma_tx_chan    = CH_SPORT1_TX,
-               .err_irq        = IRQ_SPORT1_ERR,
+               .err_irq        = IRQ_SPORT1_ERROR,
                .regs           = (struct sport_register *)SPORT1_TCR1,
        },
+#endif
+#ifdef PIN_REQ_SPORT_2
        {
                .dma_rx_chan    = CH_SPORT2_RX,
                .dma_tx_chan    = CH_SPORT2_TX,
-               .err_irq        = IRQ_SPORT2_ERR,
+               .err_irq        = IRQ_SPORT2_ERROR,
                .regs           = (struct sport_register *)SPORT2_TCR1,
        },
+#endif
+#ifdef PIN_REQ_SPORT_3
        {
                .dma_rx_chan    = CH_SPORT3_RX,
                .dma_tx_chan    = CH_SPORT3_TX,
-               .err_irq        = IRQ_SPORT3_ERR,
+               .err_irq        = IRQ_SPORT3_ERROR,
                .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
+};
 
-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--) {
-               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);
 
-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)
 {
-       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);
 
 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;
 }
 
@@ -130,7 +162,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
 
        sport_incfrag(sport, &nextfrag, 1);
 
-       nextwrite = (struct ac97_frame *)(sport->tx_buf + \
+       nextwrite = (struct ac97_frame *)(sport->tx_buf +
                        nextfrag * sport->tx_fragsize);
        pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
                sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
@@ -297,20 +329,15 @@ static int bf5xx_ac97_resume(struct platform_device *pdev,
 static int bf5xx_ac97_probe(struct platform_device *pdev,
                            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);
        if (cmd_count == NULL)
                return -ENOMEM;
 
        if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
                pr_err("Requesting Peripherals failed\n");
-               return -EFAULT;
+               ret =  -EFAULT;
+               goto peripheral_err;
                }
 
 #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
@@ -318,54 +345,54 @@ static int bf5xx_ac97_probe(struct platform_device *pdev,
        if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
                pr_err("Failed to request GPIO_%d for reset\n",
                                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);
 #endif
        sport_handle = sport_init(&sport_params[sport_num], 2, \
                        sizeof(struct ac97_frame), NULL);
        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*/
        ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
        if (ret) {
                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));
        if (ret) {
                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));
        if (ret) {
                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;
+
+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,
@@ -373,6 +400,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
 {
        free_page((unsigned long)cmd_count);
        cmd_count = NULL;
+       peripheral_free_list(&sport_req[sport_num][0]);
 #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
        gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
 #endif
@@ -381,7 +409,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
 struct snd_soc_dai bfin_ac97_dai = {
        .name = "bf5xx-ac97",
        .id = 0,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .probe = bf5xx_ac97_probe,
        .remove = bf5xx_ac97_remove,
        .suspend = bf5xx_ac97_suspend,
@@ -389,7 +417,11 @@ struct snd_soc_dai bfin_ac97_dai = {
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 2,
+#if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT)
+               .channels_max = 6,
+#else
                .channels_max = 2,
+#endif
                .rates = SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
        .capture = {
index 3f77cc558dc03fe4a4847096f5bc07f8152dd7d6..3f2a911fe0cb2adda1c45473babef9845d9280c9 100644 (file)
@@ -16,21 +16,46 @@ struct ac97_frame {
        u16 ac97_tag;           /* slot 0 */
        u16 ac97_addr;          /* slot 1 */
        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));
 
+/* 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_CMD                        0x6000
 #define TAG_PCM_LEFT           0x1000
 #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;
 
-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);
 
 #endif
index 124425d223202aeb308a45023616e26a152dc4c0..36c569a43ce18b37765f65ce4ee3e16a8f524910 100644 (file)
@@ -43,7 +43,7 @@
 #include "bf5xx-ac97-pcm.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)
 {
@@ -67,14 +67,14 @@ static struct snd_soc_dai_link bf5xx_board_dai = {
        .ops = &bf5xx_board_ops,
 };
 
-static struct snd_soc_machine bf5xx_board = {
+static struct snd_soc_card bf5xx_board = {
        .name = "bf5xx-board",
        .dai_link = &bf5xx_board_dai,
        .num_links = 1,
 };
 
 static struct snd_soc_device bf5xx_board_snd_devdata = {
-       .machine = &bf5xx_board,
+       .card = &bf5xx_board,
        .platform = &bf5xx_ac97_soc_platform,
        .codec_dev = &soc_codec_dev_ad1980,
 };
index 622c9b9095328a46bdf9b5ed9619916a8d456c92..57da14799375d93a82a90dc373c448a934356299 100644 (file)
@@ -65,7 +65,7 @@
 
 #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)
 {
@@ -168,7 +168,7 @@ static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
                params_format(params));
 
        /* 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);
        if (ret < 0)
                return ret;
@@ -190,7 +190,7 @@ static struct snd_soc_dai_link bf5xx_ad73311_dai = {
        .ops = &bf5xx_ad73311_ops,
 };
 
-static struct snd_soc_machine bf5xx_ad73311 = {
+static struct snd_soc_card bf5xx_ad73311 = {
        .name = "bf5xx_ad73311",
        .probe = bf5xx_probe,
        .dai_link = &bf5xx_ad73311_dai,
@@ -198,7 +198,7 @@ static struct snd_soc_machine bf5xx_ad73311 = {
 };
 
 static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
-       .machine = &bf5xx_ad73311,
+       .card = &bf5xx_ad73311,
        .platform = &bf5xx_i2s_soc_platform,
        .codec_dev = &soc_codec_dev_ad73311,
 };
index e020c160ee441e815b6b6c59159cd30c77ed38ec..6e5036bf9245ddb68fc20aff961dc9a293aea1af 100644 (file)
@@ -132,7 +132,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        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__);
 
@@ -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,
-                               struct snd_pcm_hw_params *params)
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
 {
        int ret = 0;
 
@@ -193,7 +195,8 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
        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__);
        bf5xx_i2s.counter--;
@@ -289,7 +292,6 @@ static int bf5xx_i2s_resume(struct platform_device *pdev,
 struct snd_soc_dai bf5xx_i2s_dai = {
        .name = "bf5xx-i2s",
        .id = 0,
-       .type = SND_SOC_DAI_I2S,
        .probe = bf5xx_i2s_probe,
        .remove = bf5xx_i2s_remove,
        .suspend = bf5xx_i2s_suspend,
@@ -307,8 +309,7 @@ struct snd_soc_dai bf5xx_i2s_dai = {
        .ops = {
                .startup   = bf5xx_i2s_startup,
                .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,
        },
 };
index fcadcc081f7fe4994a2dc3dbc0514656c7c6cd06..2e63dea73e9c64631a980dd8cc5934504e19f84b 100644 (file)
@@ -116,7 +116,7 @@ struct sport_device {
        void *err_data;
        unsigned char *tx_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 rx_dma_phy;
        int tx_pos;/*pcm sample count*/
index e15f67fd7769a8b4fdb7a26c53ef5c3ecbcb8342..0078dfcd95b92e09053f598f12d8034becdfc179 100644 (file)
@@ -44,7 +44,7 @@
 #include "bf5xx-i2s-pcm.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)
 {
@@ -92,17 +92,17 @@ static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
         */
 
        /* 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);
        if (ret < 0)
                return ret;
        /* 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);
        if (ret < 0)
                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);
        if (ret < 0)
                return ret;
@@ -135,14 +135,14 @@ static struct ssm2602_setup_data bf5xx_ssm2602_setup = {
        .i2c_address = 0x1b,
 };
 
-static struct snd_soc_machine bf5xx_ssm2602 = {
+static struct snd_soc_card bf5xx_ssm2602 = {
        .name = "bf5xx_ssm2602",
        .dai_link = &bf5xx_ssm2602_dai,
        .num_links = 1,
 };
 
 static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
-       .machine = &bf5xx_ssm2602,
+       .card = &bf5xx_ssm2602,
        .platform = &bf5xx_i2s_soc_platform,
        .codec_dev = &soc_codec_dev_ssm2602,
        .codec_data = &bf5xx_ssm2602_setup,
index 38a0e3b620a750ad4845ee861f18b323b74cf0f4..bf68052d6924db13ebf2303165f9323247f5b956 100644 (file)
@@ -1,31 +1,39 @@
 config SND_SOC_ALL_CODECS
        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_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
           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
           driver.  Selecting this option will allow these drivers to be built
           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".
 
 
@@ -60,6 +68,12 @@ config SND_SOC_CS4270_VD33_ERRATA
        bool
        depends on SND_SOC_CS4270
 
+config SND_SOC_L3
+       tristate
+
+config SND_SOC_PCM3008
+       tristate
+
 config SND_SOC_SSM2602
        tristate
 
@@ -75,6 +89,14 @@ config SND_SOC_TLV320AIC3X
        tristate
        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
         tristate
 
@@ -84,6 +106,9 @@ config SND_SOC_WM8510
 config SND_SOC_WM8580
        tristate
 
+config SND_SOC_WM8728
+       tristate
+
 config SND_SOC_WM8731
        tristate
 
index 90f0a585fc70e1f5a4d9a9be1b8514a22100917a..9a20fddd09c7e8c4825be5b4c9eefa682cf99c22 100644 (file)
@@ -3,13 +3,18 @@ snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-ak4535-objs := ak4535.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-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.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-wm8510-objs := wm8510.o
 snd-soc-wm8580-objs := wm8580.o
+snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
@@ -25,13 +30,18 @@ obj-$(CONFIG_SND_SOC_AD1980)        += snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_AK4535)   += snd-soc-ak4535.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_TLV320AIC23)      += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)      += snd-soc-tlv320aic26.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_WM8510)   += snd-soc-wm8510.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_WM8750)   += snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8753)   += snd-soc-wm8753.o
index bd1ebdc6c86ce0e6ef021139f5ac10b93831f4d4..c4208c8210c8734cf589d73058ca6b48ecb2713c 100644 (file)
@@ -24,7 +24,8 @@
 
 #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_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 = {
        .name = "AC97 HiFi",
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 1,
index 1397b8e06c0b41eda7002815a276baba8f3ea035..a9a268112d3f45e37a97cad43eb279621d3279d2 100644 (file)
@@ -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 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_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 = {
        .name = "AC97",
+       .ac97_control = 1,
        .playback = {
                .stream_name = "Playback",
                .channels_min = 2,
-               .channels_max = 2,
+               .channels_max = 6,
                .rates = SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
        .capture = {
@@ -192,6 +196,7 @@ static int ad1980_soc_probe(struct platform_device *pdev)
        struct snd_soc_codec *codec;
        int ret = 0;
        u16 vendor_id2;
+       u16 ext_status;
 
        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);
        if (ret < 0) {
-               printk(KERN_ERR "AC97 link error\n");
+               printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
                goto reset_err;
        }
 
@@ -253,9 +258,16 @@ static int ad1980_soc_probe(struct platform_device *pdev)
                                "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);
        ret = snd_soc_register_card(socdev);
index 37af8607b00a1f293aad10b8f5cfd3b120719549..59c4c8f18cbb16741604331d64a0fd2c2a7a6459 100644 (file)
@@ -15,7 +15,6 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
 #include <sound/core.h>
index 2a89b5888e11c33bdedf44b817e0c61282b95d0d..c742290e5533bcfa093a2ce439b208137761224d 100644 (file)
@@ -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,
-       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;
@@ -451,8 +452,6 @@ struct snd_soc_dai ak4535_dai = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
        .ops = {
                .hw_params = ak4535_hw_params,
-       },
-       .dai_ops = {
                .set_fmt = ak4535_set_dai_fmt,
                .digital_mute = ak4535_mute,
                .set_sysclk = ak4535_set_dai_sysclk,
index 0bbd94501d7ea45f1aa8b491bf43d275ffb779c9..7507d468b2004a4d24b578ecc035705d71f9a343 100644 (file)
@@ -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.
  *
- * 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,
  * combines it with the hardware parameters provided, and programs the
  * hardware accordingly.
  */
 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_device *socdev = rtd->socdev;
@@ -450,6 +451,19 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
                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 */
 
        ret = snd_soc_write(codec, CS4270_PWRCTL, 0);
@@ -697,10 +711,10 @@ static int cs4270_probe(struct platform_device *pdev)
        if (codec->control_data) {
                /* Initialize codec ops */
                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
-               cs4270_dai.dai_ops.digital_mute = cs4270_mute;
+               cs4270_dai.ops.digital_mute = cs4270_mute;
 #endif
        } else
                printk(KERN_INFO "cs4270: no I2C device found, "
diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c
new file mode 100644 (file)
index 0000000..5353af5
--- /dev/null
@@ -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");
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
new file mode 100644 (file)
index 0000000..651a15e
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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_register_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);
+
+MODULE_DESCRIPTION("Soc PCM3008 driver");
+MODULE_AUTHOR("Hugo Villeneuve");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm3008.h b/sound/soc/codecs/pcm3008.h
new file mode 100644 (file)
index 0000000..d04e87d
--- /dev/null
@@ -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
index 44ef0dacd564701b3999316d9c2e21c5b41ec4a8..0c5884ea1b00e1ae544f988c86e44bffbc98de00 100644 (file)
@@ -285,16 +285,23 @@ static inline int get_coeff(int mclk, int rate)
 }
 
 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;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
        struct ssm2602_priv *ssm2602 = codec->private_data;
+       struct i2c_client *i2c = codec->control_data;
        u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
        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*/
        if (i == ARRAY_SIZE(coeff_div))
                return -EINVAL;
@@ -324,19 +331,26 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
        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_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
        struct ssm2602_priv *ssm2602 = codec->private_data;
+       struct i2c_client *i2c = codec->control_data;
        struct snd_pcm_runtime *master_runtime;
 
        /* The DAI has shared clocks so if we already have a playback or
         * capture going then constrain this substream to match it.
+        * TODO: the ssm2602 allows pairs of non-matching PB/REC rates
         */
        if (ssm2602->master_substream) {
                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,
                                             SNDRV_PCM_HW_PARAM_RATE,
                                             master_runtime->rate,
@@ -354,7 +368,8 @@ static int ssm2602_startup(struct snd_pcm_substream *substream)
        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_device *socdev = rtd->socdev;
@@ -365,14 +380,21 @@ static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream)
        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_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
+       struct ssm2602_priv *ssm2602 = codec->private_data;
        /* deactivate */
        if (!codec->active)
                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)
@@ -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_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 = {
        .name = "SSM2602",
        .playback = {
@@ -503,20 +528,18 @@ struct snd_soc_dai ssm2602_dai = {
                .channels_min = 2,
                .channels_max = 2,
                .rates = SSM2602_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+               .formats = SSM2602_FORMATS,},
        .capture = {
                .stream_name = "Capture",
                .channels_min = 2,
                .channels_max = 2,
                .rates = SSM2602_RATES,
-               .formats = SNDRV_PCM_FMTBIT_S32_LE,},
+               .formats = SSM2602_FORMATS,},
        .ops = {
                .startup = ssm2602_startup,
                .prepare = ssm2602_pcm_prepare,
                .hw_params = ssm2602_hw_params,
                .shutdown = ssm2602_shutdown,
-       },
-       .dai_ops = {
                .digital_mute = ssm2602_mute,
                .set_sysclk = ssm2602_set_dai_sysclk,
                .set_fmt = ssm2602_set_dai_fmt,
index 44308dac9e18de9d268ccea819f5376c1ec24a92..a4e13d0688c955671c02bd1f3e381e45ab6cbb00 100644 (file)
 
 #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
  */
@@ -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)
 {
        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,
-                                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;
-       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 =
            tlv320aic23_read_reg_cache(codec,
                                       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)) {
        case SNDRV_PCM_FORMAT_S16_LE:
                break;
@@ -332,7 +466,8 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
        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_device *socdev = rtd->socdev;
@@ -344,17 +479,23 @@ static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream)
        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_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->codec;
+       struct aic23 *aic23 = container_of(codec, struct aic23, codec);
 
        /* deactivate */
        if (!codec->active) {
                udelay(50);
                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)
@@ -422,12 +563,9 @@ static int tlv320aic23_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;
-
-       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,
@@ -478,12 +616,10 @@ struct snd_soc_dai tlv320aic23_dai = {
                .prepare = tlv320aic23_pcm_prepare,
                .hw_params = tlv320aic23_hw_params,
                .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);
 
@@ -659,14 +795,15 @@ static int tlv320aic23_probe(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec;
+       struct aic23 *aic23;
        int ret = 0;
 
        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;
-
+       codec = &aic23->codec;
        socdev->codec = codec;
        mutex_init(&codec->mutex);
        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_codec *codec = socdev->codec;
+       struct aic23 *aic23 = container_of(codec, struct aic23, codec);
 
        if (codec->control_data)
                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);
 #endif
        kfree(codec->reg_cache);
-       kfree(codec);
+       kfree(aic23);
 
        return 0;
 }
index bed8a9e63ddc86fd0a8c2d9274b812867149fcb4..6b7ddfc92573ebdb366ebd1c34a9a1055792fbd8 100644 (file)
@@ -125,7 +125,8 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
  * Digital Audio Interface Operations
  */
 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_device *socdev = rtd->socdev;
@@ -287,8 +288,6 @@ struct snd_soc_dai aic26_dai = {
        },
        .ops = {
                .hw_params = aic26_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = aic26_mute,
                .set_sysclk = aic26_set_sysclk,
                .set_fmt = aic26_set_fmt,
index cff276ee261e7a2ecca843b23bc74d4fec761df7..255e784c805bb6419b7679a956ae6e3ef246aa95 100644 (file)
@@ -272,8 +272,10 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
                     DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
        SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
                     0x01, 0),
-       SOC_DOUBLE_R("HP PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
-                    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,
                     LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
 
@@ -281,8 +283,10 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
                     DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
        SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
                     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,
                     LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
 
@@ -333,7 +337,8 @@ SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]);
 
 /* Left DAC_L1 Mixer */
 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("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0),
@@ -341,7 +346,8 @@ static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = {
 
 /* Right DAC_R1 Mixer */
 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("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0),
        SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0),
@@ -350,14 +356,18 @@ static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = {
 /* Left PGA Mixer */
 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("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("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 */
 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("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("Mic3L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1),
        SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1),
 };
 
@@ -379,34 +389,42 @@ SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]);
 
 /* Left PGA Bypass Mixer */
 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("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 */
 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("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 */
 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("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 */
 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("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[] = {
@@ -439,22 +457,26 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
        /* Mono Output */
        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_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0,
                           &aic3x_left_pga_mixer_controls[0],
                           ARRAY_SIZE(aic3x_left_pga_mixer_controls)),
        SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0,
                         &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,
                         &aic3x_left_line2_mux_controls),
 
-       /* Right Inputs to Right ADC */
+       /* Inputs to Right ADC */
        SND_SOC_DAPM_ADC("Right ADC", "Right Capture",
                         LINE1R_2_RADC_CTRL, 2, 0),
        SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0,
                           &aic3x_right_pga_mixer_controls[0],
                           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,
                         &aic3x_right_line1_mux_controls),
        SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0,
@@ -531,7 +553,8 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left DAC Mux", "DAC_L2", "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", "HP Switch", "Left DAC Mux"},
        {"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"},
@@ -557,7 +580,8 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Right DAC Mux", "DAC_R2", "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", "HP Switch", "Right DAC Mux"},
        {"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"},
@@ -592,8 +616,10 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left Line2L Mux", "differential", "LINE2L"},
 
        {"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", "Mic3L Switch", "MIC3L"},
+       {"Left PGA Mixer", "Mic3R Switch", "MIC3R"},
 
        {"Left ADC", NULL, "Left PGA Mixer"},
        {"Left ADC", NULL, "GPIO1 dmic modclk"},
@@ -605,18 +631,23 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Right Line2R Mux", "single-ended", "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", "Line2R Switch", "Right Line2R Mux"},
+       {"Right PGA Mixer", "Mic3L Switch", "MIC3L"},
        {"Right PGA Mixer", "Mic3R Switch", "MIC3R"},
 
        {"Right ADC", NULL, "Right PGA Mixer"},
        {"Right ADC", NULL, "GPIO1 dmic modclk"},
 
        /* 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", "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", "constant VCM", "Left PGA Bypass Mixer"},
@@ -627,10 +658,13 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left HP Out", NULL, "Left PGA Bypass Mixer"},
 
        /* 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", "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", "constant VCM", "Right PGA Bypass Mixer"},
@@ -643,10 +677,11 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Right HP Out", NULL, "Right PGA Bypass Mixer"},
 
        /* 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", "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", "constant VCM", "Left Line2 Bypass Mixer"},
@@ -657,10 +692,11 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"Left HP Out", NULL, "Left Line2 Bypass Mixer"},
 
        /* 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", "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", "constant VCM", "Right Line2 Bypass Mixer"},
@@ -694,7 +730,8 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
 }
 
 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_device *socdev = rtd->socdev;
@@ -1009,8 +1046,6 @@ struct snd_soc_dai aic3x_dai = {
                .formats = AIC3X_FORMATS,},
        .ops = {
                .hw_params = aic3x_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = aic3x_mute,
                .set_sysclk = aic3x_set_dai_sysclk,
                .set_fmt = aic3x_set_dai_fmt,
index 00a195aa02e4e2321d9437f964688f532cf0fde8..7e982acf3996f1087bb504a572f868f11494213c 100644 (file)
@@ -48,7 +48,9 @@
 #define MIC3LR_2_RADC_CTRL             18
 /* Line1 Input control registers */
 #define LINE1L_2_LADC_CTRL             19
+#define LINE1R_2_LADC_CTRL             21
 #define LINE1R_2_RADC_CTRL             22
+#define LINE1L_2_RADC_CTRL             24
 /* Line2 Input control registers */
 #define LINE2L_2_LADC_CTRL             20
 #define LINE2R_2_RADC_CTRL             23
@@ -79,6 +81,8 @@
 #define LINE2L_2_HPLOUT_VOL            45
 #define LINE2R_2_HPROUT_VOL            62
 #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 DACL1_2_HPLOUT_VOL             47
 #define DACR1_2_HPROUT_VOL             64
@@ -88,6 +92,8 @@
 #define LINE2L_2_HPLCOM_VOL            52
 #define LINE2R_2_HPRCOM_VOL            69
 #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 DACL1_2_HPLCOM_VOL             54
 #define DACR1_2_HPRCOM_VOL             71
 #define MONOLOPM_CTRL                  79
 /* Line Output Plus/Minus control registers */
 #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 PGAL_2_LLOPM_VOL               81
+#define PGAL_2_RLOPM_VOL               88
+#define PGAR_2_LLOPM_VOL               84
 #define PGAR_2_RLOPM_VOL               91
 #define DACL1_2_LLOPM_VOL              82
+#define DACL1_2_RLOPM_VOL              89
 #define DACR1_2_RLOPM_VOL              92
+#define DACR1_2_LLOPM_VOL              85
 #define LLOPM_CTRL                     86
 #define RLOPM_CTRL                     93
 /* GPIO/IRQ registers */
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
new file mode 100644 (file)
index 0000000..4136231
--- /dev/null
@@ -0,0 +1,826 @@
+/*
+ * 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)   */
+       0x24, /* REG_ANAMICL            (0x5)   */
+       0x04, /* REG_ANAMICR            (0x6)   */
+       0x0a, /* 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]);
+
+}
+
+/*
+ * 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, \
+               .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;
+}
+
+/*
+ * FGAIN volume control:
+ * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
+ */
+static DECLARE_TLV_DB_SCALE(master_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(master_coarse_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new twl4030_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("Master Playback Volume",
+                TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+               0, 0x3f, 0, master_tlv),
+       SOC_DOUBLE_R_TLV("Master PCM Playback Volume",
+                TWL4030_REG_ARXL2PGA, TWL4030_REG_ARXR2PGA,
+               6, 0x2, 0, master_coarse_tlv),
+       SOC_DOUBLE_R("Capture Volume",
+                TWL4030_REG_ATXL1PGA, TWL4030_REG_ATXR1PGA,
+               0, 0x1f, 0),
+};
+
+/* 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_DAC("DACL", "Left Playback", SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("DACR", "Right Playback", SND_SOC_NOPM, 0, 0),
+
+       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[] = {
+       /* outputs */
+       {"OUTL", NULL, "DACL"},
+       {"OUTR", NULL, "DACR"},
+
+       /* 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, hsgain;
+       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 output stage and gain setting */
+       hsgain = TWL4030_HSR_GAIN_0DB | TWL4030_HSL_GAIN_0DB;
+       twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hsgain);
+
+       /* 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, hsgain;
+
+       /* 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 output stage and gain setting */
+       hsgain = TWL4030_HSR_GAIN_PWR_DOWN | TWL4030_HSL_GAIN_PWR_DOWN;
+       twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hsgain);
+
+       /* 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_register_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);
+
+MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
+MODULE_AUTHOR("Steve Sakoman");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
new file mode 100644 (file)
index 0000000..09865d9
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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
+
+/* 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
+
+/* 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
+
+/* 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__ */
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
new file mode 100644 (file)
index 0000000..91f333c
--- /dev/null
@@ -0,0 +1,656 @@
+/*
+ * 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_register_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);
+
+MODULE_DESCRIPTION("UDA134X ALSA soc codec driver");
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h
new file mode 100644 (file)
index 0000000..94f4404
--- /dev/null
@@ -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
index a69ee72a7af553ca3f36d03d2c63288d97914232..330877c7069915ac0a74b657e33a8aaa986e328b 100644 (file)
@@ -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
  * 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_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,
-       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;
@@ -477,7 +479,8 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
        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_device *socdev = rtd->socdev;
@@ -560,8 +563,6 @@ struct snd_soc_dai uda1380_dai[] = {
                .hw_params = uda1380_pcm_hw_params,
                .shutdown = uda1380_pcm_shutdown,
                .prepare = uda1380_pcm_prepare,
-       },
-       .dai_ops = {
                .digital_mute = uda1380_mute,
                .set_fmt = uda1380_set_dai_fmt,
        },
@@ -579,8 +580,6 @@ struct snd_soc_dai uda1380_dai[] = {
                .hw_params = uda1380_pcm_hw_params,
                .shutdown = uda1380_pcm_shutdown,
                .prepare = uda1380_pcm_prepare,
-       },
-       .dai_ops = {
                .digital_mute = uda1380_mute,
                .set_fmt = uda1380_set_dai_fmt,
        },
@@ -598,8 +597,6 @@ struct snd_soc_dai uda1380_dai[] = {
                .hw_params = uda1380_pcm_hw_params,
                .shutdown = uda1380_pcm_shutdown,
                .prepare = uda1380_pcm_prepare,
-       },
-       .dai_ops = {
                .set_fmt = uda1380_set_dai_fmt,
        },
 },
index d8ca2da8d634e5e2a827339f3f3939d196d89941..173b66c0c766c35009c5247cd10576f1cf03c842 100644 (file)
@@ -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,
-       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;
@@ -585,8 +586,6 @@ struct snd_soc_dai wm8510_dai = {
                .formats = WM8510_FORMATS,},
        .ops = {
                .hw_params = wm8510_pcm_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = wm8510_mute,
                .set_fmt = wm8510_set_dai_fmt,
                .set_clkdiv = wm8510_set_dai_clkdiv,
index 627ebfb4209b72b786ad66f992b0659491f8f74d..220d4b68904a504eacf6a095d9097f69cfcd70ef 100644 (file)
@@ -548,13 +548,13 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
  * Set PCM DAI bit size and sample rate.
  */
 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_dai_link *dai = rtd->dai;
        struct snd_soc_device *socdev = rtd->socdev;
        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;
        /* bit size */
@@ -574,7 +574,7 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb);
+       wm8580_write(codec, WM8580_PAIF3 + dai->id, paifb);
        return 0;
 }
 
@@ -798,8 +798,6 @@ struct snd_soc_dai wm8580_dai[] = {
                },
                .ops = {
                         .hw_params = wm8580_paif_hw_params,
-                },
-               .dai_ops = {
                         .set_fmt = wm8580_set_paif_dai_fmt,
                         .set_clkdiv = wm8580_set_dai_clkdiv,
                         .set_pll = wm8580_set_dai_pll,
@@ -818,8 +816,6 @@ struct snd_soc_dai wm8580_dai[] = {
                },
                .ops = {
                         .hw_params = wm8580_paif_hw_params,
-                },
-               .dai_ops = {
                         .set_fmt = wm8580_set_paif_dai_fmt,
                         .set_clkdiv = wm8580_set_dai_clkdiv,
                         .set_pll = wm8580_set_dai_pll,
@@ -900,85 +896,85 @@ static struct snd_soc_device *wm8580_socdev;
  *    low  = 0x1a
  *    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 wm8580_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;
-
-       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(&i2c->dev, "failed to attach codec at addr %x\n", addr);
-               goto err;
-       }
-
        ret = wm8580_init(socdev);
-       if (ret < 0) {
+       if (ret < 0)
                dev_err(&i2c->dev, "failed to initialise WM8580\n");
-               goto err;
-       }
-
-       return ret;
-
-err:
-       kfree(codec);
-       kfree(i2c);
        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);
-       i2c_detach_client(client);
        kfree(codec->reg_cache);
-       kfree(client);
        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 = {
        .driver = {
                .name = "WM8580 I2C Codec",
                .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
 
 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 (setup->i2c_address) {
-               normal_i2c[0] = setup->i2c_address;
                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
                /* Add other interfaces here */
@@ -1034,6 +1027,7 @@ static int wm8580_remove(struct platform_device *pdev)
        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(&wm8580_i2c_driver);
 #endif
        kfree(codec->private_data);
index 589ddaba21d777515545cc8a5f4b70ce3b366eea..09e4422f6f2fbfeda01f4d068b5eb13532a69a84 100644 (file)
@@ -29,6 +29,7 @@
 #define WM8580_CLKSRC_NONE 5
 
 struct wm8580_setup_data {
+       int i2c_bus;
        unsigned short i2c_address;
 };
 
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
new file mode 100644 (file)
index 0000000..71949bd
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * 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_register_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);
+
+MODULE_DESCRIPTION("ASoC WM8728 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8728.h b/sound/soc/codecs/wm8728.h
new file mode 100644 (file)
index 0000000..d269c13
--- /dev/null
@@ -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
index 7f8a7e36b33e9124d0c3d924301d9cccb6cb9c5f..c0f277053bb235c5faa71eef35c145c9931d17d5 100644 (file)
@@ -264,7 +264,8 @@ static inline int get_coeff(int mclk, int rate)
 }
 
 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_device *socdev = rtd->socdev;
@@ -293,7 +294,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
        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_device *socdev = rtd->socdev;
@@ -305,7 +307,8 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream)
        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_device *socdev = rtd->socdev;
@@ -461,8 +464,6 @@ struct snd_soc_dai wm8731_dai = {
                .prepare = wm8731_pcm_prepare,
                .hw_params = wm8731_hw_params,
                .shutdown = wm8731_shutdown,
-       },
-       .dai_ops = {
                .digital_mute = wm8731_mute,
                .set_sysclk = wm8731_set_dai_sysclk,
                .set_fmt = wm8731_set_dai_fmt,
index 9b7296ee5b08fa541c6dfdbc44f9b5b8b31f92d6..860a1d56830aa93e864b577f89e2a54e724c2562 100644 (file)
@@ -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,
-       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;
@@ -709,8 +710,6 @@ struct snd_soc_dai wm8750_dai = {
                .formats = WM8750_FORMATS,},
        .ops = {
                .hw_params = wm8750_pcm_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = wm8750_mute,
                .set_fmt = wm8750_set_dai_fmt,
                .set_sysclk = wm8750_set_dai_sysclk,
index d426eaa2218575b75004a48f86cfe9079d84fa21..5e4cd3bb824ae711b7dc6bc4b1e4193a1976002c 100644 (file)
@@ -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.
  */
 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_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.
  */
 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_device *socdev = rtd->socdev;
@@ -1323,16 +1325,15 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
+               .formats = WM8753_FORMATS},
        .capture = { /* dummy for fast DAI switching */
                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
+               .formats = WM8753_FORMATS},
        .ops = {
-               .hw_params = wm8753_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_i2s_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode1h_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1356,8 +1357,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
        .ops = {
-               .hw_params = wm8753_pcm_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_pcm_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode1v_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1385,8 +1385,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
        .ops = {
-               .hw_params = wm8753_pcm_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_pcm_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode2_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1410,8 +1409,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
        .ops = {
-               .hw_params = wm8753_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_i2s_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode3_4_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
@@ -1439,8 +1437,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
        .ops = {
-               .hw_params = wm8753_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8753_i2s_hw_params,
                .digital_mute = wm8753_mute,
                .set_fmt = wm8753_mode3_4_set_dai_fmt,
                .set_clkdiv = wm8753_set_dai_clkdiv,
index 3b326c9b55866bba7a81c1421329026bed290247..d1326be91c8bbf0fe0c0c5a62a77c0ff92367826 100644 (file)
@@ -727,7 +727,8 @@ static int wm8900_add_widgets(struct snd_soc_codec *codec)
 }
 
 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_device *socdev = rtd->socdev;
@@ -1117,8 +1118,6 @@ struct snd_soc_dai wm8900_dai = {
         },
        .ops = {
                .hw_params = wm8900_hw_params,
-        },
-       .dai_ops = {
                 .set_clkdiv = wm8900_set_dai_clkdiv,
                 .set_pll = wm8900_set_dai_pll,
                 .set_fmt = wm8900_set_dai_fmt,
@@ -1387,89 +1386,86 @@ static struct snd_soc_device *wm8900_socdev;
 
 #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)
+static int wm8900_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
 {
        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);
-               goto err;
-       }
-
        ret = wm8900_init(socdev);
-       if (ret < 0) {
-               dev_err(&adap->dev, "failed to initialise WM8900\n");
-               goto err;
-       }
-       return ret;
-
-err:
-       kfree(codec);
-       kfree(i2c);
+       if (ret < 0)
+               dev_err(&i2c->dev, "failed to initialise WM8900\n");
        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);
        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 = {
        .driver = {
                .name = "WM8900 I2C codec",
                .owner = THIS_MODULE,
        },
-       .attach_adapter = wm8900_i2c_attach,
-       .detach_client =  wm8900_i2c_detach,
-       .command =        NULL,
+       .probe = wm8900_i2c_probe,
+       .remove = wm8900_i2c_remove,
+       .id_table = wm8900_i2c_id,
 };
 
-static struct i2c_client client_template = {
-       .name =   "WM8900",
-       .driver = &wm8900_i2c_driver,
-};
+static int wm8900_add_i2c_device(struct platform_device *pdev,
+                                const struct wm8900_setup_data *setup)
+{
+       struct i2c_board_info info;
+       struct i2c_adapter *adapter;
+       struct i2c_client *client;
+       int ret;
+
+       ret = i2c_add_driver(&wm8900_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, "wm8900", 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(&wm8900_i2c_driver);
+       return -ENODEV;
+}
 #endif
 
 static int wm8900_probe(struct platform_device *pdev)
@@ -1497,11 +1493,8 @@ static int wm8900_probe(struct platform_device *pdev)
        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");
+               ret = wm8900_add_i2c_device(pdev, setup);
        }
 #else
 #error Non-I2C interfaces not yet supported
@@ -1521,6 +1514,7 @@ static int wm8900_remove(struct platform_device *pdev)
        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(&wm8900_i2c_driver);
 #endif
        kfree(codec);
index ba450d99e9026c1114d90c402f3b3a6d37e13e0d..2249a446ad37533fd545250854f41546b5594dd3 100644 (file)
@@ -55,6 +55,7 @@
 #define WM8900_
 
 struct wm8900_setup_data {
+       int i2c_bus;
        unsigned short i2c_address;
 };
 
index ce40d78776058456782f5d723152250bfc79e681..78070b2cd48005b058be67b429682f9342368647 100644 (file)
@@ -379,8 +379,8 @@ static int wm8903_output_event(struct snd_soc_dapm_widget *w,
        struct wm8903_priv *wm8903 = codec->private_data;
        struct i2c_client *i2c = codec->control_data;
        u16 val;
-       u16 reg;
-       int shift;
+       u16 uninitialized_var(reg);
+       int uninitialized_var(shift);
        u16 cp_reg = wm8903_read(codec, WM8903_CHARGE_PUMP_0);
 
        switch (w->reg) {
@@ -1257,7 +1257,8 @@ static struct {
        { 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_device *socdev = rtd->socdev;
@@ -1298,7 +1299,8 @@ static int wm8903_startup(struct snd_pcm_substream *substream)
        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_device *socdev = rtd->socdev;
@@ -1317,7 +1319,8 @@ static void wm8903_shutdown(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_device *socdev = rtd->socdev;
@@ -1515,8 +1518,6 @@ struct snd_soc_dai wm8903_dai = {
                 .startup = wm8903_startup,
                 .shutdown = wm8903_shutdown,
                 .hw_params = wm8903_hw_params,
-       },
-       .dai_ops = {
                 .digital_mute = wm8903_digital_mute,
                 .set_fmt = wm8903_set_dai_fmt,
                 .set_sysclk = wm8903_set_dai_sysclk
index f41a578ddd4fabe908053cee4b273a43155b0e77..26edcc9d6e87fab47b88653cb73f24e7c057e306 100644 (file)
@@ -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,
-       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;
@@ -634,8 +635,6 @@ struct snd_soc_dai wm8971_dai = {
                .formats = WM8971_FORMATS,},
        .ops = {
                .hw_params = wm8971_pcm_hw_params,
-       },
-       .dai_ops = {
                .digital_mute = wm8971_mute,
                .set_fmt = wm8971_set_dai_fmt,
                .set_sysclk = wm8971_set_dai_sysclk,
index 572d22b0880b5b1f1033e244cee6269831cd9216..13926516d16e16a3f3a84391843c9317d61b6739 100644 (file)
@@ -106,6 +106,7 @@ static const u16 wm8990_reg[] = {
        0x0008,     /* R60 - PLL1 */
        0x0031,     /* R61 - PLL2 */
        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)
 {
        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;
 
        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.
  */
 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_device *socdev = rtd->socdev;
@@ -1222,8 +1223,14 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
        switch (level) {
        case SND_SOC_BIAS_ON:
                break;
+
        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;
+
        case SND_SOC_BIAS_STANDBY:
                if (codec->bias_level == SND_SOC_BIAS_OFF) {
                        /* 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 */
                        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;
 
        case SND_SOC_BIAS_OFF:
@@ -1349,8 +1363,7 @@ struct snd_soc_dai wm8990_dai = {
                .rates = WM8990_RATES,
                .formats = WM8990_FORMATS,},
        .ops = {
-               .hw_params = wm8990_hw_params,},
-       .dai_ops = {
+               .hw_params = wm8990_hw_params,
                .digital_mute = wm8990_mute,
                .set_fmt = wm8990_set_dai_fmt,
                .set_clkdiv = wm8990_set_dai_clkdiv,
index 0e192f3b07882a44cb7950486c3f8bacafa4a09f..7114ddc88b4ba5fb6c2dff17712b7b05c36ac39c 100644 (file)
@@ -80,8 +80,8 @@
 #define WM8990_PLL3                             0x3E
 #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.
index ffb471e420e2e76718f931420135c8b99cf21864..40f14061fb72cc2273d42c6987f3212aca1cccfb 100644 (file)
@@ -487,7 +487,8 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
        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_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);
 }
 
-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_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[] = {
 {
        .name = "AC97 HiFi",
-       .type = SND_SOC_DAI_AC97_BUS,
+       .ac97_control = 1,
        .playback = {
                .stream_name = "HiFi Playback",
                .channels_min = 1,
@@ -688,7 +690,7 @@ static int wm9712_soc_probe(struct platform_device *pdev)
 
        ret = wm9712_reset(codec, 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;
        }
 
index 945b32ed98840039a93289c108c27fd69656a927..9dad0bffcb0589d993c56b742ad582a59b46ffcd 100644 (file)
@@ -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,
-       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;
 
        switch (params_format(params)) {
@@ -954,11 +953,10 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
        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;
 
        /* 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);
 }
 
-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_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_device *socdev = rtd->socdev;
-       struct snd_soc_codec *codec = socdev->codec;
        int reg;
        u16 vra;
 
@@ -989,12 +986,11 @@ static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
        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_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_device *socdev = rtd->socdev;
-       struct snd_soc_codec *codec = socdev->codec;
        u16 vra, xsle;
 
        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[] = {
 {
        .name = "AC97 HiFi",
-       .type = SND_SOC_DAI_AC97_BUS,
+       .ac97_control = 1,
        .playback = {
                .stream_name = "HiFi Playback",
                .channels_min = 1,
@@ -1042,8 +1038,7 @@ struct snd_soc_dai wm9713_dai[] = {
                .rates = WM9713_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
        .ops = {
-               .prepare = ac97_hifi_prepare,},
-       .dai_ops = {
+               .prepare = ac97_hifi_prepare,
                .set_clkdiv = wm9713_set_dai_clkdiv,
                .set_pll = wm9713_set_dai_pll,},
        },
@@ -1056,8 +1051,7 @@ struct snd_soc_dai wm9713_dai[] = {
                .rates = WM9713_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
        .ops = {
-               .prepare = ac97_aux_prepare,},
-       .dai_ops = {
+               .prepare = ac97_aux_prepare,
                .set_clkdiv = wm9713_set_dai_clkdiv,
                .set_pll = wm9713_set_dai_pll,},
        },
@@ -1077,8 +1071,7 @@ struct snd_soc_dai wm9713_dai[] = {
                .formats = WM9713_PCM_FORMATS,},
        .ops = {
                .hw_params = wm9713_pcm_hw_params,
-               .shutdown = wm9713_voiceshutdown,},
-       .dai_ops = {
+               .shutdown = wm9713_voiceshutdown,
                .set_clkdiv = wm9713_set_dai_clkdiv,
                .set_pll = wm9713_set_dai_pll,
                .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);
+       if (soc_ac97_ops.warm_reset)
+               soc_ac97_ops.warm_reset(codec->ac97);
        if (ac97_read(codec, 0) != wm9713_reg[0])
                return -EIO;
        return 0;
@@ -1240,7 +1235,7 @@ static int wm9713_soc_probe(struct platform_device *pdev)
        wm9713_reset(codec, 0);
        ret = wm9713_reset(codec, 1);
        if (ret < 0) {
-               printk(KERN_ERR "AC97 link error\n");
+               printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
                goto reset_err;
        }
 
index 8f7e3383490246e81c66c4dc7b92849d768b804e..b502741692d6ba866047917046c04d267a3ac89f 100644 (file)
@@ -17,3 +17,13 @@ config SND_DAVINCI_SOC_EVM
        help
          Say Y if you want to add support for SoC audio on TI
          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.
index ca772e5b463727b1a7bb32d22e845fdf634b7d17..ca8bae1fc3f6ff00b062dd42094bd4c7035fe619 100644 (file)
@@ -7,5 +7,7 @@ obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
 
 # DAVINCI Machine Support
 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_SFFSDR) += snd-soc-sffsdr.o
index 9e6062cd6b59957f6df964470a52b8756f7f9398..2ce34d44b15c82229cb5ae38e4ed3754135a0562 100644 (file)
@@ -128,7 +128,7 @@ static struct snd_soc_dai_link evm_dai = {
 };
 
 /* 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",
        .dai_link = &evm_dai,
        .num_links = 1,
@@ -142,7 +142,7 @@ static struct aic3x_setup_data evm_aic3x_setup = {
 
 /* evm audio subsystem */
 static struct snd_soc_device evm_snd_devdata = {
-       .machine = &snd_soc_machine_evm,
+       .card = &snd_soc_card_evm,
        .platform = &davinci_soc_platform,
        .codec_dev = &soc_codec_dev_aic3x,
        .codec_data = &evm_aic3x_setup,
index abb5fedb0b1e0952ecb17fedb7592ab966c0ffda..cf31b3bb96cf500f859778e2127b737e0ba76910 100644 (file)
@@ -59,6 +59,7 @@
 #define DAVINCI_MCBSP_PCR_CLKXP                (1 << 1)
 #define DAVINCI_MCBSP_PCR_FSRP         (1 << 2)
 #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_CLKXM                (1 << 9)
 #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 davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_platform *platform = socdev->platform;
        u32 w;
+       int ret;
 
        /* Start the sample generator and enable transmitter/receiver */
        w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
        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);
-       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);
-       davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+       }
+
 
        /* Start frame sync */
        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);
 }
 
-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_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -171,6 +216,16 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG,
                                        DAVINCI_MCBSP_SRGR_FSGM);
                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. */
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG,
+                                       DAVINCI_MCBSP_PCR_SCLKME |
+                                       DAVINCI_MCBSP_PCR_FSXM |
+                                       DAVINCI_MCBSP_PCR_FSRM);
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG,
+                                       DAVINCI_MCBSP_SRGR_FSGM);
+               break;
        case SND_SOC_DAIFMT_CBM_CFM:
                davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, 0);
                break;
@@ -205,11 +260,34 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                return -EINVAL;
        }
 
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG,
+                                       DAVINCI_MCBSP_RCR_RFRLEN1(1) |
+                                       DAVINCI_MCBSP_RCR_RDATDLY(0));
+               davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG,
+                                       DAVINCI_MCBSP_XCR_XFRLEN1(1) |
+                                       DAVINCI_MCBSP_XCR_XDATDLY(0) |
+                                       DAVINCI_MCBSP_XCR_XFIG);
+               break;
+       case SND_SOC_DAIFMT_I2S:
+       default:
+               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);
+               break;
+       }
+
        return 0;
 }
 
 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 davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
@@ -219,17 +297,14 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
        u32 w;
 
        /* 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);
        w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SRGR_REG);
@@ -260,20 +335,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
                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;
 }
 
-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;
 
@@ -299,8 +378,8 @@ static int davinci_i2s_probe(struct platform_device *pdev,
                             struct snd_soc_dai *dai)
 {
        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 resource *mem, *ioarea;
        struct evm_snd_platform_data *pdata;
@@ -361,8 +440,8 @@ static void davinci_i2s_remove(struct platform_device *pdev,
                               struct snd_soc_dai *dai)
 {
        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 resource *mem;
 
@@ -381,7 +460,6 @@ static void davinci_i2s_remove(struct platform_device *pdev,
 struct snd_soc_dai davinci_i2s_dai = {
        .name = "davinci-i2s",
        .id = 0,
-       .type = SND_SOC_DAI_I2S,
        .probe = davinci_i2s_probe,
        .remove = davinci_i2s_remove,
        .playback = {
@@ -397,8 +475,7 @@ struct snd_soc_dai davinci_i2s_dai = {
        .ops = {
                .startup = davinci_i2s_startup,
                .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,
        },
 };
diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c
new file mode 100644 (file)
index 0000000..e95fde1
--- /dev/null
@@ -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",
+       .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,
+       .platform = &davinci_soc_platform,
+       .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");
index d2d3da9729f25f56d7563fefabe3f7d94b51f2fa..bf92331b4768184c85f4171ca73191ac41d56ed4 100644 (file)
@@ -284,7 +284,7 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
  * fsl_dma_new: initialize this PCM driver.
  *
  * 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.
  */
 static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
index 157a7895ffa1befbb2427b121f0bb493c0190b3d..52c290bb47bfc3207e1d158071a301669caf4e58 100644 (file)
@@ -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
  * 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 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
  * 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_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
  * 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 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.
  */
-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 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,
                .shutdown = fsl_ssi_shutdown,
                .trigger = fsl_ssi_trigger,
-       },
-       .dai_ops = {
                .set_sysclk = fsl_ssi_set_sysclk,
                .set_fmt = fsl_ssi_set_fmt,
        },
index 94a02eaa4825a0ef1494c0f8e3798c2daef69504..9ad8f9a2d8e90fba88875b169f33078bc7e20755 100644 (file)
@@ -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
  * 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 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,
-                                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 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;
 }
 
-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);
        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
  * 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 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.
  */
-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 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
  */
 static struct snd_soc_dai psc_i2s_dai_template = {
-       .type = SND_SOC_DAI_I2S,
        .playback = {
                .channels_min = 2,
                .channels_max = 2,
@@ -483,8 +487,6 @@ static struct snd_soc_dai psc_i2s_dai_template = {
                .hw_free = psc_i2s_hw_free,
                .shutdown = psc_i2s_shutdown,
                .trigger = psc_i2s_trigger,
-       },
-       .dai_ops = {
                .set_sysclk = psc_i2s_set_sysclk,
                .set_fmt = psc_i2s_set_fmt,
        },
index 94f89debde1fa463a043741b022040bad7971b3e..1cf4d6eeb5384e7f659fca54d87aee20ac55f7eb 100644 (file)
@@ -29,7 +29,7 @@
 struct mpc8610_hpcd_data {
        struct snd_soc_device sound_devdata;
        struct snd_soc_dai_link dai;
-       struct snd_soc_machine machine;
+       struct snd_soc_card machine;
        unsigned int dai_format;
        unsigned int codec_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
  */
-static struct snd_soc_machine mpc8610_hpcd_machine = {
+static struct snd_soc_card mpc8610_hpcd_machine = {
        .probe = mpc8610_hpcd_machine_probe,
        .remove = mpc8610_hpcd_machine_remove,
        .name = "MPC8610 HPCD",
@@ -465,7 +465,7 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
                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.platform = &fsl_soc_platform;
 
index 0382fdac51cd44d615ea0cae24ecff239eb4bcc7..53be6491320a944beed760873b766c5fb71f294e 100644 (file)
@@ -31,7 +31,7 @@ struct of_snd_soc_device {
        int id;
        struct list_head list;
        struct snd_soc_device device;
-       struct snd_soc_machine machine;
+       struct snd_soc_card card;
        struct snd_soc_dai_link dai_link;
        struct platform_device *pdev;
        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 */
        of_soc->codec_node = codec_node;
        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;
        list_add(&of_soc->list, &of_snd_soc_device_list);
 
@@ -159,7 +159,7 @@ int of_snd_soc_register_platform(struct snd_soc_platform *platform,
        of_soc->platform_node = node;
        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.name = of_soc->dai_link.cpu_dai->name;
 
        /* Now try to register the SoC device */
        of_snd_soc_register_device(of_soc);
index 8b7766b998d7de431bd30d18d32569019a116d28..9a869390abb91952eecdebd077dfd51784ee19fb 100644 (file)
@@ -21,3 +21,28 @@ config SND_OMAP_SOC_OSK5912
        select SND_SOC_TLV320AIC23
        help
          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.
index e09d1f297f644c856c16250dc7cdc407bd6f7e6f..29cf3a856c89da466f8771b5dde5fec1fa4d5ded 100644 (file)
@@ -8,6 +8,12 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
 # OMAP Machine Support
 snd-soc-n810-objs := n810.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
 
 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_OVERO) += snd-soc-overo.o
+obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
+obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
index fae3ad36e0bfc3e03606561c48c0c54bb7d192b6..18e2062e3a11815c9112221a040ba288eef26850 100644 (file)
@@ -70,9 +70,13 @@ static void n810_ext_control(struct snd_soc_codec *codec)
 
 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_codec *codec = rtd->socdev->codec;
 
+       snd_pcm_hw_constraint_minmax(runtime,
+                                    SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+
        n810_ext_control(codec);
        return clk_enable(sys_clkout2);
 }
@@ -282,7 +286,7 @@ static struct snd_soc_dai_link n810_dai = {
 };
 
 /* Audio machine driver */
-static struct snd_soc_machine snd_soc_machine_n810 = {
+static struct snd_soc_card snd_soc_n810 = {
        .name = "N810",
        .dai_link = &n810_dai,
        .num_links = 1,
@@ -298,7 +302,7 @@ static struct aic3x_setup_data n810_aic33_setup = {
 
 /* Audio subsystem */
 static struct snd_soc_device n810_snd_devdata = {
-       .machine = &snd_soc_machine_n810,
+       .card = &snd_soc_n810,
        .platform = &omap_soc_platform,
        .codec_dev = &soc_codec_dev_aic3x,
        .codec_data = &n810_aic33_setup,
index 8485a8a9d0ff0e89687eb73a62755d064c8177e6..e8f1314762d7dc18be19d09d632d671f3fe12402 100644 (file)
@@ -36,9 +36,7 @@
 #include "omap-mcbsp.h"
 #include "omap-pcm.h"
 
-#define OMAP_MCBSP_RATES       (SNDRV_PCM_RATE_44100 | \
-                                SNDRV_PCM_RATE_48000 | \
-                                SNDRV_PCM_RATE_KNOT)
+#define OMAP_MCBSP_RATES       (SNDRV_PCM_RATE_8000_96000)
 
 struct omap_mcbsp_data {
        unsigned int                    bus_id;
@@ -140,7 +138,8 @@ static const unsigned long omap34xx_mcbsp_port[][2] = {
 static const unsigned long omap34xx_mcbsp_port[][2] = {};
 #endif
 
-static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
+static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
+                                 struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -153,7 +152,8 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream)
        return err;
 }
 
-static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
+static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
+                                   struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -165,7 +165,8 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream)
        }
 }
 
-static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int omap_mcbsp_dai_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_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -194,14 +195,15 @@ static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd)
 }
 
 static int omap_mcbsp_dai_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_dai *cpu_dai = rtd->dai->cpu_dai;
        struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
        struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
        int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
-       int wlen;
+       int wlen, channels;
        unsigned long port;
 
        if (cpu_class_is_omap1()) {
@@ -230,12 +232,17 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
                return 0;
        }
 
-       switch (params_channels(params)) {
+       channels = params_channels(params);
+       switch (channels) {
        case 2:
-               /* Set 1 word per (McBPSP) frame and use dual-phase frames */
-               regs->rcr2      |= RFRLEN2(1 - 1) | RPHASE;
+               /* Use dual-phase frames */
+               regs->rcr2      |= RPHASE;
+               regs->xcr2      |= XPHASE;
+       case 1:
+               /* Set 1 word per (McBSP) frame */
+               regs->rcr2      |= RFRLEN2(1 - 1);
                regs->rcr1      |= RFRLEN1(1 - 1);
-               regs->xcr2      |= XFRLEN2(1 - 1) | XPHASE;
+               regs->xcr2      |= XFRLEN2(1 - 1);
                regs->xcr1      |= XFRLEN1(1 - 1);
                break;
        default:
@@ -264,8 +271,8 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
                regs->srgr1     |= FWID(wlen - 1);
                break;
        case SND_SOC_DAIFMT_DSP_A:
-               regs->srgr2     |= FPER(wlen * 2 - 1);
-               regs->srgr1     |= FWID(wlen * 2 - 2);
+               regs->srgr2     |= FPER(wlen * channels - 1);
+               regs->srgr1     |= FWID(wlen * channels - 2);
                break;
        }
 
@@ -452,17 +459,16 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 
 #define OMAP_MCBSP_DAI_BUILDER(link_id)                                \
 {                                                              \
-       .name = "omap-mcbsp-dai-(link_id)",                     \
+       .name = "omap-mcbsp-dai-"#link_id,                      \
        .id = (link_id),                                        \
-       .type = SND_SOC_DAI_I2S,                                \
        .playback = {                                           \
-               .channels_min = 2,                              \
+               .channels_min = 1,                              \
                .channels_max = 2,                              \
                .rates = OMAP_MCBSP_RATES,                      \
                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
        },                                                      \
        .capture = {                                            \
-               .channels_min = 2,                              \
+               .channels_min = 1,                              \
                .channels_max = 2,                              \
                .rates = OMAP_MCBSP_RATES,                      \
                .formats = SNDRV_PCM_FMTBIT_S16_LE,             \
@@ -472,8 +478,6 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
                .shutdown = omap_mcbsp_dai_shutdown,            \
                .trigger = omap_mcbsp_dai_trigger,              \
                .hw_params = omap_mcbsp_dai_hw_params,          \
-       },                                                      \
-       .dai_ops = {                                            \
                .set_fmt = omap_mcbsp_dai_set_dai_fmt,          \
                .set_clkdiv = omap_mcbsp_dai_set_clkdiv,        \
                .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,    \
diff --git a/sound/soc/omap/omap2evm.c b/sound/soc/omap/omap2evm.c
new file mode 100644 (file)
index 0000000..7b160f9
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * omap2evm.c  --  SoC audio machine driver for omap2evm board
+ *
+ * Author: Arun KS <arunks@mistralsolutions.com>
+ *
+ * Based on sound/soc/omap/overo.c by Steve Sakoman
+ *
+ * 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/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int omap2evm_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 *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops omap2evm_ops = {
+       .hw_params = omap2evm_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap2evm_dai = {
+       .name = "TWL4030",
+       .stream_name = "TWL4030",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &twl4030_dai,
+       .ops = &omap2evm_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap2evm = {
+       .name = "omap2evm",
+       .dai_link = &omap2evm_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap2evm_snd_devdata = {
+       .card = &snd_soc_omap2evm,
+       .platform = &omap_soc_platform,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap2evm_snd_device;
+
+static int __init omap2evm_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap2evm()) {
+               pr_debug("Not omap2evm!\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "omap2evm SoC init\n");
+
+       omap2evm_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!omap2evm_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(omap2evm_snd_device, &omap2evm_snd_devdata);
+       omap2evm_snd_devdata.dev = &omap2evm_snd_device->dev;
+       *(unsigned int *)omap2evm_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+       ret = platform_device_add(omap2evm_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(omap2evm_snd_device);
+
+       return ret;
+}
+module_init(omap2evm_soc_init);
+
+static void __exit omap2evm_soc_exit(void)
+{
+       platform_device_unregister(omap2evm_snd_device);
+}
+module_exit(omap2evm_soc_exit);
+
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_DESCRIPTION("ALSA SoC omap2evm");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c
new file mode 100644 (file)
index 0000000..3ed2546
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * omap3beagle.c  --  SoC audio for OMAP3 Beagle
+ *
+ * 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/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int omap3beagle_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;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops omap3beagle_ops = {
+       .hw_params = omap3beagle_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3beagle_dai = {
+       .name = "TWL4030",
+       .stream_name = "TWL4030",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &twl4030_dai,
+       .ops = &omap3beagle_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap3beagle = {
+       .name = "omap3beagle",
+       .dai_link = &omap3beagle_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap3beagle_snd_devdata = {
+       .card = &snd_soc_omap3beagle,
+       .platform = &omap_soc_platform,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap3beagle_snd_device;
+
+static int __init omap3beagle_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap3_beagle()) {
+               pr_debug("Not OMAP3 Beagle!\n");
+               return -ENODEV;
+       }
+       pr_info("OMAP3 Beagle SoC init\n");
+
+       omap3beagle_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!omap3beagle_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(omap3beagle_snd_device, &omap3beagle_snd_devdata);
+       omap3beagle_snd_devdata.dev = &omap3beagle_snd_device->dev;
+       *(unsigned int *)omap3beagle_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+       ret = platform_device_add(omap3beagle_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(omap3beagle_snd_device);
+
+       return ret;
+}
+
+static void __exit omap3beagle_soc_exit(void)
+{
+       platform_device_unregister(omap3beagle_snd_device);
+}
+
+module_init(omap3beagle_soc_init);
+module_exit(omap3beagle_soc_exit);
+
+MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 Beagle");
+MODULE_LICENSE("GPL");
index 0fe733796898149471013ba47db2d6394343921d..7a8f14d0c772201f905f1214fc0db35a24b9b3af 100644 (file)
@@ -143,7 +143,7 @@ static struct snd_soc_dai_link osk_dai = {
 };
 
 /* Audio machine driver */
-static struct snd_soc_machine snd_soc_machine_osk = {
+static struct snd_soc_card snd_soc_card_osk = {
        .name = "OSK5912",
        .dai_link = &osk_dai,
        .num_links = 1,
@@ -151,7 +151,7 @@ static struct snd_soc_machine snd_soc_machine_osk = {
 
 /* Audio subsystem */
 static struct snd_soc_device osk_snd_devdata = {
-       .machine = &snd_soc_machine_osk,
+       .card = &snd_soc_card_osk,
        .platform = &omap_soc_platform,
        .codec_dev = &soc_codec_dev_tlv320aic23,
 };
diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c
new file mode 100644 (file)
index 0000000..eea0c37
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * overo.c  --  SoC audio for Gumstix Overo
+ *
+ * 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/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int overo_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;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops overo_ops = {
+       .hw_params = overo_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link overo_dai = {
+       .name = "TWL4030",
+       .stream_name = "TWL4030",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &twl4030_dai,
+       .ops = &overo_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_card_overo = {
+       .name = "overo",
+       .dai_link = &overo_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device overo_snd_devdata = {
+       .card = &snd_soc_card_overo,
+       .platform = &omap_soc_platform,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *overo_snd_device;
+
+static int __init overo_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_overo()) {
+               pr_debug("Not Overo!\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "overo SoC init\n");
+
+       overo_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!overo_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(overo_snd_device, &overo_snd_devdata);
+       overo_snd_devdata.dev = &overo_snd_device->dev;
+       *(unsigned int *)overo_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+       ret = platform_device_add(overo_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(overo_snd_device);
+
+       return ret;
+}
+module_init(overo_soc_init);
+
+static void __exit overo_soc_exit(void)
+{
+       platform_device_unregister(overo_snd_device);
+}
+module_exit(overo_soc_exit);
+
+MODULE_AUTHOR("Steve Sakoman <steve@sakoman.com>");
+MODULE_DESCRIPTION("ALSA SoC overo");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
new file mode 100644 (file)
index 0000000..85fd160
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * sdp3430.c  --  SoC audio for TI OMAP3430 SDP
+ *
+ * Author: Misael Lopez Cruz <x0052729@ti.com>
+ *
+ * Based on:
+ * 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/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int sdp3430_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;
+       int ret;
+
+       /* Set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec DAI configuration\n");
+               return ret;
+       }
+
+       /* Set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai,
+                                 SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_NB_NF |
+                                 SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set cpu DAI configuration\n");
+               return ret;
+       }
+
+       /* Set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+                                           SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               printk(KERN_ERR "can't set codec system clock\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops sdp3430_ops = {
+       .hw_params = sdp3430_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link sdp3430_dai = {
+       .name = "TWL4030",
+       .stream_name = "TWL4030",
+       .cpu_dai = &omap_mcbsp_dai[0],
+       .codec_dai = &twl4030_dai,
+       .ops = &sdp3430_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_machine snd_soc_machine_sdp3430 = {
+       .name = "SDP3430",
+       .dai_link = &sdp3430_dai,
+       .num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device sdp3430_snd_devdata = {
+       .machine = &snd_soc_machine_sdp3430,
+       .platform = &omap_soc_platform,
+       .codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *sdp3430_snd_device;
+
+static int __init sdp3430_soc_init(void)
+{
+       int ret;
+
+       if (!machine_is_omap_3430sdp()) {
+               pr_debug("Not SDP3430!\n");
+               return -ENODEV;
+       }
+       printk(KERN_INFO "SDP3430 SoC init\n");
+
+       sdp3430_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!sdp3430_snd_device) {
+               printk(KERN_ERR "Platform device allocation failed\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(sdp3430_snd_device, &sdp3430_snd_devdata);
+       sdp3430_snd_devdata.dev = &sdp3430_snd_device->dev;
+       *(unsigned int *)sdp3430_dai.cpu_dai->private_data = 1; /* McBSP2 */
+
+       ret = platform_device_add(sdp3430_snd_device);
+       if (ret)
+               goto err1;
+
+       return 0;
+
+err1:
+       printk(KERN_ERR "Unable to add platform device\n");
+       platform_device_put(sdp3430_snd_device);
+
+       return ret;
+}
+module_init(sdp3430_soc_init);
+
+static void __exit sdp3430_soc_exit(void)
+{
+       platform_device_unregister(sdp3430_snd_device);
+}
+module_exit(sdp3430_soc_exit);
+
+MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC SDP3430");
+MODULE_LICENSE("GPL");
+
index f8c1cdd940acf54330c05fed9c69a3f4f7c7b0e6..f82e106994717da3c561515e0ba027f1ff32a965 100644 (file)
@@ -21,6 +21,9 @@ config SND_PXA2XX_SOC_AC97
 config SND_PXA2XX_SOC_I2S
        tristate
 
+config SND_PXA_SOC_SSP
+       tristate
+
 config SND_PXA2XX_SOC_CORGI
        tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
        depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx
@@ -75,3 +78,22 @@ config SND_PXA2XX_SOC_EM_X270
        help
          Say Y if you want to add support for SoC audio on
          CompuLab EM-x270.
+
+config SND_PXA2XX_SOC_PALM27X
+       bool "SoC Audio support for Palm T|X, T5 and LifeDrive"
+       depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || MACH_PALMT5)
+       select SND_PXA2XX_SOC_AC97
+       select SND_SOC_WM9712
+       help
+         Say Y if you want to add support for SoC audio on
+         Palm T|X, T5 or LifeDrive handheld computer.
+
+config SND_SOC_ZYLONITE
+       tristate "SoC Audio support for Marvell Zylonite"
+       depends on SND_PXA2XX_SOC && MACH_ZYLONITE
+       select SND_PXA2XX_SOC_AC97
+       select SND_PXA_SOC_SSP
+       select SND_SOC_WM9713
+       help
+         Say Y if you want to add support for SoC audio on the
+         Marvell Zylonite reference platform.
index 5bc8edf9dca9ec8532ec6e56e1c7e7ea88f463fc..08a9f279772991f76adbe8f307aee987b8f8351c 100644 (file)
@@ -2,10 +2,12 @@
 snd-soc-pxa2xx-objs := pxa2xx-pcm.o
 snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
 snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
+snd-soc-pxa-ssp-objs := pxa-ssp.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
 obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
 obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
+obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o
 
 # PXA Machine Support
 snd-soc-corgi-objs := corgi.o
@@ -14,6 +16,8 @@ snd-soc-tosa-objs := tosa.o
 snd-soc-e800-objs := e800_wm9712.o
 snd-soc-spitz-objs := spitz.o
 snd-soc-em-x270-objs := em-x270.o
+snd-soc-palm27x-objs := palm27x.o
+snd-soc-zylonite-objs := zylonite.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
@@ -21,3 +25,5 @@ obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
 obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
 obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
 obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
+obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
+obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
index 2718eaf7895f39d4db2e5d93e4227400b995b1a7..647f056a3cb33749e7572dbf666d78a1e4df84c0 100644 (file)
@@ -314,7 +314,7 @@ static struct snd_soc_dai_link corgi_dai = {
 };
 
 /* corgi audio machine driver */
-static struct snd_soc_machine snd_soc_machine_corgi = {
+static struct snd_soc_card snd_soc_corgi = {
        .name = "Corgi",
        .dai_link = &corgi_dai,
        .num_links = 1,
@@ -328,7 +328,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = {
 
 /* corgi audio subsystem */
 static struct snd_soc_device corgi_snd_devdata = {
-       .machine = &snd_soc_machine_corgi,
+       .card = &snd_soc_corgi,
        .platform = &pxa2xx_soc_platform,
        .codec_dev = &soc_codec_dev_wm8731,
        .codec_data = &corgi_wm8731_setup,
index 6781c5be242fb75e3716eaa5bdbc3de3baf4c336..60c64861512a813f481f62f38d990d6dc70d2d7d 100644 (file)
@@ -29,7 +29,7 @@
 #include "pxa2xx-pcm.h"
 #include "pxa2xx-ac97.h"
 
-static struct snd_soc_machine e800;
+static struct snd_soc_card e800;
 
 static struct snd_soc_dai_link e800_dai[] = {
 {
@@ -40,14 +40,14 @@ static struct snd_soc_dai_link e800_dai[] = {
 },
 };
 
-static struct snd_soc_machine e800 = {
+static struct snd_soc_card e800 = {
        .name = "Toshiba e800",
        .dai_link = e800_dai,
        .num_links = ARRAY_SIZE(e800_dai),
 };
 
 static struct snd_soc_device e800_snd_devdata = {
-       .machine = &e800,
+       .card = &e800,
        .platform = &pxa2xx_soc_platform,
        .codec_dev = &soc_codec_dev_wm9712,
 };
index e6ff6929ab4b799f7b9875144681469c15e6d8fe..d6884b755d552eaf0fd5a5bb79b3c844609ca1e7 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/moduleparam.h>
 #include <linux/device.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -53,14 +52,14 @@ static struct snd_soc_dai_link em_x270_dai[] = {
        },
 };
 
-static struct snd_soc_machine em_x270 = {
+static struct snd_soc_card em_x270 = {
        .name = "EM-X270",
        .dai_link = em_x270_dai,
        .num_links = ARRAY_SIZE(em_x270_dai),
 };
 
 static struct snd_soc_device em_x270_snd_devdata = {
-       .machine = &em_x270,
+       .card = &em_x270,
        .platform = &pxa2xx_soc_platform,
        .codec_dev = &soc_codec_dev_wm9712,
 };
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
new file mode 100644 (file)
index 0000000..3bb8879
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * linux/sound/soc/pxa/palm27x.c
+ *
+ * SoC Audio driver for Palm T|X, T5 and LifeDrive
+ *
+ * based on tosa.c
+ *
+ * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.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/device.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/audio.h>
+#include <mach/palmasoc.h>
+
+#include "../codecs/wm9712.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+
+static int palm27x_jack_func = 1;
+static int palm27x_spk_func = 1;
+static int palm27x_ep_gpio = -1;
+
+static void palm27x_ext_control(struct snd_soc_codec *codec)
+{
+       if (!palm27x_spk_func)
+               snd_soc_dapm_enable_pin(codec, "Speaker");
+       else
+               snd_soc_dapm_disable_pin(codec, "Speaker");
+
+       if (!palm27x_jack_func)
+               snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+       else
+               snd_soc_dapm_disable_pin(codec, "Headphone Jack");
+
+       snd_soc_dapm_sync(codec);
+}
+
+static int palm27x_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->socdev->codec;
+
+       /* check the jack status at stream startup */
+       palm27x_ext_control(codec);
+       return 0;
+}
+
+static struct snd_soc_ops palm27x_ops = {
+       .startup = palm27x_startup,
+};
+
+static irqreturn_t palm27x_interrupt(int irq, void *v)
+{
+       palm27x_spk_func = gpio_get_value(palm27x_ep_gpio);
+       palm27x_jack_func = !palm27x_spk_func;
+       return IRQ_HANDLED;
+}
+
+static int palm27x_get_jack(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = palm27x_jack_func;
+       return 0;
+}
+
+static int palm27x_set_jack(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+       if (palm27x_jack_func == ucontrol->value.integer.value[0])
+               return 0;
+
+       palm27x_jack_func = ucontrol->value.integer.value[0];
+       palm27x_ext_control(codec);
+       return 1;
+}
+
+static int palm27x_get_spk(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.integer.value[0] = palm27x_spk_func;
+       return 0;
+}
+
+static int palm27x_set_spk(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+       if (palm27x_spk_func == ucontrol->value.integer.value[0])
+               return 0;
+
+       palm27x_spk_func = ucontrol->value.integer.value[0];
+       palm27x_ext_control(codec);
+       return 1;
+}
+
+/* PalmTX machine dapm widgets */
+static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+/* PalmTX audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* headphone connected to HPOUTL, HPOUTR */
+       {"Headphone Jack", NULL, "HPOUTL"},
+       {"Headphone Jack", NULL, "HPOUTR"},
+
+       /* ext speaker connected to ROUT2, LOUT2 */
+       {"Speaker", NULL, "LOUT2"},
+       {"Speaker", NULL, "ROUT2"},
+};
+
+static const char *jack_function[] = {"Headphone", "Off"};
+static const char *spk_function[] = {"On", "Off"};
+static const struct soc_enum palm27x_enum[] = {
+       SOC_ENUM_SINGLE_EXT(2, jack_function),
+       SOC_ENUM_SINGLE_EXT(2, spk_function),
+};
+
+static const struct snd_kcontrol_new palm27x_controls[] = {
+       SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack,
+               palm27x_set_jack),
+       SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk,
+               palm27x_set_spk),
+};
+
+static int palm27x_ac97_init(struct snd_soc_codec *codec)
+{
+       int i, err;
+
+       snd_soc_dapm_nc_pin(codec, "OUT3");
+       snd_soc_dapm_nc_pin(codec, "MONOOUT");
+
+       /* add palm27x specific controls */
+       for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) {
+               err = snd_ctl_add(codec->card,
+                               snd_soc_cnew(&palm27x_controls[i],
+                                               codec, NULL));
+               if (err < 0)
+                       return err;
+       }
+
+       /* add palm27x specific widgets */
+       snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets,
+                               ARRAY_SIZE(palm27x_dapm_widgets));
+
+       /* set up palm27x specific audio path audio_map */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_sync(codec);
+       return 0;
+}
+
+static struct snd_soc_dai_link palm27x_dai[] = {
+{
+       .name = "AC97 HiFi",
+       .stream_name = "AC97 HiFi",
+       .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+       .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+       .init = palm27x_ac97_init,
+       .ops = &palm27x_ops,
+},
+{
+       .name = "AC97 Aux",
+       .stream_name = "AC97 Aux",
+       .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+       .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+       .ops = &palm27x_ops,
+},
+};
+
+static struct snd_soc_card palm27x_asoc = {
+       .name = "Palm/PXA27x",
+       .dai_link = palm27x_dai,
+       .num_links = ARRAY_SIZE(palm27x_dai),
+};
+
+static struct snd_soc_device palm27x_snd_devdata = {
+       .card = &palm27x_asoc,
+       .platform = &pxa2xx_soc_platform,
+       .codec_dev = &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *palm27x_snd_device;
+
+static int __init palm27x_asoc_init(void)
+{
+       int ret;
+
+       if (!(machine_is_palmtx() || machine_is_palmt5() ||
+               machine_is_palmld()))
+               return -ENODEV;
+
+       ret = gpio_request(palm27x_ep_gpio, "Headphone Jack");
+       if (ret)
+               return ret;
+       ret = gpio_direction_input(palm27x_ep_gpio);
+       if (ret)
+               goto err_alloc;
+
+       if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt,
+                       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                       "Headphone jack", NULL))
+               goto err_alloc;
+
+       palm27x_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!palm27x_snd_device) {
+               ret = -ENOMEM;
+               goto err_dev;
+       }
+
+       platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata);
+       palm27x_snd_devdata.dev = &palm27x_snd_device->dev;
+       ret = platform_device_add(palm27x_snd_device);
+
+       if (ret != 0)
+               goto put_device;
+
+       return 0;
+
+put_device:
+       platform_device_put(palm27x_snd_device);
+err_dev:
+       free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
+err_alloc:
+       gpio_free(palm27x_ep_gpio);
+
+       return ret;
+}
+
+static void __exit palm27x_asoc_exit(void)
+{
+       free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
+       gpio_free(palm27x_ep_gpio);
+       platform_device_unregister(palm27x_snd_device);
+}
+
+void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data)
+{
+       palm27x_ep_gpio = data->jack_gpio;
+}
+
+module_init(palm27x_asoc_init);
+module_exit(palm27x_asoc_exit);
+
+/* Module information */
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive");
+MODULE_LICENSE("GPL");
index 4d9930c52789989313a8a1189bbc99b26160d8b2..03b510ab282446ba672d24da909ffbc83d5c8435 100644 (file)
@@ -276,7 +276,7 @@ static struct snd_soc_dai_link poodle_dai = {
 };
 
 /* poodle audio machine driver */
-static struct snd_soc_machine snd_soc_machine_poodle = {
+static struct snd_soc_card snd_soc_poodle = {
        .name = "Poodle",
        .dai_link = &poodle_dai,
        .num_links = 1,
@@ -290,7 +290,7 @@ static struct wm8731_setup_data poodle_wm8731_setup = {
 
 /* poodle audio subsystem */
 static struct snd_soc_device poodle_snd_devdata = {
-       .machine = &snd_soc_machine_poodle,
+       .card = &snd_soc_poodle,
        .platform = &pxa2xx_soc_platform,
        .codec_dev = &soc_codec_dev_wm8731,
        .codec_data = &poodle_wm8731_setup,
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
new file mode 100644 (file)
index 0000000..402fc5b
--- /dev/null
@@ -0,0 +1,921 @@
+#define DEBUG
+/*
+ * pxa-ssp.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2005,2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         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 as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ * TODO:
+ *  o Test network mode for > 16bit sample size
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/regs-ssp.h>
+#include <mach/audio.h>
+#include <mach/ssp.h>
+
+#include "pxa2xx-pcm.h"
+#include "pxa-ssp.h"
+
+/*
+ * SSP audio private data
+ */
+struct ssp_priv {
+       struct ssp_dev dev;
+       unsigned int sysclk;
+       int dai_fmt;
+#ifdef CONFIG_PM
+       struct ssp_state state;
+#endif
+};
+
+#define PXA2xx_SSP1_BASE       0x41000000
+#define PXA27x_SSP2_BASE       0x41700000
+#define PXA27x_SSP3_BASE       0x41900000
+#define PXA3xx_SSP4_BASE       0x41a00000
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_out = {
+       .name                   = "SSP1 PCM Mono out",
+       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
+       .drcmr                  = &DRCMR(14),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_in = {
+       .name                   = "SSP1 PCM Mono in",
+       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
+       .drcmr                  = &DRCMR(13),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_out = {
+       .name                   = "SSP1 PCM Stereo out",
+       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
+       .drcmr                  = &DRCMR(14),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_in = {
+       .name                   = "SSP1 PCM Stereo in",
+       .dev_addr               = PXA2xx_SSP1_BASE + SSDR,
+       .drcmr                  = &DRCMR(13),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_out = {
+       .name                   = "SSP2 PCM Mono out",
+       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
+       .drcmr                  = &DRCMR(16),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_in = {
+       .name                   = "SSP2 PCM Mono in",
+       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
+       .drcmr                  = &DRCMR(15),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_out = {
+       .name                   = "SSP2 PCM Stereo out",
+       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
+       .drcmr                  = &DRCMR(16),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_in = {
+       .name                   = "SSP2 PCM Stereo in",
+       .dev_addr               = PXA27x_SSP2_BASE + SSDR,
+       .drcmr                  = &DRCMR(15),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_out = {
+       .name                   = "SSP3 PCM Mono out",
+       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
+       .drcmr                  = &DRCMR(67),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_in = {
+       .name                   = "SSP3 PCM Mono in",
+       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
+       .drcmr                  = &DRCMR(66),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_out = {
+       .name                   = "SSP3 PCM Stereo out",
+       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
+       .drcmr                  = &DRCMR(67),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_in = {
+       .name                   = "SSP3 PCM Stereo in",
+       .dev_addr               = PXA27x_SSP3_BASE + SSDR,
+       .drcmr                  = &DRCMR(66),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_out = {
+       .name                   = "SSP4 PCM Mono out",
+       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
+       .drcmr                  = &DRCMR(67),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_in = {
+       .name                   = "SSP4 PCM Mono in",
+       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
+       .drcmr                  = &DRCMR(66),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_out = {
+       .name                   = "SSP4 PCM Stereo out",
+       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
+       .drcmr                  = &DRCMR(67),
+       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_in = {
+       .name                   = "SSP4 PCM Stereo in",
+       .dev_addr               = PXA3xx_SSP4_BASE + SSDR,
+       .drcmr                  = &DRCMR(66),
+       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
+                                 DCMD_BURST16 | DCMD_WIDTH4,
+};
+
+static void dump_registers(struct ssp_device *ssp)
+{
+       dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
+                ssp_read_reg(ssp, SSCR0), ssp_read_reg(ssp, SSCR1),
+                ssp_read_reg(ssp, SSTO));
+
+       dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n",
+                ssp_read_reg(ssp, SSPSP), ssp_read_reg(ssp, SSSR),
+                ssp_read_reg(ssp, SSACD));
+}
+
+static struct pxa2xx_pcm_dma_params *ssp_dma_params[4][4] = {
+       {
+               &pxa_ssp1_pcm_mono_out, &pxa_ssp1_pcm_mono_in,
+               &pxa_ssp1_pcm_stereo_out, &pxa_ssp1_pcm_stereo_in,
+       },
+       {
+               &pxa_ssp2_pcm_mono_out, &pxa_ssp2_pcm_mono_in,
+               &pxa_ssp2_pcm_stereo_out, &pxa_ssp2_pcm_stereo_in,
+       },
+       {
+               &pxa_ssp3_pcm_mono_out, &pxa_ssp3_pcm_mono_in,
+               &pxa_ssp3_pcm_stereo_out, &pxa_ssp3_pcm_stereo_in,
+       },
+       {
+               &pxa_ssp4_pcm_mono_out, &pxa_ssp4_pcm_mono_in,
+               &pxa_ssp4_pcm_stereo_out, &pxa_ssp4_pcm_stereo_in,
+       },
+};
+
+static int pxa_ssp_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct ssp_priv *priv = cpu_dai->private_data;
+       int ret = 0;
+
+       if (!cpu_dai->active) {
+               ret = ssp_init(&priv->dev, cpu_dai->id + 1, SSP_NO_IRQ);
+               if (ret < 0)
+                       return ret;
+               ssp_disable(&priv->dev);
+       }
+       return ret;
+}
+
+static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct ssp_priv *priv = cpu_dai->private_data;
+
+       if (!cpu_dai->active) {
+               ssp_disable(&priv->dev);
+               ssp_exit(&priv->dev);
+       }
+}
+
+#ifdef CONFIG_PM
+
+static int pxa_ssp_suspend(struct platform_device *pdev,
+       struct snd_soc_dai *cpu_dai)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+
+       if (!cpu_dai->active)
+               return 0;
+
+       ssp_save_state(&priv->dev, &priv->state);
+       clk_disable(priv->dev.ssp->clk);
+       return 0;
+}
+
+static int pxa_ssp_resume(struct platform_device *pdev,
+       struct snd_soc_dai *cpu_dai)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+
+       if (!cpu_dai->active)
+               return 0;
+
+       clk_enable(priv->dev.ssp->clk);
+       ssp_restore_state(&priv->dev, &priv->state);
+       ssp_enable(&priv->dev);
+
+       return 0;
+}
+
+#else
+#define pxa_ssp_suspend        NULL
+#define pxa_ssp_resume NULL
+#endif
+
+/**
+ * ssp_set_clkdiv - set SSP clock divider
+ * @div: serial clock rate divider
+ */
+static void ssp_set_scr(struct ssp_dev *dev, u32 div)
+{
+       struct ssp_device *ssp = dev->ssp;
+       u32 sscr0 = ssp_read_reg(dev->ssp, SSCR0) & ~SSCR0_SCR;
+
+       ssp_write_reg(ssp, SSCR0, (sscr0 | SSCR0_SerClkDiv(div)));
+}
+
+/*
+ * Set the SSP ports SYSCLK.
+ */
+static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+       int clk_id, unsigned int freq, int dir)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       int val;
+
+       u32 sscr0 = ssp_read_reg(ssp, SSCR0) &
+               ~(SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
+
+       dev_dbg(&ssp->pdev->dev,
+               "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n",
+               cpu_dai->id, clk_id, freq);
+
+       switch (clk_id) {
+       case PXA_SSP_CLK_NET_PLL:
+               sscr0 |= SSCR0_MOD;
+               break;
+       case PXA_SSP_CLK_PLL:
+               /* Internal PLL is fixed */
+               if (cpu_is_pxa25x())
+                       priv->sysclk = 1843200;
+               else
+                       priv->sysclk = 13000000;
+               break;
+       case PXA_SSP_CLK_EXT:
+               priv->sysclk = freq;
+               sscr0 |= SSCR0_ECS;
+               break;
+       case PXA_SSP_CLK_NET:
+               priv->sysclk = freq;
+               sscr0 |= SSCR0_NCS | SSCR0_MOD;
+               break;
+       case PXA_SSP_CLK_AUDIO:
+               priv->sysclk = 0;
+               ssp_set_scr(&priv->dev, 1);
+               sscr0 |= SSCR0_ADC;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       /* The SSP clock must be disabled when changing SSP clock mode
+        * on PXA2xx.  On PXA3xx it must be enabled when doing so. */
+       if (!cpu_is_pxa3xx())
+               clk_disable(priv->dev.ssp->clk);
+       val = ssp_read_reg(ssp, SSCR0) | sscr0;
+       ssp_write_reg(ssp, SSCR0, val);
+       if (!cpu_is_pxa3xx())
+               clk_enable(priv->dev.ssp->clk);
+
+       return 0;
+}
+
+/*
+ * Set the SSP clock dividers.
+ */
+static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+       int div_id, int div)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       int val;
+
+       switch (div_id) {
+       case PXA_SSP_AUDIO_DIV_ACDS:
+               val = (ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div);
+               ssp_write_reg(ssp, SSACD, val);
+               break;
+       case PXA_SSP_AUDIO_DIV_SCDB:
+               val = ssp_read_reg(ssp, SSACD);
+               val &= ~SSACD_SCDB;
+#if defined(CONFIG_PXA3xx)
+               if (cpu_is_pxa3xx())
+                       val &= ~SSACD_SCDX8;
+#endif
+               switch (div) {
+               case PXA_SSP_CLK_SCDB_1:
+                       val |= SSACD_SCDB;
+                       break;
+               case PXA_SSP_CLK_SCDB_4:
+                       break;
+#if defined(CONFIG_PXA3xx)
+               case PXA_SSP_CLK_SCDB_8:
+                       if (cpu_is_pxa3xx())
+                               val |= SSACD_SCDX8;
+                       else
+                               return -EINVAL;
+                       break;
+#endif
+               default:
+                       return -EINVAL;
+               }
+               ssp_write_reg(ssp, SSACD, val);
+               break;
+       case PXA_SSP_DIV_SCR:
+               ssp_set_scr(&priv->dev, div);
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/*
+ * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
+ */
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
+       int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       u32 ssacd = ssp_read_reg(ssp, SSACD) & ~0x70;
+
+#if defined(CONFIG_PXA3xx)
+       if (cpu_is_pxa3xx())
+               ssp_write_reg(ssp, SSACDD, 0);
+#endif
+
+       switch (freq_out) {
+       case 5622000:
+               break;
+       case 11345000:
+               ssacd |= (0x1 << 4);
+               break;
+       case 12235000:
+               ssacd |= (0x2 << 4);
+               break;
+       case 14857000:
+               ssacd |= (0x3 << 4);
+               break;
+       case 32842000:
+               ssacd |= (0x4 << 4);
+               break;
+       case 48000000:
+               ssacd |= (0x5 << 4);
+               break;
+       case 0:
+               /* Disable */
+               break;
+
+       default:
+#ifdef CONFIG_PXA3xx
+               /* PXA3xx has a clock ditherer which can be used to generate
+                * a wider range of frequencies - calculate a value for it.
+                */
+               if (cpu_is_pxa3xx()) {
+                       u32 val;
+                       u64 tmp = 19968;
+                       tmp *= 1000000;
+                       do_div(tmp, freq_out);
+                       val = tmp;
+
+                       val = (val << 16) | 64;;
+                       ssp_write_reg(ssp, SSACDD, val);
+
+                       ssacd |= (0x6 << 4);
+
+                       dev_dbg(&ssp->pdev->dev,
+                               "Using SSACDD %x to supply %dHz\n",
+                               val, freq_out);
+                       break;
+               }
+#endif
+
+               return -EINVAL;
+       }
+
+       ssp_write_reg(ssp, SSACD, ssacd);
+
+       return 0;
+}
+
+/*
+ * Set the active slots in TDM/Network mode
+ */
+static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+       unsigned int mask, int slots)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       u32 sscr0;
+
+       sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SlotsPerFrm(7);
+
+       /* set number of active slots */
+       sscr0 |= SSCR0_SlotsPerFrm(slots);
+       ssp_write_reg(ssp, SSCR0, sscr0);
+
+       /* set active slot mask */
+       ssp_write_reg(ssp, SSTSA, mask);
+       ssp_write_reg(ssp, SSRSA, mask);
+       return 0;
+}
+
+/*
+ * Tristate the SSP DAI lines
+ */
+static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai,
+       int tristate)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       u32 sscr1;
+
+       sscr1 = ssp_read_reg(ssp, SSCR1);
+       if (tristate)
+               sscr1 &= ~SSCR1_TTE;
+       else
+               sscr1 |= SSCR1_TTE;
+       ssp_write_reg(ssp, SSCR1, sscr1);
+
+       return 0;
+}
+
+/*
+ * Set up the SSP DAI format.
+ * The SSP Port must be inactive before calling this function as the
+ * physical interface format is changed.
+ */
+static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+               unsigned int fmt)
+{
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       u32 sscr0;
+       u32 sscr1;
+       u32 sspsp;
+
+       /* reset port settings */
+       sscr0 = ssp_read_reg(ssp, SSCR0) &
+               (SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
+       sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7);
+       sspsp = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               sscr1 |= SSCR1_SCLKDIR;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ssp_write_reg(ssp, SSCR0, sscr0);
+       ssp_write_reg(ssp, SSCR1, sscr1);
+       ssp_write_reg(ssp, SSPSP, sspsp);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               sscr0 |= SSCR0_MOD | SSCR0_PSP;
+               sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       sspsp |= SSPSP_FSRT;
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       sspsp |= SSPSP_SFRMP | SSPSP_FSRT;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       sspsp |= SSPSP_SFRMP;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       case SND_SOC_DAIFMT_DSP_A:
+               sspsp |= SSPSP_FSRT;
+       case SND_SOC_DAIFMT_DSP_B:
+               sscr0 |= SSCR0_MOD | SSCR0_PSP;
+               sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       sspsp |= SSPSP_SFRMP;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       ssp_write_reg(ssp, SSCR0, sscr0);
+       ssp_write_reg(ssp, SSCR1, sscr1);
+       ssp_write_reg(ssp, SSPSP, sspsp);
+
+       dump_registers(ssp);
+
+       /* Since we are configuring the timings for the format by hand
+        * we have to defer some things until hw_params() where we
+        * know parameters like the sample size.
+        */
+       priv->dai_fmt = fmt;
+
+       return 0;
+}
+
+/*
+ * Set the SSP audio DMA parameters and sample size.
+ * Can be called multiple times by oss emulation.
+ */
+static int pxa_ssp_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;
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       int dma = 0, chn = params_channels(params);
+       u32 sscr0;
+       u32 sspsp;
+       int width = snd_pcm_format_physical_width(params_format(params));
+
+       /* select correct DMA params */
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               dma = 1; /* capture DMA offset is 1,3 */
+       if (chn == 2)
+               dma += 2; /* stereo DMA offset is 2, mono is 0 */
+       cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
+
+       dev_dbg(&ssp->pdev->dev, "pxa_ssp_hw_params: dma %d\n", dma);
+
+       /* we can only change the settings if the port is not in use */
+       if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
+               return 0;
+
+       /* clear selected SSP bits */
+       sscr0 = ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS);
+       ssp_write_reg(ssp, SSCR0, sscr0);
+
+       /* bit size */
+       sscr0 = ssp_read_reg(ssp, SSCR0);
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+#ifdef CONFIG_PXA3xx
+               if (cpu_is_pxa3xx())
+                       sscr0 |= SSCR0_FPCKE;
+#endif
+               sscr0 |= SSCR0_DataSize(16);
+               if (params_channels(params) > 1)
+                       sscr0 |= SSCR0_EDSS;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8));
+               /* we must be in network mode (2 slots) for 24 bit stereo */
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16));
+               /* we must be in network mode (2 slots) for 32 bit stereo */
+               break;
+       }
+       ssp_write_reg(ssp, SSCR0, sscr0);
+
+       switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               /* Cleared when the DAI format is set */
+               sspsp = ssp_read_reg(ssp, SSPSP) | SSPSP_SFRMWDTH(width);
+               ssp_write_reg(ssp, SSPSP, sspsp);
+               break;
+       default:
+               break;
+       }
+
+       /* We always use a network mode so we always require TDM slots
+        * - complain loudly and fail if they've not been set up yet.
+        */
+       if (!(ssp_read_reg(ssp, SSTSA) & 0xf)) {
+               dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n");
+               return -EINVAL;
+       }
+
+       dump_registers(ssp);
+
+       return 0;
+}
+
+static int pxa_ssp_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_dai *cpu_dai = rtd->dai->cpu_dai;
+       int ret = 0;
+       struct ssp_priv *priv = cpu_dai->private_data;
+       struct ssp_device *ssp = priv->dev.ssp;
+       int val;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_RESUME:
+               ssp_enable(&priv->dev);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               val = ssp_read_reg(ssp, SSCR1);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       val |= SSCR1_TSRE;
+               else
+                       val |= SSCR1_RSRE;
+               ssp_write_reg(ssp, SSCR1, val);
+               val = ssp_read_reg(ssp, SSSR);
+               ssp_write_reg(ssp, SSSR, val);
+               break;
+       case SNDRV_PCM_TRIGGER_START:
+               val = ssp_read_reg(ssp, SSCR1);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       val |= SSCR1_TSRE;
+               else
+                       val |= SSCR1_RSRE;
+               ssp_write_reg(ssp, SSCR1, val);
+               ssp_enable(&priv->dev);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               val = ssp_read_reg(ssp, SSCR1);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       val &= ~SSCR1_TSRE;
+               else
+                       val &= ~SSCR1_RSRE;
+               ssp_write_reg(ssp, SSCR1, val);
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               ssp_disable(&priv->dev);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               val = ssp_read_reg(ssp, SSCR1);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       val &= ~SSCR1_TSRE;
+               else
+                       val &= ~SSCR1_RSRE;
+               ssp_write_reg(ssp, SSCR1, val);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       dump_registers(ssp);
+
+       return ret;
+}
+
+static int pxa_ssp_probe(struct platform_device *pdev,
+                           struct snd_soc_dai *dai)
+{
+       struct ssp_priv *priv;
+       int ret;
+
+       priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->dev.ssp = ssp_request(dai->id, "SoC audio");
+       if (priv->dev.ssp == NULL) {
+               ret = -ENODEV;
+               goto err_priv;
+       }
+
+       dai->private_data = priv;
+
+       return 0;
+
+err_priv:
+       kfree(priv);
+       return ret;
+}
+
+static void pxa_ssp_remove(struct platform_device *pdev,
+                             struct snd_soc_dai *dai)
+{
+       struct ssp_priv *priv = dai->private_data;
+       ssp_free(priv->dev.ssp);
+}
+
+#define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+                         SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+                         SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+                         SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+                           SNDRV_PCM_FMTBIT_S24_LE |   \
+                           SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai pxa_ssp_dai[] = {
+       {
+               .name = "pxa2xx-ssp1",
+               .id = 0,
+               .probe = pxa_ssp_probe,
+               .remove = pxa_ssp_remove,
+               .suspend = pxa_ssp_suspend,
+               .resume = pxa_ssp_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+               },
+               .capture = {
+                        .channels_min = 1,
+                        .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+                },
+               .ops = {
+                       .startup = pxa_ssp_startup,
+                       .shutdown = pxa_ssp_shutdown,
+                       .trigger = pxa_ssp_trigger,
+                       .hw_params = pxa_ssp_hw_params,
+                       .set_sysclk = pxa_ssp_set_dai_sysclk,
+                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+                       .set_pll = pxa_ssp_set_dai_pll,
+                       .set_fmt = pxa_ssp_set_dai_fmt,
+                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+                       .set_tristate = pxa_ssp_set_dai_tristate,
+               },
+       },
+       {       .name = "pxa2xx-ssp2",
+               .id = 1,
+               .probe = pxa_ssp_probe,
+               .remove = pxa_ssp_remove,
+               .suspend = pxa_ssp_suspend,
+               .resume = pxa_ssp_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+               },
+               .capture = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+                },
+               .ops = {
+                       .startup = pxa_ssp_startup,
+                       .shutdown = pxa_ssp_shutdown,
+                       .trigger = pxa_ssp_trigger,
+                       .hw_params = pxa_ssp_hw_params,
+                       .set_sysclk = pxa_ssp_set_dai_sysclk,
+                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+                       .set_pll = pxa_ssp_set_dai_pll,
+                       .set_fmt = pxa_ssp_set_dai_fmt,
+                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+                       .set_tristate = pxa_ssp_set_dai_tristate,
+               },
+       },
+       {
+               .name = "pxa2xx-ssp3",
+               .id = 2,
+               .probe = pxa_ssp_probe,
+               .remove = pxa_ssp_remove,
+               .suspend = pxa_ssp_suspend,
+               .resume = pxa_ssp_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+               },
+               .capture = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+                },
+               .ops = {
+                       .startup = pxa_ssp_startup,
+                       .shutdown = pxa_ssp_shutdown,
+                       .trigger = pxa_ssp_trigger,
+                       .hw_params = pxa_ssp_hw_params,
+                       .set_sysclk = pxa_ssp_set_dai_sysclk,
+                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+                       .set_pll = pxa_ssp_set_dai_pll,
+                       .set_fmt = pxa_ssp_set_dai_fmt,
+                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+                       .set_tristate = pxa_ssp_set_dai_tristate,
+               },
+       },
+       {
+               .name = "pxa2xx-ssp4",
+               .id = 3,
+               .probe = pxa_ssp_probe,
+               .remove = pxa_ssp_remove,
+               .suspend = pxa_ssp_suspend,
+               .resume = pxa_ssp_resume,
+               .playback = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+               },
+               .capture = {
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = PXA_SSP_RATES,
+                       .formats = PXA_SSP_FORMATS,
+                },
+               .ops = {
+                       .startup = pxa_ssp_startup,
+                       .shutdown = pxa_ssp_shutdown,
+                       .trigger = pxa_ssp_trigger,
+                       .hw_params = pxa_ssp_hw_params,
+                       .set_sysclk = pxa_ssp_set_dai_sysclk,
+                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
+                       .set_pll = pxa_ssp_set_dai_pll,
+                       .set_fmt = pxa_ssp_set_dai_fmt,
+                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
+                       .set_tristate = pxa_ssp_set_dai_tristate,
+               },
+       },
+};
+EXPORT_SYMBOL_GPL(pxa_ssp_dai);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa-ssp.h b/sound/soc/pxa/pxa-ssp.h
new file mode 100644 (file)
index 0000000..91deadd
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * ASoC PXA SSP port support
+ *
+ * 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 _PXA_SSP_H
+#define _PXA_SSP_H
+
+/* pxa DAI SSP IDs */
+#define PXA_DAI_SSP1                   0
+#define PXA_DAI_SSP2                   1
+#define PXA_DAI_SSP3                   2
+#define PXA_DAI_SSP4                   3
+
+/* SSP clock sources */
+#define PXA_SSP_CLK_PLL        0
+#define PXA_SSP_CLK_EXT        1
+#define PXA_SSP_CLK_NET        2
+#define PXA_SSP_CLK_AUDIO      3
+#define PXA_SSP_CLK_NET_PLL    4
+
+/* SSP audio dividers */
+#define PXA_SSP_AUDIO_DIV_ACDS         0
+#define PXA_SSP_AUDIO_DIV_SCDB         1
+#define PXA_SSP_DIV_SCR                                2
+
+/* SSP ACDS audio dividers values */
+#define PXA_SSP_CLK_AUDIO_DIV_1                0
+#define PXA_SSP_CLK_AUDIO_DIV_2                1
+#define PXA_SSP_CLK_AUDIO_DIV_4                2
+#define PXA_SSP_CLK_AUDIO_DIV_8                3
+#define PXA_SSP_CLK_AUDIO_DIV_16       4
+#define PXA_SSP_CLK_AUDIO_DIV_32       5
+
+/* SSP divider bypass */
+#define PXA_SSP_CLK_SCDB_4             0
+#define PXA_SSP_CLK_SCDB_1             1
+#define PXA_SSP_CLK_SCDB_8             2
+
+#define PXA_SSP_PLL_OUT  0
+
+extern struct snd_soc_dai pxa_ssp_dai[4];
+
+#endif
index a7a3a9c5c6ff41c6eee5ce690106de63823eb458..bffbe288634cc28667e516b4127dc786cac46f5f 100644 (file)
@@ -117,7 +117,8 @@ static void pxa2xx_ac97_remove(struct platform_device *pdev,
 }
 
 static int pxa2xx_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)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -131,7 +132,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
 }
 
 static int pxa2xx_ac97_hw_aux_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_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -145,7 +147,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
 }
 
 static int pxa2xx_ac97_hw_mic_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_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -170,7 +173,7 @@ struct snd_soc_dai pxa_ac97_dai[] = {
 {
        .name = "pxa2xx-ac97",
        .id = 0,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .probe = pxa2xx_ac97_probe,
        .remove = pxa2xx_ac97_remove,
        .suspend = pxa2xx_ac97_suspend,
@@ -193,7 +196,7 @@ struct snd_soc_dai pxa_ac97_dai[] = {
 {
        .name = "pxa2xx-ac97-aux",
        .id = 1,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .playback = {
                .stream_name = "AC97 Aux Playback",
                .channels_min = 1,
@@ -212,7 +215,7 @@ struct snd_soc_dai pxa_ac97_dai[] = {
 {
        .name = "pxa2xx-ac97-mic",
        .id = 2,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .capture = {
                .stream_name = "AC97 Mic Capture",
                .channels_min = 1,
index e758034db5c3ba38422e07592b2a933dc3e50b81..f9a9e2ebafa1e0440439462cd3f43c80d1205914 100644 (file)
@@ -121,7 +121,8 @@ static struct pxa2xx_gpio gpio_bus[] = {
        },
 };
 
-static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream)
+static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -187,7 +188,8 @@ static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 }
 
 static int pxa2xx_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_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -248,7 +250,8 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                             struct snd_soc_dai *dai)
 {
        int ret = 0;
 
@@ -269,7 +272,8 @@ static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
        return ret;
 }
 
-static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream)
+static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
 {
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                SACR1 |= SACR1_DRPL;
@@ -336,7 +340,6 @@ static int pxa2xx_i2s_resume(struct platform_device *pdev,
 struct snd_soc_dai pxa_i2s_dai = {
        .name = "pxa2xx-i2s",
        .id = 0,
-       .type = SND_SOC_DAI_I2S,
        .suspend = pxa2xx_i2s_suspend,
        .resume = pxa2xx_i2s_resume,
        .playback = {
@@ -353,8 +356,7 @@ struct snd_soc_dai pxa_i2s_dai = {
                .startup = pxa2xx_i2s_startup,
                .shutdown = pxa2xx_i2s_shutdown,
                .trigger = pxa2xx_i2s_trigger,
-               .hw_params = pxa2xx_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = pxa2xx_i2s_hw_params,
                .set_fmt = pxa2xx_i2s_set_dai_fmt,
                .set_sysclk = pxa2xx_i2s_set_dai_sysclk,
        },
index afcd892cd2fa42f0526defd607bd58db9f5722ec..0f6b7bb2d44b928b5be5639590c7b1434439d59a 100644 (file)
@@ -69,7 +69,7 @@ static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
-struct snd_pcm_ops pxa2xx_pcm_ops = {
+static struct snd_pcm_ops pxa2xx_pcm_ops = {
        .open           = __pxa2xx_pcm_open,
        .close          = __pxa2xx_pcm_close,
        .ioctl          = snd_pcm_lib_ioctl,
index d307b6757e9550646b565a4cffaac5abc0a02007..579d93368f14892e37ac1277856ef976339226ce 100644 (file)
@@ -319,7 +319,7 @@ static struct snd_soc_dai_link spitz_dai = {
 };
 
 /* spitz audio machine driver */
-static struct snd_soc_machine snd_soc_machine_spitz = {
+static struct snd_soc_card snd_soc_spitz = {
        .name = "Spitz",
        .dai_link = &spitz_dai,
        .num_links = 1,
@@ -333,7 +333,7 @@ static struct wm8750_setup_data spitz_wm8750_setup = {
 
 /* spitz audio subsystem */
 static struct snd_soc_device spitz_snd_devdata = {
-       .machine = &snd_soc_machine_spitz,
+       .card = &snd_soc_spitz,
        .platform = &pxa2xx_soc_platform,
        .codec_dev = &soc_codec_dev_wm8750,
        .codec_data = &spitz_wm8750_setup,
index afefe41b8c46c9f47ae255b14ec1ebed56e3522e..48242b32a28b4d40337c5dff15a9332814a9dae7 100644 (file)
@@ -38,7 +38,7 @@
 #include "pxa2xx-pcm.h"
 #include "pxa2xx-ac97.h"
 
-static struct snd_soc_machine tosa;
+static struct snd_soc_card tosa;
 
 #define TOSA_HP        0
 #define TOSA_MIC_INT   1
@@ -230,14 +230,36 @@ static struct snd_soc_dai_link tosa_dai[] = {
 },
 };
 
-static struct snd_soc_machine tosa = {
+static int tosa_probe(struct platform_device *dev)
+{
+       int ret;
+
+       ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack");
+       if (ret)
+               return ret;
+       ret = gpio_direction_output(TOSA_GPIO_L_MUTE, 0);
+       if (ret)
+               gpio_free(TOSA_GPIO_L_MUTE);
+
+       return ret;
+}
+
+static int tosa_remove(struct platform_device *dev)
+{
+       gpio_free(TOSA_GPIO_L_MUTE);
+       return 0;
+}
+
+static struct snd_soc_card tosa = {
        .name = "Tosa",
        .dai_link = tosa_dai,
        .num_links = ARRAY_SIZE(tosa_dai),
+       .probe = tosa_probe,
+       .remove = tosa_remove,
 };
 
 static struct snd_soc_device tosa_snd_devdata = {
-       .machine = &tosa,
+       .card = &tosa,
        .platform = &pxa2xx_soc_platform,
        .codec_dev = &soc_codec_dev_wm9712,
 };
@@ -251,11 +273,6 @@ static int __init tosa_init(void)
        if (!machine_is_tosa())
                return -ENODEV;
 
-       ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack");
-       if (ret)
-               return ret;
-       gpio_direction_output(TOSA_GPIO_L_MUTE, 0);
-
        tosa_snd_device = platform_device_alloc("soc-audio", -1);
        if (!tosa_snd_device) {
                ret = -ENOMEM;
@@ -272,15 +289,12 @@ static int __init tosa_init(void)
        platform_device_put(tosa_snd_device);
 
 err_alloc:
-       gpio_free(TOSA_GPIO_L_MUTE);
-
        return ret;
 }
 
 static void __exit tosa_exit(void)
 {
        platform_device_unregister(tosa_snd_device);
-       gpio_free(TOSA_GPIO_L_MUTE);
 }
 
 module_init(tosa_init);
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
new file mode 100644 (file)
index 0000000..842d650
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * zylonite.c  --  SoC audio for Zylonite
+ *
+ * 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 as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm9713.h"
+#include "pxa2xx-pcm.h"
+#include "pxa2xx-ac97.h"
+#include "pxa-ssp.h"
+
+static struct snd_soc_card zylonite;
+
+static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Microphone", NULL),
+       SND_SOC_DAPM_MIC("Handset Microphone", NULL),
+       SND_SOC_DAPM_SPK("Multiactor", NULL),
+       SND_SOC_DAPM_SPK("Headset Earpiece", NULL),
+};
+
+/* Currently supported audio map */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+       /* Headphone output connected to HPL/HPR */
+       { "Headphone", NULL,  "HPL" },
+       { "Headphone", NULL,  "HPR" },
+
+       /* On-board earpiece */
+       { "Headset Earpiece", NULL, "OUT3" },
+
+       /* Headphone mic */
+       { "MIC2A", NULL, "Mic Bias" },
+       { "Mic Bias", NULL, "Headset Microphone" },
+
+       /* On-board mic */
+       { "MIC1", NULL, "Mic Bias" },
+       { "Mic Bias", NULL, "Handset Microphone" },
+
+       /* Multiactor differentially connected over SPKL/SPKR */
+       { "Multiactor", NULL, "SPKL" },
+       { "Multiactor", NULL, "SPKR" },
+};
+
+static int zylonite_wm9713_init(struct snd_soc_codec *codec)
+{
+       /* Currently we only support use of the AC97 clock here.  If
+        * CLK_POUT is selected by SW15 then the clock API will need
+        * to be used to request and enable it here.
+        */
+
+       snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
+                                 ARRAY_SIZE(zylonite_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+       /* Static setup for now */
+       snd_soc_dapm_enable_pin(codec, "Headphone");
+       snd_soc_dapm_enable_pin(codec, "Headset Earpiece");
+
+       snd_soc_dapm_sync(codec);
+       return 0;
+}
+
+static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       unsigned int pll_out = 0;
+       unsigned int acds = 0;
+       unsigned int wm9713_div = 0;
+       int ret = 0;
+
+       switch (params_rate(params)) {
+       case 8000:
+               wm9713_div = 12;
+               pll_out = 2048000;
+               break;
+       case 16000:
+               wm9713_div = 6;
+               pll_out = 4096000;
+               break;
+       case 48000:
+       default:
+               wm9713_div = 2;
+               pll_out = 12288000;
+               acds = 1;
+               break;
+       }
+
+       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;
+
+       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;
+
+       ret = snd_soc_dai_set_tdm_slot(cpu_dai,
+                                      params_channels(params),
+                                      params_channels(params));
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_AUDIO_DIV_ACDS, acds);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Note that if the PLL is in use the WM9713_PCMCLK_PLL_DIV needs
+        * to be set instead.
+        */
+       ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV,
+                                    WM9713_PCMDIV(wm9713_div));
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops zylonite_voice_ops = {
+       .hw_params = zylonite_voice_hw_params,
+};
+
+static struct snd_soc_dai_link zylonite_dai[] = {
+{
+       .name = "AC97",
+       .stream_name = "AC97 HiFi",
+       .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
+       .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
+       .init = zylonite_wm9713_init,
+},
+{
+       .name = "AC97 Aux",
+       .stream_name = "AC97 Aux",
+       .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
+       .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
+},
+{
+       .name = "WM9713 Voice",
+       .stream_name = "WM9713 Voice",
+       .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP3],
+       .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
+       .ops = &zylonite_voice_ops,
+},
+};
+
+static struct snd_soc_card zylonite = {
+       .name = "Zylonite",
+       .dai_link = zylonite_dai,
+       .num_links = ARRAY_SIZE(zylonite_dai),
+};
+
+static struct snd_soc_device zylonite_snd_ac97_devdata = {
+       .card = &zylonite,
+       .platform = &pxa2xx_soc_platform,
+       .codec_dev = &soc_codec_dev_wm9713,
+};
+
+static struct platform_device *zylonite_snd_ac97_device;
+
+static int __init zylonite_init(void)
+{
+       int ret;
+
+       zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+       if (!zylonite_snd_ac97_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(zylonite_snd_ac97_device,
+                            &zylonite_snd_ac97_devdata);
+       zylonite_snd_ac97_devdata.dev = &zylonite_snd_ac97_device->dev;
+
+       ret = platform_device_add(zylonite_snd_ac97_device);
+       if (ret != 0)
+               platform_device_put(zylonite_snd_ac97_device);
+
+       return ret;
+}
+
+static void __exit zylonite_exit(void)
+{
+       platform_device_unregister(zylonite_snd_ac97_device);
+}
+
+module_init(zylonite_init);
+module_exit(zylonite_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite");
+MODULE_LICENSE("GPL");
index b9f2353effebfba49cf5010c10bcf5b2794781c6..fcd03acf10f6cd4a51d504c12ed4bf4c49d84eb8 100644 (file)
@@ -44,3 +44,8 @@ config SND_S3C24XX_SOC_LN2440SBC_ALC650
          Say Y if you want to add support for SoC audio on ln2440sbc
          with the ALC650.
 
+config SND_S3C24XX_SOC_S3C24XX_UDA134X
+       tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
+               depends on SND_S3C24XX_SOC
+               select SND_S3C24XX_SOC_I2S
+               select SND_SOC_UDA134X
index 0aa5fb0b9700ae5d4db9ff3704da92aa18c7bfa9..96b3f3f617d4280dddb8fa03c5d0aa95ec9a1e11 100644 (file)
@@ -13,7 +13,9 @@ obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
 snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
 snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
 snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
+snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
 obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
+obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
index 4eab2c19c454dbda65a62a4e000f05841332c0c5..a70cbc0fa070d716ffe6695c119974ad488705d9 100644 (file)
@@ -27,7 +27,7 @@
 #include "s3c24xx-pcm.h"
 #include "s3c24xx-ac97.h"
 
-static struct snd_soc_machine ln2440sbc;
+static struct snd_soc_card ln2440sbc;
 
 static struct snd_soc_dai_link ln2440sbc_dai[] = {
 {
@@ -38,14 +38,14 @@ static struct snd_soc_dai_link ln2440sbc_dai[] = {
 },
 };
 
-static struct snd_soc_machine ln2440sbc = {
+static struct snd_soc_card ln2440sbc = {
        .name = "LN2440SBC",
        .dai_link = ln2440sbc_dai,
        .num_links = ARRAY_SIZE(ln2440sbc_dai),
 };
 
 static struct snd_soc_device ln2440sbc_snd_ac97_devdata = {
-       .machine = &ln2440sbc,
+       .card = &ln2440sbc,
        .platform = &s3c24xx_soc_platform,
        .codec_dev = &soc_codec_dev_ac97,
 };
index 87ddfefcc2fba18b821f3b154fbe03d86e1bf3e7..3df2224a67238195cf46bb1638a875044d03fb63 100644 (file)
@@ -59,7 +59,7 @@
 #define NEO_CAPTURE_HEADSET            7
 #define NEO_CAPTURE_BLUETOOTH          8
 
-static struct snd_soc_machine neo1973;
+static struct snd_soc_card neo1973;
 static struct i2c_client *i2c;
 
 static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
@@ -548,7 +548,6 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec)
 static struct snd_soc_dai bt_dai = {
        .name = "Bluetooth",
        .id = 0,
-       .type = SND_SOC_DAI_PCM,
        .playback = {
                .channels_min = 1,
                .channels_max = 1,
@@ -579,7 +578,7 @@ static struct snd_soc_dai_link neo1973_dai[] = {
 },
 };
 
-static struct snd_soc_machine neo1973 = {
+static struct snd_soc_card neo1973 = {
        .name = "neo1973",
        .dai_link = neo1973_dai,
        .num_links = ARRAY_SIZE(neo1973_dai),
@@ -591,7 +590,7 @@ static struct wm8753_setup_data neo1973_wm8753_setup = {
 };
 
 static struct snd_soc_device neo1973_snd_devdata = {
-       .machine = &neo1973,
+       .card = &neo1973,
        .platform = &s3c24xx_soc_platform,
        .codec_dev = &soc_codec_dev_wm8753,
        .codec_data = &neo1973_wm8753_setup,
index ded7d995a9228a5f0d9080cbc4575cfd2216cd32..1c741047ae35546765ae96e18aa78091cf768aaa 100644 (file)
@@ -343,7 +343,8 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 }
 
 static int s3c2412_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;
        u32 iismod;
@@ -373,7 +374,8 @@ static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
 {
        int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
        unsigned long irqs;
@@ -711,7 +713,6 @@ static int s3c2412_i2s_resume(struct platform_device *pdev,
 struct snd_soc_dai s3c2412_i2s_dai = {
        .name   = "s3c2412-i2s",
        .id     = 0,
-       .type   = SND_SOC_DAI_I2S,
        .probe  = s3c2412_i2s_probe,
        .suspend = s3c2412_i2s_suspend,
        .resume = s3c2412_i2s_resume,
@@ -730,8 +731,6 @@ struct snd_soc_dai s3c2412_i2s_dai = {
        .ops = {
                .trigger        = s3c2412_i2s_trigger,
                .hw_params      = s3c2412_i2s_hw_params,
-       },
-       .dai_ops = {
                .set_fmt        = s3c2412_i2s_set_fmt,
                .set_clkdiv     = s3c2412_i2s_set_clkdiv,
                .set_sysclk     = s3c2412_i2s_set_sysclk,
index 19c5c3cf5d8c64138e31d9b70b489768309159a1..41bde6a3883ba7af25db08353865f3988c8c40a2 100644 (file)
@@ -271,7 +271,8 @@ static void s3c2443_ac97_remove(struct platform_device *pdev,
 }
 
 static int s3c2443_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)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -313,7 +314,8 @@ static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
 }
 
 static int s3c2443_ac97_hw_mic_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_dai *cpu_dai = rtd->dai->cpu_dai;
@@ -327,7 +329,7 @@ static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
 }
 
 static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
-       int cmd)
+                                   int cmd, struct snd_soc_dai *dai)
 {
        u32 ac_glbctrl;
 
@@ -356,7 +358,7 @@ struct snd_soc_dai s3c2443_ac97_dai[] = {
 {
        .name = "s3c2443-ac97",
        .id = 0,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .probe = s3c2443_ac97_probe,
        .remove = s3c2443_ac97_remove,
        .playback = {
@@ -378,7 +380,7 @@ struct snd_soc_dai s3c2443_ac97_dai[] = {
 {
        .name = "pxa2xx-ac97-mic",
        .id = 1,
-       .type = SND_SOC_DAI_AC97,
+       .ac97_control = 1,
        .capture = {
                .stream_name = "AC97 Mic Capture",
                .channels_min = 1,
index ba4476b55fbcc1f0e67f1311418fd015b1f3bef4..8d9135f41bc9e4c7d7f7ef15aec57c4e3532ef2b 100644 (file)
@@ -243,7 +243,8 @@ static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 }
 
 static int s3c24xx_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;
        u32 iismod;
@@ -261,10 +262,17 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
 
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S8:
+               iismod &= ~S3C2410_IISMOD_16BIT;
+               ((struct s3c24xx_pcm_dma_params *)
+                 rtd->dai->cpu_dai->dma_data)->dma_size = 1;
                break;
        case SNDRV_PCM_FORMAT_S16_LE:
                iismod |= S3C2410_IISMOD_16BIT;
+               ((struct s3c24xx_pcm_dma_params *)
+                 rtd->dai->cpu_dai->dma_data)->dma_size = 2;
                break;
+       default:
+               return -EINVAL;
        }
 
        writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
@@ -272,7 +280,8 @@ static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
+static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
 {
        int ret = 0;
 
@@ -452,7 +461,6 @@ static int s3c24xx_i2s_resume(struct platform_device *pdev,
 struct snd_soc_dai s3c24xx_i2s_dai = {
        .name = "s3c24xx-i2s",
        .id = 0,
-       .type = SND_SOC_DAI_I2S,
        .probe = s3c24xx_i2s_probe,
        .suspend = s3c24xx_i2s_suspend,
        .resume = s3c24xx_i2s_resume,
@@ -468,8 +476,7 @@ struct snd_soc_dai s3c24xx_i2s_dai = {
                .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
        .ops = {
                .trigger = s3c24xx_i2s_trigger,
-               .hw_params = s3c24xx_i2s_hw_params,},
-       .dai_ops = {
+               .hw_params = s3c24xx_i2s_hw_params,
                .set_fmt = s3c24xx_i2s_set_fmt,
                .set_clkdiv = s3c24xx_i2s_set_clkdiv,
                .set_sysclk = s3c24xx_i2s_set_sysclk,
diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c
new file mode 100644 (file)
index 0000000..23325fc
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Modifications by Christian Pellegrin <chripell@evolware.org>
+ *
+ * s3c24xx_uda134x.c  --  S3C24XX_UDA134X ALSA SoC Audio board 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.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/s3c24xx_uda134x.h>
+#include <sound/uda134x.h>
+
+#include <asm/plat-s3c24xx/regs-iis.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+#include "../codecs/uda134x.h"
+
+
+/* #define ENFORCE_RATES 1 */
+/*
+  Unfortunately the S3C24XX in master mode has a limited capacity of
+  generating the clock for the codec. If you define this only rates
+  that are really available will be enforced. But be careful, most
+  user level application just want the usual sampling frequencies (8,
+  11.025, 22.050, 44.1 kHz) and anyway resampling is a costly
+  operation for embedded systems. So if you aren't very lucky or your
+  hardware engineer wasn't very forward-looking it's better to leave
+  this undefined. If you do so an approximate value for the requested
+  sampling rate in the range -/+ 5% will be chosen. If this in not
+  possible an error will be returned.
+*/
+
+static struct clk *xtal;
+static struct clk *pclk;
+/* this is need because we don't have a place where to keep the
+ * pointers to the clocks in each substream. We get the clocks only
+ * when we are actually using them so we don't block stuff like
+ * frequency change or oscillator power-off */
+static int clk_users;
+static DEFINE_MUTEX(clk_lock);
+
+static unsigned int rates[33 * 2];
+#ifdef ENFORCE_RATES
+static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+       .count  = ARRAY_SIZE(rates),
+       .list   = rates,
+       .mask   = 0,
+};
+#endif
+
+static struct platform_device *s3c24xx_uda134x_snd_device;
+
+static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
+{
+       int ret = 0;
+#ifdef ENFORCE_RATES
+       struct snd_pcm_runtime *runtime = substream->runtime;;
+#endif
+
+       mutex_lock(&clk_lock);
+       pr_debug("%s %d\n", __func__, clk_users);
+       if (clk_users == 0) {
+               xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
+               if (!xtal) {
+                       printk(KERN_ERR "%s cannot get xtal\n", __func__);
+                       ret = -EBUSY;
+               } else {
+                       pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
+                                      "pclk");
+                       if (!pclk) {
+                               printk(KERN_ERR "%s cannot get pclk\n",
+                                      __func__);
+                               clk_put(xtal);
+                               ret = -EBUSY;
+                       }
+               }
+               if (!ret) {
+                       int i, j;
+
+                       for (i = 0; i < 2; i++) {
+                               int fs = i ? 256 : 384;
+
+                               rates[i*33] = clk_get_rate(xtal) / fs;
+                               for (j = 1; j < 33; j++)
+                                       rates[i*33 + j] = clk_get_rate(pclk) /
+                                               (j * fs);
+                       }
+               }
+       }
+       clk_users += 1;
+       mutex_unlock(&clk_lock);
+       if (!ret) {
+#ifdef ENFORCE_RATES
+               ret = snd_pcm_hw_constraint_list(runtime, 0,
+                                                SNDRV_PCM_HW_PARAM_RATE,
+                                                &hw_constraints_rates);
+               if (ret < 0)
+                       printk(KERN_ERR "%s cannot set constraints\n",
+                              __func__);
+#endif
+       }
+       return ret;
+}
+
+static void s3c24xx_uda134x_shutdown(struct snd_pcm_substream *substream)
+{
+       mutex_lock(&clk_lock);
+       pr_debug("%s %d\n", __func__, clk_users);
+       clk_users -= 1;
+       if (clk_users == 0) {
+               clk_put(xtal);
+               xtal = NULL;
+               clk_put(pclk);
+               pclk = NULL;
+       }
+       mutex_unlock(&clk_lock);
+}
+
+static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       unsigned int clk = 0;
+       int ret = 0;
+       int clk_source, fs_mode;
+       unsigned long rate = params_rate(params);
+       long err, cerr;
+       unsigned int div;
+       int i, bi;
+
+       err = 999999;
+       bi = 0;
+       for (i = 0; i < 2*33; i++) {
+               cerr = rates[i] - rate;
+               if (cerr < 0)
+                       cerr = -cerr;
+               if (cerr < err) {
+                       err = cerr;
+                       bi = i;
+               }
+       }
+       if (bi / 33 == 1)
+               fs_mode = S3C2410_IISMOD_256FS;
+       else
+               fs_mode = S3C2410_IISMOD_384FS;
+       if (bi % 33 == 0) {
+               clk_source = S3C24XX_CLKSRC_MPLL;
+               div = 1;
+       } else {
+               clk_source = S3C24XX_CLKSRC_PCLK;
+               div = bi % 33;
+       }
+       pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
+
+       clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
+       pr_debug("%s will use: %s %s %d sysclk %d err %ld\n", __func__,
+                fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
+                clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
+                div, clk, err);
+
+       if ((err * 100 / rate) > 5) {
+               printk(KERN_ERR "S3C24XX_UDA134X: effective frequency "
+                      "too different from desired (%ld%%)\n",
+                      err * 100 / rate);
+               return -EINVAL;
+       }
+
+       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;
+
+       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;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, clk_source , clk,
+                       SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, fs_mode);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
+                       S3C2410_IISMOD_32FS);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+                       S3C24XX_PRESCALE(div, div));
+       if (ret < 0)
+               return ret;
+
+       /* set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
+                       SND_SOC_CLOCK_OUT);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops s3c24xx_uda134x_ops = {
+       .startup = s3c24xx_uda134x_startup,
+       .shutdown = s3c24xx_uda134x_shutdown,
+       .hw_params = s3c24xx_uda134x_hw_params,
+};
+
+static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
+       .name = "UDA134X",
+       .stream_name = "UDA134X",
+       .codec_dai = &uda134x_dai,
+       .cpu_dai = &s3c24xx_i2s_dai,
+       .ops = &s3c24xx_uda134x_ops,
+};
+
+static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
+       .name = "S3C24XX_UDA134X",
+       .dai_link = &s3c24xx_uda134x_dai_link,
+       .num_links = 1,
+};
+
+static struct s3c24xx_uda134x_platform_data *s3c24xx_uda134x_l3_pins;
+
+static void setdat(int v)
+{
+       gpio_set_value(s3c24xx_uda134x_l3_pins->l3_data, v > 0);
+}
+
+static void setclk(int v)
+{
+       gpio_set_value(s3c24xx_uda134x_l3_pins->l3_clk, v > 0);
+}
+
+static void setmode(int v)
+{
+       gpio_set_value(s3c24xx_uda134x_l3_pins->l3_mode, v > 0);
+}
+
+static struct uda134x_platform_data s3c24xx_uda134x = {
+       .l3 = {
+               .setdat = setdat,
+               .setclk = setclk,
+               .setmode = setmode,
+               .data_hold = 1,
+               .data_setup = 1,
+               .clock_high = 1,
+               .mode_hold = 1,
+               .mode = 1,
+               .mode_setup = 1,
+       },
+};
+
+static struct snd_soc_device s3c24xx_uda134x_snd_devdata = {
+       .card = &snd_soc_s3c24xx_uda134x,
+       .platform = &s3c24xx_soc_platform,
+       .codec_dev = &soc_codec_dev_uda134x,
+       .codec_data = &s3c24xx_uda134x,
+};
+
+static int s3c24xx_uda134x_setup_pin(int pin, char *fun)
+{
+       if (gpio_request(pin, "s3c24xx_uda134x") < 0) {
+               printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
+                      "l3 %s pin already in use", fun);
+               return -EBUSY;
+       }
+       gpio_direction_output(pin, 0);
+       return 0;
+}
+
+static int s3c24xx_uda134x_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
+
+       s3c24xx_uda134x_l3_pins = pdev->dev.platform_data;
+       if (s3c24xx_uda134x_l3_pins == NULL) {
+               printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
+                      "unable to find platform data\n");
+               return -ENODEV;
+       }
+       s3c24xx_uda134x.power = s3c24xx_uda134x_l3_pins->power;
+       s3c24xx_uda134x.model = s3c24xx_uda134x_l3_pins->model;
+
+       if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data,
+                                     "data") < 0)
+               return -EBUSY;
+       if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk,
+                                     "clk") < 0) {
+               gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
+               return -EBUSY;
+       }
+       if (s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode,
+                                     "mode") < 0) {
+               gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
+               gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
+               return -EBUSY;
+       }
+
+       s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!s3c24xx_uda134x_snd_device) {
+               printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: "
+                      "Unable to register\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(s3c24xx_uda134x_snd_device,
+                            &s3c24xx_uda134x_snd_devdata);
+       s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
+       ret = platform_device_add(s3c24xx_uda134x_snd_device);
+       if (ret) {
+               printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n");
+               platform_device_put(s3c24xx_uda134x_snd_device);
+       }
+
+       return ret;
+}
+
+static int s3c24xx_uda134x_remove(struct platform_device *pdev)
+{
+       platform_device_unregister(s3c24xx_uda134x_snd_device);
+       gpio_free(s3c24xx_uda134x_l3_pins->l3_data);
+       gpio_free(s3c24xx_uda134x_l3_pins->l3_clk);
+       gpio_free(s3c24xx_uda134x_l3_pins->l3_mode);
+       return 0;
+}
+
+static struct platform_driver s3c24xx_uda134x_driver = {
+       .probe  = s3c24xx_uda134x_probe,
+       .remove = s3c24xx_uda134x_remove,
+       .driver = {
+               .name = "s3c24xx_uda134x",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init s3c24xx_uda134x_init(void)
+{
+       return platform_driver_register(&s3c24xx_uda134x_driver);
+}
+
+static void __exit s3c24xx_uda134x_exit(void)
+{
+       platform_driver_unregister(&s3c24xx_uda134x_driver);
+}
+
+
+module_init(s3c24xx_uda134x_init);
+module_exit(s3c24xx_uda134x_exit);
+
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_DESCRIPTION("S3C24XX_UDA134X ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
index 8515d6ff03f2c50c5220f8a39cf9ec7a0389693d..3d2e6a0417ececed4e2c79805b036a327e20c78a 100644 (file)
@@ -23,7 +23,7 @@
 #include "s3c24xx-pcm.h"
 #include "s3c24xx-ac97.h"
 
-static struct snd_soc_machine smdk2443;
+static struct snd_soc_card smdk2443;
 
 static struct snd_soc_dai_link smdk2443_dai[] = {
 {
@@ -34,14 +34,14 @@ static struct snd_soc_dai_link smdk2443_dai[] = {
 },
 };
 
-static struct snd_soc_machine smdk2443 = {
+static struct snd_soc_card smdk2443 = {
        .name = "SMDK2443",
        .dai_link = smdk2443_dai,
        .num_links = ARRAY_SIZE(smdk2443_dai),
 };
 
 static struct snd_soc_device smdk2443_snd_ac97_devdata = {
-       .machine = &smdk2443,
+       .card = &smdk2443,
        .platform = &s3c24xx_soc_platform,
        .codec_dev = &soc_codec_dev_ac97,
 };
index df7bc345c3205cc0ae069322f6243e1027b71492..c435913c60ebc7a0b37e6d2f7384f3186e549bca 100644 (file)
@@ -236,7 +236,8 @@ struct snd_ac97_bus_ops soc_ac97_ops = {
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
 static int hac_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 hac_priv *hac = &hac_cpu_data[rtd->dai->cpu_dai->id];
@@ -270,7 +271,7 @@ struct snd_soc_dai sh4_hac_dai[] = {
 {
        .name                   = "HAC0",
        .id                     = 0,
-       .type                   = SND_SOC_DAI_AC97,
+       .ac97_control           = 1,
        .playback = {
                .rates          = AC97_RATES,
                .formats        = AC97_FMTS,
@@ -290,8 +291,8 @@ struct snd_soc_dai sh4_hac_dai[] = {
 #ifdef CONFIG_CPU_SUBTYPE_SH7760
 {
        .name                   = "HAC1",
+       .ac97_control           = 1,
        .id                     = 1,
-       .type                   = SND_SOC_DAI_AC97,
        .playback = {
                .rates          = AC97_RATES,
                .formats        = AC97_FMTS,
index 92bfaf4774a7d05fec84bf270539c8be09a6b05d..8b44f9c8a9ff32d574877cafc8f759fad506a310 100644 (file)
@@ -38,14 +38,14 @@ static struct snd_soc_dai_link sh7760_ac97_dai = {
        .ops = NULL,
 };
 
-static struct snd_soc_machine sh7760_ac97_soc_machine  = {
+static struct snd_soc_card sh7760_ac97_soc_machine  = {
        .name = "SH7760 AC97",
        .dai_link = &sh7760_ac97_dai,
        .num_links = 1,
 };
 
 static struct snd_soc_device sh7760_ac97_snd_devdata = {
-       .machine = &sh7760_ac97_soc_machine,
+       .card = &sh7760_ac97_soc_machine,
        .platform = &sh7760_soc_platform,
        .codec_dev = &soc_codec_dev_ac97,
 };
index 55c3464163ab45b50b7c359ef50b138c994bfcd1..fed544a3deff0da0afdde389e94777f9e6c3ebb3 100644 (file)
@@ -89,7 +89,8 @@ struct ssi_priv {
  * track usage of the SSI; it is simplex-only so prevent attempts of
  * concurrent playback + capture. FIXME: any locking required?
  */
-static int ssi_startup(struct snd_pcm_substream *substream)
+static int ssi_startup(struct snd_pcm_substream *substream,
+                      struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -101,7 +102,8 @@ static int ssi_startup(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static void ssi_shutdown(struct snd_pcm_substream *substream)
+static void ssi_shutdown(struct snd_pcm_substream *substream,
+                        struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -109,7 +111,8 @@ static void ssi_shutdown(struct snd_pcm_substream *substream)
        ssi->inuse = 0;
 }
 
-static int ssi_trigger(struct snd_pcm_substream *substream, int cmd)
+static int ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+                      struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -129,7 +132,8 @@ static int ssi_trigger(struct snd_pcm_substream *substream, int cmd)
 }
 
 static int ssi_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 ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
@@ -336,7 +340,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
 {
        .name                   = "SSI0",
        .id                     = 0,
-       .type                   = SND_SOC_DAI_I2S,
        .playback = {
                .rates          = SSI_RATES,
                .formats        = SSI_FMTS,
@@ -354,8 +357,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
                .shutdown       = ssi_shutdown,
                .trigger        = ssi_trigger,
                .hw_params      = ssi_hw_params,
-       },
-       .dai_ops = {
                .set_sysclk     = ssi_set_sysclk,
                .set_clkdiv     = ssi_set_clkdiv,
                .set_fmt        = ssi_set_fmt,
@@ -365,7 +366,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
 {
        .name                   = "SSI1",
        .id                     = 1,
-       .type                   = SND_SOC_DAI_I2S,
        .playback = {
                .rates          = SSI_RATES,
                .formats        = SSI_FMTS,
@@ -383,8 +383,6 @@ struct snd_soc_dai sh4_ssi_dai[] = {
                .shutdown       = ssi_shutdown,
                .trigger        = ssi_trigger,
                .hw_params      = ssi_hw_params,
-       },
-       .dai_ops = {
                .set_sysclk     = ssi_set_sysclk,
                .set_clkdiv     = ssi_set_clkdiv,
                .set_fmt        = ssi_set_fmt,
index 16c7453f494691cd3f75b6d6fe4ef5b45c339c84..13b4aaff0e9cdb672b476fdb27bb596261572966 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/bitops.h>
+#include <linux/debugfs.h>
 #include <linux/platform_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
 
-/* debug */
-#define SOC_DEBUG 0
-#if SOC_DEBUG
-#define dbg(format, arg...) printk(format, ## arg)
-#else
-#define dbg(format, arg...)
-#endif
-
 static DEFINE_MUTEX(pcm_mutex);
 static DEFINE_MUTEX(io_mutex);
 static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
@@ -107,20 +100,6 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
 }
 #endif
 
-static inline const char *get_dai_name(int type)
-{
-       switch (type) {
-       case SND_SOC_DAI_AC97_BUS:
-       case SND_SOC_DAI_AC97:
-               return "AC97";
-       case SND_SOC_DAI_I2S:
-               return "I2S";
-       case SND_SOC_DAI_PCM:
-               return "PCM";
-       }
-       return NULL;
-}
-
 /*
  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
  * then initialized and any private data can be allocated. This also calls
@@ -141,7 +120,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
 
        /* startup the audio subsystem */
        if (cpu_dai->ops.startup) {
-               ret = cpu_dai->ops.startup(substream);
+               ret = cpu_dai->ops.startup(substream, cpu_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't open interface %s\n",
                                cpu_dai->name);
@@ -158,7 +137,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        }
 
        if (codec_dai->ops.startup) {
-               ret = codec_dai->ops.startup(substream);
+               ret = codec_dai->ops.startup(substream, codec_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't open codec %s\n",
                                codec_dai->name);
@@ -228,12 +207,12 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                goto machine_err;
        }
 
-       dbg("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
-       dbg("asoc: rate mask 0x%x\n", runtime->hw.rates);
-       dbg("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
-               runtime->hw.channels_max);
-       dbg("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
-               runtime->hw.rate_max);
+       pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
+       pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
+       pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
+                runtime->hw.channels_max);
+       pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
+                runtime->hw.rate_max);
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                cpu_dai->playback.active = codec_dai->playback.active = 1;
@@ -255,7 +234,7 @@ codec_dai_err:
 
 platform_err:
        if (cpu_dai->ops.shutdown)
-               cpu_dai->ops.shutdown(substream);
+               cpu_dai->ops.shutdown(substream, cpu_dai);
 out:
        mutex_unlock(&pcm_mutex);
        return ret;
@@ -278,18 +257,18 @@ static void close_delayed_work(struct work_struct *work)
        for (i = 0; i < codec->num_dai; i++) {
                codec_dai = &codec->dai[i];
 
-               dbg("pop wq checking: %s status: %s waiting: %s\n",
-                       codec_dai->playback.stream_name,
-                       codec_dai->playback.active ? "active" : "inactive",
-                       codec_dai->pop_wait ? "yes" : "no");
+               pr_debug("pop wq checking: %s status: %s waiting: %s\n",
+                        codec_dai->playback.stream_name,
+                        codec_dai->playback.active ? "active" : "inactive",
+                        codec_dai->pop_wait ? "yes" : "no");
 
                /* are we waiting on this codec DAI stream */
                if (codec_dai->pop_wait == 1) {
 
                        /* Reduce power if no longer active */
                        if (codec->active == 0) {
-                               dbg("pop wq D1 %s %s\n", codec->name,
-                                       codec_dai->playback.stream_name);
+                               pr_debug("pop wq D1 %s %s\n", codec->name,
+                                        codec_dai->playback.stream_name);
                                snd_soc_dapm_set_bias_level(socdev,
                                        SND_SOC_BIAS_PREPARE);
                        }
@@ -301,8 +280,8 @@ static void close_delayed_work(struct work_struct *work)
 
                        /* Fall into standby if no longer active */
                        if (codec->active == 0) {
-                               dbg("pop wq D3 %s %s\n", codec->name,
-                                       codec_dai->playback.stream_name);
+                               pr_debug("pop wq D3 %s %s\n", codec->name,
+                                        codec_dai->playback.stream_name);
                                snd_soc_dapm_set_bias_level(socdev,
                                        SND_SOC_BIAS_STANDBY);
                        }
@@ -346,10 +325,10 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
                snd_soc_dai_digital_mute(codec_dai, 1);
 
        if (cpu_dai->ops.shutdown)
-               cpu_dai->ops.shutdown(substream);
+               cpu_dai->ops.shutdown(substream, cpu_dai);
 
        if (codec_dai->ops.shutdown)
-               codec_dai->ops.shutdown(substream);
+               codec_dai->ops.shutdown(substream, codec_dai);
 
        if (machine->ops && machine->ops->shutdown)
                machine->ops->shutdown(substream);
@@ -413,7 +392,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        }
 
        if (codec_dai->ops.prepare) {
-               ret = codec_dai->ops.prepare(substream);
+               ret = codec_dai->ops.prepare(substream, codec_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: codec DAI prepare error\n");
                        goto out;
@@ -421,58 +400,49 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        }
 
        if (cpu_dai->ops.prepare) {
-               ret = cpu_dai->ops.prepare(substream);
+               ret = cpu_dai->ops.prepare(substream, cpu_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: cpu DAI prepare error\n");
                        goto out;
                }
        }
 
-       /* we only want to start a DAPM playback stream if we are not waiting
-        * on an existing one stopping */
-       if (codec_dai->pop_wait) {
-               /* we are waiting for the delayed work to start */
-               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-                               snd_soc_dapm_stream_event(socdev->codec,
-                                       codec_dai->capture.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
-               else {
-                       codec_dai->pop_wait = 0;
-                       cancel_delayed_work(&socdev->delayed_work);
-                       snd_soc_dai_digital_mute(codec_dai, 0);
-               }
-       } else {
-               /* no delayed work - do we need to power up codec */
-               if (codec->bias_level != SND_SOC_BIAS_ON) {
+       /* cancel any delayed stream shutdown that is pending */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+           codec_dai->pop_wait) {
+               codec_dai->pop_wait = 0;
+               cancel_delayed_work(&socdev->delayed_work);
+       }
 
-                       snd_soc_dapm_set_bias_level(socdev,
-                                                   SND_SOC_BIAS_PREPARE);
+       /* do we need to power up codec */
+       if (codec->bias_level != SND_SOC_BIAS_ON) {
+               snd_soc_dapm_set_bias_level(socdev,
+                                           SND_SOC_BIAS_PREPARE);
 
-                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                               snd_soc_dapm_stream_event(codec,
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       snd_soc_dapm_stream_event(codec,
                                        codec_dai->playback.stream_name,
                                        SND_SOC_DAPM_STREAM_START);
-                       else
-                               snd_soc_dapm_stream_event(codec,
+               else
+                       snd_soc_dapm_stream_event(codec,
                                        codec_dai->capture.stream_name,
                                        SND_SOC_DAPM_STREAM_START);
 
-                       snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
-                       snd_soc_dai_digital_mute(codec_dai, 0);
+               snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
+               snd_soc_dai_digital_mute(codec_dai, 0);
 
-               } else {
-                       /* codec already powered - power on widgets */
-                       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                               snd_soc_dapm_stream_event(codec,
+       } else {
+               /* codec already powered - power on widgets */
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       snd_soc_dapm_stream_event(codec,
                                        codec_dai->playback.stream_name,
                                        SND_SOC_DAPM_STREAM_START);
-                       else
-                               snd_soc_dapm_stream_event(codec,
+               else
+                       snd_soc_dapm_stream_event(codec,
                                        codec_dai->capture.stream_name,
                                        SND_SOC_DAPM_STREAM_START);
 
-                       snd_soc_dai_digital_mute(codec_dai, 0);
-               }
+               snd_soc_dai_digital_mute(codec_dai, 0);
        }
 
 out:
@@ -507,7 +477,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        if (codec_dai->ops.hw_params) {
-               ret = codec_dai->ops.hw_params(substream, params);
+               ret = codec_dai->ops.hw_params(substream, params, codec_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't set codec %s hw params\n",
                                codec_dai->name);
@@ -516,7 +486,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        }
 
        if (cpu_dai->ops.hw_params) {
-               ret = cpu_dai->ops.hw_params(substream, params);
+               ret = cpu_dai->ops.hw_params(substream, params, cpu_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: interface %s hw params failed\n",
                                cpu_dai->name);
@@ -539,11 +509,11 @@ out:
 
 platform_err:
        if (cpu_dai->ops.hw_free)
-               cpu_dai->ops.hw_free(substream);
+               cpu_dai->ops.hw_free(substream, cpu_dai);
 
 interface_err:
        if (codec_dai->ops.hw_free)
-               codec_dai->ops.hw_free(substream);
+               codec_dai->ops.hw_free(substream, codec_dai);
 
 codec_err:
        if (machine->ops && machine->ops->hw_free)
@@ -582,10 +552,10 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
 
        /* now free hw params for the DAI's  */
        if (codec_dai->ops.hw_free)
-               codec_dai->ops.hw_free(substream);
+               codec_dai->ops.hw_free(substream, codec_dai);
 
        if (cpu_dai->ops.hw_free)
-               cpu_dai->ops.hw_free(substream);
+               cpu_dai->ops.hw_free(substream, cpu_dai);
 
        mutex_unlock(&pcm_mutex);
        return 0;
@@ -602,7 +572,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        int ret;
 
        if (codec_dai->ops.trigger) {
-               ret = codec_dai->ops.trigger(substream, cmd);
+               ret = codec_dai->ops.trigger(substream, cmd, codec_dai);
                if (ret < 0)
                        return ret;
        }
@@ -614,7 +584,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        }
 
        if (cpu_dai->ops.trigger) {
-               ret = cpu_dai->ops.trigger(substream, cmd);
+               ret = cpu_dai->ops.trigger(substream, cmd, cpu_dai);
                if (ret < 0)
                        return ret;
        }
@@ -636,7 +606,7 @@ static struct snd_pcm_ops soc_pcm_ops = {
 static int soc_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        struct snd_soc_platform *platform = socdev->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
        struct snd_soc_codec *codec = socdev->codec;
@@ -653,22 +623,22 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot);
 
        /* mute any active DAC's */
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
-               if (dai->dai_ops.digital_mute && dai->playback.active)
-                       dai->dai_ops.digital_mute(dai, 1);
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
+               if (dai->ops.digital_mute && dai->playback.active)
+                       dai->ops.digital_mute(dai, 1);
        }
 
        /* suspend all pcms */
-       for (i = 0; i < machine->num_links; i++)
-               snd_pcm_suspend_all(machine->dai_link[i].pcm);
+       for (i = 0; i < card->num_links; i++)
+               snd_pcm_suspend_all(card->dai_link[i].pcm);
 
-       if (machine->suspend_pre)
-               machine->suspend_pre(pdev, state);
+       if (card->suspend_pre)
+               card->suspend_pre(pdev, state);
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai  *cpu_dai = machine->dai_link[i].cpu_dai;
-               if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97)
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai  *cpu_dai = card->dai_link[i].cpu_dai;
+               if (cpu_dai->suspend && !cpu_dai->ac97_control)
                        cpu_dai->suspend(pdev, cpu_dai);
                if (platform->suspend)
                        platform->suspend(pdev, cpu_dai);
@@ -692,14 +662,14 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        if (codec_dev->suspend)
                codec_dev->suspend(pdev, state);
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
-               if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97)
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+               if (cpu_dai->suspend && cpu_dai->ac97_control)
                        cpu_dai->suspend(pdev, cpu_dai);
        }
 
-       if (machine->suspend_post)
-               machine->suspend_post(pdev, state);
+       if (card->suspend_post)
+               card->suspend_post(pdev, state);
 
        return 0;
 }
@@ -712,7 +682,7 @@ static void soc_resume_deferred(struct work_struct *work)
        struct snd_soc_device *socdev = container_of(work,
                                                     struct snd_soc_device,
                                                     deferred_resume_work);
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        struct snd_soc_platform *platform = socdev->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
        struct snd_soc_codec *codec = socdev->codec;
@@ -723,14 +693,14 @@ static void soc_resume_deferred(struct work_struct *work)
         * so userspace apps are blocked from touching us
         */
 
-       dev_info(socdev->dev, "starting resume work\n");
+       dev_dbg(socdev->dev, "starting resume work\n");
 
-       if (machine->resume_pre)
-               machine->resume_pre(pdev);
+       if (card->resume_pre)
+               card->resume_pre(pdev);
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
-               if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97)
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+               if (cpu_dai->resume && cpu_dai->ac97_control)
                        cpu_dai->resume(pdev, cpu_dai);
        }
 
@@ -749,24 +719,24 @@ static void soc_resume_deferred(struct work_struct *work)
        }
 
        /* unmute any active DACs */
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *dai = machine->dai_link[i].codec_dai;
-               if (dai->dai_ops.digital_mute && dai->playback.active)
-                       dai->dai_ops.digital_mute(dai, 0);
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
+               if (dai->ops.digital_mute && dai->playback.active)
+                       dai->ops.digital_mute(dai, 0);
        }
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
-               if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97)
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
+               if (cpu_dai->resume && !cpu_dai->ac97_control)
                        cpu_dai->resume(pdev, cpu_dai);
                if (platform->resume)
                        platform->resume(pdev, cpu_dai);
        }
 
-       if (machine->resume_post)
-               machine->resume_post(pdev);
+       if (card->resume_post)
+               card->resume_post(pdev);
 
-       dev_info(socdev->dev, "resume work completed\n");
+       dev_dbg(socdev->dev, "resume work completed\n");
 
        /* userspace can access us now we are back as we were before */
        snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0);
@@ -777,10 +747,10 @@ static int soc_resume(struct platform_device *pdev)
 {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 
-       dev_info(socdev->dev, "scheduling resume work\n");
+       dev_dbg(socdev->dev, "scheduling resume work\n");
 
        if (!schedule_work(&socdev->deferred_resume_work))
-               dev_err(socdev->dev, "work item may be lost\n");
+               dev_err(socdev->dev, "resume work item may be lost\n");
 
        return 0;
 }
@@ -795,18 +765,18 @@ static int soc_probe(struct platform_device *pdev)
 {
        int ret = 0, i;
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        struct snd_soc_platform *platform = socdev->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 
-       if (machine->probe) {
-               ret = machine->probe(pdev);
+       if (card->probe) {
+               ret = card->probe(pdev);
                if (ret < 0)
                        return ret;
        }
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->probe) {
                        ret = cpu_dai->probe(pdev, cpu_dai);
                        if (ret < 0)
@@ -841,13 +811,13 @@ platform_err:
 
 cpu_dai_err:
        for (i--; i >= 0; i--) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->remove)
                        cpu_dai->remove(pdev, cpu_dai);
        }
 
-       if (machine->remove)
-               machine->remove(pdev);
+       if (card->remove)
+               card->remove(pdev);
 
        return ret;
 }
@@ -857,7 +827,7 @@ static int soc_remove(struct platform_device *pdev)
 {
        int i;
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        struct snd_soc_platform *platform = socdev->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 
@@ -869,14 +839,14 @@ static int soc_remove(struct platform_device *pdev)
        if (codec_dev->remove)
                codec_dev->remove(pdev);
 
-       for (i = 0; i < machine->num_links; i++) {
-               struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai;
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->remove)
                        cpu_dai->remove(pdev, cpu_dai);
        }
 
-       if (machine->remove)
-               machine->remove(pdev);
+       if (card->remove)
+               card->remove(pdev);
 
        return 0;
 }
@@ -914,8 +884,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
        codec_dai->codec = socdev->codec;
 
        /* check client and interface hw capabilities */
-       sprintf(new_name, "%s %s-%s-%d", dai_link->stream_name, codec_dai->name,
-               get_dai_name(cpu_dai->type), num);
+       sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,
+               num);
 
        if (codec_dai->playback.channels_min)
                playback = 1;
@@ -961,10 +931,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
 }
 
 /* codec register dump */
-static ssize_t codec_reg_show(struct device *dev,
-       struct device_attribute *attr, char *buf)
+static ssize_t soc_codec_reg_show(struct snd_soc_device *devdata, char *buf)
 {
-       struct snd_soc_device *devdata = dev_get_drvdata(dev);
        struct snd_soc_codec *codec = devdata->codec;
        int i, step = 1, count = 0;
 
@@ -1001,8 +969,117 @@ static ssize_t codec_reg_show(struct device *dev,
 
        return count;
 }
+static ssize_t codec_reg_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct snd_soc_device *devdata = dev_get_drvdata(dev);
+       return soc_codec_reg_show(devdata, buf);
+}
+
 static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
 
+#ifdef CONFIG_DEBUG_FS
+static int codec_reg_open_file(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       ssize_t ret;
+       struct snd_soc_device *devdata = file->private_data;
+       char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       ret = soc_codec_reg_show(devdata, buf);
+       if (ret >= 0)
+               ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+       kfree(buf);
+       return ret;
+}
+
+static ssize_t codec_reg_write_file(struct file *file,
+               const char __user *user_buf, size_t count, loff_t *ppos)
+{
+       char buf[32];
+       int buf_size;
+       char *start = buf;
+       unsigned long reg, value;
+       int step = 1;
+       struct snd_soc_device *devdata = file->private_data;
+       struct snd_soc_codec *codec = devdata->codec;
+
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       buf[buf_size] = 0;
+
+       if (codec->reg_cache_step)
+               step = codec->reg_cache_step;
+
+       while (*start == ' ')
+               start++;
+       reg = simple_strtoul(start, &start, 16);
+       if ((reg >= codec->reg_cache_size) || (reg % step))
+               return -EINVAL;
+       while (*start == ' ')
+               start++;
+       if (strict_strtoul(start, 16, &value))
+               return -EINVAL;
+       codec->write(codec, reg, value);
+       return buf_size;
+}
+
+static const struct file_operations codec_reg_fops = {
+       .open = codec_reg_open_file,
+       .read = codec_reg_read_file,
+       .write = codec_reg_write_file,
+};
+
+static void soc_init_debugfs(struct snd_soc_device *socdev)
+{
+       struct dentry *root, *file;
+       struct snd_soc_codec *codec = socdev->codec;
+       root = debugfs_create_dir(dev_name(socdev->dev), NULL);
+       if (IS_ERR(root) || !root)
+               goto exit1;
+
+       file = debugfs_create_file("codec_reg", 0644,
+                       root, socdev, &codec_reg_fops);
+       if (!file)
+               goto exit2;
+
+       file = debugfs_create_u32("dapm_pop_time", 0744,
+                       root, &codec->pop_time);
+       if (!file)
+               goto exit2;
+       socdev->debugfs_root = root;
+       return;
+exit2:
+       debugfs_remove_recursive(root);
+exit1:
+       dev_err(socdev->dev, "debugfs is not available\n");
+}
+
+static void soc_cleanup_debugfs(struct snd_soc_device *socdev)
+{
+       debugfs_remove_recursive(socdev->debugfs_root);
+       socdev->debugfs_root = NULL;
+}
+
+#else
+
+static inline void soc_init_debugfs(struct snd_soc_device *socdev)
+{
+}
+
+static inline void soc_cleanup_debugfs(struct snd_soc_device *socdev)
+{
+}
+#endif
+
 /**
  * snd_soc_new_ac97_codec - initailise AC97 device
  * @codec: audio codec
@@ -1121,7 +1198,7 @@ EXPORT_SYMBOL_GPL(snd_soc_test_bits);
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
 {
        struct snd_soc_codec *codec = socdev->codec;
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        int ret = 0, i;
 
        mutex_lock(&codec->mutex);
@@ -1140,11 +1217,11 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
        strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
 
        /* create the pcms */
-       for (i = 0; i < machine->num_links; i++) {
-               ret = soc_new_pcm(socdev, &machine->dai_link[i], i);
+       for (i = 0; i < card->num_links; i++) {
+               ret = soc_new_pcm(socdev, &card->dai_link[i], i);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't create pcm %s\n",
-                               machine->dai_link[i].stream_name);
+                               card->dai_link[i].stream_name);
                        mutex_unlock(&codec->mutex);
                        return ret;
                }
@@ -1167,26 +1244,25 @@ EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
 int snd_soc_register_card(struct snd_soc_device *socdev)
 {
        struct snd_soc_codec *codec = socdev->codec;
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        int ret = 0, i, ac97 = 0, err = 0;
 
-       for (i = 0; i < machine->num_links; i++) {
-               if (socdev->machine->dai_link[i].init) {
-                       err = socdev->machine->dai_link[i].init(codec);
+       for (i = 0; i < card->num_links; i++) {
+               if (card->dai_link[i].init) {
+                       err = card->dai_link[i].init(codec);
                        if (err < 0) {
                                printk(KERN_ERR "asoc: failed to init %s\n",
-                                       socdev->machine->dai_link[i].stream_name);
+                                       card->dai_link[i].stream_name);
                                continue;
                        }
                }
-               if (socdev->machine->dai_link[i].codec_dai->type ==
-                       SND_SOC_DAI_AC97_BUS)
+               if (card->dai_link[i].codec_dai->ac97_control)
                        ac97 = 1;
        }
        snprintf(codec->card->shortname, sizeof(codec->card->shortname),
-                "%s", machine->name);
+                "%s",  card->name);
        snprintf(codec->card->longname, sizeof(codec->card->longname),
-                "%s (%s)", machine->name, codec->name);
+                "%s (%s)", card->name, codec->name);
 
        ret = snd_card_register(codec->card);
        if (ret < 0) {
@@ -1216,6 +1292,7 @@ int snd_soc_register_card(struct snd_soc_device *socdev)
        if (err < 0)
                printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
 
+       soc_init_debugfs(socdev);
        mutex_unlock(&codec->mutex);
 
 out:
@@ -1239,10 +1316,11 @@ void snd_soc_free_pcms(struct snd_soc_device *socdev)
 #endif
 
        mutex_lock(&codec->mutex);
+       soc_cleanup_debugfs(socdev);
 #ifdef CONFIG_SND_SOC_AC97_BUS
        for (i = 0; i < codec->num_dai; i++) {
                codec_dai = &codec->dai[i];
-               if (codec_dai->type == SND_SOC_DAI_AC97_BUS && codec->ac97) {
+               if (codec_dai->ac97_control && codec->ac97) {
                        soc_ac97_dev_unregister(codec);
                        goto free_card;
                }
@@ -1756,8 +1834,8 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
        unsigned int freq, int dir)
 {
-       if (dai->dai_ops.set_sysclk)
-               return dai->dai_ops.set_sysclk(dai, clk_id, freq, dir);
+       if (dai->ops.set_sysclk)
+               return dai->ops.set_sysclk(dai, clk_id, freq, dir);
        else
                return -EINVAL;
 }
@@ -1776,8 +1854,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
 int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
        int div_id, int div)
 {
-       if (dai->dai_ops.set_clkdiv)
-               return dai->dai_ops.set_clkdiv(dai, div_id, div);
+       if (dai->ops.set_clkdiv)
+               return dai->ops.set_clkdiv(dai, div_id, div);
        else
                return -EINVAL;
 }
@@ -1795,8 +1873,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
        int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
-       if (dai->dai_ops.set_pll)
-               return dai->dai_ops.set_pll(dai, pll_id, freq_in, freq_out);
+       if (dai->ops.set_pll)
+               return dai->ops.set_pll(dai, pll_id, freq_in, freq_out);
        else
                return -EINVAL;
 }
@@ -1805,15 +1883,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
 /**
  * snd_soc_dai_set_fmt - configure DAI hardware audio format.
  * @dai: DAI
- * @clk_id: DAI specific clock ID
  * @fmt: SND_SOC_DAIFMT_ format value.
  *
  * Configures the DAI hardware format and clocking.
  */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
-       if (dai->dai_ops.set_fmt)
-               return dai->dai_ops.set_fmt(dai, fmt);
+       if (dai->ops.set_fmt)
+               return dai->ops.set_fmt(dai, fmt);
        else
                return -EINVAL;
 }
@@ -1831,8 +1908,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
        unsigned int mask, int slots)
 {
-       if (dai->dai_ops.set_sysclk)
-               return dai->dai_ops.set_tdm_slot(dai, mask, slots);
+       if (dai->ops.set_sysclk)
+               return dai->ops.set_tdm_slot(dai, mask, slots);
        else
                return -EINVAL;
 }
@@ -1847,8 +1924,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
  */
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
 {
-       if (dai->dai_ops.set_sysclk)
-               return dai->dai_ops.set_tristate(dai, tristate);
+       if (dai->ops.set_sysclk)
+               return dai->ops.set_tristate(dai, tristate);
        else
                return -EINVAL;
 }
@@ -1863,8 +1940,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
  */
 int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
 {
-       if (dai->dai_ops.digital_mute)
-               return dai->dai_ops.digital_mute(dai, mute);
+       if (dai->ops.digital_mute)
+               return dai->ops.digital_mute(dai, mute);
        else
                return -EINVAL;
 }
@@ -1872,7 +1949,6 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
 
 static int __devinit snd_soc_init(void)
 {
-       printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION);
        return platform_driver_register(&soc_driver);
 }
 
index 7351db9606e48f3d11870671a4b9e71c37081e50..61d7d85aa578f9062c43ae7fcaba2703c42aa54d 100644 (file)
@@ -37,7 +37,6 @@
 #include <linux/bitops.h>
 #include <linux/platform_device.h>
 #include <linux/jiffies.h>
-#include <linux/debugfs.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -67,17 +66,13 @@ static int dapm_status = 1;
 module_param(dapm_status, int, 0);
 MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
 
-static struct dentry *asoc_debugfs;
-
-static u32 pop_time;
-
-static void pop_wait(void)
+static void pop_wait(u32 pop_time)
 {
        if (pop_time)
                schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time));
 }
 
-static void pop_dbg(const char *fmt, ...)
+static void pop_dbg(u32 pop_time, const char *fmt, ...)
 {
        va_list args;
 
@@ -85,7 +80,7 @@ static void pop_dbg(const char *fmt, ...)
 
        if (pop_time) {
                vprintk(fmt, args);
-               pop_wait();
+               pop_wait(pop_time);
        }
 
        va_end(args);
@@ -230,10 +225,11 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
 
        change = old != new;
        if (change) {
-               pop_dbg("pop test %s : %s in %d ms\n", widget->name,
-                       widget->power ? "on" : "off", pop_time);
+               pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n",
+                       widget->name, widget->power ? "on" : "off",
+                       codec->pop_time);
                snd_soc_write(codec, widget->reg, new);
-               pop_wait();
+               pop_wait(codec->pop_time);
        }
        pr_debug("reg %x old %x new %x change %d\n", widget->reg,
                 old, new, change);
@@ -293,7 +289,7 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
        struct snd_soc_dapm_widget *w)
 {
        int i, ret = 0;
-       char name[32];
+       size_t name_len;
        struct snd_soc_dapm_path *path;
 
        /* add kcontrol */
@@ -307,11 +303,16 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
                                continue;
 
                        /* add dapm control with long name */
-                       snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
-                       path->long_name = kstrdup (name, GFP_KERNEL);
+                       name_len = 2 + strlen(w->name)
+                               + strlen(w->kcontrols[i].name);
+                       path->long_name = kmalloc(name_len, GFP_KERNEL);
                        if (path->long_name == NULL)
                                return -ENOMEM;
 
+                       snprintf(path->long_name, name_len, "%s %s",
+                                w->name, w->kcontrols[i].name);
+                       path->long_name[name_len - 1] = '\0';
+
                        path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
                                path->long_name);
                        ret = snd_ctl_add(codec->card, path->kcontrol);
@@ -821,23 +822,9 @@ static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
 
 int snd_soc_dapm_sys_add(struct device *dev)
 {
-       int ret = 0;
-
        if (!dapm_status)
                return 0;
-
-       ret = device_create_file(dev, &dev_attr_dapm_widget);
-       if (ret != 0)
-               return ret;
-
-       asoc_debugfs = debugfs_create_dir("asoc", NULL);
-       if (!IS_ERR(asoc_debugfs) && asoc_debugfs)
-               debugfs_create_u32("dapm_pop_time", 0744, asoc_debugfs,
-                                  &pop_time);
-       else
-               asoc_debugfs = NULL;
-
-       return 0;
+       return device_create_file(dev, &dev_attr_dapm_widget);
 }
 
 static void snd_soc_dapm_sys_remove(struct device *dev)
@@ -845,9 +832,6 @@ static void snd_soc_dapm_sys_remove(struct device *dev)
        if (dapm_status) {
                device_remove_file(dev, &dev_attr_dapm_widget);
        }
-
-       if (asoc_debugfs)
-               debugfs_remove_recursive(asoc_debugfs);
 }
 
 /* free all dapm widgets and resources */
@@ -1006,28 +990,6 @@ err:
        return ret;
 }
 
-/**
- * snd_soc_dapm_connect_input - connect dapm widgets
- * @codec: audio codec
- * @sink: name of target widget
- * @control: mixer control name
- * @source: name of source name
- *
- * Connects 2 dapm widgets together via a named audio path. The sink is
- * the widget receiving the audio signal, whilst the source is the sender
- * of the audio signal.
- *
- * This function has been deprecated in favour of snd_soc_dapm_add_routes().
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink,
-       const char *control, const char *source)
-{
-       return snd_soc_dapm_add_route(codec, sink, control, source);
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input);
-
 /**
  * snd_soc_dapm_add_routes - Add routes between DAPM widgets
  * @codec: codec
@@ -1440,11 +1402,11 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
                                enum snd_soc_bias_level level)
 {
        struct snd_soc_codec *codec = socdev->codec;
-       struct snd_soc_machine *machine = socdev->machine;
+       struct snd_soc_card *card = socdev->card;
        int ret = 0;
 
-       if (machine->set_bias_level)
-               ret = machine->set_bias_level(machine, level);
+       if (card->set_bias_level)
+               ret = card->set_bias_level(card, level);
        if (ret == 0 && codec->set_bias_level)
                ret = codec->set_bias_level(codec, level);