2019-05-29 17:17:59 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2018-02-06 10:02:43 +03:00
/*
* cht - bsw - nau8824 . c - ASoc Machine driver for Intel Cherryview - based
* platforms Cherrytrail and Braswell , with nau8824 codec .
*
* Copyright ( C ) 2018 Intel Corp
* Copyright ( C ) 2018 Nuvoton Technology Corp
*
* Author : Wang , Joseph C < joequant @ gmail . com >
* Co - author : John Hsu < KCHSU0 @ nuvoton . com >
* This file is based on cht_bsw_rt5672 . c and cht - bsw - max98090 . c
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
2019-01-25 23:35:04 +03:00
# include <sound/soc-acpi.h>
2018-02-06 10:02:43 +03:00
# include <sound/jack.h>
# include <linux/input.h>
# include "../atom/sst-atom-controls.h"
# include "../../codecs/nau8824.h"
struct cht_mc_private {
struct snd_soc_jack jack ;
} ;
static struct snd_soc_jack_pin cht_bsw_jack_pins [ ] = {
{
. pin = " Headphone " ,
. mask = SND_JACK_HEADPHONE ,
} ,
{
. pin = " Headset Mic " ,
. mask = SND_JACK_MICROPHONE ,
} ,
} ;
static const struct snd_soc_dapm_widget cht_dapm_widgets [ ] = {
SND_SOC_DAPM_HP ( " Headphone " , NULL ) ,
SND_SOC_DAPM_MIC ( " Headset Mic " , NULL ) ,
SND_SOC_DAPM_MIC ( " Int Mic " , NULL ) ,
SND_SOC_DAPM_SPK ( " Ext Spk " , NULL ) ,
} ;
static const struct snd_soc_dapm_route cht_audio_map [ ] = {
{ " Ext Spk " , NULL , " SPKOUTL " } ,
{ " Ext Spk " , NULL , " SPKOUTR " } ,
{ " Headphone " , NULL , " HPOL " } ,
{ " Headphone " , NULL , " HPOR " } ,
{ " MIC1 " , NULL , " Int Mic " } ,
{ " MIC2 " , NULL , " Int Mic " } ,
{ " HSMIC1 " , NULL , " Headset Mic " } ,
{ " HSMIC2 " , NULL , " Headset Mic " } ,
{ " Playback " , NULL , " ssp2 Tx " } ,
{ " ssp2 Tx " , NULL , " codec_out0 " } ,
{ " ssp2 Tx " , NULL , " codec_out1 " } ,
{ " codec_in0 " , NULL , " ssp2 Rx " } ,
{ " codec_in1 " , NULL , " ssp2 Rx " } ,
{ " ssp2 Rx " , NULL , " Capture " } ,
} ;
static const struct snd_kcontrol_new cht_mc_controls [ ] = {
SOC_DAPM_PIN_SWITCH ( " Headphone " ) ,
SOC_DAPM_PIN_SWITCH ( " Headset Mic " ) ,
SOC_DAPM_PIN_SWITCH ( " Int Mic " ) ,
SOC_DAPM_PIN_SWITCH ( " Ext Spk " ) ,
} ;
static int cht_aif1_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
2023-09-26 09:24:24 +03:00
struct snd_soc_pcm_runtime * rtd = snd_soc_substream_to_rtd ( substream ) ;
struct snd_soc_dai * codec_dai = snd_soc_rtd_to_codec ( rtd , 0 ) ;
2018-02-06 10:02:43 +03:00
int ret ;
ret = snd_soc_dai_set_sysclk ( codec_dai , NAU8824_CLK_FLL_FS , 0 ,
SND_SOC_CLOCK_IN ) ;
if ( ret < 0 ) {
dev_err ( codec_dai - > dev , " can't set FS clock %d \n " , ret ) ;
return ret ;
}
ret = snd_soc_dai_set_pll ( codec_dai , 0 , 0 , params_rate ( params ) ,
params_rate ( params ) * 256 ) ;
if ( ret < 0 ) {
dev_err ( codec_dai - > dev , " can't set FLL: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static int cht_codec_init ( struct snd_soc_pcm_runtime * runtime )
{
struct cht_mc_private * ctx = snd_soc_card_get_drvdata ( runtime - > card ) ;
struct snd_soc_jack * jack = & ctx - > jack ;
2023-09-26 09:24:24 +03:00
struct snd_soc_dai * codec_dai = snd_soc_rtd_to_codec ( runtime , 0 ) ;
2018-03-02 05:45:14 +03:00
struct snd_soc_component * component = codec_dai - > component ;
2018-02-06 10:02:43 +03:00
int ret , jack_type ;
2022-03-01 22:48:57 +03:00
/* NAU88L24 supports 4 buttons headset detection
2020-05-16 00:07:31 +03:00
* KEY_PLAYPAUSE
2018-02-06 10:02:43 +03:00
* KEY_VOICECOMMAND
* KEY_VOLUMEUP
* KEY_VOLUMEDOWN
*/
2018-05-21 15:42:51 +03:00
jack_type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
2018-02-06 10:02:43 +03:00
SND_JACK_BTN_2 | SND_JACK_BTN_3 ;
2022-04-08 07:11:14 +03:00
ret = snd_soc_card_jack_new_pins ( runtime - > card , " Headset " , jack_type ,
jack , cht_bsw_jack_pins , ARRAY_SIZE ( cht_bsw_jack_pins ) ) ;
2018-02-06 10:02:43 +03:00
if ( ret ) {
dev_err ( runtime - > dev ,
" Headset Jack creation failed %d \n " , ret ) ;
return ret ;
}
2020-05-16 00:07:31 +03:00
snd_jack_set_key ( jack - > jack , SND_JACK_BTN_0 , KEY_PLAYPAUSE ) ;
2018-02-06 10:02:43 +03:00
snd_jack_set_key ( jack - > jack , SND_JACK_BTN_1 , KEY_VOICECOMMAND ) ;
snd_jack_set_key ( jack - > jack , SND_JACK_BTN_2 , KEY_VOLUMEUP ) ;
snd_jack_set_key ( jack - > jack , SND_JACK_BTN_3 , KEY_VOLUMEDOWN ) ;
2018-03-02 05:45:14 +03:00
nau8824_enable_jack_detect ( component , jack ) ;
2018-02-06 10:02:43 +03:00
return ret ;
}
static int cht_codec_fixup ( struct snd_soc_pcm_runtime * rtd ,
struct snd_pcm_hw_params * params )
{
struct snd_interval * rate = hw_param_interval ( params ,
SNDRV_PCM_HW_PARAM_RATE ) ;
struct snd_interval * channels = hw_param_interval ( params ,
SNDRV_PCM_HW_PARAM_CHANNELS ) ;
struct snd_mask * fmt =
hw_param_mask ( params , SNDRV_PCM_HW_PARAM_FORMAT ) ;
2021-01-07 14:53:24 +03:00
int ret ;
2018-02-06 10:02:43 +03:00
2022-10-24 22:08:40 +03:00
/* The DSP will convert the FE rate to 48k, stereo, 24bits */
2018-02-06 10:02:43 +03:00
rate - > min = rate - > max = 48000 ;
channels - > min = channels - > max = 2 ;
/* set SSP2 to 24-bit */
snd_mask_none ( fmt ) ;
params_set_format ( params , SNDRV_PCM_FORMAT_S24_LE ) ;
2021-01-07 14:53:24 +03:00
/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
2023-09-26 09:24:24 +03:00
ret = snd_soc_dai_set_tdm_slot ( snd_soc_rtd_to_codec ( rtd , 0 ) , 0xf , 0x1 , 4 , 24 ) ;
2021-01-07 14:53:24 +03:00
if ( ret < 0 ) {
dev_err ( rtd - > dev , " can't set codec TDM slot %d \n " , ret ) ;
return ret ;
}
2018-02-06 10:02:43 +03:00
return 0 ;
}
static int cht_aif1_startup ( struct snd_pcm_substream * substream )
{
return snd_pcm_hw_constraint_single ( substream - > runtime ,
SNDRV_PCM_HW_PARAM_RATE , 48000 ) ;
}
static const struct snd_soc_ops cht_aif1_ops = {
. startup = cht_aif1_startup ,
} ;
static const struct snd_soc_ops cht_be_ssp2_ops = {
. hw_params = cht_aif1_hw_params ,
} ;
2019-06-06 07:21:07 +03:00
SND_SOC_DAILINK_DEF ( dummy ,
DAILINK_COMP_ARRAY ( COMP_DUMMY ( ) ) ) ;
SND_SOC_DAILINK_DEF ( media ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " media-cpu-dai " ) ) ) ;
SND_SOC_DAILINK_DEF ( deepbuffer ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " deepbuffer-cpu-dai " ) ) ) ;
SND_SOC_DAILINK_DEF ( ssp2_port ,
DAILINK_COMP_ARRAY ( COMP_CPU ( " ssp2-port " ) ) ) ;
SND_SOC_DAILINK_DEF ( ssp2_codec ,
DAILINK_COMP_ARRAY ( COMP_CODEC ( " i2c-10508824:00 " ,
NAU8824_CODEC_DAI ) ) ) ;
SND_SOC_DAILINK_DEF ( platform ,
DAILINK_COMP_ARRAY ( COMP_PLATFORM ( " sst-mfld-platform " ) ) ) ;
2018-02-06 10:02:43 +03:00
static struct snd_soc_dai_link cht_dailink [ ] = {
/* Front End DAI links */
[ MERR_DPCM_AUDIO ] = {
. name = " Audio Port " ,
. stream_name = " Audio " ,
. nonatomic = true ,
. dynamic = 1 ,
. dpcm_playback = 1 ,
. dpcm_capture = 1 ,
. ops = & cht_aif1_ops ,
2019-06-06 07:21:07 +03:00
SND_SOC_DAILINK_REG ( media , dummy , platform ) ,
2018-02-06 10:02:43 +03:00
} ,
[ MERR_DPCM_DEEP_BUFFER ] = {
. name = " Deep-Buffer Audio Port " ,
. stream_name = " Deep-Buffer Audio " ,
. nonatomic = true ,
. dynamic = 1 ,
. dpcm_playback = 1 ,
. ops = & cht_aif1_ops ,
2019-06-06 07:21:07 +03:00
SND_SOC_DAILINK_REG ( deepbuffer , dummy , platform ) ,
2018-02-06 10:02:43 +03:00
} ,
/* Back End DAI links */
{
/* SSP2 - Codec */
. name = " SSP2-Codec " ,
2020-12-06 15:24:36 +03:00
. id = 0 ,
2018-02-06 10:02:43 +03:00
. no_pcm = 1 ,
. dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
2021-09-20 09:55:08 +03:00
| SND_SOC_DAIFMT_CBC_CFC ,
2018-02-06 10:02:43 +03:00
. init = cht_codec_init ,
. be_hw_params_fixup = cht_codec_fixup ,
. dpcm_playback = 1 ,
. dpcm_capture = 1 ,
. ops = & cht_be_ssp2_ops ,
2019-06-06 07:21:07 +03:00
SND_SOC_DAILINK_REG ( ssp2_port , ssp2_codec , platform ) ,
2018-02-06 10:02:43 +03:00
} ,
} ;
2020-06-17 19:56:15 +03:00
/* use space before codec name to simplify card ID, and simplify driver name */
2020-11-13 01:38:16 +03:00
# define SOF_CARD_NAME "bytcht nau8824" /* card name will be 'sof-bytcht nau8824 */
# define SOF_DRIVER_NAME "SOF"
2020-06-17 19:56:15 +03:00
# define CARD_NAME "chtnau8824"
# define DRIVER_NAME NULL /* card name will be used for driver name */
2018-02-06 10:02:43 +03:00
/* SoC card */
static struct snd_soc_card snd_soc_card_cht = {
. owner = THIS_MODULE ,
. dai_link = cht_dailink ,
. num_links = ARRAY_SIZE ( cht_dailink ) ,
. dapm_widgets = cht_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( cht_dapm_widgets ) ,
. dapm_routes = cht_audio_map ,
. num_dapm_routes = ARRAY_SIZE ( cht_audio_map ) ,
. controls = cht_mc_controls ,
. num_controls = ARRAY_SIZE ( cht_mc_controls ) ,
} ;
static int snd_cht_mc_probe ( struct platform_device * pdev )
{
struct cht_mc_private * drv ;
2019-01-25 23:35:04 +03:00
struct snd_soc_acpi_mach * mach ;
const char * platform_name ;
2020-11-13 01:38:16 +03:00
bool sof_parent ;
2018-02-06 10:02:43 +03:00
int ret_val ;
2018-04-20 13:44:29 +03:00
drv = devm_kzalloc ( & pdev - > dev , sizeof ( * drv ) , GFP_KERNEL ) ;
2018-02-06 10:02:43 +03:00
if ( ! drv )
return - ENOMEM ;
snd_soc_card_set_drvdata ( & snd_soc_card_cht , drv ) ;
2022-03-01 22:48:57 +03:00
/* override platform name, if required */
2019-06-04 23:08:57 +03:00
snd_soc_card_cht . dev = & pdev - > dev ;
2020-03-12 22:48:56 +03:00
mach = pdev - > dev . platform_data ;
2019-01-25 23:35:04 +03:00
platform_name = mach - > mach_params . platform ;
ret_val = snd_soc_fixup_dai_links_platform_name ( & snd_soc_card_cht ,
platform_name ) ;
if ( ret_val )
return ret_val ;
2020-11-13 01:38:16 +03:00
sof_parent = snd_soc_acpi_sof_parent ( & pdev - > dev ) ;
/* set card and driver name */
if ( sof_parent ) {
snd_soc_card_cht . name = SOF_CARD_NAME ;
snd_soc_card_cht . driver_name = SOF_DRIVER_NAME ;
} else {
snd_soc_card_cht . name = CARD_NAME ;
snd_soc_card_cht . driver_name = DRIVER_NAME ;
}
2021-10-03 00:14:59 +03:00
snd_soc_card_cht . components = nau8824_components ( ) ;
2020-11-13 01:38:17 +03:00
/* set pm ops */
if ( sof_parent )
pdev - > dev . driver - > pm = & snd_soc_pm_ops ;
2018-02-06 10:02:43 +03:00
/* register the soc card */
ret_val = devm_snd_soc_register_card ( & pdev - > dev , & snd_soc_card_cht ) ;
if ( ret_val ) {
dev_err ( & pdev - > dev ,
" snd_soc_register_card failed %d \n " , ret_val ) ;
return ret_val ;
}
platform_set_drvdata ( pdev , & snd_soc_card_cht ) ;
return ret_val ;
}
static struct platform_driver snd_cht_mc_driver = {
. driver = {
. name = " cht-bsw-nau8824 " ,
} ,
. probe = snd_cht_mc_probe ,
} ;
module_platform_driver ( snd_cht_mc_driver ) ;
MODULE_DESCRIPTION ( " ASoC Intel(R) Baytrail CR Machine driver " ) ;
MODULE_AUTHOR ( " Wang, Joseph C <joequant@gmail.com> " ) ;
MODULE_AUTHOR ( " John Hsu <KCHSU0@nuvoton.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:cht-bsw-nau8824 " ) ;