2019-05-24 13:04:09 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-06-27 19:04:01 +04:00
/*
* ADAV80X Audio Codec driver supporting ADAV801 , ADAV803
*
* Copyright 2011 Analog Devices Inc .
* Author : Yi Li < yi . li @ analog . com >
* Author : Lars - Peter Clausen < lars @ metafoo . de >
*/
# include <linux/module.h>
# include <linux/kernel.h>
2014-02-17 16:16:56 +04:00
# include <linux/regmap.h>
2011-06-27 19:04:01 +04:00
# include <linux/slab.h>
2014-02-17 16:16:56 +04:00
2011-06-27 19:04:01 +04:00
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
2014-02-17 16:16:56 +04:00
# include <sound/tlv.h>
2011-06-27 19:04:01 +04:00
# include "adav80x.h"
# define ADAV80X_PLAYBACK_CTRL 0x04
# define ADAV80X_AUX_IN_CTRL 0x05
# define ADAV80X_REC_CTRL 0x06
# define ADAV80X_AUX_OUT_CTRL 0x07
# define ADAV80X_DPATH_CTRL1 0x62
# define ADAV80X_DPATH_CTRL2 0x63
# define ADAV80X_DAC_CTRL1 0x64
# define ADAV80X_DAC_CTRL2 0x65
# define ADAV80X_DAC_CTRL3 0x66
# define ADAV80X_DAC_L_VOL 0x68
# define ADAV80X_DAC_R_VOL 0x69
# define ADAV80X_PGA_L_VOL 0x6c
# define ADAV80X_PGA_R_VOL 0x6d
# define ADAV80X_ADC_CTRL1 0x6e
# define ADAV80X_ADC_CTRL2 0x6f
# define ADAV80X_ADC_L_VOL 0x70
# define ADAV80X_ADC_R_VOL 0x71
# define ADAV80X_PLL_CTRL1 0x74
# define ADAV80X_PLL_CTRL2 0x75
# define ADAV80X_ICLK_CTRL1 0x76
# define ADAV80X_ICLK_CTRL2 0x77
# define ADAV80X_PLL_CLK_SRC 0x78
# define ADAV80X_PLL_OUTE 0x7a
# define ADAV80X_PLL_CLK_SRC_PLL_XIN(pll) 0x00
# define ADAV80X_PLL_CLK_SRC_PLL_MCLKI(pll) (0x40 << (pll))
# define ADAV80X_PLL_CLK_SRC_PLL_MASK(pll) (0x40 << (pll))
# define ADAV80X_ICLK_CTRL1_DAC_SRC(src) ((src) << 5)
# define ADAV80X_ICLK_CTRL1_ADC_SRC(src) ((src) << 2)
# define ADAV80X_ICLK_CTRL1_ICLK2_SRC(src) (src)
# define ADAV80X_ICLK_CTRL2_ICLK1_SRC(src) ((src) << 3)
# define ADAV80X_PLL_CTRL1_PLLDIV 0x10
# define ADAV80X_PLL_CTRL1_PLLPD(pll) (0x04 << (pll))
# define ADAV80X_PLL_CTRL1_XTLPD 0x02
# define ADAV80X_PLL_CTRL2_FIELD(pll, x) ((x) << ((pll) * 4))
# define ADAV80X_PLL_CTRL2_FS_48(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x00)
# define ADAV80X_PLL_CTRL2_FS_32(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x08)
# define ADAV80X_PLL_CTRL2_FS_44(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0c)
# define ADAV80X_PLL_CTRL2_SEL(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x02)
# define ADAV80X_PLL_CTRL2_DOUB(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x01)
# define ADAV80X_PLL_CTRL2_PLL_MASK(pll) ADAV80X_PLL_CTRL2_FIELD((pll), 0x0f)
# define ADAV80X_ADC_CTRL1_MODULATOR_MASK 0x80
# define ADAV80X_ADC_CTRL1_MODULATOR_128FS 0x00
# define ADAV80X_ADC_CTRL1_MODULATOR_64FS 0x80
# define ADAV80X_DAC_CTRL1_PD 0x80
# define ADAV80X_DAC_CTRL2_DIV1 0x00
# define ADAV80X_DAC_CTRL2_DIV1_5 0x10
# define ADAV80X_DAC_CTRL2_DIV2 0x20
# define ADAV80X_DAC_CTRL2_DIV3 0x30
# define ADAV80X_DAC_CTRL2_DIV_MASK 0x30
# define ADAV80X_DAC_CTRL2_INTERPOL_256FS 0x00
# define ADAV80X_DAC_CTRL2_INTERPOL_128FS 0x40
# define ADAV80X_DAC_CTRL2_INTERPOL_64FS 0x80
# define ADAV80X_DAC_CTRL2_INTERPOL_MASK 0xc0
# define ADAV80X_DAC_CTRL2_DEEMPH_NONE 0x00
# define ADAV80X_DAC_CTRL2_DEEMPH_44 0x01
# define ADAV80X_DAC_CTRL2_DEEMPH_32 0x02
# define ADAV80X_DAC_CTRL2_DEEMPH_48 0x03
# define ADAV80X_DAC_CTRL2_DEEMPH_MASK 0x01
# define ADAV80X_CAPTURE_MODE_MASTER 0x20
# define ADAV80X_CAPTURE_WORD_LEN24 0x00
# define ADAV80X_CAPTURE_WORD_LEN20 0x04
# define ADAV80X_CAPTRUE_WORD_LEN18 0x08
# define ADAV80X_CAPTURE_WORD_LEN16 0x0c
# define ADAV80X_CAPTURE_WORD_LEN_MASK 0x0c
# define ADAV80X_CAPTURE_MODE_LEFT_J 0x00
# define ADAV80X_CAPTURE_MODE_I2S 0x01
# define ADAV80X_CAPTURE_MODE_RIGHT_J 0x03
# define ADAV80X_CAPTURE_MODE_MASK 0x03
# define ADAV80X_PLAYBACK_MODE_MASTER 0x10
# define ADAV80X_PLAYBACK_MODE_LEFT_J 0x00
# define ADAV80X_PLAYBACK_MODE_I2S 0x01
# define ADAV80X_PLAYBACK_MODE_RIGHT_J_24 0x04
# define ADAV80X_PLAYBACK_MODE_RIGHT_J_20 0x05
# define ADAV80X_PLAYBACK_MODE_RIGHT_J_18 0x06
# define ADAV80X_PLAYBACK_MODE_RIGHT_J_16 0x07
# define ADAV80X_PLAYBACK_MODE_MASK 0x07
# define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x))
2015-07-05 12:48:29 +03:00
static const struct reg_default adav80x_reg_defaults [ ] = {
2013-09-27 17:18:25 +04:00
{ ADAV80X_PLAYBACK_CTRL , 0x01 } ,
{ ADAV80X_AUX_IN_CTRL , 0x01 } ,
{ ADAV80X_REC_CTRL , 0x02 } ,
{ ADAV80X_AUX_OUT_CTRL , 0x01 } ,
{ ADAV80X_DPATH_CTRL1 , 0xc0 } ,
{ ADAV80X_DPATH_CTRL2 , 0x11 } ,
{ ADAV80X_DAC_CTRL1 , 0x00 } ,
{ ADAV80X_DAC_CTRL2 , 0x00 } ,
{ ADAV80X_DAC_CTRL3 , 0x00 } ,
{ ADAV80X_DAC_L_VOL , 0xff } ,
{ ADAV80X_DAC_R_VOL , 0xff } ,
{ ADAV80X_PGA_L_VOL , 0x00 } ,
{ ADAV80X_PGA_R_VOL , 0x00 } ,
{ ADAV80X_ADC_CTRL1 , 0x00 } ,
{ ADAV80X_ADC_CTRL2 , 0x00 } ,
{ ADAV80X_ADC_L_VOL , 0xff } ,
{ ADAV80X_ADC_R_VOL , 0xff } ,
{ ADAV80X_PLL_CTRL1 , 0x00 } ,
{ ADAV80X_PLL_CTRL2 , 0x00 } ,
{ ADAV80X_ICLK_CTRL1 , 0x00 } ,
{ ADAV80X_ICLK_CTRL2 , 0x00 } ,
{ ADAV80X_PLL_CLK_SRC , 0x00 } ,
{ ADAV80X_PLL_OUTE , 0x00 } ,
2011-06-27 19:04:01 +04:00
} ;
struct adav80x {
2013-09-27 17:18:25 +04:00
struct regmap * regmap ;
2011-06-27 19:04:01 +04:00
enum adav80x_clk_src clk_src ;
unsigned int sysclk ;
enum adav80x_pll_src pll_src ;
unsigned int dai_fmt [ 2 ] ;
unsigned int rate ;
bool deemph ;
bool sysclk_pd [ 3 ] ;
} ;
static const char * adav80x_mux_text [ ] = {
" ADC " ,
" Playback " ,
" Aux Playback " ,
} ;
static const unsigned int adav80x_mux_values [ ] = {
0 , 2 , 3 ,
} ;
# define ADAV80X_MUX_ENUM_DECL(name, reg, shift) \
SOC_VALUE_ENUM_DOUBLE_DECL ( name , reg , shift , 7 , \
ARRAY_SIZE ( adav80x_mux_text ) , adav80x_mux_text , \
adav80x_mux_values )
static ADAV80X_MUX_ENUM_DECL ( adav80x_aux_capture_enum , ADAV80X_DPATH_CTRL1 , 0 ) ;
static ADAV80X_MUX_ENUM_DECL ( adav80x_capture_enum , ADAV80X_DPATH_CTRL1 , 3 ) ;
static ADAV80X_MUX_ENUM_DECL ( adav80x_dac_enum , ADAV80X_DPATH_CTRL2 , 3 ) ;
static const struct snd_kcontrol_new adav80x_aux_capture_mux_ctrl =
2014-04-14 23:30:57 +04:00
SOC_DAPM_ENUM ( " Route " , adav80x_aux_capture_enum ) ;
2011-06-27 19:04:01 +04:00
static const struct snd_kcontrol_new adav80x_capture_mux_ctrl =
2014-04-14 23:30:57 +04:00
SOC_DAPM_ENUM ( " Route " , adav80x_capture_enum ) ;
2011-06-27 19:04:01 +04:00
static const struct snd_kcontrol_new adav80x_dac_mux_ctrl =
2014-04-14 23:30:57 +04:00
SOC_DAPM_ENUM ( " Route " , adav80x_dac_enum ) ;
2011-06-27 19:04:01 +04:00
# define ADAV80X_MUX(name, ctrl) \
2014-04-14 23:30:57 +04:00
SND_SOC_DAPM_MUX ( name , SND_SOC_NOPM , 0 , 0 , ctrl )
2011-06-27 19:04:01 +04:00
static const struct snd_soc_dapm_widget adav80x_dapm_widgets [ ] = {
SND_SOC_DAPM_DAC ( " DAC " , NULL , ADAV80X_DAC_CTRL1 , 7 , 1 ) ,
SND_SOC_DAPM_ADC ( " ADC " , NULL , ADAV80X_ADC_CTRL1 , 5 , 1 ) ,
SND_SOC_DAPM_PGA ( " Right PGA " , ADAV80X_ADC_CTRL1 , 0 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_PGA ( " Left PGA " , ADAV80X_ADC_CTRL1 , 1 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " AIFOUT " , " HiFi Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " AIFIN " , " HiFi Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " AIFAUXOUT " , " Aux Capture " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " AIFAUXIN " , " Aux Playback " , 0 , SND_SOC_NOPM , 0 , 0 ) ,
ADAV80X_MUX ( " Aux Capture Select " , & adav80x_aux_capture_mux_ctrl ) ,
ADAV80X_MUX ( " Capture Select " , & adav80x_capture_mux_ctrl ) ,
ADAV80X_MUX ( " DAC Select " , & adav80x_dac_mux_ctrl ) ,
SND_SOC_DAPM_INPUT ( " VINR " ) ,
SND_SOC_DAPM_INPUT ( " VINL " ) ,
SND_SOC_DAPM_OUTPUT ( " VOUTR " ) ,
SND_SOC_DAPM_OUTPUT ( " VOUTL " ) ,
SND_SOC_DAPM_SUPPLY ( " SYSCLK " , SND_SOC_NOPM , 0 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " PLL1 " , ADAV80X_PLL_CTRL1 , 2 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " PLL2 " , ADAV80X_PLL_CTRL1 , 3 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_SUPPLY ( " OSC " , ADAV80X_PLL_CTRL1 , 1 , 1 , NULL , 0 ) ,
} ;
static int adav80x_dapm_sysclk_check ( struct snd_soc_dapm_widget * source ,
struct snd_soc_dapm_widget * sink )
{
2018-01-29 07:10:06 +03:00
struct snd_soc_component * component = snd_soc_dapm_to_component ( source - > dapm ) ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
const char * clk ;
switch ( adav80x - > clk_src ) {
case ADAV80X_CLK_PLL1 :
clk = " PLL1 " ;
break ;
case ADAV80X_CLK_PLL2 :
clk = " PLL2 " ;
break ;
case ADAV80X_CLK_XTAL :
clk = " OSC " ;
break ;
default :
return 0 ;
}
2023-10-23 12:54:14 +03:00
return snd_soc_dapm_widget_name_cmp ( source , clk ) = = 0 ;
2011-06-27 19:04:01 +04:00
}
static int adav80x_dapm_pll_check ( struct snd_soc_dapm_widget * source ,
struct snd_soc_dapm_widget * sink )
{
2018-01-29 07:10:06 +03:00
struct snd_soc_component * component = snd_soc_dapm_to_component ( source - > dapm ) ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
return adav80x - > pll_src = = ADAV80X_PLL_SRC_XTAL ;
}
static const struct snd_soc_dapm_route adav80x_dapm_routes [ ] = {
{ " DAC Select " , " ADC " , " ADC " } ,
{ " DAC Select " , " Playback " , " AIFIN " } ,
{ " DAC Select " , " Aux Playback " , " AIFAUXIN " } ,
{ " DAC " , NULL , " DAC Select " } ,
{ " Capture Select " , " ADC " , " ADC " } ,
{ " Capture Select " , " Playback " , " AIFIN " } ,
{ " Capture Select " , " Aux Playback " , " AIFAUXIN " } ,
{ " AIFOUT " , NULL , " Capture Select " } ,
{ " Aux Capture Select " , " ADC " , " ADC " } ,
{ " Aux Capture Select " , " Playback " , " AIFIN " } ,
{ " Aux Capture Select " , " Aux Playback " , " AIFAUXIN " } ,
{ " AIFAUXOUT " , NULL , " Aux Capture Select " } ,
{ " VOUTR " , NULL , " DAC " } ,
{ " VOUTL " , NULL , " DAC " } ,
{ " Left PGA " , NULL , " VINL " } ,
{ " Right PGA " , NULL , " VINR " } ,
{ " ADC " , NULL , " Left PGA " } ,
{ " ADC " , NULL , " Right PGA " } ,
{ " SYSCLK " , NULL , " PLL1 " , adav80x_dapm_sysclk_check } ,
{ " SYSCLK " , NULL , " PLL2 " , adav80x_dapm_sysclk_check } ,
{ " SYSCLK " , NULL , " OSC " , adav80x_dapm_sysclk_check } ,
{ " PLL1 " , NULL , " OSC " , adav80x_dapm_pll_check } ,
{ " PLL2 " , NULL , " OSC " , adav80x_dapm_pll_check } ,
{ " ADC " , NULL , " SYSCLK " } ,
{ " DAC " , NULL , " SYSCLK " } ,
{ " AIFOUT " , NULL , " SYSCLK " } ,
{ " AIFAUXOUT " , NULL , " SYSCLK " } ,
{ " AIFIN " , NULL , " SYSCLK " } ,
{ " AIFAUXIN " , NULL , " SYSCLK " } ,
} ;
2018-01-29 07:10:06 +03:00
static int adav80x_set_deemph ( struct snd_soc_component * component )
2011-06-27 19:04:01 +04:00
{
2018-01-29 07:10:06 +03:00
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
unsigned int val ;
if ( adav80x - > deemph ) {
switch ( adav80x - > rate ) {
case 32000 :
val = ADAV80X_DAC_CTRL2_DEEMPH_32 ;
break ;
case 44100 :
val = ADAV80X_DAC_CTRL2_DEEMPH_44 ;
break ;
case 48000 :
case 64000 :
case 88200 :
case 96000 :
val = ADAV80X_DAC_CTRL2_DEEMPH_48 ;
break ;
default :
val = ADAV80X_DAC_CTRL2_DEEMPH_NONE ;
break ;
}
} else {
val = ADAV80X_DAC_CTRL2_DEEMPH_NONE ;
}
2013-09-27 17:18:25 +04:00
return regmap_update_bits ( adav80x - > regmap , ADAV80X_DAC_CTRL2 ,
2011-06-27 19:04:01 +04:00
ADAV80X_DAC_CTRL2_DEEMPH_MASK , val ) ;
}
static int adav80x_put_deemph ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 07:10:06 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2015-03-10 14:39:03 +03:00
unsigned int deemph = ucontrol - > value . integer . value [ 0 ] ;
2011-06-27 19:04:01 +04:00
if ( deemph > 1 )
return - EINVAL ;
adav80x - > deemph = deemph ;
2018-01-29 07:10:06 +03:00
return adav80x_set_deemph ( component ) ;
2011-06-27 19:04:01 +04:00
}
static int adav80x_get_deemph ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 07:10:06 +03:00
struct snd_soc_component * component = snd_soc_kcontrol_component ( kcontrol ) ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
2015-03-10 14:39:03 +03:00
ucontrol - > value . integer . value [ 0 ] = adav80x - > deemph ;
2011-06-27 19:04:01 +04:00
return 0 ;
} ;
static const DECLARE_TLV_DB_SCALE ( adav80x_inpga_tlv , 0 , 50 , 0 ) ;
static const DECLARE_TLV_DB_MINMAX ( adav80x_digital_tlv , - 9563 , 0 ) ;
static const struct snd_kcontrol_new adav80x_controls [ ] = {
SOC_DOUBLE_R_TLV ( " Master Playback Volume " , ADAV80X_DAC_L_VOL ,
ADAV80X_DAC_R_VOL , 0 , 0xff , 0 , adav80x_digital_tlv ) ,
SOC_DOUBLE_R_TLV ( " Master Capture Volume " , ADAV80X_ADC_L_VOL ,
ADAV80X_ADC_R_VOL , 0 , 0xff , 0 , adav80x_digital_tlv ) ,
SOC_DOUBLE_R_TLV ( " PGA Capture Volume " , ADAV80X_PGA_L_VOL ,
ADAV80X_PGA_R_VOL , 0 , 0x30 , 0 , adav80x_inpga_tlv ) ,
SOC_DOUBLE ( " Master Playback Switch " , ADAV80X_DAC_CTRL1 , 0 , 1 , 1 , 0 ) ,
SOC_DOUBLE ( " Master Capture Switch " , ADAV80X_ADC_CTRL1 , 2 , 3 , 1 , 1 ) ,
SOC_SINGLE ( " ADC High Pass Filter Switch " , ADAV80X_ADC_CTRL1 , 6 , 1 , 0 ) ,
SOC_SINGLE_BOOL_EXT ( " Playback De-emphasis Switch " , 0 ,
adav80x_get_deemph , adav80x_put_deemph ) ,
} ;
static unsigned int adav80x_port_ctrl_regs [ 2 ] [ 2 ] = {
{ ADAV80X_REC_CTRL , ADAV80X_PLAYBACK_CTRL , } ,
{ ADAV80X_AUX_OUT_CTRL , ADAV80X_AUX_IN_CTRL } ,
} ;
static int adav80x_set_dai_fmt ( struct snd_soc_dai * dai , unsigned int fmt )
{
2018-01-29 07:10:06 +03:00
struct snd_soc_component * component = dai - > component ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
unsigned int capture = 0x00 ;
unsigned int playback = 0x00 ;
2021-09-16 18:18:06 +03:00
switch ( fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) {
case SND_SOC_DAIFMT_CBP_CFP :
2011-06-27 19:04:01 +04:00
capture | = ADAV80X_CAPTURE_MODE_MASTER ;
playback | = ADAV80X_PLAYBACK_MODE_MASTER ;
2020-11-20 21:23:50 +03:00
break ;
2021-09-16 18:18:06 +03:00
case SND_SOC_DAIFMT_CBC_CFC :
2011-06-27 19:04:01 +04:00
break ;
default :
return - EINVAL ;
}
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_I2S :
capture | = ADAV80X_CAPTURE_MODE_I2S ;
playback | = ADAV80X_PLAYBACK_MODE_I2S ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
capture | = ADAV80X_CAPTURE_MODE_LEFT_J ;
playback | = ADAV80X_PLAYBACK_MODE_LEFT_J ;
break ;
case SND_SOC_DAIFMT_RIGHT_J :
capture | = ADAV80X_CAPTURE_MODE_RIGHT_J ;
playback | = ADAV80X_PLAYBACK_MODE_RIGHT_J_24 ;
break ;
default :
return - EINVAL ;
}
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
break ;
default :
return - EINVAL ;
}
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , adav80x_port_ctrl_regs [ dai - > id ] [ 0 ] ,
2011-06-27 19:04:01 +04:00
ADAV80X_CAPTURE_MODE_MASK | ADAV80X_CAPTURE_MODE_MASTER ,
capture ) ;
2013-09-27 17:18:25 +04:00
regmap_write ( adav80x - > regmap , adav80x_port_ctrl_regs [ dai - > id ] [ 1 ] ,
playback ) ;
2011-06-27 19:04:01 +04:00
adav80x - > dai_fmt [ dai - > id ] = fmt & SND_SOC_DAIFMT_FORMAT_MASK ;
return 0 ;
}
2018-01-29 07:10:06 +03:00
static int adav80x_set_adc_clock ( struct snd_soc_component * component ,
2011-06-27 19:04:01 +04:00
unsigned int sample_rate )
{
2018-01-29 07:10:06 +03:00
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
unsigned int val ;
if ( sample_rate < = 48000 )
val = ADAV80X_ADC_CTRL1_MODULATOR_128FS ;
else
val = ADAV80X_ADC_CTRL1_MODULATOR_64FS ;
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , ADAV80X_ADC_CTRL1 ,
2011-06-27 19:04:01 +04:00
ADAV80X_ADC_CTRL1_MODULATOR_MASK , val ) ;
return 0 ;
}
2018-01-29 07:10:06 +03:00
static int adav80x_set_dac_clock ( struct snd_soc_component * component ,
2011-06-27 19:04:01 +04:00
unsigned int sample_rate )
{
2018-01-29 07:10:06 +03:00
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
unsigned int val ;
if ( sample_rate < = 48000 )
val = ADAV80X_DAC_CTRL2_DIV1 | ADAV80X_DAC_CTRL2_INTERPOL_256FS ;
else
val = ADAV80X_DAC_CTRL2_DIV2 | ADAV80X_DAC_CTRL2_INTERPOL_128FS ;
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , ADAV80X_DAC_CTRL2 ,
2011-06-27 19:04:01 +04:00
ADAV80X_DAC_CTRL2_DIV_MASK | ADAV80X_DAC_CTRL2_INTERPOL_MASK ,
val ) ;
return 0 ;
}
2018-01-29 07:10:06 +03:00
static int adav80x_set_capture_pcm_format ( struct snd_soc_component * component ,
2014-01-08 22:50:40 +04:00
struct snd_soc_dai * dai , struct snd_pcm_hw_params * params )
2011-06-27 19:04:01 +04:00
{
2018-01-29 07:10:06 +03:00
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
unsigned int val ;
2014-01-08 22:50:40 +04:00
switch ( params_width ( params ) ) {
case 16 :
2011-06-27 19:04:01 +04:00
val = ADAV80X_CAPTURE_WORD_LEN16 ;
break ;
2014-01-08 22:50:40 +04:00
case 18 :
2011-06-27 19:04:01 +04:00
val = ADAV80X_CAPTRUE_WORD_LEN18 ;
break ;
2014-01-08 22:50:40 +04:00
case 20 :
2011-06-27 19:04:01 +04:00
val = ADAV80X_CAPTURE_WORD_LEN20 ;
break ;
2014-01-08 22:50:40 +04:00
case 24 :
2011-06-27 19:04:01 +04:00
val = ADAV80X_CAPTURE_WORD_LEN24 ;
break ;
default :
2011-07-16 06:34:58 +04:00
return - EINVAL ;
2011-06-27 19:04:01 +04:00
}
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , adav80x_port_ctrl_regs [ dai - > id ] [ 0 ] ,
2011-06-27 19:04:01 +04:00
ADAV80X_CAPTURE_WORD_LEN_MASK , val ) ;
return 0 ;
}
2018-01-29 07:10:06 +03:00
static int adav80x_set_playback_pcm_format ( struct snd_soc_component * component ,
2014-01-08 22:50:40 +04:00
struct snd_soc_dai * dai , struct snd_pcm_hw_params * params )
2011-06-27 19:04:01 +04:00
{
2018-01-29 07:10:06 +03:00
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
unsigned int val ;
if ( adav80x - > dai_fmt [ dai - > id ] ! = SND_SOC_DAIFMT_RIGHT_J )
return 0 ;
2014-01-08 22:50:40 +04:00
switch ( params_width ( params ) ) {
case 16 :
2011-06-27 19:04:01 +04:00
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_16 ;
break ;
2014-01-08 22:50:40 +04:00
case 18 :
2011-06-27 19:04:01 +04:00
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_18 ;
break ;
2014-01-08 22:50:40 +04:00
case 20 :
2011-06-27 19:04:01 +04:00
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_20 ;
break ;
2014-01-08 22:50:40 +04:00
case 24 :
2011-06-27 19:04:01 +04:00
val = ADAV80X_PLAYBACK_MODE_RIGHT_J_24 ;
break ;
default :
2011-07-16 06:34:58 +04:00
return - EINVAL ;
2011-06-27 19:04:01 +04:00
}
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , adav80x_port_ctrl_regs [ dai - > id ] [ 1 ] ,
2011-06-27 19:04:01 +04:00
ADAV80X_PLAYBACK_MODE_MASK , val ) ;
return 0 ;
}
static int adav80x_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params , struct snd_soc_dai * dai )
{
2018-01-29 07:10:06 +03:00
struct snd_soc_component * component = dai - > component ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
unsigned int rate = params_rate ( params ) ;
if ( rate * 256 ! = adav80x - > sysclk )
return - EINVAL ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
2018-01-29 07:10:06 +03:00
adav80x_set_playback_pcm_format ( component , dai , params ) ;
adav80x_set_dac_clock ( component , rate ) ;
2011-06-27 19:04:01 +04:00
} else {
2018-01-29 07:10:06 +03:00
adav80x_set_capture_pcm_format ( component , dai , params ) ;
adav80x_set_adc_clock ( component , rate ) ;
2011-06-27 19:04:01 +04:00
}
adav80x - > rate = rate ;
2018-01-29 07:10:06 +03:00
adav80x_set_deemph ( component ) ;
2011-06-27 19:04:01 +04:00
return 0 ;
}
2018-01-29 07:10:06 +03:00
static int adav80x_set_sysclk ( struct snd_soc_component * component ,
2011-08-24 23:09:01 +04:00
int clk_id , int source ,
unsigned int freq , int dir )
2011-06-27 19:04:01 +04:00
{
2018-01-29 07:10:06 +03:00
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( component ) ;
2011-06-27 19:04:01 +04:00
if ( dir = = SND_SOC_CLOCK_IN ) {
switch ( clk_id ) {
case ADAV80X_CLK_XIN :
case ADAV80X_CLK_XTAL :
case ADAV80X_CLK_MCLKI :
case ADAV80X_CLK_PLL1 :
case ADAV80X_CLK_PLL2 :
break ;
default :
return - EINVAL ;
}
adav80x - > sysclk = freq ;
if ( adav80x - > clk_src ! = clk_id ) {
unsigned int iclk_ctrl1 , iclk_ctrl2 ;
adav80x - > clk_src = clk_id ;
if ( clk_id = = ADAV80X_CLK_XTAL )
clk_id = ADAV80X_CLK_XIN ;
iclk_ctrl1 = ADAV80X_ICLK_CTRL1_DAC_SRC ( clk_id ) |
ADAV80X_ICLK_CTRL1_ADC_SRC ( clk_id ) |
ADAV80X_ICLK_CTRL1_ICLK2_SRC ( clk_id ) ;
iclk_ctrl2 = ADAV80X_ICLK_CTRL2_ICLK1_SRC ( clk_id ) ;
2013-09-27 17:18:25 +04:00
regmap_write ( adav80x - > regmap , ADAV80X_ICLK_CTRL1 ,
iclk_ctrl1 ) ;
regmap_write ( adav80x - > regmap , ADAV80X_ICLK_CTRL2 ,
iclk_ctrl2 ) ;
2011-06-27 19:04:01 +04:00
2014-02-18 19:22:16 +04:00
snd_soc_dapm_sync ( dapm ) ;
2011-06-27 19:04:01 +04:00
}
} else {
unsigned int mask ;
switch ( clk_id ) {
case ADAV80X_CLK_SYSCLK1 :
case ADAV80X_CLK_SYSCLK2 :
case ADAV80X_CLK_SYSCLK3 :
break ;
default :
return - EINVAL ;
}
clk_id - = ADAV80X_CLK_SYSCLK1 ;
mask = ADAV80X_PLL_OUTE_SYSCLKPD ( clk_id ) ;
if ( freq = = 0 ) {
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , ADAV80X_PLL_OUTE ,
mask , mask ) ;
2011-06-27 19:04:01 +04:00
adav80x - > sysclk_pd [ clk_id ] = true ;
} else {
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , ADAV80X_PLL_OUTE ,
mask , 0 ) ;
2011-06-27 19:04:01 +04:00
adav80x - > sysclk_pd [ clk_id ] = false ;
}
2014-02-18 19:22:16 +04:00
snd_soc_dapm_mutex_lock ( dapm ) ;
2011-06-27 19:04:01 +04:00
if ( adav80x - > sysclk_pd [ 0 ] )
2014-02-18 19:22:16 +04:00
snd_soc_dapm_disable_pin_unlocked ( dapm , " PLL1 " ) ;
2011-06-27 19:04:01 +04:00
else
2014-02-18 19:22:16 +04:00
snd_soc_dapm_force_enable_pin_unlocked ( dapm , " PLL1 " ) ;
2011-06-27 19:04:01 +04:00
if ( adav80x - > sysclk_pd [ 1 ] | | adav80x - > sysclk_pd [ 2 ] )
2014-02-18 19:22:16 +04:00
snd_soc_dapm_disable_pin_unlocked ( dapm , " PLL2 " ) ;
2011-06-27 19:04:01 +04:00
else
2014-02-18 19:22:16 +04:00
snd_soc_dapm_force_enable_pin_unlocked ( dapm , " PLL2 " ) ;
snd_soc_dapm_sync_unlocked ( dapm ) ;
2011-06-27 19:04:01 +04:00
2014-02-18 19:22:16 +04:00
snd_soc_dapm_mutex_unlock ( dapm ) ;
2011-06-27 19:04:01 +04:00
}
return 0 ;
}
2018-01-29 07:10:06 +03:00
static int adav80x_set_pll ( struct snd_soc_component * component , int pll_id ,
2011-06-27 19:04:01 +04:00
int source , unsigned int freq_in , unsigned int freq_out )
{
2018-01-29 07:10:06 +03:00
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( component ) ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
unsigned int pll_ctrl1 = 0 ;
unsigned int pll_ctrl2 = 0 ;
unsigned int pll_src ;
switch ( source ) {
case ADAV80X_PLL_SRC_XTAL :
case ADAV80X_PLL_SRC_XIN :
case ADAV80X_PLL_SRC_MCLKI :
break ;
default :
return - EINVAL ;
}
if ( ! freq_out )
return 0 ;
switch ( freq_in ) {
case 27000000 :
break ;
case 54000000 :
if ( source = = ADAV80X_PLL_SRC_XIN ) {
pll_ctrl1 | = ADAV80X_PLL_CTRL1_PLLDIV ;
break ;
}
2020-07-09 04:03:59 +03:00
fallthrough ;
2011-06-27 19:04:01 +04:00
default :
return - EINVAL ;
}
if ( freq_out > 12288000 ) {
pll_ctrl2 | = ADAV80X_PLL_CTRL2_DOUB ( pll_id ) ;
freq_out / = 2 ;
}
/* freq_out = sample_rate * 256 */
switch ( freq_out ) {
case 8192000 :
pll_ctrl2 | = ADAV80X_PLL_CTRL2_FS_32 ( pll_id ) ;
break ;
case 11289600 :
pll_ctrl2 | = ADAV80X_PLL_CTRL2_FS_44 ( pll_id ) ;
break ;
case 12288000 :
pll_ctrl2 | = ADAV80X_PLL_CTRL2_FS_48 ( pll_id ) ;
break ;
default :
return - EINVAL ;
}
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , ADAV80X_PLL_CTRL1 ,
ADAV80X_PLL_CTRL1_PLLDIV , pll_ctrl1 ) ;
regmap_update_bits ( adav80x - > regmap , ADAV80X_PLL_CTRL2 ,
2011-06-27 19:04:01 +04:00
ADAV80X_PLL_CTRL2_PLL_MASK ( pll_id ) , pll_ctrl2 ) ;
if ( source ! = adav80x - > pll_src ) {
if ( source = = ADAV80X_PLL_SRC_MCLKI )
pll_src = ADAV80X_PLL_CLK_SRC_PLL_MCLKI ( pll_id ) ;
else
pll_src = ADAV80X_PLL_CLK_SRC_PLL_XIN ( pll_id ) ;
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , ADAV80X_PLL_CLK_SRC ,
2011-06-27 19:04:01 +04:00
ADAV80X_PLL_CLK_SRC_PLL_MASK ( pll_id ) , pll_src ) ;
adav80x - > pll_src = source ;
2015-05-04 19:46:11 +03:00
snd_soc_dapm_sync ( dapm ) ;
2011-06-27 19:04:01 +04:00
}
return 0 ;
}
2018-01-29 07:10:06 +03:00
static int adav80x_set_bias_level ( struct snd_soc_component * component ,
2011-06-27 19:04:01 +04:00
enum snd_soc_bias_level level )
{
2018-01-29 07:10:06 +03:00
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
unsigned int mask = ADAV80X_DAC_CTRL1_PD ;
switch ( level ) {
case SND_SOC_BIAS_ON :
break ;
case SND_SOC_BIAS_PREPARE :
break ;
case SND_SOC_BIAS_STANDBY :
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , ADAV80X_DAC_CTRL1 , mask ,
0x00 ) ;
2011-06-27 19:04:01 +04:00
break ;
case SND_SOC_BIAS_OFF :
2013-09-27 17:18:25 +04:00
regmap_update_bits ( adav80x - > regmap , ADAV80X_DAC_CTRL1 , mask ,
mask ) ;
2011-06-27 19:04:01 +04:00
break ;
}
return 0 ;
}
/* Enforce the same sample rate on all audio interfaces */
static int adav80x_dai_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
2018-01-29 07:10:06 +03:00
struct snd_soc_component * component = dai - > component ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
2020-05-15 03:47:11 +03:00
if ( ! snd_soc_component_active ( component ) | | ! adav80x - > rate )
2011-06-27 19:04:01 +04:00
return 0 ;
2015-10-18 16:39:22 +03:00
return snd_pcm_hw_constraint_single ( substream - > runtime ,
SNDRV_PCM_HW_PARAM_RATE , adav80x - > rate ) ;
2011-06-27 19:04:01 +04:00
}
static void adav80x_dai_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
2018-01-29 07:10:06 +03:00
struct snd_soc_component * component = dai - > component ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
2020-05-15 03:47:11 +03:00
if ( ! snd_soc_component_active ( component ) )
2011-06-27 19:04:01 +04:00
adav80x - > rate = 0 ;
}
2011-11-23 17:11:21 +04:00
static const struct snd_soc_dai_ops adav80x_dai_ops = {
2011-06-27 19:04:01 +04:00
. set_fmt = adav80x_set_dai_fmt ,
. hw_params = adav80x_hw_params ,
. startup = adav80x_dai_startup ,
. shutdown = adav80x_dai_shutdown ,
} ;
# define ADAV80X_PLAYBACK_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000 )
# define ADAV80X_CAPTURE_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
# define ADAV80X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE )
static struct snd_soc_dai_driver adav80x_dais [ ] = {
{
. name = " adav80x-hifi " ,
. id = 0 ,
. playback = {
. stream_name = " HiFi Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = ADAV80X_PLAYBACK_RATES ,
. formats = ADAV80X_FORMATS ,
} ,
. capture = {
. stream_name = " HiFi Capture " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = ADAV80X_CAPTURE_RATES ,
. formats = ADAV80X_FORMATS ,
} ,
. ops = & adav80x_dai_ops ,
} ,
{
. name = " adav80x-aux " ,
. id = 1 ,
. playback = {
. stream_name = " Aux Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = ADAV80X_PLAYBACK_RATES ,
. formats = ADAV80X_FORMATS ,
} ,
. capture = {
. stream_name = " Aux Capture " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = ADAV80X_CAPTURE_RATES ,
. formats = ADAV80X_FORMATS ,
} ,
. ops = & adav80x_dai_ops ,
} ,
} ;
2018-01-29 07:10:06 +03:00
static int adav80x_probe ( struct snd_soc_component * component )
2011-06-27 19:04:01 +04:00
{
2018-01-29 07:10:06 +03:00
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( component ) ;
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2011-06-27 19:04:01 +04:00
/* Force PLLs on for SYSCLK output */
2015-05-04 19:46:11 +03:00
snd_soc_dapm_force_enable_pin ( dapm , " PLL1 " ) ;
snd_soc_dapm_force_enable_pin ( dapm , " PLL2 " ) ;
2011-06-27 19:04:01 +04:00
/* Power down S/PDIF receiver, since it is currently not supported */
2013-09-27 17:18:25 +04:00
regmap_write ( adav80x - > regmap , ADAV80X_PLL_OUTE , 0x20 ) ;
2011-06-27 19:04:01 +04:00
/* Disable DAC zero flag */
2013-09-27 17:18:25 +04:00
regmap_write ( adav80x - > regmap , ADAV80X_DAC_CTRL3 , 0x6 ) ;
2011-06-27 19:04:01 +04:00
2014-09-04 21:44:10 +04:00
return 0 ;
2011-06-27 19:04:01 +04:00
}
2018-01-29 07:10:06 +03:00
static int adav80x_resume ( struct snd_soc_component * component )
2011-06-27 19:04:01 +04:00
{
2018-01-29 07:10:06 +03:00
struct adav80x * adav80x = snd_soc_component_get_drvdata ( component ) ;
2013-09-27 17:18:25 +04:00
regcache_sync ( adav80x - > regmap ) ;
2011-06-27 19:04:01 +04:00
return 0 ;
}
2018-01-29 07:10:06 +03:00
static const struct snd_soc_component_driver adav80x_component_driver = {
. probe = adav80x_probe ,
. resume = adav80x_resume ,
. set_bias_level = adav80x_set_bias_level ,
. set_pll = adav80x_set_pll ,
. set_sysclk = adav80x_set_sysclk ,
. controls = adav80x_controls ,
. num_controls = ARRAY_SIZE ( adav80x_controls ) ,
. dapm_widgets = adav80x_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( adav80x_dapm_widgets ) ,
. dapm_routes = adav80x_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( adav80x_dapm_routes ) ,
. suspend_bias_off = 1 ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
2011-06-27 19:04:01 +04:00
} ;
2014-02-17 16:16:56 +04:00
int adav80x_bus_probe ( struct device * dev , struct regmap * regmap )
2011-06-27 19:04:01 +04:00
{
struct adav80x * adav80x ;
2013-09-27 17:18:25 +04:00
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
2014-02-17 16:16:55 +04:00
adav80x = devm_kzalloc ( dev , sizeof ( * adav80x ) , GFP_KERNEL ) ;
2011-06-27 19:04:01 +04:00
if ( ! adav80x )
return - ENOMEM ;
dev_set_drvdata ( dev , adav80x ) ;
2013-09-27 17:18:25 +04:00
adav80x - > regmap = regmap ;
2011-06-27 19:04:01 +04:00
2018-01-29 07:10:06 +03:00
return devm_snd_soc_register_component ( dev , & adav80x_component_driver ,
2011-06-27 19:04:01 +04:00
adav80x_dais , ARRAY_SIZE ( adav80x_dais ) ) ;
}
2014-02-17 16:16:56 +04:00
EXPORT_SYMBOL_GPL ( adav80x_bus_probe ) ;
2011-06-27 19:04:01 +04:00
2014-02-17 16:16:56 +04:00
const struct regmap_config adav80x_regmap_config = {
2013-09-27 17:18:25 +04:00
. val_bits = 8 ,
. pad_bits = 1 ,
. reg_bits = 7 ,
. max_register = ADAV80X_PLL_OUTE ,
2023-07-13 02:13:59 +03:00
. cache_type = REGCACHE_MAPLE ,
2013-09-27 17:18:25 +04:00
. reg_defaults = adav80x_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( adav80x_reg_defaults ) ,
} ;
2014-02-17 16:16:56 +04:00
EXPORT_SYMBOL_GPL ( adav80x_regmap_config ) ;
2011-06-27 19:04:01 +04:00
MODULE_DESCRIPTION ( " ASoC ADAV80x driver " ) ;
MODULE_AUTHOR ( " Lars-Peter Clausen <lars@metafoo.de> " ) ;
MODULE_AUTHOR ( " Yi Li <yi.li@analog.com>> " ) ;
MODULE_LICENSE ( " GPL " ) ;