2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-09-29 14:14:56 +04:00
/*
* wm8804 . c - - WM8804 S / PDIF transceiver driver
*
2012-05-23 15:39:07 +04:00
* Copyright 2010 - 11 Wolfson Microelectronics plc
2010-09-29 14:14:56 +04:00
*
* Author : Dimitris Papastamos < dp @ opensource . wolfsonmicro . com >
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
2015-04-07 13:34:50 +03:00
# include <linux/gpio/consumer.h>
2010-09-29 14:14:56 +04:00
# include <linux/delay.h>
# include <linux/pm.h>
2015-04-07 14:55:10 +03:00
# include <linux/pm_runtime.h>
2011-08-29 17:23:05 +04:00
# include <linux/of_device.h>
2010-09-29 14:14:56 +04:00
# include <linux/regulator/consumer.h>
# include <linux/slab.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/initval.h>
# include <sound/tlv.h>
2015-04-07 14:55:09 +03:00
# include <sound/soc-dapm.h>
2010-09-29 14:14:56 +04:00
# include "wm8804.h"
# define WM8804_NUM_SUPPLIES 2
static const char * wm8804_supply_names [ WM8804_NUM_SUPPLIES ] = {
" PVDD " ,
" DVDD "
} ;
2011-12-29 23:58:06 +04:00
static const struct reg_default wm8804_reg_defaults [ ] = {
{ 3 , 0x21 } , /* R3 - PLL1 */
{ 4 , 0xFD } , /* R4 - PLL2 */
{ 5 , 0x36 } , /* R5 - PLL3 */
{ 6 , 0x07 } , /* R6 - PLL4 */
{ 7 , 0x16 } , /* R7 - PLL5 */
{ 8 , 0x18 } , /* R8 - PLL6 */
{ 9 , 0xFF } , /* R9 - SPDMODE */
{ 10 , 0x00 } , /* R10 - INTMASK */
{ 18 , 0x00 } , /* R18 - SPDTX1 */
{ 19 , 0x00 } , /* R19 - SPDTX2 */
{ 20 , 0x00 } , /* R20 - SPDTX3 */
{ 21 , 0x71 } , /* R21 - SPDTX4 */
{ 22 , 0x0B } , /* R22 - SPDTX5 */
{ 23 , 0x70 } , /* R23 - GPO0 */
{ 24 , 0x57 } , /* R24 - GPO1 */
{ 26 , 0x42 } , /* R26 - GPO2 */
{ 27 , 0x06 } , /* R27 - AIFTX */
{ 28 , 0x06 } , /* R28 - AIFRX */
{ 29 , 0x80 } , /* R29 - SPDRX1 */
{ 30 , 0x07 } , /* R30 - PWRDN */
2010-09-29 14:14:56 +04:00
} ;
struct wm8804_priv {
2015-04-07 14:55:10 +03:00
struct device * dev ;
2011-12-29 23:58:06 +04:00
struct regmap * regmap ;
2010-09-29 14:14:56 +04:00
struct regulator_bulk_data supplies [ WM8804_NUM_SUPPLIES ] ;
struct notifier_block disable_nb [ WM8804_NUM_SUPPLIES ] ;
2014-05-29 18:08:03 +04:00
int mclk_div ;
2015-04-07 13:34:50 +03:00
struct gpio_desc * reset ;
2010-09-29 14:14:56 +04:00
2015-04-07 14:55:09 +03:00
int aif_pwr ;
} ;
2010-09-29 14:14:56 +04:00
static int txsrc_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol ) ;
2015-04-07 14:55:09 +03:00
static int wm8804_aif_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event ) ;
2010-09-29 14:14:56 +04:00
/*
* We can ' t use the same notifier block for more than one supply and
* there ' s no way I can see to get from a callback to the caller
* except container_of ( ) .
*/
# define WM8804_REGULATOR_EVENT(n) \
static int wm8804_regulator_event_ # # n ( struct notifier_block * nb , \
unsigned long event , void * data ) \
{ \
struct wm8804_priv * wm8804 = container_of ( nb , struct wm8804_priv , \
disable_nb [ n ] ) ; \
if ( event & REGULATOR_EVENT_DISABLE ) { \
2011-12-29 23:58:06 +04:00
regcache_mark_dirty ( wm8804 - > regmap ) ; \
2010-09-29 14:14:56 +04:00
} \
return 0 ; \
}
WM8804_REGULATOR_EVENT ( 0 )
WM8804_REGULATOR_EVENT ( 1 )
static const char * txsrc_text [ ] = { " S/PDIF RX " , " AIF " } ;
2015-07-27 11:56:23 +03:00
static SOC_ENUM_SINGLE_DECL ( txsrc , WM8804_SPDTX4 , 6 , txsrc_text ) ;
2010-09-29 14:14:56 +04:00
2015-04-07 14:55:09 +03:00
static const struct snd_kcontrol_new wm8804_tx_source_mux [ ] = {
SOC_DAPM_ENUM_EXT ( " Input Source " , txsrc ,
snd_soc_dapm_get_enum_double , txsrc_put ) ,
2010-09-29 14:14:56 +04:00
} ;
2015-04-07 14:55:09 +03:00
static const struct snd_soc_dapm_widget wm8804_dapm_widgets [ ] = {
SND_SOC_DAPM_OUTPUT ( " SPDIF Out " ) ,
SND_SOC_DAPM_INPUT ( " SPDIF In " ) ,
SND_SOC_DAPM_PGA ( " SPDIFTX " , WM8804_PWRDN , 2 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_PGA ( " SPDIFRX " , WM8804_PWRDN , 1 , 1 , NULL , 0 ) ,
SND_SOC_DAPM_MUX ( " Tx Source " , SND_SOC_NOPM , 6 , 0 , wm8804_tx_source_mux ) ,
SND_SOC_DAPM_AIF_OUT_E ( " AIFTX " , NULL , 0 , SND_SOC_NOPM , 0 , 0 , wm8804_aif_event ,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD ) ,
SND_SOC_DAPM_AIF_IN_E ( " AIFRX " , NULL , 0 , SND_SOC_NOPM , 0 , 0 , wm8804_aif_event ,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD ) ,
} ;
static const struct snd_soc_dapm_route wm8804_dapm_routes [ ] = {
{ " AIFRX " , NULL , " Playback " } ,
{ " Tx Source " , " AIF " , " AIFRX " } ,
{ " SPDIFRX " , NULL , " SPDIF In " } ,
{ " Tx Source " , " S/PDIF RX " , " SPDIFRX " } ,
{ " SPDIFTX " , NULL , " Tx Source " } ,
{ " SPDIF Out " , NULL , " SPDIFTX " } ,
2010-09-29 14:14:56 +04:00
2015-04-07 14:55:09 +03:00
{ " AIFTX " , NULL , " SPDIFRX " } ,
{ " Capture " , NULL , " AIFTX " } ,
} ;
static int wm8804_aif_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
2018-01-29 06:04:37 +03:00
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct wm8804_priv * wm8804 = snd_soc_component_get_drvdata ( component ) ;
2015-04-07 14:55:09 +03:00
switch ( event ) {
case SND_SOC_DAPM_POST_PMU :
/* power up the aif */
if ( ! wm8804 - > aif_pwr )
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PWRDN , 0x10 , 0x0 ) ;
2015-04-07 14:55:09 +03:00
wm8804 - > aif_pwr + + ;
break ;
case SND_SOC_DAPM_POST_PMD :
/* power down only both paths are disabled */
wm8804 - > aif_pwr - - ;
if ( ! wm8804 - > aif_pwr )
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PWRDN , 0x10 , 0x10 ) ;
2015-04-07 14:55:09 +03:00
break ;
}
2010-09-29 14:14:56 +04:00
return 0 ;
}
static int txsrc_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
2018-01-29 06:04:37 +03:00
struct snd_soc_component * component = snd_soc_dapm_kcontrol_component ( kcontrol ) ;
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( component ) ;
2015-04-07 14:55:09 +03:00
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
unsigned int val = ucontrol - > value . enumerated . item [ 0 ] < < e - > shift_l ;
unsigned int mask = 1 < < e - > shift_l ;
unsigned int txpwr ;
if ( val ! = 0 & & val ! = mask )
return - EINVAL ;
2010-09-29 14:14:56 +04:00
2015-04-07 14:55:09 +03:00
snd_soc_dapm_mutex_lock ( dapm ) ;
2010-09-29 14:14:56 +04:00
2018-01-29 06:04:37 +03:00
if ( snd_soc_component_test_bits ( component , e - > reg , mask , val ) ) {
2015-04-07 14:55:09 +03:00
/* save the current power state of the transmitter */
2020-06-16 08:21:29 +03:00
txpwr = snd_soc_component_read ( component , WM8804_PWRDN ) & 0x4 ;
2010-09-29 14:14:56 +04:00
2015-04-07 14:55:09 +03:00
/* power down the transmitter */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PWRDN , 0x4 , 0x4 ) ;
2010-09-29 14:14:56 +04:00
2015-04-07 14:55:09 +03:00
/* set the tx source */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , e - > reg , mask , val ) ;
2015-04-07 14:55:09 +03:00
/* restore the transmitter's configuration */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PWRDN , 0x4 , txpwr ) ;
2010-09-29 14:14:56 +04:00
}
2015-04-07 14:55:09 +03:00
snd_soc_dapm_mutex_unlock ( dapm ) ;
2010-09-29 14:14:56 +04:00
return 0 ;
}
2011-12-29 23:58:06 +04:00
static bool wm8804_volatile ( struct device * dev , unsigned int reg )
2010-09-29 14:14:56 +04:00
{
switch ( reg ) {
case WM8804_RST_DEVID1 :
case WM8804_DEVID2 :
case WM8804_DEVREV :
case WM8804_INTSTAT :
case WM8804_SPDSTAT :
case WM8804_RXCHAN1 :
case WM8804_RXCHAN2 :
case WM8804_RXCHAN3 :
case WM8804_RXCHAN4 :
case WM8804_RXCHAN5 :
2011-12-29 23:58:06 +04:00
return true ;
2010-09-29 14:14:56 +04:00
default :
2011-12-29 23:58:06 +04:00
return false ;
2010-09-29 14:14:56 +04:00
}
}
2015-04-07 13:34:50 +03:00
static int wm8804_soft_reset ( struct wm8804_priv * wm8804 )
2010-09-29 14:14:56 +04:00
{
2015-02-22 19:43:22 +03:00
return regmap_write ( wm8804 - > regmap , WM8804_RST_DEVID1 , 0x0 ) ;
2010-09-29 14:14:56 +04:00
}
static int wm8804_set_fmt ( struct snd_soc_dai * dai , unsigned int fmt )
{
2018-01-29 06:04:37 +03:00
struct snd_soc_component * component ;
2010-09-29 14:14:56 +04:00
u16 format , master , bcp , lrp ;
2018-01-29 06:04:37 +03:00
component = dai - > component ;
2010-09-29 14:14:56 +04:00
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_I2S :
format = 0x2 ;
break ;
case SND_SOC_DAIFMT_RIGHT_J :
format = 0x0 ;
break ;
case SND_SOC_DAIFMT_LEFT_J :
format = 0x1 ;
break ;
case SND_SOC_DAIFMT_DSP_A :
case SND_SOC_DAIFMT_DSP_B :
format = 0x3 ;
break ;
default :
dev_err ( dai - > dev , " Unknown dai format \n " ) ;
return - EINVAL ;
}
/* set data format */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_AIFTX , 0x3 , format ) ;
snd_soc_component_update_bits ( component , WM8804_AIFRX , 0x3 , format ) ;
2010-09-29 14:14:56 +04:00
switch ( fmt & SND_SOC_DAIFMT_MASTER_MASK ) {
case SND_SOC_DAIFMT_CBM_CFM :
master = 1 ;
break ;
case SND_SOC_DAIFMT_CBS_CFS :
master = 0 ;
break ;
default :
dev_err ( dai - > dev , " Unknown master/slave configuration \n " ) ;
return - EINVAL ;
}
/* set master/slave mode */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_AIFRX , 0x40 , master < < 6 ) ;
2010-09-29 14:14:56 +04:00
bcp = lrp = 0 ;
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_NF :
break ;
case SND_SOC_DAIFMT_IB_IF :
bcp = lrp = 1 ;
break ;
case SND_SOC_DAIFMT_IB_NF :
bcp = 1 ;
break ;
case SND_SOC_DAIFMT_NB_IF :
lrp = 1 ;
break ;
default :
dev_err ( dai - > dev , " Unknown polarity configuration \n " ) ;
return - EINVAL ;
}
/* set frame inversion */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_AIFTX , 0x10 | 0x20 ,
2010-09-29 14:14:56 +04:00
( bcp < < 4 ) | ( lrp < < 5 ) ) ;
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_AIFRX , 0x10 | 0x20 ,
2010-09-29 14:14:56 +04:00
( bcp < < 4 ) | ( lrp < < 5 ) ) ;
return 0 ;
}
static int wm8804_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
2018-01-29 06:04:37 +03:00
struct snd_soc_component * component ;
2010-09-29 14:14:56 +04:00
u16 blen ;
2018-01-29 06:04:37 +03:00
component = dai - > component ;
2010-09-29 14:14:56 +04:00
2014-07-31 15:52:37 +04:00
switch ( params_width ( params ) ) {
case 16 :
2010-09-29 14:14:56 +04:00
blen = 0x0 ;
break ;
2014-07-31 15:52:37 +04:00
case 20 :
2010-09-29 14:14:56 +04:00
blen = 0x1 ;
break ;
2014-07-31 15:52:37 +04:00
case 24 :
2010-09-29 14:14:56 +04:00
blen = 0x2 ;
break ;
default :
dev_err ( dai - > dev , " Unsupported word length: %u \n " ,
2014-07-31 15:52:37 +04:00
params_width ( params ) ) ;
2010-09-29 14:14:56 +04:00
return - EINVAL ;
}
/* set word length */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_AIFTX , 0xc , blen < < 2 ) ;
snd_soc_component_update_bits ( component , WM8804_AIFRX , 0xc , blen < < 2 ) ;
2010-09-29 14:14:56 +04:00
return 0 ;
}
struct pll_div {
u32 prescale : 1 ;
u32 mclkdiv : 1 ;
u32 freqmode : 2 ;
u32 n : 4 ;
u32 k : 22 ;
} ;
/* PLL rate to output rate divisions */
static struct {
unsigned int div ;
unsigned int freqmode ;
unsigned int mclkdiv ;
} post_table [ ] = {
{ 2 , 0 , 0 } ,
{ 4 , 0 , 1 } ,
{ 4 , 1 , 0 } ,
{ 8 , 1 , 1 } ,
{ 8 , 2 , 0 } ,
{ 16 , 2 , 1 } ,
{ 12 , 3 , 0 } ,
{ 24 , 3 , 1 }
} ;
# define FIXED_PLL_SIZE ((1ULL << 22) * 10)
static int pll_factors ( struct pll_div * pll_div , unsigned int target ,
2014-05-29 18:08:03 +04:00
unsigned int source , unsigned int mclk_div )
2010-09-29 14:14:56 +04:00
{
u64 Kpart ;
unsigned long int K , Ndiv , Nmod , tmp ;
int i ;
/*
* Scale the output frequency up ; the PLL should run in the
* region of 90 - 100 MHz .
*/
for ( i = 0 ; i < ARRAY_SIZE ( post_table ) ; i + + ) {
tmp = target * post_table [ i ] . div ;
2014-05-29 18:08:03 +04:00
if ( ( tmp > = 90000000 & & tmp < = 100000000 ) & &
( mclk_div = = post_table [ i ] . mclkdiv ) ) {
2010-09-29 14:14:56 +04:00
pll_div - > freqmode = post_table [ i ] . freqmode ;
pll_div - > mclkdiv = post_table [ i ] . mclkdiv ;
target * = post_table [ i ] . div ;
break ;
}
}
if ( i = = ARRAY_SIZE ( post_table ) ) {
pr_err ( " %s: Unable to scale output frequency: %uHz \n " ,
__func__ , target ) ;
return - EINVAL ;
}
pll_div - > prescale = 0 ;
Ndiv = target / source ;
if ( Ndiv < 5 ) {
source > > = 1 ;
pll_div - > prescale = 1 ;
Ndiv = target / source ;
}
if ( Ndiv < 5 | | Ndiv > 13 ) {
pr_err ( " %s: WM8804 N value is not within the recommended range: %lu \n " ,
__func__ , Ndiv ) ;
return - EINVAL ;
}
pll_div - > n = Ndiv ;
Nmod = target % source ;
Kpart = FIXED_PLL_SIZE * ( u64 ) Nmod ;
do_div ( Kpart , source ) ;
K = Kpart & 0xffffffff ;
if ( ( K % 10 ) > = 5 )
K + = 5 ;
K / = 10 ;
pll_div - > k = K ;
return 0 ;
}
static int wm8804_set_pll ( struct snd_soc_dai * dai , int pll_id ,
int source , unsigned int freq_in ,
unsigned int freq_out )
{
2018-01-29 06:04:37 +03:00
struct snd_soc_component * component = dai - > component ;
struct wm8804_priv * wm8804 = snd_soc_component_get_drvdata ( component ) ;
2015-04-07 14:55:10 +03:00
bool change ;
2010-09-29 14:14:56 +04:00
2010-10-04 12:31:42 +04:00
if ( ! freq_in | | ! freq_out ) {
/* disable the PLL */
2015-04-07 14:55:10 +03:00
regmap_update_bits_check ( wm8804 - > regmap , WM8804_PWRDN ,
0x1 , 0x1 , & change ) ;
if ( change )
pm_runtime_put ( wm8804 - > dev ) ;
2010-10-04 12:31:42 +04:00
} else {
int ret ;
struct pll_div pll_div ;
2014-05-29 18:08:03 +04:00
ret = pll_factors ( & pll_div , freq_out , freq_in ,
wm8804 - > mclk_div ) ;
2010-09-29 14:14:56 +04:00
if ( ret )
return ret ;
2010-10-04 12:31:42 +04:00
/* power down the PLL before reprogramming it */
2015-04-07 14:55:10 +03:00
regmap_update_bits_check ( wm8804 - > regmap , WM8804_PWRDN ,
0x1 , 0x1 , & change ) ;
if ( ! change )
pm_runtime_get_sync ( wm8804 - > dev ) ;
2010-09-29 14:14:56 +04:00
2010-10-04 12:31:42 +04:00
/* set PLLN and PRESCALE */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PLL4 , 0xf | 0x10 ,
2010-10-04 12:31:42 +04:00
pll_div . n | ( pll_div . prescale < < 4 ) ) ;
/* set mclkdiv and freqmode */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PLL5 , 0x3 | 0x8 ,
2010-10-04 12:31:42 +04:00
pll_div . freqmode | ( pll_div . mclkdiv < < 3 ) ) ;
/* set PLLK */
2018-01-29 06:04:37 +03:00
snd_soc_component_write ( component , WM8804_PLL1 , pll_div . k & 0xff ) ;
snd_soc_component_write ( component , WM8804_PLL2 , ( pll_div . k > > 8 ) & 0xff ) ;
snd_soc_component_write ( component , WM8804_PLL3 , pll_div . k > > 16 ) ;
2010-10-04 12:31:42 +04:00
/* power up the PLL */
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PWRDN , 0x1 , 0 ) ;
2010-10-04 12:31:42 +04:00
}
2010-09-29 14:14:56 +04:00
return 0 ;
}
static int wm8804_set_sysclk ( struct snd_soc_dai * dai ,
int clk_id , unsigned int freq , int dir )
{
2018-01-29 06:04:37 +03:00
struct snd_soc_component * component ;
2010-09-29 14:14:56 +04:00
2018-01-29 06:04:37 +03:00
component = dai - > component ;
2010-09-29 14:14:56 +04:00
switch ( clk_id ) {
case WM8804_TX_CLKSRC_MCLK :
if ( ( freq > = 10000000 & & freq < = 14400000 )
| | ( freq > = 16280000 & & freq < = 27000000 ) )
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PLL6 , 0x80 , 0x80 ) ;
2010-09-29 14:14:56 +04:00
else {
dev_err ( dai - > dev , " OSCCLOCK is not within the "
" recommended range: %uHz \n " , freq ) ;
return - EINVAL ;
}
break ;
case WM8804_TX_CLKSRC_PLL :
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PLL6 , 0x80 , 0 ) ;
2010-09-29 14:14:56 +04:00
break ;
case WM8804_CLKOUT_SRC_CLK1 :
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PLL6 , 0x8 , 0 ) ;
2010-09-29 14:14:56 +04:00
break ;
case WM8804_CLKOUT_SRC_OSCCLK :
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PLL6 , 0x8 , 0x8 ) ;
2010-09-29 14:14:56 +04:00
break ;
default :
dev_err ( dai - > dev , " Unknown clock source: %d \n " , clk_id ) ;
return - EINVAL ;
}
return 0 ;
}
static int wm8804_set_clkdiv ( struct snd_soc_dai * dai ,
int div_id , int div )
{
2018-01-29 06:04:37 +03:00
struct snd_soc_component * component ;
2014-05-29 18:08:03 +04:00
struct wm8804_priv * wm8804 ;
2010-09-29 14:14:56 +04:00
2018-01-29 06:04:37 +03:00
component = dai - > component ;
2010-09-29 14:14:56 +04:00
switch ( div_id ) {
case WM8804_CLKOUT_DIV :
2018-01-29 06:04:37 +03:00
snd_soc_component_update_bits ( component , WM8804_PLL5 , 0x30 ,
2010-09-29 14:14:56 +04:00
( div & 0x3 ) < < 4 ) ;
break ;
2014-05-29 18:08:03 +04:00
case WM8804_MCLK_DIV :
2018-01-29 06:04:37 +03:00
wm8804 = snd_soc_component_get_drvdata ( component ) ;
2014-05-29 18:08:03 +04:00
wm8804 - > mclk_div = div ;
break ;
2010-09-29 14:14:56 +04:00
default :
dev_err ( dai - > dev , " Unknown clock divider: %d \n " , div_id ) ;
return - EINVAL ;
}
return 0 ;
}
2011-11-23 14:40:40 +04:00
static const struct snd_soc_dai_ops wm8804_dai_ops = {
2010-09-29 14:14:56 +04:00
. hw_params = wm8804_hw_params ,
. set_fmt = wm8804_set_fmt ,
. set_sysclk = wm8804_set_sysclk ,
. set_clkdiv = wm8804_set_clkdiv ,
. set_pll = wm8804_set_pll
} ;
# define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE )
2011-06-08 21:07:49 +04:00
# define WM8804_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 | \
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 )
2010-09-29 14:14:56 +04:00
static struct snd_soc_dai_driver wm8804_dai = {
2010-10-01 12:12:14 +04:00
. name = " wm8804-spdif " ,
2010-09-29 14:14:56 +04:00
. playback = {
. stream_name = " Playback " ,
. channels_min = 2 ,
. channels_max = 2 ,
2011-06-08 21:07:49 +04:00
. rates = WM8804_RATES ,
2010-09-29 14:14:56 +04:00
. formats = WM8804_FORMATS ,
} ,
. capture = {
. stream_name = " Capture " ,
. channels_min = 2 ,
. channels_max = 2 ,
2011-06-08 21:07:49 +04:00
. rates = WM8804_RATES ,
2010-09-29 14:14:56 +04:00
. formats = WM8804_FORMATS ,
} ,
. ops = & wm8804_dai_ops ,
2021-01-15 07:54:13 +03:00
. symmetric_rate = 1
2010-09-29 14:14:56 +04:00
} ;
2018-01-29 06:04:37 +03:00
static const struct snd_soc_component_driver soc_component_dev_wm8804 = {
. dapm_widgets = wm8804_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( wm8804_dapm_widgets ) ,
. dapm_routes = wm8804_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( wm8804_dapm_routes ) ,
. use_pmdown_time = 1 ,
. endianness = 1 ,
2010-09-29 14:14:56 +04:00
} ;
2015-02-22 19:43:21 +03:00
const struct regmap_config wm8804_regmap_config = {
2011-12-29 23:58:06 +04:00
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = WM8804_MAX_REGISTER ,
. volatile_reg = wm8804_volatile ,
. cache_type = REGCACHE_RBTREE ,
. reg_defaults = wm8804_reg_defaults ,
. num_reg_defaults = ARRAY_SIZE ( wm8804_reg_defaults ) ,
} ;
2015-02-22 19:43:21 +03:00
EXPORT_SYMBOL_GPL ( wm8804_regmap_config ) ;
2011-12-29 23:58:06 +04:00
2015-02-22 19:43:21 +03:00
int wm8804_probe ( struct device * dev , struct regmap * regmap )
2010-09-29 14:14:56 +04:00
{
struct wm8804_priv * wm8804 ;
2015-02-22 19:43:22 +03:00
unsigned int id1 , id2 ;
int i , ret ;
2010-09-29 14:14:56 +04:00
2015-02-22 19:43:21 +03:00
wm8804 = devm_kzalloc ( dev , sizeof ( * wm8804 ) , GFP_KERNEL ) ;
2010-10-09 23:31:31 +04:00
if ( ! wm8804 )
return - ENOMEM ;
2010-09-29 14:14:56 +04:00
2015-02-22 19:43:21 +03:00
dev_set_drvdata ( dev , wm8804 ) ;
2010-09-29 14:14:56 +04:00
2015-04-07 14:55:10 +03:00
wm8804 - > dev = dev ;
2015-02-22 19:43:21 +03:00
wm8804 - > regmap = regmap ;
2010-09-29 14:14:56 +04:00
2015-04-07 13:34:50 +03:00
wm8804 - > reset = devm_gpiod_get_optional ( dev , " wlf,reset " ,
GPIOD_OUT_LOW ) ;
if ( IS_ERR ( wm8804 - > reset ) ) {
ret = PTR_ERR ( wm8804 - > reset ) ;
dev_err ( dev , " Failed to get reset line: %d \n " , ret ) ;
return ret ;
}
2015-02-22 19:43:22 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( wm8804 - > supplies ) ; i + + )
wm8804 - > supplies [ i ] . supply = wm8804_supply_names [ i ] ;
ret = devm_regulator_bulk_get ( dev , ARRAY_SIZE ( wm8804 - > supplies ) ,
wm8804 - > supplies ) ;
if ( ret ) {
dev_err ( dev , " Failed to request supplies: %d \n " , ret ) ;
return ret ;
}
wm8804 - > disable_nb [ 0 ] . notifier_call = wm8804_regulator_event_0 ;
wm8804 - > disable_nb [ 1 ] . notifier_call = wm8804_regulator_event_1 ;
/* This should really be moved into the regulator core */
for ( i = 0 ; i < ARRAY_SIZE ( wm8804 - > supplies ) ; i + + ) {
2015-03-05 18:39:21 +03:00
struct regulator * regulator = wm8804 - > supplies [ i ] . consumer ;
ret = devm_regulator_register_notifier ( regulator ,
& wm8804 - > disable_nb [ i ] ) ;
2015-02-22 19:43:22 +03:00
if ( ret ! = 0 ) {
dev_err ( dev ,
" Failed to register regulator notifier: %d \n " ,
ret ) ;
2015-03-05 18:39:22 +03:00
return ret ;
2015-02-22 19:43:22 +03:00
}
}
ret = regulator_bulk_enable ( ARRAY_SIZE ( wm8804 - > supplies ) ,
wm8804 - > supplies ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable supplies: %d \n " , ret ) ;
2015-03-05 18:39:22 +03:00
return ret ;
2015-02-22 19:43:22 +03:00
}
2017-07-17 00:11:07 +03:00
gpiod_set_value_cansleep ( wm8804 - > reset , 1 ) ;
2015-04-07 13:34:50 +03:00
2015-02-22 19:43:22 +03:00
ret = regmap_read ( regmap , WM8804_RST_DEVID1 , & id1 ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to read device ID: %d \n " , ret ) ;
goto err_reg_enable ;
}
ret = regmap_read ( regmap , WM8804_DEVID2 , & id2 ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to read device ID: %d \n " , ret ) ;
goto err_reg_enable ;
}
id2 = ( id2 < < 8 ) | id1 ;
if ( id2 ! = 0x8805 ) {
dev_err ( dev , " Invalid device ID: %#x \n " , id2 ) ;
ret = - EINVAL ;
goto err_reg_enable ;
}
ret = regmap_read ( regmap , WM8804_DEVREV , & id1 ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to read device revision: %d \n " ,
ret ) ;
goto err_reg_enable ;
}
dev_info ( dev , " revision %c \n " , id1 + ' A ' ) ;
2015-04-07 13:34:50 +03:00
if ( ! wm8804 - > reset ) {
ret = wm8804_soft_reset ( wm8804 ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to issue reset: %d \n " , ret ) ;
goto err_reg_enable ;
}
2015-02-22 19:43:22 +03:00
}
2018-01-29 06:04:37 +03:00
ret = devm_snd_soc_register_component ( dev , & soc_component_dev_wm8804 ,
2015-03-05 18:39:22 +03:00
& wm8804_dai , 1 ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to register CODEC: %d \n " , ret ) ;
goto err_reg_enable ;
}
2015-04-07 14:55:10 +03:00
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
pm_runtime_idle ( dev ) ;
2015-03-05 18:39:22 +03:00
return 0 ;
2015-02-22 19:43:22 +03:00
err_reg_enable :
regulator_bulk_disable ( ARRAY_SIZE ( wm8804 - > supplies ) , wm8804 - > supplies ) ;
return ret ;
2010-09-29 14:14:56 +04:00
}
2015-02-22 19:43:21 +03:00
EXPORT_SYMBOL_GPL ( wm8804_probe ) ;
2010-09-29 14:14:56 +04:00
2015-02-22 19:43:21 +03:00
void wm8804_remove ( struct device * dev )
2010-09-29 14:14:56 +04:00
{
2015-04-07 14:55:10 +03:00
pm_runtime_disable ( dev ) ;
2010-09-29 14:14:56 +04:00
}
2015-02-22 19:43:21 +03:00
EXPORT_SYMBOL_GPL ( wm8804_remove ) ;
2010-09-29 14:14:56 +04:00
2015-04-07 14:55:10 +03:00
# if IS_ENABLED(CONFIG_PM)
static int wm8804_runtime_resume ( struct device * dev )
{
struct wm8804_priv * wm8804 = dev_get_drvdata ( dev ) ;
int ret ;
ret = regulator_bulk_enable ( ARRAY_SIZE ( wm8804 - > supplies ) ,
wm8804 - > supplies ) ;
if ( ret ) {
dev_err ( wm8804 - > dev , " Failed to enable supplies: %d \n " , ret ) ;
return ret ;
}
regcache_sync ( wm8804 - > regmap ) ;
/* Power up OSCCLK */
regmap_update_bits ( wm8804 - > regmap , WM8804_PWRDN , 0x8 , 0x0 ) ;
return 0 ;
}
static int wm8804_runtime_suspend ( struct device * dev )
{
struct wm8804_priv * wm8804 = dev_get_drvdata ( dev ) ;
/* Power down OSCCLK */
regmap_update_bits ( wm8804 - > regmap , WM8804_PWRDN , 0x8 , 0x8 ) ;
regulator_bulk_disable ( ARRAY_SIZE ( wm8804 - > supplies ) ,
wm8804 - > supplies ) ;
return 0 ;
}
# endif
const struct dev_pm_ops wm8804_pm = {
SET_RUNTIME_PM_OPS ( wm8804_runtime_suspend , wm8804_runtime_resume , NULL )
} ;
EXPORT_SYMBOL_GPL ( wm8804_pm ) ;
2010-09-29 14:14:56 +04:00
MODULE_DESCRIPTION ( " ASoC WM8804 driver " ) ;
MODULE_AUTHOR ( " Dimitris Papastamos <dp@opensource.wolfsonmicro.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;