diff options
Diffstat (limited to 'recipes/linux/linux-2.6.31/pcm043/0013-add-a-mc13783-codec-driver.patch')
-rw-r--r-- | recipes/linux/linux-2.6.31/pcm043/0013-add-a-mc13783-codec-driver.patch | 834 |
1 files changed, 834 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.31/pcm043/0013-add-a-mc13783-codec-driver.patch b/recipes/linux/linux-2.6.31/pcm043/0013-add-a-mc13783-codec-driver.patch new file mode 100644 index 0000000000..c89b83d34b --- /dev/null +++ b/recipes/linux/linux-2.6.31/pcm043/0013-add-a-mc13783-codec-driver.patch @@ -0,0 +1,834 @@ +From a0437114fe666815acb9f7573acdcdc36dc45203 Mon Sep 17 00:00:00 2001 +From: Sascha Hauer <s.hauer@pengutronix.de> +Date: Thu, 22 Oct 2009 14:10:14 +0200 +Subject: [PATCH 13/28] add a mc13783 codec driver + +Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> +--- + sound/soc/codecs/Kconfig | 4 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/mc13783.c | 730 ++++++++++++++++++++++++++++++++++++++++++++ + sound/soc/codecs/mc13783.h | 32 ++ + 4 files changed, 768 insertions(+), 0 deletions(-) + create mode 100644 sound/soc/codecs/mc13783.c + create mode 100644 sound/soc/codecs/mc13783.h + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index bbc97fd..34f5c94 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS + select SND_SOC_AK4104 if SPI_MASTER + select SND_SOC_AK4535 if I2C + select SND_SOC_CS4270 if I2C ++ select SND_SOC_MC13783 if SPI + select SND_SOC_PCM3008 + select SND_SOC_SPDIF + select SND_SOC_SSM2602 if I2C +@@ -89,6 +90,9 @@ config SND_SOC_CS4270_VD33_ERRATA + config SND_SOC_L3 + tristate + ++config SND_SOC_MC13783 ++ tristate ++ + config SND_SOC_PCM3008 + tristate + +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 8b75305..0d3dbc7 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -5,6 +5,7 @@ snd-soc-ak4104-objs := ak4104.o + snd-soc-ak4535-objs := ak4535.o + snd-soc-cs4270-objs := cs4270.o + snd-soc-l3-objs := l3.o ++snd-soc-mc13783-objs := mc13783.o + snd-soc-pcm3008-objs := pcm3008.o + snd-soc-spdif-objs := spdif_transciever.o + snd-soc-ssm2602-objs := ssm2602.o +@@ -42,6 +43,7 @@ obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o + obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o + obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o + obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o ++obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o + obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o + obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o + obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o +diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c +new file mode 100644 +index 0000000..5a7b4e9 +--- /dev/null ++++ b/sound/soc/codecs/mc13783.c +@@ -0,0 +1,730 @@ ++/* ++ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de ++ * Copyright 2009 Sascha Hauer, s.hauer@pengutronix.de ++ * ++ * Initial development of this code was funded by ++ * Phytec Messtechnik GmbH, http://www.phytec.de ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++ * MA 02110-1301, USA. ++ */ ++#include <linux/device.h> ++#include <linux/mfd/mc13783-private.h> ++#include <sound/core.h> ++#include <sound/control.h> ++#include <sound/pcm.h> ++#include <sound/soc.h> ++#include <sound/initval.h> ++#include <sound/soc-dapm.h> ++ ++#include "mc13783.h" ++ ++#define PMIC_AUDIO_RX_0 36 ++#define PMIC_AUDIO_RX_1 37 ++#define PMIC_AUDIO_TX 38 ++#define PMIC_SSI_NETWORK 39 ++#define PMIC_AUDIO_CODEC 40 ++#define PMIC_AUDIO_DAC 41 ++ ++#define SSI_NETWORK_CDCTXRXSLOT(x) (((x) & 0x3) << 2) ++#define SSI_NETWORK_CDCTXSECSLOT(x) (((x) & 0x3) << 4) ++#define SSI_NETWORK_CDCRXSECSLOT(x) (((x) & 0x3) << 6) ++#define SSI_NETWORK_CDCRXSECGAIN(x) (((x) & 0x3) << 8) ++#define SSI_NETWORK_CDCSUMGAIN(x) (1 << 10) ++#define SSI_NETWORK_CDCFSDLY(x) (1 << 11) ++#define SSI_NETWORK_STDCSLOT(x) (((x) & 0x3) << 12) ++#define SSI_NETWORK_STDCRXSLOT(x) (((x) & 0x3) << 14) ++#define SSI_NETWORK_STDCRXSECSLOT(x) (((x) & 0x3) << 16) ++#define SSI_NETWORK_STDCRXSECGAIN(x) (((x) & 0x3) << 18) ++#define SSI_NETWORK_STDCSUMGAIN (1 << 20) ++ ++#define STEREO_DAC_STD_SSI_SEL (1 << 0) ++#define STEREO_DAC_STD_CLK_SEL (1 << 1) ++#define STEREO_DAC_STD_CSM (1 << 2) ++#define STEREO_DAC_STD_BCL_INV (1 << 3) ++#define STEREO_DAC_STD_CFS_INV (1 << 4) ++#define STEREO_DAC_STD_CFS(x) (((x) & 0x3) << 5) ++#define STEREO_DAC_STD_CLK(x) (((x) & 0x7) << 7) ++#define STEREO_DAC_STD_CFS_DLY_B (1 << 10) ++#define STEREO_DAC_STD_C_EN (1 << 11) ++#define STEREO_DAC_STD_C_CLK_EN (1 << 12) ++#define STEREO_DAC_STD_C_RESET (1 << 15) ++#define STEREO_DAC_SPDIF (1 << 16) ++#define STEREO_DAC_SR(x) (((x) & 0xf) << 17) ++ ++static struct mc13783 *mc13783; ++ ++struct mc13783_priv { ++ struct snd_soc_codec codec; ++ ++ u32 reg_cache[42]; ++ ++ int mc13783_asp_val; ++ int mc13783_alsp_val; ++}; ++ ++static unsigned int mc13783_read(struct snd_soc_codec *codec, ++ unsigned int reg) ++{ ++ struct mc13783_priv *priv = codec->private_data; ++ ++ return priv->reg_cache[reg]; ++} ++ ++static int mc13783_write(struct snd_soc_codec *codec, ++ unsigned int reg, unsigned int value) ++{ ++ struct mc13783_priv *priv = codec->private_data; ++ ++ priv->reg_cache[reg] = value; ++ ++ return mc13783_reg_write(mc13783, reg, value); ++} ++ ++/* Mapping between sample rates and register value */ ++static unsigned int mc13783_rates[] = { ++ 8000, 11025, 12000, 16000, ++ 22050, 24000, 32000, 44100, ++ 48000, 64000, 96000 ++}; ++ ++static int mc13783_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_device *socdev = rtd->socdev; ++ struct snd_soc_codec *codec = socdev->card->codec; ++ unsigned int rate = params_rate(params); ++ int i; ++ unsigned int reg; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ for (i = 0; i < ARRAY_SIZE(mc13783_rates); i++) { ++ if (rate == mc13783_rates[i]) { ++ snd_soc_update_bits(codec, PMIC_AUDIO_DAC, ++ 0xf << 17, i << 17); ++ return 0; ++ } ++ } ++ } else { ++ if (rate == 8000) ++ reg = 0; ++ else ++ reg = (1 << 10); ++ ++ snd_soc_update_bits(codec, PMIC_AUDIO_CODEC, ++ 0x1 << 10, reg); ++ ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ unsigned int reg; ++ ++ if (dai->id == MC13783_ID_STEREO_DAC) ++ reg = mc13783_read(codec, PMIC_AUDIO_DAC); ++ else ++ reg = mc13783_read(codec, PMIC_AUDIO_CODEC); ++ ++ reg &= ~STEREO_DAC_STD_CFS(3); ++ reg &= ~STEREO_DAC_STD_BCL_INV; ++ reg &= ~STEREO_DAC_STD_CFS_INV; ++ reg &= ~STEREO_DAC_STD_CSM; ++ reg &= ~STEREO_DAC_STD_C_CLK_EN; ++ ++ /* DAI mode */ ++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ reg |= STEREO_DAC_STD_CFS(2); ++ break; ++ case SND_SOC_DAIFMT_DSP_A: ++ reg |= STEREO_DAC_STD_CFS(1); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* DAI clock inversion */ ++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ reg |= STEREO_DAC_STD_BCL_INV; ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ reg |= STEREO_DAC_STD_BCL_INV; ++ reg |= STEREO_DAC_STD_CFS_INV; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ reg |= STEREO_DAC_STD_CFS_INV; ++ break; ++ } ++ ++ /* DAI clock master masks */ ++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBM_CFM: ++ reg |= STEREO_DAC_STD_C_CLK_EN; ++ break; ++ case SND_SOC_DAIFMT_CBS_CFS: ++ reg |= STEREO_DAC_STD_CSM; ++ case SND_SOC_DAIFMT_CBM_CFS: ++ case SND_SOC_DAIFMT_CBS_CFM: ++ return -EINVAL; ++ } ++ ++ reg |= STEREO_DAC_STD_C_EN; /* DAC power up */ ++ reg |= STEREO_DAC_STD_C_RESET; /* reset filter */ ++ ++ if (dai->id == MC13783_ID_STEREO_DAC) ++ mc13783_write(codec, PMIC_AUDIO_DAC, reg); ++ else ++ mc13783_write(codec, PMIC_AUDIO_CODEC, reg); ++ ++ return 0; ++} ++ ++static int mc13783_set_sysclk(struct snd_soc_dai *dai, ++ int clk_id, unsigned int freq, int dir) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ int clk; ++ unsigned int reg; ++ ++ if (dai->id == MC13783_ID_STEREO_DAC) ++ reg = mc13783_read(codec, PMIC_AUDIO_DAC); ++ else ++ reg = mc13783_read(codec, PMIC_AUDIO_CODEC); ++ ++ reg &= ~STEREO_DAC_STD_CLK(0x7); ++ reg &= ~STEREO_DAC_STD_CLK_SEL; ++ ++ switch (freq) { ++ case 13000000: ++ clk = 0; ++ break; ++ case 15360000: ++ clk = 1; ++ break; ++ case 16800000: ++ clk = 2; ++ break; ++ case 26000000: ++ clk = 4; ++ break; ++ case 12000000: ++ clk = 5; ++ break; ++ case 3686400: ++ clk = 6; ++ break; ++ case 33600000: ++ clk = 7; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (clk_id == MC13783_CLK_CLIB) ++ reg |= STEREO_DAC_STD_CLK_SEL; ++ ++ reg |= STEREO_DAC_STD_CLK(clk); ++ ++ if (dai->id == MC13783_ID_STEREO_DAC) ++ mc13783_write(codec, PMIC_AUDIO_DAC, reg); ++ else ++ mc13783_write(codec, PMIC_AUDIO_CODEC, reg); ++ ++ return 0; ++} ++ ++static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai, ++ unsigned int mask, int slots) ++{ ++ struct snd_soc_codec *codec = dai->codec; ++ unsigned int reg; ++ ++ if (slots != 4) ++ return -EINVAL; ++ ++ reg = mc13783_read(codec, PMIC_SSI_NETWORK); ++ ++ reg &= ~(0xfff << 0); ++ reg |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */ ++ reg |= (0x01 << 4); /* secondary timeslot TX is 1 */ ++ reg |= (0x01 << 6); /* secondary timeslot RX is 1 */ ++ ++ mc13783_write(codec, PMIC_SSI_NETWORK, reg); ++ ++ return 0; ++} ++ ++static int mc13783_get_alsp(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct mc13783_priv *priv = codec->private_data; ++ ++ ucontrol->value.integer.value[0] = priv->mc13783_alsp_val; ++ ++ return 0; ++} ++ ++static int mc13783_put_alsp(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct mc13783_priv *priv = codec->private_data; ++ unsigned int reg; ++ ++ priv->mc13783_alsp_val = ucontrol->value.integer.value[0]; ++ ++ reg = mc13783_read(codec, PMIC_AUDIO_RX_0); ++ ++ reg &= ~((1 << 5) | (1 << 7)); ++ ++ if (priv->mc13783_alsp_val) ++ reg |= 1 << 5; ++ ++ if (priv->mc13783_alsp_val == 2) ++ reg |= 1 << 7; ++ ++ mc13783_write(codec, PMIC_AUDIO_RX_0, reg); ++ ++ return 0; ++} ++ ++static int mc13783_pcm_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ int val; ++ ++ val = mc13783_read(codec, PMIC_AUDIO_RX_0); ++ ucontrol->value.enumerated.item[0] = (val >> 22) & 1; ++ ++ return 0; ++} ++ ++static int mc13783_pcm_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ unsigned int r36, r37; ++ ++ r36 = mc13783_read(codec, PMIC_AUDIO_RX_0); ++ r37 = mc13783_read(codec, PMIC_AUDIO_RX_1); ++ ++ r36 &= ~(1 << 22); ++ r37 &= ~(1 << 5); ++ ++ if (ucontrol->value.enumerated.item[0]) { ++ r36 |= (1 << 22); ++ r37 |= (1 << 5); ++ } else { ++ r36 &= ~(1 << 22); ++ r37 &= ~(1 << 5); ++ } ++ ++ mc13783_write(codec, PMIC_AUDIO_RX_0, r36); ++ mc13783_write(codec, PMIC_AUDIO_RX_1, r37); ++ ++ return 0; ++} ++ ++static int mc13783_linein_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ int val; ++ ++ val = mc13783_read(codec, PMIC_AUDIO_RX_0); ++ ucontrol->value.enumerated.item[0] = (val >> 23) & 1; ++ ++ return 0; ++} ++ ++static int mc13783_linein_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ unsigned int r36, r37; ++ ++ r36 = mc13783_read(codec, PMIC_AUDIO_RX_0); ++ r37 = mc13783_read(codec, PMIC_AUDIO_RX_1); ++ ++ r36 &= ~(1 << 23); ++ r37 &= ~(1 << 10); ++ ++ if (ucontrol->value.enumerated.item[0]) { ++ r36 |= (1 << 23); ++ r37 |= (1 << 10); ++ } else { ++ r36 &= ~(1 << 23); ++ r37 &= ~(1 << 10); ++ } ++ ++ mc13783_write(codec, PMIC_AUDIO_RX_0, r36); ++ mc13783_write(codec, PMIC_AUDIO_RX_1, r37); ++ ++ return 0; ++} ++ ++static int mc13783_capure_cache; ++ ++static int mc13783_get_capture(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ ucontrol->value.enumerated.item[0] = mc13783_capure_cache; ++ return 0; ++} ++ ++#define AMC1REN (1 << 5) ++#define AMC2EN (1 << 9) ++#define ATXINEN (1 << 11) ++#define RXINREC (1 << 13) ++#define AMC1LEN (1 << 7) ++ ++static int mc13783_put_capture(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ unsigned int r38, change; ++ ++ r38 = mc13783_read(codec, PMIC_AUDIO_TX); ++ ++ change = (mc13783_capure_cache != ucontrol->value.enumerated.item[0]); ++ mc13783_capure_cache = ucontrol->value.enumerated.item[0]; ++ r38 &= ~(AMC1REN | AMC2EN | ATXINEN | RXINREC | AMC1LEN); ++ ++ switch (mc13783_capure_cache) { ++ case 0: ++ break; ++ case 1: ++ r38 |= RXINREC; ++ break; ++ case 2: ++ r38 |= AMC1REN | AMC1LEN; ++ break; ++ case 3: ++ r38 |= AMC1REN; ++ break; ++ case 4: ++ r38 |= AMC2EN; ++ break; ++ case 5: ++ r38 |= AMC1LEN | AMC2EN; ++ break; ++ case 6: ++ r38 |= ATXINEN; ++ break; ++ case 7: ++ r38 |= AMC1LEN | ATXINEN; ++ break; ++ case 8: ++ r38 |= AMC1LEN | RXINREC; ++ break; ++ case 9: ++ r38 |= AMC1LEN; ++ break; ++ default: ++ break; ++ } ++ ++ mc13783_write(codec, PMIC_AUDIO_TX, r38); ++ ++ return change; ++} ++ ++static const char *mc13783_asp[] = {"Off", "Codec", "Right"}; ++static const char *mc13783_alsp[] = {"Off", "Codec", "Right"}; ++ ++static const char *mc13783_ahs[] = {"Codec", "Mixer"}; ++ ++static const char *mc13783_arxout[] = {"Codec", "Mixer"}; ++ ++static const char *mc13783_capture[] = {"off/off", "rxinl/rxinr", ++ "mc1lin/mc1rin", "off/mc1rin", "off/mc2in", "mc1lin/mc2in", ++ "off/txin", "mc1lin/txin", "mc1lin/rxinr", "mc1lin/off"}; ++ ++static const char *mc13783_3d_mixer[] = {"Stereo", "Phase Mix", ++ "Mono", "Mono Mix"}; ++ ++static const struct soc_enum mc13783_enum_asp = ++ SOC_ENUM_SINGLE(PMIC_AUDIO_RX_0, 3, ARRAY_SIZE(mc13783_asp), mc13783_asp); ++ ++static const struct soc_enum mc13783_enum_alsp = ++ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mc13783_alsp), mc13783_alsp); ++ ++static const struct soc_enum mc13783_enum_ahs = ++ SOC_ENUM_SINGLE(PMIC_AUDIO_RX_0, 11, ARRAY_SIZE(mc13783_ahs), ++ mc13783_ahs); ++ ++static const struct soc_enum mc13783_enum_arxout = ++ SOC_ENUM_SINGLE(PMIC_AUDIO_RX_0, 17, ARRAY_SIZE(mc13783_arxout), ++ mc13783_arxout); ++ ++static const struct soc_enum mc13783_enum_capture = ++ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mc13783_capture), mc13783_capture); ++ ++static const struct soc_enum mc13783_enum_3d_mixer = ++ SOC_ENUM_SINGLE(PMIC_AUDIO_RX_1, 16, ARRAY_SIZE(mc13783_3d_mixer), ++ mc13783_3d_mixer); ++ ++static struct snd_kcontrol_new mc13783_control_list[] = { ++ /* Output Routing */ ++ SOC_ENUM("Asp Source", mc13783_enum_asp), ++ SOC_ENUM_EXT("Alsp Source", mc13783_enum_alsp, mc13783_get_alsp, ++ mc13783_put_alsp), ++ SOC_ENUM("Ahs Source", mc13783_enum_ahs), ++ SOC_SINGLE("Ahsr enable", PMIC_AUDIO_RX_0, 9, 1, 0), ++ SOC_SINGLE("Ahsl enable", PMIC_AUDIO_RX_0, 10, 1, 0), ++ SOC_ENUM("Arxout Source", mc13783_enum_arxout), ++ SOC_SINGLE("ArxoutR enable", PMIC_AUDIO_RX_0, 16, 1, 0), ++ SOC_SINGLE("ArxoutL enable", PMIC_AUDIO_RX_0, 15, 1, 0), ++ SOC_SINGLE_EXT("PCM Playback Switch", 0, 0, 1, 0, mc13783_pcm_get, ++ mc13783_pcm_put), ++ SOC_SINGLE("PCM Playback Volume", PMIC_AUDIO_RX_1, 6, 15, 0), ++ SOC_SINGLE_EXT("Line in Switch", 0, 0, 1, 0, mc13783_linein_get, ++ mc13783_linein_put), ++ SOC_SINGLE("Line in Volume", PMIC_AUDIO_RX_1, 12, 15, 0), ++ SOC_ENUM_EXT("Capture Source", mc13783_enum_capture, mc13783_get_capture, ++ mc13783_put_capture), ++ SOC_DOUBLE("PCM Capture Volume", PMIC_AUDIO_TX, 19, 14, 31, 0), ++ SOC_ENUM("3D Control", mc13783_enum_3d_mixer), ++}; ++ ++static struct snd_soc_codec *mc13783_codec; ++ ++static int mc13783_probe(struct platform_device *pdev) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct snd_soc_codec *codec = mc13783_codec; ++ struct mc13783_priv *priv = codec->private_data; ++ int i, ret = 0; ++ ++ if (!mc13783_codec) { ++ dev_err(&pdev->dev, "Codec device not registered\n"); ++ return -ENODEV; ++ } ++ ++ /* these are the reset values */ ++ priv->reg_cache[PMIC_AUDIO_RX_0] = 0x001000; ++ priv->reg_cache[PMIC_AUDIO_RX_1] = 0x00d35A; ++ priv->reg_cache[PMIC_AUDIO_TX] = 0x420000; ++ priv->reg_cache[PMIC_SSI_NETWORK] = 0x013060; ++ priv->reg_cache[PMIC_AUDIO_CODEC] = 0x180027; ++ priv->reg_cache[PMIC_AUDIO_DAC] = 0x0e0004; ++ ++ /* VAUDIOON -> supply audio part, BIAS enable */ ++ priv->reg_cache[PMIC_AUDIO_RX_0] |= 0x3; ++ ++ for (i = 36; i < 42; i++) ++ mc13783_write(codec, i, priv->reg_cache[i]); ++ ++ socdev->card->codec = mc13783_codec; ++ ++ /* register pcms */ ++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); ++ if (ret < 0) { ++ dev_err(codec->dev, "failed to create pcms: %d\n", ret); ++ goto pcm_err; ++ } ++ ++ snd_soc_add_controls(codec, mc13783_control_list, ++ ARRAY_SIZE(mc13783_control_list)); ++ ret = snd_soc_init_card(socdev); ++ if (ret < 0) { ++ dev_err(codec->dev, "failed to register card: %d\n", ret); ++ goto card_err; ++ } ++ ++ return ret; ++ ++card_err: ++ snd_soc_free_pcms(socdev); ++ snd_soc_dapm_free(socdev); ++pcm_err: ++ return ret; ++} ++ ++static int mc13783_remove(struct platform_device *pdev) ++{ ++ struct snd_soc_device *socdev = platform_get_drvdata(pdev); ++ struct snd_soc_codec *codec = mc13783_codec; ++ unsigned int reg; ++ ++ reg = mc13783_read(codec, PMIC_AUDIO_RX_0); ++ ++ /* VAUDIOON -> switch off audio part, BIAS disable */ ++ reg &= ~0x3; ++ ++ mc13783_write(codec, PMIC_AUDIO_RX_0, reg); ++ ++ snd_soc_free_pcms(socdev); ++ snd_soc_dapm_free(socdev); ++ ++ return 0; ++} ++ ++#define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000) ++ ++#define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ ++ SNDRV_PCM_FMTBIT_S24_LE) ++ ++static struct snd_soc_dai_ops mc13783_ops_dac = { ++ .hw_params = mc13783_pcm_hw_params, ++ .set_fmt = mc13783_set_fmt, ++ .set_sysclk = mc13783_set_sysclk, ++}; ++ ++static struct snd_soc_dai_ops mc13783_ops_voice_codec = { ++ .hw_params = mc13783_pcm_hw_params, ++ .set_fmt = mc13783_set_fmt, ++ .set_sysclk = mc13783_set_sysclk, ++ .set_tdm_slot = mc13783_set_tdm_slot_codec, ++}; ++ ++struct snd_soc_dai mc13783_dai[] = { ++ { ++ .name = "MC13783 Playback", ++ .id = MC13783_ID_STEREO_DAC, ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = MC13783_FORMATS, ++ }, ++ .ops = &mc13783_ops_dac, ++ }, { ++ .name = "MC13783 Capture", ++ .id = MC13783_ID_STEREO_CODEC, ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = MC13783_RATES_RECORD, ++ .formats = MC13783_FORMATS, ++ }, ++ .ops = &mc13783_ops_voice_codec, ++ }, ++}; ++EXPORT_SYMBOL_GPL(mc13783_dai); ++ ++struct snd_soc_codec_device soc_codec_dev_mc13783 = { ++ .probe = mc13783_probe, ++ .remove = mc13783_remove, ++}; ++EXPORT_SYMBOL_GPL(soc_codec_dev_mc13783); ++ ++/* ++ * OK, this stinks. We currently only can support one MC13783. ++ * Lets take it as an intermediate to turn this stuff into SoC ++ * Audio. ++ */ ++static int mc13783_codec_probe(struct platform_device *pdev) ++{ ++ struct mc13783_priv *priv; ++ struct snd_soc_codec *codec; ++ int ret; ++ ++ if (mc13783) ++ return -EBUSY; ++ ++ mc13783 = dev_get_drvdata(pdev->dev.parent); ++ ++ priv = kzalloc(sizeof(struct mc13783_priv), GFP_KERNEL); ++ if (priv == NULL) ++ return -ENOMEM; ++ ++ codec = &priv->codec; ++ codec->private_data = priv; ++ mc13783_codec = codec; ++ ++ mutex_init(&codec->mutex); ++ INIT_LIST_HEAD(&codec->dapm_widgets); ++ INIT_LIST_HEAD(&codec->dapm_paths); ++ ++ codec->name = "mc13783"; ++ codec->owner = THIS_MODULE; ++ codec->read = mc13783_read; ++ codec->write = mc13783_write; ++ codec->dai = mc13783_dai; ++ codec->num_dai = ARRAY_SIZE(mc13783_dai); ++ codec->control_data = priv; ++ codec->dev = &pdev->dev; ++ ++ mc13783_dai[0].dev = codec->dev; ++ mc13783_dai[1].dev = codec->dev; ++ ++ ret = snd_soc_register_codec(codec); ++ if (ret) ++ goto err_register_codec; ++ ++ ret = snd_soc_register_dais(mc13783_dai, ARRAY_SIZE(mc13783_dai)); ++ if (ret) ++ goto err_register_dais; ++ ++ return 0; ++ ++err_register_dais: ++ snd_soc_unregister_codec(codec); ++err_register_codec: ++ dev_err(&pdev->dev, "register codec failed with %d\n", ret); ++ kfree(priv); ++ ++ return ret; ++} ++ ++static int mc13783_codec_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_codec(mc13783_codec); ++ snd_soc_unregister_dais(mc13783_dai, ARRAY_SIZE(mc13783_dai)); ++ ++ mc13783 = NULL; ++ ++ return 0; ++} ++ ++static struct platform_driver mc13783_codec_driver = { ++ .driver = { ++ .name = "mc13783-codec", ++ .owner = THIS_MODULE, ++ }, ++ .probe = mc13783_codec_probe, ++ .remove = __devexit_p(mc13783_codec_remove), ++}; ++ ++static __init int mc13783_init(void) ++{ ++ return platform_driver_register(&mc13783_codec_driver); ++} ++ ++static __exit void mc13783_exit(void) ++{ ++ platform_driver_unregister(&mc13783_codec_driver); ++} ++ ++module_init(mc13783_init); ++module_exit(mc13783_exit); ++ ++MODULE_DESCRIPTION("ASoC MC13783 driver"); ++MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>"); ++MODULE_LICENSE("GPL"); +diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h +new file mode 100644 +index 0000000..b28dbef +--- /dev/null ++++ b/sound/soc/codecs/mc13783.h +@@ -0,0 +1,32 @@ ++/* ++ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef MC13783_MIXER_H ++#define MC13783_MIXER_H ++ ++extern int mc13783_add_ctl(struct snd_card*, void *); ++ ++extern struct snd_soc_dai mc13783_dai[]; ++extern struct snd_soc_codec_device soc_codec_dev_mc13783; ++ ++#define MC13783_CLK_CLIA 1 ++#define MC13783_CLK_CLIB 2 ++ ++#define MC13783_ID_STEREO_DAC 1 ++#define MC13783_ID_STEREO_CODEC 2 ++ ++#endif /* MC13783_MIXER_H */ +-- +1.6.5.2 + |