2019-05-29 07:17:58 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2015-06-10 13:15:54 +01:00
/*
* Copyright ( c ) 2015 The Linux Foundation . All rights reserved .
*/
# include <linux/device.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/clk.h>
# include <linux/platform_device.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
2017-08-17 10:02:11 +02:00
# include <sound/jack.h>
2015-06-10 13:15:54 +01:00
# include <sound/soc.h>
2017-08-17 10:02:11 +02:00
# include <uapi/linux/input-event-codes.h>
2015-06-10 13:15:54 +01:00
# include <dt-bindings/sound/apq8016-lpass.h>
2020-07-23 20:39:03 +02:00
# include "common.h"
2021-12-02 15:55:05 +01:00
# include "qdsp6/q6afe.h"
# define MI2S_COUNT (MI2S_QUATERNARY + 1)
2015-06-10 13:15:54 +01:00
struct apq8016_sbc_data {
2020-07-23 20:39:03 +02:00
struct snd_soc_card card ;
2015-06-10 13:15:54 +01:00
void __iomem * mic_iomux ;
void __iomem * spkr_iomux ;
2017-08-17 10:02:11 +02:00
struct snd_soc_jack jack ;
bool jack_setup ;
2021-12-02 15:55:05 +01:00
int mi2s_clk_count [ MI2S_COUNT ] ;
2015-06-10 13:15:54 +01:00
} ;
2016-02-11 12:18:45 +00:00
# define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21)
2015-06-10 13:15:54 +01:00
# define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17)
# define MIC_CTRL_TLMM_SCLK_EN BIT(1)
# define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16))
2021-08-06 13:41:16 +02:00
# define SPKR_CTL_TLMM_MCLK_EN BIT(1)
# define SPKR_CTL_TLMM_SCLK_EN BIT(2)
# define SPKR_CTL_TLMM_DATA1_EN BIT(3)
# define SPKR_CTL_TLMM_WS_OUT_SEL_MASK GENMASK(7, 6)
# define SPKR_CTL_TLMM_WS_OUT_SEL_SEC BIT(6)
# define SPKR_CTL_TLMM_WS_EN_SEL_MASK GENMASK(19, 18)
# define SPKR_CTL_TLMM_WS_EN_SEL_SEC BIT(18)
2017-08-02 15:17:47 +02:00
# define DEFAULT_MCLK_RATE 9600000
2021-12-02 15:55:05 +01:00
# define MI2S_BCLK_RATE 1536000
2015-06-10 13:15:54 +01:00
2021-12-02 15:55:05 +01:00
static int apq8016_dai_init ( struct snd_soc_pcm_runtime * rtd , int mi2s )
2015-06-10 13:15:54 +01:00
{
2020-02-19 15:56:09 +09:00
struct snd_soc_dai * codec_dai ;
2017-12-05 04:24:13 +00:00
struct snd_soc_component * component ;
2015-06-10 13:15:54 +01:00
struct snd_soc_card * card = rtd - > card ;
struct apq8016_sbc_data * pdata = snd_soc_card_get_drvdata ( card ) ;
2017-08-02 15:17:47 +02:00
int i , rval ;
2021-08-06 13:41:16 +02:00
u32 value ;
2015-06-10 13:15:54 +01:00
2021-12-02 15:55:05 +01:00
switch ( mi2s ) {
2015-06-10 13:15:54 +01:00
case MI2S_PRIMARY :
writel ( readl ( pdata - > spkr_iomux ) | SPKR_CTL_PRI_WS_SLAVE_SEL_11 ,
pdata - > spkr_iomux ) ;
break ;
case MI2S_QUATERNARY :
/* Configure the Quat MI2S to TLMM */
writel ( readl ( pdata - > mic_iomux ) | MIC_CTRL_QUA_WS_SLAVE_SEL_10 |
MIC_CTRL_TLMM_SCLK_EN ,
pdata - > mic_iomux ) ;
break ;
2021-08-06 13:41:16 +02:00
case MI2S_SECONDARY :
/* Clear TLMM_WS_OUT_SEL and TLMM_WS_EN_SEL fields */
value = readl ( pdata - > spkr_iomux ) &
~ ( SPKR_CTL_TLMM_WS_OUT_SEL_MASK | SPKR_CTL_TLMM_WS_EN_SEL_MASK ) ;
/* Configure the Sec MI2S to TLMM */
writel ( value | SPKR_CTL_TLMM_MCLK_EN | SPKR_CTL_TLMM_SCLK_EN |
SPKR_CTL_TLMM_DATA1_EN | SPKR_CTL_TLMM_WS_OUT_SEL_SEC |
SPKR_CTL_TLMM_WS_EN_SEL_SEC , pdata - > spkr_iomux ) ;
break ;
2016-02-11 12:18:45 +00:00
case MI2S_TERTIARY :
writel ( readl ( pdata - > mic_iomux ) | MIC_CTRL_TER_WS_SLAVE_SEL |
MIC_CTRL_TLMM_SCLK_EN ,
pdata - > mic_iomux ) ;
break ;
2015-06-10 13:15:54 +01:00
default :
dev_err ( card - > dev , " unsupported cpu dai configuration \n " ) ;
2017-08-02 15:17:47 +02:00
return - EINVAL ;
}
2015-06-10 13:15:54 +01:00
2017-08-17 10:02:11 +02:00
if ( ! pdata - > jack_setup ) {
struct snd_jack * jack ;
rval = snd_soc_card_jack_new ( card , " Headset Jack " ,
SND_JACK_HEADSET |
SND_JACK_HEADPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3 |
SND_JACK_BTN_4 ,
2022-04-08 13:11:14 +09:00
& pdata - > jack ) ;
2017-08-17 10:02:11 +02:00
if ( rval < 0 ) {
dev_err ( card - > dev , " Unable to add Headphone Jack \n " ) ;
return rval ;
}
jack = pdata - > jack . jack ;
2017-11-22 12:56:43 -08:00
snd_jack_set_key ( jack , SND_JACK_BTN_0 , KEY_PLAYPAUSE ) ;
2017-08-17 10:02:11 +02:00
snd_jack_set_key ( jack , SND_JACK_BTN_1 , KEY_VOICECOMMAND ) ;
snd_jack_set_key ( jack , SND_JACK_BTN_2 , KEY_VOLUMEUP ) ;
snd_jack_set_key ( jack , SND_JACK_BTN_3 , KEY_VOLUMEDOWN ) ;
pdata - > jack_setup = true ;
}
2020-03-09 13:07:57 +09:00
for_each_rtd_codec_dais ( rtd , i , codec_dai ) {
2017-08-02 15:17:47 +02:00
2020-02-19 15:56:09 +09:00
component = codec_dai - > component ;
2017-08-02 15:17:47 +02:00
/* Set default mclk for internal codec */
2017-12-05 04:24:13 +00:00
rval = snd_soc_component_set_sysclk ( component , 0 , 0 , DEFAULT_MCLK_RATE ,
2017-08-02 15:17:47 +02:00
SND_SOC_CLOCK_IN ) ;
if ( rval ! = 0 & & rval ! = - ENOTSUPP ) {
dev_warn ( card - > dev , " Failed to set mclk: %d \n " , rval ) ;
return rval ;
}
2017-12-05 04:24:13 +00:00
rval = snd_soc_component_set_jack ( component , & pdata - > jack , NULL ) ;
2017-08-17 10:02:11 +02:00
if ( rval ! = 0 & & rval ! = - ENOTSUPP ) {
dev_warn ( card - > dev , " Failed to set jack: %d \n " , rval ) ;
return rval ;
}
2015-06-10 13:15:54 +01:00
}
2017-08-02 15:17:47 +02:00
return 0 ;
2015-06-10 13:15:54 +01:00
}
2021-12-02 15:55:05 +01:00
static int apq8016_sbc_dai_init ( struct snd_soc_pcm_runtime * rtd )
{
struct snd_soc_dai * cpu_dai = asoc_rtd_to_cpu ( rtd , 0 ) ;
return apq8016_dai_init ( rtd , cpu_dai - > id ) ;
}
2020-07-23 20:39:03 +02:00
static void apq8016_sbc_add_ops ( struct snd_soc_card * card )
2015-06-10 13:15:54 +01:00
{
struct snd_soc_dai_link * link ;
2020-07-23 20:39:03 +02:00
int i ;
2015-06-10 13:15:54 +01:00
2020-07-23 20:39:03 +02:00
for_each_card_prelinks ( card , i , link )
2015-06-10 13:15:54 +01:00
link - > init = apq8016_sbc_dai_init ;
}
2021-12-02 15:55:05 +01:00
static int qdsp6_dai_get_lpass_id ( struct snd_soc_dai * cpu_dai )
{
switch ( cpu_dai - > id ) {
case PRIMARY_MI2S_RX :
case PRIMARY_MI2S_TX :
return MI2S_PRIMARY ;
case SECONDARY_MI2S_RX :
case SECONDARY_MI2S_TX :
return MI2S_SECONDARY ;
case TERTIARY_MI2S_RX :
case TERTIARY_MI2S_TX :
return MI2S_TERTIARY ;
case QUATERNARY_MI2S_RX :
case QUATERNARY_MI2S_TX :
return MI2S_QUATERNARY ;
default :
return - EINVAL ;
}
}
static int msm8916_qdsp6_dai_init ( struct snd_soc_pcm_runtime * rtd )
{
struct snd_soc_dai * cpu_dai = asoc_rtd_to_cpu ( rtd , 0 ) ;
2022-05-19 16:42:39 +01:00
snd_soc_dai_set_fmt ( cpu_dai , SND_SOC_DAIFMT_BP_FP ) ;
2021-12-02 15:55:05 +01:00
return apq8016_dai_init ( rtd , qdsp6_dai_get_lpass_id ( cpu_dai ) ) ;
}
static int msm8916_qdsp6_startup ( struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_card * card = rtd - > card ;
struct apq8016_sbc_data * data = snd_soc_card_get_drvdata ( card ) ;
struct snd_soc_dai * cpu_dai = asoc_rtd_to_cpu ( rtd , 0 ) ;
int mi2s , ret ;
mi2s = qdsp6_dai_get_lpass_id ( cpu_dai ) ;
if ( mi2s < 0 )
return mi2s ;
if ( + + data - > mi2s_clk_count [ mi2s ] > 1 )
return 0 ;
ret = snd_soc_dai_set_sysclk ( cpu_dai , LPAIF_BIT_CLK , MI2S_BCLK_RATE , 0 ) ;
if ( ret )
dev_err ( card - > dev , " Failed to enable LPAIF bit clk: %d \n " , ret ) ;
return ret ;
}
static void msm8916_qdsp6_shutdown ( struct snd_pcm_substream * substream )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
struct snd_soc_card * card = rtd - > card ;
struct apq8016_sbc_data * data = snd_soc_card_get_drvdata ( card ) ;
struct snd_soc_dai * cpu_dai = asoc_rtd_to_cpu ( rtd , 0 ) ;
int mi2s , ret ;
mi2s = qdsp6_dai_get_lpass_id ( cpu_dai ) ;
if ( mi2s < 0 )
return ;
if ( - - data - > mi2s_clk_count [ mi2s ] > 0 )
return ;
ret = snd_soc_dai_set_sysclk ( cpu_dai , LPAIF_BIT_CLK , 0 , 0 ) ;
if ( ret )
dev_err ( card - > dev , " Failed to disable LPAIF bit clk: %d \n " , ret ) ;
}
static const struct snd_soc_ops msm8916_qdsp6_be_ops = {
. startup = msm8916_qdsp6_startup ,
. shutdown = msm8916_qdsp6_shutdown ,
} ;
static int msm8916_qdsp6_be_hw_params_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 ) ;
rate - > min = rate - > max = 48000 ;
channels - > min = channels - > max = 2 ;
snd_mask_set_format ( fmt , SNDRV_PCM_FORMAT_S16_LE ) ;
return 0 ;
}
static void msm8916_qdsp6_add_ops ( struct snd_soc_card * card )
{
struct snd_soc_dai_link * link ;
int i ;
/* Make it obvious to userspace that QDSP6 is used */
card - > components = " qdsp6 " ;
for_each_card_prelinks ( card , i , link ) {
if ( link - > no_pcm ) {
link - > init = msm8916_qdsp6_dai_init ;
link - > ops = & msm8916_qdsp6_be_ops ;
link - > be_hw_params_fixup = msm8916_qdsp6_be_hw_params_fixup ;
}
}
}
2016-09-06 10:57:42 +01:00
static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets [ ] = {
SND_SOC_DAPM_MIC ( " Handset Mic " , NULL ) ,
SND_SOC_DAPM_MIC ( " Headset Mic " , NULL ) ,
SND_SOC_DAPM_MIC ( " Secondary Mic " , NULL ) ,
SND_SOC_DAPM_MIC ( " Digital Mic1 " , NULL ) ,
SND_SOC_DAPM_MIC ( " Digital Mic2 " , NULL ) ,
} ;
2015-06-10 13:15:54 +01:00
static int apq8016_sbc_platform_probe ( struct platform_device * pdev )
{
2021-12-02 15:55:05 +01:00
void ( * add_ops ) ( struct snd_soc_card * card ) ;
2015-06-10 13:15:54 +01:00
struct device * dev = & pdev - > dev ;
struct snd_soc_card * card ;
struct apq8016_sbc_data * data ;
2020-07-23 20:39:03 +02:00
int ret ;
2015-06-10 13:15:54 +01:00
2021-12-02 15:55:05 +01:00
add_ops = device_get_match_data ( & pdev - > dev ) ;
if ( ! add_ops )
return - EINVAL ;
2020-07-23 20:39:03 +02:00
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
2015-06-10 13:15:54 +01:00
return - ENOMEM ;
2020-07-23 20:39:03 +02:00
card = & data - > card ;
2015-06-10 13:15:54 +01:00
card - > dev = dev ;
2020-08-20 17:45:11 +02:00
card - > owner = THIS_MODULE ;
2016-09-06 10:57:42 +01:00
card - > dapm_widgets = apq8016_sbc_dapm_widgets ;
card - > num_dapm_widgets = ARRAY_SIZE ( apq8016_sbc_dapm_widgets ) ;
2020-07-23 20:39:03 +02:00
ret = qcom_snd_parse_of ( card ) ;
if ( ret )
return ret ;
2015-06-10 13:15:54 +01:00
2021-06-15 22:07:10 +08:00
data - > mic_iomux = devm_platform_ioremap_resource_byname ( pdev , " mic-iomux " ) ;
2015-06-10 13:15:54 +01:00
if ( IS_ERR ( data - > mic_iomux ) )
return PTR_ERR ( data - > mic_iomux ) ;
2021-06-15 22:07:10 +08:00
data - > spkr_iomux = devm_platform_ioremap_resource_byname ( pdev , " spkr-iomux " ) ;
2015-06-10 13:15:54 +01:00
if ( IS_ERR ( data - > spkr_iomux ) )
return PTR_ERR ( data - > spkr_iomux ) ;
snd_soc_card_set_drvdata ( card , data ) ;
2021-12-02 15:55:05 +01:00
add_ops ( card ) ;
2015-06-10 13:15:54 +01:00
return devm_snd_soc_register_card ( & pdev - > dev , card ) ;
}
2020-11-25 17:44:22 +01:00
static const struct of_device_id apq8016_sbc_device_id [ ] __maybe_unused = {
2021-12-02 15:55:05 +01:00
{ . compatible = " qcom,apq8016-sbc-sndcard " , . data = apq8016_sbc_add_ops } ,
{ . compatible = " qcom,msm8916-qdsp6-sndcard " , . data = msm8916_qdsp6_add_ops } ,
2015-06-10 13:15:54 +01:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , apq8016_sbc_device_id ) ;
static struct platform_driver apq8016_sbc_platform_driver = {
. driver = {
. name = " qcom-apq8016-sbc " ,
. of_match_table = of_match_ptr ( apq8016_sbc_device_id ) ,
} ,
. probe = apq8016_sbc_platform_probe ,
} ;
module_platform_driver ( apq8016_sbc_platform_driver ) ;
MODULE_AUTHOR ( " Srinivas Kandagatla <srinivas.kandagatla@linaro.org " ) ;
MODULE_DESCRIPTION ( " APQ8016 ASoC Machine Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;