diff options
author | Ola Lilja <ola.o.lilja@stericsson.com> | 2010-07-02 09:05:16 +0200 |
---|---|---|
committer | John Rigby <john.rigby@linaro.org> | 2010-09-02 22:45:51 -0600 |
commit | 9a1e6a4808b23832feb458f50f65cbcfc88554a1 (patch) | |
tree | d188c853713fd8fb56d05b26e492d9d55929e46f | |
parent | 3956b4a499ce066d04fc7e21aee244cf4f582e90 (diff) |
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/Kconfig | 2 | ||||
-rw-r--r-- | sound/soc/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 11 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rwxr-xr-x | sound/soc/codecs/ab3550.c | 1652 | ||||
-rwxr-xr-x | sound/soc/codecs/ab3550.h | 178 | ||||
-rwxr-xr-x | sound/soc/codecs/cg29xx.c | 295 | ||||
-rwxr-xr-x | sound/soc/codecs/cg29xx.h | 22 | ||||
-rwxr-xr-x | sound/soc/u8500/Kconfig | 13 | ||||
-rwxr-xr-x | sound/soc/u8500/Makefile | 12 | ||||
-rwxr-xr-x | sound/soc/u8500/mop500_ab3550.c | 172 | ||||
-rwxr-xr-x | sound/soc/u8500/u8500_msp_dai.c | 513 | ||||
-rwxr-xr-x | sound/soc/u8500/u8500_msp_dai.h | 28 | ||||
-rwxr-xr-x | sound/soc/u8500/u8500_pcm.c | 427 | ||||
-rwxr-xr-x | sound/soc/u8500/u8500_pcm.h | 56 | ||||
-rwxr-xr-x | sound/soc/ux500/Kconfig | 26 | ||||
-rwxr-xr-x | sound/soc/ux500/Makefile | 9 | ||||
-rwxr-xr-x | sound/soc/ux500/ux500_ab3550.c | 208 | ||||
-rwxr-xr-x | sound/soc/ux500/ux500_cg29xx.c | 200 | ||||
-rwxr-xr-x | sound/soc/ux500/ux500_msp_dai.c | 789 | ||||
-rwxr-xr-x | sound/soc/ux500/ux500_msp_dai.h | 46 | ||||
-rwxr-xr-x | sound/soc/ux500/ux500_pcm.c | 401 | ||||
-rwxr-xr-x | sound/soc/ux500/ux500_pcm.h | 50 |
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 |