diff mbox series

[v2] ASoC: Intel: kbl_rt5660: Add a new machine driver for kbl with rt5660

Message ID 20181208024814.17843-1-hui.wang@canonical.com (mailing list archive)
State New, archived
Headers show
Series [v2] ASoC: Intel: kbl_rt5660: Add a new machine driver for kbl with rt5660 | expand

Commit Message

Hui Wang Dec. 8, 2018, 2:48 a.m. UTC
The new Dell IoT platform uses kabylake + alc3277 codec, and alc3277
shares the driver with the codec rt5660, here we generate a new
machine driver based on kbl_da7219_max98357a.

The audio design on this IoT platform is as below:
 - Intel kabylake platform
 - connect the codec ALC3277 via SSP0
 - line-out and line-in with Micbias jacks
 - line-out mute control and jack detection of line-out and line-in
 - two HDMI ports with audio capability

Signed-off-by: Hui Wang <hui.wang@canonical.com>
---
 sound/soc/intel/boards/Kconfig                |  10 +
 sound/soc/intel/boards/Makefile               |   2 +
 sound/soc/intel/boards/kbl_rt5660.c           | 536 ++++++++++++++++++
 .../intel/common/soc-acpi-intel-kbl-match.c   |   5 +
 4 files changed, 553 insertions(+)
 create mode 100644 sound/soc/intel/boards/kbl_rt5660.c

Comments

Hui Wang Dec. 11, 2018, 1:03 p.m. UTC | #1
Hello Pierre-louis,

Please take a look at this patch when you have time. :-)

Thanks,
Hui.

On 2018/12/8 上午10:48, Hui Wang wrote:
> The new Dell IoT platform uses kabylake + alc3277 codec, and alc3277
> shares the driver with the codec rt5660, here we generate a new
> machine driver based on kbl_da7219_max98357a.
>
> The audio design on this IoT platform is as below:
>   - Intel kabylake platform
>   - connect the codec ALC3277 via SSP0
>   - line-out and line-in with Micbias jacks
>   - line-out mute control and jack detection of line-out and line-in
>   - two HDMI ports with audio capability
>
> Signed-off-by: Hui Wang <hui.wang@canonical.com>
Pierre-Louis Bossart Dec. 11, 2018, 2:30 p.m. UTC | #2
Hi Hui,

Looks mostly good, couple of comments below. I didn't look at the GPIO 
parts since I am not familiar with the framework.

Thanks

-Pierre


> +++ b/sound/soc/intel/boards/kbl_rt5660.c
> @@ -0,0 +1,536 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright(c) 2018-19 Intel Corporation.

Not sure this is correct since you did the work?

> +static const struct snd_kcontrol_new kabylake_rt5660_controls[] = {
> +	SOC_DAPM_PIN_SWITCH("Line In"),
> +	SOC_DAPM_PIN_SWITCH("Line Out"),
> +};
> +
> +static const struct snd_soc_dapm_widget kabylake_rt5660_widgets[] = {
> +	SND_SOC_DAPM_MIC("Line In", NULL),
> +	SND_SOC_DAPM_LINE("Line Out", kabylake_5660_event_lineout),
> +	SND_SOC_DAPM_SPK("DP", NULL),
> +	SND_SOC_DAPM_SPK("HDMI", NULL),

I see in other machine drivers that when this DP stuff is used there is 
also a matching route which you don't have.

kbl_da7219_max98357a.c:94:    SND_SOC_DAPM_SPK("DP", NULL),
kbl_da7219_max98357a.c:113:    { "DP", NULL, "hif6 Output" },
kbl_da7219_max98927.c:109:    SND_SOC_DAPM_SPK("DP", NULL),
kbl_da7219_max98927.c:125:    { "DP", NULL, "hif6 Output" },
kbl_rt5663_max98927.c:205:    SND_SOC_DAPM_SPK("DP", NULL),
kbl_rt5663_max98927.c:223:    { "DP", NULL, "hif6 Output" },

I never noticed this in Intel machine drivers, yet another weird thing 
to reverse engineer...I have no idea why it was modeled this way, the 
interface at the DSP level does not know or need to care what the actual 
link is, it first goes over an iDISP/HDAudio link.

Naveen, any idea on this?

> +};
> +
> +static const struct snd_soc_dapm_route kabylake_rt5660_map[] = {
> +	/* other jacks */
> +	{"IN1P", NULL, "Line In"},
> +	{"IN2P", NULL, "Line In"},
> +	{"Line Out", NULL, "LOUTR"},
> +	{"Line Out", NULL, "LOUTL"},
> +
> +	/* CODEC BE connections */
> +	{ "AIF1 Playback", NULL, "ssp0 Tx"},
> +	{ "ssp0 Tx", NULL, "codec0_out"},
> +
> +	{ "codec0_in", NULL, "ssp0 Rx" },
> +	{ "ssp0 Rx", NULL, "AIF1 Capture" },
> +
> +	{ "hifi2", NULL, "iDisp2 Tx"},
> +	{ "iDisp2 Tx", NULL, "iDisp2_out"},
> +	{ "hifi1", NULL, "iDisp1 Tx"},
> +	{ "iDisp1 Tx", NULL, "iDisp1_out"},
aren't you missing the routes for the HDMI3 which are listed in the rest 
of the patch?
> +
> +static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd)
> +{
> +	int ret;
> +	struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card);
> +	struct snd_soc_component *component = rtd->codec_dai->component;
> +	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
> +
> +	ret = devm_acpi_dev_add_driver_gpios(component->dev, acpi_rt5660_gpios);
> +	if (ret)
> +		dev_warn(component->dev, "Failed to add driver gpios\n");
> +
> +	/* Request rt5660 GPIO for lineout mute control */
> +	ctx->gpio_lo_mute = devm_gpiod_get(component->dev, "lineout-mute",
> +			GPIOD_OUT_HIGH);
> +	if (IS_ERR(ctx->gpio_lo_mute)) {
> +		dev_err(component->dev, "Can't find GPIO_MUTE# gpio\n");
> +		return PTR_ERR(ctx->gpio_lo_mute);
> +	}
Here you exit with an error, but see [1] below
> +
> +	/* Create and initialize headphone jack */
> +	if (!snd_soc_card_jack_new(rtd->card, "Lineout Jack",
> +			SND_JACK_LINEOUT, &lineout_jack,
> +			&lineout_jack_pin, 1)) {
> +		lineout_jack_gpio.gpiod_dev = component->dev;
> +		if (snd_soc_jack_add_gpios(&lineout_jack, 1,
> +				&lineout_jack_gpio))
> +			dev_err(component->dev, "Can't add Lineout jack gpio\n");
> +	} else
> +		dev_err(component->dev, "Can't create Lineout jack\n");

the tests for snd_soc_jack_new are odd. It's more usual to see

ret = snd_soc_card_jack_new();

if (ret){

     dev_err(blah)

     return ret;

}

I guess you took as example bdw-rt5677 but it's the only one following 
the pattern you used.

> +
> +	/* 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 = component->dev;
> +		if (snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio))
> +			dev_err(component->dev, "Can't add mic jack gpio\n");
> +	} else
> +		dev_err(component->dev, "Can't create mic jack\n");

[1] ... for both of the jacks you don't exit. Is this intentional?

> +
> +	snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
> +	snd_soc_dapm_force_enable_pin(dapm, "BST1");
> +	snd_soc_dapm_force_enable_pin(dapm, "BST2");

Are those initializations required?

> +
> +	return 0;
> +}
>
> +static int kbl_fe_startup(struct snd_pcm_substream *substream)
> +{
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +
> +	/*
> +	 * On this platform for PCM device we support,
> +	 * 48Khz
> +	 * stereo
> +	 * 16 bit audio
> +	 */
> +
> +	runtime->hw.channels_max = DUAL_CHANNEL;
> +	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
> +					   &constraints_channels);
> +
> +	runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
> +	snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
that part is odd, the SSP is configured to work with 24 bits but somehow 
it's constrained here to 16 bits. it must be copy/paste from other 
machine drivers, but not sure if this is required here.
> --- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
> +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
> @@ -96,6 +96,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = {
>   		.quirk_data = &kbl_7219_98927_codecs,
>   		.pdata = &skl_dmic_data
>   	},
> +	{
> +		.id = "10EC3277",
> +		.drv_name = "kbl_rt5660",
> +		.fw_filename = "intel/dsp_fw_kbl.bin",
can we also add an entry for 10EC5660 since it's already a known ACPI ID?
Hui Wang Dec. 12, 2018, 2:15 a.m. UTC | #3
On 2018/12/11 下午10:30, Pierre-Louis Bossart wrote:
> Hi Hui,
>
> Looks mostly good, couple of comments below. I didn't look at the GPIO 
> parts since I am not familiar with the framework.

For GPIO part, I mainly referred to the bdw-rt5677.c, and so far this 
part works perfectly on the Dell IoT machine.

Thank you for reviewing my patch, please see my reply below.

Thanks.

>
> Thanks
>
> -Pierre
>
>
>> +++ b/sound/soc/intel/boards/kbl_rt5660.c
>> @@ -0,0 +1,536 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright(c) 2018-19 Intel Corporation.
>
> Not sure this is correct since you did the work?
will change to the Canonical Corporation.
>
>> +static const struct snd_kcontrol_new kabylake_rt5660_controls[] = {
>> +    SOC_DAPM_PIN_SWITCH("Line In"),
>> +    SOC_DAPM_PIN_SWITCH("Line Out"),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget kabylake_rt5660_widgets[] = {
>> +    SND_SOC_DAPM_MIC("Line In", NULL),
>> +    SND_SOC_DAPM_LINE("Line Out", kabylake_5660_event_lineout),
>> +    SND_SOC_DAPM_SPK("DP", NULL),
>> +    SND_SOC_DAPM_SPK("HDMI", NULL),
>
> I see in other machine drivers that when this DP stuff is used there 
> is also a matching route which you don't have.
>
> kbl_da7219_max98357a.c:94:    SND_SOC_DAPM_SPK("DP", NULL),
> kbl_da7219_max98357a.c:113:    { "DP", NULL, "hif6 Output" },
> kbl_da7219_max98927.c:109:    SND_SOC_DAPM_SPK("DP", NULL),
> kbl_da7219_max98927.c:125:    { "DP", NULL, "hif6 Output" },
> kbl_rt5663_max98927.c:205:    SND_SOC_DAPM_SPK("DP", NULL),
> kbl_rt5663_max98927.c:223:    { "DP", NULL, "hif6 Output" },

Originally I copied the matching route like { "DP", NULL, "hif5/6 
Output" }, but the kernel will output a warning that there is no dapm 
named "hif5" or "hif6", So I dropped it.

And looks like I should drop SND_SOC_DAPM_SPK("DP", NULL) and 
SND_SOC_DAPM_SPK("HDMI", NULL) too, since it is useless at all for this 
machine.

>
> I never noticed this in Intel machine drivers, yet another weird thing 
> to reverse engineer...I have no idea why it was modeled this way, the 
> interface at the DSP level does not know or need to care what the 
> actual link is, it first goes over an iDISP/HDAudio link.
>
> Naveen, any idea on this?
>
>> +};
>> +
>> +static const struct snd_soc_dapm_route kabylake_rt5660_map[] = {
>> +    /* other jacks */
>> +    {"IN1P", NULL, "Line In"},
>> +    {"IN2P", NULL, "Line In"},
>> +    {"Line Out", NULL, "LOUTR"},
>> +    {"Line Out", NULL, "LOUTL"},
>> +
>> +    /* CODEC BE connections */
>> +    { "AIF1 Playback", NULL, "ssp0 Tx"},
>> +    { "ssp0 Tx", NULL, "codec0_out"},
>> +
>> +    { "codec0_in", NULL, "ssp0 Rx" },
>> +    { "ssp0 Rx", NULL, "AIF1 Capture" },
>> +
>> +    { "hifi2", NULL, "iDisp2 Tx"},
>> +    { "iDisp2 Tx", NULL, "iDisp2_out"},
>> +    { "hifi1", NULL, "iDisp1 Tx"},
>> +    { "iDisp1 Tx", NULL, "iDisp1_out"},
> aren't you missing the routes for the HDMI3 which are listed in the 
> rest of the patch?
Ok, will add the HDMI3 in the next version.
>> +
>> +static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd)
>> +{
>> +    int ret;
>> +    struct kbl_codec_private *ctx = 
>> snd_soc_card_get_drvdata(rtd->card);
>> +    struct snd_soc_component *component = rtd->codec_dai->component;
>> +    struct snd_soc_dapm_context *dapm = 
>> snd_soc_component_get_dapm(component);
>> +
>> +    ret = devm_acpi_dev_add_driver_gpios(component->dev, 
>> acpi_rt5660_gpios);
>> +    if (ret)
>> +        dev_warn(component->dev, "Failed to add driver gpios\n");
>> +
>> +    /* Request rt5660 GPIO for lineout mute control */
>> +    ctx->gpio_lo_mute = devm_gpiod_get(component->dev, "lineout-mute",
>> +            GPIOD_OUT_HIGH);
>> +    if (IS_ERR(ctx->gpio_lo_mute)) {
>> +        dev_err(component->dev, "Can't find GPIO_MUTE# gpio\n");
>> +        return PTR_ERR(ctx->gpio_lo_mute);
>> +    }
> Here you exit with an error, but see [1] below
>> +
>> +    /* Create and initialize headphone jack */
>> +    if (!snd_soc_card_jack_new(rtd->card, "Lineout Jack",
>> +            SND_JACK_LINEOUT, &lineout_jack,
>> +            &lineout_jack_pin, 1)) {
>> +        lineout_jack_gpio.gpiod_dev = component->dev;
>> +        if (snd_soc_jack_add_gpios(&lineout_jack, 1,
>> +                &lineout_jack_gpio))
>> +            dev_err(component->dev, "Can't add Lineout jack gpio\n");
>> +    } else
>> +        dev_err(component->dev, "Can't create Lineout jack\n");
>
> the tests for snd_soc_jack_new are odd. It's more usual to see
>
> ret = snd_soc_card_jack_new();
>
> if (ret){
>
>     dev_err(blah)
>
>     return ret;
>
> }
>
> I guess you took as example bdw-rt5677 but it's the only one following 
> the pattern you used.
Will change this part as suggested.
>
>> +
>> +    /* 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 = component->dev;
>> +        if (snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio))
>> +            dev_err(component->dev, "Can't add mic jack gpio\n");
>> +    } else
>> +        dev_err(component->dev, "Can't create mic jack\n");
>
> [1] ... for both of the jacks you don't exit. Is this intentional?

Yes, it is intentional.

The gpio_lo_mute is mandatory, otherwise this driver can't play any 
sound via lineout jack since the jack is muted in hardware level. So if 
we can't get this gpio, the driver will return an error.

But the Jack and the GPIO of Jack detection are not mandatory, even jack 
or jack detection is failed to register, we can still use aplay/arecord 
to work.  And the version 0x00 of the Dell IoT machine doesn't have jack 
detection GPIOs,  to support both the version 0x00 and 0x01 boards,  we 
don't let it return here intentionally.

>
>> +
>> +    snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
>> +    snd_soc_dapm_force_enable_pin(dapm, "BST1");
>> +    snd_soc_dapm_force_enable_pin(dapm, "BST2");
>
> Are those initializations required?

Yes, they are required. Without enabling these dapms in advance, there 
will be 1~2 seconds noise at the beginning of the recorded sound.

I will add a comment here in the next version.

>
>> +
>> +    return 0;
>> +}
>>
>> +static int kbl_fe_startup(struct snd_pcm_substream *substream)
>> +{
>> +    struct snd_pcm_runtime *runtime = substream->runtime;
>> +
>> +    /*
>> +     * On this platform for PCM device we support,
>> +     * 48Khz
>> +     * stereo
>> +     * 16 bit audio
>> +     */
>> +
>> +    runtime->hw.channels_max = DUAL_CHANNEL;
>> +    snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
>> +                       &constraints_channels);
>> +
>> +    runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
>> +    snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
> that part is odd, the SSP is configured to work with 24 bits but 
> somehow it's constrained here to 16 bits. it must be copy/paste from 
> other machine drivers, but not sure if this is required here.
OK, will set to be 24bits here and do a test on the machine.
>> --- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
>> +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
>> @@ -96,6 +96,11 @@ struct snd_soc_acpi_mach 
>> snd_soc_acpi_intel_kbl_machines[] = {
>>           .quirk_data = &kbl_7219_98927_codecs,
>>           .pdata = &skl_dmic_data
>>       },
>> +    {
>> +        .id = "10EC3277",
>> +        .drv_name = "kbl_rt5660",
>> +        .fw_filename = "intel/dsp_fw_kbl.bin",
> can we also add an entry for 10EC5660 since it's already a known ACPI ID?
OK, will add it.
>
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
diff mbox series

Patch

diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index b177db2a0dbb..3839d6205fcf 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -293,6 +293,16 @@  config SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH
 	  Say Y if you have such a device.
 	  If unsure select "N".
 
+config SND_SOC_INTEL_KBL_RT5660_MACH
+	tristate "KBL with RT5660 in I2S Mode"
+	depends on MFD_INTEL_LPSS && I2C && ACPI
+	select SND_SOC_RT5660
+	select SND_SOC_HDAC_HDMI
+	help
+	  This adds support for ASoC Onboard Codec I2S machine driver. This will
+	  create an alsa sound card for RT5660 I2S audio codec.
+	  Say Y if you have such a device.
+
 config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH
 	tristate "GLK with RT5682 and MAX98357A in I2S Mode"
 	depends on MFD_INTEL_LPSS && I2C && ACPI
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index 5381e27df9cc..bf072ea299b7 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -20,6 +20,7 @@  snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o
 snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o
 snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o
 snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o
+snd-soc-kbl_rt5660-objs := kbl_rt5660.o
 snd-soc-skl_rt286-objs := skl_rt286.o
 snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o
 snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o
@@ -46,6 +47,7 @@  obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH) += snd-soc-kbl_da7219_max9
 obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH) += snd-soc-kbl_da7219_max98927.o
 obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH) += snd-soc-kbl_rt5663_max98927.o
 obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt5663_rt5514_max98927.o
+obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5660_MACH) += snd-soc-kbl_rt5660.o
 obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
 obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o
 obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o
diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c
new file mode 100644
index 000000000000..0482e6a7fc2a
--- /dev/null
+++ b/sound/soc/intel/boards/kbl_rt5660.c
@@ -0,0 +1,536 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2018-19 Intel Corporation.
+
+/*
+ * Intel Kabylake I2S Machine Driver with RT5660 Codec
+ *
+ * Modified from:
+ *   Intel Kabylake I2S Machine driver supporting MAXIM98357a and
+ *   DA7219 codecs
+ * Also referred to:
+ *   Intel Broadwell I2S Machine driver supporting RT5677 codec
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../../codecs/hdac_hdmi.h"
+#include "../../codecs/rt5660.h"
+
+#define KBL_RT5660_CODEC_DAI "rt5660-aif1"
+#define DUAL_CHANNEL 2
+
+static struct snd_soc_card *kabylake_audio_card;
+static struct snd_soc_jack skylake_hdmi[3];
+static struct snd_soc_jack lineout_jack;
+static struct snd_soc_jack mic_jack;
+
+struct kbl_hdmi_pcm {
+	struct list_head head;
+	struct snd_soc_dai *codec_dai;
+	int device;
+};
+
+struct kbl_codec_private {
+	struct gpio_desc *gpio_lo_mute;
+	struct list_head hdmi_pcm_list;
+};
+
+enum {
+	KBL_DPCM_AUDIO_PB = 0,
+	KBL_DPCM_AUDIO_CP,
+	KBL_DPCM_AUDIO_HDMI1_PB,
+	KBL_DPCM_AUDIO_HDMI2_PB,
+	KBL_DPCM_AUDIO_HDMI3_PB,
+};
+
+#define GPIO_LINEOUT_MUTE_INDEX 0
+#define GPIO_LINEOUT_DET_INDEX 3
+#define GPIO_LINEIN_DET_INDEX 4
+
+static const struct acpi_gpio_params lineout_mute_gpio = { GPIO_LINEOUT_MUTE_INDEX, 0, true };
+static const struct acpi_gpio_params lineout_det_gpio = { GPIO_LINEOUT_DET_INDEX, 0, false };
+static const struct acpi_gpio_params mic_det_gpio = { GPIO_LINEIN_DET_INDEX, 0, false };
+
+
+static const struct acpi_gpio_mapping acpi_rt5660_gpios[] = {
+	{ "lineout-mute-gpios", &lineout_mute_gpio , 1 },
+	{ "lineout-det-gpios", &lineout_det_gpio, 1 },
+	{ "mic-det-gpios", &mic_det_gpio, 1 },
+	{ NULL },
+};
+
+static struct snd_soc_jack_pin lineout_jack_pin = {
+	.pin	= "Line Out",
+	.mask	= SND_JACK_LINEOUT,
+};
+
+static struct snd_soc_jack_pin mic_jack_pin = {
+	.pin	= "Line In",
+	.mask	= SND_JACK_MICROPHONE,
+};
+
+static struct snd_soc_jack_gpio lineout_jack_gpio = {
+	.name			= "lineout-det",
+	.report			= SND_JACK_LINEOUT,
+	.debounce_time		= 200,
+};
+
+static struct snd_soc_jack_gpio mic_jack_gpio = {
+	.name			= "mic-det",
+	.report			= SND_JACK_MICROPHONE,
+	.debounce_time		= 200,
+};
+
+static int kabylake_5660_event_lineout(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *k, int event)
+{
+	struct snd_soc_dapm_context *dapm = w->dapm;
+	struct kbl_codec_private *priv = snd_soc_card_get_drvdata(dapm->card);
+
+	gpiod_set_value_cansleep(priv->gpio_lo_mute,
+			!(SND_SOC_DAPM_EVENT_ON(event)));
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new kabylake_rt5660_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Line In"),
+	SOC_DAPM_PIN_SWITCH("Line Out"),
+};
+
+static const struct snd_soc_dapm_widget kabylake_rt5660_widgets[] = {
+	SND_SOC_DAPM_MIC("Line In", NULL),
+	SND_SOC_DAPM_LINE("Line Out", kabylake_5660_event_lineout),
+	SND_SOC_DAPM_SPK("DP", NULL),
+	SND_SOC_DAPM_SPK("HDMI", NULL),
+};
+
+static const struct snd_soc_dapm_route kabylake_rt5660_map[] = {
+	/* other jacks */
+	{"IN1P", NULL, "Line In"},
+	{"IN2P", NULL, "Line In"},
+	{"Line Out", NULL, "LOUTR"},
+	{"Line Out", NULL, "LOUTL"},
+
+	/* CODEC BE connections */
+	{ "AIF1 Playback", NULL, "ssp0 Tx"},
+	{ "ssp0 Tx", NULL, "codec0_out"},
+
+	{ "codec0_in", NULL, "ssp0 Rx" },
+	{ "ssp0 Rx", NULL, "AIF1 Capture" },
+
+	{ "hifi2", NULL, "iDisp2 Tx"},
+	{ "iDisp2 Tx", NULL, "iDisp2_out"},
+	{ "hifi1", NULL, "iDisp1 Tx"},
+	{ "iDisp1 Tx", NULL, "iDisp1_out"},
+};
+
+static int kabylake_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);
+	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+	/* The ADSP will convert the FE rate to 48k, stereo */
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = DUAL_CHANNEL;
+
+	/* set SSP0 to 24 bit */
+	snd_mask_none(fmt);
+	snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+	return 0;
+}
+
+static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret;
+	struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+	struct snd_soc_component *component = rtd->codec_dai->component;
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+	ret = devm_acpi_dev_add_driver_gpios(component->dev, acpi_rt5660_gpios);
+	if (ret)
+		dev_warn(component->dev, "Failed to add driver gpios\n");
+
+	/* Request rt5660 GPIO for lineout mute control */
+	ctx->gpio_lo_mute = devm_gpiod_get(component->dev, "lineout-mute",
+			GPIOD_OUT_HIGH);
+	if (IS_ERR(ctx->gpio_lo_mute)) {
+		dev_err(component->dev, "Can't find GPIO_MUTE# gpio\n");
+		return PTR_ERR(ctx->gpio_lo_mute);
+	}
+
+	/* Create and initialize headphone jack */
+	if (!snd_soc_card_jack_new(rtd->card, "Lineout Jack",
+			SND_JACK_LINEOUT, &lineout_jack,
+			&lineout_jack_pin, 1)) {
+		lineout_jack_gpio.gpiod_dev = component->dev;
+		if (snd_soc_jack_add_gpios(&lineout_jack, 1,
+				&lineout_jack_gpio))
+			dev_err(component->dev, "Can't add Lineout jack gpio\n");
+	} else
+		dev_err(component->dev, "Can't create Lineout 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 = component->dev;
+		if (snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio))
+			dev_err(component->dev, "Can't add mic jack gpio\n");
+	} else
+		dev_err(component->dev, "Can't create mic jack\n");
+
+	snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+	snd_soc_dapm_force_enable_pin(dapm, "BST1");
+	snd_soc_dapm_force_enable_pin(dapm, "BST2");
+
+	return 0;
+}
+
+static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device)
+{
+	struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card);
+	struct snd_soc_dai *dai = rtd->codec_dai;
+	struct kbl_hdmi_pcm *pcm;
+
+	pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+	if (!pcm)
+		return -ENOMEM;
+
+	pcm->device = device;
+	pcm->codec_dai = dai;
+
+	list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+	return 0;
+}
+
+static int kabylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
+{
+	return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI1_PB);
+}
+
+static int kabylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
+{
+	return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI2_PB);
+}
+
+static int kabylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
+{
+	return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI3_PB);
+}
+
+static int kabylake_rt5660_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,
+				     RT5660_SCLK_S_PLL1, params_rate(params) * 512,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_pll(codec_dai, 0,
+				  RT5660_PLL1_S_BCLK,
+				  params_rate(params) * 50,
+				  params_rate(params) * 512);
+	if (ret < 0)
+		dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);
+
+	return ret;
+}
+
+static struct snd_soc_ops kabylake_rt5660_ops = {
+	.hw_params = kabylake_rt5660_hw_params,
+};
+
+static const unsigned int rates[] = {
+	48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+	.count = ARRAY_SIZE(rates),
+	.list  = rates,
+	.mask = 0,
+};
+
+static const unsigned int channels[] = {
+	DUAL_CHANNEL,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+	.count = ARRAY_SIZE(channels),
+	.list = channels,
+	.mask = 0,
+};
+
+static int kbl_fe_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	/*
+	 * On this platform for PCM device we support,
+	 * 48Khz
+	 * stereo
+	 * 16 bit audio
+	 */
+
+	runtime->hw.channels_max = DUAL_CHANNEL;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+					   &constraints_channels);
+
+	runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+	snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
+	snd_pcm_hw_constraint_list(runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+
+	return 0;
+}
+
+static const struct snd_soc_ops kabylake_rt5660_fe_ops = {
+	.startup = kbl_fe_startup,
+};
+
+/* kabylake digital audio interface glue - connects rt5660 codec <--> CPU */
+static struct snd_soc_dai_link kabylake_rt5660_dais[] = {
+	/* Front End DAI links */
+	[KBL_DPCM_AUDIO_PB] = {
+		.name = "Kbl Audio Port",
+		.stream_name = "Audio",
+		.cpu_dai_name = "System Pin",
+		.platform_name = "0000:00:1f.3",
+		.dynamic = 1,
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.nonatomic = 1,
+		.trigger = {
+			SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.dpcm_playback = 1,
+		.ops = &kabylake_rt5660_fe_ops,
+	},
+	[KBL_DPCM_AUDIO_CP] = {
+		.name = "Kbl Audio Capture Port",
+		.stream_name = "Audio Record",
+		.cpu_dai_name = "System Pin",
+		.platform_name = "0000:00:1f.3",
+		.dynamic = 1,
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.nonatomic = 1,
+		.trigger = {
+			SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.dpcm_capture = 1,
+		.ops = &kabylake_rt5660_fe_ops,
+	},
+	[KBL_DPCM_AUDIO_HDMI1_PB] = {
+		.name = "Kbl HDMI Port1",
+		.stream_name = "Hdmi1",
+		.cpu_dai_name = "HDMI1 Pin",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.platform_name = "0000:00:1f.3",
+		.dpcm_playback = 1,
+		.init = NULL,
+		.trigger = {
+			SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.nonatomic = 1,
+		.dynamic = 1,
+	},
+	[KBL_DPCM_AUDIO_HDMI2_PB] = {
+		.name = "Kbl HDMI Port2",
+		.stream_name = "Hdmi2",
+		.cpu_dai_name = "HDMI2 Pin",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.platform_name = "0000:00:1f.3",
+		.dpcm_playback = 1,
+		.init = NULL,
+		.trigger = {
+			SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.nonatomic = 1,
+		.dynamic = 1,
+	},
+	[KBL_DPCM_AUDIO_HDMI3_PB] = {
+		.name = "Kbl HDMI Port3",
+		.stream_name = "Hdmi3",
+		.cpu_dai_name = "HDMI3 Pin",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.platform_name = "0000:00:1f.3",
+		.trigger = {
+			SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+		.dpcm_playback = 1,
+		.init = NULL,
+		.nonatomic = 1,
+		.dynamic = 1,
+	},
+
+	/* Back End DAI links */
+	{
+		/* SSP0 - Codec */
+		.name = "SSP0-Codec",
+		.id = 0,
+		.cpu_dai_name = "SSP0 Pin",
+		.platform_name = "0000:00:1f.3",
+		.no_pcm = 1,
+		.codec_name = "i2c-10EC3277:00",
+		.codec_dai_name = KBL_RT5660_CODEC_DAI,
+		.init = kabylake_rt5660_codec_init,
+		.dai_fmt = SND_SOC_DAIFMT_I2S |
+		SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBS_CFS,
+		.ignore_pmdown_time = 1,
+		.be_hw_params_fixup = kabylake_ssp0_fixup,
+		.ops = &kabylake_rt5660_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	{
+		.name = "iDisp1",
+		.id = 1,
+		.cpu_dai_name = "iDisp1 Pin",
+		.codec_name = "ehdaudio0D2",
+		.codec_dai_name = "intel-hdmi-hifi1",
+		.platform_name = "0000:00:1f.3",
+		.dpcm_playback = 1,
+		.init = kabylake_hdmi1_init,
+		.no_pcm = 1,
+	},
+	{
+		.name = "iDisp2",
+		.id = 2,
+		.cpu_dai_name = "iDisp2 Pin",
+		.codec_name = "ehdaudio0D2",
+		.codec_dai_name = "intel-hdmi-hifi2",
+		.platform_name = "0000:00:1f.3",
+		.init = kabylake_hdmi2_init,
+		.dpcm_playback = 1,
+		.no_pcm = 1,
+	},
+	{
+		.name = "iDisp3",
+		.id = 3,
+		.cpu_dai_name = "iDisp3 Pin",
+		.codec_name = "ehdaudio0D2",
+		.codec_dai_name = "intel-hdmi-hifi3",
+		.platform_name = "0000:00:1f.3",
+		.init = kabylake_hdmi3_init,
+		.dpcm_playback = 1,
+		.no_pcm = 1,
+	},
+};
+
+
+#define NAME_SIZE	32
+static int kabylake_card_late_probe(struct snd_soc_card *card)
+{
+	struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(card);
+	struct kbl_hdmi_pcm *pcm;
+	struct snd_soc_component *component = NULL;
+	int err, i = 0;
+	char jack_name[NAME_SIZE];
+
+	list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+		component = pcm->codec_dai->component;
+		snprintf(jack_name, sizeof(jack_name),
+			"HDMI/DP, pcm=%d Jack", pcm->device);
+		err = snd_soc_card_jack_new(card, jack_name,
+					SND_JACK_AVOUT, &skylake_hdmi[i],
+					NULL, 0);
+
+		if (err)
+			return err;
+
+		err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
+				&skylake_hdmi[i]);
+		if (err < 0)
+			return err;
+
+		i++;
+
+	}
+
+	if (!component)
+		return -EINVAL;
+
+	return hdac_hdmi_jack_port_init(component, &card->dapm);
+}
+
+/* kabylake audio machine driver for rt5660 */
+static struct snd_soc_card kabylake_audio_card_rt5660 = {
+	.name = "kblrt5660",
+	.owner = THIS_MODULE,
+	.dai_link = kabylake_rt5660_dais,
+	.num_links = ARRAY_SIZE(kabylake_rt5660_dais),
+	.controls = kabylake_rt5660_controls,
+	.num_controls = ARRAY_SIZE(kabylake_rt5660_controls),
+	.dapm_widgets = kabylake_rt5660_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(kabylake_rt5660_widgets),
+	.dapm_routes = kabylake_rt5660_map,
+	.num_dapm_routes = ARRAY_SIZE(kabylake_rt5660_map),
+	.fully_routed = true,
+	.late_probe = kabylake_card_late_probe,
+};
+
+static int kabylake_audio_probe(struct platform_device *pdev)
+{
+	struct kbl_codec_private *ctx;
+
+	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+
+	kabylake_audio_card =
+		(struct snd_soc_card *)pdev->id_entry->driver_data;
+
+	kabylake_audio_card->dev = &pdev->dev;
+	snd_soc_card_set_drvdata(kabylake_audio_card, ctx);
+	return devm_snd_soc_register_card(&pdev->dev, kabylake_audio_card);
+}
+
+static const struct platform_device_id kbl_board_ids[] = {
+	{
+		.name = "kbl_rt5660",
+		.driver_data =
+			(kernel_ulong_t)&kabylake_audio_card_rt5660,
+	},
+	{ }
+};
+
+static struct platform_driver kabylake_audio = {
+	.probe = kabylake_audio_probe,
+	.driver = {
+		.name = "kbl_rt5660",
+		.pm = &snd_soc_pm_ops,
+	},
+	.id_table = kbl_board_ids,
+};
+
+module_platform_driver(kabylake_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("Audio Machine driver-RT5660 in I2S mode");
+MODULE_AUTHOR("Hui Wang <hui.wang@canonical.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:kbl_rt5660");
diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
index a317b7790fce..1e41c7ded9e9 100644
--- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c
@@ -96,6 +96,11 @@  struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = {
 		.quirk_data = &kbl_7219_98927_codecs,
 		.pdata = &skl_dmic_data
 	},
+	{
+		.id = "10EC3277",
+		.drv_name = "kbl_rt5660",
+		.fw_filename = "intel/dsp_fw_kbl.bin",
+	},
 	{},
 };
 EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines);