ASoC: Intel: Add machine driver for BDW + RT5640

Still WiP but good enough for testing today.

Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index 4ec5100..8e9cab7 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -38,6 +38,16 @@
 	  Say Y or m if you have such a device.
 	  If unsure select "N".
 
+config SND_SOC_INTEL_BDW_RT5640_MACH
+	tristate "Broadwell with RT5640 codec"
+	depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM && GPIOLIB
+	select SND_SOC_RT5640
+	help
+	  This adds support for Intel Broadwell platform based boards with
+	  the RT5640 audio codec. This is a recommended option.
+	  Say Y or m if you have such a device.
+	  If unsure select "N".
+
 config SND_SOC_INTEL_BROADWELL_MACH
 	tristate "Broadwell Wildcatpoint"
 	depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index 69d2dfa..0698336 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -3,6 +3,7 @@
 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-bdw-rt5640-mach-objs := bdw-rt5640.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
@@ -27,6 +28,7 @@
 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_BDW_RT5640_MACH) += snd-soc-sst-bdw-rt5640-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-rt5640.c b/sound/soc/intel/boards/bdw-rt5640.c
new file mode 100644
index 0000000..d29bda6
--- /dev/null
+++ b/sound/soc/intel/boards/bdw-rt5640.c
@@ -0,0 +1,216 @@
+/*
+ * 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 <linux/dmi.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/rt5640.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 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 = 48000;
+	rate->max = 48000;
+	channels->min = 2;
+	channels->max = 2;
+
+	/* set SSP0 to 16 bit */
+	params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+	return 0;
+}
+
+static int broadwell_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 const struct snd_soc_ops broadwell_rt5640_ops = {
+	.hw_params = broadwell_rt5640_hw_params,
+};
+
+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;
+	}
+
+	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;
+}
+
+/* broadwell digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link broadwell_rt5640_dais[] = {
+	{
+		.name = "Codec",
+		.stream_name = "Audio",
+		.cpu_dai_name = "haswell-pcm-audio",
+		.codec_dai_name = "rt5640-aif1",
+		.codec_name = "i2c-INT33CA:00",
+		.platform_name = "haswell-pcm-audio",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			   SND_SOC_DAIFMT_CBS_CFS,
+		.init = byt_rt5640_init,
+		.ops = &broadwell_rt5640_ops,
+		.be_hw_params_fixup = broadwell_ssp0_fixup,
+	},
+};
+
+/* broadwell audio machine driver for WPT + RT286S */
+static struct snd_soc_card broadwell_rt5640 = {
+	.name = "bdw-rt5640",
+	.owner = THIS_MODULE,
+	.dai_link = broadwell_rt5640_dais,
+	.num_links = ARRAY_SIZE(broadwell_rt5640_dais),
+	.controls = byt_rt5640_controls,
+	.num_controls = ARRAY_SIZE(byt_rt5640_controls),
+	.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 broadwell_audio_probe(struct platform_device *pdev)
+{
+	broadwell_rt5640.dev = &pdev->dev;
+	return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt5640);
+}
+
+static struct platform_driver broadwell_audio = {
+	.probe = broadwell_audio_probe,
+	.driver = {
+		.name = "bdw-rt5640",
+	},
+};
+
+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/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c
index e0e8c8c..6e250a3 100644
--- a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c
@@ -50,7 +50,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = {
 	},
 	{
 		.id = "INT33CA",
-		.drv_name = "haswell-audio",
+		.drv_name = "bdw-rt5640",
 		.fw_filename = "intel/IntcSST2.bin",
 		.sof_fw_filename = "intel/reef-bdw.ri",
 		.sof_tplg_filename = "intel/reef-bdw-rt5640.tplg",