2013-08-03 16:20:43 +02:00
/*
* PCM1792A ASoC codec driver
*
* Copyright ( c ) Amarula Solutions B . V . 2013
*
* Michael Trimarchi < michael @ amarulasolutions . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/spi/spi.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 17:23:58 +05:30
# include <linux/of.h>
2013-08-03 16:20:43 +02:00
# include <linux/of_device.h>
# include "pcm1792a.h"
# define PCM1792A_DAC_VOL_LEFT 0x10
# define PCM1792A_DAC_VOL_RIGHT 0x11
# define PCM1792A_FMT_CONTROL 0x12
2014-07-28 14:26:49 +02:00
# define PCM1792A_MODE_CONTROL 0x13
2013-08-03 16:20:43 +02:00
# define PCM1792A_SOFT_MUTE PCM1792A_FMT_CONTROL
# define PCM1792A_FMT_MASK 0x70
# define PCM1792A_FMT_SHIFT 4
# define PCM1792A_MUTE_MASK 0x01
# define PCM1792A_MUTE_SHIFT 0
# define PCM1792A_ATLD_ENABLE (1 << 7)
static const struct reg_default pcm1792a_reg_defaults [ ] = {
{ 0x10 , 0xff } ,
{ 0x11 , 0xff } ,
{ 0x12 , 0x50 } ,
{ 0x13 , 0x00 } ,
{ 0x14 , 0x00 } ,
{ 0x15 , 0x01 } ,
{ 0x16 , 0x00 } ,
{ 0x17 , 0x00 } ,
} ;
static bool pcm1792a_accessible_reg ( struct device * dev , unsigned int reg )
{
return reg > = 0x10 & & reg < = 0x17 ;
}
static bool pcm1792a_writeable_reg ( struct device * dev , unsigned register reg )
{
bool accessible ;
accessible = pcm1792a_accessible_reg ( dev , reg ) ;
return accessible & & reg ! = 0x16 & & reg ! = 0x17 ;
}
struct pcm1792a_private {
struct regmap * regmap ;
unsigned int format ;
unsigned int rate ;
} ;
static int pcm1792a_set_dai_fmt ( struct snd_soc_dai * codec_dai ,
unsigned int format )
{
struct snd_soc_codec * codec = codec_dai - > codec ;
struct pcm1792a_private * priv = snd_soc_codec_get_drvdata ( codec ) ;
priv - > format = format ;
return 0 ;
}
static int pcm1792a_digital_mute ( struct snd_soc_dai * dai , int mute )
{
struct snd_soc_codec * codec = dai - > codec ;
struct pcm1792a_private * priv = snd_soc_codec_get_drvdata ( codec ) ;
int ret ;
ret = regmap_update_bits ( priv - > regmap , PCM1792A_SOFT_MUTE ,
PCM1792A_MUTE_MASK , ! ! mute ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int pcm1792a_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct snd_soc_codec * codec = dai - > codec ;
struct pcm1792a_private * priv = snd_soc_codec_get_drvdata ( codec ) ;
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 16:07:16 +00:00
switch ( params_width ( params ) ) {
case 24 :
case 32 :
val = 2 ;
break ;
case 16 :
val = 0 ;
break ;
default :
return - EINVAL ;
}
2013-08-03 16:20:43 +02:00
break ;
case SND_SOC_DAIFMT_I2S :
2014-01-24 16:07:16 +00:00
switch ( params_width ( params ) ) {
case 24 :
case 32 :
val = 5 ;
break ;
case 16 :
val = 4 ;
break ;
default :
return - EINVAL ;
}
2013-08-03 16:20:43 +02:00
break ;
default :
dev_err ( codec - > dev , " Invalid DAI format \n " ) ;
return - EINVAL ;
}
val = val < < PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE ;
ret = regmap_update_bits ( priv - > regmap , PCM1792A_FMT_CONTROL ,
PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE , val ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static const struct snd_soc_dai_ops pcm1792a_dai_ops = {
. set_fmt = pcm1792a_set_dai_fmt ,
. hw_params = pcm1792a_hw_params ,
. digital_mute = pcm1792a_digital_mute ,
} ;
static const DECLARE_TLV_DB_SCALE ( pcm1792a_dac_tlv , - 12000 , 50 , 1 ) ;
static const struct snd_kcontrol_new pcm1792a_controls [ ] = {
SOC_DOUBLE_R_RANGE_TLV ( " DAC Playback Volume " , PCM1792A_DAC_VOL_LEFT ,
PCM1792A_DAC_VOL_RIGHT , 0 , 0xf , 0xff , 0 ,
pcm1792a_dac_tlv ) ,
2014-07-28 14:26:49 +02:00
SOC_SINGLE ( " DAC Invert Output Switch " , PCM1792A_MODE_CONTROL , 7 , 1 , 0 ) ,
SOC_SINGLE ( " DAC Rolloff Filter Switch " , PCM1792A_MODE_CONTROL , 1 , 1 , 0 ) ,
2013-08-03 16:20:43 +02:00
} ;
2013-08-13 18:30:00 +01:00
static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets [ ] = {
SND_SOC_DAPM_OUTPUT ( " IOUTL+ " ) ,
SND_SOC_DAPM_OUTPUT ( " IOUTL- " ) ,
SND_SOC_DAPM_OUTPUT ( " IOUTR+ " ) ,
SND_SOC_DAPM_OUTPUT ( " IOUTR- " ) ,
} ;
static const struct snd_soc_dapm_route pcm1792a_dapm_routes [ ] = {
{ " IOUTL+ " , NULL , " Playback " } ,
{ " IOUTL- " , NULL , " Playback " } ,
{ " IOUTR+ " , NULL , " Playback " } ,
{ " IOUTR- " , NULL , " Playback " } ,
} ;
2013-08-03 16:20:43 +02:00
static struct snd_soc_dai_driver pcm1792a_dai = {
. name = " pcm1792a-hifi " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = PCM1792A_RATES ,
. formats = PCM1792A_FORMATS , } ,
. ops = & pcm1792a_dai_ops ,
} ;
static const struct of_device_id pcm1792a_of_match [ ] = {
{ . compatible = " ti,pcm1792a " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , pcm1792a_of_match ) ;
static const struct regmap_config pcm1792a_regmap = {
. reg_bits = 8 ,
. val_bits = 8 ,
2013-10-12 17:26:49 +08:00
. max_register = 23 ,
2013-08-03 16:20:43 +02:00
. reg_defaults = pcm1792a_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( pcm1792a_reg_defaults ) ,
. writeable_reg = pcm1792a_writeable_reg ,
. readable_reg = pcm1792a_accessible_reg ,
} ;
static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = {
. controls = pcm1792a_controls ,
. num_controls = ARRAY_SIZE ( pcm1792a_controls ) ,
2013-08-13 18:30:00 +01:00
. dapm_widgets = pcm1792a_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( pcm1792a_dapm_widgets ) ,
. dapm_routes = pcm1792a_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( pcm1792a_dapm_routes ) ,
2013-08-03 16:20:43 +02:00
} ;
static int pcm1792a_spi_probe ( struct spi_device * spi )
{
struct pcm1792a_private * pcm1792a ;
int ret ;
pcm1792a = devm_kzalloc ( & spi - > dev , sizeof ( struct pcm1792a_private ) ,
GFP_KERNEL ) ;
if ( ! pcm1792a )
return - ENOMEM ;
spi_set_drvdata ( spi , pcm1792a ) ;
pcm1792a - > regmap = devm_regmap_init_spi ( spi , & pcm1792a_regmap ) ;
if ( IS_ERR ( pcm1792a - > regmap ) ) {
ret = PTR_ERR ( pcm1792a - > regmap ) ;
dev_err ( & spi - > dev , " Failed to register regmap: %d \n " , ret ) ;
return ret ;
}
return snd_soc_register_codec ( & spi - > dev ,
& soc_codec_dev_pcm1792a , & pcm1792a_dai , 1 ) ;
}
static int pcm1792a_spi_remove ( struct spi_device * spi )
{
snd_soc_unregister_codec ( & spi - > dev ) ;
return 0 ;
}
static const struct spi_device_id pcm1792a_spi_ids [ ] = {
{ " pcm1792a " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( spi , pcm1792a_spi_ids ) ;
static struct spi_driver pcm1792a_codec_driver = {
. driver = {
. name = " pcm1792a " ,
. owner = THIS_MODULE ,
2013-08-05 18:20:53 +01:00
. of_match_table = of_match_ptr ( pcm1792a_of_match ) ,
2013-08-03 16:20:43 +02:00
} ,
. id_table = pcm1792a_spi_ids ,
. probe = pcm1792a_spi_probe ,
. remove = pcm1792a_spi_remove ,
} ;
module_spi_driver ( pcm1792a_codec_driver ) ;
MODULE_DESCRIPTION ( " ASoC PCM1792A driver " ) ;
MODULE_AUTHOR ( " Michael Trimarchi <michael@amarulasolutions.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;