2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-08-03 18:20:43 +04:00
/*
2016-01-10 02:38:03 +03:00
* PCM179X ASoC codec driver
2013-08-03 18:20:43 +04:00
*
* Copyright ( c ) Amarula Solutions B . V . 2013
*
* Michael Trimarchi < michael @ amarulasolutions . com >
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/kernel.h>
# include <linux/device.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/initval.h>
# include <sound/soc.h>
# include <sound/tlv.h>
2013-10-11 15:53:58 +04:00
# include <linux/of.h>
2013-08-03 18:20:43 +04:00
2016-01-10 02:38:03 +03:00
# include "pcm179x.h"
2013-08-03 18:20:43 +04:00
2016-01-10 02:38:03 +03:00
# define PCM179X_DAC_VOL_LEFT 0x10
# define PCM179X_DAC_VOL_RIGHT 0x11
# define PCM179X_FMT_CONTROL 0x12
# define PCM179X_MODE_CONTROL 0x13
# define PCM179X_SOFT_MUTE PCM179X_FMT_CONTROL
2013-08-03 18:20:43 +04:00
2016-01-10 02:38:03 +03:00
# define PCM179X_FMT_MASK 0x70
# define PCM179X_FMT_SHIFT 4
# define PCM179X_MUTE_MASK 0x01
# define PCM179X_MUTE_SHIFT 0
# define PCM179X_ATLD_ENABLE (1 << 7)
2013-08-03 18:20:43 +04:00
2016-01-10 02:38:03 +03:00
static const struct reg_default pcm179x_reg_defaults [ ] = {
2013-08-03 18:20:43 +04:00
{ 0x10 , 0xff } ,
{ 0x11 , 0xff } ,
{ 0x12 , 0x50 } ,
{ 0x13 , 0x00 } ,
{ 0x14 , 0x00 } ,
{ 0x15 , 0x01 } ,
{ 0x16 , 0x00 } ,
{ 0x17 , 0x00 } ,
} ;
2016-01-10 02:38:03 +03:00
static bool pcm179x_accessible_reg ( struct device * dev , unsigned int reg )
2013-08-03 18:20:43 +04:00
{
return reg > = 0x10 & & reg < = 0x17 ;
}
2016-06-13 18:39:45 +03:00
static bool pcm179x_writeable_reg ( struct device * dev , unsigned int reg )
2013-08-03 18:20:43 +04:00
{
bool accessible ;
2016-01-10 02:38:03 +03:00
accessible = pcm179x_accessible_reg ( dev , reg ) ;
2013-08-03 18:20:43 +04:00
return accessible & & reg ! = 0x16 & & reg ! = 0x17 ;
}
2016-01-10 02:38:03 +03:00
struct pcm179x_private {
2013-08-03 18:20:43 +04:00
struct regmap * regmap ;
unsigned int format ;
unsigned int rate ;
} ;
2016-01-10 02:38:03 +03:00
static int pcm179x_set_dai_fmt ( struct snd_soc_dai * codec_dai ,
2013-08-03 18:20:43 +04:00
unsigned int format )
{
2018-01-29 07:16:56 +03:00
struct snd_soc_component * component = codec_dai - > component ;
struct pcm179x_private * priv = snd_soc_component_get_drvdata ( component ) ;
2013-08-03 18:20:43 +04:00
priv - > format = format ;
return 0 ;
}
2020-07-09 04:56:39 +03:00
static int pcm179x_mute ( struct snd_soc_dai * dai , int mute , int direction )
2013-08-03 18:20:43 +04:00
{
2018-01-29 07:16:56 +03:00
struct snd_soc_component * component = dai - > component ;
struct pcm179x_private * priv = snd_soc_component_get_drvdata ( component ) ;
2013-08-03 18:20:43 +04:00
int ret ;
2016-01-10 02:38:03 +03:00
ret = regmap_update_bits ( priv - > regmap , PCM179X_SOFT_MUTE ,
PCM179X_MUTE_MASK , ! ! mute ) ;
2013-08-03 18:20:43 +04:00
if ( ret < 0 )
return ret ;
return 0 ;
}
2016-01-10 02:38:03 +03:00
static int pcm179x_hw_params ( struct snd_pcm_substream * substream ,
2013-08-03 18:20:43 +04:00
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2018-01-29 07:16:56 +03:00
struct snd_soc_component * component = dai - > component ;
struct pcm179x_private * priv = snd_soc_component_get_drvdata ( component ) ;
2013-08-03 18:20:43 +04:00
int val = 0 , ret ;
priv - > rate = params_rate ( params ) ;
switch ( priv - > format & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_RIGHT_J :
2014-01-24 20:07:16 +04:00
switch ( params_width ( params ) ) {
case 24 :
case 32 :
val = 2 ;
break ;
case 16 :
val = 0 ;
break ;
default :
return - EINVAL ;
}
2013-08-03 18:20:43 +04:00
break ;
case SND_SOC_DAIFMT_I2S :
2014-01-24 20:07:16 +04:00
switch ( params_width ( params ) ) {
case 24 :
case 32 :
val = 5 ;
break ;
case 16 :
val = 4 ;
break ;
default :
return - EINVAL ;
}
2013-08-03 18:20:43 +04:00
break ;
default :
2018-01-29 07:16:56 +03:00
dev_err ( component - > dev , " Invalid DAI format \n " ) ;
2013-08-03 18:20:43 +04:00
return - EINVAL ;
}
2016-01-10 02:38:03 +03:00
val = val < < PCM179X_FMT_SHIFT | PCM179X_ATLD_ENABLE ;
2013-08-03 18:20:43 +04:00
2016-01-10 02:38:03 +03:00
ret = regmap_update_bits ( priv - > regmap , PCM179X_FMT_CONTROL ,
PCM179X_FMT_MASK | PCM179X_ATLD_ENABLE , val ) ;
2013-08-03 18:20:43 +04:00
if ( ret < 0 )
return ret ;
return 0 ;
}
2016-01-10 02:38:03 +03:00
static const struct snd_soc_dai_ops pcm179x_dai_ops = {
. set_fmt = pcm179x_set_dai_fmt ,
. hw_params = pcm179x_hw_params ,
2020-07-09 04:56:39 +03:00
. mute_stream = pcm179x_mute ,
. no_capture_mute = 1 ,
2013-08-03 18:20:43 +04:00
} ;
2016-01-10 02:38:03 +03:00
static const DECLARE_TLV_DB_SCALE ( pcm179x_dac_tlv , - 12000 , 50 , 1 ) ;
2013-08-03 18:20:43 +04:00
2016-01-10 02:38:03 +03:00
static const struct snd_kcontrol_new pcm179x_controls [ ] = {
SOC_DOUBLE_R_RANGE_TLV ( " DAC Playback Volume " , PCM179X_DAC_VOL_LEFT ,
PCM179X_DAC_VOL_RIGHT , 0 , 0xf , 0xff , 0 ,
pcm179x_dac_tlv ) ,
SOC_SINGLE ( " DAC Invert Output Switch " , PCM179X_MODE_CONTROL , 7 , 1 , 0 ) ,
SOC_SINGLE ( " DAC Rolloff Filter Switch " , PCM179X_MODE_CONTROL , 1 , 1 , 0 ) ,
2013-08-03 18:20:43 +04:00
} ;
2016-01-10 02:38:03 +03:00
static const struct snd_soc_dapm_widget pcm179x_dapm_widgets [ ] = {
2013-08-13 21:30:00 +04:00
SND_SOC_DAPM_OUTPUT ( " IOUTL+ " ) ,
SND_SOC_DAPM_OUTPUT ( " IOUTL- " ) ,
SND_SOC_DAPM_OUTPUT ( " IOUTR+ " ) ,
SND_SOC_DAPM_OUTPUT ( " IOUTR- " ) ,
} ;
2016-01-10 02:38:03 +03:00
static const struct snd_soc_dapm_route pcm179x_dapm_routes [ ] = {
2013-08-13 21:30:00 +04:00
{ " IOUTL+ " , NULL , " Playback " } ,
{ " IOUTL- " , NULL , " Playback " } ,
{ " IOUTR+ " , NULL , " Playback " } ,
{ " IOUTR- " , NULL , " Playback " } ,
} ;
2016-01-10 02:38:03 +03:00
static struct snd_soc_dai_driver pcm179x_dai = {
. name = " pcm179x-hifi " ,
2013-08-03 18:20:43 +04:00
. playback = {
. stream_name = " Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
2016-01-22 15:39:55 +03:00
. rates = SNDRV_PCM_RATE_CONTINUOUS ,
. rate_min = 10000 ,
. rate_max = 200000 ,
2013-08-03 18:20:43 +04:00
. formats = PCM1792A_FORMATS , } ,
2016-01-10 02:38:03 +03:00
. ops = & pcm179x_dai_ops ,
2013-08-03 18:20:43 +04:00
} ;
2016-01-22 15:39:53 +03:00
const struct regmap_config pcm179x_regmap_config = {
2013-08-03 18:20:43 +04:00
. reg_bits = 8 ,
. val_bits = 8 ,
2013-10-12 13:26:49 +04:00
. max_register = 23 ,
2016-01-10 02:38:03 +03:00
. reg_defaults = pcm179x_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( pcm179x_reg_defaults ) ,
. writeable_reg = pcm179x_writeable_reg ,
. readable_reg = pcm179x_accessible_reg ,
2013-08-03 18:20:43 +04:00
} ;
2016-01-22 15:39:53 +03:00
EXPORT_SYMBOL_GPL ( pcm179x_regmap_config ) ;
2013-08-03 18:20:43 +04:00
2018-01-29 07:16:56 +03:00
static const struct snd_soc_component_driver soc_component_dev_pcm179x = {
. controls = pcm179x_controls ,
. num_controls = ARRAY_SIZE ( pcm179x_controls ) ,
. dapm_widgets = pcm179x_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( pcm179x_dapm_widgets ) ,
. dapm_routes = pcm179x_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( pcm179x_dapm_routes ) ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
2013-08-03 18:20:43 +04:00
} ;
2016-01-22 15:39:53 +03:00
int pcm179x_common_init ( struct device * dev , struct regmap * regmap )
2013-08-03 18:20:43 +04:00
{
2016-01-10 02:38:03 +03:00
struct pcm179x_private * pcm179x ;
2013-08-03 18:20:43 +04:00
2016-01-22 15:39:53 +03:00
pcm179x = devm_kzalloc ( dev , sizeof ( struct pcm179x_private ) ,
2013-08-03 18:20:43 +04:00
GFP_KERNEL ) ;
2016-01-10 02:38:03 +03:00
if ( ! pcm179x )
2013-08-03 18:20:43 +04:00
return - ENOMEM ;
2016-01-22 15:39:53 +03:00
pcm179x - > regmap = regmap ;
dev_set_drvdata ( dev , pcm179x ) ;
2013-08-03 18:20:43 +04:00
2018-01-29 07:16:56 +03:00
return devm_snd_soc_register_component ( dev ,
& soc_component_dev_pcm179x , & pcm179x_dai , 1 ) ;
2013-08-03 18:20:43 +04:00
}
2016-01-22 15:39:53 +03:00
EXPORT_SYMBOL_GPL ( pcm179x_common_init ) ;
2013-08-03 18:20:43 +04:00
2016-01-10 02:38:03 +03:00
MODULE_DESCRIPTION ( " ASoC PCM179X driver " ) ;
2013-08-03 18:20:43 +04:00
MODULE_AUTHOR ( " Michael Trimarchi <michael@amarulasolutions.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;