2019-04-19 13:21:54 +03:00
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright 2010 Maurus Cuelenaere <mcuelenaere@gmail.com>
//
// Based on smdk6410_wm8987.c
// Copyright 2007 Wolfson Microelectronics PLC. - linux@wolfsonmicro.com
// Graeme Gregory - graeme.gregory@wolfsonmicro.com
2010-07-03 04:46:12 +04:00
2014-07-09 13:09:42 +04:00
# include <linux/gpio/consumer.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2010-07-03 04:46:12 +04:00
2010-11-21 20:48:44 +03:00
# include <sound/soc.h>
2010-07-03 04:46:12 +04:00
# include <sound/jack.h>
2010-11-22 09:37:02 +03:00
# include "i2s.h"
2010-07-03 04:46:12 +04:00
# include "../codecs/wm8750.h"
/*
* WM8987 is register compatible with WM8750 , so using that as base driver .
*/
static struct snd_soc_card snd_soc_smartq ;
static int smartq_hifi_hw_params ( struct snd_pcm_substream * substream ,
struct snd_pcm_hw_params * params )
{
struct snd_soc_pcm_runtime * rtd = substream - > private_data ;
2010-11-25 08:43:49 +03:00
struct snd_soc_dai * codec_dai = rtd - > codec_dai ;
struct snd_soc_dai * cpu_dai = rtd - > cpu_dai ;
2010-07-03 04:46:12 +04:00
unsigned int clk = 0 ;
int ret ;
switch ( params_rate ( params ) ) {
case 8000 :
case 16000 :
case 32000 :
case 48000 :
case 96000 :
clk = 12288000 ;
break ;
case 11025 :
case 22050 :
case 44100 :
case 88200 :
clk = 11289600 ;
break ;
}
2010-11-22 09:37:02 +03:00
/* Use PCLK for I2S signal generation */
ret = snd_soc_dai_set_sysclk ( cpu_dai , SAMSUNG_I2S_RCLKSRC_0 ,
0 , SND_SOC_CLOCK_IN ) ;
2010-07-03 04:46:12 +04:00
if ( ret < 0 )
return ret ;
2010-11-22 09:37:02 +03:00
/* Gate the RCLK output on PAD */
ret = snd_soc_dai_set_sysclk ( cpu_dai , SAMSUNG_I2S_CDCLK ,
0 , SND_SOC_CLOCK_IN ) ;
2010-07-03 04:46:12 +04:00
if ( ret < 0 )
return ret ;
2010-11-22 09:37:02 +03:00
/* set the codec system clock for DAC and ADC */
ret = snd_soc_dai_set_sysclk ( codec_dai , WM8750_SYSCLK , clk ,
SND_SOC_CLOCK_IN ) ;
2010-07-03 04:46:12 +04:00
if ( ret < 0 )
return ret ;
return 0 ;
}
/*
* SmartQ WM8987 HiFi DAI operations .
*/
static struct snd_soc_ops smartq_hifi_ops = {
. hw_params = smartq_hifi_hw_params ,
} ;
static struct snd_soc_jack smartq_jack ;
static struct snd_soc_jack_pin smartq_jack_pins [ ] = {
/* Disable speaker when headphone is plugged in */
{
. pin = " Internal Speaker " ,
. mask = SND_JACK_HEADPHONE ,
} ,
} ;
static struct snd_soc_jack_gpio smartq_jack_gpios [ ] = {
{
2014-07-09 13:09:42 +04:00
. gpio = - 1 ,
2010-07-03 04:46:12 +04:00
. name = " headphone detect " ,
. report = SND_JACK_HEADPHONE ,
. debounce_time = 200 ,
} ,
} ;
static const struct snd_kcontrol_new wm8987_smartq_controls [ ] = {
SOC_DAPM_PIN_SWITCH ( " Internal Speaker " ) ,
SOC_DAPM_PIN_SWITCH ( " Headphone Jack " ) ,
SOC_DAPM_PIN_SWITCH ( " Internal Mic " ) ,
} ;
static int smartq_speaker_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k ,
int event )
{
2014-07-09 13:09:42 +04:00
struct gpio_desc * gpio = snd_soc_card_get_drvdata ( & snd_soc_smartq ) ;
gpiod_set_value ( gpio , SND_SOC_DAPM_EVENT_OFF ( event ) ) ;
2010-07-03 04:46:12 +04:00
return 0 ;
}
static const struct snd_soc_dapm_widget wm8987_dapm_widgets [ ] = {
SND_SOC_DAPM_SPK ( " Internal Speaker " , smartq_speaker_event ) ,
SND_SOC_DAPM_HP ( " Headphone Jack " , NULL ) ,
SND_SOC_DAPM_MIC ( " Internal Mic " , NULL ) ,
} ;
static const struct snd_soc_dapm_route audio_map [ ] = {
{ " Headphone Jack " , NULL , " LOUT2 " } ,
{ " Headphone Jack " , NULL , " ROUT2 " } ,
{ " Internal Speaker " , NULL , " LOUT2 " } ,
{ " Internal Speaker " , NULL , " ROUT2 " } ,
{ " Mic Bias " , NULL , " Internal Mic " } ,
{ " LINPUT2 " , NULL , " Mic Bias " } ,
} ;
2010-11-25 08:43:49 +03:00
static int smartq_wm8987_init ( struct snd_soc_pcm_runtime * rtd )
2010-07-03 04:46:12 +04:00
{
2015-05-03 20:27:09 +03:00
struct snd_soc_dapm_context * dapm = & rtd - > card - > dapm ;
2010-07-03 04:46:12 +04:00
int err = 0 ;
/* set endpoints to not connected */
2010-11-05 16:53:46 +03:00
snd_soc_dapm_nc_pin ( dapm , " LINPUT1 " ) ;
snd_soc_dapm_nc_pin ( dapm , " RINPUT1 " ) ;
snd_soc_dapm_nc_pin ( dapm , " OUT3 " ) ;
snd_soc_dapm_nc_pin ( dapm , " ROUT1 " ) ;
2010-07-03 04:46:12 +04:00
/* Headphone jack detection */
2015-03-04 12:33:37 +03:00
err = snd_soc_card_jack_new ( rtd - > card , " Headphone Jack " ,
SND_JACK_HEADPHONE , & smartq_jack ,
smartq_jack_pins ,
ARRAY_SIZE ( smartq_jack_pins ) ) ;
2010-07-03 04:46:12 +04:00
if ( err )
return err ;
err = snd_soc_jack_add_gpios ( & smartq_jack ,
ARRAY_SIZE ( smartq_jack_gpios ) ,
smartq_jack_gpios ) ;
return err ;
}
static struct snd_soc_dai_link smartq_dai [ ] = {
{
. name = " wm8987 " ,
. stream_name = " SmartQ Hi-Fi " ,
2010-11-22 09:37:02 +03:00
. cpu_dai_name = " samsung-i2s.0 " ,
2010-03-17 23:15:21 +03:00
. codec_dai_name = " wm8750-hifi " ,
2012-12-07 12:29:21 +04:00
. platform_name = " samsung-i2s.0 " ,
2011-08-03 13:22:28 +04:00
. codec_name = " wm8750.0-0x1a " ,
2010-07-03 04:46:12 +04:00
. init = smartq_wm8987_init ,
2015-01-01 19:16:26 +03:00
. dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS ,
2010-07-03 04:46:12 +04:00
. ops = & smartq_hifi_ops ,
} ,
} ;
static struct snd_soc_card snd_soc_smartq = {
. name = " SmartQ " ,
2011-12-22 06:53:15 +04:00
. owner = THIS_MODULE ,
2010-07-03 04:46:12 +04:00
. dai_link = smartq_dai ,
. num_links = ARRAY_SIZE ( smartq_dai ) ,
2011-10-08 16:30:55 +04:00
. dapm_widgets = wm8987_dapm_widgets ,
. num_dapm_widgets = ARRAY_SIZE ( wm8987_dapm_widgets ) ,
. dapm_routes = audio_map ,
. num_dapm_routes = ARRAY_SIZE ( audio_map ) ,
. controls = wm8987_smartq_controls ,
. num_controls = ARRAY_SIZE ( wm8987_smartq_controls ) ,
2010-07-03 04:46:12 +04:00
} ;
2014-07-09 13:09:42 +04:00
static int smartq_probe ( struct platform_device * pdev )
2010-07-03 04:46:12 +04:00
{
2014-07-09 13:09:42 +04:00
struct gpio_desc * gpio ;
2010-07-03 04:46:12 +04:00
int ret ;
2014-07-09 13:09:42 +04:00
platform_set_drvdata ( pdev , & snd_soc_smartq ) ;
2010-07-03 04:46:12 +04:00
/* Initialise GPIOs used by amplifiers */
2014-07-09 13:09:42 +04:00
gpio = devm_gpiod_get ( & pdev - > dev , " amplifiers shutdown " ,
GPIOD_OUT_HIGH ) ;
if ( IS_ERR ( gpio ) ) {
dev_err ( & pdev - > dev , " Failed to register GPK12 \n " ) ;
ret = PTR_ERR ( gpio ) ;
goto out ;
2010-07-03 04:46:12 +04:00
}
2014-07-09 13:09:42 +04:00
snd_soc_card_set_drvdata ( & snd_soc_smartq , gpio ) ;
2010-07-03 04:46:12 +04:00
2014-07-09 13:09:42 +04:00
ret = devm_snd_soc_register_card ( & pdev - > dev , & snd_soc_smartq ) ;
if ( ret )
dev_err ( & pdev - > dev , " Failed to register card \n " ) ;
2010-07-03 04:46:12 +04:00
2014-07-09 13:09:42 +04:00
out :
2010-07-03 04:46:12 +04:00
return ret ;
}
2014-07-09 13:09:42 +04:00
static struct platform_driver smartq_driver = {
. driver = {
. name = " smartq-audio " ,
} ,
. probe = smartq_probe ,
} ;
2010-07-03 04:46:12 +04:00
2014-07-09 13:09:42 +04:00
module_platform_driver ( smartq_driver ) ;
2010-07-03 04:46:12 +04:00
/* Module information */
MODULE_AUTHOR ( " Maurus Cuelenaere <mcuelenaere@gmail.com> " ) ;
MODULE_DESCRIPTION ( " ALSA SoC SmartQ WM8987 " ) ;
MODULE_LICENSE ( " GPL " ) ;