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@xxxxxxxxxxx> > Signed-off-by: Kuninori Morimoto <morimoto.kuninori@xxxxxxxxxxx> 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@xxxxxxxxxxx> > + * > + * Copyright (C) 2009 Renesas Solutions Corp. > + * Cleanups by Kuninori Morimoto <morimoto.kuninori@xxxxxxxxxxx> > + * > + * 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@xxxxxxxxxxx> > + * > + * Copyright (C) 2009 Renesas Solutions Corp. > + * Cleanups by Kuninori Morimoto <morimoto.kuninori@xxxxxxxxxxx> > + * > + * 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 > + _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel