2018-12-20 18:36:35 +03:00
// SPDX-License-Identifier: GPL-2.0
//
// rk3328 ALSA SoC Audio driver
//
// Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/regmap.h>
# include <linux/mfd/syscon.h>
# include <sound/dmaengine_pcm.h>
# include <sound/pcm_params.h>
# include "rk3328_codec.h"
/*
* volume setting
* 0 : - 39 dB
* 26 : 0 dB
* 31 : 6 dB
* Step : 1.5 dB
*/
# define OUT_VOLUME (0x18)
# define RK3328_GRF_SOC_CON2 (0x0408)
# define RK3328_GRF_SOC_CON10 (0x0428)
# define INITIAL_FREQ (11289600)
struct rk3328_codec_priv {
struct regmap * regmap ;
struct regmap * grf ;
struct clk * mclk ;
struct clk * pclk ;
unsigned int sclk ;
int spk_depop_time ; /* msec */
} ;
static const struct reg_default rk3328_codec_reg_defaults [ ] = {
{ CODEC_RESET , 0x03 } ,
{ DAC_INIT_CTRL1 , 0x00 } ,
{ DAC_INIT_CTRL2 , 0x50 } ,
{ DAC_INIT_CTRL3 , 0x0e } ,
{ DAC_PRECHARGE_CTRL , 0x01 } ,
{ DAC_PWR_CTRL , 0x00 } ,
{ DAC_CLK_CTRL , 0x00 } ,
{ HPMIX_CTRL , 0x00 } ,
{ HPOUT_CTRL , 0x00 } ,
{ HPOUTL_GAIN_CTRL , 0x00 } ,
{ HPOUTR_GAIN_CTRL , 0x00 } ,
{ HPOUT_POP_CTRL , 0x11 } ,
} ;
static int rk3328_codec_reset ( struct rk3328_codec_priv * rk3328 )
{
regmap_write ( rk3328 - > regmap , CODEC_RESET , 0x00 ) ;
mdelay ( 10 ) ;
regmap_write ( rk3328 - > regmap , CODEC_RESET , 0x03 ) ;
return 0 ;
}
static int rk3328_set_dai_fmt ( struct snd_soc_dai * dai , unsigned int fmt )
{
struct rk3328_codec_priv * rk3328 =
snd_soc_component_get_drvdata ( dai - > component ) ;
unsigned int val ;
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBS_CFS :
val = PIN_DIRECTION_IN | DAC_I2S_MODE_SLAVE ;
break ;
case SND_SOC_DAIFMT_CBM_CFM :
val = PIN_DIRECTION_OUT | DAC_I2S_MODE_MASTER ;
break ;
default :
return - EINVAL ;
}
regmap_update_bits ( rk3328 - > regmap , DAC_INIT_CTRL1 ,
PIN_DIRECTION_MASK | DAC_I2S_MODE_MASK , val ) ;
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_DSP_A :
case SND_SOC_DAIFMT_DSP_B :
val = DAC_MODE_PCM ;
break ;
case SND_SOC_DAIFMT_I2S :
val = DAC_MODE_I2S ;
break ;
case SND_SOC_DAIFMT_RIGHT_J :
val = DAC_MODE_RJM ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
val = DAC_MODE_LJM ;
break ;
default :
return - EINVAL ;
}
regmap_update_bits ( rk3328 - > regmap , DAC_INIT_CTRL2 ,
DAC_MODE_MASK , val ) ;
return 0 ;
}
static void rk3328_analog_output ( struct rk3328_codec_priv * rk3328 , int mute )
{
unsigned int val = BIT ( 17 ) ;
if ( mute )
val | = BIT ( 1 ) ;
regmap_write ( rk3328 - > grf , RK3328_GRF_SOC_CON10 , val ) ;
}
static int rk3328_digital_mute ( struct snd_soc_dai * dai , int mute )
{
struct rk3328_codec_priv * rk3328 =
snd_soc_component_get_drvdata ( dai - > component ) ;
unsigned int val ;
if ( mute )
val = HPOUTL_MUTE | HPOUTR_MUTE ;
else
val = HPOUTL_UNMUTE | HPOUTR_UNMUTE ;
regmap_update_bits ( rk3328 - > regmap , HPOUT_CTRL ,
HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK , val ) ;
return 0 ;
}
static int rk3328_codec_power_on ( struct rk3328_codec_priv * rk3328 , int wait_ms )
{
regmap_update_bits ( rk3328 - > regmap , DAC_PRECHARGE_CTRL ,
DAC_CHARGE_XCHARGE_MASK , DAC_CHARGE_PRECHARGE ) ;
mdelay ( 10 ) ;
regmap_update_bits ( rk3328 - > regmap , DAC_PRECHARGE_CTRL ,
DAC_CHARGE_CURRENT_ALL_MASK ,
DAC_CHARGE_CURRENT_ALL_ON ) ;
mdelay ( wait_ms ) ;
return 0 ;
}
static int rk3328_codec_power_off ( struct rk3328_codec_priv * rk3328 , int wait_ms )
{
regmap_update_bits ( rk3328 - > regmap , DAC_PRECHARGE_CTRL ,
DAC_CHARGE_XCHARGE_MASK , DAC_CHARGE_DISCHARGE ) ;
mdelay ( 10 ) ;
regmap_update_bits ( rk3328 - > regmap , DAC_PRECHARGE_CTRL ,
DAC_CHARGE_CURRENT_ALL_MASK ,
DAC_CHARGE_CURRENT_ALL_ON ) ;
mdelay ( wait_ms ) ;
return 0 ;
}
static const struct rk3328_reg_msk_val playback_open_list [ ] = {
{ DAC_PWR_CTRL , DAC_PWR_MASK , DAC_PWR_ON } ,
{ DAC_PWR_CTRL , DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK ,
DACL_PATH_REFV_ON | DACR_PATH_REFV_ON } ,
{ DAC_PWR_CTRL , HPOUTL_ZERO_CROSSING_MASK | HPOUTR_ZERO_CROSSING_MASK ,
HPOUTL_ZERO_CROSSING_ON | HPOUTR_ZERO_CROSSING_ON } ,
{ HPOUT_POP_CTRL , HPOUTR_POP_MASK | HPOUTL_POP_MASK ,
HPOUTR_POP_WORK | HPOUTL_POP_WORK } ,
{ HPMIX_CTRL , HPMIXL_MASK | HPMIXR_MASK , HPMIXL_EN | HPMIXR_EN } ,
{ HPMIX_CTRL , HPMIXL_INIT_MASK | HPMIXR_INIT_MASK ,
HPMIXL_INIT_EN | HPMIXR_INIT_EN } ,
{ HPOUT_CTRL , HPOUTL_MASK | HPOUTR_MASK , HPOUTL_EN | HPOUTR_EN } ,
{ HPOUT_CTRL , HPOUTL_INIT_MASK | HPOUTR_INIT_MASK ,
HPOUTL_INIT_EN | HPOUTR_INIT_EN } ,
{ DAC_CLK_CTRL , DACL_REFV_MASK | DACR_REFV_MASK ,
DACL_REFV_ON | DACR_REFV_ON } ,
{ DAC_CLK_CTRL , DACL_CLK_MASK | DACR_CLK_MASK ,
DACL_CLK_ON | DACR_CLK_ON } ,
{ DAC_CLK_CTRL , DACL_MASK | DACR_MASK , DACL_ON | DACR_ON } ,
{ DAC_CLK_CTRL , DACL_INIT_MASK | DACR_INIT_MASK ,
DACL_INIT_ON | DACR_INIT_ON } ,
{ DAC_SELECT , DACL_SELECT_MASK | DACR_SELECT_MASK ,
DACL_SELECT | DACR_SELECT } ,
{ HPMIX_CTRL , HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK ,
HPMIXL_INIT2_EN | HPMIXR_INIT2_EN } ,
{ HPOUT_CTRL , HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK ,
HPOUTL_UNMUTE | HPOUTR_UNMUTE } ,
} ;
static int rk3328_codec_open_playback ( struct rk3328_codec_priv * rk3328 )
{
int i ;
regmap_update_bits ( rk3328 - > regmap , DAC_PRECHARGE_CTRL ,
DAC_CHARGE_CURRENT_ALL_MASK ,
DAC_CHARGE_CURRENT_I ) ;
for ( i = 0 ; i < ARRAY_SIZE ( playback_open_list ) ; i + + ) {
regmap_update_bits ( rk3328 - > regmap ,
playback_open_list [ i ] . reg ,
playback_open_list [ i ] . msk ,
playback_open_list [ i ] . val ) ;
mdelay ( 1 ) ;
}
msleep ( rk3328 - > spk_depop_time ) ;
rk3328_analog_output ( rk3328 , 1 ) ;
regmap_update_bits ( rk3328 - > regmap , HPOUTL_GAIN_CTRL ,
HPOUTL_GAIN_MASK , OUT_VOLUME ) ;
regmap_update_bits ( rk3328 - > regmap , HPOUTR_GAIN_CTRL ,
HPOUTR_GAIN_MASK , OUT_VOLUME ) ;
return 0 ;
}
static const struct rk3328_reg_msk_val playback_close_list [ ] = {
{ HPMIX_CTRL , HPMIXL_INIT2_MASK | HPMIXR_INIT2_MASK ,
HPMIXL_INIT2_DIS | HPMIXR_INIT2_DIS } ,
{ DAC_SELECT , DACL_SELECT_MASK | DACR_SELECT_MASK ,
DACL_UNSELECT | DACR_UNSELECT } ,
{ HPOUT_CTRL , HPOUTL_MUTE_MASK | HPOUTR_MUTE_MASK ,
HPOUTL_MUTE | HPOUTR_MUTE } ,
{ HPOUT_CTRL , HPOUTL_INIT_MASK | HPOUTR_INIT_MASK ,
HPOUTL_INIT_DIS | HPOUTR_INIT_DIS } ,
{ HPOUT_CTRL , HPOUTL_MASK | HPOUTR_MASK , HPOUTL_DIS | HPOUTR_DIS } ,
{ HPMIX_CTRL , HPMIXL_MASK | HPMIXR_MASK , HPMIXL_DIS | HPMIXR_DIS } ,
{ DAC_CLK_CTRL , DACL_MASK | DACR_MASK , DACL_OFF | DACR_OFF } ,
{ DAC_CLK_CTRL , DACL_CLK_MASK | DACR_CLK_MASK ,
DACL_CLK_OFF | DACR_CLK_OFF } ,
{ DAC_CLK_CTRL , DACL_REFV_MASK | DACR_REFV_MASK ,
DACL_REFV_OFF | DACR_REFV_OFF } ,
{ HPOUT_POP_CTRL , HPOUTR_POP_MASK | HPOUTL_POP_MASK ,
HPOUTR_POP_XCHARGE | HPOUTL_POP_XCHARGE } ,
{ DAC_PWR_CTRL , DACL_PATH_REFV_MASK | DACR_PATH_REFV_MASK ,
DACL_PATH_REFV_OFF | DACR_PATH_REFV_OFF } ,
{ DAC_PWR_CTRL , DAC_PWR_MASK , DAC_PWR_OFF } ,
{ HPMIX_CTRL , HPMIXL_INIT_MASK | HPMIXR_INIT_MASK ,
HPMIXL_INIT_DIS | HPMIXR_INIT_DIS } ,
{ DAC_CLK_CTRL , DACL_INIT_MASK | DACR_INIT_MASK ,
DACL_INIT_OFF | DACR_INIT_OFF } ,
} ;
static int rk3328_codec_close_playback ( struct rk3328_codec_priv * rk3328 )
{
size_t i ;
rk3328_analog_output ( rk3328 , 0 ) ;
regmap_update_bits ( rk3328 - > regmap , HPOUTL_GAIN_CTRL ,
HPOUTL_GAIN_MASK , 0 ) ;
regmap_update_bits ( rk3328 - > regmap , HPOUTR_GAIN_CTRL ,
HPOUTR_GAIN_MASK , 0 ) ;
for ( i = 0 ; i < ARRAY_SIZE ( playback_close_list ) ; i + + ) {
regmap_update_bits ( rk3328 - > regmap ,
playback_close_list [ i ] . reg ,
playback_close_list [ i ] . msk ,
playback_close_list [ i ] . val ) ;
mdelay ( 1 ) ;
}
2018-12-20 18:36:36 +03:00
/* Workaround for silence when changed Fs 48 -> 44.1kHz */
rk3328_codec_reset ( rk3328 ) ;
2018-12-20 18:36:35 +03:00
regmap_update_bits ( rk3328 - > regmap , DAC_PRECHARGE_CTRL ,
DAC_CHARGE_CURRENT_ALL_MASK ,
2018-12-20 18:36:36 +03:00
DAC_CHARGE_CURRENT_ALL_ON ) ;
2018-12-20 18:36:35 +03:00
return 0 ;
}
static int rk3328_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct rk3328_codec_priv * rk3328 =
snd_soc_component_get_drvdata ( dai - > component ) ;
unsigned int val = 0 ;
switch ( params_format ( params ) ) {
case SNDRV_PCM_FORMAT_S16_LE :
val = DAC_VDL_16BITS ;
break ;
case SNDRV_PCM_FORMAT_S20_3LE :
val = DAC_VDL_20BITS ;
break ;
case SNDRV_PCM_FORMAT_S24_LE :
val = DAC_VDL_24BITS ;
break ;
case SNDRV_PCM_FORMAT_S32_LE :
val = DAC_VDL_32BITS ;
break ;
default :
return - EINVAL ;
}
regmap_update_bits ( rk3328 - > regmap , DAC_INIT_CTRL2 , DAC_VDL_MASK , val ) ;
val = DAC_WL_32BITS | DAC_RST_DIS ;
regmap_update_bits ( rk3328 - > regmap , DAC_INIT_CTRL3 ,
DAC_WL_MASK | DAC_RST_MASK , val ) ;
return 0 ;
}
static int rk3328_pcm_startup ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct rk3328_codec_priv * rk3328 =
snd_soc_component_get_drvdata ( dai - > component ) ;
return rk3328_codec_open_playback ( rk3328 ) ;
}
static void rk3328_pcm_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct rk3328_codec_priv * rk3328 =
snd_soc_component_get_drvdata ( dai - > component ) ;
rk3328_codec_close_playback ( rk3328 ) ;
}
static const struct snd_soc_dai_ops rk3328_dai_ops = {
. hw_params = rk3328_hw_params ,
. set_fmt = rk3328_set_dai_fmt ,
. digital_mute = rk3328_digital_mute ,
. startup = rk3328_pcm_startup ,
. shutdown = rk3328_pcm_shutdown ,
} ;
static struct snd_soc_dai_driver rk3328_dai [ ] = {
{
. name = " rk3328-hifi " ,
. id = RK3328_HIFI ,
. playback = {
. stream_name = " HIFI Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = SNDRV_PCM_RATE_8000_96000 ,
. formats = ( SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE ) ,
} ,
. capture = {
. stream_name = " HIFI Capture " ,
. channels_min = 2 ,
. channels_max = 8 ,
. rates = SNDRV_PCM_RATE_8000_96000 ,
. formats = ( SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE ) ,
} ,
. ops = & rk3328_dai_ops ,
} ,
} ;
static int rk3328_codec_probe ( struct snd_soc_component * component )
{
struct rk3328_codec_priv * rk3328 =
snd_soc_component_get_drvdata ( component ) ;
rk3328_codec_reset ( rk3328 ) ;
rk3328_codec_power_on ( rk3328 , 0 ) ;
return 0 ;
}
static void rk3328_codec_remove ( struct snd_soc_component * component )
{
struct rk3328_codec_priv * rk3328 =
snd_soc_component_get_drvdata ( component ) ;
rk3328_codec_close_playback ( rk3328 ) ;
rk3328_codec_power_off ( rk3328 , 0 ) ;
}
static const struct snd_soc_component_driver soc_codec_rk3328 = {
. probe = rk3328_codec_probe ,
. remove = rk3328_codec_remove ,
} ;
static bool rk3328_codec_write_read_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case CODEC_RESET :
case DAC_INIT_CTRL1 :
case DAC_INIT_CTRL2 :
case DAC_INIT_CTRL3 :
case DAC_PRECHARGE_CTRL :
case DAC_PWR_CTRL :
case DAC_CLK_CTRL :
case HPMIX_CTRL :
case DAC_SELECT :
case HPOUT_CTRL :
case HPOUTL_GAIN_CTRL :
case HPOUTR_GAIN_CTRL :
case HPOUT_POP_CTRL :
return true ;
default :
return false ;
}
}
static bool rk3328_codec_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case CODEC_RESET :
return true ;
default :
return false ;
}
}
static const struct regmap_config rk3328_codec_regmap_config = {
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. max_register = HPOUT_POP_CTRL ,
. writeable_reg = rk3328_codec_write_read_reg ,
. readable_reg = rk3328_codec_write_read_reg ,
. volatile_reg = rk3328_codec_volatile_reg ,
. reg_defaults = rk3328_codec_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( rk3328_codec_reg_defaults ) ,
. cache_type = REGCACHE_FLAT ,
} ;
static int rk3328_platform_probe ( struct platform_device * pdev )
{
struct device_node * rk3328_np = pdev - > dev . of_node ;
struct rk3328_codec_priv * rk3328 ;
struct resource * res ;
struct regmap * grf ;
void __iomem * base ;
int ret = 0 ;
rk3328 = devm_kzalloc ( & pdev - > dev , sizeof ( * rk3328 ) , GFP_KERNEL ) ;
if ( ! rk3328 )
return - ENOMEM ;
grf = syscon_regmap_lookup_by_phandle ( rk3328_np ,
" rockchip,grf " ) ;
if ( IS_ERR ( grf ) ) {
dev_err ( & pdev - > dev , " missing 'rockchip,grf' \n " ) ;
return PTR_ERR ( grf ) ;
}
rk3328 - > grf = grf ;
/* enable i2s_acodec_en */
regmap_write ( grf , RK3328_GRF_SOC_CON2 ,
( BIT ( 14 ) < < 16 | BIT ( 14 ) ) ) ;
ret = of_property_read_u32 ( rk3328_np , " spk-depop-time-ms " ,
& rk3328 - > spk_depop_time ) ;
if ( ret < 0 ) {
dev_info ( & pdev - > dev , " spk_depop_time use default value. \n " ) ;
rk3328 - > spk_depop_time = 200 ;
}
rk3328_analog_output ( rk3328 , 0 ) ;
rk3328 - > mclk = devm_clk_get ( & pdev - > dev , " mclk " ) ;
if ( IS_ERR ( rk3328 - > mclk ) )
return PTR_ERR ( rk3328 - > mclk ) ;
ret = clk_prepare_enable ( rk3328 - > mclk ) ;
if ( ret )
return ret ;
clk_set_rate ( rk3328 - > mclk , INITIAL_FREQ ) ;
rk3328 - > pclk = devm_clk_get ( & pdev - > dev , " pclk " ) ;
if ( IS_ERR ( rk3328 - > pclk ) ) {
dev_err ( & pdev - > dev , " can't get acodec pclk \n " ) ;
return PTR_ERR ( rk3328 - > pclk ) ;
}
ret = clk_prepare_enable ( rk3328 - > pclk ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to enable acodec pclk \n " ) ;
return ret ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
rk3328 - > regmap = devm_regmap_init_mmio ( & pdev - > dev , base ,
& rk3328_codec_regmap_config ) ;
if ( IS_ERR ( rk3328 - > regmap ) )
return PTR_ERR ( rk3328 - > regmap ) ;
platform_set_drvdata ( pdev , rk3328 ) ;
return devm_snd_soc_register_component ( & pdev - > dev , & soc_codec_rk3328 ,
rk3328_dai ,
ARRAY_SIZE ( rk3328_dai ) ) ;
}
static const struct of_device_id rk3328_codec_of_match [ ] = {
{ . compatible = " rockchip,rk3328-codec " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , rk3328_codec_of_match ) ;
static struct platform_driver rk3328_codec_driver = {
. driver = {
. name = " rk3328-codec " ,
. of_match_table = of_match_ptr ( rk3328_codec_of_match ) ,
} ,
. probe = rk3328_platform_probe ,
} ;
module_platform_driver ( rk3328_codec_driver ) ;
MODULE_AUTHOR ( " Sugar Zhang <sugar.zhang@rock-chips.com> " ) ;
MODULE_DESCRIPTION ( " ASoC rk3328 codec driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;