[alsa-devel] [PATCH 1/2] ASoC: Add DA7210 codec device support for ALSA
Liam Girdwood
lrg at slimlogic.co.uk
Wed Dec 9 11:59:51 CET 2009
On Wed, 2009-12-09 at 11:53 +0900, Kuninori Morimoto wrote:
> This original driver was created by Dialog Semiconductor,
> and cleanuped by Kuninori Morimoto.
> Special thanks to David Chen.
> This is very simple ASoC codec driver.
> It was tested by EcoVec24 board.
>
> Signed-off-by: David Chen <Dajun.chen at diasemi.com>
> Signed-off-by: Kuninori Morimoto <morimoto.kuninori at renesas.com>
Just had a quick look and have identified some minor issues below.
> sound/soc/codecs/Kconfig | 4 +
> sound/soc/codecs/Makefile | 2 +
> sound/soc/codecs/da7210.c | 773 +++++++++++++++++++++++++++++++++++++++++++++
> sound/soc/codecs/da7210.h | 24 ++
> 4 files changed, 803 insertions(+), 0 deletions(-)
> create mode 100644 sound/soc/codecs/da7210.c
> create mode 100644 sound/soc/codecs/da7210.h
>
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 52b005f..ff33b02 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
> select SND_SOC_AK4671 if I2C
> select SND_SOC_CS4270 if I2C
> select SND_SOC_MAX9877 if I2C
> + select SND_SOC_DA7210 if I2C
> select SND_SOC_PCM3008
> select SND_SOC_SPDIF
> select SND_SOC_SSM2602 if I2C
> @@ -112,6 +113,9 @@ config SND_SOC_AK4671
> config SND_SOC_CS4270
> tristate
>
> +config SND_SOC_DA7210
> + tristate
> +
> # Cirrus Logic CS4270 Codec VD = 3.3V Errata
> # Select if you are affected by the errata where the part will not function
> # if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index dbaecb1..a495bf8 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -10,6 +10,7 @@ snd-soc-ak4642-objs := ak4642.o
> snd-soc-ak4671-objs := ak4671.o
> snd-soc-cs4270-objs := cs4270.o
> snd-soc-cx20442-objs := cx20442.o
> +snd-soc-da7210-objs := da7210.o
> snd-soc-l3-objs := l3.o
> snd-soc-pcm3008-objs := pcm3008.o
> snd-soc-spdif-objs := spdif_transciever.o
> @@ -66,6 +67,7 @@ obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
> obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.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
> obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
> obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
> obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o
> diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
> new file mode 100644
> index 0000000..1356b90
> --- /dev/null
> +++ b/sound/soc/codecs/da7210.c
> @@ -0,0 +1,773 @@
> +/*
> + * DA7210 ALSA Soc codec driver
> + *
> + * Copyright (c) 2009 Dialog Semiconductor
> + * Written by David Chen <Dajun.chen at diasemi.com>
> + *
> + * Copyright (C) 2009 Renesas Solutions Corp.
> + * Cleanups by Kuninori Morimoto <morimoto.kuninori at renesas.com>
> + *
> + * Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/delay.h>
> +#include <linux/pm.h>
> +#include <linux/i2c.h>
> +#include <linux/platform_device.h>
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/tlv.h>
> +#include <sound/initval.h>
> +#include <asm/div64.h>
> +
> +#include "da7210.h"
> +
> +/* DA7210 register space */
> +#define DA7210_STATUS 0x02
> +#define DA7210_STARTUP1 0x03
> +#define DA7210_STARTUP2 0x04
> +#define DA7210_STARTUP3 0x05
> +#define DA7210_MIC_L 0x07
> +#define DA7210_MIC_R 0x08
> +#define DA7210_IN_GAIN 0x0C
> +#define DA7210_INMIX_L 0x0D
> +#define DA7210_INMIX_R 0x0E
> +#define DA7210_ADC_HPF 0x0F
> +#define DA7210_ADC 0x10
> +#define DA7210_DAC_HPF 0x14
> +#define DA7210_DAC_L 0x15
> +#define DA7210_DAC_R 0x16
> +#define DA7210_DAC_SEL 0x17
> +#define DA7210_OUTMIX_L 0x1C
> +#define DA7210_OUTMIX_R 0x1D
> +#define DA7210_OUT2 0x20
> +#define DA7210_HP_L_VOL 0x21
> +#define DA7210_HP_R_VOL 0x22
> +#define DA7210_HP_CFG 0x23
> +#define DA7210_DAI_SRC_SEL 0x25
> +#define DA7210_DAI_CFG1 0x26
> +#define DA7210_DAI_CFG3 0x28
> +#define DA7210_PLL_DIV3 0x2B
> +#define DA7210_PLL 0x2C
> +
> +/* STARTUP1 bit fields */
> +#define DA7210_SC_MST_EN (1 << 0)
> +
> +/* STARTUP2 bit fields */
> +#define DA7210_LOUT1_L_STBY (1 << 0)
> +#define DA7210_LOUT1_R_STBY (1 << 1)
> +#define DA7210_LOUT2_STBY (1 << 2)
> +#define DA7210_HP_L_STBY (1 << 3)
> +#define DA7210_HP_R_STBY (1 << 4)
> +#define DA7210_DAC_L_STBY (1 << 5)
> +#define DA7210_DAC_R_STBY (1 << 6)
> +
> +/* STARTUP3 bit fields */
> +#define DA7210_MIC_L_STBY (1 << 0)
> +#define DA7210_MIC_R_STBY (1 << 1)
> +#define DA7210_LIN1_L_STBY (1 << 2)
> +#define DA7210_LIN1_R_STBY (1 << 3)
> +#define DA7210_LIN2_STBY (1 << 4)
> +#define DA7210_ADC_L_STBY (1 << 5)
> +#define DA7210_ADC_R_STBY (1 << 6)
> +
> +/* MIC_L bit fields */
> +#define DA7210_MICBIAS_EN (1 << 6)
> +#define DA7210_MIC_L_EN (1 << 7)
> +
> +/* MIC_R bit fields */
> +#define DA7210_MIC_R_EN (1 << 7)
> +
> +/* INMIX_L bit fields */
> +#define DA7210_IN_L_EN (1 << 7)
> +
> +/* INMIX_R bit fields */
> +#define DA7210_IN_R_EN (1 << 7)
> +
> +/* ADC_HPF bit fields */
> +#define DA7210_ADC_HPF_EN (1 << 3)
> +#define DA7210_ADC_VOICE_EN (1 << 7)
> +
> +/* ADC bit fields */
> +#define DA7210_ADC_L_EN (1 << 3)
> +#define DA7210_ADC_R_EN (1 << 7)
> +
> +/* DAC_HPF bit fields */
> +#define DA7210_DAC_HPF_EN (1 << 3)
> +#define DA7210_DAC_VOICE_EN (1 << 7)
> +
> +/* DAC_SEL bit fields */
> +#define DA7210_DAC_L_SRC_DAI_L (4 << 0)
> +#define DA7210_DAC_L_EN (1 << 3)
> +#define DA7210_DAC_R_SRC_DAI_R (5 << 4)
> +#define DA7210_DAC_R_EN (1 << 7)
> +
> +/* OUTMIX_L bit fields */
> +#define DA7210_OUT_L_EN (1 << 7)
> +
> +/* OUTMIX_R bit fields */
> +#define DA7210_OUT_R_EN (1 << 7)
> +
> +/* HP_CFG bit fields */
> +#define DA7210_HP_2CAP_MODE (1 << 1)
> +#define DA7210_HP_SENSE_EN (1 << 2)
> +#define DA7210_HP_L_EN (1 << 3)
> +#define DA7210_HP_MODE (1 << 6)
> +#define DA7210_HP_R_EN (1 << 7)
> +
> +/* DAI_SRC_SEL bit fields */
> +#define DA7210_DAI_OUT_L_SRC (6 << 0)
> +#define DA7210_DAI_OUT_R_SRC (7 << 4)
> +
> +/* DAI_CFG1 bit fields */
> +#define DA7210_DAI_WORD_S16_LE (0 << 0)
> +#define DA7210_DAI_WORD_S24_LE (2 << 0)
> +#define DA7210_DAI_FLEN_64BIT (1 << 2)
> +#define DA7210_DAI_MODE_MASTER (1 << 7)
> +
> +/* DAI_CFG3 bit fields */
> +#define DA7210_DAI_FORMAT_I2SMODE (0 << 0)
> +#define DA7210_DAI_OE (1 << 3)
> +#define DA7210_DAI_EN (1 << 7)
> +
> +/*PLL_DIV3 bit fields */
> +#define DA7210_MCLK_RANGE_10_20_MHZ (1 << 4)
> +#define DA7210_PLL_BYP (1 << 6)
> +
> +/* PLL bit fields */
> +#define DA7210_PLL_FS_48000 (11 << 0)
> +
> +#define DA7210_VERSION "0.0.1"
> +
> +/* Codec private data */
> +struct da7210_priv {
> + struct snd_soc_codec codec;
> +};
> +
> +static struct snd_soc_codec *da7210_codec;
> +
> +/*
> + * Register cache
> + */
> +static const u8 da7210_reg[] = {
> + 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R0 - R7 */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, /* R8 - RF */
> + 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x54, /* R10 - R17 */
> + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R18 - R1F */
> + 0x00, 0x00, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, /* R20 - R27 */
> + 0x04, 0x00, 0x00, 0x30, 0x2A, 0x00, 0x40, 0x00, /* R28 - R2F */
> + 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, /* R30 - R37 */
> + 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, /* R38 - R3F */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R40 - R4F */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R48 - R4F */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R50 - R57 */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R58 - R5F */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R60 - R67 */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R68 - R6F */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R70 - R77 */
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x54, 0x00, /* R78 - R7F */
> + 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, /* R80 - R87 */
> + 0x00, /* R88 */
> +};
> +
> +/*
> + * Read da7210 register cache
> + */
> +static inline unsigned int da7210_read_reg_cache(struct snd_soc_codec *codec,
> + unsigned int reg)
> +{
> + u8 *cache = codec->reg_cache;
> + BUG_ON(reg > ARRAY_SIZE(da7210_reg));
> + return cache[reg];
> +}
> +
> +/*
> + * Write da7210 register cache
> + */
> +static inline void da7210_write_reg_cache(struct snd_soc_codec *codec,
> + unsigned int reg, unsigned int value)
> +{
> + u8 *cache = codec->reg_cache;
> +
> + cache[reg] = value;
> +}
> +
> +/*
> + * Write to the da7210 register space
> + */
> +static int da7210_write(struct snd_soc_codec *codec, unsigned int reg,
> + unsigned int value)
> +{
> +
> + u8 data[2];
> +
> + /* data[0] da7210 register offset */
> + /* data[1] register data */
> + data[0] = reg & 0xff;
> + data[1] = value & 0xff;
> +
> + if (2 != codec->hw_write(codec->control_data, data, 2)) {
> + dev_warn(codec->dev, "I2C write Failed!\n");
> + return -EIO;
> + }
> +
Why not use #define register width here than use the number 2.
> + /* write the cache only if hardware write is successful */
> + if (data[0] < ARRAY_SIZE(da7210_reg))
> + da7210_write_reg_cache(codec, data[0], data[1]);
> +
> + return 0;
> +}
> +
> +/*
> + * Read from the da7210 register space.
> + */
> +static inline unsigned int da7210_read(struct snd_soc_codec *codec,
> + unsigned int reg)
> +{
> + /* FIXME !!
> + *
> + * we should read status from I2C.
> + * But not supported now.
> + */
Any particular reason for I2C read back not being supported atm. If I2C
write works, then read should too.
> + if (DA7210_STATUS == reg)
> + return -EIO;
> +
> + return da7210_read_reg_cache(codec, reg);
> +}
> +
> +static const char *da7210_mic_bias_voltage[] = { "1.5", "1.6", "2.2", "2.3" };
> +
> +static const struct soc_enum da7210_enum[] = {
> + SOC_ENUM_SINGLE(DA7210_MIC_L, 4, 4, da7210_mic_bias_voltage),
> +};
> +
> +/* Add non DAPM controls */
> +static const struct snd_kcontrol_new da7210_snd_controls[] = {
> + /* Mixer Playback controls */
> + SOC_DOUBLE_R("DAC Gain", DA7210_DAC_L, DA7210_DAC_R, 0, 0x37, 1),
"DAC Volume"
> + SOC_DOUBLE_R("HeadPhone Playback Volume",
> + DA7210_HP_L_VOL, DA7210_HP_R_VOL, 0, 0x3f, 0),
> + /* Mixer Capture controls */
> + SOC_DOUBLE_R("Mic Capture Volume",
> + DA7210_MIC_L, DA7210_MIC_R, 0, 0x07, 0),
> + SOC_DOUBLE("In PGA Gain", DA7210_IN_GAIN, 0, 4, 0x0F, 0),
Best to use volume instead of gain.
> + SOC_SINGLE("Mic Bias", DA7210_MIC_L, 6, 1, 0),
> + SOC_ENUM("Mic Bias Voltage", da7210_enum[0]),
> +};
> +
> +/* ----------------------------Capture Mixers--------------------------- */
> +
> +/* In Mixer Left */
> +static const struct snd_kcontrol_new da7210_in_left_mixer_controls[] = {
> + SOC_DAPM_SINGLE("MIC_L Switch", DA7210_INMIX_L, 0, 1, 0),
> + SOC_DAPM_SINGLE("MIC_R Switch", DA7210_INMIX_L, 1, 1, 0),
> + SOC_DAPM_SINGLE("Aux1_L Switch", DA7210_INMIX_L, 2, 1, 0),
> + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_L, 3, 1, 0),
> + SOC_DAPM_SINGLE("DAC_L Switch", DA7210_INMIX_L, 4, 1, 0),
> +};
> +
> +/* In Mixer Right */
> +static const struct snd_kcontrol_new da7210_in_right_mixer_controls[] = {
> + SOC_DAPM_SINGLE("MIC_R Switch", DA7210_INMIX_R, 0, 1, 0),
> + SOC_DAPM_SINGLE("MIC_L Switch", DA7210_INMIX_R, 1, 1, 0),
> + SOC_DAPM_SINGLE("Aux1_R Switch", DA7210_INMIX_R, 2, 1, 0),
> + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_R, 3, 1, 0),
> + SOC_DAPM_SINGLE("DAC_R Switch", DA7210_INMIX_R, 4, 1, 0),
> + SOC_DAPM_SINGLE("INPGA_L Switch", DA7210_INMIX_R, 5, 1, 0),
> +};
> +
> +/*----------------------------Playback Mixers---------------------------*/
> +/* Out Mixer Left */
> +static const struct snd_kcontrol_new da7210_out_mixer_left_controls[] = {
> + SOC_DAPM_SINGLE("AUX1_L Switch", DA7210_OUTMIX_L, 0, 1, 0),
> + SOC_DAPM_SINGLE("AUX2 Switch", DA7210_OUTMIX_L, 1, 1, 0),
> + SOC_DAPM_SINGLE("IN_L Switch", DA7210_OUTMIX_L, 2, 1, 0),
> + SOC_DAPM_SINGLE("IN_R Switch", DA7210_OUTMIX_L, 3, 1, 0),
> + SOC_DAPM_SINGLE("DAC_L Switch", DA7210_OUTMIX_L, 4, 1, 0),
> +};
> +
> +/* Out Mixer Right */
> +static const struct snd_kcontrol_new da7210_out_mixer_right_controls[] = {
> + SOC_DAPM_SINGLE("AUX1_R Switch", DA7210_OUTMIX_R, 0, 1, 0),
> + SOC_DAPM_SINGLE("AUX2 Switch", DA7210_OUTMIX_R, 1, 1, 0),
> + SOC_DAPM_SINGLE("IN_L Switch", DA7210_OUTMIX_R, 2, 1, 0),
> + SOC_DAPM_SINGLE("IN_R Switch", DA7210_OUTMIX_R, 3, 1, 0),
> + SOC_DAPM_SINGLE("DAC_R Switch", DA7210_OUTMIX_R, 4, 1, 0),
> +};
> +
> +/* Mono Mixer */
> +static const struct snd_kcontrol_new da7210_mono_mixer_controls[] = {
> + SOC_DAPM_SINGLE("IN_L Switch", DA7210_OUT2, 3, 1, 0),
> + SOC_DAPM_SINGLE("IN_R Switch", DA7210_OUT2, 4, 1, 0),
> + SOC_DAPM_SINGLE("DAC_L Switch", DA7210_OUT2, 5, 1, 0),
> + SOC_DAPM_SINGLE("DAC_R Switch", DA7210_OUT2, 6, 1, 0),
> +};
> +
> +static const struct snd_kcontrol_new da7210_headphone_left_control =
> + SOC_DAPM_SINGLE("Switch", DA7210_STARTUP2, 3, 1, 1);
> +static const struct snd_kcontrol_new da7210_headphone_right_control =
> + SOC_DAPM_SINGLE("Switch", DA7210_STARTUP2, 4, 1, 1);
> +static const struct snd_kcontrol_new da7210_MicIn_left_control =
> + SOC_DAPM_SINGLE("Switch", DA7210_STARTUP3, 0, 1, 1);
> +static const struct snd_kcontrol_new da7210_MicIn_right_control =
> + SOC_DAPM_SINGLE("Switch", DA7210_STARTUP3, 1, 1, 1);
> +
> +/* DAPM widgets */
> +static const struct snd_soc_dapm_widget da7210_dapm_widgets[] = {
> + /* DAMP stream domain - DAC. Enabled when playback is started */
> + SND_SOC_DAPM_DAC("DAC Left", "Playback", DA7210_STARTUP2, 5, 1),
> + SND_SOC_DAPM_DAC("DAC Right", "Playback", DA7210_STARTUP2, 6, 1),
> +
> + /* DAMP stream domain - ADC. Enabled when capture is started */
> + SND_SOC_DAPM_ADC("ADC Left", "Capture", DA7210_STARTUP3, 5, 1),
> + SND_SOC_DAPM_ADC("ADC Right", "Capture", DA7210_STARTUP3, 6, 1),
> +
> + /* DAPM path domain - switches and mixers */
> + /* Automatically set when mixer settings are changed by the user */
> + SND_SOC_DAPM_SWITCH("HPL Enable", SND_SOC_NOPM, 0, 0,
> + &da7210_headphone_left_control),
> + SND_SOC_DAPM_SWITCH("HPR Enable", SND_SOC_NOPM, 0, 0,
> + &da7210_headphone_right_control),
> + SND_SOC_DAPM_SWITCH("MicL Enable", SND_SOC_NOPM, 0, 0,
> + &da7210_MicIn_left_control),
> + SND_SOC_DAPM_SWITCH("MicR Enable", SND_SOC_NOPM, 0, 0,
> + &da7210_MicIn_right_control),
> +
> + SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0,
> + &da7210_out_mixer_left_controls[0],
> + ARRAY_SIZE(da7210_out_mixer_left_controls)),
> +
> + SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0,
> + &da7210_out_mixer_right_controls[0],
> + ARRAY_SIZE(da7210_out_mixer_right_controls)),
> +
> + SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0,
> + &da7210_in_left_mixer_controls[0],
> + ARRAY_SIZE(da7210_in_left_mixer_controls)),
> +
> + SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0,
> + &da7210_in_right_mixer_controls[0],
> + ARRAY_SIZE(da7210_in_right_mixer_controls)),
> +
> + /* DAPM Platform domain. Physically connected input and ouput pins */
> + SND_SOC_DAPM_OUTPUT("HPL"), /*Headphone Out left*/
> + SND_SOC_DAPM_OUTPUT("HPR"), /*Headphone out Right*/
> + SND_SOC_DAPM_INPUT("MICL"), /*MicIn left*/
> + SND_SOC_DAPM_INPUT("MICR"), /*MicIn Right*/
> +};
> +
> +/* DAPM audio route definition */
> +static const struct snd_soc_dapm_route audio_map[] = {
> + /*Out Mixer Left*/
> + {"Out Mixer Left", "DAC_L Switch", "DAC Left"},
> +
> + /*Out Mixer Right*/
> + {"Out Mixer Right", "DAC_R Switch", "DAC Right"},
> +
> + /*In Mixer Left*/
> + {"In Mixer Left", "MIC_L Switch", "MicL Enable"},
> +
> + /*In Mixer Right*/
> + {"In Mixer Right", "MIC_R Switch", "MicR Enable"},
> +
> + /*HPL*/
> + {"HPL", NULL, "HPL Enable"},
> + {"HPL Enable", "Switch", "Out Mixer Left"},
> +
> + /*HPR*/
> + {"HPR", NULL, "HPR Enable"},
> + {"HPR Enable", "Switch", "Out Mixer Right"},
> +
> + /*MICL*/
> + {"ADC Left", NULL, "In Mixer Left"},
> + {"MicL Enable", "Switch", "MICL"},
> +
> + /*MICR*/
> + {"ADC Right", NULL, "In Mixer Right"},
> + {"MicR Enable", "Switch", "MICR"},
> +};
> +
> +static int da7210_add_widgets(struct snd_soc_codec *codec)
> +{
> + snd_soc_dapm_new_controls(codec, da7210_dapm_widgets,
> + ARRAY_SIZE(da7210_dapm_widgets));
> + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
> + snd_soc_dapm_new_widgets(codec);
> + return 0;
> +}
> +
> +/*
> + * Set PCM DAI word length.
> + * Enable Voice Filter if Fs <= 16KHz
> + */
> +static int da7210_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 dai_cfg1;
> + unsigned int value, reg, mask;
> +
> + dai_cfg1 = 0xFC & da7210_read(codec, DA7210_DAI_CFG1);
> +
> + switch (params_format(params)) {
> + case SNDRV_PCM_FORMAT_S16_LE:
> + dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
> + break;
> + case SNDRV_PCM_FORMAT_S24_LE:
> + dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
> +
> + /* FIXME
> + *
> + * It support 48K only now
> + */
> + switch (params_rate(params)) {
> + case 48000:
> + if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
> + reg = DA7210_DAC_HPF;
> + mask = ~DA7210_DAC_VOICE_EN;
> + } else {
> + reg = DA7210_ADC_HPF;
> + mask = ~DA7210_ADC_VOICE_EN;
> + }
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + value = da7210_read_reg_cache(codec, reg);
> + value &= mask;
> + da7210_write(codec, reg, value);
> +
> + return 0;
> +}
> +
> +/*
> + * Set DAI mode and Format
> + */
> +static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai,
> + unsigned int fmt)
> +{
> + struct snd_soc_codec *codec = codec_dai->codec;
> + unsigned int dai_cfg1;
> + unsigned int dai_cfg3;
> +
> + dai_cfg1 = 0x7f & da7210_read(codec, DA7210_DAI_CFG1);
> + dai_cfg3 = 0xfc & da7210_read(codec, DA7210_DAI_CFG3);
> +
> + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
> + case SND_SOC_DAIFMT_CBM_CFM:
> + dai_cfg1 |= DA7210_DAI_MODE_MASTER;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* FIXME
> + *
> + * It support I2S only now
> + */
> + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> + case SND_SOC_DAIFMT_I2S:
> + dai_cfg3 |= DA7210_DAI_FORMAT_I2SMODE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* FIXME
> + *
> + * It support 64bit data transmission only now
> + */
> + dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
> +
> + da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
> + da7210_write(codec, DA7210_DAI_CFG3, dai_cfg3);
> +
> + return 0;
> +}
> +
> +#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
> +
> +/* DAI operations */
> +static struct snd_soc_dai_ops da7210_dai_ops = {
> + .hw_params = da7210_hw_params,
> + .set_fmt = da7210_set_dai_fmt,
> +};
> +
> +struct snd_soc_dai da7210_dai = {
> + .name = "DA7210 IIS",
> + .id = 0,
> + /* playback capabilities */
> + .playback = {
> + .stream_name = "Playback",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_96000,
> + .formats = DA7210_FORMATS,
> + },
> + /* capture capabilities */
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_96000,
> + .formats = DA7210_FORMATS,
> + },
> + .ops = &da7210_dai_ops,
> +};
> +EXPORT_SYMBOL_GPL(da7210_dai);
> +
> +/*
> + * Initialize the DA7210 driver
> + * register the mixer and dsp interfaces with the kernel
> + */
> +static int da7210_init(struct da7210_priv *da7210)
> +{
> + struct snd_soc_codec *codec = &da7210->codec;
> + int ret = 0;
> +
> + if (da7210_codec) {
> + dev_err(codec->dev, "Another da7210 is registered\n");
> + return -EINVAL;
> + }
> +
> + mutex_init(&codec->mutex);
> + INIT_LIST_HEAD(&codec->dapm_widgets);
> + INIT_LIST_HEAD(&codec->dapm_paths);
> +
> + codec->private_data = da7210;
> + codec->name = "DA7210";
> + codec->owner = THIS_MODULE;
> + codec->read = da7210_read;
> + codec->write = da7210_write;
> + codec->dai = &da7210_dai;
> + codec->num_dai = 1;
> + codec->hw_write = (hw_write_t)i2c_master_send;
> + codec->reg_cache_size = ARRAY_SIZE(da7210_reg);
> + codec->reg_cache = kmemdup(da7210_reg,
> + sizeof(da7210_reg), GFP_KERNEL);
> +
> + if (!codec->reg_cache)
> + return -ENOMEM;
> +
> + da7210_dai.dev = codec->dev;
> + da7210_codec = codec;
> +
> + ret = snd_soc_register_dai(&da7210_dai);
> + if (ret) {
> + dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
> + return -ENOMEM;
> + }
> +
> + /*
> + * ADC settings
> + */
> +
> + /* Enable Left & Right MIC PGA and Mic Bias */
> + da7210_write(codec, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
> + da7210_write(codec, DA7210_MIC_R, DA7210_MIC_R_EN);
> +
> + /* Enable Left and Right input PGA */
> + da7210_write(codec, DA7210_INMIX_L, DA7210_IN_L_EN);
> + da7210_write(codec, DA7210_INMIX_R, DA7210_IN_R_EN);
> +
> + /* Enable ADC Highpass Filter */
> + da7210_write(codec, DA7210_ADC_HPF, DA7210_ADC_HPF_EN);
> +
> + /* Enable Left and Right ADC */
> + da7210_write(codec, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
> +
> + /*
> + * DAC settings
> + */
> +
> + /* Enable DAC Highpass Filter */
> + da7210_write(codec, DA7210_DAC_HPF, DA7210_DAC_HPF_EN);
This could be a mixer control.
> +
> + /* Enable Left and Right DAC */
> + da7210_write(codec, DA7210_DAC_SEL,
> + DA7210_DAC_L_SRC_DAI_L | DA7210_DAC_L_EN |
> + DA7210_DAC_R_SRC_DAI_R | DA7210_DAC_R_EN);
> +
> + /* Enable Left and Right out PGA */
> + da7210_write(codec, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
> + da7210_write(codec, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
> +
> + /* Enable Left and Right HeadPhone PGA */
> + da7210_write(codec, DA7210_HP_CFG,
> + DA7210_HP_2CAP_MODE | DA7210_HP_SENSE_EN |
> + DA7210_HP_L_EN | DA7210_HP_MODE | DA7210_HP_R_EN);
> +
Are these all enabling codec functionality by powering on codec blocks.
If so, DAPM should take care of them.
> + /* set DAI source to Left and Right ADC */
> + da7210_write(codec, DA7210_DAI_SRC_SEL,
> + DA7210_DAI_OUT_R_SRC | DA7210_DAI_OUT_L_SRC);
> +
> + /* Enable DAI */
> + da7210_write(codec, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
> +
These should really be in hw_params().
> + /* Diable PLL and bypass it */
> + da7210_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
> +
> + /* Bypass PLL and set MCLK freq rang to 10-20MHz */
> + da7210_write(codec, DA7210_PLL_DIV3,
> + DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
> +
In set_clk()
> + /* Enable standbymode */
> + da7210_write(codec, DA7210_STARTUP2,
> + DA7210_LOUT1_L_STBY | DA7210_LOUT1_R_STBY |
> + DA7210_LOUT2_STBY | DA7210_HP_L_STBY |
> + DA7210_HP_R_STBY | DA7210_DAC_L_STBY | DA7210_DAC_R_STBY);
> + da7210_write(codec, DA7210_STARTUP3,
> + DA7210_LIN1_L_STBY | DA7210_LIN1_R_STBY |
> + DA7210_LIN2_STBY | DA7210_MIC_L_STBY |
> + DA7210_MIC_R_STBY | DA7210_ADC_L_STBY | DA7210_ADC_R_STBY);
> +
Some of these settings above are correct but in the wrong code function.
Have a look at the codec and alsa pcm ops for the correct places.
> + /* Activate all enabled subsystem */
> + da7210_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
> +
> + return ret;
> +}
> +
> +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
> +static int da7210_i2c_probe(struct i2c_client *i2c,
> + const struct i2c_device_id *id)
> +{
> + struct da7210_priv *da7210;
> + struct snd_soc_codec *codec;
> + int ret;
> +
> + da7210 = kzalloc(sizeof(struct da7210_priv), GFP_KERNEL);
> + if (!da7210)
> + return -ENOMEM;
> +
> + codec = &da7210->codec;
> + codec->dev = &i2c->dev;
> +
> + i2c_set_clientdata(i2c, da7210);
> + codec->control_data = i2c;
> +
> + ret = da7210_init(da7210);
> + if (ret < 0)
> + pr_err("Failed to initialise da7210 audio codec\n");
> +
> + return ret;
> +}
> +
> +static int da7210_i2c_remove(struct i2c_client *client)
> +{
> + struct da7210_priv *da7210 = i2c_get_clientdata(client);
> +
> + snd_soc_unregister_dai(&da7210_dai);
> + kfree(da7210->codec.reg_cache);
> + kfree(da7210);
> + da7210_codec = NULL;
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id da7210_i2c_id[] = {
> + { "da7210", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
> +
> +/* I2C codec control layer */
> +static struct i2c_driver da7210_i2c_driver = {
> + .driver = {
> + .name = "DA7210 I2C Codec",
> + .owner = THIS_MODULE,
> + },
> + .probe = da7210_i2c_probe,
> + .remove = __devexit_p(da7210_i2c_remove),
> + .id_table = da7210_i2c_id,
> +};
> +#endif
> +
> +static int da7210_probe(struct platform_device *pdev)
> +{
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> + struct snd_soc_codec *codec;
> + int ret;
> +
> + if (!da7210_codec) {
> + dev_err(&pdev->dev, "Codec device not registered\n");
> + return -ENODEV;
> + }
> +
> + socdev->card->codec = da7210_codec;
> + codec = da7210_codec;
> +
> + /* Register pcms */
> + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
> + if (ret < 0)
> + goto pcm_err;
> +
> + dev_info(&pdev->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
> +
> + /* Add the dapm controls */
> + snd_soc_add_controls(codec, da7210_snd_controls,
> + ARRAY_SIZE(da7210_snd_controls));
> + da7210_add_widgets(codec);
> +
> +pcm_err:
> + return ret;
> +}
> +
> +static int da7210_remove(struct platform_device *pdev)
> +{
> + struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> +
> + snd_soc_free_pcms(socdev);
> + snd_soc_dapm_free(socdev);
> +
> + return 0;
> +}
> +
> +struct snd_soc_codec_device soc_codec_dev_da7210 = {
> + .probe = da7210_probe,
> + .remove = da7210_remove,
> +};
> +EXPORT_SYMBOL_GPL(soc_codec_dev_da7210);
> +
> +static int __init da7210_modinit(void)
> +{
> + int ret;
> +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
> + ret = i2c_add_driver(&da7210_i2c_driver);
> +#endif
> + return ret;
> +}
> +module_init(da7210_modinit);
> +
> +static void __exit da7210_exit(void)
> +{
> +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
> + i2c_del_driver(&da7210_i2c_driver);
> +#endif
> +}
> +module_exit(da7210_exit);
> +
> +MODULE_DESCRIPTION("ASoC DA7210 driver");
> +MODULE_AUTHOR("David Chen, Kuninori Morimoto");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/codecs/da7210.h b/sound/soc/codecs/da7210.h
> new file mode 100644
> index 0000000..390d621
> --- /dev/null
> +++ b/sound/soc/codecs/da7210.h
> @@ -0,0 +1,24 @@
> +/*
> + * da7210.h -- audio driver for da7210
> + *
> + * Copyright (c) 2009 Dialog Semiconductor
> + * Written by David Chen <Dajun.chen at diasemi.com>
> + *
> + * Copyright (C) 2009 Renesas Solutions Corp.
> + * Cleanups by Kuninori Morimoto <morimoto.kuninori at renesas.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + */
> +
> +#ifndef _DA7210_H
> +#define _DA7210_H
> +
> +extern struct snd_soc_dai da7210_dai;
> +extern struct snd_soc_codec_device soc_codec_dev_da7210;
> +
> +#endif
> +
More information about the Alsa-devel
mailing list