2019-06-04 10:10:56 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-02-27 16:47:23 +01:00
/*
* Copyright ( c ) 2017 BayLibre , SAS .
* Author : Jerome Brunet < jbrunet @ baylibre . com >
*/
2018-06-29 17:09:21 +02:00
# include <linux/of_platform.h>
2017-02-27 16:47:23 +01:00
# include <linux/module.h>
# include <sound/soc.h>
/*
* The everest 7134 is a very simple DA converter with no register
*/
2018-06-29 17:09:21 +02:00
struct es7134_clock_mode {
unsigned int rate_min ;
unsigned int rate_max ;
unsigned int * mclk_fs ;
unsigned int mclk_fs_num ;
} ;
struct es7134_chip {
2018-07-03 17:06:00 +02:00
struct snd_soc_dai_driver * dai_drv ;
2018-06-29 17:09:21 +02:00
const struct es7134_clock_mode * modes ;
unsigned int mode_num ;
2018-07-03 17:05:59 +02:00
const struct snd_soc_dapm_widget * extra_widgets ;
unsigned int extra_widget_num ;
const struct snd_soc_dapm_route * extra_routes ;
unsigned int extra_route_num ;
2018-06-29 17:09:21 +02:00
} ;
struct es7134_data {
unsigned int mclk ;
const struct es7134_chip * chip ;
} ;
static int es7134_check_mclk ( struct snd_soc_dai * dai ,
struct es7134_data * priv ,
unsigned int rate )
{
unsigned int mfs = priv - > mclk / rate ;
int i , j ;
for ( i = 0 ; i < priv - > chip - > mode_num ; i + + ) {
const struct es7134_clock_mode * mode = & priv - > chip - > modes [ i ] ;
if ( rate < mode - > rate_min | | rate > mode - > rate_max )
continue ;
for ( j = 0 ; j < mode - > mclk_fs_num ; j + + ) {
if ( mode - > mclk_fs [ j ] = = mfs )
return 0 ;
}
dev_err ( dai - > dev , " unsupported mclk_fs %u for rate %u \n " ,
mfs , rate ) ;
return - EINVAL ;
}
/* should not happen */
dev_err ( dai - > dev , " unsupported rate: %u \n " , rate ) ;
return - EINVAL ;
}
static int es7134_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct es7134_data * priv = snd_soc_dai_get_drvdata ( dai ) ;
/* mclk has not been provided, assume it is OK */
if ( ! priv - > mclk )
return 0 ;
return es7134_check_mclk ( dai , priv , params_rate ( params ) ) ;
}
static int es7134_set_sysclk ( struct snd_soc_dai * dai , int clk_id ,
unsigned int freq , int dir )
{
struct es7134_data * priv = snd_soc_dai_get_drvdata ( dai ) ;
if ( dir = = SND_SOC_CLOCK_IN & & clk_id = = 0 ) {
priv - > mclk = freq ;
return 0 ;
}
return - ENOTSUPP ;
}
2017-02-27 16:47:23 +01:00
static int es7134_set_fmt ( struct snd_soc_dai * codec_dai , unsigned int fmt )
{
fmt & = ( SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
SND_SOC_DAIFMT_MASTER_MASK ) ;
if ( fmt ! = ( SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
2022-02-22 22:27:15 +00:00
SND_SOC_DAIFMT_CBC_CFC ) ) {
2017-02-27 16:47:23 +01:00
dev_err ( codec_dai - > dev , " Invalid DAI format \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2018-07-03 17:05:59 +02:00
static int es7134_component_probe ( struct snd_soc_component * c )
{
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( c ) ;
struct es7134_data * priv = snd_soc_component_get_drvdata ( c ) ;
const struct es7134_chip * chip = priv - > chip ;
int ret ;
if ( chip - > extra_widget_num ) {
ret = snd_soc_dapm_new_controls ( dapm , chip - > extra_widgets ,
chip - > extra_widget_num ) ;
if ( ret ) {
dev_err ( c - > dev , " failed to add extra widgets \n " ) ;
return ret ;
}
}
if ( chip - > extra_route_num ) {
ret = snd_soc_dapm_add_routes ( dapm , chip - > extra_routes ,
chip - > extra_route_num ) ;
if ( ret ) {
dev_err ( c - > dev , " failed to add extra routes \n " ) ;
return ret ;
}
}
return 0 ;
}
2017-02-27 16:47:23 +01:00
static const struct snd_soc_dai_ops es7134_dai_ops = {
. set_fmt = es7134_set_fmt ,
2018-06-29 17:09:21 +02:00
. hw_params = es7134_hw_params ,
. set_sysclk = es7134_set_sysclk ,
2017-02-27 16:47:23 +01:00
} ;
static struct snd_soc_dai_driver es7134_dai = {
. name = " es7134-hifi " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
2018-06-29 17:09:20 +02:00
. rates = ( SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_176400 |
SNDRV_PCM_RATE_192000 ) ,
2017-02-27 16:47:23 +01:00
. formats = ( SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S18_3LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_LE ) ,
} ,
. ops = & es7134_dai_ops ,
} ;
2018-06-29 17:09:21 +02:00
static const struct es7134_clock_mode es7134_modes [ ] = {
{
/* Single speed mode */
. rate_min = 8000 ,
. rate_max = 50000 ,
. mclk_fs = ( unsigned int [ ] ) { 256 , 384 , 512 , 768 , 1024 } ,
. mclk_fs_num = 5 ,
} , {
/* Double speed mode */
. rate_min = 84000 ,
. rate_max = 100000 ,
. mclk_fs = ( unsigned int [ ] ) { 128 , 192 , 256 , 384 , 512 } ,
. mclk_fs_num = 5 ,
} , {
/* Quad speed mode */
. rate_min = 167000 ,
. rate_max = 192000 ,
. mclk_fs = ( unsigned int [ ] ) { 128 , 192 , 256 } ,
. mclk_fs_num = 3 ,
} ,
} ;
2018-07-03 17:05:59 +02:00
/* Digital I/O are also supplied by VDD on the es7134 */
static const struct snd_soc_dapm_route es7134_extra_routes [ ] = {
{ " Playback " , NULL , " VDD " , }
} ;
2020-11-25 17:44:46 +01:00
static const struct es7134_chip es7134_chip __maybe_unused = {
2018-07-03 17:06:00 +02:00
. dai_drv = & es7134_dai ,
2018-06-29 17:09:21 +02:00
. modes = es7134_modes ,
. mode_num = ARRAY_SIZE ( es7134_modes ) ,
2018-07-03 17:05:59 +02:00
. extra_routes = es7134_extra_routes ,
. extra_route_num = ARRAY_SIZE ( es7134_extra_routes ) ,
2018-06-29 17:09:21 +02:00
} ;
2017-02-27 16:47:23 +01:00
static const struct snd_soc_dapm_widget es7134_dapm_widgets [ ] = {
SND_SOC_DAPM_OUTPUT ( " AOUTL " ) ,
SND_SOC_DAPM_OUTPUT ( " AOUTR " ) ,
SND_SOC_DAPM_DAC ( " DAC " , " Playback " , SND_SOC_NOPM , 0 , 0 ) ,
2018-06-29 17:09:23 +02:00
SND_SOC_DAPM_REGULATOR_SUPPLY ( " VDD " , 0 , 0 ) ,
2017-02-27 16:47:23 +01:00
} ;
static const struct snd_soc_dapm_route es7134_dapm_routes [ ] = {
{ " AOUTL " , NULL , " DAC " } ,
{ " AOUTR " , NULL , " DAC " } ,
2018-07-03 17:05:59 +02:00
{ " DAC " , NULL , " VDD " } ,
2017-02-27 16:47:23 +01:00
} ;
2018-01-29 04:32:37 +00:00
static const struct snd_soc_component_driver es7134_component_driver = {
2018-07-03 17:05:59 +02:00
. probe = es7134_component_probe ,
2018-01-29 04:32:37 +00:00
. dapm_widgets = es7134_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( es7134_dapm_widgets ) ,
. dapm_routes = es7134_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( es7134_dapm_routes ) ,
. idle_bias_on = 1 ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
2017-02-27 16:47:23 +01:00
} ;
2018-07-03 17:06:00 +02:00
static struct snd_soc_dai_driver es7154_dai = {
. name = " es7154-hifi " ,
. playback = {
. stream_name = " Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
. rates = ( SNDRV_PCM_RATE_8000_48000 |
SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000 ) ,
. formats = ( SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S18_3LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_LE ) ,
} ,
. ops = & es7134_dai_ops ,
} ;
static const struct es7134_clock_mode es7154_modes [ ] = {
{
/* Single speed mode */
. rate_min = 8000 ,
. rate_max = 50000 ,
. mclk_fs = ( unsigned int [ ] ) { 32 , 64 , 128 , 192 , 256 ,
384 , 512 , 768 , 1024 } ,
. mclk_fs_num = 9 ,
} , {
/* Double speed mode */
. rate_min = 84000 ,
. rate_max = 100000 ,
. mclk_fs = ( unsigned int [ ] ) { 128 , 192 , 256 , 384 , 512 ,
768 , 1024 } ,
. mclk_fs_num = 7 ,
}
} ;
/* Es7154 has a separate supply for digital I/O */
static const struct snd_soc_dapm_widget es7154_extra_widgets [ ] = {
SND_SOC_DAPM_REGULATOR_SUPPLY ( " PVDD " , 0 , 0 ) ,
} ;
static const struct snd_soc_dapm_route es7154_extra_routes [ ] = {
{ " Playback " , NULL , " PVDD " , }
} ;
2020-11-25 17:44:46 +01:00
static const struct es7134_chip es7154_chip __maybe_unused = {
2018-07-03 17:06:00 +02:00
. dai_drv = & es7154_dai ,
. modes = es7154_modes ,
. mode_num = ARRAY_SIZE ( es7154_modes ) ,
. extra_routes = es7154_extra_routes ,
. extra_route_num = ARRAY_SIZE ( es7154_extra_routes ) ,
. extra_widgets = es7154_extra_widgets ,
. extra_widget_num = ARRAY_SIZE ( es7154_extra_widgets ) ,
} ;
2017-02-27 16:47:23 +01:00
static int es7134_probe ( struct platform_device * pdev )
{
2018-06-29 17:09:21 +02:00
struct device * dev = & pdev - > dev ;
struct es7134_data * priv ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
platform_set_drvdata ( pdev , priv ) ;
priv - > chip = of_device_get_match_data ( dev ) ;
if ( ! priv - > chip ) {
dev_err ( dev , " failed to match device \n " ) ;
return - ENODEV ;
}
2018-01-29 04:32:37 +00:00
return devm_snd_soc_register_component ( & pdev - > dev ,
& es7134_component_driver ,
2018-07-03 17:06:00 +02:00
priv - > chip - > dai_drv , 1 ) ;
2017-02-27 16:47:23 +01:00
}
# ifdef CONFIG_OF
static const struct of_device_id es7134_ids [ ] = {
2018-06-29 17:09:21 +02:00
{ . compatible = " everest,es7134 " , . data = & es7134_chip } ,
{ . compatible = " everest,es7144 " , . data = & es7134_chip } ,
2018-07-03 17:06:00 +02:00
{ . compatible = " everest,es7154 " , . data = & es7154_chip } ,
2017-02-27 16:47:23 +01:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , es7134_ids ) ;
# endif
static struct platform_driver es7134_driver = {
. driver = {
. name = " es7134 " ,
. of_match_table = of_match_ptr ( es7134_ids ) ,
} ,
. probe = es7134_probe ,
} ;
module_platform_driver ( es7134_driver ) ;
MODULE_DESCRIPTION ( " ASoC ES7134 audio codec driver " ) ;
MODULE_AUTHOR ( " Jerome Brunet <jbrunet@baylibre.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;