diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-05 22:13:14 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-05 22:13:14 +0300 |
commit | 5691f0e9a3e7855832d5fd094801bf600347c2d0 (patch) | |
tree | 4cd4538de2bde556be944e984a3882a64b25dfcb /sound/soc/intel/boards | |
parent | 878fb5dc96b9dfae1de45be1b85aba40aca3356e (diff) | |
parent | eeea8b40cd2866ca24f25e5ef09225edb076ae45 (diff) | |
download | linux-5691f0e9a3e7855832d5fd094801bf600347c2d0.tar.xz |
Merge tag 'sound-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"Again the diffstat shows a widely distributed pattern at this cycle,
as there've been many code cleanups and refactoring allover the
places. Other than that, the development was relatively calm, and no
big surprise shouldn't be expected. Here are some highlights:
Core:
- Sequencer code refactoring / documentation updates
- TLV code moved to uapi, following some relevant cleanups
USB-Audio:
- Lots of LINE6 driver fixes / updates
- DragonFly and TEAC device quirk updates
HD-audio:
- Usual fixupes for Dell, Lenovo and HP machines
- Link-audio time reporting capability
ASoC:
- Large refactoring of simple-card code to be shared with rcar driver
- Removal of some duplicated ops over lots of CODEC drivers
- Again quite a few Intel SKL driver updates
- New drivers for Nuvoton NAU88C10, Realtek RT5660 and RT5663"
* tag 'sound-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (465 commits)
ASoC: fsl: Fix lockups with recent cache changes
ASoC: Intel: Skylake: fix memory leak of module on error exit path
ASoC: rsnd: add SNDRV_PCM_TRIGGER_SUSPEND/RESUME
ASoC: wm8960: remove usage of obsoleted TLV-related macro
ASoC: rt5616: remove usage of obsoleted TLV-related macro
ASoC: max9867: remove usage of obsoleted TLV-related macro
ASoC: trivial: system spelling fix
ASoC: da7219: fix inappropriate condition statement
ASoC: tlv320aic31xx: do not declare support for mono DAI
ASoC: stac9766: fix wrong usage of DECLARE_TLV_DB_LINEAR()
ASoC: wm8991: remove unused variable
ASoC: wm8991: fix wrong usage of DECLARE_TLV_DB_LINEAR()
ASOC: tpa6130a2: add static qualifier for file local symbols
ASoC: sst-bxt-rt298: fix obsoleted initializers for array
ASoC: sst-bxt-da7219_max98357a: fix obsoleted initializers for array
ASoC: rt5616: add static qualifier for file local symbols
ASoC: arizona: Add output power up/down delays for speaker path
ASoC: arizona: Add debug prints for output power up/down times
ALSA: hda - Add the top speaker pin config for HP Spectre x360
ASoC: Intel: Add DMIC channel constraint for bxt machine
...
Diffstat (limited to 'sound/soc/intel/boards')
-rw-r--r-- | sound/soc/intel/boards/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/intel/boards/bdw-rt5677.c | 347 | ||||
-rw-r--r-- | sound/soc/intel/boards/bxt_da7219_max98357a.c | 75 | ||||
-rw-r--r-- | sound/soc/intel/boards/bxt_rt298.c | 14 | ||||
-rw-r--r-- | sound/soc/intel/boards/bytcr_rt5640.c | 507 |
5 files changed, 883 insertions, 62 deletions
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index dac03a06bfd8..5639f10774e6 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -1,6 +1,7 @@ snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o +snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o snd-soc-sst-bxt-rt298-objs := bxt_rt298.o @@ -19,6 +20,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o +obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c new file mode 100644 index 000000000000..547e6705bf6d --- /dev/null +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -0,0 +1,347 @@ +/* + * ASoC machine driver for Intel Broadwell platforms with RT5677 codec + * + * Copyright (c) 2014, The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio/consumer.h> +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> +#include <sound/jack.h> + +#include "../common/sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" + +#include "../../codecs/rt5677.h" + +struct bdw_rt5677_priv { + struct gpio_desc *gpio_hp_en; + struct snd_soc_codec *codec; +}; + +static int bdw_rt5677_event_hp(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card); + + if (SND_SOC_DAPM_EVENT_ON(event)) + msleep(70); + + gpiod_set_value_cansleep(bdw_rt5677->gpio_hp_en, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget bdw_rt5677_widgets[] = { + SND_SOC_DAPM_HP("Headphone", bdw_rt5677_event_hp), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Local DMICs", NULL), + SND_SOC_DAPM_MIC("Remote DMICs", NULL), +}; + +static const struct snd_soc_dapm_route bdw_rt5677_map[] = { + /* Speakers */ + {"Speaker", NULL, "PDM1L"}, + {"Speaker", NULL, "PDM1R"}, + + /* Headset jack connectors */ + {"Headphone", NULL, "LOUT1"}, + {"Headphone", NULL, "LOUT2"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN1N", NULL, "Headset Mic"}, + + /* Digital MICs + * Local DMICs: the two DMICs on the mainboard + * Remote DMICs: the two DMICs on the camera module + */ + {"DMIC L1", NULL, "Remote DMICs"}, + {"DMIC R1", NULL, "Remote DMICs"}, + {"DMIC L2", NULL, "Local DMICs"}, + {"DMIC R2", NULL, "Local DMICs"}, + + /* CODEC BE connections */ + {"SSP0 CODEC IN", NULL, "AIF1 Capture"}, + {"AIF1 Playback", NULL, "SSP0 CODEC OUT"}, +}; + +static const struct snd_kcontrol_new bdw_rt5677_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Local DMICs"), + SOC_DAPM_PIN_SWITCH("Remote DMICs"), +}; + + +static struct snd_soc_jack headphone_jack; +static struct snd_soc_jack mic_jack; + +static struct snd_soc_jack_pin headphone_jack_pin = { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, +}; + +static struct snd_soc_jack_pin mic_jack_pin = { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, +}; + +static struct snd_soc_jack_gpio headphone_jack_gpio = { + .name = "plug-det", + .report = SND_JACK_HEADPHONE, + .debounce_time = 200, +}; + +static struct snd_soc_jack_gpio mic_jack_gpio = { + .name = "mic-present", + .report = SND_JACK_MICROPHONE, + .debounce_time = 200, + .invert = 1, +}; + +static int broadwell_ssp0_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); + + /* The ADSP will covert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP0 to 16 bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S16_LE); + return 0; +} + +static int bdw_rt5677_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, 24576000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk configuration\n"); + return ret; + } + + return ret; +} + +static struct snd_soc_ops bdw_rt5677_ops = { + .hw_params = bdw_rt5677_hw_params, +}; + +static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev); + struct sst_hsw *broadwell = pdata->dsp; + int ret; + + /* Set ADSP SSP port settings */ + ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0, + SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, + SST_HSW_DEVICE_CLOCK_MASTER, 9); + if (ret < 0) { + dev_err(rtd->dev, "error: failed to set device config\n"); + return ret; + } + + return 0; +} + +static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd) +{ + struct bdw_rt5677_priv *bdw_rt5677 = + snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1. + * The ASRC clock source is clk_i2s1_asrc. + */ + rt5677_sel_asrc_clk_src(codec, RT5677_DA_STEREO_FILTER | + RT5677_AD_STEREO1_FILTER | RT5677_I2S1_SOURCE, + RT5677_CLK_SEL_I2S1_ASRC); + + /* Request rt5677 GPIO for headphone amp control */ + bdw_rt5677->gpio_hp_en = devm_gpiod_get_index(codec->dev, + "headphone-enable", 0, 0); + if (IS_ERR(bdw_rt5677->gpio_hp_en)) { + dev_err(codec->dev, "Can't find HP_AMP_SHDN_L gpio\n"); + return PTR_ERR(bdw_rt5677->gpio_hp_en); + } + gpiod_direction_output(bdw_rt5677->gpio_hp_en, 0); + + /* Create and initialize headphone jack */ + if (!snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &headphone_jack, + &headphone_jack_pin, 1)) { + headphone_jack_gpio.gpiod_dev = codec->dev; + if (snd_soc_jack_add_gpios(&headphone_jack, 1, + &headphone_jack_gpio)) + dev_err(codec->dev, "Can't add headphone jack gpio\n"); + } else { + dev_err(codec->dev, "Can't create headphone jack\n"); + } + + /* Create and initialize mic jack */ + if (!snd_soc_card_jack_new(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, &mic_jack, + &mic_jack_pin, 1)) { + mic_jack_gpio.gpiod_dev = codec->dev; + if (snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio)) + dev_err(codec->dev, "Can't add mic jack gpio\n"); + } else { + dev_err(codec->dev, "Can't create mic jack\n"); + } + bdw_rt5677->codec = codec; + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + return 0; +} + +/* broadwell digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link bdw_rt5677_dais[] = { + /* Front End DAI links */ + { + .name = "System PCM", + .stream_name = "System Playback/Capture", + .cpu_dai_name = "System Pin", + .platform_name = "haswell-pcm-audio", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .init = bdw_rt5677_rtd_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST + }, + .dpcm_capture = 1, + .dpcm_playback = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "Codec", + .id = 0, + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_name = "i2c-RT5677CE:00", + .codec_dai_name = "rt5677-aif1", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = broadwell_ssp0_fixup, + .ops = &bdw_rt5677_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = bdw_rt5677_init, + }, +}; + +static int bdw_rt5677_suspend_pre(struct snd_soc_card *card) +{ + struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm; + + if (bdw_rt5677->codec) { + dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec); + snd_soc_dapm_disable_pin(dapm, "MICBIAS1"); + } + return 0; +} + +static int bdw_rt5677_resume_post(struct snd_soc_card *card) +{ + struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm; + + if (bdw_rt5677->codec) { + dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec); + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + } + return 0; +} + +/* ASoC machine driver for Broadwell DSP + RT5677 */ +static struct snd_soc_card bdw_rt5677_card = { + .name = "bdw-rt5677", + .owner = THIS_MODULE, + .dai_link = bdw_rt5677_dais, + .num_links = ARRAY_SIZE(bdw_rt5677_dais), + .dapm_widgets = bdw_rt5677_widgets, + .num_dapm_widgets = ARRAY_SIZE(bdw_rt5677_widgets), + .dapm_routes = bdw_rt5677_map, + .num_dapm_routes = ARRAY_SIZE(bdw_rt5677_map), + .controls = bdw_rt5677_controls, + .num_controls = ARRAY_SIZE(bdw_rt5677_controls), + .fully_routed = true, + .suspend_pre = bdw_rt5677_suspend_pre, + .resume_post = bdw_rt5677_resume_post, +}; + +static int bdw_rt5677_probe(struct platform_device *pdev) +{ + struct bdw_rt5677_priv *bdw_rt5677; + + bdw_rt5677_card.dev = &pdev->dev; + + /* Allocate driver private struct */ + bdw_rt5677 = devm_kzalloc(&pdev->dev, sizeof(struct bdw_rt5677_priv), + GFP_KERNEL); + if (!bdw_rt5677) { + dev_err(&pdev->dev, "Can't allocate bdw_rt5677\n"); + return -ENOMEM; + } + + snd_soc_card_set_drvdata(&bdw_rt5677_card, bdw_rt5677); + + return devm_snd_soc_register_card(&pdev->dev, &bdw_rt5677_card); +} + +static struct platform_driver bdw_rt5677_audio = { + .probe = bdw_rt5677_probe, + .driver = { + .name = "bdw-rt5677", + }, +}; + +module_platform_driver(bdw_rt5677_audio) + +/* Module information */ +MODULE_AUTHOR("Ben Zhang"); +MODULE_DESCRIPTION("Intel Broadwell RT5677 machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bdw-rt5677"); diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index 3774b117d365..6532b8f0ab2f 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -37,6 +37,7 @@ enum { BXT_DPCM_AUDIO_PB = 0, BXT_DPCM_AUDIO_CP, BXT_DPCM_AUDIO_REF_CP, + BXT_DPCM_AUDIO_DMIC_CP, BXT_DPCM_AUDIO_HDMI1_PB, BXT_DPCM_AUDIO_HDMI2_PB, BXT_DPCM_AUDIO_HDMI3_PB, @@ -252,10 +253,56 @@ static struct snd_soc_ops broxton_da7219_ops = { .hw_free = broxton_da7219_hw_free, }; +static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + channels->min = channels->max = DUAL_CHANNEL; + + return 0; +} + +static int broxton_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static const struct snd_soc_ops broxton_dmic_ops = { + .startup = broxton_dmic_startup, +}; + +static const unsigned int rates_16000[] = { + 16000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16000 = { + .count = ARRAY_SIZE(rates_16000), + .list = rates_16000, +}; + +static int broxton_refcap_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_16000); +}; + +static struct snd_soc_ops broxton_refcap_ops = { + .startup = broxton_refcap_startup, +}; + /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_dais[] = { /* Front End DAI links */ - [BXT_DPCM_AUDIO_PB] + [BXT_DPCM_AUDIO_PB] = { .name = "Bxt Audio Port", .stream_name = "Audio", @@ -271,7 +318,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .dpcm_playback = 1, .ops = &broxton_da7219_fe_ops, }, - [BXT_DPCM_AUDIO_CP] + [BXT_DPCM_AUDIO_CP] = { .name = "Bxt Audio Capture Port", .stream_name = "Audio Record", @@ -286,7 +333,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .dpcm_capture = 1, .ops = &broxton_da7219_fe_ops, }, - [BXT_DPCM_AUDIO_REF_CP] + [BXT_DPCM_AUDIO_REF_CP] = { .name = "Bxt Audio Reference cap", .stream_name = "Refcap", @@ -299,8 +346,23 @@ static struct snd_soc_dai_link broxton_dais[] = { .ignore_suspend = 1, .nonatomic = 1, .dynamic = 1, + .ops = &broxton_refcap_ops, + }, + [BXT_DPCM_AUDIO_DMIC_CP] + { + .name = "Bxt Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:0e.0", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &broxton_dmic_ops, }, - [BXT_DPCM_AUDIO_HDMI1_PB] + [BXT_DPCM_AUDIO_HDMI1_PB] = { .name = "Bxt HDMI Port1", .stream_name = "Hdmi1", @@ -313,7 +375,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_HDMI2_PB] + [BXT_DPCM_AUDIO_HDMI2_PB] = { .name = "Bxt HDMI Port2", .stream_name = "Hdmi2", @@ -326,7 +388,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_HDMI3_PB] + [BXT_DPCM_AUDIO_HDMI3_PB] = { .name = "Bxt HDMI Port3", .stream_name = "Hdmi3", @@ -382,6 +444,7 @@ static struct snd_soc_dai_link broxton_dais[] = { .codec_dai_name = "dmic-hifi", .platform_name = "0000:00:0e.0", .ignore_suspend = 1, + .be_hw_params_fixup = broxton_dmic_fixup, .dpcm_capture = 1, .no_pcm = 1, }, diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c index 253d7bfbf511..d610bdca1608 100644 --- a/sound/soc/intel/boards/bxt_rt298.c +++ b/sound/soc/intel/boards/bxt_rt298.c @@ -271,7 +271,7 @@ static const struct snd_soc_ops broxton_rt286_fe_ops = { /* broxton digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broxton_rt298_dais[] = { /* Front End DAI links */ - [BXT_DPCM_AUDIO_PB] + [BXT_DPCM_AUDIO_PB] = { .name = "Bxt Audio Port", .stream_name = "Audio", @@ -286,7 +286,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .dpcm_playback = 1, .ops = &broxton_rt286_fe_ops, }, - [BXT_DPCM_AUDIO_CP] + [BXT_DPCM_AUDIO_CP] = { .name = "Bxt Audio Capture Port", .stream_name = "Audio Record", @@ -300,7 +300,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .dpcm_capture = 1, .ops = &broxton_rt286_fe_ops, }, - [BXT_DPCM_AUDIO_REF_CP] + [BXT_DPCM_AUDIO_REF_CP] = { .name = "Bxt Audio Reference cap", .stream_name = "refcap", @@ -313,7 +313,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_DMIC_CP] + [BXT_DPCM_AUDIO_DMIC_CP] = { .name = "Bxt Audio DMIC cap", .stream_name = "dmiccap", @@ -327,7 +327,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .dynamic = 1, .ops = &broxton_dmic_ops, }, - [BXT_DPCM_AUDIO_HDMI1_PB] + [BXT_DPCM_AUDIO_HDMI1_PB] = { .name = "Bxt HDMI Port1", .stream_name = "Hdmi1", @@ -340,7 +340,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_HDMI2_PB] + [BXT_DPCM_AUDIO_HDMI2_PB] = { .name = "Bxt HDMI Port2", .stream_name = "Hdmi2", @@ -353,7 +353,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = { .nonatomic = 1, .dynamic = 1, }, - [BXT_DPCM_AUDIO_HDMI3_PB] + [BXT_DPCM_AUDIO_HDMI3_PB] = { .name = "Bxt HDMI Port3", .stream_name = "Hdmi3", diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 88efb62439ba..bff77a1f27fc 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -24,6 +24,9 @@ #include <linux/device.h> #include <linux/dmi.h> #include <linux/slab.h> +#include <asm/cpu_device_id.h> +#include <asm/platform_sst_audio.h> +#include <linux/clk.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -31,42 +34,153 @@ #include "../../codecs/rt5640.h" #include "../atom/sst-atom-controls.h" #include "../common/sst-acpi.h" +#include "../common/sst-dsp.h" enum { BYT_RT5640_DMIC1_MAP, BYT_RT5640_DMIC2_MAP, BYT_RT5640_IN1_MAP, + BYT_RT5640_IN3_MAP, }; #define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) #define BYT_RT5640_DMIC_EN BIT(16) +#define BYT_RT5640_MONO_SPEAKER BIT(17) +#define BYT_RT5640_DIFF_MIC BIT(18) /* defaut is single-ended */ +#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ +#define BYT_RT5640_SSP0_AIF1 BIT(20) +#define BYT_RT5640_SSP0_AIF2 BIT(21) +#define BYT_RT5640_MCLK_EN BIT(22) +#define BYT_RT5640_MCLK_25MHZ BIT(23) + +struct byt_rt5640_private { + struct clk *mclk; +}; static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | - BYT_RT5640_DMIC_EN; + BYT_RT5640_DMIC_EN | + BYT_RT5640_MCLK_EN; + +static void log_quirks(struct device *dev) +{ + if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC1_MAP) + dev_info(dev, "quirk DMIC1_MAP enabled"); + if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC2_MAP) + dev_info(dev, "quirk DMIC2_MAP enabled"); + if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN1_MAP) + dev_info(dev, "quirk IN1_MAP enabled"); + if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN3_MAP) + dev_info(dev, "quirk IN3_MAP enabled"); + if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) + dev_info(dev, "quirk DMIC enabled"); + if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) + dev_info(dev, "quirk MONO_SPEAKER enabled"); + if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) + dev_info(dev, "quirk DIFF_MIC enabled"); + if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) + dev_info(dev, "quirk SSP2_AIF2 enabled"); + if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) + dev_info(dev, "quirk SSP0_AIF1 enabled"); + if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) + dev_info(dev, "quirk SSP0_AIF2 enabled"); + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) + dev_info(dev, "quirk MCLK_EN enabled"); + if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) + dev_info(dev, "quirk MCLK_25MHZ enabled"); +} + + +#define BYT_CODEC_DAI1 "rt5640-aif1" +#define BYT_CODEC_DAI2 "rt5640-aif2" + +static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1, + strlen(BYT_CODEC_DAI1))) + return rtd->codec_dai; + if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI2, + strlen(BYT_CODEC_DAI2))) + return rtd->codec_dai; + + } + return NULL; +} + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); + int ret; + + codec_dai = byt_get_codec_dai(card); + if (!codec_dai) { + dev_err(card->dev, + "Codec dai not found; Unable to set platform clock\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(card->dev, + "could not configure MCLK state"); + return ret; + } + } + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, + 48000 * 512, + SND_SOC_CLOCK_IN); + } else { + /* + * Set codec clock source to internal clock before + * turning off the platform clock. Codec needs clock + * for Jack detection and button press + */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK, + 0, + SND_SOC_CLOCK_IN); + if (!ret) { + if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) + clk_disable_unprepare(priv->mclk); + } + } + + if (ret < 0) { + dev_err(card->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + + return 0; +} static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Internal Mic", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + }; static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { - {"AIF1 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, "AIF1 Capture"}, + {"Headphone", NULL, "Platform Clock"}, + {"Headset Mic", NULL, "Platform Clock"}, + {"Internal Mic", NULL, "Platform Clock"}, + {"Speaker", NULL, "Platform Clock"}, {"Headset Mic", NULL, "MICBIAS1"}, {"IN2P", NULL, "Headset Mic"}, {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, - {"Speaker", NULL, "SPOLP"}, - {"Speaker", NULL, "SPOLN"}, - {"Speaker", NULL, "SPORP"}, - {"Speaker", NULL, "SPORN"}, }; static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = { @@ -82,6 +196,59 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { {"IN1P", NULL, "Internal Mic"}, }; +static const struct snd_soc_dapm_route byt_rt5640_intmic_in3_map[] = { + {"Internal Mic", NULL, "MICBIAS1"}, + {"IN3P", NULL, "Internal Mic"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif1_map[] = { + {"ssp2 Tx", NULL, "codec_out0"}, + {"ssp2 Tx", NULL, "codec_out1"}, + {"codec_in0", NULL, "ssp2 Rx"}, + {"codec_in1", NULL, "ssp2 Rx"}, + + {"AIF1 Playback", NULL, "ssp2 Tx"}, + {"ssp2 Rx", NULL, "AIF1 Capture"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif2_map[] = { + {"ssp2 Tx", NULL, "codec_out0"}, + {"ssp2 Tx", NULL, "codec_out1"}, + {"codec_in0", NULL, "ssp2 Rx"}, + {"codec_in1", NULL, "ssp2 Rx"}, + + {"AIF2 Playback", NULL, "ssp2 Tx"}, + {"ssp2 Rx", NULL, "AIF2 Capture"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif1_map[] = { + {"ssp0 Tx", NULL, "modem_out"}, + {"modem_in", NULL, "ssp0 Rx"}, + + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Rx", NULL, "AIF1 Capture"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif2_map[] = { + {"ssp0 Tx", NULL, "modem_out"}, + {"modem_in", NULL, "ssp0 Rx"}, + + {"AIF2 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Rx", NULL, "AIF2 Capture"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_stereo_spk_map[] = { + {"Speaker", NULL, "SPOLP"}, + {"Speaker", NULL, "SPOLN"}, + {"Speaker", NULL, "SPORP"}, + {"Speaker", NULL, "SPORN"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_mono_spk_map[] = { + {"Speaker", NULL, "SPOLP"}, + {"Speaker", NULL, "SPOLN"}, +}; + static const struct snd_kcontrol_new byt_rt5640_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -96,19 +263,46 @@ static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret; - snd_soc_dai_set_bclk_ratio(codec_dai, 50); - ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, params_rate(params) * 512, SND_SOC_CLOCK_IN); + if (ret < 0) { dev_err(rtd->dev, "can't set codec clock %d\n", ret); return ret; } - ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, - params_rate(params) * 50, - params_rate(params) * 512); + if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) { + /* use bitclock as PLL input */ + if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { + + /* 2x16 bit slots on SSP0 */ + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_BCLK1, + params_rate(params) * 32, + params_rate(params) * 512); + } else { + /* 2x15 bit slots on SSP2 */ + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_BCLK1, + params_rate(params) * 50, + params_rate(params) * 512); + } + } else { + if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_MCLK, + 25000000, + params_rate(params) * 512); + } else { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5640_PLL1_S_MCLK, + 19200000, + params_rate(params) * 512); + } + } + if (ret < 0) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; @@ -127,27 +321,73 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { { .callback = byt_rt5640_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN), + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), }, - .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, + .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF2 | + BYT_RT5640_MCLK_EN + ), }, { .callback = byt_rt5640_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "DellInc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), }, .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | + BYT_RT5640_DMIC_EN | + BYT_RT5640_MCLK_EN), + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN), + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_DMIC1_MAP | BYT_RT5640_DMIC_EN), }, { .callback = byt_rt5640_quirk_cb, .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), + DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), + DMI_MATCH(DMI_BOARD_NAME, "tPAD"), }, - .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, + .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP | + BYT_RT5640_MCLK_EN | + BYT_RT5640_SSP0_AIF1), + }, + { + .callback = byt_rt5640_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), + }, + .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN | + BYT_RT5640_SSP0_AIF1), + }, {} }; @@ -158,13 +398,18 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; const struct snd_soc_dapm_route *custom_map; + struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); int num_routes; card->dapm.idle_bias_off = true; rt5640_sel_asrc_clk_src(codec, RT5640_DA_STEREO_FILTER | - RT5640_AD_STEREO_FILTER, + RT5640_DA_MONO_L_FILTER | + RT5640_DA_MONO_R_FILTER | + RT5640_AD_STEREO_FILTER | + RT5640_AD_MONO_L_FILTER | + RT5640_AD_MONO_R_FILTER, RT5640_CLK_SEL_ASRC); ret = snd_soc_add_card_controls(card, byt_rt5640_controls, @@ -179,6 +424,10 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) custom_map = byt_rt5640_intmic_in1_map; num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map); break; + case BYT_RT5640_IN3_MAP: + custom_map = byt_rt5640_intmic_in3_map; + num_routes = ARRAY_SIZE(byt_rt5640_intmic_in3_map); + break; case BYT_RT5640_DMIC2_MAP: custom_map = byt_rt5640_intmic_dmic2_map; num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map); @@ -192,6 +441,43 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; + if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_ssp2_aif2_map, + ARRAY_SIZE(byt_rt5640_ssp2_aif2_map)); + } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_ssp0_aif1_map, + ARRAY_SIZE(byt_rt5640_ssp0_aif1_map)); + } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_ssp0_aif2_map, + ARRAY_SIZE(byt_rt5640_ssp0_aif2_map)); + } else { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_ssp2_aif1_map, + ARRAY_SIZE(byt_rt5640_ssp2_aif1_map)); + } + if (ret) + return ret; + + if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_mono_spk_map, + ARRAY_SIZE(byt_rt5640_mono_spk_map)); + } else { + ret = snd_soc_dapm_add_routes(&card->dapm, + byt_rt5640_stereo_spk_map, + ARRAY_SIZE(byt_rt5640_stereo_spk_map)); + } + if (ret) + return ret; + + if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) { + snd_soc_update_bits(codec, RT5640_IN1_IN2, RT5640_IN_DF1, + RT5640_IN_DF1); + } + if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { ret = rt5640_dmic_enable(codec, 0, 0); if (ret) @@ -201,6 +487,30 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); + if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { + /* + * The firmware might enable the clock at + * boot (this information may or may not + * be reflected in the enable clock register). + * To change the rate we must disable the clock + * first to cover these cases. Due to common + * clock framework restrictions that do not allow + * to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(priv->mclk); + if (!ret) + clk_disable_unprepare(priv->mclk); + + if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) + ret = clk_set_rate(priv->mclk, 25000000); + else + ret = clk_set_rate(priv->mclk, 19200000); + + if (ret) + dev_err(card->dev, "unable to set MCLK rate\n"); + } + return ret; } @@ -221,34 +531,63 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd, SNDRV_PCM_HW_PARAM_CHANNELS); int ret; - /* The DSP will covert the FE rate to 48k, stereo, 24bits */ + /* The DSP will covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; channels->min = channels->max = 2; - /* set SSP2 to 24-bit */ - params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { + + /* set SSP0 to 16-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot, override config + * with explicit setting to I2S 2ch 16-bit. The word length is set with + * dai_set_tdm_slot() since there is no other API exposed + */ + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS + ); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); + return ret; + } - /* - * Default mode for SSP configuration is TDM 4 slot, override config - * with explicit setting to I2S 2ch 24-bit. The word length is set with - * dai_set_tdm_slot() since there is no other API exposed - */ - ret = snd_soc_dai_set_fmt(rtd->cpu_dai, - SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_IF | - SND_SOC_DAIFMT_CBS_CFS - ); - if (ret < 0) { - dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); - return ret; - } + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16); + if (ret < 0) { + dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); + return ret; + } - ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); - if (ret < 0) { - dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); - return ret; - } + } else { + + /* set SSP2 to 24-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot, override config + * with explicit setting to I2S 2ch 24-bit. The word length is set with + * dai_set_tdm_slot() since there is no other API exposed + */ + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS + ); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); + return ret; + } + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); + if (ret < 0) { + dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); + return ret; + } + } return 0; } @@ -305,10 +644,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { { .name = "SSP2-Codec", .id = 1, - .cpu_dai_name = "ssp2-port", + .cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */ .platform_name = "sst-mfld-platform", .no_pcm = 1, - .codec_dai_name = "rt5640-aif1", + .codec_dai_name = "rt5640-aif1", /* changed w/ quirk */ .codec_name = "i2c-10EC5640:00", /* overwritten with HID */ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, @@ -335,6 +674,21 @@ static struct snd_soc_card byt_rt5640_card = { }; static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ +static char byt_rt5640_codec_aif_name[12]; /* = "rt5640-aif[1|2]" */ +static char byt_rt5640_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ + +static bool is_valleyview(void) +{ + static const struct x86_cpu_id cpu_ids[] = { + { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ + {} + }; + + if (!x86_match_cpu(cpu_ids)) + return false; + return true; +} + static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { @@ -343,10 +697,16 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) const char *i2c_name = NULL; int i; int dai_index; + struct byt_rt5640_private *priv; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); + if (!priv) + return -ENOMEM; /* register the soc card */ byt_rt5640_card.dev = &pdev->dev; mach = byt_rt5640_card.dev->platform_data; + snd_soc_card_set_drvdata(&byt_rt5640_card, priv); /* fix index of codec dai */ dai_index = MERR_DPCM_COMPR + 1; @@ -366,8 +726,57 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name; } + /* + * swap SSP0 if bytcr is detected + * (will be overridden if DMI quirk is detected) + */ + if (is_valleyview()) { + struct sst_platform_info *p_info = mach->pdata; + const struct sst_res_info *res_info = p_info->res_info; + + /* TODO: use CHAN package info from BIOS to detect AIF1/AIF2 */ + if (res_info->acpi_ipc_irq_index == 0) { + byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2; + } + } + /* check quirks before creating card */ dmi_check_system(byt_rt5640_quirk_table); + log_quirks(&pdev->dev); + + if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) || + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { + + /* fixup codec aif name */ + snprintf(byt_rt5640_codec_aif_name, + sizeof(byt_rt5640_codec_aif_name), + "%s", "rt5640-aif2"); + + byt_rt5640_dais[dai_index].codec_dai_name = + byt_rt5640_codec_aif_name; + } + + if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) || + (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) { + + /* fixup cpu dai name name */ + snprintf(byt_rt5640_cpu_dai_name, + sizeof(byt_rt5640_cpu_dai_name), + "%s", "ssp0-port"); + + byt_rt5640_dais[dai_index].cpu_dai_name = + byt_rt5640_cpu_dai_name; + } + + if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && (is_valleyview())) { + priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(priv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(priv->mclk)); + return PTR_ERR(priv->mclk); + } + } ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card); |