2023-03-20 11:22:45 +00:00
// SPDX-License-Identifier: GPL-2.0-only
//
// Driver for Cirrus Logic CS35L56 smart amp
//
// Copyright (C) 2023 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
2024-02-09 11:18:40 +00:00
# include <linux/acpi.h>
2023-03-20 11:22:45 +00:00
# include <linux/completion.h>
# include <linux/debugfs.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/gpio/consumer.h>
2023-04-11 16:25:27 +01:00
# include <linux/interrupt.h>
2023-03-20 11:22:45 +00:00
# include <linux/math.h>
# include <linux/module.h>
2023-04-11 16:25:27 +01:00
# include <linux/pm.h>
2023-03-20 11:22:45 +00:00
# include <linux/pm_runtime.h>
2024-02-09 11:18:40 +00:00
# include <linux/property.h>
2023-03-20 11:22:45 +00:00
# include <linux/regmap.h>
# include <linux/regulator/consumer.h>
# include <linux/slab.h>
# include <linux/soundwire/sdw.h>
# include <linux/types.h>
# include <linux/workqueue.h>
2024-02-23 15:39:08 +00:00
# include <sound/cs-amp-lib.h>
2023-03-20 11:22:45 +00:00
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <sound/tlv.h>
# include "wm_adsp.h"
# include "cs35l56.h"
static int cs35l56_dsp_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event ) ;
2023-04-14 14:37:52 +01:00
static void cs35l56_wait_dsp_ready ( struct cs35l56_private * cs35l56 )
2023-03-20 11:22:45 +00:00
{
2023-04-14 14:37:52 +01:00
/* Wait for patching to complete */
flush_work ( & cs35l56 - > dsp_work ) ;
2023-03-20 11:22:45 +00:00
}
static int cs35l56_dspwait_get_volsw ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * component = snd_kcontrol_chip ( kcontrol ) ;
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
2023-04-14 14:37:52 +01:00
cs35l56_wait_dsp_ready ( cs35l56 ) ;
2023-03-20 11:22:45 +00:00
return snd_soc_get_volsw ( kcontrol , ucontrol ) ;
}
static int cs35l56_dspwait_put_volsw ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * component = snd_kcontrol_chip ( kcontrol ) ;
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
2023-04-14 14:37:52 +01:00
cs35l56_wait_dsp_ready ( cs35l56 ) ;
2023-03-20 11:22:45 +00:00
return snd_soc_put_volsw ( kcontrol , ucontrol ) ;
}
2024-01-29 16:27:29 +00:00
static const unsigned short cs35l56_asp1_mixer_regs [ ] = {
CS35L56_ASP1TX1_INPUT , CS35L56_ASP1TX2_INPUT ,
CS35L56_ASP1TX3_INPUT , CS35L56_ASP1TX4_INPUT ,
} ;
static const char * const cs35l56_asp1_mux_control_names [ ] = {
" ASP1 TX1 Source " , " ASP1 TX2 Source " , " ASP1 TX3 Source " , " ASP1 TX4 Source "
} ;
2024-02-08 12:37:42 +00:00
static int cs35l56_sync_asp1_mixer_widgets_with_firmware ( struct cs35l56_private * cs35l56 )
2024-01-29 16:27:29 +00:00
{
struct snd_soc_dapm_context * dapm = snd_soc_component_get_dapm ( cs35l56 - > component ) ;
const char * prefix = cs35l56 - > component - > name_prefix ;
char full_name [ SNDRV_CTL_ELEM_ID_NAME_MAXLEN ] ;
const char * name ;
struct snd_kcontrol * kcontrol ;
struct soc_enum * e ;
unsigned int val [ 4 ] ;
int i , item , ret ;
2024-02-08 12:37:42 +00:00
if ( cs35l56 - > asp1_mixer_widgets_initialized )
return 0 ;
2024-01-29 16:27:29 +00:00
/*
* Resume so we can read the registers from silicon if the regmap
* cache has not yet been populated .
*/
ret = pm_runtime_resume_and_get ( cs35l56 - > base . dev ) ;
if ( ret < 0 )
2024-02-08 12:37:42 +00:00
return ret ;
/* Wait for firmware download and reboot */
cs35l56_wait_dsp_ready ( cs35l56 ) ;
2024-01-29 16:27:29 +00:00
ret = regmap_bulk_read ( cs35l56 - > base . regmap , CS35L56_ASP1TX1_INPUT ,
val , ARRAY_SIZE ( val ) ) ;
pm_runtime_mark_last_busy ( cs35l56 - > base . dev ) ;
pm_runtime_put_autosuspend ( cs35l56 - > base . dev ) ;
if ( ret ) {
dev_err ( cs35l56 - > base . dev , " Failed to read ASP1 mixer regs: %d \n " , ret ) ;
2024-02-08 12:37:42 +00:00
return ret ;
2024-01-29 16:27:29 +00:00
}
for ( i = 0 ; i < ARRAY_SIZE ( cs35l56_asp1_mux_control_names ) ; + + i ) {
name = cs35l56_asp1_mux_control_names [ i ] ;
if ( prefix ) {
snprintf ( full_name , sizeof ( full_name ) , " %s %s " , prefix , name ) ;
name = full_name ;
}
ASoC: soc-card: Fix missing locking in snd_soc_card_get_kcontrol()
snd_soc_card_get_kcontrol() must be holding a read lock on
card->controls_rwsem while walking the controls list.
Compare with snd_ctl_find_numid().
The existing function is renamed snd_soc_card_get_kcontrol_locked()
so that it can be called from contexts that are already holding
card->controls_rwsem (for example, control get/put functions).
There are few direct or indirect callers of
snd_soc_card_get_kcontrol(), and most are safe. Three require
changes, which have been included in this patch:
codecs/cs35l45.c:
cs35l45_activate_ctl() is called from a control put() function so
is changed to call snd_soc_card_get_kcontrol_locked().
codecs/cs35l56.c:
cs35l56_sync_asp1_mixer_widgets_with_firmware() is called from
control get()/put() functions so is changed to call
snd_soc_card_get_kcontrol_locked().
fsl/fsl_xcvr.c:
fsl_xcvr_activate_ctl() is called from three places, one of which
already holds card->controls_rwsem:
1. fsl_xcvr_mode_put(), a control put function, which will
already be holding card->controls_rwsem.
2. fsl_xcvr_startup(), a DAI startup function.
3. fsl_xcvr_shutdown(), a DAI shutdown function.
To fix this, fsl_xcvr_activate_ctl() has been changed to call
snd_soc_card_get_kcontrol_locked() so that it is safe to call
directly from fsl_xcvr_mode_put().
The fsl_xcvr_startup() and fsl_xcvr_shutdown() functions have been
changed to take a read lock on card->controls_rsem() around calls
to fsl_xcvr_activate_ctl(). While this is not very elegant, it
keeps the change small, to avoid this patch creating a large
collateral churn in fsl/fsl_xcvr.c.
Analysis of other callers of snd_soc_card_get_kcontrol() is that
they do not need any changes, they are not holding card->controls_rwsem
when they call snd_soc_card_get_kcontrol().
Direct callers of snd_soc_card_get_kcontrol():
fsl/fsl_spdif.c: fsl_spdif_dai_probe() - DAI probe function
fsl/fsl_micfil.c: voice_detected_fn() - IRQ handler
Indirect callers via soc_component_notify_control():
codecs/cs42l43: cs42l43_mic_shutter() - IRQ handler
codecs/cs42l43: cs42l43_spk_shutter() - IRQ handler
codecs/ak4118.c: ak4118_irq_handler() - IRQ handler
codecs/wm_adsp.c: wm_adsp_write_ctl() - not currently used
Indirect callers via snd_soc_limit_volume():
qcom/sc8280xp.c: sc8280xp_snd_init() - DAIlink init function
ti/rx51.c: rx51_aic34_init() - DAI init function
I don't have hardware to test the fsl/*, qcom/sc828xp.c, ti/rx51.c
and ak4118.c changes.
Backport note:
The fsl/, qcom/, cs35l45, cs35l56 and cs42l43 callers were added
since the Fixes commit so won't all be present on older kernels.
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Fixes: 209c6cdfd283 ("ASoC: soc-card: move snd_soc_card_get_kcontrol() to soc-card")
Link: https://lore.kernel.org/r/20240221123710.690224-1-rf@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
2024-02-21 12:37:10 +00:00
kcontrol = snd_soc_card_get_kcontrol_locked ( dapm - > card , name ) ;
2024-01-29 16:27:29 +00:00
if ( ! kcontrol ) {
dev_warn ( cs35l56 - > base . dev , " Could not find control %s \n " , name ) ;
continue ;
}
e = ( struct soc_enum * ) kcontrol - > private_value ;
item = snd_soc_enum_val_to_item ( e , val [ i ] & CS35L56_ASP_TXn_SRC_MASK ) ;
snd_soc_dapm_mux_update_power ( dapm , kcontrol , item , e , NULL ) ;
}
2024-02-08 12:37:42 +00:00
cs35l56 - > asp1_mixer_widgets_initialized = true ;
return 0 ;
2024-01-29 16:27:29 +00:00
}
2024-02-08 12:37:42 +00:00
static int cs35l56_dspwait_asp1tx_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2024-01-29 16:27:29 +00:00
{
2024-02-08 12:37:42 +00:00
struct snd_soc_component * component = snd_soc_dapm_kcontrol_component ( kcontrol ) ;
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
int index = e - > shift_l ;
unsigned int addr , val ;
int ret ;
2024-01-29 16:27:29 +00:00
2024-02-08 12:37:42 +00:00
ret = cs35l56_sync_asp1_mixer_widgets_with_firmware ( cs35l56 ) ;
if ( ret )
return ret ;
addr = cs35l56_asp1_mixer_regs [ index ] ;
ret = regmap_read ( cs35l56 - > base . regmap , addr , & val ) ;
if ( ret )
return ret ;
val & = CS35L56_ASP_TXn_SRC_MASK ;
ucontrol - > value . enumerated . item [ 0 ] = snd_soc_enum_val_to_item ( e , val ) ;
return 0 ;
}
static int cs35l56_dspwait_asp1tx_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * component = snd_soc_dapm_kcontrol_component ( kcontrol ) ;
struct snd_soc_dapm_context * dapm = snd_soc_dapm_kcontrol_dapm ( kcontrol ) ;
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
struct soc_enum * e = ( struct soc_enum * ) kcontrol - > private_value ;
int item = ucontrol - > value . enumerated . item [ 0 ] ;
int index = e - > shift_l ;
unsigned int addr , val ;
bool changed ;
int ret ;
ret = cs35l56_sync_asp1_mixer_widgets_with_firmware ( cs35l56 ) ;
if ( ret )
return ret ;
addr = cs35l56_asp1_mixer_regs [ index ] ;
val = snd_soc_enum_item_to_val ( e , item ) ;
ret = regmap_update_bits_check ( cs35l56 - > base . regmap , addr ,
CS35L56_ASP_TXn_SRC_MASK , val , & changed ) ;
if ( ret )
return ret ;
if ( changed )
snd_soc_dapm_mux_update_power ( dapm , kcontrol , item , e , NULL ) ;
return changed ;
2024-01-29 16:27:29 +00:00
}
2023-03-20 11:22:45 +00:00
static DECLARE_TLV_DB_SCALE ( vol_tlv , - 10000 , 25 , 0 ) ;
static const struct snd_kcontrol_new cs35l56_controls [ ] = {
SOC_SINGLE_EXT ( " Speaker Switch " ,
CS35L56_MAIN_RENDER_USER_MUTE , 0 , 1 , 1 ,
cs35l56_dspwait_get_volsw , cs35l56_dspwait_put_volsw ) ,
SOC_SINGLE_S_EXT_TLV ( " Speaker Volume " ,
CS35L56_MAIN_RENDER_USER_VOLUME ,
6 , - 400 , 400 , 9 , 0 ,
cs35l56_dspwait_get_volsw ,
cs35l56_dspwait_put_volsw ,
vol_tlv ) ,
SOC_SINGLE_EXT ( " Posture Number " , CS35L56_MAIN_POSTURE_NUMBER ,
0 , 255 , 0 ,
cs35l56_dspwait_get_volsw , cs35l56_dspwait_put_volsw ) ,
} ;
static SOC_VALUE_ENUM_SINGLE_DECL ( cs35l56_asp1tx1_enum ,
2024-01-29 16:27:29 +00:00
SND_SOC_NOPM ,
0 , 0 ,
2023-03-20 11:22:45 +00:00
cs35l56_tx_input_texts ,
cs35l56_tx_input_values ) ;
static const struct snd_kcontrol_new asp1_tx1_mux =
2024-01-29 16:27:29 +00:00
SOC_DAPM_ENUM_EXT ( " ASP1TX1 SRC " , cs35l56_asp1tx1_enum ,
cs35l56_dspwait_asp1tx_get , cs35l56_dspwait_asp1tx_put ) ;
2023-03-20 11:22:45 +00:00
static SOC_VALUE_ENUM_SINGLE_DECL ( cs35l56_asp1tx2_enum ,
2024-01-29 16:27:29 +00:00
SND_SOC_NOPM ,
1 , 0 ,
2023-03-20 11:22:45 +00:00
cs35l56_tx_input_texts ,
cs35l56_tx_input_values ) ;
static const struct snd_kcontrol_new asp1_tx2_mux =
2024-01-29 16:27:29 +00:00
SOC_DAPM_ENUM_EXT ( " ASP1TX2 SRC " , cs35l56_asp1tx2_enum ,
cs35l56_dspwait_asp1tx_get , cs35l56_dspwait_asp1tx_put ) ;
2023-03-20 11:22:45 +00:00
static SOC_VALUE_ENUM_SINGLE_DECL ( cs35l56_asp1tx3_enum ,
2024-01-29 16:27:29 +00:00
SND_SOC_NOPM ,
2 , 0 ,
2023-03-20 11:22:45 +00:00
cs35l56_tx_input_texts ,
cs35l56_tx_input_values ) ;
static const struct snd_kcontrol_new asp1_tx3_mux =
2024-01-29 16:27:29 +00:00
SOC_DAPM_ENUM_EXT ( " ASP1TX3 SRC " , cs35l56_asp1tx3_enum ,
cs35l56_dspwait_asp1tx_get , cs35l56_dspwait_asp1tx_put ) ;
2023-03-20 11:22:45 +00:00
static SOC_VALUE_ENUM_SINGLE_DECL ( cs35l56_asp1tx4_enum ,
2024-01-29 16:27:29 +00:00
SND_SOC_NOPM ,
3 , 0 ,
2023-03-20 11:22:45 +00:00
cs35l56_tx_input_texts ,
cs35l56_tx_input_values ) ;
static const struct snd_kcontrol_new asp1_tx4_mux =
2024-01-29 16:27:29 +00:00
SOC_DAPM_ENUM_EXT ( " ASP1TX4 SRC " , cs35l56_asp1tx4_enum ,
cs35l56_dspwait_asp1tx_get , cs35l56_dspwait_asp1tx_put ) ;
2023-03-20 11:22:45 +00:00
static SOC_VALUE_ENUM_SINGLE_DECL ( cs35l56_sdw1tx1_enum ,
CS35L56_SWIRE_DP3_CH1_INPUT ,
0 , CS35L56_SWIRETXn_SRC_MASK ,
cs35l56_tx_input_texts ,
cs35l56_tx_input_values ) ;
static const struct snd_kcontrol_new sdw1_tx1_mux =
SOC_DAPM_ENUM ( " SDW1TX1 SRC " , cs35l56_sdw1tx1_enum ) ;
static SOC_VALUE_ENUM_SINGLE_DECL ( cs35l56_sdw1tx2_enum ,
CS35L56_SWIRE_DP3_CH2_INPUT ,
0 , CS35L56_SWIRETXn_SRC_MASK ,
cs35l56_tx_input_texts ,
cs35l56_tx_input_values ) ;
static const struct snd_kcontrol_new sdw1_tx2_mux =
SOC_DAPM_ENUM ( " SDW1TX2 SRC " , cs35l56_sdw1tx2_enum ) ;
static SOC_VALUE_ENUM_SINGLE_DECL ( cs35l56_sdw1tx3_enum ,
CS35L56_SWIRE_DP3_CH3_INPUT ,
0 , CS35L56_SWIRETXn_SRC_MASK ,
cs35l56_tx_input_texts ,
cs35l56_tx_input_values ) ;
static const struct snd_kcontrol_new sdw1_tx3_mux =
SOC_DAPM_ENUM ( " SDW1TX3 SRC " , cs35l56_sdw1tx3_enum ) ;
static SOC_VALUE_ENUM_SINGLE_DECL ( cs35l56_sdw1tx4_enum ,
CS35L56_SWIRE_DP3_CH4_INPUT ,
0 , CS35L56_SWIRETXn_SRC_MASK ,
cs35l56_tx_input_texts ,
cs35l56_tx_input_values ) ;
static const struct snd_kcontrol_new sdw1_tx4_mux =
SOC_DAPM_ENUM ( " SDW1TX4 SRC " , cs35l56_sdw1tx4_enum ) ;
2024-01-29 16:27:26 +00:00
static int cs35l56_asp1_cfg_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
/* Override register values set by firmware boot */
return cs35l56_force_sync_asp1_registers_from_cache ( & cs35l56 - > base ) ;
default :
return 0 ;
}
}
2023-04-11 16:25:24 +01:00
static int cs35l56_play_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
unsigned int val ;
int ret ;
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " play: %d \n " , event ) ;
2023-04-11 16:25:24 +01:00
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
/* Don't wait for ACK, we check in POST_PMU that it completed */
2023-07-21 14:21:10 +01:00
return regmap_write ( cs35l56 - > base . regmap , CS35L56_DSP_VIRTUAL1_MBOX_1 ,
2023-04-11 16:25:24 +01:00
CS35L56_MBOX_CMD_AUDIO_PLAY ) ;
case SND_SOC_DAPM_POST_PMU :
/* Wait for firmware to enter PS0 power state */
2023-07-21 14:21:10 +01:00
ret = regmap_read_poll_timeout ( cs35l56 - > base . regmap ,
2023-04-11 16:25:24 +01:00
CS35L56_TRANSDUCER_ACTUAL_PS ,
val , ( val = = CS35L56_PS0 ) ,
CS35L56_PS0_POLL_US ,
CS35L56_PS0_TIMEOUT_US ) ;
if ( ret )
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " PS0 wait failed: %d \n " , ret ) ;
2023-04-11 16:25:24 +01:00
return ret ;
case SND_SOC_DAPM_POST_PMD :
2023-07-21 14:21:10 +01:00
return cs35l56_mbox_send ( & cs35l56 - > base , CS35L56_MBOX_CMD_AUDIO_PAUSE ) ;
2023-04-11 16:25:24 +01:00
default :
return 0 ;
}
}
2023-03-20 11:22:45 +00:00
static const struct snd_soc_dapm_widget cs35l56_dapm_widgets [ ] = {
SND_SOC_DAPM_REGULATOR_SUPPLY ( " VDD_B " , 0 , 0 ) ,
SND_SOC_DAPM_REGULATOR_SUPPLY ( " VDD_AMP " , 0 , 0 ) ,
2024-01-29 16:27:26 +00:00
SND_SOC_DAPM_SUPPLY ( " ASP1 CFG " , SND_SOC_NOPM , 0 , 0 , cs35l56_asp1_cfg_event ,
SND_SOC_DAPM_PRE_PMU ) ,
2023-04-11 16:25:24 +01:00
SND_SOC_DAPM_SUPPLY ( " PLAY " , SND_SOC_NOPM , 0 , 0 , cs35l56_play_event ,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD ) ,
2023-03-20 11:22:45 +00:00
SND_SOC_DAPM_OUT_DRV ( " AMP " , SND_SOC_NOPM , 0 , 0 , NULL , 0 ) ,
SND_SOC_DAPM_OUTPUT ( " SPK " ) ,
SND_SOC_DAPM_PGA_E ( " DSP1 " , SND_SOC_NOPM , 0 , 0 , NULL , 0 , cs35l56_dsp_event ,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD ) ,
SND_SOC_DAPM_AIF_IN ( " ASP1RX1 " , NULL , 0 , CS35L56_ASP1_ENABLES1 ,
CS35L56_ASP_RX1_EN_SHIFT , 0 ) ,
SND_SOC_DAPM_AIF_IN ( " ASP1RX2 " , NULL , 1 , CS35L56_ASP1_ENABLES1 ,
CS35L56_ASP_RX2_EN_SHIFT , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " ASP1TX1 " , NULL , 0 , CS35L56_ASP1_ENABLES1 ,
CS35L56_ASP_TX1_EN_SHIFT , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " ASP1TX2 " , NULL , 1 , CS35L56_ASP1_ENABLES1 ,
CS35L56_ASP_TX2_EN_SHIFT , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " ASP1TX3 " , NULL , 2 , CS35L56_ASP1_ENABLES1 ,
CS35L56_ASP_TX3_EN_SHIFT , 0 ) ,
SND_SOC_DAPM_AIF_OUT ( " ASP1TX4 " , NULL , 3 , CS35L56_ASP1_ENABLES1 ,
CS35L56_ASP_TX4_EN_SHIFT , 0 ) ,
SND_SOC_DAPM_MUX ( " ASP1 TX1 Source " , SND_SOC_NOPM , 0 , 0 , & asp1_tx1_mux ) ,
SND_SOC_DAPM_MUX ( " ASP1 TX2 Source " , SND_SOC_NOPM , 0 , 0 , & asp1_tx2_mux ) ,
SND_SOC_DAPM_MUX ( " ASP1 TX3 Source " , SND_SOC_NOPM , 0 , 0 , & asp1_tx3_mux ) ,
SND_SOC_DAPM_MUX ( " ASP1 TX4 Source " , SND_SOC_NOPM , 0 , 0 , & asp1_tx4_mux ) ,
SND_SOC_DAPM_MUX ( " SDW1 TX1 Source " , SND_SOC_NOPM , 0 , 0 , & sdw1_tx1_mux ) ,
SND_SOC_DAPM_MUX ( " SDW1 TX2 Source " , SND_SOC_NOPM , 0 , 0 , & sdw1_tx2_mux ) ,
SND_SOC_DAPM_MUX ( " SDW1 TX3 Source " , SND_SOC_NOPM , 0 , 0 , & sdw1_tx3_mux ) ,
SND_SOC_DAPM_MUX ( " SDW1 TX4 Source " , SND_SOC_NOPM , 0 , 0 , & sdw1_tx4_mux ) ,
SND_SOC_DAPM_SIGGEN ( " VMON ADC " ) ,
SND_SOC_DAPM_SIGGEN ( " IMON ADC " ) ,
SND_SOC_DAPM_SIGGEN ( " ERRVOL ADC " ) ,
SND_SOC_DAPM_SIGGEN ( " CLASSH ADC " ) ,
SND_SOC_DAPM_SIGGEN ( " VDDBMON ADC " ) ,
SND_SOC_DAPM_SIGGEN ( " VBSTMON ADC " ) ,
SND_SOC_DAPM_SIGGEN ( " TEMPMON ADC " ) ,
} ;
# define CS35L56_SRC_ROUTE(name) \
{ name " Source " , " ASP1RX1 " , " ASP1RX1 " } , \
{ name " Source " , " ASP1RX2 " , " ASP1RX2 " } , \
{ name " Source " , " VMON " , " VMON ADC " } , \
{ name " Source " , " IMON " , " IMON ADC " } , \
{ name " Source " , " ERRVOL " , " ERRVOL ADC " } , \
{ name " Source " , " CLASSH " , " CLASSH ADC " } , \
{ name " Source " , " VDDBMON " , " VDDBMON ADC " } , \
{ name " Source " , " VBSTMON " , " VBSTMON ADC " } , \
{ name " Source " , " DSP1TX1 " , " DSP1 " } , \
{ name " Source " , " DSP1TX2 " , " DSP1 " } , \
{ name " Source " , " DSP1TX3 " , " DSP1 " } , \
{ name " Source " , " DSP1TX4 " , " DSP1 " } , \
{ name " Source " , " DSP1TX5 " , " DSP1 " } , \
{ name " Source " , " DSP1TX6 " , " DSP1 " } , \
{ name " Source " , " DSP1TX7 " , " DSP1 " } , \
{ name " Source " , " DSP1TX8 " , " DSP1 " } , \
{ name " Source " , " TEMPMON " , " TEMPMON ADC " } , \
{ name " Source " , " INTERPOLATOR " , " AMP " } , \
{ name " Source " , " SDW1RX1 " , " SDW1 Playback " } , \
{ name " Source " , " SDW1RX2 " , " SDW1 Playback " } ,
static const struct snd_soc_dapm_route cs35l56_audio_map [ ] = {
{ " AMP " , NULL , " VDD_B " } ,
{ " AMP " , NULL , " VDD_AMP " } ,
2024-01-29 16:27:26 +00:00
{ " ASP1 Playback " , NULL , " ASP1 CFG " } ,
{ " ASP1 Capture " , NULL , " ASP1 CFG " } ,
2023-04-11 16:25:24 +01:00
{ " ASP1 Playback " , NULL , " PLAY " } ,
{ " SDW1 Playback " , NULL , " PLAY " } ,
2023-03-20 11:22:45 +00:00
{ " ASP1RX1 " , NULL , " ASP1 Playback " } ,
{ " ASP1RX2 " , NULL , " ASP1 Playback " } ,
{ " DSP1 " , NULL , " ASP1RX1 " } ,
{ " DSP1 " , NULL , " ASP1RX2 " } ,
{ " DSP1 " , NULL , " SDW1 Playback " } ,
{ " AMP " , NULL , " DSP1 " } ,
{ " SPK " , NULL , " AMP " } ,
CS35L56_SRC_ROUTE ( " ASP1 TX1 " )
CS35L56_SRC_ROUTE ( " ASP1 TX2 " )
CS35L56_SRC_ROUTE ( " ASP1 TX3 " )
CS35L56_SRC_ROUTE ( " ASP1 TX4 " )
{ " ASP1TX1 " , NULL , " ASP1 TX1 Source " } ,
{ " ASP1TX2 " , NULL , " ASP1 TX2 Source " } ,
{ " ASP1TX3 " , NULL , " ASP1 TX3 Source " } ,
{ " ASP1TX4 " , NULL , " ASP1 TX4 Source " } ,
{ " ASP1 Capture " , NULL , " ASP1TX1 " } ,
{ " ASP1 Capture " , NULL , " ASP1TX2 " } ,
{ " ASP1 Capture " , NULL , " ASP1TX3 " } ,
{ " ASP1 Capture " , NULL , " ASP1TX4 " } ,
CS35L56_SRC_ROUTE ( " SDW1 TX1 " )
CS35L56_SRC_ROUTE ( " SDW1 TX2 " )
CS35L56_SRC_ROUTE ( " SDW1 TX3 " )
CS35L56_SRC_ROUTE ( " SDW1 TX4 " )
{ " SDW1 Capture " , NULL , " SDW1 TX1 Source " } ,
{ " SDW1 Capture " , NULL , " SDW1 TX2 Source " } ,
{ " SDW1 Capture " , NULL , " SDW1 TX3 Source " } ,
{ " SDW1 Capture " , NULL , " SDW1 TX4 Source " } ,
} ;
static int cs35l56_dsp_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * kcontrol , int event )
{
struct snd_soc_component * component = snd_soc_dapm_to_component ( w - > dapm ) ;
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " %s: %d \n " , __func__ , event ) ;
2023-03-20 11:22:45 +00:00
return wm_adsp_event ( w , kcontrol , event ) ;
}
static int cs35l56_asp_dai_set_fmt ( struct snd_soc_dai * codec_dai , unsigned int fmt )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( codec_dai - > component ) ;
unsigned int val ;
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " %s: %#x \n " , __func__ , fmt ) ;
2023-03-20 11:22:45 +00:00
switch ( fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK ) {
case SND_SOC_DAIFMT_CBC_CFC :
break ;
default :
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " Unsupported clock source mode \n " ) ;
2023-03-20 11:22:45 +00:00
return - EINVAL ;
}
switch ( fmt & SND_SOC_DAIFMT_FORMAT_MASK ) {
case SND_SOC_DAIFMT_DSP_A :
val = CS35L56_ASP_FMT_DSP_A < < CS35L56_ASP_FMT_SHIFT ;
cs35l56 - > tdm_mode = true ;
break ;
case SND_SOC_DAIFMT_I2S :
val = CS35L56_ASP_FMT_I2S < < CS35L56_ASP_FMT_SHIFT ;
cs35l56 - > tdm_mode = false ;
break ;
default :
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " Unsupported DAI format \n " ) ;
2023-03-20 11:22:45 +00:00
return - EINVAL ;
}
switch ( fmt & SND_SOC_DAIFMT_INV_MASK ) {
case SND_SOC_DAIFMT_NB_IF :
val | = CS35L56_ASP_FSYNC_INV_MASK ;
break ;
case SND_SOC_DAIFMT_IB_NF :
val | = CS35L56_ASP_BCLK_INV_MASK ;
break ;
case SND_SOC_DAIFMT_IB_IF :
val | = CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK ;
break ;
case SND_SOC_DAIFMT_NB_NF :
break ;
default :
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " Invalid clock invert \n " ) ;
2023-03-20 11:22:45 +00:00
return - EINVAL ;
}
2023-07-21 14:21:10 +01:00
regmap_update_bits ( cs35l56 - > base . regmap ,
2023-03-20 11:22:45 +00:00
CS35L56_ASP1_CONTROL2 ,
CS35L56_ASP_FMT_MASK |
CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK ,
val ) ;
/* Hi-Z DOUT in unused slots and when all TX are disabled */
2023-07-21 14:21:10 +01:00
regmap_update_bits ( cs35l56 - > base . regmap , CS35L56_ASP1_CONTROL3 ,
2023-03-20 11:22:45 +00:00
CS35L56_ASP1_DOUT_HIZ_CTRL_MASK ,
CS35L56_ASP_UNUSED_HIZ_OFF_HIZ ) ;
return 0 ;
}
2023-08-08 17:46:58 +01:00
static unsigned int cs35l56_make_tdm_config_word ( unsigned int reg_val , unsigned long mask )
2023-03-20 11:22:45 +00:00
{
2023-08-08 17:46:58 +01:00
unsigned int channel_shift ;
2023-03-20 11:22:45 +00:00
int bit_num ;
/* Enable consecutive TX1..TXn for each of the slots set in mask */
channel_shift = 0 ;
for_each_set_bit ( bit_num , & mask , 32 ) {
reg_val & = ~ ( 0x3f < < channel_shift ) ;
reg_val | = bit_num < < channel_shift ;
channel_shift + = 8 ;
}
2023-08-08 17:46:58 +01:00
return reg_val ;
2023-03-20 11:22:45 +00:00
}
static int cs35l56_asp_dai_set_tdm_slot ( struct snd_soc_dai * dai , unsigned int tx_mask ,
unsigned int rx_mask , int slots , int slot_width )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( dai - > component ) ;
if ( ( slots = = 0 ) | | ( slot_width = = 0 ) ) {
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " tdm config cleared \n " ) ;
2023-03-20 11:22:45 +00:00
cs35l56 - > asp_slot_width = 0 ;
cs35l56 - > asp_slot_count = 0 ;
return 0 ;
}
if ( slot_width > ( CS35L56_ASP_RX_WIDTH_MASK > > CS35L56_ASP_RX_WIDTH_SHIFT ) ) {
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " tdm invalid slot width %d \n " , slot_width ) ;
2023-03-20 11:22:45 +00:00
return - EINVAL ;
}
/* More than 32 slots would give an unsupportable BCLK frequency */
if ( slots > 32 ) {
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " tdm invalid slot count %d \n " , slots ) ;
2023-03-20 11:22:45 +00:00
return - EINVAL ;
}
cs35l56 - > asp_slot_width = ( u8 ) slot_width ;
cs35l56 - > asp_slot_count = ( u8 ) slots ;
// Note: rx/tx is from point of view of the CPU end
if ( tx_mask = = 0 )
tx_mask = 0x3 ; // ASPRX1/RX2 in slots 0 and 1
if ( rx_mask = = 0 )
rx_mask = 0xf ; // ASPTX1..TX4 in slots 0..3
2023-08-08 17:46:58 +01:00
/* Default unused slots to 63 */
regmap_write ( cs35l56 - > base . regmap , CS35L56_ASP1_FRAME_CONTROL1 ,
cs35l56_make_tdm_config_word ( 0x3f3f3f3f , rx_mask ) ) ;
regmap_write ( cs35l56 - > base . regmap , CS35L56_ASP1_FRAME_CONTROL5 ,
cs35l56_make_tdm_config_word ( 0x3f3f3f , tx_mask ) ) ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x \n " ,
2023-03-20 11:22:45 +00:00
cs35l56 - > asp_slot_width , cs35l56 - > asp_slot_count , tx_mask , rx_mask ) ;
return 0 ;
}
static int cs35l56_asp_dai_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( dai - > component ) ;
unsigned int rate = params_rate ( params ) ;
u8 asp_width , asp_wl ;
asp_wl = params_width ( params ) ;
if ( cs35l56 - > asp_slot_width )
asp_width = cs35l56 - > asp_slot_width ;
else
asp_width = asp_wl ;
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " %s: wl=%d, width=%d, rate=%d " ,
__func__ , asp_wl , asp_width , rate ) ;
2023-03-20 11:22:45 +00:00
if ( ! cs35l56 - > sysclk_set ) {
unsigned int slots = cs35l56 - > asp_slot_count ;
unsigned int bclk_freq ;
int freq_id ;
if ( slots = = 0 ) {
slots = params_channels ( params ) ;
/* I2S always has an even number of slots */
if ( ! cs35l56 - > tdm_mode )
slots = round_up ( slots , 2 ) ;
}
bclk_freq = asp_width * slots * rate ;
freq_id = cs35l56_get_bclk_freq_id ( bclk_freq ) ;
if ( freq_id < 0 ) {
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " %s: Invalid BCLK %u \n " , __func__ , bclk_freq ) ;
2023-03-20 11:22:45 +00:00
return - EINVAL ;
}
2023-07-21 14:21:10 +01:00
regmap_update_bits ( cs35l56 - > base . regmap , CS35L56_ASP1_CONTROL1 ,
2023-03-20 11:22:45 +00:00
CS35L56_ASP_BCLK_FREQ_MASK ,
freq_id < < CS35L56_ASP_BCLK_FREQ_SHIFT ) ;
}
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
2023-07-21 14:21:10 +01:00
regmap_update_bits ( cs35l56 - > base . regmap , CS35L56_ASP1_CONTROL2 ,
2023-03-20 11:22:45 +00:00
CS35L56_ASP_RX_WIDTH_MASK , asp_width < <
CS35L56_ASP_RX_WIDTH_SHIFT ) ;
2023-07-21 14:21:10 +01:00
regmap_update_bits ( cs35l56 - > base . regmap , CS35L56_ASP1_DATA_CONTROL5 ,
2023-03-20 11:22:45 +00:00
CS35L56_ASP_RX_WL_MASK , asp_wl ) ;
} else {
2023-07-21 14:21:10 +01:00
regmap_update_bits ( cs35l56 - > base . regmap , CS35L56_ASP1_CONTROL2 ,
2023-03-20 11:22:45 +00:00
CS35L56_ASP_TX_WIDTH_MASK , asp_width < <
CS35L56_ASP_TX_WIDTH_SHIFT ) ;
2023-07-21 14:21:10 +01:00
regmap_update_bits ( cs35l56 - > base . regmap , CS35L56_ASP1_DATA_CONTROL1 ,
2023-03-20 11:22:45 +00:00
CS35L56_ASP_TX_WL_MASK , asp_wl ) ;
}
return 0 ;
}
static int cs35l56_asp_dai_set_sysclk ( struct snd_soc_dai * dai ,
int clk_id , unsigned int freq , int dir )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( dai - > component ) ;
int freq_id ;
if ( freq = = 0 ) {
cs35l56 - > sysclk_set = false ;
return 0 ;
}
freq_id = cs35l56_get_bclk_freq_id ( freq ) ;
if ( freq_id < 0 )
return freq_id ;
2023-07-21 14:21:10 +01:00
regmap_update_bits ( cs35l56 - > base . regmap , CS35L56_ASP1_CONTROL1 ,
2023-03-20 11:22:45 +00:00
CS35L56_ASP_BCLK_FREQ_MASK ,
freq_id < < CS35L56_ASP_BCLK_FREQ_SHIFT ) ;
cs35l56 - > sysclk_set = true ;
return 0 ;
}
static const struct snd_soc_dai_ops cs35l56_ops = {
. set_fmt = cs35l56_asp_dai_set_fmt ,
. set_tdm_slot = cs35l56_asp_dai_set_tdm_slot ,
. hw_params = cs35l56_asp_dai_hw_params ,
. set_sysclk = cs35l56_asp_dai_set_sysclk ,
} ;
static void cs35l56_sdw_dai_shutdown ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
snd_soc_dai_set_dma_data ( dai , substream , NULL ) ;
}
static int cs35l56_sdw_dai_set_tdm_slot ( struct snd_soc_dai * dai , unsigned int tx_mask ,
unsigned int rx_mask , int slots , int slot_width )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( dai - > component ) ;
/* rx/tx are from point of view of the CPU end so opposite to our rx/tx */
cs35l56 - > rx_mask = tx_mask ;
cs35l56 - > tx_mask = rx_mask ;
return 0 ;
}
static int cs35l56_sdw_dai_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params ,
struct snd_soc_dai * dai )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( dai - > component ) ;
struct sdw_stream_runtime * sdw_stream = snd_soc_dai_get_dma_data ( dai , substream ) ;
struct sdw_stream_config sconfig ;
struct sdw_port_config pconfig ;
int ret ;
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " %s: rate %d \n " , __func__ , params_rate ( params ) ) ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:10 +01:00
if ( ! cs35l56 - > base . init_done )
2023-03-20 11:22:45 +00:00
return - ENODEV ;
if ( ! sdw_stream )
return - EINVAL ;
memset ( & sconfig , 0 , sizeof ( sconfig ) ) ;
memset ( & pconfig , 0 , sizeof ( pconfig ) ) ;
sconfig . frame_rate = params_rate ( params ) ;
sconfig . bps = snd_pcm_format_width ( params_format ( params ) ) ;
if ( substream - > stream = = SNDRV_PCM_STREAM_PLAYBACK ) {
sconfig . direction = SDW_DATA_DIR_RX ;
pconfig . num = CS35L56_SDW1_PLAYBACK_PORT ;
pconfig . ch_mask = cs35l56 - > rx_mask ;
} else {
sconfig . direction = SDW_DATA_DIR_TX ;
pconfig . num = CS35L56_SDW1_CAPTURE_PORT ;
pconfig . ch_mask = cs35l56 - > tx_mask ;
}
if ( pconfig . ch_mask = = 0 ) {
sconfig . ch_count = params_channels ( params ) ;
pconfig . ch_mask = GENMASK ( sconfig . ch_count - 1 , 0 ) ;
} else {
sconfig . ch_count = hweight32 ( pconfig . ch_mask ) ;
}
ret = sdw_stream_add_slave ( cs35l56 - > sdw_peripheral , & sconfig , & pconfig ,
1 , sdw_stream ) ;
if ( ret ) {
dev_err ( dai - > dev , " Failed to add sdw stream: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static int cs35l56_sdw_dai_hw_free ( struct snd_pcm_substream * substream ,
struct snd_soc_dai * dai )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( dai - > component ) ;
struct sdw_stream_runtime * sdw_stream = snd_soc_dai_get_dma_data ( dai , substream ) ;
if ( ! cs35l56 - > sdw_peripheral )
return - EINVAL ;
sdw_stream_remove_slave ( cs35l56 - > sdw_peripheral , sdw_stream ) ;
return 0 ;
}
static int cs35l56_sdw_dai_set_stream ( struct snd_soc_dai * dai ,
void * sdw_stream , int direction )
{
snd_soc_dai_dma_data_set ( dai , direction , sdw_stream ) ;
return 0 ;
}
static const struct snd_soc_dai_ops cs35l56_sdw_dai_ops = {
. set_tdm_slot = cs35l56_sdw_dai_set_tdm_slot ,
. shutdown = cs35l56_sdw_dai_shutdown ,
. hw_params = cs35l56_sdw_dai_hw_params ,
. hw_free = cs35l56_sdw_dai_hw_free ,
. set_stream = cs35l56_sdw_dai_set_stream ,
} ;
static struct snd_soc_dai_driver cs35l56_dai [ ] = {
{
. name = " cs35l56-asp1 " ,
. id = 0 ,
. playback = {
. stream_name = " ASP1 Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = CS35L56_RATES ,
. formats = CS35L56_RX_FORMATS ,
} ,
. capture = {
. stream_name = " ASP1 Capture " ,
. channels_min = 1 ,
. channels_max = 4 ,
. rates = CS35L56_RATES ,
. formats = CS35L56_TX_FORMATS ,
} ,
. ops = & cs35l56_ops ,
. symmetric_rate = 1 ,
. symmetric_sample_bits = 1 ,
} ,
{
. name = " cs35l56-sdw1 " ,
. id = 1 ,
. playback = {
. stream_name = " SDW1 Playback " ,
. channels_min = 1 ,
. channels_max = 2 ,
. rates = CS35L56_RATES ,
. formats = CS35L56_RX_FORMATS ,
} ,
. capture = {
. stream_name = " SDW1 Capture " ,
. channels_min = 1 ,
2023-04-18 15:43:07 +01:00
. channels_max = 4 ,
2023-03-20 11:22:45 +00:00
. rates = CS35L56_RATES ,
. formats = CS35L56_TX_FORMATS ,
} ,
. symmetric_rate = 1 ,
. ops = & cs35l56_sdw_dai_ops ,
}
} ;
2024-02-23 15:39:08 +00:00
static int cs35l56_write_cal ( struct cs35l56_private * cs35l56 )
{
int ret ;
if ( cs35l56 - > base . secured | | ! cs35l56 - > base . cal_data_valid )
return - ENODATA ;
ret = wm_adsp_run ( & cs35l56 - > dsp ) ;
if ( ret )
return ret ;
ret = cs_amp_write_cal_coeffs ( & cs35l56 - > dsp . cs_dsp ,
& cs35l56_calibration_controls ,
& cs35l56 - > base . cal_data ) ;
wm_adsp_stop ( & cs35l56 - > dsp ) ;
if ( ret = = 0 )
dev_info ( cs35l56 - > base . dev , " Calibration applied \n " ) ;
return ret ;
}
2024-01-29 16:27:31 +00:00
static void cs35l56_reinit_patch ( struct cs35l56_private * cs35l56 )
2023-03-20 11:22:45 +00:00
{
2023-05-18 16:02:50 +01:00
int ret ;
2023-03-20 11:22:45 +00:00
2023-05-18 16:02:50 +01:00
/* Use wm_adsp to load and apply the firmware patch and coefficient files */
2023-08-15 13:48:25 +01:00
ret = wm_adsp_power_up ( & cs35l56 - > dsp , true ) ;
2024-02-23 15:39:08 +00:00
if ( ret ) {
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " %s: wm_adsp_power_up ret %d \n " , __func__ , ret ) ;
2024-02-23 15:39:08 +00:00
return ;
}
cs35l56_write_cal ( cs35l56 ) ;
/* Always REINIT after applying patch or coefficients */
cs35l56_mbox_send ( & cs35l56 - > base , CS35L56_MBOX_CMD_AUDIO_REINIT ) ;
2023-05-18 16:02:50 +01:00
}
2023-03-20 11:22:45 +00:00
2024-01-29 16:27:31 +00:00
static void cs35l56_patch ( struct cs35l56_private * cs35l56 , bool firmware_missing )
2023-05-18 16:02:50 +01:00
{
int ret ;
2023-03-20 11:22:45 +00:00
/*
* Disable SoundWire interrupts to prevent race with IRQ work .
* Setting sdw_irq_no_unmask prevents the handler re - enabling
* the SoundWire interrupt .
*/
if ( cs35l56 - > sdw_peripheral ) {
cs35l56 - > sdw_irq_no_unmask = true ;
2023-05-12 15:42:37 +01:00
flush_work ( & cs35l56 - > sdw_irq_work ) ;
2023-03-20 11:22:45 +00:00
sdw_write_no_pm ( cs35l56 - > sdw_peripheral , CS35L56_SDW_GEN_INT_MASK_1 , 0 ) ;
sdw_read_no_pm ( cs35l56 - > sdw_peripheral , CS35L56_SDW_GEN_INT_STAT_1 ) ;
sdw_write_no_pm ( cs35l56 - > sdw_peripheral , CS35L56_SDW_GEN_INT_STAT_1 , 0xFF ) ;
2023-05-12 15:42:37 +01:00
flush_work ( & cs35l56 - > sdw_irq_work ) ;
2023-03-20 11:22:45 +00:00
}
2023-07-21 14:21:18 +01:00
ret = cs35l56_firmware_shutdown ( & cs35l56 - > base ) ;
2023-04-20 11:20:43 +01:00
if ( ret )
2023-03-20 11:22:45 +00:00
goto err ;
2023-08-15 13:48:26 +01:00
/*
* Use wm_adsp to load and apply the firmware patch and coefficient files ,
* but only if firmware is missing . If firmware is already patched just
* power - up wm_adsp without downloading firmware .
*/
ret = wm_adsp_power_up ( & cs35l56 - > dsp , ! ! firmware_missing ) ;
2023-03-20 11:22:45 +00:00
if ( ret ) {
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " %s: wm_adsp_power_up ret %d \n " , __func__ , ret ) ;
2023-03-20 11:22:45 +00:00
goto err ;
}
2023-07-21 14:21:10 +01:00
mutex_lock ( & cs35l56 - > base . irq_lock ) ;
2023-03-20 11:22:45 +00:00
2023-10-06 17:44:05 +01:00
reinit_completion ( & cs35l56 - > init_completion ) ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:11 +01:00
cs35l56 - > soft_resetting = true ;
2023-07-21 14:21:12 +01:00
cs35l56_system_reset ( & cs35l56 - > base , ! ! cs35l56 - > sdw_peripheral ) ;
2023-03-20 11:22:45 +00:00
if ( cs35l56 - > sdw_peripheral ) {
/*
* The system - reset causes the CS35L56 to detach from the bus .
* Wait for the manager to re - enumerate the CS35L56 and
* cs35l56_init ( ) to run again .
*/
if ( ! wait_for_completion_timeout ( & cs35l56 - > init_completion ,
msecs_to_jiffies ( 5000 ) ) ) {
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " %s: init_completion timed out (SDW) \n " ,
__func__ ) ;
2023-03-20 11:22:45 +00:00
goto err_unlock ;
}
} else if ( cs35l56_init ( cs35l56 ) ) {
goto err_unlock ;
}
2023-07-21 14:21:10 +01:00
regmap_clear_bits ( cs35l56 - > base . regmap , CS35L56_PROTECTION_STATUS ,
CS35L56_FIRMWARE_MISSING ) ;
cs35l56 - > base . fw_patched = true ;
2023-03-20 11:22:45 +00:00
2024-02-23 15:39:08 +00:00
if ( cs35l56_write_cal ( cs35l56 ) = = 0 )
cs35l56_mbox_send ( & cs35l56 - > base , CS35L56_MBOX_CMD_AUDIO_REINIT ) ;
2023-03-20 11:22:45 +00:00
err_unlock :
2023-07-21 14:21:10 +01:00
mutex_unlock ( & cs35l56 - > base . irq_lock ) ;
2023-03-20 11:22:45 +00:00
err :
/* Re-enable SoundWire interrupts */
if ( cs35l56 - > sdw_peripheral ) {
cs35l56 - > sdw_irq_no_unmask = false ;
sdw_write_no_pm ( cs35l56 - > sdw_peripheral , CS35L56_SDW_GEN_INT_MASK_1 ,
CS35L56_SDW_INT_MASK_CODEC_IRQ ) ;
}
2023-05-18 16:02:50 +01:00
}
static void cs35l56_dsp_work ( struct work_struct * work )
{
struct cs35l56_private * cs35l56 = container_of ( work ,
struct cs35l56_private ,
dsp_work ) ;
2024-01-29 16:27:31 +00:00
unsigned int firmware_version ;
bool firmware_missing ;
int ret ;
2023-05-18 16:02:50 +01:00
2023-07-21 14:21:10 +01:00
if ( ! cs35l56 - > base . init_done )
2023-05-18 16:02:50 +01:00
return ;
2023-07-21 14:21:10 +01:00
pm_runtime_get_sync ( cs35l56 - > base . dev ) ;
2023-05-18 16:02:50 +01:00
2024-01-29 16:27:31 +00:00
ret = cs35l56_read_prot_status ( & cs35l56 - > base , & firmware_missing , & firmware_version ) ;
if ( ret )
goto err ;
2024-01-29 16:27:30 +00:00
/* Populate fw file qualifier with the revision and security state */
2024-01-29 16:27:31 +00:00
kfree ( cs35l56 - > dsp . fwf_name ) ;
if ( firmware_missing ) {
cs35l56 - > dsp . fwf_name = kasprintf ( GFP_KERNEL , " %02x-dsp1 " , cs35l56 - > base . rev ) ;
} else {
/* Firmware files must match the running firmware version */
cs35l56 - > dsp . fwf_name = kasprintf ( GFP_KERNEL ,
" %02x%s-%06x-dsp1 " ,
2024-01-29 16:27:30 +00:00
cs35l56 - > base . rev ,
2024-01-29 16:27:31 +00:00
cs35l56 - > base . secured ? " -s " : " " ,
firmware_version ) ;
2024-01-29 16:27:30 +00:00
}
2024-01-29 16:27:31 +00:00
if ( ! cs35l56 - > dsp . fwf_name )
goto err ;
2024-01-29 16:27:30 +00:00
dev_dbg ( cs35l56 - > base . dev , " DSP fwf name: '%s' system name: '%s' \n " ,
cs35l56 - > dsp . fwf_name , cs35l56 - > dsp . system_name ) ;
2023-05-18 16:02:50 +01:00
/*
2024-01-29 16:27:31 +00:00
* The firmware cannot be patched if it is already running from
* patch RAM . In this case the firmware files are versioned to
* match the running firmware version and will only contain
* tunings . We do not need to shutdown the firmware to apply
* tunings so can use the lower cost reinit sequence instead .
2023-05-18 16:02:50 +01:00
*/
2024-01-29 16:27:31 +00:00
if ( ! firmware_missing )
cs35l56_reinit_patch ( cs35l56 ) ;
2023-05-18 16:02:50 +01:00
else
2024-01-29 16:27:31 +00:00
cs35l56_patch ( cs35l56 , firmware_missing ) ;
2023-05-18 16:02:49 +01:00
2024-01-29 16:27:30 +00:00
err :
2023-07-21 14:21:10 +01:00
pm_runtime_mark_last_busy ( cs35l56 - > base . dev ) ;
pm_runtime_put_autosuspend ( cs35l56 - > base . dev ) ;
2023-03-20 11:22:45 +00:00
}
static int cs35l56_component_probe ( struct snd_soc_component * component )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
struct dentry * debugfs_root = component - > debugfs_root ;
2023-09-12 17:32:07 +01:00
unsigned short vendor , device ;
2023-03-20 11:22:45 +00:00
BUILD_BUG_ON ( ARRAY_SIZE ( cs35l56_tx_input_texts ) ! = ARRAY_SIZE ( cs35l56_tx_input_values ) ) ;
2023-09-12 17:32:07 +01:00
if ( ! cs35l56 - > dsp . system_name & &
( snd_soc_card_get_pci_ssid ( component - > card , & vendor , & device ) = = 0 ) ) {
2024-01-29 16:27:32 +00:00
/* Append a speaker qualifier if there is a speaker ID */
if ( cs35l56 - > speaker_id > = 0 ) {
cs35l56 - > dsp . system_name = devm_kasprintf ( cs35l56 - > base . dev ,
GFP_KERNEL ,
" %04x%04x-spkid%d " ,
vendor , device ,
cs35l56 - > speaker_id ) ;
} else {
cs35l56 - > dsp . system_name = devm_kasprintf ( cs35l56 - > base . dev ,
GFP_KERNEL ,
" %04x%04x " ,
vendor , device ) ;
}
2023-09-12 17:32:07 +01:00
if ( ! cs35l56 - > dsp . system_name )
return - ENOMEM ;
}
2023-04-14 14:37:51 +01:00
if ( ! wait_for_completion_timeout ( & cs35l56 - > init_completion ,
msecs_to_jiffies ( 5000 ) ) ) {
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " %s: init_completion timed out \n " , __func__ ) ;
2023-04-14 14:37:51 +01:00
return - ENODEV ;
}
2024-03-08 13:58:58 +00:00
cs35l56 - > dsp . part = kasprintf ( GFP_KERNEL , " cs35l%02x " , cs35l56 - > base . type ) ;
if ( ! cs35l56 - > dsp . part )
return - ENOMEM ;
2023-03-20 11:22:45 +00:00
cs35l56 - > component = component ;
wm_adsp2_component_probe ( & cs35l56 - > dsp , component ) ;
2023-07-21 14:21:10 +01:00
debugfs_create_bool ( " init_done " , 0444 , debugfs_root , & cs35l56 - > base . init_done ) ;
debugfs_create_bool ( " can_hibernate " , 0444 , debugfs_root , & cs35l56 - > base . can_hibernate ) ;
debugfs_create_bool ( " fw_patched " , 0444 , debugfs_root , & cs35l56 - > base . fw_patched ) ;
2023-03-20 11:22:45 +00:00
2024-02-08 12:37:42 +00:00
/*
* The widgets for the ASP1TX mixer can ' t be initialized
* until the firmware has been downloaded and rebooted .
*/
regcache_drop_region ( cs35l56 - > base . regmap , CS35L56_ASP1TX1_INPUT , CS35L56_ASP1TX4_INPUT ) ;
cs35l56 - > asp1_mixer_widgets_initialized = false ;
2023-03-20 11:22:45 +00:00
queue_work ( cs35l56 - > dsp_wq , & cs35l56 - > dsp_work ) ;
return 0 ;
}
static void cs35l56_component_remove ( struct snd_soc_component * component )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
cancel_work_sync ( & cs35l56 - > dsp_work ) ;
2024-01-29 16:27:22 +00:00
2024-01-29 16:27:23 +00:00
if ( cs35l56 - > dsp . cs_dsp . booted )
wm_adsp_power_down ( & cs35l56 - > dsp ) ;
wm_adsp2_component_remove ( & cs35l56 - > dsp , component ) ;
2024-03-08 13:58:58 +00:00
kfree ( cs35l56 - > dsp . part ) ;
cs35l56 - > dsp . part = NULL ;
2024-01-29 16:27:30 +00:00
kfree ( cs35l56 - > dsp . fwf_name ) ;
cs35l56 - > dsp . fwf_name = NULL ;
2024-01-29 16:27:22 +00:00
cs35l56 - > component = NULL ;
2023-03-20 11:22:45 +00:00
}
static int cs35l56_set_bias_level ( struct snd_soc_component * component ,
enum snd_soc_bias_level level )
{
struct cs35l56_private * cs35l56 = snd_soc_component_get_drvdata ( component ) ;
switch ( level ) {
case SND_SOC_BIAS_STANDBY :
/*
* Wait for patching to complete when transitioning from
* BIAS_OFF to BIAS_STANDBY
*/
if ( snd_soc_component_get_bias_level ( component ) = = SND_SOC_BIAS_OFF )
2023-04-14 14:37:52 +01:00
cs35l56_wait_dsp_ready ( cs35l56 ) ;
2023-03-20 11:22:45 +00:00
break ;
default :
break ;
}
2023-04-14 14:37:52 +01:00
return 0 ;
2023-03-20 11:22:45 +00:00
}
static const struct snd_soc_component_driver soc_component_dev_cs35l56 = {
. probe = cs35l56_component_probe ,
. remove = cs35l56_component_remove ,
. dapm_widgets = cs35l56_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( cs35l56_dapm_widgets ) ,
. dapm_routes = cs35l56_audio_map ,
. num_dapm_routes = ARRAY_SIZE ( cs35l56_audio_map ) ,
. controls = cs35l56_controls ,
. num_controls = ARRAY_SIZE ( cs35l56_controls ) ,
. set_bias_level = cs35l56_set_bias_level ,
2023-04-11 16:25:28 +01:00
. suspend_bias_off = 1 , /* see cs35l56_system_resume() */
2023-03-20 11:22:45 +00:00
} ;
2023-07-21 14:21:14 +01:00
static int __maybe_unused cs35l56_runtime_suspend_i2c_spi ( struct device * dev )
2023-03-20 11:22:45 +00:00
{
struct cs35l56_private * cs35l56 = dev_get_drvdata ( dev ) ;
2023-07-21 14:21:14 +01:00
return cs35l56_runtime_suspend_common ( & cs35l56 - > base ) ;
2023-03-20 11:22:45 +00:00
}
static int __maybe_unused cs35l56_runtime_resume_i2c_spi ( struct device * dev )
{
struct cs35l56_private * cs35l56 = dev_get_drvdata ( dev ) ;
2023-07-21 14:21:14 +01:00
return cs35l56_runtime_resume_common ( & cs35l56 - > base , false ) ;
2023-03-20 11:22:45 +00:00
}
2023-04-11 16:25:27 +01:00
int cs35l56_system_suspend ( struct device * dev )
{
struct cs35l56_private * cs35l56 = dev_get_drvdata ( dev ) ;
dev_dbg ( dev , " system_suspend \n " ) ;
2024-02-08 12:37:42 +00:00
if ( cs35l56 - > component )
2023-04-11 16:25:27 +01:00
flush_work ( & cs35l56 - > dsp_work ) ;
/*
* The interrupt line is normally shared , but after we start suspending
* we can ' t check if our device is the source of an interrupt , and can ' t
* clear it . Prevent this race by temporarily disabling the parent irq
* until we reach _no_irq .
*/
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > base . irq )
disable_irq ( cs35l56 - > base . irq ) ;
2023-04-11 16:25:27 +01:00
return pm_runtime_force_suspend ( dev ) ;
}
EXPORT_SYMBOL_GPL ( cs35l56_system_suspend ) ;
int cs35l56_system_suspend_late ( struct device * dev )
{
struct cs35l56_private * cs35l56 = dev_get_drvdata ( dev ) ;
dev_dbg ( dev , " system_suspend_late \n " ) ;
/*
* Assert RESET before removing supplies .
* RESET is usually shared by all amps so it must not be asserted until
* all driver instances have done their suspend ( ) stage .
*/
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > base . reset_gpio ) {
gpiod_set_value_cansleep ( cs35l56 - > base . reset_gpio , 0 ) ;
2023-04-14 14:37:50 +01:00
cs35l56_wait_min_reset_pulse ( ) ;
2023-04-11 16:25:27 +01:00
}
regulator_bulk_disable ( ARRAY_SIZE ( cs35l56 - > supplies ) , cs35l56 - > supplies ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( cs35l56_system_suspend_late ) ;
int cs35l56_system_suspend_no_irq ( struct device * dev )
{
struct cs35l56_private * cs35l56 = dev_get_drvdata ( dev ) ;
dev_dbg ( dev , " system_suspend_no_irq \n " ) ;
/* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > base . irq )
enable_irq ( cs35l56 - > base . irq ) ;
2023-04-11 16:25:27 +01:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( cs35l56_system_suspend_no_irq ) ;
int cs35l56_system_resume_no_irq ( struct device * dev )
{
struct cs35l56_private * cs35l56 = dev_get_drvdata ( dev ) ;
dev_dbg ( dev , " system_resume_no_irq \n " ) ;
/*
* WAKE interrupts unmask if the CS35L56 hibernates , which can cause
* spurious interrupts , and the interrupt line is normally shared .
* We can ' t check if our device is the source of an interrupt , and can ' t
* clear it , until it has fully resumed . Prevent this race by temporarily
* disabling the parent irq until we complete resume ( ) .
*/
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > base . irq )
disable_irq ( cs35l56 - > base . irq ) ;
2023-04-11 16:25:27 +01:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( cs35l56_system_resume_no_irq ) ;
int cs35l56_system_resume_early ( struct device * dev )
{
struct cs35l56_private * cs35l56 = dev_get_drvdata ( dev ) ;
int ret ;
dev_dbg ( dev , " system_resume_early \n " ) ;
/* Ensure a spec-compliant RESET pulse. */
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > base . reset_gpio ) {
gpiod_set_value_cansleep ( cs35l56 - > base . reset_gpio , 0 ) ;
2023-04-14 14:37:50 +01:00
cs35l56_wait_min_reset_pulse ( ) ;
2023-04-11 16:25:27 +01:00
}
/* Enable supplies before releasing RESET. */
ret = regulator_bulk_enable ( ARRAY_SIZE ( cs35l56 - > supplies ) , cs35l56 - > supplies ) ;
if ( ret ) {
dev_err ( dev , " system_resume_early failed to enable supplies: %d \n " , ret ) ;
return ret ;
}
/* Release shared RESET before drivers start resume(). */
2023-07-21 14:21:10 +01:00
gpiod_set_value_cansleep ( cs35l56 - > base . reset_gpio , 1 ) ;
2023-04-11 16:25:27 +01:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( cs35l56_system_resume_early ) ;
int cs35l56_system_resume ( struct device * dev )
{
struct cs35l56_private * cs35l56 = dev_get_drvdata ( dev ) ;
int ret ;
dev_dbg ( dev , " system_resume \n " ) ;
2023-08-08 17:47:00 +01:00
/*
* We might have done a hard reset or the CS35L56 was power - cycled
* so wait for control port to be ready .
*/
cs35l56_wait_control_port_ready ( ) ;
2023-04-11 16:25:27 +01:00
/* Undo pm_runtime_force_suspend() before re-enabling the irq */
ret = pm_runtime_force_resume ( dev ) ;
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > base . irq )
enable_irq ( cs35l56 - > base . irq ) ;
2023-04-11 16:25:27 +01:00
2023-04-11 16:25:28 +01:00
if ( ret )
return ret ;
/* Firmware won't have been loaded if the component hasn't probed */
if ( ! cs35l56 - > component )
return 0 ;
2023-07-21 14:21:12 +01:00
ret = cs35l56_is_fw_reload_needed ( & cs35l56 - > base ) ;
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " fw_reload_needed: %d \n " , ret ) ;
2023-04-11 16:25:28 +01:00
if ( ret < 1 )
return ret ;
2023-07-21 14:21:10 +01:00
cs35l56 - > base . fw_patched = false ;
2023-08-08 17:47:02 +01:00
wm_adsp_power_down ( & cs35l56 - > dsp ) ;
2023-04-11 16:25:28 +01:00
queue_work ( cs35l56 - > dsp_wq , & cs35l56 - > dsp_work ) ;
/*
* suspend_bias_off ensures we are now in BIAS_OFF so there will be
* a BIAS_OFF - > BIAS_STANDBY transition to complete dsp patching .
*/
return 0 ;
2023-04-11 16:25:27 +01:00
}
EXPORT_SYMBOL_GPL ( cs35l56_system_resume ) ;
2023-03-20 11:22:45 +00:00
static int cs35l56_dsp_init ( struct cs35l56_private * cs35l56 )
{
struct wm_adsp * dsp ;
int ret ;
cs35l56 - > dsp_wq = create_singlethread_workqueue ( " cs35l56-dsp " ) ;
if ( ! cs35l56 - > dsp_wq )
return - ENOMEM ;
INIT_WORK ( & cs35l56 - > dsp_work , cs35l56_dsp_work ) ;
dsp = & cs35l56 - > dsp ;
2023-07-21 14:21:15 +01:00
cs35l56_init_cs_dsp ( & cs35l56 - > base , & dsp - > cs_dsp ) ;
2024-03-08 13:58:58 +00:00
/*
* dsp - > part is filled in later as it is based on the DEVID . In a
* SoundWire system that cannot be read until enumeration has occurred
* and the device has attached .
*/
2023-03-20 11:22:45 +00:00
dsp - > fw = 12 ;
dsp - > wmfw_optional = true ;
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " DSP system name: '%s' \n " , dsp - > system_name ) ;
2023-03-20 11:22:45 +00:00
ret = wm_halo_init ( dsp ) ;
if ( ret ! = 0 ) {
2023-07-21 14:21:10 +01:00
dev_err ( cs35l56 - > base . dev , " wm_halo_init failed \n " ) ;
2023-03-20 11:22:45 +00:00
return ret ;
}
return 0 ;
}
2023-08-17 12:27:11 +01:00
static int cs35l56_get_firmware_uid ( struct cs35l56_private * cs35l56 )
2023-03-20 11:22:45 +00:00
{
2023-08-17 19:18:58 +01:00
struct device * dev = cs35l56 - > base . dev ;
2023-08-17 12:27:11 +01:00
const char * prop ;
int ret ;
2023-03-20 11:22:45 +00:00
2023-08-17 12:27:11 +01:00
ret = device_property_read_string ( dev , " cirrus,firmware-uid " , & prop ) ;
/* If bad sw node property, return 0 and fallback to legacy firmware path */
if ( ret < 0 )
2023-03-20 11:22:45 +00:00
return 0 ;
2024-01-29 16:27:32 +00:00
/* Append a speaker qualifier if there is a speaker ID */
if ( cs35l56 - > speaker_id > = 0 )
cs35l56 - > dsp . system_name = devm_kasprintf ( dev , GFP_KERNEL , " %s-spkid%d " ,
prop , cs35l56 - > speaker_id ) ;
else
cs35l56 - > dsp . system_name = devm_kstrdup ( dev , prop , GFP_KERNEL ) ;
2023-08-17 12:27:11 +01:00
if ( cs35l56 - > dsp . system_name = = NULL )
return - ENOMEM ;
2023-03-20 11:22:45 +00:00
2023-08-17 12:27:11 +01:00
dev_dbg ( dev , " Firmware UID: %s \n " , cs35l56 - > dsp . system_name ) ;
2023-03-20 11:22:45 +00:00
return 0 ;
}
2024-02-09 11:18:40 +00:00
/*
* Some SoundWire laptops have a spk - id - gpios property but it points to
* the wrong ACPI Device node so can ' t be used to get the GPIO . Try to
* find the SDCA node containing the GpioIo resource and add a GPIO
* mapping to it .
*/
static const struct acpi_gpio_params cs35l56_af01_first_gpio = { 0 , 0 , false } ;
static const struct acpi_gpio_mapping cs35l56_af01_spkid_gpios_mapping [ ] = {
{ " spk-id-gpios " , & cs35l56_af01_first_gpio , 1 } ,
{ }
} ;
static void cs35l56_acpi_dev_release_driver_gpios ( void * adev )
{
acpi_dev_remove_driver_gpios ( adev ) ;
}
static int cs35l56_try_get_broken_sdca_spkid_gpio ( struct cs35l56_private * cs35l56 )
{
struct fwnode_handle * af01_fwnode ;
const union acpi_object * obj ;
struct gpio_desc * desc ;
int ret ;
/* Find the SDCA node containing the GpioIo */
af01_fwnode = device_get_named_child_node ( cs35l56 - > base . dev , " AF01 " ) ;
if ( ! af01_fwnode ) {
dev_dbg ( cs35l56 - > base . dev , " No AF01 node \n " ) ;
return - ENOENT ;
}
ret = acpi_dev_get_property ( ACPI_COMPANION ( cs35l56 - > base . dev ) ,
" spk-id-gpios " , ACPI_TYPE_PACKAGE , & obj ) ;
if ( ret ) {
dev_dbg ( cs35l56 - > base . dev , " Could not get spk-id-gpios package: %d \n " , ret ) ;
return - ENOENT ;
}
/* The broken properties we can handle are a 4-element package (one GPIO) */
if ( obj - > package . count ! = 4 ) {
dev_warn ( cs35l56 - > base . dev , " Unexpected spk-id element count %d \n " ,
obj - > package . count ) ;
return - ENOENT ;
}
/* Add a GPIO mapping if it doesn't already have one */
if ( ! fwnode_property_present ( af01_fwnode , " spk-id-gpios " ) ) {
struct acpi_device * adev = to_acpi_device_node ( af01_fwnode ) ;
/*
* Can ' t use devm_acpi_dev_add_driver_gpios ( ) because the
* mapping isn ' t being added to the node pointed to by
* ACPI_COMPANION ( ) .
*/
ret = acpi_dev_add_driver_gpios ( adev , cs35l56_af01_spkid_gpios_mapping ) ;
if ( ret ) {
return dev_err_probe ( cs35l56 - > base . dev , ret ,
" Failed to add gpio mapping to AF01 \n " ) ;
}
ret = devm_add_action_or_reset ( cs35l56 - > base . dev ,
cs35l56_acpi_dev_release_driver_gpios ,
adev ) ;
if ( ret )
return ret ;
dev_dbg ( cs35l56 - > base . dev , " Added spk-id-gpios mapping to AF01 \n " ) ;
}
desc = fwnode_gpiod_get_index ( af01_fwnode , " spk-id " , 0 , GPIOD_IN , NULL ) ;
if ( IS_ERR ( desc ) ) {
ret = PTR_ERR ( desc ) ;
return dev_err_probe ( cs35l56 - > base . dev , ret , " Get GPIO from AF01 failed \n " ) ;
}
ret = gpiod_get_value_cansleep ( desc ) ;
gpiod_put ( desc ) ;
if ( ret < 0 ) {
dev_err_probe ( cs35l56 - > base . dev , ret , " Error reading spk-id GPIO \n " ) ;
return ret ;
}
dev_info ( cs35l56 - > base . dev , " Got spk-id from AF01 \n " ) ;
return ret ;
}
2023-03-20 11:22:45 +00:00
int cs35l56_common_probe ( struct cs35l56_private * cs35l56 )
{
int ret ;
init_completion ( & cs35l56 - > init_completion ) ;
2023-07-21 14:21:10 +01:00
mutex_init ( & cs35l56 - > base . irq_lock ) ;
2024-02-23 15:39:08 +00:00
cs35l56 - > base . cal_index = - 1 ;
2024-01-29 16:27:32 +00:00
cs35l56 - > speaker_id = - ENOENT ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:10 +01:00
dev_set_drvdata ( cs35l56 - > base . dev , cs35l56 ) ;
2023-03-20 11:22:45 +00:00
cs35l56_fill_supply_names ( cs35l56 - > supplies ) ;
2023-07-21 14:21:10 +01:00
ret = devm_regulator_bulk_get ( cs35l56 - > base . dev , ARRAY_SIZE ( cs35l56 - > supplies ) ,
2023-03-20 11:22:45 +00:00
cs35l56 - > supplies ) ;
if ( ret ! = 0 )
2023-07-21 14:21:10 +01:00
return dev_err_probe ( cs35l56 - > base . dev , ret , " Failed to request supplies \n " ) ;
2023-03-20 11:22:45 +00:00
/* Reset could be controlled by the BIOS or shared by multiple amps */
2023-07-21 14:21:10 +01:00
cs35l56 - > base . reset_gpio = devm_gpiod_get_optional ( cs35l56 - > base . dev , " reset " ,
GPIOD_OUT_LOW ) ;
if ( IS_ERR ( cs35l56 - > base . reset_gpio ) ) {
ret = PTR_ERR ( cs35l56 - > base . reset_gpio ) ;
2023-03-20 11:22:45 +00:00
/*
* If RESET is shared the first amp to probe will grab the reset
* line and reset all the amps
*/
if ( ret ! = - EBUSY )
2023-07-21 14:21:10 +01:00
return dev_err_probe ( cs35l56 - > base . dev , ret , " Failed to get reset GPIO \n " ) ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:10 +01:00
dev_info ( cs35l56 - > base . dev , " Reset GPIO busy, assume shared reset \n " ) ;
cs35l56 - > base . reset_gpio = NULL ;
2023-03-20 11:22:45 +00:00
}
ret = regulator_bulk_enable ( ARRAY_SIZE ( cs35l56 - > supplies ) , cs35l56 - > supplies ) ;
if ( ret ! = 0 )
2023-07-21 14:21:10 +01:00
return dev_err_probe ( cs35l56 - > base . dev , ret , " Failed to enable supplies \n " ) ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > base . reset_gpio ) {
2023-08-08 17:46:59 +01:00
/* ACPI can override GPIOD_OUT_LOW flag so force it to start low */
gpiod_set_value_cansleep ( cs35l56 - > base . reset_gpio , 0 ) ;
2023-04-14 14:37:50 +01:00
cs35l56_wait_min_reset_pulse ( ) ;
2023-07-21 14:21:10 +01:00
gpiod_set_value_cansleep ( cs35l56 - > base . reset_gpio , 1 ) ;
2023-03-20 11:22:45 +00:00
}
2024-01-29 16:27:32 +00:00
ret = cs35l56_get_speaker_id ( & cs35l56 - > base ) ;
2024-02-09 11:18:40 +00:00
if ( ACPI_COMPANION ( cs35l56 - > base . dev ) & & cs35l56 - > sdw_peripheral & & ( ret = = - ENOENT ) )
ret = cs35l56_try_get_broken_sdca_spkid_gpio ( cs35l56 ) ;
2024-01-29 16:27:32 +00:00
if ( ( ret < 0 ) & & ( ret ! = - ENOENT ) )
goto err ;
cs35l56 - > speaker_id = ret ;
2023-08-17 12:27:11 +01:00
ret = cs35l56_get_firmware_uid ( cs35l56 ) ;
2023-03-20 11:22:45 +00:00
if ( ret ! = 0 )
goto err ;
ret = cs35l56_dsp_init ( cs35l56 ) ;
if ( ret < 0 ) {
2023-07-21 14:21:10 +01:00
dev_err_probe ( cs35l56 - > base . dev , ret , " DSP init failed \n " ) ;
2023-03-20 11:22:45 +00:00
goto err ;
}
2023-07-21 14:21:10 +01:00
ret = devm_snd_soc_register_component ( cs35l56 - > base . dev ,
2023-03-20 11:22:45 +00:00
& soc_component_dev_cs35l56 ,
cs35l56_dai , ARRAY_SIZE ( cs35l56_dai ) ) ;
if ( ret < 0 ) {
2023-07-21 14:21:10 +01:00
dev_err_probe ( cs35l56 - > base . dev , ret , " Register codec failed \n " ) ;
2023-03-20 11:22:45 +00:00
goto err ;
}
return 0 ;
err :
2023-07-21 14:21:10 +01:00
gpiod_set_value_cansleep ( cs35l56 - > base . reset_gpio , 0 ) ;
2023-03-20 11:22:45 +00:00
regulator_bulk_disable ( ARRAY_SIZE ( cs35l56 - > supplies ) , cs35l56 - > supplies ) ;
return ret ;
}
EXPORT_SYMBOL_NS_GPL ( cs35l56_common_probe , SND_SOC_CS35L56_CORE ) ;
int cs35l56_init ( struct cs35l56_private * cs35l56 )
{
int ret ;
/*
* Check whether the actions associated with soft reset or one time
* init need to be performed .
*/
if ( cs35l56 - > soft_resetting )
goto post_soft_reset ;
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > base . init_done )
2023-03-20 11:22:45 +00:00
return 0 ;
2023-07-21 14:21:10 +01:00
pm_runtime_set_autosuspend_delay ( cs35l56 - > base . dev , 100 ) ;
pm_runtime_use_autosuspend ( cs35l56 - > base . dev ) ;
pm_runtime_set_active ( cs35l56 - > base . dev ) ;
pm_runtime_enable ( cs35l56 - > base . dev ) ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:16 +01:00
ret = cs35l56_hw_init ( & cs35l56 - > base ) ;
if ( ret < 0 )
2023-03-20 11:22:45 +00:00
return ret ;
2024-01-29 16:27:24 +00:00
ret = cs35l56_set_patch ( & cs35l56 - > base ) ;
if ( ret )
return ret ;
2024-02-23 15:39:08 +00:00
ret = cs35l56_get_calibration ( & cs35l56 - > base ) ;
if ( ret )
return ret ;
2023-07-21 14:21:10 +01:00
if ( ! cs35l56 - > base . reset_gpio ) {
dev_dbg ( cs35l56 - > base . dev , " No reset gpio: using soft reset \n " ) ;
2023-07-21 14:21:11 +01:00
cs35l56 - > soft_resetting = true ;
2023-07-21 14:21:12 +01:00
cs35l56_system_reset ( & cs35l56 - > base , ! ! cs35l56 - > sdw_peripheral ) ;
2023-03-20 11:22:45 +00:00
if ( cs35l56 - > sdw_peripheral ) {
/* Keep alive while we wait for re-enumeration */
2023-07-21 14:21:10 +01:00
pm_runtime_get_noresume ( cs35l56 - > base . dev ) ;
2023-03-20 11:22:45 +00:00
return 0 ;
}
}
post_soft_reset :
if ( cs35l56 - > soft_resetting ) {
cs35l56 - > soft_resetting = false ;
/* Done re-enumerating after one-time init so release the keep-alive */
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > sdw_peripheral & & ! cs35l56 - > base . init_done )
pm_runtime_put_noidle ( cs35l56 - > base . dev ) ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:10 +01:00
regcache_mark_dirty ( cs35l56 - > base . regmap ) ;
ret = cs35l56_wait_for_firmware_boot ( & cs35l56 - > base ) ;
2023-03-20 11:22:45 +00:00
if ( ret )
return ret ;
2023-07-21 14:21:10 +01:00
dev_dbg ( cs35l56 - > base . dev , " Firmware rebooted after soft reset \n " ) ;
2023-03-20 11:22:45 +00:00
}
/* Disable auto-hibernate so that runtime_pm has control */
2023-07-21 14:21:10 +01:00
ret = cs35l56_mbox_send ( & cs35l56 - > base , CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE ) ;
2023-03-20 11:22:45 +00:00
if ( ret )
2023-07-18 15:46:25 +01:00
return ret ;
2023-03-20 11:22:45 +00:00
/* Registers could be dirty after soft reset or SoundWire enumeration */
2023-07-21 14:21:10 +01:00
regcache_sync ( cs35l56 - > base . regmap ) ;
2023-03-20 11:22:45 +00:00
2023-10-09 16:34:12 +01:00
/* Set ASP1 DOUT to high-impedance when it is not transmitting audio data. */
ret = regmap_set_bits ( cs35l56 - > base . regmap , CS35L56_ASP1_CONTROL3 ,
CS35L56_ASP1_DOUT_HIZ_CTRL_MASK ) ;
if ( ret )
return dev_err_probe ( cs35l56 - > base . dev , ret , " Failed to write ASP1_CONTROL3 \n " ) ;
2023-07-21 14:21:10 +01:00
cs35l56 - > base . init_done = true ;
2023-03-20 11:22:45 +00:00
complete ( & cs35l56 - > init_completion ) ;
return 0 ;
}
EXPORT_SYMBOL_NS_GPL ( cs35l56_init , SND_SOC_CS35L56_CORE ) ;
2023-04-14 14:37:53 +01:00
void cs35l56_remove ( struct cs35l56_private * cs35l56 )
2023-03-20 11:22:45 +00:00
{
2023-07-21 14:21:10 +01:00
cs35l56 - > base . init_done = false ;
2023-03-20 11:22:45 +00:00
/*
* WAKE IRQs unmask if CS35L56 hibernates so free the handler to
* prevent it racing with remove ( ) .
*/
2023-07-21 14:21:10 +01:00
if ( cs35l56 - > base . irq )
devm_free_irq ( cs35l56 - > base . dev , cs35l56 - > base . irq , & cs35l56 - > base ) ;
2023-03-20 11:22:45 +00:00
flush_workqueue ( cs35l56 - > dsp_wq ) ;
destroy_workqueue ( cs35l56 - > dsp_wq ) ;
2023-09-08 11:17:16 +01:00
pm_runtime_dont_use_autosuspend ( cs35l56 - > base . dev ) ;
2023-07-21 14:21:10 +01:00
pm_runtime_suspend ( cs35l56 - > base . dev ) ;
pm_runtime_disable ( cs35l56 - > base . dev ) ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:10 +01:00
regcache_cache_only ( cs35l56 - > base . regmap , true ) ;
2023-03-20 11:22:45 +00:00
2023-07-21 14:21:10 +01:00
gpiod_set_value_cansleep ( cs35l56 - > base . reset_gpio , 0 ) ;
2023-03-20 11:22:45 +00:00
regulator_bulk_disable ( ARRAY_SIZE ( cs35l56 - > supplies ) , cs35l56 - > supplies ) ;
}
EXPORT_SYMBOL_NS_GPL ( cs35l56_remove , SND_SOC_CS35L56_CORE ) ;
2023-09-14 16:09:18 +01:00
# if IS_ENABLED(CONFIG_SND_SOC_CS35L56_I2C) || IS_ENABLED(CONFIG_SND_SOC_CS35L56_SPI)
2023-09-14 16:09:17 +01:00
EXPORT_NS_GPL_DEV_PM_OPS ( cs35l56_pm_ops_i2c_spi , SND_SOC_CS35L56_CORE ) = {
2023-07-21 14:21:14 +01:00
SET_RUNTIME_PM_OPS ( cs35l56_runtime_suspend_i2c_spi , cs35l56_runtime_resume_i2c_spi , NULL )
2023-04-11 16:25:27 +01:00
SYSTEM_SLEEP_PM_OPS ( cs35l56_system_suspend , cs35l56_system_resume )
LATE_SYSTEM_SLEEP_PM_OPS ( cs35l56_system_suspend_late , cs35l56_system_resume_early )
NOIRQ_SYSTEM_SLEEP_PM_OPS ( cs35l56_system_suspend_no_irq , cs35l56_system_resume_no_irq )
2023-03-20 11:22:45 +00:00
} ;
2023-09-14 16:09:18 +01:00
# endif
2023-03-20 11:22:45 +00:00
MODULE_DESCRIPTION ( " ASoC CS35L56 driver " ) ;
MODULE_IMPORT_NS ( SND_SOC_CS35L56_SHARED ) ;
2024-02-23 15:39:08 +00:00
MODULE_IMPORT_NS ( SND_SOC_CS_AMP_LIB ) ;
2023-03-20 11:22:45 +00:00
MODULE_AUTHOR ( " Richard Fitzgerald <rf@opensource.cirrus.com> " ) ;
MODULE_AUTHOR ( " Simon Trimmer <simont@opensource.cirrus.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;