diff options
Diffstat (limited to 'sound/soc/intel/boards')
| -rw-r--r-- | sound/soc/intel/boards/Makefile | 15 | ||||
| -rw-r--r-- | sound/soc/intel/boards/broadwell.c | 292 | ||||
| -rw-r--r-- | sound/soc/intel/boards/byt-max98090.c | 187 | ||||
| -rw-r--r-- | sound/soc/intel/boards/byt-rt5640.c | 229 | ||||
| -rw-r--r-- | sound/soc/intel/boards/bytcr_rt5640.c | 227 | ||||
| -rw-r--r-- | sound/soc/intel/boards/cht_bsw_rt5645.c | 324 | ||||
| -rw-r--r-- | sound/soc/intel/boards/cht_bsw_rt5672.c | 366 | ||||
| -rw-r--r-- | sound/soc/intel/boards/haswell.c | 209 | ||||
| -rw-r--r-- | sound/soc/intel/boards/mfld_machine.c | 430 | 
9 files changed, 2279 insertions, 0 deletions
| diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile new file mode 100644 index 000000000000..f8237f0044eb --- /dev/null +++ b/sound/soc/intel/boards/Makefile @@ -0,0 +1,15 @@ +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-broadwell-objs := broadwell.o +snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o +snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o +snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o + +obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o +obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o +obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o +obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o +obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o +obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o +obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c new file mode 100644 index 000000000000..8bafaf6ceab1 --- /dev/null +++ b/sound/soc/intel/boards/broadwell.c @@ -0,0 +1,292 @@ +/* + * Intel Broadwell Wildcatpoint SST Audio + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/pcm_params.h> + +#include "../common/sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" + +#include "../../codecs/rt286.h" + +static struct snd_soc_jack broadwell_headset; +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin broadwell_headset_pins[] = { +	{ +		.pin = "Mic Jack", +		.mask = SND_JACK_MICROPHONE, +	}, +	{ +		.pin = "Headphone Jack", +		.mask = SND_JACK_HEADPHONE, +	}, +}; + +static const struct snd_kcontrol_new broadwell_controls[] = { +	SOC_DAPM_PIN_SWITCH("Speaker"), +	SOC_DAPM_PIN_SWITCH("Headphone Jack"), +}; + +static const struct snd_soc_dapm_widget broadwell_widgets[] = { +	SND_SOC_DAPM_HP("Headphone Jack", NULL), +	SND_SOC_DAPM_SPK("Speaker", NULL), +	SND_SOC_DAPM_MIC("Mic Jack", NULL), +	SND_SOC_DAPM_MIC("DMIC1", NULL), +	SND_SOC_DAPM_MIC("DMIC2", NULL), +	SND_SOC_DAPM_LINE("Line Jack", NULL), +}; + +static const struct snd_soc_dapm_route broadwell_rt286_map[] = { + +	/* speaker */ +	{"Speaker", NULL, "SPOR"}, +	{"Speaker", NULL, "SPOL"}, + +	/* HP jack connectors - unknown if we have jack deteck */ +	{"Headphone Jack", NULL, "HPO Pin"}, + +	/* other jacks */ +	{"MIC1", NULL, "Mic Jack"}, +	{"LINE1", NULL, "Line Jack"}, + +	/* digital mics */ +	{"DMIC1 Pin", NULL, "DMIC1"}, +	{"DMIC2 Pin", NULL, "DMIC2"}, + +	/* CODEC BE connections */ +	{"SSP0 CODEC IN", NULL, "AIF1 Capture"}, +	{"AIF1 Playback", NULL, "SSP0 CODEC OUT"}, +}; + +static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) +{ +	struct snd_soc_codec *codec = rtd->codec; +	int ret = 0; +	ret = snd_soc_card_jack_new(rtd->card, "Headset", +		SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset, +		broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins)); +	if (ret) +		return ret; + +	rt286_mic_detect(codec, &broadwell_headset); +	return 0; +} + + +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 */ +	params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); +	return 0; +} + +static int broadwell_rt286_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, RT286_SCLK_S_PLL, 24000000, +		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 broadwell_rt286_ops = { +	.hw_params = broadwell_rt286_hw_params, +}; + +static int broadwell_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; +} + +/* broadwell digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link broadwell_rt286_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 = broadwell_rtd_init, +		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +	}, +	{ +		.name = "Offload0", +		.stream_name = "Offload0 Playback", +		.cpu_dai_name = "Offload0 Pin", +		.platform_name = "haswell-pcm-audio", +		.dynamic = 1, +		.codec_name = "snd-soc-dummy", +		.codec_dai_name = "snd-soc-dummy-dai", +		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, +		.dpcm_playback = 1, +	}, +	{ +		.name = "Offload1", +		.stream_name = "Offload1 Playback", +		.cpu_dai_name = "Offload1 Pin", +		.platform_name = "haswell-pcm-audio", +		.dynamic = 1, +		.codec_name = "snd-soc-dummy", +		.codec_dai_name = "snd-soc-dummy-dai", +		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, +		.dpcm_playback = 1, +	}, +	{ +		.name = "Loopback PCM", +		.stream_name = "Loopback", +		.cpu_dai_name = "Loopback Pin", +		.platform_name = "haswell-pcm-audio", +		.dynamic = 0, +		.codec_name = "snd-soc-dummy", +		.codec_dai_name = "snd-soc-dummy-dai", +		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, +		.dpcm_capture = 1, +	}, +	/* Back End DAI links */ +	{ +		/* SSP0 - Codec */ +		.name = "Codec", +		.be_id = 0, +		.cpu_dai_name = "snd-soc-dummy-dai", +		.platform_name = "snd-soc-dummy", +		.no_pcm = 1, +		.codec_name = "i2c-INT343A:00", +		.codec_dai_name = "rt286-aif1", +		.init = broadwell_rt286_codec_init, +		.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 = &broadwell_rt286_ops, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +	}, +}; + +static int broadwell_suspend(struct snd_soc_card *card){ +	struct snd_soc_codec *codec; + +	list_for_each_entry(codec, &card->codec_dev_list, card_list) { +		if (!strcmp(codec->component.name, "i2c-INT343A:00")) { +			dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); +			rt286_mic_detect(codec, NULL); +			break; +		} +	} +	return 0; +} + +static int broadwell_resume(struct snd_soc_card *card){ +	struct snd_soc_codec *codec; + +	list_for_each_entry(codec, &card->codec_dev_list, card_list) { +		if (!strcmp(codec->component.name, "i2c-INT343A:00")) { +			dev_dbg(codec->dev, "enabling jack detect for resume.\n"); +			rt286_mic_detect(codec, &broadwell_headset); +			break; +		} +	} +	return 0; +} + +/* broadwell audio machine driver for WPT + RT286S */ +static struct snd_soc_card broadwell_rt286 = { +	.name = "broadwell-rt286", +	.owner = THIS_MODULE, +	.dai_link = broadwell_rt286_dais, +	.num_links = ARRAY_SIZE(broadwell_rt286_dais), +	.controls = broadwell_controls, +	.num_controls = ARRAY_SIZE(broadwell_controls), +	.dapm_widgets = broadwell_widgets, +	.num_dapm_widgets = ARRAY_SIZE(broadwell_widgets), +	.dapm_routes = broadwell_rt286_map, +	.num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map), +	.fully_routed = true, +	.suspend_pre = broadwell_suspend, +	.resume_post = broadwell_resume, +}; + +static int broadwell_audio_probe(struct platform_device *pdev) +{ +	broadwell_rt286.dev = &pdev->dev; + +	return snd_soc_register_card(&broadwell_rt286); +} + +static int broadwell_audio_remove(struct platform_device *pdev) +{ +	snd_soc_unregister_card(&broadwell_rt286); +	return 0; +} + +static struct platform_driver broadwell_audio = { +	.probe = broadwell_audio_probe, +	.remove = broadwell_audio_remove, +	.driver = { +		.name = "broadwell-audio", +	}, +}; + +module_platform_driver(broadwell_audio) + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, Xingchao Wang"); +MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:broadwell-audio"); diff --git a/sound/soc/intel/boards/byt-max98090.c b/sound/soc/intel/boards/byt-max98090.c new file mode 100644 index 000000000000..7ab8cc9fbfd5 --- /dev/null +++ b/sound/soc/intel/boards/byt-max98090.c @@ -0,0 +1,187 @@ +/* + * Intel Baytrail SST MAX98090 machine driver + * Copyright (c) 2014, Intel Corporation. + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../../codecs/max98090.h" + +struct byt_max98090_private { +	struct snd_soc_jack jack; +}; + +static const struct snd_soc_dapm_widget byt_max98090_widgets[] = { +	SND_SOC_DAPM_HP("Headphone", NULL), +	SND_SOC_DAPM_MIC("Headset Mic", NULL), +	SND_SOC_DAPM_MIC("Int Mic", NULL), +	SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_soc_dapm_route byt_max98090_audio_map[] = { +	{"IN34", NULL, "Headset Mic"}, +	{"Headset Mic", NULL, "MICBIAS"}, +	{"DMICL", NULL, "Int Mic"}, +	{"Headphone", NULL, "HPL"}, +	{"Headphone", NULL, "HPR"}, +	{"Ext Spk", NULL, "SPKL"}, +	{"Ext Spk", NULL, "SPKR"}, +}; + +static const struct snd_kcontrol_new byt_max98090_controls[] = { +	SOC_DAPM_PIN_SWITCH("Headphone"), +	SOC_DAPM_PIN_SWITCH("Headset Mic"), +	SOC_DAPM_PIN_SWITCH("Int Mic"), +	SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static struct snd_soc_jack_pin hs_jack_pins[] = { +	{ +		.pin	= "Headphone", +		.mask	= SND_JACK_HEADPHONE, +	}, +	{ +		.pin	= "Headset Mic", +		.mask	= SND_JACK_MICROPHONE, +	}, +}; + +static struct snd_soc_jack_gpio hs_jack_gpios[] = { +	{ +		.name		= "hp-gpio", +		.idx		= 0, +		.report		= SND_JACK_HEADPHONE | SND_JACK_LINEOUT, +		.debounce_time	= 200, +	}, +	{ +		.name		= "mic-gpio", +		.idx		= 1, +		.invert		= 1, +		.report		= SND_JACK_MICROPHONE, +		.debounce_time	= 200, +	}, +}; + +static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime) +{ +	int ret; +	struct snd_soc_card *card = runtime->card; +	struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card); +	struct snd_soc_jack *jack = &drv->jack; + +	card->dapm.idle_bias_off = true; + +	ret = snd_soc_dai_set_sysclk(runtime->codec_dai, +				     M98090_REG_SYSTEM_CLOCK, +				     25000000, SND_SOC_CLOCK_IN); +	if (ret < 0) { +		dev_err(card->dev, "Can't set codec clock %d\n", ret); +		return ret; +	} + +	/* Enable jack detection */ +	ret = snd_soc_card_jack_new(runtime->card, "Headset", +				    SND_JACK_LINEOUT | SND_JACK_HEADSET, jack, +				    hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); +	if (ret) +		return ret; + +	return snd_soc_jack_add_gpiods(card->dev->parent, jack, +				       ARRAY_SIZE(hs_jack_gpios), +				       hs_jack_gpios); +} + +static struct snd_soc_dai_link byt_max98090_dais[] = { +	{ +		.name = "Baytrail Audio", +		.stream_name = "Audio", +		.cpu_dai_name = "baytrail-pcm-audio", +		.codec_dai_name = "HiFi", +		.codec_name = "i2c-193C9890:00", +		.platform_name = "baytrail-pcm-audio", +		.init = byt_max98090_init, +		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | +			   SND_SOC_DAIFMT_CBS_CFS, +	}, +}; + +static struct snd_soc_card byt_max98090_card = { +	.name = "byt-max98090", +	.dai_link = byt_max98090_dais, +	.num_links = ARRAY_SIZE(byt_max98090_dais), +	.dapm_widgets = byt_max98090_widgets, +	.num_dapm_widgets = ARRAY_SIZE(byt_max98090_widgets), +	.dapm_routes = byt_max98090_audio_map, +	.num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map), +	.controls = byt_max98090_controls, +	.num_controls = ARRAY_SIZE(byt_max98090_controls), +	.fully_routed = true, +}; + +static int byt_max98090_probe(struct platform_device *pdev) +{ +	int ret_val = 0; +	struct byt_max98090_private *priv; + +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); +	if (!priv) { +		dev_err(&pdev->dev, "allocation failed\n"); +		return -ENOMEM; +	} + +	byt_max98090_card.dev = &pdev->dev; +	snd_soc_card_set_drvdata(&byt_max98090_card, priv); +	ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_max98090_card); +	if (ret_val) { +		dev_err(&pdev->dev, +			"snd_soc_register_card failed %d\n", ret_val); +		return ret_val; +	} + +	return ret_val; +} + +static int byt_max98090_remove(struct platform_device *pdev) +{ +	struct snd_soc_card *card = platform_get_drvdata(pdev); +	struct byt_max98090_private *priv = snd_soc_card_get_drvdata(card); + +	snd_soc_jack_free_gpios(&priv->jack, ARRAY_SIZE(hs_jack_gpios), +				hs_jack_gpios); + +	return 0; +} + +static struct platform_driver byt_max98090_driver = { +	.probe = byt_max98090_probe, +	.remove = byt_max98090_remove, +	.driver = { +		.name = "byt-max98090", +		.pm = &snd_soc_pm_ops, +	}, +}; +module_platform_driver(byt_max98090_driver) + +MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver"); +MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:byt-max98090"); diff --git a/sound/soc/intel/boards/byt-rt5640.c b/sound/soc/intel/boards/byt-rt5640.c new file mode 100644 index 000000000000..ae89b9b966d9 --- /dev/null +++ b/sound/soc/intel/boards/byt-rt5640.c @@ -0,0 +1,229 @@ +/* + * Intel Baytrail SST RT5640 machine driver + * Copyright (c) 2014, Intel Corporation. + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/dmi.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../../codecs/rt5640.h" + +#include "../common/sst-dsp.h" + +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), +}; + +static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { +	{"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[] = { +	{"DMIC1", NULL, "Internal Mic"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = { +	{"DMIC2", NULL, "Internal Mic"}, +}; + +static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { +	{"Internal Mic", NULL, "MICBIAS1"}, +	{"IN1P", NULL, "Internal Mic"}, +}; + +enum { +	BYT_RT5640_DMIC1_MAP, +	BYT_RT5640_DMIC2_MAP, +	BYT_RT5640_IN1_MAP, +}; + +#define BYT_RT5640_MAP(quirk)	((quirk) & 0xff) +#define BYT_RT5640_DMIC_EN	BIT(16) + +static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | +					BYT_RT5640_DMIC_EN; + +static const struct snd_kcontrol_new byt_rt5640_controls[] = { +	SOC_DAPM_PIN_SWITCH("Headphone"), +	SOC_DAPM_PIN_SWITCH("Headset Mic"), +	SOC_DAPM_PIN_SWITCH("Internal Mic"), +	SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static int byt_rt5640_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, RT5640_SCLK_S_PLL1, +				     params_rate(params) * 256, +				     SND_SOC_CLOCK_IN); +	if (ret < 0) { +		dev_err(codec_dai->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) * 64, +				  params_rate(params) * 256); +	if (ret < 0) { +		dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret); +		return ret; +	} +	return 0; +} + +static int byt_rt5640_quirk_cb(const struct dmi_system_id *id) +{ +	byt_rt5640_quirk = (unsigned long)id->driver_data; +	return 1; +} + +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"), +		}, +		.driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, +	}, +	{ +		.callback = byt_rt5640_quirk_cb, +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "DellInc."), +			DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), +		}, +		.driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | +						 BYT_RT5640_DMIC_EN), +	}, +	{} +}; + +static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) +{ +	int ret; +	struct snd_soc_codec *codec = runtime->codec; +	struct snd_soc_card *card = runtime->card; +	const struct snd_soc_dapm_route *custom_map; +	int num_routes; + +	card->dapm.idle_bias_off = true; + +	ret = snd_soc_add_card_controls(card, byt_rt5640_controls, +					ARRAY_SIZE(byt_rt5640_controls)); +	if (ret) { +		dev_err(card->dev, "unable to add card controls\n"); +		return ret; +	} + +	dmi_check_system(byt_rt5640_quirk_table); +	switch (BYT_RT5640_MAP(byt_rt5640_quirk)) { +	case BYT_RT5640_IN1_MAP: +		custom_map = byt_rt5640_intmic_in1_map; +		num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map); +		break; +	case BYT_RT5640_DMIC2_MAP: +		custom_map = byt_rt5640_intmic_dmic2_map; +		num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map); +		break; +	default: +		custom_map = byt_rt5640_intmic_dmic1_map; +		num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map); +	} + +	ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); +	if (ret) +		return ret; + +	if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { +		ret = rt5640_dmic_enable(codec, 0, 0); +		if (ret) +			return ret; +	} + +	snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); +	snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); + +	return ret; +} + +static struct snd_soc_ops byt_rt5640_ops = { +	.hw_params = byt_rt5640_hw_params, +}; + +static struct snd_soc_dai_link byt_rt5640_dais[] = { +	{ +		.name = "Baytrail Audio", +		.stream_name = "Audio", +		.cpu_dai_name = "baytrail-pcm-audio", +		.codec_dai_name = "rt5640-aif1", +		.codec_name = "i2c-10EC5640:00", +		.platform_name = "baytrail-pcm-audio", +		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | +			   SND_SOC_DAIFMT_CBS_CFS, +		.init = byt_rt5640_init, +		.ops = &byt_rt5640_ops, +	}, +}; + +static struct snd_soc_card byt_rt5640_card = { +	.name = "byt-rt5640", +	.dai_link = byt_rt5640_dais, +	.num_links = ARRAY_SIZE(byt_rt5640_dais), +	.dapm_widgets = byt_rt5640_widgets, +	.num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets), +	.dapm_routes = byt_rt5640_audio_map, +	.num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map), +	.fully_routed = true, +}; + +static int byt_rt5640_probe(struct platform_device *pdev) +{ +	struct snd_soc_card *card = &byt_rt5640_card; + +	card->dev = &pdev->dev; +	return devm_snd_soc_register_card(&pdev->dev, card); +} + +static struct platform_driver byt_rt5640_audio = { +	.probe = byt_rt5640_probe, +	.driver = { +		.name = "byt-rt5640", +		.pm = &snd_soc_pm_ops, +	}, +}; +module_platform_driver(byt_rt5640_audio) + +MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver"); +MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:byt-rt5640"); diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c new file mode 100644 index 000000000000..7f55d59024a8 --- /dev/null +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -0,0 +1,227 @@ +/* + *  byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform + * + *  Copyright (C) 2014 Intel Corp + *  Author: Subhransu S. Prusty <[email protected]> + *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; version 2 of the License. + * + *  This program is distributed in the hope that 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../codecs/rt5640.h" +#include "../atom/sst-atom-controls.h" + +static const struct snd_soc_dapm_widget byt_dapm_widgets[] = { +	SND_SOC_DAPM_HP("Headphone", NULL), +	SND_SOC_DAPM_MIC("Headset Mic", NULL), +	SND_SOC_DAPM_MIC("Int Mic", NULL), +	SND_SOC_DAPM_SPK("Ext Spk", NULL), +}; + +static const struct snd_soc_dapm_route byt_audio_map[] = { +	{"IN2P", NULL, "Headset Mic"}, +	{"IN2N", NULL, "Headset Mic"}, +	{"Headset Mic", NULL, "MICBIAS1"}, +	{"IN1P", NULL, "MICBIAS1"}, +	{"LDO2", NULL, "Int Mic"}, +	{"Headphone", NULL, "HPOL"}, +	{"Headphone", NULL, "HPOR"}, +	{"Ext Spk", NULL, "SPOLP"}, +	{"Ext Spk", NULL, "SPOLN"}, +	{"Ext Spk", NULL, "SPORP"}, +	{"Ext Spk", NULL, "SPORN"}, + +	{"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"}, +}; + +static const struct snd_kcontrol_new byt_mc_controls[] = { +	SOC_DAPM_PIN_SWITCH("Headphone"), +	SOC_DAPM_PIN_SWITCH("Headset Mic"), +	SOC_DAPM_PIN_SWITCH("Int Mic"), +	SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static int byt_aif1_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; + +	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 (ret < 0) { +		dev_err(rtd->dev, "can't set codec pll: %d\n", ret); +		return ret; +	} + +	return 0; +} + +static const struct snd_soc_pcm_stream byt_dai_params = { +	.formats = SNDRV_PCM_FMTBIT_S24_LE, +	.rate_min = 48000, +	.rate_max = 48000, +	.channels_min = 2, +	.channels_max = 2, +}; + +static int byt_codec_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 DSP will covert the FE rate to 48k, stereo, 24bits */ +	rate->min = rate->max = 48000; +	channels->min = channels->max = 2; + +	/* set SSP2 to 24-bit */ +	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); +	return 0; +} + +static unsigned int rates_48000[] = { +	48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { +	.count = ARRAY_SIZE(rates_48000), +	.list  = rates_48000, +}; + +static int byt_aif1_startup(struct snd_pcm_substream *substream) +{ +	return snd_pcm_hw_constraint_list(substream->runtime, 0, +			SNDRV_PCM_HW_PARAM_RATE, +			&constraints_48000); +} + +static struct snd_soc_ops byt_aif1_ops = { +	.startup = byt_aif1_startup, +}; + +static struct snd_soc_ops byt_be_ssp2_ops = { +	.hw_params = byt_aif1_hw_params, +}; + +static struct snd_soc_dai_link byt_dailink[] = { +	[MERR_DPCM_AUDIO] = { +		.name = "Baytrail Audio Port", +		.stream_name = "Baytrail Audio", +		.cpu_dai_name = "media-cpu-dai", +		.codec_dai_name = "snd-soc-dummy-dai", +		.codec_name = "snd-soc-dummy", +		.platform_name = "sst-mfld-platform", +		.ignore_suspend = 1, +		.dynamic = 1, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +		.ops = &byt_aif1_ops, +	}, +	[MERR_DPCM_COMPR] = { +		.name = "Baytrail Compressed Port", +		.stream_name = "Baytrail Compress", +		.cpu_dai_name = "compress-cpu-dai", +		.codec_dai_name = "snd-soc-dummy-dai", +		.codec_name = "snd-soc-dummy", +		.platform_name = "sst-mfld-platform", +	}, +		/* back ends */ +	{ +		.name = "SSP2-Codec", +		.be_id = 1, +		.cpu_dai_name = "ssp2-port", +		.platform_name = "sst-mfld-platform", +		.no_pcm = 1, +		.codec_dai_name = "rt5640-aif1", +		.codec_name = "i2c-10EC5640:00", +		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF +						| SND_SOC_DAIFMT_CBS_CFS, +		.be_hw_params_fixup = byt_codec_fixup, +		.ignore_suspend = 1, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +		.ops = &byt_be_ssp2_ops, +	}, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_byt = { +	.name = "baytrailcraudio", +	.dai_link = byt_dailink, +	.num_links = ARRAY_SIZE(byt_dailink), +	.dapm_widgets = byt_dapm_widgets, +	.num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets), +	.dapm_routes = byt_audio_map, +	.num_dapm_routes = ARRAY_SIZE(byt_audio_map), +	.controls = byt_mc_controls, +	.num_controls = ARRAY_SIZE(byt_mc_controls), +}; + +static int snd_byt_mc_probe(struct platform_device *pdev) +{ +	int ret_val = 0; + +	/* register the soc card */ +	snd_soc_card_byt.dev = &pdev->dev; + +	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt); +	if (ret_val) { +		dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val); +		return ret_val; +	} +	platform_set_drvdata(pdev, &snd_soc_card_byt); +	return ret_val; +} + +static struct platform_driver snd_byt_mc_driver = { +	.driver = { +		.name = "bytt100_rt5640", +		.pm = &snd_soc_pm_ops, +	}, +	.probe = snd_byt_mc_probe, +}; + +module_platform_driver(snd_byt_mc_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); +MODULE_AUTHOR("Subhransu S. Prusty <[email protected]>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bytt100_rt5640"); diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c new file mode 100644 index 000000000000..20a28b22e30f --- /dev/null +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -0,0 +1,324 @@ +/* + *  cht-bsw-rt5645.c - ASoc Machine driver for Intel Cherryview-based platforms + *                     Cherrytrail and Braswell, with RT5645 codec. + * + *  Copyright (C) 2015 Intel Corp + *  Author: Fang, Yang A <[email protected]> + *	        N,Harshapriya <[email protected]> + *  This file is modified from cht_bsw_rt5672.c + *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; version 2 of the License. + * + *  This program is distributed in the hope that 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../../codecs/rt5645.h" +#include "../atom/sst-atom-controls.h" + +#define CHT_PLAT_CLK_3_HZ	19200000 +#define CHT_CODEC_DAI	"rt5645-aif1" + +struct cht_mc_private { +	struct snd_soc_jack hp_jack; +	struct snd_soc_jack mic_jack; +}; + +static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) +{ +	int i; + +	for (i = 0; i < card->num_rtd; i++) { +		struct snd_soc_pcm_runtime *rtd; + +		rtd = card->rtd + i; +		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, +			     strlen(CHT_CODEC_DAI))) +			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; +	int ret; + +	codec_dai = cht_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_OFF(event)) +		return 0; + +	/* Set codec sysclk source to its internal clock because codec PLL will +	 * be off when idle and MCLK will also be off by ACPI when codec is +	 * runtime suspended. Codec needs clock for jack detection and button +	 * press. +	 */ +	ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK, +			0, SND_SOC_CLOCK_IN); +	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 cht_dapm_widgets[] = { +	SND_SOC_DAPM_HP("Headphone", NULL), +	SND_SOC_DAPM_MIC("Headset Mic", NULL), +	SND_SOC_DAPM_MIC("Int Mic", NULL), +	SND_SOC_DAPM_SPK("Ext Spk", NULL), +	SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, +			platform_clock_control, SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route cht_audio_map[] = { +	{"IN1P", NULL, "Headset Mic"}, +	{"IN1N", NULL, "Headset Mic"}, +	{"DMIC L1", NULL, "Int Mic"}, +	{"DMIC R1", NULL, "Int Mic"}, +	{"Headphone", NULL, "HPOL"}, +	{"Headphone", NULL, "HPOR"}, +	{"Ext Spk", NULL, "SPOL"}, +	{"Ext Spk", NULL, "SPOR"}, +	{"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"}, +	{"Int Mic", NULL, "Platform Clock"}, +	{"Ext Spk", NULL, "Platform Clock"}, +}; + +static const struct snd_kcontrol_new cht_mc_controls[] = { +	SOC_DAPM_PIN_SWITCH("Headphone"), +	SOC_DAPM_PIN_SWITCH("Headset Mic"), +	SOC_DAPM_PIN_SWITCH("Int Mic"), +	SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static int cht_aif1_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; + +	/* set codec PLL source to the 19.2MHz platform clock (MCLK) */ +	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK, +				  CHT_PLAT_CLK_3_HZ, params_rate(params) * 512); +	if (ret < 0) { +		dev_err(rtd->dev, "can't set codec pll: %d\n", ret); +		return ret; +	} + +	ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1, +				params_rate(params) * 512, SND_SOC_CLOCK_IN); +	if (ret < 0) { +		dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); +		return ret; +	} + +	return 0; +} + +static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) +{ +	int ret; +	struct snd_soc_codec *codec = runtime->codec; +	struct snd_soc_dai *codec_dai = runtime->codec_dai; +	struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); + +	/* Select clk_i2s1_asrc as ASRC clock source */ +	rt5645_sel_asrc_clk_src(codec, +				RT5645_DA_STEREO_FILTER | +				RT5645_DA_MONO_L_FILTER | +				RT5645_DA_MONO_R_FILTER | +				RT5645_AD_STEREO_FILTER, +				RT5645_CLK_SEL_I2S1_ASRC); + +	/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ +	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); +	if (ret < 0) { +		dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); +		return ret; +	} + +	ret = snd_soc_card_jack_new(runtime->card, "Headphone Jack", +				    SND_JACK_HEADPHONE, &ctx->hp_jack, +				    NULL, 0); +	if (ret) { +		dev_err(runtime->dev, "HP jack creation failed %d\n", ret); +		return ret; +	} + +	ret = snd_soc_card_jack_new(runtime->card, "Mic Jack", +				    SND_JACK_MICROPHONE, &ctx->mic_jack, +				    NULL, 0); +	if (ret) { +		dev_err(runtime->dev, "Mic jack creation failed %d\n", ret); +		return ret; +	} + +	rt5645_set_jack_detect(codec, &ctx->hp_jack, &ctx->mic_jack); + +	return ret; +} + +static int cht_codec_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 DSP will covert the FE rate to 48k, stereo, 24bits */ +	rate->min = rate->max = 48000; +	channels->min = channels->max = 2; + +	/* set SSP2 to 24-bit */ +	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); +	return 0; +} + +static unsigned int rates_48000[] = { +	48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { +	.count = ARRAY_SIZE(rates_48000), +	.list  = rates_48000, +}; + +static int cht_aif1_startup(struct snd_pcm_substream *substream) +{ +	return snd_pcm_hw_constraint_list(substream->runtime, 0, +			SNDRV_PCM_HW_PARAM_RATE, +			&constraints_48000); +} + +static struct snd_soc_ops cht_aif1_ops = { +	.startup = cht_aif1_startup, +}; + +static struct snd_soc_ops cht_be_ssp2_ops = { +	.hw_params = cht_aif1_hw_params, +}; + +static struct snd_soc_dai_link cht_dailink[] = { +	[MERR_DPCM_AUDIO] = { +		.name = "Audio Port", +		.stream_name = "Audio", +		.cpu_dai_name = "media-cpu-dai", +		.codec_dai_name = "snd-soc-dummy-dai", +		.codec_name = "snd-soc-dummy", +		.platform_name = "sst-mfld-platform", +		.ignore_suspend = 1, +		.dynamic = 1, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +		.ops = &cht_aif1_ops, +	}, +	[MERR_DPCM_COMPR] = { +		.name = "Compressed Port", +		.stream_name = "Compress", +		.cpu_dai_name = "compress-cpu-dai", +		.codec_dai_name = "snd-soc-dummy-dai", +		.codec_name = "snd-soc-dummy", +		.platform_name = "sst-mfld-platform", +	}, +	/* CODEC<->CODEC link */ +	/* back ends */ +	{ +		.name = "SSP2-Codec", +		.be_id = 1, +		.cpu_dai_name = "ssp2-port", +		.platform_name = "sst-mfld-platform", +		.no_pcm = 1, +		.codec_dai_name = "rt5645-aif1", +		.codec_name = "i2c-10EC5645:00", +		.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF +					| SND_SOC_DAIFMT_CBS_CFS, +		.init = cht_codec_init, +		.be_hw_params_fixup = cht_codec_fixup, +		.ignore_suspend = 1, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +		.ops = &cht_be_ssp2_ops, +	}, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cht = { +	.name = "chtrt5645", +	.dai_link = cht_dailink, +	.num_links = ARRAY_SIZE(cht_dailink), +	.dapm_widgets = cht_dapm_widgets, +	.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), +	.dapm_routes = cht_audio_map, +	.num_dapm_routes = ARRAY_SIZE(cht_audio_map), +	.controls = cht_mc_controls, +	.num_controls = ARRAY_SIZE(cht_mc_controls), +}; + +static int snd_cht_mc_probe(struct platform_device *pdev) +{ +	int ret_val = 0; +	struct cht_mc_private *drv; + +	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); +	if (!drv) +		return -ENOMEM; + +	snd_soc_card_cht.dev = &pdev->dev; +	snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); +	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); +	if (ret_val) { +		dev_err(&pdev->dev, +			"snd_soc_register_card failed %d\n", ret_val); +		return ret_val; +	} +	platform_set_drvdata(pdev, &snd_soc_card_cht); +	return ret_val; +} + +static struct platform_driver snd_cht_mc_driver = { +	.driver = { +		.name = "cht-bsw-rt5645", +		.pm = &snd_soc_pm_ops, +	}, +	.probe = snd_cht_mc_probe, +}; + +module_platform_driver(snd_cht_mc_driver) + +MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver"); +MODULE_AUTHOR("Fang, Yang A,N,Harshapriya"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cht-bsw-rt5645"); diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c new file mode 100644 index 000000000000..2c9cc5be439e --- /dev/null +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -0,0 +1,366 @@ +/* + *  cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms + *                     Cherrytrail and Braswell, with RT5672 codec. + * + *  Copyright (C) 2014 Intel Corp + *  Author: Subhransu S. Prusty <[email protected]> + *          Mengdong Lin <[email protected]> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; version 2 of the License. + * + *  This program is distributed in the hope that 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. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../../codecs/rt5670.h" +#include "../atom/sst-atom-controls.h" + +/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ +#define CHT_PLAT_CLK_3_HZ	19200000 +#define CHT_CODEC_DAI	"rt5670-aif1" + +static struct snd_soc_jack cht_bsw_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin cht_bsw_headset_pins[] = { +	{ +		.pin = "Headset Mic", +		.mask = SND_JACK_MICROPHONE, +	}, +	{ +		.pin = "Headphone", +		.mask = SND_JACK_HEADPHONE, +	}, +}; + +static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) +{ +	int i; + +	for (i = 0; i < card->num_rtd; i++) { +		struct snd_soc_pcm_runtime *rtd; + +		rtd = card->rtd + i; +		if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, +			     strlen(CHT_CODEC_DAI))) +			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; +	int ret; + +	codec_dai = cht_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)) { +		/* set codec PLL source to the 19.2MHz platform clock (MCLK) */ +		ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK, +				CHT_PLAT_CLK_3_HZ, 48000 * 512); +		if (ret < 0) { +			dev_err(card->dev, "can't set codec pll: %d\n", ret); +			return ret; +		} + +		/* set codec sysclk source to PLL */ +		ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1, +			48000 * 512, SND_SOC_CLOCK_IN); +		if (ret < 0) { +			dev_err(card->dev, "can't set codec sysclk: %d\n", ret); +			return ret; +		} +	} else { +		/* Set codec sysclk source to its internal clock because codec +		 * PLL will be off when idle and MCLK will also be off by ACPI +		 * when codec is runtime suspended. Codec needs clock for jack +		 * detection and button press. +		 */ +		snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, +				       48000 * 512, SND_SOC_CLOCK_IN); +	} +	return 0; +} + +static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { +	SND_SOC_DAPM_HP("Headphone", NULL), +	SND_SOC_DAPM_MIC("Headset Mic", NULL), +	SND_SOC_DAPM_MIC("Int Mic", NULL), +	SND_SOC_DAPM_SPK("Ext Spk", 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 cht_audio_map[] = { +	{"IN1P", NULL, "Headset Mic"}, +	{"IN1N", NULL, "Headset Mic"}, +	{"DMIC L1", NULL, "Int Mic"}, +	{"DMIC R1", NULL, "Int Mic"}, +	{"Headphone", NULL, "HPOL"}, +	{"Headphone", NULL, "HPOR"}, +	{"Ext Spk", NULL, "SPOLP"}, +	{"Ext Spk", NULL, "SPOLN"}, +	{"Ext Spk", NULL, "SPORP"}, +	{"Ext Spk", NULL, "SPORN"}, +	{"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"}, +	{"Int Mic", NULL, "Platform Clock"}, +	{"Ext Spk", NULL, "Platform Clock"}, +}; + +static const struct snd_kcontrol_new cht_mc_controls[] = { +	SOC_DAPM_PIN_SWITCH("Headphone"), +	SOC_DAPM_PIN_SWITCH("Headset Mic"), +	SOC_DAPM_PIN_SWITCH("Int Mic"), +	SOC_DAPM_PIN_SWITCH("Ext Spk"), +}; + +static int cht_aif1_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; + +	/* set codec PLL source to the 19.2MHz platform clock (MCLK) */ +	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK, +				  CHT_PLAT_CLK_3_HZ, params_rate(params) * 512); +	if (ret < 0) { +		dev_err(rtd->dev, "can't set codec pll: %d\n", ret); +		return ret; +	} + +	/* set codec sysclk source to PLL */ +	ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1, +				     params_rate(params) * 512, +				     SND_SOC_CLOCK_IN); +	if (ret < 0) { +		dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); +		return ret; +	} +	return 0; +} + +static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) +{ +	int ret; +	struct snd_soc_dai *codec_dai = runtime->codec_dai; +	struct snd_soc_codec *codec = codec_dai->codec; + +	/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ +	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); +	if (ret < 0) { +		dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); +		return ret; +	} + +	/* Select codec ASRC clock source to track I2S1 clock, because codec +	 * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot +	 * be supported by RT5672. Otherwise, ASRC will be disabled and cause +	 * noise. +	 */ +	rt5670_sel_asrc_clk_src(codec, +				RT5670_DA_STEREO_FILTER +				| RT5670_DA_MONO_L_FILTER +				| RT5670_DA_MONO_R_FILTER +				| RT5670_AD_STEREO_FILTER +				| RT5670_AD_MONO_L_FILTER +				| RT5670_AD_MONO_R_FILTER, +				RT5670_CLK_SEL_I2S1_ASRC); + +        ret = snd_soc_card_jack_new(runtime->card, "Headset", +                SND_JACK_HEADSET | SND_JACK_BTN_0 | +                SND_JACK_BTN_1 | SND_JACK_BTN_2, &cht_bsw_headset, +                cht_bsw_headset_pins, ARRAY_SIZE(cht_bsw_headset_pins)); +        if (ret) +                return ret; + +	rt5670_set_jack_detect(codec, &cht_bsw_headset); +	return 0; +} + +static int cht_codec_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 DSP will covert the FE rate to 48k, stereo, 24bits */ +	rate->min = rate->max = 48000; +	channels->min = channels->max = 2; + +	/* set SSP2 to 24-bit */ +	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); +	return 0; +} + +static unsigned int rates_48000[] = { +	48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { +	.count = ARRAY_SIZE(rates_48000), +	.list  = rates_48000, +}; + +static int cht_aif1_startup(struct snd_pcm_substream *substream) +{ +	return snd_pcm_hw_constraint_list(substream->runtime, 0, +			SNDRV_PCM_HW_PARAM_RATE, +			&constraints_48000); +} + +static struct snd_soc_ops cht_aif1_ops = { +	.startup = cht_aif1_startup, +}; + +static struct snd_soc_ops cht_be_ssp2_ops = { +	.hw_params = cht_aif1_hw_params, +}; + +static struct snd_soc_dai_link cht_dailink[] = { +	/* Front End DAI links */ +	[MERR_DPCM_AUDIO] = { +		.name = "Audio Port", +		.stream_name = "Audio", +		.cpu_dai_name = "media-cpu-dai", +		.codec_dai_name = "snd-soc-dummy-dai", +		.codec_name = "snd-soc-dummy", +		.platform_name = "sst-mfld-platform", +		.nonatomic = true, +		.dynamic = 1, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +		.ops = &cht_aif1_ops, +	}, +	[MERR_DPCM_COMPR] = { +		.name = "Compressed Port", +		.stream_name = "Compress", +		.cpu_dai_name = "compress-cpu-dai", +		.codec_dai_name = "snd-soc-dummy-dai", +		.codec_name = "snd-soc-dummy", +		.platform_name = "sst-mfld-platform", +	}, + +	/* Back End DAI links */ +	{ +		/* SSP2 - Codec */ +		.name = "SSP2-Codec", +		.be_id = 1, +		.cpu_dai_name = "ssp2-port", +		.platform_name = "sst-mfld-platform", +		.no_pcm = 1, +		.nonatomic = true, +		.codec_dai_name = "rt5670-aif1", +		.codec_name = "i2c-10EC5670:00", +		.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF +					| SND_SOC_DAIFMT_CBS_CFS, +		.init = cht_codec_init, +		.be_hw_params_fixup = cht_codec_fixup, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +		.ops = &cht_be_ssp2_ops, +	}, +}; + +static int cht_suspend_pre(struct snd_soc_card *card) +{ +	struct snd_soc_codec *codec; + +	list_for_each_entry(codec, &card->codec_dev_list, card_list) { +		if (!strcmp(codec->component.name, "i2c-10EC5670:00")) { +			dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); +			rt5670_jack_suspend(codec); +			break; +		} +	} +	return 0; +} + +static int cht_resume_post(struct snd_soc_card *card) +{ +	struct snd_soc_codec *codec; + +	list_for_each_entry(codec, &card->codec_dev_list, card_list) { +		if (!strcmp(codec->component.name, "i2c-10EC5670:00")) { +			dev_dbg(codec->dev, "enabling jack detect for resume.\n"); +			rt5670_jack_resume(codec); +			break; +		} +	} + +	return 0; +} + +/* SoC card */ +static struct snd_soc_card snd_soc_card_cht = { +	.name = "cherrytrailcraudio", +	.dai_link = cht_dailink, +	.num_links = ARRAY_SIZE(cht_dailink), +	.dapm_widgets = cht_dapm_widgets, +	.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), +	.dapm_routes = cht_audio_map, +	.num_dapm_routes = ARRAY_SIZE(cht_audio_map), +	.controls = cht_mc_controls, +	.num_controls = ARRAY_SIZE(cht_mc_controls), +	.suspend_pre = cht_suspend_pre, +	.resume_post = cht_resume_post, +}; + +static int snd_cht_mc_probe(struct platform_device *pdev) +{ +	int ret_val = 0; + +	/* register the soc card */ +	snd_soc_card_cht.dev = &pdev->dev; +	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); +	if (ret_val) { +		dev_err(&pdev->dev, +			"snd_soc_register_card failed %d\n", ret_val); +		return ret_val; +	} +	platform_set_drvdata(pdev, &snd_soc_card_cht); +	return ret_val; +} + +static struct platform_driver snd_cht_mc_driver = { +	.driver = { +		.name = "cht-bsw-rt5672", +	}, +	.probe = snd_cht_mc_probe, +}; + +module_platform_driver(snd_cht_mc_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); +MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:cht-bsw-rt5672"); diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c new file mode 100644 index 000000000000..22558572cb9c --- /dev/null +++ b/sound/soc/intel/boards/haswell.c @@ -0,0 +1,209 @@ +/* + * Intel Haswell Lynxpoint SST Audio + * + * Copyright (C) 2013, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that 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. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "../common/sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" + +#include "../../codecs/rt5640.h" + +/* Haswell ULT platforms have a Headphone and Mic jack */ +static const struct snd_soc_dapm_widget haswell_widgets[] = { +	SND_SOC_DAPM_HP("Headphones", NULL), +	SND_SOC_DAPM_MIC("Mic", NULL), +}; + +static const struct snd_soc_dapm_route haswell_rt5640_map[] = { + +	{"Headphones", NULL, "HPOR"}, +	{"Headphones", NULL, "HPOL"}, +	{"IN2P", NULL, "Mic"}, + +	/* CODEC BE connections */ +	{"SSP0 CODEC IN", NULL, "AIF1 Capture"}, +	{"AIF1 Playback", NULL, "SSP0 CODEC OUT"}, +}; + +static int haswell_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 */ +	params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); +	return 0; +} + +static int haswell_rt5640_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, RT5640_SCLK_S_MCLK, 12288000, +		SND_SOC_CLOCK_IN); + +	if (ret < 0) { +		dev_err(rtd->dev, "can't set codec sysclk configuration\n"); +		return ret; +	} + +	/* set correct codec filter for DAI format and clock config */ +	snd_soc_update_bits(rtd->codec, 0x83, 0xffff, 0x8000); + +	return ret; +} + +static struct snd_soc_ops haswell_rt5640_ops = { +	.hw_params = haswell_rt5640_hw_params, +}; + +static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ +	struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev); +	struct sst_hsw *haswell = pdata->dsp; +	int ret; + +	/* Set ADSP SSP port settings */ +	ret = sst_hsw_device_set_config(haswell, 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, "failed to set device config\n"); +		return ret; +	} + +	return 0; +} + +static struct snd_soc_dai_link haswell_rt5640_dais[] = { +	/* Front End DAI links */ +	{ +		.name = "System", +		.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 = haswell_rtd_init, +		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +	}, +	{ +		.name = "Offload0", +		.stream_name = "Offload0 Playback", +		.cpu_dai_name = "Offload0 Pin", +		.platform_name = "haswell-pcm-audio", +		.dynamic = 1, +		.codec_name = "snd-soc-dummy", +		.codec_dai_name = "snd-soc-dummy-dai", +		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, +		.dpcm_playback = 1, +	}, +	{ +		.name = "Offload1", +		.stream_name = "Offload1 Playback", +		.cpu_dai_name = "Offload1 Pin", +		.platform_name = "haswell-pcm-audio", +		.dynamic = 1, +		.codec_name = "snd-soc-dummy", +		.codec_dai_name = "snd-soc-dummy-dai", +		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, +		.dpcm_playback = 1, +	}, +	{ +		.name = "Loopback", +		.stream_name = "Loopback", +		.cpu_dai_name = "Loopback Pin", +		.platform_name = "haswell-pcm-audio", +		.dynamic = 0, +		.codec_name = "snd-soc-dummy", +		.codec_dai_name = "snd-soc-dummy-dai", +		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, +		.dpcm_capture = 1, +	}, + +	/* Back End DAI links */ +	{ +		/* SSP0 - Codec */ +		.name = "Codec", +		.be_id = 0, +		.cpu_dai_name = "snd-soc-dummy-dai", +		.platform_name = "snd-soc-dummy", +		.no_pcm = 1, +		.codec_name = "i2c-INT33CA:00", +		.codec_dai_name = "rt5640-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 = haswell_ssp0_fixup, +		.ops = &haswell_rt5640_ops, +		.dpcm_playback = 1, +		.dpcm_capture = 1, +	}, +}; + +/* audio machine driver for Haswell Lynxpoint DSP + RT5640 */ +static struct snd_soc_card haswell_rt5640 = { +	.name = "haswell-rt5640", +	.owner = THIS_MODULE, +	.dai_link = haswell_rt5640_dais, +	.num_links = ARRAY_SIZE(haswell_rt5640_dais), +	.dapm_widgets = haswell_widgets, +	.num_dapm_widgets = ARRAY_SIZE(haswell_widgets), +	.dapm_routes = haswell_rt5640_map, +	.num_dapm_routes = ARRAY_SIZE(haswell_rt5640_map), +	.fully_routed = true, +}; + +static int haswell_audio_probe(struct platform_device *pdev) +{ +	haswell_rt5640.dev = &pdev->dev; + +	return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640); +} + +static struct platform_driver haswell_audio = { +	.probe = haswell_audio_probe, +	.driver = { +		.name = "haswell-audio", +	}, +}; + +module_platform_driver(haswell_audio) + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, Xingchao Wang"); +MODULE_DESCRIPTION("Intel SST Audio for Haswell Lynxpoint"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:haswell-audio"); diff --git a/sound/soc/intel/boards/mfld_machine.c b/sound/soc/intel/boards/mfld_machine.c new file mode 100644 index 000000000000..49c09a0add79 --- /dev/null +++ b/sound/soc/intel/boards/mfld_machine.c @@ -0,0 +1,430 @@ +/* + *  mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform + * + *  Copyright (C) 2010 Intel Corp + *  Author: Vinod Koul <[email protected]> + *  Author: Harsha Priya <[email protected]> + *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; version 2 of the License. + * + *  This program is distributed in the hope that 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, write to the Free Software Foundation, Inc., + *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../codecs/sn95031.h" + +#define MID_MONO 1 +#define MID_STEREO 2 +#define MID_MAX_CAP 5 +#define MFLD_JACK_INSERT 0x04 + +enum soc_mic_bias_zones { +	MFLD_MV_START = 0, +	/* mic bias volutage range for Headphones*/ +	MFLD_MV_HP = 400, +	/* mic bias volutage range for American Headset*/ +	MFLD_MV_AM_HS = 650, +	/* mic bias volutage range for Headset*/ +	MFLD_MV_HS = 2000, +	MFLD_MV_UNDEFINED, +}; + +static unsigned int	hs_switch; +static unsigned int	lo_dac; +static struct snd_soc_codec *mfld_codec; + +struct mfld_mc_private { +	void __iomem *int_base; +	u8 interrupt_status; +}; + +struct snd_soc_jack mfld_jack; + +/*Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin mfld_jack_pins[] = { +	{ +		.pin = "Headphones", +		.mask = SND_JACK_HEADPHONE, +	}, +	{ +		.pin = "AMIC1", +		.mask = SND_JACK_MICROPHONE, +	}, +}; + +/* jack detection voltage zones */ +static struct snd_soc_jack_zone mfld_zones[] = { +	{MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE}, +	{MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET}, +}; + +/* sound card controls */ +static const char *headset_switch_text[] = {"Earpiece", "Headset"}; + +static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"}; + +static const struct soc_enum headset_enum = +	SOC_ENUM_SINGLE_EXT(2, headset_switch_text); + +static const struct soc_enum lo_enum = +	SOC_ENUM_SINGLE_EXT(4, lo_text); + +static int headset_get_switch(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_value *ucontrol) +{ +	ucontrol->value.integer.value[0] = hs_switch; +	return 0; +} + +static int headset_set_switch(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); +	struct snd_soc_dapm_context *dapm = &card->dapm; + +	if (ucontrol->value.integer.value[0] == hs_switch) +		return 0; + +	snd_soc_dapm_mutex_lock(dapm); + +	if (ucontrol->value.integer.value[0]) { +		pr_debug("hs_set HS path\n"); +		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); +	} else { +		pr_debug("hs_set EP path\n"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); +		snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT"); +	} + +	snd_soc_dapm_sync_unlocked(dapm); + +	snd_soc_dapm_mutex_unlock(dapm); + +	hs_switch = ucontrol->value.integer.value[0]; + +	return 0; +} + +static void lo_enable_out_pins(struct snd_soc_dapm_context *dapm) +{ +	snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTL"); +	snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTR"); +	snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTL"); +	snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTR"); +	snd_soc_dapm_enable_pin_unlocked(dapm, "VIB1OUT"); +	snd_soc_dapm_enable_pin_unlocked(dapm, "VIB2OUT"); +	if (hs_switch) { +		snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); +	} else { +		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); +		snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT"); +	} +} + +static int lo_get_switch(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_value *ucontrol) +{ +	ucontrol->value.integer.value[0] = lo_dac; +	return 0; +} + +static int lo_set_switch(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); +	struct snd_soc_dapm_context *dapm = &card->dapm; + +	if (ucontrol->value.integer.value[0] == lo_dac) +		return 0; + +	snd_soc_dapm_mutex_lock(dapm); + +	/* we dont want to work with last state of lineout so just enable all +	 * pins and then disable pins not required +	 */ +	lo_enable_out_pins(dapm); + +	switch (ucontrol->value.integer.value[0]) { +	case 0: +		pr_debug("set vibra path\n"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "VIB2OUT"); +		snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0); +		break; + +	case 1: +		pr_debug("set hs  path\n"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT"); +		snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x22); +		break; + +	case 2: +		pr_debug("set spkr path\n"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTL"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTR"); +		snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x44); +		break; + +	case 3: +		pr_debug("set null path\n"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTL"); +		snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTR"); +		snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x66); +		break; +	} + +	snd_soc_dapm_sync_unlocked(dapm); + +	snd_soc_dapm_mutex_unlock(dapm); + +	lo_dac = ucontrol->value.integer.value[0]; +	return 0; +} + +static const struct snd_kcontrol_new mfld_snd_controls[] = { +	SOC_ENUM_EXT("Playback Switch", headset_enum, +			headset_get_switch, headset_set_switch), +	SOC_ENUM_EXT("Lineout Mux", lo_enum, +			lo_get_switch, lo_set_switch), +}; + +static const struct snd_soc_dapm_widget mfld_widgets[] = { +	SND_SOC_DAPM_HP("Headphones", NULL), +	SND_SOC_DAPM_MIC("Mic", NULL), +}; + +static const struct snd_soc_dapm_route mfld_map[] = { +	{"Headphones", NULL, "HPOUTR"}, +	{"Headphones", NULL, "HPOUTL"}, +	{"Mic", NULL, "AMIC1"}, +}; + +static void mfld_jack_check(unsigned int intr_status) +{ +	struct mfld_jack_data jack_data; + +	if (!mfld_codec) +		return; + +	jack_data.mfld_jack = &mfld_jack; +	jack_data.intr_id = intr_status; + +	sn95031_jack_detection(mfld_codec, &jack_data); +	/* TODO: add american headset detection post gpiolib support */ +} + +static int mfld_init(struct snd_soc_pcm_runtime *runtime) +{ +	struct snd_soc_dapm_context *dapm = &runtime->card->dapm; +	int ret_val; + +	/* default is earpiece pin, userspace sets it explcitly */ +	snd_soc_dapm_disable_pin(dapm, "Headphones"); +	/* default is lineout NC, userspace sets it explcitly */ +	snd_soc_dapm_disable_pin(dapm, "LINEOUTL"); +	snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); +	lo_dac = 3; +	hs_switch = 0; +	/* we dont use linein in this so set to NC */ +	snd_soc_dapm_disable_pin(dapm, "LINEINL"); +	snd_soc_dapm_disable_pin(dapm, "LINEINR"); + +	/* Headset and button jack detection */ +	ret_val = snd_soc_card_jack_new(runtime->card, +			"Intel(R) MID Audio Jack", SND_JACK_HEADSET | +			SND_JACK_BTN_0 | SND_JACK_BTN_1, &mfld_jack, +			mfld_jack_pins, ARRAY_SIZE(mfld_jack_pins)); +	if (ret_val) { +		pr_err("jack creation failed\n"); +		return ret_val; +	} + +	ret_val = snd_soc_jack_add_zones(&mfld_jack, +			ARRAY_SIZE(mfld_zones), mfld_zones); +	if (ret_val) { +		pr_err("adding jack zones failed\n"); +		return ret_val; +	} + +	mfld_codec = runtime->codec; + +	/* we want to check if anything is inserted at boot, +	 * so send a fake event to codec and it will read adc +	 * to find if anything is there or not */ +	mfld_jack_check(MFLD_JACK_INSERT); +	return ret_val; +} + +static struct snd_soc_dai_link mfld_msic_dailink[] = { +	{ +		.name = "Medfield Headset", +		.stream_name = "Headset", +		.cpu_dai_name = "Headset-cpu-dai", +		.codec_dai_name = "SN95031 Headset", +		.codec_name = "sn95031", +		.platform_name = "sst-platform", +		.init = mfld_init, +	}, +	{ +		.name = "Medfield Speaker", +		.stream_name = "Speaker", +		.cpu_dai_name = "Speaker-cpu-dai", +		.codec_dai_name = "SN95031 Speaker", +		.codec_name = "sn95031", +		.platform_name = "sst-platform", +		.init = NULL, +	}, +	{ +		.name = "Medfield Vibra", +		.stream_name = "Vibra1", +		.cpu_dai_name = "Vibra1-cpu-dai", +		.codec_dai_name = "SN95031 Vibra1", +		.codec_name = "sn95031", +		.platform_name = "sst-platform", +		.init = NULL, +	}, +	{ +		.name = "Medfield Haptics", +		.stream_name = "Vibra2", +		.cpu_dai_name = "Vibra2-cpu-dai", +		.codec_dai_name = "SN95031 Vibra2", +		.codec_name = "sn95031", +		.platform_name = "sst-platform", +		.init = NULL, +	}, +	{ +		.name = "Medfield Compress", +		.stream_name = "Speaker", +		.cpu_dai_name = "Compress-cpu-dai", +		.codec_dai_name = "SN95031 Speaker", +		.codec_name = "sn95031", +		.platform_name = "sst-platform", +		.init = NULL, +	}, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_mfld = { +	.name = "medfield_audio", +	.owner = THIS_MODULE, +	.dai_link = mfld_msic_dailink, +	.num_links = ARRAY_SIZE(mfld_msic_dailink), + +	.controls = mfld_snd_controls, +	.num_controls = ARRAY_SIZE(mfld_snd_controls), +	.dapm_widgets = mfld_widgets, +	.num_dapm_widgets = ARRAY_SIZE(mfld_widgets), +	.dapm_routes = mfld_map, +	.num_dapm_routes = ARRAY_SIZE(mfld_map), +}; + +static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev) +{ +	struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev; + +	memcpy_fromio(&mc_private->interrupt_status, +			((void *)(mc_private->int_base)), +			sizeof(u8)); +	return IRQ_WAKE_THREAD; +} + +static irqreturn_t snd_mfld_jack_detection(int irq, void *data) +{ +	struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; + +	mfld_jack_check(mc_drv_ctx->interrupt_status); + +	return IRQ_HANDLED; +} + +static int snd_mfld_mc_probe(struct platform_device *pdev) +{ +	int ret_val = 0, irq; +	struct mfld_mc_private *mc_drv_ctx; +	struct resource *irq_mem; + +	pr_debug("snd_mfld_mc_probe called\n"); + +	/* retrive the irq number */ +	irq = platform_get_irq(pdev, 0); + +	/* audio interrupt base of SRAM location where +	 * interrupts are stored by System FW */ +	mc_drv_ctx = devm_kzalloc(&pdev->dev, sizeof(*mc_drv_ctx), GFP_ATOMIC); +	if (!mc_drv_ctx) { +		pr_err("allocation failed\n"); +		return -ENOMEM; +	} + +	irq_mem = platform_get_resource_byname( +				pdev, IORESOURCE_MEM, "IRQ_BASE"); +	if (!irq_mem) { +		pr_err("no mem resource given\n"); +		return -ENODEV; +	} +	mc_drv_ctx->int_base = devm_ioremap_nocache(&pdev->dev, irq_mem->start, +						    resource_size(irq_mem)); +	if (!mc_drv_ctx->int_base) { +		pr_err("Mapping of cache failed\n"); +		return -ENOMEM; +	} +	/* register for interrupt */ +	ret_val = devm_request_threaded_irq(&pdev->dev, irq, +			snd_mfld_jack_intr_handler, +			snd_mfld_jack_detection, +			IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx); +	if (ret_val) { +		pr_err("cannot register IRQ\n"); +		return ret_val; +	} +	/* register the soc card */ +	snd_soc_card_mfld.dev = &pdev->dev; +	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mfld); +	if (ret_val) { +		pr_debug("snd_soc_register_card failed %d\n", ret_val); +		return ret_val; +	} +	platform_set_drvdata(pdev, mc_drv_ctx); +	pr_debug("successfully exited probe\n"); +	return 0; +} + +static struct platform_driver snd_mfld_mc_driver = { +	.driver = { +		.name = "msic_audio", +	}, +	.probe = snd_mfld_mc_probe, +}; + +module_platform_driver(snd_mfld_mc_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver"); +MODULE_AUTHOR("Vinod Koul <[email protected]>"); +MODULE_AUTHOR("Harsha Priya <[email protected]>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:msic-audio"); |