aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOla Lilja <ola.o.lilja@stericsson.com>2010-07-02 09:05:16 +0200
committerJohn Rigby <john.rigby@linaro.org>2010-09-02 22:45:51 -0600
commit9a1e6a4808b23832feb458f50f65cbcfc88554a1 (patch)
treed188c853713fd8fb56d05b26e492d9d55929e46f
parent3956b4a499ce066d04fc7e21aee244cf4f582e90 (diff)
downloadlinux-2.6.34-ux500-9a1e6a4808b23832feb458f50f65cbcfc88554a1.tar.gz
sound: asoc: Added codec and machine drivers for cg29xx.
A codec driver and a machine driver have been added for cg29xx. The platform driver for ux500 has been cleaned up. Directory u8500 has changed name to ux500. Signed-off-by: Roger Nilsson <roger.xr.nilsson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/707 Reviewed-by: Ola LILJA <ola.o.lilja@stericsson.com> Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> Change-Id: I3e8d18f6182159aecc41ed352f8d7866cc70c39e Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/2843 Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
-rw-r--r--sound/soc/Kconfig2
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/codecs/Kconfig11
-rw-r--r--sound/soc/codecs/Makefile2
-rwxr-xr-xsound/soc/codecs/ab3550.c1652
-rwxr-xr-xsound/soc/codecs/ab3550.h178
-rwxr-xr-xsound/soc/codecs/cg29xx.c295
-rwxr-xr-xsound/soc/codecs/cg29xx.h22
-rwxr-xr-xsound/soc/u8500/Kconfig13
-rwxr-xr-xsound/soc/u8500/Makefile12
-rwxr-xr-xsound/soc/u8500/mop500_ab3550.c172
-rwxr-xr-xsound/soc/u8500/u8500_msp_dai.c513
-rwxr-xr-xsound/soc/u8500/u8500_msp_dai.h28
-rwxr-xr-xsound/soc/u8500/u8500_pcm.c427
-rwxr-xr-xsound/soc/u8500/u8500_pcm.h56
-rwxr-xr-xsound/soc/ux500/Kconfig26
-rwxr-xr-xsound/soc/ux500/Makefile9
-rwxr-xr-xsound/soc/ux500/ux500_ab3550.c208
-rwxr-xr-xsound/soc/ux500/ux500_cg29xx.c200
-rwxr-xr-xsound/soc/ux500/ux500_msp_dai.c789
-rwxr-xr-xsound/soc/ux500/ux500_msp_dai.h46
-rwxr-xr-xsound/soc/ux500/ux500_pcm.c401
-rwxr-xr-xsound/soc/ux500/ux500_pcm.h50
23 files changed, 3259 insertions, 1855 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 6bd98196009..ce2e5113693 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -35,7 +35,7 @@ source "sound/soc/s3c24xx/Kconfig"
source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/txx9/Kconfig"
-source "sound/soc/u8500/Kconfig"
+source "sound/soc/ux500/Kconfig"
# Supported codecs
source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index beefeec88e6..f3384c084ec 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -14,4 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/
obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += txx9/
-obj-$(CONFIG_SND_SOC) += u8500/
+obj-$(CONFIG_SND_SOC) += ux500/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index d6e4b41e3c6..a6d7b571e98 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -67,6 +67,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
select SND_SOC_WM9713 if SND_SOC_AC97_BUS
+ select SND_SOC_AB3550
+ select SND_SOC_CG29XX
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
@@ -261,11 +263,10 @@ config SND_SOC_WM9713
tristate
config SND_SOC_AB3550
- tristate "ST-Ericsson AB3550 Mixed Signal Circuit CODEC"
- depends on SND_SOC_U8500 && AB3550_CORE
- default m
- help
- Select this to support the AB3550 codec block.
+ tristate
+
+config SND_SOC_CG29XX
+ tristate
# Amp
config SND_SOC_MAX9877
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 962bfe107d8..7baa53fda31 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -9,6 +9,7 @@ snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
+snd-soc-cg29xx-objs := cg29xx.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
@@ -72,6 +73,7 @@ obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
+obj-$(CONFIG_SND_SOC_CG29XX) += snd-soc-cg29xx.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
diff --git a/sound/soc/codecs/ab3550.c b/sound/soc/codecs/ab3550.c
index 5ee08672792..de51ca963c2 100755
--- a/sound/soc/codecs/ab3550.c
+++ b/sound/soc/codecs/ab3550.c
@@ -1,7 +1,9 @@
/*
* Copyright (C) ST-Ericsson SA 2010
*
- * Author: Xie Xiaolei <xie.xiaolei@etericsson.com>
+ * Author: Xie Xiaolei <xie.xiaolei@etericsson.com>,
+ * Ola Lilja ola.o.lilja@stericsson.com,
+ * Roger Nilsson roger.xr.nilsson@stericsson.com
* for ST-Ericsson.
*
* License terms:
@@ -25,34 +27,102 @@
#include <sound/soc-dapm.h>
#include <linux/mfd/abx500.h>
#include <linux/bitops.h>
+#include <asm/atomic.h>
#include "ab3550.h"
-#include "../u8500/u8500_pcm.h"
-#include "../u8500/u8500_msp_dai.h"
#define I2C_BANK 0
static struct device *ab3550_dev = NULL;
+// Registers -----------------------------------------------------------------------------------------------------------------------
+
+struct ab3550_register {
+ const char* name;
+ u8 address;
+};
+
+struct ab3550_register ab3550_registers[] = {
+ {"IDX_MIC_BIAS1", 0x31},
+ {"IDX_MIC_BIAS2", 0x32},
+ {"IDX_MIC_BIAS2_VAD", 0x33},
+ {"IDX_MIC1_GAIN", 0x34},
+ {"IDX_MIC2_GAIN", 0x35},
+ {"IDX_MIC1_INPUT_SELECT", 0x36},
+ {"IDX_MIC2_INPUT_SELECT", 0x37},
+ {"IDX_MIC1_VMID_SELECT", 0x38},
+ {"IDX_MIC2_VMID_SELECT", 0x39},
+ {"IDX_MIC2_TO_MIC1", 0x3A},
+ {"IDX_ANALOG_LOOP_PGA1", 0x3B},
+ {"IDX_ANALOG_LOOP_PGA2", 0x3C},
+ {"IDX_APGA_VMID_SELECT", 0x3D},
+ {"IDX_EAR", 0x3E},
+ {"IDX_AUXO1", 0x3F},
+ {"IDX_AUXO2", 0x40},
+ {"IDX_AUXO_PWR_MODE", 0x41},
+ {"IDX_OFFSET_CANCEL", 0x42},
+ {"IDX_SPKR", 0x43},
+ {"IDX_LINE1", 0x44},
+ {"IDX_LINE2", 0x45},
+ {"IDX_APGA1_ADDER", 0x46},
+ {"IDX_APGA2_ADDER", 0x47},
+ {"IDX_EAR_ADDER", 0x48},
+ {"IDX_AUXO1_ADDER", 0x49},
+ {"IDX_AUXO2_ADDER", 0x4A},
+ {"IDX_SPKR_ADDER", 0x4B},
+ {"IDX_LINE1_ADDER", 0x4C},
+ {"IDX_LINE2_ADDER", 0x4D},
+ {"IDX_EAR_TO_MIC2", 0x4E},
+ {"IDX_SPKR_TO_MIC2", 0x4F},
+ {"IDX_NEGATIVE_CHARGE_PUMP", 0x50},
+ {"IDX_TX1", 0x51},
+ {"IDX_TX2", 0x52},
+ {"IDX_RX1", 0x53},
+ {"IDX_RX2", 0x54},
+ {"IDX_RX3", 0x55},
+ {"IDX_TX_DIGITAL_PGA1", 0X56},
+ {"IDX_TX_DIGITAL_PGA2", 0X57},
+ {"IDX_RX1_DIGITAL_PGA", 0x58},
+ {"IDX_RX2_DIGITAL_PGA", 0x59},
+ {"IDX_RX3_DIGITAL_PGA", 0x5A},
+ {"IDX_SIDETONE1_PGA", 0x5B},
+ {"IDX_SIDETONE2_PGA", 0x5C},
+ {"IDX_CLOCK", 0x5D},
+ {"IDX_INTERFACE0", 0x5E},
+ {"IDX_INTERFACE1", 0x60},
+ {"IDX_INTERFACE0_DATA", 0x5F},
+ {"IDX_INTERFACE1_DATA", 0x61},
+ {"IDX_INTERFACE_LOOP", 0x62},
+ {"IDX_INTERFACE_SWAP", 0x63}
+};
+
#define SET_REG(reg, val) abx500_set_register_interruptible( \
- ab3550_dev, I2C_BANK, (reg), (val))
+ ab3550_dev, I2C_BANK, (reg), (val)); \
+ printk(KERN_DEBUG "REG = 0x%02x, VAL = 0x%02x.\n", (reg), (val))
-#define MASK_SET_REG(reg, mask, val) \
- abx500_mask_and_set_register_interruptible( \
- ab3550_dev, I2C_BANK, (reg), (mask), (val))
+#define MASK_SET_REG(reg, mask, val) abx500_mask_and_set_register_interruptible( \
+ ab3550_dev, I2C_BANK, (reg), (mask), (val)); \
+ printk(KERN_DEBUG "REG = 0x%02x, MASK = 0x%02x, VAL = 0x%02x.\n", (reg), (mask), (val))
#define GET_REG(reg, val) abx500_get_register_interruptible( \
ab3550_dev, I2C_BANK, (reg), (val))
-#define AB3550_SUPPORTED_RATE (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+static enum enum_register outamp_indices[] = {
+ IDX_LINE1, IDX_LINE2, IDX_SPKR, IDX_EAR, IDX_AUXO1, IDX_AUXO2
+};
-#define AB3550_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE)
+static enum enum_control outamp_gain_controls[] = {
+ IDX_LINE1_Gain, IDX_LINE2_Gain, IDX_SPKR_Gain, IDX_EAR_Gain, IDX_AUXO1_Gain, IDX_AUXO2_Gain
+};
-static const u8 outamp_reg[] = {
- LINE1, LINE2, SPKR, EAR, AUXO1, AUXO2
+static enum enum_register outamp_adder_indices[] = {
+ IDX_LINE1_ADDER, IDX_LINE2_ADDER, IDX_SPKR_ADDER, IDX_EAR_ADDER, IDX_AUXO1_ADDER, IDX_AUXO2_ADDER
};
+
+//static const char* outamp_name[] = {
+// "LINE1", "LINE2", "SPKR", "EAR", "AUXO1", "AUXO2"
+//};
+
static const u8 outamp_gain_shift[] = {
LINEx_GAIN_SHIFT, LINEx_GAIN_SHIFT,
SPKR_GAIN_SHIFT, EAR_GAIN_SHIFT,
@@ -74,402 +144,268 @@ static const u8 outamp_pwr_mask[] = {
AUXOx_PWR_MASK, AUXOx_PWR_MASK
};
-static int ab3550_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai* dai);
-static int ab3550_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params, struct snd_soc_dai* dai);
-static void ab3550_pcm_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai* dai);
+struct codec_dai_private {
+ atomic_t substream_count;
+} privates[] = {
+ {ATOMIC_INIT(0)},
+ {ATOMIC_INIT(0)},
+};
-static int ab3550_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
- unsigned int freq, int dir);
-static int ab3550_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
+/* TODO: Add a clock use count.
+ Turn off the clock is there is no audio activity in the system.
+ */
-struct snd_soc_dai ab3550_codec_dai[] = {
- {
- .name = "ab3550_0",
- .playback = {
- .stream_name = "ab3550_0",
- .channels_min = 2,
- .channels_max = 2,
- .rates = AB3550_SUPPORTED_RATE,
- .formats = AB3550_SUPPORTED_FMT,
- },
- .capture = {
- .stream_name = "ab3550_0",
- .channels_min = 2,
- .channels_max = 2,
- .rates = AB3550_SUPPORTED_RATE,
- .formats = AB3550_SUPPORTED_FMT,
- },
- .ops = {
- .prepare = ab3550_pcm_prepare,
- .hw_params = ab3550_pcm_hw_params,
- .shutdown = ab3550_pcm_shutdown,
- .set_sysclk = ab3550_set_dai_sysclk,
- .set_fmt = ab3550_set_dai_fmt
- },
- },
- {
- .name = "ab3550_1",
- .playback = {
- .stream_name = "ab3550_1",
- .channels_min = 2,
- .channels_max = 2,
- .rates = AB3550_SUPPORTED_RATE,
- .formats = AB3550_SUPPORTED_FMT,
- },
- .capture = {
- .stream_name = "ab3550_1",
- .channels_min = 2,
- .channels_max = 2,
- .rates = AB3550_SUPPORTED_RATE,
- .formats = AB3550_SUPPORTED_FMT,
- },
- .ops = {
- .prepare = ab3550_pcm_prepare,
- .hw_params = ab3550_pcm_hw_params,
- .shutdown = ab3550_pcm_shutdown,
- .set_sysclk = ab3550_set_dai_sysclk,
- .set_fmt = ab3550_set_dai_fmt
- },
- }
-};
-EXPORT_SYMBOL_GPL(ab3550_codec_dai);
+
+
+// Controls -----------------------------------------------------------------------------------------------------------------------
struct ab3550_control {
const char *name;
u8 value;
- int is_enum;
+ enum enum_register reg_idx;
+ u8 reg_mask;
+ u8 reg_shift;
+ bool is_enum;
+ bool changed;
};
-/* When AB3550 starts up, these registers are initialized to 0 */
struct ab3550_control ab3550_ctl[] = {
- {"RX2 Select", 0, 1},
- {"DAC1 Routing", 0, 0},
- {"DAC2 Routing", 0, 0},
- {"DAC3 Routing", 0, 0},
- {"MIC1 Input Select", 0, 0},
- {"MIC2 Input Select", 0, 0},
- {"I2S0 TX", 0, 1},
- {"I2S1 TX", 0, 1},
- {"APGA1 Source", 0, 1},
- {"APGA2 Source", 0, 1},
- {"APGA1 Destination", 0, 0},
- {"APGA2 Destination", 0, 0},
- {"DAC1 Side Tone", 0, 1},
- {"DAC2 Side Tone", 0, 1},
- {"RX-DPGA1 Gain", 0x3D, 0},
- {"RX-DPGA2 Gain", 0x3D, 0},
- {"RX-DPGA3 Gain", 0x3D, 0},
- {"LINE1 Gain", 0x0A, 0},
- {"LINE2 Gain", 0x0A, 0},
- {"SPKR Gain", 0x0A, 0},
- {"EAR Gain", 0x0A, 0},
- {"AUXO1 Gain", 0x0A, 0},
- {"AUXO2 Gain", 0x0A, 0},
- {"MIC1 Gain", 0, 0},
- {"MIC2 Gain", 0, 0},
- {"TX-DPGA1 Gain", 0, 0},
- {"TX-DPGA2 Gain", 0, 0},
- {"ST-PGA1 Gain", 0, 0},
- {"ST-PGA2 Gain", 0, 0},
- {"APGA1 Gain", 0, 0},
- {"APGA2 Gain", 0, 0},
- {"DAC1 Power Mode", 0, 1},
- {"DAC2 Power Mode", 0, 1},
- {"DAC3 Power Mode", 0, 1},
- {"EAR Power Mode", 0, 1},
- {"AUXO Power Mode", 0, 1},
- {"LINE1 Inverse", 0, 0},
- {"LINE2 Inverse", 0, 0},
- {"AUXO1 Inverse", 0, 0},
- {"AUXO2 Inverse", 0, 0},
- {"AUXO1 Pulldown", 0, 0},
- {"AUXO2 Pulldown", 0, 0},
- {"VMID1", 0, 0},
- {"VMID2", 0, 0},
- {"MIC1-1 MBias", 0, 1},
- {"MIC1-2 MBias", 0, 1},
- {"MIC2-1 MBias", 0, 1},
- {"MIC2-2 MBias", 0, 1},
- {"MBIAS1 HiZ Option", 0, 1},
- {"MBIAS2 HiZ Option", 0, 1},
- {"MBIAS2 Output Voltage", 0, 1},
- {"MBIAS2 Internal Resistor", 0, 1},
- {"MIC1 Input Impedance", 0, 1},
- {"MIC2 Input Impedance", 0, 1},
- {"TX1 HP Filter", 0, 1},
- {"TX2 HP Filter", 0, 1},
- {"LINEIN1 Pre-charge", 0, 0},
- {"LINEIN2 Pre-charge", 0, 0},
- {"MIC1P1 Pre-charge", 0, 0},
- {"MIC1P2 Pre-charge", 0, 0},
- {"MIC1N1 Pre-charge", 0, 0},
- {"MIC1N2 Pre-charge", 0, 0},
- {"MIC2P1 Pre-charge", 0, 0},
- {"MIC2P2 Pre-charge", 0, 0},
- {"MIC2N1 Pre-charge", 0, 0},
- {"MIC2N2 Pre-charge", 0, 0},
- {"ST1 HP Filter", 0, 1},
- {"ST2 HP Filter", 0, 1},
- {"I2S0 Word Length", 0, 1},
- {"I2S1 Word Length", 0, 1},
- {"I2S0 Mode", 0, 1},
- {"I2S1 Mode", 0, 1},
- {"I2S0 Tri-state", 0, 1},
- {"I2S1 Tri-state", 0, 1},
- {"I2S0 Pulldown", 0, 1},
- {"I2S1 Pulldown", 0, 1},
- {"I2S0 Sample Rate", 0, 1},
- {"I2S1 Sample Rate", 0, 1},
- {"Interface Loop", 0, 0},
- {"Interface Swap", 0, 0},
- {"Commit", 0, 0},
+// name value reg_idx reg_mask reg_shift is_enum changed
+ {"RX2 Select", 0, IDX_RX2, RX2_IF_SELECT_MASK, RX2_IF_SELECT_SHIFT,true, false},
+ {"DAC1 Routing", 0x05, IDX_RX1, DACx_PWR_MASK, DACx_PWR_SHIFT, false, false}, // DAC1 -> AUXO1, SPKR
+ {"DAC2 Routing", 0x06, IDX_RX2, DACx_PWR_MASK, DACx_PWR_SHIFT, false, false}, // DAC2 -> AUXO1, SPKR
+ {"DAC3 Routing", 0x00, IDX_RX3, DACx_PWR_MASK, DACx_PWR_SHIFT, false, false},
+ {"MIC1 Input Select", 0x30, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC2 Input Select", 0x00, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"I2S0 Input Select", 0x11, IDX_TX1, 0x00, 0x00, true, false},
+ {"I2S1 Input Select", 0x22, IDX_TX2, 0x00, 0x00, true, false},
+ {"APGA1 Source", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"APGA2 Source", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"APGA1 Destination", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"APGA2 Destination", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"DAC1 Side Tone", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"DAC2 Side Tone", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"RX-DPGA1 Gain", 0x3A, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"RX-DPGA2 Gain", 0x3A, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"RX-DPGA3 Gain", 0x00, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"LINE1 Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB
+ {"LINE2 Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB
+ {"SPKR Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB
+ {"EAR Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB
+ {"AUXO1 Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB
+ {"AUXO2 Gain", 0x4, IDX_UNKNOWN, 0x00, 0x00, false, false}, // -6 dB
+ {"MIC1 Gain", 0x2, IDX_MIC1_GAIN, MICx_GAIN_MASK, MICx_GAIN_SHIFT, false, false},
+ {"MIC2 Gain", 0x2, IDX_MIC2_GAIN, MICx_GAIN_MASK, MICx_GAIN_SHIFT, false, false},
+ {"TX-DPGA1 Gain", 0x06, IDX_TX_DIGITAL_PGA1, TXDPGAx_MASK, TXDPGAx_SHIFT, false, false},
+ {"TX-DPGA2 Gain", 0x06, IDX_TX_DIGITAL_PGA2, TXDPGAx_MASK, TXDPGAx_SHIFT, false, false},
+ {"ST-PGA1 Gain", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"ST-PGA2 Gain", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"APGA1 Gain", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"APGA2 Gain", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"DAC1 Power Mode", 0x0, IDX_UNKNOWN, 0x00, 0x00, true, false}, // 100%
+ {"DAC2 Power Mode", 0x0, IDX_UNKNOWN, 0x00, 0x00, true, false}, // 100%
+ {"DAC3 Power Mode", 0x3, IDX_UNKNOWN, 0x00, 0x00, true, false}, // Do not use
+ {"EAR Power Mode", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"AUXO Power Mode", 0x4, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"LINE1 Inverse", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"LINE2 Inverse", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"AUXO1 Inverse", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"AUXO2 Inverse", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"AUXO1 Pulldown", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"AUXO2 Pulldown", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"VMID1", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"VMID2", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC1 MBias", 0x01, IDX_UNKNOWN, 0x00, 0x00, true, false}, // MBias 1 On
+ {"MIC2 MBias", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"MBIAS1 HiZ Option", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"MBIAS2 HiZ Option", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"MBIAS2 Output Voltage", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"MBIAS2 Internal Resistor",0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"MIC1 Input Impedance", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"MIC2 Input Impedance", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"TX1 HP Filter", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"TX2 HP Filter", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"LINEIN1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"LINEIN2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC1P1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC1P2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC1N1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC1N2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC2P1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC2P2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC2N1 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"MIC2N2 Pre-charge", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"ST1 HP Filter", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"ST2 HP Filter", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S0 Word Length", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S1 Word Length", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S0 Mode", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S1 Mode", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S0 Tri-state", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S1 Tri-state", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S0 Pulldown", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S1 Pulldown", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S0 Sample Rate", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"I2S1 Sample Rate", 0, IDX_UNKNOWN, 0x00, 0x00, true, false},
+ {"Interface Loop", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"Interface Swap", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"Voice Call", 0, IDX_UNKNOWN, 0x00, 0x00, false, false},
+ {"Commit", 0, IDX_UNKNOWN, 0x00, 0x00, false, false}
};
/* control No. 0 correpsonds to bit No. 0 in this array.
If a control value has been changed, but not commited
to the AB3550. Its corresponding bit is set.
*/
-static unsigned long control_changed[] = {
- 0, 0, 0, 0
-};
+//static unsigned long changed_controls[BIT_WORD(ARRAY_SIZE(ab3550_ctl)) + 1];
static const char *enum_rx2_select[] = {"I2S0", "I2S1"};
-static const char *enum_i2s_tx[] =
-{"TX1", "TX2", "tri-state", "mute"};
-
-static const char *enum_apga1_source[] =
-{"None", "LINEIN1", "MIC1", "MIC2"};
-
-static const char *enum_apga2_source[] =
-{"None", "LINEIN2", "MIC1", "MIC2"};
-
-static const char *enum_dac1_side_tone[] =
-{"None", "TX1", "TX2"};
-
-static const char *enum_dac1_power_mode[] =
-{"100%", "75%", "50%"};
-
-static const char *enum_ear_power_mode[] =
-{"100%", "75%"};
-
-static const char *enum_auxo_power_mode[] =
-{"100%", "75%", "50%", "25%"};
-
-static const char *enum_mic_bias_connection[] =
-{"MIC Bias 1", "MIC Bias 2"};
-
-static const char *enum_mbias1_hiz_option[] =
-{"GND", "HiZ"};
-
-static const char *enum_mbias2_output_voltage[] =
-{"2.0v", "2.2v"};
-
-static const char *enum_mbias2_internal_resistor[] =
-{"connected", "bypassed"};
-
-static const char *enum_mic1_input_impedance[] =
-{"12.5 kohm", "25 kohm", "50 kohm"};
-
-static const char *enum_tx1_hp_filter[] =
-{"HP3", "HP1", "bypass"};
-
-static const char *enum_i2s_word_length[] =
-{"16 bits", "24 bits"};
-
-static const char *enum_i2s_mode[] =
-{"Master Mode", "Slave Mode"};
-
-static const char *enum_i2s_tristate[] =
-{"Normal", "Tri-state"};
-
-static const char *enum_i2s_pulldown[] =
-{"disconnected", "connected"};
-
-static const char *enum_i2s_sample_rate[] =
-{"8 kHz", "16 kHz", "44.1 kHz", "48 kHz"};
-
-
-static struct soc_enum ab3550_enum[] = {
- /* RX2 Select */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_rx2_select), enum_rx2_select),
- /* I2S0 TX */
- SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_tx), enum_i2s_tx),
- /* I2S1 TX */
- SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_tx), enum_i2s_tx),
- /* APGA1 Source */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga1_source),
- enum_apga1_source),
- /* APGA2 Source */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga2_source),
- enum_apga2_source),
- /* DAC1 Side Tone */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_side_tone),
- enum_dac1_side_tone),
- /* DAC2 Side Tone */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_side_tone),
- enum_dac1_side_tone),
- /* DAC1 Power Mode */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_power_mode),
- enum_dac1_power_mode),
- /* DAC2 Power Mode */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_power_mode),
- enum_dac1_power_mode),
- /* DAC3 Power Mode */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_power_mode),
- enum_dac1_power_mode),
- /* EAR Power Mode */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_ear_power_mode),
- enum_ear_power_mode),
- /* AUXO Power Mode */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_auxo_power_mode),
- enum_auxo_power_mode),
- /* MIC Bias Connection */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection),
- enum_mic_bias_connection),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection),
- enum_mic_bias_connection),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection),
- enum_mic_bias_connection),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection),
- enum_mic_bias_connection),
- /* MBIAS1 HiZ Option */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias1_hiz_option),
- enum_mbias1_hiz_option),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias1_hiz_option),
- enum_mbias1_hiz_option),
- /* MBIAS2 Output voltage */
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_output_voltage),
- enum_mbias2_output_voltage),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_internal_resistor),
- enum_mbias2_internal_resistor),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic1_input_impedance),
- enum_mic1_input_impedance),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic1_input_impedance),
- enum_mic1_input_impedance),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter),
- enum_tx1_hp_filter),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter),
- enum_tx1_hp_filter),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter),
- enum_tx1_hp_filter),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter),
- enum_tx1_hp_filter),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length),
- enum_i2s_word_length),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length),
- enum_i2s_word_length),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode),
- enum_i2s_mode),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode),
- enum_i2s_mode),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate),
- enum_i2s_tristate),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate),
- enum_i2s_tristate),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown),
- enum_i2s_pulldown),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown),
- enum_i2s_pulldown),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate),
- enum_i2s_sample_rate),
- SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate),
- enum_i2s_sample_rate),
-
-};
+static const char *enum_i2s_input_select[] = {"tri-state", "MIC1", "MIC2", "mute"};
+static const char *enum_apga1_source[] = {"None", "LINEIN1", "MIC1", "MIC2"};
+static const char *enum_apga2_source[] = {"None", "LINEIN2", "MIC1", "MIC2"};
+static const char *enum_dac_side_tone[] = {"None", "TX1", "TX2"};
+static const char *enum_dac_power_mode[] = {"100%", "75%", "50%"};
+static const char *enum_ear_power_mode[] = {"100%", "75%"};
+static const char *enum_auxo_power_mode[] = {"100%", "75%", "50%", "25%"};
+static const char *enum_mbias[] = {"Off", "On"};
+static const char *enum_mbias_hiz_option[] = {"GND", "HiZ"};
+static const char *enum_mbias2_output_voltage[] = {"2.0v", "2.2v"};
+static const char *enum_mbias2_internal_resistor[] = {"connected", "bypassed"};
+static const char *enum_mic_input_impedance[] = {"12.5 kohm", "25 kohm", "50 kohm"};
+static const char *enum_hp_filter[] = {"HP3", "HP1", "bypass"};
+static const char *enum_i2s_word_length[] = {"16 bits", "24 bits"};
+static const char *enum_i2s_mode[] = {"Master Mode", "Slave Mode"};
+static const char *enum_i2s_tristate[] = {"Normal", "Tri-state"};
+static const char *enum_i2s_pulldown[] = {"disconnected", "connected"};
+static const char *enum_i2s_sample_rate[] = {"8 kHz", "16 kHz", "44.1 kHz", "48 kHz"};
+
+static struct soc_enum soc_enum_rx2_select = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_rx2_select), enum_rx2_select); // RX2 Select
+static struct soc_enum soc_enum_i2s0_input_select = SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_input_select), enum_i2s_input_select); // I2S0 Input Select
+static struct soc_enum soc_enum_i2s1_input_select = SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_input_select), enum_i2s_input_select); // I2S1 Input Select
+static struct soc_enum soc_enum_apga1_source = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga1_source), enum_apga1_source); // APGA1 Source
+static struct soc_enum soc_enum_apga2_source = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga2_source), enum_apga2_source); // APGA2 Source
+static struct soc_enum soc_enum_dac1_side_tone = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_side_tone), enum_dac_side_tone); // DAC1 Side Tone
+static struct soc_enum soc_enum_dac2_side_tone = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_side_tone), enum_dac_side_tone); // DAC2 Side Tone
+static struct soc_enum soc_enum_dac1_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_power_mode), enum_dac_power_mode); // DAC1 Power Mode
+static struct soc_enum soc_enum_dac2_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_power_mode), enum_dac_power_mode); // DAC2 Power Mode
+static struct soc_enum soc_enum_dac3_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_power_mode), enum_dac_power_mode); // DAC3 Power Mode
+static struct soc_enum soc_enum_ear_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_ear_power_mode), enum_ear_power_mode); // EAR Power Mode
+static struct soc_enum soc_enum_auxo_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_auxo_power_mode), enum_auxo_power_mode); // AUXO Power Mode
+static struct soc_enum soc_enum_mic1_mbias = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias), enum_mbias); // MIC Bias Connection
+static struct soc_enum soc_enum_mic2_mbias = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias), enum_mbias); // MIC Bias Connection
+static struct soc_enum soc_enum_mbias1_hiz_option = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias_hiz_option), enum_mbias_hiz_option); // MBIAS1 HiZ Option
+static struct soc_enum soc_enum_mbias2_hiz_option = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias_hiz_option), enum_mbias_hiz_option); // MBIAS1 HiZ Option
+static struct soc_enum soc_enum_mbias2_output_voltage = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_output_voltage), enum_mbias2_output_voltage); // MBIAS2 Output voltage
+static struct soc_enum soc_enum_mbias2_internal_resistor = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_internal_resistor), enum_mbias2_internal_resistor); //
+static struct soc_enum soc_enum_mic1_input_impedance = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_input_impedance), enum_mic_input_impedance); //
+static struct soc_enum soc_enum_mic2_input_impedance = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_input_impedance), enum_mic_input_impedance); //
+static struct soc_enum soc_enum_tx1_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); //
+static struct soc_enum soc_enum_tx2_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); //
+static struct soc_enum soc_enum_st1_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); //
+static struct soc_enum soc_enum_st2_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); //
+static struct soc_enum soc_enum_i2s0_word_length = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length), enum_i2s_word_length); //
+static struct soc_enum soc_enum_i2s1_word_length = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length), enum_i2s_word_length); //
+static struct soc_enum soc_enum_i2s0_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode), enum_i2s_mode); //
+static struct soc_enum soc_enum_i2s1_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode), enum_i2s_mode); //
+static struct soc_enum soc_enum_i2s0_tristate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate), enum_i2s_tristate); //
+static struct soc_enum soc_enum_i2s1_tristate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate), enum_i2s_tristate); //
+static struct soc_enum soc_enum_i2s0_pulldown = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown), enum_i2s_pulldown); //
+static struct soc_enum soc_enum_i2s1_pulldown = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown), enum_i2s_pulldown); //
+static struct soc_enum soc_enum_i2s0_sample_rate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate), enum_i2s_sample_rate); //
+static struct soc_enum soc_enum_i2s1_sample_rate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate), enum_i2s_sample_rate); //
static struct snd_kcontrol_new ab3550_snd_controls[] = {
/* RX Routing */
- SOC_ENUM("RX2 Select", ab3550_enum[0]),
- SOC_SINGLE("DAC1 Routing", 0, 0, 0x3f, 0),
- SOC_SINGLE("DAC2 Routing", 0, 0, 0x3f, 0),
- SOC_SINGLE("DAC3 Routing", 0, 0, 0x3f, 0),
+ SOC_ENUM( "RX2 Select", soc_enum_rx2_select),
+ SOC_SINGLE( "DAC1 Routing", 0, 0, 0x3f, 0),
+ SOC_SINGLE( "DAC2 Routing", 0, 0, 0x3f, 0),
+ SOC_SINGLE( "DAC3 Routing", 0, 0, 0x3f, 0),
/* TX Routing */
- SOC_SINGLE("MIC1 Input Select", 0, 0, 0xff, 0),
- SOC_SINGLE("MIC2 Input Select", 0, 0, 0x3f, 0),
- SOC_ENUM("I2S0 TX", ab3550_enum[1]),
- SOC_ENUM("I2S1 TX", ab3550_enum[2]),
+ SOC_SINGLE( "MIC1 Input Select", 0, 0, 0xff, 0),
+ SOC_SINGLE( "MIC2 Input Select", 0, 0, 0x3f, 0),
+ SOC_ENUM( "I2S0 Input Select", soc_enum_i2s0_input_select),
+ SOC_ENUM( "I2S1 Input Select", soc_enum_i2s1_input_select),
/* Routing of Side Tone and Analop Loop */
- SOC_ENUM("APGA1 Source", ab3550_enum[3]),
- SOC_ENUM("APGA2 Source", ab3550_enum[4]),
- SOC_SINGLE("APGA1 Destination", 0, 0, 0x3f, 0),
- SOC_SINGLE("APGA2 Destination", 0, 0, 0x3f, 0),
- SOC_ENUM("DAC1 Side Tone", ab3550_enum[5]),
- SOC_ENUM("DAC2 Side Tone", ab3550_enum[5]),
+ SOC_ENUM( "APGA1 Source", soc_enum_apga1_source),
+ SOC_ENUM( "APGA2 Source", soc_enum_apga2_source),
+ SOC_SINGLE( "APGA1 Destination", 0, 0, 0x3f, 0),
+ SOC_SINGLE( "APGA2 Destination", 0, 0, 0x3f, 0),
+ SOC_ENUM( "DAC1 Side Tone", soc_enum_dac1_side_tone),
+ SOC_ENUM( "DAC2 Side Tone", soc_enum_dac2_side_tone),
/* RX Volume Control */
- SOC_SINGLE("RX-DPGA1 Gain", 0, 0, 66, 0),
- SOC_SINGLE("RX-DPGA2 Gain", 0, 0, 66, 0),
- SOC_SINGLE("RX-DPGA3 Gain", 0, 0, 66, 0),
- SOC_SINGLE("LINE1 Gain", 0, 0, 10, 0),
- SOC_SINGLE("LINE2 Gain", 0, 0, 10, 0),
- SOC_SINGLE("SPKR Gain", 0, 0, 22, 0),
- SOC_SINGLE("EAR Gain", 0, 0, 14, 0),
- SOC_SINGLE("AUXO1 Gain", 0, 0, 12, 0),
- SOC_SINGLE("AUXO2 Gain", 0, 0, 12, 0),
+ SOC_SINGLE( "RX-DPGA1 Gain", 0, 0, 66, 0),
+ SOC_SINGLE( "RX-DPGA2 Gain", 0, 0, 66, 0),
+ SOC_SINGLE( "RX-DPGA3 Gain", 0, 0, 66, 0),
+ SOC_SINGLE( "LINE1 Gain", 0, 0, 10, 0),
+ SOC_SINGLE( "LINE2 Gain", 0, 0, 10, 0),
+ SOC_SINGLE( "SPKR Gain", 0, 0, 22, 0),
+ SOC_SINGLE( "EAR Gain", 0, 0, 14, 0),
+ SOC_SINGLE( "AUXO1 Gain", 0, 0, 12, 0),
+ SOC_SINGLE( "AUXO2 Gain", 0, 0, 12, 0),
/* TX Volume Control */
- SOC_SINGLE("MIC1 Gain", 0, 0, 10, 0),
- SOC_SINGLE("MIC2 Gain", 0, 0, 10, 0),
- SOC_SINGLE("TX-DPGA1 Gain", 0, 0, 15, 0),
- SOC_SINGLE("TX-DPGA2 Gain", 0, 0, 15, 0),
+ SOC_SINGLE( "MIC1 Gain", 0, 0, 10, 0),
+ SOC_SINGLE( "MIC2 Gain", 0, 0, 10, 0),
+ SOC_SINGLE( "TX-DPGA1 Gain", 0, 0, 15, 0),
+ SOC_SINGLE( "TX-DPGA2 Gain", 0, 0, 15, 0),
/* Volume Control of Side Tone and Analog Loop */
- SOC_SINGLE("ST-PGA1 Gain", 0, 0, 10, 0),
- SOC_SINGLE("ST-PGA2 Gain", 0, 0, 10, 0),
- SOC_SINGLE("APGA1 Gain", 0, 0, 28, 0),
- SOC_SINGLE("APGA2 Gain", 0, 0, 28, 0),
+ SOC_SINGLE( "ST-PGA1 Gain", 0, 0, 10, 0),
+ SOC_SINGLE( "ST-PGA2 Gain", 0, 0, 10, 0),
+ SOC_SINGLE( "APGA1 Gain", 0, 0, 28, 0),
+ SOC_SINGLE( "APGA2 Gain", 0, 0, 28, 0),
/* RX Properties */
- SOC_ENUM("DAC1 Power Mode", ab3550_enum[6]),
- SOC_ENUM("DAC2 Power Mode", ab3550_enum[7]),
- SOC_ENUM("DAC3 Power Mode", ab3550_enum[8]),
- SOC_ENUM("EAR Power Mode", ab3550_enum[9]),
- SOC_ENUM("AUXO Power Mode", ab3550_enum[10]),
- SOC_SINGLE("LINE1 Inverse", 0, 0, 1, 0),
- SOC_SINGLE("LINE2 Inverse", 0, 0, 1, 0),
- SOC_SINGLE("AUXO1 Inverse", 0, 0, 1, 0),
- SOC_SINGLE("AUXO2 Inverse", 0, 0, 1, 0),
- SOC_SINGLE("AUXO1 Pulldown", 0, 0, 1, 0),
- SOC_SINGLE("AUXO2 Pulldown", 0, 0, 1, 0),
+ SOC_ENUM( "DAC1 Power Mode", soc_enum_dac1_power_mode),
+ SOC_ENUM( "DAC2 Power Mode", soc_enum_dac2_power_mode),
+ SOC_ENUM( "DAC3 Power Mode", soc_enum_dac3_power_mode),
+ SOC_ENUM( "EAR Power Mode", soc_enum_ear_power_mode),
+ SOC_ENUM( "AUXO Power Mode", soc_enum_auxo_power_mode),
+ SOC_SINGLE( "LINE1 Inverse", 0, 0, 1, 0),
+ SOC_SINGLE( "LINE2 Inverse", 0, 0, 1, 0),
+ SOC_SINGLE( "AUXO1 Inverse", 0, 0, 1, 0),
+ SOC_SINGLE( "AUXO2 Inverse", 0, 0, 1, 0),
+ SOC_SINGLE( "AUXO1 Pulldown", 0, 0, 1, 0),
+ SOC_SINGLE( "AUXO2 Pulldown", 0, 0, 1, 0),
/* TX Properties */
- SOC_SINGLE("VMID1", 0, 0, 0xff, 0),
- SOC_SINGLE("VMID2", 0, 0, 0xff, 0),
- SOC_ENUM("MIC1-1 MBias", ab3550_enum[11]),
- SOC_ENUM("MIC1-2 MBias", ab3550_enum[12]),
- SOC_ENUM("MIC2-1 MBias", ab3550_enum[13]),
- SOC_ENUM("MIC2-2 MBias", ab3550_enum[14]),
- SOC_ENUM("MBIAS1 HiZ Option", ab3550_enum[15]),
- SOC_ENUM("MBIAS2 HiZ Option", ab3550_enum[16]),
- SOC_ENUM("MBIAS2 Output Voltage", ab3550_enum[17]),
- SOC_ENUM("MBIAS2 Internal Resistor", ab3550_enum[18]),
- SOC_ENUM("MIC1 Input Impedance", ab3550_enum[19]),
- SOC_ENUM("MIC2 Input Impedance", ab3550_enum[20]),
- SOC_ENUM("TX1 HP Filter", ab3550_enum[21]),
- SOC_ENUM("TX2 HP Filter", ab3550_enum[22]),
- SOC_SINGLE("LINEIN1 Pre-charge", 0, 0, 100, 0),
- SOC_SINGLE("LINEIN2 Pre-charge", 0, 0, 100, 0),
- SOC_SINGLE("MIC1P1 Pre-charge", 0, 0, 100, 0),
- SOC_SINGLE("MIC1P2 Pre-charge", 0, 0, 100, 0),
- SOC_SINGLE("MIC1N1 Pre-charge", 0, 0, 100, 0),
- SOC_SINGLE("MIC1N2 Pre-charge", 0, 0, 100, 0),
- SOC_SINGLE("MIC2P1 Pre-charge", 0, 0, 100, 0),
- SOC_SINGLE("MIC2P2 Pre-charge", 0, 0, 100, 0),
- SOC_SINGLE("MIC2N1 Pre-charge", 0, 0, 100, 0),
- SOC_SINGLE("MIC2N2 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "VMID1", 0, 0, 0xff, 0),
+ SOC_SINGLE( "VMID2", 0, 0, 0xff, 0),
+ SOC_ENUM( "MIC1 MBias", soc_enum_mic1_mbias),
+ SOC_ENUM( "MIC2 MBias", soc_enum_mic2_mbias),
+ SOC_ENUM( "MBIAS1 HiZ Option", soc_enum_mbias1_hiz_option),
+ SOC_ENUM( "MBIAS2 HiZ Option", soc_enum_mbias2_hiz_option),
+ SOC_ENUM( "MBIAS2 Output Voltage", soc_enum_mbias2_output_voltage),
+ SOC_ENUM( "MBIAS2 Internal Resistor", soc_enum_mbias2_internal_resistor),
+ SOC_ENUM( "MIC1 Input Impedance", soc_enum_mic1_input_impedance),
+ SOC_ENUM( "MIC2 Input Impedance", soc_enum_mic2_input_impedance),
+ SOC_ENUM( "TX1 HP Filter", soc_enum_tx1_hp_filter),
+ SOC_ENUM( "TX2 HP Filter", soc_enum_tx2_hp_filter),
+ SOC_SINGLE( "LINEIN1 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "LINEIN2 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "MIC1P1 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "MIC1P2 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "MIC1N1 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "MIC1N2 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "MIC2P1 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "MIC2P2 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "MIC2N1 Pre-charge", 0, 0, 100, 0),
+ SOC_SINGLE( "MIC2N2 Pre-charge", 0, 0, 100, 0),
/* Side Tone and Analog Loop Properties */
- SOC_ENUM("ST1 HP Filter", ab3550_enum[23]),
- SOC_ENUM("ST2 HP Filter", ab3550_enum[24]),
+ SOC_ENUM( "ST1 HP Filter", soc_enum_st1_hp_filter),
+ SOC_ENUM( "ST2 HP Filter", soc_enum_st2_hp_filter),
/* I2S Interface Properties */
- SOC_ENUM("I2S0 Word Length", ab3550_enum[25]),
- SOC_ENUM("I2S1 Word Length", ab3550_enum[26]),
- SOC_ENUM("I2S0 Mode", ab3550_enum[27]),
- SOC_ENUM("I2S1 Mode", ab3550_enum[28]),
- SOC_ENUM("I2S0 tri-state", ab3550_enum[29]),
- SOC_ENUM("I2S1 tri-state", ab3550_enum[30]),
- SOC_ENUM("I2S0 pulldown", ab3550_enum[31]),
- SOC_ENUM("I2S1 pulldown", ab3550_enum[32]),
- SOC_ENUM("I2S0 Sample Rate", ab3550_enum[33]),
- SOC_ENUM("I2S1 Sample Rate", ab3550_enum[34]),
- SOC_SINGLE("Interface Loop", 0, 0, 0x0f, 0),
- SOC_SINGLE("Interface Swap", 0, 0, 0x1f, 0),
+ SOC_ENUM( "I2S0 Word Length", soc_enum_i2s0_word_length),
+ SOC_ENUM( "I2S1 Word Length", soc_enum_i2s1_word_length),
+ SOC_ENUM( "I2S0 Mode", soc_enum_i2s0_mode),
+ SOC_ENUM( "I2S1 Mode", soc_enum_i2s1_mode),
+ SOC_ENUM( "I2S0 tri-state", soc_enum_i2s0_tristate),
+ SOC_ENUM( "I2S1 tri-state", soc_enum_i2s1_tristate),
+ SOC_ENUM( "I2S0 pulldown", soc_enum_i2s0_pulldown),
+ SOC_ENUM( "I2S1 pulldown", soc_enum_i2s1_pulldown),
+ SOC_ENUM( "I2S0 Sample Rate", soc_enum_i2s0_sample_rate),
+ SOC_ENUM( "I2S1 Sample Rate", soc_enum_i2s1_sample_rate),
+ SOC_SINGLE( "Interface Loop", 0, 0, 0x0f, 0),
+ SOC_SINGLE( "Interface Swap", 0, 0, 0x1f, 0),
/* Miscellaneous */
- SOC_SINGLE("Commit", 0, 0, 1, 0)
+ SOC_SINGLE( "Voice Call", 0, 0, 1, 0),
+ SOC_SINGLE( "Commit", 0, 0, 1, 0)
};
static const struct snd_soc_dapm_widget ab3550_dapm_widgets[] = {
@@ -478,11 +414,12 @@ static const struct snd_soc_dapm_widget ab3550_dapm_widgets[] = {
static const struct snd_soc_dapm_route intercon[] = {
};
-static unsigned int ab3550_get_control_value(struct snd_soc_codec *codec,
- unsigned int ctl)
+static unsigned int ab3550_get_control_value(struct snd_soc_codec *codec, unsigned int ctl)
{
if (ctl >= ARRAY_SIZE(ab3550_ctl))
return -EINVAL;
+ //printk(KERN_DEBUG "%s: returning value of control %s: 0x%02x.\n",
+ // __func__, ab3550_ctl[ctl].name, ab3550_ctl[ctl].value);
return ab3550_ctl[ctl].value;
}
@@ -498,25 +435,27 @@ static unsigned int get_control_index(const char *name)
static unsigned int get_control_value_by_name(const char *name)
{
- int i, n;
- for(i = 0, n = ARRAY_SIZE(ab3550_ctl); i < n; i++) {
- if (strcmp(ab3550_ctl[i].name, name) == 0)
- break;
- }
+ int i = get_control_index(name);
return ab3550_ctl[i].value;
}
+static unsigned int get_control_changed_by_name(const char *name)
+{
+ int i = get_control_index(name);
+ return ab3550_ctl[i].changed;
+}
+
static int gain_ramp(u8 reg, u8 target, int shift, u8 mask)
{
- u8 curr, step;
+// u8 curr;
int ret = 0;
// printk(KERN_DEBUG "%s invoked.\n", __func__);
- if ((ret = GET_REG(reg, &curr))) {
- return ret;
- }
- if (curr == target)
- return 0;
+// if ((ret = GET_REG(reg, &curr))) {
+// return ret;
+// }
+// if (curr == target)
+// return 0;
// step = curr < target ? 1 : -1;
// while (!ret && curr != target) {
/* At least 15ms are required, so I take 16ms :-) */
@@ -527,125 +466,79 @@ static int gain_ramp(u8 reg, u8 target, int shift, u8 mask)
return ret;
}
-static int ab3550_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
- unsigned int freq, int dir)
+static void ab3550_print_registers(int stream_dir)
{
- printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__);
- return 0;
+ int i;
+ u8 val;
+ for (i = 0; i < ARRAY_SIZE(ab3550_registers); i++) {
+ GET_REG(ab3550_registers[i].address, &val);
+ printk(KERN_DEBUG "REGISTERS: %s = 0x%02x\n", ab3550_registers[i].name, val);
+ }
}
-static int ab3550_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
-{
- printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
- return 0;
+static void ab3550_toggle_output_amp(int idx_amp, u8 val) {
+ struct ab3550_register* reg = &(ab3550_registers[(int)outamp_indices[idx_amp]]);
+ MASK_SET_REG(reg->address,
+ outamp_pwr_mask[idx_amp],
+ val << outamp_pwr_shift[idx_amp]);
+
+ printk(KERN_DEBUG "DORIANO: %s turned %s.\n", reg->name, val == 0 ? "off" : "on");
}
-static int ab3550_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai* dai)
+static void ab3550_toggle_mic_amp(int idx, u8 val) {
+ enum enum_register idx_reg_mic_amp[] = {IDX_MIC1_GAIN, IDX_MIC2_GAIN}; // PWR is in located in the gain regs.
+ struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_mic_amp[idx]]);
+ MASK_SET_REG(reg->address,
+ MICx_PWR_MASK,
+ val << MICx_PWR_SHIFT);
+
+ printk(KERN_DEBUG "DORIANO: %s turned %s.\n", reg->name, val == 0 ? "off" : "on");
+}
+
+static void ab3550_toggle_DAC(int idx_DAC, u8 val)
{
- u8 val;
- printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
- MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 1 << CLOCK_ENABLE_SHIFT);
- /* Turn on the master generator */
- MASK_SET_REG(INTERFACE0, MASTER_GENx_PWR_MASK,
- 1 << MASTER_GENx_PWR_SHIFT);
- GET_REG(CLOCK, &val);
- printk(KERN_DEBUG "%s: CLOCK = %02x.\n", __func__, val);
- GET_REG(INTERFACE0, &val);
- printk(KERN_DEBUG "%s: INTERFACE0 = %02x.\n", __func__, val);
- GET_REG(RX1, &val);
- printk(KERN_DEBUG "%s: RX1 = %02x.\n", __func__, val);
- GET_REG(RX2, &val);
- printk(KERN_DEBUG "%s: RX2 = %02x.\n", __func__, val);
- GET_REG(SPKR_ADDER, &val);
- printk(KERN_DEBUG "%s: SPKR_ADDER = %02x.\n", __func__, val);
- GET_REG(AUXO1_ADDER, &val);
- printk(KERN_DEBUG "%s: AUXO1_ADDER = %02x.\n", __func__, val);
- GET_REG(AUXO2_ADDER, &val);
- printk(KERN_DEBUG "%s: AUXO2_ADDER = %02x.\n", __func__, val);
- GET_REG(SPKR, &val);
- printk(KERN_DEBUG "%s: SPKR = %02x.\n", __func__, val);
- GET_REG(AUXO1, &val);
- printk(KERN_DEBUG "%s: AUXO1 = %02x.\n", __func__, val);
- GET_REG(AUXO2, &val);
- printk(KERN_DEBUG "%s: AUXO2 = %02x.\n", __func__, val);
- GET_REG(RX1_DIGITAL_PGA, &val);
- printk(KERN_DEBUG "%s: RX1_DIGITAL_PGA = %02x.\n", __func__, val);
- GET_REG(RX2_DIGITAL_PGA, &val);
- printk(KERN_DEBUG "%s: RX2_DIGITAL_PGA = %02x.\n", __func__, val);
- return 0;
+ enum enum_register idx_reg_DAC[] = {IDX_RX1, IDX_RX2, IDX_RX3};
+ struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_DAC[idx_DAC]]);
+ MASK_SET_REG(reg->address,
+ DACx_PWR_MASK | RXx_PWR_MASK,
+ (val << RXx_PWR_SHIFT | val << DACx_PWR_SHIFT));
+
+ printk(KERN_DEBUG "DORIANO: DAC%d turned %s.\n", idx_DAC + 1, val == 0 ? "off" : "on");
}
-static void ab3550_pcm_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai* dai)
+static void ab3550_toggle_output_adder(int idx_adder, u8 val)
{
int i;
- printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
- MASK_SET_REG(INTERFACE0, MASTER_GENx_PWR_MASK, 0);
- MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 0);
- for (i = 0; i < ARRAY_SIZE(outamp_reg); i++) {
- MASK_SET_REG(outamp_reg[i], outamp_pwr_mask[i], 0);
- }
+ struct ab3550_register* reg = &(ab3550_registers[(int)outamp_adder_indices[idx_adder]]);
+
+ MASK_SET_REG(reg->address,
+ DAC1_TO_ADDER_MASK | DAC2_TO_ADDER_MASK | DAC3_TO_ADDER_MASK,
+ val);
+
+ for (i = 0; i < 3; i++)
+ printk(KERN_DEBUG "DORIANO: DAC%d -> %s: %s.\n", i + 1, reg->name, (val & 1 << i) ? "on" : "off");
}
-static int set_up_speaker_playback(void)
+static void ab3550_toggle_ADC(int idx_ADC, u8 val)
{
- printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
- MASK_SET_REG(CLOCK, CLOCK_REF_SELECT_MASK,
- 1 << CLOCK_REF_SELECT_SHIFT);
+ enum enum_register idx_reg_ADC[] = {IDX_TX1, IDX_TX2};
+ struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_ADC[idx_ADC]]);
+ MASK_SET_REG(reg->address,
+ ADCx_PWR_MASK | TXx_PWR_MASK,
+ ( val << ADCx_PWR_SHIFT | val << TXx_PWR_SHIFT));
- /* Sample rate 48 kHz */
- SET_REG(INTERFACE0, 0x03);
- printk(KERN_DEBUG "%s: interface 0 is setup", __func__);
-
- MASK_SET_REG(RX2, RX2_IF_SELECT_MASK, 0);
- SET_REG(SPKR_ADDER, 1 << DAC1_TO_ADDER_SHIFT |
- 1 << DAC2_TO_ADDER_SHIFT);
-/* SET_REG(AUXO1_ADDER, 1 << DAC1_TO_ADDER_SHIFT); */
-/* SET_REG(AUXO2_ADDER, 1 << DAC2_TO_ADDER_SHIFT); */
- printk(KERN_DEBUG "%s: Routes to the speaker and the AUXO are setup.\n",
- __func__);
-
- /* Mute the outamps first */
- MASK_SET_REG(RX1_DIGITAL_PGA, RXx_PGA_GAIN_MASK, 0);
- MASK_SET_REG(RX2_DIGITAL_PGA, RXx_PGA_GAIN_MASK, 0);
-/* MASK_SET_REG(AUXO1, AUXOx_GAIN_MASK, 0); */
-/* MASK_SET_REG(AUXO2, AUXOx_GAIN_MASK, 0); */
- MASK_SET_REG(SPKR, SPKR_GAIN_MASK, 0);
-
- MASK_SET_REG(RX1, RX1_PWR_MASK | DAC1_PWR_MASK | DAC1_PWR_MODE_MASK,
- 1 << RX1_PWR_SHIFT | 1 << DAC1_PWR_SHIFT |
- 1 << DAC1_PWR_MODE_SHIFT);
- MASK_SET_REG(RX2, RX2_PWR_MASK | DAC2_PWR_MASK | DAC2_PWR_MODE_MASK,
- 1 << RX2_PWR_SHIFT | 1 << DAC2_PWR_SHIFT |
- 1 << DAC2_PWR_MODE_SHIFT);
- printk(KERN_DEBUG "%s: DAC1, DAC2 and RX2 are now powered up.\n",
- __func__);
-
- MASK_SET_REG(SPKR, SPKR_PWR_MASK, 1 << SPKR_PWR_SHIFT);
-/* MASK_SET_REG(AUXO1, AUXOx_PWR_MASK, 1 << AUXOx_PWR_SHIFT); */
-/* MASK_SET_REG(AUXO2, AUXOx_PWR_MASK, 1 << AUXOx_PWR_SHIFT); */
- printk(KERN_DEBUG "%s: The speaker and the AUXO are now powered up.\n",
- __func__);
-
- gain_ramp(RX1_DIGITAL_PGA, get_control_value_by_name("RX-DPGA1 Gain"),
- RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
-
- gain_ramp(RX2_DIGITAL_PGA, get_control_value_by_name("RX-DPGA2 Gain"),
- RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
- gain_ramp(SPKR, get_control_value_by_name("SPKR Gain"),
- SPKR_GAIN_SHIFT, SPKR_GAIN_MASK);
-/* gain_ramp(AUXO1, get_control_value_by_name("AUXO1 Gain"), */
-/* AUXOx_GAIN_SHIFT, AUXOx_GAIN_MASK); */
-/* gain_ramp(AUXO2, get_control_value_by_name("AUXO1 Gain"), */
-/* AUXOx_GAIN_SHIFT, AUXOx_GAIN_MASK); */
- printk(KERN_DEBUG "%s: return 0.\n", __func__);
- return 0;
+ printk(KERN_DEBUG "DORIANO: ADC%d turned %s.\n", idx_ADC + 1, val == 0 ? "off" : "on");
}
-static int ab3550_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params, struct snd_soc_dai* dai)
+static void ab3550_set_ADC_filter(int idx_ADC, u8 val)
{
- printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
- set_up_speaker_playback();
- return 0;
+ enum enum_register idx_reg_ADC[] = {IDX_TX1, IDX_TX2};
+ struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_ADC[idx_ADC]]);
+ MASK_SET_REG(reg->address,
+ TXx_HP_FILTER_MASK,
+ val << TXx_HP_FILTER_SHIFT);
+
+ printk(KERN_DEBUG "DORIANO: ADC%d filter set to %s.\n", idx_ADC + 1, enum_hp_filter[val]);
}
static int ab3550_add_widgets(struct snd_soc_codec *codec)
@@ -659,35 +552,150 @@ static int ab3550_add_widgets(struct snd_soc_codec *codec)
return 0;
}
+
+
+static void ab3550_init_registers_startup(int ifSel)
+{
+ u8 val;
+ u8 iface = (ifSel == 0) ? INTERFACE0 : INTERFACE1;
+
+ // Configure CLOCK register
+ val = 1 << CLOCK_REF_SELECT_SHIFT | // 32kHz RTC
+ 1 << CLOCK_ENABLE_SHIFT; // Enable clock
+ SET_REG(CLOCK, val);
+
+ // Enable Master Generator I2Sx
+ MASK_SET_REG(iface, MASTER_GENx_PWR_MASK | I2Sx_TRISTATE_MASK | I2Sx_PULLDOWN_MASK, 1 << MASTER_GENx_PWR_SHIFT);
+
+ // Configure INTERFACEx_DATA register
+ val = 1 << I2Sx_L_DATA_SHIFT |// TX1 to I2Sx_L
+ 1 << I2Sx_R_DATA_SHIFT; // TX1 to I2Sx_R
+ SET_REG(ifSel == 0 ? INTERFACE0_DATA : INTERFACE1_DATA, val);
+
+ GET_REG(CLOCK, &val);
+ printk(KERN_DEBUG "AB: CLOCK = 0x%02x\n", val);
+ GET_REG(iface, &val);
+ printk(KERN_DEBUG "AB: INTERFACE%d = 0x%02x\n", ifSel, val);
+ GET_REG(ifSel == 0 ? INTERFACE0_DATA : INTERFACE1_DATA, &val);
+ printk(KERN_DEBUG "AB: INTERFACE%d_DATA = 0x%02x\n", ifSel, val);
+}
+
+static void ab3550_init_registers_playback(int ifSel) {
+
+ int i, routing_left, routing_right, idx_RX_left, idx_RX_right;
+ u8 val;
+
+ // Get indices to active DACs
+ idx_RX_left = idx_RX_right = -1;
+ val = get_control_value_by_name("RX2 Select");
+ if (ifSel == 0) {
+ if (!(val & RX2_IF_SELECT_MASK))
+ idx_RX_right = 1;
+ idx_RX_left = 0;
+ } else {
+ if (val & RX2_IF_SELECT_MASK)
+ idx_RX_right = 1;
+ idx_RX_left = 2;
+ }
+printk(KERN_DEBUG "DORIANO: idx_RX_left = %d, idx_RX_right = %d\n", idx_RX_left, idx_RX_right);
+
+ // Toggle DACs
+ routing_left = ab3550_ctl[IDX_DAC1_Routing].value;
+printk(KERN_DEBUG "DORIANO: routing_left = %d\n", routing_left);
+ if (routing_left)
+ ab3550_toggle_DAC(idx_RX_left, 1);
+ routing_right = 0;
+ if (idx_RX_right > -1)
+ {
+ routing_right = ab3550_ctl[IDX_DAC2_Routing].value;
+printk(KERN_DEBUG "DORIANO: routing_right = %d\n", routing_right);
+ if (routing_right)
+ ab3550_toggle_DAC(idx_RX_right, 1);
+ }
+
+ // Toggle adders
+ for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) {
+ val = 0;
+ val |= (routing_left & 1 << i) ? 1 << idx_RX_left : 0;
+ if (idx_RX_right > -1)
+ val |= (routing_right & 1 << i) ? 1 << idx_RX_right : 0;
+ ab3550_toggle_output_adder(i, val);
+ }
+
+ /* TODO: DC cancellation */
+
+ // Toggle amplifiers
+ val = routing_left | routing_right;
+ for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) {
+printk(KERN_DEBUG "DORIANO: val & 1 << i = %d\n", val & 1 << i);
+ if (val & 1 << i)
+ ab3550_toggle_output_amp(i, 1);
+ }
+}
+
+static void ab3550_init_registers_capture(int ifSel)
+{
+ enum enum_control idx = (ifSel == 0) ? IDX_I2S0_Input_Select : IDX_I2S1_Input_Select;
+
+printk(KERN_DEBUG "DORIANO: ab3550_ctl[idx].value = 0x%02x\n", ab3550_ctl[idx].value);
+
+ // Toggle ADC (RXx) and amplifiers (MICx_GAIN)
+ if ((ab3550_ctl[idx].value & 0x0F) < 3) { // value = 3 => mute
+ int idx_ADC = (ab3550_ctl[idx].value & 0x0F) - 1; // value = 0xX1 => MIC1, value = 0xX2 => MIC2
+ ab3550_toggle_ADC(idx_ADC, 1);
+ ab3550_toggle_mic_amp(idx_ADC, 1);
+ }
+}
+
+
+
+// Commit ---------------------------------------------------------------------------------------------------------------
+
+static void printCommit(int idx) {
+ printk(KERN_DEBUG "%s: %s committed to 0x%02x.\n", __func__, ab3550_ctl[idx].name, ab3550_ctl[idx].value);
+}
+
+static bool commit_if_changed(enum enum_control idx_ctl)
+{
+ bool changed = false;
+
+ if (ab3550_ctl[idx_ctl].changed) {
+ MASK_SET_REG(ab3550_registers[ab3550_ctl[idx_ctl].reg_idx].address,
+ ab3550_ctl[idx_ctl].reg_mask,
+ ab3550_ctl[idx_ctl].value << ab3550_ctl[idx_ctl].reg_shift);
+ ab3550_ctl[idx_ctl].changed = false;
+ changed = true;
+
+ printCommit(idx_ctl);
+ }
+
+ return changed;
+}
+
static int commit_outamps_routing(void)
{
- unsigned long idx;
- int adjust = 0;
static const char *control_names[] = {
"DAC1 Routing", "DAC2 Routing", "DAC3 Routing",
"APGA1 Destination", "APGA2 Destination"
};
- static const u8 outamp_adder_reg[] = {
- LINE1_ADDER, LINE2_ADDER, SPKR_ADDER,
- EAR_ADDER, AUXO1_ADDER, AUXO2_ADDER
- };
+
static const u8 outamp_0db_gain[] = {
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C
};
- for_each_bit(idx, control_changed, ARRAY_SIZE(ab3550_ctl)) {
- if (strcmp(ab3550_ctl[idx].name, "DAC1 Routing") == 0 ||
- strcmp(ab3550_ctl[idx].name, "DAC2 Routing") == 0 ||
- strcmp(ab3550_ctl[idx].name, "DAC3 Routing") == 0 ||
- strcmp(ab3550_ctl[idx].name, "APGA1 Destination") == 0 ||
- strcmp(ab3550_ctl[idx].name, "APGA2 Destination") == 0
- ) {
- adjust = 1;
- }
- }
- if (adjust) {
+ struct ab3550_register* reg;
+
+ printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+ if ((get_control_changed_by_name("DAC1 Routing")) ||
+ (get_control_changed_by_name("DAC2 Routing")) ||
+ (get_control_changed_by_name("DAC3 Routing")) ||
+ (get_control_changed_by_name("APGA1 Destination")) ||
+ (get_control_changed_by_name("APGA2 Destination"))) {
+
int i;
- for (i = 0; i < ARRAY_SIZE(outamp_reg); i++) {
+
+ for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) {
unsigned int connect = 0;
int j;
for (j = 0; j < ARRAY_SIZE(control_names); j++) {
@@ -700,17 +708,19 @@ static int commit_outamps_routing(void)
nothing is to be disconnected to it */
if (!connect) {
u8 val = 0;
- GET_REG(outamp_reg[i], &val);
+ reg = &(ab3550_registers[(int)outamp_indices[i]]);
+ GET_REG(reg->address, &val);
if (! (val & 1 << outamp_pwr_shift[i]))
continue;
- gain_ramp(outamp_reg[i], 0,
+ gain_ramp(reg->address, 0,
outamp_gain_shift[i],
outamp_gain_mask[i]);
- MASK_SET_REG(outamp_reg[i],
+ MASK_SET_REG(reg->address,
outamp_pwr_mask[i], 0);
continue;
}
- MASK_SET_REG(outamp_adder_reg[i],
+ reg = &(ab3550_registers[(int)outamp_adder_indices[i]]);
+ MASK_SET_REG(reg->address,
DAC3_TO_ADDER_MASK |
DAC2_TO_ADDER_MASK |
DAC3_TO_ADDER_MASK,
@@ -718,88 +728,270 @@ static int commit_outamps_routing(void)
if (connect & 1 << 3) {
/* Connect APGA1 */
MASK_SET_REG(APGA1_ADDER,
- 1 << (ARRAY_SIZE(outamp_reg) - i),
- 1 << (ARRAY_SIZE(outamp_reg) - i));
+ 1 << (ARRAY_SIZE(outamp_indices) - i),
+ 1 << (ARRAY_SIZE(outamp_indices) - i));
}
if (connect & 1 << 4) {
/* Connect APGA2 */
MASK_SET_REG(APGA2_ADDER,
- 1 << (ARRAY_SIZE(outamp_reg) - i),
- 1 << (ARRAY_SIZE(outamp_reg) - i));
+ 1 << (ARRAY_SIZE(outamp_indices) - i),
+ 1 << (ARRAY_SIZE(outamp_indices) - i));
}
- MASK_SET_REG(outamp_reg[i],
+ reg = &(ab3550_registers[(int)outamp_indices[i]]);
+ MASK_SET_REG(reg->address,
outamp_pwr_mask[i],
1 << outamp_pwr_shift[i]);
- gain_ramp(outamp_reg[i], outamp_0db_gain[i],
+ gain_ramp(reg->address, outamp_0db_gain[i],
outamp_gain_shift[i],
outamp_gain_mask[i]);
}
+
+ for (i = 0; i < ARRAY_SIZE(control_names); i++)
+ ab3550_ctl[get_control_index(control_names[i])].changed = 0;
}
return 0;
}
-static int commit_gain(void)
-{
- unsigned long idx;
- for_each_bit(idx, control_changed, ARRAY_SIZE(ab3550_ctl)) {
- if (strcmp(ab3550_ctl[idx].name, "LINE1 Gain") == 0 ||
- strcmp(ab3550_ctl[idx].name, "LINE2 Gain") == 0 ||
- strcmp(ab3550_ctl[idx].name, "SPKR Gain") == 0 ||
- strcmp(ab3550_ctl[idx].name, "EAR Gain") == 0 ||
- strcmp(ab3550_ctl[idx].name, "AUXO1 Gain") == 0 ||
- strcmp(ab3550_ctl[idx].name, "AUXO2 Gain") == 0
- ) {
- /* Ramping is needed if powered up */
- int i = idx - get_control_index("LINE1 Gain");
- u8 val = GET_REG(outamp_reg[i], &val);
- if (val & 1 << outamp_pwr_shift[i]) {
- gain_ramp(outamp_reg[i], ab3550_ctl[idx].value,
- outamp_gain_shift[i],
- outamp_gain_mask[i]);
- continue;
- }
- /* Othwise set directly */
- MASK_SET_REG(outamp_reg[i], outamp_gain_mask[i],
+static int commit_rx_routing(void)
+{
+ int idx;
+
+ printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+ idx = get_control_index("RX2 Select");
+ if (ab3550_ctl[idx].changed) {
+ u8 val = ab3550_ctl[idx].value;
+ if (val & RXx_PWR_MASK)
+ MASK_SET_REG(RX2, RXx_PWR_MASK, 0);
+ MASK_SET_REG(RX2, RX2_IF_SELECT_MASK, ab3550_ctl[idx].value);
+ if (val & RXx_PWR_MASK)
+ MASK_SET_REG(RX2, RXx_PWR_MASK, 1 << RXx_PWR_SHIFT);
+ ab3550_ctl[idx].changed = false;
+
+ printCommit(idx);
+ }
+
+ commit_outamps_routing();
+
+ return 0;
+}
+
+static int commit_rx_gain(void)
+{
+ int i, idx_ctl;
+ //u8 val;
+ struct ab3550_register* reg;
+
+ printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+ for (i = 0; i < ARRAY_SIZE(outamp_gain_controls); i++) {
+ idx_ctl = (int)outamp_gain_controls[i];
+ reg = &(ab3550_registers[ab3550_ctl[idx_ctl].reg_idx]);
+ if (ab3550_ctl[idx_ctl].changed) {
+ //val = GET_REG(reg->address, &val);
+ // if (ab3550_ctl[idx_ctl].value & 1 << outamp_pwr_shift[i]) // Ramping is needed if amp is powered up
+ // gain_ramp(outamp_reg[i], ab3550_ctl[idx_ctl].value, outamp_gain_shift[i], outamp_gain_mask[i]);
+ // else // Set directly if amp is not powered up
+ MASK_SET_REG(reg->address, outamp_gain_mask[i], ab3550_ctl[idx_ctl].value);
+ ab3550_ctl[idx_ctl].changed = false;
+
+ printCommit(idx_ctl);
+ }
+ }
+
+ idx_ctl = get_control_index("RX-DPGA1 Gain");
+ if (ab3550_ctl[idx_ctl].changed) {
+ gain_ramp(RX1_DIGITAL_PGA, ab3550_ctl[idx_ctl].value, RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+ ab3550_ctl[idx_ctl].changed = false;
+
+ printCommit(idx_ctl);
+
+ }
+
+ idx_ctl = get_control_index("RX-DPGA2 Gain");
+ if (ab3550_ctl[idx_ctl].changed) {
+ gain_ramp(RX2_DIGITAL_PGA, ab3550_ctl[idx_ctl].value, RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+ ab3550_ctl[idx_ctl].changed = false;
+
+ printCommit(idx_ctl);
+ }
+
+ idx_ctl = get_control_index("RX-DPGA3 Gain");
+ if (ab3550_ctl[idx_ctl].changed) {
+ gain_ramp(RX3_DIGITAL_PGA, ab3550_ctl[idx_ctl].value, RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+ ab3550_ctl[idx_ctl].changed = false;
+
+ printCommit(idx_ctl);
+ }
+
+ return 0;
+}
+
+static int commit_tx_routing(void)
+{
+ int idx;
+
+ printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+ idx = get_control_index("MIC1 Input Select");
+ if (ab3550_ctl[idx].changed) {
+ MASK_SET_REG(MIC1_INPUT_SELECT,
+ MICxP1_SEL_MASK | MICxN1_SEL_MASK | MICxP2_SEL_MASK | MICxN2_SEL_MASK | LINEIN_SEL_MASK,
+ ab3550_ctl[idx].value);
+ MASK_SET_REG(MIC2_TO_MIC1,
+ MIC2P1_MIC1P_SEL_MASK | MIC2N1_MIC1N_SEL_MASK,
+ ab3550_ctl[idx].value >> 2);
+ ab3550_ctl[idx].changed = false;
+
+ printCommit(idx);
+ }
+
+ idx = get_control_index("MIC2 Input Select");
+ if (ab3550_ctl[idx].changed) {
+ MASK_SET_REG(MIC2_INPUT_SELECT,
+ MICxP1_SEL_MASK | MICxN1_SEL_MASK | MICxP2_SEL_MASK | MICxN2_SEL_MASK | LINEIN_SEL_MASK,
ab3550_ctl[idx].value);
- } else if (strcmp(ab3550_ctl[idx].name, "RX-DPGA1 Gain") == 0) {
- gain_ramp(RX1_DIGITAL_PGA,
- get_control_value_by_name("RX-DPGA1 Gain"),
- RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+ ab3550_ctl[idx].changed = false;
+
+ printCommit(idx);
+ }
+
+ idx = get_control_index("I2S0 Input Select");
+ if (ab3550_ctl[idx].changed) {
+ MASK_SET_REG(INTERFACE0_DATA,
+ I2Sx_L_DATA_MASK,
+ ab3550_ctl[idx].value << I2Sx_L_DATA_SHIFT);
+ MASK_SET_REG(INTERFACE0_DATA,
+ I2Sx_R_DATA_MASK,
+ ab3550_ctl[idx].value << I2Sx_R_DATA_SHIFT);
+ ab3550_ctl[idx].changed = false;
+
+ printCommit(idx);
+ }
+
+ idx = get_control_index("I2S1 Input Select");
+ if (ab3550_ctl[idx].changed) {
+ MASK_SET_REG(INTERFACE1_DATA,
+ I2Sx_L_DATA_MASK,
+ ab3550_ctl[idx].value << I2Sx_L_DATA_SHIFT);
+ MASK_SET_REG(INTERFACE1_DATA,
+ I2Sx_R_DATA_MASK,
+ ab3550_ctl[idx].value << I2Sx_R_DATA_SHIFT);
+ ab3550_ctl[idx].changed = false;
+
+ printCommit(idx);
+ }
+
+ return 0;
+}
+
+static int commit_tx_gain(void)
+{
+ printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+ commit_if_changed(IDX_MIC1_Gain);
+ commit_if_changed(IDX_MIC2_Gain);
+ commit_if_changed(IDX_TX_DPGA1_Gain);
+ commit_if_changed(IDX_TX_DPGA2_Gain);
+
+ return 0;
+}
+
+static int commit_others(void)
+{
+ int idx;
+
+ printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+ idx = get_control_index("I2S0 Sample Rate");
+ if (ab3550_ctl[idx].changed) {
+ // TODO?
+ ab3550_ctl[idx].changed = false;
+ }
+
+ idx = get_control_index("I2S1 Sample Rate");
+ if (ab3550_ctl[idx].changed) {
+ // TODO?
+ ab3550_ctl[idx].changed = false;
+ }
+
+ idx = get_control_index("MIC1 MBias");
+ if (ab3550_ctl[idx].changed) {
+ MASK_SET_REG(MIC_BIAS1,
+ MBIAS_PWR_MASK,
+ ab3550_ctl[idx].value << MBIAS_PWR_SHIFT);
+ ab3550_ctl[idx].changed = false;
+ }
+
+ idx = get_control_index("MIC1 MBias");
+ if (ab3550_ctl[idx].changed) {
+ MASK_SET_REG(MIC_BIAS2,
+ MBIAS_PWR_MASK,
+ ab3550_ctl[idx].value << MBIAS_PWR_SHIFT);
+ ab3550_ctl[idx].changed = false;
+ }
- } else if (strcmp(ab3550_ctl[idx].name, "RX-DPGA2 Gain") == 0) {
- gain_ramp(RX2_DIGITAL_PGA,
- get_control_value_by_name("RX-DPGA2 Gain"),
- RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+ idx = get_control_index("TX1 HP Filter");
+ if (ab3550_ctl[idx].changed) {
+ ab3550_set_ADC_filter(0, ab3550_ctl[idx].value);
+ ab3550_ctl[idx].changed = false;
+ }
- } else if (strcmp(ab3550_ctl[idx].name, "RX-DPGA3 Gain") == 0) {
- gain_ramp(RX3_DIGITAL_PGA,
- get_control_value_by_name("RX-DPGA3 Gain"),
- RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+ idx = get_control_index("TX2 HP Filter");
+ if (ab3550_ctl[idx].changed) {
+ ab3550_set_ADC_filter(1, ab3550_ctl[idx].value);
+ ab3550_ctl[idx].changed = false;
+ }
+ idx = get_control_index("Voice Call");
+ if (ab3550_ctl[idx].changed) {
+ printk(KERN_DEBUG "%s: commiting 'Voice Call'.\n", __func__);
+ if (ab3550_ctl[idx].value) {
+ MASK_SET_REG(MIC_BIAS1,
+ MBIAS_PWR_MASK,
+ 1 << MBIAS_PWR_SHIFT);
+ MASK_SET_REG(MIC_BIAS2,
+ MBIAS_PWR_MASK,
+ 1 << MBIAS_PWR_SHIFT);
+ //turn_on_interface_tx(INTERFACE1);
+ MASK_SET_REG(INTERFACE1,
+ MASTER_GENx_PWR_MASK,
+ 1 << MASTER_GENx_PWR_SHIFT);
+
+ printCommit(idx);
}
+ ab3550_ctl[idx].changed = false;
}
+
return 0;
}
+void commit_all(void)
+{
+ commit_rx_routing();
+ commit_rx_gain();
+ commit_tx_routing();
+ commit_tx_gain();
+ commit_others();
+}
+
static int ab3550_set_control_value(struct snd_soc_codec *codec,
unsigned int ctl, unsigned int value)
{
- int ret = 0;
-
if (ctl >= ARRAY_SIZE(ab3550_ctl))
return -EINVAL;
- if (ab3550_ctl[ctl].value == value)
- return 0;
- ab3550_ctl[ctl].value = value;
- if (strcmp(ab3550_ctl[ctl].name, "Commit") != 0)
- return 0;
- /* Commit the changes */
- commit_outamps_routing();
- commit_gain();
- memset(control_changed, 0, sizeof(control_changed));
+ if (strcmp(ab3550_ctl[ctl].name, "Commit") == 0) {
+ commit_all();
- return ret;
+ } else if (ab3550_ctl[ctl].value != value) {
+ printk(KERN_DEBUG "%s: Ctl %d changed from 0x%02x to 0x%02x\n", __func__, ctl, ab3550_ctl[ctl].value, value);
+
+ ab3550_ctl[ctl].value = value;
+ ab3550_ctl[ctl].changed = true;
+ }
+
+ return 0;
}
static int ab3550_add_controls(struct snd_soc_codec *codec)
@@ -810,11 +1002,11 @@ static int ab3550_add_controls(struct snd_soc_codec *codec)
for (i = 0; i < n; i++) {
/* Initialize the control indice */
if (! ab3550_ctl[i].is_enum) {
- ab3550_snd_controls[i].private_value |= i;
+ ((struct soc_mixer_control *)
+ ab3550_snd_controls[i].private_value)->reg = i;
} else {
- struct soc_enum *p = (struct soc_enum *)
- ab3550_snd_controls[i].private_value;
- p->reg = i;
+ ((struct soc_enum *)
+ ab3550_snd_controls[i].private_value)->reg = i;
}
printk(KERN_DEBUG "%s: %s.reg = %d\n", __func__,
ab3550_ctl[i].name, i);
@@ -831,6 +1023,232 @@ static int ab3550_add_controls(struct snd_soc_codec *codec)
return err;
}
+
+
+// snd_soc_dai ---------------------------------------------------------------------------------------------------------
+
+static int ab3550_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai* dai)
+{
+ struct codec_dai_private *priv = (struct codec_dai_private*)dai->private_data;
+
+ printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
+ /* TODO: consult the clock use count */
+
+
+ // Configure registers on startup
+ if (atomic_read(&priv->substream_count) == 0)
+ ab3550_init_registers_startup(dai->id);
+
+ atomic_inc(&priv->substream_count);
+
+ // Configure registers for either playback or capture
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ab3550_init_registers_playback(dai->id);
+ else
+ ab3550_init_registers_capture(dai->id);
+
+ ab3550_print_registers(substream->stream);
+
+ return 0;
+}
+
+static int ab3550_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai* dai)
+{
+
+ u8 val;
+ const char *reg_name;
+
+ printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__);
+
+ reg_name = dai->id == 0 ? "I2S0 Sample Rate" : "I2S1 Sample Rate";
+
+ switch(params_rate(hw_params))
+ {
+ case 8000:
+ val=0;
+ break;
+
+ case 16000:
+ val=1;
+ break;
+
+ case 44100:
+ val=2;
+ break;
+
+ case 48000:
+ val=3;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ab3550_ctl[get_control_index(reg_name)].value = val;
+
+ MASK_SET_REG(dai->id == 0 ? INTERFACE0 : INTERFACE1,
+ I2Sx_SR_MASK,
+ val);
+
+ return 0;
+}
+
+static void ab3550_pcm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai* dai)
+{
+ int i;
+ u8 iface;
+ const char *mode_ctl_name;
+ printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
+ if (dai->id == 0) {
+ iface = INTERFACE0;
+ mode_ctl_name = "I2S0 Mode";
+ } else {
+ iface = INTERFACE1;
+ mode_ctl_name = "I2S1 Mode";
+ }
+ if (get_control_value_by_name(mode_ctl_name) == 0) {
+ struct codec_dai_private *priv =
+ (struct codec_dai_private *)dai->private_data;
+ if (atomic_dec_and_test(&priv->substream_count))
+ MASK_SET_REG(iface, MASTER_GENx_PWR_MASK, 0);
+ }
+ /*TODO: consult the clock use count */
+/* MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 0); */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ struct ab3550_register* reg;
+ /* TODO: Turn off the outamps only used by this I2S */
+ for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) {
+ reg = &(ab3550_registers[(int)outamp_indices[i]]);
+ MASK_SET_REG(reg->address, outamp_pwr_mask[i], 0);
+ }
+ } else {
+ /* TODO: Turn off the inactive components */
+ }
+}
+
+static int ab3550_set_dai_sysclk(struct snd_soc_dai* dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ /*
+ u8 val;
+ const char *reg_name;
+ printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__);
+ switch(freq) {
+ case 8000:
+ val = 0;
+ break;
+ case 16000:
+ val = 1;
+ break;
+ case 44100:
+ val = 2;
+ break;
+ case 48000:
+ val = 3;
+ break;
+ default:
+ printk(KERN_ERR "%s: invalid frequency %u.\n",
+ __func__, freq);
+ return -EINVAL;
+ }
+
+ reg_name = dai->id == 0 ? "I2S0 Sample Rate" : "I2S1 Sample Rate";
+ ab3550_ctl[get_control_index(reg_name)].value = val;
+ return MASK_SET_REG(dai->id == 0 ? INTERFACE0 : INTERFACE1,
+ I2Sx_SR_MASK,
+ val << I2Sx_SR_SHIFT);
+ */
+ return 0;
+}
+
+static int ab3550_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ u8 iface = (codec_dai->id == 0) ? INTERFACE0 : INTERFACE1;
+ u8 val=0;
+ printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__);
+
+ switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+ val |= 1 << I2Sx_MODE_SHIFT;
+ break;
+
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+ break;
+
+ default:
+ printk(KERN_WARNING "AB3550_dai: unsupported DAI format 0x%x\n",fmt);
+ return -EINVAL;
+ }
+
+ return MASK_SET_REG(iface,I2Sx_MODE_MASK | I2Sx_WORDLENGTH_MASK, val);
+}
+
+struct snd_soc_dai ab3550_codec_dai[] = {
+ {
+ .name = "ab3550_0",
+ .id = 0,
+ .playback = {
+ .stream_name = "ab3550_0",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AB3550_SUPPORTED_RATE,
+ .formats = AB3550_SUPPORTED_FMT,
+ },
+ .capture = {
+ .stream_name = "ab3550_0",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AB3550_SUPPORTED_RATE,
+ .formats = AB3550_SUPPORTED_FMT,
+ },
+ .ops = {
+ .prepare = ab3550_pcm_prepare,
+ .hw_params = ab3550_pcm_hw_params,
+ .shutdown = ab3550_pcm_shutdown,
+ .set_sysclk = ab3550_set_dai_sysclk,
+ .set_fmt = ab3550_set_dai_fmt
+ },
+ .private_data = &privates[0]
+ },
+ {
+ .name = "ab3550_1",
+ .id = 1,
+ .playback = {
+ .stream_name = "ab3550_1",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AB3550_SUPPORTED_RATE,
+ .formats = AB3550_SUPPORTED_FMT,
+ },
+ .capture = {
+ .stream_name = "ab3550_1",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AB3550_SUPPORTED_RATE,
+ .formats = AB3550_SUPPORTED_FMT,
+ },
+ .ops = {
+ .prepare = ab3550_pcm_prepare,
+ .hw_params = ab3550_pcm_hw_params,
+ .shutdown = ab3550_pcm_shutdown,
+ .set_sysclk = ab3550_set_dai_sysclk,
+ .set_fmt = ab3550_set_dai_fmt
+ },
+ .private_data = &privates[1]
+ }
+};
+EXPORT_SYMBOL_GPL(ab3550_codec_dai);
+
+
+
+
+
+// snd_soc_codec_device ---------------------------------------------------------------------------------------------------
+
static int ab3550_codec_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -843,7 +1261,6 @@ static int ab3550_codec_probe(struct platform_device *pdev)
return -ENOMEM;
codec->name = "AB3550";
codec->owner = THIS_MODULE;
- /* TODO: add codec dai */
codec->dai = ab3550_codec_dai;
codec->num_dai = 2;
codec->read = ab3550_get_control_value;
@@ -911,38 +1328,31 @@ struct snd_soc_codec_device soc_codec_dev_ab3550 = {
EXPORT_SYMBOL_GPL(soc_codec_dev_ab3550);
-/* struct snd_soc_dai ab3550_codec_dai = { */
-/* .name = "AB3550", */
-/* .playback = { */
-/* .stream_name = "Playback", */
-/* .channels_min = 1, */
-/* .channels_max = 2, */
-/* .rates = AB3550_RATES, */
-/* .formats = AB3550_FORMATS,}, */
-/* .capture = { */
-/* .stream_name = "Capture", */
-/* .channels_min = 1, */
-/* .channels_max = 2, */
-/* .rates = AB3550_RATES, */
-/* .formats = AB3550_FORMATS,}, */
-/* .ops = { */
-/* .prepare = ab3550_pcm_prepare, */
-/* .hw_params = ab3550_hw_params, */
-/* .shutdown = ab3550_shutdown, */
-/* }, */
-/* .dai_ops = { */
-/* .digital_mute = ab3550_mute, */
-/* .set_sysclk = ab3550_set_dai_sysclk, */
-/* .set_fmt = ab3550_set_dai_fmt, */
-/* } */
-/* }; */
-/* EXPORT_SYMBOL_GPL(ab3550_codec_dai); */
static int ab3550_platform_probe(struct platform_device *pdev)
{
int ret = 0;
-
+ u8 reg, val;
+ unsigned long i;
printk(KERN_DEBUG "%s invoked with pdev = %p.\n", __func__, pdev);
ab3550_dev = &pdev->dev;
+
+ /* Initialize the codec registers */
+ for (reg = MIC_BIAS1; reg < INTERFACE_SWAP; reg++) {
+ SET_REG(reg, 0);
+ }
+ MASK_SET_REG(CLOCK, CLOCK_REF_SELECT_MASK,
+ 1 << CLOCK_REF_SELECT_SHIFT);
+ MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK,
+ 1 << CLOCK_ENABLE_SHIFT);
+ val = GET_REG(CLOCK, &val);
+ printk(KERN_DEBUG "%s: CLOCK = 0x%02x.\n", __func__, val);
+
+ // Init all registers from default control values
+ for (i = 0; i < ARRAY_SIZE(ab3550_ctl); i++)
+ if (ab3550_ctl[i].value)
+ ab3550_ctl[i].changed = true;
+ commit_all();
+
return ret;
}
@@ -950,6 +1360,7 @@ static int ab3550_platform_remove(struct platform_device *pdev)
{
int ret;
printk(KERN_DEBUG "%s called.\n", __func__);
+ MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 0);
if ((ret = soc_codec_dev_ab3550.remove(NULL)))
printk(KERN_ERR "%s failed with error code %d.\n",
__func__, ret);
@@ -991,18 +1402,47 @@ static struct platform_driver ab3550_platform_driver = {
static int __devinit ab3550_init(void)
{
- int ret;
+ int i, ret1;
+ int ret2 = 0;
+
printk(KERN_DEBUG "%s called.\n", __func__);
- ret= platform_driver_register(&ab3550_platform_driver);
- printk(KERN_DEBUG "%s finished with error code %d.\n",
- __func__, ret);
- return ret;
+
+ // Register codec platform driver.
+ ret1 = platform_driver_register(&ab3550_platform_driver);
+ if (ret1 < 0) {
+ printk(KERN_DEBUG "%s: Error %d: Failed to register codec platform driver.\n", __func__, ret1);
+ ret2 = -1;
+ }
+
+ // Register codec-dai.
+ printk(KERN_DEBUG "%s: Register codec-dai.\n", __func__);
+
+ for (i = 0; i < ARRAY_SIZE(ab3550_codec_dai); i++) {
+ printk(KERN_DEBUG "%s: Register codec-dai %d.\n", __func__, i);
+ ret1 = snd_soc_register_dai(&ab3550_codec_dai[i]);
+ if (ret1 < 0) {
+ printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register codec-dai %d.\n", i);
+ ret2 = -1;
+ }
+ }
+
+ return ret2;
}
static void ab3550_exit(void)
{
- printk(KERN_DEBUG "%s called.\n", __func__);
+ int i;
+
+ printk(KERN_DEBUG "u8500_ab3550_init: Enter.\n");
+
+ // Register codec platform driver.
+ printk(KERN_DEBUG "%s: Un-register codec platform driver.\n", __func__);
platform_driver_unregister(&ab3550_platform_driver);
+
+ for (i = 0; i < ARRAY_SIZE(ab3550_codec_dai); i++) {
+ printk(KERN_DEBUG "%s: Un-register codec-dai %d.\n", __func__, i);
+ snd_soc_unregister_dai(&ab3550_codec_dai[i]);
+ }
}
module_init(ab3550_init);
diff --git a/sound/soc/codecs/ab3550.h b/sound/soc/codecs/ab3550.h
index 943d9e4cdcf..00c2fc4a219 100755
--- a/sound/soc/codecs/ab3550.h
+++ b/sound/soc/codecs/ab3550.h
@@ -13,6 +13,15 @@
#ifndef AB3550_CODEC_REGISTERS_H
#define AB3550_CODEC_REGISTERS_H
+extern struct snd_soc_dai ab3550_codec_dai[2];
+extern struct snd_soc_codec_device soc_codec_dev_ab3550;
+
+#define AB3550_SUPPORTED_RATE (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define AB3550_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
/* MIC BIAS */
#define MIC_BIAS1 0X31
#define MIC_BIAS2 0X32
@@ -150,7 +159,7 @@
#define APGA1_ADDER 0x46
#define APGA2_ADDER 0x47
#define APGAx_TO_LINE1_MASK 0x20
-#define APGAx_TO_LINE1_SHIFT 5
+#define APGAx_TO_LINE1_SHIFT 5F
#define APGAx_TO_LINE2_MASK 0x10
#define APGAx_TO_LINE2_SHIFT 4
#define APGAx_TO_SPKR_MASK 0x08
@@ -201,30 +210,16 @@
#define ADCx_PWR_SHIFT 0
#define RX1 0x53
-#define RX1_PWR_MASK 0x08
-#define RX1_PWR_SHIFT 3
-#define DAC1_PWR_MASK 0x04
-#define DAC1_PWR_SHIFT 2
-#define DAC1_PWR_MODE_MASK 0x03
-#define DAC1_PWR_MODE_SHIFT 0
-
#define RX2 0x54
#define RX2_IF_SELECT_MASK 0x10
#define RX2_IF_SELECT_SHIFT 4
-#define RX2_PWR_MASK 0x08
-#define RX2_PWR_SHIFT 3
-#define DAC2_PWR_MASK 0x04
-#define DAC2_PWR_SHIFT 2
-#define DAC2_PWR_MODE_MASK 0x03
-#define DAC2_PWR_MODE_SHIFT 0
-
#define RX3 0x55
-#define RX3_PWR_MASK 0x08
-#define RX3_PWR_SHIFT 3
-#define DAC3_PWR_MASK 0x04
-#define DAC3_PWR_SHIFT 2
-#define DAC3_PWR_MODE_MASK 0x03
-#define DAC3_PWR_MODE_SHIFT 0
+#define RXx_PWR_MASK 0x08
+#define RXx_PWR_SHIFT 3
+#define DACx_PWR_MASK 0x04
+#define DACx_PWR_SHIFT 2
+#define DACx_PWR_MODE_MASK 0x03
+#define DACx_PWR_MODE_SHIFT 0
#define TX_DIGITAL_PGA1 0X56
#define TX_DIGITAL_PGA2 0X57
@@ -300,4 +295,145 @@
#define IO_SWAP1_MASK 0x01
#define IO_SWAP1_SHIFT 0
+enum enum_register {
+ IDX_UNKNOWN = -1,
+ IDX_MIC_BIAS1 = 0,
+ IDX_MIC_BIAS2 = 1,
+ IDX_MIC_BIAS2_VAD = 2,
+ IDX_MIC1_GAIN = 3,
+ IDX_MIC2_GAIN = 4,
+ IDX_MIC1_INPUT_SELECT = 5,
+ IDX_MIC2_INPUT_SELECT = 6,
+ IDX_MIC1_VMID_SELECT = 7,
+ IDX_MIC2_VMID_SELECT = 8,
+ IDX_MIC2_TO_MIC1 = 9,
+ IDX_ANALOG_LOOP_PGA1 = 10,
+ IDX_ANALOG_LOOP_PGA2 = 11,
+ IDX_APGA_VMID_SELECT = 12,
+ IDX_EAR = 13,
+ IDX_AUXO1 = 14,
+ IDX_AUXO2 = 15,
+ IDX_AUXO_PWR_MODE = 16,
+ IDX_OFFSET_CANCEL = 17,
+ IDX_SPKR = 18,
+ IDX_LINE1 = 19,
+ IDX_LINE2 = 20,
+ IDX_APGA1_ADDER = 21,
+ IDX_APGA2_ADDER = 22,
+ IDX_EAR_ADDER = 23,
+ IDX_AUXO1_ADDER = 24,
+ IDX_AUXO2_ADDER = 25,
+ IDX_SPKR_ADDER = 26,
+ IDX_LINE1_ADDER = 27,
+ IDX_LINE2_ADDER = 28,
+ IDX_EAR_TO_MIC2 = 29,
+ IDX_SPKR_TO_MIC2 = 30,
+ IDX_NEGATIVE_CHARGE_PUMP = 31,
+ IDX_TX1 = 32,
+ IDX_TX2 = 33,
+ IDX_RX1 = 34,
+ IDX_RX2 = 35,
+ IDX_RX3 = 36,
+ IDX_TX_DIGITAL_PGA1 = 37,
+ IDX_TX_DIGITAL_PGA2 = 38,
+ IDX_RX1_DIGITAL_PGA = 39,
+ IDX_RX2_DIGITAL_PGA = 40,
+ IDX_RX3_DIGITAL_PGA = 41,
+ IDX_SIDETONE1_PGA = 42,
+ IDX_SIDETONE2_PGA = 43,
+ IDX_CLOCK = 44,
+ IDX_INTERFACE0 = 45,
+ IDX_INTERFACE1 = 46,
+ IDX_INTERFACE0_DATA = 47,
+ IDX_INTERFACE1_DATA = 48,
+ IDX_INTERFACE_LOOP = 49,
+ IDX_INTERFACE_SWAP = 50
+};
+
+enum enum_control {
+ IDX_RX2_Select = 0,
+ IDX_DAC1_Routing,
+ IDX_DAC2_Routing,
+ IDX_DAC3_Routing,
+ IDX_MIC1_Input_Select,
+ IDX_MIC2_Input_Select,
+ IDX_I2S0_Input_Select,
+ IDX_I2S1_Input_Select,
+ IDX_APGA1_Source,
+ IDX_APGA2_Source,
+ IDX_APGA1_Destination,
+ IDX_APGA2_Destination,
+ IDX_DAC1_Side_Tone,
+ IDX_DAC2_Side_Tone,
+ IDX_RX_DPGA1_Gain,
+ IDX_RX_DPGA2_Gain,
+ IDX_RX_DPGA3_Gain,
+ IDX_LINE1_Gain,
+ IDX_LINE2_Gain,
+ IDX_SPKR_Gain,
+ IDX_EAR_Gain,
+ IDX_AUXO1_Gain,
+ IDX_AUXO2_Gain,
+ IDX_MIC1_Gain,
+ IDX_MIC2_Gain,
+ IDX_TX_DPGA1_Gain,
+ IDX_TX_DPGA2_Gain,
+ IDX_ST_PGA1_Gain,
+ IDX_ST_PGA2_Gain,
+ IDX_APGA1_Gain,
+ IDX_APGA2_Gain,
+ IDX_DAC1_Power_Mode,
+ IDX_DAC2_Power_Mode,
+ IDX_DAC3_Power_Mode,
+ IDX_EAR_Power_Mode,
+ IDX_AUXO_Power_Mode,
+ IDX_LINE1_Inverse,
+ IDX_LINE2_Inverse,
+ IDX_AUXO1_Inverse,
+ IDX_AUXO2_Inverse,
+ IDX_AUXO1_Pulldown,
+ IDX_AUXO2_Pulldown,
+ IDX_VMID1,
+ IDX_VMID2,
+ IDX_MIC1_MBias,
+ IDX_MIC2_MBias,
+ IDX_MBIAS1_HiZ_Option,
+ IDX_MBIAS2_HiZ_Option,
+ IDX_MBIAS2_Output_Voltage,
+ IDX_MBIAS2_Internal_Resistor,
+ IDX_MIC1_Input_Impedance,
+ IDX_MIC2_Input_Impedance,
+ IDX_TX1_HP_Filter,
+ IDX_TX2_HP_Filter,
+ IDX_LINEIN1_Pre_charge,
+ IDX_LINEIN2_Pre_charge,
+ IDX_MIC1P1_Pre_charge,
+ IDX_MIC1P2_Pre_charge,
+ IDX_MIC1N1_Pre_charge,
+ IDX_MIC1N2_Pre_charge,
+ IDX_MIC2P1_Pre_charge,
+ IDX_MIC2P2_Pre_charge,
+ IDX_MIC2N1_Pre_charge,
+ IDX_MIC2N2_Pre_charge,
+ IDX_ST1_HP_Filter,
+ IDX_ST2_HP_Filter,
+ IDX_I2S0_Word_Length,
+ IDX_I2S1_Word_Length,
+ IDX_I2S0_Mode,
+ IDX_I2S1_Mode,
+ IDX_I2S0_Tri_state,
+ IDX_I2S1_Tri_state,
+ IDX_I2S0_Pulldown,
+ IDX_I2S1_Pulldown,
+ IDX_I2S0_Sample_Rate,
+ IDX_I2S1_Sample_Rate,
+ IDX_Interface_Loop,
+ IDX_Interface_Swap,
+ IDX_Voice_Call,
+ IDX_Commit
+};
+
#endif /* AB3550_CODEC_REGISTERS_H */
+
+
+
diff --git a/sound/soc/codecs/cg29xx.c b/sound/soc/codecs/cg29xx.c
new file mode 100755
index 00000000000..eab26b47ed9
--- /dev/null
+++ b/sound/soc/codecs/cg29xx.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/bitops.h>
+#include "cg29xx.h"
+
+
+#define CG29XX_SUPPORTED_RATE (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define CG29XX_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static int cg29xx_pcm_prepare(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+
+static int cg29xx_pcm_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *dai);
+
+static void cg29xx_pcm_shutdown(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+
+static int cg29xx_set_dai_sysclk(
+ struct snd_soc_dai *codec_dai,
+ int clk_id,
+ unsigned int freq, int dir);
+
+static int cg29xx_set_dai_fmt(
+ struct snd_soc_dai *codec_dai,
+ unsigned int fmt);
+
+struct snd_soc_dai cg29xx_codec_dai[] = {
+ {
+ .name = "nullcodec_0",
+ .playback = {
+ .stream_name = "nullcodec_0",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CG29XX_SUPPORTED_RATE,
+ .formats = CG29XX_SUPPORTED_FMT,
+ },
+ .capture = {
+ .stream_name = "nullcodec_0",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CG29XX_SUPPORTED_RATE,
+ .formats = CG29XX_SUPPORTED_FMT,
+ },
+ .ops = {
+ .prepare = cg29xx_pcm_prepare,
+ .hw_params = cg29xx_pcm_hw_params,
+ .shutdown = cg29xx_pcm_shutdown,
+ .set_sysclk = cg29xx_set_dai_sysclk,
+ .set_fmt = cg29xx_set_dai_fmt
+ },
+ }
+};
+EXPORT_SYMBOL_GPL(cg29xx_codec_dai);
+
+static int cg29xx_set_dai_sysclk(
+ struct snd_soc_dai *codec_dai,
+ int clk_id,
+ unsigned int freq, int dir)
+{
+ printk(
+ KERN_DEBUG
+ "%s: %s called\n",
+ __FILE__,
+ __func__);
+ return 0;
+}
+
+static int cg29xx_set_dai_fmt(
+ struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ printk(
+ KERN_DEBUG
+ "%s: %s called.\n",
+ __FILE__,
+ __func__);
+ return 0;
+}
+
+static int cg29xx_pcm_prepare(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ printk(KERN_DEBUG "%s: %s called.\n",
+ __FILE__,
+ __func__);
+ return 0;
+}
+
+static void cg29xx_pcm_shutdown(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ printk(KERN_DEBUG "%s: %s called.\n",
+ __FILE__,
+ __func__);
+}
+
+static int cg29xx_pcm_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params,
+ struct snd_soc_dai *dai)
+{
+ printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
+ return 0;
+}
+
+static unsigned int nullcodec_codec_read(
+ struct snd_soc_codec *codec,
+ unsigned int ctl)
+{
+ printk(
+ KERN_DEBUG
+ "%s: %s called.\n",
+ __FILE__,
+ __func__);
+ return 0;
+}
+
+static int nullcodec_codec_write(
+ struct snd_soc_codec *codec,
+ unsigned int ctl,
+ unsigned int value)
+{
+ printk(
+ KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
+ return 0;
+}
+
+static int nullcodec_codec_probe(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec;
+ int ret;
+
+ printk(KERN_DEBUG "%s called. pdev = %p.\n", __func__, pdev);
+
+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+ if (codec == NULL)
+ return -ENOMEM;
+ codec->name = "NullCodec";
+ codec->owner = THIS_MODULE;
+ codec->dai = &cg29xx_codec_dai[0];
+ codec->num_dai = 1;
+ codec->read = nullcodec_codec_read;
+ codec->write = nullcodec_codec_write;
+ INIT_LIST_HEAD(&codec->dapm_widgets);
+ INIT_LIST_HEAD(&codec->dapm_paths);
+
+
+ mutex_init(&codec->mutex);
+
+ socdev->codec = codec;
+
+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+ if (ret < 0) {
+ printk(
+ KERN_ERR
+ "%s: Error: to create new PCMs. error %d\n",
+ __func__,
+ ret);
+ goto err1;
+ }
+
+ ret = snd_soc_init_card(socdev);
+ if (ret < 0) {
+ printk(
+ KERN_ERR
+ "%s: Error:"
+ " Failed to register card. error %d.\n",
+ __func__,
+ ret);
+ goto err2;
+ }
+
+ return 0;
+
+err2:
+ snd_soc_free_pcms(socdev);
+err1:
+ kfree(codec);
+ return ret;
+}
+
+static int nullcodec_codec_remove(struct platform_device *pdev)
+{
+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = socdev->codec;
+
+ if (!codec)
+ return 0;
+
+ snd_soc_free_pcms(socdev);
+ kfree(socdev->codec);
+
+ printk(KERN_DEBUG "%s : pdev=%p.\n", __func__, pdev);
+ return 0;
+}
+
+static int nullcodec_codec_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ printk(KERN_DEBUG "%s : pdev=%p.\n", __func__, pdev);
+ return 0;
+}
+
+static int nullcodec_codec_resume(struct platform_device *pdev)
+{
+ printk(KERN_DEBUG "%s : pdev=%p.\n", __func__, pdev);
+ return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_cg29xx = {
+ .probe = nullcodec_codec_probe,
+ .remove = nullcodec_codec_remove,
+ .suspend = nullcodec_codec_suspend,
+ .resume = nullcodec_codec_resume
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_cg29xx);
+
+static int __devinit cg29xx_init(void)
+{
+ int ret1;
+ int ret2 = 0;
+
+ printk(
+ KERN_DEBUG
+ "%s called.\n",
+ __func__);
+
+ printk(
+ KERN_DEBUG
+ "%s: Register codec-dai.\n",
+ __func__);
+ ret1 = snd_soc_register_dai(&cg29xx_codec_dai[0]);
+ if (ret1 < 0) {
+ printk(
+ KERN_DEBUG
+ "%s: Error %d: Failed to register codec-dai.\n",
+ __func__,
+ ret1);
+ ret2 = -1;
+ }
+
+ return ret2;
+}
+
+static void cg29xx_exit(void)
+{
+ printk(KERN_DEBUG "%s called.\n", __func__);
+
+ printk(
+ KERN_DEBUG
+ "%s: Un-register codec-dai.\n",
+ __func__);
+ snd_soc_unregister_dai(&cg29xx_codec_dai[0]);
+}
+
+module_init(cg29xx_init);
+module_exit(cg29xx_exit);
+
+MODULE_DESCRIPTION("CG29xx driver");
+MODULE_AUTHOR("www.stericsson.com");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cg29xx.h b/sound/soc/codecs/cg29xx.h
new file mode 100755
index 00000000000..d515cd19d93
--- /dev/null
+++ b/sound/soc/codecs/cg29xx.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Roger Nilsson roger.xr.nilsson@stericsson.com
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 CG29XX_CODEC_H
+#define CG29XX_CODEC_H
+
+extern struct snd_soc_dai cg29xx_codec_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_cg29xx;
+
+#endif /* CG29XX_CODEC_H */
+
+
+
diff --git a/sound/soc/u8500/Kconfig b/sound/soc/u8500/Kconfig
deleted file mode 100755
index 2e008a7031e..00000000000
--- a/sound/soc/u8500/Kconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# U8500 SoC audio configuration
-#
-
-config SND_SOC_U8500
- bool "U8500"
- depends on SND_SOC
- default y
- help
- Support for sound devices specific to ARM architectures.
- Drivers that are implemented on ASoC can be found in
- "ALSA for SoC audio support" section.
-
diff --git a/sound/soc/u8500/Makefile b/sound/soc/u8500/Makefile
deleted file mode 100755
index 438c58f2ecd..00000000000
--- a/sound/soc/u8500/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-# U8500 Platform Support
-
-snd-soc-u8500-objs := u8500_pcm.o u8500_msp_dai.o
-snd-soc-u8500-ab3550-objs := mop500_ab3550.o
-
-obj-$(CONFIG_SND_SOC_U8500) += snd-soc-u8500.o
-obj-$(CONFIG_SND_SOC_AB3550) += snd-soc-u8500-ab3550.o
-
-
-
-
-
diff --git a/sound/soc/u8500/mop500_ab3550.c b/sound/soc/u8500/mop500_ab3550.c
deleted file mode 100755
index 743206ab72d..00000000000
--- a/sound/soc/u8500/mop500_ab3550.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- * Roger Nilsson roger.xr.nilsson@stericsson.com
- * for ST-Ericsson.
- *
- * License terms:
- *
- * 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/io.h>
-
-// SOC framework
-#include <sound/soc.h>
-
-// Platform driver
-#include "u8500_pcm.h"
-#include "u8500_msp_dai.h"
-
-// AB3550 codec
-extern struct snd_soc_dai ab3550_codec_dai;
-extern struct snd_soc_codec_device soc_codec_dev_ab3550;
-
-
-
-
-// Create the snd_soc_dai_link struct ---------------------------------------
-
-static int u8500_ab3550_startup(struct snd_pcm_substream *substream)
-{
- printk(KERN_DEBUG "MOP500_AB3550: u8500_ab3550_startup\n");
-
- return 0;
-}
-
-static void u8500_ab3550_shutdown(struct snd_pcm_substream *substream)
-{
- printk(KERN_DEBUG "MOP500_AB3550: u8500_ab3550_shutdown\n");
-}
-
-static int u8500_ab3550_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
-{
- printk(KERN_DEBUG "MOP500_AB3550: u8500_ab3550_hw_params\n");
-
- return 0;
-}
-
-static struct snd_soc_ops u8500_ab3550_ops = {
- .startup = u8500_ab3550_startup,
- .shutdown = u8500_ab3550_shutdown,
- .hw_params = u8500_ab3550_hw_params,
-};
-
-struct snd_soc_dai_link u8500_ab3550_dai[2] = {
- {
- .name = "ab3550_0",
- .stream_name = "ab3550_0",
- .cpu_dai = &u8500_msp_dai[0],
- .codec_dai = &ab3550_codec_dai,
- .init = NULL,
- .ops = &u8500_ab3550_ops,
- },
- {
- .name = "ab3550_1",
- .stream_name = "ab3550_1",
- .cpu_dai = &u8500_msp_dai[1],
- .codec_dai = &ab3550_codec_dai,
- .init = NULL,
- .ops = &u8500_ab3550_ops,
- },
-};
-EXPORT_SYMBOL(u8500_ab3550_dai);
-
-
-
-// Create the snd_soc_device struct ---------------------------------------
-
-static struct snd_soc_card u8500_ab3550 = {
- .name = "u8500-ab3550",
- .probe = NULL,
- .dai_link = u8500_ab3550_dai,
- .num_links = 2,
- .platform = &u8500_soc_platform,
-};
-
-struct snd_soc_device u8500_ab3550_snd_devdata = {
- .card = &u8500_ab3550,
- .codec_dev = &soc_codec_dev_ab3550,
-};
-EXPORT_SYMBOL(u8500_ab3550_snd_devdata);
-
-
-
-// Machine driver init and exit --------------------------------------------
-
-static struct platform_device *u8500_ab3550_snd_device;
-
-static int __init u8500_ab3550_init(void)
-{
- int ret, i;
- struct snd_soc_device *socdev;
- struct snd_soc_codec *codec;
-
- printk(KERN_DEBUG "MOP500_AB3550: u8500_ab3550_init\n");
-
- // Register platform
- printk(KERN_DEBUG "MOP500_AB3550: Register platform.\n");
- ret = snd_soc_register_platform(&u8500_soc_platform);
- if (ret < 0)
- printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register platform.\n");
-
- printk(KERN_DEBUG "MOP500_AB3550: Register codec dai.\n");
- snd_soc_register_dai(&ab3550_codec_dai);
- if (ret < 0)
- printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register codec dai.\n");
-
- for (i = 0; i < U8500_NBR_OF_DAI; i++) {
- printk(KERN_DEBUG "MOP500_AB3550: Register MSP dai %d.\n", i);
- ret = snd_soc_register_dai(&u8500_msp_dai[i]);
- if (ret < 0)
- printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register MSP dai %d.\n", i);
- }
-
- // Allocate platform device
- printk(KERN_DEBUG "MOP500_AB3550: Allocate platform device.\n");
- u8500_ab3550_snd_device = platform_device_alloc("soc-audio", -1);
- if (!u8500_ab3550_snd_device)
- return -ENOMEM;
-
- // Set platform drvdata
- printk(KERN_DEBUG "MOP500_AB3550: Set platform drvdata.\n");
- platform_set_drvdata(u8500_ab3550_snd_device, &u8500_ab3550_snd_devdata);
- if (ret < 0)
- printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to set drvdata.\n");
-
- // Add platform device
- printk(KERN_DEBUG "MOP500_AB3550: Add device.\n");
- u8500_ab3550_snd_devdata.dev = &u8500_ab3550_snd_device->dev;
- ret = platform_device_add(u8500_ab3550_snd_device);
- if (ret) {
- printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to add platform device.\n");
- platform_device_put(u8500_ab3550_snd_device);
- }
-
- // Register IS2-driver
- printk(KERN_DEBUG "MOP500_AB3550: Register I2S-driver.\n");
- ret = u8500_platform_registerI2S();
- if (ret < 0)
- printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register I2S-driver.\n");
-
- return ret;
-}
-
-
-
-static void __exit u8500_ab3550_exit(void)
-{
- printk(KERN_ALERT "MOP500_AB35500: u8500_ab3550_exit\n");
-
- platform_device_unregister(u8500_ab3550_snd_device);
-}
-
-module_init(u8500_ab3550_init);
-module_exit(u8500_ab3550_exit);
-
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/u8500/u8500_msp_dai.c b/sound/soc/u8500/u8500_msp_dai.c
deleted file mode 100755
index c5600669091..00000000000
--- a/sound/soc/u8500/u8500_msp_dai.c
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- * Roger Nilsson roger.xr.nilsson@stericsson.com
- * for ST-Ericsson.
- *
- * License terms:
- *
- * 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 <sound/soc.h>
-#include <sound/soc-dai.h>
-#include <asm/dma.h>
-#include "u8500_msp_dai.h"
-#include "u8500_pcm.h"
-
-#include <mach/msp.h>
-#include <linux/i2s/i2s.h>
-
-struct u8500_dai_private_t{
- spinlock_t lock;
- struct i2s_device *i2s;
- u8 rx_active;
- u8 tx_active;
-};
-
-struct u8500_dai_private_t msp_dai_private[U8500_NBR_OF_DAI] =
-{
- /* DAI 0 */
- {
- .lock = SPIN_LOCK_UNLOCKED,
- .i2s = NULL,
- .tx_active = 0,
- .rx_active = 0,
- },
- /* DAI 1 */
- {
- .lock = SPIN_LOCK_UNLOCKED,
- .i2s = NULL,
- .tx_active = 0,
- .rx_active = 0,
- },
-};
-
-static void compile_msp_config(struct msp_generic_config *msp_config,struct snd_pcm_substream *substream)
-{
- int channels;
-
- msp_config->tx_clock_sel = 0;
- msp_config->rx_clock_sel = 0;
- msp_config->tx_frame_sync_sel = 0;
- msp_config->rx_frame_sync_sel = 0;
- msp_config->input_clock_freq = MSP_INPUT_FREQ_48MHZ;
- msp_config->srg_clock_sel = 0;
- msp_config->rx_frame_sync_pol = RX_FIFO_SYNC_HI;
- msp_config->tx_frame_sync_pol = TX_FIFO_SYNC_HI;
- msp_config->rx_fifo_config = RX_FIFO_ENABLE;
- msp_config->tx_fifo_config = TX_FIFO_ENABLE;
- msp_config->spi_clk_mode = SPI_CLK_MODE_NORMAL;
- msp_config->spi_burst_mode = 0;
- msp_config->handler = u8500_pcm_dma_eot_handler;
- msp_config->tx_callback_data = substream;
- msp_config->tx_data_enable = 0;
- msp_config->rx_callback_data = substream;
- msp_config->loopback_enable = 0;
- msp_config->multichannel_configured = 0;
- msp_config->def_elem_len = 0;
- msp_config->default_protocol_desc = 0;
- msp_config->protocol_desc.rx_phase_mode = MSP_SINGLE_PHASE;
- msp_config->protocol_desc.tx_phase_mode = MSP_SINGLE_PHASE;
- msp_config->protocol_desc.rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
- msp_config->protocol_desc.tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
- msp_config->protocol_desc.rx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
- msp_config->protocol_desc.tx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
- msp_config->protocol_desc. rx_frame_length_1 = MSP_FRAME_LENGTH_2;
- msp_config->protocol_desc. rx_frame_length_2 = MSP_FRAME_LENGTH_2;
- msp_config->protocol_desc.tx_frame_length_1 = MSP_FRAME_LENGTH_2;
- msp_config->protocol_desc.tx_frame_length_2 = MSP_FRAME_LENGTH_2;
- msp_config->protocol_desc. rx_element_length_1 = MSP_ELEM_LENGTH_32;
- msp_config->protocol_desc.rx_element_length_2 = MSP_ELEM_LENGTH_32;
- msp_config->protocol_desc.tx_element_length_1 = MSP_ELEM_LENGTH_32;
- msp_config->protocol_desc.tx_element_length_2 = MSP_ELEM_LENGTH_32;
- msp_config->protocol_desc.rx_data_delay = MSP_DELAY_0;
- msp_config->protocol_desc.tx_data_delay = MSP_DELAY_0;
- msp_config->protocol_desc.rx_clock_pol = MSP_FALLING_EDGE;
- msp_config->protocol_desc.tx_clock_pol = MSP_RISING_EDGE;
- msp_config->protocol_desc. rx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
- msp_config->protocol_desc.tx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
- msp_config->protocol_desc.rx_half_word_swap = MSP_HWS_NO_SWAP;
- msp_config->protocol_desc.tx_half_word_swap = MSP_HWS_NO_SWAP;
- msp_config->protocol_desc.compression_mode = MSP_COMPRESS_MODE_LINEAR;
- msp_config->protocol_desc.expansion_mode = MSP_EXPAND_MODE_LINEAR;
- msp_config->protocol_desc.spi_clk_mode = MSP_SPI_CLOCK_MODE_NON_SPI;
- msp_config->protocol_desc.spi_burst_mode = MSP_SPI_BURST_MODE_DISABLE;
- msp_config->protocol_desc.frame_sync_ignore = MSP_FRAME_SYNC_IGNORE;
- msp_config->protocol_desc.frame_period = 63;
- msp_config->protocol_desc.frame_width = 31;
- msp_config->protocol_desc.total_clocks_for_one_frame = 64;
- msp_config->protocol = MSP_I2S_PROTOCOL; //MSP_PCM_PROTOCOL
- msp_config->direction = MSP_BOTH_T_R_MODE;
- msp_config->frame_size = 0;
- msp_config->frame_freq = 48000;
- channels = 2;
- msp_config->data_size = (channels == 1) ? MSP_DATA_SIZE_16BIT : MSP_DATA_SIZE_32BIT;
- msp_config->work_mode = MSP_DMA_MODE;
- msp_config->multichannel_configured = 1;
- msp_config->multichannel_config.tx_multichannel_enable = 1;
- msp_config->multichannel_config.tx_channel_0_enable = 0x0000003; //Channel 1 and channel 2
- msp_config->multichannel_config.tx_channel_1_enable = 0x0000000; //Channel 1 and channel 2
- msp_config->multichannel_config.tx_channel_2_enable = 0x0000000; //Channel 1 and channel 2
- msp_config->multichannel_config.tx_channel_3_enable = 0x0000000; //Channel 1 and channel 2
- msp_config->multichannel_config.rx_multichannel_enable = 1;
- msp_config->multichannel_config.rx_channel_0_enable = 0x0000003; //Channel 1 and channel 2
- msp_config->multichannel_config.rx_channel_1_enable = 0x0000000; //Channel 1 and channel 2
- msp_config->multichannel_config.rx_channel_2_enable = 0x0000000; //Channel 1 and channel 2
- msp_config->multichannel_config.rx_channel_3_enable = 0x0000000; //Channel 1 and channel 2
-}
-
-static void u8500_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *msp_dai)
-{
- int ret = 0;
- unsigned long flags;
- struct u8500_dai_private_t *dai_private;
-
- printk(KERN_ALERT "STW4500: u8500_dai_shutdown\n");
-
- if (!(dai_private = msp_dai->private_data))
- return;
-
- spin_lock_irqsave(&dai_private->lock, flags);
-
- /* Mark the stopped direction as inactive */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dai_private->tx_active = 0;
- } else {
- dai_private->rx_active = 0;
- }
-
- if (0 == dai_private->tx_active && 0 == dai_private->rx_active) {
- msp_dai->private_data = NULL;
-
-
- ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_ALL);
-
- if (ret)
- printk(KERN_WARNING "Error closing i2s\n");
-
- } else {
-
- if(dai_private->tx_active)
- ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_RECEIVE);
- else
- ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_TRANSMIT);
-
- if (ret)
- printk(KERN_WARNING "Error closing i2s\n");
- }
-
- spin_unlock_irqrestore(&dai_private->lock, flags);
-
- return;
-}
-
-static int u8500_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *msp_dai)
-{
- struct u8500_dai_private_t *dai_private;
-
- printk(KERN_ALERT "STW4500: u8500_dai_startup\n");
-
- dai_private=&msp_dai_private[msp_dai->id];
- /* Store pointer to private data */
- msp_dai->private_data = dai_private;
-
- if (dai_private->i2s == NULL)
- {
- printk(KERN_ALERT "u8500_pcm_open: Error: i2sdrv.i2s == NULL.\n");
- return -1;
- }
- if (dai_private->i2s->controller == NULL)
- {
- printk(KERN_ALERT "u8500_pcm_open: Error: i2sdrv.i2s->controller == NULL.\n");
- return -1;
- }
-
-
- return 0;
-}
-
-static int u8500_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *msp_dai)
-{
- int ret = 0;
- unsigned long flags_private;
- struct u8500_dai_private_t *dai_private;
- struct msp_generic_config msp_config;
-
- printk(KERN_ALERT "STW4500: u8500_dai_prepare\n");
-
- dai_private = msp_dai->private_data;
-
- spin_lock_irqsave(&dai_private->lock, flags_private);
-
- /* Is the other direction already active? */
- if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- dai_private->rx_active) ||
- (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
- dai_private->tx_active)) {
- /* TODO: Check if sample rate match ! */
- /*
- if ( != runtime->rate) {
- printk(KERN_ERR "CPU_DAI configuration mismatch with already opened stream!\n");
- ret = -EBUSY;
- goto cleanup;
- }
- */
- } else if (!dai_private->rx_active && !dai_private->tx_active) {
- ret = 0;
-
- compile_msp_config(&msp_config,substream);
-
- ret = i2s_setup(dai_private->i2s->controller, &msp_config);
- if (ret < 0) {
- printk(KERN_ALERT "u8500_dai_prepare: i2s_setup failed! ret = %d\n", ret);
- goto cleanup;
- }
- }
-
- /* Mark the newly started stream as active */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dai_private->tx_active = 1;
- } else {
- dai_private->rx_active = 1;
- }
-
-cleanup:
- spin_unlock_irqrestore(&dai_private->lock, flags_private);
- return ret;
-}
-
-static int u8500_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *msp_dai)
-{
- printk(KERN_ALERT "STW4500: u8500_dai_hw_params\n");
-
- return 0;
-}
-
-static int u8500_dai_set_dai_fmt(struct snd_soc_dai *msp_dai, unsigned int fmt)
-{
- printk(KERN_ALERT "STW4500: u8500_dai_set_dai_fmt\n");
- return 0;
-}
-
-static int u8500_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *msp_dai)
-{
- unsigned long flags;
- int ret = 0;
- struct u8500_dai_private_t *u8500_dai_private = msp_dai->private_data;
-
- printk(KERN_ALERT "STW4500: u8500_dai_trigger\n");
-
- spin_lock_irqsave(&u8500_dai_private->lock, flags);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- ret = 0;
- break;
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = 0;
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- ret = 0;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = 0;
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- spin_unlock_irqrestore(&u8500_dai_private->lock, flags);
- return ret;
-}
-
-int u8500_i2s_send_data(void *data, size_t bytes, int dai_idx)
-{
- unsigned long flags;
- struct u8500_dai_private_t *dai_private;
- struct i2s_message message;
- struct i2s_device *i2s_dev;
- int ret = 0;
- dai_private=&msp_dai_private[dai_idx];
-
- spin_lock_irqsave(&dai_private->lock, flags);
-
- i2s_dev=dai_private->i2s;
-
-
- if(!dai_private->tx_active)
- {
- printk(KERN_ERR "u8500_i2s_send_data: I2S controller not available\n");
- goto cleanup;
- }
-
- message.txbytes = bytes;
- message.txdata = data;
- message.rxbytes = 0;
- message.rxdata = NULL;
- message.dma_flag = 1;
-
- ret = i2s_transfer(i2s_dev->controller, &message);
- if(ret < 0)
- {
- printk(KERN_ERR "u8500_i2s_send_data: Error: i2s_transfer failed!\n");
- goto cleanup;
- }
-
-cleanup:
- spin_unlock_irqrestore(&dai_private->lock, flags);
- return ret;
-}
-
-int u8500_i2s_receive_data(void *data, size_t bytes, int dai_idx)
-{
- unsigned long flags;
- struct u8500_dai_private_t *dai_private;
- struct i2s_message message;
- struct i2s_device *i2s_dev;
- int ret = 0;
- dai_private=&msp_dai_private[dai_idx];
-
- spin_lock_irqsave(&dai_private->lock, flags);
-
- i2s_dev=dai_private->i2s;
-
-
- if(!dai_private->rx_active)
- {
- printk(KERN_ERR "u8500_i2s_receive_data: I2S controller not available\n");
- goto cleanup;
- }
-
- message.rxbytes = bytes;
- message.rxdata = data;
- message.txbytes = 0;
- message.txdata = NULL;
- message.dma_flag = 1;
-
- ret = i2s_transfer(i2s_dev->controller, &message);
- if(ret < 0)
- {
- printk(KERN_ERR "u8500_i2s_receive_data: Error: i2s_transfer failed!\n");
- goto cleanup;
- }
-
-cleanup:
- spin_unlock_irqrestore(&dai_private->lock, flags);
- return ret;
-}
-
-#define u8500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-#define u8500_I2S_FORMATS ( SNDRV_PCM_FMTBIT_S16_LE )
-
-#define dai_suspend NULL
-#define dai_resume NULL
-#define u8500_dai_set_dai_sysclk NULL
-
-struct snd_soc_dai_ops u8500_msp_dai_ops_0 =
-{
- .set_sysclk=u8500_dai_set_dai_sysclk,
- .set_fmt=u8500_dai_set_dai_fmt,
- .startup = u8500_dai_startup,
- .shutdown = u8500_dai_shutdown,
- .prepare = u8500_dai_prepare,
- .trigger = u8500_dai_trigger,
- .hw_params = u8500_dai_hw_params,
-};
-
-struct snd_soc_dai_ops u8500_msp_dai_ops_1 =
-{
- .set_sysclk=u8500_dai_set_dai_sysclk,
- .set_fmt=u8500_dai_set_dai_fmt,
- .startup = u8500_dai_startup,
- .shutdown = u8500_dai_shutdown,
- .prepare = u8500_dai_prepare,
- .trigger = u8500_dai_trigger,
- .hw_params = u8500_dai_hw_params,
-};
-
-struct snd_soc_dai u8500_msp_dai[U8500_NBR_OF_DAI] =
-{
- {
- .name = "u8500_i2s-0",
- .id = 0,
- //.type = SND_SOC_DAI_I2S,
- .suspend = dai_suspend,
- .resume = dai_resume,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = u8500_I2S_RATES,
- .formats = u8500_I2S_FORMATS,},
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = u8500_I2S_RATES,
- .formats = u8500_I2S_FORMATS,},
- .ops = &u8500_msp_dai_ops_0,
- .private_data = &msp_dai_private[0],
- },
- {
- .name = "u8500_i2s-1",
- .id = 1,
- //.type = SND_SOC_DAI_I2S,
- .suspend = dai_suspend,
- .resume = dai_resume,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = u8500_I2S_RATES,
- .formats = u8500_I2S_FORMATS,},
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = u8500_I2S_RATES,
- .formats = u8500_I2S_FORMATS,},
- .ops = &u8500_msp_dai_ops_1,
- .private_data = &msp_dai_private[1],
- },
-};
-
-EXPORT_SYMBOL(u8500_msp_dai);
-
-
-
-static int i2sdrv_probe(struct i2s_device *i2s)
-{
- unsigned long flags;
- struct u8500_dai_private_t *u8500_dai_private;
-
- u8500_dai_private=&msp_dai_private[0];
- printk(KERN_DEBUG "i2sdrv_probe: Enter.\n");
-
- spin_lock_irqsave(&u8500_dai_private->lock, flags);
- u8500_dai_private->i2s = i2s;
- spin_unlock_irqrestore(&u8500_dai_private->lock, flags);
-
- try_module_get(i2s->controller->dev.parent->driver->owner);
- i2s_set_drvdata(i2s, (void*)u8500_dai_private);
-
- return 0;
-}
-
-
-
-static int i2sdrv_remove(struct i2s_device *i2s)
-{
- unsigned long flags;
- struct u8500_dai_private_t *u8500_dai_private = i2s_get_drvdata(i2s);
-
- printk(KERN_DEBUG "U8500_PCM: i2sdrv_remove\n");
-
- spin_lock_irqsave(&u8500_dai_private->lock, flags);
-
- u8500_dai_private->i2s = NULL;
- i2s_set_drvdata(i2s, NULL);
- spin_unlock_irqrestore(&u8500_dai_private->lock, flags);
-
-
- printk(KERN_ALERT "i2sdrv_remove: module_put\n");
- module_put(i2s->controller->dev.parent->driver->owner);
-
- return 0;
-}
-
-
-
-static const struct i2s_device_id dev_id_table[] = {
- { "i2s_device.0", 0, 0 },
- { },
-};
-MODULE_DEVICE_TABLE(i2s, acodec_id_table);
-
-
-
-static struct i2s_driver i2sdrv_i2s = {
- .driver = {
- .name = "nomadik_acodec",
- .owner = THIS_MODULE,
- },
- .probe = i2sdrv_probe,
- .remove = __devexit_p(i2sdrv_remove),
- .id_table = dev_id_table,
-};
-
-int u8500_platform_registerI2S(void)
-{
- int ret;
-
- printk(KERN_ALERT "u8500_platform_registerI2S: Register I2S-driver.\n");
- ret = i2s_register_driver(&i2sdrv_i2s);
- if (ret < 0) {
- printk(KERN_ALERT "u8500_platform_registerI2S: Unable to register I2S-driver\n");
- }
-
- return ret;
-}
diff --git a/sound/soc/u8500/u8500_msp_dai.h b/sound/soc/u8500/u8500_msp_dai.h
deleted file mode 100755
index 077f1c98432..00000000000
--- a/sound/soc/u8500/u8500_msp_dai.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- * Roger Nilsson roger.xr.nilsson@stericsson.com
- * for ST-Ericsson.
- *
- * License terms:
- *
- * 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 U8500_msp_dai_H
-#define U8500_msp_dai_H
-
-#include <linux/types.h>
-#include <linux/spinlock.h>
-
-#define U8500_NBR_OF_DAI 2
-
-extern struct snd_soc_dai u8500_msp_dai[U8500_NBR_OF_DAI];
-
-int u8500_platform_registerI2S(void);
-int u8500_i2s_send_data(void *data, size_t bytes, int dai_idx);
-int u8500_i2s_receive_data(void *data, size_t bytes, int dai_idx);
-
-#endif
diff --git a/sound/soc/u8500/u8500_pcm.c b/sound/soc/u8500/u8500_pcm.c
deleted file mode 100755
index edd22bec215..00000000000
--- a/sound/soc/u8500/u8500_pcm.c
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- * Roger Nilsson roger.xr.nilsson@stericsson.com
- * for ST-Ericsson.
- *
- * License terms:
- *
- * 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 <asm/page.h>
-
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-
-#include <linux/dma-mapping.h>
-
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "u8500_pcm.h"
-#include "u8500_msp_dai.h"
-
-
-
-// Local variables -------------------------------------------------------------------
-
-// struct dma_chan
-struct u8500_pcm_transfer {
- spinlock_t lock;
- dma_addr_t addr;
- u32 dma_chan_id;
- u32 period;
- struct dma_chan *dma_chan;
-};
-
-static struct snd_pcm_hardware u8500_pcm_hw_playback = {
- .info =
- (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE),
- .formats =
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |
- SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_BE,
- .rates = SNDRV_PCM_RATE_KNOT,
- .rate_min = U8500_PLATFORM_MIN_RATE_PLAYBACK,
- .rate_max = U8500_PLATFORM_MAX_RATE_PLAYBACK,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = NMDK_BUFFER_SIZE,
- .period_bytes_min = 128,
- .period_bytes_max = PAGE_SIZE,
- .periods_min = NMDK_BUFFER_SIZE / PAGE_SIZE,
- .periods_max = NMDK_BUFFER_SIZE / 128
-};
-
-static struct snd_pcm_hardware u8500_pcm_hw_capture = {
- .info =
- (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE),
- .formats =
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |
- SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_BE,
- .rates = SNDRV_PCM_RATE_KNOT,
- .rate_min = U8500_PLATFORM_MIN_RATE_CAPTURE,
- .rate_max = U8500_PLATFORM_MAX_RATE_CAPTURE,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = NMDK_BUFFER_SIZE,
- .period_bytes_min = 128,
- .period_bytes_max = PAGE_SIZE,
- .periods_min = NMDK_BUFFER_SIZE / PAGE_SIZE,
- .periods_max = NMDK_BUFFER_SIZE / 128
-};
-
-static struct u8500_pcm_private_t u8500_pcm_private;
-
-
-
-// DMA operations -----------------------------------------------------------------------------
-
-/**
- * nomadik_alsa_dma_start - used to transmit or recive a dma chunk
- * @stream - specifies the playback/record stream structure
- */
-static void u8500_pcm_dma_start(struct snd_pcm_substream *substream)
-{
- unsigned int offset, dma_size;
- struct snd_pcm_runtime *runtime = substream->runtime;
- int stream_id = substream->pstr->stream;
- u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
- printk(KERN_DEBUG "u8500_pcm_dma_start: Enter.\n");
-
- dma_size = frames_to_bytes(runtime, runtime->period_size);
- offset = dma_size * stream_p->period;
- stream_p->old_offset = offset;
- printk(KERN_DEBUG "u8500_pcm_dma_start: address = %x size=%d\n", (runtime->dma_addr + offset), dma_size);
-
- if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- u8500_i2s_send_data((void*)(runtime->dma_addr + offset), dma_size, 0);
- }
- else {
- u8500_i2s_receive_data((void *)(runtime->dma_addr + offset), dma_size, 0);
- }
-
- stream_p->period++;
- stream_p->period %= runtime->periods;
- stream_p->periods++;
-}
-
-static void u8500_pcm_dma_hw_free(struct device *dev, struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = runtime->dma_buffer_p;
-
- if (runtime->dma_area == NULL)
- return;
-
- if (buf != &substream->dma_buffer) {
- dma_free_coherent(buf->dev.dev, buf->bytes, buf->area, buf->addr);
- kfree(runtime->dma_buffer_p);
- }
-
- snd_pcm_set_runtime_buffer(substream, NULL);
-}
-
-/**
- * dma_eot_handler
- * @data - pointer to structure set in the dma callback handler
- * @event - specifies the DMA event: transfer complete/error
- *
- * This is the PCM tasklet handler linked to a pipe, its role is to tell
- * the PCM middler layer whene the buffer position goes across the prescribed
- * period size.To inform of this the snd_pcm_period_elapsed is called.
- *
- * this callback will be called in case of DMA_EVENT_TC only
- */
-irqreturn_t u8500_pcm_dma_eot_handler(void *data, int irq)
-{
- struct snd_pcm_substream *substream = data;
- int stream_id = substream->pstr->stream;
- u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
- /* snd_pcm_period_elapsed() is _not_ to be protected
- */
- printk(KERN_DEBUG "u8500_pcm_dma_eot_handler: Enter.\n");
-
- if (substream)
- snd_pcm_period_elapsed(substream);
- if(stream_p->state == ALSA_STATE_PAUSED)
- return IRQ_HANDLED;
- if (stream_p->active == 1) {
- u8500_pcm_dma_start(substream);
- }
- return IRQ_HANDLED;
-}
-EXPORT_SYMBOL(u8500_pcm_dma_eot_handler);
-
-
-
-
-// snd_pcm_ops -----------------------------------------------------------------------------
-
-static int u8500_pcm_open(struct snd_pcm_substream *substream)
-{
- int status;
- struct snd_pcm_runtime *runtime = substream->runtime;
- int stream_id = substream->pstr->stream;
- u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
- printk(KERN_DEBUG "u8500_pcm_open: Enter.\n");
-
- status = 0;
- printk(KERN_DEBUG "u8500_pcm_open: Setting HW-config.\n");
- if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
- {
- runtime->hw = u8500_pcm_hw_playback;
- }
- else
- {
- runtime->hw = u8500_pcm_hw_capture;
- }
-
- init_MUTEX(&(stream_p->alsa_sem));
- init_completion(&(stream_p->alsa_com));
- stream_p->state = ALSA_STATE_RUNNING;
- printk(KERN_ALERT "u8500_pcm_open: Exit.\n");
-
- return 0;
-}
-
-static int u8500_pcm_close(struct snd_pcm_substream *substream)
-{
- printk(KERN_DEBUG "u8500_pcm_close: Enter.\n");
-
- return 0;
-}
-
-static int u8500_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = runtime->dma_buffer_p;
- int ret = 0;
- int size;
-
- printk(KERN_DEBUG "u8500_pcm_hw_params: Enter.\n");
-
- size = params_buffer_bytes(hw_params);
-
- if (buf) {
- if (buf->bytes >= size)
- goto out;
- u8500_pcm_dma_hw_free(NULL, substream);
- }
-
- if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) {
- buf = &substream->dma_buffer;
- } else {
- buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
- if (!buf)
- goto nomem;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = NULL;
- buf->area = dma_alloc_coherent(NULL, size, &buf->addr, GFP_KERNEL);
- buf->bytes = size;
- buf->private_data = NULL;
-
- if (!buf->area)
- goto free;
- }
- snd_pcm_set_runtime_buffer(substream, buf);
- ret = 1;
- out:
- runtime->dma_bytes = size;
- return ret;
-
- free:
- kfree(buf);
- nomem:
- return -ENOMEM;
-}
-
-static int u8500_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- printk(KERN_ALERT "u8500_pcm_hw_free: Enter.\n");
-
- u8500_pcm_dma_hw_free(NULL, substream);
-
- return 0;
-}
-
-static int u8500_pcm_prepare(struct snd_pcm_substream *substream)
-{
- printk(KERN_DEBUG "u8500_pcm_prepare: Enter.\n");
-
- return 0;
-}
-
-static int u8500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- int stream_id = substream->pstr->stream;
- u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
- printk(KERN_DEBUG "u8500_pcm_trigger: Enter.\n");
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- // Start DMA transfer
- printk(KERN_DEBUG "u8500_pcm_trigger: TRIGGER START\n");
- if (stream_p->active == 0) {
- stream_p->active = 1;
- u8500_pcm_dma_start(substream);
- break;
- }
- printk(KERN_DEBUG "u8500_pcm_trigger: Error: H/w is busy\n");
- return -EINVAL;
-
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- // Pause DMA transfer
- printk(KERN_ALERT "u8500_pcm_trigger: SNDRV_PCM_TRIGGER_PAUSE\n");
- if (stream_p->state == ALSA_STATE_RUNNING) {
- stream_p->state = ALSA_STATE_PAUSED;
- }
-
- break;
-
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- // Resume DMA transfer
- printk(KERN_DEBUG "u8500_pcm_trigger: SNDRV_PCM_TRIGGER_RESUME\n");
- if (stream_p->state == ALSA_STATE_PAUSED) {
- stream_p->state = ALSA_STATE_RUNNING;
- u8500_pcm_dma_start(substream);
- }
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- // Stop DMA transfer
- printk(KERN_DEBUG "u8500_pcm_trigger: SNDRV_PCM_TRIGGER_STOP\n");
- if (stream_p->active == 1) {
- stream_p->active = 0;
- stream_p->period = 0;
- }
- break;
-
- default:
- printk(KERN_ERR "u8500_pcm_trigger: Invalid command in pcm trigger.\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static snd_pcm_uframes_t u8500_pcm_pointer(struct snd_pcm_substream *substream)
-{
- unsigned int offset;
- struct snd_pcm_runtime *runtime = substream->runtime;
- int stream_id = substream->pstr->stream;
- u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
- printk(KERN_DEBUG "u8500_pcm_pointer: Enter.\n");
-
- offset = bytes_to_frames(runtime, stream_p->old_offset);
- if (offset < 0 || stream_p->old_offset < 0)
- printk(KERN_DEBUG "u8500_pcm_pointer: Offset=%i %i\n", offset, stream_p->old_offset);
-
- return offset;
-}
-
-static int u8500_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- printk(KERN_DEBUG "u8500_pcm_mmap: Enter.\n");
-
- return dma_mmap_coherent(NULL, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
-}
-
-static struct snd_pcm_ops u8500_pcm_ops = {
- .open = u8500_pcm_open,
- .close = u8500_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = u8500_pcm_hw_params,
- .hw_free = u8500_pcm_hw_free,
- .prepare = u8500_pcm_prepare,
- .trigger = u8500_pcm_trigger,
- .pointer = u8500_pcm_pointer,
- .mmap = u8500_pcm_mmap
-};
-
-
-
-// snd_soc_platform -----------------------------------------------------------------------------
-
-int u8500_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm)
-{
- int i;
-
- printk(KERN_DEBUG "u8500_pcm_new: Enter.\n");
-
- printk(KERN_DEBUG "u8500_pcm_new: pcm = %d\n", (int)pcm);
-
- pcm->info_flags = 0;
- strcpy(pcm->name, "nomadik_asoc");
-
- for (i = 0; i < NO_OF_STREAMS; i++)
- {
- ;
- u8500_pcm_private.streams[i].active = 0;
- u8500_pcm_private.streams[i].period = 0;
- u8500_pcm_private.streams[i].periods = 0;
- u8500_pcm_private.streams[i].old_offset = 0;
- }
-
- printk(KERN_DEBUG "u8500_pcm_new: pcm->name = %s\n", pcm->name);
-
- return 0;
-}
-
-static void u8500_pcm_free(struct snd_pcm *pcm)
-{
- printk(KERN_DEBUG "u8500_pcm_free: Enter.\n");
-
- //u8500_pcm_dma_hw_free(NULL, substream);
-}
-
-static int u8500_pcm_suspend(struct snd_soc_dai *dai)
-{
- printk(KERN_DEBUG "u8500_pcm_suspend: Enter.\n");
-
- return 0;
-}
-
-static int u8500_pcm_resume(struct snd_soc_dai *dai)
-{
- printk(KERN_DEBUG "u8500_pcm_resume: Enter.\n");
-
- return 0;
-}
-
-struct snd_soc_platform u8500_soc_platform = {
- .name = "u8500-audio",
- .pcm_ops = &u8500_pcm_ops,
- .pcm_new = u8500_pcm_new,
- .pcm_free = u8500_pcm_free,
- .suspend = u8500_pcm_suspend,
- .resume = u8500_pcm_resume,
-};
-EXPORT_SYMBOL(u8500_soc_platform);
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sound/soc/u8500/u8500_pcm.h b/sound/soc/u8500/u8500_pcm.h
deleted file mode 100755
index 59578718d09..00000000000
--- a/sound/soc/u8500/u8500_pcm.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- * Roger Nilsson roger.xr.nilsson@stericsson.com
- * for ST-Ericsson.
- *
- * License terms:
- *
- * 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 U8500_PCM_H
-#define U8500_PCM_H
-
-#include <mach/msp.h>
-
-#define NMDK_BUFFER_SIZE (64*1024)
-
-#define U8500_PLATFORM_MIN_RATE_PLAYBACK 48000
-#define U8500_PLATFORM_MAX_RATE_PLAYBACK 48000
-#define U8500_PLATFORM_MIN_RATE_CAPTURE 48000
-#define U8500_PLATFORM_MAX_RATE_CAPTURE 48000
-#define U8500_PLATFORM_MAX_NO_OF_RATES 1
-
-#define NUMBER_OUTPUT_DEVICE 5
-#define NUMBER_INPUT_DEVICE 10
-
-#define NO_OF_STREAMS 2
-
-enum alsa_state {
- ALSA_STATE_PAUSED,
- ALSA_STATE_RUNNING
-};
-
-extern struct snd_soc_platform u8500_soc_platform;
-
-typedef struct {
- int stream_id; /* stream identifier */
- int active; /* we are using this stream for transfer now */
- int period; /* current transfer period */
- int periods; /* current count of periods registerd in the DMA engine */
- enum alsa_state state;
- unsigned int old_offset;
- struct semaphore alsa_sem;
- struct completion alsa_com;
-} u8500_pcm_stream_t;
-
-struct u8500_pcm_private_t {
- u8500_pcm_stream_t streams[2];
-};
-
-irqreturn_t u8500_pcm_dma_eot_handler(void *data, int irq);
-
-#endif
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig
new file mode 100755
index 00000000000..90b28e5c4e2
--- /dev/null
+++ b/sound/soc/ux500/Kconfig
@@ -0,0 +1,26 @@
+#
+# Ux500 SoC audio configuration
+#
+
+config SND_SOC_UX500
+ bool "ux500"
+ depends on SND_SOC && STM_I2S && STM_MSP_I2S
+ default y
+ help
+ Say Y if you want to add support for the codecs attached to
+ the I2S of the ux500. You will also need
+ to select the audio codecs to support below.
+
+config SND_SOC_UX500_AB3550
+ tristate "SoC Audio support for ux500 w break-out card AB3550"
+ depends on SND_SOC_UX500 && AB3550_CORE
+ select SND_SOC_AB3550
+ help
+ Say Y if you want to add support for SoC audio on ux500 w break-out card AB3550.
+
+config SND_SOC_UX500_CG29XX
+ tristate "SoC Audio support for ux500 w break-out card CG29xx"
+ depends on SND_SOC_UX500 && !SND_SOC_UX500_AB3550
+ select SND_SOC_CG29XX
+ help
+ Say Y if you want to add support for SoC audio on ux500 w break-out card CG29XX.
diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile
new file mode 100755
index 00000000000..3eff08bb0c9
--- /dev/null
+++ b/sound/soc/ux500/Makefile
@@ -0,0 +1,9 @@
+# Ux500 Platform Support
+
+snd-soc-ux500-objs := ux500_pcm.o ux500_msp_dai.o
+snd-soc-ux500-ab3550-objs := ux500_ab3550.o
+snd-soc-ux500-cg29xx-objs := ux500_cg29xx.o
+
+obj-$(CONFIG_SND_SOC_UX500) += snd-soc-ux500.o
+obj-$(CONFIG_SND_SOC_UX500_AB3550) += snd-soc-ux500-ab3550.o
+obj-$(CONFIG_SND_SOC_UX500_CG29XX) += snd-soc-ux500-cg29xx.o
diff --git a/sound/soc/ux500/ux500_ab3550.c b/sound/soc/ux500/ux500_ab3550.c
new file mode 100755
index 00000000000..f8132b2aac5
--- /dev/null
+++ b/sound/soc/ux500/ux500_ab3550.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ * Roger Nilsson roger.xr.nilsson@stericsson.com
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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/io.h>
+#include <sound/soc.h>
+
+#include "ux500_pcm.h"
+#include "ux500_msp_dai.h"
+#include "mach/hardware.h"
+
+#include "../codecs/ab3550.h"
+
+static struct platform_device *ux500_ab3550_platform_device;
+
+#define AB3550_DAI_FMT_I2S_M (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM)
+#define AB3550_DAI_FMT_I2S_S (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS)
+#define AB3550_DAI_FMT AB3550_DAI_FMT_I2S_S
+
+static int ux500_ab3550_startup(struct snd_pcm_substream *substream)
+{
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: Enter\n",
+ __func__);
+ return 0;
+}
+
+static void ux500_ab3550_shutdown(struct snd_pcm_substream *substream)
+{
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: Enter\n",
+ __func__);
+}
+
+static int ux500_ab3550_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 ifid, ret = 0;
+
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: Enter\n",
+ __func__);
+
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: substream->pcm->name = %s\n"
+ "substream->pcm->id = %s.\n"
+ "substream->name = %s.\n"
+ "substream->number = %d.\n",
+ __func__,
+ substream->pcm->name,
+ substream->pcm->id,
+ substream->name,
+ substream->number);
+
+ for (ifid = 0; ifid < ARRAY_SIZE(ab3550_codec_dai); ifid++) {
+ if (strcmp(codec_dai->name, ab3550_codec_dai[ifid].name) == 0)
+ break;
+ }
+
+ if (codec_dai->ops.set_fmt) {
+ ret = snd_soc_dai_set_fmt(codec_dai, AB3550_DAI_FMT);
+ if (ret < 0) {
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: snd_soc_dai_set_fmt failed with %d.\n",
+ __func__,
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, AB3550_DAI_FMT);
+
+ if (ret < 0) {
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: snd_soc_dai_set_fmt"
+ " failed with %d.\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static struct snd_soc_ops ux500_ab3550_ops = {
+ .startup = ux500_ab3550_startup,
+ .shutdown = ux500_ab3550_shutdown,
+ .hw_params = ux500_ab3550_hw_params,
+};
+
+struct snd_soc_dai_link ux500_ab3550_dai_links[] = {
+ {
+ .name = "ab3550_0",
+ .stream_name = "ab3550_0",
+ .cpu_dai = &ux500_msp_dai[0],
+ .codec_dai = &ab3550_codec_dai[0],
+ .init = NULL,
+ .ops = &ux500_ab3550_ops,
+ },
+ {
+ .name = "ab3550_1",
+ .stream_name = "ab3550_1",
+ .cpu_dai = &ux500_msp_dai[1],
+ .codec_dai = &ab3550_codec_dai[1],
+ .init = NULL,
+ .ops = &ux500_ab3550_ops,
+ },
+};
+
+static struct snd_soc_card ux500_ab3550 = {
+ .name = "ab3550",
+ .probe = NULL,
+ .dai_link = ux500_ab3550_dai_links,
+ .num_links = ARRAY_SIZE(ux500_ab3550_dai_links),
+ .platform = &ux500_soc_platform,
+};
+
+struct snd_soc_device ux500_ab3550_drvdata = {
+ .card = &ux500_ab3550,
+ .codec_dev = &soc_codec_dev_ab3550,
+};
+
+static int __init mop500_ab3550_soc_init(void)
+{
+ int i;
+ int ret = 0;
+
+ pr_debug("%s: Enter\n",
+ __func__);
+ pr_debug("%s: Card name: %s\n",
+ __func__,
+ ux500_ab3550_drvdata.card->name);
+
+ for (i = 0; i < ARRAY_SIZE(ux500_ab3550_dai_links); i++) {
+ pr_debug("%s: DAI-link %d, name: %s\n",
+ __func__,
+ i,
+ ux500_ab3550_drvdata.card->dai_link[i].name);
+ pr_debug("%s: DAI-link %d, stream_name: %s\n",
+ __func__,
+ i,
+ ux500_ab3550_drvdata.card->dai_link[i].stream_name);
+ }
+
+ pr_debug("%s: Allocate platform device (%s)\n",
+ __func__,
+ ux500_ab3550_drvdata.card->name);
+ ux500_ab3550_platform_device = platform_device_alloc("soc-audio", -1);
+ if (!ux500_ab3550_platform_device)
+ return -ENOMEM;
+
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: Set platform drvdata (%s)\n",
+ __func__,
+ ux500_ab3550_drvdata.card->name);
+ platform_set_drvdata(
+ ux500_ab3550_platform_device,
+ &ux500_ab3550_drvdata);
+
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: Add platform device (%s)\n",
+ __func__,
+ ux500_ab3550_drvdata.card->name);
+ ux500_ab3550_drvdata.dev = &ux500_ab3550_platform_device->dev;
+
+ ret = platform_device_add(ux500_ab3550_platform_device);
+ if (ret) {
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: Error: Failed to add platform device (%s)\n",
+ __func__,
+ ux500_ab3550_drvdata.card->name);
+ platform_device_put(ux500_ab3550_platform_device);
+ }
+
+ return ret;
+}
+module_init(mop500_ab3550_soc_init);
+
+static void __exit mop500_ab3550_soc_exit(void)
+{
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: Enter.\n",
+ __func__);
+
+ dev_dbg(&ux500_ab3550_platform_device->dev,
+ "%s: Un-register platform device (%s)\n",
+ __func__,
+ ux500_ab3550_drvdata.card->name);
+ platform_device_unregister(ux500_ab3550_platform_device);
+}
+module_exit(mop500_ab3550_soc_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/ux500_cg29xx.c b/sound/soc/ux500/ux500_cg29xx.c
new file mode 100755
index 00000000000..f1eaa08ab6b
--- /dev/null
+++ b/sound/soc/ux500/ux500_cg29xx.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Roger Nilsson roger.xr.nilsson@stericsson.com
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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/io.h>
+#include <sound/soc.h>
+
+#include "ux500_pcm.h"
+#include "ux500_msp_dai.h"
+#include "mach/hardware.h"
+#include "../codecs/cg29xx.h"
+
+static struct platform_device *ux500_cg29xx_platform_device;
+
+static int ux500_cg29xx_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 *cpu_dai = rtd->dai->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s: Enter.\n", __func__);
+ pr_debug("%s: substream->pcm->name = %s.\n"
+ "substream->pcm->id = %s.\n"
+ "substream->name = %s.\n"
+ "substream->number = %d.\n",
+ __func__,
+ substream->pcm->name,
+ substream->pcm->id,
+ substream->name,
+ substream->number);
+
+ if (cpu_dai->ops.set_fmt) {
+ ret = snd_soc_dai_set_fmt(
+ cpu_dai,
+ SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0) {
+ pr_debug("%s: snd_soc_dai_set_fmt"
+ " failed with %d.\n",
+ __func__,
+ ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static struct snd_soc_ops ux500_cg29xx_ops = {
+ .hw_params = ux500_cg29xx_hw_params,
+};
+
+static int ux500_hdmi_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 *cpu_dai = rtd->dai->cpu_dai;
+ int ret = 0;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ pr_debug("%s: substream->pcm->name = %s.\n"
+ "substream->pcm->id = %s.\n"
+ "substream->name = %s.\n"
+ "substream->number = %d.\n",
+ __func__,
+ substream->pcm->name,
+ substream->pcm->id,
+ substream->name,
+ substream->number);
+
+ if (cpu_dai->ops.set_fmt) {
+ ret = snd_soc_dai_set_fmt(
+ cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_CBM_CFM);
+ if (ret < 0) {
+ pr_debug("%s: snd_soc_dai_set_fmt"
+ " failed with %d.\n",
+ __func__,
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops ux500_hdmi_ops = {
+ .hw_params = ux500_hdmi_hw_params,
+};
+
+struct snd_soc_dai_link ux500_cg29xx_dai_links[] = {
+ {
+ .name = "cg29xx_0",
+ .stream_name = "cg29xx_0",
+ .cpu_dai = &ux500_msp_dai[0],
+ .codec_dai = &cg29xx_codec_dai[0],
+ .init = NULL,
+ .ops = &ux500_cg29xx_ops,
+ },
+ {
+ .name = "hdmi_0",
+ .stream_name = "hdmi_0",
+ .cpu_dai = &ux500_msp_dai[2],
+ .codec_dai = &cg29xx_codec_dai[0],
+ .init = NULL,
+ .ops = &ux500_hdmi_ops,
+ },
+};
+
+static struct snd_soc_card ux500_cg29xx = {
+ .name = "cg29xx",
+ .probe = NULL,
+ .dai_link = ux500_cg29xx_dai_links,
+ .num_links = ARRAY_SIZE(ux500_cg29xx_dai_links),
+ .platform = &ux500_soc_platform,
+};
+
+struct snd_soc_device ux500_cg29xx_drvdata = {
+ .card = &ux500_cg29xx,
+ .codec_dev = &soc_codec_dev_cg29xx,
+};
+
+static int __init ux500_cg29xx_soc_init(void)
+{
+ int i;
+ int ret = 0;
+
+ pr_debug("%s: Enter.\n", __func__);
+ pr_debug("%s: Card name: %s\n",
+ __func__,
+ ux500_cg29xx_drvdata.card->name);
+
+ for (i = 0; i < ARRAY_SIZE(ux500_cg29xx_dai_links); i++) {
+ pr_debug("%s: DAI-link %d, name: %s\n",
+ __func__,
+ i,
+ ux500_cg29xx_drvdata.card->dai_link[i].name);
+ pr_debug("%s: DAI-link %d, stream_name: %s\n",
+ __func__,
+ i,
+ ux500_cg29xx_drvdata.card->dai_link[i].stream_name);
+ }
+
+ pr_debug("%s: Allocate platform device (%s).\n",
+ __func__,
+ ux500_cg29xx_drvdata.card->name);
+ ux500_cg29xx_platform_device =
+ platform_device_alloc("soc-audio", -1);
+ if (!ux500_cg29xx_platform_device)
+ return -ENOMEM;
+ pr_debug("%s: Set platform drvdata (%s).\n",
+ __func__,
+ ux500_cg29xx_drvdata.card->name);
+ platform_set_drvdata(
+ ux500_cg29xx_platform_device,
+ &ux500_cg29xx_drvdata);
+
+ pr_debug("%s: Add platform device (%s).\n",
+ __func__,
+ ux500_cg29xx_drvdata.card->name);
+ ux500_cg29xx_drvdata.dev = &ux500_cg29xx_platform_device->dev;
+
+ ret = platform_device_add(ux500_cg29xx_platform_device);
+ if (ret) {
+ pr_debug("%s: Error: Failed to add platform device (%s).\n",
+ __func__,
+ ux500_cg29xx_drvdata.card->name);
+ platform_device_put(ux500_cg29xx_platform_device);
+ }
+
+ return ret;
+}
+module_init(ux500_cg29xx_soc_init);
+
+static void __exit ux500_cg29xx_soc_exit(void)
+{
+ pr_debug("%s: Enter.\n", __func__);
+
+ pr_debug("%s: Un-register platform device (%s).\n",
+ __func__,
+ ux500_cg29xx_drvdata.card->name);
+ platform_device_unregister(ux500_cg29xx_platform_device);
+}
+module_exit(ux500_cg29xx_soc_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
new file mode 100755
index 00000000000..db9495d7ebf
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ * Roger Nilsson roger.xr.nilsson@stericsson.com
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <asm/dma.h>
+#include "ux500_msp_dai.h"
+#include "ux500_pcm.h"
+
+#include <mach/msp.h>
+#include <linux/i2s/i2s.h>
+
+static struct ux500_msp_dai_private_s
+ ux500_msp_dai_private[UX500_NBR_OF_DAI] =
+{
+ {
+ .lock = __SPIN_LOCK_UNLOCKED(ux500_msp_dai_private[0].lock),
+ .i2s = NULL,
+ .tx_active = 0,
+ .rx_active = 0,
+ .fmt = 0,
+ .rate = 0,
+ },
+ {
+ .lock = __SPIN_LOCK_UNLOCKED(ux500_msp_dai_private[1].lock),
+ .i2s = NULL,
+ .tx_active = 0,
+ .rx_active = 0,
+ .fmt = 0,
+ .rate = 0,
+ },
+ {
+ .lock = __SPIN_LOCK_UNLOCKED(ux500_msp_dai_private[2].lock),
+ .i2s = NULL,
+ .tx_active = 0,
+ .rx_active = 0,
+ .fmt = 0,
+ .rate = 0,
+ },
+};
+
+static int ux500_msp_dai_i2s_probe(struct i2s_device *i2s)
+{
+ unsigned long flags;
+
+ pr_debug("%s: Enter (chip_select = %d, i2s = %d).\n",
+ __func__,
+ (int)i2s->chip_select, (int)(i2s));
+
+ spin_lock_irqsave(
+ &ux500_msp_dai_private[i2s->chip_select].lock,
+ flags);
+ ux500_msp_dai_private[i2s->chip_select].i2s = i2s;
+ spin_unlock_irqrestore(
+ &ux500_msp_dai_private[i2s->chip_select].lock,
+ flags);
+ try_module_get(i2s->controller->dev.parent->driver->owner);
+ i2s_set_drvdata(
+ i2s,
+ (void *)&ux500_msp_dai_private[i2s->chip_select]);
+
+ return 0;
+}
+
+static int ux500_msp_dai_i2s_remove(struct i2s_device *i2s)
+{
+ unsigned long flags;
+ struct ux500_msp_dai_private_s *ux500_msp_dai_private =
+ i2s_get_drvdata(i2s);
+
+ pr_debug("%s: Enter (chip_select = %d).\n",
+ __func__,
+ (int)i2s->chip_select);
+
+ spin_lock_irqsave(&ux500_msp_dai_private->lock, flags);
+
+ ux500_msp_dai_private->i2s = NULL;
+ i2s_set_drvdata(i2s, NULL);
+ spin_unlock_irqrestore(
+ &ux500_msp_dai_private->lock,
+ flags);
+
+ pr_debug("%s: module_put\n",
+ __func__);
+ module_put(i2s->controller->dev.parent->driver->owner);
+
+ return 0;
+}
+
+static const struct i2s_device_id dev_id_table[] = {
+ { "i2s_device.0", 0, 0 },
+ { "i2s_device.1", 0, 0 },
+ { "i2s_device.2", 0, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2s, dev_id_table);
+
+static struct i2s_driver i2sdrv_i2s = {
+ .driver = {
+ .name = "ux500_asoc_i2s",
+ .owner = THIS_MODULE,
+ },
+ .probe = ux500_msp_dai_i2s_probe,
+ .remove = __devexit_p(ux500_msp_dai_i2s_remove),
+ .id_table = dev_id_table,
+};
+
+int ux500_msp_dai_i2s_send_data(
+ void *data,
+ size_t bytes,
+ int dai_idx)
+{
+ unsigned long flags;
+ struct ux500_msp_dai_private_s *dai_private =
+ &ux500_msp_dai_private[dai_idx];
+ struct i2s_message message;
+ struct i2s_device *i2s_dev;
+ int ret = 0;
+
+ pr_debug("%s: Enter MSP Index:%d bytes = %d).\n",
+ __func__,
+ dai_idx,
+ (int)bytes);
+ spin_lock_irqsave(&dai_private->lock, flags);
+
+ i2s_dev = dai_private->i2s;
+
+ if (!dai_private->tx_active) {
+ pr_err("%s: The I2S controller is not available."
+ "MSP index:%d\n",
+ __func__,
+ dai_idx);
+ goto cleanup;
+ }
+
+ message.txbytes = bytes;
+ message.txdata = data;
+ message.rxbytes = 0;
+ message.rxdata = NULL;
+ message.dma_flag = 1;
+ ret = i2s_transfer(i2s_dev->controller, &message);
+ if (ret < 0) {
+ pr_err("%s: Error: i2s_transfer failed. MSP index: %d\n",
+ __func__,
+ dai_idx);
+ goto cleanup;
+ }
+
+cleanup:
+ spin_unlock_irqrestore(&dai_private->lock, flags);
+ return ret;
+}
+
+int ux500_msp_dai_i2s_receive_data(
+ void *data,
+ size_t bytes,
+ int dai_idx)
+{
+ unsigned long flags;
+ struct ux500_msp_dai_private_s *dai_private =
+ &ux500_msp_dai_private[dai_idx];
+ struct i2s_message message;
+ struct i2s_device *i2s_dev;
+ int ret = 0;
+
+ pr_debug("%s: Enter MSP Index: %d, bytes = %d).\n",
+ __func__,
+ dai_idx,
+ (int)bytes);
+
+ spin_lock_irqsave(&dai_private->lock, flags);
+
+ i2s_dev = dai_private->i2s;
+
+ if (!dai_private->rx_active) {
+ pr_err("%s: The I2S controller is not available."
+ "MSP index: %d\n",
+ __func__,
+ dai_idx);
+ goto cleanup;
+ }
+
+ message.rxbytes = bytes;
+ message.rxdata = data;
+ message.txbytes = 0;
+ message.txdata = NULL;
+ message.dma_flag = 1;
+ ret = i2s_transfer(i2s_dev->controller, &message);
+ if (ret < 0) {
+ pr_err("%s: Error: i2s_transfer failed. Msp index: %d\n",
+ __func__,
+ dai_idx);
+ goto cleanup;
+ }
+
+cleanup:
+ spin_unlock_irqrestore(&dai_private->lock, flags);
+ return ret;
+}
+
+static void ux500_msp_dai_shutdown(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *msp_dai)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct ux500_msp_dai_private_s *dai_private =
+ msp_dai->private_data;
+
+ pr_debug("%s: Enter (stream = %s).\n",
+ __func__,
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "SNDRV_PCM_STREAM_PLAYBACK" :
+ "SNDRV_PCM_STREAM_CAPTURE");
+
+ if (dai_private == NULL)
+ return;
+
+ pr_info("%s: MSP Index: %d\n",
+ __func__,
+ (int)dai_private->i2s->chip_select);
+
+ spin_lock_irqsave(&dai_private->lock, flags);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dai_private->tx_active = 0;
+ else
+ dai_private->rx_active = 0;
+
+ if (0 == dai_private->tx_active &&
+ 0 == dai_private->rx_active) {
+
+ msp_dai->private_data = NULL;
+ ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_ALL);
+
+ if (ret) {
+ pr_err("%s: Failed to close the i2s controller."
+ "MSP index:%d\n",
+ __func__,
+ (int)dai_private->i2s->chip_select);
+ }
+ } else {
+
+ if (dai_private->tx_active)
+ ret = i2s_cleanup(
+ dai_private->i2s->controller,
+ DISABLE_RECEIVE);
+ else
+ ret = i2s_cleanup(
+ dai_private->i2s->controller,
+ DISABLE_TRANSMIT);
+
+ if (ret) {
+ pr_err("%s: Failed to close the i2s controller."
+ "MSP index:%d\n",
+ __func__,
+ (int)dai_private->i2s->chip_select);
+ }
+ }
+
+ spin_unlock_irqrestore(&dai_private->lock, flags);
+}
+
+static int ux500_msp_dai_startup(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *msp_dai)
+{
+ struct ux500_msp_dai_private_s *dai_private =
+ &ux500_msp_dai_private[msp_dai->id];
+
+ pr_info("%s: MSP Index: %d).\n",
+ __func__,
+ msp_dai->id);
+
+ msp_dai->private_data = dai_private;
+
+ if (dai_private->i2s == NULL) {
+ pr_err("%s: Error: MSP index: %d"
+ "i2sdrv.i2s == NULL\n",
+ __func__,
+ msp_dai->id);
+ return -1;
+ }
+
+ if (dai_private->i2s->controller == NULL) {
+ pr_err("%s: Error: MSP index: %d"
+ "i2sdrv.i2s->controller == NULL.\n",
+ __func__,
+ msp_dai->id);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ux500_msp_dai_compile_msp_config(
+ struct snd_pcm_substream *substream,
+ unsigned int fmt,
+ unsigned int rate,
+ struct msp_generic_config *msp_config)
+{
+ struct msp_protocol_desc *prot_desc;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ msp_config->input_clock_freq = UX500_MSP_INTERNAL_CLOCK_FREQ;
+ msp_config->rx_frame_sync_pol = RX_FIFO_SYNC_HI;
+ msp_config->tx_frame_sync_pol = TX_FIFO_SYNC_HI;
+ msp_config->rx_fifo_config = RX_FIFO_ENABLE;
+ msp_config->tx_fifo_config = TX_FIFO_ENABLE;
+ msp_config->spi_clk_mode = SPI_CLK_MODE_NORMAL;
+ msp_config->spi_burst_mode = 0;
+ msp_config->handler = ux500_pcm_dma_eot_handler;
+ msp_config->tx_callback_data = substream;
+ msp_config->tx_data_enable = 0;
+ msp_config->rx_callback_data = substream;
+ msp_config->loopback_enable = 0;
+ msp_config->def_elem_len = 1;
+ msp_config->direction = MSP_BOTH_T_R_MODE;
+ msp_config->frame_size = 0;
+ msp_config->data_size = MSP_DATA_SIZE_32BIT;
+ msp_config->work_mode = MSP_DMA_MODE;
+ msp_config->multichannel_configured = 0;
+ msp_config->multichannel_config.tx_multichannel_enable = 0;
+ msp_config->multichannel_config.rx_multichannel_enable = 0;
+
+ switch (fmt &
+ (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+ pr_info("%s: SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS\n",
+ __func__);
+ msp_config->default_protocol_desc = 1;
+ msp_config->protocol = MSP_I2S_PROTOCOL;
+ msp_config->tx_clock_sel = TX_CLK_SEL_SRG;
+ msp_config->tx_frame_sync_sel = TX_SYNC_SRG_PROG;
+ msp_config->rx_clock_sel = RX_CLK_SEL_SRG;
+ msp_config->rx_frame_sync_sel = RX_SYNC_SRG;
+ msp_config->srg_clock_sel = 1 << SCKSEL_BIT;
+ msp_config->frame_freq = rate;
+ break;
+
+ default:
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+ pr_info("%s: SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM\n",
+ __func__);
+ msp_config->data_size = MSP_DATA_SIZE_16BIT;
+ msp_config->default_protocol_desc = 0;
+ msp_config->protocol = MSP_I2S_PROTOCOL;
+
+ prot_desc = &msp_config->protocol_desc;
+
+ prot_desc->rx_phase_mode = MSP_DUAL_PHASE;
+ prot_desc->tx_phase_mode = MSP_DUAL_PHASE;
+ prot_desc->rx_phase2_start_mode =
+ MSP_PHASE2_START_MODE_FRAME_SYNC;
+ prot_desc->tx_phase2_start_mode =
+ MSP_PHASE2_START_MODE_FRAME_SYNC;
+ prot_desc->rx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
+ prot_desc->tx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
+ prot_desc->rx_frame_length_1 = MSP_FRAME_LENGTH_1;
+ prot_desc->rx_frame_length_2 = MSP_FRAME_LENGTH_1;
+ prot_desc->tx_frame_length_1 = MSP_FRAME_LENGTH_1;
+ prot_desc->tx_frame_length_2 = MSP_FRAME_LENGTH_1;
+ prot_desc->rx_element_length_1 = MSP_ELEM_LENGTH_16;
+ prot_desc->rx_element_length_2 = MSP_ELEM_LENGTH_16;
+ prot_desc->tx_element_length_1 = MSP_ELEM_LENGTH_16;
+ prot_desc->tx_element_length_2 = MSP_ELEM_LENGTH_16;
+ prot_desc->rx_data_delay = MSP_DELAY_0;
+ prot_desc->tx_data_delay = MSP_DELAY_0;
+ prot_desc->rx_clock_pol = MSP_RISING_EDGE;
+ prot_desc->tx_clock_pol = MSP_RISING_EDGE;
+ prot_desc->rx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
+ prot_desc->tx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
+ prot_desc->rx_half_word_swap =
+ MSP_HWS_BYTE_SWAP_IN_EACH_HALF_WORD;
+ prot_desc->tx_half_word_swap = MSP_HWS_NO_SWAP;
+ prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+ prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+ prot_desc->spi_clk_mode = MSP_SPI_CLOCK_MODE_NON_SPI;
+ prot_desc->spi_burst_mode = MSP_SPI_BURST_MODE_DISABLE;
+ prot_desc->frame_sync_ignore = MSP_FRAME_SYNC_IGNORE;
+
+ msp_config->tx_clock_sel = 0;
+ msp_config->tx_frame_sync_sel = 0;
+ msp_config->rx_clock_sel = 0;
+ msp_config->rx_frame_sync_sel = 0;
+ msp_config->srg_clock_sel = 0;
+ msp_config->frame_freq = rate;
+ break;
+ case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+ prot_desc = &msp_config->protocol_desc;
+ pr_info("%s: SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS\n",
+ __func__);
+ pr_info("%s: rate: %u channels: %d\n",
+ __func__,
+ rate,
+ runtime->channels);
+ msp_config->data_size = MSP_DATA_SIZE_16BIT;
+ msp_config->default_protocol_desc = 0;
+ msp_config->protocol = MSP_PCM_PROTOCOL;
+
+ prot_desc->rx_phase_mode = MSP_SINGLE_PHASE;
+ prot_desc->tx_phase_mode = MSP_SINGLE_PHASE;
+ prot_desc->rx_phase2_start_mode =
+ MSP_PHASE2_START_MODE_IMEDIATE;
+ prot_desc->tx_phase2_start_mode =
+ MSP_PHASE2_START_MODE_IMEDIATE;
+ prot_desc->rx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
+ prot_desc->tx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
+ prot_desc->rx_frame_length_1 = MSP_FRAME_LENGTH_1;
+ prot_desc->rx_frame_length_2 = MSP_FRAME_LENGTH_1;
+ prot_desc->tx_frame_length_1 = MSP_FRAME_LENGTH_1;
+ prot_desc->tx_frame_length_2 = MSP_FRAME_LENGTH_1;
+ prot_desc->rx_element_length_1 = MSP_ELEM_LENGTH_16;
+ prot_desc->rx_element_length_2 = MSP_ELEM_LENGTH_16;
+ prot_desc->tx_element_length_1 = MSP_ELEM_LENGTH_16;
+ prot_desc->tx_element_length_2 = MSP_ELEM_LENGTH_16;
+ prot_desc->rx_data_delay = MSP_DELAY_0;
+ prot_desc->tx_data_delay = MSP_DELAY_0;
+ prot_desc->rx_clock_pol = MSP_FALLING_EDGE;
+ prot_desc->tx_clock_pol = MSP_FALLING_EDGE;
+ prot_desc->rx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
+ prot_desc->tx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
+ prot_desc->rx_half_word_swap = MSP_HWS_NO_SWAP;
+ prot_desc->tx_half_word_swap = MSP_HWS_NO_SWAP;
+ prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+ prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+ prot_desc->spi_clk_mode = MSP_SPI_CLOCK_MODE_NON_SPI;
+ prot_desc->spi_burst_mode = MSP_SPI_BURST_MODE_DISABLE;
+ prot_desc->frame_sync_ignore = MSP_FRAME_SYNC_IGNORE;
+
+ prot_desc->frame_width = 0;
+ switch (rate) {
+ case 8000:
+ prot_desc->frame_period = 249;
+ break;
+ case 16000:
+ prot_desc->frame_period = 124;
+ break;
+ case 44100:
+ prot_desc->frame_period = 63;
+ break;
+ case 48000:
+ default:
+ prot_desc->frame_period = 49;
+ break;
+ }
+ prot_desc->total_clocks_for_one_frame =
+ prot_desc->frame_period+1;
+
+ msp_config->tx_clock_sel = TX_CLK_SEL_SRG;
+ msp_config->tx_frame_sync_sel = TX_SYNC_SRG_PROG;
+ msp_config->rx_clock_sel = RX_CLK_SEL_SRG;
+ msp_config->rx_frame_sync_sel = RX_SYNC_SRG;
+ msp_config->srg_clock_sel = 1 << SCKSEL_BIT;
+ msp_config->frame_freq = rate;
+ break;
+ }
+}
+
+static int ux500_msp_dai_prepare(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *msp_dai)
+{
+ int ret = 0;
+ unsigned long flags_private;
+ struct ux500_msp_dai_private_s *dai_private = msp_dai->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct msp_generic_config msp_config;
+
+ pr_info("%s: Enter (stream = %s, chip_select = %d).\n",
+ __func__,
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "SNDRV_PCM_STREAM_PLAYBACK" :
+ "SNDRV_PCM_STREAM_CAPTURE",
+ (int)dai_private->i2s->chip_select);
+
+ spin_lock_irqsave(&dai_private->lock, flags_private);
+
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ dai_private->rx_active) ||
+ (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ dai_private->tx_active)) {
+
+ if (dai_private->rate != runtime->rate) {
+ pr_err(
+ "%s: Rate mismatch. index:%d"
+ "rate:%d requested rate:%d\n",
+ __func__,
+ msp_dai->id,
+ dai_private->rate,
+ runtime->rate);
+ ret = -EBUSY;
+ goto cleanup;
+ }
+ } else if (!dai_private->rx_active && !dai_private->tx_active) {
+ ret = 0;
+ ux500_msp_dai_compile_msp_config(substream,
+ dai_private->fmt,
+ runtime->rate,
+ &msp_config);
+
+ ret = i2s_setup(dai_private->i2s->controller, &msp_config);
+ if (ret < 0) {
+ pr_err("%s: i2s_setup failed."
+ " MSP index: %d return: %d\n",
+ __func__,
+ msp_dai->id,
+ ret);
+ goto cleanup;
+ }
+ dai_private->rate = runtime->rate;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dai_private->tx_active = 1;
+ else
+ dai_private->rx_active = 1;
+
+cleanup:
+ spin_unlock_irqrestore(&dai_private->lock, flags_private);
+ return ret;
+}
+
+static int ux500_msp_dai_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *msp_dai)
+{
+ struct ux500_msp_dai_private_s *dai_private = msp_dai->private_data;
+
+ pr_debug("%s: Enter stream: %s, MSP index: %d\n",
+ __func__,
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "SNDRV_PCM_STREAM_PLAYBACK" :
+ "SNDRV_PCM_STREAM_CAPTURE",
+ (int)dai_private->i2s->chip_select);
+
+ switch (dai_private->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ if (params_channels(params) != 2) {
+ pr_debug("%s: MSP index: %d:"
+ "I2S format requires that channels: %d"
+ " is set to two.\n",
+ __func__,
+ msp_dai->id,
+ params_channels(params));
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if (params_channels(params) != 1) {
+ pr_debug("%s: MSP index: %d:"
+ " PCM format requires that channels: %d"
+ " is set to one.\n",
+ __func__,
+ msp_dai->id,
+ params_channels(params));
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ux500_msp_dai_set_dai_fmt(
+ struct snd_soc_dai *msp_dai,
+ unsigned int fmt)
+{
+ struct ux500_msp_dai_private_s *dai_private =
+ msp_dai->private_data;
+
+ pr_debug("%s: MSP index: %d: Enter\n",
+ __func__,
+ (int)dai_private->i2s->chip_select);
+
+ switch (fmt &
+ (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+ break;
+
+ default:
+ pr_notice("Msp_dai: unsupported DAI format 0x%x\n",
+ fmt);
+ return -EINVAL;
+ }
+
+ dai_private->fmt = fmt;
+ return 0;
+}
+
+static int ux500_msp_dai_trigger(
+ struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *msp_dai)
+{
+ unsigned long flags;
+ int ret = 0;
+ struct ux500_msp_dai_private_s *dai_private =
+ msp_dai->private_data;
+
+ pr_debug("%s: Enter (stream = %s,"
+ " chip_select = %d).\n",
+ __func__,
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "SNDRV_PCM_STREAM_PLAYBACK" :
+ "SNDRV_PCM_STREAM_CAPTURE",
+ (int)dai_private->i2s->chip_select);
+
+ spin_lock_irqsave(&dai_private->lock, flags);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ spin_unlock_irqrestore(&dai_private->lock, flags);
+ return ret;
+}
+
+struct snd_soc_dai ux500_msp_dai[UX500_NBR_OF_DAI] =
+{
+ {
+ .name = "ux500_i2s-0",
+ .id = 0,
+ .suspend = NULL,
+ .resume = NULL,
+ .playback = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .ops = {
+ .set_sysclk = NULL,
+ .set_fmt = ux500_msp_dai_set_dai_fmt,
+ .startup = ux500_msp_dai_startup,
+ .shutdown = ux500_msp_dai_shutdown,
+ .prepare = ux500_msp_dai_prepare,
+ .trigger = ux500_msp_dai_trigger,
+ .hw_params = ux500_msp_dai_hw_params,
+ },
+ .private_data = &ux500_msp_dai_private[0],
+ },
+ {
+ .name = "ux500_i2s-1",
+ .id = 1,
+ .suspend = NULL,
+ .resume = NULL,
+ .playback = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .ops = {
+ .set_sysclk = NULL,
+ .set_fmt = ux500_msp_dai_set_dai_fmt,
+ .startup = ux500_msp_dai_startup,
+ .shutdown = ux500_msp_dai_shutdown,
+ .prepare = ux500_msp_dai_prepare,
+ .trigger = ux500_msp_dai_trigger,
+ .hw_params = ux500_msp_dai_hw_params,
+ },
+ .private_data = &ux500_msp_dai_private[1],
+ },
+ {
+ .name = "ux500_i2s-2",
+ .id = 2,
+ .suspend = NULL,
+ .resume = NULL,
+ .playback = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .capture = {
+ .channels_min = UX500_MSP_MIN_CHANNELS,
+ .channels_max = UX500_MSP_MAX_CHANNELS,
+ .rates = UX500_I2S_RATES,
+ .formats = UX500_I2S_FORMATS,
+ },
+ .ops = {
+ .set_sysclk = NULL,
+ .set_fmt = ux500_msp_dai_set_dai_fmt,
+ .startup = ux500_msp_dai_startup,
+ .shutdown = ux500_msp_dai_shutdown,
+ .prepare = ux500_msp_dai_prepare,
+ .trigger = ux500_msp_dai_trigger,
+ .hw_params = ux500_msp_dai_hw_params,
+ },
+ .private_data = &ux500_msp_dai_private[2],
+ },
+};
+EXPORT_SYMBOL(ux500_msp_dai);
+
+static int __init ux500_msp_dai_init(void)
+{
+ int i;
+ int ret = 0;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ ret = i2s_register_driver(&i2sdrv_i2s);
+ if (ret < 0) {
+ pr_err("%s: Unable to register I2S-driver\n",
+ __func__);
+ return ret;
+ }
+
+ for (i = 0; i < UX500_NBR_OF_DAI; i++) {
+ pr_debug("%s: Register MSP dai %d.\n",
+ __func__,
+ i);
+ ret = snd_soc_register_dai(&ux500_msp_dai[i]);
+ if (ret < 0) {
+ pr_err("MOP500_AB3550:"
+ " Error: Failed to register MSP dai %d.\n",
+ i);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+module_init(ux500_msp_dai_init);
+
+static void __exit ux500_msp_dai_exit(void)
+{
+ int i;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ i2s_unregister_driver(&i2sdrv_i2s);
+
+ for (i = 0; i < UX500_NBR_OF_DAI; i++) {
+ pr_debug("%s: Un-register MSP dai %d.\n",
+ __func__,
+ i);
+ snd_soc_unregister_dai(&ux500_msp_dai[i]);
+ }
+}
+module_exit(ux500_msp_dai_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h
new file mode 100755
index 00000000000..082a4bb36cf
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_dai.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ * Roger Nilsson roger.xr.nilsson@stericsson.com
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 UX500_msp_dai_H
+#define UX500_msp_dai_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+#define UX500_NBR_OF_DAI 3
+
+#define UX500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000;
+#define UX500_MSP_MIN_CHANNELS 1
+#define UX500_MSP_MAX_CHANNELS 2
+
+struct ux500_msp_dai_private_s {
+ spinlock_t lock;
+ struct i2s_device *i2s;
+ u8 rx_active;
+ u8 tx_active;
+ unsigned int fmt;
+ unsigned int rate;
+};
+
+extern struct snd_soc_dai ux500_msp_dai[UX500_NBR_OF_DAI];
+
+int ux500_msp_dai_i2s_send_data(void *data, size_t bytes, int dai_idx);
+int ux500_msp_dai_i2s_receive_data(void *data, size_t bytes, int dai_idx);
+
+#endif
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
new file mode 100755
index 00000000000..880ca87e0c6
--- /dev/null
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ * Roger Nilsson roger.xr.nilsson@stericsson.com
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 <asm/page.h>
+
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "ux500_pcm.h"
+#include "ux500_msp_dai.h"
+
+static struct snd_pcm_hardware ux500_pcm_hw_playback = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |
+ SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_BE,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .rate_min = UX500_PLATFORM_MIN_RATE_PLAYBACK,
+ .rate_max = UX500_PLATFORM_MAX_RATE_PLAYBACK,
+ .channels_min = UX500_PLATFORM_MIN_CHANNELS,
+ .channels_max = UX500_PLATFORM_MAX_CHANNELS,
+ .buffer_bytes_max = UX500_PLATFORM_BUFFER_SIZE,
+ .period_bytes_min = UX500_PLATFORM_MIN_PERIOD_BYTES,
+ .period_bytes_max = PAGE_SIZE,
+ .periods_min = UX500_PLATFORM_BUFFER_SIZE / PAGE_SIZE,
+ .periods_max = UX500_PLATFORM_BUFFER_SIZE /
+ UX500_PLATFORM_MIN_PERIOD_BYTES
+};
+
+static struct snd_pcm_hardware ux500_pcm_hw_capture = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |
+ SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_BE,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .rate_min = UX500_PLATFORM_MIN_RATE_CAPTURE,
+ .rate_max = UX500_PLATFORM_MAX_RATE_CAPTURE,
+ .channels_min = UX500_PLATFORM_MIN_CHANNELS,
+ .channels_max = UX500_PLATFORM_MAX_CHANNELS,
+ .buffer_bytes_max = UX500_PLATFORM_BUFFER_SIZE,
+ .period_bytes_min = UX500_PLATFORM_MIN_PERIOD_BYTES,
+ .period_bytes_max = PAGE_SIZE,
+ .periods_min = UX500_PLATFORM_BUFFER_SIZE / PAGE_SIZE,
+ .periods_max = UX500_PLATFORM_BUFFER_SIZE /
+ UX500_PLATFORM_MIN_PERIOD_BYTES
+};
+
+static void ux500_pcm_dma_start(struct snd_pcm_substream *substream)
+{
+ unsigned int offset, dma_size;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct ux500_pcm_private_s *private = substream->runtime->private_data;
+
+ pr_debug("%s: Enter MSP Index: %d\n",
+ __func__,
+ private->msp_id);
+
+ dma_size = frames_to_bytes(runtime, runtime->period_size);
+ offset = dma_size * private->period;
+ private->old_offset = offset;
+ pr_debug("%s: address = %x size = %d\n",
+ __func__,
+ (runtime->dma_addr + offset), dma_size);
+
+ if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ux500_msp_dai_i2s_send_data(
+ (void *)(runtime->dma_addr + offset),
+ dma_size,
+ private->msp_id);
+ } else{
+ ux500_msp_dai_i2s_receive_data(
+ (void *)(runtime->dma_addr + offset),
+ dma_size,
+ private->msp_id);
+ }
+
+ private->period++;
+ private->period %= runtime->periods;
+ private->periods++;
+}
+
+static void ux500_pcm_dma_hw_free(struct device *dev,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+
+ if (runtime->dma_area == NULL)
+ return;
+
+ if (buf != &substream->dma_buffer) {
+ dma_free_coherent(
+ buf->dev.dev,
+ buf->bytes,
+ buf->area,
+ buf->addr);
+ kfree(runtime->dma_buffer_p);
+ }
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+}
+
+irqreturn_t ux500_pcm_dma_eot_handler(void *data, int irq)
+{
+ struct snd_pcm_substream *substream = data;
+ struct ux500_pcm_private_s *private =
+ substream->runtime->private_data;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ if (substream)
+ snd_pcm_period_elapsed(substream);
+ if (private->state == ALSA_STATE_PAUSED)
+ return IRQ_HANDLED;
+ if (private->active == 1)
+ ux500_pcm_dma_start(substream);
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(ux500_pcm_dma_eot_handler);
+
+static int ux500_pcm_open(struct snd_pcm_substream *substream)
+{
+ int stream_id = substream->pstr->stream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct ux500_pcm_private_s *private;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ private = kzalloc(sizeof(struct ux500_pcm_private_s), GFP_KERNEL);
+ if (private == NULL)
+ return -ENOMEM;
+
+ init_MUTEX(&(private->alsa_sem));
+ init_completion(&(private->alsa_com));
+ private->state = ALSA_STATE_RUNNING;
+ private->active = 0;
+ private->msp_id = rtd->dai->cpu_dai->id;
+ runtime->private_data = private;
+
+ pr_debug("%s: Setting HW-config\n", __func__);
+ runtime->hw = (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ?
+ ux500_pcm_hw_playback : ux500_pcm_hw_capture;
+
+ return 0;
+}
+
+static int ux500_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct ux500_pcm_private_s *private = substream->runtime->private_data;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ kfree(private);
+
+ return 0;
+}
+
+static int ux500_pcm_hw_params(
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+ int ret = 0;
+ int size;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ size = params_buffer_bytes(hw_params);
+
+ if (buf) {
+ if (buf->bytes >= size)
+ goto out;
+ ux500_pcm_dma_hw_free(NULL, substream);
+ }
+
+ if (substream->dma_buffer.area != NULL &&
+ substream->dma_buffer.bytes >= size) {
+ buf = &substream->dma_buffer;
+ } else {
+ buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
+ if (!buf)
+ goto nomem;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = NULL;
+ buf->area = dma_alloc_coherent(
+ NULL,
+ size,
+ &buf->addr,
+ GFP_KERNEL);
+ buf->bytes = size;
+ buf->private_data = NULL;
+
+ if (!buf->area)
+ goto free;
+ }
+ snd_pcm_set_runtime_buffer(substream, buf);
+ ret = 1;
+ out:
+ runtime->dma_bytes = size;
+ return ret;
+
+ free:
+ kfree(buf);
+ nomem:
+ return -ENOMEM;
+}
+
+static int ux500_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s: Enter\n", __func__);
+
+ ux500_pcm_dma_hw_free(NULL, substream);
+
+ return 0;
+}
+
+static int ux500_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s: Enter\n", __func__);
+
+ return 0;
+}
+
+static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct ux500_pcm_private_s *private = substream->runtime->private_data;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pr_debug("%s: TRIGGER START\n", __func__);
+ if (private->active == 0) {
+ private->active = 1;
+ ux500_pcm_dma_start(substream);
+ break;
+ }
+ pr_debug("%s: Error: H/w is busy\n", __func__);
+ return -EINVAL;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_PAUSE\n", __func__);
+ if (private->state == ALSA_STATE_RUNNING)
+ private->state = ALSA_STATE_PAUSED;
+
+ break;
+
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_RESUME\n", __func__);
+ if (private->state == ALSA_STATE_PAUSED) {
+ private->state = ALSA_STATE_RUNNING;
+ ux500_pcm_dma_start(substream);
+ }
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
+ if (private->active == 1) {
+ private->active = 0;
+ private->period = 0;
+ }
+ break;
+
+ default:
+ pr_err("%s: Invalid command in pcm trigger\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t ux500_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ unsigned int offset;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct ux500_pcm_private_s *private = substream->runtime->private_data;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ offset = bytes_to_frames(runtime, private->old_offset);
+ if (offset < 0 || private->old_offset < 0)
+ pr_debug("%s: Offset=%i %i\n",
+ __func__,
+ offset,
+ private->old_offset);
+
+ return offset;
+}
+
+static int ux500_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ pr_debug("%s: Enter.\n", __func__);
+
+ return dma_mmap_coherent(
+ NULL,
+ vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops ux500_pcm_ops = {
+ .open = ux500_pcm_open,
+ .close = ux500_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = ux500_pcm_hw_params,
+ .hw_free = ux500_pcm_hw_free,
+ .prepare = ux500_pcm_prepare,
+ .trigger = ux500_pcm_trigger,
+ .pointer = ux500_pcm_pointer,
+ .mmap = ux500_pcm_mmap
+};
+
+int ux500_pcm_new(
+ struct snd_card *card,
+ struct snd_soc_dai *dai,
+ struct snd_pcm *pcm)
+{
+ pr_debug("%s: pcm = %d\n", __func__, (int)pcm);
+
+ pcm->info_flags = 0;
+ strcpy(pcm->name, "UX500_PCM");
+
+ pr_debug("%s: pcm->name = %s\n", __func__, pcm->name);
+
+ return 0;
+}
+
+static void ux500_pcm_free(struct snd_pcm *pcm)
+{
+ pr_debug("%s: Enter\n", __func__);
+}
+
+static int ux500_pcm_suspend(struct snd_soc_dai *dai)
+{
+ pr_debug("%s: Enter\n", __func__);
+
+ return 0;
+}
+
+static int ux500_pcm_resume(struct snd_soc_dai *dai)
+{
+ pr_debug("%s: Enter\n", __func__);
+
+ return 0;
+}
+
+struct snd_soc_platform ux500_soc_platform = {
+ .name = "ux500-audio",
+ .pcm_ops = &ux500_pcm_ops,
+ .pcm_new = ux500_pcm_new,
+ .pcm_free = ux500_pcm_free,
+ .suspend = ux500_pcm_suspend,
+ .resume = ux500_pcm_resume,
+};
+EXPORT_SYMBOL(ux500_soc_platform);
+
+static int __init ux500_pcm_init(void)
+{
+ int ret;
+
+ pr_debug("%s: Register platform\n", __func__);
+ ret = snd_soc_register_platform(&ux500_soc_platform);
+ if (ret < 0)
+ pr_debug("%s: Error: Failed to register platform\n",
+ __func__);
+
+ return 0;
+}
+module_init(ux500_pcm_init);
+
+static void __exit ux500_pcm_exit(void)
+{
+ snd_soc_unregister_platform(&ux500_soc_platform);
+}
+module_exit(ux500_pcm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/ux500_pcm.h b/sound/soc/ux500/ux500_pcm.h
new file mode 100755
index 00000000000..8320df78ae5
--- /dev/null
+++ b/sound/soc/ux500/ux500_pcm.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ * Roger Nilsson roger.xr.nilsson@stericsson.com
+ * for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 UX500_PCM_H
+#define UX500_PCM_H
+
+#include <mach/msp.h>
+
+#define UX500_PLATFORM_BUFFER_SIZE (64*1024)
+
+#define UX500_PLATFORM_MIN_RATE_PLAYBACK 8000
+#define UX500_PLATFORM_MAX_RATE_PLAYBACK 48000
+#define UX500_PLATFORM_MIN_RATE_CAPTURE 8000
+#define UX500_PLATFORM_MAX_RATE_CAPTURE 48000
+#define UX500_PLATFORM_MIN_CHANNELS 1
+#define UX500_PLATFORM_MAX_CHANNELS 2
+#define UX500_PLATFORM_MIN_PERIOD_BYTES 128
+
+enum alsa_state {
+ ALSA_STATE_PAUSED,
+ ALSA_STATE_RUNNING
+};
+
+extern struct snd_soc_platform ux500_soc_platform;
+
+struct ux500_pcm_private_s {
+ int msp_id;
+ int stream_id;
+ int active;
+ int period;
+ int periods;
+ enum alsa_state state;
+ unsigned int old_offset;
+ struct semaphore alsa_sem;
+ struct completion alsa_com;
+};
+
+irqreturn_t ux500_pcm_dma_eot_handler(void *data, int irq);
+
+#endif