AM43xx-ePOS-EVM audio support with TLV320AIC31XX driver
cover AM43xx-ePOS-EVM audio support with TLV320AIC31XX driver
This patch set adds audio support for AM43xx-ePOS-EVM. I'll mail the dts and defconfig changes once these have been merged.
Jyri Sarha (5):
ASoC: tlv320aic31xx: Add basic codec driver implementation
ASoC: tlv320aic31xx: Add codec driver to Makefile and Kconfig
ASoC: tlv320aic31xx: Add DT binding document
ASoC: davinci-evm: Add AM43xx-EPOS-EVM audio support
ASoC: davinci: Add SND_AM43XX_SOC_EPOS_EVM build option
.../bindings/sound/davinci-evm-audio.txt | 9 +- .../devicetree/bindings/sound/tlv320aic31xx.txt | 57 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tlv320aic31xx.c | 1360 ++++++++++++++++++++ sound/soc/codecs/tlv320aic31xx.h | 265 ++++ sound/soc/davinci/Kconfig | 12 + sound/soc/davinci/Makefile | 1 + sound/soc/davinci/davinci-evm.c | 41 + 9 files changed, 1748 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/tlv320aic31xx.txt create mode 100644 sound/soc/codecs/tlv320aic31xx.c create mode 100644 sound/soc/codecs/tlv320aic31xx.h
Signature · 1 line
1.7.9.5
RFC PATCH 1/5 ASoC: tlv320aic31xx: Add basic codec driver implementation
This commit adds a bare bones driver support for TLV320AIC31XX family audio codecs. The driver adds basic stereo playback trough headphone and speaker outputs and mono capture trough microphone inputs.
The driver is currently missing support at least for mini DSP features and jack detection. I have tested the driver only on TLV320AIC3111, but based on the data sheets TLV320AIC3100, TLV320AIC3110, and TLV320AIC3120 should work Ok too.
The base for the implementation was taken from:
[email protected]:ti-codecs/ti-codecs.git ajitk/topics/k3.10.1-aic31xx -branch at commit 77504eba0294764e9e63b4a0c696b44db187cd13.
--- sound/soc/codecs/tlv320aic31xx.c | 1360 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tlv320aic31xx.h | 265 ++++++++ 2 files changed, 1625 insertions(+) create mode 100644 sound/soc/codecs/tlv320aic31xx.c create mode 100644 sound/soc/codecs/tlv320aic31xx.h
sound/soc/codecs/tlv320aic31xx.c new file +1360 -0
@@ -0,0 +1,1360 @@ +/* +* ALSA SoC TLV320AIC31XX codec driver +* +* Copyright (C) 2013 Texas Instruments, Inc. +* +* Author: Jyri Sarha <[email protected]> +* +* Based on ground work by: Ajit Kulkarni <[email protected]> +* +* This package 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. +* +* THIS PACKAGE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* +* The TLV320AIC31xx series of audio codec is a low-power, highly integrated +* high performance codec which provides a stereo DAC, a mono ADC, +* and mono/stereo Class-D speaker driver. +*/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <linux/of_gpio.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "tlv320aic31xx.h" + +static const struct reg_default aic31xx_reg_defaults[] = { + {AIC31XX_CLKMUX ,0x00 }, + {AIC31XX_PLLPR ,0x11 }, + {AIC31XX_PLLJ ,0x04 }, + {AIC31XX_PLLDMSB ,0x00 }, + {AIC31XX_PLLDLSB ,0x00 }, + {AIC31XX_NDAC ,0x01 }, + {AIC31XX_MDAC ,0x01 }, + {AIC31XX_DOSRMSB ,0x00 }, + {AIC31XX_DOSRLSB ,0x80 }, + {AIC31XX_NADC ,0x01 }, + {AIC31XX_MADC ,0x01 }, + {AIC31XX_AOSR ,0x80 }, + {AIC31XX_IFACE1 ,0x00 }, + {AIC31XX_DATA_OFFSET ,0x00 }, + {AIC31XX_IFACE2 ,0x00 }, + {AIC31XX_BCLKN ,0x01 }, + {AIC31XX_DACSETUP ,0x14 }, + {AIC31XX_DACMUTE ,0x0c }, + {AIC31XX_LDACVOL ,0x00 }, + {AIC31XX_RDACVOL ,0x00 }, + {AIC31XX_ADCSETUP ,0x00 }, + {AIC31XX_ADCFGA ,0x80 }, + {AIC31XX_ADCVOL ,0x00 }, + {AIC31XX_HPDRIVER ,0x04 }, + {AIC31XX_SPKAMP ,0x06 }, + {AIC31XX_DACMIXERROUTE ,0x00 }, + {AIC31XX_LANALOGHPL ,0x7f }, + {AIC31XX_RANALOGHPR ,0x7f }, + {AIC31XX_LANALOGSPL ,0x7f }, + {AIC31XX_RANALOGSPR ,0x7f }, + {AIC31XX_HPLGAIN ,0x02 }, + {AIC31XX_HPRGAIN ,0x02 }, + {AIC31XX_SPLGAIN ,0x00 }, + {AIC31XX_SPRGAIN ,0x00 }, + {AIC31XX_MICBIAS ,0x00 }, + {AIC31XX_MICPGA ,0x80 }, + {AIC31XX_MICPGAPI ,0x00 }, + {AIC31XX_MICPGAMI ,0x00 }, +}; + +static bool aic31xx_precious (struct device *dev,unsigned int reg) +{ +switch (reg) { +case AIC31XX_OFFLAG : +case AIC31XX_INTRDACFLAG : +case AIC31XX_INTRADCFLAG : +return true; + } +return false; +} + +static bool aic31xx_real_volatile (struct device *dev,unsigned int reg) +{ +switch (reg) { +case AIC31XX_OT_FLAG : +case AIC31XX_ADCFLAG : +case AIC31XX_DACFLAG1 : +case AIC31XX_DACFLAG2 : +case AIC31XX_INTRDACFLAG2 : +case AIC31XX_INTRADCFLAG2 : +return true; + } +return false; +} + +static bool aic31xx_volatile (struct device *dev,unsigned int reg) +{ +if (aic31xx_precious (dev, reg) ||aic31xx_real_volatile (dev, reg)) +return true; + +switch (reg) { +case AIC31XX_PAGECTL :/* regmap implementation requires this */ +case AIC31XX_RESET :/* always clears after write */ +return true; + } +return false; +} + +static bool aic31xx_writeable (struct device *dev,unsigned int reg) +{ +if (aic31xx_precious (dev, reg) ||aic31xx_real_volatile (dev, reg)) +return false; + +return true; +} + +static const struct regmap_range_cfg aic31xx_ranges[] = { + { + .name ="codec-regmap" , + .range_min =0 , + .range_max =12 *128 , + .selector_reg =AIC31XX_PAGECTL , + .selector_mask =0xff , + .selector_shift =0 , + .window_start =0 , + .window_len =128 , + }, +}; + +struct regmap_config aic31xx_i2c_regmap = { + .reg_bits =8 , + .val_bits =8 , + .writeable_reg = aic31xx_writeable, + .volatile_reg = aic31xx_volatile, + .precious_reg = aic31xx_precious, + .reg_defaults = aic31xx_reg_defaults, + .num_reg_defaults =ARRAY_SIZE (aic31xx_reg_defaults), + .cache_type =REGCACHE_RBTREE , + .ranges = aic31xx_ranges, + .num_ranges =ARRAY_SIZE (aic31xx_ranges), + .max_register =12 *128 , +}; + +#define AIC31XX_NUM_SUPPLIES 6 +static const char *const aic31xx_supply_names[] = { +"HPVDD" , +"SPRVDD" , +"SPLVDD" , +"AVDD" , +"IOVDD" , +"DVDD" , +}; + +struct aic31xx_disable_nb { +struct notifier_block nb; +struct aic31xx_priv *aic31xx; +}; + +struct aic31xx_priv { +struct snd_soc_codec *codec; +u8 i2c_regs_status; +struct device *dev; +struct regmap *regmap; +struct aic31xx_pdata pdata; +struct regulator_bulk_data supplies[AIC31XX_NUM_SUPPLIES ]; +struct aic31xx_disable_nb disable_nb[AIC31XX_NUM_SUPPLIES ]; +int power; +unsigned int sysclk; +int rate_div_line; +int codec_clk_on; +}; + +struct aic31xx_rate_divs { +u32 mclk; +u32 rate; +u8 p_val; +u8 pll_j; +u16 pll_d; +u16 dosr; +u8 ndac; +u8 mdac; +u8 aosr; +u8 nadc; +u8 madc; +}; + +/* ADC dividers can be disabled by cofiguring them to 0 */ +static const struct aic31xx_rate_divs aic31xx_divs[] = { +/* mclk rate pll: p j d dosr ndac mdac aors nadc madc */ +/* 8k rate */ + {12000000 ,8000 ,1 ,8 ,1920 ,128 ,48 ,2 ,128 ,48 ,2 }, + {24000000 ,8000 ,2 ,8 ,1920 ,128 ,48 ,2 ,128 ,48 ,2 }, + {25000000 ,8000 ,2 ,7 ,8643 ,128 ,48 ,2 ,128 ,48 ,2 }, +/* 11.025k rate */ + {12000000 ,11025 ,1 ,7 ,5264 ,128 ,32 ,2 ,128 ,32 ,2 }, + {24000000 ,11025 ,2 ,7 ,5264 ,128 ,32 ,2 ,128 ,32 ,2 }, + {25000000 ,11025 ,2 ,7 ,2253 ,128 ,32 ,2 ,128 ,32 ,2 }, +/* 16k rate */ + {12000000 ,16000 ,1 ,8 ,1920 ,128 ,24 ,2 ,128 ,24 ,2 }, + {24000000 ,16000 ,2 ,8 ,1920 ,128 ,24 ,2 ,128 ,24 ,2 }, + {25000000 ,16000 ,2 ,7 ,8643 ,128 ,24 ,2 ,128 ,24 ,2 }, +/* 22.05k rate */ + {12000000 ,22050 ,1 ,7 ,5264 ,128 ,16 ,2 ,128 ,16 ,2 }, + {24000000 ,22050 ,2 ,7 ,5264 ,128 ,16 ,2 ,128 ,16 ,2 }, + {25000000 ,22050 ,2 ,7 ,2253 ,128 ,16 ,2 ,128 ,16 ,2 }, +/* 32k rate */ + {12000000 ,32000 ,1 ,8 ,1920 ,128 ,12 ,2 ,128 ,12 ,2 }, + {24000000 ,32000 ,2 ,8 ,1920 ,128 ,12 ,2 ,128 ,12 ,2 }, + {25000000 ,32000 ,2 ,7 ,8643 ,128 ,12 ,2 ,128 ,12 ,2 }, +/* 44.1k rate */ + {12000000 ,44100 ,1 ,7 ,5264 ,128 ,8 ,2 ,128 ,8 ,2 }, + {24000000 ,44100 ,2 ,7 ,5264 ,128 ,8 ,2 ,128 ,8 ,2 }, + {25000000 ,44100 ,2 ,7 ,2253 ,128 ,8 ,2 ,128 ,8 ,2 }, +/* 48k rate */ + {12000000 ,48000 ,1 ,8 ,1920 ,128 ,8 ,2 ,128 ,8 ,2 }, + {24000000 ,48000 ,2 ,8 ,1920 ,128 ,8 ,2 ,128 ,8 ,2 }, + {25000000 ,48000 ,2 ,7 ,8643 ,128 ,8 ,2 ,128 ,8 ,2 }, +/* 88.2k rate */ + {12000000 ,88200 ,1 ,7 ,5264 ,64 ,8 ,2 ,64 ,8 ,2 }, + {24000000 ,88200 ,2 ,7 ,5264 ,64 ,8 ,2 ,64 ,8 ,2 }, + {25000000 ,88200 ,2 ,7 ,2253 ,64 ,8 ,2 ,64 ,8 ,2 }, +/* 96k rate */ + {12000000 ,96000 ,1 ,8 ,1920 ,64 ,8 ,2 ,64 ,8 ,2 }, + {24000000 ,96000 ,2 ,8 ,1920 ,64 ,8 ,2 ,64 ,8 ,2 }, + {25000000 ,96000 ,2 ,7 ,8643 ,64 ,8 ,2 ,64 ,8 ,2 }, +/* 176.4k rate */ + {12000000 ,176400 ,1 ,7 ,5264 ,32 ,8 ,2 ,32 ,8 ,2 }, + {24000000 ,176400 ,2 ,7 ,5264 ,32 ,8 ,2 ,32 ,8 ,2 }, + {25000000 ,176400 ,2 ,7 ,2253 ,32 ,8 ,2 ,32 ,8 ,2 }, +/* 192k rate */ + {12000000 ,192000 ,1 ,8 ,1920 ,32 ,8 ,2 ,32 ,8 ,2 }, + {24000000 ,192000 ,2 ,8 ,1920 ,32 ,8 ,2 ,32 ,8 ,2 }, + {25000000 ,192000 ,2 ,7 ,8643 ,32 ,8 ,2 ,32 ,8 ,2 }, +}; + +static const char *const ldac_in_text[] = { +"off" ,"Left Data" ,"Right Data" ,"Mono" +}; + +static const char *const rdac_in_text[] = { +"off" ,"Right Data" ,"Left Data" ,"Mono" +}; + +static SOC_ENUM_SINGLE_DECL(ldac_in_enum, AIC31XX_DACSETUP, 4, ldac_in_text); + +static SOC_ENUM_SINGLE_DECL(rdac_in_enum, AIC31XX_DACSETUP, 2, rdac_in_text); + +static const char *const mic_select_text[] = { +"off" ,"FFR 10 Ohm" ,"FFR 20 Ohm" ,"FFR 40 Ohm" +}; + +static const +SOC_ENUM_SINGLE_DECL(mic1lp_p_enum, AIC31XX_MICPGAPI, 6, mic_select_text); +static const +SOC_ENUM_SINGLE_DECL(mic1rp_p_enum, AIC31XX_MICPGAPI, 4, mic_select_text); +static const +SOC_ENUM_SINGLE_DECL(mic1lm_p_enum, AIC31XX_MICPGAPI, 2, mic_select_text); + +static const +SOC_ENUM_SINGLE_DECL(cm_m_enum, AIC31XX_MICPGAMI, 6, mic_select_text); +static const +SOC_ENUM_SINGLE_DECL(mic1lm_m_enum, AIC31XX_MICPGAMI, 4, mic_select_text); + +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_fgain_tlv, 0, 10, 0); +static const DECLARE_TLV_DB_SCALE(adc_cgain_tlv, -2000, 50, 0); +static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_SCALE(hp_drv_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(class_D_drv_tlv, 600, 600, 0); +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -6350, 50, 0); +static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0); + +/* +* controls to be exported to the user space +*/ +static const struct snd_kcontrol_new aic31xx_snd_controls[] = { +SOC_DOUBLE_R_SX_TLV ("DAC Playback Volume" ,AIC31XX_LDACVOL , +AIC31XX_RDACVOL ,0 ,0x81 ,0xaf , dac_vol_tlv), + +SOC_SINGLE_TLV ("ADC Fine Capture Volume" ,AIC31XX_ADCFGA ,4 ,4 ,1 , + adc_fgain_tlv), + +SOC_SINGLE ("ADC Capture Switch" ,AIC31XX_ADCFGA ,7 ,1 ,1 ), + +/* ADC COARSE GAIN */ +/* Writes to AIC31XX_ADCVOL should be masked with 0x7F, but there +appears to be no side effect if writes are no masked. */ +SOC_SINGLE_SX_TLV ("ADC Capture Volume" ,AIC31XX_ADCVOL , +0 ,0x68 ,0x40 , adc_cgain_tlv), + +SOC_SINGLE_TLV ("Mic PGA Capture Volume" ,AIC31XX_MICPGA ,0 , +119 ,0 , mic_pga_tlv), + +SOC_DOUBLE_R ("HP Driver Playback Switch" ,AIC31XX_HPLGAIN , +AIC31XX_HPRGAIN ,2 ,1 ,0 ), +SOC_DOUBLE_R_TLV ("HP Driver Playback Volume" ,AIC31XX_HPLGAIN , +AIC31XX_HPRGAIN ,3 ,0x09 ,0 , hp_drv_tlv), + +SOC_DOUBLE_R_TLV ("HP Analog Playback Volume" ,AIC31XX_LANALOGHPL , +AIC31XX_RANALOGHPR ,0 ,0x7F ,1 , hp_vol_tlv), +}; + +static const struct snd_kcontrol_new aic311x_snd_controls[] = { +SOC_DOUBLE_R ("SP Driver Playback Switch" ,AIC31XX_SPLGAIN , +AIC31XX_SPRGAIN ,2 ,1 ,0 ), +SOC_DOUBLE_R_TLV ("SP Driver Playback Volume" ,AIC31XX_SPLGAIN , +AIC31XX_SPRGAIN ,3 ,3 ,0 , class_D_drv_tlv), + +SOC_DOUBLE_R_TLV ("SP Analog Playback Volume" ,AIC31XX_LANALOGSPL , +AIC31XX_RANALOGSPR ,0 ,0x7F ,1 , sp_vol_tlv), +}; + +static const struct snd_kcontrol_new aic310x_snd_controls[] = { +SOC_SINGLE ("SP Driver Playback Switch" ,AIC31XX_SPLGAIN , +2 ,1 ,0 ), +SOC_SINGLE_TLV ("SP Driver Playback Volume" ,AIC31XX_SPLGAIN , +3 ,3 ,0 , class_D_drv_tlv), + +SOC_SINGLE_TLV ("SP Analog Playback Volume" ,AIC31XX_LANALOGSPL , +0 ,0x7F ,1 , sp_vol_tlv), +}; + +static const struct snd_kcontrol_new ldac_in_control = +SOC_DAPM_ENUM ("DAC Left Input" , ldac_in_enum); + +static const struct snd_kcontrol_new rdac_in_control = +SOC_DAPM_ENUM ("DAC Right Input" , rdac_in_enum); + +int aic31xx_wait_bits (struct aic31xx_priv *aic31xx,unsigned int reg, +unsigned int mask,unsigned int wbits,int sleep, +int count) +{ +unsigned int bits; +int counter = count; +int ret =regmap_read (aic31xx->regmap, reg, &bits); +while ((bits & mask) != wbits && counter && !ret) { +usleep_range (sleep, sleep *2 ); + ret =regmap_read (aic31xx->regmap, reg, &bits); + counter--; + } +if ((bits & mask) != wbits) { +dev_err (aic31xx->dev, +"%s: Failed! 0x%x was 0x%x expected 0x%x (%d, 0x%x, %d us)\n" , + __func__, reg, bits, wbits, ret, mask, + (count - counter) * sleep); + ret =-1 ; + } +return ret; +} + +static int aic31xx_dapm_power_event (struct snd_soc_dapm_widget *w, +struct snd_kcontrol *kcontrol,int event) +{ +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (w->codec); +unsigned int reg =AIC31XX_DACFLAG1 ; +unsigned int mask; + +dev_dbg (w->codec->dev,"%s -> %s (event: %d)\n" , w->name, + event ==SND_SOC_DAPM_POST_PMU ?"on" : + event ==SND_SOC_DAPM_POST_PMD ?"off" : +"(unsupported event)" , event); + +if (!strcmp (w->name,"DAC Left" )) { + mask =AIC31XX_LDACPWRSTATUS_MASK ; + }else if (!strcmp (w->name,"DAC Right" )) { + mask =AIC31XX_RDACPWRSTATUS_MASK ; + }else if (!strcmp (w->name,"HPL Driver" )) { + mask =AIC31XX_HPLDRVPWRSTATUS_MASK ; + }else if (!strcmp (w->name,"HPR Driver" )) { + mask =AIC31XX_HPRDRVPWRSTATUS_MASK ; + }else if (!strcmp (w->name,"SPL ClassD" ) || + !strcmp (w->name,"SPK ClassD" )) { + mask =AIC31XX_SPLDRVPWRSTATUS_MASK ; + }else if (!strcmp (w->name,"SPR ClassD" )) { + mask =AIC31XX_SPRDRVPWRSTATUS_MASK ; + }else if (!strcmp (w->name,"ADC" )) { + mask =AIC31XX_ADCPWRSTATUS_MASK ; + reg =AIC31XX_ADCFLAG ; + }else { +dev_err (w->codec->dev,"Unknown widget '%s' calling %s/n" , + w->name, __func__); +return -1 ; + } + +if (event ==SND_SOC_DAPM_POST_PMU ) +return aic31xx_wait_bits (aic31xx, reg, mask, mask,5000 ,100 ); +else if (event ==SND_SOC_DAPM_POST_PMD ) +return aic31xx_wait_bits (aic31xx, reg, mask,0 ,5000 ,100 ); + +dev_dbg (w->codec->dev,"Unhandled dapm widget event %d from %s\n" , + event, w->name); +return 0 ; +} + +static const struct snd_kcontrol_new left_output_switches[] = { +SOC_DAPM_SINGLE ("From Left DAC" ,AIC31XX_DACMIXERROUTE ,6 ,1 ,0 ), +SOC_DAPM_SINGLE ("From MIC1LP" ,AIC31XX_DACMIXERROUTE ,5 ,1 ,0 ), +SOC_DAPM_SINGLE ("From MIC1RP" ,AIC31XX_DACMIXERROUTE ,4 ,1 ,0 ), +}; + +static const struct snd_kcontrol_new right_output_switches[] = { +SOC_DAPM_SINGLE ("From Right DAC" ,AIC31XX_DACMIXERROUTE ,2 ,1 ,0 ), +SOC_DAPM_SINGLE ("From MIC1RP" ,AIC31XX_DACMIXERROUTE ,1 ,1 ,0 ), +}; + +static const struct snd_kcontrol_new p_term_mic1lp = +SOC_DAPM_ENUM ("MIC1LP P-Terminal" , mic1lp_p_enum); + +static const struct snd_kcontrol_new p_term_mic1rp = +SOC_DAPM_ENUM ("MIC1RP P-Terminal" , mic1rp_p_enum); + +static const struct snd_kcontrol_new p_term_mic1lm = +SOC_DAPM_ENUM ("MIC1LM P-Terminal" , mic1lm_p_enum); + +static const struct snd_kcontrol_new m_term_mic1lm = +SOC_DAPM_ENUM ("MIC1LM M-Terminal" , mic1lm_m_enum); + +static const struct snd_kcontrol_new aic31xx_dapm_hpl_switch = +SOC_DAPM_SINGLE ("Switch" ,AIC31XX_LANALOGHPL ,7 ,1 ,0 ); + +static const struct snd_kcontrol_new aic31xx_dapm_hpr_switch = +SOC_DAPM_SINGLE ("Switch" ,AIC31XX_RANALOGHPR ,7 ,1 ,0 ); + +static const struct snd_kcontrol_new aic31xx_dapm_spl_switch = +SOC_DAPM_SINGLE ("Switch" ,AIC31XX_LANALOGSPL ,7 ,1 ,0 ); + +static const struct snd_kcontrol_new aic31xx_dapm_spr_switch = +SOC_DAPM_SINGLE ("Switch" ,AIC31XX_RANALOGSPR ,7 ,1 ,0 ); + +static int aic31xx_clk_pm_event (struct snd_soc_dapm_widget *w, +struct snd_kcontrol *kcontrol,int event) +{ +struct snd_soc_codec *codec = w->codec; +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +u8 mask =AIC31XX_PM_MASK ; +u8 on =AIC31XX_PM_MASK ; +u8 off =0 ; + +if (SND_SOC_DAPM_EVENT_ON (event)) { +dev_dbg (codec->dev,"codec clock -> on (rate %d)\n" , + aic31xx_divs[aic31xx->rate_div_line].rate); +snd_soc_update_bits (codec,AIC31XX_PLLPR , mask, on); +mdelay (10 ); +snd_soc_update_bits (codec,AIC31XX_NDAC , mask, on); +snd_soc_update_bits (codec,AIC31XX_MDAC , mask, on); +if (aic31xx_divs[aic31xx->rate_div_line].nadc) +snd_soc_update_bits (codec,AIC31XX_NADC , mask, on); +if (aic31xx_divs[aic31xx->rate_div_line].madc) +snd_soc_update_bits (codec,AIC31XX_MADC , mask, on); +snd_soc_update_bits (codec,AIC31XX_BCLKN , mask, on); + aic31xx->codec_clk_on =1 ; + }else if (SND_SOC_DAPM_EVENT_OFF (event)) { +dev_dbg (codec->dev,"codec clock -> off\n" ); +snd_soc_update_bits (codec,AIC31XX_BCLKN , mask, off); +snd_soc_update_bits (codec,AIC31XX_MADC , mask, off); +snd_soc_update_bits (codec,AIC31XX_NADC , mask, off); +snd_soc_update_bits (codec,AIC31XX_MDAC , mask, off); +snd_soc_update_bits (codec,AIC31XX_NDAC , mask, off); +snd_soc_update_bits (codec,AIC31XX_PLLPR , mask, off); + aic31xx->codec_clk_on =0 ; + } +return 0 ; +} + +static int mic_bias_event (struct snd_soc_dapm_widget *w, +struct snd_kcontrol *kcontrol,int event) +{ +struct snd_soc_codec *codec = w->codec; +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +switch (event) { +case SND_SOC_DAPM_POST_PMU : +/* change mic bias voltage to user defined */ +if (aic31xx->pdata.micbias_vg !=AIC31XX_MICBIAS_OFF ) { +snd_soc_update_bits (codec,AIC31XX_MICBIAS , +AIC31XX_MICBIAS_MASK , + aic31xx->pdata.micbias_vg << +AIC31XX_MICBIAS_SHIFT ); +dev_dbg (codec->dev,"%s: turned on\n" , __func__); + } +break ; +case SND_SOC_DAPM_PRE_PMD : +if (aic31xx->pdata.micbias_vg !=AIC31XX_MICBIAS_OFF ) { +snd_soc_update_bits (codec,AIC31XX_MICBIAS , +AIC31XX_MICBIAS_MASK ,0 ); +dev_dbg (codec->dev,"%s: turned off\n" , __func__); + } +break ; + } +return 0 ; +} + +static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = { +SND_SOC_DAPM_AIF_IN ("DAC IN" ,"DAC Playback" ,0 ,SND_SOC_NOPM ,0 ,0 ), + +SND_SOC_DAPM_MUX ("DAC Left Input" , +SND_SOC_NOPM ,0 ,0 , &ldac_in_control), +SND_SOC_DAPM_MUX ("DAC Right Input" , +SND_SOC_NOPM ,0 ,0 , &rdac_in_control), +/* DACs */ +SND_SOC_DAPM_DAC_E ("DAC Left" ,"DAC Left Input" , +AIC31XX_DACSETUP ,7 ,0 , aic31xx_dapm_power_event, +SND_SOC_DAPM_POST_PMU |SND_SOC_DAPM_POST_PMD ), + +SND_SOC_DAPM_DAC_E ("DAC Right" ,"DAC Right Input" , +AIC31XX_DACSETUP ,6 ,0 , aic31xx_dapm_power_event, +SND_SOC_DAPM_POST_PMU |SND_SOC_DAPM_POST_PMD ), + +/* Output Mixers */ +SND_SOC_DAPM_MIXER ("Output Left" ,SND_SOC_NOPM ,0 ,0 , + left_output_switches, +ARRAY_SIZE (left_output_switches)), +SND_SOC_DAPM_MIXER ("Output Right" ,SND_SOC_NOPM ,0 ,0 , + right_output_switches, +ARRAY_SIZE (right_output_switches)), + +SND_SOC_DAPM_SWITCH ("HP Left" ,SND_SOC_NOPM ,0 ,0 , + &aic31xx_dapm_hpl_switch), +SND_SOC_DAPM_SWITCH ("HP Right" ,SND_SOC_NOPM ,0 ,0 , + &aic31xx_dapm_hpr_switch), + +/* Output drivers */ +SND_SOC_DAPM_OUT_DRV_E ("HPL Driver" ,AIC31XX_HPDRIVER ,7 ,0 , +NULL ,0 , aic31xx_dapm_power_event, +SND_SOC_DAPM_POST_PMD |SND_SOC_DAPM_POST_PMU ), +SND_SOC_DAPM_OUT_DRV_E ("HPR Driver" ,AIC31XX_HPDRIVER ,6 ,0 , +NULL ,0 , aic31xx_dapm_power_event, +SND_SOC_DAPM_POST_PMD |SND_SOC_DAPM_POST_PMU ), + +/* ADC */ +SND_SOC_DAPM_ADC_E ("ADC" ,"Capture" ,AIC31XX_ADCSETUP ,7 ,0 , + aic31xx_dapm_power_event,SND_SOC_DAPM_POST_PMU | +SND_SOC_DAPM_POST_PMD ), + +/* Input Selection to MIC_PGA */ +SND_SOC_DAPM_MUX ("MIC1LP P-Terminal" ,SND_SOC_NOPM ,0 ,0 , + &p_term_mic1lp), +SND_SOC_DAPM_MUX ("MIC1RP P-Terminal" ,SND_SOC_NOPM ,0 ,0 , + &p_term_mic1rp), +SND_SOC_DAPM_MUX ("MIC1LM P-Terminal" ,SND_SOC_NOPM ,0 ,0 , + &p_term_mic1lm), + +SND_SOC_DAPM_MUX ("MIC1LM M-Terminal" ,SND_SOC_NOPM ,0 ,0 , + &m_term_mic1lm), +/* Enabling & Disabling MIC Gain Ctl */ +SND_SOC_DAPM_PGA ("MIC_GAIN_CTL" ,AIC31XX_MICPGA , +7 ,1 ,NULL ,0 ), + +SND_SOC_DAPM_SUPPLY ("CODEC_CLK_IN" ,SND_SOC_NOPM ,0 ,0 , + aic31xx_clk_pm_event, +SND_SOC_DAPM_PRE_PMU |SND_SOC_DAPM_POST_PMD ), + +/* Mic Bias */ +SND_SOC_DAPM_SUPPLY ("Mic Bias" ,SND_SOC_NOPM ,0 ,0 , mic_bias_event, +SND_SOC_DAPM_POST_PMU |SND_SOC_DAPM_PRE_PMD ), + +/* Outputs */ +SND_SOC_DAPM_OUTPUT ("HPL" ), +SND_SOC_DAPM_OUTPUT ("HPR" ), + +/* Inputs */ +SND_SOC_DAPM_INPUT ("MIC1LP" ), +SND_SOC_DAPM_INPUT ("MIC1RP" ), +SND_SOC_DAPM_INPUT ("MIC1LM" ), +}; + +static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = { +/* AIC3111 and AIC3110 have stereo class-D amplifier */ +SND_SOC_DAPM_OUT_DRV_E ("SPL ClassD" ,AIC31XX_SPKAMP ,7 ,0 ,NULL ,0 , + aic31xx_dapm_power_event,SND_SOC_DAPM_POST_PMU | +SND_SOC_DAPM_POST_PMD ), +SND_SOC_DAPM_OUT_DRV_E ("SPR ClassD" ,AIC31XX_SPKAMP ,6 ,0 ,NULL ,0 , + aic31xx_dapm_power_event,SND_SOC_DAPM_POST_PMU | +SND_SOC_DAPM_POST_PMD ), +SND_SOC_DAPM_SWITCH ("SP Left" ,SND_SOC_NOPM ,0 ,0 , + &aic31xx_dapm_spl_switch), +SND_SOC_DAPM_SWITCH ("SP Right" ,SND_SOC_NOPM ,0 ,0 , + &aic31xx_dapm_spr_switch), +SND_SOC_DAPM_OUTPUT ("SPL" ), +SND_SOC_DAPM_OUTPUT ("SPR" ), +}; + +/* AIC3100 and AIC3120 have only mono class-D amplifier */ +static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = { +SND_SOC_DAPM_OUT_DRV_E ("SPK ClassD" ,AIC31XX_SPKAMP ,7 ,0 ,NULL ,0 , + aic31xx_dapm_power_event,SND_SOC_DAPM_POST_PMU | +SND_SOC_DAPM_POST_PMD ), +SND_SOC_DAPM_SWITCH ("Speaker" ,SND_SOC_NOPM ,0 ,0 , + &aic31xx_dapm_spl_switch), +SND_SOC_DAPM_OUTPUT ("SPK" ), +}; + +static const struct snd_soc_dapm_route +aic31xx_audio_map[] = { +/* Clocks for ADC */ + {"ADC" ,NULL ,"CODEC_CLK_IN" }, + +/* DAC Input Routing */ + {"DAC Left Input" ,"Left Data" ,"DAC IN" }, + {"DAC Left Input" ,"Right Data" ,"DAC IN" }, + {"DAC Left Input" ,"Mono" ,"DAC IN" }, + {"DAC Right Input" ,"Left Data" ,"DAC IN" }, + {"DAC Right Input" ,"Right Data" ,"DAC IN" }, + {"DAC Right Input" ,"Mono" ,"DAC IN" }, + {"DAC Left" ,NULL ,"DAC Left Input" }, + {"DAC Right" ,NULL ,"DAC Right Input" }, + +/* Mic input */ + {"MIC1LP P-Terminal" ,"FFR 10 Ohm" ,"MIC1LP" }, + {"MIC1LP P-Terminal" ,"FFR 20 Ohm" ,"MIC1LP" }, + {"MIC1LP P-Terminal" ,"FFR 40 Ohm" ,"MIC1LP" }, + {"MIC1RP P-Terminal" ,"FFR 10 Ohm" ,"MIC1RP" }, + {"MIC1RP P-Terminal" ,"FFR 20 Ohm" ,"MIC1RP" }, + {"MIC1RP P-Terminal" ,"FFR 40 Ohm" ,"MIC1RP" }, + {"MIC1LM P-Terminal" ,"FFR 10 Ohm" ,"MIC1LM" }, + {"MIC1LM P-Terminal" ,"FFR 20 Ohm" ,"MIC1LM" }, + {"MIC1LM P-Terminal" ,"FFR 40 Ohm" ,"MIC1LM" }, + + {"MIC1LM M-Terminal" ,"FFR 10 Ohm" ,"MIC1LM" }, + {"MIC1LM M-Terminal" ,"FFR 20 Ohm" ,"MIC1LM" }, + {"MIC1LM M-Terminal" ,"FFR 40 Ohm" ,"MIC1LM" }, + + {"MIC_GAIN_CTL" ,NULL ,"MIC1LP P-Terminal" }, + {"MIC_GAIN_CTL" ,NULL ,"MIC1RP P-Terminal" }, + {"MIC_GAIN_CTL" ,NULL ,"MIC1LM P-Terminal" }, + {"MIC_GAIN_CTL" ,NULL ,"MIC1LM M-Terminal" }, + + {"ADC" ,NULL ,"MIC_GAIN_CTL" }, + {"MIC_GAIN_CTL" ,NULL ,"Mic Bias" }, + +/* Clocks for DAC */ + {"DAC Left" ,NULL ,"CODEC_CLK_IN" }, + {"DAC Right" ,NULL ,"CODEC_CLK_IN" }, + +/* Left Output */ + {"Output Left" ,"From Left DAC" ,"DAC Left" }, + {"Output Left" ,"From MIC1LP" ,"MIC1LP" }, + {"Output Left" ,"From MIC1RP" ,"MIC1RP" }, + +/* Right Output */ + {"Output Right" ,"From Right DAC" ,"DAC Right" }, + {"Output Right" ,"From MIC1RP" ,"MIC1RP" }, + +/* HPL path */ + {"HP Left" ,"Switch" ,"Output Left" }, + {"HPL Driver" ,NULL ,"HP Left" }, + {"HPL" ,NULL ,"HPL Driver" }, + +/* HPR path */ + {"HP Right" ,"Switch" ,"Output Right" }, + {"HPR Driver" ,NULL ,"HP Right" }, + {"HPR" ,NULL ,"HPR Driver" }, +}; + +static const struct snd_soc_dapm_route +aic311x_audio_map[] = { +/* SP L path */ + {"SP Left" ,"Switch" ,"Output Left" }, + {"SPL ClassD" ,NULL ,"SP Left" }, + {"SPL" ,NULL ,"SPL ClassD" }, + +/* SP R path */ + {"SP Right" ,"Switch" ,"Output Right" }, + {"SPR ClassD" ,NULL ,"SP Right" }, + {"SPR" ,NULL ,"SPR ClassD" }, +}; + +static const struct snd_soc_dapm_route +aic310x_audio_map[] = { +/* SP L path */ + {"Speaker" ,"Switch" ,"Output Left" }, + {"SPK ClassD" ,NULL ,"Speaker" }, + {"SPK" ,NULL ,"SPK ClassD" }, +}; + +static int aic31xx_add_controls (struct snd_soc_codec *codec) +{ +int err =0 ; +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); + +if (aic31xx->pdata.codec_type &AIC31XX_STEREO_CLASS_D_BIT ) { + err =snd_soc_add_codec_controls ( + codec, aic311x_snd_controls, +ARRAY_SIZE (aic311x_snd_controls)); +if (err <0 ) +dev_dbg (codec->dev,"Invalid control\n" ); + + }else { + err =snd_soc_add_codec_controls ( + codec, aic310x_snd_controls, +ARRAY_SIZE (aic310x_snd_controls)); +if (err <0 ) +dev_dbg (codec->dev,"Invalid Control\n" ); + } +return 0 ; +} + +static int aic31xx_add_widgets (struct snd_soc_codec *codec) +{ +struct snd_soc_dapm_context *dapm = &codec->dapm; +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +int ret =0 ; + +if (aic31xx->pdata.codec_type &AIC31XX_STEREO_CLASS_D_BIT ) { + ret =snd_soc_dapm_new_controls ( + dapm, aic311x_dapm_widgets, +ARRAY_SIZE (aic311x_dapm_widgets)); +if (ret) +dev_err (codec->dev, +"Adding %d dapm widgets failed: %d\n" , +ARRAY_SIZE (aic311x_dapm_widgets), ret); + ret =snd_soc_dapm_add_routes (dapm, aic311x_audio_map, +ARRAY_SIZE (aic311x_audio_map)); +if (ret) +dev_err (codec->dev, +"Adding %d DAPM routes failed: %d\n" , +ARRAY_SIZE (aic311x_audio_map), ret); + }else { + ret =snd_soc_dapm_new_controls ( + dapm, aic310x_dapm_widgets, +ARRAY_SIZE (aic310x_dapm_widgets)); +if (ret) +dev_err (codec->dev, +"Adding %d dapm widgets failed: %d\n" , +ARRAY_SIZE (aic310x_dapm_widgets), ret); + ret =snd_soc_dapm_add_routes (dapm, aic310x_audio_map, +ARRAY_SIZE (aic310x_audio_map)); +if (ret) +dev_err (codec->dev, +"Adding %d DAPM routes failed: %d\n" , +ARRAY_SIZE (aic310x_audio_map), ret); + } + +return 0 ; +} + +static int aic31xx_setup_pll (struct snd_soc_codec *codec, +struct snd_pcm_hw_params *params) +{ +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +int bclk_n =0 ; +int i; + +/* Use PLL as CODEC_CLKIN and DAC_CLK as BDIV_CLKIN */ +snd_soc_update_bits (codec,AIC31XX_CLKMUX , +AIC31XX_CODEC_CLKIN_MASK ,AIC31XX_CODEC_CLKIN_PLL ); +snd_soc_update_bits (codec,AIC31XX_IFACE2 , +AIC31XX_BDIVCLK_MASK ,AIC31XX_DAC2BCLK ); + +for (i =0 ; i <ARRAY_SIZE (aic31xx_divs); i++) { +if (aic31xx_divs[i].rate ==params_rate (params) && + aic31xx_divs[i].mclk == aic31xx->sysclk) +break ; + } + +if (i ==ARRAY_SIZE (aic31xx_divs)) { +dev_err (codec->dev,"%s: Sampling rate %u not supported\n" , + __func__,params_rate (params)); +return -EINVAL ; + } + +if (aic31xx->codec_clk_on) { +int old = aic31xx->rate_div_line; +if (aic31xx_divs[i].pll_j != aic31xx_divs[old].pll_j || + aic31xx_divs[i].pll_d != aic31xx_divs[old].pll_d || + aic31xx_divs[i].p_val != aic31xx_divs[old].p_val) { +dev_err (codec->dev, +"%s: Can't reconfigure PLL while it is on.\n" , + __func__); +return -EBUSY ; + } + }else { +/* PLL configuration */ +snd_soc_update_bits (codec,AIC31XX_PLLPR ,AIC31XX_PLL_MASK , + (aic31xx_divs[i].p_val <<4 ) |0x01 ); +snd_soc_write (codec,AIC31XX_PLLJ , aic31xx_divs[i].pll_j); + +snd_soc_write (codec,AIC31XX_PLLDMSB , + aic31xx_divs[i].pll_d >>8 ); +snd_soc_write (codec,AIC31XX_PLLDLSB , + aic31xx_divs[i].pll_d &0xff ); + } + +/* DAC dividers configuration */ +snd_soc_update_bits (codec,AIC31XX_NDAC ,AIC31XX_PLL_MASK , + aic31xx_divs[i].ndac); +snd_soc_update_bits (codec,AIC31XX_MDAC ,AIC31XX_PLL_MASK , + aic31xx_divs[i].mdac); + +snd_soc_write (codec,AIC31XX_DOSRMSB , aic31xx_divs[i].dosr >>8 ); +snd_soc_write (codec,AIC31XX_DOSRLSB , aic31xx_divs[i].dosr &0xff ); + +/* ADC dividers configuration. Write reset value 1 if not used. */ +snd_soc_update_bits (codec,AIC31XX_NADC ,AIC31XX_PLL_MASK , + aic31xx_divs[i].nadc ? aic31xx_divs[i].nadc :1 ); +snd_soc_update_bits (codec,AIC31XX_MADC ,AIC31XX_PLL_MASK , + aic31xx_divs[i].madc ? aic31xx_divs[i].madc :1 ); + +snd_soc_write (codec,AIC31XX_AOSR , aic31xx_divs[i].aosr); + +/* Bit clock divider configuration. */ + bclk_n = (aic31xx_divs[i].dosr * aic31xx_divs[i].mdac) + /snd_soc_params_to_frame_size (params); +if (bclk_n ==0 ) { +dev_err (codec->dev,"%s: Not enough BLCK bandwidth\n" , + __func__); +return -EINVAL ; + } + +snd_soc_update_bits (codec,AIC31XX_BCLKN , +AIC31XX_PLL_MASK , bclk_n); + + aic31xx->rate_div_line = i; + +dev_dbg (codec->dev, +"pll %d.%04d/%d dosr %d n %d m %d aosr %d n %d m %d bclk_n %d\n" , + aic31xx_divs[i].pll_j, aic31xx_divs[i].pll_d, + aic31xx_divs[i].p_val, aic31xx_divs[i].dosr, + aic31xx_divs[i].ndac, aic31xx_divs[i].mdac, + aic31xx_divs[i].aosr, aic31xx_divs[i].nadc, + aic31xx_divs[i].madc, bclk_n); + +return 0 ; +} + +static int aic31xx_hw_params (struct snd_pcm_substream *substream, +struct snd_pcm_hw_params *params, +struct snd_soc_dai *tmp) +{ +struct snd_soc_pcm_runtime *rtd = substream->private_data; +struct snd_soc_codec *codec = rtd->codec; +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +u8 data =0 ; + +dev_dbg (codec->dev,"## %s: format %d rate %d\n" , + __func__,params_format (params),params_rate (params)); + +switch (params_format (params)) { +case SNDRV_PCM_FORMAT_S16_LE : +break ; +case SNDRV_PCM_FORMAT_S20_3LE : + data = (AIC31XX_WORD_LEN_20BITS << +AIC31XX_IFACE1_DATALEN_SHIFT ); +break ; +case SNDRV_PCM_FORMAT_S24_3LE : + data = (AIC31XX_WORD_LEN_24BITS << +AIC31XX_IFACE1_DATALEN_SHIFT ); +break ; +case SNDRV_PCM_FORMAT_S32_LE : + data = (AIC31XX_WORD_LEN_32BITS << +AIC31XX_IFACE1_DATALEN_SHIFT ); +break ; +default : +dev_err (codec->dev,"%s: Unsupported format %d\n" , + __func__,params_format (params)); +return -EINVAL ; + } + +snd_soc_update_bits (codec,AIC31XX_IFACE1 , +AIC31XX_IFACE1_DATALEN_MASK , + data); + +return aic31xx_setup_pll (codec, params); +} + +static int aic31xx_dac_mute (struct snd_soc_dai *codec_dai,int mute) +{ +struct snd_soc_codec *codec = codec_dai->codec; + +if (mute) { +snd_soc_update_bits (codec,AIC31XX_DACMUTE , +AIC31XX_DACMUTE_MASK , +AIC31XX_DACMUTE_MASK ); + }else { +snd_soc_update_bits (codec,AIC31XX_DACMUTE , +AIC31XX_DACMUTE_MASK ,0x0 ); + } + +return 0 ; +} + +static int aic31xx_set_dai_fmt (struct snd_soc_dai *codec_dai, +unsigned int fmt) +{ +struct snd_soc_codec *codec = codec_dai->codec; +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +u8 iface_reg1 =0 ; +u8 iface_reg3 =0 ; +u8 dsp_a_val =0 ; + +dev_dbg (codec->dev,"## %s: fmt = 0x%x\n" , __func__, fmt); + +/* set master/slave audio interface */ +switch (fmt &SND_SOC_DAIFMT_MASTER_MASK ) { +case SND_SOC_DAIFMT_CBM_CFM : + iface_reg1 |=AIC31XX_BCLK_MASTER |AIC31XX_WCLK_MASTER ; +break ; +case SND_SOC_DAIFMT_CBS_CFM : +case SND_SOC_DAIFMT_CBM_CFS : +case SND_SOC_DAIFMT_CBS_CFS : +dev_err (codec->dev,"Unsupported DAI master/slave interface\n" ); +return -EINVAL ; +default : +dev_alert (codec->dev,"Invalid DAI master/slave interface\n" ); +return -EINVAL ; + } + +/* interface format */ +switch (fmt &SND_SOC_DAIFMT_FORMAT_MASK ) { +case SND_SOC_DAIFMT_I2S : +break ; +case SND_SOC_DAIFMT_DSP_A : + dsp_a_val =0x1 ; +case SND_SOC_DAIFMT_DSP_B : +/* NOTE: BCLKINV bit value 1 equas NB and 0 equals IB */ +switch (fmt &SND_SOC_DAIFMT_INV_MASK ) { +case SND_SOC_DAIFMT_NB_NF : + iface_reg3 |=AIC31XX_BCLKINV_MASK ; +break ; +case SND_SOC_DAIFMT_IB_NF : +break ; +default : +return -EINVAL ; + } + iface_reg1 |= (AIC31XX_DSP_MODE << +AIC31XX_IFACE1_DATATYPE_SHIFT ); +break ; +case SND_SOC_DAIFMT_RIGHT_J : + iface_reg1 |= (AIC31XX_RIGHT_JUSTIFIED_MODE << +AIC31XX_IFACE1_DATATYPE_SHIFT ); +break ; +case SND_SOC_DAIFMT_LEFT_J : + iface_reg1 |= (AIC31XX_LEFT_JUSTIFIED_MODE << +AIC31XX_IFACE1_DATATYPE_SHIFT ); +break ; +default : +dev_err (codec->dev,"Invalid DAI interface format\n" ); +return -EINVAL ; + } + +snd_soc_update_bits (codec,AIC31XX_IFACE1 , +AIC31XX_IFACE1_DATATYPE_MASK | +AIC31XX_IFACE1_MASTER_MASK , + iface_reg1); +snd_soc_update_bits (codec,AIC31XX_DATA_OFFSET , +AIC31XX_DATA_OFFSET_MASK , + dsp_a_val); +snd_soc_update_bits (codec,AIC31XX_IFACE2 , +AIC31XX_BCLKINV_MASK , + iface_reg3); + +return 0 ; +} + +static int aic31xx_set_dai_sysclk (struct snd_soc_dai *codec_dai, +int clk_id,unsigned int freq,int dir) +{ +struct snd_soc_codec *codec = codec_dai->codec; +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +int i; + +dev_dbg (codec->dev,"## %s: clk_id = %d, freq = %d, dir = %d\n" , + __func__, clk_id, freq, dir); + +for (i =0 ; aic31xx_divs[i].mclk != freq; i++) +if (i ==ARRAY_SIZE (aic31xx_divs)) { +dev_err (aic31xx->dev,"%s: Unsupported frequency %d\n" , + __func__, freq); +return -EINVAL ; + } + +/* set clock on MCLK, BCLK, or GPIO1 as PLL input */ +snd_soc_update_bits (codec,AIC31XX_CLKMUX ,AIC31XX_PLL_CLKIN_MASK , + clk_id <<AIC31XX_PLL_CLKIN_SHIFT ); + + aic31xx->sysclk = freq; +return 0 ; +} + +static int aic31xx_regulator_event (struct notifier_block *nb, +unsigned long event,void *data) +{ +struct aic31xx_disable_nb *disable_nb = + container_of(nb, struct aic31xx_disable_nb, nb); +struct aic31xx_priv *aic31xx = disable_nb->aic31xx; + +if (event ®ULATOR_EVENT_DISABLE ) { +/* +* Put codec to reset and as at least one +* of the supplies was disabled +*/ +dev_dbg (aic31xx->dev,"## %s: DISABLE received\n" , __func__); +if (gpio_is_valid (aic31xx->pdata.gpio_reset)) +gpio_set_value (aic31xx->pdata.gpio_reset,0 ); +regcache_mark_dirty (aic31xx->regmap); + } + +return 0 ; +} + +static int aic31xx_set_power (struct snd_soc_codec *codec,int power) +{ +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +int ret; + +dev_dbg (codec->dev,"## %s: %d -> %d\n" , __func__, + aic31xx->power, power); +if (aic31xx->power == power) +return 0 ; + +if (power) { + ret =regulator_bulk_enable (ARRAY_SIZE (aic31xx->supplies), + aic31xx->supplies); +if (ret) +return ret; + aic31xx->power =1 ; + +if (gpio_is_valid (aic31xx->pdata.gpio_reset)) { +gpio_set_value (aic31xx->pdata.gpio_reset,1 ); +udelay (100 ); + } +snd_soc_write (codec,AIC31XX_RESET ,0x01 ); +udelay (100 ); +regcache_cache_only (aic31xx->regmap, false); + ret =regcache_sync (aic31xx->regmap); +if (ret !=0 ) { +dev_err (codec->dev, +"Failed to restore cache: %d\n" , ret); +regcache_cache_only (aic31xx->regmap, true); +regulator_bulk_disable (ARRAY_SIZE (aic31xx->supplies), + aic31xx->supplies); +return ret; + } + }else { +/* +* Do soft reset to this codec instance in order to clear +* possible VDD leakage currents in case the supply regulators +* remain on +*/ + +snd_soc_write (codec,AIC31XX_RESET ,0x01 ); +regcache_cache_only (aic31xx->regmap, true); + +if (gpio_is_valid (aic31xx->pdata.gpio_reset)) +gpio_set_value (aic31xx->pdata.gpio_reset,0 ); + aic31xx->power =0 ; + ret =regulator_bulk_disable (ARRAY_SIZE (aic31xx->supplies), + aic31xx->supplies); +regcache_mark_dirty (aic31xx->regmap); + } + +return ret; +} + +static int aic31xx_set_bias_level (struct snd_soc_codec *codec, +enum snd_soc_bias_level level) +{ +dev_dbg (codec->dev,"## %s: %d -> %d\n" , __func__, + codec->dapm.bias_level, level); + +switch (level) { +/* full On */ +case SND_SOC_BIAS_ON : +/* All power is driven by DAPM system*/ +break ; +/* partial On */ +case SND_SOC_BIAS_PREPARE : +break ; +/* Off, with power */ +case SND_SOC_BIAS_STANDBY : +aic31xx_set_power (codec,1 ); +break ; +/* Off, without power */ +case SND_SOC_BIAS_OFF : +aic31xx_set_power (codec,0 ); +break ; + } + codec->dapm.bias_level = level; + +return 0 ; +} + + +static int aic31xx_suspend (struct snd_soc_codec *codec) +{ +aic31xx_set_bias_level (codec,SND_SOC_BIAS_OFF ); +return 0 ; +} + +static int aic31xx_resume (struct snd_soc_codec *codec) +{ +aic31xx_set_bias_level (codec,SND_SOC_BIAS_STANDBY ); +return 0 ; +} + +static int aic31xx_codec_probe (struct snd_soc_codec *codec) +{ +int ret =0 ; +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +int i; + +dev_dbg (aic31xx->dev,"## %s\n" , __func__); + + aic31xx =snd_soc_codec_get_drvdata (codec); + codec->control_data = aic31xx->regmap; + + aic31xx->codec = codec; + + ret =snd_soc_codec_set_cache_io (codec,8 ,8 ,SND_SOC_REGMAP ); + +if (ret !=0 ) { +dev_err (codec->dev,"snd_soc_codec_set_cache_io failed %d\n" , + ret); +return ret; + } + +for (i =0 ; i <ARRAY_SIZE (aic31xx->supplies); i++) { + aic31xx->disable_nb[i].nb.notifier_call = + aic31xx_regulator_event; + aic31xx->disable_nb[i].aic31xx = aic31xx; + ret =regulator_register_notifier (aic31xx->supplies[i].consumer, + &aic31xx->disable_nb[i].nb); +if (ret) { +dev_err (codec->dev, +"Failed to request regulator notifier: %d\n" , + ret); +return ret; + } + } + +regcache_cache_only (aic31xx->regmap, true); +regcache_mark_dirty (aic31xx->regmap); + +aic31xx_add_controls (codec); +aic31xx_add_widgets (codec); + +return ret; +} + +static int aic31xx_codec_remove (struct snd_soc_codec *codec) +{ +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +int i; +/* power down chip */ +aic31xx_set_bias_level (codec,SND_SOC_BIAS_OFF ); + +for (i =0 ; i <ARRAY_SIZE (aic31xx->supplies); i++) +regulator_unregister_notifier (aic31xx->supplies[i].consumer, + &aic31xx->disable_nb[i].nb); + +return 0 ; +} + +static struct snd_soc_codec_driver soc_codec_driver_aic31xx = { + .probe = aic31xx_codec_probe, + .remove = aic31xx_codec_remove, + .suspend = aic31xx_suspend, + .resume = aic31xx_resume, + .set_bias_level = aic31xx_set_bias_level, + .controls = aic31xx_snd_controls, + .num_controls =ARRAY_SIZE (aic31xx_snd_controls), + .dapm_widgets = aic31xx_dapm_widgets, + .num_dapm_widgets =ARRAY_SIZE (aic31xx_dapm_widgets), + .dapm_routes = aic31xx_audio_map, + .num_dapm_routes =ARRAY_SIZE (aic31xx_audio_map), +}; + +static struct snd_soc_dai_ops aic31xx_dai_ops = { + .hw_params = aic31xx_hw_params, + .set_sysclk = aic31xx_set_dai_sysclk, + .set_fmt = aic31xx_set_dai_fmt, + .digital_mute = aic31xx_dac_mute, +}; + +static struct snd_soc_dai_driver aic31xx_dai_driver[] = { + { + .name ="tlv320aic31xx-hifi" , + .playback = { + .stream_name ="Playback" , + .channels_min =1 , + .channels_max =2 , + .rates =AIC31XX_RATES , + .formats =AIC31XX_FORMATS , + }, + .capture = { + .stream_name ="Capture" , + .channels_min =1 , + .channels_max =2 , + .rates =AIC31XX_RATES , + .formats =AIC31XX_FORMATS , + }, + .ops = &aic31xx_dai_ops, + } +}; + +#if defined(CONFIG_OF ) +static const struct of_device_id tlv320aic31xx_of_match[] = { + { .compatible ="ti,tlv320aic310x" }, + { .compatible ="ti,tlv320aic311x" }, + { .compatible ="ti,tlv320aic3100" }, + { .compatible ="ti,tlv320aic3110" }, + { .compatible ="ti,tlv320aic3120" }, + { .compatible ="ti,tlv320aic3111" }, + {}, +}; +MODULE_DEVICE_TABLE (of, tlv320aic31xx_of_match); + +static void aic31xx_pdata_from_of (struct aic31xx_priv *aic31xx) +{ +struct device_node *np = aic31xx->dev->of_node; +unsigned int value =0 ; +int ret; + +of_property_read_u32 (np,"ai31xx-micbias-vg" , &value); +switch (value) { +case 1 : + aic31xx->pdata.micbias_vg =AIC31XX_MICBIAS_2_0V ; +break ; +case 2 : + aic31xx->pdata.micbias_vg =AIC31XX_MICBIAS_2_5V ; +break ; +case 3 : + aic31xx->pdata.micbias_vg =AIC31XX_MICBIAS_AVDDV ; +break ; +default : +dev_err (aic31xx->dev, +"Bad ai31xx-micbias-vg value %d DT\n" , + value); +/* fallthrough */ +case 0 : + aic31xx->pdata.micbias_vg =AIC31XX_MICBIAS_OFF ; + } + + ret =of_get_named_gpio (np,"gpio-reset" ,0 ); +if (ret >0 ) + aic31xx->pdata.gpio_reset = ret; +} +#else /* CONFIG_OF */ +static void aic31xx_pdata_from_of (struct aic31xx_priv *aic31xx) +{ +} +#endif /* CONFIG_OF */ + +void aic31xx_device_init (struct aic31xx_priv *aic31xx) +{ +int ret, i; + +dev_set_drvdata (aic31xx->dev, aic31xx); + +if (dev_get_platdata (aic31xx->dev)) +memcpy (&aic31xx->pdata,dev_get_platdata (aic31xx->dev), +sizeof (aic31xx->pdata)); +else if (aic31xx->dev->of_node) +aic31xx_pdata_from_of (aic31xx); + +if (aic31xx->pdata.gpio_reset) { + ret =devm_gpio_request_one (aic31xx->dev, + aic31xx->pdata.gpio_reset, +GPIOF_OUT_INIT_HIGH , +"aic31xx-reset-pin" ); +if (ret <0 ) { +dev_err (aic31xx->dev,"not able to acquire gpio\n" ); +return ; + } + } + +for (i =0 ; i <ARRAY_SIZE (aic31xx->supplies); i++) + aic31xx->supplies[i].supply = aic31xx_supply_names[i]; + + ret =devm_regulator_bulk_get (aic31xx->dev, +ARRAY_SIZE (aic31xx->supplies), + aic31xx->supplies); +if (ret !=0 ) +dev_err (aic31xx->dev,"Failed to request supplies: %d\n" , ret); + +} + +static int aic31xx_i2c_probe (struct i2c_client *i2c, +const struct i2c_device_id *id) +{ +struct aic31xx_priv *aic31xx; +int ret; +const struct regmap_config *regmap_config; + +dev_dbg (&i2c->dev,"## %s: %s codec_type = %d\n" , __func__, + id->name, (int ) id->driver_data); + + regmap_config = &aic31xx_i2c_regmap; + + aic31xx =devm_kzalloc (&i2c->dev,sizeof (*aic31xx),GFP_KERNEL ); +if (aic31xx ==NULL ) +return -ENOMEM ; + + aic31xx->regmap =devm_regmap_init_i2c (i2c, regmap_config); + +if (IS_ERR (aic31xx->regmap)) { + ret =PTR_ERR (aic31xx->regmap); +dev_err (&i2c->dev,"Failed to allocate register map: %d\n" , + ret); +return ret; + } + aic31xx->dev = &i2c->dev; + + aic31xx->pdata.codec_type = id->driver_data; + +aic31xx_device_init (aic31xx); + + ret =snd_soc_register_codec (&i2c->dev, &soc_codec_driver_aic31xx, + aic31xx_dai_driver, +ARRAY_SIZE (aic31xx_dai_driver)); + +return ret; +} + +static int aic31xx_i2c_remove (struct i2c_client *i2c) +{ +struct aic31xx_priv *aic31xx =dev_get_drvdata (&i2c->dev); + +kfree (aic31xx); +return 0 ; +} + +static const struct i2c_device_id aic31xx_i2c_id[] = { + {"tlv320aic310x" ,AIC3100 }, + {"tlv320aic311x" ,AIC3110 }, + {"tlv320aic3100" ,AIC3100 }, + {"tlv320aic3110" ,AIC3110 }, + {"tlv320aic3120" ,AIC3120 }, + {"tlv320aic3111" ,AIC3111 }, + { } +}; +MODULE_DEVICE_TABLE (i2c, aic31xx_i2c_id); + +static struct i2c_driver aic31xx_i2c_driver = { + .driver = { + .name ="tlv320aic31xx-codec" , + .owner =THIS_MODULE , + .of_match_table =of_match_ptr (tlv320aic31xx_of_match), + }, + .probe = aic31xx_i2c_probe, + .remove = (aic31xx_i2c_remove), + .id_table = aic31xx_i2c_id, +}; + +module_i2c_driver (aic31xx_i2c_driver); + +MODULE_DESCRIPTION ("ASoC TLV320AIC3111 codec driver" ); +MODULE_AUTHOR ("Jyri Sarha" ); +MODULE_LICENSE ("GPL" );
sound/soc/codecs/tlv320aic31xx.h new file +265 -0
265 lines not on this page · raw message
Signature · 1 line
1.7.9.5
A message that is not in the archive (sent off-list or not yet received): 73198e2f7aca0379abc596027d7d59ac250061c7.1393405575.git.jsarha-l0cyMroinI0@public.gmane.org
Re: [PATCH RFC 1/5] ASoC: tlv320aic31xx: Add basic codec driver implementation
Quoted 3 lines · Jyri Sarha
This commit adds a bare bones driver support for TLV320AIC31XX family audio codecs. The driver adds basic stereo playback trough headphone and speaker outputs and mono capture trough microphone inputs.
Always CC maintainers on patches please.
sound/soc/codecs/tlv320aic31xx.c line 119
Quoted 12 lines
sound/soc/codecs/tlv320aic31xx.c new file +12
-0+static bool aic31xx_volatile (struct device *dev,unsigned int reg) +{ +if (aic31xx_precious (dev, reg) ||aic31xx_real_volatile (dev, reg)) +return true; + +switch (reg) { +case AIC31XX_PAGECTL :/* regmap implementation requires this */ +case AIC31XX_RESET :/* always clears after write */ +return true; + } +return false; +}
This is more complex than you need, just write a standalone volatile function with some comments in it.
sound/soc/codecs/tlv320aic31xx.c line 131
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+static const struct regmap_range_cfg aic31xx_ranges[] = { + { + .name ="codec-regmap" ,
Make this meaningful or omit it.
sound/soc/codecs/tlv320aic31xx.c line 157
Quoted 2 lines
sound/soc/codecs/tlv320aic31xx.c new file +2
-0+#define AIC31XX_NUM_SUPPLIES 6 +static const char *const aic31xx_supply_names[] = {
Use the define in the array size to help check everything lines up.
sound/soc/codecs/tlv320aic31xx.c line 250
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+static const char *const ldac_in_text[] = { +"off" ,"Left Data" ,"Right Data" ,"Mono" +};
Off not off - be consistent both internally and with the general style.
sound/soc/codecs/tlv320aic31xx.c line 317
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+static const struct snd_kcontrol_new aic311x_snd_controls[] = { +SOC_DOUBLE_R ("SP Driver Playback Switch" ,AIC31XX_SPLGAIN , +AIC31XX_SPRGAIN ,2 ,1 ,0 ),
SP?
sound/soc/codecs/tlv320aic31xx.c line 376
Quoted 2 lines
sound/soc/codecs/tlv320aic31xx.c new file +2
-0+if (!strcmp (w->name,"DAC Left" )) { + mask =AIC31XX_LDACPWRSTATUS_MASK ;
There's no overlap with the enable bits? In general it would seem nicer to have a switch based on the register bits used for the enable rather than the tree of string comparisions but it's probably not that big a deal overall.
sound/soc/codecs/tlv320aic31xx.c line 394
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+dev_err (w->codec->dev,"Unknown widget '%s' calling %s/n" , + w->name, __func__); +return -1 ;
Return real error codes.
sound/soc/codecs/tlv320aic31xx.c line 400
Quoted 4 lines
sound/soc/codecs/tlv320aic31xx.c new file +4
-0+if (event ==SND_SOC_DAPM_POST_PMU ) +return aic31xx_wait_bits (aic31xx, reg, mask, mask,5000 ,100 ); +else if (event ==SND_SOC_DAPM_POST_PMD ) +return aic31xx_wait_bits (aic31xx, reg, mask,0 ,5000 ,100 );
switch.
sound/soc/codecs/tlv320aic31xx.c line 518
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+SND_SOC_DAPM_DAC_E ("DAC Right" ,"DAC Right Input" , +AIC31XX_DACSETUP ,6 ,0 , aic31xx_dapm_power_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
Don't use stream names, use DAPM to route from the AIF to the widgets.
sound/soc/codecs/tlv320aic31xx.c line 851
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+switch (params_format (params)) { +case SNDRV_PCM_FORMAT_S16_LE : +break ;
params_width().
sound/soc/codecs/tlv320aic31xx.c line 874
Quoted 4 lines
sound/soc/codecs/tlv320aic31xx.c new file +4
-0+AIC31XX_IFACE1_DATALEN_MASK , + data); + +return aic31xx_setup_pll (codec, params);
This is going to mean that you have a symmetry constraint I think, if you try to set up different rates for playback and capture presumably things will break. Setting symmetric_rates will tell applications about that.
sound/soc/codecs/tlv320aic31xx.c line 916
Quoted 8 lines
sound/soc/codecs/tlv320aic31xx.c new file +8
-0+case SND_SOC_DAIFMT_CBS_CFM : +case SND_SOC_DAIFMT_CBM_CFS : +case SND_SOC_DAIFMT_CBS_CFS : +dev_err (codec->dev,"Unsupported DAI master/slave interface\n" ); +return -EINVAL ; +default : +dev_alert (codec->dev,"Invalid DAI master/slave interface\n" ); +return -EINVAL ;
Just have a default.
sound/soc/codecs/tlv320aic31xx.c line 981
Quoted 6 lines
sound/soc/codecs/tlv320aic31xx.c new file +6
-0+for (i =0 ; aic31xx_divs[i].mclk != freq; i++) +if (i ==ARRAY_SIZE (aic31xx_divs)) { +dev_err (aic31xx->dev,"%s: Unsupported frequency %d\n" , + __func__, freq); +return -EINVAL ; + }
Braces around the for () too please.
sound/soc/codecs/tlv320aic31xx.c line 1020
Quoted 9 lines
sound/soc/codecs/tlv320aic31xx.c new file +9
-0+static int aic31xx_set_power (struct snd_soc_codec *codec,int power) +{ +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +int ret; + +dev_dbg (codec->dev,"## %s: %d -> %d\n" , __func__, + aic31xx->power, power); +if (aic31xx->power == power) +return 0 ;
This looks like you need sensible refcounting somewhere? Implementing this as the standard PM operations may be sensible.
sound/soc/codecs/tlv320aic31xx.c line 1035
Quoted 7 lines
sound/soc/codecs/tlv320aic31xx.c new file +7
-0+if (gpio_is_valid (aic31xx->pdata.gpio_reset)) { +gpio_set_value (aic31xx->pdata.gpio_reset,1 ); +udelay (100 ); + } +snd_soc_write (codec,AIC31XX_RESET ,0x01 ); +udelay (100 ); +regcache_cache_only (aic31xx->regmap, false);
You should be coming out of cache only mode before you issue the reset write. Is it not sensible to skip that if you have a physical reset line?
sound/soc/codecs/tlv320aic31xx.c line 1056
Quoted 5 lines
sound/soc/codecs/tlv320aic31xx.c new file +5
-0+snd_soc_write (codec,AIC31XX_RESET ,0x01 ); +regcache_cache_only (aic31xx->regmap, true); + +if (gpio_is_valid (aic31xx->pdata.gpio_reset)) +gpio_set_value (aic31xx->pdata.gpio_reset,0 );
Likewise here. Also if you do reset the CODEC then the dance you did with the regulator notifiers becomes pointless - the goal with that is to avoid the need to resync the cache if the CODEC wasn't actually reset by a power cycle but since the CODEC is going to be explicitly reset before it's powered off there's no benefit.
sound/soc/codecs/tlv320aic31xx.c line 1077
Quoted 6 lines
sound/soc/codecs/tlv320aic31xx.c new file +6
-0+switch (level) { +/* full On */ +case SND_SOC_BIAS_ON : +/* All power is driven by DAPM system*/ +break ; +/* partial On */
Coding style, please.
Re: [PATCH RFC 1/5] ASoC: tlv320aic31xx: Add basic codec driver implementation
Quoted 7 lines · Mark Brown
Quoted 3 lines · Jyri Sarha
This commit adds a bare bones driver support for TLV320AIC31XX family audio codecs. The driver adds basic stereo playback trough headphone and speaker outputs and mono capture trough microphone inputs.
Always CC maintainers on patches please.
Will do.
Quoted 7 lines
Quoted 7 lines
sound/soc/codecs/tlv320aic31xx.c new file +7
-0+static bool aic31xx_volatile (struct device *dev,unsigned int reg) +{ +if (aic31xx_precious (dev, reg) ||aic31xx_real_volatile (dev, reg)) +return true; + +switch (reg) { +case AIC31XX_PAGECTL :/* regmap implementation requires this */
Quoted 9 lines
sound/soc/codecs/tlv320aic31xx.c line 119
Quoted 5 lines
sound/soc/codecs/tlv320aic31xx.c new file +5
-0+case AIC31XX_RESET :/* always clears after write */ +return true; + } +return false; +}This is more complex than you need, just write a standalone volatile function with some comments in it.
Replaced with straight forward aic31xx_volatile() and aic31xx_writable() functions.
Quoted 6 lines
sound/soc/codecs/tlv320aic31xx.c line 131
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+static const struct regmap_range_cfg aic31xx_ranges[] = { + { + .name ="codec-regmap" ,Make this meaningful or omit it.
Removed.
Quoted 5 lines
sound/soc/codecs/tlv320aic31xx.c line 157
Quoted 2 lines
sound/soc/codecs/tlv320aic31xx.c new file +2
-0+#define AIC31XX_NUM_SUPPLIES 6 +static const char *const aic31xx_supply_names[] = {Use the define in the array size to help check everything lines up.
Done.
Quoted 6 lines
sound/soc/codecs/tlv320aic31xx.c line 250
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+static const char *const ldac_in_text[] = { +"off" ,"Left Data" ,"Right Data" ,"Mono" +};Off not off - be consistent both internally and with the general style.
Done.
Quoted 6 lines
sound/soc/codecs/tlv320aic31xx.c line 317
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+static const struct snd_kcontrol_new aic311x_snd_controls[] = { +SOC_DOUBLE_R ("SP Driver Playback Switch" ,AIC31XX_SPLGAIN , +AIC31XX_SPRGAIN ,2 ,1 ,0 ),SP?
"SP" replaced with "Speaker", also in DAPM switches.
Quoted 8 lines
sound/soc/codecs/tlv320aic31xx.c line 376
Quoted 2 lines
sound/soc/codecs/tlv320aic31xx.c new file +2
-0+if (!strcmp (w->name,"DAC Left" )) { + mask =AIC31XX_LDACPWRSTATUS_MASK ;There's no overlap with the enable bits? In general it would seem nicer to have a switch based on the register bits used for the enable rather than the tree of string comparisions but it's probably not that big a deal overall.
Not with bits, but with regs. I decided this structure would be easier to implement and understand than having a switch(w->reg) with nested switch(w->shift) cases. I replaced the string comparisons with a single switch case using a binary combination of w->reg and w->shift.
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+dev_err (w->codec->dev,"Unknown widget '%s' calling %s/n" , + w->name, __func__); +return -1 ;switch (w->shift) {
case 7: mask = AIC31XX_LDACPWRSTATUS_MASK;
Quoted 3 lines
Return real error codes.
Changed to -EINVAL.
Quoted 7 lines
sound/soc/codecs/tlv320aic31xx.c line 400
Quoted 4 lines
sound/soc/codecs/tlv320aic31xx.c new file +4
-0+if (event ==SND_SOC_DAPM_POST_PMU ) +return aic31xx_wait_bits (aic31xx, reg, mask, mask,5000 ,100 ); +else if (event ==SND_SOC_DAPM_POST_PMD ) +return aic31xx_wait_bits (aic31xx, reg, mask,0 ,5000 ,100 );switch.
Done.
Quoted 6 lines
sound/soc/codecs/tlv320aic31xx.c line 518
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+SND_SOC_DAPM_DAC_E ("DAC Right" ,"DAC Right Input" , +AIC31XX_DACSETUP ,6 ,0 , aic31xx_dapm_power_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),Don't use stream names, use DAPM to route from the AIF to the widgets.
Renamed DAC stnames to "Left Playback" and "Right Playback".
Quoted 6 lines
sound/soc/codecs/tlv320aic31xx.c line 851
Quoted 3 lines
sound/soc/codecs/tlv320aic31xx.c new file +3
-0+switch (params_format (params)) { +case SNDRV_PCM_FORMAT_S16_LE : +break ;
params_width().
Done.
Quoted 10 lines
sound/soc/codecs/tlv320aic31xx.c line 874
Quoted 4 lines
sound/soc/codecs/tlv320aic31xx.c new file +4
-0+AIC31XX_IFACE1_DATALEN_MASK , + data); + +return aic31xx_setup_pll (codec, params);This is going to mean that you have a symmetry constraint I think, if you try to set up different rates for playback and capture presumably things will break. Setting
symmetric_rateswill tell applications about that.
Done.
Quoted 11 lines
sound/soc/codecs/tlv320aic31xx.c line 916
Quoted 8 lines
sound/soc/codecs/tlv320aic31xx.c new file +8
-0+case SND_SOC_DAIFMT_CBS_CFM : +case SND_SOC_DAIFMT_CBM_CFS : +case SND_SOC_DAIFMT_CBS_CFS : +dev_err (codec->dev,"Unsupported DAI master/slave interface\n" ); +return -EINVAL ; +default : +dev_alert (codec->dev,"Invalid DAI master/slave interface\n" ); +return -EINVAL ;Just have a default.
Done.
Quoted 9 lines
sound/soc/codecs/tlv320aic31xx.c line 981
Quoted 6 lines
sound/soc/codecs/tlv320aic31xx.c new file +6
-0+for (i =0 ; aic31xx_divs[i].mclk != freq; i++) +if (i ==ARRAY_SIZE (aic31xx_divs)) { +dev_err (aic31xx->dev,"%s: Unsupported frequency %d\n" , + __func__, freq); +return -EINVAL ; + }Braces around the for () too please.
Done.
Quoted 4 lines
Quoted 4 lines
sound/soc/codecs/tlv320aic31xx.c new file +4
-0+static int aic31xx_set_power (struct snd_soc_codec *codec,int power) +{ +struct aic31xx_priv *aic31xx =snd_soc_codec_get_drvdata (codec); +int ret;
Quoted 9 lines
sound/soc/codecs/tlv320aic31xx.c line 1020
Quoted 5 lines
sound/soc/codecs/tlv320aic31xx.c new file +5
-0+ +dev_dbg (codec->dev,"## %s: %d -> %d\n" , __func__, + aic31xx->power, power); +if (aic31xx->power == power) +return 0 ;This looks like you need sensible refcounting somewhere? Implementing this as the standard PM operations may be sensible.
Not really. This was just avoid power on sequence when bias level goes from SND_SOC_BIAS_PREPARE back to SND_SOC_BIAS_STANDBY. I'll change this to a "if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)" in set_bias_level like other codecs do.
Quoted 12 lines
sound/soc/codecs/tlv320aic31xx.c line 1035
Quoted 7 lines
sound/soc/codecs/tlv320aic31xx.c new file +7
-0+if (gpio_is_valid (aic31xx->pdata.gpio_reset)) { +gpio_set_value (aic31xx->pdata.gpio_reset,1 ); +udelay (100 ); + } +snd_soc_write (codec,AIC31XX_RESET ,0x01 ); +udelay (100 ); +regcache_cache_only (aic31xx->regmap, false);You should be coming out of cache only mode before you issue the reset write. Is it not sensible to skip that if you have a physical reset line?
Quoted 12 lines
sound/soc/codecs/tlv320aic31xx.c line 1056
Quoted 5 lines
sound/soc/codecs/tlv320aic31xx.c new file +5
-0+snd_soc_write (codec,AIC31XX_RESET ,0x01 ); +regcache_cache_only (aic31xx->regmap, true); + +if (gpio_is_valid (aic31xx->pdata.gpio_reset)) +gpio_set_value (aic31xx->pdata.gpio_reset,0 );Likewise here. Also if you do reset the CODEC then the dance you did with the regulator notifiers becomes pointless - the goal with that is to avoid the need to resync the cache if the CODEC wasn't actually reset by a power cycle but since the CODEC is going to be explicitly reset before it's powered off there's no benefit.
I rewrote the bias-level/power logic a bit. It should now look more like the other codecs. I hope you like this version better.
Quoted 9 lines
sound/soc/codecs/tlv320aic31xx.c line 1077
Quoted 6 lines
sound/soc/codecs/tlv320aic31xx.c new file +6
-0+switch (level) { +/* full On */ +case SND_SOC_BIAS_ON : +/* All power is driven by DAPM system*/ +break ; +/* partial On */ Coding style, please.
Removed the redundant badly indented comments.
Fixed also the Kconfig and Makefile ordering and added dt-include for micbias and changed the default to 2.0V. All are now squashed to a single patch.
In addition to addressing your comments I also added an internal ADC input and DAC output for codec to turn on at playback/capture start even if the alsamixers are not set correctly.
I'll mail the patches shortly.
Best regards,
Jyri
RFC PATCH 2/5 ASoC: tlv320aic31xx: Add codec driver to Makefile and Kconfig
In reply to Jyri Sarha--- sound/soc/codecs/Kconfig | 4 ++++ sound/soc/codecs/Makefile | 2 ++ 2 files changed, 6 insertions(+)
sound/soc/codecs/Kconfig +4 -0
@@ -74,6 +74,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC32X4 if I2C + select SND_SOC_TLV320AIC31XX if I2C select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C
@@ -364,6 +365,9 @@ config SND_SOC_TLV320AIC26 config SND_SOC_TLV320AIC32X4 tristate +config SND_SOC_TLV320AIC31XX + tristate + config SND_SOC_TLV320AIC3X tristate
sound/soc/codecs/Makefile +2 -0
@@ -64,6 +64,7 @@ snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o +snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320dac33-objs := tlv320dac33.o
@@ -194,6 +195,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o +obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
Signature · 1 line
1.7.9.5
Re: [PATCH RFC 2/5] ASoC: tlv320aic31xx: Add codec driver to Makefile and Kconfig
Quoted 11 lines · Jyri Sarha
Signed-off-by Jyri Sarha <[email protected]>--- sound/soc/codecs/Kconfig | 4 ++++ sound/soc/codecs/Makefile | 2 ++ 2 files changed, 6 insertions(+)sound/soc/codecs/Kconfig +0
-0
sound/soc/codecs/Kconfig config SND_SOC_ALL_CODECS · line 77
Quoted 4 lines
sound/soc/codecs/Kconfig +1
-0@@ -74,6 +74,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC32X4 if I2C + select SND_SOC_TLV320AIC31XX if I2C
You should keep this file ordered.
Quoted 18 lines
sound/soc/codecs/Kconfig +3
-0select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C@@ -364,6 +365,9 @@ config SND_SOC_TLV320AIC26 config SND_SOC_TLV320AIC32X4 tristate +config SND_SOC_TLV320AIC31XX + tristate + config SND_SOC_TLV320AIC3X tristatesound/soc/codecs/Makefile +0
-0
sound/soc/codecs/Makefile snd-soc-stac9766-objs := stac9766.o · line 67
Quoted 4 lines
sound/soc/codecs/Makefile +1
-0@@ -64,6 +64,7 @@ snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o +snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
Same here.
sound/soc/codecs/Makefile obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o · line 198
Quoted 8 lines
sound/soc/codecs/Makefile +1
-0snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320dac33-objs := tlv320dac33.o@@ -194,6 +195,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o +obj-$(CONFIG_SND_SOC_TLV320AIC31XX) += snd-soc-tlv320aic31xx.o
and here
Quoted 4 lines
sound/soc/codecs/Makefile +0
-0obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_TLV320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o
Signature · 5 lines
Péter -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Mark Brown
maintainerRe: [PATCH RFC 2/5] ASoC: tlv320aic31xx: Add codec driver to Makefile and Kconfig
In reply to Jyri Sarhasound/soc/codecs/Kconfig config SND_SOC_ALL_CODECS · line 80
Quoted 10 lines · Jyri Sarha
sound/soc/codecs/Kconfig +1
-0@@ -74,6 +74,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC32X4 if I2C + select SND_SOC_TLV320AIC31XX if I2C select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C
Keep these files sorted and don't send the build infrastructure as a separate patch unless the driver as a whole is split into multiple patches - there is no value unless the driver is being built up over several patches, it just raises a review question on the driver patch.
RFC PATCH 3/5 ASoC: tlv320aic31xx: Add DT binding document
In reply to Jyri SarhaInitial version of tlv320aic31xx device tree bindings document.
--- .../devicetree/bindings/sound/tlv320aic31xx.txt | 57 ++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
Documentation/devicetree/bindings/sound/tlv320aic31xx.txt new file +57 -0
@@ -0,0 +1,57 @@ +Texas Instruments - tlv320aic31xx Codec module + +The tlv320aic31xx serial control bus communicates through I2C protocols + +Required properties: + +- compatible - "string" - One of: + "ti,tlv320aic310x" - Generic TLV320AIC31xx with mono speaker amp + "ti,tlv320aic311x" - Generic TLV320AIC31xx with stereo speaker amp + "ti,tlv320aic3100" - TLV320AIC3100 (mono speaker amp, no MiniDSP) + "ti,tlv320aic3110" - TLV320AIC3110 (stereo speaker amp, no MiniDSP) + "ti,tlv320aic3120" - TLV320AIC3120 (mono speaker amp, MiniDSP) + "ti,tlv320aic3111" - TLV320AIC3111 (stereo speaker amp, MiniDSP) + +- reg - <int> - I2C slave address + + +Optional properties: + +- gpio-reset - gpio pin number used for codec reset +- ai31xx-micbias-vg - MicBias Voltage required. + 1 - MICBIAS output is powered to 2.0V, + 2 - MICBIAS output is powered to 2.5V, + 3 - MICBIAS output is connected to AVDD, + If this node is not mentioned or if the value is incorrect, then MicBias + is powered down. +- HPVDD-supply, SPRVDD-supply, SPLVDD-supply, AVDD-supply, IOVDD-supply, + DVDD-supply : power supplies for the device as covered in + Documentation/devicetree/bindings/regulator/regulator.txt + +CODEC output pins: + * HPL + * HPR + * SPL, devices with stereo speaker amp + * SPR, devices with stereo speaker amp + * SPK, devices with mono speaker amp + +CODEC input pins: + * MIC1LP + * MIC1RP + * MIC1LM + +The pins can be used in referring sound node's audio-routing property. + +Example: + +tlv320aic31xx: tlv320aic31xx@18 { + compatible = "ti,tlv320aic311x"; + reg = <0x18>; + + HPVDD-supply = <®ulator>; + SPRVDD-supply = <®ulator>; + SPLVDD-supply = <®ulator>; + AVDD-supply = <®ulator>; + IOVDD-supply = <®ulator>; + DVDD-supply = <®ulator>; +};
Signature · 1 line
1.7.9.5
Mark Brown
maintainerRe: [PATCH RFC 3/5] ASoC: tlv320aic31xx: Add DT binding document
Documentation/devicetree/bindings/sound/tlv320aic31xx.txt line 26
Quoted 6 lines · Jyri Sarha
+- ai31xx-micbias-vg - MicBias Voltage required.
Documentation/devicetree/bindings/sound/tlv320aic31xx.txt new file +5
-0+ 1 - MICBIAS output is powered to 2.0V, + 2 - MICBIAS output is powered to 2.5V, + 3 - MICBIAS output is connected to AVDD, + If this node is not mentioned or if the value is incorrect, then MicBias + is powered down.
Please provide defines for these as part of the binding. It's not clear to me why leaving the micbias powered down is a sane default, it'll only be powered up if it's connected to something and used which doesn't seem altogether sane. I'd suggest picking 2.0V (the lowest voltage so least likely to cause damage).
RFC PATCH 4/5 ASoC: davinci-evm: Add AM43xx-EPOS-EVM audio support
In reply to Jyri SarhaAdd machine driver support for AM43xx-ePOS-EVM and update associated device tree binding document.
--- .../bindings/sound/davinci-evm-audio.txt | 9 +++-- sound/soc/davinci/davinci-evm.c | 41 ++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-)
Documentation/devicetree/bindings/sound/davinci-evm-audio.txt +6 -3
@@ -2,8 +2,10 @@ Required properties: - compatible : "ti,da830-evm-audio" : forDM365/DA8xx/OMAPL1x/AM33xx + : "ti,am43xx-epos-evm-audio" : for am43xx-epos-evm - ti,model : The user-visible name of this sound complex. -- ti,audio-codec : The phandle of the TLV320AIC3x audio codec +- ti,audio-codec : The phandle of the TLV320AIC3x audio codec, + or the TLV320AIC31xx audio codec. - ti,mcasp-controller : The phandle of the McASP controller - ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec - ti,audio-routing : A list of the connections between audio components.
@@ -14,9 +16,10 @@ Required properties: Board connectors: * Headphone Jack - * Line Out + * Line Out - "ti,da830-evm-audio" only * Mic Jack - * Line In + * Line In - "ti,da830-evm-audio" only + * Speaker - "ti,am43xx-epos-evm-audio" only Example:
sound/soc/davinci/davinci-evm.c +41 -0
58 lines not on this page · raw message
Signature · 1 line
1.7.9.5
RFC PATCH 5/5 ASoC: davinci: Add SND_AM43XX_SOC_EPOS_EVM build option
In reply to Jyri SarhaAdd support for am335x and am43x based boards with tlv320aic31xx compatible codec connected to McASP.
--- sound/soc/davinci/Kconfig | 12 ++++++++++++ sound/soc/davinci/Makefile | 1 + 2 files changed, 13 insertions(+)
sound/soc/davinci/Kconfig +12 -0
@@ -26,6 +26,18 @@ config SND_AM33XX_SOC_EVM AM335X-EVMSK, and BeagelBone with AudioCape boards have this setup. +config SND_AM43XX_SOC_EPOS_EVM + tristate "SoC Audio for the AM33XX/AM43XX and TLV320AIC31XX based board" + depends on SND_DAVINCI_SOC + depends on SOC_AM33XX || SOC_AM43XX + select SND_SOC_TLV320AIC31XX + select SND_DAVINCI_SOC_MCASP + help + Say Y or M if you want to add support for SoC audio on + AM335X/AM43XX boards using McASP to connect to a codec of + TLV320AIC31XX series. For example AM43XX-EPOS-EVM has such a + setup. + config SND_DAVINCI_SOC_EVM tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM" depends on SND_DAVINCI_SOC
sound/soc/davinci/Makefile +1 -0
@@ -13,3 +13,4 @@ obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o snd-soc-evm-objs := davinci-evm.o obj-$(CONFIG_SND_DAVINCI_SOC_GENERIC_EVM) += snd-soc-evm.o +obj-$(CONFIG_SND_AM43XX_SOC_EPOS_EVM) += snd-soc-evm.o
Signature · 1 line
1.7.9.5