2019-06-01 06:22:42 +03:00
// SPDX-License-Identifier: GPL-2.0
2018-02-19 16:45:58 +03:00
/*
* MAX9759 Amplifier Driver
*
* Copyright ( c ) 2017 BayLibre , SAS .
* Author : Neil Armstrong < narmstrong @ baylibre . com >
*/
# include <linux/gpio/consumer.h>
# include <linux/module.h>
# include <sound/soc.h>
# include <sound/soc-dapm.h>
# include <sound/tlv.h>
# define DRV_NAME "max9759"
struct max9759 {
struct gpio_desc * gpiod_shutdown ;
struct gpio_desc * gpiod_mute ;
struct gpio_descs * gpiod_gain ;
bool is_mute ;
unsigned int gain ;
} ;
static int pga_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * control , int event )
{
struct snd_soc_component * c = snd_soc_dapm_to_component ( w - > dapm ) ;
struct max9759 * priv = snd_soc_component_get_drvdata ( c ) ;
if ( SND_SOC_DAPM_EVENT_ON ( event ) )
gpiod_set_value_cansleep ( priv - > gpiod_shutdown , 0 ) ;
else
gpiod_set_value_cansleep ( priv - > gpiod_shutdown , 1 ) ;
return 0 ;
}
/* From 6dB to 24dB in steps of 6dB */
static const DECLARE_TLV_DB_SCALE ( speaker_gain_tlv , 600 , 600 , 0 ) ;
static int speaker_gain_control_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * c = snd_soc_kcontrol_component ( kcontrol ) ;
struct max9759 * priv = snd_soc_component_get_drvdata ( c ) ;
ucontrol - > value . integer . value [ 0 ] = priv - > gain ;
return 0 ;
}
static const bool speaker_gain_table [ 4 ] [ 2 ] = {
/* G1, G2 */
{ true , true } , /* +6dB */
{ false , true } , /* +12dB */
{ true , false } , /* +18dB */
{ false , false } , /* +24dB */
} ;
static int speaker_gain_control_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * c = snd_soc_kcontrol_component ( kcontrol ) ;
struct max9759 * priv = snd_soc_component_get_drvdata ( c ) ;
2022-01-19 15:31:01 +03:00
if ( ucontrol - > value . integer . value [ 0 ] < 0 | |
ucontrol - > value . integer . value [ 0 ] > 3 )
2018-02-19 16:45:58 +03:00
return - EINVAL ;
priv - > gain = ucontrol - > value . integer . value [ 0 ] ;
/* G1 */
gpiod_set_value_cansleep ( priv - > gpiod_gain - > desc [ 0 ] ,
speaker_gain_table [ priv - > gain ] [ 0 ] ) ;
/* G2 */
gpiod_set_value_cansleep ( priv - > gpiod_gain - > desc [ 1 ] ,
speaker_gain_table [ priv - > gain ] [ 1 ] ) ;
return 1 ;
}
static int speaker_mute_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * c = snd_soc_kcontrol_component ( kcontrol ) ;
struct max9759 * priv = snd_soc_component_get_drvdata ( c ) ;
ucontrol - > value . integer . value [ 0 ] = ! priv - > is_mute ;
return 0 ;
}
static int speaker_mute_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct snd_soc_component * c = snd_soc_kcontrol_component ( kcontrol ) ;
struct max9759 * priv = snd_soc_component_get_drvdata ( c ) ;
priv - > is_mute = ! ucontrol - > value . integer . value [ 0 ] ;
gpiod_set_value_cansleep ( priv - > gpiod_mute , priv - > is_mute ) ;
return 1 ;
}
static const struct snd_kcontrol_new max9759_dapm_controls [ ] = {
SOC_SINGLE_EXT_TLV ( " Speaker Gain Volume " , 0 , 0 , 3 , 0 ,
speaker_gain_control_get , speaker_gain_control_put ,
speaker_gain_tlv ) ,
SOC_SINGLE_BOOL_EXT ( " Playback Switch " , 0 ,
speaker_mute_get , speaker_mute_put ) ,
} ;
static const struct snd_soc_dapm_widget max9759_dapm_widgets [ ] = {
SND_SOC_DAPM_INPUT ( " INL " ) ,
SND_SOC_DAPM_INPUT ( " INR " ) ,
SND_SOC_DAPM_PGA_E ( " PGA " , SND_SOC_NOPM , 0 , 0 , NULL , 0 , pga_event ,
( SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD ) ) ,
SND_SOC_DAPM_OUTPUT ( " OUTL " ) ,
SND_SOC_DAPM_OUTPUT ( " OUTR " ) ,
} ;
static const struct snd_soc_dapm_route max9759_dapm_routes [ ] = {
{ " PGA " , NULL , " INL " } ,
{ " PGA " , NULL , " INR " } ,
{ " OUTL " , NULL , " PGA " } ,
{ " OUTR " , NULL , " PGA " } ,
} ;
static const struct snd_soc_component_driver max9759_component_driver = {
. controls = max9759_dapm_controls ,
. num_controls = ARRAY_SIZE ( max9759_dapm_controls ) ,
. dapm_widgets = max9759_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( max9759_dapm_widgets ) ,
. dapm_routes = max9759_dapm_routes ,
. num_dapm_routes = ARRAY_SIZE ( max9759_dapm_routes ) ,
} ;
static int max9759_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct max9759 * priv ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
platform_set_drvdata ( pdev , priv ) ;
priv - > gpiod_shutdown = devm_gpiod_get ( dev , " shutdown " , GPIOD_OUT_HIGH ) ;
2021-12-14 05:08:24 +03:00
if ( IS_ERR ( priv - > gpiod_shutdown ) )
return dev_err_probe ( dev , PTR_ERR ( priv - > gpiod_shutdown ) ,
" Failed to get 'shutdown' gpio " ) ;
2018-02-19 16:45:58 +03:00
priv - > gpiod_mute = devm_gpiod_get ( dev , " mute " , GPIOD_OUT_HIGH ) ;
2021-12-14 05:08:24 +03:00
if ( IS_ERR ( priv - > gpiod_mute ) )
return dev_err_probe ( dev , PTR_ERR ( priv - > gpiod_mute ) ,
" Failed to get 'mute' gpio " ) ;
2018-02-19 16:45:58 +03:00
priv - > is_mute = true ;
priv - > gpiod_gain = devm_gpiod_get_array ( dev , " gain " , GPIOD_OUT_HIGH ) ;
2021-12-14 05:08:24 +03:00
if ( IS_ERR ( priv - > gpiod_gain ) )
return dev_err_probe ( dev , PTR_ERR ( priv - > gpiod_gain ) ,
" Failed to get 'gain' gpios " ) ;
2018-02-19 16:45:58 +03:00
priv - > gain = 0 ;
if ( priv - > gpiod_gain - > ndescs ! = 2 ) {
dev_err ( dev , " Invalid 'gain' gpios count: %d " ,
priv - > gpiod_gain - > ndescs ) ;
return - EINVAL ;
}
return devm_snd_soc_register_component ( dev , & max9759_component_driver ,
NULL , 0 ) ;
}
# ifdef CONFIG_OF
static const struct of_device_id max9759_ids [ ] = {
{ . compatible = " maxim,max9759 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , max9759_ids ) ;
# endif
static struct platform_driver max9759_driver = {
. driver = {
. name = DRV_NAME ,
. of_match_table = of_match_ptr ( max9759_ids ) ,
} ,
. probe = max9759_probe ,
} ;
module_platform_driver ( max9759_driver ) ;
MODULE_DESCRIPTION ( " ASoC MAX9759 amplifier driver " ) ;
MODULE_AUTHOR ( " Neil Armstrong <narmstrong@baylibre.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;