Merge remote-tracking branches 'asoc/topic/intel', 'asoc/topic/kirkwood', 'asoc/topic...
authorMark Brown <broonie@linaro.org>
Mon, 4 Aug 2014 15:31:45 +0000 (16:31 +0100)
committerMark Brown <broonie@linaro.org>
Mon, 4 Aug 2014 15:31:45 +0000 (16:31 +0100)
31 files changed:
Documentation/devicetree/bindings/sound/max98090.txt
arch/x86/include/asm/platform_sst_audio.h [new file with mode: 0644]
include/sound/rt286.h [new file with mode: 0644]
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/max98090.c
sound/soc/codecs/mc13783.c
sound/soc/codecs/rt286.c [new file with mode: 0644]
sound/soc/codecs/rt286.h [new file with mode: 0644]
sound/soc/intel/Kconfig
sound/soc/intel/Makefile
sound/soc/intel/broadwell.c [new file with mode: 0644]
sound/soc/intel/byt-max98090.c
sound/soc/intel/byt-rt5640.c
sound/soc/intel/sst-atom-controls.h [new file with mode: 0644]
sound/soc/intel/sst-baytrail-ipc.c
sound/soc/intel/sst-dsp.c
sound/soc/intel/sst-dsp.h
sound/soc/intel/sst-haswell-dsp.c
sound/soc/intel/sst-haswell-ipc.c
sound/soc/intel/sst-mfld-dsp.h
sound/soc/intel/sst-mfld-platform-compress.c
sound/soc/intel/sst-mfld-platform-pcm.c
sound/soc/intel/sst-mfld-platform.h
sound/soc/kirkwood/Kconfig
sound/soc/kirkwood/Makefile
sound/soc/kirkwood/kirkwood-dma.c
sound/soc/kirkwood/kirkwood-i2s.c
sound/soc/kirkwood/kirkwood-openrd.c [deleted file]
sound/soc/kirkwood/kirkwood-t5325.c [deleted file]
sound/soc/kirkwood/kirkwood.h

index a5e63fa..c454e67 100644 (file)
@@ -4,7 +4,7 @@ This device supports I2C only.
 
 Required properties:
 
-- compatible : "maxim,max98090".
+- compatible : "maxim,max98090" or "maxim,max98091".
 
 - reg : The I2C address of the device.
 
diff --git a/arch/x86/include/asm/platform_sst_audio.h b/arch/x86/include/asm/platform_sst_audio.h
new file mode 100644 (file)
index 0000000..0a4e140
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * platform_sst_audio.h:  sst audio platform data header file
+ *
+ * Copyright (C) 2012-14 Intel Corporation
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ *     Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *     Vinod Koul ,vinod.koul@intel.com>
+ *
+ * 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.
+ */
+#ifndef _PLATFORM_SST_AUDIO_H_
+#define _PLATFORM_SST_AUDIO_H_
+
+#include <linux/sfi.h>
+
+enum sst_audio_task_id_mrfld {
+       SST_TASK_ID_NONE = 0,
+       SST_TASK_ID_SBA = 1,
+       SST_TASK_ID_MEDIA = 3,
+       SST_TASK_ID_MAX = SST_TASK_ID_MEDIA,
+};
+
+/* Device IDs for Merrifield are Pipe IDs,
+ * ref: DSP spec v0.75 */
+enum sst_audio_device_id_mrfld {
+       /* Output pipeline IDs */
+       PIPE_ID_OUT_START = 0x0,
+       PIPE_CODEC_OUT0 = 0x2,
+       PIPE_CODEC_OUT1 = 0x3,
+       PIPE_SPROT_LOOP_OUT = 0x4,
+       PIPE_MEDIA_LOOP1_OUT = 0x5,
+       PIPE_MEDIA_LOOP2_OUT = 0x6,
+       PIPE_VOIP_OUT = 0xC,
+       PIPE_PCM0_OUT = 0xD,
+       PIPE_PCM1_OUT = 0xE,
+       PIPE_PCM2_OUT = 0xF,
+       PIPE_MEDIA0_OUT = 0x12,
+       PIPE_MEDIA1_OUT = 0x13,
+/* Input Pipeline IDs */
+       PIPE_ID_IN_START = 0x80,
+       PIPE_CODEC_IN0 = 0x82,
+       PIPE_CODEC_IN1 = 0x83,
+       PIPE_SPROT_LOOP_IN = 0x84,
+       PIPE_MEDIA_LOOP1_IN = 0x85,
+       PIPE_MEDIA_LOOP2_IN = 0x86,
+       PIPE_VOIP_IN = 0x8C,
+       PIPE_PCM0_IN = 0x8D,
+       PIPE_PCM1_IN = 0x8E,
+       PIPE_MEDIA0_IN = 0x8F,
+       PIPE_MEDIA1_IN = 0x90,
+       PIPE_MEDIA2_IN = 0x91,
+       PIPE_RSVD = 0xFF,
+};
+
+/* The stream map for each platform consists of an array of the below
+ * stream map structure.
+ */
+struct sst_dev_stream_map {
+       u8 dev_num;             /* device id */
+       u8 subdev_num;          /* substream */
+       u8 direction;
+       u8 device_id;           /* fw id */
+       u8 task_id;             /* fw task */
+       u8 status;
+};
+
+struct sst_platform_data {
+       /* Intel software platform id*/
+       struct sst_dev_stream_map *pdev_strm_map;
+       unsigned int strm_map_size;
+};
+
+int add_sst_platform_device(void);
+#endif
+
diff --git a/include/sound/rt286.h b/include/sound/rt286.h
new file mode 100644 (file)
index 0000000..eb773d1
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * linux/sound/rt286.h -- Platform data for RT286
+ *
+ * Copyright 2013 Realtek Microelectronics
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_SND_RT286_H
+#define __LINUX_SND_RT286_H
+
+struct rt286_platform_data {
+       bool cbj_en; /*combo jack enable*/
+       bool gpio2_en; /*GPIO2 enable*/
+};
+
+#endif
index e4a1d2a..4c75425 100644 (file)
@@ -75,6 +75,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_PCM3008
        select SND_SOC_PCM512x_I2C if I2C
        select SND_SOC_PCM512x_SPI if SPI_MASTER
+       select SND_SOC_RT286 if I2C
        select SND_SOC_RT5631 if I2C
        select SND_SOC_RT5640 if I2C
        select SND_SOC_RT5645 if I2C
@@ -455,6 +456,9 @@ config SND_SOC_RL6231
        default m if SND_SOC_RT5645=m
        default m if SND_SOC_RT5651=m
 
+config SND_SOC_RT286
+       tristate
+
 config SND_SOC_RT5631
        tristate
 
index 97b80a1..ade412e 100644 (file)
@@ -69,6 +69,7 @@ snd-soc-pcm512x-objs := pcm512x.o
 snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
 snd-soc-pcm512x-spi-objs := pcm512x-spi.o
 snd-soc-rl6231-objs := rl6231.o
+snd-soc-rt286-objs := rt286.o
 snd-soc-rt5631-objs := rt5631.o
 snd-soc-rt5640-objs := rt5640.o
 snd-soc-rt5645-objs := rt5645.o
@@ -237,6 +238,7 @@ obj-$(CONFIG_SND_SOC_PCM512x)       += snd-soc-pcm512x.o
 obj-$(CONFIG_SND_SOC_PCM512x_I2C)      += snd-soc-pcm512x-i2c.o
 obj-$(CONFIG_SND_SOC_PCM512x_SPI)      += snd-soc-pcm512x-spi.o
 obj-$(CONFIG_SND_SOC_RL6231)   += snd-soc-rl6231.o
+obj-$(CONFIG_SND_SOC_RT286)    += snd-soc-rt286.o
 obj-$(CONFIG_SND_SOC_RT5631)   += snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_RT5640)   += snd-soc-rt5640.o
 obj-$(CONFIG_SND_SOC_RT5645)   += snd-soc-rt5645.o
index d97f1ce..4a063fa 100644 (file)
 #include <sound/max98090.h>
 #include "max98090.h"
 
-#define DEBUG
-#define EXTMIC_METHOD
-#define EXTMIC_METHOD_TEST
-
 /* Allows for sparsely populated register maps */
 static struct reg_default max98090_reg[] = {
        { 0x00, 0x00 }, /* 00 Software Reset */
@@ -820,7 +816,6 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
        else
                val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT;
 
-
        if (val >= 1) {
                if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) {
                        max98090->pa1en = val - 1; /* Update for volatile */
@@ -1140,7 +1135,6 @@ static const struct snd_kcontrol_new max98090_mixhprsel_mux =
        SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum);
 
 static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
-
        SND_SOC_DAPM_INPUT("MIC1"),
        SND_SOC_DAPM_INPUT("MIC2"),
        SND_SOC_DAPM_INPUT("DMICL"),
@@ -1304,7 +1298,6 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
-
        SND_SOC_DAPM_INPUT("DMIC3"),
        SND_SOC_DAPM_INPUT("DMIC4"),
 
@@ -1315,7 +1308,6 @@ static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
-
        {"MIC1 Input", NULL, "MIC1"},
        {"MIC2 Input", NULL, "MIC2"},
 
@@ -1493,17 +1485,14 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
        {"SPKR", NULL, "SPK Right Out"},
        {"RCVL", NULL, "RCV Left Out"},
        {"RCVR", NULL, "RCV Right Out"},
-
 };
 
 static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
-
        /* DMIC inputs */
        {"DMIC3", NULL, "DMIC3_ENA"},
        {"DMIC4", NULL, "DMIC4_ENA"},
        {"DMIC3", NULL, "AHPF"},
        {"DMIC4", NULL, "AHPF"},
-
 };
 
 static int max98090_add_widgets(struct snd_soc_codec *codec)
@@ -1531,7 +1520,6 @@ static int max98090_add_widgets(struct snd_soc_codec *codec)
 
                snd_soc_dapm_add_routes(dapm, max98091_dapm_routes,
                        ARRAY_SIZE(max98091_dapm_routes));
-
        }
 
        return 0;
@@ -2212,22 +2200,11 @@ static struct snd_soc_dai_driver max98090_dai[] = {
 }
 };
 
-static void max98090_handle_pdata(struct snd_soc_codec *codec)
-{
-       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
-       struct max98090_pdata *pdata = max98090->pdata;
-
-       if (!pdata) {
-               dev_err(codec->dev, "No platform data\n");
-               return;
-       }
-
-}
-
 static int max98090_probe(struct snd_soc_codec *codec)
 {
        struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
        struct max98090_cdata *cdata;
+       enum max98090_type devtype;
        int ret = 0;
 
        dev_dbg(codec->dev, "max98090_probe\n");
@@ -2263,16 +2240,21 @@ static int max98090_probe(struct snd_soc_codec *codec)
        }
 
        if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) {
-               max98090->devtype = MAX98090;
+               devtype = MAX98090;
                dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret);
        } else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
-               max98090->devtype = MAX98091;
+               devtype = MAX98091;
                dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret);
        } else {
-               max98090->devtype = MAX98090;
+               devtype = MAX98090;
                dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret);
        }
 
+       if (max98090->devtype != devtype) {
+               dev_warn(codec->dev, "Mismatch in DT specified CODEC type.\n");
+               max98090->devtype = devtype;
+       }
+
        max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
 
        INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work);
@@ -2317,8 +2299,6 @@ static int max98090_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
                M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
 
-       max98090_handle_pdata(codec);
-
        max98090_add_widgets(codec);
 
 err_access:
@@ -2428,7 +2408,7 @@ static int max98090_runtime_suspend(struct device *dev)
 }
 #endif
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int max98090_resume(struct device *dev)
 {
        struct max98090_priv *max98090 = dev_get_drvdata(dev);
@@ -2460,12 +2440,14 @@ static const struct dev_pm_ops max98090_pm = {
 
 static const struct i2c_device_id max98090_i2c_id[] = {
        { "max98090", MAX98090 },
+       { "max98091", MAX98091 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
 
 static const struct of_device_id max98090_of_match[] = {
        { .compatible = "maxim,max98090", },
+       { .compatible = "maxim,max98091", },
        { }
 };
 MODULE_DEVICE_TABLE(of, max98090_of_match);
index 9965277..388f90a 100644 (file)
@@ -766,11 +766,11 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
 
                ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
                if (ret)
-                       return ret;
+                       goto out;
 
                ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
                if (ret)
-                       return ret;
+                       goto out;
        }
 
        dev_set_drvdata(&pdev->dev, priv);
@@ -783,6 +783,8 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
                ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
                        mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
 
+out:
+       of_node_put(np);
        return ret;
 }
 
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
new file mode 100644 (file)
index 0000000..218f86e
--- /dev/null
@@ -0,0 +1,1224 @@
+/*
+ * rt286.c  --  RT286 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <linux/workqueue.h>
+#include <sound/rt286.h>
+#include <sound/hda_verbs.h>
+
+#include "rt286.h"
+
+#define RT286_VENDOR_ID 0x10ec0286
+
+struct rt286_priv {
+       struct regmap *regmap;
+       struct snd_soc_codec *codec;
+       struct rt286_platform_data pdata;
+       struct i2c_client *i2c;
+       struct snd_soc_jack *jack;
+       struct delayed_work jack_detect_work;
+       int sys_clk;
+       struct reg_default *index_cache;
+};
+
+static struct reg_default rt286_index_def[] = {
+       { 0x01, 0xaaaa },
+       { 0x02, 0x8aaa },
+       { 0x03, 0x0002 },
+       { 0x04, 0xaf01 },
+       { 0x08, 0x000d },
+       { 0x09, 0xd810 },
+       { 0x0a, 0x0060 },
+       { 0x0b, 0x0000 },
+       { 0x0d, 0x2800 },
+       { 0x0f, 0x0000 },
+       { 0x19, 0x0a17 },
+       { 0x20, 0x0020 },
+       { 0x33, 0x0208 },
+       { 0x49, 0x0004 },
+       { 0x4f, 0x50e9 },
+       { 0x50, 0x2c00 },
+       { 0x63, 0x2902 },
+       { 0x67, 0x1111 },
+       { 0x68, 0x1016 },
+       { 0x69, 0x273f },
+};
+#define INDEX_CACHE_SIZE ARRAY_SIZE(rt286_index_def)
+
+static const struct reg_default rt286_reg[] = {
+       { 0x00170500, 0x00000400 },
+       { 0x00220000, 0x00000031 },
+       { 0x00239000, 0x0000007f },
+       { 0x0023a000, 0x0000007f },
+       { 0x00270500, 0x00000400 },
+       { 0x00370500, 0x00000400 },
+       { 0x00870500, 0x00000400 },
+       { 0x00920000, 0x00000031 },
+       { 0x00935000, 0x000000c3 },
+       { 0x00936000, 0x000000c3 },
+       { 0x00970500, 0x00000400 },
+       { 0x00b37000, 0x00000097 },
+       { 0x00b37200, 0x00000097 },
+       { 0x00b37300, 0x00000097 },
+       { 0x00c37000, 0x00000000 },
+       { 0x00c37100, 0x00000080 },
+       { 0x01270500, 0x00000400 },
+       { 0x01370500, 0x00000400 },
+       { 0x01371f00, 0x411111f0 },
+       { 0x01439000, 0x00000080 },
+       { 0x0143a000, 0x00000080 },
+       { 0x01470700, 0x00000000 },
+       { 0x01470500, 0x00000400 },
+       { 0x01470c00, 0x00000000 },
+       { 0x01470100, 0x00000000 },
+       { 0x01837000, 0x00000000 },
+       { 0x01870500, 0x00000400 },
+       { 0x02050000, 0x00000000 },
+       { 0x02139000, 0x00000080 },
+       { 0x0213a000, 0x00000080 },
+       { 0x02170100, 0x00000000 },
+       { 0x02170500, 0x00000400 },
+       { 0x02170700, 0x00000000 },
+       { 0x02270100, 0x00000000 },
+       { 0x02370100, 0x00000000 },
+       { 0x02040000, 0x00004002 },
+       { 0x01870700, 0x00000020 },
+       { 0x00830000, 0x000000c3 },
+       { 0x00930000, 0x000000c3 },
+       { 0x01270700, 0x00000000 },
+};
+
+static bool rt286_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0 ... 0xff:
+       case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+       case RT286_GET_HP_SENSE:
+       case RT286_GET_MIC1_SENSE:
+       case RT286_PROC_COEF:
+               return true;
+       default:
+               return false;
+       }
+
+
+}
+
+static bool rt286_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case 0 ... 0xff:
+       case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+       case RT286_GET_HP_SENSE:
+       case RT286_GET_MIC1_SENSE:
+       case RT286_SET_AUDIO_POWER:
+       case RT286_SET_HPO_POWER:
+       case RT286_SET_SPK_POWER:
+       case RT286_SET_DMIC1_POWER:
+       case RT286_SPK_MUX:
+       case RT286_HPO_MUX:
+       case RT286_ADC0_MUX:
+       case RT286_ADC1_MUX:
+       case RT286_SET_MIC1:
+       case RT286_SET_PIN_HPO:
+       case RT286_SET_PIN_SPK:
+       case RT286_SET_PIN_DMIC1:
+       case RT286_SPK_EAPD:
+       case RT286_SET_AMP_GAIN_HPO:
+       case RT286_SET_DMIC2_DEFAULT:
+       case RT286_DACL_GAIN:
+       case RT286_DACR_GAIN:
+       case RT286_ADCL_GAIN:
+       case RT286_ADCR_GAIN:
+       case RT286_MIC_GAIN:
+       case RT286_SPOL_GAIN:
+       case RT286_SPOR_GAIN:
+       case RT286_HPOL_GAIN:
+       case RT286_HPOR_GAIN:
+       case RT286_F_DAC_SWITCH:
+       case RT286_F_RECMIX_SWITCH:
+       case RT286_REC_MIC_SWITCH:
+       case RT286_REC_I2S_SWITCH:
+       case RT286_REC_LINE_SWITCH:
+       case RT286_REC_BEEP_SWITCH:
+       case RT286_DAC_FORMAT:
+       case RT286_ADC_FORMAT:
+       case RT286_COEF_INDEX:
+       case RT286_PROC_COEF:
+       case RT286_SET_AMP_GAIN_ADC_IN1:
+       case RT286_SET_AMP_GAIN_ADC_IN2:
+       case RT286_SET_POWER(RT286_DAC_OUT1):
+       case RT286_SET_POWER(RT286_DAC_OUT2):
+       case RT286_SET_POWER(RT286_ADC_IN1):
+       case RT286_SET_POWER(RT286_ADC_IN2):
+       case RT286_SET_POWER(RT286_DMIC2):
+       case RT286_SET_POWER(RT286_MIC1):
+               return true;
+       default:
+               return false;
+       }
+}
+
+static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
+{
+       struct i2c_client *client = context;
+       struct rt286_priv *rt286 = i2c_get_clientdata(client);
+       u8 data[4];
+       int ret, i;
+
+       /*handle index registers*/
+       if (reg <= 0xff) {
+               rt286_hw_write(client, RT286_COEF_INDEX, reg);
+               reg = RT286_PROC_COEF;
+               for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+                       if (reg == rt286->index_cache[i].reg) {
+                               rt286->index_cache[i].def = value;
+                               break;
+                       }
+
+               }
+       }
+
+       data[0] = (reg >> 24) & 0xff;
+       data[1] = (reg >> 16) & 0xff;
+       /*
+        * 4 bit VID: reg should be 0
+        * 12 bit VID: value should be 0
+        * So we use an OR operator to handle it rather than use if condition.
+        */
+       data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
+       data[3] = value & 0xff;
+
+       ret = i2c_master_send(client, data, 4);
+
+       if (ret == 4)
+               return 0;
+       else
+               pr_err("ret=%d\n", ret);
+       if (ret < 0)
+               return ret;
+       else
+               return -EIO;
+}
+
+static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
+{
+       struct i2c_client *client = context;
+       struct i2c_msg xfer[2];
+       int ret;
+       __be32 be_reg;
+       unsigned int index, vid, buf = 0x0;
+
+       /*handle index registers*/
+       if (reg <= 0xff) {
+               rt286_hw_write(client, RT286_COEF_INDEX, reg);
+               reg = RT286_PROC_COEF;
+       }
+
+       reg = reg | 0x80000;
+       vid = (reg >> 8) & 0xfff;
+
+       if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
+               index = (reg >> 8) & 0xf;
+               reg = (reg & ~0xf0f) | index;
+       }
+       be_reg = cpu_to_be32(reg);
+
+       /* Write register */
+       xfer[0].addr = client->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = 4;
+       xfer[0].buf = (u8 *)&be_reg;
+
+       /* Read data */
+       xfer[1].addr = client->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = 4;
+       xfer[1].buf = (u8 *)&buf;
+
+       ret = i2c_transfer(client->adapter, xfer, 2);
+       if (ret < 0)
+               return ret;
+       else if (ret != 2)
+               return -EIO;
+
+       *value = be32_to_cpu(buf);
+
+       return 0;
+}
+
+static void rt286_index_sync(struct snd_soc_codec *codec)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+       int i;
+
+       for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+               snd_soc_write(codec, rt286->index_cache[i].reg,
+                                 rt286->index_cache[i].def);
+       }
+}
+
+static int rt286_support_power_controls[] = {
+       RT286_DAC_OUT1,
+       RT286_DAC_OUT2,
+       RT286_ADC_IN1,
+       RT286_ADC_IN2,
+       RT286_MIC1,
+       RT286_DMIC1,
+       RT286_DMIC2,
+       RT286_SPK_OUT,
+       RT286_HP_OUT,
+};
+#define RT286_POWER_REG_LEN ARRAY_SIZE(rt286_support_power_controls)
+
+static int rt286_jack_detect(struct snd_soc_codec *codec, bool *hp, bool *mic)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val, buf;
+       int i;
+
+       *hp = false;
+       *mic = false;
+
+       if (rt286->pdata.cbj_en) {
+               buf = snd_soc_read(codec, RT286_GET_HP_SENSE);
+               *hp = buf & 0x80000000;
+               if (*hp) {
+                       /* power on HV,VERF */
+                       snd_soc_update_bits(codec,
+                               RT286_POWER_CTRL1, 0x1001, 0x0);
+                       /* power LDO1 */
+                       snd_soc_update_bits(codec,
+                               RT286_POWER_CTRL2, 0x4, 0x4);
+                       snd_soc_write(codec, RT286_SET_MIC1, 0x24);
+                       val = snd_soc_read(codec, RT286_CBJ_CTRL2);
+
+                       msleep(200);
+                       i = 40;
+                       while (((val & 0x0800) == 0) && (i > 0)) {
+                               val =  snd_soc_read(codec,
+                                       RT286_CBJ_CTRL2);
+                               i--;
+                               msleep(20);
+                       }
+
+                       if (0x0400 == (val & 0x0700)) {
+                               *mic = false;
+
+                               snd_soc_write(codec,
+                                       RT286_SET_MIC1, 0x20);
+                               /* power off HV,VERF */
+                               snd_soc_update_bits(codec,
+                                       RT286_POWER_CTRL1, 0x1001, 0x1001);
+                               snd_soc_update_bits(codec,
+                                       RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
+                               snd_soc_update_bits(codec,
+                                       RT286_CBJ_CTRL1, 0x0030, 0x0000);
+                               snd_soc_update_bits(codec,
+                                       RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
+                       } else if ((0x0200 == (val & 0x0700)) ||
+                               (0x0100 == (val & 0x0700))) {
+                               *mic = true;
+                               snd_soc_update_bits(codec,
+                                       RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
+                               snd_soc_update_bits(codec,
+                                       RT286_CBJ_CTRL1, 0x0030, 0x0020);
+                               snd_soc_update_bits(codec,
+                                       RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
+                       } else {
+                               *mic = false;
+                       }
+
+                       snd_soc_update_bits(codec,
+                                               RT286_MISC_CTRL1,
+                                               0x0060, 0x0000);
+               } else {
+                       snd_soc_update_bits(codec,
+                                               RT286_MISC_CTRL1,
+                                               0x0060, 0x0020);
+                       snd_soc_update_bits(codec,
+                                               RT286_A_BIAS_CTRL3,
+                                               0xc000, 0x8000);
+                       snd_soc_update_bits(codec,
+                                               RT286_CBJ_CTRL1,
+                                               0x0030, 0x0020);
+                       snd_soc_update_bits(codec,
+                                               RT286_A_BIAS_CTRL2,
+                                               0xc000, 0x8000);
+
+                       *mic = false;
+               }
+       } else {
+               buf = snd_soc_read(codec, RT286_GET_HP_SENSE);
+               *hp = buf & 0x80000000;
+               buf = snd_soc_read(codec, RT286_GET_MIC1_SENSE);
+               *mic = buf & 0x80000000;
+       }
+
+       return 0;
+}
+
+static void rt286_jack_detect_work(struct work_struct *work)
+{
+       struct rt286_priv *rt286 =
+               container_of(work, struct rt286_priv, jack_detect_work.work);
+       int status = 0;
+       bool hp = false;
+       bool mic = false;
+
+       rt286_jack_detect(rt286->codec, &hp, &mic);
+
+       if (hp == true)
+               status |= SND_JACK_HEADPHONE;
+
+       if (mic == true)
+               status |= SND_JACK_MICROPHONE;
+
+       snd_soc_jack_report(rt286->jack, status,
+               SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+}
+
+int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+       rt286->jack = jack;
+
+       /* Send an initial empty report */
+       snd_soc_jack_report(rt286->jack, 0,
+               SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt286_mic_detect);
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt286_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT286_DACL_GAIN,
+                           RT286_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+       SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT286_ADCL_GAIN,
+                           RT286_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+       SOC_SINGLE_TLV("AMIC Volume", RT286_MIC_GAIN,
+                           0, 0x3, 0, mic_vol_tlv),
+       SOC_DOUBLE_R("Speaker Playback Switch", RT286_SPOL_GAIN,
+                           RT286_SPOR_GAIN, RT286_MUTE_SFT, 1, 1),
+};
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt286_front_mix[] = {
+       SOC_DAPM_SINGLE("DAC Switch",  RT286_F_DAC_SWITCH,
+                       RT286_MUTE_SFT, 1, 1),
+       SOC_DAPM_SINGLE("RECMIX Switch", RT286_F_RECMIX_SWITCH,
+                       RT286_MUTE_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt286_rec_mix[] = {
+       SOC_DAPM_SINGLE("Mic1 Switch", RT286_REC_MIC_SWITCH,
+                       RT286_MUTE_SFT, 1, 1),
+       SOC_DAPM_SINGLE("I2S Switch", RT286_REC_I2S_SWITCH,
+                       RT286_MUTE_SFT, 1, 1),
+       SOC_DAPM_SINGLE("Line1 Switch", RT286_REC_LINE_SWITCH,
+                       RT286_MUTE_SFT, 1, 1),
+       SOC_DAPM_SINGLE("Beep Switch", RT286_REC_BEEP_SWITCH,
+                       RT286_MUTE_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spo_enable_control =
+       SOC_DAPM_SINGLE("Switch", RT286_SET_PIN_SPK,
+                       RT286_SET_PIN_SFT, 1, 0);
+
+static const struct snd_kcontrol_new hpol_enable_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOL_GAIN,
+                       RT286_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpor_enable_control =
+       SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOR_GAIN,
+                       RT286_MUTE_SFT, 1, 1);
+
+/* ADC0 source */
+static const char * const rt286_adc_src[] = {
+       "Mic", "RECMIX", "Dmic"
+};
+
+static const int rt286_adc_values[] = {
+       0, 4, 5,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+       rt286_adc0_enum, RT286_ADC0_MUX, RT286_ADC_SEL_SFT,
+       RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
+
+static const struct snd_kcontrol_new rt286_adc0_mux =
+       SOC_DAPM_ENUM("ADC 0 source", rt286_adc0_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+       rt286_adc1_enum, RT286_ADC1_MUX, RT286_ADC_SEL_SFT,
+       RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
+
+static const struct snd_kcontrol_new rt286_adc1_mux =
+       SOC_DAPM_ENUM("ADC 1 source", rt286_adc1_enum);
+
+static const char * const rt286_dac_src[] = {
+       "Front", "Surround"
+};
+/* HP-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt286_hpo_enum, RT286_HPO_MUX,
+                               0, rt286_dac_src);
+
+static const struct snd_kcontrol_new rt286_hpo_mux =
+SOC_DAPM_ENUM("HPO source", rt286_hpo_enum);
+
+/* SPK-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt286_spo_enum, RT286_SPK_MUX,
+                               0, rt286_dac_src);
+
+static const struct snd_kcontrol_new rt286_spo_mux =
+SOC_DAPM_ENUM("SPO source", rt286_spo_enum);
+
+static int rt286_spk_event(struct snd_soc_dapm_widget *w,
+                           struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_write(codec,
+                       RT286_SPK_EAPD, RT286_SET_EAPD_HIGH);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_write(codec,
+                       RT286_SPK_EAPD, RT286_SET_EAPD_LOW);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
+                                 struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0x20);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt286_adc_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       unsigned int nid;
+
+       nid = (w->reg >> 20) & 0xff;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec,
+                       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
+                       0x7080, 0x7000);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec,
+                       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
+                       0x7080, 0x7080);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
+       /* Input Lines */
+       SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+       SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("LINE1"),
+       SND_SOC_DAPM_INPUT("Beep"),
+
+       /* DMIC */
+       SND_SOC_DAPM_PGA_E("DMIC1", RT286_SET_POWER(RT286_DMIC1), 0, 1,
+               NULL, 0, rt286_set_dmic1_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA("DMIC2", RT286_SET_POWER(RT286_DMIC2), 0, 1,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM,
+               0, 0, NULL, 0),
+
+       /* REC Mixer */
+       SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0,
+               rt286_rec_mix, ARRAY_SIZE(rt286_rec_mix)),
+
+       /* ADCs */
+       SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+       /* ADC Mux */
+       SND_SOC_DAPM_MUX_E("ADC 0 Mux", RT286_SET_POWER(RT286_ADC_IN1), 0, 1,
+               &rt286_adc0_mux, rt286_adc_event, SND_SOC_DAPM_PRE_PMD |
+               SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_MUX_E("ADC 1 Mux", RT286_SET_POWER(RT286_ADC_IN2), 0, 1,
+               &rt286_adc1_mux, rt286_adc_event, SND_SOC_DAPM_PRE_PMD |
+               SND_SOC_DAPM_POST_PMU),
+
+       /* Audio Interface */
+       SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+       /* Output Side */
+       /* DACs */
+       SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+       /* Output Mux */
+       SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt286_spo_mux),
+       SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt286_hpo_mux),
+
+       SND_SOC_DAPM_SUPPLY("HP Power", RT286_SET_PIN_HPO,
+               RT286_SET_PIN_SFT, 0, NULL, 0),
+
+       /* Output Mixer */
+       SND_SOC_DAPM_MIXER("Front", RT286_SET_POWER(RT286_DAC_OUT1), 0, 1,
+                       rt286_front_mix, ARRAY_SIZE(rt286_front_mix)),
+       SND_SOC_DAPM_PGA("Surround", RT286_SET_POWER(RT286_DAC_OUT2), 0, 1,
+                       NULL, 0),
+
+       /* Output Pga */
+       SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0,
+               &spo_enable_control, rt286_spk_event,
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0,
+               &hpol_enable_control),
+       SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0,
+               &hpor_enable_control),
+
+       /* Output Lines */
+       SND_SOC_DAPM_OUTPUT("SPOL"),
+       SND_SOC_DAPM_OUTPUT("SPOR"),
+       SND_SOC_DAPM_OUTPUT("HPO Pin"),
+       SND_SOC_DAPM_OUTPUT("SPDIF"),
+};
+
+static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
+       {"DMIC1", NULL, "DMIC1 Pin"},
+       {"DMIC2", NULL, "DMIC2 Pin"},
+       {"DMIC1", NULL, "DMIC Receiver"},
+       {"DMIC2", NULL, "DMIC Receiver"},
+
+       {"RECMIX", "Beep Switch", "Beep"},
+       {"RECMIX", "Line1 Switch", "LINE1"},
+       {"RECMIX", "Mic1 Switch", "MIC1"},
+
+       {"ADC 0 Mux", "Dmic", "DMIC1"},
+       {"ADC 0 Mux", "RECMIX", "RECMIX"},
+       {"ADC 0 Mux", "Mic", "MIC1"},
+       {"ADC 1 Mux", "Dmic", "DMIC2"},
+       {"ADC 1 Mux", "RECMIX", "RECMIX"},
+       {"ADC 1 Mux", "Mic", "MIC1"},
+
+       {"ADC 0", NULL, "ADC 0 Mux"},
+       {"ADC 1", NULL, "ADC 1 Mux"},
+
+       {"AIF1TX", NULL, "ADC 0"},
+       {"AIF2TX", NULL, "ADC 1"},
+
+       {"DAC 0", NULL, "AIF1RX"},
+       {"DAC 1", NULL, "AIF2RX"},
+
+       {"Front", "DAC Switch", "DAC 0"},
+       {"Front", "RECMIX Switch", "RECMIX"},
+
+       {"Surround", NULL, "DAC 1"},
+
+       {"SPK Mux", "Front", "Front"},
+       {"SPK Mux", "Surround", "Surround"},
+
+       {"HPO Mux", "Front", "Front"},
+       {"HPO Mux", "Surround", "Surround"},
+
+       {"SPO", "Switch", "SPK Mux"},
+       {"HPO L", "Switch", "HPO Mux"},
+       {"HPO R", "Switch", "HPO Mux"},
+       {"HPO L", NULL, "HP Power"},
+       {"HPO R", NULL, "HP Power"},
+
+       {"SPOL", NULL, "SPO"},
+       {"SPOR", NULL, "SPO"},
+       {"HPO Pin", NULL, "HPO L"},
+       {"HPO Pin", NULL, "HPO R"},
+};
+
+static int rt286_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = 0;
+       int d_len_code;
+
+       switch (params_rate(params)) {
+       /* bit 14 0:48K 1:44.1K */
+       case 44100:
+               val |= 0x4000;
+               break;
+       case 48000:
+               break;
+       default:
+               dev_err(codec->dev, "Unsupported sample rate %d\n",
+                                       params_rate(params));
+               return -EINVAL;
+       }
+       switch (rt286->sys_clk) {
+       case 12288000:
+       case 24576000:
+               if (params_rate(params) != 48000) {
+                       dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+                                       params_rate(params), rt286->sys_clk);
+                       return -EINVAL;
+               }
+               break;
+       case 11289600:
+       case 22579200:
+               if (params_rate(params) != 44100) {
+                       dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+                                       params_rate(params), rt286->sys_clk);
+                       return -EINVAL;
+               }
+               break;
+       }
+
+       if (params_channels(params) <= 16) {
+               /* bit 3:0 Number of Channel */
+               val |= (params_channels(params) - 1);
+       } else {
+               dev_err(codec->dev, "Unsupported channels %d\n",
+                                       params_channels(params));
+               return -EINVAL;
+       }
+
+       d_len_code = 0;
+       switch (params_width(params)) {
+       /* bit 6:4 Bits per Sample */
+       case 16:
+               d_len_code = 0;
+               val |= (0x1 << 4);
+               break;
+       case 32:
+               d_len_code = 2;
+               val |= (0x4 << 4);
+               break;
+       case 20:
+               d_len_code = 1;
+               val |= (0x2 << 4);
+               break;
+       case 24:
+               d_len_code = 2;
+               val |= (0x3 << 4);
+               break;
+       case 8:
+               d_len_code = 3;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec,
+               RT286_I2S_CTRL1, 0x0018, d_len_code << 3);
+       dev_dbg(codec->dev, "format val = 0x%x\n", val);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x407f, val);
+       else
+               snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x407f, val);
+
+       return 0;
+}
+
+static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL1, 0x800, 0x800);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL1, 0x800, 0x0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL1, 0x300, 0x0);
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL1, 0x300, 0x1 << 8);
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL1, 0x300, 0x2 << 8);
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL1, 0x300, 0x3 << 8);
+               break;
+       default:
+               return -EINVAL;
+       }
+       /* bit 15 Stream Type 0:PCM 1:Non-PCM */
+       snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x8000, 0);
+       snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x8000, 0);
+
+       return 0;
+}
+
+static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
+                               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+       dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
+
+       if (RT286_SCLK_S_MCLK == clk_id) {
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL2, 0x0100, 0x0);
+               snd_soc_update_bits(codec,
+                       RT286_PLL_CTRL1, 0x20, 0x20);
+       } else {
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL2, 0x0100, 0x0100);
+               snd_soc_update_bits(codec,
+                       RT286_PLL_CTRL, 0x4, 0x4);
+               snd_soc_update_bits(codec,
+                       RT286_PLL_CTRL1, 0x20, 0x0);
+       }
+
+       switch (freq) {
+       case 19200000:
+               if (RT286_SCLK_S_MCLK == clk_id) {
+                       dev_err(codec->dev, "Should not use MCLK\n");
+                       return -EINVAL;
+               }
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL2, 0x40, 0x40);
+               break;
+       case 24000000:
+               if (RT286_SCLK_S_MCLK == clk_id) {
+                       dev_err(codec->dev, "Should not use MCLK\n");
+                       return -EINVAL;
+               }
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL2, 0x40, 0x0);
+               break;
+       case 12288000:
+       case 11289600:
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL2, 0x8, 0x0);
+               snd_soc_update_bits(codec,
+                       RT286_CLK_DIV, 0xfc1e, 0x0004);
+               break;
+       case 24576000:
+       case 22579200:
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL2, 0x8, 0x8);
+               snd_soc_update_bits(codec,
+                       RT286_CLK_DIV, 0xfc1e, 0x5406);
+               break;
+       default:
+               dev_err(codec->dev, "Unsupported system clock\n");
+               return -EINVAL;
+       }
+
+       rt286->sys_clk = freq;
+
+       return 0;
+}
+
+static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+       if (50 == ratio)
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL1, 0x1000, 0x1000);
+       else
+               snd_soc_update_bits(codec,
+                       RT286_I2S_CTRL1, 0x1000, 0x0);
+
+
+       return 0;
+}
+
+static int rt286_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_PREPARE:
+               if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
+                       snd_soc_write(codec,
+                               RT286_SET_AUDIO_POWER, AC_PWRST_D0);
+                       snd_soc_update_bits(codec,
+                               RT286_DC_GAIN, 0x200, 0x200);
+               }
+               break;
+
+       case SND_SOC_BIAS_ON:
+               mdelay(10);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_write(codec,
+                       RT286_SET_AUDIO_POWER, AC_PWRST_D3);
+               snd_soc_update_bits(codec,
+                       RT286_DC_GAIN, 0x200, 0x0);
+               break;
+
+       default:
+               break;
+       }
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+static irqreturn_t rt286_irq(int irq, void *data)
+{
+       struct rt286_priv *rt286 = data;
+       bool hp = false;
+       bool mic = false;
+       int status = 0;
+
+       rt286_jack_detect(rt286->codec, &hp, &mic);
+
+       /* Clear IRQ */
+       snd_soc_update_bits(rt286->codec,
+                                       RT286_IRQ_CTRL, 0x1, 0x1);
+
+       if (hp == true)
+               status |= SND_JACK_HEADPHONE;
+
+       if (mic == true)
+               status |= SND_JACK_MICROPHONE;
+
+       snd_soc_jack_report(rt286->jack, status,
+               SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+       pm_wakeup_event(&rt286->i2c->dev, 300);
+
+       return IRQ_HANDLED;
+}
+
+static int rt286_probe(struct snd_soc_codec *codec)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+       codec->dapm.bias_level = SND_SOC_BIAS_OFF;
+       rt286->codec = codec;
+
+       return 0;
+}
+
+static int rt286_remove(struct snd_soc_codec *codec)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+       cancel_delayed_work_sync(&rt286->jack_detect_work);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt286_suspend(struct snd_soc_codec *codec)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(rt286->regmap, true);
+       regcache_mark_dirty(rt286->regmap);
+
+       return 0;
+}
+
+static int rt286_resume(struct snd_soc_codec *codec)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(rt286->regmap, false);
+       rt286_index_sync(codec);
+       regcache_sync(rt286->regmap);
+
+       return 0;
+}
+#else
+#define rt286_suspend NULL
+#define rt286_resume NULL
+#endif
+
+#define RT286_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT286_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt286_aif_dai_ops = {
+       .hw_params = rt286_hw_params,
+       .set_fmt = rt286_set_dai_fmt,
+       .set_sysclk = rt286_set_dai_sysclk,
+       .set_bclk_ratio = rt286_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt286_dai[] = {
+       {
+               .name = "rt286-aif1",
+               .id = RT286_AIF1,
+               .playback = {
+                       .stream_name = "AIF1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT286_STEREO_RATES,
+                       .formats = RT286_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT286_STEREO_RATES,
+                       .formats = RT286_FORMATS,
+               },
+               .ops = &rt286_aif_dai_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = "rt286-aif2",
+               .id = RT286_AIF2,
+               .playback = {
+                       .stream_name = "AIF2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT286_STEREO_RATES,
+                       .formats = RT286_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RT286_STEREO_RATES,
+                       .formats = RT286_FORMATS,
+               },
+               .ops = &rt286_aif_dai_ops,
+               .symmetric_rates = 1,
+       },
+
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt286 = {
+       .probe = rt286_probe,
+       .remove = rt286_remove,
+       .suspend = rt286_suspend,
+       .resume = rt286_resume,
+       .set_bias_level = rt286_set_bias_level,
+       .idle_bias_off = true,
+       .controls = rt286_snd_controls,
+       .num_controls = ARRAY_SIZE(rt286_snd_controls),
+       .dapm_widgets = rt286_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets),
+       .dapm_routes = rt286_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes),
+};
+
+static const struct regmap_config rt286_regmap = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .max_register = 0x02370100,
+       .volatile_reg = rt286_volatile_register,
+       .readable_reg = rt286_readable_register,
+       .reg_write = rt286_hw_write,
+       .reg_read = rt286_hw_read,
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = rt286_reg,
+       .num_reg_defaults = ARRAY_SIZE(rt286_reg),
+};
+
+static const struct i2c_device_id rt286_i2c_id[] = {
+       {"rt286", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
+
+static const struct acpi_device_id rt286_acpi_match[] = {
+       { "INT343A", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
+
+static int rt286_i2c_probe(struct i2c_client *i2c,
+                          const struct i2c_device_id *id)
+{
+       struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
+       struct rt286_priv *rt286;
+       int i, ret;
+
+       rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286),
+                               GFP_KERNEL);
+       if (NULL == rt286)
+               return -ENOMEM;
+
+       rt286->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt286_regmap);
+       if (IS_ERR(rt286->regmap)) {
+               ret = PTR_ERR(rt286->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       regmap_read(rt286->regmap,
+               RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret);
+       if (ret != RT286_VENDOR_ID) {
+               dev_err(&i2c->dev,
+                       "Device with ID register %x is not rt286\n", ret);
+               return -ENODEV;
+       }
+
+       rt286->index_cache = rt286_index_def;
+       rt286->i2c = i2c;
+       i2c_set_clientdata(i2c, rt286);
+
+       if (pdata)
+               rt286->pdata = *pdata;
+
+       regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
+
+       for (i = 0; i < RT286_POWER_REG_LEN; i++)
+               regmap_write(rt286->regmap,
+                       RT286_SET_POWER(rt286_support_power_controls[i]),
+                       AC_PWRST_D1);
+
+       if (!rt286->pdata.cbj_en) {
+               regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
+               regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
+               regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
+               regmap_update_bits(rt286->regmap,
+                                       RT286_CBJ_CTRL1, 0xf000, 0xb000);
+       } else {
+               regmap_update_bits(rt286->regmap,
+                                       RT286_CBJ_CTRL1, 0xf000, 0x5000);
+       }
+
+       mdelay(10);
+
+       if (!rt286->pdata.gpio2_en)
+               regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x4000);
+       else
+               regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0);
+
+       mdelay(10);
+
+       /*Power down LDO2*/
+       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0x8, 0x0);
+
+       /*Set depop parameter*/
+       regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
+       regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
+       regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
+
+       if (rt286->i2c->irq) {
+               regmap_update_bits(rt286->regmap,
+                                       RT286_IRQ_CTRL, 0x2, 0x2);
+
+               INIT_DELAYED_WORK(&rt286->jack_detect_work,
+                                       rt286_jack_detect_work);
+               schedule_delayed_work(&rt286->jack_detect_work,
+                                       msecs_to_jiffies(1250));
+
+               ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq,
+                       IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
+               if (ret != 0) {
+                       dev_err(&i2c->dev,
+                               "Failed to reguest IRQ: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt286,
+                                    rt286_dai, ARRAY_SIZE(rt286_dai));
+
+       return ret;
+}
+
+static int rt286_i2c_remove(struct i2c_client *i2c)
+{
+       struct rt286_priv *rt286 = i2c_get_clientdata(i2c);
+
+       if (i2c->irq)
+               free_irq(i2c->irq, rt286);
+       snd_soc_unregister_codec(&i2c->dev);
+
+       return 0;
+}
+
+
+static struct i2c_driver rt286_i2c_driver = {
+       .driver = {
+                  .name = "rt286",
+                  .owner = THIS_MODULE,
+                  .acpi_match_table = ACPI_PTR(rt286_acpi_match),
+                  },
+       .probe = rt286_i2c_probe,
+       .remove = rt286_i2c_remove,
+       .id_table = rt286_i2c_id,
+};
+
+module_i2c_driver(rt286_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT286 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt286.h b/sound/soc/codecs/rt286.h
new file mode 100644 (file)
index 0000000..b539b73
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * rt286.h  --  RT286 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.com>
+ *
+ * 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.
+ */
+
+#ifndef __RT286_H__
+#define __RT286_H__
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RT286_AUDIO_FUNCTION_GROUP                     0x01
+#define RT286_DAC_OUT1                                 0x02
+#define RT286_DAC_OUT2                                 0x03
+#define RT286_ADC_IN1                                  0x09
+#define RT286_ADC_IN2                                  0x08
+#define RT286_MIXER_IN                                 0x0b
+#define RT286_MIXER_OUT1                               0x0c
+#define RT286_MIXER_OUT2                               0x0d
+#define RT286_DMIC1                                    0x12
+#define RT286_DMIC2                                    0x13
+#define RT286_SPK_OUT                                  0x14
+#define RT286_MIC1                                     0x18
+#define RT286_LINE1                                    0x1a
+#define RT286_BEEP                                     0x1d
+#define RT286_SPDIF                                    0x1e
+#define RT286_VENDOR_REGISTERS                         0x20
+#define RT286_HP_OUT                                   0x21
+#define RT286_MIXER_IN1                                        0x22
+#define RT286_MIXER_IN2                                        0x23
+
+#define RT286_SET_PIN_SFT                              6
+#define RT286_SET_PIN_ENABLE                           0x40
+#define RT286_SET_PIN_DISABLE                          0
+#define RT286_SET_EAPD_HIGH                            0x2
+#define RT286_SET_EAPD_LOW                             0
+
+#define RT286_MUTE_SFT                                 7
+
+/* Verb commands */
+#define RT286_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
+#define RT286_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
+#define RT286_SET_AUDIO_POWER RT286_SET_POWER(RT286_AUDIO_FUNCTION_GROUP)
+#define RT286_SET_HPO_POWER RT286_SET_POWER(RT286_HP_OUT)
+#define RT286_SET_SPK_POWER RT286_SET_POWER(RT286_SPK_OUT)
+#define RT286_SET_DMIC1_POWER RT286_SET_POWER(RT286_DMIC1)
+#define RT286_SPK_MUX\
+       VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_SPK_OUT, 0)
+#define RT286_HPO_MUX\
+       VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_HP_OUT, 0)
+#define RT286_ADC0_MUX\
+       VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN1, 0)
+#define RT286_ADC1_MUX\
+       VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN2, 0)
+#define RT286_SET_MIC1\
+       VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_MIC1, 0)
+#define RT286_SET_PIN_HPO\
+       VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_HP_OUT, 0)
+#define RT286_SET_PIN_SPK\
+       VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_SPK_OUT, 0)
+#define RT286_SET_PIN_DMIC1\
+       VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_DMIC1, 0)
+#define RT286_SPK_EAPD\
+       VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT286_SPK_OUT, 0)
+#define RT286_SET_AMP_GAIN_HPO\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0)
+#define RT286_SET_AMP_GAIN_ADC_IN1\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0)
+#define RT286_SET_AMP_GAIN_ADC_IN2\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN2, 0)
+#define RT286_GET_HP_SENSE\
+       VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_HP_OUT, 0)
+#define RT286_GET_MIC1_SENSE\
+       VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_MIC1, 0)
+#define RT286_SET_DMIC2_DEFAULT\
+       VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT286_DMIC2, 0)
+#define RT286_DACL_GAIN\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0xa000)
+#define RT286_DACR_GAIN\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0x9000)
+#define RT286_ADCL_GAIN\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x6000)
+#define RT286_ADCR_GAIN\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x5000)
+#define RT286_MIC_GAIN\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIC1, 0x7000)
+#define RT286_SPOL_GAIN\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0xa000)
+#define RT286_SPOR_GAIN\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0x9000)
+#define RT286_HPOL_GAIN\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0xa000)
+#define RT286_HPOR_GAIN\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0x9000)
+#define RT286_F_DAC_SWITCH\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7000)
+#define RT286_F_RECMIX_SWITCH\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7100)
+#define RT286_REC_MIC_SWITCH\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7000)
+#define RT286_REC_I2S_SWITCH\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7100)
+#define RT286_REC_LINE_SWITCH\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7200)
+#define RT286_REC_BEEP_SWITCH\
+       VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7300)
+#define RT286_DAC_FORMAT\
+       VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_DAC_OUT1, 0)
+#define RT286_ADC_FORMAT\
+       VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_ADC_IN1, 0)
+#define RT286_COEF_INDEX\
+       VERB_CMD(AC_VERB_SET_COEF_INDEX, RT286_VENDOR_REGISTERS, 0)
+#define RT286_PROC_COEF\
+       VERB_CMD(AC_VERB_SET_PROC_COEF, RT286_VENDOR_REGISTERS, 0)
+
+/* Index registers */
+#define RT286_A_BIAS_CTRL1     0x01
+#define RT286_A_BIAS_CTRL2     0x02
+#define RT286_POWER_CTRL1      0x03
+#define RT286_A_BIAS_CTRL3     0x04
+#define RT286_POWER_CTRL2      0x08
+#define RT286_I2S_CTRL1                0x09
+#define RT286_I2S_CTRL2                0x0a
+#define RT286_CLK_DIV          0x0b
+#define RT286_DC_GAIN          0x0d
+#define RT286_POWER_CTRL3      0x0f
+#define RT286_MIC1_DET_CTRL    0x19
+#define RT286_MISC_CTRL1       0x20
+#define RT286_IRQ_CTRL         0x33
+#define RT286_PLL_CTRL1                0x49
+#define RT286_CBJ_CTRL1                0x4f
+#define RT286_CBJ_CTRL2                0x50
+#define RT286_PLL_CTRL         0x63
+#define RT286_DEPOP_CTRL1      0x66
+#define RT286_DEPOP_CTRL2      0x67
+#define RT286_DEPOP_CTRL3      0x68
+#define RT286_DEPOP_CTRL4      0x69
+
+/* SPDIF (0x06) */
+#define RT286_SPDIF_SEL_SFT    0
+#define RT286_SPDIF_SEL_PCM0   0
+#define RT286_SPDIF_SEL_PCM1   1
+#define RT286_SPDIF_SEL_SPOUT  2
+#define RT286_SPDIF_SEL_PP     3
+
+/* RECMIX (0x0b) */
+#define RT286_M_REC_BEEP_SFT   0
+#define RT286_M_REC_LINE1_SFT  1
+#define RT286_M_REC_MIC1_SFT   2
+#define RT286_M_REC_I2S_SFT    3
+
+/* Front (0x0c) */
+#define RT286_M_FRONT_DAC_SFT  0
+#define RT286_M_FRONT_REC_SFT  1
+
+/* SPK-OUT (0x14) */
+#define RT286_M_SPK_MUX_SFT    14
+#define RT286_SPK_SEL_MASK     0x1
+#define RT286_SPK_SEL_SFT      0
+#define RT286_SPK_SEL_F                0
+#define RT286_SPK_SEL_S                1
+
+/* HP-OUT (0x21) */
+#define RT286_M_HP_MUX_SFT     14
+#define RT286_HP_SEL_MASK      0x1
+#define RT286_HP_SEL_SFT       0
+#define RT286_HP_SEL_F         0
+#define RT286_HP_SEL_S         1
+
+/* ADC (0x22) (0x23) */
+#define RT286_ADC_SEL_MASK     0x7
+#define RT286_ADC_SEL_SFT      0
+#define RT286_ADC_SEL_SURR     0
+#define RT286_ADC_SEL_FRONT    1
+#define RT286_ADC_SEL_DMIC     2
+#define RT286_ADC_SEL_BEEP     4
+#define RT286_ADC_SEL_LINE1    5
+#define RT286_ADC_SEL_I2S      6
+#define RT286_ADC_SEL_MIC1     7
+
+#define RT286_SCLK_S_MCLK      0
+#define RT286_SCLK_S_PLL       1
+
+enum {
+       RT286_AIF1,
+       RT286_AIF2,
+       RT286_AIFS,
+};
+
+int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+#endif /* __RT286_H__ */
+
index c30fedb..f5b4a9c 100644 (file)
@@ -58,3 +58,15 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
        help
          This adds audio driver for Intel Baytrail platform based boards
          with the MAX98090 audio codec.
+
+config SND_SOC_INTEL_BROADWELL_MACH
+       tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
+       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC
+       select SND_SOC_INTEL_HASWELL
+       select SND_COMPRESS_OFFLOAD
+       select SND_SOC_RT286
+       help
+         This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
+         Ultrabook platforms.
+         Say Y if you have such a device
+         If unsure select "N".
index 4bfca79..7acbfc4 100644 (file)
@@ -24,7 +24,9 @@ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
 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
 
 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
diff --git a/sound/soc/intel/broadwell.c b/sound/soc/intel/broadwell.c
new file mode 100644 (file)
index 0000000..0e550f1
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * 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/pcm_params.h>
+
+#include "sst-dsp.h"
+#include "sst-haswell-ipc.h"
+
+#include "../codecs/rt286.h"
+
+static const struct snd_soc_dapm_widget broadwell_widgets[] = {
+       SND_SOC_DAPM_HP("Headphones", 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 */
+       {"Headphones", 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_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
+                       struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                       SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       /* The ADSP will covert the FE rate to 48k, stereo */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP0 to 16 bit */
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   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 snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       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;
+       }
+
+       /* always connected - check HP for jack detect */
+       snd_soc_dapm_enable_pin(dapm, "Headphones");
+       snd_soc_dapm_enable_pin(dapm, "Speaker");
+       snd_soc_dapm_enable_pin(dapm, "Mic Jack");
+       snd_soc_dapm_enable_pin(dapm, "Line Jack");
+       snd_soc_dapm_enable_pin(dapm, "DMIC1");
+       snd_soc_dapm_enable_pin(dapm, "DMIC2");
+
+       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",
+               .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,
+       },
+       {
+               .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,
+       },
+       {
+               .name = "Capture PCM",
+               .stream_name = "Capture",
+               .cpu_dai_name = "Capture 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_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",
+               .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,
+       },
+};
+
+/* 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),
+       .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,
+};
+
+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",
+               .owner = THIS_MODULE,
+       },
+};
+
+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");
index 5cfb41e..b8b8af5 100644 (file)
@@ -63,14 +63,6 @@ static struct snd_soc_jack_pin hs_jack_pins[] = {
                .pin    = "Headset Mic",
                .mask   = SND_JACK_MICROPHONE,
        },
-       {
-               .pin    = "Ext Spk",
-               .mask   = SND_JACK_LINEOUT,
-       },
-       {
-               .pin    = "Int Mic",
-               .mask   = SND_JACK_LINEIN,
-       },
 };
 
 static struct snd_soc_jack_gpio hs_jack_gpios[] = {
index 53d160d..234a58d 100644 (file)
@@ -34,6 +34,7 @@ static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
+       {"Headset Mic", NULL, "MICBIAS1"},
        {"IN2P", NULL, "Headset Mic"},
        {"IN2N", NULL, "Headset Mic"},
        {"DMIC1", NULL, "Internal Mic"},
diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h
new file mode 100644 (file)
index 0000000..14063ab
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *  Copyright (C) 2013-14 Intel Corp
+ *  Author: Ramesh Babu <ramesh.babu.koul@intel.com>
+ *     Omair M Abdullah <omair.m.abdullah@intel.com>
+ *     Samreen Nilofer <samreen.nilofer@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#ifndef __SST_CONTROLS_V2_H__
+#define __SST_CONTROLS_V2_H__
+
+enum {
+       MERR_DPCM_AUDIO = 0,
+       MERR_DPCM_COMPR,
+};
+
+
+#endif
index d207b22..67673a2 100644 (file)
@@ -122,6 +122,26 @@ struct sst_byt_tstamp {
        u32 channel_peak[8];
 } __packed;
 
+struct sst_byt_fw_version {
+       u8 build;
+       u8 minor;
+       u8 major;
+       u8 type;
+} __packed;
+
+struct sst_byt_fw_build_info {
+       u8 date[16];
+       u8 time[16];
+} __packed;
+
+struct sst_byt_fw_init {
+       struct sst_byt_fw_version fw_version;
+       struct sst_byt_fw_build_info build_info;
+       u16 result;
+       u8 module_id;
+       u8 debug_info;
+} __packed;
+
 /* driver internal IPC message structure */
 struct ipc_message {
        struct list_head list;
@@ -868,6 +888,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
 {
        struct sst_byt *byt;
        struct sst_fw *byt_sst_fw;
+       struct sst_byt_fw_init init;
        int err;
 
        dev_dbg(dev, "initialising Byt DSP IPC\n");
@@ -929,6 +950,15 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
                goto boot_err;
        }
 
+       /* show firmware information */
+       sst_dsp_inbox_read(byt->dsp, &init, sizeof(init));
+       dev_info(byt->dev, "FW version: %02x.%02x.%02x.%02x\n",
+                init.fw_version.major, init.fw_version.minor,
+                init.fw_version.build, init.fw_version.type);
+       dev_info(byt->dev, "Build type: %x\n", init.fw_version.type);
+       dev_info(byt->dev, "Build date: %s %s\n",
+                init.build_info.date, init.build_info.time);
+
        pdata->dsp = byt;
        byt->fw = byt_sst_fw;
 
index 0b715b2..cd23060 100644 (file)
@@ -224,19 +224,23 @@ EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
 
 void sst_dsp_dump(struct sst_dsp *sst)
 {
-       sst->ops->dump(sst);
+       if (sst->ops->dump)
+               sst->ops->dump(sst);
 }
 EXPORT_SYMBOL_GPL(sst_dsp_dump);
 
 void sst_dsp_reset(struct sst_dsp *sst)
 {
-       sst->ops->reset(sst);
+       if (sst->ops->reset)
+               sst->ops->reset(sst);
 }
 EXPORT_SYMBOL_GPL(sst_dsp_reset);
 
 int sst_dsp_boot(struct sst_dsp *sst)
 {
-       sst->ops->boot(sst);
+       if (sst->ops->boot)
+               sst->ops->boot(sst);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(sst_dsp_boot);
index e44423b..3165dfa 100644 (file)
 #define SST_CLKCTL             0x78
 #define SST_CSR2               0x80
 #define SST_LTRC               0xE0
-#define SST_HDMC               0xE8
+#define SST_HMDC               0xE8
+
+#define SST_SHIM_BEGIN         SST_CSR
+#define SST_SHIM_END           SST_HDMC
+
 #define SST_DBGO               0xF0
 
 #define SST_SHIM_SIZE          0x100
@@ -73,6 +77,8 @@
 #define SST_CSR_S0IOCS         (0x1 << 21)
 #define SST_CSR_S1IOCS         (0x1 << 23)
 #define SST_CSR_LPCS           (0x1 << 31)
+#define SST_CSR_24MHZ_LPCS     (SST_CSR_SBCS0 | SST_CSR_SBCS1 | SST_CSR_LPCS)
+#define SST_CSR_24MHZ_NO_LPCS  (SST_CSR_SBCS0 | SST_CSR_SBCS1)
 #define SST_BYT_CSR_RST                (0x1 << 0)
 #define SST_BYT_CSR_VECTOR_SEL (0x1 << 1)
 #define SST_BYT_CSR_STALL      (0x1 << 2)
 #define SST_IMRX_DONE          (0x1 << 0)
 #define SST_BYT_IMRX_REQUEST   (0x1 << 1)
 
+/* IMRD / IMD */
+#define SST_IMRD_DONE          (0x1 << 0)
+#define SST_IMRD_BUSY          (0x1 << 1)
+#define SST_IMRD_SSP0          (0x1 << 16)
+#define SST_IMRD_DMAC0         (0x1 << 21)
+#define SST_IMRD_DMAC1         (0x1 << 22)
+#define SST_IMRD_DMAC          (SST_IMRD_DMAC0 | SST_IMRD_DMAC1)
+
 /*  IPCX / IPCC */
 #define        SST_IPCX_DONE           (0x1 << 30)
 #define        SST_IPCX_BUSY           (0x1 << 31)
 /* LTRC */
 #define SST_LTRC_VAL(x)                (x << 0)
 
-/* HDMC */
-#define SST_HDMC_HDDA0(x)      (x << 0)
-#define SST_HDMC_HDDA1(x)      (x << 7)
+/* HMDC */
+#define SST_HMDC_HDDA0(x)      (x << 0)
+#define SST_HMDC_HDDA1(x)      (x << 7)
+#define SST_HMDC_HDDA_E0_CH0   1
+#define SST_HMDC_HDDA_E0_CH1   2
+#define SST_HMDC_HDDA_E0_CH2   4
+#define SST_HMDC_HDDA_E0_CH3   8
+#define SST_HMDC_HDDA_E1_CH0   SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH0)
+#define SST_HMDC_HDDA_E1_CH1   SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH1)
+#define SST_HMDC_HDDA_E1_CH2   SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH2)
+#define SST_HMDC_HDDA_E1_CH3   SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH3)
+#define SST_HMDC_HDDA_E0_ALLCH (SST_HMDC_HDDA_E0_CH0 | SST_HMDC_HDDA_E0_CH1 | \
+                                SST_HMDC_HDDA_E0_CH2 | SST_HMDC_HDDA_E0_CH3)
+#define SST_HMDC_HDDA_E1_ALLCH (SST_HMDC_HDDA_E1_CH0 | SST_HMDC_HDDA_E1_CH1 | \
+                                SST_HMDC_HDDA_E1_CH2 | SST_HMDC_HDDA_E1_CH3)
 
 
 /* SST Vendor Defined Registers and bits */
 #define SST_VDRTCTL3           0xaC
 
 /* VDRTCTL0 */
+#define SST_VDRTCL0_APLLSE_MASK                1
 #define SST_VDRTCL0_DSRAMPGE_SHIFT     16
 #define SST_VDRTCL0_DSRAMPGE_MASK      (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
 #define SST_VDRTCL0_ISRAMPGE_SHIFT     6
 #define SST_VDRTCL0_ISRAMPGE_MASK      (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
 
+/* PMCS */
+#define SST_PMCS               0x84
+#define SST_PMCS_PS_MASK       0x3
+
 struct sst_dsp;
 
 /*
index a33b931..4b6c163 100644 (file)
@@ -28,9 +28,6 @@
 #include <linux/firmware.h>
 #include <linux/pm_runtime.h>
 
-#include <linux/acpi.h>
-#include <acpi/acpi_bus.h>
-
 #include "sst-dsp.h"
 #include "sst-dsp-priv.h"
 #include "sst-haswell-ipc.h"
@@ -272,9 +269,9 @@ static void hsw_boot(struct sst_dsp *sst)
                SST_CSR2_SDFD_SSP1);
 
        /* enable DMA engine 0,1 all channels to access host memory */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_HDMC,
-               SST_HDMC_HDDA1(0xff)  | SST_HDMC_HDDA0(0xff),
-               SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff));
+       sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC,
+               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff),
+               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff));
 
        /* disable all clock gating */
        writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
@@ -313,9 +310,7 @@ static const struct sst_adsp_memregion lp_region[] = {
 
 /* wild cat point ADSP mem regions */
 static const struct sst_adsp_memregion wpt_region[] = {
-       {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
-       {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
-       {0x80000, 0xA0000, 4, SST_MEM_DRAM}, /* D-SRAM2 - 4 * 32kB */
+       {0x00000, 0xA0000, 20, SST_MEM_DRAM}, /* D-SRAM0,D-SRAM1,D-SRAM2 - 20 * 32kB */
        {0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */
 };
 
@@ -339,21 +334,40 @@ static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
        return 0;
 }
 
+struct sst_sram_shift {
+       u32 dev_id;     /* SST Device IDs  */
+       u32 iram_shift;
+       u32 dram_shift;
+};
+
+static const struct sst_sram_shift sram_shift[] = {
+       {SST_DEV_ID_LYNX_POINT, 6, 16}, /* lp */
+       {SST_DEV_ID_WILDCAT_POINT, 2, 12}, /* wpt */
+};
 static u32 hsw_block_get_bit(struct sst_mem_block *block)
 {
-       u32 bit = 0, shift = 0;
+       u32 bit = 0, shift = 0, index;
+       struct sst_dsp *sst = block->dsp;
 
-       switch (block->type) {
-       case SST_MEM_DRAM:
-               shift = 16;
-               break;
-       case SST_MEM_IRAM:
-               shift = 6;
-               break;
-       default:
-               return 0;
+       for (index = 0; index < ARRAY_SIZE(sram_shift); index++) {
+               if (sram_shift[index].dev_id == sst->id)
+                       break;
        }
 
+       if (index < ARRAY_SIZE(sram_shift)) {
+               switch (block->type) {
+               case SST_MEM_DRAM:
+                       shift = sram_shift[index].dram_shift;
+                       break;
+               case SST_MEM_IRAM:
+                       shift = sram_shift[index].iram_shift;
+                       break;
+               default:
+                       shift = 0;
+               }
+       } else
+               shift = 0;
+
        bit = 1 << (block->index + shift);
 
        return bit;
@@ -501,8 +515,9 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
                }
        }
 
-       /* set default power gating mask */
-       writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL0);
+       /* set default power gating control, enable power gating control for all blocks. that is,
+       can't be accessed, please enable each block before accessing. */
+       writel(0xffffffff, sst->addr.pci_cfg + SST_VDRTCTL0);
 
        return 0;
 }
index 4342363..b629151 100644 (file)
@@ -183,7 +183,7 @@ struct sst_hsw_ipc_fw_ready {
        u32 inbox_size;
        u32 outbox_size;
        u32 fw_info_size;
-       u8 fw_info[1];
+       u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
 } __attribute__((packed));
 
 struct ipc_message {
@@ -457,9 +457,10 @@ static void ipc_tx_msgs(struct kthread_work *work)
                return;
        }
 
-       /* if the DSP is busy we will TX messages after IRQ */
+       /* if the DSP is busy, we will TX messages after IRQ.
+        * also postpone if we are in the middle of procesing completion irq*/
        ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX);
-       if (ipcx & SST_IPCX_BUSY) {
+       if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
                spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
                return;
        }
@@ -502,6 +503,7 @@ static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg,
                ipc_shim_dbg(hsw, "message timeout");
 
                trace_ipc_error("error message timeout for", msg->header);
+               list_del(&msg->list);
                ret = -ETIMEDOUT;
        } else {
 
@@ -569,6 +571,9 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
 {
        struct sst_hsw_ipc_fw_ready fw_ready;
        u32 offset;
+       u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
+       char *tmp[5], *pinfo;
+       int i = 0;
 
        offset = (header & 0x1FFFFFFF) << 3;
 
@@ -589,6 +594,19 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
                fw_ready.inbox_offset, fw_ready.inbox_size);
        dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n",
                fw_ready.outbox_offset, fw_ready.outbox_size);
+       if (fw_ready.fw_info_size < sizeof(fw_ready.fw_info)) {
+               fw_ready.fw_info[fw_ready.fw_info_size] = 0;
+               dev_dbg(hsw->dev, " Firmware info: %s \n", fw_ready.fw_info);
+
+               /* log the FW version info got from the mailbox here. */
+               memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size);
+               pinfo = &fw_info[0];
+               for (i = 0; i < sizeof(tmp) / sizeof(char *); i++)
+                       tmp[i] = strsep(&pinfo, " ");
+               dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - "
+                       "version: %s.%s, build %s, source commit id: %s\n",
+                       tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
+       }
 }
 
 static void hsw_notification_work(struct work_struct *work)
@@ -671,7 +689,9 @@ static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
        switch (stream_msg) {
        case IPC_STR_STAGE_MESSAGE:
        case IPC_STR_NOTIFICATION:
+               break;
        case IPC_STR_RESET:
+               trace_ipc_notification("stream reset", stream->reply.stream_hw_id);
                break;
        case IPC_STR_PAUSE:
                stream->running = false;
@@ -762,7 +782,8 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
        }
 
        /* update any stream states */
-       hsw_stream_update(hsw, msg);
+       if (msg_get_global_type(header) == IPC_GLB_STREAM_MESSAGE)
+               hsw_stream_update(hsw, msg);
 
        /* wake up and return the error if we have waiters on this message ? */
        list_del(&msg->list);
@@ -1628,7 +1649,7 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
        enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx)
 {
        u32 header, state_;
-       int ret;
+       int ret, item;
 
        header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE);
        state_ = state;
@@ -1642,6 +1663,13 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
                return ret;
        }
 
+       for (item = 0; item < dx->entries_no; item++) {
+               dev_dbg(hsw->dev,
+                       "Item[%d] offset[%x] - size[%x] - source[%x]\n",
+                       item, dx->mem_info[item].offset,
+                       dx->mem_info[item].size,
+                       dx->mem_info[item].source);
+       }
        dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
                dx->entries_no, state);
 
@@ -1775,8 +1803,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 
        /* get the FW version */
        sst_hsw_fw_get_version(hsw, &version);
-       dev_info(hsw->dev, "FW loaded: type %d - version: %d.%d build %d\n",
-               version.type, version.major, version.minor, version.build);
 
        /* get the globalmixer */
        ret = sst_hsw_mixer_get_info(hsw);
index 8d482d7..4257263 100644 (file)
@@ -3,7 +3,7 @@
 /*
  *  sst_mfld_dsp.h - Intel SST Driver for audio engine
  *
- *  Copyright (C) 2008-12 Intel Corporation
+ *  Copyright (C) 2008-14 Intel Corporation
  *  Authors:   Vinod Koul <vinod.koul@linux.intel.com>
  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 
+#define SST_MAX_BIN_BYTES 1024
+
+#define MAX_DBG_RW_BYTES 80
+#define MAX_NUM_SCATTER_BUFFERS 8
+#define MAX_LOOP_BACK_DWORDS 8
+/* IPC base address and mailbox, timestamp offsets */
+#define SST_MAILBOX_SIZE 0x0400
+#define SST_MAILBOX_SEND 0x0000
+#define SST_TIME_STAMP 0x1800
+#define SST_TIME_STAMP_MRFLD 0x800
+#define SST_RESERVED_OFFSET 0x1A00
+#define SST_SCU_LPE_MAILBOX 0x1000
+#define SST_LPE_SCU_MAILBOX 0x1400
+#define SST_SCU_LPE_LOG_BUF (SST_SCU_LPE_MAILBOX+16)
+#define PROCESS_MSG 0x80
+
+/* Message ID's for IPC messages */
+/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */
+
+/* I2L Firmware/Codec Download msgs */
+#define IPC_IA_PREP_LIB_DNLD 0x01
+#define IPC_IA_LIB_DNLD_CMPLT 0x02
+#define IPC_IA_GET_FW_VERSION 0x04
+#define IPC_IA_GET_FW_BUILD_INF 0x05
+#define IPC_IA_GET_FW_INFO 0x06
+#define IPC_IA_GET_FW_CTXT 0x07
+#define IPC_IA_SET_FW_CTXT 0x08
+#define IPC_IA_PREPARE_SHUTDOWN 0x31
+/* I2L Codec Config/control msgs */
+#define IPC_PREP_D3 0x10
+#define IPC_IA_SET_CODEC_PARAMS 0x10
+#define IPC_IA_GET_CODEC_PARAMS 0x11
+#define IPC_IA_SET_PPP_PARAMS 0x12
+#define IPC_IA_GET_PPP_PARAMS 0x13
+#define IPC_SST_PERIOD_ELAPSED_MRFLD 0xA
+#define IPC_IA_ALG_PARAMS 0x1A
+#define IPC_IA_TUNING_PARAMS 0x1B
+#define IPC_IA_SET_RUNTIME_PARAMS 0x1C
+#define IPC_IA_SET_PARAMS 0x1
+#define IPC_IA_GET_PARAMS 0x2
+
+#define IPC_EFFECTS_CREATE 0xE
+#define IPC_EFFECTS_DESTROY 0xF
+
+/* I2L Stream config/control msgs */
+#define IPC_IA_ALLOC_STREAM_MRFLD 0x2
+#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */
+#define IPC_IA_FREE_STREAM_MRFLD 0x03
+#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */
+#define IPC_IA_SET_STREAM_PARAMS 0x22
+#define IPC_IA_SET_STREAM_PARAMS_MRFLD 0x12
+#define IPC_IA_GET_STREAM_PARAMS 0x23
+#define IPC_IA_PAUSE_STREAM 0x24
+#define IPC_IA_PAUSE_STREAM_MRFLD 0x4
+#define IPC_IA_RESUME_STREAM 0x25
+#define IPC_IA_RESUME_STREAM_MRFLD 0x5
+#define IPC_IA_DROP_STREAM 0x26
+#define IPC_IA_DROP_STREAM_MRFLD 0x07
+#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */
+#define IPC_IA_DRAIN_STREAM_MRFLD 0x8
+#define IPC_IA_CONTROL_ROUTING 0x29
+#define IPC_IA_VTSV_UPDATE_MODULES 0x20
+#define IPC_IA_VTSV_DETECTED 0x21
+
+#define IPC_IA_START_STREAM_MRFLD 0X06
+#define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */
+
+#define IPC_IA_SET_GAIN_MRFLD 0x21
+/* Debug msgs */
+#define IPC_IA_DBG_MEM_READ 0x40
+#define IPC_IA_DBG_MEM_WRITE 0x41
+#define IPC_IA_DBG_LOOP_BACK 0x42
+#define IPC_IA_DBG_LOG_ENABLE 0x45
+#define IPC_IA_DBG_SET_PROBE_PARAMS 0x47
+
+/* L2I Firmware/Codec Download msgs */
+#define IPC_IA_FW_INIT_CMPLT 0x81
+#define IPC_IA_FW_INIT_CMPLT_MRFLD 0x01
+#define IPC_IA_FW_ASYNC_ERR_MRFLD 0x11
+
+/* L2I Codec Config/control msgs */
+#define IPC_SST_FRAGMENT_ELPASED 0x90 /* Request IA more data */
+
+#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */
+#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */
+#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */
+#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */
+#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */
+#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */
+
+#define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */
+/* L2S messages */
+#define IPC_SC_DDR_LINK_UP 0xC0
+#define IPC_SC_DDR_LINK_DOWN 0xC1
+#define IPC_SC_SET_LPECLK_REQ 0xC2
+#define IPC_SC_SSP_BIT_BANG 0xC3
+
+/* L2I Error reporting msgs */
+#define IPC_IA_MEM_ALLOC_FAIL 0xE0
+#define IPC_IA_PROC_ERR 0xE1 /* error in processing a
+                                       stream can be used by playback and
+                                       capture modules */
+
+/* L2I Debug msgs */
+#define IPC_IA_PRINT_STRING 0xF0
+
+/* Buffer under-run */
+#define IPC_IA_BUF_UNDER_RUN_MRFLD 0x0B
+
+/* Mrfld specific defines:
+ * For asynchronous messages(INIT_CMPLT, PERIOD_ELAPSED, ASYNC_ERROR)
+ * received from FW, the format is:
+ *  - IPC High: pvt_id is set to zero. Always short message.
+ *  - msg_id is in lower 16-bits of IPC low payload.
+ *  - pipe_id is in higher 16-bits of IPC low payload for period_elapsed.
+ *  - error id is in higher 16-bits of IPC low payload for async errors.
+ */
+#define SST_ASYNC_DRV_ID 0
+
+/* Command Response or Acknowledge message to any IPC message will have
+ * same message ID and stream ID information which is sent.
+ * There is no specific Ack message ID. The data field is used as response
+ * meaning.
+ */
+enum ackData {
+       IPC_ACK_SUCCESS = 0,
+       IPC_ACK_FAILURE,
+};
+
+enum ipc_ia_msg_id {
+       IPC_CMD = 1,            /*!< Task Control message ID */
+       IPC_SET_PARAMS = 2,/*!< Task Set param message ID */
+       IPC_GET_PARAMS = 3,     /*!< Task Get param message ID */
+       IPC_INVALID = 0xFF,     /*!<Task Get param message ID */
+};
+
 enum sst_codec_types {
        /*  AUDIO/MUSIC CODEC Type Definitions */
        SST_CODEC_TYPE_UNKNOWN = 0,
@@ -35,14 +171,157 @@ enum stream_type {
        SST_STREAM_TYPE_MUSIC = 1,
 };
 
+enum sst_error_codes {
+       /* Error code,response to msgId: Description */
+       /* Common error codes */
+       SST_SUCCESS = 0,        /* Success */
+       SST_ERR_INVALID_STREAM_ID = 1,
+       SST_ERR_INVALID_MSG_ID = 2,
+       SST_ERR_INVALID_STREAM_OP = 3,
+       SST_ERR_INVALID_PARAMS = 4,
+       SST_ERR_INVALID_CODEC = 5,
+       SST_ERR_INVALID_MEDIA_TYPE = 6,
+       SST_ERR_STREAM_ERR = 7,
+
+       SST_ERR_STREAM_IN_USE = 15,
+};
+
+struct ipc_dsp_hdr {
+       u16 mod_index_id:8;             /*!< DSP Command ID specific to tasks */
+       u16 pipe_id:8;  /*!< instance of the module in the pipeline */
+       u16 mod_id;             /*!< Pipe_id */
+       u16 cmd_id;             /*!< Module ID = lpe_algo_types_t */
+       u16 length;             /*!< Length of the payload only */
+} __packed;
+
+union ipc_header_high {
+       struct {
+               u32  msg_id:8;      /* Message ID - Max 256 Message Types */
+               u32  task_id:4;     /* Task ID associated with this comand */
+               u32  drv_id:4;    /* Identifier for the driver to track*/
+               u32  rsvd1:8;       /* Reserved */
+               u32  result:4;      /* Reserved */
+               u32  res_rqd:1;     /* Response rqd */
+               u32  large:1;       /* Large Message if large = 1 */
+               u32  done:1;        /* bit 30 - Done bit */
+               u32  busy:1;        /* bit 31 - busy bit*/
+       } part;
+       u32 full;
+} __packed;
+/* IPC header */
+union ipc_header_mrfld {
+       struct {
+               u32 header_low_payload;
+               union ipc_header_high header_high;
+       } p;
+       u64 full;
+} __packed;
+/* CAUTION NOTE: All IPC message body must be multiple of 32 bits.*/
+
+/* IPC Header */
+union ipc_header {
+       struct {
+               u32  msg_id:8; /* Message ID - Max 256 Message Types */
+               u32  str_id:5;
+               u32  large:1;   /* Large Message if large = 1 */
+               u32  reserved:2;        /* Reserved for future use */
+               u32  data:14;   /* Ack/Info for msg, size of msg in Mailbox */
+               u32  done:1; /* bit 30 */
+               u32  busy:1; /* bit 31 */
+       } part;
+       u32 full;
+} __packed;
+
+/* Firmware build info */
+struct sst_fw_build_info {
+       unsigned char  date[16]; /* Firmware build date */
+       unsigned char  time[16]; /* Firmware build time */
+} __packed;
+
+/* Firmware Version info */
+struct snd_sst_fw_version {
+       u8 build;       /* build number*/
+       u8 minor;       /* minor number*/
+       u8 major;       /* major number*/
+       u8 type;        /* build type */
+};
+
+struct ipc_header_fw_init {
+       struct snd_sst_fw_version fw_version;/* Firmware version details */
+       struct sst_fw_build_info build_info;
+       u16 result;     /* Fw init result */
+       u8 module_id; /* Module ID in case of error */
+       u8 debug_info; /* Debug info from Module ID in case of fail */
+} __packed;
+
+struct snd_sst_tstamp {
+       u64 ring_buffer_counter;        /* PB/CP: Bytes copied from/to DDR. */
+       u64 hardware_counter;       /* PB/CP: Bytes DMAed to/from SSP. */
+       u64 frames_decoded;
+       u64 bytes_decoded;
+       u64 bytes_copied;
+       u32 sampling_frequency;
+       u32 channel_peak[8];
+} __packed;
+
+/* Stream type params struture for Alloc stream */
+struct snd_sst_str_type {
+       u8 codec_type;          /* Codec type */
+       u8 str_type;            /* 1 = voice 2 = music */
+       u8 operation;           /* Playback or Capture */
+       u8 protected_str;       /* 0=Non DRM, 1=DRM */
+       u8 time_slots;
+       u8 reserved;            /* Reserved */
+       u16 result;             /* Result used for acknowledgment */
+} __packed;
+
+/* Library info structure */
+struct module_info {
+       u32 lib_version;
+       u32 lib_type;/*TBD- KLOCKWORK u8 lib_type;*/
+       u32 media_type;
+       u8  lib_name[12];
+       u32 lib_caps;
+       unsigned char  b_date[16]; /* Lib build date */
+       unsigned char  b_time[16]; /* Lib build time */
+} __packed;
+
+/* Library slot info */
+struct lib_slot_info {
+       u8  slot_num; /* 1 or 2 */
+       u8  reserved1;
+       u16 reserved2;
+       u32 iram_size; /* slot size in IRAM */
+       u32 dram_size; /* slot size in DRAM */
+       u32 iram_offset; /* starting offset of slot in IRAM */
+       u32 dram_offset; /* starting offset of slot in DRAM */
+} __packed;
+
+struct snd_ppp_mixer_params {
+       __u32                   type; /*Type of the parameter */
+       __u32                   size;
+       __u32                   input_stream_bitmap; /*Input stream Bit Map*/
+} __packed;
+
+struct snd_sst_lib_download {
+       struct module_info lib_info; /* library info type, capabilities etc */
+       struct lib_slot_info slot_info; /* slot info to be downloaded */
+       u32 mod_entry_pt;
+};
+
+struct snd_sst_lib_download_info {
+       struct snd_sst_lib_download dload_lib;
+       u16 result;     /* Result used for acknowledgment */
+       u8 pvt_id; /* Private ID */
+       u8 reserved;  /* for alignment */
+};
 struct snd_pcm_params {
        u8 num_chan;    /* 1=Mono, 2=Stereo */
        u8 pcm_wd_sz;   /* 16/24 - bit*/
-       u32 reserved;   /* Bitrate in bits per second */
-       u32 sfreq;      /* Sampling rate in Hz */
-       u8 use_offload_path;
+       u8 use_offload_path;    /* 0-PCM using period elpased & ALSA interfaces
+                                  1-PCM stream via compressed interface  */
        u8 reserved2;
-       u16 reserved3;
+       u32 sfreq;    /* Sampling rate in Hz */
        u8 channel_map[8];
 } __packed;
 
@@ -76,6 +355,7 @@ struct snd_aac_params {
 struct snd_wma_params {
        u8  num_chan;   /* 1=Mono, 2=Stereo */
        u8  pcm_wd_sz;  /* 16/24 - bit*/
+       u16 reserved1;
        u32 brate;      /* Use the hard coded value. */
        u32 sfreq;      /* Sampling freq eg. 8000, 441000, 48000 */
        u32 channel_mask;  /* Channel Mask */
@@ -101,26 +381,153 @@ struct sst_address_info {
 };
 
 struct snd_sst_alloc_params_ext {
-       struct sst_address_info  ring_buf_info[8];
-       u8 sg_count;
-       u8 reserved;
-       u16 reserved2;
-       u32 frag_size;  /*Number of samples after which period elapsed
+       __u16 sg_count;
+       __u16 reserved;
+       __u32 frag_size;        /*Number of samples after which period elapsed
                                  message is sent valid only if path  = 0*/
-} __packed;
+       struct sst_address_info  ring_buf_info[8];
+};
 
 struct snd_sst_stream_params {
        union snd_sst_codec_params uc;
 } __packed;
 
 struct snd_sst_params {
+       u32 result;
        u32 stream_id;
        u8 codec;
        u8 ops;
        u8 stream_type;
        u8 device_type;
+       u8 task;
        struct snd_sst_stream_params sparams;
        struct snd_sst_alloc_params_ext aparams;
 };
 
+struct snd_sst_alloc_mrfld {
+       u16 codec_type;
+       u8 operation;
+       u8 sg_count;
+       struct sst_address_info ring_buf_info[8];
+       u32 frag_size;
+       u32 ts;
+       struct snd_sst_stream_params codec_params;
+} __packed;
+
+/* Alloc stream params structure */
+struct snd_sst_alloc_params {
+       struct snd_sst_str_type str_type;
+       struct snd_sst_stream_params stream_params;
+       struct snd_sst_alloc_params_ext alloc_params;
+} __packed;
+
+/* Alloc stream response message */
+struct snd_sst_alloc_response {
+       struct snd_sst_str_type str_type; /* Stream type for allocation */
+       struct snd_sst_lib_download lib_dnld; /* Valid only for codec dnld */
+};
+
+/* Drop response */
+struct snd_sst_drop_response {
+       u32 result;
+       u32 bytes;
+};
+
+struct snd_sst_async_msg {
+       u32 msg_id; /* Async msg id */
+       u32 payload[0];
+};
+
+struct snd_sst_async_err_msg {
+       u32 fw_resp; /* Firmware Result */
+       u32 lib_resp; /*Library result */
+} __packed;
+
+struct snd_sst_vol {
+       u32     stream_id;
+       s32     volume;
+       u32     ramp_duration;
+       u32     ramp_type;              /* Ramp type, default=0 */
+};
+
+/* Gain library parameters for mrfld
+ * based on DSP command spec v0.82
+ */
+struct snd_sst_gain_v2 {
+       u16 gain_cell_num;  /* num of gain cells to modify*/
+       u8 cell_nbr_idx; /* instance index*/
+       u8 cell_path_idx; /* pipe-id */
+       u16 module_id; /*module id */
+       u16 left_cell_gain; /* left gain value in dB*/
+       u16 right_cell_gain; /* right gain value in dB*/
+       u16 gain_time_const; /* gain time constant*/
+} __packed;
+
+struct snd_sst_mute {
+       u32     stream_id;
+       u32     mute;
+};
+
+struct snd_sst_runtime_params {
+       u8 type;
+       u8 str_id;
+       u8 size;
+       u8 rsvd;
+       void *addr;
+} __packed;
+
+enum stream_param_type {
+       SST_SET_TIME_SLOT = 0,
+       SST_SET_CHANNEL_INFO = 1,
+       OTHERS = 2, /*reserved for future params*/
+};
+
+/* CSV Voice call routing structure */
+struct snd_sst_control_routing {
+       u8 control; /* 0=start, 1=Stop */
+       u8 reserved[3]; /* Reserved- for 32 bit alignment */
+};
+
+struct ipc_post {
+       struct list_head node;
+       union ipc_header header; /* driver specific */
+       bool is_large;
+       bool is_process_reply;
+       union ipc_header_mrfld mrfld_header;
+       char *mailbox_data;
+};
+
+struct snd_sst_ctxt_params {
+       u32 address; /* Physical Address in DDR where the context is stored */
+       u32 size; /* size of the context */
+};
+
+struct snd_sst_lpe_log_params {
+       u8 dbg_type;
+       u8 module_id;
+       u8 log_level;
+       u8 reserved;
+} __packed;
+
+enum snd_sst_bytes_type {
+       SND_SST_BYTES_SET = 0x1,
+       SND_SST_BYTES_GET = 0x2,
+};
+
+struct snd_sst_bytes_v2 {
+       u8 type;
+       u8 ipc_msg;
+       u8 block;
+       u8 task_id;
+       u8 pipe_id;
+       u8 rsvd;
+       u16 len;
+       char bytes[0];
+};
+
+#define MAX_VTSV_FILES 2
+struct snd_sst_vtsv_info {
+       struct sst_address_info vfiles[MAX_VTSV_FILES];
+} __packed;
+
 #endif /* __SST_MFLD_DSP_H__ */
index 02abd19..29c059c 100644 (file)
@@ -100,14 +100,19 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
        int retval;
        struct snd_sst_params str_params;
        struct sst_compress_cb cb;
+       struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
 
        stream = cstream->runtime->private_data;
        /* construct fw structure for this*/
        memset(&str_params, 0, sizeof(str_params));
 
-       str_params.ops = STREAM_OPS_PLAYBACK;
-       str_params.stream_type = SST_STREAM_TYPE_MUSIC;
-       str_params.device_type = SND_SST_DEVICE_COMPRESS;
+       /* fill the device type and stream id to pass to SST driver */
+       retval = sst_fill_stream_params(cstream, ctx, &str_params, true);
+       pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval);
+       if (retval < 0)
+               return retval;
 
        switch (params->codec.id) {
        case SND_AUDIOCODEC_MP3: {
index 7c790f5..706212a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  sst_mfld_platform.c - Intel MID Platform driver
  *
- *  Copyright (C) 2010-2013 Intel Corp
+ *  Copyright (C) 2010-2014 Intel Corp
  *  Author: Vinod Koul <vinod.koul@intel.com>
  *  Author: Harsha Priya <priya.harsha@intel.com>
  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -27,7 +27,9 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
 #include "sst-mfld-platform.h"
+#include "sst-atom-controls.h"
 
 struct sst_device *sst;
 static DEFINE_MUTEX(sst_lock);
@@ -92,6 +94,13 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = {
        .fifo_size = SST_FIFO_SIZE,
 };
 
+static struct sst_dev_stream_map dpcm_strm_map[] = {
+       {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */
+       {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0},
+       {MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0},
+       {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
+};
+
 /* MFLD - MSIC */
 static struct snd_soc_dai_driver sst_platform_dai[] = {
 {
@@ -143,58 +152,142 @@ static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
        return state;
 }
 
+static void sst_fill_alloc_params(struct snd_pcm_substream *substream,
+                               struct snd_sst_alloc_params_ext *alloc_param)
+{
+       unsigned int channels;
+       snd_pcm_uframes_t period_size;
+       ssize_t periodbytes;
+       ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+       u32 buffer_addr = virt_to_phys(substream->dma_buffer.area);
+
+       channels = substream->runtime->channels;
+       period_size = substream->runtime->period_size;
+       periodbytes = samples_to_bytes(substream->runtime, period_size);
+       alloc_param->ring_buf_info[0].addr = buffer_addr;
+       alloc_param->ring_buf_info[0].size = buffer_bytes;
+       alloc_param->sg_count = 1;
+       alloc_param->reserved = 0;
+       alloc_param->frag_size = periodbytes * channels;
+
+}
 static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
-                               struct sst_pcm_params *param)
+                               struct snd_sst_stream_params *param)
 {
+       param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
+       param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
+       param->uc.pcm_params.sfreq = substream->runtime->rate;
+
+       /* PCM stream via ALSA interface */
+       param->uc.pcm_params.use_offload_path = 0;
+       param->uc.pcm_params.reserved2 = 0;
+       memset(param->uc.pcm_params.channel_map, 0, sizeof(u8));
 
-       param->num_chan = (u8) substream->runtime->channels;
-       param->pcm_wd_sz = substream->runtime->sample_bits;
-       param->reserved = 0;
-       param->sfreq = substream->runtime->rate;
-       param->ring_buffer_size = snd_pcm_lib_buffer_bytes(substream);
-       param->period_count = substream->runtime->period_size;
-       param->ring_buffer_addr = virt_to_phys(substream->dma_buffer.area);
-       pr_debug("period_cnt = %d\n", param->period_count);
-       pr_debug("sfreq= %d, wd_sz = %d\n", param->sfreq, param->pcm_wd_sz);
 }
 
-static int sst_platform_alloc_stream(struct snd_pcm_substream *substream)
+static int sst_get_stream_mapping(int dev, int sdev, int dir,
+       struct sst_dev_stream_map *map, int size)
+{
+       int i;
+
+       if (map == NULL)
+               return -EINVAL;
+
+
+       /* index 0 is not used in stream map */
+       for (i = 1; i < size; i++) {
+               if ((map[i].dev_num == dev) && (map[i].direction == dir))
+                       return i;
+       }
+       return 0;
+}
+
+int sst_fill_stream_params(void *substream,
+       const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress)
+{
+       int map_size;
+       int index;
+       struct sst_dev_stream_map *map;
+       struct snd_pcm_substream *pstream = NULL;
+       struct snd_compr_stream *cstream = NULL;
+
+       map = ctx->pdata->pdev_strm_map;
+       map_size = ctx->pdata->strm_map_size;
+
+       if (is_compress == true)
+               cstream = (struct snd_compr_stream *)substream;
+       else
+               pstream = (struct snd_pcm_substream *)substream;
+
+       str_params->stream_type = SST_STREAM_TYPE_MUSIC;
+
+       /* For pcm streams */
+       if (pstream) {
+               index = sst_get_stream_mapping(pstream->pcm->device,
+                                         pstream->number, pstream->stream,
+                                         map, map_size);
+               if (index <= 0)
+                       return -EINVAL;
+
+               str_params->stream_id = index;
+               str_params->device_type = map[index].device_id;
+               str_params->task = map[index].task_id;
+
+               str_params->ops = (u8)pstream->stream;
+       }
+
+       if (cstream) {
+               index = sst_get_stream_mapping(cstream->device->device,
+                                              0, cstream->direction,
+                                              map, map_size);
+               if (index <= 0)
+                       return -EINVAL;
+               str_params->stream_id = index;
+               str_params->device_type = map[index].device_id;
+               str_params->task = map[index].task_id;
+
+               str_params->ops = (u8)cstream->direction;
+       }
+       return 0;
+}
+
+static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
+               struct snd_soc_platform *platform)
 {
        struct sst_runtime_stream *stream =
                        substream->runtime->private_data;
-       struct sst_pcm_params param = {0};
-       struct sst_stream_params str_params = {0};
-       int ret_val;
+       struct snd_sst_stream_params param = {{{0,},},};
+       struct snd_sst_params str_params = {0};
+       struct snd_sst_alloc_params_ext alloc_params = {0};
+       int ret_val = 0;
+       struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
 
        /* set codec params and inform SST driver the same */
        sst_fill_pcm_params(substream, &param);
+       sst_fill_alloc_params(substream, &alloc_params);
        substream->runtime->dma_area = substream->dma_buffer.area;
        str_params.sparams = param;
-       str_params.codec =  param.codec;
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               str_params.ops = STREAM_OPS_PLAYBACK;
-               str_params.device_type = substream->pcm->device + 1;
-               pr_debug("Playbck stream,Device %d\n",
-                                       substream->pcm->device);
-       } else {
-               str_params.ops = STREAM_OPS_CAPTURE;
-               str_params.device_type = SND_SST_DEVICE_CAPTURE;
-               pr_debug("Capture stream,Device %d\n",
-                                       substream->pcm->device);
-       }
-       ret_val = stream->ops->open(&str_params);
-       pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
+       str_params.aparams = alloc_params;
+       str_params.codec = SST_CODEC_TYPE_PCM;
+
+       /* fill the device type and stream id to pass to SST driver */
+       ret_val = sst_fill_stream_params(substream, ctx, &str_params, false);
        if (ret_val < 0)
                return ret_val;
 
-       stream->stream_info.str_id = ret_val;
-       pr_debug("str id :  %d\n", stream->stream_info.str_id);
+       stream->stream_info.str_id = str_params.stream_id;
+
+       ret_val = stream->ops->open(&str_params);
+       if (ret_val <= 0)
+               return ret_val;
+
+
        return ret_val;
 }
 
-static void sst_period_elapsed(void *mad_substream)
+static void sst_period_elapsed(void *arg)
 {
-       struct snd_pcm_substream *substream = mad_substream;
+       struct snd_pcm_substream *substream = arg;
        struct sst_runtime_stream *stream;
        int status;
 
@@ -218,7 +311,7 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
        pr_debug("setting buffer ptr param\n");
        sst_set_stream_status(stream, SST_PLATFORM_INIT);
        stream->stream_info.period_elapsed = sst_period_elapsed;
-       stream->stream_info.mad_substream = substream;
+       stream->stream_info.arg = substream;
        stream->stream_info.buffer_ptr = 0;
        stream->stream_info.sfreq = substream->runtime->rate;
        ret_val = stream->ops->device_control(
@@ -230,19 +323,12 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
 }
 /* end -- helper functions */
 
-static int sst_platform_open(struct snd_pcm_substream *substream)
+static int sst_media_open(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
 {
+       int ret_val = 0;
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct sst_runtime_stream *stream;
-       int ret_val;
-
-       pr_debug("sst_platform_open called\n");
-
-       snd_soc_set_runtime_hwparams(substream, &sst_platform_pcm_hw);
-       ret_val = snd_pcm_hw_constraint_integer(runtime,
-                                               SNDRV_PCM_HW_PARAM_PERIODS);
-       if (ret_val < 0)
-               return ret_val;
 
        stream = kzalloc(sizeof(*stream), GFP_KERNEL);
        if (!stream)
@@ -251,50 +337,69 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
 
        /* get the sst ops */
        mutex_lock(&sst_lock);
-       if (!sst) {
+       if (!sst ||
+           !try_module_get(sst->dev->driver->owner)) {
                pr_err("no device available to run\n");
-               mutex_unlock(&sst_lock);
-               kfree(stream);
-               return -ENODEV;
-       }
-       if (!try_module_get(sst->dev->driver->owner)) {
-               mutex_unlock(&sst_lock);
-               kfree(stream);
-               return -ENODEV;
+               ret_val = -ENODEV;
+               goto out_ops;
        }
        stream->ops = sst->ops;
        mutex_unlock(&sst_lock);
 
        stream->stream_info.str_id = 0;
-       sst_set_stream_status(stream, SST_PLATFORM_INIT);
-       stream->stream_info.mad_substream = substream;
+
+       stream->stream_info.arg = substream;
        /* allocate memory for SST API set */
        runtime->private_data = stream;
 
-       return 0;
+       /* Make sure, that the period size is always even */
+       snd_pcm_hw_constraint_step(substream->runtime, 0,
+                          SNDRV_PCM_HW_PARAM_PERIODS, 2);
+
+       return snd_pcm_hw_constraint_integer(runtime,
+                        SNDRV_PCM_HW_PARAM_PERIODS);
+out_ops:
+       kfree(stream);
+       mutex_unlock(&sst_lock);
+       return ret_val;
 }
 
-static int sst_platform_close(struct snd_pcm_substream *substream)
+static void sst_media_close(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
 {
        struct sst_runtime_stream *stream;
        int ret_val = 0, str_id;
 
-       pr_debug("sst_platform_close called\n");
        stream = substream->runtime->private_data;
        str_id = stream->stream_info.str_id;
        if (str_id)
                ret_val = stream->ops->close(str_id);
        module_put(sst->dev->driver->owner);
        kfree(stream);
-       return ret_val;
 }
 
-static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
+static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform,
+                                              struct snd_pcm_substream *substream)
+{
+       struct sst_data *sst = snd_soc_platform_get_drvdata(platform);
+       struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
+       struct sst_runtime_stream *stream =
+                       substream->runtime->private_data;
+       u32 str_id = stream->stream_info.str_id;
+       unsigned int pipe_id;
+       pipe_id = map[str_id].device_id;
+
+       pr_debug("%s: got pipe_id = %#x for str_id = %d\n",
+                __func__, pipe_id, str_id);
+       return pipe_id;
+}
+
+static int sst_media_prepare(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
 {
        struct sst_runtime_stream *stream;
        int ret_val = 0, str_id;
 
-       pr_debug("sst_platform_pcm_prepare called\n");
        stream = substream->runtime->private_data;
        str_id = stream->stream_info.str_id;
        if (stream->stream_info.str_id) {
@@ -303,8 +408,8 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
                return ret_val;
        }
 
-       ret_val = sst_platform_alloc_stream(substream);
-       if (ret_val < 0)
+       ret_val = sst_platform_alloc_stream(substream, dai->platform);
+       if (ret_val <= 0)
                return ret_val;
        snprintf(substream->pcm->id, sizeof(substream->pcm->id),
                        "%d", stream->stream_info.str_id);
@@ -316,6 +421,41 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
        return ret_val;
 }
 
+static int sst_media_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+       memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
+       return 0;
+}
+
+static int sst_media_hw_free(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static struct snd_soc_dai_ops sst_media_dai_ops = {
+       .startup = sst_media_open,
+       .shutdown = sst_media_close,
+       .prepare = sst_media_prepare,
+       .hw_params = sst_media_hw_params,
+       .hw_free = sst_media_hw_free,
+};
+
+static int sst_platform_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime;
+
+       if (substream->pcm->internal)
+               return 0;
+
+       runtime = substream->runtime;
+       runtime->hw = sst_platform_pcm_hw;
+       return 0;
+}
+
 static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
                                        int cmd)
 {
@@ -331,7 +471,7 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
                pr_debug("sst: Trigger Start\n");
                str_cmd = SST_SND_START;
                status = SST_PLATFORM_RUNNING;
-               stream->stream_info.mad_substream = substream;
+               stream->stream_info.arg = substream;
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                pr_debug("sst: in stop\n");
@@ -377,32 +517,15 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer
                pr_err("sst: error code = %d\n", ret_val);
                return ret_val;
        }
-       return stream->stream_info.buffer_ptr;
-}
-
-static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
-               struct snd_pcm_hw_params *params)
-{
-       snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-       memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
-
-       return 0;
-}
-
-static int sst_platform_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-       return snd_pcm_lib_free_pages(substream);
+       substream->runtime->delay = str_info->pcm_delay;
+       return str_info->buffer_ptr;
 }
 
 static struct snd_pcm_ops sst_platform_ops = {
        .open = sst_platform_open,
-       .close = sst_platform_close,
        .ioctl = snd_pcm_lib_ioctl,
-       .prepare = sst_platform_pcm_prepare,
        .trigger = sst_platform_pcm_trigger,
        .pointer = sst_platform_pcm_pointer,
-       .hw_params = sst_platform_pcm_hw_params,
-       .hw_free = sst_platform_pcm_hw_free,
 };
 
 static void sst_pcm_free(struct snd_pcm *pcm)
@@ -413,15 +536,15 @@ static void sst_pcm_free(struct snd_pcm *pcm)
 
 static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_soc_dai *dai = rtd->cpu_dai;
        struct snd_pcm *pcm = rtd->pcm;
        int retval = 0;
 
-       pr_debug("sst_pcm_new called\n");
-       if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
-                       pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+       if (dai->driver->playback.channels_min ||
+                       dai->driver->capture.channels_min) {
                retval =  snd_pcm_lib_preallocate_pages_for_all(pcm,
                        SNDRV_DMA_TYPE_CONTINUOUS,
-                       snd_dma_continuous_data(GFP_KERNEL),
+                       snd_dma_continuous_data(GFP_DMA),
                        SST_MIN_BUFFER, SST_MAX_BUFFER);
                if (retval) {
                        pr_err("dma buffer allocationf fail\n");
@@ -445,10 +568,28 @@ static const struct snd_soc_component_driver sst_component = {
 
 static int sst_platform_probe(struct platform_device *pdev)
 {
+       struct sst_data *drv;
        int ret;
+       struct sst_platform_data *pdata;
+
+       drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+       if (drv == NULL) {
+               pr_err("kzalloc failed\n");
+               return -ENOMEM;
+       }
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (pdata == NULL) {
+               pr_err("kzalloc failed for pdata\n");
+               return -ENOMEM;
+       }
+
+       pdata->pdev_strm_map = dpcm_strm_map;
+       pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
+       drv->pdata = pdata;
+       mutex_init(&drv->lock);
+       dev_set_drvdata(&pdev->dev, drv);
 
-       pr_debug("sst_platform_probe called\n");
-       sst = NULL;
        ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
        if (ret) {
                pr_err("registering soc platform failed\n");
index 6c5e7dc..6c6a42c 100644 (file)
@@ -39,9 +39,10 @@ extern struct sst_device *sst;
 
 struct pcm_stream_info {
        int str_id;
-       void *mad_substream;
-       void (*period_elapsed) (void *mad_substream);
+       void *arg;
+       void (*period_elapsed) (void *arg);
        unsigned long long buffer_ptr;
+       unsigned long long pcm_delay;
        int sfreq;
 };
 
@@ -62,7 +63,9 @@ enum sst_controls {
        SST_SND_BUFFER_POINTER =        0x05,
        SST_SND_STREAM_INIT =           0x06,
        SST_SND_START    =              0x07,
-       SST_MAX_CONTROLS =              0x07,
+       SST_SET_BYTE_STREAM =           0x100A,
+       SST_GET_BYTE_STREAM =           0x100B,
+       SST_MAX_CONTROLS = SST_GET_BYTE_STREAM,
 };
 
 enum sst_stream_ops {
@@ -124,8 +127,9 @@ struct compress_sst_ops {
 };
 
 struct sst_ops {
-       int (*open) (struct sst_stream_params *str_param);
+       int (*open) (struct snd_sst_params *str_param);
        int (*device_control) (int cmd, void *arg);
+       int (*set_generic_params)(enum sst_controls cmd, void *arg);
        int (*close) (unsigned int str_id);
 };
 
@@ -143,10 +147,27 @@ struct sst_device {
        char *name;
        struct device *dev;
        struct sst_ops *ops;
+       struct platform_device *pdev;
        struct compress_sst_ops *compr_ops;
 };
 
+struct sst_data;
 void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
+int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
+                          struct snd_sst_params *str_params, bool is_compress);
+
+struct sst_algo_int_control_v2 {
+       struct soc_mixer_control mc;
+       u16 module_id; /* module identifieer */
+       u16 pipe_id; /* location info: pipe_id + instance_id */
+       u16 instance_id;
+       unsigned int value; /* Value received is stored here */
+};
+struct sst_data {
+       struct platform_device *pdev;
+       struct sst_platform_data *pdata;
+       struct mutex lock;
+};
 int sst_register_dsp(struct sst_device *sst);
 int sst_unregister_dsp(struct sst_device *sst);
 #endif
index 06f4e8a..132bb83 100644 (file)
@@ -1,6 +1,6 @@
 config SND_KIRKWOOD_SOC
        tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
-       depends on ARCH_KIRKWOOD || ARCH_DOVE || ARCH_MVEBU || MACH_KIRKWOOD || COMPILE_TEST
+       depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
        help
          Say Y or M if you want to add support for codecs attached to
          the Kirkwood I2S interface. You will also need to select the
@@ -15,20 +15,3 @@ config SND_KIRKWOOD_SOC_ARMADA370_DB
          Say Y if you want to add support for SoC audio on
          the Armada 370 Development Board.
 
-config SND_KIRKWOOD_SOC_OPENRD
-       tristate "SoC Audio support for Kirkwood Openrd Client"
-       depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE || COMPILE_TEST)
-       depends on I2C
-       select SND_SOC_CS42L51
-       help
-         Say Y if you want to add support for SoC audio on
-         Openrd Client.
-
-config SND_KIRKWOOD_SOC_T5325
-       tristate "SoC Audio support for HP t5325"
-       depends on SND_KIRKWOOD_SOC && (MACH_T5325 || COMPILE_TEST) && I2C
-       select SND_SOC_ALC5623
-       help
-         Say Y if you want to add support for SoC audio on
-         the HP t5325 thin client.
-
index 7c1d8fe..c36b03d 100644 (file)
@@ -2,10 +2,6 @@ snd-soc-kirkwood-objs := kirkwood-dma.o kirkwood-i2s.o
 
 obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o
 
-snd-soc-openrd-objs := kirkwood-openrd.o
-snd-soc-t5325-objs := kirkwood-t5325.o
 snd-soc-armada-370-db-objs := armada-370-db.o
 
 obj-$(CONFIG_SND_KIRKWOOD_SOC_ARMADA370_DB) += snd-soc-armada-370-db.o
-obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o
-obj-$(CONFIG_SND_KIRKWOOD_SOC_T5325) += snd-soc-t5325.o
index aac22fc..4cf2245 100644 (file)
@@ -28,11 +28,12 @@ static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
 }
 
 static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
-       .info = (SNDRV_PCM_INFO_INTERLEAVED |
-                SNDRV_PCM_INFO_MMAP |
-                SNDRV_PCM_INFO_MMAP_VALID |
-                SNDRV_PCM_INFO_BLOCK_TRANSFER |
-                SNDRV_PCM_INFO_PAUSE),
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
        .buffer_bytes_max       = KIRKWOOD_SND_MAX_BUFFER_BYTES,
        .period_bytes_min       = KIRKWOOD_SND_MIN_PERIOD_BYTES,
        .period_bytes_max       = KIRKWOOD_SND_MAX_PERIOD_BYTES,
index 9f84222..0704cd6 100644 (file)
@@ -212,7 +212,8 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
                                    KIRKWOOD_PLAYCTL_SIZE_MASK);
                priv->ctl_play |= ctl_play;
        } else {
-               priv->ctl_rec &= ~KIRKWOOD_RECCTL_SIZE_MASK;
+               priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK |
+                                  KIRKWOOD_RECCTL_SIZE_MASK);
                priv->ctl_rec |= ctl_rec;
        }
 
@@ -221,14 +222,24 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
+static unsigned kirkwood_i2s_play_mute(unsigned ctl)
+{
+       if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN))
+               ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE;
+       if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN))
+               ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE;
+       return ctl;
+}
+
 static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
                                int cmd, struct snd_soc_dai *dai)
 {
+       struct snd_pcm_runtime *runtime = substream->runtime;
        struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
        uint32_t ctl, value;
 
        ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
-       if (ctl & KIRKWOOD_PLAYCTL_PAUSE) {
+       if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) {
                unsigned timeout = 5000;
                /*
                 * The Armada510 spec says that if we enter pause mode, the
@@ -256,14 +267,16 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
                        ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN;      /* i2s */
                else
                        ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN;        /* spdif */
-
+               ctl = kirkwood_i2s_play_mute(ctl);
                value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
                writel(value, priv->io + KIRKWOOD_PLAYCTL);
 
                /* enable interrupts */
-               value = readl(priv->io + KIRKWOOD_INT_MASK);
-               value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
-               writel(value, priv->io + KIRKWOOD_INT_MASK);
+               if (!runtime->no_period_wakeup) {
+                       value = readl(priv->io + KIRKWOOD_INT_MASK);
+                       value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
+                       writel(value, priv->io + KIRKWOOD_INT_MASK);
+               }
 
                /* enable playback */
                writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
@@ -295,6 +308,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
                                KIRKWOOD_PLAYCTL_SPDIF_MUTE);
+               ctl = kirkwood_i2s_play_mute(ctl);
                writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
                break;
 
@@ -322,8 +336,7 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
                else
                        ctl &= ~KIRKWOOD_RECCTL_I2S_EN;         /* spdif */
 
-               value = ctl & ~(KIRKWOOD_RECCTL_I2S_EN |
-                               KIRKWOOD_RECCTL_SPDIF_EN);
+               value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
                writel(value, priv->io + KIRKWOOD_RECCTL);
 
                /* enable interrupts */
@@ -347,7 +360,7 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
 
                /* disable all records */
                value = readl(priv->io + KIRKWOOD_RECCTL);
-               value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
+               value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
                writel(value, priv->io + KIRKWOOD_RECCTL);
                break;
 
@@ -411,7 +424,7 @@ static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
        writel(value, priv->io + KIRKWOOD_PLAYCTL);
 
        value = readl(priv->io + KIRKWOOD_RECCTL);
-       value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
+       value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
        writel(value, priv->io + KIRKWOOD_RECCTL);
 
        return 0;
diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c
deleted file mode 100644 (file)
index 65f2a5b..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * kirkwood-openrd.c
- *
- * (c) 2010 Arnaud Patard <apatard@mandriva.com>
- * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
- *
- *  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;  either version 2 of the  License, or (at your
- *  option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <sound/soc.h>
-#include <linux/platform_data/asoc-kirkwood.h>
-#include "../codecs/cs42l51.h"
-
-static int openrd_client_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;
-       unsigned int freq;
-
-       switch (params_rate(params)) {
-       default:
-       case 44100:
-               freq = 11289600;
-               break;
-       case 48000:
-               freq = 12288000;
-               break;
-       case 96000:
-               freq = 24576000;
-               break;
-       }
-
-       return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
-
-}
-
-static struct snd_soc_ops openrd_client_ops = {
-       .hw_params = openrd_client_hw_params,
-};
-
-
-static struct snd_soc_dai_link openrd_client_dai[] = {
-{
-       .name = "CS42L51",
-       .stream_name = "CS42L51 HiFi",
-       .cpu_dai_name = "i2s",
-       .platform_name = "mvebu-audio",
-       .codec_dai_name = "cs42l51-hifi",
-       .codec_name = "cs42l51-codec.0-004a",
-       .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
-       .ops = &openrd_client_ops,
-},
-};
-
-
-static struct snd_soc_card openrd_client = {
-       .name = "OpenRD Client",
-       .owner = THIS_MODULE,
-       .dai_link = openrd_client_dai,
-       .num_links = ARRAY_SIZE(openrd_client_dai),
-};
-
-static int openrd_probe(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = &openrd_client;
-       int ret;
-
-       card->dev = &pdev->dev;
-
-       ret = snd_soc_register_card(card);
-       if (ret)
-               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
-                       ret);
-       return ret;
-}
-
-static int openrd_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
-static struct platform_driver openrd_driver = {
-       .driver         = {
-               .name   = "openrd-client-audio",
-               .owner  = THIS_MODULE,
-       },
-       .probe          = openrd_probe,
-       .remove         = openrd_remove,
-};
-
-module_platform_driver(openrd_driver);
-
-/* Module information */
-MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
-MODULE_DESCRIPTION("ALSA SoC OpenRD Client");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:openrd-client-audio");
diff --git a/sound/soc/kirkwood/kirkwood-t5325.c b/sound/soc/kirkwood/kirkwood-t5325.c
deleted file mode 100644 (file)
index 844b841..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * kirkwood-t5325.c
- *
- * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
- *
- *  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;  either version 2 of the  License, or (at your
- *  option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <sound/soc.h>
-#include <linux/platform_data/asoc-kirkwood.h>
-#include "../codecs/alc5623.h"
-
-static int t5325_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;
-       unsigned int freq;
-
-       freq = params_rate(params) * 256;
-
-       return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
-
-}
-
-static struct snd_soc_ops t5325_ops = {
-       .hw_params = t5325_hw_params,
-};
-
-static const struct snd_soc_dapm_widget t5325_dapm_widgets[] = {
-       SND_SOC_DAPM_HP("Headphone Jack", NULL),
-       SND_SOC_DAPM_SPK("Speaker", NULL),
-       SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route t5325_route[] = {
-       { "Headphone Jack",     NULL,   "HPL" },
-       { "Headphone Jack",     NULL,   "HPR" },
-
-       {"Speaker",             NULL,   "SPKOUT"},
-       {"Speaker",             NULL,   "SPKOUTN"},
-
-       { "MIC1",               NULL,   "Mic Jack" },
-       { "MIC2",               NULL,   "Mic Jack" },
-};
-
-static struct snd_soc_dai_link t5325_dai[] = {
-{
-       .name = "ALC5621",
-       .stream_name = "ALC5621 HiFi",
-       .cpu_dai_name = "i2s",
-       .platform_name = "mvebu-audio",
-       .codec_dai_name = "alc5621-hifi",
-       .codec_name = "alc562x-codec.0-001a",
-       .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
-       .ops = &t5325_ops,
-},
-};
-
-static struct snd_soc_card t5325 = {
-       .name = "t5325",
-       .owner = THIS_MODULE,
-       .dai_link = t5325_dai,
-       .num_links = ARRAY_SIZE(t5325_dai),
-
-       .dapm_widgets = t5325_dapm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(t5325_dapm_widgets),
-       .dapm_routes = t5325_route,
-       .num_dapm_routes = ARRAY_SIZE(t5325_route),
-};
-
-static int t5325_probe(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = &t5325;
-       int ret;
-
-       card->dev = &pdev->dev;
-
-       ret = snd_soc_register_card(card);
-       if (ret)
-               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
-                       ret);
-       return ret;
-}
-
-static int t5325_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
-static struct platform_driver t5325_driver = {
-       .driver         = {
-               .name   = "t5325-audio",
-               .owner  = THIS_MODULE,
-       },
-       .probe          = t5325_probe,
-       .remove         = t5325_remove,
-};
-
-module_platform_driver(t5325_driver);
-
-MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
-MODULE_DESCRIPTION("ALSA SoC t5325 audio client");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:t5325-audio");
index bf23afb..90e32a7 100644 (file)
@@ -38,6 +38,9 @@
 #define KIRKWOOD_RECCTL_SIZE_24                (1<<0)
 #define KIRKWOOD_RECCTL_SIZE_32                (0<<0)
 
+#define KIRKWOOD_RECCTL_ENABLE_MASK            (KIRKWOOD_RECCTL_SPDIF_EN | \
+                                                KIRKWOOD_RECCTL_I2S_EN)
+
 #define KIRKWOOD_REC_BUF_ADDR                  0x1004
 #define KIRKWOOD_REC_BUF_SIZE                  0x1008
 #define KIRKWOOD_REC_BYTE_COUNT                        0x100C
 
 /* Theses values come from the marvell alsa driver */
 /* need to find where they come from               */
-#define KIRKWOOD_SND_MIN_PERIODS               8
+#define KIRKWOOD_SND_MIN_PERIODS               2
 #define KIRKWOOD_SND_MAX_PERIODS               16
-#define KIRKWOOD_SND_MIN_PERIOD_BYTES          0x800
+#define KIRKWOOD_SND_MIN_PERIOD_BYTES          256
 #define KIRKWOOD_SND_MAX_PERIOD_BYTES          0x8000
 #define KIRKWOOD_SND_MAX_BUFFER_BYTES          (KIRKWOOD_SND_MAX_PERIOD_BYTES \
                                                 * KIRKWOOD_SND_MAX_PERIODS)